diff --git a/packages/excalidraw/element/distance.ts b/packages/excalidraw/element/distance.ts index 8a0fa8dc22..85ebaa4828 100644 --- a/packages/excalidraw/element/distance.ts +++ b/packages/excalidraw/element/distance.ts @@ -12,7 +12,7 @@ import { segment, segmentDistanceToPoint, } from "../../math"; -import { getCornerRadius } from "../shapes"; +import { getCornerRadius, getDiamondPoints } from "../shapes"; import type { ExcalidrawBindableElement, ExcalidrawDiamondElement, @@ -128,18 +128,19 @@ export const distanceToRectangleElement = ( */ const createDiamondSide = ( s: Segment, - r: number, + startRadius: number, + endRadius: number, ): Segment => { - if (r === 0) { - return s; - } + const a = ellipseSegmentInterceptPoints( + ellipse(s[0], startRadius, startRadius), + s, + )[0]; + const b = ellipseSegmentInterceptPoints( + ellipse(s[1], endRadius, endRadius), + s, + )[0]; - const t = (4 * r) / Math.sqrt(2); - - return segment( - ellipseSegmentInterceptPoints(ellipse(s[0], t, t), s)[0], - ellipseSegmentInterceptPoints(ellipse(s[1], t, t), s)[0], - ); + return segment(a, b); }; /** @@ -154,7 +155,10 @@ const createDiamondSide = ( * @returns */ const createDiamondArc = (start: GlobalPoint, end: GlobalPoint, r: number) => { - const c = point((start[0] + end[0]) / 2, start[1]); + const c = point( + (start[0] + end[0]) / 2, + (start[1] + end[1]) / 2, + ); return arc( c, @@ -176,41 +180,58 @@ export const distanceToDiamondElement = ( element: ExcalidrawDiamondElement, p: GlobalPoint, ): number => { - const center = point( - element.x + element.width / 2, - element.y + element.height / 2, - ); - const roundness = getCornerRadius( - Math.min(element.width, element.height), - element, - ); + const [topX, topY, rightX, rightY, bottomX, bottomY, leftX, leftY] = + getDiamondPoints(element); + const center = point((topX + bottomX) / 2, (topY + bottomY) / 2); + const verticalRadius = getCornerRadius(Math.abs(topX - leftX), element); + const horizontalRadius = getCornerRadius(Math.abs(rightY - topY), element); + // Rotate the point to the inverse direction to simulate the rotated diamond // 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 + element.width / 2, element.y), - point(element.x + element.width, element.y + element.height / 2), - point(element.x + element.width / 2, element.y + element.height), - point(element.x, element.y + element.height / 2), + 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), ]; - const topRight = createDiamondSide(segment(top, right), roundness); - const bottomRight = createDiamondSide(segment(right, bottom), roundness); - const bottomLeft = createDiamondSide(segment(bottom, left), roundness); - const topLeft = createDiamondSide(segment(left, top), roundness); + + const topRight = createDiamondSide( + segment(top, right), + verticalRadius, + horizontalRadius, + ); + const bottomRight = createDiamondSide( + segment(bottom, right), + verticalRadius, + horizontalRadius, + ); + const bottomLeft = createDiamondSide( + segment(bottom, left), + verticalRadius, + horizontalRadius, + ); + const topLeft = createDiamondSide( + segment(top, left), + verticalRadius, + horizontalRadius, + ); + + const arcs = element.roundness + ? [ + createDiamondArc(topLeft[0], topRight[0], verticalRadius), // TOP + createDiamondArc(topRight[1], bottomRight[1], horizontalRadius), // RIGHT + createDiamondArc(bottomRight[0], bottomLeft[0], verticalRadius), // BOTTOM + createDiamondArc(bottomLeft[1], topLeft[1], horizontalRadius), // LEFT + ] + : []; return Math.min( ...[ ...[topRight, bottomRight, bottomLeft, topLeft].map((s) => segmentDistanceToPoint(rotatedPoint, s), ), - ...(roundness > 0 - ? [ - createDiamondArc(topLeft[1], topRight[0], roundness), - createDiamondArc(topRight[1], bottomRight[0], roundness), - createDiamondArc(bottomRight[1], bottomLeft[0], roundness), - createDiamondArc(bottomLeft[1], topLeft[0], roundness), - ].map((a) => arcDistanceFromPoint(a, rotatedPoint)) - : []), + ...arcs.map((a) => arcDistanceFromPoint(a, rotatedPoint)), ], ); }; diff --git a/packages/excalidraw/element/index.ts b/packages/excalidraw/element/index.ts index bf9bb24952..ec43305b16 100644 --- a/packages/excalidraw/element/index.ts +++ b/packages/excalidraw/element/index.ts @@ -21,7 +21,6 @@ export { getCommonBounds, getClosestElementBounds, } from "./bounds"; -export { getDiamondPoints } from "../scene/Shape"; export { OMIT_SIDES_FOR_MULTIPLE_ELEMENTS, diff --git a/packages/excalidraw/scene/Shape.ts b/packages/excalidraw/scene/Shape.ts index 1096366c41..ada172ebdf 100644 --- a/packages/excalidraw/scene/Shape.ts +++ b/packages/excalidraw/scene/Shape.ts @@ -30,7 +30,7 @@ import { type GlobalPoint, type LocalPoint, } from "../../math"; -import { getCornerRadius } from "../shapes"; +import { getCornerRadius, getDiamondPoints } from "../shapes"; const getDashArrayDashed = (strokeWidth: number) => [8, 8 + strokeWidth]; @@ -278,21 +278,6 @@ const getArrowheadShapes = ( } }; -export const getDiamondPoints = (element: ExcalidrawElement) => { - // Here we add +1 to avoid these numbers to be 0 - // otherwise rough.js will throw an error complaining about it - const topX = Math.floor(element.width / 2) + 1; - const topY = 0; - const rightX = element.width; - const rightY = Math.floor(element.height / 2) + 1; - const bottomX = topX; - const bottomY = element.height; - const leftX = 0; - const leftY = rightY; - - return [topX, topY, rightX, rightY, bottomX, bottomY, leftX, leftY]; -}; - /** * Generates the roughjs shape for given element. * diff --git a/packages/excalidraw/shapes.tsx b/packages/excalidraw/shapes.tsx index a4a1579ce5..1330dc3df9 100644 --- a/packages/excalidraw/shapes.tsx +++ b/packages/excalidraw/shapes.tsx @@ -42,6 +42,7 @@ import { getBoundTextElement } from "./element/textElement"; import type { Bounds, ElementsMap, + ExcalidrawDiamondElement, ExcalidrawElement, ExcalidrawLinearElement, NonDeleted, @@ -472,3 +473,18 @@ export const getCornerRadius = (x: number, element: ExcalidrawElement) => { return 0; }; + +export const getDiamondPoints = (element: ExcalidrawDiamondElement) => { + // Here we add +1 to avoid these numbers to be 0 + // otherwise rough.js will throw an error complaining about it + const topX = Math.floor(element.width / 2) + 1; + const topY = 0; + const rightX = element.width; + const rightY = Math.floor(element.height / 2) + 1; + const bottomX = topX; + const bottomY = element.height; + const leftX = 0; + const leftY = rightY; + + return [topX, topY, rightX, rightY, bottomX, bottomY, leftX, leftY]; +};