Module sverchok.utils.geom_2d.dissolve_mesh
Expand source code
# This file is part of project Sverchok. It's copyrighted by the contributors
# recorded in the version control history of the file, available from
# its original location https://github.com/nortikin/sverchok/commit/master
#
# SPDX-License-Identifier: GPL3
# License-Filename: LICENSE
from .dcel import HalfEdge as HalfEdge_template, DCELMesh as DCELMesh_template
from .lin_alg import is_ccw_polygon
from .dcel_debugger import Debugger
def dissolve_faces(sv_verts, sv_faces, face_mask, is_mask=False, is_index=False):
"""
Combine adjacent faces with mask True.
It resist to combine faces in several corner cases like creating hanging face inside mesh.
:param sv_verts: list of SV vertices
:param sv_faces: list of SV faces
:param face_mask: List of True of False per input face
:param is_mask: add to output new face_mask
:param is_index: add to output index of old face per nes faces
:return: list of SV vertices, list of SV face, face_mask (optionally), index_mask (optionally)
"""
mesh = DCELMesh()
mesh.from_sv_faces(sv_verts, sv_faces, face_flag=['dissolve' if mark else None for mark in face_mask],
face_data={'index': list(range(len(sv_faces)))})
dissolve_faces_add(mesh, 'dissolve')
if is_mask and is_index:
return list(mesh.to_sv_mesh(edges=False)) + [[int('dissolve' in face.flags) for face in mesh.faces]] + \
[[face.sv_data['index'] for face in mesh.faces]]
elif is_mask:
return list(mesh.to_sv_mesh(edges=False)) + [[int('dissolve' in face.flags) for face in mesh.faces]]
elif is_index:
return list(mesh.to_sv_mesh(edges=False)) + [[face.sv_data['index'] for face in mesh.faces]]
else:
return mesh.to_sv_mesh(edges=False)
# #############################################################################
# ###################________dissolve mesh functions______#####################
# #############################################################################
# Implementation of this algorithm is not even finished. I will leave it in this condition for a while.
# It intend to be faster but solving of corner cases when the algorithm produce holes is not obvious.
class HalfEdge(HalfEdge_template):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.new_next = None
self.new_last = None
@property
def new_loop_hedges(self):
# returns hedges bounding face
if not self.mesh:
raise AttributeError("This method doesn't work with hedges({}) without link to a mesh."
"Besides, mesh object should have proper number of half edges "
"in hedges list".format(self))
yield self
next_edge = self.next
counter = 0
while id(next_edge) != id(self):
yield next_edge
try:
next_edge = next_edge.next
except AttributeError:
raise AttributeError(' Some of half edges has incomplete data (does not have link to next half edge)')
counter += 1
if counter > len(self.mesh.hedges):
raise RecursionError('Hedge - {} does not have a loop'.format(self))
class DCELMesh(DCELMesh_template):
HalfEdge=HalfEdge
def dissolve_faces_with_flag(mesh, flag):
# mark unused hedges and faces
x, y = 0, 1
boundary_hedges = []
un_used_hedges = set()
for face in mesh.faces:
if flag in face.flags:
for hedge in face.outer.loop_hedges:
if flag in hedge.twin.face.flags:
un_used_hedges.add(hedge)
else:
boundary_hedges.append(hedge)
# create new faces
used = set()
inner_faces = []
for hedge in boundary_hedges:
if hedge in used:
continue
last_hedge = hedge
for loop_hedge in walk_boundary_loop(hedge, flag):
used.add(loop_hedge)
last_hedge.new_last = loop_hedge
loop_hedge.new_next = last_hedge
last_hedge = loop_hedge
min_hedge = min([hedge for hedge in hedge.new_loop_hedges],
key=lambda he: (he.origin.co[x], he.origin.co[y]))
_is_ccw = is_ccw_polygon(most_lefts=[min_hedge.last.origin.co, min_hedge.origin.co,
min_hedge.next.origin.co], accuracy=mesh.accuracy)
if _is_ccw:
face = mesh.Face(mesh)
face.outer = min_hedge
face.sv_data = dict(min_hedge.face.sv_data)
for loop_hedge in hedge.new_loop_hedges:
loop_hedge.face = face
else:
face = mesh.Face(mesh)
face.inners.append(min_hedge)
for loop_hedge in hedge.new_loop_hedges:
loop_hedge.face = face
inner_faces.append(face)
# assign inner component to found faces
for inner_face in inner_faces:
for hedge in walk_left(inner_face.inners[0]):
if flag in hedge.face.flags:
continue
used.add(hedge)
face = mesh.Face(mesh)
new_faces.append(face)
face.outer = hedge
face.sv_data = dict(hedge.face.sv_data) # new face get all sv data related with first edge in loop
face.flags = set(hedge.face.flags)
hedge.face = face
for ccw_hedge in hedge.ccw_hedges:
if id(ccw_hedge) != id(hedge) and flag not in ccw_hedge.face.flags:
break
last_hedge = ccw_hedge.twin
hedge.last = last_hedge
last_hedge.next = hedge
used.add(last_hedge)
last_hedge.face = face
count = 0
current_hedge = last_hedge
while id(current_hedge) != id(hedge):
for ccw_hedge in current_hedge.ccw_hedges:
if id(ccw_hedge) != id(hedge) and flag not in ccw_hedge.face.flags:
break
last_hedge = ccw_hedge.twin
used.add(last_hedge)
last_hedge.face = face
current_hedge.last = last_hedge
last_hedge.next = current_hedge
current_hedge = last_hedge
count += 1
if count > len(mesh.hedges):
raise RecursionError("Dissolve face algorithm can't built a loop from hedge - {}".format(hedge))
# update faces
mesh.faces = [face for face in mesh.faces if flag not in face.flags]
mesh.faces.extend(new_faces)
# update hedges
mesh.hedges = [hedge for hedge in mesh.hedges if hedge not in un_used_hedges]
# todo how to rebuilt points list
def is_closer_left(slop1, slop2):
slop1 = slop1 if slop1 <= 2 else 2 - slop1 % 2 if slop1 != 4.0 else 0
slop2 = slop2 if slop2 <= 2 else 2 - slop2 % 2 if slop2 != 4.0 else 0
return True if slop1 < slop2 else False
def walk_left(hedge):
# return next hedge closest to -X direction
count = 0
start_hedge = hedge
while count <= len(hedge.mesh.hesges):
next_candidate = None
for ccw_hedge in start_hedge.twin.ccw_hedges:
if id(ccw_hedge) == id(start_hedge.twin):
# avoid going in back direction
continue
if not next_candidate:
# any candidate is okay
next_candidate = ccw_hedge
continue
if is_closer_left(ccw_hedge.slop, next_candidate.slop):
# next candidate should be closer to -X direction
next_candidate = ccw_hedge
else:
# according that half edges are sorted in ccw order, best candidate already has been found
break
if not next_candidate:
raise ValueError('Looks like the end of tail is reached, what to do next?')
yield next_candidate
start_hedge = next_candidate
count += 1
raise RecursionError('It looks like you forget to exit from left walk')
def walk_boundary_loop(hedge, bound_flag):
# in back direction
# yield hedge
for ccw_hedge in hedge.ccw_hedges:
if id(ccw_hedge) != id(hedge) and bound_flag not in ccw_hedge.face.flags:
break
current_hedge = ccw_hedge.twin
yield current_hedge
count = 0
while id(current_hedge) != id(hedge):
for ccw_hedge in current_hedge.ccw_hedges:
if id(ccw_hedge) != id(hedge) and bound_flag not in ccw_hedge.face.flags:
break
current_hedge = ccw_hedge.twin
yield current_hedge
count += 1
if count > len(hedge.mesh.hedges):
raise RecursionError("Dissolve face algorithm can't built a loop from hedge - {}".format(hedge))
def get_leftmost_hedge_in_point(hedge, flag):
# can return None if such hedge does not exist
# given hedge should be boundary half edge of dissolving faces
dissolved_hedge = None
for ccw_hedge in hedge.ccw_hedges:
if id(ccw_hedge) == id(hedge):
# avoid going in back direction
continue
if flag not in ccw_hedge.face.flags:
# that means that all dissolved half edges was looked up
break
if not dissolved_hedge:
# any candidate is okay
dissolved_hedge = ccw_hedge
continue
if is_closer_left(ccw_hedge.slop, dissolved_hedge.slop):
# next candidate should be closer to -X direction
dissolved_hedge = ccw_hedge
else:
# according that half edges are sorted in ccw order, best candidate already has been found
break
return dissolved_hedge
def get_leftmost_dissolved_hedge(left_loop_hedge, flag):
# left_loop_hedge - half edge with origin which is leftmost point of a loop
dis_hedge = get_leftmost_hedge_in_point(left_loop_hedge, flag)
if dis_hedge:
return dis_hedge
used = set()
next_two_candidates = [left_loop_hedge.new_next, left_loop_hedge.new_last]
while next_two_candidates:
low_hedge = next_two_candidates.pop()
used.add(low_hedge)
low_candidate = get_leftmost_hedge_in_point(low_hedge, flag)
up_hedge = next_two_candidates.pop()
used.add(up_hedge)
up_candidate = get_leftmost_hedge_in_point(up_hedge, flag)
if low_candidate and up_candidate:
return low_candidate if is_closer_left(low_candidate, up_candidate) else up_candidate
elif low_candidate:
return low_candidate
elif up_candidate:
return up_candidate
else:
next_up_hedge = up_hedge.new_next
next_low_hedge = low_hedge.new_last
if next_up_hedge not in used and next_low_hedge not in used:
next_two_candidates.extend([next_up_hedge, next_low_hedge])
elif next_low_hedge not in used:
low_candidate = get_leftmost_hedge_in_point(next_low_hedge, flag)
if low_candidate:
return low_candidate
else:
raise LookupError('Did not manage to find any dissolved half edges connected to given loop')
elif next_up_hedge not in used:
up_candidate = get_leftmost_hedge_in_point(next_up_hedge, flag)
if up_candidate:
return up_candidate
else:
raise LookupError('Did not manage to find any dissolved half edges connected to given loop')
else:
raise LookupError('Did not manage to find any dissolved half edges connected to given loop')
# #############################################################################
# ###################________dissolve mesh functions______#####################
# ###################____________second approach__________#####################
# #############################################################################
# This algorithm much more easier but should be slower.
def dissolve_faces_add(mesh, flag):
faces = []
used = set()
for face in mesh.faces:
if flag not in face.flags:
faces.append(face)
continue
if face in used:
continue
used.add(face)
faces.append(face)
checked_faces = set() # detect faces which already was added to the stack
candidate_faces = get_next_candidates(face, checked_faces, used, flag)
face_points = set([hedge.origin for hedge in face.outer.loop_hedges])
while candidate_faces:
face_candidate = candidate_faces.pop()
checked_faces.remove(face_candidate) # some face should be added several times
if is_addable(face, face_candidate, face_points):
used.add(face_candidate)
candidate_faces.extend(get_next_candidates(face_candidate, checked_faces, used, flag))
add_faces(face, face_candidate, face_points)
mesh.faces = faces
mesh.hedges = [hedge for face in mesh.faces for hedge in face.outer.loop_hedges]
def get_next_candidates(face, checked_faces, used, flag):
out = []
for hedge in face.outer.loop_hedges:
candidate = hedge.twin.face
if flag in candidate.flags and candidate not in checked_faces and candidate not in used:
checked_faces.add(candidate)
out.append(candidate)
return out
def is_addable(face, candidate, face_points):
# does not take in account possible common points yet
for loop_hedge in candidate.outer.loop_hedges:
if loop_hedge.origin in face_points:
if loop_hedge.twin.face != face and loop_hedge.last.twin.face != face:
return False
in_common_edge = False
number_common_lines = 0
start_from_common_hedge = False
for i, loop_hedge in enumerate(candidate.outer.loop_hedges):
if loop_hedge.twin.face == face:
if in_common_edge:
continue
else:
in_common_edge = True
number_common_lines += 1
if i == 0:
start_from_common_hedge = True
else:
in_common_edge = False
if in_common_edge and start_from_common_hedge:
# here case is detected when one common line take in account two times
number_common_lines -= 1
return True if number_common_lines == 1 else False
def add_faces(face, dis_face, face_points):
last_hedge_1, next_hedge_1 = None, None
last_hedge_2, next_hedge_2 = None, None
dis_hedges = []
for loop_hedge in dis_face.outer.loop_hedges:
if loop_hedge.twin.face == face and loop_hedge.last.twin.face != face:
last_hedge_1, next_hedge_1 = loop_hedge.last, loop_hedge.twin.next
elif loop_hedge.twin.face != face and loop_hedge.last.twin.face == face:
last_hedge_2, next_hedge_2 = loop_hedge.last.twin.last, loop_hedge
if loop_hedge.twin.face == face:
dis_hedges.append(loop_hedge)
else:
loop_hedge.face = face
face_points.add(loop_hedge.origin)
[setattr(hedge, 'mesh', None) for hedge in dis_hedges]
[setattr(hedge.twin, 'mesh', None) for hedge in dis_hedges]
dis_face.mesh = None
face.outer = last_hedge_1
last_hedge_1.next = next_hedge_1
next_hedge_1.last = last_hedge_1
last_hedge_2.next = next_hedge_2
next_hedge_2.last = last_hedge_2
Functions
def add_faces(face, dis_face, face_points)
-
Expand source code
def add_faces(face, dis_face, face_points): last_hedge_1, next_hedge_1 = None, None last_hedge_2, next_hedge_2 = None, None dis_hedges = [] for loop_hedge in dis_face.outer.loop_hedges: if loop_hedge.twin.face == face and loop_hedge.last.twin.face != face: last_hedge_1, next_hedge_1 = loop_hedge.last, loop_hedge.twin.next elif loop_hedge.twin.face != face and loop_hedge.last.twin.face == face: last_hedge_2, next_hedge_2 = loop_hedge.last.twin.last, loop_hedge if loop_hedge.twin.face == face: dis_hedges.append(loop_hedge) else: loop_hedge.face = face face_points.add(loop_hedge.origin) [setattr(hedge, 'mesh', None) for hedge in dis_hedges] [setattr(hedge.twin, 'mesh', None) for hedge in dis_hedges] dis_face.mesh = None face.outer = last_hedge_1 last_hedge_1.next = next_hedge_1 next_hedge_1.last = last_hedge_1 last_hedge_2.next = next_hedge_2 next_hedge_2.last = last_hedge_2
def dissolve_faces(sv_verts, sv_faces, face_mask, is_mask=False, is_index=False)
-
Combine adjacent faces with mask True. It resist to combine faces in several corner cases like creating hanging face inside mesh. :param sv_verts: list of SV vertices :param sv_faces: list of SV faces :param face_mask: List of True of False per input face :param is_mask: add to output new face_mask :param is_index: add to output index of old face per nes faces :return: list of SV vertices, list of SV face, face_mask (optionally), index_mask (optionally)
Expand source code
def dissolve_faces(sv_verts, sv_faces, face_mask, is_mask=False, is_index=False): """ Combine adjacent faces with mask True. It resist to combine faces in several corner cases like creating hanging face inside mesh. :param sv_verts: list of SV vertices :param sv_faces: list of SV faces :param face_mask: List of True of False per input face :param is_mask: add to output new face_mask :param is_index: add to output index of old face per nes faces :return: list of SV vertices, list of SV face, face_mask (optionally), index_mask (optionally) """ mesh = DCELMesh() mesh.from_sv_faces(sv_verts, sv_faces, face_flag=['dissolve' if mark else None for mark in face_mask], face_data={'index': list(range(len(sv_faces)))}) dissolve_faces_add(mesh, 'dissolve') if is_mask and is_index: return list(mesh.to_sv_mesh(edges=False)) + [[int('dissolve' in face.flags) for face in mesh.faces]] + \ [[face.sv_data['index'] for face in mesh.faces]] elif is_mask: return list(mesh.to_sv_mesh(edges=False)) + [[int('dissolve' in face.flags) for face in mesh.faces]] elif is_index: return list(mesh.to_sv_mesh(edges=False)) + [[face.sv_data['index'] for face in mesh.faces]] else: return mesh.to_sv_mesh(edges=False)
def dissolve_faces_add(mesh, flag)
-
Expand source code
def dissolve_faces_add(mesh, flag): faces = [] used = set() for face in mesh.faces: if flag not in face.flags: faces.append(face) continue if face in used: continue used.add(face) faces.append(face) checked_faces = set() # detect faces which already was added to the stack candidate_faces = get_next_candidates(face, checked_faces, used, flag) face_points = set([hedge.origin for hedge in face.outer.loop_hedges]) while candidate_faces: face_candidate = candidate_faces.pop() checked_faces.remove(face_candidate) # some face should be added several times if is_addable(face, face_candidate, face_points): used.add(face_candidate) candidate_faces.extend(get_next_candidates(face_candidate, checked_faces, used, flag)) add_faces(face, face_candidate, face_points) mesh.faces = faces mesh.hedges = [hedge for face in mesh.faces for hedge in face.outer.loop_hedges]
def dissolve_faces_with_flag(mesh, flag)
-
Expand source code
def dissolve_faces_with_flag(mesh, flag): # mark unused hedges and faces x, y = 0, 1 boundary_hedges = [] un_used_hedges = set() for face in mesh.faces: if flag in face.flags: for hedge in face.outer.loop_hedges: if flag in hedge.twin.face.flags: un_used_hedges.add(hedge) else: boundary_hedges.append(hedge) # create new faces used = set() inner_faces = [] for hedge in boundary_hedges: if hedge in used: continue last_hedge = hedge for loop_hedge in walk_boundary_loop(hedge, flag): used.add(loop_hedge) last_hedge.new_last = loop_hedge loop_hedge.new_next = last_hedge last_hedge = loop_hedge min_hedge = min([hedge for hedge in hedge.new_loop_hedges], key=lambda he: (he.origin.co[x], he.origin.co[y])) _is_ccw = is_ccw_polygon(most_lefts=[min_hedge.last.origin.co, min_hedge.origin.co, min_hedge.next.origin.co], accuracy=mesh.accuracy) if _is_ccw: face = mesh.Face(mesh) face.outer = min_hedge face.sv_data = dict(min_hedge.face.sv_data) for loop_hedge in hedge.new_loop_hedges: loop_hedge.face = face else: face = mesh.Face(mesh) face.inners.append(min_hedge) for loop_hedge in hedge.new_loop_hedges: loop_hedge.face = face inner_faces.append(face) # assign inner component to found faces for inner_face in inner_faces: for hedge in walk_left(inner_face.inners[0]): if flag in hedge.face.flags: continue used.add(hedge) face = mesh.Face(mesh) new_faces.append(face) face.outer = hedge face.sv_data = dict(hedge.face.sv_data) # new face get all sv data related with first edge in loop face.flags = set(hedge.face.flags) hedge.face = face for ccw_hedge in hedge.ccw_hedges: if id(ccw_hedge) != id(hedge) and flag not in ccw_hedge.face.flags: break last_hedge = ccw_hedge.twin hedge.last = last_hedge last_hedge.next = hedge used.add(last_hedge) last_hedge.face = face count = 0 current_hedge = last_hedge while id(current_hedge) != id(hedge): for ccw_hedge in current_hedge.ccw_hedges: if id(ccw_hedge) != id(hedge) and flag not in ccw_hedge.face.flags: break last_hedge = ccw_hedge.twin used.add(last_hedge) last_hedge.face = face current_hedge.last = last_hedge last_hedge.next = current_hedge current_hedge = last_hedge count += 1 if count > len(mesh.hedges): raise RecursionError("Dissolve face algorithm can't built a loop from hedge - {}".format(hedge)) # update faces mesh.faces = [face for face in mesh.faces if flag not in face.flags] mesh.faces.extend(new_faces) # update hedges mesh.hedges = [hedge for hedge in mesh.hedges if hedge not in un_used_hedges] # todo how to rebuilt points list
def get_leftmost_dissolved_hedge(left_loop_hedge, flag)
-
Expand source code
def get_leftmost_dissolved_hedge(left_loop_hedge, flag): # left_loop_hedge - half edge with origin which is leftmost point of a loop dis_hedge = get_leftmost_hedge_in_point(left_loop_hedge, flag) if dis_hedge: return dis_hedge used = set() next_two_candidates = [left_loop_hedge.new_next, left_loop_hedge.new_last] while next_two_candidates: low_hedge = next_two_candidates.pop() used.add(low_hedge) low_candidate = get_leftmost_hedge_in_point(low_hedge, flag) up_hedge = next_two_candidates.pop() used.add(up_hedge) up_candidate = get_leftmost_hedge_in_point(up_hedge, flag) if low_candidate and up_candidate: return low_candidate if is_closer_left(low_candidate, up_candidate) else up_candidate elif low_candidate: return low_candidate elif up_candidate: return up_candidate else: next_up_hedge = up_hedge.new_next next_low_hedge = low_hedge.new_last if next_up_hedge not in used and next_low_hedge not in used: next_two_candidates.extend([next_up_hedge, next_low_hedge]) elif next_low_hedge not in used: low_candidate = get_leftmost_hedge_in_point(next_low_hedge, flag) if low_candidate: return low_candidate else: raise LookupError('Did not manage to find any dissolved half edges connected to given loop') elif next_up_hedge not in used: up_candidate = get_leftmost_hedge_in_point(next_up_hedge, flag) if up_candidate: return up_candidate else: raise LookupError('Did not manage to find any dissolved half edges connected to given loop') else: raise LookupError('Did not manage to find any dissolved half edges connected to given loop')
def get_leftmost_hedge_in_point(hedge, flag)
-
Expand source code
def get_leftmost_hedge_in_point(hedge, flag): # can return None if such hedge does not exist # given hedge should be boundary half edge of dissolving faces dissolved_hedge = None for ccw_hedge in hedge.ccw_hedges: if id(ccw_hedge) == id(hedge): # avoid going in back direction continue if flag not in ccw_hedge.face.flags: # that means that all dissolved half edges was looked up break if not dissolved_hedge: # any candidate is okay dissolved_hedge = ccw_hedge continue if is_closer_left(ccw_hedge.slop, dissolved_hedge.slop): # next candidate should be closer to -X direction dissolved_hedge = ccw_hedge else: # according that half edges are sorted in ccw order, best candidate already has been found break return dissolved_hedge
def get_next_candidates(face, checked_faces, used, flag)
-
Expand source code
def get_next_candidates(face, checked_faces, used, flag): out = [] for hedge in face.outer.loop_hedges: candidate = hedge.twin.face if flag in candidate.flags and candidate not in checked_faces and candidate not in used: checked_faces.add(candidate) out.append(candidate) return out
def is_addable(face, candidate, face_points)
-
Expand source code
def is_addable(face, candidate, face_points): # does not take in account possible common points yet for loop_hedge in candidate.outer.loop_hedges: if loop_hedge.origin in face_points: if loop_hedge.twin.face != face and loop_hedge.last.twin.face != face: return False in_common_edge = False number_common_lines = 0 start_from_common_hedge = False for i, loop_hedge in enumerate(candidate.outer.loop_hedges): if loop_hedge.twin.face == face: if in_common_edge: continue else: in_common_edge = True number_common_lines += 1 if i == 0: start_from_common_hedge = True else: in_common_edge = False if in_common_edge and start_from_common_hedge: # here case is detected when one common line take in account two times number_common_lines -= 1 return True if number_common_lines == 1 else False
def is_closer_left(slop1, slop2)
-
Expand source code
def is_closer_left(slop1, slop2): slop1 = slop1 if slop1 <= 2 else 2 - slop1 % 2 if slop1 != 4.0 else 0 slop2 = slop2 if slop2 <= 2 else 2 - slop2 % 2 if slop2 != 4.0 else 0 return True if slop1 < slop2 else False
def walk_boundary_loop(hedge, bound_flag)
-
Expand source code
def walk_boundary_loop(hedge, bound_flag): # in back direction # yield hedge for ccw_hedge in hedge.ccw_hedges: if id(ccw_hedge) != id(hedge) and bound_flag not in ccw_hedge.face.flags: break current_hedge = ccw_hedge.twin yield current_hedge count = 0 while id(current_hedge) != id(hedge): for ccw_hedge in current_hedge.ccw_hedges: if id(ccw_hedge) != id(hedge) and bound_flag not in ccw_hedge.face.flags: break current_hedge = ccw_hedge.twin yield current_hedge count += 1 if count > len(hedge.mesh.hedges): raise RecursionError("Dissolve face algorithm can't built a loop from hedge - {}".format(hedge))
def walk_left(hedge)
-
Expand source code
def walk_left(hedge): # return next hedge closest to -X direction count = 0 start_hedge = hedge while count <= len(hedge.mesh.hesges): next_candidate = None for ccw_hedge in start_hedge.twin.ccw_hedges: if id(ccw_hedge) == id(start_hedge.twin): # avoid going in back direction continue if not next_candidate: # any candidate is okay next_candidate = ccw_hedge continue if is_closer_left(ccw_hedge.slop, next_candidate.slop): # next candidate should be closer to -X direction next_candidate = ccw_hedge else: # according that half edges are sorted in ccw order, best candidate already has been found break if not next_candidate: raise ValueError('Looks like the end of tail is reached, what to do next?') yield next_candidate start_hedge = next_candidate count += 1 raise RecursionError('It looks like you forget to exit from left walk')
Classes
class DCELMesh (accuracy=None)
-
Expand source code
class DCELMesh(DCELMesh_template): HalfEdge=HalfEdge
Ancestors
Class variables
var HalfEdge
class HalfEdge (*args, **kwargs)
-
Expand source code
class HalfEdge(HalfEdge_template): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.new_next = None self.new_last = None @property def new_loop_hedges(self): # returns hedges bounding face if not self.mesh: raise AttributeError("This method doesn't work with hedges({}) without link to a mesh." "Besides, mesh object should have proper number of half edges " "in hedges list".format(self)) yield self next_edge = self.next counter = 0 while id(next_edge) != id(self): yield next_edge try: next_edge = next_edge.next except AttributeError: raise AttributeError(' Some of half edges has incomplete data (does not have link to next half edge)') counter += 1 if counter > len(self.mesh.hedges): raise RecursionError('Hedge - {} does not have a loop'.format(self))
Ancestors
Instance variables
var new_loop_hedges
-
Expand source code
@property def new_loop_hedges(self): # returns hedges bounding face if not self.mesh: raise AttributeError("This method doesn't work with hedges({}) without link to a mesh." "Besides, mesh object should have proper number of half edges " "in hedges list".format(self)) yield self next_edge = self.next counter = 0 while id(next_edge) != id(self): yield next_edge try: next_edge = next_edge.next except AttributeError: raise AttributeError(' Some of half edges has incomplete data (does not have link to next half edge)') counter += 1 if counter > len(self.mesh.hedges): raise RecursionError('Hedge - {} does not have a loop'.format(self))
Inherited members