output

package
v0.0.12 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 4, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

Package output is the back of the rendering pipe: it carries a Figure (a built plot) to a destination Surface — a file, an in-memory image, a desktop GPU window, or a browser canvas — through one frame model.

The drawing seam lives in package canvas (canvas.Canvas and its RasterCanvas / RecordingCanvas backends). This package sits on top: a Figure paints itself onto a canvas.Canvas, and a Surface decides where those pixels or vectors go. Render presents exactly one frame and is the entire static story (PNG/SVG/PDF/in-memory image); interactive surfaces add an event loop on top (see Session).

Concrete surfaces live in subpackages (output/file, output/image, output/window, output/web) and register themselves via Register in their init(). Platform selection is therefore a blank import — no build tag ever appears in user code. This core package imports none of them.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrUnknownSurface is returned by [NewSurface] when no factory is
	// registered under the requested name — usually because the platform
	// subpackage providing it was not blank-imported.
	ErrUnknownSurface = errors.New("output: unknown surface (blank-import its package to register it)")

	// ErrSurfaceConsumed is returned by a single-shot [Surface] (file, image)
	// when Acquire is called a second time. Such surfaces present exactly one
	// frame.
	ErrSurfaceConsumed = errors.New("output: surface already consumed (single-shot surface allows one frame)")

	// ErrNotLive is returned by [NewLiveSurface] when the named surface is
	// registered but does not implement [LiveSurface] (e.g. "file").
	ErrNotLive = errors.New("output: surface is not interactive")

	// ErrUnsupportedFormat is returned by a surface factory when the requested
	// encoder format is not one it can produce.
	ErrUnsupportedFormat = errors.New("output: unsupported format")

	// ErrNoImage is returned when an image was requested from a surface that
	// does not implement [Imager].
	ErrNoImage = errors.New("output: surface does not produce an image")
)

Sentinel errors returned by the output layer.

Functions

func Register added in v0.0.10

func Register(name string, f SurfaceFactory)

Register associates a SurfaceFactory with a name. It is called from a platform subpackage's init(); registering the same name twice panics, as it indicates two packages claiming the same surface.

func Render added in v0.0.10

func Render(ctx context.Context, fig Figure, surf Surface) error

Render draws a Figure onto a Surface and presents exactly one frame. This is the entire static story — PNG, SVG, PDF, in-memory image — and is also the per-frame primitive the interactive Session loop calls for live surfaces.

Types

type Action added in v0.0.10

type Action uint8

Action is the decision a Controller returns for an Event: what the Session should do next.

const (
	// ActionIgnore does nothing.
	ActionIgnore Action = iota
	// ActionRedraw is the fast path: re-render the current Figure under the
	// (possibly updated) viewport transform in [State]. GPU-bound, no rebuild.
	ActionRedraw
	// ActionRebuild is the slow path: call Source.Build again because the data
	// extent changed (viewport crossed the trained bounds, or a brush changed a
	// stat input). Scales retrain and stats recompute.
	ActionRebuild
	// ActionExport renders the current frame to the session's export surface,
	// if one was configured via [WithExportSurface].
	ActionExport
	// ActionClose ends the session.
	ActionClose
)

type Controller added in v0.0.10

type Controller interface {
	OnEvent(ev Event, st *State) Action
}

Controller decides, per event, what the Session does next. The default controller is DataSpaceController (data-space pan/zoom); swap it via WithController for linked brushing, custom gestures, etc.

func DataSpaceController added in v0.0.10

func DataSpaceController() Controller

DataSpaceController provides data-space pan (drag) and zoom (scroll wheel) that operates on scale bounds rather than a canvas-level affine transform.

Axes remain at fixed screen positions; only the data visible within the panel changes. Tick labels update dynamically to reflect the current viewport. This is the interactive behavior expected in professional plotting tools (matplotlib, Plotly, ggplot2 shiny).

Performance: each interaction mutates two float64 values per axis (O(1)), then triggers ActionRedraw. No rebuild, no data iteration.

For faceted plots, zoom/pan applies only to the panel under the cursor.

type ControllerFunc added in v0.0.10

type ControllerFunc func(ev Event, st *State) Action

ControllerFunc adapts a plain function to the Controller interface.

