Skip to content

2D plots across a split view, one stretch mode per pane

Keywords: split view · panes · plotting · CoordinateSystem · fit_view2d · stretch · overlay · 2D

Four named scenes, each rendered by its own CoordinateSystem in a 2×2 SplitView, plus a fifth scene spanning both columns that uses the fixed screen-space overlay axes. Every 2×2 pane embeds its camera via fit_view2d(..., stretch=...) with a different mode, so the four framing behaviours are visible side by side:

  • "fit" — letterbox: preserve aspect; one axis fills, the other is centred with empty space.
  • "fill" — stretch both axes to fill the pane (non-uniform).
  • "fill_x" — x fills the pane, y keeps aspect (may overflow).
  • "fill_y" — y fills the pane, x keeps aspect (may overflow).

The bottom row is a single CoordinateSystem(display_mode="overlay") pane spanning both columns: its axes are a fixed screen-space frame with a grid underlay behind the data. It owns its own camera (stretch="fill"); the 2×2 CoordinateSystems use camera=False so their per-pane fit_view2d camera stays authoritative.

Run

uv run python py/examples/viz/plotting/multi_plot.py

Source

viz/plotting/multi_plot.py

Code

# SPDX-License-Identifier: Apache-2.0
# Copyright 2021 Christian Perwass

"""multi_plot.py — 2D plots across a split view, one stretch mode per pane.

Four named scenes, each rendered by its own ``CoordinateSystem`` in a 2×2
``SplitView``, plus a fifth scene spanning both columns that uses the fixed
screen-space overlay axes.  Every 2×2 pane embeds its camera via
``fit_view2d(..., stretch=...)`` with a different mode, so the four framing
behaviours are visible side by side:

- ``"fit"``    — letterbox: preserve aspect; one axis fills, the other is
  centred with empty space.
- ``"fill"``   — stretch both axes to fill the pane (non-uniform).
- ``"fill_x"`` — x fills the pane, y keeps aspect (may overflow).
- ``"fill_y"`` — y fills the pane, x keeps aspect (may overflow).

The bottom row is a single ``CoordinateSystem(display_mode="overlay")`` pane
spanning both columns: its axes are a fixed screen-space frame with a grid
underlay behind the data.  It owns its own camera (``stretch="fill"``); the
2×2 ``CoordinateSystem``s use ``camera=False`` so their per-pane
``fit_view2d`` camera stays authoritative.

Run with:  uv run python py/examples/viz/plotting/multi_plot.py

Keywords: split view, panes, plotting, CoordinateSystem, fit_view2d, stretch, overlay, 2D
"""

import math

from pytanga.viz import (
    CoordinateSystem,
    PointPathStyle,
    SceneView,
    Size,
    SplitView,
    Visualizer,
    fit_view2d,
)

viz = Visualizer(
    reuse_existing=False,
    title="Tanga — Multi-Plot Split",
    space_dim=2,
    add_default_axes=False,
    add_default_grid=False,
)

# ── Top-left: fit (letterbox) ───────────────────────────────
fit = viz.scene("fit", space_dim=2, add_axes=False, add_grid=False)
fit_cs = CoordinateSystem(
    fit, xlim=(0, 2 * math.pi), ylim=(-1.2, 1.2), labels=("x", "sin(x)"), camera=False
)
fit_xs = [0.05 * i for i in range(int(2 * math.pi / 0.05) + 1)]
fit_cs.plot(
    fit_xs,
    [math.sin(x) for x in fit_xs],
    color="#ffcc00",
    style=PointPathStyle(line_thickness=2),
)

# ── Top-right: fill (stretch both axes) ─────────────────────
fill = viz.scene("fill", space_dim=2, add_axes=False, add_grid=False)
fill_cs = CoordinateSystem(
    fill, xlim=(-5, 5), ylim=(0, 25), labels=("x", "x^2"), camera=False
)
fill_xs = [0.1 * i for i in range(-50, 51)]
fill_cs.plot(
    fill_xs,
    [x * x for x in fill_xs],
    color="#44ff44",
    style=PointPathStyle(line_thickness=2),
)

# ── Bottom-left: fill_x (x fills, y keeps aspect) ───────────
fillx = viz.scene("fill_x", space_dim=2, add_axes=False, add_grid=False)
fillx_cs = CoordinateSystem(
    fillx, xlim=(0, 5), ylim=(0, 25), labels=("x", "x^2"), camera=False
)
fillx_xs = [0.1 * i for i in range(51)]
fillx_cs.plot(
    fillx_xs,
    [x * x for x in fillx_xs],
    color="#ff44cc",
    style=PointPathStyle(line_thickness=2),
)

# ── Bottom-right: fill_y (y fills, x keeps aspect) ──────────
filly = viz.scene("fill_y", space_dim=2, add_axes=False, add_grid=False)
filly_cs = CoordinateSystem(
    filly,
    xlim=(0, 40),
    ylim=(1.0, 1_000_000.0),
    yscale="log",
    labels=("x", "x^2 + x"),
    camera=False,
)
filly_xs = list(range(40))
filly_cs.plot(
    filly_xs,
    [x * x + x + 0.1 for x in filly_xs],
    color="#4488ff",
    style=PointPathStyle(line_thickness=2),
)

# ── Bottom row: overlay axes (fixed frame + underlay grid) ───
overlay = viz.scene("overlay", space_dim=2, add_axes=False, add_grid=False)
overlay_cs = CoordinateSystem(
    overlay,
    display_mode="overlay",
    xlim=(0, 4 * math.pi),
    ylim=(-1.5, 1.5),
    labels=("x", "sin(x) * e^(-x/12)"),
)
overlay_xs = [0.05 * i for i in range(int(4 * math.pi / 0.05) + 1)]
overlay_cs.plot(
    overlay_xs,
    [math.sin(x) * math.exp(-x / 12) for x in overlay_xs],
    color="#44ffff",
    style=PointPathStyle(line_thickness=2),
)

# Two 2×2 rows, then a full-width overlay-axes row spanning both columns.
layout = SplitView(
    orientation="vertical",
    sizes=[Size.percent(33), Size.percent(34), Size.percent(33)],
    children=[
        SplitView(
            orientation="horizontal",
            sizes=[Size.percent(50), Size.percent(50)],
            children=[
                SceneView(
                    "fit",
                    camera=fit_view2d((0, 2 * math.pi), (-1.2, 1.2), stretch="fit"),
                ),
                SceneView("fill", camera=fit_view2d((-5, 5), (0, 25), stretch="fill")),
            ],
        ),
        SplitView(
            orientation="horizontal",
            sizes=[Size.percent(50), Size.percent(50)],
            children=[
                SceneView(
                    "fill_x", camera=fit_view2d((0, 5), (0, 25), stretch="fill_x")
                ),
                SceneView(
                    "fill_y",
                    camera=fit_view2d(
                        (0, 40), (1.0, 1_000_000.0), yscale="log", stretch="fill_y"
                    ),
                ),
            ],
        ),
        SceneView("overlay"),
    ],
)

viz.show(layout=layout)
print("2×2 stretch-mode plots plus a full-width overlay-axes plot. Press Ctrl+C to exit.")
viz.wait()