File

src/managers/ui-manager/index.ts

Description

Manager for UI related operations including the dat.GUI menu, stats-js and theme settings.

Index

Properties
Methods

Constructor

constructor(three: ThreeManager)

Constructor for the UI manager.

Parameters :
Name Type Optional Description
three ThreeManager No

Three manager to perform three.js related operations.

Properties

Private configuration
Type : Configuration

Configuration options for preset views and event data loader.

Private darkTheme
Type : boolean

If dark theme is enabled or disabled.

Private geomFolderAdded
Type : boolean
Default value : false

If the geometry folder is added or not

Private labelsFolderAdded
Type : boolean
Default value : false

If the labels folder is added or not

Private stateManager
Type : StateManager

State manager for managing the event display's state.

Private stats
Type : any

Stats object from stats-js.

Private uiMenus
Type : PhoenixUI<>[]
Default value : []

The dat.GUI menu UI. A wrapper for dat.GUI menu to perform UI related operations.

Methods

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 :
Name Type Optional Description
eventDataType string No

Name of the event data type.

collectionName string No

Name of the collection to be added in the type of event data (tracks, hits etc.).

cuts Cut[] Yes

Cuts to the collection of event data that are to be made configurable to filter event data.

collectionColor Color Yes
Returns : void
Public addEventDataFolder
addEventDataFolder()

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 dat.GUI and Phoenix menu.

Parameters :
Name Type Optional Description
typeName string No

Name of the type of event data.

Returns : void

dat.GUI and Phoenix menu's folder for event data type.

Public addGeometry
addGeometry(object: Object3D, menuSubfolder?: string)

Add geometry to the menus geometry folder and set up its configurable options.

Parameters :
Name Type Optional Description
object Object3D No

Object to add to the UI menu.

menuSubfolder string Yes

Subfolder in the menu to add the geometry to. Example Folder > Subfolder.

Returns : void
Public addGeomFolder
addGeomFolder()

Add geometry (detector geometry) folder to the dat.GUI and Phoenix menu.

Returns : void
Public addLabel
addLabel(labelId: string)

Add configuration UI for label.

Parameters :
Name Type Optional Description
labelId string No

Unique ID of the label.

Returns : void
Public addLabelsFolder
addLabelsFolder()

Add labels folder to dat.GUI and Phoenix menu.

Returns : void
Public clearUI
clearUI()

Clear the UI by removing the dat.GUI and phoenix menu(s).

Returns : void
Public detectColorScheme
detectColorScheme()

Detect the current theme and set it.

Returns : void
Public displayView
displayView(view: PresetView)

Change camera view to a preset view.

Parameters :
Name Type Optional Description
view PresetView No

Preset view to which the camera has to be transformed.

Returns : void
Public enableKeyboardControls
enableKeyboardControls()

Enable keyboard controls for some UI manager operations.

Returns : void
Public geometryVisibility
geometryVisibility(name: string, visible: boolean)

Sets the visibility of a geometry in the scene.

Parameters :
Name Type Optional Description
name string No

Name of the geometry in the scene

visible boolean No

Value for the visibility of the object

Returns : void
Public getCartesianGridConfig
getCartesianGridConfig()

Returns the cartesian grid configuration

Returns : any
Public getDarkTheme
getDarkTheme()

Get if the theme is dark or not.

Returns : boolean

If the theme is dark or not.

Public getPresetAnimations
getPresetAnimations()

Get preset animations from the configuration.

Returns : AnimationPreset[]

Available preset animations.

Public getPresetViews
getPresetViews()

Get preset views from the configuration.

Returns : PresetView[]

Available preset views.

Public getUIMenus
getUIMenus()

Get all the UI menus.

Returns : PhoenixUI[]

An array containing UI menus. (Phoenix menu, dat.GUI menu etc.)

Public init
init(configuration: Configuration)

Show/load the UI including stats, the dat.GUI menu and theme.

Parameters :
Name Type Optional Description
configuration Configuration No

Configuration options for preset views and event data loader.

Returns : void
Public loadEventFolderPhoenixMenuState
loadEventFolderPhoenixMenuState()

Load previous state of the event data folder in Phoenix menu if any.

Returns : void
Private loadLabelsFile
loadLabelsFile()

Load labels from a file.

Returns : void
Public removeLabel
removeLabel(labelId: string, removeFolders?: boolean)

Remove label from UI, scene and event data loader if it exists.

Parameters :
Name Type Optional Description
labelId string No

A unique label ID string.

removeFolders boolean Yes

