Skip to content

feat: add z-axis / 3D chart support with echarts-gl#122

Merged
fahimfaisaal merged 10 commits into
mainfrom
feat/3d-charts-zaxis
Jun 6, 2026
Merged

feat: add z-axis / 3D chart support with echarts-gl#122
fahimfaisaal merged 10 commits into
mainfrom
feat/3d-charts-zaxis

Conversation

@fahimfaisaal

Copy link
Copy Markdown
Member

Summary

  • Add 4th categorical dimension (zAxis) to benchmark data; when x+y+z all present, bar/line charts render as 3D via echarts-gl; pie falls back to 3-up side-by-side layout
  • Self-extract gzip+base64 bundle at runtime via DecompressionStream to keep single-file HTML at ~512 KB (down from ~1.24 MB with echarts-gl inlined)
  • Add fullscreen toggle, auto-rotate toggle, axis-sum tooltips (2D+3D), z-axis swap support, and z-axis dependency enforcement in parsers

Changes

feat

  • zAxis field on BenchmarkData; DimensionZAxis token; --tag-axis z support; merge inject/empty for z
  • Group-pattern/group-regex accept z/zAxis tokens across all parsers (golang, criterion, divan, tinybench, vitest)
  • use3DChartOptions: bar3D/line3D with stacked series per z-value, beveled edges, stack-top labels, per-cell tooltips
  • Fullscreen button on every chart card via requestFullscreen; chart height expands to fill viewport
  • Auto-rotate toggle wired to grid3D.viewControl (3D charts only)
  • Axis-swap selector extended to z dimension (k-permutations of present roles); z only offered when x+y both present
  • Axis-sum tooltips: 2D y-totals + x-marginal sum; 3D Σz/Σx/Σy with O(1) precomputed cell aggregates, legend-aware

fix

  • Blank 3D saveAsImage — cap export pixelRatio at device dpr so zrender composites WebGL layer
  • 1D axis-swap drops bare-n grouping (useless without z)
  • Parser rejects group patterns/regex using zAxis without both x and y

perf

  • gzip+base64 inline JS, inflate at runtime; UI shell ~512 KB vs 1.24 MB
  • html/template delimiters switched to [[VIZB..VIZB]] to avoid collision with clay.gl GLSL {{}} loop-unroll markers

refactor

  • Extract useSortedSeriesData, getEffectiveScale, computeSeriesTotals, sortByAxisTotal, makeDataItem into shared utilities
  • useSyncedSetting writable-computed eliminates ref+watch+handler triples; useActiveChartShape dedupes is3DChart/isAxisChart
  • useDashboardInit composable; split Dashboard template into BenchmarkHeader, LoadingSkeleton, LoadError, AppFooter
  • Net: 3855 → 3536 LOC (−319), bundle delta < 1 kB

docs

  • New ui/3d-charts page with features, settings, axis-swap, roadmap; cross-linked from grouping/root/merge/ui pages

Test Plan

  • go test ./... passes
  • vizb with 3D benchmark data renders bar3D/line3D chart
  • Fullscreen + auto-rotate toggles work; image export non-blank
  • Axis swap cycles correctly through xyz permutations
  • Parser rejects zAxis without x+y, accepts valid patterns
  • Single-file HTML < 600 KB; bundle self-extracts in Chrome/Firefox/Safari

Add a 4th categorical dimension (zAxis) to benchmark data. When a data
point carries xAxis, yAxis AND zAxis, bar/line charts render as 3D via
echarts-gl, and pie renders as three side-by-side reduced pies.

Parsing & data:
- BenchmarkData gains a ZAxis field (shared/bench.go)
- DimensionZAxis added; merge inject/empty + --tag-axis z support
- group-pattern/group-regex accept z/zAxis tokens across all parsers
  (golang, criterion, divan, tinybench, vitest)

UI:
- new use3DChartOptions builds bar3D/line3D: xAxis->X floor,
  yAxis->Y depth, value->Z height, zAxis->color (one stacked series
  per z, legend on left)
