brep-io-kernel 1.0.0-ci.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (271) hide show
  1. package/LICENSE.md +32 -0
  2. package/README.md +157 -0
  3. package/dist-kernel/brep-kernel.js +74699 -0
  4. package/package.json +58 -0
  5. package/src/BREP/AssemblyComponent.js +42 -0
  6. package/src/BREP/BREP.js +43 -0
  7. package/src/BREP/BetterSolid.js +805 -0
  8. package/src/BREP/Edge.js +103 -0
  9. package/src/BREP/Extrude.js +403 -0
  10. package/src/BREP/Face.js +187 -0
  11. package/src/BREP/MeshRepairer.js +634 -0
  12. package/src/BREP/OffsetShellSolid.js +614 -0
  13. package/src/BREP/PointCloudWrap.js +302 -0
  14. package/src/BREP/Revolve.js +345 -0
  15. package/src/BREP/SolidMethods/authoring.js +112 -0
  16. package/src/BREP/SolidMethods/booleanOps.js +230 -0
  17. package/src/BREP/SolidMethods/chamfer.js +122 -0
  18. package/src/BREP/SolidMethods/edgeResolution.js +25 -0
  19. package/src/BREP/SolidMethods/fillet.js +792 -0
  20. package/src/BREP/SolidMethods/index.js +72 -0
  21. package/src/BREP/SolidMethods/io.js +105 -0
  22. package/src/BREP/SolidMethods/lifecycle.js +103 -0
  23. package/src/BREP/SolidMethods/manifoldOps.js +375 -0
  24. package/src/BREP/SolidMethods/meshCleanup.js +2512 -0
  25. package/src/BREP/SolidMethods/meshQueries.js +264 -0
  26. package/src/BREP/SolidMethods/metadata.js +106 -0
  27. package/src/BREP/SolidMethods/metrics.js +51 -0
  28. package/src/BREP/SolidMethods/transforms.js +361 -0
  29. package/src/BREP/SolidMethods/visualize.js +508 -0
  30. package/src/BREP/SolidShared.js +26 -0
  31. package/src/BREP/Sweep.js +1596 -0
  32. package/src/BREP/Tube.js +857 -0
  33. package/src/BREP/Vertex.js +43 -0
  34. package/src/BREP/applyBooleanOperation.js +704 -0
  35. package/src/BREP/boundsUtils.js +48 -0
  36. package/src/BREP/chamfer.js +551 -0
  37. package/src/BREP/edgePolylineUtils.js +85 -0
  38. package/src/BREP/fillets/common.js +388 -0
  39. package/src/BREP/fillets/fillet.js +1422 -0
  40. package/src/BREP/fillets/filletGeometry.js +15 -0
  41. package/src/BREP/fillets/inset.js +389 -0
  42. package/src/BREP/fillets/offsetHelper.js +143 -0
  43. package/src/BREP/fillets/outset.js +88 -0
  44. package/src/BREP/helix.js +193 -0
  45. package/src/BREP/meshToBrep.js +234 -0
  46. package/src/BREP/primitives.js +279 -0
  47. package/src/BREP/setupManifold.js +71 -0
  48. package/src/BREP/threadGeometry.js +1120 -0
  49. package/src/BREP/triangleUtils.js +8 -0
  50. package/src/BREP/triangulate.js +608 -0
  51. package/src/FeatureRegistry.js +183 -0
  52. package/src/PartHistory.js +1132 -0
  53. package/src/UI/AccordionWidget.js +292 -0
  54. package/src/UI/CADmaterials.js +850 -0
  55. package/src/UI/EnvMonacoEditor.js +522 -0
  56. package/src/UI/FloatingWindow.js +396 -0
  57. package/src/UI/HistoryWidget.js +457 -0
  58. package/src/UI/MainToolbar.js +131 -0
  59. package/src/UI/ModelLibraryView.js +194 -0
  60. package/src/UI/OrthoCameraIdle.js +206 -0
  61. package/src/UI/PluginsWidget.js +280 -0
  62. package/src/UI/SceneListing.js +606 -0
  63. package/src/UI/SelectionFilter.js +629 -0
  64. package/src/UI/ViewCube.js +389 -0
  65. package/src/UI/assembly/AssemblyConstraintCollectionWidget.js +329 -0
  66. package/src/UI/assembly/AssemblyConstraintControlsWidget.js +282 -0
  67. package/src/UI/assembly/AssemblyConstraintsWidget.css +292 -0
  68. package/src/UI/assembly/AssemblyConstraintsWidget.js +1373 -0
  69. package/src/UI/assembly/constraintFaceUtils.js +115 -0
  70. package/src/UI/assembly/constraintHighlightUtils.js +70 -0
  71. package/src/UI/assembly/constraintLabelUtils.js +31 -0
  72. package/src/UI/assembly/constraintPointUtils.js +64 -0
  73. package/src/UI/assembly/constraintSelectionUtils.js +185 -0
  74. package/src/UI/assembly/constraintStatusUtils.js +142 -0
  75. package/src/UI/componentSelectorModal.js +240 -0
  76. package/src/UI/controls/CombinedTransformControls.js +386 -0
  77. package/src/UI/dialogs.js +351 -0
  78. package/src/UI/expressionsManager.js +100 -0
  79. package/src/UI/featureDialogWidgets/booleanField.js +25 -0
  80. package/src/UI/featureDialogWidgets/booleanOperationField.js +97 -0
  81. package/src/UI/featureDialogWidgets/buttonField.js +45 -0
  82. package/src/UI/featureDialogWidgets/componentSelectorField.js +102 -0
  83. package/src/UI/featureDialogWidgets/defaultField.js +23 -0
  84. package/src/UI/featureDialogWidgets/fileField.js +66 -0
  85. package/src/UI/featureDialogWidgets/index.js +34 -0
  86. package/src/UI/featureDialogWidgets/numberField.js +165 -0
  87. package/src/UI/featureDialogWidgets/optionsField.js +33 -0
  88. package/src/UI/featureDialogWidgets/referenceSelectionField.js +208 -0
  89. package/src/UI/featureDialogWidgets/stringField.js +24 -0
  90. package/src/UI/featureDialogWidgets/textareaField.js +28 -0
  91. package/src/UI/featureDialogWidgets/threadDesignationField.js +160 -0
  92. package/src/UI/featureDialogWidgets/transformField.js +252 -0
  93. package/src/UI/featureDialogWidgets/utils.js +43 -0
  94. package/src/UI/featureDialogWidgets/vec3Field.js +133 -0
  95. package/src/UI/featureDialogs.js +1414 -0
  96. package/src/UI/fileManagerWidget.js +615 -0
  97. package/src/UI/history/HistoryCollectionWidget.js +1294 -0
  98. package/src/UI/history/historyCollectionWidget.css.js +257 -0
  99. package/src/UI/history/historyDisplayInfo.js +133 -0
  100. package/src/UI/mobile.js +28 -0
  101. package/src/UI/objectDump.js +442 -0
  102. package/src/UI/pmi/AnnotationCollectionWidget.js +120 -0
  103. package/src/UI/pmi/AnnotationHistory.js +353 -0
  104. package/src/UI/pmi/AnnotationRegistry.js +90 -0
  105. package/src/UI/pmi/BaseAnnotation.js +269 -0
  106. package/src/UI/pmi/LabelOverlay.css +102 -0
  107. package/src/UI/pmi/LabelOverlay.js +191 -0
  108. package/src/UI/pmi/PMIMode.js +1550 -0
  109. package/src/UI/pmi/PMIViewsWidget.js +1098 -0
  110. package/src/UI/pmi/annUtils.js +729 -0
  111. package/src/UI/pmi/dimensions/AngleDimensionAnnotation.js +647 -0
  112. package/src/UI/pmi/dimensions/ExplodeBodyAnnotation.js +507 -0
  113. package/src/UI/pmi/dimensions/HoleCalloutAnnotation.js +462 -0
  114. package/src/UI/pmi/dimensions/LeaderAnnotation.js +403 -0
  115. package/src/UI/pmi/dimensions/LinearDimensionAnnotation.js +532 -0
  116. package/src/UI/pmi/dimensions/NoteAnnotation.js +110 -0
  117. package/src/UI/pmi/dimensions/RadialDimensionAnnotation.js +659 -0
  118. package/src/UI/pmi/pmiStyle.js +44 -0
  119. package/src/UI/sketcher/SketchMode3D.js +4095 -0
  120. package/src/UI/sketcher/dimensions.js +674 -0
  121. package/src/UI/sketcher/glyphs.js +236 -0
  122. package/src/UI/sketcher/highlights.js +60 -0
  123. package/src/UI/toolbarButtons/aboutButton.js +5 -0
  124. package/src/UI/toolbarButtons/exportButton.js +609 -0
  125. package/src/UI/toolbarButtons/flatPatternButton.js +307 -0
  126. package/src/UI/toolbarButtons/importButton.js +160 -0
  127. package/src/UI/toolbarButtons/inspectorToggleButton.js +12 -0
  128. package/src/UI/toolbarButtons/metadataButton.js +1063 -0
  129. package/src/UI/toolbarButtons/orientToFaceButton.js +114 -0
  130. package/src/UI/toolbarButtons/registerDefaultButtons.js +46 -0
  131. package/src/UI/toolbarButtons/saveButton.js +99 -0
  132. package/src/UI/toolbarButtons/scriptRunnerButton.js +302 -0
  133. package/src/UI/toolbarButtons/testsButton.js +26 -0
  134. package/src/UI/toolbarButtons/undoRedoButtons.js +25 -0
  135. package/src/UI/toolbarButtons/wireframeToggleButton.js +5 -0
  136. package/src/UI/toolbarButtons/zoomToFitButton.js +5 -0
  137. package/src/UI/triangleDebuggerWindow.js +945 -0
  138. package/src/UI/viewer.js +4228 -0
  139. package/src/assemblyConstraints/AssemblyConstraintHistory.js +1576 -0
  140. package/src/assemblyConstraints/AssemblyConstraintRegistry.js +120 -0
  141. package/src/assemblyConstraints/BaseAssemblyConstraint.js +66 -0
  142. package/src/assemblyConstraints/constraintExpressionUtils.js +35 -0
  143. package/src/assemblyConstraints/constraintUtils/parallelAlignment.js +676 -0
  144. package/src/assemblyConstraints/constraints/AngleConstraint.js +485 -0
  145. package/src/assemblyConstraints/constraints/CoincidentConstraint.js +194 -0
  146. package/src/assemblyConstraints/constraints/DistanceConstraint.js +616 -0
  147. package/src/assemblyConstraints/constraints/FixedConstraint.js +78 -0
  148. package/src/assemblyConstraints/constraints/ParallelConstraint.js +252 -0
  149. package/src/assemblyConstraints/constraints/TouchAlignConstraint.js +961 -0
  150. package/src/core/entities/HistoryCollectionBase.js +72 -0
  151. package/src/core/entities/ListEntityBase.js +109 -0
  152. package/src/core/entities/schemaProcesser.js +121 -0
  153. package/src/exporters/sheetMetalFlatPattern.js +659 -0
  154. package/src/exporters/sheetMetalUnfold.js +862 -0
  155. package/src/exporters/step.js +1135 -0
  156. package/src/exporters/threeMF.js +575 -0
  157. package/src/features/assemblyComponent/AssemblyComponentFeature.js +780 -0
  158. package/src/features/boolean/BooleanFeature.js +94 -0
  159. package/src/features/chamfer/ChamferFeature.js +116 -0
  160. package/src/features/datium/DatiumFeature.js +80 -0
  161. package/src/features/edgeFeatureUtils.js +41 -0
  162. package/src/features/extrude/ExtrudeFeature.js +143 -0
  163. package/src/features/fillet/FilletFeature.js +197 -0
  164. package/src/features/helix/HelixFeature.js +405 -0
  165. package/src/features/hole/HoleFeature.js +1050 -0
  166. package/src/features/hole/screwClearance.js +86 -0
  167. package/src/features/hole/threadDesignationCatalog.js +149 -0
  168. package/src/features/imageHeightSolid/ImageHeightmapSolidFeature.js +463 -0
  169. package/src/features/imageToFace/ImageToFaceFeature.js +727 -0
  170. package/src/features/imageToFace/imageEditor.js +1270 -0
  171. package/src/features/imageToFace/traceUtils.js +971 -0
  172. package/src/features/import3dModel/Import3dModelFeature.js +151 -0
  173. package/src/features/loft/LoftFeature.js +605 -0
  174. package/src/features/mirror/MirrorFeature.js +151 -0
  175. package/src/features/offsetFace/OffsetFaceFeature.js +370 -0
  176. package/src/features/offsetShell/OffsetShellFeature.js +89 -0
  177. package/src/features/overlapCleanup/OverlapCleanupFeature.js +85 -0
  178. package/src/features/pattern/PatternFeature.js +275 -0
  179. package/src/features/patternLinear/PatternLinearFeature.js +120 -0
  180. package/src/features/patternRadial/PatternRadialFeature.js +186 -0
  181. package/src/features/plane/PlaneFeature.js +154 -0
  182. package/src/features/primitiveCone/primitiveConeFeature.js +99 -0
  183. package/src/features/primitiveCube/primitiveCubeFeature.js +70 -0
  184. package/src/features/primitiveCylinder/primitiveCylinderFeature.js +91 -0
  185. package/src/features/primitivePyramid/primitivePyramidFeature.js +72 -0
  186. package/src/features/primitiveSphere/primitiveSphereFeature.js +62 -0
  187. package/src/features/primitiveTorus/primitiveTorusFeature.js +109 -0
  188. package/src/features/remesh/RemeshFeature.js +97 -0
  189. package/src/features/revolve/RevolveFeature.js +111 -0
  190. package/src/features/selectionUtils.js +118 -0
  191. package/src/features/sheetMetal/SheetMetalContourFlangeFeature.js +1656 -0
  192. package/src/features/sheetMetal/SheetMetalCutoutFeature.js +1056 -0
  193. package/src/features/sheetMetal/SheetMetalFlangeFeature.js +1568 -0
  194. package/src/features/sheetMetal/SheetMetalHemFeature.js +43 -0
  195. package/src/features/sheetMetal/SheetMetalObject.js +141 -0
  196. package/src/features/sheetMetal/SheetMetalTabFeature.js +176 -0
  197. package/src/features/sheetMetal/UNFOLD_NEUTRAL_REQUIREMENTS.md +153 -0
  198. package/src/features/sheetMetal/contour-flange-rebuild-spec.md +261 -0
  199. package/src/features/sheetMetal/profileUtils.js +25 -0
  200. package/src/features/sheetMetal/sheetMetalCleanup.js +9 -0
  201. package/src/features/sheetMetal/sheetMetalFaceTypes.js +146 -0
  202. package/src/features/sheetMetal/sheetMetalMetadata.js +165 -0
  203. package/src/features/sheetMetal/sheetMetalPipeline.js +169 -0
  204. package/src/features/sheetMetal/sheetMetalProfileUtils.js +216 -0
  205. package/src/features/sheetMetal/sheetMetalTabUtils.js +29 -0
  206. package/src/features/sheetMetal/sheetMetalTree.js +210 -0
  207. package/src/features/sketch/SketchFeature.js +955 -0
  208. package/src/features/sketch/sketchSolver2D/ConstraintEngine.js +800 -0
  209. package/src/features/sketch/sketchSolver2D/constraintDefinitions.js +704 -0
  210. package/src/features/sketch/sketchSolver2D/mathHelpersMod.js +307 -0
  211. package/src/features/spline/SplineEditorSession.js +988 -0
  212. package/src/features/spline/SplineFeature.js +1388 -0
  213. package/src/features/spline/splineUtils.js +218 -0
  214. package/src/features/sweep/SweepFeature.js +110 -0
  215. package/src/features/transform/TransformFeature.js +152 -0
  216. package/src/features/tube/TubeFeature.js +635 -0
  217. package/src/fs.proxy.js +625 -0
  218. package/src/idbStorage.js +254 -0
  219. package/src/index.js +12 -0
  220. package/src/main.js +15 -0
  221. package/src/metadataManager.js +64 -0
  222. package/src/path.proxy.js +277 -0
  223. package/src/plugins/ghLoader.worker.js +151 -0
  224. package/src/plugins/pluginManager.js +286 -0
  225. package/src/pmi/PMIViewsManager.js +134 -0
  226. package/src/services/componentLibrary.js +198 -0
  227. package/src/tests/ConsoleCapture.js +189 -0
  228. package/src/tests/S7-diagnostics-2025-12-23T18-37-23-570Z.json +630 -0
  229. package/src/tests/browserTests.js +597 -0
  230. package/src/tests/debugBoolean.js +225 -0
  231. package/src/tests/partFiles/badBoolean.json +957 -0
  232. package/src/tests/partFiles/extrudeTest.json +88 -0
  233. package/src/tests/partFiles/filletFail.json +58 -0
  234. package/src/tests/partFiles/import_TEst.part.part.json +646 -0
  235. package/src/tests/partFiles/sheetMetalHem.BREP.json +734 -0
  236. package/src/tests/test_boolean_subtract.js +27 -0
  237. package/src/tests/test_chamfer.js +17 -0
  238. package/src/tests/test_extrudeFace.js +24 -0
  239. package/src/tests/test_fillet.js +17 -0
  240. package/src/tests/test_fillet_nonClosed.js +45 -0
  241. package/src/tests/test_filletsMoreDifficult.js +46 -0
  242. package/src/tests/test_history_features_basic.js +149 -0
  243. package/src/tests/test_hole.js +282 -0
  244. package/src/tests/test_mirror.js +16 -0
  245. package/src/tests/test_offsetShellGrouping.js +85 -0
  246. package/src/tests/test_plane.js +4 -0
  247. package/src/tests/test_primitiveCone.js +11 -0
  248. package/src/tests/test_primitiveCube.js +7 -0
  249. package/src/tests/test_primitiveCylinder.js +8 -0
  250. package/src/tests/test_primitivePyramid.js +9 -0
  251. package/src/tests/test_primitiveSphere.js +17 -0
  252. package/src/tests/test_primitiveTorus.js +21 -0
  253. package/src/tests/test_pushFace.js +126 -0
  254. package/src/tests/test_sheetMetalContourFlange.js +125 -0
  255. package/src/tests/test_sheetMetal_features.js +80 -0
  256. package/src/tests/test_sketch_openLoop.js +45 -0
  257. package/src/tests/test_solidMetrics.js +58 -0
  258. package/src/tests/test_stlLoader.js +1889 -0
  259. package/src/tests/test_sweepFace.js +55 -0
  260. package/src/tests/test_tube.js +45 -0
  261. package/src/tests/test_tube_closedLoop.js +67 -0
  262. package/src/tests/tests.js +493 -0
  263. package/src/tools/assemblyConstraintDialogCapturePage.js +56 -0
  264. package/src/tools/dialogCapturePageFactory.js +227 -0
  265. package/src/tools/featureDialogCapturePage.js +47 -0
  266. package/src/tools/pmiAnnotationDialogCapturePage.js +60 -0
  267. package/src/utils/axisHelpers.js +99 -0
  268. package/src/utils/deepClone.js +69 -0
  269. package/src/utils/geometryTolerance.js +37 -0
  270. package/src/utils/normalizeTypeString.js +8 -0
  271. package/src/utils/xformMath.js +51 -0
