line.c 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  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) / sqrtf(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. Line2 shortestLineFromLineToLine2(Line2* a, Line2* b) {
  86. float s, t;
  87. vec2 da = vSub(a->end, a->start);
  88. vec2 db = vSub(b->end, b->start);
  89. // first check if they intersect
  90. float det = vCross2(da, db);
  91. if(fabsf(det) > 1e-5f) {
  92. float invdet = 1.f / det;
  93. float x02 = a->start.x - b->start.x;
  94. float y02 = a->start.y - b->start.y;
  95. s = (da.x * y02 - da.y * x02) * invdet;
  96. if(s >= 0.f && s <= 1.f) {
  97. t = (db.x * y02 - db.y * x02) * invdet;
  98. if(t >= 0.f && t <= 1.f) {
  99. vec2 pa = vAdd(a->start, vScale(da, t));
  100. return (Line2){pa, pa};
  101. }
  102. }
  103. }
  104. // otherwise get the closest point
  105. vec2 ab = vSub(b->start, a->start);
  106. float d_abda = vDot(ab, da);
  107. float d_abdb = vDot(ab, db);
  108. float d_dadb = vDot(da, db);
  109. float l2_da = vLenSq(da);
  110. float l2_db = vLenSq(db);
  111. float invl2_da = 1.f / l2_da;
  112. float den = l2_da * l2_db - d_dadb * d_dadb;
  113. if(den < 1e-6f * l2_db * l2_da) {
  114. s = fclamp(d_abda * invl2_da, 0, 1);
  115. t = 0;
  116. }
  117. else {
  118. float invden = 1.f / den;
  119. s = fclamp((d_abda * l2_db - d_abdb * d_dadb) * invden, 0, 1);
  120. t = fclamp((d_abda * d_dadb - d_abdb * l2_da) * invden, 0, 1);
  121. }
  122. return (Line2){
  123. vAdd(a->start, vScale(da, fclamp((t * d_dadb + d_abda) * invl2_da, 0, 1))),
  124. vAdd(b->start, vScale(db, fclamp((s * d_dadb - d_abdb) / l2_db, 0, 1)))
  125. };
  126. }
  127. ////
  128. // BUG, maybe: The 2d port of this gave wrong results
  129. Line3 shortestLineFromLineToLine(Line3* a, Line3* b) {
  130. Vector3 ea = vSub3(a->end, a->start);
  131. Vector3 eb = vSub3(b->end, b->start);
  132. Vector3 q = vSub(b->start, a->start);
  133. float vaa = vLenSq3(ea);
  134. float vbb = vLenSq3(eb);
  135. float vba = vDot3(ea, eb);
  136. float vba_a = vDot3(q, ea);
  137. float den = vba * vba - vbb * vaa;
  138. float ta, tb;
  139. if(fabs(den) < 1e-6) {
  140. ta = 0;
  141. tb = -vba_a / vba; // vba can never be zero here
  142. }
  143. else {
  144. float vba_b = vDot3(q, eb);
  145. ta = (vba_b * vba - vbb * vba_a) / den;
  146. tb = (-vba_a * vba + vaa * vba_b) / den;
  147. }
  148. ta = fclamp(ta, 0, 1);
  149. tb = fclamp(tb, 0, 1);
  150. Vector3 pa = vAdd(a->start, vScale(ea, ta));
  151. Vector3 pb = vAdd(b->start, vScale(eb, tb));
  152. return (Line3){pa, pb};
  153. }
  154. // C3DLAS_COPLANAR, _PARALLEL, _INTERSECT, or _DISJOINT
  155. // aboveCnt and belowCnt are always set.
  156. int linePlaneClip3p(
  157. Vector3* la,
  158. Vector3* lb,
  159. Plane* pl,
  160. Vector3* aboveOut,
  161. Vector3* belowOut,
  162. int* aboveCnt,
  163. int* belowCnt
  164. ) {
  165. Vector3 ldir, c;
  166. float da, db;
  167. vSub3p(lb, la, &ldir);
  168. da = vDot3p(la, &pl->n) - pl->d;
  169. // bail if the line and plane are parallel
  170. if(fabs(vDot3p(&pl->n, &ldir)) < FLT_CMP_EPSILON) {
  171. *aboveCnt = 0;
  172. *belowCnt = 0;
  173. // check coplanarity
  174. if(fabs(da) < FLT_CMP_EPSILON) {
  175. return C3DLAS_COPLANAR; // the end is on the plane, so the other is too
  176. }
  177. return C3DLAS_PARALLEL;
  178. }
  179. db = vDot3p(lb, &pl->n) - pl->d;
  180. // check if one of the points is on the plane
  181. if(fabs(da) < FLT_CMP_EPSILON) {
  182. if(db > 0) {
  183. aboveOut[0] = *la; // correct ordering
  184. aboveOut[1] = *lb;
  185. *aboveCnt = 1;
  186. *belowCnt = 0;
  187. }
  188. else {
  189. belowOut[0] = *la; // correct ordering
  190. belowOut[1] = *lb;
  191. *aboveCnt = 0;
  192. *belowCnt = 1;
  193. }
  194. return C3DLAS_INTERSECT;
  195. }
  196. if(fabs(db) < FLT_CMP_EPSILON) {
  197. if(da > 0) {
  198. aboveOut[0] = *la; // correct ordering
  199. aboveOut[1] = *lb;
  200. *aboveCnt = 1;
  201. *belowCnt = 0;
  202. }
  203. else {
  204. belowOut[0] = *la; // correct ordering
  205. belowOut[1] = *lb;
  206. *aboveCnt = 0;
  207. *belowCnt = 1;
  208. }
  209. return C3DLAS_INTERSECT;
  210. }
  211. // calculate itnersection point, c
  212. Vector3 p0, g, j;
  213. vScale3p(&pl->n, pl->d, &p0);
  214. vSub3p(&p0, la, &g);
  215. float h = vDot3p(&g, &pl->n);
  216. float i = vDot3p(&ldir, &pl->n);
  217. float d = i != 0 ? h / i : 0;
  218. // check if the plane intersects outside the two points
  219. if(d < 0 || d > vDist3p(la, lb)) {
  220. if(da > 0) {
  221. aboveOut[0] = *la; // correct ordering
  222. aboveOut[1] = *lb;
  223. *aboveCnt = 1;
  224. *belowCnt = 0;
  225. }
  226. else {
  227. belowOut[0] = *la; // correct ordering
  228. belowOut[1] = *lb;
  229. *aboveCnt = 0;
  230. *belowCnt = 1;
  231. }
  232. return C3DLAS_DISJOINT;
  233. }
  234. vScale3p(&ldir, d, &j);
  235. vAdd3p(la, &j, &c);
  236. if(da > 0) {
  237. aboveOut[0] = *la; // correct ordering
  238. aboveOut[1] = c;
  239. belowOut[0] = c;
  240. belowOut[1] = *lb;
  241. }
  242. else {
  243. belowOut[0] = *la; // correct ordering
  244. belowOut[1] = c;
  245. aboveOut[0] = c;
  246. aboveOut[1] = *lb;
  247. }
  248. *aboveCnt = 1;
  249. *belowCnt = 1;
  250. return C3DLAS_INTERSECT;
  251. }