chapter 2
This commit is contained in:
parent
e5b29bdc81
commit
e889e1fb75
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,3 +1,5 @@
|
||||
*.o
|
||||
*.gch
|
||||
test/test_tuples
|
||||
test/test_tuples
|
||||
test/test_canvas
|
||||
*.ppm
|
70
canvas.h
Normal file
70
canvas.h
Normal 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
11
test/run.sh
Normal 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
51
test/test_canvas.c
Normal 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)
|
@ -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)
|
||||
|
63
tuples.h
63
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
|
||||
);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user