def setUp(self): self.clean_state() self.diameter = 0.1 self.aperture_origin = Position.from_xyz((10.0, 20.0, 30.0)) self.aperture_direction = Direction.from_xyz((1.0, 2.0, 3.0)) self.placement = Ray(self.aperture_origin, self.aperture_direction.normalized()) self.aperture = Aperture(placement=self.placement, diameter=self.diameter) self.initial_wavefront = WaveFront.unit_sphere self.orthogonal_directions = [ self.aperture_direction.cross(Direction.right).normalized(), self.aperture_direction.cross(Direction.forward).normalized(), self.aperture_direction.cross(Direction.backward).normalized(), ]
class TestAperture(unittest.TestCase): """ Test Aperture class """ def clean_state(self): self.aperture = None self.diameter = None self.aperture_origin = None self.aperture_direction = None self.orthogonal_directions = None self.placement = None self.ray_location = None self.orthogonal_direction = None self.scale_factor = None self.test_ray_distance = None self.test_ray = None self.initial_wavefront = None self.filtered_wavefront = None def setUp(self): self.clean_state() self.diameter = 0.1 self.aperture_origin = Position.from_xyz((10.0, 20.0, 30.0)) self.aperture_direction = Direction.from_xyz((1.0, 2.0, 3.0)) self.placement = Ray(self.aperture_origin, self.aperture_direction.normalized()) self.aperture = Aperture(placement=self.placement, diameter=self.diameter) self.initial_wavefront = WaveFront.unit_sphere self.orthogonal_directions = [ self.aperture_direction.cross(Direction.right).normalized(), self.aperture_direction.cross(Direction.forward).normalized(), self.aperture_direction.cross(Direction.backward).normalized(), ] def tearDown(self): self.clean_state() def test_aperture_radius(self): self.assertAlmostEqual(self.diameter / 2.0, self.aperture.radius) def test_aperture_correctly_measures_ray_distances(self): self.then_test_with_multiple_location_directions_and_scales() def then_test_with_multiple_location_directions_and_scales(self): for self.ray_location in [ Position.from_xyz((44.0, 33.0, 2.0)), Position.from_xyz((-9.0, 3.0, -2.0)), ]: self.then_test_location_with_multiple_direction_and_scales() def then_test_location_with_multiple_direction_and_scales(self): for self.orthogonal_direction in self.orthogonal_directions: self.then_test_location_and_direction_with_multiple_scales() def then_test_location_and_direction_with_multiple_scales(self): for self.scale_factor in [1.0, 0.1, 0.01, 0.001, 1000.0]: self.then_test_location_direction_and_scale() def then_test_location_direction_and_scale(self): self.when_test_ray_is_constructed() self.when_test_ray_distance_is_measured() self.then_test_ray_distance_is_correct() def when_test_ray_is_constructed(self): scaled_normal = self.orthogonal_direction.scale(self.scale_factor) ray_endpoint = self.aperture_origin.add(scaled_normal) ray_direction = ray_endpoint.difference(self.ray_location).normalized() self.test_ray = Ray(self.ray_location, ray_direction) def when_test_ray_distance_is_measured(self): self.test_ray_distance = self.aperture.distance_to_ray(self.test_ray) def then_test_ray_distance_is_correct(self): self.assertAlmostEqual(self.scale_factor, self.test_ray_distance) def test_volume_calculations(self): # Testing the test to be sure expected volume is correct. # The correct math using 1-cosine(angle) is inaccurate for small angles. # Instead a more complex tangent based formula is used. # This test makes sure the more complex formula is still correct. self.assertEqual(0.0, self.volume_of_spherical_cone(0.0)) for angle in [0.1, 0.2, 0.5, 1.0, 0.01]: cosine = math.cos(angle) tangent = math.tan(angle) expected_height_factor = 1.0 - cosine actual_height_factor = self.height_factor_of_spherical_cone(tangent) self.assertAlmostEqual(expected_height_factor, actual_height_factor) def volume_of_spherical_cone(self, tangent): height_factor = self.height_factor_of_spherical_cone(tangent) volume = height_factor * math.pi * 2.0 / 3.0 return volume def height_factor_of_spherical_cone(self, tangent): tangent_squared = tangent * tangent secant_squared = 1 + tangent_squared secant = math.sqrt(secant_squared) alpha_squared = tangent_squared + 2.0 * (1.0 - secant) alpha = math.sqrt(alpha_squared) height_factor = alpha / secant return height_factor @unittest.skip("slow test because it does a lot of calculation") def test_aperture_filters_wavefront(self): self.when_wavefront_is_filtered() self.then_filter_wavefront_has_correct_properties() def when_wavefront_is_filtered(self): self.filtered_wavefront = self.aperture.filter_wavefront(self.initial_wavefront) def then_filter_wavefront_has_correct_properties(self): distance_to_aperture = vector_norm(self.aperture_origin.xyz) tangent = self.aperture.radius / distance_to_aperture expected_volume = self.volume_of_spherical_cone(tangent) actual_volume = self.filtered_wavefront.volume self.assertAlmostEqual(expected_volume, actual_volume, 3)