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,462 @@
1
+ import * as THREE from 'three';
2
+ import { BaseAnnotation } from '../BaseAnnotation.js';
3
+ import {
4
+ makeOverlayLine,
5
+ objectRepresentativePoint,
6
+ screenSizeWorld,
7
+ addArrowCone,
8
+ } from '../annUtils.js';
9
+ import { getPMIStyle } from '../pmiStyle.js';
10
+
11
+ const inputParamsSchema = {
12
+ id: {
13
+ type: 'string',
14
+ default_value: null,
15
+ label: 'ID',
16
+ hint: 'unique identifier for the hole callout',
17
+ },
18
+ target: {
19
+ type: 'reference_selection',
20
+ selectionFilter: ['VERTEX', 'EDGE', 'FACE'],
21
+ multiple: false,
22
+ default_value: '',
23
+ label: 'Target',
24
+ hint: 'Pick the hole edge/vertex/face to call out',
25
+ },
26
+ quantity: {
27
+ type: 'number',
28
+ default_value: 0,
29
+ label: 'Quantity',
30
+ hint: 'Number of identical holes this callout represents (0 = auto from feature)',
31
+ min: 0,
32
+ },
33
+ showQuantity: {
34
+ type: 'boolean',
35
+ default_value: true,
36
+ label: 'Show Quantity',
37
+ hint: 'Include the hole count in the callout label',
38
+ },
39
+ beforeText: {
40
+ type: 'string',
41
+ default_value: '',
42
+ label: 'Text Before',
43
+ hint: 'Optional text to show before the callout',
44
+ },
45
+ afterText: {
46
+ type: 'string',
47
+ default_value: '',
48
+ label: 'Text After',
49
+ hint: 'Optional text to show after the callout',
50
+ },
51
+ anchorPosition: {
52
+ type: 'options',
53
+ default_value: 'Right Top',
54
+ options: [
55
+ 'Left Top',
56
+ 'Left Middle',
57
+ 'Left Bottom',
58
+ 'Right Top',
59
+ 'Right Middle',
60
+ 'Right Bottom',
61
+ ],
62
+ label: 'Anchor Position',
63
+ hint: 'Preferred label alignment relative to anchor',
64
+ },
65
+ };
66
+
67
+ export class HoleCalloutAnnotation extends BaseAnnotation {
68
+ static entityType = 'holeCallout';
69
+ static type = 'holeCallout';
70
+ static shortName = 'HOLE';
71
+ static longName = 'Hole Callout';
72
+ static title = 'Hole Callout';
73
+ static inputParamsSchema = inputParamsSchema;
74
+
75
+ constructor(opts = {}) {
76
+ super(opts);
77
+ }
78
+
79
+ async run(renderingContext) {
80
+ this.renderingContext = renderingContext;
81
+ const { pmimode, group, idx, ctx } = renderingContext;
82
+ const ann = this.inputParams || {};
83
+
84
+ const viewer = pmimode?.viewer;
85
+ const scene = viewer?.partHistory?.scene;
86
+ const targetObj = resolveTargetObject(viewer, ann.target);
87
+ const objPoint = objectRepresentativePoint(scene, targetObj);
88
+ const descriptor = findHoleDescriptor(viewer?.partHistory, targetObj, objPoint, ann.target);
89
+ const targetPoint = descriptor?.center ? arrToVec(descriptor.center) : objPoint;
90
+ if (!targetPoint) return [];
91
+
92
+ const qty = resolveHoleQuantity(ann, descriptor, viewer?.partHistory);
93
+ const labelText = descriptor ? formatHoleCallout(descriptor, qty, {
94
+ showQuantity: ann.showQuantity !== false,
95
+ beforeText: ann.beforeText,
96
+ afterText: ann.afterText,
97
+ }) : '';
98
+ ann.value = labelText;
99
+
100
+ const basis = computeViewBasis(viewer);
101
+ const offset = ctx?.screenSizeWorld ? ctx.screenSizeWorld(80) : screenSizeWorld(viewer, 80);
102
+ const upOffset = ctx?.screenSizeWorld ? ctx.screenSizeWorld(30) : screenSizeWorld(viewer, 30);
103
+ const saved = arrToVec(ann?.persistentData?.labelWorld);
104
+ const labelPos = saved || targetPoint.clone()
105
+ .addScaledVector(basis.right, anchorSign(ann.anchorPosition || 'Right Top') * offset)
106
+ .addScaledVector(basis.up, anchorVertical(ann.anchorPosition || 'Right Top') * upOffset);
107
+
108
+ if (ctx?.updateLabel) {
109
+ ctx.updateLabel(idx, labelText, labelPos, ann);
110
+ }
111
+
112
+ const style = getPMIStyle();
113
+ const color = style.lineColor ?? 0xffea00;
114
+ group.add(makeOverlayLine(labelPos, targetPoint, color));
115
+ const arrowLenPx = style.arrowLengthPx ?? 12;
116
+ const arrowWidthPx = style.arrowWidthPx ?? 4;
117
+ const arrowLength = ctx?.screenSizeWorld ? ctx.screenSizeWorld(arrowLenPx) : screenSizeWorld(viewer, arrowLenPx);
118
+ const arrowWidth = ctx?.screenSizeWorld ? ctx.screenSizeWorld(arrowWidthPx) : screenSizeWorld(viewer, arrowWidthPx);
119
+ const dir = targetPoint.clone().sub(labelPos);
120
+ if (dir.lengthSq() > 1e-12) {
121
+ dir.normalize();
122
+ addArrowCone(group, targetPoint, dir, arrowLength, arrowWidth, style.arrowColor ?? color);
123
+ }
124
+
125
+ return [];
126
+ }
127
+
128
+ static applyParams(pmimode, ann, params) {
129
+ super.applyParams(pmimode, ann, params);
130
+ const qty = Number(ann?.quantity);
131
+ ann.quantity = Number.isFinite(qty) ? Math.max(0, Math.round(qty)) : 1;
132
+ ann.showQuantity = ann.showQuantity !== false;
133
+ ann.beforeText = normalizeAddonText(ann.beforeText);
134
+ ann.afterText = normalizeAddonText(ann.afterText);
135
+ ann.anchorPosition = normalizeAnchorPosition(ann.anchorPosition);
136
+ return { paramsPatch: {} };
137
+ }
138
+
139
+ static onLabelPointerDown(pmimode, idx, ann, e, ctx) {
140
+ const viewer = pmimode?.viewer;
141
+ const targetObj = resolveTargetObject(viewer, ann.target);
142
+ const scene = viewer?.partHistory?.scene;
143
+ const targetPoint = objectRepresentativePoint(scene, targetObj) || arrToVec(ann?.persistentData?.labelWorld);
144
+ if (!targetPoint) return;
145
+ const basis = computeViewBasis(viewer);
146
+ const normal = basis.forward;
147
+ if (!ctx?.raycastFromEvent) return;
148
+ const plane = new THREE.Plane().setFromNormalAndCoplanarPoint(normal, targetPoint);
149
+ HoleCalloutAnnotation.dragLabelOnPlane(pmimode, ctx, {
150
+ makePlane: () => plane,
151
+ suspendControls: true,
152
+ onDrag: (hit) => {
153
+ ensurePersistentData(ann);
154
+ ann.persistentData.labelWorld = [hit.x, hit.y, hit.z];
155
+ ctx.updateLabel(idx, null, hit, ann);
156
+ pmimode?.refreshAnnotationsUI?.();
157
+ },
158
+ });
159
+ }
160
+ }
161
+
162
+ function resolveTargetObject(viewer, target) {
163
+ if (!viewer || !target) return null;
164
+ const scene = viewer.partHistory?.scene;
165
+ if (!scene) return null;
166
+ if (typeof target === 'string') {
167
+ const direct = scene.getObjectByName?.(target);
168
+ if (direct) return direct;
169
+ const tokens = normalizeTargetTokens(target);
170
+ for (const t of tokens) {
171
+ const byToken = scene.getObjectByName?.(t);
172
+ if (byToken) return byToken;
173
+ }
174
+ let fuzzy = null;
175
+ try {
176
+ scene.traverse((child) => {
177
+ if (fuzzy) return;
178
+ const name = child?.name ? String(child.name) : '';
179
+ if (!name) return;
180
+ for (const t of tokens) {
181
+ if (!t) continue;
182
+ if (name === t || name.endsWith(t) || name.includes(t)) {
183
+ fuzzy = child;
184
+ return;
185
+ }
186
+ }
187
+ });
188
+ } catch { /* ignore */ }
189
+ if (fuzzy) return fuzzy;
190
+ }
191
+ if (typeof target === 'object') return target;
192
+ return scene.getObjectByName?.(String(target)) || null;
193
+ }
194
+
195
+ function findHoleDescriptor(partHistory, targetObj, fallbackPoint, targetName = null) {
196
+ const features = Array.isArray(partHistory?.features) ? partHistory.features : [];
197
+ const descriptors = [];
198
+ for (const f of features) {
199
+ const holes = Array.isArray(f?.persistentData?.holes) ? f.persistentData.holes : [];
200
+ for (const h of holes) {
201
+ descriptors.push(h);
202
+ }
203
+ }
204
+ if (!descriptors.length) return null;
205
+
206
+ const metaHole = targetObj?.userData?.hole || targetObj?.userData?.metadata?.hole;
207
+ if (metaHole) return metaHole;
208
+ if (targetObj?.parent) {
209
+ const parentHole = targetObj.parent.userData?.hole || targetObj.parent.userData?.metadata?.hole;
210
+ if (parentHole) return parentHole;
211
+ }
212
+
213
+ const targetId = targetObj?.uuid || targetObj?.id || targetObj?.name || null;
214
+ const tokens = normalizeTargetTokens(targetName, targetId);
215
+ if (targetObj?.name) {
216
+ for (const t of normalizeTargetTokens(targetObj.name)) tokens.add(t);
217
+ }
218
+
219
+ const directMatches = descriptors.filter((d) => {
220
+ const sn = d?.sourceName ? String(d.sourceName) : null;
221
+ const fid = d?.featureId ? String(d.featureId) : null;
222
+ const tid = d?.targetId ? String(d.targetId) : null;
223
+ return (sn && tokens.has(sn)) || (fid && tokens.has(fid)) || (tid && tokens.has(tid));
224
+ });
225
+ if (directMatches.length === 1) return directMatches[0];
226
+ if (directMatches.length > 1 && fallbackPoint) {
227
+ const best = nearestDescriptor(directMatches, fallbackPoint);
228
+ if (best) return best;
229
+ }
230
+
231
+ if (targetId) {
232
+ const byTarget = descriptors.filter((d) => d?.targetId && String(d.targetId) === String(targetId));
233
+ if (byTarget.length === 1) return byTarget[0];
234
+ if (byTarget.length > 1 && fallbackPoint) {
235
+ const best = nearestDescriptor(byTarget, fallbackPoint);
236
+ if (best) return best;
237
+ }
238
+ }
239
+
240
+ if (targetName) {
241
+ const t = String(targetName);
242
+ const softList = descriptors.filter((d) => {
243
+ const fid = d?.featureId ? String(d.featureId) : '';
244
+ const source = d?.sourceName ? String(d.sourceName) : '';
245
+ return (fid && t.includes(fid)) || (source && t.includes(source));
246
+ });
247
+ if (softList.length === 1) return softList[0];
248
+ if (softList.length > 1 && fallbackPoint) {
249
+ const best = nearestDescriptor(softList, fallbackPoint);
250
+ if (best) return best;
251
+ }
252
+ }
253
+
254
+ if (tokens.size) {
255
+ const byTokenList = descriptors.filter((d) => {
256
+ const fid = d?.featureId ? String(d.featureId) : '';
257
+ const source = d?.sourceName ? String(d.sourceName) : '';
258
+ const tid = d?.targetId ? String(d.targetId) : '';
259
+ return tokens.has(fid) || tokens.has(source) || tokens.has(tid);
260
+ });
261
+ if (byTokenList.length === 1) return byTokenList[0];
262
+ if (byTokenList.length > 1 && fallbackPoint) {
263
+ const best = nearestDescriptor(byTokenList, fallbackPoint);
264
+ if (best) return best;
265
+ }
266
+ }
267
+
268
+ if (fallbackPoint) {
269
+ const best = nearestDescriptor(descriptors, fallbackPoint);
270
+ if (best) return best;
271
+ }
272
+
273
+ if (targetObj?.parent) {
274
+ const parentTokens = normalizeTargetTokens(targetObj.parent.name);
275
+ const byParentList = descriptors.filter((d) => {
276
+ const fid = d?.featureId ? String(d.featureId) : '';
277
+ const source = d?.sourceName ? String(d.sourceName) : '';
278
+ const tid = d?.targetId ? String(d.targetId) : '';
279
+ return parentTokens.has(fid) || parentTokens.has(source) || parentTokens.has(tid);
280
+ });
281
+ if (byParentList.length === 1) return byParentList[0];
282
+ if (byParentList.length > 1 && fallbackPoint) {
283
+ const best = nearestDescriptor(byParentList, fallbackPoint);
284
+ if (best) return best;
285
+ }
286
+ }
287
+
288
+ if (fallbackPoint) {
289
+ const best = nearestDescriptor(descriptors, fallbackPoint);
290
+ if (best) return best;
291
+ }
292
+
293
+ return descriptors[0];
294
+ }
295
+
296
+
297
+ function formatHoleCallout(desc, quantity = 1, options = {}) {
298
+ if (!desc) return '';
299
+ const lines = [];
300
+ const before = normalizeAddonText(options.beforeText);
301
+ if (before) lines.push(before);
302
+ const includeQuantity = options.showQuantity !== false;
303
+ const prefix = (includeQuantity && quantity > 1) ? `${quantity}× ` : '';
304
+ const depthValue = Number(desc.totalDepth ?? desc.straightDepth);
305
+ const depthStr = (!desc.throughAll && depthValue > 0)
306
+ ? ` ↧ ${fmt(depthValue)}`
307
+ : (desc.throughAll ? ' THRU ALL' : '');
308
+
309
+ lines.push(`${prefix}⌀${fmt(desc.diameter)}${depthStr}`);
310
+
311
+ if (desc.type === 'COUNTERSINK') {
312
+ lines.push(`⌵ ⌀${fmt(desc.countersinkDiameter)} × ${fmt(desc.countersinkAngle, 0)}°`);
313
+ } else if (desc.type === 'COUNTERBORE') {
314
+ lines.push(`⌴ ⌀${fmt(desc.counterboreDiameter)} ↧ ${fmt(desc.counterboreDepth)}`);
315
+ }
316
+ const threadLine = formatThreadLine(desc?.thread);
317
+ if (threadLine) lines.push(threadLine);
318
+ const after = normalizeAddonText(options.afterText);
319
+ if (after) lines.push(after);
320
+ return lines.join('\n');
321
+ }
322
+
323
+ function computeViewBasis(viewer) {
324
+ const cam = viewer?.camera;
325
+ const forward = new THREE.Vector3();
326
+ if (cam?.getWorldDirection) cam.getWorldDirection(forward);
327
+ else forward.set(0, 0, -1);
328
+ forward.normalize();
329
+ const up = cam?.up ? cam.up.clone() : new THREE.Vector3(0, 1, 0);
330
+ const right = new THREE.Vector3().crossVectors(forward, up).normalize();
331
+ const realUp = new THREE.Vector3().crossVectors(right, forward).normalize();
332
+ return { forward, right, up: realUp };
333
+ }
334
+
335
+ function anchorSign(anchor) {
336
+ return String(anchor || '').startsWith('Left') ? -1 : 1;
337
+ }
338
+
339
+ function anchorVertical(anchor) {
340
+ if (!anchor) return 1;
341
+ if (anchor.includes('Bottom')) return -1;
342
+ if (anchor.includes('Middle')) return 0;
343
+ return 1;
344
+ }
345
+
346
+ function normalizeAnchorPosition(value) {
347
+ const opts = new Set([
348
+ 'Left Top',
349
+ 'Left Middle',
350
+ 'Left Bottom',
351
+ 'Right Top',
352
+ 'Right Middle',
353
+ 'Right Bottom',
354
+ ]);
355
+ const val = opts.has(value) ? value : 'Right Top';
356
+ return val;
357
+ }
358
+
359
+ function fmt(v, digits = 2) {
360
+ const n = Number(v);
361
+ if (!Number.isFinite(n)) return '';
362
+ return n.toFixed(digits);
363
+ }
364
+
365
+ function formatThreadLine(thread) {
366
+ if (!thread || !thread.designation) return '';
367
+ let designation = String(thread.designation).replace(/\s+/g, '').toUpperCase();
368
+ const series = thread.series ? String(thread.series).toUpperCase() : '';
369
+ if (series && !designation.includes(series)) {
370
+ designation += series;
371
+ }
372
+ return `THREAD ${designation}`;
373
+ }
374
+
375
+ function arrToVec(arr) {
376
+ if (!Array.isArray(arr) || arr.length < 3) return null;
377
+ const [x, y, z] = arr;
378
+ if (![x, y, z].every((v) => Number.isFinite(Number(v)))) return null;
379
+ return new THREE.Vector3(Number(x), Number(y), Number(z));
380
+ }
381
+
382
+ function ensurePersistentData(ann) {
383
+ if (!ann.persistentData || typeof ann.persistentData !== 'object') {
384
+ ann.persistentData = {};
385
+ }
386
+ }
387
+
388
+ function normalizeAddonText(value) {
389
+ if (value == null) return '';
390
+ const str = String(value).trim();
391
+ return str.length ? str : '';
392
+ }
393
+
394
+ function normalizeTargetTokens(rawName, fallbackName = null) {
395
+ const set = new Set();
396
+ const push = (v) => {
397
+ if (v == null) return;
398
+ const s = String(v);
399
+ if (!s) return;
400
+ set.add(s);
401
+ const bracket = s.match(/\[(.*?)\]/);
402
+ if (bracket && bracket[1]) {
403
+ const inner = bracket[1];
404
+ set.add(inner);
405
+ const axisStripped = inner.replace(/_AXIS_\d+$/, '');
406
+ if (axisStripped && axisStripped !== inner) set.add(axisStripped);
407
+ }
408
+ const axisStrip = s.replace(/_AXIS_\d+$/, '');
409
+ if (axisStrip && axisStrip !== s) set.add(axisStrip);
410
+ };
411
+ push(rawName);
412
+ push(fallbackName);
413
+ return set;
414
+ }
415
+
416
+ function nearestDescriptor(list, point) {
417
+ if (!Array.isArray(list) || !list.length || !point) return null;
418
+ let best = null;
419
+ let bestD2 = Infinity;
420
+ for (const d of list) {
421
+ const c = arrToVec(d?.center);
422
+ if (!c) continue;
423
+ const d2 = c.distanceToSquared(point);
424
+ if (d2 < bestD2) {
425
+ bestD2 = d2;
426
+ best = d;
427
+ }
428
+ }
429
+ return best;
430
+ }
431
+
432
+ function resolveHoleQuantity(ann, descriptor, partHistory) {
433
+ const raw = Number(ann?.quantity);
434
+ const requested = Number.isFinite(raw) ? Math.round(raw) : 1;
435
+ if (requested > 0) return requested;
436
+ const metaQty = countFeatureHoles(partHistory, descriptor);
437
+ if (Number.isFinite(metaQty) && metaQty > 0) return metaQty;
438
+ return 1;
439
+ }
440
+
441
+ function countFeatureHoles(partHistory, descriptor) {
442
+ if (!partHistory || !descriptor) return null;
443
+ const features = Array.isArray(partHistory.features) ? partHistory.features : [];
444
+ const featureId = descriptor.featureId || null;
445
+ let fallbackCount = null;
446
+ for (const f of features) {
447
+ const holes = Array.isArray(f?.persistentData?.holes) ? f.persistentData.holes : [];
448
+ if (!holes.length) continue;
449
+ if (holes.includes(descriptor)) return holes.length;
450
+ const fid = resolveFeatureId(f);
451
+ if (featureId && fid && String(fid) === String(featureId)) {
452
+ fallbackCount = holes.length;
453
+ }
454
+ }
455
+ return fallbackCount;
456
+ }
457
+
458
+ function resolveFeatureId(feature) {
459
+ if (!feature || typeof feature !== 'object') return null;
460
+ const params = feature.inputParams || {};
461
+ return feature.id ?? params.id ?? params.featureID ?? null;
462
+ }