Interactive plotting and Blender properties
Suppose we want to control parameters with Blender’s GUI instead of code. Here is the most basic example:
from ddg.visualization.blender import props
def print_props(a, b, c, s):
print(f"{a, b, c, s = }")
props.add_props_with_callback(
print_props,
("a", "b", "c", "s"), # labels for the properties
-1.0, # arbitrarily chosen initial parameters
1.0,
0.5,
50,
)
If we adjust any of the Blender properties a, b, c, d, Blender will execute the callback print_props, which just prints their values to the console.
For more interesting examples involving geometric objects, we could replace print_props by a function that links Blender objects.
Instead, we will do something more sophisticated to ensure caching of the possibly expensive callback.
First we write a function blender_objects that returns a sequence of unlinked Blender objects.
This means that we need to pass link=False to to_blender_object.
import bpy
import numpy as np
import ddg
from ddg.visualization.blender import props
from ddg.geometry import quadrics
def quadric_and_sphere(a, b, c):
quadric = quadrics.Quadric(np.diag([a, b, c, -1]))
sphere = quadrics.Quadric(np.diag([1, 1, 1, -(a**2) - b**2 - c**2 - 1]))
return quadric, sphere
# Splitting up the construction and displaying in Blender is recommended
# because it decouples the geometric construction from the displaying backend.
def blender_objects(
a,
b,
c,
s=10,
):
"""A sequence of unlinked Blender objects.
The argument s controls the sampling, increase it for finer meshes.
"""
nets = [
ddg.sample_smooth_net(ddg.to_smooth_net(quadric, affine=True), (0.1, s, "c"))
for quadric in quadric_and_sphere(a, b, c)
]
return [ddg.to_blender_object(net, link=False) for net in nets]
Then we add the properties.
# Hide and unhide objects and caches blender_objects calls in subcollections.
props.add_props_with_callback(
callback,
("a", "b", "c"), # labels for the properties
-1.0, # arbitrarily chosen initial parameters
1.0,
0.5,
s=50,
)
The callback function will link the output of blender_objects to a Blender collection named "construction".
This collection will also serve as a cache which you can clear at will.
Alternatively, callback could clear the collection before linking new objects:
1.0,
props.add_props_with_callback(
callback,
("a", "b", "c"), # labels for the properties
-1.0, # arbitrarily chosen initial parameters
1.0,
0.5,
s=50,
)
Or overwrite the collection:
callback = props.overwrite_callback("construction", blender_objects)
props.add_props_with_callback(
callback,
("a", "b", "c"), # labels for the properties
-1.0, # arbitrarily chosen initial parameters
1.0,
0.5,
s=50,
)
It is possible to switch callbacks on the fly.
Simply run add_props_with_callback with a new callback.
Saving and reloading .blend files
Since closing Blender loses all Python state, the callback will no longer exist after saving and reloading a .blend file. To fix this, run the script again. Afterwards the callback will work as expected.
Animating properties
Properties can be keyframed and interpolated.
Since animations are likely to be replayed multiple times, it is highly recommended to use hide_callback or overwrite_callback to cache the results.
Finer control over properties
If you want to set the description, minimum or maximum value of a property and more, you’ll have to define the properties yourself:
from functools import partial
props.add_props_with_callback_from_constructors(
callback,
{
"a": partial(bpy.props.FloatProperty, default=-1.0, min=-1.0, max=5.0),
"b": partial(bpy.props.FloatProperty, default=1.0),
"c": partial(bpy.props.FloatProperty, default=0.5),
},
{"s": partial(bpy.props.IntProperty, default=50)},
)
Consult the Blender documentation for all the options. The callback code remains the same.