1 #include "objects/geometry/Vector.h" 2 #include "catch.hpp" 3 #include "tests/Approx.h" 4 #include "utils/Utils.h" 5 6 using namespace Sph; 7 8 9 TEST_CASE("Vector construction", "[vector]") { 10 // construct from single value 11 Vector v2(5._f); 12 for (int i = 0; i < 3; ++i) { 13 REQUIRE(v2[i] == 5._f); 14 } 15 16 // copy construct 17 Vector v3(v2); 18 for (int i = 0; i < 3; ++i) { 19 REQUIRE(v3[i] == 5._f); 20 } 21 22 // "move" construct 23 Vector v4(Vector(3._f)); 24 for (int i = 0; i < 3; ++i) { 25 REQUIRE(v4[i] == 3._f); 26 } 27 28 Vector v5(5._f, 4._f, 3._f, 2._f); 29 REQUIRE(v5[0] == 5._f); 30 REQUIRE(v5[1] == 4._f); 31 REQUIRE(v5[2] == 3._f); 32 REQUIRE(v5[3] == 2._f); 33 } 34 35 TEST_CASE("Vector binary operators", "[vector]") { 36 Vector v3(4._f, 6._f, -12._f); 37 Vector v4(2._f, -3._f, -4._f); 38 REQUIRE(v3 + v4 == Vector(6._f, 3._f, -16._f)); 39 REQUIRE(v3 - v4 == Vector(2._f, 9._f, -8._f)); 40 REQUIRE(v3 * v4 == Vector(8._f, -18._f, 48._f)); 41 REQUIRE(v3 / v4 == Vector(2._f, -2._f, 3._f)); 42 REQUIRE(v3 * 2._f == Vector(8._f, 12._f, -24._f)); 43 REQUIRE(2._f * v3 == Vector(8._f, 12._f, -24._f)); 44 REQUIRE(v3 / 2._f == Vector(2._f, 3._f, -6._f)); 45 REQUIRE(v3 * 2._f == Vector(8._f, 12._f, -24._f)); 46 REQUIRE(2._f * v3 == Vector(8._f, 12._f, -24._f)); 47 REQUIRE(v3 / 2._f == Vector(2._f, 3._f, -6._f)); 48 } 49 50 TEST_CASE("Vector unary operators", "[vector]") { 51 Vector v1(3._f, -4._f, 1._f); 52 Vector v2(1._f, 2._f, 3._f); 53 v1 += v2; 54 REQUIRE(v1 == Vector(4._f, -2._f, 4._f)); 55 REQUIRE(v2 == Vector(1._f, 2._f, 3._f)); // unchanged 56 v2 -= v1; 57 REQUIRE(v2 == Vector(-3._f, 4._f, -1._f)); 58 v1 *= 2; 59 REQUIRE(v1 == Vector(8._f, -4._f, 8._f)); 60 v1 /= 2; 61 REQUIRE(v1 == Vector(4._f, -2._f, 4._f)); 62 63 REQUIRE(-v2 == Vector(3._f, -4, 1._f)); 64 } 65 66 TEST_CASE("Vector comparisons 1", "[vector]") { 67 Vector v(6._f, 3._f, 2._f); 68 REQUIRE(v == v); 69 REQUIRE(v == Vector(6._f, 3._f, 2._f)); 70 REQUIRE(Vector(6._f, 3._f, 2._f) == v); 71 REQUIRE(v != Vector(5._f, 3._f, 2._f)); 72 REQUIRE(v != Vector(6._f, 4._f, 2._f)); 73 REQUIRE(v != Vector(6._f, 3._f, 1._f)); 74 } 75 76 TEST_CASE("Vector comparisons 2", "[vectors]") { 77 // dummy components should not influence equality 78 const Vector v1(1.f, 1.f, 3.f, 5.f); 79 const Vector v2(1.f, 2.f, 4.f, 0.f); 80 REQUIRE(v1 != v2); 81 82 const Vector v3(1.f, 1.f, 3.f, 5.f); 83 const Vector v4(1.f, 1.f, 3.f, 0.f); 84 REQUIRE(v3 == v4); 85 } 86 87 TEST_CASE("Vector length", "[vector]") { 88 Vector v1(3._f, 4._f, 12._f); 89 REQUIRE(getSqrLength(v1) == 169._f); 90 REQUIRE(getLength(v1) == 13._f); 91 Vector v2(1._f); 92 REQUIRE(getLength(v2) == Sph::sqrt(3._f)); 93 } 94 95 TEST_CASE("Vector normalization", "[vector]") { 96 Vector v1(3._f, 4._f, 5._f); 97 const Float length = getLength(v1); 98 Vector nv1 = getNormalized(v1); 99 REQUIRE(nv1[0] == 3._f / length); 100 REQUIRE(nv1[1] == 4._f / length); 101 REQUIRE(nv1[2] == 5._f / length); 102 REQUIRE(nv1 == approx(v1 / length)); 103 104 Float l; 105 Vector nv2; 106 tieToTuple(nv2, l) = getNormalizedWithLength(v1); 107 REQUIRE(l == approx(length)); 108 REQUIRE(nv2 == approx(nv1)); 109 } 110 111 TEST_CASE("Vector products", "[vector]") { 112 // dot product 113 Vector v1(1._f, 2._f, 3._f); 114 Vector v2(4._f, -5._f, 6._f); 115 REQUIRE(dot(v1, v2) == 12._f); 116 REQUIRE(dot(v2, v1) == 12._f); 117 118 // cross product 119 Vector expected(27._f, 6._f, -13._f); 120 REQUIRE(cross(v1, v2) == expected); 121 REQUIRE(cross(v2, v1) == -expected); 122 123 const int nRounds = 10; 124 for (int i = 0; i < nRounds; ++i) { 125 const Vector v1 = randomVector(); 126 const Vector v2 = randomVector(); 127 // cross product is perpendicular to both vectors 128 const Vector c = cross(v1, v2); 129 const Float dot1 = dot(c, v1); 130 const Float dot2 = dot(c, v2); 131 REQUIRE(abs(dot1) <= EPS); 132 REQUIRE(abs(dot2) <= EPS); 133 } 134 } 135 136 TEST_CASE("Vector utilities", "[vector]") { 137 // spherical coordinates 138 Vector v = sphericalToCartesian(Sph::sqrt(2._f), PI / 2._f, PI / 4._f); 139 REQUIRE(v == approx(Vector(1._f, 1._f, 0._f))); 140 141 SphericalCoords spherical = cartensianToSpherical(v); 142 REQUIRE(spherical.r == approx(sqrt(2._f))); 143 REQUIRE(spherical.theta == approx(PI / 2._f)); 144 REQUIRE(spherical.phi == approx(PI / 4._f)); 145 } 146 147 TEST_CASE("Vector inequalities", "[vectors]") { 148 const int nRounds = 10; 149 for (int i = 0; i < nRounds; ++i) { 150 // normalization 151 const Vector v1 = randomVector(); 152 REQUIRE(abs(getLength(getNormalized(v1)) - 1._f) <= EPS); 153 154 // triangle inequality 155 const Vector v2 = randomVector(); 156 REQUIRE(getLength(v1 + v2) <= getLength(v1) + getLength(v2)); 157 158 // Cauchy-Schwarz inequality 159 REQUIRE(abs(dot(v1, v2)) <= getLength(v1) * getLength(v2)); 160 } 161 } 162 163 TEST_CASE("Vector Component-wise min and max", "[vectors]") { 164 Vector v1(6._f, -7._f, 8._f); 165 Vector v2(-1._f, 3._f, 5._f); 166 REQUIRE(max(v1, v2) == Vector(6._f, 3._f, 8._f)); 167 REQUIRE(min(v1, v2) == Vector(-1._f, -7._f, 5._f)); 168 } 169 170 TEST_CASE("Vector MinElement", "[vector]") { 171 REQUIRE(minElement(Vector(-1._f, 5._f, 2._f)) == -1._f); 172 REQUIRE(minElement(Vector(5._f, 5._f, 2._f)) == 2._f); 173 REQUIRE(minElement(Vector(-1._f, -5._f, 3._f)) == -5._f); 174 } 175 176 TEST_CASE("Vector abs", "[vector]") { 177 REQUIRE(abs(Vector(-1._f, 0._f, 1._f)) == Vector(1._f, 0._f, 1._f)); 178 REQUIRE(abs(Vector(-1._f, -2._f, -5._f)) == Vector(1._f, 2._f, 5._f)); 179 REQUIRE(abs(Vector(0._f)) == Vector(0._f)); 180 REQUIRE(abs(Vector(5._f, 5._f, -1._f)) == Vector(5._f, 5._f, 1._f)); 181 } 182 183 TEST_CASE("Vector cast", "[vector]") { 184 BasicVector<float> vf(1.f, 2.f, 3.f, 4.f); 185 BasicVector<double> dv = vectorCast<double>(vf); 186 REQUIRE(dv == BasicVector<double>(1., 2., 3., 4.)); 187 188 BasicVector<float> vf2 = vectorCast<float>(dv); 189 REQUIRE(vf2 == BasicVector<float>(1.f, 2.f, 3.f, 4.f)); 190 191 BasicVector<float> vf3 = vectorCast<float>(vf2); // casting on the same precision 192 REQUIRE(vf3 == BasicVector<float>(1.f, 2.f, 3.f, 4.f)); 193 } 194 195 TEST_CASE("Vector almostEqual", "[vector]") { 196 REQUIRE(almostEqual(Vector(1._f, 2._f, 3._f), Vector(1._f, 2._f, 3._f))); 197 REQUIRE_FALSE(almostEqual(Vector(1._f, 2._f, 3._f), Vector(1._f, -2._f, 3._f))); 198 REQUIRE_FALSE(almostEqual(Vector(1._f, 2._f, 3._f), Vector(1._f, 2._f, 2.9_f))); 199 REQUIRE(almostEqual(Vector(1._f, 2._f, 3._f), Vector(1._f, 2._f, 2.9_f), 0.1_f)); 200 201 REQUIRE(almostEqual(Vector(1.e10_f), Vector(1.1e10_f), 0.1_f)); 202 REQUIRE_FALSE(almostEqual(Vector(1.e10_f), Vector(1.1e10_f), 0.01_f)); 203 REQUIRE(almostEqual(Vector(1.e12_f, -2.e12_f, 0.5_f), Vector(1.e12_f, -2.e12_f, 10._f), 1.e-6_f)); 204 REQUIRE_FALSE(almostEqual(Vector(1.e12_f, -2.e12_f, 0.5_f), Vector(1.e12_f, -2.e12_f, 10._f), 1e-12_f)); 205 206 REQUIRE(almostEqual(Vector(1.e-10_f), Vector(1.1e-10_f), 1.e-6_f)); 207 REQUIRE_FALSE(almostEqual(Vector(1.e-10_f), Vector(1.1e-10_f), 1.e-15_f)); 208 REQUIRE(almostEqual(Vector(1.e-12_f, -2.e-12_f, 0._f), Vector(1.e-12_f, 1.e-18_f - 2.e-12_f, 0._f))); 209 } 210 211 TEST_CASE("Vector lexicographicalLess", "[vector]") { 212 REQUIRE(lexicographicalLess(Vector(5._f, 3._f, 1._f), Vector(2._f, 1._f, 2._f))); 213 REQUIRE_FALSE(lexicographicalLess(Vector(5._f, 3._f, 1._f), Vector(2._f, 1._f, 0.5_f))); 214 REQUIRE(lexicographicalLess(Vector(5._f, 0._f, 1._f), Vector(2._f, 1._f, 1._f))); 215 REQUIRE_FALSE(lexicographicalLess(Vector(5._f, 3._f, 1._f), Vector(2._f, 1._f, 1._f))); 216 REQUIRE(lexicographicalLess(Vector(1._f, 3._f, 1._f), Vector(2._f, 3._f, 1._f))); 217 REQUIRE_FALSE(lexicographicalLess(Vector(5._f, 3._f, 1._f), Vector(2._f, 3._f, 1._f))); 218 } 219 220 TEST_CASE("Vector less", "[vector]") { 221 Vector v1(2._f, 3._f, 6._f); 222 Vector v2(3._f, 3._f, 3._f); 223 REQUIRE(less(v1, v2) == Vector(1._f, 0._f, 0.f)); 224 225 Vector v3(2._f, 5._f, -1._f, 1._f); 226 Vector v4(3._f, 6._f, -2._f, -3._f); 227 REQUIRE(less(v3, v4) == Vector(1._f, 1._f, 0._f, 0._f)); 228 } 229 230 TEST_CASE("Vector unit", "[vector]") { 231 REQUIRE(Vector::unit(0) == Vector(1, 0, 0)); 232 REQUIRE(Vector::unit(1) == Vector(0, 1, 0)); 233 REQUIRE(Vector::unit(2) == Vector(0, 0, 1)); 234 REQUIRE_SPH_ASSERT(Vector::unit(3)); 235 } 236 237 /// \todo also test tensors 238 /*TEST_CASE("Vector clampWithDerivative", "[vector]") { 239 Vector v(-1._f, 3._f, 6._f); 240 Vector dv(20._f, 21._f, 22._f); 241 Range r(0._f, 4._f); 242 tie(v, dv) = clampWithDerivative(v, dv, r); 243 REQUIRE(v == Vector(0._f, 3._f, 4._f)); 244 REQUIRE(dv == Vector(0._f, 21._f, 0._f)); 245 } 246 */ 247