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 +19 -1
- package/dist/index.d.ts +152 -0
- package/dist/remesh-threejs.cjs +1 -1
- package/dist/remesh-threejs.cjs.map +1 -1
- package/dist/remesh-threejs.js +488 -0
- package/dist/remesh-threejs.js.map +1 -1
- package/package.json +1 -1
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
|
|
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
|
*
|
package/dist/remesh-threejs.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("three");function t(e){return e}function 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
|