144 lines
3.7 KiB
TypeScript
144 lines
3.7 KiB
TypeScript
import {
|
|
Bounds,
|
|
BoundsSnapshot,
|
|
Shape,
|
|
Shapes,
|
|
ShapeType,
|
|
Corner,
|
|
Edge,
|
|
ShapeByType,
|
|
} from "types"
|
|
import circle from "./circle"
|
|
import dot from "./dot"
|
|
import polyline from "./polyline"
|
|
import rectangle from "./rectangle"
|
|
import ellipse from "./ellipse"
|
|
import line from "./line"
|
|
import ray from "./ray"
|
|
|
|
/*
|
|
Shape Utiliies
|
|
|
|
A shape utility is an object containing utility methods for each type of shape
|
|
in the application. While shapes may be very different, each shape must support
|
|
a common set of utility methods, such as hit tests or translations, that
|
|
|
|
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>> {
|
|
// 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
|
|
|
|
// 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>,
|
|
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
|
|
|
|
// Get the routated bounds of the a shape.
|
|
getRotatedBounds(this: ShapeUtility<K>, shape: K): Bounds
|
|
|
|
// Get the center of the shape
|
|
getCenter(this: ShapeUtility<K>, shape: K): number[]
|
|
|
|
// Test whether a point lies within a shape.
|
|
hitTest(this: ShapeUtility<K>, shape: K, test: number[]): boolean
|
|
|
|
// Test whether bounds collide with or contain a shape.
|
|
hitTestBounds(this: ShapeUtility<K>, shape: K, bounds: Bounds): boolean
|
|
}
|
|
|
|
// A mapping of shape types to shape utilities.
|
|
const shapeUtilityMap: Record<ShapeType, ShapeUtility<Shape>> = {
|
|
[ShapeType.Circle]: circle,
|
|
[ShapeType.Dot]: dot,
|
|
[ShapeType.Polyline]: polyline,
|
|
[ShapeType.Rectangle]: rectangle,
|
|
[ShapeType.Ellipse]: ellipse,
|
|
[ShapeType.Line]: line,
|
|
[ShapeType.Ray]: ray,
|
|
}
|
|
|
|
/**
|
|
* A helper to retrieve a shape utility based on a shape object.
|
|
* @param shape
|
|
* @returns
|
|
*/
|
|
export function getShapeUtils<T extends Shape>(shape: T): ShapeUtility<T> {
|
|
return shapeUtilityMap[shape.type] as ShapeUtility<T>
|
|
}
|
|
|
|
/**
|
|
* A factory of shape utilities, with typing enforced.
|
|
* @param shape
|
|
* @returns
|
|
*/
|
|
export function registerShapeUtils<T extends Shape>(
|
|
shape: ShapeUtility<T>
|
|
): ShapeUtility<T> {
|
|
return Object.freeze(shape)
|
|
}
|
|
|
|
export function createShape<T extends Shape>(
|
|
type: T["type"],
|
|
props: Partial<T>
|
|
) {
|
|
return shapeUtilityMap[type].create(props) as T
|
|
}
|
|
|
|
export default shapeUtilityMap
|