three-cad-viewer 4.3.8 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/{README.md → Readme.md} +7 -5
  2. package/dist/core/picking-controller.d.ts +125 -0
  3. package/dist/core/studio-manager.d.ts +7 -0
  4. package/dist/core/types.d.ts +24 -6
  5. package/dist/core/viewer-state.d.ts +1 -1
  6. package/dist/core/viewer.d.ts +44 -47
  7. package/dist/index.d.ts +5 -5
  8. package/dist/rendering/environment.d.ts +3 -19
  9. package/dist/rendering/highlight.d.ts +209 -0
  10. package/dist/rendering/id-picking.d.ts +464 -0
  11. package/dist/rendering/light-detection.d.ts +3 -3
  12. package/dist/rendering/material-factory.d.ts +11 -7
  13. package/dist/rendering/picked.d.ts +55 -0
  14. package/dist/rendering/studio-composer.d.ts +3 -3
  15. package/dist/rendering/tree-model.d.ts +1 -1
  16. package/dist/scene/clipping.d.ts +53 -0
  17. package/dist/scene/nestedgroup.d.ts +46 -4
  18. package/dist/scene/objectgroup.d.ts +17 -0
  19. package/dist/scene/render-shape.d.ts +3 -19
  20. package/dist/three-cad-viewer.css +54 -20
  21. package/dist/three-cad-viewer.esm.js +4867 -1562
  22. package/dist/three-cad-viewer.esm.js.map +1 -1
  23. package/dist/three-cad-viewer.esm.min.js +3 -3
  24. package/dist/three-cad-viewer.js +4867 -1562
  25. package/dist/three-cad-viewer.min.js +4 -4
  26. package/dist/tools/cad_tools/measure.d.ts +8 -9
  27. package/dist/tools/cad_tools/mesh-measure.d.ts +264 -0
  28. package/dist/tools/cad_tools/select.d.ts +4 -3
  29. package/dist/tools/cad_tools/tools.d.ts +17 -5
  30. package/dist/tools/cad_tools/ui.d.ts +8 -6
  31. package/dist/ui/display.d.ts +25 -2
  32. package/dist/utils/utils.d.ts +1 -1
  33. package/package.json +4 -1
  34. package/dist/rendering/raycast.d.ts +0 -111
@@ -1,6 +1,6 @@
1
1
  import * as THREE from "three";
2
2
  import { DistancePanel, PropertiesPanel, DistanceResponseData, PropertiesResponseData } from "./ui.js";
3
- import type { PickedObject } from "../../rendering/raycast.js";
3
+ import type { PickedComponent } from "../../rendering/picked.js";
4
4
  import type { ViewerLike } from "./tools.js";
