Back to home page

EIC code displayed by LXR

 
 

    


Warning, /firebird/packages/firebird-core/src/painters/data-model-painter.ts is written in an unsupported language. File is not indexed.

0001 /**
0002  * This class is responsible in rendering Event or Frame data.
0003  * It first takes event pieces and manipulates three.js Scene
0004  * Then responsible for correct rendering at a given time
0005  */
0006 
0007 import { Event } from "../model/event";
0008 import { EventPiece } from "../model/event-piece";
0009 import { Object3D, Group } from "three";
0010 import {
0011   EventPiecePainter,
0012   PainterConfigView,
0013   PiecePainterConstructor,
0014   assertPainterKnobKeys,
0015 } from "./event-piece-painter";
0016 
0017 export enum DisplayMode
0018 {
0019   Timed = "timed",
0020   Timeless = "timeless"
0021 }
0022 
0023 /**
0024  * The painter registry starts EMPTY. Register painters explicitly:
0025  * - Workers/scripts (no DI): call `registerDefaultPainters(painter)` from
0026  *   ./default-painters, then `registerPainter()` for custom types.
0027  * - The Angular app contributes painters through the `PAINTERS` DI token
0028  *   (see firebird-ng `provideFirebird()` / `withPainter()`).
0029  *
0030  * Several painters may register for one piece type. Which one paints a piece
0031  * is decided per piece by `painterSelector` (the app binds it to the config
0032  * key `painters.byPiece.<pieceName>`); without a selector the first
0033  * registered painter wins.
0034  */
0035 export class DataModelPainter {
0036   private threeParentNode: Object3D | null = null;
0037   private entry: Event | null = null;
0038   private painters: EventPiecePainter[] = [];
0039 
0040   /** Registered painter classes per piece type, in registration order. */
0041   piecePainterRegistry: { [type: string]: PiecePainterConstructor[] } = {};
0042 
0043   /**
0044    * Optional hook choosing which registered painter paints a piece.
0045    * Return undefined to fall back to the first registered painter.
0046    * The Angular layer binds this to the painter-selection config keys;
0047    * workers/scripts may leave it unset.
0048    */
0049   painterSelector:
0050     | ((piece: EventPiece, candidates: readonly PiecePainterConstructor[]) => PiecePainterConstructor | undefined)
0051     | null = null;
0052 
0053   /**
0054    * Optional hook supplying the resolved config view for a painter instance.
0055    * The Angular layer binds this to ConfigService-declared keys; without it
0056    * painters run on their declared defaults.
0057    */
0058   configViewProvider:
0059     | ((piece: EventPiece, painterClass: PiecePainterConstructor) => PainterConfigView | undefined)
0060     | null = null;
0061 
0062   public setThreeSceneParent(parentNode: Object3D) {
0063     this.threeParentNode = parentNode;
0064   }
0065 
0066   public cleanupCurrentEntry() {
0067     for (let painter of this.painters) {
0068       painter.dispose();
0069     }
0070     this.painters = [];
0071   }
0072 
0073   public getEntry(): Event|null {
0074     return this.entry;
0075   }
0076 
0077   /** The painter drawing the piece with this name in the current entry, or null. */
0078   public painterFor(pieceName: string): EventPiecePainter | null {
0079     return this.painters.find(painter => painter.pieceName === pieceName) ?? null;
0080   }
0081 
0082   /** The painters of the current entry (one per painted piece). */
0083   public getPainters(): readonly EventPiecePainter[] {
0084     return this.painters;
0085   }
0086 
0087   /** The painter classes registered for a piece type, in registration order. */
0088   public paintersForType(pieceType: string): readonly PiecePainterConstructor[] {
0089     return this.piecePainterRegistry[pieceType] ?? [];
0090   }
0091 
0092   public setEntry(entry: Event): void {
0093     this.cleanupCurrentEntry();
0094     this.entry = entry;
0095 
0096     if (!this.threeParentNode) {
0097       throw new Error('Three.js parent node is not set.');
0098     }
0099 
0100     const entryStart = performance.now();
0101     for (const piece of entry.pieces) {
0102       const candidates = this.piecePainterRegistry[piece.type] ?? [];
0103       if (candidates.length === 0) {
0104         console.warn(`No piece painter registered for piece type: ${piece.type}`);
0105         continue;
0106       }
0107       const PainterClass = this.painterSelector?.(piece, candidates) ?? candidates[0];
0108 
0109       let pieceNode = new Group();
0110       pieceNode.name = piece.name;
0111       pieceNode.userData['piece'] = piece;
0112       this.threeParentNode.add(pieceNode);
0113 
0114       const configView = this.configViewProvider?.(piece, PainterClass);
0115       const painterStart = performance.now();
0116       const painter = new PainterClass(pieceNode, piece, configView);
0117       const painterMs = performance.now() - painterStart;
0118       if (painterMs > 10) {
0119         console.log(`[load-timing] painter '${piece.name}' (${PainterClass.name}): ${painterMs.toFixed(1)} ms`);
0120       }
0121 
0122       this.painters.push(painter);
0123     }
0124     const entryMs = performance.now() - entryStart;
0125     if (entryMs > 100) {
0126       console.log(`[load-timing] setEntry total (${entry.pieces.length} pieces): ${entryMs.toFixed(1)} ms`);
0127     }
0128   }
0129 
0130   /**
0131    * Registers a custom painter class provided by the user. Registering more
0132    * classes for the same type adds alternatives (selectable per piece);
0133    * registering the same class twice is a no-op.
0134    *
0135    * @param pieceType - The type of the piece for which the painter should be used.
0136    * @param painterClass - The user's custom EventPiecePainter subclass.
0137    * @throws Error when the painter's meta declares a reserved knob key
0138    *   (see `RESERVED_PAINTER_KNOB_KEYS`).
0139    */
0140   public registerPainter(pieceType: string, painterClass: PiecePainterConstructor): void {
0141     if (!pieceType || !painterClass) {
0142       throw new Error('Both pieceType and painterClass are required to register a custom painter.');
0143     }
0144     assertPainterKnobKeys(painterClass);
0145     const list = this.piecePainterRegistry[pieceType] ??= [];
0146     if (!list.includes(painterClass)) {
0147       list.push(painterClass);
0148     }
0149   }
0150 
0151   /** paints scene at the current time. null - no-time mode (draws everything) */
0152   public paint(time: number | null): void {
0153     for (let painter of this.painters) {
0154       painter.paint(time);
0155     }
0156   }
0157 }