feat(editor): visualize binding midpoints + support for simple arrows (#10611)
* feat: Force exact center focus point When the projected point is close to center snap it to the exact center. Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Tests Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Snap to center around side mid point. Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * Trigger CI * fix: Midpoint outline focus point Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Tests Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Dragging existing arrow reset focus point on outline Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Tests Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * feat: Midpoint indicator Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Rotated mid points Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: No hole Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * feat: Cache hits and scene lookups Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * chore: Remove debug Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Consider hit threshold and inside override too Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Increase outline midpoint sticky distance Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Don't show midpoint indicator when no snapping is possible Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * feat: Indicate lock-in Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * chore: Remove Map caching Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: incorrect threshold Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: threshold setting Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Hit caching Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Simple arrow mid point selection inconsistency Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: cache override Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Precise know dragging with midpoint refactor Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fear: Frame support Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Crossing arrow won't trigger mid point Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Arrow creation point highlight Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Restore types & tests Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * chore: Restore restore.ts Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: restore.ts Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Elbow arrows reliably highlight center point Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Highlight point ordering Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * feat: Bind with focus point across shape Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Lint * fix: Midpoint and binding alignment Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * chore: Indicator color Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * chore: More knob tuning Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Radius Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * fix: Tests Signed-off-by: Mark Tolmacs <mark@lazycat.hu> * simplify point indicators --------- Signed-off-by: Mark Tolmacs <mark@lazycat.hu> Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com>
This commit is contained in:
@@ -800,6 +800,7 @@ const getBindingStrategyForDraggingBindingElementEndpoints_simple = (
|
|||||||
hit,
|
hit,
|
||||||
startDragged ? "start" : "end",
|
startDragged ? "start" : "end",
|
||||||
elementsMap,
|
elementsMap,
|
||||||
|
appState.zoom,
|
||||||
) || globalPoint,
|
) || globalPoint,
|
||||||
}
|
}
|
||||||
: { mode: null };
|
: { mode: null };
|
||||||
@@ -833,6 +834,7 @@ const getBindingStrategyForDraggingBindingElementEndpoints_simple = (
|
|||||||
otherBindableElement,
|
otherBindableElement,
|
||||||
startDragged ? "end" : "start",
|
startDragged ? "end" : "start",
|
||||||
elementsMap,
|
elementsMap,
|
||||||
|
appState.zoom,
|
||||||
) || otherEndpoint,
|
) || otherEndpoint,
|
||||||
}
|
}
|
||||||
: { mode: undefined }
|
: { mode: undefined }
|
||||||
@@ -1389,14 +1391,16 @@ export const bindPointToSnapToElementOutline = (
|
|||||||
headingForPointFromElement(bindableElement, aabb, point),
|
headingForPointFromElement(bindableElement, aabb, point),
|
||||||
);
|
);
|
||||||
const snapPoint = snapToMid(
|
const snapPoint = snapToMid(
|
||||||
arrowElement,
|
|
||||||
bindableElement,
|
bindableElement,
|
||||||
elementsMap,
|
elementsMap,
|
||||||
edgePoint,
|
edgePoint,
|
||||||
|
0.05,
|
||||||
|
arrowElement,
|
||||||
);
|
);
|
||||||
|
const resolved = snapPoint || point;
|
||||||
const otherPoint = pointFrom<GlobalPoint>(
|
const otherPoint = pointFrom<GlobalPoint>(
|
||||||
isHorizontal ? bindableCenter[0] : snapPoint[0],
|
isHorizontal ? bindableCenter[0] : resolved[0],
|
||||||
!isHorizontal ? bindableCenter[1] : snapPoint[1],
|
!isHorizontal ? bindableCenter[1] : resolved[1],
|
||||||
);
|
);
|
||||||
const intersector =
|
const intersector =
|
||||||
customIntersector ??
|
customIntersector ??
|
||||||
@@ -1404,7 +1408,7 @@ export const bindPointToSnapToElementOutline = (
|
|||||||
otherPoint,
|
otherPoint,
|
||||||
pointFromVector(
|
pointFromVector(
|
||||||
vectorScale(
|
vectorScale(
|
||||||
vectorNormalize(vectorFromPoint(snapPoint, otherPoint)),
|
vectorNormalize(vectorFromPoint(resolved, otherPoint)),
|
||||||
Math.max(bindableElement.width, bindableElement.height) * 2,
|
Math.max(bindableElement.width, bindableElement.height) * 2,
|
||||||
),
|
),
|
||||||
otherPoint,
|
otherPoint,
|
||||||
@@ -1419,14 +1423,14 @@ export const bindPointToSnapToElementOutline = (
|
|||||||
|
|
||||||
if (!intersection) {
|
if (!intersection) {
|
||||||
const anotherPoint = pointFrom<GlobalPoint>(
|
const anotherPoint = pointFrom<GlobalPoint>(
|
||||||
!isHorizontal ? bindableCenter[0] : snapPoint[0],
|
!isHorizontal ? bindableCenter[0] : resolved[0],
|
||||||
isHorizontal ? bindableCenter[1] : snapPoint[1],
|
isHorizontal ? bindableCenter[1] : resolved[1],
|
||||||
);
|
);
|
||||||
const anotherIntersector = lineSegment(
|
const anotherIntersector = lineSegment(
|
||||||
anotherPoint,
|
anotherPoint,
|
||||||
pointFromVector(
|
pointFromVector(
|
||||||
vectorScale(
|
vectorScale(
|
||||||
vectorNormalize(vectorFromPoint(snapPoint, anotherPoint)),
|
vectorNormalize(vectorFromPoint(resolved, anotherPoint)),
|
||||||
Math.max(bindableElement.width, bindableElement.height) * 2,
|
Math.max(bindableElement.width, bindableElement.height) * 2,
|
||||||
),
|
),
|
||||||
anotherPoint,
|
anotherPoint,
|
||||||
@@ -1573,18 +1577,18 @@ export const avoidRectangularCorner = (
|
|||||||
return p;
|
return p;
|
||||||
};
|
};
|
||||||
|
|
||||||
const snapToMid = (
|
export const snapToMid = (
|
||||||
arrowElement: ExcalidrawArrowElement,
|
|
||||||
bindTarget: ExcalidrawBindableElement,
|
bindTarget: ExcalidrawBindableElement,
|
||||||
elementsMap: ElementsMap,
|
elementsMap: ElementsMap,
|
||||||
p: GlobalPoint,
|
p: GlobalPoint,
|
||||||
tolerance: number = 0.05,
|
tolerance: number = 0.05,
|
||||||
): GlobalPoint => {
|
arrowElement?: ExcalidrawArrowElement,
|
||||||
|
): GlobalPoint | undefined => {
|
||||||
const { x, y, width, height, angle } = bindTarget;
|
const { x, y, width, height, angle } = bindTarget;
|
||||||
const center = elementCenterPoint(bindTarget, elementsMap, -0.1, -0.1);
|
const center = elementCenterPoint(bindTarget, elementsMap, -0.1, -0.1);
|
||||||
const nonRotated = pointRotateRads(p, center, -angle as Radians);
|
const nonRotated = pointRotateRads(p, center, -angle as Radians);
|
||||||
|
|
||||||
const bindingGap = getBindingGap(bindTarget, arrowElement);
|
const bindingGap = arrowElement ? getBindingGap(bindTarget, arrowElement) : 0;
|
||||||
|
|
||||||
// snap-to-center point is adaptive to element size, but we don't want to go
|
// snap-to-center point is adaptive to element size, but we don't want to go
|
||||||
// above and below certain px distance
|
// above and below certain px distance
|
||||||
@@ -1593,7 +1597,7 @@ const snapToMid = (
|
|||||||
|
|
||||||
// Too close to the center makes it hard to resolve direction precisely
|
// Too close to the center makes it hard to resolve direction precisely
|
||||||
if (pointDistance(center, nonRotated) < bindingGap) {
|
if (pointDistance(center, nonRotated) < bindingGap) {
|
||||||
return p;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@@ -1602,8 +1606,8 @@ const snapToMid = (
|
|||||||
nonRotated[1] < center[1] + verticalThreshold
|
nonRotated[1] < center[1] + verticalThreshold
|
||||||
) {
|
) {
|
||||||
// LEFT
|
// LEFT
|
||||||
return pointRotateRads<GlobalPoint>(
|
return pointRotateRads(
|
||||||
pointFrom(x - bindingGap, center[1]),
|
pointFrom<GlobalPoint>(x - bindingGap, center[1]),
|
||||||
center,
|
center,
|
||||||
angle,
|
angle,
|
||||||
);
|
);
|
||||||
@@ -1613,7 +1617,11 @@ const snapToMid = (
|
|||||||
nonRotated[0] < center[0] + horizontalThreshold
|
nonRotated[0] < center[0] + horizontalThreshold
|
||||||
) {
|
) {
|
||||||
// TOP
|
// TOP
|
||||||
return pointRotateRads(pointFrom(center[0], y - bindingGap), center, angle);
|
return pointRotateRads(
|
||||||
|
pointFrom<GlobalPoint>(center[0], y - bindingGap),
|
||||||
|
center,
|
||||||
|
angle,
|
||||||
|
);
|
||||||
} else if (
|
} else if (
|
||||||
nonRotated[0] >= x + width / 2 &&
|
nonRotated[0] >= x + width / 2 &&
|
||||||
nonRotated[1] > center[1] - verticalThreshold &&
|
nonRotated[1] > center[1] - verticalThreshold &&
|
||||||
@@ -1621,7 +1629,7 @@ const snapToMid = (
|
|||||||
) {
|
) {
|
||||||
// RIGHT
|
// RIGHT
|
||||||
return pointRotateRads(
|
return pointRotateRads(
|
||||||
pointFrom(x + width + bindingGap, center[1]),
|
pointFrom<GlobalPoint>(x + width + bindingGap, center[1]),
|
||||||
center,
|
center,
|
||||||
angle,
|
angle,
|
||||||
);
|
);
|
||||||
@@ -1632,7 +1640,7 @@ const snapToMid = (
|
|||||||
) {
|
) {
|
||||||
// DOWN
|
// DOWN
|
||||||
return pointRotateRads(
|
return pointRotateRads(
|
||||||
pointFrom(center[0], y + height + bindingGap),
|
pointFrom<GlobalPoint>(center[0], y + height + bindingGap),
|
||||||
center,
|
center,
|
||||||
angle,
|
angle,
|
||||||
);
|
);
|
||||||
@@ -1681,7 +1689,7 @@ const snapToMid = (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return p;
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
const compareElementArea = (
|
const compareElementArea = (
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import {
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
deconstructLinearOrFreeDrawElement,
|
deconstructLinearOrFreeDrawElement,
|
||||||
|
getSnapOutlineMidPoint,
|
||||||
isPathALoop,
|
isPathALoop,
|
||||||
moveArrowAboveBindable,
|
moveArrowAboveBindable,
|
||||||
projectFixedPointOntoDiagonal,
|
projectFixedPointOntoDiagonal,
|
||||||
@@ -48,6 +49,7 @@ import {
|
|||||||
calculateFixedPointForNonElbowArrowBinding,
|
calculateFixedPointForNonElbowArrowBinding,
|
||||||
getBindingStrategyForDraggingBindingElementEndpoints,
|
getBindingStrategyForDraggingBindingElementEndpoints,
|
||||||
isBindingEnabled,
|
isBindingEnabled,
|
||||||
|
snapToMid,
|
||||||
updateBoundPoint,
|
updateBoundPoint,
|
||||||
} from "./binding";
|
} from "./binding";
|
||||||
import {
|
import {
|
||||||
@@ -355,6 +357,7 @@ export class LinearElementEditor {
|
|||||||
app,
|
app,
|
||||||
shouldRotateWithDiscreteAngle(event),
|
shouldRotateWithDiscreteAngle(event),
|
||||||
event.altKey,
|
event.altKey,
|
||||||
|
linearElementEditor,
|
||||||
);
|
);
|
||||||
|
|
||||||
LinearElementEditor.movePoints(element, app.scene, positions, {
|
LinearElementEditor.movePoints(element, app.scene, positions, {
|
||||||
@@ -408,13 +411,14 @@ export class LinearElementEditor {
|
|||||||
altFocusPoint:
|
altFocusPoint:
|
||||||
!linearElementEditor.initialState.altFocusPoint &&
|
!linearElementEditor.initialState.altFocusPoint &&
|
||||||
startBindingElement &&
|
startBindingElement &&
|
||||||
updates?.suggestedBinding?.id !== startBindingElement.id
|
updates?.suggestedBinding?.element.id !== startBindingElement.id
|
||||||
? projectFixedPointOntoDiagonal(
|
? projectFixedPointOntoDiagonal(
|
||||||
element,
|
element,
|
||||||
pointFrom<GlobalPoint>(element.x, element.y),
|
pointFrom<GlobalPoint>(element.x, element.y),
|
||||||
startBindingElement,
|
startBindingElement,
|
||||||
"start",
|
"start",
|
||||||
elementsMap,
|
elementsMap,
|
||||||
|
app.state.zoom,
|
||||||
)
|
)
|
||||||
: linearElementEditor.initialState.altFocusPoint,
|
: linearElementEditor.initialState.altFocusPoint,
|
||||||
},
|
},
|
||||||
@@ -532,6 +536,7 @@ export class LinearElementEditor {
|
|||||||
app,
|
app,
|
||||||
shouldRotateWithDiscreteAngle(event) && singlePointDragged,
|
shouldRotateWithDiscreteAngle(event) && singlePointDragged,
|
||||||
event.altKey,
|
event.altKey,
|
||||||
|
linearElementEditor,
|
||||||
);
|
);
|
||||||
|
|
||||||
LinearElementEditor.movePoints(element, app.scene, positions, {
|
LinearElementEditor.movePoints(element, app.scene, positions, {
|
||||||
@@ -607,11 +612,11 @@ export class LinearElementEditor {
|
|||||||
const altFocusPointBindableElement =
|
const altFocusPointBindableElement =
|
||||||
endIsSelected && // The "other" end (i.e. "end") is dragged
|
endIsSelected && // The "other" end (i.e. "end") is dragged
|
||||||
startBindingElement &&
|
startBindingElement &&
|
||||||
updates?.suggestedBinding?.id !== startBindingElement.id // The end point is not hovering the start bindable + it's binding gap
|
updates?.suggestedBinding?.element.id !== startBindingElement.id // The end point is not hovering the start bindable + it's binding gap
|
||||||
? startBindingElement
|
? startBindingElement
|
||||||
: startIsSelected && // The "other" end (i.e. "start") is dragged
|
: startIsSelected && // The "other" end (i.e. "start") is dragged
|
||||||
endBindingElement &&
|
endBindingElement &&
|
||||||
updates?.suggestedBinding?.id !== endBindingElement.id // The start point is not hovering the end bindable + it's binding gap
|
updates?.suggestedBinding?.element.id !== endBindingElement.id // The start point is not hovering the end bindable + it's binding gap
|
||||||
? endBindingElement
|
? endBindingElement
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
@@ -631,6 +636,7 @@ export class LinearElementEditor {
|
|||||||
altFocusPointBindableElement,
|
altFocusPointBindableElement,
|
||||||
"start",
|
"start",
|
||||||
elementsMap,
|
elementsMap,
|
||||||
|
app.state.zoom,
|
||||||
)
|
)
|
||||||
: linearElementEditor.initialState.altFocusPoint,
|
: linearElementEditor.initialState.altFocusPoint,
|
||||||
},
|
},
|
||||||
@@ -2080,6 +2086,7 @@ const pointDraggingUpdates = (
|
|||||||
app: AppClassProperties,
|
app: AppClassProperties,
|
||||||
angleLocked: boolean,
|
angleLocked: boolean,
|
||||||
altKey: boolean,
|
altKey: boolean,
|
||||||
|
linearElementEditor: LinearElementEditor,
|
||||||
): {
|
): {
|
||||||
positions: PointsPositionUpdates;
|
positions: PointsPositionUpdates;
|
||||||
updates?: PointMoveOtherUpdates;
|
updates?: PointMoveOtherUpdates;
|
||||||
@@ -2127,13 +2134,27 @@ const pointDraggingUpdates = (
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (isElbowArrow(element)) {
|
if (isElbowArrow(element)) {
|
||||||
return {
|
const suggestedBindingElement = startIsDragged
|
||||||
positions: naiveDraggingPoints,
|
|
||||||
updates: {
|
|
||||||
suggestedBinding: startIsDragged
|
|
||||||
? start.element
|
? start.element
|
||||||
: endIsDragged
|
: endIsDragged
|
||||||
? end.element
|
? end.element
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
positions: naiveDraggingPoints,
|
||||||
|
updates: {
|
||||||
|
suggestedBinding: suggestedBindingElement
|
||||||
|
? {
|
||||||
|
element: suggestedBindingElement,
|
||||||
|
midPoint: snapToMid(
|
||||||
|
suggestedBindingElement,
|
||||||
|
elementsMap,
|
||||||
|
pointFrom<GlobalPoint>(
|
||||||
|
scenePointerX - linearElementEditor.pointerOffset.x,
|
||||||
|
scenePointerY - linearElementEditor.pointerOffset.y,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
}
|
||||||
: null,
|
: null,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -2226,7 +2247,20 @@ const pointDraggingUpdates = (
|
|||||||
(updates.startBinding.mode === "orbit" ||
|
(updates.startBinding.mode === "orbit" ||
|
||||||
!getFeatureFlag("COMPLEX_BINDINGS"))
|
!getFeatureFlag("COMPLEX_BINDINGS"))
|
||||||
) {
|
) {
|
||||||
updates.suggestedBinding = start.element;
|
updates.suggestedBinding = start.element
|
||||||
|
? {
|
||||||
|
element: start.element,
|
||||||
|
midPoint: getSnapOutlineMidPoint(
|
||||||
|
pointFrom<GlobalPoint>(
|
||||||
|
scenePointerX - linearElementEditor.pointerOffset.x,
|
||||||
|
scenePointerY - linearElementEditor.pointerOffset.y,
|
||||||
|
),
|
||||||
|
start.element,
|
||||||
|
elementsMap,
|
||||||
|
app.state.zoom,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
: null;
|
||||||
}
|
}
|
||||||
} else if (startIsDragged) {
|
} else if (startIsDragged) {
|
||||||
updates.suggestedBinding = app.state.suggestedBinding;
|
updates.suggestedBinding = app.state.suggestedBinding;
|
||||||
@@ -2252,7 +2286,20 @@ const pointDraggingUpdates = (
|
|||||||
(updates.endBinding.mode === "orbit" ||
|
(updates.endBinding.mode === "orbit" ||
|
||||||
!getFeatureFlag("COMPLEX_BINDINGS"))
|
!getFeatureFlag("COMPLEX_BINDINGS"))
|
||||||
) {
|
) {
|
||||||
updates.suggestedBinding = end.element;
|
updates.suggestedBinding = end.element
|
||||||
|
? {
|
||||||
|
element: end.element,
|
||||||
|
midPoint: getSnapOutlineMidPoint(
|
||||||
|
pointFrom<GlobalPoint>(
|
||||||
|
scenePointerX - linearElementEditor.pointerOffset.x,
|
||||||
|
scenePointerY - linearElementEditor.pointerOffset.y,
|
||||||
|
),
|
||||||
|
end.element,
|
||||||
|
elementsMap,
|
||||||
|
app.state.zoom,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
: null;
|
||||||
}
|
}
|
||||||
} else if (endIsDragged) {
|
} else if (endIsDragged) {
|
||||||
updates.suggestedBinding = app.state.suggestedBinding;
|
updates.suggestedBinding = app.state.suggestedBinding;
|
||||||
|
|||||||
+119
-25
@@ -7,6 +7,7 @@ import {
|
|||||||
} from "@excalidraw/common";
|
} from "@excalidraw/common";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
bezierEquation,
|
||||||
curve,
|
curve,
|
||||||
curveCatmullRomCubicApproxPoints,
|
curveCatmullRomCubicApproxPoints,
|
||||||
curveOffsetPoints,
|
curveOffsetPoints,
|
||||||
@@ -27,19 +28,25 @@ import {
|
|||||||
|
|
||||||
import type { Curve, LineSegment, LocalPoint } from "@excalidraw/math";
|
import type { Curve, LineSegment, LocalPoint } from "@excalidraw/math";
|
||||||
|
|
||||||
import type { NormalizedZoomValue, Zoom } from "@excalidraw/excalidraw/types";
|
import type {
|
||||||
|
AppState,
|
||||||
|
NormalizedZoomValue,
|
||||||
|
Zoom,
|
||||||
|
} from "@excalidraw/excalidraw/types";
|
||||||
|
|
||||||
import { elementCenterPoint, getDiamondPoints } from "./bounds";
|
import { elementCenterPoint, getDiamondPoints } from "./bounds";
|
||||||
|
|
||||||
import { generateLinearCollisionShape } from "./shape";
|
import { generateLinearCollisionShape } from "./shape";
|
||||||
|
|
||||||
import { isPointInElement } from "./collision";
|
import { hitElementItself, isPointInElement } from "./collision";
|
||||||
import { LinearElementEditor } from "./linearElementEditor";
|
import { LinearElementEditor } from "./linearElementEditor";
|
||||||
import { isRectangularElement } from "./typeChecks";
|
import { isRectangularElement } from "./typeChecks";
|
||||||
|
import { maxBindingDistance_simple } from "./binding";
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
ElementsMap,
|
ElementsMap,
|
||||||
ExcalidrawArrowElement,
|
ExcalidrawArrowElement,
|
||||||
|
ExcalidrawBindableElement,
|
||||||
ExcalidrawDiamondElement,
|
ExcalidrawDiamondElement,
|
||||||
ExcalidrawElement,
|
ExcalidrawElement,
|
||||||
ExcalidrawFreeDrawElement,
|
ExcalidrawFreeDrawElement,
|
||||||
@@ -329,24 +336,10 @@ export function deconstructRectanguloidElement(
|
|||||||
return shape;
|
return shape;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export function getDiamondBaseCorners(
|
||||||
* Get the **unrotated** building components of a diamond element
|
|
||||||
* in the form of line segments and curves as a tuple, in this order.
|
|
||||||
*
|
|
||||||
* @param element The element to deconstruct
|
|
||||||
* @param offset An optional offset
|
|
||||||
* @returns Tuple of line **unrotated** segments (0) and curves (1)
|
|
||||||
*/
|
|
||||||
export function deconstructDiamondElement(
|
|
||||||
element: ExcalidrawDiamondElement,
|
element: ExcalidrawDiamondElement,
|
||||||
offset: number = 0,
|
offset: number = 0,
|
||||||
): [LineSegment<GlobalPoint>[], Curve<GlobalPoint>[]] {
|
): Curve<GlobalPoint>[] {
|
||||||
const cachedShape = getElementShapesCacheEntry(element, offset);
|
|
||||||
|
|
||||||
if (cachedShape) {
|
|
||||||
return cachedShape;
|
|
||||||
}
|
|
||||||
|
|
||||||
const [topX, topY, rightX, rightY, bottomX, bottomY, leftX, leftY] =
|
const [topX, topY, rightX, rightY, bottomX, bottomY, leftX, leftY] =
|
||||||
getDiamondPoints(element);
|
getDiamondPoints(element);
|
||||||
const verticalRadius = element.roundness
|
const verticalRadius = element.roundness
|
||||||
@@ -363,7 +356,7 @@ export function deconstructDiamondElement(
|
|||||||
pointFrom(element.x + leftX, element.y + leftY),
|
pointFrom(element.x + leftX, element.y + leftY),
|
||||||
];
|
];
|
||||||
|
|
||||||
const baseCorners = [
|
return [
|
||||||
curve(
|
curve(
|
||||||
pointFrom<GlobalPoint>(
|
pointFrom<GlobalPoint>(
|
||||||
right[0] - verticalRadius,
|
right[0] - verticalRadius,
|
||||||
@@ -413,6 +406,27 @@ export function deconstructDiamondElement(
|
|||||||
),
|
),
|
||||||
), // TOP
|
), // TOP
|
||||||
];
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the **unrotated** building components of a diamond element
|
||||||
|
* in the form of line segments and curves as a tuple, in this order.
|
||||||
|
*
|
||||||
|
* @param element The element to deconstruct
|
||||||
|
* @param offset An optional offset
|
||||||
|
* @returns Tuple of line **unrotated** segments (0) and curves (1)
|
||||||
|
*/
|
||||||
|
export function deconstructDiamondElement(
|
||||||
|
element: ExcalidrawDiamondElement,
|
||||||
|
offset: number = 0,
|
||||||
|
): [LineSegment<GlobalPoint>[], Curve<GlobalPoint>[]] {
|
||||||
|
const cachedShape = getElementShapesCacheEntry(element, offset);
|
||||||
|
|
||||||
|
if (cachedShape) {
|
||||||
|
return cachedShape;
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseCorners = getDiamondBaseCorners(element, offset);
|
||||||
|
|
||||||
const corners = baseCorners.map(
|
const corners = baseCorners.map(
|
||||||
(corner) =>
|
(corner) =>
|
||||||
@@ -570,18 +584,94 @@ const getDiagonalsForBindableElement = (
|
|||||||
return [diagonalOne, diagonalTwo];
|
return [diagonalOne, diagonalTwo];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getSnapOutlineMidPoint = (
|
||||||
|
point: GlobalPoint,
|
||||||
|
element: ExcalidrawBindableElement,
|
||||||
|
elementsMap: ElementsMap,
|
||||||
|
zoom: AppState["zoom"],
|
||||||
|
) => {
|
||||||
|
const center = elementCenterPoint(element, elementsMap);
|
||||||
|
const sideMidpoints =
|
||||||
|
element.type === "diamond"
|
||||||
|
? getDiamondBaseCorners(element).map((curve) => {
|
||||||
|
const point = bezierEquation(curve, 0.5);
|
||||||
|
const rotatedPoint = pointRotateRads(point, center, element.angle);
|
||||||
|
|
||||||
|
return pointFrom<GlobalPoint>(rotatedPoint[0], rotatedPoint[1]);
|
||||||
|
})
|
||||||
|
: [
|
||||||
|
// RIGHT midpoint
|
||||||
|
pointRotateRads(
|
||||||
|
pointFrom<GlobalPoint>(
|
||||||
|
element.x + element.width,
|
||||||
|
element.y + element.height / 2,
|
||||||
|
),
|
||||||
|
center,
|
||||||
|
element.angle,
|
||||||
|
),
|
||||||
|
// BOTTOM midpoint
|
||||||
|
pointRotateRads(
|
||||||
|
pointFrom<GlobalPoint>(
|
||||||
|
element.x + element.width / 2,
|
||||||
|
element.y + element.height,
|
||||||
|
),
|
||||||
|
center,
|
||||||
|
element.angle,
|
||||||
|
),
|
||||||
|
// LEFT midpoint
|
||||||
|
pointRotateRads(
|
||||||
|
pointFrom<GlobalPoint>(element.x, element.y + element.height / 2),
|
||||||
|
center,
|
||||||
|
element.angle,
|
||||||
|
),
|
||||||
|
// TOP midpoint
|
||||||
|
pointRotateRads(
|
||||||
|
pointFrom<GlobalPoint>(element.x + element.width / 2, element.y),
|
||||||
|
center,
|
||||||
|
element.angle,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
const candidate = sideMidpoints.find(
|
||||||
|
(midpoint) =>
|
||||||
|
pointDistance(point, midpoint) <=
|
||||||
|
maxBindingDistance_simple(zoom) + element.strokeWidth / 2 &&
|
||||||
|
!hitElementItself({
|
||||||
|
point,
|
||||||
|
element,
|
||||||
|
threshold: 0,
|
||||||
|
elementsMap,
|
||||||
|
overrideShouldTestInside: true,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return candidate;
|
||||||
|
};
|
||||||
|
|
||||||
export const projectFixedPointOntoDiagonal = (
|
export const projectFixedPointOntoDiagonal = (
|
||||||
arrow: ExcalidrawArrowElement,
|
arrow: ExcalidrawArrowElement,
|
||||||
point: GlobalPoint,
|
point: GlobalPoint,
|
||||||
element: ExcalidrawElement,
|
element: ExcalidrawBindableElement,
|
||||||
startOrEnd: "start" | "end",
|
startOrEnd: "start" | "end",
|
||||||
elementsMap: ElementsMap,
|
elementsMap: ElementsMap,
|
||||||
|
zoom: AppState["zoom"],
|
||||||
): GlobalPoint | null => {
|
): GlobalPoint | null => {
|
||||||
invariant(arrow.points.length >= 2, "Arrow must have at least two points");
|
invariant(arrow.points.length >= 2, "Arrow must have at least two points");
|
||||||
if (arrow.width < 3 && arrow.height < 3) {
|
if (arrow.width < 3 && arrow.height < 3) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const sideMidPoint = getSnapOutlineMidPoint(
|
||||||
|
point,
|
||||||
|
element,
|
||||||
|
elementsMap,
|
||||||
|
zoom,
|
||||||
|
);
|
||||||
|
if (sideMidPoint) {
|
||||||
|
return sideMidPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do the projection onto the diagonals (or center lines
|
||||||
|
// for non-rectangular shapes)
|
||||||
const [diagonalOne, diagonalTwo] = getDiagonalsForBindableElement(
|
const [diagonalOne, diagonalTwo] = getDiagonalsForBindableElement(
|
||||||
element,
|
element,
|
||||||
elementsMap,
|
elementsMap,
|
||||||
@@ -603,18 +693,22 @@ export const projectFixedPointOntoDiagonal = (
|
|||||||
),
|
),
|
||||||
a,
|
a,
|
||||||
);
|
);
|
||||||
const intersector = lineSegment<GlobalPoint>(point, b);
|
const intersector = lineSegment<GlobalPoint>(b, a);
|
||||||
const p1 = lineSegmentIntersectionPoints(diagonalOne, intersector);
|
const p1 = lineSegmentIntersectionPoints(diagonalOne, intersector);
|
||||||
const p2 = lineSegmentIntersectionPoints(diagonalTwo, intersector);
|
const p2 = lineSegmentIntersectionPoints(diagonalTwo, intersector);
|
||||||
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;
|
let projection = null;
|
||||||
if (d1 != null && d2 != null) {
|
if (d1 != null && d2 != null) {
|
||||||
p = d1 < d2 ? p1 : p2;
|
projection = d1 < d2 ? p1 : p2;
|
||||||
} else {
|
} else {
|
||||||
p = p1 || p2 || null;
|
projection = p1 || p2 || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return p && isPointInElement(p, element, elementsMap) ? p : null;
|
if (projection && isPointInElement(projection, element, elementsMap)) {
|
||||||
|
return projection;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1350,8 +1350,8 @@ describe("multiple selection", () => {
|
|||||||
|
|
||||||
expect(boundArrow.x).toBeCloseTo(380 * scaleX);
|
expect(boundArrow.x).toBeCloseTo(380 * scaleX);
|
||||||
expect(boundArrow.y).toBeCloseTo(240 * scaleY);
|
expect(boundArrow.y).toBeCloseTo(240 * scaleY);
|
||||||
expect(boundArrow.points[1][0]).toBeCloseTo(63.4035);
|
expect(boundArrow.points[1][0]).toBeCloseTo(63.40354208105561);
|
||||||
expect(boundArrow.points[1][1]).toBeCloseTo(-84.538);
|
expect(boundArrow.points[1][1]).toBeCloseTo(-84.53805610807356);
|
||||||
|
|
||||||
expect(arrowLabelPos.x + arrowLabel.width / 2).toBeCloseTo(
|
expect(arrowLabelPos.x + arrowLabel.width / 2).toBeCloseTo(
|
||||||
boundArrow.x + boundArrow.points[1][0] / 2,
|
boundArrow.x + boundArrow.points[1][0] / 2,
|
||||||
|
|||||||
@@ -108,7 +108,6 @@ export const actionFinalize = register<FormData>({
|
|||||||
|
|
||||||
return map;
|
return map;
|
||||||
}, new Map()) ?? new Map();
|
}, new Map()) ?? new Map();
|
||||||
|
|
||||||
bindOrUnbindBindingElement(
|
bindOrUnbindBindingElement(
|
||||||
element,
|
element,
|
||||||
draggedPoints,
|
draggedPoints,
|
||||||
|
|||||||
@@ -249,6 +249,7 @@ import {
|
|||||||
maxBindingDistance_simple,
|
maxBindingDistance_simple,
|
||||||
convertToExcalidrawElements,
|
convertToExcalidrawElements,
|
||||||
type ExcalidrawElementSkeleton,
|
type ExcalidrawElementSkeleton,
|
||||||
|
getSnapOutlineMidPoint,
|
||||||
handleFocusPointDrag,
|
handleFocusPointDrag,
|
||||||
handleFocusPointHover,
|
handleFocusPointHover,
|
||||||
handleFocusPointPointerDown,
|
handleFocusPointPointerDown,
|
||||||
@@ -6400,15 +6401,28 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
// and point
|
// and point
|
||||||
const { newElement } = this.state;
|
const { newElement } = this.state;
|
||||||
if (!newElement && isBindingEnabled(this.state)) {
|
if (!newElement && isBindingEnabled(this.state)) {
|
||||||
|
const globalPoint = pointFrom<GlobalPoint>(
|
||||||
|
scenePointerX,
|
||||||
|
scenePointerY,
|
||||||
|
);
|
||||||
|
const elementsMap = this.scene.getNonDeletedElementsMap();
|
||||||
const hoveredElement = getHoveredElementForBinding(
|
const hoveredElement = getHoveredElementForBinding(
|
||||||
pointFrom<GlobalPoint>(scenePointerX, scenePointerY),
|
globalPoint,
|
||||||
this.scene.getNonDeletedElements(),
|
this.scene.getNonDeletedElements(),
|
||||||
this.scene.getNonDeletedElementsMap(),
|
elementsMap,
|
||||||
maxBindingDistance_simple(this.state.zoom),
|
maxBindingDistance_simple(this.state.zoom),
|
||||||
);
|
);
|
||||||
if (hoveredElement) {
|
if (hoveredElement) {
|
||||||
this.setState({
|
this.setState({
|
||||||
suggestedBinding: hoveredElement,
|
suggestedBinding: {
|
||||||
|
element: hoveredElement,
|
||||||
|
midPoint: getSnapOutlineMidPoint(
|
||||||
|
globalPoint,
|
||||||
|
hoveredElement,
|
||||||
|
elementsMap,
|
||||||
|
this.state.zoom,
|
||||||
|
),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
} else if (this.state.suggestedBinding) {
|
} else if (this.state.suggestedBinding) {
|
||||||
this.setState({
|
this.setState({
|
||||||
@@ -6591,16 +6605,19 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
this.scene.getNonDeletedElementsMap(),
|
this.scene.getNonDeletedElementsMap(),
|
||||||
maxBindingDistance_simple(this.state.zoom),
|
maxBindingDistance_simple(this.state.zoom),
|
||||||
);
|
);
|
||||||
if (
|
const scenePointer = pointFrom<GlobalPoint>(scenePointerX, scenePointerY);
|
||||||
hit &&
|
const elementsMap = this.scene.getNonDeletedElementsMap();
|
||||||
!isPointInElement(
|
if (hit && !isPointInElement(scenePointer, hit, elementsMap)) {
|
||||||
pointFrom<GlobalPoint>(scenePointerX, scenePointerY),
|
|
||||||
hit,
|
|
||||||
this.scene.getNonDeletedElementsMap(),
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
this.setState({
|
this.setState({
|
||||||
suggestedBinding: hit,
|
suggestedBinding: {
|
||||||
|
element: hit,
|
||||||
|
midPoint: getSnapOutlineMidPoint(
|
||||||
|
scenePointer,
|
||||||
|
hit,
|
||||||
|
elementsMap,
|
||||||
|
this.state.zoom,
|
||||||
|
),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8762,7 +8779,18 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
bindMode: "orbit",
|
bindMode: "orbit",
|
||||||
newElement: element,
|
newElement: element,
|
||||||
startBoundElement: boundElement,
|
startBoundElement: boundElement,
|
||||||
suggestedBinding: boundElement || null,
|
suggestedBinding:
|
||||||
|
boundElement && isBindingElement(element)
|
||||||
|
? {
|
||||||
|
element: boundElement,
|
||||||
|
midPoint: getSnapOutlineMidPoint(
|
||||||
|
point,
|
||||||
|
boundElement,
|
||||||
|
elementsMap,
|
||||||
|
this.state.zoom,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
: null,
|
||||||
selectedElementIds: nextSelectedElementIds,
|
selectedElementIds: nextSelectedElementIds,
|
||||||
selectedLinearElement: linearElementEditor,
|
selectedLinearElement: linearElementEditor,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -135,7 +135,6 @@ describe("binding with linear elements", () => {
|
|||||||
) as HTMLInputElement;
|
) as HTMLInputElement;
|
||||||
expect(linear.startBinding).not.toBe(null);
|
expect(linear.startBinding).not.toBe(null);
|
||||||
expect(inputX).not.toBeNull();
|
expect(inputX).not.toBeNull();
|
||||||
|
|
||||||
UI.updateInput(inputX, String("184"));
|
UI.updateInput(inputX, String("184"));
|
||||||
expect(linear.startBinding).not.toBe(null);
|
expect(linear.startBinding).not.toBe(null);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -82,7 +82,12 @@ import {
|
|||||||
getNormalizedZoom,
|
getNormalizedZoom,
|
||||||
} from "../scene";
|
} from "../scene";
|
||||||
|
|
||||||
import type { AppState, BinaryFiles, LibraryItem } from "../types";
|
import type {
|
||||||
|
AppState,
|
||||||
|
BinaryFiles,
|
||||||
|
LibraryItem,
|
||||||
|
NormalizedZoomValue,
|
||||||
|
} from "../types";
|
||||||
import type { ImportedDataState, LegacyAppState } from "./types";
|
import type { ImportedDataState, LegacyAppState } from "./types";
|
||||||
|
|
||||||
type RestoredAppState = Omit<
|
type RestoredAppState = Omit<
|
||||||
@@ -212,6 +217,7 @@ const repairBinding = <T extends ExcalidrawArrowElement>(
|
|||||||
boundElement,
|
boundElement,
|
||||||
startOrEnd,
|
startOrEnd,
|
||||||
elementsMap,
|
elementsMap,
|
||||||
|
{ value: 1 as NormalizedZoomValue },
|
||||||
) || p;
|
) || p;
|
||||||
const { fixedPoint } = calculateFixedPointForNonElbowArrowBinding(
|
const { fixedPoint } = calculateFixedPointForNonElbowArrowBinding(
|
||||||
element,
|
element,
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import {
|
|||||||
type GlobalPoint,
|
type GlobalPoint,
|
||||||
type LocalPoint,
|
type LocalPoint,
|
||||||
type Radians,
|
type Radians,
|
||||||
|
bezierEquation,
|
||||||
|
pointRotateRads,
|
||||||
} from "@excalidraw/math";
|
} from "@excalidraw/math";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -21,11 +23,13 @@ import {
|
|||||||
deconstructDiamondElement,
|
deconstructDiamondElement,
|
||||||
deconstructRectanguloidElement,
|
deconstructRectanguloidElement,
|
||||||
elementCenterPoint,
|
elementCenterPoint,
|
||||||
|
getDiamondBaseCorners,
|
||||||
FOCUS_POINT_SIZE,
|
FOCUS_POINT_SIZE,
|
||||||
getOmitSidesForEditorInterface,
|
getOmitSidesForEditorInterface,
|
||||||
getTransformHandles,
|
getTransformHandles,
|
||||||
getTransformHandlesFromCoords,
|
getTransformHandlesFromCoords,
|
||||||
hasBoundingBox,
|
hasBoundingBox,
|
||||||
|
hitElementItself,
|
||||||
isArrowElement,
|
isArrowElement,
|
||||||
isBindableElement,
|
isBindableElement,
|
||||||
isElbowArrow,
|
isElbowArrow,
|
||||||
@@ -35,6 +39,12 @@ import {
|
|||||||
isLineElement,
|
isLineElement,
|
||||||
isTextElement,
|
isTextElement,
|
||||||
LinearElementEditor,
|
LinearElementEditor,
|
||||||
|
headingForPoint,
|
||||||
|
compareHeading,
|
||||||
|
HEADING_RIGHT,
|
||||||
|
HEADING_DOWN,
|
||||||
|
HEADING_LEFT,
|
||||||
|
HEADING_UP,
|
||||||
} from "@excalidraw/element";
|
} from "@excalidraw/element";
|
||||||
|
|
||||||
import { renderSelectionElement } from "@excalidraw/element";
|
import { renderSelectionElement } from "@excalidraw/element";
|
||||||
@@ -88,8 +98,11 @@ import {
|
|||||||
strokeRectWithRotation_simple,
|
strokeRectWithRotation_simple,
|
||||||
} from "./helpers";
|
} from "./helpers";
|
||||||
|
|
||||||
import type { AppClassProperties, InteractiveCanvasAppState } from "../types";
|
import type {
|
||||||
|
AppState,
|
||||||
|
AppClassProperties,
|
||||||
|
InteractiveCanvasAppState,
|
||||||
|
} from "../types";
|
||||||
import type {
|
import type {
|
||||||
InteractiveCanvasRenderConfig,
|
InteractiveCanvasRenderConfig,
|
||||||
InteractiveSceneRenderConfig,
|
InteractiveSceneRenderConfig,
|
||||||
@@ -209,11 +222,14 @@ const renderSingleLinearPoint = <Point extends GlobalPoint | LocalPoint>(
|
|||||||
|
|
||||||
const renderBindingHighlightForBindableElement_simple = (
|
const renderBindingHighlightForBindableElement_simple = (
|
||||||
context: CanvasRenderingContext2D,
|
context: CanvasRenderingContext2D,
|
||||||
element: ExcalidrawBindableElement,
|
suggestedBinding: NonNullable<AppState["suggestedBinding"]>,
|
||||||
elementsMap: ElementsMap,
|
elementsMap: ElementsMap,
|
||||||
appState: InteractiveCanvasAppState,
|
appState: InteractiveCanvasAppState,
|
||||||
|
pointerCoords: GlobalPoint | null,
|
||||||
) => {
|
) => {
|
||||||
const enclosingFrame = element.frameId && elementsMap.get(element.frameId);
|
const enclosingFrame =
|
||||||
|
suggestedBinding.element.frameId &&
|
||||||
|
elementsMap.get(suggestedBinding.element.frameId);
|
||||||
if (enclosingFrame && isFrameLikeElement(enclosingFrame)) {
|
if (enclosingFrame && isFrameLikeElement(enclosingFrame)) {
|
||||||
context.translate(enclosingFrame.x, enclosingFrame.y);
|
context.translate(enclosingFrame.x, enclosingFrame.y);
|
||||||
|
|
||||||
@@ -236,12 +252,12 @@ const renderBindingHighlightForBindableElement_simple = (
|
|||||||
context.translate(-enclosingFrame.x, -enclosingFrame.y);
|
context.translate(-enclosingFrame.x, -enclosingFrame.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (element.type) {
|
switch (suggestedBinding.element.type) {
|
||||||
case "magicframe":
|
case "magicframe":
|
||||||
case "frame":
|
case "frame":
|
||||||
context.save();
|
context.save();
|
||||||
|
|
||||||
context.translate(element.x, element.y);
|
context.translate(suggestedBinding.element.x, suggestedBinding.element.y);
|
||||||
|
|
||||||
context.lineWidth = FRAME_STYLE.strokeWidth / appState.zoom.value;
|
context.lineWidth = FRAME_STYLE.strokeWidth / appState.zoom.value;
|
||||||
context.strokeStyle =
|
context.strokeStyle =
|
||||||
@@ -254,14 +270,19 @@ const renderBindingHighlightForBindableElement_simple = (
|
|||||||
context.roundRect(
|
context.roundRect(
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
element.width,
|
suggestedBinding.element.width,
|
||||||
element.height,
|
suggestedBinding.element.height,
|
||||||
FRAME_STYLE.radius / appState.zoom.value,
|
FRAME_STYLE.radius / appState.zoom.value,
|
||||||
);
|
);
|
||||||
context.stroke();
|
context.stroke();
|
||||||
context.closePath();
|
context.closePath();
|
||||||
} else {
|
} else {
|
||||||
context.strokeRect(0, 0, element.width, element.height);
|
context.strokeRect(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
suggestedBinding.element.width,
|
||||||
|
suggestedBinding.element.height,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.restore();
|
context.restore();
|
||||||
@@ -269,30 +290,30 @@ const renderBindingHighlightForBindableElement_simple = (
|
|||||||
default:
|
default:
|
||||||
context.save();
|
context.save();
|
||||||
|
|
||||||
const center = elementCenterPoint(element, elementsMap);
|
const center = elementCenterPoint(suggestedBinding.element, elementsMap);
|
||||||
|
|
||||||
context.translate(center[0], center[1]);
|
context.translate(center[0], center[1]);
|
||||||
context.rotate(element.angle as Radians);
|
context.rotate(suggestedBinding.element.angle as Radians);
|
||||||
context.translate(-center[0], -center[1]);
|
context.translate(-center[0], -center[1]);
|
||||||
|
|
||||||
context.translate(element.x, element.y);
|
context.translate(suggestedBinding.element.x, suggestedBinding.element.y);
|
||||||
|
|
||||||
context.lineWidth =
|
context.lineWidth =
|
||||||
clamp(1.75, element.strokeWidth, 4) /
|
clamp(1.75, suggestedBinding.element.strokeWidth, 4) /
|
||||||
Math.max(0.25, appState.zoom.value);
|
Math.max(0.25, appState.zoom.value);
|
||||||
context.strokeStyle =
|
context.strokeStyle =
|
||||||
appState.theme === THEME.DARK
|
appState.theme === THEME.DARK
|
||||||
? `rgba(3, 93, 161, 1)`
|
? `rgba(3, 93, 161, 1)`
|
||||||
: `rgba(106, 189, 252, 1)`;
|
: `rgba(106, 189, 252, 1)`;
|
||||||
|
|
||||||
switch (element.type) {
|
switch (suggestedBinding.element.type) {
|
||||||
case "ellipse":
|
case "ellipse":
|
||||||
context.beginPath();
|
context.beginPath();
|
||||||
context.ellipse(
|
context.ellipse(
|
||||||
element.width / 2,
|
suggestedBinding.element.width / 2,
|
||||||
element.height / 2,
|
suggestedBinding.element.height / 2,
|
||||||
element.width / 2,
|
suggestedBinding.element.width / 2,
|
||||||
element.height / 2,
|
suggestedBinding.element.height / 2,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
2 * Math.PI,
|
2 * Math.PI,
|
||||||
@@ -302,18 +323,20 @@ const renderBindingHighlightForBindableElement_simple = (
|
|||||||
break;
|
break;
|
||||||
case "diamond":
|
case "diamond":
|
||||||
{
|
{
|
||||||
const [segments, curves] = deconstructDiamondElement(element);
|
const [segments, curves] = deconstructDiamondElement(
|
||||||
|
suggestedBinding.element,
|
||||||
|
);
|
||||||
|
|
||||||
// Draw each line segment individually
|
// Draw each line segment individually
|
||||||
segments.forEach((segment) => {
|
segments.forEach((segment) => {
|
||||||
context.beginPath();
|
context.beginPath();
|
||||||
context.moveTo(
|
context.moveTo(
|
||||||
segment[0][0] - element.x,
|
segment[0][0] - suggestedBinding.element.x,
|
||||||
segment[0][1] - element.y,
|
segment[0][1] - suggestedBinding.element.y,
|
||||||
);
|
);
|
||||||
context.lineTo(
|
context.lineTo(
|
||||||
segment[1][0] - element.x,
|
segment[1][0] - suggestedBinding.element.x,
|
||||||
segment[1][1] - element.y,
|
segment[1][1] - suggestedBinding.element.y,
|
||||||
);
|
);
|
||||||
context.stroke();
|
context.stroke();
|
||||||
});
|
});
|
||||||
@@ -322,14 +345,17 @@ const renderBindingHighlightForBindableElement_simple = (
|
|||||||
curves.forEach((curve) => {
|
curves.forEach((curve) => {
|
||||||
const [start, control1, control2, end] = curve;
|
const [start, control1, control2, end] = curve;
|
||||||
context.beginPath();
|
context.beginPath();
|
||||||
context.moveTo(start[0] - element.x, start[1] - element.y);
|
context.moveTo(
|
||||||
|
start[0] - suggestedBinding.element.x,
|
||||||
|
start[1] - suggestedBinding.element.y,
|
||||||
|
);
|
||||||
context.bezierCurveTo(
|
context.bezierCurveTo(
|
||||||
control1[0] - element.x,
|
control1[0] - suggestedBinding.element.x,
|
||||||
control1[1] - element.y,
|
control1[1] - suggestedBinding.element.y,
|
||||||
control2[0] - element.x,
|
control2[0] - suggestedBinding.element.x,
|
||||||
control2[1] - element.y,
|
control2[1] - suggestedBinding.element.y,
|
||||||
end[0] - element.x,
|
end[0] - suggestedBinding.element.x,
|
||||||
end[1] - element.y,
|
end[1] - suggestedBinding.element.y,
|
||||||
);
|
);
|
||||||
context.stroke();
|
context.stroke();
|
||||||
});
|
});
|
||||||
@@ -338,18 +364,20 @@ const renderBindingHighlightForBindableElement_simple = (
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
const [segments, curves] = deconstructRectanguloidElement(element);
|
const [segments, curves] = deconstructRectanguloidElement(
|
||||||
|
suggestedBinding.element,
|
||||||
|
);
|
||||||
|
|
||||||
// Draw each line segment individually
|
// Draw each line segment individually
|
||||||
segments.forEach((segment) => {
|
segments.forEach((segment) => {
|
||||||
context.beginPath();
|
context.beginPath();
|
||||||
context.moveTo(
|
context.moveTo(
|
||||||
segment[0][0] - element.x,
|
segment[0][0] - suggestedBinding.element.x,
|
||||||
segment[0][1] - element.y,
|
segment[0][1] - suggestedBinding.element.y,
|
||||||
);
|
);
|
||||||
context.lineTo(
|
context.lineTo(
|
||||||
segment[1][0] - element.x,
|
segment[1][0] - suggestedBinding.element.x,
|
||||||
segment[1][1] - element.y,
|
segment[1][1] - suggestedBinding.element.y,
|
||||||
);
|
);
|
||||||
context.stroke();
|
context.stroke();
|
||||||
});
|
});
|
||||||
@@ -358,14 +386,17 @@ const renderBindingHighlightForBindableElement_simple = (
|
|||||||
curves.forEach((curve) => {
|
curves.forEach((curve) => {
|
||||||
const [start, control1, control2, end] = curve;
|
const [start, control1, control2, end] = curve;
|
||||||
context.beginPath();
|
context.beginPath();
|
||||||
context.moveTo(start[0] - element.x, start[1] - element.y);
|
context.moveTo(
|
||||||
|
start[0] - suggestedBinding.element.x,
|
||||||
|
start[1] - suggestedBinding.element.y,
|
||||||
|
);
|
||||||
context.bezierCurveTo(
|
context.bezierCurveTo(
|
||||||
control1[0] - element.x,
|
control1[0] - suggestedBinding.element.x,
|
||||||
control1[1] - element.y,
|
control1[1] - suggestedBinding.element.y,
|
||||||
control2[0] - element.x,
|
control2[0] - suggestedBinding.element.x,
|
||||||
control2[1] - element.y,
|
control2[1] - suggestedBinding.element.y,
|
||||||
end[0] - element.x,
|
end[0] - suggestedBinding.element.x,
|
||||||
end[1] - element.y,
|
end[1] - suggestedBinding.element.y,
|
||||||
);
|
);
|
||||||
context.stroke();
|
context.stroke();
|
||||||
});
|
});
|
||||||
@@ -378,6 +409,133 @@ const renderBindingHighlightForBindableElement_simple = (
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
isFrameLikeElement(suggestedBinding.element) ||
|
||||||
|
isBindableElement(suggestedBinding.element)
|
||||||
|
) {
|
||||||
|
// Draw midpoint indicators
|
||||||
|
const linearElement = appState.selectedLinearElement;
|
||||||
|
const arrow =
|
||||||
|
linearElement?.elementId &&
|
||||||
|
LinearElementEditor.getElement(linearElement?.elementId, elementsMap);
|
||||||
|
const insideBindable =
|
||||||
|
pointerCoords &&
|
||||||
|
arrow &&
|
||||||
|
hitElementItself({
|
||||||
|
point: pointerCoords,
|
||||||
|
element: suggestedBinding.element,
|
||||||
|
elementsMap,
|
||||||
|
threshold: 0,
|
||||||
|
overrideShouldTestInside: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!insideBindable || isElbowArrow(arrow)) {
|
||||||
|
context.save();
|
||||||
|
context.translate(suggestedBinding.element.x, suggestedBinding.element.y);
|
||||||
|
|
||||||
|
const midpointRadius = 5 / appState.zoom.value;
|
||||||
|
const center = elementCenterPoint(suggestedBinding.element, elementsMap);
|
||||||
|
|
||||||
|
let midpoints: LocalPoint[];
|
||||||
|
if (suggestedBinding.element.type === "diamond") {
|
||||||
|
const center = elementCenterPoint(
|
||||||
|
suggestedBinding.element,
|
||||||
|
elementsMap,
|
||||||
|
);
|
||||||
|
midpoints = getDiamondBaseCorners(suggestedBinding.element).map(
|
||||||
|
(curve) => {
|
||||||
|
const point = bezierEquation(curve, 0.5);
|
||||||
|
const rotatedPoint = pointRotateRads(
|
||||||
|
point,
|
||||||
|
center,
|
||||||
|
suggestedBinding.element.angle,
|
||||||
|
);
|
||||||
|
|
||||||
|
return pointFrom<LocalPoint>(
|
||||||
|
rotatedPoint[0] - suggestedBinding.element.x,
|
||||||
|
rotatedPoint[1] - suggestedBinding.element.y,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const basePoints = [
|
||||||
|
{
|
||||||
|
x: suggestedBinding.element.width,
|
||||||
|
y: suggestedBinding.element.height / 2,
|
||||||
|
}, // RIGHT
|
||||||
|
{
|
||||||
|
x: suggestedBinding.element.width / 2,
|
||||||
|
y: suggestedBinding.element.height,
|
||||||
|
}, // BOTTOM
|
||||||
|
{ x: 0, y: suggestedBinding.element.height / 2 }, // LEFT
|
||||||
|
{ x: suggestedBinding.element.width / 2, y: 0 }, // TOP
|
||||||
|
];
|
||||||
|
midpoints = basePoints.map((point) => {
|
||||||
|
const globalPoint = pointFrom<GlobalPoint>(
|
||||||
|
point.x + suggestedBinding.element.x,
|
||||||
|
point.y + suggestedBinding.element.y,
|
||||||
|
);
|
||||||
|
const rotatedPoint = pointRotateRads(
|
||||||
|
globalPoint,
|
||||||
|
center,
|
||||||
|
suggestedBinding.element.angle,
|
||||||
|
);
|
||||||
|
return pointFrom<LocalPoint>(
|
||||||
|
rotatedPoint[0] - suggestedBinding.element.x,
|
||||||
|
rotatedPoint[1] - suggestedBinding.element.y,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const highlightedPoint =
|
||||||
|
suggestedBinding.midPoint &&
|
||||||
|
pointFrom<LocalPoint>(
|
||||||
|
suggestedBinding.midPoint[0] - suggestedBinding.element.x,
|
||||||
|
suggestedBinding.midPoint[1] - suggestedBinding.element.y,
|
||||||
|
);
|
||||||
|
|
||||||
|
const target = [HEADING_RIGHT, HEADING_DOWN, HEADING_LEFT, HEADING_UP];
|
||||||
|
midpoints.forEach((midpoint, idx) => {
|
||||||
|
const isHighlighted =
|
||||||
|
highlightedPoint &&
|
||||||
|
compareHeading(
|
||||||
|
headingForPoint(
|
||||||
|
pointRotateRads(
|
||||||
|
pointFrom<GlobalPoint>(
|
||||||
|
highlightedPoint[0] + suggestedBinding.element.x,
|
||||||
|
highlightedPoint[1] + suggestedBinding.element.y,
|
||||||
|
),
|
||||||
|
center,
|
||||||
|
suggestedBinding.element.angle as Radians,
|
||||||
|
),
|
||||||
|
center,
|
||||||
|
),
|
||||||
|
target[idx],
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!isHighlighted) {
|
||||||
|
context.fillStyle =
|
||||||
|
appState.theme === THEME.DARK
|
||||||
|
? `rgba(0, 0, 0, 0.5)`
|
||||||
|
: `rgba(65, 65, 65, 0.4)`;
|
||||||
|
context.beginPath();
|
||||||
|
context.arc(midpoint[0], midpoint[1], midpointRadius, 0, 2 * Math.PI);
|
||||||
|
context.fill();
|
||||||
|
} else {
|
||||||
|
context.fillStyle =
|
||||||
|
appState.theme === THEME.DARK
|
||||||
|
? `rgba(3, 93, 161, 1)`
|
||||||
|
: `rgba(106, 189, 252, 1)`;
|
||||||
|
|
||||||
|
context.beginPath();
|
||||||
|
context.arc(midpoint[0], midpoint[1], midpointRadius, 0, 2 * Math.PI);
|
||||||
|
context.fill();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
context.restore();
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderBindingHighlightForBindableElement_complex = (
|
const renderBindingHighlightForBindableElement_complex = (
|
||||||
@@ -631,6 +789,78 @@ const renderBindingHighlightForBindableElement_complex = (
|
|||||||
context.fill();
|
context.fill();
|
||||||
|
|
||||||
context.restore();
|
context.restore();
|
||||||
|
|
||||||
|
// Draw midpoint indicators
|
||||||
|
context.save();
|
||||||
|
context.translate(
|
||||||
|
element.x + appState.scrollX,
|
||||||
|
element.y + appState.scrollY,
|
||||||
|
);
|
||||||
|
|
||||||
|
const midpointRadius = 5 / appState.zoom.value;
|
||||||
|
const cutoutPadding = 5 / appState.zoom.value;
|
||||||
|
const cutoutRadius = midpointRadius + cutoutPadding;
|
||||||
|
|
||||||
|
let midpoints;
|
||||||
|
if (element.type === "diamond") {
|
||||||
|
const [, curves] = deconstructDiamondElement(element);
|
||||||
|
const center = elementCenterPoint(element, allElementsMap);
|
||||||
|
|
||||||
|
midpoints = curves.map((curve) => {
|
||||||
|
const point = bezierEquation(curve, 0.5);
|
||||||
|
const rotatedPoint = pointRotateRads(point, center, element.angle);
|
||||||
|
return {
|
||||||
|
x: rotatedPoint[0] - element.x,
|
||||||
|
y: rotatedPoint[1] - element.y,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const center = elementCenterPoint(element, allElementsMap);
|
||||||
|
const basePoints = [
|
||||||
|
{ x: element.width / 2, y: 0 }, // TOP
|
||||||
|
{ x: element.width, y: element.height / 2 }, // RIGHT
|
||||||
|
{ x: element.width / 2, y: element.height }, // BOTTOM
|
||||||
|
{ x: 0, y: element.height / 2 }, // LEFT
|
||||||
|
];
|
||||||
|
midpoints = basePoints.map((point) => {
|
||||||
|
const globalPoint = pointFrom<GlobalPoint>(
|
||||||
|
point.x + element.x,
|
||||||
|
point.y + element.y,
|
||||||
|
);
|
||||||
|
const rotatedPoint = pointRotateRads(
|
||||||
|
globalPoint,
|
||||||
|
center,
|
||||||
|
element.angle,
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
x: rotatedPoint[0] - element.x,
|
||||||
|
y: rotatedPoint[1] - element.y,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear cutouts around midpoints
|
||||||
|
midpoints.forEach((midpoint) => {
|
||||||
|
context.clearRect(
|
||||||
|
midpoint.x - cutoutRadius,
|
||||||
|
midpoint.y - cutoutRadius,
|
||||||
|
cutoutRadius * 2,
|
||||||
|
cutoutRadius * 2,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
context.fillStyle =
|
||||||
|
appState.theme === THEME.DARK
|
||||||
|
? `rgba(3, 93, 161, ${opacity})`
|
||||||
|
: `rgba(106, 189, 252, ${opacity})`;
|
||||||
|
|
||||||
|
midpoints.forEach((midpoint) => {
|
||||||
|
context.beginPath();
|
||||||
|
context.arc(midpoint.x, midpoint.y, midpointRadius, 0, 2 * Math.PI);
|
||||||
|
context.fill();
|
||||||
|
});
|
||||||
|
|
||||||
|
context.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -641,17 +871,21 @@ const renderBindingHighlightForBindableElement_complex = (
|
|||||||
const renderBindingHighlightForBindableElement = (
|
const renderBindingHighlightForBindableElement = (
|
||||||
app: AppClassProperties,
|
app: AppClassProperties,
|
||||||
context: CanvasRenderingContext2D,
|
context: CanvasRenderingContext2D,
|
||||||
element: ExcalidrawBindableElement,
|
suggestedBinding: AppState["suggestedBinding"],
|
||||||
allElementsMap: NonDeletedSceneElementsMap,
|
allElementsMap: NonDeletedSceneElementsMap,
|
||||||
appState: InteractiveCanvasAppState,
|
appState: InteractiveCanvasAppState,
|
||||||
deltaTime: number,
|
deltaTime: number,
|
||||||
state?: { runtime: number },
|
state?: { runtime: number },
|
||||||
) => {
|
) => {
|
||||||
|
if (suggestedBinding === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (getFeatureFlag("COMPLEX_BINDINGS")) {
|
if (getFeatureFlag("COMPLEX_BINDINGS")) {
|
||||||
return renderBindingHighlightForBindableElement_complex(
|
return renderBindingHighlightForBindableElement_complex(
|
||||||
app,
|
app,
|
||||||
context,
|
context,
|
||||||
element,
|
suggestedBinding.element,
|
||||||
allElementsMap,
|
allElementsMap,
|
||||||
appState,
|
appState,
|
||||||
deltaTime,
|
deltaTime,
|
||||||
@@ -661,11 +895,18 @@ const renderBindingHighlightForBindableElement = (
|
|||||||
|
|
||||||
context.save();
|
context.save();
|
||||||
context.translate(appState.scrollX, appState.scrollY);
|
context.translate(appState.scrollX, appState.scrollY);
|
||||||
|
const pointerCoords = app.lastPointerMoveCoords
|
||||||
|
? pointFrom<GlobalPoint>(
|
||||||
|
app.lastPointerMoveCoords.x,
|
||||||
|
app.lastPointerMoveCoords.y,
|
||||||
|
)
|
||||||
|
: null;
|
||||||
renderBindingHighlightForBindableElement_simple(
|
renderBindingHighlightForBindableElement_simple(
|
||||||
context,
|
context,
|
||||||
element,
|
suggestedBinding,
|
||||||
allElementsMap,
|
allElementsMap,
|
||||||
appState,
|
appState,
|
||||||
|
pointerCoords,
|
||||||
);
|
);
|
||||||
context.restore();
|
context.restore();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -227,7 +227,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
"strokeWidth": 2,
|
"strokeWidth": 2,
|
||||||
"type": "arrow",
|
"type": "arrow",
|
||||||
"updated": 1,
|
"updated": 1,
|
||||||
"version": 34,
|
"version": 33,
|
||||||
"width": "94.00000",
|
"width": "94.00000",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
"y": 0,
|
"y": 0,
|
||||||
@@ -334,7 +334,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
],
|
],
|
||||||
"mode": "orbit",
|
"mode": "orbit",
|
||||||
},
|
},
|
||||||
"height": "74.35962",
|
"height": "105.58873",
|
||||||
"points": [
|
"points": [
|
||||||
[
|
[
|
||||||
0,
|
0,
|
||||||
@@ -342,50 +342,50 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
],
|
],
|
||||||
[
|
[
|
||||||
88,
|
88,
|
||||||
"74.35962",
|
"105.58873",
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
"startBinding": {
|
"startBinding": {
|
||||||
"elementId": "id0",
|
"elementId": "id0",
|
||||||
"fixedPoint": [
|
"fixedPoint": [
|
||||||
"0.63636",
|
1,
|
||||||
"0.63636",
|
"0.50010",
|
||||||
],
|
],
|
||||||
"mode": "orbit",
|
"mode": "orbit",
|
||||||
},
|
},
|
||||||
"version": 33,
|
"version": 32,
|
||||||
"width": 88,
|
"width": 88,
|
||||||
},
|
},
|
||||||
"inserted": {
|
"inserted": {
|
||||||
"endBinding": {
|
"endBinding": {
|
||||||
"elementId": "id1",
|
"elementId": "id1",
|
||||||
"fixedPoint": [
|
"fixedPoint": [
|
||||||
"0.39746",
|
0,
|
||||||
"0.60254",
|
"0.50010",
|
||||||
],
|
],
|
||||||
"mode": "orbit",
|
"mode": "orbit",
|
||||||
},
|
},
|
||||||
"height": "1.66245",
|
"height": "0.00000",
|
||||||
"points": [
|
"points": [
|
||||||
[
|
[
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
88,
|
"88.00000",
|
||||||
"-1.66245",
|
"0.00000",
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
"startBinding": {
|
"startBinding": {
|
||||||
"elementId": "id0",
|
"elementId": "id0",
|
||||||
"fixedPoint": [
|
"fixedPoint": [
|
||||||
"0.63636",
|
1,
|
||||||
"0.63636",
|
"0.50010",
|
||||||
],
|
],
|
||||||
"mode": "orbit",
|
"mode": "orbit",
|
||||||
},
|
},
|
||||||
"version": 30,
|
"version": 29,
|
||||||
"width": 88,
|
"width": "88.00000",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -440,13 +440,13 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
],
|
],
|
||||||
],
|
],
|
||||||
"startBinding": null,
|
"startBinding": null,
|
||||||
"version": 34,
|
"version": 33,
|
||||||
"width": "94.00000",
|
"width": "94.00000",
|
||||||
"x": 0,
|
"x": 0,
|
||||||
"y": 0,
|
"y": 0,
|
||||||
},
|
},
|
||||||
"inserted": {
|
"inserted": {
|
||||||
"height": "74.35962",
|
"height": "105.58873",
|
||||||
"points": [
|
"points": [
|
||||||
[
|
[
|
||||||
0,
|
0,
|
||||||
@@ -454,21 +454,21 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
],
|
],
|
||||||
[
|
[
|
||||||
88,
|
88,
|
||||||
"74.35962",
|
"105.58873",
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
"startBinding": {
|
"startBinding": {
|
||||||
"elementId": "id0",
|
"elementId": "id0",
|
||||||
"fixedPoint": [
|
"fixedPoint": [
|
||||||
"0.63636",
|
1,
|
||||||
"0.63636",
|
"0.50010",
|
||||||
],
|
],
|
||||||
"mode": "orbit",
|
"mode": "orbit",
|
||||||
},
|
},
|
||||||
"version": 33,
|
"version": 32,
|
||||||
"width": 88,
|
"width": 88,
|
||||||
"x": 6,
|
"x": 6,
|
||||||
"y": "46.67801",
|
"y": "7.09000",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -854,7 +854,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
"strokeWidth": 2,
|
"strokeWidth": 2,
|
||||||
"type": "arrow",
|
"type": "arrow",
|
||||||
"updated": 1,
|
"updated": 1,
|
||||||
"version": 26,
|
"version": 30,
|
||||||
"width": 100,
|
"width": 100,
|
||||||
"x": 150,
|
"x": 150,
|
||||||
"y": 0,
|
"y": 0,
|
||||||
@@ -904,60 +904,60 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
"id4": {
|
"id4": {
|
||||||
"deleted": {
|
"deleted": {
|
||||||
"endBinding": null,
|
"endBinding": null,
|
||||||
"height": "5.28000",
|
"height": "0.00293",
|
||||||
"points": [
|
"points": [
|
||||||
[
|
[
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
-44,
|
"-44.00000",
|
||||||
"-5.28000",
|
"-0.00293",
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
"startBinding": {
|
"startBinding": {
|
||||||
"elementId": "id0",
|
"elementId": "id0",
|
||||||
"fixedPoint": [
|
"fixedPoint": [
|
||||||
"0.63636",
|
1,
|
||||||
"0.63636",
|
"0.50010",
|
||||||
],
|
],
|
||||||
"mode": "orbit",
|
"mode": "orbit",
|
||||||
},
|
},
|
||||||
"version": 25,
|
"version": 29,
|
||||||
"width": 44,
|
"width": "44.00000",
|
||||||
"y": "5.28000",
|
"y": "0.00293",
|
||||||
},
|
},
|
||||||
"inserted": {
|
"inserted": {
|
||||||
"endBinding": {
|
"endBinding": {
|
||||||
"elementId": "id1",
|
"elementId": "id1",
|
||||||
"fixedPoint": [
|
"fixedPoint": [
|
||||||
"0.41067",
|
0,
|
||||||
"0.58933",
|
"0.50010",
|
||||||
],
|
],
|
||||||
"mode": "orbit",
|
"mode": "orbit",
|
||||||
},
|
},
|
||||||
"height": "9.80848",
|
"height": 0,
|
||||||
"points": [
|
"points": [
|
||||||
[
|
[
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"47.06697",
|
"6.00000",
|
||||||
"9.80848",
|
0,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
"startBinding": {
|
"startBinding": {
|
||||||
"elementId": "id0",
|
"elementId": "id0",
|
||||||
"fixedPoint": [
|
"fixedPoint": [
|
||||||
"0.63636",
|
1,
|
||||||
"0.63636",
|
"0.50010",
|
||||||
],
|
],
|
||||||
"mode": "orbit",
|
"mode": "orbit",
|
||||||
},
|
},
|
||||||
"version": 24,
|
"version": 27,
|
||||||
"width": "47.06697",
|
"width": "6.00000",
|
||||||
"y": "-0.87545",
|
"y": "0.01000",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -1004,35 +1004,35 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
],
|
],
|
||||||
],
|
],
|
||||||
"startBinding": null,
|
"startBinding": null,
|
||||||
"version": 26,
|
"version": 30,
|
||||||
"width": 100,
|
"width": 100,
|
||||||
"x": 150,
|
"x": 150,
|
||||||
"y": 0,
|
"y": 0,
|
||||||
},
|
},
|
||||||
"inserted": {
|
"inserted": {
|
||||||
"height": "5.28000",
|
"height": "0.00293",
|
||||||
"points": [
|
"points": [
|
||||||
[
|
[
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
-44,
|
"-44.00000",
|
||||||
"-5.28000",
|
"-0.00293",
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
"startBinding": {
|
"startBinding": {
|
||||||
"elementId": "id0",
|
"elementId": "id0",
|
||||||
"fixedPoint": [
|
"fixedPoint": [
|
||||||
"0.63636",
|
1,
|
||||||
"0.63636",
|
"0.50010",
|
||||||
],
|
],
|
||||||
"mode": "orbit",
|
"mode": "orbit",
|
||||||
},
|
},
|
||||||
"version": 25,
|
"version": 29,
|
||||||
"width": 44,
|
"width": "44.00000",
|
||||||
"x": 144,
|
"x": 144,
|
||||||
"y": "5.28000",
|
"y": "0.00293",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -2390,7 +2390,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
"endBinding": {
|
"endBinding": {
|
||||||
"elementId": "id1",
|
"elementId": "id1",
|
||||||
"fixedPoint": [
|
"fixedPoint": [
|
||||||
"0.50010",
|
0,
|
||||||
"0.50010",
|
"0.50010",
|
||||||
],
|
],
|
||||||
"mode": "orbit",
|
"mode": "orbit",
|
||||||
@@ -2398,7 +2398,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
"fillStyle": "solid",
|
"fillStyle": "solid",
|
||||||
"frameId": null,
|
"frameId": null,
|
||||||
"groupIds": [],
|
"groupIds": [],
|
||||||
"height": "398.76619",
|
"height": "439.13521",
|
||||||
"id": "id4",
|
"id": "id4",
|
||||||
"index": "a2",
|
"index": "a2",
|
||||||
"isDeleted": false,
|
"isDeleted": false,
|
||||||
@@ -2413,7 +2413,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
],
|
],
|
||||||
[
|
[
|
||||||
488,
|
488,
|
||||||
"-398.76619",
|
"-439.13521",
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
"roughness": 1,
|
"roughness": 1,
|
||||||
@@ -2434,16 +2434,16 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
"strokeWidth": 2,
|
"strokeWidth": 2,
|
||||||
"type": "arrow",
|
"type": "arrow",
|
||||||
"updated": 1,
|
"updated": 1,
|
||||||
"version": 12,
|
"version": 13,
|
||||||
"width": 488,
|
"width": 488,
|
||||||
"x": 6,
|
"x": 6,
|
||||||
"y": "-4.89286",
|
"y": "-5.38920",
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`history > multiplayer undo/redo > conflicts in arrows and their bindable elements > should update bound element points when rectangle was remotely moved and arrow is added back through the history > [end of test] number of elements 1`] = `3`;
|
exports[`history > multiplayer undo/redo > conflicts in arrows and their bindable elements > should update bound element points when rectangle was remotely moved and arrow is added back through the history > [end of test] number of elements 1`] = `3`;
|
||||||
|
|
||||||
exports[`history > multiplayer undo/redo > conflicts in arrows and their bindable elements > should update bound element points when rectangle was remotely moved and arrow is added back through the history > [end of test] number of renders 1`] = `11`;
|
exports[`history > multiplayer undo/redo > conflicts in arrows and their bindable elements > should update bound element points when rectangle was remotely moved and arrow is added back through the history > [end of test] number of renders 1`] = `12`;
|
||||||
|
|
||||||
exports[`history > multiplayer undo/redo > conflicts in arrows and their bindable elements > should update bound element points when rectangle was remotely moved and arrow is added back through the history > [end of test] redo stack 1`] = `[]`;
|
exports[`history > multiplayer undo/redo > conflicts in arrows and their bindable elements > should update bound element points when rectangle was remotely moved and arrow is added back through the history > [end of test] redo stack 1`] = `[]`;
|
||||||
|
|
||||||
@@ -2558,7 +2558,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
"endBinding": {
|
"endBinding": {
|
||||||
"elementId": "id1",
|
"elementId": "id1",
|
||||||
"fixedPoint": [
|
"fixedPoint": [
|
||||||
"0.50010",
|
0,
|
||||||
"0.50010",
|
"0.50010",
|
||||||
],
|
],
|
||||||
"mode": "orbit",
|
"mode": "orbit",
|
||||||
@@ -2566,7 +2566,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
"fillStyle": "solid",
|
"fillStyle": "solid",
|
||||||
"frameId": null,
|
"frameId": null,
|
||||||
"groupIds": [],
|
"groupIds": [],
|
||||||
"height": "398.76619",
|
"height": "439.13521",
|
||||||
"index": "a2",
|
"index": "a2",
|
||||||
"isDeleted": false,
|
"isDeleted": false,
|
||||||
"link": null,
|
"link": null,
|
||||||
@@ -2579,7 +2579,7 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
],
|
],
|
||||||
[
|
[
|
||||||
488,
|
488,
|
||||||
"-398.76619",
|
"-439.13521",
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
"roughness": 1,
|
"roughness": 1,
|
||||||
@@ -2599,14 +2599,14 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
|||||||
"strokeStyle": "solid",
|
"strokeStyle": "solid",
|
||||||
"strokeWidth": 2,
|
"strokeWidth": 2,
|
||||||
"type": "arrow",
|
"type": "arrow",
|
||||||
"version": 12,
|
"version": 13,
|
||||||
"width": 488,
|
"width": 488,
|
||||||
"x": 6,
|
"x": 6,
|
||||||
"y": "-4.89286",
|
"y": "-5.38920",
|
||||||
},
|
},
|
||||||
"inserted": {
|
"inserted": {
|
||||||
"isDeleted": true,
|
"isDeleted": true,
|
||||||
"version": 9,
|
"version": 10,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -16375,7 +16375,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"endBinding": {
|
"endBinding": {
|
||||||
"elementId": "id2",
|
"elementId": "id2",
|
||||||
"fixedPoint": [
|
"fixedPoint": [
|
||||||
"0.50010",
|
0,
|
||||||
"0.50010",
|
"0.50010",
|
||||||
],
|
],
|
||||||
"mode": "orbit",
|
"mode": "orbit",
|
||||||
@@ -16383,7 +16383,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"fillStyle": "solid",
|
"fillStyle": "solid",
|
||||||
"frameId": null,
|
"frameId": null,
|
||||||
"groupIds": [],
|
"groupIds": [],
|
||||||
"height": "0.00092",
|
"height": "0.00004",
|
||||||
"id": "id13",
|
"id": "id13",
|
||||||
"index": "a3",
|
"index": "a3",
|
||||||
"isDeleted": false,
|
"isDeleted": false,
|
||||||
@@ -16398,7 +16398,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
],
|
],
|
||||||
[
|
[
|
||||||
"88.00000",
|
"88.00000",
|
||||||
"0.00092",
|
"0.00004",
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
"roughness": 1,
|
"roughness": 1,
|
||||||
@@ -16409,7 +16409,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"startBinding": {
|
"startBinding": {
|
||||||
"elementId": "id0",
|
"elementId": "id0",
|
||||||
"fixedPoint": [
|
"fixedPoint": [
|
||||||
"0.50010",
|
1,
|
||||||
"0.50010",
|
"0.50010",
|
||||||
],
|
],
|
||||||
"mode": "orbit",
|
"mode": "orbit",
|
||||||
@@ -16422,7 +16422,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"version": 12,
|
"version": 12,
|
||||||
"width": "88.00000",
|
"width": "88.00000",
|
||||||
"x": 6,
|
"x": 6,
|
||||||
"y": "0.00849",
|
"y": "0.00996",
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -16458,7 +16458,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"endBinding": {
|
"endBinding": {
|
||||||
"elementId": "id2",
|
"elementId": "id2",
|
||||||
"fixedPoint": [
|
"fixedPoint": [
|
||||||
"0.50010",
|
0,
|
||||||
"0.50010",
|
"0.50010",
|
||||||
],
|
],
|
||||||
"mode": "orbit",
|
"mode": "orbit",
|
||||||
@@ -16467,7 +16467,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"startBinding": {
|
"startBinding": {
|
||||||
"elementId": "id0",
|
"elementId": "id0",
|
||||||
"fixedPoint": [
|
"fixedPoint": [
|
||||||
"0.50010",
|
1,
|
||||||
"0.50010",
|
"0.50010",
|
||||||
],
|
],
|
||||||
"mode": "orbit",
|
"mode": "orbit",
|
||||||
@@ -16478,7 +16478,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"endBinding": {
|
"endBinding": {
|
||||||
"elementId": "id2",
|
"elementId": "id2",
|
||||||
"fixedPoint": [
|
"fixedPoint": [
|
||||||
"0.50010",
|
0,
|
||||||
"0.50010",
|
"0.50010",
|
||||||
],
|
],
|
||||||
"mode": "orbit",
|
"mode": "orbit",
|
||||||
@@ -16487,7 +16487,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"startBinding": {
|
"startBinding": {
|
||||||
"elementId": "id0",
|
"elementId": "id0",
|
||||||
"fixedPoint": [
|
"fixedPoint": [
|
||||||
"0.50010",
|
1,
|
||||||
"0.50010",
|
"0.50010",
|
||||||
],
|
],
|
||||||
"mode": "orbit",
|
"mode": "orbit",
|
||||||
@@ -16793,7 +16793,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"endBinding": {
|
"endBinding": {
|
||||||
"elementId": "id2",
|
"elementId": "id2",
|
||||||
"fixedPoint": [
|
"fixedPoint": [
|
||||||
"0.50010",
|
0,
|
||||||
"0.50010",
|
"0.50010",
|
||||||
],
|
],
|
||||||
"mode": "orbit",
|
"mode": "orbit",
|
||||||
@@ -16801,7 +16801,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"fillStyle": "solid",
|
"fillStyle": "solid",
|
||||||
"frameId": null,
|
"frameId": null,
|
||||||
"groupIds": [],
|
"groupIds": [],
|
||||||
"height": "0.00611",
|
"height": "0.00936",
|
||||||
"index": "a3",
|
"index": "a3",
|
||||||
"isDeleted": false,
|
"isDeleted": false,
|
||||||
"link": null,
|
"link": null,
|
||||||
@@ -16814,7 +16814,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
],
|
],
|
||||||
[
|
[
|
||||||
88,
|
88,
|
||||||
"0.00611",
|
"0.00936",
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
"roughness": 1,
|
"roughness": 1,
|
||||||
@@ -16825,7 +16825,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"startBinding": {
|
"startBinding": {
|
||||||
"elementId": "id0",
|
"elementId": "id0",
|
||||||
"fixedPoint": [
|
"fixedPoint": [
|
||||||
"0.50010",
|
1,
|
||||||
"0.50010",
|
"0.50010",
|
||||||
],
|
],
|
||||||
"mode": "orbit",
|
"mode": "orbit",
|
||||||
@@ -17126,7 +17126,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"endBinding": {
|
"endBinding": {
|
||||||
"elementId": "id2",
|
"elementId": "id2",
|
||||||
"fixedPoint": [
|
"fixedPoint": [
|
||||||
"0.50010",
|
0,
|
||||||
"0.50010",
|
"0.50010",
|
||||||
],
|
],
|
||||||
"mode": "orbit",
|
"mode": "orbit",
|
||||||
@@ -17134,7 +17134,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"fillStyle": "solid",
|
"fillStyle": "solid",
|
||||||
"frameId": null,
|
"frameId": null,
|
||||||
"groupIds": [],
|
"groupIds": [],
|
||||||
"height": "0.00092",
|
"height": "0.00004",
|
||||||
"id": "id13",
|
"id": "id13",
|
||||||
"index": "a3",
|
"index": "a3",
|
||||||
"isDeleted": false,
|
"isDeleted": false,
|
||||||
@@ -17149,7 +17149,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
],
|
],
|
||||||
[
|
[
|
||||||
"88.00000",
|
"88.00000",
|
||||||
"0.00092",
|
"0.00004",
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
"roughness": 1,
|
"roughness": 1,
|
||||||
@@ -17160,7 +17160,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"startBinding": {
|
"startBinding": {
|
||||||
"elementId": "id0",
|
"elementId": "id0",
|
||||||
"fixedPoint": [
|
"fixedPoint": [
|
||||||
"0.50010",
|
1,
|
||||||
"0.50010",
|
"0.50010",
|
||||||
],
|
],
|
||||||
"mode": "orbit",
|
"mode": "orbit",
|
||||||
@@ -17173,7 +17173,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"version": 12,
|
"version": 12,
|
||||||
"width": "88.00000",
|
"width": "88.00000",
|
||||||
"x": 6,
|
"x": 6,
|
||||||
"y": "0.00849",
|
"y": "0.00996",
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -17434,7 +17434,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"endBinding": {
|
"endBinding": {
|
||||||
"elementId": "id2",
|
"elementId": "id2",
|
||||||
"fixedPoint": [
|
"fixedPoint": [
|
||||||
"0.50010",
|
0,
|
||||||
"0.50010",
|
"0.50010",
|
||||||
],
|
],
|
||||||
"mode": "orbit",
|
"mode": "orbit",
|
||||||
@@ -17442,7 +17442,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"fillStyle": "solid",
|
"fillStyle": "solid",
|
||||||
"frameId": null,
|
"frameId": null,
|
||||||
"groupIds": [],
|
"groupIds": [],
|
||||||
"height": "0.00092",
|
"height": "0.00004",
|
||||||
"index": "a3",
|
"index": "a3",
|
||||||
"isDeleted": false,
|
"isDeleted": false,
|
||||||
"link": null,
|
"link": null,
|
||||||
@@ -17455,7 +17455,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
],
|
],
|
||||||
[
|
[
|
||||||
"88.00000",
|
"88.00000",
|
||||||
"0.00092",
|
"0.00004",
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
"roughness": 1,
|
"roughness": 1,
|
||||||
@@ -17466,7 +17466,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"startBinding": {
|
"startBinding": {
|
||||||
"elementId": "id0",
|
"elementId": "id0",
|
||||||
"fixedPoint": [
|
"fixedPoint": [
|
||||||
"0.50010",
|
1,
|
||||||
"0.50010",
|
"0.50010",
|
||||||
],
|
],
|
||||||
"mode": "orbit",
|
"mode": "orbit",
|
||||||
@@ -17478,7 +17478,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"version": 12,
|
"version": 12,
|
||||||
"width": "88.00000",
|
"width": "88.00000",
|
||||||
"x": 6,
|
"x": 6,
|
||||||
"y": "0.00849",
|
"y": "0.00996",
|
||||||
},
|
},
|
||||||
"inserted": {
|
"inserted": {
|
||||||
"isDeleted": true,
|
"isDeleted": true,
|
||||||
@@ -17775,7 +17775,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"endBinding": {
|
"endBinding": {
|
||||||
"elementId": "id2",
|
"elementId": "id2",
|
||||||
"fixedPoint": [
|
"fixedPoint": [
|
||||||
"0.50010",
|
0,
|
||||||
"0.50010",
|
"0.50010",
|
||||||
],
|
],
|
||||||
"mode": "orbit",
|
"mode": "orbit",
|
||||||
@@ -17783,7 +17783,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"fillStyle": "solid",
|
"fillStyle": "solid",
|
||||||
"frameId": null,
|
"frameId": null,
|
||||||
"groupIds": [],
|
"groupIds": [],
|
||||||
"height": "0.00092",
|
"height": "0.00004",
|
||||||
"id": "id13",
|
"id": "id13",
|
||||||
"index": "a3",
|
"index": "a3",
|
||||||
"isDeleted": false,
|
"isDeleted": false,
|
||||||
@@ -17798,7 +17798,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
],
|
],
|
||||||
[
|
[
|
||||||
"88.00000",
|
"88.00000",
|
||||||
"0.00092",
|
"0.00004",
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
"roughness": 1,
|
"roughness": 1,
|
||||||
@@ -17809,7 +17809,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"startBinding": {
|
"startBinding": {
|
||||||
"elementId": "id0",
|
"elementId": "id0",
|
||||||
"fixedPoint": [
|
"fixedPoint": [
|
||||||
"0.50010",
|
1,
|
||||||
"0.50010",
|
"0.50010",
|
||||||
],
|
],
|
||||||
"mode": "orbit",
|
"mode": "orbit",
|
||||||
@@ -17822,7 +17822,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"version": 12,
|
"version": 12,
|
||||||
"width": "88.00000",
|
"width": "88.00000",
|
||||||
"x": 6,
|
"x": 6,
|
||||||
"y": "0.00849",
|
"y": "0.00996",
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -18083,7 +18083,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"endBinding": {
|
"endBinding": {
|
||||||
"elementId": "id2",
|
"elementId": "id2",
|
||||||
"fixedPoint": [
|
"fixedPoint": [
|
||||||
"0.50010",
|
0,
|
||||||
"0.50010",
|
"0.50010",
|
||||||
],
|
],
|
||||||
"mode": "orbit",
|
"mode": "orbit",
|
||||||
@@ -18091,7 +18091,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"fillStyle": "solid",
|
"fillStyle": "solid",
|
||||||
"frameId": null,
|
"frameId": null,
|
||||||
"groupIds": [],
|
"groupIds": [],
|
||||||
"height": "0.00092",
|
"height": "0.00004",
|
||||||
"index": "a3",
|
"index": "a3",
|
||||||
"isDeleted": false,
|
"isDeleted": false,
|
||||||
"link": null,
|
"link": null,
|
||||||
@@ -18104,7 +18104,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
],
|
],
|
||||||
[
|
[
|
||||||
"88.00000",
|
"88.00000",
|
||||||
"0.00092",
|
"0.00004",
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
"roughness": 1,
|
"roughness": 1,
|
||||||
@@ -18115,7 +18115,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"startBinding": {
|
"startBinding": {
|
||||||
"elementId": "id0",
|
"elementId": "id0",
|
||||||
"fixedPoint": [
|
"fixedPoint": [
|
||||||
"0.50010",
|
1,
|
||||||
"0.50010",
|
"0.50010",
|
||||||
],
|
],
|
||||||
"mode": "orbit",
|
"mode": "orbit",
|
||||||
@@ -18127,7 +18127,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"version": 12,
|
"version": 12,
|
||||||
"width": "88.00000",
|
"width": "88.00000",
|
||||||
"x": 6,
|
"x": 6,
|
||||||
"y": "0.00849",
|
"y": "0.00996",
|
||||||
},
|
},
|
||||||
"inserted": {
|
"inserted": {
|
||||||
"isDeleted": true,
|
"isDeleted": true,
|
||||||
@@ -18422,7 +18422,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"endBinding": {
|
"endBinding": {
|
||||||
"elementId": "id2",
|
"elementId": "id2",
|
||||||
"fixedPoint": [
|
"fixedPoint": [
|
||||||
"0.50010",
|
0,
|
||||||
"0.50010",
|
"0.50010",
|
||||||
],
|
],
|
||||||
"mode": "orbit",
|
"mode": "orbit",
|
||||||
@@ -18430,7 +18430,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"fillStyle": "solid",
|
"fillStyle": "solid",
|
||||||
"frameId": null,
|
"frameId": null,
|
||||||
"groupIds": [],
|
"groupIds": [],
|
||||||
"height": "0.00092",
|
"height": "0.00004",
|
||||||
"id": "id13",
|
"id": "id13",
|
||||||
"index": "a3",
|
"index": "a3",
|
||||||
"isDeleted": false,
|
"isDeleted": false,
|
||||||
@@ -18445,7 +18445,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
],
|
],
|
||||||
[
|
[
|
||||||
"88.00000",
|
"88.00000",
|
||||||
"0.00092",
|
"0.00004",
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
"roughness": 1,
|
"roughness": 1,
|
||||||
@@ -18456,7 +18456,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"startBinding": {
|
"startBinding": {
|
||||||
"elementId": "id0",
|
"elementId": "id0",
|
||||||
"fixedPoint": [
|
"fixedPoint": [
|
||||||
"0.50010",
|
1,
|
||||||
"0.50010",
|
"0.50010",
|
||||||
],
|
],
|
||||||
"mode": "orbit",
|
"mode": "orbit",
|
||||||
@@ -18469,7 +18469,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"version": 12,
|
"version": 12,
|
||||||
"width": "88.00000",
|
"width": "88.00000",
|
||||||
"x": 6,
|
"x": 6,
|
||||||
"y": "0.00849",
|
"y": "0.00996",
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -18522,7 +18522,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"endBinding": {
|
"endBinding": {
|
||||||
"elementId": "id2",
|
"elementId": "id2",
|
||||||
"fixedPoint": [
|
"fixedPoint": [
|
||||||
"0.50010",
|
0,
|
||||||
"0.50010",
|
"0.50010",
|
||||||
],
|
],
|
||||||
"mode": "orbit",
|
"mode": "orbit",
|
||||||
@@ -18530,7 +18530,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"startBinding": {
|
"startBinding": {
|
||||||
"elementId": "id0",
|
"elementId": "id0",
|
||||||
"fixedPoint": [
|
"fixedPoint": [
|
||||||
"0.50010",
|
1,
|
||||||
"0.50010",
|
"0.50010",
|
||||||
],
|
],
|
||||||
"mode": "orbit",
|
"mode": "orbit",
|
||||||
@@ -18541,7 +18541,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"endBinding": {
|
"endBinding": {
|
||||||
"elementId": "id2",
|
"elementId": "id2",
|
||||||
"fixedPoint": [
|
"fixedPoint": [
|
||||||
"0.50010",
|
0,
|
||||||
"0.50010",
|
"0.50010",
|
||||||
],
|
],
|
||||||
"mode": "orbit",
|
"mode": "orbit",
|
||||||
@@ -18816,7 +18816,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"endBinding": {
|
"endBinding": {
|
||||||
"elementId": "id2",
|
"elementId": "id2",
|
||||||
"fixedPoint": [
|
"fixedPoint": [
|
||||||
"0.50010",
|
0,
|
||||||
"0.50010",
|
"0.50010",
|
||||||
],
|
],
|
||||||
"mode": "orbit",
|
"mode": "orbit",
|
||||||
@@ -18824,7 +18824,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"fillStyle": "solid",
|
"fillStyle": "solid",
|
||||||
"frameId": null,
|
"frameId": null,
|
||||||
"groupIds": [],
|
"groupIds": [],
|
||||||
"height": "0.00611",
|
"height": "0.00936",
|
||||||
"index": "a3",
|
"index": "a3",
|
||||||
"isDeleted": false,
|
"isDeleted": false,
|
||||||
"link": null,
|
"link": null,
|
||||||
@@ -18837,7 +18837,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
],
|
],
|
||||||
[
|
[
|
||||||
88,
|
88,
|
||||||
"0.00611",
|
"0.00936",
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
"roughness": 1,
|
"roughness": 1,
|
||||||
@@ -18848,7 +18848,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"startBinding": {
|
"startBinding": {
|
||||||
"elementId": "id0",
|
"elementId": "id0",
|
||||||
"fixedPoint": [
|
"fixedPoint": [
|
||||||
"0.50010",
|
1,
|
||||||
"0.50010",
|
"0.50010",
|
||||||
],
|
],
|
||||||
"mode": "orbit",
|
"mode": "orbit",
|
||||||
@@ -19177,7 +19177,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"endBinding": {
|
"endBinding": {
|
||||||
"elementId": "id2",
|
"elementId": "id2",
|
||||||
"fixedPoint": [
|
"fixedPoint": [
|
||||||
"0.50010",
|
0,
|
||||||
"0.50010",
|
"0.50010",
|
||||||
],
|
],
|
||||||
"mode": "orbit",
|
"mode": "orbit",
|
||||||
@@ -19185,7 +19185,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"fillStyle": "solid",
|
"fillStyle": "solid",
|
||||||
"frameId": null,
|
"frameId": null,
|
||||||
"groupIds": [],
|
"groupIds": [],
|
||||||
"height": "0.00092",
|
"height": "0.00004",
|
||||||
"id": "id13",
|
"id": "id13",
|
||||||
"index": "a3",
|
"index": "a3",
|
||||||
"isDeleted": false,
|
"isDeleted": false,
|
||||||
@@ -19200,7 +19200,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
],
|
],
|
||||||
[
|
[
|
||||||
"88.00000",
|
"88.00000",
|
||||||
"0.00092",
|
"0.00004",
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
"roughness": 1,
|
"roughness": 1,
|
||||||
@@ -19211,7 +19211,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"startBinding": {
|
"startBinding": {
|
||||||
"elementId": "id0",
|
"elementId": "id0",
|
||||||
"fixedPoint": [
|
"fixedPoint": [
|
||||||
"0.50010",
|
1,
|
||||||
"0.50010",
|
"0.50010",
|
||||||
],
|
],
|
||||||
"mode": "orbit",
|
"mode": "orbit",
|
||||||
@@ -19224,7 +19224,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"version": 13,
|
"version": 13,
|
||||||
"width": "88.00000",
|
"width": "88.00000",
|
||||||
"x": 6,
|
"x": 6,
|
||||||
"y": "0.00849",
|
"y": "0.00996",
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -19288,7 +19288,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"endBinding": {
|
"endBinding": {
|
||||||
"elementId": "id2",
|
"elementId": "id2",
|
||||||
"fixedPoint": [
|
"fixedPoint": [
|
||||||
"0.50010",
|
0,
|
||||||
"0.50010",
|
"0.50010",
|
||||||
],
|
],
|
||||||
"mode": "orbit",
|
"mode": "orbit",
|
||||||
@@ -19296,7 +19296,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"startBinding": {
|
"startBinding": {
|
||||||
"elementId": "id0",
|
"elementId": "id0",
|
||||||
"fixedPoint": [
|
"fixedPoint": [
|
||||||
"0.50010",
|
1,
|
||||||
"0.50010",
|
"0.50010",
|
||||||
],
|
],
|
||||||
"mode": "orbit",
|
"mode": "orbit",
|
||||||
@@ -19567,7 +19567,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"endBinding": {
|
"endBinding": {
|
||||||
"elementId": "id2",
|
"elementId": "id2",
|
||||||
"fixedPoint": [
|
"fixedPoint": [
|
||||||
"0.50010",
|
0,
|
||||||
"0.50010",
|
"0.50010",
|
||||||
],
|
],
|
||||||
"mode": "orbit",
|
"mode": "orbit",
|
||||||
@@ -19575,7 +19575,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"fillStyle": "solid",
|
"fillStyle": "solid",
|
||||||
"frameId": null,
|
"frameId": null,
|
||||||
"groupIds": [],
|
"groupIds": [],
|
||||||
"height": "0.00611",
|
"height": "0.00936",
|
||||||
"index": "a3",
|
"index": "a3",
|
||||||
"isDeleted": false,
|
"isDeleted": false,
|
||||||
"link": null,
|
"link": null,
|
||||||
@@ -19588,7 +19588,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
],
|
],
|
||||||
[
|
[
|
||||||
88,
|
88,
|
||||||
"0.00611",
|
"0.00936",
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
"roughness": 1,
|
"roughness": 1,
|
||||||
@@ -19599,7 +19599,7 @@ exports[`history > singleplayer undo/redo > should support bidirectional binding
|
|||||||
"startBinding": {
|
"startBinding": {
|
||||||
"elementId": "id0",
|
"elementId": "id0",
|
||||||
"fixedPoint": [
|
"fixedPoint": [
|
||||||
"0.50010",
|
1,
|
||||||
"0.50010",
|
"0.50010",
|
||||||
],
|
],
|
||||||
"mode": "orbit",
|
"mode": "orbit",
|
||||||
|
|||||||
@@ -182,14 +182,14 @@ exports[`move element > rectangles with binding arrow 7`] = `
|
|||||||
"elementId": "id3",
|
"elementId": "id3",
|
||||||
"fixedPoint": [
|
"fixedPoint": [
|
||||||
"-0.02000",
|
"-0.02000",
|
||||||
"0.44667",
|
"0.47904",
|
||||||
],
|
],
|
||||||
"mode": "orbit",
|
"mode": "orbit",
|
||||||
},
|
},
|
||||||
"fillStyle": "solid",
|
"fillStyle": "solid",
|
||||||
"frameId": null,
|
"frameId": null,
|
||||||
"groupIds": [],
|
"groupIds": [],
|
||||||
"height": "90.00000",
|
"height": "90.02554",
|
||||||
"id": "id6",
|
"id": "id6",
|
||||||
"index": "a2",
|
"index": "a2",
|
||||||
"isDeleted": false,
|
"isDeleted": false,
|
||||||
@@ -204,7 +204,7 @@ exports[`move element > rectangles with binding arrow 7`] = `
|
|||||||
],
|
],
|
||||||
[
|
[
|
||||||
89,
|
89,
|
||||||
"90.00000",
|
"90.02554",
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
"roughness": 1,
|
"roughness": 1,
|
||||||
@@ -217,7 +217,7 @@ exports[`move element > rectangles with binding arrow 7`] = `
|
|||||||
"elementId": "id0",
|
"elementId": "id0",
|
||||||
"fixedPoint": [
|
"fixedPoint": [
|
||||||
"1.06000",
|
"1.06000",
|
||||||
"0.46000",
|
"0.55687",
|
||||||
],
|
],
|
||||||
"mode": "orbit",
|
"mode": "orbit",
|
||||||
},
|
},
|
||||||
@@ -230,6 +230,6 @@ exports[`move element > rectangles with binding arrow 7`] = `
|
|||||||
"versionNonce": 271613161,
|
"versionNonce": 271613161,
|
||||||
"width": 89,
|
"width": 89,
|
||||||
"x": 106,
|
"x": 106,
|
||||||
"y": "46.00000",
|
"y": "55.68677",
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -1590,7 +1590,7 @@ describe("history", () => {
|
|||||||
expect(API.getUndoStack().length).toBe(5);
|
expect(API.getUndoStack().length).toBe(5);
|
||||||
expect(arrow.startBinding).toEqual({
|
expect(arrow.startBinding).toEqual({
|
||||||
elementId: rect1.id,
|
elementId: rect1.id,
|
||||||
fixedPoint: expect.arrayContaining([0.5001, 0.5001]),
|
fixedPoint: expect.arrayContaining([1, 0.5001]),
|
||||||
mode: "orbit",
|
mode: "orbit",
|
||||||
});
|
});
|
||||||
expect(arrow.endBinding).toEqual({
|
expect(arrow.endBinding).toEqual({
|
||||||
@@ -1613,7 +1613,7 @@ describe("history", () => {
|
|||||||
expect(API.getRedoStack().length).toBe(1);
|
expect(API.getRedoStack().length).toBe(1);
|
||||||
expect(arrow.startBinding).toEqual({
|
expect(arrow.startBinding).toEqual({
|
||||||
elementId: rect1.id,
|
elementId: rect1.id,
|
||||||
fixedPoint: expect.arrayContaining([0.5001, 0.5001]),
|
fixedPoint: expect.arrayContaining([1, 0.5001]),
|
||||||
mode: "orbit",
|
mode: "orbit",
|
||||||
});
|
});
|
||||||
expect(arrow.endBinding).toEqual({
|
expect(arrow.endBinding).toEqual({
|
||||||
@@ -1636,7 +1636,7 @@ describe("history", () => {
|
|||||||
expect(API.getRedoStack().length).toBe(0);
|
expect(API.getRedoStack().length).toBe(0);
|
||||||
expect(arrow.startBinding).toEqual({
|
expect(arrow.startBinding).toEqual({
|
||||||
elementId: rect1.id,
|
elementId: rect1.id,
|
||||||
fixedPoint: expect.arrayContaining([0.5001, 0.5001]),
|
fixedPoint: expect.arrayContaining([1, 0.5001]),
|
||||||
mode: "orbit",
|
mode: "orbit",
|
||||||
});
|
});
|
||||||
expect(arrow.endBinding).toEqual({
|
expect(arrow.endBinding).toEqual({
|
||||||
@@ -1667,7 +1667,7 @@ describe("history", () => {
|
|||||||
expect(API.getRedoStack().length).toBe(0);
|
expect(API.getRedoStack().length).toBe(0);
|
||||||
expect(arrow.startBinding).toEqual({
|
expect(arrow.startBinding).toEqual({
|
||||||
elementId: rect1.id,
|
elementId: rect1.id,
|
||||||
fixedPoint: expect.arrayContaining([0.5001, 0.5001]),
|
fixedPoint: expect.arrayContaining([1, 0.5001]),
|
||||||
mode: "orbit",
|
mode: "orbit",
|
||||||
});
|
});
|
||||||
expect(arrow.endBinding).toEqual({
|
expect(arrow.endBinding).toEqual({
|
||||||
@@ -1690,7 +1690,7 @@ describe("history", () => {
|
|||||||
expect(API.getRedoStack().length).toBe(1);
|
expect(API.getRedoStack().length).toBe(1);
|
||||||
expect(arrow.startBinding).toEqual({
|
expect(arrow.startBinding).toEqual({
|
||||||
elementId: rect1.id,
|
elementId: rect1.id,
|
||||||
fixedPoint: expect.arrayContaining([0.5001, 0.5001]),
|
fixedPoint: expect.arrayContaining([1, 0.5001]),
|
||||||
mode: "orbit",
|
mode: "orbit",
|
||||||
});
|
});
|
||||||
expect(arrow.endBinding).toEqual({
|
expect(arrow.endBinding).toEqual({
|
||||||
@@ -4510,20 +4510,20 @@ describe("history", () => {
|
|||||||
|
|
||||||
// create start binding
|
// create start binding
|
||||||
mouse.downAt(0, 0);
|
mouse.downAt(0, 0);
|
||||||
mouse.moveTo(0, 10);
|
mouse.moveTo(0, 3);
|
||||||
mouse.moveTo(0, 10);
|
mouse.moveTo(0, 3);
|
||||||
mouse.up();
|
mouse.up();
|
||||||
|
|
||||||
// create end binding
|
// create end binding
|
||||||
mouse.downAt(100, 0);
|
mouse.downAt(100, 0);
|
||||||
mouse.moveTo(100, 10);
|
mouse.moveTo(100, 3);
|
||||||
mouse.moveTo(100, 10);
|
mouse.moveTo(100, 3);
|
||||||
mouse.up();
|
mouse.up();
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
(h.elements[2] as ExcalidrawElbowArrowElement).startBinding
|
(h.elements[2] as ExcalidrawElbowArrowElement).startBinding
|
||||||
?.fixedPoint,
|
?.fixedPoint,
|
||||||
).not.toEqual([1, 0.5001]);
|
).toEqual([1, 0.5001]);
|
||||||
expect(
|
expect(
|
||||||
(h.elements[2] as ExcalidrawElbowArrowElement).startBinding?.mode,
|
(h.elements[2] as ExcalidrawElbowArrowElement).startBinding?.mode,
|
||||||
).toBe("orbit");
|
).toBe("orbit");
|
||||||
@@ -4623,12 +4623,12 @@ describe("history", () => {
|
|||||||
id: arrowId,
|
id: arrowId,
|
||||||
startBinding: expect.objectContaining({
|
startBinding: expect.objectContaining({
|
||||||
elementId: rect1.id,
|
elementId: rect1.id,
|
||||||
fixedPoint: [0.6363636363636364, 0.6363636363636364],
|
fixedPoint: [1, 0.5001],
|
||||||
mode: "orbit",
|
mode: "orbit",
|
||||||
}),
|
}),
|
||||||
endBinding: expect.objectContaining({
|
endBinding: expect.objectContaining({
|
||||||
elementId: rect2.id,
|
elementId: rect2.id,
|
||||||
fixedPoint: [0.4106696643494564, 0.5893303356505437],
|
fixedPoint: [0, 0.5001],
|
||||||
mode: "orbit",
|
mode: "orbit",
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
@@ -4666,13 +4666,13 @@ describe("history", () => {
|
|||||||
|
|
||||||
// create start binding
|
// create start binding
|
||||||
mouse.downAt(0, 0);
|
mouse.downAt(0, 0);
|
||||||
mouse.moveTo(0, 10);
|
mouse.moveTo(0, 3);
|
||||||
mouse.upAt(0, 10);
|
mouse.upAt(0, 3);
|
||||||
|
|
||||||
// create end binding
|
// create end binding
|
||||||
mouse.downAt(100, 0);
|
mouse.downAt(100, 0);
|
||||||
mouse.moveTo(100, 10);
|
mouse.moveTo(100, 3);
|
||||||
mouse.upAt(100, 10);
|
mouse.upAt(100, 3);
|
||||||
|
|
||||||
expect(h.elements).toEqual(
|
expect(h.elements).toEqual(
|
||||||
expect.arrayContaining([
|
expect.arrayContaining([
|
||||||
@@ -4766,13 +4766,13 @@ describe("history", () => {
|
|||||||
id: arrowId,
|
id: arrowId,
|
||||||
startBinding: expect.objectContaining({
|
startBinding: expect.objectContaining({
|
||||||
elementId: rect1.id,
|
elementId: rect1.id,
|
||||||
fixedPoint: [0.6363636363636364, 0.6363636363636364],
|
fixedPoint: [1, 0.5001],
|
||||||
mode: "orbit",
|
mode: "orbit",
|
||||||
}),
|
}),
|
||||||
// rebound with previous rectangle
|
// rebound with previous rectangle
|
||||||
endBinding: expect.objectContaining({
|
endBinding: expect.objectContaining({
|
||||||
elementId: rect2.id,
|
elementId: rect2.id,
|
||||||
fixedPoint: [0.39746300211416496, 0.6025369978858351],
|
fixedPoint: [0, 0.5001],
|
||||||
mode: "orbit",
|
mode: "orbit",
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
@@ -5031,8 +5031,9 @@ describe("history", () => {
|
|||||||
// bind arrow to rect1 and rect2
|
// bind arrow to rect1 and rect2
|
||||||
UI.clickTool("arrow");
|
UI.clickTool("arrow");
|
||||||
mouse.down(0, 0);
|
mouse.down(0, 0);
|
||||||
mouse.moveTo(50, 0);
|
mouse.moveTo(25, 0);
|
||||||
mouse.up(50, 0);
|
mouse.moveTo(47, 0);
|
||||||
|
mouse.up(47, 0);
|
||||||
|
|
||||||
const arrowId = h.elements[2].id;
|
const arrowId = h.elements[2].id;
|
||||||
|
|
||||||
@@ -5055,7 +5056,7 @@ describe("history", () => {
|
|||||||
}),
|
}),
|
||||||
endBinding: expect.objectContaining({
|
endBinding: expect.objectContaining({
|
||||||
elementId: rect2.id,
|
elementId: rect2.id,
|
||||||
fixedPoint: expect.arrayContaining([0.5001, 0.5001]),
|
fixedPoint: expect.arrayContaining([0, 0.5001]),
|
||||||
}),
|
}),
|
||||||
isDeleted: true,
|
isDeleted: true,
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ describe("move element", () => {
|
|||||||
// create elements
|
// create elements
|
||||||
const rectA = UI.createElement("rectangle", { size: 100 });
|
const rectA = UI.createElement("rectangle", { size: 100 });
|
||||||
const rectB = UI.createElement("rectangle", { x: 200, y: 0, size: 300 });
|
const rectB = UI.createElement("rectangle", { x: 200, y: 0, size: 300 });
|
||||||
const arrow = UI.createElement("arrow", { x: 110, y: 50, size: 80 });
|
const arrow = UI.createElement("arrow", { x: 105, y: 50, size: 88 });
|
||||||
|
|
||||||
act(() => {
|
act(() => {
|
||||||
// bind line to two rectangles
|
// bind line to two rectangles
|
||||||
@@ -110,7 +110,10 @@ describe("move element", () => {
|
|||||||
expect(h.state.selectedElementIds[rectB.id]).toBeTruthy();
|
expect(h.state.selectedElementIds[rectB.id]).toBeTruthy();
|
||||||
expect([rectA.x, rectA.y]).toEqual([0, 0]);
|
expect([rectA.x, rectA.y]).toEqual([0, 0]);
|
||||||
expect([rectB.x, rectB.y]).toEqual([200, 0]);
|
expect([rectB.x, rectB.y]).toEqual([200, 0]);
|
||||||
expect([[arrow.x, arrow.y]]).toCloselyEqualPoints([[106, 46]], 0);
|
expect([[arrow.x, arrow.y]]).toCloselyEqualPoints(
|
||||||
|
[[106.00000000000001, 55.6867741935484]],
|
||||||
|
0,
|
||||||
|
);
|
||||||
expect([[arrow.width, arrow.height]]).toCloselyEqualPoints([[88, 88]], 0);
|
expect([[arrow.width, arrow.height]]).toCloselyEqualPoints([[88, 88]], 0);
|
||||||
|
|
||||||
renderInteractiveScene.mockClear();
|
renderInteractiveScene.mockClear();
|
||||||
@@ -129,11 +132,11 @@ describe("move element", () => {
|
|||||||
expect(h.state.selectedElementIds[rectB.id]).toBeTruthy();
|
expect(h.state.selectedElementIds[rectB.id]).toBeTruthy();
|
||||||
expect([rectA.x, rectA.y]).toEqual([0, 0]);
|
expect([rectA.x, rectA.y]).toEqual([0, 0]);
|
||||||
expect([rectB.x, rectB.y]).toEqual([201, 2]);
|
expect([rectB.x, rectB.y]).toEqual([201, 2]);
|
||||||
expect([[arrow.x, arrow.y]]).toCloselyEqualPoints([[106, 46]], 0);
|
expect([[arrow.x, arrow.y]]).toCloselyEqualPoints(
|
||||||
expect([[arrow.width, arrow.height]]).toCloselyEqualPoints(
|
[[106, 55.6867741935484]],
|
||||||
[[79, 124.1678]],
|
0,
|
||||||
2,
|
|
||||||
);
|
);
|
||||||
|
expect([[arrow.width, arrow.height]]).toCloselyEqualPoints([[89, 90]], 0);
|
||||||
|
|
||||||
h.elements.forEach((element) => expect(element).toMatchSnapshot());
|
h.elements.forEach((element) => expect(element).toMatchSnapshot());
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -80,6 +80,6 @@ test("unselected bound arrows update when rotating their target elements", async
|
|||||||
expect(textArrow.x).toEqual(360);
|
expect(textArrow.x).toEqual(360);
|
||||||
expect(textArrow.y).toEqual(300);
|
expect(textArrow.y).toEqual(300);
|
||||||
expect(textArrow.points[0]).toEqual([0, 0]);
|
expect(textArrow.points[0]).toEqual([0, 0]);
|
||||||
expect(textArrow.points[1][0]).toBeCloseTo(-98.87, 0);
|
expect(textArrow.points[1][0]).toBeCloseTo(-95.4635969899922, 0);
|
||||||
expect(textArrow.points[1][1]).toBeCloseTo(-123.65, 0);
|
expect(textArrow.points[1][1]).toBeCloseTo(-126.8785027399889, 0);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ import type {
|
|||||||
DurableIncrement,
|
DurableIncrement,
|
||||||
EphemeralIncrement,
|
EphemeralIncrement,
|
||||||
} from "@excalidraw/element";
|
} from "@excalidraw/element";
|
||||||
|
import type { GlobalPoint } from "@excalidraw/math";
|
||||||
|
|
||||||
import type { Action } from "./actions/types";
|
import type { Action } from "./actions/types";
|
||||||
import type { Spreadsheet } from "./charts";
|
import type { Spreadsheet } from "./charts";
|
||||||
@@ -301,7 +302,10 @@ export interface AppState {
|
|||||||
selectionElement: NonDeletedExcalidrawElement | null;
|
selectionElement: NonDeletedExcalidrawElement | null;
|
||||||
isBindingEnabled: boolean;
|
isBindingEnabled: boolean;
|
||||||
startBoundElement: NonDeleted<ExcalidrawBindableElement> | null;
|
startBoundElement: NonDeleted<ExcalidrawBindableElement> | null;
|
||||||
suggestedBinding: NonDeleted<ExcalidrawBindableElement> | null;
|
suggestedBinding: {
|
||||||
|
element: NonDeleted<ExcalidrawBindableElement>;
|
||||||
|
midPoint?: GlobalPoint;
|
||||||
|
} | null;
|
||||||
frameToHighlight: NonDeleted<ExcalidrawFrameLikeElement> | null;
|
frameToHighlight: NonDeleted<ExcalidrawFrameLikeElement> | null;
|
||||||
frameRendering: {
|
frameRendering: {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
@@ -759,6 +763,7 @@ export type AppClassProperties = {
|
|||||||
updateEditorAtom: App["updateEditorAtom"];
|
updateEditorAtom: App["updateEditorAtom"];
|
||||||
onPointerDownEmitter: App["onPointerDownEmitter"];
|
onPointerDownEmitter: App["onPointerDownEmitter"];
|
||||||
|
|
||||||
|
lastPointerMoveCoords: App["lastPointerMoveCoords"];
|
||||||
bindModeHandler: App["bindModeHandler"];
|
bindModeHandler: App["bindModeHandler"];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user