gruff 0.3.7 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (540) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +2 -1
  3. data/.travis.yml +6 -0
  4. data/Gemfile +2 -8
  5. data/History.txt +76 -0
  6. data/MIT-LICENSE +3 -2
  7. data/README.md +96 -6
  8. data/RELEASE.md +92 -0
  9. data/Rakefile +203 -23
  10. data/gruff.gemspec +13 -10
  11. data/lib/gruff.rb +8 -7
  12. data/lib/gruff/accumulator_bar.rb +4 -13
  13. data/lib/gruff/area.rb +6 -13
  14. data/lib/gruff/bar.rb +24 -3
  15. data/lib/gruff/bar_conversion.rb +31 -31
  16. data/lib/gruff/base.rb +310 -267
  17. data/lib/gruff/bezier.rb +46 -0
  18. data/lib/gruff/bullet.rb +3 -1
  19. data/lib/gruff/dot.rb +56 -44
  20. data/lib/gruff/line.rb +83 -69
  21. data/lib/gruff/net.rb +7 -9
  22. data/lib/gruff/pie.rb +2 -4
  23. data/lib/gruff/scatter.rb +25 -29
  24. data/lib/gruff/side_bar.rb +54 -37
  25. data/lib/gruff/side_stacked_bar.rb +25 -5
  26. data/lib/gruff/spider.rb +22 -21
  27. data/lib/gruff/themes.rb +102 -0
  28. data/lib/gruff/version.rb +1 -1
  29. data/test/gruff_test_case.rb +68 -38
  30. data/test/image_compare.rb +58 -0
  31. data/test/output/accum_bar.png +0 -0
  32. data/test/output/area_keynote.png +0 -0
  33. data/test/output/area_keynote_small.png +0 -0
  34. data/test/output/area_many.png +0 -0
  35. data/test/output/area_many_areas_small.png +0 -0
  36. data/test/output/area_tiny.png +0 -0
  37. data/test/output/area_wide.png +0 -0
  38. data/test/output/bar2_wrapped_legend_16_400.png +0 -0
  39. data/test/output/bar2_wrapped_legend_16_800.png +0 -0
  40. data/test/output/bar2_wrapped_legend_30_400.png +0 -0
  41. data/test/output/bar2_wrapped_legend_30_800.png +0 -0
  42. data/test/output/bar2_wrapped_legend_4_400.png +0 -0
  43. data/test/output/bar2_wrapped_legend_4_800.png +0 -0
  44. data/test/output/bar2_wrapped_legend__400.png +0 -0
  45. data/test/output/bar2_wrapped_legend__800.png +0 -0
  46. data/test/output/bar_background_gradient_bottom_top.png +0 -0
  47. data/test/output/bar_background_gradient_left_right.png +0 -0
  48. data/test/output/bar_background_gradient_right_left.png +0 -0
  49. data/test/output/bar_background_gradient_top_bottom.png +0 -0
  50. data/test/output/bar_background_gradient_topleft_bottomright.png +0 -0
  51. data/test/output/bar_background_gradient_topright_bottomleft.png +0 -0
  52. data/test/output/bar_formatted_numbers.png +0 -0
  53. data/test/output/bar_keynote.png +0 -0
  54. data/test/output/bar_keynote_small.png +0 -0
  55. data/test/output/bar_long_legend_text.png +0 -0
  56. data/test/output/bar_manual_colors.png +0 -0
  57. data/test/output/bar_marker_shadow.png +0 -0
  58. data/test/output/bar_nearly_zero_max_10.png +0 -0
  59. data/test/output/bar_no_legend.png +0 -0
  60. data/test/output/bar_no_line_markers.png +0 -0
  61. data/test/output/bar_no_title.png +0 -0
  62. data/test/output/bar_no_title_or_legend.png +0 -0
  63. data/test/output/bar_odeo.png +0 -0
  64. data/test/output/bar_one_value.png +0 -0
  65. data/test/output/bar_pos_neg.png +0 -0
  66. data/test/output/bar_rails_keynote.png +0 -0
  67. data/test/output/bar_set_absolute_trunc.png +0 -0
  68. data/test/output/bar_set_label_stagger_height.png +0 -0
  69. data/test/output/bar_set_legend_box_size_lg.png +0 -0
  70. data/test/output/bar_set_legend_box_size_sm.png +0 -0
  71. data/test/output/bar_set_marker.png +0 -0
  72. data/test/output/bar_set_trailing_dots_trunc.png +0 -0
  73. data/test/output/bar_spacing_full.png +0 -0
  74. data/test/output/bar_spacing_half.png +0 -0
  75. data/test/output/bar_spacing_none.png +0 -0
  76. data/test/output/bar_tall_graph.png +0 -0
  77. data/test/output/bar_tall_graph_small.png +0 -0
  78. data/test/output/bar_themed.png +0 -0
  79. data/test/output/bar_title_margin.png +0 -0
  80. data/test/output/bar_value_labels.png +0 -0
  81. data/test/output/bar_wide_graph.png +0 -0
  82. data/test/output/bar_wide_graph_small.png +0 -0
  83. data/test/output/bar_wrapped_legend_16_400.png +0 -0
  84. data/test/output/bar_wrapped_legend_16_800.png +0 -0
  85. data/test/output/bar_wrapped_legend_30_400.png +0 -0
  86. data/test/output/bar_wrapped_legend_30_800.png +0 -0
  87. data/test/output/bar_wrapped_legend_4_400.png +0 -0
  88. data/test/output/bar_wrapped_legend_4_800.png +0 -0
  89. data/test/output/bar_wrapped_legend__400.png +0 -0
  90. data/test/output/bar_wrapped_legend__800.png +0 -0
  91. data/test/output/bar_x_y_labels.png +0 -0
  92. data/test/output/bar_y_increment_1.png +0 -0
  93. data/test/output/bar_y_increment_2.0.png +0 -0
  94. data/test/output/bar_y_increment_20.png +0 -0
  95. data/test/output/bar_y_increment_5.png +0 -0
  96. data/test/output/bar_zero_marker_count.png +0 -0
  97. data/test/output/bar_zero_spacing.png +0 -0
  98. data/test/output/bezier.png +0 -0
  99. data/test/output/bezier_2.png +0 -0
  100. data/test/output/bezier_3.png +0 -0
  101. data/test/output/bullet_greyscale.png +0 -0
  102. data/test/output/bullet_no_options.png +0 -0
  103. data/test/output/dot.png +0 -0
  104. data/test/output/dot_manual_colors.png +0 -0
  105. data/test/output/dot_nearly_zero_max_10.png +0 -0
  106. data/test/output/dot_no_legend.png +0 -0
  107. data/test/output/dot_no_line_markers.png +0 -0
  108. data/test/output/dot_no_title.png +0 -0
  109. data/test/output/dot_no_title_or_legend.png +0 -0
  110. data/test/output/dot_one_value.png +0 -0
  111. data/test/output/dot_pos_neg.png +0 -0
  112. data/test/output/dot_set_legend_box_size_lg.png +0 -0
  113. data/test/output/dot_set_legend_box_size_sm.png +0 -0
  114. data/test/output/dot_set_marker.png +0 -0
  115. data/test/output/dot_small.png +0 -0
  116. data/test/output/dot_tall_graph.png +0 -0
  117. data/test/output/dot_tall_graph_small.png +0 -0
  118. data/test/output/dot_themed.png +0 -0
  119. data/test/output/dot_wide_graph.png +0 -0
  120. data/test/output/dot_wide_graph_small.png +0 -0
  121. data/test/output/dot_x_y_labels.png +0 -0
  122. data/test/output/dot_y_increment_1.png +0 -0
  123. data/test/output/dot_y_increment_2.0.png +0 -0
  124. data/test/output/dot_y_increment_20.png +0 -0
  125. data/test/output/dot_y_increment_5.png +0 -0
  126. data/test/output/enhancements-1.png +0 -0
  127. data/test/output/enhancements.png +0 -0
  128. data/test/output/line_all_neg.png +0 -0
  129. data/test/output/line_all_neg_400.png +0 -0
  130. data/test/output/line_all_neg_no_max.png +0 -0
  131. data/test/output/line_baseline.png +0 -0
  132. data/test/output/line_hang_value.png +0 -0
  133. data/test/output/line_hide_dots.png +0 -0
  134. data/test/output/line_hide_lines.png +0 -0
  135. data/test/output/line_jruby_error.png +0 -0
  136. data/test/output/line_large.png +0 -0
  137. data/test/output/line_large_baseline.png +0 -0
  138. data/test/output/line_legend_at_bottom.png +0 -0
  139. data/test/output/line_many.png +0 -0
  140. data/test/output/line_many_lines_small.png +0 -0
  141. data/test/output/line_many_numbers.png +0 -0
  142. data/test/output/line_marker_label_accuracy.png +0 -0
  143. data/test/output/line_more_sets_than_colors.png +0 -0
  144. data/test/output/line_no_data.png +0 -0
  145. data/test/output/line_no_data_msg.png +0 -0
  146. data/test/output/line_no_data_other.png +0 -0
  147. data/test/output/line_no_hide.png +0 -0
  148. data/test/output/line_no_legend.png +0 -0
  149. data/test/output/line_no_line_markers.png +0 -0
  150. data/test/output/line_no_title.png +0 -0
  151. data/test/output/line_nothing_but_the_graph.png +0 -0
  152. data/test/output/line_one_value.png +0 -0
  153. data/test/output/line_one_value_array.png +0 -0
  154. data/test/output/line_pos_neg.png +0 -0
  155. data/test/output/line_pos_neg_400.png +0 -0
  156. data/test/output/line_similar_high_end_values.png +0 -0
  157. data/test/output/line_similar_high_end_values_with_floor.png +0 -0
  158. data/test/output/line_small_small_zero.png +0 -0
  159. data/test/output/line_small_values.png +0 -0
  160. data/test/output/line_small_values_small.png +0 -0
  161. data/test/output/line_small_zero.png +0 -0
  162. data/test/output/line_some_nil_points.png +0 -0
  163. data/test/output/line_theme_37signals_.png +0 -0
  164. data/test/output/line_theme_37signals_400.png +0 -0
  165. data/test/output/line_theme_keynote_.png +0 -0
  166. data/test/output/line_theme_keynote_400.png +0 -0
  167. data/test/output/line_theme_odeo_.png +0 -0
  168. data/test/output/line_theme_odeo_400.png +0 -0
  169. data/test/output/line_theme_rails_keynote_.png +0 -0
  170. data/test/output/line_theme_rails_keynote_400.png +0 -0
  171. data/test/output/line_tiny.png +0 -0
  172. data/test/output/line_transparent.png +0 -0
  173. data/test/output/line_very_small.png +0 -0
  174. data/test/output/line_wide_graph.png +0 -0
  175. data/test/output/line_wide_graph_small.png +0 -0
  176. data/test/output/line_xy.png +0 -0
  177. data/test/output/line_xy_pairs.png +0 -0
  178. data/test/output/line_y_axis_increment.png +0 -0
  179. data/test/output/mini_bar.png +0 -0
  180. data/test/output/mini_pie.png +0 -0
  181. data/test/output/mini_pie_right_legend.png +0 -0
  182. data/test/output/mini_side_bar.png +0 -0
  183. data/test/output/mini_side_bar_multi_color.png +0 -0
  184. data/test/output/net_dots_tiny.png +0 -0
  185. data/test/output/net_large.png +0 -0
  186. data/test/output/net_many.png +0 -0
  187. data/test/output/net_many_nets_small.png +0 -0
  188. data/test/output/net_no_data.png +0 -0
  189. data/test/output/net_no_data_msg.png +0 -0
  190. data/test/output/net_no_data_other.png +0 -0
  191. data/test/output/net_no_legend.png +0 -0
  192. data/test/output/net_no_net_markers.png +0 -0
  193. data/test/output/net_no_title.png +0 -0
  194. data/test/output/net_nothing_but_the_graph.png +0 -0
  195. data/test/output/net_similar_high_end_values.png +0 -0
  196. data/test/output/net_small.png +0 -0
  197. data/test/output/net_small_small.png +0 -0
  198. data/test/output/net_small_small_zero.png +0 -0
  199. data/test/output/net_small_zero.png +0 -0
  200. data/test/output/net_wide_graph.png +0 -0
  201. data/test/output/net_wide_graph_small.png +0 -0
  202. data/test/output/pie_equal.png +0 -0
  203. data/test/output/pie_grey.png +0 -0
  204. data/test/output/pie_keynote.png +0 -0
  205. data/test/output/pie_keynote_small.png +0 -0
  206. data/test/output/pie_legend.png +0 -0
  207. data/test/output/pie_legend_small.png +0 -0
  208. data/test/output/pie_nearly_equal.png +0 -0
  209. data/test/output/pie_one_val.png +0 -0
  210. data/test/output/pie_pastel.png +0 -0
  211. data/test/output/pie_simple.png +0 -0
  212. data/test/output/pie_wide.png +0 -0
  213. data/test/output/pie_wrapped_legend_16_400.png +0 -0
  214. data/test/output/pie_wrapped_legend_16_800.png +0 -0
  215. data/test/output/pie_wrapped_legend_30_400.png +0 -0
  216. data/test/output/pie_wrapped_legend_30_800.png +0 -0
  217. data/test/output/pie_wrapped_legend_4_400.png +0 -0
  218. data/test/output/pie_wrapped_legend_4_800.png +0 -0
  219. data/test/output/pie_wrapped_legend__400.png +0 -0
  220. data/test/output/pie_wrapped_legend__800.png +0 -0
  221. data/test/output/pie_zero.png +0 -0
  222. data/test/output/scatter_all_neg.png +0 -0
  223. data/test/output/scatter_all_neg_400.png +0 -0
  224. data/test/output/scatter_basic.png +0 -0
  225. data/test/output/scatter_many.png +0 -0
  226. data/test/output/scatter_no_data.png +0 -0
  227. data/test/output/scatter_no_data_msg.png +0 -0
  228. data/test/output/scatter_no_data_other.png +0 -0
  229. data/test/output/scatter_no_hide.png +0 -0
  230. data/test/output/scatter_no_labels.png +0 -0
  231. data/test/output/scatter_no_legend.png +0 -0
  232. data/test/output/scatter_no_line_markers.png +0 -0
  233. data/test/output/scatter_no_title.png +0 -0
  234. data/test/output/scatter_nothing_but_the_graph.png +0 -0
  235. data/test/output/scatter_pos_neg.png +0 -0
  236. data/test/output/scatter_pos_neg_400.png +0 -0
  237. data/test/output/scatter_wide_graph.png +0 -0
  238. data/test/output/scatter_wide_graph_small.png +0 -0
  239. data/test/output/scene_hazy_night.png +0 -0
  240. data/test/output/scene_not_hazy_day.png +0 -0
  241. data/test/output/scene_partly_cloudy_day.png +0 -0
  242. data/test/output/scene_stormy_day.png +0 -0
  243. data/test/output/scene_stormy_night.png +0 -0
  244. data/test/output/side_bar.png +0 -0
  245. data/test/output/side_bar_data_range.png +0 -0
  246. data/test/output/side_bar_labels.png +0 -0
  247. data/test/output/side_bar_spacing_full.png +0 -0
  248. data/test/output/side_bar_spacing_half.png +0 -0
  249. data/test/output/side_bar_spacing_none.png +0 -0
  250. data/test/output/side_stacked_bar_keynote.png +0 -0
  251. data/test/output/side_stacked_bar_keynote_small.png +0 -0
  252. data/test/output/side_stacked_bar_labels.png +0 -0
  253. data/test/output/side_stacked_bar_long_label.png +0 -0
  254. data/test/output/side_stacked_bar_wide.png +0 -0
  255. data/test/output/spider_37signals.png +0 -0
  256. data/test/output/spider_equal.png +0 -0
  257. data/test/output/spider_keynote.png +0 -0
  258. data/test/output/spider_legend.png +0 -0
  259. data/test/output/spider_legend_small.png +0 -0
  260. data/test/output/spider_lots_of_data.png +0 -0
  261. data/test/output/spider_lots_of_data_normal_names.png +0 -0
  262. data/test/output/spider_nearly_equal.png +0 -0
  263. data/test/output/spider_no_axes.png +0 -0
  264. data/test/output/spider_no_background.png +0 -0
  265. data/test/output/spider_no_print.png +0 -0
  266. data/test/output/spider_overlay_1.png +0 -0
  267. data/test/output/spider_overlay_2.png +0 -0
  268. data/test/output/spider_rotation.png +0 -0
  269. data/test/output/spider_small.png +0 -0
  270. data/test/output/spider_wide.png +0 -0
  271. data/test/output/spider_zero.png +0 -0
  272. data/test/output/stacked_area_keynote.png +0 -0
  273. data/test/output/stacked_area_keynote_small.png +0 -0
  274. data/test/output/stacked_bar_keynote.png +0 -0
  275. data/test/output/stacked_bar_keynote_small.png +0 -0
  276. data/test/output_java/accum_bar.png +0 -0
  277. data/test/output_java/area_keynote.png +0 -0
  278. data/test/output_java/area_keynote_small.png +0 -0
  279. data/test/output_java/area_many.png +0 -0
  280. data/test/output_java/area_many_areas_small.png +0 -0
  281. data/test/output_java/area_tiny.png +0 -0
  282. data/test/output_java/area_wide.png +0 -0
  283. data/test/output_java/bar2_wrapped_legend_16_400.png +0 -0
  284. data/test/output_java/bar2_wrapped_legend_16_800.png +0 -0
  285. data/test/output_java/bar2_wrapped_legend_30_400.png +0 -0
  286. data/test/output_java/bar2_wrapped_legend_30_800.png +0 -0
  287. data/test/output_java/bar2_wrapped_legend_4_400.png +0 -0
  288. data/test/output_java/bar2_wrapped_legend_4_800.png +0 -0
  289. data/test/output_java/bar2_wrapped_legend__400.png +0 -0
  290. data/test/output_java/bar2_wrapped_legend__800.png +0 -0
  291. data/test/output_java/bar_background_gradient_bottom_top.png +0 -0
  292. data/test/output_java/bar_background_gradient_left_right.png +0 -0
  293. data/test/output_java/bar_background_gradient_right_left.png +0 -0
  294. data/test/output_java/bar_background_gradient_top_bottom.png +0 -0
  295. data/test/output_java/bar_background_gradient_topleft_bottomright.png +0 -0
  296. data/test/output_java/bar_background_gradient_topright_bottomleft.png +0 -0
  297. data/test/output_java/bar_formatted_numbers.png +0 -0
  298. data/test/output_java/bar_keynote.png +0 -0
  299. data/test/output_java/bar_keynote_small.png +0 -0
  300. data/test/output_java/bar_long_legend_text.png +0 -0
  301. data/test/output_java/bar_manual_colors.png +0 -0
  302. data/test/output_java/bar_marker_shadow.png +0 -0
  303. data/test/output_java/bar_nearly_zero_max_10.png +0 -0
  304. data/test/output_java/bar_no_legend.png +0 -0
  305. data/test/output_java/bar_no_line_markers.png +0 -0
  306. data/test/output_java/bar_no_title.png +0 -0
  307. data/test/output_java/bar_no_title_or_legend.png +0 -0
  308. data/test/output_java/bar_odeo.png +0 -0
  309. data/test/output_java/bar_one_value.png +0 -0
  310. data/test/output_java/bar_pos_neg.png +0 -0
  311. data/test/output_java/bar_rails_keynote.png +0 -0
  312. data/test/output_java/bar_set_absolute_trunc.png +0 -0
  313. data/test/output_java/bar_set_label_stagger_height.png +0 -0
  314. data/test/output_java/bar_set_legend_box_size_lg.png +0 -0
  315. data/test/output_java/bar_set_legend_box_size_sm.png +0 -0
  316. data/test/output_java/bar_set_marker.png +0 -0
  317. data/test/output_java/bar_set_trailing_dots_trunc.png +0 -0
  318. data/test/output_java/bar_spacing_full.png +0 -0
  319. data/test/output_java/bar_spacing_half.png +0 -0
  320. data/test/output_java/bar_spacing_none.png +0 -0
  321. data/test/output_java/bar_tall_graph.png +0 -0
  322. data/test/output_java/bar_tall_graph_small.png +0 -0
  323. data/test/output_java/bar_themed.png +0 -0
  324. data/test/output_java/bar_title_margin.png +0 -0
  325. data/test/output_java/bar_value_labels.png +0 -0
  326. data/test/output_java/bar_wide_graph.png +0 -0
  327. data/test/output_java/bar_wide_graph_small.png +0 -0
  328. data/test/output_java/bar_wrapped_legend_16_400.png +0 -0
  329. data/test/output_java/bar_wrapped_legend_16_800.png +0 -0
  330. data/test/output_java/bar_wrapped_legend_30_400.png +0 -0
  331. data/test/output_java/bar_wrapped_legend_30_800.png +0 -0
  332. data/test/output_java/bar_wrapped_legend_4_400.png +0 -0
  333. data/test/output_java/bar_wrapped_legend_4_800.png +0 -0
  334. data/test/output_java/bar_wrapped_legend__400.png +0 -0
  335. data/test/output_java/bar_wrapped_legend__800.png +0 -0
  336. data/test/output_java/bar_x_y_labels.png +0 -0
  337. data/test/output_java/bar_y_increment_1.png +0 -0
  338. data/test/output_java/bar_y_increment_2.0.png +0 -0
  339. data/test/output_java/bar_y_increment_20.png +0 -0
  340. data/test/output_java/bar_y_increment_5.png +0 -0
  341. data/test/output_java/bar_zero_marker_count.png +0 -0
  342. data/test/output_java/bar_zero_spacing.png +0 -0
  343. data/test/output_java/bezier.png +0 -0
  344. data/test/output_java/bezier_2.png +0 -0
  345. data/test/output_java/bezier_3.png +0 -0
  346. data/test/output_java/bullet_greyscale.png +0 -0
  347. data/test/output_java/bullet_no_options.png +0 -0
  348. data/test/output_java/dot.png +0 -0
  349. data/test/output_java/dot_manual_colors.png +0 -0
  350. data/test/output_java/dot_nearly_zero_max_10.png +0 -0
  351. data/test/output_java/dot_no_legend.png +0 -0
  352. data/test/output_java/dot_no_line_markers.png +0 -0
  353. data/test/output_java/dot_no_title.png +0 -0
  354. data/test/output_java/dot_no_title_or_legend.png +0 -0
  355. data/test/output_java/dot_one_value.png +0 -0
  356. data/test/output_java/dot_pos_neg.png +0 -0
  357. data/test/output_java/dot_set_legend_box_size_lg.png +0 -0
  358. data/test/output_java/dot_set_legend_box_size_sm.png +0 -0
  359. data/test/output_java/dot_set_marker.png +0 -0
  360. data/test/output_java/dot_small.png +0 -0
  361. data/test/output_java/dot_tall_graph.png +0 -0
  362. data/test/output_java/dot_tall_graph_small.png +0 -0
  363. data/test/output_java/dot_themed.png +0 -0
  364. data/test/output_java/dot_wide_graph.png +0 -0
  365. data/test/output_java/dot_wide_graph_small.png +0 -0
  366. data/test/output_java/dot_x_y_labels.png +0 -0
  367. data/test/output_java/dot_y_increment_1.png +0 -0
  368. data/test/output_java/dot_y_increment_2.0.png +0 -0
  369. data/test/output_java/dot_y_increment_20.png +0 -0
  370. data/test/output_java/dot_y_increment_5.png +0 -0
  371. data/test/output_java/enhancements-1.png +0 -0
  372. data/test/output_java/enhancements.png +0 -0
  373. data/test/output_java/line_all_neg.png +0 -0
  374. data/test/output_java/line_all_neg_400.png +0 -0
  375. data/test/output_java/line_all_neg_no_max.png +0 -0
  376. data/test/output_java/line_baseline.png +0 -0
  377. data/test/output_java/line_hang_value.png +0 -0
  378. data/test/output_java/line_hide_dots.png +0 -0
  379. data/test/output_java/line_hide_lines.png +0 -0
  380. data/test/output_java/line_jruby_error.png +0 -0
  381. data/test/output_java/line_large.png +0 -0
  382. data/test/output_java/line_large_baseline.png +0 -0
  383. data/test/output_java/line_legend_at_bottom.png +0 -0
  384. data/test/output_java/line_many.png +0 -0
  385. data/test/output_java/line_many_lines_small.png +0 -0
  386. data/test/output_java/line_many_numbers.png +0 -0
  387. data/test/output_java/line_marker_label_accuracy.png +0 -0
  388. data/test/output_java/line_more_sets_than_colors.png +0 -0
  389. data/test/output_java/line_no_data.png +0 -0
  390. data/test/output_java/line_no_data_msg.png +0 -0
  391. data/test/output_java/line_no_data_other.png +0 -0
  392. data/test/output_java/line_no_hide.png +0 -0
  393. data/test/output_java/line_no_legend.png +0 -0
  394. data/test/output_java/line_no_line_markers.png +0 -0
  395. data/test/output_java/line_no_title.png +0 -0
  396. data/test/output_java/line_nothing_but_the_graph.png +0 -0
  397. data/test/output_java/line_one_value.png +0 -0
  398. data/test/output_java/line_one_value_array.png +0 -0
  399. data/test/output_java/line_pos_neg.png +0 -0
  400. data/test/output_java/line_pos_neg_400.png +0 -0
  401. data/test/output_java/line_similar_high_end_values.png +0 -0
  402. data/test/output_java/line_similar_high_end_values_with_floor.png +0 -0
  403. data/test/output_java/line_small_small_zero.png +0 -0
  404. data/test/output_java/line_small_values.png +0 -0
  405. data/test/output_java/line_small_values_small.png +0 -0
  406. data/test/output_java/line_small_zero.png +0 -0
  407. data/test/output_java/line_some_nil_points.png +0 -0
  408. data/test/output_java/line_theme_37signals_.png +0 -0
  409. data/test/output_java/line_theme_37signals_400.png +0 -0
  410. data/test/output_java/line_theme_keynote_.png +0 -0
  411. data/test/output_java/line_theme_keynote_400.png +0 -0
  412. data/test/output_java/line_theme_odeo_.png +0 -0
  413. data/test/output_java/line_theme_odeo_400.png +0 -0
  414. data/test/output_java/line_theme_rails_keynote_.png +0 -0
  415. data/test/output_java/line_theme_rails_keynote_400.png +0 -0
  416. data/test/output_java/line_tiny.png +0 -0
  417. data/test/output_java/line_transparent.png +0 -0
  418. data/test/output_java/line_very_small.png +0 -0
  419. data/test/output_java/line_wide_graph.png +0 -0
  420. data/test/output_java/line_wide_graph_small.png +0 -0
  421. data/test/output_java/line_xy.png +0 -0
  422. data/test/output_java/line_xy_pairs.png +0 -0
  423. data/test/output_java/line_y_axis_increment.png +0 -0
  424. data/test/output_java/mini_bar.png +0 -0
  425. data/test/output_java/mini_pie.png +0 -0
  426. data/test/output_java/mini_pie_right_legend.png +0 -0
  427. data/test/output_java/mini_side_bar.png +0 -0
  428. data/test/output_java/mini_side_bar_multi_color.png +0 -0
  429. data/test/output_java/net_dots_tiny.png +0 -0
  430. data/test/output_java/net_large.png +0 -0
  431. data/test/output_java/net_many.png +0 -0
  432. data/test/output_java/net_many_nets_small.png +0 -0
  433. data/test/output_java/net_no_data.png +0 -0
  434. data/test/output_java/net_no_data_msg.png +0 -0
  435. data/test/output_java/net_no_data_other.png +0 -0
  436. data/test/output_java/net_no_legend.png +0 -0
  437. data/test/output_java/net_no_net_markers.png +0 -0
  438. data/test/output_java/net_no_title.png +0 -0
  439. data/test/output_java/net_nothing_but_the_graph.png +0 -0
  440. data/test/output_java/net_similar_high_end_values.png +0 -0
  441. data/test/output_java/net_small.png +0 -0
  442. data/test/output_java/net_small_small.png +0 -0
  443. data/test/output_java/net_small_small_zero.png +0 -0
  444. data/test/output_java/net_small_zero.png +0 -0
  445. data/test/output_java/net_wide_graph.png +0 -0
  446. data/test/output_java/net_wide_graph_small.png +0 -0
  447. data/test/output_java/pie_equal.png +0 -0
  448. data/test/output_java/pie_grey.png +0 -0
  449. data/test/output_java/pie_keynote.png +0 -0
  450. data/test/output_java/pie_keynote_small.png +0 -0
  451. data/test/output_java/pie_legend.png +0 -0
  452. data/test/output_java/pie_legend_small.png +0 -0
  453. data/test/output_java/pie_nearly_equal.png +0 -0
  454. data/test/output_java/pie_one_val.png +0 -0
  455. data/test/output_java/pie_pastel.png +0 -0
  456. data/test/output_java/pie_simple.png +0 -0
  457. data/test/output_java/pie_wide.png +0 -0
  458. data/test/output_java/pie_wrapped_legend_16_400.png +0 -0
  459. data/test/output_java/pie_wrapped_legend_16_800.png +0 -0
  460. data/test/output_java/pie_wrapped_legend_30_400.png +0 -0
  461. data/test/output_java/pie_wrapped_legend_30_800.png +0 -0
  462. data/test/output_java/pie_wrapped_legend_4_400.png +0 -0
  463. data/test/output_java/pie_wrapped_legend_4_800.png +0 -0
  464. data/test/output_java/pie_wrapped_legend__400.png +0 -0
  465. data/test/output_java/pie_wrapped_legend__800.png +0 -0
  466. data/test/output_java/pie_zero.png +0 -0
  467. data/test/output_java/scatter_all_neg.png +0 -0
  468. data/test/output_java/scatter_all_neg_400.png +0 -0
  469. data/test/output_java/scatter_basic.png +0 -0
  470. data/test/output_java/scatter_many.png +0 -0
  471. data/test/output_java/scatter_no_data.png +0 -0
  472. data/test/output_java/scatter_no_data_msg.png +0 -0
  473. data/test/output_java/scatter_no_data_other.png +0 -0
  474. data/test/output_java/scatter_no_hide.png +0 -0
  475. data/test/output_java/scatter_no_labels.png +0 -0
  476. data/test/output_java/scatter_no_legend.png +0 -0
  477. data/test/output_java/scatter_no_line_markers.png +0 -0
  478. data/test/output_java/scatter_no_title.png +0 -0
  479. data/test/output_java/scatter_nothing_but_the_graph.png +0 -0
  480. data/test/output_java/scatter_pos_neg.png +0 -0
  481. data/test/output_java/scatter_pos_neg_400.png +0 -0
  482. data/test/output_java/scatter_wide_graph.png +0 -0
  483. data/test/output_java/scatter_wide_graph_small.png +0 -0
  484. data/test/output_java/scene_hazy_night.png +0 -0
  485. data/test/output_java/scene_not_hazy_day.png +0 -0
  486. data/test/output_java/scene_partly_cloudy_day.png +0 -0
  487. data/test/output_java/scene_stormy_day.png +0 -0
  488. data/test/output_java/scene_stormy_night.png +0 -0
  489. data/test/output_java/side_bar.png +0 -0
  490. data/test/output_java/side_bar_data_range.png +0 -0
  491. data/test/output_java/side_bar_labels.png +0 -0
  492. data/test/output_java/side_bar_spacing_full.png +0 -0
  493. data/test/output_java/side_bar_spacing_half.png +0 -0
  494. data/test/output_java/side_bar_spacing_none.png +0 -0
  495. data/test/output_java/side_stacked_bar_keynote.png +0 -0
  496. data/test/output_java/side_stacked_bar_keynote_small.png +0 -0
  497. data/test/output_java/side_stacked_bar_labels.png +0 -0
  498. data/test/output_java/side_stacked_bar_long_label.png +0 -0
  499. data/test/output_java/side_stacked_bar_wide.png +0 -0
  500. data/test/output_java/spider_37signals.png +0 -0
  501. data/test/output_java/spider_equal.png +0 -0
  502. data/test/output_java/spider_keynote.png +0 -0
  503. data/test/output_java/spider_legend.png +0 -0
  504. data/test/output_java/spider_legend_small.png +0 -0
  505. data/test/output_java/spider_lots_of_data.png +0 -0
  506. data/test/output_java/spider_lots_of_data_normal_names.png +0 -0
  507. data/test/output_java/spider_nearly_equal.png +0 -0
  508. data/test/output_java/spider_no_axes.png +0 -0
  509. data/test/output_java/spider_no_background.png +0 -0
  510. data/test/output_java/spider_no_print.png +0 -0
  511. data/test/output_java/spider_overlay_1.png +0 -0
  512. data/test/output_java/spider_overlay_2.png +0 -0
  513. data/test/output_java/spider_rotation.png +0 -0
  514. data/test/output_java/spider_small.png +0 -0
  515. data/test/output_java/spider_wide.png +0 -0
  516. data/test/output_java/spider_zero.png +0 -0
  517. data/test/output_java/stacked_area_keynote.png +0 -0
  518. data/test/output_java/stacked_area_keynote_small.png +0 -0
  519. data/test/output_java/stacked_bar_keynote.png +0 -0
  520. data/test/output_java/stacked_bar_keynote_small.png +0 -0
  521. data/test/test_accumulator_bar.rb +7 -6
  522. data/test/test_area.rb +2 -2
  523. data/test/test_bar.rb +296 -112
  524. data/test/test_bezier.rb +33 -0
  525. data/test/test_bullet.rb +26 -26
  526. data/test/test_dot.rb +44 -54
  527. data/test/test_legend.rb +29 -29
  528. data/test/test_line.rb +250 -218
  529. data/test/test_net.rb +2 -1
  530. data/test/test_pie.rb +2 -2
  531. data/test/test_scatter.rb +85 -85
  532. data/test/test_side_bar.rb +13 -0
  533. data/test/test_sidestacked_bar.rb +16 -0
  534. data/test/test_spider.rb +11 -1
  535. data/test/test_stacked_area.rb +5 -5
  536. metadata +1041 -49
  537. data/.rvmrc +0 -5
  538. data/Gemfile.lock +0 -20
  539. data/LICENSE.txt +0 -22
  540. data/README.txt +0 -43