Whether to remove label folders from dat.GUI and Phoenix menu.

Returns : void
Public rotateOpeningAngleClipping
rotateOpeningAngleClipping(angle: number)

Rotate the opening angle of clipping on detector geometry.

Parameters :
Name Type Optional Description
angle number No

Angle of rotation of the clipping.

Returns : void
Public rotateStartAngleClipping
rotateStartAngleClipping(angle: number)

Rotate the starting angle of clipping on detector geometry.

Parameters :
Name Type Optional Description
angle number No

Angle of rotation of the clipping.

Returns : void
Public setAutoRotate
setAutoRotate(rotate: boolean)

Set autorotate for the orbit controls.

Parameters :
Name Type Optional Description
rotate boolean No

If the autorotate is to be set or not.

Returns : void
Public setClipping
setClipping(value: boolean)

Set if the detector geometry is to be clipped or not.

Parameters :
Name Type Optional Description
value boolean No

Set clipping to be true or false.

Returns : void
Public setDarkTheme
setDarkTheme(dark: boolean)

Set if the theme is to be dark or light.

Parameters :
Name Type Optional Description
dark boolean No

If the theme is to be dark or light. True for dark and false for light theme.

Returns : void
Public setOverlayRenderer
setOverlayRenderer(overlayCanvas: HTMLCanvasElement)

Set the renderer for the secondary overlay canvas.

Parameters :
Name Type Optional Description
overlayCanvas HTMLCanvasElement No

Canvas for which the overlay renderer is to be set.

Returns : void
Public setShowAxis
setShowAxis(show: boolean)

Set whether to show the axis or not

Parameters :
Name Type Optional Description
show boolean No

If the axis is to be shown or not.

Returns : void
Public setShowCartesianGrid
setShowCartesianGrid(show: boolean, scale: number, config?: literal type)

Set whether to show the cartesian or not

Parameters :
Name Type Optional Description
show boolean No

If the grid is to be shown or not.

scale number No

The maximum dimensions (height, width, length) of the grid

config literal type Yes

Configuration related to the visibility of the grid, such as visibility of the planes, number of planes in each direction, sparsity of the gridlines

Returns : void
Public setShowEtaPhiGrid
setShowEtaPhiGrid(show: boolean)

Set whether to show the eta/phi or not

Parameters :
Name Type Optional Description
show boolean No

If the grid is to be shown or not.

Returns : void
Public shiftCartesianGridByPointer
shiftCartesianGridByPointer()

Shift cartesian grid by a mouse click

Returns : void
Public show3DDistance
show3DDistance(show: boolean)

Show 3D Distance between two clicked points

Parameters :
Name Type Optional
show boolean No
Returns : void
Public show3DMousePoints
show3DMousePoints(show: boolean)

Show 3D coordinates where the mouse pointer clicks

Parameters :
Name Type Optional Description
show boolean No

If the coordinates are to be shown or not.

Returns : void
Public showLabels
showLabels(visible: boolean)

Show labels on cartesian grid

Parameters :
Name Type Optional Description
visible boolean No

if the labels are to be shown or not

Returns : void
Private showStats
showStats(elementId: string)

Show stats including FPS, milliseconds to render a frame, allocated memory etc.

Parameters :
Name Type Optional Default value Description
elementId string No 'eventDisplay'

ID of the wrapper element.

Returns : void
Public toggleOrthographicView
toggleOrthographicView(orthographic: boolean)

Toggle orthographic/perspective view.

Parameters :
Name Type Optional Description
orthographic boolean No

If the camera is to be orthographic or perspective.

Returns : void
Public translateCartesianGrid
translateCartesianGrid(translate: Vector3)

Translate the cartesian grid

Parameters :
Name Type Optional
translate Vector3 No
Returns : void
Public translateCartesianLabels
translateCartesianLabels(translate: Vector3)

Translate the cartesian labels

Parameters :
Name Type Optional
translate Vector3 No
Returns : void
Public updateUI
updateUI()

Update the UI by updating stats for each frame.

Returns : void
import Stats from 'stats-js';
import { Color, Object3D, Vector3 } from 'three';
import { ThreeManager } from '../three-manager';
import { Configuration } from '../../lib/types/configuration';
import {
  PresetView,
  ClippingSetting,
} from '../../lib/models/preset-view.model';
import { Cut } from '../../lib/models/cut.model';
import { SceneManager } from '../three-manager/scene-manager';
import { StateManager } from '../../managers/state-manager';
import { loadFile, saveFile } from '../../helpers/file';
import { DatGUIMenuUI } from './dat-gui-ui';
import { PhoenixMenuUI } from './phoenix-menu/phoenix-menu-ui';
import {
  getFromLocalStorage,
  setToLocalStorage,
} from '../../helpers/browser-storage';
import { PhoenixUI } from './phoenix-ui';
import { AnimationPreset } from '../../managers/three-manager/animations-manager';

