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 
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   // Phoenix API
0109   private facade: PhoenixThreeFacade = new PhoenixThreeFacade(new EventDisplay());
0110 
0111   constructor(
0112     private controller: GameControllerService,
0113     private snackBar: MatSnackBar,
0114     public eventDisplay: EventDisplayService,
0115     private userConfig: LocalStorageService,
0116     private serverConfig: ServerConfigService,
0117   ) {
0118 
0119   }
0120 
0121 
0122   async ngOnInit() {
0123     // Initialize the ThreeService scene/camera/renderer/controls
0124     this.eventDisplay.initThree('eventDisplay');
0125 
0126     // The facade will be initialized in three.service
0127     this.facade.initializeScene()
0128 
0129 
0130     this.controller.buttonY.onPress.subscribe((value) => {
0131       if (value) {
0132         // TODO this.cycleGeometry();
0133       }
0134     });
0135   }
0136 
0137 
0138   // 2) AFTER VIEW INIT => handle resizing with DisplayShell or window
0139   ngAfterViewInit(): void {
0140 
0141     // Load JSON based data files
0142     this.initDexEventSource();
0143 
0144     // Load Root file based data files
0145     this.initRootData();
0146 
0147     if (this.displayShellComponent) {
0148       const resizeInvoker = () => {
0149         setTimeout(() => {
0150           this.onRendererElementResize();
0151         }, 100);
0152       };
0153       this.displayShellComponent.onVisibilityChangeLeft.subscribe(resizeInvoker);
0154       this.displayShellComponent.onVisibilityChangeRight.subscribe(resizeInvoker);
0155       this.displayShellComponent.onEndResizeLeft.subscribe(() => this.onRendererElementResize());
0156       this.displayShellComponent.onEndResizeRight.subscribe(() => this.onRendererElementResize());
0157     }
0158 
0159     this.initCubeViewportControl();
0160 
0161     window.addEventListener('resize', () => {
0162       this.onRendererElementResize();
0163     });
0164 
0165     // When sidebar is collapsed/opened, the main container, i.e. #eventDisplay offsetWidth is not yet updated.
0166     // This leads to a not proper resize  processing. We add 100ms delay before calling a function
0167     const this_obj = this;
0168     const resizeInvoker = function () {
0169       setTimeout(() => {
0170         this_obj.onRendererElementResize();
0171       }, 100);  // 100 milliseconds = 0.1 seconds
0172     };
0173     resizeInvoker();
0174 
0175     // Loads the geometry (do it last as it might be long)
0176     this.initGeometry();
0177   }
0178 
0179   // 3) UI - Toggling panes
0180   toggleLeftPane() {
0181     this.displayShellComponent?.toggleLeftPane();
0182     this.isLeftPaneOpen = !this.isLeftPaneOpen;
0183   }
0184 
0185   toggleRightPane() {
0186     this.displayShellComponent?.toggleRightPane();
0187     this.isRightPaneOpen = !this.isRightPaneOpen;
0188   }
0189 
0190   // 4) Method to initialize CubeViewportControl with the existing Three.js objects
0191   private initCubeViewportControl(): void {
0192     const {scene, camera, renderer} = this.eventDisplay.three;
0193     if (this.cubeControl && scene && camera && renderer) {
0194       // Pass the external scene, camera, and renderer to the cube control
0195       this.cubeControl.initWithExternalScene(scene, camera, renderer);
0196       this.cubeControl.gizmo.attachControls(this.eventDisplay.three.controls);
0197       this.cubeControl.gizmo.camera
0198     }
0199 
0200     const thisPointer = this;
0201     this.eventDisplay.three.addFrameCallback(() => {
0202       if (thisPointer.cubeControl?.gizmo) {
0203         thisPointer.cubeControl.gizmo.render();
0204       }
0205     });
0206   }
0207 
0208 
0209   showError(message: string) {
0210     this.snackBar.open(message, 'Dismiss', {
0211       duration: 7000, // Auto-dismiss after X ms
0212       // verticalPosition: 'top', // Place at the top of the screen
0213       panelClass: ['error-snackbar']
0214     });
0215   }
0216 
0217 
0218   ngOnDestroy(): void {
0219     // Clear the custom controls when leaving the page
0220   }
0221 
0222 
0223   // Called when we want to recalculate the size of the canvas
0224   private onRendererElementResize() {
0225     let {width, height} = this.displayShellComponent.getMainAreaVisibleDimensions();
0226     console.log(`[RendererResize] New size: ${width}x${height} px`);
0227 
0228     // Delegate resizing to ThreeService
0229     this.eventDisplay.three.setSize(width, height);
0230     if (this.cubeControl?.gizmo) {
0231       this.cubeControl.gizmo.update();
0232     }
0233   }
0234 
0235   // 8) GEOMETRY TOGGLING
0236   // showAllGeometries() {
0237   //   this.geometryGroupSwitchingIndex = ALL_GROUPS.length;
0238   //   if (!this.geomService.subdetectors) return;
0239   //   for (let detector of this.geomService.subdetectors) {
0240   //     detector.geometry.visible = true;
0241   //   }
0242   // }
0243   // showGeometryGroup(groupName: string) {
0244   //   if (!this.geomService.subdetectors) return;
0245   //   for (let detector of this.geomService.subdetectors) {
0246   //     detector.geometry.visible = (detector.groupName === groupName);
0247   //   }
0248   // }
0249   // cycleGeometry() {
0250   //   this.geometryGroupSwitchingIndex++;
0251   //   if (this.geometryGroupSwitchingIndex > ALL_GROUPS.length) {
0252   //     this.geometryGroupSwitchingIndex = 0;
0253   //   }
0254   //   if (this.geometryGroupSwitchingIndex === ALL_GROUPS.length) {
0255   //     this.showAllGeometries();
0256   //     this.currentGeometry = 'All';
0257   //   } else {
0258   //     this.currentGeometry = ALL_GROUPS[this.geometryGroupSwitchingIndex];
0259   //     this.showGeometryGroup(this.currentGeometry);
0260   //   }
0261   // }
0262 
0263   public formatCurrentTime(value: number): string {
0264     return value.toFixed(1);
0265   }
0266 
0267   changeCurrentTime(event: Event) {
0268     if (!event) return;
0269     const input = event.target as HTMLInputElement;
0270     const value = parseFloat(input.value);
0271     this.eventDisplay.updateEventTime(value);
0272   }
0273 
0274   // 10) SCENE TREE / UI
0275   private updateSceneTreeComponent() {
0276     // Example: rename lights
0277     const scene = this.eventDisplay.three.scene;
0278     if (this.geometryTreeComponent) {
0279       this.geometryTreeComponent.refreshSceneTree();
0280     }
0281   }
0282 
0283   onDebugButton() {
0284     this.showError("Error message works");
0285   }
0286 
0287 
0288   selectedConfigItem: any = null;
0289 
0290   onConfigureItemClicked(type: string) {
0291     if (type === 'track') {
0292       this.selectedConfigItem = {
0293         name: 'Track A',
0294         type: 'track',
0295         config: new TrackPainterConfig()
0296       };
0297     }
0298 
0299     this.toggleRightPane();
0300   }
0301 
0302   private initDexEventSource() {
0303 
0304     // We set loadingDex=false to be safe
0305     this.loadingDex.set(false);
0306 
0307     let dexUrl = this.userConfig.dexJsonEventSource.subject.getValue();
0308 
0309     if (!dexUrl || dexUrl.trim().length === 0) {
0310       console.log("[main-display]: No event data source specified. Skip loadDexData.");
0311     }
0312     // Check if we have the same data
0313     else if (this.eventDisplay.lastLoadedDexUrl === dexUrl) {
0314       console.log(`[main-display]: Event data (DEX) url is the same as before: '${dexUrl}', skip loading.`);
0315     }
0316     // Try to load
0317     else {
0318       this.loadingDex.set(true);
0319       this.eventDisplay.loadDexData(dexUrl).catch(error => {
0320         const msg = `Error loading events: ${error}`;
0321         console.error(`[main-display]: ${msg}`);
0322         this.showError(msg);
0323       }).then(() => {
0324         console.log("[main-display]: Event data loaded.");
0325         this.updateSceneTreeComponent();
0326       }).finally(()=>{
0327         this.loadingDex.set(false);   // switch off loading indicator
0328       });
0329     }
0330   }
0331 
0332 
0333   private initRootData() {
0334     let url = this.userConfig.rootEventSource.subject.getValue();
0335     let eventRange = this.userConfig.rootEventRange.subject.getValue();
0336 
0337     // Do we have url?
0338     if (!url || url.trim().length === 0) {
0339       console.log("[main-display]: No Edm4Eic source specified. Nothing to load");
0340       return;
0341     }
0342 
0343     // Do we have event Range?
0344     if (!eventRange || eventRange.trim().length === 0) {
0345       console.log("[main-display]: Event Range specified. Trying '0', to load the first event");
0346       eventRange = "0";
0347     }
0348 
0349     // Check if we have the same data
0350     if (this.eventDisplay.lastLoadedRootUrl === url && this.eventDisplay.lastLoadedRootEventRange === eventRange) {
0351       console.log(`[main-display]: Edm url is the same as before: '${url}', eventRange: '${eventRange}' - skip loading.`);
0352       return;
0353     }
0354 
0355     // Try to load
0356     else {
0357       this.loadingEdm.set(true);
0358       this.eventDisplay.loadRootData(url, eventRange).catch(error => {
0359         const msg = `Error loading events: ${error}`;
0360         console.error(`[main-display]: ${msg}`);
0361         this.showError(msg);
0362       }).then(() => {
0363         console.log("[main-display]: Event data loaded.");
0364         this.updateSceneTreeComponent();
0365       }).finally(()=>{
0366         this.loadingEdm.set(false);   // switch off loading indicator
0367       });
0368     }
0369   }
0370 
0371 
0372   private initGeometry() {
0373     let url = this.userConfig.geometryUrl.value;
0374 
0375     if (!url || url.trim().length === 0) {
0376       console.log("[main-display]: No geometry specified. Skip loadGeometry ");
0377     }
0378     // Check if we have the same data
0379     else if (this.eventDisplay.lastLoadedGeometryUrl === url) {
0380       console.log(`[main-display]: Geometry url is the same as before: '${url}', skip loading`);
0381     } else {
0382       // Load geometry
0383       this.loadingGeometry.set(true);
0384       this.eventDisplay.loadGeometry(url).catch(error => {
0385 
0386         const msg = `Error loading geometry: ${error}`;
0387         console.error(`[main-display]: ${msg}`);
0388         this.showError("Error loading Geometry. Open 'Configure' to change. Press F12->Console for logs");
0389       }).then(() => {
0390         this.updateSceneTreeComponent();
0391         console.log("[main-display]: Geometry loaded");
0392 
0393       }).finally(()=>{
0394         this.loadingGeometry.set(false);   // switch off loading indicator
0395       });
0396     }
0397   }
0398 }