We provide a complete example add-on in python/examples/bpy-plugin/ demonstrating how to build Blender plugins with trueform. This page walks through the key patterns.
The most powerful pattern is live preview—showing intersection curves that update as objects move. This requires three components working together.
First, register trueform's scene handlers when your add-on loads:
from trueform import bpy as tbpy
def register():
# Register trueform scene handlers for mesh caching
tbpy.register()
def unregister():
tbpy.unregister()
This enables tbpy.scene.get() to return cached meshes with automatic dirty tracking.
The preview function computes intersection curves and creates/updates a curves object:
import trueform as tf
from trueform import bpy as tbpy
import bpy
_PREVIEW_CURVES_NAME = None
def _update_preview(context):
props = context.scene.my_addon
obj_a, obj_b = props.target_a, props.target_b
if not obj_a or not obj_b:
_remove_preview_curves()
return
# Get cached meshes with world transforms
mesh_a = tbpy.scene.get(obj_a)
mesh_b = tbpy.scene.get(obj_b)
# Compute intersection curves
paths, points = tf.intersection_curves(mesh_a, mesh_b)
global _PREVIEW_CURVES_NAME
if len(paths) > 0:
# Create or update curves object
existing = bpy.data.objects.get(_PREVIEW_CURVES_NAME)
if not existing:
curves_obj = tbpy.convert.to_blender_curves(paths, points, "Preview_Curves")
_PREVIEW_CURVES_NAME = curves_obj.name
else:
# Reuse existing object, just update curve data
old_data = existing.data
existing.data = tbpy.convert.make_curves(paths, points, "Preview_Curves")
bpy.data.curves.remove(old_data)
# Style the curves
curves_obj.data.bevel_depth = 0.02
else:
_remove_preview_curves()
The key insight: reuse the existing curves object when possible, only replacing its data. This avoids flickering and keeps the object in the same position in the outliner.
To update the preview when objects change, register a depsgraph handler:
def _on_depsgraph_update(scene, depsgraph):
props = scene.my_addon
targets = {props.target_a, props.target_b}
# Check if any target object changed
for upd in depsgraph.updates:
if upd.id.original in targets:
_update_preview(bpy.context)
break
# Register handler when preview is enabled
def _on_preview_toggle(self, context):
if self.interactive_preview:
if _on_depsgraph_update not in bpy.app.handlers.depsgraph_update_post:
bpy.app.handlers.depsgraph_update_post.append(_on_depsgraph_update)
_update_preview(context)
else:
if _on_depsgraph_update in bpy.app.handlers.depsgraph_update_post:
bpy.app.handlers.depsgraph_update_post.remove(_on_depsgraph_update)
_remove_preview_curves()
The handler checks if any update affects our target objects, then triggers a preview refresh. This is efficient—we only recompute when relevant objects change.
The operator applies the selected boolean operation using the high-level scene functions:
class MESH_OT_boolean(bpy.types.Operator):
bl_idname = "mesh.my_boolean"
bl_label = "Apply Boolean"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
props = context.scene.my_addon
if not props.target_a or not props.target_b:
self.report({'WARNING'}, "Select two objects")
return {'CANCELLED'}
# Remove preview curves before applying
_remove_preview_curves()
# Apply boolean operation
op_map = {
'DIFFERENCE': tbpy.scene.boolean_difference,
'UNION': tbpy.scene.boolean_union,
'INTERSECTION': tbpy.scene.boolean_intersection
}
result = op_map[props.operation](
props.target_a,
props.target_b,
name=f"Boolean_{props.target_a.name}"
)
# Optionally hide input objects
if props.hide_inputs:
props.target_a.hide_set(True)
props.target_b.hide_set(True)
return {'FINISHED'}
The scene functions handle mesh caching internally—you just pass Blender objects and get a Blender object back.
Properties can trigger preview updates when changed:
class MyAddonProperties(bpy.types.PropertyGroup):
target_a: bpy.props.PointerProperty(
name="Mesh A",
type=bpy.types.Object,
poll=lambda s, o: o.type == 'MESH',
update=lambda s, c: _update_preview(c) # Refresh on change
)
target_b: bpy.props.PointerProperty(
name="Mesh B",
type=bpy.types.Object,
poll=lambda s, o: o.type == 'MESH',
update=lambda s, c: _update_preview(c)
)
operation: bpy.props.EnumProperty(
name="Operation",
items=[
('DIFFERENCE', "Difference", ""),
('UNION', "Union", ""),
('INTERSECTION', "Intersection", "")
],
update=lambda s, c: _update_preview(c)
)
interactive_preview: bpy.props.BoolProperty(
name="Live Preview",
default=True,
update=_on_preview_toggle # Toggle handler registration
)
The update callbacks ensure the preview stays synchronized with the UI.
The scene module's caching makes live preview fast:
This means dragging an object in the viewport only triggers a rebuild when scene.get() is called, not on every frame of the drag. Trueform is fast enough that no throttling is needed—the preview updates smoothly even during continuous interactions.