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
@@ -3,8 +3,8 @@
3
3
  *
4
4
  * Analyzes equirectangular HDR pixel data to find dominant light sources
5
5
  * (softboxes in studio HDRs, sun in outdoor HDRs). Returns direction,
6
- * intensity, and color for up to 2 lights, used to create shadow-casting
7
- * DirectionalLights in Studio mode.
6
+ * intensity, and color for up to MAX_LIGHTS (currently 1), used to create the
7
+ * shadow-casting DirectionalLight in Studio mode.
8
8
  *
9
9
  * Algorithm: downsample to 128x64 luminance grid → threshold at 10x median
10
10
  * → flood-fill cluster → convert centroids to 3D direction vectors.
@@ -32,7 +32,7 @@ export interface LightDetectionResult {
32
32
  * @param data - Raw pixel data (Uint16Array for HalfFloat, or Float32Array)
33
33
  * @param width - HDR image width in pixels
34
34
  * @param height - HDR image height in pixels
35
- * @returns Detection result with up to 2 lights
35
+ * @returns Detection result with up to MAX_LIGHTS (currently 1) lights
36
36
  */
37
37
  export declare function detectDominantLights(data: Uint16Array | Float32Array, width: number, height: number): LightDetectionResult;
38
38
  /**
@@ -117,12 +117,12 @@ declare class MaterialFactory {
117
117
  * Create a standard material for back faces with PBR properties.
118
118
  * Used for polygon rendering where back faces need full shading.
119
119
  */
120
- createBackFaceStandardMaterial({ color, alpha, polygonOffsetUnits, visible }: BackFaceMaterialOptions, label?: string): THREE.MeshStandardMaterial;
120
+ createBackFaceStandardMaterial({ color, alpha, polygonOffsetUnits, visible, }: BackFaceMaterialOptions, label?: string): THREE.MeshStandardMaterial;
121
121
  /**
122
122
  * Create a basic material for back faces (no lighting/PBR).
123
123
  * Used for shape rendering where back faces are simple fills.
124
124
  */
125
- createBackFaceBasicMaterial({ color, alpha, polygonOffsetUnits, visible }: BackFaceMaterialOptions, label?: string): THREE.MeshBasicMaterial;
125
+ createBackFaceBasicMaterial({ color, alpha, polygonOffsetUnits, visible, }: BackFaceMaterialOptions, label?: string): THREE.MeshBasicMaterial;
126
126
  /**
127
127
  * Create a basic front face material (no PBR, for simple shapes).
128
128
  */
@@ -130,7 +130,7 @@ declare class MaterialFactory {
130
130
  /**
131
131
  * Create a fat line material (LineMaterial from Three.js examples).
132
132
  */
133
- createEdgeMaterial({ lineWidth, color, vertexColors, visible, resolution }: EdgeMaterialOptions, label?: string): LineMaterial;
133
+ createEdgeMaterial({ lineWidth, color, vertexColors, visible, resolution, }: EdgeMaterialOptions, label?: string): LineMaterial;
134
134
  /**
135
135
  * Create a basic line material for simple edges (e.g., polygon outlines).
136
136
  */
@@ -156,22 +156,26 @@ declare class MaterialFactory {
156
156
  * @param label - Optional label for GPU tracking
157
157
  * @returns Configured MeshPhysicalMaterial (or MeshBasicMaterial if unlit)
158
158
  */
159
- createStudioMaterial({ materialDef, fallbackColor, fallbackAlpha, textureCache }: StudioMaterialOptions, label?: string): Promise<THREE.MeshPhysicalMaterial | THREE.MeshBasicMaterial>;
159
+ createStudioMaterial({ materialDef, fallbackColor, fallbackAlpha, textureCache, }: StudioMaterialOptions, label?: string): Promise<THREE.MeshPhysicalMaterial | THREE.MeshBasicMaterial>;
160
160
  /**
161
161
  * Create a Studio mode material from a threejs-materials format entry.
162
162
  *
163
163
  * threejs-materials `properties` uses simplified property names (e.g., "color",
164
164
  * "roughness", "normal") where each entry has an optional `value` (scalar or
165
- * [r,g,b] array in **linear RGB**) and/or `texture` (inline data URI).
165
+ * [r,g,b] color array) and/or `texture` (inline data URI). Color arrays follow
166
+ * a per-key color-space convention: `color` is sRGB, the other color keys are
167
+ * linear (see the COLOR_ARRAY_KEYS handling in the values loop below).
166
168
  *
167
169
  * @param values - Scalar PBR values from threejs-materials
168
170
  * @param textures - Texture map references from threejs-materials
169
171
  * @param textureRepeat - Optional [u, v] texture tiling applied to all loaded textures
172
+ * @param textureRotation - Optional texture rotation in radians (counterclockwise),
173
+ * pivoting around the texture center (0.5, 0.5)
170
174
  * @param textureCache - TextureCache for resolving data URI textures
171
175
  * @param label - Optional label for GPU tracking
172
176
  * @returns Configured MeshPhysicalMaterial
173
177
  */
174
- createStudioMaterialFromMaterialX(values: Record<string, unknown>, textures: Record<string, string>, textureRepeat: [number, number] | undefined, textureCache: TextureCacheInterface | null, label?: string): Promise<THREE.MeshPhysicalMaterial>;
178
+ createStudioMaterialFromMaterialX(values: Record<string, unknown>, textures: Record<string, string>, textureRepeat: [number, number] | undefined, textureRotation: number | undefined, textureCache: TextureCacheInterface | null, label?: string): Promise<THREE.MeshPhysicalMaterial>;
175
179
  /**
176
180
  * Apply alpha mode settings to a material.
177
181
  *
@@ -201,4 +205,4 @@ declare class MaterialFactory {
201
205
  update(options: UpdateOptions): void;
202
206
  }
203
207
  export { MaterialFactory };
204
- export type { MaterialFactoryOptions, UpdateOptions, StudioMaterialOptions, TextureCacheInterface };
208
+ export type { MaterialFactoryOptions, UpdateOptions, StudioMaterialOptions, TextureCacheInterface, };
@@ -0,0 +1,55 @@
1
+ import type * as THREE from "three";
2
+ import type { ComponentInfo, TopoType } from "./id-picking.js";
3
+ import type { HighlightController } from "./highlight.js";
4
+ /**
5
+ * A picked component, so the viewer's selection state and the measure/select
6
+ * tools consume ONE interface. {@link IdPicked} wraps a registry `ComponentInfo`
7
+ * + the shader `HighlightController` (the GPU id-pick path) — NO dependency on the
8
+ * exploded scene graph.
9
+ */
10
+ export interface PickedComponent {
11
+ /** Canonical "/"-path sent to the backend (component path, or solid path when
12
+ * {@link fromSolid}). Replaces measure's `getId` + tree-sync name munging. */
13
+ readonly backendId: string;
14
+ /** Leaf name, e.g. "faces_3" — source for select.ts's numeric sub-index. */
15
+ readonly name: string;
16
+ /** Topology type of the resolved component. */
17
+ readonly topo: TopoType;
18
+ /** Solid-selection mode (the topo filter is "solid"). */
19
+ readonly fromSolid: boolean;
20
+ /** World-space hit point, when the backend provides one (else null). */
21
+ readonly point: THREE.Vector3 | null;
22
+ /** Show the hover highlight (`true`) — selection is via {@link toggleSelection}. */
23
+ highlight(asHover: boolean): void;
24
+ /** Remove the hover highlight; `keepSelected` preserves the selected state. */
25
+ unhighlight(keepSelected: boolean): void;
26
+ /** Toggle the persistent selected highlight. */
27
+ toggleSelection(): void;
28
+ /** Clear all highlight (hover + selected) for this component. */
29
+ clearHighlights(): void;
30
+ /** Identity equality (same component / same solid), for hover + isNewObject dedup. */
31
+ equals(other: PickedComponent | null): boolean;
32
+ }
33
+ /**
34
+ * `PickedComponent` over a GPU id-pick result. Drives the shader
35
+ * `HighlightController` by `componentId` (faces of a solid via `*Solid`), with no
36
+ * per-component `ObjectGroup` and no exploded graph.
37
+ */
38
+ export declare class IdPicked implements PickedComponent {
39
+ readonly info: ComponentInfo;
40
+ readonly fromSolid: boolean;
41
+ readonly point: THREE.Vector3 | null;
42
+ private readonly highlightCtl;
43
+ constructor(info: ComponentInfo, fromSolid: boolean, point: THREE.Vector3 | null, highlightCtl: HighlightController);
44
+ /** Whether to act on the whole solid (faces) rather than the single component. */
45
+ private get asSolid();
46
+ get backendId(): string;
47
+ get name(): string;
48
+ get topo(): TopoType;
49
+ highlight(asHover: boolean): void;
50
+ unhighlight(keepSelected: boolean): void;
51
+ toggleSelection(): void;
52
+ clearHighlights(): void;
53
+ private _setSelected;
54
+ equals(other: PickedComponent | null): boolean;
55
+ }
@@ -4,7 +4,7 @@
4
4
  * Wraps the pmndrs EffectComposer to provide:
5
5
  * - Scene rendering (RenderPass)
6
6
  * - Screen-space ambient occlusion (N8AOPostPass)
7
- * - Screen-space shadow mask (BasicShadowMap + KawaseBlurPass)
7
+ * - Screen-space shadow mask (PCFShadowMap + KawaseBlurPass)
8
8
  * - Tone mapping + sRGB output + antialiasing (ToneMappingEffect + SMAAEffect)
9
9
  *
10
10
  * Tone mapping is handled by the postprocessing ToneMappingEffect, which uses
@@ -18,7 +18,7 @@
18
18
  * transparent, and the EffectPass alpha-blends its output onto a pre-cleared
19
19
  * canvas that already has the correct background color.
20
20
  *
21
- * Shadow mask: BasicShadowMap produces sharp shadow boundaries at 4096×4096.
21
+ * Shadow mask: PCFShadowMap produces sharp shadow boundaries at 4096×4096.
22
22
  * A half-resolution ShadowMaterial override pass captures the mask, which is
23
23
  * then blurred via KawaseBlurPass and composited by ShadowMaskEffect before
24
24
  * tone mapping. The floor keeps its own ShadowMaterial reading the shadow map
@@ -61,7 +61,7 @@ declare class StudioComposer {
61
61
  * @param width - Canvas width in pixels
62
62
  * @param height - Canvas height in pixels
63
63
  */
64
- constructor(renderer: THREE.WebGLRenderer, scene: THREE.Scene, camera: THREE.Camera, width: number, height: number);
64
+ constructor(renderer: THREE.WebGLRenderer, scene: THREE.Scene, camera: THREE.Camera, width: number, height: number, onSmaaReady?: () => void);
65
65
  /**
66
66
  * Enable or disable solid-background protection.
67
67
  *
@@ -7,7 +7,7 @@ declare const States: {
7
7
  readonly mixed: 2;
8
8
  readonly disabled: 3;
9
9
  };
10
- type StateValue = typeof States[keyof typeof States];
10
+ type StateValue = (typeof States)[keyof typeof States];
11
11
  /** Icon index: 0 for shapes, 1 for edges */
12
12
  type IconIndex = 0 | 1;
13
13
  /**
@@ -2,6 +2,18 @@ import * as THREE from "three";
2
2
  import type { Vector3Tuple } from "three";
3
3
  import { ObjectGroup } from "./nestedgroup.js";
4
4
  import type { Theme, ClipIndex } from "../core/types";
5
+ /**
6
+ * Cap-culling thresholds (see {@link Clipping.cull}). The per-solid stencil + cap
7
+ * meshes are correct but O(N) in draw calls; on a large assembly (~1300 solids ×
8
+ * 3 planes × 3 meshes ≈ 12000 draws/frame) a zoomed-out clip+rotate overruns the
9
+ * GPU watchdog → context loss. Culling bounds the per-frame work:
10
+ * - `CAP_CULL_MIN_PX`: skip a solid's caps when its projected screen radius is
11
+ * below this (sub-pixel solids contribute nothing visible zoomed out).
12
+ * - `CAP_CULL_BUDGET`: hard cap on the number of capped solids (largest-first);
13
+ * the real crash guard for pathologically dense views. Tune on a real GPU.
14
+ */
15
+ export declare const CAP_CULL_MIN_PX = 3;
16
+ export declare const CAP_CULL_BUDGET = 400;
5
17
  /**
6
18
  * A THREE.Plane that maintains a constant relative to a center point.
7
19
  */
@@ -103,6 +115,17 @@ declare class Clipping extends THREE.Group {
103
115
  objectColorCaps: boolean;
104
116
  planeHelpers: PlaneMeshGroup | null;
105
117
  private _planeMeshGroup;
118
+ /** Per-solid stencil/cap units, the unit of screen-size culling. */
119
+ private _capUnits;
120
+ /**
121
+ * Whether {@link cull} last ran with clipping active. Lets the inactive path
122
+ * gate stencils/caps off exactly once, then early-return on later still frames.
123
+ * Starts `true` so the first inactive call performs the initial gate-off (the
124
+ * meshes default to `visible:true`).
125
+ */
126
+ private _cullActive;
127
+ /** Reused buffer for the budget threshold sort (no per-frame alloc). */
128
+ private _radiiScratch;
106
129
  /**
107
130
  * Create a Clipping instance.
108
131
  * @param center - The center point [x, y, z].
@@ -168,6 +191,36 @@ declare class Clipping extends THREE.Group {
168
191
  * @param flag - True to show, false to hide.
169
192
  */
170
193
  setVisible: (flag: boolean) => void;
194
+ /**
195
+ * Bound the per-frame stencil/cap draw work to keep large assemblies from
196
+ * overrunning the GPU watchdog on clip+rotate (see {@link CAP_CULL_MIN_PX}).
197
+ *
198
+ * Toggles each unit's `Object3D.visible` (NOT material.visible), which composes
199
+ * by AND with the existing material-level toggles — `setShapeVisible` (per-solid
200
+ * hide) and `setVisible` (clip-tab on/off) — so a unit renders only when it is
201
+ * un-culled AND its solid is shown AND the clip tab is active. Render order and
202
+ * the per-solid `clearStencil` isolation are untouched (removing a whole solid's
203
+ * units never affects the remaining solids' cap correctness).
204
+ *
205
+ * @param camera - The active camera (ortho or perspective).
206
+ * @param width - Canvas width in CSS px.
207
+ * @param height - Canvas height in CSS px.
208
+ * @param clipActive - `renderer.localClippingEnabled` (clip tab selected).
209
+ */
210
+ cull(camera: THREE.Camera, width: number, height: number, clipActive: boolean): void;
211
+ /**
212
+ * World-space AABB of a solid (local bounding box transformed by the front
213
+ * mesh's world matrix, which folds in GDS z-scale). Returns a shared scratch
214
+ * Box3 (valid only until the next call) or `null` when geometry is missing.
215
+ */
216
+ private _solidWorldBox;
217
+ /**
218
+ * Projected screen radius (px) of a solid's bounding sphere. Projects the world
219
+ * sphere center and a point one world-radius along the camera-right axis, and
220
+ * measures their screen-space separation — correct for both ortho and
221
+ * perspective. Returns `Infinity` (never cull) when geometry is missing.
222
+ */
223
+ private _solidScreenRadius;
171
224
  /**
172
225
  * Save the current clipping state for later restoration.
173
226
  * Captures plane positions, helper visibility, and stencil plane visibility.
@@ -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