do not split points on de-polygonizing & highlight overlapping points

This commit is contained in:
dwelle 2025-05-05 11:52:34 +02:00
parent eb619f8fde
commit a7b4b08e86
4 changed files with 40 additions and 56 deletions

View File

@ -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 {

View File

@ -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();
}

View File

@ -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 extends GlobalPoint | LocalPoint>(
point: Point,
radius: number,
isSelected: boolean,
isPhantomPoint = false,
isPhantomPoint: boolean,
isOverlappingPoint: boolean,
) => {
context.strokeStyle = "#5e5ad8";
context.setLineDash([]);
@ -232,8 +235,9 @@ const renderSingleLinearPoint = <Point extends GlobalPoint | LocalPoint>(
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();

View File

@ -91,9 +91,10 @@ export function isPoint(p: unknown): p is LocalPoint | GlobalPoint {
export function pointsEqual<Point extends GlobalPoint | LocalPoint>(
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;
}
/**