import ddg
##############################
# utilities acting on vertices
##############################
[docs]def in_edges(v):
"""
Get the incoming edges of a vertex,
Parameters
----------
v : vertex
Yields
------
edge
All edges pointing to the vertex `v` (i.e., `edge.head` = `v`).
"""
if v.edge is None:
return
first_edge = v.edge
yield first_edge
next_edge = first_edge.nex.opp
while next_edge != first_edge:
yield next_edge
next_edge = next_edge.nex.opp
[docs]def out_edges(v):
"""
Get the outgoing edges of a vertex.
Parameters
----------
v : vertex
Yields
------
edge
All edges pointing away from the vertex `v` (i.e., `edge.tail` == `v`).
"""
if v.edge is None:
return
first_edge = v.edge.opp
yield first_edge
next_edge = first_edge.opp.nex
while next_edge != first_edge:
yield next_edge
next_edge = next_edge.opp.nex
###########################
# utilities acting on edges
###########################
[docs]def edge_loop(e):
"""
Get all consecutive edges of a given edge, forming a loop.
Parameters
----------
e : edge
Starting edge of the edge loop.
Yields
------
edge
The consecutive edges in a loop starting with argument edge `e`.
"""
first_edge = e
yield first_edge
next_edge = first_edge.nex
while next_edge is not first_edge:
yield next_edge
next_edge = next_edge.nex
[docs]def count_edges_in_loop(e):
"""
Counts the number of consecutive half-edges in a loop along a given one.
Parameters
----------
e : edge
The edge whose number of consecutive half-edges in a loop is counted.
Returns
-------
int
The number of half-edges belonging to the edge loop along `e`.
"""
i = 0
for _ in edge_loop(e):
i += 1
return i
[docs]def opposite_edge_in_face(e):
n = len(list(edge_loop_from_face(e.face)))
opp_edge = e
if n % 2 != 0:
raise ValueError("non even face")
else:
for i in range(n // 2):
opp_edge = opp_edge.nex
return opp_edge.opp
[docs]def opposite_edge_at_head(e):
n = len(list(in_edges(e.head)))
opp_edge = e
if n % 2 != 0:
raise ValueError("non even vertex degree")
else:
for i in range(n // 2):
opp_edge = opp_edge.nex.opp
return opp_edge.opp
[docs]def length_from_co(e, co_attr="co"):
"""
Calculates the euclidean length of an edge, provided its vertices have coordinates.
Parameters
----------
e : edge
The edge whose length will be calculated.
co_attr : string (default='co')
Name of the vertex attribute storing the coordinates.
Returns
-------
f : float
The length of the edge 'e'.
Raises
------
AttributeError
If head or tail of the edge has no coordinate attribute 'co'.
"""
if not hasattr(e.head, co_attr):
raise AttributeError(f"The vertex {e.head} has no coordinate attribute.")
if not hasattr(e.tail, co_attr):
raise AttributeError(f"The vertex {e.tail} has no coordinate attribute.")
a = e.head.co
b = e.tail.co
return ddg.math.euclidean.distance(a, b)
###########################
# utilities acting on faces
###########################
[docs]def face_vertices(f):
"""
Get the boundary vertices of a face in cyclic order.
Parameters
----------
f : face
Yields
------
vertex
The boundary vertices of the face `f` in cyclic order.
"""
first_edge = f.edge
yield first_edge.head
next_edge = first_edge.nex
while next_edge != first_edge:
yield next_edge.head
next_edge = next_edge.nex
[docs]def edge_loop_from_face(f):
"""
Get the edges of a face in cyclic order.
Parameters
----------
f : face
Yields
------
edge
The edges of the face `f` in cyclic order.
"""
return edge_loop(f.edge)
[docs]def number_of_edges(f):
"""
Counts the number of half-edges associated with the given face.
Parameters
----------
f : face
The face whose number of half-edges is counted.
Returns
-------
int
The number of half-edges belonging to face f.
"""
return count_edges_in_loop(f.edge)
###########################
# utilities acting on cells
###########################
[docs]def complement(cells, type, surface):
"""
Parameters
----------
cells: iterable
Iterable of cells of a surface to find the complement from.
type: str
Type of the cells, "verts", "edges" or "faces"
surface: ddg.halfedge.Surface
Surface that the cells belong to.
Returns
-------
set
set of complement cells
"""
res = set()
for cell in getattr(surface, type):
if cell not in cells:
res.add(cell)
return res
##############################
# utilities acting on surfaces
##############################
[docs]def some_vertex(s):
"""
Get one vertex of the given surface.
Parameters
----------
s : ddg.halfedge.Surface
Returns
-------
v : vertex
One of the vertices of the given surface.
"""
for v in s.verts:
return v
[docs]def some_edge(s):
"""
Get one edge of the given surface.
Parameters
----------
s : ddg.halfedge.Surface
Returns
-------
e : edge
One of the edges of the given surface.
"""
for e in s.edges:
return e
[docs]def some_face(s):
"""
Get one face of the given surface.
Parameters
----------
s : ddg.halfedge.Surface
Returns
-------
f : face
One of the faces of the given surface.
"""
for f in s.faces:
return f
[docs]def is_triangulation(surf):
"""
Checks if a surface is a triangulation.
Parameters
----------
surf : ddg.halfedge.Surface
The surface that is checked for being a triangulation.
Returns
-------
bool
"""
surf.validate()
for f in surf.faces:
if number_of_edges(f) != 3:
return False
return True
[docs]def is_connected(s):
"""Whether a half-edge object is connected.
The empty set counts as connected.
Parameters
----------
s : ddg.halfedge.Surface
Returns
-------
bool
"""
# The empty set is connected
if not s.verts:
return True
# dfs
r = some_vertex(s)
visited_vertices = [r]
Q = [r]
while Q:
v = Q[-1]
for e in out_edges(v):
w = e.head
if w not in visited_vertices:
visited_vertices.append(w)
Q.append(w)
break
# else triggers if no break was encountered
else:
Q.pop()
return len(visited_vertices) == len(s.verts)
[docs]def single_edges(surf):
"""
Yields a generator that iterates through half of the half-edges of a surface
Parameters
----------
surf: surface
The half-edge data through which must be iterated
Yields
------
edge:
half of the half-edges
"""
visited_edges = set()
for edge in surf.edges:
if edge.opp in visited_edges:
continue
visited_edges.add(edge)
yield edge
#############################
# boundary and interior cells
#############################
[docs]def boundary_vertices(surf):
"""
Get all boundary vertices of a surface.
Parameters
----------
surf : surface
Yields
------
vertex
All boundary vertices of a surface.
"""
for v in surf.verts:
if is_boundary_vertex(v):
yield v
[docs]def boundary_edges(surf):
"""
Get a list of all boundary half-edges of a surface.
Parameters
----------
surf : ddg.halfedge.Surface
Yields
------
edge
All boundary half-edges of a surface.
"""
for e in surf.edges:
if is_boundary_edge(e):
yield e
[docs]def interior_vertices(surf):
"""
Get all interior vertices of a surface.
Parameters
----------
surf : ddg.halfedge.Surface
Yields
------
vertex
All interior vertices of a surface.
"""
for v in surf.verts:
if not is_boundary_vertex(v):
yield v
[docs]def interior_edges(surf):
"""
Get all interior half-edges of a surface.
Parameters
----------
surf : ddg.halfedge.Surface
Yields
------
edge
All interior half-edges of a surface.
"""
for e in surf.edges:
if not is_boundary_edge(e):
yield e
[docs]def is_boundary_vertex(v):
"""
Checks whether a vertex lies on the boundary of the surface..
Parameters
----------
v : vertex
The vertex that is checked for being a boundary vertex.
Returns
-------
bool
True if `v` is a boundary vertex, False if not.
"""
if v.edge is None:
return True
for e in in_edges(v):
if not e.face:
return True
return False
[docs]def is_boundary_edge(e):
"""
Checks whether a half-edge lies on the boundary of the surface.
Parameters
----------
e : edge
The edge that is checked for being a boundary edge.
Returns
-------
bool
True if `e` is a boundary edge, False if not.
"""
return not e.face