react-three-nurbs
Version:
A React component library for NURBS (Non-Uniform Rational B-Spline) curves, surfaces, and solids in Three.js. Built with React Three Fiber, zero external NURBS dependencies — all math implemented from scratch. Boolean operations powered by OpenCASCADE WAS
1,528 lines • 105 kB
JavaScript
var en = Object.defineProperty;
var rn = (n, t, e) => t in n ? en(n, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : n[t] = e;
var Q = (n, t, e) => rn(n, typeof t != "symbol" ? t + "" : t, e);
import { jsx as D, jsxs as Zt } from "react/jsx-runtime";
import { useMemo as j, isValidElement as lt, forwardRef as Nt, Children as et, useRef as z, useState as jt, useEffect as on, useCallback as X } from "react";
import { Vector3 as U, BufferGeometry as tt, Float32BufferAttribute as F, DoubleSide as rt, Plane as sn, Raycaster as cn, Vector2 as ln } from "three";
import { Line as it } from "@react-three/drei";
import { useThree as un } from "@react-three/fiber";
function st(n, t, e, r) {
if (e >= r[n + 1]) return n;
if (e <= r[t]) return t;
let s = t, o = n + 1, c = s + o >> 1;
for (; e < r[c] || e >= r[c + 1]; )
e < r[c] ? o = c : s = c, c = s + o >> 1;
return c;
}
function gt(n, t, e, r) {
const s = new Array(e + 1), o = new Array(e + 1), c = new Array(e + 1);
s[0] = 1;
for (let l = 1; l <= e; l++) {
o[l] = t - r[n + 1 - l], c[l] = r[n + l] - t;
let h = 0;
for (let f = 0; f < l; f++) {
const p = s[f] / (c[f + 1] + o[l - f]);
s[f] = h + c[f + 1] * p, h = o[l - f] * p;
}
s[l] = h;
}
return s;
}
function Kt(n, t, e, r, s) {
const o = Array.from(
{ length: e + 1 },
() => new Array(e + 1).fill(0)
), c = new Array(e + 1), l = new Array(e + 1);
o[0][0] = 1;
for (let a = 1; a <= e; a++) {
c[a] = t - s[n + 1 - a], l[a] = s[n + a] - t;
let i = 0;
for (let u = 0; u < a; u++) {
o[a][u] = l[u + 1] + c[a - u];
const g = o[u][a - 1] / o[a][u];
o[u][a] = i + l[u + 1] * g, i = c[a - u] * g;
}
o[a][a] = i;
}
const h = Array.from(
{ length: r + 1 },
() => new Array(e + 1).fill(0)
);
for (let a = 0; a <= e; a++)
h[0][a] = o[a][e];
const f = [new Array(e + 1).fill(0), new Array(e + 1).fill(0)];
for (let a = 0; a <= e; a++) {
let i = 0, u = 1;
f[0][0] = 1;
for (let g = 1; g <= r; g++) {
let m = 0;
const y = a - g, w = e - g;
a >= g && (f[u][0] = f[i][0] / o[w + 1][y], m = f[u][0] * o[y][w]);
const v = y >= -1 ? 1 : -y, x = a - 1 <= w ? g - 1 : e - a;
for (let P = v; P <= x; P++)
f[u][P] = (f[i][P] - f[i][P - 1]) / o[w + 1][y + P], m += f[u][P] * o[y + P][w];
a <= w && (f[u][g] = -f[i][g - 1] / o[w + 1][a], m += f[u][g] * o[a][w]), h[g][a] = m;
const d = i;
i = u, u = d;
}
}
let p = e;
for (let a = 1; a <= r; a++) {
for (let i = 0; i <= e; i++)
h[a][i] *= p;
p *= e - a;
}
return h;
}
function Tt(n, t, e) {
let r = n;
for (let s = 0; s < e; s++)
r = an(r, t);
return r;
}
function an(n, t) {
const { degree: e, knots: r, controlPoints: s, weights: o } = n, c = s.length - 1, l = s[0].length;
let h = e;
for (let i = 0; i < r.length - 1; i++)
if (r[i] <= t && t < r[i + 1]) {
h = i;
break;
}
t >= r[r.length - e - 1] && (h = r.length - e - 2);
const f = [...r.slice(0, h + 1), t, ...r.slice(h + 1)], p = new Array(c + 2), a = new Array(c + 2);
for (let i = 0; i <= c + 1; i++)
if (i <= h - e)
p[i] = [...s[i]], a[i] = o[i];
else if (i >= h + 1)
p[i] = [...s[i - 1]], a[i] = o[i - 1];
else {
const u = (t - r[i]) / (r[i + e] - r[i]), g = o[i - 1], m = o[i], y = (1 - u) * g + u * m, w = new Array(l);
for (let v = 0; v < l; v++)
w[v] = ((1 - u) * g * s[i - 1][v] + u * m * s[i][v]) / y;
p[i] = w, a[i] = y;
}
return {
degree: e,
knots: f,
controlPoints: p,
weights: a
};
}
function Vt(n, t) {
const e = [...t].sort((s, o) => s - o);
let r = n;
for (const s of e)
r = Tt(r, s, 1);
return r;
}
function fn(n, t, e) {
const { degreeU: r, degreeV: s, knotsU: o, knotsV: c, controlPoints: l, weights: h } = n, f = l.length, p = l[0].length;
if (e) {
const a = [];
for (let i = 0; i < f; i++) {
const u = {
degree: s,
knots: [...c],
controlPoints: l[i].map((m) => [...m]),
weights: [...h[i]]
}, g = Vt(u, t);
a.push({ cp: g.controlPoints, w: g.weights, knots: g.knots });
}
return {
degreeU: r,
degreeV: s,
knotsU: [...o],
knotsV: a[0].knots,
controlPoints: a.map((i) => i.cp),
weights: a.map((i) => i.w)
};
} else {
const a = [], i = [], u = [];
for (let w = 0; w < p; w++) {
const v = {
degree: r,
knots: [...o],
controlPoints: Array.from({ length: f }, (d, P) => [...l[P][w]]),
weights: Array.from({ length: f }, (d, P) => h[P][w])
}, x = Vt(v, t);
w === 0 && a.push(...x.knots), i.push(x.controlPoints.map((d) => [d])), u.push(x.weights);
}
const g = i[0].length, m = [], y = [];
for (let w = 0; w < g; w++) {
m[w] = [], y[w] = [];
for (let v = 0; v < p; v++)
m[w][v] = i[v][w][0], y[w][v] = u[v][w];
}
return {
degreeU: r,
degreeV: s,
knotsU: a,
knotsV: [...c],
controlPoints: m,
weights: y
};
}
}
function Ot(n, t) {
if (t <= n.degree) return { ...n };
const e = t - n.degree;
let r = n;
for (let s = 0; s < e; s++)
r = hn(r);
return r;
}
function hn(n) {
const { degree: t, knots: e, controlPoints: r, weights: s } = n, o = r.length - 1, c = r[0].length, l = t + 1, h = r.map((y, w) => {
const v = s[w];
return [...y.map((x) => x * v), v];
}), f = [];
let p = 0;
for (; p < e.length; ) {
const y = e[p];
let w = 0;
for (; p < e.length && Math.abs(e[p] - y) < 1e-14; )
w++, p++;
f.push({ value: y, mult: w });
}
const a = [];
for (const y of f)
for (let w = 0; w < y.mult + 1; w++)
a.push(y.value);
const i = a.length - l - 2, u = new Array(i + 1);
for (let y = 0; y <= i; y++) {
const w = y <= 0 ? 0 : y > o ? 1 : y / l, v = Math.max(0, Math.min(o, y - 1)), x = Math.max(0, Math.min(o, y));
u[y] = new Array(c + 1);
for (let d = 0; d <= c; d++)
u[y][d] = w * h[v][d] + (1 - w) * h[x][d];
}
const g = u.map((y) => {
const w = y[c];
return y.slice(0, c).map((v) => w !== 0 ? v / w : v);
}), m = u.map((y) => y[c]);
return {
degree: l,
knots: a,
controlPoints: g,
weights: m
};
}
function Ht(n) {
if (n.length === 0) return [];
if (n.length === 1) return [{ ...n[0] }];
const t = Math.max(...n.map((s) => s.degree));
let e = n.map(
(s) => s.degree < t ? Ot(s, t) : { ...s }
);
const r = /* @__PURE__ */ new Set();
for (const s of e)
for (let o = t + 1; o < s.knots.length - t - 1; o++)
r.add(s.knots[o]);
return e = e.map((s) => {
const o = [];
for (const c of r)
s.knots.some((h) => Math.abs(h - c) < 1e-14) || o.push(c);
return o.length > 0 ? Vt(s, o) : s;
}), e;
}
function Gt(n, t) {
const e = n.length - 1, r = n[0].length, s = Math.min(t, e), o = new Array(e + 1);
o[0] = 0;
const c = new Array(e);
let l = 0;
for (let i = 1; i <= e; i++) {
let u = 0;
for (let g = 0; g < r; g++)
u += (n[i][g] - n[i - 1][g]) ** 2;
c[i - 1] = Math.sqrt(u), l += c[i - 1];
}
l === 0 && (l = 1);
for (let i = 1; i <= e; i++)
o[i] = o[i - 1] + c[i - 1] / l;
o[e] = 1;
const h = e + s + 1, f = new Array(h + 1);
for (let i = 0; i <= s; i++) f[i] = 0;
for (let i = h - s; i <= h; i++) f[i] = 1;
for (let i = 1; i <= e - s; i++) {
let u = 0;
for (let g = i; g <= i + s - 1; g++)
u += o[g];
f[i + s] = u / s;
}
const p = Array.from({ length: e + 1 }, () => new Array(e + 1).fill(0));
for (let i = 0; i <= e; i++) {
const u = st(e, s, o[i], f), g = gt(u, o[i], s, f);
for (let m = 0; m <= s; m++)
p[i][u - s + m] = g[m];
}
const a = new Array(e + 1);
for (let i = 0; i <= e; i++) a[i] = new Array(r);
for (let i = 0; i < r; i++) {
const u = n.map((m) => m[i]), g = pn(p, u);
for (let m = 0; m <= e; m++)
a[m][i] = g[m];
}
return {
degree: s,
knots: f,
controlPoints: a,
weights: new Array(e + 1).fill(1)
};
}
function gn(n, t) {
if (n.length < 2) throw new Error("Lofting requires at least 2 curves");
const e = Ht(n), r = e[0].degree, s = e[0].knots, o = e[0].controlPoints.length, c = e.length, l = Math.min(t, c - 1), h = [];
for (let g = 0; g < o; g++) {
const m = [];
for (let y = 0; y < c; y++)
m.push(e[y].controlPoints[g]);
h.push(m);
}
const f = h.map(
(g) => Gt(g, l)
), p = f[0].knots, a = f[0].controlPoints.length, i = [], u = [];
for (let g = 0; g < o; g++) {
i[g] = [], u[g] = [];
for (let m = 0; m < a; m++)
i[g][m] = f[g].controlPoints[m], u[g][m] = 1;
}
return {
degreeU: r,
degreeV: l,
knotsU: s,
knotsV: p,
controlPoints: i,
weights: u
};
}
function pn(n, t) {
const e = t.length, r = n.map((o, c) => [...o, t[c]]);
for (let o = 0; o < e; o++) {
let c = o, l = Math.abs(r[o][o]);
for (let f = o + 1; f < e; f++)
Math.abs(r[f][o]) > l && (l = Math.abs(r[f][o]), c = f);
c !== o && ([r[o], r[c]] = [r[c], r[o]]);
const h = r[o][o];
if (!(Math.abs(h) < 1e-14))
for (let f = o + 1; f < e; f++) {
const p = r[f][o] / h;
for (let a = o; a <= e; a++)
r[f][a] -= p * r[o][a];
}
}
const s = new Array(e).fill(0);
for (let o = e - 1; o >= 0; o--) {
let c = r[o][e];
for (let l = o + 1; l < e; l++)
c -= r[o][l] * s[l];
s[o] = Math.abs(r[o][o]) > 1e-14 ? c / r[o][o] : 0;
}
return s;
}
let L = class ut {
constructor(t) {
Q(this, "_degree");
Q(this, "_knots");
Q(this, "_controlPoints");
Q(this, "_weights");
this._degree = t.degree, this._knots = t.knots, this._controlPoints = t.controlPoints, this._weights = t.weights;
}
// --- Construction ---
static byKnotsControlPointsWeights(t, e, r, s) {
return new ut({ degree: t, knots: e, controlPoints: r, weights: s });
}
static byPoints(t, e) {
const r = Gt(t, e);
return new ut(r);
}
// --- Evaluation ---
/**
* Evaluate curve point at parameter t (Algorithm A4.1).
* Rational curve: C(t) = Σ R_i(t) * P_i where R_i = N_i*w_i / Σ N_j*w_j
*/
point(t) {
const e = this._controlPoints.length - 1, r = st(e, this._degree, t, this._knots), s = gt(r, t, this._degree, this._knots), o = this._controlPoints[0].length, c = new Array(o).fill(0);
let l = 0;
for (let h = 0; h <= this._degree; h++) {
const f = r - this._degree + h, p = this._weights[f], a = s[h] * p;
l += a;
for (let i = 0; i < o; i++)
c[i] += a * this._controlPoints[f][i];
}
if (l !== 0)
for (let h = 0; h < o; h++)
c[h] /= l;
return c;
}
/**
* Compute derivatives of the rational curve at parameter t.
* Uses Algorithm A4.2 + Eq. 4.20 from The NURBS Book.
*
* Returns: derivatives[k] = kth derivative vector
*/
derivatives(t, e) {
const r = this._controlPoints.length - 1, s = this._controlPoints[0].length, o = Math.min(e, this._degree), c = st(r, this._degree, t, this._knots), l = Kt(c, t, this._degree, o, this._knots), h = [], f = [];
for (let a = 0; a <= o; a++) {
const i = new Array(s).fill(0);
let u = 0;
for (let g = 0; g <= this._degree; g++) {
const m = c - this._degree + g, y = this._weights[m];
for (let w = 0; w < s; w++)
i[w] += l[a][g] * y * this._controlPoints[m][w];
u += l[a][g] * y;
}
h.push(i), f.push(u);
}
const p = [];
for (let a = 0; a <= o; a++) {
const i = [...h[a]];
for (let u = 1; u <= a; u++) {
const g = dn(a, u);
for (let m = 0; m < s; m++)
i[m] -= g * f[u] * p[a - u][m];
}
for (let u = 0; u < s; u++)
i[u] /= f[0];
p.push(i);
}
for (let a = o + 1; a <= e; a++)
p.push(new Array(s).fill(0));
return p;
}
/**
* Compute tangent vector (first derivative, NOT normalized) at parameter t.
*/
tangent(t) {
return this.derivatives(t, 1)[1];
}
/**
* Compute arc length using Gauss-Legendre quadrature.
*/
length() {
return this.lengthBetween(this._knots[this._degree], this._knots[this._knots.length - this._degree - 1]);
}
lengthBetween(t, e) {
const r = [-0.9061798459, -0.5384693101, 0, 0.5384693101, 0.9061798459], s = [0.2369268851, 0.4786286705, 0.5688888889, 0.4786286705, 0.2369268851], o = Math.max(this._controlPoints.length * 2, 8);
let c = 0;
for (let l = 0; l < o; l++) {
const h = t + (e - t) * l / o, f = t + (e - t) * (l + 1) / o, p = (f - h) / 2, a = (h + f) / 2;
let i = 0;
for (let u = 0; u < r.length; u++) {
const g = a + p * r[u], m = this.tangent(g), y = Math.sqrt(m.reduce((w, v) => w + v * v, 0));
i += s[u] * y;
}
c += p * i;
}
return c;
}
/**
* Find the parameter of the closest point on the curve to a given point.
* Implements Algorithm A6.1 from "The NURBS Book" (Piegl & Tiller), Section 6.1.
*
* Phase 1: Initial guess via control polygon (Greville abscissa of closest control point)
* + refinement by sampling within the support of that basis function.
* Phase 2: Newton iteration with four convergence criteria:
* (1) Point coincidence: ||C(t) - P|| < eps1
* (2) Zero cosine: |C'(t) · (C(t) - P)| / (|C'(t)| · |C(t) - P|) < eps2
* (3) Parameter correction: |Δt| · |C'(t)| < eps1
* (4) Domain bounds
*/
closestParam(t) {
const e = t.length, r = 1e-8, s = 1e-6, o = this._knots[this._degree], c = this._knots[this._knots.length - this._degree - 1], l = this._controlPoints.length - 1;
let h = o, f = 1 / 0;
for (let w = 0; w <= l; w++) {
let v = 0;
for (let x = 0; x < e; x++)
v += (this._controlPoints[w][x] - t[x]) ** 2;
if (v < f) {
f = v;
let x = 0;
for (let d = 1; d <= this._degree; d++)
x += this._knots[w + d];
x /= this._degree, h = Math.max(o, Math.min(c, x));
}
}
const p = 0.2 * (c - o), a = Math.max(o, h - p), i = Math.min(c, h + p), u = 20;
for (let w = 0; w <= u; w++) {
const v = a + (i - a) * w / u, x = this.point(v);
let d = 0;
for (let P = 0; P < e; P++) d += (x[P] - t[P]) ** 2;
d < f && (f = d, h = v);
}
const g = Math.max(l * 4, 20);
for (let w = 0; w <= g; w++) {
const v = o + (c - o) * w / g, x = this.point(v);
let d = 0;
for (let P = 0; P < e; P++) d += (x[P] - t[P]) ** 2;
d < f && (f = d, h = v);
}
let m = h;
const y = Math.min(2, this._degree);
for (let w = 0; w < 50; w++) {
const v = this.derivatives(m, y), x = v[0], d = v[1], P = x.map((I, N) => I - t[N]);
let M = 0;
for (let I = 0; I < e; I++) M += P[I] ** 2;
if (Math.sqrt(M) < r) break;
let k = 0, b = 0;
for (let I = 0; I < e; I++)
k += d[I] * P[I], b += d[I] ** 2;
b = Math.sqrt(b);
const A = Math.sqrt(M);
if (b > 0 && A > 0 && Math.abs(k) / (b * A) < s)
break;
let _ = k, C = b * b;
if (y >= 2) {
const I = v[2];
for (let N = 0; N < e; N++)
C += I[N] * P[N];
}
if (Math.abs(C) < 1e-14) break;
const S = -_ / C;
if (Math.abs(S) * b < r) break;
const V = Math.max(o, Math.min(c, m + S));
if (Math.abs(V - m) < 1e-14) break;
m = V;
}
return m;
}
closestPoint(t) {
return this.point(this.closestParam(t));
}
/**
* Divide curve into segments of equal arc length.
* Returns array of { u, pt } where u is the parameter and pt is the 3D point.
*/
divideByEqualArcLength(t) {
const r = this.length() / t, s = this._knots[this._degree], o = this._knots[this._knots.length - this._degree - 1], c = [];
c.push({ u: s, pt: this.point(s) });
const l = Math.max(t * 20, 200);
let h = 0, f = r, p = this.point(s), a = 1;
for (let i = 1; i <= l && a < t; i++) {
const u = s + (o - s) * i / l, g = this.point(u);
let m = 0;
for (let y = 0; y < p.length; y++)
m += (g[y] - p[y]) ** 2;
for (m = Math.sqrt(m), h += m; h >= f && a < t; ) {
const y = h - f, w = m > 0 ? (m - y) / m : 0, v = s + (o - s) * (i - 1) / l, x = v + w * (u - v);
c.push({ u: x, pt: this.point(x) }), f += r, a++;
}
p = g;
}
return c.push({ u: o, pt: this.point(o) }), c;
}
/**
* Split curve at parameter t.
* Inserts knot t until multiplicity = degree, then splits the data at that point.
*/
split(t) {
const e = this._degree;
let r = this.asData(), s = 0;
for (const y of r.knots)
Math.abs(y - t) < 1e-10 && s++;
const o = e - s;
for (let y = 0; y < o; y++)
r = Tt(r, t, 1);
let c = -1;
for (let y = 0; y < r.knots.length; y++)
if (Math.abs(r.knots[y] - t) < 1e-10) {
c = y;
break;
}
if (c === -1) return [this.clone()];
const l = c - 1, h = [...r.knots.slice(0, c + 1)];
for (; h.length < l + 1 + e + 1; ) h.push(t);
const f = r.controlPoints.slice(0, l + 1), p = r.weights.slice(0, l + 1), a = [];
for (let y = 0; y < e + 1; y++) a.push(t);
a.push(...r.knots.slice(c + e));
const i = r.controlPoints.slice(l), u = r.weights.slice(l), g = f.length > 0 && h.length === f.length + e + 1, m = i.length > 0 && a.length === i.length + e + 1;
return !g || !m ? (console.warn("Curve split produced invalid data, returning approximation"), [this.clone()]) : [
new ut({ degree: e, knots: h, controlPoints: f, weights: p }),
new ut({ degree: e, knots: a, controlPoints: i, weights: u })
];
}
reverse() {
const t = this._knots[this._knots.length - 1], e = this._knots[0];
return new ut({
degree: this._degree,
knots: this._knots.map((r) => t + e - r).reverse(),
controlPoints: [...this._controlPoints].reverse(),
weights: [...this._weights].reverse()
});
}
clone() {
return new ut({
degree: this._degree,
knots: [...this._knots],
controlPoints: this._controlPoints.map((t) => [...t]),
weights: [...this._weights]
});
}
// --- Accessors ---
degree() {
return this._degree;
}
knots() {
return this._knots;
}
controlPoints() {
return this._controlPoints;
}
weights() {
return this._weights;
}
asData() {
return {
degree: this._degree,
knots: [...this._knots],
controlPoints: this._controlPoints.map((t) => [...t]),
weights: [...this._weights]
};
}
};
function dn(n, t) {
if (t < 0 || t > n) return 0;
if (t === 0 || t === n) return 1;
let e = 1;
for (let r = 0; r < t; r++)
e = e * (n - r) / (r + 1);
return Math.round(e);
}
let J = class Ut {
// [u][v]
constructor(t) {
Q(this, "_degreeU");
Q(this, "_degreeV");
Q(this, "_knotsU");
Q(this, "_knotsV");
Q(this, "_controlPoints");
// [u][v][xyz]
Q(this, "_weights");
this._degreeU = t.degreeU, this._degreeV = t.degreeV, this._knotsU = t.knotsU, this._knotsV = t.knotsV, this._controlPoints = t.controlPoints, this._weights = t.weights;
}
// --- Construction ---
static byKnotsControlPointsWeights(t, e, r, s, o, c) {
return new Ut({ degreeU: t, degreeV: e, knotsU: r, knotsV: s, controlPoints: o, weights: c });
}
static byLoftingCurves(t, e) {
return new Ut(gn(t.map((r) => r.asData()), e));
}
static byCorners(t, e, r, s) {
return new Ut({
degreeU: 1,
degreeV: 1,
knotsU: [0, 0, 1, 1],
knotsV: [0, 0, 1, 1],
controlPoints: [[t, s], [e, r]],
weights: [[1, 1], [1, 1]]
});
}
// --- Evaluation ---
/**
* Evaluate surface point at (u, v) — Algorithm A4.3 (rational tensor product).
*/
point(t, e) {
const r = this._controlPoints.length - 1, s = this._controlPoints[0].length - 1, o = this._controlPoints[0][0].length, c = st(r, this._degreeU, t, this._knotsU), l = st(s, this._degreeV, e, this._knotsV), h = gt(c, t, this._degreeU, this._knotsU), f = gt(l, e, this._degreeV, this._knotsV), p = new Array(o).fill(0);
let a = 0;
for (let i = 0; i <= this._degreeU; i++) {
const u = c - this._degreeU + i;
for (let g = 0; g <= this._degreeV; g++) {
const m = l - this._degreeV + g, y = this._weights[u][m], w = h[i] * f[g] * y;
a += w;
for (let v = 0; v < o; v++)
p[v] += w * this._controlPoints[u][m][v];
}
}
if (a !== 0)
for (let i = 0; i < o; i++)
p[i] /= a;
return p;
}
/**
* Compute surface normal at (u, v) as cross product of partial derivatives.
*/
normal(t, e) {
const r = this.derivatives(t, e, 1), s = r[1][0], o = r[0][1];
return [
s[1] * o[2] - s[2] * o[1],
s[2] * o[0] - s[0] * o[2],
s[0] * o[1] - s[1] * o[0]
];
}
/**
* Compute partial derivatives of the rational surface at (u, v).
* Returns ders[k][l] = mixed partial derivative ∂^(k+l)S / ∂u^k ∂v^l.
* Algorithm A4.4 adapted for rational surfaces (Eq. 4.20 generalized).
*/
derivatives(t, e, r) {
const s = this._controlPoints.length - 1, o = this._controlPoints[0].length - 1, c = this._controlPoints[0][0].length, l = Math.min(r, this._degreeU), h = Math.min(r, this._degreeV), f = st(s, this._degreeU, t, this._knotsU), p = st(o, this._degreeV, e, this._knotsV), a = Kt(f, t, this._degreeU, l, this._knotsU), i = Kt(p, e, this._degreeV, h, this._knotsV), u = [], g = [];
for (let y = 0; y <= l; y++) {
u[y] = [], g[y] = [];
for (let w = 0; w <= h; w++) {
const v = new Array(c).fill(0);
let x = 0;
for (let d = 0; d <= this._degreeU; d++) {
const P = f - this._degreeU + d;
for (let M = 0; M <= this._degreeV; M++) {
const k = p - this._degreeV + M, b = this._weights[P][k], A = a[y][d] * i[w][M] * b;
for (let _ = 0; _ < c; _++)
v[_] += A * this._controlPoints[P][k][_];
x += A;
}
}
u[y][w] = v, g[y][w] = x;
}
}
const m = [];
for (let y = 0; y <= r; y++) {
m[y] = [];
for (let w = 0; w <= r; w++) {
if (y > l || w > h) {
m[y][w] = new Array(c).fill(0);
continue;
}
const v = [...u[y][w]];
for (let x = 1; x <= w; x++) {
const d = Wt(w, x);
for (let P = 0; P < c; P++)
v[P] -= d * g[0][x] * m[y][w - x][P];
}
for (let x = 1; x <= y; x++) {
const d = Wt(y, x);
for (let P = 0; P < c; P++)
v[P] -= d * g[x][0] * m[y - x][w][P];
}
for (let x = 0; x < c; x++)
v[x] /= g[0][0];
m[y][w] = v;
}
}
return m;
}
/**
* Find closest UV parameters to a 3D point.
* Implements the surface point projection from "The NURBS Book" Section 6.1.
*
* Phase 1: Initial guess via closest control point (Greville abscissa)
* + grid refinement near the best candidate.
* Phase 2: Newton iteration with four convergence criteria:
* (1) Point coincidence: ||S(u,v) - P|| < eps1
* (2) Zero cosine (U): |S_u · (S - P)| / (|S_u| · |S - P|) < eps2
* (2) Zero cosine (V): |S_v · (S - P)| / (|S_v| · |S - P|) < eps2
* (3) Parameter correction: |Δu·S_u + Δv·S_v| < eps1
* (4) Domain bounds
*/
closestParam(t) {
const e = t.length, r = this._controlPoints.length - 1, s = this._controlPoints[0].length - 1, o = this._knotsU[this._degreeU], c = this._knotsU[r + 1], l = this._knotsV[this._degreeV], h = this._knotsV[s + 1], f = 1e-8, p = 1e-6;
let a = (o + c) / 2, i = (l + h) / 2, u = 1 / 0;
for (let w = 0; w <= r; w++) {
let v = 0;
for (let x = 1; x <= this._degreeU; x++) v += this._knotsU[w + x];
v /= this._degreeU;
for (let x = 0; x <= s; x++) {
let d = 0;
for (let P = 0; P < e; P++)
d += (this._controlPoints[w][x][P] - t[P]) ** 2;
if (d < u) {
u = d, a = Math.max(o, Math.min(c, v));
let P = 0;
for (let M = 1; M <= this._degreeV; M++) P += this._knotsV[x + M];
P /= this._degreeV, i = Math.max(l, Math.min(h, P));
}
}
}
const g = 20;
for (let w = 0; w <= g; w++)
for (let v = 0; v <= g; v++) {
const x = o + (c - o) * w / g, d = l + (h - l) * v / g, P = this.point(x, d);
let M = 0;
for (let k = 0; k < e; k++) M += (P[k] - t[k]) ** 2;
M < u && (u = M, a = x, i = d);
}
let m = a, y = i;
for (let w = 0; w < 50; w++) {
const v = this.derivatives(m, y, 1), x = v[0][0], d = v[1][0], P = v[0][1], M = x.map((K, Y) => K - t[Y]);
let k = 0;
for (let K = 0; K < e; K++) k += M[K] ** 2;
const b = Math.sqrt(k);
if (b < f) break;
let A = 0, _ = 0, C = 0, S = 0;
for (let K = 0; K < e; K++)
A += d[K] * M[K], _ += P[K] * M[K], C += d[K] ** 2, S += P[K] ** 2;
C = Math.sqrt(C), S = Math.sqrt(S);
const V = C > 0 && b > 0 ? Math.abs(A) / (C * b) : 0, I = S > 0 && b > 0 ? Math.abs(_) / (S * b) : 0;
if (V < p && I < p) break;
let N = 0, E = 0, q = 0;
for (let K = 0; K < e; K++)
N += d[K] * d[K], E += d[K] * P[K], q += P[K] * P[K];
const Z = N * q - E * E;
if (Math.abs(Z) < 1e-14) break;
const H = -(q * A - E * _) / Z, B = -(N * _ - E * A) / Z;
let O = 0;
for (let K = 0; K < e; K++) {
const Y = H * d[K] + B * P[K];
O += Y * Y;
}
if (Math.sqrt(O) < f) break;
const ot = Math.max(o, Math.min(c, m + H)), bt = Math.max(l, Math.min(h, y + B));
if (Math.abs(ot - m) + Math.abs(bt - y) < 1e-14) break;
m = ot, y = bt;
}
return [m, y];
}
/**
* Extract an iso-parametric curve from the surface.
* If useV=false: fix u=param, extract curve in V direction.
* If useV=true: fix v=param, extract curve in U direction.
*/
/**
* Extract an iso-parametric curve from the surface.
* If useV=true: fix v=param, extract curve in U direction.
* If useV=false: fix u=param, extract curve in V direction.
*
* Works in homogeneous coordinates: for each row/column, blend the
* homogeneous control points (w*P, w) using basis functions, then
* store the result as the new curve's control points and weights.
*/
isocurve(t, e) {
if (e) {
const r = this._controlPoints.length - 1, s = this._controlPoints[0].length - 1, o = this._controlPoints[0][0].length, c = st(s, this._degreeV, t, this._knotsV), l = gt(c, t, this._degreeV, this._knotsV), h = [], f = [];
for (let p = 0; p <= r; p++) {
const a = new Array(o).fill(0);
let i = 0;
for (let u = 0; u <= this._degreeV; u++) {
const g = c - this._degreeV + u, m = this._weights[p][g];
for (let y = 0; y < o; y++)
a[y] += l[u] * m * this._controlPoints[p][g][y];
i += l[u] * m;
}
h.push(i !== 0 ? a.map((u) => u / i) : a), f.push(i);
}
return L.byKnotsControlPointsWeights(this._degreeU, [...this._knotsU], h, f);
} else {
const r = this._controlPoints.length - 1, s = this._controlPoints[0].length - 1, o = this._controlPoints[0][0].length, c = st(r, this._degreeU, t, this._knotsU), l = gt(c, t, this._degreeU, this._knotsU), h = [], f = [];
for (let p = 0; p <= s; p++) {
const a = new Array(o).fill(0);
let i = 0;
for (let u = 0; u <= this._degreeU; u++) {
const g = c - this._degreeU + u, m = this._weights[g][p];
for (let y = 0; y < o; y++)
a[y] += l[u] * m * this._controlPoints[g][p][y];
i += l[u] * m;
}
h.push(i !== 0 ? a.map((u) => u / i) : a), f.push(i);
}
return L.byKnotsControlPointsWeights(this._degreeV, [...this._knotsV], h, f);
}
}
// --- Accessors ---
degreeU() {
return this._degreeU;
}
degreeV() {
return this._degreeV;
}
knotsU() {
return this._knotsU;
}
knotsV() {
return this._knotsV;
}
controlPoints() {
return this._controlPoints;
}
weights() {
return this._weights;
}
asData() {
return {
degreeU: this._degreeU,
degreeV: this._degreeV,
knotsU: [...this._knotsU],
knotsV: [...this._knotsV],
controlPoints: this._controlPoints.map((t) => t.map((e) => [...e])),
weights: this._weights.map((t) => [...t])
};
}
};
function Wt(n, t) {
if (t < 0 || t > n) return 0;
if (t === 0 || t === n) return 1;
let e = 1;
for (let r = 0; r < t; r++)
e = e * (n - r) / (r + 1);
return Math.round(e);
}
function W(n, t) {
return n.map((e, r) => e + t[r]);
}
function xt(n, t) {
return n.map((e, r) => e - t[r]);
}
function $(n, t) {
return n.map((e) => e * t);
}
function Rt(n, t) {
return n.reduce((e, r, s) => e + r * t[s], 0);
}
function It(n, t) {
return [
n[1] * t[2] - n[2] * t[1],
n[2] * t[0] - n[0] * t[2],
n[0] * t[1] - n[1] * t[0]
];
}
function ct(n) {
const t = Math.sqrt(Rt(n, n));
return t > 0 ? $(n, 1 / t) : n;
}
function Ft(n) {
return Math.sqrt(Rt(n, n));
}
function at(n, t, e, r, s, o) {
const c = ct(t), l = ct(e);
let h = o - s;
h < 0 && (h += 2 * Math.PI);
let f;
h <= Math.PI / 2 ? f = 1 : h <= Math.PI ? f = 2 : h <= 3 * Math.PI / 2 ? f = 3 : f = 4;
const p = h / f, a = Math.cos(p / 2);
let i = W(n, W($(c, r * Math.cos(s)), $(l, r * Math.sin(s)))), u = W($(c, -Math.sin(s)), $(l, Math.cos(s)));
const g = [i], m = [1];
let y = s;
for (let v = 0; v < f; v++) {
y += p;
const x = W(n, W($(c, r * Math.cos(y)), $(l, r * Math.sin(y)))), d = W($(c, -Math.sin(y)), $(l, Math.cos(y))), P = xt(x, i), M = u[0] * d[1] - u[1] * d[0];
let k;
if (Math.abs(M) > 1e-14)
k = (P[0] * d[1] - P[1] * d[0]) / M;
else {
const A = u[0] * d[2] - u[2] * d[0];
Math.abs(A) > 1e-14 ? k = (P[0] * d[2] - P[2] * d[0]) / A : k = (P[1] * d[2] - P[2] * d[1]) / (u[1] * d[2] - u[2] * d[1]);
}
const b = W(i, $(u, k));
g.push(b, x), m.push(a, 1), i = x, u = d;
}
const w = [];
w.push(0, 0, 0);
for (let v = 1; v < f; v++) {
const x = v / f;
w.push(x, x);
}
return w.push(1, 1, 1), { degree: 2, knots: w, controlPoints: g, weights: m };
}
function mn(n, t, e, r) {
return at(n, t, e, r, 0, 2 * Math.PI);
}
function Jt(n, t, e, r, s) {
const o = Ft(t), c = Ft(e), l = ct(t), h = ct(e);
let f = s - r;
f < 0 && (f += 2 * Math.PI);
let p;
f <= Math.PI / 2 ? p = 1 : f <= Math.PI ? p = 2 : f <= 3 * Math.PI / 2 ? p = 3 : p = 4;
const a = f / p, i = Math.cos(a / 2);
let u = W(n, W($(l, o * Math.cos(r)), $(h, c * Math.sin(r)))), g = W($(l, -o * Math.sin(r)), $(h, c * Math.cos(r)));
const m = [u], y = [1];
let w = r;
for (let x = 0; x < p; x++) {
w += a;
const d = W(n, W($(l, o * Math.cos(w)), $(h, c * Math.sin(w)))), P = W($(l, -o * Math.sin(w)), $(h, c * Math.cos(w))), M = xt(d, u), k = g[0] * P[1] - g[1] * P[0];
let b;
if (Math.abs(k) > 1e-14)
b = (M[0] * P[1] - M[1] * P[0]) / k;
else {
const _ = g[0] * P[2] - g[2] * P[0];
Math.abs(_) > 1e-14 ? b = (M[0] * P[2] - M[2] * P[0]) / _ : b = (M[1] * P[2] - M[2] * P[1]) / (g[1] * P[2] - g[2] * P[1]);
}
const A = W(u, $(g, b));
m.push(A, d), y.push(i, 1), u = d, g = P;
}
const v = [];
v.push(0, 0, 0);
for (let x = 1; x < p; x++) {
const d = x / p;
v.push(d, d);
}
return v.push(1, 1, 1), { degree: 2, knots: v, controlPoints: m, weights: y };
}
function wn(n, t, e) {
return Jt(n, t, e, 0, 2 * Math.PI);
}
function yn(n, t, e, r, s) {
const o = ct(n), c = ct(t), l = It(o, c), h = at(e, c, l, s, 0, 2 * Math.PI), f = $(o, r);
return Et(h, f);
}
function Et(n, t) {
const e = n.controlPoints, r = e.map((s) => W(s, t));
return {
degreeU: n.degree,
degreeV: 1,
knotsU: [...n.knots],
knotsV: [0, 0, 1, 1],
controlPoints: e.map((s, o) => [s, r[o]]),
weights: n.weights.map((s) => [s, s])
};
}
function vt(n, t, e, r) {
const s = ct(e), o = at(
[0, 0, 0],
[1, 0, 0],
[0, 1, 0],
1,
0,
r
), c = o.controlPoints.length, l = [], h = [];
for (let a = 0; a < n.controlPoints.length; a++) {
const i = n.controlPoints[a], u = n.weights[a], g = xt(i, t), m = Rt(g, s), y = W(t, $(s, m)), w = xt(i, y), v = Ft(w);
if (v < 1e-14) {
l[a] = [], h[a] = [];
for (let M = 0; M < c; M++)
l[a][M] = [...i], h[a][M] = u * o.weights[M];
continue;
}
const x = $(w, 1 / v), d = It(s, x), P = at(y, x, d, v, 0, r);
l[a] = [], h[a] = [];
for (let M = 0; M < P.controlPoints.length; M++)
l[a][M] = P.controlPoints[M], h[a][M] = u * P.weights[M];
}
const f = [], p = [];
for (let a = 0; a < c; a++) {
f[a] = [], p[a] = [];
for (let i = 0; i < n.controlPoints.length; i++)
f[a][i] = l[i][a], p[a][i] = h[i][a];
}
return {
degreeU: 2,
// arc degree
degreeV: n.degree,
knotsU: o.knots,
knotsV: [...n.knots],
controlPoints: f,
weights: p
};
}
function xn(n, t) {
const e = n.controlPoints.length, r = t.controlPoints.length, s = t.controlPoints[0], o = [], c = [];
for (let l = 0; l < r; l++) {
o[l] = [], c[l] = [];
const h = xt(t.controlPoints[l], s);
for (let f = 0; f < e; f++)
o[l][f] = W(n.controlPoints[f], h), c[l][f] = t.weights[l] * n.weights[f];
}
return {
degreeU: t.degree,
degreeV: n.degree,
knotsU: [...t.knots],
knotsV: [...n.knots],
controlPoints: o,
weights: c
};
}
function ht(n, t, e, r, s = "forward") {
return {
surface: {
degreeU: 1,
degreeV: 1,
knotsU: [0, 0, 1, 1],
knotsV: [0, 0, 1, 1],
controlPoints: [[n, r], [t, e]],
weights: [[1, 1], [1, 1]]
},
orientation: s
};
}
function vn(n, t, e, r = [0, 0, 0]) {
const [s, o, c] = r, l = [s, o, c], h = [s + n, o, c], f = [s, o + t, c], p = [s + n, o + t, c], a = [s, o, c + e], i = [s + n, o, c + e], u = [s, o + t, c + e], g = [s + n, o + t, c + e];
return {
faces: [
// Bottom (z=0), normal pointing -Z
ht(l, h, p, f, "reversed"),
// Top (z=dz), normal pointing +Z
ht(a, i, g, u, "forward"),
// Front (y=0), normal pointing -Y
ht(l, h, i, a, "reversed"),
// Back (y=dy), normal pointing +Y
ht(f, p, g, u, "forward"),
// Left (x=0), normal pointing -X
ht(l, f, u, a, "forward"),
// Right (x=dx), normal pointing +X
ht(h, p, g, i, "reversed")
]
};
}
function Pn(n, t, e = [0, 1, 0], r = [0, 0, 0]) {
const s = ct(e), o = Math.abs(s[1]) < 0.9 ? [0, 1, 0] : [1, 0, 0], c = ct(It(s, o)), l = It(s, c), h = at(r, c, l, n, 0, 2 * Math.PI), f = $(s, t), p = Et(h, f), a = W(r, f), i = {
degree: 1,
knots: [0, 0, 1, 1],
controlPoints: [
[...r],
W(r, $(c, n))
],
weights: [1, 1]
}, u = vt(
i,
r,
s,
2 * Math.PI
), g = {
degree: 1,
knots: [0, 0, 1, 1],
controlPoints: [
[...a],
W(a, $(c, n))
],
weights: [1, 1]
}, m = vt(
g,
a,
s,
2 * Math.PI
);
return {
faces: [
{ surface: p, orientation: "forward" },
{ surface: u, orientation: "reversed" },
{ surface: m, orientation: "forward" }
]
};
}
function Mn(n, t = [0, 0, 0]) {
const e = at(
t,
[1, 0, 0],
// xaxis
[0, 1, 0],
// yaxis
n,
-Math.PI / 2,
// start at south pole (0, -R, 0)
Math.PI / 2
// end at north pole (0, R, 0)
);
return {
faces: [
{ surface: vt(
e,
t,
[0, 1, 0],
// Y axis
2 * Math.PI
), orientation: "forward" }
]
};
}
class G {
constructor(t) {
Q(this, "_faces");
this._faces = t.faces;
}
// --- Construction ---
static fromFaces(t) {
return new G({ faces: t });
}
static makeBox(t, e, r, s = [0, 0, 0]) {
return new G(vn(t, e, r, s));
}
static makeCylinder(t, e, r = [0, 1, 0], s = [0, 0, 0]) {
return new G(Pn(t, e, r, s));
}
static makeSphere(t, e = [0, 0, 0]) {
return new G(Mn(t, e));
}
// --- Curve/Surface → Solid bridges ---
/**
* Create a solid by revolving a closed curve profile around an axis.
* The profile should be a closed loop (or the revolution will close it).
* Result: 1 revolved surface face.
*/
static fromRevolution(t, e = [0, 0, 0], r = [0, 1, 0], s = 2 * Math.PI, o = !1) {
const l = [{ surface: vt(t, e, r, s), orientation: "forward" }];
if (o && s < 2 * Math.PI - 0.01) {
const h = Math.sqrt(r[0] ** 2 + r[1] ** 2 + r[2] ** 2), f = r[0] / h, p = r[1] / h, a = r[2] / h, i = Ct(t, e);
l.push({ surface: i, orientation: "reversed" });
const u = Math.cos(s), g = Math.sin(s), m = {
...t,
controlPoints: t.controlPoints.map((w) => {
const v = w[0] - e[0], x = w[1] - e[1], d = w[2] - e[2], P = v * f + x * p + d * a, M = v * u + (p * d - a * x) * g + f * P * (1 - u), k = x * u + (a * v - f * d) * g + p * P * (1 - u), b = d * u + (f * x - p * v) * g + a * P * (1 - u);
return [e[0] + M, e[1] + k, e[2] + b];
})
}, y = Ct(m, e);
l.push({ surface: y, orientation: "forward" });
}
return new G({ faces: l });
}
/**
* Create a solid by extruding a closed curve profile along a direction.
* Result: 1 extruded surface (sides) + 2 caps.
* Caps use a degenerate surface where one edge matches the profile exactly
* and the opposite edge collapses to the centroid.
*/
static fromExtrusion(t, e, r = !1) {
const o = [{ surface: Et(t, e), orientation: "forward" }];
if (r) {
const c = t.controlPoints.length, l = t.weights.reduce((u, g) => u + g, 0), h = [0, 0, 0];
for (let u = 0; u < c; u++) {
const g = t.weights[u] / l;
h[0] += t.controlPoints[u][0] * g, h[1] += t.controlPoints[u][1] * g, h[2] += t.controlPoints[u][2] * g;
}
const f = Ct(t, h);
o.push({ surface: f, orientation: "reversed" });
const p = [
h[0] + e[0],
h[1] + e[1],
h[2] + e[2]
], a = {
...t,
controlPoints: t.controlPoints.map((u) => [
u[0] + e[0],
u[1] + e[1],
u[2] + e[2]
])
}, i = Ct(a, p);
o.push({ surface: i, orientation: "forward" });
}
return new G({ faces: o });
}
/**
* Create a solid by thickening a surface.
* Offsets the surface by `thickness` along its normal direction,
* creating two faces (original + offset) connected by side surfaces.
* Simplified version: returns original + offset face only.
*/
static fromSurface(t, e) {
const r = new J(t), s = t.controlPoints.map(
(w, v) => w.map((x, d) => {
const P = v / (t.controlPoints.length - 1), M = d / (w.length - 1);
try {
const k = r.normal(P, M), b = Math.sqrt(k[0] ** 2 + k[1] ** 2 + k[2] ** 2);
if (b > 0)
return [
x[0] + k[0] / b * e,
x[1] + k[1] / b * e,
x[2] + k[2] / b * e
];
} catch {
}
return [x[0], x[1] + e, x[2]];
})
), o = {
...t,
controlPoints: s
}, c = t.controlPoints.length, l = t.controlPoints[0].length, h = [], f = t.controlPoints.map((w) => w[0]), p = s.map((w) => w[0]);
h.push({
surface: _t(f, p, t.degreeU, t.knotsU),
orientation: "forward"
});
const a = t.controlPoints.map((w) => w[l - 1]), i = s.map((w) => w[l - 1]);
h.push({
surface: _t(a, i, t.degreeU, t.knotsU),
orientation: "reversed"
});
const u = t.controlPoints[0], g = s[0];
h.push({
surface: _t(u, g, t.degreeV, t.knotsV),
orientation: "reversed"
});
const m = t.controlPoints[c - 1], y = s[c - 1];
return h.push({
surface: _t(m, y, t.degreeV, t.knotsV),
orientation: "forward"
}), new G({
faces: [
{ surface: t, orientation: "forward" },
{ surface: o, orientation: "reversed" },
...h
]
});
}
/**
* Create a solid face from a surface (wraps it as a FaceData).
* Useful for composing solids from individual surfaces.
*/
static faceFromSurface(t, e = "forward", r) {
return {
surface: t,
outerWire: r,
orientation: e
};
}
// --- Accessors ---
faces() {
return this._faces;
}
numFaces() {
return this._faces.length;
}
asData() {
return {
faces: this._faces.map((t) => {
var e, r;
return {
surface: {
degreeU: t.surface.degreeU,
degreeV: t.surface.degreeV,
knotsU: [...t.surface.knotsU],
knotsV: [...t.surface.knotsV],
controlPoints: t.surface.controlPoints.map(
(s) => s.map((o) => [...o])
),
weights: t.surface.weights.map((s) => [...s])
},
outerWire: (e = t.outerWire) == null ? void 0 : e.map((s) => ({
degree: s.degree,
knots: [...s.knots],
controlPoints: s.controlPoints.map((o) => [...o]),
weights: [...s.weights]
})),
holes: (r = t.holes) == null ? void 0 : r.map(
(s) => s.map((o) => ({
degree: o.degree,
knots: [...o.knots],
controlPoints: o.controlPoints.map((c) => [...c]),
weights: [...o.weights]
}))
),
orientation: t.orientation
};
})
};
}
clone() {
return new G(this.asData());
}
}
function Ct(n, t) {
return {
degreeU: n.degree,
degreeV: 1,
knotsU: [...n.knots],
knotsV: [0, 0, 1, 1],
// Each row has 2 points: [center, profileControlPoint]
// At V=0 all points collapse to center, at V=1 they trace the profile
controlPoints: n.controlPoints.map((e) => [[...t], [...e]]),
// Weights match the profile in U, uniform in V
weights: n.weights.map((e) => [1, e])
};
}
function _t(n, t, e, r) {
return {
degreeU: e,
degreeV: 1,
knotsU: [...r],
knotsV: [0, 0, 1, 1],
controlPoints: n.map((s, o) => [s, t[o]]),
weights: n.map(() => [1, 1])
};
}
function kn(n, t = {}) {
const {
tolerance: e = 0.01,
normalTolerance: r,
minDivsU: s = 4,
minDivsV: o = 4,
maxDepth: c = 6,
normals: l = !0
} = t, h = r !== void 0 && r > 0, f = h ? Math.cos(r) : 1, p = [], a = [], i = /* @__PURE__ */ new Map();
function u(d, P) {
const M = `${d.toFixed(10)},${P.toFixed(10)}`, k = i.get(M);
if (k !== void 0) return k;
const b = n.point(d, P);
let A = [0, 0, 1];
if (l)
try {
const C = n.normal(d, P), S = Math.sqrt(C[0] ** 2 + C[1] ** 2 + C[2] ** 2);
S > 0 && (A = [C[0] / S, C[1] / S, C[2] / S]);
} catch {
}
const _ = p.length;
return p.push({ pos: b, normal: A, uv: [d, P] }), i.set(M, _), _;
}
function g(d, P, M, k) {
const b = (d + M) / 2, A = (P + k) / 2, _ = n.point(b, A), C = n.point(d, P), S = n.point(M, P), V = n.point(d, k), I = n.point(M, k), N = [
0.25 * (C[0] + S[0] + V[0] + I[0]),
0.25 * (C[1] + S[1] + V[1] + I[1]),
0.25 * (C[2] + S[2] + V[2] + I[2])
], E = _[0] - N[0], q = _[1] - N[1], Z = _[2] - N[2];
if (Math.sqrt(E * E + q * q + Z * Z) > e) return !0;
if (h)
try {
const B = n.normal(b, A), O = Math.sqrt(B[0] ** 2 + B[1] ** 2 + B[2] ** 2);
if (O > 0) {
const ot = [[d, P], [M, P], [d, k], [M, k]];
for (const [bt, K] of ot) {
const Y = n.normal(bt, K), qt = Math.sqrt(Y[0] ** 2 + Y[1] ** 2 + Y[2] ** 2);
if (qt > 0 && (B[0] * Y[0] + B[1] * Y[1] + B[2] * Y[2]) / (O * qt) < f)
return !0;
}
}
} catch {
}
return !1;
}
function m(d, P, M, k, b) {
if (b < c && g(d, P, M, k)) {
const A = (d + M) / 2, _ = (P + k) / 2;
m(d, P, A, _, b + 1), m(A, P, M, _, b + 1), m(d, _, A, k, b + 1), m(A, _, M, k, b + 1);
} else {
const A = u(d, P), _ = u(M, P), C = u(d, k), S = u(M, k);
a.push(A, _, C), a.push(_, S, C);
}
}
for (let d = 0; d < s; d++)
for (let P = 0; P < o; P++) {
const M = d / s, k = (d + 1) / s, b = P / o, A = (P + 1) / o;
m(M, b, k, A, 0);
}
const y = p.length, w = new Float32Array(y * 3), v = new Float32Array(y * 3), x = new Float32Array(y * 2);
for (let d = 0; d < y; d++)
w[d * 3] = p[d].pos[0], w[d * 3 + 1] = p[d].pos[1], w[d * 3 + 2] = p[d].pos[2], v[d * 3] = p[d].normal[0], v[d * 3 + 1] = p[d].normal[1], v[d * 3 + 2] = p[d].normal[2], x[d * 2] = p[d].uv[0], x[d * 2 + 1] = p[d].uv[1];
return {
vertices: w,
normals: v,
uvs: x,
indices: new Uint32Array(a)
};
}
function bn(n, t, e = 1e-3, r = 40) {
const s = r, o = [], c = [];
for (let f = 0; f <= s; f++) {
o[f] = [], c[f] = [];
for (let p = 0; p <= s; p++) {
const a = f / s, i = p / s, u = n.point(a, i), g = t.closestParam(u), m = t.point(g[0], g[1]), y = n.normal(a, i), w = Math.sqrt(y[0] ** 2 + y[1] ** 2 + y[2] ** 2), v = [u[0] - m[0], u[1] - m[1], u[2] - m[2]];
let d = Math.sqrt(v[0] ** 2 + v[1] ** 2 + v[2] ** 2);
w > 1e-14 && (d = (v[0] * y[0] + v[1] * y[1] + v[2] * y[2]) / w), o[f][p] = d, c[f][p] = u;
}
}
const l = [];
for (let f = 0; f < s; f++)
for (let p = 0; p < s; p++) {
const a = [
{ d: o[f][p], pt: c[f][p] },
{ d: o[f + 1][p], pt: c[f + 1][p] },
{ d: o[f + 1][p + 1], pt: c[f + 1][p + 1] },
{ d: o[f][p + 1], pt: c[f][p + 1] }
];
for (let i = 0; i < 4; i++) {
const u = a[i], g = a[(i + 1) % 4];
if (u.d >= 0 != g.d >= 0) {
const m = u.d / (u.d - g.d);
l.push([
u.pt[0] + m * (g.pt[0] - u.pt[0]),
u.pt[1] + m * (g.pt[1] - u.pt[1]),
u.pt[2] + m * (g.pt[2] - u.pt[2])
]);
}
}
}
return l.length < 2 ? [] : Cn(l, e * 10).map((f) => ({ points: f }));
}
function Cn(n, t) {
if (n.length === 0) return [];
const e = /* @__PURE__ */ new Set(), r = [];
for (; e.size < n.length; ) {
let s = -1;
for (let l = 0; l < n.length; l++)
if (!e.has(l)) {
s = l;
break;
}
if (s === -1) break;
const o = [n[s]];
e.add(s);
let c = !0;
for (; c; ) {
c = !1;
const l = o[o.length - 1];
let h = -1, f = t * t;
for (let p = 0; p < n.length; p++) {
if (e.has(p)) continue;
const a = n[p][0] - l[0], i = n[p][1] - l[1], u = n[p][2] - l[2], g = a * a + i * i + u * u;
g < f && (f = g, h = p);
}
h >= 0 && (o.push(n[h]), e.add(h), c = !0);
}
o.length >= 2 && r.push(o);
}
return r;
}
function T(n, t) {
const e = n + t + 1;
return Array(e).fill(0).map((r, s) => s < t + 1 ? 0 : s >= e - t - 1 ? 1 : (s - t) / (e - 2 * t - 1));
}
function _n(n, t, e) {
const r = n.point(t, e);
return new U(r[0], r[1], r[2]);
}
function An(n, t, e) {
try {
const r = n.normal(t, e), s = new U(r[0], r[1], r[2]), o = s.length();
return o > 0 ? s.divideScalar(o) : new U(0, 1, 0);
} catch {
const s = n.point(t, e), o = n.point(Math.min(t + 1e-4, 1), e), c = n.point(t, Math.min(e + 1e-4, 1)), l = new U().subVectors(new U(...o), new U(...s)), h = new U().subVectors(new U(...c), new U(...s)), f = new U().crossVectors(l, h).normalize();
return f.length() > 0 ? f : new U(0, 1, 0);
}
}
function le(n, t = 100) {
return Array.from({ length: t }, (e, r) => {
const s = r / (t - 1), o = n.point(s);
return [o[0], o[1]];
});
}
function Sn(n, t = 5, e = 10) {
const r = (f) => [f[0], f[1]], s = (f, p, a) => {
const i = [p[0] - f[0], p[1] - f[1]], u = [a[0] - p[0], a[1] - p[1]], g = i[0] * u[0] + i[1] * u[1], m = Math.sqrt(i[0] * i[0] + i[1] * i[1]), y = Math.sqrt(u[0] * u[0] + u[1] * u[1]);
return m === 0 || y === 0 ? 0 : Math.acos(Math.max(-1, Math.min(1, g / (m * y)))) * (180 / Math.PI);
};
function o(f, p, a) {
const i = r(n.point(f)), u = r(n.point((f + p) / 2)), g = r(n.point(p));
if (s(i, u, g) > t && a < e) {
const y = o(f, (f + p) / 2, a + 1), w = o((f + p) / 2, p, a + 1);
return [...y.slice(0, -1), ...w];
} else
return [i, g];
}
const c = [], l = 10;
let h = null;
for (let f = 0; f < l; f++) {
const p = o(f / l, (f + 1) / l, 0);
for (const a of p)
(!h || a[0] !== h[0] || a[1] !== h[1]) && (c.push(a), h = a);
}
return c;
}
function ue(n, t) {
try {
const e = n.closestParam(t);
return [e[0], e[1]];
} catch {
return null;
}
}
function jn(n, t, e = 100) {
const r = [], s = t.point(0), o = n.closestParam(s);
r.push([o[0], o[1]]);
for (let c = 1; c <= e; c++) {
const l = c / e, h = t.point(l), f = r[r.length - 1], p = Un(n, h, f[0], f[1]);
r.push(p);
}
return r;
}
function Un(n, t, e, r) {
const s = t.length, o = 1e-8, c = 1e-6;
let l = e, h = r;
for (let f = 0; f < 20; f++) {
const p = n.derivatives(l, h, 1), a = p[0][0], i = p[1][0], u = p[0][1], g = a.map((C, S) => C - t[S]);
let m = 0;
for (let C = 0; C < s; C++) m += g[C] ** 2;
if (Math.sqrt(m) < o) break;
let y = 0, w = 0, v = 0, x = 0;
for (let C = 0; C < s; C++)
y += i[C] * g[C], w += u[C] * g[C], v += i[C] ** 2, x += u[C] ** 2;
v = Math.sqrt(v), x = Math.sqrt(x);
const d = Math.sqrt(m);
if (v > 0 && x > 0 && d > 0 && Math.abs(y) / (v * d) < c && Math.abs(w) / (x * d) < c)
break;
let P = 0, M = 0, k = 0;
for (let C = 0; C < s; C++)
P += i[C] * i[C], M += i[C] * u[C], k += u[C] * u[C];
const b = P * k - M * M;
if (Math.abs(b) < 1e-14) break;
const A = -(k * y - M * w) / b, _ = -(P * w - M * y) / b;
l = Math.max(0, Math.min(1, l + A)), h