247 lines
6.1 KiB
TypeScript
247 lines
6.1 KiB
TypeScript
import { Data, TransformEdge, TransformCorner } from "types"
|
|
import * as vec from "utils/vec"
|
|
import BaseSession from "./base-session"
|
|
import commands from "state/commands"
|
|
import { current } from "immer"
|
|
import { getShapeUtils } from "lib/shapes"
|
|
import {
|
|
getTransformedBoundingBox,
|
|
getCommonBounds,
|
|
getRotatedCorners,
|
|
getTransformAnchor,
|
|
} from "utils/utils"
|
|
|
|
export default class TransformSingleSession extends BaseSession {
|
|
delta = [0, 0]
|
|
isFlippedX = false
|
|
isFlippedY = false
|
|
transformType: TransformEdge | TransformCorner
|
|
origin: number[]
|
|
center: number[]
|
|
snapshot: TransformSingleSnapshot
|
|
corners: {
|
|
a: number[]
|
|
b: number[]
|
|
}
|
|
rotatedCorners: number[][]
|
|
|
|
constructor(
|
|
data: Data,
|
|
transformType: TransformCorner | TransformEdge,
|
|
point: number[]
|
|
) {
|
|
super(data)
|
|
this.origin = point
|
|
this.transformType = transformType
|
|
|
|
this.snapshot = getTransformSingleSnapshot(data, transformType)
|
|
|
|
const { minX, minY, maxX, maxY } = this.snapshot.initialShapeBounds
|
|
|
|
this.center = [(minX + maxX) / 2, (minY + maxY) / 2]
|
|
|
|
this.corners = {
|
|
a: [minX, minY],
|
|
b: [maxX, maxY],
|
|
}
|
|
|
|
this.rotatedCorners = getRotatedCorners(
|
|
this.snapshot.initialShapeBounds,
|
|
this.snapshot.initialShape.rotation
|
|
)
|
|
}
|
|
|
|
update(data: Data, point: number[]) {
|
|
const {
|
|
corners: { a, b },
|
|
transformType,
|
|
} = this
|
|
|
|
const {
|
|
boundsRotation,
|
|
initialShapeBounds,
|
|
currentPageId,
|
|
initialShape,
|
|
id,
|
|
} = this.snapshot
|
|
|
|
const { shapes } = data.document.pages[currentPageId]
|
|
|
|
const shape = shapes[id]
|
|
const rotation = shape.rotation
|
|
|
|
// 1. Create a new bounding box.
|
|
// Counter rotate the delta and apply this to the original bounding box.
|
|
|
|
const delta = vec.vec(this.origin, point)
|
|
|
|
/*
|
|
Transforms
|
|
|
|
Corners a and b are the original top-left and bottom-right corners of the
|
|
bounding box. Depending on what the user is dragging, change one or both
|
|
points. To keep things smooth, calculate based by adding the delta (the
|
|
vector between the current point and its original point) to the original
|
|
bounding box values.
|
|
*/
|
|
|
|
const newBoundingBox = getTransformedBoundingBox(
|
|
initialShapeBounds,
|
|
transformType,
|
|
delta,
|
|
shape.rotation
|
|
)
|
|
|
|
// console.log(newBoundingBox)
|
|
|
|
switch (transformType) {
|
|
case TransformEdge.Top: {
|
|
a[1] = initialShapeBounds.minY + delta[1]
|
|
break
|
|
}
|
|
case TransformEdge.Right: {
|
|
b[0] = initialShapeBounds.maxX + delta[0]
|
|
break
|
|
}
|
|
case TransformEdge.Bottom: {
|
|
b[1] = initialShapeBounds.maxY + delta[1]
|
|
break
|
|
}
|
|
case TransformEdge.Left: {
|
|
a[0] = initialShapeBounds.minX + delta[0]
|
|
break
|
|
}
|
|
case TransformCorner.TopLeft: {
|
|
a[0] = initialShapeBounds.minX + delta[0]
|
|
a[1] = initialShapeBounds.minY + delta[1]
|
|
break
|
|
}
|
|
case TransformCorner.TopRight: {
|
|
a[1] = initialShapeBounds.minY + delta[1]
|
|
b[0] = initialShapeBounds.maxX + delta[0]
|
|
break
|
|
}
|
|
case TransformCorner.BottomRight: {
|
|
b[0] = initialShapeBounds.maxX + delta[0]
|
|
b[1] = initialShapeBounds.maxY + delta[1]
|
|
break
|
|
}
|
|
case TransformCorner.BottomLeft: {
|
|
a[0] = initialShapeBounds.minX + delta[0]
|
|
b[1] = initialShapeBounds.maxY + delta[1]
|
|
break
|
|
}
|
|
}
|
|
|
|
// Calculate new common (externior) bounding box
|
|
const newBounds = {
|
|
minX: Math.min(a[0], b[0]),
|
|
minY: Math.min(a[1], b[1]),
|
|
maxX: Math.max(a[0], b[0]),
|
|
maxY: Math.max(a[1], b[1]),
|
|
width: Math.abs(b[0] - a[0]),
|
|
height: Math.abs(b[1] - a[1]),
|
|
}
|
|
|
|
this.isFlippedX = b[0] < a[0]
|
|
this.isFlippedY = b[1] < a[1]
|
|
|
|
const anchor = this.transformType
|
|
|
|
// Pass the new data to the shape's transform utility for mutation.
|
|
// Most shapes should be able to transform using only the bounding box,
|
|
// however some shapes (e.g. those with internal points) will need more
|
|
// data here too.
|
|
|
|
getShapeUtils(shape).transformSingle(shape, newBoundingBox, {
|
|
type: this.transformType,
|
|
initialShape,
|
|
initialShapeBounds,
|
|
initialBounds: initialShapeBounds,
|
|
boundsRotation,
|
|
isFlippedX: this.isFlippedX,
|
|
isFlippedY: this.isFlippedY,
|
|
isSingle: true,
|
|
anchor,
|
|
})
|
|
}
|
|
|
|
cancel(data: Data) {
|
|
const {
|
|
id,
|
|
boundsRotation,
|
|
initialShape,
|
|
initialShapeBounds,
|
|
currentPageId,
|
|
isSingle,
|
|
} = this.snapshot
|
|
|
|
const { shapes } = data.document.pages[currentPageId]
|
|
|
|
// selectedIds.forEach((id) => {
|
|
// const shape = shapes[id]
|
|
|
|
// const { initialShape, initialShapeBounds } = shapeBounds[id]
|
|
|
|
// getShapeUtils(shape).transform(shape, initialShapeBounds, {
|
|
// type: this.transformType,
|
|
// initialShape,
|
|
// initialShapeBounds,
|
|
// initialBounds,
|
|
// boundsRotation,
|
|
// isFlippedX: false,
|
|
// isFlippedY: false,
|
|
// isSingle,
|
|
// anchor: getTransformAnchor(this.transformType, false, false),
|
|
// })
|
|
// })
|
|
}
|
|
|
|
complete(data: Data) {
|
|
commands.transformSingle(
|
|
data,
|
|
this.snapshot,
|
|
getTransformSingleSnapshot(data, this.transformType),
|
|
getTransformAnchor(this.transformType, false, false)
|
|
)
|
|
}
|
|
}
|
|
|
|
export function getTransformSingleSnapshot(
|
|
data: Data,
|
|
transformType: TransformEdge | TransformCorner
|
|
) {
|
|
const {
|
|
document: { pages },
|
|
selectedIds,
|
|
currentPageId,
|
|
} = current(data)
|
|
|
|
const pageShapes = pages[currentPageId].shapes
|
|
|
|
const id = Array.from(selectedIds)[0]
|
|
const shape = pageShapes[id]
|
|
const bounds = getShapeUtils(shape).getBounds(shape)
|
|
|
|
return {
|
|
id,
|
|
currentPageId,
|
|
type: transformType,
|
|
initialShape: shape,
|
|
initialShapeBounds: {
|
|
...bounds,
|
|
nx: 0,
|
|
ny: 0,
|
|
nmx: 1,
|
|
nmy: 1,
|
|
nw: 1,
|
|
nh: 1,
|
|
},
|
|
boundsRotation: shape.rotation,
|
|
isSingle: true,
|
|
}
|
|
}
|
|
|
|
export type TransformSingleSnapshot = ReturnType<
|
|
typeof getTransformSingleSnapshot
|
|
>
|