Dynamic Structure Updates
Viso is designed for live manipulation – structures can be updated mid-session by computational backends (Rosetta energy minimization, ML structure prediction) or user actions (mutations, drag operations). This chapter covers the APIs and patterns for dynamic updates.
Per-Entity Coordinate Updates
The primary API for updating structure coordinates is update_entity_coords(), which updates the engine’s source-of-truth entities first, then propagates to the scene and animation system:
#![allow(unused)]
fn main() {
// Update a specific entity's coordinates with animation
engine.update_entity_coords(entity_id, new_coords, Transition::smooth());
}
The engine looks up the entity’s assigned behavior (or uses the provided transition) and dispatches to the per-entity animation system.
Per-Entity Behavior Control
Before updating an entity, callers can assign a specific animation behavior:
#![allow(unused)]
fn main() {
// Set behavior BEFORE updating coordinates
engine.set_entity_behavior(entity_id, Transition::collapse_expand(
Duration::from_millis(200),
Duration::from_millis(300),
));
// Update coordinates — engine uses CollapseExpand for this entity
engine.update_entity_coords(entity_id, new_coords, Transition::smooth());
// After the animation completes, the behavior remains set.
// To revert to default:
engine.clear_entity_behavior(entity_id);
}
Transitions
Every update can specify a Transition that controls the visual animation:
#![allow(unused)]
fn main() {
// Instant snap (for loading, no animation)
Transition::snap()
// Standard smooth interpolation (300ms ease-out)
Transition::smooth()
// Two-phase: sidechains collapse to CA, backbone moves, sidechains expand
Transition::collapse_expand(
Duration::from_millis(300),
Duration::from_millis(300),
)
// Two-phase: backbone animates first, then sidechains expand
Transition::backbone_then_expand(
Duration::from_millis(400),
Duration::from_millis(600),
)
// Builder flags
Transition::collapse_expand(
Duration::from_millis(200),
Duration::from_millis(300),
)
.allowing_size_change()
.suppressing_initial_sidechains()
}
See Animation System for details on preset behaviors and phase evaluation.
Preemption
If a new update arrives while an animation is playing, the current visual position becomes the new animation’s start state and the timer resets. This provides responsive feedback during rapid update cycles (e.g., Rosetta wiggle).
Band and Pull Visualization
Bands (Constraints)
Bands visualize distance constraints between atoms:
#![allow(unused)]
fn main() {
engine.execute(VisoCommand::UpdateBands {
bands: vec![
BandInfo {
endpoint_a: Vec3::new(10.0, 20.0, 30.0),
endpoint_b: Vec3::new(15.0, 22.0, 28.0),
strength: 1.0,
target_length: 3.5,
residue_idx: 42,
band_type: BandType::Default,
is_pull: false,
is_push: false,
is_disabled: false,
is_space_pull: false,
from_script: false,
},
],
});
}
Band visual properties:
- Radius scales with strength (0.1 to 0.4 angstroms)
- Color depends on band type: default (purple), backbone (yellow-orange), disulfide (yellow-green), H-bond (cyan)
- Disabled bands are gray
Pulls (Active Drag)
A pull is a temporary constraint while the user drags a residue:
#![allow(unused)]
fn main() {
engine.execute(VisoCommand::UpdatePull {
pull: Some(PullInfo {
atom_pos: atom_world_position,
target_pos: mouse_world_position,
residue_idx: 42,
}),
});
// Clear when drag ends
engine.execute(VisoCommand::UpdatePull { pull: None });
}
Pulls render as a purple cylinder from atom to mouse position with a cone/arrow at the mouse end.
Animation Control
#![allow(unused)]
fn main() {
animator.skip(); // Jump to final state
animator.cancel(); // Stay at current visual state
animator.set_enabled(false); // Disable all animation (snap)
}