1 /* 2 * Copyright 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.xr.runtime.math 18 19 import com.google.common.truth.Truth.assertThat 20 import kotlin.math.sqrt 21 import org.junit.Test 22 import org.junit.runner.RunWith 23 import org.junit.runners.JUnit4 24 25 @RunWith(JUnit4::class) 26 class PoseTest { 27 28 @Test constructor_noArguments_returnsZeroVectorAndIdentityQuaternionnull29 fun constructor_noArguments_returnsZeroVectorAndIdentityQuaternion() { 30 val underTest = Pose() 31 32 assertThat(underTest.translation).isEqualTo(Vector3(0f, 0f, 0f)) 33 assertThat(underTest.rotation).isEqualTo(Quaternion(0f, 0f, 0f, 1f)) 34 } 35 36 @Test equals_sameValues_returnsTruenull37 fun equals_sameValues_returnsTrue() { 38 val underTest = 39 Pose(translation = Vector3(1f, 2f, 3f), rotation = Quaternion(4f, 5f, 6f, 7f)) 40 val underTest2 = 41 Pose(translation = Vector3(1f, 2f, 3f), rotation = Quaternion(4f, 5f, 6f, 7f)) 42 43 assertThat(underTest).isEqualTo(underTest2) 44 } 45 46 @Test equals_differentValues_returnsFalsenull47 fun equals_differentValues_returnsFalse() { 48 val underTest = 49 Pose(translation = Vector3(1f, 2f, 3f), rotation = Quaternion(4f, 5f, 6f, 7f)) 50 val underTest2 = 51 Pose(translation = Vector3(9f, 10f, 11f), rotation = Quaternion(4f, 5f, 6f, 7f)) 52 val underTest3 = Vector3() 53 54 assertThat(underTest).isNotEqualTo(underTest2) 55 assertThat(underTest).isNotEqualTo(underTest3) 56 } 57 58 @Test hashCodeEquals_sameValues_returnsTruenull59 fun hashCodeEquals_sameValues_returnsTrue() { 60 val underTest = 61 Pose(translation = Vector3(1f, 2f, 3f), rotation = Quaternion(4f, 5f, 6f, 7f)) 62 val underTest2 = 63 Pose(translation = Vector3(1f, 2f, 3f), rotation = Quaternion(4f, 5f, 6f, 7f)) 64 65 assertThat(underTest.hashCode()).isEqualTo(underTest2.hashCode()) 66 } 67 68 @Test hashCodeEquals_differentValues_returnsFalsenull69 fun hashCodeEquals_differentValues_returnsFalse() { 70 val underTest = 71 Pose(translation = Vector3(1f, 2f, 3f), rotation = Quaternion(4f, 5f, 6f, 7f)) 72 val underTest2 = 73 Pose(translation = Vector3(9f, 10f, 11f), rotation = Quaternion(4f, 5f, 6f, 7f)) 74 val underTest3 = Vector3() 75 76 assertThat(underTest.hashCode()).isNotEqualTo(underTest2.hashCode()) 77 assertThat(underTest.hashCode()).isNotEqualTo(underTest3.hashCode()) 78 } 79 80 @Test constructorEquals_expectedToString_returnsTruenull81 fun constructorEquals_expectedToString_returnsTrue() { 82 val underTest = 83 Pose(translation = Vector3(1f, 2f, 3f), rotation = Quaternion(4f, 5f, 6f, 7f)) 84 val underTest2 = Pose() 85 86 assertThat(underTest.toString()) 87 .isEqualTo( 88 "Pose{\n\tTranslation=[x=1.0, y=2.0, z=3.0]\n\tRotation=[x=0.35634834, y=0.4454354, z=0.5345225, w=0.6236096]\n}" 89 ) 90 assertThat(underTest2.toString()) 91 .isEqualTo( 92 "Pose{\n\tTranslation=[x=0.0, y=0.0, z=0.0]\n\tRotation=[x=0.0, y=0.0, z=0.0, w=1.0]\n}" 93 ) 94 } 95 96 @Test distance_returnsLengthOfVectorBetweenTranslationsnull97 fun distance_returnsLengthOfVectorBetweenTranslations() { 98 val underTest = Pose(translation = Vector3(0F, 3f, 4F), rotation = Quaternion()) 99 100 // (0, 3, 4) - (0, 0, 0) = (0, 3, 4) -> sqrt(0^2 + 3^2 + 4^2) = sqrt(25) 101 assertThat(Pose.distance(underTest, Pose())).isEqualTo(5F) 102 } 103 104 @Test constructor_fromPose_returnsSameValuesnull105 fun constructor_fromPose_returnsSameValues() { 106 val underTest = 107 Pose(translation = Vector3(1f, 2f, 3f), rotation = Quaternion(4f, 5f, 6f, 7f)) 108 val underTest2 = Pose(underTest) 109 110 assertThat(underTest).isEqualTo(underTest2) 111 } 112 113 @Test compose_returnsPoseWithTranslationAndRotationnull114 fun compose_returnsPoseWithTranslationAndRotation() { 115 val underTest = 116 Pose( 117 translation = Vector3(3f, 0f, 0f), 118 rotation = Quaternion(0f, sqrt(2f) / 2, 0f, sqrt(2f) / 2), 119 ) 120 val underTest2 = 121 Pose( 122 translation = Vector3(0f, 0f, -3f), 123 rotation = Quaternion(0f, sqrt(2f) / 2, 0f, sqrt(2f) / 2), 124 ) 125 126 val underTestCompose = underTest.compose(underTest2) 127 128 assertTranslation(underTestCompose.translation, 0f, 0f, 0f) 129 assertRotation(underTestCompose.rotation, 0f, 1f, 0f, 0f) 130 } 131 132 @Test translate_withZeroVector3_returnsSamePosenull133 fun translate_withZeroVector3_returnsSamePose() { 134 val underTest = Pose(translation = Vector3(1f, 2f, 3f)) 135 val translation = Vector3.Zero 136 137 val translatedPose = underTest.translate(translation) 138 139 assertTranslation(translatedPose.translation, 1f, 2f, 3f) 140 assertThat(translatedPose.rotation).isEqualTo(underTest.rotation) 141 } 142 143 @Test translate_withVector3_returnsTranslatedPosenull144 fun translate_withVector3_returnsTranslatedPose() { 145 val underTest = Pose(translation = Vector3(1f, 2f, 3f)) 146 val translation = Vector3(1f, 1f, 1f) 147 148 val translatedPose = underTest.translate(translation) 149 150 assertTranslation(translatedPose.translation, 2f, 3f, 4f) 151 assertThat(translatedPose.rotation).isEqualTo(underTest.rotation) 152 } 153 154 @Test rotate_withIdentityQuaternion_returnsSamePosenull155 fun rotate_withIdentityQuaternion_returnsSamePose() { 156 val underTest = 157 Pose(translation = Vector3(1f, 2f, 3f), rotation = Quaternion(1f, 2f, 3f, 4f)) 158 val rotation = Quaternion.Identity 159 160 val rotatedPose = underTest.rotate(rotation) 161 162 assertTranslation(rotatedPose.translation, 1f, 2f, 3f) 163 assertRotation( 164 rotatedPose.rotation, 165 underTest.rotation.x, 166 underTest.rotation.y, 167 underTest.rotation.z, 168 underTest.rotation.w, 169 ) 170 } 171 172 @Test rotate_withQuaternion_returnsRotatedPosenull173 fun rotate_withQuaternion_returnsRotatedPose() { 174 val underTest = Pose(rotation = Quaternion.Identity) 175 val rotation = Quaternion.fromAxisAngle(Vector3.Forward, 180f) 176 177 val rotatedPose = underTest.rotate(rotation) 178 179 assertThat(rotatedPose.translation).isEqualTo(underTest.translation) 180 assertRotation(rotatedPose.rotation, rotation.x, rotation.y, rotation.z, rotation.w) 181 } 182 183 @Test rotate_withNonIdentityQuaternion_returnsRotatedPosenull184 fun rotate_withNonIdentityQuaternion_returnsRotatedPose() { 185 // A pose with a rotation of 45 degrees around the Y-axis. 186 val underTest = Pose(rotation = Quaternion(0f, sqrt(2f) / 2, 0f, sqrt(2f) / 2)) 187 // A Quaternion representing a 45-degree rotation around the Y-axis. 188 val rotation = Quaternion(0f, sqrt(2f) / 2, 0f, sqrt(2f) / 2) 189 190 val rotatedPose = underTest.rotate(rotation) 191 192 assertThat(rotatedPose.translation).isEqualTo(underTest.translation) 193 // The rotation of the rotated Pose is equal to a 90-degree rotation around the Y-axis. 194 assertRotation(rotatedPose.rotation, 0f, 1f, 0f, 0f) 195 } 196 197 @Test inverse_returnsPoseWithOppositeTransformationnull198 fun inverse_returnsPoseWithOppositeTransformation() { 199 val underTest = 200 Pose( 201 translation = Vector3(3f, 0f, 0f), 202 rotation = Quaternion(0f, sqrt(2f) / 2, 0f, sqrt(2f) / 2), 203 ) 204 205 val underTestInverted = underTest.inverse 206 207 assertTranslation(underTestInverted.translation, 0f, 0f, -3f) 208 assertRotation(underTestInverted.rotation, 0f, -sqrt(2f) / 2, 0f, sqrt(2f) / 2) 209 } 210 211 @Test transform_returnsTransformedPointByPosenull212 fun transform_returnsTransformedPointByPose() { 213 val underTest = 214 Pose(translation = Vector3(0f, 0f, 1f), rotation = Quaternion(0f, 0.7071f, 0f, 0.7071f)) 215 val point = Vector3(0f, 0f, 1f) 216 217 val transformedPoint = underTest.transformPoint(point) 218 219 assertTranslation(transformedPoint, 1f, 0f, 1f) 220 } 221 222 @Test lerp_returnsInterpolatedPosenull223 fun lerp_returnsInterpolatedPose() { 224 val underTest = 225 Pose(translation = Vector3(1f, 2f, 3f), rotation = Quaternion(4f, 5f, 6f, 7f)) 226 val underTest2 = 227 Pose(translation = Vector3(4f, 5f, 6f), rotation = Quaternion(8f, 9f, 10f, 11f)) 228 229 val interpolatedPose = Pose.lerp(underTest, underTest2, 0.5f) 230 231 assertTranslation(interpolatedPose.translation, 2.5f, 3.5f, 4.5f) 232 assertRotation(interpolatedPose.rotation, 0.38759f, 0.45833f, 0.52907f, 0.59981f) 233 } 234 235 @Test up_returnsUpVectorInLocalCoordinateSystem1null236 fun up_returnsUpVectorInLocalCoordinateSystem1() { 237 val underTest = 238 Pose(translation = Vector3(1f, 2f, 3f), rotation = Quaternion(0f, 0.7071f, 0f, 0.7071f)) 239 240 assertTranslation(underTest.up, 0f, 1f, 0f) 241 } 242 243 @Test up_returnsUpVectorInLocalCoordinateSystem2null244 fun up_returnsUpVectorInLocalCoordinateSystem2() { 245 val underTest = 246 Pose(translation = Vector3(1f, 2f, 3f), rotation = Quaternion(0.7071f, 0f, 0f, 0.7071f)) 247 248 assertTranslation(underTest.up, 0f, 0f, 1f) 249 } 250 251 @Test down_returnsDownVectorInLocalCoordinateSystem1null252 fun down_returnsDownVectorInLocalCoordinateSystem1() { 253 val underTest = 254 Pose(translation = Vector3(1f, 2f, 3f), rotation = Quaternion(0f, 0.7071f, 0f, 0.7071f)) 255 256 assertTranslation(underTest.down, 0f, -1f, 0f) 257 } 258 259 @Test down_returnsDownVectorInLocalCoordinateSystem2null260 fun down_returnsDownVectorInLocalCoordinateSystem2() { 261 val underTest = 262 Pose(translation = Vector3(1f, 2f, 3f), rotation = Quaternion(0.7071f, 0f, 0f, 0.7071f)) 263 264 assertTranslation(underTest.down, 0f, 0f, -1f) 265 } 266 267 @Test left_returnsLeftVectorInLocalCoordinateSystem1null268 fun left_returnsLeftVectorInLocalCoordinateSystem1() { 269 val underTest = 270 Pose(translation = Vector3(1f, 2f, 3f), rotation = Quaternion(0.7071f, 0f, 0f, 0.7071f)) 271 272 assertTranslation(underTest.left, -1f, 0f, 0f) 273 } 274 275 @Test left_returnsLeftVectorInLocalCoordinateSystem2null276 fun left_returnsLeftVectorInLocalCoordinateSystem2() { 277 val underTest = 278 Pose(translation = Vector3(1f, 2f, 3f), rotation = Quaternion(0f, 0.7071f, 0f, 0.7071f)) 279 280 assertTranslation(underTest.left, 0f, 0f, 1f) 281 } 282 283 @Test right_returnsRightVectorInLocalCoordinateSystem1null284 fun right_returnsRightVectorInLocalCoordinateSystem1() { 285 val underTest = 286 Pose(translation = Vector3(1f, 2f, 3f), rotation = Quaternion(0.7071f, 0f, 0f, 0.7071f)) 287 288 assertTranslation(underTest.right, 1f, 0f, 0f) 289 } 290 291 @Test right_returnsRightVectorInLocalCoordinateSystem2null292 fun right_returnsRightVectorInLocalCoordinateSystem2() { 293 val underTest = 294 Pose(translation = Vector3(1f, 2f, 3f), rotation = Quaternion(0f, 0.7071f, 0f, 0.7071f)) 295 296 assertTranslation(underTest.right, 0f, 0f, -1f) 297 } 298 299 @Test forward_returnsForwardVectorInLocalCoordinateSystem1null300 fun forward_returnsForwardVectorInLocalCoordinateSystem1() { 301 val underTest = 302 Pose(translation = Vector3(1f, 2f, 3f), rotation = Quaternion(0f, 0f, 0.7071f, 0.7071f)) 303 304 assertTranslation(underTest.forward, 0f, 0f, -1f) 305 } 306 307 @Test forward_returnsForwardVectorInLocalCoordinateSystem2null308 fun forward_returnsForwardVectorInLocalCoordinateSystem2() { 309 val underTest = 310 Pose(translation = Vector3(1f, 2f, 3f), rotation = Quaternion(0.7071f, 0f, 0f, 0.7071f)) 311 312 assertTranslation(underTest.forward, 0f, 1f, 0f) 313 } 314 315 @Test backward_returnsBackwardVectorInLocalCoordinateSystem1null316 fun backward_returnsBackwardVectorInLocalCoordinateSystem1() { 317 val underTest = 318 Pose(translation = Vector3(1f, 2f, 3f), rotation = Quaternion(0f, 0f, 0.7071f, 0.7071f)) 319 320 assertTranslation(underTest.backward, 0f, 0f, 1f) 321 } 322 323 @Test backward_returnsBackwardVectorInLocalCoordinateSystem2null324 fun backward_returnsBackwardVectorInLocalCoordinateSystem2() { 325 val underTest = 326 Pose(translation = Vector3(1f, 2f, 3f), rotation = Quaternion(0.7071f, 0f, 0f, 0.7071f)) 327 328 assertTranslation(underTest.backward, 0f, -1f, 0f) 329 } 330 331 @Test transformVector_returnsVectorTransformedByPosenull332 fun transformVector_returnsVectorTransformedByPose() { 333 val underTest = 334 Pose(translation = Vector3(1f, 2f, 3f), rotation = Quaternion(1f, 2f, 3f, 4f)) 335 val vector = Vector3(1f, 2f, 3f) 336 val underTestRotated = underTest.transformVector(vector) 337 338 assertTranslation(underTestRotated, 1f, 2f, 3f) 339 } 340 341 @Test copy_returnsCopyOfPosenull342 fun copy_returnsCopyOfPose() { 343 val underTest = 344 Pose(translation = Vector3(1f, 2f, 3f), rotation = Quaternion(4f, 5f, 6f, 7f)) 345 val underTest2 = underTest.copy() 346 347 assertThat(underTest).isEqualTo(underTest2) 348 } 349 350 @Test fromLookAt_returnsPoseLookingAtTargetnull351 fun fromLookAt_returnsPoseLookingAtTarget() { 352 val underTest = 353 Pose.fromLookAt( 354 eye = Vector3.Zero, 355 target = Vector3(0f, 0f, 10f), 356 up = Vector3(0f, 1f, 0f) 357 ) 358 359 assertTranslation(underTest.translation, 0f, 0f, 0f) 360 assertRotation(underTest.rotation, 0f, 0f, 0f, 1f) 361 } 362 assertTranslationnull363 private fun assertTranslation( 364 translation: Vector3, 365 expectedX: Float, 366 expectedY: Float, 367 expectedZ: Float, 368 ) { 369 assertThat(translation.x).isWithin(1.0e-4f).of(expectedX) 370 assertThat(translation.y).isWithin(1.0e-4f).of(expectedY) 371 assertThat(translation.z).isWithin(1.0e-4f).of(expectedZ) 372 } 373 assertRotationnull374 private fun assertRotation( 375 rotation: Quaternion, 376 expectedX: Float, 377 expectedY: Float, 378 expectedZ: Float, 379 expectedW: Float, 380 ) { 381 assertThat(rotation.x).isWithin(1.0e-4f).of(expectedX) 382 assertThat(rotation.y).isWithin(1.0e-4f).of(expectedY) 383 assertThat(rotation.z).isWithin(1.0e-4f).of(expectedZ) 384 assertThat(rotation.w).isWithin(1.0e-4f).of(expectedW) 385 } 386 } 387