data/gruff.gemspec CHANGED
@@ -1,33 +1,36 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  lib = File.expand_path('../lib', __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'date'
4
5
  require 'gruff/version'
5
6
 
6
7
  Gem::Specification.new do |s|
7
8
  s.name = %q{gruff}
8
9
  s.version = Gruff::VERSION
9
-
10
10
  # s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ['Geoffrey Grosenbach', 'Uwe Kubosch']
12
- # s.date = Date.today.to_s
12
+ s.date = Date.today.to_s
13
13
  s.description = %q{Beautiful graphs for one or multiple datasets. Can be used on websites or in documents.}
14
14
  s.email = %q{boss@topfunky.com}
15
15
  # s.extra_rdoc_files = ["History.txt", "Manifest.txt", "README.txt"]
16
- # s.files = ["History.txt", "MIT-LICENSE", "Manifest.txt", "README.txt", "Rakefile", "assets/bubble.png", "assets/city_scene/background/0000.png", "assets/city_scene/background/0600.png", "assets/city_scene/background/2000.png", "assets/city_scene/clouds/cloudy.png", "assets/city_scene/clouds/partly_cloudy.png", "assets/city_scene/clouds/stormy.png", "assets/city_scene/grass/default.png", "assets/city_scene/haze/true.png", "assets/city_scene/number_sample/1.png", "assets/city_scene/number_sample/2.png", "assets/city_scene/number_sample/default.png", "assets/city_scene/sky/0000.png", "assets/city_scene/sky/0200.png", "assets/city_scene/sky/0400.png", "assets/city_scene/sky/0600.png", "assets/city_scene/sky/0800.png", "assets/city_scene/sky/1000.png", "assets/city_scene/sky/1200.png", "assets/city_scene/sky/1400.png", "assets/city_scene/sky/1500.png", "assets/city_scene/sky/1700.png", "assets/city_scene/sky/2000.png", "assets/pc306715.jpg", "assets/plastik/blue.png", "assets/plastik/green.png", "assets/plastik/red.png", "init.rb", "lib/gruff.rb", "lib/gruff/accumulator_bar.rb", "lib/gruff/area.rb", "lib/gruff/bar.rb", "lib/gruff/bar_conversion.rb", "lib/gruff/base.rb", "lib/gruff/bullet.rb", "lib/gruff/deprecated.rb", "lib/gruff/dot.rb", "lib/gruff/line.rb", "lib/gruff/mini/bar.rb", "lib/gruff/mini/legend.rb", "lib/gruff/mini/pie.rb", "lib/gruff/mini/side_bar.rb", "lib/gruff/net.rb", "lib/gruff/photo_bar.rb", "lib/gruff/pie.rb", "lib/gruff/scene.rb", "lib/gruff/side_bar.rb", "lib/gruff/side_stacked_bar.rb", "lib/gruff/spider.rb", "lib/gruff/stacked_area.rb", "lib/gruff/stacked_bar.rb", "lib/gruff/stacked_mixin.rb", "rails_generators/gruff/gruff_generator.rb", "rails_generators/gruff/templates/controller.rb", "rails_generators/gruff/templates/functional_test.rb", "test/gruff_test_case.rb", "test/test_accumulator_bar.rb", "test/test_area.rb", "test/test_bar.rb", "test/test_base.rb", "test/test_bullet.rb", "test/test_dot.rb", "test/test_legend.rb", "test/test_line.rb", "test/test_mini_bar.rb", "test/test_mini_pie.rb", "test/test_mini_side_bar.rb", "test/test_net.rb", "test/test_photo.rb", "test/test_pie.rb", "test/test_scene.rb", "test/test_side_bar.rb", "test/test_sidestacked_bar.rb", "test/test_spider.rb", "test/test_stacked_area.rb", "test/test_stacked_bar.rb"]
17
16
  s.files = `git ls-files`.split($/)
18
17
  # s.has_rdoc = true
19
18
  s.homepage = %q{http://nubyonrails.com/pages/gruff}
20
19
  # s.rdoc_options = ["--main", "README.txt"]
21
- s.require_paths = ["lib"]
20
+ s.require_paths = %w(lib)
22
21
  # s.rubyforge_project = %q{gruff}
23
22
  # s.rubygems_version = %q{1.3.1}
24
23
  s.summary = %q{Beautiful graphs for one or multiple datasets.}
25
24
  # s.test_files = ["test/test_accumulator_bar.rb", "test/test_area.rb", "test/test_bar.rb", "test/test_base.rb", "test/test_bullet.rb", "test/test_dot.rb", "test/test_legend.rb", "test/test_line.rb", "test/test_mini_bar.rb", "test/test_mini_pie.rb", "test/test_mini_side_bar.rb", "test/test_net.rb", "test/test_photo.rb", "test/test_pie.rb", "test/test_scene.rb", "test/test_side_bar.rb", "test/test_sidestacked_bar.rb", "test/test_spider.rb", "test/test_stacked_area.rb", "test/test_stacked_bar.rb"]
26
25
  s.test_files = s.files.grep(%r{^(test|spec|features)/})
27
-
28
- s.executables = s.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
29
-
30
- # s.specification_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
31
- # s.add_development_dependency(%q<hoe>, [">= 1.8.2"])
32
- # s.add_dependency(%q<rmagick>, [">= 2.12.2"])
26
+ s.executables = s.files.grep(%r{^bin/}).map { |f| File.basename(f) }
27
+ s.specification_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
28
+ if defined? JRUBY_VERSION
29
+ s.platform = 'java'
30
+ s.add_dependency 'rmagick4j'
31
+ else
32
+ s.add_dependency 'rmagick'
33
+ end
34
+ s.add_development_dependency('rake')
35
+ s.license = 'MIT'
33
36
  end
data/lib/gruff.rb CHANGED
@@ -1,17 +1,20 @@
1
- require "gruff/version"
1
+ require 'gruff/version'
2
2
 
3
3
  # Extra full path added to fix loading errors on some installations.
4
4
 
5
5
  %w(
6
+ themes
6
7
  base
7
8
  area
8
9
  bar
9
- line
10
+ bezier
11
+ bullet
10
12
  dot
11
- pie
12
- spider
13
+ line
13
14
  net
15
+ pie
14
16
  scatter
17
+ spider
15
18
  stacked_area
16
19
  stacked_bar
17
20
  side_stacked_bar
@@ -25,7 +28,5 @@ require "gruff/version"
25
28
  mini/pie
26
29
  mini/side_bar
27
30
  ).each do |filename|
28
- require File.dirname(__FILE__) + "/gruff/#{filename}"
31
+ require "gruff/#{filename}"
29
32
  end
30
-
31
- # TODO bullet
@@ -6,22 +6,13 @@ require File.dirname(__FILE__) + '/base'
6
6
  # the top bar shows the new value being added to the array.
7
7
 
8
8
  class Gruff::AccumulatorBar < Gruff::StackedBar
9
-
10
9
  def draw
11
10
  raise(Gruff::IncorrectNumberOfDatasetsException) unless @data.length == 1
12
-
13
- accumulator_array = []
14
- index = 0
15
11
 
16
- increment_array = @data.first[DATA_VALUES_INDEX].inject([]) {|memo, value|
17
- memo << ((index > 0) ? (value + memo.max) : value)
18
- accumulator_array << memo[index] - value
19
- index += 1
20
- memo
21
- }
22
- data "Accumulator", accumulator_array
23
-
12
+ accum_array = @data.first[DATA_VALUES_INDEX][0..-2].inject([0]) { |a, v| a << a.last + v}
13
+ data 'Accumulator', accum_array
14
+ set_colors
15
+ @data.reverse!
24
16
  super
25
17
  end
26
-
27
18
  end
data/lib/gruff/area.rb CHANGED
@@ -2,6 +2,10 @@
2
2
  require File.dirname(__FILE__) + '/base'
3
3
 
4
4
  class Gruff::Area < Gruff::Base
5
+ def initialize(*)
6
+ super
7
+ @sorted_drawing = true
8
+ end
5
9
 
6
10
  def draw
7
11
  super
@@ -21,19 +25,8 @@ class Gruff::Area < Gruff::Base
21
25
  new_x = @graph_left + (@x_increment * index)
22
26
  new_y = @graph_top + (@graph_height - data_point * @graph_height)
23
27
 
24
- if prev_x > 0 and prev_y > 0 then
25
- poly_points << new_x
26
- poly_points << new_y
27
-
28
- #@d = @d.polyline(prev_x, prev_y, new_x, new_y)
29
- else
30
- poly_points << @graph_left
31
- poly_points << @graph_bottom - 1
32
- poly_points << new_x
33
- poly_points << new_y
34
-
35
- #@d = @d.polyline(@graph_left, @graph_bottom, new_x, new_y)
36
- end
28
+ poly_points << new_x
29
+ poly_points << new_y
37
30
 
38
31
  draw_label(new_x, index)
39
32
 
data/lib/gruff/bar.rb CHANGED
@@ -5,7 +5,12 @@ class Gruff::Bar < Gruff::Base
5
5
 
6
6
  # Spacing factor applied between bars
7
7
  attr_accessor :bar_spacing
8
-
8
+
9
+ def initialize(*args)
10
+ super
11
+ @spacing_factor = 0.9
12
+ end
13
+
9
14
  def draw
10
15
  # Labels will be centered over the left of the bar if
11
16
  # there are more labels than columns. This is basically the same
@@ -18,13 +23,24 @@ class Gruff::Bar < Gruff::Base
18
23
  draw_bars
19
24
  end
20
25
 
26
+ # Can be used to adjust the spaces between the bars.
27
+ # Accepts values between 0.00 and 1.00 where 0.00 means no spacing at all
28
+ # and 1 means that each bars' width is nearly 0 (so each bar is a simple
29
+ # line with no x dimension).
30
+ #
31
+ # Default value is 0.9.
32
+ def spacing_factor=(space_percent)
33
+ raise ArgumentError, 'spacing_factor must be between 0.00 and 1.00' unless (space_percent >= 0 and space_percent <= 1)
34
+ @spacing_factor = (1 - space_percent)
35
+ end
36
+
21
37
  protected
22
38
 
23
39
  def draw_bars
24
40
  # Setup spacing.
25
41
  #
26
42
  # Columns sit side-by-side.
27
- @bar_spacing ||= 0.9 # space between the bars
43
+ @bar_spacing ||= @spacing_factor # space between the bars
28
44
  @bar_width = @graph_width / (@column_count * @data.length).to_f
29
45
  padding = (@bar_width * (1 - @bar_spacing)) / 2
30
46
 
@@ -62,7 +78,7 @@ protected
62
78
  right_x = left_x + @bar_width * @bar_spacing
63
79
  # y
64
80
  conv = []
65
- conversion.getLeftYRightYscaled( data_point, conv )
81
+ conversion.get_left_y_right_y_scaled( data_point, conv )
66
82
 
67
83
  # create new bar
68
84
  @d = @d.fill data_row[DATA_COLOR_INDEX]
@@ -72,8 +88,13 @@ protected
72
88
  label_center = @graph_left +
73
89
  (@data.length * @bar_width * point_index) +
74
90
  (@data.length * @bar_width / 2.0)
91
+
75
92
  # Subtract half a bar width to center left if requested
76
93
  draw_label(label_center - (@center_labels_over_point ? @bar_width / 2.0 : 0.0), point_index)
94
+ if @show_labels_for_bar_values
95
+ val = (@label_formatting || '%.2f') % @norm_data[row_index][3][point_index]
96
+ draw_value_label(left_x + (right_x - left_x)/2, conv[0]-30, val.commify, true)
97
+ end
77
98
  end
78
99
 
79
100
  end
@@ -10,37 +10,37 @@
10
10
  # 3. Bars either go from zero to positive or from zero to negative
11
11
  #
12
12
  class Gruff::BarConversion
13
- attr_writer :mode
14
- attr_writer :zero
15
- attr_writer :graph_top
16
- attr_writer :graph_height
17
- attr_writer :minimum_value
18
- attr_writer :spread
19
-
20
- def getLeftYRightYscaled( data_point, result )
21
- case @mode
22
- when 1 then # Case one
23
- # minimum value >= 0 ( only positiv values )
13
+ attr_writer :mode
14
+ attr_writer :zero
15
+ attr_writer :graph_top
16
+ attr_writer :graph_height
17
+ attr_writer :minimum_value
18
+ attr_writer :spread
19
+
20
+ def get_left_y_right_y_scaled(data_point, result)
21
+ case @mode
22
+ when 1 then # Case one
23
+ # minimum value >= 0 ( only positiv values )
24
24
  result[0] = @graph_top + @graph_height*(1 - data_point) + 1
25
- result[1] = @graph_top + @graph_height - 1
26
- when 2 then # Case two
27
- # only negativ values
28
- result[0] = @graph_top + 1
29
- result[1] = @graph_top + @graph_height*(1 - data_point) - 1
30
- when 3 then # Case three
31
- # positiv and negativ values
32
- val = data_point-@minimum_value/@spread
33
- if ( data_point >= @zero ) then
34
- result[0] = @graph_top + @graph_height*(1 - (val-@zero)) + 1
35
- result[1] = @graph_top + @graph_height*(1 - @zero) - 1
36
- else
37
- result[0] = @graph_top + @graph_height*(1 - (val-@zero)) + 1
38
- result[1] = @graph_top + @graph_height*(1 - @zero) - 1
39
- end
40
- else
41
- result[0] = 0.0
42
- result[1] = 0.0
43
- end
44
- end
25
+ result[1] = @graph_top + @graph_height - 1
26
+ when 2 then # Case two
27
+ # only negativ values
28
+ result[0] = @graph_top + 1
29
+ result[1] = @graph_top + @graph_height*(1 - data_point) - 1
30
+ when 3 then # Case three
31
+ # positiv and negativ values
32
+ val = data_point-@minimum_value/@spread
33
+ if data_point >= @zero
34
+ result[0] = @graph_top + @graph_height*(1 - (val-@zero)) + 1
35
+ result[1] = @graph_top + @graph_height*(1 - @zero) - 1
36
+ else
37
+ result[0] = @graph_top + @graph_height*(1 - (val-@zero)) + 1
38
+ result[1] = @graph_top + @graph_height*(1 - @zero) - 1
39
+ end
40
+ else
41
+ result[0] = 0.0
42
+ result[1] = 0.0
43
+ end
44
+ end
45
45
 
46
46
  end
data/lib/gruff/base.rb CHANGED
@@ -87,6 +87,19 @@ module Gruff
87
87
  # Manually set increment of the horizontal marking lines
88
88
  attr_accessor :y_axis_increment
89
89
 
90
+ # Height of staggering between labels (Bar graph only)
91
+ attr_accessor :label_stagger_height
92
+
93
+ # Truncates labels if longer than max specified
94
+ attr_accessor :label_max_size
95
+
96
+ # How truncated labels visually appear if they exceed label_max_size
97
+ # :absolute - does not show trailing dots to indicate truncation. This is
98
+ # the default.
99
+ # :trailing_dots - shows trailing dots to indicate truncation (note
100
+ # that label_max_size must be greater than 3).
101
+ attr_accessor :label_truncation_style
102
+
90
103
  # Get or set the list of colors that will be used to draw the bars or lines.
91
104
  attr_accessor :colors
92
105
 
@@ -128,14 +141,18 @@ module Gruff
128
141
  # Optionally set the size of the font. Based on an 800x600px graph.
129
142
  # Default is 20.
130
143
  #
131
- # Will be scaled down if graph is smaller than 800px wide.
144
+ # Will be scaled down if the graph is smaller than 800px wide.
132
145
  attr_accessor :legend_font_size
133
146
 
147
+ # Display the legend under the graph
148
+ attr_accessor :legend_at_bottom
149
+
134
150
  # The font size of the labels around the graph
135
151
  attr_accessor :marker_font_size
136
152
 
137
153
  # The color of the auxiliary lines
138
154
  attr_accessor :marker_color
155
+ attr_accessor :marker_shadow_color
139
156
 
140
157
  # The number of horizontal lines shown for reference
141
158
  attr_accessor :marker_count
@@ -153,10 +170,14 @@ module Gruff
153
170
  # the graph object.
154
171
  attr_accessor :maximum_value
155
172
 
156
- # Set to false if you don't want the data to be sorted with largest avg
157
- # values at the back.
173
+ # Set to true if you want the data sets sorted with largest avg values drawn
174
+ # first.
158
175
  attr_accessor :sort
159
176
 
177
+ # Set to true if you want the data sets drawn with largest avg values drawn
178
+ # first. This does not affect the legend.
179
+ attr_accessor :sorted_drawing
180
+
160
181
  # Experimental
161
182
  attr_accessor :additional_line_values
162
183
 
@@ -169,6 +190,17 @@ module Gruff
169
190
  # Will be scaled down if graph is smaller than 800px wide.
170
191
  attr_accessor :legend_box_size
171
192
 
193
+ # Output the values for the bars on a bar graph
194
+ # Default is false
195
+ attr_accessor :show_labels_for_bar_values
196
+
197
+ # Set the number output format for labels using sprintf
198
+ # Default is "%.2f"
199
+ attr_accessor :label_formatting
200
+
201
+ # With Side Bars use the data label for the marker value to the left of the bar
202
+ # Default is false
203
+ attr_accessor :use_data_label
172
204
  # If one numerical argument is given, the graph is drawn at 4/3 ratio
173
205
  # according to the given width (800 results in 800x600, 400 gives 400x300,
174
206
  # etc.).
@@ -178,19 +210,19 @@ module Gruff
178
210
  # Looks for Bitstream Vera as the default font. Expects an environment var
179
211
  # of MAGICK_FONT_PATH to be set. (Uses RMagick's default font otherwise.)
180
212
  def initialize(target_width=DEFAULT_TARGET_WIDTH)
181
- if not Numeric === target_width
213
+ if Numeric === target_width
214
+ @columns = target_width.to_f
215
+ @rows = target_width.to_f * 0.75
216
+ else
182
217
  geometric_width, geometric_height = target_width.split('x')
183
218
  @columns = geometric_width.to_f
184
219
  @rows = geometric_height.to_f
185
- else
186
- @columns = target_width.to_f
187
- @rows = target_width.to_f * 0.75
188
220
  end
189
221
 
190
222
  initialize_ivars
191
223
 
192
224
  reset_themes
193
- theme_keynote
225
+ self.theme = Themes::KEYNOTE
194
226
  end
195
227
 
196
228
  # Set instance variables for this object.
@@ -210,7 +242,7 @@ module Gruff
210
242
  @data = Array.new
211
243
  @labels = Hash.new
212
244
  @labels_seen = Hash.new
213
- @sort = true
245
+ @sort = false
214
246
  @title = nil
215
247
 
216
248
  @scale = @columns / @raw_columns
@@ -228,11 +260,14 @@ module Gruff
228
260
 
229
261
  @legend_box_size = 20.0
230
262
 
231
- @no_data_message = "No Data"
263
+ @no_data_message = 'No Data'
232
264
 
233
- @hide_line_markers = @hide_legend = @hide_title = @hide_line_numbers = false
265
+ @hide_line_markers = @hide_legend = @hide_title = @hide_line_numbers = @legend_at_bottom = @show_labels_for_bar_values = false
234
266
  @center_labels_over_point = true
235
267
  @has_left_labels = false
268
+ @label_stagger_height = 0
269
+ @label_max_size = 0
270
+ @label_truncation_style = :absolute
236
271
 
237
272
  @additional_line_values = []
238
273
  @additional_line_colors = []
@@ -288,7 +323,7 @@ module Gruff
288
323
  # graph.theme = {
289
324
  # :colors => %w(orange purple green white red),
290
325
  # :marker_color => 'blue',
291
- # :background_colors => %w(black grey)
326
+ # :background_colors => ['black', 'grey', :top_bottom]
292
327
  # }
293
328
  #
294
329
  # :background_image => 'squirrel.png' is also possible.
@@ -296,146 +331,50 @@ module Gruff
296
331
  # (Or hopefully something better looking than that.)
297
332
  #
298
333
  def theme=(options)
299
- reset_themes()
334
+ reset_themes
300
335
 
301
336
  defaults = {
302
- :colors => ['black', 'white'],
303
- :additional_line_colors => [],
304
- :marker_color => 'white',
305
- :font_color => 'black',
306
- :background_colors => nil,
307
- :background_image => nil
337
+ :colors => %w(black white),
338
+ :additional_line_colors => [],
339
+ :marker_color => 'white',
340
+ :marker_shadow_color => nil,
341
+ :font_color => 'black',
342
+ :background_colors => nil,
343
+ :background_image => nil
308
344
  }
309
345
  @theme_options = defaults.merge options
310
346
 
311
347
  @colors = @theme_options[:colors]
312
348
  @marker_color = @theme_options[:marker_color]
349
+ @marker_shadow_color = @theme_options[:marker_shadow_color]
313
350
  @font_color = @theme_options[:font_color] || @marker_color
314
351
  @additional_line_colors = @theme_options[:additional_line_colors]
315
352
 
316
353
  render_background
317
354
  end
318
355
 
319
- # A color scheme similar to the popular presentation software.
320
356
  def theme_keynote
321
- # Colors
322
- @blue = '#6886B4'
323
- @yellow = '#FDD84E'
324
- @green = '#72AE6E'
325
- @red = '#D1695E'
326
- @purple = '#8A6EAF'
327
- @orange = '#EFAA43'
328
- @white = 'white'
329
- @colors = [@yellow, @blue, @green, @red, @purple, @orange, @white]
330
-
331
- self.theme = {
332
- :colors => @colors,
333
- :marker_color => 'white',
334
- :font_color => 'white',
335
- :background_colors => ['black', '#4a465a']
336
- }
357
+ self.theme = Themes::KEYNOTE
337
358
  end
338
359
 
339
- # A color scheme plucked from the colors on the popular usability blog.
340
360
  def theme_37signals
341
- # Colors
342
- @green = '#339933'
343
- @purple = '#cc99cc'
344
- @blue = '#336699'
345
- @yellow = '#FFF804'
346
- @red = '#ff0000'
347
- @orange = '#cf5910'
348
- @black = 'black'
349
- @colors = [@yellow, @blue, @green, @red, @purple, @orange, @black]
350
-
351
- self.theme = {
352
- :colors => @colors,
353
- :marker_color => 'black',
354
- :font_color => 'black',
355
- :background_colors => ['#d1edf5', 'white']
356
- }
361
+ self.theme = Themes::THIRTYSEVEN_SIGNALS
357
362
  end
358
363
 
359
- # A color scheme from the colors used on the 2005 Rails keynote
360
- # presentation at RubyConf.
361
364
  def theme_rails_keynote
362
- # Colors
363
- @green = '#00ff00'
364
- @grey = '#333333'
365
- @orange = '#ff5d00'
366
- @red = '#f61100'
367
- @white = 'white'
368
- @light_grey = '#999999'
369
- @black = 'black'
370
- @colors = [@green, @grey, @orange, @red, @white, @light_grey, @black]
371
-
372
- self.theme = {
373
- :colors => @colors,
374
- :marker_color => 'white',
375
- :font_color => 'white',
376
- :background_colors => ['#0083a3', '#0083a3']
377
- }
365
+ self.theme = Themes::RAILS_KEYNOTE
378
366
  end
379
367
 
380
- # A color scheme similar to that used on the popular podcast site.
381
368
  def theme_odeo
382
- # Colors
383
- @grey = '#202020'
384
- @white = 'white'
385
- @dark_pink = '#a21764'
386
- @green = '#8ab438'
387
- @light_grey = '#999999'
388
- @dark_blue = '#3a5b87'
389
- @black = 'black'
390
- @colors = [@grey, @white, @dark_blue, @dark_pink, @green, @light_grey, @black]
391
-
392
- self.theme = {
393
- :colors => @colors,
394
- :marker_color => 'white',
395
- :font_color => 'white',
396
- :background_colors => ['#ff47a4', '#ff1f81']
397
- }
369
+ self.theme = Themes::ODEO
398
370
  end
399
371
 
400
- # A pastel theme
401
372
  def theme_pastel
402
- # Colors
403
- @colors = [
404
- '#a9dada', # blue
405
- '#aedaa9', # green
406
- '#daaea9', # peach
407
- '#dadaa9', # yellow
408
- '#a9a9da', # dk purple
409
- '#daaeda', # purple
410
- '#dadada' # grey
411
- ]
412
-
413
- self.theme = {
414
- :colors => @colors,
415
- :marker_color => '#aea9a9', # Grey
416
- :font_color => 'black',
417
- :background_colors => 'white'
418
- }
373
+ self.theme = Themes::PASTEL
419
374
  end
420
375
 
421
- # A greyscale theme
422
376
  def theme_greyscale
423
- # Colors
424
- @colors = [
425
- '#282828', #
426
- '#383838', #
427
- '#686868', #
428
- '#989898', #
429
- '#c8c8c8', #
430
- '#e8e8e8', #
431
- ]
432
-
433
- self.theme = {
434
- :colors => @colors,
435
- :marker_color => '#aea9a9', # Grey
436
- :font_color => 'black',
437
- :background_colors => 'white'
438
- }
377
+ self.theme = Themes::GREYSCALE
439
378
  end
440
379
 
441
380
  # Parameters are an array where the first element is the name of the dataset
@@ -454,12 +393,12 @@ module Gruff
454
393
  # data("Bart S.", [95, 45, 78, 89, 88, 76], '#ffcc00')
455
394
  def data(name, data_points=[], color=nil)
456
395
  data_points = Array(data_points) # make sure it's an array
457
- @data << [name, data_points, (color || increment_color)]
458
- # Set column count if this is larger than previous counts
396
+ @data << [name, data_points, color]
397
+ # Set column count if this is larger than previous counts
459
398
  @column_count = (data_points.length > @column_count) ? data_points.length : @column_count
460
399
 
461
400
  # Pre-normalize
462
- data_points.each_with_index do |data_point, index|
401
+ data_points.each do |data_point|
463
402
  next if data_point.nil?
464
403
 
465
404
  # Setup max/min so spread starts at the low end of the data points
@@ -481,59 +420,70 @@ module Gruff
481
420
  #
482
421
  # Example:
483
422
  # write('graphs/my_pretty_graph.png')
484
- def write(filename="graph.png")
485
- draw()
423
+ def write(filename='graph.png')
424
+ draw
486
425
  @base_image.write(filename)
487
426
  end
488
427
 
489
428
  # Return the graph as a rendered binary blob.
490
429
  def to_blob(fileformat='PNG')
491
- draw()
492
- return @base_image.to_blob do
430
+ draw
431
+ @base_image.to_blob do
493
432
  self.format = fileformat
494
433
  end
495
434
  end
496
435
 
497
436
 
498
-
499
437
  protected
500
438
 
501
439
  # Overridden by subclasses to do the actual plotting of the graph.
502
440
  #
503
441
  # Subclasses should start by calling super() for this method.
504
442
  def draw
505
- make_stacked if @stacked
443
+ # Maybe should be done in one of the following functions for more granularity.
444
+ unless @has_data
445
+ draw_no_data
446
+ return
447
+ end
448
+
449
+ setup_data
506
450
  setup_drawing
507
451
 
508
452
  debug {
509
453
  # Outer margin
510
- @d.rectangle( @left_margin, @top_margin,
511
- @raw_columns - @right_margin, @raw_rows - @bottom_margin)
454
+ @d.rectangle(@left_margin, @top_margin,
455
+ @raw_columns - @right_margin, @raw_rows - @bottom_margin)
512
456
  # Graph area box
513
- @d.rectangle( @graph_left, @graph_top, @graph_right, @graph_bottom)
457
+ @d.rectangle(@graph_left, @graph_top, @graph_right, @graph_bottom)
514
458
  }
459
+
460
+ draw_legend
461
+ draw_line_markers
462
+ draw_axis_labels
463
+ draw_title
464
+ end
465
+
466
+ # Perform data manipulation before calculating chart measurements
467
+ def setup_data # :nodoc:
468
+ if @y_axis_increment && !@hide_line_markers
469
+ @maximum_value = [@y_axis_increment, @maximum_value, (@maximum_value.to_f / @y_axis_increment).round * @y_axis_increment].max
470
+ @minimum_value = [@minimum_value, (@minimum_value.to_f / @y_axis_increment).round * @y_axis_increment].min
471
+ end
472
+ make_stacked if @stacked
515
473
  end
516
474
 
517
- # Calculates size of drawable area and draws the decorations.
475
+ # Calculates size of drawable area and generates normalized data.
518
476
  #
519
477
  # * line markers
520
478
  # * legend
521
479
  # * title
522
480
  def setup_drawing
523
- # Maybe should be done in one of the following functions for more granularity.
524
- unless @has_data
525
- draw_no_data()
526
- return
527
- end
528
-
529
- normalize()
530
- setup_graph_measurements()
531
- sort_norm_data() if @sort # Sort norm_data with avg largest values set first (for display)
532
-
533
- draw_legend()
534
- draw_line_markers()
535
- draw_axis_labels()
536
- draw_title
481
+ calculate_spread
482
+ sort_data if @sort # Sort data with avg largest values set first (for display)
483
+ set_colors
484
+ normalize
485
+ setup_graph_measurements
486
+ sort_norm_data if @sorted_drawing # Sort norm_data with avg largest values set first (for display)
537
487
  end
538
488
 
539
489
  # Make copy of data with values scaled between 0-100
@@ -542,18 +492,20 @@ module Gruff
542
492
  @norm_data = []
543
493
  return unless @has_data
544
494
 
545
- calculate_spread
546
-
547
495
  @data.each do |data_row|
548
496
  norm_data_points = []
549
497
  data_row[DATA_VALUES_INDEX].each do |data_point|
550
498
  if data_point.nil?
551
499
  norm_data_points << nil
552
500
  else
553
- norm_data_points << ((data_point.to_f - @minimum_value.to_f ) / @spread)
501
+ norm_data_points << ((data_point.to_f - @minimum_value.to_f) / @spread)
554
502
  end
555
503
  end
556
- @norm_data << [data_row[DATA_LABEL_INDEX], norm_data_points, data_row[DATA_COLOR_INDEX]]
504
+ if @show_labels_for_bar_values
505
+ @norm_data << [data_row[DATA_LABEL_INDEX], norm_data_points, data_row[DATA_COLOR_INDEX], data_row[DATA_VALUES_INDEX]]
506
+ else
507
+ @norm_data << [data_row[DATA_LABEL_INDEX], norm_data_points, data_row[DATA_COLOR_INDEX]]
508
+ end
557
509
  end
558
510
  end
559
511
  end
@@ -568,45 +520,44 @@ module Gruff
568
520
 
569
521
  def setup_graph_measurements
570
522
  @marker_caps_height = @hide_line_markers ? 0 :
571
- calculate_caps_height(@marker_font_size)
523
+ calculate_caps_height(@marker_font_size)
572
524
  @title_caps_height = @hide_title ? 0 :
573
- calculate_caps_height(@title_font_size)
525
+ calculate_caps_height(@title_font_size)
574
526
  @legend_caps_height = @hide_legend ? 0 :
575
- calculate_caps_height(@legend_font_size)
527
+ calculate_caps_height(@legend_font_size)
576
528
 
577
529
  if @hide_line_markers
578
530
  (@graph_left,
579
- @graph_right_margin,
580
- @graph_bottom_margin) = [@left_margin, @right_margin, @bottom_margin]
531
+ @graph_right_margin,
532
+ @graph_bottom_margin) = [@left_margin, @right_margin, @bottom_margin]
581
533
  else
582
- longest_left_label_width = 0
583
534
  if @has_left_labels
584
- longest_left_label_width = calculate_width(@marker_font_size,
585
- labels.values.inject('') { |value, memo| (value.to_s.length > memo.to_s.length) ? value : memo }) * 1.25
535
+ longest_left_label_width = calculate_width(@marker_font_size,
536
+ labels.values.inject('') { |value, memo| (value.to_s.length > memo.to_s.length) ? value : memo }) * 1.25
586
537
  else
587
538
  longest_left_label_width = calculate_width(@marker_font_size,
588
- label(@maximum_value.to_f))
539
+ label(@maximum_value.to_f, @increment))
589
540
  end
