blender/release/scripts/freestyle/style_modules/ChainingIterators.py

704 lines
22 KiB
Python

# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# Filename : ChainingIterators.py
# Author : Stephane Grabli
# Date : 04/08/2005
# Purpose : Chaining Iterators to be used with chaining operators
from freestyle import AdjacencyIterator, ChainingIterator, ExternalContourUP1D, Nature, TVertex
from freestyle import ContextFunctions as CF
## the natural chaining iterator
## It follows the edges of same nature following the topology of
## objects with preseance on silhouettes, then borders,
## then suggestive contours, then everything else. It doesn't chain the same ViewEdge twice
## You can specify whether to stay in the selection or not.
class pyChainSilhouetteIterator(ChainingIterator):
def __init__(self, stayInSelection=True):
ChainingIterator.__init__(self, stayInSelection, True, None, True)
def init(self):
pass
def traverse(self, iter):
winner = None
it = AdjacencyIterator(iter)
tvertex = self.next_vertex
if type(tvertex) is TVertex:
mateVE = tvertex.get_mate(self.current_edge)
while not it.is_end:
ve = it.object
if ve.id == mateVE.id:
winner = ve
break
it.increment()
else:
## case of NonTVertex
natures = [Nature.SILHOUETTE,Nature.BORDER,Nature.CREASE,Nature.SUGGESTIVE_CONTOUR,Nature.VALLEY,Nature.RIDGE]
for i in range(len(natures)):
currentNature = self.current_edge.nature
if (natures[i] & currentNature) != 0:
count=0
while not it.is_end:
visitNext = 0
oNature = it.object.nature
if (oNature & natures[i]) != 0:
if natures[i] != oNature:
for j in range(i):
if (natures[j] & oNature) != 0:
visitNext = 1
break
if visitNext != 0:
break
count = count+1
winner = it.object
it.increment()
if count != 1:
winner = None
break
return winner
## the natural chaining iterator
## It follows the edges of same nature on the same
## objects with preseance on silhouettes, then borders,
## then suggestive contours, then everything else. It doesn't chain the same ViewEdge twice
## You can specify whether to stay in the selection or not.
## You can specify whether to chain iterate over edges that were
## already visited or not.
class pyChainSilhouetteGenericIterator(ChainingIterator):
def __init__(self, stayInSelection=True, stayInUnvisited=True):
ChainingIterator.__init__(self, stayInSelection, stayInUnvisited, None, True)
def init(self):
pass
def traverse(self, iter):
winner = None
it = AdjacencyIterator(iter)
tvertex = self.next_vertex
if type(tvertex) is TVertex:
mateVE = tvertex.get_mate(self.current_edge)
while not it.is_end:
ve = it.object
if ve.id == mateVE.id:
winner = ve
break
it.increment()
else:
## case of NonTVertex
natures = [Nature.SILHOUETTE,Nature.BORDER,Nature.CREASE,Nature.SUGGESTIVE_CONTOUR,Nature.VALLEY,Nature.RIDGE]
for i in range(len(natures)):
currentNature = self.current_edge.nature
if (natures[i] & currentNature) != 0:
count=0
while not it.is_end:
visitNext = 0
oNature = it.object.nature
ve = it.object
if ve.id == self.current_edge.id:
it.increment()
continue
if (oNature & natures[i]) != 0:
if natures[i] != oNature:
for j in range(i):
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):
def __init__(self):
ChainingIterator.__init__(self, False, True, None, True)
self._isExternalContour = ExternalContourUP1D()
def init(self):
self._nEdges = 0
self._isInSelection = 1
def checkViewEdge(self, ve, orientation):
if orientation != 0:
vertex = ve.second_svertex()
else:
vertex = ve.first_svertex()
it = AdjacencyIterator(vertex,1,1)
while not it.is_end:
ave = it.object
if self._isExternalContour(ave):
return 1
it.increment()
print("pyExternlContourChainingIterator : didn't find next edge")
return 0
def traverse(self, iter):
winner = None
it = AdjacencyIterator(iter)
while not it.is_end:
ve = it.object
if self._isExternalContour(ve):
if ve.time_stamp == CF.get_time_stamp():
winner = ve
it.increment()
self._nEdges = self._nEdges+1
if winner is None:
orient = 1
it = AdjacencyIterator(iter)
while not it.is_end:
ve = it.object
if it.is_incoming:
orient = 0
good = self.checkViewEdge(ve,orient)
if good != 0:
winner = ve
it.increment()
return winner
## the natural chaining iterator
## with a sketchy multiple touch
class pySketchyChainSilhouetteIterator(ChainingIterator):
def __init__(self, nRounds=3,stayInSelection=True):
ChainingIterator.__init__(self, stayInSelection, False, None, True)
self._timeStamp = CF.get_time_stamp()+nRounds
self._nRounds = nRounds
def init(self):
self._timeStamp = CF.get_time_stamp()+self._nRounds
def traverse(self, iter):
winner = None
it = AdjacencyIterator(iter)
tvertex = self.next_vertex
if type(tvertex) is TVertex:
mateVE = tvertex.get_mate(self.current_edge)
while not it.is_end:
ve = it.object
if ve.id == mateVE.id:
winner = ve
break
it.increment()
else:
## case of NonTVertex
natures = [Nature.SILHOUETTE,Nature.BORDER,Nature.CREASE,Nature.SUGGESTIVE_CONTOUR,Nature.VALLEY,Nature.RIDGE]
for i in range(len(natures)):
currentNature = self.current_edge.nature
if (natures[i] & currentNature) != 0:
count=0
while not it.is_end:
visitNext = 0
oNature = it.object.nature
ve = it.object
if ve.id == self.current_edge.id:
it.increment()
continue
if (oNature & natures[i]) != 0:
if (natures[i] != oNature) != 0:
for j in range(i):
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
if winner is None:
winner = self.current_edge
if winner.chaining_time_stamp == self._timeStamp:
winner = None
return winner
# Chaining iterator designed for sketchy style.
# can chain several times the same ViewEdge
# in order to produce multiple strokes per ViewEdge.
class pySketchyChainingIterator(ChainingIterator):
def __init__(self, nRounds=3, stayInSelection=True):
ChainingIterator.__init__(self, stayInSelection, False, None, True)
self._timeStamp = CF.get_time_stamp()+nRounds
self._nRounds = nRounds
def init(self):
self._timeStamp = CF.get_time_stamp()+self._nRounds
def traverse(self, iter):
winner = None
it = AdjacencyIterator(iter)
while not it.is_end:
ve = it.object
if ve.id == self.current_edge.id:
it.increment()
continue
winner = ve
it.increment()
if winner is None:
winner = self.current_edge
if winner.chaining_time_stamp == self._timeStamp:
return None
return winner
## Chaining iterator that fills small occlusions
## percent
## The max length of the occluded part
## expressed in % of the total chain length
class pyFillOcclusionsRelativeChainingIterator(ChainingIterator):
def __init__(self, percent):
ChainingIterator.__init__(self, False, True, None, True)
self._length = 0
self._percent = float(percent)
def init(self):
# each time we're evaluating a chain length
# we try to do it once. Thus we reinit
# the chain length here:
self._length = 0
def traverse(self, iter):
winner = None
winnerOrientation = 0
print(self.current_edge.id.first, self.current_edge.id.second)
it = AdjacencyIterator(iter)
tvertex = self.next_vertex
if type(tvertex) is TVertex:
mateVE = tvertex.get_mate(self.current_edge)
while not it.is_end:
ve = it.object
if ve.id == mateVE.id:
winner = ve
if not it.is_incoming:
winnerOrientation = 1
else:
winnerOrientation = 0
break
it.increment()
else:
## case of NonTVertex
natures = [Nature.SILHOUETTE,Nature.BORDER,Nature.CREASE,Nature.SUGGESTIVE_CONTOUR,Nature.VALLEY,Nature.RIDGE]
for nat in natures:
if (self.current_edge.nature & nat) != 0:
count=0
while not it.is_end:
ve = it.object
if (ve.nature & nat) != 0:
count = count+1
winner = ve
if not it.is_incoming:
winnerOrientation = 1
else:
winnerOrientation = 0
it.increment()
if count != 1:
winner = None
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(0,0)
_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(0,0)
_cit.begin = winner
_cit.current_edge = winner
_cit.orientation = winnerOrientation
_cit.init()
while _cit.is_end == 0 and _cit.object.time_stamp != CF.get_time_stamp():
ve = _cit.object
#print("-------- --------", ve.id.first, ve.id.second)
connexl = connexl + ve.length_2d
_cit.increment()
if connexl > self._percent * self._length:
winner = None
return winner
## Chaining iterator that fills small occlusions
## size
## The max length of the occluded part
## expressed in pixels
class pyFillOcclusionsAbsoluteChainingIterator(ChainingIterator):
def __init__(self, length):
ChainingIterator.__init__(self, False, True, None, True)
self._length = float(length)
def init(self):
pass
def traverse(self, iter):
winner = None
winnerOrientation = 0
#print(self.current_edge.id.first, self.current_edge.id.second)
it = AdjacencyIterator(iter)
tvertex = self.next_vertex
if type(tvertex) is TVertex:
mateVE = tvertex.get_mate(self.current_edge)
while not it.is_end:
ve = it.object
if ve.id == mateVE.id:
winner = ve
if not it.is_incoming:
winnerOrientation = 1
else:
winnerOrientation = 0
break
it.increment()
else:
## case of NonTVertex
natures = [Nature.SILHOUETTE,Nature.BORDER,Nature.CREASE,Nature.SUGGESTIVE_CONTOUR,Nature.VALLEY,Nature.RIDGE]
for nat in natures:
if (self.current_edge.nature & nat) != 0:
count=0
while not it.is_end:
ve = it.object
if (ve.nature & nat) != 0:
count = count+1
winner = ve
if not it.is_incoming:
winnerOrientation = 1
else:
winnerOrientation = 0
it.increment()
if count != 1:
winner = None
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)
# nw let's compute the length of this connex non selected part:
connexl = 0
_cit = pyChainSilhouetteGenericIterator(0,0)
_cit.begin = winner
_cit.current_edge = winner
_cit.orientation = winnerOrientation
_cit.init()
while _cit.is_end == 0 and _cit.object.time_stamp != CF.get_time_stamp():
ve = _cit.object
#print("-------- --------", ve.id.first, ve.id.second)
connexl = connexl + ve.length_2d
_cit.increment()
if connexl > self._length:
winner = None
return winner
## Chaining iterator that fills small occlusions
## percent
## The max length of the occluded part
## expressed in % of the total chain length
class pyFillOcclusionsAbsoluteAndRelativeChainingIterator(ChainingIterator):
def __init__(self, percent, l):
ChainingIterator.__init__(self, False, True, None, True)
self._length = 0
self._absLength = l
self._percent = float(percent)
def init(self):
# each time we're evaluating a chain length
# we try to do it once. Thus we reinit
# the chain length here:
self._length = 0
def traverse(self, iter):
winner = None
winnerOrientation = 0
print(self.current_edge.id.first, self.current_edge.id.second)
it = AdjacencyIterator(iter)
tvertex = self.next_vertex
if type(tvertex) is TVertex:
mateVE = tvertex.get_mate(self.current_edge)
while not it.is_end:
ve = it.object
if ve.id == mateVE.id:
winner = ve
if not it.is_incoming:
winnerOrientation = 1
else:
winnerOrientation = 0
break
it.increment()
else:
## case of NonTVertex
natures = [Nature.SILHOUETTE,Nature.BORDER,Nature.CREASE,Nature.SUGGESTIVE_CONTOUR,Nature.VALLEY,Nature.RIDGE]
for nat in natures:
if (self.current_edge.nature & nat) != 0:
count=0
while not it.is_end:
ve = it.object
if (ve.nature & nat) != 0:
count = count+1
winner = ve
if not it.is_incoming:
winnerOrientation = 1
else:
winnerOrientation = 0
it.increment()
if count != 1:
winner = None
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(0,0)
_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(0,0)
_cit.begin = winner
_cit.current_edge = winner
_cit.orientation = winnerOrientation
_cit.init()
while _cit.is_end == 0 and _cit.object.time_stamp != CF.get_time_stamp():
ve = _cit.object
#print("-------- --------", ve.id.first, ve.id.second)
connexl = connexl + ve.length_2d
_cit.increment()
if (connexl > self._percent * self._length) or (connexl > self._absLength):
winner = None
return winner
## Chaining iterator that fills small occlusions without caring about the
## actual selection
## percent
## The max length of the occluded part
## expressed in % of the total chain length
class pyFillQi0AbsoluteAndRelativeChainingIterator(ChainingIterator):
def __init__(self, percent, l):
ChainingIterator.__init__(self, False, True, None, True)
self._length = 0
self._absLength = l
self._percent = float(percent)
def init(self):
# each time we're evaluating a chain length
# we try to do it once. Thus we reinit
# the chain length here:
self._length = 0
def traverse(self, iter):
winner = None
winnerOrientation = 0
print(self.current_edge.id.first, self.current_edge.id.second)
it = AdjacencyIterator(iter)
tvertex = self.next_vertex
if type(tvertex) is TVertex:
mateVE = tvertex.get_mate(self.current_edge)
while not it.is_end:
ve = it.object
if ve.id == mateVE.id:
winner = ve
if not it.is_incoming:
winnerOrientation = 1
else:
winnerOrientation = 0
break
it.increment()
else:
## case of NonTVertex
natures = [Nature.SILHOUETTE,Nature.BORDER,Nature.CREASE,Nature.SUGGESTIVE_CONTOUR,Nature.VALLEY,Nature.RIDGE]
for nat in natures:
if (self.current_edge.nature & nat) != 0:
count=0
while not it.is_end:
ve = it.object
if (ve.nature & nat) != 0:
count = count+1
winner = ve
if not it.is_incoming:
winnerOrientation = 1
else:
winnerOrientation = 0
it.increment()
if count != 1:
winner = None
break
if winner is not None:
# check whether this edge was part of the selection
if winner.qi != 0:
#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(0,0)
_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(0,0)
_cit.begin = winner
_cit.current_edge = winner
_cit.orientation = winnerOrientation
_cit.init()
while not _cit.is_end and _cit.object.qi != 0:
ve = _cit.object
#print("-------- --------", ve.id.first, ve.id.second)
connexl = connexl + ve.length_2d
_cit.increment()
if (connexl > self._percent * self._length) or (connexl > self._absLength):
winner = None
return winner
## the natural chaining iterator
## It follows the edges of same nature on the same
## objects with preseance on silhouettes, then borders,
## then suggestive contours, then everything else. It doesn't chain the same ViewEdge twice
## You can specify whether to stay in the selection or not.
class pyNoIdChainSilhouetteIterator(ChainingIterator):
def __init__(self, stayInSelection=True):
ChainingIterator.__init__(self, stayInSelection, True, None, True)
def init(self):
pass
def traverse(self, iter):
winner = None
it = AdjacencyIterator(iter)
tvertex = self.next_vertex
if type(tvertex) is TVertex:
mateVE = tvertex.get_mate(self.current_edge)
while not it.is_end:
ve = it.object
feB = self.current_edge.last_fedge
feA = ve.first_fedge
vB = feB.second_svertex
vA = feA.first_svertex
if vA.id.first == vB.id.first:
winner = ve
break
feA = self.current_edge.first_fedge
feB = ve.last_fedge
vB = feB.second_svertex
vA = feA.first_svertex
if vA.id.first == vB.id.first:
winner = ve
break
feA = self.current_edge.last_fedge
feB = ve.last_fedge
vB = feB.second_svertex
vA = feA.second_svertex
if vA.id.first == vB.id.first:
winner = ve
break
feA = self.current_edge.first_fedge
feB = ve.first_fedge
vB = feB.first_svertex
vA = feA.first_svertex
if vA.id.first == vB.id.first:
winner = ve
break
it.increment()
else:
## case of NonTVertex
natures = [Nature.SILHOUETTE,Nature.BORDER,Nature.CREASE,Nature.SUGGESTIVE_CONTOUR,Nature.VALLEY,Nature.RIDGE]
for i in range(len(natures)):
currentNature = self.current_edge.nature
if (natures[i] & currentNature) != 0:
count=0
while not it.is_end:
visitNext = 0
oNature = it.object.nature
if (oNature & natures[i]) != 0:
if natures[i] != oNature:
for j in range(i):
if (natures[j] & oNature) != 0:
visitNext = 1
break
if visitNext != 0:
break
count = count+1
winner = it.object
it.increment()
if count != 1:
winner = None
break
return winner