commit e5b29bdc819f9240e3e026199af2383bbe798cbf Author: Zack Buhman Date: Sun Jul 21 22:28:32 2024 -0500 chapter 1 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c53e339 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.o +*.gch +test/test_tuples \ No newline at end of file diff --git a/test/runner.h b/test/runner.h new file mode 100644 index 0000000..0507bed --- /dev/null +++ b/test/runner.h @@ -0,0 +1,19 @@ +#pragma once + +typedef bool (*test_t)(const char ** scenario); + +#define RUNNER(tests) \ + int main() \ + { \ + int fail_count = 0; \ + for (int i = 0; i < (sizeof (tests)) / (sizeof (test_t)); i++) { \ + const char * scenario; \ + bool result = tests[i](&scenario); \ + const char * result_s = result ? "ok" : "fail"; \ + fail_count += !result; \ + fprintf(stderr, "%s: %s\n", scenario, result_s); \ + } \ + fprintf(stderr, "\nfailed tests: %d\n", fail_count); \ + \ + return !(fail_count == 0); \ + } diff --git a/test/test_tuples.c b/test/test_tuples.c new file mode 100644 index 0000000..359abd5 --- /dev/null +++ b/test/test_tuples.c @@ -0,0 +1,223 @@ +#include +#include + +#include "tuples.h" +#include "runner.h" + +static bool tuple_test_0(const char ** scenario) +{ + *scenario = "A tuple with w=1.0 is a point"; + + struct tuple a = tuple(4.3f, -4.2f, 3.1f, 1.0f); + return + a.x == 4.3f && + a.y == -4.2f && + a.z == 3.1f && + a.w == 1.0f && + tuple_is_point(a) && + !tuple_is_vector(a) + ; +} + +static bool tuple_test_1(const char ** scenario) +{ + *scenario = "A tuple with w=0.0 is a vector"; + + struct tuple a = tuple(4.3f, -4.2f, 3.1f, 0.0f); + return + a.x == 4.3f && + a.y == -4.2f && + a.z == 3.1f && + a.w == 0.0f && + !tuple_is_point(a) && + tuple_is_vector(a) + ; +} + +static bool tuple_test_2(const char ** scenario) +{ + *scenario = "point() creates tuples with w=1.0"; + struct tuple v = point(4.0f, -4.0f, 3.0f); + return tuple_equal(v, tuple(4.0f, -4.0f, 3.0f, 1.0f)); +} + +static bool tuple_test_3(const char ** scenario) +{ + *scenario = "vector() creates tuples with w=0.0"; + struct tuple v = vector(4.0f, -4.0f, 3.0f); + return tuple_equal(v, tuple(4.0f, -4.0f, 3.0f, 0.0f)); +} + +static bool tuple_test_4(const char ** scenario) +{ + *scenario = "Adding two tuples"; + struct tuple a1 = tuple(3.0f, -2.0f, 5.0f, 1.0f); + struct tuple a2 = tuple(-2.0f, 3.0f, 1.0f, 0.0f); + return tuple_equal(tuple_add(a1, a2), point(1.0f, 1.0f, 6.0f)); +} + +static bool tuple_test_5(const char ** scenario) +{ + *scenario = "Subtracting two points"; + struct tuple p1 = point(3.0f, 2.0f, 1.0f); + struct tuple p2 = point(5.0f, 6.0f, 7.0f); + return tuple_equal(tuple_sub(p1, p2), vector(-2.0f, -4.0f, -6.0f)); +} + +static bool tuple_test_6(const char ** scenario) +{ + *scenario = "Subtracting a vector from a point"; + struct tuple p = point(3.0f, 2.0f, 1.0f); + struct tuple v = vector(5.0f, 6.0f, 7.0f); + return tuple_equal(tuple_sub(p, v), point(-2.0f, -4.0f, -6.0f)); +} + +static bool tuple_test_7(const char ** scenario) +{ + *scenario = "Subtracting two vectors"; + struct tuple v1 = vector(3.0f, 2.0f, 1.0f); + struct tuple v2 = vector(5.0f, 6.0f, 7.0f); + return tuple_equal(tuple_sub(v1, v2), vector(-2.0f, -4.0f, -6.0f)); +} + +static bool tuple_test_8(const char ** scenario) +{ + *scenario = "Subtracting a vector from the zero vector"; + struct tuple zero = vector(0.0f, 0.0f, 0.0f); + struct tuple v = vector(1.0f, -2.0f, 3.0f); + return tuple_equal(tuple_sub(zero, v), vector(-1.0f, 2.0f, -3.0f)); +} + +static bool tuple_test_9(const char ** scenario) +{ + *scenario = "Negating a tuple"; + struct tuple a = tuple(1.0f, -2.0f, 3.0f, -4.0f); + return tuple_equal(tuple_neg(a), tuple(-1.0f, 2.0f, -3.0f, 4.0f)); +} + +static bool tuple_test_10(const char ** scenario) +{ + *scenario = "Multiplying a tuple by a scalar"; + struct tuple a = tuple(1.0f, -2.0f, 3.0f, -4.0f); + return tuple_equal(tuple_mul(a, 3.5f), tuple(3.5f, -7.0f, 10.5f, -14.0f)); +} + +static bool tuple_test_11(const char ** scenario) +{ + *scenario = "Multiplying a tuple by a fraction"; + struct tuple a = tuple(1.0f, -2.0f, 3.0f, -4.0f); + return tuple_equal(tuple_mul(a, 0.5f), tuple(0.5f, -1.0f, 1.5f, -2.0f)); +} + +static bool tuple_test_12(const char ** scenario) +{ + *scenario = "Dividing a tuple by a scalar"; + struct tuple a = tuple(1.0f, -2.0f, 3.0f, -4.0f); + return tuple_equal(tuple_div(a, 2.0f), tuple(0.5f, -1.0f, 1.5f, -2.0f)); +} + +static bool tuple_test_13(const char ** scenario) +{ + *scenario = "Computing the magnitude of vector(1, 0, 0)"; + struct tuple a = vector(1.0f, 0.0f, 0.0f); + return float_equal(tuple_magnitude(a), 1.0f); +} + +static bool tuple_test_14(const char ** scenario) +{ + *scenario = "Computing the magnitude of vector(0, 1, 0)"; + struct tuple a = vector(0.0f, 1.0f, 0.0f); + return float_equal(tuple_magnitude(a), 1.0f); +} + +static bool tuple_test_15(const char ** scenario) +{ + *scenario = "Computing the magnitude of vector(0, 0, 1)"; + struct tuple a = vector(0.0f, 0.0f, 1.0f); + return float_equal(tuple_magnitude(a), 1.0f); +} + +static bool tuple_test_16(const char ** scenario) +{ + *scenario = "Computing the magnitude of vector(1, 2, 3)"; + struct tuple a = vector(1.0f, 2.0f, 3.0f); + return float_equal(tuple_magnitude(a), 3.7416573867739413); +} + +static bool tuple_test_17(const char ** scenario) +{ + *scenario = "Computing the magnitude of vector(-1, -2, -3)"; + struct tuple a = vector(-1.0f, -2.0f, -3.0f); + return float_equal(tuple_magnitude(a), 3.7416573867739413); +} + +static bool tuple_test_18(const char ** scenario) +{ + *scenario = "Normalizing vector(4, 0, 0)"; + struct tuple v = vector(4.0f, 0.0f, 0.0f); + return tuple_equal(tuple_normalize(v), vector(1.0f, 0.0f, 0.0f)); +} + +static bool tuple_test_19(const char ** scenario) +{ + *scenario = "Normalizing vector(1, 2, 3)"; + struct tuple v = vector(1.0f, 2.0f, 3.0f); + return tuple_equal(tuple_normalize(v), vector(0.2672612419124244, + 0.5345224838248488, + 0.8017837257372732)); +} + +static bool tuple_test_20(const char ** scenario) +{ + *scenario = "The magnitude of a normalized vector"; + struct tuple v = vector(1.0f, 2.0f, 3.0f); + struct tuple norm = tuple_normalize(v); + return float_equal(tuple_magnitude(norm), 1.0f); +} + +static bool tuple_test_21(const char ** scenario) +{ + *scenario = "The dot product of two tuples"; + struct tuple a = vector(1.0f, 2.0f, 3.0f); + struct tuple b = vector(2.0f, 3.0f, 4.0f); + return float_equal(tuple_dot(a, b), 20.0f); +} + +static bool tuple_test_22(const char ** scenario) +{ + *scenario = "The cross product of two vectors"; + struct tuple a = vector(1.0f, 2.0f, 3.0f); + struct tuple b = vector(2.0f, 3.0f, 4.0f); + return + tuple_equal(tuple_cross(a, b), vector(-1.0f, 2.0f, -1.0f)) && + tuple_equal(tuple_cross(b, a), vector( 1.0f, -2.0f, 1.0f)) + ; +} + +test_t tuple_tests[] = { + tuple_test_0, + tuple_test_1, + tuple_test_2, + tuple_test_3, + tuple_test_4, + tuple_test_5, + tuple_test_6, + tuple_test_7, + tuple_test_8, + tuple_test_9, + tuple_test_10, + tuple_test_11, + tuple_test_12, + tuple_test_13, + tuple_test_14, + tuple_test_15, + tuple_test_16, + tuple_test_17, + tuple_test_18, + tuple_test_19, + tuple_test_20, + tuple_test_21, + tuple_test_22 +}; + +RUNNER(tuple_tests) diff --git a/tuples.h b/tuples.h new file mode 100644 index 0000000..0d70563 --- /dev/null +++ b/tuples.h @@ -0,0 +1,130 @@ +#pragma once + +struct tuple { + float x; + float y; + float z; + float w; +}; + +inline static bool float_equal(float a, float b) +{ + const float epsilon = 0.00001; + return __builtin_fabs(a - b) < epsilon; +} + +inline static struct tuple tuple(float x, float y, float z, float w) +{ + return (struct tuple){ x, y, z, w }; +} + +inline static bool tuple_is_point(struct tuple t) +{ + return t.w == 1.0f; +} + +inline static bool tuple_is_vector(struct tuple t) +{ + return t.w == 0.0f; +} + +inline static bool tuple_equal(struct tuple a, struct tuple b) +{ + return + float_equal(a.x, b.x) && + float_equal(a.y, b.y) && + float_equal(a.z, b.z) && + float_equal(a.w, b.w); +} + +inline static struct tuple point(float x, float y, float z) +{ + return (struct tuple){x, y, z, 1.0f}; +} + +inline static struct tuple vector(float x, float y, float z) +{ + return (struct tuple){x, y, z, 0.0f}; +} + +inline static struct tuple tuple_add(struct tuple a, struct tuple b) +{ + return (struct tuple){ + a.x + b.x, + a.y + b.y, + a.z + b.z, + a.w + b.w + }; +} + +inline static struct tuple tuple_sub(struct tuple a, struct tuple b) +{ + return (struct tuple){ + a.x - b.x, + a.y - b.y, + a.z - b.z, + a.w - b.w + }; +} + +inline static struct tuple tuple_neg(struct tuple a) +{ + return (struct tuple){ + -a.x, + -a.y, + -a.z, + -a.w + }; +} + +inline static struct tuple tuple_mul(struct tuple a, float s) +{ + return (struct tuple){ + a.x * s, + a.y * s, + a.z * s, + a.w * s + }; +} + +inline static struct tuple tuple_div(struct tuple a, float s) +{ + return (struct tuple){ + a.x / s, + a.y / s, + a.z / s, + a.w / s + }; +} + +inline static float tuple_magnitude(struct tuple a) +{ + return __builtin_sqrtf(a.x * a.x + + a.y * a.y + + a.z * a.z + + a.w * a.w); +} + +inline static struct tuple tuple_normalize(struct tuple a) +{ + float magnitude = tuple_magnitude(a); + return tuple_div(a, magnitude); +} + +inline static float tuple_dot(struct tuple a, struct tuple b) +{ + return + a.x * b.x + + a.y * b.y + + a.z * b.z; +} + +inline static struct tuple tuple_cross(struct tuple a, struct tuple b) +{ + return (struct tuple){ + a.y * b.z - a.z * b.y, + a.z * b.x - a.x * b.z, + a.x * b.y - a.y * b.x, + 0.0f + }; +}