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 <2, 0, 0> by 90 degrees around the Z axis 239 * will return a Translation3d of <0, 2, 0>. 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 <y₁z₂-y₂z₁, z₁x₂-z₂x₁, 278 * x₁y₂-x₂y₁>. 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}