From e889e1fb751e0ffd9411465d064ca54fa8b90c02 Mon Sep 17 00:00:00 2001 From: Zack Buhman Date: Mon, 22 Jul 2024 00:13:25 -0500 Subject: [PATCH] chapter 2 --- .gitignore | 4 ++- canvas.h | 70 ++++++++++++++++++++++++++++++++++++++++++++++ test/run.sh | 11 ++++++++ test/test_canvas.c | 51 +++++++++++++++++++++++++++++++++ test/test_tuples.c | 48 ++++++++++++++++++++++++++++++- tuples.h | 63 ++++++++++++++++++++++++++++------------- 6 files changed, 226 insertions(+), 21 deletions(-) create mode 100644 canvas.h create mode 100644 test/run.sh create mode 100644 test/test_canvas.c diff --git a/.gitignore b/.gitignore index c53e339..57854d4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ *.o *.gch -test/test_tuples \ No newline at end of file +test/test_tuples +test/test_canvas +*.ppm \ No newline at end of file diff --git a/canvas.h b/canvas.h new file mode 100644 index 0000000..5810594 --- /dev/null +++ b/canvas.h @@ -0,0 +1,70 @@ +#include +#include + +#include "tuples.h" + +struct canvas { + int width; + int height; + struct tuple * pixels; +}; + +inline static struct canvas canvas(int width, int height) +{ + return (struct canvas){ + width, + height, + calloc(width * height, (sizeof (struct tuple))) + }; +} + +inline static void canvas_write_pixel(struct canvas canvas, int x, int y, struct tuple color) +{ + struct tuple * pixel = &canvas.pixels[y * canvas.width + x]; + pixel->r = color.r; + pixel->g = color.g; + pixel->b = color.b; + pixel->a = color.a; +} + +inline static struct tuple canvas_pixel_at(struct canvas canvas, int x, int y) +{ + return canvas.pixels[y * canvas.width + x]; +} + +inline static int clamp_0_255(float n) +{ + if (n > 1.0f) { + return 255; + } else if (n < 0.0f) { + return 0; + } else { + return (int)(n * 255.0f); + } +} + +inline static void canvas_to_ppm(struct canvas canvas, const char * pathname) +{ + FILE * f = fopen(pathname, "w"); + if (f == NULL) { + perror("fopen"); + } + + fwrite("P6\n", 3, 1, f); + fprintf(f, "%d %d\n", canvas.width, canvas.height); + fwrite("255\n", 4, 1, f); + + for (int i = 0; i < canvas.width * canvas.height; i++) { + struct tuple * pixel = &canvas.pixels[i]; + char buf[3]; + buf[0] = clamp_0_255(pixel->r); + buf[1] = clamp_0_255(pixel->g); + buf[2] = clamp_0_255(pixel->b); + fwrite(buf, 3, 1, f); + } + + int ret = fclose(f); + if (ret != 0) { + perror("fclose"); + } +} diff --git a/test/run.sh b/test/run.sh new file mode 100644 index 0000000..a8d67f2 --- /dev/null +++ b/test/run.sh @@ -0,0 +1,11 @@ +#!bin/bash + +set -eux + +for name in tuples canvas; do + gcc -g -gdwarf-5 \ + -Wall -Werror -Wfatal-errors \ + -I. \ + test/test_${name}.c -o test/test_${name} -O0 -lm + ./test/test_${name} +done diff --git a/test/test_canvas.c b/test/test_canvas.c new file mode 100644 index 0000000..e0adfcd --- /dev/null +++ b/test/test_canvas.c @@ -0,0 +1,51 @@ +#include +#include + +#include "canvas.h" +#include "runner.h" + +static bool canvas_test_0(const char ** scenario) +{ + *scenario = "Creating a canvas"; + struct canvas c = canvas(10, 20); + bool zeroized = true; + for (int i = 0; i < c.width * c.height; i++) { + zeroized &= tuple_equal(c.pixels[i], color(0.0f, 0.0f, 0.0f)); + } + return + c.width == 10 && + c.height == 20 && + zeroized; +} + +static bool canvas_test_1(const char ** scenario) +{ + *scenario = "Writing pixels to the screen"; + struct canvas c = canvas(10, 20); + struct tuple red = color(1.0f, 0.0f, 0.0f); + canvas_write_pixel(c, 2, 3, red); + return tuple_equal(canvas_pixel_at(c, 2, 3), red); +} + +static bool canvas_test_2(const char ** scenario) +{ + *scenario = "PPM image"; + + struct canvas c = canvas(5, 3); + struct tuple c1 = color(1.5f, 0.0f, 0.0f); + struct tuple c2 = color(0.0f, 0.5f, 0.0f); + struct tuple c3 = color(-0.5f, 0.0f, 1.0f); + canvas_write_pixel(c, 0, 0, c1); + canvas_write_pixel(c, 2, 1, c2); + canvas_write_pixel(c, 4, 2, c3); + canvas_to_ppm(c, "canvas_test_2.ppm"); + return true; +} + +test_t canvas_tests[] = { + canvas_test_0, + canvas_test_1, + canvas_test_2, +}; + +RUNNER(canvas_tests) diff --git a/test/test_tuples.c b/test/test_tuples.c index 359abd5..ffcd6c8 100644 --- a/test/test_tuples.c +++ b/test/test_tuples.c @@ -194,6 +194,47 @@ static bool tuple_test_22(const char ** scenario) ; } +static bool tuple_test_23(const char ** scenario) +{ + *scenario = "Colors are (red, green, blue) tuples"; + struct tuple c = color(-0.5, 0.4, 1.7); + return + float_equal(c.r, -0.5) && + float_equal(c.g, 0.4) && + float_equal(c.b, 1.7); +} + +static bool tuple_test_24(const char ** scenario) +{ + *scenario = "Adding colors"; + struct tuple c1 = color(0.9f, 0.6f, 0.75f); + struct tuple c2 = color(0.7f, 0.1f, 0.25f); + return tuple_equal(tuple_add(c1, c2), color(1.6f, 0.7f, 1.0f)); +} + +static bool tuple_test_25(const char ** scenario) +{ + *scenario = "Subtracting colors"; + struct tuple c1 = color(0.9f, 0.6f, 0.75f); + struct tuple c2 = color(0.7f, 0.1f, 0.25f); + return tuple_equal(tuple_sub(c1, c2), color(0.2f, 0.5f, 0.5f)); +} + +static bool tuple_test_26(const char ** scenario) +{ + *scenario = "Multiplying a color by a scalar"; + struct tuple c = color(0.2f, 0.3f, 0.4f); + return tuple_equal(tuple_mul(c, 2.0f), color(0.4f, 0.6f, 0.8f)); +} + +static bool tuple_test_27(const char ** scenario) +{ + *scenario = "Multiplying colors"; + struct tuple c1 = color(1.0f, 0.2f, 0.4f); + struct tuple c2 = color(0.9f, 1.0f, 0.1f); + return tuple_equal(hadmard_product(c1, c2), color(0.9f, 0.2f, 0.04f)); +} + test_t tuple_tests[] = { tuple_test_0, tuple_test_1, @@ -217,7 +258,12 @@ test_t tuple_tests[] = { tuple_test_19, tuple_test_20, tuple_test_21, - tuple_test_22 + tuple_test_22, + tuple_test_23, + tuple_test_24, + tuple_test_25, + tuple_test_26, + tuple_test_27, }; RUNNER(tuple_tests) diff --git a/tuples.h b/tuples.h index 0d70563..bbdd9d7 100644 --- a/tuples.h +++ b/tuples.h @@ -1,10 +1,20 @@ #pragma once struct tuple { - float x; - float y; - float z; - float w; + union { + struct { + float x; + float y; + float z; + float w; + }; + struct { + float r; + float g; + float b; + float a; + }; + }; }; inline static bool float_equal(float a, float b) @@ -15,7 +25,7 @@ inline static bool float_equal(float a, float b) inline static struct tuple tuple(float x, float y, float z, float w) { - return (struct tuple){ x, y, z, w }; + return (struct tuple){{{ x, y, z, w }}}; } inline static bool tuple_is_point(struct tuple t) @@ -39,62 +49,67 @@ inline static bool tuple_equal(struct tuple a, struct tuple b) inline static struct tuple point(float x, float y, float z) { - return (struct tuple){x, y, z, 1.0f}; + return 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}; + return tuple(x, y, z, 0.0f); +} + +inline static struct tuple color(float r, float g, float b) +{ + return tuple(r, g, b, 0.0f); } inline static struct tuple tuple_add(struct tuple a, struct tuple b) { - return (struct tuple){ + return 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){ + return 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){ + return tuple( -a.x, -a.y, -a.z, -a.w - }; + ); } inline static struct tuple tuple_mul(struct tuple a, float s) { - return (struct tuple){ + return 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){ + return tuple( a.x / s, a.y / s, a.z / s, a.w / s - }; + ); } inline static float tuple_magnitude(struct tuple a) @@ -121,10 +136,20 @@ inline static float tuple_dot(struct tuple a, struct tuple b) inline static struct tuple tuple_cross(struct tuple a, struct tuple b) { - return (struct tuple){ + return 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 - }; + ); +} + +inline static struct tuple hadmard_product(struct tuple c1, struct tuple c2) +{ + return tuple( + c1.r * c2.r, + c1.g * c2.g, + c1.b * c2.b, + 0.0f + ); }