All Downloads are FREE. Search and download functionalities are using the official Maven repository.

us.ihmc.scs2.sessionVisualizer.jfx.controllers.camera.Camera3DOptionsPaneController Maven / Gradle / Ivy

package us.ihmc.scs2.sessionVisualizer.jfx.controllers.camera;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.lang3.mutable.MutableBoolean;

import javafx.beans.property.DoubleProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.Property;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.collections.FXCollections;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.PerspectiveCamera;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.RadioButton;
import javafx.scene.control.TextField;
import javafx.scene.control.ToggleGroup;
import javafx.scene.control.Tooltip;
import javafx.scene.image.ImageView;
import javafx.scene.transform.Transform;
import us.ihmc.scs2.sessionVisualizer.jfx.controllers.camera.CameraFocalPointHandler.TrackingTargetType;
import us.ihmc.scs2.sessionVisualizer.jfx.controllers.editor.YoCompositeEditorPaneController;
import us.ihmc.scs2.sessionVisualizer.jfx.controllers.editor.searchTextField.DoubleSearchField;
import us.ihmc.scs2.sessionVisualizer.jfx.managers.ReferenceFrameManager;
import us.ihmc.scs2.sessionVisualizer.jfx.managers.YoCompositeSearchManager;
import us.ihmc.scs2.sessionVisualizer.jfx.properties.YoDoubleProperty;
import us.ihmc.scs2.sessionVisualizer.jfx.tools.CompositePropertyTools;
import us.ihmc.scs2.sessionVisualizer.jfx.yoComposite.CompositeProperty;
import us.ihmc.scs2.sessionVisualizer.jfx.yoComposite.Tuple3DProperty;
import us.ihmc.scs2.sessionVisualizer.jfx.yoComposite.YoCompositeCollection;
import us.ihmc.scs2.sharedMemory.LinkedYoRegistry;

public class Camera3DOptionsPaneController
{
   @FXML
   private RadioButton trackCoordinatesButton, trackNodeButton;
   @FXML
   private YoCompositeEditorPaneController yoCoordinateEditorController;
   @FXML
   private TextField trackingNodeTextField;
   @FXML
   private TextField xFocalPointCurrentTextField, yFocalPointCurrentTextField, zFocalPointCurrentTextField;

   @FXML
   private ComboBox cameraPositionComboxBox;

   // The following controls are for either:
   //  - x, y, z
   //  - distance, yaw, pitch
   //  - distance, yaw, z
   /** Label using for either "x" or "distance". */
   @FXML
   private Label cameraLabel1;
   /** Label using for either "y" or "yaw". */
   @FXML
   private Label cameraLabel2;
   /** Label using for either "z" or "pitch". */
   @FXML
   private Label cameraLabel3;
   /** Entry for setting either "x" or "distance". */
   @FXML
   private TextField cameraTextField1;
   /** Entry for setting either "y" or "yaw". */
   @FXML
   private TextField cameraTextField2;
   /** Entry for setting either "z" or "pitch". */
   @FXML
   private TextField cameraTextField3;
   /** Validation for either "x" or "distance". */
   @FXML
   private ImageView cameraValidImageView1;
   /** Validation for either "y" or "yaw". */
   @FXML
   private ImageView cameraValidImageView2;
   /** Validation for either "z" or "pitch". */
   @FXML
   private ImageView cameraValidImageView3;

   @FXML
   private TextField xCameraCurrentTextField, yCameraCurrentTextField, zCameraCurrentTextField;

   private Label[] cameraLabels;
   private TextField[] cameraTextFields;
   private final DoubleSearchField[] cameraCoordinatesSearchFields = new DoubleSearchField[3];

   private final List cleanupTasks = new ArrayList<>();

   public void initialize(PerspectiveCameraController cameraController,
                          YoCompositeSearchManager searchManager,
                          LinkedYoRegistry linkedRootRegistry,
                          ReferenceFrameManager referenceFrameManager)
   {
      initializeFocalPointControls(cameraController, searchManager, linkedRootRegistry, referenceFrameManager);
      initializeCameraControls(cameraController, searchManager, linkedRootRegistry);
   }

