diff options
Diffstat (limited to 'release/scripts/freestyle')
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) |