/** If animation presets not passed in configuration, we will use this. */
const defaultAnimationPresets: AnimationPreset[] = [
  {
    name: 'Cavern to ID',
    positions: [
      {
        position: [66388.95051168812, 5264.228603228927, -46910.7848593543],
        duration: 1000,
      },
      {
        position: [12834.18729094943, 677.7571205763458, 135.68755273443463],
        duration: 2000,
      },
      {
        position: [312.02688693297375, 25.884223757326, 270.10019006776236],
        duration: 3500,
      },
      {
        position: [263.3640855132258, 19.874838262525053, -318.16541790248885],
        duration: 3000,
      },
      {
        position: [5534.140362338047, 234.03507981484574, -2933.619479808285],
        duration: 2000,
      },
      {
        position: [2681.277288705242, 646.5795158318147, 5628.5248735111745],
        duration: 1000,
      },
      {
        position: [-6062.586283740076, 790.5876682946184, 1381.1675900848818],
        duration: 1000,
      },
      {
        position: [-1766.7693725879053, 1007.1048030984678, -5928.901341784575],
        duration: 1000,
      },
      {
        position: [12814.982506255355, 2516.987185037266, -22891.902734328327],
        duration: 1000,
      },
    ],
    animateEventAfterInterval: 5000,
    collisionDuration: 6000,
  },
];

/**
 * Manager for UI related operations including the dat.GUI menu, stats-js and theme settings.
 */
export class UIManager {
  // Functions ending in PM are for Phoenix Menu

  /** The dat.GUI menu UI. A wrapper for dat.GUI menu to perform UI related operations. */
  private uiMenus: PhoenixUI<unknown>[] = [];
  /** Stats object from stats-js. */
  private stats: any;
  /** If the geometry folder is added or not */
  private geomFolderAdded: boolean = false;
  /** If the labels folder is added or not */
  private labelsFolderAdded: boolean = false;
  /** Configuration options for preset views and event data loader. */
  private configuration: Configuration;
  /** If dark theme is enabled or disabled. */
  private darkTheme: boolean;

  /** State manager for managing the event display's state. */
  private stateManager: StateManager;

  /**
   * Constructor for the UI manager.
   * @param three Three manager to perform three.js related operations.
   */
  constructor(private three: ThreeManager) {}

  /**
   * Show/load the UI including stats, the dat.GUI menu and theme.
   * @param configuration Configuration options for preset views and event data loader.
   */
  public init(configuration: Configuration) {
    // Clear the existing UI
    this.clearUI();
    // Set the configuration
    this.configuration = configuration;
    // Shows a panel on screen with information about the performance (fps).
    this.showStats(configuration.elementId);

    // UI Menus
    this.uiMenus = [];
    if (configuration.enableDatGUIMenu) {
      this.uiMenus.push(new DatGUIMenuUI(configuration.elementId, this.three));
    }
    if (configuration.phoenixMenuRoot) {
      this.uiMenus.push(
        new PhoenixMenuUI(configuration.phoenixMenuRoot, this.three),
      );
    }
    if (!configuration.forceColourTheme) {
      // Detect UI color scheme
      this.detectColorScheme();
    } else {
      this.setDarkTheme(
        configuration.forceColourTheme.toLocaleLowerCase() == 'dark',
      );
    }
    // State manager
    this.stateManager = new StateManager();
    this.stateManager.setPhoenixMenuRoot(configuration.phoenixMenuRoot);
  }

  /**
   * Show stats including FPS, milliseconds to render a frame, allocated memory etc.
   * @param elementId ID of the wrapper element.
   */
  private showStats(elementId: string = 'eventDisplay') {
    this.stats = new Stats();
    this.stats.showPanel(0);
    this.stats.dom.className = 'ui-element';
    this.stats.dom.id = 'statsElement';
    this.stats.domElement.style.cssText =
      'position: absolute; left: 0px; cursor: pointer; opacity: 0.9; z-index: 10; bottom: 0px;';
    let canvas = document.getElementById(elementId);
    if (canvas == null) {
      canvas = document.body;
    }
    canvas.appendChild(this.stats.dom);
  }

