chore: Unify math types, utils and functions (#8389)

Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com>
This commit is contained in:
Márk Tolmács 2024-09-03 00:23:38 +02:00 committed by GitHub
parent e3d1dee9d0
commit f4dd23fc31
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
98 changed files with 4291 additions and 3661 deletions

View file

@ -1,249 +1,122 @@
import type { GlobalPoint, LineSegment, Polygon, Radians } from "../../math";
import {
lineIntersectsLine,
lineRotate,
pointInEllipse,
pointInPolygon,
pointLeftofLine,
pointOnCurve,
pointOnEllipse,
pointOnLine,
point,
lineSegment,
polygon,
pointOnLineSegment,
pointOnPolygon,
pointOnPolyline,
pointRightofLine,
pointRotate,
} from "./geometry";
import type { Curve, Ellipse, Line, Point, Polygon, Polyline } from "./shape";
polygonIncludesPoint,
segmentsIntersectAt,
} from "../../math";
import { pointInEllipse, pointOnEllipse, type Ellipse } from "./shape";
describe("point and line", () => {
const line: Line = [
[1, 0],
[1, 2],
];
// const l: Line<GlobalPoint> = line(point(1, 0), point(1, 2));
it("point on left or right of line", () => {
expect(pointLeftofLine([0, 1], line)).toBe(true);
expect(pointLeftofLine([1, 1], line)).toBe(false);
expect(pointLeftofLine([2, 1], line)).toBe(false);
// it("point on left or right of line", () => {
// expect(pointLeftofLine(point(0, 1), l)).toBe(true);
// expect(pointLeftofLine(point(1, 1), l)).toBe(false);
// expect(pointLeftofLine(point(2, 1), l)).toBe(false);
expect(pointRightofLine([0, 1], line)).toBe(false);
expect(pointRightofLine([1, 1], line)).toBe(false);
expect(pointRightofLine([2, 1], line)).toBe(true);
});
// expect(pointRightofLine(point(0, 1), l)).toBe(false);
// expect(pointRightofLine(point(1, 1), l)).toBe(false);
// expect(pointRightofLine(point(2, 1), l)).toBe(true);
// });
const s: LineSegment<GlobalPoint> = lineSegment(point(1, 0), point(1, 2));
it("point on the line", () => {
expect(pointOnLine([0, 1], line)).toBe(false);
expect(pointOnLine([1, 1], line, 0)).toBe(true);
expect(pointOnLine([2, 1], line)).toBe(false);
});
});
describe("point and polylines", () => {
const polyline: Polyline = [
[
[1, 0],
[1, 2],
],
[
[1, 2],
[2, 2],
],
[
[2, 2],
[2, 1],
],
[
[2, 1],
[3, 1],
],
];
it("point on the line", () => {
expect(pointOnPolyline([1, 0], polyline)).toBe(true);
expect(pointOnPolyline([1, 2], polyline)).toBe(true);
expect(pointOnPolyline([2, 2], polyline)).toBe(true);
expect(pointOnPolyline([2, 1], polyline)).toBe(true);
expect(pointOnPolyline([3, 1], polyline)).toBe(true);
expect(pointOnPolyline([1, 1], polyline)).toBe(true);
expect(pointOnPolyline([2, 1.5], polyline)).toBe(true);
expect(pointOnPolyline([2.5, 1], polyline)).toBe(true);
expect(pointOnPolyline([0, 1], polyline)).toBe(false);
expect(pointOnPolyline([2.1, 1.5], polyline)).toBe(false);
});
it("point on the line with rotation", () => {
const truePoints = [
[1, 0],
[1, 2],
[2, 2],
[2, 1],
[3, 1],
] as Point[];
truePoints.forEach((point) => {
const rotation = Math.random() * 360;
const rotatedPoint = pointRotate(point, rotation);
const rotatedPolyline: Polyline = polyline.map((line) =>
lineRotate(line, rotation, [0, 0]),
);
expect(pointOnPolyline(rotatedPoint, rotatedPolyline)).toBe(true);
});
const falsePoints = [
[0, 1],
[2.1, 1.5],
] as Point[];
falsePoints.forEach((point) => {
const rotation = Math.random() * 360;
const rotatedPoint = pointRotate(point, rotation);
const rotatedPolyline: Polyline = polyline.map((line) =>
lineRotate(line, rotation, [0, 0]),
);
expect(pointOnPolyline(rotatedPoint, rotatedPolyline)).toBe(false);
});
expect(pointOnLineSegment(point(0, 1), s)).toBe(false);
expect(pointOnLineSegment(point(1, 1), s, 0)).toBe(true);
expect(pointOnLineSegment(point(2, 1), s)).toBe(false);
});
});
describe("point and polygon", () => {
const polygon: Polygon = [
[10, 10],
[50, 10],
[50, 50],
[10, 50],
];
const poly: Polygon<GlobalPoint> = polygon(
point(10, 10),
point(50, 10),
point(50, 50),
point(10, 50),
);
it("point on polygon", () => {
expect(pointOnPolygon([30, 10], polygon)).toBe(true);
expect(pointOnPolygon([50, 30], polygon)).toBe(true);
expect(pointOnPolygon([30, 50], polygon)).toBe(true);
expect(pointOnPolygon([10, 30], polygon)).toBe(true);
expect(pointOnPolygon([30, 30], polygon)).toBe(false);
expect(pointOnPolygon([30, 70], polygon)).toBe(false);
expect(pointOnPolygon(point(30, 10), poly)).toBe(true);
expect(pointOnPolygon(point(50, 30), poly)).toBe(true);
expect(pointOnPolygon(point(30, 50), poly)).toBe(true);
expect(pointOnPolygon(point(10, 30), poly)).toBe(true);
expect(pointOnPolygon(point(30, 30), poly)).toBe(false);
expect(pointOnPolygon(point(30, 70), poly)).toBe(false);
});
it("point in polygon", () => {
const polygon: Polygon = [
[0, 0],
[2, 0],
[2, 2],
[0, 2],
];
expect(pointInPolygon([1, 1], polygon)).toBe(true);
expect(pointInPolygon([3, 3], polygon)).toBe(false);
});
});
describe("point and curve", () => {
const curve: Curve = [
[1.4, 1.65],
[1.9, 7.9],
[5.9, 1.65],
[6.44, 4.84],
];
it("point on curve", () => {
expect(pointOnCurve(curve[0], curve)).toBe(true);
expect(pointOnCurve(curve[3], curve)).toBe(true);
expect(pointOnCurve([2, 4], curve, 0.1)).toBe(true);
expect(pointOnCurve([4, 4.4], curve, 0.1)).toBe(true);
expect(pointOnCurve([5.6, 3.85], curve, 0.1)).toBe(true);
expect(pointOnCurve([5.6, 4], curve, 0.1)).toBe(false);
expect(pointOnCurve(curve[1], curve, 0.1)).toBe(false);
expect(pointOnCurve(curve[2], curve, 0.1)).toBe(false);
const poly: Polygon<GlobalPoint> = polygon(
point(0, 0),
point(2, 0),
point(2, 2),
point(0, 2),
);
expect(polygonIncludesPoint(point(1, 1), poly)).toBe(true);
expect(polygonIncludesPoint(point(3, 3), poly)).toBe(false);
});
});
describe("point and ellipse", () => {
const ellipse: Ellipse = {
center: [0, 0],
angle: 0,
const ellipse: Ellipse<GlobalPoint> = {
center: point(0, 0),
angle: 0 as Radians,
halfWidth: 2,
halfHeight: 1,
};
it("point on ellipse", () => {
[
[0, 1],
[0, -1],
[2, 0],
[-2, 0],
].forEach((point) => {
expect(pointOnEllipse(point as Point, ellipse)).toBe(true);
[point(0, 1), point(0, -1), point(2, 0), point(-2, 0)].forEach((p) => {
expect(pointOnEllipse(p, ellipse)).toBe(true);
});
expect(pointOnEllipse([-1.4, 0.7], ellipse, 0.1)).toBe(true);
expect(pointOnEllipse([-1.4, 0.71], ellipse, 0.01)).toBe(true);
expect(pointOnEllipse(point(-1.4, 0.7), ellipse, 0.1)).toBe(true);
expect(pointOnEllipse(point(-1.4, 0.71), ellipse, 0.01)).toBe(true);
expect(pointOnEllipse([1.4, 0.7], ellipse, 0.1)).toBe(true);
expect(pointOnEllipse([1.4, 0.71], ellipse, 0.01)).toBe(true);
expect(pointOnEllipse(point(1.4, 0.7), ellipse, 0.1)).toBe(true);
expect(pointOnEllipse(point(1.4, 0.71), ellipse, 0.01)).toBe(true);
expect(pointOnEllipse([1, -0.86], ellipse, 0.1)).toBe(true);
expect(pointOnEllipse([1, -0.86], ellipse, 0.01)).toBe(true);
expect(pointOnEllipse(point(1, -0.86), ellipse, 0.1)).toBe(true);
expect(pointOnEllipse(point(1, -0.86), ellipse, 0.01)).toBe(true);
expect(pointOnEllipse([-1, -0.86], ellipse, 0.1)).toBe(true);
expect(pointOnEllipse([-1, -0.86], ellipse, 0.01)).toBe(true);
expect(pointOnEllipse(point(-1, -0.86), ellipse, 0.1)).toBe(true);
expect(pointOnEllipse(point(-1, -0.86), ellipse, 0.01)).toBe(true);
expect(pointOnEllipse([-1, 0.8], ellipse)).toBe(false);
expect(pointOnEllipse([1, -0.8], ellipse)).toBe(false);
expect(pointOnEllipse(point(-1, 0.8), ellipse)).toBe(false);
expect(pointOnEllipse(point(1, -0.8), ellipse)).toBe(false);
});
it("point in ellipse", () => {
[
[0, 1],
[0, -1],
[2, 0],
[-2, 0],
].forEach((point) => {
expect(pointInEllipse(point as Point, ellipse)).toBe(true);
[point(0, 1), point(0, -1), point(2, 0), point(-2, 0)].forEach((p) => {
expect(pointInEllipse(p, ellipse)).toBe(true);
});
expect(pointInEllipse([-1, 0.8], ellipse)).toBe(true);
expect(pointInEllipse([1, -0.8], ellipse)).toBe(true);
expect(pointInEllipse(point(-1, 0.8), ellipse)).toBe(true);
expect(pointInEllipse(point(1, -0.8), ellipse)).toBe(true);
expect(pointInEllipse([-1, 1], ellipse)).toBe(false);
expect(pointInEllipse([-1.4, 0.8], ellipse)).toBe(false);
expect(pointInEllipse(point(-1, 1), ellipse)).toBe(false);
expect(pointInEllipse(point(-1.4, 0.8), ellipse)).toBe(false);
});
});
describe("line and line", () => {
const lineA: Line = [
[1, 4],
[3, 4],
];
const lineB: Line = [
[2, 1],
[2, 7],
];
const lineC: Line = [
[1, 8],
[3, 8],
];
const lineD: Line = [
[1, 8],
[3, 8],
];
const lineE: Line = [
[1, 9],
[3, 9],
];
const lineF: Line = [
[1, 2],
[3, 4],
];
const lineG: Line = [
[0, 1],
[2, 3],
];
const lineA: LineSegment<GlobalPoint> = lineSegment(point(1, 4), point(3, 4));
const lineB: LineSegment<GlobalPoint> = lineSegment(point(2, 1), point(2, 7));
const lineC: LineSegment<GlobalPoint> = lineSegment(point(1, 8), point(3, 8));
const lineD: LineSegment<GlobalPoint> = lineSegment(point(1, 8), point(3, 8));
const lineE: LineSegment<GlobalPoint> = lineSegment(point(1, 9), point(3, 9));
const lineF: LineSegment<GlobalPoint> = lineSegment(point(1, 2), point(3, 4));
const lineG: LineSegment<GlobalPoint> = lineSegment(point(0, 1), point(2, 3));
it("intersection", () => {
expect(lineIntersectsLine(lineA, lineB)).toBe(true);
expect(lineIntersectsLine(lineA, lineC)).toBe(false);
expect(lineIntersectsLine(lineB, lineC)).toBe(false);
expect(lineIntersectsLine(lineC, lineD)).toBe(true);
expect(lineIntersectsLine(lineE, lineD)).toBe(false);
expect(lineIntersectsLine(lineF, lineG)).toBe(true);
expect(segmentsIntersectAt(lineA, lineB)).toEqual([2, 4]);
expect(segmentsIntersectAt(lineA, lineC)).toBe(null);
expect(segmentsIntersectAt(lineB, lineC)).toBe(null);
expect(segmentsIntersectAt(lineC, lineD)).toBe(null); // Line overlapping line is not intersection!
expect(segmentsIntersectAt(lineE, lineD)).toBe(null);
expect(segmentsIntersectAt(lineF, lineG)).toBe(null);
});
});