line.c 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. // Muchas gracias, Inigo.
  2. // https://iquilezles.org/articles/distfunctions2d/
  3. float vDistPointLine2(Vector2 p, Line2 ls) {
  4. Vector2 pa = vSub2(p, ls.start); // vector from the starting point to p
  5. Vector2 ba = vSub2(ls.end, ls.start); // vector from the starting point to the ending point
  6. float t = vDot2(pa, ba) / vDot2(ba, ba); // project the pa onto ba, then divide that distance by the length of ba to normalize it
  7. fclamp(t, 0.0, 1.0); // clamp t to between the endpoints of the line segment
  8. // Consider the starting point to be at the origin, for ease of visualization.
  9. // ba is the vector from the origin to the endpoint og the line that now passes through the origin.
  10. // Scaling ba by t gives the intercept point of the line through p that is perpendicular to the test line segment.
  11. // pa is p if a was the origin. Therefore, pi is the vector from p to the intercept point on the test line segment.
  12. Vector2 pi = vSub2(pa, vScale2(ba, t));
  13. return vMag2(pi); // the answer is the length of pi
  14. }
  15. float vDistPointLine3(Vector3 p, Line3 ls) {
  16. Vector3 pa = vSub3(p, ls.start);
  17. Vector3 ba = vSub3(ls.end, ls.start);
  18. float t = fclamp(vDot3(pa, ba) / vDot3(ba, ba), 0.0, 1.0);
  19. return vMag3(vSub3(pa, vScale3(ba, t)));
  20. }
  21. // This version also returns the normalized distance along the line of the closest point
  22. float vDistTPointLine2(Vector2 p, Line2 ls, float* T) {
  23. Vector2 pa = vSub2(p, ls.start);
  24. Vector2 ba = vSub2(ls.end, ls.start);
  25. float t = fclamp(vDot2(pa, ba) / vDot2(ba, ba), 0.0, 1.0);
  26. if(T) *T = t;
  27. return vMag2(vSub2(pa, vScale2(ba, t)));
  28. }
  29. float vDistTPointLine3(Vector3 p, Line3 ls, float* T) {
  30. Vector3 pa = vSub3(p, ls.start);
  31. Vector3 ba = vSub3(ls.end, ls.start);
  32. float t = fclamp(vDot3(pa, ba) / vDot3(ba, ba), 0.0, 1.0);
  33. if(T) *T = t;
  34. return vMag3(vSub3(pa, vScale3(ba, t)));
  35. }
  36. // ----
  37. float projPointLine2(Vector2 p, Line2 ls) {
  38. Vector2 pa = vSub2(p, ls.start);
  39. Vector2 ba = vSub2(ls.end, ls.start);
  40. return vDot2(pa, ba) / vDot2(ba, ba);
  41. }
  42. float distLineLine3(Line3* a, Line3* b) {
  43. Vector3 ea = vSub3(a->end, a->start);
  44. Vector3 eb = vSub3(b->end, b->start);
  45. Vector3 q = vSub(b->start, a->start);
  46. float vaa = vLenSq3(ea);
  47. float vbb = vLenSq3(eb);
  48. float vba = vDot3(ea, eb);
  49. float vba_a = vDot3(q, ea);
  50. float den = vba * vba - vbb * vaa;
  51. float ta, tb;
  52. if(fabs(den) < 1e-6) {
  53. ta = 0;
  54. tb = -vba_a / vba; // vba can never be zero here
  55. }
  56. else {
  57. float vba_b = vDot3(q, eb);
  58. ta = (vba_b * vba - vbb * vba_a) / den;
  59. tb = (-vba_a * vba + vaa * vba_b) / den;
  60. }
  61. ta = fclamp(0, 1, ta);
  62. tb = fclamp(0, 1, tb);
  63. Vector3 pa = vAdd(a->start, vScale(ea, ta));
  64. Vector3 pb = vAdd(b->start, vScale(eb, tb));
  65. return vDist(pa, pb);
  66. }
  67. /*
  68. float distLineLine3Slow(Line3* a, Line3* b) {
  69. Vector3 ea = vSub3(a->end, a->start);
  70. Vector3 eb = vSub3(b->end, b->start);
  71. Vector3 n = vCross3(ea, eb);
  72. float nsq = vLenSq3(n);
  73. // TODO: handle parallel lines
  74. vec3 b1ma1 = vSub(b->start, a->start);
  75. float ta = vDot3(vCross3(eb, n), b1ma1) / nsq;
  76. float tb = vDot3(vCross3(ea, n), b1ma1) / nsq;
  77. ta = fclamp(0, 1, ta);
  78. tb = fclamp(0, 1, tb);
  79. vec3 pa = vAdd(a->start, vScale(ea, ta));
  80. vec3 pb = vAdd(b->start, vScale(eb, tb));
  81. return vDist3(pa, pb);
  82. }
  83. */
  84. //
  85. Line3 shortestLineFromLineToLine(Line3* a, Line3* b) {
  86. Vector3 ea = vSub3(a->end, a->start);
  87. Vector3 eb = vSub3(b->end, b->start);
  88. Vector3 q = vSub(b->start, a->start);
  89. float vaa = vLenSq3(ea);
  90. float vbb = vLenSq3(eb);
  91. float vba = vDot3(ea, eb);
  92. float vba_a = vDot3(q, ea);
  93. float den = vba * vba - vbb * vaa;
  94. float ta, tb;
  95. if(fabs(den) < 1e-6) {
  96. ta = 0;
  97. tb = -vba_a / vba; // vba can never be zero here
  98. }
  99. else {
  100. float vba_b = vDot3(q, eb);
  101. ta = (vba_b * vba - vbb * vba_a) / den;
  102. tb = (-vba_a * vba + vaa * vba_b) / den;
  103. }
  104. ta = fclamp(ta, 0, 1);
  105. tb = fclamp(tb, 0, 1);
  106. Vector3 pa = vAdd(a->start, vScale(ea, ta));
  107. Vector3 pb = vAdd(b->start, vScale(eb, tb));
  108. return (Line3){pa, pb};
  109. }
  110. // C3DLAS_COPLANAR, _PARALLEL, _INTERSECT, or _DISJOINT
  111. // aboveCnt and belowCnt are always set.
  112. int linePlaneClip3p(
  113. Vector3* la,
  114. Vector3* lb,
  115. Plane* pl,
  116. Vector3* aboveOut,
  117. Vector3* belowOut,
  118. int* aboveCnt,
  119. int* belowCnt
  120. ) {
  121. Vector3 ldir, c;
  122. float da, db;
  123. vSub3p(lb, la, &ldir);
  124. da = vDot3p(la, &pl->n) - pl->d;
  125. // bail if the line and plane are parallel
  126. if(fabs(vDot3p(&pl->n, &ldir)) < FLT_CMP_EPSILON) {
  127. *aboveCnt = 0;
  128. *belowCnt = 0;
  129. // check coplanarity
  130. if(fabs(da) < FLT_CMP_EPSILON) {
  131. return C3DLAS_COPLANAR; // the end is on the plane, so the other is too
  132. }
  133. return C3DLAS_PARALLEL;
  134. }
  135. db = vDot3p(lb, &pl->n) - pl->d;
  136. // check if one of the points is on the plane
  137. if(fabs(da) < FLT_CMP_EPSILON) {
  138. if(db > 0) {
  139. aboveOut[0] = *la; // correct ordering
  140. aboveOut[1] = *lb;
  141. *aboveCnt = 1;
  142. *belowCnt = 0;
  143. }
  144. else {
  145. belowOut[0] = *la; // correct ordering
  146. belowOut[1] = *lb;
  147. *aboveCnt = 0;
  148. *belowCnt = 1;
  149. }
  150. return C3DLAS_INTERSECT;
  151. }
  152. if(fabs(db) < FLT_CMP_EPSILON) {
  153. if(da > 0) {
  154. aboveOut[0] = *la; // correct ordering
  155. aboveOut[1] = *lb;
  156. *aboveCnt = 1;
  157. *belowCnt = 0;
  158. }
  159. else {
  160. belowOut[0] = *la; // correct ordering
  161. belowOut[1] = *lb;
  162. *aboveCnt = 0;
  163. *belowCnt = 1;
  164. }
  165. return C3DLAS_INTERSECT;
  166. }
  167. // calculate itnersection point, c
  168. Vector3 p0, g, j;
  169. vScale3p(&pl->n, pl->d, &p0);
  170. vSub3p(&p0, la, &g);
  171. float h = vDot3p(&g, &pl->n);
  172. float i = vDot3p(&ldir, &pl->n);
  173. float d = i != 0 ? h / i : 0;
  174. // check if the plane intersects outside the two points
  175. if(d < 0 || d > vDist3p(la, lb)) {
  176. if(da > 0) {
  177. aboveOut[0] = *la; // correct ordering
  178. aboveOut[1] = *lb;
  179. *aboveCnt = 1;
  180. *belowCnt = 0;
  181. }
  182. else {
  183. belowOut[0] = *la; // correct ordering
  184. belowOut[1] = *lb;
  185. *aboveCnt = 0;
  186. *belowCnt = 1;
  187. }
  188. return C3DLAS_DISJOINT;
  189. }
  190. vScale3p(&ldir, d, &j);
  191. vAdd3p(la, &j, &c);
  192. if(da > 0) {
  193. aboveOut[0] = *la; // correct ordering
  194. aboveOut[1] = c;
  195. belowOut[0] = c;
  196. belowOut[1] = *lb;
  197. }
  198. else {
  199. belowOut[0] = *la; // correct ordering
  200. belowOut[1] = c;
  201. aboveOut[0] = c;
  202. aboveOut[1] = *lb;
  203. }
  204. *aboveCnt = 1;
  205. *belowCnt = 1;
  206. return C3DLAS_INTERSECT;
  207. }