  /**
   * Update the UI by updating stats for each frame.
   */
  public updateUI() {
    this.stats.update();
  }

  /**
   * Clear the UI by removing the dat.GUI and phoenix menu(s).
   */
  public clearUI() {
    this.uiMenus.forEach((menu) => menu.clear());

    this.geomFolderAdded = false;
    this.labelsFolderAdded = false;
  }

  /**
   * Add geometry (detector geometry) folder to the dat.GUI and Phoenix menu.
   */
  public addGeomFolder() {
    this.geomFolderAdded = true;
    this.uiMenus.forEach((menu) => menu.addGeometryFolder());
  }

  /**
   * Add geometry to the menus 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) {
    if (!this.geomFolderAdded) {
      this.addGeomFolder();
    }

    this.uiMenus.forEach((menu) => menu.addGeometry(object, menuSubfolder));
  }

  /**
   * Functions for event data toggles like show/hide and depthTest.
   */
  public addEventDataFolder() {
    this.uiMenus.forEach((menu) => menu.addEventDataFolder());
  }

  /**
   * Add folder for event data type like tracks or hits to the dat.GUI and Phoenix menu.
   * @param typeName Name of the type of event data.
   * @returns dat.GUI and Phoenix menu's folder for event data type.
   */
  public addEventDataTypeFolder(typeName: string): void {
    this.uiMenus.forEach((menu) => menu.addEventDataTypeFolder(typeName));
  }

  /**
   * 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.
   */
  public addCollection(
    eventDataType: string,
    collectionName: string,
    cuts?: Cut[],
    collectionColor?: Color,
  ) {
    this.uiMenus.forEach((menu) =>
      menu.addCollection(eventDataType, collectionName, cuts, collectionColor),
    );
  }

  /**
   * Add labels folder to dat.GUI and Phoenix menu.
   */
  public addLabelsFolder() {
    const sceneManager = this.three.getSceneManager();
    this.labelsFolderAdded = true;

    // Common functions for Phoenix and dat.GUI menus
    const onToggle = (toggleValue: boolean) => {
      sceneManager.objectVisibility(
        sceneManager.getObjectByName(SceneManager.LABELS_ID),
        toggleValue,
      );
    };
    const onSizeChange = (scale: number) => {
      const labels = sceneManager.getObjectByName(SceneManager.LABELS_ID);
      sceneManager.scaleObject(labels, scale);
    };
    const onColorChange = (value: any) => {
      const labels = sceneManager.getObjectByName(SceneManager.LABELS_ID);
      sceneManager.changeObjectColor(labels, value);
    };
    const onSaveLabels = () => {
      const labelsObject =
        this.configuration?.eventDataLoader?.getLabelsObject();
      if (labelsObject) {
        saveFile(JSON.stringify(labelsObject), 'phoenix-labels.json');
      }
    };
    const onLoadLabels = () => {
      this.loadLabelsFile();
    };

    this.uiMenus.forEach((menu) =>
      menu.addLabelsFolder({
        onToggle,
        onSizeChange,
        onColorChange,
        onSaveLabels,
        onLoadLabels,
      }),
    );
  }

  /**
   * Add configuration UI for label.
   * @param labelId Unique ID of the label.
   */
  public addLabel(labelId: string) {
    if (!this.labelsFolderAdded) {
      this.addLabelsFolder();
    }

    this.uiMenus.forEach((menu) =>
      menu?.addLabel(labelId, () => this.removeLabel(labelId)),
    );
  }

  /**
   * Remove label from UI, scene and event data loader if it exists.
   * @param labelId A unique label ID string.
   * @param removeFolders Whether to remove label folders from dat.GUI and Phoenix menu.
   */
  public removeLabel(labelId: string, removeFolders?: boolean) {
    this.three.getSceneManager().removeLabel(labelId);
    const objectKeys = labelId.split(' > ');
    // labelsObject[EventDataType][Collection][Index]
    const labelsObject = this.configuration.eventDataLoader?.getLabelsObject();
    delete labelsObject?.[objectKeys[0]]?.[objectKeys[1]]?.[objectKeys[2]];

    if (removeFolders) {
      this.uiMenus.forEach((menu) => menu.removeLabel(labelId));
    }
  }

  /**
   * Sets the visibility of a geometry in the scene.
   * @param name Name of the geometry in the scene
   * @param visible Value for the visibility of the object
   */
  public geometryVisibility(name: string, visible: boolean) {
    const sceneManager = this.three.getSceneManager();
    sceneManager.objectVisibility(sceneManager.getObjectByName(name), visible);
  }

