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.
- package/LICENSE.md +32 -0
- package/README.md +154 -0
- package/dist-kernel/brep-kernel.js +74699 -0
- package/package.json +58 -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,532 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
import { BaseAnnotation } from '../BaseAnnotation.js';
|
|
3
|
+
import { makeOverlayLine, addArrowCone, objectRepresentativePoint } from '../annUtils.js';
|
|
4
|
+
|
|
5
|
+
const inputParamsSchema = {
|
|
6
|
+
id: {
|
|
7
|
+
type: 'string',
|
|
8
|
+
default_value: null,
|
|
9
|
+
label: 'ID',
|
|
10
|
+
hint: 'unique identifier for the linear dimension',
|
|
11
|
+
},
|
|
12
|
+
decimals: {
|
|
13
|
+
type: 'number',
|
|
14
|
+
default_value: 3,
|
|
15
|
+
defaultResolver: ({ pmimode }) => {
|
|
16
|
+
const dec = Number.isFinite(pmimode?._opts?.dimDecimals)
|
|
17
|
+
? (pmimode._opts.dimDecimals | 0)
|
|
18
|
+
: undefined;
|
|
19
|
+
if (!Number.isFinite(dec)) return undefined;
|
|
20
|
+
return Math.max(0, Math.min(8, dec));
|
|
21
|
+
},
|
|
22
|
+
label: 'Decimals',
|
|
23
|
+
hint: 'Number of decimal places to display',
|
|
24
|
+
min: 0,
|
|
25
|
+
max: 8,
|
|
26
|
+
step: 1,
|
|
27
|
+
},
|
|
28
|
+
targets: {
|
|
29
|
+
type: 'reference_selection',
|
|
30
|
+
selectionFilter: ['VERTEX', 'EDGE'],
|
|
31
|
+
multiple: true,
|
|
32
|
+
default_value: [],
|
|
33
|
+
label: 'Targets',
|
|
34
|
+
hint: 'Select two vertices, or a vertex and an edge, or a single edge',
|
|
35
|
+
},
|
|
36
|
+
planeRefName: {
|
|
37
|
+
type: 'reference_selection',
|
|
38
|
+
selectionFilter: ['FACE', 'PLANE'],
|
|
39
|
+
multiple: false,
|
|
40
|
+
default_value: '',
|
|
41
|
+
label: 'Face/Plane',
|
|
42
|
+
hint: 'Projection plane (optional)',
|
|
43
|
+
},
|
|
44
|
+
alignment: {
|
|
45
|
+
type: 'options',
|
|
46
|
+
default_value: 'view',
|
|
47
|
+
options: ['view', 'XY', 'YZ', 'ZX'],
|
|
48
|
+
label: 'Alignment',
|
|
49
|
+
hint: 'Dimension alignment mode',
|
|
50
|
+
},
|
|
51
|
+
offset: {
|
|
52
|
+
type: 'number',
|
|
53
|
+
default_value: 0,
|
|
54
|
+
label: 'Offset',
|
|
55
|
+
hint: 'Offset distance for the dimension line',
|
|
56
|
+
step: 'any',
|
|
57
|
+
},
|
|
58
|
+
showExt: {
|
|
59
|
+
type: 'boolean',
|
|
60
|
+
default_value: true,
|
|
61
|
+
label: 'Extension Lines',
|
|
62
|
+
hint: 'Draw extension lines from anchors to offset line',
|
|
63
|
+
},
|
|
64
|
+
isReference: {
|
|
65
|
+
type: 'boolean',
|
|
66
|
+
default_value: false,
|
|
67
|
+
label: 'Reference',
|
|
68
|
+
hint: 'Mark as reference dimension (parentheses)',
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export class LinearDimensionAnnotation extends BaseAnnotation {
|
|
73
|
+
static entityType = 'linear';
|
|
74
|
+
static type = 'linear';
|
|
75
|
+
static shortName = 'DIM';
|
|
76
|
+
static longName = 'Linear Dimension';
|
|
77
|
+
static title = 'Linear';
|
|
78
|
+
static inputParamsSchema = inputParamsSchema;
|
|
79
|
+
|
|
80
|
+
constructor(opts = {}) {
|
|
81
|
+
super(opts);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
uiFieldsTest(context) {
|
|
85
|
+
const planeRefName = this.inputParams?.planeRefName;
|
|
86
|
+
const hasPlane = Array.isArray(planeRefName)
|
|
87
|
+
? planeRefName.length > 0
|
|
88
|
+
: Boolean(String(planeRefName || '').trim());
|
|
89
|
+
return hasPlane ? ['alignment'] : [];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async run(renderingContext) {
|
|
93
|
+
const { pmimode, group, idx, ctx } = renderingContext;
|
|
94
|
+
const ann = this.inputParams;
|
|
95
|
+
const pts = computeDimPoints(pmimode, ann);
|
|
96
|
+
const measured = (pts && pts.p0 && pts.p1) ? pts.p0.distanceTo(pts.p1) : null;
|
|
97
|
+
const labelInfo = formatLinearLabel(measured, ann);
|
|
98
|
+
ann.value = labelInfo.display;
|
|
99
|
+
|
|
100
|
+
if (!pts || !pts.p0 || !pts.p1) return [];
|
|
101
|
+
|
|
102
|
+
if (!ann.persistentData || typeof ann.persistentData !== 'object') {
|
|
103
|
+
ann.persistentData = {};
|
|
104
|
+
}
|
|
105
|
+
const persistent = ann.persistentData;
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
const color = 0x10b981;
|
|
109
|
+
const normal = ctx.alignNormal ? ctx.alignNormal(ann?.alignment || 'view', ann) : new THREE.Vector3(0, 0, 1);
|
|
110
|
+
const dir = new THREE.Vector3().subVectors(pts.p1, pts.p0);
|
|
111
|
+
if (dir.lengthSq() < 1e-8) return [];
|
|
112
|
+
dir.normalize();
|
|
113
|
+
const t = new THREE.Vector3().crossVectors(normal, dir).normalize();
|
|
114
|
+
|
|
115
|
+
let off = Number(ann?.offset);
|
|
116
|
+
if (!Number.isFinite(off)) off = ctx.screenSizeWorld ? ctx.screenSizeWorld(20) : 0.05;
|
|
117
|
+
const p0o = pts.p0.clone().addScaledVector(t, off);
|
|
118
|
+
const p1o = pts.p1.clone().addScaledVector(t, off);
|
|
119
|
+
|
|
120
|
+
if (ann?.showExt !== false && off !== 0) {
|
|
121
|
+
group.add(makeOverlayLine(pts.p0, p0o, color));
|
|
122
|
+
group.add(makeOverlayLine(pts.p1, p1o, color));
|
|
123
|
+
}
|
|
124
|
+
group.add(makeOverlayLine(p0o, p1o, color));
|
|
125
|
+
|
|
126
|
+
const arrowLength = ctx.screenSizeWorld ? ctx.screenSizeWorld(12) : 0.08;
|
|
127
|
+
const arrowWidth = ctx.screenSizeWorld ? ctx.screenSizeWorld(4) : 0.03;
|
|
128
|
+
addArrowCone(group, p0o, dir.clone().negate(), arrowLength, arrowWidth, color);
|
|
129
|
+
addArrowCone(group, p1o, dir.clone(), arrowLength, arrowWidth, color);
|
|
130
|
+
|
|
131
|
+
if (persistent.labelWorld) {
|
|
132
|
+
try {
|
|
133
|
+
const labelVec = arrayToVector(persistent.labelWorld);
|
|
134
|
+
const lineLen = p0o.distanceTo(p1o);
|
|
135
|
+
if (lineLen > 1e-6) {
|
|
136
|
+
const toLabel = labelVec.clone().sub(p0o);
|
|
137
|
+
const along = toLabel.dot(dir);
|
|
138
|
+
const clamped = Math.max(0, Math.min(lineLen, along));
|
|
139
|
+
const nearest = p0o.clone().addScaledVector(dir, clamped);
|
|
140
|
+
const perpDist = labelVec.distanceTo(nearest);
|
|
141
|
+
const threshold = ctx.screenSizeWorld ? ctx.screenSizeWorld(6) : 0.02;
|
|
142
|
+
if (perpDist > threshold) group.add(makeOverlayLine(nearest, labelVec, color));
|
|
143
|
+
}
|
|
144
|
+
} catch { /* ignore */ }
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const dec = Number.isFinite(ann.decimals) ? ann.decimals : (pmimode?._opts?.dimDecimals | 0);
|
|
148
|
+
const value = pts.p0.distanceTo(pts.p1);
|
|
149
|
+
const displayInfo = formatLinearLabel(value, ann, dec);
|
|
150
|
+
ann.value = displayInfo.display;
|
|
151
|
+
const labelText = ctx.formatReferenceLabel ? ctx.formatReferenceLabel(ann, displayInfo.raw) : displayInfo.display;
|
|
152
|
+
|
|
153
|
+
const labelPos = (() => {
|
|
154
|
+
if (persistent.labelWorld) return arrayToVector(persistent.labelWorld);
|
|
155
|
+
if (ann.labelWorld) return arrayToVector(ann.labelWorld);
|
|
156
|
+
const mid = new THREE.Vector3().addVectors(p0o, p1o).multiplyScalar(0.5);
|
|
157
|
+
const lift = ctx.screenSizeWorld ? ctx.screenSizeWorld(6) : 0.02;
|
|
158
|
+
return mid.addScaledVector(t, lift);
|
|
159
|
+
})();
|
|
160
|
+
|
|
161
|
+
if (labelPos) ctx.updateLabel(idx, labelText, labelPos, ann);
|
|
162
|
+
} catch { /* ignore */ }
|
|
163
|
+
return [];
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
static onLabelPointerDown(pmimode, idx, ann, e, ctx) {
|
|
167
|
+
try {
|
|
168
|
+
const v = pmimode.viewer; const cam = v?.camera; if (!cam) return;
|
|
169
|
+
const pts = computeDimPoints(pmimode, ann);
|
|
170
|
+
if (!pts || !pts.p0 || !pts.p1) return;
|
|
171
|
+
|
|
172
|
+
const p0 = pts.p0;
|
|
173
|
+
const p1 = pts.p1;
|
|
174
|
+
const normal = ctx.alignNormal ? ctx.alignNormal(ann?.alignment || 'view', ann) : new THREE.Vector3(0, 0, 1);
|
|
175
|
+
const dir = new THREE.Vector3().subVectors(p1, p0).normalize();
|
|
176
|
+
const t = new THREE.Vector3().crossVectors(normal, dir).normalize();
|
|
177
|
+
const mid = new THREE.Vector3().addVectors(p0, p1).multiplyScalar(0.5);
|
|
178
|
+
const plane = new THREE.Plane().setFromNormalAndCoplanarPoint(normal, mid);
|
|
179
|
+
|
|
180
|
+
LinearDimensionAnnotation.dragLabelOnPlane(pmimode, ctx, {
|
|
181
|
+
makePlane: () => plane,
|
|
182
|
+
onDrag: (hit) => {
|
|
183
|
+
const toMouse = new THREE.Vector3().subVectors(hit, mid);
|
|
184
|
+
const offsetDist = toMouse.dot(t);
|
|
185
|
+
ann.offset = offsetDist;
|
|
186
|
+
const vecOut = [hit.x, hit.y, hit.z];
|
|
187
|
+
LinearDimensionAnnotation.ensurePersistentData(ann);
|
|
188
|
+
ann.persistentData.labelWorld = vecOut;
|
|
189
|
+
ann.labelWorld = vecOut;
|
|
190
|
+
ctx.updateLabel(idx, null, hit, ann);
|
|
191
|
+
pmimode.refreshAnnotationsUI?.();
|
|
192
|
+
},
|
|
193
|
+
onEnd: () => {
|
|
194
|
+
try { if (pmimode?.viewer?.controls) pmimode.viewer.controls.enabled = true; } catch { }
|
|
195
|
+
},
|
|
196
|
+
});
|
|
197
|
+
} catch { /* ignore */ }
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function formatLinearLabel(measured, ann, overrideDecimals) {
|
|
203
|
+
if (typeof measured !== 'number' || !Number.isFinite(measured)) {
|
|
204
|
+
return { raw: '-', display: '-' };
|
|
205
|
+
}
|
|
206
|
+
const decRaw = overrideDecimals !== undefined ? overrideDecimals : Number(ann?.decimals);
|
|
207
|
+
const decimals = Number.isFinite(decRaw) ? Math.max(0, Math.min(8, decRaw | 0)) : 3;
|
|
208
|
+
const raw = `${measured.toFixed(decimals)}`;
|
|
209
|
+
const display = ann?.isReference ? `(${raw})` : raw;
|
|
210
|
+
return { raw, display };
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function computeDimPoints(pmimode, ann) {
|
|
214
|
+
const hasTargets = Array.isArray(ann?.targets);
|
|
215
|
+
try {
|
|
216
|
+
const scene = pmimode?.viewer?.partHistory?.scene;
|
|
217
|
+
const targets = normalizeTargetList(hasTargets ? ann.targets : null);
|
|
218
|
+
if (scene && targets.length) {
|
|
219
|
+
const objects = resolveTargetObjects(scene, targets);
|
|
220
|
+
if (objects.length) {
|
|
221
|
+
const vertices = [];
|
|
222
|
+
const edges = [];
|
|
223
|
+
for (const obj of objects) {
|
|
224
|
+
const type = typeof obj?.type === 'string' ? obj.type.toUpperCase() : '';
|
|
225
|
+
if (type === 'VERTEX') vertices.push(obj);
|
|
226
|
+
else if (type === 'EDGE') edges.push(obj);
|
|
227
|
+
}
|
|
228
|
+
const viewer = pmimode?.viewer;
|
|
229
|
+
if (vertices.length >= 2) {
|
|
230
|
+
const p0 = resolveVertexPoint(viewer, vertices[0]);
|
|
231
|
+
const p1 = resolveVertexPoint(viewer, vertices[1]);
|
|
232
|
+
if (p0 && p1) return { p0, p1 };
|
|
233
|
+
}
|
|
234
|
+
if (vertices.length === 1 && edges.length) {
|
|
235
|
+
const p0 = resolveVertexPoint(viewer, vertices[0]);
|
|
236
|
+
const p1 = p0 ? closestEndpointToPoint(edges, p0) : null;
|
|
237
|
+
if (p0 && p1) return { p0, p1 };
|
|
238
|
+
}
|
|
239
|
+
if (!vertices.length && edges.length) {
|
|
240
|
+
if (edges.length === 1) {
|
|
241
|
+
const ends = edgeEndpointsWorld(edges[0]);
|
|
242
|
+
if (ends) return { p0: ends.a, p1: ends.b };
|
|
243
|
+
} else if (edges.length >= 2) {
|
|
244
|
+
return closestPointsBetweenEdges(edges[0], edges[1]);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
} catch { /* ignore */ }
|
|
250
|
+
if (!hasTargets) {
|
|
251
|
+
try {
|
|
252
|
+
const scene = pmimode?.viewer?.partHistory?.scene;
|
|
253
|
+
const aName = ann?.aRefName || null;
|
|
254
|
+
const bName = ann?.bRefName || null;
|
|
255
|
+
if (scene && (aName || bName)) {
|
|
256
|
+
const objA = aName ? scene.getObjectByName(aName) : null;
|
|
257
|
+
const objB = bName ? scene.getObjectByName(bName) : null;
|
|
258
|
+
if (objA && objB) return closestPointsForObjects(objA, objB);
|
|
259
|
+
if (objA && !objB) {
|
|
260
|
+
const pA = objectRepresentativePoint(pmimode.viewer, objA);
|
|
261
|
+
const pB = vectorFromAnnotationPoint(ann.p1);
|
|
262
|
+
if (pA && pB) return { p0: pA, p1: pB };
|
|
263
|
+
}
|
|
264
|
+
if (!objA && objB) {
|
|
265
|
+
const pB = objectRepresentativePoint(pmimode.viewer, objB);
|
|
266
|
+
const pA = vectorFromAnnotationPoint(ann.p0);
|
|
267
|
+
if (pA && pB) return { p0: pA, p1: pB };
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
} catch { /* ignore */ }
|
|
271
|
+
}
|
|
272
|
+
return {
|
|
273
|
+
p0: vectorFromAnnotationPoint(ann?.p0) || new THREE.Vector3(0, 0, 0),
|
|
274
|
+
p1: vectorFromAnnotationPoint(ann?.p1) || new THREE.Vector3(0, 0, 0),
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function normalizeTargetList(value) {
|
|
279
|
+
if (Array.isArray(value)) return value;
|
|
280
|
+
if (value == null || value === '') return [];
|
|
281
|
+
return [value];
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function resolveTargetObjects(scene, targets) {
|
|
285
|
+
if (!scene || !Array.isArray(targets)) return [];
|
|
286
|
+
const out = [];
|
|
287
|
+
const seen = new Set();
|
|
288
|
+
for (const entry of targets) {
|
|
289
|
+
let obj = null;
|
|
290
|
+
if (entry && typeof entry === 'object') {
|
|
291
|
+
if (entry.isObject3D) obj = entry;
|
|
292
|
+
else if (entry.name) obj = scene.getObjectByName(entry.name);
|
|
293
|
+
} else if (entry != null) {
|
|
294
|
+
const key = String(entry).trim();
|
|
295
|
+
if (key) obj = scene.getObjectByName(key);
|
|
296
|
+
}
|
|
297
|
+
if (!obj) continue;
|
|
298
|
+
const key = obj.uuid || obj.id || obj.name;
|
|
299
|
+
if (key != null) {
|
|
300
|
+
const keyStr = String(key);
|
|
301
|
+
if (seen.has(keyStr)) continue;
|
|
302
|
+
seen.add(keyStr);
|
|
303
|
+
}
|
|
304
|
+
out.push(obj);
|
|
305
|
+
}
|
|
306
|
+
return out;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function resolveVertexPoint(viewer, vertex) {
|
|
310
|
+
const rep = objectRepresentativePoint(viewer, vertex);
|
|
311
|
+
if (rep && rep.clone) return rep.clone();
|
|
312
|
+
if (rep && rep.isVector3) return rep.clone();
|
|
313
|
+
if (vertex?.getWorldPosition) return vertex.getWorldPosition(new THREE.Vector3());
|
|
314
|
+
return null;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function closestEndpointToPoint(edges, point) {
|
|
318
|
+
if (!Array.isArray(edges) || !point) return null;
|
|
319
|
+
let best = null;
|
|
320
|
+
let bestD2 = Infinity;
|
|
321
|
+
for (const edge of edges) {
|
|
322
|
+
const ends = edgeEndpointsWorld(edge);
|
|
323
|
+
if (ends) {
|
|
324
|
+
const d2a = ends.a.distanceToSquared(point);
|
|
325
|
+
if (d2a < bestD2) { bestD2 = d2a; best = ends.a; }
|
|
326
|
+
const d2b = ends.b.distanceToSquared(point);
|
|
327
|
+
if (d2b < bestD2) { bestD2 = d2b; best = ends.b; }
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
const fallback = closestPointOnEdgeToPoint(edge, point);
|
|
331
|
+
if (fallback) {
|
|
332
|
+
const d2 = fallback.distanceToSquared(point);
|
|
333
|
+
if (d2 < bestD2) { bestD2 = d2; best = fallback; }
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return best ? best.clone() : null;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function edgeEndpointsWorld(edge) {
|
|
340
|
+
if (!edge) return null;
|
|
341
|
+
try { edge.updateMatrixWorld?.(true); } catch { /* ignore */ }
|
|
342
|
+
try {
|
|
343
|
+
if (typeof edge.points === 'function') {
|
|
344
|
+
const pts = edge.points(true);
|
|
345
|
+
if (Array.isArray(pts) && pts.length >= 2) {
|
|
346
|
+
const a = arrayToVector(pts[0]);
|
|
347
|
+
const b = arrayToVector(pts[pts.length - 1]);
|
|
348
|
+
if (a && b) return { a, b };
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
} catch { /* ignore */ }
|
|
352
|
+
const poly = Array.isArray(edge?.userData?.polylineLocal)
|
|
353
|
+
? edge.userData.polylineLocal
|
|
354
|
+
: null;
|
|
355
|
+
if (poly && poly.length >= 2) {
|
|
356
|
+
const a = arrayToVector(poly[0]);
|
|
357
|
+
const b = arrayToVector(poly[poly.length - 1]);
|
|
358
|
+
if (a && b) {
|
|
359
|
+
if (edge.matrixWorld) {
|
|
360
|
+
a.applyMatrix4(edge.matrixWorld);
|
|
361
|
+
b.applyMatrix4(edge.matrixWorld);
|
|
362
|
+
}
|
|
363
|
+
return { a, b };
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
const startAttr = edge?.geometry?.attributes?.instanceStart;
|
|
367
|
+
const endAttr = edge?.geometry?.attributes?.instanceEnd;
|
|
368
|
+
if (startAttr && endAttr && startAttr.count >= 1) {
|
|
369
|
+
const a = new THREE.Vector3(startAttr.getX(0), startAttr.getY(0), startAttr.getZ(0));
|
|
370
|
+
const b = new THREE.Vector3(endAttr.getX(0), endAttr.getY(0), endAttr.getZ(0));
|
|
371
|
+
if (edge.matrixWorld) {
|
|
372
|
+
a.applyMatrix4(edge.matrixWorld);
|
|
373
|
+
b.applyMatrix4(edge.matrixWorld);
|
|
374
|
+
}
|
|
375
|
+
return { a, b };
|
|
376
|
+
}
|
|
377
|
+
const pos = edge?.geometry?.getAttribute?.('position');
|
|
378
|
+
if (pos && pos.itemSize === 3 && pos.count >= 2) {
|
|
379
|
+
const a = new THREE.Vector3(pos.getX(0), pos.getY(0), pos.getZ(0));
|
|
380
|
+
const b = new THREE.Vector3(pos.getX(pos.count - 1), pos.getY(pos.count - 1), pos.getZ(pos.count - 1));
|
|
381
|
+
if (edge.matrixWorld) {
|
|
382
|
+
a.applyMatrix4(edge.matrixWorld);
|
|
383
|
+
b.applyMatrix4(edge.matrixWorld);
|
|
384
|
+
}
|
|
385
|
+
return { a, b };
|
|
386
|
+
}
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function closestPointsForObjects(objA, objB) {
|
|
391
|
+
if (objA?.type === 'VERTEX' && objB?.type === 'VERTEX') {
|
|
392
|
+
return { p0: objA.getWorldPosition(new THREE.Vector3()), p1: objB.getWorldPosition(new THREE.Vector3()) };
|
|
393
|
+
}
|
|
394
|
+
if (objA?.type === 'EDGE' && objB?.type === 'VERTEX') {
|
|
395
|
+
const v = objB.getWorldPosition(new THREE.Vector3());
|
|
396
|
+
const p = closestPointOnEdgeToPoint(objA, v);
|
|
397
|
+
return { p0: p, p1: v };
|
|
398
|
+
}
|
|
399
|
+
if (objA?.type === 'VERTEX' && objB?.type === 'EDGE') {
|
|
400
|
+
const v = objA.getWorldPosition(new THREE.Vector3());
|
|
401
|
+
const p = closestPointOnEdgeToPoint(objB, v);
|
|
402
|
+
return { p0: v, p1: p };
|
|
403
|
+
}
|
|
404
|
+
if (objA?.type === 'EDGE' && objB?.type === 'EDGE') {
|
|
405
|
+
return closestPointsBetweenEdges(objA, objB);
|
|
406
|
+
}
|
|
407
|
+
return {
|
|
408
|
+
p0: objectRepresentativePoint(null, objA) || new THREE.Vector3(),
|
|
409
|
+
p1: objectRepresentativePoint(null, objB) || new THREE.Vector3(),
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
function closestPointOnEdgeToPoint(edge, point) {
|
|
414
|
+
try {
|
|
415
|
+
const pts = edge.points(true);
|
|
416
|
+
if (!pts || pts.length < 2) return edge.getWorldPosition(new THREE.Vector3());
|
|
417
|
+
const p = point.clone();
|
|
418
|
+
let best = { d2: Infinity, q: null };
|
|
419
|
+
const a = new THREE.Vector3();
|
|
420
|
+
const b = new THREE.Vector3();
|
|
421
|
+
for (let i = 0; i < pts.length - 1; i++) {
|
|
422
|
+
a.set(pts[i].x, pts[i].y, pts[i].z);
|
|
423
|
+
b.set(pts[i + 1].x, pts[i + 1].y, pts[i + 1].z);
|
|
424
|
+
const q = closestPointOnSegment(a, b, p);
|
|
425
|
+
const d2 = q.distanceToSquared(p);
|
|
426
|
+
if (d2 < best.d2) best = { d2, q };
|
|
427
|
+
}
|
|
428
|
+
return best.q || edge.getWorldPosition(new THREE.Vector3());
|
|
429
|
+
} catch { return edge.getWorldPosition(new THREE.Vector3()); }
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
function closestPointsBetweenEdges(e1, e2) {
|
|
433
|
+
try {
|
|
434
|
+
const pts1 = e1.points(true);
|
|
435
|
+
const pts2 = e2.points(true);
|
|
436
|
+
if (!pts1 || pts1.length < 2 || !pts2 || pts2.length < 2) {
|
|
437
|
+
return {
|
|
438
|
+
p0: objectRepresentativePoint(null, e1) || new THREE.Vector3(),
|
|
439
|
+
p1: objectRepresentativePoint(null, e2) || new THREE.Vector3(),
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
const a0 = new THREE.Vector3();
|
|
443
|
+
const a1 = new THREE.Vector3();
|
|
444
|
+
const b0 = new THREE.Vector3();
|
|
445
|
+
const b1 = new THREE.Vector3();
|
|
446
|
+
let best = { d2: Infinity, p: null, q: null };
|
|
447
|
+
for (let i = 0; i < pts1.length - 1; i++) {
|
|
448
|
+
a0.set(pts1[i].x, pts1[i].y, pts1[i].z);
|
|
449
|
+
a1.set(pts1[i + 1].x, pts1[i + 1].y, pts1[i + 1].z);
|
|
450
|
+
for (let j = 0; j < pts2.length - 1; j++) {
|
|
451
|
+
b0.set(pts2[j].x, pts2[j].y, pts2[j].z);
|
|
452
|
+
b1.set(pts2[j + 1].x, pts2[j + 1].y, pts2[j + 1].z);
|
|
453
|
+
const { p, q } = closestPointsOnSegments(a0, a1, b0, b1);
|
|
454
|
+
const d2 = p.distanceToSquared(q);
|
|
455
|
+
if (d2 < best.d2) best = { d2, p, q };
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
return {
|
|
459
|
+
p0: best.p || objectRepresentativePoint(null, e1) || new THREE.Vector3(),
|
|
460
|
+
p1: best.q || objectRepresentativePoint(null, e2) || new THREE.Vector3(),
|
|
461
|
+
};
|
|
462
|
+
} catch {
|
|
463
|
+
return {
|
|
464
|
+
p0: objectRepresentativePoint(null, e1) || new THREE.Vector3(),
|
|
465
|
+
p1: objectRepresentativePoint(null, e2) || new THREE.Vector3(),
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
function closestPointOnSegment(a, b, p) {
|
|
471
|
+
const ab = b.clone().sub(a);
|
|
472
|
+
const t = Math.max(0, Math.min(1, ab.dot(p.clone().sub(a)) / (ab.lengthSq() || 1)));
|
|
473
|
+
return a.clone().addScaledVector(ab, t);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
function closestPointsOnSegments(p1, q1, p2, q2) {
|
|
477
|
+
const d1 = q1.clone().sub(p1);
|
|
478
|
+
const d2 = q2.clone().sub(p2);
|
|
479
|
+
const r = p1.clone().sub(p2);
|
|
480
|
+
const a = d1.dot(d1);
|
|
481
|
+
const e = d2.dot(d2);
|
|
482
|
+
const f = d2.dot(r);
|
|
483
|
+
let s;
|
|
484
|
+
let t;
|
|
485
|
+
const EPS = 1e-12;
|
|
486
|
+
if (a <= EPS && e <= EPS) {
|
|
487
|
+
s = 0;
|
|
488
|
+
t = 0;
|
|
489
|
+
} else if (a <= EPS) {
|
|
490
|
+
s = 0;
|
|
491
|
+
t = Math.max(0, Math.min(1, f / e));
|
|
492
|
+
} else {
|
|
493
|
+
const c = d1.dot(r);
|
|
494
|
+
if (e <= EPS) {
|
|
495
|
+
t = 0;
|
|
496
|
+
s = Math.max(0, Math.min(1, -c / a));
|
|
497
|
+
} else {
|
|
498
|
+
const b = d1.dot(d2);
|
|
499
|
+
const denom = a * e - b * b;
|
|
500
|
+
s = denom !== 0 ? Math.max(0, Math.min(1, (b * f - c * e) / denom)) : 0;
|
|
501
|
+
t = (b * s + f) / e;
|
|
502
|
+
if (t < 0) {
|
|
503
|
+
t = 0;
|
|
504
|
+
s = Math.max(0, Math.min(1, -c / a));
|
|
505
|
+
} else if (t > 1) {
|
|
506
|
+
t = 1;
|
|
507
|
+
s = Math.max(0, Math.min(1, (b - c) / a));
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
const cp1 = p1.clone().addScaledVector(d1, s);
|
|
512
|
+
const cp2 = p2.clone().addScaledVector(d2, t);
|
|
513
|
+
return { p: cp1, q: cp2 };
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
function arrayToVector(value) {
|
|
517
|
+
if (!value) return null;
|
|
518
|
+
if (value instanceof THREE.Vector3) return value.clone();
|
|
519
|
+
if (Array.isArray(value)) return new THREE.Vector3(value[0] || 0, value[1] || 0, value[2] || 0);
|
|
520
|
+
if (typeof value === 'object') {
|
|
521
|
+
return new THREE.Vector3(value.x || 0, value.y || 0, value.z || 0);
|
|
522
|
+
}
|
|
523
|
+
return null;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
function vectorFromAnnotationPoint(point) {
|
|
527
|
+
if (!point) return null;
|
|
528
|
+
if (point instanceof THREE.Vector3) return point.clone();
|
|
529
|
+
if (Array.isArray(point)) return new THREE.Vector3(point[0] || 0, point[1] || 0, point[2] || 0);
|
|
530
|
+
if (typeof point === 'object') return new THREE.Vector3(point.x || 0, point.y || 0, point.z || 0);
|
|
531
|
+
return null;
|
|
532
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// NoteAnnotation.js
|
|
2
|
+
// Note annotation following feature pattern with full implementation from old PMI code
|
|
3
|
+
|
|
4
|
+
import * as THREE from 'three';
|
|
5
|
+
import { BaseAnnotation } from '../BaseAnnotation.js';
|
|
6
|
+
import { getPMIStyle } from '../pmiStyle.js';
|
|
7
|
+
|
|
8
|
+
const inputParamsSchema = {
|
|
9
|
+
id: {
|
|
10
|
+
type: "string",
|
|
11
|
+
default_value: null,
|
|
12
|
+
hint: "unique identifier for the note",
|
|
13
|
+
},
|
|
14
|
+
text: {
|
|
15
|
+
type: "string",
|
|
16
|
+
default_value: "",
|
|
17
|
+
defaultResolver: ({ pmimode }) => {
|
|
18
|
+
const txt = pmimode?._opts?.noteText;
|
|
19
|
+
return (typeof txt === 'string') ? txt : undefined;
|
|
20
|
+
},
|
|
21
|
+
hint: "Note text content"
|
|
22
|
+
},
|
|
23
|
+
position: {
|
|
24
|
+
type: "object",
|
|
25
|
+
default_value: null,
|
|
26
|
+
hint: "3D position of the note marker"
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export class NoteAnnotation extends BaseAnnotation {
|
|
31
|
+
static entityType = 'note';
|
|
32
|
+
static type = 'note';
|
|
33
|
+
static shortName = 'NOTE';
|
|
34
|
+
static longName = 'Note';
|
|
35
|
+
static title = 'Note';
|
|
36
|
+
static inputParamsSchema = inputParamsSchema;
|
|
37
|
+
|
|
38
|
+
constructor(opts = {}) {
|
|
39
|
+
super(opts);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async run(renderingContext) {
|
|
43
|
+
const { pmimode, group, idx, ctx } = renderingContext;
|
|
44
|
+
const ann = this.inputParams;
|
|
45
|
+
const style = getPMIStyle();
|
|
46
|
+
const p = new THREE.Vector3(ann.position?.x || 0, ann.position?.y || 0, ann.position?.z || 0);
|
|
47
|
+
const g = new THREE.SphereGeometry(style.noteDotRadius ?? 0.08, 16, 12);
|
|
48
|
+
const m = new THREE.MeshBasicMaterial({ color: style.noteDotColor ?? style.dotColor ?? 0x93c5fd, depthTest: false, depthWrite: false, transparent: true });
|
|
49
|
+
const dot = new THREE.Mesh(g, m);
|
|
50
|
+
dot.position.copy(p);
|
|
51
|
+
group.add(dot);
|
|
52
|
+
|
|
53
|
+
const txt = String(ann.text || '');
|
|
54
|
+
if (!txt) return [];
|
|
55
|
+
|
|
56
|
+
let labelPos = null;
|
|
57
|
+
if (ann.labelWorld) {
|
|
58
|
+
labelPos = new THREE.Vector3(ann.labelWorld.x, ann.labelWorld.y, ann.labelWorld.z);
|
|
59
|
+
} else {
|
|
60
|
+
const n = ctx.alignNormal('view', ann);
|
|
61
|
+
const camRight = new THREE.Vector3();
|
|
62
|
+
const camUp = new THREE.Vector3(0, 1, 0);
|
|
63
|
+
try { pmimode.viewer?.camera?.getWorldDirection?.(camRight); camRight.crossVectors(n, camUp).normalize(); } catch { camRight.set(1, 0, 0); }
|
|
64
|
+
const offset = ctx.screenSizeWorld(16);
|
|
65
|
+
labelPos = p.clone().addScaledVector(camRight, offset).addScaledVector(n, offset * 0.25);
|
|
66
|
+
}
|
|
67
|
+
ctx.updateLabel(idx, txt, labelPos, ann);
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
static getSchema(pmimode, ann) {
|
|
72
|
+
const schema = {
|
|
73
|
+
text: { type: 'string', label: 'Text', default_value: ann.text || '' },
|
|
74
|
+
};
|
|
75
|
+
const params = { text: schema.text.default_value };
|
|
76
|
+
return { schema, params };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
static applyParams(pmimode, ann, params) {
|
|
80
|
+
super.applyParams(pmimode, ann, params);
|
|
81
|
+
ann.text = sanitizeText(ann.text);
|
|
82
|
+
return { paramsPatch: {} };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Drag note label on view plane
|
|
86
|
+
static onLabelPointerDown(pmimode, idx, ann, e, ctx) {
|
|
87
|
+
try {
|
|
88
|
+
const v = pmimode.viewer; const cam = v?.camera; if (!cam) return;
|
|
89
|
+
const normal = ctx.alignNormal ? ctx.alignNormal(ann.alignment || 'view', ann) : new THREE.Vector3(0,0,1);
|
|
90
|
+
const planePoint = ann.labelWorld ? new THREE.Vector3(ann.labelWorld.x, ann.labelWorld.y, ann.labelWorld.z) : new THREE.Vector3(ann.position?.x||0, ann.position?.y||0, ann.position?.z||0);
|
|
91
|
+
const plane = new THREE.Plane().setFromNormalAndCoplanarPoint(normal, planePoint);
|
|
92
|
+
NoteAnnotation.dragLabelOnPlane(pmimode, ctx, {
|
|
93
|
+
makePlane: () => plane,
|
|
94
|
+
onDrag: (hit) => {
|
|
95
|
+
ann.labelWorld = { x: hit.x, y: hit.y, z: hit.z };
|
|
96
|
+
try { ctx.updateLabel(idx, null, hit, ann); } catch {}
|
|
97
|
+
},
|
|
98
|
+
onEnd: () => {
|
|
99
|
+
try { if (pmimode?.viewer?.controls) pmimode.viewer.controls.enabled = true; } catch {}
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
} catch {}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function sanitizeText(value) {
|
|
107
|
+
if (typeof value === 'string') return value;
|
|
108
|
+
if (value == null) return '';
|
|
109
|
+
return String(value);
|
|
110
|
+
}
|