1 #include <iostream>
2 #include <sstream>
3 #include "bsvg_plot.h"
4 //:
5 // \file
6 // \author Ozge C. Ozcanli (Brown)
7 // \date April 21, 2009
8
9 #include <bxml/bxml_find.h>
10 #include <bxml/bsvg/bsvg_element.h>
11 #ifdef _MSC_VER
12 # include "vcl_msvc_warnings.h"
13 #endif
14
add_axes(float x_min,float x_max,float y_min,float y_max,float stroke_width)15 void bsvg_plot::add_axes(float x_min, float x_max, float y_min, float y_max, float stroke_width)
16 {
17 float height_y = y_max - y_min;
18 // find origin so that height of the plot is scaled wrt to the svg document width and height
19 h2_y = h_ - 4*margin_;
20 scale_factor_ = h2_y/height_y;
21
22 axes_orig_x_ = 2*margin_;
23 axes_orig_y_ = 2*margin_+h2_y;
24
25 auto* line_y = new bsvg_line(axes_orig_x_, axes_orig_y_, axes_orig_x_, axes_orig_y_-h2_y-margin_);
26 line_y->set_stroke_color("black");
27 line_y->set_stroke_width(stroke_width);
28
29 float height_x = x_max - x_min;
30 // find origin so that height of the plot is scaled wrt to the svg document width and height
31 h2_x = height_x*scale_factor_;
32
33 auto* line_x = new bsvg_line(axes_orig_x_, axes_orig_y_, axes_orig_x_+h2_x+margin_, axes_orig_y_);
34 line_x->set_stroke_color("black");
35 line_x->set_stroke_width(stroke_width);
36
37 this->add_element(line_y);
38 this->add_element(line_x);
39
40 // add text to denote x_min
41 std::stringstream ss; ss << x_min;
42 bsvg_text* t = new bsvg_text(ss.str());
43 t->set_location(axes_orig_x_, axes_orig_y_+margin_);
44 t->set_font_size(font_size_);
45 this->add_element(t);
46
47 // add text to denote y_min
48 std::stringstream ssy; ssy << y_min;
49 bsvg_text* ty = new bsvg_text(ssy.str());
50 ty->set_location(axes_orig_x_-margin_, axes_orig_y_);
51 ty->set_font_size(font_size_);
52 this->add_element(ty);
53
54 // add text to denote x_max
55 std::stringstream ssx; ssx << x_max;
56 bsvg_text* txm = new bsvg_text(ssx.str());
57 txm->set_location(axes_orig_x_+h2_x, axes_orig_y_+margin_);
58 txm->set_font_size(font_size_);
59 this->add_element(txm);
60
61 // add a short line to denote x_max
62 auto* line_xm = new bsvg_line(axes_orig_x_ + h2_x, axes_orig_y_, axes_orig_x_ + h2_x, axes_orig_y_+(margin_/4.0f));
63 line_xm->set_stroke_color("black");
64 line_xm->set_stroke_width(stroke_width);
65 this->add_element(line_xm);
66
67 // add text to denote y_max
68 std::stringstream ssym; ssym << y_max;
69 bsvg_text* tym = new bsvg_text(ssym.str());
70 tym->set_location(axes_orig_x_-margin_, axes_orig_y_-h2_y);
71 tym->set_font_size(font_size_);
72 this->add_element(tym);
73
74 // add a short line to denote y_max
75 auto* line_ym = new bsvg_line(axes_orig_x_-(margin_/4.0f), axes_orig_y_-h2_y, axes_orig_x_, axes_orig_y_-h2_y);
76 line_ym->set_stroke_color("black");
77 line_ym->set_stroke_width(stroke_width);
78 this->add_element(line_ym);
79
80 // put an arrow head at the end of x axis
81 auto* a1 = new bsvg_arrow_head(axes_orig_x_+h2_x+margin_, axes_orig_y_, 10.0f);
82 a1->set_stroke_width(stroke_width);
83 a1->set_stroke_color("black");
84 this->add_element(a1);
85
86 // put an arrow head at the end of y axis
87 auto* a2 = new bsvg_arrow_head(axes_orig_x_, axes_orig_y_-h2_y-margin_, 10.0f);
88 a2->set_stroke_width(stroke_width);
89 a2->set_rotation(-90);
90 a2->set_stroke_color("black");
91 this->add_element(a2);
92 }
93
add_axes(float x_min,float x_max,float y_min,float y_max,bool is_scale_x,float stroke_width)94 void bsvg_plot::add_axes(float x_min, float x_max, float y_min, float y_max, bool is_scale_x, float stroke_width)
95 {
96 if (is_scale_x)
97 this->add_axes(x_min, x_max, y_min, y_max, stroke_width);
98 else {
99 float height_y = y_max - y_min;
100 // find origin so that height of the plot is scaled wrt to the svg document width and height
101 h2_y = h_ - 4*margin_;
102 scale_factor_ = h2_y/height_y;
103
104 axes_orig_x_ = 2*margin_;
105 axes_orig_y_ = 2*margin_+h2_y;
106
107 auto* line_y = new bsvg_line(axes_orig_x_, axes_orig_y_, axes_orig_x_, axes_orig_y_-h2_y-margin_);
108 line_y->set_stroke_color("black");
109 line_y->set_stroke_width(stroke_width);
110
111 float height_x = x_max - x_min;
112 // find origin so that height of the plot is scaled wrt to the svg document width and height
113 h2_x = height_x;
114
115 auto* line_x = new bsvg_line(axes_orig_x_, axes_orig_y_, axes_orig_x_+h2_x+margin_, axes_orig_y_);
116 line_x->set_stroke_color("black");
117 line_x->set_stroke_width(stroke_width);
118
119 this->add_element(line_y);
120 this->add_element(line_x);
121
122 // add text to denote x_min
123 std::stringstream ss; ss << x_min;
124 bsvg_text* t = new bsvg_text(ss.str());
125 t->set_location(axes_orig_x_, axes_orig_y_+margin_);
126 t->set_font_size(font_size_);
127 this->add_element(t);
128
129 // add text to denote y_min
130 std::stringstream ssy; ssy << y_min;
131 bsvg_text* ty = new bsvg_text(ssy.str());
132 ty->set_location(axes_orig_x_-margin_, axes_orig_y_);
133 ty->set_font_size(font_size_);
134 this->add_element(ty);
135
136 // add text to denote x_max
137 std::stringstream ssx; ssx << x_max;
138 bsvg_text* txm = new bsvg_text(ssx.str());
139 txm->set_location(axes_orig_x_+h2_x, axes_orig_y_+margin_);
140 txm->set_font_size(font_size_);
141 this->add_element(txm);
142
143 // add a short line to denote x_max
144 auto* line_xm = new bsvg_line(axes_orig_x_ + h2_x, axes_orig_y_, axes_orig_x_ + h2_x, axes_orig_y_+(margin_/4.0f));
145 line_xm->set_stroke_color("black");
146 line_xm->set_stroke_width(stroke_width);
147 this->add_element(line_xm);
148
149 // add text to denote y_max
150 std::stringstream ssym; ssym << y_max;
151 bsvg_text* tym = new bsvg_text(ssym.str());
152 tym->set_location(axes_orig_x_-margin_, axes_orig_y_-h2_y);
153 tym->set_font_size(font_size_);
154 this->add_element(tym);
155
156 // add a short line to denote y_max
157 auto* line_ym = new bsvg_line(axes_orig_x_-(margin_/4.0f), axes_orig_y_-h2_y, axes_orig_x_, axes_orig_y_-h2_y);
158 line_ym->set_stroke_color("black");
159 line_ym->set_stroke_width(stroke_width);
160 this->add_element(line_ym);
161
162 // put an arrow head at the end of x axis
163 auto* a1 = new bsvg_arrow_head(axes_orig_x_+h2_x+margin_, axes_orig_y_, 10.0f);
164 a1->set_stroke_width(stroke_width);
165 a1->set_stroke_color("black");
166 this->add_element(a1);
167
168 // put an arrow head at the end of y axis
169 auto* a2 = new bsvg_arrow_head(axes_orig_x_, axes_orig_y_-h2_y-margin_, 10.0f);
170 a2->set_stroke_width(stroke_width);
171 a2->set_rotation(-90);
172 a2->set_stroke_color("black");
173 this->add_element(a2);
174 }
175
176 }
177
add_title(const std::string & t)178 void bsvg_plot::add_title(const std::string& t)
179 {
180 auto* title = new bsvg_text(t);
181 auto w = float(font_size_*t.size());
182 title->set_location((this->w_-margin_)/2.0f - w/2, margin_);
183 //title->set_location((h2_x+3*margin_-w)/2, margin_);
184 title->set_font_size(font_size_);
185 this->add_element(title);
186 }
187
188 //: assumes add_axes have been called
add_x_increments(float x_inc,float stroke_width)189 void bsvg_plot::add_x_increments(float x_inc, float stroke_width)
190 {
191 float x_inc_scaled = scale_factor_*x_inc;
192 auto* g = new bsvg_group();
193 g->set_stroke_color("black");
194 g->set_stroke_width(stroke_width);
195 g->set_stroke_opacity(0.5);
196
197 float end = axes_orig_x_ + h2_x;
198 for (float x = axes_orig_x_ + x_inc_scaled; x <= end; x += x_inc_scaled) {
199 auto* line_x = new bsvg_line(x, axes_orig_y_, x, axes_orig_y_-h2_y);
200 g->add_element(line_x);
201 }
202 this->add_element(g);
203 }
204
205 //: assumes add_axes have been called
add_y_increments(float y_inc,float stroke_width)206 void bsvg_plot::add_y_increments(float y_inc, float stroke_width)
207 {
208 float y_inc_scaled = scale_factor_*y_inc;
209 auto* g = new bsvg_group();
210 g->set_stroke_color("black");
211 g->set_stroke_width(stroke_width);
212 g->set_stroke_opacity(0.5);
213
214 float end = axes_orig_y_ - h2_y;
215 for (float y = axes_orig_y_ - y_inc_scaled; y >= end; y -= y_inc_scaled) {
216 auto* line_y = new bsvg_line(axes_orig_x_, y, axes_orig_x_+h2_x, y);
217 g->add_element(line_y);
218 }
219 this->add_element(g);
220 }
221
add_line(const std::vector<float> & xs,const std::vector<float> & ys,const std::string & color,float stroke_width)222 void bsvg_plot::add_line(const std::vector<float>& xs, const std::vector<float>& ys, const std::string& color, float stroke_width)
223 {
224 // scale the points to our plot
225 if (xs.size() != ys.size()) {
226 std::cout << " Error: bsvg_plot::add_line() - input vectors are not of the same size\n";
227 return;
228 }
229 std::vector<float> xs_copy(xs);
230 std::vector<float> ys_copy(ys);
231 for (unsigned i = 0; i < xs.size(); i++) {
232 xs_copy[i] = xs_copy[i]*scale_factor_ + axes_orig_x_;
233 ys_copy[i] = axes_orig_y_ - ys_copy[i]*scale_factor_;
234 }
235
236 auto *pl = new bsvg_polyline(xs_copy, ys_copy);
237 pl->set_stroke_color(color);
238 pl->set_fill_color("none");
239 pl->set_stroke_width(stroke_width);
240
241 this->add_element(pl);
242 }
243
add_bars_helper(const std::vector<float> & heights,const std::string & color)244 bsvg_group* bsvg_plot::add_bars_helper(const std::vector<float>& heights, const std::string& color)
245 {
246 auto* g = new bsvg_group();
247 g->set_fill_color(color);
248
249 int n = heights.size();
250 // we will set bar margins bar_w/3
251 float bar_w = h2_x / float(n + float(n + 1)/3.0f);
252 float x = axes_orig_x_ + bar_w/3.0f; // left-point of first bar
253 for (int i = 0; i < n; i++) {
254 float h = heights[i]*scale_factor_;
255 auto *r = new bsvg_rectangle(x, axes_orig_y_-h, bar_w, h);
256 g->add_element(r);
257 x += bar_w/3 + bar_w; // left-point of next bar, margin + bar width
258 }
259 return g;
260 }
261
add_x_labels_helper(const std::vector<std::string> & x_labels,const std::string & color,bool vertical_labels)262 bsvg_group* bsvg_plot::add_x_labels_helper(const std::vector<std::string>& x_labels, const std::string& color, bool vertical_labels)
263 {
264 auto* g = new bsvg_group();
265 g->set_fill_color(color);
266
267 int n = x_labels.size();
268 // we will set bar margins bar_w/3
269 float bar_w = h2_x / float(n + float(n + 1)/3.0f);
270 float x = axes_orig_x_ + bar_w/2.0f + bar_w/3.0f; // mid-point of first bar
271 for (int i = 0; i < n; i++) {
272 auto *t = new bsvg_text(x_labels[i]);
273 t->set_font_size(font_size_);
274 t->set_location(x, axes_orig_y_+margin_);
275 if (vertical_labels)
276 t->set_rotation(90);
277 g->add_element(t);
278 x += bar_w/3.0f + bar_w; // mid-point of next bar, margin + bar width
279 }
280 return g;
281 }
282
283 //: add equally spaced and equal width bars with the given heights
add_bars(const std::vector<float> & heights,const std::string & color)284 void bsvg_plot::add_bars(const std::vector<float>& heights, const std::string& color)
285 {
286 bsvg_group* g = add_bars_helper(heights, color);
287 this->add_element(g);
288 }
289
add_bars(const std::vector<float> & heights,const std::vector<std::string> & x_labels,bool vertical_labels,const std::string & color)290 void bsvg_plot::add_bars(const std::vector<float>& heights, const std::vector<std::string>& x_labels, bool vertical_labels, const std::string& color)
291 {
292 bsvg_group* g = add_bars_helper(heights, color);
293 this->add_element(g);
294 bsvg_group* tg = add_x_labels_helper(x_labels, color, vertical_labels);
295 this->add_element(tg);
296 }
297
add_bars(const std::vector<float> & heights,const std::vector<float> & x_labels,bool vertical_labels,const std::string & color)298 void bsvg_plot::add_bars(const std::vector<float>& heights, const std::vector<float>& x_labels, bool vertical_labels, const std::string& color)
299 {
300 bsvg_group* g = add_bars_helper(heights, color);
301 this->add_element(g);
302 std::vector<std::string> x_ls;
303 for (float x_label : x_labels) {
304 std::stringstream ss; ss << x_label;
305 x_ls.push_back(ss.str());
306 }
307 bsvg_group* tg = add_x_labels_helper(x_ls, color, vertical_labels);
308 this->add_element(tg);
309 }
310
311 //: recursive helper to find number of bars
number_of_bars_helper(const bxml_data_sptr & d)312 int number_of_bars_helper(const bxml_data_sptr& d)
313 {
314 auto* r_elm = dynamic_cast<bxml_element*>(d.ptr());
315 if (!r_elm)
316 return 0;
317
318 int cnt = 0;
319 for (auto it = r_elm->data_begin(); it != r_elm->data_end(); it++) {
320 if ((*it)->type() != bxml_element::ELEMENT)
321 continue;
322 auto* it_elm = dynamic_cast<bxml_element*>((*it).ptr());
323 if (it_elm->name() == "rect")
324 cnt++;
325 else if (it_elm->name() == "g") {
326 cnt += number_of_bars_helper(*it);
327 }
328 }
329 return cnt;
330 }
331
number_of_bars()332 int bsvg_plot::number_of_bars()
333 {
334 // get the root
335 bxml_element query("svg");
336 bxml_data_sptr root = bxml_find_by_name(this->root_element(), query);
337 if (!root)
338 return -1;
339 return number_of_bars_helper(root);
340 }
341
342 //: add bars sequentially with a fixed interval and width.
343 // use margin_ as the width of each bar and leave margin_/3 intervals in between
344 // the total width of the plot needs to be adjusted during initialization to contain all desired number of bars
add_bar(const float height,const std::string & color)345 int bsvg_plot::add_bar(const float height, const std::string& color)
346 {
347 // first find the next available bar location (count the number of rects in the document)
348 int cnt = this->number_of_bars();
349 if (cnt < 0) {
350 std::cerr << "In bsvg_plot::add_bar() -- problems with the plot document!\n";
351 return -1;
352 }
353 float x = axes_orig_x_ + margin_/3.0f; // left-point of first bar
354 x += cnt*(margin_/3.0f + margin_); // left-point of next bar, margin + bar width
355 float h = height*scale_factor_;
356 auto *r = new bsvg_rectangle(x, axes_orig_y_-h, margin_, h);
357 r->set_fill_color(color);
358 this->add_element(r);
359 return cnt;
360 }
361
add_bar(const float height,const std::string & label,bool vertical_label,const std::string & color)362 int bsvg_plot::add_bar(const float height, const std::string& label, bool vertical_label, const std::string& color)
363 {
364 int cnt = add_bar(height, color);
365 if (cnt < 0) {
366 std::cerr << "In bsvg_plot::add_bar() -- problems with the plot document!\n";
367 return -1;
368 }
369 float x = axes_orig_x_ + margin_/2.0f + margin_/3.0f; // mid-point of first text
370 x += cnt*(margin_/3.0f + margin_);
371 auto *t = new bsvg_text(label);
372 t->set_font_size(font_size_);
373 t->set_location(x, axes_orig_y_+margin_);
374 if (vertical_label)
375 t->set_rotation(90);
376 this->add_element(t);
377 return cnt;
378 }
379
add_bar(const float height,const float x_label,bool vertical_label,const std::string & color)380 int bsvg_plot::add_bar(const float height, const float x_label, bool vertical_label, const std::string& color)
381 {
382 std::stringstream ss; ss << x_label;
383 return add_bar(height, ss.str(), vertical_label, color);
384 }
385
386 //: add splices for a pie chart
add_splice(float center_x,float center_y,float radius,float start_angle,float end_angle,const std::string & color)387 void bsvg_plot::add_splice(float center_x, float center_y, float radius, float start_angle, float end_angle, const std::string& color)
388 {
389 auto* splice_g = new bsvg_splice(center_x, center_y, radius, start_angle, end_angle);
390 splice_g->set_fill_color(color);
391 splice_g->set_stroke_color("black");
392 this->add_element(splice_g);
393 }
394
add_splice(float center_x,float center_y,float radius,float start_angle,float end_angle,unsigned red,unsigned green,unsigned blue)395 void bsvg_plot::add_splice(float center_x, float center_y, float radius, float start_angle, float end_angle, unsigned red, unsigned green, unsigned blue)
396 {
397 auto* splice_g = new bsvg_splice(center_x, center_y, radius, start_angle, end_angle);
398 splice_g->set_fill_color(red, green, blue);
399 splice_g->set_stroke_color("black");
400 this->add_element(splice_g);
401 }
402