Warning, /firebird/packages/firebird-ng/api/src/lib/tokens.ts is written in an unsupported language. File is not indexed.
0001 /**
0002 * The DI tokens of the Firebird extension system.
0003 *
0004 * Every extensible surface follows one pattern: contributions are declared in
0005 * DI as multi-providers (via the `with*()` features in firebird-features.ts),
0006 * collected by core services, and called through narrow lifecycle interfaces.
0007 * Nothing registers itself via import side effects.
0008 *
0009 * Built-ins are first consumers: Firebird's own factories, painters, loaders
0010 * and command handlers ride these same tokens (see with-firebird-builtins.ts).
0011 *
0012 * Precedence, the same for every registry: contributions keep registration
0013 * order (the order of the `provideFirebird()` arguments, depth first, after
0014 * Firebird's built-ins). A later contribution with the id of an earlier one
0015 * replaces it at the earlier one's position. Where one entry is picked among
0016 * several, the earliest wins: the first loader that claims a source loads
0017 * it, the first painter registered for a piece type is its default. The ids:
0018 * feature ids, `DataLoaderMeta.id`, `CommandHandler.type`,
0019 * `EventPieceFactory.type`, `PainterMeta.id`, and the `id` or `name` of tabs,
0020 * themes, post-processors, camera presets and toolbar actions. To take over
0021 * a built-in, register under its id or remove it with `withoutFeatures()`.
0022 *
0023 * The generic tokens (COMMAND_HANDLERS, CONFIG_DEFAULTS, URL_ALIASES,
0024 * URL_SHORTHANDS) come from `@dexvis/app-features`; `@dexvis/firebird-ng`
0025 * re-exports them.
0026 *
0027 * Token descriptions exist in development builds only (the Angular idiom):
0028 * this file is in the initial bundle, and production builds define
0029 * `ngDevMode` as false and drop the strings.
0030 */
0031
0032 import { InjectionToken, Type, inject } from '@angular/core';
0033 import { type AppCommand, resolveRegistry } from '@dexvis/app-features';
0034 import type {
0035 EventPieceFactory,
0036 PiecePainterConstructor,
0037 GeometryDataLoader,
0038 EventDataLoader,
0039 DataCatalog,
0040 } from '@dexvis/firebird-core';
0041 import type { Object3D } from 'three';
0042 import type { ThreeExtension, LazyThreeExtensionLoader } from './three-extension';
0043 import type {
0044 GeometryPostProcessorRegistration,
0045 GeometryThemeRegistration,
0046 RootGeometryRulesSource,
0047 } from './geometry-pipeline';
0048
0049 /**
0050 * One painter registration: which piece type it paints and with what class.
0051 * Either `painterClass` (eager) or `load` (lazy — keeps heavy painter code
0052 * out of the initial bundle; resolved before the first event is painted).
0053 */
0054 export interface PainterRegistration {
0055 forPieceType: string;
0056 painterClass?: PiecePainterConstructor;
0057 load?: () => Promise<PiecePainterConstructor>;
0058 }
0059
0060 /**
0061 * One tab of the data selector (the "open data" control shown in the display
0062 * toolbar panel and on the config page). The component loads lazily so tab UI
0063 * code (Material, forms) stays out of the initial bundle. Tabs sort by
0064 * `order`, then registration order.
0065 */
0066 export interface DataSelectorTabRegistration {
0067 /** Stable id, e.g. 'presets', 'physics', 'manual'. A later tab with the same id replaces this one. */
0068 id: string;
0069 /** Tab caption. */
0070 label: string;
0071 /** Sort key; built-ins use 10, 20, 30. */
0072 order?: number;
0073 /** Resolves the tab component. It reads and writes `DataSelectionService.draft`. */
0074 load: () => Promise<Type<unknown>>;
0075 /**
0076 * When false, the tab is offered even with an empty catalog. Default true:
0077 * a tab that only lists catalog content hides itself when there is none.
0078 */
0079 needsCatalog?: boolean;
0080 }
0081
0082 /** Event piece factories (DEX type string -> model object decoder). */
0083 export const EVENT_PIECE_FACTORIES = new InjectionToken<EventPieceFactory[]>(
0084 typeof ngDevMode !== 'undefined' && ngDevMode ? 'firebird.event-piece-factories' : '');
0085
0086 /** Painter registrations consumed by EventDisplayService's DataModelPainter. */
0087 export const PAINTERS = new InjectionToken<PainterRegistration[]>(
0088 typeof ngDevMode !== 'undefined' && ngDevMode ? 'firebird.painters' : '');
0089
0090 /** Rendering-machinery extensions, instantiated eagerly through DI. */
0091 export const THREE_EXTENSIONS = new InjectionToken<ThreeExtension[]>(
0092 typeof ngDevMode !== 'undefined' && ngDevMode ? 'firebird.three-extensions' : '');
0093
0094 /** Lazily-loaded extensions: resolved after init, off the critical path. */
0095 export const LAZY_THREE_EXTENSIONS = new InjectionToken<LazyThreeExtensionLoader[]>(
0096 typeof ngDevMode !== 'undefined' && ngDevMode ? 'firebird.lazy-three-extensions' : '');
0097
0098 /** Geometry format/scheme loaders. Read them through `injectGeometryLoaders()`. */
0099 export const GEOMETRY_LOADERS = new InjectionToken<GeometryDataLoader[]>(
0100 typeof ngDevMode !== 'undefined' && ngDevMode ? 'firebird.geometry-loaders' : '');
0101
0102 /** Event data format loaders. Read them through `injectEventLoaders()`. */
0103 export const EVENT_LOADERS = new InjectionToken<EventDataLoader[]>(
0104 typeof ngDevMode !== 'undefined' && ngDevMode ? 'firebird.event-loaders' : '');
0105
0106 /** Data catalogs: datasets an installation offers (presets, tags, URL lists). Merged in order. */
0107 export const DATA_CATALOGS = new InjectionToken<DataCatalog[]>(
0108 typeof ngDevMode !== 'undefined' && ngDevMode ? 'firebird.data-catalogs' : '');
0109
0110 /** Tabs of the data selector control. */
0111 export const DATA_SELECTOR_TABS = new InjectionToken<DataSelectorTabRegistration[]>(
0112 typeof ngDevMode !== 'undefined' && ngDevMode ? 'firebird.data-selector-tabs' : '');
0113
0114 /** Pre-build TGeo rules, posted to the geometry worker with each load (see geometry-pipeline.ts). */
0115 export const ROOT_GEOMETRY_RULES = new InjectionToken<RootGeometryRulesSource[]>(
0116 typeof ngDevMode !== 'undefined' && ngDevMode ? 'firebird.root-geometry-rules' : '');
0117
0118 /** Geometry themes, selected by config `geometry.themeName`. */
0119 export const GEOMETRY_THEMES = new InjectionToken<GeometryThemeRegistration[]>(
0120 typeof ngDevMode !== 'undefined' && ngDevMode ? 'firebird.geometry-themes' : '');
0121
0122 /** Main-thread geometry post-processors, run after each geometry load. */
0123 export const GEOMETRY_POST_PROCESSORS = new InjectionToken<GeometryPostProcessorRegistration[]>(
0124 typeof ngDevMode !== 'undefined' && ngDevMode ? 'firebird.geometry-post-processors' : '');
0125
0126 // ---------------------------------------------------------------------------
0127 // Camera
0128 // ---------------------------------------------------------------------------
0129
0130 /** An [x, y, z] triple in world coordinates (mm). */
0131 export type Vec3Tuple = [number, number, number];
0132
0133 /**
0134 * A named camera view for the `camera-preset` command, the navigation
0135 * cube's home button and the camera debug panel. Two shapes:
0136 *
0137 * - a fixed pose: `position`, `target` and optionally `up`;
0138 * - a view direction: the camera looks along `-direction` with `up` as the
0139 * screen up. It keeps the current orbit target and distance, unless
0140 * `fitGeometry` is set: then it centers the loaded geometry's bounding
0141 * sphere and backs off until the sphere fits the view.
0142 */
0143 export type CameraPreset =
0144 | { name: string; label?: string; position: Vec3Tuple; target: Vec3Tuple; up?: Vec3Tuple }
0145 | { name: string; label?: string; direction: Vec3Tuple; up: Vec3Tuple; fitGeometry?: boolean };
0146
0147 /** Camera presets. A later preset with the same name replaces the earlier one. */
0148 export const CAMERA_PRESETS = new InjectionToken<CameraPreset[]>(
0149 typeof ngDevMode !== 'undefined' && ngDevMode ? 'firebird.camera-presets' : '');
0150
0151 /** The registered camera presets, after the registry rule (same name: later replaces in place). */
0152 export function injectCameraPresets(): CameraPreset[] {
0153 return resolveRegistry(inject(CAMERA_PRESETS, { optional: true }) ?? [], preset => preset.name);
0154 }
0155
0156 /**
0157 * Orbit distance limits [mm] of every view. Without them, each geometry load
0158 * derives them from the geometry's bounding sphere (radius r): 0.05 r to 5 r.
0159 */
0160 export interface CameraLimits {
0161 minDistance: number;
0162 maxDistance: number;
0163 }
0164
0165 /** Pinned camera limits (`withCameraLimits`); absent means derived from the geometry. */
0166 export const CAMERA_LIMITS = new InjectionToken<CameraLimits>(
0167 typeof ngDevMode !== 'undefined' && ngDevMode ? 'firebird.camera-limits' : '');
0168
0169 // ---------------------------------------------------------------------------
0170 // Collision intro
0171 // ---------------------------------------------------------------------------
0172
0173 /**
0174 * A short animation played before an event's time animation, typically the
0175 * beams flying in and colliding. One instance per playback. The display
0176 * drives it: live from the render loop, frame by frame in offline
0177 * recordings, so `update()` must depend on `elapsedMs` alone.
0178 */
0179 export interface CollisionIntro {
0180 /** Length of the intro [ms]. */
0181 readonly durationMs: number;
0182 /** Builds the intro objects under `parent`, part of the event data (never clipped). */
0183 begin(parent: Object3D): void;
0184 /** Shows the state `elapsedMs` after the start, 0 to `durationMs`. */
0185 update(elapsedMs: number): void;
0186 /** Removes and disposes everything `begin()` built. */
0187 end(): void;
0188 }
0189
0190 /** Resolves the intro class through a dynamic import (intros pull three.js). */
0191 export type CollisionIntroLoader = () => Promise<new () => CollisionIntro>;
0192
0193 /** The collision intro (`withCollisionIntro`); absent means none. */
0194 export const COLLISION_INTRO = new InjectionToken<CollisionIntroLoader>(
0195 typeof ngDevMode !== 'undefined' && ngDevMode ? 'firebird.collision-intro' : '');
0196
0197 // ---------------------------------------------------------------------------
0198 // UI contributions
0199 // ---------------------------------------------------------------------------
0200
0201 /**
0202 * A button in the display's time toolbar (the playback controls under the
0203 * scene), placed after the rewind button. A click dispatches `command`
0204 * through the command bus with source 'ui', so the same action is
0205 * reachable from deep links and batch.
0206 */
0207 export interface ToolbarActionRegistration {
0208 /** Identity: a later action with the same id replaces this one. */
0209 id: string;
0210 /** Material Symbols icon name. */
0211 icon: string;
0212 /** Accessible name of the button. */
0213 label: string;
0214 tooltip?: string;
0215 command: AppCommand;
0216 }
0217
0218 /** Toolbar actions, in registration order. */
0219 export const TOOLBAR_ACTIONS = new InjectionToken<ToolbarActionRegistration[]>(
0220 typeof ngDevMode !== 'undefined' && ngDevMode ? 'firebird.toolbar-actions' : '');
0221
0222 /** The registered toolbar actions, after the registry rule (same id: later replaces in place). */
0223 export function injectToolbarActions(): ToolbarActionRegistration[] {
0224 return resolveRegistry(inject(TOOLBAR_ACTIONS, { optional: true }) ?? [], action => action.id);
0225 }
0226
0227 // ---------------------------------------------------------------------------
0228 // Web workers
0229 // ---------------------------------------------------------------------------
0230
0231 /**
0232 * Starts the application's web workers. Each factory creates a worker from
0233 * an entry module the application owns, so that its bundler builds the
0234 * worker: a `new Worker(new URL(...))` inside a prebuilt library is not
0235 * bundled and fails with a 404 at runtime. The entry modules call the run
0236 * functions of `@dexvis/firebird-ng/workers/geometry` and
0237 * `@dexvis/firebird-ng/workers/root-file`.
0238 */
0239 export interface FirebirdWorkers {
0240 /** The ROOT geometry worker (entry: `runGeometryWorker(self)`). */
0241 geometry: () => Worker;
0242 /** The ROOT file worker: probing and in-browser conversion (entry: `runRootFileWorker(self)`). */
0243 rootFile: () => Worker;
0244 }
0245
0246 /** The worker factories (`withWorkers`); without them ROOT geometry and in-browser conversion fail with a message naming the feature. */
0247 export const FIREBIRD_WORKERS = new InjectionToken<FirebirdWorkers>(
0248 typeof ngDevMode !== 'undefined' && ngDevMode ? 'firebird.workers' : '');
0249
0250 /**
0251 * The application's version, shown in the logo menu of the display chrome
0252 * (`withAppVersion`); absent means the menu shows none.
0253 */
0254 export const APP_VERSION = new InjectionToken<string>(
0255 typeof ngDevMode !== 'undefined' && ngDevMode ? 'firebird.app-version' : '');
0256
0257 // ---------------------------------------------------------------------------
0258 // Registry access with the precedence rule applied
0259 // ---------------------------------------------------------------------------
0260
0261 /**
0262 * The registered event loaders, in the order they are asked: registration
0263 * order, a later loader with the same `meta.id` replacing an earlier one in
0264 * place. The first loader whose `canLoad()` accepts a source loads it.
0265 */
0266 export function injectEventLoaders(): EventDataLoader[] {
0267 return resolveRegistry(inject(EVENT_LOADERS, { optional: true }) ?? [], loader => loader.meta.id);
0268 }
0269
0270 /** The registered geometry loaders; same rule as `injectEventLoaders()`. */
0271 export function injectGeometryLoaders(): GeometryDataLoader[] {
0272 return resolveRegistry(inject(GEOMETRY_LOADERS, { optional: true }) ?? [], loader => loader.meta.id);
0273 }