plot
Header-only C++ SVG charts — heatmap, line, scatter — GR-style API + Solarized themes
Loading...
Searching...
No Matches
text.hpp
1/* Copyright (c) 2026 Steven Varga, Toronto, ON, Canada
2 * MIT License — see LICENSE
3 *
4 * Text-bearing SVG elements: free text, title, footnote, legend, style.
5 * Ported from plot:: into plot::. Dependency-free (standard library only).
6 */
7#ifndef PLOT_TEXT_HPP
8#define PLOT_TEXT_HPP
9
10#include <string>
11#include <vector>
12#include <iterator>
13#include <algorithm>
14#include <cstddef>
15
16#include "attributes.hpp"
17#include "canvas.hpp"
18#include "meta.hpp"
19
20namespace plot {
21 struct text : public attribute::element_t, public impl::io_t<text> {
22 using value_type = tag::text_t;
23 text( std::initializer_list<std::string> il) {
24 std::copy(il.begin(), il.end(), std::back_inserter(values));
25 }
26 template <class... Ts>
27 text( const std::string& value , Ts... args )
28 : attribute::element_t(args...) {
29 values.push_back(value);
30 }
31
32 void ostream( impl::canvas_t& ) const {
33 }
34
35 std::vector<std::string> values;
36 };
37
38 template <class... Ts>
39 struct title : public attribute::element_t,
40 public impl::io_t<title<Ts...>> {
41 using value_type = tag::title_t;
42 using attribute::element_t::position;
43 using attribute::element_t::color;
44 using font_t = attribute::font_t;
45
46 title(const std::string& txt, Ts... args)
47 : attribute::element_t(args...), txt(txt) {
48 this->font = arg::get<font_t>(args...);
49 // without an explicit font the <text> would inherit the SVG default
50 // (~16px) — enormous on a compactly auto-sized plot. Default to a
51 // modest bold title proportional to the small tick fonts.
52 if( !this->font ) this->font = font_t{"Arial, Helvetica, sans-serif", "bold", 8u};
53 }
54
55 void ostream( impl::canvas_t& os ) const {
56 // `position` is optional; a title created without an explicit position
57 // must NOT dereference it (that reads an uninitialized variant — UB).
58 const auto pos = position.value_or(attribute::position_t{});
59 os.text(txt, pos.x, pos.y, *this );
60 }
61 const std::string txt;
62 };
63
64 template <class... Ts>
65 struct footnote : public attribute::element_t, public impl::io_t<footnote<Ts...>> {
66 using value_type = tag::footnote_t;
67 footnote(const std::string&, Ts...) {
68 }
69 void ostream( impl::canvas_t& ) const {
70 }
71 };
72 template <class... Ts>
73 struct legend : public attribute::element_t, public impl::io_t<legend<Ts...>> {
74 using value_type = tag::legend_t;
75 using attribute::element_t::position;
76 using attribute::element_t::color;
77 using font_t = attribute::font_t;
78 using attribute_t = plot::attribute::element_t;
79 using align_t = plot::attribute::align_t;
80 using color_t = plot::attribute::color_t;
81
82 legend(Ts... args) : attribute::element_t(args...) {
83 if( !font ){
84 this->font = font_t();
85 this->font->size *= 0.7;
86 }
87 this->values = arg::get<text>(args...)->values;
88 }
89
90 void ostream( impl::canvas_t& os ) const {
91 std::size_t font_width = static_cast<std::size_t>(.8 * font->size);
92 attribute_t text_attr, marker_attr, group_attr;
93 group_attr.font = this->font; group_attr.position = this->position;
94 color_t palette = color.value();
95 float width = os.grid_x ? os.grid_x.value() : static_cast<float>(font_width);
96 float height = os.grid_y ? os.grid_y.value() : static_cast<float>(font_width);
97 os.group(position->x, position->y, group_attr,
98 [&]() -> void {
99 for(std::size_t i=0, j=0; i<values.size(); j += static_cast<std::size_t>(6 * width), i++){
100 marker_attr.color = palette[i];
101 os.text(values[i], static_cast<std::size_t>(j + 1.25 * width) , std::size_t{0}, text_attr);
102 os.rect(static_cast<float>(j), - height, .9f*width, .9f*height, 1.5f, 1.5f, marker_attr);
103 }
104 });
105 }
106
107 std::vector<std::string> values;
108 };
109 struct style : text {
110 using value_type = tag::style_t;
111 style(const std::string& txt) : text( txt ) {
112 }
113 };
114}
115#endif