Building Freestyle-related tools in Python
About me
My work on Freestyle
Today
introduction
starting projects
lunch
continue coding
finishing up
presenting results
end
What you'll need
Today's material can be found at:
github.com/folkertdev/BlenderBrussels
Freestyle
Freestyle is an edge-based non-photo realistic render engine
image by @reigully
image by Karlis Stigis
Freestyle Python API
Basic Building Blocks
The documentation can be found at
blender.org/api/blender_python_api_2_72_release/freestyle.html
Functions
class pyGetInverseProjectedZF1D(UnaryFunction1DDouble):
def __call__(self, inter):
func = GetProjectedZF1D()
z = func(inter)
return (1.0 - z)
Definition
Functions
class pyZDependingThicknessShader(StrokeShader):
def __init__(self, min, max):
...
self.func = GetProjectedZF0D()
def shade(self, stroke):
it = Interface0DIterator(stroke)
# note that the function is applied to the iterator object itself
z_indices = tuple(self.func(it) for _ in it)
...
Usage
Predicates
class pyHigherCurvature2DAngleUP0D(UnaryPredicate0D):
def __init__(self, a):
UnaryPredicate0D.__init__(self)
self._a = a
self.func = Curvature2DAngleF0D()
def __call__(self, inter):
return (self.func(inter) > self._a)
Definition
Predicates
# select visible edges with the 'Contour' edge nature
Operators.select(QuantitativeInvisibilityUP1D(0))
Usage
Stroke Shaders
Freestyle's stroke shaders apply a series of manipulations to every stroke vertex
class MyShader(StrokeShader):
def __init__(self, *args):
#initialize all the stuff
Strokeshader.__init__(self)
def shade(self, stroke):
# give every stroke vertex a red color
for sv in stroke:
sv.attribute.color = (1, 0, 0)
Definition
Stroke Shaders
class pyIncreasingThicknessShader(StrokeShader):
"""
Increasingly thickens the stroke.
"""
def __init__(self, thicknessMin, thicknessMax):
StrokeShader.__init__(self)
self._thicknessMin = thicknessMin
self._thicknessMax = thicknessMax
def shade(self, stroke):
n = len(stroke)
for i, svert in enumerate(stroke):
c = i / n
if i < (n * 0.5):
t = (1.0 - c) * self._thicknessMin + c * self._thicknessMax
else:
t = (1.0 - c) * self._thicknessMax + c * self._thicknessMin
svert.attribute.thickness = (t / 2.0, t / 2.0)
Definition
Stroke Shaders
shaders_list = [
ConstantThicknessShader(5.0),
IncreasingColorShader(0.8,0,0,1,0.1,0,0,1),
]
Operators.create(TrueUP1D(), shaders_list)
Usage
Iterators
class MyShader(StrokeShader):
def __init__(self, *args):
#initialize all the stuff
Strokeshader.__init__(self)
def shade(self, stroke):
# just assigning to attributes: use a normal for loop
for sv in stroke:
sv.attribute.color = (1, 0, 0)
# when using functions, explicitly use an Interface0DIterator
it = Interface0DIterator(stroke)
z_indices = tuple(self.func(it) for _ in it)
Usage
Manipulating Strokes
Stroke Objects
StrokeVertex Objects
name | type | code |
---|---|---|
location | mathutils.Vector (2d) | StrokeVertex.point |
location along the stroke | float | StrokeVertex.u |
attribute | attribute object | StrokeVertex.attribute |
Attribute Objects
name | type | example |
---|---|---|
color | rgb float triplet | (1.0, 0.5, 0.2) |
alpha | float | 0.5 |
thickness | two floats (right and
left of center) |
(1, 2) |
visibility | bool | True or False |
Final building blocks
Distribution and interpolation
# from pyDecreasingThicknessShader
def shade(self, stroke):
l = stroke.length_2d
n = len(stroke)
tMax = min(self._thicknessMax, 0.33 * l)
tMin = min(self._thicknessMin, 0.10 * l)
for i, svert in enumerate(stroke):
# c will go from 0.0 to 1.0
c = i / n
# the thickness of vertex #1 = tMax, of #n = tMin
t = (1.0 - c) * tMax + c * tMin
svert.attribute.thickness = (t / 2.0, t / 2.0)
Vector math
#from pyBackboneStretcherShader
def shade(self, stroke):
# get start and end points
v0, vn = stroke[0], stroke[-1]
p0, pn = v0.point, vn.point
# get the direction
d1 = (p0 - stroke[ 1].point).normalized()
dn = (pn - stroke[-2].point).normalized()
v0.point += d1 * self._l
vn.point += dn * self._l
stroke.update_length()
Freestyle Callbacks
Freestyle Callbacks
# lists of callback functions
# WARNING: highly experimental, not a stable API
callbacks_lineset_pre = []
callbacks_modifiers_post = []
callbacks_lineset_post = []
Definition
yes, it's this simple
Freestyle Callbacks
Usage
Have a look at the Freestyle SVG exporter add-on
Today's projects
Project Ideas
General add-on concept: