videx-3d
Version:
React 3D component library designed for sub surface visualizations in the browser
576 lines (575 loc) • 20.8 kB
JavaScript
var $ = Object.defineProperty;
var y = (r, t, s) => t in r ? $(r, t, { enumerable: !0, configurable: !0, writable: !0, value: s }) : r[t] = s;
var R = (r, t, s) => y(r, typeof t != "symbol" ? t + "" : t, s);
import { BufferGeometry as O, BufferAttribute as j } from "three";
import { lerp as S } from "three/src/math/MathUtils.js";
import { d as x, b as V, x as tt, y as st, f as H, a4 as nt, a1 as et, a6 as it } from "./chunk-CPDkHB2U.js";
function E(r, t, s) {
if (t[0] === s[0]) return s[1];
const n = s[0] - t[0], e = x((r - t[0]) / n, 0, 1);
return S(t[1], s[1], e);
}
function ot(r, t, s, n, e, h, l, f) {
const o = [];
if (t === "none" || h.length === 0)
o.push([s, e], [n, e]);
else {
let a = [0, e], c = [1, e];
const u = h.findIndex((g) => g[0] > s);
u === -1 ? a = h[h.length - 1] : (u > 0 && (a = h[u - 1]), c = h[u]);
const d = t === "linear" ? E(s, a, c) : a[1];
o.push([s, d]);
for (let g = u; g >= 0 && g < h.length; g++) {
const C = h[g];
if (C[0] < n)
o.push(C);
else {
t === "linear" ? o.push([n, E(n, o[o.length - 1], C)]) : o.push([n, o[o.length - 1][1]]);
break;
}
}
o[o.length - 1][0] < n && (t === "linear" ? o.push([
n,
E(n, o[o.length - 1], [1, e])
]) : o.push([n, o[o.length - 1][1]]));
}
const v = [], p = tt(
r,
s,
n,
l,
f
);
let m = 0, i = p[m];
for (let a = 0; a < o.length - 1; a++) {
const c = a + 1, [u, d] = o[a], [g, C] = o[c], P = (g - u) * r.length, z = C - d, T = Math.atan2(z, P);
for (v.push([
u,
d,
t === "linear" ? T : 0
]); i <= u && m < p.length - 1; )
i = p[++m];
for (; i < g && m < p.length; ) {
const G = t === "linear" ? E(i, o[a], o[c]) : d;
v.push([
i,
G,
t === "linear" ? T : 0
]), i = p[m++];
}
if (c < o.length) {
if (t === "linear")
v.push([
g,
C,
t === "linear" ? T : 0
]);
else if (t === "stepped") {
const G = T < 0 ? -V / 2 : V / 2;
v.push(
[g, d, 0],
[g, d, G],
[g, C, G]
);
}
}
c === o.length - 1 && t === "none" && v.push([g, C, T]);
}
const _ = st(
r,
v.map((a) => a[0])
);
return v.map((a, c) => ({
radius: a[1],
theta: a[2],
..._[c]
}));
}
function Y(r, t, s = !0, n, e = 0) {
let h = 0, l = 0;
const f = [], o = [], v = n.computeNormals ? [] : null, p = n.computeUvs ? [] : null, m = s ? [-r.tangent[0], -r.tangent[1], -r.tangent[2]] : r.tangent;
f.push(...r.position), h++, v && v.push(...m), p && p.push(0.5, 0.5);
for (let i = 0; i <= t; i++) {
const _ = i / t * V * 2, a = Math.sin(_), c = -Math.cos(_), u = H([
c * r.normal[0] + a * r.binormal[0],
c * r.normal[1] + a * r.binormal[1],
c * r.normal[2] + a * r.binormal[2]
]);
if (f.push(
r.position[0] + r.radius * u[0],
r.position[1] + r.radius * u[1],
r.position[2] + r.radius * u[2]
), h++, v && v.push(...m), p) {
const d = [(c + 1) / 2, (a + 1) / 2];
s && (d[0] = 1 - d[0]), p.push(...d);
}
}
for (let i = 1; i <= t; i++) {
let a, c;
s ? (a = i + 0, c = i + 0 + 1) : (a = i + 0 + 1, c = i + 0), o.push(a + e, c + e, 0 + e), l += 3;
}
return { vertices: f, indices: o, normals: v, uvs: p, vertexCount: h, indexCount: l };
}
function J(r, t, s, n = !0, e, h = 0) {
let l = 0, f = 0;
const o = [], v = [], p = e.computeNormals ? [] : null, m = e.computeUvs ? [] : null, i = n ? [
-r.tangent[0],
-r.tangent[1],
-r.tangent[2]
] : r.tangent, _ = t.radius / r.radius;
for (let a = 0; a <= s; a++) {
const c = a / s * V * 2, u = Math.sin(c), d = -Math.cos(c), g = H([
d * r.normal[0] + u * r.binormal[0],
d * r.normal[1] + u * r.binormal[1],
d * r.normal[2] + u * r.binormal[2]
]);
if (o.push(
r.position[0] + r.radius * g[0],
r.position[1] + r.radius * g[1],
r.position[2] + r.radius * g[2]
), l++, o.push(
t.position[0] + t.radius * g[0],
t.position[1] + t.radius * g[1],
t.position[2] + t.radius * g[2]
), l++, p && p.push(...i, ...i), m) {
const C = [(d + 1) / 2, (u + 1) / 2], w = [
(d * _ + 1) / 2,
(u * _ + 1) / 2
];
n && (C[0] = 1 - C[0], w[0] = 1 - w[0]), m.push(...C, ...w);
}
}
for (let a = 0; a < s; a++) {
const c = a * 2, u = c + 1, d = c + 2, g = c + 3;
n ? v.push(
c + h,
d + h,
u + h,
u + h,
d + h,
g + h
) : v.push(
d + h,
c + h,
u + h,
u + h,
g + h,
d + h
), f += 6;
}
return { vertices: o, indices: v, normals: p, uvs: m, vertexCount: l, indexCount: f };
}
function K(r, t, s, n, e = 0) {
let h = 0, l = 0;
const f = [], o = [], v = n.computeNormals ? [] : null, p = n.computeUvs ? [] : null, m = (i) => {
for (let _ = 0; _ <= t; _++) {
const a = _ / t * V * 2, c = Math.sin(a), u = -Math.cos(a), d = H([
u * i.normal[0] + c * i.binormal[0],
u * i.normal[1] + c * i.binormal[1],
u * i.normal[2] + c * i.binormal[2]
]), g = [
i.position[0] + i.radius * d[0],
i.position[1] + i.radius * d[1],
i.position[2] + i.radius * d[2]
];
if (v) {
let C = nt(d);
if (i.theta) {
const w = H(et(i.tangent, d));
C = it(d, w, i.theta);
}
v.push(...C);
}
f.push(...g), h++;
}
};
for (let i = 0; i < r.length; i++)
m(r[i]);
if (s && m(r[0]), p)
for (let i = 0; i < r.length; i++)
for (let _ = 0; _ <= t; _++)
p.push(_ / t, i / (r.length - 1));
for (let i = 1; i < r.length; i++)
for (let _ = 1; _ <= t; _++) {
const a = (t + 1) * (i - 1) + (_ - 1), c = (t + 1) * i + (_ - 1), u = (t + 1) * i + _, d = (t + 1) * (i - 1) + _;
o.push(a + e, c + e, d + e), o.push(c + e, u + e, d + e), l += 6;
}
return { vertices: f, indices: o, normals: v, uvs: p, vertexCount: h, indexCount: l };
}
function dt(r, t = {}) {
var G, B;
const s = x(t.from || 0, 0, 1), n = x(t.to || 1);
if (n < s)
throw Error('Value of "from" must be less than the value of "to"!');
const e = new O(), h = t.radius || 1, l = ((G = t.radiusModifier) == null ? void 0 : G.steps) || [], f = t.radialSegments || 8, o = t.startCap || !1, v = t.endCap || !1, p = r.closed;
l.sort((b, M) => b[0] - M[0]);
const m = t.segmentsPerMeter || 0.1, i = ((B = t.radiusModifier) == null ? void 0 : B.type) || "none", _ = x(
t.simplificationThreshold || 0,
0,
1
), a = ot(
r,
i,
s,
n,
h,
l,
m,
_
), c = K(
a,
f,
p,
t
);
let u = null, d = null, g = null, C = c.vertexCount, w = 0;
t.addGroups && (e.addGroup(w, c.indexCount, e.groups.length), w += c.indexCount);
let P = null;
(t.innerRadius || t.thickness) && (P = a.map((b) => ({
...b,
radius: t.innerRadius || b.radius - t.thickness,
theta: b.theta - V
})), u = K(
P,
f,
p,
t,
C
), C += u.vertexCount, t.addGroups && (e.addGroup(
w,
u.indexCount,
e.groups.length
), w += u.indexCount)), o && (!p || s > 0 || n < 1) && (P ? d = J(
a[0],
P[0],
f,
!0,
t,
C
) : d = Y(
a[0],
f,
!0,
t,
C
), C += d.vertexCount, t.addGroups && (e.addGroup(w, d.indexCount, e.groups.length), w += d.indexCount)), v && (!p || s > 0 || n < 1) && (P ? g = J(
a[a.length - 1],
P[P.length - 1],
f,
!1,
t,
C
) : g = Y(
a[a.length - 1],
f,
!1,
t,
C
), C += g.vertexCount, t.addGroups && (e.addGroup(w, g.indexCount, e.groups.length), w += g.indexCount));
let z = c.vertices, T = c.indices;
if (u && (z = z.concat(u.vertices), T = T.concat(u.indices.reverse())), d && (z = z.concat(d.vertices), T = T.concat(d.indices)), g && (z = z.concat(g.vertices), T = T.concat(g.indices)), e.setAttribute(
"position",
new j(Float32Array.from(z), 3)
), t.computeNormals) {
let b = c.normals;
u && (b = b.concat(u.normals)), d && (b = b.concat(d.normals)), g && (b = b.concat(g.normals)), e.setAttribute(
"normal",
new j(Float32Array.from(b), 3)
);
}
if (t.computeLengths || t.computeCurveNormals || t.computeCurveTangents || t.computeCurveBinormals || t.computeRelativeLengths) {
const b = t.computeLengths ? [] : null, M = t.computeRelativeLengths ? [] : null, I = t.computeCurveNormals ? [] : null, L = t.computeCurveTangents ? [] : null, A = t.computeCurveBinormals ? [] : null, F = r.length;
for (let q = 0; q < a.length; q++)
for (let N = 0; N <= f; N++)
b && b.push(a[q].curvePosition * F), M && M.push((a[q].curvePosition - s) * F), I && I.push(...a[q].normal), L && L.push(...a[q].tangent), A && A.push(...a[q].binormal);
if (u && P)
for (let q = 0; q < P.length; q++)
for (let N = 0; N <= f; N++)
b && b.push(P[q].curvePosition * F), M && M.push(
(a[q].curvePosition - s) * F
), I && I.push(...P[q].normal), L && L.push(...P[q].tangent), A && A.push(...P[q].binormal);
if (d)
for (let q = 0; q < d.vertexCount; q++)
b && b.push(s * F), M && M.push(0), I && I.push(...a[0].normal), L && L.push(...a[0].tangent), A && A.push(...a[0].binormal);
if (g)
for (let q = 0; q < g.vertexCount; q++)
b && b.push(n * F), M && M.push(F), I && I.push(...a[a.length - 1].normal), L && L.push(...a[a.length - 1].tangent), A && A.push(...a[a.length - 1].binormal);
b && e.setAttribute(
"curveLength",
new j(Float32Array.from(b), 1)
), M && e.setAttribute(
"curveRelativeLength",
new j(Float32Array.from(M), 1)
), I && e.setAttribute(
"curveNormal",
new j(Float32Array.from(I), 3)
), L && e.setAttribute(
"curveTangent",
new j(Float32Array.from(L), 3)
), A && e.setAttribute(
"curveBinormal",
new j(Float32Array.from(A), 3)
);
}
if (t.computeUvs) {
let b = c.uvs;
u && (b = b.concat(u.uvs)), d && (b = b.concat(d.uvs)), g && (b = b.concat(g.uvs)), e.setAttribute("uv", new j(Float32Array.from(b), 2));
}
return e.setIndex(new j(Uint32Array.from(T), 1)), e;
}
const Q = -1e3;
class at {
constructor(t, s, n = -1) {
R(this, "data");
R(this, "width");
R(this, "height");
R(this, "coords", []);
// vertex coordinates (x, y)
R(this, "triangles", []);
// mesh triangle indices
R(this, "nullValue");
R(this, "_queue", []);
R(this, "_queueIndices", []);
R(this, "_errors", []);
R(this, "_halfedges", []);
R(this, "_candidates", []);
R(this, "_invalidPoints");
R(this, "_rms", []);
R(this, "_pending", []);
R(this, "_pendingLen", 0);
R(this, "_rmsSum", 0);
this.data = t, this.width = s, this.height = this.data.length / s, this.nullValue = n, this._invalidPoints = /* @__PURE__ */ new Set();
const e = this.width - 1, h = this.height - 1, l = this._addPoint(0, 0), f = this._addPoint(e, 0), o = this._addPoint(0, h), v = this._addPoint(e, h), p = this._addTriangle(v, l, o, -1, -1, -1);
this._addTriangle(l, v, f, p, -1, -1), this._flush();
}
// refine the mesh until its maximum error gets below the given one
run(t = 1) {
for (; this.getMaxError() > t; )
this.refine();
}
// Removes triangles where one or more vertices contains a null value (nullValue)
removeInvalidTriangles() {
const t = [];
for (let s = 0; s < this.triangles.length; s += 3) {
const n = this.triangles[s], e = this.triangles[s + 1], h = this.triangles[s + 2];
!this._invalidPoints.has(n) && !this._invalidPoints.has(e) && !this._invalidPoints.has(h) && t.push(
this.triangles[s],
this.triangles[s + 1],
this.triangles[s + 2]
);
}
this.triangles = t;
}
// refine the mesh with a single point
refine() {
this._step(), this._flush();
}
// max error of the current mesh
getMaxError() {
return this._errors[0];
}
// root-mean-square deviation of the current mesh
getRMSD() {
return this._rmsSum > 0 ? Math.sqrt(this._rmsSum / (this.width * this.height)) : 0;
}
// height value at a given position
heightAt(t, s) {
const n = this.data[this.width * s + t];
return n === this.nullValue ? Q : n;
}
// rasterize a triangle, find its max error, and queue it for processing
_findCandidate(t, s, n, e, h, l, f) {
const o = Math.min(t, n, h), v = Math.min(s, e, l), p = Math.max(t, n, h), m = Math.max(s, e, l);
let i = U(n, e, h, l, o, v), _ = U(h, l, t, s, o, v), a = U(t, s, n, e, o, v);
const c = e - s, u = t - n, d = l - e, g = n - h, C = s - l, w = h - t, P = U(t, s, n, e, h, l), z = this.heightAt(t, s) / P, T = this.heightAt(n, e) / P, G = this.heightAt(h, l) / P;
let B = 0, b = 0, M = 0, I = 0;
for (let L = v; L <= m; L++) {
let A = 0;
i < 0 && d !== 0 && (A = Math.max(A, Math.floor(-i / d))), _ < 0 && C !== 0 && (A = Math.max(A, Math.floor(-_ / C))), a < 0 && c !== 0 && (A = Math.max(A, Math.floor(-a / c)));
let F = i + d * A, q = _ + C * A, N = a + c * A, X = !1;
for (let k = o + A; k <= p; k++) {
if (F >= 0 && q >= 0 && N >= 0) {
X = !0;
const W = z * F + T * q + G * N, Z = this.heightAt(k, L), D = Math.abs(W - Z);
I += D * D, D > B && (B = D, b = k, M = L);
} else if (X)
break;
F += d, q += C, N += c;
}
i += g, _ += w, a += u;
}
(b === t && M === s || b === n && M === e || b === h && M === l) && (B = 0), this._candidates[2 * f] = b, this._candidates[2 * f + 1] = M, this._rms[f] = I, this._queuePush(f, B, I);
}
// process the next triangle in the queue, splitting it with a new point
_step() {
const t = this._queuePop(), s = t * 3 + 0, n = t * 3 + 1, e = t * 3 + 2, h = this.triangles[s], l = this.triangles[n], f = this.triangles[e], o = this.coords[2 * h], v = this.coords[2 * h + 1], p = this.coords[2 * l], m = this.coords[2 * l + 1], i = this.coords[2 * f], _ = this.coords[2 * f + 1], a = this._candidates[2 * t], c = this._candidates[2 * t + 1], u = this._addPoint(a, c);
if (U(o, v, p, m, a, c) === 0)
this._handleCollinear(u, s);
else if (U(p, m, i, _, a, c) === 0)
this._handleCollinear(u, n);
else if (U(i, _, o, v, a, c) === 0)
this._handleCollinear(u, e);
else {
const d = this._halfedges[s], g = this._halfedges[n], C = this._halfedges[e], w = this._addTriangle(h, l, u, d, -1, -1, s), P = this._addTriangle(l, f, u, g, -1, w + 1), z = this._addTriangle(f, h, u, C, w + 2, P + 1);
this._legalize(w), this._legalize(P), this._legalize(z);
}
}
_addPoint(t, s) {
const n = this.coords.length >> 1;
return this.coords.push(t, s), this.heightAt(t, s) === Q && this._invalidPoints.add(n), n;
}
_addTriangle(t, s, n, e, h, l, f = this.triangles.length) {
const o = f / 3;
return this.triangles[f + 0] = t, this.triangles[f + 1] = s, this.triangles[f + 2] = n, this._halfedges[f + 0] = e, this._halfedges[f + 1] = h, this._halfedges[f + 2] = l, e >= 0 && (this._halfedges[e] = f + 0), h >= 0 && (this._halfedges[h] = f + 1), l >= 0 && (this._halfedges[l] = f + 2), this._candidates[2 * o + 0] = 0, this._candidates[2 * o + 1] = 0, this._queueIndices[o] = -1, this._rms[o] = 0, this._pending[this._pendingLen++] = o, f;
}
_flush() {
const t = this.coords;
for (let s = 0; s < this._pendingLen; s++) {
const n = this._pending[s], e = 2 * this.triangles[n * 3 + 0], h = 2 * this.triangles[n * 3 + 1], l = 2 * this.triangles[n * 3 + 2];
this._findCandidate(
t[e],
t[e + 1],
t[h],
t[h + 1],
t[l],
t[l + 1],
n
);
}
this._pendingLen = 0;
}
_legalize(t) {
const s = this._halfedges[t];
if (s < 0)
return;
const n = t - t % 3, e = s - s % 3, h = n + (t + 1) % 3, l = n + (t + 2) % 3, f = e + (s + 2) % 3, o = e + (s + 1) % 3, v = this.triangles[l], p = this.triangles[t], m = this.triangles[h], i = this.triangles[f], _ = this.coords;
if (!rt(
_[2 * v],
_[2 * v + 1],
_[2 * p],
_[2 * p + 1],
_[2 * m],
_[2 * m + 1],
_[2 * i],
_[2 * i + 1]
))
return;
const a = this._halfedges[h], c = this._halfedges[l], u = this._halfedges[f], d = this._halfedges[o];
this._queueRemove(n / 3), this._queueRemove(e / 3);
const g = this._addTriangle(v, i, m, -1, u, a, n), C = this._addTriangle(i, v, p, g, c, d, e);
this._legalize(g + 1), this._legalize(C + 2);
}
_handleCollinear(t, s) {
const n = s - s % 3, e = n + (s + 1) % 3, h = n + (s + 2) % 3, l = this.triangles[h], f = this.triangles[s], o = this.triangles[e], v = this._halfedges[e], p = this._halfedges[h], m = this._halfedges[s];
if (m < 0) {
const z = this._addTriangle(t, l, f, -1, p, -1, n), T = this._addTriangle(l, t, o, z, -1, v);
this._legalize(z + 1), this._legalize(T + 2);
return;
}
const i = m - m % 3, _ = i + (m + 2) % 3, a = i + (m + 1) % 3, c = this.triangles[_], u = this._halfedges[_], d = this._halfedges[a];
this._queueRemove(i / 3);
const g = this._addTriangle(l, f, t, p, -1, -1, n), C = this._addTriangle(f, c, t, d, -1, g + 1, i), w = this._addTriangle(c, o, t, u, -1, C + 1), P = this._addTriangle(o, l, t, v, g + 2, w + 1);
this._legalize(g), this._legalize(C), this._legalize(w), this._legalize(P);
}
// priority queue methods
_queuePush(t, s, n) {
const e = this._queue.length;
this._queueIndices[t] = e, this._queue.push(t), this._errors.push(s), this._rmsSum += n, this._queueUp(e);
}
_queuePop() {
const t = this._queue.length - 1;
return this._queueSwap(0, t), this._queueDown(0, t), this._queuePopBack();
}
_queuePopBack() {
const t = this._queue.pop();
return this._errors.pop(), this._rmsSum -= this._rms[t], this._queueIndices[t] = -1, t;
}
_queueRemove(t) {
const s = this._queueIndices[t];
if (s < 0) {
const e = this._pending.indexOf(t);
if (e !== -1)
this._pending[e] = this._pending[--this._pendingLen];
else
throw new Error("Broken triangulation (something went wrong).");
return;
}
const n = this._queue.length - 1;
n !== s && (this._queueSwap(s, n), this._queueDown(s, n) || this._queueUp(s)), this._queuePopBack();
}
_queueLess(t, s) {
return this._errors[t] > this._errors[s];
}
_queueSwap(t, s) {
const n = this._queue[t], e = this._queue[s];
this._queue[t] = e, this._queue[s] = n, this._queueIndices[n] = s, this._queueIndices[e] = t;
const h = this._errors[t];
this._errors[t] = this._errors[s], this._errors[s] = h;
}
_queueUp(t) {
let s = t;
for (; ; ) {
const n = s - 1 >> 1;
if (n === s || !this._queueLess(s, n))
break;
this._queueSwap(n, s), s = n;
}
}
_queueDown(t, s) {
let n = t;
for (; ; ) {
const e = 2 * n + 1;
if (e >= s || e < 0)
break;
const h = e + 1;
let l = e;
if (h < s && this._queueLess(h, e) && (l = h), !this._queueLess(l, n))
break;
this._queueSwap(n, l), n = l;
}
return n > t;
}
}
function U(r, t, s, n, e, h) {
return (s - e) * (t - h) - (n - h) * (r - e);
}
function rt(r, t, s, n, e, h, l, f) {
const o = r - l, v = t - f, p = s - l, m = n - f, i = e - l, _ = h - f, a = o * o + v * v, c = p * p + m * m, u = i * i + _ * _;
return o * (m * u - c * _) - v * (p * u - c * i) + a * (p * _ - m * i) < 0;
}
function ft(r, t, s = 1, n = 1, e = -1, h = 5) {
const l = t, f = r.length / l;
console.time("delatin");
const o = new at(r, l, e);
o.run(h), o.removeInvalidTriangles(), console.timeEnd("delatin");
const v = new Float32Array(o.coords.length * 1.5), p = new Float32Array(o.coords.length);
for (let i = 0, _ = 0; i < o.coords.length; i += 2)
p[i] = o.coords[i] / (l - 1), p[i + 1] = 1 - o.coords[i + 1] / (f - 1), _ = i * 1.5, v[_] = o.coords[i] * s, v[_ + 1] = o.heightAt(o.coords[i], o.coords[i + 1]), v[_ + 2] = o.coords[i + 1] * n;
const m = new Uint32Array(o.triangles);
return console.log(o.triangles.length / 3), { positions: v, uvs: p, indices: m };
}
function _t(r) {
return r / 3.28084;
}
function gt(r) {
return r * (Math.PI / 180);
}
function vt(r) {
let t = 0, s, n;
if (!r || r.length === 0) return t;
for (s = 0; s < r.length; s++)
n = r.charCodeAt(s), t = (t << 5) - t + n, t |= 0;
return t;
}
function pt(r) {
return r.replace(/(^|\s)\S/g, (t) => t.toUpperCase());
}
export {
at as D,
vt as a,
pt as b,
dt as c,
gt as d,
_t as f,
ft as t
};