From 9e6e58a635954e9f81857e87bb26131f16b5b251 Mon Sep 17 00:00:00 2001 From: Mark Tolmacs Date: Mon, 24 Feb 2025 13:50:53 +0100 Subject: [PATCH] Fix up distance from diamond as well --- packages/excalidraw/element/utils.ts | 246 ++++++++++-------- .../tests/__snapshots__/history.test.tsx.snap | 112 ++++---- .../tests/__snapshots__/move.test.tsx.snap | 8 +- .../tests/linearElementEditor.test.tsx | 2 +- packages/excalidraw/tests/move.test.tsx | 2 +- packages/excalidraw/tests/resize.test.tsx | 11 +- packages/excalidraw/tests/rotate.test.tsx | 2 +- 7 files changed, 213 insertions(+), 170 deletions(-) diff --git a/packages/excalidraw/element/utils.ts b/packages/excalidraw/element/utils.ts index 9c88afb66b..50307042a3 100644 --- a/packages/excalidraw/element/utils.ts +++ b/packages/excalidraw/element/utils.ts @@ -96,7 +96,7 @@ export function deconstructRectanguloidElement( pointFrom(r[0][0], r[0][1] + roundness), ); - const vecs = [ + const offsets = [ vectorScale( vectorNormalize( vectorFromPoint(pointFrom(r[0][0] - offset, r[0][1] - offset), center), @@ -125,96 +125,86 @@ export function deconstructRectanguloidElement( const corners = [ curve( - pointFromVector(vecs[0], left[1]), + pointFromVector(offsets[0], left[1]), pointFromVector( - vecs[0], + offsets[0], pointFrom( left[1][0] + (2 / 3) * (r[0][0] - left[1][0]), left[1][1] + (2 / 3) * (r[0][1] - left[1][1]), ), ), pointFromVector( - vecs[0], + offsets[0], pointFrom( top[0][0] + (2 / 3) * (r[0][0] - top[0][0]), top[0][1] + (2 / 3) * (r[0][1] - top[0][1]), ), ), - pointFromVector(vecs[0], top[0]), + pointFromVector(offsets[0], top[0]), ), // TOP LEFT curve( - pointFromVector(vecs[1], top[1]), + pointFromVector(offsets[1], top[1]), pointFromVector( - vecs[1], + offsets[1], pointFrom( top[1][0] + (2 / 3) * (r[1][0] - top[1][0]), top[1][1] + (2 / 3) * (r[0][1] - top[1][1]), ), ), pointFromVector( - vecs[1], + offsets[1], pointFrom( right[0][0] + (2 / 3) * (r[1][0] - right[0][0]), right[0][1] + (2 / 3) * (r[0][1] - right[0][1]), ), ), - pointFromVector(vecs[1], right[0]), + pointFromVector(offsets[1], right[0]), ), // TOP RIGHT curve( - pointFromVector(vecs[2], right[1]), + pointFromVector(offsets[2], right[1]), pointFromVector( - vecs[2], + offsets[2], pointFrom( right[1][0] + (2 / 3) * (r[1][0] - right[1][0]), right[1][1] + (2 / 3) * (r[1][1] - right[1][1]), ), ), pointFromVector( - vecs[2], + offsets[2], pointFrom( bottom[1][0] + (2 / 3) * (r[1][0] - bottom[1][0]), bottom[1][1] + (2 / 3) * (r[1][1] - bottom[1][1]), ), ), - pointFromVector(vecs[2], bottom[1]), + pointFromVector(offsets[2], bottom[1]), ), // BOTTOM RIGHT curve( - pointFromVector(vecs[3], bottom[0]), + pointFromVector(offsets[3], bottom[0]), pointFromVector( - vecs[3], + offsets[3], pointFrom( bottom[0][0] + (2 / 3) * (r[0][0] - bottom[0][0]), bottom[0][1] + (2 / 3) * (r[1][1] - bottom[0][1]), ), ), pointFromVector( - vecs[3], + offsets[3], pointFrom( left[0][0] + (2 / 3) * (r[0][0] - left[0][0]), left[0][1] + (2 / 3) * (r[1][1] - left[0][1]), ), ), - pointFromVector(vecs[3], left[0]), + pointFromVector(offsets[3], left[0]), ), // BOTTOM LEFT ]; const sides = [ - //top, lineSegment(corners[0][3], corners[1][0]), - //right, lineSegment(corners[1][3], corners[2][0]), - //bottom, lineSegment(corners[2][3], corners[3][0]), - //left, lineSegment(corners[3][3], corners[0][0]), ]; - debugClear(); - sides.forEach((s) => debugDrawLine(s, { color: "red", permanent: true })); - corners.forEach((c) => - debugDrawCubicBezier(c, { color: "green", permanent: true }), - ); - return [sides, corners]; } @@ -235,84 +225,136 @@ export function deconstructDiamondElement( const verticalRadius = getCornerRadius(Math.abs(topX - leftX), element); const horizontalRadius = getCornerRadius(Math.abs(rightY - topY), element); + if (element.roundness?.type == null) { + const [top, right, bottom, left]: GlobalPoint[] = [ + pointFrom(element.x + topX, element.y + topY - offset), + pointFrom(element.x + rightX + offset, element.y + rightY), + pointFrom(element.x + bottomX, element.y + bottomY + offset), + pointFrom(element.x + leftX - offset, element.y + leftY), + ]; + + // Create the line segment parts of the diamond + // NOTE: Horizontal and vertical seems to be flipped here + const topRight = lineSegment( + pointFrom(top[0] + verticalRadius, top[1] + horizontalRadius), + pointFrom(right[0] - verticalRadius, right[1] - horizontalRadius), + ); + const bottomRight = lineSegment( + pointFrom(right[0] - verticalRadius, right[1] + horizontalRadius), + pointFrom(bottom[0] + verticalRadius, bottom[1] - horizontalRadius), + ); + const bottomLeft = lineSegment( + pointFrom(bottom[0] - verticalRadius, bottom[1] - horizontalRadius), + pointFrom(left[0] + verticalRadius, left[1] + horizontalRadius), + ); + const topLeft = lineSegment( + pointFrom(left[0] + verticalRadius, left[1] - horizontalRadius), + pointFrom(top[0] - verticalRadius, top[1] + horizontalRadius), + ); + + return [[topRight, bottomRight, bottomLeft, topLeft], []]; + } + + const center = pointFrom( + element.x + element.width / 2, + element.y + element.height / 2, + ); + const [top, right, bottom, left]: GlobalPoint[] = [ - pointFrom(element.x + topX, element.y + topY - offset), - pointFrom(element.x + rightX + offset, element.y + rightY), - pointFrom(element.x + bottomX, element.y + bottomY + offset), - pointFrom(element.x + leftX - offset, 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), ]; - // Create the line segment parts of the diamond - // NOTE: Horizontal and vertical seems to be flipped here - const topRight = lineSegment( - pointFrom(top[0] + verticalRadius, top[1] + horizontalRadius), - pointFrom(right[0] - verticalRadius, right[1] - horizontalRadius), - ); - const bottomRight = lineSegment( - pointFrom(right[0] - verticalRadius, right[1] + horizontalRadius), - pointFrom(bottom[0] + verticalRadius, bottom[1] - horizontalRadius), - ); - const bottomLeft = lineSegment( - pointFrom(bottom[0] - verticalRadius, bottom[1] - horizontalRadius), - pointFrom(left[0] + verticalRadius, left[1] + horizontalRadius), - ); - const topLeft = lineSegment( - pointFrom(left[0] + verticalRadius, left[1] - horizontalRadius), - pointFrom(top[0] - verticalRadius, top[1] + horizontalRadius), - ); + const offsets = [ + vectorScale(vectorNormalize(vectorFromPoint(right, center)), offset), // RIGHT + vectorScale(vectorNormalize(vectorFromPoint(bottom, center)), offset), // BOTTOM + vectorScale(vectorNormalize(vectorFromPoint(left, center)), offset), // LEFT + vectorScale(vectorNormalize(vectorFromPoint(top, center)), offset), // TOP + ]; + + const corners = [ + curve( + pointFromVector( + offsets[0], + pointFrom( + right[0] - verticalRadius, + right[1] - horizontalRadius, + ), + ), + pointFromVector(offsets[0], right), + pointFromVector(offsets[0], right), + pointFromVector( + offsets[0], + pointFrom( + right[0] - verticalRadius, + right[1] + horizontalRadius, + ), + ), + ), // RIGHT + curve( + pointFromVector( + offsets[1], + pointFrom( + bottom[0] + verticalRadius, + bottom[1] - horizontalRadius, + ), + ), + pointFromVector(offsets[1], bottom), + pointFromVector(offsets[1], bottom), + pointFromVector( + offsets[1], + pointFrom( + bottom[0] - verticalRadius, + bottom[1] - horizontalRadius, + ), + ), + ), // BOTTOM + curve( + pointFromVector( + offsets[2], + pointFrom( + left[0] + verticalRadius, + left[1] + horizontalRadius, + ), + ), + pointFromVector(offsets[2], left), + pointFromVector(offsets[2], left), + pointFromVector( + offsets[2], + pointFrom( + left[0] + verticalRadius, + left[1] - horizontalRadius, + ), + ), + ), // LEFT + curve( + pointFromVector( + offsets[3], + pointFrom( + top[0] - verticalRadius, + top[1] + horizontalRadius, + ), + ), + pointFromVector(offsets[3], top), + pointFromVector(offsets[3], top), + pointFromVector( + offsets[3], + pointFrom( + top[0] + verticalRadius, + top[1] + horizontalRadius, + ), + ), + ), // TOP + ]; - const curves = element.roundness - ? [ - curve( - pointFrom( - right[0] - verticalRadius, - right[1] - horizontalRadius, - ), - right, - right, - pointFrom( - right[0] - verticalRadius, - right[1] + horizontalRadius, - ), - ), // RIGHT - curve( - pointFrom( - bottom[0] + verticalRadius, - bottom[1] - horizontalRadius, - ), - bottom, - bottom, - pointFrom( - bottom[0] - verticalRadius, - bottom[1] - horizontalRadius, - ), - ), // BOTTOM - curve( - pointFrom( - left[0] + verticalRadius, - left[1] + horizontalRadius, - ), - left, - left, - pointFrom( - left[0] + verticalRadius, - left[1] - horizontalRadius, - ), - ), // LEFT - curve( - pointFrom( - top[0] - verticalRadius, - top[1] + horizontalRadius, - ), - top, - top, - pointFrom( - top[0] + verticalRadius, - top[1] + horizontalRadius, - ), - ), // TOP - ] - : []; + const sides = [ + lineSegment(corners[0][3], corners[1][0]), + lineSegment(corners[1][3], corners[2][0]), + lineSegment(corners[2][3], corners[3][0]), + lineSegment(corners[3][3], corners[0][0]), + ]; - return [[topRight, bottomRight, bottomLeft, topLeft], curves]; + return [sides, corners]; } diff --git a/packages/excalidraw/tests/__snapshots__/history.test.tsx.snap b/packages/excalidraw/tests/__snapshots__/history.test.tsx.snap index c5d2af2488..d8cf4ec2d1 100644 --- a/packages/excalidraw/tests/__snapshots__/history.test.tsx.snap +++ b/packages/excalidraw/tests/__snapshots__/history.test.tsx.snap @@ -197,7 +197,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "fillStyle": "solid", "frameId": null, "groupIds": [], - "height": "102.03381", + "height": "102.35417", "id": "id172", "index": "a2", "isDeleted": false, @@ -211,8 +211,8 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl 0, ], [ - "101.21754", - "102.03381", + "101.77517", + "102.35417", ], ], "roughness": 1, @@ -227,8 +227,8 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "type": "arrow", "updated": 1, "version": 40, - "width": "101.21754", - "x": 1, + "width": "101.77517", + "x": "0.70711", "y": 0, } `; @@ -297,15 +297,15 @@ History { "focus": "0.00990", "gap": 1, }, - "height": "0.98017", + "height": "0.98597", "points": [ [ 0, 0, ], [ - 98, - "-0.98017", + "98.58579", + "-0.98597", ], ], "startBinding": { @@ -320,15 +320,15 @@ History { "focus": "-0.02000", "gap": 1, }, - "height": "0.00169", + "height": "0.00119", "points": [ [ 0, 0, ], [ - "98.00000", - "0.00169", + "98.58579", + "0.00119", ], ], "startBinding": { @@ -389,15 +389,15 @@ History { "focus": 0, "gap": 1, }, - "height": "102.03381", + "height": "102.35417", "points": [ [ 0, 0, ], [ - "101.21754", - "102.03381", + "101.77517", + "102.35417", ], ], "startBinding": null, @@ -409,15 +409,15 @@ History { "focus": "0.00990", "gap": 1, }, - "height": "0.98161", + "height": "0.98700", "points": [ [ 0, 0, ], [ - "98.00000", - "-0.98161", + "98.58579", + "-0.98700", ], ], "startBinding": { @@ -425,7 +425,7 @@ History { "focus": "0.02970", "gap": 1, }, - "y": "0.99245", + "y": "0.99465", }, }, "id175" => Delta { @@ -1238,7 +1238,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "fillStyle": "solid", "frameId": null, "groupIds": [], - "height": "2.57347", + "height": "2.52823", "id": "id178", "index": "Zz", "isDeleted": false, @@ -1252,8 +1252,8 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl 0, ], [ - 98, - "-2.57347", + "98.58579", + "-2.52823", ], ], "roughness": 1, @@ -1276,9 +1276,9 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "type": "arrow", "updated": 1, "version": 11, - "width": 98, - "x": 1, - "y": "3.91272", + "width": "98.58579", + "x": "0.70711", + "y": "3.82861", } `; @@ -1609,7 +1609,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "fillStyle": "solid", "frameId": null, "groupIds": [], - "height": "2.57347", + "height": "2.52823", "id": "id181", "index": "a0", "isDeleted": false, @@ -1623,8 +1623,8 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl 0, ], [ - 98, - "-2.57347", + "98.58579", + "-2.52823", ], ], "roughness": 1, @@ -1647,9 +1647,9 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "type": "arrow", "updated": 1, "version": 11, - "width": 98, - "x": 1, - "y": "3.91272", + "width": "98.58579", + "x": "0.70711", + "y": "3.82861", } `; @@ -1767,7 +1767,7 @@ History { "fillStyle": "solid", "frameId": null, "groupIds": [], - "height": "22.11765", + "height": "22.07000", "index": "a0", "isDeleted": false, "lastCommittedPoint": null, @@ -1780,8 +1780,8 @@ History { 0, ], [ - "98.67659", - "-22.11765", + "99.27949", + "-22.07000", ], ], "roughness": 1, @@ -1802,9 +1802,9 @@ History { "strokeStyle": "solid", "strokeWidth": 2, "type": "arrow", - "width": "98.67659", - "x": "0.32341", - "y": "33.54894", + "width": "99.27949", + "x": "0.01341", + "y": "33.34227", }, "inserted": { "isDeleted": true, @@ -2320,7 +2320,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "fillStyle": "solid", "frameId": null, "groupIds": [], - "height": "410.36954", + "height": "410.63965", "id": "id186", "index": "a2", "isDeleted": false, @@ -2334,8 +2334,8 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl 0, ], [ - "500.65079", - "-410.36954", + "501.24760", + "-410.63965", ], ], "roughness": 1, @@ -2354,8 +2354,8 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl "type": "arrow", "updated": 1, "version": 10, - "width": "500.65079", - "x": 1, + "width": "501.24760", + "x": "0.70711", "y": 0, } `; @@ -15130,7 +15130,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding 0, ], [ - 98, + "98.58579", 0, ], ], @@ -15150,8 +15150,8 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "type": "arrow", "updated": 1, "version": 10, - "width": 98, - "x": 1, + "width": "98.58579", + "x": "0.70711", "y": 0, } `; @@ -15825,7 +15825,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding 0, ], [ - 98, + "98.58579", 0, ], ], @@ -15845,8 +15845,8 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "type": "arrow", "updated": 1, "version": 10, - "width": 98, - "x": 1, + "width": "98.58579", + "x": "0.70711", "y": 0, } `; @@ -16444,7 +16444,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding 0, ], [ - 98, + "98.58579", 0, ], ], @@ -16464,8 +16464,8 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "type": "arrow", "updated": 1, "version": 10, - "width": 98, - "x": 1, + "width": "98.58579", + "x": "0.70711", "y": 0, } `; @@ -17061,7 +17061,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding 0, ], [ - 98, + "98.58579", 0, ], ], @@ -17081,8 +17081,8 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "type": "arrow", "updated": 1, "version": 10, - "width": 98, - "x": 1, + "width": "98.58579", + "x": "0.70711", "y": 0, } `; @@ -17774,7 +17774,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding 0, ], [ - 98, + "98.58579", 0, ], ], @@ -17794,8 +17794,8 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding "type": "arrow", "updated": 1, "version": 11, - "width": 98, - "x": 1, + "width": "98.58579", + "x": "0.70711", "y": 0, } `; diff --git a/packages/excalidraw/tests/__snapshots__/move.test.tsx.snap b/packages/excalidraw/tests/__snapshots__/move.test.tsx.snap index 87d03c3e03..eb5f14498d 100644 --- a/packages/excalidraw/tests/__snapshots__/move.test.tsx.snap +++ b/packages/excalidraw/tests/__snapshots__/move.test.tsx.snap @@ -196,7 +196,7 @@ exports[`move element > rectangles with binding arrow 7`] = ` "fillStyle": "solid", "frameId": null, "groupIds": [], - "height": "81.47368", + "height": "84.41974", "id": "id2", "index": "a2", "isDeleted": false, @@ -210,8 +210,8 @@ exports[`move element > rectangles with binding arrow 7`] = ` 0, ], [ - "81.00000", - "81.47368", + "83.92893", + "84.41974", ], ], "roughness": 1, @@ -232,7 +232,7 @@ exports[`move element > rectangles with binding arrow 7`] = ` "updated": 1, "version": 11, "versionNonce": 1051383431, - "width": "81.00000", + "width": "83.92893", "x": 110, "y": 50, } diff --git a/packages/excalidraw/tests/linearElementEditor.test.tsx b/packages/excalidraw/tests/linearElementEditor.test.tsx index 3f5acbf63f..dee8f31124 100644 --- a/packages/excalidraw/tests/linearElementEditor.test.tsx +++ b/packages/excalidraw/tests/linearElementEditor.test.tsx @@ -1234,7 +1234,7 @@ describe("Test Linear Elements", () => { mouse.downAt(rect.x, rect.y); mouse.moveTo(200, 0); mouse.upAt(200, 0); - expect(arrow.width).toBe(200); + expect(arrow.width).toBeCloseTo(204, 0); expect(rect.x).toBe(200); expect(rect.y).toBe(0); expect(handleBindTextResizeSpy).toHaveBeenCalledWith( diff --git a/packages/excalidraw/tests/move.test.tsx b/packages/excalidraw/tests/move.test.tsx index 9cc3e45075..1cd6e702b4 100644 --- a/packages/excalidraw/tests/move.test.tsx +++ b/packages/excalidraw/tests/move.test.tsx @@ -125,7 +125,7 @@ describe("move element", () => { expect([rectB.x, rectB.y]).toEqual([201, 2]); expect([Math.round(arrow.x), Math.round(arrow.y)]).toEqual([110, 50]); expect([Math.round(arrow.width), Math.round(arrow.height)]).toEqual([ - 81, 81, + 84, 84, ]); h.elements.forEach((element) => expect(element).toMatchSnapshot()); diff --git a/packages/excalidraw/tests/resize.test.tsx b/packages/excalidraw/tests/resize.test.tsx index ee916decbb..7fd7572729 100644 --- a/packages/excalidraw/tests/resize.test.tsx +++ b/packages/excalidraw/tests/resize.test.tsx @@ -182,12 +182,12 @@ describe("generic element", () => { UI.resize(rectangle, "e", [40, 0]); - expect(arrow.width + arrow.endBinding!.gap).toBeCloseTo(30); + expect(arrow.width + arrow.endBinding!.gap).toBeCloseTo(30, 0); UI.resize(rectangle, "w", [50, 0]); expect(arrow.endBinding?.elementId).toEqual(rectangle.id); - expect(arrow.width + arrow.endBinding!.gap).toBeCloseTo(80); + expect(arrow.width + arrow.endBinding!.gap).toBeCloseTo(80, 0); }); it("resizes with a label", async () => { @@ -812,15 +812,16 @@ describe("image element", () => { UI.resize(image, "ne", [40, 0]); - expect(arrow.width + arrow.endBinding!.gap).toBeCloseTo(30); + expect(arrow.width + arrow.endBinding!.gap).toBeCloseTo(31, 0); const imageWidth = image.width; const scale = 20 / image.height; UI.resize(image, "nw", [50, 20]); expect(arrow.endBinding?.elementId).toEqual(image.id); - expect(arrow.width + arrow.endBinding!.gap).toBeCloseTo( + expect(Math.floor(arrow.width + arrow.endBinding!.gap)).toBeCloseTo( 30 + imageWidth * scale, + 0, ); }); }); @@ -1025,7 +1026,7 @@ describe("multiple selection", () => { expect(leftBoundArrow.x).toBeCloseTo(-110); expect(leftBoundArrow.y).toBeCloseTo(50); - expect(leftBoundArrow.width).toBeCloseTo(140, 0); + expect(leftBoundArrow.width).toBeCloseTo(143, 0); expect(leftBoundArrow.height).toBeCloseTo(7, 0); expect(leftBoundArrow.angle).toEqual(0); expect(leftBoundArrow.startBinding).toBeNull(); diff --git a/packages/excalidraw/tests/rotate.test.tsx b/packages/excalidraw/tests/rotate.test.tsx index a486750086..6637029ca6 100644 --- a/packages/excalidraw/tests/rotate.test.tsx +++ b/packages/excalidraw/tests/rotate.test.tsx @@ -33,7 +33,7 @@ test("unselected bound arrow updates when rotating its target element", async () expect(arrow.endBinding?.elementId).toEqual(rectangle.id); expect(arrow.x).toBeCloseTo(-80); expect(arrow.y).toBeCloseTo(50); - expect(arrow.width).toBeCloseTo(110.7, 1); + expect(arrow.width).toBeCloseTo(116.7, 1); expect(arrow.height).toBeCloseTo(0); });