bbox.ts 1.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
  1. import {
  2. vectorCross,
  3. vectorFromPoint,
  4. type GlobalPoint,
  5. type LocalPoint,
  6. } from "../math";
  7. import type { Bounds } from "../excalidraw/element/bounds";
  8. export type LineSegment<P extends LocalPoint | GlobalPoint> = [P, P];
  9. export function getBBox<P extends LocalPoint | GlobalPoint>(
  10. line: LineSegment<P>,
  11. ): Bounds {
  12. return [
  13. Math.min(line[0][0], line[1][0]),
  14. Math.min(line[0][1], line[1][1]),
  15. Math.max(line[0][0], line[1][0]),
  16. Math.max(line[0][1], line[1][1]),
  17. ];
  18. }
  19. export function doBBoxesIntersect(a: Bounds, b: Bounds) {
  20. return a[0] <= b[2] && a[2] >= b[0] && a[1] <= b[3] && a[3] >= b[1];
  21. }
  22. const EPSILON = 0.000001;
  23. export function isPointOnLine<P extends GlobalPoint | LocalPoint>(
  24. l: LineSegment<P>,
  25. p: P,
  26. ) {
  27. const p1 = vectorFromPoint(l[1], l[0]);
  28. const p2 = vectorFromPoint(p, l[0]);
  29. const r = vectorCross(p1, p2);
  30. return Math.abs(r) < EPSILON;
  31. }
  32. export function isPointRightOfLine<P extends GlobalPoint | LocalPoint>(
  33. l: LineSegment<P>,
  34. p: P,
  35. ) {
  36. const p1 = vectorFromPoint(l[1], l[0]);
  37. const p2 = vectorFromPoint(p, l[0]);
  38. return vectorCross(p1, p2) < 0;
  39. }
  40. export function isLineSegmentTouchingOrCrossingLine<
  41. P extends GlobalPoint | LocalPoint,
  42. >(a: LineSegment<P>, b: LineSegment<P>) {
  43. return (
  44. isPointOnLine(a, b[0]) ||
  45. isPointOnLine(a, b[1]) ||
  46. (isPointRightOfLine(a, b[0])
  47. ? !isPointRightOfLine(a, b[1])
  48. : isPointRightOfLine(a, b[1]))
  49. );
  50. }
  51. // https://martin-thoma.com/how-to-check-if-two-line-segments-intersect/
  52. export function doLineSegmentsIntersect<P extends GlobalPoint | LocalPoint>(
  53. a: LineSegment<P>,
  54. b: LineSegment<P>,
  55. ) {
  56. return (
  57. doBBoxesIntersect(getBBox(a), getBBox(b)) &&
  58. isLineSegmentTouchingOrCrossingLine(a, b) &&
  59. isLineSegmentTouchingOrCrossingLine(b, a)
  60. );
  61. }