@@ -0,0 +1,252 @@
1
+ import * as THREE from 'three';
2
+ import { BaseAssemblyConstraint } from '../BaseAssemblyConstraint.js';
3
+ import { solveParallelAlignment, resolveParallelSelection } from '../constraintUtils/parallelAlignment.js';
4
+
5
+ const inputParamsSchema = {
6
+ id: {
7
+ type: 'string',
8
+ default_value: null,
9
+ hint: 'Unique identifier for the constraint.',
10
+ },
11
+ elements: {
12
+ type: 'reference_selection',
13
+ label: 'Elements',
14
+ hint: 'Select two faces, edges, or components.',
15
+ selectionFilter: ['FACE', 'EDGE', 'COMPONENT'],
16
+ multiple: true,
17
+ minSelections: 2,
18
+ maxSelections: 2,
19
+ },
20
+ applyImmediately: {
21
+ type: 'boolean',
22
+ label: 'Apply Immediately',
23
+ default_value: false,
24
+ hint: 'Maintained for compatibility; solver applies adjustments iteratively.',
25
+ },
26
+ reverse: {
27
+ type: 'boolean',
28
+ label: 'Reverse',
29
+ default_value: false,
30
+ hint: 'Flip the stored orientation preference.',
31
+ },
32
+ };
33
+
34
+ export class ParallelConstraint extends BaseAssemblyConstraint {
35
+ static shortName = '∥';
36
+ static longName = '∥ Parallel Constraint';
37
+ static constraintType = 'parallel';
38
+ static aliases = ['parallel', 'parallel_faces', 'face_parallel', 'PARA'];
39
+ static inputParamsSchema = inputParamsSchema;
40
+
41
+ constructor(partHistory) {
42
+ super(partHistory);
43
+ this._debugHelpers = [];
44
+ }
45
+
46
+ clearDebugArrows(context = {}) {
47
+ const scene = context.scene || null;
48
+ if (!scene) return;
49
+ this.#clearNormalDebug(scene);
50
+ }
51
+
52
+ async solve(context = {}) {
53
+ const pd = this.persistentData = this.persistentData || {};
54
+ const [selA, selB] = selectionPair(this.inputParams);
55
+
56
+ if ((context.iteration ?? 0) === 0) {
57
+ this.#clearNormalDebug(context.scene || null);
58
+ }
59
+
60
+ if (!selA || !selB) {
61
+ pd.status = 'incomplete';
62
+ pd.message = 'Select two references to define the constraint.';
63
+ pd.satisfied = false;
64
+ return { ok: false, status: 'incomplete', satisfied: false, applied: false, message: pd.message };
65
+ }
66
+
67
+ const opposeNormals = this.#effectiveOppose(context, selA, selB);
68
+
69
+ const result = solveParallelAlignment({
70
+ constraint: this,
71
+ context,
72
+ selectionA: selA,
73
+ selectionB: selB,
74
+ opposeNormals,
75
+ selectionLabelA: 'elements[0]',
76
+ selectionLabelB: 'elements[1]',
77
+ });
78
+
79
+ const infoA = result.infoA || null;
80
+ const infoB = result.infoB || null;
81
+
82
+ if (context.debugMode && infoA && infoB) {
83
+ this.#updateNormalDebug(context, infoA, infoB);
84
+ }
85
+
86
+ if (context.debugMode && infoA && infoB) {
87
+ const dirA = infoA.direction;
88
+ const dirB = infoB.direction;
89
+ const angle = typeof result.angle === 'number' ? result.angle : null;
90
+ const angleDeg = typeof result.angleDeg === 'number'
91
+ ? result.angleDeg
92
+ : (typeof angle === 'number' ? THREE.MathUtils.radToDeg(angle) : null);
93
+
94
+ console.log('[ParallelConstraint] directions', {
95
+ id: this.inputParams?.id ?? this.inputParams?.constraintID ?? null,
96
+ selectionA: {
97
+ normal: dirA?.toArray?.() || null,
98
+ point: infoA.origin?.toArray?.() || null,
99
+ source: infoA.directionSource,
100
+ kind: infoA.kind || null,
101
+ },
102
+ selectionB: {
103
+ normal: dirB?.toArray?.() || null,
104
+ point: infoB.origin?.toArray?.() || null,
105
+ source: infoB.directionSource,
106
+ kind: infoB.kind || null,
107
+ },
108
+ angleRad: angle,
109
+ angleDeg,
110
+ });
111
+ }
112
+
113
+ pd.status = result.status;
114
+ pd.message = result.message || '';
115
+ pd.satisfied = !!result.satisfied;
116
+
117
+ if (typeof result.angle === 'number') {
118
+ pd.error = result.angle;
119
+ pd.errorDeg = typeof result.angleDeg === 'number'
120
+ ? result.angleDeg
121
+ : THREE.MathUtils.radToDeg(result.angle);
122
+ } else {
123
+ pd.error = null;
124
+ pd.errorDeg = null;
125
+ }
126
+
127
+ if (Array.isArray(result.rotations) && result.rotations.length) {
128
+ pd.lastAppliedRotations = result.rotations;
129
+ } else if (pd.lastAppliedRotations && (!result.rotations || result.rotations.length === 0)) {
130
+ pd.lastAppliedRotations = [];
131
+ }
132
+
133
+ if (result.exception) {
134
+ pd.exception = result.exception;
135
+ } else if (pd.exception) {
136
+ delete pd.exception;
137
+ }
138
+
139
+ return result;
140
+ }
141
+
142
+ async run(context = {}) {
143
+ return this.solve(context);
144
+ }
145
+
146
+ #effectiveOppose(context, selectionA, selectionB) {
147
+ const base = this.#preferredOppose(context, selectionA, selectionB);
148
+ const reverseToggle = !!this.inputParams.reverse;
149
+ return reverseToggle ? !base : base;
150
+ }
151
+
152
+ #preferredOppose(context, selectionA, selectionB) {
153
+ const pd = this.persistentData = this.persistentData || {};
154
+ if (typeof pd.preferredOppose !== 'boolean') {
155
+ const infoA = resolveParallelSelection(this, context, selectionA, 'elements[0]');
156
+ const infoB = resolveParallelSelection(this, context, selectionB, 'elements[1]');
157
+ const dirA = infoA?.direction?.clone()?.normalize();
158
+ const dirB = infoB?.direction?.clone()?.normalize();
159
+ if (!dirA || !dirB || dirA.lengthSq() === 0 || dirB.lengthSq() === 0) {
160
+ throw new Error('ParallelConstraint: Unable to resolve directions for orientation preference.');
161
+ }
162
+ const dot = THREE.MathUtils.clamp(dirA.dot(dirB), -1, 1);
163
+ pd.preferredOppose = dot < 0;
164
+ pd.lastOrientationDot = dot;
165
+ }
166
+ pd.isNewConstraint = false;
167
+ return !!pd.preferredOppose;
168
+ }
169
+
170
+ #updateNormalDebug(context, infoA, infoB) {
171
+ if (!context?.debugMode) return;
172
+ const scene = context.scene || null;
173
+ if (!scene) return;
174
+
175
+ const iteration = context.iteration ?? 0;
176
+ const entries = [
177
+ { info: infoA, color: 0xff4d4d, label: 'A' },
178
+ { info: infoB, color: 0x4dff91, label: 'B' },
179
+ ];
180
+
181
+ const constraintId = this.inputParams?.id ?? this.inputParams?.constraintID ?? 'unknown';
182
+ for (const { info, color, label } of entries) {
183
+ if (!info?.direction || !info.origin) continue;
184
+ const dir = info.direction.clone().normalize();
185
+ if (dir.lengthSq() === 0) continue;
186
+
187
+ const origin = info.origin.clone();
188
+ const length = Math.max(this.#estimateHelperLength(info), 10);
189
+ const arrow = new THREE.ArrowHelper(dir, origin, length, color, length * 0.25, length * 0.15);
190
+ arrow.name = `parallel-constraint-normal-${constraintId}-${label}-iter${iteration}`;
191
+ scene.add(arrow);
192
+ this._debugHelpers.push(arrow);
193
+ }
194
+ }
195
+
196
+ #clearNormalDebug(scene) {
197
+ if (!this._debugHelpers) return;
198
+ for (const helper of this._debugHelpers) {
199
+ if (!helper) continue;
200
+ if (scene && helper.parent === scene) {
201
+ scene.remove(helper);
202
+ } else if (helper.parent) {
203
+ helper.parent.remove(helper);
204
+ }
205
+ }
206
+ this._debugHelpers.length = 0;
207
+ }
208
+
209
+ #estimateHelperLength(info) {
210
+ const candidates = [];
211
+ const pushBound = (obj) => {
212
+ if (!obj) return;
213
+ if (obj.geometry?.computeBoundingSphere && !obj.geometry.boundingSphere) {
214
+ try { obj.geometry.computeBoundingSphere(); } catch {}
215
+ }
216
+ const sphere = obj.geometry?.boundingSphere;
217
+ if (sphere?.radius) candidates.push(Math.abs(sphere.radius));
218
+ if (obj.geometry?.computeBoundingBox && !obj.geometry.boundingBox) {
219
+ try { obj.geometry.computeBoundingBox(); } catch {}
220
+ }
221
+ const box = obj.geometry?.boundingBox;
222
+ if (box) candidates.push(box.getSize(new THREE.Vector3()).length() / 2);
223
+ if (typeof obj.getWorldScale === 'function') {
224
+ const scale = obj.getWorldScale(new THREE.Vector3());
225
+ candidates.push(scale.length() * 5);
226
+ }
227
+ };
228
+
229
+ pushBound(info.object);
230
+ if (Array.isArray(info.component?.children)) {
231
+ for (const child of info.component.children) {
232
+ pushBound(child);
233
+ }
234
+ }
235
+
236
+ candidates.push(info.component?.userData?.boundingRadius || 0);
237
+
238
+ const max = candidates.reduce((acc, val) => (Number.isFinite(val) ? Math.max(acc, val) : acc), 0);
239
+ return Number.isFinite(max) && max > 0 ? max : 0;
240
+ }
241
+ }
242
+
243
+
244
+ function selectionPair(params) {
245
+ if (!params || typeof params !== 'object') return [null, null];
246
+ const raw = Array.isArray(params.elements) ? params.elements : [];
247
+ const picks = raw.filter((item) => item != null).slice(0, 2);
248
+ params.elements = picks;
249
+ if (picks.length === 2) return picks;
250
+ if (picks.length === 1) return [picks[0], null];
251
+ return [null, null];
252
+ }