import bmesh
import bpy
import numpy as np
import ddg
from ddg.datastructures.halfedge.surface import Surface
from ddg.datastructures.nets import utils as nutils
from ddg.datastructures.nets.net import (
DiscreteCurve,
DiscreteNet,
EmptyNet,
Net,
NetCollection,
PointNet,
SmoothNet,
)
from ddg.geometry.quadrics import Quadric
from ddg.geometry.subspaces import Subspace
from ddg.visualization.blender.bmesh import join
from ddg.visualization.blender.curve import create_curve, set_curve_properties
from ddg.visualization.blender.material import set_material
from . import halfedge, net
__all__ = ["to_blender_object_helper", "to_blender_object"]
# -------------------------------------------------------------------------------
# -------------------------------------------------------------------------------
# Constants for conversion type
# -------------------------------------------------------------------------------
# We might want to come up with a better encoding
BLD_CONV = 0x1
BLD_SURF = 0x2
BLD_DCUR = 0x4
BLD_DNET = 0x8
BLD_PNET = 0x10
BLD_ENET = 0x20
BLD_CNET = 0x40
BLD_SURF_2 = 0x80
BLD_DCUR_2 = 0x100
BLD_DNET_2 = 0x200
BLD_PNET_2 = 0x400
BLD_ENET_2 = 0x800
BLD_CNET_2 = 0x1000
BLD_FLAT = BLD_SURF | BLD_DCUR | BLD_DNET | BLD_ENET | BLD_CNET
BLD_DEPTH = BLD_SURF_2 | BLD_DCUR_2 | BLD_DNET_2 | BLD_PNET_2 | BLD_ENET_2 | BLD_CNET_2
# -------------------------------------------------------------------------------
class BlenderBridge:
"""Object creating a link between a DDG object and a Blender object.
WARNING:
This class should not be used on its own. Use ddg.to_blender_object or
ddg.to_blender_object_helper instead.
Parameters
----------
obj : DDG object
Object to link.
conv_type : int
Conversion type of the object.
options : dict (optional, default={})
Options to use in the conversion.
attributes : dict (optional, default={})
Attributes set on blender object.
depth_bounding : int (optional, default=None)
Bounding for the first layer of nets.
material : string (optional, default=None)
Material of the created Blender object.
collection : bpy.types.Collection (optional, default=None)
Collection to link the blender object to
mesh_transformations: function or list of functions (optional, default=[])
Functions which should be applied to the mesh of the object.
The transformation functions need to be defined such that
they only take one (required) parameter, namely the mesh.
bmesh_transformations: function or list of functions (optional, default=[])
Functions which should be applied to the bmesh of the object.
The transformation functions need to be defined such that
they only take one (required) parameter, namely the bmesh.
obj_transformations: function or list of functions (optional, default=[])
Functions which should be applied to the object.
The transformation functions need to be defined such that
they only take one (required) parameter, namely the object.
Attributes
----------
obj : DDG object
Object to link.
object : Blender object
Blender object associated with the DDG object.
options : dict
Options to use in the conversion.
conv_type : int
Conversion type of the object.
depth_bounding : int
Bounding for the first layer of nets.
depth : bool
True if obj is net of DDG objects.
bpy_data : bmesh or Blender curve
Blender datastructure used in conversion.
Methods
-------
update
Update Blender data.
animation
Used for animations.
"""
def __init__(
self,
obj,
conv_type,
attributes={},
options={},
depth_bounding=None,
material=None,
collection=None,
mesh_transformations=[],
bmesh_transformations=[],
obj_transformations=[],
link=True,
free=True,
):
if not conv_type & BLD_CONV:
raise TypeError(str(type(obj)) + " is not convertible.")
self.options = options
self.attributes = attributes
self.depth_bounding = depth_bounding
self.obj = obj
self.conv_type = conv_type
self.bmesh_trafos = (
bmesh_transformations
if type(bmesh_transformations) is list
else [bmesh_transformations]
)
self.mesh_trafos = (
mesh_transformations
if type(mesh_transformations) is list
else [mesh_transformations]
)
self.obj_trafos = (
obj_transformations
if type(obj_transformations) is list
else [obj_transformations]
)
self.free = free
curve = False
he = False
if conv_type & BLD_DEPTH:
self.depth = True
if conv_type & BLD_SURF_2:
self.update_bpy_data = halfedge.hes_to_bmesh
he = True
elif conv_type & BLD_DCUR_2:
curve = True
self.update_bpy_data = net.curve_to_bpy_curve
elif conv_type & BLD_DNET_2:
self.update_bpy_data = net.net_to_bmesh
elif conv_type & BLD_PNET_2:
self.update_bpy_data = net.point_to_bmesh
elif conv_type & BLD_ENET_2:
self.update_bpy_data = net.empty_to_bmesh
else:
self.depth = False
if conv_type & BLD_SURF:
self.update_bpy_data = halfedge.hes_to_bmesh
he = True
elif conv_type & BLD_DCUR:
curve = True
self.update_bpy_data = net.curve_to_bpy_curve
elif conv_type & BLD_DNET:
self.update_bpy_data = net.net_to_bmesh
elif conv_type & BLD_PNET:
self.update_bpy_data = net.point_to_bmesh
elif conv_type & BLD_ENET:
self.update_bpy_data = net.empty_to_bmesh
attributes = {
"name": None,
"matrix_world": None,
"location": (0, 0, 0),
**attributes,
}
if attributes["name"] is None:
if hasattr(obj, "name"):
attributes["name"] = obj.name
else:
attributes["name"] = "DDG Object"
if attributes["matrix_world"] is None:
attributes["matrix_world"] = np.eye(4)
attributes["matrix_world"][:-1, -1] = np.array(attributes["location"])
if curve:
self.bpy_data = bpy.data.curves.new(attributes["name"], type="CURVE")
self.bpy_data.dimensions = "3D"
self.object = bpy.data.objects.new(attributes["name"], self.bpy_data)
c_props = self.options["curve_properties"]
del self.options["curve_properties"]
set_curve_properties(self.bpy_data, **c_props)
else:
self.mesh = bpy.data.meshes.new(attributes["name"])
self.bpy_data = bmesh.new()
self.object = bpy.data.objects.new(attributes["name"], self.mesh)
# Every Blender object/curve/mesh must have a distinct .name attribute.
# If one tries to create multiple Blender objects/curves/meshes with
# the same name attributes['name'], then Blender will automatically
# append ".001" to the second object, ".002" to the third object and so
# forth.
# We already created Blender objects/curves/meshes above. This means
# that it's possible that attributes['name'] != self.object.name. We
# should not overwrite self.object.name with the deduplicated name.
for key in attributes:
if key != "name":
setattr(self.object, key, attributes[key])
if collection is None:
collection = bpy.context.scene.collection
if link:
collection.objects.link(self.object)
if material is not None:
set_material(self.object, material)
self.animationflag = False
self.animationframe = bpy.context.scene.frame_current
bpy.app.handlers.frame_change_post.append(self.animation)
self.update()
def animation(self, context, depsgraph=None):
if self.animationflag:
self._update()
self.animationflag = False
self.animationframe = bpy.context.scene.frame_current
def _update(self):
error = False
try:
upfct = self.update_bpy_data
if not self.depth:
upfct(self.obj, bpy_data=self.bpy_data, **self.options)
else:
if isinstance(self.bpy_data, bmesh.types.BMesh):
if isinstance(self.obj, NetCollection):
bmeshes = []
for net_ in self.obj: # underscore avoids shadowing net module
subdomain = nutils.bound_domain(
net_.domain, self.depth_bounding
)
for obj in net_[subdomain]:
bmeshes.append(upfct(obj, **self.options))
join(*bmeshes, free=True, bm=self.bpy_data)
else:
subdomain = nutils.bound_domain(
self.obj.domain, self.depth_bounding
)
join(
*[
upfct(obj, **self.options)
for obj in self.obj[subdomain]
],
free=True,
bm=self.bpy_data
)
else:
curve_coords = []
cyclic = []
if isinstance(self.obj, NetCollection):
for n in self.obj:
subdomain = nutils.bound_domain(
n.domain, self.depth_bounding
)
for c in n[subdomain]:
if isinstance(c, NetCollection):
for d in c:
sbdomain = nutils.bound_domain(
d.domain, self.options["bounding"]
)
curve_coords.append(list(d[sbdomain]))
cyclic.append(sbdomain.periodic)
else:
sbdomain = nutils.bound_domain(
c.domain, self.options["bounding"]
)
curve_coords.append(list(c[sbdomain]))
cyclic.append(sbdomain.periodic)
else:
subdomain = nutils.bound_domain(
self.obj.domain, self.depth_bounding
)
for c in self.obj[subdomain]:
if isinstance(c, NetCollection):
for d in c:
sbdomain = nutils.bound_domain(
d.domain, self.options["bounding"]
)
curve_coords.append(list(d[sbdomain]))
cyclic.append(sbdomain.periodic)
else:
sbdomain = nutils.bound_domain(
c.domain, self.options["bounding"]
)
curve_coords.append(list(c[sbdomain]))
cyclic.append(sbdomain.periodic)
create_curve(curve_coords, cyclic=cyclic, curve=self.bpy_data)
if isinstance(self.bpy_data, bmesh.types.BMesh):
for trafo in self.bmesh_trafos:
trafo(self.bpy_data)
self.bpy_data.to_mesh(self.mesh)
if self.free:
self.bpy_data.free()
for trafo in self.mesh_trafos:
trafo(self.mesh)
for trafo in self.obj_trafos:
trafo(self.object)
except Exception as e:
temp = conversion_type(self.obj)
if temp == self.conv_type:
raise e
else:
error = True
if error:
raise TypeError(
"Object type has changed. Object was "
+ readable_conversion_type(self.conv_type)
+ " and now is "
+ readable_conversion_type(temp)
+ "."
)
def update(self):
frame = bpy.context.scene.frame_current
if self.animationframe == frame:
self._update()
else:
self.animationflag = True
def conversion_type(obj):
"""Returns the conversion type of the given object.
Parameters
----------
obj : Object
Returns
-------
conversion_type : int
"""
conv_type = 0b0
if isinstance(obj, NetCollection):
conv_type |= BLD_CNET
obj = obj._nets[0]
if isinstance(obj, Surface):
conv_type |= BLD_CONV | BLD_SURF
elif isinstance(obj, Net):
if isinstance(obj, SmoothNet):
return conv_type
elif isinstance(obj, DiscreteCurve):
conv_type |= BLD_DCUR
elif isinstance(obj, DiscreteNet):
conv_type |= BLD_DNET
elif isinstance(obj, PointNet):
conv_type |= BLD_PNET
elif isinstance(obj, EmptyNet):
conv_type |= BLD_ENET
if not isinstance(obj, EmptyNet):
# get a single point in the domain
bounded_domain = nutils.bound_domain(obj.domain, 1)
sample = [bounded_domain[i][0] for i in range(obj.dimension)]
temp = obj(*sample)
# This is better than before, but still not perfect. There are nets
# like some from confocal_quadrics or subspaces with homogeneous
# parametrization for which some values (0,...,0 in both cases)
# are invalid inputs. Maybe domains should have a way of handling
# this.
# Perhaps something like this? But other parts of the library have
# a similar problem, for example DiscreteNet.__getitem__
# bounded_domain = nutils.bound_domain(obj.domain, 2)
# for sample in bounded_domain.traverser:
# try:
# temp = obj(*sample)
# except Exception as e:
# error = e
# # executes when no exception occured
# else:
# break
# # executes when for loop ran without encountering break
# else:
# raise Exception(
# f"Sampling of {obj} object to determine its ambient "
# f"dimension failed with (last) error '{error}'."
# )
else:
temp = obj()
# After making them subscriptable, NetCollections with three elements
# have shape (3,), so we have to exclude them here.
if not isinstance(temp, NetCollection) and np.shape(temp) == (3,):
conv_type |= BLD_CONV
elif conv_type & BLD_PNET:
return 0
else:
temp = conversion_type(temp)
if temp & BLD_DEPTH == 0:
conv_type |= (BLD_CONV & temp) | ((temp >> 1) << 7)
return conv_type
def readable_conversion_type(conv_type):
"""Converts the given conversion type into a human-readable string.
Parameters
----------
conv_type : int
Conversion type to create a string for.
Returns
-------
string
human-readable string for the conversion type
"""
out = ""
if conv_type & BLD_SURF:
out += "Halfedge Surface"
elif conv_type & BLD_ENET:
out += "EmptyNet"
elif conv_type & BLD_PNET:
out += "PointNet"
elif conv_type & BLD_DCUR:
out += "DiscreteCurve"
elif conv_type & BLD_DNET:
out += "DiscreteNet"
if conv_type & BLD_CNET:
out = "Collection of " + out + "s"
layer = ""
if conv_type & BLD_SURF_2:
layer += " Halfedge Surfaces"
elif conv_type & BLD_ENET_2:
layer += " EmptyNets"
elif conv_type & BLD_PNET_2:
layer += " PointNets"
elif conv_type & BLD_DCUR_2:
layer += " DiscreteCurves"
elif conv_type & BLD_DNET_2:
layer += " DiscreteNets"
if conv_type & BLD_CNET_2:
layer = " Collections of" + layer
if layer != "":
out += " of" + layer
if conv_type & BLD_CONV:
return "Convertible " + out
else:
return "Not convertible " + out
def is_convertible(obj):
"""Returns True, if the given object is convertible into a
Blender object.
Parameters
----------
obj : Object
Returns
-------
bool : True, when object is convertible, else False
"""
return BLD_CONV & conversion_type(obj)
[docs]def to_blender_object_helper(
obj,
name=None,
location=None,
hide_viewport=None,
hide_render=None,
scale=None,
shade_smooth=False,
bounding_box=None,
accept_all=False,
material=None,
collection=None,
**kwargs
):
"""Fast conversion function for ddg objects to be converted to blender.
It combines the following two utilities:
* converting Subspaces and Quadrics to SmoothNets, SmoothNets to
DiscreteNets and DiscreteNets and half-edge objects to blender objects
* simplifies the input of commonly used arguments for the Blender object
creation.
Notes for the conversion:
* each conversion that uses a DiscreteNet at some point requires a sampling
keyword, see Options for SmoothNet, respectively
ddg.nets.conversion.sample_smooth_domain.
* each ddg object may have specific conversion arguments, see below. Note
that e.g. for a Quadric one can use specific arguments for the conversion
from a Quadric to a SmoothNet, from a SmoothNet to a DiscreteNet and from
a DiscreteNet to a Blender object.
* conversion from 1d Subspaces or Conics use options for DiscreteCurves
Notes for the commonly used arguments:
* All the keyword arguments of this function will be converted to a form
that is readable for
:py:func:`~ddg.conversion.blender.core.to_blender_object`.
* in \\*\\*kwargs further keyword arguments can be given in a form required
by :py:func:`~ddg.conversion.blender.core.to_blender_object`.
Parameters
----------
obj : ddg object
Any object of the ddg library except some types of nets with depth (see
warning).
name : str (default=None)
Name of the resulting Blender object
location : iterable of shape (3,) (default=(0,0,0))
Location of the resulting Blender object.
hide_viewport : bool (default=False)
Boolean to set to blender_object.hide_viewport.
hide_render : bool (default=False)
Boolean to set to blender_object.hide_render.
scale: iterable of shape (3,) (default=(1,1,1))
Scaling of the resulting Blender object set as blender_object.scale.
shade_smooth : bool (default=False)
Boolean to decide whether to apply
:py:func:`ddg.visualization.blender.mesh.shade_smooth` to the mesh of the
resulting Blender object.
bounding_box : iterable of shape (3,) (default=(1,1,1))
Boolean to decide weather to apply
:py:func:`ddg.visualization.blender.bmesh.cut_bounding_box` (respectively
:py:func:`ddg.datastructures.nets.utils.cut_bounding_box` if
blender_object.data will be of type bpy.Curves).
accept_all : bool (optional default=False)
When True all options are accepted, but no warning will be given
when the conversion does not support one of them.
material : string or bpy.types.Material (optional, default=None)
Material of the created Blender object
collection : bpy.types.Collection (default=None)
Collection to link the blender object to. If set to None the object
will be linked to bpy.context.scene.collection.
**kwargs
Keyword arguments to use in the conversion. Available options depend on
the type of the given object (see below). All other keyword arguments
(that are not type specific) will be handed over to
:py:func:`~ddg.conversion.blender.core.to_blender_object`. For example
this can be further (lists of) transformations of the mesh/bmesh or
Blender object or attributes of the resulting objects.
.. rubric:: Options for Subspaces:
convex : bool (default=True)
Whether to use convex parametrization (see
:py:func:`~.subspace_to_smooth_net`).
affine : bool (default=True)
Whether the resulting smooth net should return homogeneous or
affine coordinates (see :py:func:`~.subspace_to_smooth_net`).
domain : list or SmoothDomain (default=None)
Optionally a domain to assign to the SmoothNet (see
:py:func:`ddg.datastructures.nets.utils.create_subdomain`,
:py:func:`~ddg.conversion.nets.geometry.core.to_smooth_net`).
As well as all options for SmoothNet and DiscreteNet.
.. rubric:: Options for Quadrics:
affine : bool (default=False)
Whether the resulting SmoothNet should return affine or
homogeneous coordinates (see
:py:func:`~.quadric_to_smooth_net`).
domain : list or SmoothDomain (default=None)
Optionally a domain to assign to the SmoothNet (see
:py:func:`ddg.datastructures.nets.utils.create_subdomain`,
:py:func:`~ddg.conversion.nets.geometry.core.to_smooth_net`).
As well as all options for SmoothNet and DiscreteNet.
.. rubric:: Options for Spheres:
No options.
.. rubric:: Options for SmoothNet:
sampling : list, int or float
See
:py:func:`ddg.datastructures.nets.conversion.sample_smooth_domain`.
anchor : list or None
Anchor point for sampling process.
atol : list, int or float
Tolerance(s) for sampling process.
.. rubric:: Options for DiscreteNet:
bounding : int (default=10)
Used to bound unbounded domains of nets.
only_wire : bool (default=False)
When True, only the wireframe of the net will be created.
.. rubric:: Options for DiscreteCurve:
bounding : int (default=10)
Used to bound unbounded domains of nets.
curve_type : string (default='POLY')
Blender curve type. See the Blender docs for all available types.
curve_properties : dictionary (default={'bevel_depth': 0.015})
Dictionary containing Blender curve properties.
See the Blender docs for all available properties.
.. rubric:: Options for PointNet:
sphere_radius : float (default=0)
Radius of the sphere representing a point.
sphere_subdivision : int (default=3)
How many subdivisions will be applied to the sphere.
.. rubric:: Options for Half-Edge:
co_attr : str (default='co')
Name of the vertex attribute that stores the coordinates.
See Also
--------
to_blender_object
Returns
-------
Blender object
Blender object associated with the ddg object.
Warnings
--------
This function can only handle DiscreteNets and DiscreteCurves as nets with
depth. Not supported for SmoothNets and SmoothCurves. Also in the case of
DiscreteNets and DiscreteCurves the net must consist of either
* EmptyNets
* PointNets
* DiscreteNets
* Half-edge Objects
* Collections of either
"""
def move_to_other_dict(first_dict, second_dict, key, val=None):
"""
Helper to remove a key from first_dict and write it to second_dict. If
it's not in fist_dict and a val is given this will be written to
second_dict.
"""
if key in first_dict:
second_dict[key] = first_dict[key]
del first_dict[key]
else:
# This is not ideal. What if we want the default to be None?
if val is not None:
second_dict[key] = val
# find type of obj or type of obj in NetCollection
obj_by_type = obj._nets[0] if isinstance(obj, ddg.NetCollection) else obj
# conversion of all non-directly convertible objects to DiscreteNets
# namely Subspaces, Quadrics and Spheres
if not (
is_convertible(obj_by_type)
or isinstance(obj_by_type, ddg.datastructures.nets.net.Net)
):
to_smooth_net_kwargs = {}
if isinstance(obj_by_type, Subspace):
move_to_other_dict(kwargs, to_smooth_net_kwargs, "convex", True)
move_to_other_dict(kwargs, to_smooth_net_kwargs, "affine", True)
if isinstance(obj_by_type, Quadric):
move_to_other_dict(kwargs, to_smooth_net_kwargs, "affine", True)
move_to_other_dict(kwargs, to_smooth_net_kwargs, "domain")
obj = ddg.to_smooth_net(obj, **to_smooth_net_kwargs)
# find new type of obj or type of obj in NetCollection
obj_by_type = obj._nets[0] if isinstance(obj, ddg.NetCollection) else obj
# conversion of all SmoothNets or Subspaces, Quadrics and Spheres that
# previously have been converted to a SmoothNet
if isinstance(obj_by_type, ddg.SmoothNet):
if isinstance(obj, EmptyNet) and not isinstance(obj, PointNet):
pass
else:
sample_smooth_net_kwargs = {}
move_to_other_dict(kwargs, sample_smooth_net_kwargs, "sampling")
move_to_other_dict(kwargs, sample_smooth_net_kwargs, "anchor")
move_to_other_dict(kwargs, sample_smooth_net_kwargs, "atol")
if not sample_smooth_net_kwargs.get("sampling", None):
raise ValueError(
"The given object is (or has been converted to) a "
"SmoothNet. The further conversion to a DiscreteNet "
"requires a sampling that was not given."
)
else:
obj = ddg.sample_smooth_net(obj, **sample_smooth_net_kwargs)
# treatment of objects resulting in bpy.Curves
conv_type = conversion_type(obj)
if conv_type & BLD_DCUR_2 or (conv_type & BLD_DCUR and not conv_type & BLD_DEPTH):
if bounding_box is not None:
nutils.bound_domain(obj, bounding=kwargs.pop("bounding", 100))
obj = nutils.cut_bounding_box(obj, bounding_box)
bounding_box = None
# Reformatting keyword arguments
bmesh_transformations = kwargs.get("bmesh_transformations", [])
if type(bmesh_transformations) is not list:
bmesh_transformations = [bmesh_transformations]
mesh_transformations = kwargs.get("mesh_transformations", [])
if type(mesh_transformations) is not list:
mesh_transformations = [mesh_transformations]
attributes = kwargs.get("attributes", {})
if bounding_box is not None:
bmesh_transformations.append(
lambda bmesh: ddg.visualization.blender.bmesh.cut_bounding_box(
bmesh, bounding_box, np.zeros(3)
)
)
if shade_smooth:
mesh_transformations.append(ddg.visualization.blender.mesh.shade_smooth)
attributes.update(
{
k: v
for k, v in (
("name", name),
("hide_viewport", hide_viewport),
("scale", scale),
("hide_render", hide_render),
("location", location),
)
if v is not None
}
)
kwargs["bmesh_transformations"] = bmesh_transformations
kwargs["mesh_transformations"] = mesh_transformations
kwargs["attributes"] = attributes
return to_blender_object(
obj, accept_all=accept_all, material=material, collection=collection, **kwargs
)
[docs]def to_blender_object(
obj,
accept_all=False,
depth_bounding=10,
attributes={},
material=None,
collection=None,
mesh_transformations=[],
bmesh_transformations=[],
obj_transformations=[],
link=True,
**options
):
"""Create and link a Blender object from a given DDG object.
Parameters
----------
obj : ddg.datastructure
DDG datastructure to convert into a Blender object.
accept_all : bool (optional default=False)
When True all options are accepted, but no warning will be given
when the conversion does not support one of them.
depth_bounding : int (optional, default=10)
Bounding used for the highest layer of Nets with depth
attributes : dict (optional, default={})
Attributes that will be assigned to the resulting Blender object
such as name, parent, matrix_world, location, hide_viewport etc.
in a dictionary as {'attribute': value}
material : string or bpy.types.Material (optional, default=None)
Material of the created Blender object
collection : bpy.types.Collection (optional, default=None)
Collection to link the blender object to.
If set to None the object will be linked to bpy.context.scene.collection.
mesh_transformations : function or list of functions (optional, default=[])
Functions which should be applied to the mesh of the object.
The transformation functions need to be defined such that
they only take one (required) parameter, namely the mesh.
bmesh_transformations : function or list of functions (optional, default=[])
Functions which should be applied to the bmesh of the object.
The transformation functions need to be defined such that
they only take one (required) parameter, namely the bmesh.
obj_transformations : function or list of functions (optional, default=[])
Functions which should be applied to the object.
The transformation functions need to be defined such that
they only take one (required) parameter, namely the object.
link: bool (optional, default=True)
If set to True, the object will be linked to the given collection.
If set to False, the object will be created but not linked to any collection.
**options
Options to use in the conversion. Available options depend on the type
of the given object.
.. rubric:: Options for DiscreteNet:
bounding : int (default=10)
Used to bound unbounded domains of nets.
only_wire : bool (default=False)
When True, only the wireframe of the net will be created.
.. rubric:: Options for DiscreteCurve:
bounding : int (default=10)
Used to bound unbounded domains of nets.
curve_type : string (default='POLY')
Blender curve type. See the Blender docs for all available types.
curve_properties : dictionary (default={'bevel_depth': 0.015})
Dictionary containing Blender curve properties.
See the Blender docs for all available properties.
.. rubric:: options for PointNet:
sphere_radius : float (default=0)
Radius of the sphere representing a point.
sphere_subdivision : int (default=3)
How many subdivisions will be applied to the sphere.
.. rubric:: Options for HalfEdge surfaces:
co_attr : str (default='co')
The name of the vertex attribute that contains the coordinates.
Returns
-------
Blender object
Blender object associated with the DDG object.
Raises
------
TypeError
if object is not convertible.
"""
conv_type = conversion_type(obj)
if not conv_type & BLD_CONV:
raise TypeError("Object " + str(obj) + " is not convertible.")
if conv_type & BLD_DEPTH:
if conv_type & BLD_SURF_2:
options = _hes_options(accept_all, **options)
elif conv_type & BLD_DCUR_2:
options = _curve_options(accept_all, **options)
elif conv_type & BLD_DNET_2:
options = _net_options(accept_all, **options)
elif conv_type & BLD_ENET_2:
options = _empty_options(accept_all, **options)
elif conv_type & BLD_PNET_2:
options = _point_options(accept_all, **options)
else:
if conv_type & BLD_SURF:
options = _hes_options(accept_all, **options)
elif conv_type & BLD_DCUR:
options = _curve_options(accept_all, **options)
elif conv_type & BLD_DNET:
options = _net_options(accept_all, **options)
elif conv_type & BLD_ENET:
options = _empty_options(accept_all, **options)
elif conv_type & BLD_PNET:
options = _point_options(accept_all, **options)
bobj = BlenderBridge(
obj,
conv_type,
attributes=attributes,
depth_bounding=depth_bounding,
options=options,
material=material,
collection=collection,
mesh_transformations=mesh_transformations,
bmesh_transformations=bmesh_transformations,
obj_transformations=obj_transformations,
link=link,
)
return bobj.object
def _curve_options(accept_all=False, **options):
defaults = {
"bounding": 10,
"curve_type": None,
"curve_properties": {"bevel_depth": 0.015},
}
valid_options = set(defaults)
given_options = set(options)
if not given_options.issubset(valid_options) and not accept_all:
unknown = str(given_options.difference(valid_options))
unknown = unknown.strip("{").strip("}")
raise TypeError("Received unkown option(s) for object creation: " + unknown)
for i in valid_options.intersection(given_options):
defaults[i] = options[i]
return defaults
def _empty_options(accept_all=False, **options):
given = set(options)
if not given.issubset(set()) and not accept_all:
unknown = str(given)
unknown = unknown.strip("{").strip("}")
raise TypeError("Received unkown option(s) for object creation: " + unknown)
return {}
def _net_options(accept_all=False, **options):
defaults = {"bounding": 10, "only_wire": False}
valid_options = set(defaults)
given_options = set(options)
if not given_options.issubset(valid_options) and not accept_all:
unknown = str(given_options.difference(valid_options))
unknown = unknown.strip("{").strip("}")
raise TypeError("Received unkown option(s) for object creation: " + unknown)
for i in valid_options.intersection(given_options):
defaults[i] = options[i]
return defaults
def _point_options(accept_all=False, **options):
defaults = {"sphere_radius": 0, "sphere_subdivision": 3}
valid_options = set(defaults)
given_options = set(options)
if not given_options.issubset(valid_options) and not accept_all:
unknown = str(given_options.difference(valid_options))
unknown = unknown.strip("{").strip("}")
raise TypeError("Received unkown option(s) for object creation: " + unknown)
for i in valid_options.intersection(given_options):
defaults[i] = options[i]
return defaults
def _hes_options(accept_all=False, **options):
defaults = {"co_attr": "co"}
valid_options = set(defaults)
given_options = set(options)
if not given_options.issubset(valid_options) and not accept_all:
unknown = str(given_options.difference(valid_options))
unknown = unknown.strip("{").strip("}")
raise TypeError("Received unkown option(s) for object creation: " + unknown)
for i in valid_options.intersection(given_options):
defaults[i] = options[i]
return defaults