@tscircuit/math-utils 0.0.15 → 0.0.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -64,6 +64,27 @@ function distance(p1, p2) {
64
64
  const dy = p1.y - p2.y;
65
65
  return Math.sqrt(dx * dx + dy * dy);
66
66
  }
67
+ function getSegmentIntersection(a, b, u, v) {
68
+ const dx1 = b.x - a.x;
69
+ const dy1 = b.y - a.y;
70
+ const dx2 = v.x - u.x;
71
+ const dy2 = v.y - u.y;
72
+ const dx3 = a.x - u.x;
73
+ const dy3 = a.y - u.y;
74
+ const denominator = dx1 * dy2 - dy1 * dx2;
75
+ if (Math.abs(denominator) < 1e-10) {
76
+ return null;
77
+ }
78
+ const t = (dy3 * dx2 - dx3 * dy2) / denominator;
79
+ const s = (dx1 * dy3 - dy1 * dx3) / denominator;
80
+ const epsilon = 1e-9;
81
+ if (t >= -epsilon && t <= 1 + epsilon && s >= -epsilon && s <= 1 + epsilon) {
82
+ const intersectionX = a.x + t * dx1;
83
+ const intersectionY = a.y + t * dy1;
84
+ return { x: intersectionX, y: intersectionY };
85
+ }
86
+ return null;
87
+ }
67
88
 
68
89
  export {
69
90
  doesLineIntersectLine,
@@ -71,6 +92,7 @@ export {
71
92
  orientation,
72
93
  onSegment,
73
94
  pointToSegmentDistance,
74
- distance
95
+ distance,
96
+ getSegmentIntersection
75
97
  };
76
- //# sourceMappingURL=chunk-CHQOCSFB.js.map
98
+ //# sourceMappingURL=chunk-3453HRP7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/line-intersections.ts"],"sourcesContent":["import type { Point } from \"./common\"\n\n/**\n * Returns true if the two lines intersect.\n */\nexport function doesLineIntersectLine(\n [a1, a2]: [Point, Point],\n [b1, b2]: [Point, Point],\n {\n lineThickness = 0,\n }: {\n lineThickness?: number\n } = {},\n): boolean {\n if (lineThickness === 0) {\n return doSegmentsIntersect(a1, a2, b1, b2)\n }\n const minDist = segmentsDistance(a1, a2, b1, b2)\n return minDist <= lineThickness\n}\n\n/**\n * Returns true if the two segments intersect.\n */\nexport function doSegmentsIntersect(\n p1: Point,\n q1: Point,\n p2: Point,\n q2: Point,\n): boolean {\n const o1 = orientation(p1, q1, p2)\n const o2 = orientation(p1, q1, q2)\n const o3 = orientation(p2, q2, p1)\n const o4 = orientation(p2, q2, q1)\n\n // General case\n if (o1 !== o2 && o3 !== o4) {\n return true\n }\n\n // Special Cases\n if (o1 === 0 && onSegment(p1, p2, q1)) return true\n if (o2 === 0 && onSegment(p1, q2, q1)) return true\n if (o3 === 0 && onSegment(p2, p1, q2)) return true\n if (o4 === 0 && onSegment(p2, q1, q2)) return true\n\n return false\n}\n\n/**\n * Returns 0 if the points are colinear, 1 if they are clockwise, and 2 if they are counterclockwise.\n */\nexport function orientation(p: Point, q: Point, r: Point): number {\n const val = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y)\n if (val === 0) return 0 // colinear\n return val > 0 ? 1 : 2 // clock or counterclock wise\n}\n\n/**\n * Returns true if q is on the segment p->r.\n */\nexport function onSegment(p: Point, q: Point, r: Point): boolean {\n return (\n q.x <= Math.max(p.x, r.x) &&\n q.x >= Math.min(p.x, r.x) &&\n q.y <= Math.max(p.y, r.y) &&\n q.y >= Math.min(p.y, r.y)\n )\n}\n\n/**\n * Returns the minimum distance between two segments.\n */\nfunction segmentsDistance(a1: Point, a2: Point, b1: Point, b2: Point): number {\n // Handle degenerate cases: segments of zero length\n if (a1.x === a2.x && a1.y === a2.y) {\n return pointToSegmentDistance(a1, b1, b2)\n }\n if (b1.x === b2.x && b1.y === b2.y) {\n return pointToSegmentDistance(b1, a1, a2)\n }\n\n // Check if segments intersect\n if (doSegmentsIntersect(a1, a2, b1, b2)) {\n return 0\n }\n\n // Compute the minimum distance between the segments\n const distances = [\n pointToSegmentDistance(a1, b1, b2),\n pointToSegmentDistance(a2, b1, b2),\n pointToSegmentDistance(b1, a1, a2),\n pointToSegmentDistance(b2, a1, a2),\n ]\n\n return Math.min(...distances)\n}\n\n/**\n * Returns the minimum distance between a point and a segment.\n */\nexport function pointToSegmentDistance(p: Point, v: Point, w: Point): number {\n const l2 = (w.x - v.x) ** 2 + (w.y - v.y) ** 2\n if (l2 === 0) return distance(p, v)\n\n let t = ((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / l2\n t = Math.max(0, Math.min(1, t))\n\n const projection = {\n x: v.x + t * (w.x - v.x),\n y: v.y + t * (w.y - v.y),\n }\n\n return distance(p, projection)\n}\n\n/**\n * Returns the distance between two points.\n */\nexport function distance(p1: Point, p2: Point): number {\n const dx = p1.x - p2.x\n const dy = p1.y - p2.y\n return Math.sqrt(dx * dx + dy * dy)\n}\n\n/**\n * Calculates the intersection point of two line segments.\n * Returns the intersection point {x, y} if the segments intersect, otherwise returns null.\n */\nexport function getSegmentIntersection(\n a: Point,\n b: Point,\n u: Point,\n v: Point,\n): Point | null {\n const dx1 = b.x - a.x\n const dy1 = b.y - a.y\n const dx2 = v.x - u.x\n const dy2 = v.y - u.y\n const dx3 = a.x - u.x\n const dy3 = a.y - u.y\n\n const denominator = dx1 * dy2 - dy1 * dx2\n\n // Check if lines are parallel or collinear\n if (Math.abs(denominator) < 1e-10) {\n // Lines are parallel or collinear\n // We could add checks for collinear overlapping segments if needed,\n // but for now, we return null as a single intersection point doesn't exist.\n // The doSegmentsIntersect function handles collinearity checks if only a boolean is needed.\n return null\n }\n\n // Correct formula for t (parameter for segment ab)\n // t = (dy3 * dx2 - dx3 * dy2) / denominator\n // The formula previously used was -(dy3 * dx2 - dx3 * dy2) / denominator\n const t = (dy3 * dx2 - dx3 * dy2) / denominator\n // Correct formula for s (parameter for segment uv)\n // s = (dx1 * dy3 - dy1 * dx3) / denominator\n // The formula previously used was incorrect.\n const s = (dx1 * dy3 - dy1 * dx3) / denominator\n\n // Check if the intersection point lies within both segments\n // Use a small epsilon for floating point comparisons near 0 and 1\n const epsilon = 1e-9\n if (t >= -epsilon && t <= 1 + epsilon && s >= -epsilon && s <= 1 + epsilon) {\n const intersectionX = a.x + t * dx1\n const intersectionY = a.y + t * dy1\n return { x: intersectionX, y: intersectionY }\n }\n\n // Segments do not intersect within their bounds\n return null\n}\n"],"mappings":"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","names":[]}
@@ -2,7 +2,7 @@ import {
2
2
  distance,
3
3
  doSegmentsIntersect,
4
4
  pointToSegmentDistance
5
- } from "./chunk-CHQOCSFB.js";
5
+ } from "./chunk-3453HRP7.js";
6
6
  import {
7
7
  clamp
8
8
  } from "./chunk-MHHTZHOJ.js";
