Tldraw/packages/editor/src/lib/primitives/geometry/Ellipse2d.ts

101 wiersze
2.1 KiB
TypeScript

import { Box } from '../Box'
import { Vec } from '../Vec'
import { PI, PI2 } from '../utils'
import { Edge2d } from './Edge2d'
import { Geometry2d, Geometry2dOptions } from './Geometry2d'
import { getVerticesCountForLength } from './geometry-constants'
/** @public */
export class Ellipse2d extends Geometry2d {
w: number
h: number
constructor(
public config: Omit<Geometry2dOptions, 'isClosed'> & {
width: number
height: number
}
) {
super({ ...config, isClosed: true })
const { width, height } = config
this.w = width
this.h = height
}
_edges?: Edge2d[]
// eslint-disable-next-line no-restricted-syntax
get edges() {
if (!this._edges) {
const { vertices } = this
this._edges = []
for (let i = 0, n = vertices.length; i < n; i++) {
const start = vertices[i]
const end = vertices[(i + 1) % n]
this._edges.push(new Edge2d({ start, end }))
}
}
return this._edges
}
getVertices() {
// Perimeter of the ellipse
const w = Math.max(1, this.w)
const h = Math.max(1, this.h)
const cx = w / 2
const cy = h / 2
const q = Math.pow(cx - cy, 2) / Math.pow(cx + cy, 2)
const p = PI * (cx + cy) * (1 + (3 * q) / (10 + Math.sqrt(4 - 3 * q)))
// Number of points
const len = getVerticesCountForLength(p)
// Size of step
const step = PI2 / len
const a = Math.cos(step)
const b = Math.sin(step)
let sin = 0
let cos = 1
let ts = 0
let tc = 1
const vertices = Array(len)
for (let i = 0; i < len; i++) {
vertices[i] = new Vec(cx + cx * cos, cy + cy * sin)
ts = b * cos + a * sin
tc = a * cos - b * sin
sin = ts
cos = tc
}
return vertices
}
nearestPoint(A: Vec): Vec {
let nearest: Vec | undefined
let dist = Infinity
let d: number
let p: Vec
for (const edge of this.edges) {
p = edge.nearestPoint(A)
d = Vec.Dist2(p, A)
if (d < dist) {
nearest = p
dist = d
}
}
if (!nearest) throw Error('nearest point not found')
return nearest
}
hitTestLineSegment(A: Vec, B: Vec): boolean {
return this.edges.some((edge) => edge.hitTestLineSegment(A, B))
}
getBounds() {
return new Box(0, 0, this.w, this.h)
}
}