LCOV - code coverage report
Current view: top level - test - vector.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 100.0 % 314 314
Test Date: 2026-04-03 02:26:39 Functions: 100.0 % 2 2

            Line data    Source code
       1              : #include "catch.hpp"
       2              : #include "Vector.h"
       3              : #include "Matrix.h"
       4              : 
       5           41 : SCENARIO( "Manipulating a vector", "[Vector]" ) {
       6           41 :   GIVEN( "A set vector" ) {
       7            1 :     Vector v;
       8            1 :     v.setVector(1.0f, 2.0f, 3.0f);
       9              : 
      10            2 :     WHEN( "the normalize method is called" ) {
      11            1 :       v.normalize();
      12              : 
      13            2 :       THEN( "the vector is normalized" ) {
      14            1 :         REQUIRE(v.x == Approx(0.26726f));
      15            1 :         REQUIRE(v.y == Approx(0.53452f));
      16            1 :         REQUIRE(v.z == Approx(0.80178f));
      17            1 :       }
      18            1 :     }
      19            1 :   }
      20              : 
      21           42 :   GIVEN( "Default and parameterized constructors" ) {
      22            2 :     Vector a;
      23            2 :     Vector b(3.0f, 4.0f, 0.0f);
      24              : 
      25            3 :     THEN( "default is zero" ) {
      26            1 :       REQUIRE(a.x == 0.0f);
      27            1 :       REQUIRE(a.y == 0.0f);
      28            1 :       REQUIRE(a.z == 0.0f);
      29            1 :     }
      30              : 
      31            3 :     THEN( "constructor sets components" ) {
      32            1 :       REQUIRE(b.x == 3.0f);
      33            1 :       REQUIRE(b.y == 4.0f);
      34            1 :       REQUIRE(b.z == 0.0f);
      35            1 :     }
      36            2 :   }
      37              : 
      38           42 :   GIVEN( "Vectors for dot and cross product" ) {
      39            2 :     Vector u;
      40            2 :     u.setVector(1.0f, 2.0f, 3.0f);
      41            2 :     Vector v;
      42            2 :     v.setVector(4.0f, 5.0f, 6.0f);
      43              : 
      44            3 :     THEN( "dotProduct matches hand calculation" ) {
      45            1 :       REQUIRE(u.dotProduct(v) == Approx(32.0f));
      46            1 :     }
      47              : 
      48            3 :     WHEN( "crossProduct stores u x v" ) {
      49            1 :       Vector w;
      50            1 :       w.crossProduct(u, v);
      51            2 :       THEN( "result is orthogonal to both" ) {
      52            1 :         REQUIRE(w.x == Approx(-3.0f));
      53            1 :         REQUIRE(w.y == Approx(6.0f));
      54            1 :         REQUIRE(w.z == Approx(-3.0f));
      55            1 :       }
      56            1 :     }
      57            2 :   }
      58              : 
      59           43 :   GIVEN( "Vectors for distance, length, and scale" ) {
      60            3 :     Vector p(0.0f, 0.0f, 0.0f);
      61            3 :     Vector q(2.0f, 0.0f, 0.0f);
      62              : 
      63            4 :     THEN( "getDistance is Euclidean" ) {
      64            1 :       REQUIRE(p.getDistance(q) == Approx(2.0f));
      65            1 :     }
      66              : 
      67            4 :     THEN( "getLength uses current components" ) {
      68            1 :       REQUIRE(q.getLength() == Approx(2.0f));
      69            1 :     }
      70              : 
      71            4 :     WHEN( "scale multiplies components" ) {
      72            1 :       q.scale(3.0f);
      73            2 :       THEN( "components scale uniformly" ) {
      74            1 :         REQUIRE(q.x == Approx(6.0f));
      75            1 :         REQUIRE(q.y == Approx(0.0f));
      76            1 :         REQUIRE(q.z == Approx(0.0f));
      77            1 :       }
      78            1 :     }
      79            3 :   }
      80              : 
      81           42 :   GIVEN( "getScaler and getSum" ) {
      82            2 :     Vector a(1.0f, 2.0f, 3.0f);
      83            2 :     Vector b(4.0f, 5.0f, 6.0f);
      84              : 
      85            3 :     THEN( "getScaler is component-wise multiply then sum" ) {
      86            1 :       REQUIRE(a.getScaler(b) == Approx(32.0f));
      87            1 :     }
      88              : 
      89            3 :     THEN( "getSum adds components" ) {
      90            1 :       REQUIRE(a.getSum() == Approx(6.0f));
      91            1 :     }
      92            2 :   }
      93              : 
      94           43 :   GIVEN( "Operator assignments and arithmetic" ) {
      95            3 :     Vector a(1.0f, 2.0f, 3.0f);
      96            3 :     Vector b(10.0f, 20.0f, 30.0f);
      97              : 
      98            4 :     WHEN( "operator=" ) {
      99            1 :       a = b;
     100            2 :       THEN( "values copy" ) {
     101            1 :         REQUIRE(a.x == 10.0f);
     102            1 :         REQUIRE(a.y == 20.0f);
     103            1 :         REQUIRE(a.z == 30.0f);
     104            1 :       }
     105            1 :     }
     106              : 
     107            4 :     WHEN( "operator+ and operator*" ) {
     108            1 :       Vector c = a + b;
     109            1 :       Vector d = a * 2.0f;
     110            2 :       THEN( "addition and scalar multiply" ) {
     111            1 :         REQUIRE(c.x == Approx(11.0f));
     112            1 :         REQUIRE(c.y == Approx(22.0f));
     113            1 :         REQUIRE(c.z == Approx(33.0f));
     114            1 :         REQUIRE(d.x == Approx(2.0f));
     115            1 :         REQUIRE(d.y == Approx(4.0f));
     116            1 :         REQUIRE(d.z == Approx(6.0f));
     117            1 :       }
     118            1 :     }
     119              : 
     120            4 :     WHEN( "operator+= and operator-=" ) {
     121            1 :       a += b;
     122            1 :       a -= Vector(1.0f, 2.0f, 3.0f);
     123            2 :       THEN( "in-place add and subtract" ) {
     124            1 :         REQUIRE(a.x == Approx(10.0f));
     125            1 :         REQUIRE(a.y == Approx(20.0f));
     126            1 :         REQUIRE(a.z == Approx(30.0f));
     127            1 :       }
     128            1 :     }
     129            3 :   }
     130              : 
     131           41 :   GIVEN( "Normalizing a zero vector" ) {
     132            1 :     Vector z(0.0f, 0.0f, 0.0f);
     133            1 :     z.normalize();
     134            2 :     THEN( "components stay zero" ) {
     135            1 :       REQUIRE(z.x == 0.0f);
     136            1 :       REQUIRE(z.y == 0.0f);
     137            1 :       REQUIRE(z.z == 0.0f);
     138            1 :     }
     139            1 :   }
     140              : 
     141           41 :   GIVEN( "calcNorm from three triangle corners" ) {
     142            1 :     Vector p1(0.0f, 0.0f, 0.0f);
     143            1 :     Vector p2(1.0f, 0.0f, 0.0f);
     144            1 :     Vector p3(0.0f, 1.0f, 0.0f);
     145            1 :     Vector n;
     146            1 :     n.calcNorm(p1, p2, p3);
     147            2 :     THEN( "normal points along +Z for XY triangle" ) {
     148            1 :       REQUIRE(n.x == Approx(0.0f));
     149            1 :       REQUIRE(n.y == Approx(0.0f));
     150            1 :       REQUIRE(n.z == Approx(1.0f));
     151            1 :     }
     152            1 :   }
     153              : 
     154           41 :   GIVEN( "transformVector with translation" ) {
     155            1 :     Matrix m;
     156            1 :     m.setTranslation(1.0f, 2.0f, 3.0f);
     157            1 :     Vector v(10.0f, 20.0f, 30.0f);
     158            1 :     Vector out;
     159            1 :     out.transformVector(m, v);
     160            2 :     THEN( "affine part accumulates translation" ) {
     161            1 :       REQUIRE(out.x == Approx(11.0f));
     162            1 :       REQUIRE(out.y == Approx(22.0f));
     163            1 :       REQUIRE(out.z == Approx(33.0f));
     164            1 :     }
     165            1 :   }
     166              : 
     167           41 :   GIVEN( "average of two vectors" ) {
     168            1 :     Vector a(1.0f, 0.0f, 0.0f);
     169            1 :     Vector b(0.0f, 1.0f, 0.0f);
     170            1 :     Vector mid;
     171            1 :     mid.average(a, b);
     172            2 :     THEN( "result is normalized sum" ) {
     173            1 :       const float invSqrt2 = 0.70710678118f;
     174            1 :       REQUIRE(mid.x == Approx(invSqrt2));
     175            1 :       REQUIRE(mid.y == Approx(invSqrt2));
     176            1 :       REQUIRE(mid.z == Approx(0.0f));
     177            1 :     }
     178            1 :   }
     179              : 
     180              :   // intersectBox: `this` is the ray direction; rayPosition is the ray origin.
     181              :   // collisionBox[0] = min corner, collisionBox[1] = max corner (expanded by extend).
     182              :   // Returns the entry intersection point in hitPoint (4-arg overload).
     183              :   // Returns false when no slab intersection is found for the ray direction.
     184           63 :   GIVEN( "intersectBox with a unit box at the origin" ) {
     185           46 :     Vector box[2];
     186           23 :     box[0].setVector(-1.0f, -1.0f, -1.0f);
     187           23 :     box[1].setVector( 1.0f,  1.0f,  1.0f);
     188              : 
     189           25 :     WHEN( "an X-directed ray approaches the box from the left" ) {
     190            2 :       Vector dir(1.0f, 0.0f, 0.0f);
     191            2 :       Vector origin(-5.0f, 0.0f, 0.0f);
     192              : 
     193            3 :       THEN( "the hit test returns true" ) {
     194            1 :         REQUIRE(dir.intersectBox(origin, box, 0.0f));
     195            1 :       }
     196              : 
     197            3 :       THEN( "hitPoint is placed at the entry (near) face" ) {
     198            1 :         Vector hitPoint;
     199            1 :         dir.intersectBox(origin, box, 0.0f, hitPoint);
     200            1 :         REQUIRE(hitPoint.x == Approx(-1.0f));
     201            1 :         REQUIRE(hitPoint.y == Approx( 0.0f));
     202            1 :         REQUIRE(hitPoint.z == Approx( 0.0f));
     203            1 :       }
     204            2 :     }
     205              : 
     206           25 :     WHEN( "an X-directed ray approaches the box from the right" ) {
     207            2 :       Vector dir(-1.0f, 0.0f, 0.0f);
     208            2 :       Vector origin(5.0f, 0.0f, 0.0f);
     209              : 
     210            3 :       THEN( "the hit test returns true" ) {
     211            1 :         REQUIRE(dir.intersectBox(origin, box, 0.0f));
     212            1 :       }
     213              : 
     214            3 :       THEN( "hitPoint is placed at the entry (near) face" ) {
     215            1 :         Vector hitPoint;
     216            1 :         dir.intersectBox(origin, box, 0.0f, hitPoint);
     217            1 :         REQUIRE(hitPoint.x == Approx( 1.0f));
     218            1 :         REQUIRE(hitPoint.y == Approx( 0.0f));
     219            1 :         REQUIRE(hitPoint.z == Approx( 0.0f));
     220            1 :       }
     221            2 :     }
     222              : 
     223           24 :     WHEN( "an X-directed ray passes above the box" ) {
     224            1 :       Vector dir(1.0f, 0.0f, 0.0f);
     225            1 :       Vector origin(-5.0f, 2.0f, 0.0f);
     226              : 
     227            2 :       THEN( "the hit test returns false" ) {
     228            1 :         REQUIRE_FALSE(dir.intersectBox(origin, box, 0.0f));
     229            1 :       }
     230            1 :     }
     231              : 
     232           25 :     WHEN( "a Y-directed ray approaches the box from below" ) {
     233            2 :       Vector dir(0.0f, 1.0f, 0.0f);
     234            2 :       Vector origin(0.0f, -5.0f, 0.0f);
     235              : 
     236            3 :       THEN( "the hit test returns true" ) {
     237            1 :         REQUIRE(dir.intersectBox(origin, box, 0.0f));
     238            1 :       }
     239              : 
     240            3 :       THEN( "hitPoint is placed at the entry (near) face" ) {
     241            1 :         Vector hitPoint;
     242            1 :         dir.intersectBox(origin, box, 0.0f, hitPoint);
     243            1 :         REQUIRE(hitPoint.x == Approx( 0.0f));
     244            1 :         REQUIRE(hitPoint.y == Approx(-1.0f));
     245            1 :         REQUIRE(hitPoint.z == Approx( 0.0f));
     246            1 :       }
     247            2 :     }
     248              : 
     249           25 :     WHEN( "a Y-directed ray approaches the box from above" ) {
     250            2 :       Vector dir(0.0f, -1.0f, 0.0f);
     251            2 :       Vector origin(0.0f, 5.0f, 0.0f);
     252              : 
     253            3 :       THEN( "the hit test returns true" ) {
     254            1 :         REQUIRE(dir.intersectBox(origin, box, 0.0f));
     255            1 :       }
     256              : 
     257            3 :       THEN( "hitPoint is placed at the entry (near) face" ) {
     258            1 :         Vector hitPoint;
     259            1 :         dir.intersectBox(origin, box, 0.0f, hitPoint);
     260            1 :         REQUIRE(hitPoint.x == Approx( 0.0f));
     261            1 :         REQUIRE(hitPoint.y == Approx( 1.0f));
     262            1 :         REQUIRE(hitPoint.z == Approx( 0.0f));
     263            1 :       }
     264            2 :     }
     265              : 
     266           24 :     WHEN( "a Y-directed ray passes beside the box in Z" ) {
     267            1 :       Vector dir(0.0f, 1.0f, 0.0f);
     268            1 :       Vector origin(0.0f, -5.0f, 2.0f);
     269              : 
     270            2 :       THEN( "the hit test returns false" ) {
     271            1 :         REQUIRE_FALSE(dir.intersectBox(origin, box, 0.0f));
     272            1 :       }
     273            1 :     }
     274              : 
     275           25 :     WHEN( "a Z-directed ray approaches the box from the front" ) {
     276            2 :       Vector dir(0.0f, 0.0f, 1.0f);
     277            2 :       Vector origin(0.0f, 0.0f, -5.0f);
     278              : 
     279            3 :       THEN( "the hit test returns true" ) {
     280            1 :         REQUIRE(dir.intersectBox(origin, box, 0.0f));
     281            1 :       }
     282              : 
     283            3 :       THEN( "hitPoint is placed at the entry (near) face" ) {
     284            1 :         Vector hitPoint;
     285            1 :         dir.intersectBox(origin, box, 0.0f, hitPoint);
     286            1 :         REQUIRE(hitPoint.x == Approx( 0.0f));
     287            1 :         REQUIRE(hitPoint.y == Approx( 0.0f));
     288            1 :         REQUIRE(hitPoint.z == Approx(-1.0f));
     289            1 :       }
     290            2 :     }
     291              : 
     292           25 :     WHEN( "a Z-directed ray approaches the box from behind" ) {
     293            2 :       Vector dir(0.0f, 0.0f, -1.0f);
     294            2 :       Vector origin(0.0f, 0.0f, 5.0f);
     295              : 
     296            3 :       THEN( "the hit test returns true" ) {
     297            1 :         REQUIRE(dir.intersectBox(origin, box, 0.0f));
     298            1 :       }
     299              : 
     300            3 :       THEN( "hitPoint is placed at the entry (near) face" ) {
     301            1 :         Vector hitPoint;
     302            1 :         dir.intersectBox(origin, box, 0.0f, hitPoint);
     303            1 :         REQUIRE(hitPoint.x == Approx( 0.0f));
     304            1 :         REQUIRE(hitPoint.y == Approx( 0.0f));
     305            1 :         REQUIRE(hitPoint.z == Approx( 1.0f));
     306            1 :       }
     307            2 :     }
     308              : 
     309           24 :     WHEN( "the ray direction is zero" ) {
     310            1 :       Vector dir(0.0f, 0.0f, 0.0f);
     311            1 :       Vector origin(0.0f, 0.0f, 0.0f);
     312              : 
     313            2 :       THEN( "the hit test returns false" ) {
     314            1 :         REQUIRE_FALSE(dir.intersectBox(origin, box, 0.0f));
     315            1 :       }
     316            1 :     }
     317              : 
     318              :     // A ray that starts past the box and points further away has a negative
     319              :     // tNear to the near face. It should not register as a hit.
     320           24 :     WHEN( "the ray origin is past the box and the ray points away" ) {
     321            1 :       Vector dir(1.0f, 0.0f, 0.0f);
     322            1 :       Vector origin(5.0f, 0.0f, 0.0f);
     323              : 
     324            2 :       THEN( "the hit test returns false" ) {
     325            1 :         REQUIRE_FALSE(dir.intersectBox(origin, box, 0.0f));
     326            1 :       }
     327            1 :     }
     328              : 
     329           25 :     WHEN( "a ray passes just outside the Y bound of the box" ) {
     330            2 :       Vector dir(1.0f, 0.0f, 0.0f);
     331            2 :       Vector origin(-5.0f, 1.5f, 0.0f);
     332              : 
     333            3 :       THEN( "with no extension it misses" ) {
     334            1 :         REQUIRE_FALSE(dir.intersectBox(origin, box, 0.0f));
     335            1 :       }
     336              : 
     337            3 :       THEN( "with enough extension to cover the gap it hits" ) {
     338            1 :         REQUIRE(dir.intersectBox(origin, box, 1.0f));
     339            1 :       }
     340            2 :     }
     341              : 
     342              :     // A ray parallel to an axis whose origin lies outside the box on that axis
     343              :     // must miss, even though the parallel axis slab test is skipped entirely.
     344              :     // The hitPoint bounds check on the skipped axis catches this correctly.
     345           24 :     WHEN( "a Y-directed ray has its origin outside the box on X" ) {
     346            1 :       Vector dir(0.0f, 1.0f, 0.0f);
     347            1 :       Vector origin(50.0f, -5.0f, 0.0f);
     348              : 
     349            2 :       THEN( "the hit test returns false" ) {
     350            1 :         REQUIRE_FALSE(dir.intersectBox(origin, box, 0.0f));
     351            1 :       }
     352            1 :     }
     353              : 
     354           24 :     WHEN( "a Z-directed ray has its origin outside the box on X" ) {
     355            1 :       Vector dir(0.0f, 0.0f, 1.0f);
     356            1 :       Vector origin(50.0f, 0.0f, -5.0f);
     357              : 
     358            2 :       THEN( "the hit test returns false" ) {
     359            1 :         REQUIRE_FALSE(dir.intersectBox(origin, box, 0.0f));
     360            1 :       }
     361            1 :     }
     362              : 
     363              :     // A ray whose origin is already inside the box trivially intersects it.
     364              :     // hitPoint is set to the ray origin.
     365           25 :     WHEN( "an X-directed ray starts inside the box" ) {
     366            2 :       Vector dir(1.0f, 0.0f, 0.0f);
     367            2 :       Vector origin(0.0f, 0.0f, 0.0f);
     368              : 
     369            3 :       THEN( "the hit test returns true" ) {
     370            1 :         REQUIRE(dir.intersectBox(origin, box, 0.0f));
     371            1 :       }
     372              : 
     373            3 :       THEN( "hitPoint is set to the ray origin" ) {
     374            1 :         Vector hitPoint;
     375            1 :         dir.intersectBox(origin, box, 0.0f, hitPoint);
     376            1 :         REQUIRE(hitPoint.x == Approx(0.0f));
     377            1 :         REQUIRE(hitPoint.y == Approx(0.0f));
     378            1 :         REQUIRE(hitPoint.z == Approx(0.0f));
     379            1 :       }
     380            2 :     }
     381              : 
     382           24 :     WHEN( "a Y-directed ray starts inside the box" ) {
     383            1 :       Vector dir(0.0f, 1.0f, 0.0f);
     384            1 :       Vector origin(0.0f, 0.0f, 0.0f);
     385              : 
     386            2 :       THEN( "the hit test returns true" ) {
     387            1 :         REQUIRE(dir.intersectBox(origin, box, 0.0f));
     388            1 :       }
     389            1 :     }
     390           23 :   }
     391           40 : }
        

Generated by: LCOV version 2.4-0