From 6582eb990c94ac9c7013dda8390d88dfe7ef946b Mon Sep 17 00:00:00 2001 From: Steve Ruiz Date: Sun, 23 May 2021 09:30:20 +0100 Subject: [PATCH] improve rotation --- lib/shape-utils/ellipse.tsx | 21 +++++++++++++++------ state/sessions/rotate-session.ts | 5 +++-- utils/bounds.ts | 22 ++++++++++++++++++++++ utils/utils.ts | 9 +++++++++ 4 files changed, 49 insertions(+), 8 deletions(-) diff --git a/lib/shape-utils/ellipse.tsx b/lib/shape-utils/ellipse.tsx index 556b1636f..ce298ed59 100644 --- a/lib/shape-utils/ellipse.tsx +++ b/lib/shape-utils/ellipse.tsx @@ -2,7 +2,7 @@ import { v4 as uuid } from "uuid" import * as vec from "utils/vec" import { EllipseShape, ShapeType } from "types" import { registerShapeUtils } from "./index" -import { boundsContained } from "utils/bounds" +import { boundsContained, getRotatedEllipseBounds } from "utils/bounds" import { intersectEllipseBounds } from "utils/intersections" import { pointInEllipse } from "utils/hitTests" import { @@ -47,8 +47,8 @@ const ellipse = registerShapeUtils({ const bounds = { minX: 0, - maxX: radiusX * 2, minY: 0, + maxX: radiusX * 2, maxY: radiusY * 2, width: radiusX * 2, height: radiusY * 2, @@ -61,7 +61,13 @@ const ellipse = registerShapeUtils({ }, getRotatedBounds(shape) { - return getBoundsFromPoints(getRotatedCorners(shape)) + return getRotatedEllipseBounds( + shape.point[0], + shape.point[1], + shape.radiusX, + shape.radiusY, + shape.rotation + ) }, getCenter(shape) { @@ -79,8 +85,6 @@ const ellipse = registerShapeUtils({ }, hitTestBounds(this, shape, brushBounds) { - // TODO: Account for rotation - const shapeBounds = this.getBounds(shape) return ( @@ -108,11 +112,16 @@ const ellipse = registerShapeUtils({ return shape }, - transform(shape, bounds) { + transform(shape, bounds, { scaleX, scaleY, initialShape }) { shape.point = [bounds.minX, bounds.minY] shape.radiusX = bounds.width / 2 shape.radiusY = bounds.height / 2 + shape.rotation = + (scaleX < 0 && scaleY >= 0) || (scaleY < 0 && scaleX >= 0) + ? -initialShape.rotation + : initialShape.rotation + return shape }, diff --git a/state/sessions/rotate-session.ts b/state/sessions/rotate-session.ts index 03f2dab85..10b03cad3 100644 --- a/state/sessions/rotate-session.ts +++ b/state/sessions/rotate-session.ts @@ -4,6 +4,7 @@ import BaseSession from "./base-session" import commands from "state/commands" import { current } from "immer" import { + clampToRotationToSegments, getBoundsCenter, getCommonBounds, getPage, @@ -31,10 +32,10 @@ export default class RotateSession extends BaseSession { const a1 = vec.angle(boundsCenter, this.origin) const a2 = vec.angle(boundsCenter, point) - let rot = (PI2 + (a2 - a1)) % PI2 + let rot = a2 - a1 if (isLocked) { - rot = Math.floor((rot + Math.PI / 8) / (Math.PI / 4)) * (Math.PI / 4) + rot = clampToRotationToSegments(rot, 24) } data.boundsRotation = (PI2 + (this.snapshot.boundsRotation + rot)) % PI2 diff --git a/utils/bounds.ts b/utils/bounds.ts index 5cd50316f..9fccefa1b 100644 --- a/utils/bounds.ts +++ b/utils/bounds.ts @@ -82,3 +82,25 @@ export function boundsAreEqual(a: Bounds, b: Bounds) { export function pointInBounds(A: number[], b: Bounds) { return !(A[0] < b.minX || A[0] > b.maxX || A[1] < b.minY || A[1] > b.maxY) } + +export function getRotatedEllipseBounds( + x: number, + y: number, + rx: number, + ry: number, + rotation: number +) { + const c = Math.cos(rotation) + const s = Math.sin(rotation) + const w = Math.hypot(rx * c, ry * s) + const h = Math.hypot(rx * s, ry * c) + + return { + minX: x + rx - w, + minY: y + ry - h, + maxX: x + rx + w, + maxY: y + ry + h, + width: w * 2, + height: h * 2, + } +} diff --git a/utils/utils.ts b/utils/utils.ts index e2500db2b..2f7c0089b 100644 --- a/utils/utils.ts +++ b/utils/utils.ts @@ -1366,3 +1366,12 @@ export function getShapeBounds(shape: Shape) { export function getBoundsCenter(bounds: Bounds) { return [bounds.minX + bounds.width / 2, bounds.minY + bounds.height / 2] } + +export function clampRadians(r: number) { + return (Math.PI * 2 + r) % (Math.PI * 2) +} + +export function clampToRotationToSegments(r: number, segments: number) { + const seg = (Math.PI * 2) / segments + return Math.floor((clampRadians(r) + seg / 2) / seg) * seg +}