mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
Merge branch 'master' into mrazator/separate-element-into-standalone-package
This commit is contained in:
commit
ffa8da2aa7
6 changed files with 149 additions and 117 deletions
|
@ -491,32 +491,31 @@ export const bindLinearElement = (
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const binding: PointBinding | FixedPointBinding = {
|
let binding: PointBinding | FixedPointBinding = {
|
||||||
elementId: hoveredElement.id,
|
elementId: hoveredElement.id,
|
||||||
...(isElbowArrow(linearElement)
|
...normalizePointBinding(
|
||||||
? {
|
calculateFocusAndGap(
|
||||||
...calculateFixedPointForElbowArrowBinding(
|
linearElement,
|
||||||
linearElement,
|
hoveredElement,
|
||||||
hoveredElement,
|
startOrEnd,
|
||||||
startOrEnd,
|
elementsMap,
|
||||||
elementsMap,
|
),
|
||||||
),
|
hoveredElement,
|
||||||
focus: 0,
|
),
|
||||||
gap: 0,
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
...normalizePointBinding(
|
|
||||||
calculateFocusAndGap(
|
|
||||||
linearElement,
|
|
||||||
hoveredElement,
|
|
||||||
startOrEnd,
|
|
||||||
elementsMap,
|
|
||||||
),
|
|
||||||
hoveredElement,
|
|
||||||
),
|
|
||||||
}),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (isElbowArrow(linearElement)) {
|
||||||
|
binding = {
|
||||||
|
...binding,
|
||||||
|
...calculateFixedPointForElbowArrowBinding(
|
||||||
|
linearElement,
|
||||||
|
hoveredElement,
|
||||||
|
startOrEnd,
|
||||||
|
elementsMap,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
mutateElement(linearElement, {
|
mutateElement(linearElement, {
|
||||||
[startOrEnd === "start" ? "startBinding" : "endBinding"]: binding,
|
[startOrEnd === "start" ? "startBinding" : "endBinding"]: binding,
|
||||||
});
|
});
|
||||||
|
@ -1276,39 +1275,35 @@ const updateBoundPoint = (
|
||||||
pointDistance(adjacentPoint, edgePointAbsolute) +
|
pointDistance(adjacentPoint, edgePointAbsolute) +
|
||||||
pointDistance(adjacentPoint, center) +
|
pointDistance(adjacentPoint, center) +
|
||||||
Math.max(bindableElement.width, bindableElement.height) * 2;
|
Math.max(bindableElement.width, bindableElement.height) * 2;
|
||||||
const intersections = intersectElementWithLineSegment(
|
const intersections = [
|
||||||
bindableElement,
|
...intersectElementWithLineSegment(
|
||||||
lineSegment<GlobalPoint>(
|
bindableElement,
|
||||||
adjacentPoint,
|
lineSegment<GlobalPoint>(
|
||||||
pointFromVector(
|
|
||||||
vectorScale(
|
|
||||||
vectorNormalize(vectorFromPoint(focusPointAbsolute, adjacentPoint)),
|
|
||||||
interceptorLength,
|
|
||||||
),
|
|
||||||
adjacentPoint,
|
adjacentPoint,
|
||||||
|
pointFromVector(
|
||||||
|
vectorScale(
|
||||||
|
vectorNormalize(
|
||||||
|
vectorFromPoint(focusPointAbsolute, adjacentPoint),
|
||||||
|
),
|
||||||
|
interceptorLength,
|
||||||
|
),
|
||||||
|
adjacentPoint,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
|
binding.gap,
|
||||||
|
).sort(
|
||||||
|
(g, h) =>
|
||||||
|
pointDistanceSq(g, adjacentPoint) - pointDistanceSq(h, adjacentPoint),
|
||||||
),
|
),
|
||||||
binding.gap,
|
// Fallback when arrow doesn't point to the shape
|
||||||
).sort(
|
pointFromVector(
|
||||||
(g, h) =>
|
vectorScale(
|
||||||
pointDistanceSq(g, adjacentPoint) - pointDistanceSq(h, adjacentPoint),
|
vectorNormalize(vectorFromPoint(focusPointAbsolute, adjacentPoint)),
|
||||||
);
|
pointDistance(adjacentPoint, edgePointAbsolute),
|
||||||
|
),
|
||||||
// debugClear();
|
adjacentPoint,
|
||||||
// debugDrawPoint(intersections[0], { color: "red", permanent: true });
|
),
|
||||||
// debugDrawLine(
|
];
|
||||||
// lineSegment<GlobalPoint>(
|
|
||||||
// adjacentPoint,
|
|
||||||
// pointFromVector(
|
|
||||||
// vectorScale(
|
|
||||||
// vectorNormalize(vectorFromPoint(focusPointAbsolute, adjacentPoint)),
|
|
||||||
// interceptorLength,
|
|
||||||
// ),
|
|
||||||
// adjacentPoint,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// { permanent: true, color: "green" },
|
|
||||||
// );
|
|
||||||
|
|
||||||
if (intersections.length > 1) {
|
if (intersections.length > 1) {
|
||||||
// The adjacent point is outside the shape (+ gap)
|
// The adjacent point is outside the shape (+ gap)
|
||||||
|
@ -1731,21 +1726,6 @@ const determineFocusDistance = (
|
||||||
)
|
)
|
||||||
.sort((g, h) => Math.abs(g) - Math.abs(h));
|
.sort((g, h) => Math.abs(g) - Math.abs(h));
|
||||||
|
|
||||||
// debugClear();
|
|
||||||
// [
|
|
||||||
// lineSegmentIntersectionPoints(rotatedInterceptor, interceptees[0]),
|
|
||||||
// lineSegmentIntersectionPoints(rotatedInterceptor, interceptees[1]),
|
|
||||||
// ]
|
|
||||||
// .filter((p): p is GlobalPoint => p !== null)
|
|
||||||
// .forEach((p) => debugDrawPoint(p, { color: "black", permanent: true }));
|
|
||||||
// debugDrawPoint(determineFocusPoint(element, ordered[0] ?? 0, rotatedA), {
|
|
||||||
// color: "red",
|
|
||||||
// permanent: true,
|
|
||||||
// });
|
|
||||||
// debugDrawLine(rotatedInterceptor, { color: "green", permanent: true });
|
|
||||||
// debugDrawLine(interceptees[0], { color: "red", permanent: true });
|
|
||||||
// debugDrawLine(interceptees[1], { color: "red", permanent: true });
|
|
||||||
|
|
||||||
const signedDistanceRatio = ordered[0] ?? 0;
|
const signedDistanceRatio = ordered[0] ?? 0;
|
||||||
|
|
||||||
return signedDistanceRatio;
|
return signedDistanceRatio;
|
||||||
|
|
|
@ -13,6 +13,8 @@ import type {
|
||||||
|
|
||||||
import type Scene from "@excalidraw/excalidraw/scene/Scene";
|
import type Scene from "@excalidraw/excalidraw/scene/Scene";
|
||||||
|
|
||||||
|
import type { NonDeletedExcalidrawElement } from "@excalidraw/element/types";
|
||||||
|
|
||||||
import { updateBoundElements } from "./binding";
|
import { updateBoundElements } from "./binding";
|
||||||
import { getCommonBounds } from "./bounds";
|
import { getCommonBounds } from "./bounds";
|
||||||
import { mutateElement } from "./mutateElement";
|
import { mutateElement } from "./mutateElement";
|
||||||
|
@ -28,7 +30,7 @@ import {
|
||||||
} from "./typeChecks";
|
} from "./typeChecks";
|
||||||
|
|
||||||
import type { Bounds } from "./bounds";
|
import type { Bounds } from "./bounds";
|
||||||
import type { NonDeletedExcalidrawElement } from "./types";
|
import type { ExcalidrawElement } from "./types";
|
||||||
|
|
||||||
export const dragSelectedElements = (
|
export const dragSelectedElements = (
|
||||||
pointerDownState: PointerDownState,
|
pointerDownState: PointerDownState,
|
||||||
|
@ -82,13 +84,20 @@ export const dragSelectedElements = (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const commonBounds = getCommonBounds(
|
const origElements: ExcalidrawElement[] = [];
|
||||||
Array.from(elementsToUpdate).map(
|
|
||||||
(el) => pointerDownState.originalElements.get(el.id) ?? el,
|
for (const element of elementsToUpdate) {
|
||||||
),
|
const origElement = pointerDownState.originalElements.get(element.id);
|
||||||
);
|
// if original element is not set (e.g. when you duplicate during a drag
|
||||||
|
// operation), exit to avoid undefined behavior
|
||||||
|
if (!origElement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
origElements.push(origElement);
|
||||||
|
}
|
||||||
|
|
||||||
const adjustedOffset = calculateOffset(
|
const adjustedOffset = calculateOffset(
|
||||||
commonBounds,
|
getCommonBounds(origElements),
|
||||||
offset,
|
offset,
|
||||||
snapOffset,
|
snapOffset,
|
||||||
gridSize,
|
gridSize,
|
||||||
|
|
|
@ -1004,23 +1004,32 @@ export const updateElbowArrowPoints = (
|
||||||
// 0. During all element replacement in the scene, we just need to renormalize
|
// 0. During all element replacement in the scene, we just need to renormalize
|
||||||
// the arrow
|
// the arrow
|
||||||
// TODO (dwelle,mtolmacs): Remove this once Scene.getScene() is removed
|
// TODO (dwelle,mtolmacs): Remove this once Scene.getScene() is removed
|
||||||
|
const {
|
||||||
|
startBinding: updatedStartBinding,
|
||||||
|
endBinding: updatedEndBinding,
|
||||||
|
...restOfTheUpdates
|
||||||
|
} = updates;
|
||||||
const startBinding =
|
const startBinding =
|
||||||
typeof updates.startBinding !== "undefined"
|
typeof updatedStartBinding !== "undefined"
|
||||||
? updates.startBinding
|
? updatedStartBinding
|
||||||
: arrow.startBinding;
|
: arrow.startBinding;
|
||||||
const endBinding =
|
const endBinding =
|
||||||
typeof updates.endBinding !== "undefined"
|
typeof updatedEndBinding !== "undefined"
|
||||||
? updates.endBinding
|
? updatedEndBinding
|
||||||
: arrow.endBinding;
|
: arrow.endBinding;
|
||||||
const startElement =
|
const startElement =
|
||||||
startBinding &&
|
startBinding &&
|
||||||
getBindableElementForId(startBinding.elementId, elementsMap);
|
getBindableElementForId(startBinding.elementId, elementsMap);
|
||||||
const endElement =
|
const endElement =
|
||||||
endBinding && getBindableElementForId(endBinding.elementId, elementsMap);
|
endBinding && getBindableElementForId(endBinding.elementId, elementsMap);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
(startBinding && !startElement) ||
|
||||||
|
(endBinding && !endElement) ||
|
||||||
(elementsMap.size === 0 && validateElbowPoints(updatedPoints)) ||
|
(elementsMap.size === 0 && validateElbowPoints(updatedPoints)) ||
|
||||||
startElement?.id !== startBinding?.elementId ||
|
(Object.keys(restOfTheUpdates).length === 0 &&
|
||||||
endElement?.id !== endBinding?.elementId
|
(startElement?.id !== startBinding?.elementId ||
|
||||||
|
endElement?.id !== endBinding?.elementId))
|
||||||
) {
|
) {
|
||||||
return normalizeArrowElementUpdate(
|
return normalizeArrowElementUpdate(
|
||||||
updatedPoints.map((p) =>
|
updatedPoints.map((p) =>
|
||||||
|
@ -1080,7 +1089,8 @@ export const updateElbowArrowPoints = (
|
||||||
p,
|
p,
|
||||||
arrow.points[i] ?? pointFrom<LocalPoint>(Infinity, Infinity),
|
arrow.points[i] ?? pointFrom<LocalPoint>(Infinity, Infinity),
|
||||||
),
|
),
|
||||||
)
|
) &&
|
||||||
|
validateElbowPoints(updatedPoints)
|
||||||
) {
|
) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,6 +67,10 @@ export const actionDuplicateSelection = register({
|
||||||
icon: DuplicateIcon,
|
icon: DuplicateIcon,
|
||||||
trackEvent: { category: "element" },
|
trackEvent: { category: "element" },
|
||||||
perform: (elements, appState, formData, app) => {
|
perform: (elements, appState, formData, app) => {
|
||||||
|
if (appState.selectedElementsAreBeingDragged) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// duplicate selected point(s) if editing a line
|
// duplicate selected point(s) if editing a line
|
||||||
if (appState.editingLinearElement) {
|
if (appState.editingLinearElement) {
|
||||||
// TODO: Invariants should be checked here instead of duplicateSelectedPoints()
|
// TODO: Invariants should be checked here instead of duplicateSelectedPoints()
|
||||||
|
|
|
@ -55,6 +55,8 @@ import {
|
||||||
|
|
||||||
import { hasStrokeColor } from "@excalidraw/element/comparisons";
|
import { hasStrokeColor } from "@excalidraw/element/comparisons";
|
||||||
|
|
||||||
|
import { updateElbowArrowPoints } from "@excalidraw/element/elbowArrow";
|
||||||
|
|
||||||
import type { LocalPoint } from "@excalidraw/math";
|
import type { LocalPoint } from "@excalidraw/math";
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
|
@ -1582,7 +1584,7 @@ export const actionChangeArrowType = register({
|
||||||
if (!isArrowElement(el)) {
|
if (!isArrowElement(el)) {
|
||||||
return el;
|
return el;
|
||||||
}
|
}
|
||||||
const newElement = newElementWith(el, {
|
let newElement = newElementWith(el, {
|
||||||
roundness:
|
roundness:
|
||||||
value === ARROW_TYPE.round
|
value === ARROW_TYPE.round
|
||||||
? {
|
? {
|
||||||
|
@ -1597,6 +1599,8 @@ export const actionChangeArrowType = register({
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isElbowArrow(newElement)) {
|
if (isElbowArrow(newElement)) {
|
||||||
|
newElement.fixedSegments = null;
|
||||||
|
|
||||||
const elementsMap = app.scene.getNonDeletedElementsMap();
|
const elementsMap = app.scene.getNonDeletedElementsMap();
|
||||||
|
|
||||||
app.dismissLinearEditor();
|
app.dismissLinearEditor();
|
||||||
|
@ -1671,46 +1675,71 @@ export const actionChangeArrowType = register({
|
||||||
endHoveredElement &&
|
endHoveredElement &&
|
||||||
bindLinearElement(newElement, endHoveredElement, "end", elementsMap);
|
bindLinearElement(newElement, endHoveredElement, "end", elementsMap);
|
||||||
|
|
||||||
mutateElement(newElement, {
|
const startBinding =
|
||||||
points: [finalStartPoint, finalEndPoint].map(
|
startElement && newElement.startBinding
|
||||||
(p): LocalPoint =>
|
|
||||||
pointFrom(p[0] - newElement.x, p[1] - newElement.y),
|
|
||||||
),
|
|
||||||
...(startElement && newElement.startBinding
|
|
||||||
? {
|
? {
|
||||||
startBinding: {
|
// @ts-ignore TS cannot discern check above
|
||||||
// @ts-ignore TS cannot discern check above
|
...newElement.startBinding!,
|
||||||
...newElement.startBinding!,
|
...calculateFixedPointForElbowArrowBinding(
|
||||||
...calculateFixedPointForElbowArrowBinding(
|
newElement,
|
||||||
newElement,
|
startElement,
|
||||||
startElement,
|
"start",
|
||||||
"start",
|
elementsMap,
|
||||||
elementsMap,
|
),
|
||||||
),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
: {}),
|
: null;
|
||||||
...(endElement && newElement.endBinding
|
const endBinding =
|
||||||
|
endElement && newElement.endBinding
|
||||||
? {
|
? {
|
||||||
endBinding: {
|
// @ts-ignore TS cannot discern check above
|
||||||
// @ts-ignore TS cannot discern check above
|
...newElement.endBinding,
|
||||||
...newElement.endBinding,
|
...calculateFixedPointForElbowArrowBinding(
|
||||||
...calculateFixedPointForElbowArrowBinding(
|
newElement,
|
||||||
newElement,
|
endElement,
|
||||||
endElement,
|
"end",
|
||||||
"end",
|
elementsMap,
|
||||||
elementsMap,
|
),
|
||||||
),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
: {}),
|
: null;
|
||||||
});
|
|
||||||
|
newElement = {
|
||||||
|
...newElement,
|
||||||
|
startBinding,
|
||||||
|
endBinding,
|
||||||
|
...updateElbowArrowPoints(newElement, elementsMap, {
|
||||||
|
points: [finalStartPoint, finalEndPoint].map(
|
||||||
|
(p): LocalPoint =>
|
||||||
|
pointFrom(p[0] - newElement.x, p[1] - newElement.y),
|
||||||
|
),
|
||||||
|
startBinding,
|
||||||
|
endBinding,
|
||||||
|
fixedSegments: null,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
LinearElementEditor.updateEditorMidPointsCache(
|
LinearElementEditor.updateEditorMidPointsCache(
|
||||||
newElement,
|
newElement,
|
||||||
elementsMap,
|
elementsMap,
|
||||||
app.state,
|
app.state,
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
const elementsMap = app.scene.getNonDeletedElementsMap();
|
||||||
|
if (newElement.startBinding) {
|
||||||
|
const startElement = elementsMap.get(
|
||||||
|
newElement.startBinding.elementId,
|
||||||
|
) as ExcalidrawBindableElement;
|
||||||
|
if (startElement) {
|
||||||
|
bindLinearElement(newElement, startElement, "start", elementsMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (newElement.endBinding) {
|
||||||
|
const endElement = elementsMap.get(
|
||||||
|
newElement.endBinding.elementId,
|
||||||
|
) as ExcalidrawBindableElement;
|
||||||
|
if (endElement) {
|
||||||
|
bindLinearElement(newElement, endElement, "end", elementsMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return newElement;
|
return newElement;
|
||||||
|
|
|
@ -818,8 +818,8 @@ exports[`history > multiplayer undo/redo > conflicts in arrows and their bindabl
|
||||||
"type": "arrow",
|
"type": "arrow",
|
||||||
"updated": 1,
|
"updated": 1,
|
||||||
"version": 30,
|
"version": 30,
|
||||||
"width": 50,
|
"width": 0,
|
||||||
"x": 200,
|
"x": "149.29289",
|
||||||
"y": 0,
|
"y": 0,
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -852,7 +852,7 @@ History {
|
||||||
0,
|
0,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
50,
|
0,
|
||||||
0,
|
0,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
@ -937,7 +937,7 @@ History {
|
||||||
0,
|
0,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
50,
|
0,
|
||||||
0,
|
0,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue