Example #1
0
 def when_wavefront_created(self):
     self.wavefront = WaveFront(self.triangles)
Example #2
0
class TestWaveFront(unittest.TestCase):
    """
    Test WaveFront Class
    """

    def setUp(self):
        self.wavefront = None
        self.triangles = [
            SphericalTriangle((Ray.right, Ray.forward, Ray.up)),
            SphericalTriangle((Ray.right, Ray.down, Ray.forward)),
            SphericalTriangle((Ray.left, Ray.forward, Ray.down)),
        ]
        self.unit_sphere_volume_sequence = [
            4.0 / 3.0,
            2.942809041582063,
            3.817729619828364,
            4.091600620085994,
            4.164203596248617,
        ]
        self.volume_of_unit_sphere = 4.0 / 3.0 * pi  # 4.1887902047863905
        self.accepted_diameter = 0.2
        self.orifice_diameter = 0.2

    def tearDown(self):
        self.wavefront = None
        self.triangles = None;
        self.unit_sphere_volume_sequence = None
        self.volume_of_unit_sphere = None
        self.accepted_diameter = None
        self.orifice_diameter = None

    def test_wave_front_normal_construction(self):
        self.when_wavefront_created()
        self.then_wavefront_has_normal_values()

    def when_wavefront_created(self):
        self.wavefront = WaveFront(self.triangles)

    def then_wavefront_has_normal_values(self):
        self.assertEqual(self.wavefront.triangles, tuple(self.triangles))

    def test_wavefront_constructor_requires_triangle_collection(self):
        with self.assertRaises(TypeError):
            WaveFront(self.triangles + ["wrong stuff"])

    def test_wavefront_unit_sphere_has_expected_values(self):
        self.when_wavefront_set_to_unit_sphere()
        self.then_wavefront_has_unit_sphere_content()

    def when_wavefront_set_to_unit_sphere(self):
        self.wavefront = WaveFront.unit_sphere

    def then_wavefront_has_unit_sphere_content(self):
        self.assertEqual(8, self.wavefront.count)
        expected_volume = self.unit_sphere_volume_sequence[0]
        self.assertAlmostEqual(expected_volume, self.wavefront.volume)

    def test_wavefront_subdivision(self):
        self.when_wavefront_set_to_unit_sphere()
        self.then_sequence_of_subdivided_wavefronts_meet_expectations()

    def then_sequence_of_subdivided_wavefronts_meet_expectations(self):
        expected_volume_sequence = self.unit_sphere_volume_sequence[1:]
        for depth, expected_volume in enumerate(expected_volume_sequence):
            self.when_wavefront_is_subdivided()
            self.then_wavefront_has_correct_count_for_depth(depth)
            self.then_wavefront_converges_on_perfect_volume_from_below()
            self.then_wavefront_has_expected_volume(expected_volume)

    def when_wavefront_is_subdivided(self):
        self.wavefront = self.wavefront.subdivide()

    def then_wavefront_has_correct_count_for_depth(self, depth):
        expected_count = 32 * 4 ** depth
        self.assertEqual(expected_count, self.wavefront.count)

    def then_wavefront_converges_on_perfect_volume_from_below(self):
        perfect_volume = self.volume_of_unit_sphere
        self.assertLess(self.wavefront.volume, perfect_volume)

    def then_wavefront_has_expected_volume(self, expected_volume):
        self.assertAlmostEqual(expected_volume, self.wavefront.volume)

    def test_wavefront_refine_with_diameter_filter(self):
        self.when_wavefront_set_to_unit_sphere()
        self.when_wavefront_is_refined_by_diameter()
        self.then_wavefront_has_expected_count_and_volume()
        self.then_each_triangle_in_wavefront_has_refined_diameter()

    def when_wavefront_is_refined_by_diameter(self):
        def testfilter(triangle):
            if triangle.diameter < self.accepted_diameter:
                return TriangleClassification.Accept
            else:
                return TriangleClassification.Refine

        self.wavefront = self.wavefront.refine_with_filter(testfilter)

    def then_wavefront_has_expected_count_and_volume(self):
        self.assertEqual(2048, self.wavefront.count)
        self.assertAlmostEqual(self.unit_sphere_volume_sequence[4], self.wavefront.volume)

    def then_each_triangle_in_wavefront_has_refined_diameter(self):
        for triangle in self.wavefront:
            self.assertLess(triangle.diameter, self.accepted_diameter)

    def test_wavefront_refine_with_orifice_filter(self):
        self.when_wavefront_set_to_unit_sphere()
        self.when_wavefront_is_refined_by_orifice()
        expected_volume = self.orifice_diameter ** 2
        self.assertAlmostEqual(expected_volume, self.wavefront.volume, 3)
        self.then_wavefront_points_up()

    def when_wavefront_is_refined_by_orifice(self):
        def testfilter(triangle):
            orifice_center = Ray.up.endpoint.coordinates
            orifice_diameter = self.orifice_diameter

            vertex_a = triangle.a.endpoint.coordinates
            vertex_b = triangle.b.endpoint.coordinates
            vertex_c = triangle.c.endpoint.coordinates

            a_length = vector_distance(vertex_a, orifice_center)
            b_length = vector_distance(vertex_b, orifice_center)
            c_length = vector_distance(vertex_c, orifice_center)

            farthest_distance = max(a_length, b_length, c_length)
            closest_distance = min(a_length, b_length, c_length)

            if farthest_distance <= orifice_diameter:
                return TriangleClassification.Accept
            elif triangle.diameter < 0.01 or closest_distance >= orifice_diameter:
                return TriangleClassification.Reject
            else:
                return TriangleClassification.Refine

        self.wavefront = self.wavefront.refine_with_filter(testfilter)

    def then_wavefront_points_up(self):
        actual_direction = self.wavefront.central_direction.normalized()
        expected_direction = Direction.up
        disparity = vector_distance(actual_direction.xyz, expected_direction.xyz)
        self.assertLess(disparity, 0.00000001)