1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
|
/**
* @file
* @brief Plane geometry for shooting rays.
**/
#include "AppHdr.h"
#include "geom2d.h"
#include <cmath>
namespace geom
{
static bool double_is_zero(double d)
{
return abs(d) < 0.0000001;
}
// Is v parallel to the kernel of f?
bool parallel(const vector &v, const form &f)
{
return double_is_zero(f(v));
}
vector ray::shoot(double t) const
{
return start + t*dir;
}
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;
}
double lineseq::index(const vector &v) const
{
return (f(v) - offset) / dist;
}
// 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 > 0 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);
if (double_is_zero(k - a))
k += (ls.dist*fd > 0 ? 1 : -1);
double t = (k - a) * ls.dist / fd;
return t;
}
// Move the ray towards the next grid line.
// Half way there if "half" is true, else all the way there
// Returns whether it hit a corner.
bool ray::to_grid(const grid &g, bool half)
{
// ASSERT(!parallel(g.vert, g.horiz));
bool corner = false;
double t = 0;
if (parallel(dir, g.ls1.f))
t = nextintersect(*this, g.ls2);
else if (parallel(dir, g.ls2.f))
t = nextintersect(*this, g.ls1);
else
{
double r = nextintersect(*this, g.ls1);
double s = nextintersect(*this, g.ls2);
t = min(r, s);
corner = double_is_zero(r - s);
}
advance(half ? 0.5 * t : t);
return corner && !half;
}
// Shoot the ray inside the next cell, stopping at corners.
// Returns true if it hit a corner.
bool ray::to_next_cell(const grid &g)
{
if (to_grid(g, false))
return true;
to_grid(g, true);
return false;
}
vector reflect(const vector &v, const form &f)
{
vector normal = vector(f.a, f.b);
return v - 2 * f(v)/f(normal) * normal;
}
//////////////////////////////////////////////////
// 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;
}
vector vector::operator-() const
{
return (-1) * (*this);
}
const vector& vector::operator-=(const vector &v)
{
return *this += -v;
}
vector vector::operator-(const vector &v) const
{
return *this + (-v);
}
vector operator*(double t, const vector &v)
{
return vector(t*v.x, t*v.y);
}
double form::operator()(const vector& v) const
{
return a*v.x + b*v.y;
}
}
|