Source code for ddg.conversion.blender.halfedge

import bpy
import bmesh
import ddg

import ddg.datastructures.halfedge.get as get
import ddg.datastructures.halfedge.set as setutils
import ddg.math.euclidean
from ddg.datastructures.halfedge.surface_generator import cylinder, icosphere
from ddg.visualization.blender.mesh import from_bmesh, shade_smooth, add_root
import numpy as np


[docs]def hes_to_bmesh(hes, co_attr="co", bpy_data=None): """ Converts a given halfedge surface into a bmesh. The returned bmesh can then be used for handing over the surface to blender. Parameters ---------- hes : ddg.halfedge.Surface The halfedge surface that shall be converted to a bmesh. co_attr : coordinate attribute The type of coordinates that will be taken for conversion. bpy_data : bmesh (optional, default=None) When given, bmesh to save the data into. Returns ------- bmesh The bmesh corresponding to the input halfedge surface. """ if bpy_data == None: hes_bmesh = bmesh.new() else: hes_bmesh = bpy_data hes_bmesh.clear() co = getattr(hes.verts, co_attr) # create vertices # by adding new vertices to the bmesh for _ in range(len(hes.verts)): hes_bmesh.verts.new() # and handing over the coordinates hes_bmesh.verts.ensure_lookup_table() tmp_dict = dict() # for rebuilding the faces: save the correspondence of # the bmesh-vertex and its corresponding hes-vertex-id for i, v in enumerate(hes.verts): hes_bmesh.verts[i].co = co[v] tmp_dict[id(v)] = hes_bmesh.verts[i] # create faces for f in hes.faces: face_verts = list(get.get_vertices(f)) hes_bmesh.faces.new([tmp_dict[id(v)] for v in face_verts]) # catch edges not belonging to a face for e in get.single_edges(hes): if e.face is None and e.opp.face is None: hes_bmesh.edges.new([tmp_dict[id(e.opp.head)], tmp_dict[id(e.head)]]) return hes_bmesh
[docs]def hes_to_tubes_and_spheres_blender_object(hes, co_attr='co', tube_resolution=20, fill_tube_caps=True, tube_radius=.05, sphere_subdivision_steps=2, sphere_radius=.1, parent_kwargs={}, kwargs_generator=None): """ Converts a given half-edge surface to a blender object consisting of tubes and spheres. The tubes represent the edges and the spheres represent the vertices of the surface. Their respective blender objects are linked to a common parent object. Smooth shading is always enabled. Parameters ---------- hes : ddg.halfedge.Surface The half-edge surface that shall be converted to a Blender object. co_attr : str (default='co') The name of vertex attribute storing the euclidean coordinates. tube_resolution: int (default=20) Resolution of the edge tube, i.e., number of faces of a tube. fill_tube_caps: bool (default=False) Decides whether the ends of the tubes are closed by faces (or left open). tube_radius: float (default=.05) Radius of the edge tube. sphere_subdivision_steps: int (default=2) Number of subdivisions of the icospheres at the vertices. sphere_radius: float (default=.1) Radius of the icospheres at the vertices. parent_kwargs: dict (default={}) Dictionary containing the keyword arguments that are handed to ddg.to_blender_object_helper when initializing the parent blender object. kwargs_generator: function (default=None) Function of signature kwargs_generator(cell) returning a kwargs dictionary where a cell can either be a vertex or an edge. If such a function is given, the returned dictionary will be handed over to ddg.to_blender_object_helper when creating the tubes and spheres. For example, it simply could return different material names for vertex spheres and edge tubes, respectively. Returns ------- blender.types.Object Blender object that stores tubes and spheres as children. """ setutils.set_euclidean_length_attr(hes, co_attr=co_attr, attr_name='length') # create parent blender object parent_bobj = add_root(**parent_kwargs) # create hds sphere and tube sphere = icosphere(subdivision_steps=sphere_subdivision_steps, radius=sphere_radius) tube = cylinder(resolution=tube_resolution, fill_caps=fill_tube_caps) for v in hes.verts: sphere_kwargs = {} if kwargs_generator is None else kwargs_generator(v) location = np.array(sphere_kwargs.get('attributes', {}).get('location', np.array([0, 0, 0]))) + np.array( getattr(v, co_attr)) # update attributes by new parent sphere_kwargs['attributes'] = sphere_kwargs.get('attributes', {}) if sphere_kwargs['attributes'].get('parent', None) is not None: raise Warning('Given attributes for vertex sphere contain "parent" that will be overwritten!') sphere_kwargs['attributes'].update({'location': location, 'parent': parent_bobj}) # update kwargs by shade_smooth mesh transformation sphere_kwargs['mesh_transformations'] = sphere_kwargs.get('mesh_transformations', []) sphere_kwargs['mesh_transformations'] = sphere_kwargs['mesh_transformations'] if type( sphere_kwargs['mesh_transformations']) is list else [sphere_kwargs['mesh_transformations']] sphere_kwargs['mesh_transformations'].extend([shade_smooth]) ddg.to_blender_object_helper(sphere, **sphere_kwargs) for e in get.single_edges(hes): matrix_world = ddg.math.euclidean.rotation_from_to(getattr(e.head, co_attr) - getattr(e.tail, co_attr), (0, 1, 0), 4) tube_kwargs = {} if kwargs_generator is None else kwargs_generator(e) # update attributes by matrix_world, parent and scale attributes = tube_kwargs.get('attributes', {}) location = attributes.get('location', np.array([0, 0, 0])) + np.array(getattr(e.tail, co_attr)) if attributes.get('location'): del attributes['location'] if np.any([attributes.get('matrix_world', None), attributes.get('parent', None), attributes.get('scale', None)]): raise Warning('Given attributes for edge tube contain either "matrix_world", "parent", or "scale" that ' 'will be overwritten!') attributes.update({'matrix_world': matrix_world, 'location': location, 'parent': parent_bobj, 'scale': (tube_radius, e.length, tube_radius)}) tube_kwargs['attributes'] = attributes # update kwargs by shade_smooth mesh transformation tube_kwargs['mesh_transformations'] = tube_kwargs.get('mesh_transformations', []) tube_kwargs['mesh_transformations'] = tube_kwargs['mesh_transformations'] if type( tube_kwargs['mesh_transformations']) is list else [tube_kwargs['mesh_transformations']] tube_kwargs['mesh_transformations'].extend([shade_smooth]) ddg.to_blender_object_helper(tube, **tube_kwargs) delattr(hes.edges, 'length') return parent_bobj
[docs]def hes_to_mesh(hes, name="Halfedge mesh", co_attr="co"): hes_bmesh = hes_to_bmesh(hes, co_attr=co_attr) return from_bmesh(hes_bmesh, name, free=True)
[docs]def hes_to_blender_object(hes, name="Halfedge object", co_attr="co", parent=None, matrix=np.eye(4)): hes_mesh = hes_to_mesh(hes, name=name, co_attr=co_attr) obj = bpy.data.objects.new(name, hes_mesh) obj.parent = parent obj.matrix_local = matrix bpy.context.scene.collection.objects.link(obj) return obj