Skip to content

[FEATURE] Save the outputs in more formats, with a caller-specified background color and a shipped font #266

Description

@camUrban

Problem Statement

The output functions each write exactly one format: plot_results_versus_time saves png, draw saves WebP, and animate saves animated WebP. Other venues need other formats: vector formats for papers and slides, and video formats for players that do not handle animated WebP. The suffix is already validated by _parameter_validation.pathLike_return_path, so format dispatch can ride on the file name, and the new surface is a wider suffix tuple plus a background_color parameter rather than a format enum. The formats, the background color, and the font only work as one design: several of the new formats cannot represent transparency, which forces the background decision, and the vector exports only stay layout-exact if every rendering pipeline shares one font.

Location(s): pterasoftware/output.py, pterasoftware/_output_plotting.py, pterasoftware/_output_rendering.py, pterasoftware/_parameter_validation.py

Proposed Solution

  1. Formats: The plots gain webp, jpg, svg, and pdf alongside png. draw gains png, jpg, svg, and pdf alongside webp. animate gains webm, apng, and mp4 alongside webp, with gif gated on the check below and a multi-page pdf and an animated svg as stretch goals. eps is deliberately absent: pdfLaTeX toolchains, Overleaf included, cannot consume it, PostScript has no alpha channel, and eps lacks pdf's stream compression, so pdf serves every venue eps would.
  2. The two vector formats split by venue. pdf is the vector format for documents: matplotlib writes it natively from savefig with pdf.fonttype set to 42 so the embedded fonts are TrueType rather than Type 3, and the GL2PS exporter behind save_graphic writes it directly from draw's scene. svg is the vector format for the web and for PowerPoint, which imports svg as true vector but cannot insert a pdf as anything but an icon, and it is the foundation the animated stretch goal stands on. Both draw exports share the GL2PS pipeline, so its handling of the image surface plane's texture, translucency, and the scalar bar widget needs investigating once, for both.
  3. A background_color parameter defaulting to None, meaning transparent where the format can carry it. A format that cannot (jpg and mp4 have no alpha channel, and gif's is binary) requires an explicit color rather than silently flattening onto one. One sub-decision to make here: the fixed text colors are tuned against the current backgrounds, so decide whether an unreadable caller-chosen background is the caller's problem or something the functions warn about.
  4. One shipped font used everywhere. Vendor Liberation Sans with its OFL 1.1 license file, following the pattern _colormap_data set for third-party assets. It is metric-compatible with Arial, which is itself metric-compatible with Helvetica, and that chain keeps the layout exact: GL2PS emits draw's overlay text as real, selectable text under the Helvetica name and computes its alignment with Helvetica metrics, pdf viewers must render base-14 Helvetica without any embedding, and an svg post-processor can bind the shipped font to that name as a subset base64 @font-face through fonttools, which matplotlib already depends on (woff2 output needs brotli, plain ttf does not). matplotlib registers the shipped ttf for the plots and subset-embeds it in pdf natively, and VTK's text properties can load it as a font file for the raster pipeline, so the on-screen render, the saved rasters, the plots, and the vector exports all share one typeface, and embedding it satisfies the journals that require every font embedded. Arimo, the same design under Apache 2.0, is the alternative if OFL ever chafes, and TextAsPath on the GL2PS exporter is the fallback if real-text export misbehaves, at the cost of selectability.

Additional Context

  • Measured points from benchmarking animation capture on the hero simulation: mp4 cannot store transparency, which is why it can never be the default, lossless APNG encoded quickly but came out 3.59 times larger than WebP at quality 75, and gif is limited to 256 colors with binary transparency.
  • gif is gated on history: the animations were originally gif exclusively, and recurring scalar bar artifacts plus the need for transparency forced the move to WebP. The likely culprit is gif's 256 color palette quantizing a continuous color map, so gif ships only if per-frame palettes, dithering, or a palette built from the color map demonstrably fixes the artifacts.
  • webm and mp4 bring an encoder dependency the package does not carry today, which deserves the same scrutiny as any new requirement.
  • The animation stretch goals are both vector flipbooks and are gated on the static vector exports proving out first, since they inherit every GL2PS caveat once per frame and have no inter-frame compression, so their cost grows linearly with the frame count times the scene's vector complexity. The multi-page pdf, one frame per page, is the documents entry: the LaTeX animate package plays it inside a Beamer talk, Acrobat, PDF-XChange, Foxit, and Okular support it, and every other viewer degrades gracefully to a static first page. The animated svg, one svg per frame flipped by SMIL visibility animation, is the web entry: it is markup rather than script, so it survives the sanitizers that strip scripting, GitHub's README rendering included. Sizing against the hero shows the limit: its final frame trails about 3,100 wake ring vortices, so a static vector export lands near 2 to 5 MB of markup, and 132 frames of that is on the order of 200 MB and a million DOM elements, far past what any viewer tolerates, so the viable territory is short, wake-free clips.
  • Open question: whether the plots' default format should move from png to webp. That is a behavior change needing deprecation warnings ahead of a major version increment, and it renames every plot file's suffix, so the documentation website pages and the expected-output tree move with it.

Metadata

Metadata

Assignees

Labels

featureNew feature or requestquestionFurther information is needed

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions