2010-07-26 01:23:27 +00:00
|
|
|
# ##### 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 #####
|
|
|
|
|
2013-03-24 00:53:05 +00:00
|
|
|
# Filename : parameter_editor.py
|
|
|
|
# Authors : Tamito Kajiyama
|
|
|
|
# Date : 26/07/2010
|
|
|
|
# Purpose : Interactive manipulation of stylization parameters
|
|
|
|
|
2013-11-24 22:18:38 +00:00
|
|
|
from freestyle.types import (
|
|
|
|
BinaryPredicate1D,
|
2014-04-18 05:59:02 +00:00
|
|
|
IntegrationType,
|
2013-11-24 22:18:38 +00:00
|
|
|
Interface0DIterator,
|
|
|
|
Nature,
|
|
|
|
Noise,
|
2013-12-30 14:43:47 +00:00
|
|
|
Operators,
|
2013-11-24 22:18:38 +00:00
|
|
|
StrokeAttribute,
|
|
|
|
UnaryPredicate0D,
|
|
|
|
UnaryPredicate1D,
|
|
|
|
TVertex,
|
2014-07-24 02:08:04 +00:00
|
|
|
Material,
|
|
|
|
ViewEdge,
|
2013-11-24 22:18:38 +00:00
|
|
|
)
|
|
|
|
from freestyle.chainingiterators import (
|
|
|
|
ChainPredicateIterator,
|
|
|
|
ChainSilhouetteIterator,
|
|
|
|
pySketchyChainSilhouetteIterator,
|
|
|
|
pySketchyChainingIterator,
|
|
|
|
)
|
|
|
|
from freestyle.functions import (
|
|
|
|
Curvature2DAngleF0D,
|
|
|
|
Normal2DF0D,
|
|
|
|
QuantitativeInvisibilityF1D,
|
|
|
|
VertexOrientation2DF0D,
|
2014-07-24 02:08:04 +00:00
|
|
|
CurveMaterialF0D,
|
2013-11-24 22:18:38 +00:00
|
|
|
)
|
|
|
|
from freestyle.predicates import (
|
|
|
|
AndUP1D,
|
|
|
|
ContourUP1D,
|
|
|
|
ExternalContourUP1D,
|
|
|
|
FalseBP1D,
|
|
|
|
FalseUP1D,
|
2014-04-18 05:59:02 +00:00
|
|
|
Length2DBP1D,
|
|
|
|
NotBP1D,
|
2013-11-24 22:18:38 +00:00
|
|
|
NotUP1D,
|
|
|
|
OrUP1D,
|
|
|
|
QuantitativeInvisibilityUP1D,
|
|
|
|
TrueBP1D,
|
|
|
|
TrueUP1D,
|
|
|
|
WithinImageBoundaryUP1D,
|
2014-10-01 06:42:37 +00:00
|
|
|
pyNFirstUP1D,
|
2013-11-24 22:18:38 +00:00
|
|
|
pyNatureUP1D,
|
2014-10-01 06:42:37 +00:00
|
|
|
pyProjectedXBP1D,
|
|
|
|
pyProjectedYBP1D,
|
2014-04-18 05:59:02 +00:00
|
|
|
pyZBP1D,
|
2013-11-24 22:18:38 +00:00
|
|
|
)
|
|
|
|
from freestyle.shaders import (
|
|
|
|
BackboneStretcherShader,
|
|
|
|
BezierCurveShader,
|
2014-05-03 09:51:53 +00:00
|
|
|
BlenderTextureShader,
|
2013-11-24 22:18:38 +00:00
|
|
|
ConstantColorShader,
|
|
|
|
GuidingLinesShader,
|
|
|
|
PolygonalizationShader,
|
|
|
|
SamplingShader,
|
|
|
|
SpatialNoiseShader,
|
|
|
|
StrokeShader,
|
2014-05-03 09:51:53 +00:00
|
|
|
StrokeTextureStepShader,
|
2013-11-24 22:18:38 +00:00
|
|
|
TipRemoverShader,
|
|
|
|
pyBluePrintCirclesShader,
|
|
|
|
pyBluePrintEllipsesShader,
|
|
|
|
pyBluePrintSquaresShader,
|
2014-07-24 02:08:04 +00:00
|
|
|
RoundCapShader,
|
|
|
|
SquareCapShader,
|
2013-11-24 22:18:38 +00:00
|
|
|
)
|
|
|
|
from freestyle.utils import (
|
|
|
|
ContextFunctions,
|
|
|
|
getCurrentScene,
|
2014-07-24 02:08:04 +00:00
|
|
|
iter_distance_along_stroke,
|
|
|
|
iter_t2d_along_stroke,
|
|
|
|
iter_distance_from_camera,
|
|
|
|
iter_distance_from_object,
|
|
|
|
iter_material_value,
|
2014-04-11 06:16:03 +00:00
|
|
|
stroke_normal,
|
2014-07-24 02:08:04 +00:00
|
|
|
bound,
|
|
|
|
pairwise,
|
2014-11-24 21:41:34 +00:00
|
|
|
BoundedProperty,
|
2013-11-24 22:18:38 +00:00
|
|
|
)
|
|
|
|
from _freestyle import (
|
|
|
|
blendRamp,
|
|
|
|
evaluateColorRamp,
|
|
|
|
evaluateCurveMappingF,
|
|
|
|
)
|
2014-07-24 02:08:04 +00:00
|
|
|
|
2011-08-19 14:05:11 +00:00
|
|
|
import time
|
2014-07-24 02:08:04 +00:00
|
|
|
from mathutils import Vector
|
|
|
|
from math import pi, sin, cos, acos, radians
|
|
|
|
from itertools import cycle, tee
|
2010-07-26 01:23:27 +00:00
|
|
|
|
2014-11-24 21:41:34 +00:00
|
|
|
# lists of callback functions
|
|
|
|
# WARNING: highly experimental, not a stable API
|
|
|
|
callbacks_lineset_pre = []
|
|
|
|
callbacks_modifiers_post = []
|
|
|
|
callbacks_lineset_post = []
|
|
|
|
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2010-08-01 16:02:34 +00:00
|
|
|
class ColorRampModifier(StrokeShader):
|
2014-07-24 02:44:31 +00:00
|
|
|
"""Primitive for the color modifiers."""
|
2010-07-28 00:43:45 +00:00
|
|
|
def __init__(self, blend, influence, ramp):
|
|
|
|
StrokeShader.__init__(self)
|
2014-07-24 02:08:04 +00:00
|
|
|
self.blend = blend
|
|
|
|
self.influence = influence
|
|
|
|
self.ramp = ramp
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2010-08-01 16:02:34 +00:00
|
|
|
def evaluate(self, t):
|
2014-07-24 02:08:04 +00:00
|
|
|
col = evaluateColorRamp(self.ramp, t)
|
|
|
|
return col.xyz # omit alpha
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2010-08-01 16:02:34 +00:00
|
|
|
def blend_ramp(self, a, b):
|
2014-07-24 02:08:04 +00:00
|
|
|
return blendRamp(self.blend, a, self.influence, b)
|
2010-07-28 00:43:45 +00:00
|
|
|
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2011-09-11 19:57:38 +00:00
|
|
|
class ScalarBlendModifier(StrokeShader):
|
2014-07-24 02:44:31 +00:00
|
|
|
"""Primitive for alpha and thickness modifiers."""
|
2014-07-24 02:08:04 +00:00
|
|
|
def __init__(self, blend_type, influence):
|
2010-07-28 00:43:45 +00:00
|
|
|
StrokeShader.__init__(self)
|
2014-07-24 02:08:04 +00:00
|
|
|
self.blend_type = blend_type
|
|
|
|
self.influence = influence
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2011-09-11 19:57:38 +00:00
|
|
|
def blend(self, v1, v2):
|
2014-07-24 02:08:04 +00:00
|
|
|
fac = self.influence
|
2010-08-01 16:02:34 +00:00
|
|
|
facm = 1.0 - fac
|
2014-07-24 02:08:04 +00:00
|
|
|
if self.blend_type == 'MIX':
|
2010-08-01 16:02:34 +00:00
|
|
|
v1 = facm * v1 + fac * v2
|
2014-07-24 02:08:04 +00:00
|
|
|
elif self.blend_type == 'ADD':
|
2010-08-01 16:02:34 +00:00
|
|
|
v1 += fac * v2
|
2014-07-24 02:08:04 +00:00
|
|
|
elif self.blend_type == 'MULTIPLY':
|
2013-08-21 23:19:01 +00:00
|
|
|
v1 *= facm + fac * v2
|
2014-07-24 02:08:04 +00:00
|
|
|
elif self.blend_type == 'SUBTRACT':
|
2010-08-01 16:02:34 +00:00
|
|
|
v1 -= fac * v2
|
2014-07-24 02:08:04 +00:00
|
|
|
elif self.blend_type == 'DIVIDE':
|
|
|
|
v1 = facm * v1 + fac * v1 / v2 if v2 != 0.0 else v1
|
|
|
|
elif self.blend_type == 'DIFFERENCE':
|
2010-08-01 16:02:34 +00:00
|
|
|
v1 = facm * v1 + fac * abs(v1 - v2)
|
2014-07-24 02:08:04 +00:00
|
|
|
elif self.blend_type == 'MININUM':
|
|
|
|
v1 = min(fac * v2, v1)
|
|
|
|
elif self.blend_type == 'MAXIMUM':
|
|
|
|
v1 = max(fac * v2, v1)
|
2010-08-01 16:02:34 +00:00
|
|
|
else:
|
2014-07-24 02:08:04 +00:00
|
|
|
raise ValueError("unknown curve blend type: " + self.blend_type)
|
2010-08-01 16:02:34 +00:00
|
|
|
return v1
|
|
|
|
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2011-09-11 19:57:38 +00:00
|
|
|
class CurveMappingModifier(ScalarBlendModifier):
|
|
|
|
def __init__(self, blend, influence, mapping, invert, curve):
|
|
|
|
ScalarBlendModifier.__init__(self, blend, influence)
|
2013-04-23 07:06:29 +00:00
|
|
|
assert mapping in {'LINEAR', 'CURVE'}
|
2014-07-24 02:08:04 +00:00
|
|
|
self.evaluate = getattr(self, mapping)
|
|
|
|
self.invert = invert
|
|
|
|
self.curve = curve
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2011-09-11 19:57:38 +00:00
|
|
|
def LINEAR(self, t):
|
2014-07-24 02:08:04 +00:00
|
|
|
return (1.0 - t) if self.invert else t
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2011-09-11 19:57:38 +00:00
|
|
|
def CURVE(self, t):
|
2014-07-24 02:08:04 +00:00
|
|
|
return evaluateCurveMappingF(self.curve, 0, t)
|
2011-09-11 19:57:38 +00:00
|
|
|
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2012-04-07 17:28:09 +00:00
|
|
|
class ThicknessModifierMixIn:
|
|
|
|
def __init__(self):
|
2013-11-24 22:18:38 +00:00
|
|
|
scene = getCurrentScene()
|
2014-07-24 02:08:04 +00:00
|
|
|
self.persp_camera = (scene.camera.data.type == 'PERSP')
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2012-04-07 17:28:09 +00:00
|
|
|
def set_thickness(self, sv, outer, inner):
|
2014-07-24 02:08:04 +00:00
|
|
|
fe = sv.fedge
|
Freestyle Python API improvements - part 3.
Major API updates were made to address code review comments.
This revision mostly focuses on Python wrappers of C++ 0D and 1D elements (i.e.,
Interface0D and Interface1D, as well as their subclasses).
* Most getter/setter methods were reimplemented as attributes using PyGetSetDef.
Vector attributes are now implemented based on mathutils callbacks. Boolean
attributes now only accept boolean values.
* The __getitem__ method was removed and the Sequence protocol was used instead.
* The naming of methods and attributes was fixed to follow the naming conventions
of the Blender Python API (i.e., lower case + underscores for methods and attributes,
and CamelCase for classes). Some naming inconsistency within the Freestyle Python
API was also addressed.
* The Freestyle API had a number of method names including prefix/suffix "A" and
"B", and their meanings were inconsistent (i.e., referring to different things
depending on the classes). The names with these two letters were replaced with
more straightforward names. Also some attribute names were changed so as to indicate
the type of the value (e.g., FEdge.next_fedge instead of FEdge.next_edge) in line
with other names explicitly indicating what the value is (e.g., SVertex.viewvertex).
* In addition, some code clean-up was done in both C++ and Python.
Notes:
In summary, the following irregular naming changes were made through this revision
(those resulting from regular changes of naming conventions are not listed):
- CurvePoint: {A,B} --> {first,second}_svertex
- FEdge: vertex{A,B} --> {first,second}_svertex
- FEdge: {next,previous}Edge --> {next,previous}_fedge
- FEdgeSharp: normal{A,B} --> normal_{right,left}
- FEdgeSharp: {a,b}FaceMark --> face_mark_{right,left}
- FEdgeSharp: {a,b}Material --> material_{right,left}
- FEdgeSharp: {a,b}MaterialIndex --> material_index_{right,left}
- FrsCurve: empty --> is_empty
- FrsCurve: nSegments --> segments_size
- TVertex: mate() --> get_mate()
- ViewEdge: fedge{A,B} --> {first,last}_fedge
- ViewEdge: setaShape, aShape --> occlude
- ViewEdge: {A,B} --> {first,last}_viewvertex
- ViewMap: getScene3dBBox --> scene_bbox
2013-02-14 23:48:34 +00:00
|
|
|
nature = fe.nature
|
2012-04-07 17:28:09 +00:00
|
|
|
if (nature & Nature.BORDER):
|
2014-07-24 02:08:04 +00:00
|
|
|
if self.persp_camera:
|
|
|
|
point = -sv.point_3d.normalized()
|
Freestyle Python API improvements - part 3.
Major API updates were made to address code review comments.
This revision mostly focuses on Python wrappers of C++ 0D and 1D elements (i.e.,
Interface0D and Interface1D, as well as their subclasses).
* Most getter/setter methods were reimplemented as attributes using PyGetSetDef.
Vector attributes are now implemented based on mathutils callbacks. Boolean
attributes now only accept boolean values.
* The __getitem__ method was removed and the Sequence protocol was used instead.
* The naming of methods and attributes was fixed to follow the naming conventions
of the Blender Python API (i.e., lower case + underscores for methods and attributes,
and CamelCase for classes). Some naming inconsistency within the Freestyle Python
API was also addressed.
* The Freestyle API had a number of method names including prefix/suffix "A" and
"B", and their meanings were inconsistent (i.e., referring to different things
depending on the classes). The names with these two letters were replaced with
more straightforward names. Also some attribute names were changed so as to indicate
the type of the value (e.g., FEdge.next_fedge instead of FEdge.next_edge) in line
with other names explicitly indicating what the value is (e.g., SVertex.viewvertex).
* In addition, some code clean-up was done in both C++ and Python.
Notes:
In summary, the following irregular naming changes were made through this revision
(those resulting from regular changes of naming conventions are not listed):
- CurvePoint: {A,B} --> {first,second}_svertex
- FEdge: vertex{A,B} --> {first,second}_svertex
- FEdge: {next,previous}Edge --> {next,previous}_fedge
- FEdgeSharp: normal{A,B} --> normal_{right,left}
- FEdgeSharp: {a,b}FaceMark --> face_mark_{right,left}
- FEdgeSharp: {a,b}Material --> material_{right,left}
- FEdgeSharp: {a,b}MaterialIndex --> material_index_{right,left}
- FrsCurve: empty --> is_empty
- FrsCurve: nSegments --> segments_size
- TVertex: mate() --> get_mate()
- ViewEdge: fedge{A,B} --> {first,last}_fedge
- ViewEdge: setaShape, aShape --> occlude
- ViewEdge: {A,B} --> {first,last}_viewvertex
- ViewMap: getScene3dBBox --> scene_bbox
2013-02-14 23:48:34 +00:00
|
|
|
dir = point.dot(fe.normal_left)
|
2012-04-07 17:28:09 +00:00
|
|
|
else:
|
Freestyle Python API improvements - part 3.
Major API updates were made to address code review comments.
This revision mostly focuses on Python wrappers of C++ 0D and 1D elements (i.e.,
Interface0D and Interface1D, as well as their subclasses).
* Most getter/setter methods were reimplemented as attributes using PyGetSetDef.
Vector attributes are now implemented based on mathutils callbacks. Boolean
attributes now only accept boolean values.
* The __getitem__ method was removed and the Sequence protocol was used instead.
* The naming of methods and attributes was fixed to follow the naming conventions
of the Blender Python API (i.e., lower case + underscores for methods and attributes,
and CamelCase for classes). Some naming inconsistency within the Freestyle Python
API was also addressed.
* The Freestyle API had a number of method names including prefix/suffix "A" and
"B", and their meanings were inconsistent (i.e., referring to different things
depending on the classes). The names with these two letters were replaced with
more straightforward names. Also some attribute names were changed so as to indicate
the type of the value (e.g., FEdge.next_fedge instead of FEdge.next_edge) in line
with other names explicitly indicating what the value is (e.g., SVertex.viewvertex).
* In addition, some code clean-up was done in both C++ and Python.
Notes:
In summary, the following irregular naming changes were made through this revision
(those resulting from regular changes of naming conventions are not listed):
- CurvePoint: {A,B} --> {first,second}_svertex
- FEdge: vertex{A,B} --> {first,second}_svertex
- FEdge: {next,previous}Edge --> {next,previous}_fedge
- FEdgeSharp: normal{A,B} --> normal_{right,left}
- FEdgeSharp: {a,b}FaceMark --> face_mark_{right,left}
- FEdgeSharp: {a,b}Material --> material_{right,left}
- FEdgeSharp: {a,b}MaterialIndex --> material_index_{right,left}
- FrsCurve: empty --> is_empty
- FrsCurve: nSegments --> segments_size
- TVertex: mate() --> get_mate()
- ViewEdge: fedge{A,B} --> {first,last}_fedge
- ViewEdge: setaShape, aShape --> occlude
- ViewEdge: {A,B} --> {first,last}_viewvertex
- ViewMap: getScene3dBBox --> scene_bbox
2013-02-14 23:48:34 +00:00
|
|
|
dir = fe.normal_left.z
|
2013-08-21 23:19:01 +00:00
|
|
|
if dir < 0.0: # the back side is visible
|
2012-04-07 17:28:09 +00:00
|
|
|
outer, inner = inner, outer
|
|
|
|
elif (nature & Nature.SILHOUETTE):
|
2013-08-21 23:19:01 +00:00
|
|
|
if fe.is_smooth: # TODO more tests needed
|
2012-04-07 17:28:09 +00:00
|
|
|
outer, inner = inner, outer
|
|
|
|
else:
|
|
|
|
outer = inner = (outer + inner) / 2
|
2013-01-27 20:17:49 +00:00
|
|
|
sv.attribute.thickness = (outer, inner)
|
2012-04-07 17:28:09 +00:00
|
|
|
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2012-04-07 17:28:09 +00:00
|
|
|
class ThicknessBlenderMixIn(ThicknessModifierMixIn):
|
|
|
|
def __init__(self, position, ratio):
|
|
|
|
ThicknessModifierMixIn.__init__(self)
|
2014-07-24 02:08:04 +00:00
|
|
|
self.position = position
|
|
|
|
self.ratio = ratio
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2014-07-24 02:08:04 +00:00
|
|
|
def blend_thickness(self, svert, v):
|
2014-07-24 02:44:31 +00:00
|
|
|
"""Blends and sets the thickness."""
|
2014-07-24 02:08:04 +00:00
|
|
|
outer, inner = svert.attribute.thickness
|
|
|
|
fe = svert.fedge
|
2013-08-30 09:17:27 +00:00
|
|
|
v = self.blend(outer + inner, v)
|
2012-04-07 17:28:09 +00:00
|
|
|
|
2014-07-24 02:08:04 +00:00
|
|
|
# Part 1: blend
|
2014-07-24 02:43:16 +00:00
|
|
|
if self.position == 'CENTER':
|
2014-07-24 02:08:04 +00:00
|
|
|
outer = inner = v * 0.5
|
2014-07-24 02:43:16 +00:00
|
|
|
elif self.position == 'INSIDE':
|
2014-07-24 02:08:04 +00:00
|
|
|
outer, inner = 0, v
|
2014-07-24 02:43:16 +00:00
|
|
|
elif self.position == 'OUTSIDE':
|
2014-07-24 02:08:04 +00:00
|
|
|
outer, inner = v, 0
|
2014-07-24 02:43:16 +00:00
|
|
|
elif self.position == 'RELATIVE':
|
2014-07-24 02:08:04 +00:00
|
|
|
outer, inner = v * self.ratio, v - (v * self.ratio)
|
|
|
|
else:
|
|
|
|
raise ValueError("unknown thickness position: " + position)
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2014-07-24 02:08:04 +00:00
|
|
|
# Part 2: set
|
|
|
|
if (fe.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):
|
|
|
|
if fe.is_smooth: # TODO more tests needed
|
|
|
|
outer, inner = inner, outer
|
|
|
|
else:
|
|
|
|
outer = inner = (outer + inner) / 2
|
|
|
|
svert.attribute.thickness = (outer, inner)
|
2012-04-07 17:28:09 +00:00
|
|
|
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2012-04-07 17:28:09 +00:00
|
|
|
class BaseThicknessShader(StrokeShader, ThicknessModifierMixIn):
|
|
|
|
def __init__(self, thickness, position, ratio):
|
|
|
|
StrokeShader.__init__(self)
|
|
|
|
ThicknessModifierMixIn.__init__(self)
|
2013-04-23 07:06:29 +00:00
|
|
|
if position == 'CENTER':
|
2014-07-24 02:08:04 +00:00
|
|
|
self.outer = thickness * 0.5
|
|
|
|
self.inner = thickness - self.outer
|
2013-04-23 07:06:29 +00:00
|
|
|
elif position == 'INSIDE':
|
2014-07-24 02:08:04 +00:00
|
|
|
self.outer = 0
|
|
|
|
self.inner = thickness
|
2013-04-23 07:06:29 +00:00
|
|
|
elif position == 'OUTSIDE':
|
2014-07-24 02:08:04 +00:00
|
|
|
self.outer = thickness
|
|
|
|
self.inner = 0
|
2013-04-23 07:06:29 +00:00
|
|
|
elif position == 'RELATIVE':
|
2014-07-24 02:08:04 +00:00
|
|
|
self.outer = thickness * ratio
|
|
|
|
self.inner = thickness - self.outer
|
2012-04-07 17:28:09 +00:00
|
|
|
else:
|
2014-07-24 02:08:04 +00:00
|
|
|
raise ValueError("unknown thickness position: " + position)
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2012-04-07 17:28:09 +00:00
|
|
|
def shade(self, stroke):
|
2014-07-24 02:08:04 +00:00
|
|
|
for svert in stroke:
|
|
|
|
self.set_thickness(svert, self.outer, self.inner)
|
2012-04-07 17:28:09 +00:00
|
|
|
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2010-08-01 16:02:34 +00:00
|
|
|
# Along Stroke modifiers
|
|
|
|
|
|
|
|
class ColorAlongStrokeShader(ColorRampModifier):
|
2014-07-24 02:44:31 +00:00
|
|
|
"""Maps a ramp to the color of the stroke, using the curvilinear abscissa (t)."""
|
2010-07-28 00:43:45 +00:00
|
|
|
def shade(self, stroke):
|
2014-07-24 02:08:04 +00:00
|
|
|
for svert, t in zip(stroke, iter_t2d_along_stroke(stroke)):
|
|
|
|
a = svert.attribute.color
|
2010-08-01 16:02:34 +00:00
|
|
|
b = self.evaluate(t)
|
2014-07-24 02:08:04 +00:00
|
|
|
svert.attribute.color = self.blend_ramp(a, b)
|
2010-07-28 00:43:45 +00:00
|
|
|
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2010-08-01 16:02:34 +00:00
|
|
|
class AlphaAlongStrokeShader(CurveMappingModifier):
|
2015-02-27 04:41:50 +00:00
|
|
|
"""Maps a curve to the alpha/transparency of the stroke, using the curvilinear abscissa (t)."""
|
2010-08-01 16:02:34 +00:00
|
|
|
def shade(self, stroke):
|
2014-07-24 02:08:04 +00:00
|
|
|
for svert, t in zip(stroke, iter_t2d_along_stroke(stroke)):
|
|
|
|
a = svert.attribute.alpha
|
2010-08-01 16:02:34 +00:00
|
|
|
b = self.evaluate(t)
|
2014-07-24 02:08:04 +00:00
|
|
|
svert.attribute.alpha = self.blend(a, b)
|
2010-08-01 16:02:34 +00:00
|
|
|
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2012-04-07 17:28:09 +00:00
|
|
|
class ThicknessAlongStrokeShader(ThicknessBlenderMixIn, CurveMappingModifier):
|
2014-07-24 02:44:31 +00:00
|
|
|
"""Maps a curve to the thickness of the stroke, using the curvilinear abscissa (t)."""
|
2012-04-07 17:28:09 +00:00
|
|
|
def __init__(self, thickness_position, thickness_ratio,
|
|
|
|
blend, influence, mapping, invert, curve, value_min, value_max):
|
|
|
|
ThicknessBlenderMixIn.__init__(self, thickness_position, thickness_ratio)
|
2010-08-01 16:02:34 +00:00
|
|
|
CurveMappingModifier.__init__(self, blend, influence, mapping, invert, curve)
|
2014-07-24 02:08:04 +00:00
|
|
|
self.value = BoundedProperty(value_min, value_max, value_max - value_min)
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2010-07-28 00:43:45 +00:00
|
|
|
def shade(self, stroke):
|
2014-07-24 02:08:04 +00:00
|
|
|
for svert, t in zip(stroke, iter_t2d_along_stroke(stroke)):
|
|
|
|
b = self.value.min + self.evaluate(t) * self.value.delta
|
|
|
|
self.blend_thickness(svert, b)
|
2010-08-01 16:02:34 +00:00
|
|
|
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2014-07-24 02:08:04 +00:00
|
|
|
# -- Distance from Camera modifiers -- #
|
|
|
|
|
2010-08-01 16:02:34 +00:00
|
|
|
class ColorDistanceFromCameraShader(ColorRampModifier):
|
2014-07-24 02:44:31 +00:00
|
|
|
"""Picks a color value from a ramp based on the vertex' distance from the camera."""
|
2010-08-01 16:02:34 +00:00
|
|
|
def __init__(self, blend, influence, ramp, range_min, range_max):
|
|
|
|
ColorRampModifier.__init__(self, blend, influence, ramp)
|
2014-07-24 02:08:04 +00:00
|
|
|
self.range = BoundedProperty(range_min, range_max, range_max - range_min)
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2010-08-01 16:02:34 +00:00
|
|
|
def shade(self, stroke):
|
2014-07-24 02:08:04 +00:00
|
|
|
it = iter_distance_from_camera(stroke, *self.range)
|
|
|
|
for svert, t in it:
|
|
|
|
a = svert.attribute.color
|
2010-08-01 16:02:34 +00:00
|
|
|
b = self.evaluate(t)
|
2014-07-24 02:08:04 +00:00
|
|
|
svert.attribute.color = self.blend_ramp(a, b)
|
2010-08-01 16:02:34 +00:00
|
|
|
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2010-08-01 16:02:34 +00:00
|
|
|
class AlphaDistanceFromCameraShader(CurveMappingModifier):
|
2014-07-24 02:44:31 +00:00
|
|
|
"""Picks an alpha value from a curve based on the vertex' distance from the camera"""
|
2010-08-01 16:02:34 +00:00
|
|
|
def __init__(self, blend, influence, mapping, invert, curve, range_min, range_max):
|
|
|
|
CurveMappingModifier.__init__(self, blend, influence, mapping, invert, curve)
|
2014-07-24 02:08:04 +00:00
|
|
|
self.range = BoundedProperty(range_min, range_max, range_max - range_min)
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2010-08-01 16:02:34 +00:00
|
|
|
def shade(self, stroke):
|
2014-07-24 02:08:04 +00:00
|
|
|
it = iter_distance_from_camera(stroke, *self.range)
|
|
|
|
for svert, t in it:
|
|
|
|
a = svert.attribute.alpha
|
2010-08-01 16:02:34 +00:00
|
|
|
b = self.evaluate(t)
|
2014-07-24 02:08:04 +00:00
|
|
|
svert.attribute.alpha = self.blend(a, b)
|
2010-08-01 16:02:34 +00:00
|
|
|
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2012-04-07 17:28:09 +00:00
|
|
|
class ThicknessDistanceFromCameraShader(ThicknessBlenderMixIn, CurveMappingModifier):
|
2014-07-24 02:44:31 +00:00
|
|
|
"""Picks a thickness value from a curve based on the vertex' distance from the camera."""
|
2012-04-07 17:28:09 +00:00
|
|
|
def __init__(self, thickness_position, thickness_ratio,
|
|
|
|
blend, influence, mapping, invert, curve, range_min, range_max, value_min, value_max):
|
|
|
|
ThicknessBlenderMixIn.__init__(self, thickness_position, thickness_ratio)
|
2010-08-01 16:02:34 +00:00
|
|
|
CurveMappingModifier.__init__(self, blend, influence, mapping, invert, curve)
|
2014-07-24 02:08:04 +00:00
|
|
|
self.range = BoundedProperty(range_min, range_max, range_max - range_min)
|
|
|
|
self.value = BoundedProperty(value_min, value_max, value_max - value_min)
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2010-08-01 16:02:34 +00:00
|
|
|
def shade(self, stroke):
|
2014-07-24 02:08:04 +00:00
|
|
|
for (svert, t) in iter_distance_from_camera(stroke, *self.range):
|
|
|
|
b = self.value.min + self.evaluate(t) * self.value.delta
|
|
|
|
self.blend_thickness(svert, b)
|
2010-08-01 16:02:34 +00:00
|
|
|
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2010-08-01 22:11:57 +00:00
|
|
|
# Distance from Object modifiers
|
|
|
|
|
|
|
|
class ColorDistanceFromObjectShader(ColorRampModifier):
|
2014-07-24 02:44:31 +00:00
|
|
|
"""Picks a color value from a ramp based on the vertex' distance from a given object."""
|
2010-08-01 22:11:57 +00:00
|
|
|
def __init__(self, blend, influence, ramp, target, range_min, range_max):
|
|
|
|
ColorRampModifier.__init__(self, blend, influence, ramp)
|
2014-07-24 02:08:04 +00:00
|
|
|
if target is None:
|
|
|
|
raise ValueError("ColorDistanceFromObjectShader: target can't be None ")
|
|
|
|
self.range = BoundedProperty(range_min, range_max, range_max - range_min)
|
|
|
|
# construct a model-view matrix
|
|
|
|
matrix = getCurrentScene().camera.matrix_world.inverted()
|
|
|
|
# get the object location in the camera coordinate
|
|
|
|
self.loc = matrix * target.location
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2010-08-01 22:11:57 +00:00
|
|
|
def shade(self, stroke):
|
2014-07-24 02:08:04 +00:00
|
|
|
it = iter_distance_from_object(stroke, self.loc, *self.range)
|
|
|
|
for svert, t in it:
|
|
|
|
a = svert.attribute.color
|
2010-08-01 22:11:57 +00:00
|
|
|
b = self.evaluate(t)
|
2014-07-24 02:08:04 +00:00
|
|
|
svert.attribute.color = self.blend_ramp(a, b)
|
2010-08-01 22:11:57 +00:00
|
|
|
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2010-08-01 22:11:57 +00:00
|
|
|
class AlphaDistanceFromObjectShader(CurveMappingModifier):
|
2014-07-24 02:44:31 +00:00
|
|
|
"""Picks an alpha value from a curve based on the vertex' distance from a given object."""
|
2010-08-01 22:11:57 +00:00
|
|
|
def __init__(self, blend, influence, mapping, invert, curve, target, range_min, range_max):
|
|
|
|
CurveMappingModifier.__init__(self, blend, influence, mapping, invert, curve)
|
2014-07-24 02:08:04 +00:00
|
|
|
if target is None:
|
|
|
|
raise ValueError("AlphaDistanceFromObjectShader: target can't be None ")
|
|
|
|
self.range = BoundedProperty(range_min, range_max, range_max - range_min)
|
|
|
|
# construct a model-view matrix
|
|
|
|
matrix = getCurrentScene().camera.matrix_world.inverted()
|
|
|
|
# get the object location in the camera coordinate
|
|
|
|
self.loc = matrix * target.location
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2010-08-01 22:11:57 +00:00
|
|
|
def shade(self, stroke):
|
2014-07-24 02:08:04 +00:00
|
|
|
it = iter_distance_from_object(stroke, self.loc, *self.range)
|
|
|
|
for svert, t in it:
|
|
|
|
a = svert.attribute.alpha
|
2010-08-01 22:11:57 +00:00
|
|
|
b = self.evaluate(t)
|
2014-07-24 02:08:04 +00:00
|
|
|
svert.attribute.alpha = self.blend(a, b)
|
2010-08-01 22:11:57 +00:00
|
|
|
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2012-04-07 17:28:09 +00:00
|
|
|
class ThicknessDistanceFromObjectShader(ThicknessBlenderMixIn, CurveMappingModifier):
|
2014-07-24 02:44:31 +00:00
|
|
|
"""Picks a thickness value from a curve based on the vertex' distance from a given object."""
|
2012-04-07 17:28:09 +00:00
|
|
|
def __init__(self, thickness_position, thickness_ratio,
|
|
|
|
blend, influence, mapping, invert, curve, target, range_min, range_max, value_min, value_max):
|
|
|
|
ThicknessBlenderMixIn.__init__(self, thickness_position, thickness_ratio)
|
2010-08-01 22:11:57 +00:00
|
|
|
CurveMappingModifier.__init__(self, blend, influence, mapping, invert, curve)
|
2014-07-24 02:08:04 +00:00
|
|
|
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)
|
|
|
|
# construct a model-view matrix
|
|
|
|
matrix = getCurrentScene().camera.matrix_world.inverted()
|
|
|
|
# get the object location in the camera coordinate
|
|
|
|
self.loc = matrix * target.location
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2010-08-01 22:11:57 +00:00
|
|
|
def shade(self, stroke):
|
2014-07-24 02:08:04 +00:00
|
|
|
it = iter_distance_from_object(stroke, self.loc, *self.range)
|
|
|
|
for svert, t in it:
|
|
|
|
b = self.value.min + self.evaluate(t) * self.value.delta
|
|
|
|
self.blend_thickness(svert, b)
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2011-07-24 23:29:19 +00:00
|
|
|
# Material modifiers
|
|
|
|
class ColorMaterialShader(ColorRampModifier):
|
2014-07-24 02:44:31 +00:00
|
|
|
"""Assigns a color to the vertices based on their underlying material."""
|
2013-04-23 07:06:29 +00:00
|
|
|
def __init__(self, blend, influence, ramp, material_attribute, use_ramp):
|
2011-07-24 23:29:19 +00:00
|
|
|
ColorRampModifier.__init__(self, blend, influence, ramp)
|
2014-07-24 02:08:04 +00:00
|
|
|
self.attribute = material_attribute
|
|
|
|
self.use_ramp = use_ramp
|
|
|
|
self.func = CurveMaterialF0D()
|
|
|
|
|
|
|
|
def shade(self, stroke, attributes={'DIFF', 'SPEC', 'LINE'}):
|
|
|
|
it = Interface0DIterator(stroke)
|
|
|
|
if not self.use_ramp and self.attribute in attributes:
|
|
|
|
for svert in it:
|
|
|
|
material = self.func(it)
|
2014-07-24 02:43:16 +00:00
|
|
|
if self.attribute == 'LINE':
|
2014-10-19 17:10:06 +00:00
|
|
|
b = material.line[0:3]
|
2014-07-24 02:43:16 +00:00
|
|
|
elif self.attribute == 'DIFF':
|
|
|
|
b = material.diffuse[0:3]
|
2014-07-24 02:08:04 +00:00
|
|
|
else:
|
|
|
|
b = material.specular[0:3]
|
|
|
|
a = svert.attribute.color
|
|
|
|
svert.attribute.color = self.blend_ramp(a, b)
|
2011-07-24 23:29:19 +00:00
|
|
|
else:
|
2014-07-24 02:08:04 +00:00
|
|
|
for svert, value in iter_material_value(stroke, self.func, self.attribute):
|
|
|
|
a = svert.attribute.color
|
|
|
|
b = self.evaluate(value)
|
|
|
|
svert.attribute.color = self.blend_ramp(a, b)
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2011-07-24 23:29:19 +00:00
|
|
|
class AlphaMaterialShader(CurveMappingModifier):
|
2014-07-24 02:44:31 +00:00
|
|
|
"""Assigns an alpha value to the vertices based on their underlying material."""
|
2013-04-23 07:06:29 +00:00
|
|
|
def __init__(self, blend, influence, mapping, invert, curve, material_attribute):
|
2011-07-24 23:29:19 +00:00
|
|
|
CurveMappingModifier.__init__(self, blend, influence, mapping, invert, curve)
|
2014-07-24 02:08:04 +00:00
|
|
|
self.attribute = material_attribute
|
|
|
|
self.func = CurveMaterialF0D()
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2011-07-24 23:29:19 +00:00
|
|
|
def shade(self, stroke):
|
2014-07-24 02:08:04 +00:00
|
|
|
for svert, value in iter_material_value(stroke, self.func, self.attribute):
|
|
|
|
a = svert.attribute.alpha
|
|
|
|
b = self.evaluate(value)
|
|
|
|
svert.attribute.alpha = self.blend(a, b)
|
2011-07-24 23:29:19 +00:00
|
|
|
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2012-04-07 17:28:09 +00:00
|
|
|
class ThicknessMaterialShader(ThicknessBlenderMixIn, CurveMappingModifier):
|
2014-07-24 02:44:31 +00:00
|
|
|
"""Assigns a thickness value to the vertices based on their underlying material."""
|
2012-04-07 17:28:09 +00:00
|
|
|
def __init__(self, thickness_position, thickness_ratio,
|
2013-04-23 07:06:29 +00:00
|
|
|
blend, influence, mapping, invert, curve, material_attribute, value_min, value_max):
|
2012-04-07 17:28:09 +00:00
|
|
|
ThicknessBlenderMixIn.__init__(self, thickness_position, thickness_ratio)
|
2011-07-24 23:29:19 +00:00
|
|
|
CurveMappingModifier.__init__(self, blend, influence, mapping, invert, curve)
|
2014-07-24 02:08:04 +00:00
|
|
|
self.attribute = material_attribute
|
|
|
|
self.value = BoundedProperty(value_min, value_max, value_max - value_min)
|
|
|
|
self.func = CurveMaterialF0D()
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2011-07-24 23:29:19 +00:00
|
|
|
def shade(self, stroke):
|
2014-07-24 02:08:04 +00:00
|
|
|
for svert, value in iter_material_value(stroke, self.func, self.attribute):
|
|
|
|
b = self.value.min + self.evaluate(value) * self.value.delta
|
|
|
|
self.blend_thickness(svert, b)
|
2011-09-11 19:57:38 +00:00
|
|
|
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2011-09-11 19:57:38 +00:00
|
|
|
# Calligraphic thickness modifier
|
|
|
|
|
2014-07-24 02:08:04 +00:00
|
|
|
|
2012-04-07 17:28:09 +00:00
|
|
|
class CalligraphicThicknessShader(ThicknessBlenderMixIn, ScalarBlendModifier):
|
2014-07-24 02:44:31 +00:00
|
|
|
"""Thickness modifier for achieving a calligraphy-like effect."""
|
2012-04-07 17:28:09 +00:00
|
|
|
def __init__(self, thickness_position, thickness_ratio,
|
2014-07-24 02:08:04 +00:00
|
|
|
blend_type, influence, orientation, thickness_min, thickness_max):
|
2012-04-07 17:28:09 +00:00
|
|
|
ThicknessBlenderMixIn.__init__(self, thickness_position, thickness_ratio)
|
2014-07-24 02:08:04 +00:00
|
|
|
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.func = VertexOrientation2DF0D()
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2011-09-11 19:57:38 +00:00
|
|
|
def shade(self, stroke):
|
2014-07-24 02:08:04 +00:00
|
|
|
it = Interface0DIterator(stroke)
|
|
|
|
for svert in it:
|
|
|
|
dir = self.func(it)
|
|
|
|
if dir.length != 0.0:
|
|
|
|
dir.normalize()
|
|
|
|
fac = abs(dir.orthogonal() * self.orientation)
|
|
|
|
b = self.thickness.min + fac * self.thickness.delta
|
|
|
|
else:
|
|
|
|
b = self.thickness.min
|
|
|
|
self.blend_thickness(svert, b)
|
2011-07-24 23:29:19 +00:00
|
|
|
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2011-08-19 14:05:11 +00:00
|
|
|
# Geometry modifiers
|
|
|
|
|
|
|
|
class SinusDisplacementShader(StrokeShader):
|
2014-07-24 02:44:31 +00:00
|
|
|
"""Displaces the stroke in a sinewave-like shape."""
|
2011-08-19 14:05:11 +00:00
|
|
|
def __init__(self, wavelength, amplitude, phase):
|
|
|
|
StrokeShader.__init__(self)
|
2014-07-24 02:08:04 +00:00
|
|
|
self.wavelength = wavelength
|
|
|
|
self.amplitude = amplitude
|
|
|
|
self.phase = phase / wavelength * 2 * pi
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2011-08-19 14:05:11 +00:00
|
|
|
def shade(self, stroke):
|
2014-07-24 02:08:04 +00:00
|
|
|
# normals are stored in a tuple, so they don't update when we reposition vertices.
|
|
|
|
normals = tuple(stroke_normal(stroke))
|
|
|
|
distances = iter_distance_along_stroke(stroke)
|
|
|
|
coeff = 1 / self.wavelength * 2 * pi
|
|
|
|
for svert, distance, normal in zip(stroke, distances, normals):
|
|
|
|
n = normal * self.amplitude * cos(distance * coeff + self.phase)
|
|
|
|
svert.point += n
|
Freestyle Python API improvements - part 3.
Major API updates were made to address code review comments.
This revision mostly focuses on Python wrappers of C++ 0D and 1D elements (i.e.,
Interface0D and Interface1D, as well as their subclasses).
* Most getter/setter methods were reimplemented as attributes using PyGetSetDef.
Vector attributes are now implemented based on mathutils callbacks. Boolean
attributes now only accept boolean values.
* The __getitem__ method was removed and the Sequence protocol was used instead.
* The naming of methods and attributes was fixed to follow the naming conventions
of the Blender Python API (i.e., lower case + underscores for methods and attributes,
and CamelCase for classes). Some naming inconsistency within the Freestyle Python
API was also addressed.
* The Freestyle API had a number of method names including prefix/suffix "A" and
"B", and their meanings were inconsistent (i.e., referring to different things
depending on the classes). The names with these two letters were replaced with
more straightforward names. Also some attribute names were changed so as to indicate
the type of the value (e.g., FEdge.next_fedge instead of FEdge.next_edge) in line
with other names explicitly indicating what the value is (e.g., SVertex.viewvertex).
* In addition, some code clean-up was done in both C++ and Python.
Notes:
In summary, the following irregular naming changes were made through this revision
(those resulting from regular changes of naming conventions are not listed):
- CurvePoint: {A,B} --> {first,second}_svertex
- FEdge: vertex{A,B} --> {first,second}_svertex
- FEdge: {next,previous}Edge --> {next,previous}_fedge
- FEdgeSharp: normal{A,B} --> normal_{right,left}
- FEdgeSharp: {a,b}FaceMark --> face_mark_{right,left}
- FEdgeSharp: {a,b}Material --> material_{right,left}
- FEdgeSharp: {a,b}MaterialIndex --> material_index_{right,left}
- FrsCurve: empty --> is_empty
- FrsCurve: nSegments --> segments_size
- TVertex: mate() --> get_mate()
- ViewEdge: fedge{A,B} --> {first,last}_fedge
- ViewEdge: setaShape, aShape --> occlude
- ViewEdge: {A,B} --> {first,last}_viewvertex
- ViewMap: getScene3dBBox --> scene_bbox
2013-02-14 23:48:34 +00:00
|
|
|
stroke.update_length()
|
2011-08-19 14:05:11 +00:00
|
|
|
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2011-08-30 00:29:12 +00:00
|
|
|
class PerlinNoise1DShader(StrokeShader):
|
2014-07-24 02:08:04 +00:00
|
|
|
"""
|
|
|
|
Displaces the stroke using the curvilinear abscissa. This means
|
|
|
|
that lines with the same length and sampling interval will be
|
2014-07-24 02:44:31 +00:00
|
|
|
identically distorded.
|
2014-07-24 02:08:04 +00:00
|
|
|
"""
|
|
|
|
def __init__(self, freq=10, amp=10, oct=4, angle=radians(45), seed=-1):
|
2011-08-30 00:29:12 +00:00
|
|
|
StrokeShader.__init__(self)
|
2014-07-24 02:08:04 +00:00
|
|
|
self.noise = Noise(seed)
|
|
|
|
self.freq = freq
|
|
|
|
self.amp = amp
|
|
|
|
self.oct = oct
|
|
|
|
self.dir = Vector((cos(angle), sin(angle)))
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2011-08-30 00:29:12 +00:00
|
|
|
def shade(self, stroke):
|
Freestyle Python API improvements - part 3.
Major API updates were made to address code review comments.
This revision mostly focuses on Python wrappers of C++ 0D and 1D elements (i.e.,
Interface0D and Interface1D, as well as their subclasses).
* Most getter/setter methods were reimplemented as attributes using PyGetSetDef.
Vector attributes are now implemented based on mathutils callbacks. Boolean
attributes now only accept boolean values.
* The __getitem__ method was removed and the Sequence protocol was used instead.
* The naming of methods and attributes was fixed to follow the naming conventions
of the Blender Python API (i.e., lower case + underscores for methods and attributes,
and CamelCase for classes). Some naming inconsistency within the Freestyle Python
API was also addressed.
* The Freestyle API had a number of method names including prefix/suffix "A" and
"B", and their meanings were inconsistent (i.e., referring to different things
depending on the classes). The names with these two letters were replaced with
more straightforward names. Also some attribute names were changed so as to indicate
the type of the value (e.g., FEdge.next_fedge instead of FEdge.next_edge) in line
with other names explicitly indicating what the value is (e.g., SVertex.viewvertex).
* In addition, some code clean-up was done in both C++ and Python.
Notes:
In summary, the following irregular naming changes were made through this revision
(those resulting from regular changes of naming conventions are not listed):
- CurvePoint: {A,B} --> {first,second}_svertex
- FEdge: vertex{A,B} --> {first,second}_svertex
- FEdge: {next,previous}Edge --> {next,previous}_fedge
- FEdgeSharp: normal{A,B} --> normal_{right,left}
- FEdgeSharp: {a,b}FaceMark --> face_mark_{right,left}
- FEdgeSharp: {a,b}Material --> material_{right,left}
- FEdgeSharp: {a,b}MaterialIndex --> material_index_{right,left}
- FrsCurve: empty --> is_empty
- FrsCurve: nSegments --> segments_size
- TVertex: mate() --> get_mate()
- ViewEdge: fedge{A,B} --> {first,last}_fedge
- ViewEdge: setaShape, aShape --> occlude
- ViewEdge: {A,B} --> {first,last}_viewvertex
- ViewMap: getScene3dBBox --> scene_bbox
2013-02-14 23:48:34 +00:00
|
|
|
length = stroke.length_2d
|
2014-07-24 02:08:04 +00:00
|
|
|
for svert in stroke:
|
|
|
|
nres = self.noise.turbulence1(length * svert.u, self.freq, self.amp, self.oct)
|
|
|
|
svert.point += nres * self.dir
|
Freestyle Python API improvements - part 3.
Major API updates were made to address code review comments.
This revision mostly focuses on Python wrappers of C++ 0D and 1D elements (i.e.,
Interface0D and Interface1D, as well as their subclasses).
* Most getter/setter methods were reimplemented as attributes using PyGetSetDef.
Vector attributes are now implemented based on mathutils callbacks. Boolean
attributes now only accept boolean values.
* The __getitem__ method was removed and the Sequence protocol was used instead.
* The naming of methods and attributes was fixed to follow the naming conventions
of the Blender Python API (i.e., lower case + underscores for methods and attributes,
and CamelCase for classes). Some naming inconsistency within the Freestyle Python
API was also addressed.
* The Freestyle API had a number of method names including prefix/suffix "A" and
"B", and their meanings were inconsistent (i.e., referring to different things
depending on the classes). The names with these two letters were replaced with
more straightforward names. Also some attribute names were changed so as to indicate
the type of the value (e.g., FEdge.next_fedge instead of FEdge.next_edge) in line
with other names explicitly indicating what the value is (e.g., SVertex.viewvertex).
* In addition, some code clean-up was done in both C++ and Python.
Notes:
In summary, the following irregular naming changes were made through this revision
(those resulting from regular changes of naming conventions are not listed):
- CurvePoint: {A,B} --> {first,second}_svertex
- FEdge: vertex{A,B} --> {first,second}_svertex
- FEdge: {next,previous}Edge --> {next,previous}_fedge
- FEdgeSharp: normal{A,B} --> normal_{right,left}
- FEdgeSharp: {a,b}FaceMark --> face_mark_{right,left}
- FEdgeSharp: {a,b}Material --> material_{right,left}
- FEdgeSharp: {a,b}MaterialIndex --> material_index_{right,left}
- FrsCurve: empty --> is_empty
- FrsCurve: nSegments --> segments_size
- TVertex: mate() --> get_mate()
- ViewEdge: fedge{A,B} --> {first,last}_fedge
- ViewEdge: setaShape, aShape --> occlude
- ViewEdge: {A,B} --> {first,last}_viewvertex
- ViewMap: getScene3dBBox --> scene_bbox
2013-02-14 23:48:34 +00:00
|
|
|
stroke.update_length()
|
2011-08-30 00:29:12 +00:00
|
|
|
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2011-08-30 00:29:12 +00:00
|
|
|
class PerlinNoise2DShader(StrokeShader):
|
2014-07-24 02:08:04 +00:00
|
|
|
"""
|
|
|
|
Displaces the stroke using the strokes coordinates. This means
|
2014-07-24 02:44:31 +00:00
|
|
|
that in a scene no strokes will be distorded identically.
|
2014-07-24 02:08:04 +00:00
|
|
|
|
2014-07-24 02:44:31 +00:00
|
|
|
More information on the noise shaders can be found at:
|
2014-07-24 02:08:04 +00:00
|
|
|
freestyleintegration.wordpress.com/2011/09/25/development-updates-on-september-25/
|
|
|
|
"""
|
|
|
|
def __init__(self, freq=10, amp=10, oct=4, angle=radians(45), seed=-1):
|
2011-08-30 00:29:12 +00:00
|
|
|
StrokeShader.__init__(self)
|
2014-07-24 02:08:04 +00:00
|
|
|
self.noise = Noise(seed)
|
|
|
|
self.freq = freq
|
|
|
|
self.amp = amp
|
|
|
|
self.oct = oct
|
|
|
|
self.dir = Vector((cos(angle), sin(angle)))
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2011-08-30 00:29:12 +00:00
|
|
|
def shade(self, stroke):
|
2014-07-24 02:08:04 +00:00
|
|
|
for svert in stroke:
|
|
|
|
projected = Vector((svert.projected_x, svert.projected_y))
|
|
|
|
nres = self.noise.turbulence2(projected, self.freq, self.amp, self.oct)
|
|
|
|
svert.point += nres * self.dir
|
Freestyle Python API improvements - part 3.
Major API updates were made to address code review comments.
This revision mostly focuses on Python wrappers of C++ 0D and 1D elements (i.e.,
Interface0D and Interface1D, as well as their subclasses).
* Most getter/setter methods were reimplemented as attributes using PyGetSetDef.
Vector attributes are now implemented based on mathutils callbacks. Boolean
attributes now only accept boolean values.
* The __getitem__ method was removed and the Sequence protocol was used instead.
* The naming of methods and attributes was fixed to follow the naming conventions
of the Blender Python API (i.e., lower case + underscores for methods and attributes,
and CamelCase for classes). Some naming inconsistency within the Freestyle Python
API was also addressed.
* The Freestyle API had a number of method names including prefix/suffix "A" and
"B", and their meanings were inconsistent (i.e., referring to different things
depending on the classes). The names with these two letters were replaced with
more straightforward names. Also some attribute names were changed so as to indicate
the type of the value (e.g., FEdge.next_fedge instead of FEdge.next_edge) in line
with other names explicitly indicating what the value is (e.g., SVertex.viewvertex).
* In addition, some code clean-up was done in both C++ and Python.
Notes:
In summary, the following irregular naming changes were made through this revision
(those resulting from regular changes of naming conventions are not listed):
- CurvePoint: {A,B} --> {first,second}_svertex
- FEdge: vertex{A,B} --> {first,second}_svertex
- FEdge: {next,previous}Edge --> {next,previous}_fedge
- FEdgeSharp: normal{A,B} --> normal_{right,left}
- FEdgeSharp: {a,b}FaceMark --> face_mark_{right,left}
- FEdgeSharp: {a,b}Material --> material_{right,left}
- FEdgeSharp: {a,b}MaterialIndex --> material_index_{right,left}
- FrsCurve: empty --> is_empty
- FrsCurve: nSegments --> segments_size
- TVertex: mate() --> get_mate()
- ViewEdge: fedge{A,B} --> {first,last}_fedge
- ViewEdge: setaShape, aShape --> occlude
- ViewEdge: {A,B} --> {first,last}_viewvertex
- ViewMap: getScene3dBBox --> scene_bbox
2013-02-14 23:48:34 +00:00
|
|
|
stroke.update_length()
|
2011-08-30 00:29:12 +00:00
|
|
|
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2012-01-04 00:23:34 +00:00
|
|
|
class Offset2DShader(StrokeShader):
|
2014-07-24 02:44:31 +00:00
|
|
|
"""Offsets the stroke by a given amount."""
|
2012-01-04 00:23:34 +00:00
|
|
|
def __init__(self, start, end, x, y):
|
|
|
|
StrokeShader.__init__(self)
|
2014-07-24 02:08:04 +00:00
|
|
|
self.start = start
|
|
|
|
self.end = end
|
|
|
|
self.xy = Vector((x, y))
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2012-01-04 00:23:34 +00:00
|
|
|
def shade(self, stroke):
|
2014-07-24 02:08:04 +00:00
|
|
|
# normals are stored in a tuple, so they don't update when we reposition vertices.
|
|
|
|
normals = tuple(stroke_normal(stroke))
|
|
|
|
for svert, normal in zip(stroke, normals):
|
|
|
|
a = self.start + svert.u * (self.end - self.start)
|
|
|
|
svert.point += (normal * a) + self.xy
|
Freestyle Python API improvements - part 3.
Major API updates were made to address code review comments.
This revision mostly focuses on Python wrappers of C++ 0D and 1D elements (i.e.,
Interface0D and Interface1D, as well as their subclasses).
* Most getter/setter methods were reimplemented as attributes using PyGetSetDef.
Vector attributes are now implemented based on mathutils callbacks. Boolean
attributes now only accept boolean values.
* The __getitem__ method was removed and the Sequence protocol was used instead.
* The naming of methods and attributes was fixed to follow the naming conventions
of the Blender Python API (i.e., lower case + underscores for methods and attributes,
and CamelCase for classes). Some naming inconsistency within the Freestyle Python
API was also addressed.
* The Freestyle API had a number of method names including prefix/suffix "A" and
"B", and their meanings were inconsistent (i.e., referring to different things
depending on the classes). The names with these two letters were replaced with
more straightforward names. Also some attribute names were changed so as to indicate
the type of the value (e.g., FEdge.next_fedge instead of FEdge.next_edge) in line
with other names explicitly indicating what the value is (e.g., SVertex.viewvertex).
* In addition, some code clean-up was done in both C++ and Python.
Notes:
In summary, the following irregular naming changes were made through this revision
(those resulting from regular changes of naming conventions are not listed):
- CurvePoint: {A,B} --> {first,second}_svertex
- FEdge: vertex{A,B} --> {first,second}_svertex
- FEdge: {next,previous}Edge --> {next,previous}_fedge
- FEdgeSharp: normal{A,B} --> normal_{right,left}
- FEdgeSharp: {a,b}FaceMark --> face_mark_{right,left}
- FEdgeSharp: {a,b}Material --> material_{right,left}
- FEdgeSharp: {a,b}MaterialIndex --> material_index_{right,left}
- FrsCurve: empty --> is_empty
- FrsCurve: nSegments --> segments_size
- TVertex: mate() --> get_mate()
- ViewEdge: fedge{A,B} --> {first,last}_fedge
- ViewEdge: setaShape, aShape --> occlude
- ViewEdge: {A,B} --> {first,last}_viewvertex
- ViewMap: getScene3dBBox --> scene_bbox
2013-02-14 23:48:34 +00:00
|
|
|
stroke.update_length()
|
2012-01-04 00:23:34 +00:00
|
|
|
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2012-01-04 00:23:34 +00:00
|
|
|
class Transform2DShader(StrokeShader):
|
2014-07-24 02:08:04 +00:00
|
|
|
"""Transforms the stroke (scale, rotation, location) around a given pivot point """
|
2012-01-04 00:23:34 +00:00
|
|
|
def __init__(self, pivot, scale_x, scale_y, angle, pivot_u, pivot_x, pivot_y):
|
|
|
|
StrokeShader.__init__(self)
|
2014-07-24 02:08:04 +00:00
|
|
|
self.pivot = pivot
|
|
|
|
self.scale = Vector((scale_x, scale_y))
|
|
|
|
self.cos_theta = cos(angle)
|
|
|
|
self.sin_theta = sin(angle)
|
|
|
|
self.pivot_u = pivot_u
|
|
|
|
self.pivot_x = pivot_x
|
|
|
|
self.pivot_y = pivot_y
|
|
|
|
if pivot not in {'START', 'END', 'CENTER', 'ABSOLUTE', 'PARAM'}:
|
|
|
|
raise ValueError("expected pivot in {'START', 'END', 'CENTER', 'ABSOLUTE', 'PARAM'}, not" + pivot)
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2012-01-04 00:23:34 +00:00
|
|
|
def shade(self, stroke):
|
|
|
|
# determine the pivot of scaling and rotation operations
|
2014-07-24 02:08:04 +00:00
|
|
|
if self.pivot == 'START':
|
|
|
|
pivot = stroke[0].point
|
|
|
|
elif self.pivot == 'END':
|
|
|
|
pivot = stroke[-1].point
|
|
|
|
elif self.pivot == 'CENTER':
|
|
|
|
# minor rounding errors here, because
|
|
|
|
# given v = Vector(a, b), then (v / n) != Vector(v.x / n, v.y / n)
|
|
|
|
pivot = (1 / len(stroke)) * sum((svert.point for svert in stroke), Vector((0.0, 0.0)))
|
|
|
|
elif self.pivot == 'ABSOLUTE':
|
|
|
|
pivot = Vector((self.pivot_x, self.pivot_y))
|
|
|
|
elif self.pivot == 'PARAM':
|
|
|
|
if self.pivot_u < stroke[0].u:
|
|
|
|
pivot = stroke[0].point
|
2012-01-04 00:23:34 +00:00
|
|
|
else:
|
2014-07-24 02:08:04 +00:00
|
|
|
for prev, svert in pairwise(stroke):
|
|
|
|
if self.pivot_u < svert.u:
|
|
|
|
break
|
|
|
|
pivot = svert.point + (svert.u - self.pivot_u) * (prev.point - svert.point)
|
|
|
|
|
2012-01-04 00:23:34 +00:00
|
|
|
# apply scaling and rotation operations
|
2014-07-24 02:08:04 +00:00
|
|
|
for svert in stroke:
|
|
|
|
p = (svert.point - pivot)
|
|
|
|
x = p.x * self.scale.x
|
|
|
|
y = p.y * self.scale.y
|
|
|
|
p.x = x * self.cos_theta - y * self.sin_theta
|
|
|
|
p.y = x * self.sin_theta + y * self.cos_theta
|
|
|
|
svert.point = p + pivot
|
Freestyle Python API improvements - part 3.
Major API updates were made to address code review comments.
This revision mostly focuses on Python wrappers of C++ 0D and 1D elements (i.e.,
Interface0D and Interface1D, as well as their subclasses).
* Most getter/setter methods were reimplemented as attributes using PyGetSetDef.
Vector attributes are now implemented based on mathutils callbacks. Boolean
attributes now only accept boolean values.
* The __getitem__ method was removed and the Sequence protocol was used instead.
* The naming of methods and attributes was fixed to follow the naming conventions
of the Blender Python API (i.e., lower case + underscores for methods and attributes,
and CamelCase for classes). Some naming inconsistency within the Freestyle Python
API was also addressed.
* The Freestyle API had a number of method names including prefix/suffix "A" and
"B", and their meanings were inconsistent (i.e., referring to different things
depending on the classes). The names with these two letters were replaced with
more straightforward names. Also some attribute names were changed so as to indicate
the type of the value (e.g., FEdge.next_fedge instead of FEdge.next_edge) in line
with other names explicitly indicating what the value is (e.g., SVertex.viewvertex).
* In addition, some code clean-up was done in both C++ and Python.
Notes:
In summary, the following irregular naming changes were made through this revision
(those resulting from regular changes of naming conventions are not listed):
- CurvePoint: {A,B} --> {first,second}_svertex
- FEdge: vertex{A,B} --> {first,second}_svertex
- FEdge: {next,previous}Edge --> {next,previous}_fedge
- FEdgeSharp: normal{A,B} --> normal_{right,left}
- FEdgeSharp: {a,b}FaceMark --> face_mark_{right,left}
- FEdgeSharp: {a,b}Material --> material_{right,left}
- FEdgeSharp: {a,b}MaterialIndex --> material_index_{right,left}
- FrsCurve: empty --> is_empty
- FrsCurve: nSegments --> segments_size
- TVertex: mate() --> get_mate()
- ViewEdge: fedge{A,B} --> {first,last}_fedge
- ViewEdge: setaShape, aShape --> occlude
- ViewEdge: {A,B} --> {first,last}_viewvertex
- ViewMap: getScene3dBBox --> scene_bbox
2013-02-14 23:48:34 +00:00
|
|
|
stroke.update_length()
|
2012-01-04 00:23:34 +00:00
|
|
|
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2010-08-01 16:02:34 +00:00
|
|
|
# Predicates and helper functions
|
2010-07-28 00:43:45 +00:00
|
|
|
|
|
|
|
class QuantitativeInvisibilityRangeUP1D(UnaryPredicate1D):
|
|
|
|
def __init__(self, qi_start, qi_end):
|
|
|
|
UnaryPredicate1D.__init__(self)
|
2014-07-24 02:08:04 +00:00
|
|
|
self.getQI = QuantitativeInvisibilityF1D()
|
|
|
|
self.qi_start = qi_start
|
|
|
|
self.qi_end = qi_end
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2010-07-28 00:43:45 +00:00
|
|
|
def __call__(self, inter):
|
2014-07-24 02:08:04 +00:00
|
|
|
qi = self.getQI(inter)
|
|
|
|
return self.qi_start <= qi <= self.qi_end
|
2010-07-28 00:43:45 +00:00
|
|
|
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2010-12-02 23:50:10 +00:00
|
|
|
class ObjectNamesUP1D(UnaryPredicate1D):
|
|
|
|
def __init__(self, names, negative):
|
|
|
|
UnaryPredicate1D.__init__(self)
|
2014-07-24 02:08:04 +00:00
|
|
|
self.names = names
|
|
|
|
self.negative = negative
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2010-12-02 23:50:10 +00:00
|
|
|
def __call__(self, viewEdge):
|
2014-07-24 02:08:04 +00:00
|
|
|
found = viewEdge.viewshape.name in self.names
|
|
|
|
if self.negative:
|
2010-12-02 23:50:10 +00:00
|
|
|
return not found
|
|
|
|
return found
|
|
|
|
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2014-07-24 02:08:04 +00:00
|
|
|
# -- Split by dashed line pattern -- #
|
2010-10-23 20:42:26 +00:00
|
|
|
|
2012-11-04 23:52:26 +00:00
|
|
|
class SplitPatternStartingUP0D(UnaryPredicate0D):
|
2010-10-23 20:42:26 +00:00
|
|
|
def __init__(self, controller):
|
|
|
|
UnaryPredicate0D.__init__(self)
|
2014-07-24 02:08:04 +00:00
|
|
|
self.controller = controller
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2010-10-23 20:42:26 +00:00
|
|
|
def __call__(self, inter):
|
2014-07-24 02:08:04 +00:00
|
|
|
return self.controller.start()
|
2010-10-23 20:42:26 +00:00
|
|
|
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2012-11-04 23:52:26 +00:00
|
|
|
class SplitPatternStoppingUP0D(UnaryPredicate0D):
|
2010-10-23 20:42:26 +00:00
|
|
|
def __init__(self, controller):
|
|
|
|
UnaryPredicate0D.__init__(self)
|
2014-07-24 02:08:04 +00:00
|
|
|
self.controller = controller
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2010-10-23 20:42:26 +00:00
|
|
|
def __call__(self, inter):
|
2014-07-24 02:08:04 +00:00
|
|
|
return self.controller.stop()
|
2010-10-23 20:42:26 +00:00
|
|
|
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2012-11-04 23:52:26 +00:00
|
|
|
class SplitPatternController:
|
2010-10-23 20:42:26 +00:00
|
|
|
def __init__(self, pattern, sampling):
|
|
|
|
self.sampling = float(sampling)
|
|
|
|
k = len(pattern) // 2
|
|
|
|
n = k * 2
|
2013-08-21 23:19:01 +00:00
|
|
|
self.start_pos = [pattern[i] + pattern[i + 1] for i in range(0, n, 2)]
|
2010-10-23 20:42:26 +00:00
|
|
|
self.stop_pos = [pattern[i] for i in range(0, n, 2)]
|
|
|
|
self.init()
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2010-10-23 20:42:26 +00:00
|
|
|
def init(self):
|
|
|
|
self.start_len = 0.0
|
|
|
|
self.start_idx = 0
|
|
|
|
self.stop_len = self.sampling
|
|
|
|
self.stop_idx = 0
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2010-10-23 20:42:26 +00:00
|
|
|
def start(self):
|
|
|
|
self.start_len += self.sampling
|
|
|
|
if abs(self.start_len - self.start_pos[self.start_idx]) < self.sampling / 2.0:
|
|
|
|
self.start_len = 0.0
|
|
|
|
self.start_idx = (self.start_idx + 1) % len(self.start_pos)
|
|
|
|
return True
|
|
|
|
return False
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2010-10-23 20:42:26 +00:00
|
|
|
def stop(self):
|
|
|
|
if self.start_len > 0.0:
|
|
|
|
self.init()
|
|
|
|
self.stop_len += self.sampling
|
|
|
|
if abs(self.stop_len - self.stop_pos[self.stop_idx]) < self.sampling / 2.0:
|
|
|
|
self.stop_len = self.sampling
|
|
|
|
self.stop_idx = (self.stop_idx + 1) % len(self.stop_pos)
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2012-11-04 23:52:26 +00:00
|
|
|
# Dashed line
|
|
|
|
|
|
|
|
class DashedLineShader(StrokeShader):
|
|
|
|
def __init__(self, pattern):
|
|
|
|
StrokeShader.__init__(self)
|
2014-07-24 02:08:04 +00:00
|
|
|
self.pattern = pattern
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2012-11-04 23:52:26 +00:00
|
|
|
def shade(self, stroke):
|
2013-08-21 23:19:01 +00:00
|
|
|
start = 0.0 # 2D curvilinear length
|
2012-11-04 23:52:26 +00:00
|
|
|
visible = True
|
2014-07-24 02:43:16 +00:00
|
|
|
# The extra 'sampling' term is added below, because the
|
|
|
|
# visibility attribute of the i-th vertex refers to the
|
|
|
|
# visibility of the stroke segment between the i-th and
|
|
|
|
# (i+1)-th vertices.
|
2012-11-04 23:52:26 +00:00
|
|
|
sampling = 1.0
|
Freestyle Python API improvements - part 3.
Major API updates were made to address code review comments.
This revision mostly focuses on Python wrappers of C++ 0D and 1D elements (i.e.,
Interface0D and Interface1D, as well as their subclasses).
* Most getter/setter methods were reimplemented as attributes using PyGetSetDef.
Vector attributes are now implemented based on mathutils callbacks. Boolean
attributes now only accept boolean values.
* The __getitem__ method was removed and the Sequence protocol was used instead.
* The naming of methods and attributes was fixed to follow the naming conventions
of the Blender Python API (i.e., lower case + underscores for methods and attributes,
and CamelCase for classes). Some naming inconsistency within the Freestyle Python
API was also addressed.
* The Freestyle API had a number of method names including prefix/suffix "A" and
"B", and their meanings were inconsistent (i.e., referring to different things
depending on the classes). The names with these two letters were replaced with
more straightforward names. Also some attribute names were changed so as to indicate
the type of the value (e.g., FEdge.next_fedge instead of FEdge.next_edge) in line
with other names explicitly indicating what the value is (e.g., SVertex.viewvertex).
* In addition, some code clean-up was done in both C++ and Python.
Notes:
In summary, the following irregular naming changes were made through this revision
(those resulting from regular changes of naming conventions are not listed):
- CurvePoint: {A,B} --> {first,second}_svertex
- FEdge: vertex{A,B} --> {first,second}_svertex
- FEdge: {next,previous}Edge --> {next,previous}_fedge
- FEdgeSharp: normal{A,B} --> normal_{right,left}
- FEdgeSharp: {a,b}FaceMark --> face_mark_{right,left}
- FEdgeSharp: {a,b}Material --> material_{right,left}
- FEdgeSharp: {a,b}MaterialIndex --> material_index_{right,left}
- FrsCurve: empty --> is_empty
- FrsCurve: nSegments --> segments_size
- TVertex: mate() --> get_mate()
- ViewEdge: fedge{A,B} --> {first,last}_fedge
- ViewEdge: setaShape, aShape --> occlude
- ViewEdge: {A,B} --> {first,last}_viewvertex
- ViewMap: getScene3dBBox --> scene_bbox
2013-02-14 23:48:34 +00:00
|
|
|
it = stroke.stroke_vertices_begin(sampling)
|
2014-07-24 02:08:04 +00:00
|
|
|
pattern_cycle = cycle(self.pattern)
|
|
|
|
pattern = next(pattern_cycle)
|
|
|
|
for svert in it:
|
2013-08-21 23:19:01 +00:00
|
|
|
pos = it.t # curvilinear abscissa
|
2014-07-24 02:08:04 +00:00
|
|
|
|
|
|
|
if pos - start + sampling > pattern:
|
2012-11-04 23:52:26 +00:00
|
|
|
start = pos
|
2014-07-24 02:08:04 +00:00
|
|
|
pattern = next(pattern_cycle)
|
2012-11-04 23:52:26 +00:00
|
|
|
visible = not visible
|
2014-07-24 02:08:04 +00:00
|
|
|
|
2014-05-09 11:47:05 +00:00
|
|
|
if not visible:
|
2014-07-24 02:08:04 +00:00
|
|
|
it.object.attribute.visible = False
|
2012-11-04 23:52:26 +00:00
|
|
|
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2011-01-31 20:57:39 +00:00
|
|
|
# predicates for chaining
|
|
|
|
|
|
|
|
class AngleLargerThanBP1D(BinaryPredicate1D):
|
|
|
|
def __init__(self, angle):
|
|
|
|
BinaryPredicate1D.__init__(self)
|
2014-07-24 02:08:04 +00:00
|
|
|
self.angle = angle
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2011-01-31 20:57:39 +00:00
|
|
|
def __call__(self, i1, i2):
|
Freestyle Python API improvements - part 3.
Major API updates were made to address code review comments.
This revision mostly focuses on Python wrappers of C++ 0D and 1D elements (i.e.,
Interface0D and Interface1D, as well as their subclasses).
* Most getter/setter methods were reimplemented as attributes using PyGetSetDef.
Vector attributes are now implemented based on mathutils callbacks. Boolean
attributes now only accept boolean values.
* The __getitem__ method was removed and the Sequence protocol was used instead.
* The naming of methods and attributes was fixed to follow the naming conventions
of the Blender Python API (i.e., lower case + underscores for methods and attributes,
and CamelCase for classes). Some naming inconsistency within the Freestyle Python
API was also addressed.
* The Freestyle API had a number of method names including prefix/suffix "A" and
"B", and their meanings were inconsistent (i.e., referring to different things
depending on the classes). The names with these two letters were replaced with
more straightforward names. Also some attribute names were changed so as to indicate
the type of the value (e.g., FEdge.next_fedge instead of FEdge.next_edge) in line
with other names explicitly indicating what the value is (e.g., SVertex.viewvertex).
* In addition, some code clean-up was done in both C++ and Python.
Notes:
In summary, the following irregular naming changes were made through this revision
(those resulting from regular changes of naming conventions are not listed):
- CurvePoint: {A,B} --> {first,second}_svertex
- FEdge: vertex{A,B} --> {first,second}_svertex
- FEdge: {next,previous}Edge --> {next,previous}_fedge
- FEdgeSharp: normal{A,B} --> normal_{right,left}
- FEdgeSharp: {a,b}FaceMark --> face_mark_{right,left}
- FEdgeSharp: {a,b}Material --> material_{right,left}
- FEdgeSharp: {a,b}MaterialIndex --> material_index_{right,left}
- FrsCurve: empty --> is_empty
- FrsCurve: nSegments --> segments_size
- TVertex: mate() --> get_mate()
- ViewEdge: fedge{A,B} --> {first,last}_fedge
- ViewEdge: setaShape, aShape --> occlude
- ViewEdge: {A,B} --> {first,last}_viewvertex
- ViewMap: getScene3dBBox --> scene_bbox
2013-02-14 23:48:34 +00:00
|
|
|
sv1a = i1.first_fedge.first_svertex.point_2d
|
|
|
|
sv1b = i1.last_fedge.second_svertex.point_2d
|
|
|
|
sv2a = i2.first_fedge.first_svertex.point_2d
|
|
|
|
sv2b = i2.last_fedge.second_svertex.point_2d
|
2011-01-31 20:57:39 +00:00
|
|
|
if (sv1a - sv2a).length < 1e-6:
|
|
|
|
dir1 = sv1a - sv1b
|
|
|
|
dir2 = sv2b - sv2a
|
|
|
|
elif (sv1b - sv2b).length < 1e-6:
|
|
|
|
dir1 = sv1b - sv1a
|
|
|
|
dir2 = sv2a - sv2b
|
|
|
|
elif (sv1a - sv2b).length < 1e-6:
|
|
|
|
dir1 = sv1a - sv1b
|
|
|
|
dir2 = sv2a - sv2b
|
|
|
|
elif (sv1b - sv2a).length < 1e-6:
|
|
|
|
dir1 = sv1b - sv1a
|
|
|
|
dir2 = sv2b - sv2a
|
|
|
|
else:
|
|
|
|
return False
|
|
|
|
denom = dir1.length * dir2.length
|
|
|
|
if denom < 1e-6:
|
|
|
|
return False
|
|
|
|
x = (dir1 * dir2) / denom
|
2014-07-24 02:08:04 +00:00
|
|
|
return acos(bound(-1.0, x, 1.0)) > self.angle
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2011-09-18 22:59:51 +00:00
|
|
|
# predicates for selection
|
|
|
|
|
2014-07-24 02:08:04 +00:00
|
|
|
|
2011-09-18 22:59:51 +00:00
|
|
|
class LengthThresholdUP1D(UnaryPredicate1D):
|
2013-04-23 07:06:29 +00:00
|
|
|
def __init__(self, length_min=None, length_max=None):
|
2011-09-18 22:59:51 +00:00
|
|
|
UnaryPredicate1D.__init__(self)
|
2014-07-24 02:08:04 +00:00
|
|
|
self.length_min = length_min
|
|
|
|
self.length_max = length_max
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2011-09-18 22:59:51 +00:00
|
|
|
def __call__(self, inter):
|
Freestyle Python API improvements - part 3.
Major API updates were made to address code review comments.
This revision mostly focuses on Python wrappers of C++ 0D and 1D elements (i.e.,
Interface0D and Interface1D, as well as their subclasses).
* Most getter/setter methods were reimplemented as attributes using PyGetSetDef.
Vector attributes are now implemented based on mathutils callbacks. Boolean
attributes now only accept boolean values.
* The __getitem__ method was removed and the Sequence protocol was used instead.
* The naming of methods and attributes was fixed to follow the naming conventions
of the Blender Python API (i.e., lower case + underscores for methods and attributes,
and CamelCase for classes). Some naming inconsistency within the Freestyle Python
API was also addressed.
* The Freestyle API had a number of method names including prefix/suffix "A" and
"B", and their meanings were inconsistent (i.e., referring to different things
depending on the classes). The names with these two letters were replaced with
more straightforward names. Also some attribute names were changed so as to indicate
the type of the value (e.g., FEdge.next_fedge instead of FEdge.next_edge) in line
with other names explicitly indicating what the value is (e.g., SVertex.viewvertex).
* In addition, some code clean-up was done in both C++ and Python.
Notes:
In summary, the following irregular naming changes were made through this revision
(those resulting from regular changes of naming conventions are not listed):
- CurvePoint: {A,B} --> {first,second}_svertex
- FEdge: vertex{A,B} --> {first,second}_svertex
- FEdge: {next,previous}Edge --> {next,previous}_fedge
- FEdgeSharp: normal{A,B} --> normal_{right,left}
- FEdgeSharp: {a,b}FaceMark --> face_mark_{right,left}
- FEdgeSharp: {a,b}Material --> material_{right,left}
- FEdgeSharp: {a,b}MaterialIndex --> material_index_{right,left}
- FrsCurve: empty --> is_empty
- FrsCurve: nSegments --> segments_size
- TVertex: mate() --> get_mate()
- ViewEdge: fedge{A,B} --> {first,last}_fedge
- ViewEdge: setaShape, aShape --> occlude
- ViewEdge: {A,B} --> {first,last}_viewvertex
- ViewMap: getScene3dBBox --> scene_bbox
2013-02-14 23:48:34 +00:00
|
|
|
length = inter.length_2d
|
2014-07-24 02:08:04 +00:00
|
|
|
if self.length_min is not None and length < self.length_min:
|
2011-09-18 22:59:51 +00:00
|
|
|
return False
|
2014-07-24 02:08:04 +00:00
|
|
|
if self.length_max is not None and length > self.length_max:
|
2011-09-18 22:59:51 +00:00
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2011-10-06 02:04:43 +00:00
|
|
|
class FaceMarkBothUP1D(UnaryPredicate1D):
|
2014-07-24 02:08:04 +00:00
|
|
|
def __call__(self, inter: ViewEdge):
|
Freestyle Python API improvements - part 3.
Major API updates were made to address code review comments.
This revision mostly focuses on Python wrappers of C++ 0D and 1D elements (i.e.,
Interface0D and Interface1D, as well as their subclasses).
* Most getter/setter methods were reimplemented as attributes using PyGetSetDef.
Vector attributes are now implemented based on mathutils callbacks. Boolean
attributes now only accept boolean values.
* The __getitem__ method was removed and the Sequence protocol was used instead.
* The naming of methods and attributes was fixed to follow the naming conventions
of the Blender Python API (i.e., lower case + underscores for methods and attributes,
and CamelCase for classes). Some naming inconsistency within the Freestyle Python
API was also addressed.
* The Freestyle API had a number of method names including prefix/suffix "A" and
"B", and their meanings were inconsistent (i.e., referring to different things
depending on the classes). The names with these two letters were replaced with
more straightforward names. Also some attribute names were changed so as to indicate
the type of the value (e.g., FEdge.next_fedge instead of FEdge.next_edge) in line
with other names explicitly indicating what the value is (e.g., SVertex.viewvertex).
* In addition, some code clean-up was done in both C++ and Python.
Notes:
In summary, the following irregular naming changes were made through this revision
(those resulting from regular changes of naming conventions are not listed):
- CurvePoint: {A,B} --> {first,second}_svertex
- FEdge: vertex{A,B} --> {first,second}_svertex
- FEdge: {next,previous}Edge --> {next,previous}_fedge
- FEdgeSharp: normal{A,B} --> normal_{right,left}
- FEdgeSharp: {a,b}FaceMark --> face_mark_{right,left}
- FEdgeSharp: {a,b}Material --> material_{right,left}
- FEdgeSharp: {a,b}MaterialIndex --> material_index_{right,left}
- FrsCurve: empty --> is_empty
- FrsCurve: nSegments --> segments_size
- TVertex: mate() --> get_mate()
- ViewEdge: fedge{A,B} --> {first,last}_fedge
- ViewEdge: setaShape, aShape --> occlude
- ViewEdge: {A,B} --> {first,last}_viewvertex
- ViewMap: getScene3dBBox --> scene_bbox
2013-02-14 23:48:34 +00:00
|
|
|
fe = inter.first_fedge
|
2011-10-06 02:04:43 +00:00
|
|
|
while fe is not None:
|
Freestyle Python API improvements - part 3.
Major API updates were made to address code review comments.
This revision mostly focuses on Python wrappers of C++ 0D and 1D elements (i.e.,
Interface0D and Interface1D, as well as their subclasses).
* Most getter/setter methods were reimplemented as attributes using PyGetSetDef.
Vector attributes are now implemented based on mathutils callbacks. Boolean
attributes now only accept boolean values.
* The __getitem__ method was removed and the Sequence protocol was used instead.
* The naming of methods and attributes was fixed to follow the naming conventions
of the Blender Python API (i.e., lower case + underscores for methods and attributes,
and CamelCase for classes). Some naming inconsistency within the Freestyle Python
API was also addressed.
* The Freestyle API had a number of method names including prefix/suffix "A" and
"B", and their meanings were inconsistent (i.e., referring to different things
depending on the classes). The names with these two letters were replaced with
more straightforward names. Also some attribute names were changed so as to indicate
the type of the value (e.g., FEdge.next_fedge instead of FEdge.next_edge) in line
with other names explicitly indicating what the value is (e.g., SVertex.viewvertex).
* In addition, some code clean-up was done in both C++ and Python.
Notes:
In summary, the following irregular naming changes were made through this revision
(those resulting from regular changes of naming conventions are not listed):
- CurvePoint: {A,B} --> {first,second}_svertex
- FEdge: vertex{A,B} --> {first,second}_svertex
- FEdge: {next,previous}Edge --> {next,previous}_fedge
- FEdgeSharp: normal{A,B} --> normal_{right,left}
- FEdgeSharp: {a,b}FaceMark --> face_mark_{right,left}
- FEdgeSharp: {a,b}Material --> material_{right,left}
- FEdgeSharp: {a,b}MaterialIndex --> material_index_{right,left}
- FrsCurve: empty --> is_empty
- FrsCurve: nSegments --> segments_size
- TVertex: mate() --> get_mate()
- ViewEdge: fedge{A,B} --> {first,last}_fedge
- ViewEdge: setaShape, aShape --> occlude
- ViewEdge: {A,B} --> {first,last}_viewvertex
- ViewMap: getScene3dBBox --> scene_bbox
2013-02-14 23:48:34 +00:00
|
|
|
if fe.is_smooth:
|
|
|
|
if fe.face_mark:
|
2011-10-06 02:04:43 +00:00
|
|
|
return True
|
2013-06-16 00:15:05 +00:00
|
|
|
elif (fe.nature & Nature.BORDER):
|
|
|
|
if fe.face_mark_left:
|
|
|
|
return True
|
2011-10-06 02:04:43 +00:00
|
|
|
else:
|
Freestyle Python API improvements - part 3.
Major API updates were made to address code review comments.
This revision mostly focuses on Python wrappers of C++ 0D and 1D elements (i.e.,
Interface0D and Interface1D, as well as their subclasses).
* Most getter/setter methods were reimplemented as attributes using PyGetSetDef.
Vector attributes are now implemented based on mathutils callbacks. Boolean
attributes now only accept boolean values.
* The __getitem__ method was removed and the Sequence protocol was used instead.
* The naming of methods and attributes was fixed to follow the naming conventions
of the Blender Python API (i.e., lower case + underscores for methods and attributes,
and CamelCase for classes). Some naming inconsistency within the Freestyle Python
API was also addressed.
* The Freestyle API had a number of method names including prefix/suffix "A" and
"B", and their meanings were inconsistent (i.e., referring to different things
depending on the classes). The names with these two letters were replaced with
more straightforward names. Also some attribute names were changed so as to indicate
the type of the value (e.g., FEdge.next_fedge instead of FEdge.next_edge) in line
with other names explicitly indicating what the value is (e.g., SVertex.viewvertex).
* In addition, some code clean-up was done in both C++ and Python.
Notes:
In summary, the following irregular naming changes were made through this revision
(those resulting from regular changes of naming conventions are not listed):
- CurvePoint: {A,B} --> {first,second}_svertex
- FEdge: vertex{A,B} --> {first,second}_svertex
- FEdge: {next,previous}Edge --> {next,previous}_fedge
- FEdgeSharp: normal{A,B} --> normal_{right,left}
- FEdgeSharp: {a,b}FaceMark --> face_mark_{right,left}
- FEdgeSharp: {a,b}Material --> material_{right,left}
- FEdgeSharp: {a,b}MaterialIndex --> material_index_{right,left}
- FrsCurve: empty --> is_empty
- FrsCurve: nSegments --> segments_size
- TVertex: mate() --> get_mate()
- ViewEdge: fedge{A,B} --> {first,last}_fedge
- ViewEdge: setaShape, aShape --> occlude
- ViewEdge: {A,B} --> {first,last}_viewvertex
- ViewMap: getScene3dBBox --> scene_bbox
2013-02-14 23:48:34 +00:00
|
|
|
if fe.face_mark_right and fe.face_mark_left:
|
2011-10-06 02:04:43 +00:00
|
|
|
return True
|
Freestyle Python API improvements - part 3.
Major API updates were made to address code review comments.
This revision mostly focuses on Python wrappers of C++ 0D and 1D elements (i.e.,
Interface0D and Interface1D, as well as their subclasses).
* Most getter/setter methods were reimplemented as attributes using PyGetSetDef.
Vector attributes are now implemented based on mathutils callbacks. Boolean
attributes now only accept boolean values.
* The __getitem__ method was removed and the Sequence protocol was used instead.
* The naming of methods and attributes was fixed to follow the naming conventions
of the Blender Python API (i.e., lower case + underscores for methods and attributes,
and CamelCase for classes). Some naming inconsistency within the Freestyle Python
API was also addressed.
* The Freestyle API had a number of method names including prefix/suffix "A" and
"B", and their meanings were inconsistent (i.e., referring to different things
depending on the classes). The names with these two letters were replaced with
more straightforward names. Also some attribute names were changed so as to indicate
the type of the value (e.g., FEdge.next_fedge instead of FEdge.next_edge) in line
with other names explicitly indicating what the value is (e.g., SVertex.viewvertex).
* In addition, some code clean-up was done in both C++ and Python.
Notes:
In summary, the following irregular naming changes were made through this revision
(those resulting from regular changes of naming conventions are not listed):
- CurvePoint: {A,B} --> {first,second}_svertex
- FEdge: vertex{A,B} --> {first,second}_svertex
- FEdge: {next,previous}Edge --> {next,previous}_fedge
- FEdgeSharp: normal{A,B} --> normal_{right,left}
- FEdgeSharp: {a,b}FaceMark --> face_mark_{right,left}
- FEdgeSharp: {a,b}Material --> material_{right,left}
- FEdgeSharp: {a,b}MaterialIndex --> material_index_{right,left}
- FrsCurve: empty --> is_empty
- FrsCurve: nSegments --> segments_size
- TVertex: mate() --> get_mate()
- ViewEdge: fedge{A,B} --> {first,last}_fedge
- ViewEdge: setaShape, aShape --> occlude
- ViewEdge: {A,B} --> {first,last}_viewvertex
- ViewMap: getScene3dBBox --> scene_bbox
2013-02-14 23:48:34 +00:00
|
|
|
fe = fe.next_fedge
|
2011-10-06 02:04:43 +00:00
|
|
|
return False
|
|
|
|
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2011-10-06 02:04:43 +00:00
|
|
|
class FaceMarkOneUP1D(UnaryPredicate1D):
|
2014-07-24 02:08:04 +00:00
|
|
|
def __call__(self, inter: ViewEdge):
|
Freestyle Python API improvements - part 3.
Major API updates were made to address code review comments.
This revision mostly focuses on Python wrappers of C++ 0D and 1D elements (i.e.,
Interface0D and Interface1D, as well as their subclasses).
* Most getter/setter methods were reimplemented as attributes using PyGetSetDef.
Vector attributes are now implemented based on mathutils callbacks. Boolean
attributes now only accept boolean values.
* The __getitem__ method was removed and the Sequence protocol was used instead.
* The naming of methods and attributes was fixed to follow the naming conventions
of the Blender Python API (i.e., lower case + underscores for methods and attributes,
and CamelCase for classes). Some naming inconsistency within the Freestyle Python
API was also addressed.
* The Freestyle API had a number of method names including prefix/suffix "A" and
"B", and their meanings were inconsistent (i.e., referring to different things
depending on the classes). The names with these two letters were replaced with
more straightforward names. Also some attribute names were changed so as to indicate
the type of the value (e.g., FEdge.next_fedge instead of FEdge.next_edge) in line
with other names explicitly indicating what the value is (e.g., SVertex.viewvertex).
* In addition, some code clean-up was done in both C++ and Python.
Notes:
In summary, the following irregular naming changes were made through this revision
(those resulting from regular changes of naming conventions are not listed):
- CurvePoint: {A,B} --> {first,second}_svertex
- FEdge: vertex{A,B} --> {first,second}_svertex
- FEdge: {next,previous}Edge --> {next,previous}_fedge
- FEdgeSharp: normal{A,B} --> normal_{right,left}
- FEdgeSharp: {a,b}FaceMark --> face_mark_{right,left}
- FEdgeSharp: {a,b}Material --> material_{right,left}
- FEdgeSharp: {a,b}MaterialIndex --> material_index_{right,left}
- FrsCurve: empty --> is_empty
- FrsCurve: nSegments --> segments_size
- TVertex: mate() --> get_mate()
- ViewEdge: fedge{A,B} --> {first,last}_fedge
- ViewEdge: setaShape, aShape --> occlude
- ViewEdge: {A,B} --> {first,last}_viewvertex
- ViewMap: getScene3dBBox --> scene_bbox
2013-02-14 23:48:34 +00:00
|
|
|
fe = inter.first_fedge
|
2011-10-06 02:04:43 +00:00
|
|
|
while fe is not None:
|
Freestyle Python API improvements - part 3.
Major API updates were made to address code review comments.
This revision mostly focuses on Python wrappers of C++ 0D and 1D elements (i.e.,
Interface0D and Interface1D, as well as their subclasses).
* Most getter/setter methods were reimplemented as attributes using PyGetSetDef.
Vector attributes are now implemented based on mathutils callbacks. Boolean
attributes now only accept boolean values.
* The __getitem__ method was removed and the Sequence protocol was used instead.
* The naming of methods and attributes was fixed to follow the naming conventions
of the Blender Python API (i.e., lower case + underscores for methods and attributes,
and CamelCase for classes). Some naming inconsistency within the Freestyle Python
API was also addressed.
* The Freestyle API had a number of method names including prefix/suffix "A" and
"B", and their meanings were inconsistent (i.e., referring to different things
depending on the classes). The names with these two letters were replaced with
more straightforward names. Also some attribute names were changed so as to indicate
the type of the value (e.g., FEdge.next_fedge instead of FEdge.next_edge) in line
with other names explicitly indicating what the value is (e.g., SVertex.viewvertex).
* In addition, some code clean-up was done in both C++ and Python.
Notes:
In summary, the following irregular naming changes were made through this revision
(those resulting from regular changes of naming conventions are not listed):
- CurvePoint: {A,B} --> {first,second}_svertex
- FEdge: vertex{A,B} --> {first,second}_svertex
- FEdge: {next,previous}Edge --> {next,previous}_fedge
- FEdgeSharp: normal{A,B} --> normal_{right,left}
- FEdgeSharp: {a,b}FaceMark --> face_mark_{right,left}
- FEdgeSharp: {a,b}Material --> material_{right,left}
- FEdgeSharp: {a,b}MaterialIndex --> material_index_{right,left}
- FrsCurve: empty --> is_empty
- FrsCurve: nSegments --> segments_size
- TVertex: mate() --> get_mate()
- ViewEdge: fedge{A,B} --> {first,last}_fedge
- ViewEdge: setaShape, aShape --> occlude
- ViewEdge: {A,B} --> {first,last}_viewvertex
- ViewMap: getScene3dBBox --> scene_bbox
2013-02-14 23:48:34 +00:00
|
|
|
if fe.is_smooth:
|
|
|
|
if fe.face_mark:
|
2011-10-06 02:04:43 +00:00
|
|
|
return True
|
2013-06-16 00:15:05 +00:00
|
|
|
elif (fe.nature & Nature.BORDER):
|
|
|
|
if fe.face_mark_left:
|
|
|
|
return True
|
2011-10-06 02:04:43 +00:00
|
|
|
else:
|
Freestyle Python API improvements - part 3.
Major API updates were made to address code review comments.
This revision mostly focuses on Python wrappers of C++ 0D and 1D elements (i.e.,
Interface0D and Interface1D, as well as their subclasses).
* Most getter/setter methods were reimplemented as attributes using PyGetSetDef.
Vector attributes are now implemented based on mathutils callbacks. Boolean
attributes now only accept boolean values.
* The __getitem__ method was removed and the Sequence protocol was used instead.
* The naming of methods and attributes was fixed to follow the naming conventions
of the Blender Python API (i.e., lower case + underscores for methods and attributes,
and CamelCase for classes). Some naming inconsistency within the Freestyle Python
API was also addressed.
* The Freestyle API had a number of method names including prefix/suffix "A" and
"B", and their meanings were inconsistent (i.e., referring to different things
depending on the classes). The names with these two letters were replaced with
more straightforward names. Also some attribute names were changed so as to indicate
the type of the value (e.g., FEdge.next_fedge instead of FEdge.next_edge) in line
with other names explicitly indicating what the value is (e.g., SVertex.viewvertex).
* In addition, some code clean-up was done in both C++ and Python.
Notes:
In summary, the following irregular naming changes were made through this revision
(those resulting from regular changes of naming conventions are not listed):
- CurvePoint: {A,B} --> {first,second}_svertex
- FEdge: vertex{A,B} --> {first,second}_svertex
- FEdge: {next,previous}Edge --> {next,previous}_fedge
- FEdgeSharp: normal{A,B} --> normal_{right,left}
- FEdgeSharp: {a,b}FaceMark --> face_mark_{right,left}
- FEdgeSharp: {a,b}Material --> material_{right,left}
- FEdgeSharp: {a,b}MaterialIndex --> material_index_{right,left}
- FrsCurve: empty --> is_empty
- FrsCurve: nSegments --> segments_size
- TVertex: mate() --> get_mate()
- ViewEdge: fedge{A,B} --> {first,last}_fedge
- ViewEdge: setaShape, aShape --> occlude
- ViewEdge: {A,B} --> {first,last}_viewvertex
- ViewMap: getScene3dBBox --> scene_bbox
2013-02-14 23:48:34 +00:00
|
|
|
if fe.face_mark_right or fe.face_mark_left:
|
2011-10-06 02:04:43 +00:00
|
|
|
return True
|
Freestyle Python API improvements - part 3.
Major API updates were made to address code review comments.
This revision mostly focuses on Python wrappers of C++ 0D and 1D elements (i.e.,
Interface0D and Interface1D, as well as their subclasses).
* Most getter/setter methods were reimplemented as attributes using PyGetSetDef.
Vector attributes are now implemented based on mathutils callbacks. Boolean
attributes now only accept boolean values.
* The __getitem__ method was removed and the Sequence protocol was used instead.
* The naming of methods and attributes was fixed to follow the naming conventions
of the Blender Python API (i.e., lower case + underscores for methods and attributes,
and CamelCase for classes). Some naming inconsistency within the Freestyle Python
API was also addressed.
* The Freestyle API had a number of method names including prefix/suffix "A" and
"B", and their meanings were inconsistent (i.e., referring to different things
depending on the classes). The names with these two letters were replaced with
more straightforward names. Also some attribute names were changed so as to indicate
the type of the value (e.g., FEdge.next_fedge instead of FEdge.next_edge) in line
with other names explicitly indicating what the value is (e.g., SVertex.viewvertex).
* In addition, some code clean-up was done in both C++ and Python.
Notes:
In summary, the following irregular naming changes were made through this revision
(those resulting from regular changes of naming conventions are not listed):
- CurvePoint: {A,B} --> {first,second}_svertex
- FEdge: vertex{A,B} --> {first,second}_svertex
- FEdge: {next,previous}Edge --> {next,previous}_fedge
- FEdgeSharp: normal{A,B} --> normal_{right,left}
- FEdgeSharp: {a,b}FaceMark --> face_mark_{right,left}
- FEdgeSharp: {a,b}Material --> material_{right,left}
- FEdgeSharp: {a,b}MaterialIndex --> material_index_{right,left}
- FrsCurve: empty --> is_empty
- FrsCurve: nSegments --> segments_size
- TVertex: mate() --> get_mate()
- ViewEdge: fedge{A,B} --> {first,last}_fedge
- ViewEdge: setaShape, aShape --> occlude
- ViewEdge: {A,B} --> {first,last}_viewvertex
- ViewMap: getScene3dBBox --> scene_bbox
2013-02-14 23:48:34 +00:00
|
|
|
fe = fe.next_fedge
|
2011-10-06 02:04:43 +00:00
|
|
|
return False
|
|
|
|
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2011-08-13 00:05:25 +00:00
|
|
|
# predicates for splitting
|
|
|
|
|
|
|
|
class MaterialBoundaryUP0D(UnaryPredicate0D):
|
|
|
|
def __call__(self, it):
|
2014-09-02 12:24:41 +00:00
|
|
|
# can't use only it.is_end here, see commit rBeb8964fb7f19
|
|
|
|
if it.is_begin or it.at_last or it.is_end:
|
2011-08-13 00:05:25 +00:00
|
|
|
return False
|
2014-09-02 12:24:41 +00:00
|
|
|
it.decrement()
|
|
|
|
prev, v, succ = next(it), next(it), next(it)
|
|
|
|
fe = v.get_fedge(prev)
|
Freestyle Python API improvements - part 3.
Major API updates were made to address code review comments.
This revision mostly focuses on Python wrappers of C++ 0D and 1D elements (i.e.,
Interface0D and Interface1D, as well as their subclasses).
* Most getter/setter methods were reimplemented as attributes using PyGetSetDef.
Vector attributes are now implemented based on mathutils callbacks. Boolean
attributes now only accept boolean values.
* The __getitem__ method was removed and the Sequence protocol was used instead.
* The naming of methods and attributes was fixed to follow the naming conventions
of the Blender Python API (i.e., lower case + underscores for methods and attributes,
and CamelCase for classes). Some naming inconsistency within the Freestyle Python
API was also addressed.
* The Freestyle API had a number of method names including prefix/suffix "A" and
"B", and their meanings were inconsistent (i.e., referring to different things
depending on the classes). The names with these two letters were replaced with
more straightforward names. Also some attribute names were changed so as to indicate
the type of the value (e.g., FEdge.next_fedge instead of FEdge.next_edge) in line
with other names explicitly indicating what the value is (e.g., SVertex.viewvertex).
* In addition, some code clean-up was done in both C++ and Python.
Notes:
In summary, the following irregular naming changes were made through this revision
(those resulting from regular changes of naming conventions are not listed):
- CurvePoint: {A,B} --> {first,second}_svertex
- FEdge: vertex{A,B} --> {first,second}_svertex
- FEdge: {next,previous}Edge --> {next,previous}_fedge
- FEdgeSharp: normal{A,B} --> normal_{right,left}
- FEdgeSharp: {a,b}FaceMark --> face_mark_{right,left}
- FEdgeSharp: {a,b}Material --> material_{right,left}
- FEdgeSharp: {a,b}MaterialIndex --> material_index_{right,left}
- FrsCurve: empty --> is_empty
- FrsCurve: nSegments --> segments_size
- TVertex: mate() --> get_mate()
- ViewEdge: fedge{A,B} --> {first,last}_fedge
- ViewEdge: setaShape, aShape --> occlude
- ViewEdge: {A,B} --> {first,last}_viewvertex
- ViewMap: getScene3dBBox --> scene_bbox
2013-02-14 23:48:34 +00:00
|
|
|
idx1 = fe.material_index if fe.is_smooth else fe.material_index_left
|
2014-09-02 12:24:41 +00:00
|
|
|
fe = v.get_fedge(succ)
|
Freestyle Python API improvements - part 3.
Major API updates were made to address code review comments.
This revision mostly focuses on Python wrappers of C++ 0D and 1D elements (i.e.,
Interface0D and Interface1D, as well as their subclasses).
* Most getter/setter methods were reimplemented as attributes using PyGetSetDef.
Vector attributes are now implemented based on mathutils callbacks. Boolean
attributes now only accept boolean values.
* The __getitem__ method was removed and the Sequence protocol was used instead.
* The naming of methods and attributes was fixed to follow the naming conventions
of the Blender Python API (i.e., lower case + underscores for methods and attributes,
and CamelCase for classes). Some naming inconsistency within the Freestyle Python
API was also addressed.
* The Freestyle API had a number of method names including prefix/suffix "A" and
"B", and their meanings were inconsistent (i.e., referring to different things
depending on the classes). The names with these two letters were replaced with
more straightforward names. Also some attribute names were changed so as to indicate
the type of the value (e.g., FEdge.next_fedge instead of FEdge.next_edge) in line
with other names explicitly indicating what the value is (e.g., SVertex.viewvertex).
* In addition, some code clean-up was done in both C++ and Python.
Notes:
In summary, the following irregular naming changes were made through this revision
(those resulting from regular changes of naming conventions are not listed):
- CurvePoint: {A,B} --> {first,second}_svertex
- FEdge: vertex{A,B} --> {first,second}_svertex
- FEdge: {next,previous}Edge --> {next,previous}_fedge
- FEdgeSharp: normal{A,B} --> normal_{right,left}
- FEdgeSharp: {a,b}FaceMark --> face_mark_{right,left}
- FEdgeSharp: {a,b}Material --> material_{right,left}
- FEdgeSharp: {a,b}MaterialIndex --> material_index_{right,left}
- FrsCurve: empty --> is_empty
- FrsCurve: nSegments --> segments_size
- TVertex: mate() --> get_mate()
- ViewEdge: fedge{A,B} --> {first,last}_fedge
- ViewEdge: setaShape, aShape --> occlude
- ViewEdge: {A,B} --> {first,last}_viewvertex
- ViewMap: getScene3dBBox --> scene_bbox
2013-02-14 23:48:34 +00:00
|
|
|
idx2 = fe.material_index if fe.is_smooth else fe.material_index_left
|
2011-08-13 00:05:25 +00:00
|
|
|
return idx1 != idx2
|
|
|
|
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2011-11-13 20:20:50 +00:00
|
|
|
class Curvature2DAngleThresholdUP0D(UnaryPredicate0D):
|
2013-04-23 07:06:29 +00:00
|
|
|
def __init__(self, angle_min=None, angle_max=None):
|
2011-11-13 20:20:50 +00:00
|
|
|
UnaryPredicate0D.__init__(self)
|
2014-07-24 02:08:04 +00:00
|
|
|
self.angle_min = angle_min
|
|
|
|
self.angle_max = angle_max
|
|
|
|
self.func = Curvature2DAngleF0D()
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2011-11-13 20:20:50 +00:00
|
|
|
def __call__(self, inter):
|
2014-07-24 02:08:04 +00:00
|
|
|
angle = pi - self.func(inter)
|
|
|
|
if self.angle_min is not None and angle < self.angle_min:
|
2011-11-13 20:20:50 +00:00
|
|
|
return True
|
2014-07-24 02:08:04 +00:00
|
|
|
if self.angle_max is not None and angle > self.angle_max:
|
2011-11-13 20:20:50 +00:00
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2011-12-11 23:41:15 +00:00
|
|
|
class Length2DThresholdUP0D(UnaryPredicate0D):
|
|
|
|
def __init__(self, length_limit):
|
|
|
|
UnaryPredicate0D.__init__(self)
|
2014-07-24 02:08:04 +00:00
|
|
|
self.length_limit = length_limit
|
|
|
|
self.t = 0.0
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2011-12-11 23:41:15 +00:00
|
|
|
def __call__(self, inter):
|
2013-08-21 23:19:01 +00:00
|
|
|
t = inter.t # curvilinear abscissa
|
2014-07-24 02:08:04 +00:00
|
|
|
if t < self.t:
|
|
|
|
self.t = 0.0
|
2011-12-11 23:41:15 +00:00
|
|
|
return False
|
2014-07-24 02:08:04 +00:00
|
|
|
if t - self.t < self.length_limit:
|
2011-12-11 23:41:15 +00:00
|
|
|
return False
|
2014-07-24 02:08:04 +00:00
|
|
|
self.t = t
|
2011-12-11 23:41:15 +00:00
|
|
|
return True
|
|
|
|
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2011-08-19 14:05:11 +00:00
|
|
|
# Seed for random number generation
|
|
|
|
|
|
|
|
class Seed:
|
|
|
|
def __init__(self):
|
|
|
|
self.t_max = 2 ** 15
|
|
|
|
self.t = int(time.time()) % self.t_max
|
2013-08-21 23:19:01 +00:00
|
|
|
|
2011-08-19 14:05:11 +00:00
|
|
|
def get(self, seed):
|
|
|
|
if seed < 0:
|
|
|
|
self.t = (self.t + 1) % self.t_max
|
|
|
|
return self.t
|
|
|
|
return seed
|
|
|
|
|
|
|
|
_seed = Seed()
|
|
|
|
|
2013-08-21 21:20:51 +00:00
|
|
|
|
2014-11-24 21:41:34 +00:00
|
|
|
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
|
|
|
|
|
|
|
|
|
2014-04-18 05:59:02 +00:00
|
|
|
integration_types = {
|
|
|
|
'MEAN': IntegrationType.MEAN,
|
|
|
|
'MIN': IntegrationType.MIN,
|
|
|
|
'MAX': IntegrationType.MAX,
|
|
|
|
'FIRST': IntegrationType.FIRST,
|
|
|
|
'LAST': IntegrationType.LAST}
|
|
|
|
|
|
|
|
|
2010-08-01 16:02:34 +00:00
|
|
|
# main function for parameter processing
|
2010-07-26 01:23:27 +00:00
|
|
|
def process(layer_name, lineset_name):
|
2013-11-24 22:18:38 +00:00
|
|
|
scene = getCurrentScene()
|
2010-07-26 01:23:27 +00:00
|
|
|
layer = scene.render.layers[layer_name]
|
|
|
|
lineset = layer.freestyle_settings.linesets[lineset_name]
|
|
|
|
linestyle = lineset.linestyle
|
|
|
|
|
2014-11-24 21:41:34 +00:00
|
|
|
# execute line set pre-processing callback functions
|
|
|
|
for fn in callbacks_lineset_pre:
|
2014-12-02 13:43:44 +00:00
|
|
|
fn(scene, layer, lineset)
|
2014-11-24 21:41:34 +00:00
|
|
|
|
2010-07-28 00:43:45 +00:00
|
|
|
selection_criteria = []
|
|
|
|
# prepare selection criteria by visibility
|
|
|
|
if lineset.select_by_visibility:
|
2013-04-23 07:06:29 +00:00
|
|
|
if lineset.visibility == 'VISIBLE':
|
2010-07-28 00:43:45 +00:00
|
|
|
selection_criteria.append(
|
|
|
|
QuantitativeInvisibilityUP1D(0))
|
2013-04-23 07:06:29 +00:00
|
|
|
elif lineset.visibility == 'HIDDEN':
|
2010-07-28 00:43:45 +00:00
|
|
|
selection_criteria.append(
|
|
|
|
NotUP1D(QuantitativeInvisibilityUP1D(0)))
|
2013-04-23 07:06:29 +00:00
|
|
|
elif lineset.visibility == 'RANGE':
|
2010-07-28 00:43:45 +00:00
|
|
|
selection_criteria.append(
|
|
|
|
QuantitativeInvisibilityRangeUP1D(lineset.qi_start, lineset.qi_end))
|
|
|
|
# prepare selection criteria by edge types
|
|
|
|
if lineset.select_by_edge_types:
|
|
|
|
edge_type_criteria = []
|
2011-10-27 20:57:14 +00:00
|
|
|
if lineset.select_silhouette:
|
|
|
|
upred = pyNatureUP1D(Nature.SILHOUETTE)
|
|
|
|
edge_type_criteria.append(NotUP1D(upred) if lineset.exclude_silhouette else upred)
|
|
|
|
if lineset.select_border:
|
|
|
|
upred = pyNatureUP1D(Nature.BORDER)
|
|
|
|
edge_type_criteria.append(NotUP1D(upred) if lineset.exclude_border else upred)
|
|
|
|
if lineset.select_crease:
|
|
|
|
upred = pyNatureUP1D(Nature.CREASE)
|
|
|
|
edge_type_criteria.append(NotUP1D(upred) if lineset.exclude_crease else upred)
|
2012-02-05 12:50:01 +00:00
|
|
|
if lineset.select_ridge_valley:
|
2011-10-27 20:57:14 +00:00
|
|
|
upred = pyNatureUP1D(Nature.RIDGE)
|
2012-02-05 12:50:01 +00:00
|
|
|
edge_type_criteria.append(NotUP1D(upred) if lineset.exclude_ridge_valley else upred)
|
2011-10-27 20:57:14 +00:00
|
|
|
if lineset.select_suggestive_contour:
|
|
|
|
upred = pyNatureUP1D(Nature.SUGGESTIVE_CONTOUR)
|
|
|
|
edge_type_criteria.append(NotUP1D(upred) if lineset.exclude_suggestive_contour else upred)
|
|
|
|
if lineset.select_material_boundary:
|
|
|
|
upred = pyNatureUP1D(Nature.MATERIAL_BOUNDARY)
|
|
|
|
edge_type_criteria.append(NotUP1D(upred) if lineset.exclude_material_boundary else upred)
|
|
|
|
if lineset.select_edge_mark:
|
|
|
|
upred = pyNatureUP1D(Nature.EDGE_MARK)
|
|
|
|
edge_type_criteria.append(NotUP1D(upred) if lineset.exclude_edge_mark else upred)
|
2010-07-28 00:43:45 +00:00
|
|
|
if lineset.select_contour:
|
2011-10-27 20:57:14 +00:00
|
|
|
upred = ContourUP1D()
|
|
|
|
edge_type_criteria.append(NotUP1D(upred) if lineset.exclude_contour else upred)
|
2010-07-28 00:43:45 +00:00
|
|
|
if lineset.select_external_contour:
|
2011-10-27 20:57:14 +00:00
|
|
|
upred = ExternalContourUP1D()
|
|
|
|
edge_type_criteria.append(NotUP1D(upred) if lineset.exclude_external_contour else upred)
|
2015-04-16 00:13:40 +00:00
|
|
|
if edge_type_criteria:
|
|
|
|
if lineset.edge_type_combination == 'OR':
|
|
|
|
upred = OrUP1D(*edge_type_criteria)
|
|
|
|
else:
|
|
|
|
upred = AndUP1D(*edge_type_criteria)
|
2013-04-23 07:06:29 +00:00
|
|
|
if lineset.edge_type_negation == 'EXCLUSIVE':
|
2010-07-28 00:43:45 +00:00
|
|
|
upred = NotUP1D(upred)
|
|
|
|
selection_criteria.append(upred)
|
2011-10-06 02:04:43 +00:00
|
|
|
# prepare selection criteria by face marks
|
|
|
|
if lineset.select_by_face_marks:
|
2013-04-23 07:06:29 +00:00
|
|
|
if lineset.face_mark_condition == 'BOTH':
|
2011-10-06 02:04:43 +00:00
|
|
|
upred = FaceMarkBothUP1D()
|
|
|
|
else:
|
|
|
|
upred = FaceMarkOneUP1D()
|
2014-07-24 02:08:04 +00:00
|
|
|
|
2013-04-23 07:06:29 +00:00
|
|
|
if lineset.face_mark_negation == 'EXCLUSIVE':
|
2011-10-06 02:04:43 +00:00
|
|
|
upred = NotUP1D(upred)
|
|
|
|
selection_criteria.append(upred)
|
2010-12-02 23:50:10 +00:00
|
|
|
# prepare selection criteria by group of objects
|
|
|
|
if lineset.select_by_group:
|
2012-05-04 00:37:06 +00:00
|
|
|
if lineset.group is not None:
|
2014-07-24 02:08:04 +00:00
|
|
|
names = {ob.name: True for ob in lineset.group.objects}
|
2010-12-02 23:50:10 +00:00
|
|
|
upred = ObjectNamesUP1D(names, lineset.group_negation == 'EXCLUSIVE')
|
|
|
|
selection_criteria.append(upred)
|
2010-12-03 23:17:49 +00:00
|
|
|
# prepare selection criteria by image border
|
|
|
|
if lineset.select_by_image_border:
|
2014-07-24 02:08:04 +00:00
|
|
|
upred = WithinImageBoundaryUP1D(*ContextFunctions.get_border())
|
2010-12-03 23:17:49 +00:00
|
|
|
selection_criteria.append(upred)
|
2011-09-18 22:59:51 +00:00
|
|
|
# select feature edges
|
2015-04-16 00:13:40 +00:00
|
|
|
if selection_criteria:
|
|
|
|
upred = AndUP1D(*selection_criteria)
|
|
|
|
else:
|
2010-07-28 00:43:45 +00:00
|
|
|
upred = TrueUP1D()
|
2010-07-26 01:23:27 +00:00
|
|
|
Operators.select(upred)
|
2011-09-18 22:59:51 +00:00
|
|
|
# join feature edges to form chains
|
2011-10-25 23:24:59 +00:00
|
|
|
if linestyle.use_chaining:
|
2013-04-23 07:06:29 +00:00
|
|
|
if linestyle.chaining == 'PLAIN':
|
|
|
|
if linestyle.use_same_object:
|
2013-02-24 02:39:38 +00:00
|
|
|
Operators.bidirectional_chain(ChainSilhouetteIterator(), NotUP1D(upred))
|
2011-10-25 23:24:59 +00:00
|
|
|
else:
|
2013-02-24 02:39:38 +00:00
|
|
|
Operators.bidirectional_chain(ChainPredicateIterator(upred, TrueBP1D()), NotUP1D(upred))
|
2013-04-23 07:06:29 +00:00
|
|
|
elif linestyle.chaining == 'SKETCHY':
|
|
|
|
if linestyle.use_same_object:
|
2013-02-24 02:39:38 +00:00
|
|
|
Operators.bidirectional_chain(pySketchyChainSilhouetteIterator(linestyle.rounds))
|
2011-10-25 23:24:59 +00:00
|
|
|
else:
|
2013-02-24 02:39:38 +00:00
|
|
|
Operators.bidirectional_chain(pySketchyChainingIterator(linestyle.rounds))
|
2011-12-11 23:41:15 +00:00
|
|
|
else:
|
|
|
|
Operators.chain(ChainPredicateIterator(FalseUP1D(), FalseBP1D()), NotUP1D(upred))
|
2011-09-18 22:59:51 +00:00
|
|
|
# split chains
|
|
|
|
if linestyle.material_boundary:
|
2013-02-24 02:39:38 +00:00
|
|
|
Operators.sequential_split(MaterialBoundaryUP0D())
|
2013-04-23 07:06:29 +00:00
|
|
|
if linestyle.use_angle_min or linestyle.use_angle_max:
|
|
|
|
angle_min = linestyle.angle_min if linestyle.use_angle_min else None
|
|
|
|
angle_max = linestyle.angle_max if linestyle.use_angle_max else None
|
|
|
|
Operators.sequential_split(Curvature2DAngleThresholdUP0D(angle_min, angle_max))
|
2011-12-11 23:41:15 +00:00
|
|
|
if linestyle.use_split_length:
|
2013-02-24 02:39:38 +00:00
|
|
|
Operators.sequential_split(Length2DThresholdUP0D(linestyle.split_length), 1.0)
|
2012-11-04 23:52:26 +00:00
|
|
|
if linestyle.use_split_pattern:
|
|
|
|
pattern = []
|
|
|
|
if linestyle.split_dash1 > 0 and linestyle.split_gap1 > 0:
|
|
|
|
pattern.append(linestyle.split_dash1)
|
|
|
|
pattern.append(linestyle.split_gap1)
|
|
|
|
if linestyle.split_dash2 > 0 and linestyle.split_gap2 > 0:
|
|
|
|
pattern.append(linestyle.split_dash2)
|
|
|
|
pattern.append(linestyle.split_gap2)
|
|
|
|
if linestyle.split_dash3 > 0 and linestyle.split_gap3 > 0:
|
|
|
|
pattern.append(linestyle.split_dash3)
|
|
|
|
pattern.append(linestyle.split_gap3)
|
|
|
|
if len(pattern) > 0:
|
|
|
|
sampling = 1.0
|
|
|
|
controller = SplitPatternController(pattern, sampling)
|
2013-02-24 02:39:38 +00:00
|
|
|
Operators.sequential_split(SplitPatternStartingUP0D(controller),
|
|
|
|
SplitPatternStoppingUP0D(controller),
|
|
|
|
sampling)
|
2014-04-18 05:59:02 +00:00
|
|
|
# sort selected chains
|
|
|
|
if linestyle.use_sorting:
|
|
|
|
integration = integration_types.get(linestyle.integration_type, IntegrationType.MEAN)
|
|
|
|
if linestyle.sort_key == 'DISTANCE_FROM_CAMERA':
|
|
|
|
bpred = pyZBP1D(integration)
|
|
|
|
elif linestyle.sort_key == '2D_LENGTH':
|
|
|
|
bpred = Length2DBP1D()
|
2014-10-01 06:42:37 +00:00
|
|
|
elif linestyle.sort_key == 'PROJECTED_X':
|
|
|
|
bpred = pyProjectedXBP1D(integration)
|
|
|
|
elif linestyle.sort_key == 'PROJECTED_Y':
|
|
|
|
bpred = pyProjectedYBP1D(integration)
|
2014-04-18 05:59:02 +00:00
|
|
|
if linestyle.sort_order == 'REVERSE':
|
|
|
|
bpred = NotBP1D(bpred)
|
|
|
|
Operators.sort(bpred)
|
2014-10-01 06:42:37 +00:00
|
|
|
# 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))
|
2010-07-28 00:43:45 +00:00
|
|
|
# prepare a list of stroke shaders
|
2011-08-19 14:05:11 +00:00
|
|
|
shaders_list = []
|
|
|
|
for m in linestyle.geometry_modifiers:
|
|
|
|
if not m.use:
|
|
|
|
continue
|
2013-04-23 07:06:29 +00:00
|
|
|
if m.type == 'SAMPLING':
|
2011-08-19 14:05:11 +00:00
|
|
|
shaders_list.append(SamplingShader(
|
|
|
|
m.sampling))
|
2013-04-23 07:06:29 +00:00
|
|
|
elif m.type == 'BEZIER_CURVE':
|
2011-08-19 14:05:11 +00:00
|
|
|
shaders_list.append(BezierCurveShader(
|
|
|
|
m.error))
|
2013-04-23 07:06:29 +00:00
|
|
|
elif m.type == 'SINUS_DISPLACEMENT':
|
2011-08-19 14:05:11 +00:00
|
|
|
shaders_list.append(SinusDisplacementShader(
|
|
|
|
m.wavelength, m.amplitude, m.phase))
|
2013-04-23 07:06:29 +00:00
|
|
|
elif m.type == 'SPATIAL_NOISE':
|
2011-08-19 14:05:11 +00:00
|
|
|
shaders_list.append(SpatialNoiseShader(
|
2013-04-23 07:06:29 +00:00
|
|
|
m.amplitude, m.scale, m.octaves, m.smooth, m.use_pure_random))
|
|
|
|
elif m.type == 'PERLIN_NOISE_1D':
|
2011-08-30 00:29:12 +00:00
|
|
|
shaders_list.append(PerlinNoise1DShader(
|
|
|
|
m.frequency, m.amplitude, m.octaves, m.angle, _seed.get(m.seed)))
|
2013-04-23 07:06:29 +00:00
|
|
|
elif m.type == 'PERLIN_NOISE_2D':
|
2011-08-30 00:29:12 +00:00
|
|
|
shaders_list.append(PerlinNoise2DShader(
|
|
|
|
m.frequency, m.amplitude, m.octaves, m.angle, _seed.get(m.seed)))
|
2013-04-23 07:06:29 +00:00
|
|
|
elif m.type == 'BACKBONE_STRETCHER':
|
2011-08-19 14:05:11 +00:00
|
|
|
shaders_list.append(BackboneStretcherShader(
|
2011-11-11 20:35:03 +00:00
|
|
|
m.backbone_length))
|
2013-04-23 07:06:29 +00:00
|
|
|
elif m.type == 'TIP_REMOVER':
|
2011-08-19 14:05:11 +00:00
|
|
|
shaders_list.append(TipRemoverShader(
|
|
|
|
m.tip_length))
|
2013-04-23 07:06:29 +00:00
|
|
|
elif m.type == 'POLYGONIZATION':
|
2011-11-01 09:47:41 +00:00
|
|
|
shaders_list.append(PolygonalizationShader(
|
|
|
|
m.error))
|
2013-04-23 07:06:29 +00:00
|
|
|
elif m.type == 'GUIDING_LINES':
|
2011-11-01 09:47:41 +00:00
|
|
|
shaders_list.append(GuidingLinesShader(
|
|
|
|
m.offset))
|
2013-04-23 07:06:29 +00:00
|
|
|
elif m.type == 'BLUEPRINT':
|
|
|
|
if m.shape == 'CIRCLES':
|
2011-11-11 20:35:03 +00:00
|
|
|
shaders_list.append(pyBluePrintCirclesShader(
|
|
|
|
m.rounds, m.random_radius, m.random_center))
|
2013-04-23 07:06:29 +00:00
|
|
|
elif m.shape == 'ELLIPSES':
|
2011-11-11 20:35:03 +00:00
|
|
|
shaders_list.append(pyBluePrintEllipsesShader(
|
|
|
|
m.rounds, m.random_radius, m.random_center))
|
2013-04-23 07:06:29 +00:00
|
|
|
elif m.shape == 'SQUARES':
|
2011-11-11 20:35:03 +00:00
|
|
|
shaders_list.append(pyBluePrintSquaresShader(
|
|
|
|
m.rounds, m.backbone_length, m.random_backbone))
|
2013-04-23 07:06:29 +00:00
|
|
|
elif m.type == '2D_OFFSET':
|
2012-01-04 00:23:34 +00:00
|
|
|
shaders_list.append(Offset2DShader(
|
|
|
|
m.start, m.end, m.x, m.y))
|
2013-04-23 07:06:29 +00:00
|
|
|
elif m.type == '2D_TRANSFORM':
|
2012-01-04 00:23:34 +00:00
|
|
|
shaders_list.append(Transform2DShader(
|
|
|
|
m.pivot, m.scale_x, m.scale_y, m.angle, m.pivot_u, m.pivot_x, m.pivot_y))
|
2014-07-28 03:25:26 +00:00
|
|
|
# -- Base color, alpha and thickness -- #
|
2013-04-23 07:06:29 +00:00
|
|
|
if (not linestyle.use_chaining) or (linestyle.chaining == 'PLAIN' and linestyle.use_same_object):
|
2012-04-08 22:21:48 +00:00
|
|
|
thickness_position = linestyle.thickness_position
|
|
|
|
else:
|
2013-04-23 07:06:29 +00:00
|
|
|
thickness_position = 'CENTER'
|
2013-01-03 23:27:20 +00:00
|
|
|
import bpy
|
|
|
|
if bpy.app.debug_freestyle:
|
2013-04-23 07:06:29 +00:00
|
|
|
print("Warning: Thickness position options are applied when chaining is disabled\n"
|
|
|
|
" or the Plain chaining is used with the Same Object option enabled.")
|
2014-07-24 02:08:04 +00:00
|
|
|
shaders_list.append(ConstantColorShader(*(linestyle.color), alpha=linestyle.alpha))
|
2012-04-08 22:21:48 +00:00
|
|
|
shaders_list.append(BaseThicknessShader(linestyle.thickness, thickness_position,
|
2012-04-07 17:28:09 +00:00
|
|
|
linestyle.thickness_ratio))
|
2014-07-28 03:25:26 +00:00
|
|
|
# -- Modifiers -- #
|
2010-07-28 00:43:45 +00:00
|
|
|
for m in linestyle.color_modifiers:
|
2010-10-10 23:34:27 +00:00
|
|
|
if not m.use:
|
2010-07-28 00:43:45 +00:00
|
|
|
continue
|
2013-04-23 07:06:29 +00:00
|
|
|
if m.type == 'ALONG_STROKE':
|
2010-08-01 16:02:34 +00:00
|
|
|
shaders_list.append(ColorAlongStrokeShader(
|
|
|
|
m.blend, m.influence, m.color_ramp))
|
2013-04-23 07:06:29 +00:00
|
|
|
elif m.type == 'DISTANCE_FROM_CAMERA':
|
2010-08-01 16:02:34 +00:00
|
|
|
shaders_list.append(ColorDistanceFromCameraShader(
|
|
|
|
m.blend, m.influence, m.color_ramp,
|
|
|
|
m.range_min, m.range_max))
|
2014-07-24 02:08:04 +00:00
|
|
|
elif m.type == 'DISTANCE_FROM_OBJECT' and m.target is not None:
|
2010-08-01 22:11:57 +00:00
|
|
|
shaders_list.append(ColorDistanceFromObjectShader(
|
|
|
|
m.blend, m.influence, m.color_ramp, m.target,
|
|
|
|
m.range_min, m.range_max))
|
2013-04-23 07:06:29 +00:00
|
|
|
elif m.type == 'MATERIAL':
|
2011-07-24 23:29:19 +00:00
|
|
|
shaders_list.append(ColorMaterialShader(
|
2013-04-23 07:06:29 +00:00
|
|
|
m.blend, m.influence, m.color_ramp, m.material_attribute,
|
2011-07-24 23:29:19 +00:00
|
|
|
m.use_ramp))
|
2010-07-28 00:43:45 +00:00
|
|
|
for m in linestyle.alpha_modifiers:
|
2010-10-10 23:34:27 +00:00
|
|
|
if not m.use:
|
2010-07-28 00:43:45 +00:00
|
|
|
continue
|
2013-04-23 07:06:29 +00:00
|
|
|
if m.type == 'ALONG_STROKE':
|
2010-08-01 16:02:34 +00:00
|
|
|
shaders_list.append(AlphaAlongStrokeShader(
|
|
|
|
m.blend, m.influence, m.mapping, m.invert, m.curve))
|
2013-04-23 07:06:29 +00:00
|
|
|
elif m.type == 'DISTANCE_FROM_CAMERA':
|
2010-08-01 16:02:34 +00:00
|
|
|
shaders_list.append(AlphaDistanceFromCameraShader(
|
|
|
|
m.blend, m.influence, m.mapping, m.invert, m.curve,
|
|
|
|
m.range_min, m.range_max))
|
2014-07-24 02:08:04 +00:00
|
|
|
elif m.type == 'DISTANCE_FROM_OBJECT' and m.target is not None:
|
2010-08-01 22:11:57 +00:00
|
|
|
shaders_list.append(AlphaDistanceFromObjectShader(
|
|
|
|
m.blend, m.influence, m.mapping, m.invert, m.curve, m.target,
|
|
|
|
m.range_min, m.range_max))
|
2013-04-23 07:06:29 +00:00
|
|
|
elif m.type == 'MATERIAL':
|
2011-07-24 23:29:19 +00:00
|
|
|
shaders_list.append(AlphaMaterialShader(
|
|
|
|
m.blend, m.influence, m.mapping, m.invert, m.curve,
|
2013-04-23 07:06:29 +00:00
|
|
|
m.material_attribute))
|
2010-07-28 00:43:45 +00:00
|
|
|
for m in linestyle.thickness_modifiers:
|
2010-10-10 23:34:27 +00:00
|
|
|
if not m.use:
|
2010-07-28 00:43:45 +00:00
|
|
|
continue
|
2013-04-23 07:06:29 +00:00
|
|
|
if m.type == 'ALONG_STROKE':
|
2010-08-01 16:02:34 +00:00
|
|
|
shaders_list.append(ThicknessAlongStrokeShader(
|
2012-04-08 22:21:48 +00:00
|
|
|
thickness_position, linestyle.thickness_ratio,
|
2010-08-01 16:02:34 +00:00
|
|
|
m.blend, m.influence, m.mapping, m.invert, m.curve,
|
|
|
|
m.value_min, m.value_max))
|
2013-04-23 07:06:29 +00:00
|
|
|
elif m.type == 'DISTANCE_FROM_CAMERA':
|
2010-08-01 16:02:34 +00:00
|
|
|
shaders_list.append(ThicknessDistanceFromCameraShader(
|
2012-04-08 22:21:48 +00:00
|
|
|
thickness_position, linestyle.thickness_ratio,
|
2010-08-01 16:02:34 +00:00
|
|
|
m.blend, m.influence, m.mapping, m.invert, m.curve,
|
|
|
|
m.range_min, m.range_max, m.value_min, m.value_max))
|
2014-07-24 02:08:04 +00:00
|
|
|
elif m.type == 'DISTANCE_FROM_OBJECT' and m.target is not None:
|
2010-08-01 22:11:57 +00:00
|
|
|
shaders_list.append(ThicknessDistanceFromObjectShader(
|
2012-04-08 22:21:48 +00:00
|
|
|
thickness_position, linestyle.thickness_ratio,
|
2010-08-01 22:11:57 +00:00
|
|
|
m.blend, m.influence, m.mapping, m.invert, m.curve, m.target,
|
|
|
|
m.range_min, m.range_max, m.value_min, m.value_max))
|
2013-04-23 07:06:29 +00:00
|
|
|
elif m.type == 'MATERIAL':
|
2011-07-24 23:29:19 +00:00
|
|
|
shaders_list.append(ThicknessMaterialShader(
|
2012-04-08 22:21:48 +00:00
|
|
|
thickness_position, linestyle.thickness_ratio,
|
2011-07-24 23:29:19 +00:00
|
|
|
m.blend, m.influence, m.mapping, m.invert, m.curve,
|
2013-04-23 07:06:29 +00:00
|
|
|
m.material_attribute, m.value_min, m.value_max))
|
|
|
|
elif m.type == 'CALLIGRAPHY':
|
2011-09-11 19:57:38 +00:00
|
|
|
shaders_list.append(CalligraphicThicknessShader(
|
2012-04-08 22:21:48 +00:00
|
|
|
thickness_position, linestyle.thickness_ratio,
|
2011-09-11 19:57:38 +00:00
|
|
|
m.blend, m.influence,
|
2013-04-23 07:06:29 +00:00
|
|
|
m.orientation, m.thickness_min, m.thickness_max))
|
2014-07-28 03:25:26 +00:00
|
|
|
# -- Textures -- #
|
|
|
|
has_tex = False
|
|
|
|
if scene.render.use_shading_nodes:
|
|
|
|
if linestyle.use_nodes and linestyle.node_tree:
|
|
|
|
shaders_list.append(BlenderTextureShader(linestyle.node_tree))
|
|
|
|
has_tex = True
|
|
|
|
else:
|
|
|
|
if linestyle.use_texture:
|
|
|
|
textures = tuple(BlenderTextureShader(slot) for slot in linestyle.texture_slots if slot is not None)
|
|
|
|
if textures:
|
|
|
|
shaders_list.extend(textures)
|
|
|
|
has_tex = True
|
|
|
|
if has_tex:
|
|
|
|
shaders_list.append(StrokeTextureStepShader(linestyle.texture_spacing))
|
2014-11-24 21:41:34 +00:00
|
|
|
|
|
|
|
# execute post-base stylization callbacks
|
|
|
|
for fn in callbacks_modifiers_post:
|
|
|
|
shaders_list.extend(fn(scene, layer, lineset))
|
|
|
|
|
2014-07-24 02:08:04 +00:00
|
|
|
# -- Stroke caps -- #
|
2013-04-23 07:06:29 +00:00
|
|
|
if linestyle.caps == 'ROUND':
|
2011-08-22 12:42:56 +00:00
|
|
|
shaders_list.append(RoundCapShader())
|
2013-04-23 07:06:29 +00:00
|
|
|
elif linestyle.caps == 'SQUARE':
|
2011-08-22 12:42:56 +00:00
|
|
|
shaders_list.append(SquareCapShader())
|
2014-11-24 21:41:34 +00:00
|
|
|
|
2014-10-19 17:10:06 +00:00
|
|
|
# -- Dashed line -- #
|
|
|
|
if linestyle.use_dashed_line:
|
2014-11-24 21:41:34 +00:00
|
|
|
pattern = get_dashed_pattern(linestyle)
|
2014-10-19 17:10:06 +00:00
|
|
|
if len(pattern) > 0:
|
|
|
|
shaders_list.append(DashedLineShader(pattern))
|
2014-11-24 21:41:34 +00:00
|
|
|
|
2010-07-28 00:43:45 +00:00
|
|
|
# create strokes using the shaders list
|
2010-07-26 01:23:27 +00:00
|
|
|
Operators.create(TrueUP1D(), shaders_list)
|
2014-11-24 21:41:34 +00:00
|
|
|
|
|
|
|
# execute line set post-processing callback functions
|
|
|
|
for fn in callbacks_lineset_post:
|
|
|
|
fn(scene, layer, lineset)
|