Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'release')
-rw-r--r--release/datafiles/startup.blendbin411476 -> 439496 bytes
-rw-r--r--release/scripts/freestyle/data/env_map/brown00.pngbin0 -> 22195 bytes
-rw-r--r--release/scripts/freestyle/data/env_map/gray00.pngbin0 -> 18513 bytes
-rw-r--r--release/scripts/freestyle/data/env_map/gray01.pngbin0 -> 9915 bytes
-rw-r--r--release/scripts/freestyle/data/env_map/gray02.pngbin0 -> 7197 bytes
-rw-r--r--release/scripts/freestyle/data/env_map/gray03.pngbin0 -> 16109 bytes
-rw-r--r--release/scripts/freestyle/style_modules/ChainingIterators.py703
-rw-r--r--release/scripts/freestyle/style_modules/Functions0D.py105
-rw-r--r--release/scripts/freestyle/style_modules/Functions1D.py58
-rw-r--r--release/scripts/freestyle/style_modules/PredicatesB1D.py73
-rw-r--r--release/scripts/freestyle/style_modules/PredicatesU0D.py96
-rw-r--r--release/scripts/freestyle/style_modules/PredicatesU1D.py342
-rw-r--r--release/scripts/freestyle/style_modules/anisotropic_diffusion.py44
-rw-r--r--release/scripts/freestyle/style_modules/apriori_and_causal_density.py39
-rw-r--r--release/scripts/freestyle/style_modules/apriori_density.py37
-rw-r--r--release/scripts/freestyle/style_modules/backbone_stretcher.py35
-rw-r--r--release/scripts/freestyle/style_modules/blueprint_circles.py42
-rw-r--r--release/scripts/freestyle/style_modules/blueprint_ellipses.py42
-rw-r--r--release/scripts/freestyle/style_modules/blueprint_squares.py43
-rw-r--r--release/scripts/freestyle/style_modules/cartoon.py38
-rw-r--r--release/scripts/freestyle/style_modules/contour.py36
-rw-r--r--release/scripts/freestyle/style_modules/curvature2d.py37
-rw-r--r--release/scripts/freestyle/style_modules/external_contour.py36
-rw-r--r--release/scripts/freestyle/style_modules/external_contour_sketchy.py43
-rw-r--r--release/scripts/freestyle/style_modules/external_contour_smooth.py39
-rw-r--r--release/scripts/freestyle/style_modules/haloing.py46
-rw-r--r--release/scripts/freestyle/style_modules/ignore_small_occlusions.py36
-rw-r--r--release/scripts/freestyle/style_modules/invisible_lines.py37
-rw-r--r--release/scripts/freestyle/style_modules/japanese_bigbrush.py56
-rw-r--r--release/scripts/freestyle/style_modules/logical_operators.py47
-rw-r--r--release/scripts/freestyle/style_modules/long_anisotropically_dense.py68
-rw-r--r--release/scripts/freestyle/style_modules/multiple_parameterization.py47
-rw-r--r--release/scripts/freestyle/style_modules/nature.py39
-rw-r--r--release/scripts/freestyle/style_modules/near_lines.py38
-rw-r--r--release/scripts/freestyle/style_modules/occluded_by_specific_object.py40
-rw-r--r--release/scripts/freestyle/style_modules/parameter_editor.py1259
-rw-r--r--release/scripts/freestyle/style_modules/polygonalize.py36
-rw-r--r--release/scripts/freestyle/style_modules/qi0.py36
-rw-r--r--release/scripts/freestyle/style_modules/qi0_not_external_contour.py41
-rw-r--r--release/scripts/freestyle/style_modules/qi1.py37
-rw-r--r--release/scripts/freestyle/style_modules/qi2.py37
-rw-r--r--release/scripts/freestyle/style_modules/sequentialsplit_sketchy.py44
-rw-r--r--release/scripts/freestyle/style_modules/shaders.py1238
-rw-r--r--release/scripts/freestyle/style_modules/sketchy_multiple_parameterization.py43
-rw-r--r--release/scripts/freestyle/style_modules/sketchy_topology_broken.py47
-rw-r--r--release/scripts/freestyle/style_modules/sketchy_topology_preserved.py42
-rw-r--r--release/scripts/freestyle/style_modules/split_at_highest_2d_curvatures.py41
-rw-r--r--release/scripts/freestyle/style_modules/split_at_tvertices.py40
-rw-r--r--release/scripts/freestyle/style_modules/stroke_texture.py38
-rw-r--r--release/scripts/freestyle/style_modules/suggestive.py38
-rw-r--r--release/scripts/freestyle/style_modules/thickness_fof_depth_discontinuity.py37
-rw-r--r--release/scripts/freestyle/style_modules/tipremover.py36
-rw-r--r--release/scripts/freestyle/style_modules/tvertex_remover.py37
-rw-r--r--release/scripts/freestyle/style_modules/uniformpruning_zsort.py39
-rw-r--r--release/scripts/startup/bl_operators/__init__.py14
-rw-r--r--release/scripts/startup/bl_operators/freestyle.py135
-rw-r--r--release/scripts/startup/bl_ui/__init__.py15
-rw-r--r--release/scripts/startup/bl_ui/properties_freestyle.py668
-rw-r--r--release/scripts/startup/bl_ui/properties_render.py122
-rw-r--r--release/scripts/startup/bl_ui/properties_render_layer.py178
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py22
61 files changed, 6530 insertions, 132 deletions
diff --git a/release/datafiles/startup.blend b/release/datafiles/startup.blend
index e5d781e06e2..bec18dff39e 100644
--- a/release/datafiles/startup.blend
+++ b/release/datafiles/startup.blend
Binary files differ
diff --git a/release/scripts/freestyle/data/env_map/brown00.png b/release/scripts/freestyle/data/env_map/brown00.png
new file mode 100644
index 00000000000..855f06f4fb9
--- /dev/null
+++ b/release/scripts/freestyle/data/env_map/brown00.png
Binary files differ
diff --git a/release/scripts/freestyle/data/env_map/gray00.png b/release/scripts/freestyle/data/env_map/gray00.png
new file mode 100644
index 00000000000..7c9b1a8149e
--- /dev/null
+++ b/release/scripts/freestyle/data/env_map/gray00.png
Binary files differ
diff --git a/release/scripts/freestyle/data/env_map/gray01.png b/release/scripts/freestyle/data/env_map/gray01.png
new file mode 100644
index 00000000000..06542908e6b
--- /dev/null
+++ b/release/scripts/freestyle/data/env_map/gray01.png
Binary files differ
diff --git a/release/scripts/freestyle/data/env_map/gray02.png b/release/scripts/freestyle/data/env_map/gray02.png
new file mode 100644
index 00000000000..0208f4920d9
--- /dev/null
+++ b/release/scripts/freestyle/data/env_map/gray02.png
Binary files differ
diff --git a/release/scripts/freestyle/data/env_map/gray03.png b/release/scripts/freestyle/data/env_map/gray03.png
new file mode 100644
index 00000000000..aab9b957c21
--- /dev/null
+++ b/release/scripts/freestyle/data/env_map/gray03.png
Binary files differ
diff --git a/release/scripts/freestyle/style_modules/ChainingIterators.py b/release/scripts/freestyle/style_modules/ChainingIterators.py
new file mode 100644
index 00000000000..f0dfc468adb
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/ChainingIterators.py
@@ -0,0 +1,703 @@
+# ##### 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
+
diff --git a/release/scripts/freestyle/style_modules/Functions0D.py b/release/scripts/freestyle/style_modules/Functions0D.py
new file mode 100644
index 00000000000..b36961f3f91
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/Functions0D.py
@@ -0,0 +1,105 @@
+# ##### 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 : Functions0D.py
+# Authors : Fredo Durand, Stephane Grabli, Francois Sillion, Emmanuel Turquin
+# Date : 30/06/2005
+# Purpose : Functions (functors) to be used for 0D elements
+
+from Freestyle import Curvature2DAngleF0D, CurvePoint, ReadCompleteViewMapPixelF0D, \
+ ReadSteerableViewMapPixelF0D, UnaryFunction0DDouble, UnaryFunction0DMaterial, \
+ UnaryFunction0DVec2f
+from Freestyle import ContextFunctions as CF
+
+import math
+import mathutils
+
+class CurveMaterialF0D(UnaryFunction0DMaterial):
+ # A replacement of the built-in MaterialF0D for stroke creation.
+ # MaterialF0D does not work with Curves and Strokes.
+ def __call__(self, inter):
+ cp = inter.object
+ assert(isinstance(cp, CurvePoint))
+ fe = cp.first_svertex.get_fedge(cp.second_svertex)
+ assert(fe is not None)
+ return fe.material if fe.is_smooth else fe.material_left
+
+class pyInverseCurvature2DAngleF0D(UnaryFunction0DDouble):
+ def __call__(self, inter):
+ func = Curvature2DAngleF0D()
+ c = func(inter)
+ return (3.1415 - c)
+
+class pyCurvilinearLengthF0D(UnaryFunction0DDouble):
+ def __call__(self, inter):
+ cp = inter.object
+ assert(isinstance(cp, CurvePoint))
+ return cp.t2d
+
+## estimate anisotropy of density
+class pyDensityAnisotropyF0D(UnaryFunction0DDouble):
+ def __init__(self,level):
+ UnaryFunction0DDouble.__init__(self)
+ self.IsoDensity = ReadCompleteViewMapPixelF0D(level)
+ self.d0Density = ReadSteerableViewMapPixelF0D(0, level)
+ self.d1Density = ReadSteerableViewMapPixelF0D(1, level)
+ self.d2Density = ReadSteerableViewMapPixelF0D(2, level)
+ self.d3Density = ReadSteerableViewMapPixelF0D(3, level)
+ def __call__(self, inter):
+ c_iso = self.IsoDensity(inter)
+ c_0 = self.d0Density(inter)
+ c_1 = self.d1Density(inter)
+ c_2 = self.d2Density(inter)
+ c_3 = self.d3Density(inter)
+ cMax = max(max(c_0,c_1), max(c_2,c_3))
+ cMin = min(min(c_0,c_1), min(c_2,c_3))
+ if c_iso == 0:
+ v = 0
+ else:
+ v = (cMax-cMin)/c_iso
+ return v
+
+## Returns the gradient vector for a pixel
+## l
+## the level at which one wants to compute the gradient
+class pyViewMapGradientVectorF0D(UnaryFunction0DVec2f):
+ def __init__(self, l):
+ UnaryFunction0DVec2f.__init__(self)
+ self._l = l
+ self._step = math.pow(2,self._l)
+ def __call__(self, iter):
+ p = iter.object.point_2d
+ 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))
+ 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))
+ return mathutils.Vector([gx, gy])
+
+class pyViewMapGradientNormF0D(UnaryFunction0DDouble):
+ def __init__(self, l):
+ UnaryFunction0DDouble.__init__(self)
+ self._l = l
+ self._step = math.pow(2,self._l)
+ def __call__(self, iter):
+ p = iter.object.point_2d
+ 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))
+ 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))
+ grad = mathutils.Vector([gx, gy])
+ return grad.length
diff --git a/release/scripts/freestyle/style_modules/Functions1D.py b/release/scripts/freestyle/style_modules/Functions1D.py
new file mode 100644
index 00000000000..17b4f1922a6
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/Functions1D.py
@@ -0,0 +1,58 @@
+# ##### 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 : Functions1D.py
+# Authors : Fredo Durand, Stephane Grabli, Francois Sillion, Emmanuel Turquin
+# Date : 08/04/2005
+# Purpose : Functions (functors) to be used for 1D elements
+
+from Freestyle import GetProjectedZF1D, IntegrationType, UnaryFunction1DDouble, integrate
+from Functions0D import pyDensityAnisotropyF0D, pyViewMapGradientNormF0D
+import string
+
+class pyGetInverseProjectedZF1D(UnaryFunction1DDouble):
+ def __call__(self, inter):
+ func = GetProjectedZF1D()
+ z = func(inter)
+ return (1.0 - z)
+
+class pyGetSquareInverseProjectedZF1D(UnaryFunction1DDouble):
+ def __call__(self, inter):
+ func = GetProjectedZF1D()
+ z = func(inter)
+ return (1.0 - z*z)
+
+class pyDensityAnisotropyF1D(UnaryFunction1DDouble):
+ def __init__(self,level, integrationType=IntegrationType.MEAN, sampling=2.0):
+ UnaryFunction1DDouble.__init__(self, integrationType)
+ self._func = pyDensityAnisotropyF0D(level)
+ self._integration = integrationType
+ self._sampling = sampling
+ def __call__(self, inter):
+ v = integrate(self._func, inter.pointsBegin(self._sampling), inter.pointsEnd(self._sampling), self._integration)
+ return v
+
+class pyViewMapGradientNormF1D(UnaryFunction1DDouble):
+ def __init__(self,l, integrationType, sampling=2.0):
+ UnaryFunction1DDouble.__init__(self, integrationType)
+ self._func = pyViewMapGradientNormF0D(l)
+ self._integration = integrationType
+ self._sampling = sampling
+ def __call__(self, inter):
+ v = integrate(self._func, inter.pointsBegin(self._sampling), inter.pointsEnd(self._sampling), self._integration)
+ return v
diff --git a/release/scripts/freestyle/style_modules/PredicatesB1D.py b/release/scripts/freestyle/style_modules/PredicatesB1D.py
new file mode 100644
index 00000000000..642ff5f9845
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/PredicatesB1D.py
@@ -0,0 +1,73 @@
+# ##### 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 : PredicatesB1D.py
+# Authors : Fredo Durand, Stephane Grabli, Francois Sillion, Emmanuel Turquin
+# Date : 08/04/2005
+# Purpose : Binary predicates (functors) to be used for 1D elements
+
+from Freestyle import BinaryPredicate1D, GetZF1D, IntegrationType, Nature, SameShapeIdBP1D, ZDiscontinuityF1D
+from Functions1D import pyViewMapGradientNormF1D
+
+import random
+
+class pyZBP1D(BinaryPredicate1D):
+ def __call__(self, i1, i2):
+ func = GetZF1D()
+ return (func(i1) > func(i2))
+
+class pyZDiscontinuityBP1D(BinaryPredicate1D):
+ def __init__(self, iType = IntegrationType.MEAN):
+ BinaryPredicate1D.__init__(self)
+ self._GetZDiscontinuity = ZDiscontinuityF1D(iType)
+ def __call__(self, i1, i2):
+ return (self._GetZDiscontinuity(i1) > self._GetZDiscontinuity(i2))
+
+class pyLengthBP1D(BinaryPredicate1D):
+ def __call__(self, i1, i2):
+ return (i1.length_2d > i2.length_2d)
+
+class pySilhouetteFirstBP1D(BinaryPredicate1D):
+ def __call__(self, inter1, inter2):
+ bpred = SameShapeIdBP1D()
+ if (bpred(inter1, inter2) != 1):
+ return 0
+ if (inter1.nature & Nature.SILHOUETTE):
+ return (inter2.nature & Nature.SILHOUETTE) != 0
+ return (inter1.nature == inter2.nature)
+
+class pyNatureBP1D(BinaryPredicate1D):
+ def __call__(self, inter1, inter2):
+ return (inter1.nature & inter2.nature)
+
+class pyViewMapGradientNormBP1D(BinaryPredicate1D):
+ def __init__(self,l, sampling=2.0):
+ BinaryPredicate1D.__init__(self)
+ self._GetGradient = pyViewMapGradientNormF1D(l, IntegrationType.MEAN)
+ def __call__(self, i1,i2):
+ print("compare gradient")
+ return (self._GetGradient(i1) > self._GetGradient(i2))
+
+class pyShuffleBP1D(BinaryPredicate1D):
+ def __init__(self):
+ BinaryPredicate1D.__init__(self)
+ random.seed(1)
+ def __call__(self, inter1, inter2):
+ r1 = random.uniform(0,1)
+ r2 = random.uniform(0,1)
+ return (r1<r2)
diff --git a/release/scripts/freestyle/style_modules/PredicatesU0D.py b/release/scripts/freestyle/style_modules/PredicatesU0D.py
new file mode 100644
index 00000000000..49675eb3c6a
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/PredicatesU0D.py
@@ -0,0 +1,96 @@
+# ##### 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 : PredicatesU0D.py
+# Authors : Fredo Durand, Stephane Grabli, Francois Sillion, Emmanuel Turquin
+# Date : 08/04/2005
+# Purpose : Unary predicates (functors) to be used for 0D elements
+
+from Freestyle import Curvature2DAngleF0D, Nature, QuantitativeInvisibilityF0D, UnaryPredicate0D
+from Functions0D import pyCurvilinearLengthF0D
+
+class pyHigherCurvature2DAngleUP0D(UnaryPredicate0D):
+ def __init__(self,a):
+ UnaryPredicate0D.__init__(self)
+ self._a = a
+ def __call__(self, inter):
+ func = Curvature2DAngleF0D()
+ a = func(inter)
+ return (a > self._a)
+
+class pyUEqualsUP0D(UnaryPredicate0D):
+ def __init__(self,u, w):
+ UnaryPredicate0D.__init__(self)
+ self._u = u
+ self._w = w
+ def __call__(self, inter):
+ func = pyCurvilinearLengthF0D()
+ u = func(inter)
+ return (u > (self._u-self._w)) and (u < (self._u+self._w))
+
+class pyVertexNatureUP0D(UnaryPredicate0D):
+ def __init__(self,nature):
+ UnaryPredicate0D.__init__(self)
+ self._nature = nature
+ def __call__(self, inter):
+ v = inter.object
+ 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):
+ def __init__(self):
+ UnaryPredicate0D.__init__(self)
+ self._getQI = QuantitativeInvisibilityF0D()
+ def __call__(self, iter):
+ if (iter.object.nature & Nature.T_VERTEX) == 0:
+ return 0
+ if iter.is_end:
+ return 0
+ if self._getQI(iter) != 0:
+ return 1
+ return 0
+
+class pyParameterUP0DGoodOne(UnaryPredicate0D):
+ def __init__(self,pmin,pmax):
+ UnaryPredicate0D.__init__(self)
+ self._m = pmin
+ self._M = pmax
+ #self.getCurvilinearAbscissa = GetCurvilinearAbscissaF0D()
+ def __call__(self, inter):
+ #s = self.getCurvilinearAbscissa(inter)
+ u = inter.u
+ #print(u)
+ return ((u>=self._m) and (u<=self._M))
+
+class pyParameterUP0D(UnaryPredicate0D):
+ def __init__(self,pmin,pmax):
+ UnaryPredicate0D.__init__(self)
+ self._m = pmin
+ self._M = pmax
+ #self.getCurvilinearAbscissa = GetCurvilinearAbscissaF0D()
+ def __call__(self, inter):
+ func = Curvature2DAngleF0D()
+ c = func(inter)
+ b1 = (c>0.1)
+ #s = self.getCurvilinearAbscissa(inter)
+ u = inter.u
+ #print(u)
+ b = ((u>=self._m) and (u<=self._M))
+ return b and b1
diff --git a/release/scripts/freestyle/style_modules/PredicatesU1D.py b/release/scripts/freestyle/style_modules/PredicatesU1D.py
new file mode 100644
index 00000000000..5c48219e9f4
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/PredicatesU1D.py
@@ -0,0 +1,342 @@
+# ##### 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 : PredicatesU1D.py
+# Authors : Fredo Durand, Stephane Grabli, Francois Sillion, Emmanuel Turquin
+# Date : 08/04/2005
+# Purpose : Unary predicates (functors) to be used for 1D elements
+
+from Freestyle import Curvature2DAngleF0D, CurveNatureF1D, DensityF1D, GetCompleteViewMapDensityF1D, \
+ GetDirectionalViewMapDensityF1D, GetOccludersF1D, GetProjectedZF1D, GetShapeF1D, GetSteerableViewMapDensityF1D, \
+ IntegrationType, ShapeUP1D, TVertex, UnaryPredicate1D
+from Functions1D import pyDensityAnisotropyF1D, pyViewMapGradientNormF1D
+
+class pyNFirstUP1D(UnaryPredicate1D):
+ def __init__(self, n):
+ UnaryPredicate1D.__init__(self)
+ self.__n = n
+ self.__count = 0
+ def __call__(self, inter):
+ self.__count = self.__count + 1
+ if self.__count <= self.__n:
+ return 1
+ return 0
+
+class pyHigherLengthUP1D(UnaryPredicate1D):
+ def __init__(self,l):
+ UnaryPredicate1D.__init__(self)
+ self._l = l
+ def __call__(self, inter):
+ return (inter.length_2d > self._l)
+
+class pyNatureUP1D(UnaryPredicate1D):
+ def __init__(self,nature):
+ UnaryPredicate1D.__init__(self)
+ self._nature = nature
+ self._getNature = CurveNatureF1D()
+ def __call__(self, inter):
+ if(self._getNature(inter) & self._nature):
+ return 1
+ return 0
+
+class pyHigherNumberOfTurnsUP1D(UnaryPredicate1D):
+ def __init__(self,n,a):
+ UnaryPredicate1D.__init__(self)
+ self._n = n
+ self._a = a
+ def __call__(self, inter):
+ count = 0
+ func = Curvature2DAngleF0D()
+ it = inter.vertices_begin()
+ while not it.is_end:
+ if func(it) > self._a:
+ count = count+1
+ if count > self._n:
+ return 1
+ it.increment()
+ return 0
+
+class pyDensityUP1D(UnaryPredicate1D):
+ def __init__(self,wsize,threshold, integration = IntegrationType.MEAN, sampling=2.0):
+ UnaryPredicate1D.__init__(self)
+ self._wsize = wsize
+ self._threshold = threshold
+ self._integration = integration
+ self._func = DensityF1D(self._wsize, self._integration, sampling)
+ def __call__(self, inter):
+ if self._func(inter) < self._threshold:
+ return 1
+ return 0
+
+class pyLowSteerableViewMapDensityUP1D(UnaryPredicate1D):
+ def __init__(self,threshold, level,integration = IntegrationType.MEAN):
+ UnaryPredicate1D.__init__(self)
+ self._threshold = threshold
+ self._level = level
+ self._integration = integration
+ def __call__(self, inter):
+ func = GetSteerableViewMapDensityF1D(self._level, self._integration)
+ v = func(inter)
+ print(v)
+ if v < self._threshold:
+ return 1
+ return 0
+
+class pyLowDirectionalViewMapDensityUP1D(UnaryPredicate1D):
+ def __init__(self,threshold, orientation, level,integration = IntegrationType.MEAN):
+ UnaryPredicate1D.__init__(self)
+ self._threshold = threshold
+ self._orientation = orientation
+ self._level = level
+ self._integration = integration
+ def __call__(self, inter):
+ func = GetDirectionalViewMapDensityF1D(self._orientation, self._level, self._integration)
+ v = func(inter)
+ #print(v)
+ if v < self._threshold:
+ return 1
+ return 0
+
+class pyHighSteerableViewMapDensityUP1D(UnaryPredicate1D):
+ def __init__(self,threshold, level,integration = IntegrationType.MEAN):
+ UnaryPredicate1D.__init__(self)
+ self._threshold = threshold
+ self._level = level
+ self._integration = integration
+ self._func = GetSteerableViewMapDensityF1D(self._level, self._integration)
+ def __call__(self, inter):
+ v = self._func(inter)
+ if v > self._threshold:
+ return 1
+ return 0
+
+class pyHighDirectionalViewMapDensityUP1D(UnaryPredicate1D):
+ def __init__(self,threshold, orientation, level,integration = IntegrationType.MEAN, sampling=2.0):
+ UnaryPredicate1D.__init__(self)
+ self._threshold = threshold
+ self._orientation = orientation
+ self._level = level
+ self._integration = integration
+ self._sampling = sampling
+ def __call__(self, inter):
+ func = GetDirectionalViewMapDensityF1D(self._orientation, self._level, self._integration, self._sampling)
+ v = func(inter)
+ if v > self._threshold:
+ return 1
+ return 0
+
+class pyHighViewMapDensityUP1D(UnaryPredicate1D):
+ def __init__(self,threshold, level,integration = IntegrationType.MEAN, sampling=2.0):
+ UnaryPredicate1D.__init__(self)
+ self._threshold = threshold
+ self._level = level
+ self._integration = integration
+ self._sampling = sampling
+ self._func = GetCompleteViewMapDensityF1D(self._level, self._integration, self._sampling) # 2.0 is the smpling
+ def __call__(self, inter):
+ #print("toto")
+ #print(func.name)
+ #print(inter.name)
+ v= self._func(inter)
+ if v > self._threshold:
+ return 1
+ return 0
+
+class pyDensityFunctorUP1D(UnaryPredicate1D):
+ def __init__(self,wsize,threshold, functor, funcmin=0.0, funcmax=1.0, integration = IntegrationType.MEAN):
+ UnaryPredicate1D.__init__(self)
+ self._wsize = wsize
+ self._threshold = float(threshold)
+ self._functor = functor
+ self._funcmin = float(funcmin)
+ self._funcmax = float(funcmax)
+ self._integration = integration
+ def __call__(self, inter):
+ func = DensityF1D(self._wsize, self._integration)
+ res = self._functor(inter)
+ k = (res-self._funcmin)/(self._funcmax-self._funcmin)
+ if func(inter) < self._threshold*k:
+ return 1
+ return 0
+
+class pyZSmallerUP1D(UnaryPredicate1D):
+ def __init__(self,z, integration=IntegrationType.MEAN):
+ UnaryPredicate1D.__init__(self)
+ self._z = z
+ self._integration = integration
+ def __call__(self, inter):
+ func = GetProjectedZF1D(self._integration)
+ if func(inter) < self._z:
+ return 1
+ return 0
+
+class pyIsOccludedByUP1D(UnaryPredicate1D):
+ def __init__(self,id):
+ UnaryPredicate1D.__init__(self)
+ self._id = id
+ def __call__(self, inter):
+ func = GetShapeF1D()
+ shapes = func(inter)
+ for s in shapes:
+ if(s.id == self._id):
+ return 0
+ it = inter.vertices_begin()
+ itlast = inter.vertices_end()
+ itlast.decrement()
+ v = it.object
+ vlast = itlast.object
+ tvertex = v.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 1
+ 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 1
+ print("-------", ve.id.first, "-", ve.id.second)
+ eit.increment()
+ return 0
+
+class pyIsInOccludersListUP1D(UnaryPredicate1D):
+ def __init__(self,id):
+ UnaryPredicate1D.__init__(self)
+ self._id = id
+ def __call__(self, inter):
+ func = GetOccludersF1D()
+ occluders = func(inter)
+ for a in occluders:
+ if a.id == self._id:
+ return 1
+ return 0
+
+class pyIsOccludedByItselfUP1D(UnaryPredicate1D):
+ def __init__(self):
+ UnaryPredicate1D.__init__(self)
+ self.__func1 = GetOccludersF1D()
+ self.__func2 = GetShapeF1D()
+ def __call__(self, inter):
+ lst1 = self.__func1(inter)
+ lst2 = self.__func2(inter)
+ for vs1 in lst1:
+ for vs2 in lst2:
+ if vs1.id == vs2.id:
+ return 1
+ return 0
+
+class pyIsOccludedByIdListUP1D(UnaryPredicate1D):
+ def __init__(self, idlist):
+ UnaryPredicate1D.__init__(self)
+ self._idlist = idlist
+ self.__func1 = GetOccludersF1D()
+ def __call__(self, inter):
+ lst1 = self.__func1(inter)
+ for vs1 in lst1:
+ for _id in self._idlist:
+ if vs1.id == _id:
+ return 1
+ return 0
+
+class pyShapeIdListUP1D(UnaryPredicate1D):
+ def __init__(self,idlist):
+ UnaryPredicate1D.__init__(self)
+ self._idlist = idlist
+ self._funcs = []
+ for _id in idlist :
+ self._funcs.append(ShapeUP1D(_id.first, _id.second))
+ def __call__(self, inter):
+ for func in self._funcs :
+ if func(inter) == 1:
+ return 1
+ return 0
+
+## deprecated
+class pyShapeIdUP1D(UnaryPredicate1D):
+ def __init__(self, _id):
+ UnaryPredicate1D.__init__(self)
+ self._id = _id
+ def __call__(self, inter):
+ func = GetShapeF1D()
+ shapes = func(inter)
+ for a in shapes:
+ if a.id == self._id:
+ return 1
+ return 0
+
+class pyHighDensityAnisotropyUP1D(UnaryPredicate1D):
+ def __init__(self,threshold, level, sampling=2.0):
+ UnaryPredicate1D.__init__(self)
+ self._l = threshold
+ self.func = pyDensityAnisotropyF1D(level, IntegrationType.MEAN, sampling)
+ def __call__(self, inter):
+ return (self.func(inter) > self._l)
+
+class pyHighViewMapGradientNormUP1D(UnaryPredicate1D):
+ def __init__(self,threshold, l, sampling=2.0):
+ UnaryPredicate1D.__init__(self)
+ self._threshold = threshold
+ self._GetGradient = pyViewMapGradientNormF1D(l, IntegrationType.MEAN)
+ def __call__(self, inter):
+ gn = self._GetGradient(inter)
+ #print(gn)
+ return (gn > self._threshold)
+
+class pyDensityVariableSigmaUP1D(UnaryPredicate1D):
+ def __init__(self,functor, sigmaMin,sigmaMax, lmin, lmax, tmin, tmax, integration = IntegrationType.MEAN, sampling=2.0):
+ UnaryPredicate1D.__init__(self)
+ self._functor = functor
+ self._sigmaMin = float(sigmaMin)
+ self._sigmaMax = float(sigmaMax)
+ self._lmin = float(lmin)
+ self._lmax = float(lmax)
+ self._tmin = tmin
+ self._tmax = tmax
+ self._integration = integration
+ self._sampling = sampling
+ def __call__(self, inter):
+ sigma = (self._sigmaMax-self._sigmaMin)/(self._lmax-self._lmin)*(self._functor(inter)-self._lmin) + self._sigmaMin
+ t = (self._tmax-self._tmin)/(self._lmax-self._lmin)*(self._functor(inter)-self._lmin) + self._tmin
+ if sigma < self._sigmaMin:
+ sigma = self._sigmaMin
+ self._func = DensityF1D(sigma, self._integration, self._sampling)
+ d = self._func(inter)
+ if d < t:
+ return 1
+ return 0
+
+class pyClosedCurveUP1D(UnaryPredicate1D):
+ def __call__(self, inter):
+ it = inter.vertices_begin()
+ itlast = inter.vertices_end()
+ itlast.decrement()
+ vlast = itlast.object
+ v = it.object
+ print(v.id.first, v.id.second)
+ print(vlast.id.first, vlast.id.second)
+ if v.id == vlast.id:
+ return 1
+ return 0
diff --git a/release/scripts/freestyle/style_modules/anisotropic_diffusion.py b/release/scripts/freestyle/style_modules/anisotropic_diffusion.py
new file mode 100644
index 00000000000..bc1654475ac
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/anisotropic_diffusion.py
@@ -0,0 +1,44 @@
+# ##### 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 : anisotropic_diffusion.py
+# Author : Fredo Durand
+# Date : 12/08/2004
+# Purpose : Smoothes lines using an anisotropic diffusion scheme
+
+from Freestyle import ChainPredicateIterator, ConstantThicknessShader, ExternalContourUP1D, IncreasingColorShader, \
+ Operators, QuantitativeInvisibilityUP1D, SamplingShader, Stroke, StrokeTextureShader, TrueBP1D, TrueUP1D
+from logical_operators import AndUP1D, NotUP1D
+from shaders import pyDiffusion2Shader
+
+# pyDiffusion2Shader parameters
+offset = 0.25
+nbIter = 30
+
+upred = AndUP1D(QuantitativeInvisibilityUP1D(0), ExternalContourUP1D())
+Operators.select(upred)
+bpred = TrueBP1D()
+Operators.bidirectional_chain(ChainPredicateIterator(upred, bpred), NotUP1D(upred))
+shaders_list = [
+ ConstantThicknessShader(4),
+ StrokeTextureShader("smoothAlpha.bmp", Stroke.OPAQUE_MEDIUM, False),
+ SamplingShader(2),
+ pyDiffusion2Shader(offset, nbIter),
+ IncreasingColorShader(1, 0, 0, 1, 0, 1, 0, 1),
+ ]
+Operators.create(TrueUP1D(), shaders_list)
diff --git a/release/scripts/freestyle/style_modules/apriori_and_causal_density.py b/release/scripts/freestyle/style_modules/apriori_and_causal_density.py
new file mode 100644
index 00000000000..8284f7308f8
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/apriori_and_causal_density.py
@@ -0,0 +1,39 @@
+# ##### 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 : apriori_and_causal_density.py
+# Author : Stephane Grabli
+# Date : 04/08/2005
+# Purpose : Selects the lines with high a priori density and
+# subjects them to the causal density so as to avoid
+# cluttering
+
+from Freestyle import ChainPredicateIterator, ConstantColorShader, ConstantThicknessShader, IntegrationType, \
+ Operators, QuantitativeInvisibilityUP1D, TrueBP1D
+from PredicatesU1D import pyDensityUP1D, pyHighViewMapDensityUP1D
+from logical_operators import AndUP1D, NotUP1D
+
+upred = AndUP1D(QuantitativeInvisibilityUP1D(0), pyHighViewMapDensityUP1D(0.3, IntegrationType.LAST))
+Operators.select(upred)
+bpred = TrueBP1D()
+Operators.bidirectional_chain(ChainPredicateIterator(upred, bpred), NotUP1D(QuantitativeInvisibilityUP1D(0)))
+shaders_list = [
+ ConstantThicknessShader(2),
+ ConstantColorShader(0, 0, 0, 1),
+ ]
+Operators.create(pyDensityUP1D(1, 0.1, IntegrationType.MEAN), shaders_list)
diff --git a/release/scripts/freestyle/style_modules/apriori_density.py b/release/scripts/freestyle/style_modules/apriori_density.py
new file mode 100644
index 00000000000..575f2b92a25
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/apriori_density.py
@@ -0,0 +1,37 @@
+# ##### 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 : apriori_density.py
+# Author : Stephane Grabli
+# Date : 04/08/2005
+# Purpose : Draws lines having a high a priori density
+
+from Freestyle import ChainPredicateIterator, ConstantColorShader, ConstantThicknessShader, Operators, \
+ QuantitativeInvisibilityUP1D, TrueBP1D, TrueUP1D
+from PredicatesU1D import pyHighViewMapDensityUP1D
+from logical_operators import AndUP1D, NotUP1D
+
+Operators.select(AndUP1D(QuantitativeInvisibilityUP1D(0), pyHighViewMapDensityUP1D(0.1,5)))
+bpred = TrueBP1D()
+upred = AndUP1D(QuantitativeInvisibilityUP1D(0), pyHighViewMapDensityUP1D(0.0007,5))
+Operators.bidirectional_chain(ChainPredicateIterator(upred, bpred), NotUP1D(QuantitativeInvisibilityUP1D(0)))
+shaders_list = [
+ ConstantThicknessShader(2),
+ ConstantColorShader(0.0, 0.0, 0.0,1)
+ ]
+Operators.create(TrueUP1D(), shaders_list)
diff --git a/release/scripts/freestyle/style_modules/backbone_stretcher.py b/release/scripts/freestyle/style_modules/backbone_stretcher.py
new file mode 100644
index 00000000000..1e113bec368
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/backbone_stretcher.py
@@ -0,0 +1,35 @@
+# ##### 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 : backbone_stretcher.py
+# Author : Stephane Grabli
+# Date : 04/08/2005
+# Purpose : Stretches the geometry of visible lines
+
+from Freestyle import BackboneStretcherShader, ChainSilhouetteIterator, ConstantColorShader, \
+ Operators, QuantitativeInvisibilityUP1D, TextureAssignerShader, TrueUP1D
+from logical_operators import NotUP1D
+
+Operators.select(QuantitativeInvisibilityUP1D(0))
+Operators.bidirectional_chain(ChainSilhouetteIterator(), NotUP1D(QuantitativeInvisibilityUP1D(0)))
+shaders_list = [
+ TextureAssignerShader(4),
+ ConstantColorShader(0.5, 0.5, 0.5),
+ BackboneStretcherShader(20),
+ ]
+Operators.create(TrueUP1D(), shaders_list)
diff --git a/release/scripts/freestyle/style_modules/blueprint_circles.py b/release/scripts/freestyle/style_modules/blueprint_circles.py
new file mode 100644
index 00000000000..769615e791f
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/blueprint_circles.py
@@ -0,0 +1,42 @@
+# ##### 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 : blueprint_circles.py
+# Author : Emmanuel Turquin
+# Date : 04/08/2005
+# Purpose : Produces a blueprint using circular contour strokes
+
+from Freestyle import ChainPredicateIterator, ConstantThicknessShader, ContourUP1D, IncreasingColorShader, \
+ Operators, QuantitativeInvisibilityUP1D, SameShapeIdBP1D, TextureAssignerShader, TrueUP1D
+from PredicatesU1D import pyHigherLengthUP1D
+from logical_operators import AndUP1D, NotUP1D
+from shaders import pyBluePrintCirclesShader, pyPerlinNoise1DShader
+
+upred = AndUP1D(QuantitativeInvisibilityUP1D(0), ContourUP1D())
+bpred = SameShapeIdBP1D()
+Operators.select(upred)
+Operators.bidirectional_chain(ChainPredicateIterator(upred,bpred), NotUP1D(upred))
+Operators.select(pyHigherLengthUP1D(200))
+shaders_list = [
+ ConstantThicknessShader(5),
+ pyBluePrintCirclesShader(3),
+ pyPerlinNoise1DShader(0.1, 15, 8),
+ TextureAssignerShader(4),
+ IncreasingColorShader(0.8, 0.8, 0.3, 0.4, 0.3, 0.3, 0.3, 0.1),
+ ]
+Operators.create(TrueUP1D(), shaders_list)
diff --git a/release/scripts/freestyle/style_modules/blueprint_ellipses.py b/release/scripts/freestyle/style_modules/blueprint_ellipses.py
new file mode 100644
index 00000000000..70c999fa2c7
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/blueprint_ellipses.py
@@ -0,0 +1,42 @@
+# ##### 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 : blueprint_ellipses.py
+# Author : Emmanuel Turquin
+# Date : 04/08/2005
+# Purpose : Produces a blueprint using elliptic contour strokes
+
+from Freestyle import ChainPredicateIterator, ConstantThicknessShader, ContourUP1D, IncreasingColorShader, \
+ Operators, QuantitativeInvisibilityUP1D, SameShapeIdBP1D, TextureAssignerShader, TrueUP1D
+from PredicatesU1D import pyHigherLengthUP1D
+from logical_operators import AndUP1D, NotUP1D
+from shaders import pyBluePrintEllipsesShader, pyPerlinNoise1DShader
+
+upred = AndUP1D(QuantitativeInvisibilityUP1D(0), ContourUP1D())
+bpred = SameShapeIdBP1D()
+Operators.select(upred)
+Operators.bidirectional_chain(ChainPredicateIterator(upred, bpred), NotUP1D(upred))
+Operators.select(pyHigherLengthUP1D(200))
+shaders_list = [
+ ConstantThicknessShader(5),
+ pyBluePrintEllipsesShader(3),
+ pyPerlinNoise1DShader(0.1, 10, 8),
+ TextureAssignerShader(4),
+ IncreasingColorShader(0.6, 0.3, 0.3, 0.7, 0.3, 0.3, 0.3, 0.1),
+ ]
+Operators.create(TrueUP1D(), shaders_list)
diff --git a/release/scripts/freestyle/style_modules/blueprint_squares.py b/release/scripts/freestyle/style_modules/blueprint_squares.py
new file mode 100644
index 00000000000..7193beb8de5
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/blueprint_squares.py
@@ -0,0 +1,43 @@
+# ##### 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 : blueprint_squares.py
+# Author : Emmanuel Turquin
+# Date : 04/08/2005
+# Purpose : Produces a blueprint using square contour strokes
+
+from Freestyle import ChainPredicateIterator, ConstantThicknessShader, ContourUP1D, IncreasingColorShader, \
+ Operators, QuantitativeInvisibilityUP1D, SameShapeIdBP1D, TextureAssignerShader, TrueUP1D
+from PredicatesU1D import pyHigherLengthUP1D
+from logical_operators import AndUP1D, NotUP1D
+from shaders import pyBluePrintSquaresShader, pyPerlinNoise1DShader
+
+upred = AndUP1D(QuantitativeInvisibilityUP1D(0), ContourUP1D())
+bpred = SameShapeIdBP1D()
+Operators.select(upred)
+Operators.bidirectional_chain(ChainPredicateIterator(upred, bpred), NotUP1D(upred))
+Operators.select(pyHigherLengthUP1D(200))
+shaders_list = [
+ ConstantThicknessShader(8),
+ pyBluePrintSquaresShader(2, 20),
+ pyPerlinNoise1DShader(0.07, 10, 8),
+ TextureAssignerShader(4),
+ IncreasingColorShader(0.6, 0.3, 0.3, 0.7, 0.6, 0.3, 0.3, 0.3),
+ ConstantThicknessShader(4),
+ ]
+Operators.create(TrueUP1D(), shaders_list)
diff --git a/release/scripts/freestyle/style_modules/cartoon.py b/release/scripts/freestyle/style_modules/cartoon.py
new file mode 100644
index 00000000000..61bc11520a1
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/cartoon.py
@@ -0,0 +1,38 @@
+# ##### 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 : cartoon.py
+# Author : Stephane Grabli
+# Date : 04/08/2005
+# Purpose : Draws colored lines. The color is automatically
+# infered from each object's material in a cartoon-like
+# fashion.
+
+from Freestyle import BezierCurveShader, ChainSilhouetteIterator, ConstantThicknessShader, Operators, \
+ QuantitativeInvisibilityUP1D, TrueUP1D
+from logical_operators import NotUP1D
+from shaders import pyMaterialColorShader
+
+Operators.select(QuantitativeInvisibilityUP1D(0))
+Operators.bidirectional_chain(ChainSilhouetteIterator(), NotUP1D(QuantitativeInvisibilityUP1D(0)))
+shaders_list = [
+ BezierCurveShader(3),
+ ConstantThicknessShader(4),
+ pyMaterialColorShader(0.8),
+ ]
+Operators.create(TrueUP1D(), shaders_list)
diff --git a/release/scripts/freestyle/style_modules/contour.py b/release/scripts/freestyle/style_modules/contour.py
new file mode 100644
index 00000000000..e4eb8c2c3ae
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/contour.py
@@ -0,0 +1,36 @@
+# ##### 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 : contour.py
+# Author : Stephane Grabli
+# Date : 04/08/2005
+# Purpose : Draws each object's visible contour
+
+from Freestyle import ChainPredicateIterator, ConstantThicknessShader, ContourUP1D, IncreasingColorShader, \
+ Operators, QuantitativeInvisibilityUP1D, SameShapeIdBP1D, TrueUP1D
+from logical_operators import AndUP1D, NotUP1D
+
+Operators.select(AndUP1D(QuantitativeInvisibilityUP1D(0), ContourUP1D()))
+bpred = SameShapeIdBP1D()
+upred = AndUP1D(QuantitativeInvisibilityUP1D(0), ContourUP1D())
+Operators.bidirectional_chain(ChainPredicateIterator(upred, bpred), NotUP1D(QuantitativeInvisibilityUP1D(0)))
+shaders_list = [
+ ConstantThicknessShader(5.0),
+ IncreasingColorShader(0.8,0,0,1,0.1,0,0,1),
+ ]
+Operators.create(TrueUP1D(), shaders_list)
diff --git a/release/scripts/freestyle/style_modules/curvature2d.py b/release/scripts/freestyle/style_modules/curvature2d.py
new file mode 100644
index 00000000000..57bc8382220
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/curvature2d.py
@@ -0,0 +1,37 @@
+# ##### 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 : curvature2d.py
+# Author : Stephane Grabli
+# Date : 04/08/2005
+# Purpose : The stroke points are colored in gray levels and depending
+# on the 2d curvature value
+
+from Freestyle import ChainSilhouetteIterator, ConstantThicknessShader, Operators, \
+ QuantitativeInvisibilityUP1D, Stroke, StrokeTextureShader, TrueUP1D
+from logical_operators import NotUP1D
+from shaders import py2DCurvatureColorShader
+
+Operators.select(QuantitativeInvisibilityUP1D(0))
+Operators.bidirectional_chain(ChainSilhouetteIterator(), NotUP1D(QuantitativeInvisibilityUP1D(0)))
+shaders_list = [
+ StrokeTextureShader("smoothAlpha.bmp", Stroke.OPAQUE_MEDIUM, False),
+ ConstantThicknessShader(5),
+ py2DCurvatureColorShader()
+ ]
+Operators.create(TrueUP1D(), shaders_list)
diff --git a/release/scripts/freestyle/style_modules/external_contour.py b/release/scripts/freestyle/style_modules/external_contour.py
new file mode 100644
index 00000000000..28bd28f2a7a
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/external_contour.py
@@ -0,0 +1,36 @@
+# ##### 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 : external_contour.py
+# Author : Stephane Grabli
+# Date : 04/08/2005
+# Purpose : Draws the external contour of the scene
+
+from Freestyle import ChainPredicateIterator, ConstantColorShader, ConstantThicknessShader, \
+ ExternalContourUP1D, Operators, QuantitativeInvisibilityUP1D, TrueBP1D, TrueUP1D
+from logical_operators import AndUP1D, NotUP1D
+
+upred = AndUP1D(QuantitativeInvisibilityUP1D(0), ExternalContourUP1D())
+Operators.select(upred)
+bpred = TrueBP1D()
+Operators.bidirectional_chain(ChainPredicateIterator(upred, bpred), NotUP1D(upred))
+shaders_list = [
+ ConstantThicknessShader(3),
+ ConstantColorShader(0.0, 0.0, 0.0, 1),
+ ]
+Operators.create(TrueUP1D(), shaders_list)
diff --git a/release/scripts/freestyle/style_modules/external_contour_sketchy.py b/release/scripts/freestyle/style_modules/external_contour_sketchy.py
new file mode 100644
index 00000000000..d85e9674e90
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/external_contour_sketchy.py
@@ -0,0 +1,43 @@
+# ##### 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 : external_contour_sketchy.py
+# Author : Stephane Grabli
+# Date : 04/08/2005
+# Purpose : Draws the external contour of the scene using a sketchy
+# chaining iterator (in particular each ViewEdge can be drawn
+# several times
+
+from ChainingIterators import pySketchyChainingIterator
+from Freestyle import ExternalContourUP1D, IncreasingColorShader, IncreasingThicknessShader, \
+ Operators, QuantitativeInvisibilityUP1D, SamplingShader, SmoothingShader, SpatialNoiseShader, \
+ TextureAssignerShader, TrueUP1D
+from logical_operators import AndUP1D, NotUP1D
+
+upred = AndUP1D(QuantitativeInvisibilityUP1D(0), ExternalContourUP1D())
+Operators.select(upred)
+Operators.bidirectional_chain(pySketchyChainingIterator(), NotUP1D(upred))
+shaders_list = [
+ SamplingShader(4),
+ SpatialNoiseShader(10, 150, 2, 1, 1),
+ IncreasingThicknessShader(4, 10),
+ SmoothingShader(400, 0.1, 0, 0.2, 0, 0, 0, 1),
+ IncreasingColorShader(1, 0, 0, 1, 0, 1, 0, 1),
+ TextureAssignerShader(4),
+ ]
+Operators.create(TrueUP1D(), shaders_list)
diff --git a/release/scripts/freestyle/style_modules/external_contour_smooth.py b/release/scripts/freestyle/style_modules/external_contour_smooth.py
new file mode 100644
index 00000000000..f42f3bf3338
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/external_contour_smooth.py
@@ -0,0 +1,39 @@
+# ##### 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 : external_contour_smooth.py
+# Author : Stephane Grabli
+# Date : 04/08/2005
+# Purpose : Draws a smooth external contour
+
+from Freestyle import ChainPredicateIterator, ExternalContourUP1D, IncreasingColorShader, \
+ IncreasingThicknessShader, Operators, QuantitativeInvisibilityUP1D, SamplingShader, \
+ SmoothingShader, TrueBP1D, TrueUP1D
+from logical_operators import AndUP1D, NotUP1D
+
+upred = AndUP1D(QuantitativeInvisibilityUP1D(0), ExternalContourUP1D())
+Operators.select(upred)
+bpred = TrueBP1D()
+Operators.bidirectional_chain(ChainPredicateIterator(upred, bpred), NotUP1D(upred))
+shaders_list = [
+ SamplingShader(2),
+ IncreasingThicknessShader(4,20),
+ IncreasingColorShader(1.0, 0.0, 0.5,1, 0.5,1, 0.3, 1),
+ SmoothingShader(100, 0.05, 0, 0.2, 0, 0, 0, 1),
+ ]
+Operators.create(TrueUP1D(), shaders_list)
diff --git a/release/scripts/freestyle/style_modules/haloing.py b/release/scripts/freestyle/style_modules/haloing.py
new file mode 100644
index 00000000000..ee907ec967f
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/haloing.py
@@ -0,0 +1,46 @@
+# ##### 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 : haloing.py
+# Author : Stephane Grabli
+# Date : 04/08/2005
+# Purpose : This style module selects the lines that
+# are connected (in the image) to a specific
+# object and trims them in order to produce
+# a haloing effect around the target shape
+
+from Freestyle import ChainSilhouetteIterator, Id, IncreasingColorShader, IncreasingThicknessShader, \
+ Operators, QuantitativeInvisibilityUP1D, SamplingShader, TipRemoverShader, TrueUP1D
+from PredicatesU1D import pyIsOccludedByUP1D
+from logical_operators import AndUP1D, NotUP1D
+from shaders import pyTVertexRemoverShader
+
+# id corresponds to the id of the target object
+# (accessed by SHIFT+click)
+id = Id(3,0)
+upred = AndUP1D(QuantitativeInvisibilityUP1D(0), pyIsOccludedByUP1D(id))
+Operators.select(upred)
+Operators.bidirectional_chain(ChainSilhouetteIterator(), NotUP1D(upred))
+shaders_list = [
+ IncreasingThicknessShader(3, 5),
+ IncreasingColorShader(1,0,0, 1,0,1,0,1),
+ SamplingShader(1.0),
+ pyTVertexRemoverShader(),
+ TipRemoverShader(3.0),
+ ]
+Operators.create(TrueUP1D(), shaders_list)
diff --git a/release/scripts/freestyle/style_modules/ignore_small_occlusions.py b/release/scripts/freestyle/style_modules/ignore_small_occlusions.py
new file mode 100644
index 00000000000..aeab1b2e731
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/ignore_small_occlusions.py
@@ -0,0 +1,36 @@
+# ##### 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 : ignore_small_oclusions.py
+# Author : Stephane Grabli
+# Date : 04/08/2005
+# Purpose : The strokes are drawn through small occlusions
+
+from ChainingIterators import pyFillOcclusionsAbsoluteChainingIterator
+from Freestyle import ConstantColorShader, ConstantThicknessShader, Operators, \
+ QuantitativeInvisibilityUP1D, SamplingShader, TrueUP1D
+
+Operators.select(QuantitativeInvisibilityUP1D(0))
+#Operators.bidirectional_chain(pyFillOcclusionsChainingIterator(0.1))
+Operators.bidirectional_chain(pyFillOcclusionsAbsoluteChainingIterator(12))
+shaders_list = [
+ SamplingShader(5.0),
+ ConstantThicknessShader(3),
+ ConstantColorShader(0.0, 0.0, 0.0),
+ ]
+Operators.create(TrueUP1D(), shaders_list)
diff --git a/release/scripts/freestyle/style_modules/invisible_lines.py b/release/scripts/freestyle/style_modules/invisible_lines.py
new file mode 100644
index 00000000000..6a72534d0ac
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/invisible_lines.py
@@ -0,0 +1,37 @@
+# ##### 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 : invisible_lines.py
+# Author : Stephane Grabli
+# Date : 04/08/2005
+# Purpose : Draws all lines whose Quantitative Invisibility
+# is different from 0
+
+from Freestyle import ChainSilhouetteIterator, ConstantColorShader, ConstantThicknessShader, \
+ Operators, QuantitativeInvisibilityUP1D, SamplingShader, TrueUP1D
+from logical_operators import NotUP1D
+
+upred = NotUP1D(QuantitativeInvisibilityUP1D(0))
+Operators.select(upred)
+Operators.bidirectional_chain(ChainSilhouetteIterator(), NotUP1D(upred))
+shaders_list = [
+ SamplingShader(5.0),
+ ConstantThicknessShader(3.0),
+ ConstantColorShader(0.7, 0.7, 0.7),
+ ]
+Operators.create(TrueUP1D(), shaders_list)
diff --git a/release/scripts/freestyle/style_modules/japanese_bigbrush.py b/release/scripts/freestyle/style_modules/japanese_bigbrush.py
new file mode 100644
index 00000000000..ca421536c42
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/japanese_bigbrush.py
@@ -0,0 +1,56 @@
+# ##### 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 : japanese_bigbrush.py
+# Author : Stephane Grabli
+# Date : 04/08/2005
+# Purpose : Simulates a big brush fr oriental painting
+
+from Freestyle import BezierCurveShader, ChainSilhouetteIterator, ConstantColorShader, \
+ ConstantThicknessShader, IntegrationType, Operators, QuantitativeInvisibilityUP1D, \
+ SamplingShader, TextureAssignerShader, TipRemoverShader
+from Functions0D import pyInverseCurvature2DAngleF0D
+from PredicatesB1D import pyLengthBP1D
+from PredicatesU0D import pyParameterUP0D
+from PredicatesU1D import pyDensityUP1D, pyHigherLengthUP1D, pyHigherNumberOfTurnsUP1D
+from logical_operators import NotUP1D
+from shaders import pyNonLinearVaryingThicknessShader, pySamplingShader
+
+Operators.select(QuantitativeInvisibilityUP1D(0))
+Operators.bidirectional_chain(ChainSilhouetteIterator(), NotUP1D(QuantitativeInvisibilityUP1D(0)))
+## Splits strokes at points of highest 2D curavture
+## when there are too many abrupt turns in it
+func = pyInverseCurvature2DAngleF0D()
+Operators.recursive_split(func, pyParameterUP0D(0.2, 0.8), NotUP1D(pyHigherNumberOfTurnsUP1D(3, 0.5)), 2)
+## Keeps only long enough strokes
+Operators.select(pyHigherLengthUP1D(100))
+## Sorts so as to draw the longest strokes first
+## (this will be done using the causal density)
+Operators.sort(pyLengthBP1D())
+shaders_list = [
+ pySamplingShader(10),
+ BezierCurveShader(30),
+ SamplingShader(50),
+ ConstantThicknessShader(10),
+ pyNonLinearVaryingThicknessShader(4, 25, 0.6),
+ TextureAssignerShader(6),
+ ConstantColorShader(0.2, 0.2, 0.2,1.0),
+ TipRemoverShader(10),
+ ]
+## Use the causal density to avoid cluttering
+Operators.create(pyDensityUP1D(8, 0.4, IntegrationType.MEAN), shaders_list)
diff --git a/release/scripts/freestyle/style_modules/logical_operators.py b/release/scripts/freestyle/style_modules/logical_operators.py
new file mode 100644
index 00000000000..cffb9e1cc58
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/logical_operators.py
@@ -0,0 +1,47 @@
+# ##### 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 : logical_operators.py
+# Authors : Fredo Durand, Stephane Grabli, Francois Sillion, Emmanuel Turquin
+# Date : 08/04/2005
+# Purpose : Logical unary predicates (functors) for 1D elements
+
+from Freestyle import UnaryPredicate1D
+
+class AndUP1D(UnaryPredicate1D):
+ def __init__(self, pred1, pred2):
+ UnaryPredicate1D.__init__(self)
+ self.__pred1 = pred1
+ self.__pred2 = pred2
+ def __call__(self, inter):
+ return self.__pred1(inter) and self.__pred2(inter)
+
+class OrUP1D(UnaryPredicate1D):
+ def __init__(self, pred1, pred2):
+ UnaryPredicate1D.__init__(self)
+ self.__pred1 = pred1
+ self.__pred2 = pred2
+ def __call__(self, inter):
+ return self.__pred1(inter) or self.__pred2(inter)
+
+class NotUP1D(UnaryPredicate1D):
+ def __init__(self, pred):
+ UnaryPredicate1D.__init__(self)
+ self.__pred = pred
+ def __call__(self, inter):
+ return not self.__pred(inter)
diff --git a/release/scripts/freestyle/style_modules/long_anisotropically_dense.py b/release/scripts/freestyle/style_modules/long_anisotropically_dense.py
new file mode 100644
index 00000000000..90fc84c5dae
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/long_anisotropically_dense.py
@@ -0,0 +1,68 @@
+# ##### 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 : long_anisotropically_dense.py
+# Author : Stephane Grabli
+# Date : 04/08/2005
+# Purpose : Selects the lines that are long and have a high anisotropic
+# a priori density and uses causal density
+# to draw without cluttering. Ideally, half of the
+# selected lines are culled using the causal density.
+#
+# ********************* WARNING *************************************
+# ******** The Directional a priori density maps must ******
+# ******** have been computed prior to using this style module ******
+
+from Freestyle import ChainSilhouetteIterator, ConstantColorShader, ConstantThicknessShader, DensityF1D, \
+ IntegrationType, Operators, QuantitativeInvisibilityUP1D, SamplingShader, UnaryPredicate1D
+from PredicatesB1D import pyLengthBP1D
+from PredicatesU1D import pyHighDensityAnisotropyUP1D, pyHigherLengthUP1D
+from logical_operators import NotUP1D
+
+## custom density predicate
+class pyDensityUP1D(UnaryPredicate1D):
+ def __init__(self, wsize, threshold, integration=IntegrationType.MEAN, sampling=2.0):
+ UnaryPredicate1D.__init__(self)
+ self._wsize = wsize
+ self._threshold = threshold
+ self._integration = integration
+ self._func = DensityF1D(self._wsize, self._integration, sampling)
+ self._func2 = DensityF1D(self._wsize, IntegrationType.MAX, sampling)
+ def __call__(self, inter):
+ c = self._func(inter)
+ m = self._func2(inter)
+ if c < self._threshold:
+ return 1
+ if m > 4*c:
+ if c < 1.5*self._threshold:
+ return 1
+ return 0
+
+Operators.select(QuantitativeInvisibilityUP1D(0))
+Operators.bidirectional_chain(ChainSilhouetteIterator(),NotUP1D(QuantitativeInvisibilityUP1D(0)))
+Operators.select(pyHigherLengthUP1D(40))
+## selects lines having a high anisotropic a priori density
+Operators.select(pyHighDensityAnisotropyUP1D(0.3,4))
+Operators.sort(pyLengthBP1D())
+shaders_list = [
+ SamplingShader(2.0),
+ ConstantThicknessShader(2),
+ ConstantColorShader(0.2,0.2,0.25,1),
+ ]
+## uniform culling
+Operators.create(pyDensityUP1D(3.0,2.0e-2, IntegrationType.MEAN, 0.1), shaders_list)
diff --git a/release/scripts/freestyle/style_modules/multiple_parameterization.py b/release/scripts/freestyle/style_modules/multiple_parameterization.py
new file mode 100644
index 00000000000..2296a9735ce
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/multiple_parameterization.py
@@ -0,0 +1,47 @@
+# ##### 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 : multiple_parameterization.py
+# Author : Stephane Grabli
+# Date : 04/08/2005
+# Purpose : The thickness and the color of the strokes vary continuously
+# independently from occlusions although only
+# visible lines are actually drawn. This is equivalent
+# to assigning the thickness using a parameterization covering
+# the complete silhouette (visible+invisible) and drawing
+# the strokes using a second parameterization that only
+# covers the visible portions.
+
+from Freestyle import ChainSilhouetteIterator, ConstantColorShader, IncreasingColorShader, \
+ IncreasingThicknessShader, Operators, QuantitativeInvisibilityUP1D, SamplingShader, \
+ TextureAssignerShader, TrueUP1D
+from shaders import pyHLRShader
+
+Operators.select(QuantitativeInvisibilityUP1D(0))
+## Chain following the same nature, but without the restriction
+## of staying inside the selection (0).
+Operators.bidirectional_chain(ChainSilhouetteIterator(0))
+shaders_list = [
+ SamplingShader(20),
+ IncreasingThicknessShader(1.5, 30),
+ ConstantColorShader(0.0, 0.0, 0.0),
+ IncreasingColorShader(1, 0, 0, 1, 0, 1, 0, 1),
+ TextureAssignerShader(-1),
+ pyHLRShader(), ## this shader draws only visible portions
+ ]
+Operators.create(TrueUP1D(), shaders_list)
diff --git a/release/scripts/freestyle/style_modules/nature.py b/release/scripts/freestyle/style_modules/nature.py
new file mode 100644
index 00000000000..dd520c5c85f
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/nature.py
@@ -0,0 +1,39 @@
+# ##### 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 : nature.py
+# Author : Stephane Grabli
+# Date : 04/08/2005
+# Purpose : Uses the NatureUP1D predicate to select the lines
+# of a given type (among Nature.SILHOUETTE, Nature.CREASE, Nature.SUGGESTIVE_CONTOURS,
+# Nature.BORDERS).
+# The suggestive contours must have been enabled in the
+# options dialog to appear in the View Map.
+
+from Freestyle import ChainSilhouetteIterator, IncreasingColorShader, \
+ IncreasingThicknessShader, Nature, Operators, TrueUP1D
+from PredicatesU1D import pyNatureUP1D
+from logical_operators import NotUP1D
+
+Operators.select(pyNatureUP1D(Nature.SILHOUETTE))
+Operators.bidirectional_chain(ChainSilhouetteIterator(), NotUP1D(pyNatureUP1D(Nature.SILHOUETTE)))
+shaders_list = [
+ IncreasingThicknessShader(3, 10),
+ IncreasingColorShader(0.0, 0.0, 0.0, 1, 0.8, 0, 0, 1),
+ ]
+Operators.create(TrueUP1D(), shaders_list)
diff --git a/release/scripts/freestyle/style_modules/near_lines.py b/release/scripts/freestyle/style_modules/near_lines.py
new file mode 100644
index 00000000000..d2dab89d478
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/near_lines.py
@@ -0,0 +1,38 @@
+# ##### 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 : near_lines.py
+# Author : Stephane Grabli
+# Date : 04/08/2005
+# Purpose : Draws the lines that are "closer" than a threshold
+# (between 0 and 1)
+
+from Freestyle import ChainSilhouetteIterator, ConstantColorShader, ConstantThicknessShader, \
+ IntegrationType, Operators, QuantitativeInvisibilityUP1D, TextureAssignerShader, TrueUP1D
+from PredicatesU1D import pyZSmallerUP1D
+from logical_operators import AndUP1D, NotUP1D
+
+upred = AndUP1D(QuantitativeInvisibilityUP1D(0), pyZSmallerUP1D(0.5, IntegrationType.MEAN))
+Operators.select(upred)
+Operators.bidirectional_chain(ChainSilhouetteIterator(), NotUP1D(upred))
+shaders_list = [
+ TextureAssignerShader(-1),
+ ConstantThicknessShader(5),
+ ConstantColorShader(0.0, 0.0, 0.0),
+ ]
+Operators.create(TrueUP1D(), shaders_list)
diff --git a/release/scripts/freestyle/style_modules/occluded_by_specific_object.py b/release/scripts/freestyle/style_modules/occluded_by_specific_object.py
new file mode 100644
index 00000000000..4f8c0e859ed
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/occluded_by_specific_object.py
@@ -0,0 +1,40 @@
+# ##### 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 : occluded_by_specific_object.py
+# Author : Stephane Grabli
+# Date : 04/08/2005
+# Purpose : Draws only the lines that are occluded by a given object
+
+from Freestyle import ChainSilhouetteIterator, ConstantColorShader, ConstantThicknessShader, \
+ Id, Operators, QuantitativeInvisibilityUP1D, SamplingShader, TrueUP1D
+from PredicatesU1D import pyIsInOccludersListUP1D
+from logical_operators import AndUP1D, NotUP1D
+
+## the id of the occluder (use SHIFT+click on the ViewMap to
+## retrieve ids)
+id = Id(3,0)
+upred = AndUP1D(NotUP1D(QuantitativeInvisibilityUP1D(0)), pyIsInOccludersListUP1D(id))
+Operators.select(upred)
+Operators.bidirectional_chain(ChainSilhouetteIterator(), NotUP1D(upred))
+shaders_list = [
+ SamplingShader(5),
+ ConstantThicknessShader(3),
+ ConstantColorShader(0.3, 0.3, 0.3, 1),
+ ]
+Operators.create(TrueUP1D(), shaders_list)
diff --git a/release/scripts/freestyle/style_modules/parameter_editor.py b/release/scripts/freestyle/style_modules/parameter_editor.py
new file mode 100644
index 00000000000..ffb01f06c7b
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/parameter_editor.py
@@ -0,0 +1,1259 @@
+# ##### 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 : parameter_editor.py
+# Authors : Tamito Kajiyama
+# Date : 26/07/2010
+# Purpose : Interactive manipulation of stylization parameters
+
+import Freestyle
+import math
+import mathutils
+import time
+
+from ChainingIterators import pySketchyChainSilhouetteIterator, pySketchyChainingIterator
+from Freestyle import BackboneStretcherShader, BezierCurveShader, BinaryPredicate1D, ChainPredicateIterator, \
+ ChainSilhouetteIterator, ConstantColorShader, ContourUP1D, Curvature2DAngleF0D, ExternalContourUP1D, \
+ FalseBP1D, FalseUP1D, GuidingLinesShader, Interface0DIterator, Nature, Noise, Normal2DF0D, Operators, \
+ PolygonalizationShader, QuantitativeInvisibilityF1D, QuantitativeInvisibilityUP1D, SamplingShader, \
+ SpatialNoiseShader, StrokeAttribute, StrokeShader, TipRemoverShader, TrueBP1D, TrueUP1D, UnaryPredicate0D, \
+ UnaryPredicate1D, VertexOrientation2DF0D, WithinImageBoundaryUP1D
+from Functions0D import CurveMaterialF0D
+from PredicatesU1D import pyNatureUP1D
+from logical_operators import AndUP1D, NotUP1D, OrUP1D
+from shaders import pyBluePrintCirclesShader, pyBluePrintEllipsesShader, pyBluePrintSquaresShader
+
+class ColorRampModifier(StrokeShader):
+ def __init__(self, blend, influence, ramp):
+ StrokeShader.__init__(self)
+ self.__blend = blend
+ self.__influence = influence
+ self.__ramp = ramp
+ def evaluate(self, t):
+ col = Freestyle.evaluateColorRamp(self.__ramp, t)
+ col = col.xyz # omit alpha
+ return col
+ def blend_ramp(self, a, b):
+ return Freestyle.blendRamp(self.__blend, a, self.__influence, b)
+
+class ScalarBlendModifier(StrokeShader):
+ def __init__(self, blend, influence):
+ StrokeShader.__init__(self)
+ self.__blend = blend
+ self.__influence = influence
+ def blend(self, v1, v2):
+ fac = self.__influence
+ facm = 1.0 - fac
+ if self.__blend == "MIX":
+ v1 = facm * v1 + fac * v2
+ elif self.__blend == "ADD":
+ v1 += fac * v2
+ elif self.__blend == "MULTIPLY":
+ v1 *= facm + fac * v2;
+ elif self.__blend == "SUBTRACT":
+ v1 -= fac * v2
+ elif self.__blend == "DIVIDE":
+ if v2 != 0.0:
+ v1 = facm * v1 + fac * v1 / v2
+ elif self.__blend == "DIFFERENCE":
+ v1 = facm * v1 + fac * abs(v1 - v2)
+ elif self.__blend == "MININUM":
+ tmp = fac * v1
+ if v1 > tmp:
+ v1 = tmp
+ elif self.__blend == "MAXIMUM":
+ tmp = fac * v1
+ if v1 < tmp:
+ v1 = tmp
+ else:
+ raise ValueError("unknown curve blend type: " + self.__blend)
+ return v1
+
+class CurveMappingModifier(ScalarBlendModifier):
+ def __init__(self, blend, influence, mapping, invert, curve):
+ ScalarBlendModifier.__init__(self, blend, influence)
+ assert mapping in ("LINEAR", "CURVE")
+ self.__mapping = getattr(self, mapping)
+ self.__invert = invert
+ self.__curve = curve
+ def LINEAR(self, t):
+ if self.__invert:
+ return 1.0 - t
+ return t
+ def CURVE(self, t):
+ return Freestyle.evaluateCurveMappingF(self.__curve, 0, t)
+ def evaluate(self, t):
+ return self.__mapping(t)
+
+class ThicknessModifierMixIn:
+ def __init__(self):
+ scene = Freestyle.getCurrentScene()
+ self.__persp_camera = (scene.camera.data.type == "PERSP")
+ def set_thickness(self, sv, outer, inner):
+ fe = sv.first_svertex.get_fedge(sv.second_svertex)
+ nature = fe.nature
+ if (nature & Nature.BORDER):
+ if self.__persp_camera:
+ point = -sv.point_3d.copy()
+ point.normalize()
+ dir = point.dot(fe.normal_left)
+ else:
+ dir = fe.normal_left.z
+ if dir < 0.0: # the back side is visible
+ outer, inner = inner, outer
+ elif (nature & Nature.SILHOUETTE):
+ if fe.is_smooth: # TODO more tests needed
+ outer, inner = inner, outer
+ else:
+ outer = inner = (outer + inner) / 2
+ sv.attribute.thickness = (outer, inner)
+
+class ThicknessBlenderMixIn(ThicknessModifierMixIn):
+ def __init__(self, position, ratio):
+ ThicknessModifierMixIn.__init__(self)
+ self.__position = position
+ self.__ratio = ratio
+ def blend_thickness(self, outer, inner, v):
+ if self.__position == "CENTER":
+ outer = self.blend(outer, v / 2)
+ inner = self.blend(inner, v / 2)
+ elif self.__position == "INSIDE":
+ outer = self.blend(outer, 0)
+ inner = self.blend(inner, v)
+ elif self.__position == "OUTSIDE":
+ outer = self.blend(outer, v)
+ inner = self.blend(inner, 0)
+ elif self.__position == "RELATIVE":
+ outer = self.blend(outer, v * self.__ratio)
+ inner = self.blend(inner, v * (1 - self.__ratio))
+ else:
+ raise ValueError("unknown thickness position: " + self.__position)
+ return outer, inner
+
+class BaseColorShader(ConstantColorShader):
+ pass
+
+class BaseThicknessShader(StrokeShader, ThicknessModifierMixIn):
+ def __init__(self, thickness, position, ratio):
+ StrokeShader.__init__(self)
+ ThicknessModifierMixIn.__init__(self)
+ if position == "CENTER":
+ self.__outer = thickness / 2
+ self.__inner = thickness / 2
+ elif position == "INSIDE":
+ self.__outer = 0
+ self.__inner = thickness
+ elif position == "OUTSIDE":
+ self.__outer = thickness
+ self.__inner = 0
+ elif position == "RELATIVE":
+ self.__outer = thickness * ratio
+ self.__inner = thickness * (1 - ratio)
+ else:
+ raise ValueError("unknown thickness position: " + self.position)
+ def shade(self, stroke):
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ sv = it.object
+ self.set_thickness(sv, self.__outer, self.__inner)
+ it.increment()
+
+# Along Stroke modifiers
+
+def iter_t2d_along_stroke(stroke):
+ total = stroke.length_2d
+ distance = 0.0
+ it = stroke.stroke_vertices_begin()
+ prev = it.object.point
+ while not it.is_end:
+ p = it.object.point
+ distance += (prev - p).length
+ prev = p.copy() # need a copy because the point can be altered
+ t = min(distance / total, 1.0)
+ yield it, t
+ it.increment()
+
+class ColorAlongStrokeShader(ColorRampModifier):
+ def shade(self, stroke):
+ for it, t in iter_t2d_along_stroke(stroke):
+ sv = it.object
+ a = sv.attribute.color
+ b = self.evaluate(t)
+ sv.attribute.color = self.blend_ramp(a, b)
+
+class AlphaAlongStrokeShader(CurveMappingModifier):
+ def shade(self, stroke):
+ for it, t in iter_t2d_along_stroke(stroke):
+ sv = it.object
+ a = sv.attribute.alpha
+ b = self.evaluate(t)
+ sv.attribute.alpha = self.blend(a, b)
+
+class ThicknessAlongStrokeShader(ThicknessBlenderMixIn, CurveMappingModifier):
+ def __init__(self, thickness_position, thickness_ratio,
+ blend, influence, mapping, invert, curve, value_min, value_max):
+ ThicknessBlenderMixIn.__init__(self, thickness_position, thickness_ratio)
+ CurveMappingModifier.__init__(self, blend, influence, mapping, invert, curve)
+ self.__value_min = value_min
+ self.__value_max = value_max
+ def shade(self, stroke):
+ for it, t in iter_t2d_along_stroke(stroke):
+ sv = it.object
+ a = sv.attribute.thickness
+ b = self.__value_min + self.evaluate(t) * (self.__value_max - self.__value_min)
+ c = self.blend_thickness(a[0], a[1], b)
+ self.set_thickness(sv, c[0], c[1])
+
+# Distance from Camera modifiers
+
+def iter_distance_from_camera(stroke, range_min, range_max):
+ normfac = range_max - range_min # normalization factor
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ p = it.object.point_3d # in the camera coordinate
+ distance = p.length
+ if distance < range_min:
+ t = 0.0
+ elif distance > range_max:
+ t = 1.0
+ else:
+ t = (distance - range_min) / normfac
+ yield it, t
+ it.increment()
+
+class ColorDistanceFromCameraShader(ColorRampModifier):
+ def __init__(self, blend, influence, ramp, range_min, range_max):
+ ColorRampModifier.__init__(self, blend, influence, ramp)
+ self.__range_min = range_min
+ self.__range_max = range_max
+ def shade(self, stroke):
+ for it, t in iter_distance_from_camera(stroke, self.__range_min, self.__range_max):
+ sv = it.object
+ a = sv.attribute.color
+ b = self.evaluate(t)
+ sv.attribute.color = self.blend_ramp(a, b)
+
+class AlphaDistanceFromCameraShader(CurveMappingModifier):
+ def __init__(self, blend, influence, mapping, invert, curve, range_min, range_max):
+ CurveMappingModifier.__init__(self, blend, influence, mapping, invert, curve)
+ self.__range_min = range_min
+ self.__range_max = range_max
+ def shade(self, stroke):
+ for it, t in iter_distance_from_camera(stroke, self.__range_min, self.__range_max):
+ sv = it.object
+ a = sv.attribute.alpha
+ b = self.evaluate(t)
+ sv.attribute.alpha = self.blend(a, b)
+
+class ThicknessDistanceFromCameraShader(ThicknessBlenderMixIn, CurveMappingModifier):
+ def __init__(self, thickness_position, thickness_ratio,
+ blend, influence, mapping, invert, curve, range_min, range_max, value_min, value_max):
+ ThicknessBlenderMixIn.__init__(self, thickness_position, thickness_ratio)
+ CurveMappingModifier.__init__(self, blend, influence, mapping, invert, curve)
+ self.__range_min = range_min
+ self.__range_max = range_max
+ self.__value_min = value_min
+ self.__value_max = value_max
+ def shade(self, stroke):
+ for it, t in iter_distance_from_camera(stroke, self.__range_min, self.__range_max):
+ sv = it.object
+ a = sv.attribute.thickness
+ b = self.__value_min + self.evaluate(t) * (self.__value_max - self.__value_min)
+ c = self.blend_thickness(a[0], a[1], b)
+ self.set_thickness(sv, c[0], c[1])
+
+# Distance from Object modifiers
+
+def iter_distance_from_object(stroke, object, range_min, range_max):
+ scene = Freestyle.getCurrentScene()
+ mv = scene.camera.matrix_world.copy() # model-view matrix
+ mv.invert()
+ loc = mv * object.location # loc in the camera coordinate
+ normfac = range_max - range_min # normalization factor
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ p = it.object.point_3d # in the camera coordinate
+ distance = (p - loc).length
+ if distance < range_min:
+ t = 0.0
+ elif distance > range_max:
+ t = 1.0
+ else:
+ t = (distance - range_min) / normfac
+ yield it, t
+ it.increment()
+
+class ColorDistanceFromObjectShader(ColorRampModifier):
+ def __init__(self, blend, influence, ramp, target, range_min, range_max):
+ ColorRampModifier.__init__(self, blend, influence, ramp)
+ self.__target = target
+ self.__range_min = range_min
+ self.__range_max = range_max
+ def shade(self, stroke):
+ if self.__target is None:
+ return
+ for it, t in iter_distance_from_object(stroke, self.__target, self.__range_min, self.__range_max):
+ sv = it.object
+ a = sv.attribute.color
+ b = self.evaluate(t)
+ sv.attribute.color = self.blend_ramp(a, b)
+
+class AlphaDistanceFromObjectShader(CurveMappingModifier):
+ def __init__(self, blend, influence, mapping, invert, curve, target, range_min, range_max):
+ CurveMappingModifier.__init__(self, blend, influence, mapping, invert, curve)
+ self.__target = target
+ self.__range_min = range_min
+ self.__range_max = range_max
+ def shade(self, stroke):
+ if self.__target is None:
+ return
+ for it, t in iter_distance_from_object(stroke, self.__target, self.__range_min, self.__range_max):
+ sv = it.object
+ a = sv.attribute.alpha
+ b = self.evaluate(t)
+ sv.attribute.alpha = self.blend(a, b)
+
+class ThicknessDistanceFromObjectShader(ThicknessBlenderMixIn, CurveMappingModifier):
+ def __init__(self, thickness_position, thickness_ratio,
+ blend, influence, mapping, invert, curve, target, range_min, range_max, value_min, value_max):
+ ThicknessBlenderMixIn.__init__(self, thickness_position, thickness_ratio)
+ CurveMappingModifier.__init__(self, blend, influence, mapping, invert, curve)
+ self.__target = target
+ self.__range_min = range_min
+ self.__range_max = range_max
+ self.__value_min = value_min
+ self.__value_max = value_max
+ def shade(self, stroke):
+ if self.__target is None:
+ return
+ for it, t in iter_distance_from_object(stroke, self.__target, self.__range_min, self.__range_max):
+ sv = it.object
+ a = sv.attribute.thickness
+ b = self.__value_min + self.evaluate(t) * (self.__value_max - self.__value_min)
+ c = self.blend_thickness(a[0], a[1], b)
+ self.set_thickness(sv, c[0], c[1])
+
+# Material modifiers
+
+def iter_material_color(stroke, material_attr):
+ func = CurveMaterialF0D()
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ material = func(Interface0DIterator(it))
+ if material_attr == "DIFF":
+ color = (material.diffuse[0],
+ material.diffuse[1],
+ material.diffuse[2])
+ elif material_attr == "SPEC":
+ color = (material.specular[0],
+ material.specular[1],
+ material.specular[2])
+ else:
+ raise ValueError("unexpected material attribute: " + material_attr)
+ yield it, color
+ it.increment()
+
+def iter_material_value(stroke, material_attr):
+ func = CurveMaterialF0D()
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ material = func(Interface0DIterator(it))
+ if material_attr == "DIFF":
+ r = material.diffuse[0]
+ g = material.diffuse[1]
+ b = material.diffuse[2]
+ t = 0.35 * r + 0.45 * r + 0.2 * b
+ elif material_attr == "DIFF_R":
+ t = material.diffuse[0]
+ elif material_attr == "DIFF_G":
+ t = material.diffuse[1]
+ elif material_attr == "DIFF_B":
+ t = material.diffuse[2]
+ elif material_attr == "SPEC":
+ r = material.specular[0]
+ g = material.specular[1]
+ b = material.specular[2]
+ t = 0.35 * r + 0.45 * r + 0.2 * b
+ elif material_attr == "SPEC_R":
+ t = material.specular[0]
+ elif material_attr == "SPEC_G":
+ t = material.specular[1]
+ elif material_attr == "SPEC_B":
+ t = material.specular[2]
+ elif material_attr == "SPEC_HARDNESS":
+ t = material.shininess
+ elif material_attr == "ALPHA":
+ t = material.diffuse[3]
+ else:
+ raise ValueError("unexpected material attribute: " + material_attr)
+ yield it, t
+ it.increment()
+
+class ColorMaterialShader(ColorRampModifier):
+ def __init__(self, blend, influence, ramp, material_attr, use_ramp):
+ ColorRampModifier.__init__(self, blend, influence, ramp)
+ self.__material_attr = material_attr
+ self.__use_ramp = use_ramp
+ def shade(self, stroke):
+ if self.__material_attr in ["DIFF", "SPEC"] and not self.__use_ramp:
+ for it, b in iter_material_color(stroke, self.__material_attr):
+ sv = it.object
+ a = sv.attribute.color
+ sv.attribute.color = self.blend_ramp(a, b)
+ else:
+ for it, t in iter_material_value(stroke, self.__material_attr):
+ sv = it.object
+ a = sv.attribute.color
+ b = self.evaluate(t)
+ sv.attribute.color = self.blend_ramp(a, b)
+
+class AlphaMaterialShader(CurveMappingModifier):
+ def __init__(self, blend, influence, mapping, invert, curve, material_attr):
+ CurveMappingModifier.__init__(self, blend, influence, mapping, invert, curve)
+ self.__material_attr = material_attr
+ def shade(self, stroke):
+ for it, t in iter_material_value(stroke, self.__material_attr):
+ sv = it.object
+ a = sv.attribute.alpha
+ b = self.evaluate(t)
+ sv.attribute.alpha = self.blend(a, b)
+
+class ThicknessMaterialShader(ThicknessBlenderMixIn, CurveMappingModifier):
+ def __init__(self, thickness_position, thickness_ratio,
+ blend, influence, mapping, invert, curve, material_attr, value_min, value_max):
+ ThicknessBlenderMixIn.__init__(self, thickness_position, thickness_ratio)
+ CurveMappingModifier.__init__(self, blend, influence, mapping, invert, curve)
+ self.__material_attr = material_attr
+ self.__value_min = value_min
+ self.__value_max = value_max
+ def shade(self, stroke):
+ for it, t in iter_material_value(stroke, self.__material_attr):
+ sv = it.object
+ a = sv.attribute.thickness
+ b = self.__value_min + self.evaluate(t) * (self.__value_max - self.__value_min)
+ c = self.blend_thickness(a[0], a[1], b)
+ self.set_thickness(sv, c[0], c[1])
+
+# Calligraphic thickness modifier
+
+class CalligraphicThicknessShader(ThicknessBlenderMixIn, ScalarBlendModifier):
+ def __init__(self, thickness_position, thickness_ratio,
+ blend, influence, orientation, min_thickness, max_thickness):
+ ThicknessBlenderMixIn.__init__(self, thickness_position, thickness_ratio)
+ ScalarBlendModifier.__init__(self, blend, influence)
+ self.__orientation = mathutils.Vector((math.cos(orientation), math.sin(orientation)))
+ self.__min_thickness = min_thickness
+ self.__max_thickness = max_thickness
+ def shade(self, stroke):
+ func = VertexOrientation2DF0D()
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ dir = func(Interface0DIterator(it))
+ orthDir = mathutils.Vector((-dir.y, dir.x))
+ orthDir.normalize()
+ fac = abs(orthDir * self.__orientation)
+ sv = it.object
+ a = sv.attribute.thickness
+ b = self.__min_thickness + fac * (self.__max_thickness - self.__min_thickness)
+ b = max(b, 0.0)
+ c = self.blend_thickness(a[0], a[1], b)
+ self.set_thickness(sv, c[0], c[1])
+ it.increment()
+
+# Geometry modifiers
+
+def iter_distance_along_stroke(stroke):
+ distance = 0.0
+ it = stroke.stroke_vertices_begin()
+ prev = it.object.point
+ while not it.is_end:
+ p = it.object.point
+ distance += (prev - p).length
+ prev = p.copy() # need a copy because the point can be altered
+ yield it, distance
+ it.increment()
+
+class SinusDisplacementShader(StrokeShader):
+ def __init__(self, wavelength, amplitude, phase):
+ StrokeShader.__init__(self)
+ self._wavelength = wavelength
+ self._amplitude = amplitude
+ self._phase = phase / wavelength * 2 * math.pi
+ self._getNormal = Normal2DF0D()
+ def shade(self, stroke):
+ for it, distance in iter_distance_along_stroke(stroke):
+ v = it.object
+ n = self._getNormal(Interface0DIterator(it))
+ n = n * self._amplitude * math.cos(distance / self._wavelength * 2 * math.pi + self._phase)
+ v.point = v.point + n
+ stroke.update_length()
+
+class PerlinNoise1DShader(StrokeShader):
+ def __init__(self, freq = 10, amp = 10, oct = 4, angle = math.radians(45), seed = -1):
+ StrokeShader.__init__(self)
+ self.__noise = Noise(seed)
+ self.__freq = freq
+ self.__amp = amp
+ self.__oct = oct
+ self.__dir = mathutils.Vector([math.cos(angle), math.sin(angle)])
+ def shade(self, stroke):
+ length = stroke.length_2d
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ v = it.object
+ nres = self.__noise.turbulence1(length * v.u, self.__freq, self.__amp, self.__oct)
+ v.point = v.point + nres * self.__dir
+ it.increment()
+ stroke.update_length()
+
+class PerlinNoise2DShader(StrokeShader):
+ def __init__(self, freq = 10, amp = 10, oct = 4, angle = math.radians(45), seed = -1):
+ StrokeShader.__init__(self)
+ self.__noise = Noise(seed)
+ self.__freq = freq
+ self.__amp = amp
+ self.__oct = oct
+ self.__dir = mathutils.Vector([math.cos(angle), math.sin(angle)])
+ def shade(self, stroke):
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ v = it.object
+ vec = mathutils.Vector([v.projected_x, v.projected_y])
+ nres = self.__noise.turbulence2(vec, self.__freq, self.__amp, self.__oct)
+ v.point = v.point + nres * self.__dir
+ it.increment()
+ stroke.update_length()
+
+class Offset2DShader(StrokeShader):
+ def __init__(self, start, end, x, y):
+ StrokeShader.__init__(self)
+ self.__start = start
+ self.__end = end
+ self.__xy = mathutils.Vector([x, y])
+ self.__getNormal = Normal2DF0D()
+ def shade(self, stroke):
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ v = it.object
+ u = v.u
+ a = self.__start + u * (self.__end - self.__start)
+ n = self.__getNormal(Interface0DIterator(it))
+ n = n * a
+ v.point = v.point + n + self.__xy
+ it.increment()
+ stroke.update_length()
+
+class Transform2DShader(StrokeShader):
+ def __init__(self, pivot, scale_x, scale_y, angle, pivot_u, pivot_x, pivot_y):
+ StrokeShader.__init__(self)
+ self.__pivot = pivot
+ self.__scale_x = scale_x
+ self.__scale_y = scale_y
+ self.__angle = angle
+ self.__pivot_u = pivot_u
+ self.__pivot_x = pivot_x
+ self.__pivot_y = pivot_y
+ def shade(self, stroke):
+ # determine the pivot of scaling and rotation operations
+ if self.__pivot == "START":
+ it = stroke.stroke_vertices_begin()
+ pivot = it.object.point
+ elif self.__pivot == "END":
+ it = stroke.stroke_vertices_end()
+ it.decrement()
+ pivot = it.object.point
+ elif self.__pivot == "PARAM":
+ p = None
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ prev = p
+ v = it.object
+ p = v.point
+ u = v.u
+ if self.__pivot_u < u:
+ break
+ it.increment()
+ if prev is None:
+ pivot = p
+ else:
+ delta = u - self.__pivot_u
+ pivot = p + delta * (prev - p)
+ elif self.__pivot == "CENTER":
+ pivot = mathutils.Vector([0.0, 0.0])
+ n = 0
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ p = it.object.point
+ pivot = pivot + p
+ n = n + 1
+ it.increment()
+ pivot.x = pivot.x / n
+ pivot.y = pivot.y / n
+ elif self.__pivot == "ABSOLUTE":
+ pivot = mathutils.Vector([self.__pivot_x, self.__pivot_y])
+ # apply scaling and rotation operations
+ cos_theta = math.cos(self.__angle)
+ sin_theta = math.sin(self.__angle)
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ v = it.object
+ p = v.point
+ p = p - pivot
+ x = p.x * self.__scale_x
+ y = p.y * self.__scale_y
+ p.x = x * cos_theta - y * sin_theta
+ p.y = x * sin_theta + y * cos_theta
+ v.point = p + pivot
+ it.increment()
+ stroke.update_length()
+
+# Predicates and helper functions
+
+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
+
+def join_unary_predicates(upred_list, bpred):
+ if not upred_list:
+ return None
+ upred = upred_list[0]
+ for p in upred_list[1:]:
+ upred = bpred(upred, p)
+ return upred
+
+class ObjectNamesUP1D(UnaryPredicate1D):
+ def __init__(self, names, negative):
+ UnaryPredicate1D.__init__(self)
+ self._names = names
+ self._negative = negative
+ def __call__(self, viewEdge):
+ found = viewEdge.viewshape.name in self._names
+ if self._negative:
+ return not found
+ return found
+
+# Stroke caps
+
+def iter_stroke_vertices(stroke):
+ it = stroke.stroke_vertices_begin()
+ prev_p = None
+ while not it.is_end:
+ sv = it.object
+ p = sv.point
+ if prev_p is None or (prev_p - p).length > 1e-6:
+ yield sv
+ prev_p = p.copy()
+ it.increment()
+
+class RoundCapShader(StrokeShader):
+ def round_cap_thickness(self, x):
+ x = max(0.0, min(x, 1.0))
+ return math.sqrt(1.0 - (x ** 2))
+ def shade(self, stroke):
+ # save the location and attribute of stroke vertices
+ buffer = []
+ for sv in iter_stroke_vertices(stroke):
+ buffer.append((mathutils.Vector(sv.point), StrokeAttribute(sv.attribute)))
+ nverts = len(buffer)
+ if nverts < 2:
+ return
+ # calculate the number of additional vertices to form caps
+ R, L = stroke[0].attribute.thickness
+ caplen_beg = (R + L) / 2.0
+ nverts_beg = max(5, int(R + L))
+ R, L = stroke[-1].attribute.thickness
+ caplen_end = (R + L) / 2.0
+ nverts_end = max(5, int(R + L))
+ # adjust the total number of stroke vertices
+ stroke.resample(nverts + nverts_beg + nverts_end)
+ # restore the location and attribute of the original vertices
+ for i in range(nverts):
+ p, attr = buffer[i]
+ stroke[nverts_beg + i].point = p
+ stroke[nverts_beg + i].attribute = attr
+ # reshape the cap at the beginning of the stroke
+ q, attr = buffer[1]
+ p, attr = buffer[0]
+ d = p - q
+ d = d / d.length * caplen_beg
+ n = 1.0 / nverts_beg
+ R, L = attr.thickness
+ for i in range(nverts_beg):
+ t = (nverts_beg - i) * n
+ stroke[i].point = p + d * t
+ r = self.round_cap_thickness((nverts_beg - i + 1) * n)
+ stroke[i].attribute = attr
+ stroke[i].attribute.thickness = (R * r, L * r)
+ # reshape the cap at the end of the stroke
+ q, attr = buffer[-2]
+ p, attr = buffer[-1]
+ d = p - q
+ d = d / d.length * caplen_end
+ n = 1.0 / nverts_end
+ R, L = attr.thickness
+ for i in range(nverts_end):
+ t = (nverts_end - i) * n
+ stroke[-i-1].point = p + d * t
+ r = self.round_cap_thickness((nverts_end - i + 1) * n)
+ stroke[-i-1].attribute = attr
+ stroke[-i-1].attribute.thickness = (R * r, L * r)
+ # update the curvilinear 2D length of each vertex
+ stroke.update_length()
+
+class SquareCapShader(StrokeShader):
+ def shade(self, stroke):
+ # save the location and attribute of stroke vertices
+ buffer = []
+ for sv in iter_stroke_vertices(stroke):
+ buffer.append((mathutils.Vector(sv.point), StrokeAttribute(sv.attribute)))
+ nverts = len(buffer)
+ if nverts < 2:
+ return
+ # calculate the number of additional vertices to form caps
+ R, L = stroke[0].attribute.thickness
+ caplen_beg = (R + L) / 2.0
+ nverts_beg = 1
+ R, L = stroke[-1].attribute.thickness
+ caplen_end = (R + L) / 2.0
+ nverts_end = 1
+ # adjust the total number of stroke vertices
+ stroke.resample(nverts + nverts_beg + nverts_end)
+ # restore the location and attribute of the original vertices
+ for i in range(nverts):
+ p, attr = buffer[i]
+ stroke[nverts_beg + i].point = p
+ stroke[nverts_beg + i].attribute = attr
+ # reshape the cap at the beginning of the stroke
+ q, attr = buffer[1]
+ p, attr = buffer[0]
+ d = p - q
+ stroke[0].point = p + d / d.length * caplen_beg
+ stroke[0].attribute = attr
+ # reshape the cap at the end of the stroke
+ q, attr = buffer[-2]
+ p, attr = buffer[-1]
+ d = p - q
+ stroke[-1].point = p + d / d.length * caplen_beg
+ stroke[-1].attribute = attr
+ # update the curvilinear 2D length of each vertex
+ stroke.update_length()
+
+# Split by dashed line pattern
+
+class SplitPatternStartingUP0D(UnaryPredicate0D):
+ def __init__(self, controller):
+ UnaryPredicate0D.__init__(self)
+ self._controller = controller
+ def __call__(self, inter):
+ return self._controller.start()
+
+class SplitPatternStoppingUP0D(UnaryPredicate0D):
+ def __init__(self, controller):
+ UnaryPredicate0D.__init__(self)
+ self._controller = controller
+ def __call__(self, inter):
+ return self._controller.stop()
+
+class SplitPatternController:
+ def __init__(self, pattern, sampling):
+ self.sampling = float(sampling)
+ k = len(pattern) // 2
+ n = k * 2
+ self.start_pos = [pattern[i] + pattern[i+1] for i in range(0, n, 2)]
+ self.stop_pos = [pattern[i] for i in range(0, n, 2)]
+ self.init()
+ def init(self):
+ self.start_len = 0.0
+ self.start_idx = 0
+ self.stop_len = self.sampling
+ self.stop_idx = 0
+ def start(self):
+ self.start_len += self.sampling
+ if abs(self.start_len - self.start_pos[self.start_idx]) < self.sampling / 2.0:
+ self.start_len = 0.0
+ self.start_idx = (self.start_idx + 1) % len(self.start_pos)
+ return True
+ return False
+ def stop(self):
+ if self.start_len > 0.0:
+ self.init()
+ self.stop_len += self.sampling
+ if abs(self.stop_len - self.stop_pos[self.stop_idx]) < self.sampling / 2.0:
+ self.stop_len = self.sampling
+ self.stop_idx = (self.stop_idx + 1) % len(self.stop_pos)
+ return True
+ return False
+
+# Dashed line
+
+class DashedLineShader(StrokeShader):
+ def __init__(self, pattern):
+ StrokeShader.__init__(self)
+ self._pattern = pattern
+ def shade(self, stroke):
+ index = 0 # pattern index
+ start = 0.0 # 2D curvilinear length
+ visible = True
+ sampling = 1.0
+ it = stroke.stroke_vertices_begin(sampling)
+ while not it.is_end:
+ pos = it.t # curvilinear abscissa
+ # The extra 'sampling' term is added below, because the
+ # visibility attribute of the i-th vertex refers to the
+ # visibility of the stroke segment between the i-th and
+ # (i+1)-th vertices.
+ if pos - start + sampling > self._pattern[index]:
+ start = pos
+ index += 1
+ if index == len(self._pattern):
+ index = 0
+ visible = not visible
+ it.object.attribute.visible = visible
+ it.increment()
+
+# predicates for chaining
+
+class AngleLargerThanBP1D(BinaryPredicate1D):
+ def __init__(self, angle):
+ BinaryPredicate1D.__init__(self)
+ self._angle = angle
+ def __call__(self, i1, i2):
+ sv1a = i1.first_fedge.first_svertex.point_2d
+ sv1b = i1.last_fedge.second_svertex.point_2d
+ sv2a = i2.first_fedge.first_svertex.point_2d
+ sv2b = i2.last_fedge.second_svertex.point_2d
+ if (sv1a - sv2a).length < 1e-6:
+ dir1 = sv1a - sv1b
+ dir2 = sv2b - sv2a
+ elif (sv1b - sv2b).length < 1e-6:
+ dir1 = sv1b - sv1a
+ dir2 = sv2a - sv2b
+ elif (sv1a - sv2b).length < 1e-6:
+ dir1 = sv1a - sv1b
+ dir2 = sv2a - sv2b
+ elif (sv1b - sv2a).length < 1e-6:
+ dir1 = sv1b - sv1a
+ dir2 = sv2b - sv2a
+ else:
+ return False
+ denom = dir1.length * dir2.length
+ if denom < 1e-6:
+ return False
+ x = (dir1 * dir2) / denom
+ return math.acos(min(max(x, -1.0), 1.0)) > self._angle
+
+class AndBP1D(BinaryPredicate1D):
+ def __init__(self, pred1, pred2):
+ BinaryPredicate1D.__init__(self)
+ self.__pred1 = pred1
+ self.__pred2 = pred2
+ def __call__(self, i1, i2):
+ return self.__pred1(i1, i2) and self.__pred2(i1, i2)
+
+# predicates for selection
+
+class LengthThresholdUP1D(UnaryPredicate1D):
+ def __init__(self, min_length=None, max_length=None):
+ UnaryPredicate1D.__init__(self)
+ self._min_length = min_length
+ self._max_length = max_length
+ def __call__(self, inter):
+ length = inter.length_2d
+ if self._min_length is not None and length < self._min_length:
+ return False
+ if self._max_length is not None and length > self._max_length:
+ return False
+ return True
+
+class FaceMarkBothUP1D(UnaryPredicate1D):
+ def __call__(self, inter): # ViewEdge
+ fe = inter.first_fedge
+ while fe is not None:
+ if fe.is_smooth:
+ if fe.face_mark:
+ return True
+ else:
+ if fe.face_mark_right and fe.face_mark_left:
+ return True
+ fe = fe.next_fedge
+ return False
+
+class FaceMarkOneUP1D(UnaryPredicate1D):
+ def __call__(self, inter): # ViewEdge
+ fe = inter.first_fedge
+ while fe is not None:
+ if fe.is_smooth:
+ if fe.face_mark:
+ return True
+ else:
+ if fe.face_mark_right or fe.face_mark_left:
+ return True
+ fe = fe.next_fedge
+ return False
+
+# predicates for splitting
+
+class MaterialBoundaryUP0D(UnaryPredicate0D):
+ def __call__(self, it):
+ if it.is_begin:
+ return False
+ it_prev = Interface0DIterator(it)
+ it_prev.decrement()
+ v = it.object
+ it.increment()
+ if it.is_end:
+ return False
+ fe = v.get_fedge(it_prev.object)
+ idx1 = fe.material_index if fe.is_smooth else fe.material_index_left
+ fe = v.get_fedge(it.object)
+ idx2 = fe.material_index if fe.is_smooth else fe.material_index_left
+ return idx1 != idx2
+
+class Curvature2DAngleThresholdUP0D(UnaryPredicate0D):
+ def __init__(self, min_angle=None, max_angle=None):
+ UnaryPredicate0D.__init__(self)
+ self._min_angle = min_angle
+ self._max_angle = max_angle
+ self._func = Curvature2DAngleF0D()
+ def __call__(self, inter):
+ angle = math.pi - self._func(inter)
+ if self._min_angle is not None and angle < self._min_angle:
+ return True
+ if self._max_angle is not None and angle > self._max_angle:
+ return True
+ return False
+
+class Length2DThresholdUP0D(UnaryPredicate0D):
+ def __init__(self, length_limit):
+ UnaryPredicate0D.__init__(self)
+ self._length_limit = length_limit
+ self._t = 0.0
+ def __call__(self, inter):
+ t = inter.t # curvilinear abscissa
+ if t < self._t:
+ self._t = 0.0
+ return False
+ if t - self._t < self._length_limit:
+ return False
+ self._t = t
+ return True
+
+# Seed for random number generation
+
+class Seed:
+ def __init__(self):
+ self.t_max = 2 ** 15
+ self.t = int(time.time()) % self.t_max
+ def get(self, seed):
+ if seed < 0:
+ self.t = (self.t + 1) % self.t_max
+ return self.t
+ return seed
+
+_seed = Seed()
+
+# main function for parameter processing
+
+def process(layer_name, lineset_name):
+ scene = Freestyle.getCurrentScene()
+ layer = scene.render.layers[layer_name]
+ lineset = layer.freestyle_settings.linesets[lineset_name]
+ linestyle = lineset.linestyle
+
+ selection_criteria = []
+ # prepare selection criteria by visibility
+ if lineset.select_by_visibility:
+ if lineset.visibility == "VISIBLE":
+ selection_criteria.append(
+ QuantitativeInvisibilityUP1D(0))
+ elif lineset.visibility == "HIDDEN":
+ selection_criteria.append(
+ NotUP1D(QuantitativeInvisibilityUP1D(0)))
+ elif lineset.visibility == "RANGE":
+ selection_criteria.append(
+ QuantitativeInvisibilityRangeUP1D(lineset.qi_start, lineset.qi_end))
+ # prepare selection criteria by edge types
+ if lineset.select_by_edge_types:
+ edge_type_criteria = []
+ if lineset.select_silhouette:
+ upred = pyNatureUP1D(Nature.SILHOUETTE)
+ edge_type_criteria.append(NotUP1D(upred) if lineset.exclude_silhouette else upred)
+ if lineset.select_border:
+ upred = pyNatureUP1D(Nature.BORDER)
+ edge_type_criteria.append(NotUP1D(upred) if lineset.exclude_border else upred)
+ if lineset.select_crease:
+ upred = pyNatureUP1D(Nature.CREASE)
+ edge_type_criteria.append(NotUP1D(upred) if lineset.exclude_crease else upred)
+ if lineset.select_ridge_valley:
+ upred = pyNatureUP1D(Nature.RIDGE)
+ edge_type_criteria.append(NotUP1D(upred) if lineset.exclude_ridge_valley else upred)
+ if lineset.select_suggestive_contour:
+ upred = pyNatureUP1D(Nature.SUGGESTIVE_CONTOUR)
+ edge_type_criteria.append(NotUP1D(upred) if lineset.exclude_suggestive_contour else upred)
+ if lineset.select_material_boundary:
+ upred = pyNatureUP1D(Nature.MATERIAL_BOUNDARY)
+ edge_type_criteria.append(NotUP1D(upred) if lineset.exclude_material_boundary else upred)
+ if lineset.select_edge_mark:
+ upred = pyNatureUP1D(Nature.EDGE_MARK)
+ edge_type_criteria.append(NotUP1D(upred) if lineset.exclude_edge_mark else upred)
+ if lineset.select_contour:
+ upred = ContourUP1D()
+ edge_type_criteria.append(NotUP1D(upred) if lineset.exclude_contour else upred)
+ if lineset.select_external_contour:
+ upred = ExternalContourUP1D()
+ edge_type_criteria.append(NotUP1D(upred) if lineset.exclude_external_contour else upred)
+ if lineset.edge_type_combination == "OR":
+ upred = join_unary_predicates(edge_type_criteria, OrUP1D)
+ else:
+ upred = join_unary_predicates(edge_type_criteria, AndUP1D)
+ if upred is not None:
+ if lineset.edge_type_negation == "EXCLUSIVE":
+ upred = NotUP1D(upred)
+ selection_criteria.append(upred)
+ # prepare selection criteria by face marks
+ if lineset.select_by_face_marks:
+ if lineset.face_mark_condition == "BOTH":
+ upred = FaceMarkBothUP1D()
+ else:
+ upred = FaceMarkOneUP1D()
+ if lineset.face_mark_negation == "EXCLUSIVE":
+ upred = NotUP1D(upred)
+ selection_criteria.append(upred)
+ # prepare selection criteria by group of objects
+ if lineset.select_by_group:
+ if lineset.group is not None:
+ names = dict((ob.name, True) for ob in lineset.group.objects)
+ upred = ObjectNamesUP1D(names, lineset.group_negation == 'EXCLUSIVE')
+ selection_criteria.append(upred)
+ # prepare selection criteria by image border
+ if lineset.select_by_image_border:
+ fac = scene.render.resolution_percentage / 100.0
+ w = scene.render.resolution_x * fac
+ h = scene.render.resolution_y * fac
+ if scene.render.use_border:
+ xmin = scene.render.border_min_x * w
+ xmax = scene.render.border_max_x * w
+ ymin = scene.render.border_min_y * h
+ ymax = scene.render.border_max_y * h
+ else:
+ xmin, xmax = 0.0, float(w)
+ ymin, ymax = 0.0, float(h)
+ upred = WithinImageBoundaryUP1D(xmin, ymin, xmax, ymax)
+ selection_criteria.append(upred)
+ # select feature edges
+ upred = join_unary_predicates(selection_criteria, AndUP1D)
+ if upred is None:
+ upred = TrueUP1D()
+ Operators.select(upred)
+ # join feature edges to form chains
+ if linestyle.use_chaining:
+ if linestyle.chaining == "PLAIN":
+ if linestyle.same_object:
+ Operators.bidirectional_chain(ChainSilhouetteIterator(), NotUP1D(upred))
+ else:
+ Operators.bidirectional_chain(ChainPredicateIterator(upred, TrueBP1D()), NotUP1D(upred))
+ elif linestyle.chaining == "SKETCHY":
+ if linestyle.same_object:
+ Operators.bidirectional_chain(pySketchyChainSilhouetteIterator(linestyle.rounds))
+ else:
+ Operators.bidirectional_chain(pySketchyChainingIterator(linestyle.rounds))
+ else:
+ Operators.chain(ChainPredicateIterator(FalseUP1D(), FalseBP1D()), NotUP1D(upred))
+ # split chains
+ if linestyle.material_boundary:
+ Operators.sequential_split(MaterialBoundaryUP0D())
+ if linestyle.use_min_angle or linestyle.use_max_angle:
+ min_angle = linestyle.min_angle if linestyle.use_min_angle else None
+ max_angle = linestyle.max_angle if linestyle.use_max_angle else None
+ Operators.sequential_split(Curvature2DAngleThresholdUP0D(min_angle, max_angle))
+ if linestyle.use_split_length:
+ Operators.sequential_split(Length2DThresholdUP0D(linestyle.split_length), 1.0)
+ if linestyle.use_split_pattern:
+ pattern = []
+ if linestyle.split_dash1 > 0 and linestyle.split_gap1 > 0:
+ pattern.append(linestyle.split_dash1)
+ pattern.append(linestyle.split_gap1)
+ if linestyle.split_dash2 > 0 and linestyle.split_gap2 > 0:
+ pattern.append(linestyle.split_dash2)
+ pattern.append(linestyle.split_gap2)
+ if linestyle.split_dash3 > 0 and linestyle.split_gap3 > 0:
+ pattern.append(linestyle.split_dash3)
+ pattern.append(linestyle.split_gap3)
+ if len(pattern) > 0:
+ sampling = 1.0
+ controller = SplitPatternController(pattern, sampling)
+ Operators.sequential_split(SplitPatternStartingUP0D(controller),
+ SplitPatternStoppingUP0D(controller),
+ sampling)
+ # select chains
+ if linestyle.use_min_length or linestyle.use_max_length:
+ min_length = linestyle.min_length if linestyle.use_min_length else None
+ max_length = linestyle.max_length if linestyle.use_max_length else None
+ Operators.select(LengthThresholdUP1D(min_length, max_length))
+ # prepare a list of stroke shaders
+ shaders_list = []
+ for m in linestyle.geometry_modifiers:
+ if not m.use:
+ continue
+ if m.type == "SAMPLING":
+ shaders_list.append(SamplingShader(
+ m.sampling))
+ elif m.type == "BEZIER_CURVE":
+ shaders_list.append(BezierCurveShader(
+ m.error))
+ elif m.type == "SINUS_DISPLACEMENT":
+ shaders_list.append(SinusDisplacementShader(
+ m.wavelength, m.amplitude, m.phase))
+ elif m.type == "SPATIAL_NOISE":
+ shaders_list.append(SpatialNoiseShader(
+ m.amplitude, m.scale, m.octaves, m.smooth, m.pure_random))
+ elif m.type == "PERLIN_NOISE_1D":
+ shaders_list.append(PerlinNoise1DShader(
+ m.frequency, m.amplitude, m.octaves, m.angle, _seed.get(m.seed)))
+ elif m.type == "PERLIN_NOISE_2D":
+ shaders_list.append(PerlinNoise2DShader(
+ m.frequency, m.amplitude, m.octaves, m.angle, _seed.get(m.seed)))
+ elif m.type == "BACKBONE_STRETCHER":
+ shaders_list.append(BackboneStretcherShader(
+ m.backbone_length))
+ elif m.type == "TIP_REMOVER":
+ shaders_list.append(TipRemoverShader(
+ m.tip_length))
+ elif m.type == "POLYGONIZATION":
+ shaders_list.append(PolygonalizationShader(
+ m.error))
+ elif m.type == "GUIDING_LINES":
+ shaders_list.append(GuidingLinesShader(
+ m.offset))
+ elif m.type == "BLUEPRINT":
+ if m.shape == "CIRCLES":
+ shaders_list.append(pyBluePrintCirclesShader(
+ m.rounds, m.random_radius, m.random_center))
+ elif m.shape == "ELLIPSES":
+ shaders_list.append(pyBluePrintEllipsesShader(
+ m.rounds, m.random_radius, m.random_center))
+ elif m.shape == "SQUARES":
+ shaders_list.append(pyBluePrintSquaresShader(
+ m.rounds, m.backbone_length, m.random_backbone))
+ elif m.type == "2D_OFFSET":
+ shaders_list.append(Offset2DShader(
+ m.start, m.end, m.x, m.y))
+ elif m.type == "2D_TRANSFORM":
+ shaders_list.append(Transform2DShader(
+ m.pivot, m.scale_x, m.scale_y, m.angle, m.pivot_u, m.pivot_x, m.pivot_y))
+ color = linestyle.color
+ if (not linestyle.use_chaining) or (linestyle.chaining == "PLAIN" and linestyle.same_object):
+ thickness_position = linestyle.thickness_position
+ else:
+ thickness_position = "CENTER"
+ import bpy
+ if bpy.app.debug_freestyle:
+ print("Warning: Thickness position options are applied when chaining is disabled")
+ print(" or the Plain chaining is used with the Same Object option enabled.")
+ shaders_list.append(BaseColorShader(color.r, color.g, color.b, linestyle.alpha))
+ shaders_list.append(BaseThicknessShader(linestyle.thickness, thickness_position,
+ linestyle.thickness_ratio))
+ for m in linestyle.color_modifiers:
+ if not m.use:
+ continue
+ if m.type == "ALONG_STROKE":
+ shaders_list.append(ColorAlongStrokeShader(
+ m.blend, m.influence, m.color_ramp))
+ elif m.type == "DISTANCE_FROM_CAMERA":
+ shaders_list.append(ColorDistanceFromCameraShader(
+ m.blend, m.influence, m.color_ramp,
+ m.range_min, m.range_max))
+ elif m.type == "DISTANCE_FROM_OBJECT":
+ shaders_list.append(ColorDistanceFromObjectShader(
+ m.blend, m.influence, m.color_ramp, m.target,
+ m.range_min, m.range_max))
+ elif m.type == "MATERIAL":
+ shaders_list.append(ColorMaterialShader(
+ m.blend, m.influence, m.color_ramp, m.material_attr,
+ m.use_ramp))
+ for m in linestyle.alpha_modifiers:
+ if not m.use:
+ continue
+ if m.type == "ALONG_STROKE":
+ shaders_list.append(AlphaAlongStrokeShader(
+ m.blend, m.influence, m.mapping, m.invert, m.curve))
+ elif m.type == "DISTANCE_FROM_CAMERA":
+ shaders_list.append(AlphaDistanceFromCameraShader(
+ m.blend, m.influence, m.mapping, m.invert, m.curve,
+ m.range_min, m.range_max))
+ elif m.type == "DISTANCE_FROM_OBJECT":
+ shaders_list.append(AlphaDistanceFromObjectShader(
+ m.blend, m.influence, m.mapping, m.invert, m.curve, m.target,
+ m.range_min, m.range_max))
+ elif m.type == "MATERIAL":
+ shaders_list.append(AlphaMaterialShader(
+ m.blend, m.influence, m.mapping, m.invert, m.curve,
+ m.material_attr))
+ for m in linestyle.thickness_modifiers:
+ if not m.use:
+ continue
+ if m.type == "ALONG_STROKE":
+ shaders_list.append(ThicknessAlongStrokeShader(
+ thickness_position, linestyle.thickness_ratio,
+ m.blend, m.influence, m.mapping, m.invert, m.curve,
+ m.value_min, m.value_max))
+ elif m.type == "DISTANCE_FROM_CAMERA":
+ shaders_list.append(ThicknessDistanceFromCameraShader(
+ thickness_position, linestyle.thickness_ratio,
+ m.blend, m.influence, m.mapping, m.invert, m.curve,
+ m.range_min, m.range_max, m.value_min, m.value_max))
+ elif m.type == "DISTANCE_FROM_OBJECT":
+ shaders_list.append(ThicknessDistanceFromObjectShader(
+ thickness_position, linestyle.thickness_ratio,
+ m.blend, m.influence, m.mapping, m.invert, m.curve, m.target,
+ m.range_min, m.range_max, m.value_min, m.value_max))
+ elif m.type == "MATERIAL":
+ shaders_list.append(ThicknessMaterialShader(
+ thickness_position, linestyle.thickness_ratio,
+ m.blend, m.influence, m.mapping, m.invert, m.curve,
+ m.material_attr, m.value_min, m.value_max))
+ elif m.type == "CALLIGRAPHY":
+ shaders_list.append(CalligraphicThicknessShader(
+ thickness_position, linestyle.thickness_ratio,
+ m.blend, m.influence,
+ m.orientation, m.min_thickness, m.max_thickness))
+ if linestyle.caps == "ROUND":
+ shaders_list.append(RoundCapShader())
+ elif linestyle.caps == "SQUARE":
+ shaders_list.append(SquareCapShader())
+ if linestyle.use_dashed_line:
+ pattern = []
+ if linestyle.dash1 > 0 and linestyle.gap1 > 0:
+ pattern.append(linestyle.dash1)
+ pattern.append(linestyle.gap1)
+ if linestyle.dash2 > 0 and linestyle.gap2 > 0:
+ pattern.append(linestyle.dash2)
+ pattern.append(linestyle.gap2)
+ if linestyle.dash3 > 0 and linestyle.gap3 > 0:
+ pattern.append(linestyle.dash3)
+ pattern.append(linestyle.gap3)
+ if len(pattern) > 0:
+ shaders_list.append(DashedLineShader(pattern))
+ # create strokes using the shaders list
+ Operators.create(TrueUP1D(), shaders_list)
diff --git a/release/scripts/freestyle/style_modules/polygonalize.py b/release/scripts/freestyle/style_modules/polygonalize.py
new file mode 100644
index 00000000000..092efbb2df1
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/polygonalize.py
@@ -0,0 +1,36 @@
+# ##### 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 : polygonalize.py
+# Author : Stephane Grabli
+# Date : 04/08/2005
+# Purpose : Make the strokes more "polygonal"
+
+from Freestyle import ChainSilhouetteIterator, ConstantColorShader, ConstantThicknessShader, \
+ Operators, PolygonalizationShader, QuantitativeInvisibilityUP1D, SamplingShader, TrueUP1D
+from logical_operators import NotUP1D
+
+Operators.select(QuantitativeInvisibilityUP1D(0))
+Operators.bidirectional_chain(ChainSilhouetteIterator(), NotUP1D(QuantitativeInvisibilityUP1D(0)))
+shaders_list = [
+ SamplingShader(2.0),
+ ConstantThicknessShader(3),
+ ConstantColorShader(0.0, 0.0, 0.0),
+ PolygonalizationShader(8),
+ ]
+Operators.create(TrueUP1D(), shaders_list)
diff --git a/release/scripts/freestyle/style_modules/qi0.py b/release/scripts/freestyle/style_modules/qi0.py
new file mode 100644
index 00000000000..3badf818566
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/qi0.py
@@ -0,0 +1,36 @@
+# ##### 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 : qi0.py
+# Author : Stephane Grabli
+# Date : 04/08/2005
+# Purpose : Draws the visible lines (chaining follows same nature lines)
+# (most basic style module)
+
+from Freestyle import ChainSilhouetteIterator, ConstantColorShader, ConstantThicknessShader, \
+ Operators, QuantitativeInvisibilityUP1D, SamplingShader, TrueUP1D
+from logical_operators import NotUP1D
+
+Operators.select(QuantitativeInvisibilityUP1D(0))
+Operators.bidirectional_chain(ChainSilhouetteIterator(), NotUP1D(QuantitativeInvisibilityUP1D(0)))
+shaders_list = [
+ SamplingShader(5.0),
+ ConstantThicknessShader(4.0),
+ ConstantColorShader(0.0, 0.0, 0.0)
+ ]
+Operators.create(TrueUP1D(), shaders_list)
diff --git a/release/scripts/freestyle/style_modules/qi0_not_external_contour.py b/release/scripts/freestyle/style_modules/qi0_not_external_contour.py
new file mode 100644
index 00000000000..20860b36aa3
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/qi0_not_external_contour.py
@@ -0,0 +1,41 @@
+# ##### 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 : qi0_not_external_contour.py
+# Author : Stephane Grabli
+# Date : 04/08/2005
+# Purpose : Draws the visible lines (chaining follows same nature lines)
+# that do not belong to the external contour of the scene
+
+from Freestyle import BackboneStretcherShader, ChainSilhouetteIterator, ExternalContourUP1D, \
+ IncreasingColorShader, IncreasingThicknessShader, Operators, QuantitativeInvisibilityUP1D, \
+ SamplingShader, SpatialNoiseShader, TextureAssignerShader, TrueUP1D
+from logical_operators import AndUP1D, NotUP1D
+
+upred = AndUP1D(QuantitativeInvisibilityUP1D(0), ExternalContourUP1D())
+Operators.select(upred)
+Operators.bidirectional_chain(ChainSilhouetteIterator(), NotUP1D(upred))
+shaders_list = [
+ SamplingShader(4),
+ SpatialNoiseShader(4, 150, 2, True, True),
+ IncreasingThicknessShader(2, 5),
+ BackboneStretcherShader(20),
+ IncreasingColorShader(1, 0, 0, 1, 0, 1, 0, 1),
+ TextureAssignerShader(4),
+ ]
+Operators.create(TrueUP1D(), shaders_list)
diff --git a/release/scripts/freestyle/style_modules/qi1.py b/release/scripts/freestyle/style_modules/qi1.py
new file mode 100644
index 00000000000..7ab86b67c86
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/qi1.py
@@ -0,0 +1,37 @@
+# ##### 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 : qi1.py
+# Author : Stephane Grabli
+# Date : 04/08/2005
+# Purpose : Draws lines hidden by one surface.
+# *** Quantitative Invisibility must have been
+# enabled in the options dialog to use this style module ****
+
+from Freestyle import ChainSilhouetteIterator, ConstantColorShader, ConstantThicknessShader, \
+ Operators, QuantitativeInvisibilityUP1D, SamplingShader, TrueUP1D
+from logical_operators import NotUP1D
+
+Operators.select(QuantitativeInvisibilityUP1D(1))
+Operators.bidirectional_chain(ChainSilhouetteIterator(), NotUP1D(QuantitativeInvisibilityUP1D(1)))
+shaders_list = [
+ SamplingShader(5.0),
+ ConstantThicknessShader(3),
+ ConstantColorShader(0.5, 0.5, 0.5, 1)
+ ]
+Operators.create(TrueUP1D(), shaders_list)
diff --git a/release/scripts/freestyle/style_modules/qi2.py b/release/scripts/freestyle/style_modules/qi2.py
new file mode 100644
index 00000000000..510a22e3e07
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/qi2.py
@@ -0,0 +1,37 @@
+# ##### 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 : qi2.py
+# Author : Stephane Grabli
+# Date : 04/08/2005
+# Purpose : Draws lines hidden by two surfaces.
+# *** Quantitative Invisibility must have been
+# enabled in the options dialog to use this style module ****
+
+from Freestyle import ChainSilhouetteIterator, ConstantColorShader, ConstantThicknessShader, \
+ Operators, QuantitativeInvisibilityUP1D, SamplingShader, TrueUP1D
+from logical_operators import NotUP1D
+
+Operators.select(QuantitativeInvisibilityUP1D(2))
+Operators.bidirectional_chain(ChainSilhouetteIterator(), NotUP1D(QuantitativeInvisibilityUP1D(2)))
+shaders_list = [
+ SamplingShader(10),
+ ConstantThicknessShader(1.5),
+ ConstantColorShader(0.7, 0.7, 0.7, 1),
+ ]
+Operators.create(TrueUP1D(), shaders_list)
diff --git a/release/scripts/freestyle/style_modules/sequentialsplit_sketchy.py b/release/scripts/freestyle/style_modules/sequentialsplit_sketchy.py
new file mode 100644
index 00000000000..966e5ddf129
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/sequentialsplit_sketchy.py
@@ -0,0 +1,44 @@
+# ##### 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 : sequentialsplit_sketchy.py
+# Author : Stephane Grabli
+# Date : 04/08/2005
+# Purpose : Use the sequential split with two different
+# predicates to specify respectively the starting and
+# the stopping extremities for strokes
+
+from Freestyle import ChainSilhouetteIterator, ConstantColorShader, IncreasingThicknessShader, Nature, \
+ Operators, QuantitativeInvisibilityUP1D, SpatialNoiseShader, TextureAssignerShader, TrueUP1D
+from PredicatesU0D import pyBackTVertexUP0D, pyVertexNatureUP0D
+from logical_operators import NotUP1D
+
+upred = QuantitativeInvisibilityUP1D(0)
+Operators.select(upred)
+Operators.bidirectional_chain(ChainSilhouetteIterator(), NotUP1D(upred))
+## starting and stopping predicates:
+start = pyVertexNatureUP0D(Nature.NON_T_VERTEX)
+stop = pyBackTVertexUP0D()
+Operators.sequential_split(start, stop, 10)
+shaders_list = [
+ SpatialNoiseShader(7, 120, 2, True, True),
+ IncreasingThicknessShader(5, 8),
+ ConstantColorShader(0.2, 0.2, 0.2, 1),
+ TextureAssignerShader(4),
+ ]
+Operators.create(TrueUP1D(), shaders_list)
diff --git a/release/scripts/freestyle/style_modules/shaders.py b/release/scripts/freestyle/style_modules/shaders.py
new file mode 100644
index 00000000000..b955b9d4d08
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/shaders.py
@@ -0,0 +1,1238 @@
+# ##### 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 : shaders.py
+# Authors : Fredo Durand, Stephane Grabli, Francois Sillion, Emmanuel Turquin
+# Date : 11/08/2005
+# Purpose : Stroke shaders to be used for creation of stylized strokes
+
+from Freestyle import AdjacencyIterator, Curvature2DAngleF0D, DensityF0D, GetProjectedZF0D, \
+ Interface0DIterator, MaterialF0D, Nature, Noise, Normal2DF0D, Orientation2DF1D, \
+ StrokeAttribute, StrokeShader, StrokeVertexIterator, ZDiscontinuityF0D
+from Freestyle import ContextFunctions as CF
+from PredicatesU0D import pyVertexNatureUP0D
+
+import math
+import mathutils
+import random
+
+## thickness modifiers
+######################
+
+class pyDepthDiscontinuityThicknessShader(StrokeShader):
+ def __init__(self, min, max):
+ StrokeShader.__init__(self)
+ self.__min = float(min)
+ self.__max = float(max)
+ self.__func = ZDiscontinuityF0D()
+ def shade(self, stroke):
+ z_min=0.0
+ z_max=1.0
+ a = (self.__max - self.__min)/(z_max-z_min)
+ b = (self.__min*z_max-self.__max*z_min)/(z_max-z_min)
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ z = self.__func(Interface0DIterator(it))
+ thickness = a*z+b
+ it.object.attribute.thickness = (thickness, thickness)
+ it.increment()
+
+class pyConstantThicknessShader(StrokeShader):
+ def __init__(self, thickness):
+ StrokeShader.__init__(self)
+ self._thickness = thickness
+ def shade(self, stroke):
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ t = self._thickness/2.0
+ it.object.attribute.thickness = (t, t)
+ it.increment()
+
+class pyFXSThicknessShader(StrokeShader):
+ def __init__(self, thickness):
+ StrokeShader.__init__(self)
+ self._thickness = thickness
+ def shade(self, stroke):
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ t = self._thickness/2.0
+ it.object.attribute.thickness = (t, t)
+ it.increment()
+
+class pyFXSVaryingThicknessWithDensityShader(StrokeShader):
+ def __init__(self, wsize, threshold_min, threshold_max, thicknessMin, thicknessMax):
+ StrokeShader.__init__(self)
+ self.wsize= wsize
+ self.threshold_min= threshold_min
+ self.threshold_max= threshold_max
+ self._thicknessMin = thicknessMin
+ self._thicknessMax = thicknessMax
+ def shade(self, stroke):
+ n = stroke.stroke_vertices_size()
+ i = 0
+ it = stroke.stroke_vertices_begin()
+ func = DensityF0D(self.wsize)
+ while not it.is_end:
+ c = func(Interface0DIterator(it))
+ if c < self.threshold_min:
+ c = self.threshold_min
+ if c > self.threshold_max:
+ c = self.threshold_max
+## t = (c - self.threshold_min)/(self.threshold_max - self.threshold_min)*(self._thicknessMax-self._thicknessMin) + self._thicknessMin
+ t = (self.threshold_max - c )/(self.threshold_max - self.threshold_min)*(self._thicknessMax-self._thicknessMin) + self._thicknessMin
+ it.object.attribute.thickness = (t/2.0, t/2.0)
+ i = i+1
+ it.increment()
+
+class pyIncreasingThicknessShader(StrokeShader):
+ def __init__(self, thicknessMin, thicknessMax):
+ StrokeShader.__init__(self)
+ self._thicknessMin = thicknessMin
+ self._thicknessMax = thicknessMax
+ def shade(self, stroke):
+ n = stroke.stroke_vertices_size()
+ i = 0
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ c = float(i)/float(n)
+ if i < float(n)/2.0:
+ t = (1.0 - c)*self._thicknessMin + c * self._thicknessMax
+ else:
+ t = (1.0 - c)*self._thicknessMax + c * self._thicknessMin
+ it.object.attribute.thickness = (t/2.0, t/2.0)
+ i = i+1
+ it.increment()
+
+class pyConstrainedIncreasingThicknessShader(StrokeShader):
+ def __init__(self, thicknessMin, thicknessMax, ratio):
+ StrokeShader.__init__(self)
+ self._thicknessMin = thicknessMin
+ self._thicknessMax = thicknessMax
+ self._ratio = ratio
+ def shade(self, stroke):
+ slength = stroke.length_2d
+ tmp = self._ratio*slength
+ maxT = 0.0
+ if tmp < self._thicknessMax:
+ maxT = tmp
+ else:
+ maxT = self._thicknessMax
+ n = stroke.stroke_vertices_size()
+ i = 0
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ att = it.object.attribute
+ c = float(i)/float(n)
+ if i < float(n)/2.0:
+ t = (1.0 - c)*self._thicknessMin + c * maxT
+ else:
+ t = (1.0 - c)*maxT + c * self._thicknessMin
+ att.thickness = (t/2.0, t/2.0)
+ if i == n-1:
+ att.thickness = (self._thicknessMin/2.0, self._thicknessMin/2.0)
+ i = i+1
+ it.increment()
+
+class pyDecreasingThicknessShader(StrokeShader):
+ def __init__(self, thicknessMax, thicknessMin):
+ StrokeShader.__init__(self)
+ self._thicknessMin = thicknessMin
+ self._thicknessMax = thicknessMax
+ def shade(self, stroke):
+ l = stroke.length_2d
+ tMax = self._thicknessMax
+ if self._thicknessMax > 0.33*l:
+ tMax = 0.33*l
+ tMin = self._thicknessMin
+ if self._thicknessMin > 0.1*l:
+ tMin = 0.1*l
+ n = stroke.stroke_vertices_size()
+ i = 0
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ c = float(i)/float(n)
+ t = (1.0 - c)*tMax +c*tMin
+ it.object.attribute.thickness = (t/2.0, t/2.0)
+ i = i+1
+ it.increment()
+
+def smoothC(a, exp):
+ return math.pow(float(a), exp) * math.pow(2.0, exp)
+
+class pyNonLinearVaryingThicknessShader(StrokeShader):
+ def __init__(self, thicknessExtremity, thicknessMiddle, exponent):
+ StrokeShader.__init__(self)
+ self._thicknessMin = thicknessMiddle
+ self._thicknessMax = thicknessExtremity
+ self._exponent = exponent
+ def shade(self, stroke):
+ n = stroke.stroke_vertices_size()
+ i = 0
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ if i < float(n)/2.0:
+ c = float(i)/float(n)
+ else:
+ c = float(n-i)/float(n)
+ c = smoothC(c, self._exponent)
+ t = (1.0 - c)*self._thicknessMax + c * self._thicknessMin
+ it.object.attribute.thickness = (t/2.0, t/2.0)
+ i = i+1
+ it.increment()
+
+## Spherical linear interpolation (cos)
+class pySLERPThicknessShader(StrokeShader):
+ def __init__(self, thicknessMin, thicknessMax, omega=1.2):
+ StrokeShader.__init__(self)
+ self._thicknessMin = thicknessMin
+ self._thicknessMax = thicknessMax
+ self._omega = omega
+ def shade(self, stroke):
+ slength = stroke.length_2d
+ tmp = 0.33*slength
+ maxT = self._thicknessMax
+ if tmp < self._thicknessMax:
+ maxT = tmp
+ n = stroke.stroke_vertices_size()
+ i = 0
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ c = float(i)/float(n)
+ if i < float(n)/2.0:
+ t = math.sin((1-c)*self._omega)/math.sinh(self._omega)*self._thicknessMin + math.sin(c*self._omega)/math.sinh(self._omega) * maxT
+ else:
+ t = math.sin((1-c)*self._omega)/math.sinh(self._omega)*maxT + math.sin(c*self._omega)/math.sinh(self._omega) * self._thicknessMin
+ it.object.attribute.thickness = (t/2.0, t/2.0)
+ i = i+1
+ it.increment()
+
+class pyTVertexThickenerShader(StrokeShader): ## FIXME
+ def __init__(self, a=1.5, n=3):
+ StrokeShader.__init__(self)
+ self._a = a
+ self._n = n
+ def shade(self, stroke):
+ it = stroke.stroke_vertices_begin()
+ predTVertex = pyVertexNatureUP0D(Nature.T_VERTEX)
+ while not it.is_end:
+ if predTVertex(it) == 1:
+ it2 = StrokeVertexIterator(it)
+ it2.increment()
+ if not (it.is_begin or it2.is_end):
+ it.increment()
+ continue
+ n = self._n
+ a = self._a
+ if it.is_begin:
+ it3 = StrokeVertexIterator(it)
+ count = 0
+ while (not it3.is_end) and count < n:
+ att = it3.object.attribute
+ (tr, tl) = att.thickness
+ r = (a-1.0)/float(n-1)*(float(n)/float(count+1) - 1) + 1
+ #r = (1.0-a)/float(n-1)*count + a
+ att.thickness = (r*tr, r*tl)
+ it3.increment()
+ count = count + 1
+ if it2.is_end:
+ it4 = StrokeVertexIterator(it)
+ count = 0
+ while (not it4.is_begin) and count < n:
+ att = it4.object.attribute
+ (tr, tl) = att.thickness
+ r = (a-1.0)/float(n-1)*(float(n)/float(count+1) - 1) + 1
+ #r = (1.0-a)/float(n-1)*count + a
+ att.thickness = (r*tr, r*tl)
+ it4.decrement()
+ count = count + 1
+ if it4.is_begin:
+ att = it4.object.attribute
+ (tr, tl) = att.thickness
+ r = (a-1.0)/float(n-1)*(float(n)/float(count+1) - 1) + 1
+ #r = (1.0-a)/float(n-1)*count + a
+ att.thickness = (r*tr, r*tl)
+ it.increment()
+
+class pyImportance2DThicknessShader(StrokeShader):
+ def __init__(self, x, y, w, kmin, kmax):
+ StrokeShader.__init__(self)
+ self._x = x
+ self._y = y
+ self._w = float(w)
+ self._kmin = float(kmin)
+ self._kmax = float(kmax)
+ def shade(self, stroke):
+ origin = mathutils.Vector([self._x, self._y])
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ v = it.object
+ p = mathutils.Vector([v.projected_x, v.projected_y])
+ d = (p-origin).length
+ if d > self._w:
+ k = self._kmin
+ else:
+ k = (self._kmax*(self._w-d) + self._kmin*d)/self._w
+ att = v.attribute
+ (tr, tl) = att.thickness
+ att.thickness = (k*tr/2.0, k*tl/2.0)
+ it.increment()
+
+class pyImportance3DThicknessShader(StrokeShader):
+ def __init__(self, x, y, z, w, kmin, kmax):
+ StrokeShader.__init__(self)
+ self._x = x
+ self._y = y
+ self._z = z
+ self._w = float(w)
+ self._kmin = float(kmin)
+ self._kmax = float(kmax)
+ def shade(self, stroke):
+ origin = mathutils.Vector([self._x, self._y, self._z])
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ v = it.object
+ p = v.point_3d
+ d = (p-origin).length
+ if d > self._w:
+ k = self._kmin
+ else:
+ k = (self._kmax*(self._w-d) + self._kmin*d)/self._w
+ att = v.attribute
+ (tr, tl) = att.thickness
+ att.thickness = (k*tr/2.0, k*tl/2.0)
+ it.increment()
+
+class pyZDependingThicknessShader(StrokeShader):
+ def __init__(self, min, max):
+ StrokeShader.__init__(self)
+ self.__min = min
+ self.__max = max
+ self.__func = GetProjectedZF0D()
+ def shade(self, stroke):
+ it = stroke.stroke_vertices_begin()
+ z_min = 1
+ z_max = 0
+ while not it.is_end:
+ z = self.__func(Interface0DIterator(it))
+ if z < z_min:
+ z_min = z
+ if z > z_max:
+ z_max = z
+ it.increment()
+ z_diff = 1 / (z_max - z_min)
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ z = (self.__func(Interface0DIterator(it)) - z_min) * z_diff
+ thickness = (1 - z) * self.__max + z * self.__min
+ it.object.attribute.thickness = (thickness, thickness)
+ it.increment()
+
+
+## color modifiers
+##################
+
+class pyConstantColorShader(StrokeShader):
+ def __init__(self,r,g,b, a = 1):
+ StrokeShader.__init__(self)
+ self._r = r
+ self._g = g
+ self._b = b
+ self._a = a
+ def shade(self, stroke):
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ att = it.object.attribute
+ att.color = (self._r, self._g, self._b)
+ att.alpha = self._a
+ it.increment()
+
+#c1->c2
+class pyIncreasingColorShader(StrokeShader):
+ def __init__(self,r1,g1,b1,a1, r2,g2,b2,a2):
+ StrokeShader.__init__(self)
+ self._c1 = [r1,g1,b1,a1]
+ self._c2 = [r2,g2,b2,a2]
+ def shade(self, stroke):
+ n = stroke.stroke_vertices_size() - 1
+ inc = 0
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ att = it.object.attribute
+ c = float(inc)/float(n)
+
+ att.color = ((1-c)*self._c1[0] + c*self._c2[0],
+ (1-c)*self._c1[1] + c*self._c2[1],
+ (1-c)*self._c1[2] + c*self._c2[2])
+ att.alpha = (1-c)*self._c1[3] + c*self._c2[3]
+ inc = inc+1
+ it.increment()
+
+# c1->c2->c1
+class pyInterpolateColorShader(StrokeShader):
+ def __init__(self,r1,g1,b1,a1, r2,g2,b2,a2):
+ StrokeShader.__init__(self)
+ self._c1 = [r1,g1,b1,a1]
+ self._c2 = [r2,g2,b2,a2]
+ def shade(self, stroke):
+ n = stroke.stroke_vertices_size() - 1
+ inc = 0
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ att = it.object.attribute
+ u = float(inc)/float(n)
+ c = 1-2*(math.fabs(u-0.5))
+ att.color = ((1-c)*self._c1[0] + c*self._c2[0],
+ (1-c)*self._c1[1] + c*self._c2[1],
+ (1-c)*self._c1[2] + c*self._c2[2])
+ att.alpha = (1-c)*self._c1[3] + c*self._c2[3]
+ inc = inc+1
+ it.increment()
+
+class pyMaterialColorShader(StrokeShader):
+ def __init__(self, threshold=50):
+ StrokeShader.__init__(self)
+ self._threshold = threshold
+ def shade(self, stroke):
+ it = stroke.stroke_vertices_begin()
+ func = MaterialF0D()
+ xn = 0.312713
+ yn = 0.329016
+ Yn = 1.0
+ un = 4.* xn/ ( -2.*xn + 12.*yn + 3. )
+ vn= 9.* yn/ ( -2.*xn + 12.*yn +3. )
+ while not it.is_end:
+ mat = func(Interface0DIterator(it))
+
+ r = mat.diffuse[0]
+ g = mat.diffuse[1]
+ b = mat.diffuse[2]
+
+ X = 0.412453*r + 0.35758 *g + 0.180423*b
+ Y = 0.212671*r + 0.71516 *g + 0.072169*b
+ Z = 0.019334*r + 0.119193*g + 0.950227*b
+
+ if X == 0 and Y == 0 and Z == 0:
+ X = 0.01
+ Y = 0.01
+ Z = 0.01
+ u = 4.*X / (X + 15.*Y + 3.*Z)
+ v = 9.*Y / (X + 15.*Y + 3.*Z)
+
+ L= 116. * math.pow((Y/Yn),(1./3.)) -16
+ U = 13. * L * (u - un)
+ V = 13. * L * (v - vn)
+
+ if L > self._threshold:
+ L = L/1.3
+ U = U+10
+ else:
+ L = L +2.5*(100-L)/5.
+ U = U/3.0
+ V = V/3.0
+ u = U / (13. * L) + un
+ v = V / (13. * L) + vn
+
+ Y = Yn * math.pow( ((L+16.)/116.), 3.)
+ X = -9. * Y * u / ((u - 4.)* v - u * v)
+ Z = (9. * Y - 15*v*Y - v*X) /( 3. * v)
+
+ r = 3.240479 * X - 1.53715 * Y - 0.498535 * Z
+ g = -0.969256 * X + 1.875991 * Y + 0.041556 * Z
+ b = 0.055648 * X - 0.204043 * Y + 1.057311 * Z
+
+ r = max(0,r)
+ g = max(0,g)
+ b = max(0,b)
+
+ it.object.attribute.color = (r, g, b)
+ it.increment()
+
+class pyRandomColorShader(StrokeShader):
+ def __init__(self, s=1):
+ StrokeShader.__init__(self)
+ random.seed(s)
+ def shade(self, stroke):
+ ## pick a random color
+ c0 = float(random.uniform(15,75))/100.0
+ c1 = float(random.uniform(15,75))/100.0
+ c2 = float(random.uniform(15,75))/100.0
+ print(c0, c1, c2)
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ it.object.attribute.color = (c0,c1,c2)
+ it.increment()
+
+class py2DCurvatureColorShader(StrokeShader):
+ def shade(self, stroke):
+ it = stroke.stroke_vertices_begin()
+ func = Curvature2DAngleF0D()
+ while not it.is_end:
+ c = func(Interface0DIterator(it))
+ if c < 0:
+ print("negative 2D curvature")
+ color = 10.0 * c/3.1415
+ it.object.attribute.color = (color, color, color)
+ it.increment()
+
+class pyTimeColorShader(StrokeShader):
+ def __init__(self, step=0.01):
+ StrokeShader.__init__(self)
+ self._t = 0
+ self._step = step
+ def shade(self, stroke):
+ c = self._t*1.0
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ it.object.attribute.color = (c,c,c)
+ it.increment()
+ self._t = self._t+self._step
+
+## geometry modifiers
+
+class pySamplingShader(StrokeShader):
+ def __init__(self, sampling):
+ StrokeShader.__init__(self)
+ self._sampling = sampling
+ def shade(self, stroke):
+ stroke.resample(float(self._sampling))
+ stroke.update_length()
+
+class pyBackboneStretcherShader(StrokeShader):
+ def __init__(self, l):
+ StrokeShader.__init__(self)
+ self._l = l
+ def shade(self, stroke):
+ it0 = stroke.stroke_vertices_begin()
+ it1 = StrokeVertexIterator(it0)
+ it1.increment()
+ itn = stroke.stroke_vertices_end()
+ itn.decrement()
+ itn_1 = StrokeVertexIterator(itn)
+ itn_1.decrement()
+ v0 = it0.object
+ v1 = it1.object
+ vn_1 = itn_1.object
+ vn = itn.object
+ p0 = mathutils.Vector([v0.projected_x, v0.projected_y])
+ pn = mathutils.Vector([vn.projected_x, vn.projected_y])
+ p1 = mathutils.Vector([v1.projected_x, v1.projected_y])
+ pn_1 = mathutils.Vector([vn_1.projected_x, vn_1.projected_y])
+ d1 = p0-p1
+ d1.normalize()
+ dn = pn-pn_1
+ dn.normalize()
+ newFirst = p0+d1*float(self._l)
+ newLast = pn+dn*float(self._l)
+ v0.point = newFirst
+ vn.point = newLast
+ stroke.update_length()
+
+class pyLengthDependingBackboneStretcherShader(StrokeShader):
+ def __init__(self, l):
+ StrokeShader.__init__(self)
+ self._l = l
+ def shade(self, stroke):
+ l = stroke.length_2d
+ stretch = self._l*l
+ it0 = stroke.stroke_vertices_begin()
+ it1 = StrokeVertexIterator(it0)
+ it1.increment()
+ itn = stroke.stroke_vertices_end()
+ itn.decrement()
+ itn_1 = StrokeVertexIterator(itn)
+ itn_1.decrement()
+ v0 = it0.object
+ v1 = it1.object
+ vn_1 = itn_1.object
+ vn = itn.object
+ p0 = mathutils.Vector([v0.projected_x, v0.projected_y])
+ pn = mathutils.Vector([vn.projected_x, vn.projected_y])
+ p1 = mathutils.Vector([v1.projected_x, v1.projected_y])
+ pn_1 = mathutils.Vector([vn_1.projected_x, vn_1.projected_y])
+ d1 = p0-p1
+ d1.normalize()
+ dn = pn-pn_1
+ dn.normalize()
+ newFirst = p0+d1*float(stretch)
+ newLast = pn+dn*float(stretch)
+ v0.point = newFirst
+ vn.point = newLast
+ stroke.update_length()
+
+
+## Shader to replace a stroke by its corresponding tangent
+class pyGuidingLineShader(StrokeShader):
+ def shade(self, stroke):
+ it = stroke.stroke_vertices_begin() ## get the first vertex
+ itlast = stroke.stroke_vertices_end() ##
+ itlast.decrement() ## get the last one
+ t = itlast.object.point - it.object.point ## tangent direction
+ itmiddle = StrokeVertexIterator(it) ##
+ while itmiddle.object.u < 0.5: ## look for the stroke middle vertex
+ itmiddle.increment() ##
+ it = StrokeVertexIterator(itmiddle)
+ it.increment()
+ while not it.is_end: ## position all the vertices along the tangent for the right part
+ it.object.point = itmiddle.object.point \
+ +t*(it.object.u-itmiddle.object.u)
+ it.increment()
+ it = StrokeVertexIterator(itmiddle)
+ it.decrement()
+ while not it.is_begin: ## position all the vertices along the tangent for the left part
+ it.object.point = itmiddle.object.point \
+ -t*(itmiddle.object.u-it.object.u)
+ it.decrement()
+ it.object.point = itmiddle.object.point-t*itmiddle.object.u ## first vertex
+ stroke.update_length()
+
+
+class pyBackboneStretcherNoCuspShader(StrokeShader):
+ def __init__(self, l):
+ StrokeShader.__init__(self)
+ self._l = l
+ def shade(self, stroke):
+ it0 = stroke.stroke_vertices_begin()
+ it1 = StrokeVertexIterator(it0)
+ it1.increment()
+ itn = stroke.stroke_vertices_end()
+ itn.decrement()
+ itn_1 = StrokeVertexIterator(itn)
+ itn_1.decrement()
+ v0 = it0.object
+ v1 = it1.object
+ if (v0.nature & Nature.CUSP) == 0 and (v1.nature & Nature.CUSP) == 0:
+ p0 = v0.point
+ p1 = v1.point
+ d1 = p0-p1
+ d1.normalize()
+ newFirst = p0+d1*float(self._l)
+ v0.point = newFirst
+ vn_1 = itn_1.object
+ vn = itn.object
+ if (vn.nature & Nature.CUSP) == 0 and (vn_1.nature & Nature.CUSP) == 0:
+ pn = vn.point
+ pn_1 = vn_1.point
+ dn = pn-pn_1
+ dn.normalize()
+ newLast = pn+dn*float(self._l)
+ vn.point = newLast
+ stroke.update_length()
+
+class pyDiffusion2Shader(StrokeShader):
+ """This shader iteratively adds an offset to the position of each
+ stroke vertex in the direction perpendicular to the stroke direction
+ at the point. The offset is scaled by the 2D curvature (i.e., how
+ quickly the stroke curve is) at the point."""
+ def __init__(self, lambda1, nbIter):
+ StrokeShader.__init__(self)
+ self._lambda = lambda1
+ self._nbIter = nbIter
+ self._normalInfo = Normal2DF0D()
+ self._curvatureInfo = Curvature2DAngleF0D()
+ def shade(self, stroke):
+ for i in range (1, self._nbIter):
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ v = it.object
+ p1 = v.point
+ p2 = self._normalInfo(Interface0DIterator(it))*self._lambda*self._curvatureInfo(Interface0DIterator(it))
+ v.point = p1+p2
+ it.increment()
+ stroke.update_length()
+
+class pyTipRemoverShader(StrokeShader):
+ def __init__(self, l):
+ StrokeShader.__init__(self)
+ self._l = l
+ def shade(self, stroke):
+ originalSize = stroke.stroke_vertices_size()
+ if originalSize < 4:
+ return
+ verticesToRemove = []
+ oldAttributes = []
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ v = it.object
+ if v.curvilinear_abscissa < self._l or v.stroke_length-v.curvilinear_abscissa < self._l:
+ verticesToRemove.append(v)
+ oldAttributes.append(StrokeAttribute(v.attribute))
+ it.increment()
+ if originalSize-len(verticesToRemove) < 2:
+ return
+ for sv in verticesToRemove:
+ stroke.remove_vertex(sv)
+ stroke.update_length()
+ stroke.resample(originalSize)
+ if stroke.stroke_vertices_size() != originalSize:
+ print("pyTipRemover: Warning: resampling problem")
+ it = stroke.stroke_vertices_begin()
+ for a in oldAttributes:
+ if it.is_end:
+ break
+ it.object.attribute = a
+ it.increment()
+ stroke.update_length()
+
+class pyTVertexRemoverShader(StrokeShader):
+ def shade(self, stroke):
+ if stroke.stroke_vertices_size() <= 3:
+ return
+ predTVertex = pyVertexNatureUP0D(Nature.T_VERTEX)
+ it = stroke.stroke_vertices_begin()
+ itlast = stroke.stroke_vertices_end()
+ itlast.decrement()
+ if predTVertex(it):
+ stroke.remove_vertex(it.object)
+ if predTVertex(itlast):
+ stroke.remove_vertex(itlast.object)
+ stroke.update_length()
+
+class pyExtremitiesOrientationShader(StrokeShader):
+ def __init__(self, x1,y1,x2=0,y2=0):
+ StrokeShader.__init__(self)
+ self._v1 = mathutils.Vector([x1,y1])
+ self._v2 = mathutils.Vector([x2,y2])
+ def shade(self, stroke):
+ #print(self._v1.x,self._v1.y)
+ stroke.setBeginningOrientation(self._v1.x,self._v1.y)
+ stroke.setEndingOrientation(self._v2.x,self._v2.y)
+
+def get_fedge(it1, it2):
+ return it1.get_fedge(it2)
+
+class pyHLRShader(StrokeShader):
+ def shade(self, stroke):
+ originalSize = stroke.stroke_vertices_size()
+ if originalSize < 4:
+ return
+ it = stroke.stroke_vertices_begin()
+ invisible = 0
+ it2 = StrokeVertexIterator(it)
+ it2.increment()
+ fe = get_fedge(it.object, it2.object)
+ if fe.viewedge.qi != 0:
+ invisible = 1
+ while not it2.is_end:
+ v = it.object
+ vnext = it2.object
+ if (v.nature & Nature.VIEW_VERTEX) != 0:
+ #if (v.nature & Nature.T_VERTEX) != 0:
+ fe = get_fedge(v, vnext)
+ qi = fe.viewedge.qi
+ if qi != 0:
+ invisible = 1
+ else:
+ invisible = 0
+ if invisible:
+ v.attribute.visible = False
+ it.increment()
+ it2.increment()
+
+class pyTVertexOrientationShader(StrokeShader):
+ def __init__(self):
+ StrokeShader.__init__(self)
+ self._Get2dDirection = Orientation2DF1D()
+ ## finds the TVertex orientation from the TVertex and
+ ## the previous or next edge
+ def findOrientation(self, tv, ve):
+ mateVE = tv.get_mate(ve)
+ if ve.qi != 0 or mateVE.qi != 0:
+ ait = AdjacencyIterator(tv,1,0)
+ winner = None
+ incoming = True
+ while not ait.is_end:
+ ave = ait.object
+ if ave.id != ve.id and ave.id != mateVE.id:
+ winner = ait.object
+ if not ait.isIncoming(): # FIXME
+ incoming = False
+ break
+ ait.increment()
+ if winner is not None:
+ if not incoming:
+ direction = self._Get2dDirection(winner.last_fedge)
+ else:
+ direction = self._Get2dDirection(winner.first_fedge)
+ return direction
+ return None
+ def castToTVertex(self, cp):
+ if cp.t2d() == 0.0:
+ return cp.first_svertex.viewvertex
+ elif cp.t2d() == 1.0:
+ return cp.second_svertex.viewvertex
+ return None
+ def shade(self, stroke):
+ it = stroke.stroke_vertices_begin()
+ it2 = StrokeVertexIterator(it)
+ it2.increment()
+ ## case where the first vertex is a TVertex
+ v = it.object
+ if (v.nature & Nature.T_VERTEX) != 0:
+ tv = self.castToTVertex(v)
+ if tv is not None:
+ ve = get_fedge(v, it2.object).viewedge
+ dir = self.findOrientation(tv, ve)
+ if dir is not None:
+ #print(dir.x, dir.y)
+ v.attribute.set_attribute_vec2("orientation", dir)
+ while not it2.is_end:
+ vprevious = it.object
+ v = it2.object
+ if (v.nature & Nature.T_VERTEX) != 0:
+ tv = self.castToTVertex(v)
+ if tv is not None:
+ ve = get_fedge(vprevious, v).viewedge
+ dir = self.findOrientation(tv, ve)
+ if dir is not None:
+ #print(dir.x, dir.y)
+ v.attribute.set_attribute_vec2("orientation", dir)
+ it.increment()
+ it2.increment()
+ ## case where the last vertex is a TVertex
+ v = it.object
+ if (v.nature & Nature.T_VERTEX) != 0:
+ itPrevious = StrokeVertexIterator(it)
+ itPrevious.decrement()
+ tv = self.castToTVertex(v)
+ if tv is not None:
+ ve = get_fedge(itPrevious.object, v).viewedge
+ dir = self.findOrientation(tv, ve)
+ if dir is not None:
+ #print(dir.x, dir.y)
+ v.attribute.set_attribute_vec2("orientation", dir)
+
+class pySinusDisplacementShader(StrokeShader):
+ def __init__(self, f, a):
+ StrokeShader.__init__(self)
+ self._f = f
+ self._a = a
+ self._getNormal = Normal2DF0D()
+ def shade(self, stroke):
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ v = it.object
+ #print(self._getNormal.name)
+ n = self._getNormal(Interface0DIterator(it))
+ p = v.point
+ u = v.u
+ a = self._a*(1-2*(math.fabs(u-0.5)))
+ n = n*a*math.cos(self._f*u*6.28)
+ #print(n.x, n.y)
+ v.point = p+n
+ #v.point = v.point+n*a*math.cos(f*v.u)
+ it.increment()
+ stroke.update_length()
+
+class pyPerlinNoise1DShader(StrokeShader):
+ def __init__(self, freq = 10, amp = 10, oct = 4, seed = -1):
+ StrokeShader.__init__(self)
+ self.__noise = Noise(seed)
+ self.__freq = freq
+ self.__amp = amp
+ self.__oct = oct
+ def shade(self, stroke):
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ v = it.object
+ i = v.projected_x + v.projected_y
+ nres = self.__noise.turbulence1(i, self.__freq, self.__amp, self.__oct)
+ v.point = (v.projected_x + nres, v.projected_y + nres)
+ it.increment()
+ stroke.update_length()
+
+class pyPerlinNoise2DShader(StrokeShader):
+ def __init__(self, freq = 10, amp = 10, oct = 4, seed = -1):
+ StrokeShader.__init__(self)
+ self.__noise = Noise(seed)
+ self.__freq = freq
+ self.__amp = amp
+ self.__oct = oct
+ def shade(self, stroke):
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ v = it.object
+ vec = mathutils.Vector([v.projected_x, v.projected_y])
+ nres = self.__noise.turbulence2(vec, self.__freq, self.__amp, self.__oct)
+ v.point = (v.projected_x + nres, v.projected_y + nres)
+ it.increment()
+ stroke.update_length()
+
+class pyBluePrintCirclesShader(StrokeShader):
+ def __init__(self, turns = 1, random_radius = 3, random_center = 5):
+ StrokeShader.__init__(self)
+ self.__turns = turns
+ self.__random_center = random_center
+ self.__random_radius = random_radius
+ def shade(self, stroke):
+ it = stroke.stroke_vertices_begin()
+ if it.is_end:
+ return
+ p_min = it.object.point.copy()
+ p_max = it.object.point.copy()
+ while not it.is_end:
+ p = it.object.point
+ if p.x < p_min.x:
+ p_min.x = p.x
+ if p.x > p_max.x:
+ p_max.x = p.x
+ if p.y < p_min.y:
+ p_min.y = p.y
+ if p.y > p_max.y:
+ p_max.y = p.y
+ it.increment()
+ stroke.resample(32 * self.__turns)
+ sv_nb = stroke.stroke_vertices_size()
+# print("min :", p_min.x, p_min.y) # DEBUG
+# print("mean :", p_sum.x, p_sum.y) # DEBUG
+# print("max :", p_max.x, p_max.y) # DEBUG
+# print("----------------------") # DEBUG
+#######################################################
+ sv_nb = sv_nb // self.__turns
+ center = (p_min + p_max) / 2
+ radius = (center.x - p_min.x + center.y - p_min.y) / 2
+ p_new = mathutils.Vector([0, 0])
+#######################################################
+ R = self.__random_radius
+ C = self.__random_center
+ i = 0
+ it = stroke.stroke_vertices_begin()
+ for j in range(self.__turns):
+ prev_radius = radius
+ prev_center = center
+ radius = radius + random.randint(-R, R)
+ center = center + mathutils.Vector([random.randint(-C, C), random.randint(-C, C)])
+ while i < sv_nb and not it.is_end:
+ t = float(i) / float(sv_nb - 1)
+ r = prev_radius + (radius - prev_radius) * t
+ c = prev_center + (center - prev_center) * t
+ p_new.x = c.x + r * math.cos(2 * math.pi * t)
+ p_new.y = c.y + r * math.sin(2 * math.pi * t)
+ it.object.point = p_new
+ i = i + 1
+ it.increment()
+ i = 1
+ verticesToRemove = []
+ while not it.is_end:
+ verticesToRemove.append(it.object)
+ it.increment()
+ for sv in verticesToRemove:
+ stroke.remove_vertex(sv)
+ stroke.update_length()
+
+class pyBluePrintEllipsesShader(StrokeShader):
+ def __init__(self, turns = 1, random_radius = 3, random_center = 5):
+ StrokeShader.__init__(self)
+ self.__turns = turns
+ self.__random_center = random_center
+ self.__random_radius = random_radius
+ def shade(self, stroke):
+ it = stroke.stroke_vertices_begin()
+ if it.is_end:
+ return
+ p_min = it.object.point.copy()
+ p_max = it.object.point.copy()
+ while not it.is_end:
+ p = it.object.point
+ if p.x < p_min.x:
+ p_min.x = p.x
+ if p.x > p_max.x:
+ p_max.x = p.x
+ if p.y < p_min.y:
+ p_min.y = p.y
+ if p.y > p_max.y:
+ p_max.y = p.y
+ it.increment()
+ stroke.resample(32 * self.__turns)
+ sv_nb = stroke.stroke_vertices_size()
+ sv_nb = sv_nb // self.__turns
+ center = (p_min + p_max) / 2
+ radius = center - p_min
+ p_new = mathutils.Vector([0, 0])
+#######################################################
+ R = self.__random_radius
+ C = self.__random_center
+ i = 0
+ it = stroke.stroke_vertices_begin()
+ for j in range(self.__turns):
+ prev_radius = radius
+ prev_center = center
+ radius = radius + mathutils.Vector([random.randint(-R, R), random.randint(-R, R)])
+ center = center + mathutils.Vector([random.randint(-C, C), random.randint(-C, C)])
+ while i < sv_nb and not it.is_end:
+ t = float(i) / float(sv_nb - 1)
+ r = prev_radius + (radius - prev_radius) * t
+ c = prev_center + (center - prev_center) * t
+ p_new.x = c.x + r.x * math.cos(2 * math.pi * t)
+ p_new.y = c.y + r.y * math.sin(2 * math.pi * t)
+ it.object.point = p_new
+ i = i + 1
+ it.increment()
+ i = 1
+ verticesToRemove = []
+ while not it.is_end:
+ verticesToRemove.append(it.object)
+ it.increment()
+ for sv in verticesToRemove:
+ stroke.remove_vertex(sv)
+ stroke.update_length()
+
+
+class pyBluePrintSquaresShader(StrokeShader):
+ def __init__(self, turns = 1, bb_len = 10, bb_rand = 0):
+ StrokeShader.__init__(self)
+ self.__turns = turns
+ self.__bb_len = bb_len
+ self.__bb_rand = bb_rand
+ def shade(self, stroke):
+ it = stroke.stroke_vertices_begin()
+ if it.is_end:
+ return
+ p_min = it.object.point.copy()
+ p_max = it.object.point.copy()
+ while not it.is_end:
+ p = it.object.point
+ if p.x < p_min.x:
+ p_min.x = p.x
+ if p.x > p_max.x:
+ p_max.x = p.x
+ if p.y < p_min.y:
+ p_min.y = p.y
+ if p.y > p_max.y:
+ p_max.y = p.y
+ it.increment()
+ stroke.resample(32 * self.__turns)
+ sv_nb = stroke.stroke_vertices_size()
+#######################################################
+ sv_nb = sv_nb // self.__turns
+ first = sv_nb // 4
+ second = 2 * first
+ third = 3 * first
+ fourth = sv_nb
+ p_first = mathutils.Vector([p_min.x - self.__bb_len, p_min.y])
+ p_first_end = mathutils.Vector([p_max.x + self.__bb_len, p_min.y])
+ p_second = mathutils.Vector([p_max.x, p_min.y - self.__bb_len])
+ p_second_end = mathutils.Vector([p_max.x, p_max.y + self.__bb_len])
+ p_third = mathutils.Vector([p_max.x + self.__bb_len, p_max.y])
+ p_third_end = mathutils.Vector([p_min.x - self.__bb_len, p_max.y])
+ p_fourth = mathutils.Vector([p_min.x, p_max.y + self.__bb_len])
+ p_fourth_end = mathutils.Vector([p_min.x, p_min.y - self.__bb_len])
+#######################################################
+ R = self.__bb_rand
+ r = self.__bb_rand // 2
+ it = stroke.stroke_vertices_begin()
+ visible = True
+ for j in range(self.__turns):
+ p_first = p_first + mathutils.Vector([random.randint(-R, R), random.randint(-r, r)])
+ p_first_end = p_first_end + mathutils.Vector([random.randint(-R, R), random.randint(-r, r)])
+ p_second = p_second + mathutils.Vector([random.randint(-r, r), random.randint(-R, R)])
+ p_second_end = p_second_end + mathutils.Vector([random.randint(-r, r), random.randint(-R, R)])
+ p_third = p_third + mathutils.Vector([random.randint(-R, R), random.randint(-r, r)])
+ p_third_end = p_third_end + mathutils.Vector([random.randint(-R, R), random.randint(-r, r)])
+ p_fourth = p_fourth + mathutils.Vector([random.randint(-r, r), random.randint(-R, R)])
+ p_fourth_end = p_fourth_end + mathutils.Vector([random.randint(-r, r), random.randint(-R, R)])
+ vec_first = p_first_end - p_first
+ vec_second = p_second_end - p_second
+ vec_third = p_third_end - p_third
+ vec_fourth = p_fourth_end - p_fourth
+ i = 0
+ while i < sv_nb and not it.is_end:
+ if i < first:
+ p_new = p_first + vec_first * float(i)/float(first - 1)
+ if i == first - 1:
+ visible = False
+ elif i < second:
+ p_new = p_second + vec_second * float(i - first)/float(second - first - 1)
+ if i == second - 1:
+ visible = False
+ elif i < third:
+ p_new = p_third + vec_third * float(i - second)/float(third - second - 1)
+ if i == third - 1:
+ visible = False
+ else:
+ p_new = p_fourth + vec_fourth * float(i - third)/float(fourth - third - 1)
+ if i == fourth - 1:
+ visible = False
+ if it.object == None:
+ i = i + 1
+ it.increment()
+ if not visible:
+ visible = True
+ continue
+ it.object.point = p_new
+ it.object.attribute.visible = visible
+ if not visible:
+ visible = True
+ i = i + 1
+ it.increment()
+ verticesToRemove = []
+ while not it.is_end:
+ verticesToRemove.append(it.object)
+ it.increment()
+ for sv in verticesToRemove:
+ stroke.remove_vertex(sv)
+ stroke.update_length()
+
+
+class pyBluePrintDirectedSquaresShader(StrokeShader):
+ def __init__(self, turns = 1, bb_len = 10, mult = 1):
+ StrokeShader.__init__(self)
+ self.__mult = mult
+ self.__turns = turns
+ self.__bb_len = 1 + float(bb_len) / 100
+ def shade(self, stroke):
+ stroke.resample(32 * self.__turns)
+ p_mean = mathutils.Vector([0, 0])
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ p = it.object.point
+ p_mean = p_mean + p
+ it.increment()
+ sv_nb = stroke.stroke_vertices_size()
+ p_mean = p_mean / sv_nb
+ p_var_xx = 0
+ p_var_yy = 0
+ p_var_xy = 0
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ p = it.object.point
+ p_var_xx = p_var_xx + math.pow(p.x - p_mean.x, 2)
+ p_var_yy = p_var_yy + math.pow(p.y - p_mean.y, 2)
+ p_var_xy = p_var_xy + (p.x - p_mean.x) * (p.y - p_mean.y)
+ it.increment()
+ p_var_xx = p_var_xx / sv_nb
+ p_var_yy = p_var_yy / sv_nb
+ p_var_xy = p_var_xy / sv_nb
+## print(p_var_xx, p_var_yy, p_var_xy)
+ trace = p_var_xx + p_var_yy
+ det = p_var_xx * p_var_yy - p_var_xy * p_var_xy
+ sqrt_coeff = math.sqrt(trace * trace - 4 * det)
+ lambda1 = (trace + sqrt_coeff) / 2
+ lambda2 = (trace - sqrt_coeff) / 2
+## print(lambda1, lambda2)
+ theta = math.atan(2 * p_var_xy / (p_var_xx - p_var_yy)) / 2
+## print(theta)
+ if p_var_yy > p_var_xx:
+ e1 = mathutils.Vector([math.cos(theta + math.pi / 2), math.sin(theta + math.pi / 2)]) * math.sqrt(lambda1) * self.__mult
+ e2 = mathutils.Vector([math.cos(theta + math.pi), math.sin(theta + math.pi)]) * math.sqrt(lambda2) * self.__mult
+ else:
+ e1 = mathutils.Vector([math.cos(theta), math.sin(theta)]) * math.sqrt(lambda1) * self.__mult
+ e2 = mathutils.Vector([math.cos(theta + math.pi / 2), math.sin(theta + math.pi / 2)]) * math.sqrt(lambda2) * self.__mult
+#######################################################
+ sv_nb = sv_nb // self.__turns
+ first = sv_nb // 4
+ second = 2 * first
+ third = 3 * first
+ fourth = sv_nb
+ bb_len1 = self.__bb_len
+ bb_len2 = 1 + (bb_len1 - 1) * math.sqrt(lambda1 / lambda2)
+ p_first = p_mean - e1 - e2 * bb_len2
+ p_second = p_mean - e1 * bb_len1 + e2
+ p_third = p_mean + e1 + e2 * bb_len2
+ p_fourth = p_mean + e1 * bb_len1 - e2
+ vec_first = e2 * bb_len2 * 2
+ vec_second = e1 * bb_len1 * 2
+ vec_third = vec_first * -1
+ vec_fourth = vec_second * -1
+#######################################################
+ it = stroke.stroke_vertices_begin()
+ visible = True
+ for j in range(self.__turns):
+ i = 0
+ while i < sv_nb:
+ if i < first:
+ p_new = p_first + vec_first * float(i)/float(first - 1)
+ if i == first - 1:
+ visible = False
+ elif i < second:
+ p_new = p_second + vec_second * float(i - first)/float(second - first - 1)
+ if i == second - 1:
+ visible = False
+ elif i < third:
+ p_new = p_third + vec_third * float(i - second)/float(third - second - 1)
+ if i == third - 1:
+ visible = False
+ else:
+ p_new = p_fourth + vec_fourth * float(i - third)/float(fourth - third - 1)
+ if i == fourth - 1:
+ visible = False
+ it.object.point = p_new
+ it.object.attribute.visible = visible
+ if not visible:
+ visible = True
+ i = i + 1
+ it.increment()
+ verticesToRemove = []
+ while not it.is_end:
+ verticesToRemove.append(it.object)
+ it.increment()
+ for sv in verticesToRemove:
+ stroke.remove_vertex(sv)
+ stroke.update_length()
+
+class pyModulateAlphaShader(StrokeShader):
+ def __init__(self, min = 0, max = 1):
+ StrokeShader.__init__(self)
+ self.__min = min
+ self.__max = max
+ def shade(self, stroke):
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ alpha = it.object.attribute.alpha
+ p = it.object.point
+ alpha = alpha * p.y / 400
+ if alpha < self.__min:
+ alpha = self.__min
+ elif alpha > self.__max:
+ alpha = self.__max
+ it.object.attribute.alpha = alpha
+ it.increment()
+
+## various
+class pyDummyShader(StrokeShader):
+ def shade(self, stroke):
+ it = stroke.stroke_vertices_begin()
+ while not it.is_end:
+ toto = Interface0DIterator(it)
+ att = it.object.attribute
+ att.color = (0.3, 0.4, 0.4)
+ att.thickness = (0, 5)
+ it.increment()
+
+class pyDebugShader(StrokeShader):
+ def shade(self, stroke):
+ fe = CF.get_selected_fedge()
+ id1 = fe.first_svertex.id
+ id2 = fe.second_svertex.id
+ #print(id1.first, id1.second)
+ #print(id2.first, id2.second)
+ it = stroke.stroke_vertices_begin()
+ found = True
+ foundfirst = True
+ foundsecond = False
+ while not it.is_end:
+ cp = it.object
+ if cp.first_svertex.id == id1 or cp.second_svertex.id == id1:
+ foundfirst = True
+ if cp.first_svertex.id == id2 or cp.second_svertex.id == id2:
+ foundsecond = True
+ if foundfirst and foundsecond:
+ found = True
+ break
+ it.increment()
+ if found:
+ print("The selected Stroke id is: ", stroke.id.first, stroke.id.second)
diff --git a/release/scripts/freestyle/style_modules/sketchy_multiple_parameterization.py b/release/scripts/freestyle/style_modules/sketchy_multiple_parameterization.py
new file mode 100644
index 00000000000..e10b0c32b55
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/sketchy_multiple_parameterization.py
@@ -0,0 +1,43 @@
+# ##### 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 : sketchy_multiple_parameterization.py
+# Author : Stephane Grabli
+# Date : 04/08/2005
+# Purpose : Builds sketchy strokes whose topology relies on a
+# parameterization that covers the complete lines (visible+invisible)
+# whereas only the visible portions are actually drawn
+
+from ChainingIterators import pySketchyChainSilhouetteIterator
+from Freestyle import IncreasingColorShader, IncreasingThicknessShader, Operators, \
+ QuantitativeInvisibilityUP1D, SamplingShader, SmoothingShader, SpatialNoiseShader, \
+ TextureAssignerShader, TrueUP1D
+from shaders import pyHLRShader
+
+Operators.select(QuantitativeInvisibilityUP1D(0))
+Operators.bidirectional_chain(pySketchyChainSilhouetteIterator(3, False))
+shaders_list = [
+ SamplingShader(2),
+ SpatialNoiseShader(15, 120, 2, True, True),
+ IncreasingThicknessShader(5, 30),
+ SmoothingShader(100, 0.05, 0, 0.2, 0, 0, 0, 1),
+ IncreasingColorShader(0, 0.2, 0, 1, 0.2, 0.7, 0.2, 1),
+ TextureAssignerShader(6),
+ pyHLRShader(),
+ ]
+Operators.create(TrueUP1D(), shaders_list)
diff --git a/release/scripts/freestyle/style_modules/sketchy_topology_broken.py b/release/scripts/freestyle/style_modules/sketchy_topology_broken.py
new file mode 100644
index 00000000000..7c99946c813
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/sketchy_topology_broken.py
@@ -0,0 +1,47 @@
+# ##### 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 : sketchy_topology_broken.py
+# Author : Stephane Grabli
+# Date : 04/08/2005
+# Purpose : The topology of the strokes is, first, built
+# independantly from the 3D topology of objects,
+# and, second, so as to chain several times the same ViewEdge.
+
+from ChainingIterators import pySketchyChainingIterator
+from Freestyle import IncreasingColorShader, IncreasingThicknessShader, Operators, \
+ QuantitativeInvisibilityUP1D, SamplingShader, SmoothingShader, SpatialNoiseShader, \
+ TextureAssignerShader, TrueUP1D
+from shaders import pyBackboneStretcherNoCuspShader
+
+Operators.select(QuantitativeInvisibilityUP1D(0))
+## Chain 3 times each ViewEdge indpendantly from the
+## initial objects topology
+Operators.bidirectional_chain(pySketchyChainingIterator(3))
+shaders_list = [
+ SamplingShader(4),
+ SpatialNoiseShader(6, 120, 2, True, True),
+ IncreasingThicknessShader(4, 10),
+ SmoothingShader(100, 0.1, 0, 0.2, 0, 0, 0, 1),
+ pyBackboneStretcherNoCuspShader(20),
+ #ConstantColorShader(0.0, 0.0, 0.0)
+ IncreasingColorShader(0.2, 0.2, 0.2, 1, 0.5, 0.5, 0.5, 1),
+ #IncreasingColorShader(1, 0, 0, 1, 0, 1, 0, 1),
+ TextureAssignerShader(4),
+ ]
+Operators.create(TrueUP1D(), shaders_list)
diff --git a/release/scripts/freestyle/style_modules/sketchy_topology_preserved.py b/release/scripts/freestyle/style_modules/sketchy_topology_preserved.py
new file mode 100644
index 00000000000..4b525b97f04
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/sketchy_topology_preserved.py
@@ -0,0 +1,42 @@
+# ##### 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 : sketchy_topology_preserved.py
+# Author : Stephane Grabli
+# Date : 04/08/2005
+# Purpose : The topology of the strokes is built
+# so as to chain several times the same ViewEdge.
+# The topology of the objects is preserved
+
+from ChainingIterators import pySketchyChainSilhouetteIterator
+from Freestyle import ConstantColorShader, IncreasingThicknessShader, Operators, \
+ QuantitativeInvisibilityUP1D, SamplingShader, SmoothingShader, SpatialNoiseShader, \
+ TextureAssignerShader, TrueUP1D
+
+upred = QuantitativeInvisibilityUP1D(0)
+Operators.select(upred)
+Operators.bidirectional_chain(pySketchyChainSilhouetteIterator(3, True))
+shaders_list = [
+ SamplingShader(4),
+ SpatialNoiseShader(20, 220, 2, True, True),
+ IncreasingThicknessShader(4, 8),
+ SmoothingShader(300, 0.05, 0, 0.2, 0, 0, 0, 0.5),
+ ConstantColorShader(0.6, 0.2, 0.0),
+ TextureAssignerShader(4),
+ ]
+Operators.create(TrueUP1D(), shaders_list)
diff --git a/release/scripts/freestyle/style_modules/split_at_highest_2d_curvatures.py b/release/scripts/freestyle/style_modules/split_at_highest_2d_curvatures.py
new file mode 100644
index 00000000000..4e635bc4eee
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/split_at_highest_2d_curvatures.py
@@ -0,0 +1,41 @@
+# ##### 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 : split_at_highest_2d_curvature.py
+# Author : Stephane Grabli
+# Date : 04/08/2005
+# Purpose : Draws the visible lines (chaining follows same nature lines)
+# (most basic style module)
+
+from Freestyle import ChainSilhouetteIterator, ConstantThicknessShader, IncreasingColorShader, \
+ Operators, QuantitativeInvisibilityUP1D, TextureAssignerShader, TrueUP1D
+from Functions0D import pyInverseCurvature2DAngleF0D
+from PredicatesU0D import pyParameterUP0D
+from PredicatesU1D import pyHigherLengthUP1D
+from logical_operators import NotUP1D
+
+Operators.select(QuantitativeInvisibilityUP1D(0))
+Operators.bidirectional_chain(ChainSilhouetteIterator(), NotUP1D(QuantitativeInvisibilityUP1D(0)))
+func = pyInverseCurvature2DAngleF0D()
+Operators.recursive_split(func, pyParameterUP0D(0.4, 0.6), NotUP1D(pyHigherLengthUP1D(100)), 2)
+shaders_list = [
+ ConstantThicknessShader(10),
+ IncreasingColorShader(1, 0, 0, 1, 0, 1, 0, 1),
+ TextureAssignerShader(3),
+ ]
+Operators.create(TrueUP1D(), shaders_list)
diff --git a/release/scripts/freestyle/style_modules/split_at_tvertices.py b/release/scripts/freestyle/style_modules/split_at_tvertices.py
new file mode 100644
index 00000000000..122b79450c6
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/split_at_tvertices.py
@@ -0,0 +1,40 @@
+# ##### 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 : split_at_tvertices.py
+# Author : Stephane Grabli
+# Date : 04/08/2005
+# Purpose : Draws strokes that starts and stops at Tvertices (visible or not)
+
+from Freestyle import ChainSilhouetteIterator, ConstantThicknessShader, IncreasingColorShader, \
+ Nature, Operators, QuantitativeInvisibilityUP1D, TextureAssignerShader, TrueUP1D
+from PredicatesU0D import pyVertexNatureUP0D
+from logical_operators import NotUP1D
+
+Operators.select(QuantitativeInvisibilityUP1D(0))
+Operators.bidirectional_chain(ChainSilhouetteIterator(), NotUP1D(QuantitativeInvisibilityUP1D(0)))
+start = pyVertexNatureUP0D(Nature.T_VERTEX)
+## use the same predicate to decide where to start and where to stop
+## the strokes:
+Operators.sequential_split(start, start, 10)
+shaders_list = [
+ ConstantThicknessShader(5),
+ IncreasingColorShader(1, 0, 0, 1, 0, 1, 0, 1),
+ TextureAssignerShader(3),
+ ]
+Operators.create(TrueUP1D(), shaders_list)
diff --git a/release/scripts/freestyle/style_modules/stroke_texture.py b/release/scripts/freestyle/style_modules/stroke_texture.py
new file mode 100644
index 00000000000..07fce377866
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/stroke_texture.py
@@ -0,0 +1,38 @@
+# ##### 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 : stroke_texture.py
+# Author : Stephane Grabli
+# Date : 04/08/2005
+# Purpose : Draws textured strokes (illustrate the StrokeTextureShader shader)
+
+from Freestyle import BezierCurveShader, ChainSilhouetteIterator, ConstantColorShader, \
+ ConstantThicknessShader, Operators, QuantitativeInvisibilityUP1D, SamplingShader, \
+ Stroke, StrokeTextureShader, TrueUP1D
+from logical_operators import NotUP1D
+
+Operators.select(QuantitativeInvisibilityUP1D(0))
+Operators.bidirectional_chain(ChainSilhouetteIterator(), NotUP1D(QuantitativeInvisibilityUP1D(0)))
+shaders_list = [
+ SamplingShader(3),
+ BezierCurveShader(4),
+ StrokeTextureShader("washbrushAlpha.bmp", Stroke.DRY_MEDIUM, True),
+ ConstantThicknessShader(40),
+ ConstantColorShader(0, 0, 0, 1),
+ ]
+Operators.create(TrueUP1D(), shaders_list)
diff --git a/release/scripts/freestyle/style_modules/suggestive.py b/release/scripts/freestyle/style_modules/suggestive.py
new file mode 100644
index 00000000000..3d0e031acc9
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/suggestive.py
@@ -0,0 +1,38 @@
+# ##### 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 : suggestive.py
+# Author : Stephane Grabli
+# Date : 04/08/2005
+# Purpose : Draws the suggestive contours.
+# ***** The suggestive contours must be enabled
+# in the options dialog *****
+
+from Freestyle import ChainSilhouetteIterator, ConstantColorShader, IncreasingThicknessShader, \
+ Nature, Operators, QuantitativeInvisibilityUP1D, TrueUP1D
+from PredicatesU1D import pyNatureUP1D
+from logical_operators import AndUP1D, NotUP1D
+
+upred = AndUP1D(pyNatureUP1D(Nature.SUGGESTIVE_CONTOUR), QuantitativeInvisibilityUP1D(0))
+Operators.select(upred)
+Operators.bidirectional_chain(ChainSilhouetteIterator(), NotUP1D(upred))
+shaders_list = [
+ IncreasingThicknessShader(1, 3),
+ ConstantColorShader(0.2, 0.2, 0.2, 1),
+ ]
+Operators.create(TrueUP1D(), shaders_list)
diff --git a/release/scripts/freestyle/style_modules/thickness_fof_depth_discontinuity.py b/release/scripts/freestyle/style_modules/thickness_fof_depth_discontinuity.py
new file mode 100644
index 00000000000..9dff325bb00
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/thickness_fof_depth_discontinuity.py
@@ -0,0 +1,37 @@
+# ##### 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 : thickness_fof_depth_discontinuity.py
+# Author : Stephane Grabli
+# Date : 04/08/2005
+# Purpose : Assigns to strokes a thickness that depends on the depth discontinuity
+
+from Freestyle import ChainSilhouetteIterator, ConstantColorShader, ConstantThicknessShader, \
+ Operators, QuantitativeInvisibilityUP1D, SamplingShader, TrueUP1D
+from logical_operators import NotUP1D
+from shaders import pyDepthDiscontinuityThicknessShader
+
+Operators.select(QuantitativeInvisibilityUP1D(0))
+Operators.bidirectional_chain(ChainSilhouetteIterator(), NotUP1D(QuantitativeInvisibilityUP1D(0)))
+shaders_list = [
+ SamplingShader(1),
+ ConstantThicknessShader(3),
+ ConstantColorShader(0.0, 0.0, 0.0),
+ pyDepthDiscontinuityThicknessShader(0.8, 6),
+ ]
+Operators.create(TrueUP1D(), shaders_list)
diff --git a/release/scripts/freestyle/style_modules/tipremover.py b/release/scripts/freestyle/style_modules/tipremover.py
new file mode 100644
index 00000000000..92918840bec
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/tipremover.py
@@ -0,0 +1,36 @@
+# ##### 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 : tipremover.py
+# Author : Stephane Grabli
+# Date : 04/08/2005
+# Purpose : Removes strokes extremities
+
+from Freestyle import ChainSilhouetteIterator, ConstantColorShader, ConstantThicknessShader, \
+ Operators, QuantitativeInvisibilityUP1D, SamplingShader, TipRemoverShader, TrueUP1D
+from logical_operators import NotUP1D
+
+Operators.select(QuantitativeInvisibilityUP1D(0))
+Operators.bidirectional_chain(ChainSilhouetteIterator(), NotUP1D(QuantitativeInvisibilityUP1D(0)))
+shaders_list = [
+ SamplingShader(5),
+ ConstantThicknessShader(3),
+ ConstantColorShader(0, 0, 0),
+ TipRemoverShader(20),
+ ]
+Operators.create(TrueUP1D(), shaders_list)
diff --git a/release/scripts/freestyle/style_modules/tvertex_remover.py b/release/scripts/freestyle/style_modules/tvertex_remover.py
new file mode 100644
index 00000000000..962425fa009
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/tvertex_remover.py
@@ -0,0 +1,37 @@
+# ##### 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 : tvertex_remover.py
+# Author : Stephane Grabli
+# Date : 04/08/2005
+# Purpose : Removes TVertices
+
+from Freestyle import ChainSilhouetteIterator, ConstantColorShader, IncreasingThicknessShader, \
+ Operators, QuantitativeInvisibilityUP1D, SamplingShader, TrueUP1D
+from logical_operators import NotUP1D
+from shaders import pyTVertexRemoverShader
+
+Operators.select(QuantitativeInvisibilityUP1D(0))
+Operators.bidirectional_chain(ChainSilhouetteIterator(), NotUP1D(QuantitativeInvisibilityUP1D(0)))
+shaders_list = [
+ IncreasingThicknessShader(3, 5),
+ ConstantColorShader(0.2, 0.2, 0.2, 1),
+ SamplingShader(10.0),
+ pyTVertexRemoverShader(),
+ ]
+Operators.create(TrueUP1D(), shaders_list)
diff --git a/release/scripts/freestyle/style_modules/uniformpruning_zsort.py b/release/scripts/freestyle/style_modules/uniformpruning_zsort.py
new file mode 100644
index 00000000000..b9fcb33e895
--- /dev/null
+++ b/release/scripts/freestyle/style_modules/uniformpruning_zsort.py
@@ -0,0 +1,39 @@
+# ##### 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 : uniformpruning_zsort.py
+# Authors : Fredo Durand, Stephane Grabli, Francois Sillion, Emmanuel Turquin
+# Date : 08/04/2005
+
+from Freestyle import ChainSilhouetteIterator, ConstantColorShader, ConstantThicknessShader, IntegrationType, \
+ Operators, QuantitativeInvisibilityUP1D, SamplingShader, Stroke, StrokeTextureShader
+from PredicatesB1D import pyZBP1D
+from PredicatesU1D import pyDensityUP1D
+
+Operators.select(QuantitativeInvisibilityUP1D(0))
+Operators.bidirectional_chain(ChainSilhouetteIterator())
+#Operators.sequential_split(pyVertexNatureUP0D(Nature.VIEW_VERTEX), 2)
+Operators.sort(pyZBP1D())
+shaders_list = [
+ StrokeTextureShader("smoothAlpha.bmp", Stroke.OPAQUE_MEDIUM, False),
+ ConstantThicknessShader(3),
+ SamplingShader(5.0),
+ ConstantColorShader(0, 0, 0, 1),
+ ]
+Operators.create(pyDensityUP1D(2, 0.05, IntegrationType.MEAN, 4), shaders_list)
+#Operators.create(pyDensityFunctorUP1D(8, 0.03, pyGetInverseProjectedZF1D(), 0, 1, IntegrationType.MEAN), shaders_list)
diff --git a/release/scripts/startup/bl_operators/__init__.py b/release/scripts/startup/bl_operators/__init__.py
index 3ff02420bbd..64851a3a4c1 100644
--- a/release/scripts/startup/bl_operators/__init__.py
+++ b/release/scripts/startup/bl_operators/__init__.py
@@ -22,7 +22,7 @@ if "bpy" in locals():
from imp import reload as _reload
for val in _modules_loaded.values():
_reload(val)
-_modules = (
+_modules = [
"add_mesh_torus",
"anim",
"clip",
@@ -44,16 +44,18 @@ _modules = (
"vertexpaint_dirt",
"view3d",
"wm",
-)
+]
+
+import bpy
+
+if bpy.app.build_options.freestyle:
+ _modules.append("freestyle")
__import__(name=__name__, fromlist=_modules)
_namespace = globals()
-_modules_loaded = {name: _namespace[name] for name in _modules}
+_modules_loaded = {name: _namespace[name] for name in _modules if name != 'bpy'}
del _namespace
-import bpy
-
-
def register():
bpy.utils.register_module(__name__)
diff --git a/release/scripts/startup/bl_operators/freestyle.py b/release/scripts/startup/bl_operators/freestyle.py
new file mode 100644
index 00000000000..19e6f67ba77
--- /dev/null
+++ b/release/scripts/startup/bl_operators/freestyle.py
@@ -0,0 +1,135 @@
+# ##### 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 #####
+
+import sys
+import bpy
+
+from bpy.props import (EnumProperty, StringProperty)
+
+
+class SCENE_OT_freestyle_fill_range_by_selection(bpy.types.Operator):
+ '''Fill the Range Min/Max entries by the min/max distance between selected mesh objects and the source object
+ (either a user-specified object or the active camera)'''
+ bl_idname = "scene.freestyle_fill_range_by_selection"
+ bl_label = "Fill Range by Selection"
+
+ type = EnumProperty(name="Type", description="Type of the modifier to work on",
+ items=(("COLOR", "Color", "Color modifier type"),
+ ("ALPHA", "Alpha", "Alpha modifier type"),
+ ("THICKNESS", "Thickness", "Thickness modifier type")))
+ name = StringProperty(name="Name", description="Name of the modifier to work on")
+
+ def execute(self, context):
+ rl = context.scene.render.layers.active
+ lineset = rl.freestyle_settings.linesets.active
+ linestyle = lineset.linestyle
+ # Find the modifier to work on
+ if self.type == 'COLOR':
+ m = linestyle.color_modifiers[self.name]
+ elif self.type == 'ALPHA':
+ m = linestyle.alpha_modifiers[self.name]
+ else:
+ m = linestyle.thickness_modifiers[self.name]
+ # Find the source object
+ if m.type == 'DISTANCE_FROM_CAMERA':
+ source = context.scene.camera
+ elif m.type == 'DISTANCE_FROM_OBJECT':
+ if m.target is None:
+ self.report({'ERROR'}, "Target object not specified")
+ return {'CANCELLED'}
+ source = m.target
+ else:
+ self.report({'ERROR'}, "Unexpected modifier type: " + m.type)
+ return {'CANCELLED'}
+ # Find selected mesh objects
+ selection = [ob for ob in context.scene.objects if ob.select and ob.type == 'MESH' and ob.name != source.name]
+ if len(selection) > 0:
+ # Compute the min/max distance between selected mesh objects and the source
+ min_dist = sys.float_info.max
+ max_dist = -min_dist
+ for ob in selection:
+ for vert in ob.data.vertices:
+ dist = (ob.matrix_world * vert.co - source.location).length
+ min_dist = min(dist, min_dist)
+ max_dist = max(dist, max_dist)
+ # Fill the Range Min/Max entries with the computed distances
+ m.range_min = min_dist
+ m.range_max = max_dist
+ return {'FINISHED'}
+
+
+class SCENE_OT_freestyle_add_edge_marks_to_keying_set(bpy.types.Operator):
+ '''Add the data paths to the Freestyle Edge Mark property of selected edges to the active keying set'''
+ bl_idname = "scene.freestyle_add_edge_marks_to_keying_set"
+ bl_label = "Add Edge Marks to Keying Set"
+ bl_options = {'UNDO'}
+
+ @classmethod
+ def poll(cls, context):
+ ob = context.active_object
+ return (ob and ob.type == 'MESH')
+
+ def execute(self, context):
+ # active keying set
+ scene = context.scene
+ ks = scene.keying_sets.active
+ if ks is None:
+ ks = scene.keying_sets.new(idname="FreestyleEdgeMarkKeyingSet", name="Freestyle Edge Mark Keying Set")
+ ks.bl_description = ""
+ # add data paths to the keying set
+ ob = context.active_object
+ ob_mode = ob.mode
+ mesh = ob.data
+ bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
+ for i, edge in enumerate(mesh.edges):
+ if not edge.hide and edge.select:
+ path = 'edges[%d].use_freestyle_edge_mark' % i
+ ks.paths.add(mesh, path, index=0)
+ bpy.ops.object.mode_set(mode=ob_mode, toggle=False)
+ return {'FINISHED'}
+
+
+class SCENE_OT_freestyle_add_face_marks_to_keying_set(bpy.types.Operator):
+ '''Add the data paths to the Freestyle Face Mark property of selected polygons to the active keying set'''
+ bl_idname = "scene.freestyle_add_face_marks_to_keying_set"
+ bl_label = "Add Face Marks to Keying Set"
+ bl_options = {'UNDO'}
+
+ @classmethod
+ def poll(cls, context):
+ ob = context.active_object
+ return (ob and ob.type == 'MESH')
+
+ def execute(self, context):
+ # active keying set
+ scene = context.scene
+ ks = scene.keying_sets.active
+ if ks is None:
+ ks = scene.keying_sets.new(idname="FreestyleFaceMarkKeyingSet", name="Freestyle Face Mark Keying Set")
+ ks.bl_description = ""
+ # add data paths to the keying set
+ ob = context.active_object
+ ob_mode = ob.mode
+ mesh = ob.data
+ bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
+ for i, polygon in enumerate(mesh.polygons):
+ if not polygon.hide and polygon.select:
+ path = 'polygons[%d].use_freestyle_face_mark' % i
+ ks.paths.add(mesh, path, index=0)
+ bpy.ops.object.mode_set(mode=ob_mode, toggle=False)
+ return {'FINISHED'}
diff --git a/release/scripts/startup/bl_ui/__init__.py b/release/scripts/startup/bl_ui/__init__.py
index 18fa5ea50b6..868fa2a311c 100644
--- a/release/scripts/startup/bl_ui/__init__.py
+++ b/release/scripts/startup/bl_ui/__init__.py
@@ -24,7 +24,7 @@ if "bpy" in locals():
from imp import reload as _reload
for val in _modules_loaded.values():
_reload(val)
-_modules = (
+_modules = [
"properties_animviz",
"properties_constraint",
"properties_data_armature",
@@ -53,6 +53,7 @@ _modules = (
"properties_physics_smoke",
"properties_physics_softbody",
"properties_render",
+ "properties_render_layer",
"properties_scene",
"properties_texture",
"properties_world",
@@ -74,16 +75,18 @@ _modules = (
"space_userpref",
"space_view3d",
"space_view3d_toolbar",
-)
+]
+
+import bpy
+
+if bpy.app.build_options.freestyle:
+ _modules.append("properties_freestyle")
__import__(name=__name__, fromlist=_modules)
_namespace = globals()
-_modules_loaded = {name: _namespace[name] for name in _modules}
+_modules_loaded = {name: _namespace[name] for name in _modules if name != 'bpy'}
del _namespace
-import bpy
-
-
def register():
bpy.utils.register_module(__name__)
diff --git a/release/scripts/startup/bl_ui/properties_freestyle.py b/release/scripts/startup/bl_ui/properties_freestyle.py
new file mode 100644
index 00000000000..523c58bfac3
--- /dev/null
+++ b/release/scripts/startup/bl_ui/properties_freestyle.py
@@ -0,0 +1,668 @@
+# ##### 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 #####
+
+# <pep8 compliant>
+import bpy
+from bpy.types import Menu, Panel, UIList
+from bl_ui.properties_render import RenderButtonsPanel
+from bl_ui.properties_render_layer import RenderLayerButtonsPanel
+
+
+# Render properties
+
+class RenderFreestyleButtonsPanel(RenderButtonsPanel):
+ # COMPAT_ENGINES must be defined in each subclass, external engines can add themselves here
+
+ @classmethod
+ def poll(cls, context):
+ if not super().poll(context):
+ return False
+ return bpy.app.build_options.freestyle
+
+
+class RENDER_PT_freestyle(RenderFreestyleButtonsPanel, Panel):
+ bl_label = "Freestyle"
+ bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+ def draw_header(self, context):
+ rd = context.scene.render
+ self.layout.prop(rd, "use_freestyle", text="")
+
+ def draw(self, context):
+ rd = context.scene.render
+
+ layout = self.layout
+ layout.active = rd.use_freestyle
+
+ row = layout.row()
+ row.label(text="Line Thickness:")
+ row.prop(rd, "line_thickness_mode", expand=True)
+ row = layout.row()
+ row.active = (rd.line_thickness_mode == 'ABSOLUTE')
+ row.prop(rd, "unit_line_thickness")
+
+
+# Render layer properties
+
+class RenderLayerFreestyleButtonsPanel(RenderLayerButtonsPanel):
+ # COMPAT_ENGINES must be defined in each subclass, external engines can add themselves here
+
+ @classmethod
+ def poll(cls, context):
+ if not super().poll(context):
+ return False
+ rd = context.scene.render
+ return bpy.app.build_options.freestyle and rd.use_freestyle and rd.layers.active
+
+
+class RenderLayerFreestyleEditorButtonsPanel(RenderLayerFreestyleButtonsPanel):
+ # COMPAT_ENGINES must be defined in each subclass, external engines can add themselves here
+
+ @classmethod
+ def poll(cls, context):
+ if not super().poll(context):
+ return False
+ rl = context.scene.render.layers.active
+ return rl and rl.freestyle_settings.mode == 'EDITOR'
+
+
+class RENDERLAYER_UL_linesets(UIList):
+ def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
+ lineset = item
+ if self.layout_type in {'DEFAULT', 'COMPACT'}:
+ layout.label(lineset.name, icon_value=icon)
+ layout.prop(lineset, "show_render", text="", index=index)
+ elif self.layout_type in {'GRID'}:
+ layout.alignment = 'CENTER'
+ layout.label("", icon_value=icon)
+
+##ifdef WITH_FREESTYLE
+# else if (RNA_struct_is_a(itemptr->type, &RNA_SceneRenderLayer) ||
+# RNA_struct_is_a(itemptr->type, &RNA_FreestyleLineSet)) {
+##else
+# else if (RNA_struct_is_a(itemptr->type, &RNA_SceneRenderLayer)) {
+##endif
+# uiItemL(sub, name, icon);
+# uiBlockSetEmboss(block, UI_EMBOSS);
+# uiDefButR(block, OPTION, 0, "", 0, 0, UI_UNIT_X, UI_UNIT_Y, itemptr, "use", 0, 0, 0, 0, 0, NULL);
+# }
+
+
+class RENDER_MT_lineset_specials(Menu):
+ bl_label = "Lineset Specials"
+
+ def draw(self, context):
+ layout = self.layout
+ layout.operator("scene.freestyle_lineset_copy", icon='COPYDOWN')
+ layout.operator("scene.freestyle_lineset_paste", icon='PASTEDOWN')
+
+
+class RENDERLAYER_PT_freestyle(RenderLayerFreestyleButtonsPanel, Panel):
+ bl_label = "Freestyle"
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+ def draw(self, context):
+ rd = context.scene.render
+ rl = rd.layers.active
+ freestyle = rl.freestyle_settings
+
+ layout = self.layout
+ layout.active = rl.use_freestyle
+ layout.prop(freestyle, "mode", text="Control mode")
+
+ col = layout.column()
+ col.label(text="Edge Detection Options:")
+ split = col.split()
+ sub = split.column()
+ sub.prop(freestyle, "crease_angle")
+ sub.prop(freestyle, "use_culling")
+ sub = split.column()
+ sub.prop(freestyle, "use_smoothness")
+ sub.prop(freestyle, "use_material_boundaries")
+ col.prop(freestyle, "use_advanced_options")
+ # Advanced options are hidden by default to warn new users
+ if freestyle.use_advanced_options:
+ split = col.split()
+ sub = split.column()
+ sub.active = freestyle.use_advanced_options
+ if freestyle.mode == 'SCRIPT':
+ sub.prop(freestyle, "use_ridges_and_valleys")
+ sub.prop(freestyle, "sphere_radius")
+ sub = split.column()
+ sub.active = freestyle.use_advanced_options
+ if freestyle.mode == 'SCRIPT':
+ sub.prop(freestyle, "use_suggestive_contours")
+ sub.prop(freestyle, "kr_derivative_epsilon")
+
+ if freestyle.mode == 'SCRIPT':
+ split = layout.split()
+ split.label("Style modules:")
+ split.operator("scene.freestyle_module_add", text="Add")
+ for i, module in enumerate(freestyle.modules):
+ box = layout.box()
+ box.context_pointer_set("freestyle_module", module)
+ row = box.row(align=True)
+ row.prop(module, "use", text="")
+ row.prop(module, "script", text="")
+ row.operator("scene.freestyle_module_remove", icon='X', text="")
+ row.operator("scene.freestyle_module_move", icon='TRIA_UP', text="").direction = 'UP'
+ row.operator("scene.freestyle_module_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
+
+
+class RENDERLAYER_PT_freestyle_lineset(RenderLayerFreestyleEditorButtonsPanel, Panel):
+ bl_label = "Freestyle Line Set"
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+ def draw_edge_type_buttons(self, box, lineset, edge_type):
+ # property names
+ select_edge_type = "select_" + edge_type
+ exclude_edge_type = "exclude_" + edge_type
+ # draw edge type buttons
+ row = box.row(align=True)
+ row.prop(lineset, select_edge_type)
+ sub = row.column()
+ sub.prop(lineset, exclude_edge_type, text="")
+ sub.active = getattr(lineset, select_edge_type)
+
+ def draw(self, context):
+ rd = context.scene.render
+ rl = rd.layers.active
+ freestyle = rl.freestyle_settings
+ lineset = freestyle.linesets.active
+
+ layout = self.layout
+ layout.active = rl.use_freestyle
+
+ col = layout.column()
+ row = col.row()
+ rows = 5 if lineset else 2
+ row.template_list("RENDERLAYER_UL_linesets", "", freestyle, "linesets", freestyle.linesets, "active_index", rows=rows)
+
+ sub = row.column()
+ subsub = sub.column(align=True)
+ subsub.operator("scene.freestyle_lineset_add", icon='ZOOMIN', text="")
+ subsub.operator("scene.freestyle_lineset_remove", icon='ZOOMOUT', text="")
+ subsub.menu("RENDER_MT_lineset_specials", icon='DOWNARROW_HLT', text="")
+ if lineset:
+ sub.separator()
+ subsub = sub.column(align=True)
+ subsub.operator("scene.freestyle_lineset_move", icon='TRIA_UP', text="").direction = 'UP'
+ subsub.operator("scene.freestyle_lineset_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
+
+ col.prop(lineset, "name")
+
+ col = layout.column()
+ col.label(text="Selection By:")
+ row = col.row(align=True)
+ row.prop(lineset, "select_by_visibility", text="Visibility", toggle=True)
+ row.prop(lineset, "select_by_edge_types", text="Edge Types", toggle=True)
+ row.prop(lineset, "select_by_face_marks", text="Face Marks", toggle=True)
+ row.prop(lineset, "select_by_group", text="Group", toggle=True)
+ row.prop(lineset, "select_by_image_border", text="Image Border", toggle=True)
+
+ if lineset.select_by_visibility:
+ col.label(text="Visibility:")
+ row = col.row(align=True)
+ row.prop(lineset, "visibility", expand=True)
+ if lineset.visibility == 'RANGE':
+ row = col.row(align=True)
+ row.prop(lineset, "qi_start")
+ row.prop(lineset, "qi_end")
+
+ if lineset.select_by_edge_types:
+ col.label(text="Edge Types:")
+ row = col.row()
+ row.prop(lineset, "edge_type_negation", expand=True)
+ row.prop(lineset, "edge_type_combination", expand=True)
+
+ split = col.split()
+ sub = split.column()
+ self.draw_edge_type_buttons(sub, lineset, "silhouette")
+ self.draw_edge_type_buttons(sub, lineset, "border")
+ self.draw_edge_type_buttons(sub, lineset, "contour")
+ self.draw_edge_type_buttons(sub, lineset, "suggestive_contour")
+ self.draw_edge_type_buttons(sub, lineset, "ridge_valley")
+ sub = split.column()
+ self.draw_edge_type_buttons(sub, lineset, "crease")
+ self.draw_edge_type_buttons(sub, lineset, "edge_mark")
+ self.draw_edge_type_buttons(sub, lineset, "external_contour")
+ self.draw_edge_type_buttons(sub, lineset, "material_boundary")
+
+ if lineset.select_by_face_marks:
+ col.label(text="Face Marks:")
+ row = col.row()
+ row.prop(lineset, "face_mark_negation", expand=True)
+ row.prop(lineset, "face_mark_condition", expand=True)
+
+ if lineset.select_by_group:
+ col.label(text="Group:")
+ row = col.row()
+ row.prop(lineset, "group", text="")
+ row.prop(lineset, "group_negation", expand=True)
+
+
+class RENDERLAYER_PT_freestyle_linestyle(RenderLayerFreestyleEditorButtonsPanel, Panel):
+ bl_label = "Freestyle Line Style"
+ bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+ def draw_modifier_box_header(self, box, modifier):
+ row = box.row()
+ row.context_pointer_set("modifier", modifier)
+ if modifier.expanded:
+ icon = 'TRIA_DOWN'
+ else:
+ icon = 'TRIA_RIGHT'
+ row.prop(modifier, "expanded", text="", icon=icon, emboss=False)
+ # TODO: Use icons rather than text label, would save some room!
+ row.label(text=modifier.rna_type.name)
+ row.prop(modifier, "name", text="")
+ if modifier.use:
+ icon = 'RESTRICT_RENDER_OFF'
+ else:
+ icon = 'RESTRICT_RENDER_ON'
+ row.prop(modifier, "use", text="", icon=icon)
+ sub = row.row(align=True)
+ sub.operator("scene.freestyle_modifier_copy", icon='NONE', text="Copy")
+ sub.operator("scene.freestyle_modifier_move", icon='TRIA_UP', text="").direction = 'UP'
+ sub.operator("scene.freestyle_modifier_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
+ sub.operator("scene.freestyle_modifier_remove", icon='X', text="")
+
+ def draw_modifier_common(self, box, modifier):
+ row = box.row()
+ row.prop(modifier, "blend", text="")
+ row.prop(modifier, "influence")
+
+ def draw_modifier_color_ramp_common(self, box, modifier, has_range):
+ box.template_color_ramp(modifier, "color_ramp", expand=True)
+ if has_range:
+ row = box.row(align=True)
+ row.prop(modifier, "range_min")
+ row.prop(modifier, "range_max")
+
+ def draw_modifier_curve_common(self, box, modifier, has_range, has_value):
+ row = box.row()
+ row.prop(modifier, "mapping", text="")
+ sub = row.column()
+ sub.prop(modifier, "invert")
+ if modifier.mapping == 'CURVE':
+ sub.active = False
+ box.template_curve_mapping(modifier, "curve")
+ if has_range:
+ row = box.row(align=True)
+ row.prop(modifier, "range_min")
+ row.prop(modifier, "range_max")
+ if has_value:
+ row = box.row(align=True)
+ row.prop(modifier, "value_min")
+ row.prop(modifier, "value_max")
+
+ def draw_color_modifier(self, context, modifier):
+ layout = self.layout
+
+ col = layout.column(align=True)
+ self.draw_modifier_box_header(col.box(), modifier)
+ if modifier.expanded:
+ box = col.box()
+ self.draw_modifier_common(box, modifier)
+
+ if modifier.type == 'ALONG_STROKE':
+ self.draw_modifier_color_ramp_common(box, modifier, False)
+
+ elif modifier.type == 'DISTANCE_FROM_OBJECT':
+ box.prop(modifier, "target")
+ self.draw_modifier_color_ramp_common(box, modifier, True)
+ prop = box.operator("scene.freestyle_fill_range_by_selection")
+ prop.type = 'COLOR'
+ prop.name = modifier.name
+
+ elif modifier.type == 'DISTANCE_FROM_CAMERA':
+ self.draw_modifier_color_ramp_common(box, modifier, True)
+ prop = box.operator("scene.freestyle_fill_range_by_selection")
+ prop.type = 'COLOR'
+ prop.name = modifier.name
+
+ elif modifier.type == 'MATERIAL':
+ row = box.row()
+ row.prop(modifier, "material_attr", text="")
+ sub = row.column()
+ sub.prop(modifier, "use_ramp")
+ if modifier.material_attr in {'DIFF', 'SPEC'}:
+ sub.active = True
+ show_ramp = modifier.use_ramp
+ else:
+ sub.active = False
+ show_ramp = True
+ if show_ramp:
+ self.draw_modifier_color_ramp_common(box, modifier, False)
+
+ def draw_alpha_modifier(self, context, modifier):
+ layout = self.layout
+
+ col = layout.column(align=True)
+ self.draw_modifier_box_header(col.box(), modifier)
+ if modifier.expanded:
+ box = col.box()
+ self.draw_modifier_common(box, modifier)
+
+ if modifier.type == 'ALONG_STROKE':
+ self.draw_modifier_curve_common(box, modifier, False, False)
+
+ elif modifier.type == 'DISTANCE_FROM_OBJECT':
+ box.prop(modifier, "target")
+ self.draw_modifier_curve_common(box, modifier, True, False)
+ prop = box.operator("scene.freestyle_fill_range_by_selection")
+ prop.type = 'ALPHA'
+ prop.name = modifier.name
+
+ elif modifier.type == 'DISTANCE_FROM_CAMERA':
+ self.draw_modifier_curve_common(box, modifier, True, False)
+ prop = box.operator("scene.freestyle_fill_range_by_selection")
+ prop.type = 'ALPHA'
+ prop.name = modifier.name
+
+ elif modifier.type == 'MATERIAL':
+ box.prop(modifier, "material_attr", text="")
+ self.draw_modifier_curve_common(box, modifier, False, False)
+
+ def draw_thickness_modifier(self, context, modifier):
+ layout = self.layout
+
+ col = layout.column(align=True)
+ self.draw_modifier_box_header(col.box(), modifier)
+ if modifier.expanded:
+ box = col.box()
+ self.draw_modifier_common(box, modifier)
+
+ if modifier.type == 'ALONG_STROKE':
+ self.draw_modifier_curve_common(box, modifier, False, True)
+
+ elif modifier.type == 'DISTANCE_FROM_OBJECT':
+ box.prop(modifier, "target")
+ self.draw_modifier_curve_common(box, modifier, True, True)
+ prop = box.operator("scene.freestyle_fill_range_by_selection")
+ prop.type = 'THICKNESS'
+ prop.name = modifier.name
+
+ elif modifier.type == 'DISTANCE_FROM_CAMERA':
+ self.draw_modifier_curve_common(box, modifier, True, True)
+ prop = box.operator("scene.freestyle_fill_range_by_selection")
+ prop.type = 'THICKNESS'
+ prop.name = modifier.name
+
+ elif modifier.type == 'MATERIAL':
+ box.prop(modifier, "material_attr", text="")
+ self.draw_modifier_curve_common(box, modifier, False, True)
+
+ elif modifier.type == 'CALLIGRAPHY':
+ box.prop(modifier, "orientation")
+ row = box.row(align=True)
+ row.prop(modifier, "min_thickness")
+ row.prop(modifier, "max_thickness")
+
+ def draw_geometry_modifier(self, context, modifier):
+ layout = self.layout
+
+ col = layout.column(align=True)
+ self.draw_modifier_box_header(col.box(), modifier)
+ if modifier.expanded:
+ box = col.box()
+
+ if modifier.type == 'SAMPLING':
+ box.prop(modifier, "sampling")
+
+ elif modifier.type == 'BEZIER_CURVE':
+ box.prop(modifier, "error")
+
+ elif modifier.type == 'SINUS_DISPLACEMENT':
+ split = box.split()
+ col = split.column()
+ col.prop(modifier, "wavelength")
+ col.prop(modifier, "amplitude")
+ col = split.column()
+ col.prop(modifier, "phase")
+
+ elif modifier.type == 'SPATIAL_NOISE':
+ split = box.split()
+ col = split.column()
+ col.prop(modifier, "amplitude")
+ col.prop(modifier, "scale")
+ col.prop(modifier, "octaves")
+ col = split.column()
+ col.prop(modifier, "smooth")
+ col.prop(modifier, "pure_random")
+
+ elif modifier.type == 'PERLIN_NOISE_1D':
+ split = box.split()
+ col = split.column()
+ col.prop(modifier, "frequency")
+ col.prop(modifier, "amplitude")
+ col.prop(modifier, "seed")
+ col = split.column()
+ col.prop(modifier, "octaves")
+ col.prop(modifier, "angle")
+
+ elif modifier.type == 'PERLIN_NOISE_2D':
+ split = box.split()
+ col = split.column()
+ col.prop(modifier, "frequency")
+ col.prop(modifier, "amplitude")
+ col.prop(modifier, "seed")
+ col = split.column()
+ col.prop(modifier, "octaves")
+ col.prop(modifier, "angle")
+
+ elif modifier.type == 'BACKBONE_STRETCHER':
+ box.prop(modifier, "backbone_length")
+
+ elif modifier.type == 'TIP_REMOVER':
+ box.prop(modifier, "tip_length")
+
+ elif modifier.type == 'POLYGONIZATION':
+ box.prop(modifier, "error")
+
+ elif modifier.type == 'GUIDING_LINES':
+ box.prop(modifier, "offset")
+
+ elif modifier.type == 'BLUEPRINT':
+ row = box.row()
+ row.prop(modifier, "shape", expand=True)
+ box.prop(modifier, "rounds")
+ row = box.row()
+ if modifier.shape in {'CIRCLES', 'ELLIPSES'}:
+ row.prop(modifier, "random_radius")
+ row.prop(modifier, "random_center")
+ elif modifier.shape == 'SQUARES':
+ row.prop(modifier, "backbone_length")
+ row.prop(modifier, "random_backbone")
+
+ elif modifier.type == '2D_OFFSET':
+ row = box.row(align=True)
+ row.prop(modifier, "start")
+ row.prop(modifier, "end")
+ row = box.row(align=True)
+ row.prop(modifier, "x")
+ row.prop(modifier, "y")
+
+ elif modifier.type == '2D_TRANSFORM':
+ box.prop(modifier, "pivot")
+ if modifier.pivot == 'PARAM':
+ box.prop(modifier, "pivot_u")
+ elif modifier.pivot == 'ABSOLUTE':
+ row = box.row(align=True)
+ row.prop(modifier, "pivot_x")
+ row.prop(modifier, "pivot_y")
+ row = box.row(align=True)
+ row.prop(modifier, "scale_x")
+ row.prop(modifier, "scale_y")
+ box.prop(modifier, "angle")
+
+ def draw(self, context):
+ rd = context.scene.render
+ rl = rd.layers.active
+ lineset = rl.freestyle_settings.linesets.active
+
+ layout = self.layout
+ layout.active = rl.use_freestyle
+
+ if lineset is None:
+ return
+ linestyle = lineset.linestyle
+
+ layout.template_ID(lineset, "linestyle", new="scene.freestyle_linestyle_new")
+ row = layout.row(align=True)
+ row.prop(linestyle, "panel", expand=True)
+ if linestyle.panel == 'STROKES':
+ ## Chaining
+ layout.label(text="Chaining:")
+ split = layout.split(align=True)
+ # First column
+ col = split.column()
+ col.prop(linestyle, "use_chaining", text="Enable Chaining")
+ sub = col.row()
+ sub.active = linestyle.use_chaining
+ sub.prop(linestyle, "same_object")
+ # Second column
+ col = split.column()
+ col.active = linestyle.use_chaining
+ col.prop(linestyle, "chaining", text="")
+ if linestyle.chaining == 'SKETCHY':
+ col.prop(linestyle, "rounds")
+
+ ## Splitting
+ layout.label(text="Splitting:")
+ split = layout.split(align=True)
+ # First column
+ col = split.column()
+ row = col.row(align=True)
+ row.prop(linestyle, "use_min_angle", text="")
+ sub = row.row()
+ sub.active = linestyle.use_min_angle
+ sub.prop(linestyle, "min_angle")
+ row = col.row(align=True)
+ row.prop(linestyle, "use_max_angle", text="")
+ sub = row.row()
+ sub.active = linestyle.use_max_angle
+ sub.prop(linestyle, "max_angle")
+ # Second column
+ col = split.column()
+ row = col.row(align=True)
+ row.prop(linestyle, "use_split_length", text="")
+ sub = row.row()
+ sub.active = linestyle.use_split_length
+ sub.prop(linestyle, "split_length", text="2D Length")
+ row = col.row(align=True)
+ row.prop(linestyle, "material_boundary")
+ # End of columns
+ row = layout.row(align=True)
+ row.prop(linestyle, "use_split_pattern", text="")
+ sub = row.row()
+ sub.active = linestyle.use_split_pattern
+ sub.prop(linestyle, "split_dash1", text="D1")
+ sub.prop(linestyle, "split_gap1", text="G1")
+ sub.prop(linestyle, "split_dash2", text="D2")
+ sub.prop(linestyle, "split_gap2", text="G2")
+ sub.prop(linestyle, "split_dash3", text="D3")
+ sub.prop(linestyle, "split_gap3", text="G3")
+
+ ## Selection
+ layout.label(text="Selection:")
+ split = layout.split(align=True)
+ # First column
+ col = split.column()
+ row = col.row(align=True)
+ row.prop(linestyle, "use_min_length", text="")
+ sub = row.row()
+ sub.active = linestyle.use_min_length
+ sub.prop(linestyle, "min_length")
+ # Second column
+ col = split.column()
+ row = col.row(align=True)
+ row.prop(linestyle, "use_max_length", text="")
+ sub = row.row()
+ sub.active = linestyle.use_max_length
+ sub.prop(linestyle, "max_length")
+
+ ## Caps
+ layout.label(text="Caps:")
+ row = layout.row(align=True)
+ row.prop(linestyle, "caps", expand=True)
+
+ ## Dashed lines
+ layout.label(text="Dashed Line:")
+ row = layout.row(align=True)
+ row.prop(linestyle, "use_dashed_line", text="")
+ sub = row.row()
+ sub.active = linestyle.use_dashed_line
+ sub.prop(linestyle, "dash1", text="D1")
+ sub.prop(linestyle, "gap1", text="G1")
+ sub.prop(linestyle, "dash2", text="D2")
+ sub.prop(linestyle, "gap2", text="G2")
+ sub.prop(linestyle, "dash3", text="D3")
+ sub.prop(linestyle, "gap3", text="G3")
+
+ elif linestyle.panel == 'COLOR':
+ col = layout.column()
+ row = col.row()
+ row.label(text="Base Color:")
+ row.prop(linestyle, "color", text="")
+ col.label(text="Modifiers:")
+ col.operator_menu_enum("scene.freestyle_color_modifier_add", "type", text="Add Modifier")
+ for modifier in linestyle.color_modifiers:
+ self.draw_color_modifier(context, modifier)
+
+ elif linestyle.panel == 'ALPHA':
+ col = layout.column()
+ row = col.row()
+ row.label(text="Base Transparency:")
+ row.prop(linestyle, "alpha")
+ col.label(text="Modifiers:")
+ col.operator_menu_enum("scene.freestyle_alpha_modifier_add", "type", text="Add Modifier")
+ for modifier in linestyle.alpha_modifiers:
+ self.draw_alpha_modifier(context, modifier)
+
+ elif linestyle.panel == 'THICKNESS':
+ col = layout.column()
+ row = col.row()
+ row.label(text="Base Thickness:")
+ row.prop(linestyle, "thickness")
+ row = col.row()
+ row.prop(linestyle, "thickness_position", expand=True)
+ row = col.row()
+ row.prop(linestyle, "thickness_ratio")
+ row.active = (linestyle.thickness_position == 'RELATIVE')
+ col = layout.column()
+ col.label(text="Modifiers:")
+ col.operator_menu_enum("scene.freestyle_thickness_modifier_add", "type", text="Add Modifier")
+ for modifier in linestyle.thickness_modifiers:
+ self.draw_thickness_modifier(context, modifier)
+
+ elif linestyle.panel == 'GEOMETRY':
+ col = layout.column()
+ col.label(text="Modifiers:")
+ col.operator_menu_enum("scene.freestyle_geometry_modifier_add", "type", text="Add Modifier")
+ for modifier in linestyle.geometry_modifiers:
+ self.draw_geometry_modifier(context, modifier)
+
+ elif linestyle.panel == 'MISC':
+ pass
+
+
+if __name__ == "__main__": # only for live edit.
+ bpy.utils.register_module(__name__)
diff --git a/release/scripts/startup/bl_ui/properties_render.py b/release/scripts/startup/bl_ui/properties_render.py
index dc9b5e99788..c8cc433d3af 100644
--- a/release/scripts/startup/bl_ui/properties_render.py
+++ b/release/scripts/startup/bl_ui/properties_render.py
@@ -19,7 +19,7 @@
# <pep8 compliant>
import bpy
-from bpy.types import Menu, Panel, UIList
+from bpy.types import Menu, Panel
class RENDER_MT_presets(Menu):
@@ -43,18 +43,6 @@ class RENDER_MT_framerate_presets(Menu):
draw = Menu.draw_preset
-class RENDER_UL_renderlayers(UIList):
- def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
- # assert(isinstance(item, bpy.types.SceneRenderLayer)
- layer = item
- if self.layout_type in {'DEFAULT', 'COMPACT'}:
- layout.label(text=layer.name, translate=False, icon_value=icon)
- layout.prop(layer, "use", text="", index=index)
- elif self.layout_type in {'GRID'}:
- layout.alignment = 'CENTER'
- layout.label(text="", icon_value=icon)
-
-
class RenderButtonsPanel():
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
@@ -63,8 +51,8 @@ class RenderButtonsPanel():
@classmethod
def poll(cls, context):
- rd = context.scene.render
- return context.scene and (rd.engine in cls.COMPAT_ENGINES)
+ scene = context.scene
+ return scene and (scene.render.engine in cls.COMPAT_ENGINES)
class RENDER_PT_render(RenderButtonsPanel, Panel):
@@ -84,110 +72,6 @@ class RENDER_PT_render(RenderButtonsPanel, Panel):
layout.prop(rd, "display_mode", text="Display")
-class RENDER_PT_layers(RenderButtonsPanel, Panel):
- bl_label = "Layers"
- bl_options = {'DEFAULT_CLOSED'}
- COMPAT_ENGINES = {'BLENDER_RENDER'}
-
- def draw(self, context):
- layout = self.layout
-
- scene = context.scene
- rd = scene.render
-
- row = layout.row()
- row.template_list("RENDER_UL_renderlayers", "", rd, "layers", rd.layers, "active_index", rows=2)
-
- col = row.column(align=True)
- col.operator("scene.render_layer_add", icon='ZOOMIN', text="")
- col.operator("scene.render_layer_remove", icon='ZOOMOUT', text="")
-
- row = layout.row()
- rl = rd.layers.active
- if rl:
- row.prop(rl, "name")
- row.prop(rd, "use_single_layer", text="", icon_only=True)
-
- split = layout.split()
-
- col = split.column()
- col.prop(scene, "layers", text="Scene")
- col.label(text="")
- col.prop(rl, "light_override", text="Light")
- col.prop(rl, "material_override", text="Material")
-
- col = split.column()
- col.prop(rl, "layers", text="Layer")
- col.label(text="Mask Layers:")
- col.prop(rl, "layers_zmask", text="")
-
- layout.separator()
- layout.label(text="Include:")
-
- split = layout.split()
-
- col = split.column()
- col.prop(rl, "use_zmask")
- row = col.row()
- row.prop(rl, "invert_zmask", text="Negate")
- row.active = rl.use_zmask
- col.prop(rl, "use_all_z")
-
- col = split.column()
- col.prop(rl, "use_solid")
- col.prop(rl, "use_halo")
- col.prop(rl, "use_ztransp")
-
- col = split.column()
- col.prop(rl, "use_sky")
- col.prop(rl, "use_edge_enhance")
- col.prop(rl, "use_strand")
-
- layout.separator()
-
- split = layout.split()
-
- col = split.column()
- col.label(text="Passes:")
- col.prop(rl, "use_pass_combined")
- col.prop(rl, "use_pass_z")
- col.prop(rl, "use_pass_vector")
- col.prop(rl, "use_pass_normal")
- col.prop(rl, "use_pass_uv")
- col.prop(rl, "use_pass_mist")
- col.prop(rl, "use_pass_object_index")
- col.prop(rl, "use_pass_material_index")
- col.prop(rl, "use_pass_color")
-
- col = split.column()
- col.label()
- col.prop(rl, "use_pass_diffuse")
- row = col.row()
- row.prop(rl, "use_pass_specular")
- row.prop(rl, "exclude_specular", text="")
- row = col.row()
- row.prop(rl, "use_pass_shadow")
- row.prop(rl, "exclude_shadow", text="")
- row = col.row()
- row.prop(rl, "use_pass_emit")
- row.prop(rl, "exclude_emit", text="")
- row = col.row()
- row.prop(rl, "use_pass_ambient_occlusion")
- row.prop(rl, "exclude_ambient_occlusion", text="")
- row = col.row()
- row.prop(rl, "use_pass_environment")
- row.prop(rl, "exclude_environment", text="")
- row = col.row()
- row.prop(rl, "use_pass_indirect")
- row.prop(rl, "exclude_indirect", text="")
- row = col.row()
- row.prop(rl, "use_pass_reflection")
- row.prop(rl, "exclude_reflection", text="")
- row = col.row()
- row.prop(rl, "use_pass_refraction")
- row.prop(rl, "exclude_refraction", text="")
-
-
class RENDER_PT_dimensions(RenderButtonsPanel, Panel):
bl_label = "Dimensions"
COMPAT_ENGINES = {'BLENDER_RENDER'}
diff --git a/release/scripts/startup/bl_ui/properties_render_layer.py b/release/scripts/startup/bl_ui/properties_render_layer.py
new file mode 100644
index 00000000000..af11fa505ff
--- /dev/null
+++ b/release/scripts/startup/bl_ui/properties_render_layer.py
@@ -0,0 +1,178 @@
+# ##### 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 #####
+
+# <pep8 compliant>
+import bpy
+from bpy.types import Menu, Panel, UIList
+
+
+class RenderLayerButtonsPanel():
+ bl_space_type = 'PROPERTIES'
+ bl_region_type = 'WINDOW'
+ bl_context = "render_layer"
+ # COMPAT_ENGINES must be defined in each subclass, external engines can add themselves here
+
+ @classmethod
+ def poll(cls, context):
+ scene = context.scene
+ return scene and (scene.render.engine in cls.COMPAT_ENGINES)
+
+
+class RENDERLAYER_UL_renderlayers(UIList):
+ def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
+ # assert(isinstance(item, bpy.types.SceneRenderLayer)
+ layer = item
+ if self.layout_type in {'DEFAULT', 'COMPACT'}:
+ layout.label(layer.name, icon_value=icon)
+ layout.prop(layer, "use", text="", index=index)
+ elif self.layout_type in {'GRID'}:
+ layout.alignment = 'CENTER'
+ layout.label("", icon_value=icon)
+
+# else if (RNA_struct_is_a(itemptr->type, &RNA_SceneRenderLayer)) {
+# uiItemL(sub, name, icon);
+# uiBlockSetEmboss(block, UI_EMBOSS);
+# uiDefButR(block, OPTION, 0, "", 0, 0, UI_UNIT_X, UI_UNIT_Y, itemptr, "use", 0, 0, 0, 0, 0, NULL);
+# }
+
+
+class RENDERLAYER_PT_layers(RenderLayerButtonsPanel, Panel):
+ bl_label = "Layers"
+ bl_options = {'HIDE_HEADER'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+ def draw(self, context):
+ layout = self.layout
+
+ scene = context.scene
+ rd = scene.render
+
+ row = layout.row()
+ row.template_list("RENDERLAYER_UL_renderlayers", "", rd, "layers", rd.layers, "active_index", rows=2)
+
+ col = row.column(align=True)
+ col.operator("scene.render_layer_add", icon='ZOOMIN', text="")
+ col.operator("scene.render_layer_remove", icon='ZOOMOUT', text="")
+
+ row = layout.row()
+ rl = rd.layers.active
+ if rl:
+ row.prop(rl, "name")
+ row.prop(rd, "use_single_layer", text="", icon_only=True)
+
+
+class RENDERLAYER_PT_layer_options(RenderLayerButtonsPanel, Panel):
+ bl_label = "Layer"
+ bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+ def draw(self, context):
+ layout = self.layout
+
+ scene = context.scene
+ rd = scene.render
+ rl = rd.layers.active
+
+ split = layout.split()
+
+ col = split.column()
+ col.prop(scene, "layers", text="Scene")
+ col.label(text="")
+ col.prop(rl, "light_override", text="Light")
+ col.prop(rl, "material_override", text="Material")
+
+ col = split.column()
+ col.prop(rl, "layers", text="Layer")
+ col.label(text="Mask Layers:")
+ col.prop(rl, "layers_zmask", text="")
+
+ layout.separator()
+ layout.label(text="Include:")
+
+ split = layout.split()
+
+ col = split.column()
+ col.prop(rl, "use_zmask")
+ row = col.row()
+ row.prop(rl, "invert_zmask", text="Negate")
+ row.active = rl.use_zmask
+ col.prop(rl, "use_all_z")
+
+ col = split.column()
+ col.prop(rl, "use_solid")
+ col.prop(rl, "use_halo")
+ col.prop(rl, "use_ztransp")
+
+ col = split.column()
+ col.prop(rl, "use_sky")
+ col.prop(rl, "use_edge_enhance")
+ col.prop(rl, "use_strand")
+ if bpy.app.build_options.freestyle:
+ row = col.row()
+ row.prop(rl, "use_freestyle")
+ row.active = rd.use_freestyle
+
+
+class RENDERLAYER_PT_layer_passes(RenderLayerButtonsPanel, Panel):
+ bl_label = "Render Passes"
+ bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+ def draw_pass_type_buttons(self, box, rl, pass_type):
+ # property names
+ use_pass_type = "use_pass_" + pass_type
+ exclude_pass_type = "exclude_" + pass_type
+ # draw pass type buttons
+ row = box.row()
+ row.prop(rl, use_pass_type)
+ row.prop(rl, exclude_pass_type, text="")
+
+ def draw(self, context):
+ layout = self.layout
+
+ scene = context.scene
+ rd = scene.render
+ rl = rd.layers.active
+
+ split = layout.split()
+
+ col = split.column()
+ col.prop(rl, "use_pass_combined")
+ col.prop(rl, "use_pass_z")
+ col.prop(rl, "use_pass_vector")
+ col.prop(rl, "use_pass_normal")
+ col.prop(rl, "use_pass_uv")
+ col.prop(rl, "use_pass_mist")
+ col.prop(rl, "use_pass_object_index")
+ col.prop(rl, "use_pass_material_index")
+ col.prop(rl, "use_pass_color")
+
+ col = split.column()
+ col.prop(rl, "use_pass_diffuse")
+ self.draw_pass_type_buttons(col, rl, "specular")
+ self.draw_pass_type_buttons(col, rl, "shadow")
+ self.draw_pass_type_buttons(col, rl, "emit")
+ self.draw_pass_type_buttons(col, rl, "ambient_occlusion")
+ self.draw_pass_type_buttons(col, rl, "environment")
+ self.draw_pass_type_buttons(col, rl, "indirect")
+ self.draw_pass_type_buttons(col, rl, "reflection")
+ self.draw_pass_type_buttons(col, rl, "refraction")
+
+
+if __name__ == "__main__": # only for live edit.
+ bpy.utils.register_module(__name__)
diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py
index 155be986ee1..be601d0b6a1 100644
--- a/release/scripts/startup/bl_ui/space_view3d.py
+++ b/release/scripts/startup/bl_ui/space_view3d.py
@@ -1912,6 +1912,12 @@ class VIEW3D_MT_edit_mesh_edges(Menu):
layout.separator()
+ if context.scene and bpy.app.build_options.freestyle:
+ layout.operator("mesh.mark_freestyle_edge").clear = False
+ layout.operator("mesh.mark_freestyle_edge", text="Clear Freestyle Edge").clear = True
+
+ layout.separator()
+
layout.operator("mesh.edge_rotate", text="Rotate Edge CW").use_ccw = False
layout.operator("mesh.edge_rotate", text="Rotate Edge CCW").use_ccw = True
@@ -1952,6 +1958,12 @@ class VIEW3D_MT_edit_mesh_faces(Menu):
layout.separator()
+ if context.scene and bpy.app.build_options.freestyle:
+ layout.operator("mesh.mark_freestyle_face").clear = False
+ layout.operator("mesh.mark_freestyle_face", text="Clear Freestyle Face").clear = True
+
+ layout.separator()
+
layout.operator("mesh.poke")
layout.operator("mesh.quads_convert_to_tris")
layout.operator("mesh.tris_convert_to_quads")
@@ -2541,17 +2553,25 @@ class VIEW3D_PT_view3d_meshdisplay(Panel):
split = layout.split()
+ with_freestyle = context.scene and bpy.app.build_options.freestyle
+
col = split.column()
col.label(text="Overlays:")
col.prop(mesh, "show_faces", text="Faces")
col.prop(mesh, "show_edges", text="Edges")
col.prop(mesh, "show_edge_crease", text="Creases")
+ if with_freestyle:
+ col.prop(mesh, "show_edge_seams", text="Seams")
col = split.column()
col.label()
- col.prop(mesh, "show_edge_seams", text="Seams")
+ if not with_freestyle:
+ col.prop(mesh, "show_edge_seams", text="Seams")
col.prop(mesh, "show_edge_sharp", text="Sharp", text_ctxt=i18n_contexts.plural)
col.prop(mesh, "show_edge_bevel_weight", text="Weights")
+ if with_freestyle:
+ col.prop(mesh, "show_freestyle_edge_marks", text="Edge Marks")
+ col.prop(mesh, "show_freestyle_face_marks", text="Face Marks")
col = layout.column()