import ddg
from ddg.halfedge import Surface
from ddg.indexedfaceset import IndexedFaceSet
[docs]def discrete_net_to_halfedge(net, co_attr="co"):
"""Convert discrete net to halfedge.
The vertices of the halfedge object will have an attribute co_attr
containing the values of the net. The order/index of vertices in the
returned surface will be the same as in net.traverser.
Parameters
----------
net : DiscreteNet, EmptyNet, PointNet or NetCollection containing these
co_attr : str (default='co')
Name of coordinate attribute
Returns
-------
Surface
"""
def single_net_to_halfedge(net):
# Special cases, in particular things that don't have traversers
# These aren't even technically DiscreteNets.
if isinstance(net, ddg.nets.EmptyNet):
return Surface()
if isinstance(net, ddg.nets.PointNet):
s = Surface()
v = s.verts()
s.verts.add_attribute(co_attr)
setattr(v, co_attr, net())
return s
s = discrete_domain_to_halfedge(net.domain, co_attr=co_attr)
# This requires that the order of vertices in s is the same as the
# traverser order
for v, c in zip(s.verts, net.domain.traverser):
setattr(v, co_attr, net[c])
return s
if isinstance(net, ddg.nets.NetCollection):
output_list = [single_net_to_halfedge(n) for n in net]
return ddg.halfedge.union(*output_list, verts_attr_list=[co_attr])
return single_net_to_halfedge(net)
[docs]def discrete_domain_to_halfedge(domain, co_attr="co"):
"""Convert a discrete domain to a halfedge object.
The vertices of the halfedge object will have an attribute co_attr
containing the values from domain.traverser. The order/index of vertices in
the returned surface will be the same as in domain.traverser.
Parameters
----------
domain : DiscreteDomain or EmptyDomain
co_attr : str (default='co')
Name of coordinate attribute
Returns
-------
Surface
"""
if isinstance(domain, ddg.nets.DiscreteRectangularDomain) and not domain.bounded:
raise ValueError("Can not convert unbounded domain to halfedge.")
# Determine the dimension of the domain. We can't just use domain.dimension
# because it is more like ambient_dimension, for example [[0, 5], [0,0]]
# has dimension 2, even though it is topologically an interval.
if isinstance(domain, ddg.nets.EmptyDomain): # Does not have a traverser
return Surface()
else:
num_pts = len(list(domain.traverser))
if num_pts == 0:
dim = -1
elif num_pts == 1:
dim = 0
elif len(domain.face_data) == 0 and len(domain.edge_data) != 0:
dim = 1
elif isinstance(
domain, (ddg.nets.DiscreteDiagonalDomain, ddg.nets.DiscreteTriangularDomain)
):
dim = 2
else:
dim = len([I for I in domain.intervals if I[1] - I[0] > 0])
if dim == -1:
s = Surface()
elif dim == 0:
s = Surface()
s.verts()
elif dim == 1:
s = Surface()
# We need to remember the first and last vertex in case we need to
# connect them because the domain is periodic
v_first = s.verts()
v_last = s.verts()
s.add_edge(v_first, v_last)
for _ in range(num_pts - 2):
v_next = s.verts()
s.add_edge(v_last, v_next)
v_last = v_next
if domain.periodic:
s.add_edge(v_first, v_last)
elif dim == 2:
s = IndexedFaceSet(domain.face_data)
s = ddg.indexedfaceset.indexed_face_set_to_surface(
s, vertex_index_attribute=None, face_index_attribute=None
)
else:
raise ValueError(
"Can only convert empty, points, intervals and 2D domains. This "
f"domain was determined to have dimension {dim}."
)
# This requires that the order of vertices in s is the same as the
# traverser order
s.verts.add_attribute(co_attr)
for v, c in zip(s.verts, domain.traverser):
setattr(v, co_attr, c)
return s