   public void initializeFocalPointControls(PerspectiveCameraController cameraController,
                                            YoCompositeSearchManager searchManager,
                                            LinkedYoRegistry linkedRootRegistry,
                                            ReferenceFrameManager referenceFrameManager)
   {
      PerspectiveCamera camera = cameraController.getCamera();
      CameraFocalPointHandler focalPointHandler = cameraController.getFocalPointHandler();
      ObjectProperty targetTypeProperty = focalPointHandler.targetTypeProperty();

      ToggleGroup toggleGroup = new ToggleGroup();
      toggleGroup.getToggles().addAll(trackCoordinatesButton, trackNodeButton); // TODO initialize which one is selected

      trackNodeButton.setSelected(targetTypeProperty.get() == TrackingTargetType.Node);
      trackCoordinatesButton.setSelected(targetTypeProperty.get() == TrackingTargetType.YoCoordinates);

      MutableBoolean updatingTarget = new MutableBoolean(false);

      toggleGroup.selectedToggleProperty().addListener((o, oldValue, newValue) ->
      {
         if (updatingTarget.booleanValue())
            return;
         updatingTarget.setTrue();

         if (newValue == trackNodeButton)
            targetTypeProperty.set(TrackingTargetType.Node);
         else if (newValue == trackCoordinatesButton)
            targetTypeProperty.set(TrackingTargetType.YoCoordinates);
         else
            targetTypeProperty.set(TrackingTargetType.Disabled);

         updatingTarget.setFalse();
      });

      ChangeListener targetTypeChangeListener = (o, oldValue, newValue) ->
      {
         if (updatingTarget.booleanValue())
            return;
         updatingTarget.setTrue();

         trackNodeButton.setSelected(targetTypeProperty.get() == TrackingTargetType.Node);
         trackCoordinatesButton.setSelected(targetTypeProperty.get() == TrackingTargetType.YoCoordinates);

         updatingTarget.setFalse();
      };
      targetTypeProperty.addListener(targetTypeChangeListener);
      cleanupTasks.add(() -> targetTypeProperty.removeListener(targetTypeChangeListener));
      targetTypeChangeListener.changed(targetTypeProperty, null, targetTypeProperty.get());

      ChangeListener focalPointTransformChangeListener = (o, oldValue, newValue) ->
      {
         xFocalPointCurrentTextField.setText(Double.toString(newValue.getTx()));
         yFocalPointCurrentTextField.setText(Double.toString(newValue.getTy()));
         zFocalPointCurrentTextField.setText(Double.toString(newValue.getTz()));
      };
      Node focalPoint = cameraController.getFocalPointViz();
      focalPoint.localToSceneTransformProperty().addListener(focalPointTransformChangeListener);
      cleanupTasks.add(() -> focalPoint.localToSceneTransformProperty().removeListener(focalPointTransformChangeListener));
      focalPointTransformChangeListener.changed(focalPoint.localToSceneTransformProperty(), null, focalPoint.getLocalToSceneTransform());

      ChangeListener cameraTransformChangeListener = (o, oldValue, newValue) ->
      {
         xCameraCurrentTextField.setText(Double.toString(newValue.getTx()));
         yCameraCurrentTextField.setText(Double.toString(newValue.getTy()));
         zCameraCurrentTextField.setText(Double.toString(newValue.getTz()));
      };
      camera.localToSceneTransformProperty().addListener(cameraTransformChangeListener);
      cleanupTasks.add(() -> camera.localToSceneTransformProperty().removeListener(cameraTransformChangeListener));
      cameraTransformChangeListener.changed(camera.localToSceneTransformProperty(), null, camera.getLocalToSceneTransform());

      YoCompositeCollection yoTuple3DCollection = searchManager.getYoTuple3DCollection();
      if (yoTuple3DCollection == null)
      {
         yoCoordinateEditorController.getMainPane().setDisable(true);
         // Happens when no session is loaded
      }
      else
      {
         ObjectProperty coordinatesToTrack = focalPointHandler.coordinatesToTrackProperty();
         yoCoordinateEditorController.initialize(searchManager, referenceFrameManager, linkedRootRegistry, yoTuple3DCollection, true);
         yoCoordinateEditorController.setCompositeName("Tracking Coordinates");
         yoCoordinateEditorController.getMainPane().disableProperty().bind(trackCoordinatesButton.selectedProperty().not());
         if (coordinatesToTrack.get() != null)
            yoCoordinateEditorController.setInput(coordinatesToTrack.get());

         yoCoordinateEditorController.addInputListener((coords, frame) -> coordinatesToTrack.set(new Tuple3DProperty(frame, coords)));
      }

      trackingNodeTextField.setEditable(false); // We use this field to provide info about the currently selected Node
      trackingNodeTextField.setTooltip(new Tooltip("To select a node to track, right click on it in the 3D view and select in the context menu."));
      trackingNodeTextField.disableProperty().bind(trackNodeButton.selectedProperty().not());

      ObjectProperty nodeToTrack = focalPointHandler.nodeToTrackProperty();
      trackingNodeTextField.setText(nodeToTrack.get() == null ? "null" : nodeToTrack.get().getId());

      ChangeListener nodeTrackedChangeListener = (o, oldValue, newValue) ->
      {
         if (newValue == null)
            trackingNodeTextField.setText("No node being tracked");
         else if (newValue.getId() == null || newValue.getId().isBlank())
            trackingNodeTextField.setText("Tracking node w/o id");
         else
            trackingNodeTextField.setText(newValue.getId());
      };
      nodeToTrack.addListener(nodeTrackedChangeListener);
      cleanupTasks.add(() -> nodeToTrack.removeListener(nodeTrackedChangeListener));
      nodeTrackedChangeListener.changed(nodeToTrack, null, nodeToTrack.get());
   }

