Freestyle & Python
Building Freestyle-related tools in Python
About me
My work on Freestyle
- API improvements
- for loop constructs
- SVG exporter
Today
introduction
starting projects
lunch
continue coding
finishing up
presenting results
end
What you'll need
- Recent version of blender (builder.blender.org/download/)
- good text editor
- GIT
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
- Functions
- Predicates
- Stroke Shaders
- Iterators
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
- StrokeVertexIterator: on normal iteration
- Interface0DIterator: when using Functions
Manipulating Strokes
Stroke Objects
- Are a collection of StrokeVertex objects
- can be iterated over
- are the second argument to a shade function
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
- Make a basic Stroke Shader
- Make it work from an addon
- Make a more advanced Stroke Shader
- optimise/make UI for slices.py
- Try and make pixelate.py have no external dependencies (so remove ImageMagick)
- UI/extra features to stroke_anim.py
- Freestyle to Grease Pencil converter
- Freestyle to curves converter
- curvature-dependent Stroke Shaders
- ...
Project Ideas
General add-on concept:
- make blender auto-reload changed files
Freestyle
By folkert de vries
Freestyle
- 102