From 34ec751501ef1838ae73ffe611d7b989571972bf Mon Sep 17 00:00:00 2001 From: Mark Tolmacs Date: Fri, 27 Sep 2024 20:27:10 +0200 Subject: [PATCH] Attempt at optimizing focus point calc --- packages/excalidraw/element/binding.ts | 58 +++++++++++++++++--------- packages/math/point.ts | 14 +++++-- packages/math/vector.ts | 15 ++++++- 3 files changed, 63 insertions(+), 24 deletions(-) diff --git a/packages/excalidraw/element/binding.ts b/packages/excalidraw/element/binding.ts index 8d3057c8e3..4a394a9cc6 100644 --- a/packages/excalidraw/element/binding.ts +++ b/packages/excalidraw/element/binding.ts @@ -73,6 +73,11 @@ import { pointDistanceSq, clamp, radians, + vectorScale, + pointFromVector, + vectorRotate, + vectorNormalize, + pointDistance, } from "../../math"; import { segmentIntersectRectangleElement } from "../../utils/geometry/shape"; import { distanceToBindableElement } from "./distance"; @@ -1425,7 +1430,7 @@ const determineFocusPoint = ( GAPoint.from(adjecentPoint), ); const reverseRelateToCenter = GA.reverse(relateToCenter); - let point; + let p: GA.Point; switch (element.type) { case "rectangle": case "image": @@ -1435,14 +1440,18 @@ const determineFocusPoint = ( case "embeddable": case "frame": case "magicframe": - point = findFocusPointForRectangulars(element, focus, adjecentPointRel); + p = findFocusPointForRectanguloidElement( + element, + focus, + adjecentPointRel, + ); break; case "ellipse": - point = findFocusPointForEllipse(element, focus, adjecentPointRel); + p = findFocusPointForEllipse(element, focus, adjecentPointRel); break; } return pointFromPair( - GAPoint.toTuple(GATransform.apply(reverseRelateToCenter, point)), + GAPoint.toTuple(GATransform.apply(reverseRelateToCenter, p)), ); }; @@ -1692,7 +1701,7 @@ const findFocusPointForEllipse = ( return GA.point(x, (-m * x - 1) / n); }; -const findFocusPointForRectangulars = ( +const findFocusPointForRectanguloidElement = ( element: | ExcalidrawRectangleElement | ExcalidrawImageElement @@ -1705,23 +1714,34 @@ const findFocusPointForRectangulars = ( relativeDistance: number, // The point for which we're trying to find the focus point, relative // to the element center. - point: GA.Point, + gaPoint: GA.Point, ): GA.Point => { - const relativeDistanceAbs = Math.abs(relativeDistance); - const orientation = Math.sign(relativeDistance); - const corners = getCorners(element, relativeDistanceAbs); + const relP = pointFromPair(GAPoint.toTuple(gaPoint)); + const center = point( + element.x + element.width / 2, + element.y + element.height / 2, + ); + const p = point(center[0] + relP[0], center[1] + relP[1]); + const ret = pointFromVector( + vectorScale( + vectorRotate( + vectorNormalize(vectorFromPoint(p, center)), + radians(Math.PI / 2), + ), + Math.sign(relativeDistance) * + Math.min( + pointDistance(point(element.x, element.y), center) * + Math.abs(relativeDistance), + element.width / 2, + element.height / 2, + ), + ), + center, + ); - let maxDistance = 0; - let tangentPoint: null | GA.Point = null; - corners.forEach((corner) => { - const distance = orientation * GALine.through(point, corner)[1]; - if (distance > maxDistance) { - maxDistance = distance; - tangentPoint = corner; - } - }); - return tangentPoint!; + return GA.point(ret[0] - center[0], ret[1] - center[1]); }; + export const bindingProperties: Set = new Set([ "boundElements", "frameId", diff --git a/packages/math/point.ts b/packages/math/point.ts index 471eab8765..bdfdfbeaac 100644 --- a/packages/math/point.ts +++ b/packages/math/point.ts @@ -46,8 +46,11 @@ export function pointFromPair( * @param v The vector to convert * @returns The point the vector points at with origin 0,0 */ -export function pointFromVector

(v: Vector): P { - return v as unknown as P; +export function pointFromVector

( + v: Vector, + offset: P = point(0, 0), +): P { + return point

(offset[0] + v[0], offset[1] + v[1]); } /** @@ -96,9 +99,12 @@ export function pointRotateRads( [cx, cy]: Point, angle: Radians, ): Point { + const cos = Math.cos(angle); + const sin = Math.sin(angle); + return point( - (x - cx) * Math.cos(angle) - (y - cy) * Math.sin(angle) + cx, - (x - cx) * Math.sin(angle) + (y - cy) * Math.cos(angle) + cy, + (x - cx) * cos - (y - cy) * sin + cx, + (x - cx) * sin + (y - cy) * cos + cy, ); } diff --git a/packages/math/vector.ts b/packages/math/vector.ts index d987ea1b00..17620adaf8 100644 --- a/packages/math/vector.ts +++ b/packages/math/vector.ts @@ -1,4 +1,4 @@ -import type { GenericPoint, Vector } from "./types"; +import type { GenericPoint, Radians, Vector } from "./types"; /** * Create a vector from the x and y coordiante elements. @@ -139,3 +139,16 @@ export const vectorNormalize = (v: Vector): Vector => { return vector(v[0] / m, v[1] / m); }; + +/** + * Rotate a vector by the given radians + * @param v Target vector + * @param a Angle to rotate in radians + * @returns The rotated vector + */ +export const vectorRotate = (v: Vector, a: Radians): Vector => { + const cos = Math.cos(a); + const sin = Math.sin(a); + + return vector(v[0] * cos - v[1] * sin, v[0] * sin + v[1] * cos); +};