123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418 |
- import {
- ColorStyle,
- DashStyle,
- Mutable,
- Shape,
- ShapeUtility,
- SizeStyle,
- } from 'types'
- import { createShape, getShapeUtils } from 'state/shape-utils'
- import { uniqueId } from 'utils'
- import Vec from 'utils/vec'
-
- export const codeShapes = new Set<CodeShape<Shape>>([])
-
- function getOrderedShapes() {
- return Array.from(codeShapes.values()).sort(
- (a, b) => a.shape.childIndex - b.shape.childIndex
- )
- }
-
- /**
- * 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
- */
-
- /* ----------------- Start Copy Here ---------------- */
-
- export default class CodeShape<T extends Shape> {
- private _shape: Mutable<T>
- protected utils: ShapeUtility<T>
-
- constructor(props: T) {
- this._shape = createShape(props.type, props) as Mutable<T>
- this.utils = getShapeUtils<T>(this._shape)
- codeShapes.add(this)
- }
-
- /**
- * Destroy the shape.
- *
- * ```ts
- * shape.destroy()
- * ```
- */
- destroy = (): void => {
- codeShapes.delete(this)
- }
-
- /**
- * Move the shape to a point.
- *
- * ```ts
- * shape.moveTo(100,100)
- * ```
- */
- moveTo = (point: number[]): CodeShape<T> => {
- return this.translateTo(point)
- }
-
- /**
- * Move the shape to a point.
- *
- * ```ts
- * shape.translateTo([100,100])
- * ```
- */
- translateTo = (point: number[]): CodeShape<T> => {
- this.utils.translateTo(this._shape, point)
- return this
- }
-
- /**
- * Move the shape by a delta.
- *
- * ```ts
- * shape.translateBy([100,100])
- * ```
- */
- translateBy = (delta: number[]): CodeShape<T> => {
- this.utils.translateTo(this._shape, delta)
- return this
- }
-
- /**
- * Rotate the shape.
- *
- * ```ts
- * shape.rotateTo(Math.PI / 2)
- * ```
- */
- rotateTo = (rotation: number): CodeShape<T> => {
- this.utils.rotateTo(this._shape, rotation, this.shape.rotation - rotation)
- return this
- }
-
- /**
- * Rotate the shape by a delta.
- *
- * ```ts
- * shape.rotateBy(Math.PI / 2)
- * ```
- */
- rotateBy = (rotation: number): CodeShape<T> => {
- this.utils.rotateBy(this._shape, rotation)
- return this
- }
-
- /**
- * Get the shape's bounding box.
- *
- * ```ts
- * const bounds = shape.getBounds()
- * ```
- */
- getBounds = (): CodeShape<T> => {
- this.utils.getBounds(this.shape)
- return this
- }
-
- /**
- * Test whether a point is inside of the shape.
- *
- * ```ts
- * const isHit = shape.hitTest()
- * ```
- */
- hitTest = (point: number[]): CodeShape<T> => {
- this.utils.hitTest(this.shape, point)
- return this
- }
-
- /**
- * Duplicate this shape.
- *
- * ```ts
- * const shapeB = shape.duplicate()
- * ```
- */
- duplicate = (): CodeShape<T> => {
- const duplicate = Object.assign(
- Object.create(Object.getPrototypeOf(this)),
- this
- )
-
- duplicate._shape = createShape(this._shape.type, {
- ...this._shape,
- id: uniqueId(),
- } as any)
-
- codeShapes.add(duplicate)
- return duplicate
- }
-
- /**
- * Move the shape to the back of the painting order.
- *
- * ```ts
- * shape.moveToBack()
- * ```
- */
- moveToBack = (): CodeShape<T> => {
- const sorted = getOrderedShapes()
-
- if (sorted.length <= 1) return
-
- const first = sorted[0].childIndex
- sorted.forEach((shape) => shape.childIndex++)
- this.childIndex = first
-
- codeShapes.clear()
- sorted.forEach((shape) => codeShapes.add(shape))
-
- return this
- }
-
- /**
- * Move the shape to the top of the painting order.
- *
- * ```ts
- * shape.moveToFront()
- * ```
- */
- moveToFront = (): CodeShape<T> => {
- const sorted = getOrderedShapes()
-
- if (sorted.length <= 1) return
-
- const ahead = sorted.slice(sorted.indexOf(this))
- const last = ahead[ahead.length - 1].childIndex
- ahead.forEach((shape) => shape.childIndex--)
- this.childIndex = last
-
- codeShapes.clear()
- sorted.forEach((shape) => codeShapes.add(shape))
-
- return this
- }
-
- /**
- * Move the shape backward in the painting order.
- *
- * ```ts
- * shape.moveBackward()
- * ```
- */
- moveBackward = (): CodeShape<T> => {
- const sorted = getOrderedShapes()
-
- if (sorted.length <= 1) return
-
- const next = sorted[sorted.indexOf(this) - 1]
-
- if (!next) return
-
- const index = next.childIndex
- next.childIndex = this.childIndex
- this.childIndex = index
-
- codeShapes.clear()
- sorted.forEach((shape) => codeShapes.add(shape))
-
- return this
- }
-
- /**
- * Move the shape forward in the painting order.
- *
- * ```ts
- * shape.moveForward()
- * ```
- */
- moveForward = (): CodeShape<T> => {
- const sorted = getOrderedShapes()
-
- if (sorted.length <= 1) return
-
- const next = sorted[sorted.indexOf(this) + 1]
-
- if (!next) return
-
- const index = next.childIndex
- next.childIndex = this.childIndex
- this.childIndex = index
-
- codeShapes.clear()
- sorted.forEach((shape) => codeShapes.add(shape))
-
- return this
- }
-
- get id(): string {
- return this._shape.id
- }
-
- /**
- * The shape's underlying shape (readonly).
- *
- * ```ts
- * const underlyingShape = shape.shape
- * ```
- */
- get shape(): Readonly<T> {
- return this._shape
- }
-
- /**
- * The shape's current point.
- *
- * ```ts
- * const shapePoint = shape.point()
- * ```
- */
- get point(): number[] {
- return [...this.shape.point]
- }
-
- set point(point: number[]) {
- this.utils.translateTo(this._shape, point)
- }
-
- /**
- * The shape's current x position.
- *
- * ```ts
- * const shapeX = shape.x
- *
- * shape.x = 100
- * ```
- */
- get x(): number {
- return this.point[0]
- }
-
- set x(x: number) {
- this.utils.translateTo(this._shape, [x, this.y])
- }
-
- /**
- * The shape's current y position.
- *
- * ```ts
- * const shapeY = shape.y
- *
- * shape.y = 100
- * ```
- */
- get y(): number {
- return this.point[1]
- }
-
- set y(y: number) {
- this.utils.translateTo(this._shape, [this.x, y])
- }
-
- /**
- * The shape's rotation.
- *
- * ```ts
- * const shapeRotation = shape.rotation
- *
- * shape.rotation = Math.PI / 2
- * ```
- */
- get rotation(): number {
- return this.shape.rotation
- }
-
- set rotation(rotation: number) {
- this.utils.rotateTo(this._shape, rotation, rotation - this.shape.rotation)
- }
-
- /**
- * The shape's color style (ColorStyle).
- *
- * ```ts
- * const shapeColor = shape.color
- *
- * shape.color = ColorStyle.Red
- * ```
- */
- get color(): ColorStyle {
- return this.shape.style.color
- }
-
- set color(color: ColorStyle) {
- this.utils.applyStyles(this._shape, { color })
- }
-
- /**
- * The shape's dash style (DashStyle).
- *
- * ```ts
- * const shapeDash = shape.dash
- *
- * shape.dash = DashStyle.Dotted
- * ```
- */
- get dash(): DashStyle {
- return this.shape.style.dash
- }
-
- set dash(dash: DashStyle) {
- this.utils.applyStyles(this._shape, { dash })
- }
-
- /**
- * The shape's size (SizeStyle).
- *
- * ```ts
- * const shapeSize = shape.size
- *
- * shape.size = SizeStyle.Large
- * ```
- */
- get size(): SizeStyle {
- return this.shape.style.size
- }
-
- set size(size: SizeStyle) {
- this.utils.applyStyles(this._shape, { size })
- }
-
- /**
- * The shape's index in the painting order.
- *
- * ```ts
- * const shapeChildIndex = shape.childIndex
- *
- * shape.childIndex = 10
- * ```
- */
- get childIndex(): number {
- return this.shape.childIndex
- }
-
- set childIndex(childIndex: number) {
- this.utils.setProperty(this._shape, 'childIndex', childIndex)
- }
-
- /**
- * The shape's center.
- *
- * ```ts
- * const shapeCenter = shape.center
- *
- * shape.center = [100, 100]
- * ```
- */
- get center(): number[] {
- return this.utils.getCenter(this.shape)
- }
-
- set center(center: number[]) {
- const oldCenter = this.utils.getCenter(this.shape)
- const delta = Vec.sub(center, oldCenter)
- this.translateBy(delta)
- }
- }
|