Back to home page

EIC code displayed by LXR

 
 

    


Warning, /firebird/firebird-ng/src/app/services/event-display.service.ts is written in an unsupported language. File is not indexed.

0001 import {computed, effect, Injectable, linkedSignal, Signal, signal, WritableSignal} from '@angular/core';
0002 import {Group as TweenGroup, Tween} from '@tweenjs/tween.js';
0003 import {ThreeService} from './three.service';
0004 import {GeometryService} from './geometry.service';
0005 import {DataModelService} from './data-model.service';
0006 import {UserConfigService} from './user-config.service';
0007 import {UrlService} from './url.service';
0008 
0009 
0010 import {disposeHierarchy} from '../utils/three.utils';
0011 import {ThreeEventProcessor} from '../data-pipelines/three-event.processor';
0012 import {DataModelPainter, DisplayMode} from '../painters/data-model-painter';
0013 import {AnimationManager} from "../animation/animation-manager";
0014 import {initComponentFactories} from "../model/default-components-init";
0015 
0016 
0017 @Injectable({
0018   providedIn: 'root',
0019 })
0020 export class EventDisplayService {
0021 
0022   private eventsByName = new Map<string, any>();
0023   private eventsArray: any[] = [];
0024   selectedEventKey: string | undefined;
0025 
0026   // Time
0027   //private eventDisplayMode: WritableSignal<DisplayMode> = signal(DisplayMode.Timeless);
0028   public eventTime: WritableSignal<number|null> = signal(0);
0029 
0030 
0031   public maxTime = 200;
0032   public minTime = 0;
0033 
0034   // Time animation
0035   private tweenGroup = new TweenGroup();
0036   private tween: Tween<any> | null = null;
0037   private beamAnimationTime: number = 1000;
0038 
0039   // Geometry
0040   private animateEventAfterLoad: boolean = false;
0041   private trackInfos: any | null = null; // Replace 'any' with the actual type
0042 
0043   // Painter that draws the event
0044   private painter: DataModelPainter = new DataModelPainter();
0045 
0046   // Animation manager
0047   private animationManager: AnimationManager;
0048 
0049   /** The last successfully loaded Event Data url. Switches to null on every new load attempt */
0050   public lastLoadedDexUrl:string|null = "";
0051 
0052   /** The last successfully loaded Geometry url. Switches to null on every new load attempt */
0053   public lastLoadedGeometryUrl:string|null = "";
0054 
0055   constructor(
0056     public three: ThreeService,
0057     private geomService: GeometryService,
0058     private settings: UserConfigService,
0059     private dataService: DataModelService,
0060     private urlService: UrlService
0061   ) {
0062 
0063     // Add event model factories (things that decode json to objects)
0064     initComponentFactories();
0065 
0066     // Connect painter to its scene place
0067     this.painter.setThreeSceneParent(this.three.sceneEvent);
0068 
0069     // Connect animation manager with threejs components
0070     this.animationManager = new AnimationManager(this.three.scene, this.three.camera, this.three.renderer);
0071 
0072     // On time change
0073     effect(() => {
0074       console.log("[eventDisplay] Time change effect start")
0075       const time = this.eventTime();
0076       this.painter.paint(time);
0077       console.log("[eventDisplay] Time change effect end")
0078     }, {debugName: "EventDisplayService.OnTimeChange"});
0079 
0080     effect(() => {
0081       //this.processCurrentTimeChange(this.eventTime());
0082       const geometry = this.geomService.geometry();
0083     }, {debugName: "EventDisplayService.OnTimeChange"});
0084 
0085     // On current entry change
0086     effect(() => {
0087       console.log("[eventDisplay] Entry change effect start")
0088       let event = this.dataService.currentEntry();
0089 
0090       // Make sure to clean-up even if event is null
0091       // this.painter.cleanupCurrentEntry();
0092 
0093       if(event === null || this.painter.getEntry() == event) return;
0094       this.painter.setEntry(event);
0095       this.painter.paint(null);
0096 
0097       console.log("[eventDisplay] Entry change effect end")
0098     }, {debugName: "EventDisplayService.OnEventChange"});
0099   }
0100 
0101   // ****************************************************
0102   // *************** THREE SETUP ************************
0103   // ****************************************************
0104 
0105   /**
0106    * Initialize the default three.js scene
0107    * @param container
0108    */
0109   initThree(container: string|HTMLElement) {
0110     this.three.init(container);
0111     this.painter.setThreeSceneParent(this.three.sceneEvent);
0112     this.three.startRendering();
0113 
0114     // We need this to update the animation group
0115     this.three.addFrameCallback(()=> {
0116       this.tweenGroup.update();
0117     })
0118   }
0119 
0120 
0121   // ****************************************************
0122   // *************** TIME *******************************
0123   // ****************************************************
0124 
0125   public updateEventTime(time: number) {
0126     this.eventTime.set(time);
0127   }
0128 
0129   getMaxTime(): number {
0130     return this.maxTime;
0131   }
0132 
0133   getMinTime(): number {
0134     return this.minTime;
0135   }
0136 
0137   animateTime() {
0138 
0139     let time = this.eventTime() ?? this.minTime;
0140     this.animateCurrentTime(
0141       this.maxTime,
0142       (this.maxTime - time) * 200
0143     );
0144   }
0145 
0146   stopTimeAnimation(): void {
0147     if (this.tween) {
0148       this.tween.stop(); // Stops the tween if it is running
0149       this.tween = null; // Remove reference
0150     }
0151   }
0152 
0153   rewindTime() {
0154     this.updateEventTime(0);
0155   }
0156 
0157   animateCurrentTime(targetTime: number, duration: number): void {
0158     if (this.tween) {
0159       this.stopTimeAnimation();
0160     }
0161 
0162     this.tween = new Tween({ currentTime: this.eventTime() ?? this.minTime }, this.tweenGroup)
0163       .to({ currentTime: targetTime }, duration)
0164       .onUpdate((obj) => {
0165         console.log(obj.currentTime);
0166         this.eventTime.set(obj.currentTime);
0167       })
0168       // .easing(TWEEN.Easing.Quadratic.In) // This can be changed to other easing functions
0169       .start();
0170   }
0171 
0172   animateWithCollision() {
0173     this.stopTimeAnimation();
0174     this.rewindTime();
0175     if (this.trackInfos) {
0176       for (let trackInfo of this.trackInfos) {
0177         trackInfo.trackNode.visible = false;
0178       }
0179     }
0180     // TODO
0181     // this.animationManager?.collideParticles(
0182     //   this.beamAnimationTime,
0183     //   30,
0184     //   5000,
0185     //   new Color(0xaaaaaa),
0186     //   () => {
0187     //     this.animateTime();
0188     //   }
0189     // );
0190   }
0191 
0192   timeStepBack() {
0193     // Check if we need to switch to timed display mode
0194 
0195     const time = this.eventTime() ?? this.minTime;
0196 
0197     if (time > this.minTime) this.updateEventTime(time - 1);
0198     if (time <= this.minTime) this.updateEventTime(this.minTime);
0199   }
0200 
0201   timeStep() {
0202     // Check if we need to switch to timed display mode
0203 
0204     const time = this.eventTime();
0205     if(time === null) return;
0206 
0207     if (time < this.maxTime) this.updateEventTime(time + 1);
0208     if (time > this.maxTime) this.updateEventTime(this.maxTime);
0209   }
0210 
0211   exitTimedDisplay() {
0212 
0213     this.stopTimeAnimation();
0214     this.eventTime.set(null);
0215     this.animateEventAfterLoad = false;
0216     if (this.trackInfos) {
0217       for (let trackInfo of this.trackInfos) {
0218         trackInfo.trackNode.visible = true;
0219         trackInfo.newLine.geometry.instanceCount = Infinity;
0220       }
0221     }
0222   }
0223 
0224   // ****************************************************
0225   // *************** DATA LOADING ***********************
0226   // ****************************************************
0227 
0228   /**
0229    * Load geometry
0230    */
0231   async loadGeometry(url:string, scale = 10, clearGeometry=true) {
0232     this.lastLoadedGeometryUrl = null;
0233     let { rootGeometry, threeGeometry } = await this.geomService.loadGeometry(url);
0234     if (!threeGeometry) return;
0235 
0236     // Set geometry scale
0237     if (scale) {
0238       threeGeometry.scale.setScalar(scale);
0239     }
0240 
0241     const sceneGeo = this.three.sceneGeometry;
0242 
0243     // There should be only one geometry if clearGeometry=true
0244     if(clearGeometry && sceneGeo.children.length > 0) {
0245       disposeHierarchy(sceneGeo, /* disposeSelf= */ false);
0246     }
0247 
0248     this.geomService.postProcessing(threeGeometry, this.three.clipPlanes);
0249 
0250     sceneGeo.children.push(threeGeometry);
0251     this.lastLoadedGeometryUrl = url;
0252   }
0253 
0254   /**
0255    * Load events
0256    * @private
0257    */
0258   private loadEvents() {
0259     let eventSource = this.settings.dexJsonEventSource.value;
0260     eventSource = this.urlService.resolveDownloadUrl(eventSource);
0261     let eventConfig = {
0262       eventFile:
0263         'https://firebird-eic.org/py8_all_dis-cc_beam-5x41_minq2-100_nevt-5.evt.json.zip',
0264       eventType: 'zip',
0265     };
0266     if (
0267       eventSource != 'no-events' &&
0268       !eventSource.endsWith('edm4hep.json')
0269     ) {
0270       let eventType = eventSource.endsWith('zip') ? 'zip' : 'json';
0271       let eventFile = eventSource;
0272       eventConfig = { eventFile, eventType };
0273     }
0274 
0275     if (typeof Worker !== 'undefined') {
0276       // Create a new
0277       const worker = new Worker(
0278         new URL('../workers/event-loader.worker.ts', import.meta.url)
0279       );
0280       worker.onmessage = ({ data }) => {
0281         for (let key in data) {
0282           this.eventsByName.set(key, data[key]);
0283           this.eventsArray.push(data[key]);
0284         }
0285       };
0286       worker.postMessage(eventConfig.eventFile);
0287     } else {
0288       // Web workers are not supported in this environment.
0289     }
0290   }
0291 
0292   public loadEvent(eventName: string) {
0293     const event = this.eventsByName.get(eventName);
0294     if (event) {
0295       this.buildEventDataFromJSON(event);
0296     }
0297   }
0298 
0299 
0300   async loadDexData(url:string) {
0301     this.lastLoadedDexUrl = null;
0302     const data = await this.dataService.loadDexData(url);
0303     if (data == null) {
0304       console.warn(
0305         'DataService.loadDexData() Received data is null or undefined'
0306       );
0307       return;
0308     }
0309 
0310     if (data.entries?.length ?? 0 > 0) {
0311       this.painter.setEntry(data.entries[0]);
0312       this.painter.paint(this.eventTime());
0313       this.lastLoadedDexUrl = url;
0314     } else {
0315       console.warn('DataService.loadDexData() Received data had no entries');
0316       console.log(data);
0317     }
0318   }
0319 
0320   // ****************************************************
0321   // *************** EVENTS *****************************
0322   // ****************************************************
0323 
0324   /**
0325    * Process current time change
0326    * @param value
0327    * @private
0328    */
0329   private processCurrentTimeChange(value: number|null) {
0330 
0331   }
0332 
0333   public buildEventDataFromJSON(eventData: any) {
0334     const threeEventProcessor = new ThreeEventProcessor();
0335 
0336     console.time('[buildEventDataFromJSON] BUILD EVENT');
0337 
0338     this.three.sceneEvent.clear();
0339 
0340     // Use the ThreeService to handle object groups
0341     const eventDataGroup = this.three.sceneEvent;
0342 
0343     // Event data collections by type
0344     for (const collectionType in eventData) {
0345       const collectionsOfType = eventData[collectionType];
0346 
0347       for (const collectionName in collectionsOfType) {
0348         const collection = collectionsOfType[collectionName];
0349 
0350         // // THREE.Group for this collection
0351         // const collectionGroup = new THREE.Group();
0352         // collectionGroup.name = collectionName;
0353         // eventDataGroup.add(collectionGroup);
0354         //
0355         // for (const item of collection) {
0356         //   // Object for each item
0357         //   const object = threeEventProcessor.makeObject(
0358         //     collectionType,
0359         //     collectionName,
0360         //     item
0361         //   );
0362         //
0363         //   if (object) {
0364         //     collectionGroup.add(object);
0365         //   }
0366         // }
0367       }
0368     }
0369 
0370     // Post-processing for specific event data types
0371     const mcTracksGroup = eventDataGroup.getObjectByName('mc_tracks');
0372     if (mcTracksGroup) {
0373       this.trackInfos = threeEventProcessor.processMcTracks(mcTracksGroup);
0374 
0375       let minTime = Infinity;
0376       let maxTime = 0;
0377       for (const trackInfo of this.trackInfos) {
0378         if (trackInfo.startTime < minTime) minTime = trackInfo.startTime;
0379         if (trackInfo.endTime > maxTime) maxTime = trackInfo.endTime;
0380       }
0381 
0382       this.maxTime = maxTime;
0383       this.minTime = minTime;
0384 
0385       console.log(`Tracks: ${this.trackInfos.length}`);
0386       if (this.trackInfos && this.animateEventAfterLoad) {
0387         for (const trackInfo of this.trackInfos) {
0388           trackInfo.trackNode.visible = false;
0389         }
0390       }
0391       console.timeEnd('Process tracks on event load');
0392     }
0393 
0394     // Update event metadata (not really used for now)
0395     // this.eventMetadata = {
0396     //   eventNumber: eventData.eventNumber,
0397     //   runNumber: eventData.runNumber,
0398     //   startTime: eventData.startTime * 1000, // Convert UNIX time to milliseconds
0399     // };
0400 
0401     console.timeEnd('[buildEventDataFromJSON] BUILD EVENT');
0402 
0403     if (this.animateEventAfterLoad) {
0404       this.animateWithCollision();
0405     }
0406   }
0407   }