π Blender Python API ReferenceΒΆ
Every public class and method listed here is reachable after from zozo_contact_solver import solver.
See π Blender Python API for a narrative walkthrough of the same surface.
Classes:
- class SolverΒΆ
Top-level entry point for the ZOZO Contact Solver.
Available as
solverwhen imported via:from zozo_contact_solver import solverScene parameters are accessed via
param(aSceneParamproxy). Groups, pins, and invisible colliders are created via the methods below.Unrecognized attribute access falls through to
bpy.ops.zozo_contact_solver.<name>(), so every operator registered under that namespace (including every MCP handler) can be called as a method onsolver.Example:
solver.param.gravity = (0, 0, -9.8) group = solver.create_group("Sphere", type="SOLID") group.add("Sphere") group.param.solid_density = 1000
- create_group(name: str = '', type: str = 'SOLID') GroupΒΆ
Create a new dynamics group.
- Parameters:
name β Display name for the group. Empty string leaves the auto-generated name in place.
type β One of
"SOLID","SHELL","ROD","STATIC".
- Returns:
A
Groupproxy for the newly created group.
Example:
group = solver.create_group("Shirt", type="SHELL") group.add("Shirt")
- get_group(group_uuid: str) GroupΒΆ
Look up a group by UUID.
- Parameters:
group_uuid β UUID string of the group.
- Returns:
A
Groupproxy.- Raises:
KeyError β If the group does not exist.
Example:
uuid = solver.get_groups()[0].uuid group = solver.get_group(uuid)
- get_groups() list[Group]ΒΆ
Return
Groupproxies for every active group.Example:
for group in solver.get_groups(): print(group.uuid)
- delete_all_groups() SolverΒΆ
Delete every active group and the pins they own.
- Returns:
selffor chaining.
Example:
solver.delete_all_groups()
- clear() SolverΒΆ
Reset the entire solver state to defaults.
Deletes every active group, resets scene parameters to their property defaults, clears merge pairs, invisible colliders, dynamic parameters, previously fetched frames, saved pin keyframes, and any residual
MESH_CACHEmodifiers on mesh objects. Call this at the top of any script that needs a clean slate.- Returns:
selffor chaining.
Example:
solver.clear() solver.param.gravity = (0, 0, -9.8)
- snap(object_a: str, object_b: str) SolverΒΆ
Translate object_a so its nearest vertex lands on object_b.
- Parameters:
object_a β Name of the mesh that moves.
object_b β Name of the mesh that stays in place.
- Returns:
selffor chaining.- Raises:
ValueError β If either object is missing, not a mesh, or validation in the underlying mutation service fails.
Example:
solver.snap("Shirt", "Mannequin")
- add_merge_pair(object_a: str, object_b: str) SolverΒΆ
Mark two objects to be merged at their shared contact.
- Parameters:
object_a β Name of the first mesh.
object_b β Name of the second mesh.
- Returns:
selffor chaining.- Raises:
ValueError β If either object is missing, not a mesh, or the pair is invalid.
Example:
solver.add_merge_pair("SleeveLeft", "BodyLeft")
- remove_merge_pair(object_a: str, object_b: str) SolverΒΆ
Remove a previously added merge pair.
The ordering of object_a and object_b does not matter; the pair is matched by UUID in either direction.
- Parameters:
object_a β Name of the first mesh.
object_b β Name of the second mesh.
- Returns:
selffor chaining.- Raises:
ValueError β If validation fails for the given pair.
Example:
solver.remove_merge_pair("SleeveLeft", "BodyLeft")
- get_merge_pairs() list[tuple[str, str]]ΒΆ
Return every merge pair as a list of
(object_a, object_b)tuples.Example:
for a, b in solver.get_merge_pairs(): print(f"{a} <-> {b}")
- clear_merge_pairs() SolverΒΆ
Remove every merge pair.
- Returns:
selffor chaining.
Example:
solver.clear_merge_pairs()
- add_wall(position, normal) WallΒΆ
Add an invisible infinite-plane wall collider.
- Parameters:
position β
(x, y, z)world-space point on the plane.normal β
(nx, ny, nz)outward-facing plane normal. Need not be unit-length.
- Returns:
A chainable
Wallbuilder bound to the newly added collider.- Raises:
ValueError β If the position or normal fails vec3 validation.
Example:
solver.add_wall(position=(0, 0, 0), normal=(0, 0, 1))
- add_sphere(position, radius) SphereΒΆ
Add an invisible sphere collider.
- Parameters:
position β
(x, y, z)world-space center.radius β Sphere radius.
- Returns:
A chainable
Spherebuilder bound to the newly added collider.- Raises:
ValueError β If the position or radius fails validation.
Example:
solver.add_sphere(position=(0, 0, 1.0), radius=0.25)
- get_invisible_colliders() listΒΆ
Return every invisible collider as a list of
(type, name)tuples.type is one of
"WALL"or"SPHERE".Example:
for kind, name in solver.get_invisible_colliders(): print(kind, name)
- class SceneParamΒΆ
Attribute proxy for scene and SSH/connection parameters.
Accessed as
Solver.param. Supports both get and set via attribute access. Writes go through thezozo_contact_solver.setoperator (with auto type coercion), reads fall through to the sceneβs addon state or SSH state.gravityis an alias forgravity_3d.Example:
solver.param.step_size = 0.004 print(solver.param.gravity)
Dynamic (keyframed) parameters are accessed via
dyn():solver.param.dyn("gravity").time(60).hold().time(61).change((0, 0, 9.8))- dyn(key: str) DynParamΒΆ
Select a parameter for dynamic keyframing.
- Parameters:
key β One of
"gravity","wind","air_density","air_friction","vertex_air_damp".- Returns:
A chainable
DynParambuilder.- Raises:
ValueError β If key is not one of the valid dynamic keys.
Example:
solver.param.dyn("gravity").time(60).hold().time(61).change((0, 0, 9.8))
- class DynParamΒΆ
Fluent builder for dynamic scene parameter keyframes.
Mirrors the frontend
session.param.dyn()API but uses frames instead of seconds. Obtained fromSceneParam.dyn().Valid parameter keys:
"gravity","wind","air_density","air_friction","vertex_air_damp".Frames must be strictly increasing within a chain. Every mutating method returns
selfso operations chain.Example:
solver.param.dyn("gravity").time(60).hold().time(61).change((0, 0, 9.8)) solver.param.dyn("wind").time(30).hold().time(31).change((0, 1, 0), strength=5.0)
- time(frame: int) DynParamΒΆ
Advance the frame cursor.
- Parameters:
frame β Target frame (must be strictly greater than the current cursor position).
- Returns:
selffor chaining.- Raises:
ValueError β If frame is not strictly increasing.
Example:
solver.param.dyn("gravity").time(60).hold().time(61).change((0, 0, 9.8))
- hold() DynParamΒΆ
Hold the previous value at the current cursor frame (step function).
- Returns:
selffor chaining.
Example:
solver.param.dyn("gravity").time(60).hold().time(61).change((0, 0, 9.8))
- change(value, strength=None) DynParamΒΆ
Set a new value at the current cursor frame.
- Parameters:
value β For
"gravity", an(x, y, z)tuple. For"wind", an(x, y, z)direction tuple. For scalar keys ("air_density","air_friction","vertex_air_damp"), afloat.strength β Wind strength (only for
"wind").
- Returns:
selffor chaining.
Example:
solver.param.dyn("wind").time(30).hold().time(31).change((0, 1, 0), strength=5.0)
- class GroupΒΆ
A dynamics group proxy.
Created via
Solver.create_group(). Material parameters are accessed viaparam. Every mutating method returnsselfso operations chain.Example:
group = solver.create_group("Shirt", type="SHELL") group.add("Shirt").set_overlay_color(0.9, 0.2, 0.1) group.param.friction = 0.5 group.param.shell_density = 1.0
- property uuidΒΆ
- Type:
str
The UUID of this group. Stable across renames.
Example:
group = solver.create_group("Shirt", type="SHELL") same_group = solver.get_group(group.uuid)
- property paramΒΆ
- Type:
Material and simulation parameter proxy. See
GroupParam.Example:
group.param.friction = 0.5 group.param.shell_density = 1.0
- set_overlay_color(r: float, g: float, b: float, a: float = 1.0) GroupΒΆ
Set the viewport overlay color for this group and enable it.
- Parameters:
r β Red channel in
[0, 1].g β Green channel in
[0, 1].b β Blue channel in
[0, 1].a β Alpha in
[0, 1](default1.0).
- Returns:
selffor chaining.
Example:
group.set_overlay_color(0.9, 0.2, 0.1) # red overlay
- add(*object_names: str) GroupΒΆ
Add mesh objects to this group by name.
- Parameters:
*object_names β One or more Blender object names.
- Returns:
selffor chaining.
Example:
group.add("Shirt", "Skirt", "Sleeve")
- remove(object_name: str) GroupΒΆ
Remove an object from this group.
- Parameters:
object_name β Name of the object to remove.
- Returns:
selffor chaining.
Example:
group.remove("Sleeve")
- create_pin(object_name: str, vertex_group_name: str) PinΒΆ
Pin a vertex group so its vertices stay fixed during simulation.
- Parameters:
object_name β Name of the mesh object.
vertex_group_name β Name of the vertex group on that object.
- Returns:
A
Pinproxy for the newly created pin.- Raises:
ValueError β If the object is missing, not a mesh, or the vertex group does not exist on it.
Example:
pin = group.create_pin("Cloth", "collar") pin.move(delta=(0, 0, 0.2), frame=60)
- get_pins() list[Pin]ΒΆ
Return all pins in this group as
Pinproxies.Example:
for pin in group.get_pins(): pin.clear_keyframes()
- clear_keyframes() GroupΒΆ
Delete all keyframes for all pins in this group.
Convenience method that calls
Pin.clear_keyframes()on every pin returned byget_pins().- Returns:
selffor chaining.
Example:
group.clear_keyframes()
- delete() NoneΒΆ
Delete this group and every pin it owns.
Example:
group.delete()
- class GroupParamΒΆ
Proxy for material and simulation parameters on a group.
Accessed via
Group.param. Attribute access is whitelisted: reading or writing a name outside the whitelist raisesAttributeError.Whitelisted attributes:
Solver model:
solid_model,shell_modelDensity:
solid_density,shell_density,rod_densityYoungβs modulus:
solid_young_modulus,shell_young_modulus,rod_young_modulusPoisson ratio:
solid_poisson_ratio,shell_poisson_ratioContact:
friction,contact_gap,contact_gap_rat,contact_offset,contact_offset_ratStrain limit:
enable_strain_limit,strain_limitInflation:
enable_inflate,inflate_pressurePlasticity:
enable_plasticity,plasticity,plasticity_thresholdBend plasticity:
enable_bend_plasticity,bend_plasticity,bend_plasticity_threshold,bend_rest_angle_sourceShell-specific:
bend,shrink,shrink_x,shrink_y,stitch_stiffness
Example:
# Solver model group.param.solid_model = "ARAP" group.param.shell_model = "ARAP" # Density (kg/m^3 for solid/shell, kg/m for rod) group.param.solid_density = 1000.0 group.param.shell_density = 0.3 group.param.rod_density = 0.05 # Young's modulus (Pa) and Poisson ratio group.param.solid_young_modulus = 1.0e6 group.param.shell_young_modulus = 5.0e5 group.param.rod_young_modulus = 1.0e7 group.param.solid_poisson_ratio = 0.45 group.param.shell_poisson_ratio = 0.30 # Contact group.param.friction = 0.5 group.param.contact_gap = 0.001 group.param.contact_gap_rat = 0.1 group.param.contact_offset = 0.002 group.param.contact_offset_rat = 0.2 # Strain limit group.param.enable_strain_limit = True group.param.strain_limit = 1.05 # Inflation group.param.enable_inflate = True group.param.inflate_pressure = 100.0 # Plasticity (shells and tets only) group.param.enable_plasticity = True group.param.plasticity = 0.3 group.param.plasticity_threshold = 0.2 group.param.enable_bend_plasticity = True group.param.bend_plasticity = 0.5 group.param.bend_plasticity_threshold = 0.1 group.param.bend_rest_angle_source = "REST" # Shell-specific group.param.bend = 1.0e-4 group.param.shrink = 0.98 group.param.shrink_x = 0.99 group.param.shrink_y = 0.97 group.param.stitch_stiffness = 5.0e4
- class PinΒΆ
A pinned vertex group bound to a dynamics group.
Created via
Group.create_pin(object_name, vertex_group_name). Every mutating method returnsselfso operations chain.Example:
pin = group.create_pin("Cloth", "hem") pin.move(delta=(0, 0, 1.0), frame=60) # lift hem over 60 frames pin.unpin(frame=120) # release at frame 120
- property object_nameΒΆ
- Type:
str
Name of the mesh object this pin belongs to.
Example:
pin = group.create_pin("Cloth", "hem") print(pin.object_name) # "Cloth"
- property vertex_group_nameΒΆ
- Type:
str
Name of the vertex group this pin targets.
Example:
pin = group.create_pin("Cloth", "hem") print(pin.vertex_group_name) # "hem"
- pull(strength: float = 1.0) PinΒΆ
Use pull force instead of hard pin constraint.
Pull allows the vertices to move but applies a restoring force toward their target position.
- Parameters:
strength β Pull force strength (default 1.0).
- Returns:
selffor chaining.
Example:
group.create_pin("Cloth", "shoulder").pull(strength=2.5)
- spin(axis: tuple[float, float, float] = (1, 0, 0), angular_velocity: float = 360.0, flip: bool = False, center: tuple[float, float, float] | None = None, center_mode: str | None = None, center_direction: tuple[float, float, float] | None = None, center_vertex: int | None = None, frame_start: int = 1, frame_end: int = 60, transition: str = 'LINEAR') PinΒΆ
Add a spin operation to this pin.
- Parameters:
axis β Rotation axis vector.
angular_velocity β Degrees per second.
flip β Reverse spin direction.
center β Center of rotation (for ABSOLUTE mode).
center_mode β
"CENTROID","ABSOLUTE","MAX_TOWARDS", or"VERTEX". IfNone, inferred from other args (Nonecenter β"CENTROID").center_direction β Direction for
MAX_TOWARDSmode.center_vertex β Vertex index for
VERTEXmode.frame_start β Start frame.
frame_end β End frame.
transition β
"LINEAR"or"SMOOTH".
- Returns:
selffor chaining.
Example:
# Spin about the centroid at 180 deg/s for frames 1-60 pin.spin(axis=(0, 0, 1), angular_velocity=180.0) # Spin about an absolute world-space pivot pin.spin(axis=(0, 1, 0), center=(0, 0, 1), frame_start=30, frame_end=90)
- scale(factor: float = 1.0, center: tuple[float, float, float] | None = None, center_mode: str | None = None, center_direction: tuple[float, float, float] | None = None, center_vertex: int | None = None, frame_start: int = 1, frame_end: int = 60, transition: str = 'LINEAR') PinΒΆ
Add a scale operation to this pin.
- Parameters:
factor β Scale factor.
center β Center point (for
ABSOLUTEmode).center_mode β
"CENTROID","ABSOLUTE","MAX_TOWARDS", or"VERTEX". IfNone, inferred from other args (Nonecenter β"CENTROID").center_direction β Direction for
MAX_TOWARDSmode.center_vertex β Vertex index for
VERTEXmode.frame_start β Start frame.
frame_end β End frame.
transition β
"LINEAR"or"SMOOTH".
- Returns:
selffor chaining.
Example:
# Shrink to 50% over frames 1-60 about the centroid pin.scale(factor=0.5, transition="SMOOTH")
- torque(magnitude: float = 1.0, axis_component: str = 'PC3', flip: bool = False, frame_start: int = 1, frame_end: int = 60) PinΒΆ
Add a torque operation to this pin.
Applies a rotational force around a PCA-computed axis.
- Parameters:
magnitude β Torque in NΒ·m.
axis_component β
"PC1"(major),"PC2"(middle), or"PC3"(minor).flip β Reverse torque direction.
frame_start β Start frame.
frame_end β End frame.
- Returns:
selffor chaining.
Example:
pin.torque(magnitude=2.0, axis_component="PC1", frame_start=1, frame_end=30)
- move_by(delta: tuple[float, float, float] = (0, 0, 0), frame_start: int = 1, frame_end: int = 60, transition: str = 'LINEAR') PinΒΆ
Ramp a translation of the pinned vertices over a frame range.
- Parameters:
delta β
(dx, dy, dz)offset.frame_start β Start frame.
frame_end β End frame.
transition β
"LINEAR"or"SMOOTH".
- Returns:
selffor chaining.
Example:
# Lift 1.0m along +Z between frames 10 and 90 pin.move_by(delta=(0, 0, 1.0), frame_start=10, frame_end=90, transition="SMOOTH")
- unpin(frame: int) PinΒΆ
Mark this pin to be released at the given frame.
Sets the duration on the underlying UI property so the encoder and clear logic are aware. Also prevents future
move(frame=N)calls where N >= frame.- Parameters:
frame β Frame number at which the pin is released.
- Returns:
selffor chaining.
Example:
pin.move(delta=(0, 0, 1), frame=60).unpin(frame=120)
- move(delta: tuple[float, float, float] = (0, 0, 0), frame: int | None = None) PinΒΆ
Move pin vertices by delta and optionally keyframe at frame.
On the first call with frame, the current vertex positions are automatically keyframed at the current scene frame before any movement is applied. Ignored if frame >= the unpin frame.
- Parameters:
delta β
(dx, dy, dz)offset to apply (default no movement).frame β Frame number to keyframe at.
Nonemeans no keyframe.
- Returns:
selffor chaining.- Raises:
ValueError β If the target object is missing, not a mesh, or the vertex group does not exist on it.
Example:
pin = group.create_pin("Cloth", "hem") pin.move(delta=(0, 0, 1.0), frame=60) # auto-keyframes start pin.move(delta=(0.5, 0, 0), frame=120) # adds another keyframe
- clear_keyframes() PinΒΆ
Delete all positional keyframes for this pinβs vertices.
- Returns:
selffor chaining.
Example:
pin = group.create_pin("Cloth", "hem") pin.move(delta=(0, 0, 1.0), frame=60) pin.clear_keyframes() # wipe the animation, keep the pin
- delete() NoneΒΆ
Remove this pin from its group.
- Raises:
ValueError β If the owning group or pin item can no longer be found (for example, after
solver.clear()).
Example:
pin = group.create_pin("Cloth", "hem") pin.delete() # remove the pin entry from the group
- class WallΒΆ
Chainable builder for invisible wall colliders.
Returned by
Solver.add_wall(). Keyframe frames must be strictly increasing. Every mutating method returnsself.Example:
solver.add_wall((0, 0, 0), (0, 0, 1)).param.friction = 0.5 (solver.add_wall((0, 0, 0), (0, 1, 0)) .time(60).hold().time(61).move_to((0, 1, 0)))
- property paramΒΆ
- Type:
Collider parameter proxy. See
ColliderParam.Example:
wall = solver.add_wall((0, 0, 0), (0, 0, 1)) wall.param.friction = 0.5
- time(frame: int) WallΒΆ
Advance the keyframe cursor.
- Parameters:
frame β Target frame (must be strictly greater than the current cursor position).
- Returns:
selffor chaining.- Raises:
ValueError β If frame is not strictly increasing.
Example:
(solver.add_wall((0, 0, 0), (0, 0, 1)) .time(60).move_to((0, 0, 0.5)))
- hold() WallΒΆ
Hold the previous position at the current cursor frame.
- Returns:
selffor chaining.
Example:
(solver.add_wall((0, 0, 0), (0, 0, 1)) .time(60).hold().time(90).move_to((0, 0, 0.5)))
- move_to(position) WallΒΆ
Keyframe a new absolute position at the current cursor frame.
- Parameters:
position β
(x, y, z)world-space position.- Returns:
selffor chaining.
Example:
(solver.add_wall((0, 0, 0), (0, 0, 1)) .time(60).move_to((0, 0, 1.0)))
- move_by(delta) WallΒΆ
Keyframe a position offset from the previous keyframe.
- Parameters:
delta β
(dx, dy, dz)offset added to the previous keyframed position.- Returns:
selffor chaining.
Example:
(solver.add_wall((0, 0, 0), (0, 0, 1)) .time(60).move_by((0, 0, 0.25)))
- delete() NoneΒΆ
Remove this wall collider from the scene.
Example:
wall = solver.add_wall((0, 0, 0), (0, 0, 1)) wall.delete()
- class SphereΒΆ
Chainable builder for invisible sphere colliders.
Returned by
Solver.add_sphere(). Keyframe frames must be strictly increasing. Every mutating method returnsself.Example:
solver.add_sphere((0, 0, 0), 0.98).invert().hemisphere() (solver.add_sphere((0, 0, 0), 1.0) .time(60).hold().time(61).radius(0.5))
- property paramΒΆ
- Type:
Collider parameter proxy. See
ColliderParam.Example:
sphere = solver.add_sphere((0, 0, 0), 1.0) sphere.param.friction = 0.3
- invert() SphereΒΆ
Flip the sphere inside-out so contact is on the inside surface.
- Returns:
selffor chaining.
Example:
solver.add_sphere((0, 0, 0), 1.0).invert()
- hemisphere() SphereΒΆ
Treat this collider as a hemisphere rather than a full sphere.
- Returns:
selffor chaining.
Example:
solver.add_sphere((0, 0, 0), 1.0).hemisphere()
- time(frame: int) SphereΒΆ
Advance the keyframe cursor.
- Parameters:
frame β Target frame (must be strictly greater than the current cursor position).
- Returns:
selffor chaining.- Raises:
ValueError β If frame is not strictly increasing.
Example:
(solver.add_sphere((0, 0, 0), 1.0) .time(60).move_to((0, 0, 1.0)))
- hold() SphereΒΆ
Hold the previous position and radius at the current cursor frame.
- Returns:
selffor chaining.
Example:
(solver.add_sphere((0, 0, 0), 1.0) .time(60).hold().time(90).radius(0.5))
- move_to(position) SphereΒΆ
Keyframe a new absolute position at the current cursor frame.
- Parameters:
position β
(x, y, z)world-space position.- Returns:
selffor chaining.
Example:
(solver.add_sphere((0, 0, 0), 1.0) .time(60).move_to((0, 0, 2.0)))
- radius(r) SphereΒΆ
Keyframe a new radius at the current cursor frame.
- Parameters:
r β New radius.
- Returns:
selffor chaining.
Example:
(solver.add_sphere((0, 0, 0), 1.0) .time(60).radius(0.25)) # shrink over 60 frames
- transform_to(position, radius) SphereΒΆ
Keyframe both position and radius together.
- Parameters:
position β
(x, y, z)world-space position.radius β New radius.
- Returns:
selffor chaining.
Example:
(solver.add_sphere((0, 0, 0), 1.0) .time(60).transform_to((0, 0, 1.0), 0.5))
- delete() NoneΒΆ
Remove this sphere collider from the scene.
Example:
sphere = solver.add_sphere((0, 0, 0), 1.0) sphere.delete()
- class ColliderParamΒΆ
Attribute proxy for invisible-collider parameters.
Accessed via
Wall.paramorSphere.param. Attribute access is whitelisted: reading or writing a name outside the whitelist raisesAttributeError.Whitelisted attributes:
friction: contact friction coefficientcontact_gap: contact gap thicknessthickness: wall/sphere shell thicknessenable_active_duration:Trueto limit collider lifetimeactive_duration: number of frames the collider is active whenenable_active_durationis set
Example:
wall = solver.add_wall((0, 0, 0), (0, 0, 1)) wall.param.friction = 0.5 wall.param.contact_gap = 0.002 wall.param.thickness = 0.01 wall.param.enable_active_duration = True wall.param.active_duration = 60 # active for frames 1-60 sphere = solver.add_sphere((0, 0, 1), 0.5) sphere.param.friction = 0.3