- stacked bar3D with beveled edges; per-cell tooltip lists all z values;
  stack-top label shows the cell total (rounded to 2 decimals)
- sort applies to x, y and z axes
- pie 3-up layout for 3D data
- echarts-gl wired into ChartCard; ambient type shim added
- html/template delimiters switched to [[VIZB .. VIZB]] to avoid
  colliding with clay.gl GLSL {{ }} loop-unroll markers
- 3D sample dataset added for dev
echarts-gl bundles the clay.gl WebGL engine, inflating the single-file
HTML to ~1.24MB. Store the inlined module JS gzip+base64-encoded and
inflate at runtime via DecompressionStream + a deferred module bootstrap,
cutting the UI shell to ~512KB (below the prior 786KB 2D baseline).

Also extend the axis-swap selector to the z dimension: swap options are
now k-permutations of the present axis roles, enabling 3D<->2D toggling
(xyz vs nxy). z is only offered alongside both x and y, since a 3D floor
requires an x/y grid.
- Fullscreen button (Maximize2/Minimize2) on every chart card via
  requestFullscreen; chart height expands to fill viewport
- Extract module-level helpers in use3DChartOptions (round2,
  mapPoints, makeLabel, makeAxisCommon) to eliminate duplication
- Switch 3D shading to lambert + ambient 0.9 / main 0.3 to minimize
  face-darkening during rotation without losing depth cue
ScaleSelector hides the log/linear toggle when the active chart is 3D
(x+y+z present) — log scale isn't supported for 3D yet. vite.config
now logs emitted HTML size before/after bundle compression.
- ui: add Auto-rotate toggle (3D charts only) wired to grid3D.viewControl
- ui: fix blank 3D saveAsImage — cap export pixelRatio at device dpr so
  zrender composites the WebGL layer instead of a 2D-only redraw
- parser: reject group patterns/regex that use zAxis without both x and y
  (z is the depth dim of a 3D chart and needs an x/y floor); add tests
- shared tooltip theme (light/dark) so 2D and 3D match
- 2D tooltip: per-series y-totals + labeled x-marginal sum
- 3D tooltip: Σ z / Σ x / Σ y, precomputed cell aggregates for O(1) hover
- 3D sums reflect legend z-toggle via controlled selection
- new ui/3d-charts page; sidebar entry; cross-links from grouping/root/merge/ui pages
- features, roadmap, settings, swapping updated for z-axis, auto-rotate, fullscreen, axis-sum tooltips
- AxisSwapper: 1D data offers only x/y placement (drop useless name grouping)
Chart builders: extract useSortedSeriesData, getEffectiveScale,
computeSeriesTotals, sortByAxisTotal into shared/common.ts; add
makeDataItem to seriesConfig.ts; remove dead createBar/LineSeriesConfig;
decompose 209-line pie options into helpers + spec-driven series arrays;
lift 3D inline sort closure to shared util. bar/line/pie options each
~25-35% smaller.

Badges: new useSortedHistory composable + HistoryPopover presentational
component; Cpu/Os/TimestampBadge become thin wrappers.

Settings: useSyncedSetting writable-computed kills ref+watch+handler
triples in ChartSettings, ScaleSelector, AutoRotateToggle; dedupe
is3DChart/isAxisChart into useActiveChartShape composable.

Dashboard: useDashboardInit composable owns init watchers + title side
effect; split template into BenchmarkHeader, LoadingSkeleton, LoadError,
AppFooter presentational components. 202 → 84 lines.

Helpers: isValidIndex, SORT_ORDERS, SCALE_TYPES replace 5 inline bounds
checks and hardcoded validation arrays.

Net: 3855 → 3536 LOC (-319), bundle delta < 1 kB.
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@fahimfaisaal
fahimfaisaal merged commit 368c97b into main Jun 6, 2026
1 check passed
@fahimfaisaal
fahimfaisaal deleted the feat/3d-charts-zaxis branch June 6, 2026 13:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants