gruff 0.4.0-java

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 (584) hide show
  1. data/.gitignore +7 -0
  2. data/.travis.yml +14 -0
  3. data/Gemfile +10 -0
  4. data/History.txt +193 -0
  5. data/MIT-LICENSE +22 -0
  6. data/Manifest.txt +81 -0
  7. data/README.md +139 -0
  8. data/RELEASE.md +92 -0
  9. data/Rakefile +218 -0
  10. data/assets/bubble.png +0 -0
  11. data/assets/city_scene/background/0000.png +0 -0
  12. data/assets/city_scene/background/0600.png +0 -0
  13. data/assets/city_scene/background/2000.png +0 -0
  14. data/assets/city_scene/clouds/cloudy.png +0 -0
  15. data/assets/city_scene/clouds/partly_cloudy.png +0 -0
  16. data/assets/city_scene/clouds/stormy.png +0 -0
  17. data/assets/city_scene/grass/default.png +0 -0
  18. data/assets/city_scene/haze/true.png +0 -0
  19. data/assets/city_scene/number_sample/1.png +0 -0
  20. data/assets/city_scene/number_sample/2.png +0 -0
  21. data/assets/city_scene/number_sample/default.png +0 -0
  22. data/assets/city_scene/sky/0000.png +0 -0
  23. data/assets/city_scene/sky/0200.png +0 -0
  24. data/assets/city_scene/sky/0400.png +0 -0
  25. data/assets/city_scene/sky/0600.png +0 -0
  26. data/assets/city_scene/sky/0800.png +0 -0
  27. data/assets/city_scene/sky/1000.png +0 -0
  28. data/assets/city_scene/sky/1200.png +0 -0
  29. data/assets/city_scene/sky/1400.png +0 -0
  30. data/assets/city_scene/sky/1500.png +0 -0
  31. data/assets/city_scene/sky/1700.png +0 -0
  32. data/assets/city_scene/sky/2000.png +0 -0
  33. data/assets/pc306715.jpg +0 -0
  34. data/assets/plastik/blue.png +0 -0
  35. data/assets/plastik/green.png +0 -0
  36. data/assets/plastik/red.png +0 -0
  37. data/gruff.gemspec +36 -0
  38. data/init.rb +2 -0
  39. data/lib/gruff.rb +32 -0
  40. data/lib/gruff/accumulator_bar.rb +18 -0
  41. data/lib/gruff/area.rb +51 -0
  42. data/lib/gruff/bar.rb +108 -0
  43. data/lib/gruff/bar_conversion.rb +46 -0
  44. data/lib/gruff/base.rb +1169 -0
  45. data/lib/gruff/bezier.rb +46 -0
  46. data/lib/gruff/bullet.rb +111 -0
  47. data/lib/gruff/deprecated.rb +39 -0
  48. data/lib/gruff/dot.rb +125 -0
  49. data/lib/gruff/line.rb +242 -0
  50. data/lib/gruff/mini/bar.rb +37 -0
  51. data/lib/gruff/mini/legend.rb +109 -0
  52. data/lib/gruff/mini/pie.rb +36 -0
  53. data/lib/gruff/mini/side_bar.rb +35 -0
  54. data/lib/gruff/net.rb +138 -0
  55. data/lib/gruff/photo_bar.rb +100 -0
  56. data/lib/gruff/pie.rb +124 -0
  57. data/lib/gruff/scatter.rb +264 -0
  58. data/lib/gruff/scene.rb +209 -0
  59. data/lib/gruff/side_bar.rb +138 -0
  60. data/lib/gruff/side_stacked_bar.rb +97 -0
  61. data/lib/gruff/spider.rb +131 -0
  62. data/lib/gruff/stacked_area.rb +67 -0
  63. data/lib/gruff/stacked_bar.rb +57 -0
  64. data/lib/gruff/stacked_mixin.rb +23 -0
  65. data/lib/gruff/themes.rb +102 -0
  66. data/lib/gruff/version.rb +3 -0
  67. data/rails_generators/gruff/gruff_generator.rb +63 -0
  68. data/rails_generators/gruff/templates/controller.rb +32 -0
  69. data/rails_generators/gruff/templates/functional_test.rb +24 -0
  70. data/test/gruff_test_case.rb +155 -0
  71. data/test/image_compare.rb +58 -0
  72. data/test/output/accum_bar.png +0 -0
  73. data/test/output/area_keynote.png +0 -0
  74. data/test/output/area_keynote_small.png +0 -0
  75. data/test/output/area_many.png +0 -0
  76. data/test/output/area_many_areas_small.png +0 -0
  77. data/test/output/area_tiny.png +0 -0
  78. data/test/output/area_wide.png +0 -0
  79. data/test/output/bar2_wrapped_legend_16_400.png +0 -0
  80. data/test/output/bar2_wrapped_legend_16_800.png +0 -0
  81. data/test/output/bar2_wrapped_legend_30_400.png +0 -0
  82. data/test/output/bar2_wrapped_legend_30_800.png +0 -0
  83. data/test/output/bar2_wrapped_legend_4_400.png +0 -0
  84. data/test/output/bar2_wrapped_legend_4_800.png +0 -0
  85. data/test/output/bar2_wrapped_legend__400.png +0 -0
  86. data/test/output/bar2_wrapped_legend__800.png +0 -0
  87. data/test/output/bar_background_gradient_bottom_top.png +0 -0
  88. data/test/output/bar_background_gradient_left_right.png +0 -0
  89. data/test/output/bar_background_gradient_right_left.png +0 -0
  90. data/test/output/bar_background_gradient_top_bottom.png +0 -0
  91. data/test/output/bar_background_gradient_topleft_bottomright.png +0 -0
  92. data/test/output/bar_background_gradient_topright_bottomleft.png +0 -0
  93. data/test/output/bar_formatted_numbers.png +0 -0
  94. data/test/output/bar_keynote.png +0 -0
  95. data/test/output/bar_keynote_small.png +0 -0
  96. data/test/output/bar_long_legend_text.png +0 -0
  97. data/test/output/bar_manual_colors.png +0 -0
  98. data/test/output/bar_marker_shadow.png +0 -0
  99. data/test/output/bar_nearly_zero_max_10.png +0 -0
  100. data/test/output/bar_no_legend.png +0 -0
  101. data/test/output/bar_no_line_markers.png +0 -0
  102. data/test/output/bar_no_title.png +0 -0
  103. data/test/output/bar_no_title_or_legend.png +0 -0
  104. data/test/output/bar_odeo.png +0 -0
  105. data/test/output/bar_one_value.png +0 -0
  106. data/test/output/bar_pos_neg.png +0 -0
  107. data/test/output/bar_rails_keynote.png +0 -0
  108. data/test/output/bar_set_absolute_trunc.png +0 -0
  109. data/test/output/bar_set_label_stagger_height.png +0 -0
  110. data/test/output/bar_set_legend_box_size_lg.png +0 -0
  111. data/test/output/bar_set_legend_box_size_sm.png +0 -0
  112. data/test/output/bar_set_marker.png +0 -0
  113. data/test/output/bar_set_trailing_dots_trunc.png +0 -0
  114. data/test/output/bar_spacing_full.png +0 -0
  115. data/test/output/bar_spacing_half.png +0 -0
  116. data/test/output/bar_spacing_none.png +0 -0
  117. data/test/output/bar_tall_graph.png +0 -0
  118. data/test/output/bar_tall_graph_small.png +0 -0
  119. data/test/output/bar_themed.png +0 -0
  120. data/test/output/bar_title_margin.png +0 -0
  121. data/test/output/bar_value_labels.png +0 -0
  122. data/test/output/bar_wide_graph.png +0 -0
  123. data/test/output/bar_wide_graph_small.png +0 -0
  124. data/test/output/bar_wrapped_legend_16_400.png +0 -0
  125. data/test/output/bar_wrapped_legend_16_800.png +0 -0
  126. data/test/output/bar_wrapped_legend_30_400.png +0 -0
  127. data/test/output/bar_wrapped_legend_30_800.png +0 -0
  128. data/test/output/bar_wrapped_legend_4_400.png +0 -0
  129. data/test/output/bar_wrapped_legend_4_800.png +0 -0
  130. data/test/output/bar_wrapped_legend__400.png +0 -0
  131. data/test/output/bar_wrapped_legend__800.png +0 -0
  132. data/test/output/bar_x_y_labels.png +0 -0
  133. data/test/output/bar_y_increment_1.png +0 -0
  134. data/test/output/bar_y_increment_2.0.png +0 -0
  135. data/test/output/bar_y_increment_20.png +0 -0
  136. data/test/output/bar_y_increment_5.png +0 -0
  137. data/test/output/bar_zero_marker_count.png +0 -0
  138. data/test/output/bar_zero_spacing.png +0 -0
  139. data/test/output/bezier.png +0 -0
  140. data/test/output/bezier_2.png +0 -0
  141. data/test/output/bezier_3.png +0 -0
  142. data/test/output/bullet_greyscale.png +0 -0
  143. data/test/output/bullet_no_options.png +0 -0
  144. data/test/output/dot.png +0 -0
  145. data/test/output/dot_manual_colors.png +0 -0
  146. data/test/output/dot_nearly_zero_max_10.png +0 -0
  147. data/test/output/dot_no_legend.png +0 -0
  148. data/test/output/dot_no_line_markers.png +0 -0
  149. data/test/output/dot_no_title.png +0 -0
  150. data/test/output/dot_no_title_or_legend.png +0 -0
  151. data/test/output/dot_one_value.png +0 -0
  152. data/test/output/dot_pos_neg.png +0 -0
  153. data/test/output/dot_set_legend_box_size_lg.png +0 -0
  154. data/test/output/dot_set_legend_box_size_sm.png +0 -0
  155. data/test/output/dot_set_marker.png +0 -0
  156. data/test/output/dot_small.png +0 -0
  157. data/test/output/dot_tall_graph.png +0 -0
  158. data/test/output/dot_tall_graph_small.png +0 -0
  159. data/test/output/dot_themed.png +0 -0
  160. data/test/output/dot_wide_graph.png +0 -0
  161. data/test/output/dot_wide_graph_small.png +0 -0
  162. data/test/output/dot_x_y_labels.png +0 -0
  163. data/test/output/dot_y_increment_1.png +0 -0
  164. data/test/output/dot_y_increment_2.0.png +0 -0
  165. data/test/output/dot_y_increment_20.png +0 -0
  166. data/test/output/dot_y_increment_5.png +0 -0
  167. data/test/output/enhancements-1.png +0 -0
  168. data/test/output/enhancements.png +0 -0
  169. data/test/output/line_all_neg.png +0 -0
  170. data/test/output/line_all_neg_400.png +0 -0
  171. data/test/output/line_all_neg_no_max.png +0 -0
  172. data/test/output/line_baseline.png +0 -0
  173. data/test/output/line_hang_value.png +0 -0
  174. data/test/output/line_hide_dots.png +0 -0
  175. data/test/output/line_hide_lines.png +0 -0
  176. data/test/output/line_jruby_error.png +0 -0
  177. data/test/output/line_large.png +0 -0
  178. data/test/output/line_large_baseline.png +0 -0
  179. data/test/output/line_legend_at_bottom.png +0 -0
  180. data/test/output/line_many.png +0 -0
  181. data/test/output/line_many_lines_small.png +0 -0
  182. data/test/output/line_many_numbers.png +0 -0
  183. data/test/output/line_marker_label_accuracy.png +0 -0
  184. data/test/output/line_more_sets_than_colors.png +0 -0
  185. data/test/output/line_no_data.png +0 -0
  186. data/test/output/line_no_data_msg.png +0 -0
  187. data/test/output/line_no_data_other.png +0 -0
  188. data/test/output/line_no_hide.png +0 -0
  189. data/test/output/line_no_legend.png +0 -0
  190. data/test/output/line_no_line_markers.png +0 -0
  191. data/test/output/line_no_title.png +0 -0
  192. data/test/output/line_nothing_but_the_graph.png +0 -0
  193. data/test/output/line_one_value.png +0 -0
  194. data/test/output/line_one_value_array.png +0 -0
  195. data/test/output/line_pos_neg.png +0 -0
  196. data/test/output/line_pos_neg_400.png +0 -0
  197. data/test/output/line_similar_high_end_values.png +0 -0
  198. data/test/output/line_similar_high_end_values_with_floor.png +0 -0
  199. data/test/output/line_small_small_zero.png +0 -0
  200. data/test/output/line_small_values.png +0 -0
  201. data/test/output/line_small_values_small.png +0 -0
  202. data/test/output/line_small_zero.png +0 -0
  203. data/test/output/line_some_nil_points.png +0 -0
  204. data/test/output/line_theme_37signals_.png +0 -0
  205. data/test/output/line_theme_37signals_400.png +0 -0
  206. data/test/output/line_theme_keynote_.png +0 -0
  207. data/test/output/line_theme_keynote_400.png +0 -0
  208. data/test/output/line_theme_odeo_.png +0 -0
  209. data/test/output/line_theme_odeo_400.png +0 -0
  210. data/test/output/line_theme_rails_keynote_.png +0 -0
  211. data/test/output/line_theme_rails_keynote_400.png +0 -0
  212. data/test/output/line_tiny.png +0 -0
  213. data/test/output/line_transparent.png +0 -0
  214. data/test/output/line_very_small.png +0 -0
  215. data/test/output/line_wide_graph.png +0 -0
  216. data/test/output/line_wide_graph_small.png +0 -0
  217. data/test/output/line_xy.png +0 -0
  218. data/test/output/line_xy_pairs.png +0 -0
  219. data/test/output/line_y_axis_increment.png +0 -0
  220. data/test/output/mini_bar.png +0 -0
  221. data/test/output/mini_pie.png +0 -0
  222. data/test/output/mini_pie_right_legend.png +0 -0
  223. data/test/output/mini_side_bar.png +0 -0
  224. data/test/output/mini_side_bar_multi_color.png +0 -0
  225. data/test/output/net_dots_tiny.png +0 -0
  226. data/test/output/net_large.png +0 -0
  227. data/test/output/net_many.png +0 -0
  228. data/test/output/net_many_nets_small.png +0 -0
  229. data/test/output/net_no_data.png +0 -0
  230. data/test/output/net_no_data_msg.png +0 -0
  231. data/test/output/net_no_data_other.png +0 -0
  232. data/test/output/net_no_legend.png +0 -0
  233. data/test/output/net_no_net_markers.png +0 -0
  234. data/test/output/net_no_title.png +0 -0
  235. data/test/output/net_nothing_but_the_graph.png +0 -0
  236. data/test/output/net_similar_high_end_values.png +0 -0
  237. data/test/output/net_small.png +0 -0
  238. data/test/output/net_small_small.png +0 -0
  239. data/test/output/net_small_small_zero.png +0 -0
  240. data/test/output/net_small_zero.png +0 -0
  241. data/test/output/net_wide_graph.png +0 -0
  242. data/test/output/net_wide_graph_small.png +0 -0
  243. data/test/output/pie_equal.png +0 -0
  244. data/test/output/pie_grey.png +0 -0
  245. data/test/output/pie_keynote.png +0 -0
  246. data/test/output/pie_keynote_small.png +0 -0
  247. data/test/output/pie_legend.png +0 -0
  248. data/test/output/pie_legend_small.png +0 -0
  249. data/test/output/pie_nearly_equal.png +0 -0
  250. data/test/output/pie_one_val.png +0 -0
  251. data/test/output/pie_pastel.png +0 -0
  252. data/test/output/pie_simple.png +0 -0
  253. data/test/output/pie_wide.png +0 -0
  254. data/test/output/pie_wrapped_legend_16_400.png +0 -0
  255. data/test/output/pie_wrapped_legend_16_800.png +0 -0
  256. data/test/output/pie_wrapped_legend_30_400.png +0 -0
  257. data/test/output/pie_wrapped_legend_30_800.png +0 -0
  258. data/test/output/pie_wrapped_legend_4_400.png +0 -0
  259. data/test/output/pie_wrapped_legend_4_800.png +0 -0
  260. data/test/output/pie_wrapped_legend__400.png +0 -0
  261. data/test/output/pie_wrapped_legend__800.png +0 -0
  262. data/test/output/pie_zero.png +0 -0
  263. data/test/output/scatter_all_neg.png +0 -0
  264. data/test/output/scatter_all_neg_400.png +0 -0
  265. data/test/output/scatter_basic.png +0 -0
  266. data/test/output/scatter_many.png +0 -0
  267. data/test/output/scatter_no_data.png +0 -0
  268. data/test/output/scatter_no_data_msg.png +0 -0
  269. data/test/output/scatter_no_data_other.png +0 -0
  270. data/test/output/scatter_no_hide.png +0 -0
  271. data/test/output/scatter_no_labels.png +0 -0
  272. data/test/output/scatter_no_legend.png +0 -0
  273. data/test/output/scatter_no_line_markers.png +0 -0
  274. data/test/output/scatter_no_title.png +0 -0
  275. data/test/output/scatter_nothing_but_the_graph.png +0 -0
  276. data/test/output/scatter_pos_neg.png +0 -0
  277. data/test/output/scatter_pos_neg_400.png +0 -0
  278. data/test/output/scatter_wide_graph.png +0 -0
  279. data/test/output/scatter_wide_graph_small.png +0 -0
  280. data/test/output/scene_hazy_night.png +0 -0
  281. data/test/output/scene_not_hazy_day.png +0 -0
  282. data/test/output/scene_partly_cloudy_day.png +0 -0
  283. data/test/output/scene_stormy_day.png +0 -0
  284. data/test/output/scene_stormy_night.png +0 -0
  285. data/test/output/side_bar.png +0 -0
  286. data/test/output/side_bar_data_range.png +0 -0
  287. data/test/output/side_bar_labels.png +0 -0
  288. data/test/output/side_bar_spacing_full.png +0 -0
  289. data/test/output/side_bar_spacing_half.png +0 -0
  290. data/test/output/side_bar_spacing_none.png +0 -0
  291. data/test/output/side_stacked_bar_keynote.png +0 -0
  292. data/test/output/side_stacked_bar_keynote_small.png +0 -0
  293. data/test/output/side_stacked_bar_labels.png +0 -0
  294. data/test/output/side_stacked_bar_long_label.png +0 -0
  295. data/test/output/side_stacked_bar_wide.png +0 -0
  296. data/test/output/spider_37signals.png +0 -0
  297. data/test/output/spider_equal.png +0 -0
  298. data/test/output/spider_keynote.png +0 -0
  299. data/test/output/spider_legend.png +0 -0
  300. data/test/output/spider_legend_small.png +0 -0
  301. data/test/output/spider_lots_of_data.png +0 -0
  302. data/test/output/spider_lots_of_data_normal_names.png +0 -0
  303. data/test/output/spider_nearly_equal.png +0 -0
  304. data/test/output/spider_no_axes.png +0 -0
  305. data/test/output/spider_no_background.png +0 -0
  306. data/test/output/spider_no_print.png +0 -0
  307. data/test/output/spider_overlay_1.png +0 -0
  308. data/test/output/spider_overlay_2.png +0 -0
  309. data/test/output/spider_rotation.png +0 -0
  310. data/test/output/spider_small.png +0 -0
  311. data/test/output/spider_wide.png +0 -0
  312. data/test/output/spider_zero.png +0 -0
  313. data/test/output/stacked_area_keynote.png +0 -0
  314. data/test/output/stacked_area_keynote_small.png +0 -0
  315. data/test/output/stacked_bar_keynote.png +0 -0
  316. data/test/output/stacked_bar_keynote_small.png +0 -0
  317. data/test/output_java/accum_bar.png +0 -0
  318. data/test/output_java/area_keynote.png +0 -0
  319. data/test/output_java/area_keynote_small.png +0 -0
  320. data/test/output_java/area_many.png +0 -0
  321. data/test/output_java/area_many_areas_small.png +0 -0
  322. data/test/output_java/area_tiny.png +0 -0
  323. data/test/output_java/area_wide.png +0 -0
  324. data/test/output_java/bar2_wrapped_legend_16_400.png +0 -0
  325. data/test/output_java/bar2_wrapped_legend_16_800.png +0 -0
  326. data/test/output_java/bar2_wrapped_legend_30_400.png +0 -0
  327. data/test/output_java/bar2_wrapped_legend_30_800.png +0 -0
  328. data/test/output_java/bar2_wrapped_legend_4_400.png +0 -0
  329. data/test/output_java/bar2_wrapped_legend_4_800.png +0 -0
  330. data/test/output_java/bar2_wrapped_legend__400.png +0 -0
  331. data/test/output_java/bar2_wrapped_legend__800.png +0 -0
  332. data/test/output_java/bar_background_gradient_bottom_top.png +0 -0
  333. data/test/output_java/bar_background_gradient_left_right.png +0 -0
  334. data/test/output_java/bar_background_gradient_right_left.png +0 -0
  335. data/test/output_java/bar_background_gradient_top_bottom.png +0 -0
  336. data/test/output_java/bar_background_gradient_topleft_bottomright.png +0 -0
  337. data/test/output_java/bar_background_gradient_topright_bottomleft.png +0 -0
  338. data/test/output_java/bar_formatted_numbers.png +0 -0
  339. data/test/output_java/bar_keynote.png +0 -0
  340. data/test/output_java/bar_keynote_small.png +0 -0
  341. data/test/output_java/bar_long_legend_text.png +0 -0
  342. data/test/output_java/bar_manual_colors.png +0 -0
  343. data/test/output_java/bar_marker_shadow.png +0 -0
  344. data/test/output_java/bar_nearly_zero_max_10.png +0 -0
  345. data/test/output_java/bar_no_legend.png +0 -0
  346. data/test/output_java/bar_no_line_markers.png +0 -0
  347. data/test/output_java/bar_no_title.png +0 -0
  348. data/test/output_java/bar_no_title_or_legend.png +0 -0
  349. data/test/output_java/bar_odeo.png +0 -0
  350. data/test/output_java/bar_one_value.png +0 -0
  351. data/test/output_java/bar_pos_neg.png +0 -0
  352. data/test/output_java/bar_rails_keynote.png +0 -0
  353. data/test/output_java/bar_set_absolute_trunc.png +0 -0
  354. data/test/output_java/bar_set_label_stagger_height.png +0 -0
  355. data/test/output_java/bar_set_legend_box_size_lg.png +0 -0
  356. data/test/output_java/bar_set_legend_box_size_sm.png +0 -0
  357. data/test/output_java/bar_set_marker.png +0 -0
  358. data/test/output_java/bar_set_trailing_dots_trunc.png +0 -0
  359. data/test/output_java/bar_spacing_full.png +0 -0
  360. data/test/output_java/bar_spacing_half.png +0 -0
  361. data/test/output_java/bar_spacing_none.png +0 -0
  362. data/test/output_java/bar_tall_graph.png +0 -0
  363. data/test/output_java/bar_tall_graph_small.png +0 -0
  364. data/test/output_java/bar_themed.png +0 -0
  365. data/test/output_java/bar_title_margin.png +0 -0
  366. data/test/output_java/bar_value_labels.png +0 -0
  367. data/test/output_java/bar_wide_graph.png +0 -0
  368. data/test/output_java/bar_wide_graph_small.png +0 -0
  369. data/test/output_java/bar_wrapped_legend_16_400.png +0 -0
  370. data/test/output_java/bar_wrapped_legend_16_800.png +0 -0
  371. data/test/output_java/bar_wrapped_legend_30_400.png +0 -0
  372. data/test/output_java/bar_wrapped_legend_30_800.png +0 -0
  373. data/test/output_java/bar_wrapped_legend_4_400.png +0 -0
  374. data/test/output_java/bar_wrapped_legend_4_800.png +0 -0
  375. data/test/output_java/bar_wrapped_legend__400.png +0 -0
  376. data/test/output_java/bar_wrapped_legend__800.png +0 -0
  377. data/test/output_java/bar_x_y_labels.png +0 -0
  378. data/test/output_java/bar_y_increment_1.png +0 -0
  379. data/test/output_java/bar_y_increment_2.0.png +0 -0
  380. data/test/output_java/bar_y_increment_20.png +0 -0
  381. data/test/output_java/bar_y_increment_5.png +0 -0
  382. data/test/output_java/bar_zero_marker_count.png +0 -0
  383. data/test/output_java/bar_zero_spacing.png +0 -0
  384. data/test/output_java/bezier.png +0 -0
  385. data/test/output_java/bezier_2.png +0 -0
  386. data/test/output_java/bezier_3.png +0 -0
  387. data/test/output_java/bullet_greyscale.png +0 -0
  388. data/test/output_java/bullet_no_options.png +0 -0
  389. data/test/output_java/dot.png +0 -0
  390. data/test/output_java/dot_manual_colors.png +0 -0
  391. data/test/output_java/dot_nearly_zero_max_10.png +0 -0
  392. data/test/output_java/dot_no_legend.png +0 -0
  393. data/test/output_java/dot_no_line_markers.png +0 -0
  394. data/test/output_java/dot_no_title.png +0 -0
  395. data/test/output_java/dot_no_title_or_legend.png +0 -0
  396. data/test/output_java/dot_one_value.png +0 -0
  397. data/test/output_java/dot_pos_neg.png +0 -0
  398. data/test/output_java/dot_set_legend_box_size_lg.png +0 -0
  399. data/test/output_java/dot_set_legend_box_size_sm.png +0 -0
  400. data/test/output_java/dot_set_marker.png +0 -0
  401. data/test/output_java/dot_small.png +0 -0
  402. data/test/output_java/dot_tall_graph.png +0 -0
  403. data/test/output_java/dot_tall_graph_small.png +0 -0
  404. data/test/output_java/dot_themed.png +0 -0
  405. data/test/output_java/dot_wide_graph.png +0 -0
  406. data/test/output_java/dot_wide_graph_small.png +0 -0
  407. data/test/output_java/dot_x_y_labels.png +0 -0
  408. data/test/output_java/dot_y_increment_1.png +0 -0
  409. data/test/output_java/dot_y_increment_2.0.png +0 -0
  410. data/test/output_java/dot_y_increment_20.png +0 -0
  411. data/test/output_java/dot_y_increment_5.png +0 -0
  412. data/test/output_java/enhancements-1.png +0 -0
  413. data/test/output_java/enhancements.png +0 -0
  414. data/test/output_java/line_all_neg.png +0 -0
  415. data/test/output_java/line_all_neg_400.png +0 -0
  416. data/test/output_java/line_all_neg_no_max.png +0 -0
  417. data/test/output_java/line_baseline.png +0 -0
  418. data/test/output_java/line_hang_value.png +0 -0
  419. data/test/output_java/line_hide_dots.png +0 -0
  420. data/test/output_java/line_hide_lines.png +0 -0
  421. data/test/output_java/line_jruby_error.png +0 -0
  422. data/test/output_java/line_large.png +0 -0
  423. data/test/output_java/line_large_baseline.png +0 -0
  424. data/test/output_java/line_legend_at_bottom.png +0 -0
  425. data/test/output_java/line_many.png +0 -0
  426. data/test/output_java/line_many_lines_small.png +0 -0
  427. data/test/output_java/line_many_numbers.png +0 -0
  428. data/test/output_java/line_marker_label_accuracy.png +0 -0
  429. data/test/output_java/line_more_sets_than_colors.png +0 -0
  430. data/test/output_java/line_no_data.png +0 -0
  431. data/test/output_java/line_no_data_msg.png +0 -0
  432. data/test/output_java/line_no_data_other.png +0 -0
  433. data/test/output_java/line_no_hide.png +0 -0
  434. data/test/output_java/line_no_legend.png +0 -0
  435. data/test/output_java/line_no_line_markers.png +0 -0
  436. data/test/output_java/line_no_title.png +0 -0
  437. data/test/output_java/line_nothing_but_the_graph.png +0 -0
  438. data/test/output_java/line_one_value.png +0 -0
  439. data/test/output_java/line_one_value_array.png +0 -0
  440. data/test/output_java/line_pos_neg.png +0 -0
  441. data/test/output_java/line_pos_neg_400.png +0 -0
  442. data/test/output_java/line_similar_high_end_values.png +0 -0
  443. data/test/output_java/line_similar_high_end_values_with_floor.png +0 -0
  444. data/test/output_java/line_small_small_zero.png +0 -0
  445. data/test/output_java/line_small_values.png +0 -0
  446. data/test/output_java/line_small_values_small.png +0 -0
  447. data/test/output_java/line_small_zero.png +0 -0
  448. data/test/output_java/line_some_nil_points.png +0 -0
  449. data/test/output_java/line_theme_37signals_.png +0 -0
  450. data/test/output_java/line_theme_37signals_400.png +0 -0
  451. data/test/output_java/line_theme_keynote_.png +0 -0
  452. data/test/output_java/line_theme_keynote_400.png +0 -0
  453. data/test/output_java/line_theme_odeo_.png +0 -0
  454. data/test/output_java/line_theme_odeo_400.png +0 -0
  455. data/test/output_java/line_theme_rails_keynote_.png +0 -0
  456. data/test/output_java/line_theme_rails_keynote_400.png +0 -0
  457. data/test/output_java/line_tiny.png +0 -0
  458. data/test/output_java/line_transparent.png +0 -0
  459. data/test/output_java/line_very_small.png +0 -0
  460. data/test/output_java/line_wide_graph.png +0 -0
  461. data/test/output_java/line_wide_graph_small.png +0 -0
  462. data/test/output_java/line_xy.png +0 -0
  463. data/test/output_java/line_xy_pairs.png +0 -0
  464. data/test/output_java/line_y_axis_increment.png +0 -0
  465. data/test/output_java/mini_bar.png +0 -0
  466. data/test/output_java/mini_pie.png +0 -0
  467. data/test/output_java/mini_pie_right_legend.png +0 -0
  468. data/test/output_java/mini_side_bar.png +0 -0
  469. data/test/output_java/mini_side_bar_multi_color.png +0 -0
  470. data/test/output_java/net_dots_tiny.png +0 -0
  471. data/test/output_java/net_large.png +0 -0
  472. data/test/output_java/net_many.png +0 -0
  473. data/test/output_java/net_many_nets_small.png +0 -0
  474. data/test/output_java/net_no_data.png +0 -0
  475. data/test/output_java/net_no_data_msg.png +0 -0
  476. data/test/output_java/net_no_data_other.png +0 -0
  477. data/test/output_java/net_no_legend.png +0 -0
  478. data/test/output_java/net_no_net_markers.png +0 -0
  479. data/test/output_java/net_no_title.png +0 -0
  480. data/test/output_java/net_nothing_but_the_graph.png +0 -0
  481. data/test/output_java/net_similar_high_end_values.png +0 -0
  482. data/test/output_java/net_small.png +0 -0
  483. data/test/output_java/net_small_small.png +0 -0
  484. data/test/output_java/net_small_small_zero.png +0 -0
  485. data/test/output_java/net_small_zero.png +0 -0
  486. data/test/output_java/net_wide_graph.png +0 -0
  487. data/test/output_java/net_wide_graph_small.png +0 -0
  488. data/test/output_java/pie_equal.png +0 -0
  489. data/test/output_java/pie_grey.png +0 -0
  490. data/test/output_java/pie_keynote.png +0 -0
  491. data/test/output_java/pie_keynote_small.png +0 -0
  492. data/test/output_java/pie_legend.png +0 -0
  493. data/test/output_java/pie_legend_small.png +0 -0
  494. data/test/output_java/pie_nearly_equal.png +0 -0
  495. data/test/output_java/pie_one_val.png +0 -0
  496. data/test/output_java/pie_pastel.png +0 -0
  497. data/test/output_java/pie_simple.png +0 -0
  498. data/test/output_java/pie_wide.png +0 -0
  499. data/test/output_java/pie_wrapped_legend_16_400.png +0 -0
  500. data/test/output_java/pie_wrapped_legend_16_800.png +0 -0
  501. data/test/output_java/pie_wrapped_legend_30_400.png +0 -0
  502. data/test/output_java/pie_wrapped_legend_30_800.png +0 -0
  503. data/test/output_java/pie_wrapped_legend_4_400.png +0 -0
  504. data/test/output_java/pie_wrapped_legend_4_800.png +0 -0
  505. data/test/output_java/pie_wrapped_legend__400.png +0 -0
  506. data/test/output_java/pie_wrapped_legend__800.png +0 -0
  507. data/test/output_java/pie_zero.png +0 -0
  508. data/test/output_java/scatter_all_neg.png +0 -0
  509. data/test/output_java/scatter_all_neg_400.png +0 -0
  510. data/test/output_java/scatter_basic.png +0 -0
  511. data/test/output_java/scatter_many.png +0 -0
  512. data/test/output_java/scatter_no_data.png +0 -0
  513. data/test/output_java/scatter_no_data_msg.png +0 -0
  514. data/test/output_java/scatter_no_data_other.png +0 -0
  515. data/test/output_java/scatter_no_hide.png +0 -0
  516. data/test/output_java/scatter_no_labels.png +0 -0
  517. data/test/output_java/scatter_no_legend.png +0 -0
  518. data/test/output_java/scatter_no_line_markers.png +0 -0
  519. data/test/output_java/scatter_no_title.png +0 -0
  520. data/test/output_java/scatter_nothing_but_the_graph.png +0 -0
  521. data/test/output_java/scatter_pos_neg.png +0 -0
  522. data/test/output_java/scatter_pos_neg_400.png +0 -0
  523. data/test/output_java/scatter_wide_graph.png +0 -0
  524. data/test/output_java/scatter_wide_graph_small.png +0 -0
  525. data/test/output_java/scene_hazy_night.png +0 -0
  526. data/test/output_java/scene_not_hazy_day.png +0 -0
  527. data/test/output_java/scene_partly_cloudy_day.png +0 -0
  528. data/test/output_java/scene_stormy_day.png +0 -0
  529. data/test/output_java/scene_stormy_night.png +0 -0
  530. data/test/output_java/side_bar.png +0 -0
  531. data/test/output_java/side_bar_data_range.png +0 -0
  532. data/test/output_java/side_bar_labels.png +0 -0
  533. data/test/output_java/side_bar_spacing_full.png +0 -0
  534. data/test/output_java/side_bar_spacing_half.png +0 -0
  535. data/test/output_java/side_bar_spacing_none.png +0 -0
  536. data/test/output_java/side_stacked_bar_keynote.png +0 -0
  537. data/test/output_java/side_stacked_bar_keynote_small.png +0 -0
  538. data/test/output_java/side_stacked_bar_labels.png +0 -0
  539. data/test/output_java/side_stacked_bar_long_label.png +0 -0
  540. data/test/output_java/side_stacked_bar_wide.png +0 -0
  541. data/test/output_java/spider_37signals.png +0 -0
  542. data/test/output_java/spider_equal.png +0 -0
  543. data/test/output_java/spider_keynote.png +0 -0
  544. data/test/output_java/spider_legend.png +0 -0
  545. data/test/output_java/spider_legend_small.png +0 -0
  546. data/test/output_java/spider_lots_of_data.png +0 -0
  547. data/test/output_java/spider_lots_of_data_normal_names.png +0 -0
  548. data/test/output_java/spider_nearly_equal.png +0 -0
  549. data/test/output_java/spider_no_axes.png +0 -0
  550. data/test/output_java/spider_no_background.png +0 -0
  551. data/test/output_java/spider_no_print.png +0 -0
  552. data/test/output_java/spider_overlay_1.png +0 -0
  553. data/test/output_java/spider_overlay_2.png +0 -0
  554. data/test/output_java/spider_rotation.png +0 -0
  555. data/test/output_java/spider_small.png +0 -0
  556. data/test/output_java/spider_wide.png +0 -0
  557. data/test/output_java/spider_zero.png +0 -0
  558. data/test/output_java/stacked_area_keynote.png +0 -0
  559. data/test/output_java/stacked_area_keynote_small.png +0 -0
  560. data/test/output_java/stacked_bar_keynote.png +0 -0
  561. data/test/output_java/stacked_bar_keynote_small.png +0 -0
  562. data/test/test_accumulator_bar.rb +51 -0
  563. data/test/test_area.rb +134 -0
  564. data/test/test_bar.rb +505 -0
  565. data/test/test_base.rb +8 -0
  566. data/test/test_bezier.rb +33 -0
  567. data/test/test_bullet.rb +26 -0
  568. data/test/test_dot.rb +263 -0
  569. data/test/test_legend.rb +68 -0
  570. data/test/test_line.rb +626 -0
  571. data/test/test_mini_bar.rb +33 -0
  572. data/test/test_mini_pie.rb +25 -0
  573. data/test/test_mini_side_bar.rb +36 -0
  574. data/test/test_net.rb +231 -0
  575. data/test/test_photo.rb +41 -0
  576. data/test/test_pie.rb +154 -0
  577. data/test/test_scatter.rb +233 -0
  578. data/test/test_scene.rb +100 -0
  579. data/test/test_side_bar.rb +56 -0
  580. data/test/test_sidestacked_bar.rb +105 -0
  581. data/test/test_spider.rb +226 -0
  582. data/test/test_stacked_area.rb +52 -0
  583. data/test/test_stacked_bar.rb +52 -0
  584. metadata +1176 -0
