ddg.visualization.blender.bmesh module

ddg.visualization.blender.bmesh.from_pydata(verts, edges, faces, bm=None, doubles=False)[source]

Add vertices, edges and faces to a BMesh.

Parameters:
vertssequence of vertices
edgessequence of edges
facessequence of faces
bmbmesh (default=None)

If None, a new BMesh is created. Otherwise bm is cleared before the vertices, edges and faces are added.

doublesbool (default=False)

If True, duplicate edges or faces are filtered out.

Returns:
BMesh

A BMesh with the given vertices, edges and faces.

ddg.visualization.blender.bmesh.bmesh_from_mesh(mesh)[source]

Context manager to create a BMesh.

Provides a BMesh bm. If mesh belongs to an object in edit mode, then

  • bm results from bmesh.from_edit_mesh(mesh) and

  • bmesh.update_edit_mesh(mesh) is called upon exiting the context.

Otherwise

  • bm is a new BMesh created from mesh and

  • upon exiting the context, bm.to_mesh(mesh) is called and bm is freed.

Parameters:
meshbpy.types.Mesh

See also

ddg.visualization.blender.context.mode

A context manager to switch between modes, e.g. OBJECT and EDIT mode.

Examples

The following examples assume that the currently selected object is the default cube. We also use the ddg.visualization.blender.context.mode() context manager to set a mode for a context and switch to a different mode upon exiting said context.

>>> import numpy as np
>>> from ddg.visualization.blender.context import mode
>>> from ddg.visualization.blender.bmesh import bmesh_from_mesh
>>>
>>> bpy.ops.mesh.primitive_cube_add()
{'FINISHED'}
>>> cube = bpy.context.object
>>>
>>> def maximum_norm(x):
...     '''Determine if the ||v||_max = x for all cube mesh vertices v.'''
...     return all(
...         np.isclose(np.linalg.norm(v.co, np.inf), x) for v in cube.data.vertices
...     )
...
>>> assert maximum_norm(1.0)
>>> with mode(cube, mode="EDIT", exit_mode="OBJECT"):
...     # The cube is in edit mode.
...     with bmesh_from_mesh(cube.data) as bm:
...         # bm is the edit mesh.
...         for v in bm.verts:
...             v.co *= 3.0
...     # After exiting the inner context, changes to the edit mesh have
...     # yet to be written to the cube!
...     assert maximum_norm(1.0)
...
... # After exiting the outer context, the cube is in object mode
... # and changes to the edit mesh have been written to the cube.
...
>>> assert cube.mode == "OBJECT"
>>> assert maximum_norm(3.0)

For performance reasons, Blender doesn’t write changes to the edit mesh to the underlying mesh itself until exiting edit mode. For example,

>>> bpy.ops.object.mode_set(mode="EDIT")
{'FINISHED'}
>>> with bmesh_from_mesh(cube.data) as bm:
...     for v in bm.verts:
...         v.co *= 2.0
...
>>> # Maximum norm of all MESH vertices is still 3.0 as before.
>>> # However, if running this example interactively, the cube
>>> # should appear much larger in the viewport.
>>> assert maximum_norm(3.0)

After exiting edit mode, Blender writes the changes to cube.data. The maximum norm of all vertices is now equal to 6 = 3 * 2, as expected.

>>> bpy.ops.object.mode_set(mode="OBJECT")
{'FINISHED'}
>>> assert maximum_norm(6.0)

This is one reason why ddg.visualization.blender.context.mode() is useful - it makes it more difficult to forget exiting edit mode when using Blender programmatically.

ddg.visualization.blender.bmesh.transform(bm, M)[source]

Transform vertices of a BMesh by a matrix.

Parameters:
bmBMesh
Mndarray

An array of shape (3, 3) or (4, 4).

Returns:
BMesh

The input BMesh bm with transformed vertices.

Raises:
ValueError

If shape of M is not (3, 3) or (4, 4).

ddg.visualization.blender.bmesh.join(*bmeshes, free=False, bm=None)[source]

Join BMeshes.

Parameters:
*bmeshesBMesh

BMeshes to join.

freebool (default=False)

If True, the input BMeshes are freed.

bmBMesh (default=None)

If None, a new BMesh is created. Otherwise bm will be cleared before the joined BMeshes are written to bm.

Returns:
BMesh

BMesh ‘containing’ the input BMeshes.

ddg.visualization.blender.bmesh.cut_half_space(bm, normal, dist)[source]

Intersects a BMesh with a half space.

Let n := normal / ||normal||. The half space is defined by:

<x, n> < dist

A picture:

                 discarded

                 <n, v> > dist
──────────────▲─────────────────  <n, v> = dist
              │  <n, v> < dist
      dist * n│
              │  not discarded
              x
            origin
Parameters:
bmBMesh
normalnumpy.ndarray of shape (3,)

Outward normal of the half space.

distfloat

Distance of the boundary of the half space to the origin.

Raises:
ValueError

If normal.shape != (3,).

ddg.visualization.blender.bmesh.cut_between_coordinate_half_spaces(bm, axis, dist, center=array([0., 0., 0.]))[source]

Intersect a BMesh with two half spaces.

Let e_0, e_1, e_2 be the standard basis of R^3. The BMesh is intersected with the region defined by:

dist < <x - center, e_axis> < dist

which is the intersection of two coordinate half spaces. A picture where axis is 1, which corresponds to the y-axis:

       ▲
       │
y-axis │
       │            discarded
       │
───────┼────────────────────▲────────────
       │                    │
       │                    │dist
       │                    │
       │    not discarded   *center
       │                    │
       │                    │dist
       │                    │
───────┼────────────────────▼────────────
       │
       │            discarded
       │
       └────────────────────────────►
    z-axis points at eye           x-axis
Parameters:
bmBMesh
axis{0, 1, 2}
distfloat
centernumpy.ndarray of shape (3,)
Raises:
ValueError

If axis is not 0, 1 or 2. If center.shape != (3,).

ddg.visualization.blender.bmesh.cut_bounding_box(bm, distances, center=array([0., 0., 0.]))[source]

Intersect a BMesh with an axis aligned bounding box.

Let e_0, e_1, e_2 be the standard basis of R^3. The BMesh is intersected with the axis aligned bounding box centered at center, whose distances to the sides are given by distances. If a distance is None, it is assumed to be infinity, so the box isn’t required to be bounded. More precisely, the box is given by:

{x in R^3 | - distances[i] < <x - center, e_i> < distances[i]}.

A picture of the box:

y-axis
    ▲
    │  ┌───────────▲───────────┐
    │  │        d_0│           │     d_0 = distances[0]
    │  │           │       d_1 │     d_1 = distances[1]
    │  │           └───────────►
    │  │        center         │
    │  │                       │
    │  └───────────────────────┘
    │
    └────────────────────────────►
 z-axis points at eye           x-axis
Parameters:
bmBMesh
distancessequence of floats or None of length 3
centernumpy.ndarray of shape (3,)
Raises:
ValueError

If len(distances) != (3,). If center.shape != (3,).