brep-io-kernel 1.0.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/LICENSE.md +32 -0
- package/README.md +144 -0
- package/dist-kernel/brep-kernel.js +74699 -0
- package/dist-kernel/help/CONTRIBUTING.html +248 -0
- package/dist-kernel/help/LICENSE.html +248 -0
- package/dist-kernel/help/MODELING.png +0 -0
- package/dist-kernel/help/PMI.png +0 -0
- package/dist-kernel/help/SKETCH.png +0 -0
- package/dist-kernel/help/assembly-constraints__Coincident_Constraint_dialog.png +0 -0
- package/dist-kernel/help/assembly-constraints___Angle_Constraint_dialog.png +0 -0
- package/dist-kernel/help/assembly-constraints___Distance_Constraint_dialog.png +0 -0
- package/dist-kernel/help/assembly-constraints___Fixed_Constraint_dialog.png +0 -0
- package/dist-kernel/help/assembly-constraints___Parallel_Constraint_dialog.png +0 -0
- package/dist-kernel/help/assembly-constraints___Touch_Align_Constraint_dialog.png +0 -0
- package/dist-kernel/help/assembly-constraints__angle-constraint.html +248 -0
- package/dist-kernel/help/assembly-constraints__coincident-constraint.html +248 -0
- package/dist-kernel/help/assembly-constraints__distance-constraint.html +248 -0
- package/dist-kernel/help/assembly-constraints__fixed-constraint.html +248 -0
- package/dist-kernel/help/assembly-constraints__parallel-constraint.html +248 -0
- package/dist-kernel/help/assembly-constraints__solver.html +248 -0
- package/dist-kernel/help/assembly-constraints__touch-align-constraint.html +248 -0
- package/dist-kernel/help/brep-api.html +263 -0
- package/dist-kernel/help/brep-kernel.html +258 -0
- package/dist-kernel/help/brep-model.html +248 -0
- package/dist-kernel/help/cylindrical-face-radius-embedding.html +290 -0
- package/dist-kernel/help/dialog-screenshots.html +248 -0
- package/dist-kernel/help/extruded-sketch-radius-embedding.html +336 -0
- package/dist-kernel/help/features__Assembly_Component_dialog.png +0 -0
- package/dist-kernel/help/features__Boolean_dialog.png +0 -0
- package/dist-kernel/help/features__Chamfer_dialog.png +0 -0
- package/dist-kernel/help/features__Datium_dialog.png +0 -0
- package/dist-kernel/help/features__Extrude_dialog.png +0 -0
- package/dist-kernel/help/features__Fillet_dialog.png +0 -0
- package/dist-kernel/help/features__Helix_dialog.png +0 -0
- package/dist-kernel/help/features__Hole_dialog.png +0 -0
- package/dist-kernel/help/features__Image_Heightmap_Solid_dialog.png +0 -0
- package/dist-kernel/help/features__Image_to_Face_dialog.png +0 -0
- package/dist-kernel/help/features__Import_3D_Model_dialog.png +0 -0
- package/dist-kernel/help/features__Loft_dialog.png +0 -0
- package/dist-kernel/help/features__Mirror_dialog.png +0 -0
- package/dist-kernel/help/features__Offset_Shell_dialog.png +0 -0
- package/dist-kernel/help/features__Overlap_Cleanup_dialog.png +0 -0
- package/dist-kernel/help/features__Pattern_Linear_dialog.png +0 -0
- package/dist-kernel/help/features__Pattern_Radial_dialog.png +0 -0
- package/dist-kernel/help/features__Pattern_dialog.png +0 -0
- package/dist-kernel/help/features__Plane_dialog.png +0 -0
- package/dist-kernel/help/features__Primitive_Cone_dialog.png +0 -0
- package/dist-kernel/help/features__Primitive_Cube_dialog.png +0 -0
- package/dist-kernel/help/features__Primitive_Cylinder_dialog.png +0 -0
- package/dist-kernel/help/features__Primitive_Pyramid_dialog.png +0 -0
- package/dist-kernel/help/features__Primitive_Sphere_dialog.png +0 -0
- package/dist-kernel/help/features__Primitive_Torus_dialog.png +0 -0
- package/dist-kernel/help/features__Remesh_dialog.png +0 -0
- package/dist-kernel/help/features__Revolve_dialog.png +0 -0
- package/dist-kernel/help/features__Sheet_Metal_Contour_Flange_dialog.png +0 -0
- package/dist-kernel/help/features__Sheet_Metal_Cutout_dialog.png +0 -0
- package/dist-kernel/help/features__Sheet_Metal_Flange_dialog.png +0 -0
- package/dist-kernel/help/features__Sheet_Metal_Tab_dialog.png +0 -0
- package/dist-kernel/help/features__Sketch_dialog.png +0 -0
- package/dist-kernel/help/features__Spline_dialog.png +0 -0
- package/dist-kernel/help/features__Sweep_dialog.png +0 -0
- package/dist-kernel/help/features__Transform_dialog.png +0 -0
- package/dist-kernel/help/features__Tube_dialog.png +0 -0
- package/dist-kernel/help/features__assembly-component.html +248 -0
- package/dist-kernel/help/features__boolean.html +248 -0
- package/dist-kernel/help/features__chamfer.html +248 -0
- package/dist-kernel/help/features__datium.html +248 -0
- package/dist-kernel/help/features__datum.html +248 -0
- package/dist-kernel/help/features__extrude.html +248 -0
- package/dist-kernel/help/features__fillet.html +248 -0
- package/dist-kernel/help/features__helix.html +248 -0
- package/dist-kernel/help/features__hole.html +248 -0
- package/dist-kernel/help/features__image-heightmap-solid.html +248 -0
- package/dist-kernel/help/features__image-to-face-2D_dialog.png +0 -0
- package/dist-kernel/help/features__image-to-face-3D_dialog.png +0 -0
- package/dist-kernel/help/features__image-to-face.html +248 -0
- package/dist-kernel/help/features__import-3d-model.html +248 -0
- package/dist-kernel/help/features__index.html +248 -0
- package/dist-kernel/help/features__loft.html +248 -0
- package/dist-kernel/help/features__mirror.html +248 -0
- package/dist-kernel/help/features__offset-shell.html +248 -0
- package/dist-kernel/help/features__pattern-linear.html +248 -0
- package/dist-kernel/help/features__pattern-radial.html +248 -0
- package/dist-kernel/help/features__pattern.html +248 -0
- package/dist-kernel/help/features__plane.html +248 -0
- package/dist-kernel/help/features__primitive-cone.html +248 -0
- package/dist-kernel/help/features__primitive-cube.html +248 -0
- package/dist-kernel/help/features__primitive-cylinder.html +248 -0
- package/dist-kernel/help/features__primitive-pyramid.html +248 -0
- package/dist-kernel/help/features__primitive-sphere.html +248 -0
- package/dist-kernel/help/features__primitive-torus.html +248 -0
- package/dist-kernel/help/features__remesh.html +248 -0
- package/dist-kernel/help/features__revolve.html +248 -0
- package/dist-kernel/help/features__sheet-metal-contour-flange.html +248 -0
- package/dist-kernel/help/features__sheet-metal-flange.html +248 -0
- package/dist-kernel/help/features__sheet-metal-tab.html +248 -0
- package/dist-kernel/help/features__sketch.html +248 -0
- package/dist-kernel/help/features__spline.html +248 -0
- package/dist-kernel/help/features__sweep.html +248 -0
- package/dist-kernel/help/features__transform.html +248 -0
- package/dist-kernel/help/features__tube.html +248 -0
- package/dist-kernel/help/file-formats.html +248 -0
- package/dist-kernel/help/getting-started.html +248 -0
- package/dist-kernel/help/highlights.html +248 -0
- package/dist-kernel/help/history-systems.html +248 -0
- package/dist-kernel/help/how-it-works.html +248 -0
- package/dist-kernel/help/index.html +862 -0
- package/dist-kernel/help/input-params-schema.html +363 -0
- package/dist-kernel/help/inspector-improvements.html +248 -0
- package/dist-kernel/help/inspector.html +248 -0
- package/dist-kernel/help/modes__modeling.html +248 -0
- package/dist-kernel/help/modes__pmi.html +248 -0
- package/dist-kernel/help/modes__sketch.html +248 -0
- package/dist-kernel/help/plugins.html +248 -0
- package/dist-kernel/help/pmi-annotations__Angle_Dimension_dialog.png +0 -0
- package/dist-kernel/help/pmi-annotations__Explode_Body_dialog.png +0 -0
- package/dist-kernel/help/pmi-annotations__Hole_Callout_dialog.png +0 -0
- package/dist-kernel/help/pmi-annotations__Leader_dialog.png +0 -0
- package/dist-kernel/help/pmi-annotations__Linear_Dimension_dialog.png +0 -0
- package/dist-kernel/help/pmi-annotations__Note_dialog.png +0 -0
- package/dist-kernel/help/pmi-annotations__Radial_Dimension_dialog.png +0 -0
- package/dist-kernel/help/pmi-annotations__angle-dimension.html +248 -0
- package/dist-kernel/help/pmi-annotations__explode-body.html +248 -0
- package/dist-kernel/help/pmi-annotations__hole-callout.html +248 -0
- package/dist-kernel/help/pmi-annotations__index.html +248 -0
- package/dist-kernel/help/pmi-annotations__leader.html +248 -0
- package/dist-kernel/help/pmi-annotations__linear-dimension.html +248 -0
- package/dist-kernel/help/pmi-annotations__note.html +248 -0
- package/dist-kernel/help/pmi-annotations__radial-dimension.html +248 -0
- package/dist-kernel/help/search-index.json +464 -0
- package/dist-kernel/help/simplified-radial-dimensions.html +298 -0
- package/dist-kernel/help/solid-methods.html +359 -0
- package/dist-kernel/help/table-of-contents.html +330 -0
- package/dist-kernel/help/ui-overview.html +248 -0
- package/dist-kernel/help/whats-new.html +248 -0
- package/package.json +54 -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,1132 @@
|
|
|
1
|
+
//
|
|
2
|
+
import * as THREE from 'three';
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
// Feature classes live in their own files; registry wires them up.
|
|
6
|
+
import { FeatureRegistry } from './FeatureRegistry.js';
|
|
7
|
+
import { SelectionFilter } from './UI/SelectionFilter.js';
|
|
8
|
+
import { MetadataManager } from './metadataManager.js';
|
|
9
|
+
import { AssemblyConstraintRegistry } from './assemblyConstraints/AssemblyConstraintRegistry.js';
|
|
10
|
+
import { AssemblyConstraintHistory } from './assemblyConstraints/AssemblyConstraintHistory.js';
|
|
11
|
+
import { AssemblyComponentFeature } from './features/assemblyComponent/AssemblyComponentFeature.js';
|
|
12
|
+
import { getComponentRecord, base64ToUint8Array } from './services/componentLibrary.js';
|
|
13
|
+
import { PMIViewsManager } from './pmi/PMIViewsManager.js';
|
|
14
|
+
import { deepClone } from './utils/deepClone.js';
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
const debug = false;
|
|
18
|
+
|
|
19
|
+
function resolveFeatureEntryId(entry, fallback = null) {
|
|
20
|
+
if (!entry) return fallback;
|
|
21
|
+
const params = entry.inputParams || {};
|
|
22
|
+
const rawId = params.id ?? params.featureID ?? entry.id ?? fallback;
|
|
23
|
+
if (rawId == null) return fallback;
|
|
24
|
+
return String(rawId);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
export class PartHistory {
|
|
29
|
+
constructor() {
|
|
30
|
+
this.features = [];
|
|
31
|
+
this.scene = new THREE.Scene();
|
|
32
|
+
this.idCounter = 0;
|
|
33
|
+
this.featureRegistry = new FeatureRegistry();
|
|
34
|
+
this.assemblyConstraintRegistry = new AssemblyConstraintRegistry();
|
|
35
|
+
this.assemblyConstraintHistory = new AssemblyConstraintHistory(this, this.assemblyConstraintRegistry);
|
|
36
|
+
this.callbacks = {};
|
|
37
|
+
this.currentHistoryStepId = null;
|
|
38
|
+
this.expressions = "//Examples:\nx = 10 + 6; \ny = x * 2;";
|
|
39
|
+
this.pmiViewsManager = new PMIViewsManager(this);
|
|
40
|
+
this.metadataManager = new MetadataManager
|
|
41
|
+
this._historyUndo = {
|
|
42
|
+
undoStack: [],
|
|
43
|
+
redoStack: [],
|
|
44
|
+
max: 50,
|
|
45
|
+
debounceMs: 350,
|
|
46
|
+
pendingTimer: null,
|
|
47
|
+
lastSignature: null,
|
|
48
|
+
captureInFlight: false,
|
|
49
|
+
pendingRequest: false,
|
|
50
|
+
isApplying: false,
|
|
51
|
+
};
|
|
52
|
+
if (this.assemblyConstraintHistory) {
|
|
53
|
+
this.assemblyConstraintHistory.clear();
|
|
54
|
+
this.assemblyConstraintHistory.setPartHistory(this);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
// overide the scenes remove method to console log removals along with the stack trace
|
|
59
|
+
|
|
60
|
+
const originalRemove = this.scene.remove;
|
|
61
|
+
this.scene.remove = (...args) => {
|
|
62
|
+
//console.log("Removing from scene:", args);
|
|
63
|
+
if (args[0]?.userData?.preventRemove) {
|
|
64
|
+
console.log("Removal prevented by object flag.");
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
//console.trace();
|
|
69
|
+
originalRemove.apply(this.scene, args);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// overide the scenes add method to console log additions along with the stack trace
|
|
73
|
+
const originalAdd = this.scene.add;
|
|
74
|
+
this.scene.add = (...args) => {
|
|
75
|
+
//console.log("Adding to scene:", args);
|
|
76
|
+
//console.trace();
|
|
77
|
+
originalAdd.apply(this.scene, args);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
#runFeatureEntryMigrations(rawFeature) {
|
|
83
|
+
if (!rawFeature || typeof rawFeature !== 'object') return rawFeature;
|
|
84
|
+
const migrated = rawFeature;
|
|
85
|
+
|
|
86
|
+
if (Object.prototype.hasOwnProperty.call(migrated, 'featureID')) {
|
|
87
|
+
if (!migrated.id && migrated.featureID != null) {
|
|
88
|
+
migrated.id = migrated.featureID;
|
|
89
|
+
}
|
|
90
|
+
try { delete migrated.featureID; } catch { /* ignore */ }
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const paramsSource = migrated.inputParams && typeof migrated.inputParams === 'object'
|
|
94
|
+
? migrated.inputParams
|
|
95
|
+
: null;
|
|
96
|
+
if (paramsSource) {
|
|
97
|
+
if (Object.prototype.hasOwnProperty.call(paramsSource, 'featureID')) {
|
|
98
|
+
if ((paramsSource.id == null || paramsSource.id === '') && paramsSource.featureID != null) {
|
|
99
|
+
paramsSource.id = paramsSource.featureID;
|
|
100
|
+
}
|
|
101
|
+
try { delete paramsSource.featureID; } catch { /* ignore */ }
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if ((migrated.id == null || migrated.id === '') && paramsSource?.id != null) {
|
|
106
|
+
migrated.id = paramsSource.id;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return migrated;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
#linkFeatureParams(feature) {
|
|
113
|
+
if (!feature || typeof feature !== 'object') return;
|
|
114
|
+
if (!feature.inputParams || typeof feature.inputParams !== 'object') {
|
|
115
|
+
feature.inputParams = {};
|
|
116
|
+
}
|
|
117
|
+
if (!feature.persistentData || typeof feature.persistentData !== 'object') {
|
|
118
|
+
feature.persistentData = {};
|
|
119
|
+
}
|
|
120
|
+
const params = feature.inputParams;
|
|
121
|
+
const descriptor = { configurable: true, enumerable: false };
|
|
122
|
+
const rawId = params.id ?? params.featureID ?? feature.id;
|
|
123
|
+
if (rawId != null && rawId !== '') {
|
|
124
|
+
const normalized = String(rawId);
|
|
125
|
+
params.id = normalized;
|
|
126
|
+
feature.id = normalized;
|
|
127
|
+
} else if (params.id != null && params.id !== feature.id) {
|
|
128
|
+
feature.id = params.id;
|
|
129
|
+
} else if (feature.id != null && feature.id !== params.id) {
|
|
130
|
+
params.id = feature.id;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (!Object.getOwnPropertyDescriptor(params, 'featureID')) {
|
|
134
|
+
Object.defineProperty(params, 'featureID', {
|
|
135
|
+
...descriptor,
|
|
136
|
+
get: () => params.id,
|
|
137
|
+
set: (value) => {
|
|
138
|
+
if (value == null || value === '') {
|
|
139
|
+
params.id = value;
|
|
140
|
+
feature.id = value;
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
const normalized = String(value);
|
|
144
|
+
params.id = normalized;
|
|
145
|
+
feature.id = normalized;
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
#prepareFeatureEntry(rawFeature) {
|
|
152
|
+
if (!rawFeature || typeof rawFeature !== 'object') return null;
|
|
153
|
+
const migrated = this.#runFeatureEntryMigrations(rawFeature);
|
|
154
|
+
this.#linkFeatureParams(migrated);
|
|
155
|
+
return migrated;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
#prepareFeatureList(list) {
|
|
159
|
+
if (!Array.isArray(list)) return [];
|
|
160
|
+
const prepared = [];
|
|
161
|
+
for (const rawFeature of list) {
|
|
162
|
+
const entry = this.#prepareFeatureEntry(rawFeature);
|
|
163
|
+
if (entry) prepared.push(entry);
|
|
164
|
+
}
|
|
165
|
+
return prepared;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
#disposeMaterialResources(material) {
|
|
169
|
+
if (!material) return;
|
|
170
|
+
if (Array.isArray(material)) {
|
|
171
|
+
for (const mat of material) this.#disposeMaterialResources(mat);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
if (typeof material !== 'object') return;
|
|
175
|
+
if (typeof material.dispose === 'function') {
|
|
176
|
+
try { material.dispose(); } catch { }
|
|
177
|
+
}
|
|
178
|
+
try {
|
|
179
|
+
for (const value of Object.values(material)) {
|
|
180
|
+
if (value && typeof value === 'object' && value.isTexture && typeof value.dispose === 'function') {
|
|
181
|
+
try { value.dispose(); } catch { }
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
} catch { /* ignore texture disposal errors */ }
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
#disposeObjectResources(object) {
|
|
188
|
+
if (!object || typeof object !== 'object') return;
|
|
189
|
+
try {
|
|
190
|
+
const children = Array.isArray(object.children) ? object.children.slice() : [];
|
|
191
|
+
for (const child of children) this.#disposeObjectResources(child);
|
|
192
|
+
} catch { }
|
|
193
|
+
try {
|
|
194
|
+
const geom = object.geometry;
|
|
195
|
+
if (geom && typeof geom.dispose === 'function') geom.dispose();
|
|
196
|
+
} catch { }
|
|
197
|
+
this.#disposeMaterialResources(object.material);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
#disposeSceneObjects(filterFn = null) {
|
|
201
|
+
if (!this.scene || !Array.isArray(this.scene.children)) return;
|
|
202
|
+
const children = this.scene.children.slice();
|
|
203
|
+
for (const child of children) {
|
|
204
|
+
if (child?.userData?.preventRemove) continue;
|
|
205
|
+
let shouldDispose = true;
|
|
206
|
+
if (typeof filterFn === 'function') {
|
|
207
|
+
try { shouldDispose = !!filterFn(child); }
|
|
208
|
+
catch { shouldDispose = false; }
|
|
209
|
+
}
|
|
210
|
+
if (!shouldDispose) continue;
|
|
211
|
+
this.#disposeObjectResources(child);
|
|
212
|
+
try { this.scene.remove(child); } catch { }
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
static evaluateExpression(expressionsSource, equation) {
|
|
217
|
+
const exprSource = typeof expressionsSource === 'string' ? expressionsSource : '';
|
|
218
|
+
const fnBody = `${exprSource}; return ${equation} ;`;
|
|
219
|
+
try {
|
|
220
|
+
let result = Function(fnBody)();
|
|
221
|
+
if (typeof result === 'string') {
|
|
222
|
+
const num = Number(result);
|
|
223
|
+
if (!Number.isNaN(num)) {
|
|
224
|
+
return num;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return result;
|
|
228
|
+
} catch (err) {
|
|
229
|
+
try { console.warn('[PartHistory] evaluateExpression failed:', err?.message || err); } catch { }
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
evaluateExpression(equation) {
|
|
235
|
+
return PartHistory.evaluateExpression(this.expressions, equation);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
getObjectByName(name) {
|
|
241
|
+
// traverse the scene to find an object with the given name
|
|
242
|
+
return this.scene.getObjectByName(name);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Removed: getObjectsByName (unused)
|
|
246
|
+
|
|
247
|
+
async reset() {
|
|
248
|
+
this.features = [];
|
|
249
|
+
this.idCounter = 0;
|
|
250
|
+
this.pmiViewsManager.reset();
|
|
251
|
+
this.expressions = "//Examples:\nx = 10 + 6; \ny = x * 2;";
|
|
252
|
+
// Reset MetadataManager
|
|
253
|
+
this.metadataManager = new MetadataManager();
|
|
254
|
+
this.currentHistoryStepId = null;
|
|
255
|
+
|
|
256
|
+
this.#disposeSceneObjects();
|
|
257
|
+
// empty the scene without destroying it
|
|
258
|
+
await this.scene.clear();
|
|
259
|
+
if (this.callbacks.reset) {
|
|
260
|
+
await this.callbacks.reset();
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (this.assemblyConstraintHistory) {
|
|
264
|
+
this.assemblyConstraintHistory.clear();
|
|
265
|
+
this.assemblyConstraintHistory.setPartHistory(this);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (this.callbacks.afterReset) {
|
|
269
|
+
try { await this.callbacks.afterReset(); } catch { /* ignore */ }
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
this.resetHistoryUndo();
|
|
273
|
+
await this._commitHistorySnapshot({ force: true });
|
|
274
|
+
|
|
275
|
+
// sleep for a short duration to allow scene updates to complete
|
|
276
|
+
//await new Promise(resolve => setTimeout(resolve, 1000));
|
|
277
|
+
// console.log("PartHistory reset complete.");
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
async runHistory() {
|
|
281
|
+
const whatStepToStopAt = this.currentHistoryStepId;
|
|
282
|
+
|
|
283
|
+
this.#disposeSceneObjects((obj) => !obj?.isLight && !obj?.isCamera && !obj?.isTransformGizmo);
|
|
284
|
+
await this.scene.clear();
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
let skipAllFeatures = false;
|
|
289
|
+
const features = Array.isArray(this.features) ? this.features : [];
|
|
290
|
+
let previousFeatureTimestamp = features.length ? (features[0]?.timestamp ?? null) : null;
|
|
291
|
+
const nowMs = () => (typeof performance !== 'undefined' && performance?.now ? performance.now() : Date.now());
|
|
292
|
+
for (let i = 0; i < features.length; i++) {
|
|
293
|
+
const feature = features[i];
|
|
294
|
+
this.#linkFeatureParams(feature);
|
|
295
|
+
const featureId = resolveFeatureEntryId(feature);
|
|
296
|
+
if (skipAllFeatures) {
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const nextFeature = features[i + 1];
|
|
301
|
+
|
|
302
|
+
if (whatStepToStopAt && featureId === whatStepToStopAt) {
|
|
303
|
+
skipAllFeatures = true; // stop after this feature
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Do NOT mutate currentHistoryStepId while running.
|
|
307
|
+
// It is used by the UI to indicate which panel the user wants open
|
|
308
|
+
// (and to determine the stop-at step). Updating it here caused the
|
|
309
|
+
// HistoryWidget to constantly switch the open panel to whatever
|
|
310
|
+
// feature happened to be executing, which made it impossible to
|
|
311
|
+
// expand items after PNG imports and similar long-running steps.
|
|
312
|
+
|
|
313
|
+
if (this.callbacks.run) {
|
|
314
|
+
await this.callbacks.run(featureId);
|
|
315
|
+
}
|
|
316
|
+
const FeatureClass = this.featureRegistry.getSafe(feature.type);
|
|
317
|
+
if (!FeatureClass) {
|
|
318
|
+
// Record an error on the feature but do not abort the whole run.
|
|
319
|
+
const t1 = nowMs();
|
|
320
|
+
const msg = `Feature type \"${feature.type}\" is not installed`;
|
|
321
|
+
try { feature.lastRun = { ok: false, startedAt: t1, endedAt: t1, durationMs: 0, error: { name: 'MissingFeature', message: msg, stack: null } }; } catch { }
|
|
322
|
+
// Skip visualization/add/remove steps for this feature
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
const instance = new FeatureClass(this);
|
|
326
|
+
|
|
327
|
+
//await Object.assign(instance.inputParams, feature.inputParams);
|
|
328
|
+
await Object.assign(instance.persistentData, feature.persistentData);
|
|
329
|
+
|
|
330
|
+
// Remove any existing scene children owned by this feature (rerun case)
|
|
331
|
+
const toRemoveOwned = this.scene.children.slice().filter(ch => ch?.owningFeatureID === featureId);
|
|
332
|
+
if (toRemoveOwned.length) {
|
|
333
|
+
for (const ch of toRemoveOwned) this.scene.remove(ch);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// if the previous feature had a timestamp later than this feature, we mark this feature as dirty to ensure it gets re-run
|
|
337
|
+
if (previousFeatureTimestamp != null && Number.isFinite(feature.timestamp) && previousFeatureTimestamp > feature.timestamp) {
|
|
338
|
+
feature.dirty = true;
|
|
339
|
+
}
|
|
340
|
+
// if the inputParams have changed since last run, mark dirty
|
|
341
|
+
if (JSON.stringify(feature.inputParams) !== feature.lastRunInputParams) feature.dirty = true;
|
|
342
|
+
|
|
343
|
+
instance.inputParams = await this.sanitizeInputParams(FeatureClass.inputParamsSchema, feature.inputParams);
|
|
344
|
+
// check the timestamps of any objects referenced by reference_selection inputs; if any are newer than the feature timestamp, mark dirty
|
|
345
|
+
for (const key in FeatureClass.inputParamsSchema) {
|
|
346
|
+
if (Object.prototype.hasOwnProperty.call(FeatureClass.inputParamsSchema, key)) {
|
|
347
|
+
const paramDef = FeatureClass.inputParamsSchema[key];
|
|
348
|
+
if (paramDef.type === 'reference_selection') {
|
|
349
|
+
const selected = Array.isArray(instance.inputParams[key]) ? instance.inputParams[key] : [];
|
|
350
|
+
for (const obj of selected) {
|
|
351
|
+
if (obj && typeof obj === 'object') {
|
|
352
|
+
const objTime = Number(obj.timestamp);
|
|
353
|
+
if (Number.isFinite(objTime) && (!Number.isFinite(feature.timestamp) || objTime > feature.timestamp)) {
|
|
354
|
+
feature.dirty = true;
|
|
355
|
+
break;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// compare any numeric inputParams as evaluated by the sanitizeInputParams method and catch changes due to expressions
|
|
364
|
+
// the instance.inputParams have already been sanitized
|
|
365
|
+
|
|
366
|
+
for (const key in FeatureClass.inputParamsSchema) {
|
|
367
|
+
if (Object.prototype.hasOwnProperty.call(FeatureClass.inputParamsSchema, key)) {
|
|
368
|
+
const paramDef = FeatureClass.inputParamsSchema[key];
|
|
369
|
+
if (feature.previouseExpressions === undefined) feature.previouseExpressions = {};
|
|
370
|
+
if (paramDef.type === 'number') {
|
|
371
|
+
try {
|
|
372
|
+
|
|
373
|
+
if (feature.previouseExpressions[key] === undefined) feature.dirty = true;
|
|
374
|
+
else if (Number(feature.previouseExpressions[key]) !== Number(instance.inputParams[key])) feature.dirty = true;
|
|
375
|
+
feature.previouseExpressions[key] = instance.inputParams[key];
|
|
376
|
+
} catch {
|
|
377
|
+
feature.dirty = true;
|
|
378
|
+
feature.previouseExpressions[key] = instance.inputParams[key];
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
// manually run the sketch feature and then test if the geometry has changed
|
|
386
|
+
// if so, mark dirty
|
|
387
|
+
const featureName = FeatureClass?.longName || FeatureClass?.shortName || FeatureClass?.name || '';
|
|
388
|
+
if (featureName === 'Sketch') {
|
|
389
|
+
try {
|
|
390
|
+
instance.run(this);
|
|
391
|
+
const sketchChanged = await instance.hasSketchChanged(feature);
|
|
392
|
+
if (sketchChanged) feature.dirty = true;
|
|
393
|
+
} catch (error) {
|
|
394
|
+
console.warn('[PartHistory] Sketch change detection failed:', error);
|
|
395
|
+
feature.dirty = true;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
if (feature.dirty) {
|
|
400
|
+
if (debug) console.log("feature dirty");
|
|
401
|
+
if (debug) console.log(`Running feature ${i + 1}/${features.length} (${featureId}) of type ${feature.type}...`, feature);
|
|
402
|
+
// if this one is dirty, next one should be too (conservative)
|
|
403
|
+
if (nextFeature) nextFeature.dirty = true;
|
|
404
|
+
|
|
405
|
+
// Record the current input params as lastRunInputParams
|
|
406
|
+
feature.lastRunInputParams = JSON.stringify(feature.inputParams);
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
const t0 = nowMs();
|
|
410
|
+
|
|
411
|
+
try {
|
|
412
|
+
instance.resultArtifacts = await instance.run(this);
|
|
413
|
+
feature.effects = {
|
|
414
|
+
added: instance.resultArtifacts.added || [],
|
|
415
|
+
removed: instance.resultArtifacts.removed || []
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
feature.timestamp = Date.now();
|
|
420
|
+
previousFeatureTimestamp = feature.timestamp;
|
|
421
|
+
|
|
422
|
+
const t1 = nowMs();
|
|
423
|
+
const dur = Math.max(0, Math.round(t1 - t0));
|
|
424
|
+
|
|
425
|
+
feature.lastRun = { ok: true, startedAt: t0, endedAt: t1, durationMs: dur, error: null };
|
|
426
|
+
feature.dirty = false;
|
|
427
|
+
} catch (e) {
|
|
428
|
+
const t1 = nowMs();
|
|
429
|
+
const dur = Math.max(0, Math.round(t1 - t0));
|
|
430
|
+
feature.lastRun = { ok: false, startedAt: t0, endedAt: t1, durationMs: dur, error: { message: e?.message || String(e), name: e?.name || 'Error', stack: e?.stack || null } };
|
|
431
|
+
feature.timestamp = Date.now();
|
|
432
|
+
|
|
433
|
+
previousFeatureTimestamp = feature.timestamp;
|
|
434
|
+
instance.errorString = `Error occurred while running feature ${featureId}: ${e.message}`;
|
|
435
|
+
console.error(e);
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
await this.applyFeatureEffects(feature.effects, featureId, feature);
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
feature.persistentData = instance.persistentData;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
try {
|
|
447
|
+
await this.runAssemblyConstraints();
|
|
448
|
+
} catch (error) {
|
|
449
|
+
console.warn('[PartHistory] Assembly constraints run failed:', error);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// Do not clear currentHistoryStepId here. Keeping it preserves the UX of
|
|
453
|
+
// "stop at the currently expanded feature" across subsequent runs. The
|
|
454
|
+
// UI will explicitly clear it when no section is expanded.
|
|
455
|
+
|
|
456
|
+
if (this.callbacks.afterRunHistory) {
|
|
457
|
+
try { await this.callbacks.afterRunHistory(); } catch { /* ignore */ }
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
return this;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
async _coerceRunEffects(result, featureType, featureID) {
|
|
465
|
+
if (result == null) return { added: [], removed: [] };
|
|
466
|
+
if (Array.isArray(result)) {
|
|
467
|
+
throw new Error(`[PartHistory] Feature "${featureType}" returned an array; expected { added, removed } payload (featureID=${featureID}).`);
|
|
468
|
+
}
|
|
469
|
+
const added = Array.isArray(result.added) ? result.added.filter(Boolean) : [];
|
|
470
|
+
const removed = Array.isArray(result.removed) ? result.removed.filter(Boolean) : [];
|
|
471
|
+
|
|
472
|
+
// set the owningFeatureID for each item added by this feature
|
|
473
|
+
for (const artifact of added) {
|
|
474
|
+
artifact.owningFeatureID = featureID;
|
|
475
|
+
// Ensure any stale manifold/cache is dropped before visualizing
|
|
476
|
+
try { await artifact.free(); } catch { }
|
|
477
|
+
try { await artifact.visualize(); } catch { }
|
|
478
|
+
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
|
|
483
|
+
return { added, removed };
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
async applyFeatureEffects(effects, featureID, feature) {
|
|
488
|
+
if (!effects || typeof effects !== 'object') return;
|
|
489
|
+
const added = Array.isArray(effects.added) ? effects.added : [];
|
|
490
|
+
const removed = Array.isArray(effects.removed) ? effects.removed : [];
|
|
491
|
+
|
|
492
|
+
for (const r of removed) {
|
|
493
|
+
await this._safeRemove(r);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
for (const a of added) {
|
|
497
|
+
if (a && typeof a === 'object') {
|
|
498
|
+
if (a === this.scene) continue;
|
|
499
|
+
// Free first to force rebuild from latest arrays, then visualize
|
|
500
|
+
try { await a.free(); } catch { }
|
|
501
|
+
try { await a.visualize(); } catch { }
|
|
502
|
+
await this.scene.add(a);
|
|
503
|
+
// make sure the flag for removal is cleared
|
|
504
|
+
try { a.__removeFlag = false; } catch { }
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
|
|
509
|
+
const applyTimeStampToChildrenRecursively = (obj, timestamp) => {
|
|
510
|
+
if (!obj || typeof obj !== 'object') return;
|
|
511
|
+
try { obj.timestamp = timestamp || Date.now(); } catch { }
|
|
512
|
+
const children = Array.isArray(obj.children) ? obj.children : [];
|
|
513
|
+
for (const child of children) {
|
|
514
|
+
applyTimeStampToChildrenRecursively(child, timestamp);
|
|
515
|
+
}
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
// attach the timestamp from the feature to the object for traceability
|
|
519
|
+
try {
|
|
520
|
+
a.timestamp = feature.timestamp;
|
|
521
|
+
applyTimeStampToChildrenRecursively(a, feature.timestamp);
|
|
522
|
+
} catch { }
|
|
523
|
+
|
|
524
|
+
this._attachSelectionHandlers(a);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// apply the featureID to all added/removed items for traceability
|
|
529
|
+
try { for (const obj of added) { if (obj) obj.owningFeatureID = featureID; } } catch { }
|
|
530
|
+
try { for (const obj of removed) { if (obj) obj.owningFeatureID = featureID; } } catch { }
|
|
531
|
+
|
|
532
|
+
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// Removed unused signature/canonicalization helpers
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
|
|
539
|
+
_attachSelectionHandlers(obj) {
|
|
540
|
+
if (!obj || typeof obj !== 'object') return;
|
|
541
|
+
obj.onClick = () => {
|
|
542
|
+
try {
|
|
543
|
+
if (obj.type === SelectionFilter.SOLID && obj.parent && obj.parent.type === SelectionFilter.COMPONENT) {
|
|
544
|
+
const handledByParent = SelectionFilter.toggleSelection(obj.parent);
|
|
545
|
+
if (!handledByParent) SelectionFilter.toggleSelection(obj);
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
SelectionFilter.toggleSelection(obj);
|
|
549
|
+
} catch (error) {
|
|
550
|
+
try { console.warn('[PartHistory] toggleSelection failed:', error); }
|
|
551
|
+
catch (_) { /* no-op */ }
|
|
552
|
+
}
|
|
553
|
+
};
|
|
554
|
+
const children = Array.isArray(obj.children) ? obj.children : [];
|
|
555
|
+
for (const child of children) {
|
|
556
|
+
this._attachSelectionHandlers(child);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
_safeRemove(obj) {
|
|
561
|
+
if (!obj) return;
|
|
562
|
+
try {
|
|
563
|
+
if (obj.parent) {
|
|
564
|
+
const rm = obj.parent.remove;
|
|
565
|
+
if (typeof rm === 'function') obj.parent.remove(obj);
|
|
566
|
+
else if (rm !== undefined && THREE?.Object3D?.prototype?.remove) THREE.Object3D.prototype.remove.call(obj.parent, obj);
|
|
567
|
+
else this.scene.remove(obj);
|
|
568
|
+
} else {
|
|
569
|
+
const rm = this.scene.remove;
|
|
570
|
+
if (typeof rm === 'function') this.scene.remove(obj);
|
|
571
|
+
else if (rm !== undefined && THREE?.Object3D?.prototype?.remove) THREE.Object3D.prototype.remove.call(this.scene, obj);
|
|
572
|
+
}
|
|
573
|
+
} catch { }
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// Removed unused _safeAdd and _effectsAppearApplied
|
|
577
|
+
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
|
|
584
|
+
// methods to store and retrieve feature history to JSON strings
|
|
585
|
+
// We will store the features, idCounter, expressions, and optionally PMI views
|
|
586
|
+
async toJSON() {
|
|
587
|
+
try {
|
|
588
|
+
this.syncAssemblyComponentTransforms?.();
|
|
589
|
+
} catch (error) {
|
|
590
|
+
console.warn('[PartHistory] Failed to sync assembly component transforms before export:', error);
|
|
591
|
+
}
|
|
592
|
+
const constraintsSnapshot = this.assemblyConstraintHistory?.snapshot?.() || { idCounter: 0, constraints: [] };
|
|
593
|
+
|
|
594
|
+
|
|
595
|
+
// build features object keeping only the inputParams and persistentData
|
|
596
|
+
const features = this.features.map(f => ({
|
|
597
|
+
type: f.type,
|
|
598
|
+
inputParams: f.inputParams,
|
|
599
|
+
persistentData: this._sanitizePersistentDataForExport(f.persistentData),
|
|
600
|
+
timestamp: f.timestamp || null,
|
|
601
|
+
}));
|
|
602
|
+
const pmiViews = this.pmiViewsManager.toSerializable();
|
|
603
|
+
|
|
604
|
+
return JSON.stringify({
|
|
605
|
+
features,
|
|
606
|
+
idCounter: this.idCounter,
|
|
607
|
+
expressions: this.expressions,
|
|
608
|
+
pmiViews,
|
|
609
|
+
metadata: this.metadataManager.metadata,
|
|
610
|
+
assemblyConstraints: constraintsSnapshot.constraints,
|
|
611
|
+
assemblyConstraintIdCounter: constraintsSnapshot.idCounter,
|
|
612
|
+
}, null, 2);
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
_sanitizePersistentDataForExport(raw) {
|
|
616
|
+
if (!raw || typeof raw !== 'object') return raw;
|
|
617
|
+
if (!Object.prototype.hasOwnProperty.call(raw, 'lastProfileDiagnostics')) return raw;
|
|
618
|
+
const clone = deepClone(raw);
|
|
619
|
+
delete clone.lastProfileDiagnostics;
|
|
620
|
+
return clone;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
async fromJSON(jsonString, options = {}) {
|
|
624
|
+
const importData = JSON.parse(jsonString);
|
|
625
|
+
const rawFeatures = Array.isArray(importData.features) ? importData.features : [];
|
|
626
|
+
this.features = this.#prepareFeatureList(rawFeatures);
|
|
627
|
+
this.idCounter = importData.idCounter;
|
|
628
|
+
this.expressions = importData.expressions || "";
|
|
629
|
+
this.pmiViewsManager.setViews(importData.pmiViews || []);
|
|
630
|
+
this.metadataManager.metadata = importData.metadata || {};
|
|
631
|
+
|
|
632
|
+
if (this.assemblyConstraintHistory) {
|
|
633
|
+
this.assemblyConstraintHistory.setPartHistory(this);
|
|
634
|
+
const constraintsList = Array.isArray(importData.assemblyConstraints)
|
|
635
|
+
? importData.assemblyConstraints
|
|
636
|
+
: [];
|
|
637
|
+
const constraintCounter = Number(importData.assemblyConstraintIdCounter) || 0;
|
|
638
|
+
|
|
639
|
+
if (constraintsList.length > 0) {
|
|
640
|
+
await this.assemblyConstraintHistory.replaceAll(constraintsList, constraintCounter);
|
|
641
|
+
} else {
|
|
642
|
+
this.assemblyConstraintHistory.clear();
|
|
643
|
+
this.assemblyConstraintHistory.idCounter = constraintCounter;
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
const skipUndoReset = !!(options && options.skipUndoReset);
|
|
648
|
+
if (!skipUndoReset) {
|
|
649
|
+
this.resetHistoryUndo();
|
|
650
|
+
await this._commitHistorySnapshot({ force: true });
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
async generateId(prefix) {
|
|
655
|
+
this.idCounter += 1;
|
|
656
|
+
return `${prefix}${this.idCounter}`;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
resetHistoryUndo() {
|
|
660
|
+
const state = this._historyUndo;
|
|
661
|
+
if (!state) return;
|
|
662
|
+
if (state.pendingTimer) {
|
|
663
|
+
try { clearTimeout(state.pendingTimer); } catch { }
|
|
664
|
+
}
|
|
665
|
+
state.pendingTimer = null;
|
|
666
|
+
state.undoStack = [];
|
|
667
|
+
state.redoStack = [];
|
|
668
|
+
state.lastSignature = null;
|
|
669
|
+
state.captureInFlight = false;
|
|
670
|
+
state.pendingRequest = false;
|
|
671
|
+
state.isApplying = false;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
queueHistorySnapshot(options = {}) {
|
|
675
|
+
const state = this._historyUndo;
|
|
676
|
+
if (!state || state.isApplying) return;
|
|
677
|
+
const debounceMs = (typeof options.debounceMs === 'number')
|
|
678
|
+
? options.debounceMs
|
|
679
|
+
: state.debounceMs;
|
|
680
|
+
if (state.pendingTimer) {
|
|
681
|
+
try { clearTimeout(state.pendingTimer); } catch { }
|
|
682
|
+
state.pendingTimer = null;
|
|
683
|
+
}
|
|
684
|
+
if (debounceMs <= 0) {
|
|
685
|
+
void this._commitHistorySnapshot({ force: !!options.force });
|
|
686
|
+
return;
|
|
687
|
+
}
|
|
688
|
+
state.pendingTimer = setTimeout(() => {
|
|
689
|
+
state.pendingTimer = null;
|
|
690
|
+
void this._commitHistorySnapshot({ force: !!options.force });
|
|
691
|
+
}, debounceMs);
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
async flushHistorySnapshot(options = {}) {
|
|
695
|
+
const state = this._historyUndo;
|
|
696
|
+
if (!state) return;
|
|
697
|
+
if (state.pendingTimer) {
|
|
698
|
+
try { clearTimeout(state.pendingTimer); } catch { }
|
|
699
|
+
state.pendingTimer = null;
|
|
700
|
+
}
|
|
701
|
+
await this._commitHistorySnapshot({ force: !!options.force });
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
canUndoFeatureHistory() {
|
|
705
|
+
const state = this._historyUndo;
|
|
706
|
+
return !!(state && Array.isArray(state.undoStack) && state.undoStack.length > 1);
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
canRedoFeatureHistory() {
|
|
710
|
+
const state = this._historyUndo;
|
|
711
|
+
return !!(state && Array.isArray(state.redoStack) && state.redoStack.length > 0);
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
async undoFeatureHistory() {
|
|
715
|
+
const state = this._historyUndo;
|
|
716
|
+
if (!state || state.undoStack.length <= 1) return false;
|
|
717
|
+
await this.flushHistorySnapshot();
|
|
718
|
+
if (state.undoStack.length <= 1) return false;
|
|
719
|
+
const current = state.undoStack.pop();
|
|
720
|
+
if (current) state.redoStack.push(current);
|
|
721
|
+
const prev = state.undoStack[state.undoStack.length - 1];
|
|
722
|
+
if (!prev) return false;
|
|
723
|
+
await this._applyHistorySnapshot(prev);
|
|
724
|
+
return true;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
async redoFeatureHistory() {
|
|
728
|
+
const state = this._historyUndo;
|
|
729
|
+
if (!state || state.redoStack.length === 0) return false;
|
|
730
|
+
const next = state.redoStack.pop();
|
|
731
|
+
if (!next) return false;
|
|
732
|
+
state.undoStack.push(next);
|
|
733
|
+
await this._applyHistorySnapshot(next);
|
|
734
|
+
return true;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
async _commitHistorySnapshot({ force = false } = {}) {
|
|
738
|
+
const state = this._historyUndo;
|
|
739
|
+
if (!state || state.isApplying) return;
|
|
740
|
+
if (state.captureInFlight) {
|
|
741
|
+
state.pendingRequest = true;
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
state.captureInFlight = true;
|
|
745
|
+
try {
|
|
746
|
+
const json = await this.toJSON();
|
|
747
|
+
if (!json) return;
|
|
748
|
+
if (!force && state.lastSignature === json) return;
|
|
749
|
+
const snapshot = {
|
|
750
|
+
json,
|
|
751
|
+
currentHistoryStepId: this.currentHistoryStepId != null ? String(this.currentHistoryStepId) : null,
|
|
752
|
+
};
|
|
753
|
+
state.undoStack.push(snapshot);
|
|
754
|
+
if (state.undoStack.length > state.max) state.undoStack.shift();
|
|
755
|
+
state.redoStack.length = 0;
|
|
756
|
+
state.lastSignature = json;
|
|
757
|
+
} catch (error) {
|
|
758
|
+
console.warn('[PartHistory] Failed to capture history snapshot:', error);
|
|
759
|
+
} finally {
|
|
760
|
+
state.captureInFlight = false;
|
|
761
|
+
if (state.pendingRequest) {
|
|
762
|
+
state.pendingRequest = false;
|
|
763
|
+
await this._commitHistorySnapshot({ force: false });
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
async _applyHistorySnapshot(snapshot) {
|
|
769
|
+
const state = this._historyUndo;
|
|
770
|
+
if (!state || !snapshot || !snapshot.json) return;
|
|
771
|
+
if (state.pendingTimer) {
|
|
772
|
+
try { clearTimeout(state.pendingTimer); } catch { }
|
|
773
|
+
state.pendingTimer = null;
|
|
774
|
+
}
|
|
775
|
+
state.isApplying = true;
|
|
776
|
+
try {
|
|
777
|
+
await this.fromJSON(snapshot.json, { skipUndoReset: true });
|
|
778
|
+
this.currentHistoryStepId = snapshot.currentHistoryStepId != null ? String(snapshot.currentHistoryStepId) : null;
|
|
779
|
+
await this.runHistory();
|
|
780
|
+
state.lastSignature = snapshot.json;
|
|
781
|
+
} catch (error) {
|
|
782
|
+
console.warn('[PartHistory] Failed to apply history snapshot:', error);
|
|
783
|
+
} finally {
|
|
784
|
+
state.isApplying = false;
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
async runAssemblyConstraints() {
|
|
789
|
+
if (!this.assemblyConstraintHistory) return [];
|
|
790
|
+
this.assemblyConstraintHistory.setPartHistory(this);
|
|
791
|
+
return await this.assemblyConstraintHistory.runAll(this);
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
hasAssemblyComponents() {
|
|
795
|
+
const features = Array.isArray(this.features) ? this.features : [];
|
|
796
|
+
if (!features.length) return false;
|
|
797
|
+
const normalize = (value) => {
|
|
798
|
+
if (value === 0) return '0';
|
|
799
|
+
if (value == null) return '';
|
|
800
|
+
return String(value).trim().toUpperCase();
|
|
801
|
+
};
|
|
802
|
+
const targets = new Set([
|
|
803
|
+
normalize(AssemblyComponentFeature?.shortName),
|
|
804
|
+
normalize(AssemblyComponentFeature?.longName),
|
|
805
|
+
normalize(AssemblyComponentFeature?.name),
|
|
806
|
+
]);
|
|
807
|
+
targets.delete('');
|
|
808
|
+
|
|
809
|
+
for (const feature of features) {
|
|
810
|
+
if (!feature) continue;
|
|
811
|
+
const rawType = feature?.type ?? feature?.inputParams?.type ?? null;
|
|
812
|
+
const typeKey = normalize(rawType);
|
|
813
|
+
if (typeKey && targets.has(typeKey)) return true;
|
|
814
|
+
|
|
815
|
+
try {
|
|
816
|
+
const FeatureClass = this.featureRegistry?.getSafe?.(rawType) || null;
|
|
817
|
+
if (FeatureClass === AssemblyComponentFeature) return true;
|
|
818
|
+
const resolvedKey = normalize(FeatureClass?.longName || FeatureClass?.shortName || FeatureClass?.name);
|
|
819
|
+
if (resolvedKey && targets.has(resolvedKey)) return true;
|
|
820
|
+
} catch { /* ignore unknown feature types */ }
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
return false;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
syncAssemblyComponentTransforms() {
|
|
827
|
+
if (!this.scene || !Array.isArray(this.features)) return;
|
|
828
|
+
|
|
829
|
+
const featureById = new Map();
|
|
830
|
+
for (const feature of this.features) {
|
|
831
|
+
if (!feature || !feature.inputParams) continue;
|
|
832
|
+
const id = resolveFeatureEntryId(feature);
|
|
833
|
+
if (id == null) continue;
|
|
834
|
+
featureById.set(String(id), feature);
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
const tempEuler = new THREE.Euler();
|
|
838
|
+
|
|
839
|
+
const syncOne = (component) => {
|
|
840
|
+
if (!component || !component.isAssemblyComponent) return;
|
|
841
|
+
const featureIdRaw = component.owningFeatureID;
|
|
842
|
+
if (!featureIdRaw && featureIdRaw !== 0) return;
|
|
843
|
+
const feature = featureById.get(String(featureIdRaw));
|
|
844
|
+
if (!feature) return;
|
|
845
|
+
|
|
846
|
+
component.updateMatrixWorld?.(true);
|
|
847
|
+
|
|
848
|
+
const pos = component.position || new THREE.Vector3();
|
|
849
|
+
const quat = component.quaternion || new THREE.Quaternion();
|
|
850
|
+
const scl = component.scale || new THREE.Vector3(1, 1, 1);
|
|
851
|
+
|
|
852
|
+
tempEuler.setFromQuaternion(quat, 'XYZ');
|
|
853
|
+
|
|
854
|
+
const transform = {
|
|
855
|
+
position: [pos.x, pos.y, pos.z],
|
|
856
|
+
rotationEuler: [
|
|
857
|
+
THREE.MathUtils.radToDeg(tempEuler.x),
|
|
858
|
+
THREE.MathUtils.radToDeg(tempEuler.y),
|
|
859
|
+
THREE.MathUtils.radToDeg(tempEuler.z),
|
|
860
|
+
],
|
|
861
|
+
scale: [scl.x, scl.y, scl.z],
|
|
862
|
+
};
|
|
863
|
+
|
|
864
|
+
feature.inputParams = feature.inputParams || {};
|
|
865
|
+
feature.inputParams.transform = transform;
|
|
866
|
+
};
|
|
867
|
+
|
|
868
|
+
if (typeof this.scene.traverse === 'function') {
|
|
869
|
+
this.scene.traverse((obj) => { syncOne(obj); });
|
|
870
|
+
} else {
|
|
871
|
+
const children = Array.isArray(this.scene.children) ? this.scene.children : [];
|
|
872
|
+
for (const child of children) syncOne(child);
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
_collectAssemblyComponentUpdates() {
|
|
877
|
+
if (!Array.isArray(this.features) || this.features.length === 0) {
|
|
878
|
+
return [];
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
const updates = [];
|
|
882
|
+
const targetName = String(AssemblyComponentFeature?.longName || AssemblyComponentFeature?.name || '').trim().toUpperCase();
|
|
883
|
+
|
|
884
|
+
for (const feature of this.features) {
|
|
885
|
+
if (!feature || !feature.type) continue;
|
|
886
|
+
|
|
887
|
+
let FeatureClass = null;
|
|
888
|
+
try {
|
|
889
|
+
FeatureClass = this.featureRegistry?.getSafe?.(feature.type) || null;
|
|
890
|
+
} catch {
|
|
891
|
+
FeatureClass = null;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
const isAssemblyComponent = FeatureClass === AssemblyComponentFeature
|
|
895
|
+
|| (FeatureClass && String(FeatureClass?.longName || FeatureClass?.name || '').trim().toUpperCase() === targetName);
|
|
896
|
+
if (!isAssemblyComponent) continue;
|
|
897
|
+
|
|
898
|
+
const componentName = feature?.inputParams?.componentName;
|
|
899
|
+
if (!componentName) continue;
|
|
900
|
+
|
|
901
|
+
const record = getComponentRecord(componentName);
|
|
902
|
+
if (!record || !record.data3mf) continue;
|
|
903
|
+
|
|
904
|
+
const prevData = feature?.persistentData?.componentData?.data3mf || null;
|
|
905
|
+
const prevSavedAt = feature?.persistentData?.componentData?.savedAt || null;
|
|
906
|
+
const nextSavedAt = record.savedAt || null;
|
|
907
|
+
|
|
908
|
+
const prevTime = prevSavedAt ? Date.parse(prevSavedAt) : NaN;
|
|
909
|
+
const nextTime = nextSavedAt ? Date.parse(nextSavedAt) : NaN;
|
|
910
|
+
|
|
911
|
+
const hasNewerTimestamp = Number.isFinite(nextTime) && (!Number.isFinite(prevTime) || nextTime > prevTime);
|
|
912
|
+
const hasDifferentData = record.data3mf !== prevData;
|
|
913
|
+
|
|
914
|
+
if (!hasNewerTimestamp && !hasDifferentData) continue;
|
|
915
|
+
|
|
916
|
+
updates.push({
|
|
917
|
+
feature,
|
|
918
|
+
componentName,
|
|
919
|
+
record,
|
|
920
|
+
nextSavedAt,
|
|
921
|
+
});
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
return updates;
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
getOutdatedAssemblyComponentCount() {
|
|
928
|
+
return this._collectAssemblyComponentUpdates().length;
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
async updateAssemblyComponents(options = {}) {
|
|
932
|
+
const { rerun = true } = options || {};
|
|
933
|
+
const updates = this._collectAssemblyComponentUpdates();
|
|
934
|
+
const updatedCount = updates.length;
|
|
935
|
+
|
|
936
|
+
if (updatedCount === 0) {
|
|
937
|
+
return { updatedCount: 0, reran: false };
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
for (const { feature, componentName, record, nextSavedAt } of updates) {
|
|
941
|
+
let featureInfo = feature?.persistentData?.componentData?.featureInfo || null;
|
|
942
|
+
try {
|
|
943
|
+
const tempFeature = new AssemblyComponentFeature();
|
|
944
|
+
if (typeof tempFeature._extractFeatureInfo === 'function') {
|
|
945
|
+
const bytes = base64ToUint8Array(record.data3mf);
|
|
946
|
+
if (bytes && bytes.length) {
|
|
947
|
+
const info = await tempFeature._extractFeatureInfo(bytes);
|
|
948
|
+
if (info) featureInfo = info;
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
} catch (error) {
|
|
952
|
+
console.warn('[PartHistory] Failed to extract feature info while updating component:', error);
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
feature.persistentData = feature.persistentData || {};
|
|
956
|
+
feature.persistentData.componentData = {
|
|
957
|
+
name: record.name || componentName,
|
|
958
|
+
savedAt: nextSavedAt,
|
|
959
|
+
data3mf: record.data3mf,
|
|
960
|
+
featureInfo: featureInfo || null,
|
|
961
|
+
};
|
|
962
|
+
|
|
963
|
+
feature.lastRunInputParams = null;
|
|
964
|
+
feature.timestamp = Date.now();
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
let reran = false;
|
|
968
|
+
if (rerun && typeof this.runHistory === 'function') {
|
|
969
|
+
await this.runHistory();
|
|
970
|
+
reran = true;
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
return { updatedCount, reran };
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
async newFeature(featureType) {
|
|
977
|
+
const FeatureClass = (this.featureRegistry && typeof this.featureRegistry.getSafe === 'function')
|
|
978
|
+
? (this.featureRegistry.getSafe(featureType) || this.featureRegistry.get(featureType))
|
|
979
|
+
: this.featureRegistry.get(featureType);
|
|
980
|
+
const feature = {
|
|
981
|
+
type: featureType,
|
|
982
|
+
inputParams: await extractDefaultValues(FeatureClass.inputParamsSchema),
|
|
983
|
+
persistentData: {}
|
|
984
|
+
};
|
|
985
|
+
feature.inputParams.id = await this.generateId(featureType);
|
|
986
|
+
this.#linkFeatureParams(feature);
|
|
987
|
+
// console.debug("New feature created:", feature.inputParams.id);
|
|
988
|
+
this.features.push(feature);
|
|
989
|
+
return feature;
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
// Removed unused reorderFeature
|
|
993
|
+
|
|
994
|
+
async removeFeature(featureID) {
|
|
995
|
+
if (featureID == null) return;
|
|
996
|
+
const target = String(featureID);
|
|
997
|
+
this.features = this.features.filter((f) => resolveFeatureEntryId(f) !== target);
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
|
|
1001
|
+
|
|
1002
|
+
async sanitizeInputParams(schema, inputParams) {
|
|
1003
|
+
|
|
1004
|
+
let sanitized = {};
|
|
1005
|
+
|
|
1006
|
+
for (const key in schema) {
|
|
1007
|
+
//console.log(`Sanitizing ${key}:`, inputParams[key]);
|
|
1008
|
+
if (inputParams[key] !== undefined) {
|
|
1009
|
+
// check if the schema type is number
|
|
1010
|
+
if (schema[key].type === "number") {
|
|
1011
|
+
// if it is a string use the eval() function to do some math and return it as a number
|
|
1012
|
+
sanitized[key] = PartHistory.evaluateExpression(this.expressions, inputParams[key]);
|
|
1013
|
+
} else if (schema[key].type === "reference_selection") {
|
|
1014
|
+
// Resolve references: accept objects directly or look up by name
|
|
1015
|
+
const val = inputParams[key];
|
|
1016
|
+
if (Array.isArray(val)) {
|
|
1017
|
+
const arr = [];
|
|
1018
|
+
for (const it of val) {
|
|
1019
|
+
if (!it) continue;
|
|
1020
|
+
if (typeof it === 'object') { arr.push(it); continue; }
|
|
1021
|
+
const obj = this.getObjectByName(String(it));
|
|
1022
|
+
if (obj) arr.push(obj);
|
|
1023
|
+
}
|
|
1024
|
+
sanitized[key] = arr;
|
|
1025
|
+
} else {
|
|
1026
|
+
if (!val) { sanitized[key] = []; }
|
|
1027
|
+
else if (typeof val === 'object') { sanitized[key] = [val]; }
|
|
1028
|
+
else {
|
|
1029
|
+
const obj = this.getObjectByName(String(val));
|
|
1030
|
+
sanitized[key] = obj ? [obj] : [];
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
} else if (schema[key].type === "boolean_operation") {
|
|
1035
|
+
// If it's a boolean operation, normalize op key and resolve targets to objects.
|
|
1036
|
+
// Also pass through optional biasDistance (numeric) and new sweep cap offset controls.
|
|
1037
|
+
const raw = inputParams[key] || {};
|
|
1038
|
+
const op = raw.operation;
|
|
1039
|
+
const items = Array.isArray(raw.targets) ? raw.targets : [];
|
|
1040
|
+
const targets = [];
|
|
1041
|
+
for (const it of items) {
|
|
1042
|
+
if (!it) continue;
|
|
1043
|
+
if (typeof it === 'object') { targets.push(it); continue; }
|
|
1044
|
+
const obj = this.getObjectByName(String(it));
|
|
1045
|
+
if (obj) targets.push(obj);
|
|
1046
|
+
}
|
|
1047
|
+
const bias = Number(raw.biasDistance);
|
|
1048
|
+
const offsetCapFlag = (raw.offsetCoplanarCap != null) ? String(raw.offsetCoplanarCap) : undefined;
|
|
1049
|
+
const offsetDistance = Number(raw.offsetDistance);
|
|
1050
|
+
const out = {
|
|
1051
|
+
operation: op ?? 'NONE',
|
|
1052
|
+
targets,
|
|
1053
|
+
biasDistance: Number.isFinite(bias) ? bias : 0.1,
|
|
1054
|
+
};
|
|
1055
|
+
if (offsetCapFlag !== undefined) out.offsetCoplanarCap = offsetCapFlag;
|
|
1056
|
+
if (Number.isFinite(offsetDistance)) out.offsetDistance = offsetDistance;
|
|
1057
|
+
sanitized[key] = out;
|
|
1058
|
+
} else if (schema[key].type === "transform") {
|
|
1059
|
+
// Evaluate each component; allow expressions in position/rotation/scale entries
|
|
1060
|
+
const raw = inputParams[key] || {};
|
|
1061
|
+
const evalOne = (v) => {
|
|
1062
|
+
if (typeof v === 'number' && Number.isFinite(v)) return v;
|
|
1063
|
+
if (typeof v === 'string') return PartHistory.evaluateExpression(this.expressions, v);
|
|
1064
|
+
const n = Number(v);
|
|
1065
|
+
return Number.isFinite(n) ? n : 0;
|
|
1066
|
+
};
|
|
1067
|
+
const pos = Array.isArray(raw.position) ? raw.position.map(evalOne) : [0, 0, 0];
|
|
1068
|
+
const rot = Array.isArray(raw.rotationEuler) ? raw.rotationEuler.map(evalOne) : [0, 0, 0];
|
|
1069
|
+
const scl = Array.isArray(raw.scale) ? raw.scale.map(evalOne) : [1, 1, 1];
|
|
1070
|
+
sanitized[key] = { position: pos, rotationEuler: rot, scale: scl };
|
|
1071
|
+
} else if (schema[key].type === "vec3") {
|
|
1072
|
+
// Evaluate vec3 entries; accept array [x,y,z] or object {x,y,z}
|
|
1073
|
+
const raw = inputParams[key];
|
|
1074
|
+
const evalOne = (v) => {
|
|
1075
|
+
if (typeof v === 'number' && Number.isFinite(v)) return v;
|
|
1076
|
+
if (typeof v === 'string') return PartHistory.evaluateExpression(this.expressions, v);
|
|
1077
|
+
const n = Number(v);
|
|
1078
|
+
return Number.isFinite(n) ? n : 0;
|
|
1079
|
+
};
|
|
1080
|
+
if (Array.isArray(raw)) {
|
|
1081
|
+
sanitized[key] = [evalOne(raw[0]), evalOne(raw[1]), evalOne(raw[2])];
|
|
1082
|
+
} else if (raw && typeof raw === 'object') {
|
|
1083
|
+
sanitized[key] = [evalOne(raw.x), evalOne(raw.y), evalOne(raw.z)];
|
|
1084
|
+
} else {
|
|
1085
|
+
sanitized[key] = [0, 0, 0];
|
|
1086
|
+
}
|
|
1087
|
+
} else if (schema[key].type === "boolean") {
|
|
1088
|
+
sanitized[key] = Boolean(Object.prototype.hasOwnProperty.call(inputParams, key) ? inputParams[key] : schema[key].default_value);
|
|
1089
|
+
} else {
|
|
1090
|
+
sanitized[key] = inputParams[key];
|
|
1091
|
+
}
|
|
1092
|
+
} else {
|
|
1093
|
+
// Clone structured defaults to avoid shared references across features
|
|
1094
|
+
sanitized[key] = deepClone(schema[key].default_value);
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
const params = sanitized;
|
|
1099
|
+
if (params && typeof params === 'object') {
|
|
1100
|
+
if (params.id == null && params.featureID != null) {
|
|
1101
|
+
params.id = params.featureID;
|
|
1102
|
+
}
|
|
1103
|
+
if (params.id != null && params.id !== '') {
|
|
1104
|
+
params.id = String(params.id);
|
|
1105
|
+
}
|
|
1106
|
+
if (!Object.getOwnPropertyDescriptor(params, 'featureID')) {
|
|
1107
|
+
Object.defineProperty(params, 'featureID', {
|
|
1108
|
+
configurable: true,
|
|
1109
|
+
enumerable: false,
|
|
1110
|
+
get: () => params.id,
|
|
1111
|
+
set: (value) => {
|
|
1112
|
+
params.id = value == null ? value : String(value);
|
|
1113
|
+
},
|
|
1114
|
+
});
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
return sanitized;
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
// Helper to extract default values using shared deepClone utility
|
|
1123
|
+
export function extractDefaultValues(schema) {
|
|
1124
|
+
const result = {};
|
|
1125
|
+
for (const key in schema) {
|
|
1126
|
+
if (Object.prototype.hasOwnProperty.call(schema, key)) {
|
|
1127
|
+
const def = schema[key] ? schema[key].default_value : undefined;
|
|
1128
|
+
result[key] = deepClone(def);
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
return result;
|
|
1132
|
+
}
|