Back to home page

EIC code displayed by LXR

 
 

    


Warning, /firebird/packages/firebird-example-extension/src/cherenkov-ring.painter.ts is written in an unsupported language. File is not indexed.

0001 /**
0002  * Painter side of the example extension: turns CherenkovRingPiece data into
0003  * three.js objects, time-aware (rings appear at their production time), with
0004  * the ring color as a painter knob.
0005  *
0006  * A painter is a @dexvis/firebird-core citizen too: it paints without DI, so
0007  * it can run in web workers. Its knobs are declared in `static meta.configs`;
0008  * the display turns each into the config key
0009  * `painters.byPiece.<pieceName>.<knob>`, renders it in the painter panel, and
0010  * hands the resolved values to the painter through `this.config`.
0011  */
0012 
0013 import { BufferGeometry, Color, Group, Line, LineBasicMaterial, Object3D, Vector3 } from 'three';
0014 import { EventPiece, EventPiecePainter, PainterConfigView, PainterMeta } from '@dexvis/firebird-core';
0015 import { CherenkovRingPiece } from './cherenkov-ring.piece';
0016 
0017 const RING_SEGMENTS = 64;
0018 
0019 /** Highlight tint of a selected ring. */
0020 const HIGHLIGHT_COLOR = 0xffffff;
0021 
0022 export class CherenkovRingPainter extends EventPiecePainter {
0023 
0024   static meta: PainterMeta = {
0025     id: 'example-cherenkov-rings',
0026     forPieceTypes: [CherenkovRingPiece.type],
0027     label: 'Cherenkov rings (example extension)',
0028     configs: [
0029       // Settable from the painter panel, a deep link
0030       // (?config.painters.byPiece.ExampleRings.ringColor=%23ff0000), the
0031       // server config or a pack's withConfigDefaults()
0032       { key: 'ringColor', default: '#00e5ff', label: 'Ring color' },
0033     ],
0034   };
0035 
0036   private ringObjects: { object: Line; time: number }[] = [];
0037   /** The index of the highlighted ring, or -1: a restyle must keep its tint. */
0038   private highlightedRing = -1;
0039 
0040   constructor(parentNode: Object3D, piece: EventPiece, config?: PainterConfigView) {
0041     super(parentNode, piece, config);
0042     const rings = piece as CherenkovRingPiece;
0043 
0044     const container = new Group();
0045     container.name = `${rings.name}_rings`;
0046     parentNode.add(container);
0047 
0048     // Columnar read: ring i lives at center[3i..3i+2], radius[i], time[i]
0049     for (let i = 0; i < rings.count; i++) {
0050       const object = this.buildRing(rings, i);
0051       container.add(object);
0052       this.ringObjects.push({ object, time: rings.time !== null ? rings.time[i] : 0 });
0053       // Selection mapping: ring id ≡ index
0054       this.registerEntityObject(i, object);
0055     }
0056   }
0057 
0058   /** A ring is a closed line strip in the XY plane at the ring's center (facing +Z).
0059    * A Line with the first point repeated: WebGPURenderer skips LineLoop objects. */
0060   private buildRing(rings: CherenkovRingPiece, ringIndex: number): Line {
0061     const cx = rings.center[3 * ringIndex];
0062     const cy = rings.center[3 * ringIndex + 1];
0063     const cz = rings.center[3 * ringIndex + 2];
0064     const radius = rings.radius[ringIndex];
0065 
0066     const points: Vector3[] = [];
0067     for (let i = 0; i <= RING_SEGMENTS; i++) {
0068       const angle = (i / RING_SEGMENTS) * Math.PI * 2;
0069       points.push(new Vector3(
0070         cx + radius * Math.cos(angle),
0071         cy + radius * Math.sin(angle),
0072         cz,
0073       ));
0074     }
0075     const geometry = new BufferGeometry().setFromPoints(points);
0076     // One material per ring: highlighting recolors a single ring
0077     const material = new LineBasicMaterial({ color: new Color(this.config.value<string>('ringColor')) });
0078     const line = new Line(geometry, material);
0079     line.name = `CherenkovRing_r${radius}`;
0080     return line;
0081   }
0082 
0083   /**
0084    * Restyles the existing rings when a knob changes: no rebuild. The display
0085    * schedules the next frame after this call (render-on-demand invalidate).
0086    */
0087   override onConfigChanged(): void {
0088     const ringColor = this.config.value<string>('ringColor');
0089     this.ringObjects.forEach(({ object }, index) => {
0090       (object.material as LineBasicMaterial).color.set(index === this.highlightedRing ? HIGHLIGHT_COLOR : ringColor);
0091     });
0092   }
0093 
0094   override highlightEntity(entityIndex: number): void {
0095     this.highlightedRing = entityIndex;
0096     this.onConfigChanged();
0097   }
0098 
0099   override unhighlightEntity(entityIndex: number): void {
0100     if (this.highlightedRing === entityIndex) this.highlightedRing = -1;
0101     this.onConfigChanged();
0102   }
0103 
0104   /** Time-aware: a ring is visible once the event time passed its production time. */
0105   override paint(time: number | null): void {
0106     for (const { object, time: ringTime } of this.ringObjects) {
0107       object.visible = time === null || ringTime <= time;
0108     }
0109   }
0110 
0111   override dispose(): void {
0112     for (const { object } of this.ringObjects) {
0113       object.geometry.dispose();
0114       (object.material as LineBasicMaterial).dispose();
0115     }
0116     this.ringObjects = [];
0117     super.dispose();
0118   }
0119 }