590
541
 
591
542
  # Shift graph if left line numbers are hidden
592
543
  line_number_width = @hide_line_numbers && !@has_left_labels ?
593
- 0.0 :
594
- (longest_left_label_width + LABEL_MARGIN * 2)
544
+ 0.0 :
545
+ (longest_left_label_width + LABEL_MARGIN * 2)
595
546
 
596
547
  @graph_left = @left_margin +
597
- line_number_width +
598
- (@y_axis_label.nil? ? 0.0 : @marker_caps_height + LABEL_MARGIN * 2)
548
+ line_number_width +
549
+ (@y_axis_label.nil? ? 0.0 : @marker_caps_height + LABEL_MARGIN * 2)
599
550
 
600
551
  # Make space for half the width of the rightmost column label.
601
552
  # Might be greater than the number of columns if between-style bar markers are used.
602
553
  last_label = @labels.keys.sort.last.to_i
603
554
  extra_room_for_long_label = (last_label >= (@column_count-1) && @center_labels_over_point) ?
604
- calculate_width(@marker_font_size, @labels[last_label]) / 2.0 :
605
- 0
555
+ calculate_width(@marker_font_size, @labels[last_label]) / 2.0 :
556
+ 0
606
557
  @graph_right_margin = @right_margin + extra_room_for_long_label
