Warning, /firebird/packages/firebird-ng/display/src/lib/components/scene-export/scene-export.ts is written in an unsupported language. File is not indexed.
0001 import {
0002 Component,
0003 ViewChild,
0004 TemplateRef,
0005 ElementRef,
0006 ViewContainerRef,
0007 ChangeDetectionStrategy,
0008 signal
0009 } from '@angular/core';
0010 import * as THREE from 'three';
0011
0012 import { ThreeService } from '../../services/three.service';
0013 import { MatIconButton } from "@angular/material/button";
0014 import { MatDialog, MatDialogClose, MatDialogRef } from "@angular/material/dialog";
0015 import { MatIcon } from "@angular/material/icon";
0016 import { MatTooltip } from "@angular/material/tooltip";
0017 import { MatMenuItem } from "@angular/material/menu";
0018 import { MatSlideToggle } from "@angular/material/slide-toggle";
0019 import { FormsModule } from "@angular/forms";
0020
0021 import { GLTFExporter } from 'three/examples/jsm/exporters/GLTFExporter.js';
0022
0023 @Component({
0024 selector: 'app-scene-export',
0025 templateUrl: './scene-export.html',
0026 styleUrls: ['./scene-export.scss'],
0027 changeDetection: ChangeDetectionStrategy.OnPush,
0028 imports: [
0029 MatIconButton,
0030 MatIcon,
0031 MatDialogClose,
0032 MatTooltip,
0033 MatMenuItem,
0034 MatSlideToggle,
0035 FormsModule,
0036 ]
0037 })
0038 export class SceneExportComponent {
0039 @ViewChild('openBtn', { read: ElementRef }) openBtn!: ElementRef;
0040 @ViewChild('dialogTemplate') dialogTemplate!: TemplateRef<any>;
0041 dialogRef: MatDialogRef<any> | null = null;
0042
0043 // signal: cleared inside GLTFExporter's async callback, which schedules
0044 // no change detection under zoneless
0045 isExporting = signal(false);
0046 useBinaryFormat = true; // GLB by default
0047
0048 constructor(
0049 private threeService: ThreeService,
0050 private dialog: MatDialog,
0051 private viewContainerRef: ViewContainerRef,
0052 ) {}
0053
0054 openDialog(): void {
0055 if (this.dialogRef) {
0056 this.dialogRef.close();
0057 return;
0058 }
0059
0060 const rect = this.openBtn.nativeElement.getBoundingClientRect();
0061 const dialogWidth = 280;
0062
0063 const left = Math.max(rect.right - dialogWidth, 8);
0064 const top = rect.bottom + 12;
0065
0066 this.dialogRef = this.dialog.open(this.dialogTemplate, {
0067 position: {
0068 top: `${top}px`,
0069 left: `${left}px`
0070 },
0071 hasBackdrop: false,
0072 panelClass: 'custom-position-dialog',
0073 autoFocus: false,
0074 viewContainerRef: this.viewContainerRef
0075 });
0076
0077 this.dialogRef.afterClosed().subscribe(() => {
0078 this.dialogRef = null;
0079 });
0080 }
0081
0082 exportFullScene(): void {
0083 const ext = this.useBinaryFormat ? 'glb' : 'gltf';
0084 this.exportToGLTF(this.threeService.scene, `firebird-scene.${ext}`);
0085 }
0086
0087 exportGeometryOnly(): void {
0088 const ext = this.useBinaryFormat ? 'glb' : 'gltf';
0089 this.exportToGLTF(this.threeService.sceneGeometry, `firebird-geometry.${ext}`);
0090 }
0091
0092 private exportToGLTF(object: THREE.Object3D, filename: string): void {
0093 if (this.isExporting()) return;
0094
0095 this.isExporting.set(true);
0096 const exporter = new GLTFExporter();
0097
0098 exporter.parse(
0099 object,
0100 (result) => {
0101 this.downloadFile(result, filename);
0102 this.isExporting.set(false);
0103 if (this.dialogRef) {
0104 this.dialogRef.close();
0105 }
0106 },
0107 (error) => {
0108 console.error('Error exporting scene:', error);
0109 this.isExporting.set(false);
0110 },
0111 { binary: this.useBinaryFormat }
0112 );
0113 }
0114
0115 private downloadFile(data: ArrayBuffer | object, filename: string): void {
0116 let blob: Blob;
0117
0118 if (data instanceof ArrayBuffer) {
0119 blob = new Blob([data], { type: 'application/octet-stream' });
0120 } else {
0121 const jsonString = JSON.stringify(data);
0122 blob = new Blob([jsonString], { type: 'application/json' });
0123 }
0124
0125 const url = URL.createObjectURL(blob);
0126 const link = document.createElement('a');
0127 link.href = url;
0128 link.download = filename;
0129 link.click();
0130
0131 URL.revokeObjectURL(url);
0132 }
0133 }