math.ts 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. import { Point } from "roughjs/bin/geometry";
  2. // https://stackoverflow.com/a/6853926/232122
  3. export function distanceBetweenPointAndSegment(
  4. x: number,
  5. y: number,
  6. x1: number,
  7. y1: number,
  8. x2: number,
  9. y2: number,
  10. ) {
  11. const A = x - x1;
  12. const B = y - y1;
  13. const C = x2 - x1;
  14. const D = y2 - y1;
  15. const dot = A * C + B * D;
  16. const lenSquare = C * C + D * D;
  17. let param = -1;
  18. if (lenSquare !== 0) {
  19. // in case of 0 length line
  20. param = dot / lenSquare;
  21. }
  22. let xx, yy;
  23. if (param < 0) {
  24. xx = x1;
  25. yy = y1;
  26. } else if (param > 1) {
  27. xx = x2;
  28. yy = y2;
  29. } else {
  30. xx = x1 + param * C;
  31. yy = y1 + param * D;
  32. }
  33. const dx = x - xx;
  34. const dy = y - yy;
  35. return Math.hypot(dx, dy);
  36. }
  37. export function rotate(
  38. x1: number,
  39. y1: number,
  40. x2: number,
  41. y2: number,
  42. angle: number,
  43. ) {
  44. // 𝑎′𝑥=(𝑎𝑥−𝑐𝑥)cos𝜃−(𝑎𝑦−𝑐𝑦)sin𝜃+𝑐𝑥
  45. // 𝑎′𝑦=(𝑎𝑥−𝑐𝑥)sin𝜃+(𝑎𝑦−𝑐𝑦)cos𝜃+𝑐𝑦.
  46. // https://math.stackexchange.com/questions/2204520/how-do-i-rotate-a-line-segment-in-a-specific-point-on-the-line
  47. return [
  48. (x1 - x2) * Math.cos(angle) - (y1 - y2) * Math.sin(angle) + x2,
  49. (x1 - x2) * Math.sin(angle) + (y1 - y2) * Math.cos(angle) + y2,
  50. ];
  51. }
  52. export const getPointOnAPath = (point: Point, path: Point[]) => {
  53. const [px, py] = point;
  54. const [start, ...other] = path;
  55. let [lastX, lastY] = start;
  56. let kLine: number = 0;
  57. let idx: number = 0;
  58. // if any item in the array is true, it means that a point is
  59. // on some segment of a line based path
  60. const retVal = other.some(([x2, y2], i) => {
  61. // we always take a line when dealing with line segments
  62. const x1 = lastX;
  63. const y1 = lastY;
  64. lastX = x2;
  65. lastY = y2;
  66. // if a point is not within the domain of the line segment
  67. // it is not on the line segment
  68. if (px < x1 || px > x2) {
  69. return false;
  70. }
  71. // check if all points lie on the same line
  72. // y1 = kx1 + b, y2 = kx2 + b
  73. // y2 - y1 = k(x2 - x2) -> k = (y2 - y1) / (x2 - x1)
  74. // coefficient for the line (p0, p1)
  75. const kL = (y2 - y1) / (x2 - x1);
  76. // coefficient for the line segment (p0, point)
  77. const kP1 = (py - y1) / (px - x1);
  78. // coefficient for the line segment (point, p1)
  79. const kP2 = (py - y2) / (px - x2);
  80. // because we are basing both lines from the same starting point
  81. // the only option for collinearity is having same coefficients
  82. // using it for floating point comparisons
  83. const epsilon = 0.3;
  84. // if coefficient is more than an arbitrary epsilon,
  85. // these lines are nor collinear
  86. if (Math.abs(kP1 - kL) > epsilon && Math.abs(kP2 - kL) > epsilon) {
  87. return false;
  88. }
  89. // store the coefficient because we are goint to need it
  90. kLine = kL;
  91. idx = i;
  92. return true;
  93. });
  94. // Return a coordinate that is always on the line segment
  95. if (retVal === true) {
  96. return { x: point[0], y: kLine * point[0], segment: idx };
  97. }
  98. return null;
  99. };