def test_intersection(self): root = Node(name='Root') a = Node(name="A", parent=root) b = Node(name="B", parent=a) b.geometry = Sphere(radius=1.0) a.translate((1.0, 0.0, 0.0)) loc = (-2.0, 0.0, 0.0) vec = (1.0, 0.0, 0.0) scene = Scene(root=root) intersections = scene.intersections(loc, vec) points = tuple([x.point for x in intersections]) # In frame of a everything is shifed 1 along x assert points == ((0.0, 0.0, 0.0), (2.0, 0.0, 0.0))
def test_intersection_with_rotation_around_z(self): root = Node(name='Root') a = Node(name="A", parent=root) b = Node(name="B", parent=a) b.geometry = Sphere(radius=1.0) a.translate((1.0, 0.0, 0.0)) # This rotation make an z translation in b becomes a -y translation in a a.rotate(np.pi / 2, axis=(0.0, 0.0, 1.0)) loc = (-2.0, 0.0, 0.0) vec = (1.0, 0.0, 0.0) scene = Scene(root=root) intersections = scene.intersections(loc, vec) points = tuple([x.point for x in intersections]) assert np.allclose(points, ((0.0, 0.0, 0.0), (2.0, 0.0, 0.0)))
def test_intersection_with_rotation_around_x(self): root = Node(name='Root') a = Node(name="A", parent=root) b = Node(name="B", parent=a) b.geometry = Sphere(radius=1.0) b.translate((1.0, 0.0, 0.0)) # Rotation around x therefore no displace in x b.rotate(np.pi / 2, (1.0, 0.0, 0.0)) loc = (-2.0, 0.0, 0.0) vec = (1.0, 0.0, 0.0) scene = Scene(root=root) intersections = scene.intersections(loc, vec) points = tuple([x.point for x in intersections]) assert points == ((0.0, 0.0, 0.0), (2.0, 0.0, 0.0))
def test_intersection_with_rotation_around_z(self): root = Node(name='Root') a = Node(name="A", parent=root) b = Node(name="B", parent=a) b.geometry = Sphere(radius=1.0) a.translate((1.0, 0.0, 0.0)) # This rotation make an z translation in b becomes a -y translation in a a.rotate(np.pi/2, axis=(0.0, 0.0, 1.0)) loc = (-2.0, 0.0, 0.0) vec = (1.0, 0.0, 0.0) scene = Scene(root=root) intersections = scene.intersections(loc, vec) points = tuple([x.point for x in intersections]) assert np.allclose(points, ((0.0, 0.0, 0.0), (2.0, 0.0, 0.0)))
def test_intersection_with_rotation_around_x(self): root = Node(name='Root') a = Node(name="A", parent=root) b = Node(name="B", parent=a) b.geometry = Sphere(radius=1.0) b.translate((1.0, 0.0, 0.0)) # Rotation around x therefore no displace in x b.rotate(np.pi/2, (1.0, 0.0, 0.0)) loc = (-2.0, 0.0, 0.0) vec = (1.0, 0.0, 0.0) scene = Scene(root=root) intersections = scene.intersections(loc, vec) points = tuple([x.point for x in intersections]) assert points == ((0.0, 0.0, 0.0), (2.0, 0.0, 0.0))
def test_intersection_coordinate_system(self): root = Node(name="Root", geometry=Sphere(radius=10.0)) a = Node(name="A", parent=root, geometry=Sphere(radius=1.0)) a.translate((1.0, 0.0, 0.0)) scene = Scene(root) initial_ray = Ray( position=(-2.0, 0.0, 0.0), direction=(1.0, 0.0, 0.0), wavelength=None, is_alive=True, ) scene_intersections = scene.intersections(initial_ray.position, initial_ray.direction) a_intersections = tuple(map(lambda x: x.to(root), scene_intersections)) assert scene_intersections == a_intersections
def follow(ray: Ray, scene: Scene, max_iters=1000, renderer=None) -> [Tuple[Ray, Decision]]: """ Follow the ray through a scene. This the highest level ray-tracing function. It moves the ray through the scene and returns a list of rays a Monte Carlo decision events. Each decision is an interaction which alters one of the rays properties such as position, direction or wavelength. Raises ------ TraceError If an error occurred while tracing. Returns ------- List of (Ray, Decision) tuples. """ path = [(ray, Decision.EMIT)] idx = 0 last_ray = ray while ray.is_alive: intersections = scene.intersections(ray.position, ray.direction) points, nodes = zip(*[(x.point, x.hit) for x in intersections]) for ray, decision in step(ray, points, nodes, renderer=renderer): path.append((ray, decision)) if points_equal(ray.position, last_ray.position) and np.allclose(ray.direction, last_ray.direction): raise TraceError("Ray did not move.") last_ray = ray if idx > max_iters: raise TraceError("Ray got stuck.") return path
def test_intersection_coordinate_system(self): root = Node(name="Root", geometry=Sphere(radius=10.0)) a = Node(name="A", parent=root, geometry=Sphere(radius=1.0)) a.translate((1.0, 0.0, 0.0)) scene = Scene(root) initial_ray = Ray( position=(-2.0, 0.0, 0.0), direction=(1.0, 0.0, 0.0), wavelength=None, is_alive=True, ) scene_intersections = scene.intersections( initial_ray.position, initial_ray.direction ) a_intersections = tuple(map(lambda x: x.to(root), scene_intersections)) assert scene_intersections == a_intersections
def test_basic_scene(self): a = Node(name="A", parent=None) b = Node(name="B", parent=a) b.geometry = Sphere(radius=1.0) b.translate((2.0, 0.0, 0.0)) s = Scene(root=a) r = MeshcatRenderer() r.render(s) time.sleep(0.5) r.remove(s)
def make_touching_scene(n1=1.5, n2=1.5, n3=1.5): world = Node( name="world (air)", geometry=Sphere( radius=10.0, material=Dielectric.air() ) ) box1 = Node( name="box one (glass)", geometry=Box( (1.0, 1.0, 1.0), material=Dielectric.make_constant( x_range=(300.0, 4000.0), refractive_index=n1 ) ), parent=world ) box2 = Node( name="box two (glass)", geometry=Box( (1.0, 1.0, 1.0), material=Dielectric.make_constant( x_range=(300.0, 4000.0), refractive_index=n2 ) ), parent=world ) box2.translate((0.0, 0.0, 1.0)) box3 = Node( name="box three (glass)", geometry=Box( (1.0, 1.0, 1.0), material=Dielectric.make_constant( x_range=(300.0, 4000.0), refractive_index=n3 ) ), parent=world ) box3.translate((0.0, 0.0, 2.0)) scene = Scene(world) return scene, world, box1, box2, box3
def make_embedded_scene(n1=1.5): world = Node( name="world (air)", geometry=Sphere( radius=10.0, material=Dielectric.air() ) ) box = Node( name="box (glass)", geometry=Box( (1.0, 1.0, 1.0), material=Dielectric.make_constant( x_range=(300.0, 4000.0), refractive_index=n1 ) ), parent=world ) scene = Scene(world) return scene, world, box
def make_embedded_lumophore_scene(n1=1.5): world = Node( name="world (air)", geometry=Sphere( radius=10.0, material=Dielectric.air() ) ) box = Node( name="box (lumophore)", geometry=Box( (1.0, 1.0, 1.0), material=Lumophore.make_lumogen_f_red( x=np.linspace(300.0, 4000.0), absorption_coefficient=10.0, quantum_yield=1.0 ) ), parent=world ) scene = Scene(world) return scene, world, box
cylinder = Node(name="cylinder (glass)", geometry=Cylinder(length=1.0, radius=1.0, material=Dielectric.glass()), parent=world) # A light source with 60-deg divergence light = Node(name="light (555nm laser)", light=Light(divergence_delegate=functools.partial( Light.cone_divergence, np.radians(60))), parent=world) light.translate((0.0, 0.0, -1.0)) # Make a renderer object and scene for tracing viewer = MeshcatRenderer(open_browser=True) scene = Scene(world) viewer.render(scene) # Generate some rays from the light source and trace them through the scene for ray in light.emit(10): info = photon_tracer.follow(ray, scene) rays, events = zip(*info) viewer.add_ray_path(rays) # Wait for Ctrl-C to terminate the script; keep the window open print("Ctrl-C to close") while True: try: time.sleep(.3) except KeyboardInterrupt: print("Bye")
def _make_scene(self): """ Creates the scene based on configuration values. """ # Make world node (l, w, d) = self.size world = Node( name="World", geometry=Box((l * 100, w * 100, d * 100), material=Material(refractive_index=self.n0)), ) # Create components (Absorbers, Luminophores and Scatteres) if len(self._user_components) == 0: self._user_components = self._make_default_components() components = [] for component_data in self._user_components: cls = component_data.pop("cls") coefficient = component_data.pop("coefficient") component = cls(coefficient, **component_data) components.append(component) # Create LSC node lsc = Node( name="LSC", geometry=Box( (l, w, d), material=Material( refractive_index=self.n1, components=components, surface=Surface(delegate=OptionalMirrorAndSolarCell(self)), ), ), parent=world, ) if self._air_gap_mirror_info["want_air_gap_mirror"]: sheet_thickness = 0.25 * d # make it appear thinner than the LSC air_gap_mirror = Node( name="Air Gap Mirror", geometry=Box( (l, w, sheet_thickness), # same surface air but very thin material=Material( refractive_index=self.n0, components=[], surface=Surface(delegate=AirGapMirror(self)), ), ), parent=world, ) # Move adjacent to bottom surface with a small air gap air_gap_mirror.translate((0.0, 0.0, -(0.5 * d + sheet_thickness))) # Use user light if any have been given, otherwise use default values. if len(self._user_lights) == 0: self._user_lights = self._make_default_lights() # Create light nodes for light_data in self._user_lights: name = light_data["name"] light = Light( name=name, direction=light_data["direction"], wavelength=light_data["wavelength"], position=light_data["position"], ) light_node = Node(name=name, light=light, parent=world) light_node.location = light_data["location"] if light_data["rotation"]: light_node.rotate(*light_data["rotation"]) self._scene = Scene(world)
) # Light source light = Light( divergence_delegate=functools.partial( Light.cone_divergence, np.radians(20) ) ) light_node = Node( name='light', parent=world_node, location=(0.0, 0.0, 1.0) ) light_node.rotate(np.radians(180), (1, 0, 0)) light_node.light = light scene = Scene(root=world_node) renderer = MeshcatRenderer(max_histories=None, open_browser=True) renderer.render(scene) if __name__ == "__main__": np.random.seed(1) for light_node in scene.light_nodes: for ray in light.emit(20): ray = ray.representation(light_node, world_node) steps = photon_tracer.follow(ray, scene, renderer=renderer) path, decisions = zip(*steps) print(decisions) renderer.add_ray_path(path)