def test_dot_product(self): v1 = Coordinates.vector(1, 2, 3) v2 = Coordinates.vector(2, 3, 4) result = v1.dot_product(v2) assert result == 20
def test_scaling_matrix_applied_to_point(self): transform = scaling(2, 3, 4) point = Coordinates.point(-4, 6, 8) result = transform * point assert result == Coordinates.point(-8, 18, 32)
def test_shearing_transformation_moves_x_in_proportion_to_z(self): transform = shearing(0, 0, 0, 0, 0, 1) point = Coordinates.point(2, 3, 4) result = transform * point assert result == Coordinates.point(2, 3, 7)
def test_multiplying_translation_matrix(self): transform = translation(5, -3, 2) point = Coordinates.point(-3, 4, 5) result = transform * point assert result == Coordinates.point(2, 1, 7)
def test_reflection_is_scaling_by_a_negative_value(self): transform = scaling(-1, 1, 1) point = Coordinates.point(2, 3, 4) result = transform * point assert result == Coordinates.point(-2, 3, 4)
def test_subtracting_vector_from_zero(self): v1 = Coordinates.vector(0, 0, 0) v2 = Coordinates.vector(1, -2, 3) result = v1 - v2 assert result == Coordinates.vector(-1, 2, -3)
def test_scaling_matrix_applied_to_vector(self): transform = scaling(2, 3, 4) vector = Coordinates.vector(-4, 6, 8) result = transform * vector assert result == Coordinates.vector(-8, 18, 32)
def test_coordinates_addition(self): coord_1 = Coordinates(x=3, y=-2, z=5, w=1) coord_2 = Coordinates(x=-2, y=3, z=1, w=0) result = coord_1 + coord_2 assert result == Coordinates(x=1, y=1, z=6, w=1)
def test_multiply_by_inverse_of_scaling_matrix(self): transform = scaling(2, 3, 4).inverse() vector = Coordinates.vector(-4, 6, 8) result = transform * vector assert result == Coordinates.vector(-2, 2, 2)
def test_subtract_vectors(self): v1 = Coordinates.vector(3, 2, 1) v2 = Coordinates.vector(5, 6, 7) result = v1 - v2 assert result == Coordinates.vector(-2, -4, -6) assert result.is_vector()
def test_multiplying_by_inverse_of_translation_matrix(self): transform = translation(5, -3, 2) inverse = transform.inverse() point = Coordinates.point(-3, 4, 5) result = inverse * point assert result == Coordinates.point(-8, 7, 3)
def test_subtract_vector_from_point(self): point = Coordinates.point(3, 2, 1) vector = Coordinates.vector(5, 6, 7) result = point - vector assert result == Coordinates.point(-2, -4, -6) assert result.is_point()
def test_subtract_points(self): p1 = Coordinates.point(x=3, y=2, z=1) p2 = Coordinates.point(x=5, y=6, z=7) result = p1 - p2 assert result == Coordinates.vector(x=-2, y=-4, z=-6) assert result.is_vector()
def test_matrix_multiplication_to_coordinate(self): matrix = Matrix([[1, 2, 3, 4], [2, 4, 4, 2], [8, 6, 4, 1], [0, 0, 0, 1]]) coord = Coordinates(1, 2, 3, 1) result = matrix * coord assert result == Coordinates(18, 24, 33, 1)
def test_cross_product(self): v1 = Coordinates.vector(1, 2, 3) v2 = Coordinates.vector(2, 3, 4) result1 = v1.cross_product(v2) result2 = v2.cross_product(v1) assert result1 == Coordinates.vector(-1, 2, -1) assert result2 == Coordinates.vector(1, -2, 1)
def test_chaining_transformations_must_be_applied_in_reverse_order(self): point = Coordinates.point(1, 0, 1) rotate = rotation_x(radians(90)) scale = scaling(5, 5, 5) translate = translation(10, 5, 7) transforms = translate * scale * rotate result = transforms * point assert result == Coordinates.point(15, 0, 7)
def test_rotating_a_point_around_x_axis(self): point = Coordinates.point(0, 1, 0) half_quarter = rotation_x(radians(45)) full_quarter = rotation_x(radians(90)) half_quarter_rotation = half_quarter * point full_quarter_rotaton = full_quarter * point assert rounded(half_quarter_rotation) == rounded( [0, sqrt(2) / 2, sqrt(2) / 2, 1]) assert rounded(full_quarter_rotaton) == rounded( Coordinates.point(0, 0, 1))
def test_individual_transformations_are_applied_in_sequence(self): point = Coordinates.point(1, 0, 1) rotate = rotation_x(radians(90)) scale = scaling(5, 5, 5) translate = translation(10, 5, 7) rotated_point = rotate * point scaled_point = scale * rotated_point translated_point = translate * scaled_point assert rounded(rotated_point) == rounded(Coordinates.point(1, -1, 0)) assert rounded(scaled_point) == rounded(Coordinates.point(5, -5, 0)) assert translated_point == Coordinates.point(15, 0, 7)
def test_translation_does_not_affect_vectors(self): transform = translation(5, -3, 2) vector = Coordinates.vector(-3, 4, 5) result = transform * vector assert result == vector
def test_initialization(self): coordinates = Coordinates(x=1, y=2, z=3, w=4) assert coordinates.x == 1 assert coordinates.y == 2 assert coordinates.z == 3 assert coordinates.w == 4
def test_idenity_matrix_mul_by_coordinates(self): identity_matrix = Matrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) coord = Coordinates(1, 2, 3, 4) result = identity_matrix * coord assert result == coord
def test_normalize_vector_1_2_3(self): v = Coordinates.vector(1, 2, 3) result = v.normalize() assert round(result.x, 5) == 0.26726 assert round(result.y, 5) == 0.53452 assert round(result.z, 5) == 0.80178
def _mul_matrix_by_coordinates(self, coord): data = [] for row in self.data: total = reduce( lambda prev, curr: prev + curr[0] * curr[1], zip(row, coord), 0, ) data.append(total) return Coordinates(*data)
def test_inverse_of_x_rotation_goes_in_opposite_direction(self): point = Coordinates.point(0, 1, 0) half_quarter = rotation_x(radians(45)).inverse() half_quarter_rotation = half_quarter * point assert rounded(half_quarter_rotation) == rounded([ 0, sqrt(2) / 2, -(sqrt(2) / 2), 1, ])
from data_structures import Coordinates from data_structures import Color from drawing import Canvas from drawing.formats import PPMFormat # Challenge for end of chapter 1 projectile = { "position": Coordinates.point(0, 1, 0), "velocity": Coordinates.vector(1, 1.8, 0).normalize() * 11.25, } environment = { "gravity": Coordinates.vector(0, -0.1, 0), "wind": Coordinates.vector(-0.01, 0, 0), } def tick(projectile, environment): position = projectile["position"] + projectile["velocity"] velocity = projectile["velocity"] + environment["gravity"] + environment["wind"] return {"position": position, "velocity": velocity} projectiles = [projectile] while projectile["position"].y > 0: projectile = tick(projectile, environment) projectiles.append(projectile) red = Color(1, 0, 0)
def test_vector_conveniece_constructor(self): coordinates = Coordinates.vector(1, 2, 3) assert not coordinates.is_point() assert coordinates.is_vector()
def test_negating_coordinates(self): coord = Coordinates(x=1, y=-2, z=3, w=-4) result = -coord assert result == Coordinates(x=-1, y=2, z=-3, w=4)
def test_multiplying_coordinate_by_scalar(self): coord = Coordinates(1, -2, 3, -4) result = coord * 3.5 assert result == Coordinates(3.5, -7.0, 10.5, -14.0)
def test_multiplying_coordinates_by_fraction(self): coord = Coordinates(1, -2, 3, -4) result = coord * 0.5 assert result == Coordinates(0.5, -1.0, 1.5, -2.0)
from math import radians from drawing import Canvas from drawing.formats import PPMFormat from drawing.transformations import translation, rotation_z, rotation_y, rotation_x from data_structures import Coordinates, Color width = 900 height = 900 canvas = Canvas(width, height) degrees_of_rotations = 360 / 12 radius = (height / 2) * 0.8 point = Coordinates.point(0, 0, 0) translate = translation(width / 2, height / 2, 0) for i in range(12): colors = [Color(1, 0, 0), Color(0, 1, 0), Color(0, 0, 1)] degrees = i * degrees_of_rotations print(f"degrees: {degrees}") rotate = rotation_z(radians(degrees)) rotated_point = rotate * (translate * point) print( f"rotated_point: {rotated_point.x} {rotated_point.y} {rotated_point.z}" ) canvas.write_pixel(int(rotated_point.x), int(rotated_point.y), colors[i % 3])