@@ -88,11 +88,25 @@ function segmentToCircleMinDistance(a, b, circle) {
88
88
  const distToCenter = distance(closestPoint, circleCenter);
89
89
  return Math.max(0, distToCenter - circle.radius);
90
90
  }
91
+ function pointToSegmentClosestPoint(p, a, b) {
92
+ const dx_ab = b.x - a.x;
93
+ const dy_ab = b.y - a.y;
94
+ const l2 = dx_ab * dx_ab + dy_ab * dy_ab;
95
+ if (l2 === 0) return { x: a.x, y: a.y };
96
+ let t = ((p.x - a.x) * dx_ab + (p.y - a.y) * dy_ab) / l2;
97
+ t = Math.max(0, Math.min(1, t));
98
+ const closestPoint = {
99
+ x: a.x + t * dx_ab,
100
+ y: a.y + t * dy_ab
101
+ };
102
+ return closestPoint;
103
+ }
91
104
 
92
105
  export {
93
106
  segmentToSegmentMinDistance,
94
107
  segmentToBoundsMinDistance,
95
108
  segmentToBoxMinDistance,
96
- segmentToCircleMinDistance
109
+ segmentToCircleMinDistance,
110
+ pointToSegmentClosestPoint
97
111
  };
98
- //# sourceMappingURL=chunk-K7F26NYD.js.map
112
+ //# sourceMappingURL=chunk-FWQGMQBW.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/segment-distance.ts"],"sourcesContent":["import type { Point } from \"./common\"\nimport {\n distance,\n doSegmentsIntersect,\n pointToSegmentDistance,\n} from \"./line-intersections\"\nimport { clamp } from \"./nearest-box\"\n\n/**\n * Returns the minimum distance between two line segments.\n */\nexport function segmentToSegmentMinDistance(\n a: Point,\n b: Point,\n u: Point,\n v: Point,\n): number {\n // Handle degenerate cases: segments of zero length\n if (a.x === b.x && a.y === b.y) {\n return pointToSegmentDistance(a, u, v)\n }\n if (u.x === v.x && u.y === v.y) {\n return pointToSegmentDistance(u, a, b)\n }\n\n // Check if segments intersect\n if (doSegmentsIntersect(a, b, u, v)) {\n return 0\n }\n\n // Compute the minimum distance between the segments\n const distances = [\n pointToSegmentDistance(a, u, v),\n pointToSegmentDistance(b, u, v),\n pointToSegmentDistance(u, a, b),\n pointToSegmentDistance(v, a, b),\n ]\n\n return Math.min(...distances)\n}\n\n/**\n * Returns the minimum distance from a line segment to a bounding box.\n */\nexport function segmentToBoundsMinDistance(\n a: Point,\n b: Point,\n bounds: { minX: number; minY: number; maxX: number; maxY: number },\n): number {\n // Check if segment intersects with the bounds\n // Create the four edges of the bounds\n const topLeft = { x: bounds.minX, y: bounds.minY }\n const topRight = { x: bounds.maxX, y: bounds.minY }\n const bottomLeft = { x: bounds.minX, y: bounds.maxY }\n const bottomRight = { x: bounds.maxX, y: bounds.maxY }\n\n // Check if segment intersects with any of the bounds edges\n if (\n doSegmentsIntersect(a, b, topLeft, topRight) ||\n doSegmentsIntersect(a, b, topRight, bottomRight) ||\n doSegmentsIntersect(a, b, bottomRight, bottomLeft) ||\n doSegmentsIntersect(a, b, bottomLeft, topLeft)\n ) {\n return 0\n }\n\n // Check if segment is entirely inside the bounds\n if (\n a.x >= bounds.minX &&\n a.x <= bounds.maxX &&\n a.y >= bounds.minY &&\n a.y <= bounds.maxY &&\n b.x >= bounds.minX &&\n b.x <= bounds.maxX &&\n b.y >= bounds.minY &&\n b.y <= bounds.maxY\n ) {\n return 0\n }\n\n // If not intersecting, calculate the minimum distance\n const distances = [\n pointToSegmentDistance(topLeft, a, b),\n pointToSegmentDistance(topRight, a, b),\n pointToSegmentDistance(bottomLeft, a, b),\n pointToSegmentDistance(bottomRight, a, b),\n ]\n\n // If one of the segment endpoints is inside the bounds, we need to consider its distance to the bounds as 0\n if (\n a.x >= bounds.minX &&\n a.x <= bounds.maxX &&\n a.y >= bounds.minY &&\n a.y <= bounds.maxY\n ) {\n return 0\n }\n\n if (\n b.x >= bounds.minX &&\n b.x <= bounds.maxX &&\n b.y >= bounds.minY &&\n b.y <= bounds.maxY\n ) {\n return 0\n }\n\n // Calculate distances from segment endpoints to bounds if outside\n if (\n a.x < bounds.minX ||\n a.x > bounds.maxX ||\n a.y < bounds.minY ||\n a.y > bounds.maxY\n ) {\n const closestX = clamp(a.x, bounds.minX, bounds.maxX)\n const closestY = clamp(a.y, bounds.minY, bounds.maxY)\n distances.push(distance(a, { x: closestX, y: closestY }))\n }\n\n if (\n b.x < bounds.minX ||\n b.x > bounds.maxX ||\n b.y < bounds.minY ||\n b.y > bounds.maxY\n ) {\n const closestX = clamp(b.x, bounds.minX, bounds.maxX)\n const closestY = clamp(b.y, bounds.minY, bounds.maxY)\n distances.push(distance(b, { x: closestX, y: closestY }))\n }\n\n return Math.min(...distances)\n}\n\n/**\n * Returns the minimum distance from a line segment to a box.\n */\nexport function segmentToBoxMinDistance(\n a: Point,\n b: Point,\n box: { center: Point; width: number; height: number },\n): number {\n const halfWidth = box.width / 2\n const halfHeight = box.height / 2\n const bounds = {\n minX: box.center.x - halfWidth,\n maxX: box.center.x + halfWidth,\n minY: box.center.y - halfHeight,\n maxY: box.center.y + halfHeight,\n }\n\n return segmentToBoundsMinDistance(a, b, bounds)\n}\n\n/**\n * Returns the minimum distance from a line segment to a circle.\n */\nexport function segmentToCircleMinDistance(\n a: Point,\n b: Point,\n circle: { x: number; y: number; radius: number },\n): number {\n // Calculate the distance from the circle center to the line segment\n const circleCenter = { x: circle.x, y: circle.y }\n\n // Handle degenerate case: segment of zero length (point to circle)\n if (a.x === b.x && a.y === b.y) {\n return Math.max(0, distance(a, circleCenter) - circle.radius)\n }\n\n // Vector from a to b\n const ab = { x: b.x - a.x, y: b.y - a.y }\n // Vector from a to circle center\n const ac = { x: circleCenter.x - a.x, y: circleCenter.y - a.y }\n\n // Length of segment ab squared\n const abLengthSq = ab.x * ab.x + ab.y * ab.y\n\n // Calculate projection of ac onto ab, normalized by the length of ab\n const t = Math.max(0, Math.min(1, (ab.x * ac.x + ab.y * ac.y) / abLengthSq))\n\n // Find the closest point on the segment to the circle center\n const closestPoint = {\n x: a.x + t * ab.x,\n y: a.y + t * ab.y,\n }\n\n // Calculate distance from closest point to circle center\n const distToCenter = distance(closestPoint, circleCenter)\n\n // Return the distance to the circle (subtract radius from distance to center)\n return Math.max(0, distToCenter - circle.radius)\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/segment-distance.ts"],"sourcesContent":["import type { Point } from \"./common\"\nimport {\n distance,\n doSegmentsIntersect,\n pointToSegmentDistance,\n} from \"./line-intersections\"\nimport { clamp } from \"./nearest-box\"\n\n/**\n * Returns the minimum distance between two line segments.\n */\nexport function segmentToSegmentMinDistance(\n a: Point,\n b: Point,\n u: Point,\n v: Point,\n): number {\n // Handle degenerate cases: segments of zero length\n if (a.x === b.x && a.y === b.y) {\n return pointToSegmentDistance(a, u, v)\n }\n if (u.x === v.x && u.y === v.y) {\n return pointToSegmentDistance(u, a, b)\n }\n\n // Check if segments intersect\n if (doSegmentsIntersect(a, b, u, v)) {\n return 0\n }\n\n // Compute the minimum distance between the segments\n const distances = [\n pointToSegmentDistance(a, u, v),\n pointToSegmentDistance(b, u, v),\n pointToSegmentDistance(u, a, b),\n pointToSegmentDistance(v, a, b),\n ]\n\n return Math.min(...distances)\n}\n\n/**\n * Returns the minimum distance from a line segment to a bounding box.\n */\nexport function segmentToBoundsMinDistance(\n a: Point,\n b: Point,\n bounds: { minX: number; minY: number; maxX: number; maxY: number },\n): number {\n // Check if segment intersects with the bounds\n // Create the four edges of the bounds\n const topLeft = { x: bounds.minX, y: bounds.minY }\n const topRight = { x: bounds.maxX, y: bounds.minY }\n const bottomLeft = { x: bounds.minX, y: bounds.maxY }\n const bottomRight = { x: bounds.maxX, y: bounds.maxY }\n\n // Check if segment intersects with any of the bounds edges\n if (\n doSegmentsIntersect(a, b, topLeft, topRight) ||\n doSegmentsIntersect(a, b, topRight, bottomRight) ||\n doSegmentsIntersect(a, b, bottomRight, bottomLeft) ||\n doSegmentsIntersect(a, b, bottomLeft, topLeft)\n ) {\n return 0\n }\n\n // Check if segment is entirely inside the bounds\n if (\n a.x >= bounds.minX &&\n a.x <= bounds.maxX &&\n a.y >= bounds.minY &&\n a.y <= bounds.maxY &&\n b.x >= bounds.minX &&\n b.x <= bounds.maxX &&\n b.y >= bounds.minY &&\n b.y <= bounds.maxY\n ) {\n return 0\n }\n\n // If not intersecting, calculate the minimum distance\n const distances = [\n pointToSegmentDistance(topLeft, a, b),\n pointToSegmentDistance(topRight, a, b),\n pointToSegmentDistance(bottomLeft, a, b),\n pointToSegmentDistance(bottomRight, a, b),\n ]\n\n // If one of the segment endpoints is inside the bounds, we need to consider its distance to the bounds as 0\n if (\n a.x >= bounds.minX &&\n a.x <= bounds.maxX &&\n a.y >= bounds.minY &&\n a.y <= bounds.maxY\n ) {\n return 0\n }\n\n if (\n b.x >= bounds.minX &&\n b.x <= bounds.maxX &&\n b.y >= bounds.minY &&\n b.y <= bounds.maxY\n ) {\n return 0\n }\n\n // Calculate distances from segment endpoints to bounds if outside\n if (\n a.x < bounds.minX ||\n a.x > bounds.maxX ||\n a.y < bounds.minY ||\n a.y > bounds.maxY\n ) {\n const closestX = clamp(a.x, bounds.minX, bounds.maxX)\n const closestY = clamp(a.y, bounds.minY, bounds.maxY)\n distances.push(distance(a, { x: closestX, y: closestY }))\n }\n\n if (\n b.x < bounds.minX ||\n b.x > bounds.maxX ||\n b.y < bounds.minY ||\n b.y > bounds.maxY\n ) {\n const closestX = clamp(b.x, bounds.minX, bounds.maxX)\n const closestY = clamp(b.y, bounds.minY, bounds.maxY)\n distances.push(distance(b, { x: closestX, y: closestY }))\n }\n\n return Math.min(...distances)\n}\n\n/**\n * Returns the minimum distance from a line segment to a box.\n */\nexport function segmentToBoxMinDistance(\n a: Point,\n b: Point,\n box: { center: Point; width: number; height: number },\n): number {\n const halfWidth = box.width / 2\n const halfHeight = box.height / 2\n const bounds = {\n minX: box.center.x - halfWidth,\n maxX: box.center.x + halfWidth,\n minY: box.center.y - halfHeight,\n maxY: box.center.y + halfHeight,\n }\n\n return segmentToBoundsMinDistance(a, b, bounds)\n}\n\n/**\n * Returns the minimum distance from a line segment to a circle.\n */\nexport function segmentToCircleMinDistance(\n a: Point,\n b: Point,\n circle: { x: number; y: number; radius: number },\n): number {\n // Calculate the distance from the circle center to the line segment\n const circleCenter = { x: circle.x, y: circle.y }\n\n // Handle degenerate case: segment of zero length (point to circle)\n if (a.x === b.x && a.y === b.y) {\n return Math.max(0, distance(a, circleCenter) - circle.radius)\n }\n\n // Vector from a to b\n const ab = { x: b.x - a.x, y: b.y - a.y }\n // Vector from a to circle center\n const ac = { x: circleCenter.x - a.x, y: circleCenter.y - a.y }\n\n // Length of segment ab squared\n const abLengthSq = ab.x * ab.x + ab.y * ab.y\n\n // Calculate projection of ac onto ab, normalized by the length of ab\n const t = Math.max(0, Math.min(1, (ab.x * ac.x + ab.y * ac.y) / abLengthSq))\n\n // Find the closest point on the segment to the circle center\n const closestPoint = {\n x: a.x + t * ab.x,\n y: a.y + t * ab.y,\n }\n\n // Calculate distance from closest point to circle center\n const distToCenter = distance(closestPoint, circleCenter)\n\n // Return the distance to the circle (subtract radius from distance to center)\n return Math.max(0, distToCenter - circle.radius)\n}\n\nexport function pointToSegmentClosestPoint(\n p: Point,\n a: Point,\n b: Point,\n): Point {\n const dx_ab = b.x - a.x\n const dy_ab = b.y - a.y\n const l2 = dx_ab * dx_ab + dy_ab * dy_ab\n\n if (l2 === 0) return { x: a.x, y: a.y } // Segment is a point\n\n // Project p onto the line defined by a, b\n // t = [(p - a) . (b - a)] / |b - a|^2\n let t = ((p.x - a.x) * dx_ab + (p.y - a.y) * dy_ab) / l2\n\n // Clamp t to the range [0, 1] to stay on the segment\n t = Math.max(0, Math.min(1, t))\n\n // Calculate the projection point\n const closestPoint = {\n x: a.x + t * dx_ab,\n y: a.y + t * dy_ab,\n }\n\n return closestPoint\n}\n"],"mappings":"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","names":[]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  distance
3
- } from "./chunk-CHQOCSFB.js";
3
+ } from "./chunk-3453HRP7.js";
4
4
  import {
5
5
  clamp
6
6
  } from "./chunk-MHHTZHOJ.js";
