Source: view/SubmarineDebug.js

import { Events } from "../controller/Utils/Events";
import { SubmarineType } from "../model/SubmarineType";
import Debug from "./Debug";
import * as THREE from "three";
/**
 * SubmarineDebug class is responsible for setting up and managing the debugging
 * GUI for submarines in the simulator. It allows users to switch between submarines
 * and adjust their attributes and controls in real-time.
 *
 * @class
 */
class SubmarineDebug {
  /**
   * Constructor for the SubmarineDebug class.
   * Sets up submarine related debug controls.
   *
   * @param {Simulator} simulator - The simulator instance.
   */
  constructor(simulator) {
    this.simulator = simulator;
    this.submarine = this.simulator.submarines.getCurrentSubmarine();
    this.controlsdPanel = this.simulator.controlGUI.gui;
    this.submarineAttributesPanel = this.simulator.submarineAttributesGUI.gui;
    this.environmentAttributesPanel = this.simulator.environmentAttributesGUI.gui;
    this.forcesPanel = this.simulator.forcesGUI.gui;
    this.linearMotiondPanel = this.simulator.linearMotionGUI.gui;
    this.angularMotionPanel = this.simulator.angularMotionGUI.gui;
    this.folders = [];
    this.initSelectSubmarineControl();
    this.initDebugGUI();
    this.simulator.submarines.on(Events.SwitchSubmarine, () => {
      this.submarine = this.simulator.submarines.getCurrentSubmarine();
      this.folders.forEach((gui) => {
        gui.destroy();
      });
      this.initGUIs();
      this.initSelectSubmarineControl();
      this.initDebugGUI();
    });
  }
  /**
   * Initializes the GUI control for selecting the submarine type.
   * Adds a dropdown menu to the debug GUI allowing users to switch between different submarine types.
   */
  initSelectSubmarineControl() {
    const submarineOptions = {
      type: this.simulator.submarines.getCurrentSubmarine().getType(),
    };
    this.controlsdPanel
      .add(submarineOptions, "type", [
        SubmarineType.Typhoon,
        SubmarineType.Ohio,
      ])
      .name("Select Submarine")
      .onChange((type) => {
        this.simulator.submarines.switchSubmarine(type);
      });
  }
  initGUIs() {
    this.simulator.controlGUI = new Debug({
      title: "Controls",
      top: "50px",
      bottom: "auto",
      left: "auth",
      right: "10px",
      width: 320,
    });
    this.simulator.submarineAttributesGUI = new Debug({
      title: "Submarine Attributes",
      top: "406px",
      bottom: "auto",
      left: "auto",
      right: "10px",
      width: 250,
    });
    this.simulator.forcesGUI = new Debug({
      title: "Forces",
      top: "105px",
      bottom: "auto",
      left: "10px",
      right: "auto",
      width: 200,
    });
    this.simulator.linearMotionGUI = new Debug({
      title: "Linear Motion",
      top: "240px",
      bottom: "auto",
      left: "10px",
      right: "auto",
      width: 200,
    });
    this.simulator.angularMotionGUI = new Debug({
      title: "Angular Motion",
      top: "577px",
      bottom: "auto",
      left: "10px",
      right: "auto",
      width: 200,
    });
    this.controlsdPanel = this.simulator.controlGUI.gui;
    this.submarineAttributesPanel = this.simulator.submarineAttributesGUI.gui;
    this.forcesPanel = this.simulator.forcesGUI.gui;
    this.linearMotiondPanel = this.simulator.linearMotionGUI.gui;
    this.angularMotionPanel = this.simulator.angularMotionGUI.gui;
    this.simulator.world.environment.setupAxesHelper();

  }
  /**
   * Initializes the debug GUI with submarine attributes and controls.
   */
  initDebugGUI() {
    const isConstantsFolderEnabled = true;
    const submarineConstantsFolder = this.submarineAttributesPanel;
    this.folders.push(submarineConstantsFolder);
    const submarineConstants = this.submarine.getConstants();
    submarineConstantsFolder
      .add(submarineConstants, "emptyMass")
      .name("Empty Mass (Kg)");
    submarineConstantsFolder
      .add(submarineConstants, "maxMass")
      .name("Max Mass (Kg)");
    submarineConstantsFolder
      .add(submarineConstants, "ballastTankCapacity")
      .name("Ballast Tank Capacity (m³)");
    submarineConstantsFolder.add(submarineConstants, "width").name("Width (m)");
    submarineConstantsFolder
      .add(submarineConstants, "length")
      .name("Length (m)");
    submarineConstantsFolder
      .add(submarineConstants, "volume")
      .name("Volume (m³)");
    submarineConstantsFolder
      .add(submarineConstants, "rotorDiameter")
      .name("Rotor Diameter (m)");
    submarineConstantsFolder
      .add(submarineConstants, "maxRotorRPS")
      .name("Rotor Rounds Per Second (RPS)");
    submarineConstantsFolder
      .add(submarineConstants, "sternPlaneArea")
      .name("Stern Plane Area (m²)");
    submarineConstantsFolder
      .add(submarineConstants, "rudderPlaneArea")
      .name("Rudder Plane Area (m²)");
    submarineConstantsFolder
      .add(submarineConstants, "fairwaterPlaneArea")
      .name("Fairwater Plane Area (m²)");
    submarineConstantsFolder
      .add(submarineConstants, "dragCoefficient")
      .name("Drag Coefficient");
    submarineConstantsFolder
      .add(submarineConstants, "thrustCoefficient")
      .name("Thrust Coefficient");
    submarineConstantsFolder
      .add(submarineConstants, "sternCoefficient")
      .name("Stern Coefficient");
    submarineConstantsFolder
      .add(submarineConstants, "rudderCoefficient")
      .name("Rudder Coefficient");
    submarineConstantsFolder
      .add(submarineConstants, "fairwaterCoefficient")
      .name("Fairwater Coefficient");
    submarineConstantsFolder.controllers.forEach((controller) => {
      if (isConstantsFolderEnabled) {
        controller.enable();
      } else {
        controller.disable();
      }
    });
    const environemtAttributesFolder = this.environmentAttributesPanel;
    this.folders.push(environemtAttributesFolder);
    const env = this.simulator.environment;
    environemtAttributesFolder
      .add(env, "gravity")
      .name("Gravity (m/s²)");
    environemtAttributesFolder
      .add(env, "waterDensity")
      .name("Water Density (kg/m³)");
    const submarineVariablesFolder = this.controlsdPanel;
    this.folders.push(submarineVariablesFolder);
    const submarineVariables = this.submarine.getState();
    submarineVariablesFolder
      .add(submarineVariables, "currentTotalWaterMass")
      .name("Total Water in Tanks (m³)")
      .min(0)
      .max(submarineConstants.getBallastTankCapacity())
      .listen(true)
      .onChange((value) => {
        submarineVariables.setCurrentTotalWaterMass(value);
      });
    submarineVariablesFolder
      .add(submarineVariables, "currentWaterMassFrontTank")
      .name("Water in Front Tank (m³)")
      .min(0)
      .max(submarineConstants.getBallastTankCapacity() / 2)
      .listen(true)
      .onChange((value) => {
        submarineVariables.setCurrentWaterMassFrontTank(value);
      });
    submarineVariablesFolder
      .add(submarineVariables, "currentWaterMassBackTank")
      .name("Water in Back Tank (m³)")
      .min(0)
      .max(submarineConstants.getBallastTankCapacity() / 2)
      .listen(true)
      .onChange((value) => {
        submarineVariables.setCurrentWaterMassBackTank(value);
      });
    submarineVariablesFolder
      .add(submarineVariables, "currentRotorRPS")
      .name("Rotor RPS (s)")
      .min(0)
      .max(submarineConstants.getMaxRotorRPS())
      .step(0.01);
    submarineVariablesFolder
      .add(submarineVariables, "sternAngle")
      .name("Stern Angle (°)")
      .min(-30)
      .max(30)
      .step(1);
    // .listen(true)
    // .onChange((value) => {
    //     this.simulator.world.submarineView.setRudderRotation(THREE.MathUtils.degToRad(Math.PI));
    // });
    submarineVariablesFolder
      .add(submarineVariables, "rudderAngle")
      .name("Rudder Angle (°)")
      .min(-30)
      .max(30)
      .step(1);
    submarineVariablesFolder
      .add(submarineVariables, "fairwaterAngle")
      .name("Fairwater Angle (°)")
      .min(-30)
      .max(30)
      .step(1);
    const forcesFolder = this.forcesPanel;
    this.folders.push(forcesFolder);
    // simulationFolder
    //     .add(this.simulator.time, 'start')
    //     .name("Start")
    // simulationFolder
    //     .add(submarineVariables, 'currentDepth')
    //     .name("Depth (m)")
    //     .disable()
    //     .listen(true)
    forcesFolder
      .add(submarineVariables, "weight")
      .name("Weight (N)")
      .disable()
      .listen(true);
    forcesFolder
      .add(submarineVariables, "buoyancy")
      .name("Buoyancy (N)")
      .disable()
      .listen(true);
    forcesFolder
      .add(submarineVariables, "drag")
      .name("Drag (N)")
      .disable()
      .listen(true);
    forcesFolder
      .add(submarineVariables, "thrust")
      .name("Thrust (N)")
      .disable()
      .listen(true);
    const linearMovementFolder = this.linearMotiondPanel;
    const linearAccelerationFolder = linearMovementFolder.addFolder(
      "Linear Acceleration (m/s²)"
    );
    this.folders.push(linearMovementFolder);
    linearAccelerationFolder
      .add(submarineVariables.currentAcceleration, "x")
      .name("X")
      .disable()
      .listen(true);
    linearAccelerationFolder
      .add(submarineVariables.currentAcceleration, "y")
      .name("Y")
      .disable()
      .listen(true);
    linearAccelerationFolder
      .add(submarineVariables.currentAcceleration, "z")
      .name("Z")
      .disable()
      .listen(true);
    const linearVelocityFolder = linearMovementFolder.addFolder(
      "Linear Velocity (m/s)"
    );
    linearVelocityFolder
      .add(submarineVariables.currentSpeed, "x")
      .name("X")
      .disable()
      .listen(true);
    linearVelocityFolder
      .add(submarineVariables.currentSpeed, "y")
      .name("Y")
      .disable()
      .listen(true);
    linearVelocityFolder
      .add(submarineVariables.currentSpeed, "z")
      .name("Z")
      .disable()
      .listen(true);
    const positionFolder = linearMovementFolder.addFolder("Position (m)");
    positionFolder
      .add(submarineVariables.currentPosition, "x")
      .name("X")
      .disable()
      .listen(true);
    positionFolder
      .add(submarineVariables.currentPosition, "y")
      .name("Y")
      .disable()
      .listen(true);
    positionFolder
      .add(submarineVariables.currentPosition, "z")
      .name("Z")
      .disable()
      .listen(true);
    const angularMovementFolder = this.angularMotionPanel;
    this.folders.push(angularMovementFolder);
    const angularAccelerationFolder = angularMovementFolder.addFolder(
      "Angular Acceleration (rad/s²)"
    );
    angularAccelerationFolder
      .add(submarineVariables.angularAcceleration, "x")
      .name("X")
      .disable()
      .listen(true);
    angularAccelerationFolder
      .add(submarineVariables.angularAcceleration, "y")
      .name("Y")
      .disable()
      .listen(true);
    angularAccelerationFolder
      .add(submarineVariables.angularAcceleration, "z")
      .name("Z")
      .disable()
      .listen(true);
    const angularVelocityFolder = angularMovementFolder.addFolder(
      "Angular Velocity (rad/s)"
    );
    angularVelocityFolder
      .add(submarineVariables.angularVelocity, "x")
      .name("X")
      .disable()
      .listen(true);
    angularVelocityFolder
      .add(submarineVariables.angularVelocity, "y")
      .name("Y")
      .disable()
      .listen(true);
    angularVelocityFolder
      .add(submarineVariables.angularVelocity, "z")
      .name("Z")
      .disable()
      .listen(true);
    const orientationFolder =
      angularMovementFolder.addFolder("Orientation (rad)");
    orientationFolder
      .add(submarineVariables.currentOrientation, "x")
      .name("X")
      .disable()
      .listen(true);
    orientationFolder
      .add(submarineVariables.currentOrientation, "y")
      .name("Y")
      .disable()
      .listen(true);
    orientationFolder
      .add(submarineVariables.currentOrientation, "z")
      .name("Z")
      .disable()
      .listen(true);
    let simulate = this.simulator.time.isRunning;
    const simulationControls = {
      time: "0",
      startSimulation: () => {
        if (!simulate) {
          simulate = true;
          if (startButton._name == "Resume Simulation") {
            this.simulator.time.resume();
          } else {
            this.simulator.time.start();
          }
          startButton.disable();
          pauseBotton.enable();
        }
      },
      pauseSimulation: () => {
        if (simulate) {
          simulate = false;
          this.simulator.time.pause();
          startButton.enable();
          startButton.name("Resume Simulation");
          pauseBotton.disable();
          this.simulator.world.submarineView.engineSound.pause();
        }
      },
    };
    this.simulator.time.on(Events.FrameTick, () => {
      const elapsedTime = this.simulator.time.elapsed;
      simulationControls.time = (elapsedTime / 1000).toFixed(2);
    });
    const timeFolder = this.controlsdPanel.addFolder("Time");
    this.folders.push(timeFolder);
    timeFolder
      .add(simulationControls, "time")
      .name("Elapsed Time (s)")
      .listen()
      .disable();
    const startButton = timeFolder
      .add(simulationControls, "startSimulation")
      .name("Start Simulation")
      .listen();
    const pauseBotton = timeFolder
      .add(simulationControls, "pauseSimulation")
      .name("Pause Simulation")
      .listen();
    if (this.simulator.time.isRunning) {
      startButton.disable();
    } else {
      pauseBotton.disable();
    }
    // const documentationFolder = this.controlsdPanel
    //   .addFolder("Documentation")
    //   .close();
    // this.folders.push(documentationFolder);
    // const documentationControls = {
    //   openDocs: () => {
    //     window.open("../../../docs/index.html", "_blank");
    //   },
    // };
    // documentationFolder
    //   .add(documentationControls, "openDocs")
    //   .name("Open Docs Website");
  }
}
export default SubmarineDebug;