fl-bar-chart.nut 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. /*
  2. * Copyright (C) 2013 by Domingo Alvarez Duarte <[email protected]>
  3. *
  4. * Licensed under GPLv3, see http://www.gnu.org/licenses/gpl.html.
  5. */
  6. class Fl_Bar_Chart extends Fl_Box {
  7. _bars = null; //{label, value, color}
  8. _bar_depth = null;
  9. _bar_update_count = null;
  10. _bar_num_format_decimals = null;
  11. _bar_color = null;
  12. constructor(X, Y, W, H, L = null) {
  13. base.constructor(X,Y,W,H,L);
  14. color(FL_WHITE);
  15. align(FL_ALIGN_RIGHT);
  16. labeltype(FL_NO_LABEL);
  17. _bars = [];
  18. _bar_color = FL_BLUE;
  19. _bar_depth = 8;
  20. _bar_update_count = 0;
  21. _bar_num_format_decimals = 0;
  22. }
  23. function bar_count(){return _bars.size();}
  24. function bar_add(bheader, bvalue, bcolor=FL_BLUE) {
  25. _bars.push( [bheader, bvalue, bcolor]);
  26. }
  27. function bar_get(idx) { return _bars[idx];}
  28. function bar_depth(depth=null){
  29. if(depth==null) return _depth;
  30. if(_bar_depth == depth) return;
  31. _bar_depth = depth;
  32. update_bar_chart();
  33. }
  34. function bar_num_format_decimals(decimals=null) {
  35. if(decimals==null) return _bar_num_format_decimals;
  36. if(_bar_num_format_decimals == decimals) return;
  37. _bar_num_format_decimals = decimals;
  38. update_bar_chart();
  39. }
  40. function bar_begin_update() {return ++_bar_update_count;}
  41. function bar_end_update() {
  42. if(_bar_update_count == 0) throw("update count underflow");
  43. if(--_bar_update_count == 0) update_bar_chart();
  44. }
  45. function update_bar_chart() {
  46. if(_bar_update_count == 0) redraw();
  47. }
  48. function bar_clear(){
  49. _bars.clear();
  50. update_bar_chart();
  51. }
  52. function normalize_scale_units(oldScale) {
  53. local result = oldScale;
  54. if(result < 2) result = 2;
  55. else if(result <= 5) result = 5;
  56. else if(result <= 10) result = 10;
  57. else
  58. {
  59. local str = result.tostring();
  60. local t = str[0] - '0';
  61. do
  62. {
  63. result = result / 10;
  64. t *= 10;
  65. }
  66. while(result >= 10);
  67. return t;
  68. }
  69. return result;
  70. }
  71. function draw_mypolygon(x1, y1, x2, y2, x3, y3, x4, y4, line_color, fill_color) {
  72. fl_color(fill_color);
  73. fl_polygon(x1, y1, x2, y2, x3, y3, x4, y4);
  74. fl_color(line_color);
  75. fl_line_style(FL_SOLID, 1, NULL);
  76. fl_loop(x1, y1, x2, y2, x3, y3, x4, y4);
  77. }
  78. function draw_mybar(pbx, pby, pw, ph, pdepth, line_color, fill_color) {
  79. local lbx2 = pbx+pw;
  80. local lbx3 = lbx2+pdepth;
  81. local lby2 = pby+ph;
  82. local lby3 = pby-pdepth;
  83. // face rect
  84. draw_mypolygon(
  85. pbx, pby,
  86. lbx2, pby,
  87. lbx2, lby2,
  88. pbx, lby2,
  89. line_color,
  90. fill_color);
  91. // top polygon
  92. draw_mypolygon(
  93. pbx, pby,
  94. lbx2, pby,
  95. lbx3, lby3,
  96. pbx+pdepth, lby3,
  97. line_color,
  98. fill_color);
  99. // side polygon
  100. draw_mypolygon(
  101. lbx2, pby,
  102. lbx3, lby3,
  103. lbx3, lby2-pdepth,
  104. lbx2, lby2,
  105. line_color,
  106. fill_color);
  107. }
  108. function draw() {
  109. //set font size, color and clear
  110. fl_font(labelfont(), labelsize());
  111. fl_color(color());
  112. fl_rectf(x(), y(), w(), h());
  113. local measure = {};
  114. fl_measure("W", measure, false);
  115. local char_width = measure.width, char_height = measure.height;
  116. local inner_padding = char_height;
  117. local box_x = x();
  118. local box_y = y() + char_height;
  119. local str;
  120. local nf_buf;
  121. local bars_box_x, bars_box_y, bars_box_width, bars_box_height;
  122. local box_label_width = inner_padding;
  123. local value_max = 0.0;
  124. foreach(bar in _bars)
  125. {
  126. local max_value_str_width = fl_width(bar[0] /*label*/, bar[0].size());
  127. if(max_value_str_width > box_label_width) box_label_width = max_value_str_width;
  128. if(bar[1] /*value*/ > value_max) value_max = bar[1];
  129. }
  130. box_label_width += inner_padding *2;
  131. bars_box_x = box_x + box_label_width;
  132. bars_box_y = box_y + inner_padding;
  133. bars_box_height = h() - inner_padding - 3 * char_height;
  134. bars_box_width = w() - box_label_width - 3 * _bar_depth - inner_padding;
  135. draw_mypolygon(
  136. bars_box_x, bars_box_y+_bar_depth,
  137. bars_box_x, bars_box_y+_bar_depth+bars_box_height,
  138. bars_box_x+_bar_depth, bars_box_y+bars_box_height,
  139. bars_box_x+_bar_depth, bars_box_y,
  140. FL_BLACK,
  141. FL_YELLOW
  142. );
  143. draw_mypolygon(
  144. bars_box_x, bars_box_y+_bar_depth+bars_box_height,
  145. bars_box_x+bars_box_width+_bar_depth, bars_box_y+_bar_depth+bars_box_height,
  146. bars_box_x+bars_box_width+_bar_depth*2, bars_box_y+bars_box_height,
  147. bars_box_x+_bar_depth, bars_box_y+bars_box_height,
  148. FL_BLACK,
  149. FL_WHITE
  150. );
  151. fl_color(FL_BLACK);
  152. fl_line_style(FL_SOLID, 1, NULL);
  153. fl_rect(bars_box_x+_bar_depth, bars_box_y, bars_box_width+_bar_depth, bars_box_height+1);
  154. fl_line_style(FL_SOLID, 3, NULL);
  155. fl_line(bars_box_x, bars_box_y+_bar_depth, bars_box_x, bars_box_y+_bar_depth+bars_box_height, bars_box_x+bars_box_width+_bar_depth, bars_box_y+_bar_depth+bars_box_height);
  156. local my_label = label();
  157. if(my_label) fl_draw(my_label, my_label.len(), x() + (w() - fl_width(my_label))/2, box_y);
  158. str = value_max.tostring();
  159. fl_measure(str, measure, false);
  160. local max_value_str_width = measure.width, max_value_str_height = measure.height;
  161. local pixelPerUnit, nScaleLines, scaleUnits, itmp;
  162. if(value_max == 0) {
  163. pixelPerUnit = 1;
  164. nScaleLines = 1;
  165. }
  166. else
  167. {
  168. itmp = bars_box_width-max_value_str_width-_bar_depth;
  169. pixelPerUnit = itmp / value_max;
  170. nScaleLines = math.ceil(itmp / (2*max_value_str_width));
  171. }
  172. if(nScaleLines == 0) scaleUnits = value_max +1;
  173. else
  174. {
  175. scaleUnits = math.floor(value_max / nScaleLines) +1;
  176. }
  177. scaleUnits = normalize_scale_units(scaleUnits);
  178. if(scaleUnits == 0) nScaleLines = 1;
  179. else nScaleLines = (value_max / scaleUnits)+1;
  180. fl_color(FL_DARK3);
  181. fl_line_style(FL_DASH, 1, "\0x05\0x05");
  182. local half_max_value_str_width = math.floor(max_value_str_width / 2);
  183. local bars_box_bottom = bars_box_y+_bar_depth+bars_box_height+2;
  184. local str_zero = "0";
  185. local function draw_scaleLine(dk,s) {
  186. local lx, ly, lx2, ly2;
  187. lx = bars_box_x + dk + _bar_depth;
  188. ly = bars_box_y + bars_box_height;
  189. lx2 = bars_box_x + dk;
  190. ly2 = bars_box_y + _bar_depth+bars_box_height;
  191. fl_line(lx, bars_box_y, lx, ly, lx2, ly2);
  192. fl_line(lx2, ly2, lx2, ly2 + 2);
  193. local oldcolor = fl_color();
  194. fl_color(FL_BLACK);
  195. fl_draw(s, s.len(), bars_box_x + dk - half_max_value_str_width, bars_box_bottom+char_height);
  196. fl_color(oldcolor);
  197. }
  198. if(value_max == 0)
  199. {
  200. local ik = math.floor(bars_box_width / 2);
  201. draw_scaleLine(ik,str_zero);
  202. }
  203. else
  204. {
  205. fl_color(FL_BLACK);
  206. fl_draw(str_zero, str_zero.len(), bars_box_x, bars_box_bottom+char_height);
  207. fl_color(FL_DARK3);
  208. }
  209. local _num_buf;
  210. for(local ik=0; ik < nScaleLines; ik++)
  211. {
  212. local xs = math.floor(scaleUnits*pixelPerUnit*ik);
  213. _num_buf = math.number_format(ik*scaleUnits, _bar_num_format_decimals);
  214. draw_scaleLine(xs, _num_buf);
  215. }
  216. local bar_height, bar_y;
  217. if(_bars.size() == 0) bar_height = 0;
  218. else bar_height = ((bars_box_height- _bar_depth) / ((2.0*_bars.size()+1)));
  219. bars_box_bottom -= 2*bar_height + _bar_depth;
  220. local ikl = 0;
  221. foreach(bar in _bars) {
  222. bar_y = bars_box_bottom - (2*ikl*bar_height) ;
  223. local value = bar[1]; //bar value
  224. local bar_width = math.floor(value*pixelPerUnit);
  225. draw_mybar(bars_box_x, bar_y, bar_width, bar_height, _bar_depth, FL_DARK3, bar[2] /*color*/);
  226. _num_buf = math.number_format(value, _bar_num_format_decimals);
  227. local rcenter = math.floor(bar_y+(bar_height-_bar_depth) / 2);
  228. fl_color(FL_DARK3);
  229. fl_line(bars_box_x+bar_width+_bar_depth/2,rcenter, bars_box_x+bar_width+_bar_depth*2, rcenter);
  230. fl_measure(_num_buf, measure, false);
  231. local str_width = measure.width, str_height = measure.height;
  232. local rc_x = bars_box_x+bar_width+_bar_depth*2;
  233. local rc_w = str_width+_bar_depth;
  234. local rc_y = rcenter - str_height/2;
  235. local rc_h = str_height * 1.1;
  236. fl_color(FL_YELLOW);
  237. fl_line_style(FL_SOLID, 2);
  238. fl_rectf(rc_x, rc_y, rc_w, rc_h);
  239. fl_color(FL_BLACK);
  240. fl_draw(_num_buf, _num_buf.len(), rc_x+_bar_depth/2, rcenter + str_height/3);
  241. str = bar[0]; //bar header
  242. fl_measure(str, measure, false);
  243. str_width = measure.width;
  244. str_height = measure.height;
  245. local str_x, str_y;
  246. fl_color(FL_BLACK);
  247. switch(align()){
  248. case FL_ALIGN_CENTER:
  249. {
  250. str_x = box_x+(box_label_width/2) - (str_width / 2);
  251. str_y = rcenter + str_height/3;
  252. }
  253. break;
  254. case FL_ALIGN_RIGHT:
  255. {
  256. str_x = bars_box_x-str_width-inner_padding;
  257. str_y = rcenter + str_height/3;
  258. }
  259. break;
  260. default:
  261. {
  262. str_x = box_x + inner_padding;
  263. str_y = rcenter + str_height/3;
  264. }
  265. }
  266. fl_draw(str, str.len(), str_x, str_y);
  267. ++ikl;
  268. }
  269. fl_line_style(0);
  270. }
  271. };
  272. /*
  273. math.number_format_set_thousand_sep(".");
  274. math.number_format_set_dec_point(",");
  275. math.mynumber_format <- function (value, decp=2){
  276. value = value.tofloat();
  277. local fvalue, neg, intp;
  278. fvalue = format("%.0" + decp + "f", value);
  279. if (decp > 0){
  280. if (value < 0) intp = fvalue.slice(1, -(decp+1));
  281. else intp = fvalue.slice(0, -(decp+1));
  282. }
  283. else intp = fvalue;
  284. local thousands_rep = "%1" + math.thousand_separator();
  285. local thousandsp = intp.reverse().gsub("(%d%d%d)",thousands_rep);
  286. if ((intp.len() % 3) == 0) thousandsp = thousandsp.slice(0,-1);
  287. if (decp > 0) fvalue = thousandsp.reverse() + math.decimal_separator() + fvalue.slice(-decp);
  288. else fvalue = thousandsp.reverse();
  289. if (value < 0) return "-" + fvalue;
  290. else return fvalue;
  291. }
  292. function blank_when_zero(snum) {return snum.tofloat();}
  293. local win = Fl_Double_Window(10,50, 800, 560, "My Barchart");
  294. local mychart = Fl_Bar_Chart(20,20, 760, 520, "My Barchart");
  295. win->end();
  296. mychart->labelsize(18);
  297. local function rand_value(){return math.rand() % 300;}
  298. mychart.bar_add("January", rand_value());
  299. mychart->bar_add("Febrary", rand_value());
  300. mychart->bar_add("March", rand_value());
  301. mychart->bar_add("April", rand_value());
  302. mychart->bar_add("May", rand_value());
  303. mychart->bar_add("June", rand_value(), FL_RED);
  304. mychart->bar_add("July", rand_value(), FL_GREEN);
  305. mychart->bar_add("August", rand_value());
  306. mychart->bar_add("September", rand_value());
  307. mychart->bar_add("November", rand_value());
  308. mychart->bar_add("Dicember", rand_value());
  309. win->resizable(mychart);
  310. win->show_main();
  311. Fl.run();
  312. */