differt.geometry.TriangleMesh#

class TriangleMesh(vertices, triangles, face_colors=None, face_materials=None, material_names=<factory>, object_bounds=None, assume_quads=False, mask=None)[source]#

Bases: Module

A simple geometry made of triangles.

Attributes

assume_quads

Flag indicating whether triangles can be paired into quadrilaterals.

at

Helper property for updating or indexing a subset of triangle vertices.

bounding_box

The bounding box (min.

diffraction_edges

The diffraction edges.

diffraction_edges_mask

The mask to select valid diffraction edges from triangle_edges.

face_colors

The array of face colors.

face_materials

The array of face materials.

is_empty

Whether this scene has no triangle.

mask

An optional mask to indicate which triangles are active.

normals

The triangle normals.

num_active_primitives

The number of active primitives.

num_active_quads

The number of active quadrilaterals.

num_active_triangles

The number of active triangles.

num_primitives

The number of primitives.

num_quads

The number of quadrilaterals.

num_triangles

The number of triangles.

object_bounds

The array of object indices.

triangle_edges

The triangle edges.

triangle_vertices

The array of indexed triangle vertices.

vertices

The array of triangle vertices.

triangles

The array of triangle indices.

material_names

The list of material names (must be unique).

Methods

append(other)

Return a new mesh by appending another mesh to this one.

box([length, width, height, with_top, ...])

Create a box mesh, with an optional opening on the top.

clip([x_min, x_max, y_min, y_max, z_min, z_max])

Clip all vertex coordinates to the given bounds.

drop_duplicates()

Return a new mesh with duplicate vertices removed.

empty()

Create a empty mesh.

from_core(core_mesh)

Return a triangle mesh from a mesh created by the differt_core module.

iter_objects()

Return an iterator over sub meshes (i.e., objects) defined by object_bounds.

keep_all_within([x_min, x_max, y_min, ...])

Return a new mesh, keeping only the triangles with all vertices within the given bounds.

keep_any_within([x_min, x_max, y_min, ...])

Return a new mesh, keeping only the triangles with at least one vertex within the given bounds.

load_obj(file)

Load a triangle mesh from a Wavefront .obj file.

load_ply(file)

Load a triangle mesh from a Stanford PLY .ply file.

masked()

Return a new instance of this object that only keeps masked (i.e., active) triangles.

plane(vertex_a[, vertex_b, vertex_c, ...])

Create a plane mesh, made of two triangles.

plot(*[, show_normals, show_triangle_edges, ...])

Plot this mesh on a 3D scene.

rotate(rotation_matrix)

Return a new mesh by applying a rotation matrix to all triangle coordinates.

sample(size[, replace, preserve, ...])

Generate a new mesh by randomly sampling primitives from this geometry.

scale(scale_factor)

Return a new mesh by applying a scale factor to all triangle coordinates.

set_assume_quads([flag])

Return a new instance of this scene with TriangleMesh.assume_quads set to flag.

set_face_colors([colors, key])

Return a new instance of this mesh, with new face colors.

set_face_materials(materials)

Return a new instance of this mesh, with new face materials.

set_materials(*names)

Return a new instance of this mesh, with new face materials from material names.

shuffle([preserve, return_indices])

Generate a new mesh by randomly shuffling primitives from this geometry.

translate(translation)

Return a new mesh by applying a translation to all triangle coordinates.

Detailed documentation

append(other)[source]#

Return a new mesh by appending another mesh to this one.

Tip

For convenience, you can also use the + operator.

Note

The following rules are applied when merging two meshes:

  • The vertices are concatenated;

  • The triangles are concatenated, and the indices of the second mesh are updated;

  • The face colors are concatenated. If one mesh has colors while the other does not, then mesh with no colors will have its face colors set to black (0, 0, 0);

  • The face materials are concatenated. If other has face materials not included in self, then the face materials from other are renumbered. If one mesh has colors while the other does not, then mesh with no colors will have its face materials set to -1;

  • The material names are merged, keeping only unique names;

  • The object bounds are concatenated only if both meshes have them set, otherwise, the object bounds are set to None;

  • The masks are concatenated if present in both meshes. If one mesh has a mask while the other does not, then the mesh with no mask will have its mask set to all triangles being active (i.e., True).

  • The assume_quads flag is set to True if both meshes have it set to True.

Two important exceptions are:

  • If one mesh is empty, a new instance of the other mesh is returned as is;

  • If both meshes are empty, then a new instance of self is returned.

Parameters:

other (TriangleMesh) – The mesh to append.

Return type:

Self

Returns:

The new mesh with a structure that is the result of combining both input meshes.

Examples

The following example shows how to create a mesh of nested cubes.

>>> from differt.geometry import TriangleMesh
>>>
>>> mesh = TriangleMesh.empty()
>>> for i in range(3):
...     size = 1.0 / (i + 1)
...     mesh += TriangleMesh.box(length=size, width=size, height=size)
>>> mesh = mesh.set_assume_quads().set_face_colors(
...     key=jax.random.key(1234)
... )
>>> fig = mesh.plot(opacity=0.5, backend="plotly")
>>> fig
assume_quads: bool = False#

Flag indicating whether triangles can be paired into quadrilaterals.

Setting this to True will not check anything, except that num_triangles is even, but each two consecutive triangles are assumed to represent a quadrilateral surface.

property at[source]#

Helper property for updating or indexing a subset of triangle vertices.

This at property is used to update vertices of a triangle mesh, based on triangles indices, similar to how the at property is used in jax.numpy.ndarray.at.

In particular, the following methods are available:

  • get(**kwargs): Get the vertices of selected triangles;

  • set(values, **kwargs): Set the vertices of selected triangles to some values;

  • add(values, **kwargs): Add some values to the vertices of selected triangles;

  • sub(values, **kwargs): Subtract some values from the vertices of selected triangles;

  • mul(values, **kwargs): Multiply the vertices of selected triangles by some values;

  • div(values, **kwargs): Divide the vertices of selected triangles by some values;

  • pow(values, **kwargs): Raise the vertices of selected triangles to some power;

  • min(values, **kwargs): Take the element-wise minimum with the vertices of selected triangles;

  • max(values, **kwargs): Take the element-wise maximum with the vertices of selected triangles;

  • apply(func, **kwargs): Apply a function to the vertices of selected triangles.

E.g., mesh.at[0:2].add([1.0, 2.0, 3.0]) will translate the first two triangles.

Each method accepts the same keyword arguments as the corresponding get method of jax.numpy.ndarray.at. These keyword arguments control how triangle indices are resolved internally.

Because the vertices of a triangle mesh may be shared between multiple triangles, this method prevents updating the same vertex multiple times by ignoring duplicate vertex indices. As a result, providing duplicate triangle indices will not result in duplicate updates.

Warning

As duplicate vertices are ignored, the number of update vertices is not necessarily equal to the number of triangles selected times three. Moreover, vertices are re-ordered when duplicates are removed. As a results, you should not apply any update that depends on the order of or the number of updated the vertices.

Examples

The following example shows how to translate the first two (triangle) faces.

>>> from differt.geometry import TriangleMesh
>>>
>>> mesh = (
...     sum(
...         TriangleMesh.box().iter_objects(),
...         start=TriangleMesh.empty(),
...     )
...     .at[0:2]
...     .add([1.0, 1.0, 0.0])
... )
>>> fig = mesh.plot(opacity=0.5, backend="plotly")
>>> fig

In the above example, splitting the cube mesh into separate objects is necessary, as the vertices of the cube are shared between the faces. If the cube was not split, the translation would be applied to all the faces that share the vertices of the first two faces.

>>> mesh = TriangleMesh.box().at[0:2].add([1.0, 1.0, 0.0])
>>> fig = mesh.plot(opacity=0.5, backend="plotly")
>>> fig

Finally, the at property is lazily evaluated, so checking that the index is valid is not performed until a method is called.

>>> from differt.geometry import TriangleMesh
>>>
>>> mesh = TriangleMesh.box()
>>> mesh.at
_TriangleMeshVerticesUpdateHelper(TriangleMesh(
  vertices=f32[8,3],
  triangles=i32[10,3],
  material_names=(),
  object_bounds=i32[5,2]
))
>>> index = jnp.array([True, False])
>>> mesh.at[index]
_TriangleMeshVerticesUpdateRef(TriangleMesh(
  vertices=f32[8,3],
  triangles=i32[10,3],
  material_names=(),
  object_bounds=i32[5,2]
), Array([ True, False], dtype=bool))
>>> mesh.at[index].add(1.0)
Traceback (most recent call last):
IndexError: boolean index did not match shape of indexed array in index 0:
got (2,), expected (10,)
property bounding_box: Float[Array, '2 3'][source]#

The bounding box (min. and max. coordinates).

Important

Setting mask will have an effect on the bounding box, as the bounding box is computed only for the active triangles.

classmethod box(length=1.0, width=1.0, height=1.0, *, with_top=False, with_bottom=True)[source]#

Create a box mesh, with an optional opening on the top.

Note

The mesh satisfies the guarantees expected when setting assume_quads to True.

Parameters:
  • length (Float[ArrayLike, '']) – The length of the box (along x-axis).

  • width (Float[ArrayLike, '']) – The width of the box (along y-axis).

  • height (Float[ArrayLike, '']) – The height of the box (along z-axis).

  • with_top (bool) – Whether the top of part of the box is included or not.

  • with_bottom (bool) – Whether the bottom of part of the box is included or not.

Return type:

Self

Returns:

A new box mesh.

Examples

The following example shows how to create a cube.

>>> from differt.geometry import TriangleMesh
>>>
>>> mesh = (
...     TriangleMesh
...     .box(with_top=True)
...     .set_assume_quads()
...     .set_face_colors(key=jax.random.key(1234))
... )
>>> fig = mesh.plot(opacity=0.5, backend="plotly")
>>> fig

The second example shows how to create a corridor-like mesh, without the ceiling face.

>>> mesh = (
...     TriangleMesh
...     .box(length=10.0, width=3.0, height=2.0)
...     .set_assume_quads()
...     .set_face_colors(key=jax.random.key(1234))
... )
>>> fig = mesh.plot(opacity=0.5, backend="plotly")
>>> fig = fig.update_scenes(aspectmode="data")
>>> fig
clip(x_min=None, x_max=None, y_min=None, y_max=None, z_min=None, z_max=None)[source]#

Clip all vertex coordinates to the given bounds.

Parameters:
Return type:

Self

Returns:

A new mesh with clipped vertices.

Examples

The following example shows the effect on a plotted mesh before and after clipping.

>>> from differt.geometry import TriangleMesh
>>>
>>> mesh = TriangleMesh.box(length=4.0, width=2.0, height=1.0)
>>> fig = mesh.plot(backend="plotly")
>>> fig
>>> clipped = mesh.clip(x_min=-1.0, x_max=1.0, y_min=-0.5, y_max=0.5)
>>> fig = clipped.plot(backend="plotly")
>>> fig
property diffraction_edges: Float[Array, 'num_edges 2 3'][source]#

The diffraction edges.

If you need just-in-time compilation, use diffraction_edges_mask directly.

Warning

The current return value is a placeholder and should be implemented properly.

If you are interested in contributing to this function, please reach out on GitHub.

property diffraction_edges_mask: Bool[Array, 'num_triangles 3'][source]#

The mask to select valid diffraction edges from triangle_edges.

Warning

The current return value is a placeholder and should be implemented properly.

If you are interested in contributing to this function, please reach out on GitHub.

drop_duplicates()[source]#

Return a new mesh with duplicate vertices removed.

Vertices are also sorted in ascending order

Return type:

Self

Returns:

A new mesh with duplicate vertices removed.

classmethod empty()[source]#

Create a empty mesh.

Return type:

Self

Returns:

A new empty scene.

face_colors: Float[Array, 'num_triangles 3'] | None = None#

The array of face colors.

The array contains the face colors, as RGB triplets, with a black color used as defaults (if some faces have a color). This attribute is None if all face colors are unset.

face_materials: Int[Array, 'num_triangles'] | None = None#

The array of face materials.

The array contains the material indices, with a special placeholder value of -1. To obtain the name of the material, see material_names. This attribute is None if all face materials are unset.

classmethod from_core(core_mesh)[source]#

Return a triangle mesh from a mesh created by the differt_core module.

Parameters:

core_mesh (TriangleMesh) – The mesh from the core module.

Return type:

Self

Returns:

The corresponding mesh.

property is_empty: bool[source]#

Whether this scene has no triangle.

iter_objects()[source]#

Return an iterator over sub meshes (i.e., objects) defined by object_bounds.

If object_bounds is None, then yield self.

Yields:

One or more sub meshes.

Return type:

Iterator[Self]

keep_all_within(x_min=None, x_max=None, y_min=None, y_max=None, z_min=None, z_max=None, *, preserve_objects=True, clip=False)[source]#

Return a new mesh, keeping only the triangles with all vertices within the given bounds.

If preserve_objects is set to True, and object_bounds is not None, then all triangles belonging to the same object as a triangle with all vertices within the given bounds are kept, but only if all triangles in that object satisfy the bounds.

Parameters:
Return type:

Self

Returns:

A new mesh with the triangles filtered according to the given bounds.

Seealso::

keep_any_within

Examples

The following example shows how to filter the simple street canyon scene.

>>> from differt.scene import (
...     TriangleScene,
...     download_sionna_scenes,
...     get_sionna_scene,
... )
>>>
>>> download_sionna_scenes()
>>> file = get_sionna_scene("simple_street_canyon")
>>> mesh = TriangleScene.load_xml(file).mesh
>>> fig = mesh.plot(backend="plotly")
>>> fig

Here, we keep the objects that have all vertices inside the selected range.

>>> fig = mesh.keep_all_within(y_min=-20.0).plot(
...     backend="plotly",
... )
>>> fig

By default, and if object_bounds is not None, the filtering is done by objects. You can disable this behavior by setting preserve_objects to False.

>>> fig = mesh.keep_all_within(
...     y_min=-20.0,
...     preserve_objects=False,
... ).plot(backend="plotly")
>>> fig
keep_any_within(x_min=None, x_max=None, y_min=None, y_max=None, z_min=None, z_max=None, *, preserve_objects=True, clip=False)[source]#

Return a new mesh, keeping only the triangles with at least one vertex within the given bounds.

If preserve_objects is set to True, and object_bounds is not None, then all triangles belonging to the same object as a triangle with at least one vertex within the given bounds are kept.

Parameters:
Return type:

Self

Returns:

A new mesh with the triangles filtered according to the given bounds.

Seealso::

keep_all_within

Examples

The following example shows how to filter the simple street canyon scene.

>>> from differt.scene import (
...     TriangleScene,
...     download_sionna_scenes,
...     get_sionna_scene,
... )
>>>
>>> download_sionna_scenes()
>>> file = get_sionna_scene("simple_street_canyon")
>>> mesh = TriangleScene.load_xml(file).mesh
>>> fig = mesh.plot(backend="plotly")
>>> fig

Here, we keep the objects that have all vertices inside the selected range.

>>> fig = mesh.keep_any_within(x_max=0.0).plot(
...     backend="plotly",
... )
>>> fig

By default, and if object_bounds is not None, the filtering is done by objects. You can disable this behavior by setting preserve_objects to False.

>>> fig = mesh.keep_any_within(
...     x_max=0.0,
...     preserve_objects=False,
... ).plot(backend="plotly")
>>> fig

Finally, we can also clip the vertices of the returned mesh to the given bounds. This is especially useful to trim the plane of the ground in outdoor scenes.

>>> fig = mesh.keep_any_within(
...     x_max=+15.0,
...     clip=True,
... ).plot(backend="plotly")
>>> fig
classmethod load_obj(file)[source]#

Load a triangle mesh from a Wavefront .obj file.

Currently, only vertices and triangles are loaded. Triangle normals are computed afterward (when first accessed).

Parameters:

file (str) – The path to the Wavefront .obj file.

Return type:

Self

Returns:

The corresponding mesh containing only triangles.

classmethod load_ply(file)[source]#

Load a triangle mesh from a Stanford PLY .ply file.

Currently, only vertices and triangles are loaded. Triangle normals are computed afterward (when first accessed).

Parameters:

file (str) – The path to the Stanford PLY .ply file.

Return type:

Self

Returns:

The corresponding mesh containing only triangles.

mask: Bool[Array, 'num_triangles'] | None = None#

An optional mask to indicate which triangles are active.

Using a mask allows to represent multiple sub-meshes of a single mesh, without changing the memory allocated to each sub-mesh. Masks can be conveniently created using the sample method by passing by_masking=True.

Important

Unless specified, the transformation or selection operations, like rotate, will not take the mask into account, and will apply to all triangles.

Important

When assume_quads is True, a quad is considered active if both triangles that form the quad are active.

masked()[source]#

Return a new instance of this object that only keeps masked (i.e., active) triangles.

Important

This method does not preserve the object_bounds attribute.

Return type:

Self

Returns:

A new paths instance with flattened batch dimensions and only valid paths.

material_names: tuple[str, ...]#

The list of material names (must be unique).

property normals: Float[Array, 'num_triangles 3'][source]#

The triangle normals.

property num_active_primitives: int | Int[Array, ''][source]#

The number of active primitives.

This is a convenient alias to num_active_quads if assume_quads is True else num_active_triangles.

If mask is not None, then the output value can be traced by JAX.

property num_active_quads: int | Int[Array, ''][source]#

The number of active quadrilaterals.

If mask is not None, then the output value can be traced by JAX.

Raises:

ValueError – If assume_quads is False.

property num_active_triangles: int | Int[Array, ''][source]#

The number of active triangles.

If mask is not None, then the output value can be traced by JAX.

property num_primitives: int[source]#

The number of primitives.

This is a convenient alias to num_quads if assume_quads is True else num_triangles.

property num_quads: int[source]#

The number of quadrilaterals.

Raises:

ValueError – If assume_quads is False.

property num_triangles: int[source]#

The number of triangles.

object_bounds: Int[Array, 'num_primitives 2'] | None = None#

The array of object indices.

If the present mesh contains multiple objects, usually as a result of appending multiple meshes together, this array contain start end end indices for each sub mesh.

Important

The object indices must cover exactly all triangles in this mesh, and be sorted in ascending order. Otherwise, some methods, like the random object coloring with set_face_colors, may not work as expected.

classmethod plane(vertex_a, vertex_b=None, vertex_c=None, *, normal=None, side_length=1.0, rotate=None)[source]#

Create a plane mesh, made of two triangles.

Note

The mesh satisfies the guarantees expected when setting assume_quads to True.

Parameters:
Return type:

Self

Returns:

A new plane mesh.

Raises:

ValueError – If neither vertex_b and vertex_c, nor normal have been provided, or if both have been provided simultaneously.

plot(*, show_normals=False, show_triangle_edges=False, show_diffraction_edges=False, normals_kwargs=None, triangle_edges_kwargs=None, diffraction_edges_kwargs=None, **kwargs)[source]#

Plot this mesh on a 3D scene.

Parameters:
  • show_normals (bool) – Whether to show the normals of the triangles.

  • show_triangle_edges (bool) – Whether to show the edges of the triangles.

  • show_diffraction_edges (bool) – Whether to show the diffraction edges.

  • normals_kwargs (Mapping[str, Any] | None) – A mapping of keyword arguments passed to draw_rays.

  • triangle_edges_kwargs (Mapping[str, Any] | None) – A mapping of keyword arguments passed to draw_paths.

  • diffraction_edges_kwargs (Mapping[str, Any] | None) – A mapping of keyword arguments passed to draw_paths.

  • kwargs (Any) – Keyword arguments passed to draw_mesh.

Return type:

Any

Returns:

The resulting plot output.

Examples

The following examples show how to customize the plotting of a cube.

First, we plot the mesh with a default color.

>>> from differt.geometry import TriangleMesh
>>> mesh = TriangleMesh.box(with_top=True)
>>> fig = mesh.plot(opacity=0.5, backend="plotly")
>>> fig

Next, we plot the mesh, but with normals and triangle edges.

>>> fig = mesh.plot(
...     show_normals=True,
...     show_triangle_edges=True,
...     normals_kwargs={"name": "normals", "color": "red"},
...     triangle_edges_kwargs={
...         "name": "triangle edges",
...         "line_color": "yellow",
...     },
...     opacity=0.5,
...     backend="plotly",
... )
>>> fig

Finally, we plot the mesh with diffraction edges. Diffraction edges are relatively expensive to compute, as they are computed by first computing the triangle edges, then removing duplicates, and removing co-planar edges.

>>> fig = mesh.plot(
...     show_diffraction_edges=True,
...     diffraction_edges_kwargs={
...         "name": "diffraction edges",
...         "line_color": "yellow",
...     },
...     opacity=0.5,
...     backend="plotly",
... )
>>> fig
rotate(rotation_matrix)[source]#

Return a new mesh by applying a rotation matrix to all triangle coordinates.

Parameters:

rotation_matrix (Float[ArrayLike, '3 3']) – The rotation matrix.

Return type:

Self

Returns:

The new rotated mesh.

sample(size, replace=False, preserve=False, *, by_masking=False, sample_objects=False, key)[source]#

Generate a new mesh by randomly sampling primitives from this geometry.

Important

If by_masking is set to True, then this function is compatible with jax.jit. Conversely, if by_masking is set to False, then this function is not compatible with jax.jit, as the size of the output mesh is not known at compile time.

Parameters:
  • size (int | Float[ArrayLike, '']) –

    The size of the sample, i.e., the number of primitives (or objects, if sample_objects is True).

    If a floating point number is provided, it is interpreted as a fill factor, i.e., the fraction of primitives to sample from the mesh. This is only supported if by_masking is set to True.

  • replace (bool) –

    Whether to sample with or without replacement.

    Cannot be used with by_masking set to True.

  • preserve (bool) –

    Whether to preserve object_bounds, otherwise it is discarded.

    Object bounds are re-generated by sorting the randomly generated samples, which takes additional time.

    Setting this to True has no effect if object_bounds is None.

    Cannot be used with by_masking set to True.

  • by_masking (bool) – Whether to sample by masking the primitives. If True, then the mask attribute set (ignoring any existing mask).

  • sample_objects (bool) – Whether to sample by objects, i.e., sampling whole objects at once. The value of size is interpreted as the number of objects to sample, or the fill factor of objects to sample.

  • key (Key[Array, ''] | UInt32[Array, '2']) – The jax.random.key to be used.

Return type:

Self

Returns:

A new random mesh.

Raises:
scale(scale_factor)[source]#

Return a new mesh by applying a scale factor to all triangle coordinates.

Parameters:

scale_factor (Float[ArrayLike, '']) – The scale factor.

Return type:

Self

Returns:

The new scaled mesh.

set_assume_quads(flag=True)[source]#

Return a new instance of this scene with TriangleMesh.assume_quads set to flag.

Unlike with using equinox.tree_at, this function will also perform runtime checks.

Parameters:

flag (bool) – The new flag value.

Return type:

Self

Returns:

A new mesh with the same structure with TriangleMesh.assume_quads set to flag.

set_face_colors(colors=None, *, key=None)[source]#

Return a new instance of this mesh, with new face colors.

Parameters:
Return type:

Self

Returns:

A new mesh with updated face colors.

Raises:

ValueError – If colors or key is not specified.

Examples

The following example shows how this function paints the mesh, for different argument types.

First, we load a scene from Sionna [9], that is already colored, and extract the mesh from it.

>>> from differt.scene import (
...     TriangleScene,
...     download_sionna_scenes,
...     get_sionna_scene,
... )
>>>
>>> download_sionna_scenes()
>>> file = get_sionna_scene("simple_street_canyon")
>>> mesh = TriangleScene.load_xml(file).mesh
>>> fig = mesh.plot(backend="plotly")
>>> fig

Then, we could set the same color to all triangles.

>>> fig = mesh.set_face_colors(jnp.array([0.8, 0.2, 0.0])).plot(
...     backend="plotly"
... )
>>> fig

We could also manually specify a different color for each triangle, but it can become tedious as the number of triangles gets larger. Another option is to rely on automatic random coloring, using the key argument.

As our mesh is a collection of 7 distinct objects, as this was loaded from a Sionna XML file, this utility will automatically detect it and color each object differently.

>>> mesh.object_bounds
Array([[ 0, 12],
       [12, 24],
       [24, 36],
       [36, 48],
       [48, 60],
       [60, 72],
       [72, 74]], dtype=int32)
>>> fig = mesh.set_face_colors(key=jax.random.key(1234)).plot(
...     backend="plotly"
... )
>>> fig

If you prefer to have per-triangle coloring, you can perform surgery on the mesh to remove its object_bounds attribute.

>>> import equinox as eqx
>>>
>>> mesh = eqx.tree_at(lambda m: m.object_bounds, mesh, None)
>>> fig = mesh.set_face_colors(key=jax.random.key(1234)).plot(
...     backend="plotly"
... )
>>> fig

Finally, you can also set assume_quads to True to color quadrilaterals instead.

>>> fig = (
...     mesh
...     .set_assume_quads()
...     .set_face_colors(key=jax.random.key(1234))
...     .plot(backend="plotly")
... )
>>> fig
set_face_materials(materials)[source]#

Return a new instance of this mesh, with new face materials.

Parameters:

materials (Int[ArrayLike, ''] | Int[ArrayLike, '#num_triangles']) –

The material indices. If one material is provided, it will be applied to all triangles.

No check is performed to verify that material indices are actually in bounds of material_names.

Return type:

Self

Returns:

A new mesh with updated face materials.

set_materials(*names)[source]#

Return a new instance of this mesh, with new face materials from material names.

If a material name is not in material_names, it is added.

Parameters:

names (str) – The material names. If one name is provided, it will be applied to all triangles.

Return type:

Self

Returns:

A new mesh with updated face materials.

Raises:

ValueError – If the number of names is not 1, num_triangles, or num_primitives (if assume_quads is set to True).

shuffle(preserve=False, *, return_indices=False, key)[source]#

Generate a new mesh by randomly shuffling primitives from this geometry.

Parameters:
Return type:

Self | tuple[Self, Int[ArrayLike, 'num_triangles']]

Returns:

A new random mesh.

Raises:

NotImplementedError – If preserve is True.

translate(translation)[source]#

Return a new mesh by applying a translation to all triangle coordinates.

Parameters:

translation (Float[ArrayLike, '3']) – The translation vector.

Return type:

Self

Returns:

The new translated mesh.

property triangle_edges: Float[Array, 'num_triangles 3 2 3'][source]#

The triangle edges.

property triangle_vertices: Float[Array, 'num_triangles 3 3'][source]#

The array of indexed triangle vertices.

triangles: Int[Array, 'num_triangles 3']#

The array of triangle indices.

vertices: Float[Array, 'num_vertices 3']#

The array of triangle vertices.