   @SuppressWarnings({"rawtypes", "unchecked"})
   public void initializeCameraControls(PerspectiveCameraController cameraController,
                                        YoCompositeSearchManager searchManager,
                                        LinkedYoRegistry linkedRootRegistry)
   {
      ReadOnlyObjectProperty currentCameraPose = cameraController.getCamera().localToSceneTransformProperty();
      CameraOrbitHandler orbitHandler = cameraController.getOrbitHandler();
      Property cameraPositionCoordinatesToTrack = cameraController.cameraPositionCoordinatesToTrackProperty();
      Property cameraOrbitalCoordinatesToTrack = cameraController.cameraOrbitalCoordinatesToTrackProperty();
      Property cameraLevelOrbitalCoordinatesToTrack = cameraController.cameraLevelOrbitalCoordinatesToTrackProperty();

      cameraLabels = new Label[] {cameraLabel1, cameraLabel2, cameraLabel3};
      cameraTextFields = new TextField[] {cameraTextField1, cameraTextField2, cameraTextField3};

      cameraPositionComboxBox.setItems(FXCollections.observableArrayList(CameraControlMode.values()));
      cameraCoordinatesSearchFields[0] = new DoubleSearchField(cameraTextField1, searchManager, linkedRootRegistry, cameraValidImageView1);
      cameraCoordinatesSearchFields[1] = new DoubleSearchField(cameraTextField2, searchManager, linkedRootRegistry, cameraValidImageView2);
      cameraCoordinatesSearchFields[2] = new DoubleSearchField(cameraTextField3, searchManager, linkedRootRegistry, cameraValidImageView3);

      MutableBoolean updatingCameraTextFields = new MutableBoolean(false);

      for (int i = 0; i < cameraCoordinatesSearchFields.length; i++)
      {
         cameraCoordinatesSearchFields[i].setupAutoCompletion();

         int supplierIndex = i;
         cameraCoordinatesSearchFields[i].supplierProperty().addListener((o, oldValue, newValue) ->
         {
            if (updatingCameraTextFields.booleanValue())
               return;

            CameraControlMode controlMode = cameraController.cameraControlMode().getValue();
            CompositeProperty composite = switch (controlMode)
            {
               case Position -> cameraPositionCoordinatesToTrack.getValue();
               case Orbital -> cameraOrbitalCoordinatesToTrack.getValue();
               case LevelOrbital -> cameraLevelOrbitalCoordinatesToTrack.getValue();
            };
            DoubleProperty[] components = Arrays.copyOf(composite.componentValueProperties(), 3);
            components[supplierIndex] = newValue;

            switch (controlMode)
            {
               case Position:
                  cameraPositionCoordinatesToTrack.setValue(new Tuple3DProperty(components));
                  break;
               case Orbital:
                  cameraOrbitalCoordinatesToTrack.setValue(new OrbitalCoordinateProperty(components));
                  break;
               case LevelOrbital:
                  cameraLevelOrbitalCoordinatesToTrack.setValue(new LevelOrbitalCoordinateProperty(components));
                  break;
            }
         });
      }

      cameraPositionComboxBox.getSelectionModel().selectedItemProperty().addListener((o, oldValue, newValue) ->
      {
         cameraController.cameraControlMode().setValue(newValue);

         CompositeProperty composite = switch (newValue)
         {
            case Position:
               if (cameraPositionCoordinatesToTrack.getValue() == null)
                  cameraPositionCoordinatesToTrack.setValue(new Tuple3DProperty(currentCameraPose.get().getTx(),
                                                                                currentCameraPose.get().getTy(),
                                                                                currentCameraPose.get().getTz()));
               yield cameraPositionCoordinatesToTrack.getValue();
            case Orbital:
               if (cameraOrbitalCoordinatesToTrack.getValue() == null)
                  cameraOrbitalCoordinatesToTrack.setValue(new OrbitalCoordinateProperty(orbitHandler.distanceProperty().get(),
                                                                                         orbitHandler.longitudeProperty().get(),
                                                                                         orbitHandler.latitudeProperty().get()));
               yield cameraOrbitalCoordinatesToTrack.getValue();
            case LevelOrbital:
               if (cameraLevelOrbitalCoordinatesToTrack.getValue() == null)
                  cameraLevelOrbitalCoordinatesToTrack.setValue(new LevelOrbitalCoordinateProperty(orbitHandler.distanceProperty().get(),
                                                                                                   orbitHandler.longitudeProperty().get(),
                                                                                                   currentCameraPose.get().getTz()));
               yield cameraLevelOrbitalCoordinatesToTrack.getValue();
            default:
               throw new IllegalStateException("Unexpected type: " + newValue);
         };

         updatingCameraTextFields.setTrue();
         for (int i = 0; i < 3; i++)
         {
            cameraLabels[i].setText(composite.getComponentIdentifiers()[i]);
            cameraTextFields[i].setText(CompositePropertyTools.toDoublePropertyName(composite.componentValueProperties()[i]));
         }
         updatingCameraTextFields.setFalse();
      });

      {
         CameraControlMode[] controlModes = {CameraControlMode.Position, CameraControlMode.Orbital, CameraControlMode.LevelOrbital};
         Property[] cameraCoordinatesProperties = {cameraPositionCoordinatesToTrack, cameraOrbitalCoordinatesToTrack, cameraLevelOrbitalCoordinatesToTrack};

         for (int i = 0; i < cameraCoordinatesProperties.length; i++)
         {
            CameraControlMode controlMode = controlModes[i];
            Property cameraCoordinatesProperty = cameraCoordinatesProperties[i];

            List> componentChangeListeners = new ArrayList<>();
            for (int componentIndex = 0; componentIndex < 3; componentIndex++)
            {
               int componentIndexFinal = componentIndex;
               componentChangeListeners.add((o, oldValue, newValue) ->
               {
                  if (cameraController.cameraControlMode().getValue() != controlMode)
                     return;
                  if (cameraTextFields[componentIndexFinal].isFocused())
                     return;
                  updatingCameraTextFields.setTrue();
                  CompositeProperty cameraCoordinates = (CompositeProperty) cameraCoordinatesProperty.getValue();
                  cameraTextFields[componentIndexFinal].setText(CompositePropertyTools.toDoublePropertyName(cameraCoordinates.componentValueProperties()[componentIndexFinal]));
                  updatingCameraTextFields.setFalse();
               });
            }

            ChangeListener cameraCoordinatesChangeListener = (o, oldValue, newValue) ->
            {
               if (oldValue != null && oldValue.componentValueProperties() != null)
               {
                  for (int componentIndex = 0; componentIndex < 3; componentIndex++)
                  {
                     DoubleProperty componentValueProperty = oldValue.componentValueProperties()[componentIndex];
                     if (componentValueProperty != null && !(componentValueProperty instanceof YoDoubleProperty))
                        componentValueProperty.removeListener(componentChangeListeners.get(componentIndex));
                  }
               }

               if (newValue != null && newValue.componentValueProperties() != null)
               {
                  for (int componentIndex = 0; componentIndex < 3; componentIndex++)
                  {
                     DoubleProperty componentValueProperty = newValue.componentValueProperties()[componentIndex];
                     if (componentValueProperty != null && !(componentValueProperty instanceof YoDoubleProperty))
                        componentValueProperty.addListener(componentChangeListeners.get(componentIndex));
                  }
               }
            };
            cameraCoordinatesProperty.addListener(cameraCoordinatesChangeListener);
            cleanupTasks.add(() ->
            {
               cameraCoordinatesProperty.removeListener(cameraCoordinatesChangeListener);
               // Remove component listeners
               cameraCoordinatesChangeListener.changed(null, (CompositeProperty) cameraCoordinatesProperty.getValue(), null);
            });
         }
      }

      cameraPositionComboxBox.getSelectionModel().select(cameraController.cameraControlMode().getValue());
   }

   public void closeAndDispose()
   {
      cleanupTasks.forEach(Runnable::run);
      cleanupTasks.clear();
   }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy