Compare commits

...

2 Commits

Author SHA1 Message Date
39e0d00e52 chapter 4 2024-07-23 18:03:15 -05:00
a380038f5e chapter 3 2024-07-23 13:18:02 -05:00
10 changed files with 1005 additions and 12 deletions

2
.gitignore vendored
View File

@ -2,4 +2,6 @@
*.gch
test/test_tuples
test/test_canvas
test/test_matrices
test/test_transformations
*.ppm

11
float.h Normal file
View File

@ -0,0 +1,11 @@
#pragma once
#include <stdbool.h>
#include "math.h"
inline static bool float_equal(float a, float b)
{
const float epsilon = 0.00001;
return fabsf(a - b) < epsilon;
}

6
math.h Normal file
View File

@ -0,0 +1,6 @@
#pragma once
#define sqrtf __builtin_sqrtf
#define fabsf __builtin_fabsf
#define cosf __builtin_cosf
#define sinf __builtin_sinf

265
matrices.h Normal file
View File

@ -0,0 +1,265 @@
#pragma once
#include "float.h"
#include "tuples.h"
struct mat4x4 {
float e[4][4];
};
struct mat3x3 {
float e[3][3];
};
struct mat2x2 {
float e[2][2];
};
inline static struct mat4x4 mat4x4(float a1, float a2, float a3, float a4,
float b1, float b2, float b3, float b4,
float c1, float c2, float c3, float c4,
float d1, float d2, float d3, float d4)
{
return (struct mat4x4){{
{ a1, a2, a3, a4 },
{ b1, b2, b3, b4 },
{ c1, c2, c3, c4 },
{ d1, d2, d3, d4 }
}};
}
inline static struct mat3x3 mat3x3(float a1, float a2, float a3,
float b1, float b2, float b3,
float c1, float c2, float c3)
{
return (struct mat3x3){{
{ a1, a2, a3 },
{ b1, b2, b3 },
{ c1, c2, c3 },
}};
}
inline static struct mat2x2 mat2x2(float a1, float a2,
float b1, float b2)
{
return (struct mat2x2){{
{ a1, a2 },
{ b1, b2 },
}};
}
inline static bool mat4x4_equal(struct mat4x4 const * const a,
struct mat4x4 const * const b)
{
return
float_equal(a->e[0][0], b->e[0][0]) &&
float_equal(a->e[0][1], b->e[0][1]) &&
float_equal(a->e[0][2], b->e[0][2]) &&
float_equal(a->e[0][3], b->e[0][3]) &&
float_equal(a->e[1][0], b->e[1][0]) &&
float_equal(a->e[1][1], b->e[1][1]) &&
float_equal(a->e[1][2], b->e[1][2]) &&
float_equal(a->e[1][3], b->e[1][3]) &&
float_equal(a->e[2][0], b->e[2][0]) &&
float_equal(a->e[2][1], b->e[2][1]) &&
float_equal(a->e[2][2], b->e[2][2]) &&
float_equal(a->e[2][3], b->e[2][3]) &&
float_equal(a->e[3][0], b->e[3][0]) &&
float_equal(a->e[3][1], b->e[3][1]) &&
float_equal(a->e[3][2], b->e[3][2]) &&
float_equal(a->e[3][3], b->e[3][3]);
}
inline static bool mat3x3_equal(struct mat3x3 * a,
struct mat3x3 * b)
{
return
float_equal(a->e[0][0], b->e[0][0]) &&
float_equal(a->e[0][1], b->e[0][1]) &&
float_equal(a->e[0][2], b->e[0][2]) &&
float_equal(a->e[1][0], b->e[1][0]) &&
float_equal(a->e[1][1], b->e[1][1]) &&
float_equal(a->e[1][2], b->e[1][2]) &&
float_equal(a->e[2][0], b->e[2][0]) &&
float_equal(a->e[2][1], b->e[2][1]) &&
float_equal(a->e[2][2], b->e[2][2]);
}
inline static bool mat2x2_equal(struct mat2x2 * a,
struct mat2x2 * b)
{
return
float_equal(a->e[0][0], b->e[0][0]) &&
float_equal(a->e[0][1], b->e[0][1]) &&
float_equal(a->e[1][0], b->e[1][0]) &&
float_equal(a->e[1][1], b->e[1][1]);
}
inline static struct mat4x4 mat4x4_mul_m(struct mat4x4 const * const a,
struct mat4x4 const * const b)
{
#define dot(row, col) \
a->e[row][0] * b->e[0][col] + \
a->e[row][1] * b->e[1][col] + \
a->e[row][2] * b->e[2][col] + \
a->e[row][3] * b->e[3][col]
return mat4x4(dot(0, 0), dot(0, 1), dot(0, 2), dot(0, 3),
dot(1, 0), dot(1, 1), dot(1, 2), dot(1, 3),
dot(2, 0), dot(2, 1), dot(2, 2), dot(2, 3),
dot(3, 0), dot(3, 1), dot(3, 2), dot(3, 3)
);
#undef dot
}
inline static struct tuple mat4x4_mul_t(struct mat4x4 const * const a,
struct tuple * b)
{
#define dot(row) \
a->e[row][0] * b->e[0] + \
a->e[row][1] * b->e[1] + \
a->e[row][2] * b->e[2] + \
a->e[row][3] * b->e[3]
return tuple(dot(0), dot(1), dot(2), dot(3));
#undef dot
}
inline static struct mat4x4 mat4x4_identity()
{
return mat4x4(1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
}
inline static struct mat4x4 mat4x4_transpose(struct mat4x4 const * const a)
{
return mat4x4(a->e[0][0], a->e[1][0], a->e[2][0], a->e[3][0],
a->e[0][1], a->e[1][1], a->e[2][1], a->e[3][1],
a->e[0][2], a->e[1][2], a->e[2][2], a->e[3][2],
a->e[0][3], a->e[1][3], a->e[2][3], a->e[3][3]);
}
inline static float mat2x2_determinant(struct mat2x2 * a)
{
return a->e[0][0] * a->e[1][1] - a->e[0][1] * a->e[1][0];
}
inline static struct mat2x2 mat3x3_submatrix(struct mat3x3 * a, int r, int c)
{
struct mat2x2 b;
int row2 = 0;
for (int row3 = 0; row3 < 3; row3++) {
if (row3 == r) continue;
int col2 = 0;
for (int col3 = 0; col3 < 3; col3++) {
if (col3 == c) continue;
b.e[row2][col2] = a->e[row3][col3];
col2++;
}
row2++;
}
return b;
}
inline static struct mat3x3 mat4x4_submatrix(struct mat4x4 const * const a, int r, int c)
{
struct mat3x3 b;
int row3 = 0;
for (int row4 = 0; row4 < 4; row4++) {
if (row4 == r) continue;
int col3 = 0;
for (int col4 = 0; col4 < 4; col4++) {
if (col4 == c) continue;
b.e[row3][col3] = a->e[row4][col4];
col3++;
}
row3++;
}
return b;
}
inline static float mat3x3_minor(struct mat3x3 * a, int r, int c)
{
struct mat2x2 s = mat3x3_submatrix(a, r, c);
float ret = mat2x2_determinant(&s);
return ret;
}
inline static float mat3x3_cofactor(struct mat3x3 * a, int r, int c)
{
float minor = mat3x3_minor(a, r, c);
if ((r + c) & 1)
return -minor;
else
return minor;
}
inline static float mat3x3_determinant(struct mat3x3 * a)
{
float f0 = mat3x3_cofactor(a, 0, 0);
float f1 = mat3x3_cofactor(a, 0, 1);
float f2 = mat3x3_cofactor(a, 0, 2);
return
a->e[0][0] * f0 +
a->e[0][1] * f1 +
a->e[0][2] * f2;
}
inline static float mat4x4_minor(struct mat4x4 const * const a, int r, int c)
{
struct mat3x3 s = mat4x4_submatrix(a, r, c);
float ret = mat3x3_determinant(&s);
return ret;
}
inline static float mat4x4_cofactor(struct mat4x4 const * const a, int r, int c)
{
float minor = mat4x4_minor(a, r, c);
if ((r + c) & 1)
return -minor;
else
return minor;
}
inline static float mat4x4_determinant(struct mat4x4 const * const a)
{
float f0 = mat4x4_cofactor(a, 0, 0);
float f1 = mat4x4_cofactor(a, 0, 1);
float f2 = mat4x4_cofactor(a, 0, 2);
float f3 = mat4x4_cofactor(a, 0, 3);
return
a->e[0][0] * f0 +
a->e[0][1] * f1 +
a->e[0][2] * f2 +
a->e[0][3] * f3;
}
inline static bool mat4x4_is_invertible(struct mat4x4 const * const a)
{
return !float_equal(mat4x4_determinant(a), 0.f);
}
inline static struct mat4x4 mat4x4_inverse(struct mat4x4 const * const a)
{
struct mat4x4 m;
float det = mat4x4_determinant(a);
m.e[0][0] = mat4x4_cofactor(a, 0, 0) / det;
m.e[1][0] = mat4x4_cofactor(a, 0, 1) / det;
m.e[2][0] = mat4x4_cofactor(a, 0, 2) / det;
m.e[3][0] = mat4x4_cofactor(a, 0, 3) / det;
m.e[0][1] = mat4x4_cofactor(a, 1, 0) / det;
m.e[1][1] = mat4x4_cofactor(a, 1, 1) / det;
m.e[2][1] = mat4x4_cofactor(a, 1, 2) / det;
m.e[3][1] = mat4x4_cofactor(a, 1, 3) / det;
m.e[0][2] = mat4x4_cofactor(a, 2, 0) / det;
m.e[1][2] = mat4x4_cofactor(a, 2, 1) / det;
m.e[2][2] = mat4x4_cofactor(a, 2, 2) / det;
m.e[3][2] = mat4x4_cofactor(a, 2, 3) / det;
m.e[0][3] = mat4x4_cofactor(a, 3, 0) / det;
m.e[1][3] = mat4x4_cofactor(a, 3, 1) / det;
m.e[2][3] = mat4x4_cofactor(a, 3, 2) / det;
m.e[3][3] = mat4x4_cofactor(a, 3, 3) / det;
return m;
}

View File

@ -2,7 +2,7 @@
set -eux
for name in tuples canvas; do
for name in tuples canvas matrices transformations; do
gcc -g -gdwarf-5 \
-Wall -Werror -Wfatal-errors \
-I. \

View File

@ -7,7 +7,7 @@ typedef bool (*test_t)(const char ** scenario);
{ \
int fail_count = 0; \
for (int i = 0; i < (sizeof (tests)) / (sizeof (test_t)); i++) { \
const char * scenario; \
const char * scenario = NULL; \
bool result = tests[i](&scenario); \
const char * result_s = result ? "ok" : "fail"; \
fail_count += !result; \

416
test/test_matrices.c Normal file
View File

@ -0,0 +1,416 @@
#include <stdbool.h>
#include <stdio.h>
#include "matrices.h"
#include "runner.h"
static bool matrices_test_0(const char ** scenario)
{
*scenario = "A 4x4 matrix ought to be representable";
struct mat4x4 m = mat4x4( 1.0f, 2.0f, 3.0f, 4.0f,
5.5f, 6.5f, 7.5f, 8.5f,
9.0f, 10.0f, 11.0f, 12.0f,
13.5f, 14.5f, 15.5f, 16.5f);
return
m.e[0][0] == 1.0f &&
m.e[0][3] == 4.0f &&
m.e[1][0] == 5.5f &&
m.e[1][2] == 7.5f &&
m.e[2][2] == 11.0f &&
m.e[3][0] == 13.5f &&
m.e[3][2] == 15.5f;
}
static bool matrices_test_1(const char ** scenario)
{
*scenario = "A 2x2 matrix ought to be representable";
struct mat2x2 m = mat2x2(-3.0f, 5.0f,
1.0f, -2.0f);
return
m.e[0][0] == -3.0f &&
m.e[0][1] == 5.0f &&
m.e[1][0] == 1.0f &&
m.e[1][1] == -2.0f;
}
static bool matrices_test_2(const char ** scenario)
{
*scenario = "A 3x3 matrix ought to be representable";
struct mat3x3 m = mat3x3(-3.0f, 5.0f, 0.0f,
1.0f, -2.0f, -7.0f,
0.0f, 1.0f, 1.0f);
return
m.e[0][0] == -3.0f &&
m.e[1][1] == -2.0f &&
m.e[2][2] == 1.0f;
}
static bool matrices_test_3(const char ** scenario)
{
*scenario = "Matrix equality with identical matrices";
struct mat4x4 a = mat4x4(1.0f, 2.0f, 3.0f, 4.0f,
5.0f, 6.0f, 7.0f, 8.0f,
9.0f, 8.0f, 7.0f, 6.0f,
5.0f, 4.0f, 3.0f, 2.0f);
struct mat4x4 b = mat4x4(1.0f, 2.0f, 3.0f, 4.0f,
5.0f, 6.0f, 7.0f, 8.0f,
9.0f, 8.0f, 7.0f, 6.0f,
5.0f, 4.0f, 3.0f, 2.0f);
return mat4x4_equal(&a, &b);
}
static bool matrices_test_4(const char ** scenario)
{
*scenario = "Matrix equality with different matrices";
struct mat4x4 a = mat4x4(1.0f, 2.0f, 3.0f, 4.0f,
5.0f, 6.0f, 7.0f, 8.0f,
9.0f, 8.0f, 7.0f, 6.0f,
5.0f, 4.0f, 3.0f, 2.0f);
struct mat4x4 b = mat4x4(2.0f, 3.0f, 4.0f, 5.0f,
6.0f, 7.0f, 8.0f, 9.0f,
8.0f, 7.0f, 6.0f, 5.0f,
4.0f, 3.0f, 2.0f, 1.0f);
return !mat4x4_equal(&a, &b);
}
static bool matrices_test_5(const char ** scenario)
{
*scenario = "Multiplying two matrices";
struct mat4x4 a = mat4x4(1.0f, 2.0f, 3.0f, 4.0f,
5.0f, 6.0f, 7.0f, 8.0f,
9.0f, 8.0f, 7.0f, 6.0f,
5.0f, 4.0f, 3.0f, 2.0f);
struct mat4x4 b = mat4x4(-2.0f, 1.0f, 2.0f, 3.0f,
3.0f, 2.0f, 1.0f, -1.0f,
4.0f, 3.0f, 6.0f, 5.0f,
1.0f, 2.0f, 7.0f, 8.0f);
struct mat4x4 m1 = mat4x4_mul_m(&a, &b);
struct mat4x4 m2 = mat4x4(20.0f, 22.0f, 50.0f, 48.0f,
44.0f, 54.0f, 114.0f, 108.0f,
40.0f, 58.0f, 110.0f, 102.0f,
16.0f, 26.0f, 46.0f, 42.0f);
return mat4x4_equal(&m1, &m2);
}
static bool matrices_test_6(const char ** scenario)
{
*scenario = "A matrix multiplied by a tuple";
struct mat4x4 a = mat4x4(1.0f, 2.0f, 3.0f, 4.0f,
2.0f, 4.0f, 4.0f, 2.0f,
8.0f, 6.0f, 4.0f, 1.0f,
0.0f, 0.0f, 0.0f, 1.0f);
struct tuple b = tuple(1.0f, 2.0f, 3.0f, 1.0f);
struct tuple c = mat4x4_mul_t(&a, &b);
return tuple_equal(c, tuple(18, 24, 33, 1));
}
static bool matrices_test_7(const char ** scenario)
{
*scenario = "Multiplying a matrix by the identity matrix";
struct mat4x4 a = mat4x4(0.0f, 1.0f, 2.0f, 4.0f,
1.0f, 2.0f, 4.0f, 8.0f,
2.0f, 4.0f, 8.0f, 16.0f,
4.0f, 8.0f, 16.0f, 32.0f);
struct mat4x4 id = mat4x4_identity();
struct mat4x4 c = mat4x4_mul_m(&a, &id);
return mat4x4_equal(&a, &c);
}
static bool matrices_test_8(const char ** scenario)
{
*scenario = "Multiplying a the identity matrix by a tuple";
struct tuple a = tuple(1.0f, 2.0f, 3.0f, 4.0f);
struct mat4x4 id = mat4x4_identity();
struct tuple c = mat4x4_mul_t(&id, &a);
return tuple_equal(a, c);
}
static bool matrices_test_9(const char ** scenario)
{
*scenario = "Transposing a matrix";
struct mat4x4 a = mat4x4(0.0f, 9.0f, 3.0f, 0.0f,
9.0f, 8.0f, 0.0f, 8.0f,
1.0f, 8.0f, 5.0f, 3.0f,
0.0f, 0.0f, 5.0f, 8.0f);
struct mat4x4 b = mat4x4(0.0f, 9.0f, 1.0f, 0.0f,
9.0f, 8.0f, 8.0f, 0.0f,
3.0f, 0.0f, 5.0f, 5.0f,
0.0f, 8.0f, 3.0f, 8.0f);
struct mat4x4 c = mat4x4_transpose(&a);
return mat4x4_equal(&c, &b);
}
static bool matrices_test_10(const char ** scenario)
{
*scenario = "Transposing the identity matrix";
struct mat4x4 a = mat4x4_identity();
struct mat4x4 c = mat4x4_transpose(&a);
return mat4x4_equal(&a, &c);
}
static bool matrices_test_11(const char ** scenario)
{
*scenario = "Calculating the determinant of a 2x2 matrix";
struct mat2x2 a = mat2x2( 1.0f, 5.0f,
-3.0f, 2.0f);
return float_equal(mat2x2_determinant(&a), 17);
}
static bool matrices_test_12(const char ** scenario)
{
*scenario = "A submatrix of a 3x3 matrix is a 2x2 matrix";
struct mat3x3 a = mat3x3( 1.0f, 5.0f, 0.0f,
-3.0f, 2.0f, 7.0f,
0.0f, 6.0f, -3.0f);
struct mat2x2 b = mat3x3_submatrix(&a, 0, 2);
struct mat2x2 c = mat2x2(-3.0f, 2.0f,
0.0f, 6.0f);
return mat2x2_equal(&b, &c);
}
static bool matrices_test_13(const char ** scenario)
{
*scenario = "A submatrix of a 4x4 matrix is a 3x3 matrix";
struct mat4x4 a = mat4x4(-6.0f, 1.0f, 1.0f, 6.0f,
-8.0f, 5.0f, 8.0f, 6.0f,
-1.0f, 0.0f, 8.0f, 2.0f,
-7.0f, 1.0f, -1.0f, 1.0f);
struct mat3x3 b = mat4x4_submatrix(&a, 2, 1);
struct mat3x3 c = mat3x3(-6.0f, 1.0f, 6.0f,
-8.0f, 8.0f, 6.0f,
-7.0f, -1.0f, 1.0f);
return mat3x3_equal(&b, &c);
}
static bool matrices_test_14(const char ** scenario)
{
*scenario = "Calculating a minor of a 3x3 matrix";
struct mat3x3 a = mat3x3(3, 5, 0,
2, -1, -7,
6, -1, 5);
struct mat2x2 b = mat3x3_submatrix(&a, 1, 0);
return
float_equal(mat2x2_determinant(&b), 25.0f) &&
float_equal(mat3x3_minor(&a, 1, 0), 25.0f);
}
static bool matrices_test_15(const char ** scenario)
{
*scenario = "Calculating a cofactor of a 3x3 matrix";
struct mat3x3 a = mat3x3(3, 5, 0,
2, -1, -7,
6, -1, 5);
return
float_equal(mat3x3_minor(&a, 0, 0), -12.0f) &&
float_equal(mat3x3_cofactor(&a, 0, 0), -12.0f) &&
float_equal(mat3x3_minor(&a, 1, 0), 25.0f) &&
float_equal(mat3x3_cofactor(&a, 1, 0), -25.0f) &&
float_equal(mat3x3_cofactor(&a, 2, 1), 21.0f);
}
static bool matrices_test_16(const char ** scenario)
{
*scenario = "Calculating the determinant of a 3x3 matrix";
struct mat3x3 a = mat3x3( 1.0f, 2.0f, 6.0f,
-5.0f, 8.0f, -4.0f,
2.0f, 6.0f, 4.0f);
return
float_equal(mat3x3_cofactor(&a, 0, 0), 56.0f) &&
float_equal(mat3x3_cofactor(&a, 0, 1), 12.0f) &&
float_equal(mat3x3_cofactor(&a, 0, 2), -46.0f) &&
float_equal(mat3x3_determinant(&a), -196.0f);
}
static bool matrices_test_17(const char ** scenario)
{
*scenario = "Calculating the determinant of a 4x4 matrix";
struct mat4x4 a = mat4x4(-2.0f, -8.0f, 3.0f, 5.0f,
-3.0f, 1.0f, 7.0f, 3.0f,
1.0f, 2.0f, -9.0f, 6.0f,
-6.0f, 7.0f, 7.0f, -9.0f);
return
float_equal(mat4x4_cofactor(&a, 0, 0), 690.0f) &&
float_equal(mat4x4_cofactor(&a, 0, 1), 447.0f) &&
float_equal(mat4x4_cofactor(&a, 0, 2), 210.0f) &&
float_equal(mat4x4_cofactor(&a, 0, 3), 51.0f) &&
float_equal(mat4x4_determinant(&a), -4071.0f);
}
static bool matrices_test_18(const char ** scenario)
{
*scenario = "Testing an invertible matrix for invertibility";
struct mat4x4 a = mat4x4(6.0f, 4.0f, 4.0f, 4.0f,
5.0f, 5.0f, 7.0f, 6.0f,
4.0f, -9.0f, 3.0f, -7.0f,
9.0f, 1.0f, 7.0f, -6.0f);
return
float_equal(mat4x4_determinant(&a), -2120.0f) &&
mat4x4_is_invertible(&a);
}
static bool matrices_test_19(const char ** scenario)
{
*scenario = "Testing a noninvertible matrix for invertibility";
struct mat4x4 a = mat4x4(-4.0f, 2.0f, -2.0f, -3.0f,
9.0f, 6.0f, 2.0f, 6.0f,
0.0f, -5.0f, 1.0f, -5.0f,
0.0f, 0.0f, 0.0f, 0.0f);
return
float_equal(mat4x4_determinant(&a), 0.0f) &&
!mat4x4_is_invertible(&a);
}
static bool matrices_test_20(const char ** scenario)
{
*scenario = "Calculating the inverse of a matrix";
struct mat4x4 a = mat4x4(-5.0, 2.0, 6.0, -8.0,
1.0, -5.0, 1.0, 8.0,
7.0, 7.0, -6.0, -7.0,
1.0, -3.0, 7.0, 4.0);
struct mat4x4 b = mat4x4_inverse(&a);
struct mat4x4 c = mat4x4( 0.218045f, 0.451128f, 0.240602f, -0.045113f,
-0.808271f, -1.456767f, -0.443609f, 0.520677f,
-0.078947f, -0.223684f, -0.052632f, 0.197368f,
-0.522564f, -0.813910f, -0.300752f, 0.306391f);
return
float_equal(mat4x4_determinant(&a), 532.0f) &&
float_equal(mat4x4_cofactor(&a, 2, 3), -160.0f) &&
float_equal(b.e[3][2], -160.0f/532.0f) &&
float_equal(mat4x4_cofactor(&a, 3, 2), 105.0f) &&
float_equal(b.e[2][3], 105.0f/532.0f) &&
mat4x4_equal(&b, &c);
}
static bool matrices_test_21(const char ** scenario)
{
*scenario = "Calculating the inverse of a second matrix";
struct mat4x4 a = mat4x4( 8.0f, -5.0f, 9.0f, 2.0f,
7.0f, 5.0f, 6.0f, 1.0f,
-6.0f, 0.0f, 9.0f, 6.0f,
-3.0f, 0.0f, -9.0f, -4.0f);
struct mat4x4 b = mat4x4_inverse(&a);
struct mat4x4 c = mat4x4(-0.15385f, -0.15385f, -0.28205f, -0.53846f,
-0.07692f, 0.12308f, 0.02564f, 0.03077f,
0.35897f, 0.35897f, 0.43590f, 0.92308f,
-0.69231f, -0.69231f, -0.76923f, -1.92308f);
return mat4x4_equal(&b, &c);
}
static bool matrices_test_22(const char ** scenario)
{
*scenario = "Calculating the inverse of a third matrix";
struct mat4x4 a = mat4x4( 9.0f, 3.0f, 0.0f, 9.0f,
-5.0f, -2.0f, -6.0f, -3.0f,
-4.0f, 9.0f, 6.0f, 4.0f,
-7.0f, 6.0f, 6.0f, 2.0f);
struct mat4x4 b = mat4x4_inverse(&a);
struct mat4x4 c = mat4x4(-0.04074f, -0.07778f, 0.14444f, -0.22222f,
-0.07778f, 0.03333f, 0.36667f, -0.33333f,
-0.02901f, -0.14630f, -0.10926f, 0.12963f,
0.17778f, 0.06667f, -0.26667f, 0.33333f);
return mat4x4_equal(&b, &c);
}
static bool matrices_test_23(const char ** scenario)
{
*scenario = "Multiplying a product by its inverse";
struct mat4x4 a = mat4x4( 3.0f, -9.0f, 7.0f, 3.0f,
3.0f, -8.0f, 2.0f, -9.0f,
-4.0f, 4.0f, 4.0f, 1.0f,
-6.0f, 5.0f, -1.0f, 1.0f);
struct mat4x4 b = mat4x4( 8.0f, 2.0f, 2.0f, 2.0f,
3.0f, -1.0f, 7.0f, 0.0f,
7.0f, 0.0f, 5.0f, 4.0f,
6.0f, -2.0f, 0.0f, 5.0f);
struct mat4x4 c = mat4x4_mul_m(&a, &b);
struct mat4x4 b_i = mat4x4_inverse(&b);
struct mat4x4 d = mat4x4_mul_m(&c, &b_i);
return mat4x4_equal(&d, &a);
}
test_t matrices_tests[] = {
matrices_test_0,
matrices_test_1,
matrices_test_2,
matrices_test_3,
matrices_test_4,
matrices_test_5,
matrices_test_6,
matrices_test_7,
matrices_test_8,
matrices_test_9,
matrices_test_10,
matrices_test_11,
matrices_test_12,
matrices_test_13,
matrices_test_14,
matrices_test_15,
matrices_test_16,
matrices_test_17,
matrices_test_18,
matrices_test_19,
matrices_test_20,
matrices_test_21,
matrices_test_22,
matrices_test_23,
};
RUNNER(matrices_tests)

234
test/test_transformations.c Normal file
View File

@ -0,0 +1,234 @@
#include <stdbool.h>
#include <stdio.h>
#include "transformations.h"
#include "runner.h"
static bool transformations_test_0(const char ** scenario)
{
*scenario = "Multiplying by a translation matrix";
struct mat4x4 transform = translation(5.0f, -3.0f, 2.0f);
struct tuple p = point(-3.0f, 4.0f, 5.0f);
return tuple_equal(mat4x4_mul_t(&transform, &p), point(2.0f, 1.0f, 7.0f));
}
static bool transformations_test_1(const char ** scenario)
{
*scenario = "Multiplying by the inverse of a translation matrix";
struct mat4x4 transform = translation(5.0f, -3.0f, 2.0f);
struct mat4x4 inv = mat4x4_inverse(&transform);
struct tuple p = point(-3.0f, 4.0f, 5.0f);
return tuple_equal(mat4x4_mul_t(&inv, &p), point(-8.0f, 7.0f, 3.0f));
}
static bool transformations_test_2(const char ** scenario)
{
*scenario = "Translation does not affect vectors";
struct mat4x4 transform = translation(5.0f, -3.0f, 2.0f);
struct tuple v = vector(-3.0f, 4.0f, 5.0f);
return tuple_equal(mat4x4_mul_t(&transform, &v), v);
}
static bool transformations_test_3(const char ** scenario)
{
*scenario = "A scaling matrix applied to a point";
struct mat4x4 transform = scaling(2.0f, 3.0f, 4.0f);
struct tuple p = point(-4.0f, 6.0f, 8.0f);
return tuple_equal(mat4x4_mul_t(&transform, &p), point(-8.0f, 18.0f, 32.0f));
}
static bool transformations_test_4(const char ** scenario)
{
*scenario = "A scaling matrix applied to a vector";
struct mat4x4 transform = scaling(2.0f, 3.0f, 4.0f);
struct tuple v = vector(-4.0f, 6.0f, 8.0f);
return tuple_equal(mat4x4_mul_t(&transform, &v), vector(-8.0f, 18.0f, 32.0f));
}
static bool transformations_test_5(const char ** scenario)
{
*scenario = "Multiplying by the inverse of a scaling matrix";
struct mat4x4 transform = scaling(2.0f, 3.0f, 4.0f);
struct mat4x4 inv = mat4x4_inverse(&transform);
struct tuple v = vector(-4.0f, 6.0f, 8.0f);
return tuple_equal(mat4x4_mul_t(&inv, &v), vector(-2.0f, 2.0f, 2.0f));
}
static bool transformations_test_6(const char ** scenario)
{
*scenario = "Reflection is scaling by a negative value";
struct mat4x4 transform = scaling(-1.0f, 1.0f, 1.0f);
struct tuple p = point(2.0f, 3.0f, 4.0f);
return tuple_equal(mat4x4_mul_t(&transform, &p), point(-2.0f, 3.0f, 4.0f));
}
static bool transformations_test_7(const char ** scenario)
{
*scenario = "Rotating a point around the x axis";
struct tuple p = point(0.0f, 1.0f, 0.0f);
struct mat4x4 half_quarter = rotation_x(tau / 8);
struct mat4x4 full_quarter = rotation_x(tau / 4);
return
tuple_equal(mat4x4_mul_t(&half_quarter, &p), point(0.0f, 0.7071067811865476, 0.7071067811865476)) &&
tuple_equal(mat4x4_mul_t(&full_quarter, &p), point(0.0f, 0.0f, 1.0f));
}
static bool transformations_test_8(const char ** scenario)
{
*scenario = "Rotating a point around the y axis";
struct tuple p = point(0.0f, 0.0f, 1.0f);
struct mat4x4 half_quarter = rotation_y(tau / 8);
struct mat4x4 full_quarter = rotation_y(tau / 4);
return
tuple_equal(mat4x4_mul_t(&half_quarter, &p), point(0.7071067811865476, 0.0f, 0.7071067811865476)) &&
tuple_equal(mat4x4_mul_t(&full_quarter, &p), point(1.0f, 0.0f, 0.0f));
}
static bool transformations_test_9(const char ** scenario)
{
*scenario = "Rotating a point around the z axis";
struct tuple p = point(0.0f, 1.0f, 0.0f);
struct mat4x4 half_quarter = rotation_z(tau / 8);
struct mat4x4 full_quarter = rotation_z(tau / 4);
return
tuple_equal(mat4x4_mul_t(&half_quarter, &p), point(-0.7071067811865476, 0.7071067811865476, 0.0f)) &&
tuple_equal(mat4x4_mul_t(&full_quarter, &p), point(-1.0f, 0.0f, 0.0f));
}
static bool transformations_test_10(const char ** scenario)
{
*scenario = "A shearing transformation moves x in proportion to y";
struct mat4x4 transform = shearing(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
struct tuple p = point(2.0f, 3.0f, 4.0f);
return tuple_equal(mat4x4_mul_t(&transform, &p), point(5.0f, 3.0f, 4.0f));
}
static bool transformations_test_11(const char ** scenario)
{
*scenario = "A shearing transformation moves x in proportion to z";
struct mat4x4 transform = shearing(0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f);
struct tuple p = point(2.0f, 3.0f, 4.0f);
return tuple_equal(mat4x4_mul_t(&transform, &p), point(6.0f, 3.0f, 4.0f));
}
static bool transformations_test_12(const char ** scenario)
{
*scenario = "A shearing transformation moves y in proportion to x";
struct mat4x4 transform = shearing(0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f);
struct tuple p = point(2.0f, 3.0f, 4.0f);
return tuple_equal(mat4x4_mul_t(&transform, &p), point(2.0f, 5.0f, 4.0f));
}
static bool transformations_test_13(const char ** scenario)
{
*scenario = "A shearing transformation moves y in proportion to z";
struct mat4x4 transform = shearing(0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
struct tuple p = point(2.0f, 3.0f, 4.0f);
return tuple_equal(mat4x4_mul_t(&transform, &p), point(2.0f, 7.0f, 4.0f));
}
static bool transformations_test_14(const char ** scenario)
{
*scenario = "A shearing transformation moves z in proportion to x";
struct mat4x4 transform = shearing(0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);
struct tuple p = point(2.0f, 3.0f, 4.0f);
return tuple_equal(mat4x4_mul_t(&transform, &p), point(2.0f, 3.0f, 6.0f));
}
static bool transformations_test_15(const char ** scenario)
{
*scenario = "A shearing transformation moves z in proportion to y";
struct mat4x4 transform = shearing(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f);
struct tuple p = point(2.0f, 3.0f, 4.0f);
return tuple_equal(mat4x4_mul_t(&transform, &p), point(2.0f, 3.0f, 7.0f));
}
static bool transformations_test_16(const char ** scenario)
{
*scenario = "Individual transforms are applied in sequence";
struct tuple p = point(1.0f, 0.0f, 1.0f);
struct mat4x4 a = rotation_x(tau / 4.0f);
struct mat4x4 b = scaling(5.0f, 5.0f, 5.0f);
struct mat4x4 c = translation(10.0f, 5.0f, 7.0f);
struct tuple p2 = mat4x4_mul_t(&a, &p);
struct tuple p3 = mat4x4_mul_t(&b, &p2);
struct tuple p4 = mat4x4_mul_t(&c, &p3);
return
tuple_equal(p2, point(1.0f, -1.0f, 0.0f)) &&
tuple_equal(p3, point(5.0f, -5.0f, 0.0f)) &&
tuple_equal(p4, point(15.0f, 0.0f, 7.0f));
}
static bool transformations_test_17(const char ** scenario)
{
*scenario = "Chained transformations must be applied in reverse order";
struct tuple p = point(1.0f, 0.0f, 1.0f);
struct mat4x4 a = rotation_x(tau / 4.0f);
struct mat4x4 b = scaling(5.0f, 5.0f, 5.0f);
struct mat4x4 c = translation(10.0f, 5.0f, 7.0f);
struct mat4x4 t1 = mat4x4_mul_m(&c, &b);
struct mat4x4 t2 = mat4x4_mul_m(&t1, &a);
struct tuple p1 = mat4x4_mul_t(&t2, &p);
return tuple_equal(p1, point(15.0f, 0.0f, 7.0f));
}
test_t transformations_tests[] = {
transformations_test_0,
transformations_test_1,
transformations_test_2,
transformations_test_3,
transformations_test_4,
transformations_test_5,
transformations_test_6,
transformations_test_7,
transformations_test_8,
transformations_test_9,
transformations_test_10,
transformations_test_11,
transformations_test_12,
transformations_test_13,
transformations_test_14,
transformations_test_15,
transformations_test_16,
transformations_test_17,
};
RUNNER(transformations_tests)

59
transformations.h Normal file
View File

@ -0,0 +1,59 @@
#pragma once
#include "math.h"
#include "matrices.h"
inline static struct mat4x4 translation(float x, float y, float z)
{
return mat4x4(1.0f, 0.0f, 0.0f, x,
0.0f, 1.0f, 0.0f, y,
0.0f, 0.0f, 1.0f, z,
0.0f, 0.0f, 0.0f, 1.0f);
}
inline static struct mat4x4 scaling(float x, float y, float z)
{
return mat4x4(x, 0.0f, 0.0f, 0.0f,
0.0f, y, 0.0f, 0.0f,
0.0f, 0.0f, z, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
}
static const float tau = 6.283185307179586;
inline static struct mat4x4 rotation_x(float r)
{
return mat4x4(1.0f, 0.0f, 0.0f, 0.0f,
0.0f, cosf(r), -sinf(r), 0.0f,
0.0f, sinf(r), cosf(r), 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
}
inline static struct mat4x4 rotation_y(float r)
{
return mat4x4( cosf(r), 0.0f, sinf(r), 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
-sinf(r), 0.0f, cosf(r), 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
}
inline static struct mat4x4 rotation_z(float r)
{
return mat4x4( cosf(r), -sinf(r), 0.0f, 0.0f,
sinf(r), cosf(r), 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
}
inline static struct mat4x4 shearing(float xy,
float xz,
float yx,
float yz,
float zx,
float zy)
{
return mat4x4(1.0f, xy, xz, 0.0f,
yx, 1.0f, yz, 0.0f,
zx, zy, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
}

View File

@ -1,5 +1,8 @@
#pragma once
#include "math.h"
#include "float.h"
struct tuple {
union {
struct {
@ -14,15 +17,12 @@ struct tuple {
float b;
float a;
};
struct {
float e[4];
};
};
};
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 }}};
@ -114,10 +114,10 @@ inline static struct tuple tuple_div(struct tuple a, float 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);
return 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)