  /**
   * Rotate the starting angle of clipping on detector geometry.
   * @param angle Angle of rotation of the clipping.
   */
  public rotateStartAngleClipping(angle: number) {
    const openingAngle = this.stateManager.getOpeningClippingAngle();
    this.three.setClippingAngle(angle, openingAngle);
    this.stateManager.setStartClippingAngle(angle);
  }

  /**
   * Rotate the opening angle of clipping on detector geometry.
   * @param angle Angle of rotation of the clipping.
   */
  public rotateOpeningAngleClipping(angle: number) {
    const startingAngle = this.stateManager.getStartClippingAngle();
    this.three.setClippingAngle(startingAngle, angle);
    this.stateManager.setOpeningClippingAngle(angle);
  }

  /**
   * Set if the detector geometry is to be clipped or not.
   * @param value Set clipping to be true or false.
   */
  public setClipping(value: boolean) {
    this.three.setClipping(value);
    this.stateManager.setClippingEnabled(value);
  }

  /**
   * Detect the current theme and set it.
   */
  public detectColorScheme() {
    let dark = false; // default to light

    // local storage is used to override OS theme settings
    if (getFromLocalStorage('theme')) {
      if (getFromLocalStorage('theme') === 'dark') {
        dark = true;
      }
    } else if (!window.matchMedia) {
      // matchMedia method not supported
    } else if (matchMedia('(prefers-color-scheme: dark)').matches) {
      // OS theme setting detected as dark
      dark = true;
    }

    this.darkTheme = dark;
    // dark theme preferred, set document with a `data-theme` attribute
    this.setDarkTheme(dark);
  }

  /**
   * Set if the theme is to be dark or light.
   * @param dark If the theme is to be dark or light. True for dark and false for light theme.
   */
  public setDarkTheme(dark: boolean) {
    const theme = dark ? 'dark' : 'light';
    setToLocalStorage('theme', theme);
    document.documentElement.setAttribute('data-theme', theme);
    this.three.setDarkColor(dark);
  }

  /**
   * Get if the theme is dark or not.
   * @returns If the theme is dark or not.
   */
  public getDarkTheme(): boolean {
    return this.darkTheme;
  }

  /**
   * Set autorotate for the orbit controls.
   * @param rotate If the autorotate is to be set or not.
   */
  public setAutoRotate(rotate: boolean) {
    this.three.autoRotate(rotate);
  }

  /**
   * Set whether to show the axis or not
   * @param show If the axis is to be shown or not.
   */
  public setShowAxis(show: boolean) {
    this.three.getSceneManager().setAxis(show);
  }

  /**
   * Translate the cartesian grid
   */
  public translateCartesianGrid(translate: Vector3) {
    this.three.getSceneManager().translateCartesianGrid(translate);
  }

  /**
   * Translate the cartesian labels
   */
  public translateCartesianLabels(translate: Vector3) {
    this.three.getSceneManager().translateCartesianLabels(translate);
  }

  /**
   * Show labels on cartesian grid
   * @param visible if the labels are to be shown or not
   */
  public showLabels(visible: boolean) {
    this.three.getSceneManager().showLabels(visible);
  }

  /**
   * Set whether to show the cartesian or not
   * @param show If the grid is to be shown or not.
   * @param scale The maximum dimensions (height, width, length) of the grid
   * @param config Configuration related to the visibility of the grid, such as visibility of the planes, number of planes in each direction, sparsity of the gridlines
   */
  public setShowCartesianGrid(
    show: boolean,
    scale: number,
    config?: {
      showXY: boolean;
      showYZ: boolean;
      showZX: boolean;
      xDistance: number;
      yDistance: number;
      zDistance: number;
      sparsity: number;
    },
  ) {
    if (typeof config === 'undefined') {
      this.three.getSceneManager().setCartesianGrid(show, scale);
    } else {
      this.three.getSceneManager().setCartesianGrid(show, scale, config);
    }
  }

  /**
   * Returns the cartesian grid configuration
   */
  public getCartesianGridConfig() {
    return this.three.getSceneManager().getCartesianGridConfig();
  }

  /**
   * Set whether to show the eta/phi or not
   * @param show If the grid is to be shown or not.
   */
  public setShowEtaPhiGrid(show: boolean) {
    this.three.getSceneManager().setEtaPhiGrid(show);
  }

