mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-11-19 12:14:24 +01:00
fix: Restore arrow start point when self binding
This commit is contained in:
@@ -665,41 +665,46 @@ const getBindingStrategyForDraggingBindingElementEndpoints_simple = (
|
|||||||
otherBinding.elementId,
|
otherBinding.elementId,
|
||||||
) as NonDeleted<ExcalidrawBindableElement>)
|
) as NonDeleted<ExcalidrawBindableElement>)
|
||||||
: undefined;
|
: undefined;
|
||||||
const otherFocusPoint =
|
// const otherFocusPoint =
|
||||||
otherBinding &&
|
// otherBinding &&
|
||||||
otherBindableElement &&
|
// otherBindableElement &&
|
||||||
getGlobalFixedPointForBindableElement(
|
// getGlobalFixedPointForBindableElement(
|
||||||
otherBinding.fixedPoint,
|
// otherBinding.fixedPoint,
|
||||||
otherBindableElement,
|
// otherBindableElement,
|
||||||
elementsMap,
|
// elementsMap,
|
||||||
);
|
// );
|
||||||
const otherPoint = LinearElementEditor.getPointAtIndexGlobalCoordinates(
|
// const otherPoint = LinearElementEditor.getPointAtIndexGlobalCoordinates(
|
||||||
arrow,
|
// arrow,
|
||||||
startDragged ? -1 : 0,
|
// startDragged ? -1 : 0,
|
||||||
elementsMap,
|
// elementsMap,
|
||||||
);
|
// );
|
||||||
const otherFocusPointIsInElement =
|
// const otherFocusPointIsInElement =
|
||||||
otherBindableElement &&
|
// otherBindableElement &&
|
||||||
otherFocusPoint &&
|
// otherFocusPoint &&
|
||||||
isPointInElement(otherFocusPoint, otherBindableElement, elementsMap);
|
// isPointInElement(otherFocusPoint, otherBindableElement, elementsMap);
|
||||||
|
|
||||||
// Handle outside-outside binding with the same element
|
// Handle outside-outside binding to the same element
|
||||||
if (
|
if (otherBinding && otherBinding.elementId === hit?.id) {
|
||||||
otherBinding &&
|
|
||||||
otherBinding.elementId === hit?.id &&
|
|
||||||
!pointInElement &&
|
|
||||||
!otherFocusPointIsInElement
|
|
||||||
) {
|
|
||||||
const [startFixedPoint, endFixedPoint] = getGlobalFixedPoints(
|
const [startFixedPoint, endFixedPoint] = getGlobalFixedPoints(
|
||||||
arrow,
|
arrow,
|
||||||
elementsMap,
|
elementsMap,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
invariant(
|
||||||
|
!opts?.newArrow || appState.selectedLinearElement?.initialState.origin,
|
||||||
|
"appState.selectedLinearElement.initialState.origin must be defined for new arrows",
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
start: {
|
start: {
|
||||||
mode: "inside",
|
mode: "inside",
|
||||||
element: hit,
|
element: hit,
|
||||||
focusPoint: startDragged ? globalPoint : startFixedPoint,
|
focusPoint: startDragged
|
||||||
|
? globalPoint
|
||||||
|
: // NOTE: Can only affect the start point because new arrows always drag the end point
|
||||||
|
opts?.newArrow
|
||||||
|
? appState.selectedLinearElement!.initialState.origin!
|
||||||
|
: startFixedPoint,
|
||||||
},
|
},
|
||||||
end: {
|
end: {
|
||||||
mode: "inside",
|
mode: "inside",
|
||||||
@@ -709,8 +714,31 @@ const getBindingStrategyForDraggingBindingElementEndpoints_simple = (
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle special alt key case to inside bind no matter what
|
||||||
|
if (opts?.altKey) {
|
||||||
|
return {
|
||||||
|
start:
|
||||||
|
startDragged && hit
|
||||||
|
? {
|
||||||
|
mode: "inside",
|
||||||
|
element: hit,
|
||||||
|
focusPoint: globalPoint,
|
||||||
|
}
|
||||||
|
: start,
|
||||||
|
end:
|
||||||
|
endDragged && hit
|
||||||
|
? {
|
||||||
|
mode: "inside",
|
||||||
|
element: hit,
|
||||||
|
focusPoint: globalPoint,
|
||||||
|
}
|
||||||
|
: end,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle normal cases
|
||||||
const current: BindingStrategy = hit
|
const current: BindingStrategy = hit
|
||||||
? pointInElement || opts?.altKey
|
? pointInElement
|
||||||
? {
|
? {
|
||||||
mode: "inside",
|
mode: "inside",
|
||||||
element: hit,
|
element: hit,
|
||||||
@@ -731,26 +759,12 @@ const getBindingStrategyForDraggingBindingElementEndpoints_simple = (
|
|||||||
: { mode: null };
|
: { mode: null };
|
||||||
|
|
||||||
const other: BindingStrategy =
|
const other: BindingStrategy =
|
||||||
!opts?.altKey && opts?.newArrow && otherBindableElement
|
otherBindableElement &&
|
||||||
? otherBindableElement.id === hit?.id
|
appState.selectedLinearElement?.initialState.altFocusPoint
|
||||||
? {
|
? {
|
||||||
mode: "inside",
|
|
||||||
element: otherBindableElement,
|
|
||||||
focusPoint: otherPoint,
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
mode: "orbit",
|
mode: "orbit",
|
||||||
element: otherBindableElement,
|
element: otherBindableElement,
|
||||||
focusPoint:
|
focusPoint: appState.selectedLinearElement.initialState.altFocusPoint,
|
||||||
otherFocusPointIsInElement && !opts?.initialBinding
|
|
||||||
? otherFocusPoint
|
|
||||||
: projectFixedPointOntoDiagonal(
|
|
||||||
arrow,
|
|
||||||
otherPoint,
|
|
||||||
otherBindableElement,
|
|
||||||
startDragged ? "end" : "start",
|
|
||||||
elementsMap,
|
|
||||||
) || otherPoint,
|
|
||||||
}
|
}
|
||||||
: { mode: undefined };
|
: { mode: undefined };
|
||||||
|
|
||||||
@@ -2128,7 +2142,7 @@ export class BindableElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const getGlobalFixedPointForBindableElement = (
|
export const getGlobalFixedPointForBindableElement = (
|
||||||
fixedPointRatio: [number, number],
|
fixedPointRatio: FixedPoint,
|
||||||
element: ExcalidrawBindableElement,
|
element: ExcalidrawBindableElement,
|
||||||
elementsMap: ElementsMap,
|
elementsMap: ElementsMap,
|
||||||
): GlobalPoint => {
|
): GlobalPoint => {
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import {
|
|||||||
getHoveredElementForBinding,
|
getHoveredElementForBinding,
|
||||||
isPathALoop,
|
isPathALoop,
|
||||||
moveArrowAboveBindable,
|
moveArrowAboveBindable,
|
||||||
|
projectFixedPointOntoDiagonal,
|
||||||
type Store,
|
type Store,
|
||||||
} from "@excalidraw/element";
|
} from "@excalidraw/element";
|
||||||
|
|
||||||
@@ -139,6 +140,7 @@ export class LinearElementEditor {
|
|||||||
added: boolean;
|
added: boolean;
|
||||||
};
|
};
|
||||||
arrowStartIsInside: boolean;
|
arrowStartIsInside: boolean;
|
||||||
|
altFocusPoint: Readonly<GlobalPoint> | null;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
/** whether you're dragging a point */
|
/** whether you're dragging a point */
|
||||||
@@ -189,6 +191,7 @@ export class LinearElementEditor {
|
|||||||
added: false,
|
added: false,
|
||||||
},
|
},
|
||||||
arrowStartIsInside: false,
|
arrowStartIsInside: false,
|
||||||
|
altFocusPoint: null,
|
||||||
};
|
};
|
||||||
this.hoverPointIndex = -1;
|
this.hoverPointIndex = -1;
|
||||||
this.segmentMidPointHoveredCoords = null;
|
this.segmentMidPointHoveredCoords = null;
|
||||||
@@ -395,9 +398,28 @@ export class LinearElementEditor {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const startBindingElement =
|
||||||
|
isBindingElement(element) &&
|
||||||
|
element.startBinding &&
|
||||||
|
(elementsMap.get(
|
||||||
|
element.startBinding.elementId,
|
||||||
|
) as ExcalidrawBindableElement | null);
|
||||||
const newLinearElementEditor = {
|
const newLinearElementEditor = {
|
||||||
...linearElementEditor,
|
...linearElementEditor,
|
||||||
customLineAngle,
|
customLineAngle,
|
||||||
|
initialState: {
|
||||||
|
...linearElementEditor.initialState,
|
||||||
|
altFocusPoint:
|
||||||
|
!linearElementEditor.initialState.altFocusPoint && startBindingElement
|
||||||
|
? projectFixedPointOntoDiagonal(
|
||||||
|
element,
|
||||||
|
pointFrom<GlobalPoint>(element.x, element.y),
|
||||||
|
startBindingElement,
|
||||||
|
"start",
|
||||||
|
elementsMap,
|
||||||
|
)
|
||||||
|
: linearElementEditor.initialState.altFocusPoint,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -582,6 +604,12 @@ export class LinearElementEditor {
|
|||||||
: null;
|
: null;
|
||||||
|
|
||||||
const newHoverPointIndex = newLastClickedPoint;
|
const newHoverPointIndex = newLastClickedPoint;
|
||||||
|
const startBindingElement =
|
||||||
|
isBindingElement(element) &&
|
||||||
|
element.startBinding &&
|
||||||
|
(elementsMap.get(
|
||||||
|
element.startBinding.elementId,
|
||||||
|
) as ExcalidrawBindableElement | null);
|
||||||
|
|
||||||
const newLinearElementEditor = {
|
const newLinearElementEditor = {
|
||||||
...linearElementEditor,
|
...linearElementEditor,
|
||||||
@@ -589,6 +617,16 @@ export class LinearElementEditor {
|
|||||||
initialState: {
|
initialState: {
|
||||||
...linearElementEditor.initialState,
|
...linearElementEditor.initialState,
|
||||||
lastClickedPoint: newLastClickedPoint,
|
lastClickedPoint: newLastClickedPoint,
|
||||||
|
altFocusPoint:
|
||||||
|
!linearElementEditor.initialState.altFocusPoint && startBindingElement
|
||||||
|
? projectFixedPointOntoDiagonal(
|
||||||
|
element,
|
||||||
|
pointFrom<GlobalPoint>(element.x, element.y),
|
||||||
|
startBindingElement,
|
||||||
|
"start",
|
||||||
|
elementsMap,
|
||||||
|
)
|
||||||
|
: linearElementEditor.initialState.altFocusPoint,
|
||||||
},
|
},
|
||||||
segmentMidPointHoveredCoords: newSelectedMidPointHoveredCoords,
|
segmentMidPointHoveredCoords: newSelectedMidPointHoveredCoords,
|
||||||
hoverPointIndex: newHoverPointIndex,
|
hoverPointIndex: newHoverPointIndex,
|
||||||
@@ -959,6 +997,7 @@ export class LinearElementEditor {
|
|||||||
appState,
|
appState,
|
||||||
elementsMap,
|
elementsMap,
|
||||||
);
|
);
|
||||||
|
const point = pointFrom<GlobalPoint>(scenePointer.x, scenePointer.y);
|
||||||
let segmentMidpointIndex = null;
|
let segmentMidpointIndex = null;
|
||||||
|
|
||||||
if (segmentMidpoint) {
|
if (segmentMidpoint) {
|
||||||
@@ -990,7 +1029,7 @@ export class LinearElementEditor {
|
|||||||
initialState: {
|
initialState: {
|
||||||
prevSelectedPointsIndices: linearElementEditor.selectedPointsIndices,
|
prevSelectedPointsIndices: linearElementEditor.selectedPointsIndices,
|
||||||
lastClickedPoint: -1,
|
lastClickedPoint: -1,
|
||||||
origin: pointFrom<GlobalPoint>(scenePointer.x, scenePointer.y),
|
origin: point,
|
||||||
segmentMidpoint: {
|
segmentMidpoint: {
|
||||||
value: segmentMidpoint,
|
value: segmentMidpoint,
|
||||||
index: segmentMidpointIndex,
|
index: segmentMidpointIndex,
|
||||||
@@ -999,6 +1038,7 @@ export class LinearElementEditor {
|
|||||||
arrowStartIsInside:
|
arrowStartIsInside:
|
||||||
!!app.state.newElement &&
|
!!app.state.newElement &&
|
||||||
(app.state.bindMode === "inside" || app.state.bindMode === "skip"),
|
(app.state.bindMode === "inside" || app.state.bindMode === "skip"),
|
||||||
|
altFocusPoint: null,
|
||||||
},
|
},
|
||||||
selectedPointsIndices: [element.points.length - 1],
|
selectedPointsIndices: [element.points.length - 1],
|
||||||
lastUncommittedPoint: null,
|
lastUncommittedPoint: null,
|
||||||
@@ -1051,7 +1091,7 @@ export class LinearElementEditor {
|
|||||||
initialState: {
|
initialState: {
|
||||||
prevSelectedPointsIndices: linearElementEditor.selectedPointsIndices,
|
prevSelectedPointsIndices: linearElementEditor.selectedPointsIndices,
|
||||||
lastClickedPoint: clickedPointIndex,
|
lastClickedPoint: clickedPointIndex,
|
||||||
origin: pointFrom<GlobalPoint>(scenePointer.x, scenePointer.y),
|
origin: point,
|
||||||
segmentMidpoint: {
|
segmentMidpoint: {
|
||||||
value: segmentMidpoint,
|
value: segmentMidpoint,
|
||||||
index: segmentMidpointIndex,
|
index: segmentMidpointIndex,
|
||||||
@@ -1060,6 +1100,7 @@ export class LinearElementEditor {
|
|||||||
arrowStartIsInside:
|
arrowStartIsInside:
|
||||||
!!app.state.newElement &&
|
!!app.state.newElement &&
|
||||||
(app.state.bindMode === "inside" || app.state.bindMode === "skip"),
|
(app.state.bindMode === "inside" || app.state.bindMode === "skip"),
|
||||||
|
altFocusPoint: null,
|
||||||
},
|
},
|
||||||
selectedPointsIndices: nextSelectedPointsIndices,
|
selectedPointsIndices: nextSelectedPointsIndices,
|
||||||
pointerOffset: targetPoint
|
pointerOffset: targetPoint
|
||||||
@@ -2280,7 +2321,8 @@ const pointDraggingUpdates = (
|
|||||||
)! as ExcalidrawBindableElement)
|
)! as ExcalidrawBindableElement)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
const startLocalPoint = endIsDraggingOverStartElement
|
const startLocalPoint =
|
||||||
|
endIsDraggingOverStartElement && getFeatureFlag("COMPLEX_BINDINGS")
|
||||||
? nextArrow.points[0]
|
? nextArrow.points[0]
|
||||||
: startIsDraggingOverEndElement &&
|
: startIsDraggingOverEndElement &&
|
||||||
app.state.bindMode !== "inside" &&
|
app.state.bindMode !== "inside" &&
|
||||||
@@ -2337,49 +2379,6 @@ const pointDraggingUpdates = (
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// const shouldAllowDraggingPoint = (
|
|
||||||
// element: ExcalidrawLinearElement,
|
|
||||||
// scenePointerX: number,
|
|
||||||
// scenePointerY: number,
|
|
||||||
// selectedPointsIndices: readonly number[],
|
|
||||||
// elementsMap: Readonly<NonDeletedSceneElementsMap>,
|
|
||||||
// app: AppClassProperties,
|
|
||||||
// ) => {
|
|
||||||
// if (!isSimpleArrow(element)) {
|
|
||||||
// return true;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const scenePointer = pointFrom<GlobalPoint>(scenePointerX, scenePointerY);
|
|
||||||
|
|
||||||
// // Do not allow dragging the bound arrow closer to the shape than
|
|
||||||
// // the dragging threshold
|
|
||||||
// let allowDrag = true;
|
|
||||||
|
|
||||||
// if (selectedPointsIndices.includes(0) && element.startBinding) {
|
|
||||||
// const boundElement = elementsMap.get(
|
|
||||||
// element.startBinding.elementId,
|
|
||||||
// )! as ExcalidrawBindableElement;
|
|
||||||
// const dist = distanceToElement(boundElement, elementsMap, scenePointer);
|
|
||||||
// const inside = isPointInElement(scenePointer, boundElement, elementsMap);
|
|
||||||
// allowDrag =
|
|
||||||
// allowDrag && (dist > getFixedBindingDistance(boundElement) || inside);
|
|
||||||
// }
|
|
||||||
// if (
|
|
||||||
// selectedPointsIndices.includes(element.points.length - 1) &&
|
|
||||||
// element.endBinding
|
|
||||||
// ) {
|
|
||||||
// const boundElement = elementsMap.get(
|
|
||||||
// element.endBinding.elementId,
|
|
||||||
// )! as ExcalidrawBindableElement;
|
|
||||||
// const dist = distanceToElement(boundElement, elementsMap, scenePointer);
|
|
||||||
// const inside = isPointInElement(scenePointer, boundElement, elementsMap);
|
|
||||||
// allowDrag =
|
|
||||||
// allowDrag && (dist > getFixedBindingDistance(boundElement) || inside);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return allowDrag;
|
|
||||||
// };
|
|
||||||
|
|
||||||
const determineCustomLinearAngle = (
|
const determineCustomLinearAngle = (
|
||||||
pivotPoint: LocalPoint,
|
pivotPoint: LocalPoint,
|
||||||
draggedPoint: LocalPoint,
|
draggedPoint: LocalPoint,
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import { elementCenterPoint, getDiamondPoints } from "./bounds";
|
|||||||
|
|
||||||
import { generateLinearCollisionShape } from "./shape";
|
import { generateLinearCollisionShape } from "./shape";
|
||||||
|
|
||||||
|
import { isPointInElement } from "./collision";
|
||||||
import { LinearElementEditor } from "./linearElementEditor";
|
import { LinearElementEditor } from "./linearElementEditor";
|
||||||
import { isRectangularElement } from "./typeChecks";
|
import { isRectangularElement } from "./typeChecks";
|
||||||
|
|
||||||
@@ -557,14 +558,17 @@ export const projectFixedPointOntoDiagonal = (
|
|||||||
element: ExcalidrawElement,
|
element: ExcalidrawElement,
|
||||||
startOrEnd: "start" | "end",
|
startOrEnd: "start" | "end",
|
||||||
elementsMap: ElementsMap,
|
elementsMap: ElementsMap,
|
||||||
) => {
|
): GlobalPoint | null => {
|
||||||
|
invariant(arrow.points.length >= 2, "Arrow must have at least two points");
|
||||||
|
if (arrow.width < 1 && arrow.height < 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const [diagonalOne, diagonalTwo] = getDiagonalsForBindableElement(
|
const [diagonalOne, diagonalTwo] = getDiagonalsForBindableElement(
|
||||||
element,
|
element,
|
||||||
elementsMap,
|
elementsMap,
|
||||||
);
|
);
|
||||||
|
|
||||||
invariant(arrow.points.length >= 2, "Arrow must have at least two points");
|
|
||||||
|
|
||||||
const a = LinearElementEditor.getPointAtIndexGlobalCoordinates(
|
const a = LinearElementEditor.getPointAtIndexGlobalCoordinates(
|
||||||
arrow,
|
arrow,
|
||||||
startOrEnd === "start" ? 1 : arrow.points.length - 2,
|
startOrEnd === "start" ? 1 : arrow.points.length - 2,
|
||||||
@@ -587,9 +591,12 @@ export const projectFixedPointOntoDiagonal = (
|
|||||||
const d1 = p1 && pointDistance(a, p1);
|
const d1 = p1 && pointDistance(a, p1);
|
||||||
const d2 = p2 && pointDistance(a, p2);
|
const d2 = p2 && pointDistance(a, p2);
|
||||||
|
|
||||||
|
let p = null;
|
||||||
if (d1 != null && d2 != null) {
|
if (d1 != null && d2 != null) {
|
||||||
return d1 < d2 ? p1 : p2;
|
p = d1 < d2 ? p1 : p2;
|
||||||
|
} else {
|
||||||
|
p = p1 || p2 || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return p1 || p2 || null;
|
return p && isPointInElement(p, element, elementsMap) ? p : null;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user