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_2Functions
- def add_faces(face, dis_face, face_points)
- 
Expand source codedef 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 codedef 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 codedef 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 codedef 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 codedef 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 codedef 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 codedef 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 codedef 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 codedef 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 codedef 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 codedef 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 codeclass DCELMesh(DCELMesh_template): HalfEdge=HalfEdgeAncestorsClass variables- var HalfEdge
 
- class HalfEdge (*args, **kwargs)
- 
Expand source codeclass 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))AncestorsInstance 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