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,1414 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { SelectionFilter } from './SelectionFilter.js';
|
|
4
|
+
import * as THREE from 'three';
|
|
5
|
+
// Use hybrid translate+rotate gizmo used by the Viewer
|
|
6
|
+
import { CombinedTransformControls } from './controls/CombinedTransformControls.js';
|
|
7
|
+
import { getWidgetRenderer } from './featureDialogWidgets/index.js';
|
|
8
|
+
import { normalizeReferenceList, normalizeReferenceName } from './featureDialogWidgets/utils.js';
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
17
|
+
// SchemaForm: dark-mode, framework-free, ES module UI generator for schema-driven dialogs.
|
|
18
|
+
// - Renders inputs from a schema and keeps a provided `params` object in sync.
|
|
19
|
+
// - refreshFromParams() updates inputs when params are changed elsewhere.
|
|
20
|
+
// - Supports feature dialogs and annotation dialogs with shared widget implementations.
|
|
21
|
+
// - Special: type === "reference_selection" uses a scene-driven picker instead of a text box.
|
|
22
|
+
export class SchemaForm {
|
|
23
|
+
// Track a single globally-active reference selection input across all instances
|
|
24
|
+
static __activeRefInput = null;
|
|
25
|
+
static __setGlobalActiveRefInput(el) {
|
|
26
|
+
try {
|
|
27
|
+
// If another input was active, clear its visual + attribute
|
|
28
|
+
const prev = SchemaForm.__activeRefInput;
|
|
29
|
+
if (prev && prev !== el) {
|
|
30
|
+
try { prev.style.filter = 'none'; } catch (_) { }
|
|
31
|
+
try { prev.removeAttribute('active-reference-selection'); } catch (_) { }
|
|
32
|
+
}
|
|
33
|
+
} catch (_) { }
|
|
34
|
+
SchemaForm.__activeRefInput = el || null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Track a single globally-active transform controls session across all instances
|
|
38
|
+
static __activeXform = {
|
|
39
|
+
owner: null,
|
|
40
|
+
key: null,
|
|
41
|
+
inputEl: null,
|
|
42
|
+
wrapEl: null,
|
|
43
|
+
target: null,
|
|
44
|
+
controls: null,
|
|
45
|
+
viewer: null,
|
|
46
|
+
group: null,
|
|
47
|
+
controlsChangeHandler: null,
|
|
48
|
+
captureHandlers: null,
|
|
49
|
+
stepId: null,
|
|
50
|
+
valueAdapter: null,
|
|
51
|
+
baseTransform: null,
|
|
52
|
+
};
|
|
53
|
+
static __stopGlobalActiveXform() {
|
|
54
|
+
const s = SchemaForm.__activeXform;
|
|
55
|
+
if (!s || !s.controls) return;
|
|
56
|
+
try {
|
|
57
|
+
// Detach and dispose controls
|
|
58
|
+
s.controls.detach();
|
|
59
|
+
if (s.viewer && s.viewer.scene) {
|
|
60
|
+
try { if (s.controls && s.controls.isObject3D) s.viewer.scene.remove(s.controls); } catch (_) { }
|
|
61
|
+
try { if (s.controls && s.controls.__helper && s.controls.__helper.isObject3D) s.viewer.scene.remove(s.controls.__helper); } catch (_) { }
|
|
62
|
+
try { if (s.group && s.group.isObject3D) s.viewer.scene.remove(s.group); } catch (_) { }
|
|
63
|
+
}
|
|
64
|
+
try { s.controls.dispose(); } catch (_) { }
|
|
65
|
+
} catch (_) { }
|
|
66
|
+
try {
|
|
67
|
+
// Remove any capture-phase event listeners installed during activation
|
|
68
|
+
const h = s.captureHandlers;
|
|
69
|
+
if (h && h.canvas && h.onDownCapture) {
|
|
70
|
+
h.canvas.removeEventListener('pointerdown', h.onDownCapture, true);
|
|
71
|
+
}
|
|
72
|
+
if (h && h.win && h.onUpCapture) {
|
|
73
|
+
h.win.removeEventListener('pointerup', h.onUpCapture, true);
|
|
74
|
+
}
|
|
75
|
+
} catch (_) { }
|
|
76
|
+
try {
|
|
77
|
+
if (s && s.viewer && s.viewer.controls && s.controlsChangeHandler && typeof s.viewer.controls.removeEventListener === 'function') {
|
|
78
|
+
s.viewer.controls.removeEventListener('change', s.controlsChangeHandler);
|
|
79
|
+
}
|
|
80
|
+
} catch (_) { }
|
|
81
|
+
try {
|
|
82
|
+
// Remove target object
|
|
83
|
+
if (s.viewer && s.viewer.scene && s.target) s.viewer.scene.remove(s.target);
|
|
84
|
+
} catch (_) { }
|
|
85
|
+
try { if (window.__BREP_activeXform) window.__BREP_activeXform = null; } catch (_) { }
|
|
86
|
+
try {
|
|
87
|
+
// Restore camera controls
|
|
88
|
+
if (s.viewer && s.viewer.controls) s.viewer.controls.enabled = true;
|
|
89
|
+
} catch (_) { }
|
|
90
|
+
try {
|
|
91
|
+
// Clear highlight
|
|
92
|
+
if (s.inputEl) s.inputEl.removeAttribute('active-transform');
|
|
93
|
+
const wrap = s.wrapEl;
|
|
94
|
+
if (wrap) wrap.classList.remove('ref-active');
|
|
95
|
+
} catch (_) { }
|
|
96
|
+
SchemaForm.__activeXform = {
|
|
97
|
+
owner: null,
|
|
98
|
+
key: null,
|
|
99
|
+
stepId: null,
|
|
100
|
+
inputEl: null,
|
|
101
|
+
wrapEl: null,
|
|
102
|
+
target: null,
|
|
103
|
+
controls: null,
|
|
104
|
+
viewer: null,
|
|
105
|
+
group: null,
|
|
106
|
+
captureHandlers: null,
|
|
107
|
+
controlsChangeHandler: null,
|
|
108
|
+
valueAdapter: null,
|
|
109
|
+
baseTransform: null,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
static getActiveTransformState() {
|
|
114
|
+
return SchemaForm.__activeXform;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
static getActiveReferenceInput() {
|
|
118
|
+
return SchemaForm.__activeRefInput;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
get activeTransform() {
|
|
122
|
+
return SchemaForm.__activeXform;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
get activeReferenceInput() {
|
|
126
|
+
return SchemaForm.__activeRefInput;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
isTransformSessionActiveFor(inputEl) {
|
|
130
|
+
const active = SchemaForm.__activeXform;
|
|
131
|
+
return Boolean(active && active.inputEl === inputEl);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
setActiveTransformMode(inputEl, mode) {
|
|
135
|
+
const active = SchemaForm.__activeXform;
|
|
136
|
+
if (!active || active.inputEl !== inputEl || !active.controls) return;
|
|
137
|
+
try { active.controls.setMode(mode); } catch (_) { }
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
stopTransformSessionIfOwnedByThis() {
|
|
141
|
+
const active = SchemaForm.__activeXform;
|
|
142
|
+
if (active && active.owner === this) {
|
|
143
|
+
SchemaForm.__stopGlobalActiveXform();
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* @param {Object} schema - e.g. { sizeX: {type:'number', default_value:'2*t', hint:'Width formula' }, ... }
|
|
148
|
+
* @param {Object} params - a live object to keep in sync with user edits
|
|
149
|
+
* @param {Object} [options]
|
|
150
|
+
* @param {(featureID:string|null)=>void} [options.onChange] - Callback fired on any field change
|
|
151
|
+
*/
|
|
152
|
+
constructor(schema, params, options = {}) {
|
|
153
|
+
if (!schema || typeof schema !== 'object') throw new Error('schema must be an object');
|
|
154
|
+
if (!params || typeof params !== 'object') throw new Error('params must be an object');
|
|
155
|
+
|
|
156
|
+
this.schema = schema;
|
|
157
|
+
this.params = params;
|
|
158
|
+
this.options = options;
|
|
159
|
+
this._useShadowDOM = options && Object.prototype.hasOwnProperty.call(options, 'useShadowDOM')
|
|
160
|
+
? options.useShadowDOM !== false
|
|
161
|
+
: true;
|
|
162
|
+
this._inputs = new Map();
|
|
163
|
+
this._widgets = new Map();
|
|
164
|
+
this._skipDefaultRefresh = new Set();
|
|
165
|
+
this._excludedKeys = new Set(['id', 'featureID']); // exclude from defaults & rendering
|
|
166
|
+
if (Array.isArray(options.excludeKeys)) {
|
|
167
|
+
for (const key of options.excludeKeys) {
|
|
168
|
+
if (typeof key === 'string' && key.length) this._excludedKeys.add(key);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
this.uiElement = document.createElement('div');
|
|
173
|
+
if (!this._useShadowDOM) {
|
|
174
|
+
this.uiElement.classList.add('schema-form-host');
|
|
175
|
+
}
|
|
176
|
+
this._shadow = this._useShadowDOM
|
|
177
|
+
? this.uiElement.attachShadow({ mode: 'open' })
|
|
178
|
+
: this.uiElement;
|
|
179
|
+
|
|
180
|
+
this._shadow.appendChild(this._makeStyle());
|
|
181
|
+
this._panel = document.createElement('div');
|
|
182
|
+
this._panel.className = 'panel';
|
|
183
|
+
this._shadow.appendChild(this._panel);
|
|
184
|
+
|
|
185
|
+
this._fieldsWrap = document.createElement('div');
|
|
186
|
+
this._fieldsWrap.className = 'fields';
|
|
187
|
+
this._panel.appendChild(this._fieldsWrap);
|
|
188
|
+
|
|
189
|
+
this._renderAllFields();
|
|
190
|
+
|
|
191
|
+
// Deactivate reference selection when focusing or clicking into any other control
|
|
192
|
+
const stopIfOtherControl = (target) => {
|
|
193
|
+
try {
|
|
194
|
+
// If the active input is not the current focus target, stop selection
|
|
195
|
+
const active = SchemaForm.__activeRefInput || null;
|
|
196
|
+
if (!active) return;
|
|
197
|
+
if (target === active) return;
|
|
198
|
+
// If target is inside the same active element (e.g., clicking within the input), skip
|
|
199
|
+
if (target && typeof target.closest === 'function') {
|
|
200
|
+
if (target.closest('[active-reference-selection]')) return;
|
|
201
|
+
}
|
|
202
|
+
this._stopActiveReferenceSelection();
|
|
203
|
+
} catch (_) { }
|
|
204
|
+
try {
|
|
205
|
+
// Close active transform session if clicking outside its wrapper; commit changes
|
|
206
|
+
const s = SchemaForm.__activeXform;
|
|
207
|
+
if (s && s.owner === this) {
|
|
208
|
+
if (!(target && typeof target.closest === 'function' && target.closest('.transform-wrap'))) {
|
|
209
|
+
const val = this.params[s.key];
|
|
210
|
+
SchemaForm.__stopGlobalActiveXform();
|
|
211
|
+
this._emitParamsChange(s.key, val);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
} catch (_) { }
|
|
215
|
+
};
|
|
216
|
+
// Capture focus changes within this form
|
|
217
|
+
this._shadow.addEventListener('focusin', (ev) => {
|
|
218
|
+
stopIfOtherControl(ev.target);
|
|
219
|
+
}, true);
|
|
220
|
+
// Also capture mouse interactions to be safe
|
|
221
|
+
this._shadow.addEventListener('mousedown', (ev) => {
|
|
222
|
+
stopIfOtherControl(ev.target);
|
|
223
|
+
}, true);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
destroy() {
|
|
227
|
+
// If this form owns the active reference selector, clear it before tearing down
|
|
228
|
+
try {
|
|
229
|
+
const activeRef = (typeof SchemaForm.getActiveReferenceInput === 'function')
|
|
230
|
+
? SchemaForm.getActiveReferenceInput()
|
|
231
|
+
: (SchemaForm.__activeRefInput || null);
|
|
232
|
+
const ownsActiveRef = activeRef
|
|
233
|
+
&& (
|
|
234
|
+
(this._shadow && typeof this._shadow.contains === 'function' && this._shadow.contains(activeRef))
|
|
235
|
+
|| (this.uiElement && typeof this.uiElement.contains === 'function' && this.uiElement.contains(activeRef))
|
|
236
|
+
|| (typeof activeRef.getRootNode === 'function' && activeRef.getRootNode && activeRef.getRootNode() === this._shadow)
|
|
237
|
+
);
|
|
238
|
+
if (ownsActiveRef) this._stopActiveReferenceSelection();
|
|
239
|
+
} catch (_) { /* ignore */ }
|
|
240
|
+
|
|
241
|
+
// Clean up any active transform session owned by this instance
|
|
242
|
+
try {
|
|
243
|
+
const s = SchemaForm.__activeXform;
|
|
244
|
+
if (s && s.owner === this) SchemaForm.__stopGlobalActiveXform();
|
|
245
|
+
} catch (_) { }
|
|
246
|
+
for (const widget of this._widgets.values()) {
|
|
247
|
+
if (widget && typeof widget.destroy === 'function') {
|
|
248
|
+
try { widget.destroy(); } catch (_) { /* ignore widget destroy errors */ }
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
this._widgets.clear();
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/** Returns the live params object (already kept in sync). */
|
|
255
|
+
getParams() {
|
|
256
|
+
return this.params;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/** Programmatically refresh input widgets from the current params object. */
|
|
260
|
+
refreshFromParams() {
|
|
261
|
+
for (const [key, widget] of this._widgets.entries()) {
|
|
262
|
+
if (widget && typeof widget.refreshFromParams === 'function') {
|
|
263
|
+
try {
|
|
264
|
+
widget.refreshFromParams(this.params[key], {
|
|
265
|
+
ui: this,
|
|
266
|
+
key,
|
|
267
|
+
def: this.schema[key] || {},
|
|
268
|
+
params: this.params,
|
|
269
|
+
});
|
|
270
|
+
} catch (_) { }
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
for (const [key, el] of this._inputs.entries()) {
|
|
275
|
+
const def = this.schema[key] || {};
|
|
276
|
+
if (this._skipDefaultRefresh.has(key)) {
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
const v = this._pickInitialValue(key, def);
|
|
280
|
+
// Special composite types handle their own refresh
|
|
281
|
+
if (def && def.type === 'boolean_operation') {
|
|
282
|
+
const row = this._fieldsWrap.querySelector(`[data-key="${key}"]`);
|
|
283
|
+
const select = row ? row.querySelector('select[data-role="bool-op"]') : null;
|
|
284
|
+
if (select) {
|
|
285
|
+
const opVal = (v && typeof v === 'object') ? (v.operation) : null;
|
|
286
|
+
select.value = opVal ? String(opVal) : 'NONE';
|
|
287
|
+
}
|
|
288
|
+
const chips = row ? row.querySelector('.ref-chips') : null;
|
|
289
|
+
const targets = (v && typeof v === 'object' && Array.isArray(v.targets)) ? v.targets : [];
|
|
290
|
+
if (chips) this._renderChips(chips, key, targets);
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
this._setInputValue(el, def.type, v);
|
|
294
|
+
|
|
295
|
+
// If this is a reference selection, refresh custom UI
|
|
296
|
+
if (def && def.type === 'reference_selection') {
|
|
297
|
+
const row = this._fieldsWrap.querySelector(`[data-key="${key}"]`);
|
|
298
|
+
if (def.multiple) {
|
|
299
|
+
const normalized = normalizeReferenceList(Array.isArray(v) ? v : []);
|
|
300
|
+
this.params[key] = normalized;
|
|
301
|
+
const chips = row ? row.querySelector('.ref-chips') : null;
|
|
302
|
+
if (chips) this._renderChips(chips, key, normalized);
|
|
303
|
+
} else {
|
|
304
|
+
const display = row ? row.querySelector('.ref-single-display') : null;
|
|
305
|
+
const normalized = normalizeReferenceName(v);
|
|
306
|
+
this.params[key] = normalized ?? null;
|
|
307
|
+
if (display) {
|
|
308
|
+
const label = display.querySelector('.ref-single-label');
|
|
309
|
+
const placeholder = display.dataset?.placeholder || 'Click then select in scene…';
|
|
310
|
+
if (label) label.textContent = normalized || placeholder;
|
|
311
|
+
else display.textContent = normalized || placeholder;
|
|
312
|
+
const clearBtn = display.querySelector('.ref-chip-remove');
|
|
313
|
+
if (clearBtn) clearBtn.style.visibility = normalized ? 'visible' : 'hidden';
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Transform widget: refresh info line
|
|
320
|
+
if (def && def.type === 'transform') {
|
|
321
|
+
const row = this._fieldsWrap.querySelector(`[data-key="${key}"]`);
|
|
322
|
+
const info = row ? row.querySelector('.transform-info') : null;
|
|
323
|
+
if (info) {
|
|
324
|
+
const fmt = (n) => {
|
|
325
|
+
const x = Number(n);
|
|
326
|
+
if (!Number.isFinite(x)) return '0';
|
|
327
|
+
const a = Math.abs(x);
|
|
328
|
+
const prec = a >= 100 ? 0 : (a >= 10 ? 1 : 2);
|
|
329
|
+
return String(x.toFixed(prec));
|
|
330
|
+
};
|
|
331
|
+
const p = Array.isArray(v?.position) ? v.position : [0, 0, 0];
|
|
332
|
+
const r = Array.isArray(v?.rotationEuler) ? v.rotationEuler : [0, 0, 0];
|
|
333
|
+
info.textContent = `pos(${fmt(p[0])}, ${fmt(p[1])}, ${fmt(p[2])}) rot(${fmt(r[0])}, ${fmt(r[1])}, ${fmt(r[2])})`;
|
|
334
|
+
}
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// --- Internal: rendering & behavior ---------------------------------------
|
|
341
|
+
|
|
342
|
+
_renderAllFields() {
|
|
343
|
+
// Ensure params has defaults for missing keys (without clobbering provided values)
|
|
344
|
+
for (const key in this.schema) {
|
|
345
|
+
if (!Object.prototype.hasOwnProperty.call(this.schema, key)) continue;
|
|
346
|
+
const defRaw = this.schema[key];
|
|
347
|
+
const def = (defRaw && typeof defRaw === 'object') ? defRaw : {};
|
|
348
|
+
if (this._excludedKeys.has(key)) continue;
|
|
349
|
+
if (!(key in this.params)) {
|
|
350
|
+
const raw = ('default_value' in def) ? def.default_value : this._defaultForType(def.type);
|
|
351
|
+
this.params[key] = this._cloneDefault(raw);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
this._widgets.clear();
|
|
356
|
+
this._skipDefaultRefresh.clear();
|
|
357
|
+
|
|
358
|
+
// Build field rows
|
|
359
|
+
for (const key in this.schema) {
|
|
360
|
+
if (!Object.prototype.hasOwnProperty.call(this.schema, key)) continue;
|
|
361
|
+
const defRaw = this.schema[key];
|
|
362
|
+
const def = (defRaw && typeof defRaw === 'object') ? defRaw : {};
|
|
363
|
+
if (this._excludedKeys.has(key)) continue;
|
|
364
|
+
|
|
365
|
+
const row = document.createElement('div');
|
|
366
|
+
row.className = 'field-row';
|
|
367
|
+
row.dataset.key = key;
|
|
368
|
+
|
|
369
|
+
if (def.hint != null && def.hint !== '') {
|
|
370
|
+
row.setAttribute('title', String(def.hint));
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const id = 'gfu_' + key + '_' + Math.random().toString(36).slice(2, 8);
|
|
374
|
+
|
|
375
|
+
const label = document.createElement('label');
|
|
376
|
+
label.className = 'label';
|
|
377
|
+
label.setAttribute('for', id);
|
|
378
|
+
// Allow schema to override the row label via `label`
|
|
379
|
+
label.textContent = String((def && def.label) ? def.label : this._prettyLabel(key));
|
|
380
|
+
row.appendChild(label);
|
|
381
|
+
|
|
382
|
+
const controlWrap = document.createElement('div');
|
|
383
|
+
controlWrap.className = 'control-wrap';
|
|
384
|
+
|
|
385
|
+
let inputEl;
|
|
386
|
+
let inputRegistered = true;
|
|
387
|
+
|
|
388
|
+
// Allow schema defs to supply inline renderer without touching global registry.
|
|
389
|
+
const renderer =
|
|
390
|
+
typeof def.renderWidget === 'function'
|
|
391
|
+
? def.renderWidget
|
|
392
|
+
: typeof def.widgetRenderer === 'function'
|
|
393
|
+
? def.widgetRenderer
|
|
394
|
+
: getWidgetRenderer(def.type);
|
|
395
|
+
const widget = renderer({
|
|
396
|
+
ui: this,
|
|
397
|
+
key,
|
|
398
|
+
def,
|
|
399
|
+
id,
|
|
400
|
+
controlWrap,
|
|
401
|
+
row,
|
|
402
|
+
}) || {};
|
|
403
|
+
|
|
404
|
+
inputEl = widget.inputEl;
|
|
405
|
+
if (typeof widget.inputRegistered === 'boolean') {
|
|
406
|
+
inputRegistered = widget.inputRegistered;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
if (widget && typeof widget === 'object') {
|
|
410
|
+
this._widgets.set(key, widget);
|
|
411
|
+
// Custom widgets can opt out of default refresh handling.
|
|
412
|
+
if (widget.skipDefaultRefresh === true) {
|
|
413
|
+
this._skipDefaultRefresh.add(key);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
if (!inputEl || !(inputEl instanceof HTMLElement)) {
|
|
418
|
+
inputRegistered = false;
|
|
419
|
+
const placeholder = document.createElement('div');
|
|
420
|
+
placeholder.className = 'control-placeholder';
|
|
421
|
+
placeholder.textContent = 'Control unavailable';
|
|
422
|
+
controlWrap.appendChild(placeholder);
|
|
423
|
+
} else if (!inputEl.parentNode) {
|
|
424
|
+
controlWrap.appendChild(inputEl);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
row.appendChild(controlWrap);
|
|
428
|
+
this._fieldsWrap.appendChild(row);
|
|
429
|
+
if (inputRegistered && inputEl instanceof HTMLElement) {
|
|
430
|
+
this._inputs.set(key, inputEl);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
activateField(key) {
|
|
436
|
+
const widget = this._widgets.get(key);
|
|
437
|
+
if (widget && typeof widget.activate === 'function') {
|
|
438
|
+
try { widget.activate(); } catch (_) { }
|
|
439
|
+
return true;
|
|
440
|
+
}
|
|
441
|
+
return false;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
readFieldValue(key) {
|
|
445
|
+
const widget = this._widgets.get(key);
|
|
446
|
+
if (widget && typeof widget.readValue === 'function') {
|
|
447
|
+
try { return widget.readValue(); } catch (_) { }
|
|
448
|
+
}
|
|
449
|
+
return this.params[key];
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
_cloneDefault(val) {
|
|
453
|
+
if (val == null) return val;
|
|
454
|
+
if (Array.isArray(val)) return val.map(v => this._cloneDefault(v));
|
|
455
|
+
if (typeof val === 'object') {
|
|
456
|
+
const proto = Object.getPrototypeOf(val);
|
|
457
|
+
if (proto === Object.prototype || proto === null) {
|
|
458
|
+
const out = {};
|
|
459
|
+
for (const k of Object.keys(val)) out[k] = this._cloneDefault(val[k]);
|
|
460
|
+
return out;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
return val;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// Public: Activate the first reference_selection input in this form (if any)
|
|
467
|
+
activateFirstReferenceSelection() {
|
|
468
|
+
try {
|
|
469
|
+
for (const key in this.schema) {
|
|
470
|
+
if (!Object.prototype.hasOwnProperty.call(this.schema, key)) continue;
|
|
471
|
+
const def = this.schema[key];
|
|
472
|
+
if (def && def.type === 'reference_selection') {
|
|
473
|
+
const inputEl = this._inputs.get(key);
|
|
474
|
+
if (inputEl) {
|
|
475
|
+
this._activateReferenceSelection(inputEl, def);
|
|
476
|
+
return true;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
} catch (_) { }
|
|
481
|
+
return false;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
_activateReferenceSelection(inputEl, def) {
|
|
485
|
+
// Clear any lingering scene selection so the new reference starts fresh
|
|
486
|
+
try {
|
|
487
|
+
const scene = this.options?.scene
|
|
488
|
+
|| this.options?.viewer?.partHistory?.scene
|
|
489
|
+
|| this.options?.viewer?.scene
|
|
490
|
+
|| null;
|
|
491
|
+
if (scene) {
|
|
492
|
+
SchemaForm.__setGlobalActiveRefInput(null);
|
|
493
|
+
SelectionFilter.unselectAll(scene);
|
|
494
|
+
}
|
|
495
|
+
} catch (_) { }
|
|
496
|
+
|
|
497
|
+
// Ensure only one control is globally marked as active
|
|
498
|
+
SchemaForm.__setGlobalActiveRefInput(inputEl);
|
|
499
|
+
|
|
500
|
+
// Also clear any duplicates within this shadow root (defensive)
|
|
501
|
+
const clearLocal = (root) => {
|
|
502
|
+
if (!root || typeof root.querySelectorAll !== 'function') return;
|
|
503
|
+
root.querySelectorAll('[active-reference-selection="true"],[active-reference-selection=true]').forEach(el => {
|
|
504
|
+
if (el !== inputEl) {
|
|
505
|
+
try { el.style.filter = 'none'; } catch (_) { }
|
|
506
|
+
try { el.removeAttribute('active-reference-selection'); } catch (_) { }
|
|
507
|
+
try {
|
|
508
|
+
const wrap = el.closest('.ref-single-wrap, .ref-multi-wrap');
|
|
509
|
+
if (wrap) wrap.classList.remove('ref-active');
|
|
510
|
+
} catch (_) { }
|
|
511
|
+
}
|
|
512
|
+
});
|
|
513
|
+
};
|
|
514
|
+
clearLocal(this._shadow);
|
|
515
|
+
|
|
516
|
+
// Mark this control active with a recency timestamp for any external scanners
|
|
517
|
+
try { inputEl.dataset.activatedAt = String(Date.now()); } catch (_) { }
|
|
518
|
+
inputEl.style.filter = 'invert(1)';
|
|
519
|
+
inputEl.setAttribute('active-reference-selection', 'true');
|
|
520
|
+
try {
|
|
521
|
+
const wrap = inputEl.closest('.ref-single-wrap, .ref-multi-wrap');
|
|
522
|
+
if (wrap) wrap.classList.add('ref-active');
|
|
523
|
+
} catch (_) { }
|
|
524
|
+
|
|
525
|
+
// Apply selection filter from schema
|
|
526
|
+
SelectionFilter.stashAllowedSelectionTypes();
|
|
527
|
+
SelectionFilter.SetSelectionTypes(def.selectionFilter);
|
|
528
|
+
try { window.__BREP_activeRefInput = inputEl; } catch (_) { }
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// Activate a TransformControls session for a transform widget
|
|
532
|
+
_activateTransformWidget({ inputEl, wrapEl, key, def, valueAdapter = null }) {
|
|
533
|
+
try { this._stopActiveReferenceSelection(); } catch (_) { }
|
|
534
|
+
// Toggle logic: if already active for this input, stop and hide
|
|
535
|
+
try {
|
|
536
|
+
const s = SchemaForm.__activeXform;
|
|
537
|
+
if (s && s.inputEl === inputEl) {
|
|
538
|
+
const currentVal = this.params[key];
|
|
539
|
+
SchemaForm.__stopGlobalActiveXform();
|
|
540
|
+
this._emitParamsChange(key, currentVal);
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
// If a different transform is active, stop it before starting this one
|
|
544
|
+
if (s && s.inputEl !== inputEl) {
|
|
545
|
+
SchemaForm.__stopGlobalActiveXform();
|
|
546
|
+
}
|
|
547
|
+
} catch (_) { }
|
|
548
|
+
|
|
549
|
+
const viewer = this.options?.viewer || null;
|
|
550
|
+
if (!viewer || !viewer.scene || !viewer.camera || !viewer.renderer) return;
|
|
551
|
+
|
|
552
|
+
// (Toggle handled above)
|
|
553
|
+
|
|
554
|
+
const adapter = (valueAdapter && typeof valueAdapter === 'object') ? valueAdapter : null;
|
|
555
|
+
const ensureArray3 = (arr, fallback) => {
|
|
556
|
+
const out = Array.isArray(arr) ? arr.slice(0, 3) : [];
|
|
557
|
+
while (out.length < 3) out.push(fallback);
|
|
558
|
+
return out;
|
|
559
|
+
};
|
|
560
|
+
const ensureArray4 = (arr, fallback) => {
|
|
561
|
+
if (Array.isArray(arr) && arr.length >= 4) {
|
|
562
|
+
const vals = [];
|
|
563
|
+
for (let i = 0; i < 4; i++) {
|
|
564
|
+
const n = Number(arr[i]);
|
|
565
|
+
vals.push(Number.isFinite(n) ? n : (i === 3 ? 1 : 0));
|
|
566
|
+
}
|
|
567
|
+
return vals;
|
|
568
|
+
}
|
|
569
|
+
return fallback;
|
|
570
|
+
};
|
|
571
|
+
const sanitizeTRS = (value) => {
|
|
572
|
+
const obj = (value && typeof value === 'object') ? value : {};
|
|
573
|
+
return {
|
|
574
|
+
position: ensureArray3(obj.position, 0),
|
|
575
|
+
rotationEuler: ensureArray3(obj.rotationEuler, 0),
|
|
576
|
+
scale: ensureArray3(obj.scale, 1),
|
|
577
|
+
};
|
|
578
|
+
};
|
|
579
|
+
const sanitizeBase = (value) => {
|
|
580
|
+
const obj = (value && typeof value === 'object') ? value : {};
|
|
581
|
+
const base = {
|
|
582
|
+
position: ensureArray3(obj.position, 0),
|
|
583
|
+
rotationEuler: ensureArray3(obj.rotationEuler, 0),
|
|
584
|
+
quaternion: ensureArray4(obj.quaternion, null),
|
|
585
|
+
scale: ensureArray3(obj.scale, 1),
|
|
586
|
+
};
|
|
587
|
+
if (!base.quaternion) {
|
|
588
|
+
try {
|
|
589
|
+
const e = base.rotationEuler;
|
|
590
|
+
const q = new THREE.Quaternion().setFromEuler(new THREE.Euler(
|
|
591
|
+
THREE.MathUtils.degToRad(e[0] || 0),
|
|
592
|
+
THREE.MathUtils.degToRad(e[1] || 0),
|
|
593
|
+
THREE.MathUtils.degToRad(e[2] || 0),
|
|
594
|
+
'XYZ'
|
|
595
|
+
));
|
|
596
|
+
base.quaternion = [q.x, q.y, q.z, q.w];
|
|
597
|
+
} catch (_) {
|
|
598
|
+
base.quaternion = [0, 0, 0, 1];
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
return base;
|
|
602
|
+
};
|
|
603
|
+
const safeNumber = (v, fallback) => {
|
|
604
|
+
const n = Number(v);
|
|
605
|
+
return Number.isFinite(n) ? n : fallback;
|
|
606
|
+
};
|
|
607
|
+
const safeDiv = (num, denom) => {
|
|
608
|
+
const d = safeNumber(denom, 1);
|
|
609
|
+
if (Math.abs(d) < 1e-12) return safeNumber(num, 0);
|
|
610
|
+
return safeNumber(num, 0) / d;
|
|
611
|
+
};
|
|
612
|
+
const readBaseValue = () => {
|
|
613
|
+
if (adapter && typeof adapter.getBase === 'function') {
|
|
614
|
+
try { return sanitizeBase(adapter.getBase()); } catch (_) { return sanitizeBase(null); }
|
|
615
|
+
}
|
|
616
|
+
return sanitizeBase(null);
|
|
617
|
+
};
|
|
618
|
+
const readCurrentValue = () => {
|
|
619
|
+
if (adapter && typeof adapter.get === 'function') {
|
|
620
|
+
try { return sanitizeTRS(adapter.get()); } catch (_) { return sanitizeTRS(null); }
|
|
621
|
+
}
|
|
622
|
+
return sanitizeTRS(this._pickInitialValue(key, def));
|
|
623
|
+
};
|
|
624
|
+
const writeCurrentValue = (next) => {
|
|
625
|
+
const sanitized = sanitizeTRS(next);
|
|
626
|
+
if (adapter && typeof adapter.set === 'function') {
|
|
627
|
+
try { adapter.set(sanitized); } catch (_) { }
|
|
628
|
+
} else {
|
|
629
|
+
this.params[key] = sanitized;
|
|
630
|
+
}
|
|
631
|
+
return sanitized;
|
|
632
|
+
};
|
|
633
|
+
const base = readBaseValue();
|
|
634
|
+
const cur = readCurrentValue();
|
|
635
|
+
const combineWithBase = (baseTransform, deltaTransform) => {
|
|
636
|
+
const basePos = new THREE.Vector3(
|
|
637
|
+
safeNumber(baseTransform.position[0], 0),
|
|
638
|
+
safeNumber(baseTransform.position[1], 0),
|
|
639
|
+
safeNumber(baseTransform.position[2], 0),
|
|
640
|
+
);
|
|
641
|
+
const baseQuat = new THREE.Quaternion().fromArray(baseTransform.quaternion);
|
|
642
|
+
const baseScale = new THREE.Vector3(
|
|
643
|
+
safeNumber(baseTransform.scale[0], 1),
|
|
644
|
+
safeNumber(baseTransform.scale[1], 1),
|
|
645
|
+
safeNumber(baseTransform.scale[2], 1),
|
|
646
|
+
);
|
|
647
|
+
|
|
648
|
+
const deltaPos = new THREE.Vector3(
|
|
649
|
+
safeNumber(deltaTransform.position[0], 0),
|
|
650
|
+
safeNumber(deltaTransform.position[1], 0),
|
|
651
|
+
safeNumber(deltaTransform.position[2], 0),
|
|
652
|
+
);
|
|
653
|
+
const deltaQuat = new THREE.Quaternion().setFromEuler(new THREE.Euler(
|
|
654
|
+
THREE.MathUtils.degToRad(safeNumber(deltaTransform.rotationEuler[0], 0)),
|
|
655
|
+
THREE.MathUtils.degToRad(safeNumber(deltaTransform.rotationEuler[1], 0)),
|
|
656
|
+
THREE.MathUtils.degToRad(safeNumber(deltaTransform.rotationEuler[2], 0)),
|
|
657
|
+
'XYZ',
|
|
658
|
+
));
|
|
659
|
+
const deltaScale = new THREE.Vector3(
|
|
660
|
+
safeNumber(deltaTransform.scale[0], 1),
|
|
661
|
+
safeNumber(deltaTransform.scale[1], 1),
|
|
662
|
+
safeNumber(deltaTransform.scale[2], 1),
|
|
663
|
+
);
|
|
664
|
+
|
|
665
|
+
const absPos = basePos.clone().add(deltaPos);
|
|
666
|
+
const absQuat = baseQuat.clone().multiply(deltaQuat);
|
|
667
|
+
const absScale = baseScale.clone().multiply(deltaScale);
|
|
668
|
+
return { position: absPos, quaternion: absQuat, scale: absScale };
|
|
669
|
+
};
|
|
670
|
+
const absolute = combineWithBase(base, cur);
|
|
671
|
+
|
|
672
|
+
const target = new THREE.Object3D();
|
|
673
|
+
try {
|
|
674
|
+
target.position.copy(absolute.position);
|
|
675
|
+
target.quaternion.copy(absolute.quaternion);
|
|
676
|
+
target.scale.copy(absolute.scale);
|
|
677
|
+
} catch (_) { }
|
|
678
|
+
viewer.scene.add(target);
|
|
679
|
+
|
|
680
|
+
const TCctor = CombinedTransformControls;
|
|
681
|
+
if (!TCctor) {
|
|
682
|
+
console.warn('[TransformControls] CombinedTransformControls not available; skipping gizmo.');
|
|
683
|
+
return;
|
|
684
|
+
}
|
|
685
|
+
const tc = new TCctor(viewer.camera, viewer.renderer.domElement);
|
|
686
|
+
const desiredMode = (inputEl && inputEl.dataset && inputEl.dataset.xformMode) ? String(inputEl.dataset.xformMode) : 'translate';
|
|
687
|
+
const safeMode = (desiredMode === 'scale') ? 'translate' : desiredMode;
|
|
688
|
+
tc.setMode(safeMode);
|
|
689
|
+
// Newer three.js TransformControls emit mouseDown/mouseUp instead of dragging-changed
|
|
690
|
+
let __lastCommitAt = 0;
|
|
691
|
+
const commitTransform = () => {
|
|
692
|
+
const now = Date.now();
|
|
693
|
+
if (now - __lastCommitAt < 5) return; // dedupe if two events fire together
|
|
694
|
+
__lastCommitAt = now;
|
|
695
|
+
try {
|
|
696
|
+
const featureID = (this.params && Object.prototype.hasOwnProperty.call(this.params, 'featureID'))
|
|
697
|
+
? this.params.featureID
|
|
698
|
+
: (this.params?.id ?? null);
|
|
699
|
+
if (typeof this.options.onChange === 'function') {
|
|
700
|
+
this.options.onChange(featureID);
|
|
701
|
+
}
|
|
702
|
+
} catch (_) { }
|
|
703
|
+
// After history re-runs (which clears the scene), re-add the gizmo and target so it stays active
|
|
704
|
+
try {
|
|
705
|
+
const addBack = () => {
|
|
706
|
+
try {
|
|
707
|
+
const activeState = SchemaForm.__activeXform;
|
|
708
|
+
if (!activeState) return;
|
|
709
|
+
if (activeState.owner !== this) return;
|
|
710
|
+
if (activeState.inputEl !== inputEl) return;
|
|
711
|
+
if (activeState.key !== key) return;
|
|
712
|
+
if (adapter && typeof adapter.stepId === 'string' && activeState.stepId && activeState.stepId !== adapter.stepId) return;
|
|
713
|
+
if (!viewer || !viewer.scene) return;
|
|
714
|
+
if (!tc || typeof tc.attach !== 'function') return;
|
|
715
|
+
if (target && target.isObject3D) { try { viewer.scene.add(target); } catch (_) { } }
|
|
716
|
+
const helper = (typeof tc.getHelper === 'function') ? tc.getHelper() : null;
|
|
717
|
+
if (helper && helper.isObject3D) { try { viewer.scene.add(helper); tc.__helper = helper; } catch (_) { } }
|
|
718
|
+
else if (tc && tc.isObject3D) { try { viewer.scene.add(tc); } catch (_) { } }
|
|
719
|
+
else if (tc.__fallbackGroup && tc.__fallbackGroup.isObject3D) { try { viewer.scene.add(tc.__fallbackGroup); } catch (_) { } }
|
|
720
|
+
try { if (typeof tc.attach === 'function') tc.attach(target); } catch (_) { }
|
|
721
|
+
try {
|
|
722
|
+
const m = (typeof tc.getMode === 'function') ? tc.getMode() : (tc.mode || 'translate');
|
|
723
|
+
if (typeof tc.setMode === 'function') tc.setMode(m);
|
|
724
|
+
} catch (_) { }
|
|
725
|
+
try { viewer.render && viewer.render(); } catch (_) { }
|
|
726
|
+
try { refreshOverlay(); } catch (_) { }
|
|
727
|
+
try { updateForCamera(); } catch (_) { }
|
|
728
|
+
} catch (_) { }
|
|
729
|
+
};
|
|
730
|
+
if (typeof requestAnimationFrame === 'function') requestAnimationFrame(addBack);
|
|
731
|
+
else setTimeout(addBack, 0);
|
|
732
|
+
} catch (_) { }
|
|
733
|
+
};
|
|
734
|
+
const markOverlay = (obj) => {
|
|
735
|
+
if (!obj || !obj.isObject3D) return;
|
|
736
|
+
const apply = (node) => {
|
|
737
|
+
try {
|
|
738
|
+
if (!node || !node.isObject3D) return;
|
|
739
|
+
const ud = node.userData || (node.userData = {});
|
|
740
|
+
if (ud.__brepOverlayHook) return;
|
|
741
|
+
const prev = node.onBeforeRender;
|
|
742
|
+
node.onBeforeRender = function (renderer, scene, camera, geometry, material, group) {
|
|
743
|
+
try { renderer.clearDepth(); } catch (_) { }
|
|
744
|
+
if (typeof prev === 'function') {
|
|
745
|
+
prev.call(this, renderer, scene, camera, geometry, material, group);
|
|
746
|
+
}
|
|
747
|
+
};
|
|
748
|
+
ud.__brepOverlayHook = true;
|
|
749
|
+
} catch (_) { }
|
|
750
|
+
};
|
|
751
|
+
apply(obj);
|
|
752
|
+
if (typeof obj.traverse === 'function') obj.traverse((child) => apply(child));
|
|
753
|
+
};
|
|
754
|
+
|
|
755
|
+
const refreshOverlay = () => {
|
|
756
|
+
try {
|
|
757
|
+
markOverlay(tc);
|
|
758
|
+
markOverlay(tc._gizmo);
|
|
759
|
+
markOverlay(tc._helper);
|
|
760
|
+
markOverlay(tc.gizmo);
|
|
761
|
+
markOverlay(tc.helper);
|
|
762
|
+
markOverlay(tc.__helper);
|
|
763
|
+
markOverlay(tc.__fallbackGroup);
|
|
764
|
+
} catch (_) { }
|
|
765
|
+
};
|
|
766
|
+
|
|
767
|
+
const updateForCamera = () => {
|
|
768
|
+
try {
|
|
769
|
+
if (typeof tc.update === 'function') tc.update();
|
|
770
|
+
else tc.updateMatrixWorld(true);
|
|
771
|
+
} catch (_) { }
|
|
772
|
+
refreshOverlay();
|
|
773
|
+
};
|
|
774
|
+
try { updateForCamera(); } catch (_) { }
|
|
775
|
+
try {
|
|
776
|
+
if (viewer?.controls && typeof viewer.controls.addEventListener === 'function') {
|
|
777
|
+
viewer.controls.addEventListener('change', updateForCamera);
|
|
778
|
+
}
|
|
779
|
+
} catch (_) { }
|
|
780
|
+
|
|
781
|
+
try { tc.addEventListener('mouseDown', () => { try { if (viewer.controls) viewer.controls.enabled = false; } catch (_) { } refreshOverlay(); }); } catch (_) { }
|
|
782
|
+
try { tc.addEventListener('mouseUp', () => { try { if (viewer.controls) viewer.controls.enabled = true; } catch (_) { } commitTransform(); refreshOverlay(); }); } catch (_) { }
|
|
783
|
+
// Backward/compat: older builds fire dragging-changed
|
|
784
|
+
try {
|
|
785
|
+
tc.addEventListener('dragging-changed', (ev) => {
|
|
786
|
+
try { if (viewer.controls) viewer.controls.enabled = !ev.value; } catch (_) { }
|
|
787
|
+
if (!ev.value) commitTransform();
|
|
788
|
+
refreshOverlay();
|
|
789
|
+
});
|
|
790
|
+
} catch (_) { }
|
|
791
|
+
|
|
792
|
+
const updateParamFromTarget = () => {
|
|
793
|
+
const basePosVec = new THREE.Vector3(
|
|
794
|
+
safeNumber(base.position[0], 0),
|
|
795
|
+
safeNumber(base.position[1], 0),
|
|
796
|
+
safeNumber(base.position[2], 0),
|
|
797
|
+
);
|
|
798
|
+
const relPosVec = new THREE.Vector3(target.position.x, target.position.y, target.position.z).sub(basePosVec);
|
|
799
|
+
|
|
800
|
+
const baseQuatObj = new THREE.Quaternion().fromArray(base.quaternion);
|
|
801
|
+
const relQuat = baseQuatObj.clone().invert().multiply(target.quaternion.clone());
|
|
802
|
+
const relEuler = new THREE.Euler().setFromQuaternion(relQuat, 'XYZ');
|
|
803
|
+
|
|
804
|
+
const baseScaleVec = new THREE.Vector3(
|
|
805
|
+
safeNumber(base.scale[0], 1),
|
|
806
|
+
safeNumber(base.scale[1], 1),
|
|
807
|
+
safeNumber(base.scale[2], 1),
|
|
808
|
+
);
|
|
809
|
+
const relScaleVec = new THREE.Vector3(
|
|
810
|
+
safeDiv(target.scale.x, baseScaleVec.x),
|
|
811
|
+
safeDiv(target.scale.y, baseScaleVec.y),
|
|
812
|
+
safeDiv(target.scale.z, baseScaleVec.z),
|
|
813
|
+
);
|
|
814
|
+
|
|
815
|
+
const next = {
|
|
816
|
+
position: [relPosVec.x, relPosVec.y, relPosVec.z],
|
|
817
|
+
rotationEuler: [
|
|
818
|
+
THREE.MathUtils.radToDeg(relEuler.x),
|
|
819
|
+
THREE.MathUtils.radToDeg(relEuler.y),
|
|
820
|
+
THREE.MathUtils.radToDeg(relEuler.z)
|
|
821
|
+
],
|
|
822
|
+
scale: [relScaleVec.x, relScaleVec.y, relScaleVec.z],
|
|
823
|
+
};
|
|
824
|
+
const stored = writeCurrentValue(next);
|
|
825
|
+
if (!adapter) {
|
|
826
|
+
try {
|
|
827
|
+
const row = this._fieldsWrap.querySelector(`[data-key="${key}"]`);
|
|
828
|
+
const info = row ? row.querySelector('.transform-info') : null;
|
|
829
|
+
if (info) {
|
|
830
|
+
const fmt = (n) => {
|
|
831
|
+
const x = Number(n);
|
|
832
|
+
if (!Number.isFinite(x)) return '0';
|
|
833
|
+
const a = Math.abs(x);
|
|
834
|
+
const prec = a >= 100 ? 0 : (a >= 10 ? 1 : 2);
|
|
835
|
+
return String(x.toFixed(prec));
|
|
836
|
+
};
|
|
837
|
+
info.textContent = `pos(${fmt(stored.position[0])}, ${fmt(stored.position[1])}, ${fmt(stored.position[2])}) rot(${fmt(stored.rotationEuler[0])}, ${fmt(stored.rotationEuler[1])}, ${fmt(stored.rotationEuler[2])})`;
|
|
838
|
+
}
|
|
839
|
+
try {
|
|
840
|
+
const pairs = [
|
|
841
|
+
['.tf-pos-x', stored.position[0]],
|
|
842
|
+
['.tf-pos-y', stored.position[1]],
|
|
843
|
+
['.tf-pos-z', stored.position[2]],
|
|
844
|
+
['.tf-rot-x', stored.rotationEuler[0]],
|
|
845
|
+
['.tf-rot-y', stored.rotationEuler[1]],
|
|
846
|
+
['.tf-rot-z', stored.rotationEuler[2]],
|
|
847
|
+
];
|
|
848
|
+
for (const [sel, val] of pairs) {
|
|
849
|
+
const el = row ? row.querySelector(sel) : null;
|
|
850
|
+
if (el) this._setInputValue(el, 'number', val);
|
|
851
|
+
}
|
|
852
|
+
} catch (_) { }
|
|
853
|
+
} catch (_) { }
|
|
854
|
+
}
|
|
855
|
+
};
|
|
856
|
+
tc.addEventListener('change', (ev) => { updateParamFromTarget(ev); refreshOverlay(); });
|
|
857
|
+
// Fallback commit for cases where mouseUp/dragging-changed are unreliable (some builds)
|
|
858
|
+
try { tc.addEventListener('objectChange', () => { try { if (!tc.dragging) commitTransform(); } catch (_) { } refreshOverlay(); }); } catch (_) { }
|
|
859
|
+
|
|
860
|
+
// Expose an isOver helper for Viewer to suppress its own handlers when interacting with gizmo
|
|
861
|
+
const isOver = (ev) => {
|
|
862
|
+
try {
|
|
863
|
+
const canvas = viewer.renderer.domElement;
|
|
864
|
+
const rect = canvas.getBoundingClientRect();
|
|
865
|
+
const x = (ev.clientX - rect.left) / rect.width; // 0..1
|
|
866
|
+
const y = (ev.clientY - rect.top) / rect.height; // 0..1
|
|
867
|
+
// Use viewer helper for consistent NDC mapping
|
|
868
|
+
const ndc = (typeof viewer._getPointerNDC === 'function')
|
|
869
|
+
? viewer._getPointerNDC({ clientX: ev.clientX, clientY: ev.clientY })
|
|
870
|
+
: new THREE.Vector2(x * 2 - 1, -(y * 2 - 1));
|
|
871
|
+
viewer.raycaster.setFromCamera(ndc, viewer.camera);
|
|
872
|
+
// Prefer precise picker meshes for the current mode; fallback to whole gizmo
|
|
873
|
+
const mode = (typeof tc.getMode === 'function') ? tc.getMode() : (tc.mode || desiredMode || 'translate');
|
|
874
|
+
const giz = tc._gizmo || tc.gizmo || null;
|
|
875
|
+
const pick = (giz && giz.picker) ? (giz.picker[mode] || giz.picker.translate || giz.picker.rotate) : null;
|
|
876
|
+
const pickRoot = pick || giz || tc.__fallbackGroup || null;
|
|
877
|
+
if (!pickRoot) return false;
|
|
878
|
+
const hits = viewer.raycaster.intersectObject(pickRoot, true) || [];
|
|
879
|
+
return hits.length > 0;
|
|
880
|
+
} catch (_) { return false; }
|
|
881
|
+
};
|
|
882
|
+
try {
|
|
883
|
+
window.__BREP_activeXform = {
|
|
884
|
+
controls: tc,
|
|
885
|
+
viewer,
|
|
886
|
+
isOver,
|
|
887
|
+
target,
|
|
888
|
+
group: tc.__fallbackGroup || (tc && tc.isObject3D ? tc : null),
|
|
889
|
+
updateForCamera,
|
|
890
|
+
};
|
|
891
|
+
} catch (_) { }
|
|
892
|
+
|
|
893
|
+
let addedToScene = false;
|
|
894
|
+
try { markOverlay(tc._gizmo); } catch (_) { }
|
|
895
|
+
try { markOverlay(tc._helper); } catch (_) { }
|
|
896
|
+
try { markOverlay(tc.gizmo); } catch (_) { }
|
|
897
|
+
try { markOverlay(tc.helper); } catch (_) { }
|
|
898
|
+
|
|
899
|
+
try {
|
|
900
|
+
// Preferred modern API: helper root on the controls
|
|
901
|
+
const helper = (typeof tc.getHelper === 'function') ? tc.getHelper() : null;
|
|
902
|
+
if (helper && helper.isObject3D) {
|
|
903
|
+
try { helper.userData = helper.userData || {}; helper.userData.excludeFromFit = true; } catch (_) { }
|
|
904
|
+
markOverlay(helper);
|
|
905
|
+
viewer.scene.add(helper); addedToScene = true; tc.__helper = helper;
|
|
906
|
+
}
|
|
907
|
+
else if (tc && tc.isObject3D) {
|
|
908
|
+
try { tc.userData = tc.userData || {}; tc.userData.excludeFromFit = true; } catch (_) { }
|
|
909
|
+
markOverlay(tc);
|
|
910
|
+
viewer.scene.add(tc); addedToScene = true;
|
|
911
|
+
}
|
|
912
|
+
} catch (_) { /* tolerate builds where controls aren't Object3D */ }
|
|
913
|
+
if (!addedToScene) {
|
|
914
|
+
// Fallback: try adding known internal object3D parts if present
|
|
915
|
+
try {
|
|
916
|
+
const group = new THREE.Group();
|
|
917
|
+
group.name = 'TransformControlsGroup';
|
|
918
|
+
const candidates = [tc?.gizmo, tc?._gizmo, tc?.picker, tc?._picker, tc?.helper, tc?._helper];
|
|
919
|
+
let attached = 0;
|
|
920
|
+
for (const cand of candidates) {
|
|
921
|
+
if (cand && cand.isObject3D) { try { group.add(cand); attached++; } catch (_) { } }
|
|
922
|
+
}
|
|
923
|
+
if (attached > 0) {
|
|
924
|
+
try { group.userData = group.userData || {}; group.userData.excludeFromFit = true; } catch (_) { }
|
|
925
|
+
markOverlay(group);
|
|
926
|
+
viewer.scene.add(group); addedToScene = true; tc.__fallbackGroup = group;
|
|
927
|
+
}
|
|
928
|
+
} catch (_) { /* ignore */ }
|
|
929
|
+
if (!addedToScene) {
|
|
930
|
+
// eslint-disable-next-line no-console
|
|
931
|
+
console.warn('[TransformControls] Could not add gizmo to scene (no Object3D found).');
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
try { tc.showX = true; tc.showY = true; tc.showZ = true; } catch (_) { }
|
|
935
|
+
try { tc.setSpace('world'); } catch (_) { }
|
|
936
|
+
try { tc.addEventListener('change', () => { try { viewer.render(); } catch (_) { } }); } catch (_) { }
|
|
937
|
+
try { tc.attach(target); markOverlay(tc); markOverlay(tc.__helper); markOverlay(tc.__fallbackGroup); } catch (_) { }
|
|
938
|
+
|
|
939
|
+
// Mark active
|
|
940
|
+
inputEl.setAttribute('active-transform', 'true');
|
|
941
|
+
try { wrapEl.classList.add('ref-active'); } catch (_) { }
|
|
942
|
+
|
|
943
|
+
SchemaForm.__activeXform = {
|
|
944
|
+
owner: this,
|
|
945
|
+
key,
|
|
946
|
+
stepId: adapter && typeof adapter.stepId === 'string' ? adapter.stepId : null,
|
|
947
|
+
inputEl,
|
|
948
|
+
wrapEl,
|
|
949
|
+
target,
|
|
950
|
+
controls: tc,
|
|
951
|
+
viewer,
|
|
952
|
+
group: tc.__fallbackGroup || (tc && tc.isObject3D ? tc : null),
|
|
953
|
+
captureHandlers: null,
|
|
954
|
+
controlsChangeHandler: updateForCamera,
|
|
955
|
+
valueAdapter: adapter || null,
|
|
956
|
+
baseTransform: base,
|
|
957
|
+
};
|
|
958
|
+
|
|
959
|
+
// Install capture-phase listeners to disable ArcballControls early when pressing gizmo
|
|
960
|
+
try {
|
|
961
|
+
const canvas = viewer && viewer.renderer ? viewer.renderer.domElement : null;
|
|
962
|
+
if (canvas && typeof canvas.addEventListener === 'function') {
|
|
963
|
+
const onDownCapture = (ev) => {
|
|
964
|
+
try {
|
|
965
|
+
if (isOver(ev)) {
|
|
966
|
+
if (viewer && viewer.controls) viewer.controls.enabled = false;
|
|
967
|
+
}
|
|
968
|
+
} catch (_) { }
|
|
969
|
+
};
|
|
970
|
+
const onUpCapture = (ev) => {
|
|
971
|
+
// Re-enable controls on pointer release to be safe
|
|
972
|
+
try { if (viewer && viewer.controls) viewer.controls.enabled = true; } catch (_) { }
|
|
973
|
+
void ev;
|
|
974
|
+
};
|
|
975
|
+
canvas.addEventListener('pointerdown', onDownCapture, { passive: true, capture: true });
|
|
976
|
+
// Use window to ensure we catch release even if released off-canvas
|
|
977
|
+
window.addEventListener('pointerup', onUpCapture, { passive: true, capture: true });
|
|
978
|
+
SchemaForm.__activeXform.captureHandlers = { canvas, win: window, onDownCapture, onUpCapture };
|
|
979
|
+
}
|
|
980
|
+
} catch (_) { /* ignore */ }
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
_stopActiveTransformWidget() {
|
|
984
|
+
try { SchemaForm.__stopGlobalActiveXform(); } catch (_) { }
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
|
|
988
|
+
_stopActiveReferenceSelection() {
|
|
989
|
+
// Clear global active if it belongs to this instance
|
|
990
|
+
try {
|
|
991
|
+
if (SchemaForm.__activeRefInput) {
|
|
992
|
+
try { SchemaForm.__activeRefInput.style.filter = 'none'; } catch (_) { }
|
|
993
|
+
try { SchemaForm.__activeRefInput.removeAttribute('active-reference-selection'); } catch (_) { }
|
|
994
|
+
try {
|
|
995
|
+
const wrap = SchemaForm.__activeRefInput.closest('.ref-single-wrap, .ref-multi-wrap');
|
|
996
|
+
if (wrap) wrap.classList.remove('ref-active');
|
|
997
|
+
} catch (_) { }
|
|
998
|
+
}
|
|
999
|
+
} catch (_) { }
|
|
1000
|
+
SchemaForm.__activeRefInput = null;
|
|
1001
|
+
try { if (window.__BREP_activeRefInput === undefined || window.__BREP_activeRefInput === SchemaForm.__activeRefInput) window.__BREP_activeRefInput = null; } catch (_) { }
|
|
1002
|
+
SelectionFilter.restoreAllowedSelectionTypes();
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
_renderChips(chipsWrap, key, values) {
|
|
1006
|
+
chipsWrap.textContent = '';
|
|
1007
|
+
const arr = Array.isArray(values) ? values : [];
|
|
1008
|
+
const normalizedValues = normalizeReferenceList(arr);
|
|
1009
|
+
const inputEl = (this._inputs && typeof this._inputs.get === 'function') ? this._inputs.get(key) : null;
|
|
1010
|
+
if (inputEl) {
|
|
1011
|
+
if (typeof inputEl.__updateSelectionMetadata === 'function') {
|
|
1012
|
+
try { inputEl.__updateSelectionMetadata(normalizedValues); } catch (_) { }
|
|
1013
|
+
} else if (inputEl.dataset && inputEl.dataset.multiple === 'true') {
|
|
1014
|
+
try { inputEl.dataset.selectedCount = String(normalizedValues.length); } catch (_) { }
|
|
1015
|
+
try { inputEl.dataset.selectedValues = JSON.stringify(normalizedValues); } catch (_) { }
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
if (Array.isArray(this.params[key])) {
|
|
1019
|
+
this.params[key] = normalizedValues;
|
|
1020
|
+
} else if (this.params[key] && typeof this.params[key] === 'object' && Array.isArray(this.params[key].targets)) {
|
|
1021
|
+
this.params[key].targets = normalizedValues;
|
|
1022
|
+
}
|
|
1023
|
+
for (const name of normalizedValues) {
|
|
1024
|
+
const chip = document.createElement('span');
|
|
1025
|
+
chip.className = 'ref-chip';
|
|
1026
|
+
|
|
1027
|
+
const label = document.createElement('span');
|
|
1028
|
+
label.className = 'ref-chip-label';
|
|
1029
|
+
label.textContent = name;
|
|
1030
|
+
chip.appendChild(label);
|
|
1031
|
+
|
|
1032
|
+
// Hover highlight on chip hover
|
|
1033
|
+
chip.addEventListener('mouseenter', () => {
|
|
1034
|
+
try { SelectionFilter.setHoverByName(this.options?.scene || null, name); } catch (_) { }
|
|
1035
|
+
});
|
|
1036
|
+
chip.addEventListener('mouseleave', () => {
|
|
1037
|
+
try { SelectionFilter.clearHover(); } catch (_) { }
|
|
1038
|
+
});
|
|
1039
|
+
|
|
1040
|
+
const btn = document.createElement('span');
|
|
1041
|
+
btn.className = 'ref-chip-remove';
|
|
1042
|
+
btn.textContent = '✕';
|
|
1043
|
+
btn.title = 'Remove';
|
|
1044
|
+
btn.addEventListener('click', (ev) => {
|
|
1045
|
+
ev.stopPropagation();
|
|
1046
|
+
// Support both plain array params and object-with-targets
|
|
1047
|
+
let currentArrayRef = null;
|
|
1048
|
+
if (Array.isArray(this.params[key])) {
|
|
1049
|
+
currentArrayRef = this.params[key];
|
|
1050
|
+
} else if (this.params[key] && typeof this.params[key] === 'object' && Array.isArray(this.params[key].targets)) {
|
|
1051
|
+
currentArrayRef = this.params[key].targets;
|
|
1052
|
+
} else {
|
|
1053
|
+
// Initialize as array if nothing sensible exists
|
|
1054
|
+
this.params[key] = [];
|
|
1055
|
+
currentArrayRef = this.params[key];
|
|
1056
|
+
}
|
|
1057
|
+
const idx = currentArrayRef.indexOf(name);
|
|
1058
|
+
if (idx >= 0) currentArrayRef.splice(idx, 1);
|
|
1059
|
+
this._renderChips(chipsWrap, key, currentArrayRef);
|
|
1060
|
+
this._emitParamsChange(key, this.params[key]);
|
|
1061
|
+
try {
|
|
1062
|
+
if (typeof this.options.onReferenceChipRemove === 'function') {
|
|
1063
|
+
this.options.onReferenceChipRemove(name, key);
|
|
1064
|
+
}
|
|
1065
|
+
} catch (_) { }
|
|
1066
|
+
});
|
|
1067
|
+
chip.appendChild(btn);
|
|
1068
|
+
|
|
1069
|
+
chipsWrap.appendChild(chip);
|
|
1070
|
+
}
|
|
1071
|
+
if (normalizedValues.length === 0) {
|
|
1072
|
+
const hint = document.createElement('span');
|
|
1073
|
+
hint.className = 'ref-chip';
|
|
1074
|
+
hint.style.opacity = '0.6';
|
|
1075
|
+
let hintText = 'Click then pick items in scene';
|
|
1076
|
+
if (inputEl && inputEl.dataset) {
|
|
1077
|
+
const minAttr = Number(inputEl.dataset.minSelections);
|
|
1078
|
+
if (Number.isFinite(minAttr) && minAttr > 0) {
|
|
1079
|
+
hintText = `Select at least ${minAttr} item${minAttr === 1 ? '' : 's'}`;
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
hint.textContent = hintText;
|
|
1083
|
+
chipsWrap.appendChild(hint);
|
|
1084
|
+
} else if (inputEl && inputEl.dataset) {
|
|
1085
|
+
const minAttr = Number(inputEl.dataset.minSelections);
|
|
1086
|
+
if (Number.isFinite(minAttr) && minAttr > 0 && normalizedValues.length < minAttr) {
|
|
1087
|
+
const hint = document.createElement('span');
|
|
1088
|
+
hint.className = 'ref-chip';
|
|
1089
|
+
hint.style.opacity = '0.6';
|
|
1090
|
+
hint.textContent = `Need ${minAttr - normalizedValues.length} more`;
|
|
1091
|
+
chipsWrap.appendChild(hint);
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
_emitParamsChange(key, value) {
|
|
1097
|
+
// Suppress auto-run if a transform editing session is active on this form
|
|
1098
|
+
try {
|
|
1099
|
+
const s = SchemaForm.__activeXform;
|
|
1100
|
+
if (s && s.owner === this) return;
|
|
1101
|
+
} catch (_) { }
|
|
1102
|
+
if (typeof this.options.onChange === 'function') {
|
|
1103
|
+
const featureID = (this.params && Object.prototype.hasOwnProperty.call(this.params, 'featureID'))
|
|
1104
|
+
? this.params.featureID
|
|
1105
|
+
: (this.params?.id ?? null);
|
|
1106
|
+
const details = { key, value, params: this.params, form: this };
|
|
1107
|
+
try {
|
|
1108
|
+
this.options.onChange(featureID, details);
|
|
1109
|
+
} catch (error) {
|
|
1110
|
+
// eslint-disable-next-line no-console
|
|
1111
|
+
console.log(error);
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
_pickInitialValue(key, def) {
|
|
1117
|
+
if (this.params[key] !== undefined && this.params[key] !== null) return this.params[key];
|
|
1118
|
+
if (Object.prototype.hasOwnProperty.call(def, 'default_value')) return def.default_value;
|
|
1119
|
+
return this._defaultForType(def.type);
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
_defaultForType(type) {
|
|
1123
|
+
switch (type) {
|
|
1124
|
+
case 'boolean': return false;
|
|
1125
|
+
case 'options': return '';
|
|
1126
|
+
case 'reference_selection': return null;
|
|
1127
|
+
case 'transform': return { position: [0, 0, 0], rotationEuler: [0, 0, 0], scale: [1, 1, 1] };
|
|
1128
|
+
case 'vec3': return [0, 0, 0];
|
|
1129
|
+
default: return '';
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
_setInputValue(el, type, value) {
|
|
1134
|
+
switch (type) {
|
|
1135
|
+
case 'boolean':
|
|
1136
|
+
el.checked = Boolean(value);
|
|
1137
|
+
break;
|
|
1138
|
+
case 'number': {
|
|
1139
|
+
// Accept formulas or plain numbers. If the value is not purely numeric,
|
|
1140
|
+
// render the input as text so the expression is visible. Some inputs
|
|
1141
|
+
// force text rendering to avoid type switching.
|
|
1142
|
+
const rawStr = value == null ? '' : String(value);
|
|
1143
|
+
const numericLike = /^\s*[-+]?((\d+(?:\.\d*)?)|(\.\d+))(?:[eE][-+]?\d+)?\s*$/.test(rawStr);
|
|
1144
|
+
const forceText = el && el.dataset && el.dataset.forceText === 'true';
|
|
1145
|
+
try {
|
|
1146
|
+
if (!forceText && numericLike) {
|
|
1147
|
+
if (el.type !== 'number') el.type = 'number';
|
|
1148
|
+
// Re-apply numeric attributes if we previously toggled away
|
|
1149
|
+
if (el.dataset && el.dataset.step) el.step = el.dataset.step;
|
|
1150
|
+
if (el.dataset && el.dataset.min) el.min = el.dataset.min;
|
|
1151
|
+
if (el.dataset && el.dataset.max) el.max = el.dataset.max;
|
|
1152
|
+
} else {
|
|
1153
|
+
if (el.type !== 'text') el.type = 'text';
|
|
1154
|
+
}
|
|
1155
|
+
} catch (_) { /* ignore */ }
|
|
1156
|
+
// Limit programmatically-set numeric text to 6 decimal places.
|
|
1157
|
+
const format6 = (v) => {
|
|
1158
|
+
let n = Number(v);
|
|
1159
|
+
if (!Number.isFinite(n)) return rawStr;
|
|
1160
|
+
if (Math.abs(n) < 1e-12) n = 0; // avoid tiny scientific notation
|
|
1161
|
+
let s = n.toFixed(6);
|
|
1162
|
+
s = s.replace(/\.0+$/, ''); // trim trailing .000000
|
|
1163
|
+
s = s.replace(/(\.\d*?[1-9])0+$/, '$1'); // trim trailing zeros
|
|
1164
|
+
if (s === '-0') s = '0';
|
|
1165
|
+
return s;
|
|
1166
|
+
};
|
|
1167
|
+
el.value = numericLike ? format6(value) : rawStr;
|
|
1168
|
+
break;
|
|
1169
|
+
}
|
|
1170
|
+
case 'options': {
|
|
1171
|
+
const asStr = String(value == null ? '' : value);
|
|
1172
|
+
let has = false;
|
|
1173
|
+
for (let i = 0; i < el.options.length; i++) {
|
|
1174
|
+
if (el.options[i].value === asStr) { has = true; break; }
|
|
1175
|
+
}
|
|
1176
|
+
el.value = has ? asStr : (el.options[0] ? el.options[0].value : '');
|
|
1177
|
+
break;
|
|
1178
|
+
}
|
|
1179
|
+
case 'file': {
|
|
1180
|
+
// Update the info label adjacent to the button
|
|
1181
|
+
try {
|
|
1182
|
+
const wrap = el && el.parentNode ? el.parentNode : null;
|
|
1183
|
+
const info = wrap ? wrap.querySelector('.file-info') : null;
|
|
1184
|
+
if (info) {
|
|
1185
|
+
if (typeof value === 'string' && value.startsWith('data:') && value.includes(';base64,')) {
|
|
1186
|
+
const b64 = value.split(',')[1] || '';
|
|
1187
|
+
const size = Math.floor((b64.length * 3) / 4);
|
|
1188
|
+
info.textContent = `Loaded (${size} bytes)`;
|
|
1189
|
+
} else if (value && String(value).length) {
|
|
1190
|
+
info.textContent = `Loaded (${String(value).length} chars)`;
|
|
1191
|
+
} else {
|
|
1192
|
+
info.textContent = 'No file selected';
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
} catch (_) { }
|
|
1196
|
+
break;
|
|
1197
|
+
}
|
|
1198
|
+
default:
|
|
1199
|
+
el.value = value == null ? '' : String(value);
|
|
1200
|
+
break;
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
_prettyLabel(key) {
|
|
1205
|
+
const withSpaces = String(key)
|
|
1206
|
+
.replace(/_/g, ' ')
|
|
1207
|
+
.replace(/([a-z])([A-Z])/g, '$1 $2')
|
|
1208
|
+
.replace(/\s+/g, ' ')
|
|
1209
|
+
.trim();
|
|
1210
|
+
return withSpaces.charAt(0).toUpperCase() + withSpaces.slice(1);
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
_makeStyle() {
|
|
1214
|
+
const style = document.createElement('style');
|
|
1215
|
+
style.textContent = `
|
|
1216
|
+
:host, .schema-form-host, .panel {
|
|
1217
|
+
--bg: #0f1117;
|
|
1218
|
+
--bg-elev: #12141b;
|
|
1219
|
+
--border: #262b36;
|
|
1220
|
+
--text: #e6e6e6;
|
|
1221
|
+
--muted: #9aa4b2;
|
|
1222
|
+
--accent: #6ea8fe;
|
|
1223
|
+
--focus: #3b82f6;
|
|
1224
|
+
--input-bg: #0b0e14;
|
|
1225
|
+
--radius: 12px;
|
|
1226
|
+
--gap: 3px;
|
|
1227
|
+
color-scheme: dark;
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
.panel {
|
|
1231
|
+
color: var(--text);
|
|
1232
|
+
background: transparent;
|
|
1233
|
+
border-radius: var(--radius);
|
|
1234
|
+
max-width: 100%;
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
.fields {
|
|
1238
|
+
display: flex;
|
|
1239
|
+
flex-direction: column;
|
|
1240
|
+
gap: 12px;
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
.field-row {
|
|
1244
|
+
display: grid;
|
|
1245
|
+
grid-template-columns: 1fr;
|
|
1246
|
+
gap: 6px;
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
.label {
|
|
1250
|
+
color: var(--muted);
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
.control-wrap { display: flex; flex-direction: column; gap: 6px; }
|
|
1254
|
+
|
|
1255
|
+
.input, .select {
|
|
1256
|
+
|
|
1257
|
+
background: var(--input-bg);
|
|
1258
|
+
color: var(--text);
|
|
1259
|
+
border: 1px solid var(--border);
|
|
1260
|
+
border-radius: 10px;
|
|
1261
|
+
padding: 8px 10px;
|
|
1262
|
+
outline: none;
|
|
1263
|
+
transition: border-color .15s ease, box-shadow .15s ease;
|
|
1264
|
+
width: 100%;
|
|
1265
|
+
box-sizing: border-box;
|
|
1266
|
+
}
|
|
1267
|
+
.number-input-wrap {
|
|
1268
|
+
position: relative;
|
|
1269
|
+
width: 100%;
|
|
1270
|
+
}
|
|
1271
|
+
.number-input {
|
|
1272
|
+
padding-right: 36px;
|
|
1273
|
+
}
|
|
1274
|
+
.number-stepper {
|
|
1275
|
+
position: absolute;
|
|
1276
|
+
top: 4px;
|
|
1277
|
+
bottom: 4px;
|
|
1278
|
+
right: 4px;
|
|
1279
|
+
width: 26px;
|
|
1280
|
+
display: flex;
|
|
1281
|
+
flex-direction: column;
|
|
1282
|
+
border-left: 1px solid var(--border);
|
|
1283
|
+
border-radius: 8px;
|
|
1284
|
+
overflow: hidden;
|
|
1285
|
+
background: rgba(255,255,255,.02);
|
|
1286
|
+
}
|
|
1287
|
+
.number-stepper-btn {
|
|
1288
|
+
appearance: none;
|
|
1289
|
+
border: 0;
|
|
1290
|
+
padding: 0;
|
|
1291
|
+
margin: 0;
|
|
1292
|
+
background: transparent;
|
|
1293
|
+
cursor: pointer;
|
|
1294
|
+
flex: 1 1 50%;
|
|
1295
|
+
display: flex;
|
|
1296
|
+
align-items: center;
|
|
1297
|
+
justify-content: center;
|
|
1298
|
+
}
|
|
1299
|
+
.number-stepper-btn::before {
|
|
1300
|
+
content: '';
|
|
1301
|
+
width: 0;
|
|
1302
|
+
height: 0;
|
|
1303
|
+
border-left: 4px solid transparent;
|
|
1304
|
+
border-right: 4px solid transparent;
|
|
1305
|
+
}
|
|
1306
|
+
.number-stepper-up { border-bottom: 1px solid var(--border); }
|
|
1307
|
+
.number-stepper-up::before { border-bottom: 6px solid var(--muted); }
|
|
1308
|
+
.number-stepper-down::before { border-top: 6px solid var(--muted); }
|
|
1309
|
+
.number-stepper-up:hover::before { border-bottom-color: var(--text); }
|
|
1310
|
+
.number-stepper-down:hover::before { border-top-color: var(--text); }
|
|
1311
|
+
.number-stepper-btn:active { background: rgba(255,255,255,.06); }
|
|
1312
|
+
textarea.input {
|
|
1313
|
+
resize: vertical;
|
|
1314
|
+
line-height: 1.4;
|
|
1315
|
+
min-height: 72px;
|
|
1316
|
+
font-family: inherit;
|
|
1317
|
+
}
|
|
1318
|
+
.btn {
|
|
1319
|
+
appearance: none;
|
|
1320
|
+
background: linear-gradient(180deg, rgba(255,255,255,.06), rgba(255,255,255,.03));
|
|
1321
|
+
color: var(--text);
|
|
1322
|
+
border: 1px solid var(--border);
|
|
1323
|
+
border-radius: 10px;
|
|
1324
|
+
padding: 8px 12px;
|
|
1325
|
+
cursor: pointer;
|
|
1326
|
+
transition: border-color .15s ease, box-shadow .15s ease, transform .05s ease;
|
|
1327
|
+
}
|
|
1328
|
+
.btn.btn-slim { padding: 6px 10px; border-radius: 8px; font-size: 12px; }
|
|
1329
|
+
.btn.selected { border-color: var(--focus); color: #fff; }
|
|
1330
|
+
.btn:hover { border-color: var(--focus); box-shadow: 0 0 0 3px rgba(59,130,246,.15); }
|
|
1331
|
+
.btn:active { transform: translateY(1px); }
|
|
1332
|
+
.input:focus, .select:focus {
|
|
1333
|
+
border-color: var(--focus);
|
|
1334
|
+
|
|
1335
|
+
}
|
|
1336
|
+
.file-info { font-size: 12px; color: var(--muted); }
|
|
1337
|
+
|
|
1338
|
+
.checkbox {
|
|
1339
|
+
width: 18px; height: 18px;
|
|
1340
|
+
accent-color: var(--accent);
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
.ref-select-placeholder {
|
|
1344
|
+
min-height: 36px;
|
|
1345
|
+
border: 1px dashed var(--border);
|
|
1346
|
+
border-radius: 10px;
|
|
1347
|
+
background: linear-gradient(180deg, rgba(255,255,255,.02), rgba(255,255,255,.01));
|
|
1348
|
+
}
|
|
1349
|
+
/* Single reference display (replaces textbox) */
|
|
1350
|
+
.ref-single-wrap { display: block; }
|
|
1351
|
+
.ref-single-display {
|
|
1352
|
+
appearance: none;
|
|
1353
|
+
background: var(--input-bg);
|
|
1354
|
+
color: var(--text);
|
|
1355
|
+
border: 1px solid var(--border);
|
|
1356
|
+
border-radius: 10px;
|
|
1357
|
+
padding: 8px 10px;
|
|
1358
|
+
outline: none;
|
|
1359
|
+
cursor: pointer;
|
|
1360
|
+
user-select: none;
|
|
1361
|
+
min-height: 36px;
|
|
1362
|
+
display: flex;
|
|
1363
|
+
align-items: center;
|
|
1364
|
+
justify-content: space-between;
|
|
1365
|
+
gap: 8px;
|
|
1366
|
+
}
|
|
1367
|
+
.ref-single-label { flex: 1 1 auto; overflow-wrap: anywhere; text-align: left; }
|
|
1368
|
+
/* Active highlight for ref widgets */
|
|
1369
|
+
.ref-single-wrap.ref-active .ref-single-display,
|
|
1370
|
+
.ref-multi-wrap.ref-active .ref-chips {
|
|
1371
|
+
border-color: var(--focus);
|
|
1372
|
+
box-shadow: 0 0 0 3px rgba(59,130,246,.15);
|
|
1373
|
+
}
|
|
1374
|
+
/* Multi reference chips */
|
|
1375
|
+
.ref-multi-wrap { display: flex; flex-direction: column; gap: 6px; }
|
|
1376
|
+
.ref-chips { display: flex; flex-wrap: wrap; gap: 6px; padding: 4px; border: 1px dashed var(--border); border-radius: 10px; cursor: pointer; background: linear-gradient(180deg, rgba(255,255,255,.02), rgba(255,255,255,.01)); max-width: 100%; }
|
|
1377
|
+
.ref-multi-wrap.ref-limit-reached .ref-chips { border-color: #f97316; animation: refLimitPulse 0.48s ease; }
|
|
1378
|
+
@keyframes refLimitPulse {
|
|
1379
|
+
0% { box-shadow: 0 0 0 0 rgba(249,115,22,0.32); }
|
|
1380
|
+
100% { box-shadow: 0 0 0 12px rgba(249,115,22,0); }
|
|
1381
|
+
}
|
|
1382
|
+
.ref-chip { display: inline-flex; align-items: center; gap: 6px; padding: 4px 8px; border-radius: 999px; background: #1a2030; border: 1px solid var(--border); font-size: 12px; max-width: 100%; }
|
|
1383
|
+
.ref-chip-label { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; word-break: break-word; white-space: normal; }
|
|
1384
|
+
.ref-chip-remove { color: var(--muted); cursor: pointer; flex: 0 0 auto; }
|
|
1385
|
+
.ref-chip-remove:hover { color: var(--danger); }
|
|
1386
|
+
|
|
1387
|
+
/* Transform widget */
|
|
1388
|
+
.transform-wrap { display: flex; flex-direction: column; gap: 8px; }
|
|
1389
|
+
.transform-modes { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
|
|
1390
|
+
.transform-info { font-size: 12px; color: var(--muted); }
|
|
1391
|
+
.transform-details { display: none; }
|
|
1392
|
+
.transform-wrap.ref-active .transform-details { display: block; }
|
|
1393
|
+
.transform-grid { display: flex; flex-direction: column; gap: 6px; }
|
|
1394
|
+
.transform-row { display: grid; grid-template-columns: auto 1fr; align-items: center; gap: 8px; }
|
|
1395
|
+
.transform-label { color: var(--muted); font-size: 12px; }
|
|
1396
|
+
.transform-inputs { display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap: 6px; }
|
|
1397
|
+
.transform-input { padding: 6px 8px; }
|
|
1398
|
+
.transform-wrap.ref-active .btn { border-color: var(--focus); box-shadow: 0 0 0 3px rgba(59,130,246,.15); }
|
|
1399
|
+
|
|
1400
|
+
.multi-transform-wrap { display: flex; flex-direction: column; gap: 10px; }
|
|
1401
|
+
.mt-list { display: flex; flex-direction: column; gap: 10px; }
|
|
1402
|
+
.mt-item { display: flex; flex-direction: column; gap: 8px; padding: 10px; border-radius: 12px; border: 1px solid var(--border); background: linear-gradient(180deg, rgba(255,255,255,.03), rgba(255,255,255,.01)); }
|
|
1403
|
+
.mt-item-header { display: flex; justify-content: space-between; align-items: center; font-weight: 500; }
|
|
1404
|
+
.mt-item-actions { display: inline-flex; gap: 4px; }
|
|
1405
|
+
.mt-item-actions .btn-icon { font-size: 12px; line-height: 1; padding: 4px 6px; }
|
|
1406
|
+
.mt-row { display: grid; grid-template-columns: auto 1fr; gap: 8px; align-items: center; }
|
|
1407
|
+
.mt-row-label { font-size: 12px; color: var(--muted); }
|
|
1408
|
+
.mt-row-inputs { display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap: 6px; }
|
|
1409
|
+
.mt-number { padding: 6px 8px; }
|
|
1410
|
+
.control-placeholder { padding: 8px; font-size: 12px; color: var(--muted); border: 1px dashed var(--border); border-radius: 10px; background: rgba(15,23,42,0.35); }
|
|
1411
|
+
`;
|
|
1412
|
+
return style;
|
|
1413
|
+
}
|
|
1414
|
+
}
|