Rotation support (#1099)

* rotate rectanble with fixed angle

* rotate dashed rectangle with fixed angle

* fix rotate handler rect

* fix canvas size with rotation

* angle in element base

* fix bug in calculating canvas size

* trial only for rectangle

* hitTest for rectangle rotation

* properly resize rotated rectangle

* fix canvas size calculation

* giving up... workaround for now

* **experimental** handler to rotate rectangle

* remove rotation on copy for debugging

* update snapshots

* better rotation handler with atan2

* rotate when drawImage

* add rotation handler

* hitTest for any shapes

* fix hitTest for curved lines

* rotate text element

* rotation locking

* hint messaage for rotating

* show proper handlers on mobile (a workaround, there should be a better way)

* refactor hitTest

* support exporting png

* support exporting svg

* fix rotating curved line

* refactor drawElementFromCanvas with getElementAbsoluteCoords

* fix export png and svg

* adjust resize positions for lines (N, E, S, W)

* do not make handlers big on mobile

* Update src/locales/en.json

Alright!

Co-Authored-By: Lipis <lipiridis@gmail.com>

* do not show rotation/resizing hints on mobile

* proper calculation for N and W positions

* simplify calculation

* use "rotation" as property name for clarification (may increase bundle size)

* update snapshots excluding rotation handle

* refactor with adjustPositionWithRotation

* refactor with adjustXYWithRotation

* forgot to rename rotation

* rename internal function

* initialize element angle on restore

* rotate wysiwyg editor

* fix shift-rotate around 270deg

* improve rotation locking

* refactor adjustXYWithRotation

* avoid rotation degree becomes >=360

* refactor with generateHandler

Co-authored-by: Lipis <lipiridis@gmail.com>
Co-authored-by: dwelle <luzar.david@gmail.com>
This commit is contained in:
Daishi Kato 2020-04-02 17:40:26 +09:00 committed by GitHub
parent 3e3ce18755
commit 65be7973be
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 664 additions and 108 deletions

View file

@ -1,8 +1,9 @@
import { ExcalidrawElement, PointerType } from "./types";
import { getElementAbsoluteCoords } from "./bounds";
import { rotate } from "../math";
type Sides = "n" | "s" | "w" | "e" | "nw" | "ne" | "sw" | "se";
type Sides = "n" | "s" | "w" | "e" | "nw" | "ne" | "sw" | "se" | "rotation";
const handleSizes: { [k in PointerType]: number } = {
mouse: 8,
@ -10,6 +11,21 @@ const handleSizes: { [k in PointerType]: number } = {
touch: 28,
};
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];
}
export function handlerRectangles(
element: ExcalidrawElement,
zoom: number,
@ -28,67 +44,107 @@ export function handlerRectangles(
const elementWidth = elementX2 - elementX1;
const elementHeight = elementY2 - elementY1;
const cx = (elementX1 + elementX2) / 2;
const cy = (elementY1 + elementY2) / 2;
const angle = element.angle;
const dashedLineMargin = 4 / zoom;
const centeringOffset = (size - 8) / (2 * zoom);
const handlers = {
nw: [
nw: generateHandler(
elementX1 - dashedLineMargin - handlerMarginX + centeringOffset,
elementY1 - dashedLineMargin - handlerMarginY + centeringOffset,
handlerWidth,
handlerHeight,
],
ne: [
cx,
cy,
angle,
),
ne: generateHandler(
elementX2 + dashedLineMargin - centeringOffset,
elementY1 - dashedLineMargin - handlerMarginY + centeringOffset,
handlerWidth,
handlerHeight,
],
sw: [
cx,
cy,
angle,
),
sw: generateHandler(
elementX1 - dashedLineMargin - handlerMarginX + centeringOffset,
elementY2 + dashedLineMargin - centeringOffset,
handlerWidth,
handlerHeight,
],
se: [
cx,
cy,
angle,
),
se: generateHandler(
elementX2 + dashedLineMargin - centeringOffset,
elementY2 + dashedLineMargin - centeringOffset,
handlerWidth,
handlerHeight,
],
} as { [T in Sides]: number[] };
cx,
cy,
angle,
),
rotation: generateHandler(
elementX1 + elementWidth / 2 - handlerWidth / 2,
elementY1 -
dashedLineMargin -
handlerMarginY +
centeringOffset -
ROTATION_HANDLER_GAP,
handlerWidth,
handlerHeight,
cx,
cy,
angle,
),
} as { [T in Sides]: [number, number, number, number] };
// We only want to show height handlers (all cardinal directions) above a certain size
const minimumSizeForEightHandlers = (5 * size) / zoom;
if (Math.abs(elementWidth) > minimumSizeForEightHandlers) {
handlers["n"] = [
handlers["n"] = generateHandler(
elementX1 + elementWidth / 2 - handlerWidth / 2,
elementY1 - dashedLineMargin - handlerMarginY + centeringOffset,
handlerWidth,
handlerHeight,
];
handlers["s"] = [
cx,
cy,
angle,
);
handlers["s"] = generateHandler(
elementX1 + elementWidth / 2 - handlerWidth / 2,
elementY2 + dashedLineMargin - centeringOffset,
handlerWidth,
handlerHeight,
];
cx,
cy,
angle,
);
}
if (Math.abs(elementHeight) > minimumSizeForEightHandlers) {
handlers["w"] = [
handlers["w"] = generateHandler(
elementX1 - dashedLineMargin - handlerMarginX + centeringOffset,
elementY1 + elementHeight / 2 - handlerHeight / 2,
handlerWidth,
handlerHeight,
];
handlers["e"] = [
cx,
cy,
angle,
);
handlers["e"] = generateHandler(
elementX2 + dashedLineMargin - centeringOffset,
elementY1 + elementHeight / 2 - handlerHeight / 2,
handlerWidth,
handlerHeight,
];
cx,
cy,
angle,
);
}
if (element.type === "arrow" || element.type === "line") {