Warning, /firebird/packages/firebird-example-extension/src/cherenkov-ring.piece.ts is written in an unsupported language. File is not indexed.
0001 /**
0002 * Model side of the example extension: a data type Firebird has never heard
0003 * of (Cherenkov rings from a RICH detector) arrives in DEX and is decoded
0004 * into a typed EventPiece.
0005 *
0006 * This file is a @dexvis/firebird-core citizen: plain TS, worker-safe, no
0007 * Angular. Only the registration lines (see index.ts) are Angular.
0008 *
0009 * The DEX v1 piece is columnar — parallel arrays, ring id == array index:
0010 *
0011 * {
0012 * "name": "ExampleRings", "type": "example.CherenkovRing", "version": "1.0",
0013 * "count": 2,
0014 * "columns": {
0015 * "center": [x0,y0,z0, x1,y1,z1], // [mm] flat, 3 values per ring
0016 * "radius": [600, 450], // [mm]
0017 * "nPhotons": [14, 9],
0018 * "time": [5, 4], // [ns]
0019 * "track": [0, -1] // optional: index into a trajectory piece
0020 * },
0021 * "refs": { "track": "ExampleTracks" }
0022 * }
0023 *
0024 * The optional "track" column shows the v1 reference mechanism: its values are
0025 * entity indexes into the piece named by refs (-1 = no reference), so linking
0026 * is a plain array lookup — no id maps.
0027 */
0028
0029 // The /model subpath on purpose: the factory is registered from app.config
0030 // (initial bundle), and the @dexvis/firebird-core root re-exports painter
0031 // modules that pull three.js. The model imports no three.js.
0032 import {
0033 EntityRefLink,
0034 EventPiece,
0035 EventPieceFactory,
0036 readPieceCount,
0037 readNumberColumn,
0038 readOptionalNumberColumn,
0039 } from '@dexvis/firebird-core/model';
0040
0041 export class CherenkovRingPiece extends EventPiece {
0042 /** Namespaced type string: reverse-dns-lite prefix prevents collisions
0043 * between experiment packages; bare names are reserved for core types. */
0044 static type = 'example.CherenkovRing';
0045
0046 /** Number of rings. */
0047 count = 0;
0048
0049 /** Ring centers [mm]: flat x,y,z per ring, `3 * count` values. */
0050 center: Float32Array = new Float32Array(0);
0051
0052 /** Ring radii [mm], `count` values. */
0053 radius: Float32Array = new Float32Array(0);
0054
0055 /** Detected photons per ring, `count` values, or null when not written. */
0056 nPhotons: Float32Array | null = null;
0057
0058 /** Ring production times [ns], `count` values, or null when not written. */
0059 time: Float32Array | null = null;
0060
0061 /** Ring -> trajectory references (-1 = none), or null when not written. */
0062 track: Float32Array | null = null;
0063
0064 /** Name of the piece the `track` column points into (from `refs`). */
0065 trackPieceName: string | null = null;
0066
0067 constructor(name: string, origin?: any) {
0068 super(name, CherenkovRingPiece.type, origin);
0069 }
0070
0071 /** Ring label: radius, plus photon count when written. */
0072 override entityLabel(ringIndex: number): string {
0073 let label = `#${ringIndex} r=${this.radius[ringIndex].toFixed(0)}`;
0074 if (this.nPhotons !== null) label += ` nphot=${this.nPhotons[ringIndex]}`;
0075 return label;
0076 }
0077
0078 /** The ring -> trajectory reference, when the writer declared it. */
0079 override entityRefs(ringIndex: number): EntityRefLink[] {
0080 if (this.track === null || this.trackPieceName === null) return [];
0081 const target = this.track[ringIndex];
0082 if (target < 0) return [];
0083 return [{ column: 'track', targetPiece: this.trackPieceName, targetIndex: Math.floor(target) }];
0084 }
0085
0086 static fromDexObject(obj: any): CherenkovRingPiece {
0087 const piece = new CherenkovRingPiece(obj['name'], obj['origin']);
0088 piece.count = readPieceCount(obj);
0089 piece.center = readNumberColumn(obj, 'center', piece.count, 3);
0090 piece.radius = readNumberColumn(obj, 'radius', piece.count);
0091 piece.nPhotons = readOptionalNumberColumn(obj, 'nPhotons', piece.count);
0092 piece.time = readOptionalNumberColumn(obj, 'time', piece.count);
0093 piece.track = readOptionalNumberColumn(obj, 'track', piece.count);
0094 piece.trackPieceName = obj['refs']?.['track'] ?? null;
0095 return piece;
0096 }
0097
0098 override toDexObject(): any {
0099 const columns: any = {
0100 center: Array.from(this.center),
0101 radius: Array.from(this.radius),
0102 };
0103 if (this.nPhotons !== null) columns.nPhotons = Array.from(this.nPhotons);
0104 if (this.time !== null) columns.time = Array.from(this.time);
0105 if (this.track !== null) columns.track = Array.from(this.track);
0106
0107 const result: any = {
0108 name: this.name,
0109 type: this.type,
0110 version: '1.0',
0111 count: this.count,
0112 columns: columns,
0113 };
0114 if (this.trackPieceName !== null) result.refs = { track: this.trackPieceName };
0115 if (this.origin !== undefined) result.origin = this.origin;
0116 return result;
0117 }
0118
0119 override get timeRange(): [number, number] | null {
0120 if (this.time === null || this.count === 0) return null;
0121 let minTime = this.time[0];
0122 let maxTime = this.time[0];
0123 for (let i = 1; i < this.count; i++) {
0124 if (this.time[i] < minTime) minTime = this.time[i];
0125 if (this.time[i] > maxTime) maxTime = this.time[i];
0126 }
0127 return [minTime, maxTime];
0128 }
0129 }
0130
0131 export class CherenkovRingPieceFactory implements EventPieceFactory {
0132 type = CherenkovRingPiece.type;
0133
0134 fromDexObject(obj: any): EventPiece {
0135 return CherenkovRingPiece.fromDexObject(obj);
0136 }
0137 }