func (ControllerFunc) OnEvent added in v0.0.10

func (f ControllerFunc) OnEvent(ev Event, st *State) Action

OnEvent calls f.

type Event added in v0.0.10

type Event struct {
	Kind      EventKind
	X, Y      float64 // logical coordinates
	DX, DY    float64 // scroll / drag deltas
	Buttons   uint8
	Key       string
	Modifiers uint8
}

Event is the platform-neutral input event. output/window translates OS events into it; output/web translates DOM events into it. The Session and Controller see only this type — never a platform-specific event.

type EventKind added in v0.0.10

type EventKind uint8

EventKind enumerates the platform-neutral input event kinds delivered by a LiveSurface.

const (
	EventResize EventKind = iota
	EventPointerMove
	EventPointerDown
	EventPointerUp
	EventScroll
	EventKey
	EventClose
	EventDoubleClick // double-click / double-tap for viewport reset
)

Event kinds delivered by a LiveSurface.

type Figure added in v0.0.10

type Figure interface {
	Draw(ctx context.Context, dst canvas.Canvas, width, height int) error
}

Figure is something that can paint itself onto a canvas.Canvas at a given size. *ggplot.Built satisfies Figure with no change — its Draw method already has exactly this signature.

type Imager added in v0.0.10

type Imager interface {
	Image() image.Image
}

Imager is implemented by surfaces that publish an in-memory image (the "image" surface). Image returns the frame published by the last Commit.

type LiveSurface added in v0.0.10

type LiveSurface interface {
	Surface
	Events() <-chan Event
}

LiveSurface is a Surface with an interactive lifecycle. Desktop windows and browser canvases implement it; file and image surfaces do not. Interaction is deliberately absent from the Surface contract — a PNG export never links an event loop.

func NewLiveSurface added in v0.0.10

func NewLiveSurface(ctx context.Context, name string, opts ...SurfaceOpt) (LiveSurface, error)

NewLiveSurface is NewSurface plus a runtime assertion to LiveSurface. It returns ErrNotLive if the named surface is not interactive.

type Measurable added in v0.0.10

type Measurable interface {
	// PanelInfos returns the geometry of all panels from the last Draw call.
	// Returns nil if Draw has not been called yet.
	PanelInfos() []PanelInfo
}

Measurable is an optional Figure extension that exposes per-panel layout geometry after a Draw call. Controllers use this to convert pixel mouse positions to data coordinates for data-space pan/zoom.

The returned PanelInfos are valid only for the width×height that was last passed to Draw. A resize triggers a new Draw, which updates the geometry.

type PanelInfo added in v0.0.10

type PanelInfo struct {
	// Index is the zero-based panel index in the figure's panel slice.
	Index int

	// Bounds is the data-area rectangle in figure pixel coordinates.
	// This is the region where data is drawn — axes/titles are outside.
	Bounds image.Rectangle

	// XRange is the current (possibly zoomed) X data-space bounds [min, max].
	XRange [2]float64
	// YRange is the current (possibly zoomed) Y data-space bounds [min, max].
	YRange [2]float64

	// OrigXRange is the full trained X data-space bounds (for reset).
	OrigXRange [2]float64
	// OrigYRange is the full trained Y data-space bounds (for reset).
	OrigYRange [2]float64
}

PanelInfo describes the pixel geometry and data-space bounds of one panel. Returned by Measurable after a Draw call so that controllers can convert pixel positions to data coordinates and perform per-panel hit-testing.

func (PanelInfo) ContainsPixel added in v0.0.10

func (p PanelInfo) ContainsPixel(px, py float64) bool

ContainsPixel reports whether the pixel position (px, py) falls within this panel's data-area bounds.

func (PanelInfo) PixelToData added in v0.0.10

func (p PanelInfo) PixelToData(px, py float64) (dx, dy float64)

PixelToData converts a pixel position (px, py) within this panel's Bounds to data-space coordinates. Returns (NaN, NaN) if the pixel is outside the panel bounds.

type Session added in v0.0.10

type Session struct {
	// contains filtered or unexported fields
}

Session drives a Source onto a LiveSurface: build once, draw, then re-render on events. It owns the fast-path / slow-path policy.

