chore: Refactor and remove scene from elbow arrow generation (#8342)

* Refactor and remove scene from elbow arrow generation
This commit is contained in:
Márk Tolmács 2024-08-08 14:06:26 +02:00 committed by GitHub
parent 72d6ee48fc
commit dd1370381d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 115 additions and 156 deletions

View file

@ -25,6 +25,7 @@ const deleteSelectedElements = (
appState: AppState,
app: AppClassProperties,
) => {
const elementsMap = app.scene.getNonDeletedElementsMap();
const framesToBeDeleted = new Set(
getSelectedElements(
elements.filter((el) => isFrameLikeElement(el)),
@ -51,7 +52,7 @@ const deleteSelectedElements = (
? null
: bound.endBinding,
});
mutateElbowArrow(bound, app.scene, bound.points);
mutateElbowArrow(bound, elementsMap, bound.points);
}
});
}
@ -159,7 +160,7 @@ export const actionDeleteSelected = register({
LinearElementEditor.deletePoints(
element,
selectedPointsIndices,
app.scene,
elementsMap,
);
return {

View file

@ -44,7 +44,7 @@ export const actionDuplicateSelection = register({
if (appState.editingLinearElement) {
const ret = LinearElementEditor.duplicateSelectedPoints(
appState,
app.scene,
app.scene.getNonDeletedElementsMap(),
);
if (!ret) {

View file

@ -120,7 +120,6 @@ const flipElements = (
true,
flipDirection === "horizontal" ? maxX : minX,
flipDirection === "horizontal" ? minY : maxY,
app.scene,
);
bindOrUnbindLinearElements(

View file

@ -58,7 +58,6 @@ export const createUndoAction: ActionCreator = (history, store) => ({
arrayToMap(elements) as SceneElementsMap, // TODO: #7348 refactor action manager to already include `SceneElementsMap`
appState,
store.snapshot,
app.scene,
),
),
keyTest: (event) =>
@ -100,7 +99,6 @@ export const createRedoAction: ActionCreator = (history, store) => ({
arrayToMap(elements) as SceneElementsMap, // TODO: #7348 refactor action manager to already include `SceneElementsMap`
appState,
store.snapshot,
app.scene,
),
),
keyTest: (event) =>

View file

@ -1648,7 +1648,7 @@ export const actionChangeArrowType = register({
mutateElbowArrow(
newElement,
app.scene,
elementsMap,
[finalStartPoint, finalEndPoint].map(
(point) =>
[point[0] - newElement.x, point[1] - newElement.y] as Point,

View file

@ -29,7 +29,6 @@ import type {
} from "./element/types";
import { orderByFractionalIndex, syncMovedIndices } from "./fractionalIndex";
import { getNonDeletedGroupIds } from "./groups";
import type Scene from "./scene/Scene";
import { getObservedAppState } from "./store";
import type {
AppState,
@ -1054,7 +1053,6 @@ export class ElementsChange implements Change<SceneElementsMap> {
public applyTo(
elements: SceneElementsMap,
snapshot: Map<string, OrderedExcalidrawElement>,
scene: Scene,
): [SceneElementsMap, boolean] {
let nextElements = toBrandedType<SceneElementsMap>(new Map(elements));
let changedElements: Map<string, OrderedExcalidrawElement>;
@ -1102,7 +1100,6 @@ export class ElementsChange implements Change<SceneElementsMap> {
try {
// TODO: #7348 refactor away mutations below, so that we couldn't end up in an incosistent state
ElementsChange.redrawTextBoundingBoxes(nextElements, changedElements);
ElementsChange.redrawBoundArrows(nextElements, changedElements, scene);
// the following reorder performs also mutations, but only on new instances of changed elements
// (unless something goes really bad and it fallbacks to fixing all invalid indices)
@ -1111,6 +1108,9 @@ export class ElementsChange implements Change<SceneElementsMap> {
changedElements,
flags,
);
// Need ordered nextElements to avoid z-index binding issues
ElementsChange.redrawBoundArrows(nextElements, changedElements);
} catch (e) {
console.error(
`Couldn't mutate elements after applying elements change`,
@ -1459,11 +1459,10 @@ export class ElementsChange implements Change<SceneElementsMap> {
private static redrawBoundArrows(
elements: SceneElementsMap,
changed: Map<string, OrderedExcalidrawElement>,
scene: Scene,
) {
for (const element of changed.values()) {
if (!element.isDeleted && isBindableElement(element)) {
updateBoundElements(element, elements, scene, {
updateBoundElements(element, elements, {
changedElements: changed,
});
}

View file

@ -4005,14 +4005,9 @@ class App extends React.Component<AppProps, AppState> {
y: element.y + offsetY,
});
updateBoundElements(
element,
this.scene.getNonDeletedElementsMap(),
this.scene,
{
simultaneouslyUpdated: selectedElements,
},
);
updateBoundElements(element, this.scene.getNonDeletedElementsMap(), {
simultaneouslyUpdated: selectedElements,
});
});
this.setState({
@ -4469,7 +4464,7 @@ class App extends React.Component<AppProps, AppState> {
onChange: withBatchedUpdates((nextOriginalText) => {
updateElement(nextOriginalText, false);
if (isNonDeletedElement(element)) {
updateBoundElements(element, elementsMap, this.scene);
updateBoundElements(element, this.scene.getNonDeletedElementsMap());
}
}),
onSubmit: withBatchedUpdates(({ viaKeyboard, nextOriginalText }) => {
@ -5279,7 +5274,7 @@ class App extends React.Component<AppProps, AppState> {
scenePointerX,
scenePointerY,
this.state,
this.scene,
this.scene.getNonDeletedElementsMap(),
);
if (
@ -5395,7 +5390,7 @@ class App extends React.Component<AppProps, AppState> {
if (isElbowArrow(multiElement)) {
mutateElbowArrow(
multiElement,
this.scene,
this.scene.getNonDeletedElementsMap(),
[
...points.slice(0, -1),
[
@ -7771,7 +7766,7 @@ class App extends React.Component<AppProps, AppState> {
} else if (points.length > 1 && isElbowArrow(newElement)) {
mutateElbowArrow(
newElement,
this.scene,
elementsMap,
[...points.slice(0, -1), [dx, dy]],
[0, 0],
undefined,
@ -9756,7 +9751,6 @@ class App extends React.Component<AppProps, AppState> {
resizeY,
pointerDownState.resize.center.x,
pointerDownState.resize.center.y,
this.scene,
)
) {
const suggestedBindings = getSuggestedBindingsForArrows(

View file

@ -66,7 +66,6 @@ const resizeElementInGroup = (
origElement: ExcalidrawElement,
elementsMap: NonDeletedSceneElementsMap,
originalElementsMap: ElementsMap,
scene: Scene,
) => {
const updates = getResizedUpdates(anchorX, anchorY, scale, origElement);
const { width: oldWidth, height: oldHeight } = latestElement;
@ -78,7 +77,7 @@ const resizeElementInGroup = (
);
if (boundTextElement) {
const newFontSize = boundTextElement.fontSize * scale;
updateBoundElements(latestElement, elementsMap, scene, {
updateBoundElements(latestElement, elementsMap, {
oldSize: { width: oldWidth, height: oldHeight },
});
const latestBoundTextElement = elementsMap.get(boundTextElement.id);
@ -111,7 +110,6 @@ const resizeGroup = (
originalElements: ExcalidrawElement[],
elementsMap: NonDeletedSceneElementsMap,
originalElementsMap: ElementsMap,
scene: Scene,
) => {
// keep aspect ratio for groups
if (property === "width") {
@ -135,7 +133,6 @@ const resizeGroup = (
origElement,
elementsMap,
originalElementsMap,
scene,
);
}
};
@ -190,7 +187,6 @@ const handleDimensionChange: DragInputCallbackType<
originalElements,
elementsMap,
originalElementsMap,
scene,
);
} else {
const [el] = elementsInUnit;
@ -296,7 +292,6 @@ const handleDimensionChange: DragInputCallbackType<
originalElements,
elementsMap,
originalElementsMap,
scene,
);
} else {
const [el] = elementsInUnit;

View file

@ -198,7 +198,7 @@ export const resizeElement = (
}
}
updateBoundElements(latestElement, elementsMap, scene, {
updateBoundElements(latestElement, elementsMap, {
oldSize: { width: oldWidth, height: oldHeight },
});
@ -316,6 +316,6 @@ export const updateBindings = (
[],
);
} else {
updateBoundElements(latestElement, elementsMap, scene, options);
updateBoundElements(latestElement, elementsMap, options);
}
};

View file

@ -25,6 +25,7 @@ import type {
OrderedExcalidrawElement,
ExcalidrawElbowArrowElement,
FixedPoint,
SceneElementsMap,
} from "./types";
import type { Bounds } from "./bounds";
@ -124,7 +125,6 @@ export const bindOrUnbindLinearElement = (
boundToElementIds,
unboundFromElementIds,
elementsMap,
scene,
);
bindOrUnbindLinearElementEdge(
linearElement,
@ -134,7 +134,6 @@ export const bindOrUnbindLinearElement = (
boundToElementIds,
unboundFromElementIds,
elementsMap,
scene,
);
const onlyUnbound = Array.from(unboundFromElementIds).filter(
@ -161,7 +160,6 @@ const bindOrUnbindLinearElementEdge = (
// Is mutated
unboundFromElementIds: Set<ExcalidrawBindableElement["id"]>,
elementsMap: NonDeletedSceneElementsMap,
scene: Scene,
): void => {
// "keep" is for method chaining convenience, a "no-op", so just bail out
if (bindableElement === "keep") {
@ -571,8 +569,7 @@ const calculateFocusAndGap = (
// in explicitly.
export const updateBoundElements = (
changedElement: NonDeletedExcalidrawElement,
elementsMap: ElementsMap,
scene: Scene,
elementsMap: NonDeletedSceneElementsMap | SceneElementsMap,
options?: {
simultaneouslyUpdated?: readonly ExcalidrawElement[];
oldSize?: { width: number; height: number };
@ -658,7 +655,7 @@ export const updateBoundElements = (
LinearElementEditor.movePoints(
element,
updates,
scene,
elementsMap,
{
...(changedElement.id === element.startBinding?.elementId
? { startBinding: bindings.startBinding }

View file

@ -91,14 +91,9 @@ export const dragSelectedElements = (
updateElementCoords(pointerDownState, textElement, adjustedOffset);
}
}
updateBoundElements(
element,
scene.getElementsMapIncludingDeleted(),
scene,
{
simultaneouslyUpdated: Array.from(elementsToUpdate),
},
);
updateBoundElements(element, scene.getElementsMapIncludingDeleted(), {
simultaneouslyUpdated: Array.from(elementsToUpdate),
});
});
};

View file

@ -9,6 +9,7 @@ import type {
NonDeletedSceneElementsMap,
OrderedExcalidrawElement,
FixedPointBinding,
SceneElementsMap,
} from "./types";
import {
distance2d,
@ -290,7 +291,7 @@ export class LinearElementEditor {
isDragging: selectedIndex === lastClickedPoint,
},
],
scene,
elementsMap,
);
} else {
const newDraggingPointPosition = LinearElementEditor.createPointAt(
@ -326,7 +327,7 @@ export class LinearElementEditor {
isDragging: pointIndex === lastClickedPoint,
};
}),
scene,
elementsMap,
);
}
@ -420,7 +421,7 @@ export class LinearElementEditor {
: element.points[0],
},
],
scene,
elementsMap,
);
}
@ -876,13 +877,12 @@ export class LinearElementEditor {
scenePointerX: number,
scenePointerY: number,
appState: AppState,
scene: Scene,
elementsMap: NonDeletedSceneElementsMap | SceneElementsMap,
): LinearElementEditor | null {
if (!appState.editingLinearElement) {
return null;
}
const { elementId, lastUncommittedPoint } = appState.editingLinearElement;
const elementsMap = scene.getNonDeletedElementsMap();
const element = LinearElementEditor.getElement(elementId, elementsMap);
if (!element) {
return appState.editingLinearElement;
@ -893,7 +893,11 @@ export class LinearElementEditor {
if (!event.altKey) {
if (lastPoint === lastUncommittedPoint) {
LinearElementEditor.deletePoints(element, [points.length - 1], scene);
LinearElementEditor.deletePoints(
element,
[points.length - 1],
elementsMap,
);
}
return {
...appState.editingLinearElement,
@ -939,14 +943,13 @@ export class LinearElementEditor {
point: newPoint,
},
],
scene,
elementsMap,
);
} else {
LinearElementEditor.addPoints(
element,
appState,
[{ point: newPoint }],
scene,
elementsMap,
);
}
return {
@ -1091,7 +1094,7 @@ export class LinearElementEditor {
const offsetY = points[0][1];
return {
points: points.map((point, _idx) => {
points: points.map((point) => {
return [point[0] - offsetX, point[1] - offsetY] as const;
}),
x: element.x + offsetX,
@ -1106,13 +1109,15 @@ export class LinearElementEditor {
mutateElement(element, LinearElementEditor.getNormalizedPoints(element));
}
static duplicateSelectedPoints(appState: AppState, scene: Scene) {
static duplicateSelectedPoints(
appState: AppState,
elementsMap: NonDeletedSceneElementsMap | SceneElementsMap,
) {
if (!appState.editingLinearElement) {
return false;
}
const { selectedPointsIndices, elementId } = appState.editingLinearElement;
const elementsMap = scene.getNonDeletedElementsMap();
const element = LinearElementEditor.getElement(elementId, elementsMap);
if (!element || selectedPointsIndices === null) {
@ -1163,7 +1168,7 @@ export class LinearElementEditor {
point: [lastPoint[0] + 30, lastPoint[1] + 30],
},
],
scene,
elementsMap,
);
}
@ -1181,7 +1186,7 @@ export class LinearElementEditor {
static deletePoints(
element: NonDeleted<ExcalidrawLinearElement>,
pointIndices: readonly number[],
scene: Scene,
elementsMap: NonDeletedSceneElementsMap | SceneElementsMap,
) {
let offsetX = 0;
let offsetY = 0;
@ -1214,15 +1219,14 @@ export class LinearElementEditor {
nextPoints,
offsetX,
offsetY,
scene,
elementsMap,
);
}
static addPoints(
element: NonDeleted<ExcalidrawLinearElement>,
appState: AppState,
targetPoints: { point: Point }[],
scene: Scene,
elementsMap: NonDeletedSceneElementsMap | SceneElementsMap,
) {
const offsetX = 0;
const offsetY = 0;
@ -1233,14 +1237,14 @@ export class LinearElementEditor {
nextPoints,
offsetX,
offsetY,
scene,
elementsMap,
);
}
static movePoints(
element: NonDeleted<ExcalidrawLinearElement>,
targetPoints: { index: number; point: Point; isDragging?: boolean }[],
scene: Scene,
elementsMap: NonDeletedSceneElementsMap | SceneElementsMap,
otherUpdates?: {
startBinding?: PointBinding | null;
endBinding?: PointBinding | null;
@ -1296,7 +1300,7 @@ export class LinearElementEditor {
nextPoints,
offsetX,
offsetY,
scene,
elementsMap,
otherUpdates,
{
isDragging: targetPoints.reduce(
@ -1413,7 +1417,7 @@ export class LinearElementEditor {
nextPoints: readonly Point[],
offsetX: number,
offsetY: number,
scene: Scene,
elementsMap: NonDeletedSceneElementsMap | SceneElementsMap,
otherUpdates?: {
startBinding?: PointBinding | null;
endBinding?: PointBinding | null;
@ -1445,7 +1449,7 @@ export class LinearElementEditor {
mutateElbowArrow(
element,
scene,
elementsMap,
nextPoints,
[offsetX, offsetY],
bindings,

View file

@ -11,6 +11,8 @@ import type {
ExcalidrawTextElementWithContainer,
ExcalidrawImageElement,
ElementsMap,
NonDeletedSceneElementsMap,
SceneElementsMap,
} from "./types";
import type { Mutable } from "../utility-types";
import {
@ -69,7 +71,7 @@ export const transformElements = (
originalElements: PointerDownState["originalElements"],
transformHandleType: MaybeTransformHandleType,
selectedElements: readonly NonDeletedExcalidrawElement[],
elementsMap: ElementsMap,
elementsMap: SceneElementsMap,
shouldRotateWithDiscreteAngle: boolean,
shouldResizeFromCenter: boolean,
shouldMaintainAspectRatio: boolean,
@ -77,7 +79,6 @@ export const transformElements = (
pointerY: number,
centerX: number,
centerY: number,
scene: Scene,
) => {
if (selectedElements.length === 1) {
const [element] = selectedElements;
@ -90,7 +91,7 @@ export const transformElements = (
pointerY,
shouldRotateWithDiscreteAngle,
);
updateBoundElements(element, elementsMap, scene);
updateBoundElements(element, elementsMap);
}
} else if (isTextElement(element) && transformHandleType) {
resizeSingleTextElement(
@ -102,7 +103,7 @@ export const transformElements = (
pointerX,
pointerY,
);
updateBoundElements(element, elementsMap, scene);
updateBoundElements(element, elementsMap);
} else if (transformHandleType) {
resizeSingleElement(
originalElements,
@ -113,7 +114,6 @@ export const transformElements = (
shouldResizeFromCenter,
pointerX,
pointerY,
scene,
);
}
@ -129,7 +129,6 @@ export const transformElements = (
shouldRotateWithDiscreteAngle,
centerX,
centerY,
scene,
);
return true;
} else if (transformHandleType) {
@ -142,7 +141,6 @@ export const transformElements = (
shouldMaintainAspectRatio,
pointerX,
pointerY,
scene,
);
return true;
}
@ -434,12 +432,11 @@ export const resizeSingleElement = (
originalElements: PointerDownState["originalElements"],
shouldMaintainAspectRatio: boolean,
element: NonDeletedExcalidrawElement,
elementsMap: ElementsMap,
elementsMap: SceneElementsMap,
transformHandleDirection: TransformHandleDirection,
shouldResizeFromCenter: boolean,
pointerX: number,
pointerY: number,
scene: Scene,
) => {
const stateAtResizeStart = originalElements.get(element.id)!;
// Gets bounds corners
@ -710,7 +707,7 @@ export const resizeSingleElement = (
) {
mutateElement(element, resizedElement);
updateBoundElements(element, elementsMap, scene, {
updateBoundElements(element, elementsMap, {
oldSize: {
width: stateAtResizeStart.width,
height: stateAtResizeStart.height,
@ -734,13 +731,12 @@ export const resizeSingleElement = (
export const resizeMultipleElements = (
originalElements: PointerDownState["originalElements"],
selectedElements: readonly NonDeletedExcalidrawElement[],
elementsMap: ElementsMap,
elementsMap: NonDeletedSceneElementsMap | SceneElementsMap,
transformHandleType: TransformHandleDirection,
shouldResizeFromCenter: boolean,
shouldMaintainAspectRatio: boolean,
pointerX: number,
pointerY: number,
scene: Scene,
) => {
// map selected elements to the original elements. While it never should
// happen that pointerDownState.originalElements won't contain the selected
@ -974,12 +970,19 @@ export const resizeMultipleElements = (
mutateElement(element, update, false);
if (isArrowElement(element) && isElbowArrow(element)) {
mutateElbowArrow(element, scene, element.points, undefined, undefined, {
informMutation: false,
});
mutateElbowArrow(
element,
elementsMap,
element.points,
undefined,
undefined,
{
informMutation: false,
},
);
}
updateBoundElements(element, elementsMap, scene, {
updateBoundElements(element, elementsMap, {
simultaneouslyUpdated: elementsToUpdate,
oldSize: { width: oldWidth, height: oldHeight },
});
@ -1004,13 +1007,12 @@ export const resizeMultipleElements = (
const rotateMultipleElements = (
originalElements: PointerDownState["originalElements"],
elements: readonly NonDeletedExcalidrawElement[],
elementsMap: ElementsMap,
elementsMap: SceneElementsMap,
pointerX: number,
pointerY: number,
shouldRotateWithDiscreteAngle: boolean,
centerX: number,
centerY: number,
scene: Scene,
) => {
let centerAngle =
(5 * Math.PI) / 2 + Math.atan2(pointerY - centerY, pointerX - centerX);
@ -1037,7 +1039,7 @@ const rotateMultipleElements = (
if (isArrowElement(element) && isElbowArrow(element)) {
const points = getArrowLocalFixedPoints(element, elementsMap);
mutateElbowArrow(element, scene, points);
mutateElbowArrow(element, elementsMap, points);
} else {
mutateElement(
element,
@ -1050,7 +1052,7 @@ const rotateMultipleElements = (
);
}
updateBoundElements(element, elementsMap, scene, {
updateBoundElements(element, elementsMap, {
simultaneouslyUpdated: elements,
});

View file

@ -45,7 +45,7 @@ describe("elbow arrow routing", () => {
elbowed: true,
}) as ExcalidrawElbowArrowElement;
scene.insertElement(arrow);
mutateElbowArrow(arrow, scene, [
mutateElbowArrow(arrow, scene.getNonDeletedElementsMap(), [
[-45 - arrow.x, -100.1 - arrow.y],
[45 - arrow.x, 99.9 - arrow.y],
]);
@ -98,7 +98,7 @@ describe("elbow arrow routing", () => {
expect(arrow.startBinding).not.toBe(null);
expect(arrow.endBinding).not.toBe(null);
mutateElbowArrow(arrow, scene, [
mutateElbowArrow(arrow, elementsMap, [
[0, 0],
[90, 200],
]);

View file

@ -10,7 +10,6 @@ import {
translatePoint,
} from "../math";
import { getSizeFromPoints } from "../points";
import type Scene from "../scene/Scene";
import type { Point } from "../types";
import { isAnyTrue, toBrandedType, tupleToCoors } from "../utils";
import {
@ -37,14 +36,10 @@ import { isBindableElement, isRectanguloidElement } from "./typeChecks";
import type {
ExcalidrawElbowArrowElement,
FixedPointBinding,
NonDeletedExcalidrawElement,
NonDeletedSceneElementsMap,
SceneElementsMap,
} from "./types";
import type {
ElementsMap,
ExcalidrawBindableElement,
OrderedExcalidrawElement,
} from "./types";
import type { ElementsMap, ExcalidrawBindableElement } from "./types";
type Node = {
f: number;
@ -67,7 +62,7 @@ const BASE_PADDING = 40;
export const mutateElbowArrow = (
arrow: ExcalidrawElbowArrowElement,
scene: Scene,
elementsMap: NonDeletedSceneElementsMap | SceneElementsMap,
nextPoints: readonly Point[],
offset?: Point,
otherUpdates?: {
@ -75,15 +70,11 @@ export const mutateElbowArrow = (
endBinding?: FixedPointBinding | null;
},
options?: {
changedElements?: Map<string, OrderedExcalidrawElement>;
isDragging?: boolean;
disableBinding?: boolean;
informMutation?: boolean;
},
) => {
const elements = getAllElements(scene, options?.changedElements);
const elementsMap = getAllElementsMap(scene, options?.changedElements);
const origStartGlobalPoint = translatePoint(nextPoints[0], [
arrow.x + (offset ? offset[0] : 0),
arrow.y + (offset ? offset[1] : 0),
@ -99,22 +90,9 @@ export const mutateElbowArrow = (
const endElement =
arrow.endBinding &&
getBindableElementForId(arrow.endBinding.elementId, elementsMap);
const hoveredStartElement = options?.isDragging
? getHoveredElementForBinding(
tupleToCoors(origStartGlobalPoint),
elements,
elementsMap,
true,
)
: startElement;
const hoveredEndElement = options?.isDragging
? getHoveredElementForBinding(
tupleToCoors(origEndGlobalPoint),
elements,
elementsMap,
true,
)
: endElement;
const [hoveredStartElement, hoveredEndElement] = options?.isDragging
? getHoveredElements(origStartGlobalPoint, origEndGlobalPoint, elementsMap)
: [startElement, endElement];
const startGlobalPoint = getGlobalPoint(
arrow.startBinding?.fixedPoint,
origStartGlobalPoint,
@ -895,7 +873,7 @@ const normalizedArrowElementUpdate = (
const offsetY = global[0][1];
const points = global.map(
(point, _idx) => [point[0] - offsetX, point[1] - offsetY] as const,
(point) => [point[0] - offsetX, point[1] - offsetY] as const,
);
return {
@ -935,32 +913,11 @@ const neighborIndexToHeading = (idx: number): Heading => {
return HEADING_LEFT;
};
const getAllElementsMap = (
scene: Scene,
changedElements?: Map<string, OrderedExcalidrawElement>,
): NonDeletedSceneElementsMap =>
changedElements
? toBrandedType<NonDeletedSceneElementsMap>(
new Map([...scene.getNonDeletedElementsMap(), ...changedElements]),
)
: scene.getNonDeletedElementsMap();
const getAllElements = (
scene: Scene,
changedElements?: Map<string, OrderedExcalidrawElement>,
): readonly NonDeletedExcalidrawElement[] =>
changedElements
? ([
...scene.getNonDeletedElements(),
...[...changedElements].map(([_, value]) => value),
] as NonDeletedExcalidrawElement[])
: scene.getNonDeletedElements();
const getGlobalPoint = (
fixedPointRatio: [number, number] | undefined | null,
initialPoint: Point,
otherPoint: Point,
elementsMap: NonDeletedSceneElementsMap,
elementsMap: NonDeletedSceneElementsMap | SceneElementsMap,
boundElement?: ExcalidrawBindableElement | null,
hoveredElement?: ExcalidrawBindableElement | null,
isDragging?: boolean,
@ -1016,7 +973,7 @@ const getSnapPoint = (
const getBindPointHeading = (
point: Point,
otherPoint: Point,
elementsMap: NonDeletedSceneElementsMap,
elementsMap: NonDeletedSceneElementsMap | SceneElementsMap,
hoveredElement: ExcalidrawBindableElement | null | undefined,
origPoint: Point,
) =>
@ -1034,3 +991,30 @@ const getBindPointHeading = (
elementsMap,
origPoint,
);
const getHoveredElements = (
origStartGlobalPoint: Point,
origEndGlobalPoint: Point,
elementsMap: NonDeletedSceneElementsMap | SceneElementsMap,
) => {
// TODO: Might be a performance bottleneck and the Map type
// remembers the insertion order anyway...
const nonDeletedSceneElementsMap = toBrandedType<NonDeletedSceneElementsMap>(
new Map([...elementsMap].filter((el) => !el[1].isDeleted)),
);
const elements = Array.from(elementsMap.values());
return [
getHoveredElementForBinding(
tupleToCoors(origStartGlobalPoint),
elements,
nonDeletedSceneElementsMap,
true,
),
getHoveredElementForBinding(
tupleToCoors(origEndGlobalPoint),
elements,
nonDeletedSceneElementsMap,
true,
),
];
};

View file

@ -1,7 +1,6 @@
import type { AppStateChange, ElementsChange } from "./change";
import type { SceneElementsMap } from "./element/types";
import { Emitter } from "./emitter";
import type Scene from "./scene/Scene";
import type { Snapshot } from "./store";
import type { AppState } from "./types";
@ -65,7 +64,6 @@ export class History {
elements: SceneElementsMap,
appState: AppState,
snapshot: Readonly<Snapshot>,
scene: Scene,
) {
return this.perform(
elements,
@ -73,7 +71,6 @@ export class History {
snapshot,
() => History.pop(this.undoStack),
(entry: HistoryEntry) => History.push(this.redoStack, entry, elements),
scene,
);
}
@ -81,7 +78,6 @@ export class History {
elements: SceneElementsMap,
appState: AppState,
snapshot: Readonly<Snapshot>,
scene: Scene,
) {
return this.perform(
elements,
@ -89,7 +85,6 @@ export class History {
snapshot,
() => History.pop(this.redoStack),
(entry: HistoryEntry) => History.push(this.undoStack, entry, elements),
scene,
);
}
@ -99,7 +94,6 @@ export class History {
snapshot: Readonly<Snapshot>,
pop: () => HistoryEntry | null,
push: (entry: HistoryEntry) => void,
scene: Scene,
): [SceneElementsMap, AppState] | void {
try {
let historyEntry = pop();
@ -116,7 +110,7 @@ export class History {
while (historyEntry) {
try {
[nextElements, nextAppState, containsVisibleChange] =
historyEntry.applyTo(nextElements, nextAppState, snapshot, scene);
historyEntry.applyTo(nextElements, nextAppState, snapshot);
} finally {
// make sure to always push / pop, even if the increment is corrupted
push(historyEntry);
@ -187,10 +181,9 @@ export class HistoryEntry {
elements: SceneElementsMap,
appState: AppState,
snapshot: Readonly<Snapshot>,
scene: Scene,
): [SceneElementsMap, AppState, boolean] {
const [nextElements, elementsContainVisibleChange] =
this.elementsChange.applyTo(elements, snapshot.elements, scene);
this.elementsChange.applyTo(elements, snapshot.elements);
const [nextAppState, appStateContainsVisibleChange] =
this.appStateChange.applyTo(appState, nextElements);

View file

@ -44,7 +44,6 @@ import { queryByText } from "@testing-library/react";
import { HistoryEntry } from "../history";
import { AppStateChange, ElementsChange } from "../change";
import { Snapshot, StoreAction } from "../store";
import type Scene from "../scene/Scene";
const { h } = window;
@ -118,7 +117,6 @@ describe("history", () => {
arrayToMap(h.elements) as SceneElementsMap,
appState,
Snapshot.empty(),
{} as Scene,
) as any,
);
} catch (e) {
@ -140,7 +138,6 @@ describe("history", () => {
arrayToMap(h.elements) as SceneElementsMap,
appState,
Snapshot.empty(),
{} as Scene,
) as any,
);
} catch (e) {

View file

@ -5,6 +5,7 @@ import type {
ExcalidrawLinearElement,
ExcalidrawTextElementWithContainer,
FontString,
SceneElementsMap,
} from "../element/types";
import { Excalidraw, mutateElement } from "../index";
import { centerPoint } from "../math";
@ -1344,7 +1345,7 @@ describe("Test Linear Elements", () => {
],
},
],
h.scene,
new Map() as SceneElementsMap,
);
});
expect(line.x).toBe(origStartX + 10);