123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 |
- /*************************************************************************
- * Copyright (c) 2011 AT&T Intellectual Property
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * https://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors: Details at https://graphviz.org
- *************************************************************************/
- #include <edgepaint/intersection.h>
- #include <sparse/general.h>
- #include <math.h>
- static double cross(double *u, double *v){
- return u[0]*v[1] - u[1]*v[0];
- }
- /*
- There's a nice approach to this problem that uses vector cross
- products. Define the 2-dimensional vector cross product v * w to be
- vxwy \[Minus] vywx (this is the magnitude of the 3-dimensional cross
- product).
- Suppose the two line segments run from p to p + r and from q to q +s.
- Then any point on the first line is representable as p + t r (for a
- scalar parameter t) and any point on the second line as q + u s (for a
- scalar parameter u).
- The two lines intersect if we can find t and u such that:
- p + t r = q + u s
- cross both sides with s, getting
- (p + t r) * s = (q + u s) * s
- And since s * s = 0, this means
- t(r * s) = (q \[Minus] p) * s
- And therefore, solving for t:
- t = (q \[Minus] p) * s / (r * s)
- In the same way, we can solve for u:
- u = (q \[Minus] p) * r / (r * s)
- Now if r * s = 0 then the two lines are parallel.
- (There are two cases: if (q \[Minus] p) * r = 0 too,
- then the lines are collinear, otherwise they never intersect.)
- Otherwise the intersection point is on the original pair
- of line segments if 0 <= t <= 1 and 0 <= u <= 1.
- */
- static double dist(int dim, double *x, double *y){
- int k;
- double d = 0;
- for (k = 0; k < dim; k++) d += (x[k] - y[k])*(x[k]-y[k]);
- return sqrt(d);
- }
- static double point_line_distance(double *p, double *q, double *r){
- /* distance between point p and line q--r */
- enum {dim = 2};
- double t = 0, b = 0;
- int i;
- double tmp;
-
- /* t = ((p - q).(r - q))/((r - q).(r - q)) gives the position of the project of p on line r--q */
- for (i = 0; i < dim; i++){
- t += (p[i] - q[i])*(r[i] - q[i]);
- b += (r[i] - q[i])*(r[i] - q[i]);
- }
- if (b <= MACHINEACC) return dist(dim, p, q);
- t = t/b;
- /* pointLine = Norm[p - (q + t (r - q))];
- If [t >= 0 && t <= 1, pointLine, Min[{Norm[p - q], Norm[p - r]}]]];
- */
- if (t >= 0 && t <= 1){
- b = 0;
- for (i = 0; i < dim; i++){
- tmp = p[i] - (q[i] + t*(r[i] - q[i]));
- b += tmp*tmp;
- }
- return sqrt(b);
- }
- t = dist(dim, p, q);
- b = dist(dim, p, r);
- return MIN(t, b);
- }
- static double line_segments_distance(double *p1, double *p2, double *q1, double *q2){
- /* distance between line segments p1--p2 and q1--q2 */
- double t1, t2, t3, t4;
- t1 = point_line_distance(p1, q1, q2);
- t2 = point_line_distance(p2, q1, q2);
- t3 = point_line_distance(q1, p1, p2);
- t4 = point_line_distance(q2, p1, p2);
- t1 = MIN(t1,t2);
- t3 = MIN(t3,t4);
- return MIN(t1, t3);
- }
-
- double intersection_angle(double *p1, double *p2, double *q1, double *q2){
- /* give two lines p1--p2 and q1--q2, find their intersection agle
- and return Abs[Cos[theta]] of that angle.
- - If the two lines are very close, treat as if they intersect.
- - If they do not intersect or being very close, return -2.
- - If the return value is close to 1, the two lines intersects and is close to an angle of 0 o Pi;
- . lines that intersect at close to 90 degree give return value close to 0
- - in the special case of two lines sharing an end point, we return Cos[theta], instead of
- . the absolute value, where theta
- . is the angle of the two rays emitting from the shared end point, thus the value can be
- . from -1 to 1.
- */
- enum {dim = 2};
- double r[dim], s[dim], qp[dim];
- double rnorm = 0, snorm = 0, b, t, u;
- // double epsilon = sqrt(MACHINEACC), close = 0.01;
- //this may be better. Apply to ngk10_4 and look at double edge between 28 and 43. double epsilon = sin(10/180.), close = 0.1;
- double epsilon = sin(1/180.), close = 0.01;
- int line_dist_close;
- int i;
- double res;
- for (i = 0; i < dim; i++) {
- r[i] = p2[i] - p1[i];
- rnorm += r[i]*r[i];
- }
- rnorm = sqrt(rnorm);
- for (i = 0; i < dim; i++) {
- s[i] = q2[i] - q1[i];
- snorm += s[i]*s[i];
- }
- snorm = sqrt(snorm);
- b = cross(r, s);
- line_dist_close = (line_segments_distance(p1, p2, q1, q2) <= close*MAX(rnorm, snorm));
- if (fabs(b) <= epsilon*snorm*rnorm){/* parallel */
- if (line_dist_close) {/* two parallel lines that are close */
- return 1;
- }
- return -2;/* parallel but not close */
- }
- for (i = 0; i < dim; i++) qp[i] = q1[i] - p1[i];
- t = cross(qp, s)/b;
- u = cross(qp, r)/b;
- if ((t >= 0 && t <= 1 && u >= 0 && u <= 1) /* they intersect */
- || line_dist_close){/* or lines are close */
- double rs = 0;
- if (rnorm*snorm < MACHINEACC) return 0;
- for (i = 0; i < dim; i++){
- rs += r[i]*s[i];
- }
- res = rs/(rnorm*snorm);
- /* if the two lines share an end point */
- if (p1[0] == q1[0] && p1[1] == q1[1]){
- return res;
- } else if (p1[0] == q2[0] && p1[1] == q2[1]){
- return -res;
- } else if (p2[0] == q1[0] && p2[1] == q1[1]){
- return -res;
- } else if (p2[0] == q2[0] && p2[1] == q2[1]){
- return res;
- }
- /* normal case of intersect or very close */
- return fabs(res);
- }
- return -2;/* no intersection, and lines are not even close */
- }
-
|