differt.geometry.viewing_frustum

Contents

differt.geometry.viewing_frustum#

viewing_frustum(viewing_vertex, world_vertices, *, active_vertices=None, reduce=False)[source]#

Compute the viewing frustum as seen by one viewer.

The frustum is a region, expressed in spherical coordinates, see Spherical coordinates, that fully contains the world vertices.

Warning

The frustum may present wrong results along the polar axis.

We are still looking at a better way to compute the frustum, so feel free to reach out if your have any suggestion.

Parameters:
Return type:

Float[Array, '*batch 2 3'] | Float[Array, '2 3']

Returns:

The extents (min. and max. values) of the viewing frustum.

Examples

The following example shows how to launch rays in a limited region of space, to avoid launching rays where no triangles would be hit.

>>> from differt.geometry import (
...     fibonacci_lattice,
...     viewing_frustum,
...     TriangleMesh,
... )
>>> from differt.plotting import draw_rays, reuse, draw_markers
>>>
>>> with reuse("plotly") as fig:
...     tx = jnp.array([0.0, 0.0, 0.0])
...     key = jax.random.key(1234)
...     draw_markers(tx.reshape(-1, 3), labels=["tx"], showlegend=False)
...     for mesh in (
...         TriangleMesh.box(with_top=True).translate(tx).iter_objects()
...     ):
...         key, key_color = jax.random.split(key, 2)
...         color = r, g, b = jax.random.randint(key_color, (3,), 0, 256)
...         center = mesh.bounding_box.mean(axis=0)
...         mesh = mesh.translate(5 * (center - tx)).set_face_colors(color)
...         mesh.plot(opacity=0.5)
...
...         frustum = viewing_frustum(
...             tx, mesh.triangle_vertices.reshape(-1, 3)
...         )
...         ray_origins, ray_directions = jnp.broadcast_arrays(
...             tx, fibonacci_lattice(20, frustum=frustum)
...         )
...         ray_origins += 0.5 * ray_directions
...         ray_directions *= 2.5  # Scale rays length before plotting
...         draw_rays(
...             ray_origins,
...             ray_directions,
...             color=f"rgb({r:f},{g:f},{b:f})",
...             showlegend=False,
...         )
>>> fig

This second example shows what happens if you compute the frustum on all the objects at the same time, instead of computing one frustum per object (i.e., face).

>>> with reuse("plotly") as fig:
...     tx = jnp.array([0.0, 0.0, 0.0])
...     world_vertices = jnp.empty((0, 3))
...     draw_markers(tx.reshape(-1, 3), labels=["tx"], showlegend=False)
...     for mesh in (
...         TriangleMesh
...         .box(with_top=True)
...         .translate(tx)
...         .set_face_colors(jnp.array([1.0, 0.0, 0.0]))
...         .iter_objects()
...     ):
...         center = mesh.bounding_box.mean(axis=0)
...         mesh = mesh.translate(5 * (center - tx))
...         mesh.plot(opacity=0.5)
...
...         world_vertices = jnp.concatenate(
...             (world_vertices, mesh.triangle_vertices.reshape(-1, 3)),
...             axis=0,
...         )
...
...     frustum = viewing_frustum(tx, world_vertices)
...     ray_origins, ray_directions = jnp.broadcast_arrays(
...         tx, fibonacci_lattice(20 * 6, frustum=frustum)
...     )
...     ray_origins += 0.5 * ray_directions
...     ray_directions *= 2.5  # Scale rays length before plotting
...     draw_rays(
...         ray_origins,
...         ray_directions,
...         color="red",
...         showlegend=False,
...     )
>>> fig

While the rays cover all the objects, many of them are launching in spatial regions where there is not object to hit.

This third example shows a scenario where TX is far from the mesh, where computing the frustum becomes very suitable.

>>> with reuse("plotly") as fig:
...     tx = jnp.array([30.0, 0.0, 20.0])
...     draw_markers(tx.reshape(-1, 3), labels=["tx"], showlegend=False)
...     mesh = TriangleMesh.box(
...         width=10.0, length=20.0, height=3.0, with_top=True
...     ).set_face_colors(jnp.array([1.0, 0.0, 0.0]))
...     mesh.plot(opacity=0.5)
...
...     frustum = viewing_frustum(tx, mesh.triangle_vertices.reshape(-1, 3))
...     ray_origins, ray_directions = jnp.broadcast_arrays(
...         tx, fibonacci_lattice(20 * 6, frustum=frustum)
...     )
...     ray_origins += 0.5 * ray_directions
...     ray_directions *= 40.0  # Scale rays length before plotting
...     draw_rays(
...         ray_origins,
...         ray_directions,
...         color="red",
...         showlegend=False,
...     )
>>> fig