Back to home page

EIC code displayed by LXR

 
 

    


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

0001 import {
0002   Component,
0003   OnInit,
0004   Input,
0005   ViewChild, OnDestroy, TemplateRef, signal, effect,
0006   ChangeDetectionStrategy, DestroyRef, inject
0007 } from '@angular/core';
0008 import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
0009 
0010 import {GeometryService} from '../../services/geometry.service';
0011 import {GameControllerService} from '../../services/game-controller.service';
0012 
0013 import {SceneTreeComponent} from '../../components/scene-tree/scene-tree.component';
0014 import {ModelTreeComponent} from '../../components/model-tree/model-tree.component';
0015 import {FirebirdShellComponent} from '../../components/firebird-shell/firebird-shell.component';
0016 import {ToolPanelComponent} from '../../components/tool-panel/tool-panel.component';
0017 import {EventSelectorComponent} from '../../components/event-selector/event-selector.component';
0018 import {GeometryClippingComponent} from '../../components/geometry-clipping/geometry-clipping.component';
0019 import {OpenEventComponent} from '../../components/open-event/open-event.component';
0020 
0021 import {MatIcon} from '@angular/material/icon';
0022 import { MatIconButton} from '@angular/material/button';
0023 import {MatTooltip} from '@angular/material/tooltip';
0024 
0025 import {PerfStatsComponent} from "../../components/perf-stats/perf-stats.component";
0026 import {EventDisplayService} from "../../services/event-display.service";
0027 import {EventTimeControlComponent} from "../../components/event-time-control/event-time-control.component";
0028 import {LegendWindowComponent} from "../../components/legend-window/legend-window.component";
0029 import {PainterConfigPanelComponent} from "../../components/painter-config-panel/painter-config-panel.component";
0030 import {ObjectRaycastComponent} from "../../components/object-raycast/object-raycast.component";
0031 import {MatProgressSpinner} from "@angular/material/progress-spinner";
0032 import {SceneExportComponent} from "../../components/scene-export/scene-export";
0033 import {AnimationSettingsComponent} from "../../components/animation-settings/animation-settings.component";
0034 import GUI from 'lil-gui';
0035 import {CommandBusService} from '@dexvis/app-features';
0036 import {DataModelService} from "../../services/data-model.service";
0037 import {RecordingMenuComponent} from "../../components/recording-menu/recording-menu.component";
0038 import {FirebirdDisplayComponent} from "../../components/firebird-display/firebird-display.component";
0039 import {ToolbarActionRegistration, injectCameraPresets, injectToolbarActions} from "@dexvis/firebird-ng/api";
0040 
0041 
0042 
0043 /**
0044  * The display page: `<firebird-display>` in the Firebird shell, with the
0045  * toolbar (event selector, data selector, clipping, export, recording), the
0046  * model and scene trees, the painter panel, the time controls with the
0047  * toolbar actions packs contribute (withToolbarAction), and the camera debug
0048  * panel (lil-gui).
0049  */
0050 @Component({
0051   selector: 'app-main-display',
0052   templateUrl: './main-display.component.html',
0053   styleUrls: ['./main-display.component.scss'],
0054   changeDetection: ChangeDetectionStrategy.OnPush,
0055   imports: [
0056     MatIcon,
0057     MatTooltip,
0058     MatIconButton,
0059     SceneTreeComponent,
0060     ModelTreeComponent,
0061     FirebirdShellComponent,
0062     ToolPanelComponent,
0063     EventSelectorComponent,
0064     OpenEventComponent,
0065     GeometryClippingComponent,
0066     PerfStatsComponent,
0067     EventTimeControlComponent,
0068     LegendWindowComponent,
0069     PainterConfigPanelComponent,
0070     ObjectRaycastComponent,
0071     MatProgressSpinner,
0072     SceneExportComponent,
0073     AnimationSettingsComponent,
0074     RecordingMenuComponent,
0075     FirebirdDisplayComponent,
0076   ]
0077 })
0078 export class MainDisplayComponent implements OnInit, OnDestroy {
0079   /** Load the configured geometry and events on attach; false runs only the startup commands. */
0080   @Input() isAutoLoadOnInit = true;
0081 
0082   @Input()
0083   eventDataImportOptions: string[] = []; // example, if you used them in UI
0084 
0085   @ViewChild('displayHeaderControls', {static: true})
0086   displayHeaderControls!: TemplateRef<any>;
0087 
0088   // For referencing child components
0089   @ViewChild(FirebirdShellComponent)
0090   displayShellComponent!: FirebirdShellComponent;
0091 
0092   @ViewChild(SceneTreeComponent)
0093   geometryTreeComponent: SceneTreeComponent | null | undefined;
0094 
0095   message = "";
0096 
0097   loaded: boolean = false;
0098 
0099   currentGeometry: string = 'All';
0100 
0101   // UI toggles: two-way bound to the shell layout panes; the shell's own
0102   // toolbar toggle buttons update these too.
0103   leftPaneOpen = signal(false);
0104   rightPaneOpen = signal(false);
0105 
0106   /** Left pane content: physics model tree, or the raw scene tree (debug). */
0107   leftTreeMode = signal<'model' | 'scene'>('model');
0108 
0109   // Loading indicators (service-owned so every display page shows the same state)
0110   get loadingDex() { return this.eventDisplay.loadingDex; }
0111   get loadingEdm() { return this.eventDisplay.loadingEdm; }
0112   get loadingGeometry() { return this.eventDisplay.loadingGeometry; }
0113 
0114   /** Camera debug panel (lil-gui), built once the scene exists and destroyed with the page. */
0115   private lilGui?: GUI;
0116   showGui = false;
0117 
0118   private readonly destroyRef = inject(DestroyRef);
0119 
0120   private readonly commandBus = inject(CommandBusService);
0121   /** Buttons packs add to the time controls (withToolbarAction). */
0122   readonly toolbarActions = injectToolbarActions();
0123   /** Camera presets for the debug panel's buttons. */
0124   private readonly cameraPresets = injectCameraPresets();
0125 
0126   constructor(
0127     private controller: GameControllerService,
0128     public  eventDisplay: EventDisplayService,
0129     public  geomService: GeometryService,
0130     private dataService: DataModelService,
0131   ) {
0132     // Scene-tree debug view refresh on data arrival. Signal-driven so it
0133     // also covers command-driven loads (?dex=/?geometry=), which the old
0134     // per-load callbacks missed.
0135     effect(() => {
0136       this.geomService.geometry();
0137       this.updateSceneTreeComponent();
0138     });
0139     effect(() => {
0140       this.dataService.currentEntry();
0141       this.updateSceneTreeComponent();
0142     });
0143   }
0144 
0145 
0146   ngOnInit() {
0147     // The controller service is a root singleton: the subscription ends with
0148     // the page, or every visit would add one.
0149     this.controller.buttonY.onPress.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((value) => {
0150       if (value) {
0151         // TODO this.cycleGeometry();
0152       }
0153     });
0154   }
0155 
0156 
0157   /**
0158    * The display is attached to `<firebird-display>`: the scene exists, so
0159    * the camera debug panel can bind to it.
0160    */
0161   onDisplayAttached(): void {
0162     // Camera debug panel. It sits in document.body and its .listen()
0163     // controllers poll every animation frame, so ngOnDestroy destroys it.
0164     const gui = new GUI();
0165     this.lilGui = gui;
0166     // One button per registered camera preset, through the command bus like
0167     // every other camera move
0168     for (const preset of this.cameraPresets) {
0169       const button = {go: () => this.runCommand({type: 'camera-preset', name: preset.name})};
0170       gui.add(button, 'go').name(`Camera to ${preset.label ?? preset.name}`);
0171     }
0172     gui.add(this, 'makeScreenshot').name('Make Screenshot');
0173 
0174     gui.add(this.eventDisplay.three.perspectiveCamera.position, 'x').name('Camera x[mm]').decimals(2).listen();
0175     gui.add(this.eventDisplay.three.perspectiveCamera.position, 'y').name('Camera y[mm]').decimals(2).listen();
0176     gui.add(this.eventDisplay.three.perspectiveCamera.position, 'z').name('Camera z[mm]').decimals(2).listen();
0177 
0178     gui.add(this.eventDisplay.three.controls.target, 'x').name("Pivot x[mm]").decimals(1).listen();
0179     gui.add(this.eventDisplay.three.controls.target, 'y').name("Pivot y[mm]").decimals(1).listen();
0180     gui.add(this.eventDisplay.three.controls.target, 'z').name("Pivot z[mm]").decimals(1).listen();
0181 
0182     gui.add(this.eventDisplay.three, "showBVHDebug");
0183 
0184     // GUI settings
0185     gui.domElement.style.top = '64px';
0186     gui.domElement.style.right = '120px';
0187     gui.domElement.style.display = this.showGui ? 'block' : 'none';
0188 
0189     // Recording lives in the toolbar (app-recording-menu), not in this
0190     // debug GUI.
0191   }
0192 
0193   // 3) UI - Toggling panes
0194   toggleLeftPane() {
0195     this.leftPaneOpen.update(v => !v);
0196   }
0197 
0198   toggleRightPane() {
0199     this.rightPaneOpen.update(v => !v);
0200   }
0201 
0202   ngOnDestroy(): void {
0203     this.lilGui?.destroy();
0204     this.lilGui = undefined;
0205   }
0206 
0207   // 10) SCENE TREE / UI
0208   private updateSceneTreeComponent() {
0209     // Example: rename lights
0210     const scene = this.eventDisplay.three.scene;
0211     if (this.geometryTreeComponent) {
0212       this.geometryTreeComponent.refreshSceneTree();
0213     }
0214   }
0215 
0216   toggleCameraControls() {
0217     this.showGui = !this.showGui;
0218 
0219     // Toggle GUI visibility (the panel exists once the scene initialized)
0220     if (this.lilGui) {
0221       this.lilGui.domElement.style.display = this.showGui ? 'block' : 'none';
0222     }
0223   }
0224 
0225 
0226   onConfigureItemClicked(type: string) {
0227     // The right pane hosts the painter panel, driven by the shared selection.
0228     this.rightPaneOpen.set(true);
0229   }
0230 
0231   runToolbarAction(action: ToolbarActionRegistration) {
0232     this.runCommand(action.command);
0233   }
0234 
0235   /** Dispatches a UI command; a failure reaches the user's error channel. */
0236   private runCommand(command: {type: string, [key: string]: unknown}) {
0237     this.commandBus.dispatch({...command, source: 'ui'})
0238       .catch(error => this.eventDisplay.reportError(
0239         `Command '${command.type}' failed: ${error instanceof Error ? error.message : error}`));
0240   }
0241 
0242   makeScreenshot() {
0243     const renderer = this.eventDisplay.three.renderer;
0244     // Render one frame to ensure the canvas is up to date
0245     renderer.render(this.eventDisplay.three.scene, this.eventDisplay.three.camera);
0246     // Use toDataURL for broad browser compatibility (works in Firefox)
0247     const dataUrl = renderer.domElement.toDataURL('image/png');
0248     const link = document.createElement('a');
0249     link.href = dataUrl;
0250     link.download = `firebird-screenshot-${Date.now()}.png`;
0251     document.body.appendChild(link);
0252     link.click();
0253     document.body.removeChild(link);
0254   }
0255 
0256 }