Overlay data and guides
Add labels, measurements, declared data sources, and explicit interactions to a scene.
MolGFX exposes scene items beyond molecular representations. Use them when the item has a clear semantic owner in your application; MolGFX does not detect interactions or load volume and trajectory files for you.
Labels and measurements
Anchors can be in world coordinates or target a selection. Labels and measurements are scene items:
import molframe
import molgfx
structure = molframe.read("4hhb.cif").infer_bonds()
scene = molgfx.Scene(structure)
first = molgfx.annotation.world((0.0, 0.0, 0.0))
second = molgfx.annotation.world((4.2, 0.0, 0.0))
scene.add(molgfx.annotation.label(anchor=first, text="site"))
scene.add(molgfx.measurement.distance(first, second))measurement.angle takes three anchors; measurement.dihedral takes four. Selection anchors use molgfx.annotation.selection(structure=scene.structure_id, target="resname HEM").
Explicit overlay interactions
Author an interaction only when your application or upstream analysis supplies it:
scene.add(molgfx.interaction.explicit(
kind="hydrogen_bond",
first=first,
second=second,
))The public interaction kinds are hydrogen_bond, salt_bridge, pi_stacking, cation_pi, hydrophobic, metal_coordination, and contact. MolGFX does not expose interaction detection; use MolFrame or another analysis step to establish the overlay result first.
Volumes, properties, and trajectories
molgfx.data.source() declares the identity and optional location/format of an external overlay data source. It does not upload the payload. The portable scene specification intentionally carries descriptors rather than bulk volume or trajectory values.
For scalar coloring, bind values to a structure with scene.bind_property(...), then pass the returned property to molgfx.color.property(...). Trajectory declarations use molgfx.trajectory.bind(...); their frames must match the declared structure topology and source identity before a renderer can resolve them.
Playing a trajectory
A scene holds one resident pair of frames plus a sample time, so showing an N-frame trajectory means moving that pair as time advances. molgfx.playback.attach does this with Scene.set_trajectory_pair, which needs no trajectory.bind descriptor and no rebinding:
positions = [
[[0.0, 0.0, 0.0], [4.0, 0.0, 0.0]],
[[0.0, 3.0, 0.0], [4.0, -3.0, 0.0]],
[[0.0, 0.0, 0.0], [4.0, 5.0, 0.0]],
]
subject = molgfx.Scene(molframe.read("two-atoms.pdb"))
subject.add(molgfx.rep.spacefill(target="all"))
subject.set_camera(subject.frame("all"))
play = molgfx.playback.attach(subject, positions, times=[0.0, 1.0, 2.0], unit=molgfx.time.TimeUnit.SECOND)
images = play.render(molgfx.Renderer(), times=[0.0, 0.5, 1.0, 1.5, 2.0], size=(320, 240))positions is (frames, atoms, 3) in the structure's atom order and unit is the molgfx.time.TimeUnit of times; for a molframe trajectory use molgfx.playback.attach_trajectory(scene, trajectory), which reads its picosecond times and refuses a frame whose time is NaN or infinite. molgfx.Timeline(scene) then drives resident pairs on one playback clock: fit_track plays a physical interval across a number of playback seconds, dilate_track plays it at a fixed physical time per second, apply(seconds) moves every track and readings() returns each track's (value, unit), such as (12.4, TimeUnit.NANOSECOND). play.seek(t) moves the scene to time t (clamped to the first and last frame). Renderer.render_sequence renders the scene's current state repeatedly and never advances time, so it returns identical frames for a trajectory; use play.render or call seek between renders.
Frames reach the scene as (atoms, 3) arrays: a float32 NumPy array is copied once with no per-atom Python work (float64 is narrowed the same way). The pair's frame handles are cached, so a seek inside one segment converts nothing, and a seek to the time already shown does nothing.
The declarative path is trajectory.bind(...) added to the scene, then scene.bind_trajectory(source_hash=..., start=..., end=...) once per source hash, then scene.set_trajectory_time(...). Calling set_trajectory_time before a pair is resident raises MolgfxError: ... no active trajectory segment.
Keep raw data outside the scene document
Use stable source hashes and let the application or binding provide the payload. This keeps scene JSON portable and makes failure to resolve a overlay source explicit instead of silently visualizing a different dataset.