Adds code editing and shape generation
This commit is contained in:
parent
afa8f53dff
commit
1a01c47835
34 changed files with 1298 additions and 237 deletions
|
@ -7,13 +7,18 @@ export default class Circle extends CodeShape<CircleShape> {
|
|||
super({
|
||||
id: uuid(),
|
||||
type: ShapeType.Circle,
|
||||
isGenerated: true,
|
||||
name: "Circle",
|
||||
parentId: "page0",
|
||||
childIndex: 0,
|
||||
point: [0, 0],
|
||||
rotation: 0,
|
||||
radius: 20,
|
||||
style: {},
|
||||
style: {
|
||||
fill: "#777",
|
||||
stroke: "#000",
|
||||
strokeWidth: 1,
|
||||
},
|
||||
...props,
|
||||
})
|
||||
}
|
||||
|
|
24
lib/code/dot.ts
Normal file
24
lib/code/dot.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
import CodeShape from "./index"
|
||||
import { v4 as uuid } from "uuid"
|
||||
import { DotShape, ShapeType } from "types"
|
||||
|
||||
export default class Dot extends CodeShape<DotShape> {
|
||||
constructor(props = {} as Partial<DotShape>) {
|
||||
super({
|
||||
id: uuid(),
|
||||
type: ShapeType.Dot,
|
||||
isGenerated: false,
|
||||
name: "Dot",
|
||||
parentId: "page0",
|
||||
childIndex: 0,
|
||||
point: [0, 0],
|
||||
rotation: 0,
|
||||
style: {
|
||||
fill: "#777",
|
||||
stroke: "#000",
|
||||
strokeWidth: 1,
|
||||
},
|
||||
...props,
|
||||
})
|
||||
}
|
||||
}
|
30
lib/code/ellipse.ts
Normal file
30
lib/code/ellipse.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
import CodeShape from "./index"
|
||||
import { v4 as uuid } from "uuid"
|
||||
import { EllipseShape, ShapeType } from "types"
|
||||
|
||||
export default class Ellipse extends CodeShape<EllipseShape> {
|
||||
constructor(props = {} as Partial<EllipseShape>) {
|
||||
super({
|
||||
id: uuid(),
|
||||
type: ShapeType.Ellipse,
|
||||
isGenerated: false,
|
||||
name: "Ellipse",
|
||||
parentId: "page0",
|
||||
childIndex: 0,
|
||||
point: [0, 0],
|
||||
radiusX: 20,
|
||||
radiusY: 20,
|
||||
rotation: 0,
|
||||
style: {
|
||||
fill: "#777",
|
||||
stroke: "#000",
|
||||
strokeWidth: 1,
|
||||
},
|
||||
...props,
|
||||
})
|
||||
}
|
||||
|
||||
get radius() {
|
||||
return this.shape.radius
|
||||
}
|
||||
}
|
29
lib/code/generate.ts
Normal file
29
lib/code/generate.ts
Normal file
|
@ -0,0 +1,29 @@
|
|||
import Rectangle from "./rectangle"
|
||||
import Circle from "./circle"
|
||||
import Ellipse from "./ellipse"
|
||||
import Polyline from "./polyline"
|
||||
import Dot from "./dot"
|
||||
import Line from "./line"
|
||||
import Vector from "./vector"
|
||||
import Utils from "./utils"
|
||||
import { codeShapes } from "./index"
|
||||
|
||||
const scope = { Dot, Circle, Ellipse, Line, Polyline, Rectangle, Vector, Utils }
|
||||
|
||||
/**
|
||||
* Evaluate code, collecting generated shapes in the shape set. Return the
|
||||
* collected shapes as an array.
|
||||
* @param code
|
||||
*/
|
||||
export function getShapesFromCode(code: string) {
|
||||
codeShapes.clear()
|
||||
|
||||
new Function(...Object.keys(scope), `${code}`)(...Object.values(scope))
|
||||
|
||||
const generatedShapes = Array.from(codeShapes.values()).map((instance) => {
|
||||
instance.shape.isGenerated = true
|
||||
return instance.shape
|
||||
})
|
||||
|
||||
return generatedShapes
|
||||
}
|
|
@ -2,16 +2,22 @@ import { Shape } from "types"
|
|||
import * as vec from "utils/vec"
|
||||
import { getShapeUtils } from "lib/shapes"
|
||||
|
||||
export const codeShapes = new Set<CodeShape<Shape>>([])
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
export default class CodeShape<T extends Shape> {
|
||||
private _shape: T
|
||||
|
||||
constructor(props: T) {
|
||||
this._shape = props
|
||||
shapeMap.add(this)
|
||||
codeShapes.add(this)
|
||||
}
|
||||
|
||||
destroy() {
|
||||
shapeMap.delete(this)
|
||||
codeShapes.delete(this)
|
||||
}
|
||||
|
||||
moveTo(point: number[]) {
|
||||
|
@ -50,5 +56,3 @@ export default class CodeShape<T extends Shape> {
|
|||
return this.shape.rotation
|
||||
}
|
||||
}
|
||||
|
||||
export const shapeMap = new Set<CodeShape<Shape>>([])
|
||||
|
|
25
lib/code/line.ts
Normal file
25
lib/code/line.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
import CodeShape from "./index"
|
||||
import { v4 as uuid } from "uuid"
|
||||
import { LineShape, ShapeType } from "types"
|
||||
|
||||
export default class Line extends CodeShape<LineShape> {
|
||||
constructor(props = {} as Partial<LineShape>) {
|
||||
super({
|
||||
id: uuid(),
|
||||
type: ShapeType.Line,
|
||||
isGenerated: false,
|
||||
name: "Line",
|
||||
parentId: "page0",
|
||||
childIndex: 0,
|
||||
point: [0, 0],
|
||||
direction: [0, 0],
|
||||
rotation: 0,
|
||||
style: {
|
||||
fill: "#777",
|
||||
stroke: "#000",
|
||||
strokeWidth: 1,
|
||||
},
|
||||
...props,
|
||||
})
|
||||
}
|
||||
}
|
25
lib/code/polyline.ts
Normal file
25
lib/code/polyline.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
import CodeShape from "./index"
|
||||
import { v4 as uuid } from "uuid"
|
||||
import { PolylineShape, ShapeType } from "types"
|
||||
|
||||
export default class Polyline extends CodeShape<PolylineShape> {
|
||||
constructor(props = {} as Partial<PolylineShape>) {
|
||||
super({
|
||||
id: uuid(),
|
||||
type: ShapeType.Polyline,
|
||||
isGenerated: false,
|
||||
name: "Polyline",
|
||||
parentId: "page0",
|
||||
childIndex: 0,
|
||||
point: [0, 0],
|
||||
points: [[0, 0]],
|
||||
rotation: 0,
|
||||
style: {
|
||||
fill: "none",
|
||||
stroke: "#000",
|
||||
strokeWidth: 1,
|
||||
},
|
||||
...props,
|
||||
})
|
||||
}
|
||||
}
|
|
@ -7,13 +7,18 @@ export default class Rectangle extends CodeShape<RectangleShape> {
|
|||
super({
|
||||
id: uuid(),
|
||||
type: ShapeType.Rectangle,
|
||||
isGenerated: true,
|
||||
name: "Rectangle",
|
||||
parentId: "page0",
|
||||
childIndex: 0,
|
||||
point: [0, 0],
|
||||
size: [1, 1],
|
||||
size: [100, 100],
|
||||
rotation: 0,
|
||||
style: {},
|
||||
style: {
|
||||
fill: "#777",
|
||||
stroke: "#000",
|
||||
strokeWidth: 1,
|
||||
},
|
||||
...props,
|
||||
})
|
||||
}
|
||||
|
|
151
lib/code/utils.ts
Normal file
151
lib/code/utils.ts
Normal file
|
@ -0,0 +1,151 @@
|
|||
import { Bounds } from "types"
|
||||
import Vector, { Point } from "./vector"
|
||||
|
||||
export default class Utils {
|
||||
static getRayRayIntersection(p0: Vector, n0: Vector, p1: Vector, n1: Vector) {
|
||||
const p0e = Vector.add(p0, n0),
|
||||
p1e = Vector.add(p1, n1),
|
||||
m0 = (p0e.y - p0.y) / (p0e.x - p0.x),
|
||||
m1 = (p1e.y - p1.y) / (p1e.x - p1.x),
|
||||
b0 = p0.y - m0 * p0.x,
|
||||
b1 = p1.y - m1 * p1.x,
|
||||
x = (b1 - b0) / (m0 - m1),
|
||||
y = m0 * x + b0
|
||||
|
||||
return new Vector({ x, y })
|
||||
}
|
||||
|
||||
static getCircleTangentToPoint(
|
||||
A: Point | Vector,
|
||||
r0: number,
|
||||
P: Point | Vector,
|
||||
side: number
|
||||
) {
|
||||
const v0 = Vector.cast(A)
|
||||
const v1 = Vector.cast(P)
|
||||
const B = Vector.lrp(v0, v1, 0.5),
|
||||
r1 = Vector.dist(v0, B),
|
||||
delta = Vector.sub(B, v0),
|
||||
d = Vector.len(delta)
|
||||
|
||||
if (!(d <= r0 + r1 && d >= Math.abs(r0 - r1))) {
|
||||
return
|
||||
}
|
||||
|
||||
const a = (r0 * r0 - r1 * r1 + d * d) / (2.0 * d),
|
||||
n = 1 / d,
|
||||
p = Vector.add(v0, Vector.mul(delta, a * n)),
|
||||
h = Math.sqrt(r0 * r0 - a * a),
|
||||
k = Vector.mul(Vector.per(delta), h * n)
|
||||
|
||||
return side === 0 ? p.add(k) : p.sub(k)
|
||||
}
|
||||
|
||||
static shortAngleDist(a: number, b: number) {
|
||||
const max = Math.PI * 2
|
||||
const da = (b - a) % max
|
||||
return ((2 * da) % max) - da
|
||||
}
|
||||
|
||||
static getSweep(C: Vector, A: Vector, B: Vector) {
|
||||
return Utils.shortAngleDist(Vector.ang(C, A), Vector.ang(C, B))
|
||||
}
|
||||
|
||||
static bez1d(a: number, b: number, c: number, d: number, t: number) {
|
||||
return (
|
||||
a * (1 - t) * (1 - t) * (1 - t) +
|
||||
3 * b * t * (1 - t) * (1 - t) +
|
||||
3 * c * t * t * (1 - t) +
|
||||
d * t * t * t
|
||||
)
|
||||
}
|
||||
|
||||
static getCubicBezierBounds(
|
||||
p0: Point | Vector,
|
||||
c0: Point | Vector,
|
||||
c1: Point | Vector,
|
||||
p1: Point | Vector
|
||||
): Bounds {
|
||||
// solve for x
|
||||
let a = 3 * p1[0] - 9 * c1[0] + 9 * c0[0] - 3 * p0[0]
|
||||
let b = 6 * p0[0] - 12 * c0[0] + 6 * c1[0]
|
||||
let c = 3 * c0[0] - 3 * p0[0]
|
||||
let disc = b * b - 4 * a * c
|
||||
let xl = p0[0]
|
||||
let xh = p0[0]
|
||||
|
||||
if (p1[0] < xl) xl = p1[0]
|
||||
if (p1[0] > xh) xh = p1[0]
|
||||
|
||||
if (disc >= 0) {
|
||||
const t1 = (-b + Math.sqrt(disc)) / (2 * a)
|
||||
if (t1 > 0 && t1 < 1) {
|
||||
const x1 = Utils.bez1d(p0[0], c0[0], c1[0], p1[0], t1)
|
||||
if (x1 < xl) xl = x1
|
||||
if (x1 > xh) xh = x1
|
||||
}
|
||||
const t2 = (-b - Math.sqrt(disc)) / (2 * a)
|
||||
if (t2 > 0 && t2 < 1) {
|
||||
const x2 = Utils.bez1d(p0[0], c0[0], c1[0], p1[0], t2)
|
||||
if (x2 < xl) xl = x2
|
||||
if (x2 > xh) xh = x2
|
||||
}
|
||||
}
|
||||
|
||||
// Solve for y
|
||||
a = 3 * p1[1] - 9 * c1[1] + 9 * c0[1] - 3 * p0[1]
|
||||
b = 6 * p0[1] - 12 * c0[1] + 6 * c1[1]
|
||||
c = 3 * c0[1] - 3 * p0[1]
|
||||
disc = b * b - 4 * a * c
|
||||
let yl = p0[1]
|
||||
let yh = p0[1]
|
||||
if (p1[1] < yl) yl = p1[1]
|
||||
if (p1[1] > yh) yh = p1[1]
|
||||
if (disc >= 0) {
|
||||
const t1 = (-b + Math.sqrt(disc)) / (2 * a)
|
||||
if (t1 > 0 && t1 < 1) {
|
||||
const y1 = Utils.bez1d(p0[1], c0[1], c1[1], p1[1], t1)
|
||||
if (y1 < yl) yl = y1
|
||||
if (y1 > yh) yh = y1
|
||||
}
|
||||
const t2 = (-b - Math.sqrt(disc)) / (2 * a)
|
||||
if (t2 > 0 && t2 < 1) {
|
||||
const y2 = Utils.bez1d(p0[1], c0[1], c1[1], p1[1], t2)
|
||||
if (y2 < yl) yl = y2
|
||||
if (y2 > yh) yh = y2
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
minX: xl,
|
||||
minY: yl,
|
||||
maxX: xh,
|
||||
maxY: yh,
|
||||
width: Math.abs(xl - xh),
|
||||
height: Math.abs(yl - yh),
|
||||
}
|
||||
}
|
||||
|
||||
static getExpandedBounds(a: Bounds, b: Bounds) {
|
||||
const minX = Math.min(a.minX, b.minX),
|
||||
minY = Math.min(a.minY, b.minY),
|
||||
maxX = Math.max(a.maxX, b.maxX),
|
||||
maxY = Math.max(a.maxY, b.maxY),
|
||||
width = Math.abs(maxX - minX),
|
||||
height = Math.abs(maxY - minY)
|
||||
|
||||
return { minX, minY, maxX, maxY, width, height }
|
||||
}
|
||||
|
||||
static getCommonBounds(...b: Bounds[]) {
|
||||
if (b.length < 2) return b[0]
|
||||
|
||||
let bounds = b[0]
|
||||
|
||||
for (let i = 1; i < b.length; i++) {
|
||||
bounds = Utils.getExpandedBounds(bounds, b[i])
|
||||
}
|
||||
|
||||
return bounds
|
||||
}
|
||||
}
|
476
lib/code/vector.ts
Normal file
476
lib/code/vector.ts
Normal file
|
@ -0,0 +1,476 @@
|
|||
export interface VectorOptions {
|
||||
x: number
|
||||
y: number
|
||||
}
|
||||
|
||||
export interface Point {
|
||||
x: number
|
||||
y: number
|
||||
}
|
||||
|
||||
export default class Vector {
|
||||
x = 0
|
||||
y = 0
|
||||
|
||||
constructor(x: number, y: number)
|
||||
constructor(vector: Vector, b?: undefined)
|
||||
constructor(options: Point, b?: undefined)
|
||||
constructor(a: VectorOptions | Vector | number, b?: number) {
|
||||
if (typeof a === "number") {
|
||||
this.x = a
|
||||
this.y = b
|
||||
} else {
|
||||
const { x = 0, y = 0 } = a
|
||||
this.x = x
|
||||
this.y = y
|
||||
}
|
||||
}
|
||||
|
||||
set(v: Vector | Point) {
|
||||
this.x = v.x
|
||||
this.y = v.y
|
||||
}
|
||||
|
||||
copy() {
|
||||
return new Vector(this)
|
||||
}
|
||||
|
||||
clone() {
|
||||
return this.copy()
|
||||
}
|
||||
|
||||
toArray() {
|
||||
return [this.x, this.y]
|
||||
}
|
||||
|
||||
add(b: Vector) {
|
||||
this.x += b.x
|
||||
this.y += b.y
|
||||
return this
|
||||
}
|
||||
|
||||
static add(a: Vector, b: Vector) {
|
||||
const n = new Vector(a)
|
||||
n.x += b.x
|
||||
n.y += b.y
|
||||
return n
|
||||
}
|
||||
|
||||
sub(b: Vector) {
|
||||
this.x -= b.x
|
||||
this.y -= b.y
|
||||
return this
|
||||
}
|
||||
|
||||
static sub(a: Vector, b: Vector) {
|
||||
const n = new Vector(a)
|
||||
n.x -= b.x
|
||||
n.y -= b.y
|
||||
return n
|
||||
}
|
||||
|
||||
mul(b: number): Vector
|
||||
mul(b: Vector): Vector
|
||||
mul(b: Vector | number) {
|
||||
if (b instanceof Vector) {
|
||||
this.x *= b.x
|
||||
this.y *= b.y
|
||||
} else {
|
||||
this.x *= b
|
||||
this.y *= b
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
mulScalar(b: number) {
|
||||
return this.mul(b)
|
||||
}
|
||||
|
||||
static mulScalar(a: Vector, b: number) {
|
||||
return Vector.mul(a, b)
|
||||
}
|
||||
|
||||
static mul(a: Vector, b: number): Vector
|
||||
static mul(a: Vector, b: Vector): Vector
|
||||
static mul(a: Vector, b: Vector | number) {
|
||||
const n = new Vector(a)
|
||||
if (b instanceof Vector) {
|
||||
n.x *= b.x
|
||||
n.y *= b.y
|
||||
} else {
|
||||
n.x *= b
|
||||
n.y *= b
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
div(b: number): Vector
|
||||
div(b: Vector): Vector
|
||||
div(b: Vector | number) {
|
||||
if (b instanceof Vector) {
|
||||
if (b.x) {
|
||||
this.x /= b.x
|
||||
}
|
||||
if (b.y) {
|
||||
this.y /= b.y
|
||||
}
|
||||
} else {
|
||||
if (b) {
|
||||
this.x /= b
|
||||
this.y /= b
|
||||
}
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
static div(a: Vector, b: number): Vector
|
||||
static div(a: Vector, b: Vector): Vector
|
||||
static div(a: Vector, b: Vector | number) {
|
||||
const n = new Vector(a)
|
||||
if (b instanceof Vector) {
|
||||
if (b.x) n.x /= b.x
|
||||
if (b.y) n.y /= b.y
|
||||
} else {
|
||||
if (b) {
|
||||
n.x /= b
|
||||
n.y /= b
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
divScalar(b: number) {
|
||||
return this.div(b)
|
||||
}
|
||||
|
||||
static divScalar(a: Vector, b: number) {
|
||||
return Vector.div(a, b)
|
||||
}
|
||||
|
||||
vec(b: Vector) {
|
||||
const { x, y } = this
|
||||
this.x = b.x - x
|
||||
this.y = b.y - y
|
||||
return this
|
||||
}
|
||||
|
||||
static vec(a: Vector, b: Vector) {
|
||||
const n = new Vector(a)
|
||||
n.x = b.x - a.x
|
||||
n.y = b.y - a.y
|
||||
return n
|
||||
}
|
||||
|
||||
pry(b: Vector) {
|
||||
return this.dpr(b) / b.len()
|
||||
}
|
||||
|
||||
static pry(a: Vector, b: Vector) {
|
||||
return a.dpr(b) / b.len()
|
||||
}
|
||||
|
||||
dpr(b: Vector) {
|
||||
return this.x * b.x + this.y * b.y
|
||||
}
|
||||
|
||||
static dpr(a: Vector, b: Vector) {
|
||||
return a.x & (b.x + a.y * b.y)
|
||||
}
|
||||
|
||||
cpr(b: Vector) {
|
||||
return this.x * b.y - b.y * this.y
|
||||
}
|
||||
|
||||
static cpr(a: Vector, b: Vector) {
|
||||
return a.x * b.y - b.y * a.y
|
||||
}
|
||||
|
||||
tangent(b: Vector) {
|
||||
return this.sub(b).uni()
|
||||
}
|
||||
|
||||
static tangent(a: Vector, b: Vector) {
|
||||
const n = new Vector(a)
|
||||
return n.sub(b).uni()
|
||||
}
|
||||
|
||||
dist2(b: Vector) {
|
||||
return this.sub(b).len2()
|
||||
}
|
||||
|
||||
static dist2(a: Vector, b: Vector) {
|
||||
const n = new Vector(a)
|
||||
return n.sub(b).len2()
|
||||
}
|
||||
|
||||
dist(b: Vector) {
|
||||
return Math.hypot(b.y - this.y, b.x - this.x)
|
||||
}
|
||||
|
||||
static dist(a: Vector, b: Vector) {
|
||||
const n = new Vector(a)
|
||||
return Math.hypot(b.y - n.y, b.x - n.x)
|
||||
}
|
||||
|
||||
ang(b: Vector) {
|
||||
return Math.atan2(b.y - this.y, b.x - this.x)
|
||||
}
|
||||
|
||||
static ang(a: Vector, b: Vector) {
|
||||
const n = new Vector(a)
|
||||
return Math.atan2(b.y - n.y, b.x - n.x)
|
||||
}
|
||||
|
||||
med(b: Vector) {
|
||||
return this.add(b).mul(0.5)
|
||||
}
|
||||
|
||||
static med(a: Vector, b: Vector) {
|
||||
const n = new Vector(a)
|
||||
return n.add(b).mul(0.5)
|
||||
}
|
||||
|
||||
rot(r: number) {
|
||||
const { x, y } = this
|
||||
this.x = x * Math.cos(r) - y * Math.sin(r)
|
||||
this.y = x * Math.sin(r) + y * Math.cos(r)
|
||||
return this
|
||||
}
|
||||
|
||||
static rot(a: Vector, r: number) {
|
||||
const n = new Vector(a)
|
||||
n.x = a.x * Math.cos(r) - a.y * Math.sin(r)
|
||||
n.y = a.x * Math.sin(r) + a.y * Math.cos(r)
|
||||
return n
|
||||
}
|
||||
|
||||
rotAround(b: Vector, r: number) {
|
||||
const { x, y } = this
|
||||
const s = Math.sin(r)
|
||||
const c = Math.cos(r)
|
||||
|
||||
const px = x - b.x
|
||||
const py = y - b.y
|
||||
|
||||
this.x = px * c - py * s + b.x
|
||||
this.y = px * s + py * c + b.y
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
static rotAround(a: Vector, b: Vector, r: number) {
|
||||
const n = new Vector(a)
|
||||
const s = Math.sin(r)
|
||||
const c = Math.cos(r)
|
||||
|
||||
const px = n.x - b.x
|
||||
const py = n.y - b.y
|
||||
|
||||
n.x = px * c - py * s + b.x
|
||||
n.y = px * s + py * c + b.y
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
lrp(b: Vector, t: number) {
|
||||
const n = new Vector(this)
|
||||
this.vec(b)
|
||||
.mul(t)
|
||||
.add(n)
|
||||
}
|
||||
|
||||
static lrp(a: Vector, b: Vector, t: number) {
|
||||
const n = new Vector(a)
|
||||
n.vec(b)
|
||||
.mul(t)
|
||||
.add(a)
|
||||
return n
|
||||
}
|
||||
|
||||
nudge(b: Vector, d: number) {
|
||||
this.add(b.mul(d))
|
||||
}
|
||||
|
||||
static nudge(a: Vector, b: Vector, d: number) {
|
||||
const n = new Vector(a)
|
||||
return n.add(b.mul(d))
|
||||
}
|
||||
|
||||
nudgeToward(b: Vector, d: number) {
|
||||
return this.nudge(Vector.vec(this, b).uni(), d)
|
||||
}
|
||||
|
||||
static nudgeToward(a: Vector, b: Vector, d: number) {
|
||||
return Vector.nudge(a, Vector.vec(a, b).uni(), d)
|
||||
}
|
||||
|
||||
int(b: Vector, from: number, to: number, s: number) {
|
||||
const t = (Math.max(from, to) - from) / (to - from)
|
||||
this.add(Vector.mul(this, 1 - t).add(Vector.mul(b, s)))
|
||||
return this
|
||||
}
|
||||
|
||||
static int(a: Vector, b: Vector, from: number, to: number, s: number) {
|
||||
const n = new Vector(a)
|
||||
const t = (Math.max(from, to) - from) / (to - from)
|
||||
n.add(Vector.mul(a, 1 - t).add(Vector.mul(b, s)))
|
||||
return n
|
||||
}
|
||||
|
||||
equals(b: Vector) {
|
||||
return this.x === b.x && this.y === b.y
|
||||
}
|
||||
|
||||
static equals(a: Vector, b: Vector) {
|
||||
return a.x === b.x && a.y === b.y
|
||||
}
|
||||
|
||||
abs() {
|
||||
this.x = Math.abs(this.x)
|
||||
this.y = Math.abs(this.y)
|
||||
return this
|
||||
}
|
||||
|
||||
static abs(a: Vector) {
|
||||
const n = new Vector(a)
|
||||
n.x = Math.abs(n.x)
|
||||
n.y = Math.abs(n.y)
|
||||
return n
|
||||
}
|
||||
|
||||
len() {
|
||||
return Math.hypot(this.x, this.y)
|
||||
}
|
||||
|
||||
static len(a: Vector) {
|
||||
return Math.hypot(a.x, a.y)
|
||||
}
|
||||
|
||||
len2() {
|
||||
return this.x * this.x + this.y * this.y
|
||||
}
|
||||
|
||||
static len2(a: Vector) {
|
||||
return a.x * a.x + a.y * a.y
|
||||
}
|
||||
|
||||
per() {
|
||||
const t = this.x
|
||||
this.x = this.y
|
||||
this.y = -t
|
||||
return this
|
||||
}
|
||||
|
||||
static per(a: Vector) {
|
||||
const n = new Vector(a)
|
||||
n.x = n.y
|
||||
n.y = -a.x
|
||||
return n
|
||||
}
|
||||
|
||||
neg() {
|
||||
this.x *= -1
|
||||
this.y *= -1
|
||||
return this
|
||||
}
|
||||
|
||||
static neg(v: Vector) {
|
||||
const n = new Vector(v)
|
||||
n.x *= -1
|
||||
n.y *= -1
|
||||
return n
|
||||
}
|
||||
|
||||
uni() {
|
||||
return this.div(this.len())
|
||||
}
|
||||
|
||||
static uni(v: Vector) {
|
||||
const n = new Vector(v)
|
||||
return n.div(n.len())
|
||||
}
|
||||
|
||||
isLeft(center: Vector, b: Vector) {
|
||||
return (
|
||||
(center.x - this.x) * (b.y - this.y) - (b.x - this.x) * (center.y - b.y)
|
||||
)
|
||||
}
|
||||
|
||||
static isLeft(center: Vector, a: Vector, b: Vector) {
|
||||
return (center.x - a.x) * (b.y - a.y) - (b.x - a.x) * (center.y - b.y)
|
||||
}
|
||||
|
||||
static ang3(center: Vector, a: Vector, b: Vector) {
|
||||
const v1 = Vector.vec(center, a)
|
||||
const v2 = Vector.vec(center, b)
|
||||
return Vector.ang(v1, v2)
|
||||
}
|
||||
|
||||
static clockwise(center: Vector, a: Vector, b: Vector) {
|
||||
return Vector.isLeft(center, a, b) > 0
|
||||
}
|
||||
|
||||
static cast(v: Point | Vector) {
|
||||
return "cast" in v ? v : new Vector(v)
|
||||
}
|
||||
|
||||
static from(v: Vector) {
|
||||
return new Vector(v)
|
||||
}
|
||||
|
||||
nearestPointOnLineThroughPoint(b: Vector, u: Vector) {
|
||||
return this.clone().add(u.clone().mul(Vector.sub(this, b).pry(u)))
|
||||
}
|
||||
|
||||
static nearestPointOnLineThroughPoint(a: Vector, b: Vector, u: Vector) {
|
||||
return a.clone().add(u.clone().mul(Vector.sub(a, b).pry(u)))
|
||||
}
|
||||
|
||||
distanceToLineThroughPoint(b: Vector, u: Vector) {
|
||||
return this.dist(Vector.nearestPointOnLineThroughPoint(b, u, this))
|
||||
}
|
||||
|
||||
static distanceToLineThroughPoint(a: Vector, b: Vector, u: Vector) {
|
||||
return a.dist(Vector.nearestPointOnLineThroughPoint(b, u, a))
|
||||
}
|
||||
|
||||
nearestPointOnLineSegment(p0: Vector, p1: Vector, clamp = true) {
|
||||
return Vector.nearestPointOnLineSegment(this, p0, p1, clamp)
|
||||
}
|
||||
|
||||
static nearestPointOnLineSegment(
|
||||
a: Vector,
|
||||
p0: Vector,
|
||||
p1: Vector,
|
||||
clamp = true
|
||||
) {
|
||||
const delta = Vector.sub(p1, p0)
|
||||
const length = delta.len()
|
||||
const u = Vector.div(delta, length)
|
||||
|
||||
const pt = Vector.add(p0, Vector.mul(u, Vector.pry(Vector.sub(a, p0), u)))
|
||||
|
||||
if (clamp) {
|
||||
const da = p0.dist(pt)
|
||||
const db = p1.dist(pt)
|
||||
|
||||
if (db < da && da > length) return p1
|
||||
if (da < db && db > length) return p0
|
||||
}
|
||||
|
||||
return pt
|
||||
}
|
||||
|
||||
distanceToLineSegment(p0: Vector, p1: Vector, clamp = true) {
|
||||
return Vector.distanceToLineSegment(this, p0, p1, clamp)
|
||||
}
|
||||
|
||||
static distanceToLineSegment(
|
||||
a: Vector,
|
||||
p0: Vector,
|
||||
p1: Vector,
|
||||
clamp = true
|
||||
) {
|
||||
return Vector.dist(a, Vector.nearestPointOnLineSegment(a, p0, p1, clamp))
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue