diff --git a/.vscode/launch.json b/.vscode/launch.json index c9e4255b..223e0d3a 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,15 +1,15 @@ { - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "firefox", - "request": "launch", - "name": "Launch FF against localhost", - "url": "http://localhost:4200", - "webRoot": "${workspaceFolder}" - } - ] -} \ No newline at end of file + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "firefox", + "request": "launch", + "name": "Launch FF against localhost", + "url": "http://localhost:4200", + "webRoot": "${workspaceFolder}" + } + ] +} diff --git a/packages/phoenix-event-display/src/event-display.ts b/packages/phoenix-event-display/src/event-display.ts index f72218d0..d4554513 100644 --- a/packages/phoenix-event-display/src/event-display.ts +++ b/packages/phoenix-event-display/src/event-display.ts @@ -334,8 +334,8 @@ export class EventDisplay { * @returns Promise for loading the geometry. */ public async parseGLTFGeometry(file: File): Promise { - const filename = file.name.split('/').pop(); - this.loadingManager.addLoadableItem(`parse_gltf_${filename}`); + const name = file.name.split('/').pop(); + this.loadingManager.addLoadableItem(`parse_gltf_${name}`); const allGeometriesUIParameters = await this.graphicsLibrary.parseGLTFGeometry(file); @@ -343,7 +343,7 @@ export class EventDisplay { this.ui.addGeometry(object, menuNodeName); } - this.loadingManager.itemLoaded(`parse_gltf_${filename}`); + this.loadingManager.itemLoaded(`parse_gltf_${name}`); } /** diff --git a/packages/phoenix-event-display/src/managers/three-manager/import-manager.ts b/packages/phoenix-event-display/src/managers/three-manager/import-manager.ts index 687dcfe3..39846b58 100644 --- a/packages/phoenix-event-display/src/managers/three-manager/import-manager.ts +++ b/packages/phoenix-event-display/src/managers/three-manager/import-manager.ts @@ -208,72 +208,121 @@ export class ImportManager { }); } + /** + * handles some file content and loads a Geometry contained.. + * It deals with zip file cases and then + * calls the given method on each file found + * @param path path of the original file + * @param filename name of the original file + * @param data content of the original file + * @param callback the method to be called on each file content + * @param resolve the method to be called on success + * @param reject the method to be called on failure + */ + private zipHandlingInternal( + path: string, + filename: string, + data: ArrayBuffer, + callback: ( + fileContent: ArrayBuffer, + path: string, + name: string, + ) => Promise, + resolve: any, + reject: any, + ) { + if (filename.split('.').pop() == 'zip') { + JSZip.loadAsync(data).then((archive) => { + const promises: Promise[] = []; + for (const filePath in archive.files) { + promises.push( + archive + .file(filePath) + .async('arraybuffer') + .then((fileData) => { + return callback(fileData, path, filePath.split('.')[0]); + }), + ); + } + let allGeometriesUIParameters: GeometryUIParameters[] = []; + Promise.all(promises).then((geos) => { + geos.forEach((geo) => { + allGeometriesUIParameters = allGeometriesUIParameters.concat(geo); + }); + resolve(allGeometriesUIParameters); + }); + }); + } else { + callback(data, path, filename.split('.')[0]).then( + (geo) => { + resolve(geo); + }, + (error) => { + reject(error); + }, + ); + } + } + /** * Wraps a method taking a file and returning a Promise for * loading a Geometry. It deals with zip file cases and then * calls the original method on each file found * @param file the original file - * @param callback the orignal mathod + * @param callback the original method * @returns Promise for loading the geometry. */ - private zipHandlingWrapper( - file: File | string, + private zipHandlingFileWrapper( + file: File, + callback: ( + fileContent: ArrayBuffer, + path: string, + name: string, + ) => Promise, + ): Promise { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = () => { + this.zipHandlingInternal( + '', + file.name, + reader.result as ArrayBuffer, + callback, + resolve, + reject, + ); + }; + reader.readAsArrayBuffer(file); + }); + } + + /** + * Wraps a method taking a URL and returning a Promise for + * loading a Geometry. It deals with zip file cases and then + * calls the original method on each file found + * @param file the original file + * @param callback the original method + * @returns Promise for loading the geometry. + */ + private zipHandlingURLWrapper( + file: string, callback: ( fileContent: ArrayBuffer, path: string, name: string, ) => Promise, ): Promise { - if (typeof file != 'string') { - file = file.name; - } - const path = file.substr(0, file.lastIndexOf('/')); return new Promise((resolve, reject) => { fetch(file).then((response) => { return response.arrayBuffer().then((data) => { - if (file.split('.').pop() == 'zip') { - try { - JSZip.loadAsync(data).then((archive) => { - const promises: Promise[] = []; - if (archive) { - for (const filePath in archive.files) { - const file = archive.file(filePath); - if (file) { - promises.push( - file.async('arraybuffer').then((fileData) => { - return callback( - fileData, - path, - filePath.split('.')[0], - ); - }), - ); - } - } - } - let allGeometriesUIParameters: GeometryUIParameters[] = []; - Promise.all(promises).then((geos) => { - geos.forEach((geo) => { - allGeometriesUIParameters = - allGeometriesUIParameters.concat(geo); - }); - resolve(allGeometriesUIParameters); - }); - }); - } catch (error) { - console.warn('Could not read zip file', 'Error'); - reject(error); - } - } else { - callback(data, path, file.split('.')[0]).then( - (geo) => { - resolve(geo); - }, - (error) => { - reject(error); - }, - ); - } + this.zipHandlingInternal( + file.substr(0, file.lastIndexOf('/')), + file, + data, + callback, + resolve, + reject, + ); }); }); }); @@ -296,7 +345,7 @@ export class ImportManager { scale: number, initiallyVisible: boolean, ): Promise { - return this.zipHandlingWrapper( + return this.zipHandlingURLWrapper( sceneUrl, (data: ArrayBuffer, path: string, ignoredName: string) => { return this.loadGLTFGeometryInternal( @@ -421,10 +470,12 @@ export class ImportManager { * @param fileName of the geometry file (.gltf,.glb or a zip with such file(s)) * @returns Promise for loading the geometry. */ - public parseGLTFGeometry(fileName: string): Promise { - return this.zipHandlingWrapper( - fileName, - this.parseGLTFGeometryFromArrayBuffer, + public parseGLTFGeometry(file: File): Promise { + return this.zipHandlingFileWrapper( + file, + (data: ArrayBuffer, path: string, name: string) => { + return this.parseGLTFGeometryFromArrayBuffer(data, path, name); + }, ); } @@ -454,6 +505,7 @@ export class ImportManager { for (const scene of gltf.scenes) { scene.visible = scene.userData.visible; + console.log('Dealing with scene ', scene.name); const sceneName = this.processGLTFSceneName(scene.name); this.processGeometry(scene, sceneName?.name ?? name); diff --git a/packages/phoenix-event-display/src/managers/three-manager/index.ts b/packages/phoenix-event-display/src/managers/three-manager/index.ts index cf964a9a..de4d2a03 100644 --- a/packages/phoenix-event-display/src/managers/three-manager/index.ts +++ b/packages/phoenix-event-display/src/managers/three-manager/index.ts @@ -860,7 +860,7 @@ export class ThreeManager { */ public async parseGLTFGeometry(file: File): Promise { const allGeometriesUIParameters = - await this.importManager.parseGLTFGeometry(file.name); + await this.importManager.parseGLTFGeometry(file); for (const { object } of allGeometriesUIParameters) { this.sceneManager.getGeometries().add(object); diff --git a/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/io-options/io-options-dialog/io-options-dialog.component.test.ts b/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/io-options/io-options-dialog/io-options-dialog.component.test.ts index cad31c27..0c28407f 100644 --- a/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/io-options/io-options-dialog/io-options-dialog.component.test.ts +++ b/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/io-options/io-options-dialog/io-options-dialog.component.test.ts @@ -75,6 +75,15 @@ describe('IoOptionsDialogComponent', () => { expect(mockDialogRef.close).toHaveBeenCalled(); }); + it('should handle glTF file input', () => { + const files = mockFileList([ + new File(['{}'], 'testfile.gltf', { + type: 'application/json', + }), + ]); + component.handleGLTFInput(files); + }); + describe('handleFileInput', () => { beforeEach(() => { jest.spyOn(component, 'handleFileInput').mockImplementation(() => {}); @@ -135,15 +144,6 @@ describe('IoOptionsDialogComponent', () => { component.handleSceneInput(files); }); - it('should handle glTF file input', () => { - const files = mockFileList([ - new File(['{}'], 'testfile.gltf', { - type: 'application/json', - }), - ]); - component.handleGLTFInput(files); - }); - it('should handle phoenix file input', () => { const files = mockFileList([ new File(['{}'], 'testfile.phnx', { diff --git a/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/io-options/io-options-dialog/io-options-dialog.component.ts b/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/io-options/io-options-dialog/io-options-dialog.component.ts index 7cd6dfe2..cb276c94 100644 --- a/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/io-options/io-options-dialog/io-options-dialog.component.ts +++ b/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/io-options/io-options-dialog/io-options-dialog.component.ts @@ -144,10 +144,12 @@ export class IOOptionsDialogComponent implements OnInit { } handleGLTFInput(files: FileList) { - const callback = (content: any) => { - this.eventDisplay.parseGLTFGeometry(content); + const callback = (file: File) => { + this.eventDisplay.parseGLTFGeometry(file); }; - this.handleFileInput(files[0], 'gltf,glb,gltf.zip,glb.zip', callback); + if (this.isFileOfExtension(files[0].name, 'gltf,glb,gltf.zip,glb.zip')) { + callback(files[0]); + } } handlePhoenixInput(files: FileList) { @@ -193,7 +195,7 @@ export class IOOptionsDialogComponent implements OnInit { } async handleZipEventDataInput(files: FileList) { - if (!this.isFileOfExtension(files[0].name, 'zip')) { + if (!this.isFileOfExtension(files[0].name, 'zip,json.zip')) { return; } @@ -251,7 +253,9 @@ export class IOOptionsDialogComponent implements OnInit { } private isFileOfExtension(fileName: string, extensions: string): boolean { - if (extensions.split(',').includes(fileName.split('.', 2).pop())) { + if ( + extensions.split(',').includes(fileName.slice(fileName.indexOf('.') + 1)) + ) { return true; }