refactor: simplify code

This commit is contained in:
Ryan Di 2025-08-04 13:46:33 +10:00
parent dceaa53b0c
commit 7332e76d56
3 changed files with 120 additions and 171 deletions

View File

@ -8,7 +8,6 @@ import {
pointDistance, pointDistance,
vectorFromPoint, vectorFromPoint,
line, line,
linesIntersectAt,
curveLength, curveLength,
curvePointAtLength, curvePointAtLength,
} from "@excalidraw/math"; } from "@excalidraw/math";
@ -29,6 +28,7 @@ import {
deconstructLinearOrFreeDrawElement, deconstructLinearOrFreeDrawElement,
isPathALoop, isPathALoop,
snapLinearElementPoint, snapLinearElementPoint,
snapToDiscreteAngle,
type SnapLine, type SnapLine,
type Store, type Store,
} from "@excalidraw/element"; } from "@excalidraw/element";
@ -397,45 +397,16 @@ export class LinearElementEditor {
pointFrom(referencePointCoords[0], referencePointCoords[1]), pointFrom(referencePointCoords[0], referencePointCoords[1]),
); );
const firstSnapLine = snapLines[0]; const result = snapToDiscreteAngle(
if ( snapLines,
firstSnapLine.type === "points" && angleLine,
firstSnapLine.points.length > 1 pointFrom(gridX, gridY),
) { referencePointCoords,
const snapLine = line( );
firstSnapLine.points[0],
firstSnapLine.points[1],
);
const intersection = linesIntersectAt<GlobalPoint>(
angleLine,
snapLine,
);
if (intersection) { dxFromReference = result.dxFromReference;
dxFromReference = intersection[0] - referencePointCoords[0]; dyFromReference = result.dyFromReference;
dyFromReference = intersection[1] - referencePointCoords[1]; _snapLines = result.snapLines;
const furthestPoint = firstSnapLine.points.reduce(
(furthest, point) => {
const distance = pointDistance(intersection, point);
if (distance > furthest.distance) {
return { point, distance };
}
return furthest;
},
{
point: firstSnapLine.points[0],
distance: pointDistance(
intersection,
firstSnapLine.points[0],
),
},
);
firstSnapLine.points = [furthestPoint.point, intersection];
_snapLines = [firstSnapLine];
}
}
} else if (snapLines.length > 0) { } else if (snapLines.length > 0) {
const snappedGridX = effectiveGridX + snapOffset.x; const snappedGridX = effectiveGridX + snapOffset.x;
const snappedGridY = effectiveGridY + snapOffset.y; const snappedGridY = effectiveGridY + snapOffset.y;
@ -1237,49 +1208,16 @@ export class LinearElementEditor {
pointFrom(lastCommittedPointCoords[0], lastCommittedPointCoords[1]), pointFrom(lastCommittedPointCoords[0], lastCommittedPointCoords[1]),
); );
const firstSnapLine = _snapLines[0]; const result = snapToDiscreteAngle(
if ( _snapLines,
firstSnapLine.type === "points" && angleLine,
firstSnapLine.points.length > 1 pointFrom(gridX, gridY),
) { lastCommittedPointCoords,
const snapLine = line( );
firstSnapLine.points[0],
firstSnapLine.points[1],
);
const intersection = linesIntersectAt<GlobalPoint>(
angleLine,
snapLine,
);
if (intersection) { dxFromLastCommitted = result.dxFromReference;
dxFromLastCommitted = dyFromLastCommitted = result.dyFromReference;
intersection[0] - lastCommittedPointCoords[0]; snapLines = result.snapLines;
dyFromLastCommitted =
intersection[1] - lastCommittedPointCoords[1];
const furthestPoint = firstSnapLine.points.reduce(
(furthest, point) => {
const distance = pointDistance(intersection, point);
if (distance > furthest.distance) {
return { point, distance };
}
return furthest;
},
{
point: firstSnapLine.points[0],
distance: pointDistance(
intersection,
firstSnapLine.points[0],
),
},
);
firstSnapLine.points = [furthestPoint.point, intersection];
snapLines = [firstSnapLine];
}
} else {
snapLines = [];
}
} else if (_snapLines.length > 0) { } else if (_snapLines.length > 0) {
const snappedGridX = effectiveGridX + snapOffset.x; const snappedGridX = effectiveGridX + snapOffset.x;
const snappedGridY = effectiveGridY + snapOffset.y; const snappedGridY = effectiveGridY + snapOffset.y;

View File

@ -1,5 +1,8 @@
import { import {
isCloseTo, isCloseTo,
line,
linesIntersectAt,
pointDistance,
pointFrom, pointFrom,
pointRotateRads, pointRotateRads,
rangeInclusive, rangeInclusive,
@ -1643,3 +1646,79 @@ export const isActiveToolNonLinearSnappable = (
activeToolType === TOOL_TYPE.text activeToolType === TOOL_TYPE.text
); );
}; };
/**
* Snaps to discrete angle rotation logic.
* This function handles the common pattern of finding intersections between
* angle lines and snap lines, and updating the snap lines accordingly.
*
* @param snapLines - The original snap lines from snapping
* @param angleLine - The line representing the discrete angle constraint
* @param gridPosition - The grid position (original pointer position)
* @param referencePosition - The reference position (usually the start point)
* @returns Object containing updated snap lines and position deltas
*/
export const snapToDiscreteAngle = (
snapLines: SnapLine[],
angleLine: [GlobalPoint, GlobalPoint],
gridPosition: GlobalPoint,
referencePosition: GlobalPoint,
): {
snapLines: SnapLine[];
dxFromReference: number;
dyFromReference: number;
} => {
if (snapLines.length === 0) {
return {
snapLines: [],
dxFromReference: gridPosition[0] - referencePosition[0],
dyFromReference: gridPosition[1] - referencePosition[1],
};
}
const firstSnapLine = snapLines[0];
if (firstSnapLine.type === "points" && firstSnapLine.points.length > 1) {
const snapLine = line(firstSnapLine.points[0], firstSnapLine.points[1]);
const intersection = linesIntersectAt<GlobalPoint>(
line(angleLine[0], angleLine[1]),
snapLine,
);
if (intersection) {
const dxFromReference = intersection[0] - referencePosition[0];
const dyFromReference = intersection[1] - referencePosition[1];
const furthestPoint = firstSnapLine.points.reduce(
(furthest, point) => {
const distance = pointDistance(intersection, point);
if (distance > furthest.distance) {
return { point, distance };
}
return furthest;
},
{
point: firstSnapLine.points[0],
distance: pointDistance(intersection, firstSnapLine.points[0]),
},
);
const updatedSnapLine: PointSnapLine = {
type: "points",
points: [furthestPoint.point, intersection],
};
return {
snapLines: [updatedSnapLine],
dxFromReference,
dyFromReference,
};
}
}
// If no intersection found, return original snap lines with grid position
return {
snapLines,
dxFromReference: gridPosition[0] - referencePosition[0],
dyFromReference: gridPosition[1] - referencePosition[1],
};
};

