refactor: Update generic constraint Point to use GenericPoint

- Changed existing generic constraints that used LocalPoint | GlobalPoint as Point to now use the unified GenericPoint type.
This commit is contained in:
sunub 2025-04-17 13:16:35 +09:00
parent 71e55dba9f
commit 85cb973936
21 changed files with 153 additions and 211 deletions

View file

@ -1,12 +1,6 @@
import { PRECISION } from "./utils";
import type {
Degrees,
GlobalPoint,
LocalPoint,
PolarCoords,
Radians,
} from "./types";
import type { Degrees, GenericPoint, PolarCoords, Radians } from "./types";
// TODO: Simplify with modulo and fix for angles beyond 4*Math.PI and - 4*Math.PI
export const normalizeRadians = (angle: Radians): Radians => {
@ -24,7 +18,7 @@ export const normalizeRadians = (angle: Radians): Radians => {
* (x, y) for the center point 0,0 where the first number returned is the radius,
* the second is the angle in radians.
*/
export const cartesian2Polar = <P extends GlobalPoint | LocalPoint>([
export const cartesian2Polar = <P extends GenericPoint>([
x,
y,
]: P): PolarCoords => [

View file

@ -3,7 +3,7 @@ import type { Bounds } from "@excalidraw/element/bounds";
import { isPoint, pointDistance, pointFrom } from "./point";
import { rectangle, rectangleIntersectLineSegment } from "./rectangle";
import type { Curve, GlobalPoint, LineSegment, LocalPoint } from "./types";
import type { Curve, GenericPoint, LineSegment } from "./types";
/**
*
@ -13,7 +13,7 @@ import type { Curve, GlobalPoint, LineSegment, LocalPoint } from "./types";
* @param d
* @returns
*/
export function curve<Point extends GlobalPoint | LocalPoint>(
export function curve<Point extends GenericPoint>(
a: Point,
b: Point,
c: Point,
@ -82,7 +82,7 @@ function solve(
return [t0, s0];
}
const bezierEquation = <Point extends GlobalPoint | LocalPoint>(
const bezierEquation = <Point extends GenericPoint>(
c: Curve<Point>,
t: number,
) =>
@ -100,9 +100,10 @@ const bezierEquation = <Point extends GlobalPoint | LocalPoint>(
/**
* Computes the intersection between a cubic spline and a line segment.
*/
export function curveIntersectLineSegment<
Point extends GlobalPoint | LocalPoint,
>(c: Curve<Point>, l: LineSegment<Point>): Point[] {
export function curveIntersectLineSegment<Point extends GenericPoint>(
c: Curve<Point>,
l: LineSegment<Point>,
): Point[] {
// Optimize by doing a cheap bounding box check first
const bounds = curveBounds(c);
if (
@ -188,7 +189,7 @@ export function curveIntersectLineSegment<
* @param maxLevel
* @returns
*/
export function curveClosestPoint<Point extends GlobalPoint | LocalPoint>(
export function curveClosestPoint<Point extends GenericPoint>(
c: Curve<Point>,
p: Point,
tolerance: number = 1e-3,
@ -245,7 +246,7 @@ export function curveClosestPoint<Point extends GlobalPoint | LocalPoint>(
* @param c The curve to test
* @param p The point to measure from
*/
export function curvePointDistance<Point extends GlobalPoint | LocalPoint>(
export function curvePointDistance<Point extends GenericPoint>(
c: Curve<Point>,
p: Point,
) {
@ -261,9 +262,7 @@ export function curvePointDistance<Point extends GlobalPoint | LocalPoint>(
/**
* Determines if the parameter is a Curve
*/
export function isCurve<P extends GlobalPoint | LocalPoint>(
v: unknown,
): v is Curve<P> {
export function isCurve<P extends GenericPoint>(v: unknown): v is Curve<P> {
return (
Array.isArray(v) &&
v.length === 4 &&
@ -274,9 +273,7 @@ export function isCurve<P extends GlobalPoint | LocalPoint>(
);
}
function curveBounds<Point extends GlobalPoint | LocalPoint>(
c: Curve<Point>,
): Bounds {
function curveBounds<Point extends GenericPoint>(c: Curve<Point>): Bounds {
const [P0, P1, P2, P3] = c;
const x = [P0[0], P1[0], P2[0], P3[0]];
const y = [P0[1], P1[1], P2[1], P3[1]];

View file

@ -13,13 +13,7 @@ import {
vectorScale,
} from "./vector";
import type {
Ellipse,
GlobalPoint,
Line,
LineSegment,
LocalPoint,
} from "./types";
import type { Ellipse, GenericPoint, Line, LineSegment } from "./types";
/**
* Construct an Ellipse object from the parameters
@ -30,7 +24,7 @@ import type {
* @param halfHeight Half of the height of a non-slanted version of the ellipse
* @returns The constructed Ellipse object
*/
export function ellipse<Point extends GlobalPoint | LocalPoint>(
export function ellipse<Point extends GenericPoint>(
center: Point,
halfWidth: number,
halfHeight: number,
@ -49,7 +43,7 @@ export function ellipse<Point extends GlobalPoint | LocalPoint>(
* @param ellipse The ellipse to compare against
* @returns TRUE if the point is inside or on the outline of the ellipse
*/
export const ellipseIncludesPoint = <Point extends GlobalPoint | LocalPoint>(
export const ellipseIncludesPoint = <Point extends GenericPoint>(
p: Point,
ellipse: Ellipse<Point>,
) => {
@ -69,7 +63,7 @@ export const ellipseIncludesPoint = <Point extends GlobalPoint | LocalPoint>(
* @param threshold The distance to consider a point close enough to be "on" the outline
* @returns TRUE if the point is on the ellise outline
*/
export const ellipseTouchesPoint = <Point extends GlobalPoint | LocalPoint>(
export const ellipseTouchesPoint = <Point extends GenericPoint>(
point: Point,
ellipse: Ellipse<Point>,
threshold = PRECISION,
@ -85,9 +79,7 @@ export const ellipseTouchesPoint = <Point extends GlobalPoint | LocalPoint>(
* @param ellipse The ellipse to calculate the distance to
* @returns The eucledian distance
*/
export const ellipseDistanceFromPoint = <
Point extends GlobalPoint | LocalPoint,
>(
export const ellipseDistanceFromPoint = <Point extends GenericPoint>(
p: Point,
ellipse: Ellipse<Point>,
): number => {
@ -140,9 +132,10 @@ export const ellipseDistanceFromPoint = <
* Calculate a maximum of two intercept points for a line going throug an
* ellipse.
*/
export function ellipseSegmentInterceptPoints<
Point extends GlobalPoint | LocalPoint,
>(e: Readonly<Ellipse<Point>>, s: Readonly<LineSegment<Point>>): Point[] {
export function ellipseSegmentInterceptPoints<Point extends GenericPoint>(
e: Readonly<Ellipse<Point>>,
s: Readonly<LineSegment<Point>>,
): Point[] {
const rx = e.halfWidth;
const ry = e.halfHeight;
@ -194,9 +187,7 @@ export function ellipseSegmentInterceptPoints<
return intersections;
}
export function ellipseLineIntersectionPoints<
Point extends GlobalPoint | LocalPoint,
>(
export function ellipseLineIntersectionPoints<Point extends GenericPoint>(
{ center, halfWidth, halfHeight }: Ellipse<Point>,
[g, h]: Line<Point>,
): Point[] {

View file

@ -1,6 +1,6 @@
import { pointFrom } from "./point";
import type { GlobalPoint, Line, LocalPoint } from "./types";
import type { GenericPoint, Line } from "./types";
/**
* Create a line from two points.
@ -8,7 +8,7 @@ import type { GlobalPoint, Line, LocalPoint } from "./types";
* @param points The two points lying on the line
* @returns The line on which the points lie
*/
export function line<P extends GlobalPoint | LocalPoint>(a: P, b: P): Line<P> {
export function line<P extends GenericPoint>(a: P, b: P): Line<P> {
return [a, b] as Line<P>;
}
@ -20,7 +20,7 @@ export function line<P extends GlobalPoint | LocalPoint>(a: P, b: P): Line<P> {
* @param b
* @returns
*/
export function linesIntersectAt<Point extends GlobalPoint | LocalPoint>(
export function linesIntersectAt<Point extends GenericPoint>(
a: Line<Point>,
b: Line<Point>,
): Point | null {

View file

@ -2,13 +2,7 @@ import { degreesToRadians } from "./angle";
import { PRECISION } from "./utils";
import { vectorFromPoint, vectorScale } from "./vector";
import type {
LocalPoint,
GlobalPoint,
Radians,
Degrees,
Vector,
} from "./types";
import type { Radians, Degrees, Vector, GenericPoint } from "./types";
/**
* Create a properly typed Point instance from the X and Y coordinates.
@ -17,7 +11,7 @@ import type {
* @param y The Y coordinate
* @returns The branded and created point
*/
export function pointFrom<Point extends GlobalPoint | LocalPoint>(
export function pointFrom<Point extends GenericPoint>(
x: number,
y: number,
): Point {
@ -30,7 +24,7 @@ export function pointFrom<Point extends GlobalPoint | LocalPoint>(
* @param numberArray The number array to check and to convert to Point
* @returns The point instance
*/
export function pointFromArray<Point extends GlobalPoint | LocalPoint>(
export function pointFromArray<Point extends GenericPoint>(
numberArray: number[],
): Point | undefined {
return numberArray.length === 2
@ -44,7 +38,7 @@ export function pointFromArray<Point extends GlobalPoint | LocalPoint>(
* @param pair A number pair to convert to Point
* @returns The point instance
*/
export function pointFromPair<Point extends GlobalPoint | LocalPoint>(
export function pointFromPair<Point extends GenericPoint>(
pair: [number, number],
): Point {
return pair as Point;
@ -56,7 +50,7 @@ export function pointFromPair<Point extends GlobalPoint | LocalPoint>(
* @param v The vector to convert
* @returns The point the vector points at with origin 0,0
*/
export function pointFromVector<P extends GlobalPoint | LocalPoint>(
export function pointFromVector<P extends GenericPoint>(
v: Vector,
offset: P = pointFrom(0, 0),
): P {
@ -69,7 +63,7 @@ export function pointFromVector<P extends GlobalPoint | LocalPoint>(
* @param p The value to attempt verification on
* @returns TRUE if the provided value has the shape of a local or global point
*/
export function isPoint(p: unknown): p is LocalPoint | GlobalPoint {
export function isPoint(p: unknown): p is GenericPoint {
return (
Array.isArray(p) &&
p.length === 2 &&
@ -88,7 +82,7 @@ export function isPoint(p: unknown): p is LocalPoint | GlobalPoint {
* @param b Point The second point to compare
* @returns TRUE if the points are sufficiently close to each other
*/
export function pointsEqual<Point extends GlobalPoint | LocalPoint>(
export function pointsEqual<Point extends GenericPoint>(
a: Point,
b: Point,
): boolean {
@ -104,7 +98,7 @@ export function pointsEqual<Point extends GlobalPoint | LocalPoint>(
* @param angle The radians to rotate the point by
* @returns The rotated point
*/
export function pointRotateRads<Point extends GlobalPoint | LocalPoint>(
export function pointRotateRads<Point extends GenericPoint>(
[x, y]: Point,
[cx, cy]: Point,
angle: Radians,
@ -123,7 +117,7 @@ export function pointRotateRads<Point extends GlobalPoint | LocalPoint>(
* @param angle The degree to rotate the point by
* @returns The rotated point
*/
export function pointRotateDegs<Point extends GlobalPoint | LocalPoint>(
export function pointRotateDegs<Point extends GenericPoint>(
point: Point,
center: Point,
angle: Degrees,
@ -145,8 +139,8 @@ export function pointRotateDegs<Point extends GlobalPoint | LocalPoint>(
*/
// TODO 99% of use is translating between global and local coords, which need to be formalized
export function pointTranslate<
From extends GlobalPoint | LocalPoint,
To extends GlobalPoint | LocalPoint,
From extends GenericPoint,
To extends GenericPoint,
>(p: From, v: Vector = [0, 0] as Vector): To {
return pointFrom(p[0] + v[0], p[1] + v[1]);
}
@ -158,7 +152,7 @@ export function pointTranslate<
* @param b The other point to create the middle point for
* @returns The middle point
*/
export function pointCenter<P extends LocalPoint | GlobalPoint>(a: P, b: P): P {
export function pointCenter<P extends GenericPoint>(a: P, b: P): P {
return pointFrom((a[0] + b[0]) / 2, (a[1] + b[1]) / 2);
}
@ -169,10 +163,7 @@ export function pointCenter<P extends LocalPoint | GlobalPoint>(a: P, b: P): P {
* @param b Second point
* @returns The euclidean distance between the two points.
*/
export function pointDistance<P extends LocalPoint | GlobalPoint>(
a: P,
b: P,
): number {
export function pointDistance<P extends GenericPoint>(a: P, b: P): number {
return Math.hypot(b[0] - a[0], b[1] - a[1]);
}
@ -185,10 +176,7 @@ export function pointDistance<P extends LocalPoint | GlobalPoint>(
* @param b Second point
* @returns The euclidean distance between the two points.
*/
export function pointDistanceSq<P extends LocalPoint | GlobalPoint>(
a: P,
b: P,
): number {
export function pointDistanceSq<P extends GenericPoint>(a: P, b: P): number {
const xDiff = b[0] - a[0];
const yDiff = b[1] - a[1];
@ -203,7 +191,7 @@ export function pointDistanceSq<P extends LocalPoint | GlobalPoint>(
* @param multiplier The scaling factor
* @returns
*/
export const pointScaleFromOrigin = <P extends GlobalPoint | LocalPoint>(
export const pointScaleFromOrigin = <P extends GenericPoint>(
p: P,
mid: P,
multiplier: number,
@ -218,7 +206,7 @@ export const pointScaleFromOrigin = <P extends GlobalPoint | LocalPoint>(
* @param r The other point to compare against
* @returns TRUE if q is indeed between p and r
*/
export const isPointWithinBounds = <P extends GlobalPoint | LocalPoint>(
export const isPointWithinBounds = <P extends GenericPoint>(
p: P,
q: P,
r: P,

View file

@ -2,21 +2,17 @@ import { pointsEqual } from "./point";
import { lineSegment, pointOnLineSegment } from "./segment";
import { PRECISION } from "./utils";
import type { GlobalPoint, LocalPoint, Polygon } from "./types";
import type { GenericPoint, Polygon } from "./types";
export function polygon<Point extends GlobalPoint | LocalPoint>(
...points: Point[]
) {
export function polygon<Point extends GenericPoint>(...points: Point[]) {
return polygonClose(points) as Polygon<Point>;
}
export function polygonFromPoints<Point extends GlobalPoint | LocalPoint>(
points: Point[],
) {
export function polygonFromPoints<Point extends GenericPoint>(points: Point[]) {
return polygonClose(points) as Polygon<Point>;
}
export const polygonIncludesPoint = <Point extends LocalPoint | GlobalPoint>(
export const polygonIncludesPoint = <Point extends GenericPoint>(
point: Point,
polygon: Polygon<Point>,
) => {
@ -69,7 +65,7 @@ export const polygonIncludesPointNonZero = <Point extends [number, number]>(
return windingNumber !== 0;
};
export const pointOnPolygon = <Point extends LocalPoint | GlobalPoint>(
export const pointOnPolygon = <Point extends GenericPoint>(
p: Point,
poly: Polygon<Point>,
threshold = PRECISION,
@ -86,16 +82,12 @@ export const pointOnPolygon = <Point extends LocalPoint | GlobalPoint>(
return on;
};
function polygonClose<Point extends LocalPoint | GlobalPoint>(
polygon: Point[],
) {
function polygonClose<Point extends GenericPoint>(polygon: Point[]) {
return polygonIsClosed(polygon)
? polygon
: ([...polygon, polygon[0]] as Polygon<Point>);
}
function polygonIsClosed<Point extends LocalPoint | GlobalPoint>(
polygon: Point[],
) {
function polygonIsClosed<Point extends GenericPoint>(polygon: Point[]) {
return pointsEqual(polygon[0], polygon[polygon.length - 1]);
}

View file

@ -1,18 +1,19 @@
import { pointFrom } from "./point";
import { lineSegment, lineSegmentIntersectionPoints } from "./segment";
import type { GlobalPoint, LineSegment, LocalPoint, Rectangle } from "./types";
import type { GenericPoint, LineSegment, Rectangle } from "./types";
export function rectangle<P extends GlobalPoint | LocalPoint>(
export function rectangle<P extends GenericPoint>(
topLeft: P,
bottomRight: P,
): Rectangle<P> {
return [topLeft, bottomRight] as Rectangle<P>;
}
export function rectangleIntersectLineSegment<
Point extends LocalPoint | GlobalPoint,
>(r: Rectangle<Point>, l: LineSegment<Point>): Point[] {
export function rectangleIntersectLineSegment<Point extends GenericPoint>(
r: Rectangle<Point>,
l: LineSegment<Point>,
): Point[] {
return [
lineSegment(r[0], pointFrom(r[1][0], r[0][1])),
lineSegment(pointFrom(r[1][0], r[0][1]), r[1]),

View file

@ -14,7 +14,7 @@ import {
vectorSubtract,
} from "./vector";
import type { GlobalPoint, LineSegment, LocalPoint, Radians } from "./types";
import type { GenericPoint, LineSegment, Radians } from "./types";
/**
* Create a line segment from two points.
@ -22,7 +22,7 @@ import type { GlobalPoint, LineSegment, LocalPoint, Radians } from "./types";
* @param points The two points delimiting the line segment on each end
* @returns The line segment delineated by the points
*/
export function lineSegment<P extends GlobalPoint | LocalPoint>(
export function lineSegment<P extends GenericPoint>(
a: P,
b: P,
): LineSegment<P> {
@ -34,7 +34,7 @@ export function lineSegment<P extends GlobalPoint | LocalPoint>(
* @param segment
* @returns
*/
export const isLineSegment = <Point extends GlobalPoint | LocalPoint>(
export const isLineSegment = <Point extends GenericPoint>(
segment: unknown,
): segment is LineSegment<Point> =>
Array.isArray(segment) &&
@ -51,7 +51,7 @@ export const isLineSegment = <Point extends GlobalPoint | LocalPoint>(
* @param origin
* @returns
*/
export const lineSegmentRotate = <Point extends LocalPoint | GlobalPoint>(
export const lineSegmentRotate = <Point extends GenericPoint>(
l: LineSegment<Point>,
angle: Radians,
origin?: Point,
@ -66,7 +66,7 @@ export const lineSegmentRotate = <Point extends LocalPoint | GlobalPoint>(
* Calculates the point two line segments with a definite start and end point
* intersect at.
*/
export const segmentsIntersectAt = <Point extends GlobalPoint | LocalPoint>(
export const segmentsIntersectAt = <Point extends GenericPoint>(
a: Readonly<LineSegment<Point>>,
b: Readonly<LineSegment<Point>>,
): Point | null => {
@ -99,7 +99,7 @@ export const segmentsIntersectAt = <Point extends GlobalPoint | LocalPoint>(
return null;
};
export const pointOnLineSegment = <Point extends LocalPoint | GlobalPoint>(
export const pointOnLineSegment = <Point extends GenericPoint>(
point: Point,
line: LineSegment<Point>,
threshold = PRECISION,
@ -113,7 +113,7 @@ export const pointOnLineSegment = <Point extends LocalPoint | GlobalPoint>(
return distance < threshold;
};
export const distanceToLineSegment = <Point extends LocalPoint | GlobalPoint>(
export const distanceToLineSegment = <Point extends GenericPoint>(
point: Point,
line: LineSegment<Point>,
) => {
@ -158,9 +158,7 @@ export const distanceToLineSegment = <Point extends LocalPoint | GlobalPoint>(
* @param s
* @returns
*/
export function lineSegmentIntersectionPoints<
Point extends GlobalPoint | LocalPoint,
>(
export function lineSegmentIntersectionPoints<Point extends GenericPoint>(
l: LineSegment<Point>,
s: LineSegment<Point>,
threshold?: number,

View file

@ -1,4 +1,4 @@
import type { GlobalPoint, LocalPoint, Triangle } from "./types";
import type { GenericPoint, Triangle } from "./types";
// Types
@ -11,7 +11,7 @@ import type { GlobalPoint, LocalPoint, Triangle } from "./types";
* @param p The point to test whether is in the triangle
* @returns TRUE if the point is inside of the triangle
*/
export function triangleIncludesPoint<P extends GlobalPoint | LocalPoint>(
export function triangleIncludesPoint<P extends GenericPoint>(
[a, b, c]: Triangle<P>,
p: P,
): boolean {

View file

@ -1,4 +1,4 @@
import type { GlobalPoint, LocalPoint, Vector } from "./types";
import type { GenericPoint, Vector } from "./types";
/**
* Create a vector from the x and y coordiante elements.
@ -23,7 +23,7 @@ export function vector(
* @param origin The origin point in a given coordiante system
* @returns The created vector from the point and the origin
*/
export function vectorFromPoint<Point extends GlobalPoint | LocalPoint>(
export function vectorFromPoint<Point extends GenericPoint>(
p: Point,
origin: Point = [0, 0] as Point,
): Vector {