remesh-threejs 0.2.0 → 0.3.0
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 +106 -5
- package/dist/index.d.ts +253 -0
- package/dist/remesh-threejs.cjs +1 -1
- package/dist/remesh-threejs.cjs.map +1 -1
- package/dist/remesh-threejs.js +389 -8
- package/dist/remesh-threejs.js.map +1 -1
- package/package.json +1 -1
package/dist/remesh-threejs.js
CHANGED
|
@@ -3925,6 +3925,24 @@ class VertexRelocator {
|
|
|
3925
3925
|
return vertex.type === VertexType.Manifold || vertex.type === VertexType.OpenBook;
|
|
3926
3926
|
}
|
|
3927
3927
|
}
|
|
3928
|
+
function safeMin(arr) {
|
|
3929
|
+
if (arr.length === 0) return 0;
|
|
3930
|
+
let min = arr[0];
|
|
3931
|
+
for (let i = 1; i < arr.length; i++) {
|
|
3932
|
+
const val = arr[i];
|
|
3933
|
+
if (val < min) min = val;
|
|
3934
|
+
}
|
|
3935
|
+
return min;
|
|
3936
|
+
}
|
|
3937
|
+
function safeMax(arr) {
|
|
3938
|
+
if (arr.length === 0) return 0;
|
|
3939
|
+
let max = arr[0];
|
|
3940
|
+
for (let i = 1; i < arr.length; i++) {
|
|
3941
|
+
const val = arr[i];
|
|
3942
|
+
if (val > max) max = val;
|
|
3943
|
+
}
|
|
3944
|
+
return max;
|
|
3945
|
+
}
|
|
3928
3946
|
function computeMeshQuality(mesh, poorQualityThreshold = 0.3) {
|
|
3929
3947
|
const faces = mesh.getFaces();
|
|
3930
3948
|
const edges = mesh.getEdges();
|
|
@@ -3941,17 +3959,17 @@ function computeMeshQuality(mesh, poorQualityThreshold = 0.3) {
|
|
|
3941
3959
|
}
|
|
3942
3960
|
}
|
|
3943
3961
|
const edgeLengths = edges.map((e) => e.length);
|
|
3944
|
-
const minQuality =
|
|
3945
|
-
const maxQuality =
|
|
3962
|
+
const minQuality = safeMin(qualities);
|
|
3963
|
+
const maxQuality = safeMax(qualities);
|
|
3946
3964
|
const averageQuality = qualities.length > 0 ? qualities.reduce((a, b) => a + b, 0) / qualities.length : 0;
|
|
3947
3965
|
const variance = qualities.length > 0 ? qualities.reduce((sum, q) => sum + Math.pow(q - averageQuality, 2), 0) / qualities.length : 0;
|
|
3948
3966
|
const stdDevQuality = Math.sqrt(variance);
|
|
3949
3967
|
const poorQualityCount = qualities.filter((q) => q < poorQualityThreshold).length;
|
|
3950
|
-
const minEdgeLength =
|
|
3951
|
-
const maxEdgeLength =
|
|
3968
|
+
const minEdgeLength = safeMin(edgeLengths);
|
|
3969
|
+
const maxEdgeLength = safeMax(edgeLengths);
|
|
3952
3970
|
const averageEdgeLength = edgeLengths.length > 0 ? edgeLengths.reduce((a, b) => a + b, 0) / edgeLengths.length : 0;
|
|
3953
|
-
const minArea =
|
|
3954
|
-
const maxArea =
|
|
3971
|
+
const minArea = safeMin(areas);
|
|
3972
|
+
const maxArea = safeMax(areas);
|
|
3955
3973
|
const totalArea = areas.reduce((a, b) => a + b, 0);
|
|
3956
3974
|
return {
|
|
3957
3975
|
minQuality,
|
|
@@ -4013,8 +4031,8 @@ function computeTriangleAspectRatio(face) {
|
|
|
4013
4031
|
return null;
|
|
4014
4032
|
}
|
|
4015
4033
|
const lengths = halfedges.map((he) => he.edge.length);
|
|
4016
|
-
const minLen =
|
|
4017
|
-
const maxLen =
|
|
4034
|
+
const minLen = safeMin(lengths);
|
|
4035
|
+
const maxLen = safeMax(lengths);
|
|
4018
4036
|
if (minLen < 1e-10) {
|
|
4019
4037
|
return Infinity;
|
|
4020
4038
|
}
|
|
@@ -4222,12 +4240,368 @@ function createRemesher(geometry, options = {}) {
|
|
|
4222
4240
|
const mesh = NonManifoldMesh.fromBufferGeometry(geometry, options.featureEdges);
|
|
4223
4241
|
return new AdaptiveRemesher(mesh, options);
|
|
4224
4242
|
}
|
|
4243
|
+
class RepairOperation {
|
|
4244
|
+
constructor(mesh, verbose = false) {
|
|
4245
|
+
this.mesh = mesh;
|
|
4246
|
+
this.verbose = verbose;
|
|
4247
|
+
}
|
|
4248
|
+
/**
|
|
4249
|
+
* Execute the operation (detect + repair).
|
|
4250
|
+
* @returns Operation statistics
|
|
4251
|
+
*/
|
|
4252
|
+
execute() {
|
|
4253
|
+
const startTime = performance.now();
|
|
4254
|
+
const defectsFound = this.detect();
|
|
4255
|
+
if (defectsFound === 0) {
|
|
4256
|
+
return {
|
|
4257
|
+
operation: this.getName(),
|
|
4258
|
+
defectsFound: 0,
|
|
4259
|
+
defectsFixed: 0,
|
|
4260
|
+
timeMs: performance.now() - startTime,
|
|
4261
|
+
success: true
|
|
4262
|
+
};
|
|
4263
|
+
}
|
|
4264
|
+
const defectsFixed = this.repair();
|
|
4265
|
+
const timeMs = performance.now() - startTime;
|
|
4266
|
+
const result = {
|
|
4267
|
+
operation: this.getName(),
|
|
4268
|
+
defectsFound,
|
|
4269
|
+
defectsFixed,
|
|
4270
|
+
timeMs,
|
|
4271
|
+
success: defectsFixed === defectsFound
|
|
4272
|
+
};
|
|
4273
|
+
if (defectsFixed < defectsFound) {
|
|
4274
|
+
result.reason = `Only fixed ${defectsFixed}/${defectsFound} defects`;
|
|
4275
|
+
}
|
|
4276
|
+
return result;
|
|
4277
|
+
}
|
|
4278
|
+
}
|
|
4279
|
+
class IsolatedVertexRepair extends RepairOperation {
|
|
4280
|
+
constructor() {
|
|
4281
|
+
super(...arguments);
|
|
4282
|
+
this.isolatedVertices = [];
|
|
4283
|
+
}
|
|
4284
|
+
detect() {
|
|
4285
|
+
this.isolatedVertices = [];
|
|
4286
|
+
for (const vertex of this.mesh.vertices.values()) {
|
|
4287
|
+
if (!vertex.halfedge || vertex.degree() === 0) {
|
|
4288
|
+
this.isolatedVertices.push(vertex);
|
|
4289
|
+
}
|
|
4290
|
+
}
|
|
4291
|
+
if (this.verbose && this.isolatedVertices.length > 0) {
|
|
4292
|
+
console.log(`Found ${this.isolatedVertices.length} isolated vertices`);
|
|
4293
|
+
}
|
|
4294
|
+
return this.isolatedVertices.length;
|
|
4295
|
+
}
|
|
4296
|
+
repair() {
|
|
4297
|
+
let fixedCount = 0;
|
|
4298
|
+
for (const vertex of this.isolatedVertices) {
|
|
4299
|
+
this.mesh.vertices.delete(vertex.id);
|
|
4300
|
+
fixedCount++;
|
|
4301
|
+
}
|
|
4302
|
+
if (this.verbose && fixedCount > 0) {
|
|
4303
|
+
console.log(`Removed ${fixedCount} isolated vertices`);
|
|
4304
|
+
}
|
|
4305
|
+
return fixedCount;
|
|
4306
|
+
}
|
|
4307
|
+
getName() {
|
|
4308
|
+
return "Remove Isolated Vertices";
|
|
4309
|
+
}
|
|
4310
|
+
canParallelize() {
|
|
4311
|
+
return this.isolatedVertices.length > 1e3;
|
|
4312
|
+
}
|
|
4313
|
+
}
|
|
4314
|
+
class DegenerateFaceRepair extends RepairOperation {
|
|
4315
|
+
constructor(mesh, verbose = false, areaThreshold = 1e-10) {
|
|
4316
|
+
super(mesh, verbose);
|
|
4317
|
+
this.degenerateFaces = [];
|
|
4318
|
+
this.areaThreshold = areaThreshold;
|
|
4319
|
+
}
|
|
4320
|
+
detect() {
|
|
4321
|
+
this.degenerateFaces = [];
|
|
4322
|
+
for (const face of this.mesh.faces.values()) {
|
|
4323
|
+
const vertices = face.getVertices();
|
|
4324
|
+
if (!vertices || vertices.length !== 3) continue;
|
|
4325
|
+
const [v0, v1, v2] = vertices;
|
|
4326
|
+
if (v0.id === v1.id || v1.id === v2.id || v2.id === v0.id) {
|
|
4327
|
+
this.degenerateFaces.push(face);
|
|
4328
|
+
continue;
|
|
4329
|
+
}
|
|
4330
|
+
const area = triangleArea(v0.position, v1.position, v2.position);
|
|
4331
|
+
if (area < this.areaThreshold) {
|
|
4332
|
+
this.degenerateFaces.push(face);
|
|
4333
|
+
}
|
|
4334
|
+
}
|
|
4335
|
+
if (this.verbose && this.degenerateFaces.length > 0) {
|
|
4336
|
+
console.log(`Found ${this.degenerateFaces.length} degenerate faces`);
|
|
4337
|
+
}
|
|
4338
|
+
return this.degenerateFaces.length;
|
|
4339
|
+
}
|
|
4340
|
+
repair() {
|
|
4341
|
+
let fixedCount = 0;
|
|
4342
|
+
for (const face of this.degenerateFaces) {
|
|
4343
|
+
const halfedges = face.getHalfedges();
|
|
4344
|
+
if (!halfedges) continue;
|
|
4345
|
+
this.mesh.faces.delete(face.id);
|
|
4346
|
+
for (const he of halfedges) {
|
|
4347
|
+
this.mesh.halfedges.delete(he.id);
|
|
4348
|
+
he.edge.allHalfedges = he.edge.allHalfedges.filter((h) => h.id !== he.id);
|
|
4349
|
+
}
|
|
4350
|
+
fixedCount++;
|
|
4351
|
+
}
|
|
4352
|
+
if (this.verbose && fixedCount > 0) {
|
|
4353
|
+
console.log(`Removed ${fixedCount} degenerate faces`);
|
|
4354
|
+
}
|
|
4355
|
+
return fixedCount;
|
|
4356
|
+
}
|
|
4357
|
+
getName() {
|
|
4358
|
+
return "Remove Degenerate Faces";
|
|
4359
|
+
}
|
|
4360
|
+
canParallelize() {
|
|
4361
|
+
return this.degenerateFaces.length > 1e3;
|
|
4362
|
+
}
|
|
4363
|
+
}
|
|
4364
|
+
class DuplicateFaceRepair extends RepairOperation {
|
|
4365
|
+
constructor() {
|
|
4366
|
+
super(...arguments);
|
|
4367
|
+
this.duplicates = /* @__PURE__ */ new Map();
|
|
4368
|
+
}
|
|
4369
|
+
detect() {
|
|
4370
|
+
this.duplicates.clear();
|
|
4371
|
+
const faceMap = /* @__PURE__ */ new Map();
|
|
4372
|
+
for (const face of this.mesh.faces.values()) {
|
|
4373
|
+
const vertices = face.getVertices();
|
|
4374
|
+
if (!vertices) continue;
|
|
4375
|
+
const key = this.makeFaceKey(vertices);
|
|
4376
|
+
if (!faceMap.has(key)) {
|
|
4377
|
+
faceMap.set(key, []);
|
|
4378
|
+
}
|
|
4379
|
+
faceMap.get(key).push(face);
|
|
4380
|
+
}
|
|
4381
|
+
let duplicateCount = 0;
|
|
4382
|
+
for (const [key, faces] of faceMap) {
|
|
4383
|
+
if (faces.length > 1) {
|
|
4384
|
+
this.duplicates.set(key, faces);
|
|
4385
|
+
duplicateCount += faces.length - 1;
|
|
4386
|
+
}
|
|
4387
|
+
}
|
|
4388
|
+
if (this.verbose && duplicateCount > 0) {
|
|
4389
|
+
console.log(`Found ${duplicateCount} duplicate faces in ${this.duplicates.size} groups`);
|
|
4390
|
+
}
|
|
4391
|
+
return duplicateCount;
|
|
4392
|
+
}
|
|
4393
|
+
repair() {
|
|
4394
|
+
let fixedCount = 0;
|
|
4395
|
+
for (const faces of this.duplicates.values()) {
|
|
4396
|
+
for (let i = 1; i < faces.length; i++) {
|
|
4397
|
+
const face = faces[i];
|
|
4398
|
+
if (!face) continue;
|
|
4399
|
+
this.mesh.faces.delete(face.id);
|
|
4400
|
+
const halfedges = face.getHalfedges();
|
|
4401
|
+
if (!halfedges) continue;
|
|
4402
|
+
for (const he of halfedges) {
|
|
4403
|
+
this.mesh.halfedges.delete(he.id);
|
|
4404
|
+
he.edge.allHalfedges = he.edge.allHalfedges.filter((h) => h.id !== he.id);
|
|
4405
|
+
}
|
|
4406
|
+
fixedCount++;
|
|
4407
|
+
}
|
|
4408
|
+
}
|
|
4409
|
+
if (this.verbose && fixedCount > 0) {
|
|
4410
|
+
console.log(`Removed ${fixedCount} duplicate faces`);
|
|
4411
|
+
}
|
|
4412
|
+
return fixedCount;
|
|
4413
|
+
}
|
|
4414
|
+
/**
|
|
4415
|
+
* Create canonical key from sorted vertex IDs.
|
|
4416
|
+
*/
|
|
4417
|
+
makeFaceKey(vertices) {
|
|
4418
|
+
const ids = vertices.map((v) => v.id).sort((a, b) => a - b);
|
|
4419
|
+
return ids.join(",");
|
|
4420
|
+
}
|
|
4421
|
+
getName() {
|
|
4422
|
+
return "Remove Duplicate Faces";
|
|
4423
|
+
}
|
|
4424
|
+
canParallelize() {
|
|
4425
|
+
return false;
|
|
4426
|
+
}
|
|
4427
|
+
}
|
|
4428
|
+
class MeshRepairer {
|
|
4429
|
+
constructor(meshOrGeometry, options) {
|
|
4430
|
+
this.operations = [];
|
|
4431
|
+
if (meshOrGeometry instanceof NonManifoldMesh) {
|
|
4432
|
+
this.mesh = meshOrGeometry;
|
|
4433
|
+
} else {
|
|
4434
|
+
this.mesh = NonManifoldMesh.fromBufferGeometry(meshOrGeometry);
|
|
4435
|
+
}
|
|
4436
|
+
this.options = {
|
|
4437
|
+
useWorkers: (options == null ? void 0 : options.useWorkers) ?? (typeof navigator !== "undefined" && this.mesh.faces.size > ((options == null ? void 0 : options.parallelThreshold) ?? 1e4)),
|
|
4438
|
+
workerCount: (options == null ? void 0 : options.workerCount) ?? (typeof navigator !== "undefined" ? navigator.hardwareConcurrency || 4 : 4),
|
|
4439
|
+
useAcceleration: (options == null ? void 0 : options.useAcceleration) ?? true,
|
|
4440
|
+
parallelThreshold: (options == null ? void 0 : options.parallelThreshold) ?? 1e4,
|
|
4441
|
+
verbose: (options == null ? void 0 : options.verbose) ?? false,
|
|
4442
|
+
validateAfterEach: (options == null ? void 0 : options.validateAfterEach) ?? false
|
|
4443
|
+
};
|
|
4444
|
+
this.stats = {
|
|
4445
|
+
input: {
|
|
4446
|
+
vertices: this.mesh.vertices.size,
|
|
4447
|
+
faces: this.mesh.faces.size,
|
|
4448
|
+
edges: this.mesh.edges.size
|
|
4449
|
+
},
|
|
4450
|
+
output: {
|
|
4451
|
+
vertices: this.mesh.vertices.size,
|
|
4452
|
+
faces: this.mesh.faces.size,
|
|
4453
|
+
edges: this.mesh.edges.size
|
|
4454
|
+
},
|
|
4455
|
+
operations: [],
|
|
4456
|
+
totalTimeMs: 0,
|
|
4457
|
+
success: true,
|
|
4458
|
+
totalDefectsFound: 0,
|
|
4459
|
+
totalDefectsFixed: 0
|
|
4460
|
+
};
|
|
4461
|
+
}
|
|
4462
|
+
/**
|
|
4463
|
+
* Remove isolated vertices (vertices with no faces).
|
|
4464
|
+
* @returns this for chaining
|
|
4465
|
+
*/
|
|
4466
|
+
removeIsolatedVertices() {
|
|
4467
|
+
this.operations.push(new IsolatedVertexRepair(this.mesh, this.options.verbose));
|
|
4468
|
+
return this;
|
|
4469
|
+
}
|
|
4470
|
+
/**
|
|
4471
|
+
* Remove zero-area and degenerate triangles.
|
|
4472
|
+
* @param areaThreshold - Minimum area threshold (default: 1e-10)
|
|
4473
|
+
* @returns this for chaining
|
|
4474
|
+
*/
|
|
4475
|
+
removeDegenerateFaces(areaThreshold) {
|
|
4476
|
+
this.operations.push(new DegenerateFaceRepair(this.mesh, this.options.verbose, areaThreshold));
|
|
4477
|
+
return this;
|
|
4478
|
+
}
|
|
4479
|
+
/**
|
|
4480
|
+
* Remove duplicate faces with identical vertices.
|
|
4481
|
+
* @returns this for chaining
|
|
4482
|
+
*/
|
|
4483
|
+
removeDuplicateFaces() {
|
|
4484
|
+
this.operations.push(new DuplicateFaceRepair(this.mesh, this.options.verbose));
|
|
4485
|
+
return this;
|
|
4486
|
+
}
|
|
4487
|
+
/**
|
|
4488
|
+
* Run all common repairs in optimal order.
|
|
4489
|
+
* @returns this for chaining
|
|
4490
|
+
*/
|
|
4491
|
+
repairAll() {
|
|
4492
|
+
this.removeIsolatedVertices();
|
|
4493
|
+
this.removeDuplicateFaces();
|
|
4494
|
+
this.removeDegenerateFaces();
|
|
4495
|
+
return this;
|
|
4496
|
+
}
|
|
4497
|
+
/**
|
|
4498
|
+
* Execute all queued operations.
|
|
4499
|
+
* @returns Repair statistics
|
|
4500
|
+
*/
|
|
4501
|
+
execute() {
|
|
4502
|
+
const startTime = performance.now();
|
|
4503
|
+
this.stats.operations = [];
|
|
4504
|
+
this.stats.totalDefectsFound = 0;
|
|
4505
|
+
this.stats.totalDefectsFixed = 0;
|
|
4506
|
+
this.stats.success = true;
|
|
4507
|
+
for (const operation of this.operations) {
|
|
4508
|
+
const opStats = operation.execute();
|
|
4509
|
+
this.stats.operations.push(opStats);
|
|
4510
|
+
this.stats.totalDefectsFound += opStats.defectsFound;
|
|
4511
|
+
this.stats.totalDefectsFixed += opStats.defectsFixed;
|
|
4512
|
+
if (!opStats.success) {
|
|
4513
|
+
this.stats.success = false;
|
|
4514
|
+
}
|
|
4515
|
+
if (this.options.validateAfterEach) {
|
|
4516
|
+
const validation = validateTopology(this.mesh);
|
|
4517
|
+
if (!validation.isValid) {
|
|
4518
|
+
const errors = [...validation.errors, ...validation.warnings];
|
|
4519
|
+
console.warn(`Topology validation failed after ${opStats.operation}:`, errors);
|
|
4520
|
+
this.stats.success = false;
|
|
4521
|
+
}
|
|
4522
|
+
}
|
|
4523
|
+
}
|
|
4524
|
+
this.stats.totalTimeMs = performance.now() - startTime;
|
|
4525
|
+
this.stats.output = {
|
|
4526
|
+
vertices: this.mesh.vertices.size,
|
|
4527
|
+
faces: this.mesh.faces.size,
|
|
4528
|
+
edges: this.mesh.edges.size
|
|
4529
|
+
};
|
|
4530
|
+
this.operations = [];
|
|
4531
|
+
return this.stats;
|
|
4532
|
+
}
|
|
4533
|
+
/**
|
|
4534
|
+
* Get current statistics.
|
|
4535
|
+
*/
|
|
4536
|
+
getStats() {
|
|
4537
|
+
return this.stats;
|
|
4538
|
+
}
|
|
4539
|
+
/**
|
|
4540
|
+
* Get the repaired mesh.
|
|
4541
|
+
*/
|
|
4542
|
+
getMesh() {
|
|
4543
|
+
return this.mesh;
|
|
4544
|
+
}
|
|
4545
|
+
/**
|
|
4546
|
+
* Export to BufferGeometry.
|
|
4547
|
+
*/
|
|
4548
|
+
toBufferGeometry() {
|
|
4549
|
+
return exportBufferGeometry(this.mesh);
|
|
4550
|
+
}
|
|
4551
|
+
/**
|
|
4552
|
+
* Validate the mesh after repairs.
|
|
4553
|
+
*/
|
|
4554
|
+
validate() {
|
|
4555
|
+
const validation = validateTopology(this.mesh);
|
|
4556
|
+
return {
|
|
4557
|
+
isValid: validation.isValid,
|
|
4558
|
+
errors: [
|
|
4559
|
+
...validation.errors.map((e) => e.message),
|
|
4560
|
+
...validation.warnings.map((w) => w.message)
|
|
4561
|
+
]
|
|
4562
|
+
};
|
|
4563
|
+
}
|
|
4564
|
+
}
|
|
4565
|
+
function repairMesh(geometry, options) {
|
|
4566
|
+
const repairer = new MeshRepairer(geometry, options);
|
|
4567
|
+
const stats = repairer.repairAll().execute();
|
|
4568
|
+
return {
|
|
4569
|
+
geometry: repairer.toBufferGeometry(),
|
|
4570
|
+
stats
|
|
4571
|
+
};
|
|
4572
|
+
}
|
|
4573
|
+
function removeIsolatedVertices(geometry, options) {
|
|
4574
|
+
const repairer = new MeshRepairer(geometry, options);
|
|
4575
|
+
const stats = repairer.removeIsolatedVertices().execute();
|
|
4576
|
+
return {
|
|
4577
|
+
geometry: repairer.toBufferGeometry(),
|
|
4578
|
+
stats
|
|
4579
|
+
};
|
|
4580
|
+
}
|
|
4581
|
+
function removeDegenerateFaces(geometry, options) {
|
|
4582
|
+
const repairer = new MeshRepairer(geometry, options);
|
|
4583
|
+
const stats = repairer.removeDegenerateFaces(options == null ? void 0 : options.areaThreshold).execute();
|
|
4584
|
+
return {
|
|
4585
|
+
geometry: repairer.toBufferGeometry(),
|
|
4586
|
+
stats
|
|
4587
|
+
};
|
|
4588
|
+
}
|
|
4589
|
+
function removeDuplicateFaces(geometry, options) {
|
|
4590
|
+
const repairer = new MeshRepairer(geometry, options);
|
|
4591
|
+
const stats = repairer.removeDuplicateFaces().execute();
|
|
4592
|
+
return {
|
|
4593
|
+
geometry: repairer.toBufferGeometry(),
|
|
4594
|
+
stats
|
|
4595
|
+
};
|
|
4596
|
+
}
|
|
4225
4597
|
export {
|
|
4226
4598
|
AdaptiveRemesher,
|
|
4227
4599
|
BVH,
|
|
4228
4600
|
BufferGeometryExporter,
|
|
4229
4601
|
BufferGeometryImporter,
|
|
4230
4602
|
DEFAULT_REMESH_OPTIONS,
|
|
4603
|
+
DegenerateFaceRepair,
|
|
4604
|
+
DuplicateFaceRepair,
|
|
4231
4605
|
Edge,
|
|
4232
4606
|
EdgeContractor,
|
|
4233
4607
|
EdgeFlipper,
|
|
@@ -4236,9 +4610,12 @@ export {
|
|
|
4236
4610
|
Face,
|
|
4237
4611
|
FeatureSkeleton,
|
|
4238
4612
|
Halfedge,
|
|
4613
|
+
IsolatedVertexRepair,
|
|
4239
4614
|
ManifoldAnalyzer,
|
|
4615
|
+
MeshRepairer,
|
|
4240
4616
|
NonManifoldMesh,
|
|
4241
4617
|
QualityMetrics,
|
|
4618
|
+
RepairOperation,
|
|
4242
4619
|
SkeletonBuilder,
|
|
4243
4620
|
SkeletonConstraints,
|
|
4244
4621
|
SkeletonSegment,
|
|
@@ -4318,6 +4695,10 @@ export {
|
|
|
4318
4695
|
reclassifyVertices,
|
|
4319
4696
|
relocateVertex,
|
|
4320
4697
|
remesh,
|
|
4698
|
+
removeDegenerateFaces,
|
|
4699
|
+
removeDuplicateFaces,
|
|
4700
|
+
removeIsolatedVertices,
|
|
4701
|
+
repairMesh,
|
|
4321
4702
|
scale,
|
|
4322
4703
|
smoothAllVertices,
|
|
4323
4704
|
smoothVertex,
|