# What's New in *playNano* 0.4.0 **Release date:** 226-05-11 This release adds a new file reader for Asylum Research high-speed AFM data, three new animated export formats, a unified colormap system with perceptually linear maps designed for AFM data, and a vertical-flip processing filter. It also extends Python and NumPy support, refactors the visual rendering pipeline, and includes numerous fixes to per-frame metadata handling across all loaders. ## Added - **ARIS File Reader:** playNano can now load Asylum Research `.aris` high-speed AFM files. The loader extracts channel names from HDF5 metadata, computes global and per-frame pixel-size scaling with well-defined fallback behaviour, sorts and stacks frames, and parses and validates timestamps. Multi-suffix file loading (e.g. `.ome.tif`) is also now supported in the loader dispatch logic. - **Video and Image Sequence Export:** Two new animated export formats are available alongside GIF: - **Video** — MP4, AVI, MOV, and MKV via `playnano.io.video_export`. - **Image sequences** — individual PNG or JPEG files in a named folder via `playnano.io.image_sequence_export`. All three formats share a unified rendering pipeline in the new `playnano.io.render_utils` module, which enforces a minimum output resolution (512 px), ensures scale bars are physically accurate regardless of frame size, and keeps annotation proportions consistent across resolutions. See :doc:`exporting` for full details. - **Native Colormaps:** Three perceptually designed colormaps are now bundled and registered globally as Matplotlib colormaps on import: - `afm_brown` *(default)* — perceptually linear (R² ≈ 0.996), eliminates the dark plateau and flicker artefacts common in traditional AFM colour schemes. - `playnano_gold` — full dynamic range (L* 0–100), high contrast for complex topography. - `classic_afm` — non-linear legacy map for continuity with existing workflows. Reversed variants (`_r`) are registered automatically. See :doc:`colormaps` for background, usage, and a comparison with `afmhot`. - **Vertical Flip Filter:** A new built-in processing filter `vertical_flip` reverses the row order of a 2D frame using `numpy.flipud`. - **Python 3.13 and NumPy 2.x Support:** Python 3.13 is now officially supported and included in the CI test matrix. The NumPy upper-version pin (`<2.0`) has been removed, enabling compatibility with NumPy 2.0 and later. - **CLI Additions:** - `--version` global flag prints the installed playNano version and exits. - `--cmap` selects a colormap in `play`, `process`, and `wizard`. - `--fps` controls frame rate for animated exports. In `play`, the argument is optional and falls back gracefully to a default of 5 fps with a logged warning if a non-numeric value is supplied; in `process`, a numeric value is required. - `--make-video` exports video after processing (default format: MP4). - `--make-sequence` exports a PNG image sequence after processing. - `--draw-ts` toggles timestamp rendering on animated exports (default: off). - Wizard REPL extended: after GIF export the wizard now prompts for timestamps, scale-bar length, and optionally exports a video with configurable `zmin`, `zmax`, timestamps, and scale bar. - **`src/playnano/resources/`:** A dedicated package for non-code assets. Bundled fonts have been moved here from `src/playnano/fonts/`, and colormap CSV files are loaded via `importlib.resources` for robust cross-platform asset access. - **`sphinx-apidoc` Integration:** API documentation is now generated automatically via a `builder-inited` hook in `conf.py`, removing the need to maintain RST stubs manually. ## Changed ### Per-Frame Pixel-Size Semantics `AFMImageStack.pixel_size_nm` now represents the global or first-frame pixel size only. Per-frame overrides are stored in `frame_metadata[i]["frame_pixel_size_nm"]` and are read via the new `AFMImageStack.scaling_for_frame(idx)` method. For most datasets the values are identical; differences arise when scan size changes mid-acquisition (e.g. ARIS files). GUI playback and all animated exports now respect per-frame pixel size. All loaders have been updated to populate `frame_pixel_size_nm` in every `frame_metadata` entry. The `frame_metadata` construction bug that caused entries to be overwritten or incomplete in the `.spm` and `.jpk` loaders has been fixed. The `.h5-jpk` and `.asd` loaders record a constant value; `.jpk`, `.spm`, and `.aris` record per-frame values. OME-TIFF export now raises a `ValueError` if pixel sizes are not consistent across frames, since the format only supports a single `PhysicalSizeX`/`PhysicalSizeY` value. ### Visualisation Rendering The rendering logic previously embedded in `export_gif` has been extracted into `playnano.io.render_utils`, which is now the single source of truth for frame normalisation, colourisation, upscaling, and annotation across all visual exports. Visual constants (minimum frame height, reference height, annotation colour, font scale) are centralised here. The default colormap for all exports has changed from `afmhot` to `afm_brown`. ### GUI - A colormap selection dropdown has been added to the export panel. - The z-scale histogram bars are now coloured with the active colormap and update dynamically when `zmin`, `zmax`, or the colormap change. - The GIF export panel has been replaced with a unified **Save Animated Data** panel supporting GIF, MP4, AVI, and PNG folder exports via per-format checkboxes. - FPS is now initialised from `--fps` if provided, otherwise derived from `line_rate` in the first frame's metadata (previously always defaulted to 10 fps in the controls). ### Developer Tooling - Pre-commit exclude paths updated from `src/playnano/fonts/` to `src/playnano/resources/fonts/` across all hooks. - `np.string_()` replaced with `np.bytes_()` in HDF5 export for NumPy 2.x compatibility. - `_decode_attr` in `read_h5jpk` promoted to `playnano.utils.io_utils.decode_hdf5_attr` and reused in `read_aris`. ## Fixed - Fixed loader errors caused by missing file extensions or no-suffix inputs; the loader now correctly handles multi-suffix formats such as `.ome.tif` and `.ome.tiff`. - Connected FPS calculation based on `line_rate` to GUI playback; previously the computed value was not passed through to the GUI controls. - Fixed `frame_metadata` construction in `.spm` and `.jpk` folder loaders: entries were previously overwritten in the loop, resulting in only the last frame's metadata being retained. - Standardised per-frame metadata keys across all formats: `timestamp`, `frame_pixel_size_nm`, and `line_rate`. - Removed the inconsistent pixel-size equality check in `.spm` and `.jpk` folder loaders; per-frame variation is now recorded rather than raising a `ValueError`. - Improved numerical robustness in tests: floating-point comparisons now use `pytest.approx` for cross-platform consistency. ## Documentation - New :doc:`colormaps` page covering the three native colormaps, their perceptual properties, usage in the CLI and GUI, and background on perceptual linearity in AFM visualisation. - :doc:`exporting` restructured into **Animated Exports** (GIF, video, image sequence) and **Data Exports** (OME-TIFF, NPZ, HDF5) sections, with full CLI and programmatic usage for each format. - :doc:`cli` updated with global options (`--version`, `--log-level`), new `process` and `play` flags, and corrected help text for FPS defaults. - :doc:`gui` updated to document the colormap selector, unified animated export panel, and revised annotation controls. - :doc:`introduction` and :doc:`index` updated to include `.aris` in the list of supported formats. - `processing-operations-reference` updated to include `vertical_flip`. - API reference reorganised into captioned sections: Core, IO & Data Formats, Processing Pipeline, Analysis & Modules, General Utilities, CLI & App Utils, and Graphical Interface. ## Pytest Coverage Added - ARIS HDF5 loading: channel extraction, global pixel scaling, frame key sorting, per-frame pixel-size overrides and fallback to global scale, timestamp mismatch, and missing channel errors. - Multi-suffix file handling: `.ome.tif`, `.ome.tiff`, and all single-suffix formats via `get_loader_for_file`. - Video export: all four container formats, flat data, various z-scale configurations, raw/processed data selection, and early-exit when `make_video=False`. - Image sequence export: PNG and JPEG output, flat data, various z-scale configurations, raw/processed data selection, and invalid format rejection. - `render_utils`: upscaling behaviour, global normalisation, flat-frame handling, NaN/inf robustness, and physically accurate scale bar width across frame sizes and pixel sizes. - Colormap utilities: `resolve_cmap` fallback and warning, load-failure error logging, `is_valid_cmap` with non-string and unregistered inputs, `get_available_cmaps` sort order. - GUI: `_get_cmap` fallback, `_on_cmap_changed` with valid and invalid names, `_update_histogram_colors` z-range selection, and `_export_animated` raw/processed branch logic. - CLI FPS parsing: `play` with no flag, flag without value, valid numeric value, and invalid string; `process` error on invalid value; help text content. - `AFMImageStack.scaling_for_frame`: present and missing `frame_pixel_size_nm` entries.