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 org.wpilib.math.geometry; 006 007import static org.wpilib.units.Units.Meters; 008 009import java.util.Objects; 010import org.wpilib.math.geometry.proto.Transform3dProto; 011import org.wpilib.math.geometry.struct.Transform3dStruct; 012import org.wpilib.math.jni.Transform3dJNI; 013import org.wpilib.math.linalg.MatBuilder; 014import org.wpilib.math.linalg.Matrix; 015import org.wpilib.math.numbers.N4; 016import org.wpilib.math.util.Nat; 017import org.wpilib.units.measure.Distance; 018import org.wpilib.util.protobuf.ProtobufSerializable; 019import org.wpilib.util.struct.StructSerializable; 020 021/** 022 * Represents a transformation for a Pose3d in the pose's frame. Translation is applied before 023 * rotation. (The translation is applied in the pose's original frame, not the transformed frame.) 024 */ 025public class Transform3d implements ProtobufSerializable, StructSerializable { 026 /** 027 * A preallocated Transform3d representing no transformation. 028 * 029 * <p>This exists to avoid allocations for common transformations. 030 */ 031 public static final Transform3d kZero = new Transform3d(); 032 033 private final Translation3d m_translation; 034 private final Rotation3d m_rotation; 035 036 /** 037 * Constructs the transform that maps the initial pose to the final pose. 038 * 039 * @param initial The initial pose for the transformation. 040 * @param last The final pose for the transformation. 041 */ 042 public Transform3d(Pose3d initial, Pose3d last) { 043 // To transform the global translation delta to be relative to the initial 044 // pose, rotate by the inverse of the initial pose's orientation. 045 m_translation = 046 last.getTranslation() 047 .minus(initial.getTranslation()) 048 .rotateBy(initial.getRotation().inverse()); 049 050 m_rotation = last.getRotation().relativeTo(initial.getRotation()); 051 } 052 053 /** 054 * Constructs a transform with the given translation and rotation components. 055 * 056 * @param translation Translational component of the transform. 057 * @param rotation Rotational component of the transform. 058 */ 059 public Transform3d(Translation3d translation, Rotation3d rotation) { 060 m_translation = translation; 061 m_rotation = rotation; 062 } 063 064 /** 065 * Constructs a transform with x, y, and z translations instead of a separate Translation3d. 066 * 067 * @param x The x component of the translational component of the transform. 068 * @param y The y component of the translational component of the transform. 069 * @param z The z component of the translational component of the transform. 070 * @param rotation The rotational component of the transform. 071 */ 072 public Transform3d(double x, double y, double z, Rotation3d rotation) { 073 m_translation = new Translation3d(x, y, z); 074 m_rotation = rotation; 075 } 076 077 /** 078 * Constructs a transform with x, y, and z translations instead of a separate Translation3d. The 079 * X, Y, and Z translations will be converted to and tracked as meters. 080 * 081 * @param x The x component of the translational component of the transform. 082 * @param y The y component of the translational component of the transform. 083 * @param z The z component of the translational component of the transform. 084 * @param rotation The rotational component of the transform. 085 */ 086 public Transform3d(Distance x, Distance y, Distance z, Rotation3d rotation) { 087 this(x.in(Meters), y.in(Meters), z.in(Meters), rotation); 088 } 089 090 /** 091 * Constructs a transform with the specified affine transformation matrix. 092 * 093 * @param matrix The affine transformation matrix. 094 * @throws IllegalArgumentException if the affine transformation matrix is invalid. 095 */ 096 public Transform3d(Matrix<N4, N4> matrix) { 097 m_translation = new Translation3d(matrix.get(0, 3), matrix.get(1, 3), matrix.get(2, 3)); 098 m_rotation = new Rotation3d(matrix.block(3, 3, 0, 0)); 099 if (matrix.get(3, 0) != 0.0 100 || matrix.get(3, 1) != 0.0 101 || matrix.get(3, 2) != 0.0 102 || matrix.get(3, 3) != 1.0) { 103 throw new IllegalArgumentException("Affine transformation matrix is invalid"); 104 } 105 } 106 107 /** Constructs the identity transform -- maps an initial pose to itself. */ 108 public Transform3d() { 109 m_translation = Translation3d.kZero; 110 m_rotation = Rotation3d.kZero; 111 } 112 113 /** 114 * Constructs a 3D transform from a 2D transform in the X-Y plane. 115 * 116 * @param transform The 2D transform. 117 * @see Rotation3d#Rotation3d(Rotation2d) 118 * @see Translation3d#Translation3d(Translation2d) 119 */ 120 public Transform3d(Transform2d transform) { 121 m_translation = new Translation3d(transform.getTranslation()); 122 m_rotation = new Rotation3d(transform.getRotation()); 123 } 124 125 /** 126 * Multiplies the transform by the scalar. 127 * 128 * @param scalar The scalar. 129 * @return The scaled Transform3d. 130 */ 131 public Transform3d times(double scalar) { 132 return new Transform3d(m_translation.times(scalar), m_rotation.times(scalar)); 133 } 134 135 /** 136 * Divides the transform by the scalar. 137 * 138 * @param scalar The scalar. 139 * @return The scaled Transform3d. 140 */ 141 public Transform3d div(double scalar) { 142 return times(1.0 / scalar); 143 } 144 145 /** 146 * Composes two transformations. The second transform is applied relative to the orientation of 147 * the first. 148 * 149 * @param other The transform to compose with this one. 150 * @return The composition of the two transformations. 151 */ 152 public Transform3d plus(Transform3d other) { 153 return new Transform3d(Pose3d.kZero, Pose3d.kZero.transformBy(this).transformBy(other)); 154 } 155 156 /** 157 * Returns the translation component of the transformation. 158 * 159 * @return The translational component of the transform. 160 */ 161 public Translation3d getTranslation() { 162 return m_translation; 163 } 164 165 /** 166 * Returns the X component of the transformation's translation. 167 * 168 * @return The x component of the transformation's translation. 169 */ 170 public double getX() { 171 return m_translation.getX(); 172 } 173 174 /** 175 * Returns the Y component of the transformation's translation. 176 * 177 * @return The y component of the transformation's translation. 178 */ 179 public double getY() { 180 return m_translation.getY(); 181 } 182 183 /** 184 * Returns the Z component of the transformation's translation. 185 * 186 * @return The z component of the transformation's translation. 187 */ 188 public double getZ() { 189 return m_translation.getZ(); 190 } 191 192 /** 193 * Returns the X component of the transformation's translation in a measure. 194 * 195 * @return The x component of the transformation's translation in a measure. 196 */ 197 public Distance getMeasureX() { 198 return m_translation.getMeasureX(); 199 } 200 201 /** 202 * Returns the Y component of the transformation's translation in a measure. 203 * 204 * @return The y component of the transformation's translation in a measure. 205 */ 206 public Distance getMeasureY() { 207 return m_translation.getMeasureY(); 208 } 209 210 /** 211 * Returns the Z component of the transformation's translation in a measure. 212 * 213 * @return The z component of the transformation's translation in a measure. 214 */ 215 public Distance getMeasureZ() { 216 return m_translation.getMeasureZ(); 217 } 218 219 /** 220 * Returns an affine transformation matrix representation of this transformation. 221 * 222 * @return An affine transformation matrix representation of this transformation. 223 */ 224 public Matrix<N4, N4> toMatrix() { 225 var vec = m_translation.toVector(); 226 var mat = m_rotation.toMatrix(); 227 return MatBuilder.fill( 228 Nat.N4(), 229 Nat.N4(), 230 mat.get(0, 0), 231 mat.get(0, 1), 232 mat.get(0, 2), 233 vec.get(0), 234 mat.get(1, 0), 235 mat.get(1, 1), 236 mat.get(1, 2), 237 vec.get(1), 238 mat.get(2, 0), 239 mat.get(2, 1), 240 mat.get(2, 2), 241 vec.get(2), 242 0.0, 243 0.0, 244 0.0, 245 1.0); 246 } 247 248 /** 249 * Returns the rotational component of the transformation. 250 * 251 * @return Reference to the rotational component of the transform. 252 */ 253 public Rotation3d getRotation() { 254 return m_rotation; 255 } 256 257 /** 258 * Returns a Twist3d of the current transform (pose delta). If b is the output of {@code a.log()}, 259 * then {@code b.exp()} would yield a. 260 * 261 * @return The twist that maps the current transform. 262 */ 263 public Twist3d log() { 264 var thisQuaternion = m_rotation.getQuaternion(); 265 double[] resultArray = 266 Transform3dJNI.log( 267 this.getX(), 268 this.getY(), 269 this.getZ(), 270 thisQuaternion.getW(), 271 thisQuaternion.getX(), 272 thisQuaternion.getY(), 273 thisQuaternion.getZ()); 274 return new Twist3d( 275 resultArray[0], 276 resultArray[1], 277 resultArray[2], 278 resultArray[3], 279 resultArray[4], 280 resultArray[5]); 281 } 282 283 /** 284 * Invert the transformation. This is useful for undoing a transformation. 285 * 286 * @return The inverted transformation. 287 */ 288 public Transform3d inverse() { 289 // We are rotating the difference between the translations 290 // using a clockwise rotation matrix. This transforms the global 291 // delta into a local delta (relative to the initial pose). 292 return new Transform3d( 293 getTranslation().unaryMinus().rotateBy(getRotation().inverse()), getRotation().inverse()); 294 } 295 296 @Override 297 public String toString() { 298 return String.format("Transform3d(%s, %s)", m_translation, m_rotation); 299 } 300 301 /** 302 * Checks equality between this Transform3d and another object. 303 * 304 * @param obj The other object. 305 * @return Whether the two objects are equal or not. 306 */ 307 @Override 308 public boolean equals(Object obj) { 309 return obj instanceof Transform3d other 310 && other.m_translation.equals(m_translation) 311 && other.m_rotation.equals(m_rotation); 312 } 313 314 @Override 315 public int hashCode() { 316 return Objects.hash(m_translation, m_rotation); 317 } 318 319 /** Transform3d protobuf for serialization. */ 320 public static final Transform3dProto proto = new Transform3dProto(); 321 322 /** Transform3d struct for serialization. */ 323 public static final Transform3dStruct struct = new Transform3dStruct(); 324}