607
558
 
608
559
  @graph_bottom_margin = @bottom_margin +
609
- @marker_caps_height + LABEL_MARGIN
560
+ @marker_caps_height + LABEL_MARGIN
610
561
  end
611
562
 
612
563
  @graph_right = @raw_columns - @graph_right_margin
@@ -614,13 +565,14 @@ module Gruff
614
565
 
615
566
  # When @hide title, leave a title_margin space for aesthetics.
616
567
  # Same with @hide_legend
617
- @graph_top = @top_margin +
618
- (@hide_title ? title_margin : @title_caps_height + title_margin ) +
619
- (@hide_legend ? legend_margin : @legend_caps_height + legend_margin)
568
+ @graph_top = @legend_at_bottom ? @top_margin : (@top_margin +
569
+ (@hide_title ? title_margin : @title_caps_height + title_margin) +
570
+ (@hide_legend ? legend_margin : @legend_caps_height + legend_margin))
620
571
 
621
572
  x_axis_label_height = @x_axis_label.nil? ? 0.0 :
622
- @marker_caps_height + LABEL_MARGIN
623
- @graph_bottom = @raw_rows - @graph_bottom_margin - x_axis_label_height
573
+ @marker_caps_height + LABEL_MARGIN
574
+ # FIXME: Consider chart types other than bar
575
+ @graph_bottom = @raw_rows - @graph_bottom_margin - x_axis_label_height - @label_stagger_height
624
576
  @graph_height = @graph_bottom - @graph_top
