| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149 |
- import { Data, ShapeType } from 'types'
- import * as vec from 'utils/vec'
- import BaseSession from './base-session'
- import commands from 'state/commands'
- import { current } from 'immer'
- import {
- clampToRotationToSegments,
- getBoundsCenter,
- getCommonBounds,
- getPage,
- getSelectedShapes,
- getRotatedBounds,
- getShapeBounds,
- updateParents,
- getDocumentBranch,
- setToArray,
- getSelectedIds,
- } from 'utils/utils'
- import { getShapeUtils } from 'lib/shape-utils'
-
- const PI2 = Math.PI * 2
-
- export default class RotateSession extends BaseSession {
- delta = [0, 0]
- origin: number[]
- snapshot: RotateSnapshot
- prev = 0
-
- constructor(data: Data, point: number[]) {
- super(data)
- this.origin = point
- this.snapshot = getRotateSnapshot(data)
- }
-
- update(data: Data, point: number[], isLocked: boolean) {
- const { commonBoundsCenter, initialShapes } = this.snapshot
-
- const page = getPage(data)
- const a1 = vec.angle(commonBoundsCenter, this.origin)
- const a2 = vec.angle(commonBoundsCenter, point)
-
- let rot = a2 - a1
-
- const delta = rot - this.prev
- this.prev = rot
-
- if (isLocked) {
- rot = clampToRotationToSegments(rot, 24)
- }
-
- data.boundsRotation = (PI2 + (this.snapshot.boundsRotation + rot)) % PI2
-
- for (let { id, center, offset, rotation } of initialShapes) {
- const shape = page.shapes[id]
-
- const nextRotation =
- PI2 +
- ((isLocked
- ? clampToRotationToSegments(rotation + rot, 24)
- : rotation + rot) %
- PI2)
-
- const nextPoint = vec.sub(
- vec.rotWith(center, commonBoundsCenter, rot),
- offset
- )
-
- getShapeUtils(shape)
- .rotateTo(shape, nextRotation, delta)
- .translateTo(shape, nextPoint)
- }
-
- updateParents(
- data,
- initialShapes.map((s) => s.id)
- )
- }
-
- cancel(data: Data) {
- const { currentPageId, initialShapes } = this.snapshot
- const page = getPage(data, currentPageId)
-
- for (let { id, point, rotation } of initialShapes) {
- const shape = page.shapes[id]
- getShapeUtils(shape)
- .rotateTo(shape, rotation, rotation - shape.rotation)
- .translateTo(shape, point)
- }
-
- updateParents(
- data,
- initialShapes.map((s) => s.id)
- )
- }
-
- complete(data: Data) {
- if (!this.snapshot.hasUnlockedShapes) return
- commands.rotate(data, this.snapshot, getRotateSnapshot(data))
- }
- }
-
- export function getRotateSnapshot(data: Data) {
- const cData = current(data)
- const page = getPage(cData)
-
- const initialShapes = setToArray(getSelectedIds(data))
- .flatMap((id) => getDocumentBranch(cData, id).map((id) => page.shapes[id]))
- .filter((shape) => !shape.isLocked)
-
- const hasUnlockedShapes = initialShapes.length > 0
-
- const shapesBounds = Object.fromEntries(
- initialShapes.map((shape) => [shape.id, getShapeBounds(shape)])
- )
-
- const bounds = getCommonBounds(...Object.values(shapesBounds))
-
- const commonBoundsCenter = getBoundsCenter(bounds)
-
- return {
- hasUnlockedShapes,
- currentPageId: data.currentPageId,
- boundsRotation: data.boundsRotation,
- commonBoundsCenter,
- 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,
- }
- }),
- }
- }
-
- export type RotateSnapshot = ReturnType<typeof getRotateSnapshot>
|