More intersection detection functions

This commit is contained in:
Mark Tolmacs 2024-09-30 15:31:02 +02:00
parent b7f504796b
commit 6e67aa3a3c
No known key found for this signature in database
4 changed files with 75 additions and 12 deletions

View file

@ -3,7 +3,9 @@ import {
ellipseSegmentInterceptPoints,
ellipseIncludesPoint,
ellipseTouchesPoint,
ellipseIntersectsLine,
} from "./ellipse";
import { line } from "./line";
import { point } from "./point";
import { segment } from "./segment";
import type { Ellipse, GlobalPoint } from "./types";
@ -47,7 +49,7 @@ describe("point and ellipse", () => {
});
});
describe("line and ellipse", () => {
describe("segment and ellipse", () => {
it("detects outside segment", () => {
const e = ellipse(point(0, 0), 2, 2);
@ -77,3 +79,32 @@ describe("line and ellipse", () => {
).toEqual([]);
});
});
describe("line and ellipse", () => {
const e = ellipse(point(0, 0), 2, 2);
it("detects outside line", () => {
expect(
ellipseIntersectsLine(
e,
line<GlobalPoint>(point(-10, -10), point(10, -10)),
),
).toEqual([]);
});
it("detects line intersecting ellipse", () => {
expect(
ellipseIntersectsLine(e, line<GlobalPoint>(point(0, -1), point(0, 1))),
).toEqual([point(0, 2), point(0, -2)]);
expect(
ellipseIntersectsLine(
e,
line<GlobalPoint>(point(-100, 0), point(-10, 0)),
).map(([x, y]) => point(Math.round(x), Math.round(y))),
).toEqual([point(2, 0), point(-2, 0)]);
});
it("detects line touching ellipse", () => {
expect(
ellipseIntersectsLine(e, line<GlobalPoint>(point(-2, -2), point(2, -2))),
).toEqual([point(0, -2)]);
});
});

View file

@ -1,5 +1,5 @@
import { point, pointDistance, pointFromVector } from "./point";
import type { Ellipse, GenericPoint, Segment } from "./types";
import { point, pointDistance, pointFromVector, pointsEqual } from "./point";
import type { Ellipse, GenericPoint, Line, Segment } from "./types";
import { PRECISION } from "./utils";
import {
vector,
@ -180,3 +180,37 @@ export function ellipseSegmentInterceptPoints<Point extends GenericPoint>(
return intersections;
}
export function ellipseIntersectsLine<Point extends GenericPoint>(
{ center, halfWidth, halfHeight }: Ellipse<Point>,
[g, h]: Line<Point>,
): Point[] {
const [cx, cy] = center;
const x1 = g[0] - cx;
const y1 = g[1] - cy;
const x2 = h[0] - cx;
const y2 = h[1] - cy;
const a =
Math.pow(x2 - x1, 2) / Math.pow(halfWidth, 2) +
Math.pow(y2 - y1, 2) / Math.pow(halfHeight, 2);
const b =
2 *
((x1 * (x2 - x1)) / Math.pow(halfWidth, 2) +
(y1 * (y2 - y1)) / Math.pow(halfHeight, 2));
const c =
Math.pow(x1, 2) / Math.pow(halfWidth, 2) +
Math.pow(y1, 2) / Math.pow(halfHeight, 2) -
1;
const t1 = (-b + Math.sqrt(Math.pow(b, 2) - 4 * a * c)) / (2 * a);
const t2 = (-b - Math.sqrt(Math.pow(b, 2) - 4 * a * c)) / (2 * a);
const candidates = [
point<Point>(x1 + t1 * (x2 - x1) + cx, y1 + t1 * (y2 - y1) + cy),
point<Point>(x1 + t2 * (x2 - x1) + cx, y1 + t2 * (y2 - y1) + cy),
].filter((p) => !isNaN(p[0]) && !isNaN(p[1]));
if (candidates.length === 2 && pointsEqual(candidates[0], candidates[1])) {
return [candidates[0]];
}
return candidates;
}

View file

@ -1,3 +1,4 @@
import { ellipseIntersectsLine } from "./ellipse";
import { point, pointCenter, pointRotateRads } from "./point";
import { segmentIncludesPoint } from "./segment";
import type { GenericPoint, Line, Radians, Segment } from "./types";
@ -93,3 +94,5 @@ export function lineIntersectsSegment<Point extends GenericPoint>(
return candidate;
}
export const lineInterceptsEllipse = ellipseIntersectsLine;

View file

@ -6,7 +6,7 @@ import {
pointRotateRads,
pointsEqual,
} from "./point";
import type { GenericPoint, Segment, Radians, Line } from "./types";
import type { GenericPoint, Segment, Radians } from "./types";
import { PRECISION } from "./utils";
import {
vectorAdd,
@ -180,15 +180,10 @@ export function segmentDistanceToPoint<Point extends GenericPoint>(
}
/**
* Returns the intersection point between a segment and a line, if any
* Returns the intersection point of a segment and a line
*
* @param s
* @param l
* @param s
* @returns
*/
export function segmentIntersectsLine<Point extends GenericPoint>(
s: Segment<Point>,
l: Line<Point>,
): Point | null {
return lineIntersectsSegment(l, s);
}
export const segmentIntersectsLine = lineIntersectsSegment;