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 com.fasterxml.jackson.annotation.JsonAutoDetect;
008import com.fasterxml.jackson.annotation.JsonCreator;
009import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
010import com.fasterxml.jackson.annotation.JsonProperty;
011import edu.wpi.first.math.WPIMathJNI;
012import edu.wpi.first.math.geometry.proto.Pose3dProto;
013import edu.wpi.first.math.geometry.struct.Pose3dStruct;
014import edu.wpi.first.math.interpolation.Interpolatable;
015import java.util.Objects;
016
017/** Represents a 3D pose containing translational and rotational elements. */
018@JsonIgnoreProperties(ignoreUnknown = true)
019@JsonAutoDetect(getterVisibility = JsonAutoDetect.Visibility.NONE)
020public class Pose3d implements Interpolatable<Pose3d> {
021  private final Translation3d m_translation;
022  private final Rotation3d m_rotation;
023
024  /** Constructs a pose at the origin facing toward the positive X axis. */
025  public Pose3d() {
026    m_translation = new Translation3d();
027    m_rotation = new Rotation3d();
028  }
029
030  /**
031   * Constructs a pose with the specified translation and rotation.
032   *
033   * @param translation The translational component of the pose.
034   * @param rotation The rotational component of the pose.
035   */
036  @JsonCreator
037  public Pose3d(
038      @JsonProperty(required = true, value = "translation") Translation3d translation,
039      @JsonProperty(required = true, value = "rotation") Rotation3d rotation) {
040    m_translation = translation;
041    m_rotation = rotation;
042  }
043
044  /**
045   * Constructs a pose with x, y, and z translations instead of a separate Translation3d.
046   *
047   * @param x The x component of the translational component of the pose.
048   * @param y The y component of the translational component of the pose.
049   * @param z The z component of the translational component of the pose.
050   * @param rotation The rotational component of the pose.
051   */
052  public Pose3d(double x, double y, double z, Rotation3d rotation) {
053    m_translation = new Translation3d(x, y, z);
054    m_rotation = rotation;
055  }
056
057  /**
058   * Constructs a 3D pose from a 2D pose in the X-Y plane.
059   *
060   * @param pose The 2D pose.
061   */
062  public Pose3d(Pose2d pose) {
063    m_translation = new Translation3d(pose.getX(), pose.getY(), 0.0);
064    m_rotation = new Rotation3d(0.0, 0.0, pose.getRotation().getRadians());
065  }
066
067  /**
068   * Transforms the pose by the given transformation and returns the new transformed pose. The
069   * transform is applied relative to the pose's frame. Note that this differs from {@link
070   * Pose3d#rotateBy(Rotation3d)}, which is applied relative to the global frame and around the
071   * origin.
072   *
073   * @param other The transform to transform the pose by.
074   * @return The transformed pose.
075   */
076  public Pose3d plus(Transform3d other) {
077    return transformBy(other);
078  }
079
080  /**
081   * Returns the Transform3d that maps the one pose to another.
082   *
083   * @param other The initial pose of the transformation.
084   * @return The transform that maps the other pose to the current pose.
085   */
086  public Transform3d minus(Pose3d other) {
087    final var pose = this.relativeTo(other);
088    return new Transform3d(pose.getTranslation(), pose.getRotation());
089  }
090
091  /**
092   * Returns the translation component of the transformation.
093   *
094   * @return The translational component of the pose.
095   */
096  @JsonProperty
097  public Translation3d getTranslation() {
098    return m_translation;
099  }
100
101  /**
102   * Returns the X component of the pose's translation.
103   *
104   * @return The x component of the pose's translation.
105   */
106  public double getX() {
107    return m_translation.getX();
108  }
109
110  /**
111   * Returns the Y component of the pose's translation.
112   *
113   * @return The y component of the pose's translation.
114   */
115  public double getY() {
116    return m_translation.getY();
117  }
118
119  /**
120   * Returns the Z component of the pose's translation.
121   *
122   * @return The z component of the pose's translation.
123   */
124  public double getZ() {
125    return m_translation.getZ();
126  }
127
128  /**
129   * Returns the rotational component of the transformation.
130   *
131   * @return The rotational component of the pose.
132   */
133  @JsonProperty
134  public Rotation3d getRotation() {
135    return m_rotation;
136  }
137
138  /**
139   * Multiplies the current pose by a scalar.
140   *
141   * @param scalar The scalar.
142   * @return The new scaled Pose3d.
143   */
144  public Pose3d times(double scalar) {
145    return new Pose3d(m_translation.times(scalar), m_rotation.times(scalar));
146  }
147
148  /**
149   * Divides the current pose by a scalar.
150   *
151   * @param scalar The scalar.
152   * @return The new scaled Pose3d.
153   */
154  public Pose3d div(double scalar) {
155    return times(1.0 / scalar);
156  }
157
158  /**
159   * Rotates the pose around the origin and returns the new pose.
160   *
161   * @param other The rotation to transform the pose by, which is applied extrinsically (from the
162   *     global frame).
163   * @return The rotated pose.
164   */
165  public Pose3d rotateBy(Rotation3d other) {
166    return new Pose3d(m_translation.rotateBy(other), m_rotation.rotateBy(other));
167  }
168
169  /**
170   * Transforms the pose by the given transformation and returns the new transformed pose. The
171   * transform is applied relative to the pose's frame. Note that this differs from {@link
172   * Pose3d#rotateBy(Rotation3d)}, which is applied relative to the global frame and around the
173   * origin.
174   *
175   * @param other The transform to transform the pose by.
176   * @return The transformed pose.
177   */
178  public Pose3d transformBy(Transform3d other) {
179    return new Pose3d(
180        m_translation.plus(other.getTranslation().rotateBy(m_rotation)),
181        other.getRotation().plus(m_rotation));
182  }
183
184  /**
185   * Returns the current pose relative to the given pose.
186   *
187   * <p>This function can often be used for trajectory tracking or pose stabilization algorithms to
188   * get the error between the reference and the current pose.
189   *
190   * @param other The pose that is the origin of the new coordinate frame that the current pose will
191   *     be converted into.
192   * @return The current pose relative to the new origin pose.
193   */
194  public Pose3d relativeTo(Pose3d other) {
195    var transform = new Transform3d(other, this);
196    return new Pose3d(transform.getTranslation(), transform.getRotation());
197  }
198
199  /**
200   * Obtain a new Pose3d from a (constant curvature) velocity.
201   *
202   * <p>The twist is a change in pose in the robot's coordinate frame since the previous pose
203   * update. When the user runs exp() on the previous known field-relative pose with the argument
204   * being the twist, the user will receive the new field-relative pose.
205   *
206   * <p>"Exp" represents the pose exponential, which is solving a differential equation moving the
207   * pose forward in time.
208   *
209   * @param twist The change in pose in the robot's coordinate frame since the previous pose update.
210   *     For example, if a non-holonomic robot moves forward 0.01 meters and changes angle by 0.5
211   *     degrees since the previous pose update, the twist would be Twist3d(0.01, 0.0, 0.0, new new
212   *     Rotation3d(0.0, 0.0, Units.degreesToRadians(0.5))).
213   * @return The new pose of the robot.
214   */
215  public Pose3d exp(Twist3d twist) {
216    var quaternion = this.getRotation().getQuaternion();
217    double[] resultArray =
218        WPIMathJNI.expPose3d(
219            this.getX(),
220            this.getY(),
221            this.getZ(),
222            quaternion.getW(),
223            quaternion.getX(),
224            quaternion.getY(),
225            quaternion.getZ(),
226            twist.dx,
227            twist.dy,
228            twist.dz,
229            twist.rx,
230            twist.ry,
231            twist.rz);
232    return new Pose3d(
233        resultArray[0],
234        resultArray[1],
235        resultArray[2],
236        new Rotation3d(
237            new Quaternion(resultArray[3], resultArray[4], resultArray[5], resultArray[6])));
238  }
239
240  /**
241   * Returns a Twist3d that maps this pose to the end pose. If c is the output of {@code a.Log(b)},
242   * then {@code a.Exp(c)} would yield b.
243   *
244   * @param end The end pose for the transformation.
245   * @return The twist that maps this to end.
246   */
247  public Twist3d log(Pose3d end) {
248    var thisQuaternion = this.getRotation().getQuaternion();
249    var endQuaternion = end.getRotation().getQuaternion();
250    double[] resultArray =
251        WPIMathJNI.logPose3d(
252            this.getX(),
253            this.getY(),
254            this.getZ(),
255            thisQuaternion.getW(),
256            thisQuaternion.getX(),
257            thisQuaternion.getY(),
258            thisQuaternion.getZ(),
259            end.getX(),
260            end.getY(),
261            end.getZ(),
262            endQuaternion.getW(),
263            endQuaternion.getX(),
264            endQuaternion.getY(),
265            endQuaternion.getZ());
266    return new Twist3d(
267        resultArray[0],
268        resultArray[1],
269        resultArray[2],
270        resultArray[3],
271        resultArray[4],
272        resultArray[5]);
273  }
274
275  /**
276   * Returns a Pose2d representing this Pose3d projected into the X-Y plane.
277   *
278   * @return A Pose2d representing this Pose3d projected into the X-Y plane.
279   */
280  public Pose2d toPose2d() {
281    return new Pose2d(m_translation.toTranslation2d(), m_rotation.toRotation2d());
282  }
283
284  @Override
285  public String toString() {
286    return String.format("Pose3d(%s, %s)", m_translation, m_rotation);
287  }
288
289  /**
290   * Checks equality between this Pose3d and another object.
291   *
292   * @param obj The other object.
293   * @return Whether the two objects are equal or not.
294   */
295  @Override
296  public boolean equals(Object obj) {
297    if (obj instanceof Pose3d) {
298      return ((Pose3d) obj).m_translation.equals(m_translation)
299          && ((Pose3d) obj).m_rotation.equals(m_rotation);
300    }
301    return false;
302  }
303
304  @Override
305  public int hashCode() {
306    return Objects.hash(m_translation, m_rotation);
307  }
308
309  @Override
310  public Pose3d interpolate(Pose3d endValue, double t) {
311    if (t < 0) {
312      return this;
313    } else if (t >= 1) {
314      return endValue;
315    } else {
316      var twist = this.log(endValue);
317      var scaledTwist =
318          new Twist3d(
319              twist.dx * t, twist.dy * t, twist.dz * t, twist.rx * t, twist.ry * t, twist.rz * t);
320      return this.exp(scaledTwist);
321    }
322  }
323
324  public static final Pose3dStruct struct = new Pose3dStruct();
325  public static final Pose3dProto proto = new Pose3dProto();
326}