@@ -34,10 +34,16 @@ function midpoint(p1, p2) {
34
34
  y: (p1.y + p2.y) / 2
35
35
  };
36
36
  }
37
+ function distSq(p1, p2) {
38
+ const dx = p1.x - p2.x;
39
+ const dy = p1.y - p2.y;
40
+ return dx * dx + dy * dy;
41
+ }
37
42
 
38
43
  export {
39
44
  pointToBoxDistance,
40
45
  pointToBoundsDistance,
41
- midpoint
46
+ midpoint,
47
+ distSq
42
48
  };
43
- //# sourceMappingURL=chunk-W6CW26U5.js.map
49
+ //# sourceMappingURL=chunk-SLG2OU3P.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/point-distance.ts"],"sourcesContent":["import type { Point, Bounds } from \"./common\"\nimport { distance } from \"./line-intersections\"\nimport { clamp } from \"./nearest-box\"\nimport type { Box } from \"./nearest-box\"\n\n/**\n * Returns the minimum distance from a point to a box.\n * If the point is inside the box, the distance is 0.\n */\nexport function pointToBoxDistance(p: Point, box: Box): number {\n const halfWidth = box.width / 2\n const halfHeight = box.height / 2\n const minX = box.center.x - halfWidth\n const maxX = box.center.x + halfWidth\n const minY = box.center.y - halfHeight\n const maxY = box.center.y + halfHeight\n\n // Check if the point is inside the box\n if (p.x >= minX && p.x <= maxX && p.y >= minY && p.y <= maxY) {\n return 0\n }\n\n // Find the closest point on the box boundary\n const closestX = clamp(p.x, minX, maxX)\n const closestY = clamp(p.y, minY, maxY)\n\n // Calculate the distance to the closest point\n return distance(p, { x: closestX, y: closestY })\n}\n\n/**\n * Returns the minimum distance from a point to a bounds rectangle.\n * If the point is inside the bounds, the distance is 0.\n */\nexport function pointToBoundsDistance(p: Point, bounds: Bounds): number {\n // Check if the point is inside the bounds\n if (\n p.x >= bounds.minX &&\n p.x <= bounds.maxX &&\n p.y >= bounds.minY &&\n p.y <= bounds.maxY\n ) {\n return 0\n }\n\n // Find the closest point on the bounds boundary\n const closestX = clamp(p.x, bounds.minX, bounds.maxX)\n const closestY = clamp(p.y, bounds.minY, bounds.maxY)\n\n // Calculate the distance to the closest point\n return distance(p, { x: closestX, y: closestY })\n}\n\nexport function midpoint(p1: Point, p2: Point): Point {\n return {\n x: (p1.x + p2.x) / 2,\n y: (p1.y + p2.y) / 2,\n }\n}\n"],"mappings":";;;;;;;;AASO,SAAS,mBAAmB,GAAU,KAAkB;AAC7D,QAAM,YAAY,IAAI,QAAQ;AAC9B,QAAM,aAAa,IAAI,SAAS;AAChC,QAAM,OAAO,IAAI,OAAO,IAAI;AAC5B,QAAM,OAAO,IAAI,OAAO,IAAI;AAC5B,QAAM,OAAO,IAAI,OAAO,IAAI;AAC5B,QAAM,OAAO,IAAI,OAAO,IAAI;AAG5B,MAAI,EAAE,KAAK,QAAQ,EAAE,KAAK,QAAQ,EAAE,KAAK,QAAQ,EAAE,KAAK,MAAM;AAC5D,WAAO;AAAA,EACT;AAGA,QAAM,WAAW,MAAM,EAAE,GAAG,MAAM,IAAI;AACtC,QAAM,WAAW,MAAM,EAAE,GAAG,MAAM,IAAI;AAGtC,SAAO,SAAS,GAAG,EAAE,GAAG,UAAU,GAAG,SAAS,CAAC;AACjD;AAMO,SAAS,sBAAsB,GAAU,QAAwB;AAEtE,MACE,EAAE,KAAK,OAAO,QACd,EAAE,KAAK,OAAO,QACd,EAAE,KAAK,OAAO,QACd,EAAE,KAAK,OAAO,MACd;AACA,WAAO;AAAA,EACT;AAGA,QAAM,WAAW,MAAM,EAAE,GAAG,OAAO,MAAM,OAAO,IAAI;AACpD,QAAM,WAAW,MAAM,EAAE,GAAG,OAAO,MAAM,OAAO,IAAI;AAGpD,SAAO,SAAS,GAAG,EAAE,GAAG,UAAU,GAAG,SAAS,CAAC;AACjD;AAEO,SAAS,SAAS,IAAW,IAAkB;AACpD,SAAO;AAAA,IACL,IAAI,GAAG,IAAI,GAAG,KAAK;AAAA,IACnB,IAAI,GAAG,IAAI,GAAG,KAAK;AAAA,EACrB;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/point-distance.ts"],"sourcesContent":["import type { Point, Bounds } from \"./common\"\nimport { distance } from \"./line-intersections\"\nimport { clamp } from \"./nearest-box\"\nimport type { Box } from \"./nearest-box\"\n\n/**\n * Returns the minimum distance from a point to a box.\n * If the point is inside the box, the distance is 0.\n */\nexport function pointToBoxDistance(p: Point, box: Box): number {\n const halfWidth = box.width / 2\n const halfHeight = box.height / 2\n const minX = box.center.x - halfWidth\n const maxX = box.center.x + halfWidth\n const minY = box.center.y - halfHeight\n const maxY = box.center.y + halfHeight\n\n // Check if the point is inside the box\n if (p.x >= minX && p.x <= maxX && p.y >= minY && p.y <= maxY) {\n return 0\n }\n\n // Find the closest point on the box boundary\n const closestX = clamp(p.x, minX, maxX)\n const closestY = clamp(p.y, minY, maxY)\n\n // Calculate the distance to the closest point\n return distance(p, { x: closestX, y: closestY })\n}\n\n/**\n * Returns the minimum distance from a point to a bounds rectangle.\n * If the point is inside the bounds, the distance is 0.\n */\nexport function pointToBoundsDistance(p: Point, bounds: Bounds): number {\n // Check if the point is inside the bounds\n if (\n p.x >= bounds.minX &&\n p.x <= bounds.maxX &&\n p.y >= bounds.minY &&\n p.y <= bounds.maxY\n ) {\n return 0\n }\n\n // Find the closest point on the bounds boundary\n const closestX = clamp(p.x, bounds.minX, bounds.maxX)\n const closestY = clamp(p.y, bounds.minY, bounds.maxY)\n\n // Calculate the distance to the closest point\n return distance(p, { x: closestX, y: closestY })\n}\n\nexport function midpoint(p1: Point, p2: Point): Point {\n return {\n x: (p1.x + p2.x) / 2,\n y: (p1.y + p2.y) / 2,\n }\n}\n\nexport function distSq(p1: Point, p2: Point): number {\n const dx = p1.x - p2.x\n const dy = p1.y - p2.y\n return dx * dx + dy * dy\n}\n"],"mappings":";;;;;;;;AASO,SAAS,mBAAmB,GAAU,KAAkB;AAC7D,QAAM,YAAY,IAAI,QAAQ;AAC9B,QAAM,aAAa,IAAI,SAAS;AAChC,QAAM,OAAO,IAAI,OAAO,IAAI;AAC5B,QAAM,OAAO,IAAI,OAAO,IAAI;AAC5B,QAAM,OAAO,IAAI,OAAO,IAAI;AAC5B,QAAM,OAAO,IAAI,OAAO,IAAI;AAG5B,MAAI,EAAE,KAAK,QAAQ,EAAE,KAAK,QAAQ,EAAE,KAAK,QAAQ,EAAE,KAAK,MAAM;AAC5D,WAAO;AAAA,EACT;AAGA,QAAM,WAAW,MAAM,EAAE,GAAG,MAAM,IAAI;AACtC,QAAM,WAAW,MAAM,EAAE,GAAG,MAAM,IAAI;AAGtC,SAAO,SAAS,GAAG,EAAE,GAAG,UAAU,GAAG,SAAS,CAAC;AACjD;AAMO,SAAS,sBAAsB,GAAU,QAAwB;AAEtE,MACE,EAAE,KAAK,OAAO,QACd,EAAE,KAAK,OAAO,QACd,EAAE,KAAK,OAAO,QACd,EAAE,KAAK,OAAO,MACd;AACA,WAAO;AAAA,EACT;AAGA,QAAM,WAAW,MAAM,EAAE,GAAG,OAAO,MAAM,OAAO,IAAI;AACpD,QAAM,WAAW,MAAM,EAAE,GAAG,OAAO,MAAM,OAAO,IAAI;AAGpD,SAAO,SAAS,GAAG,EAAE,GAAG,UAAU,GAAG,SAAS,CAAC;AACjD;AAEO,SAAS,SAAS,IAAW,IAAkB;AACpD,SAAO;AAAA,IACL,IAAI,GAAG,IAAI,GAAG,KAAK;AAAA,IACnB,IAAI,GAAG,IAAI,GAAG,KAAK;AAAA,EACrB;AACF;AAEO,SAAS,OAAO,IAAW,IAAmB;AACnD,QAAM,KAAK,GAAG,IAAI,GAAG;AACrB,QAAM,KAAK,GAAG,IAAI,GAAG;AACrB,SAAO,KAAK,KAAK,KAAK;AACxB;","names":[]}
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- export { distance, doSegmentsIntersect, doesLineIntersectLine, onSegment, orientation, pointToSegmentDistance } from './line-intersections.js';
1
+ export { distance, doSegmentsIntersect, doesLineIntersectLine, getSegmentIntersection, onSegment, orientation, pointToSegmentDistance } from './line-intersections.js';
2
2
  export { Box, BoxSet, GridCell, clamp, computeDistanceBetweenBoxes, findNearestPointsBetweenBoxSets, getBoundingBox } from './nearest-box.js';
