videx-3d
Version:
React 3D component library designed for sub surface visualizations in the browser
476 lines (475 loc) • 20.5 kB
JavaScript
var $ = Object.defineProperty;
var y = (o, t, s) => t in o ? $(o, t, { enumerable: !0, configurable: !0, writable: !0, value: s }) : o[t] = s;
var L = (o, t, s) => y(o, 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, a1 as tt, y as st, f as H, a3 as nt, a0 as et, a5 as it } from "./chunk-DBaq_0xI.js";
function ot(o, t) {
let s = t.findIndex((c) => c[0] > o);
s === -1 && (s = t.length - 1);
let n = s - 1;
n < 0 && (n = 0);
const e = t[n], a = t[s];
return [e, a];
}
function E(o, t, s) {
if (t[0] === s[0]) return s[1];
const n = s[0] - t[0], e = x((o - t[0]) / n, 0, 1);
return S(t[1], s[1], e);
}
function at(o, t, s, n, e, a, c, d) {
const r = [];
if (t === "none" || a.length === 0)
r.push([s, e], [n, e]);
else {
let i = [0, e], u = [1, e];
const h = a.findIndex((l) => l[0] > s);
h === -1 ? i = a[a.length - 1] : (h > 0 && (i = a[h - 1]), u = a[h]);
const _ = t === "linear" ? E(s, i, u) : i[1];
r.push([s, _]);
for (let l = h; l >= 0 && l < a.length; l++) {
const f = a[l];
if (f[0] < n)
r.push(f);
else {
t === "linear" ? r.push([n, E(n, r[r.length - 1], f)]) : r.push([n, r[r.length - 1][1]]);
break;
}
}
r[r.length - 1][0] < n && (t === "linear" ? r.push([n, E(n, r[r.length - 1], [1, e])]) : r.push([n, r[r.length - 1][1]]));
}
const v = o.length, g = [];
for (let i = 0; i < r.length - 1; i++) {
const u = i + 1, [h, _] = r[i], [l, f] = r[u], m = l - h, q = m * v, w = f - _, C = Math.atan2(w, q), T = Math.floor(c * q), F = m / T;
g.push([h, _, t === "linear" ? C : 0]);
let B = d ? o.getTangentAt(h) : null;
for (let I = 1; I < T; I++) {
const p = h + I * F, M = d ? o.getTangentAt(p) : null;
if (!d || Math.abs(tt(B, M)) < 1 - d) {
const [R, z] = ot(p, r), P = t === "linear" ? E(p, R, z) : R[1];
g.push([p, P, t === "linear" ? C : 0]), B = M;
}
}
if (u < r.length) {
if (t === "linear")
g.push([l, f, t === "linear" ? C : 0]);
else if (t === "stepped") {
const I = C < 0 ? -V / 2 : V / 2;
g.push([l, _, 0], [l, _, I], [l, f, I]);
}
}
u === r.length - 1 && t === "none" && g.push([l, f, C]);
}
const b = st(o, g.map((i) => i[0]));
return g.map((i, u) => ({
radius: i[1],
theta: i[2],
...b[u]
}));
}
function Y(o, t, s = !0, n, e = 0) {
let a = 0, c = 0;
const d = [], r = [], v = n.computeNormals ? [] : null, g = n.computeUvs ? [] : null, b = s ? [-o.tangent[0], -o.tangent[1], -o.tangent[2]] : o.tangent;
d.push(...o.position), a++, v && v.push(...b), g && g.push(0.5, 0.5);
for (let i = 0; i <= t; i++) {
const u = i / t * V * 2, h = Math.sin(u), _ = -Math.cos(u), l = H([
_ * o.normal[0] + h * o.binormal[0],
_ * o.normal[1] + h * o.binormal[1],
_ * o.normal[2] + h * o.binormal[2]
]);
if (d.push(
o.position[0] + o.radius * l[0],
o.position[1] + o.radius * l[1],
o.position[2] + o.radius * l[2]
), a++, v && v.push(...b), g) {
const f = [(_ + 1) / 2, (h + 1) / 2];
s && (f[0] = 1 - f[0]), g.push(...f);
}
}
for (let i = 1; i <= t; i++) {
let h, _;
s ? (h = i + 0, _ = i + 0 + 1) : (h = i + 0 + 1, _ = i + 0), r.push(h + e, _ + e, 0 + e), c += 3;
}
return { vertices: d, indices: r, normals: v, uvs: g, vertexCount: a, indexCount: c };
}
function J(o, t, s, n = !0, e, a = 0) {
let c = 0, d = 0;
const r = [], v = [], g = e.computeNormals ? [] : null, b = e.computeUvs ? [] : null, i = n ? [-o.tangent[0], -o.tangent[1], -o.tangent[2]] : o.tangent, u = t.radius / o.radius;
for (let h = 0; h <= s; h++) {
const _ = h / s * V * 2, l = Math.sin(_), f = -Math.cos(_), m = H([
f * o.normal[0] + l * o.binormal[0],
f * o.normal[1] + l * o.binormal[1],
f * o.normal[2] + l * o.binormal[2]
]);
if (r.push(
o.position[0] + o.radius * m[0],
o.position[1] + o.radius * m[1],
o.position[2] + o.radius * m[2]
), c++, r.push(
t.position[0] + t.radius * m[0],
t.position[1] + t.radius * m[1],
t.position[2] + t.radius * m[2]
), c++, g && g.push(...i, ...i), b) {
const q = [(f + 1) / 2, (l + 1) / 2], w = [(f * u + 1) / 2, (l * u + 1) / 2];
n && (q[0] = 1 - q[0], w[0] = 1 - w[0]), b.push(...q, ...w);
}
}
for (let h = 0; h < s; h++) {
const _ = h * 2, l = _ + 1, f = _ + 2, m = _ + 3;
n ? v.push(
_ + a,
f + a,
l + a,
l + a,
f + a,
m + a
) : v.push(
f + a,
_ + a,
l + a,
l + a,
m + a,
f + a
), d += 6;
}
return { vertices: r, indices: v, normals: g, uvs: b, vertexCount: c, indexCount: d };
}
function K(o, t, s, n, e = 0) {
let a = 0, c = 0;
const d = [], r = [], v = n.computeNormals ? [] : null, g = n.computeUvs ? [] : null, b = (i) => {
for (let u = 0; u <= t; u++) {
const h = u / t * V * 2, _ = Math.sin(h), l = -Math.cos(h), f = H([
l * i.normal[0] + _ * i.binormal[0],
l * i.normal[1] + _ * i.binormal[1],
l * i.normal[2] + _ * i.binormal[2]
]), m = [
i.position[0] + i.radius * f[0],
i.position[1] + i.radius * f[1],
i.position[2] + i.radius * f[2]
];
if (v) {
let q = nt(f);
if (i.theta) {
const w = H(et(i.tangent, f));
q = it(f, w, i.theta);
}
v.push(...q);
}
d.push(...m), a++;
}
};
for (let i = 0; i < o.length; i++)
b(o[i]);
if (s && b(o[0]), g)
for (let i = 0; i < o.length; i++)
for (let u = 0; u <= t; u++)
g.push(
u / t,
i / (o.length - 1)
);
for (let i = 1; i < o.length; i++)
for (let u = 1; u <= t; u++) {
const h = (t + 1) * (i - 1) + (u - 1), _ = (t + 1) * i + (u - 1), l = (t + 1) * i + u, f = (t + 1) * (i - 1) + u;
r.push(h + e, _ + e, f + e), r.push(_ + e, l + e, f + e), c += 6;
}
return { vertices: d, indices: r, normals: v, uvs: g, vertexCount: a, indexCount: c };
}
function ft(o, t = {}) {
var B, I;
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(), a = t.radius || 1, c = ((B = t.radiusModifier) == null ? void 0 : B.steps) || [], d = t.radialSegments || 8, r = t.startCap || !1, v = t.endCap || !1, g = o.closed;
c.sort((p, M) => p[0] - M[0]);
const b = t.segmentsPerMeter || 0.1, i = ((I = t.radiusModifier) == null ? void 0 : I.type) || "none", u = x(t.simplificationThreshold || 0, 0, 1), h = at(o, i, s, n, a, c, b, u), _ = K(h, d, g, t);
let l = null, f = null, m = null, q = _.vertexCount, w = 0;
t.addGroups && (e.addGroup(w, _.indexCount, e.groups.length), w += _.indexCount);
let C = null;
(t.innerRadius || t.thickness) && (C = h.map((p) => ({
...p,
radius: t.innerRadius || p.radius - t.thickness,
theta: p.theta - V
})), l = K(C, d, g, t, q), q += l.vertexCount, t.addGroups && (e.addGroup(w, l.indexCount, e.groups.length), w += l.indexCount)), r && (!g || s > 0 || n < 1) && (C ? f = J(h[0], C[0], d, !0, t, q) : f = Y(h[0], d, !0, t, q), q += f.vertexCount, t.addGroups && (e.addGroup(w, f.indexCount, e.groups.length), w += f.indexCount)), v && (!g || s > 0 || n < 1) && (C ? m = J(h[h.length - 1], C[C.length - 1], d, !1, t, q) : m = Y(h[h.length - 1], d, !1, t, q), q += m.vertexCount, t.addGroups && (e.addGroup(w, m.indexCount, e.groups.length), w += m.indexCount));
let T = _.vertices, F = _.indices;
if (l && (T = T.concat(l.vertices), F = F.concat(l.indices.reverse())), f && (T = T.concat(f.vertices), F = F.concat(f.indices)), m && (T = T.concat(m.vertices), F = F.concat(m.indices)), e.setAttribute("position", new j(Float32Array.from(T), 3)), t.computeNormals) {
let p = _.normals;
l && (p = p.concat(l.normals)), f && (p = p.concat(f.normals)), m && (p = p.concat(m.normals)), e.setAttribute("normal", new j(Float32Array.from(p), 3));
}
if (t.computeLengths || t.computeCurveNormals || t.computeCurveTangents || t.computeCurveBinormals || t.computeRelativeLengths) {
const p = t.computeLengths ? [] : null, M = t.computeRelativeLengths ? [] : null, R = t.computeCurveNormals ? [] : null, z = t.computeCurveTangents ? [] : null, P = t.computeCurveBinormals ? [] : null, G = o.length;
for (let A = 0; A < h.length; A++)
for (let N = 0; N <= d; N++)
p && p.push(h[A].curvePosition * G), M && M.push((h[A].curvePosition - s) * G), R && R.push(...h[A].normal), z && z.push(...h[A].tangent), P && P.push(...h[A].binormal);
if (l && C)
for (let A = 0; A < C.length; A++)
for (let N = 0; N <= d; N++)
p && p.push(C[A].curvePosition * G), M && M.push((h[A].curvePosition - s) * G), R && R.push(...C[A].normal), z && z.push(...C[A].tangent), P && P.push(...C[A].binormal);
if (f)
for (let A = 0; A < f.vertexCount; A++)
p && p.push(s * G), M && M.push(0), R && R.push(...h[0].normal), z && z.push(...h[0].tangent), P && P.push(...h[0].binormal);
if (m)
for (let A = 0; A < m.vertexCount; A++)
p && p.push(n * G), M && M.push(G), R && R.push(...h[h.length - 1].normal), z && z.push(...h[h.length - 1].tangent), P && P.push(...h[h.length - 1].binormal);
p && e.setAttribute("curveLength", new j(Float32Array.from(p), 1)), M && e.setAttribute("curveRelativeLength", new j(Float32Array.from(M), 1)), R && e.setAttribute("curveNormal", new j(Float32Array.from(R), 3)), z && e.setAttribute("curveTangent", new j(Float32Array.from(z), 3)), P && e.setAttribute("curveBinormal", new j(Float32Array.from(P), 3));
}
if (t.computeUvs) {
let p = _.uvs;
l && (p = p.concat(l.uvs)), f && (p = p.concat(f.uvs)), m && (p = p.concat(m.uvs)), e.setAttribute("uv", new j(Float32Array.from(p), 2));
}
return e.setIndex(new j(Uint32Array.from(F), 1)), e;
}
const Q = -1e3;
class rt {
constructor(t, s, n = -1) {
L(this, "data");
L(this, "width");
L(this, "height");
L(this, "coords", []);
// vertex coordinates (x, y)
L(this, "triangles", []);
// mesh triangle indices
L(this, "nullValue");
L(this, "_queue", []);
L(this, "_queueIndices", []);
L(this, "_errors", []);
L(this, "_halfedges", []);
L(this, "_candidates", []);
L(this, "_invalidPoints");
L(this, "_rms", []);
L(this, "_pending", []);
L(this, "_pendingLen", 0);
L(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, a = this.height - 1, c = this._addPoint(0, 0), d = this._addPoint(e, 0), r = this._addPoint(0, a), v = this._addPoint(e, a), g = this._addTriangle(v, c, r, -1, -1, -1);
this._addTriangle(c, v, d, g, -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], a = this.triangles[s + 2];
!this._invalidPoints.has(n) && !this._invalidPoints.has(e) && !this._invalidPoints.has(a) && 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, a, c, d) {
const r = Math.min(t, n, a), v = Math.min(s, e, c), g = Math.max(t, n, a), b = Math.max(s, e, c);
let i = U(n, e, a, c, r, v), u = U(a, c, t, s, r, v), h = U(t, s, n, e, r, v);
const _ = e - s, l = t - n, f = c - e, m = n - a, q = s - c, w = a - t, C = U(t, s, n, e, a, c), T = this.heightAt(t, s) / C, F = this.heightAt(n, e) / C, B = this.heightAt(a, c) / C;
let I = 0, p = 0, M = 0, R = 0;
for (let z = v; z <= b; z++) {
let P = 0;
i < 0 && f !== 0 && (P = Math.max(P, Math.floor(-i / f))), u < 0 && q !== 0 && (P = Math.max(P, Math.floor(-u / q))), h < 0 && _ !== 0 && (P = Math.max(P, Math.floor(-h / _)));
let G = i + f * P, A = u + q * P, N = h + _ * P, X = !1;
for (let k = r + P; k <= g; k++) {
if (G >= 0 && A >= 0 && N >= 0) {
X = !0;
const W = T * G + F * A + B * N, Z = this.heightAt(k, z), D = Math.abs(W - Z);
R += D * D, D > I && (I = D, p = k, M = z);
} else if (X)
break;
G += f, A += q, N += _;
}
i += m, u += w, h += l;
}
(p === t && M === s || p === n && M === e || p === a && M === c) && (I = 0), this._candidates[2 * d] = p, this._candidates[2 * d + 1] = M, this._rms[d] = R, this._queuePush(d, I, R);
}
// 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, a = this.triangles[s], c = this.triangles[n], d = this.triangles[e], r = this.coords[2 * a], v = this.coords[2 * a + 1], g = this.coords[2 * c], b = this.coords[2 * c + 1], i = this.coords[2 * d], u = this.coords[2 * d + 1], h = this._candidates[2 * t], _ = this._candidates[2 * t + 1], l = this._addPoint(h, _);
if (U(r, v, g, b, h, _) === 0)
this._handleCollinear(l, s);
else if (U(g, b, i, u, h, _) === 0)
this._handleCollinear(l, n);
else if (U(i, u, r, v, h, _) === 0)
this._handleCollinear(l, e);
else {
const f = this._halfedges[s], m = this._halfedges[n], q = this._halfedges[e], w = this._addTriangle(a, c, l, f, -1, -1, s), C = this._addTriangle(c, d, l, m, -1, w + 1), T = this._addTriangle(d, a, l, q, w + 2, C + 1);
this._legalize(w), this._legalize(C), this._legalize(T);
}
}
_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, a, c, d = this.triangles.length) {
const r = d / 3;
return this.triangles[d + 0] = t, this.triangles[d + 1] = s, this.triangles[d + 2] = n, this._halfedges[d + 0] = e, this._halfedges[d + 1] = a, this._halfedges[d + 2] = c, e >= 0 && (this._halfedges[e] = d + 0), a >= 0 && (this._halfedges[a] = d + 1), c >= 0 && (this._halfedges[c] = d + 2), this._candidates[2 * r + 0] = 0, this._candidates[2 * r + 1] = 0, this._queueIndices[r] = -1, this._rms[r] = 0, this._pending[this._pendingLen++] = r, d;
}
_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], a = 2 * this.triangles[n * 3 + 1], c = 2 * this.triangles[n * 3 + 2];
this._findCandidate(
t[e],
t[e + 1],
t[a],
t[a + 1],
t[c],
t[c + 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, a = n + (t + 1) % 3, c = n + (t + 2) % 3, d = e + (s + 2) % 3, r = e + (s + 1) % 3, v = this.triangles[c], g = this.triangles[t], b = this.triangles[a], i = this.triangles[d], u = this.coords;
if (!ht(
u[2 * v],
u[2 * v + 1],
u[2 * g],
u[2 * g + 1],
u[2 * b],
u[2 * b + 1],
u[2 * i],
u[2 * i + 1]
))
return;
const h = this._halfedges[a], _ = this._halfedges[c], l = this._halfedges[d], f = this._halfedges[r];
this._queueRemove(n / 3), this._queueRemove(e / 3);
const m = this._addTriangle(v, i, b, -1, l, h, n), q = this._addTriangle(i, v, g, m, _, f, e);
this._legalize(m + 1), this._legalize(q + 2);
}
_handleCollinear(t, s) {
const n = s - s % 3, e = n + (s + 1) % 3, a = n + (s + 2) % 3, c = this.triangles[a], d = this.triangles[s], r = this.triangles[e], v = this._halfedges[e], g = this._halfedges[a], b = this._halfedges[s];
if (b < 0) {
const T = this._addTriangle(t, c, d, -1, g, -1, n), F = this._addTriangle(c, t, r, T, -1, v);
this._legalize(T + 1), this._legalize(F + 2);
return;
}
const i = b - b % 3, u = i + (b + 2) % 3, h = i + (b + 1) % 3, _ = this.triangles[u], l = this._halfedges[u], f = this._halfedges[h];
this._queueRemove(i / 3);
const m = this._addTriangle(c, d, t, g, -1, -1, n), q = this._addTriangle(d, _, t, f, -1, m + 1, i), w = this._addTriangle(_, r, t, l, -1, q + 1), C = this._addTriangle(r, c, t, v, m + 2, w + 1);
this._legalize(m), this._legalize(q), this._legalize(w), this._legalize(C);
}
// 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 a = this._errors[t];
this._errors[t] = this._errors[s], this._errors[s] = a;
}
_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 a = e + 1;
let c = e;
if (a < s && this._queueLess(a, e) && (c = a), !this._queueLess(c, n))
break;
this._queueSwap(n, c), n = c;
}
return n > t;
}
}
function U(o, t, s, n, e, a) {
return (s - e) * (t - a) - (n - a) * (o - e);
}
function ht(o, t, s, n, e, a, c, d) {
const r = o - c, v = t - d, g = s - c, b = n - d, i = e - c, u = a - d, h = r * r + v * v, _ = g * g + b * b, l = i * i + u * u;
return r * (b * l - _ * u) - v * (g * l - _ * i) + h * (g * u - b * i) < 0;
}
function _t(o, t, s = 1, n = 1, e = -1, a = 5) {
const c = t, d = o.length / c;
console.time("delatin");
const r = new rt(o, c, e);
r.run(a), r.removeInvalidTriangles(), console.timeEnd("delatin");
const v = new Float32Array(r.coords.length * 1.5), g = new Float32Array(r.coords.length);
for (let i = 0, u = 0; i < r.coords.length; i += 2)
g[i] = r.coords[i] / (c - 1), g[i + 1] = 1 - r.coords[i + 1] / (d - 1), u = i * 1.5, v[u] = r.coords[i] * s, v[u + 1] = r.heightAt(r.coords[i], r.coords[i + 1]), v[u + 2] = r.coords[i + 1] * n;
const b = new Uint32Array(r.triangles);
return console.log(r.triangles.length / 3), { positions: v, uvs: g, indices: b };
}
function gt(o) {
return o / 3.28084;
}
function vt(o) {
return o * (Math.PI / 180);
}
function pt(o) {
let t = 0, s, n;
if (!o || o.length === 0) return t;
for (s = 0; s < o.length; s++)
n = o.charCodeAt(s), t = (t << 5) - t + n, t |= 0;
return t;
}
function mt(o) {
return o.replace(/(^|\s)\S/g, (t) => t.toUpperCase());
}
export {
rt as D,
pt as a,
mt as b,
ft as c,
vt as d,
gt as f,
_t as t
};