chapter 2

This commit is contained in:
Zack Buhman 2024-07-22 00:13:25 -05:00
parent e5b29bdc81
commit e889e1fb75
6 changed files with 226 additions and 21 deletions

4
.gitignore vendored
View File

@ -1,3 +1,5 @@
*.o
*.gch
test/test_tuples
test/test_tuples
test/test_canvas
*.ppm

70
canvas.h Normal file
View File

@ -0,0 +1,70 @@
#include <stdlib.h>
#include <stdio.h>
#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");
}
}

11
test/run.sh Normal file
View File

@ -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

51
test/test_canvas.c Normal file
View File

@ -0,0 +1,51 @@
#include <stdbool.h>
#include <stdio.h>
#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)

View File

@ -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)

View File

@ -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
);
}