/** * BCF 3D Overlay — viewer-agnostic types and position computation * * Derives 3D marker positions from BCF topics so they can be rendered * as floating overlays in any 3D viewer (WebGPU, Three.js, Babylon, etc.). * * Position derivation strategy (in priority order): * 1. Selected component bounding-box center (most accurate) * 2. Camera target point — the orbit center from the viewpoint (robust fallback) * 3. Camera viewpoint position itself (last resort) */ import type { BCFTopic } from './types.js'; /** A 3D point in the viewer's Y-up coordinate system */ export interface OverlayPoint3D { x: number; y: number; z: number; } /** Axis-aligned bounding box */ export interface OverlayBBox { min: OverlayPoint3D; max: OverlayPoint3D; } /** * A positioned 3D marker derived from a BCF topic. * Contains everything a renderer needs to display the marker. */ export interface BCFMarker3D { /** Topic GUID — unique identifier */ topicGuid: string; /** * World-space position (Y-up) where the marker pin-tip sits. * For component-based markers this is well above the bbox top * so the marker floats above geometry from any camera angle. */ position: OverlayPoint3D; /** * World-space anchor point where the connector line ends. * Typically the bbox top-center of the referenced component. * If absent, the connector ends at `position`. */ connectorAnchor?: OverlayPoint3D; /** Topic title */ title: string; /** Topic status (e.g. 'Open', 'In Progress', 'Resolved', 'Closed') */ status: string; /** Priority (e.g. 'High', 'Medium', 'Low') */ priority: string; /** Topic type (e.g. 'Error', 'Warning', 'Info') */ topicType: string; /** Number of comments */ commentCount: number; /** Whether the topic has at least one viewpoint */ hasViewpoint: boolean; /** Optional snapshot thumbnail (data URL) from the first viewpoint */ snapshot?: string; /** How the position was derived — useful for debugging */ positionSource: 'component' | 'camera-target' | 'camera-position'; /** Topic index (for numbering markers) */ index: number; } /** * Viewer-agnostic projection interface. * Each renderer (WebGPU, Three.js, Babylon.js, etc.) provides an * implementation of this interface to enable BCF 3D overlays. */ export interface BCFOverlayProjection { /** * Project a world-space position to screen coordinates (pixels). * Returns null if the point is behind the camera. */ projectToScreen(worldPos: OverlayPoint3D): { x: number; y: number; } | null; /** * Get the axis-aligned bounding box for an entity by its expressId. * Returns null if the entity is not found. */ getEntityBounds(expressId: number): OverlayBBox | null; /** Get the canvas/viewport dimensions in pixels */ getCanvasSize(): { width: number; height: number; }; /** * Get the current camera position in world space (Y-up). * Used for depth-based scaling. Optional — markers render at * uniform size if not provided. */ getCameraPosition?(): OverlayPoint3D; /** * Subscribe to camera/render changes. * The callback is invoked **synchronously inside the polling RAF** * so the overlay can re-project in the same animation frame * for zero-lag tracking during orbit/pan/zoom. * Returns an unsubscribe function. */ onCameraChange(callback: () => void): () => void; } /** * Callback that resolves an IFC GlobalId (22-char base64) to * a bounding box in Y-up viewer coordinates. * Returns null when the entity is not loaded or has no geometry. */ export type EntityBoundsLookup = (ifcGuid: string) => OverlayBBox | null; export interface ComputeMarkersOptions { /** * Camera-to-target distance in world units. Used when a viewpoint has * camera data but no resolvable component selection. Should match the * viewer's camera distance (e.g. `camera.getDistance()`). * Default 50. */ targetDistance?: number; /** Only include topics with these statuses (default: all) */ statusFilter?: string[]; } /** * Compute positioned 3D markers from BCF topics. * * This is a **pure function** — no DOM, no renderer dependency. * It takes a lookup callback so any renderer can provide entity bounds. * * @param topics Array of BCF topics to create markers for * @param boundsLookup Callback to resolve IFC GlobalId → bounding box (Y-up) * @param options Optional configuration * @returns Array of positioned markers (topics without derivable positions are skipped) */ export declare function computeMarkerPositions(topics: BCFTopic[], boundsLookup: EntityBoundsLookup, options?: ComputeMarkersOptions): BCFMarker3D[]; //# sourceMappingURL=overlay.d.ts.map