Back to home page

EIC code displayed by LXR

 
 

    


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

0001 /**
0002  * The `with*()` feature functions: the composition API through which an
0003  * application (Firebird's own or an external experiment's) assembles its
0004  * event display. `provideFirebird()` (provide-firebird.ts) installs them.
0005  *
0006  * API shape: one contribution per `with*()` call. Plurality comes from
0007  * composition: `provideFirebird(...)` is variadic, and `firebirdFeatures(...)`
0008  * and `firebirdPack(id, ...)` bundle features (experiment packs).
0009  *
0010  * The composition mechanism (feature type and identity, multi-provider
0011  * plumbing, config layering, command bus, URL startup, server config
0012  * loading) comes from `@dexvis/app-features`; this file adds Firebird's own
0013  * extension points and its URL shorthand grammar. The registry precedence
0014  * rule is documented in tokens.ts.
0015  */
0016 
0017 import { Type } from '@angular/core';
0018 import {
0019   AppFeature,
0020   AppFeatureInput,
0021   appFeaturePack,
0022   appFeatures,
0023   contributeClass,
0024   contributeValue,
0025   withConfigDefaults,
0026   withUrlShorthand,
0027 } from '@dexvis/app-features';
0028 import type {
0029   PiecePainterConstructor,
0030   EventPieceFactory,
0031   GeometryDataLoader,
0032   EventDataLoader,
0033   DataCatalog,
0034 } from '@dexvis/firebird-core';
0035 import {
0036   APP_VERSION,
0037   CAMERA_LIMITS,
0038   CAMERA_PRESETS,
0039   COLLISION_INTRO,
0040   CameraLimits,
0041   CameraPreset,
0042   CollisionIntroLoader,
0043   DATA_CATALOGS,
0044   DATA_SELECTOR_TABS,
0045   DataSelectorTabRegistration,
0046   EVENT_LOADERS,
0047   EVENT_PIECE_FACTORIES,
0048   FIREBIRD_WORKERS,
0049   FirebirdWorkers,
0050   GEOMETRY_LOADERS,
0051   GEOMETRY_POST_PROCESSORS,
0052   GEOMETRY_THEMES,
0053   LAZY_THREE_EXTENSIONS,
0054   PAINTERS,
0055   ROOT_GEOMETRY_RULES,
0056   THREE_EXTENSIONS,
0057   TOOLBAR_ACTIONS,
0058   ToolbarActionRegistration,
0059 } from './tokens';
0060 import type { LazyThreeExtensionLoader, ThreeExtension } from './three-extension';
0061 import type {
0062   GeometryPostProcessorRegistration,
0063   GeometryThemeRegistration,
0064   RootGeometryRulesSource,
0065 } from './geometry-pipeline';
0066 
0067 /**
0068  * A Firebird feature: the providers contributed by one `with*()` call, or a
0069  * composition of features. The same type as `AppFeature` from
0070  * `@dexvis/app-features`, so features from both compose freely.
0071  */
0072 export type FirebirdFeature = AppFeature;
0073 
0074 /**
0075  * Composes features into one feature. Falsy entries are skipped so packs can
0076  * include conditional features. The same function as `appFeatures()` from
0077  * `@dexvis/app-features`.
0078  */
0079 export const firebirdFeatures: (...features: AppFeatureInput[]) => FirebirdFeature = appFeatures;
0080 
0081 /**
0082  * Composes features into a pack with an id: the mechanism behind experiment
0083  * packs. The id makes the pack replaceable and removable
0084  * (`withoutFeatures(id)`), a pack installed twice is installed once, and
0085  * development builds name the pack in collision warnings.
0086  *
0087  * ```ts
0088  * export function withMyExperiment(): FirebirdFeature {
0089  *   return firebirdPack('my-experiment', withEventPiece(MyFactory), withDefaultGeometry(url));
0090  * }
0091  * ```
0092  *
0093  * The same function as `appFeaturePack()` from `@dexvis/app-features`.
0094  */
0095 export const firebirdPack: (id: string, ...features: AppFeatureInput[]) => FirebirdFeature = appFeaturePack;
0096 
0097 /** Registers an event piece factory (DEX type decoder). The later factory for a type wins. */
0098 export function withEventPiece(factory: Type<EventPieceFactory>): FirebirdFeature {
0099   return contributeClass(EVENT_PIECE_FACTORIES, factory, { kind: 'event-piece' });
0100 }
0101 
0102 /**
0103  * Registers a painter for a piece type. The type comes from
0104  * `opts.forPieceType`, or from the painter's static `meta.forPieceTypes`.
0105  * The first painter registered for a type is its default.
0106  */
0107 export function withPainter(
0108   painterClass: PiecePainterConstructor,
0109   opts?: { forPieceType?: string },
0110 ): FirebirdFeature {
0111   const meta = (painterClass as unknown as { meta?: { forPieceTypes?: string[] } }).meta;
0112   const types = opts?.forPieceType ? [opts.forPieceType] : (meta?.forPieceTypes ?? []);
0113   if (types.length === 0) {
0114     throw new Error(`withPainter(${painterClass.name}): pass { forPieceType } or declare static meta.forPieceTypes`);
0115   }
0116   return firebirdFeatures(...types.map(forPieceType =>
0117     contributeValue(PAINTERS, { forPieceType, painterClass }, { kind: 'painter' })));
0118 }
0119 
0120 /**
0121  * Registers a painter through a dynamic import, keeping heavy painter code
0122  * (three.js materials etc.) out of the initial bundle. The class resolves
0123  * before the first event is painted; its place among the painters of the
0124  * type is its registration position, whenever its chunk arrives.
0125  */
0126 export function withLazyPainter(
0127   forPieceType: string,
0128   load: () => Promise<PiecePainterConstructor>,
0129 ): FirebirdFeature {
0130   return contributeValue(PAINTERS, { forPieceType, load }, { kind: 'painter' });
0131 }
0132 
0133 /** Registers a rendering-machinery extension (instantiated through DI). */
0134 export function withThreeExtension(extension: Type<ThreeExtension>): FirebirdFeature {
0135   return contributeClass(THREE_EXTENSIONS, extension, { kind: 'three-extension' });
0136 }
0137 
0138 /**
0139  * Registers a lazily-loaded extension. The dynamic import keeps it out of the
0140  * initial bundle; it is loaded and initialized after the scene is up.
0141  */
0142 export function withLazyThreeExtension(load: LazyThreeExtensionLoader): FirebirdFeature {
0143   return contributeValue(LAZY_THREE_EXTENSIONS, load, { kind: 'three-extension' });
0144 }
0145 
0146 /** Registers a geometry format/scheme loader. A later loader with the same `meta.id` replaces it in place. */
0147 export function withGeometryLoader(loader: Type<GeometryDataLoader>): FirebirdFeature {
0148   return contributeClass(GEOMETRY_LOADERS, loader, { kind: 'geometry-loader' });
0149 }
0150 
0151 /** Registers an event data format loader. A later loader with the same `meta.id` replaces it in place. */
0152 export function withEventLoader(loader: Type<EventDataLoader>): FirebirdFeature {
0153   return contributeClass(EVENT_LOADERS, loader, { kind: 'event-loader' });
0154 }
0155 
0156 /**
0157  * Contributes datasets to the data selector: named presets (geometry + events),
0158  * tagged for the physics picker, and URL lists for manual pick. Several
0159  * contributions merge in registration order; the server's config.jsonc
0160  * `dataCatalog` and a remote `catalog.url` file add to them at runtime.
0161  *
0162  * ```ts
0163  * withDataCatalog({
0164  *   facets: [{ key: 'process', label: 'Process', values: { 'dis-nc': { label: 'DIS NC' } } }],
0165  *   entries: [{ name: 'DIS NC 10x100', geometry: 'exp://detector.root',
0166  *               events: 'https://host/nc_10x100.firebird.zip', tags: { process: 'dis-nc', beam: '10x100' } }],
0167  * })
0168  * ```
0169  */
0170 export function withDataCatalog(catalog: DataCatalog): FirebirdFeature {
0171   return contributeValue(DATA_CATALOGS, catalog, { kind: 'data-catalog' });
0172 }
0173 
0174 /**
0175  * Adds a tab to the data selector control. The component must be a dynamic
0176  * import (tab UI pulls Material/forms code that must stay out of the initial
0177  * bundle); it reads and writes the shared `DataSelectionService.draft`.
0178  * Feature id `data-selector-tab:<id>`: a later tab with the same id replaces
0179  * this one, and `withoutFeatures('data-selector-tab:<id>')` drops it.
0180  *
0181  * ```ts
0182  * withDataSelectorTab({ id: 'campaigns', label: 'Campaigns', order: 15,
0183  *   load: () => import('./campaigns-tab.component').then(m => m.CampaignsTabComponent) })
0184  * ```
0185  */
0186 export function withDataSelectorTab(tab: DataSelectorTabRegistration): FirebirdFeature {
0187   return contributeValue(DATA_SELECTOR_TABS, tab, { kind: 'data-selector-tab', id: `data-selector-tab:${tab.id}` });
0188 }
0189 
0190 /**
0191  * Sets the detector geometry the display loads when nothing else selects one.
0192  * Sugar for `withConfigDefaults({'geometry.selectedGeometry': url})`: the
0193  * same precedence applies, so server config, a saved user choice, and
0194  * `?geometry=` deep links all override it.
0195  */
0196 export function withDefaultGeometry(url: string): FirebirdFeature {
0197   return withConfigDefaults({ 'geometry.selectedGeometry': url });
0198 }
0199 
0200 /**
0201  * Contributes pre-build TGeo rules: named edit rule sets (selected by
0202  * `geometry.rootFilterName`) and named cut lists (selected by
0203  * `geometry.cutListName`). The rules are data, posted to the geometry
0204  * worker with each load; pass a dynamic import to keep large rule data out
0205  * of the initial bundle. Contributions merge; a later rule set or cut list
0206  * with the same name replaces the earlier one.
0207  *
0208  * ```ts
0209  * withRootGeometryRules(() => import('./my-geometry-rules').then(m => m.myGeometryRules))
0210  * ```
0211  */
0212 export function withRootGeometryRules(rules: RootGeometryRulesSource): FirebirdFeature {
0213   return contributeValue(ROOT_GEOMETRY_RULES, rules, { kind: 'root-geometry-rules' });
0214 }
0215 
0216 /**
0217  * Registers a geometry theme: subdetector rule sets (colors, merging,
0218  * outlines) applied after each load when config `geometry.themeName` is
0219  * `id`. Feature id `geometry-theme:<id>`.
0220  *
0221  * ```ts
0222  * withGeometryTheme({ id: 'my-colors', label: 'My colors',
0223  *   load: () => import('./my-theme').then(m => m.myRuleSets) })
0224  * ```
0225  */
0226 export function withGeometryTheme(theme: GeometryThemeRegistration): FirebirdFeature {
0227   return contributeValue(GEOMETRY_THEMES, theme, { kind: 'geometry-theme', id: `geometry-theme:${theme.id}` });
0228 }
0229 
0230 /**
0231  * Registers main-thread code that runs after each geometry load, once the
0232  * geometry sits in the scene and before the first frame shows it.
0233  * Processors run in registration order, each after the ids in its `after`.
0234  * Feature id `geometry-post-processor:<id>`.
0235  *
0236  * ```ts
0237  * withGeometryPostProcessor({ id: 'my-mirrors', after: ['my-arranger'],
0238  *   load: () => import('./my-mirrors').then(m => m.MirrorProcessor) })
0239  * ```
0240  */
0241 export function withGeometryPostProcessor(processor: GeometryPostProcessorRegistration): FirebirdFeature {
0242   return contributeValue(GEOMETRY_POST_PROCESSORS, processor,
0243     { kind: 'geometry-post-processor', id: `geometry-post-processor:${processor.id}` });
0244 }
0245 
0246 /**
0247  * Registers a named camera view for the `camera-preset` command
0248  * (`?cmd=camera-preset:<name>`), the navigation cube and the camera debug
0249  * panel. Feature id `camera-preset:<name>`: a later preset with the same name
0250  * replaces this one, so a pack pins `home` by registering it.
0251  *
0252  * ```ts
0253  * withCameraPreset({ name: 'endcap', position: [0, 2000, 9000], target: [0, 0, 3000] })
0254  * ```
0255  */
0256 export function withCameraPreset(preset: CameraPreset): FirebirdFeature {
0257   return contributeValue(CAMERA_PRESETS, preset, { kind: 'camera-preset', id: `camera-preset:${preset.name}` });
0258 }
0259 
0260 /**
0261  * Pins the orbit distance limits [mm] of every view. Without this feature,
0262  * each geometry load derives them from the geometry's bounding sphere.
0263  * One setting per application (feature id `camera-limits`).
0264  */
0265 export function withCameraLimits(limits: CameraLimits): FirebirdFeature {
0266   return { kind: 'camera-limits', id: 'camera-limits', providers: [{ provide: CAMERA_LIMITS, useValue: limits }] };
0267 }
0268 
0269 /**
0270  * Sets the collision intro: the animation played before an event's time
0271  * animation (the `animate-collision` command, offline recordings). A dynamic
0272  * import: intros pull three.js. Without it, events animate with no intro.
0273  * One per application (feature id `collision-intro`).
0274  *
0275  * ```ts
0276  * withCollisionIntro(() => import('./beams-intro').then(m => m.BeamsIntro))
0277  * ```
0278  */
0279 export function withCollisionIntro(load: CollisionIntroLoader): FirebirdFeature {
0280   return { kind: 'collision-intro', id: 'collision-intro', providers: [{ provide: COLLISION_INTRO, useValue: load }] };
0281 }
0282 
0283 /**
0284  * Adds a button to the display's time toolbar that dispatches a command.
0285  * Feature id `toolbar-action:<id>`.
0286  *
0287  * ```ts
0288  * withToolbarAction({ id: 'collision', icon: 'close_fullscreen', label: 'Collision',
0289  *   tooltip: 'Animate with beam particles collision', command: { type: 'animate-collision' } })
0290  * ```
0291  */
0292 export function withToolbarAction(action: ToolbarActionRegistration): FirebirdFeature {
0293   return contributeValue(TOOLBAR_ACTIONS, action, { kind: 'toolbar-action', id: `toolbar-action:${action.id}` });
0294 }
0295 
0296 /**
0297  * Starts Firebird's web workers from entry modules of the application, which
0298  * its bundler builds (feature id `workers`). Required for ROOT geometry and
0299  * for converting ROOT event files in the browser.
0300  *
0301  * ```ts
0302  * withWorkers({
0303  *   geometry: () => new Worker(new URL('./workers/geometry.worker', import.meta.url), { type: 'module' }),
0304  *   rootFile: () => new Worker(new URL('./workers/root-file.worker', import.meta.url), { type: 'module' }),
0305  * })
0306  * ```
0307  *
0308  * with `geometry.worker.ts` holding `runGeometryWorker(self)` (from
0309  * `@dexvis/firebird-ng/workers/geometry`) and `root-file.worker.ts` holding
0310  * `runRootFileWorker(self)` (from `@dexvis/firebird-ng/workers/root-file`).
0311  * The `new Worker(new URL(...))` expressions
0312  * must stay literal in application code: that is the pattern the Angular
0313  * build recognizes and bundles.
0314  */
0315 export function withWorkers(workers: FirebirdWorkers): FirebirdFeature {
0316   return { kind: 'workers', id: 'workers', providers: [{ provide: FIREBIRD_WORKERS, useValue: workers }] };
0317 }
0318 
0319 /**
0320  * Sets the version the display chrome shows in its logo menu: the
0321  * application's own release, which the library cannot know. One per
0322  * application (feature id `app-version`).
0323  *
0324  * ```ts
0325  * import { version } from '../../package.json';
0326  * provideFirebird(withAppVersion(version))
0327  * ```
0328  */
0329 export function withAppVersion(version: string): FirebirdFeature {
0330   return { kind: 'app-version', id: 'app-version', providers: [{ provide: APP_VERSION, useValue: version }] };
0331 }
0332 
0333 /**
0334  * Firebird's URL shorthand grammar: `?geometry=<url>`, `?dex=<url>` and
0335  * `?event=<N>` stand for `?cmd=open-geometry:<url>`, `?cmd=open-dex:<url>`
0336  * and `?cmd=show-event:<N>`, queued in this order before the generic `?cmd=`
0337  * list. The built-in command handlers build the commands. Part of the
0338  * built-ins (`withFirebirdBuiltins()`).
0339  */
0340 export function withFirebirdUrlShorthands(): FirebirdFeature {
0341   return firebirdPack('firebird.url-shorthands',
0342     withUrlShorthand('geometry', 'open-geometry'),
0343     withUrlShorthand('dex', 'open-dex'),
0344     withUrlShorthand('event', 'show-event'),
0345   );
0346 }