Back to home page

EIC code displayed by LXR

 
 

    


Warning, /firebird/firebird-ng/src/app/pages/main-display/main-display.component.ts is written in an unsupported language. File is not indexed.

0001 import {
0002   Component,
0003   OnInit,
0004   AfterViewInit,
0005   Input,
0006   ViewChild, OnDestroy, TemplateRef, ElementRef, signal
0007 } from '@angular/core';
0008 
0009 import {ALL_GROUPS} from '../../services/geometry.service';
0010 import {GameControllerService} from '../../services/game-controller.service';
0011 import {LocalStorageService} from '../../services/local-storage.service';
0012 
0013 import {SceneTreeComponent} from '../../components/scene-tree/scene-tree.component';
0014 import {ShellComponent} from '../../components/shell/shell.component';
0015 import {ToolPanelComponent} from '../../components/tool-panel/tool-panel.component';
0016 import {EventSelectorComponent} from '../../components/event-selector/event-selector.component';
0017 import {ObjectClippingComponent} from '../../components/object-clipping/object-clipping.component';
0018 import {PhoenixThreeFacade} from "../../utils/phoenix-three-facade";
0019 
0020 import {MatSnackBar} from '@angular/material/snack-bar';
0021 import {MatIcon} from '@angular/material/icon';
0022 import { MatIconButton} from '@angular/material/button';
0023 import {MatTooltip} from '@angular/material/tooltip';
0024 import {EventDisplay} from "phoenix-event-display";
0025 
0026 import {PerfStatsComponent} from "../../components/perf-stats/perf-stats.component";
0027 import {EventDisplayService} from "../../services/event-display.service";
0028 import {EventTimeControlComponent} from "../../components/event-time-control/event-time-control.component";
0029 import {ServerConfigService} from "../../services/server-config.service";
0030 import {CubeViewportControlComponent} from "../../components/cube-viewport-control/cube-viewport-control.component";
0031 import {LegendWindowComponent} from "../../components/legend-window/legend-window.component";
0032 import {PainterConfigPageComponent} from "../../services/configurator/painter-config-page.component";
0033 import {NgIf} from "@angular/common";
0034 import {TrackPainterConfig} from "../../services/track-painter-config";
0035 import {ObjectRaycastComponent} from "../../components/object-raycast/object-raycast.component";
0036 import {MatProgressSpinner} from "@angular/material/progress-spinner";
0037 import GUI from 'lil-gui';
0038 
0039 /**
0040  * This MainDisplayComponent:
0041  *  - Initializes and uses ThreeService (which sets up scene, camera, controls, etc.).
0042  *  - Loads geometry via GeometryService, attaches it to scene.
0043  *  - Loads event data (Dex or custom) via DataModelService, builds objects in "EventData" group.
0044  *  - Uses EicAnimationsManager for collisions/expansions.
0045  *  - Has leftover UI logic for sliders, time stepping, left/right pane toggling, etc.
0046  *  - Has *no* references to phoenix-event-display or eventDisplay.
0047  */
0048 @Component({
0049   selector: 'app-main-display',
0050   templateUrl: './main-display.component.html',
0051   styleUrls: ['./main-display.component.scss'],
0052   imports: [
0053     MatIcon,
0054     MatTooltip,
0055     MatIconButton,
0056     SceneTreeComponent,
0057     ShellComponent,
0058     ToolPanelComponent,
0059     EventSelectorComponent,
0060     ObjectClippingComponent,
0061     PerfStatsComponent,
0062     EventTimeControlComponent,
0063     CubeViewportControlComponent,
0064     LegendWindowComponent,
0065     PainterConfigPageComponent,
0066     NgIf,
0067     ObjectRaycastComponent,
0068     MatProgressSpinner,
0069   ]
0070 })
0071 export class MainDisplayComponent implements OnInit, AfterViewInit, OnDestroy {
0072   @Input()
0073   eventDataImportOptions: string[] = []; // example, if you used them in UI
0074 
0075   @ViewChild('displayHeaderControls', {static: true})
0076   displayHeaderControls!: TemplateRef<any>;
0077 
0078   @ViewChild('eventDisplay')
0079   eventDisplayDiv!: ElementRef;
0080 
0081   // For referencing child components
0082   @ViewChild(ShellComponent)
0083   displayShellComponent!: ShellComponent;
0084 
0085   @ViewChild(SceneTreeComponent)
0086   geometryTreeComponent: SceneTreeComponent | null | undefined;
0087 
0088   @ViewChild(CubeViewportControlComponent)
0089   private cubeControl!: CubeViewportControlComponent;
0090 
0091   message = "";
0092 
0093   loaded: boolean = false;
0094 
0095   // The geometry group switching index, used in cycleGeometry()
0096   private geometryGroupSwitchingIndex = ALL_GROUPS.length;
0097   currentGeometry: string = 'All';
0098 
0099   // UI toggles
0100   isLeftPaneOpen: boolean = false;
0101   isRightPaneOpen: boolean = false;
0102 
0103   // Loading indicators
0104   loadingDex     = signal(false);
0105   loadingEdm     = signal(false);
0106   loadingGeometry = signal(false);
0107 
0108   // lil GUI for right panel
0109   lilGui = new GUI();
0110   showGui = false;
0111 
0112   // Phoenix API
0113   private facade: PhoenixThreeFacade = new PhoenixThreeFacade(new EventDisplay());
0114 
0115   constructor(
0116     private controller: GameControllerService,
0117     private snackBar: MatSnackBar,
0118     public eventDisplay: EventDisplayService,
0119     private userConfig: LocalStorageService,
0120     private serverConfig: ServerConfigService,
0121   ) {
0122 
0123   }
0124 
0125 
0126   async ngOnInit() {
0127     // Initialize the ThreeService scene/camera/renderer/controls
0128     this.eventDisplay.initThree('eventDisplay');
0129 
0130     // The facade will be initialized in three.service
0131     this.facade.initializeScene()
0132 
0133 
0134     this.controller.buttonY.onPress.subscribe((value) => {
0135       if (value) {
0136         // TODO this.cycleGeometry();
0137       }
0138     });
0139   }
0140 
0141 
0142   // 2) AFTER VIEW INIT => handle resizing with DisplayShell or window
0143   ngAfterViewInit(): void {
0144 
0145     // Load JSON based data files
0146     this.initDexEventSource();
0147 
0148     // Load Root file based data files
0149     this.initRootData();
0150 
0151     if (this.displayShellComponent) {
0152       const resizeInvoker = () => {
0153         setTimeout(() => {
0154           this.onRendererElementResize();
0155         }, 100);
0156       };
0157       this.displayShellComponent.onVisibilityChangeLeft.subscribe(resizeInvoker);
0158       this.displayShellComponent.onVisibilityChangeRight.subscribe(resizeInvoker);
0159       this.displayShellComponent.onEndResizeLeft.subscribe(() => this.onRendererElementResize());
0160       this.displayShellComponent.onEndResizeRight.subscribe(() => this.onRendererElementResize());
0161     }
0162 
0163     this.initCubeViewportControl();
0164 
0165     window.addEventListener('resize', () => {
0166       this.onRendererElementResize();
0167     });
0168 
0169     // When sidebar is collapsed/opened, the main container, i.e. #eventDisplay offsetWidth is not yet updated.
0170     // This leads to a not proper resize  processing. We add 100ms delay before calling a function
0171     const this_obj = this;
0172     const resizeInvoker = function () {
0173       setTimeout(() => {
0174         this_obj.onRendererElementResize();
0175       }, 100);  // 100 milliseconds = 0.1 seconds
0176     };
0177     resizeInvoker();
0178 
0179     // Loads the geometry (do it last as it might be long)
0180     this.initGeometry();
0181 
0182     // Init gui
0183     this.lilGui.add(this.eventDisplay.three.perspectiveCamera.position, 'x').listen();
0184     this.lilGui.add(this.eventDisplay.three.perspectiveCamera.position, 'y').listen();
0185     this.lilGui.add(this.eventDisplay.three.perspectiveCamera.position, 'z').listen();
0186 
0187     // GUI settings
0188     this.lilGui.domElement.style.top = '64px';
0189     this.lilGui.domElement.style.right = '120px';
0190     this.lilGui.domElement.style.display = 'none';
0191 
0192   }
0193 
0194   // 3) UI - Toggling panes
0195   toggleLeftPane() {
0196     this.displayShellComponent?.toggleLeftPane();
0197     this.isLeftPaneOpen = !this.isLeftPaneOpen;
0198   }
0199 
0200   toggleRightPane() {
0201     this.displayShellComponent?.toggleRightPane();
0202     this.isRightPaneOpen = !this.isRightPaneOpen;
0203   }
0204 
0205   // 4) Method to initialize CubeViewportControl with the existing Three.js objects
0206   private initCubeViewportControl(): void {
0207     const {scene, camera, renderer} = this.eventDisplay.three;
0208     if (this.cubeControl && scene && camera && renderer) {
0209       // Pass the external scene, camera, and renderer to the cube control
0210       this.cubeControl.initWithExternalScene(scene, camera, renderer);
0211       this.cubeControl.gizmo.attachControls(this.eventDisplay.three.controls);
0212       this.cubeControl.gizmo.camera
0213     }
0214 
0215     const thisPointer = this;
0216     this.eventDisplay.three.addFrameCallback(() => {
0217       if (thisPointer.cubeControl?.gizmo) {
0218         thisPointer.cubeControl.gizmo.render();
0219       }
0220     });
0221   }
0222 
0223 
0224   showError(message: string) {
0225     this.snackBar.open(message, 'Dismiss', {
0226       duration: 7000, // Auto-dismiss after X ms
0227       // verticalPosition: 'top', // Place at the top of the screen
0228       panelClass: ['error-snackbar']
0229     });
0230   }
0231 
0232 
0233   ngOnDestroy(): void {
0234     // Clear the custom controls when leaving the page
0235   }
0236 
0237 
0238   // Called when we want to recalculate the size of the canvas
0239   private onRendererElementResize() {
0240     let {width, height} = this.displayShellComponent.getMainAreaVisibleDimensions();
0241     console.log(`[RendererResize] New size: ${width}x${height} px`);
0242 
0243     // Delegate resizing to ThreeService
0244     this.eventDisplay.three.setSize(width, height);
0245     if (this.cubeControl?.gizmo) {
0246       this.cubeControl.gizmo.update();
0247     }
0248   }
0249 
0250   // 8) GEOMETRY TOGGLING
0251   // showAllGeometries() {
0252   //   this.geometryGroupSwitchingIndex = ALL_GROUPS.length;
0253   //   if (!this.geomService.subdetectors) return;
0254   //   for (let detector of this.geomService.subdetectors) {
0255   //     detector.geometry.visible = true;
0256   //   }
0257   // }
0258   // showGeometryGroup(groupName: string) {
0259   //   if (!this.geomService.subdetectors) return;
0260   //   for (let detector of this.geomService.subdetectors) {
0261   //     detector.geometry.visible = (detector.groupName === groupName);
0262   //   }
0263   // }
0264   // cycleGeometry() {
0265   //   this.geometryGroupSwitchingIndex++;
0266   //   if (this.geometryGroupSwitchingIndex > ALL_GROUPS.length) {
0267   //     this.geometryGroupSwitchingIndex = 0;
0268   //   }
0269   //   if (this.geometryGroupSwitchingIndex === ALL_GROUPS.length) {
0270   //     this.showAllGeometries();
0271   //     this.currentGeometry = 'All';
0272   //   } else {
0273   //     this.currentGeometry = ALL_GROUPS[this.geometryGroupSwitchingIndex];
0274   //     this.showGeometryGroup(this.currentGeometry);
0275   //   }
0276   // }
0277 
0278   public formatCurrentTime(value: number): string {
0279     return value.toFixed(1);
0280   }
0281 
0282   changeCurrentTime(event: Event) {
0283     if (!event) return;
0284     const input = event.target as HTMLInputElement;
0285     const value = parseFloat(input.value);
0286     this.eventDisplay.updateEventTime(value);
0287   }
0288 
0289   // 10) SCENE TREE / UI
0290   private updateSceneTreeComponent() {
0291     // Example: rename lights
0292     const scene = this.eventDisplay.three.scene;
0293     if (this.geometryTreeComponent) {
0294       this.geometryTreeComponent.refreshSceneTree();
0295     }
0296   }
0297 
0298   onDebugButton() {
0299     this.showGui = !this.showGui;
0300 
0301     // Toggle GUI visibility
0302     const guiElement = this.lilGui.domElement;
0303     if (this.showGui) {
0304       guiElement.style.display = 'block';
0305     } else {
0306       guiElement.style.display = 'none';
0307     }
0308   }
0309 
0310   toggleAnimationCycling() {
0311     if (this.eventDisplay.animationIsCycling()) {
0312       this.eventDisplay.stopAnimationCycling();
0313     } else {
0314       this.eventDisplay.startAnimationCycling();
0315     }
0316   }
0317 
0318 
0319   selectedConfigItem: any = null;
0320 
0321   onConfigureItemClicked(type: string) {
0322     if (type === 'track') {
0323       this.selectedConfigItem = {
0324         name: 'Track A',
0325         type: 'track',
0326         config: new TrackPainterConfig()
0327       };
0328     }
0329 
0330     this.toggleRightPane();
0331   }
0332 
0333   private initDexEventSource() {
0334 
0335     // We set loadingDex=false to be safe
0336     this.loadingDex.set(false);
0337 
0338     let dexUrl = this.userConfig.dexJsonEventSource.subject.getValue();
0339 
0340     if (!dexUrl || dexUrl.trim().length === 0) {
0341       console.log("[main-display]: No event data source specified. Skip loadDexData.");
0342     }
0343     // Check if we have the same data
0344     else if (this.eventDisplay.lastLoadedDexUrl === dexUrl) {
0345       console.log(`[main-display]: Event data (DEX) url is the same as before: '${dexUrl}', skip loading.`);
0346     }
0347     // Try to load
0348     else {
0349       this.loadingDex.set(true);
0350       this.eventDisplay.loadDexData(dexUrl).catch(error => {
0351         const msg = `Error loading events: ${error}`;
0352         console.error(`[main-display]: ${msg}`);
0353         this.showError(msg);
0354       }).then(() => {
0355         console.log("[main-display]: Event data loaded.");
0356         this.updateSceneTreeComponent();
0357       }).finally(()=>{
0358         this.loadingDex.set(false);   // switch off loading indicator
0359       });
0360     }
0361   }
0362 
0363 
0364   private initRootData() {
0365     let url = this.userConfig.rootEventSource.subject.getValue();
0366     let eventRange = this.userConfig.rootEventRange.subject.getValue();
0367 
0368     // Do we have url?
0369     if (!url || url.trim().length === 0) {
0370       console.log("[main-display]: No Edm4Eic source specified. Nothing to load");
0371       return;
0372     }
0373 
0374     // Do we have event Range?
0375     if (!eventRange || eventRange.trim().length === 0) {
0376       console.log("[main-display]: Event Range specified. Trying '0', to load the first event");
0377       eventRange = "0";
0378     }
0379 
0380     // Check if we have the same data
0381     if (this.eventDisplay.lastLoadedRootUrl === url && this.eventDisplay.lastLoadedRootEventRange === eventRange) {
0382       console.log(`[main-display]: Edm url is the same as before: '${url}', eventRange: '${eventRange}' - skip loading.`);
0383       return;
0384     }
0385 
0386     // Try to load
0387     else {
0388       this.loadingEdm.set(true);
0389       this.eventDisplay.loadRootData(url, eventRange).catch(error => {
0390         const msg = `Error loading events: ${error}`;
0391         console.error(`[main-display]: ${msg}`);
0392         this.showError(msg);
0393       }).then(() => {
0394         console.log("[main-display]: Event data loaded.");
0395         this.updateSceneTreeComponent();
0396       }).finally(()=>{
0397         this.loadingEdm.set(false);   // switch off loading indicator
0398       });
0399     }
0400   }
0401 
0402 
0403   private initGeometry() {
0404     let url = this.userConfig.geometryUrl.value;
0405 
0406     if (!url || url.trim().length === 0) {
0407       console.log("[main-display]: No geometry specified. Skip loadGeometry ");
0408     }
0409     // Check if we have the same data
0410     else if (this.eventDisplay.lastLoadedGeometryUrl === url) {
0411       console.log(`[main-display]: Geometry url is the same as before: '${url}', skip loading`);
0412     } else {
0413       // Load geometry
0414       this.loadingGeometry.set(true);
0415       this.eventDisplay.loadGeometry(url).catch(error => {
0416 
0417         const msg = `Error loading geometry: ${error}`;
0418         console.error(`[main-display]: ${msg}`);
0419         this.showError("Error loading Geometry. Open 'Configure' to change. Press F12->Console for logs");
0420       }).then(() => {
0421         this.updateSceneTreeComponent();
0422         console.log("[main-display]: Geometry loaded");
0423 
0424       }).finally(()=>{
0425         this.loadingGeometry.set(false);   // switch off loading indicator
0426       });
0427     }
0428   }
0429 
0430   animateWithCollision() {
0431     this.eventDisplay.animateWithCollision();
0432   }
0433 }