3
3
  export { Bounds, Point } from './common.js';
4
4
  export { getUnitVectorFromDirection, getUnitVectorFromPointAToB } from './get-unit-vector.js';
5
5
  export { GridCellPositions, GridOptions, grid } from './grid.js';
6
- export { segmentToBoundsMinDistance, segmentToBoxMinDistance, segmentToCircleMinDistance, segmentToSegmentMinDistance } from './segment-distance.js';
7
- export { midpoint, pointToBoundsDistance, pointToBoxDistance } from './point-distance.js';
6
+ export { pointToSegmentClosestPoint, segmentToBoundsMinDistance, segmentToBoxMinDistance, segmentToCircleMinDistance, segmentToSegmentMinDistance } from './segment-distance.js';
7
+ export { distSq, midpoint, pointToBoundsDistance, pointToBoxDistance } from './point-distance.js';
package/dist/index.js CHANGED
@@ -7,24 +7,27 @@ import {
7
7
  grid
8
8
  } from "./chunk-U45EKA3R.js";
9
9
  import {
10
+ distSq,
10
11
  midpoint,
11
12
  pointToBoundsDistance,
12
13
  pointToBoxDistance
13
- } from "./chunk-W6CW26U5.js";
14
+ } from "./chunk-SLG2OU3P.js";
14
15
  import {
16
+ pointToSegmentClosestPoint,
15
17
  segmentToBoundsMinDistance,
16
18
  segmentToBoxMinDistance,
17
19
  segmentToCircleMinDistance,
18
20
  segmentToSegmentMinDistance
19
- } from "./chunk-K7F26NYD.js";
21
+ } from "./chunk-FWQGMQBW.js";
20
22
  import {
21
23
  distance,
22
24
  doSegmentsIntersect,
23
25
  doesLineIntersectLine,
26
+ getSegmentIntersection,
24
27
  onSegment,
25
28
  orientation,
26
29
  pointToSegmentDistance
27
- } from "./chunk-CHQOCSFB.js";
30
+ } from "./chunk-3453HRP7.js";
28
31
  import {
29
32
  clamp,
30
33
  computeDistanceBetweenBoxes,
@@ -34,11 +37,13 @@ import {
34
37
  export {
35
38
  clamp,
36
39
  computeDistanceBetweenBoxes,
40
+ distSq,
37
41
  distance,
38
42
  doSegmentsIntersect,
39
43
  doesLineIntersectLine,
40
44
  findNearestPointsBetweenBoxSets,
41
45
  getBoundingBox,
46
+ getSegmentIntersection,
42
47
  getUnitVectorFromDirection,
43
48
  getUnitVectorFromPointAToB,
44
49
  grid,
@@ -47,6 +52,7 @@ export {
47
52
  orientation,
48
53
  pointToBoundsDistance,
49
54
  pointToBoxDistance,
55
+ pointToSegmentClosestPoint,
50
56
  pointToSegmentDistance,
51
57
  segmentToBoundsMinDistance,
52
58
  segmentToBoxMinDistance,
@@ -26,5 +26,10 @@ declare function pointToSegmentDistance(p: Point, v: Point, w: Point): number;
26
26
  * Returns the distance between two points.
27
27
  */
28
28
  declare function distance(p1: Point, p2: Point): number;
29
+ /**
30
+ * Calculates the intersection point of two line segments.
31
+ * Returns the intersection point {x, y} if the segments intersect, otherwise returns null.
32
+ */
33
+ declare function getSegmentIntersection(a: Point, b: Point, u: Point, v: Point): Point | null;
29
34
 
30
- export { distance, doSegmentsIntersect, doesLineIntersectLine, onSegment, orientation, pointToSegmentDistance };
35
+ export { distance, doSegmentsIntersect, doesLineIntersectLine, getSegmentIntersection, onSegment, orientation, pointToSegmentDistance };
@@ -2,14 +2,16 @@ import {
2
2
  distance,
3
3
  doSegmentsIntersect,
4
4
  doesLineIntersectLine,
5
+ getSegmentIntersection,
5
6
  onSegment,
6
7
  orientation,
7
8
  pointToSegmentDistance
8
- } from "./chunk-CHQOCSFB.js";
9
+ } from "./chunk-3453HRP7.js";
9
10
  export {
10
11
  distance,
11
12
  doSegmentsIntersect,
12
13
  doesLineIntersectLine,
14
+ getSegmentIntersection,
13
15
  onSegment,
14
16
  orientation,
15
17
  pointToSegmentDistance
@@ -12,5 +12,6 @@ declare function pointToBoxDistance(p: Point, box: Box): number;
12
12
  */
13
13
  declare function pointToBoundsDistance(p: Point, bounds: Bounds): number;
14
14
  declare function midpoint(p1: Point, p2: Point): Point;
15
+ declare function distSq(p1: Point, p2: Point): number;
15
16
 
16
- export { midpoint, pointToBoundsDistance, pointToBoxDistance };
17
+ export { distSq, midpoint, pointToBoundsDistance, pointToBoxDistance };
@@ -1,11 +1,13 @@
1
1
  import {
2
+ distSq,
2
3
  midpoint,
3
4
  pointToBoundsDistance,
4
5
  pointToBoxDistance
5
- } from "./chunk-W6CW26U5.js";
6
- import "./chunk-CHQOCSFB.js";
6
+ } from "./chunk-SLG2OU3P.js";
7
+ import "./chunk-3453HRP7.js";
7
8
  import "./chunk-MHHTZHOJ.js";
8
9
  export {
10
+ distSq,
9
11
  midpoint,
10
12
  pointToBoundsDistance,
11
13
  pointToBoxDistance
@@ -29,5 +29,6 @@ declare function segmentToCircleMinDistance(a: Point, b: Point, circle: {
29
29
  y: number;
30
30
  radius: number;
31
31
  }): number;
32
+ declare function pointToSegmentClosestPoint(p: Point, a: Point, b: Point): Point;
32
33
 
33
- export { segmentToBoundsMinDistance, segmentToBoxMinDistance, segmentToCircleMinDistance, segmentToSegmentMinDistance };
34
+ export { pointToSegmentClosestPoint, segmentToBoundsMinDistance, segmentToBoxMinDistance, segmentToCircleMinDistance, segmentToSegmentMinDistance };
@@ -1,12 +1,14 @@
1
1
  import {
2
+ pointToSegmentClosestPoint,
2
3
  segmentToBoundsMinDistance,
3
4
  segmentToBoxMinDistance,
4
5
  segmentToCircleMinDistance,
5
6
  segmentToSegmentMinDistance
6
- } from "./chunk-K7F26NYD.js";
7
- import "./chunk-CHQOCSFB.js";
7
+ } from "./chunk-FWQGMQBW.js";
8
+ import "./chunk-3453HRP7.js";
8
9
  import "./chunk-MHHTZHOJ.js";
9
10
  export {
11
+ pointToSegmentClosestPoint,
10
12
  segmentToBoundsMinDistance,
11
13
  segmentToBoxMinDistance,
12
14
  segmentToCircleMinDistance,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tscircuit/math-utils",
3
3
  "main": "dist/index.js",
4
- "version": "0.0.15",
4
+ "version": "0.0.17",
5
5
  "type": "module",
6
6
  "module": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/line-intersections.ts"],"sourcesContent":["import type { Point } from \"./common\"\n\n/**\n * Returns true if the two lines intersect.\n */\nexport function doesLineIntersectLine(\n [a1, a2]: [Point, Point],\n [b1, b2]: [Point, Point],\n {\n lineThickness = 0,\n }: {\n lineThickness?: number\n } = {},\n): boolean {\n if (lineThickness === 0) {\n return doSegmentsIntersect(a1, a2, b1, b2)\n }\n const minDist = segmentsDistance(a1, a2, b1, b2)\n return minDist <= lineThickness\n}\n\n/**\n * Returns true if the two segments intersect.\n */\nexport function doSegmentsIntersect(\n p1: Point,\n q1: Point,\n p2: Point,\n q2: Point,\n): boolean {\n const o1 = orientation(p1, q1, p2)\n const o2 = orientation(p1, q1, q2)\n const o3 = orientation(p2, q2, p1)\n const o4 = orientation(p2, q2, q1)\n\n // General case\n if (o1 !== o2 && o3 !== o4) {\n return true\n }\n\n // Special Cases\n if (o1 === 0 && onSegment(p1, p2, q1)) return true\n if (o2 === 0 && onSegment(p1, q2, q1)) return true\n if (o3 === 0 && onSegment(p2, p1, q2)) return true\n if (o4 === 0 && onSegment(p2, q1, q2)) return true\n\n return false\n}\n\n/**\n * Returns 0 if the points are colinear, 1 if they are clockwise, and 2 if they are counterclockwise.\n */\nexport function orientation(p: Point, q: Point, r: Point): number {\n const val = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y)\n if (val === 0) return 0 // colinear\n return val > 0 ? 1 : 2 // clock or counterclock wise\n}\n\n/**\n * Returns true if q is on the segment p->r.\n */\nexport function onSegment(p: Point, q: Point, r: Point): boolean {\n return (\n q.x <= Math.max(p.x, r.x) &&\n q.x >= Math.min(p.x, r.x) &&\n q.y <= Math.max(p.y, r.y) &&\n q.y >= Math.min(p.y, r.y)\n )\n}\n\n/**\n * Returns the minimum distance between two segments.\n */\nfunction segmentsDistance(a1: Point, a2: Point, b1: Point, b2: Point): number {\n // Handle degenerate cases: segments of zero length\n if (a1.x === a2.x && a1.y === a2.y) {\n return pointToSegmentDistance(a1, b1, b2)\n }\n if (b1.x === b2.x && b1.y === b2.y) {\n return pointToSegmentDistance(b1, a1, a2)\n }\n\n // Check if segments intersect\n if (doSegmentsIntersect(a1, a2, b1, b2)) {\n return 0\n }\n\n // Compute the minimum distance between the segments\n const distances = [\n pointToSegmentDistance(a1, b1, b2),\n pointToSegmentDistance(a2, b1, b2),\n pointToSegmentDistance(b1, a1, a2),\n pointToSegmentDistance(b2, a1, a2),\n ]\n\n return Math.min(...distances)\n}\n\n/**\n * Returns the minimum distance between a point and a segment.\n */\nexport function pointToSegmentDistance(p: Point, v: Point, w: Point): number {\n const l2 = (w.x - v.x) ** 2 + (w.y - v.y) ** 2\n if (l2 === 0) return distance(p, v)\n\n let t = ((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / l2\n t = Math.max(0, Math.min(1, t))\n\n const projection = {\n x: v.x + t * (w.x - v.x),\n y: v.y + t * (w.y - v.y),\n }\n\n return distance(p, projection)\n}\n\n/**\n * Returns the distance between two points.\n */\nexport function distance(p1: Point, p2: Point): number {\n const dx = p1.x - p2.x\n const dy = p1.y - p2.y\n return Math.sqrt(dx * dx + dy * dy)\n}\n"],"mappings":"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","names":[]}