mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
Revert to master
This commit is contained in:
parent
1a87aa8e55
commit
4d1e2c2bbb
25 changed files with 410 additions and 577 deletions
|
@ -463,10 +463,23 @@ export const maybeBindLinearElement = (
|
|||
}
|
||||
};
|
||||
|
||||
const normalizePointBinding = (binding: { focus: number; gap: number }) => {
|
||||
const normalizePointBinding = (
|
||||
binding: { focus: number; gap: number },
|
||||
hoveredElement: ExcalidrawBindableElement,
|
||||
) => {
|
||||
let gap = binding.gap;
|
||||
const maxGap = maxBindingGap(
|
||||
hoveredElement,
|
||||
hoveredElement.width,
|
||||
hoveredElement.height,
|
||||
);
|
||||
|
||||
if (gap > maxGap) {
|
||||
gap = BINDING_HIGHLIGHT_THICKNESS + BINDING_HIGHLIGHT_OFFSET;
|
||||
}
|
||||
return {
|
||||
...binding,
|
||||
gap: FIXED_BINDING_DISTANCE,
|
||||
gap,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -716,7 +729,7 @@ const calculateFocusAndGap = (
|
|||
|
||||
return {
|
||||
focus: determineFocusDistance(hoveredElement, adjacentPoint, edgePoint),
|
||||
gap: FIXED_BINDING_DISTANCE,
|
||||
gap: Math.max(1, distanceToBindableElement(hoveredElement, edgePoint)),
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -734,7 +747,7 @@ export const updateBoundElements = (
|
|||
changedElements?: Map<string, OrderedExcalidrawElement>;
|
||||
},
|
||||
) => {
|
||||
const { simultaneouslyUpdated } = options ?? {};
|
||||
const { newSize, simultaneouslyUpdated } = options ?? {};
|
||||
const simultaneouslyUpdatedElementIds = getSimultaneouslyUpdatedElementIds(
|
||||
simultaneouslyUpdated,
|
||||
);
|
||||
|
@ -767,13 +780,23 @@ export const updateBoundElements = (
|
|||
startBounds = getElementBounds(startBindingElement, elementsMap);
|
||||
endBounds = getElementBounds(endBindingElement, elementsMap);
|
||||
}
|
||||
|
||||
const bindings = {
|
||||
startBinding: maybeCalculateNewGapWhenScaling(
|
||||
changedElement,
|
||||
element.startBinding,
|
||||
newSize,
|
||||
),
|
||||
endBinding: maybeCalculateNewGapWhenScaling(
|
||||
changedElement,
|
||||
element.endBinding,
|
||||
newSize,
|
||||
),
|
||||
};
|
||||
|
||||
// `linearElement` is being moved/scaled already, just update the binding
|
||||
if (simultaneouslyUpdatedElementIds.has(element.id)) {
|
||||
mutateElement(
|
||||
element,
|
||||
{ startBinding: element.startBinding, endBinding: element.endBinding },
|
||||
true,
|
||||
);
|
||||
mutateElement(element, bindings, true);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -795,9 +818,7 @@ export const updateBoundElements = (
|
|||
const point = updateBoundPoint(
|
||||
element,
|
||||
bindingProp,
|
||||
bindingProp === "startBinding"
|
||||
? element.startBinding
|
||||
: element.endBinding,
|
||||
bindings[bindingProp],
|
||||
bindableElement,
|
||||
elementsMap,
|
||||
);
|
||||
|
@ -917,7 +938,6 @@ export const bindPointToSnapToElementOutline = (
|
|||
arrow: ExcalidrawElbowArrowElement,
|
||||
bindableElement: ExcalidrawBindableElement,
|
||||
startOrEnd: "start" | "end",
|
||||
elementsMap: ElementsMap,
|
||||
): GlobalPoint => {
|
||||
if (isDevEnv() || isTestEnv()) {
|
||||
invariant(arrow.points.length > 1, "Arrow should have at least 2 points");
|
||||
|
@ -925,12 +945,10 @@ export const bindPointToSnapToElementOutline = (
|
|||
|
||||
const aabb = aabbForElement(bindableElement);
|
||||
const localP =
|
||||
linearElement.points[
|
||||
startOrEnd === "start" ? 0 : linearElement.points.length - 1
|
||||
];
|
||||
arrow.points[startOrEnd === "start" ? 0 : arrow.points.length - 1];
|
||||
const globalP = pointFrom<GlobalPoint>(
|
||||
linearElement.x + localP[0],
|
||||
linearElement.y + localP[1],
|
||||
arrow.x + localP[0],
|
||||
arrow.y + localP[1],
|
||||
);
|
||||
const edgePoint = isRectanguloidElement(bindableElement)
|
||||
? avoidRectangularCorner(bindableElement, globalP)
|
||||
|
@ -967,11 +985,7 @@ export const bindPointToSnapToElementOutline = (
|
|||
),
|
||||
otherPoint,
|
||||
),
|
||||
adjacentPoint,
|
||||
),
|
||||
).sort(
|
||||
(g, h) =>
|
||||
pointDistanceSq(g, adjacentPoint) - pointDistanceSq(h, adjacentPoint),
|
||||
)[0];
|
||||
} else {
|
||||
intersection = intersectElementWithLineSegment(
|
||||
|
@ -1019,20 +1033,6 @@ export const bindPointToSnapToElementOutline = (
|
|||
);
|
||||
}
|
||||
|
||||
const currentDistance = pointDistance(edgePoint, center);
|
||||
const fullDistance = Math.max(
|
||||
pointDistance(intersection ?? edgePoint, center),
|
||||
1e-5, // Avoid division by zero
|
||||
);
|
||||
|
||||
if (!isInside) {
|
||||
return intersection;
|
||||
}
|
||||
|
||||
if (elbowed) {
|
||||
return headingToMidBindPoint(edgePoint, bindableElement, aabb);
|
||||
}
|
||||
|
||||
return edgePoint;
|
||||
};
|
||||
|
||||
|
@ -1040,7 +1040,10 @@ export const avoidRectangularCorner = (
|
|||
element: ExcalidrawBindableElement,
|
||||
p: GlobalPoint,
|
||||
): GlobalPoint => {
|
||||
const center = elementCenterPoint(element);
|
||||
const center = pointFrom<GlobalPoint>(
|
||||
element.x + element.width / 2,
|
||||
element.y + element.height / 2,
|
||||
);
|
||||
const nonRotatedPoint = pointRotateRads(p, center, -element.angle as Radians);
|
||||
|
||||
if (nonRotatedPoint[0] < element.x && nonRotatedPoint[1] < element.y) {
|
||||
|
@ -1310,24 +1313,6 @@ const updateBoundPoint = (
|
|||
),
|
||||
];
|
||||
|
||||
// debugClear();
|
||||
// intersections.forEach((intersection) => {
|
||||
// debugDrawPoint(intersection, { permanent: true, color: "red" });
|
||||
// });
|
||||
// debugDrawLine(
|
||||
// lineSegment<GlobalPoint>(
|
||||
// adjacentPoint,
|
||||
// pointFromVector(
|
||||
// vectorScale(
|
||||
// vectorNormalize(vectorFromPoint(focusPointAbsolute, adjacentPoint)),
|
||||
// interceptorLength,
|
||||
// ),
|
||||
// adjacentPoint,
|
||||
// ),
|
||||
// ),
|
||||
// { permanent: true, color: "green" },
|
||||
// );
|
||||
|
||||
if (intersections.length > 1) {
|
||||
// The adjacent point is outside the shape (+ gap)
|
||||
newEdgePoint = intersections[0];
|
||||
|
@ -1363,7 +1348,6 @@ export const calculateFixedPointForElbowArrowBinding = (
|
|||
linearElement,
|
||||
hoveredElement,
|
||||
startOrEnd,
|
||||
elementsMap,
|
||||
);
|
||||
const globalMidPoint = pointFrom(
|
||||
bounds[0] + (bounds[2] - bounds[0]) / 2,
|
||||
|
@ -1385,6 +1369,28 @@ export const calculateFixedPointForElbowArrowBinding = (
|
|||
};
|
||||
};
|
||||
|
||||
const maybeCalculateNewGapWhenScaling = (
|
||||
changedElement: ExcalidrawBindableElement,
|
||||
currentBinding: PointBinding | null | undefined,
|
||||
newSize: { width: number; height: number } | undefined,
|
||||
): PointBinding | null | undefined => {
|
||||
if (currentBinding == null || newSize == null) {
|
||||
return currentBinding;
|
||||
}
|
||||
const { width: newWidth, height: newHeight } = newSize;
|
||||
const { width, height } = changedElement;
|
||||
const newGap = Math.max(
|
||||
1,
|
||||
Math.min(
|
||||
maxBindingGap(changedElement, newWidth, newHeight),
|
||||
currentBinding.gap *
|
||||
(newWidth < newHeight ? newWidth / width : newHeight / height),
|
||||
),
|
||||
);
|
||||
|
||||
return { ...currentBinding, gap: newGap };
|
||||
};
|
||||
|
||||
const getElligibleElementForBindingElement = (
|
||||
linearElement: NonDeleted<ExcalidrawLinearElement>,
|
||||
startOrEnd: "start" | "end",
|
||||
|
@ -1765,7 +1771,10 @@ const determineFocusDistance = (
|
|||
// Another point on the line, in absolute coordinates (closer to element)
|
||||
b: GlobalPoint,
|
||||
): number => {
|
||||
const center = elementCenterPoint(element);
|
||||
const center = pointFrom<GlobalPoint>(
|
||||
element.x + element.width / 2,
|
||||
element.y + element.height / 2,
|
||||
);
|
||||
|
||||
if (pointsEqual(a, b)) {
|
||||
return 0;
|
||||
|
@ -1895,7 +1904,10 @@ const determineFocusPoint = (
|
|||
focus: number,
|
||||
adjacentPoint: GlobalPoint,
|
||||
): GlobalPoint => {
|
||||
const center = elementCenterPoint(element);
|
||||
const center = pointFrom<GlobalPoint>(
|
||||
element.x + element.width / 2,
|
||||
element.y + element.height / 2,
|
||||
);
|
||||
|
||||
if (focus === 0) {
|
||||
return center;
|
||||
|
@ -2326,7 +2338,10 @@ export const getGlobalFixedPointForBindableElement = (
|
|||
element.x + element.width * fixedX,
|
||||
element.y + element.height * fixedY,
|
||||
),
|
||||
elementCenterPoint(element),
|
||||
pointFrom<GlobalPoint>(
|
||||
element.x + element.width / 2,
|
||||
element.y + element.height / 2,
|
||||
),
|
||||
element.angle,
|
||||
);
|
||||
};
|
||||
|
|
|
@ -41,7 +41,6 @@ import {
|
|||
import {
|
||||
deconstructDiamondElement,
|
||||
deconstructRectanguloidElement,
|
||||
elementCenterPoint,
|
||||
} from "./utils";
|
||||
|
||||
import type {
|
||||
|
@ -192,7 +191,10 @@ const intersectRectanguloidWithLineSegment = (
|
|||
l: LineSegment<GlobalPoint>,
|
||||
offset: number = 0,
|
||||
): GlobalPoint[] => {
|
||||
const center = elementCenterPoint(element);
|
||||
const center = pointFrom<GlobalPoint>(
|
||||
element.x + element.width / 2,
|
||||
element.y + element.height / 2,
|
||||
);
|
||||
// To emulate a rotated rectangle we rotate the point in the inverse angle
|
||||
// instead. It's all the same distance-wise.
|
||||
const rotatedA = pointRotateRads<GlobalPoint>(
|
||||
|
@ -210,32 +212,31 @@ const intersectRectanguloidWithLineSegment = (
|
|||
const [sides, corners] = deconstructRectanguloidElement(element, offset);
|
||||
|
||||
return (
|
||||
// Test intersection against the sides, keep only the valid
|
||||
// intersection points and rotate them back to scene space
|
||||
sides
|
||||
.map((s) =>
|
||||
lineSegmentIntersectionPoints(
|
||||
lineSegment<GlobalPoint>(rotatedA, rotatedB),
|
||||
s,
|
||||
),
|
||||
)
|
||||
.filter((x) => x != null)
|
||||
.map((j) => pointRotateRads<GlobalPoint>(j!, center, element.angle))
|
||||
[
|
||||
// Test intersection against the sides, keep only the valid
|
||||
// intersection points and rotate them back to scene space
|
||||
...sides
|
||||
.map((s) =>
|
||||
lineSegmentIntersectionPoints(
|
||||
lineSegment<GlobalPoint>(rotatedA, rotatedB),
|
||||
s,
|
||||
),
|
||||
)
|
||||
.filter((x) => x != null)
|
||||
.map((j) => pointRotateRads<GlobalPoint>(j!, center, element.angle)),
|
||||
// Test intersection against the corners which are cubic bezier curves,
|
||||
// keep only the valid intersection points and rotate them back to scene
|
||||
// space
|
||||
.concat(
|
||||
corners
|
||||
.flatMap((t) =>
|
||||
curveIntersectLineSegment(t, lineSegment(rotatedA, rotatedB)),
|
||||
)
|
||||
.filter((i) => i != null)
|
||||
.map((j) => pointRotateRads(j, center, element.angle)),
|
||||
)
|
||||
...corners
|
||||
.flatMap((t) =>
|
||||
curveIntersectLineSegment(t, lineSegment(rotatedA, rotatedB)),
|
||||
)
|
||||
.filter((i) => i != null)
|
||||
.map((j) => pointRotateRads(j, center, element.angle)),
|
||||
]
|
||||
// Remove duplicates
|
||||
.filter(
|
||||
(p, idx, points) =>
|
||||
points.findIndex((d) => pointsEqual(p, d, 1e-3)) === idx,
|
||||
(p, idx, points) => points.findIndex((d) => pointsEqual(p, d)) === idx,
|
||||
)
|
||||
);
|
||||
};
|
||||
|
@ -252,7 +253,10 @@ const intersectDiamondWithLineSegment = (
|
|||
l: LineSegment<GlobalPoint>,
|
||||
offset: number = 0,
|
||||
): GlobalPoint[] => {
|
||||
const center = elementCenterPoint(element);
|
||||
const center = pointFrom<GlobalPoint>(
|
||||
element.x + element.width / 2,
|
||||
element.y + element.height / 2,
|
||||
);
|
||||
|
||||
// Rotate the point to the inverse direction to simulate the rotated diamond
|
||||
// points. It's all the same distance-wise.
|
||||
|
@ -262,29 +266,28 @@ const intersectDiamondWithLineSegment = (
|
|||
const [sides, curves] = deconstructDiamondElement(element, offset);
|
||||
|
||||
return (
|
||||
sides
|
||||
.map((s) =>
|
||||
lineSegmentIntersectionPoints(
|
||||
lineSegment<GlobalPoint>(rotatedA, rotatedB),
|
||||
s,
|
||||
),
|
||||
)
|
||||
.filter((p): p is GlobalPoint => p != null)
|
||||
// Rotate back intersection points
|
||||
.map((p) => pointRotateRads<GlobalPoint>(p!, center, element.angle))
|
||||
.concat(
|
||||
curves
|
||||
.flatMap((p) =>
|
||||
curveIntersectLineSegment(p, lineSegment(rotatedA, rotatedB)),
|
||||
)
|
||||
.filter((p) => p != null)
|
||||
// Rotate back intersection points
|
||||
.map((p) => pointRotateRads(p, center, element.angle)),
|
||||
)
|
||||
[
|
||||
...sides
|
||||
.map((s) =>
|
||||
lineSegmentIntersectionPoints(
|
||||
lineSegment<GlobalPoint>(rotatedA, rotatedB),
|
||||
s,
|
||||
),
|
||||
)
|
||||
.filter((p): p is GlobalPoint => p != null)
|
||||
// Rotate back intersection points
|
||||
.map((p) => pointRotateRads<GlobalPoint>(p!, center, element.angle)),
|
||||
...curves
|
||||
.flatMap((p) =>
|
||||
curveIntersectLineSegment(p, lineSegment(rotatedA, rotatedB)),
|
||||
)
|
||||
.filter((p) => p != null)
|
||||
// Rotate back intersection points
|
||||
.map((p) => pointRotateRads(p, center, element.angle)),
|
||||
]
|
||||
// Remove duplicates
|
||||
.filter(
|
||||
(p, idx, points) =>
|
||||
points.findIndex((d) => pointsEqual(p, d, 1e-3)) === idx,
|
||||
(p, idx, points) => points.findIndex((d) => pointsEqual(p, d)) === idx,
|
||||
)
|
||||
);
|
||||
};
|
||||
|
@ -301,7 +304,10 @@ const intersectEllipseWithLineSegment = (
|
|||
l: LineSegment<GlobalPoint>,
|
||||
offset: number = 0,
|
||||
): GlobalPoint[] => {
|
||||
const center = elementCenterPoint(element);
|
||||
const center = pointFrom<GlobalPoint>(
|
||||
element.x + element.width / 2,
|
||||
element.y + element.height / 2,
|
||||
);
|
||||
|
||||
const rotatedA = pointRotateRads(l[0], center, -element.angle as Radians);
|
||||
const rotatedB = pointRotateRads(l[1], center, -element.angle as Radians);
|
||||
|
|
|
@ -61,7 +61,7 @@ export const cropElement = (
|
|||
|
||||
const rotatedPointer = pointRotateRads(
|
||||
pointFrom(pointerX, pointerY),
|
||||
elementCenterPoint(element),
|
||||
pointFrom(element.x + element.width / 2, element.y + element.height / 2),
|
||||
-element.angle as Radians,
|
||||
);
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import {
|
||||
curvePointDistance,
|
||||
distanceToLineSegment,
|
||||
pointFrom,
|
||||
pointRotateRads,
|
||||
} from "@excalidraw/math";
|
||||
|
||||
|
@ -52,7 +53,10 @@ const distanceToRectanguloidElement = (
|
|||
element: ExcalidrawRectanguloidElement,
|
||||
p: GlobalPoint,
|
||||
) => {
|
||||
const center = elementCenterPoint(element);
|
||||
const center = pointFrom<GlobalPoint>(
|
||||
element.x + element.width / 2,
|
||||
element.y + element.height / 2,
|
||||
);
|
||||
// To emulate a rotated rectangle we rotate the point in the inverse angle
|
||||
// instead. It's all the same distance-wise.
|
||||
const rotatedPoint = pointRotateRads(p, center, -element.angle as Radians);
|
||||
|
@ -80,7 +84,10 @@ const distanceToDiamondElement = (
|
|||
element: ExcalidrawDiamondElement,
|
||||
p: GlobalPoint,
|
||||
): number => {
|
||||
const center = elementCenterPoint(element);
|
||||
const center = pointFrom<GlobalPoint>(
|
||||
element.x + element.width / 2,
|
||||
element.y + element.height / 2,
|
||||
);
|
||||
|
||||
// Rotate the point to the inverse direction to simulate the rotated diamond
|
||||
// points. It's all the same distance-wise.
|
||||
|
@ -108,7 +115,10 @@ const distanceToEllipseElement = (
|
|||
element: ExcalidrawEllipseElement,
|
||||
p: GlobalPoint,
|
||||
): number => {
|
||||
const center = elementCenterPoint(element);
|
||||
const center = pointFrom(
|
||||
element.x + element.width / 2,
|
||||
element.y + element.height / 2,
|
||||
);
|
||||
return ellipseDistanceFromPoint(
|
||||
// Instead of rotating the ellipse, rotate the point to the inverse angle
|
||||
pointRotateRads(p, center, -element.angle as Radians),
|
||||
|
|
|
@ -1249,7 +1249,6 @@ const getElbowArrowData = (
|
|||
...arrow,
|
||||
type: "arrow",
|
||||
elbowed: true,
|
||||
type: "arrow",
|
||||
points: nextPoints,
|
||||
} as ExcalidrawElbowArrowElement,
|
||||
"start",
|
||||
|
@ -1263,7 +1262,6 @@ const getElbowArrowData = (
|
|||
...arrow,
|
||||
type: "arrow",
|
||||
elbowed: true,
|
||||
type: "arrow",
|
||||
points: nextPoints,
|
||||
} as ExcalidrawElbowArrowElement,
|
||||
"end",
|
||||
|
@ -2223,7 +2221,6 @@ const getGlobalPoint = (
|
|||
arrow,
|
||||
element,
|
||||
startOrEnd,
|
||||
elementsMap,
|
||||
);
|
||||
|
||||
return snapToMid(element, snapPoint);
|
||||
|
|
|
@ -239,43 +239,6 @@ export class LinearElementEditor {
|
|||
});
|
||||
}
|
||||
|
||||
static getOutlineAvoidingPoint(
|
||||
element: NonDeleted<ExcalidrawLinearElement>,
|
||||
coords: GlobalPoint,
|
||||
pointIndex: number,
|
||||
app: AppClassProperties,
|
||||
fallback?: GlobalPoint,
|
||||
): GlobalPoint {
|
||||
const hoveredElement = getHoveredElementForBinding(
|
||||
{ x: coords[0], y: coords[1] },
|
||||
app.scene.getNonDeletedElements(),
|
||||
app.scene.getNonDeletedElementsMap(),
|
||||
app.state.zoom,
|
||||
true,
|
||||
isElbowArrow(element),
|
||||
);
|
||||
|
||||
if (hoveredElement) {
|
||||
const newPoints = Array.from(element.points);
|
||||
newPoints[pointIndex] = pointFrom<LocalPoint>(
|
||||
coords[0] - element.x,
|
||||
coords[1] - element.y,
|
||||
);
|
||||
|
||||
return bindPointToSnapToElementOutline(
|
||||
{
|
||||
...element,
|
||||
points: newPoints,
|
||||
},
|
||||
hoveredElement,
|
||||
pointIndex === 0 ? "start" : "end",
|
||||
app.scene.getNonDeletedElementsMap(),
|
||||
);
|
||||
}
|
||||
|
||||
return fallback ?? coords;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns whether point was dragged
|
||||
*/
|
||||
|
@ -295,16 +258,14 @@ export class LinearElementEditor {
|
|||
return null;
|
||||
}
|
||||
const { elementId } = linearElementEditor;
|
||||
const elementsMap = app.scene.getNonDeletedElementsMap();
|
||||
const elementsMap = scene.getNonDeletedElementsMap();
|
||||
const element = LinearElementEditor.getElement(elementId, elementsMap);
|
||||
if (!element) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const elbowed = isElbowArrow(element);
|
||||
|
||||
if (
|
||||
elbowed &&
|
||||
isElbowArrow(element) &&
|
||||
!linearElementEditor.pointerDownState.lastClickedIsEndPoint &&
|
||||
linearElementEditor.pointerDownState.lastClickedPoint !== 0
|
||||
) {
|
||||
|
@ -321,7 +282,7 @@ export class LinearElementEditor {
|
|||
: undefined,
|
||||
].filter((idx): idx is number => idx !== undefined)
|
||||
: linearElementEditor.selectedPointsIndices;
|
||||
const lastClickedPoint = elbowed
|
||||
const lastClickedPoint = isElbowArrow(element)
|
||||
? linearElementEditor.pointerDownState.lastClickedPoint > 0
|
||||
? element.points.length - 1
|
||||
: 0
|
||||
|
@ -373,43 +334,19 @@ export class LinearElementEditor {
|
|||
LinearElementEditor.movePoints(
|
||||
element,
|
||||
selectedPointsIndices.map((pointIndex) => {
|
||||
let newPointPosition = pointFrom<LocalPoint>(
|
||||
element.points[pointIndex][0] + deltaX,
|
||||
element.points[pointIndex][1] + deltaY,
|
||||
);
|
||||
|
||||
// Check if point dragging is happening
|
||||
if (pointIndex === lastClickedPoint) {
|
||||
let globalNewPointPosition = pointFrom<GlobalPoint>(
|
||||
scenePointerX - linearElementEditor.pointerOffset.x,
|
||||
scenePointerY - linearElementEditor.pointerOffset.y,
|
||||
);
|
||||
|
||||
if (
|
||||
pointIndex === 0 ||
|
||||
pointIndex === element.points.length - 1
|
||||
) {
|
||||
globalNewPointPosition =
|
||||
LinearElementEditor.getOutlineAvoidingPoint(
|
||||
const newPointPosition: LocalPoint =
|
||||
pointIndex === lastClickedPoint
|
||||
? LinearElementEditor.createPointAt(
|
||||
element,
|
||||
pointFrom<GlobalPoint>(
|
||||
element.x + element.points[pointIndex][0] + deltaX,
|
||||
element.y + element.points[pointIndex][1] + deltaY,
|
||||
),
|
||||
pointIndex,
|
||||
app,
|
||||
elementsMap,
|
||||
scenePointerX - linearElementEditor.pointerOffset.x,
|
||||
scenePointerY - linearElementEditor.pointerOffset.y,
|
||||
event[KEYS.CTRL_OR_CMD] ? null : app.getEffectiveGridSize(),
|
||||
)
|
||||
: pointFrom(
|
||||
element.points[pointIndex][0] + deltaX,
|
||||
element.points[pointIndex][1] + deltaY,
|
||||
);
|
||||
}
|
||||
|
||||
newPointPosition = LinearElementEditor.createPointAt(
|
||||
element,
|
||||
elementsMap,
|
||||
globalNewPointPosition[0],
|
||||
globalNewPointPosition[1],
|
||||
event[KEYS.CTRL_OR_CMD] ? null : app.getEffectiveGridSize(),
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
index: pointIndex,
|
||||
point: newPointPosition,
|
||||
|
|
|
@ -18,7 +18,6 @@ import { getDiamondPoints } from "./bounds";
|
|||
|
||||
import type {
|
||||
ExcalidrawDiamondElement,
|
||||
ExcalidrawElement,
|
||||
ExcalidrawRectanguloidElement,
|
||||
} from "./types";
|
||||
|
||||
|
@ -69,7 +68,10 @@ export function deconstructRectanguloidElement(
|
|||
return [sides, []];
|
||||
}
|
||||
|
||||
const center = elementCenterPoint(element);
|
||||
const center = pointFrom<GlobalPoint>(
|
||||
element.x + element.width / 2,
|
||||
element.y + element.height / 2,
|
||||
);
|
||||
|
||||
const r = rectangle(
|
||||
pointFrom(element.x, element.y),
|
||||
|
@ -252,7 +254,10 @@ export function deconstructDiamondElement(
|
|||
return [[topRight, bottomRight, bottomLeft, topLeft], []];
|
||||
}
|
||||
|
||||
const center = elementCenterPoint(element);
|
||||
const center = pointFrom<GlobalPoint>(
|
||||
element.x + element.width / 2,
|
||||
element.y + element.height / 2,
|
||||
);
|
||||
|
||||
const [top, right, bottom, left]: GlobalPoint[] = [
|
||||
pointFrom(element.x + topX, element.y + topY),
|
||||
|
@ -352,10 +357,3 @@ export function deconstructDiamondElement(
|
|||
|
||||
return [sides, corners];
|
||||
}
|
||||
|
||||
export function elementCenterPoint(element: ExcalidrawElement) {
|
||||
return pointFrom<GlobalPoint>(
|
||||
element.x + element.width / 2,
|
||||
element.y + element.height / 2,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -190,18 +190,7 @@ describe("element binding", () => {
|
|||
|
||||
// Sever connection
|
||||
expect(API.getSelectedElement().type).toBe("arrow");
|
||||
Keyboard.withModifierKeys({ shift: true }, () => {
|
||||
// We have to move a significant distance to get out of the binding zone
|
||||
Keyboard.keyPress(KEYS.ARROW_LEFT);
|
||||
Keyboard.keyPress(KEYS.ARROW_LEFT);
|
||||
Keyboard.keyPress(KEYS.ARROW_LEFT);
|
||||
Keyboard.keyPress(KEYS.ARROW_LEFT);
|
||||
Keyboard.keyPress(KEYS.ARROW_LEFT);
|
||||
Keyboard.keyPress(KEYS.ARROW_LEFT);
|
||||
Keyboard.keyPress(KEYS.ARROW_LEFT);
|
||||
Keyboard.keyPress(KEYS.ARROW_LEFT);
|
||||
Keyboard.keyPress(KEYS.ARROW_LEFT);
|
||||
});
|
||||
Keyboard.keyPress(KEYS.ARROW_LEFT);
|
||||
expect(arrow.endBinding).toBe(null);
|
||||
Keyboard.keyPress(KEYS.ARROW_RIGHT);
|
||||
expect(arrow.endBinding).toBe(null);
|
||||
|
|
|
@ -77,9 +77,9 @@ describe("elbow arrow segment move", () => {
|
|||
|
||||
expect(arrow.points).toCloselyEqualPoints([
|
||||
[0, 0],
|
||||
[107.93, 0],
|
||||
[107.93, 185.86],
|
||||
[185.86, 185.86],
|
||||
[110, 0],
|
||||
[110, 200],
|
||||
[190, 200],
|
||||
]);
|
||||
|
||||
mouse.reset();
|
||||
|
@ -88,9 +88,9 @@ describe("elbow arrow segment move", () => {
|
|||
|
||||
expect(arrow.points).toCloselyEqualPoints([
|
||||
[0, 0],
|
||||
[107.93, 0],
|
||||
[107.93, 185.86],
|
||||
[185.86, 185.86],
|
||||
[110, 0],
|
||||
[110, 200],
|
||||
[190, 200],
|
||||
]);
|
||||
});
|
||||
|
||||
|
@ -198,11 +198,11 @@ describe("elbow arrow routing", () => {
|
|||
points: [pointFrom<LocalPoint>(0, 0), pointFrom<LocalPoint>(90, 200)],
|
||||
});
|
||||
|
||||
expect(arrow.points).toCloselyEqualPoints([
|
||||
expect(arrow.points).toEqual([
|
||||
[0, 0],
|
||||
[42.93, 0],
|
||||
[42.93, 195.7],
|
||||
[85.86, 195.7],
|
||||
[45, 0],
|
||||
[45, 200],
|
||||
[90, 200],
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
@ -241,9 +241,9 @@ describe("elbow arrow ui", () => {
|
|||
expect(h.state.currentItemArrowType).toBe(ARROW_TYPE.elbow);
|
||||
|
||||
mouse.reset();
|
||||
mouse.moveTo(-50, -100);
|
||||
mouse.moveTo(-43, -99);
|
||||
mouse.click();
|
||||
mouse.moveTo(50, 100);
|
||||
mouse.moveTo(43, 99);
|
||||
mouse.click();
|
||||
|
||||
const arrow = h.scene.getSelectedElements(
|
||||
|
@ -252,11 +252,11 @@ describe("elbow arrow ui", () => {
|
|||
|
||||
expect(arrow.type).toBe("arrow");
|
||||
expect(arrow.elbowed).toBe(true);
|
||||
expect(arrow.points).toCloselyEqualPoints([
|
||||
expect(arrow.points).toEqual([
|
||||
[0, 0],
|
||||
[42.93, 0],
|
||||
[42.93, 153.48],
|
||||
[85.86, 153.48],
|
||||
[45, 0],
|
||||
[45, 200],
|
||||
[90, 200],
|
||||
]);
|
||||
});
|
||||
|
||||
|
@ -296,8 +296,9 @@ describe("elbow arrow ui", () => {
|
|||
|
||||
expect(arrow.points.map((point) => point.map(Math.round))).toEqual([
|
||||
[0, 0],
|
||||
[129, 0],
|
||||
[129, 131],
|
||||
[35, 0],
|
||||
[35, 165],
|
||||
[103, 165],
|
||||
]);
|
||||
});
|
||||
|
||||
|
|
|
@ -195,7 +195,7 @@ describe("generic element", () => {
|
|||
UI.resize(rectangle, "w", [50, 0]);
|
||||
|
||||
expect(arrow.endBinding?.elementId).toEqual(rectangle.id);
|
||||
expect(arrow.width + arrow.endBinding!.gap).toBeCloseTo(80.62, 0);
|
||||
expect(arrow.width + arrow.endBinding!.gap).toBeCloseTo(80, 0);
|
||||
});
|
||||
|
||||
it("resizes with a label", async () => {
|
||||
|
@ -510,13 +510,13 @@ describe("arrow element", () => {
|
|||
h.state,
|
||||
)[0] as ExcalidrawElbowArrowElement;
|
||||
|
||||
expect(arrow.startBinding?.fixedPoint?.[0]).toBeCloseTo(1.07);
|
||||
expect(arrow.startBinding?.fixedPoint?.[1]).toBeCloseTo(0.86);
|
||||
expect(arrow.startBinding?.fixedPoint?.[0]).toBeCloseTo(1);
|
||||
expect(arrow.startBinding?.fixedPoint?.[1]).toBeCloseTo(0.75);
|
||||
|
||||
UI.resize(rectangle, "se", [-200, -150]);
|
||||
|
||||
expect(arrow.startBinding?.fixedPoint?.[0]).toBeCloseTo(1.07);
|
||||
expect(arrow.startBinding?.fixedPoint?.[1]).toBeCloseTo(0.86);
|
||||
expect(arrow.startBinding?.fixedPoint?.[0]).toBeCloseTo(1);
|
||||
expect(arrow.startBinding?.fixedPoint?.[1]).toBeCloseTo(0.75);
|
||||
});
|
||||
|
||||
it("flips the fixed point binding on negative resize for group selection", () => {
|
||||
|
@ -538,8 +538,8 @@ describe("arrow element", () => {
|
|||
h.state,
|
||||
)[0] as ExcalidrawElbowArrowElement;
|
||||
|
||||
expect(arrow.startBinding?.fixedPoint?.[0]).toBeCloseTo(1.07);
|
||||
expect(arrow.startBinding?.fixedPoint?.[1]).toBeCloseTo(0.86);
|
||||
expect(arrow.startBinding?.fixedPoint?.[0]).toBeCloseTo(1);
|
||||
expect(arrow.startBinding?.fixedPoint?.[1]).toBeCloseTo(0.75);
|
||||
|
||||
UI.resize([rectangle, arrow], "nw", [300, 350]);
|
||||
expect(arrow.startBinding?.fixedPoint?.[0]).toBeCloseTo(0);
|
||||
|
@ -809,7 +809,7 @@ describe("image element", () => {
|
|||
});
|
||||
API.setElements([image]);
|
||||
const arrow = UI.createElement("arrow", {
|
||||
x: -29,
|
||||
x: -30,
|
||||
y: 50,
|
||||
width: 28,
|
||||
height: 5,
|
||||
|
@ -819,14 +819,14 @@ describe("image element", () => {
|
|||
|
||||
UI.resize(image, "ne", [40, 0]);
|
||||
|
||||
expect(arrow.width + arrow.endBinding!.gap).toBeCloseTo(30, 0);
|
||||
expect(arrow.width + arrow.endBinding!.gap).toBeCloseTo(31, 0);
|
||||
|
||||
const imageWidth = image.width;
|
||||
const scale = 20 / image.height;
|
||||
UI.resize(image, "nw", [50, 20]);
|
||||
|
||||
expect(arrow.endBinding?.elementId).toEqual(image.id);
|
||||
expect(arrow.width + arrow.endBinding!.gap).toBeCloseTo(
|
||||
expect(Math.floor(arrow.width + arrow.endBinding!.gap)).toBeCloseTo(
|
||||
30 + imageWidth * scale,
|
||||
0,
|
||||
);
|
||||
|
@ -1033,11 +1033,11 @@ describe("multiple selection", () => {
|
|||
|
||||
expect(leftBoundArrow.x).toBeCloseTo(-110);
|
||||
expect(leftBoundArrow.y).toBeCloseTo(50);
|
||||
expect(leftBoundArrow.width).toBeCloseTo(146.46, 0);
|
||||
expect(leftBoundArrow.width).toBeCloseTo(143, 0);
|
||||
expect(leftBoundArrow.height).toBeCloseTo(7, 0);
|
||||
expect(leftBoundArrow.angle).toEqual(0);
|
||||
expect(leftBoundArrow.startBinding).toBeNull();
|
||||
expect(leftBoundArrow.endBinding?.gap).toEqual(FIXED_BINDING_DISTANCE);
|
||||
expect(leftBoundArrow.endBinding?.gap).toBeCloseTo(10);
|
||||
expect(leftBoundArrow.endBinding?.elementId).toBe(
|
||||
leftArrowBinding.elementId,
|
||||
);
|
||||
|
@ -1051,7 +1051,7 @@ describe("multiple selection", () => {
|
|||
expect(rightBoundArrow.height).toBeCloseTo(0);
|
||||
expect(rightBoundArrow.angle).toEqual(0);
|
||||
expect(rightBoundArrow.startBinding).toBeNull();
|
||||
expect(rightBoundArrow.endBinding?.gap).toEqual(FIXED_BINDING_DISTANCE);
|
||||
expect(rightBoundArrow.endBinding?.gap).toBeCloseTo(8.0952);
|
||||
expect(rightBoundArrow.endBinding?.elementId).toBe(
|
||||
rightArrowBinding.elementId,
|
||||
);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue