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 {AfterViewInit, Component, HostListener, Input, OnInit, ViewChild} from '@angular/core';
0002 import {
0003 EventDataFormat,
0004 EventDataImportOption,
0005 EventDisplayService
0006 } from 'phoenix-ui-components';
0007 import {ClippingSetting, Configuration, PhoenixLoader, PhoenixMenuNode, PresetView} from 'phoenix-event-display';
0008 import * as THREE from 'three';
0009 import {Color, DoubleSide, InstancedBufferGeometry, Line, MeshLambertMaterial, MeshPhongMaterial,} from "three";
0010 import {ALL_GROUPS, GeometryService} from '../../services/geometry.service';
0011 import {ThreeGeometryProcessor} from "../../data-pipelines/three-geometry.processor";
0012 import * as TWEEN from '@tweenjs/tween.js';
0013 import {produceRenderOrder} from "jsroot/geom";
0014 import {getColorOrDefault} from "../../utils/three.utils";
0015 import {PhoenixThreeFacade} from "../../utils/phoenix-three-facade";
0016 import {GameControllerService} from "../../services/game-controller.service";
0017 import {IoOptionsComponent} from "../../components/io-options/io-options.component";
0018 import {ProcessTrackInfo, ThreeEventProcessor} from "../../data-pipelines/three-event.processor";
0019 import {UserConfigService} from "../../services/user-config.service";
0020 import {EicAnimationsManager} from "../../phoenix-overload/eic-animation-manager";
0021 import {MatSlider, MatSliderThumb} from "@angular/material/slider";
0022 import {MatIcon} from "@angular/material/icon";
0023 import {MatButton, MatIconButton} from "@angular/material/button";
0024 import {DecimalPipe, NgClass, NgForOf, NgIf} from "@angular/common";
0025 import {MatTooltip} from "@angular/material/tooltip";
0026 import {MatSnackBar} from "@angular/material/snack-bar"
0027 import {MatFormField} from "@angular/material/form-field";
0028 import {MatOption, MatSelect} from "@angular/material/select";
0029 import {DataModelService} from "../../services/data-model.service";
0030 import {AngularSplitModule} from "angular-split";
0031 import {SceneTreeComponent} from "../geometry-tree/scene-tree.component";
0032 import {DisplayShellComponent} from "../../components/display-shell/display-shell.component";
0033 import {DataModelPainter} from "../../painters/data-model-painter";
0034 import {ToolPanelComponent} from "../../components/tool-panel/tool-panel.component";
0035 import {NavConfigComponent} from "../../components/nav-config/nav-config.component";
0036 import {UrlService} from "../../services/url.service";
0037 import {EventSelectorComponent} from "../../components/event-selector/event-selector.component";
0038 import {AutoRotateComponent} from "../../components/auto-rotate/auto-rotate.component";
0039 import {DarkThemeComponent} from "../../components/dark-theme/dark-theme.component";
0040 import {ObjectClippingComponent} from "../../components/object-clipping/object-clipping.component";
0041
0042
0043 @Component({
0044 selector: 'app-test-experiment',
0045 templateUrl: './main-display.component.html',
0046 imports: [IoOptionsComponent, MatSlider, MatIcon, MatButton, MatSliderThumb, DecimalPipe, MatTooltip, MatFormField, MatSelect, MatOption, NgForOf, AngularSplitModule, SceneTreeComponent, NgClass, MatIconButton, DisplayShellComponent, ToolPanelComponent, NavConfigComponent, NgIf, EventSelectorComponent, AutoRotateComponent, DarkThemeComponent, ObjectClippingComponent],
0047 standalone: true,
0048 styleUrls: ['./main-display.component.scss']
0049 })
0050 export class MainDisplayComponent implements OnInit, AfterViewInit {
0051
0052 @Input()
0053 eventDataImportOptions: EventDataImportOption[] = Object.values(EventDataFormat);
0054
0055 currentTime = 0;
0056 maxTime = 200;
0057 minTime = 0;
0058 message = "";
0059
0060
0061 /** The root Phoenix menu node. */
0062 phoenixMenuRoot = new PhoenixMenuNode("Phoenix Menu");
0063
0064 threeGeometryProcessor = new ThreeGeometryProcessor();
0065 threeEventProcessor = new ThreeEventProcessor();
0066
0067 /** is geometry loaded */
0068 loaded: boolean = false;
0069
0070 /** loading progress */
0071 loadingProgress: number = 0;
0072
0073 /** The Default color of elements if not set */
0074 defaultColor: Color = new Color(0x2fd691);
0075
0076 private geometryGroupSwitchingIndex = ALL_GROUPS.length;
0077
0078
0079 private renderer: THREE.Renderer|null = null;
0080 private camera: THREE.Camera|null = null;
0081 private scene: THREE.Scene|null = null;
0082 private stats: any|null = null; // Stats JS display from UI manager
0083
0084 private threeFacade: PhoenixThreeFacade;
0085 private trackInfos: ProcessTrackInfo[] | null = null;
0086 private tween: TWEEN.Tween<any> | null = null;
0087 private animationManager: EicAnimationsManager| null = null;
0088 currentGeometry: string = "All";
0089 private animateEventAfterLoad: boolean = false;
0090 protected eventsByName = new Map<string, any>();
0091 private eventsArray: any[] = [];
0092 selectedEventKey: string|undefined;
0093 private beamAnimationTime: number = 1000;
0094
0095 isLeftPaneOpen: boolean = false;
0096 isDarkTheme = false;
0097
0098 isPhoenixMenuOpen: boolean = false;
0099 isSmallScreen: boolean = window.innerWidth < 768;
0100
0101 private painter: DataModelPainter = new DataModelPainter();
0102
0103
0104 constructor(
0105 private geomService: GeometryService,
0106 private eventDisplay: EventDisplayService,
0107 private controller: GameControllerService,
0108 private settings: UserConfigService,
0109 private dataService: DataModelService,
0110 private urlService: UrlService,
0111 private _snackBar: MatSnackBar) {
0112 this.threeFacade = new PhoenixThreeFacade(this.eventDisplay);
0113 }
0114
0115 @ViewChild(DisplayShellComponent)
0116 displayShellComponent!: DisplayShellComponent;
0117
0118 @ViewChild(SceneTreeComponent)
0119 geometryTreeComponent: SceneTreeComponent|null|undefined;
0120
0121 toggleLeftPane() {
0122 this.displayShellComponent.toggleLeftPane();
0123 this.isLeftPaneOpen = !this.isLeftPaneOpen;
0124
0125 }
0126
0127 toggleRightPane() {
0128 this.displayShellComponent.toggleRightPane();
0129 }
0130
0131 @HostListener('window:resize', ['$event'])
0132 onResize(event: any) {
0133 this.isSmallScreen = event.target.innerWidth < 768;
0134 if (!this.isSmallScreen) {
0135 this.isPhoenixMenuOpen = true;
0136 }
0137 }
0138
0139 togglePhoenixMenu() {
0140 this.isPhoenixMenuOpen = !this.isPhoenixMenuOpen;
0141 }
0142
0143
0144 logRendererInfo() {
0145 let renderer = this.threeFacade.mainRenderer;
0146 console.log('Draw calls:', renderer.info.render.calls);
0147 console.log('Triangles:', renderer.info.render.triangles);
0148 console.log('Points:', renderer.info.render.points);
0149 console.log('Lines:', renderer.info.render.lines);
0150 console.log('Geometries in memory:', renderer.info.memory.geometries);
0151 console.log('Textures in memory:', renderer.info.memory.textures);
0152 console.log('Programs:', renderer.info?.programs?.length);
0153 console.log(renderer.info?.programs);
0154 }
0155
0156 async loadGeometry(initiallyVisible=true, scale=10) {
0157
0158 let {rootGeometry, threeGeometry} = await this.geomService.loadGeometry();
0159 if(!threeGeometry) return;
0160
0161
0162 let threeManager = this.eventDisplay.getThreeManager();
0163 let uiManager = this.eventDisplay.getUIManager();
0164 let openThreeManager: any = threeManager;
0165 let importManager = openThreeManager.importManager;
0166 const doubleSided = true;
0167
0168 const sceneGeometry = threeManager.getSceneManager().getGeometries();
0169
0170 // Set geometry scale
0171 if (scale) {
0172 threeGeometry.scale.setScalar(scale);
0173 }
0174
0175 // Add root geometry to scene
0176 // console.log("CERN ROOT converted to Object3d: ", rootObject3d);
0177 sceneGeometry.add(threeGeometry);
0178
0179 // Now we want to change the materials
0180 sceneGeometry.traverse( (child: any) => {
0181
0182 if(child.type!=="Mesh" || !child?.material?.isMaterial) {
0183 return;
0184 }
0185
0186 // Assuming `getObjectSize` is correctly typed and available
0187 child.userData["size"] = importManager.getObjectSize(child);
0188
0189 // Handle the material of the child
0190
0191 const color = getColorOrDefault(child.material, this.defaultColor);
0192 const side = doubleSided ? DoubleSide : child.material.side;
0193
0194 child.material.dispose(); // Dispose the old material if it's a heavy object
0195
0196 let opacity = threeGeometry.userData["opacity"] ?? 1;
0197 let transparent = opacity < 1;
0198
0199 // child.material = new MeshPhongMaterial({
0200 // color: color,
0201 // shininess: 0,
0202 // side: side,
0203 // transparent: true,
0204 // opacity: 0.7,
0205 // blending: THREE.NormalBlending,
0206 // depthTest: true,
0207 // depthWrite: true,
0208 // clippingPlanes: openThreeManager.clipPlanes,
0209 // clipIntersection: true,
0210 // clipShadows: false
0211 // });
0212
0213 child.material = new MeshLambertMaterial({
0214 color: color,
0215 side: side,
0216 transparent: true,
0217 opacity: 0.7,
0218 blending: THREE.NormalBlending,
0219 depthTest: true,
0220 depthWrite: true,
0221 clippingPlanes: openThreeManager.clipPlanes,
0222 clipIntersection: true,
0223 clipShadows: false
0224 });
0225
0226 // Material
0227 let name:string = child.name;
0228
0229 if(! child.material?.clippingPlanes !== undefined) {
0230 child.material.clippingPlanes = openThreeManager.clipPlanes;
0231 }
0232
0233 if(! child.material?.clipIntersection !== undefined) {
0234 child.material.clipIntersection = true;
0235 }
0236
0237 if(! child.material?.clipShadows !== undefined) {
0238 child.material.clipShadows = false;
0239 }
0240 });
0241
0242 // HERE WE DO POSTPROCESSING STEP
0243 this.threeGeometryProcessor.process(this.geomService.subdetectors);
0244
0245 // Now we want to change the materials
0246 sceneGeometry.traverse( (child: any) => {
0247
0248 if(!child?.material?.isMaterial) {
0249 return;
0250 }
0251
0252 if(child.material?.clippingPlanes !== undefined) {
0253 child.material.clippingPlanes = openThreeManager.clipPlanes;
0254 }
0255
0256 if(child.material?.clipIntersection !== undefined) {
0257 child.material.clipIntersection = true;
0258 }
0259
0260 if(child.material?.clipShadows !== undefined) {
0261 child.material.clipShadows = false;
0262 }
0263 });
0264
0265 let renderer = openThreeManager.rendererManager;
0266 // Set render priority
0267 let scene = threeManager.getSceneManager().getScene();
0268 scene.background = new THREE.Color( 0x3F3F3F );
0269 renderer.getMainRenderer().sortObjects = false;
0270
0271 let camera = openThreeManager.controlsManager.getMainCamera();
0272 // camera.far = 5000;
0273 produceRenderOrder(scene, camera.position, 'ray');
0274 }
0275
0276 produceRenderOrder() {
0277
0278 console.log("produceRenderOrder. scene: ", this.scene, " camera ", this.camera);
0279 produceRenderOrder(this.scene, this.camera?.position, 'ray');
0280 }
0281
0282 logGamepadStates () {
0283 const gamepads = navigator.getGamepads();
0284
0285 for (const gamepad of gamepads) {
0286 if (gamepad) {
0287 console.log(`Gamepad connected at index ${gamepad.index}: ${gamepad.id}.`);
0288 console.log(`Timestamp: ${gamepad.timestamp}`);
0289 console.log('Axes states:');
0290 gamepad.axes.forEach((axis, index) => {
0291 console.log(`Axis ${index}: ${axis.toFixed(4)}`);
0292 });
0293 console.log('Button states:');
0294 gamepad.buttons.forEach((button, index) => {
0295 console.log(`Button ${index}: ${button.pressed ? 'pressed' : 'released'}, value: ${button.value}`);
0296 });
0297 }
0298 }
0299 };
0300
0301 rotateCamera(xAxisChange: number, yAxisChange: number) {
0302 let orbitControls = this.threeFacade.activeOrbitControls;
0303 let camera = this.threeFacade.mainCamera;
0304
0305 const offset = new THREE.Vector3(); // Offset of the camera from the target
0306 const quat = new THREE.Quaternion().setFromUnitVectors(camera.up, new THREE.Vector3(0, 1, 0));
0307 const quatInverse = quat.clone().invert();
0308
0309 const currentPosition = camera.position.clone().sub(orbitControls.target);
0310 currentPosition.applyQuaternion(quat); // Apply the quaternion
0311
0312 // Spherical coordinates
0313 const spherical = new THREE.Spherical().setFromVector3(currentPosition);
0314
0315 // Adjusting spherical coordinates
0316 spherical.theta -= xAxisChange * 0.023; // Azimuth angle change
0317 spherical.phi += yAxisChange * 0.023; // Polar angle change, for rotating up/down
0318
0319 // Ensure phi is within bounds to avoid flipping
0320 spherical.phi = Math.max(0.1, Math.min(Math.PI - 0.1, spherical.phi));
0321
0322 // Convert back to Cartesian coordinates
0323 const newPostion = new THREE.Vector3().setFromSpherical(spherical);
0324 newPostion.applyQuaternion(quatInverse);
0325
0326 camera.position.copy(newPostion.add(orbitControls.target));
0327 camera.lookAt(orbitControls.target);
0328 orbitControls.update();
0329 }
0330
0331 zoom(factor: number) {
0332 let orbitControls = this.threeFacade.activeOrbitControls;
0333 let camera = this.threeFacade.mainCamera;
0334 orbitControls.object.position.subVectors(camera.position, orbitControls.target).multiplyScalar(factor).add(orbitControls.target);
0335 orbitControls.update();
0336 }
0337
0338 handleGamepadInputV2 () {
0339 this.controller.animationLoopHandler();
0340 }
0341
0342 logCamera() {
0343 console.log(this.threeFacade.mainCamera);
0344 }
0345
0346
0347 handleGamepadInputV1 () {
0348
0349 // Update stats display that showing FPS, etc.
0350 if (this.stats) {
0351 this.stats.update();
0352 }
0353
0354 this.controller.animationLoopHandler();
0355
0356 const gamepads = navigator.getGamepads();
0357 for (const gamepad of gamepads) {
0358 if (gamepad) {
0359 // Example: Using left joystick to control OrbitControls
0360 // Axis 0: Left joystick horizontal (left/right)
0361 // Axis 1: Left joystick vertical (up/down)
0362 const xAxis = gamepad.axes[0];
0363 const yAxis = gamepad.axes[1];
0364
0365 if (Math.abs(xAxis) > 0.1 || Math.abs(yAxis) > 0.1) {
0366 this.rotateCamera(xAxis, yAxis);
0367 }
0368
0369 // Zooming using buttons
0370 const zoomInButton = gamepad.buttons[2];
0371 const zoomOutButton = gamepad.buttons[0];
0372
0373 if (zoomInButton.pressed) {
0374 this.zoom(0.99);
0375 }
0376
0377 if (zoomOutButton.pressed) {
0378 this.zoom(1.01);
0379 }
0380
0381 break; // Only use the first connected gamepad
0382 }
0383 }
0384 };
0385
0386 updateProjectionMatrix() {
0387 let camera = this.threeFacade.mainCamera;
0388 camera.updateProjectionMatrix();
0389 }
0390
0391 /**
0392 * Receives an object containing one event and builds the different collections
0393 * of physics objects.
0394 * @param eventData Object containing the event data.
0395 */
0396 public buildEventDataFromJSON(eventData: any) {
0397
0398 console.time("[buildEventDataFromJSON] BUILD EVENT");
0399 let openEventDisplay = (this.eventDisplay as any);
0400
0401 // Reset labels
0402 openEventDisplay.resetLabels();
0403 // Creating UI folder
0404 openEventDisplay.ui.addEventDataFolder();
0405 openEventDisplay.ui.addLabelsFolder();
0406 // Clearing existing event data
0407 openEventDisplay.graphicsLibrary.clearEventData();
0408 // Build data and add to scene
0409 openEventDisplay.configuration.eventDataLoader.buildEventData(
0410 eventData,
0411 openEventDisplay.graphicsLibrary,
0412 openEventDisplay.ui,
0413 openEventDisplay.infoLogger,
0414 );
0415 console.timeEnd("[buildEventDataFromJSON] BUILD EVENT");
0416 console.time("[buildEventDataFromJSON] onDisplayedEventChange");
0417 openEventDisplay.onDisplayedEventChange.forEach((callback:any) => {
0418 return callback(eventData);
0419 });
0420 console.timeEnd("[buildEventDataFromJSON] onDisplayedEventChange");
0421
0422
0423 // Reload the event data state in Phoenix menu
0424 // openEventDisplay.ui.loadEventFolderPhoenixMenuState();
0425 }
0426
0427
0428
0429
0430 ngOnInit() {
0431 let eventSource = this.settings.trajectoryEventSource.value;
0432 eventSource = this.urlService.resolveDownloadUrl(eventSource);
0433 let eventConfig = {eventFile: "https://firebird-eic.org/py8_all_dis-cc_beam-5x41_minq2-100_nevt-5.evt.json.zip", eventType: "zip"};
0434 if( eventSource != "no-events" && !eventSource.endsWith("edm4hep.json")) {
0435 let eventType = eventSource.endsWith("zip") ? "zip" : "json";
0436 let eventFile = eventSource;
0437 eventConfig = {eventFile, eventType};
0438 }
0439
0440 if (typeof Worker !== 'undefined') {
0441 // Create a new
0442 const worker = new Worker(new URL('../../workers/event-loader.worker.ts', import.meta.url));
0443 worker.onmessage = ({ data }) => {
0444 console.log(`Result from worker: ${data}`);
0445 console.log(data);
0446
0447 for(let key in data) {
0448 this.eventsByName.set(key, data[key]);
0449 this.eventsArray.push(data[key]);
0450 }
0451 //this.buildEventDataFromJSON(this.eventsArray[0]);
0452 this.eventDisplay.parsePhoenixEvents(data);
0453 console.log(this);
0454 console.log(`Loading event: `);
0455 console.log(data);
0456 };
0457 worker.postMessage(eventConfig.eventFile);
0458 } else {
0459 // Web workers are not supported in this environment.
0460 }
0461
0462 // Create the event display configuration
0463 const configuration: Configuration = {
0464 eventDataLoader: new PhoenixLoader(),
0465 presetViews: [
0466 // simple preset views, looking at point 0,0,0 and with no clipping
0467 new PresetView('Left View', [0, 0, -12000], [0, 0, 0], 'left-cube'),
0468 new PresetView('Center View', [-500, 12000, 0], [0, 0, 0], 'top-cube'),
0469 new PresetView('Perspective + clip', [-8000, 8000, -3000], [0, 0, 0], 'top-cube', ClippingSetting.On, 45, 120),
0470 // more fancy view, looking at point 0,0,5000 and with some clipping
0471 new PresetView('Perspective2 + clip', [-4500, 8000, -6000], [0, 0, -5000], 'right-cube', ClippingSetting.On, 90, 90)
0472 ],
0473 // default view with x, y, z of the camera and then x, y, z of the point it looks at
0474 defaultView: [-2500, 0, -8000, 0, 0 ,0],
0475
0476 phoenixMenuRoot: this.phoenixMenuRoot,
0477 // Event data to load by default
0478 // defaultEventFile: eventConfig
0479 // defaultEventFile: {
0480 // // (Assuming the file exists in the `src/assets` directory of the app)
0481 // //eventFile: 'assets/herwig_18x275_5evt.json',
0482 // //eventFile: 'assets/events/py8_all_dis-cc_beam-18x275_minq2-1000_nevt-20.evt.json',
0483 // //eventFile: 'assets/events/py8_dis-cc_mixed.json.zip',
0484 // eventFile: 'https://firebird-eic.org/py8_all_dis-cc_beam-5x41_minq2-100_nevt-5.evt.json.zip',
0485 // eventType: 'zip' // or zip
0486 // },
0487 }
0488
0489 // Initialize the event display
0490 this.eventDisplay.init(configuration);
0491
0492
0493 this.controller.buttonB.onPress.subscribe(value => {
0494 this.onControllerBPressed(value);
0495 });
0496
0497 this.controller.buttonRT.onPress.subscribe(value => {
0498 this.onControllerRTPressed(value);
0499 });
0500
0501 this.controller.buttonLT.onPress.subscribe(value => {
0502 this.onControllerLTPressed(value);
0503 });
0504
0505 this.controller.buttonY.onPress.subscribe(value => {
0506 this.onControllerYPressed(value);
0507 });
0508
0509 // let uiManager = this.eventDisplay.getUIManager();
0510 let openThreeManager: any = this.eventDisplay.getThreeManager();
0511 let threeManager = this.eventDisplay.getThreeManager();
0512
0513 // Replace animation manager with EIC animation manager:
0514
0515 // Animations manager (!) DANGER ZONE (!) But we have to, right?
0516 // Deadline approaches and meteor will erase the humanity if we are not in time...
0517 openThreeManager.animationsManager = this.animationManager = new EicAnimationsManager(
0518 openThreeManager.sceneManager.getScene(),
0519 openThreeManager.controlsManager.getActiveCamera(),
0520 openThreeManager.rendererManager,
0521 );
0522
0523
0524 this.renderer = openThreeManager.rendererManager.getMainRenderer();
0525 this.scene = threeManager.getSceneManager().getScene() as THREE.Scene;
0526 this.camera = openThreeManager.controlsManager.getMainCamera() as THREE.Camera;
0527
0528 this.painter.setThreeSceneParent(openThreeManager.sceneManager.getEventData());
0529
0530 // // GUI
0531 // const globalPlane = new THREE.Plane( new THREE.Vector3( - 1, 0, 0 ), 0.1 );
0532 //
0533 // const gui = new GUI({
0534 // container: document.getElementById("lil-gui-place") ?? undefined,
0535 // });
0536
0537 // gui.title("Dev Controls");
0538 // gui.add(this, "produceRenderOrder");
0539 // gui.add(this, "logGamepadStates").name( 'Log controls' );
0540 // gui.add(this, "logCamera").name( 'Log camera' );
0541 // gui.add(this, "updateProjectionMatrix").name( 'Try to screw up the camera =)' );
0542 // gui.close();
0543
0544 // Set default clipping
0545 this.eventDisplay.getUIManager().setClipping(true);
0546 this.eventDisplay.getUIManager().rotateOpeningAngleClipping(180);
0547 this.eventDisplay.getUIManager().rotateStartAngleClipping(90);
0548
0549 this.eventDisplay.listenToDisplayedEventChange(event => {
0550 this.updateSceneTreeComponent();
0551 console.log("listenToDisplayedEventChange");
0552 console.log(event);
0553 this.trackInfos = null;
0554
0555 let mcTracksGroup = threeManager.getSceneManager().getObjectByName("mc_tracks");
0556 if(mcTracksGroup) {
0557 console.time("Process tracks on event load");
0558 this.trackInfos = this.threeEventProcessor.processMcTracks(mcTracksGroup);
0559
0560 let minTime = Infinity;
0561 let maxTime = 0;
0562 for(let trackInfo of this.trackInfos) {
0563 if(trackInfo.startTime < minTime) minTime = trackInfo.startTime;
0564 if(trackInfo.endTime > maxTime) maxTime = trackInfo.endTime;
0565 }
0566
0567 this.maxTime = maxTime;
0568 this.minTime = minTime;
0569
0570 this.message = `Tracks: ${this.trackInfos.length}`;
0571 if(this.trackInfos && this.animateEventAfterLoad) {
0572 for (let trackInfo of this.trackInfos) {
0573 trackInfo.trackNode.visible = false;
0574 }
0575 }
0576 console.timeEnd("Process tracks on event load");
0577
0578 }
0579 if(this.animateEventAfterLoad) {
0580 this.animateWithCollision();
0581 }
0582 })
0583
0584 // Display event loader
0585 this.eventDisplay.getLoadingManager().addLoadListenerWithCheck(() => {
0586 console.log('Loading default configuration.');
0587 this.loaded = true;
0588 });
0589
0590 this.eventDisplay
0591 .getLoadingManager().toLoad.push("MyGeometry");
0592
0593
0594 this.eventDisplay
0595 .getLoadingManager()
0596 .addProgressListener((progress) => (this.loadingProgress = progress));
0597
0598 this.stats = (this.eventDisplay.getUIManager() as any).stats;
0599
0600
0601 threeManager.setAnimationLoop(()=>{this.handleGamepadInputV1()});
0602
0603
0604 this.loadGeometry().then(jsonGeom => {
0605
0606 this.updateSceneTreeComponent();
0607
0608 this.eventDisplay.getLoadingManager().itemLoaded("MyGeometry");
0609 }).catch(reason=> {
0610 console.error("ERROR LOADING GEOMETRY");
0611 console.log(reason);
0612 });
0613
0614 this.dataService.loadEdm4EicData().then(data => {
0615 this.updateSceneTreeComponent();
0616 console.log("loadEdm4EicData data:");
0617 console.log(data);
0618 if(data == null) {
0619 console.warn("DataService.loadEdm4EicData() Received data is null or undefined");
0620 return;
0621 }
0622
0623 if(data.entries?.length ?? 0 > 0) {
0624 this.painter.setThreeSceneParent(openThreeManager.sceneManager.getEventData());
0625 this.painter.setEntry(data.entries[0]);
0626 this.painter.paint(this.currentTime);
0627 this.updateSceneTreeComponent();
0628 } else {
0629 console.warn("DataService.loadEdm4EicData() Received data had no entries");
0630 console.log(data);
0631 }
0632 })
0633 //
0634 this.dataService.loadDexData().then(data => {
0635 this.updateSceneTreeComponent();
0636 if(data == null) {
0637 console.warn("DataService.loadDexData() Received data is null or undefined");
0638 return;
0639 }
0640
0641 if(data.entries?.length ?? 0 > 0) {
0642 this.painter.setThreeSceneParent(openThreeManager.sceneManager.getEventData());
0643 this.painter.setEntry(data.entries[0]);
0644 this.painter.paint(this.currentTime);
0645 this.updateSceneTreeComponent();
0646 } else {
0647 console.warn("DataService.loadDexData() Received data had no entries");
0648 console.log(data);
0649 }
0650
0651 //console.log("loaded data model");
0652 //console.log(data);
0653 });
0654
0655 document.addEventListener('keydown', (e) => {
0656 if ((e as KeyboardEvent).key === 'Enter') {
0657 // do something..
0658 }
0659 if ((e as KeyboardEvent).key === 'q') {
0660 this.nextRandomEvent();
0661 }
0662 if ((e as KeyboardEvent).key === 'r') {
0663 this.logRendererInfo();
0664 }
0665 console.log((e as KeyboardEvent).key);
0666
0667 });
0668
0669
0670 }
0671
0672
0673 ngAfterViewInit(): void {
0674
0675 // When sidebar is collapsed/opened, the main container, i.e. #eventDisplay offsetWidth is not yet updated.
0676 // This leads to a not proper resize processing. We add 100ms delay before calling a function
0677 const this_obj = this
0678 const resizeInvoker = function(){
0679 setTimeout(() => {
0680 this_obj.onRendererElementResize();
0681 }, 100); // 100 milliseconds = 0.1 seconds
0682 };
0683
0684 this.displayShellComponent.onVisibilityChangeLeft.subscribe(resizeInvoker);
0685 this.displayShellComponent.onVisibilityChangeRight.subscribe(resizeInvoker);
0686
0687 // This works good without 100mv delay
0688 this.displayShellComponent.onEndResizeLeft.subscribe(()=> {this.onRendererElementResize();})
0689 this.displayShellComponent.onEndResizeRight.subscribe(()=> {this.onRendererElementResize();})
0690
0691 window.addEventListener('resize', () => {
0692 this.onRendererElementResize();
0693 });
0694 }
0695
0696 private onRendererElementResize() {
0697 const renderer = this.threeFacade.mainRenderer;
0698 const camera = this.threeFacade.mainCamera;
0699 const rendererElement = renderer.domElement;
0700 if(rendererElement == null) {
0701 return;
0702 }
0703
0704 // This is the element in which Three.js canvas is located
0705 const outerElement = document.getElementById('eventDisplay');
0706 if(outerElement == null) {
0707 return;
0708 }
0709
0710 if(this.displayShellComponent == null) {
0711 return;
0712 }
0713
0714 // Calculate adjusted dimensions
0715 let headerHeight = 0;
0716 const footerHeight = 0; // TODO?
0717 const sidePanelWidth = this.displayShellComponent.leftPaneWidth;
0718
0719 // We use padding to
0720 let element = document.getElementById('eventDisplay');
0721 if(element) {
0722 let computedStyle = window.getComputedStyle(element);
0723 headerHeight = parseFloat(computedStyle.paddingTop) ?? 0;
0724 }
0725
0726 const adjustedWidth = outerElement.offsetWidth;
0727 const adjustedHeight = outerElement.offsetHeight - headerHeight - footerHeight;
0728 console.log(`[RendererResize] New size: ${adjustedWidth}x${adjustedHeight} px`)
0729
0730 // Update renderer size
0731 renderer.setSize(adjustedWidth, adjustedHeight);
0732
0733 if (camera.isOrthographicCamera) {
0734 camera.left = adjustedWidth / -2;
0735 camera.right = adjustedWidth / 2;
0736 camera.top = adjustedHeight / 2;
0737 camera.bottom = adjustedHeight / -2;
0738 } else {
0739 camera.aspect = adjustedWidth / adjustedHeight;
0740 }
0741 camera.updateProjectionMatrix();
0742 }
0743
0744 private onControllerBPressed(value: boolean) {
0745 if(value) {
0746 this.beamAnimationTime = 1800;
0747 this.nextRandomEvent("5x41");
0748 }
0749 }
0750
0751 private onControllerRTPressed(value: boolean) {
0752
0753 if(value) {
0754 this.beamAnimationTime = 1200;
0755 this.nextRandomEvent("10x100");
0756 }
0757 }
0758
0759 private onControllerLTPressed(value: boolean) {
0760 if(value) {
0761 this.beamAnimationTime = 700;
0762 this.nextRandomEvent("18x275");
0763 }
0764
0765 }
0766
0767 private onControllerYPressed(value: boolean) {
0768 if(value) this.cycleGeometry();
0769 }
0770
0771 changeCurrentTime(event: Event) {
0772 if(!event) return;
0773 const input = event.target as HTMLInputElement;
0774 const value = parseInt(input.value, 10);
0775 this.currentTime = value;
0776
0777 this.processCurrentTimeChange();
0778
0779 //this.updateParticlePosition(value);
0780 }
0781 timeStepBack($event: MouseEvent) {
0782 if(this.currentTime > this.minTime) this.currentTime--;
0783 if(this.currentTime < this.minTime) this.currentTime = this.minTime;
0784 this.processCurrentTimeChange();
0785 }
0786
0787 timeStep($event: MouseEvent) {
0788 if(this.currentTime < this.maxTime) this.currentTime++;
0789 if(this.currentTime > this.maxTime) this.currentTime = this.maxTime;
0790 this.processCurrentTimeChange();
0791 }
0792
0793 public formatCurrentTime (value: number): string {
0794 return value.toFixed(1);
0795 }
0796
0797 private processCurrentTimeChange() {
0798 this.painter.paint(this.currentTime);
0799 let partialTracks: ProcessTrackInfo[] = [];
0800 if(this.trackInfos) {
0801 for (let trackInfo of this.trackInfos) {
0802 if(trackInfo.startTime > this.currentTime) {
0803 trackInfo.trackNode.visible = false;
0804 }
0805 else
0806 {
0807 trackInfo.trackNode.visible = true;
0808 trackInfo.newLine.geometry.instanceCount=trackInfo.positions.length;
0809
0810 if(trackInfo.endTime > this.currentTime) {
0811 partialTracks.push(trackInfo)
0812 }
0813 else {
0814 // track should be visible fully
0815 trackInfo.newLine.geometry.instanceCount=Infinity;
0816 }
0817 }
0818 }
0819 }
0820
0821
0822 if(partialTracks.length > 0) {
0823 for(let trackInfo of partialTracks) {
0824 let geometryPosCount = trackInfo.positions.length;
0825
0826 //if (!geometryPosCount || geometryPosCount < 10) continue;
0827
0828 let trackProgress = (this.currentTime - trackInfo.startTime)/(trackInfo.endTime-trackInfo.startTime);
0829 let roundedProgress = Math.round(geometryPosCount*trackProgress*2)/2; // *2/2 to stick to 0.5 rounding
0830
0831 //(trackInfo.newLine.geometry as InstancedBufferGeometry). = drawCount;(0, roundedProgress);
0832 trackInfo.newLine.geometry.instanceCount=roundedProgress;
0833 }
0834 }
0835 }
0836
0837 animateCurrentTime(targetTime: number, duration: number): void {
0838 if(this.tween) {
0839 this.stopAnimation();
0840 }
0841 this.tween = new TWEEN.Tween({ currentTime: this.currentTime })
0842 .to({ currentTime: targetTime }, duration)
0843 .onUpdate((obj) => {
0844 this.currentTime = obj.currentTime;
0845 this.processCurrentTimeChange(); // Assuming this method updates your display
0846 })
0847 //.easing(TWEEN.Easing.Quadratic.In) // This can be changed to other easing functions
0848 .start();
0849
0850 //this.animate();
0851 }
0852
0853
0854 animateTime() {
0855 this.animateCurrentTime(this.maxTime, (this.maxTime-this.currentTime)*200 )
0856
0857 }
0858
0859 stopAnimation(): void {
0860 if (this.tween) {
0861 this.tween.stop(); // Stops the tween if it is running
0862 this.tween = null; // Remove reference
0863 }
0864 }
0865
0866 exitTimedDisplay() {
0867 this.stopAnimation();
0868 this.rewindTime();
0869 this.painter.paint(null);
0870 this.animateEventAfterLoad = false;
0871 if(this.trackInfos) {
0872 for (let trackInfo of this.trackInfos) {
0873 trackInfo.trackNode.visible = true;
0874 trackInfo.newLine.geometry.instanceCount=Infinity;
0875 }
0876 }
0877 }
0878
0879 rewindTime() {
0880 this.currentTime = 0;
0881 }
0882
0883 animateWithCollision() {
0884 this.stopAnimation();
0885 this.rewindTime();
0886 if(this.trackInfos) {
0887 for (let trackInfo of this.trackInfos) {
0888 trackInfo.trackNode.visible = false;
0889 }
0890 }
0891 this.animationManager?.collideParticles(this.beamAnimationTime, 30, 5000, new Color(0xAAAAAA),
0892 () => {
0893 this.animateTime();
0894 });
0895 }
0896
0897 showGeometryGroup(groupName: string) {
0898 if(!this.geomService.subdetectors) return;
0899 for(let detector of this.geomService.subdetectors) {
0900 if(detector.groupName === groupName) {
0901 detector.geometry.visible = true;
0902 } else {
0903 detector.geometry.visible = false;
0904 }
0905 }
0906
0907 }
0908
0909 showAllGeometries() {
0910 this.geometryGroupSwitchingIndex = ALL_GROUPS.length
0911 if(!this.geomService.subdetectors) return;
0912 for(let detector of this.geomService.subdetectors) {
0913 detector.geometry.visible = true;
0914 }
0915 }
0916
0917 cycleGeometry() {
0918 this.geometryGroupSwitchingIndex ++;
0919 if(this.geometryGroupSwitchingIndex > ALL_GROUPS.length) {
0920 this.geometryGroupSwitchingIndex = 0;
0921 }
0922
0923 if(this.geometryGroupSwitchingIndex === ALL_GROUPS.length) {
0924 this.showAllGeometries();
0925 this.currentGeometry = "All";
0926 } else {
0927 this.currentGeometry = ALL_GROUPS[this.geometryGroupSwitchingIndex];
0928 this.showGeometryGroup(this.currentGeometry);
0929 }
0930 }
0931
0932 protected nextRandomEvent(energyStr="18x275") {
0933 const name = `event_18x275_minq2_100_${Math.floor(Math.random() * 10)}`
0934 console.log(name); // This will log a random index from 0 to 3
0935
0936 let eventNames=[];
0937 for(const eventName of this.eventsByName.keys()) {
0938 if(eventName.includes(energyStr)) {
0939 eventNames.push(eventName);
0940 }
0941 }
0942
0943 if(eventNames.length === 0) {
0944 console.warn(`this.eventsByName that has ${this.eventsByName.size} elements, doesn't have keys with energy: ${energyStr}`);
0945 console.log(this.eventsByName);
0946 return;
0947 }
0948
0949 let eventIndex = Math.floor(Math.random() * eventNames.length);
0950 if(eventIndex < 0 || eventIndex >= eventNames.length) {
0951 console.warn(`if(eventIndex < 0 || eventIndex >= eventNames.length) eventIndex=${eventIndex}`);
0952 }
0953
0954 const eventName = eventNames[eventIndex];
0955
0956 // Handle existing animations
0957 this.stopAnimation();
0958 if(this.trackInfos) {
0959 for (let trackInfo of this.trackInfos) {
0960 trackInfo.trackNode.visible = false;
0961 } }
0962
0963 this._snackBar.open(`Showing event: ${eventName}`, 'Dismiss', {
0964 duration: 2000, // Duration in milliseconds after which the snack-bar will auto dismiss
0965 horizontalPosition: 'right', // 'start' | 'center' | 'end' | 'left' | 'right'
0966 verticalPosition: 'top', // 'top' | 'bottom'
0967 });
0968
0969 this.animateEventAfterLoad = true;
0970
0971 this.eventDisplay.loadEvent(eventName);
0972
0973 if(this.trackInfos && this.animateEventAfterLoad) {
0974 for (let trackInfo of this.trackInfos) {
0975 trackInfo.trackNode.visible = false;
0976 }
0977 }
0978
0979
0980 // this.buildEventDataFromJSON(eventIndex);
0981
0982 }
0983
0984 onUserSelectedEvent() {
0985
0986 let event = this.eventsByName.get(this.selectedEventKey ?? "");
0987 if(event === undefined) {
0988 console.warn(`User selected event ${this.selectedEventKey} which is not found in eventsByName collection.
0989 Collection has ${this.eventsByName.size} elements `);
0990 return;
0991 }
0992 console.log(`User selected event ${this.selectedEventKey} `);
0993 this.buildEventDataFromJSON(event);
0994 }
0995
0996 private updateSceneTreeComponent() {
0997 // Name scene lights
0998 if (this.scene) {
0999 if (this.scene.children.length > 2) {
1000 if (this.scene.children[0]) {
1001 this.scene.children[0].name = "Ambient light";
1002 }
1003 if (this.scene.children[1]) {
1004 this.scene.children[1].name = "Direct. light";
1005 }
1006 }
1007 }
1008
1009 if(this.geometryTreeComponent) {
1010 this.geometryTreeComponent.refreshScheneTree();
1011 }
1012
1013 }
1014 }