remesh-threejs 0.3.0 → 0.3.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/README.md CHANGED
@@ -48,7 +48,7 @@ const outputGeometry = result.geometry;
48
48
  ```typescript
49
49
  import { repairMesh, removeIsolatedVertices } from 'remesh-threejs';
50
50
 
51
- // Quick repair (removes isolated vertices, degenerate faces, duplicates)
51
+ // Quick repair (removes all common defects in optimal order)
52
52
  const result = repairMesh(geometry);
53
53
  console.log(`Fixed ${result.stats.totalDefectsFixed} defects in ${result.stats.totalTimeMs}ms`);
54
54
 
@@ -56,6 +56,9 @@ console.log(`Fixed ${result.stats.totalDefectsFixed} defects in ${result.stats.t
56
56
  const result2 = removeIsolatedVertices(geometry);
57
57
  const result3 = removeDegenerateFaces(geometry, { areaThreshold: 1e-10 });
58
58
  const result4 = removeDuplicateFaces(geometry);
59
+ const result5 = removeNonManifoldEdges(geometry, { strategy: 'auto' });
60
+ const result6 = fillHoles(geometry, { maxHoleSize: 10 });
61
+ const result7 = unifyNormals(geometry);
59
62
  ```
60
63
 
61
64
  ### Analyze Mesh for Non-Manifold Issues
@@ -125,6 +128,9 @@ const outputGeometry = exportBufferGeometry(mesh);
125
128
  | `removeIsolatedVertices(geometry)` | Remove orphaned vertices with no faces |
126
129
  | `removeDegenerateFaces(geometry, options?)` | Remove zero-area triangles |
127
130
  | `removeDuplicateFaces(geometry)` | Remove faces with identical vertices |
131
+ | `removeNonManifoldEdges(geometry, options?)` | Fix edges with >2 incident faces |
132
+ | `fillHoles(geometry, options?)` | Fill boundary holes via triangulation |
133
+ | `unifyNormals(geometry, options?)` | Make face orientations consistent |
128
134
  | `analyzeManifold(geometry)` | Analyze mesh for non-manifold features |
129
135
  | `isManifold(geometry)` | Quick check if mesh is manifold |
130
136
  | `validateTopology(mesh)` | Validate mesh topology integrity |
@@ -199,6 +205,9 @@ import {
199
205
  removeIsolatedVertices,
200
206
  removeDegenerateFaces,
201
207
  removeDuplicateFaces,
208
+ removeNonManifoldEdges,
209
+ fillHoles,
210
+ unifyNormals,
202
211
  } from 'remesh-threejs';
203
212
 
204
213
  // Run all repairs in optimal order
@@ -209,6 +218,9 @@ console.log(`Fixed ${result.stats.totalDefectsFixed} defects`);
209
218
  const result2 = removeIsolatedVertices(geometry); // 100x+ faster
210
219
  const result3 = removeDegenerateFaces(geometry); // 50-100x faster
211
220
  const result4 = removeDuplicateFaces(geometry); // 30-60x faster
221
+ const result5 = removeNonManifoldEdges(geometry, { strategy: 'split' }); // 10-30x faster
222
+ const result6 = fillHoles(geometry, { maxHoleSize: 10 }); // 20-50x faster
223
+ const result7 = unifyNormals(geometry); // 40-80x faster
212
224
  ```
213
225
 
214
226
  #### Class-Based API (Advanced)
@@ -226,6 +238,9 @@ const stats = repairer
226
238
  .removeIsolatedVertices()
227
239
  .removeDegenerateFaces()
228
240
  .removeDuplicateFaces()
241
+ .removeNonManifoldEdges('auto')
242
+ .fillHoles(10)
243
+ .unifyNormals()
229
244
  .execute();
230
245
 
231
246
  const repairedGeometry = repairer.toBufferGeometry();
@@ -244,6 +259,9 @@ if (!validation.isValid) {
244
259
  | Orphaned vertices | `removeIsolatedVertices()` | 100x+ |
245
260
  | Zero-area triangles | `removeDegenerateFaces()` | 50-100x |
246
261
  | Duplicate faces | `removeDuplicateFaces()` | 30-60x |
262
+ | Edges with >2 faces | `removeNonManifoldEdges()` | 10-30x |
263
+ | Boundary holes | `fillHoles()` | 20-50x |
264
+ | Inconsistent normals | `unifyNormals()` | 40-80x |
247
265
 
248
266
  ### Visualization Helpers
249
267
 
package/dist/index.d.ts CHANGED
@@ -1026,6 +1026,17 @@ export declare class FeatureSkeleton {
1026
1026
  };
1027
1027
  }
1028
1028
 
1029
+ /**
1030
+ * Fill holes in the mesh by triangulating boundary loops.
1031
+ *
1032
+ * @param geometry - Input BufferGeometry
1033
+ * @param options - Repair options with optional maxHoleSize
1034
+ * @returns Repaired geometry and statistics
1035
+ */
1036
+ export declare function fillHoles(geometry: BufferGeometry, options?: RepairOptions & {
1037
+ maxHoleSize?: number;
1038
+ }): RepairResult;
1039
+
1029
1040
  /**
1030
1041
  * Flips an edge in the mesh.
1031
1042
  *
@@ -1206,6 +1217,36 @@ export declare type HalfedgeId = number & {
1206
1217
  readonly __brand: 'HalfedgeId';
1207
1218
  };
1208
1219
 
1220
+ /**
1221
+ * Fills holes in the mesh by triangulating boundary loops.
1222
+ */
1223
+ export declare class HoleFiller extends RepairOperation {
1224
+ private holes;
1225
+ private maxHoleSize;
1226
+ private preserveBoundary;
1227
+ constructor(mesh: NonManifoldMesh, verbose?: boolean, maxHoleSize?: number, preserveBoundary?: boolean);
1228
+ detect(): number;
1229
+ repair(): number;
1230
+ /**
1231
+ * Extract boundary loops from a set of boundary edges.
1232
+ */
1233
+ private extractBoundaryLoops;
1234
+ /**
1235
+ * Find the next boundary edge connected to a vertex.
1236
+ */
1237
+ private findNextBoundaryEdge;
1238
+ /**
1239
+ * Triangulate a boundary loop using ear clipping algorithm.
1240
+ */
1241
+ private triangulateBoundaryLoop;
1242
+ /**
1243
+ * Find an "ear" in the polygon (a triangle with no vertices inside).
1244
+ */
1245
+ private findEar;
1246
+ getName(): string;
1247
+ canParallelize(): boolean;
1248
+ }
1249
+
1209
1250
  /**
1210
1251
  * Imports a Three.js BufferGeometry into a NonManifoldMesh.
1211
1252
  *
@@ -1456,6 +1497,24 @@ export declare class MeshRepairer {
1456
1497
  * @returns this for chaining
1457
1498
  */
1458
1499
  removeDuplicateFaces(): this;
1500
+ /**
1501
+ * Remove non-manifold edges by splitting or collapsing.
1502
+ * @param strategy - Repair strategy: 'split', 'collapse', or 'auto' (default: 'auto')
1503
+ * @returns this for chaining
1504
+ */
1505
+ removeNonManifoldEdges(strategy?: NonManifoldRepairStrategy): this;
1506
+ /**
1507
+ * Fill boundary loops (holes) with triangulation.
1508
+ * @param maxHoleSize - Maximum number of edges in a hole to fill (default: 100)
1509
+ * @returns this for chaining
1510
+ */
1511
+ fillHoles(maxHoleSize?: number): this;
1512
+ /**
1513
+ * Unify face orientations to make normals consistent.
1514
+ * @param seedFaceIndex - Index of the face to use as orientation reference (default: 0)
1515
+ * @returns this for chaining
1516
+ */
1517
+ unifyNormals(seedFaceIndex?: number): this;
1459
1518
  /**
1460
1519
  * Run all common repairs in optimal order.
1461
1520
  * @returns this for chaining
@@ -1514,6 +1573,35 @@ export declare interface NonManifoldEdgeInfo {
1514
1573
  }];
1515
1574
  }
1516
1575
 
1576
+ /**
1577
+ * Repairs non-manifold edges (edges with >2 incident faces).
1578
+ */
1579
+ export declare class NonManifoldEdgeRepair extends RepairOperation {
1580
+ private nonManifoldEdges;
1581
+ private strategy;
1582
+ constructor(mesh: NonManifoldMesh, verbose?: boolean, strategy?: NonManifoldRepairStrategy);
1583
+ detect(): number;
1584
+ repair(): number;
1585
+ /**
1586
+ * Determine which strategy to use for a specific edge.
1587
+ */
1588
+ private determineStrategy;
1589
+ /**
1590
+ * Compute average edge length in the mesh.
1591
+ */
1592
+ private computeAverageEdgeLength;
1593
+ /**
1594
+ * Split a non-manifold edge by duplicating vertices.
1595
+ */
1596
+ private splitNonManifoldEdge;
1597
+ /**
1598
+ * Collapse a non-manifold edge by removing excess faces.
1599
+ */
1600
+ private collapseNonManifoldEdge;
1601
+ getName(): string;
1602
+ canParallelize(): boolean;
1603
+ }
1604
+
1517
1605
  /**
1518
1606
  * Represents a non-manifold mesh using an extended halfedge data structure.
1519
1607
  * Supports edges with more than 2 incident faces (non-manifold seams).
@@ -1669,6 +1757,11 @@ export declare class NonManifoldMesh {
1669
1757
  };
1670
1758
  }
1671
1759
 
1760
+ /**
1761
+ * Strategy for repairing non-manifold edges.
1762
+ */
1763
+ export declare type NonManifoldRepairStrategy = 'split' | 'collapse' | 'auto';
1764
+
1672
1765
  /**
1673
1766
  * Information about a non-manifold vertex.
1674
1767
  */
@@ -1693,6 +1786,43 @@ export declare interface NonManifoldVertexInfo {
1693
1786
  */
1694
1787
  export declare function normalize(v: Vec3): Vec3;
1695
1788
 
1789
+ /**
1790
+ * Unifies face orientations to make normals consistent.
1791
+ */
1792
+ export declare class NormalUnifier extends RepairOperation {
1793
+ private inconsistentFaces;
1794
+ private seedFaceIndex;
1795
+ constructor(mesh: NonManifoldMesh, verbose?: boolean, seedFaceIndex?: number);
1796
+ detect(): number;
1797
+ repair(): number;
1798
+ /**
1799
+ * Get faces that share an edge with the given face.
1800
+ */
1801
+ private getNeighborFaces;
1802
+ /**
1803
+ * Find the edge shared between two faces.
1804
+ */
1805
+ private getSharedEdge;
1806
+ /**
1807
+ * Check if two faces have consistent normal orientation across their shared edge.
1808
+ */
1809
+ private areNormalsConsistent;
1810
+ /**
1811
+ * Get the halfedge in a face that corresponds to a given edge.
1812
+ */
1813
+ private getHalfedgeInFace;
1814
+ /**
1815
+ * Get the start and end vertices of a halfedge.
1816
+ */
1817
+ private getHalfedgeVertices;
1818
+ /**
1819
+ * Flip the orientation of a face by reversing its halfedge order.
1820
+ */
1821
+ private flipFaceOrientation;
1822
+ getName(): string;
1823
+ canParallelize(): boolean;
1824
+ }
1825
+
1696
1826
  /**
1697
1827
  * Statistics for a single repair operation.
1698
1828
  */
@@ -1938,6 +2068,17 @@ export declare function removeDuplicateFaces(geometry: BufferGeometry, options?:
1938
2068
  */
1939
2069
  export declare function removeIsolatedVertices(geometry: BufferGeometry, options?: RepairOptions): RepairResult;
1940
2070
 
2071
+ /**
2072
+ * Remove non-manifold edges by splitting or collapsing.
2073
+ *
2074
+ * @param geometry - Input BufferGeometry
2075
+ * @param options - Repair options with optional strategy
2076
+ * @returns Repaired geometry and statistics
2077
+ */
2078
+ export declare function removeNonManifoldEdges(geometry: BufferGeometry, options?: RepairOptions & {
2079
+ strategy?: NonManifoldRepairStrategy;
2080
+ }): RepairResult;
2081
+
1941
2082
  /**
1942
2083
  * Repair a mesh by applying all repair operations in optimal order.
1943
2084
  * Fast alternative to full remeshing for common defects.
@@ -2505,6 +2646,17 @@ export declare function triangleNormal(v0: Vec3, v1: Vec3, v2: Vec3): Vec3;
2505
2646
  */
2506
2647
  export declare function triangleQuality(v0: Vec3, v1: Vec3, v2: Vec3): number;
2507
2648
 
2649
+ /**
2650
+ * Unify face orientations to make normals consistent.
2651
+ *
2652
+ * @param geometry - Input BufferGeometry
2653
+ * @param options - Repair options with optional seedFaceIndex
2654
+ * @returns Repaired geometry and statistics
2655
+ */
2656
+ export declare function unifyNormals(geometry: BufferGeometry, options?: RepairOptions & {
2657
+ seedFaceIndex?: number;
2658
+ }): RepairResult;
2659
+
2508
2660
  /**
2509
2661
  * Validates a BufferGeometry for import.
2510
2662
  *
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("three");function t(e){return e}function s(e){return e}function n(e){return e}function i(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,s=0;do{if(e(t),!t.twin||!t.twin.next)break;if(t=t.twin.next,s++,s>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,s){this.id=e,this.twin=null,this.next=null,this.prev=null,this.face=null,this.vertex=t,this.edge=s}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,s){this.id=e,this.allHalfedges=[],this.type=a.Manifold,this.isInPath=!1,this.halfedge=t,this.length=s,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 s=e.degree(),n=t.degree();return!(null===s||null===n||s<=1||n<=1)}getOtherVertex(e){const[t,s]=this.getVertices();return t&&s?e.id===t.id?s:e.id===s.id?t:null:null}markAsFeature(){this.type===a.Manifold&&(this.type=a.Feature)}}class d{constructor(e,t){this.id=e,this.isMarked=!1,this.halfedge=t}getVertices(){const e=this.halfedge,t=e.next,s=null==t?void 0:t.next;return t&&s?[e.vertex,t.vertex,s.vertex]:null}getHalfedges(){const e=this.halfedge,t=e.next,s=null==t?void 0:t.next;return t&&s?[e,t,s]:null}forEachHalfedge(e){const t=this.getHalfedges();if(t)for(const s of t)e(s)}forEachVertex(e){const t=this.getVertices();if(t)for(const s of t)e(s)}getCentroid(){const e=this.getVertices();if(!e)return null;const[t,s,n]=e;return{x:(t.position.x+s.position.x+n.position.x)/3,y:(t.position.y+s.position.y+n.position.y)/3,z:(t.position.z+s.position.z+n.position.z)/3}}getNormal(){const e=this.getVertices();if(!e)return null;const[t,s,n]=e,i=s.position.x-t.position.x,o=s.position.y-t.position.y,r=s.position.z-t.position.z,a=n.position.x-t.position.x,l=n.position.y-t.position.y,c=n.position.z-t.position.z,h=o*c-r*l,u=r*a-i*c,d=i*l-o*a,g=Math.sqrt(h*h+u*u+d*d);return g<1e-10?{x:0,y:0,z:1}:{x:h/g,y:u/g,z:d/g}}getArea(){const e=this.getVertices();if(!e)return null;const[t,s,n]=e,i=s.position.x-t.position.x,o=s.position.y-t.position.y,r=s.position.z-t.position.z,a=n.position.x-t.position.x,l=n.position.y-t.position.y,c=n.position.z-t.position.z,h=o*c-r*l,u=r*a-i*c,d=i*l-o*a;return Math.sqrt(h*h+u*u+d*d)/2}getQuality(){const e=this.getVertices();if(!e)return null;const[t,s,n]=e,i=Math.sqrt(Math.pow(s.position.x-n.position.x,2)+Math.pow(s.position.y-n.position.y,2)+Math.pow(s.position.z-n.position.z,2)),o=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)),r=Math.sqrt(Math.pow(t.position.x-s.position.x,2)+Math.pow(t.position.y-s.position.y,2)+Math.pow(t.position.z-s.position.z,2)),a=(i+o+r)/2,l=a*(a-i)*(a-o)*(a-r);if(l<=0)return 0;const c=Math.sqrt(l),h=i*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 s of t){const t=s.getSourceVertex();if(t&&t.id!==e.id&&s.vertex.id!==e.id)return s}return null}isDegenerate(e=1e-10){const t=this.getArea();return null===t||t<e}}class g{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(s,o){const r=new g,a=s.attributes.position;if(!a)throw new Error("Geometry must have a position attribute");const l=s.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 n=0;n<u;n++){const s=a.getX(n),i=a.getY(n),o=a.getZ(n),l=new c(t(r.nextVertexId++),new e.Vector3(s,i,o));r.vertices.set(l.id,l)}for(let e=0;e<f;e++){const s=l.getX(3*e+0),o=l.getX(3*e+1),a=l.getX(3*e+2),c=r.vertices.get(t(s)),u=r.vertices.get(t(o)),g=r.vertices.get(t(a));if(!c||!u||!g)throw new Error(`Vertex not found in face ${e}: ${s}, ${o}, ${a}`);const f=r.getOrCreateEdge(s,o),p=r.getOrCreateEdge(o,a),x=r.getOrCreateEdge(a,s),y=new d(i(r.nextFaceId++),null);r.faces.set(y.id,y);const m=new h(n(r.nextHalfedgeId++),u,f),v=new h(n(r.nextHalfedgeId++),g,p),z=new h(n(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),g.halfedge||(g.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 s=this.makeEdgeKey(e,t);let n=this.edgeMap.get(s);if(!n){const i=this.vertices.get(e),o=this.vertices.get(t);if(!i||!o)throw new Error(`Vertex not found: ${e} or ${t}`);const r=o.position.x-i.position.x,a=o.position.y-i.position.y,l=o.position.z-i.position.z,c=Math.sqrt(r*r+a*a+l*l);n=new u(this.nextEdgeId++,null,c),n.allHalfedges=[],this.edgeMap.set(s,n),this.edges.set(n.id,n)}return n}makeEdgeKey(e,t){return e<t?`${e},${t}`:`${t},${e}`}setupTwinHalfedges(){var e;for(const t of this.edges.values()){const e=t.allHalfedges,s=e.length;0!==s&&(1===s?(e[0].twin=null,t.type=a.Boundary):2===s?(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 s=null==(e=t.prev)?void 0:e.vertex;s&&(s.halfedge=t)}}setupNonManifoldTwins(e,t){const[s,n]=e.getVertices();if(!s||!n)return;const i=[],o=[];for(const a of t)a.vertex.id===s.id?i.push(a):o.push(a);const r=Math.min(i.length,o.length);for(let a=0;a<r;a++)i[a].twin=o[a],o[a].twin=i[a];for(let a=r;a<i.length;a++)i[a].twin=null;for(let a=r;a<o.length;a++)o[a].twin=null}markFeatureEdges(e){for(const[t,s]of e){const e=this.makeEdgeKey(t,s),n=this.edgeMap.get(e);n&&n.type===a.Manifold&&n.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 s=this.makeEdgeKey(e.id,t.id);return this.edgeMap.get(s)}createVertex(e){const t=new c(this.nextVertexId++,e);return this.vertices.set(t.id,t),t}createFace(e,t,s){const n=this.getOrCreateEdge(e.id,t.id),i=this.getOrCreateEdge(t.id,s.id),o=this.getOrCreateEdge(s.id,e.id),r=new d(this.nextFaceId++,null);this.faces.set(r.id,r);const a=new h(this.nextHalfedgeId++,t,n),l=new h(this.nextHalfedgeId++,s,i),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),n.addHalfedge(a),i.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),s.halfedge||(s.halfedge=c),n.halfedge=a,i.halfedge=l,o.halfedge=c,r}getStats(){const e=this.getNonManifoldEdges().length,t=this.getBoundaryEdges().length,s=this.getFeatureEdges().length,n=this.vertexCount-this.edgeCount+this.faceCount;return{vertexCount:this.vertexCount,edgeCount:this.edgeCount,faceCount:this.faceCount,nonManifoldEdgeCount:e,boundaryEdgeCount:t,featureEdgeCount:s,eulerCharacteristic:n}}}function f(e,t){const s=t.x-e.x,n=t.y-e.y,i=t.z-e.z;return s*s+n*n+i*i}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 w(e,t){return{x:e.x-t.x,y:e.y-t.y,z:e.z-t.z}}function V(e,t){return{x:e.x*t,y:e.y*t,z:e.z*t}}function E(e,t,s){return{x:e.x+(t.x-e.x)*s,y:e.y+(t.y-e.y)*s,z:e.z+(t.z-e.z)*s}}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 s=m(e),n=m(t);if(s<1e-10||n<1e-10)return 0;const i=x(e,t)/(s*n);return Math.acos(Math.max(-1,Math.min(1,i)))}function S(e,t,s){const n=w(s,t),i=v(n);if(i<1e-10)return{...t};return M(t,V(n,Math.max(0,Math.min(1,x(w(e,t),n)/i))))}function b(e,t,s){return m(y(w(t,e),w(s,e)))/2}function F(e,t,s){return z(y(w(t,e),w(s,e)))}function B(e,t,s){const n=p(t,s),i=p(e,s),o=p(e,t),r=b(e,t,s);return r<1e-10?null:n*i*o/(4*r)}function H(e,t,s){const n=(p(t,s)+p(e,s)+p(e,t))/2,i=b(e,t,s);return n<1e-10?null:i/n}function _(e,t,s,n){const i=z(M(F(e,t,s),F(e,n,t)));let o;o=Math.abs(i.x)<.9?z(y(i,{x:1,y:0,z:0})):z(y(i,{x:0,y:1,z:0}));const r=y(i,o),a=e=>({x:x(e,o),y:x(e,r)}),l=a(e),c=a(t),h=a(s),u=a(n),d=(e,t)=>e.x*t.y-e.y*t.x,g={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(d(g,f)*d(g,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},w={x:c.x-h.x,y:c.y-h.y};return d(m,v)*d(m,w)<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],s=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+=s}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],s=this.vertices[e],n=p({x:t.position.x,y:t.position.y,z:t.position.z},{x:s.position.x,y:s.position.y,z:s.position.z});this._totalLength+=n,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 n=1;n<this.vertices.length;n++){const e=this._cumulativeLengths[n-1]??0,s=this._cumulativeLengths[n]??0;if(t<=s){const i=this.vertices[n-1],o=this.vertices[n],r=s-e;if(r<1e-10)return{x:i.position.x,y:i.position.y,z:i.position.z};const a=(t-e)/r;return E({x:i.position.x,y:i.position.y,z:i.position.z},{x:o.position.x,y:o.position.y,z:o.position.z},a)}}const s=this.vertices[this.vertices.length-1];return{x:s.position.x,y:s.position.y,z:s.position.z}}projectPoint(e){if(0===this.vertices.length)return null;if(1===this.vertices.length){const t=this.vertices[0],s={x:t.position.x,y:t.position.y,z:t.position.z};return{point:s,parameter:0,distance:p(e,s)}}let t=null,s=0,n=1/0;for(let i=1;i<this.vertices.length;i++){const o=this.vertices[i-1],r=this.vertices[i],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<n){n=c,t=l;const e=this._cumulativeLengths[i-1]??0;if((this._cumulativeLengths[i]??0)-e<1e-10)s=e/this._totalLength;else{s=(e+p(a,l))/this._totalLength}}}return t?{point:t,parameter:s,distance:n}: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=[],s=[];for(const n of this.mesh.getVertices())n.type===r.SkeletonBranching?t.push(n):n.type===r.OpenBook&&s.push(n);return{segments:this.buildSegments(e,t),skeletonEdges:e,branchingVertices:t,openBookVertices:s}}buildSegments(e,t){const s=[],n=new Set,i=new Set(t.map(e=>e.id));for(const o of t){const t=this.getIncidentSkeletonEdges(o,e);for(const e of t){if(n.has(e.id))continue;const t=this.traceSegment(o,e,i,n);t&&s.push(t)}}for(const o of e){if(n.has(o.id))continue;const e=this.traceClosedLoop(o,n);e&&s.push(e)}return s}getIncidentSkeletonEdges(e,t){const s=new Set(t.map(e=>e.id)),n=[];return e.forEachOutgoingHalfedge(e=>{s.has(e.edge.id)&&(n.some(t=>t.id===e.edge.id)||n.push(e.edge))}),n}traceSegment(e,t,s,n){var i,o;const r=new I(this.createSegmentId());r.addVertex(e);let a=e,l=t;for(;l&&!n.has(l.id);){n.add(l.id),r.addEdge(l);const e=l.getOtherVertex(a);if(!e)break;if(r.addVertex(e),a=e,s.has(a.id))break;l=this.getNextSkeletonEdge(a,l,n)}return r.vertices.length>2&&(null==(i=r.startVertex)?void 0:i.id)===(null==(o=r.endVertex)?void 0:o.id)&&(r.isClosed=!0,r.vertices.pop()),r.recomputeLengths(),r}traceClosedLoop(e,t){const[s,n]=e.getVertices();if(!s||!n)return null;const i=new I(this.createSegmentId());i.addVertex(s),i.addEdge(e),i.addVertex(n),t.add(e.id);let o=n,r=this.getNextSkeletonEdge(o,e,t);for(;r&&!t.has(r.id);){t.add(r.id),i.addEdge(r);const e=r.getOtherVertex(o);if(!e)break;if(e.id===s.id){i.isClosed=!0;break}i.addVertex(e),o=e,r=this.getNextSkeletonEdge(o,r,t)}return i.recomputeLengths(),i}getNextSkeletonEdge(e,t,s){let n=null;return e.forEachOutgoingHalfedge(e=>{e.edge.id!==t.id&&e.edge.isSkeletonEdge()&&!s.has(e.edge.id)&&(n=e.edge)}),n}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 s=t.vertices[e];s&&this.vertexToSegment.set(s.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 s of this.segments.values()){const n=s.projectPoint(e);n&&(!t||n.distance<t.distance)&&(t={point:n.point,segment:s,parameter:n.parameter,distance:n.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 $(e){const t=new N(e);return t.build(),t}class A{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 s=this.skeleton.getSegmentForVertex(e);if(!s){const s=this.skeleton.projectPoint(t);return s?{position:s.point,wasConstrained:!0,segment:s.segment,constraintDistance:s.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 n=s.projectPoint(t);return n?{position:n.point,wasConstrained:!0,segment:s,constraintDistance:n.distance}:{position:{x:e.position.x,y:e.position.y,z:e.position.z},wasConstrained:!0,segment:s,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 s=t.indexOfVertex(e);if(s<0)return null;let n,i;if(0===s){const e=t.vertices[0],s=t.vertices[1];n={x:e.position.x,y:e.position.y,z:e.position.z},i={x:s.position.x,y:s.position.y,z:s.position.z}}else if(s===t.vertices.length-1){const e=t.vertices[s-1],o=t.vertices[s];n={x:e.position.x,y:e.position.y,z:e.position.z},i={x:o.position.x,y:o.position.y,z:o.position.z}}else{const e=t.vertices[s-1],o=t.vertices[s+1];n={x:e.position.x,y:e.position.y,z:e.position.z},i={x:o.position.x,y:o.position.y,z:o.position.z}}const o=i.x-n.x,a=i.y-n.y,l=i.z-n.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 L(e){return new A(e)}function T(e){const t=[],s=[],n=e.attributes.position;if(!n)return t.push("Geometry must have a position attribute"),{isValid:!1,errors:t,warnings:s};const i=e.index;if(!i)return t.push("Geometry must be indexed"),{isValid:!1,errors:t,warnings:s};i.count%3!=0&&t.push(`Index count (${i.count}) must be divisible by 3 for triangle mesh`);const o=n.count;for(let c=0;c<i.count;c++){const e=i.getX(c);if(e<0||e>=o){t.push(`Invalid index ${e} at position ${c} (vertex count: ${o})`);break}}let r=0;const a=i.count/3;for(let c=0;c<a;c++){const e=i.getX(3*c),t=i.getX(3*c+1),s=i.getX(3*c+2);e!==t&&t!==s&&s!==e||r++}r>0&&s.push(`Found ${r} degenerate triangle(s) with repeated vertices`);let l=0;for(let c=0;c<o;c++){const e=n.getX(c),t=n.getY(c),s=n.getZ(c);isFinite(e)&&isFinite(t)&&isFinite(s)||l++}return l>0&&t.push(`Found ${l} vertex position(s) with NaN or Infinity values`),{isValid:0===t.length,errors:t,warnings:s}}function P(e,t={}){const{featureEdges:s,validate:n=!0}=t;if(n){const t=T(e);if(!t.isValid)throw new Error(`Invalid geometry: ${t.errors.join("; ")}`)}return g.fromBufferGeometry(e,s)}function q(t,s={}){const{computeNormals:n=!0,smoothNormals:i=!0}=s,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[s,n,i]=t,o=l.get(s.id),r=l.get(n.id),a=l.get(i.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),n&&(i?function(t,s,n){const i=s.getVertices(),o=new Float32Array(3*i.length),r=new Uint32Array(i.length);for(const e of s.getFaces()){const t=e.getNormal();if(!t)continue;const s=e.getVertices();if(s)for(const e of s){const s=n.get(e.id);if(void 0===s)continue;const i=3*s;o[i]=(o[i]??0)+t.x,o[i+1]=(o[i+1]??0)+t.y,o[i+2]=(o[i+2]??0)+t.z,r[s]=(r[s]??0)+1}}for(let e=0;e<i.length;e++){const t=r[e]??0;if(0===t)continue;const s=3*e,n=(o[s]??0)/t,i=(o[s+1]??0)/t,a=(o[s+2]??0)/t,l=Math.sqrt(n*n+i*i+a*a);l>1e-10?(o[3*e]=n/l,o[3*e+1]=i/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 D(t){const s=new e.BufferGeometry,n=t.getSkeletonEdges();if(0===n.length)return s;const i=new Float32Array(6*n.length);let o=0;for(const e of n){const[t,s]=e.getVertices();t&&s&&(i[o++]=t.position.x,i[o++]=t.position.y,i[o++]=t.position.z,i[o++]=s.position.x,i[o++]=s.position.y,i[o++]=s.position.z)}return s.setAttribute("position",new e.BufferAttribute(i.slice(0,o),3)),s}function R(t){const s=q(t,{computeNormals:!0}),n=t.getVertices(),i=new Float32Array(3*n.length),o=new Map;n.forEach((e,t)=>{o.set(e.id,t)});for(const e of n){const t=o.get(e.id);if(void 0===t)continue;let s=0,n=0,r=0;switch(e.type){case"manifold":s=.2,n=.8,r=.2;break;case"open_book":s=.2,n=.4,r=.9;break;case"skeleton_branching":s=.9,n=.2,r=.2;break;case"non_manifold_other":s=.9,n=.2,r=.9}i[3*t]=s,i[3*t+1]=n,i[3*t+2]=r}return s.setAttribute("color",new e.BufferAttribute(i,3)),s}function G(t){const s=t.getFaces();if(0===s.length)return new e.BufferGeometry;const n=new Float32Array(9*s.length),i=new Float32Array(9*s.length),o=new Float32Array(9*s.length);let r=0;for(const e of s){const t=e.getVertices(),s=e.getNormal(),a=e.getQuality()??0;if(!t||!s)continue;const l=a<.5?1:2-2*a,c=a<.5?2*a:1,h=.1;for(const e of t)n[r]=e.position.x,n[r+1]=e.position.y,n[r+2]=e.position.z,i[r]=l,i[r+1]=c,i[r+2]=h,o[r]=s.x,o[r+1]=s.y,o[r+2]=s.z,r+=3}const a=new e.BufferGeometry;return a.setAttribute("position",new e.BufferAttribute(n.slice(0,r),3)),a.setAttribute("color",new e.BufferAttribute(i.slice(0,r),3)),a.setAttribute("normal",new e.BufferAttribute(o.slice(0,r),3)),a}class Q{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,s){return`${Math.floor(e/this.cellSize)},${Math.floor(t/this.cellSize)},${Math.floor(s/this.cellSize)}`}getCellIndices(e,t,s){return[Math.floor(e/this.cellSize),Math.floor(t/this.cellSize),Math.floor(s/this.cellSize)]}insert(e,t){const s=this.getCellKey(t.x,t.y,t.z);let n=this.cells.get(s);n||(n=[],this.cells.set(s,n)),n.push(e),this.itemPositions.set(e,t)}remove(e){const t=this.itemPositions.get(e);if(!t)return!1;const s=this.getCellKey(t.x,t.y,t.z),n=this.cells.get(s);if(n){const t=n.indexOf(e);-1!==t&&(n.splice(t,1),0===n.length&&this.cells.delete(s))}return this.itemPositions.delete(e),!0}update(e,t){this.remove(e),this.insert(e,t)}queryRadius(e,t){const s=[],n=t*t,i=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=i[0];r<=o[0];r++)for(let t=i[1];t<=o[1];t++)for(let a=i[2];a<=o[2];a++){const i=`${r},${t},${a}`,o=this.cells.get(i);if(o)for(const t of o){const i=this.itemPositions.get(t);if(i){const o=i.x-e.x,r=i.y-e.y,a=i.z-e.z;o*o+r*r+a*a<=n&&s.push(t)}}}return s}queryKNearest(e,t,s){let n=this.cellSize,i=[];for(;i.length<t&&!(void 0!==s&&n>s);){i=[];const t=this.queryRadius(e,n);for(const s of t){const t=this.itemPositions.get(s);if(t){const n=t.x-e.x,o=t.y-e.y,r=t.z-e.z;i.push({item:s,distSq:n*n+o*o+r*r})}}n*=2}return i.sort((e,t)=>e.distSq-t.distSq),i.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 K(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 X(e,t){let s=0;return e.x<t.min.x?s+=(t.min.x-e.x)**2:e.x>t.max.x&&(s+=(e.x-t.max.x)**2),e.y<t.min.y?s+=(t.min.y-e.y)**2:e.y>t.max.y&&(s+=(e.y-t.max.y)**2),e.z<t.min.z?s+=(t.min.z-e.z)**2:e.z>t.max.z&&(s+=(e.z-t.max.z)**2),s}function W(e,t){const s=t.v0,n=t.v1,i=t.v2,o=n.x-s.x,r=n.y-s.y,a=n.z-s.z,l=i.x-s.x,c=i.y-s.y,h=i.z-s.z,u=e.x-s.x,d=e.y-s.y,g=e.z-s.z,f=o*u+r*d+a*g,p=l*u+c*d+h*g;if(f<=0&&p<=0)return s;const x=e.x-n.x,y=e.y-n.y,m=e.z-n.z,v=o*x+r*y+a*m,z=l*x+c*y+h*m;if(v>=0&&z<=v)return n;const M=f*z-v*p;if(M<=0&&f>=0&&v<=0){const e=f/(f-v);return{x:s.x+e*o,y:s.y+e*r,z:s.z+e*a}}const w=e.x-i.x,V=e.y-i.y,E=e.z-i.z,k=o*w+r*V+a*E,C=l*w+c*V+h*E;if(C>=0&&k<=C)return i;const S=k*p-f*C;if(S<=0&&p>=0&&C<=0){const e=p/(p-C);return{x:s.x+e*l,y:s.y+e*c,z:s.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:n.x+e*(i.x-n.x),y:n.y+e*(i.y-n.y),z:n.z+e*(i.z-n.z)}}const F=1/(b+S+M),B=S*F,H=M*F;return{x:s.x+o*B+l*H,y:s.y+r*B+c*H,z:s.z+a*B+h*H}}class U{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=K(t,j(this.triangles[e[h]]));if(e.length<=this.maxLeafSize)return{bounds:t,triangleIndices:e};const s=t.max.x-t.min.x,n=t.max.y-t.min.y,i=t.max.z-t.min.z;let o="x";n>s&&n>i?o="y":i>s&&i>n&&(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,s=1/0;const n=[this.root];for(;n.length>0;){const i=n.pop();if(!(X(e,i.bounds)>=s))if(i.triangleIndices)for(const n of i.triangleIndices){const i=W(e,this.triangles[n]),o=f(e,i);o<s&&(s=o,t={point:i,distance:Math.sqrt(o),triangleIndex:n})}else if(i.left&&i.right){X(e,i.left.bounds)<X(e,i.right.bounds)?(n.push(i.right),n.push(i.left)):(n.push(i.left),n.push(i.right))}else i.left?n.push(i.left):i.right&&n.push(i.right)}return t}queryRadius(e,t){const s=[],n=t*t;if(!this.root)return s;const i=[this.root];for(;i.length>0;){const t=i.pop();if(!(X(e,t.bounds)>n))if(t.triangleIndices)for(const i of t.triangleIndices){f(e,W(e,this.triangles[i]))<=n&&s.push(i)}else t.left&&i.push(t.left),t.right&&i.push(t.right)}return s}get triangleCount(){return this.triangles.length}getTriangle(e){return this.triangles[e]}}function Y(e){const t=e.getEdges(),s=e.getVertices();let n=0,i=0,o=0;const l=[];for(const r of t)switch(r.type){case a.Manifold:case a.Feature:n++;break;case a.NonManifold:i++,l.push(Z(r));break;case a.Boundary:o++}let c=0,h=0,u=0;const d=[];for(const a of s){u+=a.degree()??0,a.type===r.Manifold?c++:(h++,d.push(J(a)))}const g=s.length>0?u/s.length:0,f=e.vertexCount-e.edgeCount+e.faceCount;return{isManifold:0===i,hasBoundary:o>0,vertexCount:e.vertexCount,edgeCount:e.edgeCount,faceCount:e.faceCount,manifoldEdgeCount:n,nonManifoldEdgeCount:i,boundaryEdgeCount:o,manifoldVertexCount:c,nonManifoldVertexCount:h,nonManifoldEdges:l,nonManifoldVertices:d,eulerCharacteristic:f,averageVertexDegree:g}}function Z(e){const[t,s]=e.getVertices();return{edgeId:e.id,vertexIndices:[t?t.id:-1,s?s.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},s?{x:s.position.x,y:s.position.y,z:s.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 s of e.getVertices())switch(s.type=te(s),t.total++,s.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,s=0,n=!1,i=!1;if(e.forEachOutgoingHalfedge(e=>{s++,e.edge.isSkeletonEdge()&&t++,e.edge.isBoundary()&&(n=!0),e.edge.isNonManifold()&&(i=!0)}),0===s)return r.Manifold;if(i)return 2===t?r.OpenBook:1===t||t>2?r.SkeletonBranching:r.NonManifoldOther;if(n){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 se(e,t){return e.getVertices().filter(e=>e.type===t)}function ne(e){return se(e,r.Manifold)}function ie(e){return e.getVertices().filter(e=>e.type!==r.Manifold)}function oe(e){e.classifyVertices()}function re(e){const t=[],s=[];return function(e,t){for(const s of e.getVertices())s.halfedge&&(e.getHalfedge(s.halfedge.id)||t.push({type:"invalid_vertex_halfedge",message:`Vertex ${s.id} references non-existent halfedge ${s.halfedge.id}`,elementIds:[s.id]})),isFinite(s.position.x)&&isFinite(s.position.y)&&isFinite(s.position.z)||t.push({type:"invalid_vertex_position",message:`Vertex ${s.id} has invalid position`,elementIds:[s.id]})}(e,t),function(e,t,s){for(const n of e.getEdges())if(0!==n.allHalfedges.length){e.getHalfedge(n.halfedge.id)||t.push({type:"invalid_edge_halfedge",message:`Edge ${n.id} references non-existent halfedge`,elementIds:[n.id]}),(n.length<=0||!isFinite(n.length))&&s.push({type:"invalid_edge_length",message:`Edge ${n.id} has invalid length: ${n.length}`,elementIds:[n.id]});for(const e of n.allHalfedges)e.edge.id!==n.id&&t.push({type:"halfedge_edge_mismatch",message:`Halfedge ${e.id} in edge ${n.id} references different edge ${e.edge.id}`,elementIds:[n.id,e.id]})}else t.push({type:"edge_no_halfedges",message:`Edge ${n.id} has no halfedges`,elementIds:[n.id]})}(e,t,s),function(e,t,s){var n;for(const i of e.getFaces()){if(!i.halfedge){t.push({type:"face_no_halfedge",message:`Face ${i.id} has no halfedge`,elementIds:[i.id]});continue}if(!e.getHalfedge(i.halfedge.id)){t.push({type:"invalid_face_halfedge",message:`Face ${i.id} references non-existent halfedge`,elementIds:[i.id]});continue}const o=i.getHalfedges();if(o){for(const e of o)(null==(n=e.face)?void 0:n.id)!==i.id&&t.push({type:"halfedge_face_mismatch",message:`Halfedge ${e.id} in face ${i.id} references different face`,elementIds:[i.id,e.id]});i.isDegenerate()&&s.push({type:"degenerate_face",message:`Face ${i.id} is degenerate (near-zero area)`,elementIds:[i.id]})}else t.push({type:"face_invalid_loop",message:`Face ${i.id} has invalid halfedge loop`,elementIds:[i.id]})}}(e,t,s),function(e,t){for(const s of e.getHalfedges())s.next?e.getHalfedge(s.next.id)||t.push({type:"halfedge_invalid_next",message:`Halfedge ${s.id} has invalid next pointer`,elementIds:[s.id]}):t.push({type:"halfedge_no_next",message:`Halfedge ${s.id} has no next pointer`,elementIds:[s.id]}),s.prev?e.getHalfedge(s.prev.id)||t.push({type:"halfedge_invalid_prev",message:`Halfedge ${s.id} has invalid prev pointer`,elementIds:[s.id]}):t.push({type:"halfedge_no_prev",message:`Halfedge ${s.id} has no prev pointer`,elementIds:[s.id]}),s.next&&s.next.prev!==s&&t.push({type:"halfedge_next_prev_mismatch",message:`Halfedge ${s.id}: next.prev does not point back`,elementIds:[s.id]}),s.prev&&s.prev.next!==s&&t.push({type:"halfedge_prev_next_mismatch",message:`Halfedge ${s.id}: prev.next does not point back`,elementIds:[s.id]}),s.twin&&(e.getHalfedge(s.twin.id)?s.twin.twin!==s&&t.push({type:"halfedge_twin_mismatch",message:`Halfedge ${s.id}: twin.twin does not point back`,elementIds:[s.id]}):t.push({type:"halfedge_invalid_twin",message:`Halfedge ${s.id} has invalid twin pointer`,elementIds:[s.id]})),e.getVertex(s.vertex.id)||t.push({type:"halfedge_invalid_vertex",message:`Halfedge ${s.id} references non-existent vertex`,elementIds:[s.id]}),e.getEdge(s.edge.id)||t.push({type:"halfedge_invalid_edge",message:`Halfedge ${s.id} references non-existent edge`,elementIds:[s.id]})}(e,t),{isValid:0===t.length,errors:t,warnings:s}}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&&_(t.v0,t.v1,t.v2,t.v3)}function ce(e){const t=e.halfedge,s=t.twin;if(!s||!t.face||!s.face)return null;const n=t.next,i=s.next;if(!n||!i)return null;const o=s.vertex,r=t.vertex,a=n.vertex,l=i.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 s=t.halfedge,n=s.twin;if(!n)return{success:!1,reason:"Edge has no twin"};const i=s.next,o=s.prev,r=n.next,a=n.prev,l=n.vertex,c=s.vertex,h=i.vertex,u=r.vertex,d=s.face,g=n.face;if(!d||!g)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,s.vertex=u,n.vertex=h,s.next=a,s.prev=i,a.next=i,a.prev=s,i.next=s,i.prev=a,n.next=o,n.prev=r,o.next=r,o.prev=n,r.next=n,r.prev=o,s.face=d,a.face=d,i.face=d,n.face=g,o.face=g,r.face=g,d.halfedge=s,g.halfedge=n,l.halfedge===s&&(l.halfedge=r),c.halfedge===n&&(c.halfedge=i),{success:!0,newLength:f}}function ue(e){const t=e.halfedge,s=t.twin;if(!s||!t.face||!s.face)return!0;const n=ce(e);if(!n)return!0;return de(n.v2,n.v0,n.v1)+de(n.v3,n.v0,n.v1)<=Math.PI+1e-10}function de(e,t,s){const n=t.x-e.x,i=t.y-e.y,o=t.z-e.z,r=s.x-e.x,a=s.y-e.y,l=s.z-e.z,c=n*r+i*a+o*l,h=Math.sqrt(n*n+i*i+o*o),u=Math.sqrt(r*r+a*a+l*l);if(h<1e-10||u<1e-10)return 0;const d=Math.max(-1,Math.min(1,c/(h*u)));return Math.acos(d)}function ge(e,t){const s=e.getEdges(),n=t??10*s.length;let i=0,o=0;for(;o<n;){o++;let e=!1;for(const t of s)if(!ue(t)&&le(t)){he(0,t).success&&(i++,e=!0)}if(!e)break}return i}function fe(t,s,n=.5){const[i,o]=s.getVertices();if(!i||!o)return{success:!1,reason:"Edge has no vertices"};const r=i.position.x,a=i.position.y,l=i.position.z,c={x:r+(o.position.x-r)*n,y:a+(o.position.y-a)*n,z:l+(o.position.z-l)*n},h=t.createVertex(new e.Vector3(c.x,c.y,c.z));s.isSkeletonEdge();const u=[],d=[],g=s.getFaces();if(0===g.length)return{success:!0,newVertex:h,newEdges:[],newFaces:[]};for(const e of g){if(!e)continue;const n=e.getHalfedges();if(!n)continue;let i=null;for(const e of n)if(e.edge.id===s.id){i=e;break}i&&pe(t,e,i,h,u,d)}return s.length=s.length*n,t.classifyVertices(),{success:!0,newVertex:h,newEdges:u,newFaces:d}}function pe(e,t,s,n,i,o){const r=s.next.vertex,a=s.vertex,l=e.createFace(n,a,r);o.push(l),s.vertex=n,n.halfedge||(n.halfedge=s)}function xe(e,t){const s=[];let n=0;const i=[];for(const o of e.getEdges())o.length>t&&i.push(o);for(const o of i){const t=fe(e,o);t.success&&t.newVertex&&(n++,s.push(t.newVertex))}return{splitCount:n,newVertices:s}}function ye(e){const[t,s]=e.getVertices();return!(!t||!s)&&((t.type!==r.SkeletonBranching&&t.type!==r.NonManifoldOther||s.type!==r.SkeletonBranching&&s.type!==r.NonManifoldOther)&&(s.type===r.SkeletonBranching||(s.type,r.NonManifoldOther),!!function(e,t){const s=new Set,n=new Set;e.forEachNeighbor(e=>{s.add(e.id)}),t.forEachNeighbor(e=>{n.add(e.id)});let i=0;for(const a of s)a!==e.id&&a!==t.id&&n.has(a)&&i++;const o=me(e,t),r=o.length;return i<=r}(t,s)))}function me(e,t){const s=new Set,n=[];return e.forEachOutgoingHalfedge(e=>{e.face&&s.add(e.face.id)}),t.forEachOutgoingHalfedge(e=>{e.face&&s.has(e.face.id)&&n.push(e.face)}),n}function ve(e,t){var s;if(!ye(t))return{success:!1,reason:"Edge cannot be contracted"};const[n,i]=t.getVertices();if(!n||!i)return{success:!1,reason:"Edge has no vertices"};const{keepVertex:o,removeVertex:a,newPosition:l}=function(e,t){const s=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}},n=s(e),i=s(t);let o,a,l;n>=i?(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}}(n,i);o.position.set(l.x,l.y,l.z);const c=me(n,i);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==(s=r.getSourceVertex())?void 0:s.id)===o.id){o.halfedge=r;break}return e.classifyVertices(),{success:!0,remainingVertex:o,removedFaces:c}}function ze(e,t){let s=0,n=0,i=[];for(const o of e.getEdges())o.length<t&&ye(o)&&i.push(o);for(;i.length>0;){const t=i.pop();if(!e.edges.has(t.id)||!ye(t))continue;ve(e,t).success&&(s++,n++)}return{contractCount:s,removedVertices:n}}function Me(e){const t=e.getNeighbors();if(0===t.length)return null;let s=0,n=0,i=0;for(const c of t)s+=c.position.x,n+=c.position.y,i+=c.position.z;s/=t.length,n/=t.length,i/=t.length;const o={x:s,y:n,z:i},r=function(e){let t=0,s=0,n=0,i=0;if(e.forEachOutgoingHalfedge(e=>{if(e.face){const o=e.face.getNormal();o&&(t+=o.x,s+=o.y,n+=o.z,i++)}}),0===i)return null;return z({x:t/i,y:s/i,z:n/i})}(e);if(!r)return o;const a={x:e.position.x,y:e.position.y,z:e.position.z},l=w(o,a);return M(a,w(l,V(r,x(l,r))))}function we(e,t,s,n){if(t.type===r.SkeletonBranching||t.type===r.NonManifoldOther)return{success:!1,reason:"Vertex is fixed"};const i={x:t.position.x,y:t.position.y,z:t.position.z};let o=s,a=!1;if(n){const e=n.constrainPosition(t,s);o=e.position,a=e.wasConstrained}if(!function(e,t){const s={x:e.position.x,y:e.position.y,z:e.position.z};e.position.set(t.x,t.y,t.z);let n=!0;return e.forEachOutgoingHalfedge(e=>{if(e.face){const t=e.face.getArea();null!==t&&t<1e-10&&(n=!1)}}),e.position.set(s.x,s.y,s.z),n}(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(i,o)}}function Ve(e,t,s,n=.5){const i=Me(t);if(!i)return{success:!1,reason:"Cannot compute smoothing target"};const o=t.position.x,r=t.position.y,a=t.position.z;return we(0,t,{x:o+(i.x-o)*n,y:r+(i.y-r)*n,z:a+(i.z-a)*n},s)}function Ee(e,t,s=.5){let n=0,i=0;for(const o of e.getVertices())if(o.type===r.Manifold||o.type===r.OpenBook){const e=Ve(0,o,t,s);e.success&&(n++,i+=e.distanceMoved??0)}return{smoothedCount:n,totalDistance:i}}function ke(e){if(0===e.length)return 0;let t=e[0];for(let s=1;s<e.length;s++){const n=e[s];n<t&&(t=n)}return t}function Ce(e){if(0===e.length)return 0;let t=e[0];for(let s=1;s<e.length;s++){const n=e[s];n>t&&(t=n)}return t}function Se(e,t=.3){const s=e.getFaces(),n=e.getEdges(),i=[],o=[];for(const v of s){const e=v.getQuality();null!==e&&i.push(e);const t=v.getArea();null!==t&&o.push(t)}const r=n.map(e=>e.length),a=ke(i),l=Ce(i),c=i.length>0?i.reduce((e,t)=>e+t,0)/i.length:0,h=i.length>0?i.reduce((e,t)=>e+Math.pow(t-c,2),0)/i.length:0,u=Math.sqrt(h),d=i.filter(e=>e<t).length,g=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:d,minEdgeLength:g,maxEdgeLength:f,averageEdgeLength:p,minArea:x,maxArea:y,totalArea:m}}function be(e,t=.3){return e.getFaces().filter(e=>{const s=e.getQuality();return null!==s&&s<t})}function Fe(e,t,s=1.333){const n=t*s;return e.getEdges().filter(e=>e.length>n)}function Be(e,t,s=.4){const n=t*s;return e.getEdges().filter(e=>e.length<n)}function He(e,t){const s=e.getVertices();if(0===s.length)return 1;let n=1/0,i=1/0,o=1/0,r=-1/0,a=-1/0,l=-1/0;for(const h of s)n=Math.min(n,h.position.x),i=Math.min(i,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-n,2)+Math.pow(a-i,2)+Math.pow(l-o,2));if(void 0!==t&&t>0){const s=Se(e);if(s.totalArea>0)return Math.sqrt(s.totalArea/(2*t))}return c/Math.sqrt(s.length)}function _e(e,t=8){return e.getVertices().filter(e=>{const s=e.degree();return null!==s&&s>t})}function Ie(e,t=4){return e.getVertices().filter(e=>{const s=e.degree();return null!==s&&s<t})}class Oe{constructor(e,t={}){this.skeleton=null,this.constraints=null,this.mesh=e;const s=t.targetEdgeLength??He(e);this.options={...l,...t,targetEdgeLength:s},this.state={iteration:0,edgeSplits:0,edgeContractions:0,edgeFlips:0,vertexRelocations:0,quality:Se(e)},e.isManifold()||(this.skeleton=$(e),this.constraints=L(this.skeleton))}iterate(){this.state.iteration++;const e=this.options.targetEdgeLength,t=e*this.options.minEdgeLengthRatio,s=e*this.options.maxEdgeLengthRatio,n=xe(this.mesh,s);this.state.edgeSplits+=n.splitCount;const i=ze(this.mesh,t);this.state.edgeContractions+=i.contractCount;const o=ge(this.mesh);this.state.edgeFlips+=o;const r=Ee(this.mesh,this.constraints??void 0,.5);return this.state.vertexRelocations+=r.smoothedCount,this.skeleton&&(n.splitCount>0||i.contractCount>0)&&this.skeleton.rebuild(),this.state.quality=Se(this.mesh,this.options.minTriangleQuality),this.options.verbose&&console.warn(`Iteration ${this.state.iteration}: splits=${n.splitCount}, contractions=${i.contractCount}, flips=${o}, smoothed=${r.smoothedCount}, avgQuality=${this.state.quality.averageQuality.toFixed(3)}`),{...this.state}}run(e){const t=e??this.options.iterations,s=Date.now(),n={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 i=Date.now()-s;return{inputVertices:n.vertices,inputFaces:n.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:i}}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 q(this.mesh)}}class Ne{constructor(e,t=!1){this.mesh=e,this.verbose=t}execute(){const e=performance.now(),t=this.detect();if(0===t)return{operation:this.getName(),defectsFound:0,defectsFixed:0,timeMs:performance.now()-e,success:!0};const s=this.repair(),n=performance.now()-e,i={operation:this.getName(),defectsFound:t,defectsFixed:s,timeMs:n,success:s===t};return s<t&&(i.reason=`Only fixed ${s}/${t} defects`),i}}class $e extends Ne{constructor(){super(...arguments),this.isolatedVertices=[]}detect(){this.isolatedVertices=[];for(const e of this.mesh.vertices.values())e.halfedge&&0!==e.degree()||this.isolatedVertices.push(e);return this.verbose&&this.isolatedVertices.length>0&&console.log(`Found ${this.isolatedVertices.length} isolated vertices`),this.isolatedVertices.length}repair(){let e=0;for(const t of this.isolatedVertices)this.mesh.vertices.delete(t.id),e++;return this.verbose&&e>0&&console.log(`Removed ${e} isolated vertices`),e}getName(){return"Remove Isolated Vertices"}canParallelize(){return this.isolatedVertices.length>1e3}}class Ae extends Ne{constructor(e,t=!1,s=1e-10){super(e,t),this.degenerateFaces=[],this.areaThreshold=s}detect(){this.degenerateFaces=[];for(const e of this.mesh.faces.values()){const t=e.getVertices();if(!t||3!==t.length)continue;const[s,n,i]=t;if(s.id===n.id||n.id===i.id||i.id===s.id){this.degenerateFaces.push(e);continue}b(s.position,n.position,i.position)<this.areaThreshold&&this.degenerateFaces.push(e)}return this.verbose&&this.degenerateFaces.length>0&&console.log(`Found ${this.degenerateFaces.length} degenerate faces`),this.degenerateFaces.length}repair(){let e=0;for(const t of this.degenerateFaces){const s=t.getHalfedges();if(s){this.mesh.faces.delete(t.id);for(const e of s)this.mesh.halfedges.delete(e.id),e.edge.allHalfedges=e.edge.allHalfedges.filter(t=>t.id!==e.id);e++}}return this.verbose&&e>0&&console.log(`Removed ${e} degenerate faces`),e}getName(){return"Remove Degenerate Faces"}canParallelize(){return this.degenerateFaces.length>1e3}}class Le extends Ne{constructor(){super(...arguments),this.duplicates=new Map}detect(){this.duplicates.clear();const e=new Map;for(const s of this.mesh.faces.values()){const t=s.getVertices();if(!t)continue;const n=this.makeFaceKey(t);e.has(n)||e.set(n,[]),e.get(n).push(s)}let t=0;for(const[s,n]of e)n.length>1&&(this.duplicates.set(s,n),t+=n.length-1);return this.verbose&&t>0&&console.log(`Found ${t} duplicate faces in ${this.duplicates.size} groups`),t}repair(){let e=0;for(const t of this.duplicates.values())for(let s=1;s<t.length;s++){const n=t[s];if(!n)continue;this.mesh.faces.delete(n.id);const i=n.getHalfedges();if(i){for(const e of i)this.mesh.halfedges.delete(e.id),e.edge.allHalfedges=e.edge.allHalfedges.filter(t=>t.id!==e.id);e++}}return this.verbose&&e>0&&console.log(`Removed ${e} duplicate faces`),e}makeFaceKey(e){return e.map(e=>e.id).sort((e,t)=>e-t).join(",")}getName(){return"Remove Duplicate Faces"}canParallelize(){return!1}}class Te{constructor(e,t){this.operations=[],this.mesh=e instanceof g?e:g.fromBufferGeometry(e),this.options={useWorkers:(null==t?void 0:t.useWorkers)??("undefined"!=typeof navigator&&this.mesh.faces.size>((null==t?void 0:t.parallelThreshold)??1e4)),workerCount:(null==t?void 0:t.workerCount)??("undefined"!=typeof navigator&&navigator.hardwareConcurrency||4),useAcceleration:(null==t?void 0:t.useAcceleration)??!0,parallelThreshold:(null==t?void 0:t.parallelThreshold)??1e4,verbose:(null==t?void 0:t.verbose)??!1,validateAfterEach:(null==t?void 0:t.validateAfterEach)??!1},this.stats={input:{vertices:this.mesh.vertices.size,faces:this.mesh.faces.size,edges:this.mesh.edges.size},output:{vertices:this.mesh.vertices.size,faces:this.mesh.faces.size,edges:this.mesh.edges.size},operations:[],totalTimeMs:0,success:!0,totalDefectsFound:0,totalDefectsFixed:0}}removeIsolatedVertices(){return this.operations.push(new $e(this.mesh,this.options.verbose)),this}removeDegenerateFaces(e){return this.operations.push(new Ae(this.mesh,this.options.verbose,e)),this}removeDuplicateFaces(){return this.operations.push(new Le(this.mesh,this.options.verbose)),this}repairAll(){return this.removeIsolatedVertices(),this.removeDuplicateFaces(),this.removeDegenerateFaces(),this}execute(){const e=performance.now();this.stats.operations=[],this.stats.totalDefectsFound=0,this.stats.totalDefectsFixed=0,this.stats.success=!0;for(const t of this.operations){const e=t.execute();if(this.stats.operations.push(e),this.stats.totalDefectsFound+=e.defectsFound,this.stats.totalDefectsFixed+=e.defectsFixed,e.success||(this.stats.success=!1),this.options.validateAfterEach){const t=re(this.mesh);if(!t.isValid){const s=[...t.errors,...t.warnings];console.warn(`Topology validation failed after ${e.operation}:`,s),this.stats.success=!1}}}return this.stats.totalTimeMs=performance.now()-e,this.stats.output={vertices:this.mesh.vertices.size,faces:this.mesh.faces.size,edges:this.mesh.edges.size},this.operations=[],this.stats}getStats(){return this.stats}getMesh(){return this.mesh}toBufferGeometry(){return q(this.mesh)}validate(){const e=re(this.mesh);return{isValid:e.isValid,errors:[...e.errors.map(e=>e.message),...e.warnings.map(e=>e.message)]}}}exports.AdaptiveRemesher=Oe,exports.BVH=U,exports.BufferGeometryExporter=class{constructor(e={}){this.options=e}export(e){return q(e,this.options)}exportSkeleton(e){return D(e)}exportClassification(e){return R(e)}exportQuality(e){return G(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 T(e)}setFeatureEdges(e){return this.options.featureEdges=e,this}setValidation(e){return this.options.validate=e,this}},exports.DEFAULT_REMESH_OPTIONS=l,exports.DegenerateFaceRepair=Ae,exports.DuplicateFaceRepair=Le,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,s=.4){return e.length<t*s}},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 ge(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,s=1.333){return e.length>t*s}},exports.EdgeType=a,exports.Face=d,exports.FeatureSkeleton=N,exports.Halfedge=h,exports.IsolatedVertexRepair=$e,exports.ManifoldAnalyzer=class{constructor(){this.mesh=null,this.cachedResult=null}load(e){return this.mesh=g.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=Y(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.MeshRepairer=Te,exports.NonManifoldMesh=g,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 Fe(this.mesh,e,t)}getShortEdges(e,t=.4){return Be(this.mesh,e,t)}computeTargetEdgeLength(e){return He(this.mesh,e)}getHighValenceVertices(e=8){return _e(this.mesh,e)}getLowValenceVertices(e=4){return Ie(this.mesh,e)}},exports.RepairOperation=Ne,exports.SkeletonBuilder=O,exports.SkeletonConstraints=A,exports.SkeletonSegment=I,exports.SpatialHash=Q,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 se(this.mesh,e)}getManifold(){return ne(this.mesh)}getNonManifold(){return ie(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 we(this.mesh,e,t,this.constraints??void 0)}smooth(e,t=.5){return Ve(this.mesh,e,this.constraints??void 0,t)}smoothAll(e=.5){return Ee(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 Y(g.fromBufferGeometry(e))},exports.analyzeMesh=Y,exports.angleAtVertex=function(e,t,s){return C(w(e,t),w(s,t))},exports.angleBetween=C,exports.barycentricCoordinates=function(e,t,s,n){const i=w(s,t),o=w(n,t),r=w(e,t),a=x(i,i),l=x(i,o),c=x(o,o),h=x(r,i),u=x(r,o),d=a*c-l*l;if(Math.abs(d)<1e-10)return null;const g=(c*h-l*u)/d,f=(a*u-l*h)/d;return{u:1-g-f,v:g,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=He,exports.computeTriangleAspectRatio=function(e){const t=e.getHalfedges();if(!t)return null;const s=t.map(e=>e.edge.length),n=ke(s),i=Ce(s);return n<1e-10?1/0:i/n},exports.contractEdge=ve,exports.contractShortEdges=ze,exports.cotangent=function(e,t,s){const n=w(e,t),i=w(s,t),o=x(n,i),r=m(y(n,i));return Math.abs(r)<1e-10?0:o/r},exports.createBVHFromMesh=function(e){const t=[];for(const n of e.getFaces()){const e=n.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 s=new U;return s.build(t),s},exports.createEdgeId=s,exports.createFaceId=i,exports.createHalfedgeId=n,exports.createRemesher=function(e,t={}){const s=g.fromBufferGeometry(e,t.featureEdges);return new Oe(s,t)},exports.createSegmentId=o,exports.createSkeleton=$,exports.createSkeletonConstraints=L,exports.createSpatialHash=function(e,t,s){let n=s;if(void 0===n&&e.length>1){let s=1/0,i=1/0,o=1/0,r=-1/0,a=-1/0,l=-1/0;for(const n of e){const e=t(n);s=Math.min(s,e.x),i=Math.min(i,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)}n=Math.sqrt((r-s)**2+(a-i)**2+(l-o)**2)/Math.sqrt(e.length)}const i=new Q(n??1);for(const o of e)i.insert(o,t(o));return i},exports.createVertexId=t,exports.cross=y,exports.distance=p,exports.distanceSquared=f,exports.dot=x,exports.exportBufferGeometry=q,exports.exportClassificationGeometry=R,exports.exportQualityGeometry=G,exports.exportSkeletonGeometry=D,exports.flipEdge=he,exports.fromVector3=function(e){return{x:e.x,y:e.y,z:e.z}},exports.getHighValenceVertices=_e,exports.getLongEdges=Fe,exports.getLowValenceVertices=Ie,exports.getManifoldVertices=ne,exports.getNonManifoldVertices=ie,exports.getOpenBookVertices=function(e){return se(e,r.OpenBook)},exports.getPoorQualityFaces=be,exports.getShortEdges=Be,exports.getSkeletonBranchingVertices=function(e){return se(e,r.SkeletonBranching)},exports.getVerticesByType=se,exports.importBufferGeometry=P,exports.isDelaunay=ue,exports.isManifold=function(e){return g.fromBufferGeometry(e).isManifold()},exports.isPointInTriangle=function(e,t,s,n){const i=w(s,t),o=w(n,t),r=w(e,t),a=x(i,i),l=x(i,o),c=x(o,o),h=x(r,i),u=x(r,o),d=a*c-l*l;if(Math.abs(d)<1e-10)return!1;const g=(c*h-l*u)/d,f=(a*u-l*h)/d;return 1-g-f>=0&&g>=0&&f>=0},exports.isPositionFixed=function(e){return"skeleton_branching"===e||"non_manifold_other"===e},exports.isQuadConvex=_,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=E,exports.makeDelaunay=ge,exports.midpoint=k,exports.normalize=z,exports.projectPointOnLine=function(e,t,s){const n=w(s,t),i=v(n);return i<1e-10?{...t}:M(t,V(n,x(w(e,t),n)/i))},exports.projectPointOnSegment=S,exports.reclassifyVertices=oe,exports.relocateVertex=we,exports.remesh=function(e,t={}){const s=g.fromBufferGeometry(e,t.featureEdges),n=new Oe(s,t),i=n.run();return{geometry:n.toBufferGeometry(),stats:i}},exports.removeDegenerateFaces=function(e,t){const s=new Te(e,t),n=s.removeDegenerateFaces(null==t?void 0:t.areaThreshold).execute();return{geometry:s.toBufferGeometry(),stats:n}},exports.removeDuplicateFaces=function(e,t){const s=new Te(e,t),n=s.removeDuplicateFaces().execute();return{geometry:s.toBufferGeometry(),stats:n}},exports.removeIsolatedVertices=function(e,t){const s=new Te(e,t),n=s.removeIsolatedVertices().execute();return{geometry:s.toBufferGeometry(),stats:n}},exports.repairMesh=function(e,t){const s=new Te(e,t),n=s.repairAll().execute();return{geometry:s.toBufferGeometry(),stats:n}},exports.scale=V,exports.smoothAllVertices=Ee,exports.smoothVertex=Ve,exports.splitEdge=fe,exports.splitLongEdges=xe,exports.subtract=w,exports.toNumber=function(e){return e},exports.triangleArea=b,exports.triangleCentroid=function(e,t,s){return{x:(e.x+t.x+s.x)/3,y:(e.y+t.y+s.y)/3,z:(e.z+t.z+s.z)/3}},exports.triangleCircumcenter=function(e,t,s){const n=w(t,e),i=w(s,e),o=y(n,i),r=2*v(o);if(r<1e-10)return null;const a=v(n),l=v(i),c=V(y(o,n),l),h=V(y(i,o),a),u=V(M(c,h),1/r);return M(e,u)},exports.triangleCircumradius=B,exports.triangleInradius=H,exports.triangleNormal=F,exports.triangleQuality=function(e,t,s){const n=H(e,t,s),i=B(e,t,s);return null===n||null===i||i<1e-10?0:Math.max(0,Math.min(1,2*n/i))},exports.validateGeometry=T,exports.validateTopology=re;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("three");function t(e){return e}function s(e){return e}function n(e){return e}function i(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,s=0;do{if(e(t),!t.twin||!t.twin.next)break;if(t=t.twin.next,s++,s>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,s){this.id=e,this.twin=null,this.next=null,this.prev=null,this.face=null,this.vertex=t,this.edge=s}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,s){this.id=e,this.allHalfedges=[],this.type=a.Manifold,this.isInPath=!1,this.halfedge=t,this.length=s,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 s=e.degree(),n=t.degree();return!(null===s||null===n||s<=1||n<=1)}getOtherVertex(e){const[t,s]=this.getVertices();return t&&s?e.id===t.id?s:e.id===s.id?t:null:null}markAsFeature(){this.type===a.Manifold&&(this.type=a.Feature)}}class d{constructor(e,t){this.id=e,this.isMarked=!1,this.halfedge=t}getVertices(){const e=this.halfedge,t=e.next,s=null==t?void 0:t.next;return t&&s?[e.vertex,t.vertex,s.vertex]:null}getHalfedges(){const e=this.halfedge,t=e.next,s=null==t?void 0:t.next;return t&&s?[e,t,s]:null}forEachHalfedge(e){const t=this.getHalfedges();if(t)for(const s of t)e(s)}forEachVertex(e){const t=this.getVertices();if(t)for(const s of t)e(s)}getCentroid(){const e=this.getVertices();if(!e)return null;const[t,s,n]=e;return{x:(t.position.x+s.position.x+n.position.x)/3,y:(t.position.y+s.position.y+n.position.y)/3,z:(t.position.z+s.position.z+n.position.z)/3}}getNormal(){const e=this.getVertices();if(!e)return null;const[t,s,n]=e,i=s.position.x-t.position.x,o=s.position.y-t.position.y,r=s.position.z-t.position.z,a=n.position.x-t.position.x,l=n.position.y-t.position.y,c=n.position.z-t.position.z,h=o*c-r*l,u=r*a-i*c,d=i*l-o*a,g=Math.sqrt(h*h+u*u+d*d);return g<1e-10?{x:0,y:0,z:1}:{x:h/g,y:u/g,z:d/g}}getArea(){const e=this.getVertices();if(!e)return null;const[t,s,n]=e,i=s.position.x-t.position.x,o=s.position.y-t.position.y,r=s.position.z-t.position.z,a=n.position.x-t.position.x,l=n.position.y-t.position.y,c=n.position.z-t.position.z,h=o*c-r*l,u=r*a-i*c,d=i*l-o*a;return Math.sqrt(h*h+u*u+d*d)/2}getQuality(){const e=this.getVertices();if(!e)return null;const[t,s,n]=e,i=Math.sqrt(Math.pow(s.position.x-n.position.x,2)+Math.pow(s.position.y-n.position.y,2)+Math.pow(s.position.z-n.position.z,2)),o=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)),r=Math.sqrt(Math.pow(t.position.x-s.position.x,2)+Math.pow(t.position.y-s.position.y,2)+Math.pow(t.position.z-s.position.z,2)),a=(i+o+r)/2,l=a*(a-i)*(a-o)*(a-r);if(l<=0)return 0;const c=Math.sqrt(l),h=i*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 s of t){const t=s.getSourceVertex();if(t&&t.id!==e.id&&s.vertex.id!==e.id)return s}return null}isDegenerate(e=1e-10){const t=this.getArea();return null===t||t<e}}class g{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(s,o){const r=new g,a=s.attributes.position;if(!a)throw new Error("Geometry must have a position attribute");const l=s.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 n=0;n<u;n++){const s=a.getX(n),i=a.getY(n),o=a.getZ(n),l=new c(t(r.nextVertexId++),new e.Vector3(s,i,o));r.vertices.set(l.id,l)}for(let e=0;e<f;e++){const s=l.getX(3*e+0),o=l.getX(3*e+1),a=l.getX(3*e+2),c=r.vertices.get(t(s)),u=r.vertices.get(t(o)),g=r.vertices.get(t(a));if(!c||!u||!g)throw new Error(`Vertex not found in face ${e}: ${s}, ${o}, ${a}`);const f=r.getOrCreateEdge(s,o),p=r.getOrCreateEdge(o,a),x=r.getOrCreateEdge(a,s),m=new d(i(r.nextFaceId++),null);r.faces.set(m.id,m);const y=new h(n(r.nextHalfedgeId++),u,f),v=new h(n(r.nextHalfedgeId++),g,p),z=new h(n(r.nextHalfedgeId++),c,x);r.halfedges.set(y.id,y),r.halfedges.set(v.id,v),r.halfedges.set(z.id,z),f.addHalfedge(y),p.addHalfedge(v),x.addHalfedge(z),y.next=v,v.next=z,z.next=y,y.prev=z,v.prev=y,z.prev=v,y.face=m,v.face=m,z.face=m,m.halfedge=y,c.halfedge||(c.halfedge=y),u.halfedge||(u.halfedge=v),g.halfedge||(g.halfedge=z),f.halfedge=y,p.halfedge=v,x.halfedge=z}return r.setupTwinHalfedges(),o&&o.length>0&&r.markFeatureEdges(o),r.classifyVertices(),r}getOrCreateEdge(e,t){const s=this.makeEdgeKey(e,t);let n=this.edgeMap.get(s);if(!n){const i=this.vertices.get(e),o=this.vertices.get(t);if(!i||!o)throw new Error(`Vertex not found: ${e} or ${t}`);const r=o.position.x-i.position.x,a=o.position.y-i.position.y,l=o.position.z-i.position.z,c=Math.sqrt(r*r+a*a+l*l);n=new u(this.nextEdgeId++,null,c),n.allHalfedges=[],this.edgeMap.set(s,n),this.edges.set(n.id,n)}return n}makeEdgeKey(e,t){return e<t?`${e},${t}`:`${t},${e}`}setupTwinHalfedges(){var e;for(const t of this.edges.values()){const e=t.allHalfedges,s=e.length;0!==s&&(1===s?(e[0].twin=null,t.type=a.Boundary):2===s?(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 s=null==(e=t.prev)?void 0:e.vertex;s&&(s.halfedge=t)}}setupNonManifoldTwins(e,t){const[s,n]=e.getVertices();if(!s||!n)return;const i=[],o=[];for(const a of t)a.vertex.id===s.id?i.push(a):o.push(a);const r=Math.min(i.length,o.length);for(let a=0;a<r;a++)i[a].twin=o[a],o[a].twin=i[a];for(let a=r;a<i.length;a++)i[a].twin=null;for(let a=r;a<o.length;a++)o[a].twin=null}markFeatureEdges(e){for(const[t,s]of e){const e=this.makeEdgeKey(t,s),n=this.edgeMap.get(e);n&&n.type===a.Manifold&&n.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 s=this.makeEdgeKey(e.id,t.id);return this.edgeMap.get(s)}createVertex(e){const t=new c(this.nextVertexId++,e);return this.vertices.set(t.id,t),t}createFace(e,t,s){const n=this.getOrCreateEdge(e.id,t.id),i=this.getOrCreateEdge(t.id,s.id),o=this.getOrCreateEdge(s.id,e.id),r=new d(this.nextFaceId++,null);this.faces.set(r.id,r);const a=new h(this.nextHalfedgeId++,t,n),l=new h(this.nextHalfedgeId++,s,i),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),n.addHalfedge(a),i.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),s.halfedge||(s.halfedge=c),n.halfedge=a,i.halfedge=l,o.halfedge=c,r}getStats(){const e=this.getNonManifoldEdges().length,t=this.getBoundaryEdges().length,s=this.getFeatureEdges().length,n=this.vertexCount-this.edgeCount+this.faceCount;return{vertexCount:this.vertexCount,edgeCount:this.edgeCount,faceCount:this.faceCount,nonManifoldEdgeCount:e,boundaryEdgeCount:t,featureEdgeCount:s,eulerCharacteristic:n}}}function f(e,t){const s=t.x-e.x,n=t.y-e.y,i=t.z-e.z;return s*s+n*n+i*i}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 m(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 y(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=y(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 w(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,y:e.y*t,z:e.z*t}}function V(e,t,s){return{x:e.x+(t.x-e.x)*s,y:e.y+(t.y-e.y)*s,z:e.z+(t.z-e.z)*s}}function k(e,t){return{x:(e.x+t.x)/2,y:(e.y+t.y)/2,z:(e.z+t.z)/2}}function F(e,t){const s=y(e),n=y(t);if(s<1e-10||n<1e-10)return 0;const i=x(e,t)/(s*n);return Math.acos(Math.max(-1,Math.min(1,i)))}function b(e,t,s){const n=w(s,t),i=v(n);if(i<1e-10)return{...t};return M(t,E(n,Math.max(0,Math.min(1,x(w(e,t),n)/i))))}function S(e,t,s){return y(m(w(t,e),w(s,e)))/2}function C(e,t,s){return z(m(w(t,e),w(s,e)))}function B(e,t,s){const n=p(t,s),i=p(e,s),o=p(e,t),r=S(e,t,s);return r<1e-10?null:n*i*o/(4*r)}function H(e,t,s){const n=(p(t,s)+p(e,s)+p(e,t))/2,i=S(e,t,s);return n<1e-10?null:i/n}function N(e,t,s,n){const i=w(s,t),o=w(n,t),r=w(e,t),a=x(i,i),l=x(i,o),c=x(o,o),h=x(r,i),u=x(r,o),d=a*c-l*l;if(Math.abs(d)<1e-10)return!1;const g=(c*h-l*u)/d,f=(a*u-l*h)/d;return 1-g-f>=0&&g>=0&&f>=0}function I(e,t,s,n){const i=z(M(C(e,t,s),C(e,n,t)));let o;o=Math.abs(i.x)<.9?z(m(i,{x:1,y:0,z:0})):z(m(i,{x:0,y:1,z:0}));const r=m(i,o),a=e=>({x:x(e,o),y:x(e,r)}),l=a(e),c=a(t),h=a(s),u=a(n),d=(e,t)=>e.x*t.y-e.y*t.x,g={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(d(g,f)*d(g,p)>=0)return!1;const y={x:u.x-h.x,y:u.y-h.y},v={x:l.x-h.x,y:l.y-h.y},w={x:c.x-h.x,y:c.y-h.y};return d(y,v)*d(y,w)<0}class _{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],s=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+=s}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],s=this.vertices[e],n=p({x:t.position.x,y:t.position.y,z:t.position.z},{x:s.position.x,y:s.position.y,z:s.position.z});this._totalLength+=n,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 n=1;n<this.vertices.length;n++){const e=this._cumulativeLengths[n-1]??0,s=this._cumulativeLengths[n]??0;if(t<=s){const i=this.vertices[n-1],o=this.vertices[n],r=s-e;if(r<1e-10)return{x:i.position.x,y:i.position.y,z:i.position.z};const a=(t-e)/r;return V({x:i.position.x,y:i.position.y,z:i.position.z},{x:o.position.x,y:o.position.y,z:o.position.z},a)}}const s=this.vertices[this.vertices.length-1];return{x:s.position.x,y:s.position.y,z:s.position.z}}projectPoint(e){if(0===this.vertices.length)return null;if(1===this.vertices.length){const t=this.vertices[0],s={x:t.position.x,y:t.position.y,z:t.position.z};return{point:s,parameter:0,distance:p(e,s)}}let t=null,s=0,n=1/0;for(let i=1;i<this.vertices.length;i++){const o=this.vertices[i-1],r=this.vertices[i],a={x:o.position.x,y:o.position.y,z:o.position.z},l=b(e,a,{x:r.position.x,y:r.position.y,z:r.position.z}),c=p(e,l);if(c<n){n=c,t=l;const e=this._cumulativeLengths[i-1]??0;if((this._cumulativeLengths[i]??0)-e<1e-10)s=e/this._totalLength;else{s=(e+p(a,l))/this._totalLength}}}return t?{point:t,parameter:s,distance:n}: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 _(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=[],s=[];for(const n of this.mesh.getVertices())n.type===r.SkeletonBranching?t.push(n):n.type===r.OpenBook&&s.push(n);return{segments:this.buildSegments(e,t),skeletonEdges:e,branchingVertices:t,openBookVertices:s}}buildSegments(e,t){const s=[],n=new Set,i=new Set(t.map(e=>e.id));for(const o of t){const t=this.getIncidentSkeletonEdges(o,e);for(const e of t){if(n.has(e.id))continue;const t=this.traceSegment(o,e,i,n);t&&s.push(t)}}for(const o of e){if(n.has(o.id))continue;const e=this.traceClosedLoop(o,n);e&&s.push(e)}return s}getIncidentSkeletonEdges(e,t){const s=new Set(t.map(e=>e.id)),n=[];return e.forEachOutgoingHalfedge(e=>{s.has(e.edge.id)&&(n.some(t=>t.id===e.edge.id)||n.push(e.edge))}),n}traceSegment(e,t,s,n){var i,o;const r=new _(this.createSegmentId());r.addVertex(e);let a=e,l=t;for(;l&&!n.has(l.id);){n.add(l.id),r.addEdge(l);const e=l.getOtherVertex(a);if(!e)break;if(r.addVertex(e),a=e,s.has(a.id))break;l=this.getNextSkeletonEdge(a,l,n)}return r.vertices.length>2&&(null==(i=r.startVertex)?void 0:i.id)===(null==(o=r.endVertex)?void 0:o.id)&&(r.isClosed=!0,r.vertices.pop()),r.recomputeLengths(),r}traceClosedLoop(e,t){const[s,n]=e.getVertices();if(!s||!n)return null;const i=new _(this.createSegmentId());i.addVertex(s),i.addEdge(e),i.addVertex(n),t.add(e.id);let o=n,r=this.getNextSkeletonEdge(o,e,t);for(;r&&!t.has(r.id);){t.add(r.id),i.addEdge(r);const e=r.getOtherVertex(o);if(!e)break;if(e.id===s.id){i.isClosed=!0;break}i.addVertex(e),o=e,r=this.getNextSkeletonEdge(o,r,t)}return i.recomputeLengths(),i}getNextSkeletonEdge(e,t,s){let n=null;return e.forEachOutgoingHalfedge(e=>{e.edge.id!==t.id&&e.edge.isSkeletonEdge()&&!s.has(e.edge.id)&&(n=e.edge)}),n}createSegmentId(){return this.nextSegmentId++}}class ${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 s=t.vertices[e];s&&this.vertexToSegment.set(s.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 s of this.segments.values()){const n=s.projectPoint(e);n&&(!t||n.distance<t.distance)&&(t={point:n.point,segment:s,parameter:n.parameter,distance:n.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 A(e){const t=new $(e);return t.build(),t}class L{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 s=this.skeleton.getSegmentForVertex(e);if(!s){const s=this.skeleton.projectPoint(t);return s?{position:s.point,wasConstrained:!0,segment:s.segment,constraintDistance:s.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 n=s.projectPoint(t);return n?{position:n.point,wasConstrained:!0,segment:s,constraintDistance:n.distance}:{position:{x:e.position.x,y:e.position.y,z:e.position.z},wasConstrained:!0,segment:s,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 s=t.indexOfVertex(e);if(s<0)return null;let n,i;if(0===s){const e=t.vertices[0],s=t.vertices[1];n={x:e.position.x,y:e.position.y,z:e.position.z},i={x:s.position.x,y:s.position.y,z:s.position.z}}else if(s===t.vertices.length-1){const e=t.vertices[s-1],o=t.vertices[s];n={x:e.position.x,y:e.position.y,z:e.position.z},i={x:o.position.x,y:o.position.y,z:o.position.z}}else{const e=t.vertices[s-1],o=t.vertices[s+1];n={x:e.position.x,y:e.position.y,z:e.position.z},i={x:o.position.x,y:o.position.y,z:o.position.z}}const o=i.x-n.x,a=i.y-n.y,l=i.z-n.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 T(e){return new L(e)}function P(e){const t=[],s=[],n=e.attributes.position;if(!n)return t.push("Geometry must have a position attribute"),{isValid:!1,errors:t,warnings:s};const i=e.index;if(!i)return t.push("Geometry must be indexed"),{isValid:!1,errors:t,warnings:s};i.count%3!=0&&t.push(`Index count (${i.count}) must be divisible by 3 for triangle mesh`);const o=n.count;for(let c=0;c<i.count;c++){const e=i.getX(c);if(e<0||e>=o){t.push(`Invalid index ${e} at position ${c} (vertex count: ${o})`);break}}let r=0;const a=i.count/3;for(let c=0;c<a;c++){const e=i.getX(3*c),t=i.getX(3*c+1),s=i.getX(3*c+2);e!==t&&t!==s&&s!==e||r++}r>0&&s.push(`Found ${r} degenerate triangle(s) with repeated vertices`);let l=0;for(let c=0;c<o;c++){const e=n.getX(c),t=n.getY(c),s=n.getZ(c);isFinite(e)&&isFinite(t)&&isFinite(s)||l++}return l>0&&t.push(`Found ${l} vertex position(s) with NaN or Infinity values`),{isValid:0===t.length,errors:t,warnings:s}}function R(e,t={}){const{featureEdges:s,validate:n=!0}=t;if(n){const t=P(e);if(!t.isValid)throw new Error(`Invalid geometry: ${t.errors.join("; ")}`)}return g.fromBufferGeometry(e,s)}function q(t,s={}){const{computeNormals:n=!0,smoothNormals:i=!0}=s,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[s,n,i]=t,o=l.get(s.id),r=l.get(n.id),a=l.get(i.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),n&&(i?function(t,s,n){const i=s.getVertices(),o=new Float32Array(3*i.length),r=new Uint32Array(i.length);for(const e of s.getFaces()){const t=e.getNormal();if(!t)continue;const s=e.getVertices();if(s)for(const e of s){const s=n.get(e.id);if(void 0===s)continue;const i=3*s;o[i]=(o[i]??0)+t.x,o[i+1]=(o[i+1]??0)+t.y,o[i+2]=(o[i+2]??0)+t.z,r[s]=(r[s]??0)+1}}for(let e=0;e<i.length;e++){const t=r[e]??0;if(0===t)continue;const s=3*e,n=(o[s]??0)/t,i=(o[s+1]??0)/t,a=(o[s+2]??0)/t,l=Math.sqrt(n*n+i*i+a*a);l>1e-10?(o[3*e]=n/l,o[3*e+1]=i/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 D(t){const s=new e.BufferGeometry,n=t.getSkeletonEdges();if(0===n.length)return s;const i=new Float32Array(6*n.length);let o=0;for(const e of n){const[t,s]=e.getVertices();t&&s&&(i[o++]=t.position.x,i[o++]=t.position.y,i[o++]=t.position.z,i[o++]=s.position.x,i[o++]=s.position.y,i[o++]=s.position.z)}return s.setAttribute("position",new e.BufferAttribute(i.slice(0,o),3)),s}function G(t){const s=q(t,{computeNormals:!0}),n=t.getVertices(),i=new Float32Array(3*n.length),o=new Map;n.forEach((e,t)=>{o.set(e.id,t)});for(const e of n){const t=o.get(e.id);if(void 0===t)continue;let s=0,n=0,r=0;switch(e.type){case"manifold":s=.2,n=.8,r=.2;break;case"open_book":s=.2,n=.4,r=.9;break;case"skeleton_branching":s=.9,n=.2,r=.2;break;case"non_manifold_other":s=.9,n=.2,r=.9}i[3*t]=s,i[3*t+1]=n,i[3*t+2]=r}return s.setAttribute("color",new e.BufferAttribute(i,3)),s}function Q(t){const s=t.getFaces();if(0===s.length)return new e.BufferGeometry;const n=new Float32Array(9*s.length),i=new Float32Array(9*s.length),o=new Float32Array(9*s.length);let r=0;for(const e of s){const t=e.getVertices(),s=e.getNormal(),a=e.getQuality()??0;if(!t||!s)continue;const l=a<.5?1:2-2*a,c=a<.5?2*a:1,h=.1;for(const e of t)n[r]=e.position.x,n[r+1]=e.position.y,n[r+2]=e.position.z,i[r]=l,i[r+1]=c,i[r+2]=h,o[r]=s.x,o[r+1]=s.y,o[r+2]=s.z,r+=3}const a=new e.BufferGeometry;return a.setAttribute("position",new e.BufferAttribute(n.slice(0,r),3)),a.setAttribute("color",new e.BufferAttribute(i.slice(0,r),3)),a.setAttribute("normal",new e.BufferAttribute(o.slice(0,r),3)),a}class j{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,s){return`${Math.floor(e/this.cellSize)},${Math.floor(t/this.cellSize)},${Math.floor(s/this.cellSize)}`}getCellIndices(e,t,s){return[Math.floor(e/this.cellSize),Math.floor(t/this.cellSize),Math.floor(s/this.cellSize)]}insert(e,t){const s=this.getCellKey(t.x,t.y,t.z);let n=this.cells.get(s);n||(n=[],this.cells.set(s,n)),n.push(e),this.itemPositions.set(e,t)}remove(e){const t=this.itemPositions.get(e);if(!t)return!1;const s=this.getCellKey(t.x,t.y,t.z),n=this.cells.get(s);if(n){const t=n.indexOf(e);-1!==t&&(n.splice(t,1),0===n.length&&this.cells.delete(s))}return this.itemPositions.delete(e),!0}update(e,t){this.remove(e),this.insert(e,t)}queryRadius(e,t){const s=[],n=t*t,i=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=i[0];r<=o[0];r++)for(let t=i[1];t<=o[1];t++)for(let a=i[2];a<=o[2];a++){const i=`${r},${t},${a}`,o=this.cells.get(i);if(o)for(const t of o){const i=this.itemPositions.get(t);if(i){const o=i.x-e.x,r=i.y-e.y,a=i.z-e.z;o*o+r*r+a*a<=n&&s.push(t)}}}return s}queryKNearest(e,t,s){let n=this.cellSize,i=[];for(;i.length<t&&!(void 0!==s&&n>s);){i=[];const t=this.queryRadius(e,n);for(const s of t){const t=this.itemPositions.get(s);if(t){const n=t.x-e.x,o=t.y-e.y,r=t.z-e.z;i.push({item:s,distSq:n*n+o*o+r*r})}}n*=2}return i.sort((e,t)=>e.distSq-t.distSq),i.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 K(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 U(e,t){let s=0;return e.x<t.min.x?s+=(t.min.x-e.x)**2:e.x>t.max.x&&(s+=(e.x-t.max.x)**2),e.y<t.min.y?s+=(t.min.y-e.y)**2:e.y>t.max.y&&(s+=(e.y-t.max.y)**2),e.z<t.min.z?s+=(t.min.z-e.z)**2:e.z>t.max.z&&(s+=(e.z-t.max.z)**2),s}function W(e,t){const s=t.v0,n=t.v1,i=t.v2,o=n.x-s.x,r=n.y-s.y,a=n.z-s.z,l=i.x-s.x,c=i.y-s.y,h=i.z-s.z,u=e.x-s.x,d=e.y-s.y,g=e.z-s.z,f=o*u+r*d+a*g,p=l*u+c*d+h*g;if(f<=0&&p<=0)return s;const x=e.x-n.x,m=e.y-n.y,y=e.z-n.z,v=o*x+r*m+a*y,z=l*x+c*m+h*y;if(v>=0&&z<=v)return n;const M=f*z-v*p;if(M<=0&&f>=0&&v<=0){const e=f/(f-v);return{x:s.x+e*o,y:s.y+e*r,z:s.z+e*a}}const w=e.x-i.x,E=e.y-i.y,V=e.z-i.z,k=o*w+r*E+a*V,F=l*w+c*E+h*V;if(F>=0&&k<=F)return i;const b=k*p-f*F;if(b<=0&&p>=0&&F<=0){const e=p/(p-F);return{x:s.x+e*l,y:s.y+e*c,z:s.z+e*h}}const S=v*F-k*z;if(S<=0&&z-v>=0&&k-F>=0){const e=(z-v)/(z-v+(k-F));return{x:n.x+e*(i.x-n.x),y:n.y+e*(i.y-n.y),z:n.z+e*(i.z-n.z)}}const C=1/(S+b+M),B=b*C,H=M*C;return{x:s.x+o*B+l*H,y:s.y+r*B+c*H,z:s.z+a*B+h*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=K(this.triangles[e[0]]);for(let h=1;h<e.length;h++)t=X(t,K(this.triangles[e[h]]));if(e.length<=this.maxLeafSize)return{bounds:t,triangleIndices:e};const s=t.max.x-t.min.x,n=t.max.y-t.min.y,i=t.max.z-t.min.z;let o="x";n>s&&n>i?o="y":i>s&&i>n&&(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,s=1/0;const n=[this.root];for(;n.length>0;){const i=n.pop();if(!(U(e,i.bounds)>=s))if(i.triangleIndices)for(const n of i.triangleIndices){const i=W(e,this.triangles[n]),o=f(e,i);o<s&&(s=o,t={point:i,distance:Math.sqrt(o),triangleIndex:n})}else if(i.left&&i.right){U(e,i.left.bounds)<U(e,i.right.bounds)?(n.push(i.right),n.push(i.left)):(n.push(i.left),n.push(i.right))}else i.left?n.push(i.left):i.right&&n.push(i.right)}return t}queryRadius(e,t){const s=[],n=t*t;if(!this.root)return s;const i=[this.root];for(;i.length>0;){const t=i.pop();if(!(U(e,t.bounds)>n))if(t.triangleIndices)for(const i of t.triangleIndices){f(e,W(e,this.triangles[i]))<=n&&s.push(i)}else t.left&&i.push(t.left),t.right&&i.push(t.right)}return s}get triangleCount(){return this.triangles.length}getTriangle(e){return this.triangles[e]}}function Z(e){const t=e.getEdges(),s=e.getVertices();let n=0,i=0,o=0;const l=[];for(const r of t)switch(r.type){case a.Manifold:case a.Feature:n++;break;case a.NonManifold:i++,l.push(J(r));break;case a.Boundary:o++}let c=0,h=0,u=0;const d=[];for(const a of s){u+=a.degree()??0,a.type===r.Manifold?c++:(h++,d.push(ee(a)))}const g=s.length>0?u/s.length:0,f=e.vertexCount-e.edgeCount+e.faceCount;return{isManifold:0===i,hasBoundary:o>0,vertexCount:e.vertexCount,edgeCount:e.edgeCount,faceCount:e.faceCount,manifoldEdgeCount:n,nonManifoldEdgeCount:i,boundaryEdgeCount:o,manifoldVertexCount:c,nonManifoldVertexCount:h,nonManifoldEdges:l,nonManifoldVertices:d,eulerCharacteristic:f,averageVertexDegree:g}}function J(e){const[t,s]=e.getVertices();return{edgeId:e.id,vertexIndices:[t?t.id:-1,s?s.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},s?{x:s.position.x,y:s.position.y,z:s.position.z}:{x:0,y:0,z:0}]}}function ee(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 te(e){const t={manifold:0,openBook:0,skeletonBranching:0,nonManifoldOther:0,total:0};for(const s of e.getVertices())switch(s.type=se(s),t.total++,s.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 se(e){let t=0,s=0,n=!1,i=!1;if(e.forEachOutgoingHalfedge(e=>{s++,e.edge.isSkeletonEdge()&&t++,e.edge.isBoundary()&&(n=!0),e.edge.isNonManifold()&&(i=!0)}),0===s)return r.Manifold;if(i)return 2===t?r.OpenBook:1===t||t>2?r.SkeletonBranching:r.NonManifoldOther;if(n){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 oe(e){return e.getVertices().filter(e=>e.type!==r.Manifold)}function re(e){e.classifyVertices()}function ae(e){const t=[],s=[];return function(e,t){for(const s of e.getVertices())s.halfedge&&(e.getHalfedge(s.halfedge.id)||t.push({type:"invalid_vertex_halfedge",message:`Vertex ${s.id} references non-existent halfedge ${s.halfedge.id}`,elementIds:[s.id]})),isFinite(s.position.x)&&isFinite(s.position.y)&&isFinite(s.position.z)||t.push({type:"invalid_vertex_position",message:`Vertex ${s.id} has invalid position`,elementIds:[s.id]})}(e,t),function(e,t,s){for(const n of e.getEdges())if(0!==n.allHalfedges.length){e.getHalfedge(n.halfedge.id)||t.push({type:"invalid_edge_halfedge",message:`Edge ${n.id} references non-existent halfedge`,elementIds:[n.id]}),(n.length<=0||!isFinite(n.length))&&s.push({type:"invalid_edge_length",message:`Edge ${n.id} has invalid length: ${n.length}`,elementIds:[n.id]});for(const e of n.allHalfedges)e.edge.id!==n.id&&t.push({type:"halfedge_edge_mismatch",message:`Halfedge ${e.id} in edge ${n.id} references different edge ${e.edge.id}`,elementIds:[n.id,e.id]})}else t.push({type:"edge_no_halfedges",message:`Edge ${n.id} has no halfedges`,elementIds:[n.id]})}(e,t,s),function(e,t,s){var n;for(const i of e.getFaces()){if(!i.halfedge){t.push({type:"face_no_halfedge",message:`Face ${i.id} has no halfedge`,elementIds:[i.id]});continue}if(!e.getHalfedge(i.halfedge.id)){t.push({type:"invalid_face_halfedge",message:`Face ${i.id} references non-existent halfedge`,elementIds:[i.id]});continue}const o=i.getHalfedges();if(o){for(const e of o)(null==(n=e.face)?void 0:n.id)!==i.id&&t.push({type:"halfedge_face_mismatch",message:`Halfedge ${e.id} in face ${i.id} references different face`,elementIds:[i.id,e.id]});i.isDegenerate()&&s.push({type:"degenerate_face",message:`Face ${i.id} is degenerate (near-zero area)`,elementIds:[i.id]})}else t.push({type:"face_invalid_loop",message:`Face ${i.id} has invalid halfedge loop`,elementIds:[i.id]})}}(e,t,s),function(e,t){for(const s of e.getHalfedges())s.next?e.getHalfedge(s.next.id)||t.push({type:"halfedge_invalid_next",message:`Halfedge ${s.id} has invalid next pointer`,elementIds:[s.id]}):t.push({type:"halfedge_no_next",message:`Halfedge ${s.id} has no next pointer`,elementIds:[s.id]}),s.prev?e.getHalfedge(s.prev.id)||t.push({type:"halfedge_invalid_prev",message:`Halfedge ${s.id} has invalid prev pointer`,elementIds:[s.id]}):t.push({type:"halfedge_no_prev",message:`Halfedge ${s.id} has no prev pointer`,elementIds:[s.id]}),s.next&&s.next.prev!==s&&t.push({type:"halfedge_next_prev_mismatch",message:`Halfedge ${s.id}: next.prev does not point back`,elementIds:[s.id]}),s.prev&&s.prev.next!==s&&t.push({type:"halfedge_prev_next_mismatch",message:`Halfedge ${s.id}: prev.next does not point back`,elementIds:[s.id]}),s.twin&&(e.getHalfedge(s.twin.id)?s.twin.twin!==s&&t.push({type:"halfedge_twin_mismatch",message:`Halfedge ${s.id}: twin.twin does not point back`,elementIds:[s.id]}):t.push({type:"halfedge_invalid_twin",message:`Halfedge ${s.id} has invalid twin pointer`,elementIds:[s.id]})),e.getVertex(s.vertex.id)||t.push({type:"halfedge_invalid_vertex",message:`Halfedge ${s.id} references non-existent vertex`,elementIds:[s.id]}),e.getEdge(s.edge.id)||t.push({type:"halfedge_invalid_edge",message:`Halfedge ${s.id} references non-existent edge`,elementIds:[s.id]})}(e,t),{isValid:0===t.length,errors:t,warnings:s}}function le(e){return ae(e).isValid}function ce(e){if(e.isSkeletonEdge())return!1;if(!e.canFlip())return!1;const t=he(e);return!!t&&I(t.v0,t.v1,t.v2,t.v3)}function he(e){const t=e.halfedge,s=t.twin;if(!s||!t.face||!s.face)return null;const n=t.next,i=s.next;if(!n||!i)return null;const o=s.vertex,r=t.vertex,a=n.vertex,l=i.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 ue(e,t){if(!ce(t))return{success:!1,reason:"Edge cannot be flipped"};const s=t.halfedge,n=s.twin;if(!n)return{success:!1,reason:"Edge has no twin"};const i=s.next,o=s.prev,r=n.next,a=n.prev,l=n.vertex,c=s.vertex,h=i.vertex,u=r.vertex,d=s.face,g=n.face;if(!d||!g)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,s.vertex=u,n.vertex=h,s.next=a,s.prev=i,a.next=i,a.prev=s,i.next=s,i.prev=a,n.next=o,n.prev=r,o.next=r,o.prev=n,r.next=n,r.prev=o,s.face=d,a.face=d,i.face=d,n.face=g,o.face=g,r.face=g,d.halfedge=s,g.halfedge=n,l.halfedge===s&&(l.halfedge=r),c.halfedge===n&&(c.halfedge=i),{success:!0,newLength:f}}function de(e){const t=e.halfedge,s=t.twin;if(!s||!t.face||!s.face)return!0;const n=he(e);if(!n)return!0;return ge(n.v2,n.v0,n.v1)+ge(n.v3,n.v0,n.v1)<=Math.PI+1e-10}function ge(e,t,s){const n=t.x-e.x,i=t.y-e.y,o=t.z-e.z,r=s.x-e.x,a=s.y-e.y,l=s.z-e.z,c=n*r+i*a+o*l,h=Math.sqrt(n*n+i*i+o*o),u=Math.sqrt(r*r+a*a+l*l);if(h<1e-10||u<1e-10)return 0;const d=Math.max(-1,Math.min(1,c/(h*u)));return Math.acos(d)}function fe(e,t){const s=e.getEdges(),n=t??10*s.length;let i=0,o=0;for(;o<n;){o++;let e=!1;for(const t of s)if(!de(t)&&ce(t)){ue(0,t).success&&(i++,e=!0)}if(!e)break}return i}function pe(t,s,n=.5){const[i,o]=s.getVertices();if(!i||!o)return{success:!1,reason:"Edge has no vertices"};const r=i.position.x,a=i.position.y,l=i.position.z,c={x:r+(o.position.x-r)*n,y:a+(o.position.y-a)*n,z:l+(o.position.z-l)*n},h=t.createVertex(new e.Vector3(c.x,c.y,c.z));s.isSkeletonEdge();const u=[],d=[],g=s.getFaces();if(0===g.length)return{success:!0,newVertex:h,newEdges:[],newFaces:[]};for(const e of g){if(!e)continue;const n=e.getHalfedges();if(!n)continue;let i=null;for(const e of n)if(e.edge.id===s.id){i=e;break}i&&xe(t,e,i,h,u,d)}return s.length=s.length*n,t.classifyVertices(),{success:!0,newVertex:h,newEdges:u,newFaces:d}}function xe(e,t,s,n,i,o){const r=s.next.vertex,a=s.vertex,l=e.createFace(n,a,r);o.push(l),s.vertex=n,n.halfedge||(n.halfedge=s)}function me(e,t){const s=[];let n=0;const i=[];for(const o of e.getEdges())o.length>t&&i.push(o);for(const o of i){const t=pe(e,o);t.success&&t.newVertex&&(n++,s.push(t.newVertex))}return{splitCount:n,newVertices:s}}function ye(e){const[t,s]=e.getVertices();return!(!t||!s)&&((t.type!==r.SkeletonBranching&&t.type!==r.NonManifoldOther||s.type!==r.SkeletonBranching&&s.type!==r.NonManifoldOther)&&(s.type===r.SkeletonBranching||(s.type,r.NonManifoldOther),!!function(e,t){const s=new Set,n=new Set;e.forEachNeighbor(e=>{s.add(e.id)}),t.forEachNeighbor(e=>{n.add(e.id)});let i=0;for(const a of s)a!==e.id&&a!==t.id&&n.has(a)&&i++;const o=ve(e,t),r=o.length;return i<=r}(t,s)))}function ve(e,t){const s=new Set,n=[];return e.forEachOutgoingHalfedge(e=>{e.face&&s.add(e.face.id)}),t.forEachOutgoingHalfedge(e=>{e.face&&s.has(e.face.id)&&n.push(e.face)}),n}function ze(e,t){var s;if(!ye(t))return{success:!1,reason:"Edge cannot be contracted"};const[n,i]=t.getVertices();if(!n||!i)return{success:!1,reason:"Edge has no vertices"};const{keepVertex:o,removeVertex:a,newPosition:l}=function(e,t){const s=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}},n=s(e),i=s(t);let o,a,l;n>=i?(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}}(n,i);o.position.set(l.x,l.y,l.z);const c=ve(n,i);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==(s=r.getSourceVertex())?void 0:s.id)===o.id){o.halfedge=r;break}return e.classifyVertices(),{success:!0,remainingVertex:o,removedFaces:c}}function Me(e,t){let s=0,n=0,i=[];for(const o of e.getEdges())o.length<t&&ye(o)&&i.push(o);for(;i.length>0;){const t=i.pop();if(!e.edges.has(t.id)||!ye(t))continue;ze(e,t).success&&(s++,n++)}return{contractCount:s,removedVertices:n}}function we(e){const t=e.getNeighbors();if(0===t.length)return null;let s=0,n=0,i=0;for(const c of t)s+=c.position.x,n+=c.position.y,i+=c.position.z;s/=t.length,n/=t.length,i/=t.length;const o={x:s,y:n,z:i},r=function(e){let t=0,s=0,n=0,i=0;if(e.forEachOutgoingHalfedge(e=>{if(e.face){const o=e.face.getNormal();o&&(t+=o.x,s+=o.y,n+=o.z,i++)}}),0===i)return null;return z({x:t/i,y:s/i,z:n/i})}(e);if(!r)return o;const a={x:e.position.x,y:e.position.y,z:e.position.z},l=w(o,a);return M(a,w(l,E(r,x(l,r))))}function Ee(e,t,s,n){if(t.type===r.SkeletonBranching||t.type===r.NonManifoldOther)return{success:!1,reason:"Vertex is fixed"};const i={x:t.position.x,y:t.position.y,z:t.position.z};let o=s,a=!1;if(n){const e=n.constrainPosition(t,s);o=e.position,a=e.wasConstrained}if(!function(e,t){const s={x:e.position.x,y:e.position.y,z:e.position.z};e.position.set(t.x,t.y,t.z);let n=!0;return e.forEachOutgoingHalfedge(e=>{if(e.face){const t=e.face.getArea();null!==t&&t<1e-10&&(n=!1)}}),e.position.set(s.x,s.y,s.z),n}(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(i,o)}}function Ve(e,t,s,n=.5){const i=we(t);if(!i)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+(i.x-o)*n,y:r+(i.y-r)*n,z:a+(i.z-a)*n},s)}function ke(e,t,s=.5){let n=0,i=0;for(const o of e.getVertices())if(o.type===r.Manifold||o.type===r.OpenBook){const e=Ve(0,o,t,s);e.success&&(n++,i+=e.distanceMoved??0)}return{smoothedCount:n,totalDistance:i}}function Fe(e){if(0===e.length)return 0;let t=e[0];for(let s=1;s<e.length;s++){const n=e[s];n<t&&(t=n)}return t}function be(e){if(0===e.length)return 0;let t=e[0];for(let s=1;s<e.length;s++){const n=e[s];n>t&&(t=n)}return t}function Se(e,t=.3){const s=e.getFaces(),n=e.getEdges(),i=[],o=[];for(const v of s){const e=v.getQuality();null!==e&&i.push(e);const t=v.getArea();null!==t&&o.push(t)}const r=n.map(e=>e.length),a=Fe(i),l=be(i),c=i.length>0?i.reduce((e,t)=>e+t,0)/i.length:0,h=i.length>0?i.reduce((e,t)=>e+Math.pow(t-c,2),0)/i.length:0,u=Math.sqrt(h),d=i.filter(e=>e<t).length,g=Fe(r),f=be(r),p=r.length>0?r.reduce((e,t)=>e+t,0)/r.length:0,x=Fe(o),m=be(o),y=o.reduce((e,t)=>e+t,0);return{minQuality:a,maxQuality:l,averageQuality:c,stdDevQuality:u,poorQualityCount:d,minEdgeLength:g,maxEdgeLength:f,averageEdgeLength:p,minArea:x,maxArea:m,totalArea:y}}function Ce(e,t=.3){return e.getFaces().filter(e=>{const s=e.getQuality();return null!==s&&s<t})}function Be(e,t,s=1.333){const n=t*s;return e.getEdges().filter(e=>e.length>n)}function He(e,t,s=.4){const n=t*s;return e.getEdges().filter(e=>e.length<n)}function Ne(e,t){const s=e.getVertices();if(0===s.length)return 1;let n=1/0,i=1/0,o=1/0,r=-1/0,a=-1/0,l=-1/0;for(const h of s)n=Math.min(n,h.position.x),i=Math.min(i,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-n,2)+Math.pow(a-i,2)+Math.pow(l-o,2));if(void 0!==t&&t>0){const s=Se(e);if(s.totalArea>0)return Math.sqrt(s.totalArea/(2*t))}return c/Math.sqrt(s.length)}function Ie(e,t=8){return e.getVertices().filter(e=>{const s=e.degree();return null!==s&&s>t})}function _e(e,t=4){return e.getVertices().filter(e=>{const s=e.degree();return null!==s&&s<t})}class Oe{constructor(e,t={}){this.skeleton=null,this.constraints=null,this.mesh=e;const s=t.targetEdgeLength??Ne(e);this.options={...l,...t,targetEdgeLength:s},this.state={iteration:0,edgeSplits:0,edgeContractions:0,edgeFlips:0,vertexRelocations:0,quality:Se(e)},e.isManifold()||(this.skeleton=A(e),this.constraints=T(this.skeleton))}iterate(){this.state.iteration++;const e=this.options.targetEdgeLength,t=e*this.options.minEdgeLengthRatio,s=e*this.options.maxEdgeLengthRatio,n=me(this.mesh,s);this.state.edgeSplits+=n.splitCount;const i=Me(this.mesh,t);this.state.edgeContractions+=i.contractCount;const o=fe(this.mesh);this.state.edgeFlips+=o;const r=ke(this.mesh,this.constraints??void 0,.5);return this.state.vertexRelocations+=r.smoothedCount,this.skeleton&&(n.splitCount>0||i.contractCount>0)&&this.skeleton.rebuild(),this.state.quality=Se(this.mesh,this.options.minTriangleQuality),this.options.verbose&&console.warn(`Iteration ${this.state.iteration}: splits=${n.splitCount}, contractions=${i.contractCount}, flips=${o}, smoothed=${r.smoothedCount}, avgQuality=${this.state.quality.averageQuality.toFixed(3)}`),{...this.state}}run(e){const t=e??this.options.iterations,s=Date.now(),n={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 i=Date.now()-s;return{inputVertices:n.vertices,inputFaces:n.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:i}}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 q(this.mesh)}}class $e{constructor(e,t=!1){this.mesh=e,this.verbose=t}execute(){const e=performance.now(),t=this.detect();if(0===t)return{operation:this.getName(),defectsFound:0,defectsFixed:0,timeMs:performance.now()-e,success:!0};const s=this.repair(),n=performance.now()-e,i={operation:this.getName(),defectsFound:t,defectsFixed:s,timeMs:n,success:s===t};return s<t&&(i.reason=`Only fixed ${s}/${t} defects`),i}}class Ae extends $e{constructor(){super(...arguments),this.isolatedVertices=[]}detect(){this.isolatedVertices=[];for(const e of this.mesh.vertices.values())e.halfedge&&0!==e.degree()||this.isolatedVertices.push(e);return this.verbose&&this.isolatedVertices.length>0&&console.log(`Found ${this.isolatedVertices.length} isolated vertices`),this.isolatedVertices.length}repair(){let e=0;for(const t of this.isolatedVertices)this.mesh.vertices.delete(t.id),e++;return this.verbose&&e>0&&console.log(`Removed ${e} isolated vertices`),e}getName(){return"Remove Isolated Vertices"}canParallelize(){return this.isolatedVertices.length>1e3}}class Le extends $e{constructor(e,t=!1,s=1e-10){super(e,t),this.degenerateFaces=[],this.areaThreshold=s}detect(){this.degenerateFaces=[];for(const e of this.mesh.faces.values()){const t=e.getVertices();if(!t||3!==t.length)continue;const[s,n,i]=t;if(s.id===n.id||n.id===i.id||i.id===s.id){this.degenerateFaces.push(e);continue}S(s.position,n.position,i.position)<this.areaThreshold&&this.degenerateFaces.push(e)}return this.verbose&&this.degenerateFaces.length>0&&console.log(`Found ${this.degenerateFaces.length} degenerate faces`),this.degenerateFaces.length}repair(){let e=0;for(const t of this.degenerateFaces){const s=t.getHalfedges();if(s){this.mesh.faces.delete(t.id);for(const e of s)this.mesh.halfedges.delete(e.id),e.edge.allHalfedges=e.edge.allHalfedges.filter(t=>t.id!==e.id);e++}}return this.verbose&&e>0&&console.log(`Removed ${e} degenerate faces`),e}getName(){return"Remove Degenerate Faces"}canParallelize(){return this.degenerateFaces.length>1e3}}class Te extends $e{constructor(){super(...arguments),this.duplicates=new Map}detect(){this.duplicates.clear();const e=new Map;for(const s of this.mesh.faces.values()){const t=s.getVertices();if(!t)continue;const n=this.makeFaceKey(t);e.has(n)||e.set(n,[]),e.get(n).push(s)}let t=0;for(const[s,n]of e)n.length>1&&(this.duplicates.set(s,n),t+=n.length-1);return this.verbose&&t>0&&console.log(`Found ${t} duplicate faces in ${this.duplicates.size} groups`),t}repair(){let e=0;for(const t of this.duplicates.values())for(let s=1;s<t.length;s++){const n=t[s];if(!n)continue;this.mesh.faces.delete(n.id);const i=n.getHalfedges();if(i){for(const e of i)this.mesh.halfedges.delete(e.id),e.edge.allHalfedges=e.edge.allHalfedges.filter(t=>t.id!==e.id);e++}}return this.verbose&&e>0&&console.log(`Removed ${e} duplicate faces`),e}makeFaceKey(e){return e.map(e=>e.id).sort((e,t)=>e-t).join(",")}getName(){return"Remove Duplicate Faces"}canParallelize(){return!1}}class Pe extends $e{constructor(e,t=!1,s="auto"){super(e,t),this.nonManifoldEdges=[],this.strategy=s}detect(){this.nonManifoldEdges=[];for(const e of this.mesh.edges.values())e.allHalfedges.length>2&&this.nonManifoldEdges.push(e);return this.verbose&&this.nonManifoldEdges.length>0&&console.log(`Found ${this.nonManifoldEdges.length} non-manifold edges`),this.nonManifoldEdges.length}repair(){let e=0;for(const t of this.nonManifoldEdges){if(!this.mesh.edges.has(t.id))continue;"split"===this.determineStrategy(t)?this.splitNonManifoldEdge(t)&&e++:this.collapseNonManifoldEdge(t)&&e++}return this.verbose&&e>0&&console.log(`Repaired ${e} non-manifold edges`),e}determineStrategy(e){if("auto"!==this.strategy)return this.strategy;const t=this.computeAverageEdgeLength();return e.length>t?"split":"collapse"}computeAverageEdgeLength(){let e=0,t=0;for(const s of this.mesh.edges.values())e+=s.length,t++;return t>0?e/t:1}splitNonManifoldEdge(e){try{const[t,s]=e.getVertices();if(!t||!s)return!1;const n=[...e.allHalfedges];if(n.length<=2)return!1;for(let e=2;e<n.length;e++){const t=n[e];if(!t||!t.face)continue;const i=this.mesh.createVertex(s.position.clone()),o=t.face.getVertices();if(!o)continue;const r=[];for(const e of o)e.id===s.id?r.push(i.id):r.push(e.id);this.mesh.faces.delete(t.face.id);const a=r.map(e=>this.mesh.vertices.get(e));a[0]&&a[1]&&a[2]&&this.mesh.createFace(a[0],a[1],a[2])}return!0}catch{return!1}}collapseNonManifoldEdge(e){try{const t=[...e.allHalfedges];if(t.length<=2)return!1;let s=0;for(let e=2;e<t.length;e++){const n=t[e];if(!n||!n.face)continue;this.mesh.faces.delete(n.face.id);const i=n.face.getHalfedges();if(i)for(const e of i)this.mesh.halfedges.delete(e.id),e.edge.allHalfedges=e.edge.allHalfedges.filter(t=>t.id!==e.id);s++}return s>0}catch{return!1}}getName(){return"Remove Non-Manifold Edges"}canParallelize(){return this.nonManifoldEdges.length>100}}class Re extends $e{constructor(e,t=!1,s=100,n=!1){super(e,t),this.holes=[],this.maxHoleSize=s,this.preserveBoundary=n}detect(){this.holes=[];const e=[];for(const t of this.mesh.edges.values())1===t.allHalfedges.length&&e.push(t);return 0===e.length?0:(this.holes=this.extractBoundaryLoops(e),this.holes=this.holes.filter(e=>e.vertices.length<=this.maxHoleSize),this.verbose&&this.holes.length>0&&console.log(`Found ${this.holes.length} holes`),this.holes.length)}repair(){if(this.preserveBoundary)return 0;let e=0;for(const t of this.holes){const s=this.triangulateBoundaryLoop(t);if(s.length>0){for(const e of s)try{this.mesh.createFace(e.v0,e.v1,e.v2)}catch{continue}e++}}return this.verbose&&e>0&&console.log(`Filled ${e} holes`),e}extractBoundaryLoops(e){const t=[],s=new Set;for(const n of e){if(s.has(n.id))continue;const i=[],o=[];let r=n,a=0;const l=1e4;do{if(a++>l)break;s.add(r.id);const[t,c]=r.getVertices();if(!t||!c)break;i.push(t),o.push(r);const h=this.findNextBoundaryEdge(c,r,e);if(!h||h===n){i.push(c);break}r=h}while(r!==n);i.length>=3&&t.push({vertices:i,edges:o})}return t}findNextBoundaryEdge(e,t,s){for(const n of s){if(n===t)continue;const[s,i]=n.getVertices();if(s&&i&&(s.id===e.id||i.id===e.id))return n}return null}triangulateBoundaryLoop(e){const t=[],s=[...e.vertices];let n=0;const i=s.length*s.length;for(;s.length>3&&n++<i;){const e=this.findEar(s);if(-1===e)break;const n=(e-1+s.length)%s.length,i=(e+1)%s.length,o=s[n],r=s[e],a=s[i];if(!o||!r||!a)break;t.push({v0:o,v1:r,v2:a}),s.splice(e,1)}if(3===s.length){const e=s[0],n=s[1],i=s[2];e&&n&&i&&t.push({v0:e,v1:n,v2:i})}return t}findEar(e){const t=e.length;for(let s=0;s<t;s++){const n=(s-1+t)%t,i=(s+1)%t,o=e[n],r=e[s],a=e[i];if(!o||!r||!a)continue;if(S(o.position,r.position,a.position)<1e-10)continue;let l=!0;for(let c=0;c<t;c++){if(c===n||c===s||c===i)continue;const t=e[c];if(t&&N(t.position,o.position,r.position,a.position)){l=!1;break}}if(l)return s}return-1}getName(){return"Fill Holes"}canParallelize(){return this.holes.length>10}}class qe extends $e{constructor(e,t=!1,s=0){super(e,t),this.inconsistentFaces=[],this.seedFaceIndex=s}detect(){this.inconsistentFaces=[];const e=Array.from(this.mesh.faces.values());if(0===e.length)return 0;const t=new Set,s=[],n=e[this.seedFaceIndex]||e[0];if(!n)return 0;for(s.push(n),t.add(n.id);s.length>0;){const e=s.shift(),n=this.getNeighborFaces(e);for(const i of n){const n=i.id;if(t.has(n))continue;t.add(n);const o=this.getSharedEdge(e,i);o&&!this.areNormalsConsistent(e,i,o)&&this.inconsistentFaces.push(i),s.push(i)}}return this.verbose&&this.inconsistentFaces.length>0&&console.log(`Found ${this.inconsistentFaces.length} faces with inconsistent normals`),this.inconsistentFaces.length}repair(){let e=0;for(const t of this.inconsistentFaces)this.flipFaceOrientation(t)&&e++;return this.verbose&&e>0&&console.log(`Unified normals for ${e} faces`),e}getNeighborFaces(e){const t=[],s=e.getHalfedges();if(!s)return t;for(const n of s)n.twin&&n.twin.face&&n.twin.face!==e&&t.push(n.twin.face);return t}getSharedEdge(e,t){const s=e.getHalfedges(),n=t.getHalfedges();if(!s||!n)return null;for(const i of s)for(const e of n)if(i.edge===e.edge)return i.edge;return null}areNormalsConsistent(e,t,s){const n=this.getHalfedgeInFace(e,s),i=this.getHalfedgeInFace(t,s);if(!n||!i)return!0;const[o,r]=this.getHalfedgeVertices(n),[a,l]=this.getHalfedgeVertices(i);return!(o&&r&&a&&l)||o.id===l.id&&r.id===a.id}getHalfedgeInFace(e,t){const s=e.getHalfedges();if(!s)return null;for(const n of s)if(n.edge===t)return n;return null}getHalfedgeVertices(e){var t;return[e.vertex,(null==(t=e.next)?void 0:t.vertex)||null]}flipFaceOrientation(e){try{const t=e.getHalfedges();if(!t||3!==t.length)return!1;const[s,n,i]=t,o=s.next;s.next=s.prev,s.prev=o;const r=n.next;n.next=n.prev,n.prev=r;const a=i.next;return i.next=i.prev,i.prev=a,!0}catch{return!1}}getName(){return"Unify Normals"}canParallelize(){return!1}}class De{constructor(e,t){this.operations=[],this.mesh=e instanceof g?e:g.fromBufferGeometry(e),this.options={useWorkers:(null==t?void 0:t.useWorkers)??("undefined"!=typeof navigator&&this.mesh.faces.size>((null==t?void 0:t.parallelThreshold)??1e4)),workerCount:(null==t?void 0:t.workerCount)??("undefined"!=typeof navigator&&navigator.hardwareConcurrency||4),useAcceleration:(null==t?void 0:t.useAcceleration)??!0,parallelThreshold:(null==t?void 0:t.parallelThreshold)??1e4,verbose:(null==t?void 0:t.verbose)??!1,validateAfterEach:(null==t?void 0:t.validateAfterEach)??!1},this.stats={input:{vertices:this.mesh.vertices.size,faces:this.mesh.faces.size,edges:this.mesh.edges.size},output:{vertices:this.mesh.vertices.size,faces:this.mesh.faces.size,edges:this.mesh.edges.size},operations:[],totalTimeMs:0,success:!0,totalDefectsFound:0,totalDefectsFixed:0}}removeIsolatedVertices(){return this.operations.push(new Ae(this.mesh,this.options.verbose)),this}removeDegenerateFaces(e){return this.operations.push(new Le(this.mesh,this.options.verbose,e)),this}removeDuplicateFaces(){return this.operations.push(new Te(this.mesh,this.options.verbose)),this}removeNonManifoldEdges(e){return this.operations.push(new Pe(this.mesh,this.options.verbose,e)),this}fillHoles(e){return this.operations.push(new Re(this.mesh,this.options.verbose,e)),this}unifyNormals(e){return this.operations.push(new qe(this.mesh,this.options.verbose,e)),this}repairAll(){return this.removeIsolatedVertices(),this.removeDuplicateFaces(),this.removeDegenerateFaces(),this.fillHoles(),this.unifyNormals(),this}execute(){const e=performance.now();this.stats.operations=[],this.stats.totalDefectsFound=0,this.stats.totalDefectsFixed=0,this.stats.success=!0;for(const t of this.operations){const e=t.execute();if(this.stats.operations.push(e),this.stats.totalDefectsFound+=e.defectsFound,this.stats.totalDefectsFixed+=e.defectsFixed,e.success||(this.stats.success=!1),this.options.validateAfterEach){const t=ae(this.mesh);if(!t.isValid){const s=[...t.errors,...t.warnings];console.warn(`Topology validation failed after ${e.operation}:`,s),this.stats.success=!1}}}return this.stats.totalTimeMs=performance.now()-e,this.stats.output={vertices:this.mesh.vertices.size,faces:this.mesh.faces.size,edges:this.mesh.edges.size},this.operations=[],this.stats}getStats(){return this.stats}getMesh(){return this.mesh}toBufferGeometry(){return q(this.mesh)}validate(){const e=ae(this.mesh);return{isValid:e.isValid,errors:[...e.errors.map(e=>e.message),...e.warnings.map(e=>e.message)]}}}exports.AdaptiveRemesher=Oe,exports.BVH=Y,exports.BufferGeometryExporter=class{constructor(e={}){this.options=e}export(e){return q(e,this.options)}exportSkeleton(e){return D(e)}exportClassification(e){return G(e)}exportQuality(e){return Q(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 R(e,this.options)}validate(e){return P(e)}setFeatureEdges(e){return this.options.featureEdges=e,this}setValidation(e){return this.options.validate=e,this}},exports.DEFAULT_REMESH_OPTIONS=l,exports.DegenerateFaceRepair=Le,exports.DuplicateFaceRepair=Te,exports.Edge=u,exports.EdgeContractor=class{constructor(e){this.mesh=e}contract(e){return ze(this.mesh,e)}canContract(e){return ye(e)}contractShortEdges(e){return Me(this.mesh,e)}shouldContract(e,t,s=.4){return e.length<t*s}},exports.EdgeFlipper=class{constructor(e){this.mesh=e}flip(e){return ue(this.mesh,e)}canFlip(e){return ce(e)}isDelaunay(e){return de(e)}makeDelaunay(e){return fe(this.mesh,e)}},exports.EdgeSplitter=class{constructor(e){this.mesh=e}split(e,t=.5){return pe(this.mesh,e,t)}splitLongEdges(e){return me(this.mesh,e)}shouldSplit(e,t,s=1.333){return e.length>t*s}},exports.EdgeType=a,exports.Face=d,exports.FeatureSkeleton=$,exports.Halfedge=h,exports.HoleFiller=Re,exports.IsolatedVertexRepair=Ae,exports.ManifoldAnalyzer=class{constructor(){this.mesh=null,this.cachedResult=null}load(e){return this.mesh=g.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.MeshRepairer=De,exports.NonManifoldEdgeRepair=Pe,exports.NonManifoldMesh=g,exports.NormalUnifier=qe,exports.QualityMetrics=class{constructor(e){this.mesh=e}computeStats(e=.3){return Se(this.mesh,e)}getPoorQualityFaces(e=.3){return Ce(this.mesh,e)}getLongEdges(e,t=1.333){return Be(this.mesh,e,t)}getShortEdges(e,t=.4){return He(this.mesh,e,t)}computeTargetEdgeLength(e){return Ne(this.mesh,e)}getHighValenceVertices(e=8){return Ie(this.mesh,e)}getLowValenceVertices(e=4){return _e(this.mesh,e)}},exports.RepairOperation=$e,exports.SkeletonBuilder=O,exports.SkeletonConstraints=L,exports.SkeletonSegment=_,exports.SpatialHash=j,exports.TopologyValidator=class{constructor(e){this.mesh=e}validate(){return ae(this.mesh)}isValid(){return le(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 te(this.mesh)}getByType(e){return ne(this.mesh,e)}getManifold(){return ie(this.mesh)}getNonManifold(){return oe(this.mesh)}reclassify(){re(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 Ve(this.mesh,e,this.constraints??void 0,t)}smoothAll(e=.5){return ke(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(g.fromBufferGeometry(e))},exports.analyzeMesh=Z,exports.angleAtVertex=function(e,t,s){return F(w(e,t),w(s,t))},exports.angleBetween=F,exports.barycentricCoordinates=function(e,t,s,n){const i=w(s,t),o=w(n,t),r=w(e,t),a=x(i,i),l=x(i,o),c=x(o,o),h=x(r,i),u=x(r,o),d=a*c-l*l;if(Math.abs(d)<1e-10)return null;const g=(c*h-l*u)/d,f=(a*u-l*h)/d;return{u:1-g-f,v:g,w:f}},exports.buildSkeleton=function(e){return new O(e).build()},exports.canContractEdge=ye,exports.canFlipEdge=function(e){return"manifold"===e},exports.canFlipEdgeGeometric=ce,exports.canMoveFreely=function(e){return"manifold"===e},exports.classifyAllVertices=te,exports.classifyVertex=se,exports.computeMeshQuality=Se,exports.computeTangentialSmoothing=we,exports.computeTargetEdgeLength=Ne,exports.computeTriangleAspectRatio=function(e){const t=e.getHalfedges();if(!t)return null;const s=t.map(e=>e.edge.length),n=Fe(s),i=be(s);return n<1e-10?1/0:i/n},exports.contractEdge=ze,exports.contractShortEdges=Me,exports.cotangent=function(e,t,s){const n=w(e,t),i=w(s,t),o=x(n,i),r=y(m(n,i));return Math.abs(r)<1e-10?0:o/r},exports.createBVHFromMesh=function(e){const t=[];for(const n of e.getFaces()){const e=n.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 s=new Y;return s.build(t),s},exports.createEdgeId=s,exports.createFaceId=i,exports.createHalfedgeId=n,exports.createRemesher=function(e,t={}){const s=g.fromBufferGeometry(e,t.featureEdges);return new Oe(s,t)},exports.createSegmentId=o,exports.createSkeleton=A,exports.createSkeletonConstraints=T,exports.createSpatialHash=function(e,t,s){let n=s;if(void 0===n&&e.length>1){let s=1/0,i=1/0,o=1/0,r=-1/0,a=-1/0,l=-1/0;for(const n of e){const e=t(n);s=Math.min(s,e.x),i=Math.min(i,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)}n=Math.sqrt((r-s)**2+(a-i)**2+(l-o)**2)/Math.sqrt(e.length)}const i=new j(n??1);for(const o of e)i.insert(o,t(o));return i},exports.createVertexId=t,exports.cross=m,exports.distance=p,exports.distanceSquared=f,exports.dot=x,exports.exportBufferGeometry=q,exports.exportClassificationGeometry=G,exports.exportQualityGeometry=Q,exports.exportSkeletonGeometry=D,exports.fillHoles=function(e,t){const s=new De(e,t),n=s.fillHoles(null==t?void 0:t.maxHoleSize).execute();return{geometry:s.toBufferGeometry(),stats:n}},exports.flipEdge=ue,exports.fromVector3=function(e){return{x:e.x,y:e.y,z:e.z}},exports.getHighValenceVertices=Ie,exports.getLongEdges=Be,exports.getLowValenceVertices=_e,exports.getManifoldVertices=ie,exports.getNonManifoldVertices=oe,exports.getOpenBookVertices=function(e){return ne(e,r.OpenBook)},exports.getPoorQualityFaces=Ce,exports.getShortEdges=He,exports.getSkeletonBranchingVertices=function(e){return ne(e,r.SkeletonBranching)},exports.getVerticesByType=ne,exports.importBufferGeometry=R,exports.isDelaunay=de,exports.isManifold=function(e){return g.fromBufferGeometry(e).isManifold()},exports.isPointInTriangle=N,exports.isPositionFixed=function(e){return"skeleton_branching"===e||"non_manifold_other"===e},exports.isQuadConvex=I,exports.isSkeletonConstrained=function(e){return"open_book"===e},exports.isSkeletonEdge=function(e){return"non_manifold"===e||"feature"===e||"boundary"===e},exports.isTopologyValid=le,exports.length=y,exports.lengthSquared=v,exports.lerp=V,exports.makeDelaunay=fe,exports.midpoint=k,exports.normalize=z,exports.projectPointOnLine=function(e,t,s){const n=w(s,t),i=v(n);return i<1e-10?{...t}:M(t,E(n,x(w(e,t),n)/i))},exports.projectPointOnSegment=b,exports.reclassifyVertices=re,exports.relocateVertex=Ee,exports.remesh=function(e,t={}){const s=g.fromBufferGeometry(e,t.featureEdges),n=new Oe(s,t),i=n.run();return{geometry:n.toBufferGeometry(),stats:i}},exports.removeDegenerateFaces=function(e,t){const s=new De(e,t),n=s.removeDegenerateFaces(null==t?void 0:t.areaThreshold).execute();return{geometry:s.toBufferGeometry(),stats:n}},exports.removeDuplicateFaces=function(e,t){const s=new De(e,t),n=s.removeDuplicateFaces().execute();return{geometry:s.toBufferGeometry(),stats:n}},exports.removeIsolatedVertices=function(e,t){const s=new De(e,t),n=s.removeIsolatedVertices().execute();return{geometry:s.toBufferGeometry(),stats:n}},exports.removeNonManifoldEdges=function(e,t){const s=new De(e,t),n=s.removeNonManifoldEdges(null==t?void 0:t.strategy).execute();return{geometry:s.toBufferGeometry(),stats:n}},exports.repairMesh=function(e,t){const s=new De(e,t),n=s.repairAll().execute();return{geometry:s.toBufferGeometry(),stats:n}},exports.scale=E,exports.smoothAllVertices=ke,exports.smoothVertex=Ve,exports.splitEdge=pe,exports.splitLongEdges=me,exports.subtract=w,exports.toNumber=function(e){return e},exports.triangleArea=S,exports.triangleCentroid=function(e,t,s){return{x:(e.x+t.x+s.x)/3,y:(e.y+t.y+s.y)/3,z:(e.z+t.z+s.z)/3}},exports.triangleCircumcenter=function(e,t,s){const n=w(t,e),i=w(s,e),o=m(n,i),r=2*v(o);if(r<1e-10)return null;const a=v(n),l=v(i),c=E(m(o,n),l),h=E(m(i,o),a),u=E(M(c,h),1/r);return M(e,u)},exports.triangleCircumradius=B,exports.triangleInradius=H,exports.triangleNormal=C,exports.triangleQuality=function(e,t,s){const n=H(e,t,s),i=B(e,t,s);return null===n||null===i||i<1e-10?0:Math.max(0,Math.min(1,2*n/i))},exports.unifyNormals=function(e,t){const s=new De(e,t),n=s.unifyNormals(null==t?void 0:t.seedFaceIndex).execute();return{geometry:s.toBufferGeometry(),stats:n}},exports.validateGeometry=P,exports.validateTopology=ae;
2
2
  //# sourceMappingURL=remesh-threejs.cjs.map