mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
More refactor
This commit is contained in:
parent
392dd5b0b8
commit
b697f63cad
18 changed files with 206 additions and 199 deletions
|
@ -1,11 +1,11 @@
|
|||
import { radians } from "./angle";
|
||||
import { arc, isPointOnSymmetricArc } from "./arc";
|
||||
import { arc, arcIncludesPoint } from "./arc";
|
||||
import { point } from "./point";
|
||||
|
||||
describe("point on arc", () => {
|
||||
it("should detect point on simple arc", () => {
|
||||
expect(
|
||||
isPointOnSymmetricArc(
|
||||
arcIncludesPoint(
|
||||
arc(point(0, 0), 1, radians(-Math.PI / 4), radians(Math.PI / 4)),
|
||||
point(0.92291667, 0.385),
|
||||
),
|
||||
|
@ -13,7 +13,7 @@ describe("point on arc", () => {
|
|||
});
|
||||
it("should not detect point outside of a simple arc", () => {
|
||||
expect(
|
||||
isPointOnSymmetricArc(
|
||||
arcIncludesPoint(
|
||||
arc(point(0, 0), 1, radians(-Math.PI / 4), radians(Math.PI / 4)),
|
||||
point(-0.92291667, 0.385),
|
||||
),
|
||||
|
@ -21,7 +21,7 @@ describe("point on arc", () => {
|
|||
});
|
||||
it("should not detect point with good angle but incorrect radius", () => {
|
||||
expect(
|
||||
isPointOnSymmetricArc(
|
||||
arcIncludesPoint(
|
||||
arc(point(0, 0), 1, radians(-Math.PI / 4), radians(Math.PI / 4)),
|
||||
point(-0.5, 0.5),
|
||||
),
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { cartesian2Polar, radians } from "./angle";
|
||||
import { ellipse, interceptPointsOfLineAndEllipse } from "./ellipse";
|
||||
import { ellipse, ellipseSegmentInterceptPoints } from "./ellipse";
|
||||
import { point } from "./point";
|
||||
import type { GenericPoint, LineSegment, Radians, SymmetricArc } from "./types";
|
||||
import type { GenericPoint, Segment, Radians, Arc } from "./types";
|
||||
import { PRECISION } from "./utils";
|
||||
|
||||
/**
|
||||
|
@ -20,15 +20,15 @@ export function arc<Point extends GenericPoint>(
|
|||
startAngle: Radians,
|
||||
endAngle: Radians,
|
||||
) {
|
||||
return { center, radius, startAngle, endAngle } as SymmetricArc<Point>;
|
||||
return { center, radius, startAngle, endAngle } as Arc<Point>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a cartesian point lies on a symmetric arc, i.e. an arc which
|
||||
* is part of a circle contour centered on 0, 0.
|
||||
*/
|
||||
export function isPointOnSymmetricArc<P extends GenericPoint>(
|
||||
{ center, radius: arcRadius, startAngle, endAngle }: SymmetricArc<P>,
|
||||
export function arcIncludesPoint<P extends GenericPoint>(
|
||||
{ center, radius: arcRadius, startAngle, endAngle }: Arc<P>,
|
||||
p: P,
|
||||
): boolean {
|
||||
const [radius, angle] = cartesian2Polar(
|
||||
|
@ -47,10 +47,10 @@ export function isPointOnSymmetricArc<P extends GenericPoint>(
|
|||
* point and end point and a symmetric arc.
|
||||
*/
|
||||
export function interceptOfSymmetricArcAndSegment<Point extends GenericPoint>(
|
||||
a: Readonly<SymmetricArc<Point>>,
|
||||
l: Readonly<LineSegment<Point>>,
|
||||
a: Readonly<Arc<Point>>,
|
||||
l: Readonly<Segment<Point>>,
|
||||
): Point[] {
|
||||
return interceptPointsOfLineAndEllipse(
|
||||
return ellipseSegmentInterceptPoints(
|
||||
ellipse(a.center, radians(0), a.radius, a.radius),
|
||||
l,
|
||||
).filter((candidate) => {
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { radians } from "./angle";
|
||||
import {
|
||||
ellipse,
|
||||
interceptPointsOfLineAndEllipse,
|
||||
pointInEllipse,
|
||||
pointOnEllipse,
|
||||
ellipseSegmentInterceptPoints,
|
||||
ellipseIncludesPoint,
|
||||
ellipseTouchesPoint,
|
||||
} from "./ellipse";
|
||||
import { point } from "./point";
|
||||
import { lineSegment } from "./segment";
|
||||
import { segment } from "./segment";
|
||||
import type { Ellipse, GlobalPoint } from "./types";
|
||||
|
||||
describe("point and ellipse", () => {
|
||||
|
@ -14,41 +14,44 @@ describe("point and ellipse", () => {
|
|||
|
||||
it("point on ellipse", () => {
|
||||
[point(0, 1), point(0, -1), point(2, 0), point(-2, 0)].forEach((p) => {
|
||||
expect(pointOnEllipse(p, target)).toBe(true);
|
||||
expect(ellipseTouchesPoint(p, target)).toBe(true);
|
||||
});
|
||||
expect(pointOnEllipse(point(-1.4, 0.7), target, 0.1)).toBe(true);
|
||||
expect(pointOnEllipse(point(-1.4, 0.71), target, 0.01)).toBe(true);
|
||||
expect(ellipseTouchesPoint(point(-1.4, 0.7), target, 0.1)).toBe(true);
|
||||
expect(ellipseTouchesPoint(point(-1.4, 0.71), target, 0.01)).toBe(true);
|
||||
|
||||
expect(pointOnEllipse(point(1.4, 0.7), target, 0.1)).toBe(true);
|
||||
expect(pointOnEllipse(point(1.4, 0.71), target, 0.01)).toBe(true);
|
||||
expect(ellipseTouchesPoint(point(1.4, 0.7), target, 0.1)).toBe(true);
|
||||
expect(ellipseTouchesPoint(point(1.4, 0.71), target, 0.01)).toBe(true);
|
||||
|
||||
expect(pointOnEllipse(point(1, -0.86), target, 0.1)).toBe(true);
|
||||
expect(pointOnEllipse(point(1, -0.86), target, 0.01)).toBe(true);
|
||||
expect(ellipseTouchesPoint(point(1, -0.86), target, 0.1)).toBe(true);
|
||||
expect(ellipseTouchesPoint(point(1, -0.86), target, 0.01)).toBe(true);
|
||||
|
||||
expect(pointOnEllipse(point(-1, -0.86), target, 0.1)).toBe(true);
|
||||
expect(pointOnEllipse(point(-1, -0.86), target, 0.01)).toBe(true);
|
||||
expect(ellipseTouchesPoint(point(-1, -0.86), target, 0.1)).toBe(true);
|
||||
expect(ellipseTouchesPoint(point(-1, -0.86), target, 0.01)).toBe(true);
|
||||
|
||||
expect(pointOnEllipse(point(-1, 0.8), target)).toBe(false);
|
||||
expect(pointOnEllipse(point(1, -0.8), target)).toBe(false);
|
||||
expect(ellipseTouchesPoint(point(-1, 0.8), target)).toBe(false);
|
||||
expect(ellipseTouchesPoint(point(1, -0.8), target)).toBe(false);
|
||||
});
|
||||
|
||||
it("point in ellipse", () => {
|
||||
[point(0, 1), point(0, -1), point(2, 0), point(-2, 0)].forEach((p) => {
|
||||
expect(pointInEllipse(p, target)).toBe(true);
|
||||
expect(ellipseIncludesPoint(p, target)).toBe(true);
|
||||
});
|
||||
|
||||
expect(pointInEllipse(point(-1, 0.8), target)).toBe(true);
|
||||
expect(pointInEllipse(point(1, -0.8), target)).toBe(true);
|
||||
expect(ellipseIncludesPoint(point(-1, 0.8), target)).toBe(true);
|
||||
expect(ellipseIncludesPoint(point(1, -0.8), target)).toBe(true);
|
||||
|
||||
expect(pointInEllipse(point(-1, 1), target)).toBe(false);
|
||||
expect(pointInEllipse(point(-1.4, 0.8), target)).toBe(false);
|
||||
// Point on outline
|
||||
expect(ellipseIncludesPoint(point(2, 0), target)).toBe(true);
|
||||
|
||||
expect(ellipseIncludesPoint(point(-1, 1), target)).toBe(false);
|
||||
expect(ellipseIncludesPoint(point(-1.4, 0.8), target)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("line and ellipse", () => {
|
||||
it("detects outside segment", () => {
|
||||
const l = lineSegment<GlobalPoint>(point(-100, 0), point(-10, 0));
|
||||
const l = segment<GlobalPoint>(point(-100, 0), point(-10, 0));
|
||||
const e = ellipse(point(0, 0), radians(0), 2, 2);
|
||||
expect(interceptPointsOfLineAndEllipse(e, l).length).toBe(0);
|
||||
expect(ellipseSegmentInterceptPoints(e, l).length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,7 +6,7 @@ import {
|
|||
pointFromVector,
|
||||
pointRotateRads,
|
||||
} from "./point";
|
||||
import type { Ellipse, GenericPoint, LineSegment, Radians } from "./types";
|
||||
import type { Ellipse, GenericPoint, Segment, Radians } from "./types";
|
||||
import { PRECISION } from "./utils";
|
||||
import {
|
||||
vector,
|
||||
|
@ -16,6 +16,15 @@ import {
|
|||
vectorScale,
|
||||
} from "./vector";
|
||||
|
||||
/**
|
||||
* Construct an Ellipse object from the parameters
|
||||
*
|
||||
* @param center The center of the ellipse
|
||||
* @param angle The slanting of the ellipse in radians
|
||||
* @param halfWidth Half of the width of a non-slanted version of the ellipse
|
||||
* @param halfHeight Half of the height of a non-slanted version of the ellipse
|
||||
* @returns The constructed Ellipse object
|
||||
*/
|
||||
export function ellipse<Point extends GenericPoint>(
|
||||
center: Point,
|
||||
angle: Radians,
|
||||
|
@ -30,7 +39,14 @@ export function ellipse<Point extends GenericPoint>(
|
|||
} as Ellipse<Point>;
|
||||
}
|
||||
|
||||
export const pointInEllipse = <Point extends GenericPoint>(
|
||||
/**
|
||||
* Determines if a point is inside or on the ellipse outline
|
||||
*
|
||||
* @param p The point to test
|
||||
* @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 GenericPoint>(
|
||||
p: Point,
|
||||
ellipse: Ellipse<Point>,
|
||||
) => {
|
||||
|
@ -52,7 +68,16 @@ export const pointInEllipse = <Point extends GenericPoint>(
|
|||
);
|
||||
};
|
||||
|
||||
export const pointOnEllipse = <Point extends GenericPoint>(
|
||||
/**
|
||||
* Tests whether a point lies on the outline of the ellipse within a given
|
||||
* tolerance
|
||||
*
|
||||
* @param point The point to test
|
||||
* @param ellipse The ellipse to compare against
|
||||
* @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 GenericPoint>(
|
||||
point: Point,
|
||||
ellipse: Ellipse<Point>,
|
||||
threshold = PRECISION,
|
||||
|
@ -60,7 +85,7 @@ export const pointOnEllipse = <Point extends GenericPoint>(
|
|||
return distanceToEllipse(point, ellipse) <= threshold;
|
||||
};
|
||||
|
||||
export const ellipseAxes = <Point extends GenericPoint>(
|
||||
export const ellipseFocusToCenter = <Point extends GenericPoint>(
|
||||
ellipse: Ellipse<Point>,
|
||||
) => {
|
||||
const widthGreaterThanHeight = ellipse.halfWidth > ellipse.halfHeight;
|
||||
|
@ -72,17 +97,6 @@ export const ellipseAxes = <Point extends GenericPoint>(
|
|||
? ellipse.halfHeight * 2
|
||||
: ellipse.halfWidth * 2;
|
||||
|
||||
return {
|
||||
majorAxis,
|
||||
minorAxis,
|
||||
};
|
||||
};
|
||||
|
||||
export const ellipseFocusToCenter = <Point extends GenericPoint>(
|
||||
ellipse: Ellipse<Point>,
|
||||
) => {
|
||||
const { majorAxis, minorAxis } = ellipseAxes(ellipse);
|
||||
|
||||
return Math.sqrt(majorAxis ** 2 - minorAxis ** 2);
|
||||
};
|
||||
|
||||
|
@ -90,7 +104,14 @@ export const ellipseExtremes = <Point extends GenericPoint>(
|
|||
ellipse: Ellipse<Point>,
|
||||
) => {
|
||||
const { center, angle } = ellipse;
|
||||
const { majorAxis, minorAxis } = ellipseAxes(ellipse);
|
||||
const widthGreaterThanHeight = ellipse.halfWidth > ellipse.halfHeight;
|
||||
|
||||
const majorAxis = widthGreaterThanHeight
|
||||
? ellipse.halfWidth * 2
|
||||
: ellipse.halfHeight * 2;
|
||||
const minorAxis = widthGreaterThanHeight
|
||||
? ellipse.halfHeight * 2
|
||||
: ellipse.halfWidth * 2;
|
||||
|
||||
const cos = Math.cos(angle);
|
||||
const sin = Math.sin(angle);
|
||||
|
@ -175,9 +196,9 @@ const distanceToEllipse = <Point extends GenericPoint>(
|
|||
* Calculate a maximum of two intercept points for a line going throug an
|
||||
* ellipse.
|
||||
*/
|
||||
export function interceptPointsOfLineAndEllipse<Point extends GenericPoint>(
|
||||
export function ellipseSegmentInterceptPoints<Point extends GenericPoint>(
|
||||
e: Readonly<Ellipse<Point>>,
|
||||
l: Readonly<LineSegment<Point>>,
|
||||
l: Readonly<Segment<Point>>,
|
||||
): Point[] {
|
||||
const rx = e.halfWidth;
|
||||
const ry = e.halfHeight;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { pointsEqual } from "./point";
|
||||
import { lineSegment, pointOnLineSegment } from "./segment";
|
||||
import { segment, segmentIncludesPoint } from "./segment";
|
||||
import type { GenericPoint, Polygon } from "./types";
|
||||
import { PRECISION } from "./utils";
|
||||
|
||||
|
@ -44,7 +44,7 @@ export const pointOnPolygon = <Point extends GenericPoint>(
|
|||
let on = false;
|
||||
|
||||
for (let i = 0, l = poly.length - 1; i < l; i++) {
|
||||
if (pointOnLineSegment(p, lineSegment(poly[i], poly[i + 1]), threshold)) {
|
||||
if (segmentIncludesPoint(p, segment(poly[i], poly[i + 1]), threshold)) {
|
||||
on = true;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
import { point } from "./point";
|
||||
import { lineSegment, segmentsIntersectAt } from "./segment";
|
||||
import type { GlobalPoint, LineSegment } from "./types";
|
||||
import { segment, segmentsIntersectAt } from "./segment";
|
||||
import type { GlobalPoint, Segment } from "./types";
|
||||
|
||||
describe("segment intersects segment", () => {
|
||||
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));
|
||||
const lineA: Segment<GlobalPoint> = segment(point(1, 4), point(3, 4));
|
||||
const lineB: Segment<GlobalPoint> = segment(point(2, 1), point(2, 7));
|
||||
const lineC: Segment<GlobalPoint> = segment(point(1, 8), point(3, 8));
|
||||
const lineD: Segment<GlobalPoint> = segment(point(1, 8), point(3, 8));
|
||||
const lineE: Segment<GlobalPoint> = segment(point(1, 9), point(3, 9));
|
||||
const lineF: Segment<GlobalPoint> = segment(point(1, 2), point(3, 4));
|
||||
const lineG: Segment<GlobalPoint> = segment(point(0, 1), point(2, 3));
|
||||
|
||||
it("intersection", () => {
|
||||
expect(segmentsIntersectAt(lineA, lineB)).toEqual([2, 4]);
|
||||
|
|
|
@ -4,7 +4,7 @@ import {
|
|||
pointFromVector,
|
||||
pointRotateRads,
|
||||
} from "./point";
|
||||
import type { GenericPoint, LineSegment, Radians } from "./types";
|
||||
import type { GenericPoint, Segment, Radians } from "./types";
|
||||
import { PRECISION } from "./utils";
|
||||
import {
|
||||
vectorAdd,
|
||||
|
@ -20,18 +20,15 @@ import {
|
|||
* @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 GenericPoint>(
|
||||
a: P,
|
||||
b: P,
|
||||
): LineSegment<P> {
|
||||
return [a, b] as LineSegment<P>;
|
||||
export function segment<P extends GenericPoint>(a: P, b: P): Segment<P> {
|
||||
return [a, b] as Segment<P>;
|
||||
}
|
||||
|
||||
export function lineSegmentFromPointArray<P extends GenericPoint>(
|
||||
export function segmentFromPointArray<P extends GenericPoint>(
|
||||
pointArray: P[],
|
||||
): LineSegment<P> | undefined {
|
||||
): Segment<P> | undefined {
|
||||
return pointArray.length === 2
|
||||
? lineSegment<P>(pointArray[0], pointArray[1])
|
||||
? segment<P>(pointArray[0], pointArray[1])
|
||||
: undefined;
|
||||
}
|
||||
|
||||
|
@ -40,9 +37,9 @@ export function lineSegmentFromPointArray<P extends GenericPoint>(
|
|||
* @param segment
|
||||
* @returns
|
||||
*/
|
||||
export const isLineSegment = <Point extends GenericPoint>(
|
||||
export const isSegment = <Point extends GenericPoint>(
|
||||
segment: unknown,
|
||||
): segment is LineSegment<Point> =>
|
||||
): segment is Segment<Point> =>
|
||||
Array.isArray(segment) &&
|
||||
segment.length === 2 &&
|
||||
isPoint(segment[0]) &&
|
||||
|
@ -57,12 +54,12 @@ export const isLineSegment = <Point extends GenericPoint>(
|
|||
* @param origin
|
||||
* @returns
|
||||
*/
|
||||
export const lineSegmentRotate = <Point extends GenericPoint>(
|
||||
l: LineSegment<Point>,
|
||||
export const segmentRotate = <Point extends GenericPoint>(
|
||||
l: Segment<Point>,
|
||||
angle: Radians,
|
||||
origin?: Point,
|
||||
): LineSegment<Point> => {
|
||||
return lineSegment(
|
||||
): Segment<Point> => {
|
||||
return segment(
|
||||
pointRotateRads(l[0], origin || pointCenter(l[0], l[1]), angle),
|
||||
pointRotateRads(l[1], origin || pointCenter(l[0], l[1]), angle),
|
||||
);
|
||||
|
@ -73,8 +70,8 @@ export const lineSegmentRotate = <Point extends GenericPoint>(
|
|||
* intersect at.
|
||||
*/
|
||||
export const segmentsIntersectAt = <Point extends GenericPoint>(
|
||||
a: Readonly<LineSegment<Point>>,
|
||||
b: Readonly<LineSegment<Point>>,
|
||||
a: Readonly<Segment<Point>>,
|
||||
b: Readonly<Segment<Point>>,
|
||||
): Point | null => {
|
||||
const a0 = vectorFromPoint(a[0]);
|
||||
const a1 = vectorFromPoint(a[1]);
|
||||
|
@ -105,12 +102,12 @@ export const segmentsIntersectAt = <Point extends GenericPoint>(
|
|||
return null;
|
||||
};
|
||||
|
||||
export const pointOnLineSegment = <Point extends GenericPoint>(
|
||||
export const segmentIncludesPoint = <Point extends GenericPoint>(
|
||||
point: Point,
|
||||
line: LineSegment<Point>,
|
||||
line: Segment<Point>,
|
||||
threshold = PRECISION,
|
||||
) => {
|
||||
const distance = distanceToLineSegment(point, line);
|
||||
const distance = segmentDistanceToPoint(point, line);
|
||||
|
||||
if (distance === 0) {
|
||||
return true;
|
||||
|
@ -119,9 +116,9 @@ export const pointOnLineSegment = <Point extends GenericPoint>(
|
|||
return distance < threshold;
|
||||
};
|
||||
|
||||
export const distanceToLineSegment = <Point extends GenericPoint>(
|
||||
export const segmentDistanceToPoint = <Point extends GenericPoint>(
|
||||
point: Point,
|
||||
line: LineSegment<Point>,
|
||||
line: Segment<Point>,
|
||||
) => {
|
||||
const [x, y] = point;
|
||||
const [[x1, y1], [x2, y2]] = line;
|
||||
|
|
|
@ -67,8 +67,8 @@ export type Line<P extends GenericPoint> = [p: P, q: P] & {
|
|||
* line that is bounded by two distinct end points, and
|
||||
* contains every point on the line that is between its endpoints.
|
||||
*/
|
||||
export type LineSegment<P extends GenericPoint> = [a: P, b: P] & {
|
||||
_brand: "excalimath_linesegment";
|
||||
export type Segment<P extends GenericPoint> = [a: P, b: P] & {
|
||||
_brand: "excalimath_segment";
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -81,23 +81,14 @@ export type Vector = [u: number, v: number] & {
|
|||
/**
|
||||
* A triangle represented by 3 points
|
||||
*/
|
||||
export type Triangle<P extends GlobalPoint | LocalPoint | ViewportPoint> = [
|
||||
a: P,
|
||||
b: P,
|
||||
c: P,
|
||||
] & {
|
||||
export type Triangle<P extends GenericPoint> = [a: P, b: P, c: P] & {
|
||||
_brand: "excalimath__triangle";
|
||||
};
|
||||
|
||||
/**
|
||||
* A rectangular shape represented by 4 points at its corners
|
||||
*/
|
||||
export type Rectangle<P extends GlobalPoint | LocalPoint | ViewportPoint> = [
|
||||
a: P,
|
||||
b: P,
|
||||
c: P,
|
||||
d: P,
|
||||
] & {
|
||||
export type Rectangle<P extends GenericPoint> = [a: P, b: P, c: P, d: P] & {
|
||||
_brand: "excalimath__rectangle";
|
||||
};
|
||||
|
||||
|
@ -105,28 +96,24 @@ export type Rectangle<P extends GlobalPoint | LocalPoint | ViewportPoint> = [
|
|||
* A polygon is a closed shape by connecting the given points
|
||||
* rectangles and diamonds are modelled by polygons
|
||||
*/
|
||||
export type Polygon<Point extends GlobalPoint | LocalPoint | ViewportPoint> =
|
||||
Point[] & {
|
||||
_brand: "excalimath_polygon";
|
||||
};
|
||||
export type Polygon<Point extends GenericPoint> = Point[] & {
|
||||
_brand: "excalimath_polygon";
|
||||
};
|
||||
|
||||
/**
|
||||
* Cubic bezier curve with four control points
|
||||
*/
|
||||
export type Curve<Point extends GlobalPoint | LocalPoint | ViewportPoint> = [
|
||||
Point,
|
||||
Point,
|
||||
Point,
|
||||
Point,
|
||||
] & {
|
||||
export type Curve<Point extends GenericPoint> = [Point, Point, Point, Point] & {
|
||||
_brand: "excalimath_curve";
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents a symmetric arc, a segment of a circular path
|
||||
*
|
||||
* Angles are in radians and centered on 0, 0. Zero radians on a 1 radius circle
|
||||
* corresponds to (1, 0) cartesian coordinates (point), i.e. to the "right"
|
||||
*/
|
||||
export type SymmetricArc<Point extends GenericPoint> = {
|
||||
export type Arc<Point extends GenericPoint> = {
|
||||
center: Point;
|
||||
radius: number;
|
||||
startAngle: Radians;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue