From 336fa9d00202f53125d716322be801a6a58c79f3 Mon Sep 17 00:00:00 2001 From: Mark Tolmacs Date: Wed, 2 Oct 2024 12:09:01 +0200 Subject: [PATCH] Master merge Signed-off-by: Mark Tolmacs --- packages/excalidraw/actions/actionCanvas.tsx | 4 +- packages/excalidraw/animated-trail.ts | 4 +- .../components/canvases/InteractiveCanvas.tsx | 4 +- .../components/hyperlink/helpers.ts | 2 +- .../element/ElementCanvasButtons.tsx | 4 +- packages/excalidraw/element/arrow.ts | 28 +++--- packages/excalidraw/element/collision.ts | 8 +- packages/excalidraw/element/distance.ts | 47 +++++----- packages/excalidraw/element/sizeHelpers.ts | 10 +-- packages/excalidraw/element/textElement.ts | 8 +- packages/excalidraw/scene/scroll.ts | 10 +-- packages/excalidraw/utils.ts | 6 +- packages/math/angle.test.ts | 6 +- packages/math/arc.ts | 12 +-- packages/math/ellipse.test.ts | 88 +++++++++++-------- packages/math/ellipse.ts | 19 ++-- packages/math/line.test.ts | 34 +++---- packages/math/line.ts | 4 +- packages/math/rectangle.test.ts | 22 ++--- packages/math/rectangle.ts | 10 +-- packages/math/segment.test.ts | 16 ++-- 21 files changed, 189 insertions(+), 157 deletions(-) diff --git a/packages/excalidraw/actions/actionCanvas.tsx b/packages/excalidraw/actions/actionCanvas.tsx index 63dddc97c6..47a8d889e3 100644 --- a/packages/excalidraw/actions/actionCanvas.tsx +++ b/packages/excalidraw/actions/actionCanvas.tsx @@ -38,7 +38,7 @@ import { DEFAULT_CANVAS_BACKGROUND_PICKS } from "../colors"; import type { ViewportBounds } from "../element/bounds"; import { setCursor } from "../cursor"; import { StoreAction } from "../store"; -import { clamp, point, roundToStep } from "../../math"; +import { clamp, pointFrom, roundToStep } from "../../math"; export const actionChangeViewBackgroundColor = register({ name: "changeViewBackgroundColor", @@ -324,7 +324,7 @@ export const zoomToFitBounds = ({ ); const centerScroll = centerScrollOn({ - scenePoint: point(centerX, centerY), + scenePoint: pointFrom(centerX, centerY), viewportDimensions: { width: appState.width, height: appState.height, diff --git a/packages/excalidraw/animated-trail.ts b/packages/excalidraw/animated-trail.ts index 0570c02e83..8bf3070774 100644 --- a/packages/excalidraw/animated-trail.ts +++ b/packages/excalidraw/animated-trail.ts @@ -5,7 +5,7 @@ import type { AppState } from "./types"; import { getSvgPathFromStroke, sceneCoordsToViewportCoords } from "./utils"; import type App from "./components/App"; import { SVG_NS } from "./constants"; -import { point } from "../math"; +import { pointFrom } from "../math"; export interface Trail { start(container: SVGSVGElement): void; @@ -136,7 +136,7 @@ export class AnimatedTrail implements Trail { private drawTrail(trail: LaserPointer, state: AppState): string { const stroke = trail .getStrokeOutline(trail.options.size / state.zoom.value) - .map((p) => sceneCoordsToViewportCoords(point(p[0], p[1]), state)); + .map((p) => sceneCoordsToViewportCoords(pointFrom(p[0], p[1]), state)); return getSvgPathFromStroke(stroke, true); } diff --git a/packages/excalidraw/components/canvases/InteractiveCanvas.tsx b/packages/excalidraw/components/canvases/InteractiveCanvas.tsx index 93fbedd393..79a3f840c6 100644 --- a/packages/excalidraw/components/canvases/InteractiveCanvas.tsx +++ b/packages/excalidraw/components/canvases/InteractiveCanvas.tsx @@ -15,7 +15,7 @@ import type { } from "../../element/types"; import { isRenderThrottlingEnabled } from "../../reactUtils"; import { renderInteractiveScene } from "../../renderer/interactiveScene"; -import { point } from "../../../math"; +import { pointFrom } from "../../../math"; type InteractiveCanvasProps = { containerRef: React.RefObject; @@ -104,7 +104,7 @@ const InteractiveCanvas = (props: InteractiveCanvasProps) => { remotePointerViewportCoords.set( socketId, sceneCoordsToViewportCoords( - point(user.pointer.x, user.pointer.y), + pointFrom(user.pointer.x, user.pointer.y), props.appState, ), ); diff --git a/packages/excalidraw/components/hyperlink/helpers.ts b/packages/excalidraw/components/hyperlink/helpers.ts index 21cb744fa0..8090ef0429 100644 --- a/packages/excalidraw/components/hyperlink/helpers.ts +++ b/packages/excalidraw/components/hyperlink/helpers.ts @@ -81,7 +81,7 @@ export const isPointHittingLink = ( if ( !isMobile && appState.viewModeEnabled && - hitElementBoundingBox(point(x, y), element, elementsMap) + hitElementBoundingBox(pointFrom(x, y), element, elementsMap) ) { return true; } diff --git a/packages/excalidraw/element/ElementCanvasButtons.tsx b/packages/excalidraw/element/ElementCanvasButtons.tsx index 22b2d87b9d..c5577bae88 100644 --- a/packages/excalidraw/element/ElementCanvasButtons.tsx +++ b/packages/excalidraw/element/ElementCanvasButtons.tsx @@ -5,7 +5,7 @@ import { getElementAbsoluteCoords } from "."; import { useExcalidrawAppState } from "../components/App"; import "./ElementCanvasButtons.scss"; -import { point } from "../../math"; +import { pointFrom } from "../../math"; const CONTAINER_PADDING = 5; @@ -16,7 +16,7 @@ const getContainerCoords = ( ) => { const [x1, y1] = getElementAbsoluteCoords(element, elementsMap); const [viewportX, viewportY] = sceneCoordsToViewportCoords( - point(x1 + element.width, y1), + pointFrom(x1 + element.width, y1), appState, ); const x = viewportX - appState.offsetLeft + 10; diff --git a/packages/excalidraw/element/arrow.ts b/packages/excalidraw/element/arrow.ts index 63717ca2fd..7cbfd8e1c9 100644 --- a/packages/excalidraw/element/arrow.ts +++ b/packages/excalidraw/element/arrow.ts @@ -2,7 +2,7 @@ import type { Drawable } from "roughjs/bin/core"; import { degrees, degreesToRadians, - point, + pointFrom, pointFromArray, pointRotateRads, radians, @@ -55,21 +55,21 @@ export const getArrowheadPoints = ( invariant(data.length === 6, "Op data length is not 6"); - const p3 = point(data[4], data[5]); - const p2 = point(data[2], data[3]); - const p1 = point(data[0], data[1]); + const p3 = pointFrom(data[4], data[5]); + const p2 = pointFrom(data[2], data[3]); + const p1 = pointFrom(data[0], data[1]); // We need to find p0 of the bezier curve. // It is typically the last point of the previous // curve; it can also be the position of moveTo operation. const prevOp = ops[index - 1]; - let p0 = point(0, 0); + let p0 = pointFrom(0, 0); if (prevOp.op === "move") { const p = pointFromArray(prevOp.data); invariant(p != null, "Op data is not a point"); p0 = p; } else if (prevOp.op === "bcurveTo") { - p0 = point(prevOp.data[4], prevOp.data[5]); + p0 = pointFrom(prevOp.data[4], prevOp.data[5]); } // B(t) = p0 * (1-t)^3 + 3p1 * t * (1-t)^2 + 3p2 * t^2 * (1-t) + p3 * t^3 @@ -135,13 +135,13 @@ export const getArrowheadPoints = ( // Return points const [x3, y3] = pointRotateRads( - point(xs, ys), - point(x2, y2), + pointFrom(xs, ys), + pointFrom(x2, y2), radians((-angle * Math.PI) / 180), ); const [x4, y4] = pointRotateRads( - point(xs, ys), - point(x2, y2), + pointFrom(xs, ys), + pointFrom(x2, y2), degreesToRadians(angle), ); @@ -154,8 +154,8 @@ export const getArrowheadPoints = ( const [px, py] = element.points.length > 1 ? element.points[1] : [0, 0]; [ox, oy] = pointRotateRads( - point(x2 + minSize * 2, y2), - point(x2, y2), + pointFrom(x2 + minSize * 2, y2), + pointFrom(x2, y2), radians(Math.atan2(py - y2, px - x2)), ); } else { @@ -165,8 +165,8 @@ export const getArrowheadPoints = ( : [0, 0]; [ox, oy] = pointRotateRads( - point(x2 - minSize * 2, y2), - point(x2, y2), + pointFrom(x2 - minSize * 2, y2), + pointFrom(x2, y2), radians(Math.atan2(y2 - py, x2 - px)), ); } diff --git a/packages/excalidraw/element/collision.ts b/packages/excalidraw/element/collision.ts index b39593f798..5a7f805b52 100644 --- a/packages/excalidraw/element/collision.ts +++ b/packages/excalidraw/element/collision.ts @@ -227,7 +227,9 @@ export const intersectRectanguloidWithLine = ( lineSegmentIntersectionPoints(line(rotatedA, rotatedB), s), ) .filter((x) => x != null) - .map((j) => pointRotateRads(j, center, element.angle)); + .map((j: GlobalPoint) => + pointRotateRads(j, center, element.angle), + ); const cornerIntersections: GlobalPoint[] = roundness > 0 ? [ @@ -335,7 +337,9 @@ export const intersectDiamondWithLine = ( ) .filter((x) => x != null) // Rotate back intersection points - .map((p) => pointRotateRads(p, center, element.angle)); + .map((p: GlobalPoint) => + pointRotateRads(p, center, element.angle), + ); const corners = arcs .flatMap((x) => arcLineInterceptPoints(x, line(rotatedA, rotatedB))) .filter((x) => x != null) diff --git a/packages/excalidraw/element/distance.ts b/packages/excalidraw/element/distance.ts index dba1beb525..66cbaf3c3d 100644 --- a/packages/excalidraw/element/distance.ts +++ b/packages/excalidraw/element/distance.ts @@ -4,7 +4,7 @@ import { arcDistanceFromPoint, ellipse, ellipseDistanceFromPoint, - point, + pointFrom, pointRotateRads, radians, rectangle, @@ -53,14 +53,14 @@ export const distanceToRectangleElement = ( p: GlobalPoint, ) => { const r = rectangle( - point(element.x, element.y), - point(element.x + element.width, element.y + element.height), + pointFrom(element.x, element.y), + pointFrom(element.x + element.width, element.y + element.height), ); // To emulate a rotated rectangle we rotate the point in the inverse angle // instead. It's all the same distance-wise. const rotatedPoint = pointRotateRads( p, - point(element.x + element.width / 2, element.y + element.height / 2), + pointFrom(element.x + element.width / 2, element.y + element.height / 2), radians(-element.angle), ); const roundness = getCornerRadius( @@ -69,45 +69,45 @@ export const distanceToRectangleElement = ( ); const sideDistances = [ segment( - point(r[0][0] + roundness, r[0][1]), - point(r[1][0] - roundness, r[0][1]), + pointFrom(r[0][0] + roundness, r[0][1]), + pointFrom(r[1][0] - roundness, r[0][1]), ), segment( - point(r[1][0], r[0][1] + roundness), - point(r[1][0], r[1][1] - roundness), + pointFrom(r[1][0], r[0][1] + roundness), + pointFrom(r[1][0], r[1][1] - roundness), ), segment( - point(r[1][0] - roundness, r[1][1]), - point(r[0][0] + roundness, r[1][1]), + pointFrom(r[1][0] - roundness, r[1][1]), + pointFrom(r[0][0] + roundness, r[1][1]), ), segment( - point(r[0][0], r[1][1] - roundness), - point(r[0][0], r[0][1] + roundness), + pointFrom(r[0][0], r[1][1] - roundness), + pointFrom(r[0][0], r[0][1] + roundness), ), ].map((s) => segmentDistanceToPoint(rotatedPoint, s)); const cornerDistances = roundness > 0 ? [ arc( - point(r[0][0] + roundness, r[0][1] + roundness), + pointFrom(r[0][0] + roundness, r[0][1] + roundness), roundness, radians(Math.PI), radians((3 / 4) * Math.PI), ), arc( - point(r[1][0] - roundness, r[0][1] + roundness), + pointFrom(r[1][0] - roundness, r[0][1] + roundness), roundness, radians((3 / 4) * Math.PI), radians(0), ), arc( - point(r[1][0] - roundness, r[1][1] - roundness), + pointFrom(r[1][0] - roundness, r[1][1] - roundness), roundness, radians(0), radians((1 / 2) * Math.PI), ), arc( - point(r[0][0] + roundness, r[1][1] - roundness), + pointFrom(r[0][0] + roundness, r[1][1] - roundness), roundness, radians((1 / 2) * Math.PI), radians(Math.PI), @@ -132,7 +132,10 @@ export const distanceToDiamondElement = ( ): number => { const [topX, topY, rightX, rightY, bottomX, bottomY, leftX, leftY] = getDiamondPoints(element); - const center = point((topX + bottomX) / 2, (topY + bottomY) / 2); + const center = pointFrom( + (topX + bottomX) / 2, + (topY + bottomY) / 2, + ); const verticalRadius = getCornerRadius(Math.abs(topX - leftX), element); const horizontalRadius = getCornerRadius(Math.abs(rightY - topY), element); @@ -140,10 +143,10 @@ export const distanceToDiamondElement = ( // points. It's all the same distance-wise. const rotatedPoint = pointRotateRads(p, center, radians(-element.angle)); const [top, right, bottom, left]: GlobalPoint[] = [ - point(element.x + topX, element.y + topY), - point(element.x + rightX, element.y + rightY), - point(element.x + bottomX, element.y + bottomY), - point(element.x + leftX, element.y + leftY), + pointFrom(element.x + topX, element.y + topY), + pointFrom(element.x + rightX, element.y + rightY), + pointFrom(element.x + bottomX, element.y + bottomY), + pointFrom(element.x + leftX, element.y + leftY), ]; const topRight = createDiamondSide( @@ -198,7 +201,7 @@ export const distanceToEllipseElement = ( element: ExcalidrawEllipseElement, p: GlobalPoint, ): number => { - const center = point( + const center = pointFrom( element.x + element.width / 2, element.y + element.height / 2, ); diff --git a/packages/excalidraw/element/sizeHelpers.ts b/packages/excalidraw/element/sizeHelpers.ts index 7a1deb462d..baee7fe3c9 100644 --- a/packages/excalidraw/element/sizeHelpers.ts +++ b/packages/excalidraw/element/sizeHelpers.ts @@ -5,7 +5,7 @@ import { SHIFT_LOCKING_ANGLE } from "../constants"; import type { AppState, Offsets, Zoom } from "../types"; import { getCommonBounds, getElementBounds } from "./bounds"; import { viewportCoordsToSceneCoords } from "../utils"; -import { point } from "../../math"; +import { pointFrom } from "../../math"; // TODO: remove invisible elements consistently actions, so that invisible elements are not recorded by the store, exported, broadcasted or persisted // - perhaps could be as part of a standalone 'cleanup' action, in addition to 'finalize' @@ -34,11 +34,11 @@ export const isElementInViewport = ( ) => { const [x1, y1, x2, y2] = getElementBounds(element, elementsMap); // scene coordinates const topLeftSceneCoords = viewportCoordsToSceneCoords( - point(viewTransformations.offsetLeft, viewTransformations.offsetTop), + pointFrom(viewTransformations.offsetLeft, viewTransformations.offsetTop), viewTransformations, ); const bottomRightSceneCoords = viewportCoordsToSceneCoords( - point( + pointFrom( viewTransformations.offsetLeft + width, viewTransformations.offsetTop + height, ), @@ -69,14 +69,14 @@ export const isElementCompletelyInViewport = ( ) => { const [x1, y1, x2, y2] = getCommonBounds(elements, elementsMap); // scene coordinates const topLeftSceneCoords = viewportCoordsToSceneCoords( - point( + pointFrom( viewTransformations.offsetLeft + (padding?.left || 0), viewTransformations.offsetTop + (padding?.top || 0), ), viewTransformations, ); const bottomRightSceneCoords = viewportCoordsToSceneCoords( - point( + pointFrom( viewTransformations.offsetLeft + width - (padding?.right || 0), viewTransformations.offsetTop + height - (padding?.bottom || 0), ), diff --git a/packages/excalidraw/element/textElement.ts b/packages/excalidraw/element/textElement.ts index 8a1bb21362..3275d92d84 100644 --- a/packages/excalidraw/element/textElement.ts +++ b/packages/excalidraw/element/textElement.ts @@ -30,7 +30,7 @@ import { } from "./containerCache"; import type { ExtractSetType } from "../utility-types"; import type { GlobalPoint } from "../../math"; -import { point } from "../../math"; +import { pointFrom } from "../../math"; export const normalizeText = (text: string) => { return ( @@ -678,7 +678,7 @@ export const getContainerCenter = ( elementsMap: ElementsMap, ): GlobalPoint => { if (!isArrowElement(container)) { - return point( + return pointFrom( container.x + container.width / 2, container.y + container.height / 2, ); @@ -694,7 +694,7 @@ export const getContainerCenter = ( container.points[index], elementsMap, ); - return point(midPoint[0], midPoint[1]); + return pointFrom(midPoint[0], midPoint[1]); } const index = container.points.length / 2 - 1; let midSegmentMidpoint = LinearElementEditor.getEditorMidPoints( @@ -711,7 +711,7 @@ export const getContainerCenter = ( elementsMap, ); } - return point(midSegmentMidpoint[0], midSegmentMidpoint[1]); + return pointFrom(midSegmentMidpoint[0], midSegmentMidpoint[1]); }; export const getContainerCoords = (container: NonDeletedExcalidrawElement) => { diff --git a/packages/excalidraw/scene/scroll.ts b/packages/excalidraw/scene/scroll.ts index 9fe7dfda24..af2e0b0c43 100644 --- a/packages/excalidraw/scene/scroll.ts +++ b/packages/excalidraw/scene/scroll.ts @@ -11,16 +11,16 @@ import { tupleToCoors, viewportCoordsToSceneCoords, } from "../utils"; -import { point, type GlobalPoint } from "../../math"; +import { pointFrom, type GlobalPoint } from "../../math"; const isOutsideViewPort = (appState: AppState, cords: Array) => { const [x1, y1, x2, y2] = cords; const [viewportX1, viewportY1] = sceneCoordsToViewportCoords( - point(x1, y1), + pointFrom(x1, y1), appState, ); const [viewportX2, viewportY2] = sceneCoordsToViewportCoords( - point(x2, y2), + pointFrom(x2, y2), appState, ); return ( @@ -77,7 +77,7 @@ export const calculateScrollCenter = ( elements, tupleToCoors( viewportCoordsToSceneCoords( - point(appState.scrollX, appState.scrollY), + pointFrom(appState.scrollX, appState.scrollY), appState, ), ), @@ -88,7 +88,7 @@ export const calculateScrollCenter = ( const centerY = (y1 + y2) / 2; return centerScrollOn({ - scenePoint: point(centerX, centerY), + scenePoint: pointFrom(centerX, centerY), viewportDimensions: { width: appState.width, height: appState.height }, zoom: appState.zoom, }); diff --git a/packages/excalidraw/utils.ts b/packages/excalidraw/utils.ts index c244238fce..ec8ea322d1 100644 --- a/packages/excalidraw/utils.ts +++ b/packages/excalidraw/utils.ts @@ -1,5 +1,5 @@ import type { GlobalPoint, ViewportPoint } from "../math"; -import { average, point } from "../math"; +import { average, pointFrom } from "../math"; import { COLOR_PALETTE } from "./colors"; import type { EVENT } from "./constants"; import { @@ -436,7 +436,7 @@ export const viewportCoordsToSceneCoords = ( const x = (clientX - offsetLeft) / zoom.value - scrollX; const y = (clientY - offsetTop) / zoom.value - scrollY; - return point(x, y); + return pointFrom(x, y); }; export const sceneCoordsToViewportCoords = ( @@ -457,7 +457,7 @@ export const sceneCoordsToViewportCoords = ( ): ViewportPoint => { const x = (sceneX + scrollX) * zoom.value + offsetLeft; const y = (sceneY + scrollY) * zoom.value + offsetTop; - return point(x, y); + return pointFrom(x, y); }; export const getGlobalCSSVariable = (name: string) => diff --git a/packages/math/angle.test.ts b/packages/math/angle.test.ts index 74e9d679dc..21f882ba34 100644 --- a/packages/math/angle.test.ts +++ b/packages/math/angle.test.ts @@ -1,12 +1,12 @@ import { cartesian2Polar, polar, radians } from "./angle"; -import { point } from "./point"; +import { pointFrom } from "./point"; describe("cartesian to polar coordinate conversion", () => { it("converts values properly", () => { - expect(cartesian2Polar(point(12, 5))).toEqual( + expect(cartesian2Polar(pointFrom(12, 5))).toEqual( polar(13, radians(Math.atan(5 / 12))), ); - expect(cartesian2Polar(point(5, 5))).toEqual( + expect(cartesian2Polar(pointFrom(5, 5))).toEqual( polar(5 * Math.sqrt(2), radians(Math.PI / 4)), ); }); diff --git a/packages/math/arc.ts b/packages/math/arc.ts index 016d1032e2..4e58f4fc31 100644 --- a/packages/math/arc.ts +++ b/packages/math/arc.ts @@ -5,7 +5,7 @@ import { ellipseLineIntersectionPoints, ellipseSegmentInterceptPoints, } from "./ellipse"; -import { point, pointDistance } from "./point"; +import { pointFrom, pointDistance } from "./point"; import type { GenericPoint, Segment, Radians, Arc, Line } from "./types"; import { PRECISION } from "./utils"; @@ -37,7 +37,7 @@ export function arcIncludesPoint

( p: P, ): boolean { const [radius, angle] = cartesian2Polar( - point(p[0] - center[0], p[1] - center[1]), + pointFrom(p[0] - center[0], p[1] - center[1]), ); return startAngle < endAngle @@ -69,14 +69,14 @@ export function arcDistanceFromPoint( return Math.min( pointDistance( p, - point( + pointFrom( a.center[0] + a.radius + Math.cos(a.startAngle), a.center[1] + a.radius + Math.sin(a.startAngle), ), ), pointDistance( p, - point( + pointFrom( a.center[0] + a.radius + Math.cos(a.endAngle), a.center[1] + a.radius + Math.sin(a.endAngle), ), @@ -97,7 +97,7 @@ export function arcSegmentInterceptPoints( s, ).filter((candidate) => { const [candidateRadius, candidateAngle] = cartesian2Polar( - point(candidate[0] - a.center[0], candidate[1] - a.center[1]), + pointFrom(candidate[0] - a.center[0], candidate[1] - a.center[1]), ); return a.startAngle < a.endAngle @@ -125,7 +125,7 @@ export function arcLineInterceptPoints( l, ).filter((candidate) => { const [candidateRadius, candidateAngle] = cartesian2Polar( - point(candidate[0] - a.center[0], candidate[1] - a.center[1]), + pointFrom(candidate[0] - a.center[0], candidate[1] - a.center[1]), ); return a.startAngle < a.endAngle diff --git a/packages/math/ellipse.test.ts b/packages/math/ellipse.test.ts index cf87a19705..6d4c546683 100644 --- a/packages/math/ellipse.test.ts +++ b/packages/math/ellipse.test.ts @@ -6,105 +6,121 @@ import { ellipseLineIntersectionPoints, } from "./ellipse"; import { line } from "./line"; -import { point } from "./point"; +import { pointFrom } from "./point"; import { segment } from "./segment"; import type { Ellipse, GlobalPoint } from "./types"; describe("point and ellipse", () => { it("point on ellipse", () => { - const target: Ellipse = ellipse(point(1, 2), 2, 1); - [point(1, 3), point(1, 1), point(3, 2), point(-1, 2)].forEach((p) => { + const target: Ellipse = ellipse(pointFrom(1, 2), 2, 1); + [ + pointFrom(1, 3), + pointFrom(1, 1), + pointFrom(3, 2), + pointFrom(-1, 2), + ].forEach((p) => { expect(ellipseTouchesPoint(p, target)).toBe(true); }); - expect(ellipseTouchesPoint(point(-0.4, 2.7), target, 0.1)).toBe(true); - expect(ellipseTouchesPoint(point(-0.4, 2.71), target, 0.01)).toBe(true); + expect(ellipseTouchesPoint(pointFrom(-0.4, 2.7), target, 0.1)).toBe(true); + expect(ellipseTouchesPoint(pointFrom(-0.4, 2.71), target, 0.01)).toBe(true); - expect(ellipseTouchesPoint(point(2.4, 2.7), target, 0.1)).toBe(true); - expect(ellipseTouchesPoint(point(2.4, 2.71), target, 0.01)).toBe(true); + expect(ellipseTouchesPoint(pointFrom(2.4, 2.7), target, 0.1)).toBe(true); + expect(ellipseTouchesPoint(pointFrom(2.4, 2.71), target, 0.01)).toBe(true); - expect(ellipseTouchesPoint(point(2, 1.14), target, 0.1)).toBe(true); - expect(ellipseTouchesPoint(point(2, 1.14), target, 0.01)).toBe(true); + expect(ellipseTouchesPoint(pointFrom(2, 1.14), target, 0.1)).toBe(true); + expect(ellipseTouchesPoint(pointFrom(2, 1.14), target, 0.01)).toBe(true); - expect(ellipseTouchesPoint(point(0, 1.14), target, 0.1)).toBe(true); - expect(ellipseTouchesPoint(point(0, 1.14), target, 0.01)).toBe(true); + expect(ellipseTouchesPoint(pointFrom(0, 1.14), target, 0.1)).toBe(true); + expect(ellipseTouchesPoint(pointFrom(0, 1.14), target, 0.01)).toBe(true); - expect(ellipseTouchesPoint(point(0, 2.8), target)).toBe(false); - expect(ellipseTouchesPoint(point(2, 1.2), target)).toBe(false); + expect(ellipseTouchesPoint(pointFrom(0, 2.8), target)).toBe(false); + expect(ellipseTouchesPoint(pointFrom(2, 1.2), target)).toBe(false); }); it("point in ellipse", () => { - const target: Ellipse = ellipse(point(0, 0), 2, 1); - [point(0, 1), point(0, -1), point(2, 0), point(-2, 0)].forEach((p) => { + const target: Ellipse = ellipse(pointFrom(0, 0), 2, 1); + [ + pointFrom(0, 1), + pointFrom(0, -1), + pointFrom(2, 0), + pointFrom(-2, 0), + ].forEach((p) => { expect(ellipseIncludesPoint(p, target)).toBe(true); }); - expect(ellipseIncludesPoint(point(-1, 0.8), target)).toBe(true); - expect(ellipseIncludesPoint(point(1, -0.8), target)).toBe(true); + expect(ellipseIncludesPoint(pointFrom(-1, 0.8), target)).toBe(true); + expect(ellipseIncludesPoint(pointFrom(1, -0.8), target)).toBe(true); // Point on outline - expect(ellipseIncludesPoint(point(2, 0), target)).toBe(true); + expect(ellipseIncludesPoint(pointFrom(2, 0), target)).toBe(true); - expect(ellipseIncludesPoint(point(-1, 1), target)).toBe(false); - expect(ellipseIncludesPoint(point(-1.4, 0.8), target)).toBe(false); + expect(ellipseIncludesPoint(pointFrom(-1, 1), target)).toBe(false); + expect(ellipseIncludesPoint(pointFrom(-1.4, 0.8), target)).toBe(false); }); }); describe("segment and ellipse", () => { it("detects outside segment", () => { - const e = ellipse(point(0, 0), 2, 2); + const e = ellipse(pointFrom(0, 0), 2, 2); expect( ellipseSegmentInterceptPoints( e, - segment(point(-100, 0), point(-10, 0)), + segment(pointFrom(-100, 0), pointFrom(-10, 0)), ), ).toEqual([]); expect( ellipseSegmentInterceptPoints( e, - segment(point(-10, 0), point(10, 0)), + segment(pointFrom(-10, 0), pointFrom(10, 0)), ), - ).toEqual([point(-2, 0), point(2, 0)]); + ).toEqual([pointFrom(-2, 0), pointFrom(2, 0)]); expect( ellipseSegmentInterceptPoints( e, - segment(point(-10, -2), point(10, -2)), + segment(pointFrom(-10, -2), pointFrom(10, -2)), ), - ).toEqual([point(0, -2)]); + ).toEqual([pointFrom(0, -2)]); expect( ellipseSegmentInterceptPoints( e, - segment(point(0, -1), point(0, 1)), + segment(pointFrom(0, -1), pointFrom(0, 1)), ), ).toEqual([]); }); }); describe("line and ellipse", () => { - const e = ellipse(point(0, 0), 2, 2); + const e = ellipse(pointFrom(0, 0), 2, 2); it("detects outside line", () => { expect( ellipseLineIntersectionPoints( e, - line(point(-10, -10), point(10, -10)), + line(pointFrom(-10, -10), pointFrom(10, -10)), ), ).toEqual([]); }); it("detects line intersecting ellipse", () => { expect( - ellipseLineIntersectionPoints(e, line(point(0, -1), point(0, 1))), - ).toEqual([point(0, 2), point(0, -2)]); + ellipseLineIntersectionPoints( + e, + line(pointFrom(0, -1), pointFrom(0, 1)), + ), + ).toEqual([pointFrom(0, 2), pointFrom(0, -2)]); expect( ellipseLineIntersectionPoints( e, - line(point(-100, 0), point(-10, 0)), - ).map(([x, y]) => point(Math.round(x), Math.round(y))), - ).toEqual([point(2, 0), point(-2, 0)]); + line(pointFrom(-100, 0), pointFrom(-10, 0)), + ).map(([x, y]) => pointFrom(Math.round(x), Math.round(y))), + ).toEqual([pointFrom(2, 0), pointFrom(-2, 0)]); }); it("detects line touching ellipse", () => { expect( - ellipseLineIntersectionPoints(e, line(point(-2, -2), point(2, -2))), - ).toEqual([point(0, -2)]); + ellipseLineIntersectionPoints( + e, + line(pointFrom(-2, -2), pointFrom(2, -2)), + ), + ).toEqual([pointFrom(0, -2)]); }); }); diff --git a/packages/math/ellipse.ts b/packages/math/ellipse.ts index 8e6b258842..f8d2c1d7bf 100644 --- a/packages/math/ellipse.ts +++ b/packages/math/ellipse.ts @@ -1,4 +1,9 @@ -import { point, pointDistance, pointFromVector, pointsEqual } from "./point"; +import { + pointFrom, + pointDistance, + pointFromVector, + pointsEqual, +} from "./point"; import type { Ellipse, GenericPoint, Line, Segment } from "./types"; import { PRECISION } from "./utils"; import { @@ -119,7 +124,7 @@ export const ellipseDistanceFromPoint = ( b * ty * Math.sign(translatedPoint[1]), ]; - return pointDistance(pointFromVector(translatedPoint), point(minX, minY)); + return pointDistance(pointFromVector(translatedPoint), pointFrom(minX, minY)); }; /** @@ -151,7 +156,7 @@ export function ellipseSegmentInterceptPoints( if (0 <= t_a && t_a <= 1) { intersections.push( - point( + pointFrom( s[0][0] + (s[1][0] - s[0][0]) * t_a, s[0][1] + (s[1][1] - s[0][1]) * t_a, ), @@ -160,7 +165,7 @@ export function ellipseSegmentInterceptPoints( if (0 <= t_b && t_b <= 1) { intersections.push( - point( + pointFrom( s[0][0] + (s[1][0] - s[0][0]) * t_b, s[0][1] + (s[1][1] - s[0][1]) * t_b, ), @@ -170,7 +175,7 @@ export function ellipseSegmentInterceptPoints( const t = -b / a; if (0 <= t && t <= 1) { intersections.push( - point( + pointFrom( s[0][0] + (s[1][0] - s[0][0]) * t, s[0][1] + (s[1][1] - s[0][1]) * t, ), @@ -204,8 +209,8 @@ export function ellipseLineIntersectionPoints( const t1 = (-b + Math.sqrt(Math.pow(b, 2) - 4 * a * c)) / (2 * a); const t2 = (-b - Math.sqrt(Math.pow(b, 2) - 4 * a * c)) / (2 * a); const candidates = [ - point(x1 + t1 * (x2 - x1) + cx, y1 + t1 * (y2 - y1) + cy), - point(x1 + t2 * (x2 - x1) + cx, y1 + t2 * (y2 - y1) + cy), + pointFrom(x1 + t1 * (x2 - x1) + cx, y1 + t1 * (y2 - y1) + cy), + pointFrom(x1 + t2 * (x2 - x1) + cx, y1 + t2 * (y2 - y1) + cy), ].filter((p) => !isNaN(p[0]) && !isNaN(p[1])); if (candidates.length === 2 && pointsEqual(candidates[0], candidates[1])) { diff --git a/packages/math/line.test.ts b/packages/math/line.test.ts index a47eba2869..aae556f45b 100644 --- a/packages/math/line.test.ts +++ b/packages/math/line.test.ts @@ -1,31 +1,35 @@ -import { line, lineLineIntersectionPoint, lineSegmentIntersectionPoints } from "./line"; -import { point } from "./point"; +import { + line, + lineLineIntersectionPoint, + lineSegmentIntersectionPoints, +} from "./line"; +import { pointFrom } from "./point"; import { segment } from "./segment"; describe("line-line intersections", () => { it("should correctly detect intersection at origin", () => { expect( lineLineIntersectionPoint( - line(point(-5, -5), point(5, 5)), - line(point(5, -5), point(-5, 5)), + line(pointFrom(-5, -5), pointFrom(5, 5)), + line(pointFrom(5, -5), pointFrom(-5, 5)), ), - ).toEqual(point(0, 0)); + ).toEqual(pointFrom(0, 0)); }); it("should correctly detect intersection at non-origin", () => { expect( lineLineIntersectionPoint( - line(point(0, 0), point(10, 10)), - line(point(10, 0), point(0, 10)), + line(pointFrom(0, 0), pointFrom(10, 10)), + line(pointFrom(10, 0), pointFrom(0, 10)), ), - ).toEqual(point(5, 5)); + ).toEqual(pointFrom(5, 5)); }); it("should correctly detect parallel lines", () => { expect( lineLineIntersectionPoint( - line(point(0, 0), point(0, 10)), - line(point(10, 0), point(10, 10)), + line(pointFrom(0, 0), pointFrom(0, 10)), + line(pointFrom(10, 0), pointFrom(10, 10)), ), ).toBe(null); }); @@ -35,16 +39,16 @@ describe("line-segment intersections", () => { it("should correctly detect intersection", () => { expect( lineSegmentIntersectionPoints( - line(point(0, 0), point(5, 0)), - segment(point(2, -2), point(3, 2)), + line(pointFrom(0, 0), pointFrom(5, 0)), + segment(pointFrom(2, -2), pointFrom(3, 2)), ), - ).toEqual(point(2.5, -0)); + ).toEqual(pointFrom(2.5, -0)); }); it("should correctly detect non-intersection", () => { expect( lineSegmentIntersectionPoints( - line(point(0, 0), point(5, 0)), - segment(point(3, 1), point(4, 4)), + line(pointFrom(0, 0), pointFrom(5, 0)), + segment(pointFrom(3, 1), pointFrom(4, 4)), ), ).toEqual(null); }); diff --git a/packages/math/line.ts b/packages/math/line.ts index 0fadb64a5d..b00ffb10db 100644 --- a/packages/math/line.ts +++ b/packages/math/line.ts @@ -1,5 +1,5 @@ import { ellipseLineIntersectionPoints } from "./ellipse"; -import { point, pointCenter, pointRotateRads } from "./point"; +import { pointFrom, pointCenter, pointRotateRads } from "./point"; import { segmentIncludesPoint } from "./segment"; import type { GenericPoint, Line, Radians, Segment } from "./types"; @@ -73,7 +73,7 @@ export function lineLineIntersectionPoint( const xnum = a * (x3 - x4) - (x1 - x2) * c; const ynum = a * (y3 - y4) - (y1 - y2) * c; - return point(xnum / den, ynum / den); + return pointFrom(xnum / den, ynum / den); } /** diff --git a/packages/math/rectangle.test.ts b/packages/math/rectangle.test.ts index 632ae4562d..372bea3789 100644 --- a/packages/math/rectangle.test.ts +++ b/packages/math/rectangle.test.ts @@ -1,38 +1,38 @@ -import { point } from "./point"; +import { pointFrom } from "./point"; import { rectangle, rectangleDistanceFromPoint } from "./rectangle"; describe("rectangle distance", () => { it("finds the shortest distance", () => { expect( rectangleDistanceFromPoint( - rectangle(point(-1, -1), point(1, 1)), - point(2, 0), + rectangle(pointFrom(-1, -1), pointFrom(1, 1)), + pointFrom(2, 0), ), ).toBe(1); expect( rectangleDistanceFromPoint( - rectangle(point(-1, -1), point(1, 1)), - point(0, 2), + rectangle(pointFrom(-1, -1), pointFrom(1, 1)), + pointFrom(0, 2), ), ).toBe(1); expect( rectangleDistanceFromPoint( - rectangle(point(-1, -1), point(1, 1)), - point(-2, 0), + rectangle(pointFrom(-1, -1), pointFrom(1, 1)), + pointFrom(-2, 0), ), ).toBe(1); expect( rectangleDistanceFromPoint( - rectangle(point(-1, -1), point(1, 1)), - point(0, -2), + rectangle(pointFrom(-1, -1), pointFrom(1, 1)), + pointFrom(0, -2), ), ).toBe(1); }); it("finds the corner as closest point", () => { expect( rectangleDistanceFromPoint( - rectangle(point(-1, -1), point(1, 1)), - point(2, 2), + rectangle(pointFrom(-1, -1), pointFrom(1, 1)), + pointFrom(2, 2), ), ).toBe(Math.sqrt(2)); }); diff --git a/packages/math/rectangle.ts b/packages/math/rectangle.ts index f0c76a1c21..86b9b18beb 100644 --- a/packages/math/rectangle.ts +++ b/packages/math/rectangle.ts @@ -1,5 +1,5 @@ import { invariant } from "../excalidraw/utils"; -import { point } from "./point"; +import { pointFrom } from "./point"; import { segment, segmentDistanceToPoint } from "./segment"; import type { GenericPoint, Rectangle } from "./types"; @@ -32,10 +32,10 @@ export function rectangleDistanceFromPoint( p: Point, ): number { const sides = [ - segment(point(r[0][0], r[0][1]), point(r[1][0], r[0][1])), - segment(point(r[1][0], r[0][1]), point(r[1][0], r[1][1])), - segment(point(r[1][0], r[1][1]), point(r[0][0], r[1][1])), - segment(point(r[0][0], r[1][1]), point(r[0][0], r[0][1])), + segment(pointFrom(r[0][0], r[0][1]), pointFrom(r[1][0], r[0][1])), + segment(pointFrom(r[1][0], r[0][1]), pointFrom(r[1][0], r[1][1])), + segment(pointFrom(r[1][0], r[1][1]), pointFrom(r[0][0], r[1][1])), + segment(pointFrom(r[0][0], r[1][1]), pointFrom(r[0][0], r[0][1])), ]; return Math.min(...sides.map((side) => segmentDistanceToPoint(p, side))); diff --git a/packages/math/segment.test.ts b/packages/math/segment.test.ts index c956923e40..9df783c0dc 100644 --- a/packages/math/segment.test.ts +++ b/packages/math/segment.test.ts @@ -1,15 +1,15 @@ -import { point } from "./point"; +import { pointFrom } from "./point"; import { segment, segmentsIntersectAt } from "./segment"; import type { GlobalPoint, Segment } from "./types"; describe("segment intersects segment", () => { - const lineA: Segment = segment(point(1, 4), point(3, 4)); - const lineB: Segment = segment(point(2, 1), point(2, 7)); - const lineC: Segment = segment(point(1, 8), point(3, 8)); - const lineD: Segment = segment(point(1, 8), point(3, 8)); - const lineE: Segment = segment(point(1, 9), point(3, 9)); - const lineF: Segment = segment(point(1, 2), point(3, 4)); - const lineG: Segment = segment(point(0, 1), point(2, 3)); + const lineA: Segment = segment(pointFrom(1, 4), pointFrom(3, 4)); + const lineB: Segment = segment(pointFrom(2, 1), pointFrom(2, 7)); + const lineC: Segment = segment(pointFrom(1, 8), pointFrom(3, 8)); + const lineD: Segment = segment(pointFrom(1, 8), pointFrom(3, 8)); + const lineE: Segment = segment(pointFrom(1, 9), pointFrom(3, 9)); + const lineF: Segment = segment(pointFrom(1, 2), pointFrom(3, 4)); + const lineG: Segment = segment(pointFrom(0, 1), pointFrom(2, 3)); it("intersection", () => { expect(segmentsIntersectAt(lineA, lineB)).toEqual([2, 4]);