summaryrefslogtreecommitdiffstats
path: root/crawl-ref/source/geom2d.cc
diff options
context:
space:
mode:
authorRobert Vollmert <rvollmert@gmx.net>2009-10-29 23:31:45 +0100
committerRobert Vollmert <rvollmert@gmx.net>2009-11-01 21:45:23 +0100
commit8145afa69c7dd611850e521a557dc082a21e9e02 (patch)
treeb403653f7eb3ce434e1788baf56a6a18585d1034 /crawl-ref/source/geom2d.cc
parent1c16d581d94baca62ddb6ad136e444e87fbe13d6 (diff)
downloadcrawl-ref-8145afa69c7dd611850e521a557dc082a21e9e02.tar.gz
crawl-ref-8145afa69c7dd611850e521a557dc082a21e9e02.zip
Add simple 2d geometry library.
Its point is to allow intersecting rays with grid lines and advancing rays from one cell to the next.
Diffstat (limited to 'crawl-ref/source/geom2d.cc')
-rw-r--r--crawl-ref/source/geom2d.cc149
1 files changed, 149 insertions, 0 deletions
diff --git a/crawl-ref/source/geom2d.cc b/crawl-ref/source/geom2d.cc
new file mode 100644
index 0000000000..ddb2ec771d
--- /dev/null
+++ b/crawl-ref/source/geom2d.cc
@@ -0,0 +1,149 @@
+/*
+ * File: geom2d.cc
+ * Summary: Plane geometry for shooting rays.
+ */
+
+#include "AppHdr.h"
+#include "debug.h"
+#include "geom2d.h"
+
+#include <cmath>
+
+namespace geom {
+
+static bool double_is_zero(double d)
+{
+ return (std::abs(d) < 0.0000001);
+}
+
+// Is v parallel to the kernel of f?
+static bool parallel(vector v, form f)
+{
+ return (double_is_zero(f(v)));
+}
+
+vector ray::shoot(double t) const
+{
+ return (start + dir * t);
+}
+
+void ray::advance(double t)
+{
+ start = shoot(t);
+}
+
+// Determine the value of t such that
+// r.start + t * r.dir lies on the line l.
+// r and l must not be parallel.
+double intersect(const ray &r, const line &l)
+{
+ ASSERT(!parallel(r.dir, l.f));
+ double fp = l.f(r.start);
+ double fd = l.f(r.dir);
+ double t = (l.val - fp) / fd;
+ return (t);
+}
+
+// Find the next intersection of r with a line in ls.
+double nextintersect(const ray &r, const lineseq &ls)
+{
+ // ray must not be parallel to the lines
+ ASSERT(!parallel(r.dir, ls.f));
+ double fp = ls.f(r.start);
+ double fd = ls.f(r.dir);
+ // want smallest positive t with
+ // fp + t*fd = ls.offset + k*ls.dist, k integral
+ double a = (fp - ls.offset) / ls.dist;
+ double k = (ls.dist*fd) > 0 ? ceil(a) : floor(a);
+ double t = (k - a) * ls.dist / fd;
+ return (t);
+}
+
+// Line distance in the ray parameter.
+// ls.f(r.shoot(tdist)) == ls.f(r.start) +- ls.dist
+static double tdist(const ray &r, const lineseq &ls)
+{
+ ASSERT(!parallel(r.dir, ls.f));
+ return (std::abs(ls.dist / ls.f(r.dir)));
+}
+
+// Shoot the ray inside the next cell.
+// Returns whether it hit a corner of the grid.
+bool nextcell(ray &r, const grid &g, bool pass_corner)
+{
+ // XXX: ASSERT(!parallel(g.vert, g.horiz));
+ lineseq ls;
+ if (parallel(r.dir, g.ls1.f))
+ ls = g.ls2;
+ else if (parallel(r.dir, g.ls2.f))
+ ls = g.ls1;
+ else
+ {
+ double s = nextintersect(r, g.ls1);
+ double t = nextintersect(r, g.ls2);
+ double sd = tdist(r, g.ls1);
+ double td = tdist(r, g.ls2);
+ if (double_is_zero(s - t))
+ {
+ // Hitting the corner.
+ r.advance(s);
+ double u = std::min(sd, td);
+ if (pass_corner)
+ r.advance(0.5 * u);
+ else
+ {
+ // XXX: fudge ray into off-diagonal cell.
+ r.start.x += 0.001 * u * r.dir.y;
+ r.start.y += -0.001 * u * r.dir.x;
+ }
+ return (true);
+ }
+ double dmin = std::min(s,t);
+ double dnext = std::min(std::max(s,t), dmin + std::min(sd, td));
+ r.advance((dmin + dnext) / 2.0);
+ return (false);
+ }
+ // advance an extra half
+ double s = nextintersect(r, ls);
+ r.advance(s + 0.5 * tdist(r, ls));
+ return (false);
+}
+
+
+// vector space implementation
+
+const vector& vector::operator+=(const vector &v)
+{
+ x += v.x;
+ y += v.y;
+ return (*this);
+}
+
+vector vector::operator+(const vector &v) const
+{
+ vector copy = *this;
+ copy += v;
+ return (copy);
+}
+
+const vector& vector::operator*=(double t)
+{
+ x *= t;
+ y *= t;
+ return (*this);
+}
+
+vector vector::operator*(double t) const
+{
+ vector copy = *this;
+ copy *= t;
+ return (copy);
+}
+
+double form::operator()(const vector& v) const
+{
+ return (a*v.x + b*v.y);
+}
+
+}
+