import * as THREE from "three"; import { type IdPicker } from "../rendering/id-picking.js"; import { type PickedComponent } from "../rendering/picked.js"; import type { Shapes } from "./types.js"; import type { RenderedState } from "./viewer.js"; import type { Display } from "../ui/display.js"; import type { Tools } from "../tools/cad_tools/tools.js"; /** * The narrow surface {@link PickingController} needs from its host (the Viewer). * Keeps the controller decoupled from the rest of the Viewer god-object and * unit-testable. `rendered` is a lazy getter that throws before render() — the * controller must always read through it (never cache `nestedGroup`/`highlight`, * which are rebuilt every render()). */ export interface PickHost { readonly idPicker: IdPicker | null; readonly ready: boolean; readonly hasAnimationLoop: boolean; /** True while the Studio (presentation) tab owns the render. */ readonly studioActive: boolean; readonly shapes: Shapes | null; readonly renderer: THREE.WebGLRenderer; readonly rendered: RenderedState; readonly display: Display; readonly cadTools: Tools; update(updateMarker: boolean, notify: boolean): void; /** Restore the most recently meta-double-click-hidden leaf (hide-undo stack). */ showLastHidden(): void; handlePick(path: string, name: string, meta: boolean, shift: boolean, alt: boolean, point: THREE.Vector3 | null, nodeType?: string | null, tree?: boolean): void; } /** * Owns all pointer-driven picking on the compact graph: hover preselection * (highlight + status line), left-click selection commit, right-click/key removal, * and double-click pick. Hover proposes the component under the cursor * ({@link lastObject}); a left-click commits it ({@link lastSelection}) — one state * machine, so both fields live here. * * Listener lifecycle: hover (pointermove/leave) is always-on and added at * construction; selection (mousedown/mouseup + document keydown) is tool-scoped via * {@link setSelectionInput}; double-click via {@link setPickHandler}. {@link dispose} * tears them all down. */ export declare class PickingController { private readonly host; /** Component under the cursor (hover); committed on left-click. */ lastObject: PickedComponent | null; /** Last committed selection. */ lastSelection: PickedComponent | null; private idHoverClientX; private idHoverClientY; private idHoverInside; private idHoverRenderQueued; /** Per-component hover status text (fixed per mesh; cleared on reload / z-scale). */ private hoverStatusCache; private selectionInputActive; private selectDownPosition; private pickHandlerActive; constructor(host: PickHost); /** Remove every listener this controller owns. */ dispose(): void; /** Record the cursor position over the canvas. */ private onIdHoverMove; /** Cursor left the canvas → clear any hover highlight. */ private onIdHoverLeave; /** * Pick the visible component under the cursor with the current topo filter, or * `null` when the cursor is off the canvas or over background. */ private pickUnderCursor; /** * World-space point on the visible component under the cursor (same pick as the * hover), or `null` when there is none or the GPU lacks the position attachment. */ pointUnderCursor(): THREE.Vector3 | null; /** * Tree-row hover: HOVER the faces of the tree node at `path` (leaf or group), or * clear when `null`. Drops the canvas hover target first — otherwise the next * render's {@link handleIdHover} (cursor outside the canvas) would release that * stale target and wipe the tree hover with it. */ setTreeHover(path: string | null): void; /** * Whether hover preselection (highlight + status line) is active. Disabled for GDS: * dense, stacked, instance-unrolled layout data where per-pixel hover flickers * endlessly and the B-rep readout ("area ≈ …") is meaningless, so GDS is * double-click-identify only. Also disabled in Studio (presentation) mode: hover * tint/status is a CAD/analysis affordance, not wanted in Studio, and skipping it * means the id buffer is never re-rendered for a bare mouse-move there. */ private hoverPreselectActive; /** * Called once per render: drive hover preselection unless a model is GDS or the * camera is being dragged. */ handleHover(): void; /** * Hover via the GPU id picker on the compact graph: resolve the component under the * cursor and drive the shader `HighlightController`. Sets {@link lastObject} * (committed on left-click by {@link commitSelection} when a tool is active). */ private handleIdHover; /** Drop the cached hover texts (e.g. on z-scale change — world lengths change). */ invalidateHoverCache(): void; /** * Drop any lingering hover highlight + status line (keeping the committed selection). * Called when entering Studio mode: hover preselection is disabled there, so the * per-render {@link handleHover} no longer runs its own release and a stale tint/status * from CAD mode would otherwise persist. */ clearHover(): void; /** * Whether the component a pick resolved to is currently visible — used to drop picks * of hidden geometry. Visibility is the owning group's material `visible` flag * (faces for solids/standalone faces; edge/vertex materials for standalone leaves). */ private pickVisible; clearSelection: () => void; private releaseLastSelected; removeLastSelected(): void; /** Drop selection state + hover cache + status line (on model reload). */ reset(): void; /** * Add/remove the canvas mousedown+mouseup and document keydown listeners * (idempotent, guarded on `selectionInputActive`). Hover maintains * {@link lastObject}; these handlers commit it on left-click and handle the key + * right-click actions. */ setSelectionInput(flag: boolean): void; private onSelectMouseDown; private onSelectMouseUp; private onSelectKeyDown; private commitSelection; setPickHandler(flag: boolean): void; /** * Double-click pick via the GPU id picker: `idPicker.pickAt` resolves the component * under the cursor, the registry gives its owning tree-leaf path ({@link leafPath}), * and the readback world-space `point` feeds `handlePick` (the `shift && meta` camera * target, with a bbox-center fallback when `point` is null). No topo filter: a * double-click selects whatever is under the cursor and resolves it to its leaf. */ private onDoubleClick; }