three-cad-viewer 4.3.9 → 5.0.1

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.
@@ -2,6 +2,9 @@ import * as THREE from "three";
2
2
  import { BoundingBox } from "./bbox.js";
3
3
  import { ObjectGroup, isObjectGroup } from "./objectgroup.js";
4
4
  import { MaterialFactory } from "../rendering/material-factory.js";
5
+ import { ComponentRegistry } from "../rendering/id-picking.js";
6
+ import { HighlightController } from "../rendering/highlight.js";
7
+ import { MeshGeometrySource } from "../tools/cad_tools/mesh-measure.js";
5
8
  import type { ZebraColorScheme, ZebraMappingMode, StudioTextureMapping, Shapes, ColorValue, MaterialAppearance, MaterialXMaterial } from "../core/types";
6
9
  interface ShapeData {
7
10
  vertices: Float32Array | number[][];
@@ -9,9 +12,16 @@ interface ShapeData {
9
12
  triangles: Uint32Array | number[][];
10
13
  edges?: Float32Array | number[][];
11
14
  uvs?: Float32Array | number[];
15
+ triangles_per_face?: number[] | Uint32Array;
16
+ segments_per_edge?: number[] | Uint32Array;
17
+ obj_vertices?: Float32Array | number[];
18
+ face_types?: number[] | Uint32Array;
19
+ edge_types?: number[] | Uint8Array | Uint32Array;
12
20
  }
13
21
  interface EdgeData {
14
22
  edges: Float32Array | number[][];
23
+ segments_per_edge?: number[] | Uint32Array;
24
+ obj_vertices?: Float32Array | number[];
15
25
  }
16
26
  interface VertexData {
17
27
  obj_vertices: Float32Array | number[];
@@ -37,7 +47,6 @@ interface ShapeEntry {
37
47
  state: number[];
38
48
  loc?: [[number, number, number], [number, number, number, number]];
39
49
  renderback?: boolean;
40
- exploded?: boolean;
41
50
  geomtype?: number | null;
42
51
  subtype?: string | null;
43
52
  texture?: {
@@ -77,6 +86,24 @@ declare class NestedGroup {
77
86
  bbox: BoundingBox | null;
78
87
  bsphere: THREE.Sphere | null;
79
88
  groups: GroupsMap;
89
+ /**
90
+ * id-based-picking component registry: maps each per-vertex `componentId` to its
91
+ * `ComponentInfo` for the GPU picker (populated when `assignIds === true`).
92
+ */
93
+ registry: ComponentRegistry;
94
+ /** Whether to assign component ids while rendering. */
95
+ assignIds: boolean;
96
+ /**
97
+ * Component-level shader highlight: patches visual materials to tint/widen by
98
+ * `componentId` (created when `assignIds`).
99
+ */
100
+ highlight: HighlightController | null;
101
+ /**
102
+ * Per-node mesh geometry for the internal (TypeScript) measurement backend.
103
+ * Populated for the compact group only (`assignIds`), keyed by node path like the
104
+ * {@link registry}; lets measurements be computed without the Python backend.
105
+ */
106
+ meshGeometry: MeshGeometrySource;
80
107
  clipPlanes: THREE.Plane[] | null;
81
108
  materialFactory: MaterialFactory;
82
109
  materialsTable: Record<string, string | MaterialXMaterial | MaterialAppearance> | null;
@@ -159,10 +186,25 @@ declare class NestedGroup {
159
186
  /**
160
187
  * Render a tessellated 3D shape with front/back faces and optional edges.
161
188
  */
162
- renderShape(shape: ShapeData, color: ColorValue, alpha: number | null, renderback: boolean, exploded: boolean, path: string, name: string, states: number[], geomtype?: {
189
+ renderShape(shape: ShapeData, color: ColorValue, alpha: number | null, renderback: boolean, path: string, name: string, states: number[], geomtype?: {
163
190
  topo: string;
164
191
  geomtype: number | string | null;
165
192
  } | null, subtype?: string | null, texture_data?: TextureData | null, texture_width?: number | null, texture_height?: number | null): ObjectGroup;
193
+ /**
194
+ * Build the pick-only B-rep-corner cloud (+ a selection-marker highlight Points) for a
195
+ * node carrying `obj_vertices` — a solid, a standalone face, or a standalone edge — so
196
+ * its corners are selectable via id-picking.
197
+ *
198
+ * The pick cloud lives on the VERTEX pick layer ONLY (NOT visual layer 0), so the visual
199
+ * camera never draws it; its material stays `visible:true` because three drops
200
+ * `material.visible===false` objects before `overrideMaterial` applies. Its pick-buffer
201
+ * visibility is kept in sync with the owning group (`setPickVertices`) so a hidden shape
202
+ * stops contributing corner ids.
203
+ *
204
+ * `subtype` is "solid" for a solid (→ the corner ids carry the owning `solidPath`, so the
205
+ * pick gates on faces||edges) and null for standalone faces/edges (→ own-node corners).
206
+ */
207
+ private _addPickVertices;
166
208
  /**
167
209
  * Create edge geometry from extruded polygons.
168
210
  */
@@ -170,14 +212,14 @@ declare class NestedGroup {
170
212
  /**
171
213
  * Render extruded 2D polygons (GDS format) as 3D geometry.
172
214
  */
173
- renderPolygons(shape: PolygonShape, minZ: number, color: ColorValue, alpha: number, renderback: boolean, _exploded: boolean, path: string, name: string, states: number[], geomtype?: {
215
+ renderPolygons(shape: PolygonShape, minZ: number, color: ColorValue, alpha: number, renderback: boolean, path: string, name: string, states: number[], geomtype?: {
174
216
  topo: string;
175
217
  geomtype: number | string | null;
176
218
  } | null, subtype?: string | null): ObjectGroup;
177
219
  /**
178
220
  * Recursively render all shapes in the shape tree.
179
221
  * Note: The shapes parameter uses the public Shapes type but internally
180
- * contains ShapeEntry/ShapeTree data after decomposition by viewer._decompose()
222
+ * contains ShapeEntry/ShapeTree data.
181
223
  */
182
224
  renderLoop(shapes: Shapes): THREE.Group;
183
225
  /**
@@ -61,6 +61,13 @@ declare class ObjectGroup extends THREE.Group {
61
61
  edges: Edges | null;
62
62
  edgeMaterial: EdgeMaterial | null;
63
63
  vertices: VertexPoints | null;
64
+ /**
65
+ * Pick-only `obj_vertices` cloud for a solid's B-rep corners (vertex pick layer,
66
+ * off the visual layer). Has its own always-visible material, so it is NOT covered by
67
+ * {@link setShapeVisible}/{@link setEdgesVisible} — its pick-buffer visibility is kept
68
+ * in sync explicitly so a hidden solid's corners don't keep stealing hover picks.
69
+ */
70
+ pickVertices: VertexPoints | null;
64
71
  clipping: Map<number, THREE.Group>;
65
72
  isSelected: boolean;
66
73
  originalColor: THREE.Color | null;
@@ -120,6 +127,16 @@ declare class ObjectGroup extends THREE.Group {
120
127
  * Set the vertices points.
121
128
  */
122
129
  setVertices(points: VertexPoints): void;
130
+ /**
131
+ * Register the pick-only B-rep-corner cloud (vertex pick layer). Its pick-buffer
132
+ * visibility is then kept in sync with the solid's overall visibility so a hidden
133
+ * solid stops contributing vertex ids to the pick buffer (otherwise its corners win
134
+ * the vertex>face priority over a visible face behind them → hover flicker).
135
+ */
136
+ setPickVertices(points: VertexPoints): void;
137
+ /** Sync the pick-only vertex cloud's visibility with the group's (pickable iff the
138
+ * front faces or edges are visible — matches the picker's hidden-component gate). */
139
+ private _syncPickVertices;
123
140
  /**
124
141
  * Add a clipping group for a plane index.
125
142
  */
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Shape tessellation and decomposition for rendering CAD objects.
2
+ * Shape tessellation for rendering CAD objects.
3
3
  */
4
4
  import { NestedGroup } from "./nestedgroup.js";
5
5
  import { BoundingBox } from "./bbox.js";
@@ -32,7 +32,7 @@ interface ShapeRenderConfig {
32
32
  normalLen: number;
33
33
  }
34
34
  /**
35
- * Handles tessellation and decomposition of CAD shapes for rendering.
35
+ * Handles tessellation of CAD shapes for rendering.
36
36
  */
37
37
  declare class ShapeRenderer {
38
38
  private config;
@@ -58,28 +58,12 @@ declare class ShapeRenderer {
58
58
  * @returns The navigation tree object.
59
59
  */
60
60
  private _getTree;
61
- /**
62
- * Decompose a CAD object into faces, edges and vertices.
63
- * @param part - The part to decompose.
64
- * @returns A decomposed part object.
65
- */
66
- private _decompose;
67
- /**
68
- * Convert Shape arrays to TypedArrays for efficient rendering.
69
- * Note: This mutates the shape in place.
70
- */
71
- private _convertArrays;
72
- /**
73
- * Recursively process shapes, converting arrays and decomposing parts.
74
- */
75
- private _processShapes;
76
61
  /**
77
62
  * Render the shapes of the CAD object.
78
- * @param exploded - Whether to render the compact or exploded version
79
63
  * @param shapes - The Shapes object.
80
64
  * @returns A nested THREE.Group object and navigation tree.
81
65
  */
82
- render(exploded: boolean, shapes: Shapes): RenderResult;
66
+ render(shapes: Shapes): RenderResult;
83
67
  }
84
68
  export { ShapeRenderer };
85
69
  export type { ShapeTreeData, RenderResult, ShapeRenderConfig };
@@ -258,7 +258,6 @@ input[type="radio"] {
258
258
  background-image: var(--tcv-icon-nav_closed);
259
259
  } */
260
260
 
261
-
262
261
  .tcv_cad_viewer {
263
262
  --tcv-ui-row-gap: 8px;
264
263
  margin: 0;
@@ -288,6 +287,30 @@ input[type="radio"] {
288
287
  margin: 2px;
289
288
  }
290
289
 
290
+ /* Hover preselection readout: a rounded overlay badge in a FIXED row directly
291
+ below the grid-size badge (.tcv_tick_size at bottom:22px), so the grid badge
292
+ never moves when this appears/vanishes. Left:84px clears the 80px orientation
293
+ marker — keep in sync with OrientationMarker(80,80) in viewer.ts. JS hides it
294
+ (display:none) when empty. Pure overlay — never intercepts pointer events. */
295
+ .tcv_status_line {
296
+ position: absolute;
297
+ bottom: 4px;
298
+ left: 84px;
299
+ max-width: calc(100% - 88px);
300
+ font-size: 12px;
301
+ line-height: 14px;
302
+ font-family: sans-serif;
303
+ color: var(--tcv-font-color);
304
+ background-color: var(--tcv-bg-overlay-rest-color);
305
+ border-radius: 4px;
306
+ padding: 1px 4px;
307
+ white-space: nowrap;
308
+ overflow: hidden;
309
+ text-overflow: ellipsis;
310
+ pointer-events: none;
311
+ z-index: 100;
312
+ }
313
+
291
314
  .tcv_cad_view_glass {
292
315
  position: absolute !important;
293
316
  left: 4px;
@@ -326,7 +349,7 @@ input[type="radio"] {
326
349
  font-family: sans-serif;
327
350
  font-size: 12px;
328
351
  overflow: hidden;
329
- user-select:text;
352
+ user-select: text;
330
353
  }
331
354
 
332
355
  .tcv_cad_info_wrapper {
@@ -418,7 +441,7 @@ input[type="radio"] {
418
441
  line-height: 14px;
419
442
  }
420
443
 
421
- .tcv_small_table_row>td,
444
+ .tcv_small_table_row > td,
422
445
  th {
423
446
  padding-left: 6px;
424
447
  }
@@ -856,7 +879,6 @@ input[type="button"] {
856
879
  margin-right: 0;
857
880
  }
858
881
 
859
-
860
882
  .tcv_zebra_radio {
861
883
  margin-bottom: var(--tcv-ui-row-gap);
862
884
  margin-left: -3px;
@@ -1107,7 +1129,7 @@ input[type="button"] {
1107
1129
  max-width: 80%;
1108
1130
  text-align: center;
1109
1131
  pointer-events: auto;
1110
- box-shadow: 0 1px 4px rgba(0,0,0,0.3);
1132
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
1111
1133
  }
1112
1134
 
1113
1135
  .tcv_studio_spinner {
@@ -1124,7 +1146,9 @@ input[type="button"] {
1124
1146
  }
1125
1147
 
1126
1148
  @keyframes tcv_spin {
1127
- to { transform: rotate(360deg); }
1149
+ to {
1150
+ transform: rotate(360deg);
1151
+ }
1128
1152
  }
1129
1153
 
1130
1154
  .tcv_filter_menu {
@@ -1144,10 +1168,13 @@ input[type="button"] {
1144
1168
  color: var(--tcv-font-color);
1145
1169
  font-size: 13px;
1146
1170
  border: none;
1147
- float: right;
1148
1171
  position: absolute;
1149
- right: 0px;
1150
- width: auto;
1172
+ right: -4px;
1173
+ top: -3px;
1174
+ /* Fixed width so the trigger pill and the option list share one right-anchored
1175
+ box — their right borders coincide (and line up with the panel edge). Wide
1176
+ enough for the longest option ("Vertex"). */
1177
+ width: 5em;
1151
1178
  }
1152
1179
 
1153
1180
  .tcv_shape_filter:hover {
@@ -1161,9 +1188,9 @@ input[type="button"] {
1161
1188
  }
1162
1189
 
1163
1190
  .tcv_filter_value {
1164
- width: 3em;
1191
+ flex: 1;
1192
+ text-align: left;
1165
1193
  padding-left: 3px;
1166
- display: inline-block;
1167
1194
  }
1168
1195
 
1169
1196
  .tcv_filter_dropdown_active {
@@ -1180,22 +1207,29 @@ input[type="button"] {
1180
1207
  }
1181
1208
 
1182
1209
  .tcv_filter_content {
1210
+ display: flex;
1211
+ align-items: center;
1212
+ justify-content: space-between;
1183
1213
  background-color: var(--tcv-bg-overlay-color);
1184
1214
  }
1185
1215
 
1186
- .tcv_tick_size{
1187
- background-color: var(--tcv-bg-overlay-color);
1216
+ /* Grid-size badge: FIXED bottom-left row, just above the status readout. Stays
1217
+ put whether or not the status line is shown (no jumping). */
1218
+ .tcv_tick_size {
1219
+ position: absolute;
1220
+ bottom: 22px;
1221
+ left: 84px;
1222
+ background-color: var(--tcv-bg-overlay-rest-color);
1188
1223
  border-radius: 4px;
1189
- padding:2px;
1190
- width:fit-content;
1224
+ padding: 1px 4px;
1225
+ width: fit-content;
1191
1226
  font-size: 12px;
1192
- margin-left:100px;
1193
- margin-top: -24px;
1194
- z-index: 1000;
1227
+ line-height: 14px;
1228
+ z-index: 100;
1195
1229
  }
1196
1230
 
1197
- .tcv_tick_size_value{
1198
- padding:3px;
1231
+ .tcv_tick_size_value {
1232
+ padding: 3px;
1199
1233
  }
1200
1234
 
1201
1235