summaryrefslogtreecommitdiffstats
path: root/packages/utils/geometry/geometry.test.ts
diff options
context:
space:
mode:
authorkj_sh6042026-03-15 16:19:35 -0400
committerkj_sh6042026-03-15 16:19:35 -0400
commit6ec259a0e71174651bae95d4628138bf6fd68742 (patch)
tree5e33c6a5ec091ecabfcb257fdc7b6a88ed8754ac /packages/utils/geometry/geometry.test.ts
parent16c8578b15c727f22921f8a80a56ee4d4e7f2272 (diff)
refactor: packages/
Diffstat (limited to 'packages/utils/geometry/geometry.test.ts')
-rw-r--r--packages/utils/geometry/geometry.test.ts161
1 files changed, 161 insertions, 0 deletions
diff --git a/packages/utils/geometry/geometry.test.ts b/packages/utils/geometry/geometry.test.ts
new file mode 100644
index 0000000..e9ad067
--- /dev/null
+++ b/packages/utils/geometry/geometry.test.ts
@@ -0,0 +1,161 @@
+import type {
+ GlobalPoint,
+ LineSegment,
+ Polygon,
+ Radians,
+} from "@excalidraw/math";
+import {
+ pointFrom,
+ lineSegment,
+ polygon,
+ pointOnLineSegment,
+ pointOnPolygon,
+ polygonIncludesPoint,
+ segmentsIntersectAt,
+} from "@excalidraw/math";
+import { pointInEllipse, pointOnEllipse, type Ellipse } from "./shape";
+
+describe("point and line", () => {
+ // const l: Line<GlobalPoint> = line(point(1, 0), point(1, 2));
+
+ // 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(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(
+ pointFrom(1, 0),
+ pointFrom(1, 2),
+ );
+
+ it("point on the line", () => {
+ expect(pointOnLineSegment(pointFrom(0, 1), s)).toBe(false);
+ expect(pointOnLineSegment(pointFrom(1, 1), s, 0)).toBe(true);
+ expect(pointOnLineSegment(pointFrom(2, 1), s)).toBe(false);
+ });
+});
+
+describe("point and polygon", () => {
+ const poly: Polygon<GlobalPoint> = polygon(
+ pointFrom(10, 10),
+ pointFrom(50, 10),
+ pointFrom(50, 50),
+ pointFrom(10, 50),
+ );
+
+ it("point on polygon", () => {
+ expect(pointOnPolygon(pointFrom(30, 10), poly)).toBe(true);
+ expect(pointOnPolygon(pointFrom(50, 30), poly)).toBe(true);
+ expect(pointOnPolygon(pointFrom(30, 50), poly)).toBe(true);
+ expect(pointOnPolygon(pointFrom(10, 30), poly)).toBe(true);
+ expect(pointOnPolygon(pointFrom(30, 30), poly)).toBe(false);
+ expect(pointOnPolygon(pointFrom(30, 70), poly)).toBe(false);
+ });
+
+ it("point in polygon", () => {
+ const poly: Polygon<GlobalPoint> = polygon(
+ pointFrom(0, 0),
+ pointFrom(2, 0),
+ pointFrom(2, 2),
+ pointFrom(0, 2),
+ );
+ expect(polygonIncludesPoint(pointFrom(1, 1), poly)).toBe(true);
+ expect(polygonIncludesPoint(pointFrom(3, 3), poly)).toBe(false);
+ });
+});
+
+describe("point and ellipse", () => {
+ const ellipse: Ellipse<GlobalPoint> = {
+ center: pointFrom(0, 0),
+ angle: 0 as Radians,
+ halfWidth: 2,
+ halfHeight: 1,
+ };
+
+ it("point on ellipse", () => {
+ [
+ pointFrom(0, 1),
+ pointFrom(0, -1),
+ pointFrom(2, 0),
+ pointFrom(-2, 0),
+ ].forEach((p) => {
+ expect(pointOnEllipse(p, ellipse)).toBe(true);
+ });
+ expect(pointOnEllipse(pointFrom(-1.4, 0.7), ellipse, 0.1)).toBe(true);
+ expect(pointOnEllipse(pointFrom(-1.4, 0.71), ellipse, 0.01)).toBe(true);
+
+ expect(pointOnEllipse(pointFrom(1.4, 0.7), ellipse, 0.1)).toBe(true);
+ expect(pointOnEllipse(pointFrom(1.4, 0.71), ellipse, 0.01)).toBe(true);
+
+ expect(pointOnEllipse(pointFrom(1, -0.86), ellipse, 0.1)).toBe(true);
+ expect(pointOnEllipse(pointFrom(1, -0.86), ellipse, 0.01)).toBe(true);
+
+ expect(pointOnEllipse(pointFrom(-1, -0.86), ellipse, 0.1)).toBe(true);
+ expect(pointOnEllipse(pointFrom(-1, -0.86), ellipse, 0.01)).toBe(true);
+
+ expect(pointOnEllipse(pointFrom(-1, 0.8), ellipse)).toBe(false);
+ expect(pointOnEllipse(pointFrom(1, -0.8), ellipse)).toBe(false);
+ });
+
+ it("point in ellipse", () => {
+ [
+ pointFrom(0, 1),
+ pointFrom(0, -1),
+ pointFrom(2, 0),
+ pointFrom(-2, 0),
+ ].forEach((p) => {
+ expect(pointInEllipse(p, ellipse)).toBe(true);
+ });
+
+ expect(pointInEllipse(pointFrom(-1, 0.8), ellipse)).toBe(true);
+ expect(pointInEllipse(pointFrom(1, -0.8), ellipse)).toBe(true);
+
+ expect(pointInEllipse(pointFrom(-1, 1), ellipse)).toBe(false);
+ expect(pointInEllipse(pointFrom(-1.4, 0.8), ellipse)).toBe(false);
+ });
+});
+
+describe("line and line", () => {
+ const lineA: LineSegment<GlobalPoint> = lineSegment(
+ pointFrom(1, 4),
+ pointFrom(3, 4),
+ );
+ const lineB: LineSegment<GlobalPoint> = lineSegment(
+ pointFrom(2, 1),
+ pointFrom(2, 7),
+ );
+ const lineC: LineSegment<GlobalPoint> = lineSegment(
+ pointFrom(1, 8),
+ pointFrom(3, 8),
+ );
+ const lineD: LineSegment<GlobalPoint> = lineSegment(
+ pointFrom(1, 8),
+ pointFrom(3, 8),
+ );
+ const lineE: LineSegment<GlobalPoint> = lineSegment(
+ pointFrom(1, 9),
+ pointFrom(3, 9),
+ );
+ const lineF: LineSegment<GlobalPoint> = lineSegment(
+ pointFrom(1, 2),
+ pointFrom(3, 4),
+ );
+ const lineG: LineSegment<GlobalPoint> = lineSegment(
+ pointFrom(0, 1),
+ pointFrom(2, 3),
+ );
+
+ it("intersection", () => {
+ 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);
+ });
+});