281 lines
8.9 KiB
C
281 lines
8.9 KiB
C
#include <group.h>
|
|
#include <math.h>
|
|
#include <sphere.h>
|
|
#include <unity.h>
|
|
|
|
#include "shape.h"
|
|
#include "testshape.h"
|
|
|
|
#include "../testutils.h"
|
|
|
|
void setUp(void) {}
|
|
void tearDown(void) {}
|
|
|
|
void test_build_a_test_shape(void) {
|
|
TESTSHAPE_TestShape *shape = TESTSHAPE_new();
|
|
TEST_ASSERT_EQUAL_DOUBLE(5.0, shape->size);
|
|
TESTSHAPE_delete(shape);
|
|
}
|
|
|
|
void test_shape_default_transformation(void) {
|
|
TESTSHAPE_TestShape *s = TESTSHAPE_new();
|
|
MATRIX_Matrix *identity_m = MATRIX_new_identity(4);
|
|
TEST_ASSERT_TRUE(MATRIX_is_equal(identity_m, ((SHAPE_Shape *)s)->transform));
|
|
TESTSHAPE_delete(s);
|
|
MATRIX_delete(identity_m);
|
|
}
|
|
|
|
void test_shape_modifying_transformation(void) {
|
|
TESTSHAPE_TestShape *s = TESTSHAPE_new();
|
|
MATRIX_Matrix *t = MATRIX_new_translation(2, 3, 4);
|
|
SHAPE_set_transform((SHAPE_Shape *)s, t);
|
|
TEST_ASSERT_TRUE(MATRIX_is_equal(t, ((SHAPE_Shape *)s)->transform));
|
|
TESTSHAPE_delete(s);
|
|
MATRIX_delete(t);
|
|
}
|
|
|
|
void test_shape_has_a_default_material(void) {
|
|
TESTSHAPE_TestShape *s = TESTSHAPE_new();
|
|
MATERIAL_Material *m = MATERIAL_new();
|
|
TEST_ASSERT_TRUE(MATERIAL_is_equal(((SHAPE_Shape *)s)->material, m));
|
|
MATERIAL_delete(m);
|
|
TESTSHAPE_delete(s);
|
|
}
|
|
|
|
void test_shape_may_be_assigned_material(void) {
|
|
TESTSHAPE_TestShape *s = TESTSHAPE_new();
|
|
MATERIAL_Material *m = MATERIAL_new();
|
|
m->ambient = 1;
|
|
SHAPE_set_material((SHAPE_Shape *)s, m);
|
|
TEST_ASSERT_TRUE(MATERIAL_is_equal(m, ((SHAPE_Shape *)s)->material));
|
|
TESTSHAPE_delete(s);
|
|
MATERIAL_delete(m);
|
|
}
|
|
|
|
void test_shapeholder_intersect_scaled_shape_with_ray(void) {
|
|
RAY_Ray *r = RAY_new(0, 0, -5, 0, 0, 1);
|
|
|
|
TESTSHAPE_TestShape *s = TESTSHAPE_new();
|
|
MATRIX_Matrix *transform = MATRIX_new_scaling(2, 2, 2);
|
|
SHAPE_set_transform((SHAPE_Shape *)s, transform);
|
|
|
|
RAY_Intersections *intersections = RAY_new_intersections();
|
|
SHAPE_intersect(intersections, (SHAPE_Shape *)s, r);
|
|
RAY_delete_intersections(intersections);
|
|
|
|
TUPLES_Point *expected_origin = TUPLES_new_point(0, 0, -2.5);
|
|
TUPLES_Vector *expected_vector = TUPLES_new_vector(0, 0, 0.5);
|
|
test_tuples(expected_origin, &s->saved_ray.origin);
|
|
test_tuples(expected_vector, &s->saved_ray.direction);
|
|
|
|
TESTSHAPE_delete(s);
|
|
MATRIX_delete(transform);
|
|
TUPLES_delete_all(expected_origin, expected_vector);
|
|
RAY_delete(r);
|
|
}
|
|
|
|
void test_shapeholder_intersect_translated_shape_with_ray(void) {
|
|
RAY_Ray *r = RAY_new(0, 0, -5, 0, 0, 1);
|
|
|
|
TESTSHAPE_TestShape *s = TESTSHAPE_new();
|
|
MATRIX_Matrix *transform = MATRIX_new_translation(5, 0, 0);
|
|
SHAPE_set_transform((SHAPE_Shape *)s, transform);
|
|
MATRIX_delete(transform);
|
|
|
|
RAY_Intersections *intersections = RAY_new_intersections();
|
|
SHAPE_intersect(intersections, (SHAPE_Shape *)s, r);
|
|
RAY_delete_intersections(intersections);
|
|
|
|
TUPLES_Point *expected_origin = TUPLES_new_point(-5, 0, -5);
|
|
TUPLES_Vector *expected_vector = TUPLES_new_vector(0, 0, 1);
|
|
test_tuples(expected_origin, &s->saved_ray.origin);
|
|
test_tuples(expected_vector, &s->saved_ray.direction);
|
|
|
|
TESTSHAPE_delete(s);
|
|
TUPLES_delete_all(expected_origin, expected_vector);
|
|
RAY_delete(r);
|
|
}
|
|
|
|
void test_compute_normal_on_translated_shape(void) {
|
|
TESTSHAPE_TestShape *s = TESTSHAPE_new();
|
|
MATRIX_Matrix *transform = MATRIX_new_translation(0, 1, 0);
|
|
SHAPE_set_transform((SHAPE_Shape *)s, transform);
|
|
MATRIX_delete(transform);
|
|
|
|
TUPLES_Point hitpoint;
|
|
TUPLES_init_point(&hitpoint, 0, 1.70711, -0.70711);
|
|
|
|
TUPLES_Vector world_normal;
|
|
SHAPE_normal_at(&world_normal, (SHAPE_Shape *)s, &hitpoint, NULL);
|
|
|
|
TUPLES_Vector expected;
|
|
TUPLES_init_vector(&expected, 0, 0.70711, -0.70711);
|
|
test_tuples(&expected, &world_normal);
|
|
|
|
TESTSHAPE_delete(s);
|
|
}
|
|
|
|
void test_compute_normal_on_transformed_shape(void) {
|
|
TESTSHAPE_TestShape *s = TESTSHAPE_new();
|
|
MATRIX_Matrix *scaling = MATRIX_new_scaling(1, 0.5, 1);
|
|
MATRIX_Matrix *rotationz = MATRIX_new_rotation_z(M_PI / 5.0);
|
|
MATRIX_Matrix *transform = MATRIX_multiply(scaling, rotationz);
|
|
SHAPE_set_transform((SHAPE_Shape *)s, transform);
|
|
MATRIX_delete_all(transform, rotationz, scaling);
|
|
|
|
TUPLES_Point hitpoint;
|
|
TUPLES_init_point(&hitpoint, 0, sqrt(2) / 2.0, -sqrt(2) / 2.0);
|
|
|
|
TUPLES_Vector world_normal;
|
|
SHAPE_normal_at(&world_normal, (SHAPE_Shape *)s, &hitpoint, NULL);
|
|
|
|
TUPLES_Vector expected;
|
|
TUPLES_init_vector(&expected, 0, 0.97014, -0.24254);
|
|
test_tuples(&expected, &world_normal);
|
|
|
|
TESTSHAPE_delete(s);
|
|
}
|
|
|
|
void test_shape_has_parent_attribute(void) {
|
|
TESTSHAPE_TestShape *ts = TESTSHAPE_new();
|
|
TEST_ASSERT_NULL(SHAPE_get_parent(ts));
|
|
TESTSHAPE_delete(ts);
|
|
}
|
|
|
|
void test_convert_point_from_world_to_object_space(void) {
|
|
GROUP_Group *g1 = GROUP_new();
|
|
MATRIX_Matrix *g1_transform = MATRIX_new_rotation_y(M_PI_2);
|
|
GROUP_set_transform(g1, g1_transform);
|
|
|
|
GROUP_Group *g2 = GROUP_new();
|
|
MATRIX_Matrix *g2_transform = MATRIX_new_scaling(2, 2, 2);
|
|
GROUP_set_transform(g2, g2_transform);
|
|
|
|
GROUP_add_child(g1, g2);
|
|
|
|
SPHERE_Sphere *s = SPHERE_new();
|
|
MATRIX_Matrix *s_transform = MATRIX_new_translation(5, 0, 0);
|
|
SPHERE_set_transform(s, s_transform);
|
|
|
|
GROUP_add_child(g2, s);
|
|
|
|
TUPLES_Point world_point, expected, result;
|
|
TUPLES_init_point(&world_point, -2, 0, -10);
|
|
SHAPE_world_to_object(&result, s, &world_point);
|
|
|
|
TUPLES_init_point(&expected, 0, 0, -1);
|
|
test_tuples(&expected, &result);
|
|
|
|
MATRIX_delete_all(g1_transform, g2_transform, s_transform);
|
|
GROUP_delete(g1);
|
|
}
|
|
|
|
void test_convert_normal_from_object_to_world_space(void) {
|
|
GROUP_Group *g1 = GROUP_new();
|
|
MATRIX_Matrix *g1_transform = MATRIX_new_rotation_y(M_PI_2);
|
|
GROUP_set_transform(g1, g1_transform);
|
|
|
|
GROUP_Group *g2 = GROUP_new();
|
|
MATRIX_Matrix *g2_transform = MATRIX_new_scaling(1, 2, 3);
|
|
GROUP_set_transform(g2, g2_transform);
|
|
|
|
GROUP_add_child(g1, g2);
|
|
|
|
SPHERE_Sphere *s = SPHERE_new();
|
|
MATRIX_Matrix *s_transform = MATRIX_new_translation(5, 0, 0);
|
|
SPHERE_set_transform(s, s_transform);
|
|
|
|
GROUP_add_child(g2, s);
|
|
|
|
TUPLES_Vector world_vector, expected, result;
|
|
TUPLES_init_vector(&world_vector, sqrt(3) / 3, sqrt(3) / 3, sqrt(3) / 3);
|
|
TUPLES_init_vector(&expected, 0.2857, 0.4286, -0.8571);
|
|
|
|
SHAPE_normal_to_world(&result, s, &world_vector);
|
|
test_tuples(&expected, &result);
|
|
|
|
MATRIX_delete_all(g1_transform, g2_transform, s_transform);
|
|
GROUP_delete(g1);
|
|
}
|
|
|
|
void test_finding_normal_on_child_object(void) {
|
|
GROUP_Group *g1 = GROUP_new();
|
|
MATRIX_Matrix *g1_transform = MATRIX_new_rotation_y(M_PI_2);
|
|
GROUP_set_transform(g1, g1_transform);
|
|
|
|
GROUP_Group *g2 = GROUP_new();
|
|
MATRIX_Matrix *g2_transform = MATRIX_new_scaling(1, 2, 3);
|
|
GROUP_set_transform(g2, g2_transform);
|
|
|
|
GROUP_add_child(g1, g2);
|
|
|
|
SPHERE_Sphere *s = SPHERE_new();
|
|
MATRIX_Matrix *s_transform = MATRIX_new_translation(5, 0, 0);
|
|
SPHERE_set_transform(s, s_transform);
|
|
|
|
GROUP_add_child(g2, s);
|
|
|
|
TUPLES_Point point;
|
|
TUPLES_init_point(&point, 1.7321, 1.1547, -5.5774);
|
|
TUPLES_Vector expected, result;
|
|
TUPLES_init_vector(&expected, 0.2857, 0.4286, -0.8571);
|
|
|
|
SHAPE_normal_at(&result, s, &point, NULL);
|
|
test_tuples(&expected, &result);
|
|
|
|
MATRIX_delete_all(g1_transform, g2_transform, s_transform);
|
|
GROUP_delete(g1);
|
|
}
|
|
|
|
void test_testshape_bounding_box(void) {
|
|
TESTSHAPE_TestShape *t = TESTSHAPE_new();
|
|
BOUND_Box box;
|
|
((SHAPE_Shape *)t)->vtable->bounds_of((SHAPE_Shape *)t, &box);
|
|
TUPLES_Point min_expected, max_expected;
|
|
TUPLES_init_point(&min_expected, -1, -1, -1);
|
|
TUPLES_init_point(&max_expected, 1, 1, 1);
|
|
TEST_ASSERT_TRUE(TUPLES_is_equal(&min_expected, &box.min));
|
|
TEST_ASSERT_TRUE(TUPLES_is_equal(&max_expected, &box.max));
|
|
TESTSHAPE_delete(t);
|
|
}
|
|
|
|
void test_get_shape_bounding_box_in_parents_space(void) {
|
|
SPHERE_Sphere *sphere = SPHERE_new();
|
|
MATRIX_Matrix *translation = MATRIX_new_translation(1, -3, 5);
|
|
MATRIX_Matrix *scaling = MATRIX_new_scaling(0.5, 2, 4);
|
|
MATRIX_Matrix *transformation = MATRIX_multiply_many(translation, scaling);
|
|
SPHERE_set_transform(sphere, transformation);
|
|
|
|
BOUND_Box box;
|
|
SHAPE_parent_space_bounds_of(&box, (SHAPE_Shape *)sphere);
|
|
|
|
TUPLES_Point min_expected, max_expected;
|
|
TUPLES_init_point(&min_expected, 0.5, -5, 1);
|
|
TUPLES_init_point(&max_expected, 1.5, -1, 9);
|
|
test_tuples(&min_expected, &box.min);
|
|
test_tuples(&max_expected, &box.max);
|
|
|
|
MATRIX_delete_all(transformation, translation, scaling);
|
|
SPHERE_delete(sphere);
|
|
}
|
|
|
|
int main(void) {
|
|
UNITY_BEGIN();
|
|
RUN_TEST(test_build_a_test_shape);
|
|
RUN_TEST(test_shape_default_transformation);
|
|
RUN_TEST(test_shape_modifying_transformation);
|
|
RUN_TEST(test_shape_has_a_default_material);
|
|
RUN_TEST(test_shape_may_be_assigned_material);
|
|
RUN_TEST(test_shapeholder_intersect_scaled_shape_with_ray);
|
|
RUN_TEST(test_shapeholder_intersect_translated_shape_with_ray);
|
|
RUN_TEST(test_compute_normal_on_translated_shape);
|
|
RUN_TEST(test_compute_normal_on_transformed_shape);
|
|
RUN_TEST(test_shape_has_parent_attribute);
|
|
RUN_TEST(test_convert_point_from_world_to_object_space);
|
|
RUN_TEST(test_convert_normal_from_object_to_world_space);
|
|
RUN_TEST(test_finding_normal_on_child_object);
|
|
RUN_TEST(test_testshape_bounding_box);
|
|
RUN_TEST(test_get_shape_bounding_box_in_parents_space);
|
|
return UNITY_END();
|
|
}
|