Back to home page

EIC code displayed by LXR

 
 

    


Warning, /firebird/firebird-ng/src/app/services/geometry.service.ts is written in an unsupported language. File is not indexed.

0001 import {Injectable, signal, WritableSignal} from '@angular/core';
0002 import {openFile} from 'jsroot';
0003 import {
0004   analyzeGeoNodes,
0005   findGeoManager, getGeoNodesByLevel
0006 } from '../../lib-root-geometry/root-geo-navigation';
0007 import {build} from 'jsroot/geom';
0008 import {pruneTopLevelDetectors, RootGeometryProcessor} from "../data-pipelines/root-geometry.processor";
0009 import {LocalStorageService} from "./local-storage.service";
0010 import {Subdetector} from "../model/subdetector";
0011 import {Color, DoubleSide, MeshLambertMaterial, NormalBlending, Object3D, Plane} from "three";
0012 import {UrlService} from "./url.service";
0013 import {DetectorThreeRuleSet, ThreeGeometryProcessor} from "../data-pipelines/three-geometry.processor";
0014 import * as THREE from "three";
0015 import {disposeHierarchy, getColorOrDefault} from "../utils/three.utils";
0016 
0017 import {cool2ColorRules} from "../theme/cool2-geometry-ruleset";
0018 import {cadColorRules} from "../theme/cad-geometry-ruleset";
0019 import {monoColorRules} from "../theme/mono-geometry-ruleset";
0020 import {cool2NoOutlineColorRules} from "../theme/cool2no-geometry-ruleset";
0021 
0022 
0023 export const GROUP_CALORIMETRY = "Calorimeters";
0024 export const GROUP_TRACKING = "Tracking";
0025 export const GROUP_PID = "PID";
0026 export const GROUP_MAGNETS = "Magnets";
0027 export const GROUP_SUPPORT = "Beam pipe and support";
0028 export const ALL_GROUPS = [
0029   GROUP_CALORIMETRY,
0030   GROUP_TRACKING,
0031   GROUP_PID,
0032   GROUP_MAGNETS,
0033   GROUP_SUPPORT,
0034 ]
0035 
0036 export const defaultRules: DetectorThreeRuleSet[] = [
0037   {
0038     names: ["FluxBarrel_env_25", "FluxEndcapP_26", "FluxEndcapN_28"],
0039     rules: [
0040       {
0041         color: 0x373766,
0042 
0043       }
0044     ]
0045   },
0046   {
0047     name: "EcalEndcapN*",
0048     rules: [
0049       {
0050         patterns: ["**/crystal_vol_0"],
0051         color: 0xffef8b,
0052         material: new THREE.MeshStandardMaterial({
0053           color: 0xffef8b,
0054           roughness: 0.7,
0055           metalness: 0.869,
0056           transparent: true,
0057           opacity: 0.8,
0058           side: THREE.DoubleSide
0059         })
0060       },
0061       {
0062         patterns: ["**/inner_support*", "**/ring*"],
0063         material: new THREE.MeshStandardMaterial({
0064           color: 0x19a5f5,
0065           roughness: 0.7,
0066           metalness: 0.869,
0067           transparent: true,
0068           opacity: 0.8,
0069           side: THREE.DoubleSide
0070         })
0071       }
0072 
0073     ]
0074   },
0075   {
0076     name: "InnerTrackerSupport_assembly_13",
0077     rules: [
0078       {
0079         material: new THREE.MeshStandardMaterial({
0080           color: 0xEEEEEE,
0081           roughness: 0.7,
0082           metalness: 0.3,
0083           transparent: true,
0084           opacity: 0.8,
0085           blending: THREE.NormalBlending,
0086           // premultipliedAlpha: true,
0087           depthWrite: false, // Ensures correct blending
0088           polygonOffset: true,
0089           polygonOffsetFactor: 1,
0090           side: THREE.DoubleSide
0091         }),
0092         outline: true,
0093         outlineColor: 0x666666,
0094         merge: true,
0095         newName: "InnerTrackerSupport"
0096       }
0097     ]
0098   },
0099   {
0100     name: "DIRC_14",
0101     rules: [
0102       {
0103         patterns:     ["**/*box*", "**/*prism*"],
0104         material: new THREE.MeshPhysicalMaterial({
0105           color: 0xe5ba5d,
0106           metalness: .9,
0107           roughness: .05,
0108           envMapIntensity: 0.9,
0109           clearcoat: 1,
0110           transparent: true,
0111           //transmission: .60,
0112           opacity: .6,
0113           reflectivity: 0.2,
0114           //refr: 0.985,
0115           ior: 0.9,
0116           side: THREE.DoubleSide,
0117         }),
0118         newName: "DIRC_barAndPrisms"
0119       },
0120       {
0121         patterns: ["**/*rail*"],
0122         newName: "DIRC_rails",
0123         color: 0xAAAACC
0124       },
0125       {
0126         patterns: ["**/*mcp*"],
0127         newName: "DIRC_mcps"
0128       }
0129     ]
0130 
0131   },
0132   {
0133     // This is when DIRC geometry is standalone
0134     name: "DIRC_0",
0135     rules: [
0136       {
0137         patterns:     ["**/*box*", "**/*prism*"],
0138         material: new THREE.MeshPhysicalMaterial({
0139           color: 0xe5ba5d,
0140           metalness: .9,
0141           roughness: .05,
0142           envMapIntensity: 0.9,
0143           clearcoat: 1,
0144           transparent: true,
0145           //transmission: .60,
0146           opacity: .6,
0147           reflectivity: 0.2,
0148           //refr: 0.985,
0149           ior: 0.9,
0150           side: THREE.DoubleSide,
0151         }),
0152         newName: "DIRC_barAndPrisms",
0153         merge: false,
0154         outline: true
0155       },
0156       {
0157         patterns: ["**/*rail*"],
0158         newName: "DIRC_rails",
0159         color: 0xAAAACC
0160       },
0161       {
0162         patterns: ["**/*mcp*"],
0163         newName: "DIRC_mcps"
0164       }
0165     ]
0166 
0167   },
0168   {
0169     name: "VertexBarrelSubAssembly_3",
0170     rules: [
0171       {
0172         merge: true,
0173         outline: true
0174       }
0175     ]
0176   },
0177   {
0178     name: "*",
0179     rules: [
0180       {
0181         merge: true,
0182         outline: true
0183       }
0184     ]
0185   }
0186 ]
0187 
0188 /**
0189  * Detectors (top level TGeo nodes) to be removed.
0190  * (!) startsWith function is used for filtering (aka: detector.fName.startsWith(removeDetectorNames[i]) ... )
0191  */
0192 const removeDetectorNames: string[] = [
0193   "Lumi",
0194   //"Magnet",
0195   //"B0",
0196   "B1",
0197   "B2",
0198   //"Q0",
0199   //"Q1",
0200   "Q2",
0201   //"BeamPipe",
0202   //"Pipe",
0203   "ForwardOffM",
0204   "Forward",
0205   "Backward",
0206   "Vacuum",
0207   "SweeperMag",
0208   "AnalyzerMag",
0209   "ZDC",
0210   //"LFHCAL",
0211   "HcalFarForward",
0212   "InnerTrackingSupport"
0213 ];
0214 
0215 
0216 // constants.ts
0217 export const DEFAULT_GEOMETRY = 'builtin://epic-central-optimized';
0218 
0219 @Injectable({
0220   providedIn: 'root'
0221 })
0222 export class GeometryService {
0223 
0224   public rootGeometryProcessor = new RootGeometryProcessor();
0225 
0226   /** Collection of subdetectors */
0227   public subdetectors: Subdetector[] = [];
0228 
0229   /** TGeoManager if available */
0230   public rootGeometry: any|null = null;
0231 
0232   /** Main/entry/root THREEJS geometry tree node with the whole geometry */
0233   // public geometry:
0234 
0235   public groupsByDetName: Map<string, string>;
0236 
0237   /** for geometry post-processing */
0238   private threeGeometryProcessor = new ThreeGeometryProcessor();
0239 
0240   private defaultColor: Color = new Color(0x68698D);
0241 
0242   public geometry:WritableSignal<Object3D|null> = signal(null)
0243 
0244   constructor(private urlService: UrlService,
0245               private localStorage: LocalStorageService,
0246               ) {
0247     this.groupsByDetName = new Map<string,string> ([
0248       ["SolenoidBarrel_assembly_0", GROUP_MAGNETS],
0249       ["SolenoidEndcapP_1", GROUP_MAGNETS],
0250       ["SolenoidEndcapN_2", GROUP_MAGNETS],
0251       ["VertexBarrelSubAssembly_3", GROUP_TRACKING],
0252       ["InnerSiTrackerSubAssembly_4", GROUP_TRACKING],
0253       ["MiddleSiTrackerSubAssembly_5", GROUP_TRACKING],
0254       ["OuterSiTrackerSubAssembly_6", GROUP_TRACKING],
0255       ["EndcapMPGDSubAssembly_7", GROUP_TRACKING],
0256       ["InnerMPGDBarrelSubAssembly_8", GROUP_TRACKING],
0257       ["EndcapTOFSubAssembly_9", GROUP_PID],
0258       ["BarrelTOFSubAssembly_10", GROUP_PID],
0259       ["OuterBarrelMPGDSubAssembly_11", GROUP_TRACKING],
0260       ["B0TrackerSubAssembly_12", GROUP_TRACKING],
0261       ["InnerTrackerSupport_assembly_13", GROUP_SUPPORT],
0262       ["DIRC_14", GROUP_PID],
0263       ["RICHEndcapN_Vol_15", GROUP_PID],
0264       ["DRICH_16", GROUP_PID],
0265       ["EcalEndcapP_17", GROUP_CALORIMETRY],
0266       ["EcalEndcapPInsert_18", GROUP_CALORIMETRY],
0267       ["EcalBarrelImaging_19", GROUP_CALORIMETRY],
0268       ["EcalBarrelScFi_20", GROUP_CALORIMETRY],
0269       ["EcalEndcapN_21", GROUP_CALORIMETRY],
0270       ["LFHCAL_env_22", GROUP_CALORIMETRY],
0271       ["HcalEndcapPInsert_23", GROUP_CALORIMETRY],
0272       ["HcalBarrel_24", GROUP_CALORIMETRY],
0273       ["FluxBarrel_env_25", GROUP_SUPPORT],
0274       ["FluxEndcapP_26", GROUP_SUPPORT],
0275       ["HcalEndcapN_27", GROUP_CALORIMETRY],
0276       ["FluxEndcapN_28", GROUP_SUPPORT],
0277       ["BeamPipe_assembly_29", GROUP_SUPPORT],
0278       ["B0PF_BeamlineMagnet_assembly_30", GROUP_MAGNETS],
0279       ["B0APF_BeamlineMagnet_assembly_31", GROUP_MAGNETS],
0280       ["Q1APF_BeamlineMagnet_assembly_32", GROUP_MAGNETS],
0281       ["Q1BPF_BeamlineMagnet_assembly_33", GROUP_MAGNETS],
0282       ["BeamPipeB0_assembly_38", GROUP_SUPPORT],
0283       ["Pipe_cen_to_pos_assembly_39", GROUP_SUPPORT],
0284       ["Q0EF_assembly_40", GROUP_MAGNETS],
0285       ["Q0EF_vac_41", GROUP_MAGNETS],
0286       ["Q1EF_assembly_42", GROUP_MAGNETS],
0287       ["Q1EF_vac_43", GROUP_MAGNETS],
0288       ["B0ECal_44", GROUP_CALORIMETRY],
0289       ["Pipe_Q1eR_to_B2BeR_assembly_54", GROUP_SUPPORT],
0290       ["Magnet_Q1eR_assembly_55", GROUP_MAGNETS],
0291       ["Magnet_Q2eR_assembly_56", GROUP_MAGNETS],
0292       ["Magnet_B2AeR_assembly_57", GROUP_MAGNETS],
0293       ["Magnet_B2BeR_assembly_58", GROUP_MAGNETS],
0294       ["Magnets_Q3eR_assembly_59", GROUP_MAGNETS],
0295     ])
0296   }
0297 
0298 
0299   async loadGeometry(url:string): Promise<{rootGeometry: any|null, threeGeometry: Object3D|null}> {
0300 
0301     this.subdetectors = [];
0302     //let url: string = 'assets/epic_pid_only.root';
0303     //let url: string = 'https://eic.github.io/epic/artifacts/tgeo/epic_dirc_only.root';
0304     // let url: string = 'https://eic.github.io/epic/artifacts/tgeo/epic_full.root';
0305     // >oO let objectName = 'default';
0306 
0307     if(url === DEFAULT_GEOMETRY) {
0308       url = 'https://eic.github.io/epic/artifacts/tgeo/epic_full.root';
0309     }
0310     // TODO check aliases
0311 
0312     const finalUrl = this.urlService.resolveDownloadUrl(url);
0313 
0314     console.time('[GeometryService]: Total load geometry time');
0315     console.log(`[GeometryService]: Loading file ${finalUrl}`)
0316 
0317     console.time('[GeometryService]: Open root file');
0318     const file = await openFile(finalUrl);
0319     // >oO debug console.log(file);
0320     console.timeEnd('[GeometryService]: Open root file');
0321 
0322 
0323     console.time('[GeometryService]: Reading geometry from file');
0324     this.rootGeometry = await findGeoManager(file) // await file.readObject(objectName);
0325     // >oO
0326     // console.log("Got TGeoManager. For inspection:")
0327     // console.log(this.rootGeometry);
0328     console.timeEnd('[GeometryService]: Reading geometry from file');
0329 
0330 
0331     // Getting main detector nodes
0332     if(this.localStorage.geometryCutListName.value === "central") {
0333       let result = pruneTopLevelDetectors(this.rootGeometry, removeDetectorNames);
0334       console.log(`[GeometryService]: Done prune geometry. Nodes left: ${result.nodes.length}, Nodes removed: ${result.removedNodes.length}`);
0335     } else {
0336       console.log("[GeometryService]: Prune geometry IS OFF");
0337 
0338     }
0339 
0340     if(this.localStorage.geometryRootFilterName.value === "default") {
0341       console.time('[GeometryService]: Root geometry pre-processing');
0342       this.rootGeometryProcessor.process(this.rootGeometry);
0343       console.timeEnd('[GeometryService]: Root geometry pre-processing');
0344     } else {
0345       console.log("[GeometryService]: Root geometry pre-processing IS OFF");
0346     }
0347 
0348 
0349     console.log("[GeometryService]: Number of tree elements analysis:");
0350     analyzeGeoNodes(this.rootGeometry, 1);
0351 
0352     //
0353     console.time('[GeometryService]: Build geometry');
0354     const geometry = build(this.rootGeometry,
0355       {
0356         numfaces: 5000000000,
0357         numnodes: 5000000000,
0358         instancing:-1,
0359         dflt_colors: false,
0360         vislevel: 200,
0361         doubleside:true,
0362         transparency:true
0363       });
0364     console.timeEnd('[GeometryService]: Build geometry');
0365 
0366     // Validate the geometry
0367     if(!geometry) {
0368       throw new Error("Geometry is null or undefined after TGeoPainter.build");
0369     }
0370 
0371     if(!geometry.children.length) {
0372       throw new Error("Geometry is converted but empty. Anticipated 'world_volume' but got nothing");
0373     }
0374 
0375     if(!geometry.children[0].children.length) {
0376       throw new Error("Geometry is converted but empty. Anticipated array of top level nodes (usually subdetectors) but got nothing");
0377     }
0378 
0379     // We now know it is not empty array
0380     console.time('[GeometryService]: Map root geometry to threejs geometry');
0381     let topDetectorNodes = geometry.children[0].children;
0382     for(const topNode of topDetectorNodes) {
0383 
0384       // Process name
0385       const originalName = topNode.name;
0386       const name = this.stripIdFromName(originalName);   // Remove id in the end EcalN_21 => Ecal
0387 
0388       const rootGeoNodes = getGeoNodesByLevel(this.rootGeometry, 1).map(obj=>obj.geoNode);
0389       const rootNode = rootGeoNodes.find(obj => obj.fName === originalName);
0390 
0391       let subdetector: Subdetector = {
0392         sourceGeometry: rootNode,
0393         sourceGeometryName: rootNode?.fName ?? "",
0394         geometry: topNode,
0395         name: this.stripIdFromName(originalName),
0396         groupName: this.groupsByDetName.get(originalName) || ""
0397       }
0398       // console.log(subdetector.sourceGeometryName);
0399       this.subdetectors.push(subdetector);
0400     }
0401     console.timeEnd('[GeometryService]: Map root geometry to threejs geometry');
0402 
0403     console.timeEnd('[GeometryService]: Total load geometry time');
0404 
0405     this.geometry.set(geometry);
0406 
0407     return {rootGeometry: this.rootGeometry, threeGeometry: geometry};
0408   }
0409 
0410   public postProcessing(geometry: Object3D, clippingPlanes: Plane[]) {
0411     let threeGeometry  = this.geometry();
0412     if (!threeGeometry) return;
0413 
0414 
0415     // Now we want to set default materials
0416     threeGeometry.traverse((child: any) => {
0417       if (child.type !== 'Mesh' || !child?.material?.isMaterial) {
0418         return;
0419       }
0420 
0421       // Handle the material of the child
0422       const color = getColorOrDefault(child.material, this.defaultColor);
0423 
0424       if(this.localStorage.geometryFastAndUgly.value) {
0425         child.material = new MeshLambertMaterial({
0426           color: color,
0427           side: DoubleSide,           // you said you can’t change this
0428           transparent: false,
0429           opacity: 1,                 // false transparency; use 1 for full opacity
0430           blending: THREE.NoBlending,       // since transparent is false
0431           depthTest: true,
0432           depthWrite: true,
0433           clippingPlanes,
0434           clipIntersection: true,
0435           clipShadows: false,
0436           fog: false,                 // disable fog math
0437           vertexColors: false,        // disable vertex-color math
0438           flatShading: true,          // simpler “flat” shading
0439           toneMapped: false           // skip tone-mapping
0440         });
0441       } else {
0442         child.material = new MeshLambertMaterial({
0443           color: color,
0444           side: DoubleSide,
0445           transparent: true,
0446           opacity: 0.7,
0447           blending: NormalBlending,
0448           depthTest: true,
0449           depthWrite: true,
0450           clippingPlanes: clippingPlanes,
0451           clipIntersection: true,
0452           clipShadows: false,
0453         });
0454 
0455       }
0456     });
0457 
0458 
0459     // HERE WE DO POSTPROCESSING STEP
0460     // TODO this.threeGeometryProcessor.processRuleSets(defaultRules, this.subdetectors);
0461     console.log(`[GeometryService]: Geometry theme name is set to '${this.localStorage.geometryThemeName.value}'`);
0462     if(this.localStorage.geometryThemeName.value === "cool2") {
0463       this.threeGeometryProcessor.processRuleSets(cool2ColorRules, this.subdetectors);
0464     }else if(this.localStorage.geometryThemeName.value === "cool2no") {
0465       this.threeGeometryProcessor.processRuleSets(cool2NoOutlineColorRules, this.subdetectors);
0466     } else if(this.localStorage.geometryThemeName.value === "cad") {
0467       this.threeGeometryProcessor.processRuleSets(cadColorRules, this.subdetectors);
0468     } else if(this.localStorage.geometryThemeName.value === "grey") {
0469       this.threeGeometryProcessor.processRuleSets(monoColorRules, this.subdetectors);
0470     }
0471 
0472 
0473 
0474     // Now we want to change the materials
0475     threeGeometry.traverse((child: any) => {
0476       if (!child?.material?.isMaterial) {
0477         return;
0478       }
0479 
0480       if (child.material?.clippingPlanes !== undefined) {
0481         child.material.clippingPlanes = clippingPlanes;
0482       }
0483 
0484       if (child.material?.clipIntersection !== undefined) {
0485         child.material.clipIntersection = true;
0486       }
0487 
0488       if (child.material?.clipShadows !== undefined) {
0489         child.material.clipShadows = false;
0490       }
0491     });
0492   }
0493 
0494   private stripIdFromName(name: string) {
0495       return name.replace(/_\d+$/, '');
0496   }
0497 
0498   toggleVisibility(object: Object3D) {
0499     if (object) {
0500       object.visible = !object.visible;
0501       console.log(`Visibility toggled for object: ${object.name}. Now visible: ${object.visible}`);
0502     }
0503   }
0504 }
0505