spacr.setting_animations

Registry for the short animations that explain visual spaCR settings.

The registry is intentionally independent of Qt. GUI code, documentation, plugins and tests can therefore resolve the same exact setting key to the same packaged GIF without importing the desktop application.

Attributes

ANIMATION_DOCS_BASE

Published setting-animation gallery used by API and GUI links.

SCHEMA_VERSION

Manifest schema understood by this spaCR release.

Exceptions

SettingAnimationError

Raised when packaged setting-animation metadata is invalid.

Classes

SettingAnimation

One explanatory animation and every exact setting key that uses it.

Functions

animation_for_setting(→ Optional[SettingAnimation])

Return the animation mapped to setting_key, if one exists.

animation_path_for_setting(→ Optional[pathlib.Path])

Return the installed GIF path for setting_key, if available.

animations_by_setting(→ Mapping[str, SettingAnimation])

Return an immutable-by-convention exact setting-key lookup.

iter_setting_animations(→ Iterator[SettingAnimation])

Iterate over all animations in deterministic gallery order.

measure_border_artifact(→ float)

Largest fraction of the frame border that changes after the first frame.

measure_border_object_removal(→ Dict[str, int])

Classify what a "remove border objects" animation actually removes.

measure_visible_change(→ float)

Fraction of the frame that changes between an animation's states.

setting_animations(→ Tuple[SettingAnimation, ...])

Load, validate and cache every packaged setting animation.

validate_animations_have_no_border_artifact(...)

No animation may flash a ring around itself.

validate_animations_show_something(→ Dict[str, float])

Every animation must visibly change something.

validate_border_animations_remove_only_edge_objects(...)

Every border animation must remove only objects crossing the well edge.

validate_setting_animation_assets(→ Dict[str, int])

Validate packaged files and optionally recompute their SHA-256 hashes.

Module Contents

exception spacr.setting_animations.SettingAnimationError[source]

Bases: RuntimeError

Raised when packaged setting-animation metadata is invalid.

Initialize self. See help(type(self)) for accurate signature.

class spacr.setting_animations.SettingAnimation[source]

One explanatory animation and every exact setting key that uses it.

Parameters:
  • slug – Stable identifier used by the GIF filename and docs anchor.

  • title – Short human-readable animation title.

  • category – Gallery section containing the animation.

  • scene – Deterministic renderer scene used to generate the GIF.

  • settings – Exact spaCR setting keys mapped to this animation.

  • relative_file – Safe path below the packaged animation directory.

  • sha256 – Expected SHA-256 digest recorded during generation.

  • frames – Encoded GIF frame count after identical-frame coalescing.

  • unique_frames – Number of visually distinct encoded frames.

  • byte_size – Generated GIF size in bytes.

property docs_anchor: str[source]

Return this animation’s stable Sphinx/HTML anchor.

property docs_url: str[source]

Return the published gallery URL anchored to this animation.

property path: pathlib.Path[source]

Return the installed GIF path for this animation.

spacr.setting_animations.animation_for_setting(setting_key: str) → SettingAnimation | None[source]

Return the animation mapped to setting_key, if one exists.

Parameters:

setting_key – exact, case-sensitive settings key to look up.

Exact matches take precedence. A numbered organelle slot can reuse its primary slot’s animation through the shared slot-name mapping; unknown settings and case variants still do not acquire unrelated help.

spacr.setting_animations.animation_path_for_setting(setting_key: str) → pathlib.Path | None[source]

Return the installed GIF path for setting_key, if available.

Parameters:

setting_key – exact settings key whose animation path is requested.

spacr.setting_animations.animations_by_setting() → Mapping[str, SettingAnimation][source]

Return an immutable-by-convention exact setting-key lookup.

spacr.setting_animations.iter_setting_animations() → Iterator[SettingAnimation][source]

Iterate over all animations in deterministic gallery order.

spacr.setting_animations.measure_border_artifact(path) → float[source]

Largest fraction of the frame border that changes after the first frame.

This measures an ENCODING fault rather than a drawing one. Saving a GIF with disposal=2 tells a decoder to clear each frame to the background colour before drawing the next; when the encoder then optimises a frame down to the sub-rectangle that actually changed, everything outside that rectangle is left showing the background. The animation plays with a bright ring flashing around it once per loop, and Qt’s QMovie renders it exactly as described – this was measured through the GUI path, not inferred from the file.

It also corrupts measure_visible_change(), because a ring around a 360x360 frame is 9.75% of it: an animation that shows almost nothing scores over 10% and clears the threshold on the artifact alone.

Parameters:

path – the GIF to measure.

Returns:

0.0 to 1.0, where 1.0 means an entire frame border changed. Returns 0.0 when the file cannot be read, so a caller sees the other animations rather than a traceback.

spacr.setting_animations.measure_border_object_removal(path) → Dict[str, int][source]

Classify what a “remove border objects” animation actually removes.