The fast path (ActionRedraw) re-renders the current figure under a viewport affine transform — cheap and frequent. The slow path (ActionRebuild) calls Source.Build again when the data extent changes. By default the rebuild is synchronous; WithRebuildDelay makes it asynchronous and debounced — the last good figure keeps drawing while the next one is computed off the event loop.

func NewSession added in v0.0.10

func NewSession(src Source, surf LiveSurface, opts ...SessionOpt) *Session

NewSession creates a session that drives src onto surf. With no options it uses DataSpaceController for data-space pan/zoom.

func (*Session) Run added in v0.0.10

func (s *Session) Run(ctx context.Context) error

Run builds the initial figure, draws one frame, then processes events until the surface's event channel closes, a controller returns ActionClose, an EventClose arrives, or ctx is cancelled.

type SessionOpt added in v0.0.10

type SessionOpt func(*Session)

SessionOpt configures a Session.

func WithController added in v0.0.10

func WithController(c Controller) SessionOpt

WithController overrides the default pan/zoom controller.

func WithExportSurface added in v0.0.10

func WithExportSurface(fn func(ctx context.Context) (Surface, error)) SessionOpt

WithExportSurface sets the factory used to create a destination surface when a controller returns ActionExport.

func WithRebuildDelay added in v0.0.10

func WithRebuildDelay(d time.Duration) SessionOpt

WithRebuildDelay enables asynchronous, debounced rebuilds. When d > 0, an ActionRebuild no longer blocks the event loop: rapid triggers within d are coalesced into a single background [Source.Build], the last good figure stays on screen until it completes, and the result is swapped in when ready. When d <= 0 (the default) rebuilds run synchronously on the event loop.

Pending async rebuilds are flushed when the event channel closes, but are dropped on ActionClose/EventClose or context cancellation (the session is shutting down).

func WithRebuildError added in v0.0.10

func WithRebuildError(fn func(error)) SessionOpt

WithRebuildError sets a handler for errors from asynchronous rebuilds. Such errors are non-fatal: the last good figure is kept and the session continues. Without a handler they are dropped. (Synchronous rebuilds instead return the error from Session.Run.)

type Sizer added in v0.0.10

type Sizer interface {
	PreferredSize(width int) (w, h int)
}

Sizer is an optional Figure extension: given a width, it proposes a preferred size. Façades (Save/Encode/Image) use it to infer a height when the caller passes a non-positive one. *ggplot.Built implements Sizer by wrapping its existing auto-height rules.

type Source added in v0.0.10

type Source interface {
	Build(ctx context.Context) (Figure, error)
}

Source yields a fresh Figure. *ggplot.Plot is a Source. Live surfaces hold a Source so they can rebuild when an interaction crosses the trained data extent (scales retrain, stats recompute — the slow path).

type State added in v0.0.10

type State struct {
	// Bounds is the surface's current logical drawing size, refreshed before
	// each event is dispatched.
	Bounds image.Rectangle

	// Figure is the current figure being displayed (read-only for controllers
	// that need the trained extent, e.g. to decide redraw vs. rebuild).
	Figure Figure
}

State is the mutable interaction state shared between the Session and its Controller. The controller reads events and mutates the viewport; the session reads the viewport to render.

type Surface added in v0.0.10

type Surface interface {
	// Acquire returns the drawing Canvas for the next frame, sized to Bounds.
	// The returned Canvas is valid only until the next Commit.
	Acquire(ctx context.Context) (canvas.Canvas, error)

	// Commit finalizes the frame acquired by Acquire:
	//   file   -> encode + flush bytes to disk
	//   image  -> publish the in-memory image
	//   window -> submit GPU work + present (zero-copy)
	//   web    -> submit + swapchain present
	Commit(ctx context.Context) error

	// Bounds is the current logical drawing size (device scale handled
	// internally). Fixed for file/image; tracks the window/canvas for live.
	Bounds() image.Rectangle

	Close() error
}

Surface is a destination a Figure is drawn to — one interface for file, in-memory image, desktop GPU window, and browser canvas.

Static surfaces (file, image) accept exactly one Acquire/Commit cycle and return ErrSurfaceConsumed on a second Acquire. Live surfaces accept repeated cycles. The single- vs. multi-shot contract is documented per implementation; the type is shared.

func NewSurface added in v0.0.10

