10#ifndef PLOT_CANVAS_HPP
11#define PLOT_CANVAS_HPP
26#include "attributes.hpp"
31 using color_t = plot::attribute::color_t;
32 using font_t = plot::attribute::font_t;
33 using stroke_t = plot::attribute::stroke_t;
34 using opacity_t = plot::attribute::opacity_t;
35 using id_t = plot::attribute::id_t;
36 using position_t = plot::attribute::position_t;
37 using attribute_t = plot::attribute::element_t;
38 using align_t = plot::attribute::align_t;
41 canvas_t(std::ostream& os, std::size_t x, std::size_t y,
const std::array<float,4>& margin);
44 void group (std::variant<std::size_t, align_t> x, std::variant<std::size_t, align_t> y,
45 const attribute_t& attr, std::function<
void()>
const& call );
48 void group_scaled (
float x,
float y,
float scale, std::function<
void()>
const& call );
49 void rect(
float x,
float y,
float width,
float height,
float rx,
float ry,
const attribute_t& attr );
50 void circle(
float cx,
float cy,
float radius,
const attribute_t& attr );
51 void line(
float x1,
float y1,
float x2,
float y2,
const attribute_t& attr );
52 void poly_line(
const std::vector<float>& x,
const std::vector<float>& y,
const attribute_t& attr );
53 void polygon(
const std::vector<float>& x,
const std::vector<float>& y,
const attribute_t& attr );
54 static std::pair<std::size_t,std::size_t>
55 bounding_box(
const std::vector<std::string>& x_axis,
double angle_x,
56 const std::vector<std::string>& y_axis,
double angle_y, std::size_t font_size );
57 void text(
const std::string& txt,
58 std::variant<std::size_t, align_t> x, std::variant<std::size_t, align_t> y,
const attribute_t& attr );
60 std::optional<float> grid_x, grid_y;
66 template <
class... Args>
67 static std::string fmt(std::string_view f, Args... args){
68 return std::vformat(f, std::make_format_args(args...));
70 void align( std::optional<align_t> alignment );
71 float align_x( std::variant<std::size_t, align_t> x );
72 float align_y( std::variant<std::size_t, align_t> y,
const std::optional<font_t>& font);
74 std::size_t width, height;
76 std::array<float,4> margin;
77 static constexpr double rad = 0.017453292519943295;
80 const std::string gr_begin_ =
"<g {}>\n";
81 const std::string gr_end_ =
"</g>\n";
82 const std::string rotate_ =
" rotate({} {} {})";
83 const std::string translate_ =
"translate({} {})";
84 const std::string translate_scale_ =
"translate({:.3f} {:.3f}) scale({:.5f})";
85 const std::string transform_ =
" transform=\"{}\"";
87 const std::string svg_start_ =
"<svg viewBox=\"{} {} {} {}\" xmlns=\"http://www.w3.org/2000/svg\">\n";
88 const std::string svg_end_ =
"</svg>\n";
89 const std::string href_begin_ =
"<a href=\"{}\">";
90 const std::string href_end_ =
"</a>\n";
92 const std::string text_anchor_ =
" text-anchor=\"{}\"";
93 const std::string font_ =
" font-size=\"{}\" font-family=\"{}\" font-weight=\"{}\"";
94 const std::string fill_ =
" fill=\"#{:06X}\"";
95 const std::string stroke_attr_ =
" stroke=\"#{:06X}\" stroke-width=\"{:.2f}\"";
96 const std::array<std::string, 5> align_horizontal = {
"start",
"middle",
"end",
"",
""};
98 const std::string rect_ =
"<rect x=\"{:.2f}\" y=\"{:.2f}\" width=\"{:.2f}\" height=\"{:.2f}\" rx=\"{:.2f}\" ry=\"{:.2f}\" {}>{}</rect>\n";
99 const std::string line_ =
"<line x1=\"{:.2f}\" y1=\"{:.2f}\" x2=\"{:.2f}\" y2=\"{:.2f}\"{}/>\n";
100 const std::string polyline_ =
"<polyline points=\"{}\" fill=\"none\"{}/>\n";
101 const std::string polygon_ =
"<polygon points=\"{}\"{}>{}</polygon>\n";
102 const std::string circle_ =
"<circle cx=\"{:.2f}\" cy=\"{:.2f}\" r=\"{:.2f}\" {}>{}</circle>\n";
103 const std::string text_ =
"<text x=\"{:.2f}\" y=\"{:.2f}\"{}>{}</text>\n";
104 const std::string title_ =
"<title>{}</title>";
108namespace plot::impl {
109 template <
class Derived >
111 void ostream( impl::canvas_t& )
const {
114 friend impl::canvas_t& operator<<(impl::canvas_t& cs,
const io_t<Derived>& attr){
115 const Derived& derived =
static_cast<const Derived&
>(attr);
116 derived.ostream( cs );
122inline plot::impl::canvas_t::canvas_t( std::ostream& os, std::size_t width, std::size_t height,
const std::array<float,4>& margin )
123 : width(width), height(height), margin(margin), os(os) {
125 os << fmt( svg_start_, 0,0, width,height);
128inline plot::impl::canvas_t::~canvas_t(){
133inline void plot::impl::canvas_t::group (std::variant<std::size_t, align_t> x, std::variant<std::size_t, align_t> y,
134 const attribute_t& attr, std::function<
void()>
const& call ){
135 float _x = align_x( x );
float _y = align_y( y, attr.font );
137 std::string _attr = fmt(transform_, fmt(translate_, _x, _y));
138 if( attr.font ) _attr += fmt(font_, attr.font->size, attr.font->family, attr.font->weight);
139 if( attr.color ) _attr += fmt(fill_,
static_cast<unsigned>(attr.color.value()) );
141 os << fmt(gr_begin_, _attr);
146inline void plot::impl::canvas_t::group_scaled (
float x,
float y,
float scale,
147 std::function<
void()>
const& call ){
148 std::string _attr = fmt(transform_, fmt(translate_scale_, x, y, scale));
149 os << fmt(gr_begin_, _attr);
154inline std::pair<std::size_t,std::size_t>
155plot::impl::canvas_t::bounding_box(
const std::vector<std::string>&,
double,
156 const std::vector<std::string>&,
double, std::size_t ){
158 return std::make_pair(std::size_t{0}, std::size_t{0});
161inline void plot::impl::canvas_t::rect(
float x,
float y,
float width,
float height,
float rx,
float ry,
162 const attribute_t& attr){
163 std::string _attr, _lbl;
164 if( attr.color ) _attr += fmt(fill_,
static_cast<unsigned>(attr.color.value()) );
165 if( attr.label ) _lbl = fmt(title_, util::html_escape(attr.label.value()));
167 if( attr.href ) os << fmt(href_begin_, attr.href.value());
168 os << fmt(rect_, x, y, width, height, rx, ry, _attr, _lbl );
169 if( attr.href ) os << fmt(href_end_);
172inline void plot::impl::canvas_t::circle(
float cx,
float cy,
float radius,
const attribute_t& attr){
173 std::string _attr, _lbl;
174 if( attr.color ) _attr += fmt(fill_,
static_cast<unsigned>(attr.color.value()) );
175 if( attr.stroke ) _attr += fmt(stroke_attr_, attr.color ?
static_cast<unsigned>(attr.color.value()) : 0u, attr.stroke->width);
176 if( attr.label ) _lbl = fmt(title_, util::html_escape(attr.label.value()));
178 if( attr.href ) os << fmt(href_begin_, attr.href.value());
179 os << fmt(circle_, cx, cy, radius, _attr, _lbl );
180 if( attr.href ) os << fmt(href_end_);
183inline void plot::impl::canvas_t::line(
float x1,
float y1,
float x2,
float y2,
const attribute_t& attr){
184 const unsigned color = attr.color ?
static_cast<unsigned>(attr.color.value()) : 0u;
185 const float w = attr.stroke ? attr.stroke->width : 1.0f;
186 os << fmt(line_, x1, y1, x2, y2, fmt(stroke_attr_, color, w));
189inline void plot::impl::canvas_t::poly_line(
const std::vector<float>& x,
const std::vector<float>& y,
190 const attribute_t& attr){
191 const unsigned color = attr.color ?
static_cast<unsigned>(attr.color.value()) : 0u;
192 const float w = attr.stroke ? attr.stroke->width : 1.0f;
194 const std::size_t n = x.size() < y.size() ? x.size() : y.size();
195 for(std::size_t i=0; i<n; i++){
197 points += std::format(
"{:.2f},{:.2f}", x[i], y[i]);
199 os << fmt(polyline_, points, fmt(stroke_attr_, color, w));
202inline void plot::impl::canvas_t::polygon(
const std::vector<float>& x,
const std::vector<float>& y,
203 const attribute_t& attr){
205 const std::size_t n = x.size() < y.size() ? x.size() : y.size();
206 for(std::size_t i=0; i<n; i++){
208 points += std::format(
"{:.2f},{:.2f}", x[i], y[i]);
211 std::string _attr, _lbl;
212 if( attr.color ) _attr += fmt(fill_,
static_cast<unsigned>(attr.color.value()) );
213 if( attr.stroke ) _attr += fmt(stroke_attr_, attr.color ?
static_cast<unsigned>(attr.color.value()) : 0u, attr.stroke->width);
214 if( attr.label ) _lbl = fmt(title_, util::html_escape(attr.label.value()));
216 if( attr.href ) os << fmt(href_begin_, attr.href.value());
217 os << fmt(polygon_, points, _attr, _lbl );
218 if( attr.href ) os << fmt(href_end_);
221inline void plot::impl::canvas_t::text(
const std::string& txt,
222 std::variant<std::size_t, align_t> x, std::variant<std::size_t, align_t> y,
const attribute_t& attr ){
223 float _x = align_x( x );
float _y = align_y( y, attr.font );
224 int i = std::holds_alternative<std::size_t>( x ) ? 0 :
static_cast<int>(std::get<align_t>(x));
227 if( attr.font ) _attr += fmt(font_, attr.font->size, attr.font->family, attr.font->weight);
228 if( attr.color ) _attr += fmt(fill_,
static_cast<unsigned>(attr.color.value()) );
229 if( attr.rotate ) _attr += fmt(transform_, fmt(rotate_, attr.rotate->value, _x, _y));
230 _attr += fmt(text_anchor_, align_horizontal[i]);
232 os << fmt(text_, _x, _y, _attr, util::html_escape(txt) );
235inline void plot::impl::canvas_t::align( std::optional<align_t> x ) {
237 os << fmt(text_anchor_, align_horizontal[ static_cast<int>(x.value()) ]);
240inline float plot::impl::canvas_t::align_x( std::variant<std::size_t, align_t> x ) {
241 if( std::holds_alternative<std::size_t>( x ) )
242 return static_cast<float>(std::get<std::size_t>( x ));
243 switch( std::get<align_t>(x) ){
244 case align_t::left:
return std::get<0>(margin);
245 case align_t::right:
return width - std::get<2>(margin);
246 case align_t::center:
return width / 2 - std::get<0>(margin);
247 default:
throw std::runtime_error(
"fixme: #169");
249 return std::get<0>(margin);
252inline float plot::impl::canvas_t::align_y( std::variant<std::size_t, align_t> y,
const std::optional<font_t>& font ) {
253 if( std::holds_alternative<std::size_t>( y ) )
254 return static_cast<float>(std::get<std::size_t>( y ));
255 switch( std::get<align_t>(y) ){
256 case align_t::top:
return std::get<1>(margin) + (font ? font->size : 0);
257 case align_t::bottom:
return height - std::get<3>(margin);
258 case align_t::center:
return height / 2 - std::get<1>(margin);
260 throw std::runtime_error(
"fixme: 182");
263 return std::get<1>(margin);