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 static edu.wpi.first.units.Units.Meters;
008
009import com.fasterxml.jackson.annotation.JsonAutoDetect;
010import com.fasterxml.jackson.annotation.JsonCreator;
011import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
012import com.fasterxml.jackson.annotation.JsonProperty;
013import edu.wpi.first.math.MathUtil;
014import edu.wpi.first.math.VecBuilder;
015import edu.wpi.first.math.Vector;
016import edu.wpi.first.math.geometry.proto.Translation3dProto;
017import edu.wpi.first.math.geometry.struct.Translation3dStruct;
018import edu.wpi.first.math.interpolation.Interpolatable;
019import edu.wpi.first.math.numbers.N3;
020import edu.wpi.first.units.measure.Distance;
021import edu.wpi.first.util.protobuf.ProtobufSerializable;
022import edu.wpi.first.util.struct.StructSerializable;
023import java.util.Collection;
024import java.util.Collections;
025import java.util.Comparator;
026import java.util.Objects;
027
028/**
029 * Represents a translation in 3D space. This object can be used to represent a point or a vector.
030 *
031 * <p>This assumes that you are using conventional mathematical axes. When the robot is at the
032 * origin facing in the positive X direction, forward is positive X, left is positive Y, and up is
033 * positive Z.
034 */
035@JsonIgnoreProperties(ignoreUnknown = true)
036@JsonAutoDetect(getterVisibility = JsonAutoDetect.Visibility.NONE)
037public class Translation3d
038    implements Interpolatable<Translation3d>, ProtobufSerializable, StructSerializable {
039  /**
040   * A preallocated Translation3d representing the origin.
041   *
042   * <p>This exists to avoid allocations for common translations.
043   */
044  public static final Translation3d kZero = new Translation3d();
045
046  private final double m_x;
047  private final double m_y;
048  private final double m_z;
049
050  /** Constructs a Translation3d with X, Y, and Z components equal to zero. */
051  public Translation3d() {
052    this(0.0, 0.0, 0.0);
053  }
054
055  /**
056   * Constructs a Translation3d with the X, Y, and Z components equal to the provided values.
057   *
058   * @param x The x component of the translation.
059   * @param y The y component of the translation.
060   * @param z The z component of the translation.
061   */
062  @JsonCreator
063  public Translation3d(
064      @JsonProperty(required = true, value = "x") double x,
065      @JsonProperty(required = true, value = "y") double y,
066      @JsonProperty(required = true, value = "z") double z) {
067    m_x = x;
068    m_y = y;
069    m_z = z;
070  }
071
072  /**
073   * Constructs a Translation3d with the provided distance and angle. This is essentially converting
074   * from polar coordinates to Cartesian coordinates.
075   *
076   * @param distance The distance from the origin to the end of the translation.
077   * @param angle The angle between the x-axis and the translation vector.
078   */
079  public Translation3d(double distance, Rotation3d angle) {
080    final var rectangular = new Translation3d(distance, 0.0, 0.0).rotateBy(angle);
081    m_x = rectangular.getX();
082    m_y = rectangular.getY();
083    m_z = rectangular.getZ();
084  }
085
086  /**
087   * Constructs a Translation3d with the X, Y, and Z components equal to the provided values. The
088   * components will be converted to and tracked as meters.
089   *
090   * @param x The x component of the translation.
091   * @param y The y component of the translation.
092   * @param z The z component of the translation.
093   */
094  public Translation3d(Distance x, Distance y, Distance z) {
095    this(x.in(Meters), y.in(Meters), z.in(Meters));
096  }
097
098  /**
099   * Constructs a 3D translation from a 2D translation in the X-Y plane.
100   *
101   * @param translation The 2D translation.
102   * @see Pose3d#Pose3d(Pose2d)
103   * @see Transform3d#Transform3d(Transform2d)
104   */
105  public Translation3d(Translation2d translation) {
106    this(translation.getX(), translation.getY(), 0.0);
107  }
108
109  /**
110   * Constructs a Translation3d from a 3D translation vector. The values are assumed to be in
111   * meters.
112   *
113   * @param vector The translation vector.
114   */
115  public Translation3d(Vector<N3> vector) {
116    this(vector.get(0), vector.get(1), vector.get(2));
117  }
118
119  /**
120   * Calculates the distance between two translations in 3D space.
121   *
122   * <p>The distance between translations is defined as √((x₂−x₁)²+(y₂−y₁)²+(z₂−z₁)²).
123   *
124   * @param other The translation to compute the distance to.
125   * @return The distance between the two translations.
126   */
127  public double getDistance(Translation3d other) {
128    double dx = other.m_x - m_x;
129    double dy = other.m_y - m_y;
130    double dz = other.m_z - m_z;
131    return Math.sqrt(dx * dx + dy * dy + dz * dz);
132  }
133
134  /**
135   * Calculates the squared distance between two translations in 3D space. This is equivalent to
136   * squaring the result of {@link #getDistance(Translation3d)}, but avoids computing a square root.
137   *
138   * <p>The squared distance between translations is defined as (x₂−x₁)²+(y₂−y₁)²+(z₂−z₁)².
139   *
140   * @param other The translation to compute the squared distance to.
141   * @return The squared distance between the two translations.
142   */
143  public double getSquaredDistance(Translation3d other) {
144    double dx = other.m_x - m_x;
145    double dy = other.m_y - m_y;
146    double dz = other.m_z - m_z;
147    return dx * dx + dy * dy + dz * dz;
148  }
149
150  /**
151   * Returns the X component of the translation.
152   *
153   * @return The X component of the translation.
154   */
155  @JsonProperty
156  public double getX() {
157    return m_x;
158  }
159
160  /**
161   * Returns the Y component of the translation.
162   *
163   * @return The Y component of the translation.
164   */
165  @JsonProperty
166  public double getY() {
167    return m_y;
168  }
169
170  /**
171   * Returns the Z component of the translation.
172   *
173   * @return The Z component of the translation.
174   */
175  @JsonProperty
176  public double getZ() {
177    return m_z;
178  }
179
180  /**
181   * Returns the X component of the translation in a measure.
182   *
183   * @return The x component of the translation in a measure.
184   */
185  public Distance getMeasureX() {
186    return Meters.of(m_x);
187  }
188
189  /**
190   * Returns the Y component of the translation in a measure.
191   *
192   * @return The y component of the translation in a measure.
193   */
194  public Distance getMeasureY() {
195    return Meters.of(m_y);
196  }
197
198  /**
199   * Returns the Z component of the translation in a measure.
200   *
201   * @return The z component of the translation in a measure.
202   */
203  public Distance getMeasureZ() {
204    return Meters.of(m_z);
205  }
206
207  /**
208   * Returns a 2D translation vector representation of this translation.
209   *
210   * @return A 2D translation vector representation of this translation.
211   */
212  public Vector<N3> toVector() {
213    return VecBuilder.fill(m_x, m_y, m_z);
214  }
215
216  /**
217   * Returns the norm, or distance from the origin to the translation.
218   *
219   * @return The norm of the translation.
220   */
221  public double getNorm() {
222    return Math.sqrt(m_x * m_x + m_y * m_y + m_z * m_z);
223  }
224
225  /**
226   * Returns the squared norm, or squared distance from the origin to the translation. This is
227   * equivalent to squaring the result of {@link #getNorm()}, but avoids computing a square root.
228   *
229   * @return The squared norm of the translation.
230   */
231  public double getSquaredNorm() {
232    return m_x * m_x + m_y * m_y + m_z * m_z;
233  }
234
235  /**
236   * Applies a rotation to the translation in 3D space.
237   *
238   * <p>For example, rotating a Translation3d of &lt;2, 0, 0&gt; by 90 degrees around the Z axis
239   * will return a Translation3d of &lt;0, 2, 0&gt;.
240   *
241   * @param other The rotation to rotate the translation by.
242   * @return The new rotated translation.
243   */
244  public Translation3d rotateBy(Rotation3d other) {
245    final var p = new Quaternion(0.0, m_x, m_y, m_z);
246    final var qprime = other.getQuaternion().times(p).times(other.getQuaternion().inverse());
247    return new Translation3d(qprime.getX(), qprime.getY(), qprime.getZ());
248  }
249
250  /**
251   * Rotates this translation around another translation in 3D space.
252   *
253   * @param other The other translation to rotate around.
254   * @param rot The rotation to rotate the translation by.
255   * @return The new rotated translation.
256   */
257  public Translation3d rotateAround(Translation3d other, Rotation3d rot) {
258    return this.minus(other).rotateBy(rot).plus(other);
259  }
260
261  /**
262   * Computes the dot product between this translation and another translation in 3D space.
263   *
264   * <p>The dot product between two translations is defined as x₁x₂+y₁y₂+z₁z₂.
265   *
266   * @param other The translation to compute the dot product with.
267   * @return The dot product between the two translations, in square meters.
268   */
269  public double dot(Translation3d other) {
270    return m_x * other.m_x + m_y * other.m_y + m_z * other.m_z;
271  }
272
273  /**
274   * Computes the cross product between this translation and another translation in 3D space. The
275   * resulting translation will be perpendicular to both translations.
276   *
277   * <p>The 3D cross product between two translations is defined as &lt;y₁z₂-y₂z₁, z₁x₂-z₂x₁,
278   * x₁y₂-x₂y₁&gt;.
279   *
280   * @param other The translation to compute the cross product with.
281   * @return The cross product between the two translations.
282   */
283  public Vector<N3> cross(Translation3d other) {
284    return VecBuilder.fill(
285        m_y * other.m_z - other.m_y * m_z,
286        m_z * other.m_x - other.m_z * m_x,
287        m_x * other.m_y - other.m_x * m_y);
288  }
289
290  /**
291   * Returns a Translation2d representing this Translation3d projected into the X-Y plane.
292   *
293   * @return A Translation2d representing this Translation3d projected into the X-Y plane.
294   */
295  public Translation2d toTranslation2d() {
296    return new Translation2d(m_x, m_y);
297  }
298
299  /**
300   * Returns the sum of two translations in 3D space.
301   *
302   * <p>For example, Translation3d(1.0, 2.5, 3.5) + Translation3d(2.0, 5.5, 7.5) =
303   * Translation3d{3.0, 8.0, 11.0).
304   *
305   * @param other The translation to add.
306   * @return The sum of the translations.
307   */
308  public Translation3d plus(Translation3d other) {
309    return new Translation3d(m_x + other.m_x, m_y + other.m_y, m_z + other.m_z);
310  }
311
312  /**
313   * Returns the difference between two translations.
314   *
315   * <p>For example, Translation3d(5.0, 4.0, 3.0) - Translation3d(1.0, 2.0, 3.0) =
316   * Translation3d(4.0, 2.0, 0.0).
317   *
318   * @param other The translation to subtract.
319   * @return The difference between the two translations.
320   */
321  public Translation3d minus(Translation3d other) {
322    return new Translation3d(m_x - other.m_x, m_y - other.m_y, m_z - other.m_z);
323  }
324
325  /**
326   * Returns the inverse of the current translation. This is equivalent to negating all components
327   * of the translation.
328   *
329   * @return The inverse of the current translation.
330   */
331  public Translation3d unaryMinus() {
332    return new Translation3d(-m_x, -m_y, -m_z);
333  }
334
335  /**
336   * Returns the translation multiplied by a scalar.
337   *
338   * <p>For example, Translation3d(2.0, 2.5, 4.5) * 2 = Translation3d(4.0, 5.0, 9.0).
339   *
340   * @param scalar The scalar to multiply by.
341   * @return The scaled translation.
342   */
343  public Translation3d times(double scalar) {
344    return new Translation3d(m_x * scalar, m_y * scalar, m_z * scalar);
345  }
346
347  /**
348   * Returns the translation divided by a scalar.
349   *
350   * <p>For example, Translation3d(2.0, 2.5, 4.5) / 2 = Translation3d(1.0, 1.25, 2.25).
351   *
352   * @param scalar The scalar to multiply by.
353   * @return The reference to the new mutated object.
354   */
355  public Translation3d div(double scalar) {
356    return new Translation3d(m_x / scalar, m_y / scalar, m_z / scalar);
357  }
358
359  /**
360   * Returns the nearest Translation3d from a collection of translations.
361   *
362   * @param translations The collection of translations to find the nearest.
363   * @return The nearest Translation3d from the collection.
364   */
365  public Translation3d nearest(Collection<Translation3d> translations) {
366    return Collections.min(translations, Comparator.comparing(this::getDistance));
367  }
368
369  @Override
370  public String toString() {
371    return String.format("Translation3d(X: %.2f, Y: %.2f, Z: %.2f)", m_x, m_y, m_z);
372  }
373
374  /**
375   * Checks equality between this Translation3d and another object.
376   *
377   * @param obj The other object.
378   * @return Whether the two objects are equal or not.
379   */
380  @Override
381  public boolean equals(Object obj) {
382    return obj instanceof Translation3d other
383        && Math.abs(other.m_x - m_x) < 1E-9
384        && Math.abs(other.m_y - m_y) < 1E-9
385        && Math.abs(other.m_z - m_z) < 1E-9;
386  }
387
388  @Override
389  public int hashCode() {
390    return Objects.hash(m_x, m_y, m_z);
391  }
392
393  @Override
394  public Translation3d interpolate(Translation3d endValue, double t) {
395    return new Translation3d(
396        MathUtil.lerp(this.getX(), endValue.getX(), t),
397        MathUtil.lerp(this.getY(), endValue.getY(), t),
398        MathUtil.lerp(this.getZ(), endValue.getZ(), t));
399  }
400
401  /** Translation3d protobuf for serialization. */
402  public static final Translation3dProto proto = new Translation3dProto();
403
404  /** Translation3d struct for serialization. */
405  public static final Translation3dStruct struct = new Translation3dStruct();
406}