CGR Localization
 All Classes Namespaces Files Functions Variables Macros Pages
geomalgo.h
1 #ifndef __GEOM_ALGO_H__
2 #define __GEOM_ALGO_H__
3 
4 #include "gvector.h"
5 
6 namespace GVector {
7 
8 // returns distance from point p to line x0-x1
9 template <class num>
10 num distance_to_line(const vector2d<num> x0,const vector2d<num> x1,const vector2d<num> p)
11 {
12  // dot with unit length normal to line
13  return(fabs((x1 - x0).norm().perpdot(p - x0)));
14 }
15 
16 // returns perpendicular offset from line x0-x1 to point p
17 template <class num>
18 num offset_to_line(const vector2d<num> x0,const vector2d<num> x1,const vector2d<num> p)
19 {
20  vector2d<num> n;
21 
22  // get normal to line
23  n = (x1 - x0).perp().norm();
24 
25  return(n.dot(p - x0));
26 }
27 
28 // returns perpendicular offset from line x0-x1 to point p
29 template <class num>
30 num offset_along_line(const vector2d<num> x0,const vector2d<num> x1,const vector2d<num> p)
31 {
32  vector2d<num> n,v;
33 
34  // get normal to line
35  n = x1 - x0;
36  n.normalize();
37 
38  v = p - x0;
39 
40  return(n.dot(v));
41 }
42 
43 // returns nearest point on segment a0-a1 to line b0-b1
44 template <class num>
45 vector2d<num> segment_near_line(const vector2d<num> a0,const vector2d<num> a1,
46  const vector2d<num> b0,const vector2d<num> b1)
47 {
48  vector2d<num> v,n,p;
49  double dn,t;
50 
51  v = a1-a0;
52  n = (b1-b0).norm();
53  n.set(-n.y,n.x);
54 
55  dn = dot(v,n);
56  if(fabs(dn) < EPSILON) return(a0);
57 
58  t = -dot(a0-b0,n) / dn;
59  // printf("t=%f dn=%f\n",t,dn);
60  if(t < 0) t = 0;
61  if(t > 1) t = 1;
62  p = a0 + v*t;
63 
64  return(p);
65 }
66 
67 //
68 template <class num>
69 vector2d<num> intersection(const vector2d<num> a1, const vector2d<num> a2,
70  const vector2d<num> b1, const vector2d<num> b2)
71 {
72  vector2d<num> a = a2 - a1;
73 
74  vector2d<num> b1r = (b1 - a1).rotate(-a.angle());
75  vector2d<num> b2r = (b2 - a1).rotate(-a.angle());
76  vector2d<num> br = (b1r - b2r);
77 
78  return
79  vector2d<num>(b2r.x - b2r.y * (br.x / br.y), 0.0).rotate(a.angle()) + a1;
80 }
81 
82 // gives counterclockwise angle from <a-b> to <c-b>
83 template <class num>
84 num vertex_angle(const vector2d<num> a,
85  const vector2d<num> b,
86  const vector2d<num> c)
87 {
88  return(angle_mod((a-b).angle() - (c-b).angle()));
89 }
90 
91 // calc_circle
92 template <class num>
93 bool CalcCircle(vector2d<num> &cen,num &rad,
94  const vector2d<num> p1,
95  const vector2d<num> p2,
96  const vector2d<num> p3)
97 {
98  vector2d<num> a,b;
99  num da,db;
100 
101  // edge vectors
102  a = (p2 - p1);
103  b = (p3 - p2);
104 
105  // dot products of midpoint of each edge vector
106  da = a.dot(p1 + p2) * 0.5;
107  db = b.dot(p2 + p3) * 0.5;
108 
109  /*
110  We want to find a point c on each line, so:
111  [ a.x a.y ] [c.x] = [da]
112  [ b.x b.y ] [c.y] [db]
113 
114  We can invert the matrix to do that, and multiply it on the left of both
115  side. That yeilds a direct expression for center point c.
116  */
117 
118  num det = a.x * b.y - a.y * b.x;
119  if(fabs(det) < EPSILON) return(false);
120 
121  cen.x = ( b.y*da + -a.y*db) / det;
122  cen.y = (-b.x*da + a.x*db) / det;
123 
124  // use averaging to get a more numerically accurate radius
125  // rad = sqrt((sqdist(cen,p1) + sqdist(cen,p2) + sqdist(cen,p3)) / 3.0);
126  rad = dist(cen,p1);
127 
128  if(false){
129  printf("%f %f, %f %f\n",
130  a.dot(cen),da,
131  b.dot(cen),db);
132  }
133 
134  return(true);
135 }
136 
137 //==== Generic functions =============================================//
138 // (work on 2d or 3d vectors)
139 
140 template <class vector>
141 vector interpolate(const vector x0,const vector x1,double t)
142 {
143  // t = bound(t, 0.0, 1.0);
144  return(x0 + (x1-x0)*t);
145 }
146 
147 template <class vector>
148 vector point_on_line(const vector x0,const vector x1,const vector p)
149 // returns nearest point on line through x0-x1 to point p
150 // Preconditions: x0!=x1
151 {
152  vector sx,sp,r;
153  double f,l;
154 
155  sx = x1 - x0;
156  sp = p - x0;
157 
158  f = dot(sx,sp);
159  l = sx.sqlength();
160 
161  // if line is degenerate, any point will do
162  if(l < EPSILON) return(p);
163 
164  // calculate point along line nearest to p
165  r = x0 + sx * (f / l);
166  return(r);
167 }
168 
169 template <class vector>
170 vector point_on_segment(const vector x0,const vector x1,const vector p)
171 // returns nearest point on line segment x0-x1 to point p
172 {
173  vector sx,sp,r;
174  double f,l;
175 
176  sx = x1 - x0;
177  sp = p - x0;
178 
179  f = dot(sx,sp);
180  if(f <= 0.0) return(x0); // also handles x0=x1 case
181 
182  l = sx.sqlength();
183  if(f >= l) return(x1);
184 
185  r = x0 + sx * (f / l);
186 
187  return(r);
188 }
189 
190 
191 template <class vector>
192 double point_on_segment_t(const vector x0,const vector x1,const vector p)
193 // returns nearest point on line segment x0-x1 to point p
194 {
195  vector sx,sp,r;
196  double f,l;
197 
198  sx = x1 - x0;
199  sp = p - x0;
200 
201  f = dot(sx,sp);
202  l = sx.sqlength();
203 
204  if (f <= 0) return 0.0;
205  else if(f >= l) return 1.0;
206  else return (f/l);
207 
208 }
209 
210 // returns shortest distance between line segment x0-x1 to point p
211 template <class vector>
212 double distance_to_segment(const vector x0,const vector x1,const vector p)
213 {
214  vector sx,sp,r;
215  double f,l;
216 
217  sx = x1 - x0;
218  sp = p - x0;
219 
220  f = dot(sx,sp);
221  if(f <= 0.0) return(distance(x0,p)); // also handles x0=x1 case
222 
223  l = sx.sqlength();
224  if(f >= l) return(distance(x1,p));
225 
226  r = x0 + sx * (f / l);
227 
228  return(distance(r,p));
229 }
230 
231 template <class vector>
232 double closest_point_time(const vector x1,const vector v1,
233  const vector x2,const vector v2)
234 // returns time of closest point of approach of two points
235 // moving along constant velocity vectors.
236 {
237  vector v = v1 - v2;
238  double sl = v.sqlength();
239  double t;
240 
241  if(sl < EPSILON) return(0.0); // parallel tracks, any time is ok.
242 
243  t = -v.dot(x1 - x2) / sl;
244  if(t < 0.0) return(0.0); // nearest time was in the past, now
245  // is closest point from now on.
246 
247  return(t);
248 }
249 
250 // Ported from: dist3D_Segment_to_Segment
251 // from http://geometryalgorithms.com
252 // Copyright 2001, softSurfer (www.softsurfer.com)
253 // This code may be freely used and modified for any purpose providing
254 // that this copyright notice is included with it. SoftSurfer makes
255 // no warranty for this code, and cannot be held liable for any real
256 // or imagined damage resulting from its use. Users of this code must
257 // verify correctness for their application.
258 template <class vector>
259 double distance_seg_to_seg(vector s1a,vector s1b,vector s2a,vector s2b)
260 // return distnace between segments s1a-s1b and s2a-s2b
261 {
262  vector dp;
263  vector u = s1b - s1a;
264  vector v = s2b - s2a;
265  vector w = s1a - s2a;
266  float a = dot(u,u); // always >= 0
267  float b = dot(u,v);
268  float c = dot(v,v); // always >= 0
269  float d = dot(u,w);
270  float e = dot(v,w);
271  float D = a*c - b*b; // always >= 0
272  float sc, sN, sD = D; // sc = sN / sD, default sD = D >= 0
273  float tc, tN, tD = D; // tc = tN / tD, default tD = D >= 0
274 
275  if(true){
276  printf("SegDist (%f,%f)-(%f,%f) to (%f,%f)-(%f,%f) a=%f b=%f\n",
277  V2COMP(s1a),V2COMP(s1b),V2COMP(s2a),V2COMP(s2b),
278  a,b);
279  }
280 
281  if((a < EPSILON) || (b < EPSILON)){
282  if((a < EPSILON) && (b < EPSILON)){
283  return(dist(s1a,s2a));
284  }else if(a < EPSILON){
285  return(distance_to_segment(s2a,s2b,s1a));
286  }else{
287  return(distance_to_segment(s1a,s1b,s2a));
288  }
289  }
290 
291  // compute the line parameters of the two closest points
292  if(D < EPSILON){ // the lines are almost parallel
293  sN = 0.0; // force using point P0 on segment S1
294  sD = 1.0; // to prevent possible division by 0.0 later
295  tN = e;
296  tD = c;
297  }else{ // get the closest points on the infinite lines
298  sN = (b*e - c*d);
299  tN = (a*e - b*d);
300  if(sN < 0){ // sc < 0 => the s=0 edge is visible
301  sN = 0.0;
302  tN = e;
303  tD = c;
304  }else if(sN > sD){ // sc > 1 => the s=1 edge is visible
305  sN = sD;
306  tN = e + b;
307  tD = c;
308  }
309  }
310 
311  if(tN < 0){ // tc < 0 => the t=0 edge is visible
312  tN = 0.0;
313  // recompute sc for this edge
314  if(-d < 0){
315  sN = 0.0;
316  }else if(-d > a){
317  sN = sD;
318  }else{
319  sN = -d;
320  sD = a;
321  }
322  }else if(tN > tD){ // tc > 1 => the t=1 edge is visible
323  tN = tD;
324  // recompute sc for this edge
325  if((-d + b) < 0){
326  sN = 0;
327  }else if((-d + b) > a){
328  sN = sD;
329  }else{
330  sN = (-d + b);
331  sD = a;
332  }
333  }
334 
335 
336  // finally do the division to get sc and tc
337  // sc = sN / sD;
338  // tc = tN / tD;
339  sc = (fabs(sN) < EPSILON)? 0.0 : sN / sD;
340  tc = (fabs(tN) < EPSILON)? 0.0 : tN / tD;
341 
342  // get the difference of the two closest points
343  dp = w + u*sc - v*tc; // = S1(sc) - S2(tc)
344 
345  return(dp.length()); // return the closest distance
346 }
347 
348 // Inputs: plane origin, plane normal, ray origin ray vector.
349 // NOTE: both vectors are assumed to be normalized
350 template <class vector>
351 double ray_plane_intersect(vector pOrigin,vector pNormal,
352  vector rOrigin,vector rVector)
353 {
354  //FIXME: if they're parallel, this will fail!
355  return((dot(pNormal,pOrigin) - dot(pNormal,rOrigin)) /
356  dot(pNormal,rVector));
357 
358  /*
359  double numer = dot(pNormal,rOrigin) - dot(pNormal,pOrigin);
360  double denom = dot(pNormal,rVector);
361  return(-(numer / denom));
362  */
363 }
364 
365 
366 /*template <class vector>
367 double intersect(vector rOrigin, vector rVector, vector a, vector b)
368 {
369 
370  double t = ((rVector.x * rOrigin.y + rVector.y *
371  (a.x - rOrigin.x)) -
372  (rVector.x * b.y)) /
373  (rVector.y * (a.x + b.x) -
374  rVector.x * (a.y + b.y));
375 
376  return t;
377 
378 }*/
379 
380 
381 
382 
383 
384 template <class vector, class real>
385 real ray_sphere_intersect(vector rO, vector rV,vector sO,real sR)
386 // intersect a ray (r0+rV*t) with a sphere at s0 with radius sR
387 // returns: t, or -1 if no intersection
388 // NOTE: rV is assumed to be normalized
389 {
390  vector Q = sO - rO;
391  real c = Q.length();
392  real v = dot(Q,rV);
393  real d = sR*sR - (c*c - v*v);
394 
395  // If there was no intersection, return -1
396  if(d < 0.0) return(-1.0);
397 
398  // Return the distance to the [first] intersecting point
399  return(v - sqrt(d));
400 }
401 
402 template <class vector, class real>
403 real ray_stretched_circle_intersect(vector rO, vector rV,vector sO,real sR, vector stretchH)
404 // |---
405 // | |
406 // X-sR-| -------
407 // | | | stretchH
408 // s0---- -------
409 // | |
410 // X----|
411 // | |
412 // /---
413 // intersect a ray (r0+rV*t) with a stretched circle at s0 with end radii sR and 2*stretchH between its two centers
414 // returns: t, or -1 if no intersection
415 // NOTE: rV is assumed to be normalized
416 // yes, it's nasty code...but needed for the new defense area!
417 {
418 
419  real t=(-1.0);
420  vector p;
421  bool found=false;
422  //positive circle:
423  real t1=ray_sphere_intersect(rO, rV,sO+stretchH,sR);
424  if (t1 >= 0.0) {
425  found=true;
426  t=t1;
427  p=(rO+(rV*t));
428  }
429 
430  //negative circle:
431  real t2=ray_sphere_intersect(rO, rV,sO-stretchH,sR);
432  if (t2 >= 0.0) {
433  if (found==false || t2 < t) {
434  found=true;
435  t=t2;
436  p=(rO+(rV*t));
437  }
438  }
439 
440  //rotate stretchH by 90 degrees.
441  //compute two planes:
442  //one left, one right
443  vector perp=(stretchH.perp()).norm(sR);
444 
445  real t3=ray_plane_intersect(sO+perp,perp,rO,rV);
446  if (t3 >= 0.0) {
447  if (found==false || t3 < t) {
448  vector ptmp=(rO+(rV*t3));
449  //check if intersection point is within the line-segment:
450  if ((ptmp - (sO+perp)).sqlength() < stretchH.sqlength()) {
451  found=true;
452  t=t3;
453  p=ptmp;
454  }
455  }
456  }
457 
458 
459  perp*=(-1.0);
460  real t4=ray_plane_intersect(sO+perp,perp,rO,rV);
461  if (t4 >= 0.0) {
462  if (found==false || t4 < t) {
463  vector ptmp=(rO+(rV*t4));
464  //check if intersection point is within the line-segment:
465  if ((ptmp - (sO+perp)).sqlength() < stretchH.sqlength()) {
466  found=true;
467  t=t4;
468  p=ptmp;
469  }
470  }
471  }
472 
473  return t;
474 }
475 
476 
477 template <class vector, class real>
478 bool CircleTangentDir(vector cen,real rad,vector p,
479  vector &left,vector &right)
480 {
481  vector delta,basis;
482  vector tl,tr;
483  double d2,l,t;
484 
485  // l,r,d form a right triangle where l*l + rad*rad = d*d = d2
486  delta = cen - p;
487  d2 = delta.sqlength();
488 
489  t = d2 - rad*rad;
490  if(t < 0) return(false); // inside circle
491 
492  // set basis as sin/d,cos/d
493  l = sqrt(t);
494  basis.set(l/d2,rad/d2);
495 
496  // project to get left and right tangents
497  // resulting vectors are normalized
498  left = basis.project_out(delta);
499  basis.y = -basis.y;
500  right = basis.project_out(delta);
501 
502  return(true);
503 }
504 
505 template <class vector, class real>
506 bool CircleTangent(vector cen,real rad,vector p,
507  vector &left,vector &right)
508 {
509  vector delta,basis;
510  vector tl,tr;
511  double d2,l,t;
512 
513  // l,r,d form a right triangle where l*l + rad*rad = d*d = d2
514  delta = cen - p;
515  d2 = delta.sqlength();
516 
517  t = d2 - rad*rad;
518  if(t < 0) return(false); // inside circle
519 
520  // set basis as sin/d,cos/d
521  l = sqrt(t);
522  basis.set(l/d2,rad/d2);
523 
524  // project to get left and right tangents
525  // resulting vectors are normalized
526  left = basis.project_out(delta)*l;
527  basis.y = -basis.y;
528  right = basis.project_out(delta)*l;
529 
530  return(true);
531 }
532 
533 // calculate the unit vector pointing at the "midpoint" of a line
534 // based on splitting the angle from the origin. The unit vector is
535 // returned, while the distance is stored in <dist>. dir*dist lies on
536 // the line p0-p1.
537 template <class vector, class real>
538 vector LineMidpointAngular(const vector &p0,const vector &p1,real &dist)
539 {
540  vector dir = (p0.norm() + p1.norm()).norm();
541  vector pn = (p1 - p0).norm();
542  dist = pn.perpdot(p0) / pn.perpdot(dir);
543  return(dir);
544 }
545 
546 }; // namespace
547 
548 #endif
void set(num nx, num ny)
set the components of the vector
Definition: gvector.h:768
num angle() const MustUseResult
calculate the clockwise angle from <1,0>
Definition: gvector.h:799
void normalize()
normalize to unit length in place
Definition: gvector.h:916
num sqlength() const MustUseResult
calculate squared Euclidean length (faster than length())
Definition: gvector.h:887
vector2d< num > norm() const MustUseResult
return a unit length vector in the same direction
Definition: gvector.h:892
vector2d< num > rotate(const double a) const MustUseResult
return vector rotated by angle a
Definition: gvector.h:975
num dot(const vec p) const MustUseResult
return dot product of vector with p
Definition: gvector.h:942