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