tldraw/state/sessions/rotate-session.ts

150 lines
3.7 KiB
TypeScript
Raw Normal View History

2021-06-04 21:34:12 +00:00
import { Data, ShapeType } from 'types'
import vec from 'utils/vec'
2021-05-28 16:25:43 +00:00
import BaseSession from './base-session'
import commands from 'state/commands'
import { current } from 'immer'
import {
2021-05-23 08:30:20 +00:00
clampToRotationToSegments,
getBoundsCenter,
getCommonBounds,
getPage,
2021-05-29 22:27:19 +00:00
getRotatedBounds,
getShapeBounds,
2021-06-04 16:08:43 +00:00
updateParents,
2021-06-04 21:34:12 +00:00
getDocumentBranch,
setToArray,
getSelectedIds,
2021-06-24 08:18:14 +00:00
} from 'utils'
2021-06-21 21:35:28 +00:00
import { getShapeUtils } from 'state/shape-utils'
const PI2 = Math.PI * 2
2021-05-17 21:27:18 +00:00
export default class RotateSession extends BaseSession {
delta = [0, 0]
origin: number[]
snapshot: RotateSnapshot
2021-06-05 07:42:17 +00:00
prev = 0
2021-05-17 21:27:18 +00:00
constructor(data: Data, point: number[]) {
super(data)
this.origin = point
this.snapshot = getRotateSnapshot(data)
}
2021-06-21 21:35:28 +00:00
update(data: Data, point: number[], isLocked: boolean): void {
2021-05-29 22:27:19 +00:00
const { commonBoundsCenter, initialShapes } = this.snapshot
2021-05-17 21:27:18 +00:00
const page = getPage(data)
2021-05-29 22:27:19 +00:00
const a1 = vec.angle(commonBoundsCenter, this.origin)
const a2 = vec.angle(commonBoundsCenter, point)
2021-05-17 21:27:18 +00:00
2021-05-23 08:30:20 +00:00
let rot = a2 - a1
2021-06-05 07:42:17 +00:00
const delta = rot - this.prev
this.prev = rot
if (isLocked) {
2021-05-23 08:30:20 +00:00
rot = clampToRotationToSegments(rot, 24)
}
data.boundsRotation = (PI2 + (this.snapshot.boundsRotation + rot)) % PI2
2021-05-18 08:32:20 +00:00
2021-06-21 21:35:28 +00:00
for (const { id, center, offset, rotation } of initialShapes) {
const shape = page.shapes[id]
2021-06-05 07:42:17 +00:00
const nextRotation =
PI2 +
((isLocked
? clampToRotationToSegments(rotation + rot, 24)
: rotation + rot) %
PI2)
2021-05-29 22:27:19 +00:00
const nextPoint = vec.sub(
vec.rotWith(center, commonBoundsCenter, rot),
offset
)
getShapeUtils(shape)
2021-06-05 07:42:17 +00:00
.rotateTo(shape, nextRotation, delta)
.translateTo(shape, nextPoint)
2021-05-17 21:27:18 +00:00
}
2021-06-04 16:08:43 +00:00
updateParents(
data,
initialShapes.map((s) => s.id)
)
2021-05-17 21:27:18 +00:00
}
2021-06-21 21:35:28 +00:00
cancel(data: Data): void {
2021-06-24 12:34:43 +00:00
const { initialShapes } = this.snapshot
const page = getPage(data)
2021-05-17 21:27:18 +00:00
2021-06-21 21:35:28 +00:00
for (const { id, point, rotation } of initialShapes) {
const shape = page.shapes[id]
2021-06-02 15:05:44 +00:00
getShapeUtils(shape)
2021-06-05 07:42:17 +00:00
.rotateTo(shape, rotation, rotation - shape.rotation)
.translateTo(shape, point)
2021-05-17 21:27:18 +00:00
}
2021-06-04 16:08:43 +00:00
updateParents(
data,
initialShapes.map((s) => s.id)
)
2021-05-17 21:27:18 +00:00
}
2021-06-21 21:35:28 +00:00
complete(data: Data): void {
2021-05-29 12:54:33 +00:00
if (!this.snapshot.hasUnlockedShapes) return
2021-05-17 21:27:18 +00:00
commands.rotate(data, this.snapshot, getRotateSnapshot(data))
}
}
2021-06-21 21:35:28 +00:00
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
2021-05-17 21:27:18 +00:00
export function getRotateSnapshot(data: Data) {
2021-06-04 21:34:12 +00:00
const cData = current(data)
const page = getPage(cData)
const initialShapes = setToArray(getSelectedIds(data))
2021-06-04 21:34:12 +00:00
.flatMap((id) => getDocumentBranch(cData, id).map((id) => page.shapes[id]))
.filter((shape) => !shape.isLocked)
2021-05-29 12:54:33 +00:00
const hasUnlockedShapes = initialShapes.length > 0
2021-05-17 21:27:18 +00:00
const shapesBounds = Object.fromEntries(
2021-05-29 12:54:33 +00:00
initialShapes.map((shape) => [shape.id, getShapeBounds(shape)])
2021-05-17 21:27:18 +00:00
)
const bounds = getCommonBounds(...Object.values(shapesBounds))
2021-05-29 22:27:19 +00:00
const commonBoundsCenter = getBoundsCenter(bounds)
2021-05-17 21:27:18 +00:00
return {
2021-05-29 12:54:33 +00:00
hasUnlockedShapes,
currentPageId: data.currentPageId,
boundsRotation: data.boundsRotation,
2021-05-29 22:27:19 +00:00
commonBoundsCenter,
2021-06-04 21:34:12 +00:00
initialShapes: initialShapes
.filter((shape) => shape.type !== ShapeType.Group)
.map((shape) => {
const bounds = shapesBounds[shape.id]
const center = getBoundsCenter(bounds)
const offset = vec.sub(center, shape.point)
const rotationOffset = vec.sub(
center,
getBoundsCenter(getRotatedBounds(shape))
)
return {
id: shape.id,
point: shape.point,
rotation: shape.rotation,
offset,
rotationOffset,
center,
}
}),
2021-05-17 21:27:18 +00:00
}
}
export type RotateSnapshot = ReturnType<typeof getRotateSnapshot>