Tldraw/packages/primitives/src/lib/polygon-helpers.ts

136 wiersze
3.1 KiB
TypeScript

import { Vec2d, VecLike } from './Vec2d'
import { toDomPrecision } from './utils'
function precise(A: VecLike) {
return `${toDomPrecision(A.x)},${toDomPrecision(A.y)} `
}
function rng(seed = '') {
let x = 0
let y = 0
let z = 0
let w = 0
function next() {
const t = x ^ (x << 11)
x = y
y = z
z = w
w ^= ((w >>> 19) ^ t ^ (t >>> 8)) >>> 0
return (w / 0x100000000) * 2
}
for (let k = 0; k < seed.length + 64; k++) {
x ^= seed.charCodeAt(k) | 0
next()
}
return next
}
/** @public */
export function getRoundedInkyPolygonPath(points: VecLike[]) {
let polylineA = `M`
const len = points.length
let p0: VecLike
let p1: VecLike
let p2: VecLike
for (let i = 0, n = len; i < n; i += 3) {
p0 = points[i]
p1 = points[i + 1]
p2 = points[i + 2]
polylineA += `${precise(p0)}L${precise(p1)}Q${precise(p2)}`
}
polylineA += `${precise(points[0])}`
return polylineA
}
/** @public */
export function getRoundedPolygonPoints(
id: string,
outline: VecLike[],
offset: number,
roundness: number,
passes: number
) {
const results: VecLike[] = []
const random = rng(id)
let p0 = outline[0]
let p1: VecLike
const len = outline.length
for (let i = 0, n = len * passes; i < n; i++) {
p1 = Vec2d.AddXY(outline[(i + 1) % len], random() * offset, random() * offset)
const delta = Vec2d.Sub(p1, p0)
const distance = Vec2d.Len(delta)
const vector = Vec2d.Div(delta, distance).mul(Math.min(distance / 4, roundness))
results.push(Vec2d.Add(p0, vector), Vec2d.Add(p1, vector.neg()), p1)
p0 = p1
}
return results
}
/** @public */
export function getDrawLinePathData(id: string, outline: VecLike[], strokeWidth: number) {
let innerPathData = `M${precise(outline[0])}L`
let outerPathData1 = `M${precise(outline[0])}L`
let outerPathData2 = `M${precise(outline[0])}L`
const offset = strokeWidth / 3
const roundness = strokeWidth * 2
const random = rng(id)
let p0 = outline[0]
let p1: VecLike
let s0 = outline[0]
let s1: VecLike
const len = outline.length
for (let i = 0, n = len - 1; i < n; i++) {
p1 = Vec2d.AddXY(outline[i + 1], random() * offset, random() * offset)
s1 = Vec2d.AddXY(outline[i + 1], random(), random() * offset)
const delta = Vec2d.Sub(p1, p0)
const distance = Vec2d.Len(delta)
const vector = Vec2d.Div(delta, distance).mul(Math.min(distance / 4, roundness))
const q0 = Vec2d.Add(p0, vector)
const q1 = Vec2d.Add(p1, vector.neg())
const sDelta = Vec2d.Sub(s1, s0)
const sDistance = Vec2d.Len(sDelta)
const sVector = Vec2d.Div(sDelta, sDistance).mul(Math.min(sDistance / 4, roundness))
const sq0 = Vec2d.Add(s0, sVector)
const sq1 = Vec2d.Add(s1, sVector.neg())
if (i === n - 1) {
innerPathData += `${precise(q0)} L ${precise(p1)}`
outerPathData1 += `${precise(q0)} L ${precise(p1)}`
outerPathData2 += `${precise(sq0)} L ${precise(s1)}`
} else {
innerPathData += `${precise(q0)}L ${precise(q1)}Q ${precise(p1)}`
outerPathData1 += `${precise(q0)}L ${precise(q1)}Q ${precise(p1)}`
outerPathData2 += `${precise(sq0)}L ${precise(sq1)}Q ${precise(s1)}`
p0 = p1
s0 = s1
}
}
return [innerPathData, outerPathData1 + outerPathData2]
}