Removes translateTo, rotateTo

This commit is contained in:
Steve Ruiz 2021-06-02 16:05:44 +01:00
parent 7c768bddf5
commit 9ab86ba9ae
22 changed files with 212 additions and 156 deletions

View file

@ -1,12 +1,12 @@
import { Shape } from "types"
import { Shape } from 'types'
import shapeUtilityMap, {
createShape,
getShapeUtils,
ShapeUtility,
} from "lib/shape-utils"
import * as vec from "utils/vec"
import Vector from "./vector"
import { vectorToPoint } from "utils/utils"
} 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>>([])
@ -29,20 +29,21 @@ export default class CodeShape<T extends Shape> {
}
moveTo(point: Vector) {
this.utils.translateTo(this._shape, vectorToPoint(point))
this.utils.setProperty(this._shape, 'point', vectorToPoint(point))
return this
}
translate(delta: Vector) {
this.utils.translateTo(
this.utils.setProperty(
this._shape,
'point',
vec.add(this._shape.point, vectorToPoint(delta))
)
return this
}
rotate(rotation: number) {
this.utils.rotateTo(this._shape, rotation)
this.utils.setProperty(this._shape, 'rotation', rotation)
return this
}

View file

@ -213,19 +213,6 @@ const arrow = registerShapeUtils<ArrowShape>({
}
},
rotateTo(shape, rotation) {
// const rot = rotation - shape.rotation
// const center = this.getCenter(shape)
// shape.points = shape.points.map((pt) => vec.rotWith(pt, shape.point, rot))
shape.rotation = rotation
return this
},
translateTo(shape, point) {
shape.point = vec.toPrecision(point)
return this
},
transform(shape, bounds, { initialShape, scaleX, scaleY }) {
const initialShapeBounds = this.getBounds(initialShape)

View file

@ -96,16 +96,6 @@ const circle = registerShapeUtils<CircleShape>({
)
},
rotateTo(shape, rotation) {
shape.rotation = rotation
return this
},
translateTo(shape, point) {
shape.point = vec.toPrecision(point)
return this
},
transform(shape, bounds, { initialShape, transformOrigin, scaleX, scaleY }) {
shape.radius =
initialShape.radius * Math.min(Math.abs(scaleX), Math.abs(scaleY))

View file

@ -79,15 +79,6 @@ const dot = registerShapeUtils<DotShape>({
)
},
rotateTo(shape) {
return this
},
translateTo(shape, point) {
shape.point = vec.toPrecision(point)
return this
},
transform(shape, bounds) {
shape.point = [bounds.minX, bounds.minY]

View file

@ -137,20 +137,6 @@ const draw = registerShapeUtils<DrawShape>({
)
},
rotateTo(shape, rotation) {
shape.rotation = rotation
// console.log(shape.points.map(([x, y]) => [x, y]))
// const bounds = this.getBounds(shape)
// const center = [bounds.width / 2, bounds.height / 2]
// shape.points = shape.points.map((pt) => vec.rotWith(pt, center, rotation))
return this
},
translateTo(shape, point) {
shape.point = vec.toPrecision(point)
return this
},
transform(shape, bounds, { initialShape, scaleX, scaleY }) {
const initialShapeBounds = this.boundsCache.get(initialShape)
shape.points = initialShape.points.map(([x, y]) => {

View file

@ -112,16 +112,6 @@ const ellipse = registerShapeUtils<EllipseShape>({
)
},
rotateTo(shape, rotation) {
shape.rotation = rotation
return this
},
translateTo(shape, point) {
shape.point = vec.toPrecision(point)
return this
},
transform(shape, bounds, { scaleX, scaleY, initialShape }) {
// TODO: Locked aspect ratio transform

View file

@ -29,7 +29,7 @@ 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 Readonly<Shape>> {
export interface ShapeUtility<K extends Shape> {
// A cache for the computed bounds of this kind of shape.
boundsCache: WeakMap<K, Bounds>
@ -51,12 +51,6 @@ export interface ShapeUtility<K extends Readonly<Shape>> {
style: Partial<ShapeStyles>
): ShapeUtility<K>
// Set the shape's point.
translateTo(this: ShapeUtility<K>, shape: K, delta: number[]): ShapeUtility<K>
// Set the shape's rotation.
rotateTo(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>,

View file

@ -89,15 +89,6 @@ const line = registerShapeUtils<LineShape>({
)
},
rotateTo(shape) {
return this
},
translateTo(shape, point) {
shape.point = vec.toPrecision(point)
return this
},
transform(shape, bounds) {
shape.point = [bounds.minX, bounds.minY]

View file

@ -90,16 +90,6 @@ const polyline = registerShapeUtils<PolylineShape>({
)
},
rotateTo(shape, rotation) {
shape.rotation = rotation
return this
},
translateTo(shape, point) {
shape.point = vec.toPrecision(point)
return this
},
transform(shape, bounds, { initialShape, scaleX, scaleY }) {
const initialShapeBounds = this.getBounds(initialShape)

View file

@ -87,15 +87,6 @@ const ray = registerShapeUtils<RayShape>({
)
},
rotateTo(shape) {
return this
},
translateTo(shape, point) {
shape.point = vec.toPrecision(point)
return this
},
transform(shape, bounds) {
shape.point = [bounds.minX, bounds.minY]

View file

@ -98,16 +98,6 @@ const rectangle = registerShapeUtils<RectangleShape>({
)
},
rotateTo(shape, rotation) {
shape.rotation = rotation
return this
},
translateTo(shape, point) {
shape.point = vec.toPrecision(point)
return this
},
transform(shape, bounds, { initialShape, transformOrigin, scaleX, scaleY }) {
if (shape.rotation === 0 && !shape.isAspectRatioLocked) {
shape.size = [bounds.width, bounds.height]

131
lib/shapes/BaseShape.tsx Normal file
View file

@ -0,0 +1,131 @@
import { defaultStyle } from 'lib/shape-styles'
import {
Shape,
Bounds,
BaseShape,
ShapeSpecificProps,
ShapeType,
ShapeStyles,
MutableShape,
Edge,
Corner,
ShapeBinding,
} from 'types'
import { v4 as uuid } from 'uuid'
import * as vec from 'utils/vec'
import {
getBoundsCenter,
getRotatedCorners,
getBoundsFromPoints,
} from 'utils/utils'
class ShapeUtility<K extends MutableShape> {
boundsCache = new WeakMap<K, Bounds>([])
canTransform = true
canChangeAspectRatio = true
canStyleFill = true
// Create a new shape.
create(props: Partial<K> & ShapeSpecificProps<K>): K {
return {
id: uuid(),
isGenerated: false,
point: [0, 0],
name: 'Shape',
parentId: 'page0',
childIndex: 0,
rotation: 0,
isAspectRatioLocked: false,
isLocked: false,
isHidden: false,
...props,
} as K
}
applyStyles = (shape: K, style: Partial<ShapeStyles>) => {
Object.assign(shape.style, style)
return this
}
transform = (
shape: K,
bounds: Bounds,
info: {
type: Edge | Corner
initialShape: K
scaleX: number
scaleY: number
transformOrigin: number[]
}
) => {
shape.point = [bounds.minX, bounds.minY]
return this
}
transformSingle = (
shape: K,
bounds: Bounds,
info: {
type: Edge | Corner
initialShape: K
scaleX: number
scaleY: number
transformOrigin: number[]
}
) => {
return this.transform(shape, bounds, info)
}
setProperty = <P extends keyof K>(shape: K, prop: P, value: K[P]) => {
shape[prop] = value
return this
}
onBindingMove? = (shape: K, bindings: Record<string, ShapeBinding>) => {
return this
}
onHandleMove? = (shape: K, handle: Partial<K['handles']>) => {
return this
}
render = (shape: K): JSX.Element => {
return <circle id={shape.id} />
}
// Get the bounds of the a shape.
getBounds = (shape: K): Bounds => {
const [x, y] = shape.point
return {
minX: x,
minY: y,
maxX: x + 1,
maxY: y + 1,
width: 1,
height: 1,
}
}
// Get the routated bounds of the a shape.
getRotatedBounds = (shape: K): Bounds => {
return getBoundsFromPoints(
getRotatedCorners(this.getBounds(shape), shape.rotation)
)
}
// Get the center of the shape
getCenter = (shape: K): number[] => {
return getBoundsCenter(this.getBounds(shape))
}
// Test whether a point lies within a shape.
hitTest = (shape: K, test: number[]): boolean => {
return true
}
// Test whether bounds collide with or contain a shape.
hitTestBounds = (shape: K, bounds: Bounds): boolean => {
return true
}
}

View file

@ -1,8 +1,8 @@
import Command from "./command"
import history from "../history"
import { AlignType, Data } from "types"
import { getCommonBounds, getPage, getSelectedShapes } from "utils/utils"
import { getShapeUtils } from "lib/shape-utils"
import Command from './command'
import history from '../history'
import { AlignType, Data } from 'types'
import { getCommonBounds, getPage, getSelectedShapes } from 'utils/utils'
import { getShapeUtils } from 'lib/shape-utils'
export default function alignCommand(data: Data, type: AlignType) {
const { currentPageId } = data
@ -18,8 +18,8 @@ export default function alignCommand(data: Data, type: AlignType) {
history.execute(
data,
new Command({
name: "aligned",
category: "canvas",
name: 'aligned',
category: 'canvas',
do(data) {
const { shapes } = getPage(data, currentPageId)
@ -27,7 +27,7 @@ export default function alignCommand(data: Data, type: AlignType) {
case AlignType.Top: {
for (let id in boundsForShapes) {
const shape = shapes[id]
getShapeUtils(shape).translateTo(shape, [
getShapeUtils(shape).setProperty(shape, 'point', [
shape.point[0],
commonBounds.minY,
])
@ -37,7 +37,7 @@ export default function alignCommand(data: Data, type: AlignType) {
case AlignType.CenterVertical: {
for (let id in boundsForShapes) {
const shape = shapes[id]
getShapeUtils(shape).translateTo(shape, [
getShapeUtils(shape).setProperty(shape, 'point', [
shape.point[0],
midY - boundsForShapes[id].height / 2,
])
@ -47,7 +47,7 @@ export default function alignCommand(data: Data, type: AlignType) {
case AlignType.Bottom: {
for (let id in boundsForShapes) {
const shape = shapes[id]
getShapeUtils(shape).translateTo(shape, [
getShapeUtils(shape).setProperty(shape, 'point', [
shape.point[0],
commonBounds.maxY - boundsForShapes[id].height,
])
@ -57,7 +57,7 @@ export default function alignCommand(data: Data, type: AlignType) {
case AlignType.Left: {
for (let id in boundsForShapes) {
const shape = shapes[id]
getShapeUtils(shape).translateTo(shape, [
getShapeUtils(shape).setProperty(shape, 'point', [
commonBounds.minX,
shape.point[1],
])
@ -67,7 +67,7 @@ export default function alignCommand(data: Data, type: AlignType) {
case AlignType.CenterHorizontal: {
for (let id in boundsForShapes) {
const shape = shapes[id]
getShapeUtils(shape).translateTo(shape, [
getShapeUtils(shape).setProperty(shape, 'point', [
midX - boundsForShapes[id].width / 2,
shape.point[1],
])
@ -77,7 +77,7 @@ export default function alignCommand(data: Data, type: AlignType) {
case AlignType.Right: {
for (let id in boundsForShapes) {
const shape = shapes[id]
getShapeUtils(shape).translateTo(shape, [
getShapeUtils(shape).setProperty(shape, 'point', [
commonBounds.maxX - boundsForShapes[id].width,
shape.point[1],
])
@ -91,7 +91,7 @@ export default function alignCommand(data: Data, type: AlignType) {
for (let id in boundsForShapes) {
const shape = shapes[id]
const initialBounds = boundsForShapes[id]
getShapeUtils(shape).translateTo(shape, [
getShapeUtils(shape).setProperty(shape, 'point', [
initialBounds.minX,
initialBounds.minY,
])

View file

@ -1,13 +1,13 @@
import Command from "./command"
import history from "../history"
import { Data, DistributeType } from "types"
import Command from './command'
import history from '../history'
import { Data, DistributeType } from 'types'
import {
getBoundsCenter,
getCommonBounds,
getPage,
getSelectedShapes,
} from "utils/utils"
import { getShapeUtils } from "lib/shape-utils"
} from 'utils/utils'
import { getShapeUtils } from 'lib/shape-utils'
export default function distributeCommand(data: Data, type: DistributeType) {
const { currentPageId } = data
@ -32,8 +32,8 @@ export default function distributeCommand(data: Data, type: DistributeType) {
history.execute(
data,
new Command({
name: "distributed",
category: "canvas",
name: 'distributed',
category: 'canvas',
do(data) {
const { shapes } = getPage(data, currentPageId)
const len = entries.length
@ -59,7 +59,7 @@ export default function distributeCommand(data: Data, type: DistributeType) {
for (let i = 0; i < entriesToMove.length; i++) {
const [id, bounds] = entriesToMove[i]
const shape = shapes[id]
getShapeUtils(shape).translateTo(shape, [
getShapeUtils(shape).setProperty(shape, 'point', [
x + step * i - bounds.width / 2,
bounds.minY,
])
@ -75,7 +75,10 @@ export default function distributeCommand(data: Data, type: DistributeType) {
for (let i = 0; i < entriesToMove.length - 1; i++) {
const [id, bounds] = entriesToMove[i]
const shape = shapes[id]
getShapeUtils(shape).translateTo(shape, [x, bounds.minY])
getShapeUtils(shape).setProperty(shape, 'point', [
x,
bounds.minY,
])
x += bounds.width + step
}
}
@ -101,7 +104,7 @@ export default function distributeCommand(data: Data, type: DistributeType) {
for (let i = 0; i < entriesToMove.length; i++) {
const [id, bounds] = entriesToMove[i]
const shape = shapes[id]
getShapeUtils(shape).translateTo(shape, [
getShapeUtils(shape).setProperty(shape, 'point', [
bounds.minX,
y + step * i - bounds.height / 2,
])
@ -117,7 +120,10 @@ export default function distributeCommand(data: Data, type: DistributeType) {
for (let i = 0; i < entriesToMove.length - 1; i++) {
const [id, bounds] = entriesToMove[i]
const shape = shapes[id]
getShapeUtils(shape).translateTo(shape, [bounds.minX, y])
getShapeUtils(shape).setProperty(shape, 'point', [
bounds.minX,
y,
])
y += bounds.height + step
}
}
@ -131,7 +137,7 @@ export default function distributeCommand(data: Data, type: DistributeType) {
for (let id in boundsForShapes) {
const shape = shapes[id]
const initialBounds = boundsForShapes[id]
getShapeUtils(shape).translateTo(shape, [
getShapeUtils(shape).setProperty(shape, 'point', [
initialBounds.minX,
initialBounds.minY,
])

View file

@ -24,7 +24,11 @@ export default function nudgeCommand(data: Data, delta: number[]) {
for (let id in shapeBounds) {
const shape = shapes[id]
getShapeUtils(shape).translateTo(shape, vec.add(shape.point, delta))
getShapeUtils(shape).setProperty(
shape,
'point',
vec.add(shape.point, delta)
)
}
},
undo(data) {
@ -32,7 +36,11 @@ export default function nudgeCommand(data: Data, delta: number[]) {
for (let id in shapeBounds) {
const shape = shapes[id]
getShapeUtils(shape).translateTo(shape, vec.sub(shape.point, delta))
getShapeUtils(shape).setProperty(
shape,
'point',
vec.sub(shape.point, delta)
)
}
},
})

View file

@ -47,7 +47,9 @@ export default function rotateCcwCommand(data: Data) {
const rot = (PI2 + (shape.rotation - PI2 / 4)) % PI2
getShapeUtils(shape).rotateTo(shape, rot).translateTo(shape, nextPoint)
getShapeUtils(shape)
.setProperty(shape, 'rotation', rot)
.setProperty(shape, 'point', nextPoint)
return [id, shape]
})
@ -67,8 +69,8 @@ export default function rotateCcwCommand(data: Data) {
const shape = shapes[id]
getShapeUtils(shape)
.rotateTo(shape, nextShapes[id].rotation)
.translateTo(shape, nextShapes[id].point)
.setProperty(shape, 'rotation', nextShapes[id].rotation)
.setProperty(shape, 'point', nextShapes[id].point)
}
data.boundsRotation = nextboundsRotation
@ -81,7 +83,9 @@ export default function rotateCcwCommand(data: Data) {
const shape = shapes[id]
const utils = getShapeUtils(shape)
utils.rotateTo(shape, rotation).translateTo(shape, point)
utils
.setProperty(shape, 'rotation', rotation)
.setProperty(shape, 'point', point)
}
data.boundsRotation = boundsRotation

View file

@ -21,7 +21,9 @@ export default function rotateCommand(
for (let { id, point, rotation } of after.initialShapes) {
const shape = shapes[id]
const utils = getShapeUtils(shape)
utils.rotateTo(shape, rotation).translateTo(shape, point)
utils
.setProperty(shape, 'rotation', rotation)
.setProperty(shape, 'point', point)
}
data.boundsRotation = after.boundsRotation
@ -32,7 +34,9 @@ export default function rotateCommand(
for (let { id, point, rotation } of before.initialShapes) {
const shape = shapes[id]
const utils = getShapeUtils(shape)
utils.rotateTo(shape, rotation).translateTo(shape, point)
utils
.setProperty(shape, 'rotation', rotation)
.setProperty(shape, 'point', point)
}
data.boundsRotation = before.boundsRotation

View file

@ -34,7 +34,7 @@ export default function translateCommand(
for (const { id, point } of initialShapes) {
const shape = shapes[id]
getShapeUtils(shape).translateTo(shape, point)
getShapeUtils(shape).setProperty(shape, 'point', point)
data.selectedIds.add(id)
}
},
@ -52,7 +52,7 @@ export default function translateCommand(
for (const { id, point } of initialShapes) {
const shape = shapes[id]
getShapeUtils(shape).translateTo(shape, point)
getShapeUtils(shape).setProperty(shape, 'point', point)
data.selectedIds.add(id)
}
},

View file

@ -66,7 +66,7 @@ export default class PointsSession extends BaseSession {
getShapeUtils(shape)
.onHandleMove(shape, { end: initialShape.handles.end })
.translateTo(shape, initialShape.point)
.setProperty(shape, 'point', initialShape.point)
}
complete(data: Data) {
@ -95,7 +95,7 @@ export default class PointsSession extends BaseSession {
nextHandles.end.point,
])
.setProperty(shape, 'handles', nextHandles)
.translateTo(shape, newPoint)
.setProperty(shape, 'point', newPoint)
.onHandleMove(shape, nextHandles)
commands.arrow(

View file

@ -25,7 +25,7 @@ export default class BrushSession extends BaseSession {
const page = getPage(data)
const shape = page.shapes[id]
getShapeUtils(shape).translateTo(shape, point)
getShapeUtils(shape).setProperty(shape, 'point', point)
}
update = (data: Data, point: number[], isLocked = false) => {
@ -108,7 +108,7 @@ export default class BrushSession extends BaseSession {
getShapeUtils(shape)
.setProperty(shape, 'points', pts)
.translateTo(shape, vec.add(shape.point, [minX, minY]))
.setProperty(shape, 'point', vec.add(shape.point, [minX, minY]))
}
commands.draw(data, this.snapshot.id, this.points)

View file

@ -60,8 +60,8 @@ export default class RotateSession extends BaseSession {
)
getShapeUtils(shape)
.rotateTo(shape, (PI2 + nextRotation) % PI2)
.translateTo(shape, nextPoint)
.setProperty(shape, 'rotation', (PI2 + nextRotation) % PI2)
.setProperty(shape, 'point', nextPoint)
}
}
@ -70,7 +70,9 @@ export default class RotateSession extends BaseSession {
for (let { id, point, rotation } of this.snapshot.initialShapes) {
const shape = page.shapes[id]
getShapeUtils(shape).rotateTo(shape, rotation).translateTo(shape, point)
getShapeUtils(shape)
.setProperty(shape, 'rotation', rotation)
.setProperty(shape, 'point', point)
}
}

View file

@ -40,7 +40,7 @@ export default class TranslateSession extends BaseSession {
for (const { id, point } of initialShapes) {
const shape = shapes[id]
getShapeUtils(shape).translateTo(shape, point)
getShapeUtils(shape).setProperty(shape, 'point', point)
}
for (const clone of clones) {
@ -51,7 +51,7 @@ export default class TranslateSession extends BaseSession {
for (const { id, point } of clones) {
const shape = shapes[id]
getShapeUtils(shape).translateTo(shape, vec.add(point, delta))
getShapeUtils(shape).setProperty(shape, 'point', vec.add(point, delta))
}
} else {
if (this.isCloning) {
@ -69,7 +69,7 @@ export default class TranslateSession extends BaseSession {
for (const { id, point } of initialShapes) {
const shape = shapes[id]
getShapeUtils(shape).translateTo(shape, vec.add(point, delta))
getShapeUtils(shape).setProperty(shape, 'point', vec.add(point, delta))
}
}
}
@ -80,7 +80,7 @@ export default class TranslateSession extends BaseSession {
for (const { id, point } of initialShapes) {
const shape = shapes[id]
getShapeUtils(shape).translateTo(shape, point)
getShapeUtils(shape).setProperty(shape, 'point', point)
}
for (const { id } of clones) {