adds polyline, intersections for polyline
This commit is contained in:
parent
7ee3a1ef3d
commit
f5d555863c
11 changed files with 254 additions and 128 deletions
|
@ -3,6 +3,7 @@ import { useSelector } from "state"
|
|||
import { ShapeType } from "types"
|
||||
import Circle from "./shapes/circle"
|
||||
import Dot from "./shapes/dot"
|
||||
import Polyline from "./shapes/polyline"
|
||||
import Rectangle from "./shapes/rectangle"
|
||||
|
||||
/*
|
||||
|
@ -24,6 +25,8 @@ function Shape({ id }: { id: string }) {
|
|||
return <Circle {...shape} />
|
||||
case ShapeType.Rectangle:
|
||||
return <Rectangle {...shape} />
|
||||
case ShapeType.Polyline:
|
||||
return <Polyline {...shape} />
|
||||
default:
|
||||
return null
|
||||
}
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
import state, { useSelector } from "state"
|
||||
import { useSelector } from "state"
|
||||
import { CircleShape } from "types"
|
||||
import ShapeGroup from "./shape-group"
|
||||
import { getPointerEventInfo } from "utils/utils"
|
||||
import ShapeGroup from "./shape-g"
|
||||
|
||||
interface BaseCircleProps {
|
||||
point: number[]
|
||||
interface BaseCircleProps extends Pick<CircleShape, "radius"> {
|
||||
radius: number
|
||||
fill?: string
|
||||
stroke?: string
|
||||
|
@ -12,7 +10,6 @@ interface BaseCircleProps {
|
|||
}
|
||||
|
||||
function BaseCircle({
|
||||
point,
|
||||
radius,
|
||||
fill = "#ccc",
|
||||
stroke = "none",
|
||||
|
@ -20,8 +17,8 @@ function BaseCircle({
|
|||
}: BaseCircleProps) {
|
||||
return (
|
||||
<circle
|
||||
cx={point[0] + strokeWidth}
|
||||
cy={point[1] + strokeWidth}
|
||||
cx={strokeWidth}
|
||||
cy={strokeWidth}
|
||||
r={radius - strokeWidth}
|
||||
fill={fill}
|
||||
stroke={stroke}
|
||||
|
@ -33,16 +30,10 @@ function BaseCircle({
|
|||
export default function Circle({ id, point, radius }: CircleShape) {
|
||||
const isSelected = useSelector((state) => state.values.selectedIds.has(id))
|
||||
return (
|
||||
<ShapeGroup id={id}>
|
||||
<BaseCircle point={point} radius={radius} />
|
||||
<ShapeGroup id={id} point={point}>
|
||||
<BaseCircle radius={radius} />
|
||||
{isSelected && (
|
||||
<BaseCircle
|
||||
point={point}
|
||||
radius={radius}
|
||||
fill="none"
|
||||
stroke="blue"
|
||||
strokeWidth={1}
|
||||
/>
|
||||
<BaseCircle radius={radius} fill="none" stroke="blue" strokeWidth={1} />
|
||||
)}
|
||||
</ShapeGroup>
|
||||
)
|
||||
|
|
|
@ -1,50 +1,46 @@
|
|||
import { useSelector } from "state"
|
||||
import { DotShape } from "types"
|
||||
import ShapeGroup from "./shape-group"
|
||||
import ShapeGroup from "./shape-g"
|
||||
|
||||
interface BaseCircleProps {
|
||||
point: number[]
|
||||
fill?: string
|
||||
stroke?: string
|
||||
strokeWidth?: number
|
||||
}
|
||||
|
||||
function BaseDot({
|
||||
point,
|
||||
fill = "#ccc",
|
||||
stroke = "none",
|
||||
strokeWidth = 0,
|
||||
}: BaseCircleProps) {
|
||||
return (
|
||||
<g>
|
||||
<>
|
||||
<circle
|
||||
cx={point[0] + strokeWidth}
|
||||
cy={point[1] + strokeWidth}
|
||||
cx={strokeWidth}
|
||||
cy={strokeWidth}
|
||||
r={8}
|
||||
fill="transparent"
|
||||
stroke="none"
|
||||
strokeWidth="0"
|
||||
/>
|
||||
<circle
|
||||
cx={point[0] + strokeWidth}
|
||||
cy={point[1] + strokeWidth}
|
||||
cx={strokeWidth}
|
||||
cy={strokeWidth}
|
||||
r={Math.max(1, 4 - strokeWidth)}
|
||||
fill={fill}
|
||||
stroke={stroke}
|
||||
strokeWidth={strokeWidth}
|
||||
/>
|
||||
</g>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default function Dot({ id, point }: DotShape) {
|
||||
const isSelected = useSelector((state) => state.values.selectedIds.has(id))
|
||||
return (
|
||||
<ShapeGroup id={id}>
|
||||
<BaseDot point={point} />
|
||||
{isSelected && (
|
||||
<BaseDot point={point} fill="none" stroke="blue" strokeWidth={1} />
|
||||
)}
|
||||
<ShapeGroup id={id} point={point}>
|
||||
<BaseDot />
|
||||
{isSelected && <BaseDot fill="none" stroke="blue" strokeWidth={1} />}
|
||||
</ShapeGroup>
|
||||
)
|
||||
}
|
||||
|
|
35
components/canvas/shapes/polyline.tsx
Normal file
35
components/canvas/shapes/polyline.tsx
Normal file
|
@ -0,0 +1,35 @@
|
|||
import { useSelector } from "state"
|
||||
import { PolylineShape } from "types"
|
||||
import ShapeGroup from "./shape-g"
|
||||
|
||||
interface BasePolylineProps extends Pick<PolylineShape, "points"> {
|
||||
fill?: string
|
||||
stroke?: string
|
||||
strokeWidth?: number
|
||||
}
|
||||
|
||||
function BasePolyline({
|
||||
points,
|
||||
fill = "none",
|
||||
stroke = "#ccc",
|
||||
strokeWidth = 2,
|
||||
}: BasePolylineProps) {
|
||||
return (
|
||||
<polyline
|
||||
points={points.toString()}
|
||||
fill={fill}
|
||||
stroke={stroke}
|
||||
strokeWidth={strokeWidth}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default function Polyline({ id, point, points }: PolylineShape) {
|
||||
const isSelected = useSelector((state) => state.values.selectedIds.has(id))
|
||||
return (
|
||||
<ShapeGroup id={id} point={point}>
|
||||
<BasePolyline points={points} />
|
||||
{isSelected && <BasePolyline points={points} fill="none" stroke="blue" />}
|
||||
</ShapeGroup>
|
||||
)
|
||||
}
|
|
@ -1,9 +1,8 @@
|
|||
import { useSelector } from "state"
|
||||
import { RectangleShape } from "types"
|
||||
import ShapeGroup from "./shape-group"
|
||||
import ShapeGroup from "./shape-g"
|
||||
|
||||
interface BaseRectangleProps {
|
||||
point: number[]
|
||||
interface BaseRectangleProps extends Pick<RectangleShape, "size"> {
|
||||
size: number[]
|
||||
fill?: string
|
||||
stroke?: string
|
||||
|
@ -11,7 +10,6 @@ interface BaseRectangleProps {
|
|||
}
|
||||
|
||||
function BaseRectangle({
|
||||
point,
|
||||
size,
|
||||
fill = "#ccc",
|
||||
stroke = "none",
|
||||
|
@ -19,8 +17,8 @@ function BaseRectangle({
|
|||
}: BaseRectangleProps) {
|
||||
return (
|
||||
<rect
|
||||
x={point[0] + strokeWidth}
|
||||
y={point[1] + strokeWidth}
|
||||
x={strokeWidth}
|
||||
y={strokeWidth}
|
||||
width={size[0] - strokeWidth * 2}
|
||||
height={size[1] - strokeWidth * 2}
|
||||
fill={fill}
|
||||
|
@ -33,16 +31,10 @@ function BaseRectangle({
|
|||
export default function Rectangle({ id, point, size }: RectangleShape) {
|
||||
const isSelected = useSelector((state) => state.values.selectedIds.has(id))
|
||||
return (
|
||||
<ShapeGroup id={id}>
|
||||
<BaseRectangle point={point} size={size} />
|
||||
<ShapeGroup id={id} point={point}>
|
||||
<BaseRectangle size={size} />
|
||||
{isSelected && (
|
||||
<BaseRectangle
|
||||
point={point}
|
||||
size={size}
|
||||
fill="none"
|
||||
stroke="blue"
|
||||
strokeWidth={1}
|
||||
/>
|
||||
<BaseRectangle size={size} fill="none" stroke="blue" strokeWidth={1} />
|
||||
)}
|
||||
</ShapeGroup>
|
||||
)
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
import React from "react"
|
||||
import state from "state"
|
||||
import { Shape } from "types"
|
||||
import { getPointerEventInfo } from "utils/utils"
|
||||
|
||||
export default function ShapeGroup({
|
||||
id,
|
||||
children,
|
||||
point,
|
||||
}: {
|
||||
id: string
|
||||
children: React.ReactNode
|
||||
point: number[]
|
||||
}) {
|
||||
return (
|
||||
<g
|
||||
transform={`translate(${point})`}
|
||||
onPointerDown={(e) =>
|
||||
state.send("POINTED_SHAPE", { id, ...getPointerEventInfo(e) })
|
||||
}
|
|
@ -30,12 +30,16 @@ export const defaultDocument: Data["document"] = {
|
|||
},
|
||||
shape2: {
|
||||
id: "shape2",
|
||||
type: ShapeType.Circle,
|
||||
type: ShapeType.Polyline,
|
||||
name: "Shape 2",
|
||||
parentId: "page0",
|
||||
childIndex: 2,
|
||||
point: [200, 800],
|
||||
radius: 25,
|
||||
point: [200, 600],
|
||||
points: [
|
||||
[0, 0],
|
||||
[75, 200],
|
||||
[100, 50],
|
||||
],
|
||||
rotation: 0,
|
||||
},
|
||||
shape3: {
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
import { current } from "immer"
|
||||
import { Bounds, Data, Shape, ShapeType } from "types"
|
||||
import BaseSession from "./base-session"
|
||||
import shapeUtils from "utils/shapes"
|
||||
import shapeUtils from "utils/shape-utils"
|
||||
import { getBoundsFromPoints } from "utils/utils"
|
||||
import * as vec from "utils/vec"
|
||||
import { intersectCircleBounds } from "utils/intersections"
|
||||
import {
|
||||
intersectCircleBounds,
|
||||
intersectPolylineBounds,
|
||||
} from "utils/intersections"
|
||||
|
||||
interface BrushSnapshot {
|
||||
selectedIds: string[]
|
||||
shapes: { shape: Shape; bounds: Bounds }[]
|
||||
shapes: { shape: Shape; test: (bounds: Bounds) => boolean }[]
|
||||
}
|
||||
|
||||
export default class BrushSession extends BaseSession {
|
||||
|
@ -31,32 +34,7 @@ export default class BrushSession extends BaseSession {
|
|||
data.selectedIds = [
|
||||
...snapshot.selectedIds,
|
||||
...snapshot.shapes
|
||||
.filter(({ shape, bounds }) => {
|
||||
switch (shape.type) {
|
||||
case ShapeType.Circle: {
|
||||
return (
|
||||
boundsContained(bounds, brushBounds) ||
|
||||
intersectCircleBounds(shape.point, shape.radius, brushBounds)
|
||||
.length
|
||||
)
|
||||
}
|
||||
case ShapeType.Dot: {
|
||||
return (
|
||||
boundsContained(bounds, brushBounds) ||
|
||||
intersectCircleBounds(shape.point, 4, brushBounds).length
|
||||
)
|
||||
}
|
||||
case ShapeType.Rectangle: {
|
||||
return (
|
||||
boundsContained(bounds, brushBounds) ||
|
||||
boundsCollide(bounds, brushBounds)
|
||||
)
|
||||
}
|
||||
default: {
|
||||
return boundsContained(bounds, brushBounds)
|
||||
}
|
||||
}
|
||||
})
|
||||
.filter(({ test }) => test(brushBounds))
|
||||
.map(({ shape }) => shape.id),
|
||||
]
|
||||
|
||||
|
@ -72,7 +50,7 @@ export default class BrushSession extends BaseSession {
|
|||
data.brush = undefined
|
||||
}
|
||||
|
||||
static getSnapshot(data: Data) {
|
||||
static getSnapshot(data: Data): BrushSnapshot {
|
||||
const {
|
||||
selectedIds,
|
||||
document: { pages },
|
||||
|
@ -88,21 +66,47 @@ export default class BrushSession extends BaseSession {
|
|||
.map((shape) => {
|
||||
switch (shape.type) {
|
||||
case ShapeType.Dot: {
|
||||
const bounds = shapeUtils[shape.type].getBounds(shape)
|
||||
|
||||
return {
|
||||
shape,
|
||||
bounds: shapeUtils[shape.type].getBounds(shape),
|
||||
test: (brushBounds: Bounds) =>
|
||||
boundsContained(bounds, brushBounds) ||
|
||||
intersectCircleBounds(shape.point, 4, brushBounds).length > 0,
|
||||
}
|
||||
}
|
||||
case ShapeType.Circle: {
|
||||
const bounds = shapeUtils[shape.type].getBounds(shape)
|
||||
|
||||
return {
|
||||
shape,
|
||||
bounds: shapeUtils[shape.type].getBounds(shape),
|
||||
test: (brushBounds: Bounds) =>
|
||||
boundsContained(bounds, brushBounds) ||
|
||||
intersectCircleBounds(shape.point, shape.radius, brushBounds)
|
||||
.length > 0,
|
||||
}
|
||||
}
|
||||
case ShapeType.Rectangle: {
|
||||
const bounds = shapeUtils[shape.type].getBounds(shape)
|
||||
|
||||
return {
|
||||
shape,
|
||||
bounds: shapeUtils[shape.type].getBounds(shape),
|
||||
test: (brushBounds: Bounds) =>
|
||||
boundsContained(bounds, brushBounds) ||
|
||||
boundsCollide(bounds, brushBounds),
|
||||
}
|
||||
}
|
||||
case ShapeType.Polyline: {
|
||||
const bounds = shapeUtils[shape.type].getBounds(shape)
|
||||
const points = shape.points.map((point) =>
|
||||
vec.add(point, shape.point)
|
||||
)
|
||||
|
||||
return {
|
||||
shape,
|
||||
test: (brushBounds: Bounds) =>
|
||||
boundsContained(bounds, brushBounds) ||
|
||||
intersectPolylineBounds(points, brushBounds).length > 0,
|
||||
}
|
||||
}
|
||||
default: {
|
||||
|
|
20
types.ts
20
types.ts
|
@ -26,7 +26,7 @@ export enum ShapeType {
|
|||
Ellipse = "ellipse",
|
||||
Line = "line",
|
||||
Ray = "ray",
|
||||
LineSegment = "lineSegment",
|
||||
Polyline = "Polyline",
|
||||
Rectangle = "rectangle",
|
||||
// Glob = "glob",
|
||||
// Spline = "spline",
|
||||
|
@ -40,48 +40,42 @@ export interface BaseShape {
|
|||
parentId: string
|
||||
childIndex: number
|
||||
name: string
|
||||
point: number[]
|
||||
rotation: 0
|
||||
}
|
||||
|
||||
export interface DotShape extends BaseShape {
|
||||
type: ShapeType.Dot
|
||||
point: number[]
|
||||
}
|
||||
|
||||
export interface CircleShape extends BaseShape {
|
||||
type: ShapeType.Circle
|
||||
point: number[]
|
||||
radius: number
|
||||
}
|
||||
|
||||
export interface EllipseShape extends BaseShape {
|
||||
type: ShapeType.Ellipse
|
||||
point: number[]
|
||||
radiusX: number
|
||||
radiusY: number
|
||||
}
|
||||
|
||||
export interface LineShape extends BaseShape {
|
||||
type: ShapeType.Line
|
||||
point: number[]
|
||||
vector: number[]
|
||||
}
|
||||
|
||||
export interface RayShape extends BaseShape {
|
||||
type: ShapeType.Ray
|
||||
point: number[]
|
||||
vector: number[]
|
||||
}
|
||||
|
||||
export interface LineSegmentShape extends BaseShape {
|
||||
type: ShapeType.LineSegment
|
||||
start: number[]
|
||||
end: number[]
|
||||
export interface PolylineShape extends BaseShape {
|
||||
type: ShapeType.Polyline
|
||||
points: number[][]
|
||||
}
|
||||
|
||||
export interface RectangleShape extends BaseShape {
|
||||
type: ShapeType.Rectangle
|
||||
point: number[]
|
||||
size: number[]
|
||||
}
|
||||
|
||||
|
@ -91,7 +85,7 @@ export type Shape =
|
|||
| EllipseShape
|
||||
| LineShape
|
||||
| RayShape
|
||||
| LineSegmentShape
|
||||
| PolylineShape
|
||||
| RectangleShape
|
||||
|
||||
export interface Bounds {
|
||||
|
@ -109,6 +103,6 @@ export interface Shapes extends Record<ShapeType, Shape> {
|
|||
[ShapeType.Ellipse]: EllipseShape
|
||||
[ShapeType.Line]: LineShape
|
||||
[ShapeType.Ray]: RayShape
|
||||
[ShapeType.LineSegment]: LineSegmentShape
|
||||
[ShapeType.Polyline]: PolylineShape
|
||||
[ShapeType.Rectangle]: RectangleShape
|
||||
}
|
||||
|
|
|
@ -7,7 +7,47 @@ interface Intersection {
|
|||
points: number[][]
|
||||
}
|
||||
|
||||
export function intersectCircleLine(
|
||||
function getIntersection(
|
||||
points: number[][],
|
||||
message = points.length ? "Intersection" : "No intersection"
|
||||
) {
|
||||
return { didIntersect: points.length > 0, message, points }
|
||||
}
|
||||
|
||||
export function intersectLineSegments(
|
||||
a1: number[],
|
||||
a2: number[],
|
||||
b1: number[],
|
||||
b2: number[]
|
||||
) {
|
||||
const AB = vec.sub(a1, b1)
|
||||
const BV = vec.sub(b2, b1)
|
||||
const AV = vec.sub(a2, a1)
|
||||
|
||||
const ua_t = BV[0] * AB[1] - BV[1] * AB[0]
|
||||
const ub_t = AV[0] * AB[1] - AV[1] * AB[0]
|
||||
const u_b = BV[1] * AV[0] - BV[0] * AV[1]
|
||||
|
||||
if (ua_t === 0 || ub_t === 0) {
|
||||
return getIntersection([], "Coincident")
|
||||
}
|
||||
|
||||
if (u_b === 0) {
|
||||
return getIntersection([], "Parallel")
|
||||
}
|
||||
|
||||
if (u_b != 0) {
|
||||
const ua = ua_t / u_b
|
||||
const ub = ub_t / u_b
|
||||
if (0 <= ua && ua <= 1 && 0 <= ub && ub <= 1) {
|
||||
return getIntersection([vec.add(a1, vec.mul(AV, ua))])
|
||||
}
|
||||
}
|
||||
|
||||
return getIntersection([])
|
||||
}
|
||||
|
||||
export function intersectCircleLineSegment(
|
||||
c: number[],
|
||||
r: number,
|
||||
a1: number[],
|
||||
|
@ -66,19 +106,23 @@ export function intersectCircleRectangle(
|
|||
|
||||
const intersections: Intersection[] = []
|
||||
|
||||
const topIntersection = intersectCircleLine(c, r, tl, tr)
|
||||
const topIntersection = intersectCircleLineSegment(c, r, tl, tr)
|
||||
const rightIntersection = intersectCircleLineSegment(c, r, tr, br)
|
||||
const bottomIntersection = intersectCircleLineSegment(c, r, bl, br)
|
||||
const leftIntersection = intersectCircleLineSegment(c, r, tl, bl)
|
||||
|
||||
if (topIntersection.didIntersect) {
|
||||
intersections.push({ ...topIntersection, message: "top" })
|
||||
}
|
||||
const rightIntersection = intersectCircleLine(c, r, tr, br)
|
||||
|
||||
if (rightIntersection.didIntersect) {
|
||||
intersections.push({ ...rightIntersection, message: "right" })
|
||||
}
|
||||
const bottomIntersection = intersectCircleLine(c, r, bl, br)
|
||||
|
||||
if (bottomIntersection.didIntersect) {
|
||||
intersections.push({ ...bottomIntersection, message: "bottom" })
|
||||
}
|
||||
const leftIntersection = intersectCircleLine(c, r, tl, bl)
|
||||
|
||||
if (leftIntersection.didIntersect) {
|
||||
intersections.push({ ...leftIntersection, message: "left" })
|
||||
}
|
||||
|
@ -86,18 +130,79 @@ export function intersectCircleRectangle(
|
|||
return intersections
|
||||
}
|
||||
|
||||
export function intersectRectangleLineSegment(
|
||||
point: number[],
|
||||
size: number[],
|
||||
a1: number[],
|
||||
a2: number[]
|
||||
) {
|
||||
const tl = point
|
||||
const tr = vec.add(point, [size[0], 0])
|
||||
const br = vec.add(point, size)
|
||||
const bl = vec.add(point, [0, size[1]])
|
||||
|
||||
const intersections: Intersection[] = []
|
||||
|
||||
const topIntersection = intersectLineSegments(a1, a2, tl, tr)
|
||||
const rightIntersection = intersectLineSegments(a1, a2, tr, br)
|
||||
const bottomIntersection = intersectLineSegments(a1, a2, bl, br)
|
||||
const leftIntersection = intersectLineSegments(a1, a2, tl, bl)
|
||||
|
||||
if (topIntersection.didIntersect) {
|
||||
intersections.push({ ...topIntersection, message: "top" })
|
||||
}
|
||||
|
||||
if (rightIntersection.didIntersect) {
|
||||
intersections.push({ ...rightIntersection, message: "right" })
|
||||
}
|
||||
|
||||
if (bottomIntersection.didIntersect) {
|
||||
intersections.push({ ...bottomIntersection, message: "bottom" })
|
||||
}
|
||||
|
||||
if (leftIntersection.didIntersect) {
|
||||
intersections.push({ ...leftIntersection, message: "left" })
|
||||
}
|
||||
|
||||
return intersections
|
||||
}
|
||||
|
||||
/* -------------------------------------------------- */
|
||||
/* Shape vs. Bounds */
|
||||
/* -------------------------------------------------- */
|
||||
|
||||
export function intersectCircleBounds(
|
||||
c: number[],
|
||||
r: number,
|
||||
bounds: Bounds
|
||||
): Intersection[] {
|
||||
const { minX, minY, width, height } = bounds
|
||||
const intersections = intersectCircleRectangle(
|
||||
c,
|
||||
r,
|
||||
[minX, minY],
|
||||
[width, height]
|
||||
)
|
||||
return intersectCircleRectangle(c, r, [minX, minY], [width, height])
|
||||
}
|
||||
|
||||
export function intersectLineSegmentBounds(
|
||||
a1: number[],
|
||||
a2: number[],
|
||||
bounds: Bounds
|
||||
) {
|
||||
const { minX, minY, width, height } = bounds
|
||||
return intersectRectangleLineSegment([minX, minY], [width, height], a1, a2)
|
||||
}
|
||||
|
||||
export function intersectPolylineBounds(points: number[][], bounds: Bounds) {
|
||||
const { minX, minY, width, height } = bounds
|
||||
const intersections: Intersection[] = []
|
||||
|
||||
for (let i = 1; i < points.length; i++) {
|
||||
intersections.push(
|
||||
...intersectRectangleLineSegment(
|
||||
[minX, minY],
|
||||
[width, height],
|
||||
points[i - 1],
|
||||
points[i]
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
return intersections
|
||||
}
|
||||
|
|
|
@ -3,19 +3,7 @@ import {
|
|||
boundsContain,
|
||||
pointInBounds,
|
||||
} from "state/sessions/brush-session"
|
||||
import {
|
||||
Shape,
|
||||
Bounds,
|
||||
ShapeType,
|
||||
CircleShape,
|
||||
DotShape,
|
||||
RectangleShape,
|
||||
Shapes,
|
||||
EllipseShape,
|
||||
LineShape,
|
||||
RayShape,
|
||||
LineSegmentShape,
|
||||
} from "types"
|
||||
import { Bounds, ShapeType, Shapes } from "types"
|
||||
import { intersectCircleBounds } from "./intersections"
|
||||
import * as vec from "./vec"
|
||||
|
||||
|
@ -224,15 +212,27 @@ const RayUtils: BaseShapeUtils<ShapeType.Ray> = {
|
|||
|
||||
/* ------------------ Line Segment ------------------ */
|
||||
|
||||
const LineSegmentUtils: BaseShapeUtils<ShapeType.LineSegment> = {
|
||||
const PolylineUtils: BaseShapeUtils<ShapeType.Polyline> = {
|
||||
getBounds(shape) {
|
||||
let minX = 0
|
||||
let minY = 0
|
||||
let maxX = 0
|
||||
let maxY = 0
|
||||
|
||||
for (let [x, y] of shape.points) {
|
||||
minX = Math.min(x, minX)
|
||||
minY = Math.min(y, minY)
|
||||
maxX = Math.max(x, maxX)
|
||||
maxY = Math.max(y, maxY)
|
||||
}
|
||||
|
||||
return {
|
||||
minX: 0,
|
||||
minY: 0,
|
||||
maxX: 0,
|
||||
maxY: 0,
|
||||
width: 0,
|
||||
height: 0,
|
||||
minX: minX + shape.point[0],
|
||||
minY: minY + shape.point[1],
|
||||
maxX: maxX + shape.point[0],
|
||||
maxY: maxY + shape.point[1],
|
||||
width: maxX - minX,
|
||||
height: maxY - minY,
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -303,7 +303,7 @@ const shapeUtils: { [K in ShapeType]: BaseShapeUtils<K> } = {
|
|||
[ShapeType.Ellipse]: EllipseUtils,
|
||||
[ShapeType.Line]: LineUtils,
|
||||
[ShapeType.Ray]: RayUtils,
|
||||
[ShapeType.LineSegment]: LineSegmentUtils,
|
||||
[ShapeType.Polyline]: PolylineUtils,
|
||||
[ShapeType.Rectangle]: RectangleUtils,
|
||||
}
|
||||
|
Loading…
Reference in a new issue