示例#1
0
 def filter_wavefront(self, wavefront, make_report = False):
     if make_report:
         self.monitor = FilterMonitor()
     else:
         self.monitor = NullFilterMonitor()
     self.count = 0
     
     # filter the wavefront until it fits through the hole.
     resolver = self.FilterResolver(self)
     result = wavefront.refine_with_filter(resolver)
     
     if make_report:
         self.monitor.print_report()
         
     return result
示例#2
0
class Aperture(SpecificationsHolder):
    """
    Represents circular opening in light blocking plane.
    """
    def __init__(self, **kwargs):
        super(Aperture, self).__init__(**kwargs)
        self.plane = Plane.orthogonal_to_ray(self.placement)
    
    @property
    def diameter(self):
        return self._specs['diameter']
    
    @property
    def origin(self):
        return self.placement.position
    
    @property
    def placement(self):
        return self._specs['placement']
    
    @property
    def radius(self):
        return 0.5 * self.diameter 

    def distance_to_ray(self, ray):
        plane_intercept_point = self.intercept_point(ray)
        distance_from_center = self.origin.difference(plane_intercept_point).length()
        return distance_from_center
    
    class FilterResolver:
        def __init__(self, aperture):
            self.aperture = aperture
            self.intercept_point = aperture.intercept_point
            self.monitor = aperture.monitor
            self.origin = aperture.origin
            self.placement = aperture.placement
            self.radius = aperture.radius
            self.count = 0
            self.calculate_size_criteria()
        def calculate_size_criteria(self):
            absolute_minimum_size = 0.000000001
            relative_minimum_size = self.radius * 0.005
            self.minimum_size = max(absolute_minimum_size, relative_minimum_size)
             
        def __call__(self, triangle):
            self.triangle = triangle
            
            # these checks have side effects, so do not change the order
            tests_to_try = [
                self.check_too_many,
                self.check_too_big,
                self.check_backwards,
                self.check_too_small,
                self.check_inside,
                self.check_straddle,
                self.check_outside,
                ]
            for test in tests_to_try:
                result = test()
                if result != None:
                    return result
                
            # still ambiguous
            return self.monitor.refine(triangle, "ambiguous")

        def check_too_many(self):
            # sanity limit
            self.count = self.count + 1
            if self.count > 300000:
                return self.monitor.reject(self.triangle, "reject since too many")
            return None
        
        def check_too_big(self):
            # refine large triangles because later tests assume they are
            # smaller than a full octant
            if self.triangle.diameter > 0.5:
                return self.monitor.refine(self.triangle, "refine since too big")
            return None

        def check_backwards(self):
            # reject backwards facing triangles
            for vertex in self.triangle:
                criteria = self.placement.direction.normalized().dot(vertex.direction)
                if criteria <= 0.0:
                    return self.monitor.reject(self.triangle, "reject since backwards")
            return None
            
        def check_too_small(self):
            self.calculate_relative_vertices() # side effect needed later
            self.calculate_perimeter()
            # reject if too small
            if self.perimeter < self.minimum_size:
                return self.monitor.reject(self.triangle, "reject since too small")
            return None
        def calculate_relative_vertices(self):
            absolute_vertices = [self.intercept_point(vertex) for vertex in self.triangle] 
            self.relative_vertices = [vertex.difference(self.origin) for vertex in absolute_vertices]
            self.a, self.b, self.c = self.relative_vertices
        def calculate_perimeter(self):
            edges = [self.a.difference(self.b), self.b.difference(self.c), self.c.difference(self.a)]
            self.perimeter = sum(edge.length() for edge in edges)            
           
        def check_inside(self):
            self.calculate_near_and_far_vertex_distances() # side effect
            # accept if all vertices are inside aperture
            if self.farthest_vertex_distance <= self.radius:
                return self.monitor.accept(self.triangle, "accept since inside")
            return None
        def calculate_near_and_far_vertex_distances(self):
            self.vertex_distances = [point.length() for point in self.relative_vertices]
            self.farthest_vertex_distance = max(self.vertex_distances)
            self.nearest_vertex_distance = min(self.vertex_distances)            
        
        def check_straddle(self):
            # refine if some, but not all, vertices are inside aperture
            if self.nearest_vertex_distance < self.radius:
                return self.monitor.refine(self.triangle, "refine since straddle")
            return None
            
        def check_outside(self):
            # reject if some projection of the vertices exceed aperture radius
            test_directions = [
                    # use triangle vertices themselves...
                    self.a.normalized(),
                    self.b.normalized(),
                    self.c.normalized(),
                    # ... plus these will reject outside of a cube containing the aperture
                    Direction.up, 
                    Direction.down, 
                    Direction.right,
                    Direction.forward, 
                    Direction.backward,
                ]
            for direction in test_directions:
                projected_vertices = [direction.dot(vertex) for vertex in self.relative_vertices]
                if min(projected_vertices) > self.radius:
                    return self.monitor.reject(self.triangle, "reject since outside")                
            return None
            
    def filter_wavefront(self, wavefront, make_report = False):
        if make_report:
            self.monitor = FilterMonitor()
        else:
            self.monitor = NullFilterMonitor()
        self.count = 0
        
        # filter the wavefront until it fits through the hole.
        resolver = self.FilterResolver(self)
        result = wavefront.refine_with_filter(resolver)
        
        if make_report:
            self.monitor.print_report()
            
        return result
            
    def intercept_point(self, ray):
        return self.plane.intersect(ray)