The reported failure – one edge object and one non-edge object disappear – was true, and three separate ways of measuring it agreed it was fine. Each of those is avoided here, deliberately:

  • Not first-vs-last, and not “the frame most different from frame 0”. This scene zooms in and back out, so the most-different frame is the one at full zoom and that diff is dominated by the camera: every object registers as changed. The pair used here is the FIRST and LAST frames at which the drawn well edge sits at the same column, which is the interval where the zoom is complete and only the removal happens.

  • Not the generator’s own touches flag. That flag is the thing under test; a check that classifies objects by it validates the generator against itself and reported “all correct” on the assets that shipped the bug. The well edge is recovered from the drawn pixels.

  • Not raw connected components. The well’s bright rule is drawn over the objects and splits each into a left and a right half, and the organelle glyph is a scatter of separate strokes. Classifying those fragments individually reports a straddling Golgi as ~30 interior objects. The mask is closed first so one object is one component.

Parameters:

path – the GIF to measure.

Returns:

{"edge_x", "before", "after", "crossing", "interior"} – the drawn well edge column, the two frame indices compared, and how many changed objects do and do not cross that edge.

Raises:

SettingAnimationError – if the animation cannot be measured, which for this check is a failure rather than a zero: an unreadable border animation is not a correct one.

spacr.setting_animations.measure_visible_change(path) → float[source]

Fraction of the frame that changes between an animation’s states.

The “after” state is the frame MOST DIFFERENT from the first, not the last: these GIFs loop, so the last frame is the first one again and comparing them reports zero change for every animation ever made.

Parameters:

path – the GIF to measure.

Returns:

fraction of pixels that differ, 0.0 to 1.0. Returns 0.0 when the file cannot be read as an animation, so a caller sees “shows nothing” rather than an exception.

spacr.setting_animations.setting_animations() → Tuple[SettingAnimation, ...][source]

Load, validate and cache every packaged setting animation.

Returns:

Animations in deterministic gallery order.

Raises:

SettingAnimationError – If the manifest or an asset is invalid.

spacr.setting_animations.validate_animations_have_no_border_artifact(*, maximum: float = MAX_BORDER_ARTIFACT) → Dict[str, float][source]

No animation may flash a ring around itself.

Separate from validate_animations_show_something() because the two fail in opposite directions: this artifact ADDS 9.75% of changed frame, so an animation carrying it passes the “shows something” check while showing the viewer a rectangle the setting has nothing to do with.

Parameters:

maximum – fraction of the frame border allowed to change.

Returns:

{slug: fraction} for every animation ABOVE the threshold, empty when they all pass. Returned rather than raised so a caller can report the whole list.

spacr.setting_animations.validate_animations_show_something(*, minimum: float = MIN_VISIBLE_CHANGE) → Dict[str, float][source]

Every animation must visibly change something.

An animation is a stronger claim than a sentence: a user who watches one believes it. One that changes nothing teaches nothing, and unlike a wrong sentence it cannot be spotted by reading the source.

Separate from validate_setting_animation_assets(), which checks that the FILES are intact – an unchanged, perfectly-hashed GIF passes that and fails this.

Parameters:

minimum – fraction of the frame that must change.

Returns:

{slug: fraction} for every animation BELOW the threshold, empty when they all pass. Returned rather than raised so a caller can report the whole list instead of the first one.

spacr.setting_animations.validate_border_animations_remove_only_edge_objects() → Dict[str, int][source]

Every border animation must remove only objects crossing the well edge.

The setting says “delete every label touching an image edge”. An animation that also removes something in the middle of the well teaches the opposite of the sentence beside it, and it looks finished while doing so – the file has the right size, the right digest, and 16% of the frame changes.

This reads the SHIPPED GIFs. tests/test_border_animation_geometry.py checks the same property in the generator’s spec, which is where the fault was fixed; this is the half that still holds if an asset is regenerated by a different build, edited, or shipped from a spec that was never re-rendered.

Returns:

{slug: interior_objects_removed} for each border animation that removes something not crossing the edge. Empty when they are all correct. Returned rather than raised so one report covers the family instead of stopping at the first.

spacr.setting_animations.validate_setting_animation_assets(*, check_hashes: bool = False) → Dict[str, int][source]

Validate packaged files and optionally recompute their SHA-256 hashes.

Parameters:

check_hashes – When true, read every GIF and compare its digest with the manifest. Runtime callers normally leave this false; release tests enable it.

Returns:

Counts for animations, mapped setting keys and total asset bytes.

Raises:

SettingAnimationError – If a file is absent, has changed size or has a digest different from the generated manifest.

spacr.setting_animations.ANIMATION_DOCS_BASE = 'https://einarolafsson.github.io/spacr/setting_animations.html'[source]

Published setting-animation gallery used by API and GUI links.

spacr.setting_animations.SCHEMA_VERSION = 1[source]

Manifest schema understood by this spaCR release.

Nested helpers

measure_border_artifact.perimeter(frame)

Flatten every border pixel into RGB rows exactly once.

Parameters:

frame – captured-animation RGB frame to sample.

Returns:

top and bottom bands followed by the left and right interior bands. Omitting corners from the side bands prevents double counting while preserving a stable comparison order.

spacr/setting_animations.py:370