mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-08-06 05:46:26 +00:00
Move point click arrow creation over to common strategy
Signed-off-by: Mark Tolmacs <mark@lazycat.hu>
This commit is contained in:
@@ -135,7 +135,10 @@ export const bindOrUnbindBindingElement = (
|
||||
scene.getNonDeletedElementsMap(),
|
||||
scene.getNonDeletedElements(),
|
||||
appState,
|
||||
opts,
|
||||
{
|
||||
...opts,
|
||||
appState,
|
||||
},
|
||||
);
|
||||
bindOrUnbindBindingElementEdge(arrow, start, "start", scene);
|
||||
bindOrUnbindBindingElementEdge(arrow, end, "end", scene);
|
||||
@@ -225,6 +228,71 @@ const getOriginalBindingsIfStillCloseToBindingEnds = (
|
||||
return null;
|
||||
});
|
||||
|
||||
export const getStartGlobalEndLocalPointsForBinding = (
|
||||
arrow: NonDeleted<ExcalidrawArrowElement>,
|
||||
start: BindingStrategy,
|
||||
end: BindingStrategy,
|
||||
startPoint: GlobalPoint,
|
||||
endPoint: LocalPoint,
|
||||
elementsMap: ElementsMap,
|
||||
): [GlobalPoint, LocalPoint] => {
|
||||
let startGlobalPoint = startPoint;
|
||||
let endLocalPoint = endPoint;
|
||||
if (start.mode) {
|
||||
const newStartLocalPoint = updateBoundPoint(
|
||||
arrow,
|
||||
"startBinding",
|
||||
start.mode
|
||||
? {
|
||||
...calculateFixedPointForNonElbowArrowBinding(
|
||||
arrow,
|
||||
start.element,
|
||||
"start",
|
||||
elementsMap,
|
||||
start.focusPoint,
|
||||
),
|
||||
elementId: start.element.id,
|
||||
mode: start.mode,
|
||||
}
|
||||
: null,
|
||||
start.element,
|
||||
elementsMap,
|
||||
);
|
||||
startGlobalPoint = newStartLocalPoint
|
||||
? LinearElementEditor.getPointGlobalCoordinates(
|
||||
arrow,
|
||||
newStartLocalPoint,
|
||||
elementsMap,
|
||||
)
|
||||
: startGlobalPoint;
|
||||
}
|
||||
|
||||
if (end.mode) {
|
||||
const newEndLocalPoint = updateBoundPoint(
|
||||
arrow,
|
||||
"endBinding",
|
||||
end.mode
|
||||
? {
|
||||
...calculateFixedPointForNonElbowArrowBinding(
|
||||
arrow,
|
||||
end.element,
|
||||
"end",
|
||||
elementsMap,
|
||||
end.focusPoint,
|
||||
),
|
||||
elementId: end.element.id,
|
||||
mode: end.mode,
|
||||
}
|
||||
: null,
|
||||
end.element,
|
||||
elementsMap,
|
||||
);
|
||||
endLocalPoint = newEndLocalPoint ?? endLocalPoint;
|
||||
}
|
||||
|
||||
return [startGlobalPoint, endLocalPoint];
|
||||
};
|
||||
|
||||
const bindingStrategyForEndpointDragging = (
|
||||
point: GlobalPoint,
|
||||
oppositeBinding: FixedPointBinding | null,
|
||||
@@ -233,7 +301,8 @@ const bindingStrategyForEndpointDragging = (
|
||||
zoom: AppState["zoom"],
|
||||
globalBindMode?: AppState["bindMode"],
|
||||
opts?: {
|
||||
newArrow: boolean;
|
||||
newArrow?: boolean;
|
||||
appState?: AppState;
|
||||
},
|
||||
): { current: BindingStrategy; other: BindingStrategy } => {
|
||||
let current: BindingStrategy = { mode: undefined };
|
||||
@@ -256,7 +325,34 @@ const bindingStrategyForEndpointDragging = (
|
||||
return { current, other };
|
||||
}
|
||||
|
||||
// Dragged start point is outside of any bindable element
|
||||
// Update the start point on new arrows
|
||||
if (
|
||||
opts?.newArrow &&
|
||||
opts?.appState?.selectedLinearElement &&
|
||||
oppositeBinding
|
||||
) {
|
||||
const oppositeBindingElement = elementsMap.get(
|
||||
oppositeBinding?.elementId,
|
||||
) as ExcalidrawBindableElement;
|
||||
|
||||
if (oppositeBinding.elementId !== hovered?.id) {
|
||||
other = {
|
||||
element: oppositeBindingElement,
|
||||
mode: "orbit",
|
||||
focusPoint: elementCenterPoint(oppositeBindingElement, elementsMap),
|
||||
};
|
||||
} else {
|
||||
other = {
|
||||
element: oppositeBindingElement,
|
||||
mode: "inside",
|
||||
focusPoint:
|
||||
opts.appState.selectedLinearElement.pointerDownState
|
||||
.arrowOriginalStartPoint,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Dragged point is outside of any bindable element
|
||||
// so we break any existing binding
|
||||
if (!hovered) {
|
||||
return { current: { mode: null }, other };
|
||||
@@ -342,14 +438,15 @@ const bindingStrategyForEndpointDragging = (
|
||||
return { current, other };
|
||||
};
|
||||
|
||||
const getBindingStrategyForDraggingBindingElementEndpoints = (
|
||||
export const getBindingStrategyForDraggingBindingElementEndpoints = (
|
||||
arrow: NonDeleted<ExcalidrawArrowElement>,
|
||||
draggingPoints: PointsPositionUpdates,
|
||||
elementsMap: NonDeletedSceneElementsMap,
|
||||
elements: readonly Ordered<NonDeletedExcalidrawElement>[],
|
||||
appState: AppState,
|
||||
opts?: {
|
||||
newArrow: boolean;
|
||||
newArrow?: boolean;
|
||||
appState?: AppState;
|
||||
},
|
||||
): { start: BindingStrategy; end: BindingStrategy } => {
|
||||
const globalBindMode = appState.bindMode || "focus";
|
||||
|
||||
@@ -16,7 +16,6 @@ import {
|
||||
vectorSubtract,
|
||||
vectorDot,
|
||||
vectorNormalize,
|
||||
pointsEqual,
|
||||
lineSegment,
|
||||
} from "@excalidraw/math";
|
||||
|
||||
@@ -240,7 +239,8 @@ import {
|
||||
calculateFixedPointForNonElbowArrowBinding,
|
||||
normalizeFixedPoint,
|
||||
bindOrUnbindBindingElement,
|
||||
updateBoundPoint,
|
||||
getBindingStrategyForDraggingBindingElementEndpoints,
|
||||
getStartGlobalEndLocalPointsForBinding,
|
||||
} from "@excalidraw/element";
|
||||
|
||||
import type { GlobalPoint, LocalPoint, Radians } from "@excalidraw/math";
|
||||
@@ -6155,81 +6155,60 @@ class App extends React.Component<AppProps, AppState> {
|
||||
));
|
||||
}
|
||||
|
||||
if (
|
||||
isBindingElement(multiElement) &&
|
||||
this.state.bindMode === "orbit" &&
|
||||
isBindingEnabled(this.state)
|
||||
) {
|
||||
const elementsMap = this.scene.getNonDeletedElementsMap();
|
||||
const hoveredElement = getHoveredElementForBinding(
|
||||
pointFrom<GlobalPoint>(scenePointerX, scenePointerY),
|
||||
this.scene.getNonDeletedElements(),
|
||||
this.scene.getNonDeletedElementsMap(),
|
||||
this.state.zoom,
|
||||
);
|
||||
const otherPoint =
|
||||
LinearElementEditor.getPointAtIndexGlobalCoordinates(
|
||||
multiElement,
|
||||
0,
|
||||
this.scene.getNonDeletedElementsMap(),
|
||||
);
|
||||
const otherHoveredElement = getHoveredElementForBinding(
|
||||
otherPoint,
|
||||
this.scene.getNonDeletedElements(),
|
||||
this.scene.getNonDeletedElementsMap(),
|
||||
this.state.zoom,
|
||||
);
|
||||
|
||||
if (hoveredElement?.id !== otherHoveredElement?.id) {
|
||||
const avoidancePoint =
|
||||
multiElement &&
|
||||
hoveredElement &&
|
||||
getOutlineAvoidingPoint(
|
||||
multiElement,
|
||||
hoveredElement,
|
||||
pointFrom<GlobalPoint>(scenePointerX, scenePointerY),
|
||||
multiElement.points.length - 1,
|
||||
elementsMap,
|
||||
shouldRotateWithDiscreteAngle(event)
|
||||
? lineSegment<GlobalPoint>(
|
||||
otherPoint,
|
||||
pointFrom<GlobalPoint>(
|
||||
multiElement.x + lastCommittedX + dxFromLastCommitted,
|
||||
multiElement.y + lastCommittedY + dyFromLastCommitted,
|
||||
),
|
||||
)
|
||||
: undefined,
|
||||
);
|
||||
const x = avoidancePoint
|
||||
? avoidancePoint[0]
|
||||
: hoveredElement
|
||||
? scenePointerX
|
||||
: gridX;
|
||||
const y = avoidancePoint
|
||||
? avoidancePoint[1]
|
||||
: hoveredElement
|
||||
? scenePointerY
|
||||
: gridY;
|
||||
dxFromLastCommitted = x - rx - lastCommittedX;
|
||||
dyFromLastCommitted = y - ry - lastCommittedY;
|
||||
}
|
||||
}
|
||||
|
||||
if (isPathALoop(points, this.state.zoom.value)) {
|
||||
setCursor(this.interactiveCanvas, CURSOR_TYPE.POINTER);
|
||||
}
|
||||
|
||||
// Update arrow points
|
||||
const elementsMap = this.scene.getNonDeletedElementsMap();
|
||||
let startGlobalPoint =
|
||||
this.state.selectedLinearElement?.pointerDownState
|
||||
?.arrowOriginalStartPoint ??
|
||||
LinearElementEditor.getPointAtIndexGlobalCoordinates(
|
||||
multiElement,
|
||||
0,
|
||||
elementsMap,
|
||||
);
|
||||
let endLocalPoint = pointFrom<LocalPoint>(
|
||||
lastCommittedX + dxFromLastCommitted,
|
||||
lastCommittedY + dyFromLastCommitted,
|
||||
);
|
||||
|
||||
if (isBindingElement(multiElement)) {
|
||||
const point = pointFrom<LocalPoint>(
|
||||
scenePointerX - rx,
|
||||
scenePointerY - ry,
|
||||
);
|
||||
const { start, end } =
|
||||
getBindingStrategyForDraggingBindingElementEndpoints(
|
||||
multiElement,
|
||||
new Map([
|
||||
[multiElement.points.length - 1, { point, isDragging: true }],
|
||||
]),
|
||||
elementsMap,
|
||||
this.scene.getNonDeletedElements(),
|
||||
this.state,
|
||||
{ newArrow: !!this.state.newElement, appState: this.state },
|
||||
);
|
||||
|
||||
[startGlobalPoint, endLocalPoint] =
|
||||
getStartGlobalEndLocalPointsForBinding(
|
||||
multiElement,
|
||||
start,
|
||||
end,
|
||||
startGlobalPoint,
|
||||
endLocalPoint,
|
||||
elementsMap,
|
||||
);
|
||||
}
|
||||
|
||||
// update last uncommitted point
|
||||
this.scene.mutateElement(
|
||||
multiElement,
|
||||
{
|
||||
points: [
|
||||
...points.slice(0, -1),
|
||||
pointFrom<LocalPoint>(
|
||||
lastCommittedX + dxFromLastCommitted,
|
||||
lastCommittedY + dyFromLastCommitted,
|
||||
),
|
||||
],
|
||||
x: startGlobalPoint[0],
|
||||
y: startGlobalPoint[1],
|
||||
points: [...points.slice(0, -1), endLocalPoint],
|
||||
},
|
||||
{
|
||||
isDragging: true,
|
||||
@@ -6237,62 +6216,6 @@ class App extends React.Component<AppProps, AppState> {
|
||||
},
|
||||
);
|
||||
|
||||
// If start is bound then snap the fixed binding point if needed
|
||||
if (
|
||||
isArrowElement(multiElement) &&
|
||||
multiElement.startBinding &&
|
||||
multiElement.startBinding.mode === "orbit"
|
||||
) {
|
||||
const elementsMap = this.scene.getNonDeletedElementsMap();
|
||||
const hoveredElement = getHoveredElementForBinding(
|
||||
pointFrom<GlobalPoint>(scenePointerX, scenePointerY),
|
||||
this.scene.getNonDeletedElements(),
|
||||
this.scene.getNonDeletedElementsMap(),
|
||||
this.state.zoom,
|
||||
);
|
||||
if (
|
||||
!hoveredElement ||
|
||||
hoveredElement.id !== multiElement.startBinding.elementId
|
||||
) {
|
||||
const startPoint =
|
||||
LinearElementEditor.getPointAtIndexGlobalCoordinates(
|
||||
multiElement,
|
||||
0,
|
||||
elementsMap,
|
||||
);
|
||||
const startElement = this.scene.getElement(
|
||||
multiElement.startBinding.elementId,
|
||||
) as ExcalidrawBindableElement;
|
||||
const localPoint = updateBoundPoint(
|
||||
multiElement,
|
||||
"startBinding",
|
||||
multiElement.startBinding,
|
||||
startElement,
|
||||
elementsMap,
|
||||
);
|
||||
const avoidancePoint = localPoint
|
||||
? LinearElementEditor.getPointGlobalCoordinates(
|
||||
multiElement,
|
||||
localPoint,
|
||||
elementsMap,
|
||||
)
|
||||
: null;
|
||||
if (avoidancePoint && !pointsEqual(startPoint, avoidancePoint)) {
|
||||
const point = LinearElementEditor.pointFromAbsoluteCoords(
|
||||
multiElement,
|
||||
avoidancePoint,
|
||||
elementsMap,
|
||||
);
|
||||
|
||||
LinearElementEditor.movePoints(
|
||||
multiElement,
|
||||
this.scene,
|
||||
new Map([[0, { point }]]),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// in this path, we're mutating multiElement to reflect
|
||||
// how it will be after adding pointer position as the next point
|
||||
// trigger update here so that new element canvas renders again to reflect this
|
||||
|
||||
Reference in New Issue
Block a user