eyepiece
Plotting and animation primitives for astronomical imaging simulations.
What eyepiece is
eyepiece provides ax-first, stateless figure functions for the plots that
recur across direct-imaging work: log-scaled and diverging image displays,
side-by-side and A/B comparison panels, radial profiles and contrast curves,
corner plots, orbit and sky-track scenes, optical-train schematics, and a
multi-sink animation recorder that grabs frames once and writes them to any
combination of gif, mp4, and html. Every primitive takes plain arrays in and
returns a small result object holding the axes and the artists it drew, so
callers can update or extend a figure without re-deriving which line or
image object came from where.
For a scene that has to appear more than once, eyepiece also offers prepared
views: plain records of images, curves, tracks, and their marks, carrying
borrowed arrays, units, masks, display scales, and stable IDs. A simulation
library prepares a scene once, and the same records render as a paper still
or a movie through eyepiece.mpl, and as native Manim objects for a talk
through eyepiece.manim, without recomputing any science.
Figure style (color, colormaps, and mode) is resolved through
hwostyle at call
time, not at import time. import eyepiece never activates a style or
touches matplotlib's rcParams. Without an active hwostyle mode, primitives
fall back to a fixed light-mode palette, so eyepiece also works as a
standalone plotting library.
What eyepiece is not
- Not a simulation library. eyepiece takes arrays and returns figures; it never imports a simulation, orbit, or optics package.
- Not a style engine. Color, colormap, and mode definitions live in hwostyle; eyepiece only reads them at call time.
- Not a data pipeline. Aggregating or transforming simulation output before plotting is the caller's job.
What is in it
Every name below is importable straight from eyepiece; the submodules that
implement them are internal organization.
- Images.
imshow_log(log scale clipped to a floor, so a zero-valued pixel cannot break the norm),imshow_diverging(symmetric norm about zero), both placed byextentor by pixelcenters,kymograph(a cut stacked against time or wavelength),show_field(amplitude and phase panels of a complex field),compare_row(several images sharing one norm and colorbar),compare_grid(the same on a 2D layout with empty cells allowed), andtriptych(A, B, and a ratio or residual comparison panel, side by side). - Annotations.
overlay_circleandoverlay_line(dashed circles and paths that read over bright and dark pixels),ruler(a dimension arrow), andbracket(a labeled span). - Distributions and sequences.
corner,corner_overlay(a second sample set laid over an existing triangle plot),hist_vs_pdf,cov_ellipse, and three that reveal samples one at a time:convergence(a running mean or sum),signed_trace(a trace shaded above and below a level), andhist_fill(a histogram filling on a pinned scale). - Profiles.
plot_radial(a precomputed radial profile line),plot_contrast_curve(a contrast curve with inner/outer working angle shading and reference floor curves, drawn once per axes even across repeated calls, with each curve taking the next palette color so two calls on one axes are distinguishable), andradial_profile_plot(computes the profile viahwoutilsand plots it in one call, under the[hwo]extra). - Scenes.
trail(a 2D or 3D trajectory with depth-cued markers),sky_fan(weighted candidate sky tracks with an inner-working-angle disk), andfading_track. - Schematics.
rail(an optical-train diagram built from a plain(label, glyph)element list, over theGLYPHSvocabulary), andschematic, a preset wrapper overrailfor the imager and coronagraph trains that come up constantly. - Motion.
Timeline(an animation's holds and ramps declared in seconds and resolved for any frame rate into aPlanof per-frame knob values), withease,stagger, andframe_count. - Cameras and morphs.
zoom_path(the optimal pan-and-zoom path between two views),view_limitsandoverview_boxfor a camera that zooms an axes,PageCamera(any view of a finished figure, re-rendered sharp at a fixed output size, with an optional overview margin),spotlight(a veil that dims a figure outside chosen rectangles),quad_image(an image drawn as one movable shape per pixel, frompixel_quads), andbrush(the same objects lit in several panels at once). - Layout. Pixel-edge extent helpers (
extent_lod,extent_arcsec,extent_au, ...) with matching axis labelers, plusFrameandSourceStylesfor keeping several panels of one scene consistent. - Output.
save_figfor a styled write to disk,record(a context manager that opens every sink at once and takes frames from a loop the caller drives) andanimate(which binds a figure, a draw function, and a frame source into the publicAnimationtype it returns),RawSink(raw frames from anywhere, such as aPageCamera, into a reproducible mp4), andPRESETSof measured fps/dpi pairs.
Prepared views live in namespaces of their own:
eyepiece.prepared. The frozen records (ImageView,CurveView,TrackView,PanelGroup, and thePath,Points,Region,ReferenceLine, andLabelmarks),Sequencefor replayable states over physical time, and the shared display mapping. It imports only NumPy.eyepiece.style.SourceCastfor fixed source colors and markers, andsnapshot_profile, which captures the active hwostyle appearance as a read-onlyRenderProfile.eyepiece.mpl.renderandanimatefor Matplotlib stills and movies.eyepiece.manim.renderandanimatefor native Manim Community objects, usable in a Manim scene or a Manim Slides deck, withUnitsfor point sizes andensure_fontfor Pango fonts.
Usage
import eyepiece as ep
result = ep.imshow_log(psf, extent=ep.extent_lod_from_pixels(psf.shape[0], 0.5))
ep.label_lod(result.ax)
ep.save_fig(result.fig, "psf")
Every primitive returns a small result object carrying the axes it drew on
and the artists it made (keyed by the ARTIST_KEYS vocabulary), so a caller
can keep working on the figure without hunting for the objects again. An
image primitive also returns an .update that redraws with new data through
the same transform, which is what makes animation a few lines:
frames = [cube[k] for k in range(len(cube))]
result = ep.imshow_log(frames[0])
def draw(fig, k):
result.update(frames[k])
ep.animate(result.fig, draw, len(frames), fps=10).save("run.mp4", "run.gif")
A prepared sequence gives a still, a strip of selected epochs, and a movie from one preparation:
import eyepiece.mpl as mpl
from eyepiece.style import SourceCast, snapshot_profile
cast = SourceCast(["planet b"])
still = mpl.render(sequence.frame(10), cast=cast)
strip = mpl.render(sequence.strip([0, 10, 20]), cast=cast)
movie = mpl.animate(sequence, run_time=8, fps=30, cast=cast)
movie.save("run.mp4")
The same sequence plays in a talk through Manim:
import eyepiece.manim as em
clip = em.animate(sequence, cast=cast, profile=snapshot_profile())
self.add(clip.mobject) # inside a Manim Scene or Slide
self.play(clip.playback(run_time=8))
rail draws a miniature optical-train diagram from a plain element list, so
a physics panel can sit beside a reminder of which plane it shows:
import eyepiece as ep
result = ep.rail(
[("Pupil", "pupil"), ("FPM", "fpm"), ("Lyot", "lyot"), ("Focal", "focal")],
highlight="FPM",
)
ep.save_fig(result.fig, "coronagraph_rail")
Status
eyepiece is young: the primitives above are implemented and tested, and the public API may still shift before 1.0.
Installation
pip install eyepiece
Unit conversions used by a small number of layout helpers (arcsecond and AU
extents) and radial_profile_plot's profile computation are optional and
pull in hwoutils:
pip install eyepiece[hwo]
The Manim renderer is optional too. [manim] adds Manim Community, and
[slides] adds Manim Slides beside it; Manim needs system Cairo, Pango,
pkg-config, and ffmpeg:
pip install "eyepiece[manim]"
pip install "eyepiece[slides]"
License
MIT
Metadata
Release files for eyepiece 0.8.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| eyepiece-0.8.0.tar.gz | 178.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| eyepiece-0.8.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 362.7 kB
Release files / eyepiece-0.8.0.tar.gz
| Download URL | eyepiece-0.8.0.tar.gz |
|---|---|
| Size | 178.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
948146a3930511184bfee74d4df5d68cbda8631a7aafff935f03149cc823e702
|
|
BLAKE2b-256 checksum How to use checksums |
c66b0c9caa80ea7d92c0930bd9f323cc52f96d6146e8b3b6a4819dc07e918f99
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.
Transparency logRelease files / eyepiece-0.8.0-py3-none-any.whl
| Download URL | eyepiece-0.8.0-py3-none-any.whl |
|---|---|
| Size | 184.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
eb1c6c04a420c7521bfc9c1af0906278887ad50814234a5d2b602b4a9362d938
|
|
BLAKE2b-256 checksum How to use checksums |
eb4785a02908d5461b1fa6dd398a6201c37f4af691d9a15ba0e725d3c1f75344
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.
Transparency log