From a7b4b08e864dddfd4d3404d6a0c7f65eb6ac49cf Mon Sep 17 00:00:00 2001 From: dwelle <5153846+dwelle@users.noreply.github.com> Date: Mon, 5 May 2025 11:52:34 +0200 Subject: [PATCH] do not split points on de-polygonizing & highlight overlapping points --- packages/element/src/shapes.ts | 47 ------------------- packages/excalidraw/renderer/helpers.ts | 7 ++- .../excalidraw/renderer/interactiveScene.ts | 39 ++++++++++++--- packages/math/src/point.ts | 3 +- 4 files changed, 40 insertions(+), 56 deletions(-) diff --git a/packages/element/src/shapes.ts b/packages/element/src/shapes.ts index 25a481e77..8d6b6332d 100644 --- a/packages/element/src/shapes.ts +++ b/packages/element/src/shapes.ts @@ -422,53 +422,6 @@ export const toggleLinePolygonState = ( firstPoint[1], ); } - } else if (element.loopLock) { - // When toggling from loopLock=true to loopLock=false - // We need to dislocate the end point by 15 points - - // When loopLock is true, the last point is the same as the first point - // We'll use the direction from second-to-last point to first point - const firstPoint = updatedPoints[0]; - - if (updatedPoints.length >= 3) { - const secondLastPoint = updatedPoints[updatedPoints.length - 2]; - - // Get direction from second-last to first - const dx = firstPoint[0] - secondLastPoint[0]; - const dy = firstPoint[1] - secondLastPoint[1]; - - // Calculate perpendicular direction (rotate 90 degrees) - // This creates a visible gap perpendicular to the line direction - const perpDx = dy; - const perpDy = -dx; - - // Normalize the perpendicular direction vector - const perpLength = Math.sqrt(perpDx * perpDx + perpDy * perpDy); - let normalizedPerpDx = 0; - let normalizedPerpDy = 0; - - if (perpLength > 0) { - normalizedPerpDx = perpDx / perpLength; - normalizedPerpDy = perpDy / perpLength; - } else { - // Default perpendicular if points are the same - normalizedPerpDx = -0.7071; - normalizedPerpDy = 0.7071; - } - - // Move the end point perpendicular to the line direction - updatedPoints[updatedPoints.length - 1] = pointFrom( - firstPoint[0] + normalizedPerpDx * 15, - firstPoint[1] + normalizedPerpDy * 15, - ); - } else { - // For simple lines with fewer than 3 points - // Just move away from the first point at a 45-degree angle - updatedPoints[updatedPoints.length - 1] = pointFrom( - firstPoint[0] + 10.6, - firstPoint[1] - 10.6, // Different direction to avoid crossing - ); - } } return { diff --git a/packages/excalidraw/renderer/helpers.ts b/packages/excalidraw/renderer/helpers.ts index 765ef4294..faacc8710 100644 --- a/packages/excalidraw/renderer/helpers.ts +++ b/packages/excalidraw/renderer/helpers.ts @@ -8,11 +8,14 @@ export const fillCircle = ( cx: number, cy: number, radius: number, - stroke = true, + stroke: boolean, + fill = true, ) => { context.beginPath(); context.arc(cx, cy, radius, 0, Math.PI * 2); - context.fill(); + if (fill) { + context.fill(); + } if (stroke) { context.stroke(); } diff --git a/packages/excalidraw/renderer/interactiveScene.ts b/packages/excalidraw/renderer/interactiveScene.ts index 69c6a8196..a2d0fe675 100644 --- a/packages/excalidraw/renderer/interactiveScene.ts +++ b/packages/excalidraw/renderer/interactiveScene.ts @@ -1,6 +1,7 @@ import oc from "open-color"; import { pointFrom, + pointsEqual, type GlobalPoint, type LocalPoint, type Radians, @@ -32,6 +33,7 @@ import { isFrameLikeElement, isImageElement, isLinearElement, + isLineElement, isTextElement, } from "@excalidraw/element/typeChecks"; @@ -217,7 +219,8 @@ const renderSingleLinearPoint = ( point: Point, radius: number, isSelected: boolean, - isPhantomPoint = false, + isPhantomPoint: boolean, + isOverlappingPoint: boolean, ) => { context.strokeStyle = "#5e5ad8"; context.setLineDash([]); @@ -232,8 +235,9 @@ const renderSingleLinearPoint = ( context, point[0], point[1], - radius / appState.zoom.value, + (isOverlappingPoint ? radius * 2 : radius) / appState.zoom.value, !isPhantomPoint, + !isOverlappingPoint, ); }; @@ -345,7 +349,7 @@ const renderBindingHighlightForSuggestedPointBinding = ( index, elementsMap, ); - fillCircle(context, x, y, threshold); + fillCircle(context, x, y, threshold, true); }); }; @@ -532,15 +536,36 @@ const renderLinearPointHandles = ( const radius = appState.editingLinearElement ? POINT_HANDLE_SIZE : POINT_HANDLE_SIZE / 2; + + const _isElbowArrow = isElbowArrow(element); + const _isLineElement = isLineElement(element); + points.forEach((point, idx) => { - if (isElbowArrow(element) && idx !== 0 && idx !== points.length - 1) { + if (_isElbowArrow && idx !== 0 && idx !== points.length - 1) { return; } const isSelected = !!appState.editingLinearElement?.selectedPointsIndices?.includes(idx); - renderSingleLinearPoint(context, appState, point, radius, isSelected); + const isOverlappingPoint = + idx > 0 && + (idx !== points.length - 1 || !_isLineElement || !element.loopLock) && + pointsEqual( + point, + idx === points.length - 1 ? points[0] : points[idx - 1], + 2 / appState.zoom.value, + ); + + renderSingleLinearPoint( + context, + appState, + point, + radius, + isSelected, + false, + isOverlappingPoint, + ); }); // Rendering segment mid points @@ -567,6 +592,7 @@ const renderLinearPointHandles = ( POINT_HANDLE_SIZE / 2, false, !fixedSegments.includes(idx + 1), + false, ); } }); @@ -590,6 +616,7 @@ const renderLinearPointHandles = ( POINT_HANDLE_SIZE / 2, false, true, + false, ); } }); @@ -616,7 +643,7 @@ const renderTransformHandles = ( context.strokeStyle = renderConfig.selectionColor; } if (key === "rotation") { - fillCircle(context, x + width / 2, y + height / 2, width / 2); + fillCircle(context, x + width / 2, y + height / 2, width / 2, true); // prefer round corners if roundRect API is available } else if (context.roundRect) { context.beginPath(); diff --git a/packages/math/src/point.ts b/packages/math/src/point.ts index b6054a10a..863febfd4 100644 --- a/packages/math/src/point.ts +++ b/packages/math/src/point.ts @@ -91,9 +91,10 @@ export function isPoint(p: unknown): p is LocalPoint | GlobalPoint { export function pointsEqual( a: Point, b: Point, + tolerance: number = PRECISION, ): boolean { const abs = Math.abs; - return abs(a[0] - b[0]) < PRECISION && abs(a[1] - b[1]) < PRECISION; + return abs(a[0] - b[0]) < tolerance && abs(a[1] - b[1]) < tolerance; } /**