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 }