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.geometry.proto.Transform3dProto;
008import edu.wpi.first.math.geometry.struct.Transform3dStruct;
009import edu.wpi.first.util.protobuf.ProtobufSerializable;
010import edu.wpi.first.util.struct.StructSerializable;
011import java.util.Objects;
012
013/** Represents a transformation for a Pose3d in the pose's frame. */
014public class Transform3d implements ProtobufSerializable, StructSerializable {
015  /**
016   * A preallocated Transform3d representing no transformation.
017   *
018   * <p>This exists to avoid allocations for common transformations.
019   */
020  public static final Transform3d kZero = new Transform3d();
021
022  private final Translation3d m_translation;
023  private final Rotation3d m_rotation;
024
025  /**
026   * Constructs the transform that maps the initial pose to the final pose.
027   *
028   * @param initial The initial pose for the transformation.
029   * @param last The final pose for the transformation.
030   */
031  public Transform3d(Pose3d initial, Pose3d last) {
032    // We are rotating the difference between the translations
033    // using a clockwise rotation matrix. This transforms the global
034    // delta into a local delta (relative to the initial pose).
035    m_translation =
036        last.getTranslation()
037            .minus(initial.getTranslation())
038            .rotateBy(initial.getRotation().unaryMinus());
039
040    m_rotation = last.getRotation().minus(initial.getRotation());
041  }
042
043  /**
044   * Constructs a transform with the given translation and rotation components.
045   *
046   * @param translation Translational component of the transform.
047   * @param rotation Rotational component of the transform.
048   */
049  public Transform3d(Translation3d translation, Rotation3d rotation) {
050    m_translation = translation;
051    m_rotation = rotation;
052  }
053
054  /**
055   * Constructs a transform with x, y, and z translations instead of a separate Translation3d.
056   *
057   * @param x The x component of the translational component of the transform.
058   * @param y The y component of the translational component of the transform.
059   * @param z The z component of the translational component of the transform.
060   * @param rotation The rotational component of the transform.
061   */
062  public Transform3d(double x, double y, double z, Rotation3d rotation) {
063    m_translation = new Translation3d(x, y, z);
064    m_rotation = rotation;
065  }
066
067  /** Constructs the identity transform -- maps an initial pose to itself. */
068  public Transform3d() {
069    m_translation = Translation3d.kZero;
070    m_rotation = Rotation3d.kZero;
071  }
072
073  /**
074   * Multiplies the transform by the scalar.
075   *
076   * @param scalar The scalar.
077   * @return The scaled Transform3d.
078   */
079  public Transform3d times(double scalar) {
080    return new Transform3d(m_translation.times(scalar), m_rotation.times(scalar));
081  }
082
083  /**
084   * Divides the transform by the scalar.
085   *
086   * @param scalar The scalar.
087   * @return The scaled Transform3d.
088   */
089  public Transform3d div(double scalar) {
090    return times(1.0 / scalar);
091  }
092
093  /**
094   * Composes two transformations. The second transform is applied relative to the orientation of
095   * the first.
096   *
097   * @param other The transform to compose with this one.
098   * @return The composition of the two transformations.
099   */
100  public Transform3d plus(Transform3d other) {
101    return new Transform3d(Pose3d.kZero, Pose3d.kZero.transformBy(this).transformBy(other));
102  }
103
104  /**
105   * Returns the translation component of the transformation.
106   *
107   * @return The translational component of the transform.
108   */
109  public Translation3d getTranslation() {
110    return m_translation;
111  }
112
113  /**
114   * Returns the X component of the transformation's translation.
115   *
116   * @return The x component of the transformation's translation.
117   */
118  public double getX() {
119    return m_translation.getX();
120  }
121
122  /**
123   * Returns the Y component of the transformation's translation.
124   *
125   * @return The y component of the transformation's translation.
126   */
127  public double getY() {
128    return m_translation.getY();
129  }
130
131  /**
132   * Returns the Z component of the transformation's translation.
133   *
134   * @return The z component of the transformation's translation.
135   */
136  public double getZ() {
137    return m_translation.getZ();
138  }
139
140  /**
141   * Returns the rotational component of the transformation.
142   *
143   * @return Reference to the rotational component of the transform.
144   */
145  public Rotation3d getRotation() {
146    return m_rotation;
147  }
148
149  /**
150   * Invert the transformation. This is useful for undoing a transformation.
151   *
152   * @return The inverted transformation.
153   */
154  public Transform3d inverse() {
155    // We are rotating the difference between the translations
156    // using a clockwise rotation matrix. This transforms the global
157    // delta into a local delta (relative to the initial pose).
158    return new Transform3d(
159        getTranslation().unaryMinus().rotateBy(getRotation().unaryMinus()),
160        getRotation().unaryMinus());
161  }
162
163  @Override
164  public String toString() {
165    return String.format("Transform3d(%s, %s)", m_translation, m_rotation);
166  }
167
168  /**
169   * Checks equality between this Transform3d and another object.
170   *
171   * @param obj The other object.
172   * @return Whether the two objects are equal or not.
173   */
174  @Override
175  public boolean equals(Object obj) {
176    if (obj instanceof Transform3d) {
177      return ((Transform3d) obj).m_translation.equals(m_translation)
178          && ((Transform3d) obj).m_rotation.equals(m_rotation);
179    }
180    return false;
181  }
182
183  @Override
184  public int hashCode() {
185    return Objects.hash(m_translation, m_rotation);
186  }
187
188  /** Transform3d protobuf for serialization. */
189  public static final Transform3dProto proto = new Transform3dProto();
190
191  /** Transform3d struct for serialization. */
192  public static final Transform3dStruct struct = new Transform3dStruct();
193}