intersection.c 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. /*************************************************************************
  2. * Copyright (c) 2011 AT&T Intellectual Property
  3. * All rights reserved. This program and the accompanying materials
  4. * are made available under the terms of the Eclipse Public License v1.0
  5. * which accompanies this distribution, and is available at
  6. * https://www.eclipse.org/legal/epl-v10.html
  7. *
  8. * Contributors: Details at https://graphviz.org
  9. *************************************************************************/
  10. #include <edgepaint/intersection.h>
  11. #include <sparse/general.h>
  12. #include <math.h>
  13. static double cross(double *u, double *v){
  14. return u[0]*v[1] - u[1]*v[0];
  15. }
  16. /*
  17. There's a nice approach to this problem that uses vector cross
  18. products. Define the 2-dimensional vector cross product v * w to be
  19. vxwy \[Minus] vywx (this is the magnitude of the 3-dimensional cross
  20. product).
  21. Suppose the two line segments run from p to p + r and from q to q +s.
  22. Then any point on the first line is representable as p + t r (for a
  23. scalar parameter t) and any point on the second line as q + u s (for a
  24. scalar parameter u).
  25. The two lines intersect if we can find t and u such that:
  26. p + t r = q + u s
  27. cross both sides with s, getting
  28. (p + t r) * s = (q + u s) * s
  29. And since s * s = 0, this means
  30. t(r * s) = (q \[Minus] p) * s
  31. And therefore, solving for t:
  32. t = (q \[Minus] p) * s / (r * s)
  33. In the same way, we can solve for u:
  34. u = (q \[Minus] p) * r / (r * s)
  35. Now if r * s = 0 then the two lines are parallel.
  36. (There are two cases: if (q \[Minus] p) * r = 0 too,
  37. then the lines are collinear, otherwise they never intersect.)
  38. Otherwise the intersection point is on the original pair
  39. of line segments if 0 <= t <= 1 and 0 <= u <= 1.
  40. */
  41. static double dist(int dim, double *x, double *y){
  42. int k;
  43. double d = 0;
  44. for (k = 0; k < dim; k++) d += (x[k] - y[k])*(x[k]-y[k]);
  45. return sqrt(d);
  46. }
  47. static double point_line_distance(double *p, double *q, double *r){
  48. /* distance between point p and line q--r */
  49. enum {dim = 2};
  50. double t = 0, b = 0;
  51. int i;
  52. double tmp;
  53. /* t = ((p - q).(r - q))/((r - q).(r - q)) gives the position of the project of p on line r--q */
  54. for (i = 0; i < dim; i++){
  55. t += (p[i] - q[i])*(r[i] - q[i]);
  56. b += (r[i] - q[i])*(r[i] - q[i]);
  57. }
  58. if (b <= MACHINEACC) return dist(dim, p, q);
  59. t = t/b;
  60. /* pointLine = Norm[p - (q + t (r - q))];
  61. If [t >= 0 && t <= 1, pointLine, Min[{Norm[p - q], Norm[p - r]}]]];
  62. */
  63. if (t >= 0 && t <= 1){
  64. b = 0;
  65. for (i = 0; i < dim; i++){
  66. tmp = p[i] - (q[i] + t*(r[i] - q[i]));
  67. b += tmp*tmp;
  68. }
  69. return sqrt(b);
  70. }
  71. t = dist(dim, p, q);
  72. b = dist(dim, p, r);
  73. return MIN(t, b);
  74. }
  75. static double line_segments_distance(double *p1, double *p2, double *q1, double *q2){
  76. /* distance between line segments p1--p2 and q1--q2 */
  77. double t1, t2, t3, t4;
  78. t1 = point_line_distance(p1, q1, q2);
  79. t2 = point_line_distance(p2, q1, q2);
  80. t3 = point_line_distance(q1, p1, p2);
  81. t4 = point_line_distance(q2, p1, p2);
  82. t1 = MIN(t1,t2);
  83. t3 = MIN(t3,t4);
  84. return MIN(t1, t3);
  85. }
  86. double intersection_angle(double *p1, double *p2, double *q1, double *q2){
  87. /* give two lines p1--p2 and q1--q2, find their intersection agle
  88. and return Abs[Cos[theta]] of that angle.
  89. - If the two lines are very close, treat as if they intersect.
  90. - If they do not intersect or being very close, return -2.
  91. - If the return value is close to 1, the two lines intersects and is close to an angle of 0 o Pi;
  92. . lines that intersect at close to 90 degree give return value close to 0
  93. - in the special case of two lines sharing an end point, we return Cos[theta], instead of
  94. . the absolute value, where theta
  95. . is the angle of the two rays emitting from the shared end point, thus the value can be
  96. . from -1 to 1.
  97. */
  98. enum {dim = 2};
  99. double r[dim], s[dim], qp[dim];
  100. double rnorm = 0, snorm = 0, b, t, u;
  101. // double epsilon = sqrt(MACHINEACC), close = 0.01;
  102. //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;
  103. double epsilon = sin(1/180.), close = 0.01;
  104. int line_dist_close;
  105. int i;
  106. double res;
  107. for (i = 0; i < dim; i++) {
  108. r[i] = p2[i] - p1[i];
  109. rnorm += r[i]*r[i];
  110. }
  111. rnorm = sqrt(rnorm);
  112. for (i = 0; i < dim; i++) {
  113. s[i] = q2[i] - q1[i];
  114. snorm += s[i]*s[i];
  115. }
  116. snorm = sqrt(snorm);
  117. b = cross(r, s);
  118. line_dist_close = (line_segments_distance(p1, p2, q1, q2) <= close*MAX(rnorm, snorm));
  119. if (fabs(b) <= epsilon*snorm*rnorm){/* parallel */
  120. if (line_dist_close) {/* two parallel lines that are close */
  121. return 1;
  122. }
  123. return -2;/* parallel but not close */
  124. }
  125. for (i = 0; i < dim; i++) qp[i] = q1[i] - p1[i];
  126. t = cross(qp, s)/b;
  127. u = cross(qp, r)/b;
  128. if ((t >= 0 && t <= 1 && u >= 0 && u <= 1) /* they intersect */
  129. || line_dist_close){/* or lines are close */
  130. double rs = 0;
  131. if (rnorm*snorm < MACHINEACC) return 0;
  132. for (i = 0; i < dim; i++){
  133. rs += r[i]*s[i];
  134. }
  135. res = rs/(rnorm*snorm);
  136. /* if the two lines share an end point */
  137. if (p1[0] == q1[0] && p1[1] == q1[1]){
  138. return res;
  139. } else if (p1[0] == q2[0] && p1[1] == q2[1]){
  140. return -res;
  141. } else if (p2[0] == q1[0] && p2[1] == q1[1]){
  142. return -res;
  143. } else if (p2[0] == q2[0] && p2[1] == q2[1]){
  144. return res;
  145. }
  146. /* normal case of intersect or very close */
  147. return fabs(res);
  148. }
  149. return -2;/* no intersection, and lines are not even close */
  150. }