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