Back to home page

EIC code displayed by LXR

 
 

    


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

0001 /**
0002  * The example extension as a template: the piece decodes DEX columns and
0003  * rejects malformed ones, and the painter reads its ring color knob through
0004  * `this.config`, restyles live in `onConfigChanged()`, and shows rings by
0005  * event time.
0006  */
0007 import { describe, expect, it } from 'vitest';
0008 import { Group, Line, LineBasicMaterial } from 'three';
0009 import {
0010   PainterConfigView,
0011   RESERVED_PAINTER_KNOB_KEYS,
0012   assertPainterKnobKeys,
0013   defaultPainterConfigView,
0014 } from '@dexvis/firebird-core';
0015 import { Signal, signal } from '@angular/core';
0016 import { CherenkovRingPiece, CherenkovRingPieceFactory } from './cherenkov-ring.piece';
0017 import { CherenkovRingPainter } from './cherenkov-ring.painter';
0018 
0019 function ringsDex(overrides: Record<string, unknown> = {}) {
0020   return {
0021     name: 'ExampleRings',
0022     type: 'example.CherenkovRing',
0023     version: '1.0',
0024     count: 2,
0025     columns: {
0026       center: [0, 0, 1200, 100, 0, 1200],
0027       radius: [600, 450],
0028       nPhotons: [14, 9],
0029       time: [5, 8],
0030       track: [0, -1],
0031     },
0032     refs: { track: 'ExampleTracks' },
0033     ...overrides,
0034   };
0035 }
0036 
0037 /** A config view whose ring color the test changes, like a knob edit in the painter panel. */
0038 function editableConfig(ringColor: string) {
0039   const color = signal(ringColor);
0040   const view: PainterConfigView = {
0041     value: <T>(key: string) => (key === 'ringColor' ? color() : undefined) as T,
0042     signal: <T>() => color as unknown as Signal<T>,
0043   };
0044   return { view, color };
0045 }
0046 
0047 function ringColors(node: Group): string[] {
0048   const colors: string[] = [];
0049   node.traverse(object => {
0050     if (object instanceof Line) colors.push((object.material as LineBasicMaterial).color.getHexString());
0051   });
0052   return colors;
0053 }
0054 
0055 function paintRings(config?: PainterConfigView) {
0056   const piece = new CherenkovRingPieceFactory().fromDexObject(ringsDex()) as CherenkovRingPiece;
0057   const node = new Group();
0058   const painter = new CherenkovRingPainter(node, piece, config);
0059   return { piece, node, painter };
0060 }
0061 
0062 describe('CherenkovRingPiece', () => {
0063   it('decodes the columns and the ring -> track reference', () => {
0064     const piece = new CherenkovRingPieceFactory().fromDexObject(ringsDex()) as CherenkovRingPiece;
0065     expect(piece.count).toBe(2);
0066     expect(Array.from(piece.radius)).toEqual([600, 450]);
0067     expect(piece.timeRange).toEqual([5, 8]);
0068     expect(piece.entityRefs(0)).toEqual([{ column: 'track', targetPiece: 'ExampleTracks', targetIndex: 0 }]);
0069     expect(piece.entityRefs(1)).toEqual([]);
0070     expect(piece.toDexObject().columns.radius).toEqual([600, 450]);
0071   });
0072 
0073   it('rejects a column whose length disagrees with count', () => {
0074     expect(() => CherenkovRingPiece.fromDexObject(ringsDex({ count: 3 }))).toThrow(/radius|center/);
0075   });
0076 });
0077 
0078 describe('CherenkovRingPainter', () => {
0079   it('declares its ring color as a knob, with no reserved knob names', () => {
0080     expect(CherenkovRingPainter.meta.configs?.map(knob => knob.key)).toEqual(['ringColor']);
0081     expect(RESERVED_PAINTER_KNOB_KEYS).not.toContain('ringColor');
0082     expect(() => assertPainterKnobKeys(CherenkovRingPainter)).not.toThrow();
0083   });
0084 
0085   it('paints in the knob default without a config system (workers, scripts)', () => {
0086     const { node } = paintRings();
0087     expect(defaultPainterConfigView(CherenkovRingPainter.meta).value('ringColor')).toBe('#00e5ff');
0088     expect(ringColors(node)).toEqual(['00e5ff', '00e5ff']);
0089   });
0090 
0091   it('builds rings in the configured color and restyles them live on knob changes', () => {
0092     const { view, color } = editableConfig('#ff0000');
0093     const { node, painter } = paintRings(view);
0094     expect(ringColors(node)).toEqual(['ff0000', 'ff0000']);
0095 
0096     color.set('#00ff00');
0097     painter.onConfigChanged();
0098     expect(ringColors(node)).toEqual(['00ff00', '00ff00']);
0099   });
0100 
0101   it('keeps a highlighted ring white through a restyle, and returns it to the knob color', () => {
0102     const { view, color } = editableConfig('#ff0000');
0103     const { node, painter } = paintRings(view);
0104     painter.highlightEntity(1);
0105     color.set('#0000ff');
0106     painter.onConfigChanged();
0107     expect(ringColors(node)).toEqual(['0000ff', 'ffffff']);
0108 
0109     painter.unhighlightEntity(1);
0110     expect(ringColors(node)).toEqual(['0000ff', '0000ff']);
0111   });
0112 
0113   it('shows a ring once the event time passed its production time', () => {
0114     const { node, painter } = paintRings();
0115     const rings: Line[] = [];
0116     node.traverse(object => { if (object instanceof Line) rings.push(object); });
0117 
0118     painter.paint(6);
0119     expect(rings.map(ring => ring.visible)).toEqual([true, false]);
0120     painter.paint(null);
0121     expect(rings.map(ring => ring.visible)).toEqual([true, true]);
0122   });
0123 
0124   it('maps each ring object back to its entity index for selection', () => {
0125     const { painter } = paintRings();
0126     expect(painter.objectForEntity(1)?.userData['entityIndex']).toBe(1);
0127     expect(painter.objectForEntity(1)?.userData['pieceName']).toBe('ExampleRings');
0128   });
0129 });