brep-io-kernel 1.0.0-ci.9
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/LICENSE.md +32 -0
- package/README.md +154 -0
- package/dist-kernel/brep-kernel.js +74699 -0
- package/package.json +58 -0
- package/src/BREP/AssemblyComponent.js +42 -0
- package/src/BREP/BREP.js +43 -0
- package/src/BREP/BetterSolid.js +805 -0
- package/src/BREP/Edge.js +103 -0
- package/src/BREP/Extrude.js +403 -0
- package/src/BREP/Face.js +187 -0
- package/src/BREP/MeshRepairer.js +634 -0
- package/src/BREP/OffsetShellSolid.js +614 -0
- package/src/BREP/PointCloudWrap.js +302 -0
- package/src/BREP/Revolve.js +345 -0
- package/src/BREP/SolidMethods/authoring.js +112 -0
- package/src/BREP/SolidMethods/booleanOps.js +230 -0
- package/src/BREP/SolidMethods/chamfer.js +122 -0
- package/src/BREP/SolidMethods/edgeResolution.js +25 -0
- package/src/BREP/SolidMethods/fillet.js +792 -0
- package/src/BREP/SolidMethods/index.js +72 -0
- package/src/BREP/SolidMethods/io.js +105 -0
- package/src/BREP/SolidMethods/lifecycle.js +103 -0
- package/src/BREP/SolidMethods/manifoldOps.js +375 -0
- package/src/BREP/SolidMethods/meshCleanup.js +2512 -0
- package/src/BREP/SolidMethods/meshQueries.js +264 -0
- package/src/BREP/SolidMethods/metadata.js +106 -0
- package/src/BREP/SolidMethods/metrics.js +51 -0
- package/src/BREP/SolidMethods/transforms.js +361 -0
- package/src/BREP/SolidMethods/visualize.js +508 -0
- package/src/BREP/SolidShared.js +26 -0
- package/src/BREP/Sweep.js +1596 -0
- package/src/BREP/Tube.js +857 -0
- package/src/BREP/Vertex.js +43 -0
- package/src/BREP/applyBooleanOperation.js +704 -0
- package/src/BREP/boundsUtils.js +48 -0
- package/src/BREP/chamfer.js +551 -0
- package/src/BREP/edgePolylineUtils.js +85 -0
- package/src/BREP/fillets/common.js +388 -0
- package/src/BREP/fillets/fillet.js +1422 -0
- package/src/BREP/fillets/filletGeometry.js +15 -0
- package/src/BREP/fillets/inset.js +389 -0
- package/src/BREP/fillets/offsetHelper.js +143 -0
- package/src/BREP/fillets/outset.js +88 -0
- package/src/BREP/helix.js +193 -0
- package/src/BREP/meshToBrep.js +234 -0
- package/src/BREP/primitives.js +279 -0
- package/src/BREP/setupManifold.js +71 -0
- package/src/BREP/threadGeometry.js +1120 -0
- package/src/BREP/triangleUtils.js +8 -0
- package/src/BREP/triangulate.js +608 -0
- package/src/FeatureRegistry.js +183 -0
- package/src/PartHistory.js +1132 -0
- package/src/UI/AccordionWidget.js +292 -0
- package/src/UI/CADmaterials.js +850 -0
- package/src/UI/EnvMonacoEditor.js +522 -0
- package/src/UI/FloatingWindow.js +396 -0
- package/src/UI/HistoryWidget.js +457 -0
- package/src/UI/MainToolbar.js +131 -0
- package/src/UI/ModelLibraryView.js +194 -0
- package/src/UI/OrthoCameraIdle.js +206 -0
- package/src/UI/PluginsWidget.js +280 -0
- package/src/UI/SceneListing.js +606 -0
- package/src/UI/SelectionFilter.js +629 -0
- package/src/UI/ViewCube.js +389 -0
- package/src/UI/assembly/AssemblyConstraintCollectionWidget.js +329 -0
- package/src/UI/assembly/AssemblyConstraintControlsWidget.js +282 -0
- package/src/UI/assembly/AssemblyConstraintsWidget.css +292 -0
- package/src/UI/assembly/AssemblyConstraintsWidget.js +1373 -0
- package/src/UI/assembly/constraintFaceUtils.js +115 -0
- package/src/UI/assembly/constraintHighlightUtils.js +70 -0
- package/src/UI/assembly/constraintLabelUtils.js +31 -0
- package/src/UI/assembly/constraintPointUtils.js +64 -0
- package/src/UI/assembly/constraintSelectionUtils.js +185 -0
- package/src/UI/assembly/constraintStatusUtils.js +142 -0
- package/src/UI/componentSelectorModal.js +240 -0
- package/src/UI/controls/CombinedTransformControls.js +386 -0
- package/src/UI/dialogs.js +351 -0
- package/src/UI/expressionsManager.js +100 -0
- package/src/UI/featureDialogWidgets/booleanField.js +25 -0
- package/src/UI/featureDialogWidgets/booleanOperationField.js +97 -0
- package/src/UI/featureDialogWidgets/buttonField.js +45 -0
- package/src/UI/featureDialogWidgets/componentSelectorField.js +102 -0
- package/src/UI/featureDialogWidgets/defaultField.js +23 -0
- package/src/UI/featureDialogWidgets/fileField.js +66 -0
- package/src/UI/featureDialogWidgets/index.js +34 -0
- package/src/UI/featureDialogWidgets/numberField.js +165 -0
- package/src/UI/featureDialogWidgets/optionsField.js +33 -0
- package/src/UI/featureDialogWidgets/referenceSelectionField.js +208 -0
- package/src/UI/featureDialogWidgets/stringField.js +24 -0
- package/src/UI/featureDialogWidgets/textareaField.js +28 -0
- package/src/UI/featureDialogWidgets/threadDesignationField.js +160 -0
- package/src/UI/featureDialogWidgets/transformField.js +252 -0
- package/src/UI/featureDialogWidgets/utils.js +43 -0
- package/src/UI/featureDialogWidgets/vec3Field.js +133 -0
- package/src/UI/featureDialogs.js +1414 -0
- package/src/UI/fileManagerWidget.js +615 -0
- package/src/UI/history/HistoryCollectionWidget.js +1294 -0
- package/src/UI/history/historyCollectionWidget.css.js +257 -0
- package/src/UI/history/historyDisplayInfo.js +133 -0
- package/src/UI/mobile.js +28 -0
- package/src/UI/objectDump.js +442 -0
- package/src/UI/pmi/AnnotationCollectionWidget.js +120 -0
- package/src/UI/pmi/AnnotationHistory.js +353 -0
- package/src/UI/pmi/AnnotationRegistry.js +90 -0
- package/src/UI/pmi/BaseAnnotation.js +269 -0
- package/src/UI/pmi/LabelOverlay.css +102 -0
- package/src/UI/pmi/LabelOverlay.js +191 -0
- package/src/UI/pmi/PMIMode.js +1550 -0
- package/src/UI/pmi/PMIViewsWidget.js +1098 -0
- package/src/UI/pmi/annUtils.js +729 -0
- package/src/UI/pmi/dimensions/AngleDimensionAnnotation.js +647 -0
- package/src/UI/pmi/dimensions/ExplodeBodyAnnotation.js +507 -0
- package/src/UI/pmi/dimensions/HoleCalloutAnnotation.js +462 -0
- package/src/UI/pmi/dimensions/LeaderAnnotation.js +403 -0
- package/src/UI/pmi/dimensions/LinearDimensionAnnotation.js +532 -0
- package/src/UI/pmi/dimensions/NoteAnnotation.js +110 -0
- package/src/UI/pmi/dimensions/RadialDimensionAnnotation.js +659 -0
- package/src/UI/pmi/pmiStyle.js +44 -0
- package/src/UI/sketcher/SketchMode3D.js +4095 -0
- package/src/UI/sketcher/dimensions.js +674 -0
- package/src/UI/sketcher/glyphs.js +236 -0
- package/src/UI/sketcher/highlights.js +60 -0
- package/src/UI/toolbarButtons/aboutButton.js +5 -0
- package/src/UI/toolbarButtons/exportButton.js +609 -0
- package/src/UI/toolbarButtons/flatPatternButton.js +307 -0
- package/src/UI/toolbarButtons/importButton.js +160 -0
- package/src/UI/toolbarButtons/inspectorToggleButton.js +12 -0
- package/src/UI/toolbarButtons/metadataButton.js +1063 -0
- package/src/UI/toolbarButtons/orientToFaceButton.js +114 -0
- package/src/UI/toolbarButtons/registerDefaultButtons.js +46 -0
- package/src/UI/toolbarButtons/saveButton.js +99 -0
- package/src/UI/toolbarButtons/scriptRunnerButton.js +302 -0
- package/src/UI/toolbarButtons/testsButton.js +26 -0
- package/src/UI/toolbarButtons/undoRedoButtons.js +25 -0
- package/src/UI/toolbarButtons/wireframeToggleButton.js +5 -0
- package/src/UI/toolbarButtons/zoomToFitButton.js +5 -0
- package/src/UI/triangleDebuggerWindow.js +945 -0
- package/src/UI/viewer.js +4228 -0
- package/src/assemblyConstraints/AssemblyConstraintHistory.js +1576 -0
- package/src/assemblyConstraints/AssemblyConstraintRegistry.js +120 -0
- package/src/assemblyConstraints/BaseAssemblyConstraint.js +66 -0
- package/src/assemblyConstraints/constraintExpressionUtils.js +35 -0
- package/src/assemblyConstraints/constraintUtils/parallelAlignment.js +676 -0
- package/src/assemblyConstraints/constraints/AngleConstraint.js +485 -0
- package/src/assemblyConstraints/constraints/CoincidentConstraint.js +194 -0
- package/src/assemblyConstraints/constraints/DistanceConstraint.js +616 -0
- package/src/assemblyConstraints/constraints/FixedConstraint.js +78 -0
- package/src/assemblyConstraints/constraints/ParallelConstraint.js +252 -0
- package/src/assemblyConstraints/constraints/TouchAlignConstraint.js +961 -0
- package/src/core/entities/HistoryCollectionBase.js +72 -0
- package/src/core/entities/ListEntityBase.js +109 -0
- package/src/core/entities/schemaProcesser.js +121 -0
- package/src/exporters/sheetMetalFlatPattern.js +659 -0
- package/src/exporters/sheetMetalUnfold.js +862 -0
- package/src/exporters/step.js +1135 -0
- package/src/exporters/threeMF.js +575 -0
- package/src/features/assemblyComponent/AssemblyComponentFeature.js +780 -0
- package/src/features/boolean/BooleanFeature.js +94 -0
- package/src/features/chamfer/ChamferFeature.js +116 -0
- package/src/features/datium/DatiumFeature.js +80 -0
- package/src/features/edgeFeatureUtils.js +41 -0
- package/src/features/extrude/ExtrudeFeature.js +143 -0
- package/src/features/fillet/FilletFeature.js +197 -0
- package/src/features/helix/HelixFeature.js +405 -0
- package/src/features/hole/HoleFeature.js +1050 -0
- package/src/features/hole/screwClearance.js +86 -0
- package/src/features/hole/threadDesignationCatalog.js +149 -0
- package/src/features/imageHeightSolid/ImageHeightmapSolidFeature.js +463 -0
- package/src/features/imageToFace/ImageToFaceFeature.js +727 -0
- package/src/features/imageToFace/imageEditor.js +1270 -0
- package/src/features/imageToFace/traceUtils.js +971 -0
- package/src/features/import3dModel/Import3dModelFeature.js +151 -0
- package/src/features/loft/LoftFeature.js +605 -0
- package/src/features/mirror/MirrorFeature.js +151 -0
- package/src/features/offsetFace/OffsetFaceFeature.js +370 -0
- package/src/features/offsetShell/OffsetShellFeature.js +89 -0
- package/src/features/overlapCleanup/OverlapCleanupFeature.js +85 -0
- package/src/features/pattern/PatternFeature.js +275 -0
- package/src/features/patternLinear/PatternLinearFeature.js +120 -0
- package/src/features/patternRadial/PatternRadialFeature.js +186 -0
- package/src/features/plane/PlaneFeature.js +154 -0
- package/src/features/primitiveCone/primitiveConeFeature.js +99 -0
- package/src/features/primitiveCube/primitiveCubeFeature.js +70 -0
- package/src/features/primitiveCylinder/primitiveCylinderFeature.js +91 -0
- package/src/features/primitivePyramid/primitivePyramidFeature.js +72 -0
- package/src/features/primitiveSphere/primitiveSphereFeature.js +62 -0
- package/src/features/primitiveTorus/primitiveTorusFeature.js +109 -0
- package/src/features/remesh/RemeshFeature.js +97 -0
- package/src/features/revolve/RevolveFeature.js +111 -0
- package/src/features/selectionUtils.js +118 -0
- package/src/features/sheetMetal/SheetMetalContourFlangeFeature.js +1656 -0
- package/src/features/sheetMetal/SheetMetalCutoutFeature.js +1056 -0
- package/src/features/sheetMetal/SheetMetalFlangeFeature.js +1568 -0
- package/src/features/sheetMetal/SheetMetalHemFeature.js +43 -0
- package/src/features/sheetMetal/SheetMetalObject.js +141 -0
- package/src/features/sheetMetal/SheetMetalTabFeature.js +176 -0
- package/src/features/sheetMetal/UNFOLD_NEUTRAL_REQUIREMENTS.md +153 -0
- package/src/features/sheetMetal/contour-flange-rebuild-spec.md +261 -0
- package/src/features/sheetMetal/profileUtils.js +25 -0
- package/src/features/sheetMetal/sheetMetalCleanup.js +9 -0
- package/src/features/sheetMetal/sheetMetalFaceTypes.js +146 -0
- package/src/features/sheetMetal/sheetMetalMetadata.js +165 -0
- package/src/features/sheetMetal/sheetMetalPipeline.js +169 -0
- package/src/features/sheetMetal/sheetMetalProfileUtils.js +216 -0
- package/src/features/sheetMetal/sheetMetalTabUtils.js +29 -0
- package/src/features/sheetMetal/sheetMetalTree.js +210 -0
- package/src/features/sketch/SketchFeature.js +955 -0
- package/src/features/sketch/sketchSolver2D/ConstraintEngine.js +800 -0
- package/src/features/sketch/sketchSolver2D/constraintDefinitions.js +704 -0
- package/src/features/sketch/sketchSolver2D/mathHelpersMod.js +307 -0
- package/src/features/spline/SplineEditorSession.js +988 -0
- package/src/features/spline/SplineFeature.js +1388 -0
- package/src/features/spline/splineUtils.js +218 -0
- package/src/features/sweep/SweepFeature.js +110 -0
- package/src/features/transform/TransformFeature.js +152 -0
- package/src/features/tube/TubeFeature.js +635 -0
- package/src/fs.proxy.js +625 -0
- package/src/idbStorage.js +254 -0
- package/src/index.js +12 -0
- package/src/main.js +15 -0
- package/src/metadataManager.js +64 -0
- package/src/path.proxy.js +277 -0
- package/src/plugins/ghLoader.worker.js +151 -0
- package/src/plugins/pluginManager.js +286 -0
- package/src/pmi/PMIViewsManager.js +134 -0
- package/src/services/componentLibrary.js +198 -0
- package/src/tests/ConsoleCapture.js +189 -0
- package/src/tests/S7-diagnostics-2025-12-23T18-37-23-570Z.json +630 -0
- package/src/tests/browserTests.js +597 -0
- package/src/tests/debugBoolean.js +225 -0
- package/src/tests/partFiles/badBoolean.json +957 -0
- package/src/tests/partFiles/extrudeTest.json +88 -0
- package/src/tests/partFiles/filletFail.json +58 -0
- package/src/tests/partFiles/import_TEst.part.part.json +646 -0
- package/src/tests/partFiles/sheetMetalHem.BREP.json +734 -0
- package/src/tests/test_boolean_subtract.js +27 -0
- package/src/tests/test_chamfer.js +17 -0
- package/src/tests/test_extrudeFace.js +24 -0
- package/src/tests/test_fillet.js +17 -0
- package/src/tests/test_fillet_nonClosed.js +45 -0
- package/src/tests/test_filletsMoreDifficult.js +46 -0
- package/src/tests/test_history_features_basic.js +149 -0
- package/src/tests/test_hole.js +282 -0
- package/src/tests/test_mirror.js +16 -0
- package/src/tests/test_offsetShellGrouping.js +85 -0
- package/src/tests/test_plane.js +4 -0
- package/src/tests/test_primitiveCone.js +11 -0
- package/src/tests/test_primitiveCube.js +7 -0
- package/src/tests/test_primitiveCylinder.js +8 -0
- package/src/tests/test_primitivePyramid.js +9 -0
- package/src/tests/test_primitiveSphere.js +17 -0
- package/src/tests/test_primitiveTorus.js +21 -0
- package/src/tests/test_pushFace.js +126 -0
- package/src/tests/test_sheetMetalContourFlange.js +125 -0
- package/src/tests/test_sheetMetal_features.js +80 -0
- package/src/tests/test_sketch_openLoop.js +45 -0
- package/src/tests/test_solidMetrics.js +58 -0
- package/src/tests/test_stlLoader.js +1889 -0
- package/src/tests/test_sweepFace.js +55 -0
- package/src/tests/test_tube.js +45 -0
- package/src/tests/test_tube_closedLoop.js +67 -0
- package/src/tests/tests.js +493 -0
- package/src/tools/assemblyConstraintDialogCapturePage.js +56 -0
- package/src/tools/dialogCapturePageFactory.js +227 -0
- package/src/tools/featureDialogCapturePage.js +47 -0
- package/src/tools/pmiAnnotationDialogCapturePage.js +60 -0
- package/src/utils/axisHelpers.js +99 -0
- package/src/utils/deepClone.js +69 -0
- package/src/utils/geometryTolerance.js +37 -0
- package/src/utils/normalizeTypeString.js +8 -0
- package/src/utils/xformMath.js +51 -0
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
import { THREE } from "../SolidShared.js";
|
|
2
|
+
import { composeTrsMatrixDeg } from "../../utils/xformMath.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Geometry transforms applied directly to authored data.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Apply a Matrix4 to all authored vertices (bake transform into geometry arrays).
|
|
10
|
+
* Does not modify the Object3D transform; marks manifold dirty for rebuild.
|
|
11
|
+
*/
|
|
12
|
+
export function bakeTransform(matrix) {
|
|
13
|
+
try {
|
|
14
|
+
if (!matrix || typeof matrix.elements === 'undefined') return this;
|
|
15
|
+
if (!Array.isArray(this._vertProperties) || this._vertProperties.length === 0) return this;
|
|
16
|
+
const m = (matrix && matrix.isMatrix4) ? matrix : new THREE.Matrix4().fromArray(matrix.elements || matrix);
|
|
17
|
+
const e = m.elements;
|
|
18
|
+
const vp = this._vertProperties;
|
|
19
|
+
// Inline mat4 multiply for speed
|
|
20
|
+
for (let i = 0; i < vp.length; i += 3) {
|
|
21
|
+
const x = vp[i + 0], y = vp[i + 1], z = vp[i + 2];
|
|
22
|
+
const nx = e[0] * x + e[4] * y + e[8] * z + e[12];
|
|
23
|
+
const ny = e[1] * x + e[5] * y + e[9] * z + e[13];
|
|
24
|
+
const nz = e[2] * x + e[6] * y + e[10] * z + e[14];
|
|
25
|
+
vp[i + 0] = nx; vp[i + 1] = ny; vp[i + 2] = nz;
|
|
26
|
+
}
|
|
27
|
+
// Rebuild exact-key map and mark dirty
|
|
28
|
+
this._vertKeyToIndex = new Map();
|
|
29
|
+
for (let i = 0; i < vp.length; i += 3) {
|
|
30
|
+
const X = vp[i], Y = vp[i + 1], Z = vp[i + 2];
|
|
31
|
+
this._vertKeyToIndex.set(`${X},${Y},${Z}`, (i / 3) | 0);
|
|
32
|
+
}
|
|
33
|
+
this._dirty = true;
|
|
34
|
+
this._faceIndex = null;
|
|
35
|
+
// Bake the same transform into any auxiliary edges
|
|
36
|
+
try {
|
|
37
|
+
if (Array.isArray(this._auxEdges) && this._auxEdges.length) {
|
|
38
|
+
const tmp = new THREE.Vector3();
|
|
39
|
+
for (const aux of this._auxEdges) {
|
|
40
|
+
const pts = Array.isArray(aux?.points) ? aux.points : null;
|
|
41
|
+
if (!pts) continue;
|
|
42
|
+
for (let i = 0; i < pts.length; i++) {
|
|
43
|
+
const p = pts[i];
|
|
44
|
+
if (!Array.isArray(p) || p.length !== 3) continue;
|
|
45
|
+
tmp.set(p[0], p[1], p[2]).applyMatrix4(m);
|
|
46
|
+
pts[i] = [tmp.x, tmp.y, tmp.z];
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
} catch { /* ignore aux bake errors */ }
|
|
51
|
+
|
|
52
|
+
// Bake the same transform into face metadata (center and axis vectors)
|
|
53
|
+
try {
|
|
54
|
+
if (this._faceMetadata && this._faceMetadata.size > 0) {
|
|
55
|
+
const tmp = new THREE.Vector3();
|
|
56
|
+
for (const [, metadata] of this._faceMetadata.entries()) {
|
|
57
|
+
if (!metadata || typeof metadata !== 'object') continue;
|
|
58
|
+
|
|
59
|
+
// Transform center point if present
|
|
60
|
+
if (Array.isArray(metadata.center) && metadata.center.length === 3) {
|
|
61
|
+
tmp.set(metadata.center[0], metadata.center[1], metadata.center[2]);
|
|
62
|
+
tmp.applyMatrix4(m);
|
|
63
|
+
metadata.center = [tmp.x, tmp.y, tmp.z];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Transform axis direction if present (use transformDirection for vectors)
|
|
67
|
+
if (Array.isArray(metadata.axis) && metadata.axis.length === 3) {
|
|
68
|
+
tmp.set(metadata.axis[0], metadata.axis[1], metadata.axis[2]);
|
|
69
|
+
tmp.transformDirection(m).normalize();
|
|
70
|
+
metadata.axis = [tmp.x, tmp.y, tmp.z];
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (this._edgeMetadata && this._edgeMetadata.size > 0) {
|
|
75
|
+
// Edge metadata currently only carries scalar tags; keep the map intact
|
|
76
|
+
this._edgeMetadata = new Map(this._edgeMetadata);
|
|
77
|
+
}
|
|
78
|
+
} catch { /* ignore metadata bake errors */ }
|
|
79
|
+
} catch (_) { /* ignore */ }
|
|
80
|
+
return this;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Convenience: compose TRS and bake transform.
|
|
85
|
+
*/
|
|
86
|
+
export function bakeTRS(trs) {
|
|
87
|
+
try {
|
|
88
|
+
const m = composeTrsMatrixDeg(trs, THREE);
|
|
89
|
+
return this.bakeTransform(m);
|
|
90
|
+
} catch (_) { return this; }
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Offset all vertices belonging to the given face along the face's
|
|
95
|
+
* area-weighted average normal by the specified distance.
|
|
96
|
+
*/
|
|
97
|
+
export function offsetFace(faceName, distance) {
|
|
98
|
+
const dist = Number(distance);
|
|
99
|
+
if (!Number.isFinite(dist) || dist === 0) return this;
|
|
100
|
+
const id = this._faceNameToID.get(faceName);
|
|
101
|
+
if (id === undefined) return this; // unknown face name → no-op
|
|
102
|
+
|
|
103
|
+
const mesh = this.getMesh();
|
|
104
|
+
const vp = mesh.vertProperties; // Float32Array
|
|
105
|
+
const tv = mesh.triVerts; // Uint32Array
|
|
106
|
+
const faceIDs = mesh.faceID; // Uint32Array
|
|
107
|
+
const triCount = (tv.length / 3) | 0;
|
|
108
|
+
if (!faceIDs || faceIDs.length !== triCount) return this;
|
|
109
|
+
|
|
110
|
+
// 1) Gather triangles and accumulate area-weighted normal.
|
|
111
|
+
let nx = 0, ny = 0, nz = 0;
|
|
112
|
+
const affectedVerts = new Set();
|
|
113
|
+
for (let t = 0; t < triCount; t++) {
|
|
114
|
+
if (faceIDs[t] !== id) continue;
|
|
115
|
+
const b = t * 3;
|
|
116
|
+
const i0 = tv[b + 0] >>> 0;
|
|
117
|
+
const i1 = tv[b + 1] >>> 0;
|
|
118
|
+
const i2 = tv[b + 2] >>> 0;
|
|
119
|
+
affectedVerts.add(i0); affectedVerts.add(i1); affectedVerts.add(i2);
|
|
120
|
+
|
|
121
|
+
const ax = vp[i0 * 3 + 0], ay = vp[i0 * 3 + 1], az = vp[i0 * 3 + 2];
|
|
122
|
+
const bx = vp[i1 * 3 + 0], by = vp[i1 * 3 + 1], bz = vp[i1 * 3 + 2];
|
|
123
|
+
const cx = vp[i2 * 3 + 0], cy = vp[i2 * 3 + 1], cz = vp[i2 * 3 + 2];
|
|
124
|
+
const ux = bx - ax, uy = by - ay, uz = bz - az;
|
|
125
|
+
const vx = cx - ax, vy = cy - ay, vz = cz - az;
|
|
126
|
+
nx += uy * vz - uz * vy;
|
|
127
|
+
ny += uz * vx - ux * vz;
|
|
128
|
+
nz += ux * vy - uy * vx;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// 2) Normalize to get unit average normal.
|
|
132
|
+
const len = Math.hypot(nx, ny, nz);
|
|
133
|
+
if (!(len > 0)) return this;
|
|
134
|
+
const sx = (nx / len) * dist;
|
|
135
|
+
const sy = (ny / len) * dist;
|
|
136
|
+
const sz = (nz / len) * dist;
|
|
137
|
+
|
|
138
|
+
// 3) Apply translation to authoring vertex buffer.
|
|
139
|
+
if (!this._vertProperties || this._vertProperties.length === 0) return this;
|
|
140
|
+
for (const vi of affectedVerts) {
|
|
141
|
+
const base = (vi * 3) | 0;
|
|
142
|
+
this._vertProperties[base + 0] += sx;
|
|
143
|
+
this._vertProperties[base + 1] += sy;
|
|
144
|
+
this._vertProperties[base + 2] += sz;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// 4) Rebuild exact-key map and mark dirty so manifold rebuilds on demand.
|
|
148
|
+
this._vertKeyToIndex = new Map();
|
|
149
|
+
for (let i = 0; i < this._vertProperties.length; i += 3) {
|
|
150
|
+
const x = this._vertProperties[i], y = this._vertProperties[i + 1], z = this._vertProperties[i + 2];
|
|
151
|
+
this._vertKeyToIndex.set(`${x},${y},${z}`, (i / 3) | 0);
|
|
152
|
+
}
|
|
153
|
+
this._dirty = true;
|
|
154
|
+
this._faceIndex = null;
|
|
155
|
+
try { return this; } finally { try { if (mesh && typeof mesh.delete === 'function') mesh.delete(); } catch { } }
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Return a mirrored copy of this solid across a plane defined by a point and a normal.
|
|
160
|
+
*/
|
|
161
|
+
export function mirrorAcrossPlane(point, normal) {
|
|
162
|
+
const Solid = this.constructor;
|
|
163
|
+
const P0 = (point instanceof THREE.Vector3)
|
|
164
|
+
? point.clone()
|
|
165
|
+
: new THREE.Vector3(point[0], point[1], point[2]);
|
|
166
|
+
const n = (normal instanceof THREE.Vector3)
|
|
167
|
+
? normal.clone().normalize()
|
|
168
|
+
: new THREE.Vector3(normal[0], normal[1], normal[2]).normalize();
|
|
169
|
+
|
|
170
|
+
const mesh = this.getMesh();
|
|
171
|
+
try {
|
|
172
|
+
const vp = mesh.vertProperties; // Float32Array
|
|
173
|
+
const tv = mesh.triVerts; // Uint32Array
|
|
174
|
+
const faceIDs = mesh.faceID && mesh.faceID.length ? Array.from(mesh.faceID) : [];
|
|
175
|
+
|
|
176
|
+
const mirrored = new Solid();
|
|
177
|
+
mirrored._numProp = mesh.numProp || 3;
|
|
178
|
+
|
|
179
|
+
// Reflect vertices across plane
|
|
180
|
+
const outVP = new Array(vp.length);
|
|
181
|
+
const X = new THREE.Vector3();
|
|
182
|
+
for (let i = 0; i < vp.length; i += 3) {
|
|
183
|
+
X.set(vp[i + 0], vp[i + 1], vp[i + 2]);
|
|
184
|
+
const d = X.clone().sub(P0);
|
|
185
|
+
const t = 2 * d.dot(n);
|
|
186
|
+
const Xp = X.sub(n.clone().multiplyScalar(t));
|
|
187
|
+
outVP[i + 0] = Xp.x;
|
|
188
|
+
outVP[i + 1] = Xp.y;
|
|
189
|
+
outVP[i + 2] = Xp.z;
|
|
190
|
+
}
|
|
191
|
+
mirrored._vertProperties = outVP;
|
|
192
|
+
|
|
193
|
+
// Copy triangles and face IDs
|
|
194
|
+
mirrored._triVerts = Array.from(tv);
|
|
195
|
+
mirrored._triIDs = faceIDs.length ? faceIDs : new Array((tv.length / 3) | 0).fill(0);
|
|
196
|
+
|
|
197
|
+
// Restore face name maps
|
|
198
|
+
try {
|
|
199
|
+
mirrored._idToFaceName = new Map(this._idToFaceName);
|
|
200
|
+
mirrored._faceNameToID = new Map(this._faceNameToID);
|
|
201
|
+
} catch (_) { }
|
|
202
|
+
|
|
203
|
+
// Mirror auxiliary edges (e.g., centerlines) across the same plane.
|
|
204
|
+
try {
|
|
205
|
+
const aux = Array.isArray(this._auxEdges) ? this._auxEdges : [];
|
|
206
|
+
const X = new THREE.Vector3();
|
|
207
|
+
const d = new THREE.Vector3();
|
|
208
|
+
const nScaled = new THREE.Vector3();
|
|
209
|
+
mirrored._auxEdges = aux.map(edge => {
|
|
210
|
+
const pts = [];
|
|
211
|
+
if (Array.isArray(edge?.points)) {
|
|
212
|
+
for (const p of edge.points) {
|
|
213
|
+
if (!Array.isArray(p) || p.length !== 3) continue;
|
|
214
|
+
X.set(p[0], p[1], p[2]);
|
|
215
|
+
d.subVectors(X, P0);
|
|
216
|
+
nScaled.copy(n).multiplyScalar(2 * d.dot(n));
|
|
217
|
+
X.sub(nScaled);
|
|
218
|
+
pts.push([X.x, X.y, X.z]);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return {
|
|
222
|
+
name: edge?.name,
|
|
223
|
+
closedLoop: !!edge?.closedLoop,
|
|
224
|
+
polylineWorld: !!edge?.polylineWorld,
|
|
225
|
+
materialKey: edge?.materialKey,
|
|
226
|
+
centerline: !!edge?.centerline,
|
|
227
|
+
points: pts,
|
|
228
|
+
};
|
|
229
|
+
}).filter(e => Array.isArray(e.points) && e.points.length);
|
|
230
|
+
} catch (_) { mirrored._auxEdges = []; }
|
|
231
|
+
|
|
232
|
+
// Rebuild vertex key map for exact-key lookup consistency
|
|
233
|
+
mirrored._vertKeyToIndex = new Map();
|
|
234
|
+
for (let i = 0; i < mirrored._vertProperties.length; i += 3) {
|
|
235
|
+
const x = mirrored._vertProperties[i];
|
|
236
|
+
const y = mirrored._vertProperties[i + 1];
|
|
237
|
+
const z = mirrored._vertProperties[i + 2];
|
|
238
|
+
mirrored._vertKeyToIndex.set(`${x},${y},${z}`, (i / 3) | 0);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
mirrored._dirty = true; // manifold must rebuild on demand
|
|
242
|
+
mirrored._faceIndex = null;
|
|
243
|
+
mirrored._manifold = null;
|
|
244
|
+
return mirrored;
|
|
245
|
+
} finally { try { if (mesh && typeof mesh.delete === 'function') mesh.delete(); } catch { } }
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Push a named face outward by the specified distance.
|
|
250
|
+
* Simple implementation: calculate face normal, create displacement vector, apply to all vertices.
|
|
251
|
+
*
|
|
252
|
+
* @param {string} faceName - Name of the face to push
|
|
253
|
+
* @param {number} distance - Distance to push the face (positive = outward along normal)
|
|
254
|
+
* @returns {Solid} this for chaining
|
|
255
|
+
*/
|
|
256
|
+
export function pushFace(faceName, distance = 0.001) {
|
|
257
|
+
const dist = Number(distance);
|
|
258
|
+
if (!faceName || !Number.isFinite(dist) || dist === 0) return this;
|
|
259
|
+
|
|
260
|
+
// Make sure triangle windings are coherent so the averaged normal points outward.
|
|
261
|
+
try { this._manifoldize(); } catch { /* best effort; fall back to existing winding */ }
|
|
262
|
+
|
|
263
|
+
const faceID = this._faceNameToID.get(faceName);
|
|
264
|
+
if (faceID === undefined) {
|
|
265
|
+
console.warn(`pushFace: Face "${faceName}" not found`);
|
|
266
|
+
return this;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const triIDs = this._triIDs;
|
|
270
|
+
const triVerts = this._triVerts;
|
|
271
|
+
const vp = this._vertProperties;
|
|
272
|
+
if (!triIDs || !triVerts || !vp) return this;
|
|
273
|
+
|
|
274
|
+
const triCount = Math.min(triIDs.length, (triVerts.length / 3) | 0);
|
|
275
|
+
let nx = 0, ny = 0, nz = 0;
|
|
276
|
+
let areaSum = 0;
|
|
277
|
+
const affected = new Set();
|
|
278
|
+
const vertNormals = new Map();
|
|
279
|
+
|
|
280
|
+
for (let t = 0; t < triCount; t++) {
|
|
281
|
+
if (triIDs[t] !== faceID) continue;
|
|
282
|
+
const base = t * 3;
|
|
283
|
+
const i0 = triVerts[base + 0] >>> 0;
|
|
284
|
+
const i1 = triVerts[base + 1] >>> 0;
|
|
285
|
+
const i2 = triVerts[base + 2] >>> 0;
|
|
286
|
+
affected.add(i0); affected.add(i1); affected.add(i2);
|
|
287
|
+
|
|
288
|
+
const ax = vp[i0 * 3 + 0], ay = vp[i0 * 3 + 1], az = vp[i0 * 3 + 2];
|
|
289
|
+
const bx = vp[i1 * 3 + 0], by = vp[i1 * 3 + 1], bz = vp[i1 * 3 + 2];
|
|
290
|
+
const cx = vp[i2 * 3 + 0], cy = vp[i2 * 3 + 1], cz = vp[i2 * 3 + 2];
|
|
291
|
+
const ux = bx - ax, uy = by - ay, uz = bz - az;
|
|
292
|
+
const vx = cx - ax, vy = cy - ay, vz = cz - az;
|
|
293
|
+
const tx = uy * vz - uz * vy;
|
|
294
|
+
const ty = uz * vx - ux * vz;
|
|
295
|
+
const tz = ux * vy - uy * vx;
|
|
296
|
+
const tLen = Math.hypot(tx, ty, tz);
|
|
297
|
+
if (!(tLen > 0)) continue;
|
|
298
|
+
areaSum += tLen;
|
|
299
|
+
nx += tx; ny += ty; nz += tz;
|
|
300
|
+
const addNormal = (idx) => {
|
|
301
|
+
const existing = vertNormals.get(idx);
|
|
302
|
+
if (existing) {
|
|
303
|
+
existing[0] += tx;
|
|
304
|
+
existing[1] += ty;
|
|
305
|
+
existing[2] += tz;
|
|
306
|
+
} else {
|
|
307
|
+
vertNormals.set(idx, [tx, ty, tz]);
|
|
308
|
+
}
|
|
309
|
+
};
|
|
310
|
+
addNormal(i0); addNormal(i1); addNormal(i2);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const affectedIndices = [...affected];
|
|
314
|
+
if (affectedIndices.length === 0) return this;
|
|
315
|
+
|
|
316
|
+
const len = Math.hypot(nx, ny, nz);
|
|
317
|
+
const planarRatio = areaSum > 0 ? (len / areaSum) : 0;
|
|
318
|
+
if (len > 0 && planarRatio > 0.98) {
|
|
319
|
+
// Push strictly along the outward normal derived from triangle winding;
|
|
320
|
+
// positive distance moves outward, negative moves inward.
|
|
321
|
+
const scale = dist / len;
|
|
322
|
+
const dx = nx * scale;
|
|
323
|
+
const dy = ny * scale;
|
|
324
|
+
const dz = nz * scale;
|
|
325
|
+
for (const idx of affectedIndices) {
|
|
326
|
+
const base = idx * 3;
|
|
327
|
+
vp[base + 0] += dx;
|
|
328
|
+
vp[base + 1] += dy;
|
|
329
|
+
vp[base + 2] += dz;
|
|
330
|
+
}
|
|
331
|
+
} else {
|
|
332
|
+
// Fall back to per-vertex normals for curved faces.
|
|
333
|
+
let moved = 0;
|
|
334
|
+
for (const idx of affectedIndices) {
|
|
335
|
+
const normal = vertNormals.get(idx);
|
|
336
|
+
if (!normal) continue;
|
|
337
|
+
const nLen = Math.hypot(normal[0], normal[1], normal[2]);
|
|
338
|
+
if (!(nLen > 0)) continue;
|
|
339
|
+
const scale = dist / nLen;
|
|
340
|
+
const base = idx * 3;
|
|
341
|
+
vp[base + 0] += normal[0] * scale;
|
|
342
|
+
vp[base + 1] += normal[1] * scale;
|
|
343
|
+
vp[base + 2] += normal[2] * scale;
|
|
344
|
+
moved++;
|
|
345
|
+
}
|
|
346
|
+
if (moved === 0) {
|
|
347
|
+
console.warn(`pushFace: Invalid normal for face "${faceName}"`);
|
|
348
|
+
return this;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
this._vertKeyToIndex = new Map();
|
|
353
|
+
for (let i = 0; i < vp.length; i += 3) {
|
|
354
|
+
const x = vp[i], y = vp[i + 1], z = vp[i + 2];
|
|
355
|
+
this._vertKeyToIndex.set(`${x},${y},${z}`, (i / 3) | 0);
|
|
356
|
+
}
|
|
357
|
+
this._dirty = true;
|
|
358
|
+
this._faceIndex = null;
|
|
359
|
+
this._manifold = null;
|
|
360
|
+
return this;
|
|
361
|
+
}
|