Adds lines, improves transforms
This commit is contained in:
parent
b50045c9b7
commit
b8d3b35b07
14 changed files with 260 additions and 125 deletions
|
@ -27,7 +27,7 @@ export default function Bounds() {
|
|||
height={height}
|
||||
pointerEvents="none"
|
||||
/>
|
||||
{width * zoom > 8 && (
|
||||
{width * zoom > 8 && height * zoom > 8 && (
|
||||
<>
|
||||
<EdgeHorizontal
|
||||
x={minX + p}
|
||||
|
|
|
@ -7,13 +7,12 @@ import styled from "styles"
|
|||
function Shape({ id }: { id: string }) {
|
||||
const rGroup = useRef<SVGGElement>(null)
|
||||
|
||||
const shape = useSelector(
|
||||
({ data: { currentPageId, document } }) =>
|
||||
document.pages[currentPageId].shapes[id]
|
||||
)
|
||||
|
||||
const isSelected = useSelector((state) => state.values.selectedIds.has(id))
|
||||
|
||||
const shape = useSelector(
|
||||
({ data }) => data.document.pages[data.currentPageId].shapes[id]
|
||||
)
|
||||
|
||||
const handlePointerDown = useCallback(
|
||||
(e: React.PointerEvent) => {
|
||||
e.stopPropagation()
|
||||
|
@ -33,12 +32,12 @@ function Shape({ id }: { id: string }) {
|
|||
)
|
||||
|
||||
const handlePointerEnter = useCallback(
|
||||
(e: React.PointerEvent) => state.send("HOVERED_SHAPE", { id }),
|
||||
() => state.send("HOVERED_SHAPE", { id }),
|
||||
[id]
|
||||
)
|
||||
|
||||
const handlePointerLeave = useCallback(
|
||||
(e: React.PointerEvent) => state.send("UNHOVERED_SHAPE", { id }),
|
||||
() => state.send("UNHOVERED_SHAPE", { id }),
|
||||
[id]
|
||||
)
|
||||
return (
|
||||
|
|
19
lib/shapes/base-shape.tsx
Normal file
19
lib/shapes/base-shape.tsx
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { Bounds, Shape } from "types"
|
||||
|
||||
export default interface ShapeUtil<K extends Shape> {
|
||||
create(props: Partial<K>): K
|
||||
getBounds(this: ShapeUtil<K>, shape: K): Bounds
|
||||
hitTest(this: ShapeUtil<K>, shape: K, test: number[]): boolean
|
||||
hitTestBounds(this: ShapeUtil<K>, shape: K, bounds: Bounds): boolean
|
||||
rotate(this: ShapeUtil<K>, shape: K): K
|
||||
translate(this: ShapeUtil<K>, shape: K, delta: number[]): K
|
||||
scale(this: ShapeUtil<K>, shape: K, scale: number): K
|
||||
stretch(this: ShapeUtil<K>, shape: K, scaleX: number, scaleY: number): K
|
||||
render(this: ShapeUtil<K>, shape: K): JSX.Element
|
||||
}
|
||||
|
||||
export function createShape<T extends Shape>(
|
||||
shape: ShapeUtil<T>
|
||||
): ShapeUtil<T> {
|
||||
return shape
|
||||
}
|
|
@ -88,8 +88,12 @@ const circle = createShape<CircleShape>({
|
|||
},
|
||||
|
||||
transform(shape, bounds) {
|
||||
shape.point = [bounds.minX, bounds.minY]
|
||||
// shape.point = [bounds.minX, bounds.minY]
|
||||
shape.radius = Math.min(bounds.width, bounds.height) / 2
|
||||
shape.point = [
|
||||
bounds.minX + bounds.width / 2 - shape.radius,
|
||||
bounds.minY + bounds.height / 2 - shape.radius,
|
||||
]
|
||||
|
||||
return shape
|
||||
},
|
||||
|
|
|
@ -41,7 +41,14 @@ export interface ShapeUtility<K extends Shape> {
|
|||
translate(this: ShapeUtility<K>, shape: K, delta: number[]): K
|
||||
|
||||
// Transform to fit a new bounding box.
|
||||
transform(this: ShapeUtility<K>, shape: K, bounds: Bounds): K
|
||||
transform(
|
||||
this: ShapeUtility<K>,
|
||||
shape: K,
|
||||
bounds: Bounds & { isFlippedX: boolean; isFlippedY: boolean },
|
||||
initialShape: K,
|
||||
initialShapeBounds: BoundsSnapshot,
|
||||
initialBounds: Bounds
|
||||
): K
|
||||
|
||||
// Apply a scale to a shape.
|
||||
scale(this: ShapeUtility<K>, shape: K, scale: number): K
|
||||
|
|
|
@ -16,15 +16,23 @@ const line = createShape<LineShape>({
|
|||
parentId: "page0",
|
||||
childIndex: 0,
|
||||
point: [0, 0],
|
||||
vector: [0, 0],
|
||||
direction: [0, 0],
|
||||
rotation: 0,
|
||||
style: {},
|
||||
...props,
|
||||
}
|
||||
},
|
||||
|
||||
render({ id }) {
|
||||
return <circle id={id} cx={4} cy={4} r={4} />
|
||||
render({ id, direction }) {
|
||||
const [x1, y1] = vec.add([0, 0], vec.mul(direction, 100000))
|
||||
const [x2, y2] = vec.sub([0, 0], vec.mul(direction, 100000))
|
||||
|
||||
return (
|
||||
<g id={id}>
|
||||
<line x1={x1} y1={y1} x2={x2} y2={y2} />
|
||||
<circle cx={0} cy={0} r={4} />
|
||||
</g>
|
||||
)
|
||||
},
|
||||
|
||||
getBounds(shape) {
|
||||
|
@ -38,11 +46,11 @@ const line = createShape<LineShape>({
|
|||
|
||||
const bounds = {
|
||||
minX: x,
|
||||
maxX: x + 8,
|
||||
maxX: x + 1,
|
||||
minY: y,
|
||||
maxY: y + 8,
|
||||
width: 8,
|
||||
height: 8,
|
||||
maxY: y + 1,
|
||||
width: 1,
|
||||
height: 1,
|
||||
}
|
||||
|
||||
this.boundsCache.set(shape, bounds)
|
||||
|
@ -55,11 +63,7 @@ const line = createShape<LineShape>({
|
|||
},
|
||||
|
||||
hitTestBounds(this, shape, brushBounds) {
|
||||
const shapeBounds = this.getBounds(shape)
|
||||
return (
|
||||
boundsContained(shapeBounds, brushBounds) ||
|
||||
intersectCircleBounds(shape.point, 4, brushBounds).length > 0
|
||||
)
|
||||
return true
|
||||
},
|
||||
|
||||
rotate(shape) {
|
||||
|
@ -80,6 +84,8 @@ const line = createShape<LineShape>({
|
|||
},
|
||||
|
||||
transform(shape, bounds) {
|
||||
shape.point = [bounds.minX, bounds.minY]
|
||||
|
||||
return shape
|
||||
},
|
||||
})
|
||||
|
|
|
@ -90,15 +90,20 @@ const polyline = createShape<PolylineShape>({
|
|||
return shape
|
||||
},
|
||||
|
||||
transform(shape, bounds) {
|
||||
const currentBounds = this.getBounds(shape)
|
||||
transform(shape, bounds, initialShape, initialShapeBounds) {
|
||||
shape.points = shape.points.map((_, i) => {
|
||||
const [x, y] = initialShape.points[i]
|
||||
|
||||
const scaleX = bounds.width / currentBounds.width
|
||||
const scaleY = bounds.height / currentBounds.height
|
||||
|
||||
shape.points = shape.points.map((point) => {
|
||||
let pt = vec.mulV(point, [scaleX, scaleY])
|
||||
return pt
|
||||
return [
|
||||
bounds.width *
|
||||
(bounds.isFlippedX
|
||||
? 1 - x / initialShapeBounds.width
|
||||
: x / initialShapeBounds.width),
|
||||
bounds.height *
|
||||
(bounds.isFlippedY
|
||||
? 1 - y / initialShapeBounds.height
|
||||
: y / initialShapeBounds.height),
|
||||
]
|
||||
})
|
||||
|
||||
shape.point = [bounds.minX, bounds.minY]
|
||||
|
|
|
@ -16,7 +16,7 @@ const ray = createShape<RayShape>({
|
|||
parentId: "page0",
|
||||
childIndex: 0,
|
||||
point: [0, 0],
|
||||
vector: [0, 0],
|
||||
direction: [0, 0],
|
||||
rotation: 0,
|
||||
style: {},
|
||||
...props,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import translate from "./translate-command"
|
||||
import transform from "./transform-command"
|
||||
|
||||
const commands = { translate }
|
||||
const commands = { translate, transform }
|
||||
|
||||
export default commands
|
||||
|
|
67
state/commands/transform-command.ts
Normal file
67
state/commands/transform-command.ts
Normal file
|
@ -0,0 +1,67 @@
|
|||
import Command from "./command"
|
||||
import history from "../history"
|
||||
import { Data } from "types"
|
||||
import { TransformSnapshot } from "state/sessions/transform-session"
|
||||
import { getShapeUtils } from "lib/shapes"
|
||||
|
||||
export default function translateCommand(
|
||||
data: Data,
|
||||
before: TransformSnapshot,
|
||||
after: TransformSnapshot
|
||||
) {
|
||||
history.execute(
|
||||
data,
|
||||
new Command({
|
||||
name: "translate_shapes",
|
||||
category: "canvas",
|
||||
do(data) {
|
||||
const { shapeBounds, initialBounds, currentPageId, selectedIds } = after
|
||||
const { shapes } = data.document.pages[currentPageId]
|
||||
|
||||
selectedIds.forEach((id) => {
|
||||
const { initialShape, initialShapeBounds } = shapeBounds[id]
|
||||
const shape = shapes[id]
|
||||
|
||||
getShapeUtils(shape).transform(
|
||||
shape,
|
||||
{
|
||||
...initialShapeBounds,
|
||||
isFlippedX: false,
|
||||
isFlippedY: false,
|
||||
},
|
||||
initialShape,
|
||||
initialShapeBounds,
|
||||
initialBounds
|
||||
)
|
||||
})
|
||||
},
|
||||
undo(data) {
|
||||
const {
|
||||
shapeBounds,
|
||||
initialBounds,
|
||||
currentPageId,
|
||||
selectedIds,
|
||||
} = before
|
||||
|
||||
const { shapes } = data.document.pages[currentPageId]
|
||||
|
||||
selectedIds.forEach((id) => {
|
||||
const { initialShape, initialShapeBounds } = shapeBounds[id]
|
||||
const shape = shapes[id]
|
||||
|
||||
getShapeUtils(shape).transform(
|
||||
shape,
|
||||
{
|
||||
...initialShapeBounds,
|
||||
isFlippedX: false,
|
||||
isFlippedY: false,
|
||||
},
|
||||
initialShape,
|
||||
initialShapeBounds,
|
||||
initialBounds
|
||||
)
|
||||
})
|
||||
},
|
||||
})
|
||||
)
|
||||
}
|
|
@ -15,7 +15,7 @@ export const defaultDocument: Data["document"] = {
|
|||
childIndex: 3,
|
||||
point: [500, 100],
|
||||
style: {
|
||||
fill: "#aaa",
|
||||
fill: "#AAA",
|
||||
stroke: "#777",
|
||||
strokeWidth: 1,
|
||||
},
|
||||
|
@ -27,7 +27,7 @@ export const defaultDocument: Data["document"] = {
|
|||
point: [100, 100],
|
||||
radius: 50,
|
||||
style: {
|
||||
fill: "#aaa",
|
||||
fill: "#AAA",
|
||||
stroke: "#777",
|
||||
strokeWidth: 1,
|
||||
},
|
||||
|
@ -40,7 +40,7 @@ export const defaultDocument: Data["document"] = {
|
|||
radiusX: 50,
|
||||
radiusY: 30,
|
||||
style: {
|
||||
fill: "#aaa",
|
||||
fill: "#AAA",
|
||||
stroke: "#777",
|
||||
strokeWidth: 1,
|
||||
},
|
||||
|
@ -70,7 +70,19 @@ export const defaultDocument: Data["document"] = {
|
|||
point: [300, 300],
|
||||
size: [200, 200],
|
||||
style: {
|
||||
fill: "#aaa",
|
||||
fill: "#AAA",
|
||||
stroke: "#777",
|
||||
strokeWidth: 1,
|
||||
},
|
||||
}),
|
||||
shape6: shapeUtils[ShapeType.Line].create({
|
||||
id: "shape6",
|
||||
name: "Shape 6",
|
||||
childIndex: 1,
|
||||
point: [400, 400],
|
||||
direction: [0.2, 0.2],
|
||||
style: {
|
||||
fill: "#AAA",
|
||||
stroke: "#777",
|
||||
strokeWidth: 1,
|
||||
},
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { current } from "immer"
|
||||
import { ShapeUtil, Bounds, Data, Shapes } from "types"
|
||||
import BaseSession from "./base-session"
|
||||
import shapes from "lib/shapes"
|
||||
import shapes, { getShapeUtils } from "lib/shapes"
|
||||
import { getBoundsFromPoints } from "utils/utils"
|
||||
import * as vec from "utils/vec"
|
||||
|
||||
|
@ -68,9 +68,7 @@ export default class BrushSession extends BaseSession {
|
|||
.map((shape) => ({
|
||||
id: shape.id,
|
||||
test: (brushBounds: Bounds): boolean =>
|
||||
(shapes[shape.type] as ShapeUtil<
|
||||
Shapes[typeof shape.type]
|
||||
>).hitTestBounds(shape, brushBounds),
|
||||
getShapeUtils(shape).hitTestBounds(shape, brushBounds),
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
import { Data, TransformEdge, TransformCorner, Bounds } from "types"
|
||||
import {
|
||||
Data,
|
||||
TransformEdge,
|
||||
TransformCorner,
|
||||
Bounds,
|
||||
BoundsSnapshot,
|
||||
} from "types"
|
||||
import * as vec from "utils/vec"
|
||||
import BaseSession from "./base-session"
|
||||
import commands from "state/commands"
|
||||
|
@ -11,7 +17,6 @@ export default class TransformSession extends BaseSession {
|
|||
transformType: TransformEdge | TransformCorner
|
||||
origin: number[]
|
||||
snapshot: TransformSnapshot
|
||||
currentBounds: Bounds
|
||||
corners: {
|
||||
a: number[]
|
||||
b: number[]
|
||||
|
@ -29,8 +34,6 @@ export default class TransformSession extends BaseSession {
|
|||
|
||||
const { minX, minY, maxX, maxY } = this.snapshot.initialBounds
|
||||
|
||||
this.currentBounds = { ...this.snapshot.initialBounds }
|
||||
|
||||
this.corners = {
|
||||
a: [minX, minY],
|
||||
b: [maxX, maxY],
|
||||
|
@ -38,130 +41,144 @@ export default class TransformSession extends BaseSession {
|
|||
}
|
||||
|
||||
update(data: Data, point: number[]) {
|
||||
const { shapeBounds, currentPageId, selectedIds } = this.snapshot
|
||||
const {
|
||||
document: { pages },
|
||||
} = data
|
||||
shapeBounds,
|
||||
initialBounds,
|
||||
currentPageId,
|
||||
selectedIds,
|
||||
} = this.snapshot
|
||||
|
||||
const { shapes } = data.document.pages[currentPageId]
|
||||
|
||||
let [x, y] = point
|
||||
const { corners, transformType } = this
|
||||
|
||||
const {
|
||||
corners: { a, b },
|
||||
transformType,
|
||||
} = this
|
||||
|
||||
// Edge Transform
|
||||
|
||||
switch (transformType) {
|
||||
case TransformEdge.Top: {
|
||||
corners.a[1] = y
|
||||
a[1] = y
|
||||
break
|
||||
}
|
||||
case TransformEdge.Right: {
|
||||
corners.b[0] = x
|
||||
b[0] = x
|
||||
break
|
||||
}
|
||||
case TransformEdge.Bottom: {
|
||||
corners.b[1] = y
|
||||
b[1] = y
|
||||
break
|
||||
}
|
||||
case TransformEdge.Left: {
|
||||
corners.a[0] = x
|
||||
a[0] = x
|
||||
break
|
||||
}
|
||||
case TransformCorner.TopLeft: {
|
||||
corners.a[1] = y
|
||||
corners.a[0] = x
|
||||
a[1] = y
|
||||
a[0] = x
|
||||
break
|
||||
}
|
||||
case TransformCorner.TopRight: {
|
||||
corners.b[0] = x
|
||||
corners.a[1] = y
|
||||
b[0] = x
|
||||
a[1] = y
|
||||
break
|
||||
}
|
||||
case TransformCorner.BottomRight: {
|
||||
corners.b[1] = y
|
||||
corners.b[0] = x
|
||||
b[1] = y
|
||||
b[0] = x
|
||||
break
|
||||
}
|
||||
case TransformCorner.BottomLeft: {
|
||||
corners.a[0] = x
|
||||
corners.b[1] = y
|
||||
a[0] = x
|
||||
b[1] = y
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate new common (externior) bounding box
|
||||
const newBounds = {
|
||||
minX: Math.min(corners.a[0], corners.b[0]),
|
||||
minY: Math.min(corners.a[1], corners.b[1]),
|
||||
maxX: Math.max(corners.a[0], corners.b[0]),
|
||||
maxY: Math.max(corners.a[1], corners.b[1]),
|
||||
width: Math.abs(corners.b[0] - corners.a[0]),
|
||||
height: Math.abs(corners.b[1] - corners.a[1]),
|
||||
minX: Math.min(a[0], b[0]),
|
||||
minY: Math.min(a[1], b[1]),
|
||||
maxX: Math.max(a[0], b[0]),
|
||||
maxY: Math.max(a[1], b[1]),
|
||||
width: Math.abs(b[0] - a[0]),
|
||||
height: Math.abs(b[1] - a[1]),
|
||||
}
|
||||
|
||||
const isFlippedX = corners.b[0] - corners.a[0] < 0
|
||||
const isFlippedY = corners.b[1] - corners.a[1] < 0
|
||||
const isFlippedX = b[0] < a[0]
|
||||
const isFlippedY = b[1] < a[1]
|
||||
|
||||
// const dx = newBounds.minX - currentBounds.minX
|
||||
// const dy = newBounds.minY - currentBounds.minY
|
||||
// const scaleX = newBounds.width / currentBounds.width
|
||||
// const scaleY = newBounds.height / currentBounds.height
|
||||
|
||||
this.currentBounds = newBounds
|
||||
// Now work backward to calculate a new bounding box for each of the shapes.
|
||||
|
||||
selectedIds.forEach((id) => {
|
||||
const { nx, nmx, nw, ny, nmy, nh } = shapeBounds[id]
|
||||
const { initialShape, initialShapeBounds } = shapeBounds[id]
|
||||
const { nx, nmx, nw, ny, nmy, nh } = initialShapeBounds
|
||||
const shape = shapes[id]
|
||||
|
||||
const minX = newBounds.minX + (isFlippedX ? nmx : nx) * newBounds.width
|
||||
const minY = newBounds.minY + (isFlippedY ? nmy : ny) * newBounds.height
|
||||
const width = nw * newBounds.width
|
||||
const height = nh * newBounds.height
|
||||
|
||||
const shape = pages[currentPageId].shapes[id]
|
||||
|
||||
getShapeUtils(shape).transform(shape, {
|
||||
const newShapeBounds = {
|
||||
minX,
|
||||
minY,
|
||||
maxX: minX + width,
|
||||
maxY: minY + height,
|
||||
width,
|
||||
height,
|
||||
isFlippedX,
|
||||
isFlippedY,
|
||||
}
|
||||
|
||||
// Pass the new data to the shape's transform utility for mutation.
|
||||
// Most shapes should be able to transform using only the bounding box,
|
||||
// however some shapes (e.g. those with internal points) will need more
|
||||
// data here too.
|
||||
|
||||
getShapeUtils(shape).transform(
|
||||
shape,
|
||||
newShapeBounds,
|
||||
initialShape,
|
||||
initialShapeBounds,
|
||||
initialBounds
|
||||
)
|
||||
})
|
||||
// utils.stretch(shape, scaleX, scaleY)
|
||||
})
|
||||
|
||||
// switch (this.transformHandle) {
|
||||
// case TransformEdge.Top:
|
||||
// case TransformEdge.Left:
|
||||
// case TransformEdge.Right:
|
||||
// case TransformEdge.Bottom: {
|
||||
// for (let id in shapeBounds) {
|
||||
// const { ny, nmy, nh } = shapeBounds[id]
|
||||
// const minY = v.my + (v.y1 < v.y0 ? nmy : ny) * v.mh
|
||||
// const height = nh * v.mh
|
||||
|
||||
// const shape = pages[currentPageId].shapes[id]
|
||||
|
||||
// getShapeUtils(shape).transform(shape)
|
||||
// }
|
||||
// }
|
||||
// case TransformCorner.TopLeft:
|
||||
// case TransformCorner.TopRight:
|
||||
// case TransformCorner.BottomLeft:
|
||||
// case TransformCorner.BottomRight: {
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
cancel(data: Data) {
|
||||
const { currentPageId } = this.snapshot
|
||||
const { document } = data
|
||||
const {
|
||||
shapeBounds,
|
||||
initialBounds,
|
||||
currentPageId,
|
||||
selectedIds,
|
||||
} = this.snapshot
|
||||
|
||||
// for (let id in shapes) {
|
||||
// Restore shape using original bounds
|
||||
// document.pages[currentPageId].shapes[id]
|
||||
// }
|
||||
const { shapes } = data.document.pages[currentPageId]
|
||||
|
||||
selectedIds.forEach((id) => {
|
||||
const shape = shapes.shapes[id]
|
||||
const { initialShape, initialShapeBounds } = shapeBounds[id]
|
||||
|
||||
getShapeUtils(shape).transform(
|
||||
shape,
|
||||
{
|
||||
...initialShapeBounds,
|
||||
isFlippedX: false,
|
||||
isFlippedY: false,
|
||||
},
|
||||
initialShape,
|
||||
initialShapeBounds,
|
||||
initialBounds
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
complete(data: Data) {
|
||||
// commands.translate(data, this.snapshot, getTransformSnapshot(data))
|
||||
commands.transform(data, this.snapshot, getTransformSnapshot(data))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -172,21 +189,18 @@ export function getTransformSnapshot(data: Data) {
|
|||
currentPageId,
|
||||
} = current(data)
|
||||
|
||||
const pageShapes = pages[currentPageId].shapes
|
||||
|
||||
// A mapping of selected shapes and their bounds
|
||||
const shapesBounds = Object.fromEntries(
|
||||
Array.from(selectedIds.values()).map((id) => {
|
||||
const shape = pages[currentPageId].shapes[id]
|
||||
const shape = pageShapes[id]
|
||||
return [shape.id, getShapeUtils(shape).getBounds(shape)]
|
||||
})
|
||||
)
|
||||
|
||||
// The common (exterior) bounds of the selected shapes
|
||||
const bounds = getCommonBounds(
|
||||
...Array.from(selectedIds.values()).map((id) => {
|
||||
const shape = pages[currentPageId].shapes[id]
|
||||
return getShapeUtils(shape).getBounds(shape)
|
||||
})
|
||||
)
|
||||
const bounds = getCommonBounds(...Object.values(shapesBounds))
|
||||
|
||||
// Return a mapping of shapes to bounds together with the relative
|
||||
// positions of the shape's bounds within the common bounds shape.
|
||||
|
@ -200,7 +214,9 @@ export function getTransformSnapshot(data: Data) {
|
|||
return [
|
||||
id,
|
||||
{
|
||||
...bounds,
|
||||
initialShape: pageShapes[id],
|
||||
initialShapeBounds: {
|
||||
...shapesBounds[id],
|
||||
nx: (minX - bounds.minX) / bounds.width,
|
||||
ny: (minY - bounds.minY) / bounds.height,
|
||||
nmx: 1 - (minX + width - bounds.minX) / bounds.width,
|
||||
|
@ -208,6 +224,7 @@ export function getTransformSnapshot(data: Data) {
|
|||
nw: width / bounds.width,
|
||||
nh: height / bounds.height,
|
||||
},
|
||||
},
|
||||
]
|
||||
})
|
||||
),
|
||||
|
|
4
types.ts
4
types.ts
|
@ -65,12 +65,12 @@ export interface EllipseShape extends BaseShape {
|
|||
|
||||
export interface LineShape extends BaseShape {
|
||||
type: ShapeType.Line
|
||||
vector: number[]
|
||||
direction: number[]
|
||||
}
|
||||
|
||||
export interface RayShape extends BaseShape {
|
||||
type: ShapeType.Ray
|
||||
vector: number[]
|
||||
direction: number[]
|
||||
}
|
||||
|
||||
export interface PolylineShape extends BaseShape {
|
||||
|
|
Loading…
Reference in a new issue