src/managers/ui-manager/phoenix-menu/phoenix-menu-ui.ts
A wrapper class for Phoenix menu to perform UI related operations.
Properties |
|
Methods |
|
constructor(phoenixMenuRoot: PhoenixMenuNode, three: ThreeManager)
|
||||||||||||
Create Phoenix menu UI with different controls related to detector geometry and event data.
Parameters :
|
Private eventFolder |
Type : PhoenixMenuNode
|
Phoenix menu node containing event related data. |
Private eventFolderState |
Type : any
|
State of the Phoenix menu node containing event related data. |
Private geomFolder |
Type : PhoenixMenuNode
|
Phoenix menu node containing geometries data |
Private labelsFolder |
Type : PhoenixMenuNode
|
Phoenix menu node containing labels. |
Private sceneManager |
Type : SceneManager
|
Manager for managing functions of the three.js scene. |
Public addCollection | ||||||||||||||||||||
addCollection(eventDataType: string, collectionName: string, cuts?: Cut[], collectionColor?: Color)
|
||||||||||||||||||||
Add collection folder and its configurable options to the event data type (tracks, hits etc.) folder.
Parameters :
Returns :
void
|
Private addCutOptions | ||||||||||||||||
addCutOptions(collectionNode: PhoenixMenuNode, collectionName: string, cuts: Cut[])
|
||||||||||||||||
Add Cut Options folder to the menu.
Parameters :
Returns :
void
|
Private addDrawOptions | ||||||||||||
addDrawOptions(collectionNode: PhoenixMenuNode, collectionName: string)
|
||||||||||||
Add Draw Options folder to the menu.
Parameters :
Returns :
void
|
Public addEventDataFolder |
addEventDataFolder()
|
Add event data folder with functions for event data toggles like show/hide and depthTest.
Returns :
void
|
Public addEventDataTypeFolder | ||||||||
addEventDataTypeFolder(typeName: string)
|
||||||||
Add folder for event data type like tracks or hits to the menu.
Parameters :
Returns :
void
|
Public addGeometry | ||||||||||||
addGeometry(object: Object3D, menuSubfolder?: string)
|
||||||||||||
Add geometry to the menu's geometry folder and set up its configurable options.
Parameters :
Returns :
void
|
Public addGeometryFolder |
addGeometryFolder()
|
Add geometry (detector geometry) folder to the menu.
Returns :
void
|
Public addLabel | ||||||||||||
addLabel(labelId: string, onRemoveLabel?: () => void)
|
||||||||||||
Add folder for configuration of label.
Parameters :
Returns :
void
|
Public addLabelsFolder | ||||||||
addLabelsFolder(configFunctions: any)
|
||||||||
Add labels folder to the menu.
Parameters :
Returns :
void
|
Public clear |
clear()
|
Clear the menu by removing all folders.
Returns :
void
|
Public getEventDataTypeFolder | ||||||||
getEventDataTypeFolder(typeName: string)
|
||||||||
Get the folder of the event data type.
Parameters :
Returns :
PhoenixMenuNode | undefined
Folder of the event data type. |
Public loadEventFolderState |
loadEventFolderState()
|
Load previous state of the event data folder in Phoenix menu if any.
Returns :
void
|
Public removeLabel | ||||||||||||
removeLabel(labelId: string, labelNode?: PhoenixMenuNode)
|
||||||||||||
Remove label from the menu and scene if it exists.
Parameters :
Returns :
void
|
import {
BufferGeometry,
Color,
Mesh,
MeshPhongMaterial,
Object3D,
} from 'three';
import { SceneManager } from '../../three-manager/scene-manager';
import { ThreeManager } from '../../three-manager/index';
import { PhoenixMenuNode } from './phoenix-menu-node';
import { Cut } from '../../../lib/models/cut.model';
import { ColorByOptionKeys, ColorOptions } from '../color-options';
import type { PhoenixUI } from '../phoenix-ui';
/**
* A wrapper class for Phoenix menu to perform UI related operations.
*/
export class PhoenixMenuUI implements PhoenixUI<PhoenixMenuNode> {
/** Phoenix menu node containing geometries data */
private geomFolder: PhoenixMenuNode;
/** Phoenix menu node containing event related data. */
private eventFolder: PhoenixMenuNode;
/** State of the Phoenix menu node containing event related data. */
private eventFolderState: any;
/** Phoenix menu node containing labels. */
private labelsFolder: PhoenixMenuNode;
/** Manager for managing functions of the three.js scene. */
private sceneManager: SceneManager;
/**
* Create Phoenix menu UI with different controls related to detector geometry and event data.
* @param phoenixMenuRoot Root node of the Phoenix menu.
* @param three The three manager for managing three.js related operations.
*/
constructor(
private phoenixMenuRoot: PhoenixMenuNode,
private three: ThreeManager,
) {
this.sceneManager = three.getSceneManager();
}
/**
* Clear the menu by removing all folders.
*/
public clear() {
if (this.phoenixMenuRoot) {
this.phoenixMenuRoot.truncate();
}
this.eventFolderState = undefined;
}
/**
* Add geometry (detector geometry) folder to the menu.
*/
public addGeometryFolder() {
// Phoenix menu
if (this.eventFolderState === undefined) {
this.geomFolder = this.phoenixMenuRoot.addChild(
'Detector',
(value) => {
this.sceneManager.groupVisibility(SceneManager.GEOMETRIES_ID, value);
},
'perspective',
);
}
this.geomFolder
.addConfig({
type: 'checkbox',
label: 'Wireframe',
isChecked: false,
onChange: (value) => {
this.sceneManager.wireframeGeometries(value);
},
})
.addConfig({
type: 'slider',
label: 'Opacity',
min: 0,
max: 1,
step: 0.01,
allowCustomValue: true,
onChange: (value) => {
this.sceneManager.setGeometryOpacity(
this.sceneManager.getObjectByName(
SceneManager.GEOMETRIES_ID,
) as Mesh,
value,
);
},
})
.addConfig({
type: 'slider',
label: 'Scale',
min: 0,
max: 20,
step: 0.01,
allowCustomValue: true,
onChange: (scale) => {
this.sceneManager.scaleObject(
this.sceneManager.getObjectByName(SceneManager.GEOMETRIES_ID),
scale,
);
},
});
}
/**
* Add geometry to the menu's geometry folder and set up its configurable options.
* @param object Object to add to the UI menu.
* @param menuSubfolder Subfolder in the menu to add the geometry to. Example `Folder > Subfolder`.
*/
public addGeometry(object: Object3D, menuSubfolder?: string) {
const { name, material, visible } = object as Mesh<
BufferGeometry,
MeshPhongMaterial
>;
const color = material?.color;
let parentNode: PhoenixMenuNode = this.geomFolder;
if (menuSubfolder) {
parentNode = this.geomFolder.findInTreeOrCreate(menuSubfolder);
}
// find out where the actual object name starts, providing that the name
// is hierarchical and contents higher level menu names too
let nameStart = name.lastIndexOf(' > ');
if (nameStart < 0) {
nameStart = 0; // case where there is no hierarchy
} else {
nameStart += 3; // skip the last ' > '
}
const objFolder = parentNode.addChild(
name.substring(nameStart),
(value: boolean) => {
this.sceneManager.objectVisibility(object, value);
},
);
objFolder.toggleState = visible;
objFolder
.addConfig({
type: 'color',
label: 'Color',
color: color ? `#${new Color(color).getHexString()}` : undefined,
onChange: (value) => {
this.sceneManager.changeObjectColor(object, value);
},
})
.addConfig({
type: 'slider',
label: 'Opacity',
min: 0,
max: 1,
step: 0.05,
allowCustomValue: true,
onChange: (opacity) => {
this.sceneManager.setGeometryOpacity(object as Mesh, opacity);
},
})
.addConfig({
type: 'button',
label: 'Remove',
onClick: () => {
objFolder.remove();
this.sceneManager.removeGeometry(object);
},
});
}
/**
* Add event data folder with functions for event data toggles like show/hide and depthTest.
*/
public addEventDataFolder() {
// Phoenix menu
if (this.eventFolder) {
this.eventFolderState = this.eventFolder.getNodeState();
this.eventFolder.remove();
}
this.eventFolder = this.phoenixMenuRoot.addChild(
'Event Data',
(value: boolean) => {
this.sceneManager.groupVisibility(SceneManager.EVENT_DATA_ID, value);
},
'event-folder',
);
this.eventFolder.addConfig({
type: 'checkbox',
label: 'Depth Test',
isChecked: true,
onChange: (value) => {
this.three.eventDataDepthTest(value);
},
});
}
/**
* Add folder for event data type like tracks or hits to the menu.
* @param typeName Name of the type of event data.
*/
public addEventDataTypeFolder(typeName: string): void {
this.eventFolder.addChild(typeName, (value: boolean) => {
this.sceneManager.objectVisibility(
this.sceneManager
.getObjectByName(SceneManager.EVENT_DATA_ID)
.getObjectByName(typeName) as Object3D,
value,
);
});
}
/**
* Add collection folder and its configurable options to the event data type (tracks, hits etc.) folder.
* @param eventDataType Name of the event data type.
* @param collectionName Name of the collection to be added in the type of event data (tracks, hits etc.).
* @param cuts Cuts to the collection of event data that are to be made configurable to filter event data.
* @param collectionColor Default color of the collection.
*/
public addCollection(
eventDataType: string,
collectionName: string,
cuts?: Cut[],
collectionColor?: Color,
) {
const typeFolder = this.eventFolder.children.find(
(eventDataTypeNode) => eventDataTypeNode.name === eventDataType,
);
if (!typeFolder) {
return;
}
const collectionNode = typeFolder.addChild(
collectionName,
(value: boolean) => {
const collectionObject = this.sceneManager
.getObjectByName(SceneManager.EVENT_DATA_ID)
.getObjectByName(collectionName);
if (collectionObject)
this.sceneManager.objectVisibility(collectionObject, value);
},
);
this.addDrawOptions(collectionNode, collectionName);
if (cuts && cuts.length > 0) {
this.addCutOptions(collectionNode, collectionName, cuts);
}
const colorByOptions: ColorByOptionKeys[] = [];
// Extra config options specific to tracks
if (typeFolder.name === 'Tracks') {
colorByOptions.push(
ColorByOptionKeys.CHARGE,
ColorByOptionKeys.MOM,
ColorByOptionKeys.VERTEX,
);
}
new ColorOptions(
this.three.getColorManager(),
collectionNode,
collectionColor ? collectionColor : new Color(),
colorByOptions,
);
}
/**
* Add Cut Options folder to the menu.
* @param collectionNode The parent node to attach folder to
* @param collectionName The name of the collection
*/
private addCutOptions(
collectionNode: PhoenixMenuNode,
collectionName: string,
cuts: Cut[],
) {
const cutsOptionsNode = collectionNode.addChild('Cut Options');
cutsOptionsNode
.addConfig({
type: 'label',
label: 'Cuts',
})
.addConfig({
type: 'button',
label: 'Reset cuts',
onClick: () => {
this.sceneManager.groupVisibility(
collectionName,
true,
SceneManager.EVENT_DATA_ID,
);
for (const cut of cuts) {
cut.reset();
}
},
});
// Add range sliders for cuts
for (const cut of cuts) {
cutsOptionsNode.addConfig(
cut.getConfigRangeSlider(() =>
this.sceneManager.collectionFilter(collectionName, cuts),
),
);
}
}
/**
* Add Draw Options folder to the menu.
* @param collectionNode The parent node to attach folder to
* @param collectionName The name of the collection
*/
private addDrawOptions(
collectionNode: PhoenixMenuNode,
collectionName: string,
) {
const drawOptionsNode = collectionNode.addChild('Draw Options');
drawOptionsNode.addConfig({
type: 'slider',
label: 'Opacity',
min: 0.1,
step: 0.1,
max: 1,
onChange: (value) => {
const eventDataObject = this.sceneManager.getObjectByName(
SceneManager.EVENT_DATA_ID,
);
if (eventDataObject) {
const collectionObject =
eventDataObject.getObjectByName(collectionName);
if (collectionObject) {
this.sceneManager.setGeometryOpacity(
collectionObject as Mesh,
value,
);
}
}
},
});
drawOptionsNode.addConfig({
type: 'checkbox',
label: 'Wireframe',
onChange: (value) =>
this.sceneManager.wireframeObjects(
this.sceneManager
.getObjectByName(SceneManager.EVENT_DATA_ID)
?.getObjectByName(collectionName) as Object3D,
value,
),
});
}
/**
* Add labels folder to the menu.
* @param configFunctions Functions to attach to the labels folder configuration.
*/
public addLabelsFolder(configFunctions: any) {
if (this.labelsFolder) {
return;
}
const {
onToggle,
onSizeChange,
onColorChange,
onSaveLabels,
onLoadLabels,
} = configFunctions;
this.labelsFolder = this.phoenixMenuRoot.addChild(
SceneManager.LABELS_ID,
onToggle,
'info',
);
this.labelsFolder.addConfig({
type: 'slider',
label: 'Size',
min: 0,
max: 10,
step: 0.01,
allowCustomValue: true,
onChange: onSizeChange,
});
this.labelsFolder.addConfig({
type: 'color',
label: 'Color',
color: '#a8a8a8',
onChange: onColorChange,
});
this.labelsFolder.addConfig({
type: 'button',
label: 'Save Labels',
onClick: onSaveLabels,
});
this.labelsFolder.addConfig({
type: 'button',
label: 'Load Labels',
onClick: onLoadLabels,
});
}
/**
* Add folder for configuration of label.
* @param labelId Unique ID of the label.
* @param onRemoveLabel Function called when label is removed.
*/
public addLabel(labelId: string, onRemoveLabel?: () => void) {
let labelNode = this.labelsFolder.children.find(
(phoenixMenuNode) => phoenixMenuNode.name === labelId,
);
if (labelNode) {
return;
}
labelNode = this.labelsFolder.addChild(labelId, (value) => {
const labelObject = this.sceneManager
.getObjectByName(SceneManager.LABELS_ID)
.getObjectByName(labelId);
if (labelObject) this.sceneManager.objectVisibility(labelObject, value);
});
labelNode.addConfig({
type: 'color',
label: 'Color',
color: '#a8a8a8',
onChange: (value) => {
this.sceneManager.changeObjectColor(
this.sceneManager.getObjectByName(labelId),
value,
);
},
});
labelNode.addConfig({
type: 'button',
label: 'Remove',
onClick: () => {
onRemoveLabel?.();
this.removeLabel(labelId, labelNode);
},
});
}
/**
* Remove label from the menu and scene if it exists.
* @param labelId A unique label ID string.
* @param labelFolderReference Reference to the label folder.
*/
public removeLabel(labelId: string, labelNode?: PhoenixMenuNode) {
if (!labelNode) {
labelNode = this.labelsFolder?.children.find(
(singleLabelNode) => singleLabelNode.name === labelId,
);
}
labelNode?.remove();
}
/**
* Get the folder of the event data type.
* @param typeName Name of the event data type.
* @returns Folder of the event data type.
*/
public getEventDataTypeFolder(typeName: string): PhoenixMenuNode | undefined {
return this.eventFolder.children.find(
(eventDataTypeNode) => eventDataTypeNode.name === typeName,
);
}
/**
* Load previous state of the event data folder in Phoenix menu if any.
*/
public loadEventFolderState() {
if (this.eventFolderState) {
this.eventFolder.loadStateFromJSON(this.eventFolderState);
}
}
}