Back to home page

EIC code displayed by LXR

 
 

    


Warning, /firebird/packages/firebird-ng/api/src/lib/three-extension.ts is written in an unsupported language. File is not indexed.

0001 /**
0002  * The ThreeExtension contract: the narrow lifecycle interface through which
0003  * extensions hook the rendering machinery (scene lifecycle, frame loop).
0004  *
0005  * Boundary rule: if code turns *data* (event groups, fields, geometry) into
0006  * visuals, it is a painter — register it with `withPainter()`. If it hooks
0007  * the *machinery* — scene lifecycle, frame loop, input, services — it is a
0008  * ThreeExtension.
0009  */
0010 
0011 import type { Type } from '@angular/core';
0012 import type * as THREE from 'three';
0013 import type { WebGPURenderer, ClippingGroup } from 'three/webgpu';
0014 import type { Event as FbEvent } from '@dexvis/firebird-core';
0015 import type { GeometrySlice, RenderView, RenderViewOptions } from './views';
0016 
0017 /**
0018  * Everything an extension needs to work with the scene. Handed to
0019  * `onSceneInit` strictly AFTER the async renderer initialization resolved —
0020  * extension authors never hand-roll "defer until ready" logic.
0021  *
0022  * The context hands out real three.js objects, never wrappers. An author who
0023  * wants three.js verbatim builds a `Raycaster` from `camera` + pointer math.
0024  */
0025 export interface SceneContext {
0026   scene: THREE.Scene;
0027   /** Detector geometry container. Clipping applies here — never to sceneEvent. */
0028   sceneGeometry: ClippingGroup;
0029   /** Event data container. Never clipped. */
0030   sceneEvent: THREE.Group;
0031   /** Lights, axes, gizmos, measurement markers. */
0032   sceneHelpers: THREE.Group;
0033   /** The active camera (perspective or orthographic). */
0034   camera: THREE.PerspectiveCamera | THREE.OrthographicCamera;
0035   renderer: WebGPURenderer;
0036   /**
0037    * The shared canvas every view draws into. Do not attach pointer input
0038    * here: on multi-view pages the view containers sit above the canvas and
0039    * receive the events, so the canvas gets none. Listen on a view's
0040    * `container` instead (`mainView.container` for the display), and move
0041    * the listeners when the view changes containers on page switches
0042    * (`ViewOverlay.onViewContainerChange` reports it).
0043    */
0044   canvas: HTMLCanvasElement;
0045   /**
0046    * The render views sharing this scene. Live list: multi-view pages add and
0047    * remove views at runtime. `views[0]` is always the main view.
0048    */
0049   readonly views: readonly RenderView[];
0050   /**
0051    * The main view — the display page's camera/controls. Per-view overlays
0052    * (like the navigation cube) attach here via `mainView.addOverlay()`.
0053    */
0054   readonly mainView: RenderView;
0055   /** Adds a view of the shared scene (see RenderViewOptions for the contract). */
0056   addView(options: RenderViewOptions): RenderView;
0057   /** Removes a view added with addView. The main view cannot be removed. */
0058   removeView(view: RenderView): void;
0059   /**
0060    * The independently clipped geometry copy for projection views, or null
0061    * when no view uses one. Create it with `createGeometrySlice()`, pass it
0062    * with a `clipPlane` in the `addView` options to give a view its own cut,
0063    * and call `rebuildGeometrySlice()` after editing the loaded geometry
0064    * (the display rebuilds it after every geometry load).
0065    *
0066    * @experimental See `GeometrySlice`.
0067    */
0068   readonly geometrySlice: GeometrySlice | null;
0069   /** @experimental See `geometrySlice`. */
0070   createGeometrySlice(): GeometrySlice;
0071   /** @experimental See `geometrySlice`. */
0072   rebuildGeometrySlice(): void;
0073   /** @experimental See `geometrySlice`. */
0074   removeGeometrySlice(): void;
0075   /**
0076    * Adds an object to the event data: under `sceneEvent`, on
0077    * EVENT_DATA_LAYER with all its descendants (never clipped, drawn in
0078    * every view, over geometry in tracks-on-top views), and schedules a
0079    * frame. Remove it with `object.removeFromParent()` and `invalidate()`.
0080    * Objects added to `sceneEvent` directly keep layer 0 and disappear from
0081    * the second pass of tracks-on-top views.
0082    */
0083   addEventObject(object: THREE.Object3D): void;
0084   /**
0085    * Signal that you changed renderable state — the next animation frame
0086    * renders. The render loop is on-demand by default (config
0087    * `rendering.mode`): a mutation without an invalidate() shows up only
0088    * when something else triggers a render. Call it after mutating anything
0089    * the next frame must show. Cheap and idempotent.
0090    */
0091   invalidate(): void;
0092 }
0093 
0094 /**
0095  * Per-frame context passed to `onFrame` inside the render loop.
0096  * Keep `onFrame` cheap: no allocation, no per-frame polling of app state.
0097  *
0098  * Rule: `onFrame` is for animation only. State changes must travel through
0099  * signals/effects, never by polling inside the frame loop. Under the
0100  * default on-demand scheduling, `onFrame` runs only on frames that render;
0101  * an animation sustains itself by calling `invalidate()` from its update
0102  * (start it with one seed `invalidate()`), and the chain ends by itself
0103  * when the animation stops updating.
0104  */
0105 export interface FrameContext {
0106   /** Milliseconds since the previous RENDERED frame. */
0107   deltaTime: number;
0108   renderer: WebGPURenderer;
0109   camera: THREE.PerspectiveCamera | THREE.OrthographicCamera;
0110   /** Same contract as SceneContext.invalidate. */
0111   invalidate(): void;
0112 }
0113 
0114 /**
0115  * Lifecycle interface for rendering-machinery extensions. All methods are
0116  * optional; implement only what you need. Registered with
0117  * `withThreeExtension(MyExtension)` — the class is instantiated through DI,
0118  * so `inject()` works in its constructor.
0119  */
0120 export interface ThreeExtension {
0121   /** Called once, after the async ThreeService.init completed. */
0122   onSceneInit?(ctx: SceneContext): void;
0123   /**
0124    * Called before rendering, on every frame that renders. Keep it cheap. If
0125    * it throws, the error is logged and this extension's onFrame is not
0126    * called again; rendering and the other extensions continue.
0127    */
0128   onFrame?(ctx: FrameContext): void;
0129   /** Called when a new event (entry) was loaded and painted. */
0130   onEventLoaded?(event: FbEvent): void;
0131   /**
0132    * Called once at application teardown, when the root injector destroys
0133    * the rendering service. The scene and the extension outlive display
0134    * pages: leaving /display does not call this, and returning does not call
0135    * onSceneInit again. Remove listeners and objects here.
0136    */
0137   onDispose?(): void;
0138 }
0139 
0140 /**
0141  * Loader shape for heavy extensions that must not sit in the initial bundle:
0142  * `withLazyThreeExtension(() => import('./vr').then(m => m.VrExtension))`.
0143  * The bundler splits each lazy extension into its own chunk; the extension
0144  * class itself is written identically in both cases.
0145  */
0146 export type LazyThreeExtensionLoader = () => Promise<Type<ThreeExtension>>;