625
577
  end
626
578
 
@@ -638,22 +590,22 @@ module Gruff
638
590
  @d.stroke('transparent')
639
591
  @d.pointsize = scale_fontsize(@marker_font_size)
640
592
  @d.gravity = NorthGravity
641
- @d = @d.annotate_scaled( @base_image,
642
- @raw_columns, 1.0,
643
- 0.0, x_axis_label_y_coordinate,
644
- @x_axis_label, @scale)
593
+ @d = @d.annotate_scaled(@base_image,
594
+ @raw_columns, 1.0,
595
+ 0.0, x_axis_label_y_coordinate,
596
+ @x_axis_label, @scale)
645
597
  debug { @d.line 0.0, x_axis_label_y_coordinate, @raw_columns, x_axis_label_y_coordinate }
646
598
  end
647
599
 
648
600
  unless @y_axis_label.nil?
649
601
  # Y Axis, rotated vertically
650
- @d.rotation = 90.0
651
- @d.gravity = CenterGravity
652
- @d = @d.annotate_scaled( @base_image,
653
- 1.0, @raw_rows,
654
- @left_margin + @marker_caps_height / 2.0, 0.0,
655
- @y_axis_label, @scale)
656
602
  @d.rotation = -90.0
603
+ @d.gravity = CenterGravity
604
+ @d = @d.annotate_scaled(@base_image,
605
+ 1.0, @raw_rows,
606
+ @left_margin + @marker_caps_height / 2.0, 0.0,
607
+ @y_axis_label, @scale)
608
+ @d.rotation = 90.0
657
609
  end
