Skip to content

Commit

Permalink
feat(event-display): complete support to save and load labels
Browse files Browse the repository at this point in the history
  • Loading branch information
9inpachi committed Feb 6, 2021
1 parent d90e3a6 commit 2219a3e
Show file tree
Hide file tree
Showing 9 changed files with 147 additions and 90 deletions.
8 changes: 7 additions & 1 deletion packages/phoenix-event-display/src/event-data-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ export interface EventDataLoader {
* @param indexInCollection Event object's index in collection.
* @returns A unique label ID string.
*/
addLabelOfEventObject(label: string, collection: string, indexInCollection: number): string;
addLabelToEventObject(label: string, collection: string, indexInCollection: number): string;

/**
* Get the object containing labels.
* @returns The labels object.
*/
getLabelsObject(): object;

}
6 changes: 4 additions & 2 deletions packages/phoenix-event-display/src/event-display.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ export class EventDisplay {
public buildEventDataFromJSON(eventData: any) {
// Creating UI folder
this.ui.addEventDataFolder();
this.ui.addLabelsFolder();
// Clearing existing event data
this.graphicsLibrary.clearEventData();
// Build data and add to scene
Expand Down Expand Up @@ -236,6 +237,7 @@ export class EventDisplay {
if (phoenixScene.sceneConfiguration && phoenixScene.scene) {
// Creating UI folder
this.ui.addEventDataFolder();
this.ui.addLabelsFolder();
// Clearing existing event data
this.graphicsLibrary.clearEventData();
// Add to scene
Expand Down Expand Up @@ -633,8 +635,8 @@ export class EventDisplay {
* @param uuid UUID of the three.js object.
*/
public addLabelToObject(label: string, collection: string, indexInCollection: number, uuid: string) {
const labelId = this.configuration.eventDataLoader.addLabelOfEventObject(label, collection, indexInCollection);
this.ui.addLabel(label, labelId);
const labelId = this.configuration.eventDataLoader.addLabelToEventObject(label, collection, indexInCollection);
this.ui.addLabel(labelId);
this.graphicsLibrary.addLabelToObject(label, uuid, labelId);
}
}
42 changes: 42 additions & 0 deletions packages/phoenix-event-display/src/helpers/file.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* Save and download file with the given data.
* @param data String data to save in the file.
* @param fileName Name of the downloaded file.
* @param contentType Content type of the file.
*/
export const saveFile = (data: string, fileName: string, contentType: string = 'application/json') => {
const blob = new Blob([data], { type: contentType });
const tempAnchor = document.createElement('a');
tempAnchor.style.display = 'none';
tempAnchor.href = URL.createObjectURL(blob);
tempAnchor.download = fileName;
tempAnchor.click();
tempAnchor.remove();
};

/**
* Load a file from user by mocking an input element.
* @param onFileRead Callback when the file is read.
* @param contentType Content type of the file. Default 'application/json'.
*/
export const loadFile = (
onFileRead: (data: string) => void,
contentType: string = 'application/json'
) => {
// Create a mock input file element and use that to read the file
let inputFile = document.createElement('input');
inputFile.type = 'file';
inputFile.accept = contentType;
inputFile.onchange = (e: any) => {
const configFile = e.target?.files[0];
const reader = new FileReader();
reader.onload = e => {
onFileRead?.(e.target.result.toString());

inputFile.remove();
inputFile = null;
};
reader.readAsText(configFile);
}
inputFile.click();
};
10 changes: 9 additions & 1 deletion packages/phoenix-event-display/src/loaders/phoenix-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ export class PhoenixLoader implements EventDataLoader {
* @param indexInCollection Event object's index in collection.
* @returns A unique label ID string.
*/
public addLabelOfEventObject(label: string, collection: string, indexInCollection: number): string {
public addLabelToEventObject(label: string, collection: string, indexInCollection: number): string {
for (const eventDataType of Object.keys(this.eventData)) {
if (this.eventData[eventDataType] && Object.keys(this.eventData[eventDataType]).includes(collection)) {
this.labelsObject[eventDataType] = this.labelsObject[eventDataType] || {};
Expand All @@ -401,4 +401,12 @@ export class PhoenixLoader implements EventDataLoader {
}
}

/**
* Get the object containing labels.
* @returns The labels object.
*/
public getLabelsObject(): object {
return this.labelsObject;
}

}
41 changes: 5 additions & 36 deletions packages/phoenix-event-display/src/managers/state-manager.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { EventDisplay } from "../event-display";
import { Camera } from "three";
import { PhoenixMenuNode } from "../ui/phoenix-menu/phoenix-menu-node";
import { loadFile, saveFile } from "../helpers/file";

/**
* A singleton manager for managing the scene's state.
Expand Down Expand Up @@ -55,7 +56,9 @@ export class StateManager {
}).addConfig('button', {
label: 'Load state',
onClick: () => {
this.loadStateFromFile();
loadFile((data) => {
this.loadStateFromJSON(JSON.parse(data));
});
}
});
}
Expand All @@ -73,41 +76,7 @@ export class StateManager {
}
};

const blob = new Blob([JSON.stringify(state)], {
type: 'application/json'
});
const tempAnchor = document.createElement('a');
tempAnchor.href = URL.createObjectURL(blob);
tempAnchor.download = 'phoenix-config.json';
tempAnchor.click();
tempAnchor.remove();
}

/**
* Load data from JSON file.
* @param onFileRead Callback with JSON file data when the file data is read.
*/
loadStateFromFile(onFileRead?: (json: object) => void) {
// Create a mock input file element and use that to read the file
let inputFile = document.createElement('input');
inputFile.type = 'file';
inputFile.accept = 'application/json';
inputFile.onchange = (e: any) => {
const configFile = e.target?.files[0];
const reader = new FileReader();
reader.onload = e => {
const jsonData = JSON.parse(e.target.result.toString());

onFileRead?.(jsonData);

this.loadStateFromJSON(jsonData);

inputFile.remove();
inputFile = null;
};
reader.readAsText(configFile);
}
inputFile.click();
saveFile(JSON.stringify(state), 'phoenix-config.json');
}

/**
Expand Down
5 changes: 3 additions & 2 deletions packages/phoenix-event-display/src/tests/three.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ThreeManager } from '../three';
import { InfoLogger } from '../info-logger';
import { Group } from 'three';
import * as Helpers from '../helpers/save-file';

describe('ThreeManager', () => {

Expand Down Expand Up @@ -149,8 +150,8 @@ describe('ThreeManager', () => {
});

it('should export scene', () => {
threePrivate.exportManager.saveString = (text: string, filename: string) => { }

spyOn(Helpers, 'saveFile').and.stub();
spyOn(threePrivate.exportManager, 'exportSceneToOBJ').and.callThrough();
three.exportSceneToOBJ();
expect(threePrivate.exportManager.exportSceneToOBJ).toHaveBeenCalled();
Expand Down
36 changes: 4 additions & 32 deletions packages/phoenix-event-display/src/three/export-manager.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
import { OBJExporter } from 'three/examples/jsm/exporters/OBJExporter';
import { Scene, Object3D, Group } from 'three';
import { Scene, Object3D } from 'three';
import { GLTFExporter } from 'three/examples/jsm/exporters/GLTFExporter';
import { saveFile } from '../helpers/file';

/**
* Manager for managing event display's export related functionality.
*/
export class ExportManager {

/**
* Constructor for the export manager.
*/
constructor() {
}

/**
* Exports scene to OBJ file format.
* @param scene The scene to be exported.
Expand All @@ -21,7 +16,7 @@ export class ExportManager {
// Instantiate a exporter
const exporter = new OBJExporter();
const result = exporter.parse(scene);
this.saveString(result, 'phoenix-obj.obj');
saveFile(result, 'phoenix-obj.obj', 'text/plain');
}

/**
Expand All @@ -40,7 +35,7 @@ export class ExportManager {
result => {
const jsonResult = { sceneConfiguration: sceneConfig, scene: result };
const output = JSON.stringify(jsonResult, null, 2);
this.saveString(output, 'phoenix-scene.phnx');
saveFile(output, 'phoenix-scene.phnx', 'text/plain');
},
null
);
Expand Down Expand Up @@ -91,27 +86,4 @@ export class ExportManager {
return geometriesConfig;
}

/**
* Save string in the file and download it.
* @param text Text to be stored.
* @param filename Name of the file.
*/
private saveString(text: string, filename: string) {
this.save(new Blob([text], { type: 'text/plain' }), filename);
}

/**
* Create a temporary link and download/save the data (blob) in a file.
* @param blob Blob containing exported data.
* @param filename Name of the export file.
*/
private save(blob: Blob, filename: string) {
const link = document.createElement('a');
link.style.display = 'none';
document.body.appendChild(link);
link.href = URL.createObjectURL(blob);
link.download = filename;
link.click();
}

}
11 changes: 10 additions & 1 deletion packages/phoenix-event-display/src/three/scene-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ export class SceneManager {
}

/**
* Removes an object from the scene.
* Removes a geometry from the scene.
* @param name Name of the object to be removed.
*/
public removeGeometry(name: string) {
Expand All @@ -213,6 +213,15 @@ export class SceneManager {
geometries.remove(object);
}

/**
* Remove label from the scene.
* @param name Name of the label to remove.
*/
public removeLabel(name: string) {
const object = this.scene.getObjectByName(name);
this.getObjectsGroup(SceneManager.LABELS_ID).remove(object);
}

/**
* Scales an object.
* @param name Name of the object to scale.
Expand Down
Loading

0 comments on commit 2219a3e

Please sign in to comment.