44#include "attributes.hpp"
52 struct rows {
using value_type = tag::rows_t; std::size_t value; };
53 struct cols {
using value_type = tag::cols_t; std::size_t value; };
59 template <
class view_t>
void render_view(std::ostream& os,
const view_t& v);
60 template <
class view_t>
void save_view(
const std::string& file,
const view_t& v);
65 template <
class... opt_t>
66 std::pair<std::size_t,std::size_t>
67 natural_size(std::size_t dw, std::size_t dh,
const opt_t&... opts){
68 using width_t =
typename arg::tpos<tag::width_t, opt_t...>;
69 using height_t =
typename arg::tpos<tag::height_t, opt_t...>;
70 auto tuple = std::forward_as_tuple(opts...);
71 std::size_t W = dw, H = dh;
72 if constexpr( width_t::present ) W = std::get<width_t::position>(tuple).value;
73 if constexpr( height_t::present ) H = std::get<height_t::position>(tuple).value;
80 template <
class... opt_t>
82 using value_type = tag::view_t;
83 std::vector<double> xs, ys;
84 std::tuple<opt_t...> opts;
86 std::pair<std::size_t,std::size_t> natural()
const {
87 return std::apply([](
const auto&... o){
88 return impl::natural_size(std::size_t{640}, std::size_t{400}, o...); }, opts);
90 void draw_into(canvas_t& cv,
float x,
float y,
float w,
float h)
const {
91 std::apply([&](
const auto&... o){
92 const theme_t& th = impl::resolve_theme(o...);
93 auto [title, xl, yl] = impl::texts(o...);
94 auto [xlg, xb] = impl::xlog_of(o...);
95 auto [ylg, yb] = impl::ylog_of(o...);
96 auto [xmn,xmx] = impl::minmax_of(xs);
97 auto [ymn,ymx] = impl::minmax_of(ys);
98 impl::scale_t sx = impl::make_scale(xmn,xmx,xlg,xb);
99 impl::scale_t sy = impl::make_scale(ymn,ymx,ylg,yb);
100 impl::render_panel_into(cv, x, y,
101 static_cast<std::size_t
>(w),
static_cast<std::size_t
>(h),
102 th, sx, sy, title, xl, yl, {},
103 [&](impl::canvas_t& c,
auto px,
auto py){
104 using attribute_t = plot::attribute::element_t;
105 std::vector<float> xpx, ypx;
106 const std::size_t n = std::min(xs.size(), ys.size());
107 for(std::size_t i=0;i<n;++i){
108 xpx.push_back(px(sx.xform(xs[i])));
109 ypx.push_back(py(sy.xform(ys[i])));
112 a.color = plot::attribute::color_t{ th.series.empty()? th.fg : th.series[0] };
113 a.stroke = plot::attribute::stroke_t{1.0f, 1.8f, {}, {}, {}};
114 c.poly_line(xpx, ypx, a);
121 template <
class... opt_t>
123 using value_type = tag::view_t;
124 std::vector<double> xs;
125 std::vector<impl::series_t> series;
126 std::tuple<opt_t...> opts;
128 std::pair<std::size_t,std::size_t> natural()
const {
129 return std::apply([](
const auto&... o){
130 return impl::natural_size(std::size_t{640}, std::size_t{400}, o...); }, opts);
132 void draw_into(canvas_t& cv,
float x,
float y,
float w,
float h)
const {
133 std::apply([&](
const auto&... o){
134 const theme_t& th = impl::resolve_theme(o...);
135 auto [title, xl, yl] = impl::texts(o...);
136 auto [xlg, xb] = impl::xlog_of(o...);
137 auto [ylg, yb] = impl::ylog_of(o...);
138 auto [xmn,xmx] = impl::minmax_of(xs);
139 double ymn = std::numeric_limits<double>::infinity();
140 double ymx = -std::numeric_limits<double>::infinity();
141 for(
const auto& s : series){
142 auto [a,b] = impl::minmax_of(s.data);
143 if(a<ymn) ymn=a;
if(b>ymx) ymx=b;
145 if( !(ymn<=ymx) ){ ymn=0; ymx=1; }
146 impl::scale_t sx = impl::make_scale(xmn,xmx,xlg,xb);
147 impl::scale_t sy = impl::make_scale(ymn,ymx,ylg,yb);
148 std::vector<std::string> labels;
149 for(
const auto& s : series) labels.push_back(s.label);
150 impl::render_panel_into(cv, x, y,
151 static_cast<std::size_t
>(w),
static_cast<std::size_t
>(h),
152 th, sx, sy, title, xl, yl, labels,
153 [&](impl::canvas_t& c,
auto px,
auto py){
154 using attribute_t = plot::attribute::element_t;
155 for(std::size_t k=0;k<series.size();++k){
156 const auto& s = series[k];
157 std::vector<float> xpx, ypx;
158 const std::size_t n = std::min(xs.size(), s.data.size());
159 for(std::size_t i=0;i<n;++i){
160 xpx.push_back(px(sx.xform(xs[i])));
161 ypx.push_back(py(sy.xform(s.data[i])));
163 std::uint32_t col = th.series.empty()? th.fg
164 : th.series[k % th.series.size()];
165 attribute_t a; a.color = plot::attribute::color_t{ col };
166 a.stroke = plot::attribute::stroke_t{1.0f, 1.8f, {}, {}, {}};
167 c.poly_line(xpx, ypx, a);
175 template <
class... opt_t>
176 struct scatter_view {
177 using value_type = tag::view_t;
178 std::vector<double> xs, ys;
179 std::tuple<opt_t...> opts;
181 std::pair<std::size_t,std::size_t> natural()
const {
182 return std::apply([](
const auto&... o){
183 return impl::natural_size(std::size_t{640}, std::size_t{400}, o...); }, opts);
185 void draw_into(canvas_t& cv,
float x,
float y,
float w,
float h)
const {
186 std::apply([&](
const auto&... o){
187 const theme_t& th = impl::resolve_theme(o...);
188 auto [title, xl, yl] = impl::texts(o...);
189 auto [xlg, xb] = impl::xlog_of(o...);
190 auto [ylg, yb] = impl::ylog_of(o...);
191 auto [xmn,xmx] = impl::minmax_of(xs);
192 auto [ymn,ymx] = impl::minmax_of(ys);
193 impl::scale_t sx = impl::make_scale(xmn,xmx,xlg,xb);
194 impl::scale_t sy = impl::make_scale(ymn,ymx,ylg,yb);
195 impl::render_panel_into(cv, x, y,
196 static_cast<std::size_t
>(w),
static_cast<std::size_t
>(h),
197 th, sx, sy, title, xl, yl, {},
198 [&](impl::canvas_t& c,
auto px,
auto py){
199 using attribute_t = plot::attribute::element_t;
200 const std::size_t n = std::min(xs.size(), ys.size());
201 std::uint32_t col = th.series.empty()? th.fg : th.series[0];
202 for(std::size_t i=0;i<n;++i){
203 attribute_t a; a.color = plot::attribute::color_t{ col };
204 c.circle(px(sx.xform(xs[i])), py(sy.xform(ys[i])), 3.0f, a);
215 template <
class T,
class... opt_t>
216 struct heatmap_view {
217 using value_type = tag::view_t;
219 std::tuple<opt_t...> opts;
221 std::pair<std::size_t,std::size_t> natural()
const {
223 std::ostringstream probe;
224 std::array<float,4> M{5,5,5,5};
225 impl::canvas_t scratch(probe, 1, 1, M);
226 return std::apply([&](
const auto&... o){
227 return impl::heatmap_render(scratch, data, o...); }, opts);
229 void draw_into(canvas_t& cv,
float x,
float y,
float w,
float h)
const {
230 auto [nw, nh] = natural();
231 float s = std::min( w /
float(nw ? nw : 1), h / float(nh ? nh : 1) );
232 if( s <= 0.0f ) s = 1.0f;
234 float ox = x + (w - float(nw) * s) * 0.5f;
235 float oy = y + (h - float(nh) * s) * 0.5f;
236 cv.group_scaled(ox, oy, s, [&]{
237 std::apply([&](
const auto&... o){
238 (void)impl::heatmap_render(cv, data, o...); }, opts);
244 template <
class view_t>
245 void render_view(std::ostream& os,
const view_t& v){
246 auto [W,H] = v.natural();
247 std::array<float,4> M{5,5,5,5};
248 canvas_t cv(os, W, H, M);
249 v.draw_into(cv, 0, 0,
float(W),
float(H));
251 template <
class view_t>
252 void save_view(
const std::string& file,
const view_t& v){
253 std::ofstream ofs(file, std::ios::out | std::ios::trunc);
265 void save(
const std::string& file,
const V& view){ impl::save_view(file, view); }
267 void render(std::ostream& os,
const V& view){ impl::render_view(os, view); }
270 template <
class X,
class Y,
class... opt_t>
271 impl::line_view<opt_t...> line(
const std::vector<X>& xs,
const std::vector<Y>& ys, opt_t... opts){
272 impl::line_view<opt_t...> v{ {}, {}, std::make_tuple(opts...) };
273 v.xs.assign(xs.begin(), xs.end());
274 v.ys.assign(ys.begin(), ys.end());
277 template <
class X,
class... opt_t>
278 impl::mline_view<opt_t...> line(
const std::vector<X>& xs,
279 const std::vector<impl::series_t>& series, opt_t... opts){
280 impl::mline_view<opt_t...> v{ {}, series, std::make_tuple(opts...) };
281 v.xs.assign(xs.begin(), xs.end());
284 template <
class X,
class... opt_t>
285 impl::mline_view<opt_t...> line(
const std::vector<X>& xs,
286 std::initializer_list<impl::series_t> series, opt_t... opts){
287 return line(xs, std::vector<impl::series_t>(series), opts...);
289 template <
class X,
class Y,
class... opt_t>
290 impl::scatter_view<opt_t...> scatter(
const std::vector<X>& xs,
const std::vector<Y>& ys, opt_t... opts){
291 impl::scatter_view<opt_t...> v{ {}, {}, std::make_tuple(opts...) };
292 v.xs.assign(xs.begin(), xs.end());
293 v.ys.assign(ys.begin(), ys.end());
298 template <
class T,
class... opt_t,
299 class = std::enable_if_t<
300 !std::is_convertible_v<const T&, const std::string&> &&
301 !std::is_base_of_v<std::ostream, std::decay_t<T>>>>
302 impl::heatmap_view<T,opt_t...> heatmap(
const T& data, opt_t... opts){
303 return impl::heatmap_view<T,opt_t...>{ data, std::make_tuple(opts...) };
307namespace plot::impl {
311 template <
class... arg_t>
312 void grid_render(std::ostream& os, arg_t... args){
313 auto tuple = std::forward_as_tuple(args...);
316 constexpr std::size_t N = ( 0 + ... +
317 (impl::is_value_type<arg_t, tag::view_t>::value ? 1u : 0u) );
318 static_assert( N > 0,
"plot::grid needs at least one view argument" );
321 using rows_t =
typename arg::tpos<tag::rows_t, arg_t...>;
322 using cols_t =
typename arg::tpos<tag::cols_t, arg_t...>;
323 using width_t =
typename arg::tpos<tag::width_t, arg_t...>;
324 using height_t =
typename arg::tpos<tag::height_t, arg_t...>;
326 std::size_t C = 0, R = 0;
327 if constexpr( cols_t::present ) C = std::get<cols_t::position>(tuple).value;
328 if constexpr( rows_t::present ) R = std::get<rows_t::position>(tuple).value;
330 if( C == 0 && R == 0 ){
331 C =
static_cast<std::size_t
>(std::ceil(std::sqrt(
double(N))));
342 std::size_t W = 1200, H = 800;
343 if constexpr( width_t::present ) W = std::get<width_t::position>(tuple).value;
344 if constexpr( height_t::present ) H = std::get<height_t::position>(tuple).value;
346 const theme_t& fig_th = impl::resolve_theme(args...);
348 const float cellW = float(W) / float(C);
349 const float cellH = float(H) / float(R);
351 std::array<float,4> M{0,0,0,0};
352 canvas_t canvas(os, W, H, M);
354 { plot::attribute::element_t bg; bg.color = plot::attribute::color_t{ fig_th.bg };
355 canvas.rect(0, 0,
float(W),
float(H), 0, 0, bg); }
360 impl::static_for<std::tuple<arg_t...>>(
362 using A = std::tuple_element_t<idx.value, std::tuple<arg_t...>>;
363 if constexpr( impl::is_value_type<A, tag::view_t>::value ){
364 const std::size_t col = i % C;
365 const std::size_t row = i / C;
366 const float cx = float(col) * cellW;
367 const float cy = float(row) * cellH;
368 std::get<idx.value>(tuple).draw_into(canvas, cx, cy, cellW, cellH);
377 template <
class... arg_t>
378 void grid(std::ostream& os, arg_t... args){
379 impl::grid_render(os, args...);
381 template <
class... arg_t>
382 void grid(
const std::string& filename, arg_t... args){
383 std::ofstream ofs(filename, std::ios::out | std::ios::trunc);
384 impl::grid_render(ofs, args...);