658
610
  end
659
611
 
@@ -676,14 +628,9 @@ module Gruff
676
628
  end
677
629
  @marker_count ||= 4
678
630
  end
679
- @increment = (@spread > 0) ? significant(@spread / @marker_count) : 1
631
+ @increment = (@spread > 0 && @marker_count > 0) ? significant(@spread / @marker_count) : 1
680
632
  else
681
633
  # TODO Make this work for negative values
682
- @maximum_value = [@maximum_value.ceil, @y_axis_increment].max
683
- @minimum_value = @minimum_value.floor
684
- calculate_spread
685
- normalize(true)
686
-
687
634
  @marker_count = (@spread / @y_axis_increment).to_i
688
635
  @increment = @y_axis_increment
689
636
  end
@@ -694,10 +641,23 @@ module Gruff
694
641
  y = @graph_top + @graph_height - index.to_f * @increment_scaled
695
642
 
696
643
  @d = @d.fill(@marker_color)
644
+
645
+ # FIXME(uwe): Workaround for Issue #66
646
+ # https://github.com/topfunky/gruff/issues/66
647
+ # https://github.com/rmagick/rmagick/issues/82
648
+ # Remove if the issue gets fixed.
649
+ y += 0.001 unless defined?(JRUBY_VERSION)
650
+ # EMXIF
651
+
697
652
  @d = @d.line(@graph_left, y, @graph_right, y)
