summaryrefslogtreecommitdiffstats
path: root/crawl-ref/source/ray.cc
diff options
context:
space:
mode:
authorRobert Vollmert <rvollmert@gmx.net>2009-11-05 17:23:34 +0100
committerRobert Vollmert <rvollmert@gmx.net>2009-11-05 17:25:36 +0100
commit0dc3e02bc8bc01336bd9421257635b49d646502d (patch)
tree08df1ac0807ad8871fcd04a3c824987d24e68ce3 /crawl-ref/source/ray.cc
parent259a353db2a544831c572146cf50bdafcdc4ed5d (diff)
downloadcrawl-ref-0dc3e02bc8bc01336bd9421257635b49d646502d.tar.gz
crawl-ref-0dc3e02bc8bc01336bd9421257635b49d646502d.zip
A variety of changes to make the reflection code more robust.
In particular, round things to grid or corner when they're regarded as on the grid or corner, and normalize direction vectors.
Diffstat (limited to 'crawl-ref/source/ray.cc')
-rw-r--r--crawl-ref/source/ray.cc96
1 files changed, 76 insertions, 20 deletions
diff --git a/crawl-ref/source/ray.cc b/crawl-ref/source/ray.cc
index cc30f3dfe5..f2b8c067ef 100644
--- a/crawl-ref/source/ray.cc
+++ b/crawl-ref/source/ray.cc
@@ -84,35 +84,84 @@ static bool in_non_diamond_int(const geom::vector &v)
return (!in_diamond(v) && !on_line(v));
}
+static bool _to_grid(geom::ray *r, bool half);
// Is r on a corner and heading into a non-diamond?
static bool bad_corner(const geom::ray &r)
{
if (!is_corner(r.start))
return (false);
geom::ray copy = r;
- copy.to_grid(diamonds, true);
+ _to_grid(&copy, true);
return (in_non_diamond_int(copy.start));
}
-static coord_def round_vec(const geom::vector &v)
+static coord_def floor_vec(const geom::vector &v)
{
int x = ifloor(v.x);
int y = ifloor(v.y);
return (coord_def(x, y));
}
-
coord_def ray_def::pos() const
{
ASSERT(_valid());
// XXX: pretty arbitrary if we're just on a corner.
- return (round_vec(r.start));
+ return (floor_vec(r.start));
+}
+
+void _round_to_corner(geom::ray *r)
+{
+ geom::vector v = 2.0 * r->start;
+ v.x = round(v.x);
+ v.y = round(v.y);
+ ASSERT((iround(v.x) + iround(v.y)) % 2 == 1);
+ r->start = 0.5 * v;
+}
+
+void _round_to_grid(geom::ray *r)
+{
+ // x + y or x - y is of the form 0.5+k
+ geom::vector v = r->start;
+ double sum = v.x + v.y - 0.5;
+ double diff = v.x - v.y - 0.5;
+ double deltas = round(sum) - sum;
+ double deltad = round(diff) - diff;
+ if (std::abs(deltas) <= std::abs(deltad))
+ {
+ v.x += 0.5 * deltas;
+ v.y += 0.5 * deltas;
+ }
+ else
+ {
+ v.x += 0.5 * deltad;
+ v.y -= 0.5 * deltad;
+ }
+ r->start = v;
+}
+
+static bool _to_next_cell(geom::ray *r)
+{
+ bool c = r->to_next_cell(diamonds);
+ if (c)
+ _round_to_corner(r);
+ return (c);
+}
+
+static bool _to_grid(geom::ray *r, bool half)
+{
+ bool c = r->to_grid(diamonds, half);
+ if (!half)
+ _round_to_grid(r);
+ c = c || is_corner(r->start);
+ if (c)
+ _round_to_corner(r);
+ return (c);
}
static bool _advance_from_non_diamond(geom::ray *r)
{
ASSERT(in_non_diamond_int(r->start));
- if (!r->to_next_cell(diamonds))
+ if (!_to_next_cell(r))
{
ASSERT(in_diamond_int(r->start));
return (false);
@@ -128,30 +177,37 @@ static bool _advance_from_non_diamond(geom::ray *r)
// The ray is in a legal state to be passed around externally.
bool ray_def::_valid() const
{
- return (on_corner && is_corner(r.start) ||
+ return (on_corner && is_corner(r.start) && bad_corner(r) ||
!on_corner && in_diamond_int(r.start));
}
+geom::vector _normalize(const geom::vector &v)
+{
+ double n = sqrt(v.x*v.x + v.y*v.y);
+ return ((1.0 / n) * v);
+}
+
// Return true if we didn't hit a corner, hence if this
// is a good ray so far.
bool ray_def::advance()
{
ASSERT(_valid());
+ r.dir = _normalize(r.dir);
if (on_corner)
{
ASSERT (is_corner(r.start));
on_corner = false;
- r.to_grid(diamonds, true);
+ _to_grid(&r, true);
}
else
{
// Starting inside a diamond.
- bool c = r.to_next_cell(diamonds);
+ bool c = _to_next_cell(&r);
if (c)
{
// r is now on a corner, going from diamond to diamond.
- r.to_grid(diamonds, true);
+ _to_grid(&r, true);
ASSERT(_valid());
return (true);
}
@@ -259,17 +315,17 @@ geom::vector _fudge_corner(const geom::vector &w, const reflect_grid &rg)
{
// just try both sides
v.x += 10 * EPSILON_VALUE;
- if (rg(round_vec(v) + rg_o))
+ if (rg(floor_vec(v) + rg_o))
v.x -= 20 * EPSILON_VALUE;
- ASSERT(!rg(round_vec(v) + rg_o));
+ ASSERT(!rg(floor_vec(v) + rg_o));
}
else
{
ASSERT(double_is_integral(v.y));
v.y += 10 * EPSILON_VALUE;
- if (rg(round_vec(v) + rg_o))
+ if (rg(floor_vec(v) + rg_o))
v.y -= 20 * EPSILON_VALUE;
- ASSERT(!rg(round_vec(v) + rg_o));
+ ASSERT(!rg(floor_vec(v) + rg_o));
}
return (v);
}
@@ -288,11 +344,11 @@ geom::ray _bounce_diag_corridor(const geom::ray &rorig)
// Now bounce back and forth between l1 and l2 until we hit k.
while (!double_is_zero(geom::intersect(r, k)))
{
- r.to_grid(diamonds, false);
+ _to_grid(&r, false);
r.dir = reflect(r.dir, wall);
}
// Now pointing inside the destination cell (1,1) -- move inside.
- r.to_grid(diamonds, true);
+ _to_grid(&r, true);
return (r);
}
@@ -342,7 +398,7 @@ geom::ray _bounce_noncorner(const geom::ray &r, const coord_def &side,
// Depending on the case, we're on some diamond edge
// or between diamonds. We'll just move safely into
// the next one.
- rmirr.to_grid(diamonds, true);
+ _to_grid(&rmirr, true);
if (in_non_diamond_int(rmirr.start))
_advance_from_non_diamond(&rmirr);
}
@@ -402,13 +458,13 @@ geom::ray _bounce_corner(const geom::ray &rorig, const coord_def &side,
if (f.a != 0 && f.b != 0)
{
// Diagonal wall: to the next diamond, then inside.
- r.to_grid(diamonds, false);
- r.to_grid(diamonds, true);
+ _to_grid(&r, false);
+ _to_grid(&r, true);
}
else
{
// Back inside diamond (0,0).
- r.to_grid(diamonds, true);
+ _to_grid(&r, true);
}
}
return (r);
@@ -438,7 +494,7 @@ void ray_def::bounce(const reflect_grid &rg)
// Move to the diamond edge to determine the side.
coord_def side;
- bool corner = rtrans.to_grid(diamonds, false);
+ bool corner = _to_grid(&rtrans, false);
double d1 = diamonds.ls1.index(rtrans.start);
if (double_is_integral(d1))
side += iround(d1) ? coord_def(1,1) : coord_def(-1,-1);