Skip to content

Commit 746e904

Browse files
authored
Merge pull request #25 from Kwasniok/feature/advanced-renderer
adds meta data and filters
2 parents 5438b04 + 3377af5 commit 746e904

20 files changed

+1983
-9
lines changed

src/demo_0.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ def make_scene(canvas_dimension: int) -> Scene:
141141
return scene
142142

143143

144-
def render(
144+
def render( # pylint: disable=R0913
145145
scene: Scene,
146146
geometry: Geometry,
147147
render_ray_depth: bool,

src/demo_1.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ def make_scene(canvas_dimension: int) -> Scene:
141141
return scene
142142

143143

144-
def render(
144+
def render( # pylint: disable=R0913
145145
scene: Scene,
146146
geometry: Geometry,
147147
render_ray_depth: bool,

src/demo_2.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ def make_scene(canvas_dimension: int) -> Scene:
110110
return scene
111111

112112

113-
def render(
113+
def render( # pylint: disable=R0913
114114
scene: Scene,
115115
geometry: Geometry,
116116
render_ray_depth: bool,
@@ -124,7 +124,6 @@ def render(
124124
"""
125125

126126
for projection_mode in ProjectionMode:
127-
# for mode in (ImageRenderer.Mode.PERSPECTIVE,):
128127
print(f"rendering {projection_mode.name} projection ...")
129128
if render_ray_depth:
130129
image_renderer: ImageRenderer = ImageRayDepthRenderer(

src/demo_3.py

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
"""
2+
Demonstartes the image filter renderer with an example scene in cylindrical
3+
coordinates an various filters.
4+
"""
5+
6+
import os
7+
import math
8+
9+
from nerte.values.coordinates import Coordinates3D
10+
from nerte.values.domain import Domain1D
11+
from nerte.values.linalg import AbstractVector
12+
from nerte.values.face import Face
13+
from nerte.values.manifolds.cylindrical import (
14+
Plane as CarthesianPlaneInCylindric,
15+
)
16+
from nerte.world.object import Object
17+
from nerte.world.camera import Camera
18+
from nerte.world.scene import Scene
19+
from nerte.geometry.geometry import Geometry
20+
from nerte.geometry.cylindircal_swirl_geometry import (
21+
SwirlCylindricRungeKuttaGeometry,
22+
)
23+
from nerte.render.projection import ProjectionMode
24+
from nerte.render.image_filter_renderer import (
25+
ImageFilterRenderer,
26+
Filter,
27+
HitFilter,
28+
)
29+
from nerte.render.ray_depth_filter import RayDepthFilter
30+
from nerte.render.meta_info_filter import MetaInfoFilter
31+
from nerte.util.random_color_generator import RandomColorGenerator
32+
33+
# pseudo-random color generator
34+
COLOR = RandomColorGenerator()
35+
36+
37+
def make_camera(canvas_dimension: int) -> Camera:
38+
"""Creates a camera with preset values."""
39+
40+
location = Coordinates3D((2.0, 0.0, 2.0))
41+
manifold = CarthesianPlaneInCylindric(
42+
b0=AbstractVector((0.0, -1.0, 0.0)),
43+
b1=AbstractVector((-0.4, 0.0, 0.4)),
44+
x0_domain=Domain1D(-1.0, +1.0),
45+
x1_domain=Domain1D(-1.0, +1.0),
46+
offset=AbstractVector((1.5, 0.0, 1.5)),
47+
)
48+
camera = Camera(
49+
location=location,
50+
detector_manifold=manifold,
51+
canvas_dimensions=(canvas_dimension, canvas_dimension),
52+
)
53+
return camera
54+
55+
56+
def add_cylinder(scene: Scene, radius: float, height: float) -> None:
57+
"""Adds a cylinder at the center of the scene."""
58+
59+
# cylinder
60+
# top 1
61+
point0 = Coordinates3D((0.0, -math.pi, +height))
62+
point1 = Coordinates3D((radius, -math.pi, +height))
63+
point2 = Coordinates3D((radius, math.pi, +height))
64+
tri = Face(point0, point1, point2)
65+
obj = Object(color=next(COLOR)) # pseudo-random color
66+
obj.add_face(tri)
67+
scene.add_object(obj)
68+
# top 2
69+
point0 = Coordinates3D((0.0, -math.pi, +height))
70+
point1 = Coordinates3D((0.0, +math.pi, +height))
71+
point2 = Coordinates3D((radius, +math.pi, +height))
72+
tri = Face(point0, point1, point2)
73+
obj = Object(color=next(COLOR)) # pseudo-random color
74+
obj.add_face(tri)
75+
scene.add_object(obj)
76+
# side 1
77+
point0 = Coordinates3D((radius, -math.pi, -height))
78+
point1 = Coordinates3D((radius, -math.pi, +height))
79+
point2 = Coordinates3D((radius, +math.pi, +height))
80+
tri = Face(point0, point1, point2)
81+
obj = Object(color=next(COLOR)) # pseudo-random color
82+
obj.add_face(tri)
83+
scene.add_object(obj)
84+
# side 2
85+
point0 = Coordinates3D((radius, -math.pi, -height))
86+
point1 = Coordinates3D((radius, +math.pi, -height))
87+
point2 = Coordinates3D((radius, +math.pi, +height))
88+
tri = Face(point0, point1, point2)
89+
obj = Object(color=next(COLOR)) # pseudo-random color
90+
obj.add_face(tri)
91+
scene.add_object(obj)
92+
# bottom 1
93+
point0 = Coordinates3D((0.0, -math.pi, -height))
94+
point1 = Coordinates3D((radius, -math.pi, -height))
95+
point2 = Coordinates3D((radius, +math.pi, -height))
96+
tri = Face(point0, point1, point2)
97+
obj = Object(color=next(COLOR)) # pseudo-random color
98+
obj.add_face(tri)
99+
scene.add_object(obj)
100+
# bottom 2
101+
point0 = Coordinates3D((0.0, -math.pi, -height))
102+
point1 = Coordinates3D((0.0, +math.pi, -height))
103+
point2 = Coordinates3D((radius, +math.pi, -height))
104+
tri = Face(point0, point1, point2)
105+
obj = Object(color=next(COLOR)) # pseudo-random color
106+
obj.add_face(tri)
107+
scene.add_object(obj)
108+
109+
110+
def make_scene(canvas_dimension: int) -> Scene:
111+
"""
112+
Creates a scene with a camera pointing towards an object.
113+
"""
114+
115+
camera = make_camera(canvas_dimension=canvas_dimension)
116+
scene = Scene(camera=camera)
117+
add_cylinder(scene, radius=1.0, height=1.0)
118+
119+
return scene
120+
121+
122+
def render( # pylint: disable=R0913
123+
scene: Scene,
124+
geometry: Geometry,
125+
filtr: Filter,
126+
output_path: str,
127+
file_prefix: str,
128+
show: bool,
129+
) -> None:
130+
"""
131+
Renders a preset scene with non-euclidean geometry in orthographic and
132+
perspective projection.
133+
"""
134+
135+
projection_mode = ProjectionMode.PERSPECTIVE
136+
print(
137+
f"rendering {projection_mode.name} projection for filter type"
138+
f" '{type(filtr).__name__}' ..."
139+
)
140+
renderer = ImageFilterRenderer(
141+
projection_mode=projection_mode,
142+
filtr=filtr,
143+
print_warings=False,
144+
)
145+
renderer.render(scene=scene, geometry=geometry)
146+
renderer.apply_filter()
147+
os.makedirs(output_path, exist_ok=True)
148+
image = renderer.last_image()
149+
if image is not None:
150+
image.save(f"{output_path}/{file_prefix}_{projection_mode.name}.png")
151+
if show:
152+
image.show()
153+
154+
155+
def main() -> None:
156+
"""Creates and renders the demo scene."""
157+
158+
# NOTE: Increase the canvas dimension to improve the image quality.
159+
# This will also increase rendering time!
160+
scene = make_scene(canvas_dimension=100)
161+
max_steps = 25
162+
geo = SwirlCylindricRungeKuttaGeometry(
163+
max_ray_depth=math.inf,
164+
step_size=0.2,
165+
max_steps=max_steps,
166+
swirl_strength=0.25,
167+
)
168+
169+
output_path = "../images"
170+
file_prefix = "demo3"
171+
show = True # disable if images cannot be displayed
172+
173+
# hit filter
174+
filtr: Filter = HitFilter()
175+
render(
176+
scene=scene,
177+
geometry=geo,
178+
filtr=filtr,
179+
output_path=output_path,
180+
file_prefix=file_prefix + "_hit_filter",
181+
show=show,
182+
)
183+
184+
# ray depth filter
185+
filtr = RayDepthFilter()
186+
render(
187+
scene=scene,
188+
geometry=geo,
189+
filtr=filtr,
190+
output_path=output_path,
191+
file_prefix=file_prefix + "_ray_depth_filter",
192+
show=show,
193+
)
194+
195+
# meta info filter
196+
filtr = MetaInfoFilter(
197+
meta_data_key="steps", min_value=0, max_value=max_steps
198+
)
199+
render(
200+
scene=scene,
201+
geometry=geo,
202+
filtr=filtr,
203+
output_path=output_path,
204+
file_prefix=file_prefix + "_meta_info_steps_filter",
205+
show=show,
206+
)
207+
208+
209+
if __name__ == "__main__":
210+
main()

src/nerte/geometry/runge_kutta_geometry.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
add_ray_segment_delta,
2222
)
2323
from nerte.values.intersection_info import IntersectionInfo, IntersectionInfos
24+
from nerte.values.extended_intersection_info import ExtendedIntersectionInfo
2425
from nerte.geometry.geometry import Geometry, intersection_ray_depth
2526

2627

@@ -165,7 +166,9 @@ def intersection_info(self, face: Face) -> IntersectionInfo:
165166
)
166167
if relative_segment_depth < math.inf:
167168
total_ray_depth += relative_segment_depth * segment_length
168-
return IntersectionInfo(ray_depth=total_ray_depth)
169+
return ExtendedIntersectionInfo(
170+
ray_depth=total_ray_depth, meta_data={"steps": step + 1}
171+
)
169172

170173
step += 1
171174
total_ray_depth += segment_length

src/nerte/geometry/runge_kutta_geometry_unittest.py

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
import unittest
99

10-
from typing import Callable, Type, TypeVar
10+
from typing import Callable, Type, TypeVar, cast
1111

1212
from itertools import permutations
1313
import math
@@ -20,6 +20,7 @@
2020
from nerte.values.ray_segment_delta import RaySegmentDelta
2121
from nerte.values.face import Face
2222
from nerte.values.intersection_info import IntersectionInfo
23+
from nerte.values.extended_intersection_info import ExtendedIntersectionInfo
2324
from nerte.values.util.convert import coordinates_as_vector
2425
from nerte.geometry.runge_kutta_geometry import RungeKuttaGeometry
2526

@@ -387,6 +388,51 @@ def test_runge_kutta_geometry_intersects(self) -> None:
387388
)
388389

389390

391+
class RungeKuttaGeometryRayIntersectsMetaDataTest(GeometryTestCase):
392+
def setUp(self) -> None:
393+
p1 = Coordinates3D((1.0, 0.0, 0.0))
394+
p2 = Coordinates3D((0.0, 1.0, 0.0))
395+
p3 = Coordinates3D((0.0, 0.0, 1.0))
396+
self.face = Face(p1, p2, p3)
397+
# geometry (carthesian & euclidean)
398+
DummyRungeKuttaGeometryGeo = _make_dummy_runge_kutta_geometry()
399+
geos = (
400+
DummyRungeKuttaGeometryGeo(
401+
max_ray_depth=1000.0,
402+
step_size=1, # direct hit
403+
max_steps=100,
404+
),
405+
DummyRungeKuttaGeometryGeo(
406+
max_ray_depth=1000.0,
407+
step_size=0.1, # 6 steps until hit (1/sqrt(3) ~ 0.577...)
408+
max_steps=100,
409+
),
410+
)
411+
self.rays = tuple(
412+
geo.ray_from_tangent(
413+
start=Coordinates3D((0.0, 0.0, 0.0)),
414+
direction=AbstractVector((1.0, 1.0, 1.0)),
415+
)
416+
for geo in geos
417+
)
418+
self.steps = (1, 6)
419+
420+
def test_runge_kutta_geometry_intersects_meta_data(self) -> None:
421+
"""
422+
Tests if ray's meta data.
423+
"""
424+
for ray, steps in zip(self.rays, self.steps):
425+
info = ray.intersection_info(self.face)
426+
self.assertIsInstance(info, ExtendedIntersectionInfo)
427+
if isinstance(info, ExtendedIntersectionInfo):
428+
info = cast(ExtendedIntersectionInfo, info)
429+
meta_data = info.meta_data
430+
self.assertIsNotNone(meta_data)
431+
if meta_data is not None:
432+
self.assertTrue("steps" in meta_data)
433+
self.assertAlmostEqual(meta_data["steps"], steps)
434+
435+
390436
class DummyRungeKuttaGeometryRayFromTest(GeometryTestCase):
391437
def setUp(self) -> None:
392438
DummyRungeKuttaGeometryGeo = _make_dummy_runge_kutta_geometry()

src/nerte/geometry/segmented_ray_geometry.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from nerte.values.linalg import AbstractVector
1515
from nerte.values.ray_segment import RaySegment
1616
from nerte.values.intersection_info import IntersectionInfo, IntersectionInfos
17+
from nerte.values.extended_intersection_info import ExtendedIntersectionInfo
1718
from nerte.geometry.geometry import Geometry, intersection_ray_depth
1819

1920

@@ -113,7 +114,9 @@ def intersection_info(self, face: Face) -> IntersectionInfo:
113114
total_ray_depth = (
114115
step + relative_segment_ray_depth
115116
) * geometry.ray_segment_length()
116-
return IntersectionInfo(ray_depth=total_ray_depth)
117+
return ExtendedIntersectionInfo(
118+
ray_depth=total_ray_depth, meta_data={"steps": step + 1}
119+
)
117120

118121
return IntersectionInfos.NO_INTERSECTION
119122

0 commit comments

Comments
 (0)