653
+ #If the user specified a marker shadow color, draw a shadow just below it
654
+ unless @marker_shadow_color.nil?
655
+ @d = @d.fill(@marker_shadow_color)
656
+ @d = @d.line(@graph_left, y + 1, @graph_right, y + 1)
657
+ end
698
658
 
699
659
  marker_label = BigDecimal(index.to_s) * BigDecimal(@increment.to_s) +
700
- BigDecimal(@minimum_value.to_s)
660
+ BigDecimal(@minimum_value.to_s)
701
661
 
702
662
  unless @hide_line_numbers
703
663
  @d.fill = @font_color
@@ -707,10 +667,10 @@ module Gruff
707
667
  @d.gravity = EastGravity
708
668
 
709
669
  # Vertically center with 1.0 for the height
710
- @d = @d.annotate_scaled( @base_image,
711
- @graph_left - LABEL_MARGIN, 1.0,
712
- 0.0, y,
713
- label(marker_label), @scale)
670
+ @d = @d.annotate_scaled(@base_image,
671
+ @graph_left - LABEL_MARGIN, 1.0,
672
+ 0.0, y,
673
+ label(marker_label, @increment), @scale)
714
674
  end
715
675
  end
716
676
 
@@ -763,7 +723,7 @@ module Gruff
763
723
  def draw_legend
764
724
  return if @hide_legend
765
725
 
766
- @legend_labels = @data.collect {|item| item[DATA_LABEL_INDEX] }
726
+ @legend_labels = @data.collect { |item| item[DATA_LABEL_INDEX] }
767
727
 
768
728
  legend_square_width = @legend_box_size # small square with color of this item
769
729
 
@@ -783,9 +743,9 @@ module Gruff
783
743
  end
784
744
 
785
745
  current_x_offset = center(sum(label_widths.first))
786
- current_y_offset = @hide_title ?
787
- @top_margin + title_margin :
788
- @top_margin + title_margin + @title_caps_height
746
+ current_y_offset = @legend_at_bottom ? @graph_height + title_margin : (@hide_title ?
747
+ @top_margin + title_margin :
748
+ @top_margin + title_margin + @title_caps_height)
789
749
 
790
750
  @legend_labels.each_with_index do |legend_label, index|
791
751
 
@@ -796,10 +756,10 @@ module Gruff
796
756
  @d.stroke('transparent')
797
757
  @d.font_weight = NormalWeight
798
758
  @d.gravity = WestGravity
799
- @d = @d.annotate_scaled( @base_image,
800
- @raw_columns, 1.0,
801
- current_x_offset + (legend_square_width * 1.7), current_y_offset,
802
- legend_label.to_s, @scale)
759
+ @d = @d.annotate_scaled(@base_image,
760
+ @raw_columns, 1.0,
761
+ current_x_offset + (legend_square_width * 1.7), current_y_offset,
762
+ legend_label.to_s, @scale)
803
763
 
804
764
  # Now draw box with color of this dataset
805
765
  @d = @d.stroke('transparent')
@@ -844,10 +804,10 @@ module Gruff
844
804
  @d.pointsize = scale_fontsize(@title_font_size)
845
805
  @d.font_weight = BoldWeight
846
806
  @d.gravity = NorthGravity
847
- @d = @d.annotate_scaled( @base_image,
848
- @raw_columns, 1.0,
849
- 0, @top_margin,
850
- @title, @scale)
807
+ @d = @d.annotate_scaled(@base_image,
808
+ @raw_columns, 1.0,
809
+ 0, @top_margin,
810
+ @title, @scale)
851
811
  end
852
812
 
853
813
  # Draws column labels below graph, centered over x_offset
@@ -859,21 +819,64 @@ module Gruff
859
819
  if !@labels[index].nil? && @labels_seen[index].nil?
860
820
  y_offset = @graph_bottom + LABEL_MARGIN
861
821
 
862
- @d.fill = @font_color
863
- @d.font = @font if @font
864
- @d.stroke('transparent')
865
- @d.font_weight = NormalWeight
866
- @d.pointsize = scale_fontsize(@marker_font_size)
867
- @d.gravity = NorthGravity
868
- @d = @d.annotate_scaled(@base_image,
869
- 1.0, 1.0,
870
- x_offset, y_offset,
871
- @labels[index], @scale)
822
+ # TESTME
823
+ # FIXME: Consider chart types other than bar
824
+ # TODO: See if index.odd? is the best stragegy
825
+ y_offset += @label_stagger_height if index.odd?
826
+
827
+ label_text = @labels[index]
828
+
829
+ # TESTME
830
+ # FIXME: Consider chart types other than bar
831
+ if label_text.size > @label_max_size
832
+ if @label_truncation_style == :trailing_dots
833
+ if @label_max_size > 3
834
+ # 4 because '...' takes up 3 chars
835
+ label_text = "#{label_text[0 .. (@label_max_size - 4)]}..."
836
+ end
837
+ else # @label_truncation_style is :absolute (default)
838
+ label_text = label_text[0 .. (@label_max_size - 1)]
839
+ end
840
+
841
+ end
842
+
843
+ if x_offset >= @graph_left && x_offset <= @graph_right
844
+ @d.fill = @font_color
845
+ @d.font = @font if @font
846
+ @d.stroke('transparent')
847
+ @d.font_weight = NormalWeight
848
+ @d.pointsize = scale_fontsize(@marker_font_size)
849
+ @d.gravity = NorthGravity
850
+ @d = @d.annotate_scaled(@base_image,
851
+ 1.0, 1.0,
852
+ x_offset, y_offset,
853
+ label_text, @scale)
854
+ end
872
855
  @labels_seen[index] = 1
