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.
- checksums.yaml +7 -0
- data/.gitignore +2 -1
- data/.travis.yml +6 -0
- data/Gemfile +2 -8
- data/History.txt +76 -0
- data/MIT-LICENSE +3 -2
- data/README.md +96 -6
- data/RELEASE.md +92 -0
- data/Rakefile +203 -23
- data/gruff.gemspec +13 -10
- data/lib/gruff.rb +8 -7
- data/lib/gruff/accumulator_bar.rb +4 -13
- data/lib/gruff/area.rb +6 -13
- data/lib/gruff/bar.rb +24 -3
- data/lib/gruff/bar_conversion.rb +31 -31
- data/lib/gruff/base.rb +310 -267
- data/lib/gruff/bezier.rb +46 -0
- data/lib/gruff/bullet.rb +3 -1
- data/lib/gruff/dot.rb +56 -44
- data/lib/gruff/line.rb +83 -69
- data/lib/gruff/net.rb +7 -9
- data/lib/gruff/pie.rb +2 -4
- data/lib/gruff/scatter.rb +25 -29
- data/lib/gruff/side_bar.rb +54 -37
- data/lib/gruff/side_stacked_bar.rb +25 -5
- data/lib/gruff/spider.rb +22 -21
- data/lib/gruff/themes.rb +102 -0
- data/lib/gruff/version.rb +1 -1
- data/test/gruff_test_case.rb +68 -38
- data/test/image_compare.rb +58 -0
- data/test/output/accum_bar.png +0 -0
- data/test/output/area_keynote.png +0 -0
- data/test/output/area_keynote_small.png +0 -0
- data/test/output/area_many.png +0 -0
- data/test/output/area_many_areas_small.png +0 -0
- data/test/output/area_tiny.png +0 -0
- data/test/output/area_wide.png +0 -0
- data/test/output/bar2_wrapped_legend_16_400.png +0 -0
- data/test/output/bar2_wrapped_legend_16_800.png +0 -0
- data/test/output/bar2_wrapped_legend_30_400.png +0 -0
- data/test/output/bar2_wrapped_legend_30_800.png +0 -0
- data/test/output/bar2_wrapped_legend_4_400.png +0 -0
- data/test/output/bar2_wrapped_legend_4_800.png +0 -0
- data/test/output/bar2_wrapped_legend__400.png +0 -0
- data/test/output/bar2_wrapped_legend__800.png +0 -0
- data/test/output/bar_background_gradient_bottom_top.png +0 -0
- data/test/output/bar_background_gradient_left_right.png +0 -0
- data/test/output/bar_background_gradient_right_left.png +0 -0
- data/test/output/bar_background_gradient_top_bottom.png +0 -0
- data/test/output/bar_background_gradient_topleft_bottomright.png +0 -0
- data/test/output/bar_background_gradient_topright_bottomleft.png +0 -0
- data/test/output/bar_formatted_numbers.png +0 -0
- data/test/output/bar_keynote.png +0 -0
- data/test/output/bar_keynote_small.png +0 -0
- data/test/output/bar_long_legend_text.png +0 -0
- data/test/output/bar_manual_colors.png +0 -0
- data/test/output/bar_marker_shadow.png +0 -0
- data/test/output/bar_nearly_zero_max_10.png +0 -0
- data/test/output/bar_no_legend.png +0 -0
- data/test/output/bar_no_line_markers.png +0 -0
- data/test/output/bar_no_title.png +0 -0
- data/test/output/bar_no_title_or_legend.png +0 -0
- data/test/output/bar_odeo.png +0 -0
- data/test/output/bar_one_value.png +0 -0
- data/test/output/bar_pos_neg.png +0 -0
- data/test/output/bar_rails_keynote.png +0 -0
- data/test/output/bar_set_absolute_trunc.png +0 -0
- data/test/output/bar_set_label_stagger_height.png +0 -0
- data/test/output/bar_set_legend_box_size_lg.png +0 -0
- data/test/output/bar_set_legend_box_size_sm.png +0 -0
- data/test/output/bar_set_marker.png +0 -0
- data/test/output/bar_set_trailing_dots_trunc.png +0 -0
- data/test/output/bar_spacing_full.png +0 -0
- data/test/output/bar_spacing_half.png +0 -0
- data/test/output/bar_spacing_none.png +0 -0
- data/test/output/bar_tall_graph.png +0 -0
- data/test/output/bar_tall_graph_small.png +0 -0
- data/test/output/bar_themed.png +0 -0
- data/test/output/bar_title_margin.png +0 -0
- data/test/output/bar_value_labels.png +0 -0
- data/test/output/bar_wide_graph.png +0 -0
- data/test/output/bar_wide_graph_small.png +0 -0
- data/test/output/bar_wrapped_legend_16_400.png +0 -0
- data/test/output/bar_wrapped_legend_16_800.png +0 -0
- data/test/output/bar_wrapped_legend_30_400.png +0 -0
- data/test/output/bar_wrapped_legend_30_800.png +0 -0
- data/test/output/bar_wrapped_legend_4_400.png +0 -0
- data/test/output/bar_wrapped_legend_4_800.png +0 -0
- data/test/output/bar_wrapped_legend__400.png +0 -0
- data/test/output/bar_wrapped_legend__800.png +0 -0
- data/test/output/bar_x_y_labels.png +0 -0
- data/test/output/bar_y_increment_1.png +0 -0
- data/test/output/bar_y_increment_2.0.png +0 -0
- data/test/output/bar_y_increment_20.png +0 -0
- data/test/output/bar_y_increment_5.png +0 -0
- data/test/output/bar_zero_marker_count.png +0 -0
- data/test/output/bar_zero_spacing.png +0 -0
- data/test/output/bezier.png +0 -0
- data/test/output/bezier_2.png +0 -0
- data/test/output/bezier_3.png +0 -0
- data/test/output/bullet_greyscale.png +0 -0
- data/test/output/bullet_no_options.png +0 -0
- data/test/output/dot.png +0 -0
- data/test/output/dot_manual_colors.png +0 -0
- data/test/output/dot_nearly_zero_max_10.png +0 -0
- data/test/output/dot_no_legend.png +0 -0
- data/test/output/dot_no_line_markers.png +0 -0
- data/test/output/dot_no_title.png +0 -0
- data/test/output/dot_no_title_or_legend.png +0 -0
- data/test/output/dot_one_value.png +0 -0
- data/test/output/dot_pos_neg.png +0 -0
- data/test/output/dot_set_legend_box_size_lg.png +0 -0
- data/test/output/dot_set_legend_box_size_sm.png +0 -0
- data/test/output/dot_set_marker.png +0 -0
- data/test/output/dot_small.png +0 -0
- data/test/output/dot_tall_graph.png +0 -0
- data/test/output/dot_tall_graph_small.png +0 -0
- data/test/output/dot_themed.png +0 -0
- data/test/output/dot_wide_graph.png +0 -0
- data/test/output/dot_wide_graph_small.png +0 -0
- data/test/output/dot_x_y_labels.png +0 -0
- data/test/output/dot_y_increment_1.png +0 -0
- data/test/output/dot_y_increment_2.0.png +0 -0
- data/test/output/dot_y_increment_20.png +0 -0
- data/test/output/dot_y_increment_5.png +0 -0
- data/test/output/enhancements-1.png +0 -0
- data/test/output/enhancements.png +0 -0
- data/test/output/line_all_neg.png +0 -0
- data/test/output/line_all_neg_400.png +0 -0
- data/test/output/line_all_neg_no_max.png +0 -0
- data/test/output/line_baseline.png +0 -0
- data/test/output/line_hang_value.png +0 -0
- data/test/output/line_hide_dots.png +0 -0
- data/test/output/line_hide_lines.png +0 -0
- data/test/output/line_jruby_error.png +0 -0
- data/test/output/line_large.png +0 -0
- data/test/output/line_large_baseline.png +0 -0
- data/test/output/line_legend_at_bottom.png +0 -0
- data/test/output/line_many.png +0 -0
- data/test/output/line_many_lines_small.png +0 -0
- data/test/output/line_many_numbers.png +0 -0
- data/test/output/line_marker_label_accuracy.png +0 -0
- data/test/output/line_more_sets_than_colors.png +0 -0
- data/test/output/line_no_data.png +0 -0
- data/test/output/line_no_data_msg.png +0 -0
- data/test/output/line_no_data_other.png +0 -0
- data/test/output/line_no_hide.png +0 -0
- data/test/output/line_no_legend.png +0 -0
- data/test/output/line_no_line_markers.png +0 -0
- data/test/output/line_no_title.png +0 -0
- data/test/output/line_nothing_but_the_graph.png +0 -0
- data/test/output/line_one_value.png +0 -0
- data/test/output/line_one_value_array.png +0 -0
- data/test/output/line_pos_neg.png +0 -0
- data/test/output/line_pos_neg_400.png +0 -0
- data/test/output/line_similar_high_end_values.png +0 -0
- data/test/output/line_similar_high_end_values_with_floor.png +0 -0
- data/test/output/line_small_small_zero.png +0 -0
- data/test/output/line_small_values.png +0 -0
- data/test/output/line_small_values_small.png +0 -0
- data/test/output/line_small_zero.png +0 -0
- data/test/output/line_some_nil_points.png +0 -0
- data/test/output/line_theme_37signals_.png +0 -0
- data/test/output/line_theme_37signals_400.png +0 -0
- data/test/output/line_theme_keynote_.png +0 -0
- data/test/output/line_theme_keynote_400.png +0 -0
- data/test/output/line_theme_odeo_.png +0 -0
- data/test/output/line_theme_odeo_400.png +0 -0
- data/test/output/line_theme_rails_keynote_.png +0 -0
- data/test/output/line_theme_rails_keynote_400.png +0 -0
- data/test/output/line_tiny.png +0 -0
- data/test/output/line_transparent.png +0 -0
- data/test/output/line_very_small.png +0 -0
- data/test/output/line_wide_graph.png +0 -0
- data/test/output/line_wide_graph_small.png +0 -0
- data/test/output/line_xy.png +0 -0
- data/test/output/line_xy_pairs.png +0 -0
- data/test/output/line_y_axis_increment.png +0 -0
- data/test/output/mini_bar.png +0 -0
- data/test/output/mini_pie.png +0 -0
- data/test/output/mini_pie_right_legend.png +0 -0
- data/test/output/mini_side_bar.png +0 -0
- data/test/output/mini_side_bar_multi_color.png +0 -0
- data/test/output/net_dots_tiny.png +0 -0
- data/test/output/net_large.png +0 -0
- data/test/output/net_many.png +0 -0
- data/test/output/net_many_nets_small.png +0 -0
- data/test/output/net_no_data.png +0 -0
- data/test/output/net_no_data_msg.png +0 -0
- data/test/output/net_no_data_other.png +0 -0
- data/test/output/net_no_legend.png +0 -0
- data/test/output/net_no_net_markers.png +0 -0
- data/test/output/net_no_title.png +0 -0
- data/test/output/net_nothing_but_the_graph.png +0 -0
- data/test/output/net_similar_high_end_values.png +0 -0
- data/test/output/net_small.png +0 -0
- data/test/output/net_small_small.png +0 -0
- data/test/output/net_small_small_zero.png +0 -0
- data/test/output/net_small_zero.png +0 -0
- data/test/output/net_wide_graph.png +0 -0
- data/test/output/net_wide_graph_small.png +0 -0
- data/test/output/pie_equal.png +0 -0
- data/test/output/pie_grey.png +0 -0
- data/test/output/pie_keynote.png +0 -0
- data/test/output/pie_keynote_small.png +0 -0
- data/test/output/pie_legend.png +0 -0
- data/test/output/pie_legend_small.png +0 -0
- data/test/output/pie_nearly_equal.png +0 -0
- data/test/output/pie_one_val.png +0 -0
- data/test/output/pie_pastel.png +0 -0
- data/test/output/pie_simple.png +0 -0
- data/test/output/pie_wide.png +0 -0
- data/test/output/pie_wrapped_legend_16_400.png +0 -0
- data/test/output/pie_wrapped_legend_16_800.png +0 -0
- data/test/output/pie_wrapped_legend_30_400.png +0 -0
- data/test/output/pie_wrapped_legend_30_800.png +0 -0
- data/test/output/pie_wrapped_legend_4_400.png +0 -0
- data/test/output/pie_wrapped_legend_4_800.png +0 -0
- data/test/output/pie_wrapped_legend__400.png +0 -0
- data/test/output/pie_wrapped_legend__800.png +0 -0
- data/test/output/pie_zero.png +0 -0
- data/test/output/scatter_all_neg.png +0 -0
- data/test/output/scatter_all_neg_400.png +0 -0
- data/test/output/scatter_basic.png +0 -0
- data/test/output/scatter_many.png +0 -0
- data/test/output/scatter_no_data.png +0 -0
- data/test/output/scatter_no_data_msg.png +0 -0
- data/test/output/scatter_no_data_other.png +0 -0
- data/test/output/scatter_no_hide.png +0 -0
- data/test/output/scatter_no_labels.png +0 -0
- data/test/output/scatter_no_legend.png +0 -0
- data/test/output/scatter_no_line_markers.png +0 -0
- data/test/output/scatter_no_title.png +0 -0
- data/test/output/scatter_nothing_but_the_graph.png +0 -0
- data/test/output/scatter_pos_neg.png +0 -0
- data/test/output/scatter_pos_neg_400.png +0 -0
- data/test/output/scatter_wide_graph.png +0 -0
- data/test/output/scatter_wide_graph_small.png +0 -0
- data/test/output/scene_hazy_night.png +0 -0
- data/test/output/scene_not_hazy_day.png +0 -0
- data/test/output/scene_partly_cloudy_day.png +0 -0
- data/test/output/scene_stormy_day.png +0 -0
- data/test/output/scene_stormy_night.png +0 -0
- data/test/output/side_bar.png +0 -0
- data/test/output/side_bar_data_range.png +0 -0
- data/test/output/side_bar_labels.png +0 -0
- data/test/output/side_bar_spacing_full.png +0 -0
- data/test/output/side_bar_spacing_half.png +0 -0
- data/test/output/side_bar_spacing_none.png +0 -0
- data/test/output/side_stacked_bar_keynote.png +0 -0
- data/test/output/side_stacked_bar_keynote_small.png +0 -0
- data/test/output/side_stacked_bar_labels.png +0 -0
- data/test/output/side_stacked_bar_long_label.png +0 -0
- data/test/output/side_stacked_bar_wide.png +0 -0
- data/test/output/spider_37signals.png +0 -0
- data/test/output/spider_equal.png +0 -0
- data/test/output/spider_keynote.png +0 -0
- data/test/output/spider_legend.png +0 -0
- data/test/output/spider_legend_small.png +0 -0
- data/test/output/spider_lots_of_data.png +0 -0
- data/test/output/spider_lots_of_data_normal_names.png +0 -0
- data/test/output/spider_nearly_equal.png +0 -0
- data/test/output/spider_no_axes.png +0 -0
- data/test/output/spider_no_background.png +0 -0
- data/test/output/spider_no_print.png +0 -0
- data/test/output/spider_overlay_1.png +0 -0
- data/test/output/spider_overlay_2.png +0 -0
- data/test/output/spider_rotation.png +0 -0
- data/test/output/spider_small.png +0 -0
- data/test/output/spider_wide.png +0 -0
- data/test/output/spider_zero.png +0 -0
- data/test/output/stacked_area_keynote.png +0 -0
- data/test/output/stacked_area_keynote_small.png +0 -0
- data/test/output/stacked_bar_keynote.png +0 -0
- data/test/output/stacked_bar_keynote_small.png +0 -0
- data/test/output_java/accum_bar.png +0 -0
- data/test/output_java/area_keynote.png +0 -0
- data/test/output_java/area_keynote_small.png +0 -0
- data/test/output_java/area_many.png +0 -0
- data/test/output_java/area_many_areas_small.png +0 -0
- data/test/output_java/area_tiny.png +0 -0
- data/test/output_java/area_wide.png +0 -0
- data/test/output_java/bar2_wrapped_legend_16_400.png +0 -0
- data/test/output_java/bar2_wrapped_legend_16_800.png +0 -0
- data/test/output_java/bar2_wrapped_legend_30_400.png +0 -0
- data/test/output_java/bar2_wrapped_legend_30_800.png +0 -0
- data/test/output_java/bar2_wrapped_legend_4_400.png +0 -0
- data/test/output_java/bar2_wrapped_legend_4_800.png +0 -0
- data/test/output_java/bar2_wrapped_legend__400.png +0 -0
- data/test/output_java/bar2_wrapped_legend__800.png +0 -0
- data/test/output_java/bar_background_gradient_bottom_top.png +0 -0
- data/test/output_java/bar_background_gradient_left_right.png +0 -0
- data/test/output_java/bar_background_gradient_right_left.png +0 -0
- data/test/output_java/bar_background_gradient_top_bottom.png +0 -0
- data/test/output_java/bar_background_gradient_topleft_bottomright.png +0 -0
- data/test/output_java/bar_background_gradient_topright_bottomleft.png +0 -0
- data/test/output_java/bar_formatted_numbers.png +0 -0
- data/test/output_java/bar_keynote.png +0 -0
- data/test/output_java/bar_keynote_small.png +0 -0
- data/test/output_java/bar_long_legend_text.png +0 -0
- data/test/output_java/bar_manual_colors.png +0 -0
- data/test/output_java/bar_marker_shadow.png +0 -0
- data/test/output_java/bar_nearly_zero_max_10.png +0 -0
- data/test/output_java/bar_no_legend.png +0 -0
- data/test/output_java/bar_no_line_markers.png +0 -0
- data/test/output_java/bar_no_title.png +0 -0
- data/test/output_java/bar_no_title_or_legend.png +0 -0
- data/test/output_java/bar_odeo.png +0 -0
- data/test/output_java/bar_one_value.png +0 -0
- data/test/output_java/bar_pos_neg.png +0 -0
- data/test/output_java/bar_rails_keynote.png +0 -0
- data/test/output_java/bar_set_absolute_trunc.png +0 -0
- data/test/output_java/bar_set_label_stagger_height.png +0 -0
- data/test/output_java/bar_set_legend_box_size_lg.png +0 -0
- data/test/output_java/bar_set_legend_box_size_sm.png +0 -0
- data/test/output_java/bar_set_marker.png +0 -0
- data/test/output_java/bar_set_trailing_dots_trunc.png +0 -0
- data/test/output_java/bar_spacing_full.png +0 -0
- data/test/output_java/bar_spacing_half.png +0 -0
- data/test/output_java/bar_spacing_none.png +0 -0
- data/test/output_java/bar_tall_graph.png +0 -0
- data/test/output_java/bar_tall_graph_small.png +0 -0
- data/test/output_java/bar_themed.png +0 -0
- data/test/output_java/bar_title_margin.png +0 -0
- data/test/output_java/bar_value_labels.png +0 -0
- data/test/output_java/bar_wide_graph.png +0 -0
- data/test/output_java/bar_wide_graph_small.png +0 -0
- data/test/output_java/bar_wrapped_legend_16_400.png +0 -0
- data/test/output_java/bar_wrapped_legend_16_800.png +0 -0
- data/test/output_java/bar_wrapped_legend_30_400.png +0 -0
- data/test/output_java/bar_wrapped_legend_30_800.png +0 -0
- data/test/output_java/bar_wrapped_legend_4_400.png +0 -0
- data/test/output_java/bar_wrapped_legend_4_800.png +0 -0
- data/test/output_java/bar_wrapped_legend__400.png +0 -0
- data/test/output_java/bar_wrapped_legend__800.png +0 -0
- data/test/output_java/bar_x_y_labels.png +0 -0
- data/test/output_java/bar_y_increment_1.png +0 -0
- data/test/output_java/bar_y_increment_2.0.png +0 -0
- data/test/output_java/bar_y_increment_20.png +0 -0
- data/test/output_java/bar_y_increment_5.png +0 -0
- data/test/output_java/bar_zero_marker_count.png +0 -0
- data/test/output_java/bar_zero_spacing.png +0 -0
- data/test/output_java/bezier.png +0 -0
- data/test/output_java/bezier_2.png +0 -0
- data/test/output_java/bezier_3.png +0 -0
- data/test/output_java/bullet_greyscale.png +0 -0
- data/test/output_java/bullet_no_options.png +0 -0
- data/test/output_java/dot.png +0 -0
- data/test/output_java/dot_manual_colors.png +0 -0
- data/test/output_java/dot_nearly_zero_max_10.png +0 -0
- data/test/output_java/dot_no_legend.png +0 -0
- data/test/output_java/dot_no_line_markers.png +0 -0
- data/test/output_java/dot_no_title.png +0 -0
- data/test/output_java/dot_no_title_or_legend.png +0 -0
- data/test/output_java/dot_one_value.png +0 -0
- data/test/output_java/dot_pos_neg.png +0 -0
- data/test/output_java/dot_set_legend_box_size_lg.png +0 -0
- data/test/output_java/dot_set_legend_box_size_sm.png +0 -0
- data/test/output_java/dot_set_marker.png +0 -0
- data/test/output_java/dot_small.png +0 -0
- data/test/output_java/dot_tall_graph.png +0 -0
- data/test/output_java/dot_tall_graph_small.png +0 -0
- data/test/output_java/dot_themed.png +0 -0
- data/test/output_java/dot_wide_graph.png +0 -0
- data/test/output_java/dot_wide_graph_small.png +0 -0
- data/test/output_java/dot_x_y_labels.png +0 -0
- data/test/output_java/dot_y_increment_1.png +0 -0
- data/test/output_java/dot_y_increment_2.0.png +0 -0
- data/test/output_java/dot_y_increment_20.png +0 -0
- data/test/output_java/dot_y_increment_5.png +0 -0
- data/test/output_java/enhancements-1.png +0 -0
- data/test/output_java/enhancements.png +0 -0
- data/test/output_java/line_all_neg.png +0 -0
- data/test/output_java/line_all_neg_400.png +0 -0
- data/test/output_java/line_all_neg_no_max.png +0 -0
- data/test/output_java/line_baseline.png +0 -0
- data/test/output_java/line_hang_value.png +0 -0
- data/test/output_java/line_hide_dots.png +0 -0
- data/test/output_java/line_hide_lines.png +0 -0
- data/test/output_java/line_jruby_error.png +0 -0
- data/test/output_java/line_large.png +0 -0
- data/test/output_java/line_large_baseline.png +0 -0
- data/test/output_java/line_legend_at_bottom.png +0 -0
- data/test/output_java/line_many.png +0 -0
- data/test/output_java/line_many_lines_small.png +0 -0
- data/test/output_java/line_many_numbers.png +0 -0
- data/test/output_java/line_marker_label_accuracy.png +0 -0
- data/test/output_java/line_more_sets_than_colors.png +0 -0
- data/test/output_java/line_no_data.png +0 -0
- data/test/output_java/line_no_data_msg.png +0 -0
- data/test/output_java/line_no_data_other.png +0 -0
- data/test/output_java/line_no_hide.png +0 -0
- data/test/output_java/line_no_legend.png +0 -0
- data/test/output_java/line_no_line_markers.png +0 -0
- data/test/output_java/line_no_title.png +0 -0
- data/test/output_java/line_nothing_but_the_graph.png +0 -0
- data/test/output_java/line_one_value.png +0 -0
- data/test/output_java/line_one_value_array.png +0 -0
- data/test/output_java/line_pos_neg.png +0 -0
- data/test/output_java/line_pos_neg_400.png +0 -0
- data/test/output_java/line_similar_high_end_values.png +0 -0
- data/test/output_java/line_similar_high_end_values_with_floor.png +0 -0
- data/test/output_java/line_small_small_zero.png +0 -0
- data/test/output_java/line_small_values.png +0 -0
- data/test/output_java/line_small_values_small.png +0 -0
- data/test/output_java/line_small_zero.png +0 -0
- data/test/output_java/line_some_nil_points.png +0 -0
- data/test/output_java/line_theme_37signals_.png +0 -0
- data/test/output_java/line_theme_37signals_400.png +0 -0
- data/test/output_java/line_theme_keynote_.png +0 -0
- data/test/output_java/line_theme_keynote_400.png +0 -0
- data/test/output_java/line_theme_odeo_.png +0 -0
- data/test/output_java/line_theme_odeo_400.png +0 -0
- data/test/output_java/line_theme_rails_keynote_.png +0 -0
- data/test/output_java/line_theme_rails_keynote_400.png +0 -0
- data/test/output_java/line_tiny.png +0 -0
- data/test/output_java/line_transparent.png +0 -0
- data/test/output_java/line_very_small.png +0 -0
- data/test/output_java/line_wide_graph.png +0 -0
- data/test/output_java/line_wide_graph_small.png +0 -0
- data/test/output_java/line_xy.png +0 -0
- data/test/output_java/line_xy_pairs.png +0 -0
- data/test/output_java/line_y_axis_increment.png +0 -0
- data/test/output_java/mini_bar.png +0 -0
- data/test/output_java/mini_pie.png +0 -0
- data/test/output_java/mini_pie_right_legend.png +0 -0
- data/test/output_java/mini_side_bar.png +0 -0
- data/test/output_java/mini_side_bar_multi_color.png +0 -0
- data/test/output_java/net_dots_tiny.png +0 -0
- data/test/output_java/net_large.png +0 -0
- data/test/output_java/net_many.png +0 -0
- data/test/output_java/net_many_nets_small.png +0 -0
- data/test/output_java/net_no_data.png +0 -0
- data/test/output_java/net_no_data_msg.png +0 -0
- data/test/output_java/net_no_data_other.png +0 -0
- data/test/output_java/net_no_legend.png +0 -0
- data/test/output_java/net_no_net_markers.png +0 -0
- data/test/output_java/net_no_title.png +0 -0
- data/test/output_java/net_nothing_but_the_graph.png +0 -0
- data/test/output_java/net_similar_high_end_values.png +0 -0
- data/test/output_java/net_small.png +0 -0
- data/test/output_java/net_small_small.png +0 -0
- data/test/output_java/net_small_small_zero.png +0 -0
- data/test/output_java/net_small_zero.png +0 -0
- data/test/output_java/net_wide_graph.png +0 -0
- data/test/output_java/net_wide_graph_small.png +0 -0
- data/test/output_java/pie_equal.png +0 -0
- data/test/output_java/pie_grey.png +0 -0
- data/test/output_java/pie_keynote.png +0 -0
- data/test/output_java/pie_keynote_small.png +0 -0
- data/test/output_java/pie_legend.png +0 -0
- data/test/output_java/pie_legend_small.png +0 -0
- data/test/output_java/pie_nearly_equal.png +0 -0
- data/test/output_java/pie_one_val.png +0 -0
- data/test/output_java/pie_pastel.png +0 -0
- data/test/output_java/pie_simple.png +0 -0
- data/test/output_java/pie_wide.png +0 -0
- data/test/output_java/pie_wrapped_legend_16_400.png +0 -0
- data/test/output_java/pie_wrapped_legend_16_800.png +0 -0
- data/test/output_java/pie_wrapped_legend_30_400.png +0 -0
- data/test/output_java/pie_wrapped_legend_30_800.png +0 -0
- data/test/output_java/pie_wrapped_legend_4_400.png +0 -0
- data/test/output_java/pie_wrapped_legend_4_800.png +0 -0
- data/test/output_java/pie_wrapped_legend__400.png +0 -0
- data/test/output_java/pie_wrapped_legend__800.png +0 -0
- data/test/output_java/pie_zero.png +0 -0
- data/test/output_java/scatter_all_neg.png +0 -0
- data/test/output_java/scatter_all_neg_400.png +0 -0
- data/test/output_java/scatter_basic.png +0 -0
- data/test/output_java/scatter_many.png +0 -0
- data/test/output_java/scatter_no_data.png +0 -0
- data/test/output_java/scatter_no_data_msg.png +0 -0
- data/test/output_java/scatter_no_data_other.png +0 -0
- data/test/output_java/scatter_no_hide.png +0 -0
- data/test/output_java/scatter_no_labels.png +0 -0
- data/test/output_java/scatter_no_legend.png +0 -0
- data/test/output_java/scatter_no_line_markers.png +0 -0
- data/test/output_java/scatter_no_title.png +0 -0
- data/test/output_java/scatter_nothing_but_the_graph.png +0 -0
- data/test/output_java/scatter_pos_neg.png +0 -0
- data/test/output_java/scatter_pos_neg_400.png +0 -0
- data/test/output_java/scatter_wide_graph.png +0 -0
- data/test/output_java/scatter_wide_graph_small.png +0 -0
- data/test/output_java/scene_hazy_night.png +0 -0
- data/test/output_java/scene_not_hazy_day.png +0 -0
- data/test/output_java/scene_partly_cloudy_day.png +0 -0
- data/test/output_java/scene_stormy_day.png +0 -0
- data/test/output_java/scene_stormy_night.png +0 -0
- data/test/output_java/side_bar.png +0 -0
- data/test/output_java/side_bar_data_range.png +0 -0
- data/test/output_java/side_bar_labels.png +0 -0
- data/test/output_java/side_bar_spacing_full.png +0 -0
- data/test/output_java/side_bar_spacing_half.png +0 -0
- data/test/output_java/side_bar_spacing_none.png +0 -0
- data/test/output_java/side_stacked_bar_keynote.png +0 -0
- data/test/output_java/side_stacked_bar_keynote_small.png +0 -0
- data/test/output_java/side_stacked_bar_labels.png +0 -0
- data/test/output_java/side_stacked_bar_long_label.png +0 -0
- data/test/output_java/side_stacked_bar_wide.png +0 -0
- data/test/output_java/spider_37signals.png +0 -0
- data/test/output_java/spider_equal.png +0 -0
- data/test/output_java/spider_keynote.png +0 -0
- data/test/output_java/spider_legend.png +0 -0
- data/test/output_java/spider_legend_small.png +0 -0
- data/test/output_java/spider_lots_of_data.png +0 -0
- data/test/output_java/spider_lots_of_data_normal_names.png +0 -0
- data/test/output_java/spider_nearly_equal.png +0 -0
- data/test/output_java/spider_no_axes.png +0 -0
- data/test/output_java/spider_no_background.png +0 -0
- data/test/output_java/spider_no_print.png +0 -0
- data/test/output_java/spider_overlay_1.png +0 -0
- data/test/output_java/spider_overlay_2.png +0 -0
- data/test/output_java/spider_rotation.png +0 -0
- data/test/output_java/spider_small.png +0 -0
- data/test/output_java/spider_wide.png +0 -0
- data/test/output_java/spider_zero.png +0 -0
- data/test/output_java/stacked_area_keynote.png +0 -0
- data/test/output_java/stacked_area_keynote_small.png +0 -0
- data/test/output_java/stacked_bar_keynote.png +0 -0
- data/test/output_java/stacked_bar_keynote_small.png +0 -0
- data/test/test_accumulator_bar.rb +7 -6
- data/test/test_area.rb +2 -2
- data/test/test_bar.rb +296 -112
- data/test/test_bezier.rb +33 -0
- data/test/test_bullet.rb +26 -26
- data/test/test_dot.rb +44 -54
- data/test/test_legend.rb +29 -29
- data/test/test_line.rb +250 -218
- data/test/test_net.rb +2 -1
- data/test/test_pie.rb +2 -2
- data/test/test_scatter.rb +85 -85
- data/test/test_side_bar.rb +13 -0
- data/test/test_sidestacked_bar.rb +16 -0
- data/test/test_spider.rb +11 -1
- data/test/test_stacked_area.rb +5 -5
- metadata +1041 -49
- data/.rvmrc +0 -5
- data/Gemfile.lock +0 -20
- data/LICENSE.txt +0 -22
- 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
|
-
|
|
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 =
|
|
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.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
|
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
|
-
|
|
10
|
+
bezier
|
|
11
|
+
bullet
|
|
10
12
|
dot
|
|
11
|
-
|
|
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
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
25
|
-
|
|
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 ||=
|
|
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.
|
|
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
|
data/lib/gruff/bar_conversion.rb
CHANGED
|
@@ -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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
|
157
|
-
#
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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 =>
|
|
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
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
458
|
-
|
|
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.
|
|
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=
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
511
|
-
|
|
454
|
+
@d.rectangle(@left_margin, @top_margin,
|
|
455
|
+
@raw_columns - @right_margin, @raw_rows - @bottom_margin)
|
|
512
456
|
# Graph area box
|
|
513
|
-
@d.rectangle(
|
|
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
|
|
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
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
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
|
|
501
|
+
norm_data_points << ((data_point.to_f - @minimum_value.to_f) / @spread)
|
|
554
502
|
end
|
|
555
503
|
end
|
|
556
|
-
@
|
|
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
|
-
|
|
523
|
+
calculate_caps_height(@marker_font_size)
|
|
572
524
|
@title_caps_height = @hide_title ? 0 :
|
|
573
|
-
|
|
525
|
+
calculate_caps_height(@title_font_size)
|
|
574
526
|
@legend_caps_height = @hide_legend ? 0 :
|
|
575
|
-
|
|
527
|
+
calculate_caps_height(@legend_font_size)
|
|
576
528
|
|
|
577
529
|
if @hide_line_markers
|
|
578
530
|
(@graph_left,
|
|
579
|
-
|
|
580
|
-
|
|
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 =
|
|
585
|
-
|
|
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
|
-
|
|
594
|
-
|
|
544
|
+
0.0 :
|
|
545
|
+
(longest_left_label_width + LABEL_MARGIN * 2)
|
|
595
546
|
|
|
596
547
|
@graph_left = @left_margin +
|
|
597
|
-
|
|
598
|
-
|
|
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
|
-
|
|
605
|
-
|
|
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
|
-
|
|
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
|
-
|
|
619
|
-
|
|
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
|
-
|
|
623
|
-
|
|
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(
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
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
|
-
|
|
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(
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
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 =
|
|
787
|
-
|
|
788
|
-
|
|
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(
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
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(
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
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
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
@
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
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(
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
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(
|
|
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
|
-
|
|
915
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
987
|
+
while inc < 10
|
|
984
988
|
inc *= 10
|
|
985
989
|
factor /= 10
|
|
986
990
|
end
|
|
987
991
|
|
|
988
|
-
while
|
|
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
|
|
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
|
|
1005
|
-
|
|
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
|
-
|
|
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
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
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;
|
|
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(
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
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
|