brep-io-kernel 1.0.0-ci.9

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 +154 -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,403 @@
1
+ import * as THREE from 'three';
2
+ import { BaseAnnotation } from '../BaseAnnotation.js';
3
+ import { getPMIStyle } from '../pmiStyle.js';
4
+ import {
5
+ addArrowCone,
6
+ makeOverlayLine,
7
+ makeOverlaySphere,
8
+ objectRepresentativePoint,
9
+ screenSizeWorld,
10
+ } from '../annUtils.js';
11
+
12
+ const inputParamsSchema = {
13
+ id: {
14
+ type: 'string',
15
+ default_value: null,
16
+ label: 'ID',
17
+ hint: 'unique identifier for the leader',
18
+ },
19
+ target: {
20
+ type: 'reference_selection',
21
+ selectionFilter: ['VERTEX'],
22
+ multiple: true,
23
+ default_value: '',
24
+ label: 'Target Point',
25
+ hint: 'Select target point',
26
+ },
27
+ text: {
28
+ type: 'textarea',
29
+ default_value: 'TEXT HERE',
30
+ defaultResolver: ({ pmimode }) => {
31
+ const txt = pmimode?._opts?.leaderText;
32
+ return (typeof txt === 'string' && txt.length) ? txt : undefined;
33
+ },
34
+ label: 'Text',
35
+ hint: 'Leader text content',
36
+ rows: 3,
37
+ },
38
+ anchorPosition: {
39
+ type: 'options',
40
+ default_value: 'Right Middle',
41
+ options: [
42
+ 'Left Top',
43
+ 'Left Middle',
44
+ 'Left Bottom',
45
+ 'Right Top',
46
+ 'Right Middle',
47
+ 'Right Bottom',
48
+ ],
49
+ label: 'Anchor Position',
50
+ hint: 'Preferred label alignment relative to anchor',
51
+ },
52
+ endStyle: {
53
+ type: 'options',
54
+ default_value: 'arrow',
55
+ options: ['arrow', 'dot'],
56
+ label: 'Leader End',
57
+ hint: 'Choose arrowhead or dot for end marker',
58
+ },
59
+ };
60
+
61
+ export class LeaderAnnotation extends BaseAnnotation {
62
+ static entityType = 'leader';
63
+ static type = 'leader';
64
+ static shortName = 'LEAD';
65
+ static longName = 'Leader';
66
+ static title = 'Leader';
67
+ static inputParamsSchema = inputParamsSchema;
68
+
69
+ constructor(opts = {}) {
70
+ super(opts);
71
+ }
72
+
73
+ async run(renderingContext) {
74
+ this.renderingContext = renderingContext;
75
+ const { pmimode, group, idx, ctx } = renderingContext;
76
+ const ann = this.inputParams || {};
77
+ const style = getPMIStyle();
78
+ ensurePersistentData(ann);
79
+ ann.anchorPosition = normalizeAnchorPosition(ann.anchorPosition ?? ann.anchorSide);
80
+ delete ann.anchorSide;
81
+
82
+ const viewer = pmimode?.viewer;
83
+ const scene = viewer?.partHistory?.scene;
84
+
85
+ const targets = resolveTargetPoints(viewer, scene, ann);
86
+ const labelPos = resolveLabelPosition(pmimode, ann, targets, ctx);
87
+ const displayText = ctx?.formatReferenceLabel
88
+ ? ctx.formatReferenceLabel(ann, sanitizeText(ann.text))
89
+ : sanitizeText(ann.text);
90
+
91
+ ann.value = displayText;
92
+
93
+ if (labelPos) {
94
+ ctx?.updateLabel?.(idx, displayText, labelPos, ann);
95
+ }
96
+
97
+ if (!targets.length || !labelPos) {
98
+ return [];
99
+ }
100
+
101
+ const color = style.lineColor ?? 0x93c5fd;
102
+ const basis = computeViewBasis(pmimode, viewer, ann);
103
+ const originPoint = averageTargets(targets) || labelPos;
104
+ const shoulderDir = computeShoulderDirection(labelPos, originPoint, basis);
105
+ const approachSpacing = Math.max(ctx?.screenSizeWorld ? ctx.screenSizeWorld(18) : screenSizeWorld(viewer, 18), 1e-4);
106
+ const shoulderLength = Math.max(
107
+ ctx?.screenSizeWorld ? ctx.screenSizeWorld(36) : screenSizeWorld(viewer, 36),
108
+ 1e-4,
109
+ );
110
+ const sortedTargets = sortTargetsByViewUp(targets, basis, labelPos);
111
+
112
+ const halfCount = (sortedTargets.length - 1) * 0.5;
113
+ sortedTargets.forEach(({ point, order }) => {
114
+ const verticalOffset = (order - halfCount) * approachSpacing;
115
+ const approachPoint = labelPos.clone()
116
+ .addScaledVector(shoulderDir, -shoulderLength)
117
+ .addScaledVector(basis.up, verticalOffset);
118
+
119
+ group.add(makeOverlayLine(point, approachPoint, color));
120
+ group.add(makeOverlayLine(approachPoint, labelPos, color));
121
+
122
+ if (ann.endStyle === 'dot') {
123
+ const dotPx = style.leaderDotRadiusPx ?? 6;
124
+ const dotRadius = ctx?.screenSizeWorld ? ctx.screenSizeWorld(dotPx) : screenSizeWorld(viewer, dotPx);
125
+ const dot = makeOverlaySphere(Math.max(dotRadius, 1e-4), style.dotColor ?? color);
126
+ dot.position.copy(point);
127
+ group.add(dot);
128
+ } else {
129
+ const direction = point.clone().sub(approachPoint);
130
+ if (!direction.lengthSq()) direction.copy(shoulderDir);
131
+ direction.normalize();
132
+ const arrowLenPx = style.arrowLengthPx ?? 12;
133
+ const arrowWidthPx = style.arrowWidthPx ?? 4;
134
+ const arrowLength = ctx?.screenSizeWorld ? ctx.screenSizeWorld(arrowLenPx) : screenSizeWorld(viewer, arrowLenPx);
135
+ const arrowWidth = ctx?.screenSizeWorld ? ctx.screenSizeWorld(arrowWidthPx) : screenSizeWorld(viewer, arrowWidthPx);
136
+ addArrowCone(group, point, direction, arrowLength, arrowWidth, style.arrowColor ?? color);
137
+ }
138
+ });
139
+ return [];
140
+ }
141
+
142
+ static applyParams(pmimode, ann, params) {
143
+ super.applyParams(pmimode, ann, params);
144
+ ann.text = sanitizeText(ann.text);
145
+ if (!Array.isArray(ann.target)) {
146
+ ann.target = ann.target ? [String(ann.target)] : [];
147
+ }
148
+ ann.anchorPosition = normalizeAnchorPosition(ann.anchorPosition ?? ann.anchorSide);
149
+ delete ann.anchorSide;
150
+ ann.endStyle = normalizeEndStyle(ann.endStyle);
151
+ return { paramsPatch: {} };
152
+ }
153
+
154
+ static onLabelPointerDown(pmimode, idx, ann, e, ctx) {
155
+ try {
156
+ const viewer = pmimode?.viewer;
157
+ const targets = resolveTargetPoints(viewer, viewer?.partHistory?.scene, ann);
158
+ const labelPos = resolveLabelPosition(pmimode, ann, targets, ctx) || new THREE.Vector3();
159
+ const basis = computeViewBasis(pmimode, viewer, ann);
160
+ const normal = basis.forward;
161
+ const anchor = (targets && targets.length && targets[0]) ? targets[0] : (averageTargets(targets) || labelPos);
162
+ if (!ctx?.raycastFromEvent) return;
163
+ const plane = new THREE.Plane().setFromNormalAndCoplanarPoint(normal, anchor);
164
+ LeaderAnnotation.dragLabelOnPlane(pmimode, ctx, {
165
+ makePlane: () => plane,
166
+ onDrag: (hit) => {
167
+ ensurePersistentData(ann);
168
+ ann.persistentData.labelWorld = [hit.x, hit.y, hit.z];
169
+ ctx.updateLabel(idx, null, hit, ann);
170
+ pmimode?.refreshAnnotationsUI?.();
171
+ },
172
+ onEnd: () => {
173
+ try { if (pmimode?.viewer?.controls) pmimode.viewer.controls.enabled = true; } catch {}
174
+ },
175
+ });
176
+ } catch {
177
+ // ignore drag failures
178
+ }
179
+ }
180
+ }
181
+
182
+ function ensurePersistentData(ann) {
183
+ if (!ann.persistentData || typeof ann.persistentData !== 'object') {
184
+ ann.persistentData = {};
185
+ }
186
+ }
187
+
188
+ function sanitizeText(value) {
189
+ if (typeof value === 'string') return value;
190
+ if (value == null) return '';
191
+ return String(value);
192
+ }
193
+
194
+ function normalizeAnchorPosition(value) {
195
+ const OPTIONS = new Set([
196
+ 'Left Top',
197
+ 'Left Middle',
198
+ 'Left Bottom',
199
+ 'Right Top',
200
+ 'Right Middle',
201
+ 'Right Bottom',
202
+ ]);
203
+ if (typeof value === 'string') {
204
+ const trimmed = value.trim();
205
+ if (OPTIONS.has(trimmed)) return trimmed;
206
+ const lower = trimmed.toLowerCase();
207
+ if (lower === 'left') return 'Left Middle';
208
+ if (lower === 'right') return 'Right Middle';
209
+ if (lower === 'left-top' || lower === 'lefttop') return 'Left Top';
210
+ if (lower === 'left-bottom' || lower === 'leftbottom') return 'Left Bottom';
211
+ if (lower === 'right-top' || lower === 'righttop') return 'Right Top';
212
+ if (lower === 'right-bottom' || lower === 'rightbottom') return 'Right Bottom';
213
+ }
214
+ if (value && typeof value === 'object') {
215
+ const str = String(value.label || value.value || value.name || '').trim();
216
+ if (OPTIONS.has(str)) return str;
217
+ }
218
+ return 'Right Middle';
219
+ }
220
+
221
+ function normalizeEndStyle(value) {
222
+ return value === 'dot' ? 'dot' : 'arrow';
223
+ }
224
+
225
+ function resolveTargetPoints(viewer, scene, ann) {
226
+ const names = Array.isArray(ann?.target) ? ann.target : [];
227
+ if (!names.length || !scene) return [];
228
+ const out = [];
229
+ const unique = new Set();
230
+ for (const name of names) {
231
+ const key = typeof name === 'string' ? name : String(name ?? '');
232
+ if (!key || unique.has(key)) continue;
233
+ unique.add(key);
234
+ try {
235
+ const obj = scene.getObjectByName?.(key);
236
+ if (!obj) continue;
237
+ let pos = objectRepresentativePoint(viewer, obj);
238
+ if (!pos && typeof obj.getWorldPosition === 'function') {
239
+ pos = obj.getWorldPosition(new THREE.Vector3());
240
+ }
241
+ if (pos) out.push(pos.clone());
242
+ } catch { /* ignore */ }
243
+ }
244
+ return out;
245
+ }
246
+
247
+ function resolveLabelPosition(pmimode, ann, targets, ctx) {
248
+ const viewer = pmimode?.viewer;
249
+ const basis = computeViewBasis(pmimode, viewer, ann);
250
+ const anchor = (targets && targets.length && targets[0])
251
+ ? targets[0].clone()
252
+ : (averageTargets(targets) || new THREE.Vector3());
253
+ const planeNormal = (basis?.forward && basis.forward.lengthSq()) ? basis.forward.clone() : new THREE.Vector3(0, 0, 1);
254
+ const plane = new THREE.Plane().setFromNormalAndCoplanarPoint(planeNormal, anchor);
255
+
256
+ const stored = vectorFromAny(ann?.persistentData?.labelWorld);
257
+ if (stored) {
258
+ const projected = stored.clone();
259
+ plane.projectPoint(projected, projected);
260
+ return projected;
261
+ }
262
+
263
+ const horizontal = Math.max(ctx?.screenSizeWorld ? ctx.screenSizeWorld(90) : screenSizeWorld(viewer, 90), 1e-4);
264
+ const vertical = Math.max(ctx?.screenSizeWorld ? ctx.screenSizeWorld(36) : screenSizeWorld(viewer, 36), 1e-4);
265
+ const rightAxis = (basis?.right && basis.right.lengthSq()) ? basis.right.clone() : new THREE.Vector3(1, 0, 0);
266
+ const upAxis = (basis?.up && basis.up.lengthSq()) ? basis.up.clone() : new THREE.Vector3(0, 1, 0);
267
+ const label = anchor.clone()
268
+ .addScaledVector(rightAxis, horizontal)
269
+ .addScaledVector(upAxis, vertical);
270
+ plane.projectPoint(label, label);
271
+ return label;
272
+ }
273
+
274
+ function computeViewBasis(pmimode, viewer, ann) {
275
+ const saved = basisFromSavedCamera(pmimode?.viewEntry?.camera);
276
+ if (saved) return saved;
277
+
278
+ const forward = new THREE.Vector3(0, 0, -1);
279
+ const up = new THREE.Vector3(0, 1, 0);
280
+ try {
281
+ if (viewer?.camera?.getWorldDirection) {
282
+ viewer.camera.getWorldDirection(forward);
283
+ forward.normalize();
284
+ }
285
+ if (viewer?.camera?.up) {
286
+ up.copy(viewer.camera.up).normalize();
287
+ }
288
+ } catch { }
289
+ if (!forward.lengthSq()) forward.set(0, 0, -1);
290
+ if (!up.lengthSq()) up.set(0, 1, 0);
291
+ const right = new THREE.Vector3().crossVectors(forward, up);
292
+ if (!right.lengthSq()) {
293
+ if (Math.abs(forward.z) < 0.9) {
294
+ up.set(0, 0, 1);
295
+ } else {
296
+ up.set(1, 0, 0);
297
+ }
298
+ right.crossVectors(forward, up);
299
+ }
300
+ right.normalize();
301
+ const trueUp = new THREE.Vector3().crossVectors(right, forward);
302
+ if (!trueUp.lengthSq()) {
303
+ trueUp.copy(up.lengthSq() ? up : new THREE.Vector3(0, 1, 0));
304
+ }
305
+ trueUp.normalize();
306
+ const normForward = forward.clone().normalize();
307
+ return { right, up: trueUp, forward: normForward };
308
+ }
309
+
310
+ function computeShoulderDirection(labelPos, originPoint, basis) {
311
+ try {
312
+ const dir = labelPos.clone().sub(originPoint || new THREE.Vector3());
313
+ if (dir.lengthSq() > 1e-10) return dir.normalize();
314
+ } catch { /* ignore */ }
315
+ const fallback = basis?.right?.clone?.() || new THREE.Vector3(1, 0, 0);
316
+ if (!fallback.lengthSq()) fallback.set(1, 0, 0);
317
+ return fallback.normalize();
318
+ }
319
+
320
+ function sortTargetsByViewUp(points, basis, labelPos) {
321
+ if (!points.length) return [];
322
+ const upAxis = (basis?.up && basis.up.lengthSq()) ? basis.up : new THREE.Vector3(0, 1, 0);
323
+ const records = points.map((point, i) => {
324
+ const rel = point.clone().sub(labelPos || new THREE.Vector3());
325
+ const upVal = rel.dot(upAxis);
326
+ return { point, metric: upVal, index: i };
327
+ });
328
+ records.sort((a, b) => a.metric - b.metric);
329
+ return records.map((rec, orderIndex) => ({ point: rec.point, order: orderIndex }));
330
+ }
331
+
332
+ function basisFromSavedCamera(snapshot) {
333
+ if (!snapshot || typeof snapshot !== 'object') return null;
334
+ const quat = quaternionFromSnapshot(snapshot);
335
+ if (!quat) return null;
336
+ const forward = new THREE.Vector3(0, 0, -1).applyQuaternion(quat);
337
+ if (!forward.lengthSq()) return null;
338
+ const up = new THREE.Vector3(0, 1, 0).applyQuaternion(quat);
339
+ let right = new THREE.Vector3().crossVectors(forward, up);
340
+ if (!right.lengthSq()) {
341
+ right = new THREE.Vector3().crossVectors(forward, new THREE.Vector3(0, 1, 0));
342
+ }
343
+ if (!right.lengthSq()) right.set(1, 0, 0);
344
+ right.normalize();
345
+ const trueUp = new THREE.Vector3().crossVectors(right, forward);
346
+ if (!trueUp.lengthSq()) trueUp.copy(up.lengthSq() ? up : new THREE.Vector3(0, 1, 0));
347
+ trueUp.normalize();
348
+ const normForward = forward.clone().normalize();
349
+ return { right, up: trueUp, forward: normForward };
350
+ }
351
+
352
+ function quaternionFromSnapshot(snapshot) {
353
+ const q = snapshot?.quaternion || snapshot?.camera?.quaternion;
354
+ if (Array.isArray(q) && q.length >= 4) {
355
+ const [x, y, z, w] = q;
356
+ if ([x, y, z, w].every((n) => Number.isFinite(n))) {
357
+ return new THREE.Quaternion(x, y, z, w).normalize();
358
+ }
359
+ }
360
+ if (q && typeof q === 'object') {
361
+ const x = Number(q.x); const y = Number(q.y); const z = Number(q.z); const w = Number(q.w);
362
+ if ([x, y, z, w].every((n) => Number.isFinite(n))) {
363
+ return new THREE.Quaternion(x, y, z, w).normalize();
364
+ }
365
+ }
366
+ const matrixArr = snapshot?.worldMatrix || snapshot?.cameraMatrix || snapshot?.matrix;
367
+ const elements = Array.isArray(matrixArr?.elements) ? matrixArr.elements : matrixArr;
368
+ if (Array.isArray(elements) && elements.length === 16) {
369
+ const mat = new THREE.Matrix4().fromArray(elements);
370
+ const pos = new THREE.Vector3();
371
+ const quatOut = new THREE.Quaternion();
372
+ const scale = new THREE.Vector3();
373
+ mat.decompose(pos, quatOut, scale);
374
+ return quatOut.normalize();
375
+ }
376
+ return null;
377
+ }
378
+
379
+ function vectorFromAny(value) {
380
+ if (!value && value !== 0) return null;
381
+ if (value instanceof THREE.Vector3) return value.clone();
382
+ if (Array.isArray(value) && value.length >= 3) {
383
+ const [x, y, z] = value;
384
+ if ([x, y, z].some((n) => typeof n !== 'number' || !Number.isFinite(n))) return null;
385
+ return new THREE.Vector3(x, y, z);
386
+ }
387
+ if (typeof value === 'object') {
388
+ const x = Number(value.x);
389
+ const y = Number(value.y);
390
+ const z = Number(value.z);
391
+ if ([x, y, z].every((n) => Number.isFinite(n))) {
392
+ return new THREE.Vector3(x, y, z);
393
+ }
394
+ }
395
+ return null;
396
+ }
397
+
398
+ function averageTargets(points) {
399
+ if (!points || !points.length) return null;
400
+ const sum = new THREE.Vector3();
401
+ points.forEach((p) => sum.add(p));
402
+ return sum.multiplyScalar(1 / points.length);
403
+ }