873
856
  debug { @d.line 0.0, y_offset, @raw_columns, y_offset }
874
857
  end
875
858
  end
876
859
 
860
+ # Draws the data value over the data point in bar graphs
861
+ def draw_value_label(x_offset, y_offset, data_point, bar_value=false)
862
+ return if @hide_line_markers && !bar_value
863
+
864
+ #y_offset = @graph_bottom + LABEL_MARGIN
865
+
866
+ @d.fill = @font_color
867
+ @d.font = @font if @font
868
+ @d.stroke('transparent')
869
+ @d.font_weight = NormalWeight
870
+ @d.pointsize = scale_fontsize(@marker_font_size)
871
+ @d.gravity = NorthGravity
872
+ @d = @d.annotate_scaled(@base_image,
873
+ 1.0, 1.0,
874
+ x_offset, y_offset,
875
+ data_point.to_s, @scale)
876
+
877
+ debug { @d.line 0.0, y_offset, @raw_columns, y_offset }
878
+ end
879
+
877
880
  # Shows an error message because you have no data.
878
881
  def draw_no_data
879
882
  @d.fill = @font_color
@@ -882,10 +885,10 @@ module Gruff
882
885
  @d.font_weight = NormalWeight
883
886
  @d.pointsize = scale_fontsize(80)
884
887
  @d.gravity = CenterGravity
885
- @d = @d.annotate_scaled( @base_image,
886
- @raw_columns, @raw_rows/2.0,
887
- 0, 10,
888
- @no_data_message, @scale)
888
+ @d = @d.annotate_scaled(@base_image,
889
+ @raw_columns, @raw_rows/2.0,
890
+ 0, 10,
891
+ @no_data_message, @scale)
889
892
  end
890
893
 
891
894
  # Finds the best background to render based on the provided theme options.
@@ -894,7 +897,7 @@ module Gruff
894
897
  def render_background
895
898
  case @theme_options[:background_colors]
896
899
  when Array
897
- @base_image = render_gradiated_background(*@theme_options[:background_colors])
900
+ @base_image = render_gradiated_background(@theme_options[:background_colors][0], @theme_options[:background_colors][1], @theme_options[:background_direction])
898
901
  when String
899
902
  @base_image = render_solid_background(@theme_options[:background_colors])
900
903
  else
@@ -910,9 +913,22 @@ module Gruff
910
913
  end
911
914
 
912
915
  # Use with a theme definition method to draw a gradiated background.
913
- def render_gradiated_background(top_color, bottom_color)
914
- Image.new(@columns, @rows,
915
- GradientFill.new(0, 0, 100, 0, top_color, bottom_color))
916
+ def render_gradiated_background(top_color, bottom_color, direct = :top_bottom)
917
+ case direct
918
+ when :bottom_top
919
+ gradient_fill = GradientFill.new(0, 0, 100, 0, bottom_color, top_color)
920
+ when :left_right
921
+ gradient_fill = GradientFill.new(0, 0, 0, 100, top_color, bottom_color)
922
+ when :right_left
923
+ gradient_fill = GradientFill.new(0, 0, 0, 100, bottom_color, top_color)
924
+ when :topleft_bottomright
925
+ gradient_fill = GradientFill.new(0, 100, 100, 0, top_color, bottom_color)
926
+ when :topright_bottomleft
927
+ gradient_fill = GradientFill.new(0, 0, 100, 100, bottom_color, top_color)
928
+ else
929
+ gradient_fill = GradientFill.new(0, 0, 100, 0, top_color, bottom_color)
930
+ end
931
+ Image.new(@columns, @rows, gradient_fill)
916
932
  end
917
933
 
918
934
  # Use with a theme to use an image (800x600 original) background.
@@ -948,9 +964,7 @@ module Gruff
948
964
 
949
965
  # Return a comparable fontsize for the current graph.
950
966
  def scale_fontsize(value)
951
- new_fontsize = value * @scale
952
- # return new_fontsize < 10.0 ? 10.0 : new_fontsize
953
- return new_fontsize
967
+ value * @scale
954
968
  end
955
969
 
956
970
  def clip_value_if_greater_than(value, max_value) # :nodoc:
@@ -958,56 +972,51 @@ module Gruff
958
972
  end
959
973
 
960
974
  # Overridden by subclasses such as stacked bar.
961
- def larger_than_max?(data_point, index=0) # :nodoc:
975
+ def larger_than_max?(data_point) # :nodoc:
962
976
  data_point > @maximum_value
963
977
  end
964
978
 
965
- def less_than_min?(data_point, index=0) # :nodoc:
979
+ def less_than_min?(data_point) # :nodoc:
966
980
  data_point < @minimum_value
967
981
  end
968
982
 
969
- # Overridden by subclasses that need it.
970
- def max(data_point, index) # :nodoc:
971
- data_point
972
- end
973
-
974
- # Overridden by subclasses that need it.
975
- def min(data_point, index) # :nodoc:
976
- data_point
977
- end
978
-
979
983
  def significant(i) # :nodoc:
980
984
  return 1.0 if i == 0 # Keep from going into infinite loop
981
985
  inc = BigDecimal(i.to_s)
982
986
  factor = BigDecimal('1.0')
983
- while (inc < 10)
987
+ while inc < 10
984
988
  inc *= 10
985
989
  factor /= 10
986
990
  end
987
991
 
988
- while (inc > 100)
992
+ while inc > 100
989
993
  inc /= 10
990
994
  factor *= 10
991
995
  end
992
996
 
993
997
  res = inc.floor * factor
994
- if (res.to_i.to_f == res)
998
+ if res.to_i.to_f == res
995
999
  res.to_i
996
1000
  else
997
1001
  res
998
1002
  end
999
1003
  end
1000
1004
 
1005
+ # Sort with largest overall summed value at front of array.
1006
+ def sort_data
1007
+ @data = @data.sort_by { |a| -a[DATA_VALUES_INDEX].inject(0) { |sum, num| sum + num.to_f } }
1008
+ end
1009
+
1010
+ # Set the color for each data set unless it was gived in the data(...) call.
1011
+ def set_colors
1012
+ @data.each { |a| a[DATA_COLOR_INDEX] ||= increment_color }
1013
+ end
1014
+
1001
1015
  # Sort with largest overall summed value at front of array so it shows up
1002
1016
  # correctly in the drawn graph.
1003
1017
  def sort_norm_data
1004
- @norm_data.sort! { |a,b| sums(b[DATA_VALUES_INDEX]) <=> sums(a[DATA_VALUES_INDEX]) }
1005
- end
1006
-
1007
- def sums(data_set) # :nodoc:
1008
- total_sum = 0
1009
- data_set.collect {|num| total_sum += num.to_f }
1010
- total_sum
1018
+ @norm_data =
1019
+ @norm_data.sort_by { |a| -a[DATA_VALUES_INDEX].inject(0) { |sum, num| sum + num.to_f } }
1011
1020
  end
1012
1021
 
1013
1022
  # Used by StackedBar and child classes.
@@ -1057,21 +1066,35 @@ module Gruff
1057
1066
  # Returns the next color in your color list.
1058
1067
  def increment_color
1059
1068
  @color_index = (@color_index + 1) % @colors.length
1060
- return @colors[@color_index - 1]
1069
+ @colors[@color_index - 1]
1061
1070
  end
1062
1071
 
1063
1072
  # Return a formatted string representing a number value that should be
1064
1073
  # printed as a label.
1065
- def label(value)
1066
- label = if (@spread.to_f % (@marker_count.to_f==0 ? 1 : @marker_count.to_f) == 0) || !@y_axis_increment.nil?
1067
- value.to_i.to_s
1068
- elsif @spread > 10.0
1069
- sprintf("%0i", value)
1070
- elsif @spread >= 3.0
1071
- sprintf("%0.2f", value)
1072
- else
1073
- value.to_s
1074
- end
1074
+ def label(value, increment)
1075
+ label = if increment
1076
+ if increment >= 10 || (increment * 1) == (increment * 1).to_i.to_f
1077
+ sprintf('%0i', value)
1078
+ elsif increment >= 1.0 || (increment * 10) == (increment * 10).to_i.to_f
1079
+ sprintf('%0.1f', value)
1080
+ elsif increment >= 0.1 || (increment * 100) == (increment * 100).to_i.to_f
1081
+ sprintf('%0.2f', value)
1082
+ elsif increment >= 0.01 || (increment * 1000) == (increment * 1000).to_i.to_f
1083
+ sprintf('%0.3f', value)
1084
+ elsif increment >= 0.001 || (increment * 10000) == (increment * 10000).to_i.to_f
1085
+ sprintf('%0.4f', value)
1086
+ else
1087
+ value.to_s
1088
+ end
1089
+ elsif (@spread.to_f % (@marker_count.to_f==0 ? 1 : @marker_count.to_f) == 0) || !@y_axis_increment.nil?
1090
+ value.to_i.to_s
1091
+ elsif @spread > 10.0
1092
+ sprintf('%0i', value)
1093
+ elsif @spread >= 3.0
1094
+ sprintf('%0.2f', value)
1095
+ else
1096
+ value.to_s
1097
+ end
1075
1098
 
1076
1099
  parts = label.split('.')
1077
1100
  parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{THOUSAND_SEPARATOR}")
@@ -1102,7 +1125,8 @@ module Gruff
1102
1125
 
1103
1126
  end # Gruff::Base
1104
1127
 
1105
- class IncorrectNumberOfDatasetsException < StandardError; end
1128
+ class IncorrectNumberOfDatasetsException < StandardError;
1129
+ end
1106
1130
 
1107
1131
  end # Gruff
1108
1132
 
@@ -1115,12 +1139,31 @@ module Magick
1115
1139
  scaled_width = (width * scale) >= 1 ? (width * scale) : 1
1116
1140
  scaled_height = (height * scale) >= 1 ? (height * scale) : 1
1117
1141
 
1118
- self.annotate( img,
1119
- scaled_width, scaled_height,
1120
- x * scale, y * scale,
1121
- text)
1142
+ self.annotate(img,
1143
+ scaled_width, scaled_height,
1144
+ x * scale, y * scale,
1145
+ text.gsub('%', '%%'))
1146
+ end
1147
+
1148
+ if defined? JRUBY_VERSION
1149
+ # FIXME(uwe): We should NOT need to implement this method.
1150
+ # Remove this method as soon as RMagick4J Issue #16 is fixed.
1151
+ # https://github.com/Serabe/RMagick4J/issues/16
1152
+ def fill=(fill)
1153
+ fill = {:white => '#FFFFFF'}[fill.to_sym] || fill
1154
+ @draw.fill = Magick4J.ColorDatabase.query_default(fill)
1155
+ self
1156
+ end
1157
+ # EMXIF
1122
1158
  end
1123
1159
 
1124
1160
  end
1125
1161
 
1126
1162
  end # Magick
1163
+
1164
+ class String
1165
+ #Taken from http://codesnippets.joyent.com/posts/show/330
1166
+ def commify(delimiter=',')
1167
+ self.gsub(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{delimiter}")
1168
+ end
1169
+ end