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:
ModuleA simple geometry made of triangles.
Attributes
Flag indicating whether triangles can be paired into quadrilaterals.
Helper property for updating or indexing a subset of triangle vertices.
The bounding box (min.
The diffraction edges.
The mask to select valid diffraction edges from
triangle_edges.The array of face colors.
The array of face materials.
Whether this scene has no triangle.
An optional mask to indicate which triangles are active.
The triangle normals.
The number of active primitives.
The number of active quadrilaterals.
The number of active triangles.
The number of primitives.
The number of quadrilaterals.
The number of triangles.
The array of object indices.
The triangle edges.
The array of indexed triangle vertices.
The array of triangle vertices.
The array of triangle indices.
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.
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_coremodule.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_quadsset toflag.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
otherhas face materials not included inself, then the face materials fromotherare 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_quadsflag is set toTrueif both meshes have it set toTrue.
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
selfis 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
Truewill not check anything, except thatnum_trianglesis 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
atproperty is used to update vertices of a triangle mesh, based on triangles indices, similar to how theatproperty is used injax.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
getmethod ofjax.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
atproperty 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
maskwill 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_quadstoTrue.- 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:
x_min (
Float[ArrayLike, '']|None) – The minimum x coordinate.x_max (
Float[ArrayLike, '']|None) – The maximum x coordinate.y_min (
Float[ArrayLike, '']|None) – The minimum y coordinate.y_max (
Float[ArrayLike, '']|None) – The maximum y coordinate.z_min (
Float[ArrayLike, '']|None) – The minimum z coordinate.z_max (
Float[ArrayLike, '']|None) – The maximum z coordinate.
- 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_maskdirectly.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.
-
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
Noneif 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, seematerial_names. This attribute isNoneif all face materials are unset.
- classmethod from_core(core_mesh)[source]#
Return a triangle mesh from a mesh created by the
differt_coremodule.- Parameters:
core_mesh (
TriangleMesh) – The mesh from the core module.- Return type:
Self- Returns:
The corresponding mesh.
- iter_objects()[source]#
Return an iterator over sub meshes (i.e., objects) defined by
object_bounds.If
object_boundsisNone, then yieldself.- 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_objectsis set toTrue, andobject_boundsis notNone, 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:
x_min (
Float[ArrayLike, '']|None) – The minimum x coordinate.x_max (
Float[ArrayLike, '']|None) – The maximum x coordinate.y_min (
Float[ArrayLike, '']|None) – The minimum y coordinate.y_max (
Float[ArrayLike, '']|None) – The maximum y coordinate.z_min (
Float[ArrayLike, '']|None) – The minimum z coordinate.z_max (
Float[ArrayLike, '']|None) – The maximum z coordinate.preserve_objects (
bool) – Whether to preserve objects.clip (
bool) – Whether to clip the vertices of the returned mesh to the given bounds.
- Return type:
Self- Returns:
A new mesh with the triangles filtered according to the given bounds.
- Seealso::
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_boundsis notNone, the filtering is done by objects. You can disable this behavior by settingpreserve_objectstoFalse.>>> 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_objectsis set toTrue, andobject_boundsis notNone, then all triangles belonging to the same object as a triangle with at least one vertex within the given bounds are kept.- Parameters:
x_min (
Float[ArrayLike, '']|None) – The minimum x coordinate.x_max (
Float[ArrayLike, '']|None) – The maximum x coordinate.y_min (
Float[ArrayLike, '']|None) – The minimum y coordinate.y_max (
Float[ArrayLike, '']|None) – The maximum y coordinate.z_min (
Float[ArrayLike, '']|None) – The minimum z coordinate.z_max (
Float[ArrayLike, '']|None) – The maximum z coordinate.preserve_objects (
bool) – Whether to preserve objects.clip (
bool) – Whether to clip the vertices of the returned mesh to the given bounds.
- Return type:
Self- Returns:
A new mesh with the triangles filtered according to the given bounds.
- Seealso::
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_boundsis notNone, the filtering is done by objects. You can disable this behavior by settingpreserve_objectstoFalse.>>> 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
samplemethod by passingby_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_quadsisTrue, 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_boundsattribute.- Return type:
Self- Returns:
A new paths instance with flattened batch dimensions and only valid paths.
- property num_active_primitives: int | Int[Array, ''][source]#
The number of active primitives.
This is a convenient alias to
num_active_quadsifassume_quadsisTrueelsenum_active_triangles.If
maskis notNone, then the output value can be traced by JAX.
- property num_active_quads: int | Int[Array, ''][source]#
The number of active quadrilaterals.
If
maskis notNone, then the output value can be traced by JAX.- Raises:
ValueError – If
assume_quadsisFalse.
- property num_active_triangles: int | Int[Array, ''][source]#
The number of active triangles.
If
maskis notNone, 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_quadsifassume_quadsisTrueelsenum_triangles.
- property num_quads: int[source]#
The number of quadrilaterals.
- Raises:
ValueError – If
assume_quadsisFalse.
-
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_quadstoTrue.- Parameters:
vertex_a (
Float[ArrayLike, '3']) – The center of the plane.vertex_b (
Float[ArrayLike, '3']|None) –Any second vertex on the plane.
This and
vertex_c, ornormalis required.vertex_c (
Float[ArrayLike, '3']|None) –Any third vertex on the plane.
This and
vertex_b, ornormalis required.normal (
Float[ArrayLike, '3']|None) –The plane normal.
Must be of unit length.
side_length (
Float[ArrayLike, '']) – The side length of the plane.rotate (
Float[ArrayLike, '']|None) – An optional rotation angle, in radians, to be applied around the normal of the plane and its center.
- Return type:
Self- Returns:
A new plane mesh.
- Raises:
ValueError – If neither
vertex_bandvertex_c, nornormalhave 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 todraw_rays.triangle_edges_kwargs (
Mapping[str,Any] |None) – A mapping of keyword arguments passed todraw_paths.diffraction_edges_kwargs (
Mapping[str,Any] |None) – A mapping of keyword arguments passed todraw_paths.
- Return type:
- 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_maskingis set toTrue, then this function is compatible withjax.jit. Conversely, ifby_maskingis set toFalse, then this function is not compatible withjax.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_objectsisTrue).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_maskingis set toTrue.replace (
bool) –Whether to sample with or without replacement.
Cannot be used with
by_maskingset toTrue.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
Truehas no effect ifobject_boundsisNone.Cannot be used with
by_maskingset toTrue.by_masking (
bool) – Whether to sample by masking the primitives. IfTrue, then themaskattribute set (ignoring any existing mask).sample_objects (
bool) – Whether to sample by objects, i.e., sampling whole objects at once. The value ofsizeis interpreted as the number of objects to sample, or the fill factor of objects to sample.key (
Key[Array, '']|UInt32[Array, '2']) – Thejax.random.keyto be used.
- Return type:
Self- Returns:
A new random mesh.
- Raises:
TypeError – If
by_maskingisFalseandsizeis not an integer.ValueError – If
by_maskingisTrueandreplaceorpreserveare set toTrue.ValueError – If
sample_objectsisTrueandobject_boundsisNone.
- 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_quadsset toflag.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_quadsset toflag.
- set_face_colors(colors=None, *, key=None)[source]#
Return a new instance of this mesh, with new face colors.
- Parameters:
colors (
Float[ArrayLike, '#num_triangles 3']|Float[ArrayLike, '3']|None) –The array of RGB colors. If one color is provided, it will be applied to all triangles.
This or
keymust be specified.key (
Key[Array, '']|UInt32[Array, '2']|None) –If provided, colors will be randomly generated.
If
object_boundsis notNone, then triangles within the same object will share the same color. Otherwise, a random color is generated for each triangle (or quadrilateral ifassume_quadsisTrue).
- Return type:
Self- Returns:
A new mesh with updated face colors.
- Raises:
ValueError – If
colorsorkeyis 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
keyargument.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_boundsattribute.>>> 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_quadstoTrueto 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, ornum_primitives(ifassume_quadsis set toTrue).
- shuffle(preserve=False, *, return_indices=False, key)[source]#
Generate a new mesh by randomly shuffling primitives from this geometry.
- Parameters:
preserve (
bool) –Whether to preserve
object_bounds, otherwise it is discarded.Warning
Not implemented yet.
Setting this to
Truehas no effect ifobject_boundsisNone.return_indices (
bool) – Whether to return the indices used for shuffling.key (
Key[Array, '']|UInt32[Array, '2']) – Thejax.random.keyto be used.
- 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_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.