From bac26441c575916cb06729d4c9bced385236f62a Mon Sep 17 00:00:00 2001 From: NADAL Jean-Baptiste Date: Tue, 13 Feb 2024 18:41:42 +0100 Subject: [PATCH] [WIP] tests transform it into BDD Style --- tests/01_tuples.cpp | 596 +++++++++++++++++------- tests/02_1_colors.cpp | 189 +++++--- tests/02_2_canvas.cpp | 192 +++++--- tests/03_matrix.cpp | 853 ++++++++++++++++++++++------------- tests/04_transformations.cpp | 428 +++++++++++++----- tests/05_rays.cpp | 108 +++-- 6 files changed, 1635 insertions(+), 731 deletions(-) diff --git a/tests/01_tuples.cpp b/tests/01_tuples.cpp index 74d7761..1a95824 100644 --- a/tests/01_tuples.cpp +++ b/tests/01_tuples.cpp @@ -35,318 +35,594 @@ using namespace Raytracer; /* ------------------------------------------------------------------------- */ -TEST_CASE("[01][Tuple] a tuple with w=1.0 is a point", "[Tuple]") +SCENARIO("A tuple with w=1.0 is a point", "[features/tuples.feature]") { - Tuple a(4.3, -4.2, 3.1, 1.0); - - REQUIRE(a.x() == 4.3); - REQUIRE(a.y() == -4.2); - REQUIRE(a.z() == 3.1); - REQUIRE(a.w() == 1.0); - - REQUIRE(a.is_point() == true); - REQUIRE(a.is_vector() == false); + GIVEN("A tuple with w=1.0 is a point") + { + Tuple a(4.3, -4.2, 3.1, 1.0); + THEN("a.x = 4.3") + { + REQUIRE(a.x() == 4.3); + } + AND_THEN("a.y = -4.2") + { + REQUIRE(a.y() == -4.2); + } + AND_THEN("a.z = 3.1") + { + REQUIRE(a.z() == 3.1); + } + AND_THEN("a.w = 1.0") + { + REQUIRE(a.w() == 1.0); + } + AND_THEN("a is a point") + { + REQUIRE(a.is_point() == true); + } + AND_THEN("a is not a vector") + { + REQUIRE(a.is_vector() == false); + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[01][Tuple] a tuple with w=0 is a vector", "[Tuple]") +SCENARIO("A tuple with w=0 is a vector", "[features/tuples.feature]") { - Tuple a(4.3, -4.2, 3.1, 0.0); - - REQUIRE(a.x() == 4.3); - REQUIRE(a.y() == -4.2); - REQUIRE(a.z() == 3.1); - REQUIRE(a.w() == 0.0); - - REQUIRE(a.is_point() == false); + GIVEN("a <-tuple(4.3, -4.2, 3.1, 0.0)") + { + Tuple a(4.3, -4.2, 3.1, 0.0); + THEN("a.x = 4.3") + { + REQUIRE(a.x() == 4.3); + } + AND_THEN("a.y = -4.2") + { + REQUIRE(a.y() == -4.2); + } + AND_THEN("a.z = 3.1") + { + REQUIRE(a.z() == 3.1); + } + AND_THEN("a.w = 0.0") + { + REQUIRE(a.w() == 0.0); + } + AND_THEN("a is not a point") + { + REQUIRE(a.is_point() == false); + } + AND_THEN("a is a vector") + { + REQUIRE(a.is_vector() == true); + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[01][Tuple] access of data with []", "[Tuple]") +SCENARIO("Access of data with []", "[features/tuples.feature]") { - Tuple a(4.3, -4.2, 3.1, 0.0); - - REQUIRE(a[0] == 4.3); - REQUIRE(a[1] == -4.2); - REQUIRE(a[2] == 3.1); - REQUIRE(a[3] == 0.0); + GIVEN(" a <- tuple(4.3, -4.2, 3.1, 0.0)") + { + Tuple a(4.3, -4.2, 3.1, 0.0); + THEN("a[0] = 4.3") + { + REQUIRE(a[0] == 4.3); + } + AND_THEN("a[1] = -4.2") + { + REQUIRE(a[1] == -4.2); + } + AND_THEN("a[2] = 3.1") + { + REQUIRE(a[2] == 3.1); + } + AND_THEN("a[3] = 0.0") + { + REQUIRE(a[3] == 0.0); + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[01][Tuple] constructor with std::vector", "[Tuple]") +SCENARIO("A constructor with std::vector", "[features/tuples.feature]") { - std::vector v = {4.3, -4.2, 3.1, 0.0}; - Tuple a(v); + GIVEN(" a <- tuple with {4.3, -4.2, 3.1, 0.0}") + { + std::vector v = {4.3, -4.2, 3.1, 0.0}; + Tuple a(v); - REQUIRE(a[0] == 4.3); - REQUIRE(a[1] == -4.2); - REQUIRE(a[2] == 3.1); - REQUIRE(a[3] == 0.0); + THEN("a[0] = 4.3") + { + REQUIRE(a[0] == 4.3); + } + AND_THEN("a[1] = -4.2") + { + REQUIRE(a[1] == -4.2); + } + AND_THEN("a[2] = 3.1") + { + REQUIRE(a[2] == 3.1); + } + AND_THEN("a[3] = 0.0") + { + REQUIRE(a[3] == 0.0); + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[01][Tuple] test copy constructor", "[Tuple]") +SCENARIO("A test copy constructor", "[features/tuples.feature]") { - Tuple a; - Tuple b(4.3, -4.2, 3.1, 0.0); - - a = b; - - REQUIRE(a[0] == 4.3); - REQUIRE(a[1] == -4.2); - REQUIRE(a[2] == 3.1); - REQUIRE(a[3] == 0.0); + GIVEN(" a <- tuple(4.3, -4.2, 3.1, 0.0)") + { + Tuple a(4.3, -4.2, 3.1, 0.0); + AND_GIVEN(" b <- tuple") + { + Tuple b; + WHEN("b = a") + { + b = a; + THEN("b[0] = 4.3") + { + REQUIRE(b[0] == 4.3); + } + AND_THEN("b[1] = -4.2") + { + REQUIRE(b[1] == -4.2); + } + AND_THEN("b[2] = 3.1") + { + REQUIRE(b[2] == 3.1); + } + AND_THEN("b[3] = 0.0") + { + REQUIRE(b[3] == 0.0); + } + } + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[01][Tuple] Tuple could be copy", "[Tuple]") +SCENARIO("Tuple could be copy", "[features/tuples.feature]") { - Tuple p = Tuple::Point(4, -4, 3); - Tuple n; - - n = p; - - REQUIRE(n == p); + GIVEN("p <-point(4, -4, 3)") + { + Tuple p = Tuple::Point(4, -4, 3); + Tuple n; + WHEN("n <- p") + { + n = p; + THEN(" n = p") + { + REQUIRE(n == p); + } + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[01][Tuple] Point() creates tuples with w=1", "[Tuple][Point]") +SCENARIO("Point() creates tuples with w=1", "[features/tuples.feature]") { - Tuple p = Tuple::Point(4, -4, 3); - - REQUIRE(p == Tuple(4, -4, 3, 1)); + GIVEN("p <-point(4, -4, 3)") + { + Tuple p = Tuple::Point(4, -4, 3); + THEN("p = tuple(4,-4, 3, 1)") + { + REQUIRE(p == Tuple(4, -4, 3, 1)); + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[01][Tuple] Vector() creates tuples with w=0", "[Tuple][Vector]") +SCENARIO("Vector() creates tuples with w=0", "[features/tuples.feature]") { - Tuple v = Tuple::Vector(4, -4, 3); - - REQUIRE(v == Tuple(4, -4, 3, 0)); + GIVEN("v <-vector(4, -4, 3)") + { + Tuple v = Tuple::Vector(4, -4, 3); + THEN("v = tuple(4,-4, 3, 1)") + { + REQUIRE(v == Tuple(4, -4, 3, 0)); + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[01][Tuple] Adding two tuples", "[Tuple][Operations]") +SCENARIO("Adding two tuples", "[features/tuples.feature]") { - Tuple a1(3, -2, 5, 1); - Tuple a2(-2, 3, 1, 0); - - REQUIRE((a1 + a2) == Tuple(1, 1, 6, 1)); + GIVEN("a1 <-tuple(3, -2, 5, 1)") + { + Tuple a1(3, -2, 5, 1); + AND_GIVEN("a2 <- tuple(-2, 3, 1, 0)") + { + Tuple a2(-2, 3, 1, 0); + THEN("a1 + a2 = tuple(1, 1, 6, 1)") + { + REQUIRE((a1 + a2) == Tuple(1, 1, 6, 1)); + } + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[01][Tuple] Adding two tuples without modify a1", "[Tuple][Operations]") +SCENARIO("Adding two tuples without modify a1", "[features/tuples.feature]") { - Tuple a1(3, -2, 5, 1); - Tuple a2(-2, 3, 1, 0); - - Tuple a3 = a1 + a2; - - REQUIRE((a1 + a2) == Tuple(1, 1, 6, 1)); + GIVEN("a1 <-tuple(3, -2, 5, 1)") + { + Tuple a1(3, -2, 5, 1); + AND_GIVEN("a2 <- tuple(-2, 3, 1, 0)") + { + Tuple a2(-2, 3, 1, 0); + WHEN("a3 = a1 + a2") + { + Tuple a3 = a1 + a2; + THEN("a1 + a2 = tuple(1, 1, 6, 1)") + { + REQUIRE((a1 + a2) == Tuple(1, 1, 6, 1)); + } + } + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[01][Tuple] Subtracting two points", "[Tuple][Operations]") +SCENARIO("Subtracting two points", "[features/tuples.feature]") { - Tuple p1 = Tuple::Point(3, 2, 1); - Tuple p2 = Tuple::Point(5, 6, 7); - - REQUIRE((p1 - p2) == Tuple::Vector(-2, -4, -6)); + GIVEN("p1 <- point(3, 2, 1)") + { + Tuple p1 = Tuple::Point(3, 2, 1); + AND_GIVEN("p2 <- point(5, 6, 7)") + { + Tuple p2 = Tuple::Point(5, 6, 7); + THEN("p1 - p2 = vector(-2, -4, -6)") + { + REQUIRE((p1 - p2) == Tuple::Vector(-2, -4, -6)); + } + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[01][Tuple] Subtracting two points without modify p1", "[Tuple][Operations]") +SCENARIO("Subtracting two points without modify p1", "[features/tuples.feature]") { - Tuple p1 = Tuple::Point(3, 2, 1); - Tuple p2 = Tuple::Point(5, 6, 7); + GIVEN("p1 <- point(3, 2, 1)") + { + Tuple p1 = Tuple::Point(3, 2, 1); + AND_GIVEN("p2 <- point(5, 6, 7)") + { + Tuple p2 = Tuple::Point(5, 6, 7); - Tuple p3 = p1 - p2; - - REQUIRE((p1 - p2) == Tuple::Vector(-2, -4, -6)); + WHEN("p3 = p1 - p2") + { + Tuple p3 = p1 - p2; + THEN("(p1 - p2) = vector(-2, -4, -6)") + { + REQUIRE((p1 - p2) == Tuple::Vector(-2, -4, -6)); + } + } + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[01][Tuple] Subtracting a vector from a point", "[Tuple][Operations]") +SCENARIO("Subtracting a vector from a point", "[features/tuples.feature]") { - Tuple p = Tuple::Point(3, 2, 1); - Tuple v = Tuple::Vector(5, 6, 7); + GIVEN("p <- point(3, 2, 1)") + { + Tuple p = Tuple::Point(3, 2, 1); - REQUIRE((p - v) == Tuple::Point(-2, -4, -6)); + AND_GIVEN("v <-vector(5, 6, 7)") + { + Tuple v = Tuple::Vector(5, 6, 7); + THEN("p - v = point(-2, -4, -6)") + { + REQUIRE((p - v) == Tuple::Point(-2, -4, -6)); + } + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[01][Tuple] Subtracting two vectors", "[Tuple][Operations]") +SCENARIO("Subtracting two vectors", "[features/tuples.feature]") { - Tuple v1 = Tuple::Vector(3, 2, 1); - Tuple v2 = Tuple::Vector(5, 6, 7); - - REQUIRE((v1 - v2) == Tuple::Vector(-2, -4, -6)); + GIVEN("v1 <-vector(3, 2, 1)") + { + Tuple v1 = Tuple::Vector(3, 2, 1); + AND_GIVEN("v2 <-vector(5, 6, 7)") + { + Tuple v2 = Tuple::Vector(5, 6, 7); + THEN("v1 - v2 = vector(-2, -4, -6)") + { + REQUIRE((v1 - v2) == Tuple::Vector(-2, -4, -6)); + } + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[01][Tuple] Subtracting a vector from the zero vector", "[Tuple][Operations]") +SCENARIO("Subtracting a vector from the zero vector", "[features/tuples.feature]") { - Tuple zero = Tuple::Vector(0, 0, 0); - Tuple v = Tuple::Vector(1, -2, 3); - - REQUIRE((zero - v) == Tuple::Vector(-1, 2, -3)); + GIVEN("zero <-vector(0, 0, 0)") + { + Tuple zero = Tuple::Vector(0, 0, 0); + AND_GIVEN("v <-vector(1, -2, 3)") + { + Tuple v = Tuple::Vector(1, -2, 3); + THEN("zero - v = vector(-1, 2, -3)") + { + REQUIRE((zero - v) == Tuple::Vector(-1, 2, -3)); + } + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[01][Tuple] Negative a tuple", "[Tuple][Operations]") +SCENARIO("Negative a tuple", "[features/tuples.feature]") { - Tuple a(1, -2, 3, -4); - - REQUIRE(-a == Tuple(-1, 2, -3, 4)); + GIVEN("a <-tuple(1, -2, 3, -4)") + { + Tuple a(1, -2, 3, -4); + THEN("-a = tuple(-1, 2, -3, 4)") + { + REQUIRE(-a == Tuple(-1, 2, -3, 4)); + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[01][Tuple] Multiplying a tuple by a scalar", "[Tuple][Multiplication]") +SCENARIO("Multiplying a tuple by a scalar", "[features/tuples.feature]") { - Tuple a(1, -2, 3, -4); - - REQUIRE(a * 3.5 == Tuple(3.5, -7, 10.5, -14)); + GIVEN("a <-tuple(1, -2, 3, -4)") + { + Tuple a(1, -2, 3, -4); + THEN("a * 3.5 = tuple(3.5, -7, 10.5, -14)") + { + REQUIRE(a * 3.5 == Tuple(3.5, -7, 10.5, -14)); + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[01][Tuple] Multiplying a tuple by a scalar without modify a", "[Tuple][Multiplication]") +SCENARIO("Multiplying a tuple by a scalar without modify a", "[features/tuples.feature]") { - Tuple a(1, -2, 3, -4); - - Tuple b = a * 3.5; - - REQUIRE(a * 3.5 == Tuple(3.5, -7, 10.5, -14)); + GIVEN("a <-tuple(1, -2, 3, -4)") + { + Tuple a(1, -2, 3, -4); + WHEN("b = a * 3.5;") + { + Tuple b = a * 3.5; + THEN("a * 3.5 = tuple(3.5, -7, 10.5, -14)") + { + REQUIRE(a * 3.5 == Tuple(3.5, -7, 10.5, -14)); + } + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[01][Tuple] Dividing a tuple by a scalar", "[Tuple][Multiplication]") +SCENARIO("Multiplying a tuple by a fraction", "[features/tuples.feature]") { - Tuple a(1, -2, 3, -4); - - REQUIRE(a / 2 == Tuple(0.5, -1, 1.5, -2)); + GIVEN("a <-tuple(1, -2, 3, -4)") + { + Tuple a(1, -2, 3, -4); + THEN("a * 0.5 = tuple(0.5, -1, 1.5, -2)") + { + REQUIRE(a * 0.5 == Tuple(0.5, -1, 1.5, -2)); + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[01][Tuple] Dividing a tuple by a scalar without modify a", "[Tuple][Multiplication]") +SCENARIO("Dividing a tuple by a scalar", "[features/tuples.feature]") { - Tuple a(1, -2, 3, -4); - - Tuple b = a / 2; - - REQUIRE(a / 2 == Tuple(0.5, -1, 1.5, -2)); + GIVEN("a <-tuple(1, -2, 3, -4)") + { + Tuple a(1, -2, 3, -4); + THEN("a / 2 = tuple(0.5, -1, 1.5, -2)") + { + REQUIRE(a / 2 == Tuple(0.5, -1, 1.5, -2)); + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[01][Tuple] Computing the magnitude of vector(1,0,0)", "[Tuple][Magnitude]") +SCENARIO("Dividing a tuple by a scalar without modify a", "[features/tuples.feature]") { - Tuple v = Tuple::Vector(1, 0, 0); - - REQUIRE(v.magnitude() == 1); + GIVEN("a <-tuple(1, -2, 3, -4)") + { + Tuple a(1, -2, 3, -4); + WHEN("b = a / 2") + { + Tuple b = a / 2; + THEN("a / 2 = tuple(0.5, -1, 1.5, -2)") + { + REQUIRE(a / 2 == Tuple(0.5, -1, 1.5, -2)); + } + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[01][Tuple] Computing the magnitude of vector(0,1,0)", "[Tuple][Magnitude]") +SCENARIO("Computing the magnitude of vector(1,0,0)", "[features/tuples.feature]") { - Tuple v = Tuple::Vector(0, 1, 0); - - REQUIRE(v.magnitude() == 1); + GIVEN("v <-vector(1, 0, 0)") + { + Tuple v = Tuple::Vector(1, 0, 0); + THEN("magnitude(v) = 1") + { + REQUIRE(v.magnitude() == 1); + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[01][Tuple] Computing the magnitude of vector(0,0,1)", "[Tuple][Magnitude]") +SCENARIO("Computing the magnitude of vector(0,1,0)", "[features/tuples.feature]") { - Tuple v = Tuple::Vector(0, 0, 1); + GIVEN("v <-vector(0, 1, 0)") + { + Tuple v = Tuple::Vector(0, 1, 0); - REQUIRE(v.magnitude() == 1); + THEN("magnitude(v) = 1") + { + REQUIRE(v.magnitude() == 1); + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[01][Tuple] Computing the magnitude of vector(1,2,3)", "[Tuple][Magnitude]") +SCENARIO("Computing the magnitude of vector(0,0,1)", "[features/tuples.feature]") { - Tuple v = Tuple::Vector(1, 2, 3); + GIVEN("v <-vector(0, 0, 1)") + { + Tuple v = Tuple::Vector(0, 0, 1); - REQUIRE(v.magnitude() == sqrt(14)); + THEN("magnitude(v) = 1") + { + REQUIRE(v.magnitude() == 1); + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[01][Tuple] Computing the magnitude of vector(-1,-2,-3)", "[Tuple][Magnitude]") +SCENARIO("Computing the magnitude of vector(1,2,3)", "[features/tuples.feature]") { - Tuple v = Tuple::Vector(-1, -2, -3); + GIVEN("v <-vector(1, 2, 3)") + { + Tuple v = Tuple::Vector(1, 2, 3); - REQUIRE(v.magnitude() == sqrt(14)); + THEN("magnitude(v) = sqrt(14)") + { + REQUIRE(v.magnitude() == sqrt(14)); + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[01][Tuple] Normalize vector(4,0,0) gives (1,0,0)", "[Tuple][Normalize]") +SCENARIO("Computing the magnitude of vector(-1,-2,-3)", "[features/tuples.feature]") { - Tuple v = Tuple::Vector(4, 0, 0); + GIVEN("v <-vector(-1, -2, -3)") + { + Tuple v = Tuple::Vector(-1, -2, -3); - REQUIRE(v.normalize() == Tuple::Vector(1, 0, 0)); + THEN("magnitude(v) = sqrt(14)") + { + REQUIRE(v.magnitude() == sqrt(14)); + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[01][Tuple] Normalize vector(1,2,3)", "[Tuple][Normalize]") +SCENARIO("Normalize vector(4,0,0) gives (1,0,0)", "[features/tuples.feature]") { - Tuple v = Tuple::Vector(1, 2, 3); - - REQUIRE(v.normalize() == Tuple::Vector(1 / sqrtf(14), 2 / sqrtf(14), 3 / sqrtf(14))); + GIVEN("v <-vector(4, 0, 0)") + { + Tuple v = Tuple::Vector(4, 0, 0); + THEN("normalize(v) = vector(1, 0, 0)") + { + REQUIRE(v.normalize() == Tuple::Vector(1, 0, 0)); + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[01][Tuple] The magnitude of a normalized vector", "[Tuple][Normalize]") +SCENARIO("Normalize vector(1,2,3)", "[features/tuples.feature]") { - Tuple norm; - Tuple v = Tuple::Vector(1, 2, 3); - - norm = v.normalize(); - - REQUIRE(norm.magnitude() == 1); + GIVEN("v <-vector(1, 2, 3)") + { + Tuple v = Tuple::Vector(1, 2, 3); + // vector (1/sqrt(14), 2/sqrt(14), 3/sqrt(14)) + THEN("normalize(v) = approximately vector(0.26726, 0.53452, 0.80178)") + { + REQUIRE(v.normalize() == Tuple::Vector(1 / sqrtf(14), 2 / sqrtf(14), 3 / sqrtf(14))); + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[01][Tuple] The dot product of two tuples", "[Tuple][Dot]") +SCENARIO("The magnitude of a normalized vector", "[features/tuples.feature]") { - Tuple a = Tuple::Vector(1, 2, 3); - Tuple b = Tuple::Vector(2, 3, 4); - - REQUIRE(a.dot(b) == 20); + GIVEN("v <-vector(1, 2, 3)") + { + Tuple v = Tuple::Vector(1, 2, 3); + Tuple norm; + WHEN("norm <- normalize(v)") + { + norm = v.normalize(); + THEN("magnitude(norm) = 1") + { + REQUIRE(norm.magnitude() == 1); + } + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[01][Tuple] The cross product of two vector", "[Tuple][Cross]") +SCENARIO("The dot product of two tuples", "[features/tuples.feature]") { - Tuple a = Tuple::Vector(1, 2, 3); - Tuple b = Tuple::Vector(2, 3, 4); + GIVEN("a <-vector(1, 2, 3)") + { + Tuple a = Tuple::Vector(1, 2, 3); - REQUIRE(a.cross(b) == Tuple::Vector(-1, 2, -1)); - REQUIRE(b.cross(a) == Tuple::Vector(1, -2, 1)); + AND_GIVEN("b <-vector(2, 3, 4)") + { + Tuple b = Tuple::Vector(2, 3, 4); + THEN("dot(a,b) = 20") + { + REQUIRE(a.dot(b) == 20); + } + } + } +} + +/* ------------------------------------------------------------------------- */ + +SCENARIO("The cross product of two vectors", "[features/tuples.feature]") +{ + GIVEN("a <-vector(1, 2, 3)") + { + Tuple a = Tuple::Vector(1, 2, 3); + AND_GIVEN("b <-vector(2, 3, 4)") + { + Tuple b = Tuple::Vector(2, 3, 4); + THEN("cross(a,b) = vector(-1, 2, -1)") + { + REQUIRE(a.cross(b) == Tuple::Vector(-1, 2, -1)); + } + AND_THEN("cross(b,a) = vector(1, -2, -1)") + { + REQUIRE(b.cross(a) == Tuple::Vector(1, -2, 1)); + } + } + } } diff --git a/tests/02_1_colors.cpp b/tests/02_1_colors.cpp index f3b62da..abf2ce6 100644 --- a/tests/02_1_colors.cpp +++ b/tests/02_1_colors.cpp @@ -33,99 +33,184 @@ using namespace Raytracer; /* ------------------------------------------------------------------------- */ -TEST_CASE("[02][01][Color] colors are (red,green,blue) tuples", "[Colors]") +SCENARIO("Colors are (red,green,blue) tuples", "[features/tuples.feature]") { - Color c(-0.5, 0.4, 1.7); - - REQUIRE(c.red() == -0.5); - REQUIRE(c.green() == 0.4); - REQUIRE(c.blue() == 1.7); + GIVEN("c <-color(-0.5, 0.4, 1.7)") + { + Color c(-0.5, 0.4, 1.7); + THEN("c.red = -0.5") + { + REQUIRE(c.red() == -0.5); + } + AND_THEN("c.green = 0.4") + { + REQUIRE(c.green() == 0.4); + } + AND_THEN("c.blue = 1.7") + { + REQUIRE(c.blue() == 1.7); + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[02][01][Color] Colors could be copied", "[Colors]") +SCENARIO("Colors could be copied", "[features/tuples.feature]") { - Color c1(-0.5, 0.4, 1.7); - Color c2; - - c2 = c1; - - REQUIRE(c2.red() == -0.5); - REQUIRE(c2.green() == 0.4); - REQUIRE(c2.blue() == 1.7); + GIVEN("c1 <-color(-0.5, 0.4, 1.7)") + { + Color c1(-0.5, 0.4, 1.7); + AND_GIVEN("c2 <-color") + { + Color c2; + WHEN("c2 <- c1") + { + c2 = c1; + THEN("c2.red = -0.5") + { + REQUIRE(c2.red() == -0.5); + } + AND_THEN("c2.green = 0.4") + { + REQUIRE(c2.green() == 0.4); + } + AND_THEN("c2.blue = 1.7") + { + REQUIRE(c2.blue() == 1.7); + } + } + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[02][01][Color] Adding colors", "[Colors]") +SCENARIO("Adding colors", "[features/tuples.feature]") { - Color c1(0.9, 0.6, 0.75); - Color c2(0.7, 0.1, 0.25); + GIVEN("c1 <-color(0.9, 0.6, 0.75)") + { + Color c1(0.9, 0.6, 0.75); + AND_GIVEN("c2 <-color(0.7, 0.1, 0.25)") + { + Color c2(0.7, 0.1, 0.25); - REQUIRE((c1 + c2) == Color(1.6, 0.7, 1.0)); + THEN("c1 + c2 = color(1.6, 0.7, 1.0)") + { + REQUIRE((c1 + c2) == Color(1.6, 0.7, 1.0)); + } + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[02][01][Color] Adding colors without modify c1", "[Colors]") +SCENARIO("Adding colors without modify c1", "[features/tuples.feature]") { - Color c1(0.9, 0.6, 0.75); - Color c2(0.7, 0.1, 0.25); - - Color c3 = c1 + c2; - - REQUIRE((c1 + c2) == Color(1.6, 0.7, 1.0)); + GIVEN("c1 <-color(0.9, 0.6, 0.75)") + { + Color c1(0.9, 0.6, 0.75); + AND_GIVEN("c2 <-color(0.7, 0.1, 0.25)") + { + Color c2(0.7, 0.1, 0.25); + WHEN("color c3 = c1 + c2") + { + Color c3 = c1 + c2; + THEN("c1 + c2 = color(1.6, 0.7, 1.0)") + { + REQUIRE((c1 + c2) == Color(1.6, 0.7, 1.0)); + } + } + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[02][01][Color] Subtracting colors", "[Colors]") +SCENARIO("Subtracting colors", "[features/tuples.feature]") { - Color c1(0.9, 0.6, 0.75); - Color c2(0.7, 0.1, 0.25); + GIVEN("c1 <-color(0.9, 0.6, 0.75)") + { + Color c1(0.9, 0.6, 0.75); + AND_GIVEN("c2 <-color(0.7, 0.1, 0.25)") + { + Color c2(0.7, 0.1, 0.25); - REQUIRE((c1 - c2) == Color(0.2, 0.5, 0.5)); + THEN("c1 - c2 = color(0.2, 0.5, 0.5)") + { + REQUIRE((c1 - c2) == Color(0.2, 0.5, 0.5)); + } + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[02][01][Color] Subtracting colors without modify c1", "[Colors]") +SCENARIO("Subtracting colors without modify c1", "[features/tuples.feature]") { - Color c1(0.9, 0.6, 0.75); - Color c2(0.7, 0.1, 0.25); - - Color c3 = c1 - c2; - - REQUIRE((c1 - c2) == Color(0.2, 0.5, 0.5)); + GIVEN("c1 <-color(0.9, 0.6, 0.75)") + { + Color c1(0.9, 0.6, 0.75); + AND_GIVEN("c2 <-color(0.7, 0.1, 0.25)") + { + Color c2(0.7, 0.1, 0.25); + WHEN("color c3 = c1 - c2") + { + Color c3 = c1 - c2; + THEN("c1 - c2 = color(0.2, 0.5, 0.5)") + { + REQUIRE((c1 - c2) == Color(0.2, 0.5, 0.5)); + } + } + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[02][01][Color] Multiplying a color by a scalar", "[Colors]") +SCENARIO("Multiplying a color by a scalar", "[features/tuples.feature]") { - Color c(0.2, 0.3, 0.4); - - REQUIRE(c * 2 == Color(0.4, 0.6, 0.8)); + GIVEN("c <-color(0.2, 0.3, 0.4)") + { + Color c(0.2, 0.3, 0.4); + THEN("c * 2 = color(0.4, 0.6, 0.8)") + { + REQUIRE(c * 2 == Color(0.4, 0.6, 0.8)); + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[02][01][Color] Multiplying a color by a scalar without modify c", "[Colors]") +SCENARIO("Multiplying a color by a scalar without modify c", "[features/tuples.feature]") { - Color c(0.2, 0.3, 0.4); - - Color c3 = c * 4; - - REQUIRE(c * 2 == Color(0.4, 0.6, 0.8)); + GIVEN("c <-color(0.2, 0.3, 0.4)") + { + Color c(0.2, 0.3, 0.4); + WHEN("color c3 = c * 4") + { + Color c3 = c * 4; + THEN("c * 2 = color(0.4, 0.6, 0.8)") + { + REQUIRE(c * 2 == Color(0.4, 0.6, 0.8)); + } + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[02][01][Color] Multiplying a colors", "[Colors]") +SCENARIO("Multiplying a colors", "[features/tuples.feature]") { - Color c1(1, 0.2, 0.4); - Color c2(0.9, 1, 0.1); - - REQUIRE((c1 * c2) == Color(0.9, 0.2, 0.04)); + GIVEN("c1 <-color(1, 0.2, 0.4)") + { + Color c1(1, 0.2, 0.4); + AND_GIVEN("c2 <-color(0.9, 1, 0.1)") + { + Color c2(0.9, 1, 0.1); + THEN("c1 * c2 = color(0.9, 0.2, 0.04)") + { + REQUIRE((c1 * c2) == Color(0.9, 0.2, 0.04)); + } + } + } } diff --git a/tests/02_2_canvas.cpp b/tests/02_2_canvas.cpp index 423d6b5..0529091 100644 --- a/tests/02_2_canvas.cpp +++ b/tests/02_2_canvas.cpp @@ -33,80 +33,136 @@ using namespace Raytracer; /* ------------------------------------------------------------------------- */ -TEST_CASE("[02][02][Canvas] Creating a canvas", "[Canvas]") +SCENARIO("Creating a canvas", "[features/canvas.feature]") { - Canvas c(10, 20); - - REQUIRE(c.width() == 10); - REQUIRE(c.height() == 20); - - for (int i = 0; i < 10; ++i) + GIVEN("c <-canvas(10, 20)") { - for (int j = 0; j < 20; ++j) + Canvas c(10, 20); + THEN("c.width = 10") { - REQUIRE(c.pixel_at(2, 3) == Color(0, 0, 0)); + REQUIRE(c.width() == 10); + } + AND_THEN("c.height = 20") + { + REQUIRE(c.height() == 20); } - } -} - -/* ------------------------------------------------------------------------- */ - -TEST_CASE("[02][02][Canvas] Writing pixels to a canvas", "[Canvas]") -{ - Canvas c(10, 20); - Color red(1, 0, 0); - - c.write_pixel(2, 3, red); - - REQUIRE(c.pixel_at(2, 3) == red); -} - -/* ------------------------------------------------------------------------- */ - -TEST_CASE("[02][02][Canvas] Constructing the PPM pixel data", "[Canvas]") -{ - std::string ppm, the_ref_ppm; - Canvas c(5, 3); - Color c1(1.5, 0, 0); - Color c2(0, 0.5, 0); - Color c3(-0.5, 0, 1); - - c.write_pixel(0, 0, c1); - c.write_pixel(2, 1, c2); - c.write_pixel(4, 2, c3); - - ppm = c.to_ppm(); - - the_ref_ppm = "P3\n5 3\n255\n"; - the_ref_ppm += "255 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"; - the_ref_ppm += "0 0 0 0 0 0 0 128 0 0 0 0 0 0 0\n"; - the_ref_ppm += "0 0 0 0 0 0 0 0 0 0 0 0 0 0 255\n"; - - REQUIRE(ppm == the_ref_ppm); -} - -/* ------------------------------------------------------------------------- */ - -TEST_CASE("[02][02][Canvas] Split long lines in PPM files", "[Canvas]") -{ - std::string ppm, the_ref_ppm; - Canvas c(10, 2); - - for (int j = 0; j < 2; ++j) - { for (int i = 0; i < 10; ++i) { - c.write_pixel(i, j, Color(1, 0.8, 0.6)); + for (int j = 0; j < 20; ++j) + { + AND_THEN("every pixel of c is color(0,0,0)") + { + REQUIRE(c.pixel_at(2, 3) == Color(0, 0, 0)); + } + } + } + } +} + +/* ------------------------------------------------------------------------- */ + +SCENARIO("Writing pixels to a canvas", "[features/canvas.feature]") +{ + GIVEN("c <-canvas(10, 20)") + { + Canvas c(10, 20); + AND_GIVEN("red <-color(1, 0, 0)") + { + Color red(1, 0, 0); + + WHEN("write_pixel(c,2,3,red)") + { + c.write_pixel(2, 3, red); + THEN("pixel_at(c,2,3) = red") + { + REQUIRE(c.pixel_at(2, 3) == red); + } + } + } + } +} + +/* ------------------------------------------------------------------------- */ + +SCENARIO("Constructing the PPM pixel data", "[features/canvas.feature]") +{ + std::string ppm, the_ref_ppm; + GIVEN("c <-canvas(5, 3)") + { + Canvas c(5, 3); + AND_GIVEN(" c1 color(1.5, 0, 0)") + { + Color c1(1.5, 0, 0); + AND_GIVEN(" c2 color(0, 0.5, 0)") + { + Color c2(0, 0.5, 0); + AND_GIVEN(" c3 color(-0.5, 0, 1))") + { + Color c3(-0.5, 0, 1); + WHEN("write_pixel(c, 0, 0, c1)") + { + c.write_pixel(0, 0, c1); + AND_WHEN("write_pixel(c, 2, 1, c2)") + { + c.write_pixel(2, 1, c2); + AND_WHEN("write_pixel(c, 4, 2, c3)") + { + c.write_pixel(4, 2, c3); + AND_WHEN("ppm <- canvas_to_ppm(c)") + { + ppm = c.to_ppm(); + + the_ref_ppm = "P3\n5 3\n255\n"; + the_ref_ppm += "255 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"; + the_ref_ppm += "0 0 0 0 0 0 0 128 0 0 0 0 0 0 0\n"; + the_ref_ppm += "0 0 0 0 0 0 0 0 0 0 0 0 0 0 255\n"; + THEN("line 4-6 of ppm are 255 0 0 ....") + { + REQUIRE(ppm == the_ref_ppm); + } + } + } + } + } + } + } + } + } +} + +/* ------------------------------------------------------------------------- */ + +SCENARIO("Split long lines in PPM files", "[features/canvas.feature]") +{ + std::string ppm, the_ref_ppm; + GIVEN("c <-canvas(10, 2)") + { + Canvas c(10, 2); + WHEN("every pixel of c is set to color(1,0.8,0.6)") + { + for (int j = 0; j < 2; ++j) + { + for (int i = 0; i < 10; ++i) + { + c.write_pixel(i, j, Color(1, 0.8, 0.6)); + } + } + + AND_WHEN("ppm <- canvas_to_ppm(c)") + { + ppm = c.to_ppm(); + + the_ref_ppm = "P3\n10 2\n255\n"; + the_ref_ppm += "255 204 153 255 204 153 255 204 153 255 204 153 255 204 153 255 204\n"; + the_ref_ppm += "153 255 204 153 255 204 153 255 204 153 255 204 153\n"; + the_ref_ppm += "255 204 153 255 204 153 255 204 153 255 204 153 255 204 153 255 204\n"; + the_ref_ppm += "153 255 204 153 255 204 153 255 204 153 255 204 153\n"; + + THEN("line 4-7 of ppm are 255 204 153 255 204 ...") + { + REQUIRE(ppm == the_ref_ppm); + } + } } } - - ppm = c.to_ppm(); - - the_ref_ppm = "P3\n10 2\n255\n"; - the_ref_ppm += "255 204 153 255 204 153 255 204 153 255 204 153 255 204 153 255 204\n"; - the_ref_ppm += "153 255 204 153 255 204 153 255 204 153 255 204 153\n"; - the_ref_ppm += "255 204 153 255 204 153 255 204 153 255 204 153 255 204 153 255 204\n"; - the_ref_ppm += "153 255 204 153 255 204 153 255 204 153 255 204 153\n"; - - REQUIRE(ppm == the_ref_ppm); } diff --git a/tests/03_matrix.cpp b/tests/03_matrix.cpp index 94116fb..93140b1 100644 --- a/tests/03_matrix.cpp +++ b/tests/03_matrix.cpp @@ -33,431 +33,680 @@ using namespace Raytracer; /* ------------------------------------------------------------------------- */ -TEST_CASE("[03][Matrix] Constructing and inspecting a 4x4 matrix", "[Matrix]") +SCENARIO("Constructing and inspecting a 4x4 matrix", "[features/matrices.feature]") { - Matrix m = { - { 1, 2, 3, 4}, - { 5.5, 6.5, 7.5, 8.5}, - { 9, 10, 11, 12}, - {13.5, 14.5, 15.5, 16.5} - }; + GIVEN("rgz following 4x4 matrix M") + { + Matrix M = { + { 1, 2, 3, 4}, + { 5.5, 6.5, 7.5, 8.5}, + { 9, 10, 11, 12}, + {13.5, 14.5, 15.5, 16.5} + }; - REQUIRE(m.rows() == 4); - REQUIRE(m.cols() == 4); + THEN("M.rows = 4") + { + REQUIRE(M.rows() == 4); + } + AND_THEN("M.cols = 4") + { + REQUIRE(M.cols() == 4); + } - REQUIRE(m[0][0] == 1); - REQUIRE(m[0][3] == 4); - REQUIRE(m[1][0] == 5.5); - REQUIRE(m[1][2] == 7.5); - REQUIRE(m[2][2] == 11); - REQUIRE(m[3][0] == 13.5); - REQUIRE(m[3][2] == 15.5); + AND_THEN("M[0][0] = 1") + { + REQUIRE(M[0][0] == 1); + } + AND_THEN("M[0][3] = 4") + { + REQUIRE(M[0][3] == 4); + } + AND_THEN("M[1][0] = 5.5") + { + REQUIRE(M[1][0] == 5.5); + } + AND_THEN("M[1][2] = 7.5") + { + REQUIRE(M[1][2] == 7.5); + } + AND_THEN("M[2][2] = 11") + { + REQUIRE(M[2][2] == 11); + } + AND_THEN("M[3][0] = 13.5") + { + REQUIRE(M[3][0] == 13.5); + } + AND_THEN("M[3][2] = 15.5") + { + REQUIRE(M[3][2] == 15.5); + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[03][Matrix] A 2x2 matrix ought to be representable", "[Matrix]") +SCENARIO("A 2x2 matrix ought to be representable", "[features/matrices.feature]") { - Matrix m = { - {-3, 5}, - { 1, -2} - }; - - REQUIRE(m.rows() == 2); - REQUIRE(m.cols() == 2); - - REQUIRE(m[0][0] == -3); - REQUIRE(m[0][1] == 5); - REQUIRE(m[1][0] == 1); - REQUIRE(m[1][1] == -2); + GIVEN("the following 2x2 matrix M") + { + Matrix M = { + {-3, 5}, + { 1, -2} + }; + THEN("M.rows = 2") + { + REQUIRE(M.rows() == 2); + } + AND_THEN("M.cols = 2") + { + REQUIRE(M.cols() == 2); + } + AND_THEN("M[0][0] = -3") + { + REQUIRE(M[0][0] == -3); + } + AND_THEN("M[0][1] = 5") + { + REQUIRE(M[0][1] == 5); + } + AND_THEN("M[1][0] = 1") + { + REQUIRE(M[1][0] == 1); + } + AND_THEN("M[1][1] = -2") + { + REQUIRE(M[1][1] == -2); + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[03][Matrix] A 3x3 matrix ought to be representable", "[Matrix]") +SCENARIO("A 3x3 matrix ought to be representable", "[features/matrices.feature]") { - Matrix m = { - {-3, 5, 0}, - { 1, -2, -7}, - { 0, 1, 1} - }; + GIVEN("the following 3x3 matrix M") + { + Matrix M = { + {-3, 5, 0}, + { 1, -2, -7}, + { 0, 1, 1} + }; + THEN("M.rows = 3") + { + REQUIRE(M.rows() == 3); + } + AND_THEN("M.cols == 3") + { + REQUIRE(M.cols() == 3); + } - REQUIRE(m.rows() == 3); - REQUIRE(m.cols() == 3); - - REQUIRE(m[0][0] == -3); - REQUIRE(m[1][1] == -2); - REQUIRE(m[2][2] == 1); + AND_THEN("M[0][0] = -3") + { + REQUIRE(M[0][0] == -3); + } + AND_THEN("M[1][1] = -2") + { + REQUIRE(M[1][1] == -2); + } + AND_THEN("M[2][2] = 1") + { + REQUIRE(M[2][2] == 1); + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[03][Matrix] Matrix equality with identical matrices", "[Matrix]") +SCENARIO("Matrix equality with identical matrices", "[features/matrices.feature]") { - Matrix a = { - {1, 2, 3, 4}, - {5, 6, 7, 8}, - {9, 8, 7, 6}, - {5, 4, 3, 2} - }; - Matrix b = { - {1, 2, 3, 4}, - {5, 6, 7, 8}, - {9, 8, 7, 6}, - {5, 4, 3, 2} - }; - - REQUIRE(a == b); + GIVEN("the following matrix A") + { + Matrix A = { + {1, 2, 3, 4}, + {5, 6, 7, 8}, + {9, 8, 7, 6}, + {5, 4, 3, 2} + }; + AND_GIVEN("the following matrix B") + { + Matrix B = { + {1, 2, 3, 4}, + {5, 6, 7, 8}, + {9, 8, 7, 6}, + {5, 4, 3, 2} + }; + THEN("A = B") + { + REQUIRE(A == B); + } + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[03][Matrix] Matrix equality with different matrices", "[Matrix]") +SCENARIO("Matrix equality with different matrices", "[features/matrices.feature]") { - Matrix a = { - {1, 2, 3, 4}, - {5, 6, 7, 8}, - {9, 8, 7, 6}, - {5, 4, 3, 2} - }; - Matrix b = { - {2, 3, 4, 5}, - {6, 7, 8, 9}, - {8, 7, 6, 5}, - {4, 3, 2, 1} - }; - - REQUIRE(a != b); + GIVEN("the following matrix A") + { + Matrix A = { + {1, 2, 3, 4}, + {5, 6, 7, 8}, + {9, 8, 7, 6}, + {5, 4, 3, 2} + }; + AND_GIVEN("the following matrix B") + { + Matrix B = { + {2, 3, 4, 5}, + {6, 7, 8, 9}, + {8, 7, 6, 5}, + {4, 3, 2, 1} + }; + THEN("A != B") + { + REQUIRE(A != B); + } + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[03][Matrix] Multiplying two matrices", "[Matrix]") +SCENARIO("Multiplying two matrices", "[features/matrices.feature]") { - Matrix a = { - {1, 2, 3, 4}, - {5, 6, 7, 8}, - {9, 8, 7, 6}, - {5, 4, 3, 2} - }; - Matrix b = { - {-2, 1, 2, 3}, - { 3, 2, 1, -1}, - { 4, 3, 6, 5}, - { 1, 2, 7, 8} - }; - Matrix c = { - {20, 22, 50, 48}, - {44, 54, 114, 108}, - {40, 58, 110, 102}, - {16, 26, 46, 42} - }; - - REQUIRE((a * b) == c); + GIVEN("the following matrix A") + { + Matrix A = { + {1, 2, 3, 4}, + {5, 6, 7, 8}, + {9, 8, 7, 6}, + {5, 4, 3, 2} + }; + AND_GIVEN("the following matrix B") + { + Matrix B = { + {-2, 1, 2, 3}, + { 3, 2, 1, -1}, + { 4, 3, 6, 5}, + { 1, 2, 7, 8} + }; + AND_GIVEN("the following matrix C") + { + Matrix C = { + {20, 22, 50, 48}, + {44, 54, 114, 108}, + {40, 58, 110, 102}, + {16, 26, 46, 42} + }; + THEN("A * B = C") + REQUIRE((A * B) == C); + } + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[03][Matrix] a matrix multiplied by a tuple", "[Matrix]") +SCENARIO("A matrix multiplied by a tuple", "[features/matrices.feature]") { - Matrix a = { - {1, 2, 3, 4}, - {2, 4, 4, 2}, - {8, 6, 4, 1}, - {0, 0, 0, 1} - }; - Tuple b(1, 2, 3, 1); - - REQUIRE((a * b) == Tuple(18, 24, 33, 1)); + GIVEN("the following matrix A") + { + Matrix A = { + {1, 2, 3, 4}, + {2, 4, 4, 2}, + {8, 6, 4, 1}, + {0, 0, 0, 1} + }; + AND_GIVEN("b <- tuple(1, 2, 3, 1)") + { + Tuple b(1, 2, 3, 1); + THEN("A * b = tuple(18, 24, 33, 1)") + { + REQUIRE((A * b) == Tuple(18, 24, 33, 1)); + } + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[03][Matrix] Multiplying a matrix by the identity matrix", "[Matrix]") +SCENARIO("Multiplying a matrix by the identity matrix", "[features/matrices.feature]") { - Matrix a = { - {0, 1, 2, 4}, - {1, 2, 4, 8}, - {2, 4, 8, 16}, - {4, 8, 16, 32} - }; - - REQUIRE((a * Matrix::identity()) == a); + GIVEN("the following matrix A") + { + Matrix A = { + {0, 1, 2, 4}, + {1, 2, 4, 8}, + {2, 4, 8, 16}, + {4, 8, 16, 32} + }; + THEN("A * identity_matrix = A") + { + REQUIRE((A * Matrix::identity()) == A); + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[03][Matrix] Multiplying the identity matrix by a tuple", "[Matrix]") +SCENARIO("Multiplying the identity matrix by a tuple", "[features/matrices.feature]") { - Tuple a(1, 2, 3, 4); - - REQUIRE((Matrix::identity() * a) == a); + GIVEN("a <- tuple(1, 2, 3, 4)") + { + Tuple a(1, 2, 3, 4); + THEN("identity_matrix * a = a") + { + REQUIRE((Matrix::identity() * a) == a); + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[03][Matrix] Transposing a matrix", "[Matrix]") +SCENARIO("Transposing a matrix", "[features/matrices.feature]") { - Matrix a = { - {0, 9, 3, 0}, - {9, 8, 0, 8}, - {1, 8, 5, 3}, - {0, 0, 5, 8} - }; - Matrix transposed = { - {0, 9, 1, 0}, - {9, 8, 8, 0}, - {3, 0, 5, 5}, - {0, 8, 3, 8} - }; + GIVEN("the following matrix A") + { + Matrix A = { + {0, 9, 3, 0}, + {9, 8, 0, 8}, + {1, 8, 5, 3}, + {0, 0, 5, 8} + }; + THEN("transpose(A) is the following matrix") + { + Matrix transposed = { + {0, 9, 1, 0}, + {9, 8, 8, 0}, + {3, 0, 5, 5}, + {0, 8, 3, 8} + }; - a.transpose(); + A.transpose(); - REQUIRE(a == transposed); + REQUIRE(A == transposed); + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[03][Matrix] Transposing the identity matrix", "[Matrix]") +SCENARIO("Transposing the identity matrix", "[features/matrices.feature]") { - Matrix a = Matrix::identity(); + GIVEN("A <- transpose(identity_matrix)") + { + Matrix A = Matrix::identity(); - a.transpose(); - - REQUIRE(a == Matrix::identity()); + A.transpose(); + THEN("A = identity_matrix") + { + REQUIRE(A == Matrix::identity()); + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[03][Matrix] Calculating the determinant of a 2x2 matrix", "[Matrix]") +SCENARIO("Calculating the determinant of a 2x2 matrix", "[features/matrices.feature]") { - Matrix a = { - { 1, 5}, - {-3, 2} - }; - - REQUIRE(a.determinant() == 17); + GIVEN("the following 2x2 matrix A") + { + Matrix A = { + { 1, 5}, + {-3, 2} + }; + THEN("determinant(A) = 17") + { + REQUIRE(A.determinant() == 17); + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[03][Matrix] A submatrix of a 3x3 matrix is a 2x2 matrix", "[Matrix]") +SCENARIO("A submatrix of a 3x3 matrix is a 2x2 matrix", "[features/matrices.feature]") { - Matrix a = { - { 1, 5, 0}, - {-3, 2, 7}, - { 0, 6, -3} - }; + GIVEN("the following 3x3 matrix A") + { + Matrix A = { + { 1, 5, 0}, + {-3, 2, 7}, + { 0, 6, -3} + }; - Matrix b = { - {-3, 2}, - { 0, 6} - }; - - REQUIRE(a.sub_matrix(0, 2) == b); + Matrix B = { + {-3, 2}, + { 0, 6} + }; + THEN("submatrix(A,0,2) is the following 2x2 matrix") + { + REQUIRE(A.sub_matrix(0, 2) == B); + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[03][Matrix] A submatrix of a 4x4 matrix is a 3x3 matrix", "[Matrix]") +SCENARIO("A submatrix of a 4x4 matrix is a 3x3 matrix", "[features/matrices.feature]") { - Matrix a = { - {-6, 1, 1, 6}, - {-8, 5, 8, 6}, - {-1, 0, 8, 2}, - {-7, 1, -1, 1} - }; + GIVEN("the following 4x4 matrix A") + { + Matrix A = { + {-6, 1, 1, 6}, + {-8, 5, 8, 6}, + {-1, 0, 8, 2}, + {-7, 1, -1, 1} + }; - Matrix b = { - {-6, 1, 6}, - {-8, 8, 6}, - {-7, -1, 1} - }; - - REQUIRE(a.sub_matrix(2, 1) == b); + Matrix B = { + {-6, 1, 6}, + {-8, 8, 6}, + {-7, -1, 1} + }; + THEN("submatrix(B,2,1) is the following 3x3 matrix") + { + REQUIRE(A.sub_matrix(2, 1) == B); + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[03][Matrix] Calculating a minor of a 3x3 matrix", "[Matrix]") +SCENARIO("Calculating a minor of a 3x3 matrix", "[features/matrices.feature]") { - Matrix a = { - {3, 5, 0}, - {2, -1, -7}, - {6, -1, 5} - }; - - Matrix b = a.sub_matrix(1, 0); - - REQUIRE(b.determinant() == 25); - - REQUIRE(a.minor(1, 0) == 25); + GIVEN("the following 3x3 matrix A") + { + Matrix A = { + {3, 5, 0}, + {2, -1, -7}, + {6, -1, 5} + }; + AND_GIVEN("B <- submatrix(A,1,0)") + { + Matrix B = A.sub_matrix(1, 0); + THEN("determinant(B) = 25") + { + REQUIRE(B.determinant() == 25); + } + AND_THEN("minor(A,1,0)=25") + { + REQUIRE(A.minor(1, 0) == 25); + } + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[03][Matrix] Calculating a cofactor of a 3x3 matrix", "[Matrix]") +SCENARIO("Calculating a cofactor of a 3x3 matrix", "[features/matrices.feature]") { - Matrix a = { - {3, 5, 0}, - {2, -1, -7}, - {6, -1, 5} - }; - - REQUIRE(a.minor(0, 0) == -12); - REQUIRE(a.cofactor(0, 0) == -12); - REQUIRE(a.minor(1, 0) == 25); - REQUIRE(a.cofactor(1, 0) == -25); + GIVEN("the following 3x3 matrix A") + { + Matrix A = { + {3, 5, 0}, + {2, -1, -7}, + {6, -1, 5} + }; + THEN("minor(A, 0, 0) = -12") + { + REQUIRE(A.minor(0, 0) == -12); + } + AND_THEN("cofactor(A, 0, 0) = -12") + { + REQUIRE(A.cofactor(0, 0) == -12); + } + AND_THEN("minor(A, 1, 0) = 25") + { + REQUIRE(A.minor(1, 0) == 25); + } + AND_THEN("cofactor(A, 1, 0) = -25") + { + REQUIRE(A.cofactor(1, 0) == -25); + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[03][Matrix] Calculating the determinant of a 3x3 matrix", "[Matrix]") +SCENARIO("Calculating the determinant of a 3x3 matrix", "[features/matrices.feature]") { - Matrix a = { - { 1, 2, 6}, - {-5, 8, -4}, - { 2, 6, 4} - }; - - REQUIRE(a.cofactor(0, 0) == 56); - REQUIRE(a.cofactor(0, 1) == 12); - REQUIRE(a.cofactor(0, 2) == -46); - REQUIRE(a.determinant() == -196); + GIVEN("the following 3x3 matrix A") + { + Matrix A = { + { 1, 2, 6}, + {-5, 8, -4}, + { 2, 6, 4} + }; + THEN("cofactor(A, 0, 0) = 56") + { + REQUIRE(A.cofactor(0, 0) == 56); + } + AND_THEN("cofactor(A, 0, 1) = 12") + { + REQUIRE(A.cofactor(0, 1) == 12); + } + AND_THEN("cofactor(A, 0, 2) = -46") + { + REQUIRE(A.cofactor(0, 2) == -46); + } + AND_THEN("determinant(A) = -196") + { + REQUIRE(A.determinant() == -196); + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[03][Matrix] Calculating the determinant of a 4x4 matrix", "[Matrix]") +SCENARIO("Calculating the determinant of a 4x4 matrix", "[features/matrices.feature]") { - Matrix a = { - {-2, -8, 3, 5}, - {-3, 1, 7, 3}, - { 1, 2, -9, 6}, - {-6, 7, 7, -9} - }; - - REQUIRE(a.cofactor(0, 0) == 690); - REQUIRE(a.cofactor(0, 1) == 447); - REQUIRE(a.cofactor(0, 2) == 210); - REQUIRE(a.cofactor(0, 3) == 51); - REQUIRE(a.determinant() == -4071); + GIVEN("the following 4x4 matrix A") + { + Matrix A = { + {-2, -8, 3, 5}, + {-3, 1, 7, 3}, + { 1, 2, -9, 6}, + {-6, 7, 7, -9} + }; + THEN("cofactor(A, 0, 0) = 690") + { + REQUIRE(A.cofactor(0, 0) == 690); + } + AND_THEN("cofactor(A, 0, 1) = 447") + { + REQUIRE(A.cofactor(0, 1) == 447); + } + AND_THEN("cofactor(A, 0, 2) = 210") + { + REQUIRE(A.cofactor(0, 2) == 210); + } + AND_THEN("cofactor(A, 0, 3) = 51") + { + REQUIRE(A.cofactor(0, 3) == 51); + } + AND_THEN("determinant(A) = -4071") + { + REQUIRE(A.determinant() == -4071); + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[03][Matrix] Testing an invertible matrix for invertibility", "[Matrix]") +SCENARIO("Testing an invertible matrix for invertibility", "[features/matrices.feature]") { - Matrix a = { - {6, 4, 4, 4}, - {5, 5, 7, 6}, - {4, -9, 3, -7}, - {9, 1, 7, -6} - }; - - REQUIRE(a.determinant() == -2120); - REQUIRE(a.invertible() == true); + GIVEN("the following 4x4 matrix A") + { + Matrix A = { + {6, 4, 4, 4}, + {5, 5, 7, 6}, + {4, -9, 3, -7}, + {9, 1, 7, -6} + }; + THEN("determinant(A) = -2120") + { + REQUIRE(A.determinant() == -2120); + } + AND_THEN("A is invertible") + { + REQUIRE(A.invertible() == true); + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[03][Matrix] Testing an noninvertible matrix for invertibility", "[Matrix]") +SCENARIO("Testing an noninvertible matrix for invertibility", "[features/matrices.feature]") { - Matrix a = { - {-4, 2, -2, -3}, - { 9, 6, 2, 6}, - { 0, -5, 1, -5}, - { 0, 0, 0, 0} - }; - - REQUIRE(a.determinant() == 0); - REQUIRE(a.invertible() == false); + GIVEN("the following 4x4 matrix A") + { + Matrix A = { + {-4, 2, -2, -3}, + { 9, 6, 2, 6}, + { 0, -5, 1, -5}, + { 0, 0, 0, 0} + }; + THEN("determinant(A) = 0") + { + REQUIRE(A.determinant() == 0); + } + AND_THEN("A is not invertible") + { + REQUIRE(A.invertible() == false); + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[03][Matrix] Calculating the inverse of a matrix", "[Matrix]") +SCENARIO("Calculating the inverse of a matrix", "[features/matrices.feature]") { - Matrix a = { - {-5, 2, 6, -8}, - { 1, -5, 1, 8}, - { 7, 7, -6, -7}, - { 1, -3, 7, 4} - }; + GIVEN("the following 4x4 matrix A") + { + Matrix A = { + {-5, 2, 6, -8}, + { 1, -5, 1, 8}, + { 7, 7, -6, -7}, + { 1, -3, 7, 4} + }; - Matrix a_inverted = { - { 0.21805, 0.45113, 0.24060, -0.04511}, - {-0.80827, -1.45677, -0.44361, 0.52068}, - {-0.07895, -0.22368, -0.05263, 0.19737}, - {-0.52256, -0.81391, -0.30075, 0.30639} - }; - - Matrix b = a.inverse(); - - REQUIRE(a.determinant() == 532); - REQUIRE(a.cofactor(2, 3) == -160); - REQUIRE(b[3][2] == -160.0 / 532.0); - REQUIRE(a.cofactor(3, 2) == 105); - REQUIRE(b[2][3] == 105.0 / 532.0); - REQUIRE(b == a_inverted); + Matrix a_inverted = { + { 0.21805, 0.45113, 0.24060, -0.04511}, + {-0.80827, -1.45677, -0.44361, 0.52068}, + {-0.07895, -0.22368, -0.05263, 0.19737}, + {-0.52256, -0.81391, -0.30075, 0.30639} + }; + AND_GIVEN("B <- inverse(A)") + { + Matrix B = A.inverse(); + THEN("determinant(A) = 532") + { + REQUIRE(A.determinant() == 532); + } + AND_THEN("cofactor(A, 2, 3) = -160") + { + REQUIRE(A.cofactor(2, 3) == -160); + } + AND_THEN("B[3][2] = -160.0 / 532.0") + { + REQUIRE(B[3][2] == -160.0 / 532.0); + } + AND_THEN("cofactor(A, 3, 2) = 105") + { + REQUIRE(A.cofactor(3, 2) == 105); + } + AND_THEN("B[2][3] = 105.0 / 532.0") + { + REQUIRE(B[2][3] == 105.0 / 532.0); + } + AND_THEN("B is the following 4x4 matrix") + { + REQUIRE(B == a_inverted); + } + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[03][Matrix] Calculating the inverse of another matrix", "[Matrix]") +SCENARIO("Calculating the inverse of another matrix", "[features/matrices.feature]") { - Matrix a = { - { 8, -5, 9, 2}, - { 7, 5, 6, 1}, - {-6, 0, 9, 6}, - {-3, 0, -9, -4} - }; - Matrix a_inverted = { - {-0.15385, -0.15385, -0.28205, -0.53846}, - {-0.07692, 0.12308, 0.02564, 0.03077}, - { 0.35897, 0.35897, 0.43590, 0.92308}, - {-0.69231, -0.69231, -0.76923, -1.92308} - }; - - REQUIRE(a.inverse() == a_inverted); + GIVEN("the following 4x4 matrix A") + { + Matrix A = { + { 8, -5, 9, 2}, + { 7, 5, 6, 1}, + {-6, 0, 9, 6}, + {-3, 0, -9, -4} + }; + Matrix a_inverted = { + {-0.15385, -0.15385, -0.28205, -0.53846}, + {-0.07692, 0.12308, 0.02564, 0.03077}, + { 0.35897, 0.35897, 0.43590, 0.92308}, + {-0.69231, -0.69231, -0.76923, -1.92308} + }; + THEN("inverse(A) is the following matrix") + { + REQUIRE(A.inverse() == a_inverted); + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[03][Matrix] Calculating the inverse of third matrix", "[Matrix]") +SCENARIO("Calculating the inverse of third matrix", "[features/matrices.feature]") { - Matrix a = { - { 9, 3, 0, 9}, - {-5, -2, -6, -3}, - {-4, 9, 6, 4}, - {-7, 6, 6, 2} - }; - Matrix a_inverted = { - {-0.04074, -0.07778, 0.14444, -0.22222}, - {-0.07778, 0.03333, 0.36667, -0.33333}, - {-0.02901, -0.14630, -0.10926, 0.12963}, - { 0.17778, 0.06667, -0.26667, 0.33333} - }; - - REQUIRE(a.inverse() == a_inverted); + GIVEN("the following 4x4 matrix A") + { + Matrix A = { + { 9, 3, 0, 9}, + {-5, -2, -6, -3}, + {-4, 9, 6, 4}, + {-7, 6, 6, 2} + }; + Matrix a_inverted = { + {-0.04074, -0.07778, 0.14444, -0.22222}, + {-0.07778, 0.03333, 0.36667, -0.33333}, + {-0.02901, -0.14630, -0.10926, 0.12963}, + { 0.17778, 0.06667, -0.26667, 0.33333} + }; + THEN("inverse(A) is the following matrix") + { + REQUIRE(A.inverse() == a_inverted); + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[03][Matrix] Multiplying a product by its inverse", "[Matrix]") +SCENARIO("Multiplying a product by its inverse", "[features/matrices.feature]") { - Matrix a = { - { 3, -9, 7, 3}, - { 3, -8, 2, -9}, - {-4, 4, 4, 1}, - {-6, 5, -1, 1} - }; - Matrix b = { - {8, 2, 2, 2}, - {3, -1, 7, 0}, - {7, 0, 5, 4}, - {6, -2, 0, 5} - }; - Matrix c = a * b; - REQUIRE(c * b.inverse() == a); + GIVEN("the following 4x4 matrix A") + { + Matrix A = { + { 3, -9, 7, 3}, + { 3, -8, 2, -9}, + {-4, 4, 4, 1}, + {-6, 5, -1, 1} + }; + AND_GIVEN("the following 4x4 matrix B") + { + Matrix B = { + {8, 2, 2, 2}, + {3, -1, 7, 0}, + {7, 0, 5, 4}, + {6, -2, 0, 5} + }; + AND_GIVEN("C <- A * B") + { + Matrix C = A * B; + THEN("C * inverse(B) = A") + { + REQUIRE(C * B.inverse() == A); + } + } + } + } } diff --git a/tests/04_transformations.cpp b/tests/04_transformations.cpp index cdb6c2b..2ae9608 100644 --- a/tests/04_transformations.cpp +++ b/tests/04_transformations.cpp @@ -33,204 +33,408 @@ using namespace Raytracer; /* ------------------------------------------------------------------------- */ -TEST_CASE("[04][Trans] Multiplying by a translation matrix", "[Matrix]") +SCENARIO("Multiplying by a translation matrix", "[features/transformations.feature]") { - Matrix transform = Matrix::translation(5, -3, 2); - Tuple p = Tuple::Point(-3, 4, 5); - - REQUIRE(transform * p == Tuple::Point(2, 1, 7)); + GIVEN("transform <- translation(5, -3, 2)") + { + Matrix transform = Matrix::translation(5, -3, 2); + AND_GIVEN("p <- point(-3, 4, 5)") + { + Tuple p = Tuple::Point(-3, 4, 5); + THEN("transform * p = point(2, 1, 7)") + { + REQUIRE(transform * p == Tuple::Point(2, 1, 7)); + } + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[04][Trans] Multiplying by the inverse of a translation matrix", "[Matrix]") +SCENARIO("Multiplying by the inverse of a translation matrix", "[features/transformations.feature]") { - Matrix transform = Matrix::translation(5, -3, 2); - Matrix inv = transform.inverse(); - Tuple p = Tuple::Point(-3, 4, 5); - - REQUIRE(inv * p == Tuple::Point(-8, 7, 3)); + GIVEN("transform <- translation(5, -3, 2)") + { + Matrix transform = Matrix::translation(5, -3, 2); + AND_GIVEN("inv <- inverse(transform)") + { + Matrix inv = transform.inverse(); + AND_GIVEN("p <- point(-3, 4, 5)") + { + Tuple p = Tuple::Point(-3, 4, 5); + THEN("inv * p = point(-8, 7, 3)") + { + REQUIRE(inv * p == Tuple::Point(-8, 7, 3)); + } + } + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[04][Trans] Translation does not affect vectors", "[Matrix]") +SCENARIO("Translation does not affect vectors", "[features/transformations.feature]") { - Matrix transform = Matrix::translation(5, -3, 2); - Tuple v = Tuple::Vector(-3, 4, 5); - - REQUIRE(transform * v == v); + GIVEN("transform <- translation(5, -3, 2)") + { + Matrix transform = Matrix::translation(5, -3, 2); + AND_GIVEN("v <- vector(-3, 4, 5)") + { + Tuple v = Tuple::Vector(-3, 4, 5); + THEN("transform * v = v") + { + REQUIRE(transform * v == v); + } + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[04][Trans] A scaling matrix applied to a point", "[Matrix]") +SCENARIO("A scaling matrix applied to a point", "[features/transformations.feature]") { - Matrix transform = Matrix::scaling(2, 3, 4); - Tuple p = Tuple::Point(-4, 6, 8); - - REQUIRE(transform * p == Tuple::Point(-8, 18, 32)); + GIVEN("transform <- scaling(2, 3, 4)") + { + Matrix transform = Matrix::scaling(2, 3, 4); + AND_GIVEN("p <- point(-4, 6, 8)") + { + Tuple p = Tuple::Point(-4, 6, 8); + THEN("transform * p = point(-8, 18, 32)") + { + REQUIRE(transform * p == Tuple::Point(-8, 18, 32)); + } + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[04][Trans] A scaling matrix applied to a vector", "[Matrix]") +SCENARIO("A scaling matrix applied to a vector", "[features/transformations.feature]") { - Matrix transform = Matrix::scaling(2, 3, 4); - Tuple v = Tuple::Vector(-4, 6, 8); - - REQUIRE(transform * v == Tuple::Vector(-8, 18, 32)); + GIVEN("transform <- scaling(2, 3, 4)") + { + Matrix transform = Matrix::scaling(2, 3, 4); + AND_GIVEN("v <- vector(-4, 6, 8)") + { + Tuple v = Tuple::Vector(-4, 6, 8); + THEN("transform * p = vector(-8, 18, 32)") + { + REQUIRE(transform * v == Tuple::Vector(-8, 18, 32)); + } + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[04][Trans] Multiplying by the inverse of a scaling matrix", "[Matrix]") +SCENARIO("Multiplying by the inverse of a scaling matrix", "[features/transformations.feature]") { - Matrix transform = Matrix::scaling(2, 3, 4); - Matrix inv = transform.inverse(); - Tuple v = Tuple::Vector(-4, 6, 8); - - REQUIRE(inv * v == Tuple::Vector(-2, 2, 2)); + GIVEN("transform <- scaling(2, 3, 4)") + { + Matrix transform = Matrix::scaling(2, 3, 4); + AND_GIVEN("inv <- inverse(transform)") + { + Matrix inv = transform.inverse(); + AND_GIVEN("v <- vector(-4, 6, 8)") + { + Tuple v = Tuple::Vector(-4, 6, 8); + THEN("inv * v = vector(-2, 2, 2)") + { + REQUIRE(inv * v == Tuple::Vector(-2, 2, 2)); + } + } + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[04][Trans] Reflection is scaling by a negative value", "[Matrix]") +SCENARIO("Reflection is scaling by a negative value", "[features/transformations.feature]") { - Matrix transform = Matrix::scaling(-1, 1, 1); - Tuple p = Tuple::Point(2, 3, 4); - - REQUIRE(transform * p == Tuple::Point(-2, 3, 4)); + GIVEN("transform <- scaling(-1, 1, 1)") + { + Matrix transform = Matrix::scaling(-1, 1, 1); + AND_GIVEN("p <- point(2, 3, 4)") + { + Tuple p = Tuple::Point(2, 3, 4); + THEN("transform * p = point(-2, 3, 4)") + { + REQUIRE(transform * p == Tuple::Point(-2, 3, 4)); + } + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[04][Trans] Rotating a point around the x axis", "[Matrix]") +SCENARIO("Rotating a point around the x axis", "[features/transformations.feature]") { - Tuple p = Tuple::Point(0, 1, 0); - Matrix half_quarter = Matrix::rotation_x(std::numbers::pi / 4); - Matrix full_quarter = Matrix::rotation_x(std::numbers::pi / 2); - - REQUIRE(half_quarter * p == Tuple::Point(0, sqrt(2) / 2, sqrt(2) / 2)); - REQUIRE(full_quarter * p == Tuple::Point(0, 0, 1)); + GIVEN("p <- point(0, 1, 0)") + { + Tuple p = Tuple::Point(0, 1, 0); + AND_GIVEN("half_quarter <- rotation_x(pi/4)") + { + Matrix half_quarter = Matrix::rotation_x(std::numbers::pi / 4); + AND_GIVEN("full_quarter <- rotation_x(pi/2)") + { + Matrix full_quarter = Matrix::rotation_x(std::numbers::pi / 2); + THEN("half_quarter * p = point(0, sqrt(2) / 2, sqrt(2) / 2)") + { + REQUIRE(half_quarter * p == Tuple::Point(0, sqrt(2) / 2, sqrt(2) / 2)); + } + AND_THEN("full_quarter * p == point(0, 0, 1)") + { + REQUIRE(full_quarter * p == Tuple::Point(0, 0, 1)); + } + } + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[04][Trans] The inverse of an x-rotation rotates in the opposite direction", "[Matrix]") +SCENARIO("The inverse of an x-rotation rotates in the opposite direction", "[features/transformations.feature]") { - Tuple p = Tuple::Point(0, 1, 0); - Matrix half_quarter = Matrix::rotation_x(std::numbers::pi / 4); - Matrix inv = half_quarter.inverse(); - - REQUIRE(inv * p == Tuple::Point(0, sqrt(2) / 2, -sqrt(2) / 2)); + GIVEN("p <- point(0, 1, 0)") + { + Tuple p = Tuple::Point(0, 1, 0); + AND_GIVEN("half_quarter <- rotation_x(pi/4)") + { + Matrix half_quarter = Matrix::rotation_x(std::numbers::pi / 4); + AND_GIVEN("inv <- inverse(half_quarter)") + { + Matrix inv = half_quarter.inverse(); + THEN("inv * p = point(0, sqrt(2) / 2, -sqrt(2) / 2)") + { + REQUIRE(inv * p == Tuple::Point(0, sqrt(2) / 2, -sqrt(2) / 2)); + } + } + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[04][Trans] Rotating a point around the y axis", "[Matrix]") +SCENARIO("Rotating a point around the y axis", "[features/transformations.feature]") { - Tuple p = Tuple::Point(0, 0, 1); - Matrix half_quarter = Matrix::rotation_y(std::numbers::pi / 4); - Matrix full_quarter = Matrix::rotation_y(std::numbers::pi / 2); - - REQUIRE(half_quarter * p == Tuple::Point(sqrt(2) / 2, 0, sqrt(2) / 2)); - REQUIRE(full_quarter * p == Tuple::Point(1, 0, 0)); + GIVEN("p <- point(0, 1, 0)") + { + Tuple p = Tuple::Point(0, 0, 1); + AND_GIVEN("half_quarter <- rotation_y(pi/4)") + { + Matrix half_quarter = Matrix::rotation_y(std::numbers::pi / 4); + AND_GIVEN("full_quarter <- rotation_y(pi/2)") + { + Matrix full_quarter = Matrix::rotation_y(std::numbers::pi / 2); + THEN("half_quarter * p = point(sqrt(2) / 2, 0, sqrt(2) / 2)") + { + REQUIRE(half_quarter * p == Tuple::Point(sqrt(2) / 2, 0, sqrt(2) / 2)); + } + AND_THEN("full_quarter * p = point(1, 0, 0)") + { + REQUIRE(full_quarter * p == Tuple::Point(1, 0, 0)); + } + } + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[04][Trans] Rotating a point around the z axis", "[Matrix]") +SCENARIO("Rotating a point around the z axis", "[features/transformations.feature]") { - Tuple p = Tuple::Point(0, 1, 0); - Matrix half_quarter = Matrix::rotation_z(std::numbers::pi / 4); - Matrix full_quarter = Matrix::rotation_z(std::numbers::pi / 2); + GIVEN("p <- point(0, 1, 0)") + { + Tuple p = Tuple::Point(0, 1, 0); + AND_GIVEN("full_quarter <- rotation_z(pi/4)") + { + Matrix half_quarter = Matrix::rotation_z(std::numbers::pi / 4); + AND_GIVEN("full_quarter <- rotation_z(pi/2)") + { + Matrix full_quarter = Matrix::rotation_z(std::numbers::pi / 2); - Tuple z = half_quarter * p; - - REQUIRE(half_quarter * p == Tuple::Point(-sqrt(2) / 2, sqrt(2) / 2, 0)); - REQUIRE(full_quarter * p == Tuple::Point(-1, 0, 0)); + Tuple z = half_quarter * p; + THEN("half_quarter * p = point(-sqrt(2) / 2, sqrt(2) / 2, 0)") + { + REQUIRE(half_quarter * p == Tuple::Point(-sqrt(2) / 2, sqrt(2) / 2, 0)); + } + AND_THEN("full_quarter * p = point(-1, 0, 0)") + { + REQUIRE(full_quarter * p == Tuple::Point(-1, 0, 0)); + } + } + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[04][Trans] A shearing transformation moves x in proportion to y", "[Matrix]") +SCENARIO("A shearing transformation moves x in proportion to y", "[features/transformations.feature]") { - Matrix transform = Matrix::shearing(1, 0, 0, 0, 0, 0); - Tuple p = Tuple::Point(2, 3, 4); - - REQUIRE(transform * p == Tuple::Point(5, 3, 4)); + GIVEN("transform <- shearing(1, 0, 0, 0, 0, 0)") + { + Matrix transform = Matrix::shearing(1, 0, 0, 0, 0, 0); + AND_GIVEN("p <- point(2, 3, 4)") + { + Tuple p = Tuple::Point(2, 3, 4); + THEN("transform * p == point(5, 3, 4)") + { + REQUIRE(transform * p == Tuple::Point(5, 3, 4)); + } + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[04][Trans] A shearing transformation moves y in proportion to x", "[Matrix]") +SCENARIO("A shearing transformation moves y in proportion to x", "[features/transformations.feature]") { - Matrix transform = Matrix::shearing(0, 0, 1, 0, 0, 0); - Tuple p = Tuple::Point(2, 3, 4); - - REQUIRE(transform * p == Tuple::Point(2, 5, 4)); + GIVEN("transform <- shearing(0, 0, 1, 0, 0, 0)") + { + Matrix transform = Matrix::shearing(0, 0, 1, 0, 0, 0); + AND_GIVEN("p <- point(2, 3, 4)") + { + Tuple p = Tuple::Point(2, 3, 4); + THEN("transform * p == point(2, 5, 4)") + { + REQUIRE(transform * p == Tuple::Point(2, 5, 4)); + } + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[04][Trans] A shearing transformation moves y in proportion to z", "[Matrix]") +SCENARIO("A shearing transformation moves y in proportion to z", "[features/transformations.feature]") { - Matrix transform = Matrix::shearing(0, 0, 0, 1, 0, 0); - Tuple p = Tuple::Point(2, 3, 4); - - REQUIRE(transform * p == Tuple::Point(2, 7, 4)); + GIVEN("transform <- shearing(0, 0, 0, 1, 0, 0)") + { + Matrix transform = Matrix::shearing(0, 0, 0, 1, 0, 0); + AND_GIVEN("p <- point(2, 3, 4)") + { + Tuple p = Tuple::Point(2, 3, 4); + THEN("transform * p == point(2, 7, 4)") + { + REQUIRE(transform * p == Tuple::Point(2, 7, 4)); + } + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[04][Trans] A shearing transformation moves z in proportion to x", "[Matrix]") +SCENARIO("A shearing transformation moves z in proportion to x", "[features/transformations.feature]") { - Matrix transform = Matrix::shearing(0, 0, 0, 0, 1, 0); - Tuple p = Tuple::Point(2, 3, 4); - - REQUIRE(transform * p == Tuple::Point(2, 3, 6)); + GIVEN("transform <- shearing(0, 0, 0, 0, 1, 0)") + { + Matrix transform = Matrix::shearing(0, 0, 0, 0, 1, 0); + AND_GIVEN("p <- point(2, 3, 4)") + { + Tuple p = Tuple::Point(2, 3, 4); + THEN("transform * p == point(2, 3, 6)") + { + REQUIRE(transform * p == Tuple::Point(2, 3, 6)); + } + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[04][Trans] A shearing transformation moves z in proportion to y", "[Matrix]") +SCENARIO("A shearing transformation moves z in proportion to y", "[features/transformations.feature]") { - Matrix transform = Matrix::shearing(0, 0, 0, 0, 0, 1); - Tuple p = Tuple::Point(2, 3, 4); - - REQUIRE(transform * p == Tuple::Point(2, 3, 7)); + GIVEN("transform <- shearing(0, 0, 0, 0, 0, 1)") + { + Matrix transform = Matrix::shearing(0, 0, 0, 0, 0, 1); + AND_GIVEN("p <- point(2, 3, 4)") + { + Tuple p = Tuple::Point(2, 3, 4); + THEN("transform * p == point(2, 3, 7)") + { + REQUIRE(transform * p == Tuple::Point(2, 3, 7)); + } + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[04][Trans] Individual transformations are applied in sequence", "[Matrix]") +SCENARIO("Individual transformations are applied in sequence", "[features/transformations.feature]") { - Tuple p = Tuple::Point(1, 0, 1); - Matrix a = Matrix::rotation_x(std::numbers::pi / 2); - Matrix b = Matrix::scaling(5, 5, 5); - Matrix c = Matrix::translation(10, 5, 7); + GIVEN("p <- point(1, 0, 1)") + { + Tuple p = Tuple::Point(1, 0, 1); + AND_GIVEN("A <- rotation_x(pi/2)") + { + Matrix A = Matrix::rotation_x(std::numbers::pi / 2); + AND_GIVEN("B <- scaling(5, 5, 5)") + { + Matrix B = Matrix::scaling(5, 5, 5); + AND_GIVEN("C <- translation(10, 5, 7))") + { + Matrix C = Matrix::translation(10, 5, 7); - // Appply rotation first. - Tuple p2 = a * p; - REQUIRE(p2 == Tuple::Point(1, -1, 0)); - // Then Apply scaling - Tuple p3 = b * p2; - REQUIRE(p3 == Tuple::Point(5, -5, 0)); - // Then Apply translation - Tuple p4 = c * p3; - REQUIRE(p4 == Tuple::Point(15, 0, 7)); + // Apply rotation first. + WHEN("p2 <- A * p") + { + Tuple p2 = A * p; + THEN("p2 = point(1, -1, 0)") + { + REQUIRE(p2 == Tuple::Point(1, -1, 0)); + } + + // Then Apply scaling + WHEN("p3 <- B * p2") + { + Tuple p3 = B * p2; + THEN("p3 = point(5, -5, 0)") + { + REQUIRE(p3 == Tuple::Point(5, -5, 0)); + } + // Then Apply translation + WHEN("p4 = C * p3") + + { + Tuple p4 = C * p3; + THEN("p4 = point(15, 0, 7)") + { + REQUIRE(p4 == Tuple::Point(15, 0, 7)); + } + } + } + } + } + } + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[04][Trans] Chained transformation must be applied in rever order", "[Matrix]") +SCENARIO("Chained transformation must be applied in revert order", "[features/transformations.feature]") { - Tuple p = Tuple::Point(1, 0, 1); - Matrix a = Matrix::rotation_x(std::numbers::pi / 2); - Matrix b = Matrix::scaling(5, 5, 5); - Matrix c = Matrix::translation(10, 5, 7); - Matrix t = c * b * a; - - REQUIRE(t * p == Tuple::Point(15, 0, 7)); + GIVEN("p <- point(1, 0, 1)") + { + Tuple p = Tuple::Point(1, 0, 1); + AND_GIVEN("A <- rotation_x(pi/2)") + { + Matrix A = Matrix::rotation_x(std::numbers::pi / 2); + AND_GIVEN("B <- scaling(5, 5, 5)") + { + Matrix B = Matrix::scaling(5, 5, 5); + AND_GIVEN("C <- translation(10, 5, 7))") + { + Matrix C = Matrix::translation(10, 5, 7); + WHEN("t <- C * B * A") + { + Matrix T = C * B * A; + THEN("T * p == point(15, 0, 7)") + { + REQUIRE(T * p == Tuple::Point(15, 0, 7)); + } + } + } + } + } + } } diff --git a/tests/05_rays.cpp b/tests/05_rays.cpp index 1515470..f74fcac 100644 --- a/tests/05_rays.cpp +++ b/tests/05_rays.cpp @@ -35,31 +35,59 @@ using namespace Raytracer; /* ------------------------------------------------------------------------- */ -TEST_CASE("[05][Rays] Creating and querying a ray", "[Rays]") +SCENARIO("Creating and querying a ray", "[features/rays.feature]") { - Tuple origin = Tuple::Point(1, 2, 3); - Tuple direction = Tuple::Vector(4, 5, 6); - Ray r(origin, direction); - - REQUIRE(r.origin() == origin); - REQUIRE(r.direction() == direction); + GIVEN("origin <- point(1, 2, 3)") + { + Tuple origin = Tuple::Point(1, 2, 3); + AND_GIVEN("direction <- vector(4, 5, 6)") + { + Tuple direction = Tuple::Vector(4, 5, 6); + WHEN("r <- ray(origin, direction)") + { + Ray r(origin, direction); + THEN("r.origin = origin") + { + REQUIRE(r.origin() == origin); + } + AND_THEN("r.direction = direction") + { + REQUIRE(r.direction() == direction); + } + } + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[05][Rays] Computing a point from a distance", "[Rays]") +SCENARIO("Computing a point from a distance", "[features/rays.feature]") { - Ray r(Tuple::Point(2, 3, 4), Tuple::Vector(1, 0, 0)); - - REQUIRE(r.position(0) == Tuple::Point(2, 3, 4)); - REQUIRE(r.position(1) == Tuple::Point(3, 3, 4)); - REQUIRE(r.position(-1) == Tuple::Point(1, 3, 4)); - REQUIRE(r.position(2.5) == Tuple::Point(4.5, 3, 4)); + GIVEN("r <- ray(point(2, 3, 4), vector(1, 0, 0))") + { + Ray r(Tuple::Point(2, 3, 4), Tuple::Vector(1, 0, 0)); + THEN("position(r,0) = point(2, 3, 4)") + { + REQUIRE(r.position(0) == Tuple::Point(2, 3, 4)); + } + AND_THEN("position(r,1) == point(3, 3, 4)") + { + REQUIRE(r.position(1) == Tuple::Point(3, 3, 4)); + } + AND_THEN("position(r,-1) = point(1, 3, 4)") + { + REQUIRE(r.position(-1) == Tuple::Point(1, 3, 4)); + } + AND_THEN("position(r,2.5) == point(4.5, 3, 4)") + { + REQUIRE(r.position(2.5) == Tuple::Point(4.5, 3, 4)); + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[05][Rays] a ray intersects a sphere at two points", "[Sphere]") +SCENARIO("A ray intersects a sphere at two points", "[features/spheres.feature]") { Ray r(Tuple::Point(0, 0, -5), Tuple::Vector(0, 0, 1)); Sphere s; @@ -72,7 +100,7 @@ TEST_CASE("[05][Rays] a ray intersects a sphere at two points", "[Sphere]") /* ------------------------------------------------------------------------- */ -TEST_CASE("[05][Rays] a ray intersects a sphere at a tangent", "[Sphere]") +SCENARIO("A ray intersects a sphere at a tangent", "[features/spheres.feature]") { Ray r(Tuple::Point(0, 1, -5), Tuple::Vector(0, 0, 1)); Sphere s; @@ -85,7 +113,7 @@ TEST_CASE("[05][Rays] a ray intersects a sphere at a tangent", "[Sphere]") /* ------------------------------------------------------------------------- */ -TEST_CASE("[05][Rays] a ray misses a sphere", "[Sphere]") +SCENARIO("A ray misses a sphere", "[features/spheres.feature]") { Ray r(Tuple::Point(0, 2, -5), Tuple::Vector(0, 0, 1)); Sphere s; @@ -96,7 +124,7 @@ TEST_CASE("[05][Rays] a ray misses a sphere", "[Sphere]") /* ------------------------------------------------------------------------- */ -TEST_CASE("[05][Rays] a originates inside a sphere", "[Sphere]") +SCENARIO("A originates inside a sphere", "[features/spheres.feature]") { Ray r(Tuple::Point(0, 0, 0), Tuple::Vector(0, 0, 1)); Sphere s; @@ -109,7 +137,7 @@ TEST_CASE("[05][Rays] a originates inside a sphere", "[Sphere]") /* ------------------------------------------------------------------------- */ -TEST_CASE("[05][Rays] a sphere is behind a ray", "[Sphere]") +SCENARIO("A sphere is behind a ray", "[features/spheres.feature]") { Ray r(Tuple::Point(0, 0, 5), Tuple::Vector(0, 0, 1)); Sphere s; @@ -122,7 +150,7 @@ TEST_CASE("[05][Rays] a sphere is behind a ray", "[Sphere]") /* ------------------------------------------------------------------------- */ -TEST_CASE("[05][Rays] Test Sphere Object", "[Sphere]") +SCENARIO("Test Sphere Object", "[features/spheres.feature]") { Sphere s1; Sphere s2 = s1; @@ -134,7 +162,7 @@ TEST_CASE("[05][Rays] Test Sphere Object", "[Sphere]") /* ------------------------------------------------------------------------- */ -TEST_CASE("[05][Rays] An intersection encapsulates t and object", "[Intersections]") +SCENARIO("An intersection encapsulates t and object", "[features/intersections.feature]") { Sphere s; Intersection i(3.5, s); @@ -145,7 +173,7 @@ TEST_CASE("[05][Rays] An intersection encapsulates t and object", "[Intersection /* ------------------------------------------------------------------------- */ -TEST_CASE("[05][Rays] Aggregating intersections", "[Intersections]") +SCENARIO("Aggregating intersections", "[features/intersections.feature]") { Sphere s; Intersection i1(1, s); @@ -159,7 +187,7 @@ TEST_CASE("[05][Rays] Aggregating intersections", "[Intersections]") /* ------------------------------------------------------------------------- */ -TEST_CASE("[05][Rays] Intersect set the object on the intersection", "[Intersections]") +SCENARIO("Intersect sets the object on the intersection", "[features/spheres.feature]") { Ray r(Tuple::Point(0, 0, 5), Tuple::Vector(0, 0, 1)); Sphere s; @@ -172,7 +200,7 @@ TEST_CASE("[05][Rays] Intersect set the object on the intersection", "[Intersect /* ------------------------------------------------------------------------- */ -TEST_CASE("[05][Rays] The hit, when all intersections have positive t", "[Intersections]") +SCENARIO("The hit, when all intersections have positive t", "[features/intersections.feature]") { Sphere s; Intersection i1(1, s); @@ -186,7 +214,7 @@ TEST_CASE("[05][Rays] The hit, when all intersections have positive t", "[Inters /* ------------------------------------------------------------------------- */ -TEST_CASE("[05][Rays] The hit, when some intersections have negative t", "[Intersections]") +SCENARIO("The hit, when some intersections have negative t", "[features/intersections.feature]") { Sphere s; Intersection i1(-1, s); @@ -200,7 +228,7 @@ TEST_CASE("[05][Rays] The hit, when some intersections have negative t", "[Inter /* ------------------------------------------------------------------------- */ -TEST_CASE("[05][Rays] The hit, when all intersections have negative t", "[Intersections]") +SCENARIO("The hit, when all intersections have negative t", "[features/intersections.feature]") { Sphere s; Intersection i1(-2, s); @@ -214,7 +242,7 @@ TEST_CASE("[05][Rays] The hit, when all intersections have negative t", "[Inters /* ------------------------------------------------------------------------- */ -TEST_CASE("[05][Rays] The hit is always the lowest nonnegative intersection", "[Intersections]") +SCENARIO("The hit is always the lowest nonnegative intersection", "[features/intersections.feature]") { Sphere s; Intersection i1(5, s); @@ -230,7 +258,7 @@ TEST_CASE("[05][Rays] The hit is always the lowest nonnegative intersection", "[ /* ------------------------------------------------------------------------- */ -TEST_CASE("[05][Rays] Translating a ray", "[Rays]") +SCENARIO("Translating a ray", "[features/rays.feature]") { Ray r(Tuple::Point(1, 2, 3), Tuple::Vector(0, 1, 0)); Matrix m = Matrix::translation(3, 4, 5); @@ -242,7 +270,7 @@ TEST_CASE("[05][Rays] Translating a ray", "[Rays]") /* ------------------------------------------------------------------------- */ -TEST_CASE("[05][Rays] Scaling a ray", "[Rays]") +SCENARIO("Scaling a ray", "[features/rays.feature]") { Ray r(Tuple::Point(1, 2, 3), Tuple::Vector(0, 1, 0)); Matrix m = Matrix::scaling(2, 3, 4); @@ -254,15 +282,21 @@ TEST_CASE("[05][Rays] Scaling a ray", "[Rays]") /* ------------------------------------------------------------------------- */ -TEST_CASE("[05][Rays] A sphere's default transformation", "[Sphere]") +SCENARIO("A sphere's default transformation", "[features/spheres.feature]") { - Sphere s; - REQUIRE(s.transform() == Matrix::identity()); + GIVEN("s <- Sphere()") + { + Sphere s; + THEN("s.transform = identity_matrix") + { + REQUIRE(s.transform() == Matrix::identity()); + } + } } /* ------------------------------------------------------------------------- */ -TEST_CASE("[05][Rays] Changing a sphere's transformation", "[Sphere]") +SCENARIO("Changing a sphere's transformation", "[features/spheres.feature]") { Sphere s; Matrix t = Matrix::translation(2, 3, 4); @@ -271,8 +305,8 @@ TEST_CASE("[05][Rays] Changing a sphere's transformation", "[Sphere]") } /* ------------------------------------------------------------------------- */ -#if 0 -TEST_CASE("[05][Rays] Intersecting a scaled sphere with a ray", "[Sphere]") + +SCENARIO("Intersecting a scaled sphere with a ray", "[features/spheres.feature]") { Ray r(Tuple::Point(0, 0, -5), Tuple::Vector(0, 0, 1)); Sphere s; @@ -283,10 +317,10 @@ TEST_CASE("[05][Rays] Intersecting a scaled sphere with a ray", "[Sphere]") REQUIRE(xs[0].distance_t() == 3); REQUIRE(xs[1].distance_t() == 7); } -#endif + /* ------------------------------------------------------------------------- */ -TEST_CASE("[05][Rays] Intersecting a translated sphere with a ray", "[Sphere]") +SCENARIO("Intersecting a translated sphere with a ray", "[features/spheres.feature]") { Ray r(Tuple::Point(0, 0, -5), Tuple::Vector(0, 0, 1)); Sphere s;