D319: Freestyle Python scripts update.
This revision is meant to update Freestyle's Python scripts to make full usage of the new features of Python and Freestyle's Python API. Freestyle's Python scripts are pretty old already, and were never given much attention. With the 2.7x generation of Blender coming up, this is an excellent time to update Freestyle's Python scripts, hopefully adding some new features and achieving some speed improvements on the way. Main goals: * use for loops where possible * general cleanup, making use of more recent python features (generators, ternary operator, ect.) * update the documentation on the way (it's lacking atm) Differential revision: https://developer.blender.org/D319 Author: flokkievids (Folkert de Vries) Reviewed by: kjym3 (Tamito Kajiyama)
This commit is contained in:
@@ -23,6 +23,20 @@ rules. Also intended to be a collection of examples for defining
|
|||||||
chaining iterators in Python
|
chaining iterators in Python
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
__all__ = (
|
||||||
|
"pyChainSilhouetteIterator",
|
||||||
|
"pyChainSilhouetteGenericIterator",
|
||||||
|
"pyExternalContourChainingIterator",
|
||||||
|
"pySketchyChainSilhouetteIterator",
|
||||||
|
"pySketchyChainingIterator",
|
||||||
|
"pyFillOcclusionsRelativeChainingIterator",
|
||||||
|
"pyFillOcclusionsAbsoluteChainingIterator",
|
||||||
|
"pyFillOcclusionsAbsoluteAndRelativeChainingIterator",
|
||||||
|
"pyFillQi0AbsoluteAndRelativeChainingIterator",
|
||||||
|
"pyNoIdChainSilhouetteIterator",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# module members
|
# module members
|
||||||
from _freestyle import (
|
from _freestyle import (
|
||||||
ChainPredicateIterator,
|
ChainPredicateIterator,
|
||||||
@@ -41,11 +55,30 @@ from freestyle.predicates import (
|
|||||||
)
|
)
|
||||||
from freestyle.utils import (
|
from freestyle.utils import (
|
||||||
ContextFunctions as CF,
|
ContextFunctions as CF,
|
||||||
stroke_normal,
|
get_chain_length,
|
||||||
|
find_matching_vertex,
|
||||||
)
|
)
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
|
|
||||||
|
|
||||||
|
NATURES = (
|
||||||
|
Nature.SILHOUETTE,
|
||||||
|
Nature.BORDER,
|
||||||
|
Nature.CREASE,
|
||||||
|
Nature.MATERIAL_BOUNDARY,
|
||||||
|
Nature.EDGE_MARK,
|
||||||
|
Nature.SUGGESTIVE_CONTOUR,
|
||||||
|
Nature.VALLEY,
|
||||||
|
Nature.RIDGE
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def nature_in_preceding(nature, index):
|
||||||
|
""" Returns True if given nature appears before index, else False """
|
||||||
|
return any(nature & nat for nat in NATURES[:index])
|
||||||
|
|
||||||
|
|
||||||
class pyChainSilhouetteIterator(ChainingIterator):
|
class pyChainSilhouetteIterator(ChainingIterator):
|
||||||
"""Natural chaining iterator
|
"""Natural chaining iterator
|
||||||
|
|
||||||
@@ -61,43 +94,28 @@ class pyChainSilhouetteIterator(ChainingIterator):
|
|||||||
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 = vertex.get_mate(self.current_edge)
|
||||||
ve = it.object
|
return find_matching_vertex(mate.id, it)
|
||||||
if ve.id == mateVE.id:
|
## case of NonTVertex
|
||||||
winner = ve
|
winner = None
|
||||||
break
|
for i, nat in enumerate(NATURES):
|
||||||
it.increment()
|
if (nat & self.current_edge.nature):
|
||||||
else:
|
for ve in it:
|
||||||
## case of NonTVertex
|
ve_nat = ve.nature
|
||||||
natures = [Nature.SILHOUETTE,Nature.BORDER,Nature.CREASE,Nature.MATERIAL_BOUNDARY,Nature.EDGE_MARK,
|
if (ve_nat & nat):
|
||||||
Nature.SUGGESTIVE_CONTOUR,Nature.VALLEY,Nature.RIDGE]
|
# search for matches in previous natures. if match -> break
|
||||||
for i in range(len(natures)):
|
if nat != ve_nat and nature_in_preceding(ve_nat, index=i):
|
||||||
currentNature = self.current_edge.nature
|
break
|
||||||
if (natures[i] & currentNature) != 0:
|
# a second match must be an error
|
||||||
count=0
|
if winner is not None:
|
||||||
while not it.is_end:
|
return None
|
||||||
visitNext = 0
|
# assign winner
|
||||||
oNature = it.object.nature
|
winner = ve
|
||||||
if (oNature & natures[i]) != 0:
|
return winner
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
class pyChainSilhouetteGenericIterator(ChainingIterator):
|
class pyChainSilhouetteGenericIterator(ChainingIterator):
|
||||||
@@ -120,47 +138,30 @@ class pyChainSilhouetteGenericIterator(ChainingIterator):
|
|||||||
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 = vertex.get_mate(self.current_edge)
|
||||||
ve = it.object
|
return find_matching_vertex(mate.id, it)
|
||||||
if ve.id == mateVE.id:
|
## case of NonTVertex
|
||||||
winner = ve
|
winner = None
|
||||||
break
|
for i, nat in enumerate(NATURES):
|
||||||
it.increment()
|
if (nat & self.current_edge.nature):
|
||||||
else:
|
for ve in it:
|
||||||
## case of NonTVertex
|
ve_nat = ve.nature
|
||||||
natures = [Nature.SILHOUETTE,Nature.BORDER,Nature.CREASE,Nature.MATERIAL_BOUNDARY,Nature.EDGE_MARK,
|
if ve.id == self.current_edge.id:
|
||||||
Nature.SUGGESTIVE_CONTOUR,Nature.VALLEY,Nature.RIDGE]
|
continue
|
||||||
for i in range(len(natures)):
|
if (ve_nat & nat):
|
||||||
currentNature = self.current_edge.nature
|
if nat != ve_nat and nature_in_preceding(ve_nat, index=i):
|
||||||
if (natures[i] & currentNature) != 0:
|
break
|
||||||
count=0
|
|
||||||
while not it.is_end:
|
if winner is not None:
|
||||||
visitNext = 0
|
return None
|
||||||
oNature = it.object.nature
|
|
||||||
ve = it.object
|
winner = ve
|
||||||
if ve.id == self.current_edge.id:
|
return winner
|
||||||
it.increment()
|
return None
|
||||||
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):
|
class pyExternalContourChainingIterator(ChainingIterator):
|
||||||
@@ -168,49 +169,40 @@ class pyExternalContourChainingIterator(ChainingIterator):
|
|||||||
|
|
||||||
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
|
||||||
self._isInSelection = 1
|
|
||||||
|
|
||||||
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)
|
||||||
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 not result and bpy.app.debug_freestyle:
|
||||||
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
|
||||||
it = AdjacencyIterator(iter)
|
self._nEdges += 1
|
||||||
while not it.is_end:
|
|
||||||
ve = it.object
|
it = AdjacencyIterator(iter)
|
||||||
if self._isExternalContour(ve):
|
time_stamp = CF.get_time_stamp()
|
||||||
if ve.time_stamp == CF.get_time_stamp():
|
|
||||||
winner = ve
|
for ve in it:
|
||||||
it.increment()
|
if self.ExternalContour(ve) and ve.time_stamp == time_stamp:
|
||||||
|
winner = ve
|
||||||
|
|
||||||
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()
|
|
||||||
return winner
|
return winner
|
||||||
|
|
||||||
|
|
||||||
@@ -227,58 +219,49 @@ class pySketchyChainSilhouetteIterator(ChainingIterator):
|
|||||||
|
|
||||||
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 = vertex.get_mate(self.current_edge)
|
||||||
ve = it.object
|
return self.make_sketchy(find_matching_vertex(mate.id, it))
|
||||||
if ve.id == mateVE.id:
|
## case of NonTVertex
|
||||||
winner = ve
|
winner = None
|
||||||
break
|
for i, nat in enumerate(NATURES):
|
||||||
it.increment()
|
if (nat & self.current_edge.nature):
|
||||||
else:
|
for ve in it:
|
||||||
## case of NonTVertex
|
if ve.id == self.current_edge.id:
|
||||||
natures = [Nature.SILHOUETTE,Nature.BORDER,Nature.CREASE,Nature.MATERIAL_BOUNDARY,Nature.EDGE_MARK,
|
continue
|
||||||
Nature.SUGGESTIVE_CONTOUR,Nature.VALLEY,Nature.RIDGE]
|
ve_nat = ve.nature
|
||||||
for i in range(len(natures)):
|
if (ve_nat & nat):
|
||||||
currentNature = self.current_edge.nature
|
if nat != ve_nat and nature_in_preceding(ve_nat, i):
|
||||||
if (natures[i] & currentNature) != 0:
|
break
|
||||||
count=0
|
|
||||||
while not it.is_end:
|
if winner is not None:
|
||||||
visitNext = 0
|
return self.make_sketchy(None)
|
||||||
oNature = it.object.nature
|
|
||||||
ve = it.object
|
winner = ve
|
||||||
if ve.id == self.current_edge.id:
|
break
|
||||||
it.increment()
|
return self.make_sketchy(winner)
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
class pySketchyChainingIterator(ChainingIterator):
|
class pySketchyChainingIterator(ChainingIterator):
|
||||||
@@ -289,30 +272,30 @@ class pySketchyChainingIterator(ChainingIterator):
|
|||||||
"""
|
"""
|
||||||
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
|
||||||
|
self.t = False
|
||||||
|
|
||||||
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)
|
|
||||||
while not it.is_end:
|
for ve in AdjacencyIterator(iter):
|
||||||
ve = it.object
|
if self.current_edge.id == ve.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:
|
||||||
@@ -330,97 +313,61 @@ class pyFillOcclusionsRelativeChainingIterator(ChainingIterator):
|
|||||||
|
|
||||||
def __init__(self, percent):
|
def __init__(self, percent):
|
||||||
ChainingIterator.__init__(self, False, True, None, True)
|
ChainingIterator.__init__(self, False, True, None, True)
|
||||||
self._length = 0
|
self._length = 0.0
|
||||||
self._percent = float(percent)
|
self._percent = float(percent)
|
||||||
|
self.timestamp = CF.get_time_stamp()
|
||||||
|
|
||||||
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 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 = vertex.get_mate(self.current_edge)
|
||||||
ve = it.object
|
winner = find_matching_vertex(mate.id, it)
|
||||||
if ve.id == mateVE.id:
|
winnerOrientation = not it.is_incoming if not it.is_end else False
|
||||||
winner = ve
|
## case of NonTVertex
|
||||||
winnerOrientation = not it.is_incoming
|
|
||||||
break
|
|
||||||
it.increment()
|
|
||||||
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:
|
# check timestamp to see if this edge was part of the selection
|
||||||
# nw let's compute the length of this connex non selected part:
|
if winner is not None and winner.time_stamp != self.timestamp:
|
||||||
connexl = 0
|
# if the edge wasn't part of the selection, let's see
|
||||||
|
# whether it's short enough (with respect to self.percent)
|
||||||
|
# to be included.
|
||||||
|
if self._length == 0.0:
|
||||||
|
self._length = get_chain_length(winner, winnerOrientation)
|
||||||
|
|
||||||
|
# check if the gap can be bridged
|
||||||
|
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 != self.timestamp:
|
||||||
|
connexl += _cit.object.length_2d
|
||||||
|
_cit.increment()
|
||||||
|
if _cit.is_begin: break
|
||||||
|
|
||||||
|
if connexl > self._percent * self._length:
|
||||||
|
return None
|
||||||
|
|
||||||
_cit = pyChainSilhouetteGenericIterator(False, False)
|
|
||||||
_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
|
return winner
|
||||||
|
|
||||||
|
|
||||||
@@ -433,6 +380,7 @@ class pyFillOcclusionsAbsoluteChainingIterator(ChainingIterator):
|
|||||||
def __init__(self, length):
|
def __init__(self, length):
|
||||||
ChainingIterator.__init__(self, False, True, None, True)
|
ChainingIterator.__init__(self, False, True, None, True)
|
||||||
self._length = float(length)
|
self._length = float(length)
|
||||||
|
self.timestamp = CF.get_time_stamp()
|
||||||
|
|
||||||
def init(self):
|
def init(self):
|
||||||
pass
|
pass
|
||||||
@@ -440,53 +388,41 @@ class pyFillOcclusionsAbsoluteChainingIterator(ChainingIterator):
|
|||||||
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 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 = vertex.get_mate(self.current_edge)
|
||||||
ve = it.object
|
winner = find_matching_vertex(mate.id, it)
|
||||||
if ve.id == mateVE.id:
|
winnerOrientation = not it.is_incoming if not it.is_end else False
|
||||||
winner = ve
|
## case of NonTVertex
|
||||||
winnerOrientation = not it.is_incoming
|
|
||||||
break
|
|
||||||
it.increment()
|
|
||||||
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 != self.timestamp:
|
||||||
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 != self.timestamp:
|
||||||
_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
|
if _cit.is_begin: break
|
||||||
#print("-------- --------", ve.id.first, ve.id.second)
|
|
||||||
connexl = connexl + ve.length_2d
|
if connexl > self._length:
|
||||||
_cit.increment()
|
return None
|
||||||
if connexl > self._length:
|
|
||||||
winner = None
|
|
||||||
return winner
|
return winner
|
||||||
|
|
||||||
|
|
||||||
@@ -500,7 +436,7 @@ class pyFillOcclusionsAbsoluteAndRelativeChainingIterator(ChainingIterator):
|
|||||||
"""
|
"""
|
||||||
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)
|
||||||
|
|
||||||
@@ -508,88 +444,48 @@ class pyFillOcclusionsAbsoluteAndRelativeChainingIterator(ChainingIterator):
|
|||||||
# 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 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 = vertex.get_mate(self.current_edge)
|
||||||
ve = it.object
|
winner = find_matching_vertex(mate.id, it)
|
||||||
if ve.id == mateVE.id:
|
winnerOrientation = not it.is_incoming if not it.is_end else False
|
||||||
winner = ve
|
## case of NonTVertex
|
||||||
winnerOrientation = not it.is_incoming
|
|
||||||
break
|
|
||||||
it.increment()
|
|
||||||
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:
|
if winner is not None and winner.time_stamp != CF.get_time_stamp():
|
||||||
# nw let's compute the length of this connex non selected part:
|
|
||||||
connexl = 0
|
if self._length == 0.0:
|
||||||
|
self._length = get_chain_length(winner, winnerOrientation)
|
||||||
|
|
||||||
|
connexl = 0.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 _cit.is_begin: break
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
|
|
||||||
@@ -603,96 +499,55 @@ class pyFillQi0AbsoluteAndRelativeChainingIterator(ChainingIterator):
|
|||||||
"""
|
"""
|
||||||
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 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 = vertex.get_mate(self.current_edge)
|
||||||
ve = it.object
|
winner = find_matching_vertex(mate.id, it)
|
||||||
if ve.id == mateVE.id:
|
winnerOrientation = not it.is_incoming if not it.is_end else False
|
||||||
winner = ve
|
## case of NonTVertex
|
||||||
winnerOrientation = not it.is_incoming
|
|
||||||
break
|
|
||||||
it.increment()
|
|
||||||
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.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(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:
|
if winner is not None and winner.qi:
|
||||||
# nw let's compute the length of this connex non selected part:
|
|
||||||
|
|
||||||
|
if self._length == 0.0:
|
||||||
|
self._length = get_chain_length(winner, winnerOrientation)
|
||||||
|
|
||||||
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 != 0:
|
||||||
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 _cit.is_begin: break
|
||||||
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
|
||||||
|
|
||||||
|
|
||||||
@@ -717,63 +572,44 @@ class pyNoIdChainSilhouetteIterator(ChainingIterator):
|
|||||||
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
|
return None
|
||||||
it.increment()
|
## case of NonTVertex
|
||||||
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:
|
return None
|
||||||
visitNext = 1
|
|
||||||
break
|
|
||||||
if visitNext != 0:
|
|
||||||
break
|
|
||||||
count = count+1
|
|
||||||
winner = it.object
|
|
||||||
it.increment()
|
|
||||||
if count != 1:
|
|
||||||
winner = None
|
|
||||||
break
|
|
||||||
return winner
|
|
||||||
|
@@ -91,8 +91,8 @@ from freestyle.utils import integrate
|
|||||||
|
|
||||||
from mathutils import Vector
|
from mathutils import Vector
|
||||||
|
|
||||||
## Functions for 0D elements (vertices)
|
|
||||||
#######################################
|
# -- Functions for 0D elements (vertices) -- #
|
||||||
|
|
||||||
|
|
||||||
class CurveMaterialF0D(UnaryFunction0DMaterial):
|
class CurveMaterialF0D(UnaryFunction0DMaterial):
|
||||||
@@ -104,7 +104,7 @@ class CurveMaterialF0D(UnaryFunction0DMaterial):
|
|||||||
cp = inter.object
|
cp = inter.object
|
||||||
assert(isinstance(cp, CurvePoint))
|
assert(isinstance(cp, CurvePoint))
|
||||||
fe = cp.first_svertex.get_fedge(cp.second_svertex)
|
fe = cp.first_svertex.get_fedge(cp.second_svertex)
|
||||||
assert(fe is not None)
|
assert(fe is not None), "CurveMaterialF0D: fe is None"
|
||||||
return fe.material if fe.is_smooth else fe.material_left
|
return fe.material if fe.is_smooth else fe.material_left
|
||||||
|
|
||||||
|
|
||||||
@@ -140,11 +140,7 @@ class pyDensityAnisotropyF0D(UnaryFunction0DDouble):
|
|||||||
c_3 = self.d3Density(inter)
|
c_3 = self.d3Density(inter)
|
||||||
cMax = max(max(c_0,c_1), max(c_2,c_3))
|
cMax = max(max(c_0,c_1), max(c_2,c_3))
|
||||||
cMin = min(min(c_0,c_1), min(c_2,c_3))
|
cMin = min(min(c_0,c_1), min(c_2,c_3))
|
||||||
if c_iso == 0:
|
return 0 if (c_iso == 0) else (cMax-cMin) / c_iso
|
||||||
v = 0
|
|
||||||
else:
|
|
||||||
v = (cMax-cMin)/c_iso
|
|
||||||
return v
|
|
||||||
|
|
||||||
|
|
||||||
class pyViewMapGradientVectorF0D(UnaryFunction0DVec2f):
|
class pyViewMapGradientVectorF0D(UnaryFunction0DVec2f):
|
||||||
@@ -161,9 +157,9 @@ class pyViewMapGradientVectorF0D(UnaryFunction0DVec2f):
|
|||||||
def __call__(self, iter):
|
def __call__(self, iter):
|
||||||
p = iter.object.point_2d
|
p = iter.object.point_2d
|
||||||
gx = CF.read_complete_view_map_pixel(self._l, int(p.x+self._step), int(p.y)) - \
|
gx = CF.read_complete_view_map_pixel(self._l, int(p.x+self._step), int(p.y)) - \
|
||||||
CF.read_complete_view_map_pixel(self._l, int(p.x), int(p.y))
|
CF.read_complete_view_map_pixel(self._l, int(p.x), int(p.y))
|
||||||
gy = CF.read_complete_view_map_pixel(self._l, int(p.x), int(p.y+self._step)) - \
|
gy = CF.read_complete_view_map_pixel(self._l, int(p.x), int(p.y+self._step)) - \
|
||||||
CF.read_complete_view_map_pixel(self._l, int(p.x), int(p.y))
|
CF.read_complete_view_map_pixel(self._l, int(p.x), int(p.y))
|
||||||
return Vector((gx, gy))
|
return Vector((gx, gy))
|
||||||
|
|
||||||
|
|
||||||
@@ -171,19 +167,18 @@ class pyViewMapGradientNormF0D(UnaryFunction0DDouble):
|
|||||||
def __init__(self, l):
|
def __init__(self, l):
|
||||||
UnaryFunction0DDouble.__init__(self)
|
UnaryFunction0DDouble.__init__(self)
|
||||||
self._l = l
|
self._l = l
|
||||||
self._step = pow(2,self._l)
|
self._step = pow(2, self._l)
|
||||||
|
|
||||||
def __call__(self, iter):
|
def __call__(self, iter):
|
||||||
p = iter.object.point_2d
|
p = iter.object.point_2d
|
||||||
gx = CF.read_complete_view_map_pixel(self._l, int(p.x+self._step), int(p.y)) - \
|
gx = CF.read_complete_view_map_pixel(self._l, int(p.x + self._step), int(p.y)) - \
|
||||||
CF.read_complete_view_map_pixel(self._l, int(p.x), int(p.y))
|
CF.read_complete_view_map_pixel(self._l, int(p.x), int(p.y))
|
||||||
gy = CF.read_complete_view_map_pixel(self._l, int(p.x), int(p.y+self._step)) - \
|
gy = CF.read_complete_view_map_pixel(self._l, int(p.x), int(p.y + self._step)) - \
|
||||||
CF.read_complete_view_map_pixel(self._l, int(p.x), int(p.y))
|
CF.read_complete_view_map_pixel(self._l, int(p.x), int(p.y))
|
||||||
grad = Vector((gx, gy))
|
return Vector((gx, gy)).length
|
||||||
return grad.length
|
|
||||||
|
|
||||||
## Functions for 1D elements (curves)
|
|
||||||
#####################################
|
# -- Functions for 1D elements (curves) -- #
|
||||||
|
|
||||||
|
|
||||||
class pyGetInverseProjectedZF1D(UnaryFunction1DDouble):
|
class pyGetInverseProjectedZF1D(UnaryFunction1DDouble):
|
||||||
|
@@ -51,6 +51,7 @@ from freestyle.types import (
|
|||||||
TVertex,
|
TVertex,
|
||||||
UnaryPredicate0D,
|
UnaryPredicate0D,
|
||||||
UnaryPredicate1D,
|
UnaryPredicate1D,
|
||||||
|
Id,
|
||||||
)
|
)
|
||||||
from freestyle.functions import (
|
from freestyle.functions import (
|
||||||
Curvature2DAngleF0D,
|
Curvature2DAngleF0D,
|
||||||
@@ -70,14 +71,15 @@ from freestyle.functions import (
|
|||||||
pyDensityAnisotropyF1D,
|
pyDensityAnisotropyF1D,
|
||||||
pyViewMapGradientNormF1D,
|
pyViewMapGradientNormF1D,
|
||||||
)
|
)
|
||||||
|
|
||||||
import random
|
import random
|
||||||
|
|
||||||
|
|
||||||
## Unary predicates for 0D elements (vertices)
|
# -- Unary predicates for 0D elements (vertices) -- #
|
||||||
##############################################
|
|
||||||
|
|
||||||
class pyHigherCurvature2DAngleUP0D(UnaryPredicate0D):
|
class pyHigherCurvature2DAngleUP0D(UnaryPredicate0D):
|
||||||
def __init__(self,a):
|
def __init__(self, a):
|
||||||
UnaryPredicate0D.__init__(self)
|
UnaryPredicate0D.__init__(self)
|
||||||
self._a = a
|
self._a = a
|
||||||
|
|
||||||
@@ -88,15 +90,15 @@ class pyHigherCurvature2DAngleUP0D(UnaryPredicate0D):
|
|||||||
|
|
||||||
|
|
||||||
class pyUEqualsUP0D(UnaryPredicate0D):
|
class pyUEqualsUP0D(UnaryPredicate0D):
|
||||||
def __init__(self,u, w):
|
def __init__(self, u, w):
|
||||||
UnaryPredicate0D.__init__(self)
|
UnaryPredicate0D.__init__(self)
|
||||||
self._u = u
|
self._u = u
|
||||||
self._w = w
|
self._w = w
|
||||||
|
self._func = pyCurvilinearLengthF0D()
|
||||||
|
|
||||||
def __call__(self, inter):
|
def __call__(self, inter):
|
||||||
func = pyCurvilinearLengthF0D()
|
u = self._func(inter)
|
||||||
u = func(inter)
|
return (u > (self._u - self._w)) and (u < (self._u + self._w))
|
||||||
return (u > (self._u-self._w)) and (u < (self._u+self._w))
|
|
||||||
|
|
||||||
|
|
||||||
class pyVertexNatureUP0D(UnaryPredicate0D):
|
class pyVertexNatureUP0D(UnaryPredicate0D):
|
||||||
@@ -105,26 +107,23 @@ class pyVertexNatureUP0D(UnaryPredicate0D):
|
|||||||
self._nature = nature
|
self._nature = nature
|
||||||
|
|
||||||
def __call__(self, inter):
|
def __call__(self, inter):
|
||||||
v = inter.object
|
return bool(inter.object.nature & self._nature)
|
||||||
return (v.nature & self._nature) != 0
|
|
||||||
|
|
||||||
|
|
||||||
## check whether an Interface0DIterator
|
|
||||||
## is a TVertex and is the one that is
|
|
||||||
## hidden (inferred from the context)
|
|
||||||
class pyBackTVertexUP0D(UnaryPredicate0D):
|
class pyBackTVertexUP0D(UnaryPredicate0D):
|
||||||
|
"""
|
||||||
|
Check whether an Interface0DIterator
|
||||||
|
references a TVertex and is the one that is
|
||||||
|
hidden (inferred from the context)
|
||||||
|
"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
UnaryPredicate0D.__init__(self)
|
UnaryPredicate0D.__init__(self)
|
||||||
self._getQI = QuantitativeInvisibilityF0D()
|
self._getQI = QuantitativeInvisibilityF0D()
|
||||||
|
|
||||||
def __call__(self, iter):
|
def __call__(self, iter):
|
||||||
if (iter.object.nature & Nature.T_VERTEX) == 0:
|
if not (iter.object.nature & Nature.T_VERTEX) or iter.is_end:
|
||||||
return False
|
return False
|
||||||
if iter.is_end:
|
return self._getQI(iter) != 0
|
||||||
return False
|
|
||||||
if self._getQI(iter) != 0:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
class pyParameterUP0DGoodOne(UnaryPredicate0D):
|
class pyParameterUP0DGoodOne(UnaryPredicate0D):
|
||||||
@@ -135,7 +134,7 @@ class pyParameterUP0DGoodOne(UnaryPredicate0D):
|
|||||||
|
|
||||||
def __call__(self, inter):
|
def __call__(self, inter):
|
||||||
u = inter.u
|
u = inter.u
|
||||||
return ((u>=self._m) and (u<=self._M))
|
return ((u >= self._m) and (u <= self._M))
|
||||||
|
|
||||||
|
|
||||||
class pyParameterUP0D(UnaryPredicate0D):
|
class pyParameterUP0D(UnaryPredicate0D):
|
||||||
@@ -143,36 +142,39 @@ class pyParameterUP0D(UnaryPredicate0D):
|
|||||||
UnaryPredicate0D.__init__(self)
|
UnaryPredicate0D.__init__(self)
|
||||||
self._m = pmin
|
self._m = pmin
|
||||||
self._M = pmax
|
self._M = pmax
|
||||||
|
self._func = Curvature2DAngleF0D()
|
||||||
|
|
||||||
def __call__(self, inter):
|
def __call__(self, inter):
|
||||||
func = Curvature2DAngleF0D()
|
c = self._func(inter)
|
||||||
c = func(inter)
|
b1 = (c > 0.1)
|
||||||
b1 = (c>0.1)
|
|
||||||
u = inter.u
|
u = inter.u
|
||||||
b = ((u>=self._m) and (u<=self._M))
|
b = ((u >= self._m) and (u <= self._M))
|
||||||
return b and b1
|
return (b and b1)
|
||||||
|
|
||||||
|
|
||||||
|
# -- Unary predicates for 1D elements (curves) -- #
|
||||||
|
|
||||||
## Unary predicates for 1D elements (curves)
|
|
||||||
############################################
|
|
||||||
|
|
||||||
class AndUP1D(UnaryPredicate1D):
|
class AndUP1D(UnaryPredicate1D):
|
||||||
def __init__(self, pred1, pred2):
|
def __init__(self, *predicates):
|
||||||
UnaryPredicate1D.__init__(self)
|
UnaryPredicate1D.__init__(self)
|
||||||
self.__pred1 = pred1
|
self.predicates = predicates
|
||||||
self.__pred2 = pred2
|
if len(self.predicates) < 2:
|
||||||
|
raise ValueError("Expected two or more UnaryPredicate1D")
|
||||||
|
|
||||||
def __call__(self, inter):
|
def __call__(self, inter):
|
||||||
return self.__pred1(inter) and self.__pred2(inter)
|
return all(pred(inter) for pred in self.predicates)
|
||||||
|
|
||||||
|
|
||||||
class OrUP1D(UnaryPredicate1D):
|
class OrUP1D(UnaryPredicate1D):
|
||||||
def __init__(self, pred1, pred2):
|
def __init__(self, *predicates):
|
||||||
UnaryPredicate1D.__init__(self)
|
UnaryPredicate1D.__init__(self)
|
||||||
self.__pred1 = pred1
|
self.predicates = predicates
|
||||||
self.__pred2 = pred2
|
if len(self.predicates) < 2:
|
||||||
|
raise ValueError("Expected two or more UnaryPredicate1D")
|
||||||
|
|
||||||
def __call__(self, inter):
|
def __call__(self, inter):
|
||||||
return self.__pred1(inter) or self.__pred2(inter)
|
return any(pred(inter) for pred in self.predicates)
|
||||||
|
|
||||||
|
|
||||||
class NotUP1D(UnaryPredicate1D):
|
class NotUP1D(UnaryPredicate1D):
|
||||||
@@ -184,6 +186,29 @@ class NotUP1D(UnaryPredicate1D):
|
|||||||
return not self.__pred(inter)
|
return not self.__pred(inter)
|
||||||
|
|
||||||
|
|
||||||
|
class ObjectNamesUP1D(UnaryPredicate1D):
|
||||||
|
def __init__(self, names, negative=False):
|
||||||
|
UnaryPredicate1D.__init__(self)
|
||||||
|
self._names = names
|
||||||
|
self._negative = negative
|
||||||
|
|
||||||
|
def __call__(self, viewEdge):
|
||||||
|
found = viewEdge.viewshape.name in self._names
|
||||||
|
return found if not self._negative else not found
|
||||||
|
|
||||||
|
|
||||||
|
class QuantitativeInvisibilityRangeUP1D(UnaryPredicate1D):
|
||||||
|
def __init__(self, qi_start, qi_end):
|
||||||
|
UnaryPredicate1D.__init__(self)
|
||||||
|
self.__getQI = QuantitativeInvisibilityF1D()
|
||||||
|
self.__qi_start = qi_start
|
||||||
|
self.__qi_end = qi_end
|
||||||
|
|
||||||
|
def __call__(self, inter):
|
||||||
|
qi = self.__getQI(inter)
|
||||||
|
return (self.__qi_start <= qi <= self.__qi_end)
|
||||||
|
|
||||||
|
|
||||||
class pyNFirstUP1D(UnaryPredicate1D):
|
class pyNFirstUP1D(UnaryPredicate1D):
|
||||||
def __init__(self, n):
|
def __init__(self, n):
|
||||||
UnaryPredicate1D.__init__(self)
|
UnaryPredicate1D.__init__(self)
|
||||||
@@ -191,14 +216,12 @@ class pyNFirstUP1D(UnaryPredicate1D):
|
|||||||
self.__count = 0
|
self.__count = 0
|
||||||
|
|
||||||
def __call__(self, inter):
|
def __call__(self, inter):
|
||||||
self.__count = self.__count + 1
|
self.__count += 1
|
||||||
if self.__count <= self.__n:
|
return (self.__count <= self.__n)
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
class pyHigherLengthUP1D(UnaryPredicate1D):
|
class pyHigherLengthUP1D(UnaryPredicate1D):
|
||||||
def __init__(self,l):
|
def __init__(self, l):
|
||||||
UnaryPredicate1D.__init__(self)
|
UnaryPredicate1D.__init__(self)
|
||||||
self._l = l
|
self._l = l
|
||||||
|
|
||||||
@@ -213,28 +236,20 @@ class pyNatureUP1D(UnaryPredicate1D):
|
|||||||
self._getNature = CurveNatureF1D()
|
self._getNature = CurveNatureF1D()
|
||||||
|
|
||||||
def __call__(self, inter):
|
def __call__(self, inter):
|
||||||
if(self._getNature(inter) & self._nature):
|
return bool(self._getNature(inter) & self._nature)
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
class pyHigherNumberOfTurnsUP1D(UnaryPredicate1D):
|
class pyHigherNumberOfTurnsUP1D(UnaryPredicate1D):
|
||||||
def __init__(self,n,a):
|
def __init__(self, n, a):
|
||||||
UnaryPredicate1D.__init__(self)
|
UnaryPredicate1D.__init__(self)
|
||||||
self._n = n
|
self._n = n
|
||||||
self._a = a
|
self._a = a
|
||||||
|
|
||||||
def __call__(self, inter):
|
def __call__(self, inter):
|
||||||
count = 0
|
|
||||||
func = Curvature2DAngleF0D()
|
func = Curvature2DAngleF0D()
|
||||||
it = inter.vertices_begin()
|
it = inter.vertices_begin()
|
||||||
while not it.is_end:
|
# sum the turns, check against n
|
||||||
if func(it) > self._a:
|
return sum(1 for ve in it if func(it) > self._a) > self._n
|
||||||
count = count+1
|
|
||||||
if count > self._n:
|
|
||||||
return True
|
|
||||||
it.increment()
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
class pyDensityUP1D(UnaryPredicate1D):
|
class pyDensityUP1D(UnaryPredicate1D):
|
||||||
@@ -278,9 +293,7 @@ class pyHighSteerableViewMapDensityUP1D(UnaryPredicate1D):
|
|||||||
def __init__(self, threshold, level, integration=IntegrationType.MEAN):
|
def __init__(self, threshold, level, integration=IntegrationType.MEAN):
|
||||||
UnaryPredicate1D.__init__(self)
|
UnaryPredicate1D.__init__(self)
|
||||||
self._threshold = threshold
|
self._threshold = threshold
|
||||||
self._level = level
|
self._func = GetSteerableViewMapDensityF1D(level, integration)
|
||||||
self._integration = integration
|
|
||||||
self._func = GetSteerableViewMapDensityF1D(self._level, self._integration)
|
|
||||||
|
|
||||||
def __call__(self, inter):
|
def __call__(self, inter):
|
||||||
return (self._func(inter) > self._threshold)
|
return (self._func(inter) > self._threshold)
|
||||||
@@ -290,24 +303,17 @@ class pyHighDirectionalViewMapDensityUP1D(UnaryPredicate1D):
|
|||||||
def __init__(self, threshold, orientation, level, integration=IntegrationType.MEAN, sampling=2.0):
|
def __init__(self, threshold, orientation, level, integration=IntegrationType.MEAN, sampling=2.0):
|
||||||
UnaryPredicate1D.__init__(self)
|
UnaryPredicate1D.__init__(self)
|
||||||
self._threshold = threshold
|
self._threshold = threshold
|
||||||
self._orientation = orientation
|
self._func = GetDirectionalViewMapDensityF1D(orientation, level, integration, sampling)
|
||||||
self._level = level
|
|
||||||
self._integration = integration
|
|
||||||
self._sampling = sampling
|
|
||||||
|
|
||||||
def __call__(self, inter):
|
def __call__(self, inter):
|
||||||
func = GetDirectionalViewMapDensityF1D(self._orientation, self._level, self._integration, self._sampling)
|
return (self.func(inter) > self._threshold)
|
||||||
return (func(inter) > self._threshold)
|
|
||||||
|
|
||||||
|
|
||||||
class pyHighViewMapDensityUP1D(UnaryPredicate1D):
|
class pyHighViewMapDensityUP1D(UnaryPredicate1D):
|
||||||
def __init__(self, threshold, level, integration=IntegrationType.MEAN, sampling=2.0):
|
def __init__(self, threshold, level, integration=IntegrationType.MEAN, sampling=2.0):
|
||||||
UnaryPredicate1D.__init__(self)
|
UnaryPredicate1D.__init__(self)
|
||||||
self._threshold = threshold
|
self._threshold = threshold
|
||||||
self._level = level
|
self._func = GetCompleteViewMapDensityF1D(level, integration, sampling)
|
||||||
self._integration = integration
|
|
||||||
self._sampling = sampling
|
|
||||||
self._func = GetCompleteViewMapDensityF1D(self._level, self._integration, self._sampling) # 2.0 is the smpling
|
|
||||||
|
|
||||||
def __call__(self, inter):
|
def __call__(self, inter):
|
||||||
return (self._func(inter) > self._threshold)
|
return (self._func(inter) > self._threshold)
|
||||||
@@ -316,67 +322,56 @@ class pyHighViewMapDensityUP1D(UnaryPredicate1D):
|
|||||||
class pyDensityFunctorUP1D(UnaryPredicate1D):
|
class pyDensityFunctorUP1D(UnaryPredicate1D):
|
||||||
def __init__(self, wsize, threshold, functor, funcmin=0.0, funcmax=1.0, integration=IntegrationType.MEAN):
|
def __init__(self, wsize, threshold, functor, funcmin=0.0, funcmax=1.0, integration=IntegrationType.MEAN):
|
||||||
UnaryPredicate1D.__init__(self)
|
UnaryPredicate1D.__init__(self)
|
||||||
self._wsize = wsize
|
|
||||||
self._threshold = float(threshold)
|
self._threshold = float(threshold)
|
||||||
self._functor = functor
|
self._functor = functor
|
||||||
self._funcmin = float(funcmin)
|
self._funcmin = float(funcmin)
|
||||||
self._funcmax = float(funcmax)
|
self._funcmax = float(funcmax)
|
||||||
self._integration = integration
|
self._func = DensityF1D(wsize, integration)
|
||||||
|
|
||||||
def __call__(self, inter):
|
def __call__(self, inter):
|
||||||
func = DensityF1D(self._wsize, self._integration)
|
|
||||||
res = self._functor(inter)
|
res = self._functor(inter)
|
||||||
k = (res-self._funcmin)/(self._funcmax-self._funcmin)
|
k = (res - self._funcmin) / (self._funcmax - self._funcmin)
|
||||||
return (func(inter) < (self._threshold * k))
|
return (func(inter) < (self._threshold * k))
|
||||||
|
|
||||||
|
|
||||||
class pyZSmallerUP1D(UnaryPredicate1D):
|
class pyZSmallerUP1D(UnaryPredicate1D):
|
||||||
def __init__(self,z, integration=IntegrationType.MEAN):
|
def __init__(self, z, integration=IntegrationType.MEAN):
|
||||||
UnaryPredicate1D.__init__(self)
|
UnaryPredicate1D.__init__(self)
|
||||||
self._z = z
|
self._z = z
|
||||||
self._integration = integration
|
self.func = GetProjectedZF1D(integration)
|
||||||
|
|
||||||
def __call__(self, inter):
|
def __call__(self, inter):
|
||||||
func = GetProjectedZF1D(self._integration)
|
return (self.func(inter) < self._z)
|
||||||
return (func(inter) < self._z)
|
|
||||||
|
|
||||||
|
|
||||||
class pyIsOccludedByUP1D(UnaryPredicate1D):
|
class pyIsOccludedByUP1D(UnaryPredicate1D):
|
||||||
def __init__(self,id):
|
def __init__(self,id):
|
||||||
UnaryPredicate1D.__init__(self)
|
UnaryPredicate1D.__init__(self)
|
||||||
|
if not isinstance(id, Id):
|
||||||
|
raise TypeError("pyIsOccludedByUP1D expected freestyle.types.Id, not " + type(id).__name__)
|
||||||
self._id = id
|
self._id = id
|
||||||
|
|
||||||
def __call__(self, inter):
|
def __call__(self, inter):
|
||||||
func = GetShapeF1D()
|
shapes = GetShapeF1D()(inter)
|
||||||
shapes = func(inter)
|
if any(s.id == self._id for s in shapes):
|
||||||
for s in shapes:
|
return False
|
||||||
if(s.id == self._id):
|
|
||||||
return False
|
# construct iterators
|
||||||
it = inter.vertices_begin()
|
it = inter.vertices_begin()
|
||||||
itlast = inter.vertices_end()
|
itlast = inter.vertices_end()
|
||||||
itlast.decrement()
|
itlast.decrement()
|
||||||
v = it.object
|
|
||||||
vlast = itlast.object
|
vertex = next(it)
|
||||||
tvertex = v.viewvertex
|
if type(vertex) is TVertex:
|
||||||
if type(tvertex) is TVertex:
|
eit = vertex.edges_begin()
|
||||||
#print("TVertex: [ ", tvertex.id.first, ",", tvertex.id.second," ]")
|
if any(ve.id == self._id for (ve, incoming) in eit):
|
||||||
|
return True
|
||||||
|
|
||||||
|
vertex = next(itlast)
|
||||||
|
if type(vertex) is TVertex:
|
||||||
eit = tvertex.edges_begin()
|
eit = tvertex.edges_begin()
|
||||||
while not eit.is_end:
|
if any(ve.id == self._id for (ve, incoming) in eit):
|
||||||
ve, incoming = eit.object
|
return True
|
||||||
if ve.id == self._id:
|
|
||||||
return True
|
|
||||||
#print("-------", ve.id.first, "-", ve.id.second)
|
|
||||||
eit.increment()
|
|
||||||
tvertex = vlast.viewvertex
|
|
||||||
if type(tvertex) is TVertex:
|
|
||||||
#print("TVertex: [ ", tvertex.id.first, ",", tvertex.id.second," ]")
|
|
||||||
eit = tvertex.edges_begin()
|
|
||||||
while not eit.is_end:
|
|
||||||
ve, incoming = eit.object
|
|
||||||
if ve.id == self._id:
|
|
||||||
return True
|
|
||||||
#print("-------", ve.id.first, "-", ve.id.second)
|
|
||||||
eit.increment()
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
@@ -386,12 +381,8 @@ class pyIsInOccludersListUP1D(UnaryPredicate1D):
|
|||||||
self._id = id
|
self._id = id
|
||||||
|
|
||||||
def __call__(self, inter):
|
def __call__(self, inter):
|
||||||
func = GetOccludersF1D()
|
occluders = GetOccludersF1D()(inter)
|
||||||
occluders = func(inter)
|
return any(a.id == self._id for a in occluders)
|
||||||
for a in occluders:
|
|
||||||
if a.id == self._id:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
class pyIsOccludedByItselfUP1D(UnaryPredicate1D):
|
class pyIsOccludedByItselfUP1D(UnaryPredicate1D):
|
||||||
@@ -403,11 +394,7 @@ class pyIsOccludedByItselfUP1D(UnaryPredicate1D):
|
|||||||
def __call__(self, inter):
|
def __call__(self, inter):
|
||||||
lst1 = self.__func1(inter)
|
lst1 = self.__func1(inter)
|
||||||
lst2 = self.__func2(inter)
|
lst2 = self.__func2(inter)
|
||||||
for vs1 in lst1:
|
return any(vs1.id == vs2.id for vs1 in lst1 for vs2 in lst2)
|
||||||
for vs2 in lst2:
|
|
||||||
if vs1.id == vs2.id:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
class pyIsOccludedByIdListUP1D(UnaryPredicate1D):
|
class pyIsOccludedByIdListUP1D(UnaryPredicate1D):
|
||||||
@@ -417,27 +404,17 @@ class pyIsOccludedByIdListUP1D(UnaryPredicate1D):
|
|||||||
self.__func1 = GetOccludersF1D()
|
self.__func1 = GetOccludersF1D()
|
||||||
|
|
||||||
def __call__(self, inter):
|
def __call__(self, inter):
|
||||||
lst1 = self.__func1(inter)
|
lst1 = self.__func1(inter.object)
|
||||||
for vs1 in lst1:
|
return any(vs1.id == _id for vs1 in lst1 for _id in self._idlist)
|
||||||
for _id in self._idlist:
|
|
||||||
if vs1.id == _id:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
class pyShapeIdListUP1D(UnaryPredicate1D):
|
class pyShapeIdListUP1D(UnaryPredicate1D):
|
||||||
def __init__(self,idlist):
|
def __init__(self,idlist):
|
||||||
UnaryPredicate1D.__init__(self)
|
UnaryPredicate1D.__init__(self)
|
||||||
self._idlist = idlist
|
self._funcs = tuple(ShapeUP1D(_id, 0) for _id in idlist)
|
||||||
self._funcs = []
|
|
||||||
for _id in idlist:
|
|
||||||
self._funcs.append(ShapeUP1D(_id.first, _id.second))
|
|
||||||
|
|
||||||
def __call__(self, inter):
|
def __call__(self, inter):
|
||||||
for func in self._funcs:
|
return any(func(inter) for func in self._funcs)
|
||||||
if func(inter) == 1:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
## deprecated
|
## deprecated
|
||||||
@@ -447,12 +424,8 @@ class pyShapeIdUP1D(UnaryPredicate1D):
|
|||||||
self._id = _id
|
self._id = _id
|
||||||
|
|
||||||
def __call__(self, inter):
|
def __call__(self, inter):
|
||||||
func = GetShapeF1D()
|
shapes = GetShapeF1D()(inter)
|
||||||
shapes = func(inter)
|
return any(a.id == self._id for a in shapes)
|
||||||
for a in shapes:
|
|
||||||
if a.id == self._id:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
class pyHighDensityAnisotropyUP1D(UnaryPredicate1D):
|
class pyHighDensityAnisotropyUP1D(UnaryPredicate1D):
|
||||||
@@ -473,7 +446,6 @@ class pyHighViewMapGradientNormUP1D(UnaryPredicate1D):
|
|||||||
|
|
||||||
def __call__(self, inter):
|
def __call__(self, inter):
|
||||||
gn = self._GetGradient(inter)
|
gn = self._GetGradient(inter)
|
||||||
#print(gn)
|
|
||||||
return (gn > self._threshold)
|
return (gn > self._threshold)
|
||||||
|
|
||||||
|
|
||||||
@@ -503,53 +475,50 @@ class pyClosedCurveUP1D(UnaryPredicate1D):
|
|||||||
it = inter.vertices_begin()
|
it = inter.vertices_begin()
|
||||||
itlast = inter.vertices_end()
|
itlast = inter.vertices_end()
|
||||||
itlast.decrement()
|
itlast.decrement()
|
||||||
vlast = itlast.object
|
return (next(it).id == next(itlast).id)
|
||||||
v = it.object
|
|
||||||
#print(v.id.first, v.id.second)
|
|
||||||
#print(vlast.id.first, vlast.id.second)
|
# -- Binary predicates for 1D elements (curves) -- #
|
||||||
if v.id == vlast.id:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
## Binary predicates for 1D elements (curves)
|
|
||||||
#############################################
|
|
||||||
|
|
||||||
class AndBP1D(BinaryPredicate1D):
|
class AndBP1D(BinaryPredicate1D):
|
||||||
def __init__(self, pred1, pred2):
|
def __init__(self, *predicates):
|
||||||
BinaryPredicate1D.__init__(self)
|
BinaryPredicate1D.__init__(self)
|
||||||
self.__pred1 = pred1
|
self._predicates = predicates
|
||||||
self.__pred2 = pred2
|
if len(self.predicates) < 2:
|
||||||
|
raise ValueError("Expected two or more BinaryPredicate1D")
|
||||||
|
|
||||||
def __call__(self, inter1, inter2):
|
def __call__(self, i1, i2):
|
||||||
return self.__pred1(inter1, inter2) and self.__pred2(inter1, inter2)
|
return all(pred(i1, i2) for pred in self._predicates)
|
||||||
|
|
||||||
|
|
||||||
class OrBP1D(BinaryPredicate1D):
|
class OrBP1D(BinaryPredicate1D):
|
||||||
def __init__(self, pred1, pred2):
|
def __init__(self, *predicates):
|
||||||
BinaryPredicate1D.__init__(self)
|
BinaryPredicate1D.__init__(self)
|
||||||
self.__pred1 = pred1
|
self._predicates = predicates
|
||||||
self.__pred2 = pred2
|
if len(self.predicates) < 2:
|
||||||
|
raise ValueError("Expected two or more BinaryPredicate1D")
|
||||||
|
|
||||||
def __call__(self, inter1, inter2):
|
def __call__(self, i1, i2):
|
||||||
return self.__pred1(inter1, inter2) or self.__pred2(inter1, inter2)
|
return any(pred(i1, i2) for pred in self._predicates)
|
||||||
|
|
||||||
|
|
||||||
class NotBP1D(BinaryPredicate1D):
|
class NotBP1D(BinaryPredicate1D):
|
||||||
def __init__(self, pred):
|
def __init__(self, predicate):
|
||||||
BinaryPredicate1D.__init__(self)
|
BinaryPredicate1D.__init__(self)
|
||||||
self.__pred = pred
|
self._predicate = predicate
|
||||||
|
|
||||||
def __call__(self, inter1, inter2):
|
def __call__(self, i1, i2):
|
||||||
return not self.__pred(inter1, inter2)
|
return (not self._precicate(i1, i2))
|
||||||
|
|
||||||
|
|
||||||
class pyZBP1D(BinaryPredicate1D):
|
class pyZBP1D(BinaryPredicate1D):
|
||||||
def __init__(self, iType=IntegrationType.MEAN):
|
def __init__(self, iType=IntegrationType.MEAN):
|
||||||
BinaryPredicate1D.__init__(self)
|
BinaryPredicate1D.__init__(self)
|
||||||
self._GetZ = GetZF1D(iType)
|
self.func = GetZF1D(iType)
|
||||||
|
|
||||||
def __call__(self, i1, i2):
|
def __call__(self, i1, i2):
|
||||||
return (self._GetZ(i1) > self._GetZ(i2))
|
return (self.func(i1) > self.func(i2))
|
||||||
|
|
||||||
|
|
||||||
class pyZDiscontinuityBP1D(BinaryPredicate1D):
|
class pyZDiscontinuityBP1D(BinaryPredicate1D):
|
||||||
@@ -569,10 +538,10 @@ class pyLengthBP1D(BinaryPredicate1D):
|
|||||||
class pySilhouetteFirstBP1D(BinaryPredicate1D):
|
class pySilhouetteFirstBP1D(BinaryPredicate1D):
|
||||||
def __call__(self, inter1, inter2):
|
def __call__(self, inter1, inter2):
|
||||||
bpred = SameShapeIdBP1D()
|
bpred = SameShapeIdBP1D()
|
||||||
if (bpred(inter1, inter2) != 1):
|
if (not bpred(inter1, inter2)):
|
||||||
return False
|
return False
|
||||||
if (inter1.nature & Nature.SILHOUETTE):
|
if (inter1.nature & Nature.SILHOUETTE):
|
||||||
return (inter2.nature & Nature.SILHOUETTE) != 0
|
return bool(inter2.nature & Nature.SILHOUETTE)
|
||||||
return (inter1.nature == inter2.nature)
|
return (inter1.nature == inter2.nature)
|
||||||
|
|
||||||
|
|
||||||
@@ -587,16 +556,13 @@ class pyViewMapGradientNormBP1D(BinaryPredicate1D):
|
|||||||
self._GetGradient = pyViewMapGradientNormF1D(l, IntegrationType.MEAN)
|
self._GetGradient = pyViewMapGradientNormF1D(l, IntegrationType.MEAN)
|
||||||
|
|
||||||
def __call__(self, i1,i2):
|
def __call__(self, i1,i2):
|
||||||
#print("compare gradient")
|
|
||||||
return (self._GetGradient(i1) > self._GetGradient(i2))
|
return (self._GetGradient(i1) > self._GetGradient(i2))
|
||||||
|
|
||||||
|
|
||||||
class pyShuffleBP1D(BinaryPredicate1D):
|
class pyShuffleBP1D(BinaryPredicate1D):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
BinaryPredicate1D.__init__(self)
|
BinaryPredicate1D.__init__(self)
|
||||||
random.seed(1)
|
random.seed = 1
|
||||||
|
|
||||||
def __call__(self, inter1, inter2):
|
def __call__(self, inter1, inter2):
|
||||||
r1 = random.uniform(0,1)
|
return (random.uniform(0,1) < random.uniform(0,1))
|
||||||
r2 = random.uniform(0,1)
|
|
||||||
return (r1<r2)
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -27,11 +27,275 @@ from _freestyle import (
|
|||||||
integrate,
|
integrate,
|
||||||
)
|
)
|
||||||
|
|
||||||
# constructs for definition of helper functions in Python
|
from mathutils import Vector
|
||||||
from freestyle.types import (
|
from functools import lru_cache
|
||||||
StrokeVertexIterator,
|
from math import cos, sin, pi
|
||||||
)
|
|
||||||
import mathutils
|
|
||||||
|
# -- real utility functions -- #
|
||||||
|
|
||||||
|
|
||||||
|
def rgb_to_bw(r, g, b):
|
||||||
|
""" Method to convert rgb to a bw intensity value. """
|
||||||
|
return 0.35 * r + 0.45 * g + 0.2 * b
|
||||||
|
|
||||||
|
|
||||||
|
def bound(lower, x, higher):
|
||||||
|
""" Returns x bounded by a maximum and minimum value. equivalent to:
|
||||||
|
return min(max(x, lower), higher)
|
||||||
|
"""
|
||||||
|
# this is about 50% quicker than min(max(x, lower), higher)
|
||||||
|
return (lower if x <= lower else higher if x >= higher else x)
|
||||||
|
|
||||||
|
|
||||||
|
def bounding_box(stroke):
|
||||||
|
"""
|
||||||
|
Returns the maximum and minimum coordinates (the bounding box) of the stroke's vertices
|
||||||
|
"""
|
||||||
|
x, y = zip(*(svert.point for svert in stroke))
|
||||||
|
return (Vector((min(x), min(y))), Vector((max(x), max(y))))
|
||||||
|
|
||||||
|
|
||||||
|
# -- General helper functions -- #
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache(maxsize=32)
|
||||||
|
def phase_to_direction(length):
|
||||||
|
"""
|
||||||
|
Returns a list of tuples each containing:
|
||||||
|
- the phase
|
||||||
|
- a Vector with the values of the cosine and sine of 2pi * phase (the direction)
|
||||||
|
"""
|
||||||
|
results = list()
|
||||||
|
for i in range(length):
|
||||||
|
phase = i / (length - 1)
|
||||||
|
results.append((phase, Vector((cos(2 * pi * phase), sin(2 * pi * phase)))))
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
# -- helper functions for chaining -- #
|
||||||
|
|
||||||
|
|
||||||
|
def get_chain_length(ve, orientation):
|
||||||
|
"""Returns the 2d length of a given ViewEdge """
|
||||||
|
from freestyle.chainingiterators import pyChainSilhouetteGenericIterator
|
||||||
|
length = 0.0
|
||||||
|
# setup iterator
|
||||||
|
_it = pyChainSilhouetteGenericIterator(False, False)
|
||||||
|
_it.begin = ve
|
||||||
|
_it.current_edge = ve
|
||||||
|
_it.orientation = orientation
|
||||||
|
_it.init()
|
||||||
|
|
||||||
|
# run iterator till end of chain
|
||||||
|
while not (_it.is_end):
|
||||||
|
length += _it.object.length_2d
|
||||||
|
if (_it.is_begin):
|
||||||
|
# _it has looped back to the beginning;
|
||||||
|
# break to prevent infinite loop
|
||||||
|
break
|
||||||
|
_it.increment()
|
||||||
|
|
||||||
|
# reset iterator
|
||||||
|
_it.begin = ve
|
||||||
|
_it.current_edge = ve
|
||||||
|
_it.orientation = orientation
|
||||||
|
|
||||||
|
# run iterator till begin of chain
|
||||||
|
if not _it.is_begin:
|
||||||
|
_it.decrement()
|
||||||
|
while not (_it.is_end or _it.is_begin):
|
||||||
|
length += _it.object.length_2d
|
||||||
|
_it.decrement()
|
||||||
|
|
||||||
|
return length
|
||||||
|
|
||||||
|
|
||||||
|
def find_matching_vertex(id, it):
|
||||||
|
"""Finds the matching vertexn, or returns None """
|
||||||
|
return next((ve for ve in it if ve.id == id), None)
|
||||||
|
|
||||||
|
|
||||||
|
# -- helper functions for iterating -- #
|
||||||
|
|
||||||
|
|
||||||
|
def iter_current_previous(stroke):
|
||||||
|
"""
|
||||||
|
iterates over the given iterator. yields a tuple of the form
|
||||||
|
(it, prev, current)
|
||||||
|
"""
|
||||||
|
prev = stroke[0]
|
||||||
|
it = Interface0DIterator(stroke)
|
||||||
|
for current in it:
|
||||||
|
yield (it, prev, current)
|
||||||
|
|
||||||
|
|
||||||
|
def iter_t2d_along_stroke(stroke):
|
||||||
|
"""
|
||||||
|
Yields the distance between two stroke vertices
|
||||||
|
relative to the total stroke length.
|
||||||
|
"""
|
||||||
|
total = stroke.length_2d
|
||||||
|
distance = 0.0
|
||||||
|
for it, prev, svert in iter_current_previous(stroke):
|
||||||
|
distance += (prev.point - svert.point).length
|
||||||
|
t = min(distance / total, 1.0) if total > 0.0 else 0.0
|
||||||
|
yield (it, t)
|
||||||
|
|
||||||
|
|
||||||
|
def iter_distance_from_camera(stroke, range_min, range_max):
|
||||||
|
"""
|
||||||
|
Yields the distance to the camera relative to the maximum
|
||||||
|
possible distance for every stroke vertex, constrained by
|
||||||
|
given minimum and maximum values.
|
||||||
|
"""
|
||||||
|
normfac = range_max - range_min # normalization factor
|
||||||
|
it = Interface0DIterator(stroke)
|
||||||
|
for svert in it:
|
||||||
|
distance = svert.point_3d.length # in the camera coordinate
|
||||||
|
if distance < range_min:
|
||||||
|
t = 0.0
|
||||||
|
elif distance > range_max:
|
||||||
|
t = 1.0
|
||||||
|
else:
|
||||||
|
t = (distance - range_min) / normfac
|
||||||
|
yield (it, t)
|
||||||
|
|
||||||
|
|
||||||
|
def iter_distance_from_object(stroke, object, range_min, range_max):
|
||||||
|
"""
|
||||||
|
yields the distance to the given object relative to the maximum
|
||||||
|
possible distance for every stroke vertex, constrained by
|
||||||
|
given minimum and maximum values.
|
||||||
|
"""
|
||||||
|
scene = getCurrentScene()
|
||||||
|
mv = scene.camera.matrix_world.copy().inverted() # model-view matrix
|
||||||
|
loc = mv * object.location # loc in the camera coordinate
|
||||||
|
normfac = range_max - range_min # normalization factor
|
||||||
|
it = Interface0DIterator(stroke)
|
||||||
|
for svert in it:
|
||||||
|
distance = (svert.point_3d - loc).length # in the camera coordinate
|
||||||
|
if distance < range_min:
|
||||||
|
t = 0.0
|
||||||
|
elif distance > range_max:
|
||||||
|
t = 1.0
|
||||||
|
else:
|
||||||
|
t = (distance - range_min) / normfac
|
||||||
|
yield (it, t)
|
||||||
|
|
||||||
|
|
||||||
|
def iter_material_color(stroke, material_attribute):
|
||||||
|
"""
|
||||||
|
yields the specified material attribute for every stroke vertex.
|
||||||
|
the material is taken from the object behind the vertex.
|
||||||
|
"""
|
||||||
|
func = CurveMaterialF0D()
|
||||||
|
it = Interface0DIterator(stroke)
|
||||||
|
for inter in it:
|
||||||
|
material = func(it)
|
||||||
|
if material_attribute == 'DIFF':
|
||||||
|
color = material.diffuse[0:3]
|
||||||
|
elif material_attribute == 'SPEC':
|
||||||
|
color = material.specular[0:3]
|
||||||
|
else:
|
||||||
|
raise ValueError("unexpected material attribute: " + material_attribute)
|
||||||
|
yield (it, color)
|
||||||
|
|
||||||
|
|
||||||
|
def iter_material_value(stroke, material_attribute):
|
||||||
|
"""
|
||||||
|
yields a specific material attribute
|
||||||
|
from the vertex' underlying material.
|
||||||
|
"""
|
||||||
|
func = CurveMaterialF0D()
|
||||||
|
it = Interface0DIterator(stroke)
|
||||||
|
for svert in it:
|
||||||
|
material = func(it)
|
||||||
|
if material_attribute == 'DIFF':
|
||||||
|
t = rgb_to_bw(*material.diffuse[0:3])
|
||||||
|
elif material_attribute == 'DIFF_R':
|
||||||
|
t = material.diffuse[0]
|
||||||
|
elif material_attribute == 'DIFF_G':
|
||||||
|
t = material.diffuse[1]
|
||||||
|
elif material_attribute == 'DIFF_B':
|
||||||
|
t = material.diffuse[2]
|
||||||
|
elif material_attribute == 'SPEC':
|
||||||
|
t = rgb_to_bw(*material.specular[0:3])
|
||||||
|
elif material_attribute == 'SPEC_R':
|
||||||
|
t = material.specular[0]
|
||||||
|
elif material_attribute == 'SPEC_G':
|
||||||
|
t = material.specular[1]
|
||||||
|
elif material_attribute == 'SPEC_B':
|
||||||
|
t = material.specular[2]
|
||||||
|
elif material_attribute == 'SPEC_HARDNESS':
|
||||||
|
t = material.shininess
|
||||||
|
elif material_attribute == 'ALPHA':
|
||||||
|
t = material.diffuse[3]
|
||||||
|
else:
|
||||||
|
raise ValueError("unexpected material attribute: " + material_attribute)
|
||||||
|
yield (it, t)
|
||||||
|
|
||||||
|
|
||||||
|
def iter_distance_along_stroke(stroke):
|
||||||
|
"""
|
||||||
|
yields the absolute distance between
|
||||||
|
the current and preceding vertex.
|
||||||
|
"""
|
||||||
|
distance = 0.0
|
||||||
|
prev = stroke[0]
|
||||||
|
it = Interface0DIterator(stroke)
|
||||||
|
for svert in it:
|
||||||
|
p = svert.point
|
||||||
|
distance += (prev - p).length
|
||||||
|
prev = p.copy() # need a copy because the point can be altered
|
||||||
|
yield it, distance
|
||||||
|
|
||||||
|
|
||||||
|
def iter_triplet(it):
|
||||||
|
"""
|
||||||
|
Iterates over it, yielding a tuple containing
|
||||||
|
the current vertex and its immediate neighbors
|
||||||
|
"""
|
||||||
|
prev = next(it)
|
||||||
|
current = next(it)
|
||||||
|
for succ in it:
|
||||||
|
yield prev, current, succ
|
||||||
|
prev, current = current, succ
|
||||||
|
|
||||||
|
|
||||||
|
# -- mathmatical operations -- #
|
||||||
|
|
||||||
|
|
||||||
|
def stroke_curvature(it):
|
||||||
|
"""
|
||||||
|
Compute the 2D curvature at the stroke vertex pointed by the iterator 'it'.
|
||||||
|
K = 1 / R
|
||||||
|
where R is the radius of the circle going through the current vertex and its neighbors
|
||||||
|
"""
|
||||||
|
|
||||||
|
if it.is_end or it.is_begin:
|
||||||
|
return 0.0
|
||||||
|
|
||||||
|
next = it.incremented().point
|
||||||
|
prev = it.decremented().point
|
||||||
|
current = it.object.point
|
||||||
|
|
||||||
|
|
||||||
|
ab = (current - prev)
|
||||||
|
bc = (next - current)
|
||||||
|
ac = (prev - next)
|
||||||
|
|
||||||
|
a, b, c = ab.length, bc.length, ac.length
|
||||||
|
|
||||||
|
try:
|
||||||
|
area = 0.5 * ab.cross(ac)
|
||||||
|
K = (4 * area) / (a * b * c)
|
||||||
|
K = bound(0.0, K, 1.0)
|
||||||
|
|
||||||
|
except ZeroDivisionError:
|
||||||
|
K = 0.0
|
||||||
|
|
||||||
|
return K
|
||||||
|
|
||||||
|
|
||||||
def stroke_normal(it):
|
def stroke_normal(it):
|
||||||
@@ -42,28 +306,21 @@ def stroke_normal(it):
|
|||||||
they have already been modified by stroke geometry modifiers.
|
they have already been modified by stroke geometry modifiers.
|
||||||
"""
|
"""
|
||||||
# first stroke segment
|
# first stroke segment
|
||||||
it_next = StrokeVertexIterator(it)
|
it_next = it.incremented()
|
||||||
it_next.increment()
|
|
||||||
if it.is_begin:
|
if it.is_begin:
|
||||||
e = it_next.object.point_2d - it.object.point_2d
|
e = it_next.object.point_2d - it.object.point_2d
|
||||||
n = mathutils.Vector((e[1], -e[0]))
|
n = Vector((e[1], -e[0]))
|
||||||
n.normalize()
|
return n.normalized()
|
||||||
return n
|
|
||||||
# last stroke segment
|
# last stroke segment
|
||||||
it_prev = StrokeVertexIterator(it)
|
it_prev = it.decremented()
|
||||||
it_prev.decrement()
|
|
||||||
if it_next.is_end:
|
if it_next.is_end:
|
||||||
e = it.object.point_2d - it_prev.object.point_2d
|
e = it.object.point_2d - it_prev.object.point_2d
|
||||||
n = mathutils.Vector((e[1], -e[0]))
|
n = Vector((e[1], -e[0]))
|
||||||
n.normalize()
|
return n.normalized()
|
||||||
return n
|
|
||||||
# two subsequent stroke segments
|
# two subsequent stroke segments
|
||||||
e1 = it_next.object.point_2d - it.object.point_2d
|
e1 = it_next.object.point_2d - it.object.point_2d
|
||||||
e2 = it.object.point_2d - it_prev.object.point_2d
|
e2 = it.object.point_2d - it_prev.object.point_2d
|
||||||
n1 = mathutils.Vector((e1[1], -e1[0]))
|
n1 = Vector((e1[1], -e1[0])).normalized()
|
||||||
n2 = mathutils.Vector((e2[1], -e2[0]))
|
n2 = Vector((e2[1], -e2[0])).normalized()
|
||||||
n1.normalize()
|
n = (n1 + n2)
|
||||||
n2.normalize()
|
return n.normalized()
|
||||||
n = n1 + n2
|
|
||||||
n.normalize()
|
|
||||||
return n
|
|
||||||
|
Reference in New Issue
Block a user