2020-04-08 09:49:52 -07:00
|
|
|
import {
|
|
|
|
ExcalidrawElement,
|
|
|
|
PointerType,
|
|
|
|
NonDeletedExcalidrawElement,
|
|
|
|
} from "./types";
|
2020-01-06 19:34:22 +04:00
|
|
|
|
2020-04-07 17:49:59 +09:00
|
|
|
import {
|
|
|
|
OMIT_SIDES_FOR_MULTIPLE_ELEMENTS,
|
2020-08-10 14:16:39 +02:00
|
|
|
getTransformHandlesFromCoords,
|
|
|
|
getTransformHandles,
|
|
|
|
TransformHandleType,
|
|
|
|
TransformHandle,
|
|
|
|
MaybeTransformHandleType,
|
|
|
|
} from "./transformHandles";
|
2020-03-08 10:20:55 -07:00
|
|
|
import { AppState } from "../types";
|
2020-01-06 19:34:22 +04:00
|
|
|
|
2020-08-10 14:16:39 +02:00
|
|
|
const isInsideTransformHandle = (
|
|
|
|
transformHandle: TransformHandle,
|
2020-04-02 17:40:26 +09:00
|
|
|
x: number,
|
|
|
|
y: number,
|
2020-05-20 16:21:37 +03:00
|
|
|
) =>
|
2020-08-10 14:16:39 +02:00
|
|
|
x >= transformHandle[0] &&
|
|
|
|
x <= transformHandle[0] + transformHandle[2] &&
|
|
|
|
y >= transformHandle[1] &&
|
|
|
|
y <= transformHandle[1] + transformHandle[3];
|
2020-04-02 17:40:26 +09:00
|
|
|
|
2020-05-20 16:21:37 +03:00
|
|
|
export const resizeTest = (
|
2020-04-08 09:49:52 -07:00
|
|
|
element: NonDeletedExcalidrawElement,
|
2020-03-08 10:20:55 -07:00
|
|
|
appState: AppState,
|
2020-01-06 19:34:22 +04:00
|
|
|
x: number,
|
|
|
|
y: number,
|
2020-02-15 21:03:32 +01:00
|
|
|
zoom: number,
|
2020-02-21 14:34:18 -05:00
|
|
|
pointerType: PointerType,
|
2020-08-10 14:16:39 +02:00
|
|
|
): MaybeTransformHandleType => {
|
2020-04-02 17:40:26 +09:00
|
|
|
if (!appState.selectedElementIds[element.id]) {
|
2020-02-02 20:04:35 +02:00
|
|
|
return false;
|
|
|
|
}
|
2020-01-06 19:34:22 +04:00
|
|
|
|
2020-08-10 14:16:39 +02:00
|
|
|
const {
|
|
|
|
rotation: rotationTransformHandle,
|
|
|
|
...transformHandles
|
|
|
|
} = getTransformHandles(element, zoom, pointerType);
|
2020-04-02 17:40:26 +09:00
|
|
|
|
2020-08-10 14:16:39 +02:00
|
|
|
if (
|
|
|
|
rotationTransformHandle &&
|
|
|
|
isInsideTransformHandle(rotationTransformHandle, x, y)
|
|
|
|
) {
|
|
|
|
return "rotation" as TransformHandleType;
|
2020-04-02 17:40:26 +09:00
|
|
|
}
|
|
|
|
|
2020-08-10 14:16:39 +02:00
|
|
|
const filter = Object.keys(transformHandles).filter((key) => {
|
|
|
|
const transformHandle = transformHandles[
|
|
|
|
key as Exclude<TransformHandleType, "rotation">
|
|
|
|
]!;
|
|
|
|
if (!transformHandle) {
|
2020-02-02 20:04:35 +02:00
|
|
|
return false;
|
|
|
|
}
|
2020-08-10 14:16:39 +02:00
|
|
|
return isInsideTransformHandle(transformHandle, x, y);
|
2020-01-06 19:34:22 +04:00
|
|
|
});
|
|
|
|
|
|
|
|
if (filter.length > 0) {
|
2020-08-10 14:16:39 +02:00
|
|
|
return filter[0] as TransformHandleType;
|
2020-01-06 19:34:22 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
2020-05-20 16:21:37 +03:00
|
|
|
};
|
2020-01-08 23:56:35 -03:00
|
|
|
|
2020-08-10 14:16:39 +02:00
|
|
|
export const getElementWithTransformHandleType = (
|
2020-04-08 09:49:52 -07:00
|
|
|
elements: readonly NonDeletedExcalidrawElement[],
|
2020-03-08 10:20:55 -07:00
|
|
|
appState: AppState,
|
2020-06-01 11:35:44 +02:00
|
|
|
scenePointerX: number,
|
|
|
|
scenePointerY: number,
|
2020-02-15 21:03:32 +01:00
|
|
|
zoom: number,
|
2020-02-21 14:34:18 -05:00
|
|
|
pointerType: PointerType,
|
2020-06-01 11:35:44 +02:00
|
|
|
) => {
|
|
|
|
return elements.reduce((result, element) => {
|
2020-01-08 23:56:35 -03:00
|
|
|
if (result) {
|
|
|
|
return result;
|
|
|
|
}
|
2020-08-10 14:16:39 +02:00
|
|
|
const transformHandleType = resizeTest(
|
2020-06-01 11:35:44 +02:00
|
|
|
element,
|
|
|
|
appState,
|
|
|
|
scenePointerX,
|
|
|
|
scenePointerY,
|
|
|
|
zoom,
|
|
|
|
pointerType,
|
|
|
|
);
|
2020-08-10 14:16:39 +02:00
|
|
|
return transformHandleType ? { element, transformHandleType } : null;
|
|
|
|
}, null as { element: NonDeletedExcalidrawElement; transformHandleType: MaybeTransformHandleType } | null);
|
2020-06-01 11:35:44 +02:00
|
|
|
};
|
2020-01-19 21:11:46 +00:00
|
|
|
|
2020-08-10 14:16:39 +02:00
|
|
|
export const getTransformHandleTypeFromCoords = (
|
2020-04-07 17:49:59 +09:00
|
|
|
[x1, y1, x2, y2]: readonly [number, number, number, number],
|
2020-06-01 11:35:44 +02:00
|
|
|
scenePointerX: number,
|
|
|
|
scenePointerY: number,
|
2020-04-07 17:49:59 +09:00
|
|
|
zoom: number,
|
|
|
|
pointerType: PointerType,
|
2020-08-10 14:16:39 +02:00
|
|
|
): MaybeTransformHandleType => {
|
|
|
|
const transformHandles = getTransformHandlesFromCoords(
|
2020-04-07 17:49:59 +09:00
|
|
|
[x1, y1, x2, y2],
|
|
|
|
0,
|
|
|
|
zoom,
|
|
|
|
pointerType,
|
|
|
|
OMIT_SIDES_FOR_MULTIPLE_ELEMENTS,
|
|
|
|
);
|
|
|
|
|
2020-08-10 14:16:39 +02:00
|
|
|
const found = Object.keys(transformHandles).find((key) => {
|
|
|
|
const transformHandle = transformHandles[
|
|
|
|
key as Exclude<TransformHandleType, "rotation">
|
|
|
|
]!;
|
|
|
|
return (
|
|
|
|
transformHandle &&
|
|
|
|
isInsideTransformHandle(transformHandle, scenePointerX, scenePointerY)
|
|
|
|
);
|
2020-04-07 17:49:59 +09:00
|
|
|
});
|
2020-08-10 14:16:39 +02:00
|
|
|
return (found || false) as MaybeTransformHandleType;
|
2020-05-20 16:21:37 +03:00
|
|
|
};
|
2020-04-07 17:49:59 +09:00
|
|
|
|
2020-04-22 22:11:01 +09:00
|
|
|
const RESIZE_CURSORS = ["ns", "nesw", "ew", "nwse"];
|
|
|
|
const rotateResizeCursor = (cursor: string, angle: number) => {
|
|
|
|
const index = RESIZE_CURSORS.indexOf(cursor);
|
|
|
|
if (index >= 0) {
|
|
|
|
const a = Math.round(angle / (Math.PI / 4));
|
|
|
|
cursor = RESIZE_CURSORS[(index + a) % RESIZE_CURSORS.length];
|
|
|
|
}
|
|
|
|
return cursor;
|
|
|
|
};
|
|
|
|
|
2020-01-19 21:11:46 +00:00
|
|
|
/*
|
|
|
|
* Returns bi-directional cursor for the element being resized
|
|
|
|
*/
|
2020-05-20 16:21:37 +03:00
|
|
|
export const getCursorForResizingElement = (resizingElement: {
|
2020-04-07 17:49:59 +09:00
|
|
|
element?: ExcalidrawElement;
|
2020-08-10 14:16:39 +02:00
|
|
|
transformHandleType: MaybeTransformHandleType;
|
2020-05-20 16:21:37 +03:00
|
|
|
}): string => {
|
2020-08-10 14:16:39 +02:00
|
|
|
const { element, transformHandleType } = resizingElement;
|
2020-01-19 21:11:46 +00:00
|
|
|
const shouldSwapCursors =
|
2020-04-07 17:49:59 +09:00
|
|
|
element && Math.sign(element.height) * Math.sign(element.width) === -1;
|
2020-01-19 21:11:46 +00:00
|
|
|
let cursor = null;
|
|
|
|
|
2020-08-10 14:16:39 +02:00
|
|
|
switch (transformHandleType) {
|
2020-01-19 21:11:46 +00:00
|
|
|
case "n":
|
|
|
|
case "s":
|
|
|
|
cursor = "ns";
|
|
|
|
break;
|
|
|
|
case "w":
|
|
|
|
case "e":
|
|
|
|
cursor = "ew";
|
|
|
|
break;
|
|
|
|
case "nw":
|
|
|
|
case "se":
|
|
|
|
if (shouldSwapCursors) {
|
|
|
|
cursor = "nesw";
|
|
|
|
} else {
|
|
|
|
cursor = "nwse";
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case "ne":
|
|
|
|
case "sw":
|
|
|
|
if (shouldSwapCursors) {
|
|
|
|
cursor = "nwse";
|
|
|
|
} else {
|
|
|
|
cursor = "nesw";
|
|
|
|
}
|
|
|
|
break;
|
2020-04-02 17:40:26 +09:00
|
|
|
case "rotation":
|
2020-04-22 22:11:01 +09:00
|
|
|
return "grab";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cursor && element) {
|
|
|
|
cursor = rotateResizeCursor(cursor, element.angle);
|
2020-01-19 21:11:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return cursor ? `${cursor}-resize` : "";
|
2020-05-20 16:21:37 +03:00
|
|
|
};
|
2020-01-24 20:45:52 +01:00
|
|
|
|
2020-08-10 14:16:39 +02:00
|
|
|
export const normalizeTransformHandleType = (
|
2020-01-24 20:45:52 +01:00
|
|
|
element: ExcalidrawElement,
|
2020-08-10 14:16:39 +02:00
|
|
|
transformHandleType: TransformHandleType,
|
|
|
|
): TransformHandleType => {
|
2020-04-09 18:53:12 +09:00
|
|
|
if (element.width >= 0 && element.height >= 0) {
|
2020-08-10 14:16:39 +02:00
|
|
|
return transformHandleType;
|
2020-01-24 20:45:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (element.width < 0 && element.height < 0) {
|
2020-08-10 14:16:39 +02:00
|
|
|
switch (transformHandleType) {
|
2020-01-24 20:45:52 +01:00
|
|
|
case "nw":
|
|
|
|
return "se";
|
|
|
|
case "ne":
|
|
|
|
return "sw";
|
|
|
|
case "se":
|
|
|
|
return "nw";
|
|
|
|
case "sw":
|
|
|
|
return "ne";
|
|
|
|
}
|
|
|
|
} else if (element.width < 0) {
|
2020-08-10 14:16:39 +02:00
|
|
|
switch (transformHandleType) {
|
2020-01-24 20:45:52 +01:00
|
|
|
case "nw":
|
|
|
|
return "ne";
|
|
|
|
case "ne":
|
|
|
|
return "nw";
|
|
|
|
case "se":
|
|
|
|
return "sw";
|
|
|
|
case "sw":
|
|
|
|
return "se";
|
|
|
|
case "e":
|
|
|
|
return "w";
|
|
|
|
case "w":
|
|
|
|
return "e";
|
|
|
|
}
|
|
|
|
} else {
|
2020-08-10 14:16:39 +02:00
|
|
|
switch (transformHandleType) {
|
2020-01-24 20:45:52 +01:00
|
|
|
case "nw":
|
|
|
|
return "sw";
|
|
|
|
case "ne":
|
|
|
|
return "se";
|
|
|
|
case "se":
|
|
|
|
return "ne";
|
|
|
|
case "sw":
|
|
|
|
return "nw";
|
|
|
|
case "n":
|
|
|
|
return "s";
|
|
|
|
case "s":
|
|
|
|
return "n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-10 14:16:39 +02:00
|
|
|
return transformHandleType;
|
2020-05-20 16:21:37 +03:00
|
|
|
};
|