@excalidraw/excalidraw 0.17.1-c0b80a0 → 0.17.1-c329470
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/dev/excalidraw-assets-dev/{chunk-JGDL4H2X.js → chunk-3DLVY5XU.js} +8272 -6864
- package/dist/browser/dev/excalidraw-assets-dev/chunk-3DLVY5XU.js.map +7 -0
- package/dist/browser/dev/excalidraw-assets-dev/{chunk-V7NFEZA6.js → chunk-NOAEU4NM.js} +9 -2
- package/dist/browser/dev/excalidraw-assets-dev/chunk-NOAEU4NM.js.map +7 -0
- package/dist/browser/dev/excalidraw-assets-dev/{en-ZSVWGT55.js → en-7IBTMWBG.js} +2 -2
- package/dist/browser/dev/excalidraw-assets-dev/{image-RJG3J34Y.js → image-N5AC7SEK.js} +2 -6
- package/dist/browser/dev/index.css +85 -50
- package/dist/browser/dev/index.css.map +3 -3
- package/dist/browser/dev/index.js +4375 -3766
- package/dist/browser/dev/index.js.map +4 -4
- package/dist/browser/prod/excalidraw-assets/{chunk-LDVEIXGO.js → chunk-7CSIPVOW.js} +2 -2
- package/dist/browser/prod/excalidraw-assets/chunk-TX3BU7T2.js +47 -0
- package/dist/browser/prod/excalidraw-assets/{en-UPNEHLDS.js → en-LOGQBETY.js} +1 -1
- package/dist/browser/prod/excalidraw-assets/image-3V4U7GZE.js +1 -0
- package/dist/browser/prod/index.css +1 -1
- package/dist/browser/prod/index.js +40 -40
- package/dist/dev/index.css +85 -50
- package/dist/dev/index.css.map +3 -3
- package/dist/dev/index.js +8688 -6706
- package/dist/dev/index.js.map +4 -4
- package/dist/{prod/locales/en-ZXYG7GCR.json → dev/locales/en-V6KXFSCK.json} +8 -1
- package/dist/excalidraw/actions/actionAlign.d.ts +7 -6
- package/dist/excalidraw/actions/actionAlign.js +14 -14
- package/dist/excalidraw/actions/actionClipboard.d.ts +7 -3
- package/dist/excalidraw/actions/actionDeleteSelected.d.ts +7 -3
- package/dist/excalidraw/actions/actionDeleteSelected.js +103 -34
- package/dist/excalidraw/actions/actionDuplicateSelection.js +105 -95
- package/dist/excalidraw/actions/actionFlip.js +16 -7
- package/dist/excalidraw/actions/actionFrame.d.ts +493 -0
- package/dist/excalidraw/actions/actionFrame.js +45 -2
- package/dist/excalidraw/actions/actionGroup.js +6 -4
- package/dist/excalidraw/actions/actionProperties.js +145 -116
- package/dist/excalidraw/actions/actionSelectAll.js +4 -3
- package/dist/excalidraw/actions/shortcuts.d.ts +1 -1
- package/dist/excalidraw/actions/shortcuts.js +1 -0
- package/dist/excalidraw/actions/types.d.ts +1 -1
- package/dist/excalidraw/align.d.ts +2 -1
- package/dist/excalidraw/align.js +15 -6
- package/dist/excalidraw/clipboard.d.ts +27 -5
- package/dist/excalidraw/clipboard.js +55 -28
- package/dist/excalidraw/components/Actions.d.ts +2 -1
- package/dist/excalidraw/components/Actions.js +4 -2
- package/dist/excalidraw/components/ActiveConfirmDialog.d.ts +1 -1
- package/dist/excalidraw/components/ActiveConfirmDialog.js +2 -3
- package/dist/excalidraw/components/App.d.ts +1 -0
- package/dist/excalidraw/components/App.js +216 -111
- package/dist/excalidraw/components/ColorPicker/ColorInput.js +2 -3
- package/dist/excalidraw/components/ColorPicker/ColorPicker.js +2 -3
- package/dist/excalidraw/components/ColorPicker/CustomColorList.js +1 -1
- package/dist/excalidraw/components/ColorPicker/Picker.js +1 -1
- package/dist/excalidraw/components/ColorPicker/PickerColorList.js +1 -1
- package/dist/excalidraw/components/ColorPicker/ShadeList.js +1 -1
- package/dist/excalidraw/components/ColorPicker/colorPickerUtils.d.ts +1 -1
- package/dist/excalidraw/components/ColorPicker/colorPickerUtils.js +1 -1
- package/dist/excalidraw/components/CommandPalette/CommandPalette.js +3 -3
- package/dist/excalidraw/components/ConfirmDialog.js +17 -5
- package/dist/excalidraw/components/Dialog.js +2 -3
- package/dist/excalidraw/components/EyeDropper.d.ts +1 -1
- package/dist/excalidraw/components/EyeDropper.js +1 -1
- package/dist/excalidraw/components/IconPicker.d.ts +2 -2
- package/dist/excalidraw/components/IconPicker.js +56 -53
- package/dist/excalidraw/components/LayerUI.js +6 -6
- package/dist/excalidraw/components/LibraryMenu.d.ts +2 -16
- package/dist/excalidraw/components/LibraryMenu.js +70 -28
- package/dist/excalidraw/components/LibraryMenuHeaderContent.js +4 -5
- package/dist/excalidraw/components/MobileMenu.js +1 -1
- package/dist/excalidraw/components/OverwriteConfirm/OverwriteConfirm.js +2 -3
- package/dist/excalidraw/components/OverwriteConfirm/OverwriteConfirmState.d.ts +1 -1
- package/dist/excalidraw/components/OverwriteConfirm/OverwriteConfirmState.js +2 -3
- package/dist/excalidraw/components/Range.d.ts +9 -0
- package/dist/excalidraw/components/Range.js +24 -0
- package/dist/excalidraw/components/SearchMenu.d.ts +1 -1
- package/dist/excalidraw/components/SearchMenu.js +3 -4
- package/dist/excalidraw/components/Sidebar/Sidebar.d.ts +1 -1
- package/dist/excalidraw/components/Sidebar/Sidebar.js +2 -3
- package/dist/excalidraw/components/Stats/Collapsible.d.ts +2 -1
- package/dist/excalidraw/components/Stats/Collapsible.js +2 -2
- package/dist/excalidraw/components/Stats/Dimension.js +94 -8
- package/dist/excalidraw/components/Stats/MultiDimension.js +8 -5
- package/dist/excalidraw/components/Stats/Position.js +63 -3
- package/dist/excalidraw/components/Stats/index.js +21 -4
- package/dist/excalidraw/components/Stats/utils.d.ts +1 -1
- package/dist/excalidraw/components/Stats/utils.js +2 -55
- package/dist/excalidraw/components/TTDDialog/TTDDialog.js +1 -1
- package/dist/excalidraw/components/ToolButton.js +4 -9
- package/dist/excalidraw/components/hoc/withInternalFallback.js +3 -3
- package/dist/excalidraw/components/hyperlink/Hyperlink.js +6 -12
- package/dist/excalidraw/components/icons.d.ts +9 -0
- package/dist/excalidraw/components/icons.js +4 -4
- package/dist/excalidraw/components/main-menu/DefaultItems.js +2 -3
- package/dist/excalidraw/constants.d.ts +5 -1
- package/dist/excalidraw/constants.js +9 -1
- package/dist/excalidraw/context/tunnels.d.ts +2 -1
- package/dist/excalidraw/context/tunnels.js +3 -1
- package/dist/excalidraw/data/blob.d.ts +1 -0
- package/dist/excalidraw/data/blob.js +7 -3
- package/dist/excalidraw/data/filesystem.d.ts +2 -1
- package/dist/excalidraw/data/filesystem.js +1 -0
- package/dist/excalidraw/data/image.d.ts +0 -6
- package/dist/excalidraw/data/image.js +1 -43
- package/dist/excalidraw/data/index.js +6 -6
- package/dist/excalidraw/data/library.d.ts +9 -3
- package/dist/excalidraw/data/library.js +43 -6
- package/dist/excalidraw/data/restore.js +26 -8
- package/dist/excalidraw/data/url.d.ts +0 -1
- package/dist/excalidraw/data/url.js +2 -4
- package/dist/excalidraw/editor-jotai.d.ts +56 -0
- package/dist/excalidraw/editor-jotai.js +8 -0
- package/dist/excalidraw/element/binding.d.ts +9 -6
- package/dist/excalidraw/element/binding.js +124 -44
- package/dist/excalidraw/element/bounds.js +10 -0
- package/dist/excalidraw/element/cropElement.d.ts +5 -0
- package/dist/excalidraw/element/cropElement.js +28 -1
- package/dist/excalidraw/element/dragElements.js +13 -7
- package/dist/excalidraw/element/elbowArrow.d.ts +16 -0
- package/dist/excalidraw/element/elbowArrow.js +1268 -0
- package/dist/excalidraw/element/embeddable.js +4 -5
- package/dist/excalidraw/element/flowchart.d.ts +1 -1
- package/dist/excalidraw/element/flowchart.js +25 -9
- package/dist/excalidraw/element/heading.d.ts +5 -1
- package/dist/excalidraw/element/heading.js +5 -1
- package/dist/excalidraw/element/image.js +19 -5
- package/dist/excalidraw/element/linearElementEditor.d.ts +9 -10
- package/dist/excalidraw/element/linearElementEditor.js +97 -38
- package/dist/excalidraw/element/mutateElement.d.ts +3 -1
- package/dist/excalidraw/element/mutateElement.js +31 -4
- package/dist/excalidraw/element/newElement.d.ts +8 -12
- package/dist/excalidraw/element/newElement.js +36 -21
- package/dist/excalidraw/element/resizeElements.d.ts +20 -5
- package/dist/excalidraw/element/resizeElements.js +593 -361
- package/dist/excalidraw/element/sortElements.js +1 -4
- package/dist/excalidraw/element/types.d.ts +23 -1
- package/dist/excalidraw/fonts/Fonts.d.ts +0 -16
- package/dist/excalidraw/fonts/Fonts.js +6 -31
- package/dist/excalidraw/frame.d.ts +11 -5
- package/dist/excalidraw/frame.js +146 -35
- package/dist/excalidraw/groups.js +3 -0
- package/dist/excalidraw/hooks/useLibraryItemSvg.d.ts +1 -1
- package/dist/excalidraw/hooks/useLibraryItemSvg.js +2 -3
- package/dist/excalidraw/hooks/useScrollPosition.js +1 -1
- package/dist/excalidraw/i18n.js +3 -4
- package/dist/excalidraw/index.js +3 -4
- package/dist/excalidraw/locales/en.json +8 -1
- package/dist/excalidraw/renderer/interactiveScene.js +43 -32
- package/dist/excalidraw/renderer/staticScene.js +6 -4
- package/dist/excalidraw/renderer/staticSvgScene.js +1 -1
- package/dist/excalidraw/scene/Shape.js +40 -17
- package/dist/excalidraw/scene/comparisons.d.ts +0 -477
- package/dist/excalidraw/scene/comparisons.js +0 -37
- package/dist/excalidraw/scene/export.d.ts +7 -0
- package/dist/excalidraw/scene/export.js +107 -43
- package/dist/excalidraw/scene/index.d.ts +1 -1
- package/dist/excalidraw/scene/index.js +1 -1
- package/dist/excalidraw/scene/selection.js +4 -1
- package/dist/excalidraw/types.d.ts +15 -0
- package/dist/excalidraw/utility-types.d.ts +1 -0
- package/dist/excalidraw/utils.d.ts +8 -1
- package/dist/excalidraw/utils.js +9 -0
- package/dist/excalidraw/visualdebug.d.ts +8 -1
- package/dist/excalidraw/visualdebug.js +3 -0
- package/dist/math/line.d.ts +19 -0
- package/dist/math/line.js +32 -3
- package/dist/math/point.d.ts +10 -0
- package/dist/math/point.js +12 -1
- package/dist/prod/index.css +1 -1
- package/dist/prod/index.js +29 -44
- package/dist/{dev/locales/en-ZXYG7GCR.json → prod/locales/en-V6KXFSCK.json} +8 -1
- package/package.json +5 -2
- package/dist/browser/dev/excalidraw-assets-dev/chunk-JGDL4H2X.js.map +0 -7
- package/dist/browser/dev/excalidraw-assets-dev/chunk-V7NFEZA6.js.map +0 -7
- package/dist/browser/prod/excalidraw-assets/chunk-S2XKB3DE.js +0 -62
- package/dist/browser/prod/excalidraw-assets/image-OFI2YYMP.js +0 -1
- package/dist/excalidraw/element/routing.d.ts +0 -12
- package/dist/excalidraw/element/routing.js +0 -642
- package/dist/excalidraw/jotai.d.ts +0 -34
- package/dist/excalidraw/jotai.js +0 -18
- /package/dist/browser/dev/excalidraw-assets-dev/{en-ZSVWGT55.js.map → en-7IBTMWBG.js.map} +0 -0
- /package/dist/browser/dev/excalidraw-assets-dev/{image-RJG3J34Y.js.map → image-N5AC7SEK.js.map} +0 -0
|
@@ -1,49 +1,72 @@
|
|
|
1
1
|
import { MIN_FONT_SIZE, SHIFT_LOCKING_ANGLE } from "../constants";
|
|
2
2
|
import { rescalePoints } from "../points";
|
|
3
|
-
import { getElementAbsoluteCoords, getCommonBounds, getResizedElementAbsoluteCoords, getCommonBoundingBox, } from "./bounds";
|
|
3
|
+
import { getElementAbsoluteCoords, getCommonBounds, getResizedElementAbsoluteCoords, getCommonBoundingBox, getElementBounds, } from "./bounds";
|
|
4
4
|
import { isArrowElement, isBoundToContainer, isElbowArrow, isFrameLikeElement, isFreeDrawElement, isImageElement, isLinearElement, isTextElement, } from "./typeChecks";
|
|
5
5
|
import { mutateElement } from "./mutateElement";
|
|
6
6
|
import { getFontString } from "../utils";
|
|
7
7
|
import { getArrowLocalFixedPoints, updateBoundElements } from "./binding";
|
|
8
|
-
import Scene from "../scene/Scene";
|
|
9
8
|
import { getApproxMinLineWidth, getBoundTextElement, getBoundTextElementId, getContainerElement, handleBindTextResize, getBoundTextMaxWidth, getApproxMinLineHeight, measureText, getMinTextElementWidth, } from "./textElement";
|
|
10
9
|
import { wrapText } from "./textWrapping";
|
|
11
10
|
import { LinearElementEditor } from "./linearElementEditor";
|
|
12
11
|
import { isInGroup } from "../groups";
|
|
13
|
-
import { mutateElbowArrow } from "./routing";
|
|
14
12
|
import { pointCenter, normalizeRadians, pointFrom, pointFromPair, pointRotateRads, } from "../../math";
|
|
15
13
|
// Returns true when transform (resizing/rotation) happened
|
|
16
|
-
export const transformElements = (originalElements, transformHandleType, selectedElements, elementsMap, shouldRotateWithDiscreteAngle, shouldResizeFromCenter, shouldMaintainAspectRatio, pointerX, pointerY, centerX, centerY) => {
|
|
14
|
+
export const transformElements = (originalElements, transformHandleType, selectedElements, elementsMap, scene, shouldRotateWithDiscreteAngle, shouldResizeFromCenter, shouldMaintainAspectRatio, pointerX, pointerY, centerX, centerY) => {
|
|
17
15
|
if (selectedElements.length === 1) {
|
|
18
16
|
const [element] = selectedElements;
|
|
19
17
|
if (transformHandleType === "rotation") {
|
|
20
18
|
if (!isElbowArrow(element)) {
|
|
21
|
-
rotateSingleElement(element, elementsMap, pointerX, pointerY, shouldRotateWithDiscreteAngle);
|
|
19
|
+
rotateSingleElement(element, elementsMap, scene, pointerX, pointerY, shouldRotateWithDiscreteAngle);
|
|
22
20
|
updateBoundElements(element, elementsMap);
|
|
23
21
|
}
|
|
24
22
|
}
|
|
25
23
|
else if (isTextElement(element) && transformHandleType) {
|
|
26
24
|
resizeSingleTextElement(originalElements, element, elementsMap, transformHandleType, shouldResizeFromCenter, pointerX, pointerY);
|
|
27
25
|
updateBoundElements(element, elementsMap);
|
|
26
|
+
return true;
|
|
28
27
|
}
|
|
29
28
|
else if (transformHandleType) {
|
|
30
|
-
|
|
29
|
+
const elementId = selectedElements[0].id;
|
|
30
|
+
const latestElement = elementsMap.get(elementId);
|
|
31
|
+
const origElement = originalElements.get(elementId);
|
|
32
|
+
if (latestElement && origElement) {
|
|
33
|
+
const { nextWidth, nextHeight } = getNextSingleWidthAndHeightFromPointer(latestElement, origElement, elementsMap, originalElements, transformHandleType, pointerX, pointerY, {
|
|
34
|
+
shouldMaintainAspectRatio,
|
|
35
|
+
shouldResizeFromCenter,
|
|
36
|
+
});
|
|
37
|
+
resizeSingleElement(nextWidth, nextHeight, latestElement, origElement, elementsMap, originalElements, transformHandleType, {
|
|
38
|
+
shouldMaintainAspectRatio,
|
|
39
|
+
shouldResizeFromCenter,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
31
42
|
}
|
|
32
43
|
return true;
|
|
33
44
|
}
|
|
34
45
|
else if (selectedElements.length > 1) {
|
|
35
46
|
if (transformHandleType === "rotation") {
|
|
36
|
-
rotateMultipleElements(originalElements, selectedElements, elementsMap, pointerX, pointerY, shouldRotateWithDiscreteAngle, centerX, centerY);
|
|
47
|
+
rotateMultipleElements(originalElements, selectedElements, elementsMap, scene, pointerX, pointerY, shouldRotateWithDiscreteAngle, centerX, centerY);
|
|
37
48
|
return true;
|
|
38
49
|
}
|
|
39
50
|
else if (transformHandleType) {
|
|
40
|
-
|
|
51
|
+
const { nextWidth, nextHeight, flipByX, flipByY, originalBoundingBox } = getNextMultipleWidthAndHeightFromPointer(selectedElements, originalElements, elementsMap, transformHandleType, pointerX, pointerY, {
|
|
52
|
+
shouldMaintainAspectRatio,
|
|
53
|
+
shouldResizeFromCenter,
|
|
54
|
+
});
|
|
55
|
+
resizeMultipleElements(selectedElements, elementsMap, transformHandleType, scene, originalElements, {
|
|
56
|
+
shouldResizeFromCenter,
|
|
57
|
+
shouldMaintainAspectRatio,
|
|
58
|
+
flipByX,
|
|
59
|
+
flipByY,
|
|
60
|
+
nextWidth,
|
|
61
|
+
nextHeight,
|
|
62
|
+
originalBoundingBox,
|
|
63
|
+
});
|
|
41
64
|
return true;
|
|
42
65
|
}
|
|
43
66
|
}
|
|
44
67
|
return false;
|
|
45
68
|
};
|
|
46
|
-
const rotateSingleElement = (element, elementsMap, pointerX, pointerY, shouldRotateWithDiscreteAngle) => {
|
|
69
|
+
const rotateSingleElement = (element, elementsMap, scene, pointerX, pointerY, shouldRotateWithDiscreteAngle) => {
|
|
47
70
|
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element, elementsMap);
|
|
48
71
|
const cx = (x1 + x2) / 2;
|
|
49
72
|
const cy = (y1 + y2) / 2;
|
|
@@ -63,7 +86,7 @@ const rotateSingleElement = (element, elementsMap, pointerX, pointerY, shouldRot
|
|
|
63
86
|
const boundTextElementId = getBoundTextElementId(element);
|
|
64
87
|
mutateElement(element, { angle });
|
|
65
88
|
if (boundTextElementId) {
|
|
66
|
-
const textElement =
|
|
89
|
+
const textElement = scene.getElement(boundTextElementId);
|
|
67
90
|
if (textElement && !isArrowElement(element)) {
|
|
68
91
|
mutateElement(textElement, { angle });
|
|
69
92
|
}
|
|
@@ -211,67 +234,203 @@ const resizeSingleTextElement = (originalElements, element, elementsMap, transfo
|
|
|
211
234
|
mutateElement(element, resizedElement);
|
|
212
235
|
}
|
|
213
236
|
};
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
const startBottomRight = pointFrom(x2, y2);
|
|
220
|
-
const startCenter = pointCenter(startTopLeft, startBottomRight);
|
|
221
|
-
// Calculate new dimensions based on cursor position
|
|
222
|
-
const rotatedPointer = pointRotateRads(pointFrom(pointerX, pointerY), startCenter, -stateAtResizeStart.angle);
|
|
223
|
-
// Get bounds corners rendered on screen
|
|
224
|
-
const [esx1, esy1, esx2, esy2] = getResizedElementAbsoluteCoords(element, element.width, element.height, true);
|
|
225
|
-
const boundsCurrentWidth = esx2 - esx1;
|
|
226
|
-
const boundsCurrentHeight = esy2 - esy1;
|
|
227
|
-
// It's important we set the initial scale value based on the width and height at resize start,
|
|
228
|
-
// otherwise previous dimensions affected by modifiers will be taken into account.
|
|
229
|
-
const atStartBoundsWidth = startBottomRight[0] - startTopLeft[0];
|
|
230
|
-
const atStartBoundsHeight = startBottomRight[1] - startTopLeft[1];
|
|
231
|
-
let scaleX = atStartBoundsWidth / boundsCurrentWidth;
|
|
232
|
-
let scaleY = atStartBoundsHeight / boundsCurrentHeight;
|
|
233
|
-
let boundTextFont = {};
|
|
234
|
-
const boundTextElement = getBoundTextElement(element, elementsMap);
|
|
235
|
-
if (transformHandleDirection.includes("e")) {
|
|
236
|
-
scaleX = (rotatedPointer[0] - startTopLeft[0]) / boundsCurrentWidth;
|
|
237
|
-
}
|
|
238
|
-
if (transformHandleDirection.includes("s")) {
|
|
239
|
-
scaleY = (rotatedPointer[1] - startTopLeft[1]) / boundsCurrentHeight;
|
|
237
|
+
const rotateMultipleElements = (originalElements, elements, elementsMap, scene, pointerX, pointerY, shouldRotateWithDiscreteAngle, centerX, centerY) => {
|
|
238
|
+
let centerAngle = (5 * Math.PI) / 2 + Math.atan2(pointerY - centerY, pointerX - centerX);
|
|
239
|
+
if (shouldRotateWithDiscreteAngle) {
|
|
240
|
+
centerAngle += SHIFT_LOCKING_ANGLE / 2;
|
|
241
|
+
centerAngle -= centerAngle % SHIFT_LOCKING_ANGLE;
|
|
240
242
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
+
for (const element of elements) {
|
|
244
|
+
if (!isFrameLikeElement(element)) {
|
|
245
|
+
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element, elementsMap);
|
|
246
|
+
const cx = (x1 + x2) / 2;
|
|
247
|
+
const cy = (y1 + y2) / 2;
|
|
248
|
+
const origAngle = originalElements.get(element.id)?.angle ?? element.angle;
|
|
249
|
+
const [rotatedCX, rotatedCY] = pointRotateRads(pointFrom(cx, cy), pointFrom(centerX, centerY), (centerAngle + origAngle - element.angle));
|
|
250
|
+
if (isElbowArrow(element)) {
|
|
251
|
+
// Needed to re-route the arrow
|
|
252
|
+
mutateElement(element, {
|
|
253
|
+
points: getArrowLocalFixedPoints(element, elementsMap),
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
mutateElement(element, {
|
|
258
|
+
x: element.x + (rotatedCX - cx),
|
|
259
|
+
y: element.y + (rotatedCY - cy),
|
|
260
|
+
angle: normalizeRadians((centerAngle + origAngle)),
|
|
261
|
+
}, false);
|
|
262
|
+
}
|
|
263
|
+
updateBoundElements(element, elementsMap, {
|
|
264
|
+
simultaneouslyUpdated: elements,
|
|
265
|
+
});
|
|
266
|
+
const boundText = getBoundTextElement(element, elementsMap);
|
|
267
|
+
if (boundText && !isArrowElement(element)) {
|
|
268
|
+
mutateElement(boundText, {
|
|
269
|
+
x: boundText.x + (rotatedCX - cx),
|
|
270
|
+
y: boundText.y + (rotatedCY - cy),
|
|
271
|
+
angle: normalizeRadians((centerAngle + origAngle)),
|
|
272
|
+
}, false);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
243
275
|
}
|
|
244
|
-
|
|
245
|
-
|
|
276
|
+
scene.triggerUpdate();
|
|
277
|
+
};
|
|
278
|
+
export const getResizeOffsetXY = (transformHandleType, selectedElements, elementsMap, x, y) => {
|
|
279
|
+
const [x1, y1, x2, y2] = selectedElements.length === 1
|
|
280
|
+
? getElementAbsoluteCoords(selectedElements[0], elementsMap)
|
|
281
|
+
: getCommonBounds(selectedElements);
|
|
282
|
+
const cx = (x1 + x2) / 2;
|
|
283
|
+
const cy = (y1 + y2) / 2;
|
|
284
|
+
const angle = (selectedElements.length === 1 ? selectedElements[0].angle : 0);
|
|
285
|
+
[x, y] = pointRotateRads(pointFrom(x, y), pointFrom(cx, cy), -angle);
|
|
286
|
+
switch (transformHandleType) {
|
|
287
|
+
case "n":
|
|
288
|
+
return pointRotateRads(pointFrom(x - (x1 + x2) / 2, y - y1), pointFrom(0, 0), angle);
|
|
289
|
+
case "s":
|
|
290
|
+
return pointRotateRads(pointFrom(x - (x1 + x2) / 2, y - y2), pointFrom(0, 0), angle);
|
|
291
|
+
case "w":
|
|
292
|
+
return pointRotateRads(pointFrom(x - x1, y - (y1 + y2) / 2), pointFrom(0, 0), angle);
|
|
293
|
+
case "e":
|
|
294
|
+
return pointRotateRads(pointFrom(x - x2, y - (y1 + y2) / 2), pointFrom(0, 0), angle);
|
|
295
|
+
case "nw":
|
|
296
|
+
return pointRotateRads(pointFrom(x - x1, y - y1), pointFrom(0, 0), angle);
|
|
297
|
+
case "ne":
|
|
298
|
+
return pointRotateRads(pointFrom(x - x2, y - y1), pointFrom(0, 0), angle);
|
|
299
|
+
case "sw":
|
|
300
|
+
return pointRotateRads(pointFrom(x - x1, y - y2), pointFrom(0, 0), angle);
|
|
301
|
+
case "se":
|
|
302
|
+
return pointRotateRads(pointFrom(x - x2, y - y2), pointFrom(0, 0), angle);
|
|
303
|
+
default:
|
|
304
|
+
return [0, 0];
|
|
246
305
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
306
|
+
};
|
|
307
|
+
export const getResizeArrowDirection = (transformHandleType, element) => {
|
|
308
|
+
const [, [px, py]] = element.points;
|
|
309
|
+
const isResizeEnd = (transformHandleType === "nw" && (px < 0 || py < 0)) ||
|
|
310
|
+
(transformHandleType === "ne" && px >= 0) ||
|
|
311
|
+
(transformHandleType === "sw" && px <= 0) ||
|
|
312
|
+
(transformHandleType === "se" && (px > 0 || py > 0));
|
|
313
|
+
return isResizeEnd ? "end" : "origin";
|
|
314
|
+
};
|
|
315
|
+
const getResizeAnchor = (handleDirection, shouldMaintainAspectRatio, shouldResizeFromCenter) => {
|
|
255
316
|
if (shouldResizeFromCenter) {
|
|
256
|
-
|
|
257
|
-
eleNewHeight = 2 * eleNewHeight - eleInitialHeight;
|
|
317
|
+
return "center";
|
|
258
318
|
}
|
|
259
|
-
// adjust dimensions to keep sides ratio
|
|
260
319
|
if (shouldMaintainAspectRatio) {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
320
|
+
switch (handleDirection) {
|
|
321
|
+
case "n":
|
|
322
|
+
return "south-side";
|
|
323
|
+
case "e": {
|
|
324
|
+
return "west-side";
|
|
325
|
+
}
|
|
326
|
+
case "s":
|
|
327
|
+
return "north-side";
|
|
328
|
+
case "w":
|
|
329
|
+
return "east-side";
|
|
330
|
+
case "ne":
|
|
331
|
+
return "bottom-left";
|
|
332
|
+
case "nw":
|
|
333
|
+
return "bottom-right";
|
|
334
|
+
case "se":
|
|
335
|
+
return "top-left";
|
|
336
|
+
case "sw":
|
|
337
|
+
return "top-right";
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
if (["e", "se", "s"].includes(handleDirection)) {
|
|
341
|
+
return "top-left";
|
|
342
|
+
}
|
|
343
|
+
else if (["n", "nw", "w"].includes(handleDirection)) {
|
|
344
|
+
return "bottom-right";
|
|
345
|
+
}
|
|
346
|
+
else if (handleDirection === "ne") {
|
|
347
|
+
return "bottom-left";
|
|
348
|
+
}
|
|
349
|
+
return "top-right";
|
|
350
|
+
};
|
|
351
|
+
const getResizedOrigin = (prevOrigin, prevWidth, prevHeight, newWidth, newHeight, angle, handleDirection, shouldMaintainAspectRatio, shouldResizeFromCenter) => {
|
|
352
|
+
const anchor = getResizeAnchor(handleDirection, shouldMaintainAspectRatio, shouldResizeFromCenter);
|
|
353
|
+
const [x, y] = prevOrigin;
|
|
354
|
+
switch (anchor) {
|
|
355
|
+
case "top-left":
|
|
356
|
+
return {
|
|
357
|
+
x: x +
|
|
358
|
+
(prevWidth - newWidth) / 2 +
|
|
359
|
+
((newWidth - prevWidth) / 2) * Math.cos(angle) +
|
|
360
|
+
((prevHeight - newHeight) / 2) * Math.sin(angle),
|
|
361
|
+
y: y +
|
|
362
|
+
(prevHeight - newHeight) / 2 +
|
|
363
|
+
((newWidth - prevWidth) / 2) * Math.sin(angle) +
|
|
364
|
+
((newHeight - prevHeight) / 2) * Math.cos(angle),
|
|
365
|
+
};
|
|
366
|
+
case "top-right":
|
|
367
|
+
return {
|
|
368
|
+
x: x +
|
|
369
|
+
((prevWidth - newWidth) / 2) * (Math.cos(angle) + 1) +
|
|
370
|
+
((prevHeight - newHeight) / 2) * Math.sin(angle),
|
|
371
|
+
y: y +
|
|
372
|
+
(prevHeight - newHeight) / 2 +
|
|
373
|
+
((prevWidth - newWidth) / 2) * Math.sin(angle) +
|
|
374
|
+
((newHeight - prevHeight) / 2) * Math.cos(angle),
|
|
375
|
+
};
|
|
376
|
+
case "bottom-left":
|
|
377
|
+
return {
|
|
378
|
+
x: x +
|
|
379
|
+
((prevWidth - newWidth) / 2) * (1 - Math.cos(angle)) +
|
|
380
|
+
((newHeight - prevHeight) / 2) * Math.sin(angle),
|
|
381
|
+
y: y +
|
|
382
|
+
((prevHeight - newHeight) / 2) * (Math.cos(angle) + 1) +
|
|
383
|
+
((newWidth - prevWidth) / 2) * Math.sin(angle),
|
|
384
|
+
};
|
|
385
|
+
case "bottom-right":
|
|
386
|
+
return {
|
|
387
|
+
x: x +
|
|
388
|
+
((prevWidth - newWidth) / 2) * (Math.cos(angle) + 1) +
|
|
389
|
+
((newHeight - prevHeight) / 2) * Math.sin(angle),
|
|
390
|
+
y: y +
|
|
391
|
+
((prevHeight - newHeight) / 2) * (Math.cos(angle) + 1) +
|
|
392
|
+
((prevWidth - newWidth) / 2) * Math.sin(angle),
|
|
393
|
+
};
|
|
394
|
+
case "center":
|
|
395
|
+
return {
|
|
396
|
+
x: x - (newWidth - prevWidth) / 2,
|
|
397
|
+
y: y - (newHeight - prevHeight) / 2,
|
|
398
|
+
};
|
|
399
|
+
case "east-side":
|
|
400
|
+
return {
|
|
401
|
+
x: x + ((prevWidth - newWidth) / 2) * (Math.cos(angle) + 1),
|
|
402
|
+
y: y +
|
|
403
|
+
((prevWidth - newWidth) / 2) * Math.sin(angle) +
|
|
404
|
+
(prevHeight - newHeight) / 2,
|
|
405
|
+
};
|
|
406
|
+
case "west-side":
|
|
407
|
+
return {
|
|
408
|
+
x: x + ((prevWidth - newWidth) / 2) * (1 - Math.cos(angle)),
|
|
409
|
+
y: y +
|
|
410
|
+
((newWidth - prevWidth) / 2) * Math.sin(angle) +
|
|
411
|
+
(prevHeight - newHeight) / 2,
|
|
412
|
+
};
|
|
413
|
+
case "north-side":
|
|
414
|
+
return {
|
|
415
|
+
x: x +
|
|
416
|
+
(prevWidth - newWidth) / 2 +
|
|
417
|
+
((prevHeight - newHeight) / 2) * Math.sin(angle),
|
|
418
|
+
y: y + ((newHeight - prevHeight) / 2) * (Math.cos(angle) - 1),
|
|
419
|
+
};
|
|
420
|
+
case "south-side":
|
|
421
|
+
return {
|
|
422
|
+
x: x +
|
|
423
|
+
(prevWidth - newWidth) / 2 +
|
|
424
|
+
((newHeight - prevHeight) / 2) * Math.sin(angle),
|
|
425
|
+
y: y + ((prevHeight - newHeight) / 2) * (Math.cos(angle) + 1),
|
|
426
|
+
};
|
|
272
427
|
}
|
|
428
|
+
};
|
|
429
|
+
export const resizeSingleElement = (nextWidth, nextHeight, latestElement, origElement, elementsMap, originalElementsMap, handleDirection, { shouldInformMutation = true, shouldMaintainAspectRatio = false, shouldResizeFromCenter = false, } = {}) => {
|
|
430
|
+
let boundTextFont = {};
|
|
431
|
+
const boundTextElement = getBoundTextElement(latestElement, elementsMap);
|
|
273
432
|
if (boundTextElement) {
|
|
274
|
-
const stateOfBoundTextElementAtResize =
|
|
433
|
+
const stateOfBoundTextElementAtResize = originalElementsMap.get(boundTextElement.id);
|
|
275
434
|
if (stateOfBoundTextElementAtResize) {
|
|
276
435
|
boundTextFont = {
|
|
277
436
|
fontSize: stateOfBoundTextElementAtResize.fontSize,
|
|
@@ -279,9 +438,9 @@ export const resizeSingleElement = (originalElements, shouldMaintainAspectRatio,
|
|
|
279
438
|
}
|
|
280
439
|
if (shouldMaintainAspectRatio) {
|
|
281
440
|
const updatedElement = {
|
|
282
|
-
...
|
|
283
|
-
width:
|
|
284
|
-
height:
|
|
441
|
+
...latestElement,
|
|
442
|
+
width: nextWidth,
|
|
443
|
+
height: nextHeight,
|
|
285
444
|
};
|
|
286
445
|
const nextFont = measureFontSizeFromWidth(boundTextElement, elementsMap, getBoundTextMaxWidth(updatedElement, boundTextElement));
|
|
287
446
|
if (nextFont === null) {
|
|
@@ -294,147 +453,140 @@ export const resizeSingleElement = (originalElements, shouldMaintainAspectRatio,
|
|
|
294
453
|
else {
|
|
295
454
|
const minWidth = getApproxMinLineWidth(getFontString(boundTextElement), boundTextElement.lineHeight);
|
|
296
455
|
const minHeight = getApproxMinLineHeight(boundTextElement.fontSize, boundTextElement.lineHeight);
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
const
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
];
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
const
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
}
|
|
329
|
-
const flipX = eleNewWidth < 0;
|
|
330
|
-
const flipY = eleNewHeight < 0;
|
|
331
|
-
// Flip horizontally
|
|
332
|
-
if (flipX) {
|
|
333
|
-
if (transformHandleDirection.includes("e")) {
|
|
334
|
-
newTopLeft[0] -= Math.abs(newBoundsWidth);
|
|
335
|
-
}
|
|
336
|
-
if (transformHandleDirection.includes("w")) {
|
|
337
|
-
newTopLeft[0] += Math.abs(newBoundsWidth);
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
// Flip vertically
|
|
341
|
-
if (flipY) {
|
|
342
|
-
if (transformHandleDirection.includes("s")) {
|
|
343
|
-
newTopLeft[1] -= Math.abs(newBoundsHeight);
|
|
344
|
-
}
|
|
345
|
-
if (transformHandleDirection.includes("n")) {
|
|
346
|
-
newTopLeft[1] += Math.abs(newBoundsHeight);
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
if (shouldResizeFromCenter) {
|
|
350
|
-
newTopLeft[0] = startCenter[0] - Math.abs(newBoundsWidth) / 2;
|
|
351
|
-
newTopLeft[1] = startCenter[1] - Math.abs(newBoundsHeight) / 2;
|
|
352
|
-
}
|
|
353
|
-
// adjust topLeft to new rotation point
|
|
354
|
-
const angle = stateAtResizeStart.angle;
|
|
355
|
-
const rotatedTopLeft = pointRotateRads(pointFromPair(newTopLeft), startCenter, angle);
|
|
356
|
-
const newCenter = pointFrom(newTopLeft[0] + Math.abs(newBoundsWidth) / 2, newTopLeft[1] + Math.abs(newBoundsHeight) / 2);
|
|
357
|
-
const rotatedNewCenter = pointRotateRads(newCenter, startCenter, angle);
|
|
358
|
-
newTopLeft = pointRotateRads(rotatedTopLeft, rotatedNewCenter, -angle);
|
|
359
|
-
// For linear elements (x,y) are the coordinates of the first drawn point not the top-left corner
|
|
360
|
-
// So we need to readjust (x,y) to be where the first point should be
|
|
361
|
-
const newOrigin = [...newTopLeft];
|
|
362
|
-
const linearElementXOffset = stateAtResizeStart.x - newBoundsX1;
|
|
363
|
-
const linearElementYOffset = stateAtResizeStart.y - newBoundsY1;
|
|
364
|
-
newOrigin[0] += linearElementXOffset;
|
|
365
|
-
newOrigin[1] += linearElementYOffset;
|
|
366
|
-
const nextX = newOrigin[0];
|
|
367
|
-
const nextY = newOrigin[1];
|
|
368
|
-
// Readjust points for linear elements
|
|
369
|
-
let rescaledElementPointsY;
|
|
370
|
-
let rescaledPoints;
|
|
371
|
-
if (isLinearElement(element) || isFreeDrawElement(element)) {
|
|
372
|
-
rescaledElementPointsY = rescalePoints(1, eleNewHeight, stateAtResizeStart.points, true);
|
|
373
|
-
rescaledPoints = rescalePoints(0, eleNewWidth, rescaledElementPointsY, true);
|
|
374
|
-
}
|
|
375
|
-
const resizedElement = {
|
|
376
|
-
width: Math.abs(eleNewWidth),
|
|
377
|
-
height: Math.abs(eleNewHeight),
|
|
378
|
-
x: nextX,
|
|
379
|
-
y: nextY,
|
|
380
|
-
points: rescaledPoints,
|
|
381
|
-
};
|
|
382
|
-
if ("scale" in element && "scale" in stateAtResizeStart) {
|
|
383
|
-
mutateElement(element, {
|
|
456
|
+
nextWidth = Math.max(nextWidth, minWidth);
|
|
457
|
+
nextHeight = Math.max(nextHeight, minHeight);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
const rescaledPoints = rescalePointsInElement(origElement, nextWidth, nextHeight, true);
|
|
461
|
+
let previousOrigin = pointFrom(origElement.x, origElement.y);
|
|
462
|
+
if (isLinearElement(origElement)) {
|
|
463
|
+
const [x1, y1] = getElementBounds(origElement, originalElementsMap);
|
|
464
|
+
previousOrigin = pointFrom(x1, y1);
|
|
465
|
+
}
|
|
466
|
+
const newOrigin = getResizedOrigin(previousOrigin, origElement.width, origElement.height, nextWidth, nextHeight, origElement.angle, handleDirection, shouldMaintainAspectRatio, shouldResizeFromCenter);
|
|
467
|
+
if (isLinearElement(origElement) && rescaledPoints.points) {
|
|
468
|
+
const offsetX = origElement.x - previousOrigin[0];
|
|
469
|
+
const offsetY = origElement.y - previousOrigin[1];
|
|
470
|
+
newOrigin.x += offsetX;
|
|
471
|
+
newOrigin.y += offsetY;
|
|
472
|
+
const scaledX = rescaledPoints.points[0][0];
|
|
473
|
+
const scaledY = rescaledPoints.points[0][1];
|
|
474
|
+
newOrigin.x += scaledX;
|
|
475
|
+
newOrigin.y += scaledY;
|
|
476
|
+
rescaledPoints.points = rescaledPoints.points.map((p) => pointFrom(p[0] - scaledX, p[1] - scaledY));
|
|
477
|
+
}
|
|
478
|
+
// flipping
|
|
479
|
+
if (nextWidth < 0) {
|
|
480
|
+
newOrigin.x = newOrigin.x + nextWidth;
|
|
481
|
+
}
|
|
482
|
+
if (nextHeight < 0) {
|
|
483
|
+
newOrigin.y = newOrigin.y + nextHeight;
|
|
484
|
+
}
|
|
485
|
+
if ("scale" in latestElement && "scale" in origElement) {
|
|
486
|
+
mutateElement(latestElement, {
|
|
384
487
|
scale: [
|
|
385
488
|
// defaulting because scaleX/Y can be 0/-0
|
|
386
|
-
(Math.sign(
|
|
387
|
-
|
|
388
|
-
(Math.sign(newBoundsY2 - stateAtResizeStart.y) ||
|
|
389
|
-
stateAtResizeStart.scale[1]) * stateAtResizeStart.scale[1],
|
|
489
|
+
(Math.sign(nextWidth) || origElement.scale[0]) * origElement.scale[0],
|
|
490
|
+
(Math.sign(nextHeight) || origElement.scale[1]) * origElement.scale[1],
|
|
390
491
|
],
|
|
391
492
|
});
|
|
392
493
|
}
|
|
393
|
-
if (isArrowElement(
|
|
494
|
+
if (isArrowElement(latestElement) &&
|
|
394
495
|
boundTextElement &&
|
|
395
496
|
shouldMaintainAspectRatio) {
|
|
396
|
-
const fontSize = (
|
|
497
|
+
const fontSize = (nextWidth / latestElement.width) * boundTextElement.fontSize;
|
|
397
498
|
if (fontSize < MIN_FONT_SIZE) {
|
|
398
499
|
return;
|
|
399
500
|
}
|
|
400
501
|
boundTextFont.fontSize = fontSize;
|
|
401
502
|
}
|
|
402
|
-
if (
|
|
403
|
-
|
|
404
|
-
Number.isFinite(
|
|
405
|
-
Number.isFinite(
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
503
|
+
if (nextWidth !== 0 &&
|
|
504
|
+
nextHeight !== 0 &&
|
|
505
|
+
Number.isFinite(newOrigin.x) &&
|
|
506
|
+
Number.isFinite(newOrigin.y)) {
|
|
507
|
+
const updates = {
|
|
508
|
+
...newOrigin,
|
|
509
|
+
width: Math.abs(nextWidth),
|
|
510
|
+
height: Math.abs(nextHeight),
|
|
511
|
+
...rescaledPoints,
|
|
512
|
+
};
|
|
513
|
+
mutateElement(latestElement, updates, shouldInformMutation);
|
|
514
|
+
updateBoundElements(latestElement, elementsMap, {
|
|
515
|
+
// TODO: confirm with MARK if this actually makes sense
|
|
516
|
+
newSize: { width: nextWidth, height: nextHeight },
|
|
412
517
|
});
|
|
413
518
|
if (boundTextElement && boundTextFont != null) {
|
|
414
519
|
mutateElement(boundTextElement, {
|
|
415
520
|
fontSize: boundTextFont.fontSize,
|
|
416
521
|
});
|
|
417
522
|
}
|
|
418
|
-
handleBindTextResize(
|
|
523
|
+
handleBindTextResize(latestElement, elementsMap, handleDirection, shouldMaintainAspectRatio);
|
|
419
524
|
}
|
|
420
525
|
};
|
|
421
|
-
|
|
422
|
-
//
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
const
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
526
|
+
const getNextSingleWidthAndHeightFromPointer = (latestElement, origElement, elementsMap, originalElementsMap, handleDirection, pointerX, pointerY, { shouldMaintainAspectRatio = false, shouldResizeFromCenter = false, } = {}) => {
|
|
527
|
+
// Gets bounds corners
|
|
528
|
+
const [x1, y1, x2, y2] = getResizedElementAbsoluteCoords(origElement, origElement.width, origElement.height, true);
|
|
529
|
+
const startTopLeft = pointFrom(x1, y1);
|
|
530
|
+
const startBottomRight = pointFrom(x2, y2);
|
|
531
|
+
const startCenter = pointCenter(startTopLeft, startBottomRight);
|
|
532
|
+
// Calculate new dimensions based on cursor position
|
|
533
|
+
const rotatedPointer = pointRotateRads(pointFrom(pointerX, pointerY), startCenter, -origElement.angle);
|
|
534
|
+
// Get bounds corners rendered on screen
|
|
535
|
+
const [esx1, esy1, esx2, esy2] = getResizedElementAbsoluteCoords(latestElement, latestElement.width, latestElement.height, true);
|
|
536
|
+
const boundsCurrentWidth = esx2 - esx1;
|
|
537
|
+
const boundsCurrentHeight = esy2 - esy1;
|
|
538
|
+
// It's important we set the initial scale value based on the width and height at resize start,
|
|
539
|
+
// otherwise previous dimensions affected by modifiers will be taken into account.
|
|
540
|
+
const atStartBoundsWidth = startBottomRight[0] - startTopLeft[0];
|
|
541
|
+
const atStartBoundsHeight = startBottomRight[1] - startTopLeft[1];
|
|
542
|
+
let scaleX = atStartBoundsWidth / boundsCurrentWidth;
|
|
543
|
+
let scaleY = atStartBoundsHeight / boundsCurrentHeight;
|
|
544
|
+
if (handleDirection.includes("e")) {
|
|
545
|
+
scaleX = (rotatedPointer[0] - startTopLeft[0]) / boundsCurrentWidth;
|
|
546
|
+
}
|
|
547
|
+
if (handleDirection.includes("s")) {
|
|
548
|
+
scaleY = (rotatedPointer[1] - startTopLeft[1]) / boundsCurrentHeight;
|
|
549
|
+
}
|
|
550
|
+
if (handleDirection.includes("w")) {
|
|
551
|
+
scaleX = (startBottomRight[0] - rotatedPointer[0]) / boundsCurrentWidth;
|
|
552
|
+
}
|
|
553
|
+
if (handleDirection.includes("n")) {
|
|
554
|
+
scaleY = (startBottomRight[1] - rotatedPointer[1]) / boundsCurrentHeight;
|
|
555
|
+
}
|
|
556
|
+
// We have to use dimensions of element on screen, otherwise the scaling of the
|
|
557
|
+
// dimensions won't match the cursor for linear elements.
|
|
558
|
+
let nextWidth = latestElement.width * scaleX;
|
|
559
|
+
let nextHeight = latestElement.height * scaleY;
|
|
560
|
+
if (shouldResizeFromCenter) {
|
|
561
|
+
nextWidth = 2 * nextWidth - origElement.width;
|
|
562
|
+
nextHeight = 2 * nextHeight - origElement.height;
|
|
563
|
+
}
|
|
564
|
+
// adjust dimensions to keep sides ratio
|
|
565
|
+
if (shouldMaintainAspectRatio) {
|
|
566
|
+
const widthRatio = Math.abs(nextWidth) / origElement.width;
|
|
567
|
+
const heightRatio = Math.abs(nextHeight) / origElement.height;
|
|
568
|
+
if (handleDirection.length === 1) {
|
|
569
|
+
nextHeight *= widthRatio;
|
|
570
|
+
nextWidth *= heightRatio;
|
|
430
571
|
}
|
|
431
|
-
|
|
432
|
-
|
|
572
|
+
if (handleDirection.length === 2) {
|
|
573
|
+
const ratio = Math.max(widthRatio, heightRatio);
|
|
574
|
+
nextWidth = origElement.width * ratio * Math.sign(nextWidth);
|
|
575
|
+
nextHeight = origElement.height * ratio * Math.sign(nextHeight);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
return {
|
|
579
|
+
nextWidth,
|
|
580
|
+
nextHeight,
|
|
581
|
+
};
|
|
582
|
+
};
|
|
583
|
+
const getNextMultipleWidthAndHeightFromPointer = (selectedElements, originalElementsMap, elementsMap, handleDirection, pointerX, pointerY, { shouldMaintainAspectRatio = false, shouldResizeFromCenter = false, } = {}) => {
|
|
584
|
+
const originalElementsArray = selectedElements.map((el) => originalElementsMap.get(el.id));
|
|
433
585
|
// getCommonBoundingBox() uses getBoundTextElement() which returns null for
|
|
434
586
|
// original elements from pointerDownState, so we have to find and add these
|
|
435
587
|
// bound text elements manually. Additionally, the coordinates of bound text
|
|
436
588
|
// elements aren't always up to date.
|
|
437
|
-
const boundTextElements =
|
|
589
|
+
const boundTextElements = originalElementsArray.reduce((acc, orig) => {
|
|
438
590
|
if (!isLinearElement(orig)) {
|
|
439
591
|
return acc;
|
|
440
592
|
}
|
|
@@ -442,50 +594,48 @@ export const resizeMultipleElements = (originalElements, selectedElements, eleme
|
|
|
442
594
|
if (!textId) {
|
|
443
595
|
return acc;
|
|
444
596
|
}
|
|
445
|
-
const text =
|
|
597
|
+
const text = originalElementsMap.get(textId) ?? null;
|
|
446
598
|
if (!isBoundToContainer(text)) {
|
|
447
599
|
return acc;
|
|
448
600
|
}
|
|
449
|
-
|
|
450
|
-
|
|
601
|
+
return [
|
|
602
|
+
...acc,
|
|
603
|
+
{
|
|
604
|
+
...text,
|
|
605
|
+
...LinearElementEditor.getBoundTextElementPosition(orig, text, elementsMap),
|
|
606
|
+
},
|
|
607
|
+
];
|
|
451
608
|
}, []);
|
|
452
|
-
const
|
|
609
|
+
const originalBoundingBox = getCommonBoundingBox(originalElementsArray.map((orig) => orig).concat(boundTextElements));
|
|
610
|
+
const { minX, minY, maxX, maxY, midX, midY } = originalBoundingBox;
|
|
453
611
|
const width = maxX - minX;
|
|
454
612
|
const height = maxY - minY;
|
|
455
|
-
const direction = transformHandleType;
|
|
456
613
|
const anchorsMap = {
|
|
457
|
-
ne:
|
|
458
|
-
se:
|
|
459
|
-
sw:
|
|
460
|
-
nw:
|
|
461
|
-
e:
|
|
462
|
-
w:
|
|
463
|
-
n:
|
|
464
|
-
s:
|
|
614
|
+
ne: [minX, maxY],
|
|
615
|
+
se: [minX, minY],
|
|
616
|
+
sw: [maxX, minY],
|
|
617
|
+
nw: [maxX, maxY],
|
|
618
|
+
e: [minX, minY + height / 2],
|
|
619
|
+
w: [maxX, minY + height / 2],
|
|
620
|
+
n: [minX + width / 2, maxY],
|
|
621
|
+
s: [minX + width / 2, minY],
|
|
465
622
|
};
|
|
466
623
|
// anchor point must be on the opposite side of the dragged selection handle
|
|
467
624
|
// or be the center of the selection if shouldResizeFromCenter
|
|
468
625
|
const [anchorX, anchorY] = shouldResizeFromCenter
|
|
469
|
-
?
|
|
470
|
-
: anchorsMap[
|
|
626
|
+
? [midX, midY]
|
|
627
|
+
: anchorsMap[handleDirection];
|
|
471
628
|
const resizeFromCenterScale = shouldResizeFromCenter ? 2 : 1;
|
|
472
629
|
const scale = Math.max(Math.abs(pointerX - anchorX) / width || 0, Math.abs(pointerY - anchorY) / height || 0) * resizeFromCenterScale;
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
let
|
|
477
|
-
?
|
|
478
|
-
:
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
const keepAspectRatio = shouldMaintainAspectRatio ||
|
|
483
|
-
targetElements.some((item) => item.latest.angle !== 0 ||
|
|
484
|
-
isTextElement(item.latest) ||
|
|
485
|
-
isInGroup(item.latest));
|
|
486
|
-
if (keepAspectRatio) {
|
|
487
|
-
scaleX = scale;
|
|
488
|
-
scaleY = scale;
|
|
630
|
+
let nextWidth = handleDirection.includes("e") || handleDirection.includes("w")
|
|
631
|
+
? Math.abs(pointerX - anchorX) * resizeFromCenterScale
|
|
632
|
+
: width;
|
|
633
|
+
let nextHeight = handleDirection.includes("n") || handleDirection.includes("s")
|
|
634
|
+
? Math.abs(pointerY - anchorY) * resizeFromCenterScale
|
|
635
|
+
: height;
|
|
636
|
+
if (shouldMaintainAspectRatio) {
|
|
637
|
+
nextWidth = width * scale * Math.sign(pointerX - anchorX);
|
|
638
|
+
nextHeight = height * scale * Math.sign(pointerY - anchorY);
|
|
489
639
|
}
|
|
490
640
|
const flipConditionsMap = {
|
|
491
641
|
ne: [pointerX < anchorX, pointerY > anchorY],
|
|
@@ -499,162 +649,244 @@ export const resizeMultipleElements = (originalElements, selectedElements, eleme
|
|
|
499
649
|
n: [false, pointerY > anchorY],
|
|
500
650
|
s: [false, pointerY < anchorY],
|
|
501
651
|
};
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
const
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
const update = {
|
|
531
|
-
x,
|
|
532
|
-
y,
|
|
533
|
-
width,
|
|
534
|
-
height,
|
|
535
|
-
angle,
|
|
536
|
-
...rescaledPoints,
|
|
537
|
-
};
|
|
538
|
-
if (isImageElement(orig)) {
|
|
539
|
-
update.scale = [orig.scale[0] * flipFactorX, orig.scale[1] * flipFactorY];
|
|
652
|
+
const [flipByX, flipByY] = flipConditionsMap[handleDirection].map((condition) => condition);
|
|
653
|
+
return {
|
|
654
|
+
originalBoundingBox,
|
|
655
|
+
nextWidth,
|
|
656
|
+
nextHeight,
|
|
657
|
+
flipByX,
|
|
658
|
+
flipByY,
|
|
659
|
+
};
|
|
660
|
+
};
|
|
661
|
+
export const resizeMultipleElements = (selectedElements, elementsMap, handleDirection, scene, originalElementsMap, { shouldMaintainAspectRatio = false, shouldResizeFromCenter = false, flipByX = false, flipByY = false, nextHeight, nextWidth, originalBoundingBox, } = {}) => {
|
|
662
|
+
// in the case of just flipping, there is no need to specify the next width and height
|
|
663
|
+
if (nextWidth === undefined &&
|
|
664
|
+
nextHeight === undefined &&
|
|
665
|
+
flipByX === undefined &&
|
|
666
|
+
flipByY === undefined) {
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
// do not allow next width or height to be 0
|
|
670
|
+
if (nextHeight === 0 || nextWidth === 0) {
|
|
671
|
+
return;
|
|
672
|
+
}
|
|
673
|
+
if (!originalElementsMap) {
|
|
674
|
+
originalElementsMap = elementsMap;
|
|
675
|
+
}
|
|
676
|
+
const targetElements = selectedElements.reduce((acc, element) => {
|
|
677
|
+
const origElement = originalElementsMap.get(element.id);
|
|
678
|
+
if (origElement) {
|
|
679
|
+
acc.push({ orig: origElement, latest: element });
|
|
540
680
|
}
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
681
|
+
return acc;
|
|
682
|
+
}, []);
|
|
683
|
+
let boundingBox;
|
|
684
|
+
if (originalBoundingBox) {
|
|
685
|
+
boundingBox = originalBoundingBox;
|
|
686
|
+
}
|
|
687
|
+
else {
|
|
688
|
+
const boundTextElements = targetElements.reduce((acc, { orig }) => {
|
|
689
|
+
if (!isLinearElement(orig)) {
|
|
690
|
+
return acc;
|
|
691
|
+
}
|
|
692
|
+
const textId = getBoundTextElementId(orig);
|
|
693
|
+
if (!textId) {
|
|
694
|
+
return acc;
|
|
695
|
+
}
|
|
696
|
+
const text = originalElementsMap.get(textId) ?? null;
|
|
697
|
+
if (!isBoundToContainer(text)) {
|
|
698
|
+
return acc;
|
|
545
699
|
}
|
|
546
|
-
|
|
700
|
+
return [
|
|
701
|
+
...acc,
|
|
702
|
+
{
|
|
703
|
+
...text,
|
|
704
|
+
...LinearElementEditor.getBoundTextElementPosition(orig, text, elementsMap),
|
|
705
|
+
},
|
|
706
|
+
];
|
|
707
|
+
}, []);
|
|
708
|
+
boundingBox = getCommonBoundingBox(targetElements.map(({ orig }) => orig).concat(boundTextElements));
|
|
709
|
+
}
|
|
710
|
+
const { minX, minY, maxX, maxY, midX, midY } = boundingBox;
|
|
711
|
+
const width = maxX - minX;
|
|
712
|
+
const height = maxY - minY;
|
|
713
|
+
if (nextWidth === undefined && nextHeight === undefined) {
|
|
714
|
+
nextWidth = width;
|
|
715
|
+
nextHeight = height;
|
|
716
|
+
}
|
|
717
|
+
if (shouldMaintainAspectRatio) {
|
|
718
|
+
if (nextWidth === undefined) {
|
|
719
|
+
nextWidth = nextHeight * (width / height);
|
|
720
|
+
}
|
|
721
|
+
else if (nextHeight === undefined) {
|
|
722
|
+
nextHeight = nextWidth * (height / width);
|
|
723
|
+
}
|
|
724
|
+
else if (Math.abs(nextWidth / nextHeight - width / height) > 0.001) {
|
|
725
|
+
nextWidth = nextHeight * (width / height);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
if (nextWidth && nextHeight) {
|
|
729
|
+
let scaleX = handleDirection.includes("e") || handleDirection.includes("w")
|
|
730
|
+
? Math.abs(nextWidth) / width
|
|
731
|
+
: 1;
|
|
732
|
+
let scaleY = handleDirection.includes("n") || handleDirection.includes("s")
|
|
733
|
+
? Math.abs(nextHeight) / height
|
|
734
|
+
: 1;
|
|
735
|
+
let scale;
|
|
736
|
+
if (handleDirection.length === 1) {
|
|
737
|
+
scale =
|
|
738
|
+
handleDirection.includes("e") || handleDirection.includes("w")
|
|
739
|
+
? scaleX
|
|
740
|
+
: scaleY;
|
|
547
741
|
}
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
742
|
+
else {
|
|
743
|
+
scale = Math.max(Math.abs(nextWidth) / width || 0, Math.abs(nextHeight) / height || 0);
|
|
744
|
+
}
|
|
745
|
+
const anchorsMap = {
|
|
746
|
+
ne: [minX, maxY],
|
|
747
|
+
se: [minX, minY],
|
|
748
|
+
sw: [maxX, minY],
|
|
749
|
+
nw: [maxX, maxY],
|
|
750
|
+
e: [minX, minY + height / 2],
|
|
751
|
+
w: [maxX, minY + height / 2],
|
|
752
|
+
n: [minX + width / 2, maxY],
|
|
753
|
+
s: [minX + width / 2, minY],
|
|
754
|
+
};
|
|
755
|
+
// anchor point must be on the opposite side of the dragged selection handle
|
|
756
|
+
// or be the center of the selection if shouldResizeFromCenter
|
|
757
|
+
const [anchorX, anchorY] = shouldResizeFromCenter
|
|
758
|
+
? [midX, midY]
|
|
759
|
+
: anchorsMap[handleDirection];
|
|
760
|
+
const keepAspectRatio = shouldMaintainAspectRatio ||
|
|
761
|
+
targetElements.some((item) => item.latest.angle !== 0 ||
|
|
762
|
+
isTextElement(item.latest) ||
|
|
763
|
+
isInGroup(item.latest));
|
|
764
|
+
if (keepAspectRatio) {
|
|
765
|
+
scaleX = scale;
|
|
766
|
+
scaleY = scale;
|
|
767
|
+
}
|
|
768
|
+
/**
|
|
769
|
+
* to flip an element:
|
|
770
|
+
* 1. determine over which axis is the element being flipped
|
|
771
|
+
* (could be x, y, or both) indicated by `flipFactorX` & `flipFactorY`
|
|
772
|
+
* 2. shift element's position by the amount of width or height (or both) or
|
|
773
|
+
* mirror points in the case of linear & freedraw elemenets
|
|
774
|
+
* 3. adjust element angle
|
|
775
|
+
*/
|
|
776
|
+
const [flipFactorX, flipFactorY] = [flipByX ? -1 : 1, flipByY ? -1 : 1];
|
|
777
|
+
const elementsAndUpdates = [];
|
|
778
|
+
for (const { orig, latest } of targetElements) {
|
|
779
|
+
// bounded text elements are updated along with their container elements
|
|
780
|
+
if (isTextElement(orig) && isBoundToContainer(orig)) {
|
|
781
|
+
continue;
|
|
782
|
+
}
|
|
783
|
+
const width = orig.width * scaleX;
|
|
784
|
+
const height = orig.height * scaleY;
|
|
785
|
+
const angle = normalizeRadians((orig.angle * flipFactorX * flipFactorY));
|
|
786
|
+
const isLinearOrFreeDraw = isLinearElement(orig) || isFreeDrawElement(orig);
|
|
787
|
+
const offsetX = orig.x - anchorX;
|
|
788
|
+
const offsetY = orig.y - anchorY;
|
|
789
|
+
const shiftX = flipByX && !isLinearOrFreeDraw ? width : 0;
|
|
790
|
+
const shiftY = flipByY && !isLinearOrFreeDraw ? height : 0;
|
|
791
|
+
const x = anchorX + flipFactorX * (offsetX * scaleX + shiftX);
|
|
792
|
+
const y = anchorY + flipFactorY * (offsetY * scaleY + shiftY);
|
|
793
|
+
const rescaledPoints = rescalePointsInElement(orig, width * flipFactorX, height * flipFactorY, false);
|
|
794
|
+
const update = {
|
|
795
|
+
x,
|
|
796
|
+
y,
|
|
797
|
+
width,
|
|
798
|
+
height,
|
|
799
|
+
angle,
|
|
800
|
+
...rescaledPoints,
|
|
801
|
+
};
|
|
802
|
+
if (isElbowArrow(orig)) {
|
|
803
|
+
// Mirror fixed point binding for elbow arrows
|
|
804
|
+
// when resize goes into the negative direction
|
|
805
|
+
if (orig.startBinding) {
|
|
806
|
+
update.startBinding = {
|
|
807
|
+
...orig.startBinding,
|
|
808
|
+
fixedPoint: [
|
|
809
|
+
flipByX
|
|
810
|
+
? -orig.startBinding.fixedPoint[0] + 1
|
|
811
|
+
: orig.startBinding.fixedPoint[0],
|
|
812
|
+
flipByY
|
|
813
|
+
? -orig.startBinding.fixedPoint[1] + 1
|
|
814
|
+
: orig.startBinding.fixedPoint[1],
|
|
815
|
+
],
|
|
816
|
+
};
|
|
817
|
+
}
|
|
818
|
+
if (orig.endBinding) {
|
|
819
|
+
update.endBinding = {
|
|
820
|
+
...orig.endBinding,
|
|
821
|
+
fixedPoint: [
|
|
822
|
+
flipByX
|
|
823
|
+
? -orig.endBinding.fixedPoint[0] + 1
|
|
824
|
+
: orig.endBinding.fixedPoint[0],
|
|
825
|
+
flipByY
|
|
826
|
+
? -orig.endBinding.fixedPoint[1] + 1
|
|
827
|
+
: orig.endBinding.fixedPoint[1],
|
|
828
|
+
],
|
|
829
|
+
};
|
|
830
|
+
}
|
|
831
|
+
if (orig.fixedSegments && rescaledPoints.points) {
|
|
832
|
+
update.fixedSegments = orig.fixedSegments.map((segment) => ({
|
|
833
|
+
...segment,
|
|
834
|
+
start: rescaledPoints.points[segment.index - 1],
|
|
835
|
+
end: rescaledPoints.points[segment.index],
|
|
836
|
+
}));
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
if (isImageElement(orig)) {
|
|
840
|
+
update.scale = [
|
|
841
|
+
orig.scale[0] * flipFactorX,
|
|
842
|
+
orig.scale[1] * flipFactorY,
|
|
843
|
+
];
|
|
844
|
+
}
|
|
845
|
+
if (isTextElement(orig)) {
|
|
846
|
+
const metrics = measureFontSizeFromWidth(orig, elementsMap, width);
|
|
847
|
+
if (!metrics) {
|
|
553
848
|
return;
|
|
554
849
|
}
|
|
555
|
-
update.
|
|
850
|
+
update.fontSize = metrics.size;
|
|
556
851
|
}
|
|
557
|
-
|
|
558
|
-
|
|
852
|
+
const boundTextElement = originalElementsMap.get(getBoundTextElementId(orig) ?? "");
|
|
853
|
+
if (boundTextElement) {
|
|
854
|
+
if (keepAspectRatio) {
|
|
855
|
+
const newFontSize = boundTextElement.fontSize * scale;
|
|
856
|
+
if (newFontSize < MIN_FONT_SIZE) {
|
|
857
|
+
return;
|
|
858
|
+
}
|
|
859
|
+
update.boundTextFontSize = newFontSize;
|
|
860
|
+
}
|
|
861
|
+
else {
|
|
862
|
+
update.boundTextFontSize = boundTextElement.fontSize;
|
|
863
|
+
}
|
|
559
864
|
}
|
|
865
|
+
elementsAndUpdates.push({
|
|
866
|
+
element: latest,
|
|
867
|
+
update,
|
|
868
|
+
});
|
|
560
869
|
}
|
|
561
|
-
elementsAndUpdates.
|
|
562
|
-
|
|
563
|
-
update
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
}
|
|
580
|
-
handleBindTextResize(element, elementsMap, transformHandleType, true);
|
|
870
|
+
const elementsToUpdate = elementsAndUpdates.map(({ element }) => element);
|
|
871
|
+
for (const { element, update: { boundTextFontSize, ...update }, } of elementsAndUpdates) {
|
|
872
|
+
const { width, height, angle } = update;
|
|
873
|
+
mutateElement(element, update, false, {
|
|
874
|
+
// needed for the fixed binding point udpate to take effect
|
|
875
|
+
isDragging: true,
|
|
876
|
+
});
|
|
877
|
+
updateBoundElements(element, elementsMap, {
|
|
878
|
+
simultaneouslyUpdated: elementsToUpdate,
|
|
879
|
+
newSize: { width, height },
|
|
880
|
+
});
|
|
881
|
+
const boundTextElement = getBoundTextElement(element, elementsMap);
|
|
882
|
+
if (boundTextElement && boundTextFontSize) {
|
|
883
|
+
mutateElement(boundTextElement, {
|
|
884
|
+
fontSize: boundTextFontSize,
|
|
885
|
+
angle: isLinearElement(element) ? undefined : angle,
|
|
886
|
+
}, false);
|
|
887
|
+
handleBindTextResize(element, elementsMap, handleDirection, true);
|
|
888
|
+
}
|
|
581
889
|
}
|
|
890
|
+
scene.triggerUpdate();
|
|
582
891
|
}
|
|
583
|
-
Scene.getScene(elementsAndUpdates[0].element)?.triggerUpdate();
|
|
584
|
-
};
|
|
585
|
-
const rotateMultipleElements = (originalElements, elements, elementsMap, pointerX, pointerY, shouldRotateWithDiscreteAngle, centerX, centerY) => {
|
|
586
|
-
let centerAngle = (5 * Math.PI) / 2 + Math.atan2(pointerY - centerY, pointerX - centerX);
|
|
587
|
-
if (shouldRotateWithDiscreteAngle) {
|
|
588
|
-
centerAngle += SHIFT_LOCKING_ANGLE / 2;
|
|
589
|
-
centerAngle -= centerAngle % SHIFT_LOCKING_ANGLE;
|
|
590
|
-
}
|
|
591
|
-
elements
|
|
592
|
-
.filter((element) => !isFrameLikeElement(element))
|
|
593
|
-
.forEach((element) => {
|
|
594
|
-
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element, elementsMap);
|
|
595
|
-
const cx = (x1 + x2) / 2;
|
|
596
|
-
const cy = (y1 + y2) / 2;
|
|
597
|
-
const origAngle = originalElements.get(element.id)?.angle ?? element.angle;
|
|
598
|
-
const [rotatedCX, rotatedCY] = pointRotateRads(pointFrom(cx, cy), pointFrom(centerX, centerY), (centerAngle + origAngle - element.angle));
|
|
599
|
-
if (isElbowArrow(element)) {
|
|
600
|
-
const points = getArrowLocalFixedPoints(element, elementsMap);
|
|
601
|
-
mutateElbowArrow(element, elementsMap, points);
|
|
602
|
-
}
|
|
603
|
-
else {
|
|
604
|
-
mutateElement(element, {
|
|
605
|
-
x: element.x + (rotatedCX - cx),
|
|
606
|
-
y: element.y + (rotatedCY - cy),
|
|
607
|
-
angle: normalizeRadians((centerAngle + origAngle)),
|
|
608
|
-
}, false);
|
|
609
|
-
}
|
|
610
|
-
updateBoundElements(element, elementsMap, {
|
|
611
|
-
simultaneouslyUpdated: elements,
|
|
612
|
-
});
|
|
613
|
-
const boundText = getBoundTextElement(element, elementsMap);
|
|
614
|
-
if (boundText && !isArrowElement(element)) {
|
|
615
|
-
mutateElement(boundText, {
|
|
616
|
-
x: boundText.x + (rotatedCX - cx),
|
|
617
|
-
y: boundText.y + (rotatedCY - cy),
|
|
618
|
-
angle: normalizeRadians((centerAngle + origAngle)),
|
|
619
|
-
}, false);
|
|
620
|
-
}
|
|
621
|
-
});
|
|
622
|
-
Scene.getScene(elements[0])?.triggerUpdate();
|
|
623
|
-
};
|
|
624
|
-
export const getResizeOffsetXY = (transformHandleType, selectedElements, elementsMap, x, y) => {
|
|
625
|
-
const [x1, y1, x2, y2] = selectedElements.length === 1
|
|
626
|
-
? getElementAbsoluteCoords(selectedElements[0], elementsMap)
|
|
627
|
-
: getCommonBounds(selectedElements);
|
|
628
|
-
const cx = (x1 + x2) / 2;
|
|
629
|
-
const cy = (y1 + y2) / 2;
|
|
630
|
-
const angle = (selectedElements.length === 1 ? selectedElements[0].angle : 0);
|
|
631
|
-
[x, y] = pointRotateRads(pointFrom(x, y), pointFrom(cx, cy), -angle);
|
|
632
|
-
switch (transformHandleType) {
|
|
633
|
-
case "n":
|
|
634
|
-
return pointRotateRads(pointFrom(x - (x1 + x2) / 2, y - y1), pointFrom(0, 0), angle);
|
|
635
|
-
case "s":
|
|
636
|
-
return pointRotateRads(pointFrom(x - (x1 + x2) / 2, y - y2), pointFrom(0, 0), angle);
|
|
637
|
-
case "w":
|
|
638
|
-
return pointRotateRads(pointFrom(x - x1, y - (y1 + y2) / 2), pointFrom(0, 0), angle);
|
|
639
|
-
case "e":
|
|
640
|
-
return pointRotateRads(pointFrom(x - x2, y - (y1 + y2) / 2), pointFrom(0, 0), angle);
|
|
641
|
-
case "nw":
|
|
642
|
-
return pointRotateRads(pointFrom(x - x1, y - y1), pointFrom(0, 0), angle);
|
|
643
|
-
case "ne":
|
|
644
|
-
return pointRotateRads(pointFrom(x - x2, y - y1), pointFrom(0, 0), angle);
|
|
645
|
-
case "sw":
|
|
646
|
-
return pointRotateRads(pointFrom(x - x1, y - y2), pointFrom(0, 0), angle);
|
|
647
|
-
case "se":
|
|
648
|
-
return pointRotateRads(pointFrom(x - x2, y - y2), pointFrom(0, 0), angle);
|
|
649
|
-
default:
|
|
650
|
-
return [0, 0];
|
|
651
|
-
}
|
|
652
|
-
};
|
|
653
|
-
export const getResizeArrowDirection = (transformHandleType, element) => {
|
|
654
|
-
const [, [px, py]] = element.points;
|
|
655
|
-
const isResizeEnd = (transformHandleType === "nw" && (px < 0 || py < 0)) ||
|
|
656
|
-
(transformHandleType === "ne" && px >= 0) ||
|
|
657
|
-
(transformHandleType === "sw" && px <= 0) ||
|
|
658
|
-
(transformHandleType === "se" && (px > 0 || py > 0));
|
|
659
|
-
return isResizeEnd ? "end" : "origin";
|
|
660
892
|
};
|