Changeset View
Standalone View
release/scripts/freestyle/modules/freestyle/chainingiterators.py
| Context not available. | |||||
| chaining iterators in Python | chaining iterators in Python | ||||
| """ | """ | ||||
| __all__ = ( | |||||
| "pyChainSilhouetteIterator", | |||||
| "pyChainSilhouetteGenericIterator", | |||||
| "pyExternalContourChainingIterator", # broken | |||||
| "pySketchyChainSilhouetteIterator", | |||||
| "pySketchyChainingIterator", | |||||
| "pyFillOcclusionsRelativeChainingIterator", | |||||
| "pyFillOcclusionsAbsoluteChainingIterator", | |||||
| "pyFillOcclusionsAbsoluteAndRelativeChainingIterator", | |||||
| "pyFillQi0AbsoluteAndRelativeChainingIterator", | |||||
| "pyNoIdChainSilhouetteIterator", | |||||
| ) | |||||
| # module members | # module members | ||||
| from _freestyle import ( | from _freestyle import ( | ||||
| ChainPredicateIterator, | ChainPredicateIterator, | ||||
| Context not available. | |||||
| from freestyle.predicates import ( | from freestyle.predicates import ( | ||||
| ExternalContourUP1D, | ExternalContourUP1D, | ||||
| ) | ) | ||||
| from freestyle.utils import ContextFunctions as CF | from freestyle.utils import ( | ||||
| ContextFunctions as CF, | |||||
| get_chain_length, | |||||
| ) | |||||
| import bpy | import bpy | ||||
| NATURES = ( | |||||
| Nature.SILHOUETTE, | |||||
| Nature.BORDER, | |||||
| Nature.CREASE, | |||||
| Nature.MATERIAL_BOUNDARY, | |||||
| Nature.EDGE_MARK, | |||||
| Nature.SUGGESTIVE_CONTOUR, | |||||
| Nature.VALLEY, | |||||
| Nature.RIDGE | |||||
| ) | |||||
| class pyChainSilhouetteIterator2(ChainingIterator): | |||||
| """Natural chaining iterator | |||||
| Follows the edges of the same nature following the topology of | |||||
| objects, with decreasing priority for silhouettes, then borders, | |||||
| then suggestive contours, then all other edge types. A ViewEdge | |||||
| is only chained once. | |||||
| """ | |||||
| def __init__(self, stayInSelection=True): | |||||
| ChainingIterator.__init__(self, stayInSelection, True, None, True) | |||||
| def init(self): | |||||
| pass | |||||
| def traverse(self, iter): | |||||
| it = AdjacencyIterator(iter) | |||||
| ## case of TVertex | |||||
| vertex = self.next_vertex | |||||
| if type(vertex) is TVertex: | |||||
| mate_id = vertex.get_mate(self.current_edge).id | |||||
| # return the first vertex that matches the id, implicit None if no matches | |||||
| for ve in it: | |||||
| if ve.id == mate_id: | |||||
| return ve | |||||
| # Note: change to this if pep 463 gets in | |||||
kjym3: Just a thought: We could define a local function (predicate) and use `filter` built-in function… | |||||
Not Done Inline ActionsI prefer using a generator expression over an anonymous function and a barely-used built-in. flokkievids: I prefer using a generator expression over an anonymous function and a barely-used built-in. | |||||
| # return next(ve for ve in it if ve.id == mate_id) except StopIteration: None | |||||
| ## case of NonTVertex | |||||
| winner = None | |||||
| for i, nat in enumerate(NATURES): | |||||
| if (nat & self.current_edge.nature): | |||||
| for ve in it: | |||||
| ve_nat = ve.nature | |||||
| if (ve_nat & nat): | |||||
| # search for matches in previous natures. if match -> break | |||||
| if nat != ve_nat and any((n & ve_nat) for n in NATURES[:i]): | |||||
| break | |||||
| # a second match must be an error | |||||
| if winner is not None: | |||||
| return None | |||||
| # assign winner | |||||
| winner = ve | |||||
| return winner | |||||
| class pyChainSilhouetteIterator(ChainingIterator): | class pyChainSilhouetteIterator(ChainingIterator): | ||||
| """Natural chaining iterator | """Natural chaining iterator | ||||
| Context not available. | |||||
| break | break | ||||
| return winner | return winner | ||||
| class pyChainSilhouetteGenericIterator(ChainingIterator): | class pyChainSilhouetteGenericIterator(ChainingIterator): | ||||
| """Natural chaining iterator | """Natural chaining iterator | ||||
Not Done Inline ActionsMinor comment: not result and bpy.app.debug_freestyle would a bit faster. kjym3: Minor comment: `not result and bpy.app.debug_freestyle` would a bit faster. | |||||
Not Done Inline ActionsJust interested here: would it really, significantly matter? flokkievids: Just interested here: would it really, significantly matter?
Adjusted in rev. 7 | |||||
Not Done Inline ActionsNo, not really. My comment was mostly for consistency (in other cases, we have put bpy.app.debug_freestyle at the end of conditions). kjym3: No, not really. My comment was mostly for consistency (in other cases, we have put bpy.app. | |||||
| Context not available. | |||||
| pass | pass | ||||
| def traverse(self, iter): | def traverse(self, iter): | ||||
| winner = None | |||||
| it = AdjacencyIterator(iter) | it = AdjacencyIterator(iter) | ||||
| tvertex = self.next_vertex | ## case of TVertex | ||||
| if type(tvertex) is TVertex: | vertex = self.next_vertex | ||||
| mateVE = tvertex.get_mate(self.current_edge) | if type(vertex) is TVertex: | ||||
| while not it.is_end: | mate_id = vertex.get_mate(self.current_edge).id | ||||
| ve = it.object | for ve in it: | ||||
| if ve.id == mateVE.id: | if ve.id == mate_id: | ||||
| winner = ve | return ve | ||||
Not Done Inline ActionsIf type(vertex) is TVertex, the method has to end here and return None. kjym3: If type(vertex) is TVertex, the method has to end here and return None. | |||||
| break | ## case of NonTVertex | ||||
| it.increment() | winner = None | ||||
| else: | for i, nat in enumerate(NATURES): | ||||
| ## case of NonTVertex | if (nat & self.current_edge.nature): | ||||
| natures = [Nature.SILHOUETTE,Nature.BORDER,Nature.CREASE,Nature.MATERIAL_BOUNDARY,Nature.EDGE_MARK, | for ve in it: | ||||
| Nature.SUGGESTIVE_CONTOUR,Nature.VALLEY,Nature.RIDGE] | ve_nat = ve.nature | ||||
| for i in range(len(natures)): | if ve.id == self.current_edge.id: | ||||
| currentNature = self.current_edge.nature | continue | ||||
| if (natures[i] & currentNature) != 0: | if (ve_nat & nat): | ||||
| count=0 | if nat != ve_nat and any(n & ve_nat for n in NATURES[:i]): | ||||
| while not it.is_end: | break | ||||
| visitNext = 0 | |||||
| oNature = it.object.nature | if winner is not None: | ||||
| ve = it.object | return None | ||||
| if ve.id == self.current_edge.id: | |||||
| it.increment() | winner = ve | ||||
| continue | return winner | ||||
| if (oNature & natures[i]) != 0: | |||||
| if natures[i] != oNature: | |||||
Not Done Inline ActionsThe revised traverse() needs an explicit return statement at the end of the method. kjym3: The revised traverse() needs an explicit return statement at the end of the method. | |||||
| for j in range(i): | ## broken: ve.*_svertex doesn't exist | ||||
| if (natures[j] & oNature) != 0: | |||||
| visitNext = 1 | |||||
| break | |||||
| if visitNext != 0: | |||||
| break | |||||
| count = count+1 | |||||
| winner = ve | |||||
| it.increment() | |||||
| if count != 1: | |||||
| winner = None | |||||
| break | |||||
| return winner | |||||
| class pyExternalContourChainingIterator(ChainingIterator): | class pyExternalContourChainingIterator(ChainingIterator): | ||||
| """Chains by external contour""" | """Chains by external contour""" | ||||
| def __init__(self): | def __init__(self): | ||||
| ChainingIterator.__init__(self, False, True, None, True) | ChainingIterator.__init__(self, False, True, None, True) | ||||
| self._isExternalContour = ExternalContourUP1D() | self.ExternalContour = ExternalContourUP1D() | ||||
| def init(self): | def init(self): | ||||
| self._nEdges = 0 | self._nEdges = 0 | ||||
Not Done Inline Actionsself._isInSelection seems not necessary. This line can be removed. kjym3: self._isInSelection seems not necessary. This line can be removed. | |||||
| self._isInSelection = 1 | self._isInSelection = True | ||||
| def checkViewEdge(self, ve, orientation): | def checkViewEdge(self, ve, orientation): | ||||
| if orientation != 0: | vertex = (ve.first_viewvertex if orientation else | ||||
| vertex = ve.second_svertex() | ve.last_viewvertex) | ||||
| else: | |||||
| vertex = ve.first_svertex() | it = AdjacencyIterator(vertex, True, True) | ||||
Not Done Inline ActionsIt would be faster just to return True here if result is True (as in the original code). kjym3: It would be faster just to return True here if result is True (as in the original code). | |||||
Not Done Inline ActionsIn what way? I didn't really test this but it seems to me that any() made for situations exactly like this. I would assume that it has also been optimized (memory usage ect.) It shouldn't be slower and the code shows exactly what you want to achieve. flokkievids: In what way? I didn't really test this but it seems to me that any() made for situations… | |||||
Not Done Inline ActionsYou are right, it looks like I was confused with all(). kjym3: You are right, it looks like I was confused with all(). | |||||
| it = AdjacencyIterator(vertex,1,1) | result = any(self.ExternalContour(ave) for ave in it) | ||||
| while not it.is_end: | # report if there is no result (that's bad) | ||||
| ave = it.object | if bpy.app.debug_freestyle and not result: | ||||
| if self._isExternalContour(ave): | |||||
| return True | |||||
| it.increment() | |||||
| if bpy.app.debug_freestyle: | |||||
| print("pyExternalContourChainingIterator : didn't find next edge") | print("pyExternalContourChainingIterator : didn't find next edge") | ||||
| return False | |||||
| return result | |||||
| def traverse(self, iter): | def traverse(self, iter): | ||||
| winner = None | winner = None | ||||
| self._nEdges += 1 | |||||
| it = AdjacencyIterator(iter) | it = AdjacencyIterator(iter) | ||||
| while not it.is_end: | time_stamp = CF.get_time_stamp() | ||||
| ve = it.object | |||||
| if self._isExternalContour(ve): | for ve in it: | ||||
| if ve.time_stamp == CF.get_time_stamp(): | if self.ExternalContour(ve) and ve.time_stamp == time_stamp: | ||||
| winner = ve | winner = ve | ||||
| it.increment() | |||||
| self._nEdges = self._nEdges+1 | |||||
| if winner is None: | if winner is None: | ||||
| orient = 1 | |||||
| it = AdjacencyIterator(iter) | it = AdjacencyIterator(iter) | ||||
| while not it.is_end: | for ve in it: | ||||
| ve = it.object | if self.checkViewEdge(ve, not it.is_incoming): | ||||
| if it.is_incoming: | |||||
| orient = 0 | |||||
| good = self.checkViewEdge(ve,orient) | |||||
| if good != 0: | |||||
| winner = ve | winner = ve | ||||
| it.increment() | |||||
Not Done Inline ActionsIn the original code, once 'orient' is set to 0 it stays as so. Can you confirm the new logic is okay? kjym3: In the original code, once 'orient' is set to 0 it stays as so. Can you confirm the new logic… | |||||
Not Done Inline Actionstests with imagemagick over the past two months have not once given an error here. I will however look into it. flokkievids: tests with imagemagick over the past two months have not once given an error here. I will… | |||||
| return winner | return winner | ||||
| Context not available. | |||||
| def __init__(self, nRounds=3,stayInSelection=True): | def __init__(self, nRounds=3,stayInSelection=True): | ||||
| ChainingIterator.__init__(self, stayInSelection, False, None, True) | ChainingIterator.__init__(self, stayInSelection, False, None, True) | ||||
| self._timeStamp = CF.get_time_stamp()+nRounds | self._timeStamp = CF.get_time_stamp() + nRounds | ||||
| self._nRounds = nRounds | self._nRounds = nRounds | ||||
| def init(self): | def init(self): | ||||
| self._timeStamp = CF.get_time_stamp()+self._nRounds | self._timeStamp = CF.get_time_stamp() + self._nRounds | ||||
| # keeping this local saves passing a reference to 'self' around | |||||
| def make_sketchy(self, ve): | |||||
| """ | |||||
| Creates the skeychy effect by causing the chain to run from | |||||
| the start again. (loop over itself again) | |||||
| """ | |||||
| if ve is None: | |||||
| ve = self.current_edge | |||||
| if ve.chaining_time_stamp == self._timeStamp: | |||||
| return None | |||||
| return ve | |||||
| def traverse(self, iter): | def traverse(self, iter): | ||||
| winner = None | |||||
| it = AdjacencyIterator(iter) | it = AdjacencyIterator(iter) | ||||
| tvertex = self.next_vertex | ## case of TVertex | ||||
| if type(tvertex) is TVertex: | vertex = self.next_vertex | ||||
| mateVE = tvertex.get_mate(self.current_edge) | if type(vertex) is TVertex: | ||||
| while not it.is_end: | mate_id = vertex.get_mate(self.current_edge).id | ||||
| ve = it.object | # return the first vertex that matches the id, None if no matches | ||||
| if ve.id == mateVE.id: | for ve in it: | ||||
| winner = ve | if ve.id == mate_id: | ||||
Not Done Inline ActionsIf type(vertex) is TVertex, the method has to end here and return None. kjym3: If type(vertex) is TVertex, the method has to end here and return None. | |||||
| break | return self.make_sketchy(ve) | ||||
| it.increment() | |||||
| else: | ## case of NonTVertex | ||||
| ## case of NonTVertex | winner = None | ||||
| natures = [Nature.SILHOUETTE,Nature.BORDER,Nature.CREASE,Nature.MATERIAL_BOUNDARY,Nature.EDGE_MARK, | for i, nat in enumerate(NATURES): | ||||
| Nature.SUGGESTIVE_CONTOUR,Nature.VALLEY,Nature.RIDGE] | if (nat & self.current_edge.nature): | ||||
| for i in range(len(natures)): | for ve in it: | ||||
| currentNature = self.current_edge.nature | if ve.id == self.current_edge.id: | ||||
| if (natures[i] & currentNature) != 0: | continue | ||||
| count=0 | ve_nat = ve.nature | ||||
| while not it.is_end: | if (ve_nat & nat): | ||||
| visitNext = 0 | if (nat != ve_nat) and any(n & ve_nat for n in NATURES[:i]): | ||||
| oNature = it.object.nature | break | ||||
| ve = it.object | |||||
| if ve.id == self.current_edge.id: | if winner is not None: | ||||
| it.increment() | return self.make_sketchy(None) | ||||
| continue | |||||
| if (oNature & natures[i]) != 0: | winner = ve | ||||
| if (natures[i] != oNature) != 0: | break | ||||
| for j in range(i): | |||||
Not Done Inline ActionsI like the idea of introducing the .make_sketchy() helper method which helps code reading :) kjym3: I like the idea of introducing the .make_sketchy() helper method which helps code reading :) | |||||
| if (natures[j] & oNature) != 0: | return self.make_sketchy(winner) | ||||
| visitNext = 1 | |||||
| break | |||||
| if visitNext != 0: | |||||
| break | |||||
| count = count+1 | |||||
| winner = ve | |||||
| it.increment() | |||||
| if count != 1: | |||||
| winner = None | |||||
| break | |||||
| if winner is None: | |||||
| winner = self.current_edge | |||||
| if winner.chaining_time_stamp == self._timeStamp: | |||||
| winner = None | |||||
| return winner | |||||
| class pySketchyChainingIterator(ChainingIterator): | class pySketchyChainingIterator(ChainingIterator): | ||||
Not Done Inline ActionsShould be TVertex. Please, double-check all chaining iterators for this typo. kjym3: Should be `TVertex`. Please, double-check all chaining iterators for this typo. | |||||
| Context not available. | |||||
| """ | """ | ||||
| def __init__(self, nRounds=3, stayInSelection=True): | def __init__(self, nRounds=3, stayInSelection=True): | ||||
| ChainingIterator.__init__(self, stayInSelection, False, None, True) | ChainingIterator.__init__(self, stayInSelection, False, None, True) | ||||
| self._timeStamp = CF.get_time_stamp()+nRounds | self._timeStamp = CF.get_time_stamp() + nRounds | ||||
| self._nRounds = nRounds | self._nRounds = nRounds | ||||
| def init(self): | def init(self): | ||||
| self._timeStamp = CF.get_time_stamp()+self._nRounds | self._timeStamp = CF.get_time_stamp() + self._nRounds | ||||
| def traverse(self, iter): | def traverse(self, iter): | ||||
| winner = None | winner = None | ||||
| found = False | found = False | ||||
| it = AdjacencyIterator(iter) | for ve in AdjacencyIterator(iter): | ||||
| while not it.is_end: | |||||
| ve = it.object | |||||
| if ve.id == self.current_edge.id: | if ve.id == self.current_edge.id: | ||||
| found = True | found = True | ||||
| it.increment() | |||||
| continue | continue | ||||
| winner = ve | winner = ve | ||||
| it.increment() | |||||
| if not found: | if not found: | ||||
| # This is a fatal error condition: self.current_edge must be found | # This is a fatal error condition: self.current_edge must be found | ||||
| # among the edges seen by the AdjacencyIterator [bug #35695]. | # among the edges seen by the AdjacencyIterator [bug #35695]. | ||||
| if bpy.app.debug_freestyle: | if bpy.app.debug_freestyle: | ||||
| print('pySketchyChainingIterator: current edge not found') | print('pySketchyChainingIterator: current edge not found') | ||||
| return None | return None | ||||
| if winner is None: | if winner is None: | ||||
| winner = self.current_edge | winner = self.current_edge | ||||
| if winner.chaining_time_stamp == self._timeStamp: | if winner.chaining_time_stamp == self._timeStamp: | ||||
| Context not available. | |||||
| def init(self): | def init(self): | ||||
| # A chain's length should preferably be evaluated only once. | # A chain's length should preferably be evaluated only once. | ||||
| # Therefore, the chain length is reset here. | # Therefore, the chain length is reset here. | ||||
| self._length = 0 | self._length = 0.0 | ||||
| def traverse(self, iter): | def traverse(self, iter): | ||||
| winner = None | winner = None | ||||
| winnerOrientation = False | |||||
| winnerOrientation = False | |||||
| winnerOrientation = 0 | |||||
| #print(self.current_edge.id.first, self.current_edge.id.second) | |||||
| it = AdjacencyIterator(iter) | it = AdjacencyIterator(iter) | ||||
| tvertex = self.next_vertex | ## case of NonTVertex | ||||
| if type(tvertex) is TVertex: | vertex = self.next_vertex | ||||
| mateVE = tvertex.get_mate(self.current_edge) | if type(vertex) is TVertex: | ||||
| while not it.is_end: | mate_id = vertex.get_mate(self.current_edge).id | ||||
| ve = it.object | for ve in it: | ||||
| if ve.id == mateVE.id: | if ve.id == mate_id: | ||||
| winner = ve | winner = ve | ||||
| winnerOrientation = not it.is_incoming | winnerOrientation = not it.is_incoming | ||||
| break | break | ||||
| it.increment() | ## case of NonTVertex | ||||
| else: | else: | ||||
| ## case of NonTVertex | for nat in NATURES: | ||||
| natures = [Nature.SILHOUETTE,Nature.BORDER,Nature.CREASE,Nature.MATERIAL_BOUNDARY,Nature.EDGE_MARK, | if (self.current_edge.nature & nat): | ||||
| Nature.SUGGESTIVE_CONTOUR,Nature.VALLEY,Nature.RIDGE] | for ve in it: | ||||
| for nat in natures: | if (ve.nature & nat): | ||||
| if (self.current_edge.nature & nat) != 0: | if winner is not None: | ||||
| count=0 | return None | ||||
| while not it.is_end: | |||||
| ve = it.object | |||||
| if (ve.nature & nat) != 0: | |||||
| count = count+1 | |||||
| winner = ve | winner = ve | ||||
| winnerOrientation = not it.is_incoming | winnerOrientation = not it.is_incoming | ||||
| it.increment() | |||||
| if count != 1: | |||||
| winner = None | |||||
| break | break | ||||
| if winner is not None: | |||||
| # check whether this edge was part of the selection | |||||
| if winner.time_stamp != CF.get_time_stamp(): | |||||
| #print("---", winner.id.first, winner.id.second) | |||||
| # if not, let's check whether it's short enough with | |||||
| # respect to the chain made without staying in the selection | |||||
| #------------------------------------------------------------ | |||||
| # Did we compute the prospective chain length already ? | |||||
| if self._length == 0: | |||||
| #if not, let's do it | |||||
| _it = pyChainSilhouetteGenericIterator(False, False) | |||||
| _it.begin = winner | |||||
| _it.current_edge = winner | |||||
| _it.orientation = winnerOrientation | |||||
| _it.init() | |||||
| while not _it.is_end: | |||||
| ve = _it.object | |||||
| #print("--------", ve.id.first, ve.id.second) | |||||
| self._length = self._length + ve.length_2d | |||||
| _it.increment() | |||||
| if _it.is_begin: | |||||
| break; | |||||
| _it.begin = winner | |||||
| _it.current_edge = winner | |||||
| _it.orientation = winnerOrientation | |||||
| if not _it.is_begin: | |||||
| _it.decrement() | |||||
| while (not _it.is_end) and (not _it.is_begin): | |||||
| ve = _it.object | |||||
| #print("--------", ve.id.first, ve.id.second) | |||||
| self._length = self._length + ve.length_2d | |||||
| _it.decrement() | |||||
| # let's do the comparison: | |||||
| # nw let's compute the length of this connex non selected part: | |||||
| connexl = 0 | |||||
| _cit = pyChainSilhouetteGenericIterator(False, False) | # check timestamp to see if this edge was part of the selection | ||||
| _cit.begin = winner | if winner is not None and winner.time_stamp != CF.get_time_stamp(): | ||||
| _cit.current_edge = winner | """ | ||||
| _cit.orientation = winnerOrientation | if the edge wasn't part of the selection, let's see | ||||
| _cit.init() | whether it's short enough (with respect to self.percent) | ||||
| while _cit.is_end == 0 and _cit.object.time_stamp != CF.get_time_stamp(): | to be included. | ||||
Not Done Inline ActionsNot sure if a docstring here is useful. Usual comment lines would suffice. kjym3: Not sure if a docstring here is useful. Usual comment lines would suffice. | |||||
| ve = _cit.object | """ | ||||
Not Done Inline ActionsThe original length == 0 appears more logical than not length. Please, find a few more occurrence of the same notation change in the rest of the code. kjym3: The original `length == 0` appears more logical than `not length`. Please, find a few more… | |||||
| #print("-------- --------", ve.id.first, ve.id.second) | if not self._length: | ||||
| connexl = connexl + ve.length_2d | self._length = get_chain_length(winner, winnerOrientation) | ||||
| _cit.increment() | |||||
| if connexl > self._percent * self._length: | # check if the gap can be bridged | ||||
| winner = None | connexl = 0.0 | ||||
| _cit = pyChainSilhouetteGenericIterator(False, False) | |||||
| _cit.begin = winner | |||||
| _cit.current_edge = winner | |||||
| _cit.orientation = winnerOrientation | |||||
| _cit.init() | |||||
| while (not _cit.is_end) and _cit.object.time_stamp != CF.get_time_stamp(): | |||||
| connexl += _cit.object.length_2d | |||||
| _cit.increment() | |||||
Not Done Inline ActionsThis if ... break block does not exist in the original code. Was it left here by mistake? There are a few more occurrence of the additional if ... break block. Please, double-check them all. kjym3: This `if ... break` block does not exist in the original code. Was it left here by mistake? | |||||
Not Done Inline ActionsWe spoke about this earlier. there is a chance the iterator will turn back on itself, creating an infinite loop. This eliminates the chance of that happening. flokkievids: We spoke about this earlier. there is a chance the iterator will turn back on itself, creating… | |||||
Not Done Inline ActionsThanks for the reminder. The code revision looks okay. kjym3: Thanks for the reminder. The code revision looks okay. | |||||
| if connexl > self._percent * self._length: | |||||
| return None | |||||
| return winner | return winner | ||||
| Context not available. | |||||
| def traverse(self, iter): | def traverse(self, iter): | ||||
| winner = None | winner = None | ||||
| winnerOrientation = False | winnerOrientation = False | ||||
| #print(self.current_edge.id.first, self.current_edge.id.second) | |||||
| it = AdjacencyIterator(iter) | it = AdjacencyIterator(iter) | ||||
| tvertex = self.next_vertex | ## case of NonTVertex | ||||
| if type(tvertex) is TVertex: | vertex = self.next_vertex | ||||
| mateVE = tvertex.get_mate(self.current_edge) | if type(vertex) is TVertex: | ||||
| while not it.is_end: | mate_id = vertex.get_mate(self.current_edge).id | ||||
| ve = it.object | for ve in it: | ||||
| if ve.id == mateVE.id: | if ve.id == mate_id: | ||||
| winner = ve | winner = ve | ||||
| winnerOrientation = not it.is_incoming | winnerOrientation = not it.is_incoming | ||||
| break | break | ||||
| it.increment() | ## case of NonTVertex | ||||
| else: | else: | ||||
| ## case of NonTVertex | for nat in NATURES: | ||||
| natures = [Nature.SILHOUETTE,Nature.BORDER,Nature.CREASE,Nature.MATERIAL_BOUNDARY,Nature.EDGE_MARK, | if (self.current_edge.nature & nat): | ||||
| Nature.SUGGESTIVE_CONTOUR,Nature.VALLEY,Nature.RIDGE] | for ve in it: | ||||
| for nat in natures: | if (ve.nature & nat): | ||||
| if (self.current_edge.nature & nat) != 0: | if winner is not None: | ||||
| count=0 | return None | ||||
| while not it.is_end: | |||||
| ve = it.object | |||||
| if (ve.nature & nat) != 0: | |||||
| count = count+1 | |||||
| winner = ve | winner = ve | ||||
| winnerOrientation = not it.is_incoming | winnerOrientation = not it.is_incoming | ||||
| it.increment() | |||||
| if count != 1: | |||||
| winner = None | |||||
| break | break | ||||
| if winner is not None: | |||||
| # check whether this edge was part of the selection | if winner is not None and winner.time_stamp != CF.get_time_stamp(): | ||||
| if winner.time_stamp != CF.get_time_stamp(): | connexl = 0.0 | ||||
| #print("---", winner.id.first, winner.id.second) | _cit = pyChainSilhouetteGenericIterator(False, False) | ||||
| # nw let's compute the length of this connex non selected part: | _cit.begin = winner | ||||
| connexl = 0 | _cit.current_edge = winner | ||||
| _cit = pyChainSilhouetteGenericIterator(False, False) | _cit.orientation = winnerOrientation | ||||
| _cit.begin = winner | _cit.init() | ||||
| _cit.current_edge = winner | |||||
| _cit.orientation = winnerOrientation | while (not _cit.is_end) and _cit.object.time_stamp != CF.get_time_stamp(): | ||||
| _cit.init() | connexl += _cit.object.length_2d | ||||
| while _cit.is_end == 0 and _cit.object.time_stamp != CF.get_time_stamp(): | _cit.increment() | ||||
| ve = _cit.object | |||||
| #print("-------- --------", ve.id.first, ve.id.second) | if connexl > self._length: | ||||
| connexl = connexl + ve.length_2d | return None | ||||
| _cit.increment() | |||||
| if connexl > self._length: | |||||
| winner = None | |||||
| return winner | return winner | ||||
| Context not available. | |||||
| """ | """ | ||||
| def __init__(self, percent, l): | def __init__(self, percent, l): | ||||
| ChainingIterator.__init__(self, False, True, None, True) | ChainingIterator.__init__(self, False, True, None, True) | ||||
| self._length = 0 | self._length = 0.0 | ||||
| self._absLength = l | self._absLength = l | ||||
| self._percent = float(percent) | self._percent = float(percent) | ||||
| Context not available. | |||||
| # each time we're evaluating a chain length | # each time we're evaluating a chain length | ||||
| # we try to do it once. Thus we reinit | # we try to do it once. Thus we reinit | ||||
| # the chain length here: | # the chain length here: | ||||
| self._length = 0 | self._length = 0.0 | ||||
| def traverse(self, iter): | def traverse(self, iter): | ||||
| winner = None | winner = None | ||||
| winnerOrientation = False | winnerOrientation = False | ||||
| #print(self.current_edge.id.first, self.current_edge.id.second) | |||||
| it = AdjacencyIterator(iter) | it = AdjacencyIterator(iter) | ||||
| tvertex = self.next_vertex | ## case of NonTVertex | ||||
| if type(tvertex) is TVertex: | vertex = self.next_vertex | ||||
Not Done Inline ActionsThis comment should be ## case of TVertex. Please, correct a few more occurrence of the same typo in other places. kjym3: This comment should be `## case of TVertex`. Please, correct a few more occurrence of the same… | |||||
| mateVE = tvertex.get_mate(self.current_edge) | if type(vertex) is TVertex: | ||||
| while not it.is_end: | mate_id = vertex.get_mate(self.current_edge).id | ||||
| ve = it.object | for ve in it: | ||||
| if ve.id == mateVE.id: | if ve.id == mate_id: | ||||
| winner = ve | winner = ve | ||||
| winnerOrientation = not it.is_incoming | winnerOrientation = not it.is_incoming | ||||
| break | break | ||||
| it.increment() | ## case of NonTVertex | ||||
| else: | else: | ||||
| ## case of NonTVertex | for nat in NATURES: | ||||
| natures = [Nature.SILHOUETTE,Nature.BORDER,Nature.CREASE,Nature.MATERIAL_BOUNDARY,Nature.EDGE_MARK, | if (self.current_edge.nature & nat): | ||||
| Nature.SUGGESTIVE_CONTOUR,Nature.VALLEY,Nature.RIDGE] | for ve in it: | ||||
| for nat in natures: | if (ve.nature & nat): | ||||
| if (self.current_edge.nature & nat) != 0: | if winner is not None: | ||||
| count=0 | return None | ||||
| while not it.is_end: | |||||
| ve = it.object | |||||
| if (ve.nature & nat) != 0: | |||||
| count = count+1 | |||||
| winner = ve | winner = ve | ||||
| winnerOrientation = not it.is_incoming | winnerOrientation = not it.is_incoming | ||||
| it.increment() | |||||
| if count != 1: | |||||
| winner = None | |||||
| break | break | ||||
| if winner is not None: | |||||
| # check whether this edge was part of the selection | if winner is not None and winner.time_stamp != CF.get_time_stamp(): | ||||
| if winner.time_stamp != CF.get_time_stamp(): | |||||
| #print("---", winner.id.first, winner.id.second) | if not self._length: | ||||
| # if not, let's check whether it's short enough with | self._length = get_chain_length(winner, winnerOrientation) | ||||
| # respect to the chain made without staying in the selection | |||||
| #------------------------------------------------------------ | connexl = 0.0 | ||||
| # Did we compute the prospective chain length already ? | |||||
| if self._length == 0: | |||||
| #if not, let's do it | |||||
| _it = pyChainSilhouetteGenericIterator(False, False) | |||||
| _it.begin = winner | |||||
| _it.current_edge = winner | |||||
| _it.orientation = winnerOrientation | |||||
| _it.init() | |||||
| while not _it.is_end: | |||||
| ve = _it.object | |||||
| #print("--------", ve.id.first, ve.id.second) | |||||
| self._length = self._length + ve.length_2d | |||||
| _it.increment() | |||||
| if _it.is_begin: | |||||
| break; | |||||
| _it.begin = winner | |||||
| _it.current_edge = winner | |||||
| _it.orientation = winnerOrientation | |||||
| if not _it.is_begin: | |||||
| _it.decrement() | |||||
| while (not _it.is_end) and (not _it.is_begin): | |||||
| ve = _it.object | |||||
| #print("--------", ve.id.first, ve.id.second) | |||||
| self._length = self._length + ve.length_2d | |||||
| _it.decrement() | |||||
| # let's do the comparison: | |||||
| # nw let's compute the length of this connex non selected part: | |||||
| connexl = 0 | |||||
| _cit = pyChainSilhouetteGenericIterator(False, False) | _cit = pyChainSilhouetteGenericIterator(False, False) | ||||
| _cit.begin = winner | _cit.begin = winner | ||||
| _cit.current_edge = winner | _cit.current_edge = winner | ||||
| _cit.orientation = winnerOrientation | _cit.orientation = winnerOrientation | ||||
| _cit.init() | _cit.init() | ||||
| while _cit.is_end == 0 and _cit.object.time_stamp != CF.get_time_stamp(): | while (not _cit.is_end) and _cit.object.time_stamp != CF.get_time_stamp(): | ||||
| ve = _cit.object | connexl += _cit.object.length_2d | ||||
| #print("-------- --------", ve.id.first, ve.id.second) | |||||
| connexl = connexl + ve.length_2d | |||||
| _cit.increment() | _cit.increment() | ||||
| if (connexl > self._percent * self._length) or (connexl > self._absLength): | if (connexl > self._percent * self._length) or (connexl > self._absLength): | ||||
| winner = None | return None | ||||
| return winner | return winner | ||||
| Context not available. | |||||
| """ | """ | ||||
| def __init__(self, percent, l): | def __init__(self, percent, l): | ||||
| ChainingIterator.__init__(self, False, True, None, True) | ChainingIterator.__init__(self, False, True, None, True) | ||||
| self._length = 0 | self._length = 0.0 | ||||
| self._absLength = l | self._absLength = l | ||||
| self._percent = float(percent) | self._percent = percent | ||||
| def init(self): | def init(self): | ||||
| # A chain's length should preverably be evaluated only once. | # A chain's length should preverably be evaluated only once. | ||||
| # Therefore, the chain length is reset here. | # Therefore, the chain length is reset here. | ||||
| self._length = 0 | self._length = 0.0 | ||||
| def traverse(self, iter): | def traverse(self, iter): | ||||
| winner = None | winner = None | ||||
| winnerOrientation = False | winnerOrientation = False | ||||
| #print(self.current_edge.id.first, self.current_edge.id.second) | |||||
| it = AdjacencyIterator(iter) | it = AdjacencyIterator(iter) | ||||
| tvertex = self.next_vertex | ## case of NonTVertex | ||||
| if type(tvertex) is TVertex: | vertex = self.next_vertex | ||||
| mateVE = tvertex.get_mate(self.current_edge) | if type(vertex) is TVertex: | ||||
| while not it.is_end: | mate_id = vertex.get_mate(self.current_edge).id | ||||
| ve = it.object | for ve in it: | ||||
| if ve.id == mateVE.id: | if ve.id == mate_id: | ||||
| winner = ve | winner = ve | ||||
| winnerOrientation = not it.is_incoming | winnerOrientation = not it.is_incoming | ||||
| break | break | ||||
| it.increment() | ## case of NonTVertex | ||||
| else: | else: | ||||
| ## case of NonTVertex | for nat in NATURES: | ||||
| natures = [Nature.SILHOUETTE,Nature.BORDER,Nature.CREASE,Nature.MATERIAL_BOUNDARY,Nature.EDGE_MARK, | if (self.current_edge.nature & nat): | ||||
| Nature.SUGGESTIVE_CONTOUR,Nature.VALLEY,Nature.RIDGE] | for ve in it: | ||||
| for nat in natures: | if (ve.nature & nat): | ||||
| if (self.current_edge.nature & nat) != 0: | if winner is not None: | ||||
| count=0 | return None | ||||
| while not it.is_end: | |||||
| ve = it.object | |||||
| if (ve.nature & nat) != 0: | |||||
| count = count+1 | |||||
| winner = ve | winner = ve | ||||
| winnerOrientation = not it.is_incoming | winnerOrientation = not it.is_incoming | ||||
| it.increment() | |||||
| if count != 1: | |||||
| winner = None | |||||
| break | break | ||||
| if winner is not None: | |||||
| # check whether this edge was part of the selection | if winner is not None and winner.qi: | ||||
| if winner.qi != 0: | |||||
| #print("---", winner.id.first, winner.id.second) | |||||
| # if not, let's check whether it's short enough with | if not self._length: | ||||
| # respect to the chain made without staying in the selection | self._length = get_chain_length(winner, winnerOrientation) | ||||
| #------------------------------------------------------------ | |||||
| # Did we compute the prospective chain length already ? | |||||
| if self._length == 0: | |||||
| #if not, let's do it | |||||
| _it = pyChainSilhouetteGenericIterator(False, False) | |||||
| _it.begin = winner | |||||
| _it.current_edge = winner | |||||
| _it.orientation = winnerOrientation | |||||
| _it.init() | |||||
| while not _it.is_end: | |||||
| ve = _it.object | |||||
| #print("--------", ve.id.first, ve.id.second) | |||||
| self._length = self._length + ve.length_2d | |||||
| _it.increment() | |||||
| if _it.is_begin: | |||||
| break; | |||||
| _it.begin = winner | |||||
| _it.current_edge = winner | |||||
| _it.orientation = winnerOrientation | |||||
| if not _it.is_begin: | |||||
| _it.decrement() | |||||
| while (not _it.is_end) and (not _it.is_begin): | |||||
| ve = _it.object | |||||
| #print("--------", ve.id.first, ve.id.second) | |||||
| self._length = self._length + ve.length_2d | |||||
| _it.decrement() | |||||
| # let's do the comparison: | |||||
| # nw let's compute the length of this connex non selected part: | |||||
| connexl = 0 | connexl = 0 | ||||
| _cit = pyChainSilhouetteGenericIterator(False, False) | _cit = pyChainSilhouetteGenericIterator(False, False) | ||||
| _cit.begin = winner | _cit.begin = winner | ||||
| _cit.current_edge = winner | _cit.current_edge = winner | ||||
| _cit.orientation = winnerOrientation | _cit.orientation = winnerOrientation | ||||
| _cit.init() | _cit.init() | ||||
| while not _cit.is_end and _cit.object.qi != 0: | while (not _cit.is_end) and _cit.object.qi: | ||||
| ve = _cit.object | connexl += _cit.object.length_2d | ||||
| #print("-------- --------", ve.id.first, ve.id.second) | |||||
| connexl = connexl + ve.length_2d | |||||
| _cit.increment() | _cit.increment() | ||||
Not Done Inline ActionsThe original qi != 0 seems more logical. kjym3: The original `qi != 0` seems more logical. | |||||
| if (connexl > self._percent * self._length) or (connexl > self._absLength): | if (connexl > self._percent * self._length) or (connexl > self._absLength): | ||||
| winner = None | return None | ||||
| return winner | return winner | ||||
| Context not available. | |||||
| def traverse(self, iter): | def traverse(self, iter): | ||||
| winner = None | winner = None | ||||
| it = AdjacencyIterator(iter) | it = AdjacencyIterator(iter) | ||||
| tvertex = self.next_vertex | # case of TVertex | ||||
| if type(tvertex) is TVertex: | vertex = self.next_vertex | ||||
| mateVE = tvertex.get_mate(self.current_edge) | if type(vertex) is TVertex: | ||||
| while not it.is_end: | for ve in it: | ||||
| ve = it.object | # case one | ||||
| feB = self.current_edge.last_fedge | vA = self.current_edge.last_fedge.second_svertex | ||||
| feA = ve.first_fedge | vB = ve.first_fedge.first_svertex | ||||
| vB = feB.second_svertex | |||||
| vA = feA.first_svertex | |||||
| if vA.id.first == vB.id.first: | if vA.id.first == vB.id.first: | ||||
| winner = ve | return ve | ||||
| break | # case two | ||||
| feA = self.current_edge.first_fedge | vA = self.current_edge.first_fedge.first_svertex | ||||
| feB = ve.last_fedge | vB = ve.last_fedge.second_svertex | ||||
| vB = feB.second_svertex | |||||
| vA = feA.first_svertex | |||||
| if vA.id.first == vB.id.first: | if vA.id.first == vB.id.first: | ||||
| winner = ve | return ve | ||||
| break | # case three | ||||
| feA = self.current_edge.last_fedge | vA = self.current_edge.last_fedge.second_svertex | ||||
| feB = ve.last_fedge | vB = ve.last_fedge.second_svertex | ||||
| vB = feB.second_svertex | |||||
| vA = feA.second_svertex | |||||
| if vA.id.first == vB.id.first: | if vA.id.first == vB.id.first: | ||||
| winner = ve | return ve | ||||
| break | # case four | ||||
| feA = self.current_edge.first_fedge | vA = self.current_edge.first_fedge.first_svertex | ||||
| feB = ve.first_fedge | vB = ve.first_fedge.first_svertex | ||||
| vB = feB.first_svertex | |||||
| vA = feA.first_svertex | |||||
| if vA.id.first == vB.id.first: | if vA.id.first == vB.id.first: | ||||
| winner = ve | return ve | ||||
| break | ## case of NonTVertex | ||||
| it.increment() | |||||
| else: | else: | ||||
| ## case of NonTVertex | for i, nat in enumerate(NATURES): | ||||
| natures = [Nature.SILHOUETTE,Nature.BORDER,Nature.CREASE,Nature.MATERIAL_BOUNDARY,Nature.EDGE_MARK, | if (nat & self.current_edge.nature): | ||||
| Nature.SUGGESTIVE_CONTOUR,Nature.VALLEY,Nature.RIDGE] | for ve in it: | ||||
| for i in range(len(natures)): | ve_nat = ve.nature | ||||
| currentNature = self.current_edge.nature | if (ve_nat & nat): | ||||
| if (natures[i] & currentNature) != 0: | if (nat != ve_nat) and any(n & ve_nat for n in NATURES[:i]): | ||||
| count=0 | break | ||||
| while not it.is_end: | |||||
| visitNext = 0 | if winner is not None: | ||||
| oNature = it.object.nature | return | ||||
| if (oNature & natures[i]) != 0: | |||||
| if natures[i] != oNature: | winner = ve | ||||
| for j in range(i): | return winner | ||||
| if (natures[j] & oNature) != 0: | |||||
| visitNext = 1 | |||||
| break | |||||
| if visitNext != 0: | |||||
| break | |||||
| count = count+1 | |||||
| winner = it.object | # for i, nat in enumerate(NATURES): | ||||
| it.increment() | # if (nat & self.current_edge.nature): | ||||
| if count != 1: | # for ve in it: | ||||
| winner = None | # ve_nat = ve.nature | ||||
| break | # if (ve_nat & nat): | ||||
| return winner | # # search for matches in previous natures. if match -> break | ||||
| # if nat != ve_nat and any((n & ve_nat) for n in NATURES[:i]): | |||||
| # break | |||||
| # # a second match must be an error | |||||
| # if winner is not None: | |||||
| # return None | |||||
| # # assign winner | |||||
| # winner = ve | |||||
| # return winner | |||||
| Context not available. | |||||
Not Done Inline ActionsPlease, consider add an explicit return None here. kjym3: Please, consider add an explicit `return None` here. | |||||
Not Done Inline ActionsIs this comment block (lines 690-702) worth retaining here? kjym3: Is this comment block (lines 690-702) worth retaining here? | |||||
Just a thought: We could define a local function (predicate) and use filter built-in function to make this generator-based expression:
mate_id = vertex.get_mate(self.current_edge).id def is_mate(ve): return ve.id == mate_id return next(filter(is_mate, it), None)The predicate can be an unnamed lambda function in this case, there are similar cases in other chaining iterators that could employ the same idiom. Regular use of the same idiom is preferable.