func NewSurface(ctx context.Context, name string, opts ...SurfaceOpt) (Surface, error)

NewSurface constructs a registered surface by name. It returns ErrUnknownSurface if the corresponding subpackage was not blank-imported.

type SurfaceFactory added in v0.0.10

type SurfaceFactory func(ctx context.Context, opt SurfaceOptions) (Surface, error)

SurfaceFactory constructs a Surface from resolved options. Platform subpackages register one via Register in their init().

type SurfaceOpt added in v0.0.10

type SurfaceOpt func(*SurfaceOptions)

SurfaceOpt configures SurfaceOptions functionally.

func WithCPU added in v0.0.10

func WithCPU(cpu bool) SurfaceOpt

WithCPU forces a pure-CPU rasterizer for raster output.

func WithFormat added in v0.0.10

func WithFormat(format string) SurfaceOpt

WithFormat overrides the encoder format ("png", "svg", "pdf").

func WithPath added in v0.0.10

func WithPath(path string) SurfaceOpt

WithPath sets the destination file path (file surface).

func WithScale added in v0.0.10

func WithScale(scale float64) SurfaceOpt

WithScale sets the device-pixel multiplier for HiDPI output.

func WithSize added in v0.0.10

func WithSize(w, h int) SurfaceOpt

WithSize sets the logical drawing size.

func WithWriter added in v0.0.10

func WithWriter(w io.Writer) SurfaceOpt

WithWriter sets an io.Writer destination for the file surface (used in place of a path).

type SurfaceOptions added in v0.0.10

type SurfaceOptions struct {
	// Width and Height are the logical drawing size in pixels.
	Width, Height int

	// Path is the destination file path (file surface). The encoder is
	// inferred from its extension unless Format is set.
	Path string

	// Writer is an alternative destination for the file surface: when set,
	// encoded bytes are written here instead of to Path.
	Writer io.Writer

	// Format overrides the encoder ("png", "svg", "pdf"). Empty means infer
	// from Path.
	Format string

	// Scale is the device-pixel multiplier for HiDPI output (1 = none).
	Scale float64

	// CPU forces a pure-CPU rasterizer for raster output (deterministic,
	// headless-safe), bypassing the GPU accelerator.
	CPU bool
}

SurfaceOptions is the resolved configuration passed to a SurfaceFactory. Fields not relevant to a given surface are ignored by its factory.

func BuildOptions added in v0.0.10

func BuildOptions(opts ...SurfaceOpt) SurfaceOptions

BuildOptions applies the given options and returns the resolved SurfaceOptions. Surface packages use this to build options from variadic SurfaceOpt slices.

type Zoomable added in v0.0.10

type Zoomable interface {
	// SetPanelViewport overrides the X and Y scale bounds for the panel at
	// panelIndex. Pass nil for either limit endpoint to keep the current
	// value. After calling this, the next Draw will render with the new
	// viewport (clipped data, updated ticks).
	SetPanelViewport(panelIndex int, xlim, ylim [2]*float64)

	// ResetViewport restores all panels to their original trained bounds.
	ResetViewport()
}

Zoomable is an optional Figure extension that supports fast viewport changes without rebuilding the figure from source. The controller mutates scale bounds directly on the built figure, then triggers ActionRedraw.

This is O(1) — no data iteration, no stat recomputation, no memory allocation. Only the scale endpoints change; ticks regenerate on the next Draw call.

Directories

Path Synopsis
Package file provides a output.Surface that encodes a single frame to disk (or any io.Writer) as PNG, SVG, or PDF.
Package file provides a output.Surface that encodes a single frame to disk (or any io.Writer) as PNG, SVG, or PDF.
Package image provides a output.Surface that renders a single frame into an in-memory image.Image.
Package image provides a output.Surface that renders a single frame into an in-memory image.Image.
Package web — serve.go provides a development HTTP server for serving WASM ggplot applications.
Package web — serve.go provides a development HTTP server for serving WASM ggplot applications.
Package window presents a ggplot figure in a native desktop GPU window via gogpu + ggcanvas, the zero-copy GPU rendering integration.
Package window presents a ggplot figure in a native desktop GPU window via gogpu + ggcanvas, the zero-copy GPU rendering integration.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL