Pose
DazPose
- class dazpy.DazPose(figure, bones, morphs, props)[source]
Bases:
objectA snapshot of a figure’s complete pose state.
Stores bone rotations (Euler XYZ degrees), geometry morph values, and node-level numeric properties. Sparse by default — zero values are omitted so the object stays compact for morph-heavy figures.
Typical workflow:
from dazpy import DazScene, DazPose scene = DazScene() figure = scene.find_skeleton_by_label("Genesis 9") neutral = DazPose.capture(figure) neutral.save("neutral.json") smile = DazPose.load("smile.json") neutral.lerp(smile, t=0.5).apply(figure)
JSON schema (same as
character_state.pyoutput):{ "figure": "Genesis 9", "bones": {"hip": [0, 2.3, 0], "rForeArm": [0, 0, -45]}, "morphs": {"PHMSmileFull": 0.8}, "props": {"facs_ctrl_SmileFullFace": 0.5} }
- Parameters:
- classmethod capture(skeleton)[source]
Capture the current pose of skeleton in a single HTTP call.
Records all non-zero bone rotations, morph values, and node-level numeric properties. Zero values are omitted (sparse storage); they are implied as 0.0 during
lerp()andapply_full().- Parameters:
skeleton (DazSkeleton) – The figure to capture.
- Returns:
A new
DazPose.- Raises:
NodeNotFoundError – If the skeleton is not found.
- Return type:
- classmethod load(path)[source]
Load a pose from a JSON file.
Accepts files produced by
save()and by thecharacter_state.pyexample script.
- lerp(other, t)[source]
Linearly interpolate between this pose and other.
Missing keys in either pose are treated as zero. The result uses the figure label from self.
This is a pure-Python operation — no HTTP round-trip.
- apply(skeleton, *, retry_on_busy=True, max_wait=30.0)[source]
Apply this pose to skeleton in a single HTTP call.
Only channels present in the pose are changed. Bones and morphs not stored in the pose are left at their current values. Use
apply_full()when you need a clean, authoritative reset to exactly this pose.- Parameters:
skeleton (DazSkeleton) – The figure to pose.
retry_on_busy (bool) – If
True(the default), transparently retry with backoff when the server reportsStudioBusyErrorfor the HTTP call itself, up to max_wait seconds total. This does not protect against DAZ Studio’s main thread bailing partway through the script under contention (see dpi-mxq) – callers that need correctness guarantees on the result should verify with an independent read-back after applying.max_wait (float) – Maximum total seconds to retry when retry_on_busy is
True.
- apply_full(skeleton, *, retry_on_busy=True, max_wait=30.0)[source]
Apply this pose and zero every channel not present in the pose.
Unlike
apply(), every bone rotation, morph, and node property on the skeleton is explicitly set — channels absent from the pose are driven to zero. Use this to restore a known baseline cleanly.- Parameters:
skeleton (DazSkeleton) – The figure to pose.
retry_on_busy (bool) – If
True(the default), transparently retry with backoff when the server reportsStudioBusyErrorfor the HTTP call itself, up to max_wait seconds total. This only covers a busy error raised for the whole call – it does not detect DAZ Studio’s main thread bailing partway through the script under contention and leaving some writes unapplied while the HTTP call still reports success (see dpi-mxq). Callers that need correctness guarantees (e.g. checkpoint restore) must independently verify the result with a freshcapture()read-back, such asapply()does.max_wait (float) – Maximum total seconds to retry when retry_on_busy is
True.
dazpy.poses
Common pose operations built on DazPose.
- dazpy.apply_pose(skeleton, pose)[source]
Apply pose to skeleton in a single HTTP call.
- Parameters:
skeleton (DazSkeleton) – The figure to pose.
pose (DazPose | str | Path) – A
DazPoseinstance, or a path to a pose JSON file (loaded viaload()first).
- dazpy.reset_transforms(node)[source]
Reset node’s local position and rotation to zero, and scale to 1.0.
Works on any
DazNode— camera, prop, or figure root. Uses a single DazScript evaluation viaset_transform().- Parameters:
node (DazNode) – The node to reset.
- dazpy.zero_figure(skeleton, *, include_props=False)[source]
Drive every bone rotation and morph on skeleton to zero.
The default (
include_props=False) is what guarantees this function never touches the figure’s root position/rotation/scale — usereset_transforms()for that instead.- Parameters:
skeleton (DazSkeleton) – The figure to zero.
include_props (bool) –
When
True, node-level numeric properties are also zeroed, viaapply_full(). This is opt-in, not the default:apply_fullwrites 0 for every property returned by the figure’s node-property enumeration that is absent from the pose it’s given, and every other caller passes a captured pose whosepropsdict already contains those values.zero_figureinstead passes an emptyprops={}, so withinclude_props=Truethat “absent → 0” fallback can drive built-in transform-adjacent dials — e.g. the figure’s general Scale property (see the DzERCLink comment indazpy/_pose.pyaround line 117-125) — to 0, contradicting the “does not touch root transform” guarantee. Only passTrueif you know your rig doesn’t route transforms through its node-property list.The two modes also differ in how they write ERC-driven channels: the
Truepath goes throughapply_full(), which preferssetRawValue()writes to avoid double-applyingDzERCLinkcontroller contributions (see the comment indazpy/_pose.pyaround line 117-125). TheFalsepath usesset_bone_rotations()/set_morph_values(), which use plainsetValue()writes. On ERC-driven channels the two modes can therefore leave the figure in different end states.