2020-02-21 14:34:18 -05:00
|
|
|
import { ExcalidrawElement, PointerType } from "./types";
|
2020-02-15 21:03:32 +01:00
|
|
|
|
|
|
|
import { getElementAbsoluteCoords } from "./bounds";
|
2020-04-02 17:40:26 +09:00
|
|
|
import { rotate } from "../math";
|
2020-01-06 19:34:22 +04:00
|
|
|
|
2020-04-02 17:40:26 +09:00
|
|
|
type Sides = "n" | "s" | "w" | "e" | "nw" | "ne" | "sw" | "se" | "rotation";
|
2020-01-08 16:55:13 +01:00
|
|
|
|
2020-02-21 14:34:18 -05:00
|
|
|
const handleSizes: { [k in PointerType]: number } = {
|
|
|
|
mouse: 8,
|
|
|
|
pen: 16,
|
|
|
|
touch: 28,
|
|
|
|
};
|
2020-02-01 15:49:18 +04:00
|
|
|
|
2020-04-02 17:40:26 +09:00
|
|
|
const ROTATION_HANDLER_GAP = 16;
|
|
|
|
|
|
|
|
function generateHandler(
|
|
|
|
x: number,
|
|
|
|
y: number,
|
|
|
|
width: number,
|
|
|
|
height: number,
|
|
|
|
cx: number,
|
|
|
|
cy: number,
|
|
|
|
angle: number,
|
|
|
|
): [number, number, number, number] {
|
|
|
|
const [xx, yy] = rotate(x + width / 2, y + height / 2, cx, cy, angle);
|
|
|
|
return [xx - width / 2, yy - height / 2, width, height];
|
|
|
|
}
|
|
|
|
|
2020-02-21 14:34:18 -05:00
|
|
|
export function handlerRectangles(
|
|
|
|
element: ExcalidrawElement,
|
|
|
|
zoom: number,
|
|
|
|
pointerType: PointerType = "mouse",
|
|
|
|
) {
|
|
|
|
const size = handleSizes[pointerType];
|
|
|
|
const handlerWidth = size / zoom;
|
|
|
|
const handlerHeight = size / zoom;
|
|
|
|
|
|
|
|
const handlerMarginX = size / zoom;
|
|
|
|
const handlerMarginY = size / zoom;
|
2020-02-01 15:49:18 +04:00
|
|
|
|
2020-02-15 21:03:32 +01:00
|
|
|
const [elementX1, elementY1, elementX2, elementY2] = getElementAbsoluteCoords(
|
|
|
|
element,
|
|
|
|
);
|
|
|
|
|
|
|
|
const elementWidth = elementX2 - elementX1;
|
|
|
|
const elementHeight = elementY2 - elementY1;
|
2020-04-02 17:40:26 +09:00
|
|
|
const cx = (elementX1 + elementX2) / 2;
|
|
|
|
const cy = (elementY1 + elementY2) / 2;
|
|
|
|
const angle = element.angle;
|
2020-02-15 21:03:32 +01:00
|
|
|
|
|
|
|
const dashedLineMargin = 4 / zoom;
|
2020-01-06 19:34:22 +04:00
|
|
|
|
2020-02-21 14:34:18 -05:00
|
|
|
const centeringOffset = (size - 8) / (2 * zoom);
|
|
|
|
|
2020-04-06 14:21:07 +03:00
|
|
|
const handlers =
|
|
|
|
{
|
|
|
|
nw: generateHandler(
|
|
|
|
elementX1 - dashedLineMargin - handlerMarginX + centeringOffset,
|
|
|
|
elementY1 - dashedLineMargin - handlerMarginY + centeringOffset,
|
|
|
|
handlerWidth,
|
|
|
|
handlerHeight,
|
|
|
|
cx,
|
|
|
|
cy,
|
|
|
|
angle,
|
|
|
|
),
|
|
|
|
ne: generateHandler(
|
|
|
|
elementX2 + dashedLineMargin - centeringOffset,
|
|
|
|
elementY1 - dashedLineMargin - handlerMarginY + centeringOffset,
|
|
|
|
handlerWidth,
|
|
|
|
handlerHeight,
|
|
|
|
cx,
|
|
|
|
cy,
|
|
|
|
angle,
|
|
|
|
),
|
|
|
|
sw: generateHandler(
|
|
|
|
elementX1 - dashedLineMargin - handlerMarginX + centeringOffset,
|
|
|
|
elementY2 + dashedLineMargin - centeringOffset,
|
|
|
|
handlerWidth,
|
|
|
|
handlerHeight,
|
|
|
|
cx,
|
|
|
|
cy,
|
|
|
|
angle,
|
|
|
|
),
|
|
|
|
se: generateHandler(
|
|
|
|
elementX2 + dashedLineMargin - centeringOffset,
|
|
|
|
elementY2 + dashedLineMargin - centeringOffset,
|
|
|
|
handlerWidth,
|
|
|
|
handlerHeight,
|
|
|
|
cx,
|
|
|
|
cy,
|
|
|
|
angle,
|
|
|
|
),
|
|
|
|
rotation: generateHandler(
|
|
|
|
elementX1 + elementWidth / 2 - handlerWidth / 2,
|
|
|
|
elementY1 -
|
|
|
|
dashedLineMargin -
|
|
|
|
handlerMarginY +
|
|
|
|
centeringOffset -
|
|
|
|
ROTATION_HANDLER_GAP / zoom,
|
|
|
|
handlerWidth,
|
|
|
|
handlerHeight,
|
|
|
|
cx,
|
|
|
|
cy,
|
|
|
|
angle,
|
|
|
|
),
|
|
|
|
} as { [T in Sides]: [number, number, number, number] };
|
2020-01-06 19:34:22 +04:00
|
|
|
|
2020-02-15 21:03:32 +01:00
|
|
|
// We only want to show height handlers (all cardinal directions) above a certain size
|
2020-02-21 14:34:18 -05:00
|
|
|
const minimumSizeForEightHandlers = (5 * size) / zoom;
|
2020-02-15 21:03:32 +01:00
|
|
|
if (Math.abs(elementWidth) > minimumSizeForEightHandlers) {
|
2020-04-02 17:40:26 +09:00
|
|
|
handlers["n"] = generateHandler(
|
2020-02-21 14:34:18 -05:00
|
|
|
elementX1 + elementWidth / 2 - handlerWidth / 2,
|
|
|
|
elementY1 - dashedLineMargin - handlerMarginY + centeringOffset,
|
2020-02-15 21:03:32 +01:00
|
|
|
handlerWidth,
|
|
|
|
handlerHeight,
|
2020-04-02 17:40:26 +09:00
|
|
|
cx,
|
|
|
|
cy,
|
|
|
|
angle,
|
|
|
|
);
|
|
|
|
handlers["s"] = generateHandler(
|
2020-02-21 14:34:18 -05:00
|
|
|
elementX1 + elementWidth / 2 - handlerWidth / 2,
|
|
|
|
elementY2 + dashedLineMargin - centeringOffset,
|
2020-02-15 21:03:32 +01:00
|
|
|
handlerWidth,
|
|
|
|
handlerHeight,
|
2020-04-02 17:40:26 +09:00
|
|
|
cx,
|
|
|
|
cy,
|
|
|
|
angle,
|
|
|
|
);
|
2020-01-06 19:34:22 +04:00
|
|
|
}
|
2020-02-15 21:03:32 +01:00
|
|
|
if (Math.abs(elementHeight) > minimumSizeForEightHandlers) {
|
2020-04-02 17:40:26 +09:00
|
|
|
handlers["w"] = generateHandler(
|
2020-02-21 14:34:18 -05:00
|
|
|
elementX1 - dashedLineMargin - handlerMarginX + centeringOffset,
|
|
|
|
elementY1 + elementHeight / 2 - handlerHeight / 2,
|
2020-02-15 21:03:32 +01:00
|
|
|
handlerWidth,
|
|
|
|
handlerHeight,
|
2020-04-02 17:40:26 +09:00
|
|
|
cx,
|
|
|
|
cy,
|
|
|
|
angle,
|
|
|
|
);
|
|
|
|
handlers["e"] = generateHandler(
|
2020-02-21 14:34:18 -05:00
|
|
|
elementX2 + dashedLineMargin - centeringOffset,
|
|
|
|
elementY1 + elementHeight / 2 - handlerHeight / 2,
|
2020-02-15 21:03:32 +01:00
|
|
|
handlerWidth,
|
|
|
|
handlerHeight,
|
2020-04-02 17:40:26 +09:00
|
|
|
cx,
|
|
|
|
cy,
|
|
|
|
angle,
|
|
|
|
);
|
2020-01-06 19:34:22 +04:00
|
|
|
}
|
|
|
|
|
2020-02-04 13:45:22 +04:00
|
|
|
if (element.type === "arrow" || element.type === "line") {
|
2020-02-01 15:49:18 +04:00
|
|
|
if (element.points.length === 2) {
|
|
|
|
// only check the last point because starting point is always (0,0)
|
|
|
|
const [, p1] = element.points;
|
|
|
|
|
|
|
|
if (p1[0] === 0 || p1[1] === 0) {
|
2020-04-06 14:21:07 +03:00
|
|
|
return (
|
|
|
|
{
|
|
|
|
nw: handlers.nw,
|
|
|
|
se: handlers.se,
|
|
|
|
} as typeof handlers
|
|
|
|
);
|
2020-02-01 15:49:18 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (p1[0] > 0 && p1[1] < 0) {
|
2020-04-06 14:21:07 +03:00
|
|
|
return (
|
|
|
|
{
|
|
|
|
ne: handlers.ne,
|
|
|
|
sw: handlers.sw,
|
|
|
|
} as typeof handlers
|
|
|
|
);
|
2020-02-01 15:49:18 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (p1[0] > 0 && p1[1] > 0) {
|
2020-04-06 14:21:07 +03:00
|
|
|
return (
|
|
|
|
{
|
|
|
|
nw: handlers.nw,
|
|
|
|
se: handlers.se,
|
|
|
|
} as typeof handlers
|
|
|
|
);
|
2020-02-01 15:49:18 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (p1[0] < 0 && p1[1] > 0) {
|
2020-04-06 14:21:07 +03:00
|
|
|
return (
|
|
|
|
{
|
|
|
|
ne: handlers.ne,
|
|
|
|
sw: handlers.sw,
|
|
|
|
} as typeof handlers
|
|
|
|
);
|
2020-02-01 15:49:18 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (p1[0] < 0 && p1[1] < 0) {
|
2020-04-06 14:21:07 +03:00
|
|
|
return (
|
|
|
|
{
|
|
|
|
nw: handlers.nw,
|
|
|
|
se: handlers.se,
|
|
|
|
} as typeof handlers
|
|
|
|
);
|
2020-02-01 15:49:18 +04:00
|
|
|
}
|
|
|
|
}
|
2020-01-06 19:34:22 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return handlers;
|
|
|
|
}
|