5
5
  interface PanelDragData {
6
6
  x: number | null;
@@ -8,7 +8,7 @@ interface PanelDragData {
8
8
  clicked: boolean;
9
9
  }
10
10
  declare class Measurement {
11
- selectedShapes: PickedObject[];
11
+ selectedShapes: PickedComponent[];
12
12
  point1: THREE.Vector3 | null;
13
13
  point2: THREE.Vector3 | null;
14
14
  middlePoint: THREE.Vector3 | null;
@@ -26,7 +26,8 @@ declare class Measurement {
26
26
  coneLength: number | undefined;
27
27
  panelDragData: PanelDragData;
28
28
  shift: boolean;
29
- debug: boolean;
29
+ /** Monotonic request id; invalidates stale `_waitResponse` polls (see `_waitResponse`). */
30
+ _responseGen: number;
30
31
  constructor(viewer: ViewerLike, panel: DistancePanel | PropertiesPanel);
31
32
  enableContext(): void;
32
33
  disableContext(): void;
@@ -45,7 +46,7 @@ declare class Measurement {
45
46
  /**
46
47
  * Wait for the backend to send the data needed to display the real BREP measurement.
47
48
  */
48
- _waitResponse(resolve: (data: DistanceResponseData | PropertiesResponseData) => void, _reject: (reason?: Error) => void): void;
49
+ _waitResponse(resolve: (data: DistanceResponseData | PropertiesResponseData) => void, _reject: (reason?: Error) => void, gen: number): void;
49
50
  /**
50
51
  * Update the measurement panel, if enough shapes have been selected for the current tool,
51
52
  * ask the backend for the real measurement data and display it.
@@ -54,7 +55,7 @@ declare class Measurement {
54
55
  /**
55
56
  * React to each new selected element in the viewer.
56
57
  */
57
- handleSelection: (selectedObj: PickedObject, shift?: boolean) => void;
58
+ handleSelection: (selectedObj: PickedComponent, shift?: boolean) => void;
58
59
  _mouseup: (e: MouseEvent) => void;
59
60
  _movePanel: () => void;
60
61
  /**
@@ -71,8 +72,7 @@ declare class Measurement {
71
72
  dispose(): void;
72
73
  }
73
74
  declare class DistanceMeasurement extends Measurement {
74
- debug: boolean;
75
- constructor(viewer: ViewerLike, debug: boolean);
75
+ constructor(viewer: ViewerLike);
76
76
  _createPanel(): void;
77
77
  _getMaxObjSelected(): number;
78
78
  _getPoints(): void;
@@ -84,8 +84,7 @@ declare class DistanceMeasurement extends Measurement {
84
84
  handleResponse(response: DistanceResponseData): void;
85
85
  }
86
86
  declare class PropertiesMeasurement extends Measurement {
87
- debug: boolean;
88
- constructor(viewer: ViewerLike, debug: boolean);
87
+ constructor(viewer: ViewerLike);
89
88
  _createPanel(): void;
90
89
  _getMaxObjSelected(): number;
91
90
  _getPoint(): void;
@@ -0,0 +1,264 @@
1
+ import * as THREE from "three";
2
+ import type { TopoType, ComponentInfo } from "../../rendering/id-picking.js";
3
+ import type { ToolType } from "./tools.js";
4
+ /**
5
+ * A measurement response. Carries both `subtype` (so the viewer's
6
+ * `handleBackendResponse` routes it like a Python response) and `tool_type` (so
7
+ * `Tools.handleResponse` dispatches to the right measurement). The remaining fields
8
+ * (`result`, `refpoint*`, `shape_type`, `geom_type`, …) mirror the Python backend.
9
+ */
10
+ export interface MeasureResponse {
11
+ type: "backend_response";
12
+ subtype: "tool_response";
13
+ tool_type: ToolType;
14
+ [key: string]: unknown;
15
+ }
16
+ /**
17
+ * TypeScript mesh-based measurement backend.
18
+ *
19
+ * Computes meaningful measurements (area, length, volume, bounding box, min/center
20
+ * distance, angle) from the tessellated mesh, so the measure tools work WITHOUT the
21
+ * external Python (`ocp_vscode`) backend — the default when developing three-cad-viewer
22
+ * standalone. Selected via the `externalMeasurementBackend` option (default `false` =
23
+ * this backend; `ocp_vscode` sets `true` to use Python). See MeshBackend.md.
24
+ *
25
+ * Accuracy: `shape_type` / `geom_type` are EXACT (carried per-component in the
26
+ * tessellation `face_types` / `edge_types`). Numeric values are mesh-accurate — exact
27
+ * for planar faces / straight edges, within tessellation deflection for curved geometry.
28
+ *
29
+ * This module is the FROZEN contract (APPROACH.md "contracts-first"): the GeomAbs name
30
+ * tables, the {@link MeshComponentGeometry} shape the provider yields, the
31
+ * {@link MeshGeometryProvider} interface, and the pure geometry math operate on
32
+ * world-space coordinates (the provider applies each node's transform).
33
+ */
34
+ /** GeomAbs_SurfaceType ordinals → name (face_types). */
35
+ export declare const SURFACE_TYPE_NAMES: readonly ["Plane", "Cylinder", "Cone", "Sphere", "Torus", "BezierSurface", "BSplineSurface", "SurfaceOfRevolution", "SurfaceOfExtrusion", "OffsetSurface", "OtherSurface"];
36
+ /** GeomAbs_CurveType ordinals → name (edge_types). */
37
+ export declare const CURVE_TYPE_NAMES: readonly ["Line", "Circle", "Ellipse", "Hyperbola", "Parabola", "BezierCurve", "BSplineCurve", "OffsetCurve", "OtherCurve"];
38
+ /** Resolve a face's `face_types` code to its geom_type name (`"Other"` if unknown). */
39
+ export declare function faceGeomType(code: number): string;
40
+ /** Resolve an edge's `edge_types` code to its geom_type name (`"Other"` if unknown). */
41
+ export declare function edgeGeomType(code: number): string;
42
+ /**
43
+ * Short, lowercase geom_type for a status-line readout. Drops the trailing
44
+ * `Curve`/`Surface` suffix (`BSplineSurface`/`BSplineCurve` → `bspline`,
45
+ * `Plane` → `plane`, `Line` → `line`) and spaces the remaining CamelCase words
46
+ * for readability (`SurfaceOfExtrusion` → `surface of extrusion`,
47
+ * `SurfaceOfRevolution` → `surface of revolution`). The lowercase→uppercase
48
+ * split leaves single words and the leading-caps `BSpline` untouched. Topo
49
+ * selects the surface vs curve table.
50
+ */
51
+ export declare function displayGeomType(topo: TopoType, code: number): string;
52
+ /**
53
+ * Mesh geometry for one resolved component, in WORLD space (the provider has already
54
+ * applied the owning node's transform). Discriminated by {@link topo}:
55
+ * - `face` / `solid`: {@link positions} is a vertex pool, {@link indices} are triangle
56
+ * index triples into it (a solid merges all its faces).
57
+ * - `edge`: {@link positions} is a flat list of segment endpoint pairs (6 floats per
58
+ * segment: `x0,y0,z0, x1,y1,z1`); {@link indices} is undefined.
59
+ * - `vertex`: {@link positions} is exactly one point (3 floats); {@link indices} undefined.
60
+ */
61
+ export interface MeshComponentGeometry {
62
+ topo: TopoType;
63
+ /** Raw GeomAbs code (`face_types`/`edge_types` entry); `-1` for vertex/solid. */
64
+ geomType: number;
65
+ /** World-space coordinates (layout depends on {@link topo}; see interface doc). */
66
+ positions: Float32Array;
67
+ /** Triangle index triples into {@link positions} (face/solid only). */
68
+ indices?: Uint32Array;
69
+ }
70
+ /**
71
+ * Resolves a backend component path (e.g. `"/box/faces/faces_3"`, `"/box/edges/edges_1"`,
72
+ * `"/box/vertices/vertices_0"`, or a bare solid path `"/box"`) to its world-space mesh
73
+ * geometry. Returns `null` for unknown paths or components with no captured geometry.
74
+ * Populated at tessellation time (see Phase task: render-shape / nestedgroup).
75
+ */
76
+ export interface MeshGeometryProvider {
77
+ resolve(path: string): MeshComponentGeometry | null;
78
+ }
79
+ /**
80
+ * Decomposition of a component into closest-distance primitives. The minimum distance
81
+ * between two triangle meshes is always realized by a vertex-vs-triangle or an
82
+ * edge-vs-edge pair, so these three lists are sufficient (and exact for the mesh).
83
+ */
84
+ export interface MeshFeatures {
85
+ /** All vertices (world space). */
86
+ points: THREE.Vector3[];
87
+ /** Triangles as [a, b, c] vertex triples (face/solid). */
88
+ tris: [THREE.Vector3, THREE.Vector3, THREE.Vector3][];
89
+ /** Line segments as [a, b] endpoint pairs (edges, and triangle edges of faces). */
90
+ segs: [THREE.Vector3, THREE.Vector3][];
91
+ }
92
+ /** Build the {@link MeshFeatures} primitive sets from a component's geometry. */
93
+ export declare function buildFeatures(geom: MeshComponentGeometry): MeshFeatures;
94
+ /** Total surface area of a triangulated face/solid (Σ ½·|(b−a)×(c−a)|). */
95
+ export declare function triangulatedArea(geom: MeshComponentGeometry): number;
96
+ /** Total length of an edge polyline (Σ segment lengths). */
97
+ export declare function polylineLength(geom: MeshComponentGeometry): number;
98
+ /**
99
+ * Signed volume of a closed triangulated solid via the divergence theorem
100
+ * (Σ (1/6)·a·(b×c) over triangles). The tessellation winds triangles CCW outward, so
101
+ * the sum is positive; returned as an absolute value for robustness.
102
+ */
103
+ export declare function meshVolume(geom: MeshComponentGeometry): number;
104
+ /**
105
+ * Exact circle (center + radius) through three well-separated points of a circular
106
+ * edge's tessellation polyline. The tessellation vertices lie ON the true curve, so
107
+ * the circumcircle of any three is exact (up to float precision). `positions` is the
108
+ * flat segment-endpoint list (works for both arcs and closed circles). Returns `null`
109
+ * if the three points are degenerate/collinear.
110
+ */
111
+ export declare function circleFromPolyline(positions: Float32Array): {
112
+ center: number[];
113
+ radius: number;
114
+ } | null;
115
+ /**
116
+ * Status-line text for a hovered component — FIXED attributes only, no backend, no
117
+ * live cursor coord, NO path (the object is identified via the tree on double-click).
118
+ * Mesh-estimated length/area/volume are shown with `≈` (BRep-grade values come from
119
+ * the measurement tool). face: `geom: area ≈ A`; edge: `geom: len ≈ L, start/end`;
120
+ * vertex: coords; solid: `solid: N faces, M edges, vol ≈ V`.
121
+ */
122
+ export declare function hoverStatusText(info: ComponentInfo, fromSolid: boolean, provider: MeshGeometrySource): string;
123
+ /** Axis-aligned bounding box of a component's vertices. */
124
+ export declare function boundingBox(geom: MeshComponentGeometry): {
125
+ min: number[];
126
+ center: number[];
127
+ max: number[];
128
+ size: number[];
129
+ };
130
+ /**
131
+ * Representative center point of a component (mesh approximation of `refpoint`):
132
+ * - vertex → the point;
133
+ * - edge → length-weighted midpoint of its segments;
134
+ * - face → area-weighted centroid of its triangles;
135
+ * - solid → volume-weighted centroid of its tetrahedra (apex at origin).
136
+ */
137
+ export declare function centroid(geom: MeshComponentGeometry): THREE.Vector3;
138
+ /** Area-weighted average triangle normal of a face (unit length), or `null`. */
139
+ export declare function averageNormal(geom: MeshComponentGeometry): THREE.Vector3 | null;
140
+ /** Overall direction of an edge (first → last endpoint, unit length), or `null`. */
141
+ export declare function edgeDirection(geom: MeshComponentGeometry): THREE.Vector3 | null;
142
+ /** Closest point on segment [a,b] to point p, written into `out`. */
143
+ export declare function closestPointOnSegment(p: THREE.Vector3, a: THREE.Vector3, b: THREE.Vector3, out: THREE.Vector3): THREE.Vector3;
144
+ /** Closest point on triangle (a,b,c) to point p, written into `out` (Ericson §5.1.5). */
145
+ export declare function closestPointOnTriangle(p: THREE.Vector3, a: THREE.Vector3, b: THREE.Vector3, c: THREE.Vector3, out: THREE.Vector3): THREE.Vector3;
146
+ /**
147
+ * Closest points between segments [p1,q1] and [p2,q2], written into `outA`/`outB`;
148
+ * returns the squared distance (Ericson §5.1.9).
149
+ */
150
+ export declare function closestSegmentSegment(p1: THREE.Vector3, q1: THREE.Vector3, p2: THREE.Vector3, q2: THREE.Vector3, outA: THREE.Vector3, outB: THREE.Vector3): number;
151
+ /**
152
+ * Brute-force minimum distance between two components and the realizing points.
153
+ * Evaluates every closest-feature pair, in BOTH argument orders, over the feature sets:
154
+ * point-vs-triangle, point-vs-segment, segment-vs-segment, and point-vs-point. This is
155
+ * the complete set of closest-feature pairs for triangle meshes and their lower-dim
156
+ * degenerations (edges = segments, vertices = points), so the result is exact for the
157
+ * mesh and independent of which component is passed first.
158
+ *
159
+ * O(Ta·Tb) — quadratic. {@link minDistance} is the production entry: it uses this
160
+ * directly for small components and the BVH branch-and-bound above the pair threshold.
161
+ * Kept exported as the reference oracle the BVH path is property-tested against.
162
+ */
163
+ export declare function minDistanceBrute(ga: MeshComponentGeometry, gb: MeshComponentGeometry): {
164
+ distance: number;
165
+ point1: THREE.Vector3;
166
+ point2: THREE.Vector3;
167
+ };
168
+ /**
169
+ * Minimum squared distance between triangle (ta,tb,tc) and segment (sa,sb), with the
170
+ * realizing points (on the triangle → `outTri`, on the segment → `outSeg`). Complete
171
+ * set: each segment endpoint vs the triangle (covers face interior + edges + vertices)
172
+ * and each triangle edge vs the segment.
173
+ */
174
+ export declare function triSegDistance(ta: THREE.Vector3, tb: THREE.Vector3, tc: THREE.Vector3, sa: THREE.Vector3, sb: THREE.Vector3, outTri: THREE.Vector3, outSeg: THREE.Vector3): number;
175
+ /**
176
+ * Minimum squared distance between triangles (a0,a1,a2) and (b0,b1,b2), with the
177
+ * realizing points (`outA` on the first, `outB` on the second). Complete set for
178
+ * disjoint triangles: each vertex vs the other triangle, plus all 9 edge-edge pairs.
179
+ * (Interpenetrating triangles can report a small positive value rather than 0 — the
180
+ * same limitation the brute oracle has, and irrelevant for distance between surfaces.)
181
+ */
182
+ export declare function triTriDistance(a0: THREE.Vector3, a1: THREE.Vector3, a2: THREE.Vector3, b0: THREE.Vector3, b1: THREE.Vector3, b2: THREE.Vector3, outA: THREE.Vector3, outB: THREE.Vector3): number;
183
+ /**
184
+ * Minimum distance between two components and the realizing points — the production
185
+ * entry. Exact and sub-quadratic via a per-component AABB tree + branch-and-bound
186
+ * (see {@link minDistanceBrute} for the O(Ta·Tb) reference it is property-tested
187
+ * against, and which it delegates to for small inputs). Result is identical to the
188
+ * brute force and independent of argument order.
189
+ */
190
+ export declare function minDistance(ga: MeshComponentGeometry, gb: MeshComponentGeometry): {
191
+ distance: number;
192
+ point1: THREE.Vector3;
193
+ point2: THREE.Vector3;
194
+ };
195
+ /** Angle (degrees) between two unit directions. */
196
+ export declare function angleBetween(d1: THREE.Vector3, d2: THREE.Vector3): number;
197
+ /**
198
+ * Angle (degrees) of a face normal or edge tangent to the world XY plane (+Z up),
199
+ * mirroring `measure.py` `angle to XY`: for a face normal, the angle to +Z; for an edge
200
+ * direction `abs(90 − angle(dir, +Z))`.
201
+ */
202
+ export declare function angleToXY(dir: THREE.Vector3, isEdge: boolean): number;
203
+ /** Raw per-node tessellation arrays the source needs (subset of `Shape`). */
204
+ export interface RawNodeShape {
205
+ vertices?: number[] | number[][] | Float32Array | undefined;
206
+ triangles?: number[] | number[][] | Uint32Array | undefined;
207
+ triangles_per_face?: number[] | Uint32Array | undefined;
208
+ edges?: number[] | number[][] | Float32Array | undefined;
209
+ segments_per_edge?: number[] | Uint32Array | undefined;
210
+ obj_vertices?: number[] | Float32Array | undefined;
211
+ face_types?: number[] | Uint32Array | undefined;
212
+ edge_types?: number[] | Uint8Array | Uint32Array | undefined;
213
+ }
214
+ /**
215
+ * Populated as the compact `NestedGroup` tessellates each node (faces/edges/vertices),
216
+ * keyed by the node's `/`-path — the SAME paths the `ComponentRegistry` uses, so a
217
+ * backend component id resolves directly. Mode-independent: it stores the raw per-node
218
+ * arrays + the owning group (for `matrixWorld`) and slices/transforms on demand.
219
+ */
220
+ export declare class MeshGeometrySource implements MeshGeometryProvider {
221
+ private nodes;
222
+ constructor();
223
+ /** Record a node's raw tessellation arrays. `object` provides the world transform. */
224
+ register(path: string, shape: RawNodeShape, object: THREE.Object3D, subtype: string | null): void;
225
+ /** Drop all entries (called from `NestedGroup.clear`/`dispose`). */
226
+ clear(): void;
227
+ /**
228
+ * Drop nodes whose path is `prefix` or lies under it (`prefix + "/"`), for
229
+ * {@link Viewer.removePart} — keeps the provider from growing across remove/add
230
+ * cycles.
231
+ */
232
+ removeByPathPrefix(prefix: string): void;
233
+ /**
234
+ * Static face/edge counts of a node from its tessellation type arrays
235
+ * (`len(face_types)` / `len(edge_types)`). For a solid node these are the solid's
236
+ * total face/edge counts. `null` for an unknown node.
237
+ */
238
+ nodeCounts(path: string): {
239
+ faces: number;
240
+ edges: number;
241
+ } | null;
242
+ resolve(path: string): MeshComponentGeometry | null;
243
+ private _typeAt;
244
+ /** Index triples for face `i` into the vertex pool (flat-with-counts or nested). */
245
+ private _faceTriangleIndices;
246
+ /** All triangle index triples (every face) for a solid. */
247
+ private _allTriangleIndices;
248
+ /** World-local segment endpoint pairs (6 floats/segment) for edge `i`. */
249
+ private _edgeSegments;
250
+ }
251
+ /**
252
+ * Computes measurement responses from the mesh (no external Python backend). The
253
+ * default measurement backend when `externalMeasurementBackend === false`. Returns a
254
+ * `ToolResponse` ready for `viewer.handleBackendResponse`, or `null` if a component
255
+ * can't be resolved (caller leaves the panel unanswered, same as a backend timeout).
256
+ */
257
+ export declare class MeshMeasureBackend {
258
+ private getProvider;
259
+ constructor(getProvider: () => MeshGeometryProvider | null);
260
+ /** PropertiesMeasurement response for a single component. */
261
+ properties(path: string): MeasureResponse | null;
262
+ /** DistanceMeasurement response between two components. */
263
+ distance(path1: string, path2: string, center: boolean): MeasureResponse | null;
264
+ }
@@ -1,17 +1,18 @@
1
- import type { PickedObject } from "../../rendering/raycast.js";
1
+ import type { PickedComponent } from "../../rendering/picked.js";
2
2
  import type { ViewerLike } from "./tools.js";
3
3
  declare class SelectObject {
4
4
  viewer: ViewerLike;
5
- selectedShapes: PickedObject[];
5
+ selectedShapes: PickedComponent[];
6
6
  contextEnabled: boolean;
7
7
  constructor(viewer: ViewerLike);
8
8
  enableContext(): void;
9
9
  disableContext(): void;
10
10
  _getMaxObjSelected(): null;
11
+ /** Numeric sub-index from a leaf name like "faces_3" → "3". */
11
12
  private _getIndex;
12
13
  private _includes;
13
14
  notify(): void;
14
- handleSelection(selectedObj: PickedObject | null): void;
15
+ handleSelection(selectedObj: PickedComponent | null): void;
15
16
  private _removeLastSelectedObj;
16
17
  removeLastSelectedObj(force: boolean): void;
17
18
  update(): void;
@@ -1,7 +1,8 @@
1
1
  import * as THREE from "three";
2
2
  import { DistanceMeasurement, PropertiesMeasurement } from "./measure.js";
3
3
  import { SelectObject } from "./select.js";
4
- import type { PickedObject } from "../../rendering/raycast.js";
4
+ import type { PickedComponent } from "../../rendering/picked.js";
5
+ import type { MeshMeasureBackend, MeasureResponse } from "./mesh-measure.js";
5
6
  /**
6
7
  * Enum representing tool types.
7
8
  */
@@ -11,7 +12,7 @@ export declare const ToolTypes: {
11
12
  readonly PROPERTIES: "PropertiesMeasurement";
12
13
  readonly SELECT: "SelectObjects";
13
14
  };
14
- export type ToolType = typeof ToolTypes[keyof typeof ToolTypes];
15
+ export type ToolType = (typeof ToolTypes)[keyof typeof ToolTypes];
15
16
  /**
16
17
  * UI elements for measurement panels.
17
18
  */
@@ -66,6 +67,10 @@ export interface ViewerLike {
66
67
  ortho: boolean;
67
68
  bb_radius: number;
68
69
  checkChanges(changes: Record<string, unknown>, notify?: boolean): void;
70
+ /** Internal mesh measurement backend (answers when no external/Python backend). */
71
+ meshBackend: MeshMeasureBackend;
72
+ /** Dispatch a (mesh or backend) measurement response to the tools. */
73
+ handleBackendResponse(response: MeasureResponse): void;
69
74
  }
70
75
  export interface ToolResponse {
71
76
  tool_type: ToolType;
@@ -77,7 +82,7 @@ export declare class Tools {
77
82
  propertiesMeasurement: PropertiesMeasurement;
78
83
  selectObject: SelectObject;
79
84
  enabledTool: ToolType | null;
80
- constructor(viewer: ViewerLike, debug: boolean);
85
+ constructor(viewer: ViewerLike);
81
86
  /**
82
87
  * Enables a specific tool. (Disables the currently enabled tool if any)
83
88
  */
@@ -89,10 +94,17 @@ export declare class Tools {
89
94
  _disable(): void;
90
95
  handleRemoveLastSelection(force?: boolean): void;
91
96
  /**
92
- * Handle selected object from raycaster.
97
+ * Handle a selected object from the picker.
93
98
  */
94
- handleSelectedObj(selectedObj: PickedObject, isNewObject: boolean, shift: boolean): void;
99
+ handleSelectedObj(selectedObj: PickedComponent, isNewObject: boolean, shift: boolean): void;
95
100
  handleResetSelection(): void;
101
+ /**
102
+ * Compute and dispatch a measurement response from the internal mesh backend for the
103
+ * active measure tool. `payload` is `[...selectedPaths, shift]` (the same array
104
+ * measure sends to the Python backend): Distance = 2 paths + shift, Properties = 1
105
+ * path + shift. No-op for any other tool / shape count.
106
+ */
107
+ answerMeasurement(payload: unknown): void;
96
108
  /**
97
109
  * Handle the response from the backend.
98
110
  */
@@ -1,4 +1,4 @@
1
- import { Raycaster } from "../../rendering/raycast.js";
1
+ import { type TopoFilterType } from "../../rendering/id-picking.js";
2
2
  import type { DisplayLike } from "./tools.js";
3
3
  interface CallbackEntry {
4
4
  callback: EventListener;
@@ -12,6 +12,8 @@ declare abstract class Panel {
12
12
  constructor(display: DisplayLike);
13
13
  private removeTable;
14
14
  protected resetTable(): void;
15
+ /** Set the panel title (e.g. "Distance" or "Distance (mesh based)"). */
16
+ protected setHeader(text: string): void;
15
17
  protected abstract getHtmlElement(): HTMLElement;
16
18
  /**
17
19
  * Show or hide the panel
@@ -60,15 +62,15 @@ declare class PropertiesPanel extends Panel {
60
62
  declare class FilterByDropDownMenu {
61
63
  private display;
62
64
  private elements;
63
- private raycaster;
64
65
  /**
65
- * Initialize a new filter drop down menu, it needs the raycast to update interactively the filter mode
66
+ * The current topo filter the GPU id picker reads. `[TopoFilter.none]` means
67
+ * "no filter" (all topos eligible).
66
68
  */
67
- constructor(display: DisplayLike);
69
+ currentFilter: TopoFilterType[];
68
70
  /**
69
- * Set the raycaster to update the filter mode
71
+ * Initialize a new filter drop down menu.
70
72
  */
71
- setRaycaster(raycaster: Raycaster): void;
73
+ constructor(display: DisplayLike);
72
74
  private setValue;
73
75
  private toggleDropdown;
74
76
  private closeDropdown;
@@ -14,7 +14,7 @@ import type { ThemeInput, ClipIndex } from "../core/types.js";
14
14
  */
15
15
  export interface DisplayOptions {
16
16
  measureTools: boolean;
17
- measurementDebug: boolean;
17
+ externalMeasurementBackend: boolean;
18
18
  selectTool: boolean;
19
19
  explodeTool: boolean;
20
20
  zscaleTool: boolean;
@@ -100,6 +100,7 @@ declare class Display {
100
100
  container: HTMLElement;
101
101
  cadBody: HTMLElement;
102
102
  cadView: HTMLElement;
103
+ statusLine: HTMLElement;
103
104
  cadTree: HTMLElement;
104
105
  cadTreeScrollContainer: HTMLElement;
105
106
  cadTreeToggles: HTMLElement;
@@ -158,7 +159,6 @@ declare class Display {
158
159
  viewer: Viewer;
159
160
  state: ViewerState;
160
161
  measureTools: boolean;
161
- measurementDebug: boolean;
162
162
  selectTool: boolean;
163
163
  explodeTool: boolean;
164
164
  zscaleTool: boolean;
@@ -247,6 +247,12 @@ declare class Display {
247
247
  * @param center - The center coordinates [x, y, z].
248
248
  */
249
249
  showCenterInfo(center: Vector3Tuple): void;
250
+ /**
251
+ * Set the hover status line (preselection readout). Empty string clears it.
252
+ * A rounded overlay badge stacked below the grid-size indicator at the bottom
253
+ * left of the canvas; hidden entirely when empty so no empty badge shows.
254
+ */
255
+ setStatusLine(text: string): void;
250
256
  /**
251
257
  * Display bounding box information for a selected object.
252
258
  * @param path - The object's path in the tree.
@@ -389,6 +395,16 @@ declare class Display {
389
395
  * @internal
390
396
  */
391
397
  private _restoreToolsAfterStudio;
398
+ /**
399
+ * Entering Clip mode: hide the measure + select tool buttons (a measure/select
400
+ * tool can't be active here — it disables the clip tab — but the buttons must not
401
+ * be invocable while clipping). Mirrors {@link _deactivateToolsForStudio};
402
+ * explode/zscale stay enabled for consistency with studio mode. Restored by
403
+ * {@link _restoreToolsAfterClip} on leave.
404
+ */
405
+ private _deactivateToolsForClip;
406
+ /** Leaving Clip mode: restore measure + select buttons per their feature flags. */
407
+ private _restoreToolsAfterClip;
392
408
  /**
393
409
  * Checkbox Handler for setting the clip planes parameter
394
410
  */
@@ -668,6 +684,13 @@ declare class Display {
668
684
  * Show or hide tools panel (tabs + content) in glass mode.
669
685
  * Also toggles the orientation marker and animation/explode slider.
670
686
  */
687
+ /**
688
+ * Show the shape-filter dropdown only for a B-rep model with the tools panel open:
689
+ * `tools` enabled AND not collapsed AND a non-GDS model is rendered. Hidden otherwise
690
+ * (clean canvas; also detaches the a/v/e/f/s keyboard shortcuts). Hover preselection
691
+ * is always-on for B-rep, so the filter is independent of which tool (if any) is active.
692
+ */
693
+ updateShapeFilter: () => void;
671
694
  showToolsPanel: (flag: boolean) => void;
672
695
  /**
673
696
  * Toggle tools panel visibility
@@ -143,4 +143,4 @@ declare class EventListenerManager {
143
143
  dispose(): void;
144
144
  }
145
145
  export { flatten, isEqual, sceneTraverse, prettyPrintVector, KeyMapper, scaleLight, deepDispose, disposeGeometry, EventListenerManager, isMesh, isLine, isPoints, isOrthographicCamera, isPerspectiveCamera, isLineSegments2, hasColor, hasEmissive, isMeshStandardMaterial, toVector3Tuple, toQuaternionTuple, };
146
- export type { KeyEventKey, KeyMappingConfig, DisposableTree, };
146
+ export type { KeyEventKey, KeyMappingConfig, DisposableTree };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "three-cad-viewer",
3
- "version": "4.3.8",
3
+ "version": "5.0.0",
4
4
  "type": "module",
5
5
  "description": "WebGL-based CAD viewer built on Three.js with clipping planes, measurement tools, and tree navigation",
6
6
  "repository": {
@@ -40,6 +40,8 @@
40
40
  "docs": "./scripts/build_docs.sh",
41
41
  "release": "./scripts/release.sh",
42
42
  "lint": "eslint src/",
43
+ "format": "prettier --write \"src/**/*.{ts,js,css}\" \"tests/**/*.{ts,js}\" \"*.{html,md,json}\" \".prettierrc\" \".vscode/*.json\"",
44
+ "format:check": "prettier --check \"src/**/*.{ts,js,css}\" \"tests/**/*.{ts,js}\" \"*.{html,md,json}\" \".prettierrc\" \".vscode/*.json\"",
43
45
  "test": "vitest",
44
46
  "test:ui": "vitest --ui",
45
47
  "test:run": "vitest run",
@@ -56,6 +58,7 @@
56
58
  "devDependencies": {
57
59
  "@babel/core": "7.29.0",
58
60
  "@babel/eslint-parser": "7.28.6",
61
+ "@eslint/js": "10.0.1",
59
62
  "@rollup/plugin-image": "3.0.3",
60
63
  "@rollup/plugin-node-resolve": "16.0.3",
61
64
  "@rollup/plugin-terser": "1.0.0",
@@ -1,111 +0,0 @@
1
- import * as THREE from "three";
2
- import { type ObjectGroup } from "../scene/objectgroup.js";
3
- import type { Camera } from "../camera/camera.js";
4
- /**
5
- * Filter types for topology-based raycasting.
6
- */
7
- export declare const TopoFilter: {
8
- none: null;
9
- vertex: "vertex";
10
- edge: "edge";
11
- face: "face";
12
- solid: "solid";
13
- };
14
- export type TopoFilterType = typeof TopoFilter[keyof typeof TopoFilter];
15
- interface RaycastFilters {
16
- topoFilter: TopoFilterType[];
17
- }
18
- interface RaycastCallback {
19
- (event: {
20
- mouse?: "left" | "right";
21
- shift?: boolean;
22
- key?: string;
23
- }): void;
24
- }
25
- /**
26
- * Represents a picked object from raycasting.
27
- * Can represent either a single shape or all faces of a solid.
28
- */
29
- export declare class PickedObject {
30
- obj: ObjectGroup;
31
- fromSolid: boolean;
32
- /**
33
- * Create a PickedObject.
34
- * @param objectGroup - The picked ObjectGroup.
35
- * @param fromSolid - Whether this pick is from a solid selection.
36
- */
37
- constructor(objectGroup: ObjectGroup, fromSolid: boolean);
38
- /**
39
- * Returns all the faces ObjectGroups that define the solid from the picked object.
40
- */
41
- private _getSolidObjectGroups;
42
- /**
43
- * If the picked object is part of a solid, returns all the faces ObjectGroups that define the solid.
44
- * Otherwise, returns the picked object.
45
- */
46
- objs(): ObjectGroup[];
47
- }
48
- /**
49
- * Handles mouse-based raycasting for object selection in the 3D scene.
50
- * Supports topology filtering and provides click/keyboard callbacks.
51
- */
52
- declare class Raycaster {
53
- camera: Camera | null;
54
- group: THREE.Object3D | null;
55
- domElement: HTMLElement | null;
56
- width: number;
57
- height: number;
58
- threshold: number;
59
- callback: RaycastCallback;
60
- raycaster: THREE.Raycaster;
61
- raycastMode: boolean;
62
- lastPosition: THREE.Vector3 | null;
63
- mouse: THREE.Vector2;
64
- mouseMoved: boolean;
65
- filters: RaycastFilters;
66
- /**
67
- * Create a Raycaster for object picking.
68
- * @param camera - The camera used for ray projection.
69
- * @param domElement - The DOM element to listen for events.
70
- * @param width - Viewport width in pixels.
71
- * @param height - Viewport height in pixels.
72
- * @param threshold - Point picking threshold in world units.
73
- * @param group - The scene group to raycast against.
74
- * @param callback - Callback for pick events.
75
- */
76
- constructor(camera: Camera, domElement: HTMLElement, width: number, height: number, threshold: number, group: THREE.Object3D, callback: RaycastCallback);
77
- /**
78
- * Dispose of event listeners and clean up resources.
79
- */
80
- dispose(): void;
81
- /**
82
- * Initialize event listeners and enable raycast mode.
83
- */
84
- init(): void;
85
- /**
86
- * Retrieve all the valid intersected objects by a ray caster from the mouse.
87
- */
88
- getIntersectedObjs(): THREE.Intersection[];
89
- /**
90
- * Retrieve all the valid intersected objects by a ray caster from the mouse.
91
- * The objects are sorted by their distance from the ray. (The closest first)
92
- */
93
- getValidIntersectedObjs(): THREE.Intersection[];
94
- /**
95
- * Handle left mouse button down event
96
- */
97
- onMouseKeyDown: (e: MouseEvent) => void;
98
- /**
99
- * Handle left mouse button up event
100
- */
101
- onMouseKeyUp: (e: MouseEvent) => void;
102
- /**
103
- * Handle key down event
104
- */
105
- onKeyDown: (e: KeyboardEvent) => void;
106
- /**
107
- * Get the current mouse position
108
- */
109
- onPointerMove: (e: MouseEvent) => void;
110
- }
111
- export { Raycaster };