plot
Header-only C++ SVG charts — heatmap, line, scatter — GR-style API + Solarized themes
Loading...
Searching...
No Matches
attributes.hpp
1/* Copyright (c) 2026 Steven Varga, Toronto, ON, Canada
2 * MIT License — see LICENSE
3 *
4 * SVG element attributes (color, font, stroke, position, rotation, ...) and the
5 * order-independent `element_t` aggregate built from named arguments. Ported
6 * from plot:: into plot::. Dependency-free (standard library only).
7 */
8#ifndef PLOT_ATTRIBUTE_HPP
9#define PLOT_ATTRIBUTE_HPP
10
11#include <memory>
12#include <string>
13#include <vector>
14#include <array>
15#include <tuple>
16#include <variant>
17#include <optional>
18#include <algorithm>
19#include <cstddef>
20
21#include "tags.hpp"
22#include "meta.hpp"
23
24namespace plot::attribute {
25 enum class hue_t : unsigned {
26 aliceblue=0xF0F8FF, blueviolet=0x8a2be2,
27 };
28 enum class align_t : char { // how objects behave within a bounding box
29 left=0, center, right, top, bottom };
30
31 enum class weight_t : char { // font property
32 bold=0x0, normal=0x1, italic=0x2 };
33
34 struct degree_t { // in degrees, specify rotation/angle
35 using value_type = tag::degree_t;
36 float value;
37 };
38 struct ccw_t : public degree_t { // counter clock wise on degree
39 using value_type = tag::ccw_t;
40 };
41 struct cw_t : public degree_t { // clock wise in degree
42 using value_type = tag::cw_t;
43 };
44 struct radian_t : public degree_t { // from radian to degree
45 using value_type = tag::radian_t;
46 };
47 struct layout_t { // layout type
48 using value_type = tag::layout_t;
49 int value;
50 };
51 struct margin_t {
52 using value_type = tag::margin_t;
53 std::array<float,4> value;
54 };
55
56
57 struct color_t {
58 using value_type = tag::color_t;
59 color_t(unsigned value ) : color_t({value}){
60 }
61 color_t( const std::initializer_list<unsigned int> list )
62 : length(list.size()), ptr(new unsigned[length]) {
63 std::copy(list.begin(),list.end(), ptr.get());
64 }
65 operator unsigned() const {
66 return *ptr.get();
67 }
68 const unsigned& operator[]( std::ptrdiff_t i) const{
69 return ptr.get()[ i % length ];
70 }
71 std::size_t length;
72 // array shared_ptr: the ctor allocates with `new unsigned[length]`, so the
73 // deleter must be `delete[]`. A plain `shared_ptr<unsigned>` uses scalar
74 // `delete` → alloc/dealloc mismatch (UB) that crashed plot_grid on
75 // gcc-13/clang. `shared_ptr<unsigned[]>` selects the array deleter.
76 std::shared_ptr<unsigned[]> ptr;
77 };
78
79 struct font_t {
80 using value_type = tag::font_t;
81
82 font_t() : family("Arial, Helvetica, sans-serif"),
83 size(9), weight("normal") {
84 }
85 font_t(const std::string& name, const std::string& weight, unsigned size)
86 : family(name), size(size), weight(weight){
87 }
88 font_t(const std::string& name, unsigned size)
89 : family(name), size(size), weight("normal"){
90 }
91 font_t(unsigned size)
92 : family("Arial, Helvetica, sans-serif"), size(size), weight("normal"){
93 }
94
95 std::string family;
96 unsigned size;
97 std::string weight;
98 std::optional<std::string> style;
99 };
100
101 struct stroke_t {
102 using value_type = tag::stroke_t;
103 float opacity;
104 float width;
105
106 std::optional<unsigned > linecap;
107 std::optional<unsigned> linejoin;
108 std::optional<unsigned> miterlimit;
109 };
110
111 struct opacity_t {
112 using value_type = tag::opacity_t;
113 float value;
114 };
115 struct id_t {
116 using value_type = tag::id_t;
117 std::string value;
118 };
119
120 struct position_t {
121 using value_type = tag::position_t;
122
123 std::variant<std::size_t, align_t> x;
124 std::variant<std::size_t, align_t> y;
125 };
126 struct marker_t {
127 using value_type = tag::marker_t;
128 int value;
129 };
130
131
132 template <class... Ts>
133 std::optional<degree_t> get_rotate(Ts... args){
134 using degree_tt = typename arg::tpos<tag::degree_t, Ts...>;
135 using cw_tt = typename arg::tpos<tag::cw_t, Ts...>;
136 using ccw_tt = typename arg::tpos<tag::ccw_t, Ts...>;
137
138 auto tuple = std::forward_as_tuple(args...);
139 // rotation can be defined differently
140 if constexpr( degree_tt::present )
141 return std::get<degree_tt::position>( tuple );
142 else if constexpr( cw_tt::present )
143 return std::get<cw_tt::position>( tuple );
144 else if constexpr( ccw_tt::present ){
145 return attribute::degree_t{ 360.0f - std::get<ccw_tt::position>( tuple ).value};
146 }
147 return std::nullopt;
148 }
149
150 struct element_t {
151 template <class... Ts> element_t(Ts... args ){
152 auto tuple = std::forward_as_tuple(args...);
153
154 this->color = arg::get<color_t>(args...);
155 this->opacity = arg::get<opacity_t>(args...);
156 this->position = arg::get<position_t>(args...);
157 this->stroke = arg::get<stroke_t>(args...);
158 this->id = arg::get<id_t>(args...);
159 this->rotate = get_rotate(args...);
160
161 using align_tt = typename arg::tpos<attribute::align_t, Ts...>;
162 if constexpr( align_tt::present )
163 this->align = std::get<align_tt::value>( tuple );
164 this->layout = arg::get<layout_t>(args...);
165 this->font = arg::get<font_t>(args...);
166 }
167
168 std::optional<color_t> color;
169 std::optional<opacity_t> opacity;
170 std::optional<position_t> position;
171 std::optional<id_t> id;
172 std::optional<stroke_t> stroke;
173 std::optional<color_t> fill;
174 std::optional<degree_t> rotate;
175 std::optional<align_t> align;
176 std::optional<layout_t> layout;
177 std::optional<font_t> font;
178 std::optional<std::string> label;
179 std::optional<std::string> href;
180 };
181}
182
183namespace plot {
184 using position = attribute::position_t;
185 using font = attribute::font_t;
186 using hue = attribute::hue_t;
187 using align = attribute::align_t;
188 using weight = attribute::weight_t;
189 using margin = attribute::margin_t;
190 namespace layout {
191 using layout_t = attribute::layout_t;
192 static constexpr layout_t horizontal = attribute::layout_t{1};
193 static constexpr layout_t vertical = attribute::layout_t{0};
194 }
195 namespace marker {
196 using marker_t = attribute::marker_t;
197 static constexpr marker_t rect = attribute::marker_t{1};
198 static constexpr marker_t circle = attribute::marker_t{2};
199 static constexpr marker_t hex = attribute::marker_t{3};
200 static constexpr marker_t star = attribute::marker_t{4};
201 static constexpr marker_t point = attribute::marker_t{5};
202 static constexpr marker_t line = attribute::marker_t{6};
203 }
204 namespace rotate {
205 using ccw = attribute::ccw_t;
206 using cw = attribute::cw_t;
207 using degree = attribute::degree_t;
208 using radian = attribute::radian_t;
209 static constexpr degree horizontal = attribute::degree_t{0.0f};
210 static constexpr degree no = attribute::degree_t{0.0f};
211 static constexpr degree vertical = attribute::degree_t{270.0f};
212 }
213
214 namespace color {
215 using fg = attribute::color_t;
216 using bg = attribute::color_t;
217 }
218
219 struct width {
220 using value_type = tag::width_t;
221 std::size_t value;
222 };
223 struct height {
224 using value_type = tag::height_t;
225 std::size_t value;
226 };
227}
228#endif