Warning, /firebird/packages/firebird-ng/api/src/lib/views.ts is written in an unsupported language. File is not indexed.
0001 /**
0002 * The render view contracts: what an extension sees of the views that draw
0003 * the shared scene, the options for adding one, view overlays, and the
0004 * camera-layer routing.
0005 *
0006 * Interfaces and plain constants only: this module is part of the initial
0007 * bundle and imports three.js types, never three.js values.
0008 */
0009
0010 import type { OrthographicCamera, PerspectiveCamera, Plane, Raycaster, Vector2, Vector3 } from 'three';
0011 import type { ClippingGroup } from 'three/webgpu';
0012 import type { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
0013 import type { CameraLimits, Vec3Tuple } from './tokens';
0014
0015 // ---------------------------------------------------------------------------
0016 // Camera-layer routing
0017 // ---------------------------------------------------------------------------
0018 //
0019 // Every camera renders layer 0 (helpers, lights) and EVENT_DATA_LAYER. The
0020 // detector geometry stays on layer 0 until a geometry slice exists; then the
0021 // original geometry moves to GEOMETRY_MAIN_LAYER (the main view enables it)
0022 // and the slice copy sits on GEOMETRY_SLICE_LAYER (views created with
0023 // `geometrySlice` enable it).
0024
0025 /** Original detector geometry while a geometry slice exists: rendered by the main view. */
0026 export const GEOMETRY_MAIN_LAYER = 1;
0027 /** The geometry slice copy: rendered by views created with `geometrySlice`. */
0028 export const GEOMETRY_SLICE_LAYER = 2;
0029 /**
0030 * Event data: everything under `sceneEvent`. Every view renders it; a
0031 * tracks-on-top view renders it in a second pass over a cleared depth
0032 * buffer. Painted objects are routed here by the display; objects an
0033 * extension adds go through `SceneContext.addEventObject()`. The lights
0034 * carry this layer too: both passes must collect the same light set, or
0035 * every shared render object rebuilds each frame.
0036 */
0037 export const EVENT_DATA_LAYER = 3;
0038
0039 // ---------------------------------------------------------------------------
0040 // Views
0041 // ---------------------------------------------------------------------------
0042
0043 /** A camera pose: where the camera is, what it orbits, and its screen up. */
0044 export interface CameraPose {
0045 position: Vec3Tuple;
0046 target: Vec3Tuple;
0047 up: Vec3Tuple;
0048 }
0049
0050 /** Viewport rectangle in CSS pixels, top-left origin, as three's WebGPURenderer takes it. */
0051 export interface ViewportRect {
0052 /** Left edge relative to the canvas. */
0053 x: number;
0054 /** Top edge relative to the canvas; pass to `renderer.setViewport`/`setScissor` as is. */
0055 y: number;
0056 width: number;
0057 height: number;
0058 }
0059
0060 /**
0061 * The independently clipped copy of the detector geometry that projection
0062 * views cut (see `SceneContext.createGeometrySlice()`).
0063 *
0064 * @experimental Edits to the original geometry after a load (visibility,
0065 * materials) do not reach the copy until `rebuildGeometrySlice()`.
0066 */
0067 export interface GeometrySlice {
0068 /** The clipping group holding the copy; the display adds it to the scene. */
0069 readonly group: ClippingGroup;
0070 /** The one clip plane, written by each view with its own `clipPlane` before it renders. */
0071 readonly plane: Plane;
0072 }
0073
0074 /**
0075 * One view of the shared scene: a DOM container, a perspective and an
0076 * orthographic camera, orbit controls on that container, and a rectangle
0077 * of the shared canvas. The display's own view is `SceneContext.mainView`;
0078 * `SceneContext.addView()` adds more.
0079 */
0080 export interface RenderView {
0081 /** The name given at creation ('main' for the display's view). */
0082 readonly name: string;
0083 /**
0084 * The element the view fills. Pointer input (controls, picking) arrives
0085 * here, never on the shared canvas; the element changes when the view
0086 * moves to another page (`ViewOverlay.onViewContainerChange`).
0087 */
0088 readonly container: HTMLElement;
0089 /** The active camera (perspective or orthographic). */
0090 readonly camera: PerspectiveCamera | OrthographicCamera;
0091 readonly perspectiveCamera: PerspectiveCamera;
0092 readonly orthographicCamera: OrthographicCamera;
0093 /** Orbit controls bound to `container`. */
0094 readonly controls: OrbitControls;
0095 /** True for a view created with `fixedDirection`: it pans and zooms, never rotates. */
0096 readonly isRotationLocked: boolean;
0097 /**
0098 * This view's geometry cut in world space, or null. Mutate its normal and
0099 * constant to move the cut, then set `dirty`.
0100 *
0101 * @experimental Works only on a view created with `geometrySlice`.
0102 */
0103 readonly clipPlane: Plane | null;
0104 /** Draws event data over geometry regardless of depth (two render passes). */
0105 tracksOnTop: boolean;
0106 /**
0107 * True when this view needs a new frame. Set it after changing a per-view
0108 * setting; the render loop clears it. `SceneContext.invalidate()` marks
0109 * every view.
0110 */
0111 dirty: boolean;
0112 /** The view's rectangle inside the canvas, y ready for `renderer.setViewport`. */
0113 readonly viewportRect: Readonly<ViewportRect>;
0114 /** CSS-pixel size of the view container. */
0115 readonly size: Readonly<{ width: number; height: number }>;
0116 /** Adds an overlay drawn after this view's scene render. */
0117 addOverlay(overlay: ViewOverlay): void;
0118 /** Removes an overlay and calls its `dispose()`. */
0119 removeOverlay(overlay: ViewOverlay): void;
0120 /**
0121 * Sets the screen up vector of both cameras and resyncs the orbit
0122 * controls, which capture the up axis once at construction.
0123 */
0124 setCameraUp(up: Vector3): void;
0125 /** Normalized device coordinates of a pointer event inside this view; null outside it. */
0126 ndcFromEvent(event: { clientX: number; clientY: number }): Vector2 | null;
0127 /** A raycaster through the pointer position for this view's camera; null outside the view. */
0128 raycasterFromEvent(event: { clientX: number; clientY: number }): Raycaster | null;
0129 }
0130
0131 /**
0132 * Something drawn on top of a view after its scene render: an orientation
0133 * cube, axes, 2D annotations. Overlays render into the shared canvas and set
0134 * their own viewport (`RenderView.viewportRect`); restore what you change.
0135 * A render that throws removes the overlay.
0136 */
0137 export interface ViewOverlay {
0138 /** Called every rendered frame, after the view's scene render. */
0139 render(view: RenderView): void;
0140 /** Called when the view's size or position changed. */
0141 onViewResize?(view: RenderView): void;
0142 /** Called when the view moved to another DOM container (page switches). */
0143 onViewContainerChange?(view: RenderView): void;
0144 /** Called when the view or the overlay is removed. */
0145 dispose?(): void;
0146 }
0147
0148 /** Options of `SceneContext.addView()`. */
0149 export interface RenderViewOptions {
0150 /** Shown in debug output and used to find views. */
0151 name: string;
0152 /**
0153 * The element this view fills. It must sit above the shared canvas;
0154 * pointer input is listened on it and the viewport rectangle tracks it.
0155 */
0156 container: HTMLElement;
0157 /** Start with the orthographic camera instead of the perspective one. */
0158 orthographic?: boolean;
0159 /**
0160 * Initial visible world height [mm] of an orthographic view. Zooming and
0161 * resizing keep the height/zoom relation.
0162 */
0163 orthoWorldHeight?: number;
0164 /**
0165 * Fixed view direction (unit vector from the target toward the camera)
0166 * and screen up, for projection views. The controls pan and zoom but do
0167 * not rotate.
0168 */
0169 fixedDirection?: { direction: [number, number, number]; up: [number, number, number] };
0170 /** Extra camera layers this view renders, besides layer 0 and EVENT_DATA_LAYER. */
0171 extraLayers?: number[];
0172 /**
0173 * The geometry slice this view renders instead of the original geometry
0174 * (its camera gets GEOMETRY_SLICE_LAYER). Views sharing the slice each
0175 * write their own `clipPlane` into it before they render.
0176 *
0177 * @experimental See `GeometrySlice`.
0178 */
0179 geometrySlice?: GeometrySlice;
0180 /**
0181 * This view's geometry cut in world space; requires `geometrySlice`. The
0182 * visible side is `normal . p + constant >= 0`.
0183 *
0184 * @experimental See `GeometrySlice`.
0185 */
0186 clipPlane?: { normal: [number, number, number]; constant: number };
0187 /** Draw event data over geometry regardless of depth (see `RenderView.tracksOnTop`). */
0188 tracksOnTop?: boolean;
0189 /** The camera pose at construction. Default: the top view, 7 m above the origin. */
0190 startPose?: CameraPose;
0191 /** Orbit distance limits [mm]. Default: the display's current limits. */
0192 distanceLimits?: CameraLimits;
0193 }