cleans up shape utils
This commit is contained in:
parent
9ab86ba9ae
commit
bef35b9207
13 changed files with 134 additions and 415 deletions
|
@ -153,12 +153,6 @@ const arrow = registerShapeUtils<ArrowShape>({
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
applyStyles(shape, style) {
|
|
||||||
Object.assign(shape.style, style)
|
|
||||||
shape.style.isFilled = false
|
|
||||||
return this
|
|
||||||
},
|
|
||||||
|
|
||||||
getBounds(shape) {
|
getBounds(shape) {
|
||||||
if (!this.boundsCache.has(shape)) {
|
if (!this.boundsCache.has(shape)) {
|
||||||
this.boundsCache.set(shape, getBoundsFromPoints(shape.points))
|
this.boundsCache.set(shape, getBoundsFromPoints(shape.points))
|
||||||
|
@ -167,15 +161,6 @@ const arrow = registerShapeUtils<ArrowShape>({
|
||||||
return translateBounds(this.boundsCache.get(shape), shape.point)
|
return translateBounds(this.boundsCache.get(shape), shape.point)
|
||||||
},
|
},
|
||||||
|
|
||||||
getRotatedBounds(shape) {
|
|
||||||
return this.getBounds(shape)
|
|
||||||
},
|
|
||||||
|
|
||||||
getCenter(shape) {
|
|
||||||
const bounds = this.getBounds(shape)
|
|
||||||
return [bounds.minX + bounds.width / 2, bounds.minY + bounds.height / 2]
|
|
||||||
},
|
|
||||||
|
|
||||||
hitTest(shape, point) {
|
hitTest(shape, point) {
|
||||||
const { start, end, bend } = shape.handles
|
const { start, end, bend } = shape.handles
|
||||||
if (shape.bend === 0) {
|
if (shape.bend === 0) {
|
||||||
|
@ -247,16 +232,6 @@ const arrow = registerShapeUtils<ArrowShape>({
|
||||||
return this
|
return this
|
||||||
},
|
},
|
||||||
|
|
||||||
transformSingle(shape, bounds, info) {
|
|
||||||
this.transform(shape, bounds, info)
|
|
||||||
return this
|
|
||||||
},
|
|
||||||
|
|
||||||
setProperty(shape, prop, value) {
|
|
||||||
shape[prop] = value
|
|
||||||
return this
|
|
||||||
},
|
|
||||||
|
|
||||||
onHandleMove(shape, handles) {
|
onHandleMove(shape, handles) {
|
||||||
for (let id in handles) {
|
for (let id in handles) {
|
||||||
const handle = handles[id]
|
const handle = handles[id]
|
||||||
|
@ -293,8 +268,12 @@ const arrow = registerShapeUtils<ArrowShape>({
|
||||||
return this
|
return this
|
||||||
},
|
},
|
||||||
|
|
||||||
canTransform: true,
|
applyStyles(shape, style) {
|
||||||
canChangeAspectRatio: true,
|
Object.assign(shape.style, style)
|
||||||
|
shape.style.isFilled = false
|
||||||
|
return this
|
||||||
|
},
|
||||||
|
|
||||||
canStyleFill: false,
|
canStyleFill: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -43,11 +43,6 @@ const circle = registerShapeUtils<CircleShape>({
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
applyStyles(shape, style) {
|
|
||||||
Object.assign(shape.style, style)
|
|
||||||
return this
|
|
||||||
},
|
|
||||||
|
|
||||||
getBounds(shape) {
|
getBounds(shape) {
|
||||||
if (!this.boundsCache.has(shape)) {
|
if (!this.boundsCache.has(shape)) {
|
||||||
const { radius } = shape
|
const { radius } = shape
|
||||||
|
@ -118,14 +113,7 @@ const circle = registerShapeUtils<CircleShape>({
|
||||||
return this
|
return this
|
||||||
},
|
},
|
||||||
|
|
||||||
setProperty(shape, prop, value) {
|
|
||||||
shape[prop] = value
|
|
||||||
return this
|
|
||||||
},
|
|
||||||
|
|
||||||
canTransform: true,
|
|
||||||
canChangeAspectRatio: false,
|
canChangeAspectRatio: false,
|
||||||
canStyleFill: true,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
export default circle
|
export default circle
|
||||||
|
|
|
@ -37,11 +37,6 @@ const dot = registerShapeUtils<DotShape>({
|
||||||
return <DotCircle id={id} cx={0} cy={0} r={3} />
|
return <DotCircle id={id} cx={0} cy={0} r={3} />
|
||||||
},
|
},
|
||||||
|
|
||||||
applyStyles(shape, style) {
|
|
||||||
Object.assign(shape.style, style)
|
|
||||||
return this
|
|
||||||
},
|
|
||||||
|
|
||||||
getBounds(shape) {
|
getBounds(shape) {
|
||||||
if (!this.boundsCache.has(shape)) {
|
if (!this.boundsCache.has(shape)) {
|
||||||
const bounds = {
|
const bounds = {
|
||||||
|
@ -85,19 +80,8 @@ const dot = registerShapeUtils<DotShape>({
|
||||||
return this
|
return this
|
||||||
},
|
},
|
||||||
|
|
||||||
transformSingle(shape, bounds, info) {
|
|
||||||
this.transform(shape, bounds, info)
|
|
||||||
return this
|
|
||||||
},
|
|
||||||
|
|
||||||
setProperty(shape, prop, value) {
|
|
||||||
shape[prop] = value
|
|
||||||
return this
|
|
||||||
},
|
|
||||||
|
|
||||||
canTransform: false,
|
canTransform: false,
|
||||||
canChangeAspectRatio: false,
|
canChangeAspectRatio: false,
|
||||||
canStyleFill: true,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
export default dot
|
export default dot
|
||||||
|
|
|
@ -70,13 +70,6 @@ const draw = registerShapeUtils<DrawShape>({
|
||||||
return <path id={id} d={pathCache.get(points)} fill={styles.stroke} />
|
return <path id={id} d={pathCache.get(points)} fill={styles.stroke} />
|
||||||
},
|
},
|
||||||
|
|
||||||
applyStyles(shape, style) {
|
|
||||||
Object.assign(shape.style, style)
|
|
||||||
shape.style.isFilled = false
|
|
||||||
shape.style.dash = DashStyle.Solid
|
|
||||||
return this
|
|
||||||
},
|
|
||||||
|
|
||||||
getBounds(shape) {
|
getBounds(shape) {
|
||||||
if (!this.boundsCache.has(shape)) {
|
if (!this.boundsCache.has(shape)) {
|
||||||
const bounds = getBoundsFromPoints(shape.points)
|
const bounds = getBoundsFromPoints(shape.points)
|
||||||
|
@ -161,18 +154,13 @@ const draw = registerShapeUtils<DrawShape>({
|
||||||
return this
|
return this
|
||||||
},
|
},
|
||||||
|
|
||||||
transformSingle(shape, bounds, info) {
|
applyStyles(shape, style) {
|
||||||
this.transform(shape, bounds, info)
|
Object.assign(shape.style, style)
|
||||||
|
shape.style.isFilled = false
|
||||||
|
shape.style.dash = DashStyle.Solid
|
||||||
return this
|
return this
|
||||||
},
|
},
|
||||||
|
|
||||||
setProperty(shape, prop, value) {
|
|
||||||
shape[prop] = value
|
|
||||||
return this
|
|
||||||
},
|
|
||||||
|
|
||||||
canTransform: true,
|
|
||||||
canChangeAspectRatio: true,
|
|
||||||
canStyleFill: false,
|
canStyleFill: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -49,11 +49,6 @@ const ellipse = registerShapeUtils<EllipseShape>({
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
applyStyles(shape, style) {
|
|
||||||
Object.assign(shape.style, style)
|
|
||||||
return this
|
|
||||||
},
|
|
||||||
|
|
||||||
getBounds(shape) {
|
getBounds(shape) {
|
||||||
if (!this.boundsCache.has(shape)) {
|
if (!this.boundsCache.has(shape)) {
|
||||||
const { radiusX, radiusY } = shape
|
const { radiusX, radiusY } = shape
|
||||||
|
@ -130,15 +125,6 @@ const ellipse = registerShapeUtils<EllipseShape>({
|
||||||
transformSingle(shape, bounds, info) {
|
transformSingle(shape, bounds, info) {
|
||||||
return this.transform(shape, bounds, info)
|
return this.transform(shape, bounds, info)
|
||||||
},
|
},
|
||||||
|
|
||||||
setProperty(shape, prop, value) {
|
|
||||||
shape[prop] = value
|
|
||||||
return this
|
|
||||||
},
|
|
||||||
|
|
||||||
canTransform: true,
|
|
||||||
canChangeAspectRatio: true,
|
|
||||||
canStyleFill: true,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
export default ellipse
|
export default ellipse
|
||||||
|
|
|
@ -7,7 +7,11 @@ import {
|
||||||
ShapeStyles,
|
ShapeStyles,
|
||||||
ShapeHandle,
|
ShapeHandle,
|
||||||
ShapeBinding,
|
ShapeBinding,
|
||||||
|
BaseShape,
|
||||||
|
ShapeSpecificProps,
|
||||||
|
Mutable,
|
||||||
} from 'types'
|
} from 'types'
|
||||||
|
import { v4 as uuid } from 'uuid'
|
||||||
import circle from './circle'
|
import circle from './circle'
|
||||||
import dot from './dot'
|
import dot from './dot'
|
||||||
import polyline from './polyline'
|
import polyline from './polyline'
|
||||||
|
@ -17,6 +21,18 @@ import line from './line'
|
||||||
import ray from './ray'
|
import ray from './ray'
|
||||||
import draw from './draw'
|
import draw from './draw'
|
||||||
import arrow from './arrow'
|
import arrow from './arrow'
|
||||||
|
import rectangleUtils from '../shapes/Rectangle'
|
||||||
|
import {
|
||||||
|
getBoundsCenter,
|
||||||
|
getBoundsFromPoints,
|
||||||
|
getRotatedCorners,
|
||||||
|
} from 'utils/utils'
|
||||||
|
import shape from 'components/canvas/shape'
|
||||||
|
import {
|
||||||
|
boundsCollidePolygon,
|
||||||
|
boundsContainPolygon,
|
||||||
|
pointInBounds,
|
||||||
|
} from 'utils/bounds'
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Shape Utiliies
|
Shape Utiliies
|
||||||
|
@ -47,14 +63,14 @@ export interface ShapeUtility<K extends Shape> {
|
||||||
|
|
||||||
applyStyles(
|
applyStyles(
|
||||||
this: ShapeUtility<K>,
|
this: ShapeUtility<K>,
|
||||||
shape: K,
|
shape: Mutable<K>,
|
||||||
style: Partial<ShapeStyles>
|
style: Partial<ShapeStyles>
|
||||||
): ShapeUtility<K>
|
): ShapeUtility<K>
|
||||||
|
|
||||||
// Transform to fit a new bounding box when more than one shape is selected.
|
// Transform to fit a new bounding box when more than one shape is selected.
|
||||||
transform(
|
transform(
|
||||||
this: ShapeUtility<K>,
|
this: ShapeUtility<K>,
|
||||||
shape: K,
|
shape: Mutable<K>,
|
||||||
bounds: Bounds,
|
bounds: Bounds,
|
||||||
info: {
|
info: {
|
||||||
type: Edge | Corner
|
type: Edge | Corner
|
||||||
|
@ -68,7 +84,7 @@ export interface ShapeUtility<K extends Shape> {
|
||||||
// Transform a single shape to fit a new bounding box.
|
// Transform a single shape to fit a new bounding box.
|
||||||
transformSingle(
|
transformSingle(
|
||||||
this: ShapeUtility<K>,
|
this: ShapeUtility<K>,
|
||||||
shape: K,
|
shape: Mutable<K>,
|
||||||
bounds: Bounds,
|
bounds: Bounds,
|
||||||
info: {
|
info: {
|
||||||
type: Edge | Corner
|
type: Edge | Corner
|
||||||
|
@ -81,7 +97,7 @@ export interface ShapeUtility<K extends Shape> {
|
||||||
|
|
||||||
setProperty<P extends keyof K>(
|
setProperty<P extends keyof K>(
|
||||||
this: ShapeUtility<K>,
|
this: ShapeUtility<K>,
|
||||||
shape: K,
|
shape: Mutable<K>,
|
||||||
prop: P,
|
prop: P,
|
||||||
value: K[P]
|
value: K[P]
|
||||||
): ShapeUtility<K>
|
): ShapeUtility<K>
|
||||||
|
@ -89,14 +105,14 @@ export interface ShapeUtility<K extends Shape> {
|
||||||
// Respond when a user moves one of the shape's bound elements.
|
// Respond when a user moves one of the shape's bound elements.
|
||||||
onBindingMove?(
|
onBindingMove?(
|
||||||
this: ShapeUtility<K>,
|
this: ShapeUtility<K>,
|
||||||
shape: K,
|
shape: Mutable<K>,
|
||||||
bindings: Record<string, ShapeBinding>
|
bindings: Record<string, ShapeBinding>
|
||||||
): ShapeUtility<K>
|
): ShapeUtility<K>
|
||||||
|
|
||||||
// Respond when a user moves one of the shape's handles.
|
// Respond when a user moves one of the shape's handles.
|
||||||
onHandleMove?(
|
onHandleMove?(
|
||||||
this: ShapeUtility<K>,
|
this: ShapeUtility<K>,
|
||||||
shape: K,
|
shape: Mutable<K>,
|
||||||
handle: Partial<K['handles']>
|
handle: Partial<K['handles']>
|
||||||
): ShapeUtility<K>
|
): ShapeUtility<K>
|
||||||
|
|
||||||
|
@ -141,15 +157,109 @@ export function getShapeUtils<T extends Shape>(shape: T): ShapeUtility<T> {
|
||||||
return shapeUtilityMap[shape.type] as ShapeUtility<T>
|
return shapeUtilityMap[shape.type] as ShapeUtility<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getDefaultShapeUtil<T extends Shape>(): ShapeUtility<T> {
|
||||||
|
return {
|
||||||
|
boundsCache: new WeakMap(),
|
||||||
|
canTransform: true,
|
||||||
|
canChangeAspectRatio: true,
|
||||||
|
canStyleFill: true,
|
||||||
|
|
||||||
|
create(props) {
|
||||||
|
return {
|
||||||
|
id: uuid(),
|
||||||
|
isGenerated: false,
|
||||||
|
point: [0, 0],
|
||||||
|
name: 'Shape',
|
||||||
|
parentId: 'page0',
|
||||||
|
childIndex: 0,
|
||||||
|
rotation: 0,
|
||||||
|
isAspectRatioLocked: false,
|
||||||
|
isLocked: false,
|
||||||
|
isHidden: false,
|
||||||
|
...props,
|
||||||
|
} as T
|
||||||
|
},
|
||||||
|
|
||||||
|
render(shape) {
|
||||||
|
return <circle id={shape.id} />
|
||||||
|
},
|
||||||
|
|
||||||
|
transform(shape, bounds) {
|
||||||
|
shape.point = [bounds.minX, bounds.minY]
|
||||||
|
return this
|
||||||
|
},
|
||||||
|
|
||||||
|
transformSingle(shape, bounds, info) {
|
||||||
|
return this.transform(shape, bounds, info)
|
||||||
|
},
|
||||||
|
|
||||||
|
onBindingMove() {
|
||||||
|
return this
|
||||||
|
},
|
||||||
|
|
||||||
|
onHandleMove() {
|
||||||
|
return this
|
||||||
|
},
|
||||||
|
|
||||||
|
getBounds(shape) {
|
||||||
|
const [x, y] = shape.point
|
||||||
|
return {
|
||||||
|
minX: x,
|
||||||
|
minY: y,
|
||||||
|
maxX: x + 1,
|
||||||
|
maxY: y + 1,
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getRotatedBounds(shape) {
|
||||||
|
return getBoundsFromPoints(
|
||||||
|
getRotatedCorners(this.getBounds(shape), shape.rotation)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
getCenter(shape) {
|
||||||
|
return getBoundsCenter(this.getBounds(shape))
|
||||||
|
},
|
||||||
|
|
||||||
|
hitTest(shape, point) {
|
||||||
|
return pointInBounds(point, this.getBounds(shape))
|
||||||
|
},
|
||||||
|
|
||||||
|
hitTestBounds(shape, brushBounds) {
|
||||||
|
const rotatedCorners = getRotatedCorners(
|
||||||
|
this.getBounds(shape),
|
||||||
|
shape.rotation
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
boundsContainPolygon(brushBounds, rotatedCorners) ||
|
||||||
|
boundsCollidePolygon(brushBounds, rotatedCorners)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
setProperty(shape, prop, value) {
|
||||||
|
shape[prop] = value
|
||||||
|
return this
|
||||||
|
},
|
||||||
|
|
||||||
|
applyStyles(shape, style) {
|
||||||
|
Object.assign(shape.style, style)
|
||||||
|
return this
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A factory of shape utilities, with typing enforced.
|
* A factory of shape utilities, with typing enforced.
|
||||||
* @param shape
|
* @param shape
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function registerShapeUtils<T extends Shape>(
|
export function registerShapeUtils<K extends Shape>(
|
||||||
shape: ShapeUtility<T>
|
shapeUtil: Partial<ShapeUtility<K>>
|
||||||
): ShapeUtility<T> {
|
): ShapeUtility<K> {
|
||||||
return Object.freeze(shape)
|
return Object.freeze({ ...getDefaultShapeUtil<K>(), ...shapeUtil })
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createShape<T extends Shape>(
|
export function createShape<T extends Shape>(
|
||||||
|
|
|
@ -47,11 +47,6 @@ const line = registerShapeUtils<LineShape>({
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
applyStyles(shape, style) {
|
|
||||||
Object.assign(shape.style, style)
|
|
||||||
return this
|
|
||||||
},
|
|
||||||
|
|
||||||
getBounds(shape) {
|
getBounds(shape) {
|
||||||
if (!this.boundsCache.has(shape)) {
|
if (!this.boundsCache.has(shape)) {
|
||||||
const bounds = {
|
const bounds = {
|
||||||
|
@ -99,11 +94,6 @@ const line = registerShapeUtils<LineShape>({
|
||||||
return this.transform(shape, bounds, info)
|
return this.transform(shape, bounds, info)
|
||||||
},
|
},
|
||||||
|
|
||||||
setProperty(shape, prop, value) {
|
|
||||||
shape[prop] = value
|
|
||||||
return this
|
|
||||||
},
|
|
||||||
|
|
||||||
canTransform: false,
|
canTransform: false,
|
||||||
canChangeAspectRatio: false,
|
canChangeAspectRatio: false,
|
||||||
canStyleFill: false,
|
canStyleFill: false,
|
||||||
|
|
|
@ -33,11 +33,6 @@ const polyline = registerShapeUtils<PolylineShape>({
|
||||||
return <polyline id={id} points={points.toString()} />
|
return <polyline id={id} points={points.toString()} />
|
||||||
},
|
},
|
||||||
|
|
||||||
applyStyles(shape, style) {
|
|
||||||
Object.assign(shape.style, style)
|
|
||||||
return this
|
|
||||||
},
|
|
||||||
|
|
||||||
getBounds(shape) {
|
getBounds(shape) {
|
||||||
if (!this.boundsCache.has(shape)) {
|
if (!this.boundsCache.has(shape)) {
|
||||||
this.boundsCache.set(shape, getBoundsFromPoints(shape.points))
|
this.boundsCache.set(shape, getBoundsFromPoints(shape.points))
|
||||||
|
@ -117,11 +112,6 @@ const polyline = registerShapeUtils<PolylineShape>({
|
||||||
return this
|
return this
|
||||||
},
|
},
|
||||||
|
|
||||||
setProperty(shape, prop, value) {
|
|
||||||
shape[prop] = value
|
|
||||||
return this
|
|
||||||
},
|
|
||||||
|
|
||||||
canTransform: true,
|
canTransform: true,
|
||||||
canChangeAspectRatio: true,
|
canChangeAspectRatio: true,
|
||||||
canStyleFill: false,
|
canStyleFill: false,
|
||||||
|
|
|
@ -45,11 +45,6 @@ const ray = registerShapeUtils<RayShape>({
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
applyStyles(shape, style) {
|
|
||||||
Object.assign(shape.style, style)
|
|
||||||
return this
|
|
||||||
},
|
|
||||||
|
|
||||||
getRotatedBounds(shape) {
|
getRotatedBounds(shape) {
|
||||||
return this.getBounds(shape)
|
return this.getBounds(shape)
|
||||||
},
|
},
|
||||||
|
@ -97,11 +92,6 @@ const ray = registerShapeUtils<RayShape>({
|
||||||
return this.transform(shape, bounds, info)
|
return this.transform(shape, bounds, info)
|
||||||
},
|
},
|
||||||
|
|
||||||
setProperty(shape, prop, value) {
|
|
||||||
shape[prop] = value
|
|
||||||
return this
|
|
||||||
},
|
|
||||||
|
|
||||||
canTransform: false,
|
canTransform: false,
|
||||||
canChangeAspectRatio: false,
|
canChangeAspectRatio: false,
|
||||||
canStyleFill: false,
|
canStyleFill: false,
|
||||||
|
|
|
@ -2,12 +2,7 @@ import { v4 as uuid } from 'uuid'
|
||||||
import * as vec from 'utils/vec'
|
import * as vec from 'utils/vec'
|
||||||
import { RectangleShape, ShapeType } from 'types'
|
import { RectangleShape, ShapeType } from 'types'
|
||||||
import { registerShapeUtils } from './index'
|
import { registerShapeUtils } from './index'
|
||||||
import { boundsCollidePolygon, boundsContainPolygon } from 'utils/bounds'
|
import { translateBounds } from 'utils/utils'
|
||||||
import {
|
|
||||||
getBoundsFromPoints,
|
|
||||||
getRotatedCorners,
|
|
||||||
translateBounds,
|
|
||||||
} from 'utils/utils'
|
|
||||||
import { defaultStyle, getShapeStyle } from 'lib/shape-styles'
|
import { defaultStyle, getShapeStyle } from 'lib/shape-styles'
|
||||||
|
|
||||||
const rectangle = registerShapeUtils<RectangleShape>({
|
const rectangle = registerShapeUtils<RectangleShape>({
|
||||||
|
@ -48,11 +43,6 @@ const rectangle = registerShapeUtils<RectangleShape>({
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
applyStyles(shape, style) {
|
|
||||||
Object.assign(shape.style, style)
|
|
||||||
return this
|
|
||||||
},
|
|
||||||
|
|
||||||
getBounds(shape) {
|
getBounds(shape) {
|
||||||
if (!this.boundsCache.has(shape)) {
|
if (!this.boundsCache.has(shape)) {
|
||||||
const [width, height] = shape.size
|
const [width, height] = shape.size
|
||||||
|
@ -71,33 +61,10 @@ const rectangle = registerShapeUtils<RectangleShape>({
|
||||||
return translateBounds(this.boundsCache.get(shape), shape.point)
|
return translateBounds(this.boundsCache.get(shape), shape.point)
|
||||||
},
|
},
|
||||||
|
|
||||||
getRotatedBounds(shape) {
|
hitTest() {
|
||||||
return getBoundsFromPoints(
|
|
||||||
getRotatedCorners(this.getBounds(shape), shape.rotation)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
|
|
||||||
getCenter(shape) {
|
|
||||||
const bounds = this.getRotatedBounds(shape)
|
|
||||||
return [bounds.minX + bounds.width / 2, bounds.minY + bounds.height / 2]
|
|
||||||
},
|
|
||||||
|
|
||||||
hitTest(shape) {
|
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
|
|
||||||
hitTestBounds(shape, brushBounds) {
|
|
||||||
const rotatedCorners = getRotatedCorners(
|
|
||||||
this.getBounds(shape),
|
|
||||||
shape.rotation
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
|
||||||
boundsContainPolygon(brushBounds, rotatedCorners) ||
|
|
||||||
boundsCollidePolygon(brushBounds, rotatedCorners)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
|
|
||||||
transform(shape, bounds, { initialShape, transformOrigin, scaleX, scaleY }) {
|
transform(shape, bounds, { initialShape, transformOrigin, scaleX, scaleY }) {
|
||||||
if (shape.rotation === 0 && !shape.isAspectRatioLocked) {
|
if (shape.rotation === 0 && !shape.isAspectRatioLocked) {
|
||||||
shape.size = [bounds.width, bounds.height]
|
shape.size = [bounds.width, bounds.height]
|
||||||
|
@ -131,15 +98,6 @@ const rectangle = registerShapeUtils<RectangleShape>({
|
||||||
shape.point = [bounds.minX, bounds.minY]
|
shape.point = [bounds.minX, bounds.minY]
|
||||||
return this
|
return this
|
||||||
},
|
},
|
||||||
|
|
||||||
setProperty(shape, prop, value) {
|
|
||||||
shape[prop] = value
|
|
||||||
return this
|
|
||||||
},
|
|
||||||
|
|
||||||
canTransform: true,
|
|
||||||
canChangeAspectRatio: true,
|
|
||||||
canStyleFill: true,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
export default rectangle
|
export default rectangle
|
||||||
|
|
|
@ -1,115 +0,0 @@
|
||||||
interface Props {
|
|
||||||
name: string
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Core {
|
|
||||||
id: string
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Instance extends Props, Core {}
|
|
||||||
|
|
||||||
const defaults: Props = {
|
|
||||||
name: 'Spot',
|
|
||||||
}
|
|
||||||
|
|
||||||
const core: Core = {
|
|
||||||
id: '0',
|
|
||||||
}
|
|
||||||
|
|
||||||
class ClassInstance<T extends object = {}> implements Instance {
|
|
||||||
id = '0'
|
|
||||||
name = 'Spot'
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
props: Partial<Props> &
|
|
||||||
{ [K in keyof T]: K extends keyof Core ? never : T[K] }
|
|
||||||
) {
|
|
||||||
Object.assign(this, props)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface InstanceConstructor {
|
|
||||||
new <T extends object = {}>(
|
|
||||||
props: Partial<Props> &
|
|
||||||
{ [K in keyof T]: K extends keyof Core ? never : T[K] }
|
|
||||||
): Instance
|
|
||||||
}
|
|
||||||
|
|
||||||
function makeInstance<T extends object = {}>(
|
|
||||||
props: Partial<Props> &
|
|
||||||
{ [K in keyof T]: K extends keyof Core ? never : T[K] } &
|
|
||||||
ThisType<ClassInstance>
|
|
||||||
) {
|
|
||||||
return new ClassInstance<T>({ ...defaults, ...props, ...core })
|
|
||||||
}
|
|
||||||
|
|
||||||
function getInstance<T extends object = {}>(
|
|
||||||
props: Partial<Props> &
|
|
||||||
{ [K in keyof T]: K extends keyof Core ? never : T[K] }
|
|
||||||
) {
|
|
||||||
return { ...defaults, ...props, ...core }
|
|
||||||
}
|
|
||||||
|
|
||||||
const instance = getInstance({
|
|
||||||
name: 'Steve',
|
|
||||||
age: 93,
|
|
||||||
wag(this: Instance) {
|
|
||||||
return this.name
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
interface AnimalProps {
|
|
||||||
name: string
|
|
||||||
greet(this: Animal, name: string): string
|
|
||||||
}
|
|
||||||
|
|
||||||
interface AnimalCore {
|
|
||||||
id: string
|
|
||||||
sleep(this: Animal): void
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Animal extends AnimalProps, AnimalCore {}
|
|
||||||
|
|
||||||
const getAnimal = <T extends object>(
|
|
||||||
props: Partial<AnimalProps> &
|
|
||||||
{ [K in keyof T]: K extends keyof AnimalCore ? never : T[K] }
|
|
||||||
): Animal & T => {
|
|
||||||
return {
|
|
||||||
// Defaults
|
|
||||||
name: 'Animal',
|
|
||||||
greet(name) {
|
|
||||||
return 'Hey ' + name
|
|
||||||
},
|
|
||||||
// Overrides
|
|
||||||
...props,
|
|
||||||
// Core
|
|
||||||
id: 'hi',
|
|
||||||
sleep() {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const dog = getAnimal({
|
|
||||||
name: 'doggo',
|
|
||||||
greet(name) {
|
|
||||||
return 'Woof ' + this.name
|
|
||||||
},
|
|
||||||
wag() {
|
|
||||||
return 'wagging...'
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
dog.greet('steve')
|
|
||||||
dog.wag()
|
|
||||||
dog.sleep()
|
|
||||||
|
|
||||||
class ShapeTest {}
|
|
||||||
|
|
||||||
const shapeTest = new ShapeTest()
|
|
||||||
|
|
||||||
export default shapeTest
|
|
||||||
|
|
||||||
type Greet = (name: string) => string
|
|
||||||
|
|
||||||
const greet: Greet = (name: string | number) => {
|
|
||||||
return 'hello ' + name
|
|
||||||
}
|
|
|
@ -1,131 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
2
types.ts
2
types.ts
|
@ -395,3 +395,5 @@ export type CodeControl =
|
||||||
export type PropsOfType<T extends object, K> = {
|
export type PropsOfType<T extends object, K> = {
|
||||||
[K in keyof T]: T[K] extends boolean ? K : never
|
[K in keyof T]: T[K] extends boolean ? K : never
|
||||||
}[keyof T]
|
}[keyof T]
|
||||||
|
|
||||||
|
export type Mutable<T extends Shape> = { -readonly [K in keyof T]: T[K] }
|
||||||
|
|
Loading…
Reference in a new issue