2021-06-24 22:09:36 +00:00
|
|
|
import {
|
|
|
|
ColorStyle,
|
|
|
|
DashStyle,
|
|
|
|
Mutable,
|
|
|
|
Shape,
|
|
|
|
ShapeUtility,
|
|
|
|
SizeStyle,
|
|
|
|
} from 'types'
|
2021-06-21 21:35:28 +00:00
|
|
|
import { createShape, getShapeUtils } from 'state/shape-utils'
|
2021-06-24 22:09:36 +00:00
|
|
|
import { setToArray } from 'utils'
|
2021-05-14 22:56:41 +00:00
|
|
|
|
2021-05-15 13:02:13 +00:00
|
|
|
export const codeShapes = new Set<CodeShape<Shape>>([])
|
|
|
|
|
2021-06-24 22:09:36 +00:00
|
|
|
function getOrderedShapes() {
|
|
|
|
return setToArray(codeShapes).sort(
|
|
|
|
(a, b) => a.shape.childIndex - b.shape.childIndex
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2021-05-15 13:02:13 +00:00
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
*/
|
2021-05-14 22:56:41 +00:00
|
|
|
export default class CodeShape<T extends Shape> {
|
2021-06-06 11:05:51 +00:00
|
|
|
private _shape: Mutable<T>
|
2021-05-25 09:09:51 +00:00
|
|
|
private utils: ShapeUtility<T>
|
2021-05-14 22:56:41 +00:00
|
|
|
|
|
|
|
constructor(props: T) {
|
2021-06-06 11:05:51 +00:00
|
|
|
this._shape = createShape(props.type, props) as Mutable<T>
|
2021-05-25 09:09:51 +00:00
|
|
|
this.utils = getShapeUtils<T>(this._shape)
|
2021-05-15 13:02:13 +00:00
|
|
|
codeShapes.add(this)
|
2021-05-14 22:56:41 +00:00
|
|
|
}
|
|
|
|
|
2021-06-23 22:32:21 +00:00
|
|
|
export(): Mutable<T> {
|
|
|
|
return { ...this._shape }
|
|
|
|
}
|
|
|
|
|
2021-06-24 22:09:36 +00:00
|
|
|
/**
|
|
|
|
* Destroy the shape.
|
|
|
|
*/
|
2021-06-21 21:35:28 +00:00
|
|
|
destroy(): void {
|
2021-05-15 13:02:13 +00:00
|
|
|
codeShapes.delete(this)
|
2021-05-14 22:56:41 +00:00
|
|
|
}
|
|
|
|
|
2021-06-24 22:09:36 +00:00
|
|
|
/**
|
|
|
|
* Move the shape to a point.
|
|
|
|
* @param delta
|
|
|
|
*/
|
2021-06-23 22:32:21 +00:00
|
|
|
moveTo(point: number[]): CodeShape<T> {
|
2021-06-24 22:09:36 +00:00
|
|
|
return this.translateTo(point)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Move the shape to a point.
|
|
|
|
* @param delta
|
|
|
|
*/
|
|
|
|
translateTo(point: number[]): CodeShape<T> {
|
|
|
|
this.utils.translateTo(this._shape, point)
|
2021-05-25 09:00:59 +00:00
|
|
|
return this
|
2021-05-14 22:56:41 +00:00
|
|
|
}
|
|
|
|
|
2021-06-24 22:09:36 +00:00
|
|
|
/**
|
|
|
|
* Move the shape by a delta.
|
|
|
|
* @param delta
|
|
|
|
*/
|
|
|
|
translateBy(delta: number[]): CodeShape<T> {
|
|
|
|
this.utils.translateTo(this._shape, delta)
|
|
|
|
return this
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Rotate the shape.
|
|
|
|
*/
|
|
|
|
rotateTo(rotation: number): CodeShape<T> {
|
|
|
|
this.utils.rotateTo(this._shape, rotation, this.shape.rotation - rotation)
|
2021-05-25 09:00:59 +00:00
|
|
|
return this
|
2021-05-14 22:56:41 +00:00
|
|
|
}
|
|
|
|
|
2021-06-24 22:09:36 +00:00
|
|
|
/**
|
|
|
|
* Rotate the shape by a delta.
|
|
|
|
*/
|
|
|
|
rotateBy(rotation: number): CodeShape<T> {
|
|
|
|
this.utils.rotateBy(this._shape, rotation)
|
2021-05-25 09:00:59 +00:00
|
|
|
return this
|
2021-05-14 22:56:41 +00:00
|
|
|
}
|
|
|
|
|
2021-06-24 22:09:36 +00:00
|
|
|
/**
|
|
|
|
* Get the shape's bounding box.
|
|
|
|
*/
|
2021-06-21 21:35:28 +00:00
|
|
|
getBounds(): CodeShape<T> {
|
2021-05-25 09:00:59 +00:00
|
|
|
this.utils.getBounds(this.shape)
|
|
|
|
return this
|
2021-05-14 22:56:41 +00:00
|
|
|
}
|
|
|
|
|
2021-06-24 22:09:36 +00:00
|
|
|
/**
|
|
|
|
* Test whether a point is inside of the shape.
|
|
|
|
*/
|
2021-06-23 22:32:21 +00:00
|
|
|
hitTest(point: number[]): CodeShape<T> {
|
|
|
|
this.utils.hitTest(this.shape, point)
|
2021-05-25 09:00:59 +00:00
|
|
|
return this
|
2021-05-14 22:56:41 +00:00
|
|
|
}
|
|
|
|
|
2021-06-24 22:09:36 +00:00
|
|
|
/**
|
|
|
|
* Move the shape to the back of the painting order.
|
|
|
|
*/
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The shape's underlying shape.
|
|
|
|
*/
|
2021-06-21 21:35:28 +00:00
|
|
|
get shape(): T {
|
2021-05-14 22:56:41 +00:00
|
|
|
return this._shape
|
|
|
|
}
|
|
|
|
|
2021-06-24 22:09:36 +00:00
|
|
|
/**
|
|
|
|
* The shape's current point.
|
|
|
|
*/
|
2021-06-21 21:35:28 +00:00
|
|
|
get point(): number[] {
|
2021-05-14 22:56:41 +00:00
|
|
|
return [...this.shape.point]
|
|
|
|
}
|
|
|
|
|
2021-06-24 22:09:36 +00:00
|
|
|
set point(point: number[]) {
|
|
|
|
getShapeUtils(this.shape).translateTo(this._shape, point)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The shape's rotation.
|
|
|
|
*/
|
2021-06-21 21:35:28 +00:00
|
|
|
get rotation(): number {
|
2021-05-14 22:56:41 +00:00
|
|
|
return this.shape.rotation
|
|
|
|
}
|
2021-06-24 22:09:36 +00:00
|
|
|
|
|
|
|
set rotation(rotation: number) {
|
|
|
|
getShapeUtils(this.shape).rotateTo(
|
|
|
|
this._shape,
|
|
|
|
rotation,
|
|
|
|
rotation - this.shape.rotation
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The shape's color style.
|
|
|
|
*/
|
|
|
|
get color(): ColorStyle {
|
|
|
|
return this.shape.style.color
|
|
|
|
}
|
|
|
|
|
|
|
|
set color(color: ColorStyle) {
|
|
|
|
getShapeUtils(this.shape).applyStyles(this._shape, { color })
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The shape's dash style.
|
|
|
|
*/
|
|
|
|
get dash(): DashStyle {
|
|
|
|
return this.shape.style.dash
|
|
|
|
}
|
|
|
|
|
|
|
|
set dash(dash: DashStyle) {
|
|
|
|
getShapeUtils(this.shape).applyStyles(this._shape, { dash })
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The shape's stroke width.
|
|
|
|
*/
|
|
|
|
get strokeWidth(): SizeStyle {
|
|
|
|
return this.shape.style.size
|
|
|
|
}
|
|
|
|
|
|
|
|
set strokeWidth(size: SizeStyle) {
|
|
|
|
getShapeUtils(this.shape).applyStyles(this._shape, { size })
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The shape's index in the painting order.
|
|
|
|
*/
|
|
|
|
get childIndex(): number {
|
|
|
|
return this.shape.childIndex
|
|
|
|
}
|
|
|
|
|
|
|
|
set childIndex(childIndex: number) {
|
|
|
|
getShapeUtils(this.shape).setProperty(this._shape, 'childIndex', childIndex)
|
|
|
|
}
|
2021-05-14 22:56:41 +00:00
|
|
|
}
|