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.
- data/.gitignore +7 -0
- data/.travis.yml +14 -0
- data/Gemfile +10 -0
- data/History.txt +193 -0
- data/MIT-LICENSE +22 -0
- data/Manifest.txt +81 -0
- data/README.md +139 -0
- data/RELEASE.md +92 -0
- data/Rakefile +218 -0
- data/assets/bubble.png +0 -0
- data/assets/city_scene/background/0000.png +0 -0
- data/assets/city_scene/background/0600.png +0 -0
- data/assets/city_scene/background/2000.png +0 -0
- data/assets/city_scene/clouds/cloudy.png +0 -0
- data/assets/city_scene/clouds/partly_cloudy.png +0 -0
- data/assets/city_scene/clouds/stormy.png +0 -0
- data/assets/city_scene/grass/default.png +0 -0
- data/assets/city_scene/haze/true.png +0 -0
- data/assets/city_scene/number_sample/1.png +0 -0
- data/assets/city_scene/number_sample/2.png +0 -0
- data/assets/city_scene/number_sample/default.png +0 -0
- data/assets/city_scene/sky/0000.png +0 -0
- data/assets/city_scene/sky/0200.png +0 -0
- data/assets/city_scene/sky/0400.png +0 -0
- data/assets/city_scene/sky/0600.png +0 -0
- data/assets/city_scene/sky/0800.png +0 -0
- data/assets/city_scene/sky/1000.png +0 -0
- data/assets/city_scene/sky/1200.png +0 -0
- data/assets/city_scene/sky/1400.png +0 -0
- data/assets/city_scene/sky/1500.png +0 -0
- data/assets/city_scene/sky/1700.png +0 -0
- data/assets/city_scene/sky/2000.png +0 -0
- data/assets/pc306715.jpg +0 -0
- data/assets/plastik/blue.png +0 -0
- data/assets/plastik/green.png +0 -0
- data/assets/plastik/red.png +0 -0
- data/gruff.gemspec +36 -0
- data/init.rb +2 -0
- data/lib/gruff.rb +32 -0
- data/lib/gruff/accumulator_bar.rb +18 -0
- data/lib/gruff/area.rb +51 -0
- data/lib/gruff/bar.rb +108 -0
- data/lib/gruff/bar_conversion.rb +46 -0
- data/lib/gruff/base.rb +1169 -0
- data/lib/gruff/bezier.rb +46 -0
- data/lib/gruff/bullet.rb +111 -0
- data/lib/gruff/deprecated.rb +39 -0
- data/lib/gruff/dot.rb +125 -0
- data/lib/gruff/line.rb +242 -0
- data/lib/gruff/mini/bar.rb +37 -0
- data/lib/gruff/mini/legend.rb +109 -0
- data/lib/gruff/mini/pie.rb +36 -0
- data/lib/gruff/mini/side_bar.rb +35 -0
- data/lib/gruff/net.rb +138 -0
- data/lib/gruff/photo_bar.rb +100 -0
- data/lib/gruff/pie.rb +124 -0
- data/lib/gruff/scatter.rb +264 -0
- data/lib/gruff/scene.rb +209 -0
- data/lib/gruff/side_bar.rb +138 -0
- data/lib/gruff/side_stacked_bar.rb +97 -0
- data/lib/gruff/spider.rb +131 -0
- data/lib/gruff/stacked_area.rb +67 -0
- data/lib/gruff/stacked_bar.rb +57 -0
- data/lib/gruff/stacked_mixin.rb +23 -0
- data/lib/gruff/themes.rb +102 -0
- data/lib/gruff/version.rb +3 -0
- data/rails_generators/gruff/gruff_generator.rb +63 -0
- data/rails_generators/gruff/templates/controller.rb +32 -0
- data/rails_generators/gruff/templates/functional_test.rb +24 -0
- data/test/gruff_test_case.rb +155 -0
- 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 +51 -0
- data/test/test_area.rb +134 -0
- data/test/test_bar.rb +505 -0
- data/test/test_base.rb +8 -0
- data/test/test_bezier.rb +33 -0
- data/test/test_bullet.rb +26 -0
- data/test/test_dot.rb +263 -0
- data/test/test_legend.rb +68 -0
- data/test/test_line.rb +626 -0
- data/test/test_mini_bar.rb +33 -0
- data/test/test_mini_pie.rb +25 -0
- data/test/test_mini_side_bar.rb +36 -0
- data/test/test_net.rb +231 -0
- data/test/test_photo.rb +41 -0
- data/test/test_pie.rb +154 -0
- data/test/test_scatter.rb +233 -0
- data/test/test_scene.rb +100 -0
- data/test/test_side_bar.rb +56 -0
- data/test/test_sidestacked_bar.rb +105 -0
- data/test/test_spider.rb +226 -0
- data/test/test_stacked_area.rb +52 -0
- data/test/test_stacked_bar.rb +52 -0
- metadata +1176 -0
data/lib/gruff/pie.rb
ADDED
|
@@ -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
|
data/lib/gruff/scene.rb
ADDED
|
@@ -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
|