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/scripts/freestyle')
-rw-r--r--release/scripts/freestyle/modules/freestyle/chainingiterators.py8
-rw-r--r--release/scripts/freestyle/modules/freestyle/functions.py6
-rw-r--r--release/scripts/freestyle/modules/freestyle/predicates.py75
-rw-r--r--release/scripts/freestyle/modules/freestyle/shaders.py52
-rw-r--r--release/scripts/freestyle/modules/freestyle/utils.py282
-rw-r--r--release/scripts/freestyle/modules/parameter_editor.py489
-rw-r--r--release/scripts/freestyle/styles/anisotropic_diffusion.py4
-rw-r--r--release/scripts/freestyle/styles/apriori_density.py2
-rw-r--r--release/scripts/freestyle/styles/backbone_stretcher.py2
-rw-r--r--release/scripts/freestyle/styles/blueprint_circles.py2
-rw-r--r--release/scripts/freestyle/styles/blueprint_ellipses.py2
-rw-r--r--release/scripts/freestyle/styles/blueprint_squares.py2
-rw-r--r--release/scripts/freestyle/styles/cartoon.py2
-rw-r--r--release/scripts/freestyle/styles/curvature2d.py2
-rw-r--r--release/scripts/freestyle/styles/external_contour_sketchy.py2
-rw-r--r--release/scripts/freestyle/styles/japanese_bigbrush.py4
-rw-r--r--release/scripts/freestyle/styles/multiple_parameterization.py2
-rw-r--r--release/scripts/freestyle/styles/near_lines.py2
-rw-r--r--release/scripts/freestyle/styles/qi0_not_external_contour.py2
-rw-r--r--release/scripts/freestyle/styles/sequentialsplit_sketchy.py2
-rw-r--r--release/scripts/freestyle/styles/sketchy_multiple_parameterization.py2
-rw-r--r--release/scripts/freestyle/styles/sketchy_topology_broken.py10
-rw-r--r--release/scripts/freestyle/styles/sketchy_topology_preserved.py2
-rw-r--r--release/scripts/freestyle/styles/split_at_highest_2d_curvatures.py2
-rw-r--r--release/scripts/freestyle/styles/split_at_tvertices.py2
-rw-r--r--release/scripts/freestyle/styles/stroke_texture.py49
-rw-r--r--release/scripts/freestyle/styles/uniformpruning_zsort.py3
27 files changed, 771 insertions, 243 deletions
diff --git a/release/scripts/freestyle/modules/freestyle/chainingiterators.py b/release/scripts/freestyle/modules/freestyle/chainingiterators.py
index 52f22b3e8be..fc9de6475b2 100644
--- a/release/scripts/freestyle/modules/freestyle/chainingiterators.py
+++ b/release/scripts/freestyle/modules/freestyle/chainingiterators.py
@@ -275,7 +275,7 @@ class pySketchyChainSilhouetteIterator(ChainingIterator):
class pySketchyChainingIterator(ChainingIterator):
"""
- Chaining iterator designed for sketchy style. It chaines the same
+ Chaining iterator designed for sketchy style. It chains the same
ViewEdge several times in order to produce multiple strokes per
ViewEdge.
"""
@@ -320,7 +320,7 @@ class pyFillOcclusionsRelativeChainingIterator(ChainingIterator):
Builds a pyFillOcclusionsRelativeChainingIterator object.
- :arg percent: The maximul length of the occluded part, expressed
+ :arg percent: The maximal length of the occluded part, expressed
in a percentage of the total chain length.
:type percent: float
"""
@@ -454,7 +454,7 @@ class pyFillOcclusionsAbsoluteAndRelativeChainingIterator(ChainingIterator):
Builds a pyFillOcclusionsAbsoluteAndRelativeChainingIterator object.
- :arg percent: The maximul length of the occluded part as a
+ :arg percent: The maximal length of the occluded part as a
percentage of the total chain length.
:type percent: float
:arg l: Absolute length.
@@ -524,7 +524,7 @@ class pyFillQi0AbsoluteAndRelativeChainingIterator(ChainingIterator):
Builds a pyFillQi0AbsoluteAndRelativeChainingIterator object.
- :arg percent: The maximul length of the occluded part as a
+ :arg percent: The maximal length of the occluded part as a
percentage of the total chain length.
:type percent: float
:arg l: Absolute length.
diff --git a/release/scripts/freestyle/modules/freestyle/functions.py b/release/scripts/freestyle/modules/freestyle/functions.py
index 48d9b2e2b39..426d344e8ab 100644
--- a/release/scripts/freestyle/modules/freestyle/functions.py
+++ b/release/scripts/freestyle/modules/freestyle/functions.py
@@ -189,11 +189,13 @@ class CurveMaterialF0D(UnaryFunction0DMaterial):
priority is used to pick one of the two materials at material
boundaries.
- Note: expects instances of CurvePoint to be iterated over
+ Notes: expects instances of CurvePoint to be iterated over
+ can return None if no fedge can be found
"""
def __call__(self, inter):
fe = inter.object.fedge
- assert(fe is not None), "CurveMaterialF0D: fe is None"
+ if fe is None:
+ return None
if fe.is_smooth:
return fe.material
else:
diff --git a/release/scripts/freestyle/modules/freestyle/predicates.py b/release/scripts/freestyle/modules/freestyle/predicates.py
index 68ec9e05f6e..5cbe577b956 100644
--- a/release/scripts/freestyle/modules/freestyle/predicates.py
+++ b/release/scripts/freestyle/modules/freestyle/predicates.py
@@ -43,6 +43,7 @@ __all__ = (
"FalseUP0D",
"FalseUP1D",
"Length2DBP1D",
+ "MaterialBP1D",
"NotBP1D",
"NotUP1D",
"ObjectNamesUP1D",
@@ -82,6 +83,8 @@ __all__ = (
"pyNatureUP1D",
"pyParameterUP0D",
"pyParameterUP0DGoodOne",
+ "pyProjectedXBP1D",
+ "pyProjectedYBP1D",
"pyShapeIdListUP1D",
"pyShapeIdUP1D",
"pyShuffleBP1D",
@@ -135,6 +138,8 @@ from freestyle.functions import (
GetCurvilinearAbscissaF0D,
GetDirectionalViewMapDensityF1D,
GetOccludersF1D,
+ GetProjectedXF1D,
+ GetProjectedYF1D,
GetProjectedZF1D,
GetShapeF1D,
GetSteerableViewMapDensityF1D,
@@ -146,12 +151,13 @@ from freestyle.functions import (
pyViewMapGradientNormF1D,
)
+from freestyle.utils import material_from_fedge
+
import random
# -- Unary predicates for 0D elements (vertices) -- #
-
class pyHigherCurvature2DAngleUP0D(UnaryPredicate0D):
def __init__(self, a):
UnaryPredicate0D.__init__(self)
@@ -230,9 +236,10 @@ class AndUP1D(UnaryPredicate1D):
def __init__(self, *predicates):
UnaryPredicate1D.__init__(self)
self.predicates = predicates
- # there are cases in which only one predicate is supplied (in the parameter editor)
- if len(self.predicates) < 1:
- raise ValueError("Expected one or more UnaryPredicate1D, got ", len(predicates))
+ correct_types = all(isinstance(p, UnaryPredicate1D) for p in self.predicates)
+ if not (correct_types and predicates):
+ raise TypeError("%s: Expected one or more UnaryPredicate1D, got %r" %
+ (self.__class__.__name__, self.predicates))
def __call__(self, inter):
return all(pred(inter) for pred in self.predicates)
@@ -242,9 +249,10 @@ class OrUP1D(UnaryPredicate1D):
def __init__(self, *predicates):
UnaryPredicate1D.__init__(self)
self.predicates = predicates
- # there are cases in which only one predicate is supplied (in the parameter editor)
- if len(self.predicates) < 1:
- raise ValueError("Expected one or more UnaryPredicate1D, got ", len(predicates))
+ correct_types = all(isinstance(p, UnaryPredicate1D) for p in self.predicates)
+ if not (correct_types and predicates):
+ raise TypeError("%s: Expected one or more UnaryPredicate1D, got %r" %
+ (self.__class__.__name__, self.predicates))
def __call__(self, inter):
return any(pred(inter) for pred in self.predicates)
@@ -253,10 +261,10 @@ class OrUP1D(UnaryPredicate1D):
class NotUP1D(UnaryPredicate1D):
def __init__(self, pred):
UnaryPredicate1D.__init__(self)
- self.__pred = pred
+ self.predicate = pred
def __call__(self, inter):
- return not self.__pred(inter)
+ return not self.predicate(inter)
class ObjectNamesUP1D(UnaryPredicate1D):
@@ -559,32 +567,36 @@ class pyClosedCurveUP1D(UnaryPredicate1D):
class AndBP1D(BinaryPredicate1D):
def __init__(self, *predicates):
BinaryPredicate1D.__init__(self)
- self._predicates = predicates
- if len(self.predicates) < 2:
- raise ValueError("Expected two or more BinaryPredicate1D, got ", len(predictates))
+ self.predicates = tuple(predicates)
+ correct_types = all(isinstance(p, BinaryPredicate1D) for p in self.predicates)
+ if not (correct_types and predicates):
+ raise TypeError("%s: Expected one or more BinaryPredicate1D, got %r" %
+ (self.__class__.__name__, self.predicates))
def __call__(self, i1, i2):
- return all(pred(i1, i2) for pred in self._predicates)
+ return all(pred(i1, i2) for pred in self.predicates)
class OrBP1D(BinaryPredicate1D):
def __init__(self, *predicates):
BinaryPredicate1D.__init__(self)
- self._predicates = predicates
- if len(self.predicates) < 2:
- raise ValueError("Expected two or more BinaryPredicate1D, got ", len(predictates))
+ self.predicates = tuple(predicates)
+ correct_types = all(isinstance(p, BinaryPredicate1D) for p in self.predicates)
+ if not (correct_types and predicates):
+ raise TypeError("%s: Expected one or more BinaryPredicate1D, got %r" %
+ (self.__class__.__name__, self.predicates))
def __call__(self, i1, i2):
- return any(pred(i1, i2) for pred in self._predicates)
+ return any(pred(i1, i2) for pred in self.predicates)
class NotBP1D(BinaryPredicate1D):
def __init__(self, predicate):
BinaryPredicate1D.__init__(self)
- self._predicate = predicate
+ self.predicate = predicate
def __call__(self, i1, i2):
- return (not self._precicate(i1, i2))
+ return (not self.predicate(i1, i2))
class pyZBP1D(BinaryPredicate1D):
@@ -596,6 +608,24 @@ class pyZBP1D(BinaryPredicate1D):
return (self.func(i1) > self.func(i2))
+class pyProjectedXBP1D(BinaryPredicate1D):
+ def __init__(self, iType=IntegrationType.MEAN):
+ BinaryPredicate1D.__init__(self)
+ self.func = GetProjectedXF1D(iType)
+
+ def __call__(self, i1, i2):
+ return (self.func(i1) > self.func(i2))
+
+
+class pyProjectedYBP1D(BinaryPredicate1D):
+ def __init__(self, iType=IntegrationType.MEAN):
+ BinaryPredicate1D.__init__(self)
+ self.func = GetProjectedYF1D(iType)
+
+ def __call__(self, i1, i2):
+ return (self.func(i1) > self.func(i2))
+
+
class pyZDiscontinuityBP1D(BinaryPredicate1D):
def __init__(self, iType=IntegrationType.MEAN):
BinaryPredicate1D.__init__(self)
@@ -641,3 +671,10 @@ class pyShuffleBP1D(BinaryPredicate1D):
def __call__(self, inter1, inter2):
return (random.uniform(0, 1) < random.uniform(0, 1))
+
+class MaterialBP1D(BinaryPredicate1D):
+ """Checks whether the two supplied ViewEdges have the same material."""
+ def __call__(self, i1, i2):
+ fedges = (fe for ve in (i1, i2) for fe in (ve.first_fedge, ve.last_fedge))
+ materials = {material_from_fedge(fe) for fe in fedges}
+ return len(materials) < 2
diff --git a/release/scripts/freestyle/modules/freestyle/shaders.py b/release/scripts/freestyle/modules/freestyle/shaders.py
index 508df7feca0..633def38b5b 100644
--- a/release/scripts/freestyle/modules/freestyle/shaders.py
+++ b/release/scripts/freestyle/modules/freestyle/shaders.py
@@ -36,7 +36,6 @@ __all__ = (
"BlenderTextureShader",
"CalligraphicShader",
"ColorNoiseShader",
- "ColorVariationPatternShader",
"ConstantColorShader",
"ConstantThicknessShader",
"ConstrainedIncreasingThicknessShader",
@@ -49,13 +48,9 @@ __all__ = (
"SmoothingShader",
"SpatialNoiseShader",
"SquareCapShader",
- "StrokeTextureShader",
"StrokeTextureStepShader",
- "TextureAssignerShader",
"ThicknessNoiseShader",
- "ThicknessVariationPatternShader",
"TipRemoverShader",
- "fstreamShader",
"py2DCurvatureColorShader",
"pyBackboneStretcherNoCuspShader",
"pyBackboneStretcherShader",
@@ -92,7 +87,6 @@ __all__ = (
"pyTimeColorShader",
"pyTipRemoverShader",
"pyZDependingThicknessShader",
- "streamShader",
)
@@ -103,7 +97,6 @@ from _freestyle import (
BlenderTextureShader,
CalligraphicShader,
ColorNoiseShader,
- ColorVariationPatternShader,
ConstantColorShader,
ConstantThicknessShader,
ConstrainedIncreasingThicknessShader,
@@ -114,14 +107,9 @@ from _freestyle import (
SamplingShader,
SmoothingShader,
SpatialNoiseShader,
- StrokeTextureShader,
StrokeTextureStepShader,
- TextureAssignerShader,
ThicknessNoiseShader,
- ThicknessVariationPatternShader,
TipRemoverShader,
- fstreamShader,
- streamShader,
)
# constructs for shader definition in Python
@@ -150,7 +138,8 @@ from freestyle.predicates import (
from freestyle.utils import (
bound,
- bounding_box,
+ BoundingBox,
+ pairwise,
phase_to_direction,
)
@@ -201,7 +190,7 @@ class pyConstantThicknessShader(StrokeShader):
class pyFXSVaryingThicknessWithDensityShader(StrokeShader):
"""
- Assings thickness to a stroke based on the density of the diffuse map.
+ Assigns thickness to a stroke based on the density of the diffuse map.
"""
def __init__(self, wsize, threshold_min, threshold_max, thicknessMin, thicknessMax):
StrokeShader.__init__(self)
@@ -612,7 +601,7 @@ class pyTimeColorShader(StrokeShader):
class pySamplingShader(StrokeShader):
"""
- Resamples the stroke, which gives the stroke the ammount of
+ Resamples the stroke, which gives the stroke the amount of
vertices specified.
"""
def __init__(self, sampling):
@@ -788,7 +777,7 @@ class pyTVertexRemoverShader(StrokeShader):
class pyHLRShader(StrokeShader):
"""
- Controlls visibility based upon the quantative invisibility (QI)
+ Controls visibility based upon the quantitative invisibility (QI)
based on hidden line removal (HLR).
"""
def shade(self, stroke):
@@ -877,7 +866,7 @@ class pyBluePrintCirclesShader(StrokeShader):
def shade(self, stroke):
# get minimum and maximum coordinates
- p_min, p_max = bounding_box(stroke)
+ p_min, p_max = BoundingBox.from_sequence(svert.point for svert in stroke).corners
stroke.resample(32 * self.__turns)
sv_nb = len(stroke) // self.__turns
@@ -886,7 +875,7 @@ class pyBluePrintCirclesShader(StrokeShader):
R = self.__random_radius
C = self.__random_center
- # The directions (and phases) are calculated using a seperate
+ # The directions (and phases) are calculated using a separate
# function decorated with an lru-cache. This guarantees that
# the directions (involving sin and cos) are calculated as few
# times as possible.
@@ -929,7 +918,7 @@ class pyBluePrintEllipsesShader(StrokeShader):
self.__random_radius = random_radius
def shade(self, stroke):
- p_min, p_max = bounding_box(stroke)
+ p_min, p_max = BoundingBox.from_sequence(svert.point for svert in stroke).corners
stroke.resample(32 * self.__turns)
sv_nb = len(stroke) // self.__turns
@@ -954,7 +943,7 @@ class pyBluePrintEllipsesShader(StrokeShader):
c = prev_center + (center - prev_center) * phase
svert.point = (c.x + r.x * direction.x, c.y + r.y * direction.y)
- # remove exessive vertices
+ # remove excess vertices
if not it.is_end:
it.increment()
for sv in tuple(it):
@@ -976,7 +965,7 @@ class pyBluePrintSquaresShader(StrokeShader):
return
# get minimum and maximum coordinates
- p_min, p_max = bounding_box(stroke)
+ p_min, p_max = BoundingBox.from_sequence(svert.point for svert in stroke).corners
stroke.resample(32 * self.__turns)
num_segments = len(stroke) // self.__turns
@@ -1016,7 +1005,7 @@ class pyBluePrintSquaresShader(StrokeShader):
points = tuple(p + rand for (p, rand) in zip(points, randomization_mat))
- # substract even from uneven; result is length four tuple of vectors
+ # subtract even from uneven; result is length four tuple of vectors
it = iter(points)
old_vecs = tuple(next(it) - current for current in it)
@@ -1040,7 +1029,7 @@ class pyBluePrintSquaresShader(StrokeShader):
# special case; remove these vertices
verticesToRemove.append(svert)
- # remove exessive vertices (if any)
+ # remove excess vertices (if any)
if not it.is_end:
it.increment()
verticesToRemove += [svert for svert in it]
@@ -1078,7 +1067,7 @@ class pyBluePrintDirectedSquaresShader(StrokeShader):
sqrt_coeff = sqrt(trace * trace - 4 * det)
lambda1, lambda2 = (trace + sqrt_coeff) / 2, (trace - sqrt_coeff) / 2
- # make sure those numers aren't to small, if they are, rooting them will yield complex numbers
+ # make sure those numbers aren't to small, if they are, rooting them will yield complex numbers
lambda1, lambda2 = max(1e-12, lambda1), max(1e-12, lambda2)
theta = atan(2 * p_var_xy / (p_var.x - p_var.y)) / 2
@@ -1131,7 +1120,7 @@ class pyBluePrintDirectedSquaresShader(StrokeShader):
# special case; remove these vertices
verticesToRemove.append(svert)
- # remove exessive vertices
+ # remove excess vertices
if not it.is_end:
it.increment()
verticesToRemove += [svert for svert in it]
@@ -1143,6 +1132,13 @@ class pyBluePrintDirectedSquaresShader(StrokeShader):
# -- various (used in the parameter editor) -- #
+def iter_stroke_vertices(stroke, epsilon=1e-6):
+ yield stroke[0]
+ for prev, svert in pairwise(stroke):
+ if (prev.point - svert.point).length > epsilon:
+ yield svert
+
+
class RoundCapShader(StrokeShader):
def round_cap_thickness(self, x):
x = max(0.0, min(x, 1.0))
@@ -1150,7 +1146,8 @@ class RoundCapShader(StrokeShader):
def shade(self, stroke):
# save the location and attribute of stroke vertices
- buffer = tuple((Vector(sv.point), StrokeAttribute(sv.attribute)) for sv in stroke)
+ buffer = tuple((Vector(sv.point), StrokeAttribute(sv.attribute))
+ for sv in iter_stroke_vertices(stroke))
nverts = len(buffer)
if nverts < 2:
return
@@ -1198,7 +1195,8 @@ class RoundCapShader(StrokeShader):
class SquareCapShader(StrokeShader):
def shade(self, stroke):
# save the location and attribute of stroke vertices
- buffer = tuple((Vector(sv.point), StrokeAttribute(sv.attribute)) for sv in stroke)
+ buffer = tuple((Vector(sv.point), StrokeAttribute(sv.attribute))
+ for sv in iter_stroke_vertices(stroke))
nverts = len(buffer)
if nverts < 2:
return
diff --git a/release/scripts/freestyle/modules/freestyle/utils.py b/release/scripts/freestyle/modules/freestyle/utils.py
index e0679a53954..d3eba187f70 100644
--- a/release/scripts/freestyle/modules/freestyle/utils.py
+++ b/release/scripts/freestyle/modules/freestyle/utils.py
@@ -22,28 +22,37 @@ writing.
"""
__all__ = (
- "ContextFunctions",
+ "angle_x_normal",
"bound",
"bounding_box",
+ "BoundingBox",
+ "ContextFunctions",
+ "curvature_from_stroke_vertex",
"find_matching_vertex",
- "getCurrentScene",
"get_chain_length",
+ "get_object_name",
+ "get_strokes",
"get_test_stroke",
+ "getCurrentScene",
"integrate",
+ "is_poly_clockwise",
"iter_distance_along_stroke",
"iter_distance_from_camera",
"iter_distance_from_object",
"iter_material_value",
"iter_t2d_along_stroke",
+ "material_from_fedge",
+ "normal_at_I0D",
"pairwise",
"phase_to_direction",
"rgb_to_bw",
+ "simplify",
"stroke_curvature",
"stroke_normal",
+ "StrokeCollector",
"tripplewise",
)
-
# module members
from _freestyle import (
ContextFunctions,
@@ -55,13 +64,29 @@ from _freestyle import (
from freestyle.types import (
Interface0DIterator,
Stroke,
+ StrokeShader,
StrokeVertexIterator,
)
from mathutils import Vector
from functools import lru_cache, namedtuple
-from math import cos, sin, pi
-from itertools import tee
+from math import cos, sin, pi, atan2
+from itertools import tee, compress
+
+
+# -- types -- #
+
+# A named tuple primitive used for storing data that has an upper and
+# lower bound (e.g., thickness, range and certain other values)
+class BoundedProperty(namedtuple("BoundedProperty", ["min", "max", "delta"])):
+ def __new__(cls, minimum, maximum, delta=None):
+ if delta is None:
+ delta = abs(maximum - minimum)
+ return super().__new__(cls, minimum, maximum, delta)
+
+ def interpolate(self, val):
+ result = (self.max - val) / self.delta
+ return 1.0 - bound(0, result, 1)
# -- real utility functions -- #
@@ -79,6 +104,40 @@ def bound(lower, x, higher):
return (lower if x <= lower else higher if x >= higher else x)
+def get_strokes():
+ """Get all strokes that are currently available"""
+ return tuple(map(Operators().get_stroke_from_index, range(Operators().get_strokes_size())))
+
+
+def is_poly_clockwise(stroke):
+ """True if the stroke is orientated in a clockwise way, False otherwise"""
+ v = sum((v2.point.x - v1.point.x) * (v1.point.y + v2.point.y) for v1, v2 in pairwise(stroke))
+ v1, v2 = stroke[0], stroke[-1]
+ if (v1.point - v2.point).length > 1e-3:
+ v += (v2.point.x - v1.point.x) * (v1.point.y + v2.point.y)
+ return v > 0
+
+
+def get_object_name(stroke):
+ """Returns the name of the object that this stroke is drawn on."""
+ fedge = stroke[0].fedge
+ if fedge is None:
+ return None
+ return fedge.viewedge.viewshape.name
+
+
+def material_from_fedge(fe):
+ "get the diffuse rgba color from an FEdge"
+ if fe is None:
+ return None
+ if fe.is_smooth:
+ material = fe.material
+ else:
+ right, left = fe.material_right, fe.material_left
+ material = right if (right.priority > left.priority) else left
+ return material
+
+
def bounding_box(stroke):
"""
Returns the maximum and minimum coordinates (the bounding box) of the stroke's vertices
@@ -86,8 +145,57 @@ def bounding_box(stroke):
x, y = zip(*(svert.point for svert in stroke))
return (Vector((min(x), min(y))), Vector((max(x), max(y))))
-# -- General helper functions -- #
+def normal_at_I0D(it: Interface0DIterator) -> Vector:
+ """Normal at an Interface0D object. In contrast to Normal2DF0D this
+ function uses the actual data instead of underlying Fedge objects.
+ """
+ if it.at_last and it.is_begin:
+ # corner-case
+ return Vector((0, 0))
+ elif it.at_last:
+ it.decrement()
+ a, b = it.object, next(it)
+ elif it.is_begin:
+ a, b = it.object, next(it)
+ # give iterator back in original state
+ it.decrement()
+ elif it.is_end:
+ # just fail hard: this shouldn not happen
+ raise StopIteration()
+ else:
+ # this case sometimes has a small difference with Normal2DF0D (1e-3 -ish)
+ it.decrement()
+ a = it.object
+ curr, b = next(it), next(it)
+ # give iterator back in original state
+ it.decrement()
+ return (b.point - a.point).orthogonal().normalized()
+
+
+def angle_x_normal(it: Interface0DIterator):
+ """unsigned angle between a Point's normal and the X axis, in radians"""
+ normal = normal_at_I0D(it)
+ return abs(atan2(normal[1], normal[0]))
+
+
+def curvature_from_stroke_vertex(svert):
+ """The 3D curvature of an stroke vertex' underlying geometry
+ The result is None or in the range [-inf, inf]"""
+ c1 = svert.first_svertex.curvatures
+ c2 = svert.second_svertex.curvatures
+ if c1 is None and c2 is None:
+ Kr = None
+ elif c1 is None:
+ Kr = c2[4]
+ elif c2 is None:
+ Kr = c1[4]
+ else:
+ Kr = c1[4] + svert.t2d * (c2[4] - c1[4])
+ return Kr
+
+
+# -- General helper functions -- #
@lru_cache(maxsize=32)
def phase_to_direction(length):
@@ -102,9 +210,122 @@ def phase_to_direction(length):
results.append((phase, Vector((cos(2 * pi * phase), sin(2 * pi * phase)))))
return results
-# A named tuple primitive used for storing data that has an upper and
-# lower bound (e.g., thickness, range and certain values)
-BoundedProperty = namedtuple("BoundedProperty", ["min", "max", "delta"])
+
+# -- simplification of a set of points; based on simplify.js by Vladimir Agafonkin --
+# https://mourner.github.io/simplify-js/
+
+def getSquareSegmentDistance(p, p1, p2):
+ """
+ Square distance between point and a segment
+ """
+ x, y = p1
+
+ dx, dy = (p2 - p1)
+
+ if dx or dy:
+ t = ((p.x - x) * dx + (p.y - y) * dy) / (dx * dx + dy * dy)
+
+ if t > 1:
+ x, y = p2
+ elif t > 0:
+ x += dx * t
+ y += dy * t
+
+ dx, dy = p.x - x, p.y - y
+ return dx * dx + dy * dy
+
+
+def simplifyDouglasPeucker(points, tolerance):
+ length = len(points)
+ markers = [0] * length
+
+ first = 0
+ last = length - 1
+
+ first_stack = []
+ last_stack = []
+
+ new_points = []
+
+ markers[first] = 1
+ markers[last] = 1
+
+ while last:
+ max_sqdist = 0
+
+ for i in range(first, last):
+ sqdist = getSquareSegmentDistance(points[i], points[first], points[last])
+
+ if sqdist > max_sqdist:
+ index = i
+ max_sqdist = sqdist
+
+ if max_sqdist > tolerance:
+ markers[index] = 1
+
+ first_stack.append(first)
+ last_stack.append(index)
+
+ first_stack.append(index)
+ last_stack.append(last)
+
+ first = first_stack.pop() if first_stack else None
+ last = last_stack.pop() if last_stack else None
+
+ return tuple(compress(points, markers))
+
+
+def simplify(points, tolerance):
+ """Simplifies a set of points"""
+ return simplifyDouglasPeucker(points, tolerance * tolerance)
+
+
+class BoundingBox:
+ """Object representing a bounding box consisting out of 2 2D vectors"""
+
+ __slots__ = (
+ "minimum",
+ "maximum",
+ "size",
+ "corners",
+ )
+
+ def __init__(self, minimum: Vector, maximum: Vector):
+ self.minimum = minimum
+ self.maximum = maximum
+ if len(minimum) != len(maximum):
+ raise TypeError("Expected two vectors of size 2, got", minimum, maximum)
+ self.size = len(minimum)
+ self.corners = (minimum, maximum)
+
+ def __repr__(self):
+ return "BoundingBox(%r, %r)" % (self.minimum, self.maximum)
+
+ @classmethod
+ def from_sequence(cls, sequence):
+ """BoundingBox from sequence of 2D or 3D Vector objects"""
+ x, y = zip(*sequence)
+ mini = Vector((min(x), min(y)))
+ maxi = Vector((max(x), max(y)))
+ return cls(mini, maxi)
+
+ def inside(self, other):
+ """True if self inside other, False otherwise"""
+ if self.size != other.size:
+ raise TypeError("Expected two BoundingBox of the same size, got", self, other)
+ return (self.minimum.x >= other.minimum.x and self.minimum.y >= other.minimum.y and
+ self.maximum.x <= other.maximum.x and self.maximum.y <= other.maximum.y)
+
+
+class StrokeCollector(StrokeShader):
+ "Collects and Stores stroke objects"
+ def __init__(self):
+ StrokeShader.__init__(self)
+ self.strokes = []
+
+ def shade(self, stroke):
+ self.strokes.append(stroke)
+
# -- helper functions for chaining -- #
@@ -147,6 +368,7 @@ def find_matching_vertex(id, it):
"""Finds the matching vertex, or returns None."""
return next((ve for ve in it if ve.id == id), None)
+
# -- helper functions for iterating -- #
def pairwise(iterable, types={Stroke, StrokeVertexIterator}):
@@ -210,34 +432,36 @@ def iter_distance_from_object(stroke, location, range_min, range_max, normfac):
def iter_material_value(stroke, func, attribute):
- "Yields a specific material attribute from the vertex' underlying material."
+ """Yields a specific material attribute from the vertex' underlying material."""
it = Interface0DIterator(stroke)
for svert in it:
material = func(it)
# main
if attribute == 'LINE':
value = rgb_to_bw(*material.line[0:3])
- elif attribute == 'ALPHA':
- value = material.line[3]
elif attribute == 'DIFF':
value = rgb_to_bw(*material.diffuse[0:3])
elif attribute == 'SPEC':
value = rgb_to_bw(*material.specular[0:3])
- # line seperate
+ # line separate
elif attribute == 'LINE_R':
value = material.line[0]
elif attribute == 'LINE_G':
value = material.line[1]
elif attribute == 'LINE_B':
value = material.line[2]
- # diffuse seperate
+ elif attribute == 'LINE_A':
+ value = material.line[3]
+ # diffuse separate
elif attribute == 'DIFF_R':
value = material.diffuse[0]
elif attribute == 'DIFF_G':
value = material.diffuse[1]
elif attribute == 'DIFF_B':
value = material.diffuse[2]
- # specular seperate
+ elif attribute == 'ALPHA':
+ value = material.diffuse[3]
+ # specular separate
elif attribute == 'SPEC_R':
value = material.specular[0]
elif attribute == 'SPEC_G':
@@ -250,8 +474,9 @@ def iter_material_value(stroke, func, attribute):
raise ValueError("unexpected material attribute: " + attribute)
yield (svert, value)
+
def iter_distance_along_stroke(stroke):
- "Yields the absolute distance along the stroke up to the current vertex."
+ """Yields the absolute distance along the stroke up to the current vertex."""
distance = 0.0
# the positions need to be copied, because they are changed in the calling function
points = tuple(svert.point.copy() for svert in stroke)
@@ -260,8 +485,8 @@ def iter_distance_along_stroke(stroke):
distance += (prev - curr).length
yield distance
-# -- mathmatical operations -- #
+# -- mathematical operations -- #
def stroke_curvature(it):
"""
@@ -293,6 +518,7 @@ def stroke_curvature(it):
yield abs(K)
+
def stroke_normal(stroke):
"""
Compute the 2D normal at the stroke vertex pointed by the iterator
@@ -302,31 +528,19 @@ def stroke_normal(stroke):
The returned normals are dynamic: they update when the
vertex position (and therefore the vertex normal) changes.
- for use in geometry modifiers it is advised to
+ for use in geometry modifiers it is advised to
cast this generator function to a tuple or list
"""
- n = len(stroke) - 1
-
- for i, svert in enumerate(stroke):
- if i == 0:
- e = stroke[i + 1].point - svert.point
- yield Vector((e[1], -e[0])).normalized()
- elif i == n:
- e = svert.point - stroke[i - 1].point
- yield Vector((e[1], -e[0])).normalized()
- else:
- e1 = stroke[i + 1].point - svert.point
- e2 = svert.point - stroke[i - 1].point
- n1 = Vector((e1[1], -e1[0])).normalized()
- n2 = Vector((e2[1], -e2[0])).normalized()
- yield (n1 + n2).normalized()
+ it = iter(stroke)
+ yield from (normal_at_I0D(it) for _ in it)
+
def get_test_stroke():
"""Returns a static stroke object for testing """
from freestyle.types import Stroke, Interface0DIterator, StrokeVertexIterator, SVertex, Id, StrokeVertex
# points for our fake stroke
points = (Vector((1.0, 5.0, 3.0)), Vector((1.0, 2.0, 9.0)),
- Vector((6.0, 2.0, 3.0)), Vector((7.0, 2.0, 3.0)),
+ Vector((6.0, 2.0, 3.0)), Vector((7.0, 2.0, 3.0)),
Vector((2.0, 6.0, 3.0)), Vector((2.0, 8.0, 3.0)))
ids = (Id(0, 0), Id(1, 1), Id(2, 2), Id(3, 3), Id(4, 4), Id(5, 5))
diff --git a/release/scripts/freestyle/modules/parameter_editor.py b/release/scripts/freestyle/modules/parameter_editor.py
index ebd09bd0181..fe6c6f641ac 100644
--- a/release/scripts/freestyle/modules/parameter_editor.py
+++ b/release/scripts/freestyle/modules/parameter_editor.py
@@ -62,7 +62,10 @@ from freestyle.predicates import (
TrueBP1D,
TrueUP1D,
WithinImageBoundaryUP1D,
+ pyNFirstUP1D,
pyNatureUP1D,
+ pyProjectedXBP1D,
+ pyProjectedYBP1D,
pyZBP1D,
)
from freestyle.shaders import (
@@ -72,29 +75,34 @@ from freestyle.shaders import (
ConstantColorShader,
GuidingLinesShader,
PolygonalizationShader,
- SamplingShader,
- SpatialNoiseShader,
- StrokeShader,
- StrokeTextureStepShader,
- TipRemoverShader,
pyBluePrintCirclesShader,
pyBluePrintEllipsesShader,
pyBluePrintSquaresShader,
RoundCapShader,
+ SamplingShader,
+ SpatialNoiseShader,
SquareCapShader,
+ StrokeShader,
+ StrokeTextureStepShader,
+ ThicknessNoiseShader as thickness_noise,
+ TipRemoverShader,
)
from freestyle.utils import (
+ angle_x_normal,
+ bound,
+ BoundedProperty,
ContextFunctions,
+ curvature_from_stroke_vertex,
getCurrentScene,
iter_distance_along_stroke,
- iter_t2d_along_stroke,
iter_distance_from_camera,
iter_distance_from_object,
iter_material_value,
- stroke_normal,
- bound,
+ iter_t2d_along_stroke,
+ normal_at_I0D,
pairwise,
- BoundedProperty
+ simplify,
+ stroke_normal,
)
from _freestyle import (
blendRamp,
@@ -103,10 +111,20 @@ from _freestyle import (
)
import time
+import bpy
+import random
+
from mathutils import Vector
-from math import pi, sin, cos, acos, radians
+from math import pi, sin, cos, acos, radians, atan2
from itertools import cycle, tee
+# WARNING: highly experimental, not a stable API
+# lists of callback functions
+# used by the render_freestyle_svg addon
+callbacks_lineset_pre = []
+callbacks_modifiers_post = []
+callbacks_lineset_post = []
+
class ColorRampModifier(StrokeShader):
"""Primitive for the color modifiers."""
@@ -167,7 +185,13 @@ class CurveMappingModifier(ScalarBlendModifier):
return (1.0 - t) if self.invert else t
def CURVE(self, t):
- return evaluateCurveMappingF(self.curve, 0, t)
+ # deprecated: return evaluateCurveMappingF(self.curve, 0, t)
+ curve = self.curve
+ curve.initialize()
+ result = curve.curves[0].evaluate(t)
+ # float precision errors in t can give a very weird result for evaluate.
+ # therefore, bound the result by the curve's min and max values
+ return bound(curve.clip_min_y, result, curve.clip_max_y)
class ThicknessModifierMixIn:
@@ -200,10 +224,19 @@ class ThicknessBlenderMixIn(ThicknessModifierMixIn):
self.position = position
self.ratio = ratio
- def blend_thickness(self, svert, v):
- """Blends and sets the thickness."""
+ def blend_thickness(self, svert, thickness, asymmetric=False):
+ """Blends and sets the thickness with respect to the position, blend mode and symmetry."""
+ if asymmetric:
+ right, left = thickness
+ self.blend_thickness_asymmetric(svert, right, left)
+ else:
+ if type(thickness) not in {int, float}:
+ thickness = sum(thickness)
+ self.blend_thickness_symmetric(svert, thickness)
+
+ def blend_thickness_symmetric(self, svert, v):
+ """Blends and sets the thickness. Thickness is equal on each side of the backbone"""
outer, inner = svert.attribute.thickness
- fe = svert.fedge
v = self.blend(outer + inner, v)
# Part 1: blend
@@ -218,21 +251,29 @@ class ThicknessBlenderMixIn(ThicknessModifierMixIn):
else:
raise ValueError("unknown thickness position: " + position)
- # Part 2: set
- if (fe.nature & Nature.BORDER):
+ self.set_thickness(svert, outer, inner)
+
+ def blend_thickness_asymmetric(self, svert, right, left):
+ """Blends and sets the thickness. Thickness may be unequal on each side of the backbone"""
+ # blend the thickness values for both sides. This way, the blend mode is supported.
+ old = svert.attribute.thickness
+ new = (right, left)
+ right, left = (self.blend(*val) for val in zip(old, new))
+
+ fe = svert.fedge
+ nature = fe.nature
+ if (nature & Nature.BORDER):
if self.persp_camera:
point = -svert.point_3d.normalized()
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 (fe.nature & Nature.SILHOUETTE):
+ right, left = left, right
+ elif (nature & Nature.SILHOUETTE):
if fe.is_smooth: # TODO more tests needed
- outer, inner = inner, outer
- else:
- outer = inner = (outer + inner) / 2
- svert.attribute.thickness = (outer, inner)
+ right, left = left, right
+ svert.attribute.thickness = (right, left)
class BaseThicknessShader(StrokeShader, ThicknessModifierMixIn):
@@ -271,7 +312,7 @@ class ColorAlongStrokeShader(ColorRampModifier):
class AlphaAlongStrokeShader(CurveMappingModifier):
- """Maps a curve to the alpha/transparancy of the stroke, using the curvilinear abscissa (t)."""
+ """Maps a curve to the alpha/transparency of the stroke, using the curvilinear abscissa (t)."""
def shade(self, stroke):
for svert, t in zip(stroke, iter_t2d_along_stroke(stroke)):
a = svert.attribute.alpha
@@ -285,7 +326,7 @@ class ThicknessAlongStrokeShader(ThicknessBlenderMixIn, CurveMappingModifier):
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 = BoundedProperty(value_min, value_max, value_max - value_min)
+ self.value = BoundedProperty(value_min, value_max)
def shade(self, stroke):
for svert, t in zip(stroke, iter_t2d_along_stroke(stroke)):
@@ -299,7 +340,7 @@ class ColorDistanceFromCameraShader(ColorRampModifier):
"""Picks a color value from a ramp based on the vertex' distance from the camera."""
def __init__(self, blend, influence, ramp, range_min, range_max):
ColorRampModifier.__init__(self, blend, influence, ramp)
- self.range = BoundedProperty(range_min, range_max, range_max - range_min)
+ self.range = BoundedProperty(range_min, range_max)
def shade(self, stroke):
it = iter_distance_from_camera(stroke, *self.range)
@@ -313,7 +354,7 @@ class AlphaDistanceFromCameraShader(CurveMappingModifier):
"""Picks an alpha value from a curve based on the vertex' distance from the camera"""
def __init__(self, blend, influence, mapping, invert, curve, range_min, range_max):
CurveMappingModifier.__init__(self, blend, influence, mapping, invert, curve)
- self.range = BoundedProperty(range_min, range_max, range_max - range_min)
+ self.range = BoundedProperty(range_min, range_max)
def shade(self, stroke):
it = iter_distance_from_camera(stroke, *self.range)
@@ -329,8 +370,8 @@ class ThicknessDistanceFromCameraShader(ThicknessBlenderMixIn, CurveMappingModif
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 = BoundedProperty(range_min, range_max, range_max - range_min)
- self.value = BoundedProperty(value_min, value_max, value_max - value_min)
+ self.range = BoundedProperty(range_min, range_max)
+ self.value = BoundedProperty(value_min, value_max)
def shade(self, stroke):
for (svert, t) in iter_distance_from_camera(stroke, *self.range):
@@ -346,7 +387,7 @@ class ColorDistanceFromObjectShader(ColorRampModifier):
ColorRampModifier.__init__(self, blend, influence, ramp)
if target is None:
raise ValueError("ColorDistanceFromObjectShader: target can't be None ")
- self.range = BoundedProperty(range_min, range_max, range_max - range_min)
+ self.range = BoundedProperty(range_min, range_max)
# construct a model-view matrix
matrix = getCurrentScene().camera.matrix_world.inverted()
# get the object location in the camera coordinate
@@ -366,7 +407,7 @@ class AlphaDistanceFromObjectShader(CurveMappingModifier):
CurveMappingModifier.__init__(self, blend, influence, mapping, invert, curve)
if target is None:
raise ValueError("AlphaDistanceFromObjectShader: target can't be None ")
- self.range = BoundedProperty(range_min, range_max, range_max - range_min)
+ self.range = BoundedProperty(range_min, range_max)
# construct a model-view matrix
matrix = getCurrentScene().camera.matrix_world.inverted()
# get the object location in the camera coordinate
@@ -388,8 +429,8 @@ class ThicknessDistanceFromObjectShader(ThicknessBlenderMixIn, CurveMappingModif
CurveMappingModifier.__init__(self, blend, influence, mapping, invert, curve)
if target is None:
raise ValueError("ThicknessDistanceFromObjectShader: target can't be None ")
- self.range = BoundedProperty(range_min, range_max, range_max - range_min)
- self.value = BoundedProperty(value_min, value_max, value_max - value_min)
+ self.range = BoundedProperty(range_min, range_max)
+ self.value = BoundedProperty(value_min, value_max)
# construct a model-view matrix
matrix = getCurrentScene().camera.matrix_world.inverted()
# get the object location in the camera coordinate
@@ -401,6 +442,7 @@ class ThicknessDistanceFromObjectShader(ThicknessBlenderMixIn, CurveMappingModif
b = self.value.min + self.evaluate(t) * self.value.delta
self.blend_thickness(svert, b)
+
# Material modifiers
class ColorMaterialShader(ColorRampModifier):
"""Assigns a color to the vertices based on their underlying material."""
@@ -416,7 +458,7 @@ class ColorMaterialShader(ColorRampModifier):
for svert in it:
material = self.func(it)
if self.attribute == 'LINE':
- b = material.line[0:3]
+ b = material.line[0:3]
elif self.attribute == 'DIFF':
b = material.diffuse[0:3]
else:
@@ -429,6 +471,7 @@ class ColorMaterialShader(ColorRampModifier):
b = self.evaluate(value)
svert.attribute.color = self.blend_ramp(a, b)
+
class AlphaMaterialShader(CurveMappingModifier):
"""Assigns an alpha value to the vertices based on their underlying material."""
def __init__(self, blend, influence, mapping, invert, curve, material_attribute):
@@ -450,7 +493,7 @@ class ThicknessMaterialShader(ThicknessBlenderMixIn, CurveMappingModifier):
ThicknessBlenderMixIn.__init__(self, thickness_position, thickness_ratio)
CurveMappingModifier.__init__(self, blend, influence, mapping, invert, curve)
self.attribute = material_attribute
- self.value = BoundedProperty(value_min, value_max, value_max - value_min)
+ self.value = BoundedProperty(value_min, value_max)
self.func = CurveMaterialF0D()
def shade(self, stroke):
@@ -461,7 +504,6 @@ class ThicknessMaterialShader(ThicknessBlenderMixIn, CurveMappingModifier):
# Calligraphic thickness modifier
-
class CalligraphicThicknessShader(ThicknessBlenderMixIn, ScalarBlendModifier):
"""Thickness modifier for achieving a calligraphy-like effect."""
def __init__(self, thickness_position, thickness_ratio,
@@ -469,7 +511,7 @@ class CalligraphicThicknessShader(ThicknessBlenderMixIn, ScalarBlendModifier):
ThicknessBlenderMixIn.__init__(self, thickness_position, thickness_ratio)
ScalarBlendModifier.__init__(self, blend_type, influence)
self.orientation = Vector((cos(orientation), sin(orientation)))
- self.thickness = BoundedProperty(thickness_min, thickness_max, thickness_max - thickness_min)
+ self.thickness = BoundedProperty(thickness_min, thickness_max)
self.func = VertexOrientation2DF0D()
def shade(self, stroke):
@@ -485,10 +527,253 @@ class CalligraphicThicknessShader(ThicknessBlenderMixIn, ScalarBlendModifier):
self.blend_thickness(svert, b)
+# - Tangent Modifiers - #
+
+class TangentColorShader(ColorRampModifier):
+ """Color based on the direction of the stroke"""
+ def shade(self, stroke):
+ it = Interface0DIterator(stroke)
+ for svert in it:
+ angle = angle_x_normal(it)
+ fac = self.evaluate(angle / pi)
+ a = svert.attribute.color
+ svert.attribute.color = self.blend_ramp(a, fac)
+
+
+class TangentAlphaShader(CurveMappingModifier):
+ """Alpha transparency based on the direction of the stroke"""
+ def shade(self, stroke):
+ it = Interface0DIterator(stroke)
+ for svert in it:
+ angle = angle_x_normal(it)
+ fac = self.evaluate(angle / pi)
+ a = svert.attribute.alpha
+ svert.attribute.alpha = self.blend(a, fac)
+
+
+class TangentThicknessShader(ThicknessBlenderMixIn, CurveMappingModifier):
+ """Thickness based on the direction of the stroke"""
+ def __init__(self, thickness_position, thickness_ratio, blend, influence, mapping, invert, curve,
+ thickness_min, thickness_max):
+ ThicknessBlenderMixIn.__init__(self, thickness_position, thickness_ratio)
+ CurveMappingModifier.__init__(self, blend, influence, mapping, invert, curve)
+ self.thickness = BoundedProperty(thickness_min, thickness_max)
+
+ def shade(self, stroke):
+ it = Interface0DIterator(stroke)
+ for svert in it:
+ angle = angle_x_normal(it)
+ thickness = self.thickness.min + self.evaluate(angle / pi) * self.thickness.delta
+ self.blend_thickness(svert, thickness)
+
+
+# - Noise Modifiers - #
+
+class NoiseShader:
+ """Base class for noise shaders"""
+ def __init__(self, amplitude, period, seed=512):
+ self.amplitude = amplitude
+ self.scale = 1 / period / seed
+ self.seed = seed
+
+ def noisegen(self, stroke, n1=Noise(), n2=Noise()):
+ """Produces two noise values per StrokeVertex for every vertex in the stroke"""
+ initU1 = stroke.length_2d * self.seed + n1.rand(512) * self.seed
+ initU2 = stroke.length_2d * self.seed + n2.rand() * self.seed
+
+ for svert in stroke:
+ a = n1.turbulence_smooth(self.scale * svert.curvilinear_abscissa + initU1, 2)
+ b = n2.turbulence_smooth(self.scale * svert.curvilinear_abscissa + initU2, 2)
+ yield (svert, a, b)
+
+
+class ThicknessNoiseShader(ThicknessBlenderMixIn, ScalarBlendModifier, NoiseShader):
+ """Thickness based on pseudo-noise"""
+ def __init__(self, thickness_position, thickness_ratio, blend_type, influence, amplitude, period, seed=512, asymmetric=True):
+ ScalarBlendModifier.__init__(self, blend_type, influence)
+ ThicknessBlenderMixIn.__init__(self, thickness_position, thickness_ratio)
+ NoiseShader.__init__(self, amplitude, period, seed)
+ self.asymmetric = asymmetric
+
+ def shade(self, stroke):
+ for svert, noiseval1, noiseval2 in self.noisegen(stroke):
+ (r, l) = svert.attribute.thickness
+ l += noiseval1 * self.amplitude
+ r += noiseval2 * self.amplitude
+ self.blend_thickness(svert, (r, l), self.asymmetric)
+
+
+class ColorNoiseShader(ColorRampModifier, NoiseShader):
+ """Color based on pseudo-noise"""
+ def __init__(self, blend, influence, ramp, amplitude, period, seed=512):
+ ColorRampModifier.__init__(self, blend, influence, ramp)
+ NoiseShader.__init__(self, amplitude, period, seed)
+
+ def shade(self, stroke):
+ for svert, noiseval1, noiseval2 in self.noisegen(stroke):
+ position = abs(noiseval1 + noiseval2)
+ svert.attribute.color = self.blend_ramp(svert.attribute.color, self.evaluate(position))
+
+
+class AlphaNoiseShader(CurveMappingModifier, NoiseShader):
+ """Alpha transparency on based pseudo-noise"""
+ def __init__(self, blend, influence, mapping, invert, curve, amplitude, period, seed=512):
+ CurveMappingModifier.__init__(self, blend, influence, mapping, invert, curve)
+ NoiseShader.__init__(self, amplitude, period, seed)
+
+ def shade(self, stroke, n1=Noise(), n2=Noise()):
+ for svert, noiseval1, noiseval2 in self.noisegen(stroke):
+ position = abs(noiseval1 + noiseval2)
+ svert.attribute.alpha = self.blend(svert.attribute.alpha, self.evaluate(position))
+
+
+# - Crease Angle Modifiers - #
+
+def crease_angle(svert):
+ """Returns the crease angle between the StrokeVertex' two adjacent faces (in radians)"""
+ fe = svert.fedge
+ if not fe or fe.is_smooth or not (fe.nature & Nature.CREASE):
+ return None
+ # make sure that the input is within the domain of the acos function
+ product = bound(-1.0, -fe.normal_left.dot(fe.normal_right), 1.0)
+ return acos(product)
+
+
+class CreaseAngleColorShader(ColorRampModifier):
+ """Color based on the crease angle between two adjacent faces on the underlying geometry"""
+ def __init__(self, blend, influence, ramp, angle_min, angle_max):
+ ColorRampModifier.__init__(self, blend, influence, ramp)
+ # angles are (already) in radians
+ self.angle = BoundedProperty(angle_min, angle_max)
+
+ def shade(self, stroke):
+ for svert in stroke:
+ angle = crease_angle(svert)
+ if angle is None:
+ continue
+ t = self.angle.interpolate(angle)
+ svert.attribute.color = self.blend_ramp(svert.attribute.color, self.evaluate(t))
+
+
+class CreaseAngleAlphaShader(CurveMappingModifier):
+ """Alpha transparency based on the crease angle between two adjacent faces on the underlying geometry"""
+ def __init__(self, blend, influence, mapping, invert, curve, angle_min, angle_max):
+ CurveMappingModifier.__init__(self, blend, influence, mapping, invert, curve)
+ # angles are (already) in radians
+ self.angle = BoundedProperty(angle_min, angle_max)
+
+ def shade(self, stroke):
+ for svert in stroke:
+ angle = crease_angle(svert)
+ if angle is None:
+ continue
+ t = self.angle.interpolate(angle)
+ svert.attribute.alpha = self.blend(svert.attribute.alpha, self.evaluate(t))
+
+
+class CreaseAngleThicknessShader(ThicknessBlenderMixIn, CurveMappingModifier):
+ """Thickness based on the crease angle between two adjacent faces on the underlying geometry"""
+ def __init__(self, thickness_position, thickness_ratio, blend, influence, mapping, invert, curve,
+ angle_min, angle_max, thickness_min, thickness_max):
+ ThicknessBlenderMixIn.__init__(self, thickness_position, thickness_ratio)
+ CurveMappingModifier.__init__(self, blend, influence, mapping, invert, curve)
+ # angles are (already) in radians
+ self.angle = BoundedProperty(angle_min, angle_max)
+ self.thickness = BoundedProperty(thickness_min, thickness_max)
+
+ def shade(self, stroke):
+ for svert in stroke:
+ angle = crease_angle(svert)
+ if angle is None:
+ continue
+ t = self.angle.interpolate(angle)
+ thickness = self.thickness.min + self.evaluate(t) * self.thickness.delta
+ self.blend_thickness(svert, thickness)
+
+
+# - Curvature3D Modifiers - #
+
+def normalized_absolute_curvature(svert, bounded_curvature):
+ """
+ Gives the absolute curvature in range [0, 1].
+
+ The actual curvature (Kr) value can be anywhere in the range [-inf, inf], where convex curvature
+ yields a positive value, and concave a negative one. These shaders only look for the magnitude
+ of the 3D curvature, hence the abs()
+ """
+ curvature = curvature_from_stroke_vertex(svert)
+ if curvature is None:
+ return 0.0
+ return bounded_curvature.interpolate(abs(curvature))
+
+
+class Curvature3DColorShader(ColorRampModifier):
+ """Color based on the 3D curvature of the underlying geometry"""
+ def __init__(self, blend, influence, ramp, curvature_min, curvature_max):
+ ColorRampModifier.__init__(self, blend, influence, ramp)
+ self.curvature = BoundedProperty(curvature_min, curvature_max)
+
+ def shade(self, stroke):
+ for svert in stroke:
+ t = normalized_absolute_curvature(svert, self.curvature)
+ a = svert.attribute.color
+ b = self.evaluate(t)
+ svert.attribute.color = self.blend_ramp(a, b)
+
+
+class Curvature3DAlphaShader(CurveMappingModifier):
+ """Alpha based on the 3D curvature of the underlying geometry"""
+ def __init__(self, blend, influence, mapping, invert, curve, curvature_min, curvature_max):
+ CurveMappingModifier.__init__(self, blend, influence, mapping, invert, curve)
+ self.curvature = BoundedProperty(curvature_min, curvature_max)
+
+ def shade(self, stroke):
+ for svert in stroke:
+ t = normalized_absolute_curvature(svert, self.curvature)
+ a = svert.attribute.alpha
+ b = self.evaluate(t)
+ svert.attribute.alpha = self.blend(a, b)
+
+
+class Curvature3DThicknessShader(ThicknessBlenderMixIn, CurveMappingModifier):
+ """Alpha based on the 3D curvature of the underlying geometry"""
+ def __init__(self, thickness_position, thickness_ratio, blend, influence, mapping, invert, curve,
+ curvature_min, curvature_max, thickness_min, thickness_max):
+ ThicknessBlenderMixIn.__init__(self, thickness_position, thickness_ratio)
+ CurveMappingModifier.__init__(self, blend, influence, mapping, invert, curve)
+ self.curvature = BoundedProperty(curvature_min, curvature_max)
+ self.thickness = BoundedProperty(thickness_min, thickness_max)
+
+
+ def shade(self, stroke):
+ for svert in stroke:
+ t = normalized_absolute_curvature(svert, self.curvature)
+ thickness = self.thickness.min + self.evaluate(t) * self.thickness.delta
+ self.blend_thickness(svert, thickness)
+
+
# Geometry modifiers
+class SimplificationShader(StrokeShader):
+ """Simplifies a stroke by merging points together"""
+ def __init__(self, tolerance):
+ StrokeShader.__init__(self)
+ self.tolerance = tolerance
+
+ def shade(self, stroke):
+ points = tuple(svert.point for svert in stroke)
+ points_simplified = simplify(points, tolerance=self.tolerance)
+
+ it = iter(stroke)
+ for svert, point in zip(it, points_simplified):
+ svert.point = point
+
+ for svert in tuple(it):
+ stroke.remove_vertex(svert)
+
+
class SinusDisplacementShader(StrokeShader):
- """Displaces the stroke in a sinewave-like shape."""
+ """Displaces the stroke in a sine wave-like shape."""
def __init__(self, wavelength, amplitude, phase):
StrokeShader.__init__(self)
self.wavelength = wavelength
@@ -531,7 +816,7 @@ class PerlinNoise1DShader(StrokeShader):
class PerlinNoise2DShader(StrokeShader):
"""
Displaces the stroke using the strokes coordinates. This means
- that in a scene no strokes will be distorded identically.
+ that in a scene no strokes will be distorted identically.
More information on the noise shaders can be found at:
freestyleintegration.wordpress.com/2011/09/25/development-updates-on-september-25/
@@ -758,8 +1043,8 @@ class AngleLargerThanBP1D(BinaryPredicate1D):
x = (dir1 * dir2) / denom
return acos(bound(-1.0, x, 1.0)) > self.angle
-# predicates for selection
+# predicates for selection
class LengthThresholdUP1D(UnaryPredicate1D):
def __init__(self, length_min=None, length_max=None):
@@ -874,6 +1159,20 @@ class Seed:
_seed = Seed()
+def get_dashed_pattern(linestyle):
+ """Extracts the dashed pattern from the various UI options """
+ 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)
+ return pattern
+
integration_types = {
'MEAN': IntegrationType.MEAN,
@@ -884,13 +1183,16 @@ integration_types = {
# main function for parameter processing
-
def process(layer_name, lineset_name):
scene = getCurrentScene()
layer = scene.render.layers[layer_name]
lineset = layer.freestyle_settings.linesets[lineset_name]
linestyle = lineset.linestyle
+ # execute line set pre-processing callback functions
+ for fn in callbacks_lineset_pre:
+ fn(scene, layer, lineset)
+
selection_criteria = []
# prepare selection criteria by visibility
if lineset.select_by_visibility:
@@ -933,11 +1235,11 @@ def process(layer_name, lineset_name):
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 = OrUP1D(*edge_type_criteria)
- else:
- upred = AndUP1D(*edge_type_criteria)
- if upred is not None:
+ if edge_type_criteria:
+ if lineset.edge_type_combination == 'OR':
+ upred = OrUP1D(*edge_type_criteria)
+ else:
+ upred = AndUP1D(*edge_type_criteria)
if lineset.edge_type_negation == 'EXCLUSIVE':
upred = NotUP1D(upred)
selection_criteria.append(upred)
@@ -962,8 +1264,9 @@ def process(layer_name, lineset_name):
upred = WithinImageBoundaryUP1D(*ContextFunctions.get_border())
selection_criteria.append(upred)
# select feature edges
- upred = AndUP1D(*selection_criteria)
- if upred is None:
+ if selection_criteria:
+ upred = AndUP1D(*selection_criteria)
+ else:
upred = TrueUP1D()
Operators.select(upred)
# join feature edges to form chains
@@ -1006,11 +1309,6 @@ def process(layer_name, lineset_name):
Operators.sequential_split(SplitPatternStartingUP0D(controller),
SplitPatternStoppingUP0D(controller),
sampling)
- # select chains
- if linestyle.use_length_min or linestyle.use_length_max:
- length_min = linestyle.length_min if linestyle.use_length_min else None
- length_max = linestyle.length_max if linestyle.use_length_max else None
- Operators.select(LengthThresholdUP1D(length_min, length_max))
# sort selected chains
if linestyle.use_sorting:
integration = integration_types.get(linestyle.integration_type, IntegrationType.MEAN)
@@ -1018,9 +1316,20 @@ def process(layer_name, lineset_name):
bpred = pyZBP1D(integration)
elif linestyle.sort_key == '2D_LENGTH':
bpred = Length2DBP1D()
+ elif linestyle.sort_key == 'PROJECTED_X':
+ bpred = pyProjectedXBP1D(integration)
+ elif linestyle.sort_key == 'PROJECTED_Y':
+ bpred = pyProjectedYBP1D(integration)
if linestyle.sort_order == 'REVERSE':
bpred = NotBP1D(bpred)
Operators.sort(bpred)
+ # select chains
+ if linestyle.use_length_min or linestyle.use_length_max:
+ length_min = linestyle.length_min if linestyle.use_length_min else None
+ length_max = linestyle.length_max if linestyle.use_length_max else None
+ Operators.select(LengthThresholdUP1D(length_min, length_max))
+ if linestyle.use_chain_count:
+ Operators.select(pyNFirstUP1D(linestyle.chain_count))
# prepare a list of stroke shaders
shaders_list = []
for m in linestyle.geometry_modifiers:
@@ -1032,6 +1341,8 @@ def process(layer_name, lineset_name):
elif m.type == 'BEZIER_CURVE':
shaders_list.append(BezierCurveShader(
m.error))
+ elif m.type == 'SIMPLIFICATION':
+ shaders_list.append(SimplificationShader(m.tolerance))
elif m.type == 'SINUS_DISPLACEMENT':
shaders_list.append(SinusDisplacementShader(
m.wavelength, m.amplitude, m.phase))
@@ -1103,6 +1414,21 @@ def process(layer_name, lineset_name):
shaders_list.append(ColorMaterialShader(
m.blend, m.influence, m.color_ramp, m.material_attribute,
m.use_ramp))
+ elif m.type == 'TANGENT':
+ shaders_list.append(TangentColorShader(
+ m.blend, m.influence, m.color_ramp))
+ elif m.type == 'CREASE_ANGLE':
+ shaders_list.append(CreaseAngleColorShader(
+ m.blend, m.influence, m.color_ramp,
+ m.angle_min, m.angle_max))
+ elif m.type == 'CURVATURE_3D':
+ shaders_list.append(Curvature3DColorShader(
+ m.blend, m.influence, m.color_ramp,
+ m.curvature_min, m.curvature_max))
+ elif m.type == 'NOISE':
+ shaders_list.append(ColorNoiseShader(
+ m.blend, m.influence, m.color_ramp,
+ m.amplitude, m.period, m.seed))
for m in linestyle.alpha_modifiers:
if not m.use:
continue
@@ -1121,6 +1447,21 @@ def process(layer_name, lineset_name):
shaders_list.append(AlphaMaterialShader(
m.blend, m.influence, m.mapping, m.invert, m.curve,
m.material_attribute))
+ elif m.type == 'TANGENT':
+ shaders_list.append(TangentAlphaShader(
+ m.blend, m.influence, m.mapping, m.invert, m.curve,))
+ elif m.type == 'CREASE_ANGLE':
+ shaders_list.append(CreaseAngleAlphaShader(
+ m.blend, m.influence, m.mapping, m.invert, m.curve,
+ m.angle_min, m.angle_max))
+ elif m.type == 'CURVATURE_3D':
+ shaders_list.append(Curvature3DAlphaShader(
+ m.blend, m.influence, m.mapping, m.invert, m.curve,
+ m.curvature_min, m.curvature_max))
+ elif m.type == 'NOISE':
+ shaders_list.append(AlphaNoiseShader(
+ m.blend, m.influence, m.mapping, m.invert, m.curve,
+ m.amplitude, m.period, m.seed))
for m in linestyle.thickness_modifiers:
if not m.use:
continue
@@ -1149,6 +1490,28 @@ def process(layer_name, lineset_name):
thickness_position, linestyle.thickness_ratio,
m.blend, m.influence,
m.orientation, m.thickness_min, m.thickness_max))
+ elif m.type == 'TANGENT':
+ shaders_list.append(TangentThicknessShader(
+ thickness_position, linestyle.thickness_ratio,
+ m.blend, m.influence, m.mapping, m.invert, m.curve,
+ m.thickness_min, m.thickness_max))
+ elif m.type == 'NOISE':
+ shaders_list.append(ThicknessNoiseShader(
+ thickness_position, linestyle.thickness_ratio,
+ m.blend, m.influence,
+ m.amplitude, m.period, m.seed, m.use_asymmetric))
+ elif m.type == 'CREASE_ANGLE':
+ shaders_list.append(CreaseAngleThicknessShader(
+ thickness_position, linestyle.thickness_ratio,
+ m.blend, m.influence, m.mapping, m.invert, m.curve,
+ m.angle_min, m.angle_max, m.thickness_min, m.thickness_max))
+ elif m.type == 'CURVATURE_3D':
+ shaders_list.append(Curvature3DThicknessShader(
+ thickness_position, linestyle.thickness_ratio,
+ m.blend, m.influence, m.mapping, m.invert, m.curve,
+ m.curvature_min, m.curvature_max, m.thickness_min, m.thickness_max))
+ else:
+ raise RuntimeError("No Thickness modifier with type", type(m), m)
# -- Textures -- #
has_tex = False
if scene.render.use_shading_nodes:
@@ -1163,24 +1526,26 @@ def process(layer_name, lineset_name):
has_tex = True
if has_tex:
shaders_list.append(StrokeTextureStepShader(linestyle.texture_spacing))
+
+ # execute post-base stylization callbacks
+ for fn in callbacks_modifiers_post:
+ shaders_list.extend(fn(scene, layer, lineset))
+
# -- Stroke caps -- #
if linestyle.caps == 'ROUND':
shaders_list.append(RoundCapShader())
elif linestyle.caps == 'SQUARE':
shaders_list.append(SquareCapShader())
+
# -- Dashed line -- #
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)
+ pattern = get_dashed_pattern(linestyle)
if len(pattern) > 0:
shaders_list.append(DashedLineShader(pattern))
+
# create strokes using the shaders list
Operators.create(TrueUP1D(), shaders_list)
+
+ # execute line set post-processing callback functions
+ for fn in callbacks_lineset_post:
+ fn(scene, layer, lineset)
diff --git a/release/scripts/freestyle/styles/anisotropic_diffusion.py b/release/scripts/freestyle/styles/anisotropic_diffusion.py
index b17e5c2a38c..fc136b23d82 100644
--- a/release/scripts/freestyle/styles/anisotropic_diffusion.py
+++ b/release/scripts/freestyle/styles/anisotropic_diffusion.py
@@ -19,7 +19,7 @@
# Filename : anisotropic_diffusion.py
# Author : Fredo Durand
# Date : 12/08/2004
-# Purpose : Smoothes lines using an anisotropic diffusion scheme
+# Purpose : Smooth lines using an anisotropic diffusion scheme
from freestyle.chainingiterators import ChainPredicateIterator
from freestyle.predicates import (
@@ -34,7 +34,6 @@ from freestyle.shaders import (
ConstantThicknessShader,
IncreasingColorShader,
SamplingShader,
- StrokeTextureShader,
pyDiffusion2Shader,
)
from freestyle.types import Operators, Stroke
@@ -50,7 +49,6 @@ 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),
diff --git a/release/scripts/freestyle/styles/apriori_density.py b/release/scripts/freestyle/styles/apriori_density.py
index b52bcfbeb4e..1de2c4c0334 100644
--- a/release/scripts/freestyle/styles/apriori_density.py
+++ b/release/scripts/freestyle/styles/apriori_density.py
@@ -19,7 +19,7 @@
# Filename : apriori_density.py
# Author : Stephane Grabli
# Date : 04/08/2005
-# Purpose : Draws lines having a high a priori density
+# Purpose : Draws lines having a high a prior density
from freestyle.chainingiterators import ChainPredicateIterator
from freestyle.predicates import (
diff --git a/release/scripts/freestyle/styles/backbone_stretcher.py b/release/scripts/freestyle/styles/backbone_stretcher.py
index 3eb921ddb34..cc495f33716 100644
--- a/release/scripts/freestyle/styles/backbone_stretcher.py
+++ b/release/scripts/freestyle/styles/backbone_stretcher.py
@@ -30,7 +30,6 @@ from freestyle.predicates import (
from freestyle.shaders import (
BackboneStretcherShader,
ConstantColorShader,
- TextureAssignerShader,
)
from freestyle.types import Operators
@@ -38,7 +37,6 @@ from freestyle.types import Operators
Operators.select(QuantitativeInvisibilityUP1D(0))
Operators.bidirectional_chain(ChainSilhouetteIterator(), NotUP1D(QuantitativeInvisibilityUP1D(0)))
shaders_list = [
- TextureAssignerShader(4),
ConstantColorShader(0.5, 0.5, 0.5),
BackboneStretcherShader(20),
]
diff --git a/release/scripts/freestyle/styles/blueprint_circles.py b/release/scripts/freestyle/styles/blueprint_circles.py
index aade164698a..f39cda5cff9 100644
--- a/release/scripts/freestyle/styles/blueprint_circles.py
+++ b/release/scripts/freestyle/styles/blueprint_circles.py
@@ -34,7 +34,6 @@ from freestyle.predicates import (
from freestyle.shaders import (
ConstantThicknessShader,
IncreasingColorShader,
- TextureAssignerShader,
pyBluePrintCirclesShader,
pyPerlinNoise1DShader,
)
@@ -50,7 +49,6 @@ 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/styles/blueprint_ellipses.py b/release/scripts/freestyle/styles/blueprint_ellipses.py
index d0bd0c45c6c..3d977a10d1c 100644
--- a/release/scripts/freestyle/styles/blueprint_ellipses.py
+++ b/release/scripts/freestyle/styles/blueprint_ellipses.py
@@ -34,7 +34,6 @@ from freestyle.predicates import (
from freestyle.shaders import (
ConstantThicknessShader,
IncreasingColorShader,
- TextureAssignerShader,
pyBluePrintEllipsesShader,
pyPerlinNoise1DShader,
)
@@ -50,7 +49,6 @@ 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/styles/blueprint_squares.py b/release/scripts/freestyle/styles/blueprint_squares.py
index 82ab6a80dd7..7110fe413fc 100644
--- a/release/scripts/freestyle/styles/blueprint_squares.py
+++ b/release/scripts/freestyle/styles/blueprint_squares.py
@@ -34,7 +34,6 @@ from freestyle.predicates import (
from freestyle.shaders import (
ConstantThicknessShader,
IncreasingColorShader,
- TextureAssignerShader,
pyBluePrintSquaresShader,
pyPerlinNoise1DShader,
)
@@ -50,7 +49,6 @@ 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),
]
diff --git a/release/scripts/freestyle/styles/cartoon.py b/release/scripts/freestyle/styles/cartoon.py
index 2e2962cefe5..e630127db1a 100644
--- a/release/scripts/freestyle/styles/cartoon.py
+++ b/release/scripts/freestyle/styles/cartoon.py
@@ -20,7 +20,7 @@
# 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
+# inferred from each object's material in a cartoon-like
# fashion.
from freestyle.chainingiterators import ChainSilhouetteIterator
diff --git a/release/scripts/freestyle/styles/curvature2d.py b/release/scripts/freestyle/styles/curvature2d.py
index 66c8a6c6544..faf1223b735 100644
--- a/release/scripts/freestyle/styles/curvature2d.py
+++ b/release/scripts/freestyle/styles/curvature2d.py
@@ -30,7 +30,6 @@ from freestyle.predicates import (
)
from freestyle.shaders import (
ConstantThicknessShader,
- StrokeTextureShader,
py2DCurvatureColorShader,
)
from freestyle.types import Operators, Stroke
@@ -39,7 +38,6 @@ from freestyle.types import Operators, Stroke
Operators.select(QuantitativeInvisibilityUP1D(0))
Operators.bidirectional_chain(ChainSilhouetteIterator(), NotUP1D(QuantitativeInvisibilityUP1D(0)))
shaders_list = [
- StrokeTextureShader("smoothAlpha.bmp", Stroke.OPAQUE_MEDIUM, False),
ConstantThicknessShader(5),
py2DCurvatureColorShader()
]
diff --git a/release/scripts/freestyle/styles/external_contour_sketchy.py b/release/scripts/freestyle/styles/external_contour_sketchy.py
index 6a7a2333bb5..44dbda4709f 100644
--- a/release/scripts/freestyle/styles/external_contour_sketchy.py
+++ b/release/scripts/freestyle/styles/external_contour_sketchy.py
@@ -37,7 +37,6 @@ from freestyle.shaders import (
SamplingShader,
SmoothingShader,
SpatialNoiseShader,
- TextureAssignerShader,
)
from freestyle.types import Operators
@@ -51,6 +50,5 @@ shaders_list = [
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/styles/japanese_bigbrush.py b/release/scripts/freestyle/styles/japanese_bigbrush.py
index 80afeff48d0..a312bcd4358 100644
--- a/release/scripts/freestyle/styles/japanese_bigbrush.py
+++ b/release/scripts/freestyle/styles/japanese_bigbrush.py
@@ -37,7 +37,6 @@ from freestyle.shaders import (
ConstantColorShader,
ConstantThicknessShader,
SamplingShader,
- TextureAssignerShader,
TipRemoverShader,
pyNonLinearVaryingThicknessShader,
pySamplingShader,
@@ -47,7 +46,7 @@ from freestyle.types import IntegrationType, Operators
Operators.select(QuantitativeInvisibilityUP1D(0))
Operators.bidirectional_chain(ChainSilhouetteIterator(), NotUP1D(QuantitativeInvisibilityUP1D(0)))
-## Splits strokes at points of highest 2D curavture
+## Splits strokes at points of highest 2D curvature
## 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)
@@ -62,7 +61,6 @@ shaders_list = [
SamplingShader(50),
ConstantThicknessShader(10),
pyNonLinearVaryingThicknessShader(4, 25, 0.6),
- TextureAssignerShader(6),
ConstantColorShader(0.2, 0.2, 0.2,1.0),
TipRemoverShader(10),
]
diff --git a/release/scripts/freestyle/styles/multiple_parameterization.py b/release/scripts/freestyle/styles/multiple_parameterization.py
index bf0691f2ce1..0e224c74bbf 100644
--- a/release/scripts/freestyle/styles/multiple_parameterization.py
+++ b/release/scripts/freestyle/styles/multiple_parameterization.py
@@ -37,7 +37,6 @@ from freestyle.shaders import (
IncreasingColorShader,
IncreasingThicknessShader,
SamplingShader,
- TextureAssignerShader,
pyHLRShader,
)
from freestyle.types import Operators
@@ -52,7 +51,6 @@ shaders_list = [
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/styles/near_lines.py b/release/scripts/freestyle/styles/near_lines.py
index b91eaea4023..5e260a22382 100644
--- a/release/scripts/freestyle/styles/near_lines.py
+++ b/release/scripts/freestyle/styles/near_lines.py
@@ -33,7 +33,6 @@ from freestyle.predicates import (
from freestyle.shaders import (
ConstantColorShader,
ConstantThicknessShader,
- TextureAssignerShader,
)
from freestyle.types import IntegrationType, Operators
@@ -42,7 +41,6 @@ upred = AndUP1D(QuantitativeInvisibilityUP1D(0), pyZSmallerUP1D(0.5, Integration
Operators.select(upred)
Operators.bidirectional_chain(ChainSilhouetteIterator(), NotUP1D(upred))
shaders_list = [
- TextureAssignerShader(-1),
ConstantThicknessShader(5),
ConstantColorShader(0.0, 0.0, 0.0),
]
diff --git a/release/scripts/freestyle/styles/qi0_not_external_contour.py b/release/scripts/freestyle/styles/qi0_not_external_contour.py
index fb3e3279b6c..3c9b8873485 100644
--- a/release/scripts/freestyle/styles/qi0_not_external_contour.py
+++ b/release/scripts/freestyle/styles/qi0_not_external_contour.py
@@ -36,7 +36,6 @@ from freestyle.shaders import (
IncreasingThicknessShader,
SamplingShader,
SpatialNoiseShader,
- TextureAssignerShader,
)
from freestyle.types import Operators
@@ -50,6 +49,5 @@ shaders_list = [
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/styles/sequentialsplit_sketchy.py b/release/scripts/freestyle/styles/sequentialsplit_sketchy.py
index e97e8f6da39..b980fdc87d7 100644
--- a/release/scripts/freestyle/styles/sequentialsplit_sketchy.py
+++ b/release/scripts/freestyle/styles/sequentialsplit_sketchy.py
@@ -35,7 +35,6 @@ from freestyle.shaders import (
ConstantColorShader,
IncreasingThicknessShader,
SpatialNoiseShader,
- TextureAssignerShader,
)
from freestyle.types import Nature, Operators
@@ -51,6 +50,5 @@ 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/styles/sketchy_multiple_parameterization.py b/release/scripts/freestyle/styles/sketchy_multiple_parameterization.py
index 0beb6e37c21..a3ce6112165 100644
--- a/release/scripts/freestyle/styles/sketchy_multiple_parameterization.py
+++ b/release/scripts/freestyle/styles/sketchy_multiple_parameterization.py
@@ -34,7 +34,6 @@ from freestyle.shaders import (
SamplingShader,
SmoothingShader,
SpatialNoiseShader,
- TextureAssignerShader,
pyHLRShader,
)
from freestyle.types import Operators
@@ -48,7 +47,6 @@ shaders_list = [
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/styles/sketchy_topology_broken.py b/release/scripts/freestyle/styles/sketchy_topology_broken.py
index 671bc2b1592..191d2d8bcbe 100644
--- a/release/scripts/freestyle/styles/sketchy_topology_broken.py
+++ b/release/scripts/freestyle/styles/sketchy_topology_broken.py
@@ -20,7 +20,7 @@
# Author : Stephane Grabli
# Date : 04/08/2005
# Purpose : The topology of the strokes is, first, built
-# independantly from the 3D topology of objects,
+# independently from the 3D topology of objects,
# and, second, so as to chain several times the same ViewEdge.
from freestyle.chainingiterators import pySketchyChainingIterator
@@ -34,14 +34,13 @@ from freestyle.shaders import (
SamplingShader,
SmoothingShader,
SpatialNoiseShader,
- TextureAssignerShader,
- pyBackboneStretcherNoCuspShader
+ pyBackboneStretcherNoCuspShader,
)
from freestyle.types import Operators
Operators.select(QuantitativeInvisibilityUP1D(0))
-## Chain 3 times each ViewEdge indpendantly from the
+## Chain 3 times each ViewEdge independently from the
## initial objects topology
Operators.bidirectional_chain(pySketchyChainingIterator(3))
shaders_list = [
@@ -50,9 +49,6 @@ shaders_list = [
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/styles/sketchy_topology_preserved.py b/release/scripts/freestyle/styles/sketchy_topology_preserved.py
index 161654f1f97..26c7fd37964 100644
--- a/release/scripts/freestyle/styles/sketchy_topology_preserved.py
+++ b/release/scripts/freestyle/styles/sketchy_topology_preserved.py
@@ -34,7 +34,6 @@ from freestyle.shaders import (
SamplingShader,
SmoothingShader,
SpatialNoiseShader,
- TextureAssignerShader,
)
from freestyle.types import Operators
@@ -48,6 +47,5 @@ shaders_list = [
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/styles/split_at_highest_2d_curvatures.py b/release/scripts/freestyle/styles/split_at_highest_2d_curvatures.py
index 3bfdb05a2db..68a80d89ea7 100644
--- a/release/scripts/freestyle/styles/split_at_highest_2d_curvatures.py
+++ b/release/scripts/freestyle/styles/split_at_highest_2d_curvatures.py
@@ -34,7 +34,6 @@ from freestyle.predicates import (
from freestyle.shaders import (
ConstantThicknessShader,
IncreasingColorShader,
- TextureAssignerShader,
)
from freestyle.types import Operators
@@ -46,6 +45,5 @@ Operators.recursive_split(func, pyParameterUP0D(0.4, 0.6), NotUP1D(pyHigherLengt
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/styles/split_at_tvertices.py b/release/scripts/freestyle/styles/split_at_tvertices.py
index 213922a03b1..8978b98f56b 100644
--- a/release/scripts/freestyle/styles/split_at_tvertices.py
+++ b/release/scripts/freestyle/styles/split_at_tvertices.py
@@ -31,7 +31,6 @@ from freestyle.predicates import (
from freestyle.shaders import (
ConstantThicknessShader,
IncreasingColorShader,
- TextureAssignerShader,
)
from freestyle.types import Nature, Operators
@@ -45,6 +44,5 @@ 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/styles/stroke_texture.py b/release/scripts/freestyle/styles/stroke_texture.py
deleted file mode 100644
index 614c34a6654..00000000000
--- a/release/scripts/freestyle/styles/stroke_texture.py
+++ /dev/null
@@ -1,49 +0,0 @@
-# ##### 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.chainingiterators import ChainSilhouetteIterator
-from freestyle.predicates import (
- NotUP1D,
- QuantitativeInvisibilityUP1D,
- TrueUP1D,
- )
-from freestyle.shaders import (
- BezierCurveShader,
- ConstantColorShader,
- ConstantThicknessShader,
- SamplingShader,
- StrokeTextureShader,
- )
-from freestyle.types import Operators, Stroke
-
-
-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/styles/uniformpruning_zsort.py b/release/scripts/freestyle/styles/uniformpruning_zsort.py
index 29b634b765b..814e140ee49 100644
--- a/release/scripts/freestyle/styles/uniformpruning_zsort.py
+++ b/release/scripts/freestyle/styles/uniformpruning_zsort.py
@@ -30,7 +30,6 @@ from freestyle.shaders import (
ConstantColorShader,
ConstantThicknessShader,
SamplingShader,
- StrokeTextureShader,
)
from freestyle.types import IntegrationType, Operators, Stroke
@@ -40,10 +39,8 @@ 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)