|
|
@ -7,9 +7,10 @@ import type {
|
|
|
|
ExcalidrawTextElementWithContainer,
|
|
|
|
ExcalidrawTextElementWithContainer,
|
|
|
|
ElementsMap,
|
|
|
|
ElementsMap,
|
|
|
|
NonDeletedSceneElementsMap,
|
|
|
|
NonDeletedSceneElementsMap,
|
|
|
|
OrderedExcalidrawElement,
|
|
|
|
|
|
|
|
FixedPointBinding,
|
|
|
|
FixedPointBinding,
|
|
|
|
SceneElementsMap,
|
|
|
|
SceneElementsMap,
|
|
|
|
|
|
|
|
FixedSegment,
|
|
|
|
|
|
|
|
ExcalidrawElbowArrowElement,
|
|
|
|
} from "./types";
|
|
|
|
} from "./types";
|
|
|
|
import { getElementAbsoluteCoords, getLockedLinearCursorAlignSize } from ".";
|
|
|
|
import { getElementAbsoluteCoords, getLockedLinearCursorAlignSize } from ".";
|
|
|
|
import type { Bounds } from "./bounds";
|
|
|
|
import type { Bounds } from "./bounds";
|
|
|
@ -24,6 +25,7 @@ import type {
|
|
|
|
InteractiveCanvasAppState,
|
|
|
|
InteractiveCanvasAppState,
|
|
|
|
AppClassProperties,
|
|
|
|
AppClassProperties,
|
|
|
|
NullableGridSize,
|
|
|
|
NullableGridSize,
|
|
|
|
|
|
|
|
Zoom,
|
|
|
|
} from "../types";
|
|
|
|
} from "../types";
|
|
|
|
import { mutateElement } from "./mutateElement";
|
|
|
|
import { mutateElement } from "./mutateElement";
|
|
|
|
|
|
|
|
|
|
|
@ -32,7 +34,7 @@ import {
|
|
|
|
getHoveredElementForBinding,
|
|
|
|
getHoveredElementForBinding,
|
|
|
|
isBindingEnabled,
|
|
|
|
isBindingEnabled,
|
|
|
|
} from "./binding";
|
|
|
|
} from "./binding";
|
|
|
|
import { invariant, toBrandedType, tupleToCoors } from "../utils";
|
|
|
|
import { invariant, tupleToCoors } from "../utils";
|
|
|
|
import {
|
|
|
|
import {
|
|
|
|
isBindingElement,
|
|
|
|
isBindingElement,
|
|
|
|
isElbowArrow,
|
|
|
|
isElbowArrow,
|
|
|
@ -44,7 +46,6 @@ import { DRAGGING_THRESHOLD } from "../constants";
|
|
|
|
import type { Mutable } from "../utility-types";
|
|
|
|
import type { Mutable } from "../utility-types";
|
|
|
|
import { ShapeCache } from "../scene/ShapeCache";
|
|
|
|
import { ShapeCache } from "../scene/ShapeCache";
|
|
|
|
import type { Store } from "../store";
|
|
|
|
import type { Store } from "../store";
|
|
|
|
import { mutateElbowArrow } from "./routing";
|
|
|
|
|
|
|
|
import type Scene from "../scene/Scene";
|
|
|
|
import type Scene from "../scene/Scene";
|
|
|
|
import type { Radians } from "../../math";
|
|
|
|
import type { Radians } from "../../math";
|
|
|
|
import {
|
|
|
|
import {
|
|
|
@ -56,6 +57,8 @@ import {
|
|
|
|
type GlobalPoint,
|
|
|
|
type GlobalPoint,
|
|
|
|
type LocalPoint,
|
|
|
|
type LocalPoint,
|
|
|
|
pointDistance,
|
|
|
|
pointDistance,
|
|
|
|
|
|
|
|
pointTranslate,
|
|
|
|
|
|
|
|
vectorFromPoint,
|
|
|
|
} from "../../math";
|
|
|
|
} from "../../math";
|
|
|
|
import {
|
|
|
|
import {
|
|
|
|
getBezierCurveLength,
|
|
|
|
getBezierCurveLength,
|
|
|
@ -65,6 +68,7 @@ import {
|
|
|
|
mapIntervalToBezierT,
|
|
|
|
mapIntervalToBezierT,
|
|
|
|
} from "../shapes";
|
|
|
|
} from "../shapes";
|
|
|
|
import { getGridPoint } from "../snapping";
|
|
|
|
import { getGridPoint } from "../snapping";
|
|
|
|
|
|
|
|
import { headingIsHorizontal, vectorToHeading } from "./heading";
|
|
|
|
|
|
|
|
|
|
|
|
const editorMidPointsCache: {
|
|
|
|
const editorMidPointsCache: {
|
|
|
|
version: number | null;
|
|
|
|
version: number | null;
|
|
|
@ -144,13 +148,13 @@ export class LinearElementEditor {
|
|
|
|
* @param id the `elementId` from the instance of this class (so that we can
|
|
|
|
* @param id the `elementId` from the instance of this class (so that we can
|
|
|
|
* statically guarantee this method returns an ExcalidrawLinearElement)
|
|
|
|
* statically guarantee this method returns an ExcalidrawLinearElement)
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
static getElement(
|
|
|
|
static getElement<T extends ExcalidrawLinearElement>(
|
|
|
|
id: InstanceType<typeof LinearElementEditor>["elementId"],
|
|
|
|
id: InstanceType<typeof LinearElementEditor>["elementId"],
|
|
|
|
elementsMap: ElementsMap,
|
|
|
|
elementsMap: ElementsMap,
|
|
|
|
) {
|
|
|
|
): T | null {
|
|
|
|
const element = elementsMap.get(id);
|
|
|
|
const element = elementsMap.get(id);
|
|
|
|
if (element) {
|
|
|
|
if (element) {
|
|
|
|
return element as NonDeleted<ExcalidrawLinearElement>;
|
|
|
|
return element as NonDeleted<T>;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -291,9 +295,7 @@ export class LinearElementEditor {
|
|
|
|
event[KEYS.CTRL_OR_CMD] ? null : app.getEffectiveGridSize(),
|
|
|
|
event[KEYS.CTRL_OR_CMD] ? null : app.getEffectiveGridSize(),
|
|
|
|
);
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
LinearElementEditor.movePoints(
|
|
|
|
LinearElementEditor.movePoints(element, [
|
|
|
|
element,
|
|
|
|
|
|
|
|
[
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
index: selectedIndex,
|
|
|
|
index: selectedIndex,
|
|
|
|
point: pointFrom(
|
|
|
|
point: pointFrom(
|
|
|
@ -302,9 +304,7 @@ export class LinearElementEditor {
|
|
|
|
),
|
|
|
|
),
|
|
|
|
isDragging: selectedIndex === lastClickedPoint,
|
|
|
|
isDragging: selectedIndex === lastClickedPoint,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
]);
|
|
|
|
elementsMap,
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
const newDraggingPointPosition = LinearElementEditor.createPointAt(
|
|
|
|
const newDraggingPointPosition = LinearElementEditor.createPointAt(
|
|
|
|
element,
|
|
|
|
element,
|
|
|
@ -339,7 +339,6 @@ export class LinearElementEditor {
|
|
|
|
isDragging: pointIndex === lastClickedPoint,
|
|
|
|
isDragging: pointIndex === lastClickedPoint,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
elementsMap,
|
|
|
|
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -422,9 +421,7 @@ export class LinearElementEditor {
|
|
|
|
selectedPoint === element.points.length - 1
|
|
|
|
selectedPoint === element.points.length - 1
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
if (isPathALoop(element.points, appState.zoom.value)) {
|
|
|
|
if (isPathALoop(element.points, appState.zoom.value)) {
|
|
|
|
LinearElementEditor.movePoints(
|
|
|
|
LinearElementEditor.movePoints(element, [
|
|
|
|
element,
|
|
|
|
|
|
|
|
[
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
index: selectedPoint,
|
|
|
|
index: selectedPoint,
|
|
|
|
point:
|
|
|
|
point:
|
|
|
@ -432,9 +429,7 @@ export class LinearElementEditor {
|
|
|
|
? element.points[element.points.length - 1]
|
|
|
|
? element.points[element.points.length - 1]
|
|
|
|
: element.points[0],
|
|
|
|
: element.points[0],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
]);
|
|
|
|
elementsMap,
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const bindingElement = isBindingEnabled(appState)
|
|
|
|
const bindingElement = isBindingEnabled(appState)
|
|
|
@ -495,6 +490,7 @@ export class LinearElementEditor {
|
|
|
|
|
|
|
|
|
|
|
|
// Since its not needed outside editor unless 2 pointer lines or bound text
|
|
|
|
// Since its not needed outside editor unless 2 pointer lines or bound text
|
|
|
|
if (
|
|
|
|
if (
|
|
|
|
|
|
|
|
!isElbowArrow(element) &&
|
|
|
|
!appState.editingLinearElement &&
|
|
|
|
!appState.editingLinearElement &&
|
|
|
|
element.points.length > 2 &&
|
|
|
|
element.points.length > 2 &&
|
|
|
|
!boundText
|
|
|
|
!boundText
|
|
|
@ -533,6 +529,7 @@ export class LinearElementEditor {
|
|
|
|
element,
|
|
|
|
element,
|
|
|
|
element.points[index],
|
|
|
|
element.points[index],
|
|
|
|
element.points[index + 1],
|
|
|
|
element.points[index + 1],
|
|
|
|
|
|
|
|
index,
|
|
|
|
appState.zoom,
|
|
|
|
appState.zoom,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
) {
|
|
|
|
) {
|
|
|
@ -573,19 +570,23 @@ export class LinearElementEditor {
|
|
|
|
scenePointer.x,
|
|
|
|
scenePointer.x,
|
|
|
|
scenePointer.y,
|
|
|
|
scenePointer.y,
|
|
|
|
);
|
|
|
|
);
|
|
|
|
if (clickedPointIndex >= 0) {
|
|
|
|
if (!isElbowArrow(element) && clickedPointIndex >= 0) {
|
|
|
|
return null;
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const points = LinearElementEditor.getPointsGlobalCoordinates(
|
|
|
|
const points = LinearElementEditor.getPointsGlobalCoordinates(
|
|
|
|
element,
|
|
|
|
element,
|
|
|
|
elementsMap,
|
|
|
|
elementsMap,
|
|
|
|
);
|
|
|
|
);
|
|
|
|
if (points.length >= 3 && !appState.editingLinearElement) {
|
|
|
|
if (
|
|
|
|
|
|
|
|
points.length >= 3 &&
|
|
|
|
|
|
|
|
!appState.editingLinearElement &&
|
|
|
|
|
|
|
|
!isElbowArrow(element)
|
|
|
|
|
|
|
|
) {
|
|
|
|
return null;
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const threshold =
|
|
|
|
const threshold =
|
|
|
|
LinearElementEditor.POINT_HANDLE_SIZE / appState.zoom.value;
|
|
|
|
(LinearElementEditor.POINT_HANDLE_SIZE + 1) / appState.zoom.value;
|
|
|
|
|
|
|
|
|
|
|
|
const existingSegmentMidpointHitCoords =
|
|
|
|
const existingSegmentMidpointHitCoords =
|
|
|
|
linearElementEditor.segmentMidPointHoveredCoords;
|
|
|
|
linearElementEditor.segmentMidPointHoveredCoords;
|
|
|
@ -604,10 +605,11 @@ export class LinearElementEditor {
|
|
|
|
let index = 0;
|
|
|
|
let index = 0;
|
|
|
|
const midPoints: typeof editorMidPointsCache["points"] =
|
|
|
|
const midPoints: typeof editorMidPointsCache["points"] =
|
|
|
|
LinearElementEditor.getEditorMidPoints(element, elementsMap, appState);
|
|
|
|
LinearElementEditor.getEditorMidPoints(element, elementsMap, appState);
|
|
|
|
|
|
|
|
|
|
|
|
while (index < midPoints.length) {
|
|
|
|
while (index < midPoints.length) {
|
|
|
|
if (midPoints[index] !== null) {
|
|
|
|
if (midPoints[index] !== null) {
|
|
|
|
const distance = pointDistance(
|
|
|
|
const distance = pointDistance(
|
|
|
|
pointFrom(midPoints[index]![0], midPoints[index]![1]),
|
|
|
|
midPoints[index]!,
|
|
|
|
pointFrom(scenePointer.x, scenePointer.y),
|
|
|
|
pointFrom(scenePointer.x, scenePointer.y),
|
|
|
|
);
|
|
|
|
);
|
|
|
|
if (distance <= threshold) {
|
|
|
|
if (distance <= threshold) {
|
|
|
@ -620,16 +622,25 @@ export class LinearElementEditor {
|
|
|
|
return null;
|
|
|
|
return null;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static isSegmentTooShort(
|
|
|
|
static isSegmentTooShort<P extends GlobalPoint | LocalPoint>(
|
|
|
|
element: NonDeleted<ExcalidrawLinearElement>,
|
|
|
|
element: NonDeleted<ExcalidrawLinearElement>,
|
|
|
|
startPoint: GlobalPoint | LocalPoint,
|
|
|
|
startPoint: P,
|
|
|
|
endPoint: GlobalPoint | LocalPoint,
|
|
|
|
endPoint: P,
|
|
|
|
zoom: AppState["zoom"],
|
|
|
|
index: number,
|
|
|
|
|
|
|
|
zoom: Zoom,
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
let distance = pointDistance(
|
|
|
|
if (isElbowArrow(element)) {
|
|
|
|
pointFrom(startPoint[0], startPoint[1]),
|
|
|
|
if (index >= 0 && index < element.points.length) {
|
|
|
|
pointFrom(endPoint[0], endPoint[1]),
|
|
|
|
return (
|
|
|
|
|
|
|
|
pointDistance(startPoint, endPoint) * zoom.value <
|
|
|
|
|
|
|
|
LinearElementEditor.POINT_HANDLE_SIZE / 2
|
|
|
|
);
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let distance = pointDistance(startPoint, endPoint);
|
|
|
|
if (element.points.length > 2 && element.roundness) {
|
|
|
|
if (element.points.length > 2 && element.roundness) {
|
|
|
|
distance = getBezierCurveLength(element, endPoint);
|
|
|
|
distance = getBezierCurveLength(element, endPoint);
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -748,12 +759,8 @@ export class LinearElementEditor {
|
|
|
|
segmentMidpoint,
|
|
|
|
segmentMidpoint,
|
|
|
|
elementsMap,
|
|
|
|
elementsMap,
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
} else if (event.altKey && appState.editingLinearElement) {
|
|
|
|
if (event.altKey && appState.editingLinearElement) {
|
|
|
|
if (linearElementEditor.lastUncommittedPoint == null) {
|
|
|
|
if (
|
|
|
|
|
|
|
|
linearElementEditor.lastUncommittedPoint == null &&
|
|
|
|
|
|
|
|
!isElbowArrow(element)
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
mutateElement(element, {
|
|
|
|
mutateElement(element, {
|
|
|
|
points: [
|
|
|
|
points: [
|
|
|
|
...element.points,
|
|
|
|
...element.points,
|
|
|
@ -909,12 +916,7 @@ export class LinearElementEditor {
|
|
|
|
|
|
|
|
|
|
|
|
if (!event.altKey) {
|
|
|
|
if (!event.altKey) {
|
|
|
|
if (lastPoint === lastUncommittedPoint) {
|
|
|
|
if (lastPoint === lastUncommittedPoint) {
|
|
|
|
LinearElementEditor.deletePoints(
|
|
|
|
LinearElementEditor.deletePoints(element, [points.length - 1]);
|
|
|
|
element,
|
|
|
|
|
|
|
|
[points.length - 1],
|
|
|
|
|
|
|
|
elementsMap,
|
|
|
|
|
|
|
|
app.state.zoom,
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
return {
|
|
|
|
...appState.editingLinearElement,
|
|
|
|
...appState.editingLinearElement,
|
|
|
@ -952,23 +954,14 @@ export class LinearElementEditor {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (lastPoint === lastUncommittedPoint) {
|
|
|
|
if (lastPoint === lastUncommittedPoint) {
|
|
|
|
LinearElementEditor.movePoints(
|
|
|
|
LinearElementEditor.movePoints(element, [
|
|
|
|
element,
|
|
|
|
|
|
|
|
[
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
index: element.points.length - 1,
|
|
|
|
index: element.points.length - 1,
|
|
|
|
point: newPoint,
|
|
|
|
point: newPoint,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
]);
|
|
|
|
elementsMap,
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
LinearElementEditor.addPoints(
|
|
|
|
LinearElementEditor.addPoints(element, [{ point: newPoint }]);
|
|
|
|
element,
|
|
|
|
|
|
|
|
[{ point: newPoint }],
|
|
|
|
|
|
|
|
elementsMap,
|
|
|
|
|
|
|
|
app.state.zoom,
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
return {
|
|
|
|
...appState.editingLinearElement,
|
|
|
|
...appState.editingLinearElement,
|
|
|
@ -1197,16 +1190,12 @@ export class LinearElementEditor {
|
|
|
|
// potentially expanding the bounding box
|
|
|
|
// potentially expanding the bounding box
|
|
|
|
if (pointAddedToEnd) {
|
|
|
|
if (pointAddedToEnd) {
|
|
|
|
const lastPoint = element.points[element.points.length - 1];
|
|
|
|
const lastPoint = element.points[element.points.length - 1];
|
|
|
|
LinearElementEditor.movePoints(
|
|
|
|
LinearElementEditor.movePoints(element, [
|
|
|
|
element,
|
|
|
|
|
|
|
|
[
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
index: element.points.length - 1,
|
|
|
|
index: element.points.length - 1,
|
|
|
|
point: pointFrom(lastPoint[0] + 30, lastPoint[1] + 30),
|
|
|
|
point: pointFrom(lastPoint[0] + 30, lastPoint[1] + 30),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
]);
|
|
|
|
elementsMap,
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
return {
|
|
|
@ -1221,8 +1210,6 @@ export class LinearElementEditor {
|
|
|
|
static deletePoints(
|
|
|
|
static deletePoints(
|
|
|
|
element: NonDeleted<ExcalidrawLinearElement>,
|
|
|
|
element: NonDeleted<ExcalidrawLinearElement>,
|
|
|
|
pointIndices: readonly number[],
|
|
|
|
pointIndices: readonly number[],
|
|
|
|
elementsMap: NonDeletedSceneElementsMap | SceneElementsMap,
|
|
|
|
|
|
|
|
zoom: AppState["zoom"],
|
|
|
|
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
let offsetX = 0;
|
|
|
|
let offsetX = 0;
|
|
|
|
let offsetY = 0;
|
|
|
|
let offsetY = 0;
|
|
|
@ -1252,47 +1239,27 @@ export class LinearElementEditor {
|
|
|
|
return acc;
|
|
|
|
return acc;
|
|
|
|
}, []);
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
|
|
LinearElementEditor._updatePoints(
|
|
|
|
LinearElementEditor._updatePoints(element, nextPoints, offsetX, offsetY);
|
|
|
|
element,
|
|
|
|
|
|
|
|
nextPoints,
|
|
|
|
|
|
|
|
offsetX,
|
|
|
|
|
|
|
|
offsetY,
|
|
|
|
|
|
|
|
elementsMap,
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static addPoints(
|
|
|
|
static addPoints(
|
|
|
|
element: NonDeleted<ExcalidrawLinearElement>,
|
|
|
|
element: NonDeleted<ExcalidrawLinearElement>,
|
|
|
|
targetPoints: { point: LocalPoint }[],
|
|
|
|
targetPoints: { point: LocalPoint }[],
|
|
|
|
elementsMap: NonDeletedSceneElementsMap | SceneElementsMap,
|
|
|
|
|
|
|
|
zoom: AppState["zoom"],
|
|
|
|
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
const offsetX = 0;
|
|
|
|
const offsetX = 0;
|
|
|
|
const offsetY = 0;
|
|
|
|
const offsetY = 0;
|
|
|
|
|
|
|
|
|
|
|
|
const nextPoints = [...element.points, ...targetPoints.map((x) => x.point)];
|
|
|
|
const nextPoints = [...element.points, ...targetPoints.map((x) => x.point)];
|
|
|
|
LinearElementEditor._updatePoints(
|
|
|
|
LinearElementEditor._updatePoints(element, nextPoints, offsetX, offsetY);
|
|
|
|
element,
|
|
|
|
|
|
|
|
nextPoints,
|
|
|
|
|
|
|
|
offsetX,
|
|
|
|
|
|
|
|
offsetY,
|
|
|
|
|
|
|
|
elementsMap,
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static movePoints(
|
|
|
|
static movePoints(
|
|
|
|
element: NonDeleted<ExcalidrawLinearElement>,
|
|
|
|
element: NonDeleted<ExcalidrawLinearElement>,
|
|
|
|
targetPoints: { index: number; point: LocalPoint; isDragging?: boolean }[],
|
|
|
|
targetPoints: { index: number; point: LocalPoint; isDragging?: boolean }[],
|
|
|
|
elementsMap: NonDeletedSceneElementsMap | SceneElementsMap,
|
|
|
|
|
|
|
|
otherUpdates?: {
|
|
|
|
otherUpdates?: {
|
|
|
|
startBinding?: PointBinding | null;
|
|
|
|
startBinding?: PointBinding | null;
|
|
|
|
endBinding?: PointBinding | null;
|
|
|
|
endBinding?: PointBinding | null;
|
|
|
|
},
|
|
|
|
},
|
|
|
|
options?: {
|
|
|
|
|
|
|
|
changedElements?: Map<string, OrderedExcalidrawElement>;
|
|
|
|
|
|
|
|
isDragging?: boolean;
|
|
|
|
|
|
|
|
zoom?: AppState["zoom"];
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
const { points } = element;
|
|
|
|
const { points } = element;
|
|
|
|
|
|
|
|
|
|
|
@ -1335,7 +1302,6 @@ export class LinearElementEditor {
|
|
|
|
nextPoints,
|
|
|
|
nextPoints,
|
|
|
|
offsetX,
|
|
|
|
offsetX,
|
|
|
|
offsetY,
|
|
|
|
offsetY,
|
|
|
|
elementsMap,
|
|
|
|
|
|
|
|
otherUpdates,
|
|
|
|
otherUpdates,
|
|
|
|
{
|
|
|
|
{
|
|
|
|
isDragging: targetPoints.reduce(
|
|
|
|
isDragging: targetPoints.reduce(
|
|
|
@ -1343,8 +1309,6 @@ export class LinearElementEditor {
|
|
|
|
dragging || targetPoint.isDragging === true,
|
|
|
|
dragging || targetPoint.isDragging === true,
|
|
|
|
false,
|
|
|
|
false,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
changedElements: options?.changedElements,
|
|
|
|
|
|
|
|
zoom: options?.zoom,
|
|
|
|
|
|
|
|
},
|
|
|
|
},
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -1451,54 +1415,49 @@ export class LinearElementEditor {
|
|
|
|
nextPoints: readonly LocalPoint[],
|
|
|
|
nextPoints: readonly LocalPoint[],
|
|
|
|
offsetX: number,
|
|
|
|
offsetX: number,
|
|
|
|
offsetY: number,
|
|
|
|
offsetY: number,
|
|
|
|
elementsMap: NonDeletedSceneElementsMap | SceneElementsMap,
|
|
|
|
|
|
|
|
otherUpdates?: {
|
|
|
|
otherUpdates?: {
|
|
|
|
startBinding?: PointBinding | null;
|
|
|
|
startBinding?: PointBinding | null;
|
|
|
|
endBinding?: PointBinding | null;
|
|
|
|
endBinding?: PointBinding | null;
|
|
|
|
},
|
|
|
|
},
|
|
|
|
options?: {
|
|
|
|
options?: {
|
|
|
|
changedElements?: Map<string, OrderedExcalidrawElement>;
|
|
|
|
|
|
|
|
isDragging?: boolean;
|
|
|
|
isDragging?: boolean;
|
|
|
|
zoom?: AppState["zoom"];
|
|
|
|
zoom?: AppState["zoom"];
|
|
|
|
},
|
|
|
|
},
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
if (isElbowArrow(element)) {
|
|
|
|
if (isElbowArrow(element)) {
|
|
|
|
const bindings: {
|
|
|
|
const updates: {
|
|
|
|
startBinding?: FixedPointBinding | null;
|
|
|
|
startBinding?: FixedPointBinding | null;
|
|
|
|
endBinding?: FixedPointBinding | null;
|
|
|
|
endBinding?: FixedPointBinding | null;
|
|
|
|
|
|
|
|
points?: LocalPoint[];
|
|
|
|
} = {};
|
|
|
|
} = {};
|
|
|
|
if (otherUpdates?.startBinding !== undefined) {
|
|
|
|
if (otherUpdates?.startBinding !== undefined) {
|
|
|
|
bindings.startBinding =
|
|
|
|
updates.startBinding =
|
|
|
|
otherUpdates.startBinding !== null &&
|
|
|
|
otherUpdates.startBinding !== null &&
|
|
|
|
isFixedPointBinding(otherUpdates.startBinding)
|
|
|
|
isFixedPointBinding(otherUpdates.startBinding)
|
|
|
|
? otherUpdates.startBinding
|
|
|
|
? otherUpdates.startBinding
|
|
|
|
: null;
|
|
|
|
: null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (otherUpdates?.endBinding !== undefined) {
|
|
|
|
if (otherUpdates?.endBinding !== undefined) {
|
|
|
|
bindings.endBinding =
|
|
|
|
updates.endBinding =
|
|
|
|
otherUpdates.endBinding !== null &&
|
|
|
|
otherUpdates.endBinding !== null &&
|
|
|
|
isFixedPointBinding(otherUpdates.endBinding)
|
|
|
|
isFixedPointBinding(otherUpdates.endBinding)
|
|
|
|
? otherUpdates.endBinding
|
|
|
|
? otherUpdates.endBinding
|
|
|
|
: null;
|
|
|
|
: null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const mergedElementsMap = options?.changedElements
|
|
|
|
updates.points = Array.from(nextPoints);
|
|
|
|
? toBrandedType<SceneElementsMap>(
|
|
|
|
updates.points[0] = pointTranslate(
|
|
|
|
new Map([...elementsMap, ...options.changedElements]),
|
|
|
|
updates.points[0],
|
|
|
|
)
|
|
|
|
|
|
|
|
: elementsMap;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mutateElbowArrow(
|
|
|
|
|
|
|
|
element,
|
|
|
|
|
|
|
|
mergedElementsMap,
|
|
|
|
|
|
|
|
nextPoints,
|
|
|
|
|
|
|
|
vector(offsetX, offsetY),
|
|
|
|
vector(offsetX, offsetY),
|
|
|
|
bindings,
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
isDragging: options?.isDragging,
|
|
|
|
|
|
|
|
zoom: options?.zoom,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
);
|
|
|
|
);
|
|
|
|
|
|
|
|
updates.points[updates.points.length - 1] = pointTranslate(
|
|
|
|
|
|
|
|
updates.points[updates.points.length - 1],
|
|
|
|
|
|
|
|
vector(offsetX, offsetY),
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mutateElement(element, updates, true, {
|
|
|
|
|
|
|
|
isDragging: options?.isDragging,
|
|
|
|
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
const nextCoords = getElementPointsCoords(element, nextPoints);
|
|
|
|
const nextCoords = getElementPointsCoords(element, nextPoints);
|
|
|
|
const prevCoords = getElementPointsCoords(element, element.points);
|
|
|
|
const prevCoords = getElementPointsCoords(element, element.points);
|
|
|
@ -1773,6 +1732,99 @@ export class LinearElementEditor {
|
|
|
|
|
|
|
|
|
|
|
|
return coords;
|
|
|
|
return coords;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static moveFixedSegment(
|
|
|
|
|
|
|
|
linearElement: LinearElementEditor,
|
|
|
|
|
|
|
|
index: number,
|
|
|
|
|
|
|
|
x: number,
|
|
|
|
|
|
|
|
y: number,
|
|
|
|
|
|
|
|
elementsMap: ElementsMap,
|
|
|
|
|
|
|
|
): LinearElementEditor {
|
|
|
|
|
|
|
|
const element = LinearElementEditor.getElement(
|
|
|
|
|
|
|
|
linearElement.elementId,
|
|
|
|
|
|
|
|
elementsMap,
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!element || !isElbowArrow(element)) {
|
|
|
|
|
|
|
|
return linearElement;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (index && index > 0 && index < element.points.length) {
|
|
|
|
|
|
|
|
const isHorizontal = headingIsHorizontal(
|
|
|
|
|
|
|
|
vectorToHeading(
|
|
|
|
|
|
|
|
vectorFromPoint(element.points[index], element.points[index - 1]),
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const fixedSegments = (element.fixedSegments ?? []).reduce(
|
|
|
|
|
|
|
|
(segments, s) => {
|
|
|
|
|
|
|
|
segments[s.index] = s;
|
|
|
|
|
|
|
|
return segments;
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{} as Record<number, FixedSegment>,
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
fixedSegments[index] = {
|
|
|
|
|
|
|
|
index,
|
|
|
|
|
|
|
|
start: pointFrom<LocalPoint>(
|
|
|
|
|
|
|
|
!isHorizontal ? x - element.x : element.points[index - 1][0],
|
|
|
|
|
|
|
|
isHorizontal ? y - element.y : element.points[index - 1][1],
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
end: pointFrom<LocalPoint>(
|
|
|
|
|
|
|
|
!isHorizontal ? x - element.x : element.points[index][0],
|
|
|
|
|
|
|
|
isHorizontal ? y - element.y : element.points[index][1],
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
const nextFixedSegments = Object.values(fixedSegments).sort(
|
|
|
|
|
|
|
|
(a, b) => a.index - b.index,
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const offset = nextFixedSegments
|
|
|
|
|
|
|
|
.map((segment) => segment.index)
|
|
|
|
|
|
|
|
.reduce((count, idx) => (idx < index ? count + 1 : count), 0);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mutateElement(element, {
|
|
|
|
|
|
|
|
fixedSegments: nextFixedSegments,
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const point = pointFrom<GlobalPoint>(
|
|
|
|
|
|
|
|
element.x +
|
|
|
|
|
|
|
|
(element.fixedSegments![offset].start[0] +
|
|
|
|
|
|
|
|
element.fixedSegments![offset].end[0]) /
|
|
|
|
|
|
|
|
2,
|
|
|
|
|
|
|
|
element.y +
|
|
|
|
|
|
|
|
(element.fixedSegments![offset].start[1] +
|
|
|
|
|
|
|
|
element.fixedSegments![offset].end[1]) /
|
|
|
|
|
|
|
|
2,
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
|
|
...linearElement,
|
|
|
|
|
|
|
|
segmentMidPointHoveredCoords: point,
|
|
|
|
|
|
|
|
pointerDownState: {
|
|
|
|
|
|
|
|
...linearElement.pointerDownState,
|
|
|
|
|
|
|
|
segmentMidpoint: {
|
|
|
|
|
|
|
|
added: false,
|
|
|
|
|
|
|
|
index: element.fixedSegments![offset].index,
|
|
|
|
|
|
|
|
value: point,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return linearElement;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static deleteFixedSegment(
|
|
|
|
|
|
|
|
element: ExcalidrawElbowArrowElement,
|
|
|
|
|
|
|
|
index: number,
|
|
|
|
|
|
|
|
): void {
|
|
|
|
|
|
|
|
mutateElement(element, {
|
|
|
|
|
|
|
|
fixedSegments: element.fixedSegments?.filter(
|
|
|
|
|
|
|
|
(segment) => segment.index !== index,
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
mutateElement(element, {}, true);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const normalizeSelectedPoints = (
|
|
|
|
const normalizeSelectedPoints = (
|
|
|
|