001// Copyright (c) FIRST and other WPILib contributors.
002// Open Source Software; you can modify and/or share it under the terms of
003// the WPILib BSD license file in the root directory of this project.
004
005package edu.wpi.first.math.geometry;
006
007import edu.wpi.first.math.Matrix;
008import edu.wpi.first.math.Nat;
009
010/** A helper class that converts Pose3d objects between different standard coordinate frames. */
011public class CoordinateSystem {
012  private static final CoordinateSystem m_nwu =
013      new CoordinateSystem(CoordinateAxis.N(), CoordinateAxis.W(), CoordinateAxis.U());
014  private static final CoordinateSystem m_edn =
015      new CoordinateSystem(CoordinateAxis.E(), CoordinateAxis.D(), CoordinateAxis.N());
016  private static final CoordinateSystem m_ned =
017      new CoordinateSystem(CoordinateAxis.N(), CoordinateAxis.E(), CoordinateAxis.D());
018
019  // Rotation from this coordinate system to NWU coordinate system
020  private final Rotation3d m_rotation;
021
022  /**
023   * Constructs a coordinate system with the given cardinal directions for each axis.
024   *
025   * @param positiveX The cardinal direction of the positive x-axis.
026   * @param positiveY The cardinal direction of the positive y-axis.
027   * @param positiveZ The cardinal direction of the positive z-axis.
028   * @throws IllegalArgumentException if the coordinate system isn't special orthogonal
029   */
030  public CoordinateSystem(
031      CoordinateAxis positiveX, CoordinateAxis positiveY, CoordinateAxis positiveZ) {
032    // Construct a change of basis matrix from the source coordinate system to the
033    // NWU coordinate system. Each column vector in the change of basis matrix is
034    // one of the old basis vectors mapped to its representation in the new basis.
035    var R = new Matrix<>(Nat.N3(), Nat.N3());
036    R.assignBlock(0, 0, positiveX.m_axis);
037    R.assignBlock(0, 1, positiveY.m_axis);
038    R.assignBlock(0, 2, positiveZ.m_axis);
039
040    // The change of basis matrix should be a pure rotation. The Rotation3d
041    // constructor will verify this by checking for special orthogonality.
042    m_rotation = new Rotation3d(R);
043  }
044
045  /**
046   * Returns an instance of the North-West-Up (NWU) coordinate system.
047   *
048   * <p>The +X axis is north, the +Y axis is west, and the +Z axis is up.
049   *
050   * @return An instance of the North-West-Up (NWU) coordinate system.
051   */
052  public static CoordinateSystem NWU() {
053    return m_nwu;
054  }
055
056  /**
057   * Returns an instance of the East-Down-North (EDN) coordinate system.
058   *
059   * <p>The +X axis is east, the +Y axis is down, and the +Z axis is north.
060   *
061   * @return An instance of the East-Down-North (EDN) coordinate system.
062   */
063  public static CoordinateSystem EDN() {
064    return m_edn;
065  }
066
067  /**
068   * Returns an instance of the North-East-Down (NED) coordinate system.
069   *
070   * <p>The +X axis is north, the +Y axis is east, and the +Z axis is down.
071   *
072   * @return An instance of the North-East-Down (NED) coordinate system.
073   */
074  public static CoordinateSystem NED() {
075    return m_ned;
076  }
077
078  /**
079   * Converts the given translation from one coordinate system to another.
080   *
081   * @param translation The translation to convert.
082   * @param from The coordinate system the pose starts in.
083   * @param to The coordinate system to which to convert.
084   * @return The given translation in the desired coordinate system.
085   */
086  public static Translation3d convert(
087      Translation3d translation, CoordinateSystem from, CoordinateSystem to) {
088    return translation.rotateBy(from.m_rotation.minus(to.m_rotation));
089  }
090
091  /**
092   * Converts the given rotation from one coordinate system to another.
093   *
094   * @param rotation The rotation to convert.
095   * @param from The coordinate system the rotation starts in.
096   * @param to The coordinate system to which to convert.
097   * @return The given rotation in the desired coordinate system.
098   */
099  public static Rotation3d convert(
100      Rotation3d rotation, CoordinateSystem from, CoordinateSystem to) {
101    return rotation.rotateBy(from.m_rotation.minus(to.m_rotation));
102  }
103
104  /**
105   * Converts the given pose from one coordinate system to another.
106   *
107   * @param pose The pose to convert.
108   * @param from The coordinate system the pose starts in.
109   * @param to The coordinate system to which to convert.
110   * @return The given pose in the desired coordinate system.
111   */
112  public static Pose3d convert(Pose3d pose, CoordinateSystem from, CoordinateSystem to) {
113    return new Pose3d(
114        convert(pose.getTranslation(), from, to), convert(pose.getRotation(), from, to));
115  }
116
117  /**
118   * Converts the given transform from one coordinate system to another.
119   *
120   * @param transform The transform to convert.
121   * @param from The coordinate system the transform starts in.
122   * @param to The coordinate system to which to convert.
123   * @return The given transform in the desired coordinate system.
124   */
125  public static Transform3d convert(
126      Transform3d transform, CoordinateSystem from, CoordinateSystem to) {
127    var coordRot = from.m_rotation.minus(to.m_rotation);
128    return new Transform3d(
129        convert(transform.getTranslation(), from, to),
130        coordRot.unaryMinus().plus(transform.getRotation().rotateBy(coordRot)));
131  }
132}