Skip to content

Commit 13c72ce

Browse files
authored
Merge pull request #191 from CU-ESIIL/codex/add-viewer-backend-documentation-page
Add viewer backend developer documentation
2 parents 65e870d + 17dd2e3 commit 13c72ce

File tree

2 files changed

+176
-0
lines changed

2 files changed

+176
-0
lines changed

docs/dev/viewer_backend.md

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
# Viewer backend (cube HTML renderer)
2+
3+
This page explains how `v.plot()` produces the interactive “HTML cube” viewer in Cubedynamics, and where to edit the code if you want to change rendering, layout, axes, or styling.
4+
5+
The important idea is: **`v.plot()` itself is a thin wrapper.** The actual cube rendering happens deeper in the plotting backend and ultimately returns a self-contained HTML string that is displayed in a notebook iframe.
6+
7+
---
8+
9+
## High-level flow
10+
11+
When you run:
12+
13+
```python
14+
from cubedynamics import pipe, verbs as v
15+
viewer = (pipe(cube) | v.plot()).unwrap()
16+
viewer
17+
```
18+
19+
the call chain is:
20+
21+
- `cubedynamics.verbs.plot.plot()`
22+
- validates the input (xarray.DataArray or VirtualCube)
23+
- infers the (time, y, x) dimension names (unless overridden)
24+
- builds a CubePlot object and adds geometry layers (e.g. geom_cube)
25+
- returns the CubePlot viewer object (the pipe contract keeps the cube flowing)
26+
- `cubedynamics.plotting.cube_plot.CubePlot`
27+
- stores plot options, theme, caption metadata, and layer definitions
28+
- when displayed in a notebook, Jupyter calls `CubePlot._repr_html_()`
29+
- `CubePlot._repr_html_()``CubePlot.to_html()`
30+
- computes the “stat layer” output (if any stats/transforms are applied)
31+
- computes fill scale limits / breaks / labels
32+
- builds axis metadata (`axis_meta`) for labels/ticks
33+
- calls the low-level viewer function to actually build the HTML
34+
- `cubedynamics.plotting.cube_viewer.cube_from_dataarray()`
35+
- constructs the HTML/CSS/JS viewer string
36+
- samples/decimates time frames for responsiveness (`thin_time_factor`)
37+
- rasterizes faces into images (often base64-encoded PNGs inside the HTML)
38+
- returns either:
39+
- a self-contained HTML string (`return_html=True`), or
40+
- writes to `out_html` and returns a viewer handle, depending on usage
41+
- `show_cube_viewer(...)`
42+
- writes HTML to a temp file and returns an iframe wrapper for notebook display.
43+
44+
In short:
45+
46+
`v.plot` (verb) → `CubePlot` (grammar container) → `cube_from_dataarray` (HTML builder) → iframe
47+
48+
## The key files (start here)
49+
50+
The viewer backend is split across two main modules:
51+
52+
- `cubedynamics/verbs/plot.py`
53+
- Entry point for `v.plot()` (pipe-friendly verb wrapper).
54+
- `cubedynamics/plotting/cube_plot.py`
55+
- The CubePlot class: stores plot configuration, layers, theme, and turns a cube into HTML through `to_html()`.
56+
- `cubedynamics/plotting/cube_viewer.py`
57+
- The actual HTML/CSS/JS viewer builder. If you want to change DOM structure, CSS classes, cube transforms, face layout, or add new overlay elements, this is typically the file you edit.
58+
59+
Supporting modules you may encounter:
60+
61+
- `cubedynamics/plotting/show.py` (or similarly named)
62+
- Helpers that write HTML to disk and display it in an iframe in notebooks.
63+
64+
## Expected input shape and dimension conventions
65+
66+
The cube viewer is designed for a 3D data array with dimensions:
67+
68+
- time: the temporal axis
69+
- y: the vertical spatial axis (rows)
70+
- x: the horizontal spatial axis (columns)
71+
72+
Commonly this is an xarray.DataArray with dims like `("time", "y", "x")`.
73+
74+
If your cube uses different dimension names, `v.plot()` attempts to infer them. You can also override the time dimension via the `time_dim=` argument to `v.plot()` (or the underlying CubePlot).
75+
76+
## Coordinate orientation (how the cube is interpreted)
77+
78+
The cube viewer uses a “front face” and “depth axis” convention:
79+
80+
- The front face represents the spatial slice at one end of time.
81+
- The depth axis represents progression through time (older → newer, or vice versa depending on the chosen convention).
82+
- The CoordCube / camera settings determine the initial “iso” viewing angle and zoom.
83+
84+
When working on axes or annotations, treat the viewer as a 3D scene with a consistent cube-local coordinate system. Overlay elements should be attached to the cube DOM so they rotate/scale with the cube.
85+
86+
## Output artifacts and file naming
87+
88+
CubePlot has an `out_html` path (default is typically `cube_da.html`). When faceting is enabled, `CubePlot.to_html()` may write multiple panel files with a suffix like:
89+
90+
- `cube_da_facet0.html`
91+
- `cube_da_facet1.html`
92+
- ...
93+
94+
In notebook display mode, the viewer is typically written to a temp location and shown via an iframe.
95+
96+
## Where the HTML comes from
97+
98+
The low-level `cube_from_dataarray(...)` function returns a complete HTML document (or fragment) containing:
99+
100+
- `<style>` blocks for the cube viewer CSS
101+
- DOM elements for the cube wrapper and faces
102+
- JavaScript for rotation/drag/interaction
103+
- embedded imagery for the faces (often as base64 PNGs)
104+
105+
This design means the viewer output can be saved and shared as a standalone HTML artifact.
106+
107+
## Prototyping without editing the repo (patch workflow)
108+
109+
When iterating quickly, it can be useful to prototype by monkeypatching the viewer function in a notebook session rather than editing the repository.
110+
111+
A common pattern is:
112+
113+
- Import the viewer function(s)
114+
- Capture the original function
115+
- Replace it with a wrapper that:
116+
- calls the original
117+
- modifies the returned HTML string (e.g., inject CSS/DOM overlays)
118+
- returns the modified HTML
119+
120+
Important notes:
121+
122+
- Patch both references if needed:
123+
- `cubedynamics.plotting.cube_viewer.cube_from_dataarray`
124+
- and any module that imported it by value (e.g., cube_plot may hold a reference)
125+
- Reload modules first (`importlib.reload`) to avoid stacking patches or causing recursion.
126+
- Keep patches “HTML-injection only” when prototyping layout and overlays.
127+
128+
Example skeleton (for documentation only — adapt in your notebook):
129+
130+
```python
131+
import importlib
132+
import cubedynamics.plotting.cube_viewer as cube_viewer_mod
133+
import cubedynamics.plotting.cube_plot as cube_plot_mod
134+
135+
cube_viewer_mod = importlib.reload(cube_viewer_mod)
136+
cube_plot_mod = importlib.reload(cube_plot_mod)
137+
138+
_ORIG = cube_viewer_mod.cube_from_dataarray
139+
140+
def patched(*args, **kwargs):
141+
out = _ORIG(*args, **kwargs)
142+
if not isinstance(out, str):
143+
return out
144+
# TODO: inject CSS/HTML into out
145+
return out
146+
147+
cube_viewer_mod.cube_from_dataarray = patched
148+
cube_plot_mod.cube_from_dataarray = patched
149+
```
150+
151+
This approach lets you experiment quickly with:
152+
153+
- axis overlays
154+
- new labels/ticks
155+
- CSS themes
156+
- DOM structure changes
157+
158+
…without touching the installed package or the repository.
159+
160+
## Where to edit what
161+
162+
Use this mental model:
163+
164+
- Change verb behavior / API surface:
165+
- `cubedynamics/verbs/plot.py`
166+
- Change grammar concepts (layers, scales, themes, captions, faceting):
167+
- `cubedynamics/plotting/cube_plot.py`
168+
- Change rendered HTML/CSS/JS, cube transforms, face layout, overlays:
169+
- `cubedynamics/plotting/cube_viewer.py`
170+
171+
## Debugging tips for viewer development
172+
173+
- Add temporary debug labels to the HTML (e.g., watermark text like “RIG V3”) to confirm you are seeing the patched output.
174+
- Print short snippets around key DOM nodes (`cd-cube`, `cube-wrapper`, `cube-rotation`) to verify injection points.
175+
- Keep patch scripts reload-safe to avoid accidental recursion.

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ nav:
5454
- CI and Testing: dev/ci_testing.md
5555
- Cube viewer invariants: dev/cube_viewer_invariants.md
5656
- Figure backend (v.plot): dev/figure_backend.md
57+
- Viewer backend: dev/viewer_backend.md
5758
- Legacy reference: dev/legacy_reference.md
5859
- Public API & scope: project/public_api.md
5960
- Project Scope: project/scope.md

0 commit comments

Comments
 (0)