gruff 0.3.7 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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