Makes shapes immutable, adds parenting methods to utils
This commit is contained in:
parent
f4e429af0e
commit
a1cc578bb9
18 changed files with 245 additions and 163 deletions
|
@ -39,10 +39,9 @@ export function generateFromCode(code: string) {
|
|||
|
||||
new Function(...Object.keys(scope), `${code}`)(...Object.values(scope))
|
||||
|
||||
const generatedShapes = Array.from(codeShapes.values()).map((instance) => {
|
||||
instance.shape.isGenerated = true
|
||||
return instance.shape
|
||||
})
|
||||
const generatedShapes = Array.from(codeShapes.values()).map(
|
||||
(instance) => instance.shape
|
||||
)
|
||||
|
||||
const generatedControls = Array.from(codeControls.values())
|
||||
|
||||
|
@ -73,10 +72,9 @@ export function updateFromCode(
|
|||
|
||||
new Function(...Object.keys(scope), `${code}`)(...Object.values(scope))
|
||||
|
||||
const generatedShapes = Array.from(codeShapes.values()).map((instance) => {
|
||||
instance.shape.isGenerated = true
|
||||
return instance.shape
|
||||
})
|
||||
const generatedShapes = Array.from(codeShapes.values()).map(
|
||||
(instance) => instance.shape
|
||||
)
|
||||
|
||||
return { shapes: generatedShapes }
|
||||
}
|
||||
|
|
|
@ -1,24 +1,23 @@
|
|||
import { Shape } from "types"
|
||||
import { getShapeUtils } from "lib/shape-utils"
|
||||
import { getShapeUtils, ShapeUtility } from "lib/shape-utils"
|
||||
import * as vec from "utils/vec"
|
||||
import Vector from "./vector"
|
||||
import { vectorToPoint } from "utils/utils"
|
||||
|
||||
export const codeShapes = new Set<CodeShape<Shape>>([])
|
||||
|
||||
type WithVectors<T extends Shape> = {
|
||||
[key in keyof T]: number[] extends T[key] ? Vector : T[key]
|
||||
}
|
||||
|
||||
/**
|
||||
* A base class for code shapes. Note that creating a shape adds it to the
|
||||
* shape map, while deleting it removes it from the collected shapes set
|
||||
*/
|
||||
export default class CodeShape<T extends Shape> {
|
||||
private _shape: T
|
||||
private utils: ShapeUtility<Shape>
|
||||
|
||||
constructor(props: T) {
|
||||
this._shape = props
|
||||
this.utils = getShapeUtils(this.shape)
|
||||
|
||||
codeShapes.add(this)
|
||||
}
|
||||
|
||||
|
@ -27,27 +26,31 @@ export default class CodeShape<T extends Shape> {
|
|||
}
|
||||
|
||||
moveTo(point: Vector) {
|
||||
this.shape.point = vectorToPoint(point)
|
||||
this.utils.translate(this._shape, vectorToPoint(point))
|
||||
return this
|
||||
}
|
||||
|
||||
translate(delta: Vector) {
|
||||
this.shape.point = vec.add(this._shape.point, vectorToPoint(delta))
|
||||
this.utils.translate(
|
||||
this._shape,
|
||||
vec.add(this._shape.point, vectorToPoint(delta))
|
||||
)
|
||||
return this
|
||||
}
|
||||
|
||||
rotate(rotation: number) {
|
||||
this.shape.rotation = rotation
|
||||
}
|
||||
|
||||
scale(scale: number) {
|
||||
return getShapeUtils(this.shape).scale(this.shape, scale)
|
||||
this.utils.rotate(this._shape, rotation)
|
||||
return this
|
||||
}
|
||||
|
||||
getBounds() {
|
||||
return getShapeUtils(this.shape).getBounds(this.shape)
|
||||
this.utils.getBounds(this.shape)
|
||||
return this
|
||||
}
|
||||
|
||||
hitTest(point: Vector) {
|
||||
return getShapeUtils(this.shape).hitTest(this.shape, vectorToPoint(point))
|
||||
this.utils.hitTest(this.shape, vectorToPoint(point))
|
||||
return this
|
||||
}
|
||||
|
||||
get shape() {
|
||||
|
|
|
@ -81,17 +81,13 @@ const circle = registerShapeUtils<CircleShape>({
|
|||
)
|
||||
},
|
||||
|
||||
rotate(shape) {
|
||||
return shape
|
||||
},
|
||||
|
||||
translate(shape, delta) {
|
||||
shape.point = vec.add(shape.point, delta)
|
||||
return shape
|
||||
return this
|
||||
},
|
||||
|
||||
scale(shape, scale) {
|
||||
return shape
|
||||
rotate(shape) {
|
||||
return this
|
||||
},
|
||||
|
||||
transform(shape, bounds, { initialShape, transformOrigin, scaleX, scaleY }) {
|
||||
|
@ -107,13 +103,23 @@ const circle = registerShapeUtils<CircleShape>({
|
|||
(scaleY < 0 ? 1 - transformOrigin[1] : transformOrigin[1]),
|
||||
]
|
||||
|
||||
return shape
|
||||
return this
|
||||
},
|
||||
|
||||
transformSingle(shape, bounds, info) {
|
||||
shape.radius = Math.min(bounds.width, bounds.height) / 2
|
||||
shape.point = [bounds.minX, bounds.minY]
|
||||
return shape
|
||||
return this
|
||||
},
|
||||
|
||||
setParent(shape, parentId) {
|
||||
shape.parentId = parentId
|
||||
return this
|
||||
},
|
||||
|
||||
setChildIndex(shape, childIndex) {
|
||||
shape.childIndex = childIndex
|
||||
return this
|
||||
},
|
||||
|
||||
canTransform: true,
|
||||
|
|
|
@ -70,26 +70,33 @@ const dot = registerShapeUtils<DotShape>({
|
|||
},
|
||||
|
||||
rotate(shape) {
|
||||
return shape
|
||||
},
|
||||
|
||||
scale(shape, scale: number) {
|
||||
return shape
|
||||
return this
|
||||
},
|
||||
|
||||
translate(shape, delta) {
|
||||
shape.point = vec.add(shape.point, delta)
|
||||
return shape
|
||||
return this
|
||||
},
|
||||
|
||||
transform(shape, bounds) {
|
||||
shape.point = [bounds.minX, bounds.minY]
|
||||
|
||||
return shape
|
||||
return this
|
||||
},
|
||||
|
||||
transformSingle(shape, bounds, info) {
|
||||
return this.transform(shape, bounds, info)
|
||||
this.transform(shape, bounds, info)
|
||||
return this
|
||||
},
|
||||
|
||||
setParent(shape, parentId) {
|
||||
shape.parentId = parentId
|
||||
return this
|
||||
},
|
||||
|
||||
setChildIndex(shape, childIndex) {
|
||||
shape.childIndex = childIndex
|
||||
return this
|
||||
},
|
||||
|
||||
canTransform: false,
|
||||
|
|
|
@ -100,16 +100,12 @@ const ellipse = registerShapeUtils<EllipseShape>({
|
|||
},
|
||||
|
||||
rotate(shape) {
|
||||
return shape
|
||||
return this
|
||||
},
|
||||
|
||||
translate(shape, delta) {
|
||||
shape.point = vec.add(shape.point, delta)
|
||||
return shape
|
||||
},
|
||||
|
||||
scale(shape, scale: number) {
|
||||
return shape
|
||||
return this
|
||||
},
|
||||
|
||||
transform(shape, bounds, { scaleX, scaleY, initialShape }) {
|
||||
|
@ -122,13 +118,23 @@ const ellipse = registerShapeUtils<EllipseShape>({
|
|||
? -initialShape.rotation
|
||||
: initialShape.rotation
|
||||
|
||||
return shape
|
||||
return this
|
||||
},
|
||||
|
||||
transformSingle(shape, bounds, info) {
|
||||
return this.transform(shape, bounds, info)
|
||||
},
|
||||
|
||||
setParent(shape, parentId) {
|
||||
shape.parentId = parentId
|
||||
return this
|
||||
},
|
||||
|
||||
setChildIndex(shape, childIndex) {
|
||||
shape.childIndex = childIndex
|
||||
return this
|
||||
},
|
||||
|
||||
canTransform: true,
|
||||
canChangeAspectRatio: true,
|
||||
})
|
||||
|
|
|
@ -6,6 +6,7 @@ import {
|
|||
ShapeType,
|
||||
Corner,
|
||||
Edge,
|
||||
ShapeByType,
|
||||
} from "types"
|
||||
import circle from "./circle"
|
||||
import dot from "./dot"
|
||||
|
@ -26,13 +27,66 @@ Operations throughout the app will call these utility methods
|
|||
when performing tests (such as hit tests) or mutations, such as translations.
|
||||
*/
|
||||
|
||||
export interface ShapeUtility<K extends Shape> {
|
||||
export interface ShapeUtility<K extends Readonly<Shape>> {
|
||||
// A cache for the computed bounds of this kind of shape.
|
||||
boundsCache: WeakMap<K, Bounds>
|
||||
|
||||
// Whether to show transform controls when this shape is selected.
|
||||
canTransform: boolean
|
||||
|
||||
// Whether the shape's aspect ratio can change
|
||||
canChangeAspectRatio: boolean
|
||||
|
||||
// Create a new shape.
|
||||
create(props: Partial<K>): K
|
||||
|
||||
// Apply a translation to a shape.
|
||||
translate(this: ShapeUtility<K>, shape: K, delta: number[]): ShapeUtility<K>
|
||||
|
||||
// Apply a rotation to a shape.
|
||||
rotate(this: ShapeUtility<K>, shape: K, rotation: number): ShapeUtility<K>
|
||||
|
||||
// Transform to fit a new bounding box when more than one shape is selected.
|
||||
transform(
|
||||
this: ShapeUtility<K>,
|
||||
shape: K,
|
||||
bounds: Bounds,
|
||||
info: {
|
||||
type: Edge | Corner
|
||||
initialShape: K
|
||||
scaleX: number
|
||||
scaleY: number
|
||||
transformOrigin: number[]
|
||||
}
|
||||
): ShapeUtility<K>
|
||||
|
||||
// Transform a single shape to fit a new bounding box.
|
||||
transformSingle(
|
||||
this: ShapeUtility<K>,
|
||||
shape: K,
|
||||
bounds: Bounds,
|
||||
info: {
|
||||
type: Edge | Corner
|
||||
initialShape: K
|
||||
scaleX: number
|
||||
scaleY: number
|
||||
transformOrigin: number[]
|
||||
}
|
||||
): ShapeUtility<K>
|
||||
|
||||
// Move a shape to a new parent.
|
||||
setParent(this: ShapeUtility<K>, shape: K, parentId: string): ShapeUtility<K>
|
||||
|
||||
// Change the child index of a shape
|
||||
setChildIndex(
|
||||
this: ShapeUtility<K>,
|
||||
shape: K,
|
||||
childIndex: number
|
||||
): ShapeUtility<K>
|
||||
|
||||
// Render a shape to JSX.
|
||||
render(this: ShapeUtility<K>, shape: K): JSX.Element
|
||||
|
||||
// Get the bounds of the a shape.
|
||||
getBounds(this: ShapeUtility<K>, shape: K): Bounds
|
||||
|
||||
|
@ -47,55 +101,10 @@ export interface ShapeUtility<K extends Shape> {
|
|||
|
||||
// Test whether bounds collide with or contain a shape.
|
||||
hitTestBounds(this: ShapeUtility<K>, shape: K, bounds: Bounds): boolean
|
||||
|
||||
// Apply a rotation to a shape.
|
||||
rotate(this: ShapeUtility<K>, shape: K): K
|
||||
|
||||
// Apply a translation to a shape.
|
||||
translate(this: ShapeUtility<K>, shape: K, delta: number[]): K
|
||||
|
||||
// Transform to fit a new bounding box.
|
||||
transform(
|
||||
this: ShapeUtility<K>,
|
||||
shape: K,
|
||||
bounds: Bounds,
|
||||
info: {
|
||||
type: Edge | Corner
|
||||
initialShape: K
|
||||
scaleX: number
|
||||
scaleY: number
|
||||
transformOrigin: number[]
|
||||
}
|
||||
): K
|
||||
|
||||
transformSingle(
|
||||
this: ShapeUtility<K>,
|
||||
shape: K,
|
||||
bounds: Bounds,
|
||||
info: {
|
||||
type: Edge | Corner
|
||||
initialShape: K
|
||||
scaleX: number
|
||||
scaleY: number
|
||||
transformOrigin: number[]
|
||||
}
|
||||
): K
|
||||
|
||||
// Apply a scale to a shape.
|
||||
scale(this: ShapeUtility<K>, shape: K, scale: number): K
|
||||
|
||||
// Render a shape to JSX.
|
||||
render(this: ShapeUtility<K>, shape: K): JSX.Element
|
||||
|
||||
// Whether to show transform controls when this shape is selected.
|
||||
canTransform: boolean
|
||||
|
||||
// Whether the shape's aspect ratio can change
|
||||
canChangeAspectRatio: boolean
|
||||
}
|
||||
|
||||
// A mapping of shape types to shape utilities.
|
||||
const shapeUtilityMap: { [key in ShapeType]: ShapeUtility<Shapes[key]> } = {
|
||||
const shapeUtilityMap: Record<ShapeType, ShapeUtility<Shape>> = {
|
||||
[ShapeType.Circle]: circle,
|
||||
[ShapeType.Dot]: dot,
|
||||
[ShapeType.Polyline]: polyline,
|
||||
|
@ -110,8 +119,8 @@ const shapeUtilityMap: { [key in ShapeType]: ShapeUtility<Shapes[key]> } = {
|
|||
* @param shape
|
||||
* @returns
|
||||
*/
|
||||
export function getShapeUtils(shape: Shape): ShapeUtility<typeof shape> {
|
||||
return shapeUtilityMap[shape.type]
|
||||
export function getShapeUtils<T extends Shape>(shape: T): ShapeUtility<T> {
|
||||
return shapeUtilityMap[shape.type] as ShapeUtility<T>
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -125,4 +134,11 @@ export function registerShapeUtils<T extends Shape>(
|
|||
return Object.freeze(shape)
|
||||
}
|
||||
|
||||
export function createShape<T extends ShapeType>(
|
||||
type: T,
|
||||
props: Partial<ShapeByType<T>>
|
||||
) {
|
||||
return shapeUtilityMap[type].create(props) as ShapeByType<T>
|
||||
}
|
||||
|
||||
export default shapeUtilityMap
|
||||
|
|
|
@ -79,28 +79,34 @@ const line = registerShapeUtils<LineShape>({
|
|||
},
|
||||
|
||||
rotate(shape) {
|
||||
return shape
|
||||
return this
|
||||
},
|
||||
|
||||
translate(shape, delta) {
|
||||
shape.point = vec.add(shape.point, delta)
|
||||
return shape
|
||||
},
|
||||
|
||||
scale(shape, scale: number) {
|
||||
return shape
|
||||
return this
|
||||
},
|
||||
|
||||
transform(shape, bounds) {
|
||||
shape.point = [bounds.minX, bounds.minY]
|
||||
|
||||
return shape
|
||||
return this
|
||||
},
|
||||
|
||||
transformSingle(shape, bounds, info) {
|
||||
return this.transform(shape, bounds, info)
|
||||
},
|
||||
|
||||
setParent(shape, parentId) {
|
||||
shape.parentId = parentId
|
||||
return this
|
||||
},
|
||||
|
||||
setChildIndex(shape, childIndex) {
|
||||
shape.childIndex = childIndex
|
||||
return this
|
||||
},
|
||||
|
||||
canTransform: false,
|
||||
canChangeAspectRatio: false,
|
||||
})
|
||||
|
|
|
@ -87,16 +87,12 @@ const polyline = registerShapeUtils<PolylineShape>({
|
|||
},
|
||||
|
||||
rotate(shape) {
|
||||
return shape
|
||||
return this
|
||||
},
|
||||
|
||||
translate(shape, delta) {
|
||||
shape.point = vec.add(shape.point, delta)
|
||||
return shape
|
||||
},
|
||||
|
||||
scale(shape, scale: number) {
|
||||
return shape
|
||||
return this
|
||||
},
|
||||
|
||||
transform(shape, bounds, { initialShape, scaleX, scaleY }) {
|
||||
|
@ -117,11 +113,22 @@ const polyline = registerShapeUtils<PolylineShape>({
|
|||
})
|
||||
|
||||
shape.point = [bounds.minX, bounds.minY]
|
||||
return shape
|
||||
return this
|
||||
},
|
||||
|
||||
transformSingle(shape, bounds, info) {
|
||||
return this.transform(shape, bounds, info)
|
||||
this.transform(shape, bounds, info)
|
||||
return this
|
||||
},
|
||||
|
||||
setParent(shape, parentId) {
|
||||
shape.parentId = parentId
|
||||
return this
|
||||
},
|
||||
|
||||
setChildIndex(shape, childIndex) {
|
||||
shape.childIndex = childIndex
|
||||
return this
|
||||
},
|
||||
|
||||
canTransform: true,
|
||||
|
|
|
@ -79,28 +79,34 @@ const ray = registerShapeUtils<RayShape>({
|
|||
},
|
||||
|
||||
rotate(shape) {
|
||||
return shape
|
||||
return this
|
||||
},
|
||||
|
||||
translate(shape, delta) {
|
||||
shape.point = vec.add(shape.point, delta)
|
||||
return shape
|
||||
},
|
||||
|
||||
scale(shape, scale: number) {
|
||||
return shape
|
||||
return this
|
||||
},
|
||||
|
||||
transform(shape, bounds) {
|
||||
shape.point = [bounds.minX, bounds.minY]
|
||||
|
||||
return shape
|
||||
return this
|
||||
},
|
||||
|
||||
transformSingle(shape, bounds, info) {
|
||||
return this.transform(shape, bounds, info)
|
||||
},
|
||||
|
||||
setParent(shape, parentId) {
|
||||
shape.parentId = parentId
|
||||
return this
|
||||
},
|
||||
|
||||
setChildIndex(shape, childIndex) {
|
||||
shape.childIndex = childIndex
|
||||
return this
|
||||
},
|
||||
|
||||
canTransform: false,
|
||||
canChangeAspectRatio: false,
|
||||
})
|
||||
|
|
|
@ -96,16 +96,12 @@ const rectangle = registerShapeUtils<RectangleShape>({
|
|||
},
|
||||
|
||||
rotate(shape) {
|
||||
return shape
|
||||
return this
|
||||
},
|
||||
|
||||
translate(shape, delta) {
|
||||
shape.point = vec.add(shape.point, delta)
|
||||
return shape
|
||||
},
|
||||
|
||||
scale(shape, scale) {
|
||||
return shape
|
||||
return this
|
||||
},
|
||||
|
||||
transform(shape, bounds, { initialShape, transformOrigin, scaleX, scaleY }) {
|
||||
|
@ -133,13 +129,23 @@ const rectangle = registerShapeUtils<RectangleShape>({
|
|||
: initialShape.rotation
|
||||
}
|
||||
|
||||
return shape
|
||||
return this
|
||||
},
|
||||
|
||||
transformSingle(shape, bounds) {
|
||||
shape.size = [bounds.width, bounds.height]
|
||||
shape.point = [bounds.minX, bounds.minY]
|
||||
return shape
|
||||
return this
|
||||
},
|
||||
|
||||
setParent(shape, parentId) {
|
||||
shape.parentId = parentId
|
||||
return this
|
||||
},
|
||||
|
||||
setChildIndex(shape, childIndex) {
|
||||
shape.childIndex = childIndex
|
||||
return this
|
||||
},
|
||||
|
||||
canTransform: true,
|
||||
|
|
|
@ -2,6 +2,7 @@ import Command from "./command"
|
|||
import history from "../history"
|
||||
import { Data, MoveType, Shape } from "types"
|
||||
import { forceIntegerChildIndices, getChildren, getPage } from "utils/utils"
|
||||
import { getShapeUtils } from "lib/shape-utils"
|
||||
|
||||
export default function moveCommand(data: Data, type: MoveType) {
|
||||
const { currentPageId } = data
|
||||
|
@ -75,7 +76,8 @@ export default function moveCommand(data: Data, type: MoveType) {
|
|||
const page = getPage(data)
|
||||
|
||||
for (let id of selectedIds) {
|
||||
page.shapes[id].childIndex = initialIndices[id]
|
||||
const shape = page.shapes[id]
|
||||
getShapeUtils(shape).setChildIndex(shape, initialIndices[id])
|
||||
}
|
||||
},
|
||||
})
|
||||
|
@ -93,7 +95,9 @@ function moveToFront(shapes: Shape[], siblings: Shape[]) {
|
|||
|
||||
const startIndex = Math.ceil(diff[0].childIndex) + 1
|
||||
|
||||
shapes.forEach((shape, i) => (shape.childIndex = startIndex + i))
|
||||
shapes.forEach((shape, i) =>
|
||||
getShapeUtils(shape).setChildIndex(shape, startIndex + i)
|
||||
)
|
||||
}
|
||||
|
||||
function moveToBack(shapes: Shape[], siblings: Shape[]) {
|
||||
|
@ -109,7 +113,9 @@ function moveToBack(shapes: Shape[], siblings: Shape[]) {
|
|||
|
||||
const step = startIndex / (shapes.length + 1)
|
||||
|
||||
shapes.forEach((shape, i) => (shape.childIndex = startIndex - (i + 1) * step))
|
||||
shapes.forEach((shape, i) =>
|
||||
getShapeUtils(shape).setChildIndex(shape, startIndex - (i + 1) * step)
|
||||
)
|
||||
}
|
||||
|
||||
function moveForward(shape: Shape, siblings: Shape[], visited: Set<string>) {
|
||||
|
@ -132,7 +138,7 @@ function moveForward(shape: Shape, siblings: Shape[], visited: Set<string>) {
|
|||
: Math.ceil(nextSibling.childIndex + 1)
|
||||
}
|
||||
|
||||
shape.childIndex = nextIndex
|
||||
getShapeUtils(shape).setChildIndex(shape, nextIndex)
|
||||
|
||||
siblings.sort((a, b) => a.childIndex - b.childIndex)
|
||||
}
|
||||
|
@ -158,7 +164,7 @@ function moveBackward(shape: Shape, siblings: Shape[], visited: Set<string>) {
|
|||
: nextSibling.childIndex / 2
|
||||
}
|
||||
|
||||
shape.childIndex = nextIndex
|
||||
getShapeUtils(shape).setChildIndex(shape, nextIndex)
|
||||
|
||||
siblings.sort((a, b) => a.childIndex - b.childIndex)
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import history from "../history"
|
|||
import { Data } from "types"
|
||||
import { RotateSnapshot } from "state/sessions/rotate-session"
|
||||
import { getPage } from "utils/utils"
|
||||
import { getShapeUtils } from "lib/shape-utils"
|
||||
|
||||
export default function rotateCommand(
|
||||
data: Data,
|
||||
|
@ -19,8 +20,9 @@ export default function rotateCommand(
|
|||
|
||||
for (let { id, point, rotation } of after.shapes) {
|
||||
const shape = shapes[id]
|
||||
shape.rotation = rotation
|
||||
shape.point = point
|
||||
const utils = getShapeUtils(shape)
|
||||
utils.rotate(shape, rotation)
|
||||
utils.translate(shape, point)
|
||||
}
|
||||
|
||||
data.boundsRotation = after.boundsRotation
|
||||
|
@ -30,8 +32,9 @@ export default function rotateCommand(
|
|||
|
||||
for (let { id, point, rotation } of before.shapes) {
|
||||
const shape = shapes[id]
|
||||
shape.rotation = rotation
|
||||
shape.point = point
|
||||
const utils = getShapeUtils(shape)
|
||||
utils.rotate(shape, rotation)
|
||||
utils.translate(shape, point)
|
||||
}
|
||||
|
||||
data.boundsRotation = before.boundsRotation
|
||||
|
|
|
@ -3,6 +3,7 @@ import history from "../history"
|
|||
import { TranslateSnapshot } from "state/sessions/translate-session"
|
||||
import { Data } from "types"
|
||||
import { getPage } from "utils/utils"
|
||||
import { getShapeUtils } from "lib/shape-utils"
|
||||
|
||||
export default function translateCommand(
|
||||
data: Data,
|
||||
|
@ -32,7 +33,8 @@ export default function translateCommand(
|
|||
}
|
||||
|
||||
for (const { id, point } of initialShapes) {
|
||||
shapes[id].point = point
|
||||
const shape = shapes[id]
|
||||
getShapeUtils(shape).translate(shape, point)
|
||||
data.selectedIds.add(id)
|
||||
}
|
||||
},
|
||||
|
@ -49,7 +51,8 @@ export default function translateCommand(
|
|||
}
|
||||
|
||||
for (const { id, point } of initialShapes) {
|
||||
shapes[id].point = point
|
||||
const shape = shapes[id]
|
||||
getShapeUtils(shape).translate(shape, point)
|
||||
data.selectedIds.add(id)
|
||||
}
|
||||
},
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
getSelectedShapes,
|
||||
getShapeBounds,
|
||||
} from "utils/utils"
|
||||
import { getShapeUtils } from "lib/shape-utils"
|
||||
|
||||
const PI2 = Math.PI * 2
|
||||
|
||||
|
@ -42,9 +43,13 @@ export default class RotateSession extends BaseSession {
|
|||
|
||||
for (let { id, center, offset, rotation } of shapes) {
|
||||
const shape = page.shapes[id]
|
||||
shape.rotation = (PI2 + (rotation + rot)) % PI2
|
||||
const newCenter = vec.rotWith(center, boundsCenter, rot % PI2)
|
||||
shape.point = vec.sub(newCenter, offset)
|
||||
|
||||
getShapeUtils(shape)
|
||||
.rotate(shape, (PI2 + (rotation + rot)) % PI2)
|
||||
.translate(
|
||||
shape,
|
||||
vec.sub(vec.rotWith(center, boundsCenter, rot % PI2), offset)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,8 +58,7 @@ export default class RotateSession extends BaseSession {
|
|||
|
||||
for (let { id, point, rotation } of this.snapshot.shapes) {
|
||||
const shape = page.shapes[id]
|
||||
shape.rotation = rotation
|
||||
shape.point = point
|
||||
getShapeUtils(shape).rotate(shape, rotation).translate(shape, point)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import commands from "state/commands"
|
|||
import { current } from "immer"
|
||||
import { v4 as uuid } from "uuid"
|
||||
import { getChildIndexAbove, getPage, getSelectedShapes } from "utils/utils"
|
||||
import { getShapeUtils } from "lib/shape-utils"
|
||||
|
||||
export default class TranslateSession extends BaseSession {
|
||||
delta = [0, 0]
|
||||
|
@ -38,7 +39,8 @@ export default class TranslateSession extends BaseSession {
|
|||
data.selectedIds.clear()
|
||||
|
||||
for (const { id, point } of initialShapes) {
|
||||
shapes[id].point = point
|
||||
const shape = shapes[id]
|
||||
getShapeUtils(shape).translate(shape, point)
|
||||
}
|
||||
|
||||
for (const clone of clones) {
|
||||
|
@ -48,7 +50,8 @@ export default class TranslateSession extends BaseSession {
|
|||
}
|
||||
|
||||
for (const { id, point } of clones) {
|
||||
shapes[id].point = vec.add(point, delta)
|
||||
const shape = shapes[id]
|
||||
getShapeUtils(shape).translate(shape, vec.add(point, delta))
|
||||
}
|
||||
} else {
|
||||
if (this.isCloning) {
|
||||
|
@ -65,7 +68,8 @@ export default class TranslateSession extends BaseSession {
|
|||
}
|
||||
|
||||
for (const { id, point } of initialShapes) {
|
||||
shapes[id].point = vec.add(point, delta)
|
||||
const shape = shapes[id]
|
||||
getShapeUtils(shape).translate(shape, vec.add(point, delta))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -75,7 +79,8 @@ export default class TranslateSession extends BaseSession {
|
|||
const { shapes } = getPage(data, currentPageId)
|
||||
|
||||
for (const { id, point } of initialShapes) {
|
||||
shapes[id].point = point
|
||||
const shape = shapes[id]
|
||||
getShapeUtils(shape).translate(shape, point)
|
||||
}
|
||||
|
||||
for (const { id } of clones) {
|
||||
|
|
|
@ -525,12 +525,16 @@ const state = createState({
|
|||
},
|
||||
actions: {
|
||||
/* --------------------- Shapes --------------------- */
|
||||
createShape(data, payload: PointerInfo, shape: Shape) {
|
||||
createShape(data, payload, shape: Shape) {
|
||||
const siblings = getChildren(data, shape.parentId)
|
||||
shape.childIndex =
|
||||
siblings.length > 0 ? siblings[siblings.length - 1].childIndex + 1 : 1
|
||||
const childIndex = siblings.length
|
||||
? siblings[siblings.length - 1].childIndex + 1
|
||||
: 1
|
||||
|
||||
getShapeUtils(shape).setChildIndex(shape, childIndex)
|
||||
|
||||
getPage(data).shapes[shape.id] = shape
|
||||
|
||||
data.selectedIds.clear()
|
||||
data.selectedIds.add(shape.id)
|
||||
},
|
||||
|
@ -608,19 +612,11 @@ const state = createState({
|
|||
data,
|
||||
payload: PointerInfo & { target: Corner | Edge }
|
||||
) {
|
||||
const point = screenToWorld(inputs.pointer.origin, data)
|
||||
session =
|
||||
data.selectedIds.size === 1
|
||||
? new Sessions.TransformSingleSession(
|
||||
data,
|
||||
payload.target,
|
||||
screenToWorld(payload.point, data),
|
||||
false
|
||||
)
|
||||
: new Sessions.TransformSession(
|
||||
data,
|
||||
payload.target,
|
||||
screenToWorld(payload.point, data)
|
||||
)
|
||||
? new Sessions.TransformSingleSession(data, payload.target, point)
|
||||
: new Sessions.TransformSession(data, payload.target, point)
|
||||
},
|
||||
startDrawTransformSession(data, payload: PointerInfo) {
|
||||
session = new Sessions.TransformSingleSession(
|
||||
|
@ -651,7 +647,7 @@ const state = createState({
|
|||
startDirectionSession(data, payload: PointerInfo) {
|
||||
session = new Sessions.DirectionSession(
|
||||
data,
|
||||
screenToWorld(payload.point, data)
|
||||
screenToWorld(inputs.pointer.origin, data)
|
||||
)
|
||||
},
|
||||
updateDirectionSession(data, payload: PointerInfo) {
|
||||
|
|
7
types.ts
7
types.ts
|
@ -106,7 +106,7 @@ export interface RectangleShape extends BaseShape {
|
|||
size: number[]
|
||||
}
|
||||
|
||||
export type Shape =
|
||||
export type Shape = Readonly<
|
||||
| DotShape
|
||||
| CircleShape
|
||||
| EllipseShape
|
||||
|
@ -114,8 +114,9 @@ export type Shape =
|
|||
| RayShape
|
||||
| PolylineShape
|
||||
| RectangleShape
|
||||
>
|
||||
|
||||
export interface Shapes extends Record<ShapeType, Shape> {
|
||||
export interface Shapes {
|
||||
[ShapeType.Dot]: DotShape
|
||||
[ShapeType.Circle]: CircleShape
|
||||
[ShapeType.Ellipse]: EllipseShape
|
||||
|
@ -125,6 +126,8 @@ export interface Shapes extends Record<ShapeType, Shape> {
|
|||
[ShapeType.Rectangle]: RectangleShape
|
||||
}
|
||||
|
||||
export type ShapeByType<T extends ShapeType> = Shapes[T]
|
||||
|
||||
export interface CodeFile {
|
||||
id: string
|
||||
name: string
|
||||
|
|
|
@ -1479,7 +1479,8 @@ export function getChildIndexBelow(
|
|||
|
||||
export function forceIntegerChildIndices(shapes: Shape[]) {
|
||||
for (let i = 0; i < shapes.length; i++) {
|
||||
shapes[i].childIndex = i + 1
|
||||
const shape = shapes[i]
|
||||
getShapeUtils(shape).setChildIndex(shape, i + 1)
|
||||
}
|
||||
}
|
||||
export function setZoomCSS(zoom: number) {
|
||||
|
|
Loading…
Reference in a new issue