From 85b4b315c7efaac0841134a4a41f465d7a3a32cd Mon Sep 17 00:00:00 2001 From: Mark Tolmacs Date: Tue, 11 Feb 2025 15:17:04 +0100 Subject: [PATCH] Arc code updated --- packages/math/arc.test.ts | 62 ++++++++++++++++++++++++++++++++++----- packages/math/arc.ts | 25 +++++++++++----- 2 files changed, 71 insertions(+), 16 deletions(-) diff --git a/packages/math/arc.test.ts b/packages/math/arc.test.ts index ed4176f8f3..d0a80cc474 100644 --- a/packages/math/arc.test.ts +++ b/packages/math/arc.test.ts @@ -1,4 +1,3 @@ -import { radians } from "./angle"; import { arc, arcIncludesPoint, @@ -8,12 +7,18 @@ import { import { line } from "./line"; import { pointFrom } from "./point"; import { segment } from "./segment"; +import type { GlobalPoint, Radians } from "./types"; describe("point on arc", () => { it("should detect point on simple arc", () => { expect( arcIncludesPoint( - arc(pointFrom(0, 0), 1, radians(-Math.PI / 4), radians(Math.PI / 4)), + arc( + pointFrom(0, 0), + 1, + (-Math.PI / 4) as Radians, + (Math.PI / 4) as Radians, + ), pointFrom(0.92291667, 0.385), ), ).toBe(true); @@ -21,7 +26,12 @@ describe("point on arc", () => { it("should not detect point outside of a simple arc", () => { expect( arcIncludesPoint( - arc(pointFrom(0, 0), 1, radians(-Math.PI / 4), radians(Math.PI / 4)), + arc( + pointFrom(0, 0), + 1, + (-Math.PI / 4) as Radians, + (Math.PI / 4) as Radians, + ), pointFrom(-0.92291667, 0.385), ), ).toBe(false); @@ -29,7 +39,12 @@ describe("point on arc", () => { it("should not detect point with good angle but incorrect radius", () => { expect( arcIncludesPoint( - arc(pointFrom(0, 0), 1, radians(-Math.PI / 4), radians(Math.PI / 4)), + arc( + pointFrom(0, 0), + 1, + (-Math.PI / 4) as Radians, + (Math.PI / 4) as Radians, + ), pointFrom(-0.5, 0.5), ), ).toBe(false); @@ -40,16 +55,37 @@ describe("intersection", () => { it("should report correct interception point for segment", () => { expect( arcSegmentInterceptPoints( - arc(pointFrom(0, 0), 1, radians(-Math.PI / 4), radians(Math.PI / 4)), + arc( + pointFrom(0, 0), + 1, + (-Math.PI / 4) as Radians, + (Math.PI / 4) as Radians, + ), segment(pointFrom(2, 1), pointFrom(0, 0)), ), ).toEqual([pointFrom(0.894427190999916, 0.447213595499958)]); + expect( + arcSegmentInterceptPoints( + arc( + pointFrom(0, 0), + 1, + Math.PI as Radians, + ((3 / 2) * Math.PI) as Radians, + ), + segment(pointFrom(-10, -10), pointFrom(0, 0)), + ), + ).toEqual([pointFrom(-0.7071067811865479, -0.7071067811865479)]); }); it("should report both interception points when present for segment", () => { expect( arcSegmentInterceptPoints( - arc(pointFrom(0, 0), 1, radians(-Math.PI / 4), radians(Math.PI / 4)), + arc( + pointFrom(0, 0), + 1, + (-Math.PI / 4) as Radians, + (Math.PI / 4) as Radians, + ), segment(pointFrom(0.9, -2), pointFrom(0.9, 2)), ), ).toEqual([ @@ -61,7 +97,12 @@ describe("intersection", () => { it("should report correct interception point for line", () => { expect( arcLineInterceptPoints( - arc(pointFrom(0, 0), 1, radians(-Math.PI / 4), radians(Math.PI / 4)), + arc( + pointFrom(0, 0), + 1, + (-Math.PI / 4) as Radians, + (Math.PI / 4) as Radians, + ), line(pointFrom(2, 1), pointFrom(0, 0)), ), ).toEqual([pointFrom(0.894427190999916, 0.447213595499958)]); @@ -70,7 +111,12 @@ describe("intersection", () => { it("should report both interception points when present for line", () => { expect( arcLineInterceptPoints( - arc(pointFrom(0, 0), 1, radians(-Math.PI / 4), radians(Math.PI / 4)), + arc( + pointFrom(0, 0), + 1, + (-Math.PI / 4) as Radians, + (Math.PI / 4) as Radians, + ), line(pointFrom(0.9, -2), pointFrom(0.9, 2)), ), ).toEqual([ diff --git a/packages/math/arc.ts b/packages/math/arc.ts index 37f84e7554..1bdb3f16c3 100644 --- a/packages/math/arc.ts +++ b/packages/math/arc.ts @@ -1,4 +1,4 @@ -import { cartesian2Polar, normalizeRadians, radians } from "./angle"; +import { cartesian2Polar, normalizeRadians } from "./angle"; import { ellipse, ellipseDistanceFromPoint, @@ -6,7 +6,15 @@ import { ellipseSegmentInterceptPoints, } from "./ellipse"; import { pointFrom, pointDistance, isPoint } from "./point"; -import type { GenericPoint, Segment, Radians, Arc, Line } from "./types"; +import type { + GlobalPoint, + LocalPoint, + Radians, + Arc, + Line, + GenericPoint, + Segment, +} from "./types"; import { PRECISION } from "./utils"; /** @@ -60,7 +68,7 @@ export function arcDistanceFromPoint( p: Point, ) { const theta = normalizeRadians( - radians(Math.atan2(p[0] - a.center[0], p[1] - a.center[1])), + Math.atan2(p[0] - a.center[0], p[1] - a.center[1]) as Radians, ); if (a.startAngle <= theta && a.endAngle >= theta) { @@ -98,11 +106,10 @@ export function arcSegmentInterceptPoints( return ellipseSegmentInterceptPoints( ellipse(a.center, a.radius, a.radius), s, - ).filter((candidate) => { + ).filter((candidate: Point) => { const [candidateRadius, candidateAngle] = cartesian2Polar( pointFrom(candidate[0] - a.center[0], candidate[1] - a.center[1]), ); - return Math.abs(a.radius - candidateRadius) < PRECISION && a.startAngle > a.endAngle ? a.startAngle <= candidateAngle || a.endAngle >= candidateAngle @@ -118,14 +125,14 @@ export function arcSegmentInterceptPoints( * @param l * @returns */ -export function arcLineInterceptPoints( +export function arcLineInterceptPoints( a: Readonly>, l: Readonly>, ): Point[] { return ellipseLineIntersectionPoints( ellipse(a.center, a.radius, a.radius), l, - ).filter((candidate) => { + ).filter((candidate: Point) => { const [candidateRadius, candidateAngle] = cartesian2Polar( pointFrom(candidate[0] - a.center[0], candidate[1] - a.center[1]), ); @@ -137,7 +144,9 @@ export function arcLineInterceptPoints( }); } -export function isArc(v: unknown): v is Arc { +export function isArc( + v: unknown, +): v is Arc { return ( v != null && typeof v === "object" &&