From 9fefced73b216cfee435b9db461d03748f9b2937 Mon Sep 17 00:00:00 2001 From: steven varga Date: Thu, 18 Jun 2026 12:22:36 +0000 Subject: [PATCH] [#19]:svarga:feature, add plot::donut driver with adjustable plot::hole MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit plot::donut(labels, values, opts...) is now its own driver — a pie with a default 0.55 inner-radius hole. Both pie and donut accept plot::hole{r} to set/override the hole fraction (tag renamed donut_t -> hole_t). pie_view gains a default_inner member; impl::hole_of(fallback, ...) returns the fallback when no plot::hole{} is given. Updated example, tests (donut driver + hole override), and docs. --- doxy/docs/index.md | 2 +- examples/pie.cpp | 2 +- include/plot/pie.hpp | 33 ++++++++++++++++++++++----------- include/plot/tags.hpp | 2 +- test/plot_fill.cpp | 2 +- test/plot_new.cpp | 6 +++++- 6 files changed, 31 insertions(+), 16 deletions(-) diff --git a/doxy/docs/index.md b/doxy/docs/index.md index 81780d4..ed612bb 100644 --- a/doxy/docs/index.md +++ b/doxy/docs/index.md @@ -132,7 +132,7 @@ shown above. - `plot::ohlc(t, open, high, low, close, opts...)` — open/high/low/close candles *(coming in the gallery)*. - `plot::graph(nodes, edges, opts...)` — node/edge network layout *(coming in the gallery)*. - `plot::pie(labels, values, opts...)` — proportional wedges *(coming in the gallery)*. -- `plot::pie(labels, values, plot::donut{0.55}, opts...)` — donut (pie with an inner-radius hole). +- `plot::donut(labels, values, opts...)` — donut (pie with a default inner-radius hole; adjust with `plot::hole{r}`). - `plot::hexbin(xs, ys, opts...)` — hexagonal density binning *(coming in the gallery)*. ## Gallery diff --git a/examples/pie.cpp b/examples/pie.cpp index 1697b1d..cc02110 100644 --- a/examples/pie.cpp +++ b/examples/pie.cpp @@ -11,7 +11,7 @@ int main(){ plot::save("pie.svg", plot::pie(labels, share, plot::title("language share"), plot::width{520}, plot::height{520})); - plot::save("donut.svg", plot::pie(labels, share, plot::donut{0.55}, + plot::save("donut.svg", plot::donut(labels, share, plot::title("language share (donut)"), plot::width{520}, plot::height{520})); return 0; } diff --git a/include/plot/pie.hpp b/include/plot/pie.hpp index 91d5a94..21106f2 100644 --- a/include/plot/pie.hpp +++ b/include/plot/pie.hpp @@ -1,10 +1,11 @@ /* Copyright (c) 2026 Steven Varga, Toronto, ON, Canada * MIT License — see LICENSE * - * plot::pie(labels, values, opts...) — a deferred view (plot::view) drawing a - * pie chart, or a donut when plot::donut{r} (inner-radius fraction in (0,1)) is - * given. Each slice is a FILLED wedge: the arc is segmented into short - * chords, centre→arc→centre for a pie, or outer-arc→inner-arc for a donut ring. + * plot::pie(labels, values, opts...) and plot::donut(labels, values, opts...) — + * deferred views (plot::view) drawing a pie / donut. donut defaults to a 0.55 + * inner-radius hole; adjust either with plot::hole{r} (r in (0,1)). Each slice is + * a FILLED wedge: the arc is segmented into short chords, centre→arc→ + * centre for a pie, or outer-arc→inner-arc for a donut ring. * Slices are filled with the theme colour. Slice colours cycle theme.series; a * label is placed at the slice's mid-angle. * @@ -31,21 +32,22 @@ #include "view.hpp" namespace plot { - // donut inner-radius fraction in (0,1); 0 (or absent) ⇒ a full pie. - struct donut { using value_type = tag::donut_t; double value; }; + // inner-radius hole fraction in (0,1); 0 (or absent) ⇒ a full pie. Set by + // plot::donut (default 0.55) and accepted by plot::pie too. + struct hole { using value_type = tag::hole_t; double value; }; } namespace plot::impl { template - double donut_of(const opt_t&... opts){ - using d_t = typename arg::tpos; + double hole_of(double fallback, const opt_t&... opts){ + using d_t = typename arg::tpos; if constexpr( d_t::present ){ auto tuple = std::forward_as_tuple(opts...); double r = std::get(tuple).value; if( r < 0 ) r = 0; else if( r > 0.95 ) r = 0.95; return r; } - return 0.0; + return fallback; } template @@ -54,6 +56,7 @@ namespace plot::impl { std::vector labels; std::vector values; std::tuple opts; + double default_inner = 0.0; // plot::pie ⇒ 0 (full); plot::donut ⇒ 0.55 std::pair natural() const { return std::apply([](const auto&... o){ @@ -63,7 +66,7 @@ namespace plot::impl { std::apply([&](const auto&... o){ const theme_t& th = impl::resolve_theme(o...); auto [title, xl, yl] = impl::texts(o...); - const double inner = impl::donut_of(o...); + const double inner = impl::hole_of(default_inner, o...); using attribute_t = plot::attribute::element_t; const std::size_t n = std::min(labels.size(), values.size()); @@ -143,7 +146,15 @@ namespace plot { impl::pie_view pie(std::vector labels, std::vector values, opt_t... opts){ return impl::pie_view{ std::move(labels), std::move(values), - std::make_tuple(opts...) }; + std::make_tuple(opts...) }; // default_inner = 0 → full pie + } + + // donut: a pie with a default inner-radius hole (0.55); adjust with plot::hole{}. + template + impl::pie_view donut(std::vector labels, + std::vector values, opt_t... opts){ + return impl::pie_view{ std::move(labels), std::move(values), + std::make_tuple(opts...), 0.55 }; } } #endif diff --git a/include/plot/tags.hpp b/include/plot/tags.hpp index cee7b5f..13d0316 100644 --- a/include/plot/tags.hpp +++ b/include/plot/tags.hpp @@ -48,7 +48,7 @@ namespace plot::tag { struct bins_t{}; // histogram bin count struct bandwidth_t{}; // KDE bandwidth struct levels_t{}; // contour iso-level count - struct donut_t{}; // pie inner-radius fraction (0 = full pie) + struct hole_t{}; // pie/donut inner-radius hole fraction (0 = full pie) struct hex_t{}; // hexagonal heatmap style marker struct wireframe_t{}; // outline-only bars/bins (no fill) — issue #13 } diff --git a/test/plot_fill.cpp b/test/plot_fill.cpp index 9291b81..74fae73 100644 --- a/test/plot_fill.cpp +++ b/test/plot_fill.cpp @@ -51,7 +51,7 @@ int main(){ { std::vector labels{"x","y","z","w"}; std::vector vals{30,20,40,10}; - auto out = render_of(plot::pie(labels, vals, plot::donut{0.5}, plot::title("donut"))); + auto out = render_of(plot::donut(labels, vals, plot::title("donut"))); if(!well_formed(out)) return 4; if(out.find("x<") == std::string::npos) return 19; // slice label - auto dn = render_of(plot::pie(labels, vals, plot::donut{0.5}, plot::title("donut"))); + auto dn = render_of(plot::donut(labels, vals, plot::title("donut"))); if(!well_formed(dn)) return 20; if(dn.find("