API reference
Scene construction
- class hofmann.StructureScene[source]
Top-level scene holding atoms, frames, styles, bond rules, and view.
The
view(camera/projection state) andatom_data(per-atom metadata) properties are documented individually below.- species
One entry per site row, either a species label or a
Compositiondescribing a partially occupied or mixed site.
- frames
List of coordinate snapshots. Each
Framemay carry its ownlatticefor variable-cell trajectories.
- atom_styles
Mapping from species label to visual style.
- bond_specs
Declarative bond detection rules.
- polyhedra
Declarative polyhedron rendering rules.
- title
Scene title for display.
- Parameters:
species (
Sequence[str|Composition])polyhedra (
list[PolyhedronSpec] |None(default:None))title (
str(default:''))
- __init__(species, frames, atom_styles=None, bond_specs=None, polyhedra=None, view=None, title='', atom_data=None)[source]
- Parameters:
species (
Sequence[str|Composition])polyhedra (
list[PolyhedronSpec] |None(default:None))title (
str(default:''))
- Return type:
None
- property lattice: ndarray | None
Lattice matrix of the first frame.
Convenience accessor equivalent to
self.frames[0].lattice. ReturnsNonewhen the scene has no frames or the first frame has no lattice (non-periodic structure).
- property atom_data: AtomData
Return a read-only mapping view of per-atom metadata.
Each stored value is either a 1-D array of length
n_atoms(static across the trajectory) or a 2-D array of shape(len(self.frames), n_atoms)(per-frame values). The 2-D shape is checked against the trajectory length at two points: byset_atom_data()at assignment time, and by the private_validate_for_renderhelper at the start of every publicrender_*call. Mutatingself.framesafter a 2-D assignment leaves the container temporarily out of sync until the next render call raises, or until aset_atom_data()call (withclear_2d_atom_data()first if more than one 2-D entry is stored) restores consistency.Stored arrays are returned read-only. The property has no setter;
scene.atom_data = ...raisesAttributeError. The container itself exposes onlyMappingreads, soscene.atom_data[key] = ...raisesTypeErrorandscene.atom_data.pop(...)raisesAttributeError. Usecolour_byon the render methods to visualise a key, and seeset_atom_data(),del_atom_data(), andclear_2d_atom_data()for all modifications.
- classmethod from_xbs(bs_path, mv_path=None)[source]
Create a StructureScene from XBS
.bs(and optional.mv) files.- Parameters:
- Return type:
- Returns:
A fully configured StructureScene with styles and bond specs parsed from the file.
See also
hofmann.construction.scene_builders.from_xbs()
- classmethod from_ase(atoms, bond_specs=None, *, polyhedra=None, centre_atom=None, atom_styles=None, title='', view=None, atom_data=None)[source]
Create a StructureScene from ASE
Atomsobject(s).For periodic systems, fractional coordinates are wrapped to
[0, 1)and stored as Cartesian coordinates. For non-periodic systems, Cartesian positions are stored directly andlatticeisNone.- Parameters:
atoms (
Atoms|Sequence[Atoms]) – A single ASEAtomsobject or a sequence ofAtoms(e.g. from an MD trajectory orase.io.Trajectory).bond_specs (
list[BondSpec] |None(default:None)) – Bond detection rules.Nonegenerates sensible defaults from VESTA bond length cutoffs; pass an empty list to disable bonds.polyhedra (
list[PolyhedronSpec] |None(default:None)) – Polyhedron rendering rules.Nonedisables polyhedra.centre_atom (
int|None(default:None)) – Index of the atom to centre the unit cell on. Fractional coordinates are shifted so this atom sits at (0.5, 0.5, 0.5). Only valid for periodic systems. If view is also provided, the explicit view takes precedence and only the fractional-coordinate shift is applied.atom_styles (
dict[str,AtomStyle] |None(default:None)) – Per-species style overrides. When provided, these are merged on top of the auto-generated defaults so you only need to specify the species you want to customise.title (
str(default:'')) – Scene title for display.view (
ViewState|None(default:None)) – Camera / projection state. WhenNone(the default), the view is auto-centred on the centre atom (if set) or the centroid of all atoms.atom_data (
dict[str,TypeAliasType] |None(default:None)) – Per-atom metadata arrays, keyed by name. Each value is a 1-D array of lengthn_atoms(same every frame) or a 2-D array of shape(n_frames, n_atoms)(per-frame values).
- Return type:
- Returns:
A StructureScene with default element styles.
- Raises:
ImportError – If ASE is not installed.
ValueError – If atoms is an empty sequence, if centre_atom is out of range, if centre_atom is used with a non-periodic system, or if frames in a trajectory have inconsistent species, atom counts, or periodicity.
See also
hofmann.construction.scene_builders.from_ase()
- classmethod from_pymatgen(structure, bond_specs=None, *, polyhedra=None, centre_atom=None, atom_styles=None, title='', view=None, atom_data=None)[source]
Create a StructureScene from pymatgen
Structureobject(s).Fractional coordinates are wrapped to
[0, 1)and stored as Cartesian coordinates. Periodic boundary handling (image-atom expansion, recursive bond depth, molecule deduplication) is controlled at render time viaRenderStyle.- Parameters:
structure (
Structure|Sequence[Structure]) – A single pymatgenStructureor a sequence of structures (e.g. from an MD trajectory).bond_specs (
list[BondSpec] |None(default:None)) – Bond detection rules.Nonegenerates sensible defaults from VESTA bond length cutoffs; pass an empty list to disable bonds.polyhedra (
list[PolyhedronSpec] |None(default:None)) – Polyhedron rendering rules.Nonedisables polyhedra.centre_atom (
int|None(default:None)) – Index of the atom to centre the unit cell on. Fractional coordinates are shifted so this atom sits at (0.5, 0.5, 0.5). If view is also provided, the explicit view takes precedence and only the fractional- coordinate shift is applied.atom_styles (
dict[str,AtomStyle] |None(default:None)) – Per-species style overrides. When provided, these are merged on top of the auto-generated defaults so you only need to specify the species you want to customise.title (
str(default:'')) – Scene title for display.view (
ViewState|None(default:None)) – Camera / projection state. WhenNone(the default), the view is auto-centred on the structure.atom_data (
dict[str,TypeAliasType] |None(default:None)) – Per-atom metadata arrays, keyed by name. Each value is a 1-D array of lengthn_atoms(same every frame) or a 2-D array of shape(n_frames, n_atoms)(per-frame values).
- Return type:
- Returns:
A StructureScene with default element styles.
- Raises:
ImportError – If pymatgen is not installed.
See also
hofmann.construction.scene_builders.from_pymatgen()
- save_styles(path)[source]
Save the scene’s styles to a JSON file.
Writes
atom_styles,bond_specs, andpolyhedrasections. Render style is not included (it belongs to the render call, not the scene).
- load_styles(path)[source]
Load styles from a JSON file and apply them to the scene.
Atom styles are merged (existing species keep their styles unless overridden). Bond specs and polyhedra are replaced entirely. The
render_stylesection, if present in the file, is ignored; pass it to the render call instead.
- centre_on(atom_index, *, frame=0)[source]
Centre the view on a specific atom.
Sets
view.centreto the Cartesian position of the atom at atom_index in the given frame.
- set_atom_data(key, values=None, *, by_species=None, by_index=None)[source]
Set per-atom metadata for colourmap-based rendering.
Canonical write entry point for per-atom metadata. The container is otherwise read-only: to remove a single entry use
del_atom_data(), and to bulk-drop all 2-D entries (for example after extending the trajectory) useclear_2d_atom_data().Provide data in one of two forms:
Full array via values: a 1-D array-like of length
n_atoms(same value every frame) or a 2-D array-like of shape(n_frames, n_atoms)(per-frame values).Sparse via by_species and/or by_index: maps species labels or atom indices to values. See below for shape rules and precedence.
Mixing values with by_species or by_index raises
ValueError.by_species maps species labels to values. Scalars broadcast to all atoms of the species; 1-D arrays (length = count of that species’ atoms) assign per-atom; 2-D arrays of shape
(n_frames, n_species_atoms)assign per-frame. A 1-D array is always interpreted as static per-atom, even when its length equalsn_frames.by_index maps atom indices to values. Scalars are static; 1-D arrays of length
n_framesare per-frame.When both are provided, by_index values take precedence over by_species at overlapping atoms.
Unspecified atoms are filled with
NaN(numeric) orNone(categorical, stored as object-dtype).A 2-D values array, or any
by_*form that promotes to 2-D, is validated against the container’s prospective post-write state: the array’s frame count must matchlen(self.frames).- Parameters:
key (
str) – Name for this metadata (e.g."charge","site").values (
TypeAliasType|None(default:None)) – Full-length array-like. Must not be a dict; use by_index for sparse assignment by atom index.by_species (
dict[str,object] |None(default:None)) – Maps species labels to scalar, 1-D, or 2-D values. All keys must be present inscene.species.by_index (
dict[int,object] |None(default:None)) – Maps atom indices to scalar or 1-D values. All keys must be inrange(len(scene.species)).
- Raises:
ValueError – If values is mixed with by_species or by_index; if all three are absent; if a species label is unknown; if an atom index is out of range; if an array has the wrong shape for its context; or if a 2-D array’s frame count does not match
len(self.frames).TypeError – If a dict is passed as values (use
by_index=instead), or if values contain a mixture of string and numeric types.
- Return type:
None
See also
del_atom_data(): Remove a single entry.clear_2d_atom_data(): Remove all 2-D entries.
- del_atom_data(key)[source]
Remove a per-atom metadata entry.
- Parameters:
key (
str) – The metadata key to remove.- Raises:
- Return type:
None
See also
set_atom_data(): Canonical write entry point.clear_2d_atom_data(): Remove all 2-D entries at once.
- clear_2d_atom_data()[source]
Remove all 2-D per-atom metadata entries, preserving 1-D.
Required when two or more 2-D entries are stored and the trajectory has been extended: every stored 2-D entry is now stale relative to
len(self.frames), so each must be replaced before the next render. For scenes with a single 2-D entry,set_atom_data()can reassign the key directly at the new shape – the stored version is treated as overridden by the pending write – and this method is unnecessary.The multi-entry recovery workflow is: call this method, then re-assign each 2-D key via
set_atom_data()at the new shape, then render.See also
set_atom_data(): Canonical write entry point.del_atom_data(): Remove a single entry.- Return type:
- select_by_species(values, species)[source]
Keep values for selected species, fill the rest with sentinels.
Returns a copy of values with entries for non-selected atoms replaced by the appropriate missing sentinel:
NaNfor numeric data (with integer-to-float promotion) orNonefor categorical data (with unicode-to-object promotion).Intended for filtering a full-length array before passing it to
set_atom_data():scene.set_atom_data( "charge", scene.select_by_species(full_array, "O"), )
- Parameters:
- Return type:
- Returns:
A new array with the same shape as values.
- Raises:
ValueError – If species contains unknown labels or if values has the wrong shape.
- render_mpl(output=None, *, ax=None, style=None, frame_index=0, figsize=(5.0, 5.0), dpi=150, background='white', show=None, colour_by=None, cmap='viridis', colour_range=None, **style_kwargs)[source]
Render the scene as a static matplotlib figure.
- Parameters:
output (
str|Path|None(default:None)) – Optional file path to save the figure. The format is inferred from the extension (.svg,.pdf,.png). Ignored when ax is provided.ax (
Axes|None(default:None)) – Optional matplotlibAxesto draw into. When provided, the caller is responsible for saving and closing the figure. The output, figsize, dpi, background, and show parameters are ignored.style (
RenderStyle|None(default:None)) – ARenderStylecontrolling visual appearance. AnyRenderStylefield name may also be passed as a keyword argument to override individual fields.frame_index (
int(default:0)) – Which frame to render (default 0).figsize (
tuple[float,float] (default:(5.0, 5.0))) – Figure size in inches(width, height).dpi (
int(default:150)) – Resolution for raster output formats.background (
str|float|tuple[float,float,float] |list[float] (default:'white')) – Background colour.show (
bool|None(default:None)) – Whether to callplt.show(). Defaults toTruewhen output isNone,Falsewhen saving to a file.colour_by (
str|list[str] |None(default:None)) – Key (or list of keys) intoatom_datato colour atoms by. WhenNone(the default), species-based colouring is used. When a list, layers are tried in priority order and the first non-missing value determines the atom’s colour.cmap (
str|Callable[[float],Sequence[float]] |list[str|Callable[[float],Sequence[float]]] (default:'viridis')) – ACmapSpec: matplotlib colourmap name (e.g."viridis"),Colormapobject, or callable mapping a float in[0, 1]to an(r, g, b)tuple. When colour_by is a list, cmap may also be a list of the same length (one per layer).colour_range (
tuple[float,float] |None|list[tuple[float,float] |None] (default:None)) – Explicit(vmin, vmax)for normalising numerical data.Noneauto-ranges from the data. When colour_by is a list, may also be a list of the same length.**style_kwargs (
object) – AnyRenderStylefield name as a keyword argument (e.g.show_bonds=False).
- Return type:
- Returns:
The matplotlib
Figure.
- render_mpl_interactive(*, style=None, frame_index=0, figsize=(5.0, 5.0), dpi=150, background='white', colour_by=None, cmap='viridis', colour_range=None, **style_kwargs)[source]
Open an interactive matplotlib viewer with mouse and keyboard controls.
Left-drag rotates, scroll zooms, and keyboard shortcuts control rotation, pan, perspective, display toggles, and frame navigation. Press h to show a help overlay listing all keybindings.
When the window is closed the updated
ViewStateandRenderStyleare returned so they can be reused for static rendering:view, style = scene.render_mpl_interactive() scene.view = view scene.render_mpl("output.svg", style=style)
- Parameters:
style (
RenderStyle|None(default:None)) – ARenderStylecontrolling visual appearance. AnyRenderStylefield name may also be passed as a keyword argument to override individual fields.frame_index (
int(default:0)) – Which frame to render initially.figsize (
tuple[float,float] (default:(5.0, 5.0))) – Figure size in inches(width, height).dpi (
int(default:150)) – Resolution.background (
str|float|tuple[float,float,float] |list[float] (default:'white')) – Background colour.colour_by (
str|list[str] |None(default:None)) – Key (or list of keys) intoatom_datato colour atoms by. When a list, layers are tried in priority order.cmap (
str|Callable[[float],Sequence[float]] |list[str|Callable[[float],Sequence[float]]] (default:'viridis')) – ACmapSpec: colourmap name, object, or callable. When colour_by is a list, may also be a list of the same length.colour_range (
tuple[float,float] |None|list[tuple[float,float] |None] (default:None)) – Explicit(vmin, vmax)for numerical data. When colour_by is a list, may also be a list of the same length.**style_kwargs (
object) – AnyRenderStylefield name as a keyword argument (e.g.show_bonds=False).
- Return type:
- Returns:
A
(ViewState, RenderStyle)tuple reflecting any view and style changes applied during the interactive session.
- render_animation(output, *, style=None, frames=None, fps=30, figsize=(5.0, 5.0), dpi=150, background='white', colour_by=None, cmap='viridis', colour_range=None, **style_kwargs)[source]
Render a trajectory animation to a GIF or MP4 file.
Loops over the specified frames, rendering each with the per-frame pipeline and writing it to the output file.
- Parameters:
output (
str|Path) – Destination file path. Extension determines the format (e.g..gif,.mp4).style (
RenderStyle|None(default:None)) – ARenderStylecontrolling visual appearance. AnyRenderStylefield name may also be passed as a keyword argument to override individual fields.frames (
range|Sequence[int] |None(default:None)) – Which frame indices to render, in order.Nonerenders all frames. Acceptsrange(0, 100, 5)or an arbitrary sequence of indices.fps (
int(default:30)) – Frames per second in the output file.figsize (
tuple[float,float] (default:(5.0, 5.0))) – Figure size in inches(width, height).dpi (
int(default:150)) – Resolution in dots per inch.background (
str|float|tuple[float,float,float] |list[float] (default:'white')) – Background colour.colour_by (
str|list[str] |None(default:None)) – Key (or list of keys) intoatom_datato colour atoms by.cmap (
str|Callable[[float],Sequence[float]] |list[str|Callable[[float],Sequence[float]]] (default:'viridis')) – ACmapSpec: colourmap name, object, or callable.colour_range (
tuple[float,float] |None|list[tuple[float,float] |None] (default:None)) – Explicit(vmin, vmax)for numerical data.**style_kwargs (
object) – AnyRenderStylefield name as a keyword argument (e.g.show_bonds=False).
- Return type:
- Returns:
The output file path as a
Path.- Raises:
ValueError – If frames is empty or contains out-of-range indices, if fps is less than 1, or if output has an unsupported file extension (must be
.gifor.mp4).ImportError – If
imageiois not installed.
See also
hofmann.rendering.animation.render_animation()
- hofmann.from_xbs(bs_path, mv_path=None)[source]
Create a StructureScene from XBS .bs (and optional .mv) files.
- hofmann.from_ase(atoms, bond_specs=None, *, polyhedra=None, centre_atom=None, atom_styles=None, title='', view=None, atom_data=None)[source]
Create a StructureScene from ASE
Atomsobject(s).For periodic systems (where
atoms.pbcis set and the cell is non-degenerate), fractional coordinates are wrapped to[0, 1)and stored as Cartesian coordinates, following the same approach asfrom_pymatgen(). For non-periodic systems, Cartesian positions are stored directly andlatticeisNone.- Parameters:
atoms (
Atoms|Sequence[Atoms]) – A single ASEAtomsobject or a sequence ofAtoms(e.g. from an MD trajectory orase.io.Trajectory).bond_specs (
list[BondSpec] |None(default:None)) – Bond detection rules.Nonegenerates sensible defaults from VESTA bond length cutoffs; pass an empty list to disable bonds.polyhedra (
list[PolyhedronSpec] |None(default:None)) – Polyhedron rendering rules.Nonedisables polyhedra.centre_atom (
int|None(default:None)) – Index of the atom to centre the unit cell on. Fractional coordinates are shifted so this atom sits at (0.5, 0.5, 0.5). Only valid for periodic systems. If view is also provided, the explicit view takes precedence and only the fractional-coordinate shift is applied.atom_styles (
dict[str,AtomStyle] |None(default:None)) – Per-species style overrides. When provided, these are merged on top of the auto-generated defaults so you only need to specify the species you want to customise.title (
str(default:'')) – Scene title for display.view (
ViewState|None(default:None)) – Camera / projection state. WhenNone(the default), the view is auto-centred on the centre atom (if set) or the centroid of all atoms.atom_data (
dict[str,TypeAliasType] |None(default:None)) – Per-atom metadata arrays, keyed by name.
- Return type:
- Returns:
A StructureScene with default element styles.
- Raises:
ImportError – If ASE is not installed.
ValueError – If atoms is an empty sequence, if centre_atom is out of range, if centre_atom is used with a non-periodic system, or if frames in a trajectory have inconsistent species, atom counts, or periodicity.
- hofmann.from_pymatgen(structure, bond_specs=None, *, polyhedra=None, centre_atom=None, atom_styles=None, title='', view=None, atom_data=None)[source]
Create a StructureScene from pymatgen Structure(s).
Fractional coordinates are wrapped to
[0, 1)and stored as Cartesian coordinates. Periodic boundary handling (PBC bond computation, image-atom expansion, recursive depth, molecule deduplication) is controlled at render time viaRenderStyle.- Parameters:
structure (
Structure|Sequence[Structure]) – A single pymatgenStructureor a list ofStructureobjects (e.g. from an MD trajectory).bond_specs (
list[BondSpec] |None(default:None)) – Optional bond specification rules. IfNone, sensible defaults are generated from VESTA bond length cutoffs. Pass an empty list to disable bonds.polyhedra (
list[PolyhedronSpec] |None(default:None)) – Optional polyhedron rendering rules. IfNone, no polyhedra are drawn.centre_atom (
int|None(default:None)) – Index of the atom to centre the unit cell on. When set, all fractional coordinates are shifted so that this atom sits at (0.5, 0.5, 0.5), and the view is centred on this atom. If view is also provided, the explicit view takes precedence and only the fractional- coordinate shift is applied.atom_styles (
dict[str,AtomStyle] |None(default:None)) – Per-species style overrides. When provided, these are merged on top of the auto-generated defaults so you only need to specify the species you want to customise.title (
str(default:'')) – Scene title for display.view (
ViewState|None(default:None)) – Camera / projection state. WhenNone(the default), the view is auto-centred on the structure.atom_data (
dict[str,TypeAliasType] |None(default:None)) – Per-atom metadata arrays, keyed by name.
- Return type:
- Returns:
A StructureScene with default element styles.
- Raises:
ImportError – If pymatgen is not installed.
Data model
- class hofmann.Frame[source]
A single snapshot of atomic coordinates.
- coords
Cartesian coordinates, shape
(n_atoms, 3).
- lattice
Unit-cell matrix, shape
(3, 3)with rows as lattice vectors, orNonefor non-periodic structures.
- label
Optional frame label or identifier.
- Raises:
ValueError – If coords does not have shape
(n_atoms, 3).ValueError – If lattice is not
Noneand does not have shape(3, 3).
- Parameters:
- class hofmann.AtomStyle[source]
Visual style for an atomic species.
- radius
Display radius in angstroms. Typical values range from about 0.5 (hydrogen) to 2.0 (heavy metals). See
COVALENT_RADIIfor physically motivated starting points.
- colour
Fill colour specification (CSS name, hex string, grey float, or RGB tuple/list). See
Colour.
- visible
Whether atoms of this species are drawn. Set to
Falseto hide atoms without removing them from the scene. Bonds to hidden atoms are also suppressed. This flag has no effect on sites occupied by aComposition: mixed sites always render every constituent regardless of any constituent’svisibleflag.
- Parameters:
- to_dict()[source]
Serialise to a JSON-compatible dictionary.
Colours are normalised to
[r, g, b]lists. Thevisiblefield is omitted whenTrue(the default).- Return type:
- classmethod from_dict(d)[source]
Deserialise from a dictionary.
Accepts any colour format understood by
normalise_colour().
- class hofmann.Composition[source]
Species-to-occupancy mapping for a (possibly mixed) site.
A frozen, hashable
Mapping[str, float]describing how a site is occupied: a single species at full occupancy (a “pure” site, also expressible as a plainstr), a weighted mix of species (a “mixed” site), or any of the above with an implicit vacancy fraction (1 - sum(occupancies)).Validated at construction:
All occupancy values must be finite and lie in
[0, 1]. Zero values are dropped before further validation; negative or non-finite values raise.The occupancy sum must not exceed
1.0(within a tolerance of1e-9). Any deficit is interpreted as a vacancy fraction.Keys must be non-empty strings.
Iteration order is canonical: descending occupancy, with alphabetical tiebreak. This ordering determines wedge layout in the renderer, so visual reproducibility is guaranteed across runs.
- Parameters:
occupancies (
Mapping[str,float]) – A mapping of species labels to occupancy fractions.- Raises:
ValueError – If any value is non-finite or outside
[0, 1]; if the sum exceeds 1.0; if the resulting mapping is empty (all values zero, or the input was empty).TypeError – If any key is not a string.
- class hofmann.BondSpec[source]
Declarative rule for bond detection between species pairs.
The species pair is stored in sorted order so that the data structure is invariant under exchange of the two labels.
Species names support fnmatch-style wildcards (
*,?).Only species and max_length are required. radius and colour default to
None, meaning “use the class-level default” (BondSpec.default_radiusandBondSpec.default_colour). The resolved value is returned by the corresponding property;repr()showsradius=<default 0.1>when unset so the intent is visible.To change the defaults for all specs that have not been explicitly set:
BondSpec.default_radius = 0.15 BondSpec.default_colour = "grey"
- species
Sorted pair of species patterns.
- max_length
Maximum bond length threshold.
- min_length
Minimum bond length threshold. Defaults to
0.0.
- complete
Controls single-pass bond completion across periodic boundaries. A species string (e.g.
"Zr") adds missing partners around visible atoms of that species."*"completes around both species in the pair.False(default) disables completion. Unlike recursive, newly added atoms are not themselves searched.
- recursive
If
True, atoms bonded via this spec are searched recursively across periodic boundaries. When an image atom is materialised, its own bonded partners are checked on the next iteration, extending the expansion outward. Useful for molecules that span periodic boundaries.
- Parameters:
- default_colour: ClassVar[str | float | tuple[float, float, float] | list[float]] = 0.5
Class-level default for colour when not set explicitly.
- __init__(species, max_length, min_length=0.0, radius=None, colour=None, complete=False, recursive=False)[source]
- Parameters:
- Return type:
None
- property colour: str | float | tuple[float, float, float] | list[float]
Bond colour (resolved from class default when not set explicitly).
- matches(species_1, species_2)[source]
Check whether this spec matches a given species pair.
Matching is symmetric:
BondSpec(("C", "H"), ...).matches("H", "C")returnsTrue.
- class hofmann.Bond[source]
A computed bond between two atoms.
- index_a
Index of the first atom.
- index_b
Index of the second atom.
- length
Interatomic distance.
- spec
The BondSpec rule that produced this bond.
- image
Lattice translation applied to atom b to form the bond.
(0, 0, 0)means a direct bond within the cell.(1, 0, 0)means atom b is shifted by +1 along lattice vector a. Always(0, 0, 0)for non-periodic scenes.
- Parameters:
- class hofmann.PolyhedronSpec[source]
Declarative rule for rendering coordination polyhedra.
A polyhedron is drawn around each atom whose species matches centre, using its bonded neighbours as vertices of a convex hull. Species names support fnmatch-style wildcards.
- centre
Species pattern for the centre atom (e.g.
"Ti").
- colour
Face colour, or
Noneto inherit from the centre atom’s style colour.
- alpha
Face transparency (0 = fully transparent, 1 = opaque).
- edge_colour
Colour for face wireframe edges.
- edge_width
Line width for face wireframe edges (points).
- hide_centre
Whether to hide the centre atom circle when a polyhedron is drawn.
- hide_bonds
Whether to hide bonds from the centre atom to its coordinating neighbours when a polyhedron is drawn.
- hide_vertices
Whether to hide the vertex atom circles. An atom is only hidden if every polyhedron it participates in has
hide_vertices=True.
- min_vertices
Minimum number of bonded neighbours required to draw a polyhedron. Centre atoms with fewer neighbours are skipped.
Noneuses the default minimum of 3.
- Parameters:
centre (
str)colour (
str|float|tuple[float,float,float] |list[float] |None(default:None))alpha (
float(default:0.4))edge_colour (
str|float|tuple[float,float,float] |list[float] (default:(0.15, 0.15, 0.15)))edge_width (
float(default:1.0))hide_centre (
bool(default:False))hide_bonds (
bool(default:False))hide_vertices (
bool(default:False))
- to_dict()[source]
Serialise to a JSON-compatible dictionary.
Fields at their default values are omitted. Colours are normalised to
[r, g, b]lists.- Return type:
- __init__(centre, colour=None, alpha=0.4, edge_colour=(0.15, 0.15, 0.15), edge_width=1.0, hide_centre=False, hide_bonds=False, hide_vertices=False, min_vertices=None)
- Parameters:
centre (
str)colour (
str|float|tuple[float,float,float] |list[float] |None(default:None))alpha (
float(default:0.4))edge_colour (
str|float|tuple[float,float,float] |list[float] (default:(0.15, 0.15, 0.15)))edge_width (
float(default:1.0))hide_centre (
bool(default:False))hide_bonds (
bool(default:False))hide_vertices (
bool(default:False))
- Return type:
None
- class hofmann.Polyhedron[source]
A computed coordination polyhedron.
- centre_index
Index of the centre atom.
- neighbour_indices
Indices of the coordinating atoms.
- faces
List of faces, each a 1-D array of vertex indices into neighbour_indices. Triangular faces have length 3; merged coplanar faces may have 4 or more vertices.
- spec
The PolyhedronSpec that produced this polyhedron.
- Parameters:
- class hofmann.ViewState[source]
Camera state for 3D-to-2D projection.
Encapsulates rotation, zoom, centring, and optional perspective projection. Renderers consume the projected 2D coordinates and depth values produced by
project().Depth-slab clipping is controlled by
slab_near,slab_far, andslab_origin. When set, only atoms whose depth (along the viewing direction) falls within the range[origin_depth + slab_near, origin_depth + slab_far]are rendered. If slab_origin isNone, the slab is centred oncentre.- rotation
3x3 rotation matrix.
- zoom
Magnification factor.
- centre
3D point about which to centre the view.
- perspective
Perspective strength (0 = orthographic).
- view_distance
Distance from camera to scene centre.
- slab_origin
3D point defining the slab reference depth, or
Noneto use centre.
- slab_near
Near offset from the slab origin depth (negative = further from camera), or
Nonefor no near limit.
- slab_far
Far offset from the slab origin depth (positive = closer to camera), or
Nonefor no far limit.
- Parameters:
- project(coords, radii=None)[source]
Project 3D coordinates to 2D with depth information.
The eye sits at
[0, 0, view_distance]and each sphere’s visible silhouette is projected onto the z=0 plane.- Parameters:
- Returns:
xy:
(n, 2)projected 2D coordinates.depth:
(n,)depth values (larger = closer to viewer).projected_radii:
(n,)screen-space sphere radii.
- Return type:
- slab_mask(coords)[source]
Return a boolean mask selecting atoms within the depth slab.
If neither
slab_nearnorslab_faris set, all atoms are selected. The depth of each atom is measured along the current viewing direction, relative to the slab origin (orcentreif no origin is set).
- look_along(direction, *, up=(0.0, 1.0, 0.0))[source]
Set the rotation so the camera looks along direction.
The view is oriented so that direction points into the screen (along +z in camera space). The up vector determines which way is “up” on screen.
This is equivalent to placing the camera at a point along direction looking back towards the origin.
Returns
selfso callers can chain, e.g.:scene.view = ViewState(centre=centroid).look_along([1, 1, 1])
- Parameters:
direction (
ndarray|list[float] |tuple[float,...]) – 3D vector giving the viewing direction (from the camera towards the scene). Need not be normalised.up (
ndarray|list[float] |tuple[float,...] (default:(0.0, 1.0, 0.0))) – 3D vector indicating the upward direction in screen space. Defaults to[0, 1, 0].
- Return type:
- Returns:
self, with the rotation updated in place.- Raises:
ValueError – If direction is zero-length or up is parallel to direction.
- __init__(rotation=<factory>, zoom=1.0, centre=<factory>, perspective=0.0, view_distance=10.0, slab_origin=None, slab_near=None, slab_far=None)
- class hofmann.model.atom_data.AtomData[source]
Per-atom metadata container.
The supported way to obtain an
AtomDatais viaatom_data; the class is not re-exported fromhofmannorhofmann.model, and direct construction is considered an internal implementation detail. User-facing access goes throughatom_datafor reads,set_atom_data(),del_atom_data(), andclear_2d_atom_data()for writes.Stores named per-atom arrays. Each value is either a 1-D array of shape
(n_atoms,)(static across the trajectory) or a 2-D array of shape(m, n_atoms)wheremis the trajectory length the caller declares at write time via theexpected_frameskwarg on_set. All stored 2-D entries in a single container must share the samem; this cross- entry invariant is enforced at assignment.The container holds no cached frame count between calls; each
_setis toldexpected_framesby the caller, and the invariant is re-derived from the stored data on every call. Frame consistency is enforced at two sites:At assignment, via
_setcalling_check_2d_consistencywithpending={key: arr}, validating the prospective post-write state againstexpected_frames. Both the incoming array and any already-stored 2-D entries not being overridden are checked.At render, via
render_mpl()(and friends) calling the scene’s private_validate_for_renderhelper, which in turn calls_check_2d_consistencywith nopending. This validates the current stored state againstlen(scene.frames)as a backstop that catches the specific case wherescene.framesis mutated after the last write but before the next render.
Inherits from
collections.abc.Mapping(notMutableMapping). Mutation goes through the private_set,_del, and_clear_2dmethods; noad[key] = valueordel ad[key]shortcut exists. Assigned values are always copied vianumpy.array()– including existing numpy arrays – so the container owns the buffer and the caller’s source array is left untouched.Note
Stored arrays are returned read-only. In-place mutation (e.g.
ad["charge"][0] = 99) raisesValueError: assignment destination is read-only. To update values, pass a fresh array throughset_atom_data(), which re-validates the shape and recomputes therangesandlabelsentries for the key. Only the array buffer is frozen – forobject-dtype arrays, any mutable objects stored inside remain mutable.- n_atoms
The number of atoms the container was built for. Fixed at construction and not mutable.
- ranges
Read-only mapping of keys to
(min, max)tuples for 2-D numeric arrays, orNonefor keys that do not have a meaningful numeric range (1-D arrays, categorical arrays, empty arrays, all-NaN numeric arrays). Entries are added on assignment, replaced on reassignment, and removed on deletion.
- labels
Read-only mapping of keys to tuples of unique non-missing categorical labels, or
Nonefor keys without a meaningful label set (1-D arrays, numeric dtypes, categorical arrays with no non-missing values). Missing values (None,"", NaN) are excluded from the label set. Entries are added on assignment, replaced on reassignment, and removed on deletion.
For 2-D arrays,
rangesis populated for numeric dtypes andlabelsfor categorical dtypes; the other side is alwaysNone. Either side may itself beNonefor empty arrays or arrays containing only missing values. For 1-D arrays, both areNone.- Parameters:
n_atoms (
int) – Number of atoms in the scene. Non-negative.- Raises:
ValueError – If n_atoms is negative.
- classmethod __new__(*args, **kwargs)
Rendering
- class hofmann.RenderStyle[source]
Visual style settings for rendering.
Groups all appearance parameters that control how a scene is drawn, independent of the scene data itself. A default
RenderStyle()gives the standard ball-and-stick look.Pass a style to
render_mpl()via the style keyword, or override individual fields with convenience kwargs:style = RenderStyle(show_outlines=False, atom_scale=0.8) scene.render_mpl("out.svg", style=style) # Or override a single field: scene.render_mpl("out.svg", show_bonds=False)
- atom_scale
Scale factor for atom display radii.
0.5gives ball-and-stick;1.0gives space-filling.
- bond_scale
Scale factor for bond cylinder radii.
- bond_colour
Override colour for all bonds, or
Noneto use per-spec / half-bond colouring.
- half_bonds
Split each bond at the midpoint and colour halves to match the nearest atom.
- show_bonds
Whether to draw bonds at all.
- show_polyhedra
Whether to draw coordination polyhedra.
- show_outlines
Whether to draw outlines around atoms and bonds.
- outline_colour
Colour for outlines when show_outlines is
True.
- atom_outline_width
Line width for atom outlines (points). Applies uniformly to pure-string sites and to the outer outline and radial wedge separators of mixed sites.
- bond_outline_width
Line width for bond outlines (points).
- slab_clip_mode
How slab clipping affects polyhedra at the boundary.
"per_face"drops individual faces with out-of-slab vertices (default),"clip_whole"hides the entire polyhedron if any vertex is clipped, and"include_whole"forces the complete polyhedron to be visible when its centre atom is in the slab.
- circle_segments
Number of line segments used to approximate atom circles in static output. Higher values give smoother circles in vector output (PDF/SVG). The default (
72) gives publication-quality output.
- arc_segments
Number of line segments per semicircular bond end-cap in static output. Higher values give smoother bond ends in vector output. The default (
12) gives publication-quality output.
- interactive_circle_segments
Number of line segments for atom circles in the interactive viewer. Lower values give faster redraws. The default (
24) balances quality and responsiveness.
- interactive_arc_segments
Number of line segments per bond end-cap in the interactive viewer. Lower values give faster redraws. The default (
5) balances quality and responsiveness.
- polyhedra_shading
Strength of diffuse shading on polyhedra faces.
0.0gives flat colouring (no shading);1.0(the default) gives full Lambertian-style shading where faces pointing at the viewer are bright and edge-on faces are dimmed.
- light_direction
Direction of the virtual light source for polyhedra face shading, in screen space (x = right, y = up, z = towards viewer). Normalised internally before use. The zero vector is rejected.
- polyhedra_outline_width
Global override for polyhedra outline line width (points). When
None(the default), each polyhedron uses its ownPolyhedronSpec.edge_width. When set, overrides all per-spec values.
- show_cell
Whether to draw unit cell edges.
None(the default) auto-detects: edges are drawn when the scene has a lattice.Trueforces drawing (raisesValueErrorat render time if no lattice is available).Falsesuppresses drawing.
- cell_style
Visual style for unit cell edges. See
CellEdgeStyle.
- show_axes
Whether to draw the crystallographic axes orientation widget.
None(the default) auto-detects: the widget is drawn when the scene has a lattice.Trueforces drawing (raisesValueErrorat render time if no lattice is available).Falsesuppresses drawing.
- show_legend
Whether to draw the species legend.
False(the default) suppresses drawing.Truedraws a legend showing each visible species with its colour.
- legend_style
Visual style for the legend widget. See
LegendStyle.
- pbc
Whether to use the lattice for periodic bond computation and image-atom expansion. Only meaningful when the scene has a lattice. Set to
Falseto disable all periodic boundary handling and render only the physical atoms with Euclidean bond detection.
- pbc_padding
Cartesian margin (angstroms) for geometric cell-face expansion. Atoms within this distance of a unit cell face are duplicated on the opposite side, producing an expanded view of the structure.
Nonedisables geometric expansion. The default of0.1angstroms gives a thin shell that catches atoms sitting exactly on cell edges.
- max_recursive_depth
Maximum iterations for recursive bond expansion. Only relevant when one or more bond_specs have
recursive=True. Must be >= 1.
- deduplicate_molecules
Whether to remove duplicate molecular fragments that span cell boundaries. When
True, each molecule appears only once, keeping the largest connected cluster.
- Raises:
ValueError – If atom_scale or bond_scale are not positive, max_recursive_depth is less than 1, atom_outline_width or bond_outline_width are negative, circle_segments or interactive_circle_segments < 3, arc_segments or interactive_arc_segments < 2, polyhedra_shading is outside
[0, 1], light_direction does not have exactly 3 components or is the zero vector, or polyhedra_outline_width is negative.- Parameters:
atom_scale (
float(default:0.5))bond_scale (
float(default:1.0))bond_colour (
str|float|tuple[float,float,float] |list[float] |None(default:None))half_bonds (
bool(default:True))show_bonds (
bool(default:True))show_polyhedra (
bool(default:True))show_outlines (
bool(default:True))outline_colour (
str|float|tuple[float,float,float] |list[float] (default:(0.15, 0.15, 0.15)))atom_outline_width (
float(default:1.0))bond_outline_width (
float(default:1.0))wedge_start_angle (
float(default:1.5707963267948966))vacancy_colour (
str|float|tuple[float,float,float] |list[float] |None(default:None))show_wedge_edges (
bool(default:True))slab_clip_mode (
SlabClipMode(default:<SlabClipMode.PER_FACE: 'per_face'>))circle_segments (
int(default:72))arc_segments (
int(default:12))interactive_circle_segments (
int(default:24))interactive_arc_segments (
int(default:5))polyhedra_shading (
float(default:1.0))light_direction (
tuple[float,float,float] (default:(0.0, 0.0, 1.0)))cell_style (
CellEdgeStyle(default:<factory>))axes_style (
AxesStyle(default:<factory>))show_legend (
bool(default:False))legend_style (
LegendStyle(default:<factory>))pbc (
bool(default:True))max_recursive_depth (
int(default:5))deduplicate_molecules (
bool(default:False))
- wedge_start_angle: float = 1.5707963267948966
Starting angle for mixed-site pie wedges (radians).
Default
pi / 2(12 o’clock). Applied globally to all mixed sites in the scene.
- vacancy_colour: str | float | tuple[float, float, float] | list[float] | None = None
Fill colour for the vacancy fraction of a partially occupied site.
None(the default) fills the vacancy wedge with the canvas background colour, so partial sites read as opaque circles with one slice “missing”. Set to an explicit colour to make the vacancy stand out against the background (for example,"lightgrey"on a white canvas).
- show_wedge_edges: bool = True
Whether to draw radial separators between wedges of a mixed site.
Default
Truedraws thin radial lines between adjacent wedges, including at the vacancy boundary. Set toFalseto render mixed sites as seamless pies bounded only by the outer arc.Both the outer arc and the radial separators are stroked at
atom_outline_widthpoints, so mixed-site outlines have the same visual weight as pure-circle outlines.
- __init__(atom_scale=0.5, bond_scale=1.0, bond_colour=None, half_bonds=True, show_bonds=True, show_polyhedra=True, show_outlines=True, outline_colour=(0.15, 0.15, 0.15), atom_outline_width=1.0, bond_outline_width=1.0, wedge_start_angle=1.5707963267948966, vacancy_colour=None, show_wedge_edges=True, slab_clip_mode=SlabClipMode.PER_FACE, circle_segments=72, arc_segments=12, interactive_circle_segments=24, interactive_arc_segments=5, polyhedra_shading=1.0, light_direction=(0.0, 0.0, 1.0), polyhedra_outline_width=None, show_cell=None, cell_style=<factory>, show_axes=None, axes_style=<factory>, show_legend=False, legend_style=<factory>, pbc=True, pbc_padding=0.1, max_recursive_depth=5, deduplicate_molecules=False)
- Parameters:
atom_scale (
float(default:0.5))bond_scale (
float(default:1.0))bond_colour (
str|float|tuple[float,float,float] |list[float] |None(default:None))half_bonds (
bool(default:True))show_bonds (
bool(default:True))show_polyhedra (
bool(default:True))show_outlines (
bool(default:True))outline_colour (
str|float|tuple[float,float,float] |list[float] (default:(0.15, 0.15, 0.15)))atom_outline_width (
float(default:1.0))bond_outline_width (
float(default:1.0))wedge_start_angle (
float(default:1.5707963267948966))vacancy_colour (
str|float|tuple[float,float,float] |list[float] |None(default:None))show_wedge_edges (
bool(default:True))slab_clip_mode (
SlabClipMode(default:<SlabClipMode.PER_FACE: 'per_face'>))circle_segments (
int(default:72))arc_segments (
int(default:12))interactive_circle_segments (
int(default:24))interactive_arc_segments (
int(default:5))polyhedra_shading (
float(default:1.0))light_direction (
tuple[float,float,float] (default:(0.0, 0.0, 1.0)))cell_style (
CellEdgeStyle(default:<factory>))axes_style (
AxesStyle(default:<factory>))show_legend (
bool(default:False))legend_style (
LegendStyle(default:<factory>))pbc (
bool(default:True))max_recursive_depth (
int(default:5))deduplicate_molecules (
bool(default:False))
- Return type:
None
- to_dict()[source]
Serialise to a JSON-compatible dictionary.
Fields at their default values are omitted. Nested
cell_style,axes_style, andlegend_styleare serialised as sub-dicts (omitted entirely when they equal their own defaults).- Return type:
- classmethod from_dict(d)[source]
Deserialise from a dictionary.
Missing fields use their defaults. The
slab_clip_modestring is coerced toSlabClipModeandbond_colourlists are converted to tuples for type consistency.- Parameters:
d (
dict)- Return type:
- class hofmann.CellEdgeStyle[source]
Visual style for unit cell edges.
- colour
Edge colour. Accepts any format understood by
normalise_colour().
- line_width
Width of the edge line in display units.
- linestyle
Line pattern:
"solid","dashed","dotted", or"dashdot".
- Parameters:
- to_dict()[source]
Serialise to a JSON-compatible dictionary.
Fields at their default values are omitted.
- Return type:
- class hofmann.AxesStyle[source]
Visual style for the crystallographic axes orientation widget.
The widget draws three axis lines (a, b, c lattice directions) from a common origin in a corner of the viewport. Lines rotate in sync with the structure, with italic labels at the tips.
- colours
Tuple of three colours for the (a, b, c) axes. Each element accepts any format understood by
normalise_colour(). Defaults to uniform dark grey. Pass distinct colours for per-axis colouring.
- labels
Tuple of three label strings for the axes.
- font_size
Font size for axis labels in points.
- italic
Whether to render labels in italic (crystallographic convention).
- arrow_length
Axis line length as a fraction of the viewport half-extent.
- line_width
Width of the axis lines in points.
- corner
Widget origin position. Pass a
WidgetCorner(or its string value) for automatic placement in one of the four viewport corners, offset by margin. Pass an(x, y)tuple of fractional viewport coordinates (0.0 = left/bottom, 1.0 = right/top) for an explicit position; margin is ignored in this case.
- margin
Offset from the corner as a fraction of the viewport half-extent. Only used when corner is a
WidgetCorner.
- Parameters:
colours (
tuple[str|float|tuple[float,float,float] |list[float],str|float|tuple[float,float,float] |list[float],str|float|tuple[float,float,float] |list[float]] (default:((0.3, 0.3, 0.3), (0.3, 0.3, 0.3), (0.3, 0.3, 0.3))))font_size (
float(default:10.0))italic (
bool(default:True))arrow_length (
float(default:0.12))line_width (
float(default:1.0))corner (
WidgetCorner|tuple[float,float] (default:<WidgetCorner.BOTTOM_LEFT: 'bottom_left'>))margin (
float(default:0.15))
- to_dict()[source]
Serialise to a JSON-compatible dictionary.
Fields at their default values are omitted.
- Return type:
- __init__(colours=((0.3, 0.3, 0.3), (0.3, 0.3, 0.3), (0.3, 0.3, 0.3)), labels=('a', 'b', 'c'), font_size=10.0, italic=True, arrow_length=0.12, line_width=1.0, corner=WidgetCorner.BOTTOM_LEFT, margin=0.15)
- Parameters:
colours (
tuple[str|float|tuple[float,float,float] |list[float],str|float|tuple[float,float,float] |list[float],str|float|tuple[float,float,float] |list[float]] (default:((0.3, 0.3, 0.3), (0.3, 0.3, 0.3), (0.3, 0.3, 0.3))))font_size (
float(default:10.0))italic (
bool(default:True))arrow_length (
float(default:0.12))line_width (
float(default:1.0))corner (
WidgetCorner|tuple[float,float] (default:<WidgetCorner.BOTTOM_LEFT: 'bottom_left'>))margin (
float(default:0.15))
- Return type:
None
- class hofmann.LegendItem[source]
Abstract base class for legend entries.
Concrete subclasses:
AtomLegendItem— circle marker (atoms).PolygonLegendItem— regular-polygon marker.PolyhedronLegendItem— miniature 3D polyhedron icon.
Shared fields live on the base class; marker-specific fields (
sides,rotation,shape) live on the relevant subclass.- Parameters:
key (
str) – Identifier for this legend entry. Also used as the default display label when label isNone.colour (
str|float|tuple[float,float,float] |list[float]) – Fill colour for the legend marker. Accepts any format understood bynormalise_colour(); the value is normalised to an(R, G, B)tuple on assignment.label (
str|None(default:None)) – Display label text.Nonefalls back to key. Common chemical notation is auto-formatted at render time: trailing charges become superscripts, embedded digits become subscripts. Labels containing$are passed through as explicit matplotlib mathtext.radius (
float|None(default:None)) – Marker radius in points (before display-space scaling).Nonefalls back toLegendStyle.circle_radiuswhen that is a plain float, or to its default value otherwise (the proportional and per-species dict modes do not apply to individual items).gap_after (
float|None(default:None)) – Vertical gap in points between this entry and the next one.Nonefalls back toLegendStyle.spacing. Must be non-negative. Ignored for the final entry in the list.alpha (
float(default:1.0)) – Opacity of the marker face, from 0.0 (fully transparent) to 1.0 (fully opaque, the default). Marker outlines are unaffected and remain fully opaque.edge_colour (
str|float|tuple[float,float,float] |list[float] |None(default:None)) – Override edge/outline colour for this item.None(the default) falls back to the scene-level outline colour. Accepts any format understood bynormalise_colour().edge_width (
float|None(default:None)) – Override edge/outline width in points for this item.None(the default) falls back to the scene-level outline width. Must be non-negative.
- __init__(key, colour, label=None, radius=None, gap_after=None, alpha=1.0, edge_colour=None, edge_width=None)[source]
- abstract property marker_type: str
Return the marker type identifier (
"atom","polygon", or"polyhedron").
- property edge_colour: tuple[float, float, float] | None
Per-item edge colour (normalised RGB), or
Nonefor scene default.
- classmethod from_dict(d)[source]
Deserialise from a dictionary.
Dispatches to the correct subclass based on the
"type"key. Dictionaries without a"type"key default toAtomLegendItem. Legacy dicts containingsidesorpolyhedronfields from 0.12.x are not inferred automatically and must be migrated.- Parameters:
d (
dict)- Return type:
- class hofmann.LegendStyle[source]
Visual style for the species legend widget.
The widget draws a vertical column of coloured circles with labels beside them. By default, entries are auto-generated from the scene’s species and atom styles. To display a fully custom legend (e.g. for
colour_bydata), pass a tuple ofLegendIteminstances via the items parameter — this bypasses species auto-generation entirely.- corner
Widget position. Pass a
WidgetCorner(or its string value) for automatic placement in one of the four viewport corners, offset by margin. Pass an(x, y)tuple of fractional viewport coordinates (0.0 = left/bottom, 1.0 = right/top) for an explicit position; margin is ignored in this case.
- margin
Offset from the corner as a fraction of the viewport half-extent. Only used when corner is a
WidgetCorner.
- font_size
Font size for species labels in points.
- circle_radius
Controls the size of the coloured circles in points. Accepts three forms:
float — uniform radius for all entries (default 5.0).
tuple (min, max) — proportional sizing. Each species’ circle radius is linearly interpolated between min and max based on its
AtomStyle.radiusrelative to the smallest and largest radii in the legend. When all atom radii are equal, max is used.dict[str, float] — explicit per-species radii. Species not present in the dict use the class default (5.0 points).
- spacing
Vertical gap between legend entries in points.
- label_gap
Horizontal gap between the circle edge and the species label in points.
- species
Explicit list of species to include, in display order.
None(the default) auto-detects from the scene: unique species in first-seen order. Pure-string site rows contribute their species only when itsAtomStyle.visibleisTrue; species sourced from aCompositionconstituent are always included (mixed-site rendering ignores per-constituent visibility). Ignored when items is provided.
- labels
Custom display labels for legend entries, mapping species name to label string. Common chemical notation is auto-formatted: trailing charges become superscripts (
"Sr2+"), embedded digits become subscripts ("TiO6"). Labels containing$are passed through as explicit matplotlib mathtext.None(the default) uses species names for all entries. Ignored when items is provided.
- items
Explicit legend entries. When provided, the legend displays these items instead of auto-generating from species. species, labels, and the tuple/dict forms of circle_radius are all ignored. Items with
radius=Nonefall back to circle_radius when that is a plain float, or to 5.0 points otherwise.
- Parameters:
corner (
WidgetCorner|tuple[float,float] (default:<WidgetCorner.BOTTOM_RIGHT: 'bottom_right'>))margin (
float(default:0.15))font_size (
float(default:10.0))circle_radius (
float|tuple[float,float] |dict[str,float] (default:5.0))spacing (
float(default:2.5))label_gap (
float(default:5.0))items (
tuple[LegendItem,...] |None(default:None))
- to_dict()[source]
Serialise to a JSON-compatible dictionary.
Fields at their default values are omitted.
- Return type:
- __init__(corner=WidgetCorner.BOTTOM_RIGHT, margin=0.15, font_size=10.0, circle_radius=5.0, spacing=2.5, label_gap=5.0, species=None, labels=None, items=None)
- Parameters:
corner (
WidgetCorner|tuple[float,float] (default:<WidgetCorner.BOTTOM_RIGHT: 'bottom_right'>))margin (
float(default:0.15))font_size (
float(default:10.0))circle_radius (
float|tuple[float,float] |dict[str,float] (default:5.0))spacing (
float(default:2.5))label_gap (
float(default:5.0))items (
tuple[LegendItem,...] |None(default:None))
- Return type:
None
- class hofmann.WidgetCorner[source]
Which corner of the viewport to place a widget.
Used by both
AxesStyle(defaultBOTTOM_LEFT) andLegendStyle(defaultBOTTOM_RIGHT).- BOTTOM_LEFT
Bottom-left corner.
- BOTTOM_RIGHT
Bottom-right corner.
- TOP_LEFT
Top-left corner.
- TOP_RIGHT
Top-right corner.
- __new__(value)
- class hofmann.SlabClipMode[source]
How slab clipping interacts with coordination polyhedra.
Controls whether polyhedra at the slab boundary are drawn partially, dropped entirely, or forced to be complete.
- PER_FACE
Drop individual faces whose vertices lie outside the slab. May produce partial polyhedron fragments.
- CLIP_WHOLE
If any vertex of a polyhedron is outside the slab, skip the entire polyhedron and its centre-to-vertex bonds.
- INCLUDE_WHOLE
If the centre atom is inside the slab, force all vertices and bonds of the polyhedron to be visible regardless of slab depth.
- __new__(value)
- hofmann.rendering.static.render_mpl(scene, output=None, *, ax=None, style=None, frame_index=0, figsize=(5.0, 5.0), dpi=150, background='white', show=None, colour_by=None, cmap='viridis', colour_range=None, **style_kwargs)[source]
Render a StructureScene as a static matplotlib figure.
Uses a depth-sorted painter’s algorithm: atoms are sorted back-to-front, and each atom’s bonds are drawn just before the atom itself is painted. Bond-sphere intersections are computed in 3D and then projected to screen space.
Example usage:
scene = StructureScene.from_xbs("ch4.bs") # Save to file (no interactive window): scene.render_mpl("ch4.png") # Publication-quality SVG with custom sizing: scene.render_mpl("ch4.svg", figsize=(8, 8), dpi=300, background="black") # Interactive display (no file): scene.render_mpl() # Custom style with no outlines: from hofmann import RenderStyle style = RenderStyle(show_outlines=False, atom_scale=0.8) scene.render_mpl("clean.svg", style=style) # View along the [1, 1, 1] direction with a depth slab: scene.view.look_along([1, 1, 1]) scene.view.slab_near = -2.0 scene.view.slab_far = 2.0 scene.render_mpl("slice.png") # Colour by per-atom metadata: scene.set_atom_data("charge", charges) scene.render_mpl(colour_by="charge", cmap="coolwarm") # Render into an existing axes for multi-panel figures: import matplotlib.pyplot as plt fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 5)) ax1.plot(x, y) scene.render_mpl(ax=ax2) fig.savefig("panel.pdf", bbox_inches="tight")
- Parameters:
scene (
StructureScene) – The StructureScene to render.output (
str|Path|None(default:None)) – Optional file path to save the figure. The format is inferred from the extension (e.g..svg,.pdf,.png). Ignored when ax is provided.ax (
Axes|None(default:None)) – Optional matplotlibAxesto draw into. When provided, the scene is rendered onto this axes and the caller retains control of the parent figure (saving, display, layout). The caller is responsible for saving and closing the figure. The output, figsize, dpi, background, and show parameters are ignored.style (
RenderStyle|None(default:None)) – ARenderStylecontrolling visual appearance. IfNone, defaults are used. AnyRenderStylefield name may also be passed as a keyword argument to override individual fields (e.g.show_bonds=False,half_bonds=False).frame_index (
int(default:0)) – Which frame to render (default 0).figsize (
tuple[float,float] (default:(5.0, 5.0))) – Figure size in inches(width, height).dpi (
int(default:150)) – Resolution for raster output formats.background (
str|float|tuple[float,float,float] |list[float] (default:'white')) – Background colour (CSS name, hex string, grey float, or RGB tuple).show (
bool|None(default:None)) – Whether to callplt.show()to open an interactive window. Defaults toTruewhen output isNone,Falsewhen saving to a file. Pass explicitly to override (e.g.show=Trueto both save and display).colour_by (
str|list[str] |None(default:None)) – Key intoscene.atom_datato colour atoms by. WhenNone(the default), species-based colouring is used.cmap (
str|Callable[[float],Sequence[float]] |list[str|Callable[[float],Sequence[float]]] (default:'viridis')) – Matplotlib colourmap name (e.g."viridis"),Colormapobject, or callable mapping a float in[0, 1]to an(r, g, b)tuple.colour_range (
tuple[float,float] |None|list[tuple[float,float] |None] (default:None)) – Explicit(vmin, vmax)for normalising numerical data.Noneauto-ranges from the data.**style_kwargs (
object) – AnyRenderStylefield name as a keyword argument. Unknown names raiseTypeError.
- Return type:
- Returns:
The matplotlib
Figureobject.
- hofmann.rendering.static.render_legend(scene, output=None, *, legend_style=None, show_outlines=True, outline_colour=(0.15, 0.15, 0.15), outline_width=1.0, polyhedra_shading=1.0, light_direction=(0.0, 0.0, 1.0), dpi=150, background='white', transparent=False, figsize=None)[source]
Render a standalone species legend as a tight matplotlib figure.
Produces a figure containing only the legend — no structure, bonds, cell edges, or axes widget. Useful for composing figures manually in external tools (Inkscape, Illustrator, LaTeX).
The legend entries, colours, and circle sizes are determined by the scene’s atom styles and the legend_style settings, using the same rendering code as the in-scene legend drawn by
show_legend=True.- Parameters:
scene (
StructureScene) – The structure scene (provides species and atom styles).output (
str|Path|None(default:None)) – Optional file path to save the figure. The format is inferred from the extension (e.g.".svg",".png").legend_style (
LegendStyle|None(default:None)) – Visual style for the legend.Noneuses defaults. SeeLegendStyle.show_outlines (
bool(default:True)) – Whether to draw outlines around legend circles.outline_colour (
str|float|tuple[float,float,float] |list[float] (default:(0.15, 0.15, 0.15))) – Colour for circle outlines when show_outlines isTrue.outline_width (
float(default:1.0)) – Line width for circle outlines in points.polyhedra_shading (
float(default:1.0)) – Shading strength for 3D polyhedron legend icons (0 = flat, 1 = full).light_direction (
tuple[float,float,float] (default:(0.0, 0.0, 1.0))) – Direction of the virtual light source for polyhedra face shading, in screen space (x = right, y = up, z = towards viewer). Must have exactly 3 components. Normalised internally before use. The zero vector raisesValueError.dpi (
int(default:150)) – Resolution for raster output formats.background (
str|float|tuple[float,float,float] |list[float] (default:'white')) – Figure background colour.transparent (
bool(default:False)) – IfTrue, save with a transparent background. Useful for embedding in documents or web pages with non-white backgrounds.figsize (
tuple[float,float] |None(default:None)) – Figure size in inches(width, height). When provided the saved image is this exact size with the legend centred inside; whenNone(the default) the saved image is tight-cropped to the legend artists. Only affects the saved file — the returned figure always uses a fixed internal canvas.
- Return type:
- Returns:
The matplotlib
Figure. When output is given the figure is saved and then closed; otherwise it remains open for further manipulation (note that the figure canvas is larger than the cropped output).
Example:
from hofmann import LegendStyle from hofmann.rendering.static import render_legend fig = render_legend(scene, "legend.svg") # Proportional circle sizes: style = LegendStyle(circle_radius=(3.0, 8.0)) fig = render_legend(scene, "legend.svg", legend_style=style)
- hofmann.rendering.interactive.render_mpl_interactive(scene, *, style=None, frame_index=0, figsize=(5.0, 5.0), dpi=150, background='white', colour_by=None, cmap='viridis', colour_range=None, **style_kwargs)[source]
Interactive matplotlib viewer with mouse and keyboard controls.
Opens a matplotlib window where the user can manipulate the view with the mouse and keyboard:
Mouse:
Left-drag to rotate the structure.
Scroll to zoom in/out.
Keyboard:
Arrow keys rotate around the horizontal/vertical axes.
, / . roll in the screen plane.
+ / = / - zoom in/out.
Shift+Arrow keys pan the view.
p / P increase/decrease perspective strength.
d / D increase/decrease viewing distance.
b toggle bonds, o toggle outlines, e toggle polyhedra, u toggle unit cell, a toggle axes widget.
[ / ] step to the previous/next frame; { / } jump to the first/last frame.
f toggle frame indicator, g go to a specific frame, s set frame step size.
r reset the view to its initial state.
h toggle a help overlay listing all keybindings.
When the window is closed the updated
ViewStateandRenderStyleare returned, allowing the user to re-use both for static rendering:view, style = scene.render_mpl_interactive() scene.view = view scene.render_mpl("output.svg", style=style)
- Parameters:
scene (
StructureScene) – The StructureScene to render.style (
RenderStyle|None(default:None)) – ARenderStylecontrolling visual appearance. AnyRenderStylefield name may also be passed as a keyword argument to override individual fields.frame_index (
int(default:0)) – Which frame to render initially.figsize (
tuple[float,float] (default:(5.0, 5.0))) – Figure size in inches(width, height).dpi (
int(default:150)) – Resolution.background (
str|float|tuple[float,float,float] |list[float] (default:'white')) – Background colour.colour_by (
str|list[str] |None(default:None)) – Key (or list of keys) intoscene.atom_datato colour atoms by.cmap (
str|Callable[[float],Sequence[float]] |list[str|Callable[[float],Sequence[float]]] (default:'viridis')) – Matplotlib colourmap name, object, or callable. When colour_by is a list, may also be a list of the same length.colour_range (
tuple[float,float] |None|list[tuple[float,float] |None] (default:None)) – Explicit(vmin, vmax)for numerical data. When colour_by is a list, may also be a list of the same length.**style_kwargs (
object) – AnyRenderStylefield name as a keyword argument. Unknown names raiseTypeError.
- Return type:
- Returns:
A
(ViewState, RenderStyle)tuple reflecting any view and style changes applied during the interactive session.
Colours and defaults
- hofmann.Colour: str | float | tuple[float, float, float] | list[float]
A colour specification accepted throughout hofmann.
Can be any of:
A CSS colour name or hex string (e.g.
"red","#ff0000").A single float for grey (
0.0= black,1.0= white).An RGB tuple or list with values in
[0, 1](e.g.(1.0, 0.0, 0.0)).
See
normalise_colour()for conversion to a normalised RGB tuple.
- hofmann.normalise_colour(colour)[source]
Convert a colour specification to a normalised (r, g, b) tuple.
Accepts CSS colour names (e.g.
"red"), hex strings (e.g."#FF0000"), grey floats (e.g.0.7), or RGB tuples (e.g.(1.0, 0.3, 0.3)).
- hofmann.CmapSpec
Type alias for colourmap specifications accepted by the
cmapparameter of render methods. See the type definition inhofmann.modelfor details.
- hofmann.ELEMENT_COLOURS: dict[str, tuple[float, float, float]]
Mapping from element symbols to muted, publication-friendly RGB colours. Common elements use hand-picked colours; less common elements use desaturated tones grouped by periodic table region. Values are normalised to the
[0, 1]range.
- hofmann.COVALENT_RADII: dict[str, float]
Covalent radii in angstroms, from Cordero et al., Dalton Trans. 2008. Used by
default_atom_style()for display radii.
- hofmann.default_atom_style(element)[source]
Return a default AtomStyle for the given element symbol.
Uses Cordero covalent radii and a muted colour palette. Falls back to grey and a radius of 1.0 for unknown elements.
- hofmann.default_bond_specs(species, *, bond_radius=None, bond_colour=None)[source]
Generate BondSpec rules from VESTA bond length cutoffs.
Creates one spec per unique species pair that has a VESTA cutoff entry (including same-element pairs such as C-C where present). Pairs absent from the VESTA data are silently skipped.
- Parameters:
species (
Sequence[str]) – Species labels to generate rules for.bond_radius (
float|None(default:None)) – Visual radius of the bond cylinder. Defaults toBondSpec.default_radiuswhen not given.bond_colour (
str|float|tuple[float,float,float] |list[float] |None(default:None)) – Default colour for all generated bonds. Defaults toBondSpec.default_colourwhen not given.
- Return type:
- Returns:
A list of BondSpec rules, one per unique pair.
Style I/O
- class hofmann.StyleSet[source]
A collection of style settings loaded from or saved to a file.
All fields are optional. A
StyleSetloaded from a file that only contains"atom_styles"will havebond_specs,polyhedra, andrender_styleset toNone.- atom_styles
Per-species visual styles, keyed by species label.
- bond_specs
Bond detection and appearance rules.
- polyhedra
Polyhedron rendering rules.
- render_style
Global rendering parameters.
- Parameters:
- __init__(atom_styles=None, bond_specs=None, polyhedra=None, render_style=None)
- hofmann.save_styles(path, *, atom_styles=None, bond_specs=None, polyhedra=None, render_style=None)[source]
Save style settings to a JSON file.
Only sections that are not
Noneare written. The file is human-readable with two-space indentation.- Parameters:
atom_styles (
dict[str,AtomStyle] |None(default:None)) – Per-species visual styles.bond_specs (
list[BondSpec] |None(default:None)) – Bond detection and appearance rules.polyhedra (
list[PolyhedronSpec] |None(default:None)) – Polyhedron rendering rules.render_style (
RenderStyle|None(default:None)) – Global rendering parameters.
- Return type:
- hofmann.load_styles(path)[source]
Load style settings from a JSON file.
All sections are optional. Unknown top-level keys raise
ValueError.- Parameters:
- Return type:
- Returns:
A
StyleSetwith the parsed sections.- Raises:
ValueError – If the file contains unknown top-level keys.
Bond and polyhedra computation
- hofmann.compute_bonds(species, coords, bond_specs, lattice=None)[source]
Compute bonds for a single frame based on bond specification rules.
For each pair of atoms, checks all bond specs in order to find the first matching rule where the interatomic distance falls within
[min_length, max_length].When lattice is provided, bonds across periodic boundaries are found correctly. If all bond lengths are shorter than the inscribed sphere radius of the cell, the minimum image convention (MIC) is used for an efficient O(n²) computation. Otherwise, all 27 images in
{-1, 0, 1}^3are checked iteratively to handle multi-image bonds (e.g. atoms on opposite cell faces at half a lattice parameter apart).- Parameters:
species (
tuple[str|Composition,...]) – Species labels, one per atom.coords (
ndarray) – Coordinates array of shape(n_atoms, 3).bond_specs (
list[BondSpec]) – List of BondSpec rules to apply.lattice (
ndarray|None(default:None)) – 3x3 matrix of lattice vectors (row vectors).Nonefor non-periodic scenes.
- Return type:
- Returns:
List of Bond objects for all detected bonds.
- hofmann.compute_polyhedra(species, coords, bonds, polyhedra_specs)[source]
Compute coordination polyhedra from bonds and declarative specs.
For each atom whose species matches a
PolyhedronSpeccentre pattern, the bonded neighbours are collected and their convex hull is computed. Adjacent coplanar triangles are then merged into polygonal faces to reduce visual artefacts from triangulation seams.Specs are applied in order; the first matching spec wins for each atom (consistent with
compute_bonds()).- Parameters:
species (
tuple[str|Composition,...]) – Species labels, one per atom.coords (
ndarray) – Coordinate array of shape(n_atoms, 3).polyhedra_specs (
list[PolyhedronSpec]) – Declarative polyhedron rules.
- Return type:
- Returns:
List of
Polyhedronobjects.