Skip to content

Commit

Permalink
chore(lib): implementing Fermat path tracing
Browse files Browse the repository at this point in the history
  • Loading branch information
jeertmans committed Feb 19, 2024
1 parent 3b46c86 commit b153bf2
Show file tree
Hide file tree
Showing 10 changed files with 199 additions and 33 deletions.
1 change: 1 addition & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ DiffeRT: Differentiable Ray Tracing Toolbox for Radio Propagation
notebooks/quickstart
notebooks/advanced_path_tracing
notebooks/plotting_backend
notebooks/diffraction
notebooks/ray_tracing_at_city_scale

.. toctree::
Expand Down
7 changes: 7 additions & 0 deletions docs/source/references.bib
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,10 @@ @inproceedings{mpt-eucap2023
pages = {1--5},
doi = {10.23919/EuCAP57121.2023.10132934}
}
@misc{fermat-principle,
title = {Fermat's principle --- {Wikipedia}{,} The Free Encyclopedia},
author = {{Wikipedia contributors}},
year = 2024,
url = {https://en.wikipedia.org/w/index.php?title=Fermat%27s_principle&oldid=1206886888},
note = {[Online; accessed 19-February-2024]}
}
8 changes: 4 additions & 4 deletions python/differt/geometry/triangle_mesh.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def triangles_contain_vertices_assuming_inside_same_plane(
triangle_vertices: an array of triangle vertices.
vertices: an array of vertices that will be checked.
Returns:
Return:
A boolean array indicating whether vertices are in the corresponding triangles or not.
"""
# [*batch 3]
Expand Down Expand Up @@ -89,7 +89,7 @@ def paths_intersect_triangles(
a small portion of the path, to avoid indicating intersection
when a path *bounces off* a triangle.
Returns:
Return:
A boolean array indicating whether vertices are in the corresponding triangles or not.
"""
ray_origins = paths[..., :-1, :]
Expand Down Expand Up @@ -146,7 +146,7 @@ def load_obj(cls, file: Path) -> "TriangleMesh":
Args:
file: The path to the Wavefront .obj file.
Returns:
Return:
The corresponding mesh containing only triangles.
"""
mesh = _core.geometry.triangle_mesh.TriangleMesh.load_obj(str(file))
Expand All @@ -163,7 +163,7 @@ def plot(self, **kwargs: Any) -> Any:
kwargs: Keyword arguments passed to
:py:func:`draw_mesh<differt.plotting.draw_mesh>`.
Returns:
Return:
The resulting plot output.
"""
return draw_mesh(
Expand Down
4 changes: 2 additions & 2 deletions python/differt/geometry/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def pairwise_cross(
u: First array of vectors.
v: Second array of vectors.
Returns:
Return:
A 3D tensor with all cross products.
"""
return jnp.cross(u[:, None, :], v[None, :, :])
Expand All @@ -31,7 +31,7 @@ def normalize(
Args:
vector: An array of vectors.
Returns:
Return:
The normalized vector and their length.
:Examples:
Expand Down
6 changes: 3 additions & 3 deletions python/differt/plotting/_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def draw_mesh(
or :py:class:`Mesh3d<plotly.graph_objects.Mesh3d>`, depending on the
backend.
Returns:
Return:
The resulting plot output.
"""

Expand Down Expand Up @@ -102,7 +102,7 @@ def draw_paths(
or :py:class:`Scatter3d<plotly.graph_objects.Scatter3d>`, depending on the
backend.
Returns:
Return:
The resulting plot output.
"""

Expand Down Expand Up @@ -165,7 +165,7 @@ def draw_markers(
or :py:class:`Scatter3d<plotly.graph_objects.Scatter3d>`, depending on the
backend.
Returns:
Return:
The resulting plot output.
Warning:
Expand Down
8 changes: 4 additions & 4 deletions python/differt/plotting/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ def view_from_canvas(canvas: SceneCanvas) -> ViewBox:
Args:
canvas: The canvas that draws the contents of the scene.
Returns:
Return:
The view on which contents are displayed.
"""
from vispy.scene.widgets.viewbox import ViewBox
Expand Down Expand Up @@ -287,7 +287,7 @@ def process_vispy_kwargs(
must ensure that ``view in canvas.central_widget.children``
evaluates to :py:data:`True`.
Returns:
Return:
The canvas and view used to display contents.
"""
from vispy import scene
Expand Down Expand Up @@ -332,7 +332,7 @@ def process_matplotlib_kwargs(
must ensure that ``ax in figure.axes``
evaluates to :py:data:`True`.
Returns:
Return:
The figure and axes used to display contents.
"""
import matplotlib.pyplot as plt
Expand Down Expand Up @@ -378,7 +378,7 @@ def process_plotly_kwargs(
figure (:py:class:`Figure<plotly.graph_objects.Figure>`):
The figure that draws contents of the scene.
Returns:
Return:
The figure used to display contents.
"""
import plotly.graph_objects as go
Expand Down
72 changes: 72 additions & 0 deletions python/differt/rt/fermat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
"""
Path tracing utilities that utilize Fermat's principle.
Fermat's principle states that the path taken by a ray between two
given points is the path that can be traveled in the least time
:cite:`fermat-principle`. In a homogeneous medium,
this means that the path of least time is also the path of last distance.
As a result, this module offers minimization methods for finding ray paths.
"""
from jaxtyping import Array, Float, jaxtyped
from typeguard import typechecked as typechecker

Check warning on line 12 in python/differt/rt/fermat.py

View check run for this annotation

Codecov / codecov/patch

python/differt/rt/fermat.py#L11-L12

Added lines #L11 - L12 were not covered by tests


@jaxtyped(typechecker=typechecker)
def fermat_path_on_planar_surfaces(

Check warning on line 16 in python/differt/rt/fermat.py

View check run for this annotation

Codecov / codecov/patch

python/differt/rt/fermat.py#L15-L16

Added lines #L15 - L16 were not covered by tests
from_vertices: Float[Array, "*batch 3"],
to_vertices: Float[Array, "*batch 3"],
mirror_vertices: Float[Array, "num_mirrors *batch 3"],
mirror_normals: Float[Array, "num_mirrors *batch 3"],
) -> Float[Array, "num_mirrors *batch 3"]:
"""
Return the ray paths between pairs of vertices, that reflect on a given list of mirrors in between.
Args:
from_vertices: An array of ``from`` vertices, i.e., vertices from which the
ray paths start. In a radio communications context, this is usually
an array of transmitters.
to_vertices: An array of ``to`` vertices, i.e., vertices to which the
ray paths end. In a radio communications context, this is usually
an array of receivers.
mirror_vertices: An array of mirror vertices. For each mirror, any
vertex on the infinite plane that describes the mirror is considered
to be a valid vertex.
mirror_normals: An array of mirror normals, where each normal has a unit
length and if perpendicular to the corresponding mirror.
Return:
An array of ray paths obtained using Fermat's principle.
.. note::
The paths do not contain the starting and ending vertices.
You can easily create the complete ray paths using
:func:`jax.numpy.concatenate`:
.. code-block:: python
paths = fermat_path_on_planar_surfaces(
from_vertices,
to_vertices,
mirror_vertices,
mirror_normals,
)
full_paths = jnp.concatenate(
(
from_vertices[
None,
...,
],
paths,
to_vertices[
None,
...,
],
)
)
"""
_mirror_directions = ...

Check warning on line 71 in python/differt/rt/fermat.py

View check run for this annotation

Codecov / codecov/patch

python/differt/rt/fermat.py#L71

Added line #L71 was not covered by tests
raise NotImplementedError
39 changes: 25 additions & 14 deletions python/differt/rt/image_method.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def image_of_vertices_with_respect_to_mirrors(
mirror_normals: An array of mirror normals, where each normal has a unit
length and if perpendicular to the corresponding mirror.
Returns:
Return:
An array of image vertices.
Examples:
Expand Down Expand Up @@ -112,7 +112,7 @@ def intersection_of_line_segments_with_planes(
plane_normals: an array of plane normals, where each normal has a unit
length and if perpendicular to the corresponding plane.
Returns:
Return:
An array of intersection vertices.
"""
u = segment_ends - segment_starts
Expand All @@ -134,13 +134,20 @@ def image_method(
Return the ray paths between pairs of vertices, that reflect on a given list of mirrors in between.
Args:
from_vertices: *TODO*.
to_vertices: *TODO*.
mirror_vertices: *TODO*.
mirror_normals: *TODO*.
from_vertices: An array of ``from`` vertices, i.e., vertices from which the
ray paths start. In a radio communications context, this is usually
an array of transmitters.
to_vertices: An array of ``to`` vertices, i.e., vertices to which the
ray paths end. In a radio communications context, this is usually
an array of receivers.
mirror_vertices: An array of mirror vertices. For each mirror, any
vertex on the infinite plane that describes the mirror is considered
to be a valid vertex.
mirror_normals: An array of mirror normals, where each normal has a unit
length and if perpendicular to the corresponding mirror.
Returns:
*TODO*.
Return:
An array of ray paths obtained with the image method.
.. note::
Expand Down Expand Up @@ -225,16 +232,20 @@ def consecutive_vertices_are_on_same_side_of_mirrors(
Check if consecutive vertices, but skiping one every other vertex, are on the same side of a given mirror. The number of vertices ``num_vertices`` must be equal to ``num_mirrors + 2``.
This check is needed after using :func:`image_method` because it can return
vertices that are behind a mirror, which causes the path to go trough this
vertices that are behind a mirror, which causes the path to go through this
mirror, and is someone we want to avoid.
Args:
vertices: *TODO*.
mirror_vertices: *TODO*.
mirror_normals: *TODO*.
vertices: An array of vertices, usually describing ray paths.
mirror_vertices: An array of mirror vertices. For each mirror, any
vertex on the infinite plane that describes the mirror is considered
to be a valid vertex.
mirror_normals: An array of mirror normals, where each normal has a unit
length and if perpendicular to the corresponding mirror.
Returns:
*TODO*.
Return:
A boolean array indicating whether pairs of consecutive vertices
are on the same side of the corresponding mirror.
"""
chex.assert_axis_dimension(vertices, 0, mirror_vertices.shape[0] + 2)

Expand Down
8 changes: 4 additions & 4 deletions python/differt/rt/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def generate_all_path_candidates(
num_primitives: The (positive) number of primitives.
order: The path order. An order less than one returns an empty array.
Returns:
Return:
An unsigned array with primitive indices on each columns. Its number of
columns is actually equal to
``num_primitives * ((num_primitives - 1) ** (order - 1))``.
Expand All @@ -75,7 +75,7 @@ def generate_all_path_candidates_iter(
num_primitives: The (positive) number of primitives.
order: The path order.
Returns:
Return:
An iterator of unsigned arrays with primitive indices.
"""
return map(
Expand All @@ -96,7 +96,7 @@ def generate_all_path_candidates_chunks_iter(
order: The path order.
chunk_size: The size of each chunk.
Returns:
Return:
An iterator of unsigned arrays with primitive indices.
"""
return map(
Expand Down Expand Up @@ -133,7 +133,7 @@ def rays_intersect_triangles(
triangle edges, a very common case if geometries are planes
split into multiple triangles.
Returns:
Return:
For each ray, return the scale factor of ``ray_directions`` for the
vector to reach the corresponding triangle, and whether the intersection
actually lies inside the triangle.
Expand Down
Loading

0 comments on commit b153bf2

Please sign in to comment.