remesh-threejs 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/remesh-threejs.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("three");function t(e){return e}function n(e){return e}function i(e){return e}function s(e){return e}function o(e){return e}var r=(e=>(e.Manifold="manifold",e.OpenBook="open_book",e.SkeletonBranching="skeleton_branching",e.NonManifoldOther="non_manifold_other",e))(r||{}),a=(e=>(e.Manifold="manifold",e.NonManifold="non_manifold",e.Feature="feature",e.Boundary="boundary",e))(a||{});const l={iterations:5,preserveBoundary:!0,minEdgeLengthRatio:.4,maxEdgeLengthRatio:1.333,minTriangleQuality:.3,maxNormalDeviation:Math.PI/6,useAcceleration:!0,chunkSize:0,memoryBudget:0,verbose:!1};class c{constructor(e,t){this.id=e,this.position=t,this.halfedge=null,this.type=r.Manifold,this.isMarked=!1}degree(){if(!this.halfedge)return null;let e=0,t=this.halfedge;do{if(e++,!t.twin||!t.twin.next)break;if(t=t.twin.next,e>1e4)throw new Error(`Vertex ${this.id}: degree calculation exceeded maximum iterations`)}while(t!==this.halfedge);return e}forEachOutgoingHalfedge(e){if(!this.halfedge)return;let t=this.halfedge,n=0;do{if(e(t),!t.twin||!t.twin.next)break;if(t=t.twin.next,n++,n>1e4)throw new Error(`Vertex ${this.id}: halfedge iteration exceeded maximum iterations`)}while(t!==this.halfedge)}getOutgoingHalfedges(){const e=[];return this.forEachOutgoingHalfedge(t=>e.push(t)),e}forEachNeighbor(e){this.forEachOutgoingHalfedge(t=>{e(t.vertex)})}getNeighbors(){const e=[];return this.forEachNeighbor(t=>e.push(t)),e}isBoundary(){if(!this.halfedge)return!0;let e=!1;return this.forEachOutgoingHalfedge(t=>{t.face||(e=!0)}),e}canMoveFreely(){return this.type===r.Manifold}isSkeletonConstrained(){return this.type===r.OpenBook}isPositionFixed(){return this.type===r.SkeletonBranching||this.type===r.NonManifoldOther}isOnSkeleton(){return this.type!==r.Manifold}}class h{constructor(e,t,n){this.id=e,this.twin=null,this.next=null,this.prev=null,this.face=null,this.vertex=t,this.edge=n}getSourceVertex(){var e;return(null==(e=this.twin)?void 0:e.vertex)??null}getTargetVertex(){return this.vertex}isBoundary(){return null===this.face}getOppositeHalfedge(){var e;return(null==(e=this.next)?void 0:e.next)??null}getOppositeVertex(){var e;return(null==(e=this.next)?void 0:e.vertex)??null}getVector(){const e=this.getSourceVertex();return e?{x:this.vertex.position.x-e.position.x,y:this.vertex.position.y-e.position.y,z:this.vertex.position.z-e.position.z}:null}}class u{constructor(e,t,n){this.id=e,this.allHalfedges=[],this.type=a.Manifold,this.isInPath=!1,this.halfedge=t,this.length=n,this.allHalfedges.push(t)}addHalfedge(e){this.allHalfedges.push(e),this.updateType()}getHalfedgeCount(){return this.allHalfedges.length}updateType(){const e=this.getFaceCount();0===e||1===e?this.type=a.Boundary:2===e?this.type!==a.Feature&&(this.type=a.Manifold):this.type=a.NonManifold}getFaceCount(){let e=0;for(const t of this.allHalfedges)null!==t.face&&e++;return e}getVertices(){const e=this.halfedge.getSourceVertex(),t=this.halfedge.getTargetVertex();return e?[e,t]:[null,null]}getFaces(){const e=[];for(const t of this.allHalfedges)null!==t.face&&e.push(t.face);return e}getTwoFaces(){var e;return[this.halfedge.face,(null==(e=this.halfedge.twin)?void 0:e.face)??null]}isBoundary(){return this.type===a.Boundary}isNonManifold(){return this.type===a.NonManifold}isSkeletonEdge(){return this.type===a.NonManifold||this.type===a.Feature||this.type===a.Boundary}canFlip(){if(this.type!==a.Manifold)return!1;if(2!==this.getFaceCount())return!1;const[e,t]=this.getVertices();if(!e||!t)return!1;const n=e.degree(),i=t.degree();return!(null===n||null===i||n<=1||i<=1)}getOtherVertex(e){const[t,n]=this.getVertices();return t&&n?e.id===t.id?n:e.id===n.id?t:null:null}markAsFeature(){this.type===a.Manifold&&(this.type=a.Feature)}}class g{constructor(e,t){this.id=e,this.isMarked=!1,this.halfedge=t}getVertices(){const e=this.halfedge,t=e.next,n=null==t?void 0:t.next;return t&&n?[e.vertex,t.vertex,n.vertex]:null}getHalfedges(){const e=this.halfedge,t=e.next,n=null==t?void 0:t.next;return t&&n?[e,t,n]:null}forEachHalfedge(e){const t=this.getHalfedges();if(t)for(const n of t)e(n)}forEachVertex(e){const t=this.getVertices();if(t)for(const n of t)e(n)}getCentroid(){const e=this.getVertices();if(!e)return null;const[t,n,i]=e;return{x:(t.position.x+n.position.x+i.position.x)/3,y:(t.position.y+n.position.y+i.position.y)/3,z:(t.position.z+n.position.z+i.position.z)/3}}getNormal(){const e=this.getVertices();if(!e)return null;const[t,n,i]=e,s=n.position.x-t.position.x,o=n.position.y-t.position.y,r=n.position.z-t.position.z,a=i.position.x-t.position.x,l=i.position.y-t.position.y,c=i.position.z-t.position.z,h=o*c-r*l,u=r*a-s*c,g=s*l-o*a,d=Math.sqrt(h*h+u*u+g*g);return d<1e-10?{x:0,y:0,z:1}:{x:h/d,y:u/d,z:g/d}}getArea(){const e=this.getVertices();if(!e)return null;const[t,n,i]=e,s=n.position.x-t.position.x,o=n.position.y-t.position.y,r=n.position.z-t.position.z,a=i.position.x-t.position.x,l=i.position.y-t.position.y,c=i.position.z-t.position.z,h=o*c-r*l,u=r*a-s*c,g=s*l-o*a;return Math.sqrt(h*h+u*u+g*g)/2}getQuality(){const e=this.getVertices();if(!e)return null;const[t,n,i]=e,s=Math.sqrt(Math.pow(n.position.x-i.position.x,2)+Math.pow(n.position.y-i.position.y,2)+Math.pow(n.position.z-i.position.z,2)),o=Math.sqrt(Math.pow(t.position.x-i.position.x,2)+Math.pow(t.position.y-i.position.y,2)+Math.pow(t.position.z-i.position.z,2)),r=Math.sqrt(Math.pow(t.position.x-n.position.x,2)+Math.pow(t.position.y-n.position.y,2)+Math.pow(t.position.z-n.position.z,2)),a=(s+o+r)/2,l=a*(a-s)*(a-o)*(a-r);if(l<=0)return 0;const c=Math.sqrt(l),h=s*o*r/(4*c);if(h<1e-10)return 0;const u=2*(c/a)/h;return Math.max(0,Math.min(1,u))}containsVertex(e){const t=this.getVertices();return!!t&&t.some(t=>t.id===e.id)}getOppositeHalfedge(e){const t=this.getHalfedges();if(!t)return null;for(const n of t){const t=n.getSourceVertex();if(t&&t.id!==e.id&&n.vertex.id!==e.id)return n}return null}isDegenerate(e=1e-10){const t=this.getArea();return null===t||t<e}}class d{constructor(){this.vertices=new Map,this.edges=new Map,this.halfedges=new Map,this.faces=new Map,this.nextVertexId=0,this.nextEdgeId=0,this.nextHalfedgeId=0,this.nextFaceId=0,this.edgeMap=new Map}static fromBufferGeometry(n,o){const r=new d,a=n.attributes.position;if(!a)throw new Error("Geometry must have a position attribute");const l=n.index;if(!l)throw new Error("Geometry must be indexed");const u=a.count,f=l.count/3;if(l.count%3!=0)throw new Error("Geometry must be triangulated (indices count must be divisible by 3)");for(let i=0;i<u;i++){const n=a.getX(i),s=a.getY(i),o=a.getZ(i),l=new c(t(r.nextVertexId++),new e.Vector3(n,s,o));r.vertices.set(l.id,l)}for(let e=0;e<f;e++){const n=l.getX(3*e+0),o=l.getX(3*e+1),a=l.getX(3*e+2),c=r.vertices.get(t(n)),u=r.vertices.get(t(o)),d=r.vertices.get(t(a));if(!c||!u||!d)throw new Error(`Vertex not found in face ${e}: ${n}, ${o}, ${a}`);const f=r.getOrCreateEdge(n,o),p=r.getOrCreateEdge(o,a),x=r.getOrCreateEdge(a,n),y=new g(s(r.nextFaceId++),null);r.faces.set(y.id,y);const m=new h(i(r.nextHalfedgeId++),u,f),v=new h(i(r.nextHalfedgeId++),d,p),z=new h(i(r.nextHalfedgeId++),c,x);r.halfedges.set(m.id,m),r.halfedges.set(v.id,v),r.halfedges.set(z.id,z),f.addHalfedge(m),p.addHalfedge(v),x.addHalfedge(z),m.next=v,v.next=z,z.next=m,m.prev=z,v.prev=m,z.prev=v,m.face=y,v.face=y,z.face=y,y.halfedge=m,c.halfedge||(c.halfedge=m),u.halfedge||(u.halfedge=v),d.halfedge||(d.halfedge=z),f.halfedge=m,p.halfedge=v,x.halfedge=z}return r.setupTwinHalfedges(),o&&o.length>0&&r.markFeatureEdges(o),r.classifyVertices(),r}getOrCreateEdge(e,t){const n=this.makeEdgeKey(e,t);let i=this.edgeMap.get(n);if(!i){const s=this.vertices.get(e),o=this.vertices.get(t);if(!s||!o)throw new Error(`Vertex not found: ${e} or ${t}`);const r=o.position.x-s.position.x,a=o.position.y-s.position.y,l=o.position.z-s.position.z,c=Math.sqrt(r*r+a*a+l*l);i=new u(this.nextEdgeId++,null,c),i.allHalfedges=[],this.edgeMap.set(n,i),this.edges.set(i.id,i)}return i}makeEdgeKey(e,t){return e<t?`${e},${t}`:`${t},${e}`}setupTwinHalfedges(){var e;for(const t of this.edges.values()){const e=t.allHalfedges,n=e.length;0!==n&&(1===n?(e[0].twin=null,t.type=a.Boundary):2===n?(e[0].twin=e[1],e[1].twin=e[0],t.type=a.Manifold):(this.setupNonManifoldTwins(t,e),t.type=a.NonManifold))}for(const t of this.halfedges.values())if(null!==t.twin){const n=null==(e=t.prev)?void 0:e.vertex;n&&(n.halfedge=t)}}setupNonManifoldTwins(e,t){const[n,i]=e.getVertices();if(!n||!i)return;const s=[],o=[];for(const a of t)a.vertex.id===n.id?s.push(a):o.push(a);const r=Math.min(s.length,o.length);for(let a=0;a<r;a++)s[a].twin=o[a],o[a].twin=s[a];for(let a=r;a<s.length;a++)s[a].twin=null;for(let a=r;a<o.length;a++)o[a].twin=null}markFeatureEdges(e){for(const[t,n]of e){const e=this.makeEdgeKey(t,n),i=this.edgeMap.get(e);i&&i.type===a.Manifold&&i.markAsFeature()}}classifyVertices(){for(const e of this.vertices.values())e.type=this.classifyVertex(e)}classifyVertex(e){let t=0;return e.forEachOutgoingHalfedge(e=>{e.edge.isSkeletonEdge()&&t++}),0===t?r.Manifold:2===t?r.OpenBook:r.SkeletonBranching}toBufferGeometry(){return new e.Vector3.constructor}getVertices(){return Array.from(this.vertices.values())}getEdges(){return Array.from(this.edges.values())}getFaces(){return Array.from(this.faces.values())}getHalfedges(){return Array.from(this.halfedges.values())}get vertexCount(){return this.vertices.size}get edgeCount(){return this.edges.size}get faceCount(){return this.faces.size}get halfedgeCount(){return this.halfedges.size}getNonManifoldEdges(){return this.getEdges().filter(e=>e.type===a.NonManifold)}getBoundaryEdges(){return this.getEdges().filter(e=>e.type===a.Boundary)}getFeatureEdges(){return this.getEdges().filter(e=>e.type===a.Feature)}getSkeletonEdges(){return this.getEdges().filter(e=>e.isSkeletonEdge())}isManifold(){return 0===this.getNonManifoldEdges().length}hasBoundary(){return this.getBoundaryEdges().length>0}getVertex(e){return this.vertices.get(e)}getEdge(e){return this.edges.get(e)}getFace(e){return this.faces.get(e)}getHalfedge(e){return this.halfedges.get(e)}getEdgeBetween(e,t){const n=this.makeEdgeKey(e.id,t.id);return this.edgeMap.get(n)}createVertex(e){const t=new c(this.nextVertexId++,e);return this.vertices.set(t.id,t),t}createFace(e,t,n){const i=this.getOrCreateEdge(e.id,t.id),s=this.getOrCreateEdge(t.id,n.id),o=this.getOrCreateEdge(n.id,e.id),r=new g(this.nextFaceId++,null);this.faces.set(r.id,r);const a=new h(this.nextHalfedgeId++,t,i),l=new h(this.nextHalfedgeId++,n,s),c=new h(this.nextHalfedgeId++,e,o);return this.halfedges.set(a.id,a),this.halfedges.set(l.id,l),this.halfedges.set(c.id,c),i.addHalfedge(a),s.addHalfedge(l),o.addHalfedge(c),a.next=l,l.next=c,c.next=a,a.prev=c,l.prev=a,c.prev=l,a.face=r,l.face=r,c.face=r,r.halfedge=a,e.halfedge||(e.halfedge=a),t.halfedge||(t.halfedge=l),n.halfedge||(n.halfedge=c),i.halfedge=a,s.halfedge=l,o.halfedge=c,r}getStats(){const e=this.getNonManifoldEdges().length,t=this.getBoundaryEdges().length,n=this.getFeatureEdges().length,i=this.vertexCount-this.edgeCount+this.faceCount;return{vertexCount:this.vertexCount,edgeCount:this.edgeCount,faceCount:this.faceCount,nonManifoldEdgeCount:e,boundaryEdgeCount:t,featureEdgeCount:n,eulerCharacteristic:i}}}function f(e,t){const n=t.x-e.x,i=t.y-e.y,s=t.z-e.z;return n*n+i*i+s*s}function p(e,t){return Math.sqrt(f(e,t))}function x(e,t){return e.x*t.x+e.y*t.y+e.z*t.z}function y(e,t){return{x:e.y*t.z-e.z*t.y,y:e.z*t.x-e.x*t.z,z:e.x*t.y-e.y*t.x}}function m(e){return Math.sqrt(e.x*e.x+e.y*e.y+e.z*e.z)}function v(e){return e.x*e.x+e.y*e.y+e.z*e.z}function z(e){const t=m(e);return t<1e-10?{x:0,y:0,z:0}:{x:e.x/t,y:e.y/t,z:e.z/t}}function M(e,t){return{x:e.x+t.x,y:e.y+t.y,z:e.z+t.z}}function E(e,t){return{x:e.x-t.x,y:e.y-t.y,z:e.z-t.z}}function w(e,t){return{x:e.x*t,y:e.y*t,z:e.z*t}}function V(e,t,n){return{x:e.x+(t.x-e.x)*n,y:e.y+(t.y-e.y)*n,z:e.z+(t.z-e.z)*n}}function k(e,t){return{x:(e.x+t.x)/2,y:(e.y+t.y)/2,z:(e.z+t.z)/2}}function C(e,t){const n=m(e),i=m(t);if(n<1e-10||i<1e-10)return 0;const s=x(e,t)/(n*i);return Math.acos(Math.max(-1,Math.min(1,s)))}function S(e,t,n){const i=E(n,t),s=v(i);if(s<1e-10)return{...t};return M(t,w(i,Math.max(0,Math.min(1,x(E(e,t),i)/s))))}function b(e,t,n){return m(y(E(t,e),E(n,e)))/2}function B(e,t,n){return z(y(E(t,e),E(n,e)))}function F(e,t,n){const i=p(t,n),s=p(e,n),o=p(e,t),r=b(e,t,n);return r<1e-10?null:i*s*o/(4*r)}function _(e,t,n){const i=(p(t,n)+p(e,n)+p(e,t))/2,s=b(e,t,n);return i<1e-10?null:s/i}function H(e,t,n,i){const s=z(M(B(e,t,n),B(e,i,t)));let o;o=Math.abs(s.x)<.9?z(y(s,{x:1,y:0,z:0})):z(y(s,{x:0,y:1,z:0}));const r=y(s,o),a=e=>({x:x(e,o),y:x(e,r)}),l=a(e),c=a(t),h=a(n),u=a(i),g=(e,t)=>e.x*t.y-e.y*t.x,d={x:c.x-l.x,y:c.y-l.y},f={x:h.x-l.x,y:h.y-l.y},p={x:u.x-l.x,y:u.y-l.y};if(g(d,f)*g(d,p)>=0)return!1;const m={x:u.x-h.x,y:u.y-h.y},v={x:l.x-h.x,y:l.y-h.y},E={x:c.x-h.x,y:c.y-h.y};return g(m,v)*g(m,E)<0}class I{constructor(e){this.id=e,this.vertices=[],this.edges=[],this.isClosed=!1,this._totalLength=0,this._cumulativeLengths=[]}get startVertex(){return this.vertices[0]}get endVertex(){return this.vertices[this.vertices.length-1]}get totalLength(){return this._totalLength}get vertexCount(){return this.vertices.length}get edgeCount(){return this.edges.length}addVertex(e){if(this.vertices.length>0){const t=this.vertices[this.vertices.length-1],n=p({x:t.position.x,y:t.position.y,z:t.position.z},{x:e.position.x,y:e.position.y,z:e.position.z});this._totalLength+=n}this._cumulativeLengths.push(this._totalLength),this.vertices.push(e)}addEdge(e){this.edges.push(e)}recomputeLengths(){this._totalLength=0,this._cumulativeLengths=[0];for(let e=1;e<this.vertices.length;e++){const t=this.vertices[e-1],n=this.vertices[e],i=p({x:t.position.x,y:t.position.y,z:t.position.z},{x:n.position.x,y:n.position.y,z:n.position.z});this._totalLength+=i,this._cumulativeLengths.push(this._totalLength)}}getParameterAtVertex(e){return 0===this._totalLength||e<0||e>=this.vertices.length?0:(this._cumulativeLengths[e]??0)/this._totalLength}getPositionAt(e){if(0===this.vertices.length)return null;if(1===this.vertices.length){const e=this.vertices[0];return{x:e.position.x,y:e.position.y,z:e.position.z}}const t=(e=Math.max(0,Math.min(1,e)))*this._totalLength;for(let i=1;i<this.vertices.length;i++){const e=this._cumulativeLengths[i-1]??0,n=this._cumulativeLengths[i]??0;if(t<=n){const s=this.vertices[i-1],o=this.vertices[i],r=n-e;if(r<1e-10)return{x:s.position.x,y:s.position.y,z:s.position.z};const a=(t-e)/r;return V({x:s.position.x,y:s.position.y,z:s.position.z},{x:o.position.x,y:o.position.y,z:o.position.z},a)}}const n=this.vertices[this.vertices.length-1];return{x:n.position.x,y:n.position.y,z:n.position.z}}projectPoint(e){if(0===this.vertices.length)return null;if(1===this.vertices.length){const t=this.vertices[0],n={x:t.position.x,y:t.position.y,z:t.position.z};return{point:n,parameter:0,distance:p(e,n)}}let t=null,n=0,i=1/0;for(let s=1;s<this.vertices.length;s++){const o=this.vertices[s-1],r=this.vertices[s],a={x:o.position.x,y:o.position.y,z:o.position.z},l=S(e,a,{x:r.position.x,y:r.position.y,z:r.position.z}),c=p(e,l);if(c<i){i=c,t=l;const e=this._cumulativeLengths[s-1]??0;if((this._cumulativeLengths[s]??0)-e<1e-10)n=e/this._totalLength;else{n=(e+p(a,l))/this._totalLength}}}return t?{point:t,parameter:n,distance:i}:null}getVertex(e){return this.vertices[e]}getEdge(e){return this.edges[e]}containsVertex(e){return this.vertices.some(t=>t.id===e.id)}containsEdge(e){return this.edges.some(t=>t.id===e.id)}indexOfVertex(e){return this.vertices.findIndex(t=>t.id===e.id)}forEachVertex(e){this.vertices.forEach(e)}forEachEdge(e){this.edges.forEach(e)}clone(e){const t=new I(e);return t.vertices=[...this.vertices],t.edges=[...this.edges],t.isClosed=this.isClosed,t._totalLength=this._totalLength,t._cumulativeLengths=[...this._cumulativeLengths],t}}class O{constructor(e){this.nextSegmentId=0,this.mesh=e}build(){const e=this.mesh.getSkeletonEdges(),t=[],n=[];for(const i of this.mesh.getVertices())i.type===r.SkeletonBranching?t.push(i):i.type===r.OpenBook&&n.push(i);return{segments:this.buildSegments(e,t),skeletonEdges:e,branchingVertices:t,openBookVertices:n}}buildSegments(e,t){const n=[],i=new Set,s=new Set(t.map(e=>e.id));for(const o of t){const t=this.getIncidentSkeletonEdges(o,e);for(const e of t){if(i.has(e.id))continue;const t=this.traceSegment(o,e,s,i);t&&n.push(t)}}for(const o of e){if(i.has(o.id))continue;const e=this.traceClosedLoop(o,i);e&&n.push(e)}return n}getIncidentSkeletonEdges(e,t){const n=new Set(t.map(e=>e.id)),i=[];return e.forEachOutgoingHalfedge(e=>{n.has(e.edge.id)&&(i.some(t=>t.id===e.edge.id)||i.push(e.edge))}),i}traceSegment(e,t,n,i){var s,o;const r=new I(this.createSegmentId());r.addVertex(e);let a=e,l=t;for(;l&&!i.has(l.id);){i.add(l.id),r.addEdge(l);const e=l.getOtherVertex(a);if(!e)break;if(r.addVertex(e),a=e,n.has(a.id))break;l=this.getNextSkeletonEdge(a,l,i)}return r.vertices.length>2&&(null==(s=r.startVertex)?void 0:s.id)===(null==(o=r.endVertex)?void 0:o.id)&&(r.isClosed=!0,r.vertices.pop()),r.recomputeLengths(),r}traceClosedLoop(e,t){const[n,i]=e.getVertices();if(!n||!i)return null;const s=new I(this.createSegmentId());s.addVertex(n),s.addEdge(e),s.addVertex(i),t.add(e.id);let o=i,r=this.getNextSkeletonEdge(o,e,t);for(;r&&!t.has(r.id);){t.add(r.id),s.addEdge(r);const e=r.getOtherVertex(o);if(!e)break;if(e.id===n.id){s.isClosed=!0;break}s.addVertex(e),o=e,r=this.getNextSkeletonEdge(o,r,t)}return s.recomputeLengths(),s}getNextSkeletonEdge(e,t,n){let i=null;return e.forEachOutgoingHalfedge(e=>{e.edge.id!==t.id&&e.edge.isSkeletonEdge()&&!n.has(e.edge.id)&&(i=e.edge)}),i}createSegmentId(){return this.nextSegmentId++}}class N{constructor(e){this.segments=new Map,this.skeletonEdges=[],this.branchingVertices=[],this.openBookVertices=[],this.vertexToSegment=new Map,this.isBuilt=!1,this.mesh=e}build(){const e=new O(this.mesh).build();this.applyBuildResult(e),this.isBuilt=!0}applyBuildResult(e){this.segments.clear(),this.vertexToSegment.clear();for(const t of e.segments){this.segments.set(t.id,t);for(let e=1;e<t.vertices.length-1;e++){const n=t.vertices[e];n&&this.vertexToSegment.set(n.id,t)}}this.skeletonEdges=e.skeletonEdges,this.branchingVertices=e.branchingVertices,this.openBookVertices=e.openBookVertices}rebuild(){this.build()}getSegments(){return Array.from(this.segments.values())}getSegment(e){return this.segments.get(e)}getSegmentForVertex(e){return this.vertexToSegment.get(e.id)}getSkeletonEdges(){return this.skeletonEdges}getBranchingVertices(){return this.branchingVertices}getOpenBookVertices(){return this.openBookVertices}get segmentCount(){return this.segments.size}get skeletonEdgeCount(){return this.skeletonEdges.length}get branchingVertexCount(){return this.branchingVertices.length}get openBookVertexCount(){return this.openBookVertices.length}get built(){return this.isBuilt}projectPoint(e){let t=null;for(const n of this.segments.values()){const i=n.projectPoint(e);i&&(!t||i.distance<t.distance)&&(t={point:i.point,segment:n,parameter:i.parameter,distance:i.distance})}return t}isVertexOnSkeleton(e){return e.isOnSkeleton()}isEdgeOnSkeleton(e){return e.isSkeletonEdge()}getAllSkeletonVertices(){return[...this.branchingVertices,...this.openBookVertices]}getTotalLength(){let e=0;for(const t of this.segments.values())e+=t.totalLength;return e}getStats(){let e=0;for(const t of this.segments.values())t.isClosed&&e++;return{segmentCount:this.segmentCount,skeletonEdgeCount:this.skeletonEdgeCount,branchingVertexCount:this.branchingVertexCount,openBookVertexCount:this.openBookVertexCount,totalLength:this.getTotalLength(),closedLoopCount:e}}}function L(e){const t=new N(e);return t.build(),t}class ${constructor(e){this.skeleton=e}constrainPosition(e,t){switch(e.type){case r.Manifold:return{position:t,wasConstrained:!1,constraintDistance:0};case r.OpenBook:return this.constrainToSegment(e,t);case r.SkeletonBranching:case r.NonManifoldOther:return{position:{x:e.position.x,y:e.position.y,z:e.position.z},wasConstrained:!0,constraintDistance:p(t,{x:e.position.x,y:e.position.y,z:e.position.z})};default:return{position:t,wasConstrained:!1,constraintDistance:0}}}constrainToSegment(e,t){const n=this.skeleton.getSegmentForVertex(e);if(!n){const n=this.skeleton.projectPoint(t);return n?{position:n.point,wasConstrained:!0,segment:n.segment,constraintDistance:n.distance}:{position:{x:e.position.x,y:e.position.y,z:e.position.z},wasConstrained:!0,constraintDistance:p(t,{x:e.position.x,y:e.position.y,z:e.position.z})}}const i=n.projectPoint(t);return i?{position:i.point,wasConstrained:!0,segment:n,constraintDistance:i.distance}:{position:{x:e.position.x,y:e.position.y,z:e.position.z},wasConstrained:!0,segment:n,constraintDistance:0}}canMoveFreely(e){return e.type===r.Manifold}isFixed(e){return e.type===r.SkeletonBranching||e.type===r.NonManifoldOther}isConstrainedToSegment(e){return e.type===r.OpenBook}getConstraintSegment(e){if(e.type===r.OpenBook)return this.skeleton.getSegmentForVertex(e)}getAllowedDirection(e){if(e.type!==r.OpenBook)return null;const t=this.skeleton.getSegmentForVertex(e);if(!t||t.vertices.length<2)return null;const n=t.indexOfVertex(e);if(n<0)return null;let i,s;if(0===n){const e=t.vertices[0],n=t.vertices[1];i={x:e.position.x,y:e.position.y,z:e.position.z},s={x:n.position.x,y:n.position.y,z:n.position.z}}else if(n===t.vertices.length-1){const e=t.vertices[n-1],o=t.vertices[n];i={x:e.position.x,y:e.position.y,z:e.position.z},s={x:o.position.x,y:o.position.y,z:o.position.z}}else{const e=t.vertices[n-1],o=t.vertices[n+1];i={x:e.position.x,y:e.position.y,z:e.position.z},s={x:o.position.x,y:o.position.y,z:o.position.z}}const o=s.x-i.x,a=s.y-i.y,l=s.z-i.z,c=Math.sqrt(o*o+a*a+l*l);return c<1e-10?null:{x:o/c,y:a/c,z:l/c}}}function A(e){return new $(e)}function q(e){const t=[],n=[],i=e.attributes.position;if(!i)return t.push("Geometry must have a position attribute"),{isValid:!1,errors:t,warnings:n};const s=e.index;if(!s)return t.push("Geometry must be indexed"),{isValid:!1,errors:t,warnings:n};s.count%3!=0&&t.push(`Index count (${s.count}) must be divisible by 3 for triangle mesh`);const o=i.count;for(let c=0;c<s.count;c++){const e=s.getX(c);if(e<0||e>=o){t.push(`Invalid index ${e} at position ${c} (vertex count: ${o})`);break}}let r=0;const a=s.count/3;for(let c=0;c<a;c++){const e=s.getX(3*c),t=s.getX(3*c+1),n=s.getX(3*c+2);e!==t&&t!==n&&n!==e||r++}r>0&&n.push(`Found ${r} degenerate triangle(s) with repeated vertices`);let l=0;for(let c=0;c<o;c++){const e=i.getX(c),t=i.getY(c),n=i.getZ(c);isFinite(e)&&isFinite(t)&&isFinite(n)||l++}return l>0&&t.push(`Found ${l} vertex position(s) with NaN or Infinity values`),{isValid:0===t.length,errors:t,warnings:n}}function P(e,t={}){const{featureEdges:n,validate:i=!0}=t;if(i){const t=q(e);if(!t.isValid)throw new Error(`Invalid geometry: ${t.errors.join("; ")}`)}return d.fromBufferGeometry(e,n)}function T(t,n={}){const{computeNormals:i=!0,smoothNormals:s=!0}=n,o=new e.BufferGeometry,r=t.getFaces();if(0===r.length)return o;const a=t.getVertices(),l=new Map;a.forEach((e,t)=>{l.set(e.id,t)});const c=new Float32Array(3*a.length);a.forEach((e,t)=>{c[3*t]=e.position.x,c[3*t+1]=e.position.y,c[3*t+2]=e.position.z});const h=[];for(const e of r){const t=e.getVertices();if(!t)continue;const[n,i,s]=t,o=l.get(n.id),r=l.get(i.id),a=l.get(s.id);void 0!==o&&void 0!==r&&void 0!==a&&h.push(o,r,a)}return o.setAttribute("position",new e.BufferAttribute(c,3)),o.setIndex(h),i&&(s?function(t,n,i){const s=n.getVertices(),o=new Float32Array(3*s.length),r=new Uint32Array(s.length);for(const e of n.getFaces()){const t=e.getNormal();if(!t)continue;const n=e.getVertices();if(n)for(const e of n){const n=i.get(e.id);if(void 0===n)continue;const s=3*n;o[s]=(o[s]??0)+t.x,o[s+1]=(o[s+1]??0)+t.y,o[s+2]=(o[s+2]??0)+t.z,r[n]=(r[n]??0)+1}}for(let e=0;e<s.length;e++){const t=r[e]??0;if(0===t)continue;const n=3*e,i=(o[n]??0)/t,s=(o[n+1]??0)/t,a=(o[n+2]??0)/t,l=Math.sqrt(i*i+s*s+a*a);l>1e-10?(o[3*e]=i/l,o[3*e+1]=s/l,o[3*e+2]=a/l):(o[3*e]=0,o[3*e+1]=1,o[3*e+2]=0)}t.setAttribute("normal",new e.BufferAttribute(o,3))}(o,t,l):o.computeVertexNormals()),o}function G(t){const n=new e.BufferGeometry,i=t.getSkeletonEdges();if(0===i.length)return n;const s=new Float32Array(6*i.length);let o=0;for(const e of i){const[t,n]=e.getVertices();t&&n&&(s[o++]=t.position.x,s[o++]=t.position.y,s[o++]=t.position.z,s[o++]=n.position.x,s[o++]=n.position.y,s[o++]=n.position.z)}return n.setAttribute("position",new e.BufferAttribute(s.slice(0,o),3)),n}function Q(t){const n=T(t,{computeNormals:!0}),i=t.getVertices(),s=new Float32Array(3*i.length),o=new Map;i.forEach((e,t)=>{o.set(e.id,t)});for(const e of i){const t=o.get(e.id);if(void 0===t)continue;let n=0,i=0,r=0;switch(e.type){case"manifold":n=.2,i=.8,r=.2;break;case"open_book":n=.2,i=.4,r=.9;break;case"skeleton_branching":n=.9,i=.2,r=.2;break;case"non_manifold_other":n=.9,i=.2,r=.9}s[3*t]=n,s[3*t+1]=i,s[3*t+2]=r}return n.setAttribute("color",new e.BufferAttribute(s,3)),n}function R(t){const n=t.getFaces();if(0===n.length)return new e.BufferGeometry;const i=new Float32Array(9*n.length),s=new Float32Array(9*n.length),o=new Float32Array(9*n.length);let r=0;for(const e of n){const t=e.getVertices(),n=e.getNormal(),a=e.getQuality()??0;if(!t||!n)continue;const l=a<.5?1:2-2*a,c=a<.5?2*a:1,h=.1;for(const e of t)i[r]=e.position.x,i[r+1]=e.position.y,i[r+2]=e.position.z,s[r]=l,s[r+1]=c,s[r+2]=h,o[r]=n.x,o[r+1]=n.y,o[r+2]=n.z,r+=3}const a=new e.BufferGeometry;return a.setAttribute("position",new e.BufferAttribute(i.slice(0,r),3)),a.setAttribute("color",new e.BufferAttribute(s.slice(0,r),3)),a.setAttribute("normal",new e.BufferAttribute(o.slice(0,r),3)),a}class D{constructor(e){if(this.cells=new Map,this.itemPositions=new Map,e<=0)throw new Error("Cell size must be positive");this.cellSize=e}getCellKey(e,t,n){return`${Math.floor(e/this.cellSize)},${Math.floor(t/this.cellSize)},${Math.floor(n/this.cellSize)}`}getCellIndices(e,t,n){return[Math.floor(e/this.cellSize),Math.floor(t/this.cellSize),Math.floor(n/this.cellSize)]}insert(e,t){const n=this.getCellKey(t.x,t.y,t.z);let i=this.cells.get(n);i||(i=[],this.cells.set(n,i)),i.push(e),this.itemPositions.set(e,t)}remove(e){const t=this.itemPositions.get(e);if(!t)return!1;const n=this.getCellKey(t.x,t.y,t.z),i=this.cells.get(n);if(i){const t=i.indexOf(e);-1!==t&&(i.splice(t,1),0===i.length&&this.cells.delete(n))}return this.itemPositions.delete(e),!0}update(e,t){this.remove(e),this.insert(e,t)}queryRadius(e,t){const n=[],i=t*t,s=this.getCellIndices(e.x-t,e.y-t,e.z-t),o=this.getCellIndices(e.x+t,e.y+t,e.z+t);for(let r=s[0];r<=o[0];r++)for(let t=s[1];t<=o[1];t++)for(let a=s[2];a<=o[2];a++){const s=`${r},${t},${a}`,o=this.cells.get(s);if(o)for(const t of o){const s=this.itemPositions.get(t);if(s){const o=s.x-e.x,r=s.y-e.y,a=s.z-e.z;o*o+r*r+a*a<=i&&n.push(t)}}}return n}queryKNearest(e,t,n){let i=this.cellSize,s=[];for(;s.length<t&&!(void 0!==n&&i>n);){s=[];const t=this.queryRadius(e,i);for(const n of t){const t=this.itemPositions.get(n);if(t){const i=t.x-e.x,o=t.y-e.y,r=t.z-e.z;s.push({item:n,distSq:i*i+o*o+r*r})}}i*=2}return s.sort((e,t)=>e.distSq-t.distSq),s.slice(0,t).map(e=>e.item)}clear(){this.cells.clear(),this.itemPositions.clear()}get size(){return this.itemPositions.size}get cellCount(){return this.cells.size}getPosition(e){return this.itemPositions.get(e)}has(e){return this.itemPositions.has(e)}*[Symbol.iterator](){for(const e of this.itemPositions.keys())yield e}getAll(){return Array.from(this.itemPositions.keys())}}function j(e){return{min:{x:Math.min(e.v0.x,e.v1.x,e.v2.x),y:Math.min(e.v0.y,e.v1.y,e.v2.y),z:Math.min(e.v0.z,e.v1.z,e.v2.z)},max:{x:Math.max(e.v0.x,e.v1.x,e.v2.x),y:Math.max(e.v0.y,e.v1.y,e.v2.y),z:Math.max(e.v0.z,e.v1.z,e.v2.z)}}}function X(e,t){return{min:{x:Math.min(e.min.x,t.min.x),y:Math.min(e.min.y,t.min.y),z:Math.min(e.min.z,t.min.z)},max:{x:Math.max(e.max.x,t.max.x),y:Math.max(e.max.y,t.max.y),z:Math.max(e.max.z,t.max.z)}}}function K(e,t){let n=0;return e.x<t.min.x?n+=(t.min.x-e.x)**2:e.x>t.max.x&&(n+=(e.x-t.max.x)**2),e.y<t.min.y?n+=(t.min.y-e.y)**2:e.y>t.max.y&&(n+=(e.y-t.max.y)**2),e.z<t.min.z?n+=(t.min.z-e.z)**2:e.z>t.max.z&&(n+=(e.z-t.max.z)**2),n}function U(e,t){const n=t.v0,i=t.v1,s=t.v2,o=i.x-n.x,r=i.y-n.y,a=i.z-n.z,l=s.x-n.x,c=s.y-n.y,h=s.z-n.z,u=e.x-n.x,g=e.y-n.y,d=e.z-n.z,f=o*u+r*g+a*d,p=l*u+c*g+h*d;if(f<=0&&p<=0)return n;const x=e.x-i.x,y=e.y-i.y,m=e.z-i.z,v=o*x+r*y+a*m,z=l*x+c*y+h*m;if(v>=0&&z<=v)return i;const M=f*z-v*p;if(M<=0&&f>=0&&v<=0){const e=f/(f-v);return{x:n.x+e*o,y:n.y+e*r,z:n.z+e*a}}const E=e.x-s.x,w=e.y-s.y,V=e.z-s.z,k=o*E+r*w+a*V,C=l*E+c*w+h*V;if(C>=0&&k<=C)return s;const S=k*p-f*C;if(S<=0&&p>=0&&C<=0){const e=p/(p-C);return{x:n.x+e*l,y:n.y+e*c,z:n.z+e*h}}const b=v*C-k*z;if(b<=0&&z-v>=0&&k-C>=0){const e=(z-v)/(z-v+(k-C));return{x:i.x+e*(s.x-i.x),y:i.y+e*(s.y-i.y),z:i.z+e*(s.z-i.z)}}const B=1/(b+S+M),F=S*B,_=M*B;return{x:n.x+o*F+l*_,y:n.y+r*F+c*_,z:n.z+a*F+h*_}}class Y{constructor(e=4){this.root=null,this.triangles=[],this.maxLeafSize=e}build(e){if(this.triangles=e,0===e.length)return void(this.root=null);const t=e.map((e,t)=>t);this.root=this.buildNode(t)}buildNode(e){let t=j(this.triangles[e[0]]);for(let h=1;h<e.length;h++)t=X(t,j(this.triangles[e[h]]));if(e.length<=this.maxLeafSize)return{bounds:t,triangleIndices:e};const n=t.max.x-t.min.x,i=t.max.y-t.min.y,s=t.max.z-t.min.z;let o="x";i>n&&i>s?o="y":s>n&&s>i&&(o="z");const r=e.map(e=>{return{index:e,centroid:(t=this.triangles[e],{x:(t.v0.x+t.v1.x+t.v2.x)/3,y:(t.v0.y+t.v1.y+t.v2.y)/3,z:(t.v0.z+t.v1.z+t.v2.z)/3})};var t});r.sort((e,t)=>e.centroid[o]-t.centroid[o]);const a=Math.floor(r.length/2),l=r.slice(0,a).map(e=>e.index),c=r.slice(a).map(e=>e.index);return 0===l.length||0===c.length?{bounds:t,triangleIndices:e}:{bounds:t,left:this.buildNode(l),right:this.buildNode(c)}}closestPoint(e){if(!this.root||0===this.triangles.length)return null;let t=null,n=1/0;const i=[this.root];for(;i.length>0;){const s=i.pop();if(!(K(e,s.bounds)>=n))if(s.triangleIndices)for(const i of s.triangleIndices){const s=U(e,this.triangles[i]),o=f(e,s);o<n&&(n=o,t={point:s,distance:Math.sqrt(o),triangleIndex:i})}else if(s.left&&s.right){K(e,s.left.bounds)<K(e,s.right.bounds)?(i.push(s.right),i.push(s.left)):(i.push(s.left),i.push(s.right))}else s.left?i.push(s.left):s.right&&i.push(s.right)}return t}queryRadius(e,t){const n=[],i=t*t;if(!this.root)return n;const s=[this.root];for(;s.length>0;){const t=s.pop();if(!(K(e,t.bounds)>i))if(t.triangleIndices)for(const s of t.triangleIndices){f(e,U(e,this.triangles[s]))<=i&&n.push(s)}else t.left&&s.push(t.left),t.right&&s.push(t.right)}return n}get triangleCount(){return this.triangles.length}getTriangle(e){return this.triangles[e]}}function Z(e){const t=e.getEdges(),n=e.getVertices();let i=0,s=0,o=0;const l=[];for(const r of t)switch(r.type){case a.Manifold:case a.Feature:i++;break;case a.NonManifold:s++,l.push(W(r));break;case a.Boundary:o++}let c=0,h=0,u=0;const g=[];for(const a of n){u+=a.degree()??0,a.type===r.Manifold?c++:(h++,g.push(J(a)))}const d=n.length>0?u/n.length:0,f=e.vertexCount-e.edgeCount+e.faceCount;return{isManifold:0===s,hasBoundary:o>0,vertexCount:e.vertexCount,edgeCount:e.edgeCount,faceCount:e.faceCount,manifoldEdgeCount:i,nonManifoldEdgeCount:s,boundaryEdgeCount:o,manifoldVertexCount:c,nonManifoldVertexCount:h,nonManifoldEdges:l,nonManifoldVertices:g,eulerCharacteristic:f,averageVertexDegree:d}}function W(e){const[t,n]=e.getVertices();return{edgeId:e.id,vertexIndices:[t?t.id:-1,n?n.id:-1],faceCount:e.getFaceCount(),positions:[t?{x:t.position.x,y:t.position.y,z:t.position.z}:{x:0,y:0,z:0},n?{x:n.position.x,y:n.position.y,z:n.position.z}:{x:0,y:0,z:0}]}}function J(e){let t=0;return e.forEachOutgoingHalfedge(e=>{e.edge.isSkeletonEdge()&&t++}),{vertexId:e.id,position:{x:e.position.x,y:e.position.y,z:e.position.z},type:e.type,skeletonEdgeCount:t}}function ee(e){const t={manifold:0,openBook:0,skeletonBranching:0,nonManifoldOther:0,total:0};for(const n of e.getVertices())switch(n.type=te(n),t.total++,n.type){case r.Manifold:t.manifold++;break;case r.OpenBook:t.openBook++;break;case r.SkeletonBranching:t.skeletonBranching++;break;case r.NonManifoldOther:t.nonManifoldOther++}return t}function te(e){let t=0,n=0,i=!1,s=!1;if(e.forEachOutgoingHalfedge(e=>{n++,e.edge.isSkeletonEdge()&&t++,e.edge.isBoundary()&&(i=!0),e.edge.isNonManifold()&&(s=!0)}),0===n)return r.Manifold;if(s)return 2===t?r.OpenBook:1===t||t>2?r.SkeletonBranching:r.NonManifoldOther;if(i){if(2===t)return r.OpenBook;if(1===t||t>2)return r.SkeletonBranching}return 0===t?r.Manifold:2===t?r.OpenBook:r.SkeletonBranching}function ne(e,t){return e.getVertices().filter(e=>e.type===t)}function ie(e){return ne(e,r.Manifold)}function se(e){return e.getVertices().filter(e=>e.type!==r.Manifold)}function oe(e){e.classifyVertices()}function re(e){const t=[],n=[];return function(e,t){for(const n of e.getVertices())n.halfedge&&(e.getHalfedge(n.halfedge.id)||t.push({type:"invalid_vertex_halfedge",message:`Vertex ${n.id} references non-existent halfedge ${n.halfedge.id}`,elementIds:[n.id]})),isFinite(n.position.x)&&isFinite(n.position.y)&&isFinite(n.position.z)||t.push({type:"invalid_vertex_position",message:`Vertex ${n.id} has invalid position`,elementIds:[n.id]})}(e,t),function(e,t,n){for(const i of e.getEdges())if(0!==i.allHalfedges.length){e.getHalfedge(i.halfedge.id)||t.push({type:"invalid_edge_halfedge",message:`Edge ${i.id} references non-existent halfedge`,elementIds:[i.id]}),(i.length<=0||!isFinite(i.length))&&n.push({type:"invalid_edge_length",message:`Edge ${i.id} has invalid length: ${i.length}`,elementIds:[i.id]});for(const e of i.allHalfedges)e.edge.id!==i.id&&t.push({type:"halfedge_edge_mismatch",message:`Halfedge ${e.id} in edge ${i.id} references different edge ${e.edge.id}`,elementIds:[i.id,e.id]})}else t.push({type:"edge_no_halfedges",message:`Edge ${i.id} has no halfedges`,elementIds:[i.id]})}(e,t,n),function(e,t,n){var i;for(const s of e.getFaces()){if(!s.halfedge){t.push({type:"face_no_halfedge",message:`Face ${s.id} has no halfedge`,elementIds:[s.id]});continue}if(!e.getHalfedge(s.halfedge.id)){t.push({type:"invalid_face_halfedge",message:`Face ${s.id} references non-existent halfedge`,elementIds:[s.id]});continue}const o=s.getHalfedges();if(o){for(const e of o)(null==(i=e.face)?void 0:i.id)!==s.id&&t.push({type:"halfedge_face_mismatch",message:`Halfedge ${e.id} in face ${s.id} references different face`,elementIds:[s.id,e.id]});s.isDegenerate()&&n.push({type:"degenerate_face",message:`Face ${s.id} is degenerate (near-zero area)`,elementIds:[s.id]})}else t.push({type:"face_invalid_loop",message:`Face ${s.id} has invalid halfedge loop`,elementIds:[s.id]})}}(e,t,n),function(e,t){for(const n of e.getHalfedges())n.next?e.getHalfedge(n.next.id)||t.push({type:"halfedge_invalid_next",message:`Halfedge ${n.id} has invalid next pointer`,elementIds:[n.id]}):t.push({type:"halfedge_no_next",message:`Halfedge ${n.id} has no next pointer`,elementIds:[n.id]}),n.prev?e.getHalfedge(n.prev.id)||t.push({type:"halfedge_invalid_prev",message:`Halfedge ${n.id} has invalid prev pointer`,elementIds:[n.id]}):t.push({type:"halfedge_no_prev",message:`Halfedge ${n.id} has no prev pointer`,elementIds:[n.id]}),n.next&&n.next.prev!==n&&t.push({type:"halfedge_next_prev_mismatch",message:`Halfedge ${n.id}: next.prev does not point back`,elementIds:[n.id]}),n.prev&&n.prev.next!==n&&t.push({type:"halfedge_prev_next_mismatch",message:`Halfedge ${n.id}: prev.next does not point back`,elementIds:[n.id]}),n.twin&&(e.getHalfedge(n.twin.id)?n.twin.twin!==n&&t.push({type:"halfedge_twin_mismatch",message:`Halfedge ${n.id}: twin.twin does not point back`,elementIds:[n.id]}):t.push({type:"halfedge_invalid_twin",message:`Halfedge ${n.id} has invalid twin pointer`,elementIds:[n.id]})),e.getVertex(n.vertex.id)||t.push({type:"halfedge_invalid_vertex",message:`Halfedge ${n.id} references non-existent vertex`,elementIds:[n.id]}),e.getEdge(n.edge.id)||t.push({type:"halfedge_invalid_edge",message:`Halfedge ${n.id} references non-existent edge`,elementIds:[n.id]})}(e,t),{isValid:0===t.length,errors:t,warnings:n}}function ae(e){return re(e).isValid}function le(e){if(e.isSkeletonEdge())return!1;if(!e.canFlip())return!1;const t=ce(e);return!!t&&H(t.v0,t.v1,t.v2,t.v3)}function ce(e){const t=e.halfedge,n=t.twin;if(!n||!t.face||!n.face)return null;const i=t.next,s=n.next;if(!i||!s)return null;const o=n.vertex,r=t.vertex,a=i.vertex,l=s.vertex;return{v0:{x:o.position.x,y:o.position.y,z:o.position.z},v1:{x:r.position.x,y:r.position.y,z:r.position.z},v2:{x:a.position.x,y:a.position.y,z:a.position.z},v3:{x:l.position.x,y:l.position.y,z:l.position.z}}}function he(e,t){if(!le(t))return{success:!1,reason:"Edge cannot be flipped"};const n=t.halfedge,i=n.twin;if(!i)return{success:!1,reason:"Edge has no twin"};const s=n.next,o=n.prev,r=i.next,a=i.prev,l=i.vertex,c=n.vertex,h=s.vertex,u=r.vertex,g=n.face,d=i.face;if(!g||!d)return{success:!1,reason:"Missing faces"};const f=p({x:h.position.x,y:h.position.y,z:h.position.z},{x:u.position.x,y:u.position.y,z:u.position.z});return t.length=f,n.vertex=u,i.vertex=h,n.next=a,n.prev=s,a.next=s,a.prev=n,s.next=n,s.prev=a,i.next=o,i.prev=r,o.next=r,o.prev=i,r.next=i,r.prev=o,n.face=g,a.face=g,s.face=g,i.face=d,o.face=d,r.face=d,g.halfedge=n,d.halfedge=i,l.halfedge===n&&(l.halfedge=r),c.halfedge===i&&(c.halfedge=s),{success:!0,newLength:f}}function ue(e){const t=e.halfedge,n=t.twin;if(!n||!t.face||!n.face)return!0;const i=ce(e);if(!i)return!0;return ge(i.v2,i.v0,i.v1)+ge(i.v3,i.v0,i.v1)<=Math.PI+1e-10}function ge(e,t,n){const i=t.x-e.x,s=t.y-e.y,o=t.z-e.z,r=n.x-e.x,a=n.y-e.y,l=n.z-e.z,c=i*r+s*a+o*l,h=Math.sqrt(i*i+s*s+o*o),u=Math.sqrt(r*r+a*a+l*l);if(h<1e-10||u<1e-10)return 0;const g=Math.max(-1,Math.min(1,c/(h*u)));return Math.acos(g)}function de(e,t){const n=e.getEdges(),i=t??10*n.length;let s=0,o=0;for(;o<i;){o++;let e=!1;for(const t of n)if(!ue(t)&&le(t)){he(0,t).success&&(s++,e=!0)}if(!e)break}return s}function fe(t,n,i=.5){const[s,o]=n.getVertices();if(!s||!o)return{success:!1,reason:"Edge has no vertices"};const r=s.position.x,a=s.position.y,l=s.position.z,c={x:r+(o.position.x-r)*i,y:a+(o.position.y-a)*i,z:l+(o.position.z-l)*i},h=t.createVertex(new e.Vector3(c.x,c.y,c.z));n.isSkeletonEdge();const u=[],g=[],d=n.getFaces();if(0===d.length)return{success:!0,newVertex:h,newEdges:[],newFaces:[]};for(const e of d){if(!e)continue;const i=e.getHalfedges();if(!i)continue;let s=null;for(const e of i)if(e.edge.id===n.id){s=e;break}s&&pe(t,e,s,h,u,g)}return n.length=n.length*i,t.classifyVertices(),{success:!0,newVertex:h,newEdges:u,newFaces:g}}function pe(e,t,n,i,s,o){const r=n.next.vertex,a=n.vertex,l=e.createFace(i,a,r);o.push(l),n.vertex=i,i.halfedge||(i.halfedge=n)}function xe(e,t){const n=[];let i=0;const s=[];for(const o of e.getEdges())o.length>t&&s.push(o);for(const o of s){const t=fe(e,o);t.success&&t.newVertex&&(i++,n.push(t.newVertex))}return{splitCount:i,newVertices:n}}function ye(e){const[t,n]=e.getVertices();return!(!t||!n)&&((t.type!==r.SkeletonBranching&&t.type!==r.NonManifoldOther||n.type!==r.SkeletonBranching&&n.type!==r.NonManifoldOther)&&(n.type===r.SkeletonBranching||(n.type,r.NonManifoldOther),!!function(e,t){const n=new Set,i=new Set;e.forEachNeighbor(e=>{n.add(e.id)}),t.forEachNeighbor(e=>{i.add(e.id)});let s=0;for(const a of n)a!==e.id&&a!==t.id&&i.has(a)&&s++;const o=me(e,t),r=o.length;return s<=r}(t,n)))}function me(e,t){const n=new Set,i=[];return e.forEachOutgoingHalfedge(e=>{e.face&&n.add(e.face.id)}),t.forEachOutgoingHalfedge(e=>{e.face&&n.has(e.face.id)&&i.push(e.face)}),i}function ve(e,t){var n;if(!ye(t))return{success:!1,reason:"Edge cannot be contracted"};const[i,s]=t.getVertices();if(!i||!s)return{success:!1,reason:"Edge has no vertices"};const{keepVertex:o,removeVertex:a,newPosition:l}=function(e,t){const n=e=>{switch(e.type){case r.SkeletonBranching:case r.NonManifoldOther:return 3;case r.OpenBook:return 2;case r.Manifold:return 1;default:return 0}},i=n(e),s=n(t);let o,a,l;i>=s?(o=e,a=t):(o=t,a=e);l=o.type===r.SkeletonBranching||o.type===r.NonManifoldOther||o.type===r.OpenBook&&a.type===r.Manifold?{x:o.position.x,y:o.position.y,z:o.position.z}:k({x:e.position.x,y:e.position.y,z:e.position.z},{x:t.position.x,y:t.position.y,z:t.position.z});return{keepVertex:o,removeVertex:a,newPosition:l}}(i,s);o.position.set(l.x,l.y,l.z);const c=me(i,s);var h;h=o,a.forEachOutgoingHalfedge(e=>{e.twin&&(e.twin.vertex=h)});for(const r of c)e.faces.delete(r.id);e.edges.delete(t.id);for(const r of t.allHalfedges)e.halfedges.delete(r.id);if(e.vertices.delete(a.id),o.halfedge&&!e.halfedges.has(o.halfedge.id))for(const r of e.halfedges.values())if((null==(n=r.getSourceVertex())?void 0:n.id)===o.id){o.halfedge=r;break}return e.classifyVertices(),{success:!0,remainingVertex:o,removedFaces:c}}function ze(e,t){let n=0,i=0,s=[];for(const o of e.getEdges())o.length<t&&ye(o)&&s.push(o);for(;s.length>0;){const t=s.pop();if(!e.edges.has(t.id)||!ye(t))continue;ve(e,t).success&&(n++,i++)}return{contractCount:n,removedVertices:i}}function Me(e){const t=e.getNeighbors();if(0===t.length)return null;let n=0,i=0,s=0;for(const c of t)n+=c.position.x,i+=c.position.y,s+=c.position.z;n/=t.length,i/=t.length,s/=t.length;const o={x:n,y:i,z:s},r=function(e){let t=0,n=0,i=0,s=0;if(e.forEachOutgoingHalfedge(e=>{if(e.face){const o=e.face.getNormal();o&&(t+=o.x,n+=o.y,i+=o.z,s++)}}),0===s)return null;return z({x:t/s,y:n/s,z:i/s})}(e);if(!r)return o;const a={x:e.position.x,y:e.position.y,z:e.position.z},l=E(o,a);return M(a,E(l,w(r,x(l,r))))}function Ee(e,t,n,i){if(t.type===r.SkeletonBranching||t.type===r.NonManifoldOther)return{success:!1,reason:"Vertex is fixed"};const s={x:t.position.x,y:t.position.y,z:t.position.z};let o=n,a=!1;if(i){const e=i.constrainPosition(t,n);o=e.position,a=e.wasConstrained}if(!function(e,t){const n={x:e.position.x,y:e.position.y,z:e.position.z};e.position.set(t.x,t.y,t.z);let i=!0;return e.forEachOutgoingHalfedge(e=>{if(e.face){const t=e.face.getArea();null!==t&&t<1e-10&&(i=!1)}}),e.position.set(n.x,n.y,n.z),i}(t,o))return{success:!1,reason:"Relocation would create invalid geometry"};t.position.set(o.x,o.y,o.z),function(e){e.forEachOutgoingHalfedge(e=>{const t=e.getSourceVertex();t&&(e.edge.length=p({x:t.position.x,y:t.position.y,z:t.position.z},{x:e.vertex.position.x,y:e.vertex.position.y,z:e.vertex.position.z}))})}(t);return{success:!0,newPosition:o,wasConstrained:a,distanceMoved:p(s,o)}}function we(e,t,n,i=.5){const s=Me(t);if(!s)return{success:!1,reason:"Cannot compute smoothing target"};const o=t.position.x,r=t.position.y,a=t.position.z;return Ee(0,t,{x:o+(s.x-o)*i,y:r+(s.y-r)*i,z:a+(s.z-a)*i},n)}function Ve(e,t,n=.5){let i=0,s=0;for(const o of e.getVertices())if(o.type===r.Manifold||o.type===r.OpenBook){const e=we(0,o,t,n);e.success&&(i++,s+=e.distanceMoved??0)}return{smoothedCount:i,totalDistance:s}}function ke(e,t=.3){const n=e.getFaces(),i=e.getEdges(),s=[],o=[];for(const v of n){const e=v.getQuality();null!==e&&s.push(e);const t=v.getArea();null!==t&&o.push(t)}const r=i.map(e=>e.length),a=s.length>0?Math.min(...s):0,l=s.length>0?Math.max(...s):0,c=s.length>0?s.reduce((e,t)=>e+t,0)/s.length:0,h=s.length>0?s.reduce((e,t)=>e+Math.pow(t-c,2),0)/s.length:0,u=Math.sqrt(h),g=s.filter(e=>e<t).length,d=r.length>0?Math.min(...r):0,f=r.length>0?Math.max(...r):0,p=r.length>0?r.reduce((e,t)=>e+t,0)/r.length:0,x=o.length>0?Math.min(...o):0,y=o.length>0?Math.max(...o):0,m=o.reduce((e,t)=>e+t,0);return{minQuality:a,maxQuality:l,averageQuality:c,stdDevQuality:u,poorQualityCount:g,minEdgeLength:d,maxEdgeLength:f,averageEdgeLength:p,minArea:x,maxArea:y,totalArea:m}}function Ce(e,t=.3){return e.getFaces().filter(e=>{const n=e.getQuality();return null!==n&&n<t})}function Se(e,t,n=1.333){const i=t*n;return e.getEdges().filter(e=>e.length>i)}function be(e,t,n=.4){const i=t*n;return e.getEdges().filter(e=>e.length<i)}function Be(e,t){const n=e.getVertices();if(0===n.length)return 1;let i=1/0,s=1/0,o=1/0,r=-1/0,a=-1/0,l=-1/0;for(const h of n)i=Math.min(i,h.position.x),s=Math.min(s,h.position.y),o=Math.min(o,h.position.z),r=Math.max(r,h.position.x),a=Math.max(a,h.position.y),l=Math.max(l,h.position.z);const c=Math.sqrt(Math.pow(r-i,2)+Math.pow(a-s,2)+Math.pow(l-o,2));if(void 0!==t&&t>0){const n=ke(e);if(n.totalArea>0)return Math.sqrt(n.totalArea/(2*t))}return c/Math.sqrt(n.length)}function Fe(e,t=8){return e.getVertices().filter(e=>{const n=e.degree();return null!==n&&n>t})}function _e(e,t=4){return e.getVertices().filter(e=>{const n=e.degree();return null!==n&&n<t})}class He{constructor(e,t={}){this.skeleton=null,this.constraints=null,this.mesh=e;const n=t.targetEdgeLength??Be(e);this.options={...l,...t,targetEdgeLength:n},this.state={iteration:0,edgeSplits:0,edgeContractions:0,edgeFlips:0,vertexRelocations:0,quality:ke(e)},e.isManifold()||(this.skeleton=L(e),this.constraints=A(this.skeleton))}iterate(){this.state.iteration++;const e=this.options.targetEdgeLength,t=e*this.options.minEdgeLengthRatio,n=e*this.options.maxEdgeLengthRatio,i=xe(this.mesh,n);this.state.edgeSplits+=i.splitCount;const s=ze(this.mesh,t);this.state.edgeContractions+=s.contractCount;const o=de(this.mesh);this.state.edgeFlips+=o;const r=Ve(this.mesh,this.constraints??void 0,.5);return this.state.vertexRelocations+=r.smoothedCount,this.skeleton&&(i.splitCount>0||s.contractCount>0)&&this.skeleton.rebuild(),this.state.quality=ke(this.mesh,this.options.minTriangleQuality),this.options.verbose&&console.warn(`Iteration ${this.state.iteration}: splits=${i.splitCount}, contractions=${s.contractCount}, flips=${o}, smoothed=${r.smoothedCount}, avgQuality=${this.state.quality.averageQuality.toFixed(3)}`),{...this.state}}run(e){const t=e??this.options.iterations,n=Date.now(),i={vertices:this.mesh.vertexCount,faces:this.mesh.faceCount};for(let o=0;o<t;o++){const e=this.state.quality.averageQuality;this.iterate();const t=this.state.quality.averageQuality-e;if(Math.abs(t)<.001&&o>0){this.options.verbose&&console.warn(`Converged after ${o+1} iterations`);break}}const s=Date.now()-n;return{inputVertices:i.vertices,inputFaces:i.faces,outputVertices:this.mesh.vertexCount,outputFaces:this.mesh.faceCount,iterations:this.state.iteration,finalQuality:this.state.quality.averageQuality,nonManifoldEdges:this.mesh.getNonManifoldEdges().length,skeletonEdges:this.mesh.getSkeletonEdges().length,edgeFlips:this.state.edgeFlips,edgeSplits:this.state.edgeSplits,edgeContractions:this.state.edgeContractions,vertexRelocations:this.state.vertexRelocations,processingTimeMs:s}}hasConverged(){return this.state.quality.averageQuality>.9||0===this.state.quality.poorQualityCount}getMesh(){return this.mesh}getSkeleton(){return this.skeleton}getQuality(){return this.state.quality}getState(){return{...this.state}}toBufferGeometry(){return T(this.mesh)}}exports.AdaptiveRemesher=He,exports.BVH=Y,exports.BufferGeometryExporter=class{constructor(e={}){this.options=e}export(e){return T(e,this.options)}exportSkeleton(e){return G(e)}exportClassification(e){return Q(e)}exportQuality(e){return R(e)}setComputeNormals(e){return this.options.computeNormals=e,this}setSmoothNormals(e){return this.options.smoothNormals=e,this}},exports.BufferGeometryImporter=class{constructor(e={}){this.options=e}import(e){return P(e,this.options)}validate(e){return q(e)}setFeatureEdges(e){return this.options.featureEdges=e,this}setValidation(e){return this.options.validate=e,this}},exports.DEFAULT_REMESH_OPTIONS=l,exports.Edge=u,exports.EdgeContractor=class{constructor(e){this.mesh=e}contract(e){return ve(this.mesh,e)}canContract(e){return ye(e)}contractShortEdges(e){return ze(this.mesh,e)}shouldContract(e,t,n=.4){return e.length<t*n}},exports.EdgeFlipper=class{constructor(e){this.mesh=e}flip(e){return he(this.mesh,e)}canFlip(e){return le(e)}isDelaunay(e){return ue(e)}makeDelaunay(e){return de(this.mesh,e)}},exports.EdgeSplitter=class{constructor(e){this.mesh=e}split(e,t=.5){return fe(this.mesh,e,t)}splitLongEdges(e){return xe(this.mesh,e)}shouldSplit(e,t,n=1.333){return e.length>t*n}},exports.EdgeType=a,exports.Face=g,exports.FeatureSkeleton=N,exports.Halfedge=h,exports.ManifoldAnalyzer=class{constructor(){this.mesh=null,this.cachedResult=null}load(e){return this.mesh=d.fromBufferGeometry(e),this.cachedResult=null,this}loadMesh(e){return this.mesh=e,this.cachedResult=null,this}analyze(){if(!this.mesh)throw new Error("No mesh loaded. Call load() first.");return this.cachedResult||(this.cachedResult=Z(this.mesh)),this.cachedResult}isManifold(){return this.analyze().isManifold}hasBoundary(){return this.analyze().hasBoundary}getNonManifoldEdges(){return this.analyze().nonManifoldEdges}getNonManifoldVertices(){return this.analyze().nonManifoldVertices}getMesh(){return this.mesh}clearCache(){return this.cachedResult=null,this}},exports.NonManifoldMesh=d,exports.QualityMetrics=class{constructor(e){this.mesh=e}computeStats(e=.3){return ke(this.mesh,e)}getPoorQualityFaces(e=.3){return Ce(this.mesh,e)}getLongEdges(e,t=1.333){return Se(this.mesh,e,t)}getShortEdges(e,t=.4){return be(this.mesh,e,t)}computeTargetEdgeLength(e){return Be(this.mesh,e)}getHighValenceVertices(e=8){return Fe(this.mesh,e)}getLowValenceVertices(e=4){return _e(this.mesh,e)}},exports.SkeletonBuilder=O,exports.SkeletonConstraints=$,exports.SkeletonSegment=I,exports.SpatialHash=D,exports.TopologyValidator=class{constructor(e){this.mesh=e}validate(){return re(this.mesh)}isValid(){return ae(this.mesh)}getErrors(){return this.validate().errors}getWarnings(){return this.validate().warnings}},exports.Vertex=c,exports.VertexClassifier=class{constructor(e){this.mesh=e}classifyAll(){return ee(this.mesh)}getByType(e){return ne(this.mesh,e)}getManifold(){return ie(this.mesh)}getNonManifold(){return se(this.mesh)}reclassify(){oe(this.mesh)}},exports.VertexRelocator=class{constructor(e,t){this.constraints=null,this.mesh=e,this.constraints=t??null}setConstraints(e){this.constraints=e}relocate(e,t){return Ee(this.mesh,e,t,this.constraints??void 0)}smooth(e,t=.5){return we(this.mesh,e,this.constraints??void 0,t)}smoothAll(e=.5){return Ve(this.mesh,this.constraints??void 0,e)}canRelocate(e){return e.type===r.Manifold||e.type===r.OpenBook}},exports.VertexType=r,exports.add=M,exports.analyzeManifold=function(e){return Z(d.fromBufferGeometry(e))},exports.analyzeMesh=Z,exports.angleAtVertex=function(e,t,n){return C(E(e,t),E(n,t))},exports.angleBetween=C,exports.barycentricCoordinates=function(e,t,n,i){const s=E(n,t),o=E(i,t),r=E(e,t),a=x(s,s),l=x(s,o),c=x(o,o),h=x(r,s),u=x(r,o),g=a*c-l*l;if(Math.abs(g)<1e-10)return null;const d=(c*h-l*u)/g,f=(a*u-l*h)/g;return{u:1-d-f,v:d,w:f}},exports.buildSkeleton=function(e){return new O(e).build()},exports.canContractEdge=ye,exports.canFlipEdge=function(e){return"manifold"===e},exports.canFlipEdgeGeometric=le,exports.canMoveFreely=function(e){return"manifold"===e},exports.classifyAllVertices=ee,exports.classifyVertex=te,exports.computeMeshQuality=ke,exports.computeTangentialSmoothing=Me,exports.computeTargetEdgeLength=Be,exports.computeTriangleAspectRatio=function(e){const t=e.getHalfedges();if(!t)return null;const n=t.map(e=>e.edge.length),i=Math.min(...n),s=Math.max(...n);return i<1e-10?1/0:s/i},exports.contractEdge=ve,exports.contractShortEdges=ze,exports.cotangent=function(e,t,n){const i=E(e,t),s=E(n,t),o=x(i,s),r=m(y(i,s));return Math.abs(r)<1e-10?0:o/r},exports.createBVHFromMesh=function(e){const t=[];for(const i of e.getFaces()){const e=i.getVertices();e&&t.push({v0:{x:e[0].position.x,y:e[0].position.y,z:e[0].position.z},v1:{x:e[1].position.x,y:e[1].position.y,z:e[1].position.z},v2:{x:e[2].position.x,y:e[2].position.y,z:e[2].position.z}})}const n=new Y;return n.build(t),n},exports.createEdgeId=n,exports.createFaceId=s,exports.createHalfedgeId=i,exports.createRemesher=function(e,t={}){const n=d.fromBufferGeometry(e,t.featureEdges);return new He(n,t)},exports.createSegmentId=o,exports.createSkeleton=L,exports.createSkeletonConstraints=A,exports.createSpatialHash=function(e,t,n){let i=n;if(void 0===i&&e.length>1){let n=1/0,s=1/0,o=1/0,r=-1/0,a=-1/0,l=-1/0;for(const i of e){const e=t(i);n=Math.min(n,e.x),s=Math.min(s,e.y),o=Math.min(o,e.z),r=Math.max(r,e.x),a=Math.max(a,e.y),l=Math.max(l,e.z)}i=Math.sqrt((r-n)**2+(a-s)**2+(l-o)**2)/Math.sqrt(e.length)}const s=new D(i??1);for(const o of e)s.insert(o,t(o));return s},exports.createVertexId=t,exports.cross=y,exports.distance=p,exports.distanceSquared=f,exports.dot=x,exports.exportBufferGeometry=T,exports.exportClassificationGeometry=Q,exports.exportQualityGeometry=R,exports.exportSkeletonGeometry=G,exports.flipEdge=he,exports.fromVector3=function(e){return{x:e.x,y:e.y,z:e.z}},exports.getHighValenceVertices=Fe,exports.getLongEdges=Se,exports.getLowValenceVertices=_e,exports.getManifoldVertices=ie,exports.getNonManifoldVertices=se,exports.getOpenBookVertices=function(e){return ne(e,r.OpenBook)},exports.getPoorQualityFaces=Ce,exports.getShortEdges=be,exports.getSkeletonBranchingVertices=function(e){return ne(e,r.SkeletonBranching)},exports.getVerticesByType=ne,exports.importBufferGeometry=P,exports.isDelaunay=ue,exports.isManifold=function(e){return d.fromBufferGeometry(e).isManifold()},exports.isPointInTriangle=function(e,t,n,i){const s=E(n,t),o=E(i,t),r=E(e,t),a=x(s,s),l=x(s,o),c=x(o,o),h=x(r,s),u=x(r,o),g=a*c-l*l;if(Math.abs(g)<1e-10)return!1;const d=(c*h-l*u)/g,f=(a*u-l*h)/g;return 1-d-f>=0&&d>=0&&f>=0},exports.isPositionFixed=function(e){return"skeleton_branching"===e||"non_manifold_other"===e},exports.isQuadConvex=H,exports.isSkeletonConstrained=function(e){return"open_book"===e},exports.isSkeletonEdge=function(e){return"non_manifold"===e||"feature"===e||"boundary"===e},exports.isTopologyValid=ae,exports.length=m,exports.lengthSquared=v,exports.lerp=V,exports.makeDelaunay=de,exports.midpoint=k,exports.normalize=z,exports.projectPointOnLine=function(e,t,n){const i=E(n,t),s=v(i);return s<1e-10?{...t}:M(t,w(i,x(E(e,t),i)/s))},exports.projectPointOnSegment=S,exports.reclassifyVertices=oe,exports.relocateVertex=Ee,exports.remesh=function(e,t={}){const n=d.fromBufferGeometry(e,t.featureEdges),i=new He(n,t),s=i.run();return{geometry:i.toBufferGeometry(),stats:s}},exports.scale=w,exports.smoothAllVertices=Ve,exports.smoothVertex=we,exports.splitEdge=fe,exports.splitLongEdges=xe,exports.subtract=E,exports.toNumber=function(e){return e},exports.triangleArea=b,exports.triangleCentroid=function(e,t,n){return{x:(e.x+t.x+n.x)/3,y:(e.y+t.y+n.y)/3,z:(e.z+t.z+n.z)/3}},exports.triangleCircumcenter=function(e,t,n){const i=E(t,e),s=E(n,e),o=y(i,s),r=2*v(o);if(r<1e-10)return null;const a=v(i),l=v(s),c=w(y(o,i),l),h=w(y(s,o),a),u=w(M(c,h),1/r);return M(e,u)},exports.triangleCircumradius=F,exports.triangleInradius=_,exports.triangleNormal=B,exports.triangleQuality=function(e,t,n){const i=_(e,t,n),s=F(e,t,n);return null===i||null===s||s<1e-10?0:Math.max(0,Math.min(1,2*i/s))},exports.validateGeometry=q,exports.validateTopology=re;
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("three");function t(e){return e}function n(e){return e}function i(e){return e}function s(e){return e}function o(e){return e}var r=(e=>(e.Manifold="manifold",e.OpenBook="open_book",e.SkeletonBranching="skeleton_branching",e.NonManifoldOther="non_manifold_other",e))(r||{}),a=(e=>(e.Manifold="manifold",e.NonManifold="non_manifold",e.Feature="feature",e.Boundary="boundary",e))(a||{});const l={iterations:5,preserveBoundary:!0,minEdgeLengthRatio:.4,maxEdgeLengthRatio:1.333,minTriangleQuality:.3,maxNormalDeviation:Math.PI/6,useAcceleration:!0,chunkSize:0,memoryBudget:0,verbose:!1};class c{constructor(e,t){this.id=e,this.position=t,this.halfedge=null,this.type=r.Manifold,this.isMarked=!1}degree(){if(!this.halfedge)return null;let e=0,t=this.halfedge;do{if(e++,!t.twin||!t.twin.next)break;if(t=t.twin.next,e>1e4)throw new Error(`Vertex ${this.id}: degree calculation exceeded maximum iterations`)}while(t!==this.halfedge);return e}forEachOutgoingHalfedge(e){if(!this.halfedge)return;let t=this.halfedge,n=0;do{if(e(t),!t.twin||!t.twin.next)break;if(t=t.twin.next,n++,n>1e4)throw new Error(`Vertex ${this.id}: halfedge iteration exceeded maximum iterations`)}while(t!==this.halfedge)}getOutgoingHalfedges(){const e=[];return this.forEachOutgoingHalfedge(t=>e.push(t)),e}forEachNeighbor(e){this.forEachOutgoingHalfedge(t=>{e(t.vertex)})}getNeighbors(){const e=[];return this.forEachNeighbor(t=>e.push(t)),e}isBoundary(){if(!this.halfedge)return!0;let e=!1;return this.forEachOutgoingHalfedge(t=>{t.face||(e=!0)}),e}canMoveFreely(){return this.type===r.Manifold}isSkeletonConstrained(){return this.type===r.OpenBook}isPositionFixed(){return this.type===r.SkeletonBranching||this.type===r.NonManifoldOther}isOnSkeleton(){return this.type!==r.Manifold}}class h{constructor(e,t,n){this.id=e,this.twin=null,this.next=null,this.prev=null,this.face=null,this.vertex=t,this.edge=n}getSourceVertex(){var e;return(null==(e=this.twin)?void 0:e.vertex)??null}getTargetVertex(){return this.vertex}isBoundary(){return null===this.face}getOppositeHalfedge(){var e;return(null==(e=this.next)?void 0:e.next)??null}getOppositeVertex(){var e;return(null==(e=this.next)?void 0:e.vertex)??null}getVector(){const e=this.getSourceVertex();return e?{x:this.vertex.position.x-e.position.x,y:this.vertex.position.y-e.position.y,z:this.vertex.position.z-e.position.z}:null}}class u{constructor(e,t,n){this.id=e,this.allHalfedges=[],this.type=a.Manifold,this.isInPath=!1,this.halfedge=t,this.length=n,this.allHalfedges.push(t)}addHalfedge(e){this.allHalfedges.push(e),this.updateType()}getHalfedgeCount(){return this.allHalfedges.length}updateType(){const e=this.getFaceCount();0===e||1===e?this.type=a.Boundary:2===e?this.type!==a.Feature&&(this.type=a.Manifold):this.type=a.NonManifold}getFaceCount(){let e=0;for(const t of this.allHalfedges)null!==t.face&&e++;return e}getVertices(){const e=this.halfedge.getSourceVertex(),t=this.halfedge.getTargetVertex();return e?[e,t]:[null,null]}getFaces(){const e=[];for(const t of this.allHalfedges)null!==t.face&&e.push(t.face);return e}getTwoFaces(){var e;return[this.halfedge.face,(null==(e=this.halfedge.twin)?void 0:e.face)??null]}isBoundary(){return this.type===a.Boundary}isNonManifold(){return this.type===a.NonManifold}isSkeletonEdge(){return this.type===a.NonManifold||this.type===a.Feature||this.type===a.Boundary}canFlip(){if(this.type!==a.Manifold)return!1;if(2!==this.getFaceCount())return!1;const[e,t]=this.getVertices();if(!e||!t)return!1;const n=e.degree(),i=t.degree();return!(null===n||null===i||n<=1||i<=1)}getOtherVertex(e){const[t,n]=this.getVertices();return t&&n?e.id===t.id?n:e.id===n.id?t:null:null}markAsFeature(){this.type===a.Manifold&&(this.type=a.Feature)}}class g{constructor(e,t){this.id=e,this.isMarked=!1,this.halfedge=t}getVertices(){const e=this.halfedge,t=e.next,n=null==t?void 0:t.next;return t&&n?[e.vertex,t.vertex,n.vertex]:null}getHalfedges(){const e=this.halfedge,t=e.next,n=null==t?void 0:t.next;return t&&n?[e,t,n]:null}forEachHalfedge(e){const t=this.getHalfedges();if(t)for(const n of t)e(n)}forEachVertex(e){const t=this.getVertices();if(t)for(const n of t)e(n)}getCentroid(){const e=this.getVertices();if(!e)return null;const[t,n,i]=e;return{x:(t.position.x+n.position.x+i.position.x)/3,y:(t.position.y+n.position.y+i.position.y)/3,z:(t.position.z+n.position.z+i.position.z)/3}}getNormal(){const e=this.getVertices();if(!e)return null;const[t,n,i]=e,s=n.position.x-t.position.x,o=n.position.y-t.position.y,r=n.position.z-t.position.z,a=i.position.x-t.position.x,l=i.position.y-t.position.y,c=i.position.z-t.position.z,h=o*c-r*l,u=r*a-s*c,g=s*l-o*a,d=Math.sqrt(h*h+u*u+g*g);return d<1e-10?{x:0,y:0,z:1}:{x:h/d,y:u/d,z:g/d}}getArea(){const e=this.getVertices();if(!e)return null;const[t,n,i]=e,s=n.position.x-t.position.x,o=n.position.y-t.position.y,r=n.position.z-t.position.z,a=i.position.x-t.position.x,l=i.position.y-t.position.y,c=i.position.z-t.position.z,h=o*c-r*l,u=r*a-s*c,g=s*l-o*a;return Math.sqrt(h*h+u*u+g*g)/2}getQuality(){const e=this.getVertices();if(!e)return null;const[t,n,i]=e,s=Math.sqrt(Math.pow(n.position.x-i.position.x,2)+Math.pow(n.position.y-i.position.y,2)+Math.pow(n.position.z-i.position.z,2)),o=Math.sqrt(Math.pow(t.position.x-i.position.x,2)+Math.pow(t.position.y-i.position.y,2)+Math.pow(t.position.z-i.position.z,2)),r=Math.sqrt(Math.pow(t.position.x-n.position.x,2)+Math.pow(t.position.y-n.position.y,2)+Math.pow(t.position.z-n.position.z,2)),a=(s+o+r)/2,l=a*(a-s)*(a-o)*(a-r);if(l<=0)return 0;const c=Math.sqrt(l),h=s*o*r/(4*c);if(h<1e-10)return 0;const u=2*(c/a)/h;return Math.max(0,Math.min(1,u))}containsVertex(e){const t=this.getVertices();return!!t&&t.some(t=>t.id===e.id)}getOppositeHalfedge(e){const t=this.getHalfedges();if(!t)return null;for(const n of t){const t=n.getSourceVertex();if(t&&t.id!==e.id&&n.vertex.id!==e.id)return n}return null}isDegenerate(e=1e-10){const t=this.getArea();return null===t||t<e}}class d{constructor(){this.vertices=new Map,this.edges=new Map,this.halfedges=new Map,this.faces=new Map,this.nextVertexId=0,this.nextEdgeId=0,this.nextHalfedgeId=0,this.nextFaceId=0,this.edgeMap=new Map}static fromBufferGeometry(n,o){const r=new d,a=n.attributes.position;if(!a)throw new Error("Geometry must have a position attribute");const l=n.index;if(!l)throw new Error("Geometry must be indexed");const u=a.count,f=l.count/3;if(l.count%3!=0)throw new Error("Geometry must be triangulated (indices count must be divisible by 3)");for(let i=0;i<u;i++){const n=a.getX(i),s=a.getY(i),o=a.getZ(i),l=new c(t(r.nextVertexId++),new e.Vector3(n,s,o));r.vertices.set(l.id,l)}for(let e=0;e<f;e++){const n=l.getX(3*e+0),o=l.getX(3*e+1),a=l.getX(3*e+2),c=r.vertices.get(t(n)),u=r.vertices.get(t(o)),d=r.vertices.get(t(a));if(!c||!u||!d)throw new Error(`Vertex not found in face ${e}: ${n}, ${o}, ${a}`);const f=r.getOrCreateEdge(n,o),p=r.getOrCreateEdge(o,a),x=r.getOrCreateEdge(a,n),y=new g(s(r.nextFaceId++),null);r.faces.set(y.id,y);const m=new h(i(r.nextHalfedgeId++),u,f),v=new h(i(r.nextHalfedgeId++),d,p),z=new h(i(r.nextHalfedgeId++),c,x);r.halfedges.set(m.id,m),r.halfedges.set(v.id,v),r.halfedges.set(z.id,z),f.addHalfedge(m),p.addHalfedge(v),x.addHalfedge(z),m.next=v,v.next=z,z.next=m,m.prev=z,v.prev=m,z.prev=v,m.face=y,v.face=y,z.face=y,y.halfedge=m,c.halfedge||(c.halfedge=m),u.halfedge||(u.halfedge=v),d.halfedge||(d.halfedge=z),f.halfedge=m,p.halfedge=v,x.halfedge=z}return r.setupTwinHalfedges(),o&&o.length>0&&r.markFeatureEdges(o),r.classifyVertices(),r}getOrCreateEdge(e,t){const n=this.makeEdgeKey(e,t);let i=this.edgeMap.get(n);if(!i){const s=this.vertices.get(e),o=this.vertices.get(t);if(!s||!o)throw new Error(`Vertex not found: ${e} or ${t}`);const r=o.position.x-s.position.x,a=o.position.y-s.position.y,l=o.position.z-s.position.z,c=Math.sqrt(r*r+a*a+l*l);i=new u(this.nextEdgeId++,null,c),i.allHalfedges=[],this.edgeMap.set(n,i),this.edges.set(i.id,i)}return i}makeEdgeKey(e,t){return e<t?`${e},${t}`:`${t},${e}`}setupTwinHalfedges(){var e;for(const t of this.edges.values()){const e=t.allHalfedges,n=e.length;0!==n&&(1===n?(e[0].twin=null,t.type=a.Boundary):2===n?(e[0].twin=e[1],e[1].twin=e[0],t.type=a.Manifold):(this.setupNonManifoldTwins(t,e),t.type=a.NonManifold))}for(const t of this.halfedges.values())if(null!==t.twin){const n=null==(e=t.prev)?void 0:e.vertex;n&&(n.halfedge=t)}}setupNonManifoldTwins(e,t){const[n,i]=e.getVertices();if(!n||!i)return;const s=[],o=[];for(const a of t)a.vertex.id===n.id?s.push(a):o.push(a);const r=Math.min(s.length,o.length);for(let a=0;a<r;a++)s[a].twin=o[a],o[a].twin=s[a];for(let a=r;a<s.length;a++)s[a].twin=null;for(let a=r;a<o.length;a++)o[a].twin=null}markFeatureEdges(e){for(const[t,n]of e){const e=this.makeEdgeKey(t,n),i=this.edgeMap.get(e);i&&i.type===a.Manifold&&i.markAsFeature()}}classifyVertices(){for(const e of this.vertices.values())e.type=this.classifyVertex(e)}classifyVertex(e){let t=0;return e.forEachOutgoingHalfedge(e=>{e.edge.isSkeletonEdge()&&t++}),0===t?r.Manifold:2===t?r.OpenBook:r.SkeletonBranching}toBufferGeometry(){return new e.Vector3.constructor}getVertices(){return Array.from(this.vertices.values())}getEdges(){return Array.from(this.edges.values())}getFaces(){return Array.from(this.faces.values())}getHalfedges(){return Array.from(this.halfedges.values())}get vertexCount(){return this.vertices.size}get edgeCount(){return this.edges.size}get faceCount(){return this.faces.size}get halfedgeCount(){return this.halfedges.size}getNonManifoldEdges(){return this.getEdges().filter(e=>e.type===a.NonManifold)}getBoundaryEdges(){return this.getEdges().filter(e=>e.type===a.Boundary)}getFeatureEdges(){return this.getEdges().filter(e=>e.type===a.Feature)}getSkeletonEdges(){return this.getEdges().filter(e=>e.isSkeletonEdge())}isManifold(){return 0===this.getNonManifoldEdges().length}hasBoundary(){return this.getBoundaryEdges().length>0}getVertex(e){return this.vertices.get(e)}getEdge(e){return this.edges.get(e)}getFace(e){return this.faces.get(e)}getHalfedge(e){return this.halfedges.get(e)}getEdgeBetween(e,t){const n=this.makeEdgeKey(e.id,t.id);return this.edgeMap.get(n)}createVertex(e){const t=new c(this.nextVertexId++,e);return this.vertices.set(t.id,t),t}createFace(e,t,n){const i=this.getOrCreateEdge(e.id,t.id),s=this.getOrCreateEdge(t.id,n.id),o=this.getOrCreateEdge(n.id,e.id),r=new g(this.nextFaceId++,null);this.faces.set(r.id,r);const a=new h(this.nextHalfedgeId++,t,i),l=new h(this.nextHalfedgeId++,n,s),c=new h(this.nextHalfedgeId++,e,o);return this.halfedges.set(a.id,a),this.halfedges.set(l.id,l),this.halfedges.set(c.id,c),i.addHalfedge(a),s.addHalfedge(l),o.addHalfedge(c),a.next=l,l.next=c,c.next=a,a.prev=c,l.prev=a,c.prev=l,a.face=r,l.face=r,c.face=r,r.halfedge=a,e.halfedge||(e.halfedge=a),t.halfedge||(t.halfedge=l),n.halfedge||(n.halfedge=c),i.halfedge=a,s.halfedge=l,o.halfedge=c,r}getStats(){const e=this.getNonManifoldEdges().length,t=this.getBoundaryEdges().length,n=this.getFeatureEdges().length,i=this.vertexCount-this.edgeCount+this.faceCount;return{vertexCount:this.vertexCount,edgeCount:this.edgeCount,faceCount:this.faceCount,nonManifoldEdgeCount:e,boundaryEdgeCount:t,featureEdgeCount:n,eulerCharacteristic:i}}}function f(e,t){const n=t.x-e.x,i=t.y-e.y,s=t.z-e.z;return n*n+i*i+s*s}function p(e,t){return Math.sqrt(f(e,t))}function x(e,t){return e.x*t.x+e.y*t.y+e.z*t.z}function y(e,t){return{x:e.y*t.z-e.z*t.y,y:e.z*t.x-e.x*t.z,z:e.x*t.y-e.y*t.x}}function m(e){return Math.sqrt(e.x*e.x+e.y*e.y+e.z*e.z)}function v(e){return e.x*e.x+e.y*e.y+e.z*e.z}function z(e){const t=m(e);return t<1e-10?{x:0,y:0,z:0}:{x:e.x/t,y:e.y/t,z:e.z/t}}function M(e,t){return{x:e.x+t.x,y:e.y+t.y,z:e.z+t.z}}function E(e,t){return{x:e.x-t.x,y:e.y-t.y,z:e.z-t.z}}function w(e,t){return{x:e.x*t,y:e.y*t,z:e.z*t}}function V(e,t,n){return{x:e.x+(t.x-e.x)*n,y:e.y+(t.y-e.y)*n,z:e.z+(t.z-e.z)*n}}function k(e,t){return{x:(e.x+t.x)/2,y:(e.y+t.y)/2,z:(e.z+t.z)/2}}function C(e,t){const n=m(e),i=m(t);if(n<1e-10||i<1e-10)return 0;const s=x(e,t)/(n*i);return Math.acos(Math.max(-1,Math.min(1,s)))}function S(e,t,n){const i=E(n,t),s=v(i);if(s<1e-10)return{...t};return M(t,w(i,Math.max(0,Math.min(1,x(E(e,t),i)/s))))}function b(e,t,n){return m(y(E(t,e),E(n,e)))/2}function B(e,t,n){return z(y(E(t,e),E(n,e)))}function F(e,t,n){const i=p(t,n),s=p(e,n),o=p(e,t),r=b(e,t,n);return r<1e-10?null:i*s*o/(4*r)}function _(e,t,n){const i=(p(t,n)+p(e,n)+p(e,t))/2,s=b(e,t,n);return i<1e-10?null:s/i}function H(e,t,n,i){const s=z(M(B(e,t,n),B(e,i,t)));let o;o=Math.abs(s.x)<.9?z(y(s,{x:1,y:0,z:0})):z(y(s,{x:0,y:1,z:0}));const r=y(s,o),a=e=>({x:x(e,o),y:x(e,r)}),l=a(e),c=a(t),h=a(n),u=a(i),g=(e,t)=>e.x*t.y-e.y*t.x,d={x:c.x-l.x,y:c.y-l.y},f={x:h.x-l.x,y:h.y-l.y},p={x:u.x-l.x,y:u.y-l.y};if(g(d,f)*g(d,p)>=0)return!1;const m={x:u.x-h.x,y:u.y-h.y},v={x:l.x-h.x,y:l.y-h.y},E={x:c.x-h.x,y:c.y-h.y};return g(m,v)*g(m,E)<0}class I{constructor(e){this.id=e,this.vertices=[],this.edges=[],this.isClosed=!1,this._totalLength=0,this._cumulativeLengths=[]}get startVertex(){return this.vertices[0]}get endVertex(){return this.vertices[this.vertices.length-1]}get totalLength(){return this._totalLength}get vertexCount(){return this.vertices.length}get edgeCount(){return this.edges.length}addVertex(e){if(this.vertices.length>0){const t=this.vertices[this.vertices.length-1],n=p({x:t.position.x,y:t.position.y,z:t.position.z},{x:e.position.x,y:e.position.y,z:e.position.z});this._totalLength+=n}this._cumulativeLengths.push(this._totalLength),this.vertices.push(e)}addEdge(e){this.edges.push(e)}recomputeLengths(){this._totalLength=0,this._cumulativeLengths=[0];for(let e=1;e<this.vertices.length;e++){const t=this.vertices[e-1],n=this.vertices[e],i=p({x:t.position.x,y:t.position.y,z:t.position.z},{x:n.position.x,y:n.position.y,z:n.position.z});this._totalLength+=i,this._cumulativeLengths.push(this._totalLength)}}getParameterAtVertex(e){return 0===this._totalLength||e<0||e>=this.vertices.length?0:(this._cumulativeLengths[e]??0)/this._totalLength}getPositionAt(e){if(0===this.vertices.length)return null;if(1===this.vertices.length){const e=this.vertices[0];return{x:e.position.x,y:e.position.y,z:e.position.z}}const t=(e=Math.max(0,Math.min(1,e)))*this._totalLength;for(let i=1;i<this.vertices.length;i++){const e=this._cumulativeLengths[i-1]??0,n=this._cumulativeLengths[i]??0;if(t<=n){const s=this.vertices[i-1],o=this.vertices[i],r=n-e;if(r<1e-10)return{x:s.position.x,y:s.position.y,z:s.position.z};const a=(t-e)/r;return V({x:s.position.x,y:s.position.y,z:s.position.z},{x:o.position.x,y:o.position.y,z:o.position.z},a)}}const n=this.vertices[this.vertices.length-1];return{x:n.position.x,y:n.position.y,z:n.position.z}}projectPoint(e){if(0===this.vertices.length)return null;if(1===this.vertices.length){const t=this.vertices[0],n={x:t.position.x,y:t.position.y,z:t.position.z};return{point:n,parameter:0,distance:p(e,n)}}let t=null,n=0,i=1/0;for(let s=1;s<this.vertices.length;s++){const o=this.vertices[s-1],r=this.vertices[s],a={x:o.position.x,y:o.position.y,z:o.position.z},l=S(e,a,{x:r.position.x,y:r.position.y,z:r.position.z}),c=p(e,l);if(c<i){i=c,t=l;const e=this._cumulativeLengths[s-1]??0;if((this._cumulativeLengths[s]??0)-e<1e-10)n=e/this._totalLength;else{n=(e+p(a,l))/this._totalLength}}}return t?{point:t,parameter:n,distance:i}:null}getVertex(e){return this.vertices[e]}getEdge(e){return this.edges[e]}containsVertex(e){return this.vertices.some(t=>t.id===e.id)}containsEdge(e){return this.edges.some(t=>t.id===e.id)}indexOfVertex(e){return this.vertices.findIndex(t=>t.id===e.id)}forEachVertex(e){this.vertices.forEach(e)}forEachEdge(e){this.edges.forEach(e)}clone(e){const t=new I(e);return t.vertices=[...this.vertices],t.edges=[...this.edges],t.isClosed=this.isClosed,t._totalLength=this._totalLength,t._cumulativeLengths=[...this._cumulativeLengths],t}}class O{constructor(e){this.nextSegmentId=0,this.mesh=e}build(){const e=this.mesh.getSkeletonEdges(),t=[],n=[];for(const i of this.mesh.getVertices())i.type===r.SkeletonBranching?t.push(i):i.type===r.OpenBook&&n.push(i);return{segments:this.buildSegments(e,t),skeletonEdges:e,branchingVertices:t,openBookVertices:n}}buildSegments(e,t){const n=[],i=new Set,s=new Set(t.map(e=>e.id));for(const o of t){const t=this.getIncidentSkeletonEdges(o,e);for(const e of t){if(i.has(e.id))continue;const t=this.traceSegment(o,e,s,i);t&&n.push(t)}}for(const o of e){if(i.has(o.id))continue;const e=this.traceClosedLoop(o,i);e&&n.push(e)}return n}getIncidentSkeletonEdges(e,t){const n=new Set(t.map(e=>e.id)),i=[];return e.forEachOutgoingHalfedge(e=>{n.has(e.edge.id)&&(i.some(t=>t.id===e.edge.id)||i.push(e.edge))}),i}traceSegment(e,t,n,i){var s,o;const r=new I(this.createSegmentId());r.addVertex(e);let a=e,l=t;for(;l&&!i.has(l.id);){i.add(l.id),r.addEdge(l);const e=l.getOtherVertex(a);if(!e)break;if(r.addVertex(e),a=e,n.has(a.id))break;l=this.getNextSkeletonEdge(a,l,i)}return r.vertices.length>2&&(null==(s=r.startVertex)?void 0:s.id)===(null==(o=r.endVertex)?void 0:o.id)&&(r.isClosed=!0,r.vertices.pop()),r.recomputeLengths(),r}traceClosedLoop(e,t){const[n,i]=e.getVertices();if(!n||!i)return null;const s=new I(this.createSegmentId());s.addVertex(n),s.addEdge(e),s.addVertex(i),t.add(e.id);let o=i,r=this.getNextSkeletonEdge(o,e,t);for(;r&&!t.has(r.id);){t.add(r.id),s.addEdge(r);const e=r.getOtherVertex(o);if(!e)break;if(e.id===n.id){s.isClosed=!0;break}s.addVertex(e),o=e,r=this.getNextSkeletonEdge(o,r,t)}return s.recomputeLengths(),s}getNextSkeletonEdge(e,t,n){let i=null;return e.forEachOutgoingHalfedge(e=>{e.edge.id!==t.id&&e.edge.isSkeletonEdge()&&!n.has(e.edge.id)&&(i=e.edge)}),i}createSegmentId(){return this.nextSegmentId++}}class N{constructor(e){this.segments=new Map,this.skeletonEdges=[],this.branchingVertices=[],this.openBookVertices=[],this.vertexToSegment=new Map,this.isBuilt=!1,this.mesh=e}build(){const e=new O(this.mesh).build();this.applyBuildResult(e),this.isBuilt=!0}applyBuildResult(e){this.segments.clear(),this.vertexToSegment.clear();for(const t of e.segments){this.segments.set(t.id,t);for(let e=1;e<t.vertices.length-1;e++){const n=t.vertices[e];n&&this.vertexToSegment.set(n.id,t)}}this.skeletonEdges=e.skeletonEdges,this.branchingVertices=e.branchingVertices,this.openBookVertices=e.openBookVertices}rebuild(){this.build()}getSegments(){return Array.from(this.segments.values())}getSegment(e){return this.segments.get(e)}getSegmentForVertex(e){return this.vertexToSegment.get(e.id)}getSkeletonEdges(){return this.skeletonEdges}getBranchingVertices(){return this.branchingVertices}getOpenBookVertices(){return this.openBookVertices}get segmentCount(){return this.segments.size}get skeletonEdgeCount(){return this.skeletonEdges.length}get branchingVertexCount(){return this.branchingVertices.length}get openBookVertexCount(){return this.openBookVertices.length}get built(){return this.isBuilt}projectPoint(e){let t=null;for(const n of this.segments.values()){const i=n.projectPoint(e);i&&(!t||i.distance<t.distance)&&(t={point:i.point,segment:n,parameter:i.parameter,distance:i.distance})}return t}isVertexOnSkeleton(e){return e.isOnSkeleton()}isEdgeOnSkeleton(e){return e.isSkeletonEdge()}getAllSkeletonVertices(){return[...this.branchingVertices,...this.openBookVertices]}getTotalLength(){let e=0;for(const t of this.segments.values())e+=t.totalLength;return e}getStats(){let e=0;for(const t of this.segments.values())t.isClosed&&e++;return{segmentCount:this.segmentCount,skeletonEdgeCount:this.skeletonEdgeCount,branchingVertexCount:this.branchingVertexCount,openBookVertexCount:this.openBookVertexCount,totalLength:this.getTotalLength(),closedLoopCount:e}}}function L(e){const t=new N(e);return t.build(),t}class ${constructor(e){this.skeleton=e}constrainPosition(e,t){switch(e.type){case r.Manifold:return{position:t,wasConstrained:!1,constraintDistance:0};case r.OpenBook:return this.constrainToSegment(e,t);case r.SkeletonBranching:case r.NonManifoldOther:return{position:{x:e.position.x,y:e.position.y,z:e.position.z},wasConstrained:!0,constraintDistance:p(t,{x:e.position.x,y:e.position.y,z:e.position.z})};default:return{position:t,wasConstrained:!1,constraintDistance:0}}}constrainToSegment(e,t){const n=this.skeleton.getSegmentForVertex(e);if(!n){const n=this.skeleton.projectPoint(t);return n?{position:n.point,wasConstrained:!0,segment:n.segment,constraintDistance:n.distance}:{position:{x:e.position.x,y:e.position.y,z:e.position.z},wasConstrained:!0,constraintDistance:p(t,{x:e.position.x,y:e.position.y,z:e.position.z})}}const i=n.projectPoint(t);return i?{position:i.point,wasConstrained:!0,segment:n,constraintDistance:i.distance}:{position:{x:e.position.x,y:e.position.y,z:e.position.z},wasConstrained:!0,segment:n,constraintDistance:0}}canMoveFreely(e){return e.type===r.Manifold}isFixed(e){return e.type===r.SkeletonBranching||e.type===r.NonManifoldOther}isConstrainedToSegment(e){return e.type===r.OpenBook}getConstraintSegment(e){if(e.type===r.OpenBook)return this.skeleton.getSegmentForVertex(e)}getAllowedDirection(e){if(e.type!==r.OpenBook)return null;const t=this.skeleton.getSegmentForVertex(e);if(!t||t.vertices.length<2)return null;const n=t.indexOfVertex(e);if(n<0)return null;let i,s;if(0===n){const e=t.vertices[0],n=t.vertices[1];i={x:e.position.x,y:e.position.y,z:e.position.z},s={x:n.position.x,y:n.position.y,z:n.position.z}}else if(n===t.vertices.length-1){const e=t.vertices[n-1],o=t.vertices[n];i={x:e.position.x,y:e.position.y,z:e.position.z},s={x:o.position.x,y:o.position.y,z:o.position.z}}else{const e=t.vertices[n-1],o=t.vertices[n+1];i={x:e.position.x,y:e.position.y,z:e.position.z},s={x:o.position.x,y:o.position.y,z:o.position.z}}const o=s.x-i.x,a=s.y-i.y,l=s.z-i.z,c=Math.sqrt(o*o+a*a+l*l);return c<1e-10?null:{x:o/c,y:a/c,z:l/c}}}function A(e){return new $(e)}function q(e){const t=[],n=[],i=e.attributes.position;if(!i)return t.push("Geometry must have a position attribute"),{isValid:!1,errors:t,warnings:n};const s=e.index;if(!s)return t.push("Geometry must be indexed"),{isValid:!1,errors:t,warnings:n};s.count%3!=0&&t.push(`Index count (${s.count}) must be divisible by 3 for triangle mesh`);const o=i.count;for(let c=0;c<s.count;c++){const e=s.getX(c);if(e<0||e>=o){t.push(`Invalid index ${e} at position ${c} (vertex count: ${o})`);break}}let r=0;const a=s.count/3;for(let c=0;c<a;c++){const e=s.getX(3*c),t=s.getX(3*c+1),n=s.getX(3*c+2);e!==t&&t!==n&&n!==e||r++}r>0&&n.push(`Found ${r} degenerate triangle(s) with repeated vertices`);let l=0;for(let c=0;c<o;c++){const e=i.getX(c),t=i.getY(c),n=i.getZ(c);isFinite(e)&&isFinite(t)&&isFinite(n)||l++}return l>0&&t.push(`Found ${l} vertex position(s) with NaN or Infinity values`),{isValid:0===t.length,errors:t,warnings:n}}function P(e,t={}){const{featureEdges:n,validate:i=!0}=t;if(i){const t=q(e);if(!t.isValid)throw new Error(`Invalid geometry: ${t.errors.join("; ")}`)}return d.fromBufferGeometry(e,n)}function T(t,n={}){const{computeNormals:i=!0,smoothNormals:s=!0}=n,o=new e.BufferGeometry,r=t.getFaces();if(0===r.length)return o;const a=t.getVertices(),l=new Map;a.forEach((e,t)=>{l.set(e.id,t)});const c=new Float32Array(3*a.length);a.forEach((e,t)=>{c[3*t]=e.position.x,c[3*t+1]=e.position.y,c[3*t+2]=e.position.z});const h=[];for(const e of r){const t=e.getVertices();if(!t)continue;const[n,i,s]=t,o=l.get(n.id),r=l.get(i.id),a=l.get(s.id);void 0!==o&&void 0!==r&&void 0!==a&&h.push(o,r,a)}return o.setAttribute("position",new e.BufferAttribute(c,3)),o.setIndex(h),i&&(s?function(t,n,i){const s=n.getVertices(),o=new Float32Array(3*s.length),r=new Uint32Array(s.length);for(const e of n.getFaces()){const t=e.getNormal();if(!t)continue;const n=e.getVertices();if(n)for(const e of n){const n=i.get(e.id);if(void 0===n)continue;const s=3*n;o[s]=(o[s]??0)+t.x,o[s+1]=(o[s+1]??0)+t.y,o[s+2]=(o[s+2]??0)+t.z,r[n]=(r[n]??0)+1}}for(let e=0;e<s.length;e++){const t=r[e]??0;if(0===t)continue;const n=3*e,i=(o[n]??0)/t,s=(o[n+1]??0)/t,a=(o[n+2]??0)/t,l=Math.sqrt(i*i+s*s+a*a);l>1e-10?(o[3*e]=i/l,o[3*e+1]=s/l,o[3*e+2]=a/l):(o[3*e]=0,o[3*e+1]=1,o[3*e+2]=0)}t.setAttribute("normal",new e.BufferAttribute(o,3))}(o,t,l):o.computeVertexNormals()),o}function G(t){const n=new e.BufferGeometry,i=t.getSkeletonEdges();if(0===i.length)return n;const s=new Float32Array(6*i.length);let o=0;for(const e of i){const[t,n]=e.getVertices();t&&n&&(s[o++]=t.position.x,s[o++]=t.position.y,s[o++]=t.position.z,s[o++]=n.position.x,s[o++]=n.position.y,s[o++]=n.position.z)}return n.setAttribute("position",new e.BufferAttribute(s.slice(0,o),3)),n}function Q(t){const n=T(t,{computeNormals:!0}),i=t.getVertices(),s=new Float32Array(3*i.length),o=new Map;i.forEach((e,t)=>{o.set(e.id,t)});for(const e of i){const t=o.get(e.id);if(void 0===t)continue;let n=0,i=0,r=0;switch(e.type){case"manifold":n=.2,i=.8,r=.2;break;case"open_book":n=.2,i=.4,r=.9;break;case"skeleton_branching":n=.9,i=.2,r=.2;break;case"non_manifold_other":n=.9,i=.2,r=.9}s[3*t]=n,s[3*t+1]=i,s[3*t+2]=r}return n.setAttribute("color",new e.BufferAttribute(s,3)),n}function R(t){const n=t.getFaces();if(0===n.length)return new e.BufferGeometry;const i=new Float32Array(9*n.length),s=new Float32Array(9*n.length),o=new Float32Array(9*n.length);let r=0;for(const e of n){const t=e.getVertices(),n=e.getNormal(),a=e.getQuality()??0;if(!t||!n)continue;const l=a<.5?1:2-2*a,c=a<.5?2*a:1,h=.1;for(const e of t)i[r]=e.position.x,i[r+1]=e.position.y,i[r+2]=e.position.z,s[r]=l,s[r+1]=c,s[r+2]=h,o[r]=n.x,o[r+1]=n.y,o[r+2]=n.z,r+=3}const a=new e.BufferGeometry;return a.setAttribute("position",new e.BufferAttribute(i.slice(0,r),3)),a.setAttribute("color",new e.BufferAttribute(s.slice(0,r),3)),a.setAttribute("normal",new e.BufferAttribute(o.slice(0,r),3)),a}class D{constructor(e){if(this.cells=new Map,this.itemPositions=new Map,e<=0)throw new Error("Cell size must be positive");this.cellSize=e}getCellKey(e,t,n){return`${Math.floor(e/this.cellSize)},${Math.floor(t/this.cellSize)},${Math.floor(n/this.cellSize)}`}getCellIndices(e,t,n){return[Math.floor(e/this.cellSize),Math.floor(t/this.cellSize),Math.floor(n/this.cellSize)]}insert(e,t){const n=this.getCellKey(t.x,t.y,t.z);let i=this.cells.get(n);i||(i=[],this.cells.set(n,i)),i.push(e),this.itemPositions.set(e,t)}remove(e){const t=this.itemPositions.get(e);if(!t)return!1;const n=this.getCellKey(t.x,t.y,t.z),i=this.cells.get(n);if(i){const t=i.indexOf(e);-1!==t&&(i.splice(t,1),0===i.length&&this.cells.delete(n))}return this.itemPositions.delete(e),!0}update(e,t){this.remove(e),this.insert(e,t)}queryRadius(e,t){const n=[],i=t*t,s=this.getCellIndices(e.x-t,e.y-t,e.z-t),o=this.getCellIndices(e.x+t,e.y+t,e.z+t);for(let r=s[0];r<=o[0];r++)for(let t=s[1];t<=o[1];t++)for(let a=s[2];a<=o[2];a++){const s=`${r},${t},${a}`,o=this.cells.get(s);if(o)for(const t of o){const s=this.itemPositions.get(t);if(s){const o=s.x-e.x,r=s.y-e.y,a=s.z-e.z;o*o+r*r+a*a<=i&&n.push(t)}}}return n}queryKNearest(e,t,n){let i=this.cellSize,s=[];for(;s.length<t&&!(void 0!==n&&i>n);){s=[];const t=this.queryRadius(e,i);for(const n of t){const t=this.itemPositions.get(n);if(t){const i=t.x-e.x,o=t.y-e.y,r=t.z-e.z;s.push({item:n,distSq:i*i+o*o+r*r})}}i*=2}return s.sort((e,t)=>e.distSq-t.distSq),s.slice(0,t).map(e=>e.item)}clear(){this.cells.clear(),this.itemPositions.clear()}get size(){return this.itemPositions.size}get cellCount(){return this.cells.size}getPosition(e){return this.itemPositions.get(e)}has(e){return this.itemPositions.has(e)}*[Symbol.iterator](){for(const e of this.itemPositions.keys())yield e}getAll(){return Array.from(this.itemPositions.keys())}}function j(e){return{min:{x:Math.min(e.v0.x,e.v1.x,e.v2.x),y:Math.min(e.v0.y,e.v1.y,e.v2.y),z:Math.min(e.v0.z,e.v1.z,e.v2.z)},max:{x:Math.max(e.v0.x,e.v1.x,e.v2.x),y:Math.max(e.v0.y,e.v1.y,e.v2.y),z:Math.max(e.v0.z,e.v1.z,e.v2.z)}}}function X(e,t){return{min:{x:Math.min(e.min.x,t.min.x),y:Math.min(e.min.y,t.min.y),z:Math.min(e.min.z,t.min.z)},max:{x:Math.max(e.max.x,t.max.x),y:Math.max(e.max.y,t.max.y),z:Math.max(e.max.z,t.max.z)}}}function K(e,t){let n=0;return e.x<t.min.x?n+=(t.min.x-e.x)**2:e.x>t.max.x&&(n+=(e.x-t.max.x)**2),e.y<t.min.y?n+=(t.min.y-e.y)**2:e.y>t.max.y&&(n+=(e.y-t.max.y)**2),e.z<t.min.z?n+=(t.min.z-e.z)**2:e.z>t.max.z&&(n+=(e.z-t.max.z)**2),n}function U(e,t){const n=t.v0,i=t.v1,s=t.v2,o=i.x-n.x,r=i.y-n.y,a=i.z-n.z,l=s.x-n.x,c=s.y-n.y,h=s.z-n.z,u=e.x-n.x,g=e.y-n.y,d=e.z-n.z,f=o*u+r*g+a*d,p=l*u+c*g+h*d;if(f<=0&&p<=0)return n;const x=e.x-i.x,y=e.y-i.y,m=e.z-i.z,v=o*x+r*y+a*m,z=l*x+c*y+h*m;if(v>=0&&z<=v)return i;const M=f*z-v*p;if(M<=0&&f>=0&&v<=0){const e=f/(f-v);return{x:n.x+e*o,y:n.y+e*r,z:n.z+e*a}}const E=e.x-s.x,w=e.y-s.y,V=e.z-s.z,k=o*E+r*w+a*V,C=l*E+c*w+h*V;if(C>=0&&k<=C)return s;const S=k*p-f*C;if(S<=0&&p>=0&&C<=0){const e=p/(p-C);return{x:n.x+e*l,y:n.y+e*c,z:n.z+e*h}}const b=v*C-k*z;if(b<=0&&z-v>=0&&k-C>=0){const e=(z-v)/(z-v+(k-C));return{x:i.x+e*(s.x-i.x),y:i.y+e*(s.y-i.y),z:i.z+e*(s.z-i.z)}}const B=1/(b+S+M),F=S*B,_=M*B;return{x:n.x+o*F+l*_,y:n.y+r*F+c*_,z:n.z+a*F+h*_}}class Y{constructor(e=4){this.root=null,this.triangles=[],this.maxLeafSize=e}build(e){if(this.triangles=e,0===e.length)return void(this.root=null);const t=e.map((e,t)=>t);this.root=this.buildNode(t)}buildNode(e){let t=j(this.triangles[e[0]]);for(let h=1;h<e.length;h++)t=X(t,j(this.triangles[e[h]]));if(e.length<=this.maxLeafSize)return{bounds:t,triangleIndices:e};const n=t.max.x-t.min.x,i=t.max.y-t.min.y,s=t.max.z-t.min.z;let o="x";i>n&&i>s?o="y":s>n&&s>i&&(o="z");const r=e.map(e=>{return{index:e,centroid:(t=this.triangles[e],{x:(t.v0.x+t.v1.x+t.v2.x)/3,y:(t.v0.y+t.v1.y+t.v2.y)/3,z:(t.v0.z+t.v1.z+t.v2.z)/3})};var t});r.sort((e,t)=>e.centroid[o]-t.centroid[o]);const a=Math.floor(r.length/2),l=r.slice(0,a).map(e=>e.index),c=r.slice(a).map(e=>e.index);return 0===l.length||0===c.length?{bounds:t,triangleIndices:e}:{bounds:t,left:this.buildNode(l),right:this.buildNode(c)}}closestPoint(e){if(!this.root||0===this.triangles.length)return null;let t=null,n=1/0;const i=[this.root];for(;i.length>0;){const s=i.pop();if(!(K(e,s.bounds)>=n))if(s.triangleIndices)for(const i of s.triangleIndices){const s=U(e,this.triangles[i]),o=f(e,s);o<n&&(n=o,t={point:s,distance:Math.sqrt(o),triangleIndex:i})}else if(s.left&&s.right){K(e,s.left.bounds)<K(e,s.right.bounds)?(i.push(s.right),i.push(s.left)):(i.push(s.left),i.push(s.right))}else s.left?i.push(s.left):s.right&&i.push(s.right)}return t}queryRadius(e,t){const n=[],i=t*t;if(!this.root)return n;const s=[this.root];for(;s.length>0;){const t=s.pop();if(!(K(e,t.bounds)>i))if(t.triangleIndices)for(const s of t.triangleIndices){f(e,U(e,this.triangles[s]))<=i&&n.push(s)}else t.left&&s.push(t.left),t.right&&s.push(t.right)}return n}get triangleCount(){return this.triangles.length}getTriangle(e){return this.triangles[e]}}function Z(e){const t=e.getEdges(),n=e.getVertices();let i=0,s=0,o=0;const l=[];for(const r of t)switch(r.type){case a.Manifold:case a.Feature:i++;break;case a.NonManifold:s++,l.push(W(r));break;case a.Boundary:o++}let c=0,h=0,u=0;const g=[];for(const a of n){u+=a.degree()??0,a.type===r.Manifold?c++:(h++,g.push(J(a)))}const d=n.length>0?u/n.length:0,f=e.vertexCount-e.edgeCount+e.faceCount;return{isManifold:0===s,hasBoundary:o>0,vertexCount:e.vertexCount,edgeCount:e.edgeCount,faceCount:e.faceCount,manifoldEdgeCount:i,nonManifoldEdgeCount:s,boundaryEdgeCount:o,manifoldVertexCount:c,nonManifoldVertexCount:h,nonManifoldEdges:l,nonManifoldVertices:g,eulerCharacteristic:f,averageVertexDegree:d}}function W(e){const[t,n]=e.getVertices();return{edgeId:e.id,vertexIndices:[t?t.id:-1,n?n.id:-1],faceCount:e.getFaceCount(),positions:[t?{x:t.position.x,y:t.position.y,z:t.position.z}:{x:0,y:0,z:0},n?{x:n.position.x,y:n.position.y,z:n.position.z}:{x:0,y:0,z:0}]}}function J(e){let t=0;return e.forEachOutgoingHalfedge(e=>{e.edge.isSkeletonEdge()&&t++}),{vertexId:e.id,position:{x:e.position.x,y:e.position.y,z:e.position.z},type:e.type,skeletonEdgeCount:t}}function ee(e){const t={manifold:0,openBook:0,skeletonBranching:0,nonManifoldOther:0,total:0};for(const n of e.getVertices())switch(n.type=te(n),t.total++,n.type){case r.Manifold:t.manifold++;break;case r.OpenBook:t.openBook++;break;case r.SkeletonBranching:t.skeletonBranching++;break;case r.NonManifoldOther:t.nonManifoldOther++}return t}function te(e){let t=0,n=0,i=!1,s=!1;if(e.forEachOutgoingHalfedge(e=>{n++,e.edge.isSkeletonEdge()&&t++,e.edge.isBoundary()&&(i=!0),e.edge.isNonManifold()&&(s=!0)}),0===n)return r.Manifold;if(s)return 2===t?r.OpenBook:1===t||t>2?r.SkeletonBranching:r.NonManifoldOther;if(i){if(2===t)return r.OpenBook;if(1===t||t>2)return r.SkeletonBranching}return 0===t?r.Manifold:2===t?r.OpenBook:r.SkeletonBranching}function ne(e,t){return e.getVertices().filter(e=>e.type===t)}function ie(e){return ne(e,r.Manifold)}function se(e){return e.getVertices().filter(e=>e.type!==r.Manifold)}function oe(e){e.classifyVertices()}function re(e){const t=[],n=[];return function(e,t){for(const n of e.getVertices())n.halfedge&&(e.getHalfedge(n.halfedge.id)||t.push({type:"invalid_vertex_halfedge",message:`Vertex ${n.id} references non-existent halfedge ${n.halfedge.id}`,elementIds:[n.id]})),isFinite(n.position.x)&&isFinite(n.position.y)&&isFinite(n.position.z)||t.push({type:"invalid_vertex_position",message:`Vertex ${n.id} has invalid position`,elementIds:[n.id]})}(e,t),function(e,t,n){for(const i of e.getEdges())if(0!==i.allHalfedges.length){e.getHalfedge(i.halfedge.id)||t.push({type:"invalid_edge_halfedge",message:`Edge ${i.id} references non-existent halfedge`,elementIds:[i.id]}),(i.length<=0||!isFinite(i.length))&&n.push({type:"invalid_edge_length",message:`Edge ${i.id} has invalid length: ${i.length}`,elementIds:[i.id]});for(const e of i.allHalfedges)e.edge.id!==i.id&&t.push({type:"halfedge_edge_mismatch",message:`Halfedge ${e.id} in edge ${i.id} references different edge ${e.edge.id}`,elementIds:[i.id,e.id]})}else t.push({type:"edge_no_halfedges",message:`Edge ${i.id} has no halfedges`,elementIds:[i.id]})}(e,t,n),function(e,t,n){var i;for(const s of e.getFaces()){if(!s.halfedge){t.push({type:"face_no_halfedge",message:`Face ${s.id} has no halfedge`,elementIds:[s.id]});continue}if(!e.getHalfedge(s.halfedge.id)){t.push({type:"invalid_face_halfedge",message:`Face ${s.id} references non-existent halfedge`,elementIds:[s.id]});continue}const o=s.getHalfedges();if(o){for(const e of o)(null==(i=e.face)?void 0:i.id)!==s.id&&t.push({type:"halfedge_face_mismatch",message:`Halfedge ${e.id} in face ${s.id} references different face`,elementIds:[s.id,e.id]});s.isDegenerate()&&n.push({type:"degenerate_face",message:`Face ${s.id} is degenerate (near-zero area)`,elementIds:[s.id]})}else t.push({type:"face_invalid_loop",message:`Face ${s.id} has invalid halfedge loop`,elementIds:[s.id]})}}(e,t,n),function(e,t){for(const n of e.getHalfedges())n.next?e.getHalfedge(n.next.id)||t.push({type:"halfedge_invalid_next",message:`Halfedge ${n.id} has invalid next pointer`,elementIds:[n.id]}):t.push({type:"halfedge_no_next",message:`Halfedge ${n.id} has no next pointer`,elementIds:[n.id]}),n.prev?e.getHalfedge(n.prev.id)||t.push({type:"halfedge_invalid_prev",message:`Halfedge ${n.id} has invalid prev pointer`,elementIds:[n.id]}):t.push({type:"halfedge_no_prev",message:`Halfedge ${n.id} has no prev pointer`,elementIds:[n.id]}),n.next&&n.next.prev!==n&&t.push({type:"halfedge_next_prev_mismatch",message:`Halfedge ${n.id}: next.prev does not point back`,elementIds:[n.id]}),n.prev&&n.prev.next!==n&&t.push({type:"halfedge_prev_next_mismatch",message:`Halfedge ${n.id}: prev.next does not point back`,elementIds:[n.id]}),n.twin&&(e.getHalfedge(n.twin.id)?n.twin.twin!==n&&t.push({type:"halfedge_twin_mismatch",message:`Halfedge ${n.id}: twin.twin does not point back`,elementIds:[n.id]}):t.push({type:"halfedge_invalid_twin",message:`Halfedge ${n.id} has invalid twin pointer`,elementIds:[n.id]})),e.getVertex(n.vertex.id)||t.push({type:"halfedge_invalid_vertex",message:`Halfedge ${n.id} references non-existent vertex`,elementIds:[n.id]}),e.getEdge(n.edge.id)||t.push({type:"halfedge_invalid_edge",message:`Halfedge ${n.id} references non-existent edge`,elementIds:[n.id]})}(e,t),{isValid:0===t.length,errors:t,warnings:n}}function ae(e){return re(e).isValid}function le(e){if(e.isSkeletonEdge())return!1;if(!e.canFlip())return!1;const t=ce(e);return!!t&&H(t.v0,t.v1,t.v2,t.v3)}function ce(e){const t=e.halfedge,n=t.twin;if(!n||!t.face||!n.face)return null;const i=t.next,s=n.next;if(!i||!s)return null;const o=n.vertex,r=t.vertex,a=i.vertex,l=s.vertex;return{v0:{x:o.position.x,y:o.position.y,z:o.position.z},v1:{x:r.position.x,y:r.position.y,z:r.position.z},v2:{x:a.position.x,y:a.position.y,z:a.position.z},v3:{x:l.position.x,y:l.position.y,z:l.position.z}}}function he(e,t){if(!le(t))return{success:!1,reason:"Edge cannot be flipped"};const n=t.halfedge,i=n.twin;if(!i)return{success:!1,reason:"Edge has no twin"};const s=n.next,o=n.prev,r=i.next,a=i.prev,l=i.vertex,c=n.vertex,h=s.vertex,u=r.vertex,g=n.face,d=i.face;if(!g||!d)return{success:!1,reason:"Missing faces"};const f=p({x:h.position.x,y:h.position.y,z:h.position.z},{x:u.position.x,y:u.position.y,z:u.position.z});return t.length=f,n.vertex=u,i.vertex=h,n.next=a,n.prev=s,a.next=s,a.prev=n,s.next=n,s.prev=a,i.next=o,i.prev=r,o.next=r,o.prev=i,r.next=i,r.prev=o,n.face=g,a.face=g,s.face=g,i.face=d,o.face=d,r.face=d,g.halfedge=n,d.halfedge=i,l.halfedge===n&&(l.halfedge=r),c.halfedge===i&&(c.halfedge=s),{success:!0,newLength:f}}function ue(e){const t=e.halfedge,n=t.twin;if(!n||!t.face||!n.face)return!0;const i=ce(e);if(!i)return!0;return ge(i.v2,i.v0,i.v1)+ge(i.v3,i.v0,i.v1)<=Math.PI+1e-10}function ge(e,t,n){const i=t.x-e.x,s=t.y-e.y,o=t.z-e.z,r=n.x-e.x,a=n.y-e.y,l=n.z-e.z,c=i*r+s*a+o*l,h=Math.sqrt(i*i+s*s+o*o),u=Math.sqrt(r*r+a*a+l*l);if(h<1e-10||u<1e-10)return 0;const g=Math.max(-1,Math.min(1,c/(h*u)));return Math.acos(g)}function de(e,t){const n=e.getEdges(),i=t??10*n.length;let s=0,o=0;for(;o<i;){o++;let e=!1;for(const t of n)if(!ue(t)&&le(t)){he(0,t).success&&(s++,e=!0)}if(!e)break}return s}function fe(t,n,i=.5){const[s,o]=n.getVertices();if(!s||!o)return{success:!1,reason:"Edge has no vertices"};const r=s.position.x,a=s.position.y,l=s.position.z,c={x:r+(o.position.x-r)*i,y:a+(o.position.y-a)*i,z:l+(o.position.z-l)*i},h=t.createVertex(new e.Vector3(c.x,c.y,c.z));n.isSkeletonEdge();const u=[],g=[],d=n.getFaces();if(0===d.length)return{success:!0,newVertex:h,newEdges:[],newFaces:[]};for(const e of d){if(!e)continue;const i=e.getHalfedges();if(!i)continue;let s=null;for(const e of i)if(e.edge.id===n.id){s=e;break}s&&pe(t,e,s,h,u,g)}return n.length=n.length*i,t.classifyVertices(),{success:!0,newVertex:h,newEdges:u,newFaces:g}}function pe(e,t,n,i,s,o){const r=n.next.vertex,a=n.vertex,l=e.createFace(i,a,r);o.push(l),n.vertex=i,i.halfedge||(i.halfedge=n)}function xe(e,t){const n=[];let i=0;const s=[];for(const o of e.getEdges())o.length>t&&s.push(o);for(const o of s){const t=fe(e,o);t.success&&t.newVertex&&(i++,n.push(t.newVertex))}return{splitCount:i,newVertices:n}}function ye(e){const[t,n]=e.getVertices();return!(!t||!n)&&((t.type!==r.SkeletonBranching&&t.type!==r.NonManifoldOther||n.type!==r.SkeletonBranching&&n.type!==r.NonManifoldOther)&&(n.type===r.SkeletonBranching||(n.type,r.NonManifoldOther),!!function(e,t){const n=new Set,i=new Set;e.forEachNeighbor(e=>{n.add(e.id)}),t.forEachNeighbor(e=>{i.add(e.id)});let s=0;for(const a of n)a!==e.id&&a!==t.id&&i.has(a)&&s++;const o=me(e,t),r=o.length;return s<=r}(t,n)))}function me(e,t){const n=new Set,i=[];return e.forEachOutgoingHalfedge(e=>{e.face&&n.add(e.face.id)}),t.forEachOutgoingHalfedge(e=>{e.face&&n.has(e.face.id)&&i.push(e.face)}),i}function ve(e,t){var n;if(!ye(t))return{success:!1,reason:"Edge cannot be contracted"};const[i,s]=t.getVertices();if(!i||!s)return{success:!1,reason:"Edge has no vertices"};const{keepVertex:o,removeVertex:a,newPosition:l}=function(e,t){const n=e=>{switch(e.type){case r.SkeletonBranching:case r.NonManifoldOther:return 3;case r.OpenBook:return 2;case r.Manifold:return 1;default:return 0}},i=n(e),s=n(t);let o,a,l;i>=s?(o=e,a=t):(o=t,a=e);l=o.type===r.SkeletonBranching||o.type===r.NonManifoldOther||o.type===r.OpenBook&&a.type===r.Manifold?{x:o.position.x,y:o.position.y,z:o.position.z}:k({x:e.position.x,y:e.position.y,z:e.position.z},{x:t.position.x,y:t.position.y,z:t.position.z});return{keepVertex:o,removeVertex:a,newPosition:l}}(i,s);o.position.set(l.x,l.y,l.z);const c=me(i,s);var h;h=o,a.forEachOutgoingHalfedge(e=>{e.twin&&(e.twin.vertex=h)});for(const r of c)e.faces.delete(r.id);e.edges.delete(t.id);for(const r of t.allHalfedges)e.halfedges.delete(r.id);if(e.vertices.delete(a.id),o.halfedge&&!e.halfedges.has(o.halfedge.id))for(const r of e.halfedges.values())if((null==(n=r.getSourceVertex())?void 0:n.id)===o.id){o.halfedge=r;break}return e.classifyVertices(),{success:!0,remainingVertex:o,removedFaces:c}}function ze(e,t){let n=0,i=0,s=[];for(const o of e.getEdges())o.length<t&&ye(o)&&s.push(o);for(;s.length>0;){const t=s.pop();if(!e.edges.has(t.id)||!ye(t))continue;ve(e,t).success&&(n++,i++)}return{contractCount:n,removedVertices:i}}function Me(e){const t=e.getNeighbors();if(0===t.length)return null;let n=0,i=0,s=0;for(const c of t)n+=c.position.x,i+=c.position.y,s+=c.position.z;n/=t.length,i/=t.length,s/=t.length;const o={x:n,y:i,z:s},r=function(e){let t=0,n=0,i=0,s=0;if(e.forEachOutgoingHalfedge(e=>{if(e.face){const o=e.face.getNormal();o&&(t+=o.x,n+=o.y,i+=o.z,s++)}}),0===s)return null;return z({x:t/s,y:n/s,z:i/s})}(e);if(!r)return o;const a={x:e.position.x,y:e.position.y,z:e.position.z},l=E(o,a);return M(a,E(l,w(r,x(l,r))))}function Ee(e,t,n,i){if(t.type===r.SkeletonBranching||t.type===r.NonManifoldOther)return{success:!1,reason:"Vertex is fixed"};const s={x:t.position.x,y:t.position.y,z:t.position.z};let o=n,a=!1;if(i){const e=i.constrainPosition(t,n);o=e.position,a=e.wasConstrained}if(!function(e,t){const n={x:e.position.x,y:e.position.y,z:e.position.z};e.position.set(t.x,t.y,t.z);let i=!0;return e.forEachOutgoingHalfedge(e=>{if(e.face){const t=e.face.getArea();null!==t&&t<1e-10&&(i=!1)}}),e.position.set(n.x,n.y,n.z),i}(t,o))return{success:!1,reason:"Relocation would create invalid geometry"};t.position.set(o.x,o.y,o.z),function(e){e.forEachOutgoingHalfedge(e=>{const t=e.getSourceVertex();t&&(e.edge.length=p({x:t.position.x,y:t.position.y,z:t.position.z},{x:e.vertex.position.x,y:e.vertex.position.y,z:e.vertex.position.z}))})}(t);return{success:!0,newPosition:o,wasConstrained:a,distanceMoved:p(s,o)}}function we(e,t,n,i=.5){const s=Me(t);if(!s)return{success:!1,reason:"Cannot compute smoothing target"};const o=t.position.x,r=t.position.y,a=t.position.z;return Ee(0,t,{x:o+(s.x-o)*i,y:r+(s.y-r)*i,z:a+(s.z-a)*i},n)}function Ve(e,t,n=.5){let i=0,s=0;for(const o of e.getVertices())if(o.type===r.Manifold||o.type===r.OpenBook){const e=we(0,o,t,n);e.success&&(i++,s+=e.distanceMoved??0)}return{smoothedCount:i,totalDistance:s}}function ke(e){if(0===e.length)return 0;let t=e[0];for(let n=1;n<e.length;n++){const i=e[n];i<t&&(t=i)}return t}function Ce(e){if(0===e.length)return 0;let t=e[0];for(let n=1;n<e.length;n++){const i=e[n];i>t&&(t=i)}return t}function Se(e,t=.3){const n=e.getFaces(),i=e.getEdges(),s=[],o=[];for(const v of n){const e=v.getQuality();null!==e&&s.push(e);const t=v.getArea();null!==t&&o.push(t)}const r=i.map(e=>e.length),a=ke(s),l=Ce(s),c=s.length>0?s.reduce((e,t)=>e+t,0)/s.length:0,h=s.length>0?s.reduce((e,t)=>e+Math.pow(t-c,2),0)/s.length:0,u=Math.sqrt(h),g=s.filter(e=>e<t).length,d=ke(r),f=Ce(r),p=r.length>0?r.reduce((e,t)=>e+t,0)/r.length:0,x=ke(o),y=Ce(o),m=o.reduce((e,t)=>e+t,0);return{minQuality:a,maxQuality:l,averageQuality:c,stdDevQuality:u,poorQualityCount:g,minEdgeLength:d,maxEdgeLength:f,averageEdgeLength:p,minArea:x,maxArea:y,totalArea:m}}function be(e,t=.3){return e.getFaces().filter(e=>{const n=e.getQuality();return null!==n&&n<t})}function Be(e,t,n=1.333){const i=t*n;return e.getEdges().filter(e=>e.length>i)}function Fe(e,t,n=.4){const i=t*n;return e.getEdges().filter(e=>e.length<i)}function _e(e,t){const n=e.getVertices();if(0===n.length)return 1;let i=1/0,s=1/0,o=1/0,r=-1/0,a=-1/0,l=-1/0;for(const h of n)i=Math.min(i,h.position.x),s=Math.min(s,h.position.y),o=Math.min(o,h.position.z),r=Math.max(r,h.position.x),a=Math.max(a,h.position.y),l=Math.max(l,h.position.z);const c=Math.sqrt(Math.pow(r-i,2)+Math.pow(a-s,2)+Math.pow(l-o,2));if(void 0!==t&&t>0){const n=Se(e);if(n.totalArea>0)return Math.sqrt(n.totalArea/(2*t))}return c/Math.sqrt(n.length)}function He(e,t=8){return e.getVertices().filter(e=>{const n=e.degree();return null!==n&&n>t})}function Ie(e,t=4){return e.getVertices().filter(e=>{const n=e.degree();return null!==n&&n<t})}class Oe{constructor(e,t={}){this.skeleton=null,this.constraints=null,this.mesh=e;const n=t.targetEdgeLength??_e(e);this.options={...l,...t,targetEdgeLength:n},this.state={iteration:0,edgeSplits:0,edgeContractions:0,edgeFlips:0,vertexRelocations:0,quality:Se(e)},e.isManifold()||(this.skeleton=L(e),this.constraints=A(this.skeleton))}iterate(){this.state.iteration++;const e=this.options.targetEdgeLength,t=e*this.options.minEdgeLengthRatio,n=e*this.options.maxEdgeLengthRatio,i=xe(this.mesh,n);this.state.edgeSplits+=i.splitCount;const s=ze(this.mesh,t);this.state.edgeContractions+=s.contractCount;const o=de(this.mesh);this.state.edgeFlips+=o;const r=Ve(this.mesh,this.constraints??void 0,.5);return this.state.vertexRelocations+=r.smoothedCount,this.skeleton&&(i.splitCount>0||s.contractCount>0)&&this.skeleton.rebuild(),this.state.quality=Se(this.mesh,this.options.minTriangleQuality),this.options.verbose&&console.warn(`Iteration ${this.state.iteration}: splits=${i.splitCount}, contractions=${s.contractCount}, flips=${o}, smoothed=${r.smoothedCount}, avgQuality=${this.state.quality.averageQuality.toFixed(3)}`),{...this.state}}run(e){const t=e??this.options.iterations,n=Date.now(),i={vertices:this.mesh.vertexCount,faces:this.mesh.faceCount};for(let o=0;o<t;o++){const e=this.state.quality.averageQuality;this.iterate();const t=this.state.quality.averageQuality-e;if(Math.abs(t)<.001&&o>0){this.options.verbose&&console.warn(`Converged after ${o+1} iterations`);break}}const s=Date.now()-n;return{inputVertices:i.vertices,inputFaces:i.faces,outputVertices:this.mesh.vertexCount,outputFaces:this.mesh.faceCount,iterations:this.state.iteration,finalQuality:this.state.quality.averageQuality,nonManifoldEdges:this.mesh.getNonManifoldEdges().length,skeletonEdges:this.mesh.getSkeletonEdges().length,edgeFlips:this.state.edgeFlips,edgeSplits:this.state.edgeSplits,edgeContractions:this.state.edgeContractions,vertexRelocations:this.state.vertexRelocations,processingTimeMs:s}}hasConverged(){return this.state.quality.averageQuality>.9||0===this.state.quality.poorQualityCount}getMesh(){return this.mesh}getSkeleton(){return this.skeleton}getQuality(){return this.state.quality}getState(){return{...this.state}}toBufferGeometry(){return T(this.mesh)}}exports.AdaptiveRemesher=Oe,exports.BVH=Y,exports.BufferGeometryExporter=class{constructor(e={}){this.options=e}export(e){return T(e,this.options)}exportSkeleton(e){return G(e)}exportClassification(e){return Q(e)}exportQuality(e){return R(e)}setComputeNormals(e){return this.options.computeNormals=e,this}setSmoothNormals(e){return this.options.smoothNormals=e,this}},exports.BufferGeometryImporter=class{constructor(e={}){this.options=e}import(e){return P(e,this.options)}validate(e){return q(e)}setFeatureEdges(e){return this.options.featureEdges=e,this}setValidation(e){return this.options.validate=e,this}},exports.DEFAULT_REMESH_OPTIONS=l,exports.Edge=u,exports.EdgeContractor=class{constructor(e){this.mesh=e}contract(e){return ve(this.mesh,e)}canContract(e){return ye(e)}contractShortEdges(e){return ze(this.mesh,e)}shouldContract(e,t,n=.4){return e.length<t*n}},exports.EdgeFlipper=class{constructor(e){this.mesh=e}flip(e){return he(this.mesh,e)}canFlip(e){return le(e)}isDelaunay(e){return ue(e)}makeDelaunay(e){return de(this.mesh,e)}},exports.EdgeSplitter=class{constructor(e){this.mesh=e}split(e,t=.5){return fe(this.mesh,e,t)}splitLongEdges(e){return xe(this.mesh,e)}shouldSplit(e,t,n=1.333){return e.length>t*n}},exports.EdgeType=a,exports.Face=g,exports.FeatureSkeleton=N,exports.Halfedge=h,exports.ManifoldAnalyzer=class{constructor(){this.mesh=null,this.cachedResult=null}load(e){return this.mesh=d.fromBufferGeometry(e),this.cachedResult=null,this}loadMesh(e){return this.mesh=e,this.cachedResult=null,this}analyze(){if(!this.mesh)throw new Error("No mesh loaded. Call load() first.");return this.cachedResult||(this.cachedResult=Z(this.mesh)),this.cachedResult}isManifold(){return this.analyze().isManifold}hasBoundary(){return this.analyze().hasBoundary}getNonManifoldEdges(){return this.analyze().nonManifoldEdges}getNonManifoldVertices(){return this.analyze().nonManifoldVertices}getMesh(){return this.mesh}clearCache(){return this.cachedResult=null,this}},exports.NonManifoldMesh=d,exports.QualityMetrics=class{constructor(e){this.mesh=e}computeStats(e=.3){return Se(this.mesh,e)}getPoorQualityFaces(e=.3){return be(this.mesh,e)}getLongEdges(e,t=1.333){return Be(this.mesh,e,t)}getShortEdges(e,t=.4){return Fe(this.mesh,e,t)}computeTargetEdgeLength(e){return _e(this.mesh,e)}getHighValenceVertices(e=8){return He(this.mesh,e)}getLowValenceVertices(e=4){return Ie(this.mesh,e)}},exports.SkeletonBuilder=O,exports.SkeletonConstraints=$,exports.SkeletonSegment=I,exports.SpatialHash=D,exports.TopologyValidator=class{constructor(e){this.mesh=e}validate(){return re(this.mesh)}isValid(){return ae(this.mesh)}getErrors(){return this.validate().errors}getWarnings(){return this.validate().warnings}},exports.Vertex=c,exports.VertexClassifier=class{constructor(e){this.mesh=e}classifyAll(){return ee(this.mesh)}getByType(e){return ne(this.mesh,e)}getManifold(){return ie(this.mesh)}getNonManifold(){return se(this.mesh)}reclassify(){oe(this.mesh)}},exports.VertexRelocator=class{constructor(e,t){this.constraints=null,this.mesh=e,this.constraints=t??null}setConstraints(e){this.constraints=e}relocate(e,t){return Ee(this.mesh,e,t,this.constraints??void 0)}smooth(e,t=.5){return we(this.mesh,e,this.constraints??void 0,t)}smoothAll(e=.5){return Ve(this.mesh,this.constraints??void 0,e)}canRelocate(e){return e.type===r.Manifold||e.type===r.OpenBook}},exports.VertexType=r,exports.add=M,exports.analyzeManifold=function(e){return Z(d.fromBufferGeometry(e))},exports.analyzeMesh=Z,exports.angleAtVertex=function(e,t,n){return C(E(e,t),E(n,t))},exports.angleBetween=C,exports.barycentricCoordinates=function(e,t,n,i){const s=E(n,t),o=E(i,t),r=E(e,t),a=x(s,s),l=x(s,o),c=x(o,o),h=x(r,s),u=x(r,o),g=a*c-l*l;if(Math.abs(g)<1e-10)return null;const d=(c*h-l*u)/g,f=(a*u-l*h)/g;return{u:1-d-f,v:d,w:f}},exports.buildSkeleton=function(e){return new O(e).build()},exports.canContractEdge=ye,exports.canFlipEdge=function(e){return"manifold"===e},exports.canFlipEdgeGeometric=le,exports.canMoveFreely=function(e){return"manifold"===e},exports.classifyAllVertices=ee,exports.classifyVertex=te,exports.computeMeshQuality=Se,exports.computeTangentialSmoothing=Me,exports.computeTargetEdgeLength=_e,exports.computeTriangleAspectRatio=function(e){const t=e.getHalfedges();if(!t)return null;const n=t.map(e=>e.edge.length),i=ke(n),s=Ce(n);return i<1e-10?1/0:s/i},exports.contractEdge=ve,exports.contractShortEdges=ze,exports.cotangent=function(e,t,n){const i=E(e,t),s=E(n,t),o=x(i,s),r=m(y(i,s));return Math.abs(r)<1e-10?0:o/r},exports.createBVHFromMesh=function(e){const t=[];for(const i of e.getFaces()){const e=i.getVertices();e&&t.push({v0:{x:e[0].position.x,y:e[0].position.y,z:e[0].position.z},v1:{x:e[1].position.x,y:e[1].position.y,z:e[1].position.z},v2:{x:e[2].position.x,y:e[2].position.y,z:e[2].position.z}})}const n=new Y;return n.build(t),n},exports.createEdgeId=n,exports.createFaceId=s,exports.createHalfedgeId=i,exports.createRemesher=function(e,t={}){const n=d.fromBufferGeometry(e,t.featureEdges);return new Oe(n,t)},exports.createSegmentId=o,exports.createSkeleton=L,exports.createSkeletonConstraints=A,exports.createSpatialHash=function(e,t,n){let i=n;if(void 0===i&&e.length>1){let n=1/0,s=1/0,o=1/0,r=-1/0,a=-1/0,l=-1/0;for(const i of e){const e=t(i);n=Math.min(n,e.x),s=Math.min(s,e.y),o=Math.min(o,e.z),r=Math.max(r,e.x),a=Math.max(a,e.y),l=Math.max(l,e.z)}i=Math.sqrt((r-n)**2+(a-s)**2+(l-o)**2)/Math.sqrt(e.length)}const s=new D(i??1);for(const o of e)s.insert(o,t(o));return s},exports.createVertexId=t,exports.cross=y,exports.distance=p,exports.distanceSquared=f,exports.dot=x,exports.exportBufferGeometry=T,exports.exportClassificationGeometry=Q,exports.exportQualityGeometry=R,exports.exportSkeletonGeometry=G,exports.flipEdge=he,exports.fromVector3=function(e){return{x:e.x,y:e.y,z:e.z}},exports.getHighValenceVertices=He,exports.getLongEdges=Be,exports.getLowValenceVertices=Ie,exports.getManifoldVertices=ie,exports.getNonManifoldVertices=se,exports.getOpenBookVertices=function(e){return ne(e,r.OpenBook)},exports.getPoorQualityFaces=be,exports.getShortEdges=Fe,exports.getSkeletonBranchingVertices=function(e){return ne(e,r.SkeletonBranching)},exports.getVerticesByType=ne,exports.importBufferGeometry=P,exports.isDelaunay=ue,exports.isManifold=function(e){return d.fromBufferGeometry(e).isManifold()},exports.isPointInTriangle=function(e,t,n,i){const s=E(n,t),o=E(i,t),r=E(e,t),a=x(s,s),l=x(s,o),c=x(o,o),h=x(r,s),u=x(r,o),g=a*c-l*l;if(Math.abs(g)<1e-10)return!1;const d=(c*h-l*u)/g,f=(a*u-l*h)/g;return 1-d-f>=0&&d>=0&&f>=0},exports.isPositionFixed=function(e){return"skeleton_branching"===e||"non_manifold_other"===e},exports.isQuadConvex=H,exports.isSkeletonConstrained=function(e){return"open_book"===e},exports.isSkeletonEdge=function(e){return"non_manifold"===e||"feature"===e||"boundary"===e},exports.isTopologyValid=ae,exports.length=m,exports.lengthSquared=v,exports.lerp=V,exports.makeDelaunay=de,exports.midpoint=k,exports.normalize=z,exports.projectPointOnLine=function(e,t,n){const i=E(n,t),s=v(i);return s<1e-10?{...t}:M(t,w(i,x(E(e,t),i)/s))},exports.projectPointOnSegment=S,exports.reclassifyVertices=oe,exports.relocateVertex=Ee,exports.remesh=function(e,t={}){const n=d.fromBufferGeometry(e,t.featureEdges),i=new Oe(n,t),s=i.run();return{geometry:i.toBufferGeometry(),stats:s}},exports.scale=w,exports.smoothAllVertices=Ve,exports.smoothVertex=we,exports.splitEdge=fe,exports.splitLongEdges=xe,exports.subtract=E,exports.toNumber=function(e){return e},exports.triangleArea=b,exports.triangleCentroid=function(e,t,n){return{x:(e.x+t.x+n.x)/3,y:(e.y+t.y+n.y)/3,z:(e.z+t.z+n.z)/3}},exports.triangleCircumcenter=function(e,t,n){const i=E(t,e),s=E(n,e),o=y(i,s),r=2*v(o);if(r<1e-10)return null;const a=v(i),l=v(s),c=w(y(o,i),l),h=w(y(s,o),a),u=w(M(c,h),1/r);return M(e,u)},exports.triangleCircumradius=F,exports.triangleInradius=_,exports.triangleNormal=B,exports.triangleQuality=function(e,t,n){const i=_(e,t,n),s=F(e,t,n);return null===i||null===s||s<1e-10?0:Math.max(0,Math.min(1,2*i/s))},exports.validateGeometry=q,exports.validateTopology=re;
|
|
2
2
|
//# sourceMappingURL=remesh-threejs.cjs.map
|