View File

@ -17,7 +17,6 @@ import {
vectorDot, vectorDot,
vectorNormalize, vectorNormalize,
line, line,
linesIntersectAt,
} from "@excalidraw/math"; } from "@excalidraw/math";
import { import {
@ -240,6 +239,7 @@ import {
isActiveToolNonLinearSnappable, isActiveToolNonLinearSnappable,
getSnapLinesAtPointer, getSnapLinesAtPointer,
snapLinearElementPoint, snapLinearElementPoint,
snapToDiscreteAngle,
isSnappingEnabled, isSnappingEnabled,
getReferenceSnapPoints, getReferenceSnapPoints,
getVisibleGaps, getVisibleGaps,
@ -6030,54 +6030,19 @@ class App extends React.Component<AppProps, AppState> {
pointFrom(lastCommittedX + rx, lastCommittedY + ry), pointFrom(lastCommittedX + rx, lastCommittedY + ry),
); );
// Find intersection with first snap line const result = snapToDiscreteAngle(
const firstSnapLine = snapLines[0]; snapLines,
if ( angleLine,
firstSnapLine.type === "points" && pointFrom(gridX, gridY),
firstSnapLine.points.length > 1 pointFrom(lastCommittedX + rx, lastCommittedY + ry),
) { );
const snapLine = line(
firstSnapLine.points[0],
firstSnapLine.points[1],
);
const intersection = linesIntersectAt<GlobalPoint>(
angleLine,
snapLine,
);
if (intersection) { dxFromLastCommitted = result.dxFromReference;
dxFromLastCommitted = intersection[0] - rx - lastCommittedX; dyFromLastCommitted = result.dyFromReference;
dyFromLastCommitted = intersection[1] - ry - lastCommittedY;
// Find the furthest point from the intersection this.setState({
const furthestPoint = firstSnapLine.points.reduce( snapLines: result.snapLines,
(furthest, point) => { });
const distance = pointDistance(intersection, point);
if (distance > furthest.distance) {
return { point, distance };
}
return furthest;
},
{
point: firstSnapLine.points[0],
distance: pointDistance(
intersection,
firstSnapLine.points[0],
),
},
);
firstSnapLine.points = [furthestPoint.point, intersection];
this.setState({
snapLines: [firstSnapLine],
});
this.setState({
snapLines: [firstSnapLine],
});
}
}
} else { } else {
const snappedGridX = effectiveGridX + snapOffset.x; const snappedGridX = effectiveGridX + snapOffset.x;
const snappedGridY = effectiveGridY + snapOffset.y; const snappedGridY = effectiveGridY + snapOffset.y;
@ -8814,52 +8779,19 @@ class App extends React.Component<AppProps, AppState> {
pointFrom(newElement.x, newElement.y), pointFrom(newElement.x, newElement.y),
); );
const firstSnapLine = snapLines.find( const result = snapToDiscreteAngle(
(snapLine) => snapLines,
snapLine.type === "points" && snapLine.points.length > 2, angleLine,
pointFrom(gridX, gridY),
pointFrom(newElement.x, newElement.y),
); );
if (firstSnapLine && firstSnapLine.points.length > 1) {
const snapLine = line(
firstSnapLine.points[0],
firstSnapLine.points[1],
);
const intersection = linesIntersectAt<GlobalPoint>(
angleLine,
snapLine,
);
if (intersection) {
dx = intersection[0] - newElement.x;
dy = intersection[1] - newElement.y;
// Find the furthest point from the intersection dx = result.dxFromReference;
const furthestPoint = firstSnapLine.points.reduce( dy = result.dyFromReference;
(furthest, point) => {
const distance = pointDistance(intersection, point);
if (distance > furthest.distance) {
return { point, distance };
}
return furthest;
},
{
point: firstSnapLine.points[0],
distance: pointDistance(
intersection,
firstSnapLine.points[0],
),
},
);
firstSnapLine.points = [furthestPoint.point, intersection]; this.setState({
snapLines: result.snapLines,
this.setState({ });
snapLines: [firstSnapLine],
});
} else {
this.setState({
snapLines: [],
});
}
}
} else { } else {
dx = gridX + snapOffset.x - newElement.x; dx = gridX + snapOffset.x - newElement.x;
dy = gridY + snapOffset.y - newElement.y; dy = gridY + snapOffset.y - newElement.y;