  /**
   * Show 3D coordinates where the mouse pointer clicks
   * @param show If the coordinates are to be shown or not.
   */
  public show3DMousePoints(show: boolean) {
    this.three.show3DMousePoints(show);
  }

  /**
   * Show 3D Distance between two clicked points
   */
  public show3DDistance(show: boolean) {
    this.three.show3DDistance(show);
  }

  /**
   * Shift cartesian grid by a mouse click
   */
  public shiftCartesianGridByPointer() {
    this.three.shiftCartesianGrid();
  }

  /**
   * Get preset views from the configuration.
   * @returns Available preset views.
   */
  public getPresetViews(): PresetView[] {
    return this.configuration?.presetViews;
  }

  /**
   * Get preset animations from the configuration.
   * @returns Available preset animations.
   */
  public getPresetAnimations(): AnimationPreset[] {
    if (this.configuration?.presetAnimations) {
      return this.configuration?.presetAnimations;
    } else {
      return defaultAnimationPresets;
    }
  }

  /**
   * Change camera view to a preset view.
   * @param view Preset view to which the camera has to be transformed.
   */
  public displayView(view: PresetView) {
    this.three.animateCameraTransform(view.cameraPos, view.cameraTarget, 1000);
    if (view.clipping != ClippingSetting.NotForced) {
      this.rotateStartAngleClipping(view.clippingStartAngle);
      this.rotateOpeningAngleClipping(view.clippingOpeningAngle);
      this.setClipping(view.clipping == ClippingSetting.On);
    }
  }

  /**
   * Toggle orthographic/perspective view.
   * @param orthographic If the camera is to be orthographic or perspective.
   */
  public toggleOrthographicView(orthographic: boolean) {
    this.three.swapCameras(orthographic);
  }

  /**
   * Set the renderer for the secondary overlay canvas.
   * @param overlayCanvas Canvas for which the overlay renderer is to be set.
   */
  public setOverlayRenderer(overlayCanvas: HTMLCanvasElement) {
    this.three.setOverlayRenderer(overlayCanvas);
  }

  /**
   * Enable keyboard controls for some UI manager operations.
   */
  public enableKeyboardControls() {
    document.addEventListener('keydown', (e: KeyboardEvent) => {
      const isTyping = ['input', 'textarea'].includes(
        (e.target as HTMLElement)?.tagName.toLowerCase(),
      );

      if (!isTyping && e.shiftKey) {
        switch (e.code) {
          case 'KeyT': // shift + "t"
            this.setDarkTheme(!this.getDarkTheme());
            break;
        }

        // Shortcut keys for preset views (shift + 1...9)
        if (this.configuration?.presetViews) {
          if (e.code.startsWith('Digit')) {
            const index = parseInt(e.code.slice(-1)) - 1;
            if (this.configuration.presetViews?.[index]) {
              this.displayView(this.configuration.presetViews[index]);
            }
          }
        }
      }
    });
  }

  /**
   * Load labels from a file.
   */
  private loadLabelsFile() {
    const eventDataLoader = this.configuration?.eventDataLoader;
    const labelsObject = eventDataLoader?.getLabelsObject();
    if (eventDataLoader && labelsObject) {
      loadFile((data) => {
        const labelsObject = JSON.parse(data);
        for (const eventDataType of Object.keys(labelsObject)) {
          for (const collection of Object.keys(labelsObject[eventDataType])) {
            const collectionObject = eventDataLoader.getCollection(collection);
            for (const labelIndex of Object.keys(
              labelsObject[eventDataType][collection],
            )) {
              const label = labelsObject[eventDataType][collection][labelIndex];
              const objectUuid = collectionObject[labelIndex].uuid;
              const labelId = eventDataLoader.addLabelToEventObject(
                label,
                collection,
                Number(labelIndex),
              );
              this.addLabel(labelId);
              this.three.addLabelToObject(label, objectUuid, labelId);
            }
          }
        }
      });
    }
  }

  /**
   * Load previous state of the event data folder in Phoenix menu if any.
   */
  public loadEventFolderPhoenixMenuState() {
    const phoenixMenuUI = this.uiMenus.find(
      (uiMenu) => uiMenu instanceof PhoenixMenuUI,
    ) as PhoenixMenuUI;
    phoenixMenuUI?.loadEventFolderState();
  }

  /**
   * Get all the UI menus.
   * @returns An array containing UI menus. (Phoenix menu, dat.GUI menu etc.)
   */
  public getUIMenus(): PhoenixUI<unknown>[] {
    return this.uiMenus;
  }
}

results matching ""

    No results matching ""