Back to home page

EIC code displayed by LXR

 
 

    


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

0001 import {
0002   ChangeDetectorRef,
0003   Component,
0004   EventEmitter,
0005   OnInit,
0006   Output,
0007   ChangeDetectionStrategy
0008 } from '@angular/core';
0009 
0010 import {
0011   MatTree,
0012   MatTreeNode,
0013   MatTreeNodeDef,
0014   MatTreeNodePadding,
0015   MatTreeNodeToggle,
0016 } from '@angular/material/tree';
0017 import { MatIcon } from '@angular/material/icon';
0018 import { MatIconButton } from '@angular/material/button';
0019 import { MatTooltip } from '@angular/material/tooltip';
0020 
0021 import * as THREE from 'three';
0022 import { Mesh, Object3D} from 'three';
0023 
0024 import { Line, LineSegments } from 'three';
0025 import { Line2 } from 'three/examples/jsm/lines/Line2.js';
0026 import { ThreeService } from '../../services/three.service';
0027 
0028 
0029 
0030 @Component({
0031   selector: 'app-scene-tree',
0032   standalone: true,
0033   imports: [
0034     MatTree,
0035     MatTreeNode,
0036     MatTreeNodeToggle,
0037     MatTreeNodeDef,
0038     MatTreeNodePadding,
0039     MatIcon,
0040     MatTooltip,
0041     MatIconButton,
0042   ],
0043   templateUrl: './scene-tree.component.html',
0044   changeDetection: ChangeDetectionStrategy.OnPush,
0045   styleUrls: ['./scene-tree.component.scss'],
0046 })
0047 export class SceneTreeComponent implements OnInit {
0048 
0049   @Output() configureItem = new EventEmitter<string>();
0050 
0051   /** Enable/disable generic geometry highlighting. */
0052   public isHighlightingEnabled = false;
0053 
0054   /** Enable/disable event-track highlighting. */
0055   public isTrackHighlightingEnabled = false;
0056 
0057 
0058   /* ---------------- Highlight materials for regular geometry ---------------- */
0059 
0060   private readonly geometryHighlightMaterial = new THREE.MeshBasicMaterial({
0061     color: 0xffff00,
0062     wireframe: true,
0063   });
0064 
0065   /* ---------------- Tree helpers ---------------- */
0066 
0067   public childrenAccessor = (node: Object3D): Object3D[] => node.children ?? [];
0068 
0069   public isExpandable = (node: Object3D): boolean =>
0070     !!node.children && node.children.length > 0;
0071 
0072   public dataSource: Object3D[] = [];
0073 
0074   public hasChild = (_: number, node: Object3D) =>
0075     !!node.children && node.children.length > 0;
0076 
0077   /* ---------------- Constructor / init ---------------- */
0078 
0079   constructor(
0080     private threeService: ThreeService,
0081     private cdr: ChangeDetectorRef,
0082   ) {}
0083 
0084   ngOnInit(): void {
0085     this.refreshSceneTree();
0086   }
0087 
0088   /* ---------------- UI callbacks ---------------- */
0089 
0090 
0091   public get isAnyHighlightingEnabled(): boolean {
0092     return this.isHighlightingEnabled || this.isTrackHighlightingEnabled;
0093   }
0094 
0095 
0096   public toggleHighlighting(): void {
0097     const newState = !this.isAnyHighlightingEnabled;
0098 
0099     this.isHighlightingEnabled = newState;
0100     this.isTrackHighlightingEnabled = newState;
0101   }
0102 
0103   /* ---------------- Tree population ---------------- */
0104 
0105   public refreshSceneTree(): void {
0106     const scene = this.threeService.scene;
0107     if (!scene) {
0108       console.warn('No scene present in ThreeService.');
0109       return;
0110     }
0111     // Force tree to disconnect old data source, then reconnect with new data
0112     this.dataSource = [];
0113     this.cdr.detectChanges();
0114     this.dataSource = [...scene.children];
0115     this.cdr.detectChanges();
0116   }
0117 
0118   /* ---------------- Visibility toggle ---------------- */
0119 
0120   public isEffectivelyVisible(obj: Object3D): boolean {
0121     let cur: Object3D | null = obj;
0122     while (cur) {
0123       if (!cur.visible) return false;
0124       cur = cur.parent!;
0125     }
0126     return true;
0127   }
0128 
0129 
0130   private revealPath(target: Object3D, full = false): void {
0131     //  enabling each invisible parent and hiding its other children
0132     let branch: Object3D = target;
0133     let parent: Object3D | null = branch.parent;
0134     while (parent) {
0135       if (!parent.visible) {
0136         parent.visible = true;
0137         parent.children.forEach(child => {
0138           if (child !== branch) child.visible = false;
0139         });
0140       }
0141       branch = parent;
0142       parent = parent.parent;
0143     }
0144 
0145     // show every descendant of the original target
0146     if (full) target.traverse(child => (child.visible = true));
0147   }
0148 
0149 
0150   public toggleVisibility(node: Object3D): void {
0151     if (node.visible) {
0152       node.visible = false;
0153     } else {
0154       const hiddenAncestor = (() => {
0155         for (let p = node.parent; p; p = p.parent) if (!p.visible) return true;
0156         return false;
0157       })();
0158 
0159       if (hiddenAncestor) {
0160         this.revealPath(node, /* full = */ false);
0161         node.visible = true;
0162       } else {
0163         this.revealPath(node, /* full = */ true);
0164         node.visible = true;
0165       }
0166     }
0167     this.threeService.invalidate();
0168   }
0169 
0170 
0171   /* ---------------- Mouse-over handlers ---------------- */
0172 
0173   public onMouseEnterNode(node: Object3D): void {
0174     if (this.isHighlightingEnabled && !this.isTrackNode(node)) {
0175       this.highlightNode(node);
0176     }
0177 
0178     if (this.isTrackHighlightingEnabled && this.isTrackNode(node)) {
0179       this.highlightTrack(node);
0180     }
0181   }
0182 
0183   public onMouseLeaveNode(node: Object3D): void {
0184     if (this.isHighlightingEnabled && !this.isTrackNode(node)) {
0185       this.unhighlightNode(node);
0186     }
0187 
0188     if (this.isTrackHighlightingEnabled && this.isTrackNode(node)) {
0189       this.unhighlightTrack(node);
0190     }
0191   }
0192 
0193   /* ---------------- Geometry highlight (meshes) ---------------- */
0194 
0195   public highlightNode(node: Object3D): void {
0196     node.traverse(child => {
0197       if (child instanceof Mesh) {
0198         if (!child.userData['origMaterial']) {
0199           child.userData['origMaterial'] = child.material;
0200         }
0201         child.material = this.geometryHighlightMaterial;
0202       }
0203     });
0204     this.threeService.invalidate();
0205   }
0206 
0207   public unhighlightNode(node: Object3D): void {
0208     node.traverse(child => {
0209       if (child instanceof Mesh && child.userData['origMaterial']) {
0210         child.material = child.userData['origMaterial'];
0211         delete child.userData['origMaterial'];
0212       }
0213     });
0214     this.threeService.invalidate();
0215   }
0216 
0217 
0218   /** Returns true if node belongs to the "event track" hierarchy. */
0219   public isTrackNode(node: Object3D): boolean {
0220     return (
0221       this.isUnderEventParent(node) &&
0222       (node.name.toLowerCase().includes('track') ||
0223         this.hasLineGeometry(node) ||
0224         node.userData?.['isTrack'] === true)
0225     );
0226   }
0227 
0228   /** Detect "Event" parent upwards in scene graph. */
0229   private isUnderEventParent(node: Object3D): boolean {
0230     if (
0231       node.name.toLowerCase() === 'event' ||
0232       node.userData?.['isEvent'] === true
0233     ) {
0234       return true;
0235     }
0236     let cur: Object3D | null = node;
0237     while (cur && cur.parent) {
0238       if (
0239         cur.parent.name.toLowerCase() === 'event' ||
0240         cur.parent.userData?.['isEvent'] === true
0241       ) {
0242         return true;
0243       }
0244       cur = cur.parent;
0245     }
0246     return false;
0247   }
0248 
0249   /** Quick geometry test for lines. */
0250   private hasLineGeometry(object: Object3D): boolean {
0251     let found = false;
0252     object.traverse(o => {
0253       if (
0254         o instanceof Line ||
0255         o instanceof LineSegments ||
0256         o instanceof Line2 ||
0257         (o instanceof Mesh &&
0258           o.geometry &&
0259           o.geometry.type.includes('Line'))
0260       ) {
0261         found = true;
0262       }
0263     });
0264     return found;
0265   }
0266 
0267 
0268   public highlightTrack(node: Object3D): void {
0269     node.traverse(obj3d => {
0270       if (obj3d.userData['highlightFunction']) {
0271         obj3d.userData['highlightFunction']();
0272       }
0273     });
0274     this.threeService.invalidate();
0275   }
0276 
0277   public unhighlightTrack(node: Object3D): void {
0278     node.traverse(obj3d => {
0279       if (obj3d.userData['unhighlightFunction']) {
0280         obj3d.userData['unhighlightFunction']();
0281       }
0282     });
0283     this.threeService.invalidate();
0284   }
0285 }