|
@@ -6,7 +6,6 @@ import {
|
|
ExcalidrawLinearElement,
|
|
ExcalidrawLinearElement,
|
|
NonDeletedExcalidrawElement,
|
|
NonDeletedExcalidrawElement,
|
|
NonDeleted,
|
|
NonDeleted,
|
|
- ResizeArrowFnType,
|
|
|
|
} from "./types";
|
|
} from "./types";
|
|
import {
|
|
import {
|
|
getElementAbsoluteCoords,
|
|
getElementAbsoluteCoords,
|
|
@@ -32,15 +31,10 @@ export const resizeElements = (
|
|
resizeHandle: ResizeTestType,
|
|
resizeHandle: ResizeTestType,
|
|
setResizeHandle: (nextResizeHandle: ResizeTestType) => void,
|
|
setResizeHandle: (nextResizeHandle: ResizeTestType) => void,
|
|
selectedElements: NonDeletedExcalidrawElement[],
|
|
selectedElements: NonDeletedExcalidrawElement[],
|
|
- resizeArrowFn: ResizeArrowFnType | null, // XXX eliminate in #1339
|
|
|
|
- setResizeArrowFn: (fn: ResizeArrowFnType) => void, // XXX eliminate in #1339
|
|
|
|
|
|
+ resizeArrowDirection: "origin" | "end",
|
|
event: PointerEvent, // XXX we want to make it independent?
|
|
event: PointerEvent, // XXX we want to make it independent?
|
|
pointerX: number,
|
|
pointerX: number,
|
|
pointerY: number,
|
|
pointerY: number,
|
|
- offsetX: number,
|
|
|
|
- offsetY: number,
|
|
|
|
- lastX: number, // XXX eliminate in #1339
|
|
|
|
- lastY: number, // XXX eliminate in #1339
|
|
|
|
) => {
|
|
) => {
|
|
if (selectedElements.length === 1) {
|
|
if (selectedElements.length === 1) {
|
|
const [element] = selectedElements;
|
|
const [element] = selectedElements;
|
|
@@ -56,14 +50,10 @@ export const resizeElements = (
|
|
) {
|
|
) {
|
|
resizeSingleTwoPointElement(
|
|
resizeSingleTwoPointElement(
|
|
element,
|
|
element,
|
|
- resizeHandle,
|
|
|
|
- resizeArrowFn,
|
|
|
|
- setResizeArrowFn,
|
|
|
|
|
|
+ resizeArrowDirection,
|
|
event.shiftKey,
|
|
event.shiftKey,
|
|
pointerX,
|
|
pointerX,
|
|
pointerY,
|
|
pointerY,
|
|
- lastX,
|
|
|
|
- lastY,
|
|
|
|
);
|
|
);
|
|
} else if (resizeHandle) {
|
|
} else if (resizeHandle) {
|
|
resizeSingleElement(
|
|
resizeSingleElement(
|
|
@@ -73,8 +63,6 @@ export const resizeElements = (
|
|
getResizeCenterPointKey(event),
|
|
getResizeCenterPointKey(event),
|
|
pointerX,
|
|
pointerX,
|
|
pointerY,
|
|
pointerY,
|
|
- offsetX,
|
|
|
|
- offsetY,
|
|
|
|
);
|
|
);
|
|
setResizeHandle(normalizeResizeHandle(element, resizeHandle));
|
|
setResizeHandle(normalizeResizeHandle(element, resizeHandle));
|
|
if (element.width < 0) {
|
|
if (element.width < 0) {
|
|
@@ -100,14 +88,7 @@ export const resizeElements = (
|
|
resizeHandle === "sw" ||
|
|
resizeHandle === "sw" ||
|
|
resizeHandle === "se")
|
|
resizeHandle === "se")
|
|
) {
|
|
) {
|
|
- resizeMultipleElements(
|
|
|
|
- selectedElements,
|
|
|
|
- resizeHandle,
|
|
|
|
- pointerX,
|
|
|
|
- pointerY,
|
|
|
|
- offsetX,
|
|
|
|
- offsetY,
|
|
|
|
- );
|
|
|
|
|
|
+ resizeMultipleElements(selectedElements, resizeHandle, pointerX, pointerY);
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
return false;
|
|
@@ -135,122 +116,61 @@ const rotateSingleElement = (
|
|
|
|
|
|
const resizeSingleTwoPointElement = (
|
|
const resizeSingleTwoPointElement = (
|
|
element: NonDeleted<ExcalidrawLinearElement>,
|
|
element: NonDeleted<ExcalidrawLinearElement>,
|
|
- resizeHandle: "nw" | "ne" | "sw" | "se",
|
|
|
|
- resizeArrowFn: ResizeArrowFnType | null,
|
|
|
|
- setResizeArrowFn: (fn: ResizeArrowFnType) => void,
|
|
|
|
- sidesWithSameLength: boolean,
|
|
|
|
|
|
+ resizeArrowDirection: "origin" | "end",
|
|
|
|
+ isAngleLocking: boolean,
|
|
pointerX: number,
|
|
pointerX: number,
|
|
pointerY: number,
|
|
pointerY: number,
|
|
- lastX: number,
|
|
|
|
- lastY: number,
|
|
|
|
) => {
|
|
) => {
|
|
- const [, [px, py]] = element.points;
|
|
|
|
- const isResizeEnd =
|
|
|
|
- (resizeHandle === "nw" && (px < 0 || py < 0)) ||
|
|
|
|
- (resizeHandle === "ne" && px >= 0) ||
|
|
|
|
- (resizeHandle === "sw" && px <= 0) ||
|
|
|
|
- (resizeHandle === "se" && (px > 0 || py > 0));
|
|
|
|
- applyResizeArrowFn(
|
|
|
|
- element,
|
|
|
|
- resizeArrowFn,
|
|
|
|
- setResizeArrowFn,
|
|
|
|
- isResizeEnd,
|
|
|
|
- sidesWithSameLength,
|
|
|
|
- pointerX,
|
|
|
|
- pointerY,
|
|
|
|
- lastX,
|
|
|
|
- lastY,
|
|
|
|
- );
|
|
|
|
-};
|
|
|
|
-
|
|
|
|
-const arrowResizeOrigin: ResizeArrowFnType = (
|
|
|
|
- element,
|
|
|
|
- pointIndex,
|
|
|
|
- deltaX,
|
|
|
|
- deltaY,
|
|
|
|
- pointerX,
|
|
|
|
- pointerY,
|
|
|
|
- sidesWithSameLength,
|
|
|
|
-) => {
|
|
|
|
- const [px, py] = element.points[pointIndex];
|
|
|
|
- let x = element.x + deltaX;
|
|
|
|
- let y = element.y + deltaY;
|
|
|
|
- let pointX = px - deltaX;
|
|
|
|
- let pointY = py - deltaY;
|
|
|
|
-
|
|
|
|
- if (sidesWithSameLength) {
|
|
|
|
- const { width, height } = getPerfectElementSize(
|
|
|
|
- element.type,
|
|
|
|
- px + element.x - pointerX,
|
|
|
|
- py + element.y - pointerY,
|
|
|
|
- );
|
|
|
|
- x = px + element.x - width;
|
|
|
|
- y = py + element.y - height;
|
|
|
|
- pointX = width;
|
|
|
|
- pointY = height;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- mutateElement(element, {
|
|
|
|
- x,
|
|
|
|
- y,
|
|
|
|
- points: element.points.map((point, i) =>
|
|
|
|
- i === pointIndex ? ([pointX, pointY] as const) : point,
|
|
|
|
- ),
|
|
|
|
- });
|
|
|
|
-};
|
|
|
|
-
|
|
|
|
-const arrowResizeEnd: ResizeArrowFnType = (
|
|
|
|
- element,
|
|
|
|
- pointIndex,
|
|
|
|
- deltaX,
|
|
|
|
- deltaY,
|
|
|
|
- pointerX,
|
|
|
|
- pointerY,
|
|
|
|
- sidesWithSameLength,
|
|
|
|
-) => {
|
|
|
|
- const [px, py] = element.points[pointIndex];
|
|
|
|
- if (sidesWithSameLength) {
|
|
|
|
- const { width, height } = getPerfectElementSize(
|
|
|
|
- element.type,
|
|
|
|
- pointerX - element.x,
|
|
|
|
- pointerY - element.y,
|
|
|
|
- );
|
|
|
|
- mutateElement(element, {
|
|
|
|
- points: element.points.map((point, i) =>
|
|
|
|
- i === pointIndex ? ([width, height] as const) : point,
|
|
|
|
- ),
|
|
|
|
- });
|
|
|
|
|
|
+ const pointOrigin = element.points[0]; // can assume always [0, 0]?
|
|
|
|
+ const pointEnd = element.points[1];
|
|
|
|
+ if (resizeArrowDirection === "end") {
|
|
|
|
+ if (isAngleLocking) {
|
|
|
|
+ const { width, height } = getPerfectElementSize(
|
|
|
|
+ element.type,
|
|
|
|
+ pointerX - element.x,
|
|
|
|
+ pointerY - element.y,
|
|
|
|
+ );
|
|
|
|
+ mutateElement(element, {
|
|
|
|
+ points: [pointOrigin, [width, height]],
|
|
|
|
+ });
|
|
|
|
+ } else {
|
|
|
|
+ mutateElement(element, {
|
|
|
|
+ points: [
|
|
|
|
+ pointOrigin,
|
|
|
|
+ [
|
|
|
|
+ pointerX - pointOrigin[0] - element.x,
|
|
|
|
+ pointerY - pointOrigin[1] - element.y,
|
|
|
|
+ ],
|
|
|
|
+ ],
|
|
|
|
+ });
|
|
|
|
+ }
|
|
} else {
|
|
} else {
|
|
- mutateElement(element, {
|
|
|
|
- points: element.points.map((point, i) =>
|
|
|
|
- i === pointIndex ? ([px + deltaX, py + deltaY] as const) : point,
|
|
|
|
- ),
|
|
|
|
- });
|
|
|
|
- }
|
|
|
|
-};
|
|
|
|
-
|
|
|
|
-const applyResizeArrowFn = (
|
|
|
|
- element: NonDeleted<ExcalidrawLinearElement>,
|
|
|
|
- resizeArrowFn: ResizeArrowFnType | null,
|
|
|
|
- setResizeArrowFn: (fn: ResizeArrowFnType) => void,
|
|
|
|
- isResizeEnd: boolean,
|
|
|
|
- sidesWithSameLength: boolean,
|
|
|
|
- x: number,
|
|
|
|
- y: number,
|
|
|
|
- lastX: number,
|
|
|
|
- lastY: number,
|
|
|
|
-) => {
|
|
|
|
- const angle = element.angle;
|
|
|
|
- const [deltaX, deltaY] = rotate(x - lastX, y - lastY, 0, 0, -angle);
|
|
|
|
- if (!resizeArrowFn) {
|
|
|
|
- if (isResizeEnd) {
|
|
|
|
- resizeArrowFn = arrowResizeEnd;
|
|
|
|
|
|
+ // resizeArrowDirection === "origin"
|
|
|
|
+ if (isAngleLocking) {
|
|
|
|
+ const { width, height } = getPerfectElementSize(
|
|
|
|
+ element.type,
|
|
|
|
+ element.x + pointEnd[0] - pointOrigin[0] - pointerX,
|
|
|
|
+ element.y + pointEnd[1] - pointOrigin[1] - pointerY,
|
|
|
|
+ );
|
|
|
|
+ mutateElement(element, {
|
|
|
|
+ x: element.x + pointEnd[0] - pointOrigin[0] - width,
|
|
|
|
+ y: element.y + pointEnd[1] - pointOrigin[1] - height,
|
|
|
|
+ points: [pointOrigin, [width, height]],
|
|
|
|
+ });
|
|
} else {
|
|
} else {
|
|
- resizeArrowFn = arrowResizeOrigin;
|
|
|
|
|
|
+ mutateElement(element, {
|
|
|
|
+ x: pointerX,
|
|
|
|
+ y: pointerY,
|
|
|
|
+ points: [
|
|
|
|
+ pointOrigin,
|
|
|
|
+ [
|
|
|
|
+ pointEnd[0] - (pointerX - pointOrigin[0] - element.x),
|
|
|
|
+ pointEnd[1] - (pointerY - pointOrigin[1] - element.y),
|
|
|
|
+ ],
|
|
|
|
+ ],
|
|
|
|
+ });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- resizeArrowFn(element, 1, deltaX, deltaY, x, y, sidesWithSameLength);
|
|
|
|
- setResizeArrowFn(resizeArrowFn);
|
|
|
|
};
|
|
};
|
|
|
|
|
|
const resizeSingleElement = (
|
|
const resizeSingleElement = (
|
|
@@ -260,16 +180,14 @@ const resizeSingleElement = (
|
|
isResizeFromCenter: boolean,
|
|
isResizeFromCenter: boolean,
|
|
pointerX: number,
|
|
pointerX: number,
|
|
pointerY: number,
|
|
pointerY: number,
|
|
- offsetX: number,
|
|
|
|
- offsetY: number,
|
|
|
|
) => {
|
|
) => {
|
|
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element);
|
|
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element);
|
|
const cx = (x1 + x2) / 2;
|
|
const cx = (x1 + x2) / 2;
|
|
const cy = (y1 + y2) / 2;
|
|
const cy = (y1 + y2) / 2;
|
|
// rotation pointer with reverse angle
|
|
// rotation pointer with reverse angle
|
|
const [rotatedX, rotatedY] = rotate(
|
|
const [rotatedX, rotatedY] = rotate(
|
|
- pointerX - offsetX,
|
|
|
|
- pointerY - offsetY,
|
|
|
|
|
|
+ pointerX,
|
|
|
|
+ pointerY,
|
|
cx,
|
|
cx,
|
|
cy,
|
|
cy,
|
|
-element.angle,
|
|
-element.angle,
|
|
@@ -366,15 +284,13 @@ const resizeMultipleElements = (
|
|
resizeHandle: "nw" | "ne" | "sw" | "se",
|
|
resizeHandle: "nw" | "ne" | "sw" | "se",
|
|
pointerX: number,
|
|
pointerX: number,
|
|
pointerY: number,
|
|
pointerY: number,
|
|
- offsetX: number,
|
|
|
|
- offsetY: number,
|
|
|
|
) => {
|
|
) => {
|
|
const [x1, y1, x2, y2] = getCommonBounds(elements);
|
|
const [x1, y1, x2, y2] = getCommonBounds(elements);
|
|
switch (resizeHandle) {
|
|
switch (resizeHandle) {
|
|
case "se": {
|
|
case "se": {
|
|
const scale = Math.max(
|
|
const scale = Math.max(
|
|
- (pointerX - offsetX - x1) / (x2 - x1),
|
|
|
|
- (pointerY - offsetY - y1) / (y2 - y1),
|
|
|
|
|
|
+ (pointerX - x1) / (x2 - x1),
|
|
|
|
+ (pointerY - y1) / (y2 - y1),
|
|
);
|
|
);
|
|
if (scale > 0) {
|
|
if (scale > 0) {
|
|
elements.forEach((element) => {
|
|
elements.forEach((element) => {
|
|
@@ -389,8 +305,8 @@ const resizeMultipleElements = (
|
|
}
|
|
}
|
|
case "nw": {
|
|
case "nw": {
|
|
const scale = Math.max(
|
|
const scale = Math.max(
|
|
- (x2 - (pointerX - offsetX)) / (x2 - x1),
|
|
|
|
- (y2 - (pointerY - offsetY)) / (y2 - y1),
|
|
|
|
|
|
+ (x2 - pointerX) / (x2 - x1),
|
|
|
|
+ (y2 - pointerY) / (y2 - y1),
|
|
);
|
|
);
|
|
if (scale > 0) {
|
|
if (scale > 0) {
|
|
elements.forEach((element) => {
|
|
elements.forEach((element) => {
|
|
@@ -405,8 +321,8 @@ const resizeMultipleElements = (
|
|
}
|
|
}
|
|
case "ne": {
|
|
case "ne": {
|
|
const scale = Math.max(
|
|
const scale = Math.max(
|
|
- (pointerX - offsetX - x1) / (x2 - x1),
|
|
|
|
- (y2 - (pointerY - offsetY)) / (y2 - y1),
|
|
|
|
|
|
+ (pointerX - x1) / (x2 - x1),
|
|
|
|
+ (y2 - pointerY) / (y2 - y1),
|
|
);
|
|
);
|
|
if (scale > 0) {
|
|
if (scale > 0) {
|
|
elements.forEach((element) => {
|
|
elements.forEach((element) => {
|
|
@@ -421,8 +337,8 @@ const resizeMultipleElements = (
|
|
}
|
|
}
|
|
case "sw": {
|
|
case "sw": {
|
|
const scale = Math.max(
|
|
const scale = Math.max(
|
|
- (x2 - (pointerX - offsetX)) / (x2 - x1),
|
|
|
|
- (pointerY - offsetY - y1) / (y2 - y1),
|
|
|
|
|
|
+ (x2 - pointerX) / (x2 - x1),
|
|
|
|
+ (pointerY - y1) / (y2 - y1),
|
|
);
|
|
);
|
|
if (scale > 0) {
|
|
if (scale > 0) {
|
|
elements.forEach((element) => {
|
|
elements.forEach((element) => {
|
|
@@ -481,3 +397,16 @@ export const getResizeOffsetXY = (
|
|
return [0, 0];
|
|
return [0, 0];
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
+
|
|
|
|
+export const getResizeArrowDirection = (
|
|
|
|
+ resizeHandle: ResizeTestType,
|
|
|
|
+ element: NonDeleted<ExcalidrawLinearElement>,
|
|
|
|
+): "origin" | "end" => {
|
|
|
|
+ const [, [px, py]] = element.points;
|
|
|
|
+ const isResizeEnd =
|
|
|
|
+ (resizeHandle === "nw" && (px < 0 || py < 0)) ||
|
|
|
|
+ (resizeHandle === "ne" && px >= 0) ||
|
|
|
|
+ (resizeHandle === "sw" && px <= 0) ||
|
|
|
|
+ (resizeHandle === "se" && (px > 0 || py > 0));
|
|
|
|
+ return isResizeEnd ? "end" : "origin";
|
|
|
|
+};
|