Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion doxy/docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion examples/pie.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
33 changes: 22 additions & 11 deletions include/plot/pie.hpp
Original file line number Diff line number Diff line change
@@ -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 <polygon> 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 <polygon> 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.
*
Expand All @@ -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 <class... opt_t>
double donut_of(const opt_t&... opts){
using d_t = typename arg::tpos<tag::donut_t, opt_t...>;
double hole_of(double fallback, const opt_t&... opts){
using d_t = typename arg::tpos<tag::hole_t, opt_t...>;
if constexpr( d_t::present ){
auto tuple = std::forward_as_tuple(opts...);
double r = std::get<d_t::position>(tuple).value;
if( r < 0 ) r = 0; else if( r > 0.95 ) r = 0.95;
return r;
}
return 0.0;
return fallback;
}

template <class... opt_t>
Expand All @@ -54,6 +56,7 @@ namespace plot::impl {
std::vector<std::string> labels;
std::vector<double> values;
std::tuple<opt_t...> opts;
double default_inner = 0.0; // plot::pie ⇒ 0 (full); plot::donut ⇒ 0.55

std::pair<std::size_t,std::size_t> natural() const {
return std::apply([](const auto&... o){
Expand All @@ -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());
Expand Down Expand Up @@ -143,7 +146,15 @@ namespace plot {
impl::pie_view<opt_t...> pie(std::vector<std::string> labels,
std::vector<double> values, opt_t... opts){
return impl::pie_view<opt_t...>{ 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 <class... opt_t>
impl::pie_view<opt_t...> donut(std::vector<std::string> labels,
std::vector<double> values, opt_t... opts){
return impl::pie_view<opt_t...>{ std::move(labels), std::move(values),
std::make_tuple(opts...), 0.55 };
}
}
#endif
2 changes: 1 addition & 1 deletion include/plot/tags.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
2 changes: 1 addition & 1 deletion test/plot_fill.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ int main(){
{
std::vector<std::string> labels{"x","y","z","w"};
std::vector<double> 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("<polygon") == std::string::npos) return 5;
if(!has_filled_polygon(out)) return 6;
Expand Down
6 changes: 5 additions & 1 deletion test/plot_new.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,13 @@ int main(){
if(!well_formed(pie)) return 17;
if(pie.find("<polygon") == std::string::npos) return 18; // filled wedges
if(pie.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("<polygon") == std::string::npos) return 21; // filled ring wedges
// plot::hole{} overrides the donut's default inner radius
auto dh = render_of(plot::donut(labels, vals, plot::hole{0.3}, plot::title("donut")));
if(!well_formed(dh)) return 22;
if(dh.find("<polygon") == std::string::npos) return 23;
}

// ---- hexbin (hexagonal heatmap) -----------------------------------------
Expand Down
Loading