@@ -0,0 +1,124 @@
1
+ require File.dirname(__FILE__) + '/base'
2
+
3
+ ##
4
+ # Here's how to make a Pie graph:
5
+ #
6
+ # g = Gruff::Pie.new
7
+ # g.title = "Visual Pie Graph Test"
8
+ # g.data 'Fries', 20
9
+ # g.data 'Hamburgers', 50
10
+ # g.write("test/output/pie_keynote.png")
11
+ #
12
+ # To control where the pie chart starts creating slices, use #zero_degree.
13
+
14
+ class Gruff::Pie < Gruff::Base
15
+
16
+ TEXT_OFFSET_PERCENTAGE = 0.15
17
+
18
+ # Can be used to make the pie start cutting slices at the top (-90.0)
19
+ # or at another angle. Default is 0.0, which starts at 3 o'clock.
20
+ attr_accessor :zero_degree
21
+ # Do not show labels for slices that are less than this percent. Use 0 to always show all labels.
22
+ # Defaults to 0
23
+ attr_accessor :hide_labels_less_than
24
+
25
+ def initialize_ivars
26
+ super
27
+ @zero_degree = 0.0
28
+ @hide_labels_less_than = 0.0
29
+ end
30
+
31
+ def draw
32
+ @hide_line_markers = true
33
+
34
+ super
35
+
36
+ return unless @has_data
37
+
38
+ diameter = @graph_height
39
+ radius = ([@graph_width, @graph_height].min / 2.0) * 0.8
40
+ center_x = @graph_left + (@graph_width / 2.0)
41
+ center_y = @graph_top + (@graph_height / 2.0) - 10 # Move graph up a bit
42
+ total_sum = sums_for_pie()
43
+ prev_degrees = @zero_degree
44
+
45
+ # Use full data since we can easily calculate percentages
46
+ data = (@sort ? @data.sort{ |a, b| a[DATA_VALUES_INDEX].first <=> b[DATA_VALUES_INDEX].first } : @data)
47
+ data.each do |data_row|
48
+ if data_row[DATA_VALUES_INDEX].first > 0
49
+ @d = @d.stroke data_row[DATA_COLOR_INDEX]
50
+ @d = @d.fill 'transparent'
51
+ @d.stroke_width(radius) # stroke width should be equal to radius. we'll draw centered on (radius / 2)
52
+
53
+ current_degrees = (data_row[DATA_VALUES_INDEX].first / total_sum) * 360.0
54
+
55
+ # ellipse will draw the the stroke centered on the first two parameters offset by the second two.
56
+ # therefore, in order to draw a circle of the proper diameter we must center the stroke at
57
+ # half the radius for both x and y
58
+ @d = @d.ellipse(center_x, center_y,
59
+ radius / 2.0, radius / 2.0,
60
+ prev_degrees, prev_degrees + current_degrees + 0.5) # <= +0.5 'fudge factor' gets rid of the ugly gaps
61
+
62
+ half_angle = prev_degrees + ((prev_degrees + current_degrees) - prev_degrees) / 2
63
+
64
+ label_val = ((data_row[DATA_VALUES_INDEX].first / total_sum) * 100.0).round
65
+ unless label_val < @hide_labels_less_than
66
+ # RMagick must use sprintf with the string and % has special significance.
67
+ label_string = label_val.to_s + '%'
68
+ @d = draw_label(center_x,center_y, half_angle,
69
+ radius + (radius * TEXT_OFFSET_PERCENTAGE),
70
+ label_string)
71
+ end
72
+
73
+ prev_degrees += current_degrees
74
+ end
75
+ end
76
+
77
+ # TODO debug a circle where the text is drawn...
78
+
79
+ @d.draw(@base_image)
80
+ end
81
+
82
+ private
83
+
84
+ ##
85
+ # Labels are drawn around a slightly wider ellipse to give room for
86
+ # labels on the left and right.
87
+ def draw_label(center_x, center_y, angle, radius, amount)
88
+ # TODO Don't use so many hard-coded numbers
89
+ r_offset = 20.0 # The distance out from the center of the pie to get point
90
+ x_offset = center_x # + 15.0 # The label points need to be tweaked slightly
91
+ y_offset = center_y # This one doesn't though
92
+ radius_offset = (radius + r_offset)
93
+ ellipse_factor = radius_offset * TEXT_OFFSET_PERCENTAGE
94
+ x = x_offset + ((radius_offset + ellipse_factor) * Math.cos(angle.deg2rad))
95
+ y = y_offset + (radius_offset * Math.sin(angle.deg2rad))
96
+
97
+ # Draw label
98
+ @d.fill = @font_color
99
+ @d.font = @font if @font
100
+ @d.pointsize = scale_fontsize(@marker_font_size)
101
+ @d.stroke = 'transparent'
102
+ @d.font_weight = BoldWeight
103
+ @d.gravity = CenterGravity
104
+ @d.annotate_scaled( @base_image,
105
+ 0, 0,
106
+ x, y,
107
+ amount, @scale)
108
+ end
109
+
110
+ def sums_for_pie
111
+ total_sum = 0.0
112
+ @data.collect {|data_row| total_sum += data_row[DATA_VALUES_INDEX].first }
113
+ total_sum
114
+ end
115
+
116
+ end
117
+
118
+ class Float
119
+ # Used for degree => radian conversions
120
+ def deg2rad
121
+ self * (Math::PI/180.0)
122
+ end
123
+ end
124
+
@@ -0,0 +1,264 @@
1
+ require File.dirname(__FILE__) + '/base'
2
+
3
+ # Here's how to set up an XY Scatter Chart
4
+ #
5
+ # g = Gruff::Scatter.new(800)
6
+ # g.data(:apples, [1,2,3,4], [4,3,2,1])
7
+ # g.data('oranges', [5,7,8], [4,1,7])
8
+ # g.write('test/output/scatter.png')
9
+ #
10
+ #
11
+ class Gruff::Scatter < Gruff::Base
12
+
13
+ # Maximum X Value. The value will get overwritten by the max in the
14
+ # datasets.
15
+ attr_accessor :maximum_x_value
16
+
17
+ # Minimum X Value. The value will get overwritten by the min in the
18
+ # datasets.
19
+ attr_accessor :minimum_x_value
20
+
21
+ # The number of vertical lines shown for reference
22
+ attr_accessor :marker_x_count
23
+
24
+ #~ # Draw a dashed horizontal line at the given y value
25
+ #~ attr_accessor :baseline_y_value
26
+
27
+ #~ # Color of the horizontal baseline
28
+ #~ attr_accessor :baseline_y_color
29
+
30
+ #~ # Draw a dashed horizontal line at the given y value
31
+ #~ attr_accessor :baseline_x_value
32
+
33
+ #~ # Color of the horizontal baseline
34
+ #~ attr_accessor :baseline_x_color
35
+
36
+
37
+ # Gruff::Scatter takes the same parameters as the Gruff::Line graph
38
+ #
39
+ # ==== Example
40
+ #
41
+ # g = Gruff::Scatter.new
42
+ #
43
+ def initialize(*args)
44
+ super(*args)
45
+
46
+ @maximum_x_value = @minimum_x_value = nil
47
+ @baseline_x_color = @baseline_y_color = 'red'
48
+ @baseline_x_value = @baseline_y_value = nil
49
+ @marker_x_count = nil
50
+ end
51
+
52
+ def setup_drawing
53
+ # TODO Need to get x-axis labels working. Current behavior will be to not allow.
54
+ @labels = {}
55
+
56
+ super
57
+
58
+ # Translate our values so that we can use the base methods for drawing
59
+ # the standard chart stuff
60
+ @column_count = @x_spread
61
+ end
62
+
63
+ def draw
64
+ super
65
+ return unless @has_data
66
+
67
+ # Check to see if more than one datapoint was given. NaN can result otherwise.
68
+ @x_increment = (@column_count > 1) ? (@graph_width / (@column_count - 1).to_f) : @graph_width
69
+
70
+ #~ if (defined?(@norm_y_baseline)) then
71
+ #~ level = @graph_top + (@graph_height - @norm_baseline * @graph_height)
72
+ #~ @d = @d.push
73
+ #~ @d.stroke_color @baseline_color
74
+ #~ @d.fill_opacity 0.0
75
+ #~ @d.stroke_dasharray(10, 20)
76
+ #~ @d.stroke_width 5
77
+ #~ @d.line(@graph_left, level, @graph_left + @graph_width, level)
78
+ #~ @d = @d.pop
79
+ #~ end
80
+
81
+ #~ if (defined?(@norm_x_baseline)) then
82
+
83
+ #~ end
84
+
85
+ @norm_data.each do |data_row|
86
+ data_row[DATA_VALUES_INDEX].each_with_index do |data_point, index|
87
+ x_value = data_row[DATA_VALUES_X_INDEX][index]
88
+ next if data_point.nil? || x_value.nil?
89
+
90
+ new_x = get_x_coord(x_value, @graph_width, @graph_left)
91
+ new_y = @graph_top + (@graph_height - data_point * @graph_height)
92
+
93
+ # Reset each time to avoid thin-line errors
94
+ @d = @d.stroke data_row[DATA_COLOR_INDEX]
95
+ @d = @d.fill data_row[DATA_COLOR_INDEX]
96
+ @d = @d.stroke_opacity 1.0
97
+ @d = @d.stroke_width clip_value_if_greater_than(@columns / (@norm_data.first[1].size * 4), 5.0)
98
+
99
+ circle_radius = clip_value_if_greater_than(@columns / (@norm_data.first[1].size * 2.5), 5.0)
100
+ @d = @d.circle(new_x, new_y, new_x - circle_radius, new_y)
101
+ end
102
+ end
103
+
104
+ @d.draw(@base_image)
105
+ end
106
+
107
+ # The first parameter is the name of the dataset. The next two are the
108
+ # x and y axis data points contain in their own array in that respective
109
+ # order. The final parameter is the color.
110
+ #
111
+ # Can be called multiple times with different datasets for a multi-valued
112
+ # graph.
113
+ #
114
+ # If the color argument is nil, the next color from the default theme will
115
+ # be used.
116
+ #
117
+ # NOTE: If you want to use a preset theme, you must set it before calling
118
+ # data().
119
+ #
120
+ # ==== Parameters
121
+ # name:: String or Symbol containing the name of the dataset.
122
+ # x_data_points:: An Array of of x-axis data points.
123
+ # y_data_points:: An Array of of y-axis data points.
124
+ # color:: The hex string for the color of the dataset. Defaults to nil.
125
+ #
126
+ # ==== Exceptions
127
+ # Data points contain nil values::
128
+ # This error will get raised if either the x or y axis data points array
129
+ # contains a <tt>nil</tt> value. The graph will not make an assumption
130
+ # as how to graph <tt>nil</tt>
131
+ # x_data_points is empty::
132
+ # This error is raised when the array for the x-axis points are empty
133
+ # y_data_points is empty::
134
+ # This error is raised when the array for the y-axis points are empty
135
+ # x_data_points.length != y_data_points.length::
136
+ # Error means that the x and y axis point arrays do not match in length
137
+ #
138
+ # ==== Examples
139
+ # g = Gruff::Scatter.new
140
+ # g.data(:apples, [1,2,3], [3,2,1])
141
+ # g.data('oranges', [1,1,1], [2,3,4])
142
+ # g.data('bitter_melon', [3,5,6], [6,7,8], '#000000')
143
+ #
144
+ def data(name, x_data_points=[], y_data_points=[], color=nil)
145
+
146
+ raise ArgumentError, 'Data Points contain nil Value!' if x_data_points.include?(nil) || y_data_points.include?(nil)
147
+ raise ArgumentError, 'x_data_points is empty!' if x_data_points.empty?
148
+ raise ArgumentError, 'y_data_points is empty!' if y_data_points.empty?
149
+ raise ArgumentError, 'x_data_points.length != y_data_points.length!' if x_data_points.length != y_data_points.length
150
+
151
+ # Call the existing data routine for the y axis data
152
+ super(name, y_data_points, color)
153
+
154
+ #append the x data to the last entry that was just added in the @data member
155
+ last_elem = @data.length()-1
156
+ @data[last_elem] << x_data_points
157
+
158
+ if @maximum_x_value.nil? && @minimum_x_value.nil?
159
+ @maximum_x_value = @minimum_x_value = x_data_points.first
160
+ end
161
+
162
+ @maximum_x_value = x_data_points.max > @maximum_x_value ?
163
+ x_data_points.max : @maximum_x_value
164
+ @minimum_x_value = x_data_points.min < @minimum_x_value ?
165
+ x_data_points.min : @minimum_x_value
166
+ end
167
+
168
+ protected
169
+
170
+ def calculate_spread #:nodoc:
171
+ super
172
+ @x_spread = @maximum_x_value.to_f - @minimum_x_value.to_f
173
+ @x_spread = @x_spread > 0 ? @x_spread : 1
174
+ end
175
+
176
+ def normalize(force=@xy_normalize)
177
+ if @norm_data.nil? || force
178
+ @norm_data = []
179
+ return unless @has_data
180
+
181
+ @data.each do |data_row|
182
+ norm_data_points = [data_row[DATA_LABEL_INDEX]]
183
+ norm_data_points << data_row[DATA_VALUES_INDEX].map do |r|
184
+ (r.to_f - @minimum_value.to_f) / @spread
185
+ end
186
+ norm_data_points << data_row[DATA_COLOR_INDEX]
187
+ norm_data_points << data_row[DATA_VALUES_X_INDEX].map do |r|
188
+ (r.to_f - @minimum_x_value.to_f) / @x_spread
189
+ end
190
+ @norm_data << norm_data_points
191
+ end
192
+ end
193
+ #~ @norm_y_baseline = (@baseline_y_value.to_f / @maximum_value.to_f) if @baseline_y_value
194
+ #~ @norm_x_baseline = (@baseline_x_value.to_f / @maximum_x_value.to_f) if @baseline_x_value
195
+ end
196
+
197
+ def draw_line_markers
198
+ # do all of the stuff for the horizontal lines on the y-axis
199
+ super
200
+ return if @hide_line_markers
201
+
202
+ @d = @d.stroke_antialias false
203
+
204
+ if @x_axis_increment.nil?
205
+ # TODO Do the same for larger numbers...100, 75, 50, 25
206
+ if @marker_x_count.nil?
207
+ (3..7).each do |lines|
208
+ if @x_spread % lines == 0.0
209
+ @marker_x_count = lines
210
+ break
211
+ end
212
+ end
213
+ @marker_x_count ||= 4
214
+ end
215
+ @x_increment = (@x_spread > 0) ? significant(@x_spread / @marker_x_count) : 1
216
+ else
217
+ # TODO Make this work for negative values
218
+ @maximum_x_value = [@maximum_value.ceil, @x_axis_increment].max
219
+ @minimum_x_value = @minimum_x_value.floor
220
+ calculate_spread
221
+ normalize(true)
222
+
223
+ @marker_count = (@x_spread / @x_axis_increment).to_i
224
+ @x_increment = @x_axis_increment
225
+ end
226
+ @increment_x_scaled = @graph_width.to_f / (@x_spread / @x_increment)
227
+
228
+ # Draw vertical line markers and annotate with numbers
229
+ (0..@marker_x_count).each do |index|
230
+
231
+ # TODO Fix the vertical lines. Not pretty when they don't match up with top y-axis line
232
+ # x = @graph_left + @graph_width - index.to_f * @increment_x_scaled
233
+ # @d = @d.stroke(@marker_color)
234
+ # @d = @d.stroke_width 1
235
+ # @d = @d.line(x, @graph_top, x, @graph_bottom)
236
+
237
+ unless @hide_line_numbers
238
+ marker_label = index * @x_increment + @minimum_x_value.to_f
239
+ y_offset = @graph_bottom + LABEL_MARGIN
240
+ x_offset = get_x_coord(index.to_f, @increment_x_scaled, @graph_left)
241
+
242
+ @d.fill = @font_color
243
+ @d.font = @font if @font
244
+ @d.stroke('transparent')
245
+ @d.pointsize = scale_fontsize(@marker_font_size)
246
+ @d.gravity = NorthGravity
247
+
248
+ @d = @d.annotate_scaled(@base_image,
249
+ 1.0, 1.0,
250
+ x_offset, y_offset,
251
+ label(marker_label, @x_increment), @scale)
252
+ end
253
+ end
254
+
255
+ @d = @d.stroke_antialias true
256
+ end
257
+
258
+ private
259
+
260
+ def get_x_coord(x_data_point, width, offset) #:nodoc:
261
+ x_data_point * width + offset
262
+ end
263
+
264
+ end # end Gruff::Scatter
@@ -0,0 +1,209 @@
1
+
2
+ require "observer"
3
+ require File.dirname(__FILE__) + '/base'
4
+
5
+ ##
6
+ # A scene is a non-linear graph that assembles layers together to tell a story.
7
+ # Layers are folders with appropriately named files (see below). You can group
8
+ # layers and control them together or just set their values individually.
9
+ #
10
+ # Examples:
11
+ #
12
+ # * A city scene that changes with the time of day and the weather conditions.
13
+ # * A traffic map that shows red lines on streets that are crowded and green on free-flowing ones.
14
+ #
15
+ # Usage:
16
+ #
17
+ # g = Gruff::Scene.new("500x100", "path/to/city_scene_directory")
18
+ #
19
+ # # Define order of layers, back to front
20
+ # g.layers = %w(background haze sky clouds)
21
+ #
22
+ # # Define groups that will be controlled by the same input
23
+ # g.weather_group = %w(clouds)
24
+ # g.time_group = %w(background sky)
25
+ #
26
+ # # Set values for the layers or groups
27
+ # g.weather = "cloudy"
28
+ # g.time = Time.now
29
+ # g.haze = true
30
+ #
31
+ # # Write the final graph to disk
32
+ # g.write "hazy_daytime_city_scene.png"
33
+ #
34
+ #
35
+ # There are several rules that will magically select a layer when possible.
36
+ #
37
+ # * Numbered files will be selected according to the closest value that is less than the input value.
38
+ # * 'true.png' and 'false.png' will be used as booleans.
39
+ # * Other named files will be used if the input matches the filename (without the filetype extension).
40
+ # * If there is a file named 'default.png', it will be used unless other input values are set for the corresponding layer.
41
+
42
+ class Gruff::Scene < Gruff::Base
43
+
44
+ # An array listing the foldernames that will be rendered, from back to front.
45
+ #
46
+ # g.layers = %w(sky clouds buildings street people)
47
+ #
48
+ attr_reader :layers
49
+
50
+ def initialize(target_width, base_dir)
51
+ @base_dir = base_dir
52
+ @groups = {}
53
+ @layers = []
54
+ super target_width
55
+ end
56
+
57
+ def draw
58
+ # Join all the custom paths and filter out the empty ones
59
+ image_paths = @layers.map { |layer| layer.path }.select { |path| !path.empty? }
60
+ images = Magick::ImageList.new(*image_paths)
61
+ @base_image = images.flatten_images
62
+ end
63
+
64
+ def layers=(ordered_list)
65
+ ordered_list.each do |layer_name|
66
+ @layers << Gruff::Layer.new(@base_dir, layer_name)
67
+ end
68
+ end
69
+
70
+ # Group layers to input values
71
+ #
72
+ # g.weather_group = ["sky", "sea", "clouds"]
73
+ #
74
+ # Set input values
75
+ #
76
+ # g.weather = "cloudy"
77
+ #
78
+ def method_missing(method_name, *args)
79
+ case method_name.to_s
80
+ when /^(\w+)_group=$/
81
+ add_group $1, *args
82
+ return
83
+ when /^(\w+)=$/
84
+ set_input $1, args.first
85
+ return
86
+ end
87
+ super
88
+ end
89
+
90
+ private
91
+
92
+ def add_group(input_name, layer_names)
93
+ @groups[input_name] = Gruff::Group.new(input_name, @layers.select { |layer| layer_names.include?(layer.name) })
94
+ end
95
+
96
+ def set_input(input_name, input_value)
97
+ if not @groups[input_name].nil?
98
+ @groups[input_name].send_updates(input_value)
99
+ else
100
+ if chosen_layer = @layers.detect { |layer| layer.name == input_name }
101
+ chosen_layer.update input_value
102
+ end
103
+ end
104
+ end
105
+
106
+ end
107
+
108
+
109
+ class Gruff::Group
110
+
111
+ include Observable
112
+ attr_reader :name
113
+
114
+ def initialize(folder_name, layers)
115
+ @name = folder_name
116
+ layers.each do |layer|
117
+ layer.observe self
118
+ end
119
+ end
120
+
121
+ def send_updates(value)
122
+ changed
123
+ notify_observers value
124
+ end
125
+
126
+ end
127
+
128
+
129
+ class Gruff::Layer
130
+
131
+ attr_reader :name
132
+
133
+ def initialize(base_dir, folder_name)
134
+ @base_dir = base_dir.to_s
135
+ @name = folder_name.to_s
136
+ @filenames = Dir.open(File.join(base_dir, folder_name)).entries.select { |file| file =~ /^[^.]+\.png$/ }.sort
137
+ @selected_filename = select_default
138
+ end
139
+
140
+ # Register this layer so it receives updates from the group
141
+ def observe(obj)
142
+ obj.add_observer self
143
+ end
144
+
145
+ # Choose the appropriate filename for this layer, based on the input
146
+ def update(value)
147
+ @selected_filename = case value.to_s
148
+ when /^(true|false)$/
149
+ select_boolean value
150
+ when /^(\w|\s)+$/
151
+ select_string value
152
+ when /^-?(\d+\.)?\d+$/
153
+ select_numeric value
154
+ when /(\d\d):(\d\d):\d\d/
155
+ select_time "#{$1}#{$2}"
156
+ else
157
+ select_default
158
+ end
159
+ # Finally, try to use 'default' if we're still blank
160
+ @selected_filename ||= select_default
161
+ end
162
+
163
+ # Returns the full path to the selected image, or a blank string
164
+ def path
165
+ unless @selected_filename.nil? || @selected_filename.empty?
166
+ return File.join(@base_dir, @name, @selected_filename)
167
+ end
168
+ ''
169
+ end
170
+
171
+ private
172
+
173
+ # Match "true.png" or "false.png"
174
+ def select_boolean(value)
175
+ file_exists_or_blank value.to_s
176
+ end
177
+
178
+ # Match -5 to _5.png
179
+ def select_numeric(value)
180
+ file_exists_or_blank value.to_s.gsub('-', '_')
181
+ end
182
+
183
+ def select_time(value)
184
+ times = @filenames.map { |filename| filename.gsub('.png', '') }
185
+ times.each_with_index do |time, index|
186
+ if (time > value) && (index > 0)
187
+ return "#{times[index - 1]}.png"
188
+ end
189
+ end
190
+ return "#{times.last}.png"
191
+ end
192
+
193
+ # Match "partly cloudy" to "partly_cloudy.png"
194
+ def select_string(value)
195
+ file_exists_or_blank value.to_s.gsub(' ', '_')
196
+ end
197
+
198
+ def select_default
199
+ @filenames.include?("default.png") ? "default.png" : ''
200
+ end
201
+
202
+ # Returns the string "#{filename}.png", if it exists.
203
+ #
204
+ # Failing that, it returns default.png, or '' if that doesn't exist.
205
+ def file_exists_or_blank(filename)
206
+ @filenames.include?("#{filename}.png") ? "#{filename}.png" : select_default
207
+ end
208
+
209
+ end