Improves rotated transforms, cleans up code
This commit is contained in:
parent
c3740cacdd
commit
0c205d1377
9 changed files with 208 additions and 625 deletions
|
@ -5,7 +5,7 @@ import { createShape } from "./index"
|
||||||
import { boundsContained } from "utils/bounds"
|
import { boundsContained } from "utils/bounds"
|
||||||
import { intersectCircleBounds } from "utils/intersections"
|
import { intersectCircleBounds } from "utils/intersections"
|
||||||
import { pointInCircle } from "utils/hitTests"
|
import { pointInCircle } from "utils/hitTests"
|
||||||
import { translateBounds } from "utils/utils"
|
import { getTransformAnchor, translateBounds } from "utils/utils"
|
||||||
|
|
||||||
const circle = createShape<CircleShape>({
|
const circle = createShape<CircleShape>({
|
||||||
boundsCache: new WeakMap([]),
|
boundsCache: new WeakMap([]),
|
||||||
|
@ -94,7 +94,9 @@ const circle = createShape<CircleShape>({
|
||||||
return shape
|
return shape
|
||||||
},
|
},
|
||||||
|
|
||||||
transform(shape, bounds, { anchor }) {
|
transform(shape, bounds, { type, initialShape, scaleX, scaleY }) {
|
||||||
|
const anchor = getTransformAnchor(type, scaleX < 0, scaleY < 0)
|
||||||
|
|
||||||
// Set the new corner or position depending on the anchor
|
// Set the new corner or position depending on the anchor
|
||||||
switch (anchor) {
|
switch (anchor) {
|
||||||
case TransformCorner.TopLeft: {
|
case TransformCorner.TopLeft: {
|
||||||
|
@ -112,7 +114,11 @@ const circle = createShape<CircleShape>({
|
||||||
}
|
}
|
||||||
case TransformCorner.BottomRight: {
|
case TransformCorner.BottomRight: {
|
||||||
shape.radius = Math.min(bounds.width, bounds.height) / 2
|
shape.radius = Math.min(bounds.width, bounds.height) / 2
|
||||||
shape.point = [bounds.minX, bounds.minY]
|
shape.point = [
|
||||||
|
bounds.maxX - shape.radius * 2,
|
||||||
|
bounds.maxY - shape.radius * 2,
|
||||||
|
]
|
||||||
|
break
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case TransformCorner.BottomLeft: {
|
case TransformCorner.BottomLeft: {
|
||||||
|
|
|
@ -61,14 +61,9 @@ export interface ShapeUtility<K extends Shape> {
|
||||||
bounds: Bounds,
|
bounds: Bounds,
|
||||||
info: {
|
info: {
|
||||||
type: TransformEdge | TransformCorner
|
type: TransformEdge | TransformCorner
|
||||||
boundsRotation: number
|
|
||||||
initialShape: K
|
initialShape: K
|
||||||
initialShapeBounds: BoundsSnapshot
|
scaleX: number
|
||||||
initialBounds: Bounds
|
scaleY: number
|
||||||
isFlippedX: boolean
|
|
||||||
isFlippedY: boolean
|
|
||||||
isSingle: boolean
|
|
||||||
anchor: TransformEdge | TransformCorner
|
|
||||||
}
|
}
|
||||||
): K
|
): K
|
||||||
|
|
||||||
|
@ -78,14 +73,9 @@ export interface ShapeUtility<K extends Shape> {
|
||||||
bounds: Bounds,
|
bounds: Bounds,
|
||||||
info: {
|
info: {
|
||||||
type: TransformEdge | TransformCorner
|
type: TransformEdge | TransformCorner
|
||||||
boundsRotation: number
|
|
||||||
initialShape: K
|
initialShape: K
|
||||||
initialShapeBounds: BoundsSnapshot
|
scaleX: number
|
||||||
initialBounds: Bounds
|
scaleY: number
|
||||||
isFlippedX: boolean
|
|
||||||
isFlippedY: boolean
|
|
||||||
isSingle: boolean
|
|
||||||
anchor: TransformEdge | TransformCorner
|
|
||||||
}
|
}
|
||||||
): K
|
): K
|
||||||
|
|
||||||
|
|
|
@ -99,142 +99,34 @@ const rectangle = createShape<RectangleShape>({
|
||||||
return shape
|
return shape
|
||||||
},
|
},
|
||||||
|
|
||||||
transform(
|
transform(shape, bounds, { initialShape, scaleX, scaleY }) {
|
||||||
shape,
|
if (shape.rotation === 0) {
|
||||||
shapeBounds,
|
shape.size = [bounds.width, bounds.height]
|
||||||
{ initialShape, isSingle, initialShapeBounds, isFlippedX, isFlippedY }
|
shape.point = [bounds.minX, bounds.minY]
|
||||||
) {
|
|
||||||
if (shape.rotation === 0 || isSingle) {
|
|
||||||
shape.size = [shapeBounds.width, shapeBounds.height]
|
|
||||||
shape.point = [shapeBounds.minX, shapeBounds.minY]
|
|
||||||
} else {
|
} else {
|
||||||
|
// Center shape in resized bounds
|
||||||
shape.size = vec.mul(
|
shape.size = vec.mul(
|
||||||
initialShape.size,
|
initialShape.size,
|
||||||
Math.min(
|
Math.min(Math.abs(scaleX), Math.abs(scaleY))
|
||||||
shapeBounds.width / initialShapeBounds.width,
|
|
||||||
shapeBounds.height / initialShapeBounds.height
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const newCenter = [
|
shape.point = vec.sub(
|
||||||
shapeBounds.minX + shapeBounds.width / 2,
|
vec.med([bounds.minX, bounds.minY], [bounds.maxX, bounds.maxY]),
|
||||||
shapeBounds.minY + shapeBounds.height / 2,
|
vec.div(shape.size, 2)
|
||||||
]
|
)
|
||||||
|
|
||||||
shape.point = vec.sub(newCenter, vec.div(shape.size, 2))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rotation for flipped shapes
|
// Set rotation for flipped shapes
|
||||||
|
|
||||||
shape.rotation = initialShape.rotation
|
shape.rotation = initialShape.rotation
|
||||||
|
if (scaleX < 0) shape.rotation *= -1
|
||||||
if (isFlippedX) {
|
if (scaleY < 0) shape.rotation *= -1
|
||||||
shape.rotation *= -1
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isFlippedY) {
|
|
||||||
shape.rotation *= -1
|
|
||||||
}
|
|
||||||
|
|
||||||
return shape
|
return shape
|
||||||
},
|
},
|
||||||
|
|
||||||
transformSingle(
|
transformSingle(shape, bounds) {
|
||||||
shape,
|
|
||||||
bounds,
|
|
||||||
{ initialShape, initialShapeBounds, anchor, isFlippedY, isFlippedX }
|
|
||||||
) {
|
|
||||||
shape.size = [bounds.width, bounds.height]
|
shape.size = [bounds.width, bounds.height]
|
||||||
shape.point = [bounds.minX, bounds.minY]
|
shape.point = [bounds.minX, bounds.minY]
|
||||||
|
|
||||||
// const prevCorners = getRotatedCorners(
|
|
||||||
// initialShapeBounds,
|
|
||||||
// initialShape.rotation
|
|
||||||
// )
|
|
||||||
|
|
||||||
// let currCorners = getRotatedCorners(this.getBounds(shape), shape.rotation)
|
|
||||||
|
|
||||||
// if (isFlippedX) {
|
|
||||||
// let t = currCorners[3]
|
|
||||||
// currCorners[3] = currCorners[2]
|
|
||||||
// currCorners[2] = t
|
|
||||||
|
|
||||||
// t = currCorners[0]
|
|
||||||
// currCorners[0] = currCorners[1]
|
|
||||||
// currCorners[1] = t
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (isFlippedY) {
|
|
||||||
// let t = currCorners[3]
|
|
||||||
// currCorners[3] = currCorners[0]
|
|
||||||
// currCorners[0] = t
|
|
||||||
|
|
||||||
// t = currCorners[2]
|
|
||||||
// currCorners[2] = currCorners[1]
|
|
||||||
// currCorners[1] = t
|
|
||||||
// }
|
|
||||||
|
|
||||||
// switch (anchor) {
|
|
||||||
// case TransformCorner.TopLeft: {
|
|
||||||
// shape.point = vec.sub(
|
|
||||||
// shape.point,
|
|
||||||
// vec.sub(currCorners[2], prevCorners[2])
|
|
||||||
// )
|
|
||||||
// break
|
|
||||||
// }
|
|
||||||
// case TransformCorner.TopRight: {
|
|
||||||
// shape.point = vec.sub(
|
|
||||||
// shape.point,
|
|
||||||
// vec.sub(currCorners[3], prevCorners[3])
|
|
||||||
// )
|
|
||||||
// break
|
|
||||||
// }
|
|
||||||
// case TransformCorner.BottomRight: {
|
|
||||||
// shape.point = vec.sub(
|
|
||||||
// shape.point,
|
|
||||||
// vec.sub(currCorners[0], prevCorners[0])
|
|
||||||
// )
|
|
||||||
// break
|
|
||||||
// }
|
|
||||||
// case TransformCorner.BottomLeft: {
|
|
||||||
// shape.point = vec.sub(
|
|
||||||
// shape.point,
|
|
||||||
// vec.sub(currCorners[1], prevCorners[1])
|
|
||||||
// )
|
|
||||||
// break
|
|
||||||
// }
|
|
||||||
// case TransformEdge.Top: {
|
|
||||||
// shape.point = vec.sub(
|
|
||||||
// shape.point,
|
|
||||||
// vec.sub(currCorners[3], prevCorners[3])
|
|
||||||
// )
|
|
||||||
// break
|
|
||||||
// }
|
|
||||||
// case TransformEdge.Right: {
|
|
||||||
// shape.point = vec.sub(
|
|
||||||
// shape.point,
|
|
||||||
// vec.sub(currCorners[3], prevCorners[3])
|
|
||||||
// )
|
|
||||||
// break
|
|
||||||
// }
|
|
||||||
// case TransformEdge.Bottom: {
|
|
||||||
// shape.point = vec.sub(
|
|
||||||
// shape.point,
|
|
||||||
// vec.sub(currCorners[0], prevCorners[0])
|
|
||||||
// )
|
|
||||||
// break
|
|
||||||
// }
|
|
||||||
// case TransformEdge.Left: {
|
|
||||||
// shape.point = vec.sub(
|
|
||||||
// shape.point,
|
|
||||||
// vec.sub(currCorners[2], prevCorners[2])
|
|
||||||
// )
|
|
||||||
// break
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// console.log(shape.point, shape.size)
|
|
||||||
|
|
||||||
return shape
|
return shape
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -8,63 +8,38 @@ export default function transformSingleCommand(
|
||||||
data: Data,
|
data: Data,
|
||||||
before: TransformSingleSnapshot,
|
before: TransformSingleSnapshot,
|
||||||
after: TransformSingleSnapshot,
|
after: TransformSingleSnapshot,
|
||||||
anchor: TransformCorner | TransformEdge
|
scaleX: number,
|
||||||
|
scaleY: number
|
||||||
) {
|
) {
|
||||||
history.execute(
|
history.execute(
|
||||||
data,
|
data,
|
||||||
new Command({
|
new Command({
|
||||||
name: "translate_shapes",
|
name: "transform_single_shape",
|
||||||
category: "canvas",
|
category: "canvas",
|
||||||
do(data) {
|
do(data) {
|
||||||
const {
|
const { id, currentPageId, type, initialShape, initialShapeBounds } =
|
||||||
|
after
|
||||||
|
|
||||||
|
const shape = data.document.pages[currentPageId].shapes[id]
|
||||||
|
|
||||||
|
getShapeUtils(shape).transformSingle(shape, initialShapeBounds, {
|
||||||
type,
|
type,
|
||||||
initialShape,
|
initialShape,
|
||||||
initialShapeBounds,
|
scaleX,
|
||||||
currentPageId,
|
scaleY,
|
||||||
id,
|
|
||||||
boundsRotation,
|
|
||||||
} = after
|
|
||||||
|
|
||||||
const { shapes } = data.document.pages[currentPageId]
|
|
||||||
|
|
||||||
const shape = shapes[id]
|
|
||||||
|
|
||||||
getShapeUtils(shape).transform(shape, initialShapeBounds, {
|
|
||||||
type,
|
|
||||||
initialShape,
|
|
||||||
initialShapeBounds,
|
|
||||||
initialBounds: initialShapeBounds,
|
|
||||||
boundsRotation,
|
|
||||||
isFlippedX: false,
|
|
||||||
isFlippedY: false,
|
|
||||||
isSingle: false,
|
|
||||||
anchor,
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
undo(data) {
|
undo(data) {
|
||||||
const {
|
const { id, currentPageId, type, initialShape, initialShapeBounds } =
|
||||||
type,
|
before
|
||||||
initialShape,
|
|
||||||
initialShapeBounds,
|
|
||||||
currentPageId,
|
|
||||||
id,
|
|
||||||
boundsRotation,
|
|
||||||
} = before
|
|
||||||
|
|
||||||
const { shapes } = data.document.pages[currentPageId]
|
const shape = data.document.pages[currentPageId].shapes[id]
|
||||||
|
|
||||||
const shape = shapes[id]
|
|
||||||
|
|
||||||
getShapeUtils(shape).transform(shape, initialShapeBounds, {
|
getShapeUtils(shape).transform(shape, initialShapeBounds, {
|
||||||
type,
|
type,
|
||||||
initialShape,
|
initialShape: after.initialShape,
|
||||||
initialShapeBounds,
|
scaleX: 1,
|
||||||
initialBounds: initialShapeBounds,
|
scaleY: 1,
|
||||||
boundsRotation,
|
|
||||||
isFlippedX: false,
|
|
||||||
isFlippedY: false,
|
|
||||||
isSingle: false,
|
|
||||||
anchor,
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -8,7 +8,8 @@ export default function transformCommand(
|
||||||
data: Data,
|
data: Data,
|
||||||
before: TransformSnapshot,
|
before: TransformSnapshot,
|
||||||
after: TransformSnapshot,
|
after: TransformSnapshot,
|
||||||
anchor: TransformCorner | TransformEdge
|
scaleX: number,
|
||||||
|
scaleY: number
|
||||||
) {
|
) {
|
||||||
history.execute(
|
history.execute(
|
||||||
data,
|
data,
|
||||||
|
@ -16,60 +17,32 @@ export default function transformCommand(
|
||||||
name: "translate_shapes",
|
name: "translate_shapes",
|
||||||
category: "canvas",
|
category: "canvas",
|
||||||
do(data) {
|
do(data) {
|
||||||
const {
|
const { type, currentPageId, selectedIds } = after
|
||||||
type,
|
|
||||||
shapeBounds,
|
|
||||||
initialBounds,
|
|
||||||
currentPageId,
|
|
||||||
selectedIds,
|
|
||||||
boundsRotation,
|
|
||||||
} = after
|
|
||||||
|
|
||||||
const { shapes } = data.document.pages[currentPageId]
|
|
||||||
|
|
||||||
selectedIds.forEach((id) => {
|
selectedIds.forEach((id) => {
|
||||||
const { initialShape, initialShapeBounds } = shapeBounds[id]
|
const { initialShape, initialShapeBounds } = after.shapeBounds[id]
|
||||||
const shape = shapes[id]
|
const shape = data.document.pages[currentPageId].shapes[id]
|
||||||
|
|
||||||
getShapeUtils(shape).transform(shape, initialShapeBounds, {
|
getShapeUtils(shape).transform(shape, initialShapeBounds, {
|
||||||
type,
|
type,
|
||||||
initialShape,
|
initialShape,
|
||||||
initialShapeBounds,
|
scaleX: 1,
|
||||||
initialBounds,
|
scaleY: 1,
|
||||||
boundsRotation,
|
|
||||||
isFlippedX: false,
|
|
||||||
isFlippedY: false,
|
|
||||||
isSingle: false,
|
|
||||||
anchor,
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
undo(data) {
|
undo(data) {
|
||||||
const {
|
const { type, currentPageId, selectedIds } = before
|
||||||
type,
|
|
||||||
shapeBounds,
|
|
||||||
initialBounds,
|
|
||||||
currentPageId,
|
|
||||||
selectedIds,
|
|
||||||
boundsRotation,
|
|
||||||
} = before
|
|
||||||
|
|
||||||
const { shapes } = data.document.pages[currentPageId]
|
|
||||||
|
|
||||||
selectedIds.forEach((id) => {
|
selectedIds.forEach((id) => {
|
||||||
const { initialShape, initialShapeBounds } = shapeBounds[id]
|
const { initialShape, initialShapeBounds } = before.shapeBounds[id]
|
||||||
const shape = shapes[id]
|
const shape = data.document.pages[currentPageId].shapes[id]
|
||||||
|
|
||||||
getShapeUtils(shape).transform(shape, initialShapeBounds, {
|
getShapeUtils(shape).transform(shape, initialShapeBounds, {
|
||||||
type,
|
type,
|
||||||
initialShape,
|
initialShape,
|
||||||
initialShapeBounds,
|
scaleX: 1,
|
||||||
initialBounds,
|
scaleY: 1,
|
||||||
boundsRotation,
|
|
||||||
isFlippedX: false,
|
|
||||||
isFlippedY: false,
|
|
||||||
isSingle: false,
|
|
||||||
anchor: type,
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,28 +1,21 @@
|
||||||
import {
|
import { Data, TransformEdge, TransformCorner } from "types"
|
||||||
Data,
|
|
||||||
TransformEdge,
|
|
||||||
TransformCorner,
|
|
||||||
Bounds,
|
|
||||||
BoundsSnapshot,
|
|
||||||
} from "types"
|
|
||||||
import * as vec from "utils/vec"
|
import * as vec from "utils/vec"
|
||||||
import BaseSession from "./base-session"
|
import BaseSession from "./base-session"
|
||||||
import commands from "state/commands"
|
import commands from "state/commands"
|
||||||
import { current } from "immer"
|
import { current } from "immer"
|
||||||
import { getShapeUtils } from "lib/shapes"
|
import { getShapeUtils } from "lib/shapes"
|
||||||
import { getCommonBounds, getTransformAnchor } from "utils/utils"
|
import {
|
||||||
|
getCommonBounds,
|
||||||
|
getRelativeTransformedBoundingBox,
|
||||||
|
getTransformedBoundingBox,
|
||||||
|
} from "utils/utils"
|
||||||
|
|
||||||
export default class TransformSession extends BaseSession {
|
export default class TransformSession extends BaseSession {
|
||||||
delta = [0, 0]
|
scaleX = 1
|
||||||
isFlippedX = false
|
scaleY = 1
|
||||||
isFlippedY = false
|
|
||||||
transformType: TransformEdge | TransformCorner
|
transformType: TransformEdge | TransformCorner
|
||||||
origin: number[]
|
origin: number[]
|
||||||
snapshot: TransformSnapshot
|
snapshot: TransformSnapshot
|
||||||
corners: {
|
|
||||||
a: number[]
|
|
||||||
b: number[]
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
data: Data,
|
data: Data,
|
||||||
|
@ -32,196 +25,62 @@ export default class TransformSession extends BaseSession {
|
||||||
super(data)
|
super(data)
|
||||||
this.origin = point
|
this.origin = point
|
||||||
this.transformType = transformType
|
this.transformType = transformType
|
||||||
|
|
||||||
// if (data.selectedIds.size === 1) {
|
|
||||||
// const shape =
|
|
||||||
// data.document.pages[data.currentPageId].shapes[
|
|
||||||
// Array.from(data.selectedIds.values())[0]
|
|
||||||
// ]
|
|
||||||
|
|
||||||
// if (shape.rotation > 0) {
|
|
||||||
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
this.snapshot = getTransformSnapshot(data, transformType)
|
this.snapshot = getTransformSnapshot(data, transformType)
|
||||||
|
|
||||||
const { minX, minY, maxX, maxY } = this.snapshot.initialBounds
|
|
||||||
|
|
||||||
this.corners = {
|
|
||||||
a: [minX, minY],
|
|
||||||
b: [maxX, maxY],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
update(data: Data, point: number[]) {
|
update(data: Data, point: number[]) {
|
||||||
const {
|
const { transformType } = this
|
||||||
corners: { a, b },
|
|
||||||
transformType,
|
|
||||||
} = this
|
|
||||||
|
|
||||||
const {
|
const { currentPageId, selectedIds, shapeBounds, initialBounds } =
|
||||||
boundsRotation,
|
this.snapshot
|
||||||
shapeBounds,
|
|
||||||
|
const newBoundingBox = getTransformedBoundingBox(
|
||||||
initialBounds,
|
initialBounds,
|
||||||
currentPageId,
|
transformType,
|
||||||
selectedIds,
|
vec.vec(this.origin, point),
|
||||||
} = this.snapshot
|
data.boundsRotation
|
||||||
|
)
|
||||||
|
|
||||||
const { shapes } = data.document.pages[currentPageId]
|
this.scaleX = newBoundingBox.scaleX
|
||||||
|
this.scaleY = newBoundingBox.scaleY
|
||||||
let delta = vec.vec(this.origin, point)
|
|
||||||
|
|
||||||
// if (isSingle) {
|
|
||||||
// const center = [
|
|
||||||
// initialBounds.minX + initialBounds.width / 2,
|
|
||||||
// initialBounds.minY + initialBounds.height / 2,
|
|
||||||
// ]
|
|
||||||
|
|
||||||
// const rotation = shapes[Array.from(selectedIds.values())[0]].rotation
|
|
||||||
|
|
||||||
// const rotatedOrigin = vec.rotWith(this.origin, center, -rotation)
|
|
||||||
// const rotatedPoint = vec.rotWith(point, center, -rotation)
|
|
||||||
|
|
||||||
// delta = vec.vec(rotatedOrigin, rotatedPoint)
|
|
||||||
// }
|
|
||||||
|
|
||||||
/*
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
switch (transformType) {
|
|
||||||
case TransformEdge.Top: {
|
|
||||||
a[1] = initialBounds.minY + delta[1]
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case TransformEdge.Right: {
|
|
||||||
b[0] = initialBounds.maxX + delta[0]
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case TransformEdge.Bottom: {
|
|
||||||
b[1] = initialBounds.maxY + delta[1]
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case TransformEdge.Left: {
|
|
||||||
a[0] = initialBounds.minX + delta[0]
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case TransformCorner.TopLeft: {
|
|
||||||
a[0] = initialBounds.minX + delta[0]
|
|
||||||
a[1] = initialBounds.minY + delta[1]
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case TransformCorner.TopRight: {
|
|
||||||
a[1] = initialBounds.minY + delta[1]
|
|
||||||
b[0] = initialBounds.maxX + delta[0]
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case TransformCorner.BottomRight: {
|
|
||||||
b[0] = initialBounds.maxX + delta[0]
|
|
||||||
b[1] = initialBounds.maxY + delta[1]
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case TransformCorner.BottomLeft: {
|
|
||||||
a[0] = initialBounds.minX + delta[0]
|
|
||||||
b[1] = initialBounds.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]
|
|
||||||
|
|
||||||
// Now work backward to calculate a new bounding box for each of the shapes.
|
// Now work backward to calculate a new bounding box for each of the shapes.
|
||||||
|
|
||||||
selectedIds.forEach((id) => {
|
selectedIds.forEach((id) => {
|
||||||
const { initialShape, initialShapeBounds } = shapeBounds[id]
|
const { initialShape, initialShapeBounds } = shapeBounds[id]
|
||||||
const { nx, nmx, nw, ny, nmy, nh } = initialShapeBounds
|
|
||||||
const shape = shapes[id]
|
|
||||||
|
|
||||||
const minX =
|
const newShapeBounds = getRelativeTransformedBoundingBox(
|
||||||
newBounds.minX + (this.isFlippedX ? nmx : nx) * newBounds.width
|
newBoundingBox,
|
||||||
const minY =
|
initialBounds,
|
||||||
newBounds.minY + (this.isFlippedY ? nmy : ny) * newBounds.height
|
initialShapeBounds,
|
||||||
const width = nw * newBounds.width
|
this.scaleX < 0,
|
||||||
const height = nh * newBounds.height
|
this.scaleY < 0
|
||||||
|
)
|
||||||
|
|
||||||
const newShapeBounds = {
|
const shape = data.document.pages[currentPageId].shapes[id]
|
||||||
minX,
|
|
||||||
minY,
|
|
||||||
maxX: minX + width,
|
|
||||||
maxY: minY + height,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
isFlippedX: this.isFlippedX,
|
|
||||||
isFlippedY: this.isFlippedY,
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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).transform(shape, newShapeBounds, {
|
getShapeUtils(shape).transform(shape, newShapeBounds, {
|
||||||
type: this.transformType,
|
type: this.transformType,
|
||||||
initialShape,
|
initialShape,
|
||||||
initialShapeBounds,
|
scaleX: this.scaleX,
|
||||||
initialBounds,
|
scaleY: this.scaleY,
|
||||||
boundsRotation,
|
|
||||||
isFlippedX: this.isFlippedX,
|
|
||||||
isFlippedY: this.isFlippedY,
|
|
||||||
isSingle: false,
|
|
||||||
anchor: getTransformAnchor(
|
|
||||||
this.transformType,
|
|
||||||
this.isFlippedX,
|
|
||||||
this.isFlippedY
|
|
||||||
),
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
cancel(data: Data) {
|
cancel(data: Data) {
|
||||||
const {
|
const { currentPageId, selectedIds, shapeBounds } = this.snapshot
|
||||||
shapeBounds,
|
|
||||||
boundsRotation,
|
|
||||||
initialBounds,
|
|
||||||
currentPageId,
|
|
||||||
selectedIds,
|
|
||||||
} = this.snapshot
|
|
||||||
|
|
||||||
const { shapes } = data.document.pages[currentPageId]
|
|
||||||
|
|
||||||
selectedIds.forEach((id) => {
|
selectedIds.forEach((id) => {
|
||||||
const shape = shapes[id]
|
const shape = data.document.pages[currentPageId].shapes[id]
|
||||||
|
|
||||||
const { initialShape, initialShapeBounds } = shapeBounds[id]
|
const { initialShape, initialShapeBounds } = shapeBounds[id]
|
||||||
|
|
||||||
getShapeUtils(shape).transform(shape, initialShapeBounds, {
|
getShapeUtils(shape).transform(shape, initialShapeBounds, {
|
||||||
type: this.transformType,
|
type: this.transformType,
|
||||||
initialShape,
|
initialShape,
|
||||||
initialShapeBounds,
|
scaleX: 1,
|
||||||
initialBounds,
|
scaleY: 1,
|
||||||
boundsRotation,
|
|
||||||
isFlippedX: false,
|
|
||||||
isFlippedY: false,
|
|
||||||
isSingle: false,
|
|
||||||
anchor: getTransformAnchor(this.transformType, false, false),
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -231,7 +90,8 @@ export default class TransformSession extends BaseSession {
|
||||||
data,
|
data,
|
||||||
this.snapshot,
|
this.snapshot,
|
||||||
getTransformSnapshot(data, this.transformType),
|
getTransformSnapshot(data, this.transformType),
|
||||||
getTransformAnchor(this.transformType, false, false)
|
this.scaleX,
|
||||||
|
this.scaleY
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -244,7 +104,6 @@ export function getTransformSnapshot(
|
||||||
document: { pages },
|
document: { pages },
|
||||||
selectedIds,
|
selectedIds,
|
||||||
currentPageId,
|
currentPageId,
|
||||||
boundsRotation,
|
|
||||||
} = current(data)
|
} = current(data)
|
||||||
|
|
||||||
const pageShapes = pages[currentPageId].shapes
|
const pageShapes = pages[currentPageId].shapes
|
||||||
|
@ -263,27 +122,17 @@ export function getTransformSnapshot(
|
||||||
// Return a mapping of shapes to bounds together with the relative
|
// Return a mapping of shapes to bounds together with the relative
|
||||||
// positions of the shape's bounds within the common bounds shape.
|
// positions of the shape's bounds within the common bounds shape.
|
||||||
return {
|
return {
|
||||||
currentPageId,
|
|
||||||
type: transformType,
|
type: transformType,
|
||||||
initialBounds: bounds,
|
currentPageId,
|
||||||
boundsRotation,
|
|
||||||
selectedIds: new Set(selectedIds),
|
selectedIds: new Set(selectedIds),
|
||||||
|
initialBounds: bounds,
|
||||||
shapeBounds: Object.fromEntries(
|
shapeBounds: Object.fromEntries(
|
||||||
Array.from(selectedIds.values()).map((id) => {
|
Array.from(selectedIds.values()).map((id) => {
|
||||||
const { minX, minY, width, height } = shapesBounds[id]
|
|
||||||
return [
|
return [
|
||||||
id,
|
id,
|
||||||
{
|
{
|
||||||
initialShape: pageShapes[id],
|
initialShape: pageShapes[id],
|
||||||
initialShapeBounds: {
|
initialShapeBounds: shapesBounds[id],
|
||||||
...shapesBounds[id],
|
|
||||||
nx: (minX - bounds.minX) / bounds.width,
|
|
||||||
ny: (minY - bounds.minY) / bounds.height,
|
|
||||||
nmx: 1 - (minX + width - bounds.minX) / bounds.width,
|
|
||||||
nmy: 1 - (minY + height - bounds.minY) / bounds.height,
|
|
||||||
nw: width / bounds.width,
|
|
||||||
nh: height / bounds.height,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
|
@ -13,17 +13,11 @@ import {
|
||||||
|
|
||||||
export default class TransformSingleSession extends BaseSession {
|
export default class TransformSingleSession extends BaseSession {
|
||||||
delta = [0, 0]
|
delta = [0, 0]
|
||||||
isFlippedX = false
|
scaleX = 1
|
||||||
isFlippedY = false
|
scaleY = 1
|
||||||
transformType: TransformEdge | TransformCorner
|
transformType: TransformEdge | TransformCorner
|
||||||
origin: number[]
|
|
||||||
center: number[]
|
|
||||||
snapshot: TransformSingleSnapshot
|
snapshot: TransformSingleSnapshot
|
||||||
corners: {
|
origin: number[]
|
||||||
a: number[]
|
|
||||||
b: number[]
|
|
||||||
}
|
|
||||||
rotatedCorners: number[][]
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
data: Data,
|
data: Data,
|
||||||
|
@ -33,168 +27,49 @@ export default class TransformSingleSession extends BaseSession {
|
||||||
super(data)
|
super(data)
|
||||||
this.origin = point
|
this.origin = point
|
||||||
this.transformType = transformType
|
this.transformType = transformType
|
||||||
|
|
||||||
this.snapshot = getTransformSingleSnapshot(data, 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[]) {
|
update(data: Data, point: number[]) {
|
||||||
const {
|
const { transformType } = this
|
||||||
corners: { a, b },
|
|
||||||
transformType,
|
|
||||||
} = this
|
|
||||||
|
|
||||||
const {
|
const { initialShapeBounds, currentPageId, initialShape, id } =
|
||||||
boundsRotation,
|
this.snapshot
|
||||||
initialShapeBounds,
|
|
||||||
currentPageId,
|
|
||||||
initialShape,
|
|
||||||
id,
|
|
||||||
} = this.snapshot
|
|
||||||
|
|
||||||
const { shapes } = data.document.pages[currentPageId]
|
const shape = data.document.pages[currentPageId].shapes[id]
|
||||||
|
|
||||||
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(
|
const newBoundingBox = getTransformedBoundingBox(
|
||||||
initialShapeBounds,
|
initialShapeBounds,
|
||||||
transformType,
|
transformType,
|
||||||
delta,
|
vec.vec(this.origin, point),
|
||||||
shape.rotation
|
shape.rotation
|
||||||
)
|
)
|
||||||
|
|
||||||
// console.log(newBoundingBox)
|
this.scaleX = newBoundingBox.scaleX
|
||||||
|
this.scaleY = newBoundingBox.scaleY
|
||||||
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, {
|
getShapeUtils(shape).transformSingle(shape, newBoundingBox, {
|
||||||
type: this.transformType,
|
|
||||||
initialShape,
|
initialShape,
|
||||||
initialShapeBounds,
|
type: this.transformType,
|
||||||
initialBounds: initialShapeBounds,
|
scaleX: this.scaleX,
|
||||||
boundsRotation,
|
scaleY: this.scaleY,
|
||||||
isFlippedX: this.isFlippedX,
|
|
||||||
isFlippedY: this.isFlippedY,
|
|
||||||
isSingle: true,
|
|
||||||
anchor,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
cancel(data: Data) {
|
cancel(data: Data) {
|
||||||
const {
|
const { id, initialShape, initialShapeBounds, currentPageId } =
|
||||||
id,
|
this.snapshot
|
||||||
boundsRotation,
|
|
||||||
initialShape,
|
|
||||||
initialShapeBounds,
|
|
||||||
currentPageId,
|
|
||||||
isSingle,
|
|
||||||
} = this.snapshot
|
|
||||||
|
|
||||||
const { shapes } = data.document.pages[currentPageId]
|
const { shapes } = data.document.pages[currentPageId]
|
||||||
|
|
||||||
// selectedIds.forEach((id) => {
|
const shape = shapes[id]
|
||||||
// const shape = shapes[id]
|
|
||||||
|
|
||||||
// const { initialShape, initialShapeBounds } = shapeBounds[id]
|
getShapeUtils(shape).transform(shape, initialShapeBounds, {
|
||||||
|
initialShape,
|
||||||
// getShapeUtils(shape).transform(shape, initialShapeBounds, {
|
type: this.transformType,
|
||||||
// type: this.transformType,
|
scaleX: this.scaleX,
|
||||||
// initialShape,
|
scaleY: this.scaleY,
|
||||||
// initialShapeBounds,
|
})
|
||||||
// initialBounds,
|
|
||||||
// boundsRotation,
|
|
||||||
// isFlippedX: false,
|
|
||||||
// isFlippedY: false,
|
|
||||||
// isSingle,
|
|
||||||
// anchor: getTransformAnchor(this.transformType, false, false),
|
|
||||||
// })
|
|
||||||
// })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
complete(data: Data) {
|
complete(data: Data) {
|
||||||
|
@ -202,7 +77,8 @@ export default class TransformSingleSession extends BaseSession {
|
||||||
data,
|
data,
|
||||||
this.snapshot,
|
this.snapshot,
|
||||||
getTransformSingleSnapshot(data, this.transformType),
|
getTransformSingleSnapshot(data, this.transformType),
|
||||||
getTransformAnchor(this.transformType, false, false)
|
this.scaleX,
|
||||||
|
this.scaleY
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -217,10 +93,8 @@ export function getTransformSingleSnapshot(
|
||||||
currentPageId,
|
currentPageId,
|
||||||
} = current(data)
|
} = current(data)
|
||||||
|
|
||||||
const pageShapes = pages[currentPageId].shapes
|
|
||||||
|
|
||||||
const id = Array.from(selectedIds)[0]
|
const id = Array.from(selectedIds)[0]
|
||||||
const shape = pageShapes[id]
|
const shape = pages[currentPageId].shapes[id]
|
||||||
const bounds = getShapeUtils(shape).getBounds(shape)
|
const bounds = getShapeUtils(shape).getBounds(shape)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -228,17 +102,7 @@ export function getTransformSingleSnapshot(
|
||||||
currentPageId,
|
currentPageId,
|
||||||
type: transformType,
|
type: transformType,
|
||||||
initialShape: shape,
|
initialShape: shape,
|
||||||
initialShapeBounds: {
|
initialShapeBounds: bounds,
|
||||||
...bounds,
|
|
||||||
nx: 0,
|
|
||||||
ny: 0,
|
|
||||||
nmx: 1,
|
|
||||||
nmy: 1,
|
|
||||||
nw: 1,
|
|
||||||
nh: 1,
|
|
||||||
},
|
|
||||||
boundsRotation: shape.rotation,
|
|
||||||
isSingle: true,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -541,7 +541,7 @@ const state = createState({
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
startDrawTransformSession(data, payload: PointerInfo) {
|
startDrawTransformSession(data, payload: PointerInfo) {
|
||||||
session = new Sessions.TransformSession(
|
session = new Sessions.TransformSingleSession(
|
||||||
data,
|
data,
|
||||||
TransformCorner.BottomRight,
|
TransformCorner.BottomRight,
|
||||||
screenToWorld(payload.point, data)
|
screenToWorld(payload.point, data)
|
||||||
|
|
140
utils/utils.ts
140
utils/utils.ts
|
@ -915,6 +915,8 @@ export function getTransformAnchor(
|
||||||
anchor = TransformCorner.TopRight
|
anchor = TransformCorner.TopRight
|
||||||
} else if (isFlippedY) {
|
} else if (isFlippedY) {
|
||||||
anchor = TransformCorner.BottomLeft
|
anchor = TransformCorner.BottomLeft
|
||||||
|
} else {
|
||||||
|
anchor = TransformCorner.BottomRight
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -925,6 +927,8 @@ export function getTransformAnchor(
|
||||||
anchor = TransformCorner.TopLeft
|
anchor = TransformCorner.TopLeft
|
||||||
} else if (isFlippedY) {
|
} else if (isFlippedY) {
|
||||||
anchor = TransformCorner.BottomRight
|
anchor = TransformCorner.BottomRight
|
||||||
|
} else {
|
||||||
|
anchor = TransformCorner.BottomLeft
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -935,6 +939,8 @@ export function getTransformAnchor(
|
||||||
anchor = TransformCorner.BottomLeft
|
anchor = TransformCorner.BottomLeft
|
||||||
} else if (isFlippedY) {
|
} else if (isFlippedY) {
|
||||||
anchor = TransformCorner.TopRight
|
anchor = TransformCorner.TopRight
|
||||||
|
} else {
|
||||||
|
anchor = TransformCorner.TopLeft
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -945,6 +951,8 @@ export function getTransformAnchor(
|
||||||
anchor = TransformCorner.BottomRight
|
anchor = TransformCorner.BottomRight
|
||||||
} else if (isFlippedY) {
|
} else if (isFlippedY) {
|
||||||
anchor = TransformCorner.TopLeft
|
anchor = TransformCorner.TopLeft
|
||||||
|
} else {
|
||||||
|
anchor = TransformCorner.TopRight
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -1052,54 +1060,34 @@ export function getTransformedBoundingBox(
|
||||||
const [dx, dy] = vec.rot(delta, -rotation)
|
const [dx, dy] = vec.rot(delta, -rotation)
|
||||||
|
|
||||||
// Depending on the dragging handle (an edge or corner of
|
// Depending on the dragging handle (an edge or corner of
|
||||||
// the bounding box), find the anchor corner and use the delta
|
// the bounding box), use the delta to adjust the result's corners.
|
||||||
// to adjust the result's corners.
|
|
||||||
|
|
||||||
let anchor: TransformCorner | TransformEdge
|
|
||||||
|
|
||||||
switch (handle) {
|
switch (handle) {
|
||||||
case TransformEdge.Top: {
|
case TransformEdge.Top:
|
||||||
anchor = TransformCorner.BottomRight
|
case TransformCorner.TopLeft:
|
||||||
by0 += dy
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case TransformEdge.Right: {
|
|
||||||
anchor = TransformCorner.TopLeft
|
|
||||||
bx1 += dx
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case TransformEdge.Bottom: {
|
|
||||||
anchor = TransformCorner.TopLeft
|
|
||||||
by1 += dy
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case TransformEdge.Left: {
|
|
||||||
anchor = TransformCorner.BottomRight
|
|
||||||
bx0 += dx
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case TransformCorner.TopLeft: {
|
|
||||||
anchor = TransformCorner.BottomRight
|
|
||||||
bx0 += dx
|
|
||||||
by0 += dy
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case TransformCorner.TopRight: {
|
case TransformCorner.TopRight: {
|
||||||
anchor = TransformCorner.BottomLeft
|
|
||||||
bx1 += dx
|
|
||||||
by0 += dy
|
by0 += dy
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
case TransformEdge.Bottom:
|
||||||
|
case TransformCorner.BottomLeft:
|
||||||
case TransformCorner.BottomRight: {
|
case TransformCorner.BottomRight: {
|
||||||
anchor = TransformCorner.TopLeft
|
|
||||||
bx1 += dx
|
|
||||||
by1 += dy
|
by1 += dy
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (handle) {
|
||||||
|
case TransformEdge.Left:
|
||||||
|
case TransformCorner.TopLeft:
|
||||||
case TransformCorner.BottomLeft: {
|
case TransformCorner.BottomLeft: {
|
||||||
anchor = TransformCorner.TopRight
|
|
||||||
bx0 += dx
|
bx0 += dx
|
||||||
by1 += dy
|
break
|
||||||
|
}
|
||||||
|
case TransformEdge.Right:
|
||||||
|
case TransformCorner.TopRight:
|
||||||
|
case TransformCorner.BottomRight: {
|
||||||
|
bx1 += dx
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1115,35 +1103,39 @@ export function getTransformedBoundingBox(
|
||||||
const c0 = vec.med([ax0, ay0], [ax1, ay1])
|
const c0 = vec.med([ax0, ay0], [ax1, ay1])
|
||||||
const c1 = vec.med([bx0, by0], [bx1, by1])
|
const c1 = vec.med([bx0, by0], [bx1, by1])
|
||||||
|
|
||||||
switch (anchor) {
|
switch (handle) {
|
||||||
case TransformCorner.TopLeft: {
|
case TransformCorner.TopLeft:
|
||||||
cv = vec.sub(
|
case TransformEdge.Top:
|
||||||
vec.rotWith([bx0, by0], c1, rotation),
|
case TransformEdge.Left: {
|
||||||
vec.rotWith([ax0, ay0], c0, rotation)
|
|
||||||
)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case TransformCorner.TopRight: {
|
|
||||||
cv = vec.sub(
|
|
||||||
vec.rotWith([bx1, by0], c1, rotation),
|
|
||||||
vec.rotWith([ax1, ay0], c0, rotation)
|
|
||||||
)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case TransformCorner.BottomRight: {
|
|
||||||
cv = vec.sub(
|
cv = vec.sub(
|
||||||
vec.rotWith([bx1, by1], c1, rotation),
|
vec.rotWith([bx1, by1], c1, rotation),
|
||||||
vec.rotWith([ax1, ay1], c0, rotation)
|
vec.rotWith([ax1, ay1], c0, rotation)
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case TransformCorner.BottomLeft: {
|
case TransformCorner.TopRight: {
|
||||||
cv = vec.sub(
|
cv = vec.sub(
|
||||||
vec.rotWith([bx0, by1], c1, rotation),
|
vec.rotWith([bx0, by1], c1, rotation),
|
||||||
vec.rotWith([ax0, ay1], c0, rotation)
|
vec.rotWith([ax0, ay1], c0, rotation)
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
case TransformCorner.BottomRight:
|
||||||
|
case TransformEdge.Bottom:
|
||||||
|
case TransformEdge.Right: {
|
||||||
|
cv = vec.sub(
|
||||||
|
vec.rotWith([bx0, by0], c1, rotation),
|
||||||
|
vec.rotWith([ax0, ay0], c0, rotation)
|
||||||
|
)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case TransformCorner.BottomLeft: {
|
||||||
|
cv = vec.sub(
|
||||||
|
vec.rotWith([bx1, by0], c1, rotation),
|
||||||
|
vec.rotWith([ax1, ay0], c0, rotation)
|
||||||
|
)
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
;[bx0, by0] = vec.sub([bx0, by0], cv)
|
;[bx0, by0] = vec.sub([bx0, by0], cv)
|
||||||
|
@ -1153,6 +1145,9 @@ export function getTransformedBoundingBox(
|
||||||
// If the axes are flipped (e.g. if the right edge has been dragged
|
// If the axes are flipped (e.g. if the right edge has been dragged
|
||||||
// left past the initial left edge) then swap points on that axis.
|
// left past the initial left edge) then swap points on that axis.
|
||||||
|
|
||||||
|
let scaleX = (bx1 - bx0) / (ax1 - ax0)
|
||||||
|
let scaleY = (by1 - by0) / (ay1 - ay0)
|
||||||
|
|
||||||
if (bx1 < bx0) {
|
if (bx1 < bx0) {
|
||||||
;[bx1, bx0] = [bx0, bx1]
|
;[bx1, bx0] = [bx0, bx1]
|
||||||
}
|
}
|
||||||
|
@ -1168,5 +1163,44 @@ export function getTransformedBoundingBox(
|
||||||
maxY: by1,
|
maxY: by1,
|
||||||
width: bx1 - bx0,
|
width: bx1 - bx0,
|
||||||
height: by1 - by0,
|
height: by1 - by0,
|
||||||
|
scaleX,
|
||||||
|
scaleY,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getRelativeTransformedBoundingBox(
|
||||||
|
bounds: Bounds,
|
||||||
|
initialBounds: Bounds,
|
||||||
|
initialShapeBounds: Bounds,
|
||||||
|
isFlippedX: boolean,
|
||||||
|
isFlippedY: boolean
|
||||||
|
) {
|
||||||
|
const minX =
|
||||||
|
bounds.minX +
|
||||||
|
bounds.width *
|
||||||
|
((isFlippedX
|
||||||
|
? initialBounds.maxX - initialShapeBounds.maxX
|
||||||
|
: initialShapeBounds.minX - initialBounds.minX) /
|
||||||
|
initialBounds.width)
|
||||||
|
|
||||||
|
const minY =
|
||||||
|
bounds.minY +
|
||||||
|
bounds.height *
|
||||||
|
((isFlippedY
|
||||||
|
? initialBounds.maxY - initialShapeBounds.maxY
|
||||||
|
: initialShapeBounds.minY - initialBounds.minY) /
|
||||||
|
initialBounds.height)
|
||||||
|
|
||||||
|
const width = (initialShapeBounds.width / initialBounds.width) * bounds.width
|
||||||
|
const height =
|
||||||
|
(initialShapeBounds.height / initialBounds.height) * bounds.height
|
||||||
|
|
||||||
|
return {
|
||||||
|
minX,
|
||||||
|
minY,
|
||||||
|
maxX: minX + width,
|
||||||
|
maxY: minY + height,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue