Warning, /firebird/packages/firebird-core/src/painters/default-painters.spec.ts is written in an unsupported language. File is not indexed.
0001 // The default painters stamp every entity object, so a 3D pick on any part
0002 // of an entity resolves back to (piece name, entity index) through
0003 // entityRefOf - the contract SelectionService relies on. Painted the way
0004 // workers and scripts paint: DataModelPainter + registerDefaultPainters.
0005 import { BoxGeometry, Group, Mesh, MeshBasicMaterial, Object3D } from 'three';
0006 import { DataModelPainter } from './data-model-painter';
0007 import { registerDefaultPainters } from './default-painters';
0008 import { EventPiecePainter, entityRefOf } from './event-piece-painter';
0009 import { Event } from '../model/event';
0010 import type { EventPiece } from '../model/event-piece';
0011 import { BoxHitPiece } from '../model/box-hit.piece';
0012 import { PointTrajectoryPiece } from '../model/point-trajectory.piece';
0013
0014 function hitsPiece(): BoxHitPiece {
0015 const piece = new BoxHitPiece('Hits');
0016 piece.count = 3;
0017 piece.pos = new Float32Array([0, 0, 0, 10, 0, 0, 20, 0, 0]);
0018 piece.dim = new Float32Array([1, 1, 1, 1, 1, 1, 1, 1, 1]);
0019 piece.time = new Float32Array([1, 2, 3]);
0020 return piece;
0021 }
0022
0023 function tracksPiece(): PointTrajectoryPiece {
0024 const piece = new PointTrajectoryPiece('Tracks');
0025 piece.pointColumns = ['x', 'y', 'z', 't'];
0026 piece.count = 2;
0027 piece.columns = { pdg: [11, 22], charge: [-1, 0] };
0028 piece.points = [
0029 [[0, 0, 0, 10], [10, 0, 0, 20]],
0030 [[0, 10, 0, 15], [10, 10, 0, 25], [20, 10, 0, 35]],
0031 ];
0032 return piece;
0033 }
0034
0035 function paintEvent(): DataModelPainter {
0036 const painter = new DataModelPainter();
0037 registerDefaultPainters(painter);
0038 painter.setThreeSceneParent(new Group());
0039 const event = new Event();
0040 event.pieces = [hitsPiece(), tracksPiece()];
0041 painter.setEntry(event);
0042 return painter;
0043 }
0044
0045 describe('entityRefOf with the default painters', () => {
0046 for (const [pieceName, entityCount] of [['Hits', 3], ['Tracks', 2]] as const) {
0047 it(`resolves every ${pieceName} entity object, and a child of it, to its entity`, () => {
0048 const painter = paintEvent().painterFor(pieceName)!;
0049 for (let entityIndex = 0; entityIndex < entityCount; entityIndex++) {
0050 const object = painter.objectForEntity(entityIndex)!;
0051 expect(entityRefOf(object)).toEqual({ pieceName, entityIndex });
0052
0053 const child = new Object3D();
0054 object.add(child);
0055 expect(entityRefOf(child)).toEqual({ pieceName, entityIndex });
0056 }
0057 });
0058 }
0059
0060 it('returns null for objects no painter stamped', () => {
0061 paintEvent();
0062 expect(entityRefOf(new Mesh())).toBeNull();
0063 });
0064 });
0065
0066 // A painter that keeps the base dispose() must still free what it built:
0067 // cleanupCurrentEntry() (every setEntry) disposes the whole subtree of its
0068 // node, so the renderer releases the GPU buffers of every child.
0069 describe('EventPiecePainter.dispose', () => {
0070 class NestedMeshPainter extends EventPiecePainter {
0071 readonly meshes: Mesh<BoxGeometry, MeshBasicMaterial>[] = [];
0072 constructor(node: Object3D, piece: EventPiece) {
0073 super(node, piece);
0074 const holder = new Group();
0075 for (let i = 0; i < 2; i++) {
0076 const mesh = new Mesh(new BoxGeometry(), new MeshBasicMaterial());
0077 holder.add(mesh);
0078 this.meshes.push(mesh);
0079 }
0080 node.add(holder);
0081 }
0082 paint(): void { /* nothing to animate */ }
0083 }
0084
0085 it('disposes the geometries and materials of every child after the next setEntry', () => {
0086 const painter = new DataModelPainter();
0087 painter.registerPainter(BoxHitPiece.type, NestedMeshPainter);
0088 const scene = new Group();
0089 painter.setThreeSceneParent(scene);
0090
0091 const first = new Event();
0092 first.pieces = [hitsPiece()];
0093 painter.setEntry(first);
0094 const built = painter.painterFor('Hits') as NestedMeshPainter;
0095 const disposed: string[] = [];
0096 built.meshes.forEach((mesh, index) => {
0097 mesh.geometry.addEventListener('dispose', () => disposed.push(`geometry ${index}`));
0098 mesh.material.addEventListener('dispose', () => disposed.push(`material ${index}`));
0099 });
0100
0101 painter.setEntry(new Event());
0102
0103 expect(disposed.sort()).toEqual(['geometry 0', 'geometry 1', 'material 0', 'material 1']);
0104 expect(scene.children).toEqual([]);
0105 });
0106 });