brep-io-kernel 1.0.0-ci.10
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 +157 -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,86 @@
|
|
|
1
|
+
import { ThreadStandard } from '../../BREP/threadGeometry.js';
|
|
2
|
+
|
|
3
|
+
const MM_PER_INCH = 25.4;
|
|
4
|
+
|
|
5
|
+
const normalizeKey = (s) => String(s || '').replace(/\s+/g, '').toUpperCase();
|
|
6
|
+
|
|
7
|
+
// ISO 273 style clearance holes (fine=close, normal, coarse=loose)
|
|
8
|
+
const METRIC_CLEARANCE = {
|
|
9
|
+
'M2': { CLOSE: 2.2, NORMAL: 2.4, LOOSE: 2.6 },
|
|
10
|
+
'M2.5': { CLOSE: 2.8, NORMAL: 3.0, LOOSE: 3.2 },
|
|
11
|
+
'M3': { CLOSE: 3.2, NORMAL: 3.4, LOOSE: 3.6 },
|
|
12
|
+
'M4': { CLOSE: 4.3, NORMAL: 4.5, LOOSE: 4.8 },
|
|
13
|
+
'M5': { CLOSE: 5.3, NORMAL: 5.5, LOOSE: 5.8 },
|
|
14
|
+
'M6': { CLOSE: 6.4, NORMAL: 6.6, LOOSE: 7.0 },
|
|
15
|
+
'M8': { CLOSE: 8.4, NORMAL: 9.0, LOOSE: 10.0 },
|
|
16
|
+
'M10': { CLOSE: 10.5, NORMAL: 11.0, LOOSE: 12.0 },
|
|
17
|
+
'M12': { CLOSE: 13.0, NORMAL: 13.5, LOOSE: 14.0 },
|
|
18
|
+
'M16': { CLOSE: 17.0, NORMAL: 18.0, LOOSE: 19.0 },
|
|
19
|
+
'M20': { CLOSE: 21.0, NORMAL: 22.0, LOOSE: 24.0 },
|
|
20
|
+
'M24': { CLOSE: 25.0, NORMAL: 26.0, LOOSE: 28.0 },
|
|
21
|
+
'M30': { CLOSE: 32.0, NORMAL: 33.0, LOOSE: 35.0 },
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// Typical UNC/UNF clearance holes (in inches); converted to mm on read.
|
|
25
|
+
const UNIFIED_CLEARANCE = {
|
|
26
|
+
'#2-56': { CLOSE: 0.089, NORMAL: 0.096, LOOSE: 0.106 },
|
|
27
|
+
'#4-40': { CLOSE: 0.116, NORMAL: 0.128, LOOSE: 0.144 },
|
|
28
|
+
'#6-32': { CLOSE: 0.140, NORMAL: 0.149, LOOSE: 0.169 },
|
|
29
|
+
'#8-32': { CLOSE: 0.169, NORMAL: 0.182, LOOSE: 0.201 },
|
|
30
|
+
'#8-36': { CLOSE: 0.169, NORMAL: 0.182, LOOSE: 0.201 },
|
|
31
|
+
'#10-24': { CLOSE: 0.196, NORMAL: 0.201, LOOSE: 0.221 },
|
|
32
|
+
'#10-32': { CLOSE: 0.196, NORMAL: 0.201, LOOSE: 0.221 },
|
|
33
|
+
'1/4-20': { CLOSE: 0.257, NORMAL: 0.266, LOOSE: 0.281 },
|
|
34
|
+
'1/4-28': { CLOSE: 0.257, NORMAL: 0.266, LOOSE: 0.281 },
|
|
35
|
+
'5/16-18': { CLOSE: 0.320, NORMAL: 0.332, LOOSE: 0.352 },
|
|
36
|
+
'5/16-24': { CLOSE: 0.320, NORMAL: 0.332, LOOSE: 0.352 },
|
|
37
|
+
'3/8-16': { CLOSE: 0.385, NORMAL: 0.397, LOOSE: 0.421 },
|
|
38
|
+
'3/8-24': { CLOSE: 0.385, NORMAL: 0.397, LOOSE: 0.421 },
|
|
39
|
+
'7/16-14': { CLOSE: 0.438, NORMAL: 0.453, LOOSE: 0.484 },
|
|
40
|
+
'7/16-20': { CLOSE: 0.438, NORMAL: 0.453, LOOSE: 0.484 },
|
|
41
|
+
'1/2-13': { CLOSE: 0.515, NORMAL: 0.531, LOOSE: 0.562 },
|
|
42
|
+
'1/2-20': { CLOSE: 0.515, NORMAL: 0.531, LOOSE: 0.562 },
|
|
43
|
+
'5/8-11': { CLOSE: 0.642, NORMAL: 0.656, LOOSE: 0.688 },
|
|
44
|
+
'5/8-18': { CLOSE: 0.642, NORMAL: 0.656, LOOSE: 0.688 },
|
|
45
|
+
'3/4-10': { CLOSE: 0.769, NORMAL: 0.781, LOOSE: 0.812 },
|
|
46
|
+
'3/4-16': { CLOSE: 0.769, NORMAL: 0.781, LOOSE: 0.812 },
|
|
47
|
+
'7/8-9': { CLOSE: 0.885, NORMAL: 0.906, LOOSE: 0.938 },
|
|
48
|
+
'1-8': { CLOSE: 1.027, NORMAL: 1.063, LOOSE: 1.094 },
|
|
49
|
+
'1-12': { CLOSE: 1.027, NORMAL: 1.063, LOOSE: 1.094 },
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
function lookupUnified(designation, fit) {
|
|
53
|
+
const key = normalizeKey(designation);
|
|
54
|
+
for (const raw in UNIFIED_CLEARANCE) {
|
|
55
|
+
if (!Object.prototype.hasOwnProperty.call(UNIFIED_CLEARANCE, raw)) continue;
|
|
56
|
+
const candidate = normalizeKey(raw);
|
|
57
|
+
if (candidate === key) {
|
|
58
|
+
const diam = UNIFIED_CLEARANCE[raw]?.[fit];
|
|
59
|
+
return typeof diam === 'number' ? diam * MM_PER_INCH : null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function lookupMetric(designation, fit) {
|
|
66
|
+
const key = normalizeKey(designation).replace(/X.+$/, ''); // strip pitch, keep base size
|
|
67
|
+
if (METRIC_CLEARANCE[key] && typeof METRIC_CLEARANCE[key][fit] === 'number') {
|
|
68
|
+
return METRIC_CLEARANCE[key][fit];
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function getClearanceDiameter({ standard, designation, fit }) {
|
|
74
|
+
const fitKey = String(fit || 'NONE').toUpperCase();
|
|
75
|
+
if (fitKey === 'NONE') return null;
|
|
76
|
+
if (!designation) return null;
|
|
77
|
+
const std = String(standard || '').toUpperCase();
|
|
78
|
+
if (std === ThreadStandard.UNIFIED) {
|
|
79
|
+
return lookupUnified(designation, fitKey);
|
|
80
|
+
}
|
|
81
|
+
if (std === ThreadStandard.ISO_METRIC || std === ThreadStandard.TRAPEZOIDAL_METRIC) {
|
|
82
|
+
return lookupMetric(designation, fitKey);
|
|
83
|
+
}
|
|
84
|
+
// fallback: try metric-style lookup anyway
|
|
85
|
+
return lookupMetric(designation, fitKey) || lookupUnified(designation, fitKey);
|
|
86
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { ThreadStandard } from '../../BREP/threadGeometry.js';
|
|
2
|
+
|
|
3
|
+
const makeOptions = (list) => list
|
|
4
|
+
.map((entry) => {
|
|
5
|
+
if (typeof entry === 'string') return { value: entry, label: entry };
|
|
6
|
+
if (Array.isArray(entry) && entry[0]) {
|
|
7
|
+
return { value: String(entry[0]), label: String(entry[1] || entry[0]) };
|
|
8
|
+
}
|
|
9
|
+
return null;
|
|
10
|
+
})
|
|
11
|
+
.filter(Boolean);
|
|
12
|
+
|
|
13
|
+
const ISO_METRIC = makeOptions([
|
|
14
|
+
['M2x0.4', 'M2 x 0.4'],
|
|
15
|
+
['M2.5x0.45', 'M2.5 x 0.45'],
|
|
16
|
+
['M3x0.5', 'M3 x 0.5'],
|
|
17
|
+
['M4x0.7', 'M4 x 0.7'],
|
|
18
|
+
['M5x0.8', 'M5 x 0.8'],
|
|
19
|
+
['M6x1', 'M6 x 1 (coarse)'],
|
|
20
|
+
['M6x0.75', 'M6 x 0.75 (fine)'],
|
|
21
|
+
['M8x1.25', 'M8 x 1.25 (coarse)'],
|
|
22
|
+
['M8x1', 'M8 x 1 (fine)'],
|
|
23
|
+
['M10x1.5', 'M10 x 1.5 (coarse)'],
|
|
24
|
+
['M10x1.25', 'M10 x 1.25 (fine)'],
|
|
25
|
+
['M12x1.75', 'M12 x 1.75 (coarse)'],
|
|
26
|
+
['M12x1.25', 'M12 x 1.25 (fine)'],
|
|
27
|
+
['M16x2', 'M16 x 2'],
|
|
28
|
+
['M16x1.5', 'M16 x 1.5'],
|
|
29
|
+
['M20x2.5', 'M20 x 2.5'],
|
|
30
|
+
['M20x1.5', 'M20 x 1.5'],
|
|
31
|
+
['M24x3', 'M24 x 3'],
|
|
32
|
+
['M24x2', 'M24 x 2'],
|
|
33
|
+
['M30x3.5', 'M30 x 3.5'],
|
|
34
|
+
]);
|
|
35
|
+
|
|
36
|
+
const UNIFIED = makeOptions([
|
|
37
|
+
['#2-56UNF', '#2-56 UNF'],
|
|
38
|
+
['#4-40UNC', '#4-40 UNC'],
|
|
39
|
+
['#6-32UNC', '#6-32 UNC'],
|
|
40
|
+
['#8-32UNC', '#8-32 UNC'],
|
|
41
|
+
['#8-36UNF', '#8-36 UNF'],
|
|
42
|
+
['#10-24UNC', '#10-24 UNC'],
|
|
43
|
+
['#10-32UNF', '#10-32 UNF'],
|
|
44
|
+
['1/4-20UNC', '1/4-20 UNC'],
|
|
45
|
+
['1/4-28UNF', '1/4-28 UNF'],
|
|
46
|
+
['5/16-18UNC', '5/16-18 UNC'],
|
|
47
|
+
['5/16-24UNF', '5/16-24 UNF'],
|
|
48
|
+
['3/8-16UNC', '3/8-16 UNC'],
|
|
49
|
+
['3/8-24UNF', '3/8-24 UNF'],
|
|
50
|
+
['7/16-14UNC', '7/16-14 UNC'],
|
|
51
|
+
['7/16-20UNF', '7/16-20 UNF'],
|
|
52
|
+
['1/2-13UNC', '1/2-13 UNC'],
|
|
53
|
+
['1/2-20UNF', '1/2-20 UNF'],
|
|
54
|
+
['5/8-11UNC', '5/8-11 UNC'],
|
|
55
|
+
['5/8-18UNF', '5/8-18 UNF'],
|
|
56
|
+
['3/4-10UNC', '3/4-10 UNC'],
|
|
57
|
+
['3/4-16UNF', '3/4-16 UNF'],
|
|
58
|
+
['7/8-9UNC', '7/8-9 UNC'],
|
|
59
|
+
['1-8UNC', '1-8 UNC'],
|
|
60
|
+
['1-12UNF', '1-12 UNF'],
|
|
61
|
+
]);
|
|
62
|
+
|
|
63
|
+
const TRAPEZOIDAL_METRIC = makeOptions([
|
|
64
|
+
['Tr8x1.5', 'Tr8 x 1.5'],
|
|
65
|
+
['Tr10x2', 'Tr10 x 2'],
|
|
66
|
+
['Tr12x3', 'Tr12 x 3'],
|
|
67
|
+
['Tr16x4', 'Tr16 x 4'],
|
|
68
|
+
['Tr20x4', 'Tr20 x 4'],
|
|
69
|
+
['Tr20x5', 'Tr20 x 5'],
|
|
70
|
+
['Tr24x5', 'Tr24 x 5'],
|
|
71
|
+
['Tr30x6', 'Tr30 x 6'],
|
|
72
|
+
['Tr36x6', 'Tr36 x 6'],
|
|
73
|
+
]);
|
|
74
|
+
|
|
75
|
+
const ACME = makeOptions([
|
|
76
|
+
['1/4-16', '1/4-16 Acme'],
|
|
77
|
+
['3/8-12', '3/8-12 Acme'],
|
|
78
|
+
['1/2-10', '1/2-10 Acme'],
|
|
79
|
+
['5/8-8', '5/8-8 Acme'],
|
|
80
|
+
['3/4-6', '3/4-6 Acme'],
|
|
81
|
+
['1-5', '1-5 Acme'],
|
|
82
|
+
['1.25-5', '1.25-5 Acme'],
|
|
83
|
+
['1.5-4', '1.5-4 Acme'],
|
|
84
|
+
['2-4', '2-4 Acme'],
|
|
85
|
+
]);
|
|
86
|
+
|
|
87
|
+
const STUB_ACME = makeOptions([
|
|
88
|
+
['3/8-16', '3/8-16 Stub Acme'],
|
|
89
|
+
['1/2-16', '1/2-16 Stub Acme'],
|
|
90
|
+
['5/8-12', '5/8-12 Stub Acme'],
|
|
91
|
+
['3/4-12', '3/4-12 Stub Acme'],
|
|
92
|
+
['1-10', '1-10 Stub Acme'],
|
|
93
|
+
['1.25-10', '1.25-10 Stub Acme'],
|
|
94
|
+
['1.5-8', '1.5-8 Stub Acme'],
|
|
95
|
+
['2-6', '2-6 Stub Acme'],
|
|
96
|
+
]);
|
|
97
|
+
|
|
98
|
+
const WHITWORTH = makeOptions([
|
|
99
|
+
['1/8-40', '1/8-40 BSW'],
|
|
100
|
+
['3/16-32', '3/16-32 BSW'],
|
|
101
|
+
['1/4-20', '1/4-20 BSW'],
|
|
102
|
+
['5/16-18', '5/16-18 BSW'],
|
|
103
|
+
['3/8-16', '3/8-16 BSW'],
|
|
104
|
+
['7/16-14', '7/16-14 BSW'],
|
|
105
|
+
['1/2-12', '1/2-12 BSW'],
|
|
106
|
+
['5/8-11', '5/8-11 BSW'],
|
|
107
|
+
['3/4-10', '3/4-10 BSW'],
|
|
108
|
+
['1-8', '1-8 BSW'],
|
|
109
|
+
['1/4-26', '1/4-26 BSF'],
|
|
110
|
+
['5/16-22', '5/16-22 BSF'],
|
|
111
|
+
['3/8-20', '3/8-20 BSF'],
|
|
112
|
+
['7/16-18', '7/16-18 BSF'],
|
|
113
|
+
['1/2-16', '1/2-16 BSF'],
|
|
114
|
+
]);
|
|
115
|
+
|
|
116
|
+
const NPT = makeOptions([
|
|
117
|
+
['1/16-27NPT', '1/16-27 NPT'],
|
|
118
|
+
['1/8-27NPT', '1/8-27 NPT'],
|
|
119
|
+
['1/4-18NPT', '1/4-18 NPT'],
|
|
120
|
+
['3/8-18NPT', '3/8-18 NPT'],
|
|
121
|
+
['1/2-14NPT', '1/2-14 NPT'],
|
|
122
|
+
['3/4-14NPT', '3/4-14 NPT'],
|
|
123
|
+
['1-11.5NPT', '1-11.5 NPT'],
|
|
124
|
+
['1.25-11.5NPT', '1-1/4-11.5 NPT'],
|
|
125
|
+
['1.5-11.5NPT', '1-1/2-11.5 NPT'],
|
|
126
|
+
['2-11.5NPT', '2-11.5 NPT'],
|
|
127
|
+
]);
|
|
128
|
+
|
|
129
|
+
export const THREAD_DESIGNATION_PRESETS = {
|
|
130
|
+
[ThreadStandard.ISO_METRIC]: ISO_METRIC,
|
|
131
|
+
[ThreadStandard.UNIFIED]: UNIFIED,
|
|
132
|
+
[ThreadStandard.TRAPEZOIDAL_METRIC]: TRAPEZOIDAL_METRIC,
|
|
133
|
+
[ThreadStandard.ACME]: ACME,
|
|
134
|
+
[ThreadStandard.STUB_ACME]: STUB_ACME,
|
|
135
|
+
[ThreadStandard.WHITWORTH]: WHITWORTH,
|
|
136
|
+
[ThreadStandard.NPT]: NPT,
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
export function normalizeThreadStandard(standard) {
|
|
140
|
+
if (standard == null && standard !== 0) return 'NONE';
|
|
141
|
+
return String(standard).toUpperCase();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function getThreadDesignationOptions(standard) {
|
|
145
|
+
const key = normalizeThreadStandard(standard);
|
|
146
|
+
const list = THREAD_DESIGNATION_PRESETS[key] || [];
|
|
147
|
+
// Return a shallow copy so callers cannot mutate the shared presets.
|
|
148
|
+
return list.map((opt) => ({ ...opt }));
|
|
149
|
+
}
|
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
import { BREP } from "../../BREP/BREP.js";
|
|
2
|
+
const THREE = BREP.THREE;
|
|
3
|
+
import { ImageEditorUI } from '../imageToFace/imageEditor.js';
|
|
4
|
+
|
|
5
|
+
const inputParamsSchema = {
|
|
6
|
+
id: {
|
|
7
|
+
type: "string",
|
|
8
|
+
default_value: null,
|
|
9
|
+
hint: "unique identifier for the heightmap solid feature",
|
|
10
|
+
},
|
|
11
|
+
fileToImport: {
|
|
12
|
+
type: "file",
|
|
13
|
+
default_value: "",
|
|
14
|
+
accept: ".png,image/png",
|
|
15
|
+
hint: "Grayscale or RGB PNG heightmap (click to choose a file)",
|
|
16
|
+
},
|
|
17
|
+
editImage: {
|
|
18
|
+
type: "button",
|
|
19
|
+
label: "Edit Image",
|
|
20
|
+
default_value: null,
|
|
21
|
+
hint: "Launch the paint like image editor",
|
|
22
|
+
actionFunction: (ctx) => {
|
|
23
|
+
let { fileToImport } = ctx.feature.inputParams;
|
|
24
|
+
if (!fileToImport) {
|
|
25
|
+
try {
|
|
26
|
+
const c = document.createElement('canvas');
|
|
27
|
+
c.width = 300; c.height = 300;
|
|
28
|
+
const ctx2d = c.getContext('2d');
|
|
29
|
+
ctx2d.fillStyle = '#ffffff';
|
|
30
|
+
ctx2d.fillRect(0, 0, c.width, c.height);
|
|
31
|
+
fileToImport = c.toDataURL('image/png');
|
|
32
|
+
} catch (_) { fileToImport = null; }
|
|
33
|
+
}
|
|
34
|
+
const imageEditor = new ImageEditorUI(fileToImport, {
|
|
35
|
+
onSave: (editedImage) => {
|
|
36
|
+
try { ctx.feature.inputParams.fileToImport = editedImage; } catch (_) { }
|
|
37
|
+
try { if (ctx.params) ctx.params.fileToImport = editedImage; } catch (_) { }
|
|
38
|
+
try {
|
|
39
|
+
if (ctx.partHistory) {
|
|
40
|
+
ctx.partHistory.currentHistoryStepId = ctx.feature.inputParams.featureID;
|
|
41
|
+
if (typeof ctx.partHistory.runHistory === 'function') ctx.partHistory.runHistory();
|
|
42
|
+
}
|
|
43
|
+
} catch (_) { }
|
|
44
|
+
},
|
|
45
|
+
onCancel: () => { /* no-op */ }
|
|
46
|
+
});
|
|
47
|
+
imageEditor.open();
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
heightScale: {
|
|
51
|
+
type: "number",
|
|
52
|
+
default_value: 1,
|
|
53
|
+
hint: "World units of elevation per full-intensity pixel (0-255)",
|
|
54
|
+
},
|
|
55
|
+
baseHeight: {
|
|
56
|
+
type: "number",
|
|
57
|
+
default_value: 0,
|
|
58
|
+
hint: "Base plane height used as the minimum elevation",
|
|
59
|
+
},
|
|
60
|
+
invertHeights: {
|
|
61
|
+
type: "boolean",
|
|
62
|
+
default_value: false,
|
|
63
|
+
hint: "Invert grayscale so dark pixels become tall regions",
|
|
64
|
+
},
|
|
65
|
+
pixelScale: {
|
|
66
|
+
type: "number",
|
|
67
|
+
default_value: 1,
|
|
68
|
+
hint: "World units per pixel in X/Y directions",
|
|
69
|
+
},
|
|
70
|
+
center: {
|
|
71
|
+
type: "boolean",
|
|
72
|
+
default_value: true,
|
|
73
|
+
hint: "Center the heightmap around the origin before placement",
|
|
74
|
+
},
|
|
75
|
+
sampleStride: {
|
|
76
|
+
type: "number",
|
|
77
|
+
default_value: 1,
|
|
78
|
+
hint: "Sample every Nth pixel (higher stride reduces triangles)",
|
|
79
|
+
},
|
|
80
|
+
placementPlane: {
|
|
81
|
+
type: "reference_selection",
|
|
82
|
+
selectionFilter: ["PLANE", "FACE"],
|
|
83
|
+
multiple: false,
|
|
84
|
+
default_value: null,
|
|
85
|
+
hint: "Select a plane or face where the heightmap will be placed",
|
|
86
|
+
},
|
|
87
|
+
simplifyTolerance: {
|
|
88
|
+
type: "number",
|
|
89
|
+
default_value: 0,
|
|
90
|
+
hint: "Simplify tolerance (>0 enables Manifold simplify)",
|
|
91
|
+
},
|
|
92
|
+
boolean: {
|
|
93
|
+
type: "boolean_operation",
|
|
94
|
+
default_value: { targets: [], operation: 'NONE' },
|
|
95
|
+
hint: "Optional boolean operation with selected solids"
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
export class ImageHeightmapSolidFeature {
|
|
100
|
+
static shortName = "HEIGHTMAP";
|
|
101
|
+
static longName = "Image Heightmap Solid";
|
|
102
|
+
static inputParamsSchema = inputParamsSchema;
|
|
103
|
+
|
|
104
|
+
constructor() {
|
|
105
|
+
this.inputParams = {};
|
|
106
|
+
this.persistentData = {};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async run(partHistory) {
|
|
110
|
+
const {
|
|
111
|
+
fileToImport,
|
|
112
|
+
heightScale,
|
|
113
|
+
baseHeight,
|
|
114
|
+
invertHeights,
|
|
115
|
+
pixelScale,
|
|
116
|
+
center,
|
|
117
|
+
sampleStride,
|
|
118
|
+
simplifyTolerance,
|
|
119
|
+
} = this.inputParams;
|
|
120
|
+
|
|
121
|
+
const imageData = await decodeToImageData(fileToImport);
|
|
122
|
+
if (!imageData) {
|
|
123
|
+
console.warn('[HEIGHTMAP] No image data decoded');
|
|
124
|
+
return { added: [], removed: [] };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const width = imageData.width | 0;
|
|
128
|
+
const height = imageData.height | 0;
|
|
129
|
+
if (!(width >= 2 && height >= 2)) {
|
|
130
|
+
console.warn('[HEIGHTMAP] Heightmap requires an image at least 2x2 pixels');
|
|
131
|
+
return { added: [], removed: [] };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const src = imageData.data;
|
|
135
|
+
const scaleXY = Number.isFinite(Number(pixelScale)) && Number(pixelScale) !== 0 ? Number(pixelScale) : 1;
|
|
136
|
+
const scaleZ = Number.isFinite(Number(heightScale)) ? Number(heightScale) : 1;
|
|
137
|
+
const base = Number.isFinite(Number(baseHeight)) ? Number(baseHeight) : 0;
|
|
138
|
+
const invert = !!invertHeights;
|
|
139
|
+
const centerXY = center !== false;
|
|
140
|
+
const strideRaw = Number(sampleStride);
|
|
141
|
+
const stride = Number.isFinite(strideRaw) && strideRaw >= 1 ? Math.floor(strideRaw) : 1;
|
|
142
|
+
|
|
143
|
+
const offsetX = centerXY ? (width - 1) * 0.5 : 0;
|
|
144
|
+
const offsetY = centerXY ? (height - 1) * 0.5 : 0;
|
|
145
|
+
|
|
146
|
+
const sampleXs = buildSampleIndices(width, stride);
|
|
147
|
+
const sampleYs = buildSampleIndices(height, stride);
|
|
148
|
+
const gridWidth = sampleXs.length;
|
|
149
|
+
const gridHeight = sampleYs.length;
|
|
150
|
+
if (!(gridWidth >= 2 && gridHeight >= 2)) {
|
|
151
|
+
console.warn('[HEIGHTMAP] Sampling produced insufficient grid resolution');
|
|
152
|
+
return { added: [], removed: [] };
|
|
153
|
+
}
|
|
154
|
+
const idx = (x, y) => y * gridWidth + x;
|
|
155
|
+
const topVerts = new Array(gridWidth * gridHeight);
|
|
156
|
+
const bottomVerts = new Array(gridWidth * gridHeight);
|
|
157
|
+
|
|
158
|
+
let minX = Infinity, minY = Infinity, minZ = Infinity;
|
|
159
|
+
let maxX = -Infinity, maxY = -Infinity, maxZ = -Infinity;
|
|
160
|
+
const updateBounds = (p) => {
|
|
161
|
+
if (!p) return;
|
|
162
|
+
if (p[0] < minX) minX = p[0];
|
|
163
|
+
if (p[1] < minY) minY = p[1];
|
|
164
|
+
if (p[2] < minZ) minZ = p[2];
|
|
165
|
+
if (p[0] > maxX) maxX = p[0];
|
|
166
|
+
if (p[1] > maxY) maxY = p[1];
|
|
167
|
+
if (p[2] > maxZ) maxZ = p[2];
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
for (let gy = 0; gy < gridHeight; gy++) {
|
|
171
|
+
const sy = sampleYs[gy];
|
|
172
|
+
for (let gx = 0; gx < gridWidth; gx++) {
|
|
173
|
+
const sx = sampleXs[gx];
|
|
174
|
+
const i = idx(gx, gy);
|
|
175
|
+
const si = (sy * width + sx) * 4;
|
|
176
|
+
const r = src[si] | 0;
|
|
177
|
+
const g = src[si + 1] | 0;
|
|
178
|
+
const b = src[si + 2] | 0;
|
|
179
|
+
const a = src[si + 3] | 0;
|
|
180
|
+
const gray = (r * 0.2126 + g * 0.7152 + b * 0.0722) / 255;
|
|
181
|
+
const alphaMask = (a >= 16) ? 1 : 0;
|
|
182
|
+
let value = invert ? (1 - gray) : gray;
|
|
183
|
+
if (alphaMask === 0) value = 0;
|
|
184
|
+
value = Math.max(0, Math.min(1, value));
|
|
185
|
+
const h = base + value * scaleZ;
|
|
186
|
+
const px = (sx - offsetX) * scaleXY;
|
|
187
|
+
const py = ((centerXY ? (offsetY - sy) : -sy)) * scaleXY;
|
|
188
|
+
const top = [px, py, h];
|
|
189
|
+
const bottom = [px, py, base];
|
|
190
|
+
topVerts[i] = top;
|
|
191
|
+
bottomVerts[i] = bottom;
|
|
192
|
+
updateBounds(top);
|
|
193
|
+
updateBounds(bottom);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (!Number.isFinite(minX) || !Number.isFinite(maxX) || !Number.isFinite(minZ) || !Number.isFinite(maxZ)) {
|
|
198
|
+
console.warn('[HEIGHTMAP] Failed to compute geometry bounds');
|
|
199
|
+
return { added: [], removed: [] };
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (!((maxZ - minZ) > 1e-9)) {
|
|
203
|
+
console.warn('[HEIGHTMAP] Heightmap has zero thickness; increase heightScale or height variation');
|
|
204
|
+
return { added: [], removed: [] };
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const centerVec = [
|
|
208
|
+
(minX + maxX) * 0.5,
|
|
209
|
+
(minY + maxY) * 0.5,
|
|
210
|
+
(minZ + maxZ) * 0.5,
|
|
211
|
+
];
|
|
212
|
+
|
|
213
|
+
const solid = new BREP.Solid();
|
|
214
|
+
const featureName = this.inputParams.featureID || 'HEIGHTMAP_SOLID';
|
|
215
|
+
solid.name = featureName;
|
|
216
|
+
|
|
217
|
+
const addTriangleFacingOutward = (faceName, p0, p1, p2) => {
|
|
218
|
+
if (!p0 || !p1 || !p2) return;
|
|
219
|
+
const ax = p1[0] - p0[0];
|
|
220
|
+
const ay = p1[1] - p0[1];
|
|
221
|
+
const az = p1[2] - p0[2];
|
|
222
|
+
const bx = p2[0] - p0[0];
|
|
223
|
+
const by = p2[1] - p0[1];
|
|
224
|
+
const bz = p2[2] - p0[2];
|
|
225
|
+
const nx = ay * bz - az * by;
|
|
226
|
+
const ny = az * bx - ax * bz;
|
|
227
|
+
const nz = ax * by - ay * bx;
|
|
228
|
+
const lenSq = nx * nx + ny * ny + nz * nz;
|
|
229
|
+
if (!(lenSq > 1e-24)) return;
|
|
230
|
+
const cx = (p0[0] + p1[0] + p2[0]) / 3;
|
|
231
|
+
const cy = (p0[1] + p1[1] + p2[1]) / 3;
|
|
232
|
+
const cz = (p0[2] + p1[2] + p2[2]) / 3;
|
|
233
|
+
const vx = centerVec[0] - cx;
|
|
234
|
+
const vy = centerVec[1] - cy;
|
|
235
|
+
const vz = centerVec[2] - cz;
|
|
236
|
+
const dot = nx * vx + ny * vy + nz * vz;
|
|
237
|
+
if (dot > 0) {
|
|
238
|
+
solid.addTriangle(faceName, p0, p2, p1);
|
|
239
|
+
} else {
|
|
240
|
+
solid.addTriangle(faceName, p0, p1, p2);
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
const faceBase = featureName ? `${featureName}` : 'HEIGHTMAP';
|
|
245
|
+
const topFace = `${faceBase}:TOP`;
|
|
246
|
+
const bottomFace = `${faceBase}:BOTTOM`;
|
|
247
|
+
const sidePosY = `${faceBase}:SIDE_POS_Y`;
|
|
248
|
+
const sideNegY = `${faceBase}:SIDE_NEG_Y`;
|
|
249
|
+
const sideNegX = `${faceBase}:SIDE_NEG_X`;
|
|
250
|
+
const sidePosX = `${faceBase}:SIDE_POS_X`;
|
|
251
|
+
|
|
252
|
+
for (let y = 0; y < gridHeight - 1; y++) {
|
|
253
|
+
for (let x = 0; x < gridWidth - 1; x++) {
|
|
254
|
+
const i00 = idx(x, y);
|
|
255
|
+
const i10 = idx(x + 1, y);
|
|
256
|
+
const i01 = idx(x, y + 1);
|
|
257
|
+
const i11 = idx(x + 1, y + 1);
|
|
258
|
+
const v00 = topVerts[i00];
|
|
259
|
+
const v10 = topVerts[i10];
|
|
260
|
+
const v01 = topVerts[i01];
|
|
261
|
+
const v11 = topVerts[i11];
|
|
262
|
+
addTriangleFacingOutward(topFace, v00, v11, v10);
|
|
263
|
+
addTriangleFacingOutward(topFace, v00, v01, v11);
|
|
264
|
+
|
|
265
|
+
const b00 = bottomVerts[i00];
|
|
266
|
+
const b10 = bottomVerts[i10];
|
|
267
|
+
const b01 = bottomVerts[i01];
|
|
268
|
+
const b11 = bottomVerts[i11];
|
|
269
|
+
addTriangleFacingOutward(bottomFace, b00, b10, b11);
|
|
270
|
+
addTriangleFacingOutward(bottomFace, b00, b11, b01);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const addSideQuad = (faceName, topA, topB, bottomB, bottomA) => {
|
|
275
|
+
addTriangleFacingOutward(faceName, topA, topB, bottomB);
|
|
276
|
+
addTriangleFacingOutward(faceName, topA, bottomB, bottomA);
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
for (let x = 0; x < gridWidth - 1; x++) {
|
|
280
|
+
const iA = idx(x, 0);
|
|
281
|
+
const iB = idx(x + 1, 0);
|
|
282
|
+
addSideQuad(sidePosY, topVerts[iA], topVerts[iB], bottomVerts[iB], bottomVerts[iA]);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
for (let x = 0; x < gridWidth - 1; x++) {
|
|
286
|
+
const row = gridHeight - 1;
|
|
287
|
+
const iA = idx(x, row);
|
|
288
|
+
const iB = idx(x + 1, row);
|
|
289
|
+
addSideQuad(sideNegY, topVerts[iA], topVerts[iB], bottomVerts[iB], bottomVerts[iA]);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
for (let y = 0; y < gridHeight - 1; y++) {
|
|
293
|
+
const iA = idx(0, y);
|
|
294
|
+
const iB = idx(0, y + 1);
|
|
295
|
+
addSideQuad(sideNegX, topVerts[iA], topVerts[iB], bottomVerts[iB], bottomVerts[iA]);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
for (let y = 0; y < gridHeight - 1; y++) {
|
|
299
|
+
const col = gridWidth - 1;
|
|
300
|
+
const iA = idx(col, y);
|
|
301
|
+
const iB = idx(col, y + 1);
|
|
302
|
+
addSideQuad(sidePosX, topVerts[iA], topVerts[iB], bottomVerts[iB], bottomVerts[iA]);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const basis = getPlacementBasis(this.inputParams?.placementPlane, partHistory);
|
|
306
|
+
const origin = new THREE.Vector3().fromArray(basis.origin);
|
|
307
|
+
const xAxis = new THREE.Vector3().fromArray(basis.x);
|
|
308
|
+
const yAxis = new THREE.Vector3().fromArray(basis.y);
|
|
309
|
+
const zAxis = new THREE.Vector3().fromArray(basis.z);
|
|
310
|
+
const placement = new THREE.Matrix4().makeBasis(xAxis, yAxis, zAxis).setPosition(origin);
|
|
311
|
+
solid.bakeTransform(placement);
|
|
312
|
+
|
|
313
|
+
solid.userData = solid.userData || {};
|
|
314
|
+
solid.userData.heightmap = {
|
|
315
|
+
width,
|
|
316
|
+
height,
|
|
317
|
+
pixelScale: scaleXY,
|
|
318
|
+
heightScale: scaleZ,
|
|
319
|
+
baseHeight: base,
|
|
320
|
+
invert: invert,
|
|
321
|
+
sampleStride: stride,
|
|
322
|
+
gridWidth,
|
|
323
|
+
gridHeight,
|
|
324
|
+
};
|
|
325
|
+
let finalSolid = solid;
|
|
326
|
+
const tol = Number(simplifyTolerance);
|
|
327
|
+
if (Number.isFinite(tol) && tol > 0) {
|
|
328
|
+
try {
|
|
329
|
+
const simplified = solid.simplify(tol);
|
|
330
|
+
if (simplified && simplified instanceof BREP.Solid) finalSolid = simplified;
|
|
331
|
+
} catch (e) {
|
|
332
|
+
console.warn('[HEIGHTMAP] Simplify failed, using original solid:', e);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
if (finalSolid !== solid) {
|
|
336
|
+
finalSolid.name = featureName;
|
|
337
|
+
finalSolid.userData = { ...solid.userData };
|
|
338
|
+
}
|
|
339
|
+
finalSolid.visualize();
|
|
340
|
+
|
|
341
|
+
const effects = await BREP.applyBooleanOperation(partHistory || {}, finalSolid, this.inputParams.boolean, this.inputParams.featureID);
|
|
342
|
+
return effects;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
async function decodeToImageData(raw) {
|
|
347
|
+
try {
|
|
348
|
+
if (!raw) return null;
|
|
349
|
+
if (raw instanceof ImageData) return raw;
|
|
350
|
+
if (raw instanceof ArrayBuffer) {
|
|
351
|
+
try {
|
|
352
|
+
const blob = new Blob([raw], { type: 'image/png' });
|
|
353
|
+
const img = await createImageBitmap(blob);
|
|
354
|
+
const c = document.createElement('canvas');
|
|
355
|
+
c.width = img.width; c.height = img.height;
|
|
356
|
+
const ctx = c.getContext('2d');
|
|
357
|
+
ctx.drawImage(img, 0, 0);
|
|
358
|
+
const id = ctx.getImageData(0, 0, img.width, img.height);
|
|
359
|
+
try { img.close && img.close(); } catch { }
|
|
360
|
+
return id;
|
|
361
|
+
} catch { }
|
|
362
|
+
return null;
|
|
363
|
+
}
|
|
364
|
+
if (typeof raw === 'string') {
|
|
365
|
+
if (raw.startsWith('data:')) {
|
|
366
|
+
const img = await createImageBitmap(await (await fetch(raw)).blob());
|
|
367
|
+
const c = document.createElement('canvas');
|
|
368
|
+
c.width = img.width; c.height = img.height;
|
|
369
|
+
const ctx = c.getContext('2d');
|
|
370
|
+
ctx.drawImage(img, 0, 0);
|
|
371
|
+
const id = ctx.getImageData(0, 0, img.width, img.height);
|
|
372
|
+
try { img.close && img.close(); } catch { }
|
|
373
|
+
return id;
|
|
374
|
+
}
|
|
375
|
+
try {
|
|
376
|
+
const b64 = raw;
|
|
377
|
+
const binaryStr = (typeof atob === 'function') ? atob(b64) : (typeof Buffer !== 'undefined' ? Buffer.from(b64, 'base64').toString('binary') : '');
|
|
378
|
+
const len = binaryStr.length | 0;
|
|
379
|
+
const bytes = new Uint8Array(len);
|
|
380
|
+
for (let i = 0; i < len; i++) bytes[i] = binaryStr.charCodeAt(i) & 0xff;
|
|
381
|
+
const blob = new Blob([bytes], { type: 'image/png' });
|
|
382
|
+
const img = await createImageBitmap(blob);
|
|
383
|
+
const c = document.createElement('canvas');
|
|
384
|
+
c.width = img.width; c.height = img.height;
|
|
385
|
+
const ctx = c.getContext('2d');
|
|
386
|
+
ctx.drawImage(img, 0, 0);
|
|
387
|
+
const id = ctx.getImageData(0, 0, img.width, img.height);
|
|
388
|
+
try { img.close && img.close(); } catch { }
|
|
389
|
+
return id;
|
|
390
|
+
} catch { }
|
|
391
|
+
return null;
|
|
392
|
+
}
|
|
393
|
+
} catch (e) {
|
|
394
|
+
console.warn('[HEIGHTMAP] Failed to decode input as image data', e);
|
|
395
|
+
}
|
|
396
|
+
return null;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
function buildSampleIndices(count, stride) {
|
|
400
|
+
const out = [];
|
|
401
|
+
if (count <= 0) return out;
|
|
402
|
+
for (let i = 0; i < count; i += stride) out.push(i);
|
|
403
|
+
const last = count - 1;
|
|
404
|
+
if (out[out.length - 1] !== last) out.push(last);
|
|
405
|
+
return out;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function getPlacementBasis(ref, partHistory) {
|
|
409
|
+
const x = new THREE.Vector3(1, 0, 0);
|
|
410
|
+
const y = new THREE.Vector3(0, 1, 0);
|
|
411
|
+
const z = new THREE.Vector3(0, 0, 1);
|
|
412
|
+
const origin = new THREE.Vector3(0, 0, 0);
|
|
413
|
+
|
|
414
|
+
let refObj = null;
|
|
415
|
+
try {
|
|
416
|
+
if (Array.isArray(ref)) refObj = ref[0] || null;
|
|
417
|
+
else if (ref && typeof ref === 'object') refObj = ref;
|
|
418
|
+
else if (ref) refObj = partHistory?.scene?.getObjectByName(ref);
|
|
419
|
+
} catch { }
|
|
420
|
+
|
|
421
|
+
if (refObj) {
|
|
422
|
+
try { refObj.updateWorldMatrix(true, true); } catch { }
|
|
423
|
+
try {
|
|
424
|
+
const g = refObj.geometry;
|
|
425
|
+
if (g) {
|
|
426
|
+
const bs = g.boundingSphere || (g.computeBoundingSphere(), g.boundingSphere);
|
|
427
|
+
if (bs) origin.copy(refObj.localToWorld(bs.center.clone()));
|
|
428
|
+
else origin.copy(refObj.getWorldPosition(new THREE.Vector3()));
|
|
429
|
+
} else origin.copy(refObj.getWorldPosition(new THREE.Vector3()));
|
|
430
|
+
} catch { origin.copy(refObj.getWorldPosition(new THREE.Vector3())); }
|
|
431
|
+
|
|
432
|
+
let n = null;
|
|
433
|
+
try {
|
|
434
|
+
if (refObj.type === 'FACE' && typeof refObj.getAverageNormal === 'function') {
|
|
435
|
+
n = refObj.getAverageNormal().clone();
|
|
436
|
+
} else {
|
|
437
|
+
n = new THREE.Vector3(0, 0, 1).applyQuaternion(refObj.getWorldQuaternion(new THREE.Quaternion()));
|
|
438
|
+
}
|
|
439
|
+
} catch { n = null; }
|
|
440
|
+
if (!n || n.lengthSq() < 1e-16) n = new THREE.Vector3(0, 0, 1);
|
|
441
|
+
n.normalize();
|
|
442
|
+
|
|
443
|
+
const worldX = new THREE.Vector3(1, 0, 0).applyQuaternion(refObj.getWorldQuaternion(new THREE.Quaternion()));
|
|
444
|
+
const worldY = new THREE.Vector3().crossVectors(n, worldX);
|
|
445
|
+
if (worldY.lengthSq() < 1e-20) {
|
|
446
|
+
worldX.set(1, 0, 0);
|
|
447
|
+
worldY.set(0, 1, 0);
|
|
448
|
+
} else {
|
|
449
|
+
worldY.normalize();
|
|
450
|
+
worldX.crossVectors(worldY, n).normalize();
|
|
451
|
+
}
|
|
452
|
+
x.copy(worldX);
|
|
453
|
+
y.copy(worldY);
|
|
454
|
+
z.copy(n);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
return {
|
|
458
|
+
origin: [origin.x, origin.y, origin.z],
|
|
459
|
+
x: [x.x, x.y, x.z],
|
|
460
|
+
y: [y.x, y.y, y.z],
|
|
461
|
+
z: [z.x, z.y, z.z],
|
|
462
|
+
};
|
|
463
|
+
}
|