1 /* 2 * Copyright (C) 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.ink.geometry 18 19 import com.google.common.truth.Truth.assertThat 20 import kotlin.math.sqrt 21 import kotlin.test.assertFailsWith 22 import org.junit.Test 23 import org.junit.runner.RunWith 24 import org.junit.runners.JUnit4 25 26 @RunWith(JUnit4::class) 27 class VecTest { 28 29 @Test isAlmostEqual_whenNoToleranceGiven_returnsCorrectValuenull30 fun isAlmostEqual_whenNoToleranceGiven_returnsCorrectValue() { 31 val vec = ImmutableVec(1f, 2f) 32 33 assertThat(vec.isAlmostEqual(vec)).isTrue() 34 assertThat(vec.isAlmostEqual(ImmutableVec(1f, 2f))).isTrue() 35 assertThat(vec.isAlmostEqual(ImmutableVec(1.00001f, 1.99999f))).isTrue() 36 assertThat(vec.isAlmostEqual(ImmutableVec(1f, 1.99f))).isFalse() 37 assertThat(vec.isAlmostEqual(ImmutableVec(1.01f, 2f))).isFalse() 38 assertThat(vec.isAlmostEqual(ImmutableVec(1.01f, 1.99f))).isFalse() 39 } 40 41 @Test isAlmostEqual_withToleranceGiven_returnsCorrectValuenull42 fun isAlmostEqual_withToleranceGiven_returnsCorrectValue() { 43 val vec = ImmutableVec(1f, 2f) 44 45 assertThat(vec.isAlmostEqual(vec, tolerance = 0.00000001f)).isTrue() 46 assertThat(vec.isAlmostEqual(ImmutableVec(1f, 2f), tolerance = 0.00000001f)).isTrue() 47 assertThat(vec.isAlmostEqual(ImmutableVec(1.00001f, 1.99999f), tolerance = 0.000001f)) 48 .isFalse() 49 assertThat(vec.isAlmostEqual(ImmutableVec(1f, 1.99f), tolerance = 0.02f)).isTrue() 50 assertThat(vec.isAlmostEqual(ImmutableVec(1.01f, 2f), tolerance = 0.02f)).isTrue() 51 assertThat(vec.isAlmostEqual(ImmutableVec(1.01f, 1.99f), tolerance = 0.02f)).isTrue() 52 assertThat(vec.isAlmostEqual(ImmutableVec(2.5f, 0.5f), tolerance = 2f)).isTrue() 53 } 54 55 @Test isAlmostEqual_whenSameInterface_returnsTruenull56 fun isAlmostEqual_whenSameInterface_returnsTrue() { 57 val vec = MutableVec(1f, 2f) 58 val other = ImmutableVec(0.99999f, 2.00001f) 59 assertThat(vec.isAlmostEqual(other)).isTrue() 60 } 61 62 @Test direction_returnsCorrectValuenull63 fun direction_returnsCorrectValue() { 64 assertThat(ImmutableVec(5f, 0f).computeDirection()).isEqualTo(Angle.degreesToRadians(0f)) 65 assertThat(ImmutableVec(0f, 5f).computeDirection()).isEqualTo(Angle.degreesToRadians(90f)) 66 assertThat(ImmutableVec(-5f, 0f).computeDirection()).isEqualTo(Angle.degreesToRadians(180f)) 67 assertThat(ImmutableVec(0f, -5f).computeDirection()).isEqualTo(Angle.degreesToRadians(-90f)) 68 assertThat(ImmutableVec(5f, 5f).computeDirection()).isEqualTo(Angle.degreesToRadians(45f)) 69 assertThat(ImmutableVec(-5f, 5f).computeDirection()).isEqualTo(Angle.degreesToRadians(135f)) 70 assertThat(ImmutableVec(-5f, -5f).computeDirection()) 71 .isEqualTo(Angle.degreesToRadians(-135f)) 72 assertThat(ImmutableVec(5f, -5f).computeDirection()).isEqualTo(Angle.degreesToRadians(-45f)) 73 } 74 75 @Test direction_whenVecContainsZero_returnsCorrectValuenull76 fun direction_whenVecContainsZero_returnsCorrectValue() { 77 assertThat(ImmutableVec(+0f, +0f).computeDirection()).isEqualTo(Angle.degreesToRadians(0f)) 78 assertThat(ImmutableVec(+0f, -0f).computeDirection()).isEqualTo(Angle.degreesToRadians(-0f)) 79 assertThat(ImmutableVec(-0f, +0f).computeDirection()) 80 .isEqualTo(Angle.degreesToRadians(180f)) 81 assertThat(ImmutableVec(-0f, -0f).computeDirection()) 82 .isEqualTo(Angle.degreesToRadians(-180f)) 83 } 84 85 @Test unitVec_returnsCorrectValuenull86 fun unitVec_returnsCorrectValue() { 87 assertThat(ImmutableVec(4f, 0f).computeUnitVec()).isEqualTo(ImmutableVec(1f, 0f)) 88 assertThat(MutableVec(0f, -25f).computeUnitVec()).isEqualTo(ImmutableVec(0f, -1f)) 89 assertThat( 90 ImmutableVec(30f, 30f) 91 .computeUnitVec() 92 .isAlmostEqual(ImmutableVec(sqrt(.5f), sqrt(.5f)), tolerance = 0.000001f) 93 ) 94 .isTrue() 95 assertThat( 96 MutableVec(-.05f, -.05f) 97 .computeUnitVec() 98 .isAlmostEqual(ImmutableVec(-sqrt(.5f), -sqrt(.5f)), tolerance = 0.000001f) 99 ) 100 .isTrue() 101 } 102 103 @Test unitVec_whenVecContainsZeroes_returnsCorrectValuenull104 fun unitVec_whenVecContainsZeroes_returnsCorrectValue() { 105 assertThat(ImmutableVec(+0f, 0f).computeUnitVec()).isEqualTo(ImmutableVec(1f, 0f)) 106 assertThat(MutableVec(-0f, 0f).computeUnitVec()).isEqualTo(ImmutableVec(-1f, 0f)) 107 } 108 109 @Test populateUnitVec_populatesCorrectValuenull110 fun populateUnitVec_populatesCorrectValue() { 111 val mutableVec = MutableVec(0f, 0f) 112 MutableVec(4f, 0f).computeUnitVec(mutableVec) 113 assertThat(mutableVec).isEqualTo(ImmutableVec(1f, 0f)) 114 115 ImmutableVec(0f, -25f).computeUnitVec(mutableVec) 116 assertThat(mutableVec).isEqualTo(ImmutableVec(0f, -1f)) 117 118 MutableVec(30f, 30f).computeUnitVec(mutableVec) 119 assertThat(mutableVec.isAlmostEqual(ImmutableVec(sqrt(.5f), sqrt(.5f)))).isTrue() 120 121 ImmutableVec(-.05f, -.05f).computeUnitVec(mutableVec) 122 assertThat(mutableVec.isAlmostEqual(ImmutableVec(-sqrt(.5f), -sqrt(.5f)))).isTrue() 123 } 124 125 @Test populateUnitVec_whenVecContainsZeroes_populatesCorrectValuenull126 fun populateUnitVec_whenVecContainsZeroes_populatesCorrectValue() { 127 val mutableVec = MutableVec(0f, 0f) 128 MutableVec(+0f, 0f).computeUnitVec(mutableVec) 129 assertThat(mutableVec).isEqualTo(ImmutableVec(1f, 0f)) 130 131 ImmutableVec(-0f, -0f).computeUnitVec(mutableVec) 132 assertThat(mutableVec).isEqualTo(ImmutableVec(-1f, 0f)) 133 } 134 135 @Test absoluteAngleBetween_returnsCorrectValuenull136 fun absoluteAngleBetween_returnsCorrectValue() { 137 assertThat(Vec.absoluteAngleBetween(ImmutableVec(10f, 0f), ImmutableVec(40f, 0f))) 138 .isEqualTo(Angle.degreesToRadians(0f)) 139 assertThat(Vec.absoluteAngleBetween(MutableVec(7f, 0f), MutableVec(0f, 12f))) 140 .isEqualTo(Angle.degreesToRadians(90f)) 141 assertThat(Vec.absoluteAngleBetween(ImmutableVec(-5f, 0f), MutableVec(.1f, 0f))) 142 .isEqualTo(Angle.degreesToRadians(180f)) 143 assertThat(Vec.absoluteAngleBetween(MutableVec(20f, 20f), ImmutableVec(0f, 10f))) 144 .isEqualTo(Angle.degreesToRadians(45f)) 145 assertThat(Vec.absoluteAngleBetween(ImmutableVec(-2f, 2f), ImmutableVec(0f, -3f))) 146 .isEqualTo(Angle.degreesToRadians(135f)) 147 assertThat( 148 Vec.absoluteAngleBetween(MutableVec(-1f, -sqrt(3.0f)), MutableVec(1f, -sqrt(3.0f))) 149 ) 150 .isEqualTo(Angle.degreesToRadians(60f)) 151 } 152 153 @Test signedAngleBetween_returnsCorrectValuenull154 fun signedAngleBetween_returnsCorrectValue() { 155 assertThat(Vec.signedAngleBetween(MutableVec(2f, 0f), MutableVec(2f, 0f))) 156 .isEqualTo(Angle.degreesToRadians(0f)) 157 assertThat(Vec.signedAngleBetween(ImmutableVec(20f, 0f), ImmutableVec(0f, .1f))) 158 .isEqualTo(Angle.degreesToRadians(90f)) 159 assertThat(Vec.signedAngleBetween(MutableVec(0f, 10f), ImmutableVec(17f, 0f))) 160 .isEqualTo(Angle.degreesToRadians(-90f)) 161 assertThat(Vec.signedAngleBetween(ImmutableVec(-1f, 0f), MutableVec(.11f, 0f))) 162 .isEqualTo(Angle.degreesToRadians(180f)) 163 assertThat(Vec.signedAngleBetween(MutableVec(12f, 12f), MutableVec(-3f, 3f))) 164 .isEqualTo(Angle.degreesToRadians(90f)) 165 assertThat(Vec.signedAngleBetween(ImmutableVec(-1f, -1f), ImmutableVec(-987f, 0f))) 166 .isEqualTo(Angle.degreesToRadians(-45f)) 167 assertThat(Vec.signedAngleBetween(ImmutableVec(-62f, -62f), ImmutableVec(sqrt(3.0f), 1f))) 168 .isEqualTo(Angle.degreesToRadians(165f)) 169 assertThat(Vec.signedAngleBetween(MutableVec(-11f, 11f), ImmutableVec(.01f, 0f))) 170 .isEqualTo(Angle.degreesToRadians(-135f)) 171 assertThat( 172 Vec.signedAngleBetween(MutableVec(1f, -sqrt(3.0f)), MutableVec(-1f, -sqrt(3.0f))) 173 ) 174 .isEqualTo(Angle.degreesToRadians(-60f)) 175 } 176 177 @Test isParallelTo_withEquivalentVecs_returnsTruenull178 fun isParallelTo_withEquivalentVecs_returnsTrue() { 179 assertThat(MutableVec(1f, 0f).isParallelTo(MutableVec(1f, 0f), .001f)).isTrue() 180 assertThat(MutableVec(0f, 100f).isParallelTo(MutableVec(0f, 100f), .001f)).isTrue() 181 assertThat(MutableVec(359.38f, -7.84f).isParallelTo(MutableVec(359.38f, -7.84f), .001f)) 182 .isTrue() 183 } 184 185 @Test isParallelTo_whenVecsHaveSameDirection_returnsTruenull186 fun isParallelTo_whenVecsHaveSameDirection_returnsTrue() { 187 assertThat(MutableVec(10f, 0f).isParallelTo(MutableVec(99f, 0f), .001f)).isTrue() 188 assertThat(MutableVec(0f, 40f).isParallelTo(MutableVec(0f, 99f), .001f)).isTrue() 189 assertThat(MutableVec(3f, -6f).isParallelTo(MutableVec(32f, -64f), .001f)).isTrue() 190 assertThat(MutableVec(.0001f, .0009f).isParallelTo(MutableVec(.0005f, .0045f), .001f)) 191 .isTrue() 192 } 193 194 @Test isParallelTo_whenVecsHaveOppositeDirections_returnsTruenull195 fun isParallelTo_whenVecsHaveOppositeDirections_returnsTrue() { 196 assertThat(MutableVec(8f, 0f).isParallelTo(MutableVec(-7f, 0f), .001f)).isTrue() 197 assertThat(MutableVec(0f, 30f).isParallelTo(MutableVec(0f, -.99f), .001f)).isTrue() 198 assertThat(MutableVec(.2f, .2f).isParallelTo(MutableVec(-99f, -99f), .001f)).isTrue() 199 assertThat(MutableVec(-32f, 64f).isParallelTo(MutableVec(5f, -10f), .001f)).isTrue() 200 } 201 202 @Test isParallelTo_whenVecsHaveDifferentDirections_returnsFalsenull203 fun isParallelTo_whenVecsHaveDifferentDirections_returnsFalse() { 204 assertThat(MutableVec(5f, 5f).isParallelTo(MutableVec(1f, -1f), .001f)).isFalse() 205 assertThat(MutableVec(-3f, -10f).isParallelTo(MutableVec(-88f, 17.5f), .001f)).isFalse() 206 207 // These Vecs have different but close directions. These would pass with sufficiently high 208 // tolerance, but fail with low tolerance. 209 assertThat(MutableVec(100f, 100f).isParallelTo(MutableVec(99f, 100f), .001f)).isFalse() 210 assertThat(MutableVec(100f, 100f).isParallelTo(MutableVec(100f, 99f), .001f)).isFalse() 211 assertThat(MutableVec(-100f, 100f).isParallelTo(MutableVec(-99f, 100f), .001f)).isFalse() 212 assertThat(MutableVec(100f, -100f).isParallelTo(MutableVec(100f, -99f), .001f)).isFalse() 213 } 214 215 @Test isPerpendicularTo_returnsCorrectValuenull216 fun isPerpendicularTo_returnsCorrectValue() { 217 assertThat(MutableVec(1f, 0f).isPerpendicularTo(MutableVec(0f, 5f), .001f)).isTrue() 218 assertThat(MutableVec(5f, 0f).isPerpendicularTo(MutableVec(0f, -10f), .001f)).isTrue() 219 assertThat(MutableVec(0f, 100f).isPerpendicularTo(MutableVec(-.01f, 0f), .001f)).isTrue() 220 assertThat(MutableVec(77f, -77f).isPerpendicularTo(MutableVec(200f, 200f), .001f)).isTrue() 221 assertThat(MutableVec(-32f, 64f).isPerpendicularTo(MutableVec(86f, 43f), .001f)).isTrue() 222 assertThat( 223 MutableVec(.0001f, -.0009f).isPerpendicularTo(MutableVec(-.0045f, -.0005f), .001f) 224 ) 225 .isTrue() 226 227 assertThat(MutableVec(1f, -2f).isPerpendicularTo(MutableVec(1f, -2f), .001f)).isFalse() 228 assertThat(MutableVec(1f, -2f).isPerpendicularTo(MutableVec(-1f, 2f), .001f)).isFalse() 229 assertThat(MutableVec(10f, 10f).isPerpendicularTo(MutableVec(0f, 10f), .001f)).isFalse() 230 assertThat(MutableVec(-30f, 25f).isPerpendicularTo(MutableVec(50f, 30f), .001f)).isFalse() 231 232 // These Vecs are close but not quite perpendicular. These would pass with sufficiently high 233 // tolerance, but fail with low tolerance. 234 assertThat(MutableVec(100f, 100f).isPerpendicularTo(MutableVec(-99f, 100f), .001f)) 235 .isFalse() 236 assertThat(MutableVec(100f, 100f).isPerpendicularTo(MutableVec(-100f, 99f), .001f)) 237 .isFalse() 238 assertThat(MutableVec(-100f, 100f).isPerpendicularTo(MutableVec(-99f, -100f), .001f)) 239 .isFalse() 240 assertThat(MutableVec(100f, -100f).isPerpendicularTo(MutableVec(100f, 99f), .001f)) 241 .isFalse() 242 } 243 244 @Test determinant_returnsCorrectValuenull245 fun determinant_returnsCorrectValue() { 246 val a = ImmutableVec(3f, 0f) 247 val b = ImmutableVec(-1f, 4f) 248 val c = ImmutableVec(2f, .5f) 249 250 assertThat(Vec.determinant(a, b)).isEqualTo(12f) 251 assertThat(Vec.determinant(a, c)).isEqualTo(1.5f) 252 assertThat(Vec.determinant(b, a)).isEqualTo(-12f) 253 assertThat(Vec.determinant(b, c)).isEqualTo(-8.5f) 254 assertThat(Vec.determinant(c, a)).isEqualTo(-1.5f) 255 assertThat(Vec.determinant(c, b)).isEqualTo(8.5f) 256 } 257 258 @Test add_populatesCorrectValuenull259 fun add_populatesCorrectValue() { 260 val a = ImmutableVec(3f, 0f) 261 val b = MutableVec(-1f, .3f) 262 val c = ImmutableVec(2.7f, 4f) 263 264 val aPlusbOut = MutableVec() 265 val aPluscOut = MutableVec() 266 val bPluscOut = MutableVec() 267 268 Vec.add(a, b, aPlusbOut) 269 Vec.add(a, c, aPluscOut) 270 Vec.add(b, c, bPluscOut) 271 272 assertThat(aPlusbOut.isAlmostEqual(ImmutableVec(2f, .3f))).isTrue() 273 assertThat(aPluscOut.isAlmostEqual(ImmutableVec(5.7f, 4f))).isTrue() 274 assertThat(bPluscOut.isAlmostEqual(ImmutableVec(1.7f, 4.3f))).isTrue() 275 } 276 277 @Test multiply_populatesCorrectValuenull278 fun multiply_populatesCorrectValue() { 279 val a = ImmutableVec(.7f, -3f) 280 val b = MutableVec(3f, 5f) 281 282 val aMultipliedBy2Out = MutableVec() 283 val aMultipliedBy1TenthOut = MutableVec() 284 val bMultipliedBy4Out = MutableVec() 285 val bMultipliedByNegative3TenthsOut = MutableVec() 286 287 Vec.multiply(a, 2f, aMultipliedBy2Out) 288 Vec.multiply(.1f, a, aMultipliedBy1TenthOut) 289 Vec.multiply(b, 4f, bMultipliedBy4Out) 290 Vec.multiply(-.3f, b, bMultipliedByNegative3TenthsOut) 291 292 assertThat(aMultipliedBy2Out.isAlmostEqual(ImmutableVec(1.4f, -6f))).isTrue() 293 assertThat(aMultipliedBy1TenthOut.isAlmostEqual(ImmutableVec(.07f, -0.3f))).isTrue() 294 assertThat(bMultipliedBy4Out.isAlmostEqual(ImmutableVec(12f, 20f))).isTrue() 295 assertThat(bMultipliedByNegative3TenthsOut.isAlmostEqual(ImmutableVec(-0.9f, -1.5f))) 296 .isTrue() 297 } 298 299 @Test divide_populatesCorrectValuenull300 fun divide_populatesCorrectValue() { 301 val a = ImmutableVec(7f, .9f) 302 val b = MutableVec(-4.5f, -2f) 303 304 val aDividedBy2Out = MutableVec() 305 val aDividedByNegative1TenthOut = MutableVec() 306 val bDividedBy5Out = MutableVec() 307 val bDividedBy2TenthsOut = MutableVec() 308 309 Vec.divide(a, 2f, aDividedBy2Out) 310 Vec.divide(a, -.1f, aDividedByNegative1TenthOut) 311 Vec.divide(b, 5f, bDividedBy5Out) 312 Vec.divide(b, .2f, bDividedBy2TenthsOut) 313 314 assertThat(aDividedBy2Out.isAlmostEqual(ImmutableVec(3.5f, .45f))).isTrue() 315 assertThat(aDividedByNegative1TenthOut.isAlmostEqual(ImmutableVec(-70f, -9f))).isTrue() 316 assertThat(bDividedBy5Out.isAlmostEqual(ImmutableVec(-.9f, -.4f))).isTrue() 317 assertThat(bDividedBy2TenthsOut.isAlmostEqual(ImmutableVec(-22.5f, -10f))).isTrue() 318 } 319 320 @Test divide_whenDividingByZero_throwsExceptionnull321 fun divide_whenDividingByZero_throwsException() { 322 val testOutput = MutableVec() 323 324 assertFailsWith<IllegalArgumentException> { 325 Vec.divide(ImmutableVec(2f, 3f), 0f, testOutput) 326 } 327 assertFailsWith<IllegalArgumentException> { Vec.divide(MutableVec(0f, 0f), 0f, testOutput) } 328 } 329 330 @Test subtract_returnsCorrectValuenull331 fun subtract_returnsCorrectValue() { 332 val a = ImmutableVec(0f, -2f) 333 val b = MutableVec(.5f, 19f) 334 val c = ImmutableVec(1.1f, -3.4f) 335 val aMinusbOut = MutableVec() 336 val aMinuscOut = MutableVec() 337 val bMinuscOut = MutableVec() 338 339 Vec.subtract(a, b, aMinusbOut) 340 Vec.subtract(a, c, aMinuscOut) 341 Vec.subtract(b, c, bMinuscOut) 342 343 assertThat(aMinusbOut.isAlmostEqual(ImmutableVec(-.5f, -21f), tolerance = 0.001f)).isTrue() 344 assertThat(aMinuscOut.isAlmostEqual(ImmutableVec(-1.1f, 1.4f), tolerance = 0.001f)).isTrue() 345 assertThat(bMinuscOut.isAlmostEqual(ImmutableVec(-.6f, 22.4f), tolerance = 0.001f)).isTrue() 346 } 347 348 @Test dotProduct_returnsCorrectValuenull349 fun dotProduct_returnsCorrectValue() { 350 val a = ImmutableVec(3f, 0f) 351 val b = MutableVec(-1f, 4f) 352 val c = MutableVec(2f, .5f) 353 val d = ImmutableVec(6f, 6f) 354 355 assertThat(Vec.dotProduct(a, b)).isEqualTo(-3f) 356 assertThat(Vec.dotProduct(a, c)).isEqualTo(6f) 357 assertThat(Vec.dotProduct(a, d)).isEqualTo(18f) 358 assertThat(Vec.dotProduct(b, c)).isEqualTo(0f) 359 assertThat(Vec.dotProduct(b, d)).isEqualTo(18f) 360 assertThat(Vec.dotProduct(c, d)).isEqualTo(15f) 361 } 362 363 @Test origin_isCorrectValueAndReturnsSameInstancenull364 fun origin_isCorrectValueAndReturnsSameInstance() { 365 assertThat(Vec.ORIGIN).isEqualTo(ImmutableVec(0f, 0f)) 366 assertThat(Vec.ORIGIN).isSameInstanceAs(Vec.ORIGIN) 367 } 368 } 369