prawn_charts 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. data/README.md +87 -17
  2. data/lib/prawn_charts.rb +1 -2
  3. data/lib/prawn_charts/core_extensions.rb +7 -0
  4. data/lib/prawn_charts/data_collector.rb +68 -0
  5. data/lib/prawn_charts/data_points.rb +16 -0
  6. data/lib/prawn_charts/examples/linear_example.rb +55 -0
  7. data/lib/prawn_charts/examples/log_example.rb +56 -133
  8. data/lib/prawn_charts/helpers.rb +26 -0
  9. data/lib/prawn_charts/linear_vertical_data_collector.rb +23 -0
  10. data/lib/prawn_charts/log_vertical_data_collector.rb +27 -0
  11. data/lib/prawn_charts/prawn_chart_renderer.rb +139 -0
  12. data/lib/prawn_charts/renderer_assistant.rb +146 -0
  13. data/lib/prawn_charts/version.rb +1 -1
  14. data/spec/data_collector_spec.rb +64 -0
  15. data/spec/linear_vertical_data_collector_spec.rb +17 -0
  16. data/spec/log_vertical_data_collector.rb +12 -0
  17. data/todo +5 -0
  18. metadata +18 -31
  19. data/lib/prawn_charts/data_collectors/container/container_data_collector.rb +0 -59
  20. data/lib/prawn_charts/data_collectors/container/graph_title_data_collector.rb +0 -15
  21. data/lib/prawn_charts/data_collectors/graph/horizontal_lines_data_collector.rb +0 -22
  22. data/lib/prawn_charts/data_collectors/graph/linear_y_pdf_data_collector.rb +0 -23
  23. data/lib/prawn_charts/data_collectors/graph/log_y_pdf_data_collector.rb +0 -21
  24. data/lib/prawn_charts/data_collectors/graph/pdf_data_collector.rb +0 -26
  25. data/lib/prawn_charts/data_collectors/graph/x_labels_data_collector.rb +0 -29
  26. data/lib/prawn_charts/data_collectors/graph/x_pdf_data_collector.rb +0 -16
  27. data/lib/prawn_charts/data_collectors/graph/y_labels_data_collector.rb +0 -35
  28. data/lib/prawn_charts/data_collectors/graph/y_pdf_data_collector.rb +0 -21
  29. data/lib/prawn_charts/examples/simple_linear_example.rb +0 -59
  30. data/lib/prawn_charts/examples/simple_log_example.rb +0 -59
  31. data/lib/prawn_charts/renderers/prawn_chart_renderer.rb +0 -31
  32. data/spec/data_collectors/container/container_data_collector_spec.rb +0 -59
  33. data/spec/data_collectors/graph/horizontal_lines_data_collector_spec.rb +0 -19
  34. data/spec/data_collectors/graph/linear_y_pdf_data_collector_spec.rb +0 -19
  35. data/spec/data_collectors/graph/log_y_pdf_data_collector_spec.rb +0 -26
  36. data/spec/data_collectors/graph/pdf_data_collector_spec.rb +0 -49
  37. data/spec/data_collectors/graph/x_labels_data_collector_spec.rb +0 -20
  38. data/spec/data_collectors/graph/x_pdf_data_collector_spec.rb +0 -23
  39. data/spec/data_collectors/graph/y_labels_data_collector_spec.rb +0 -21
@@ -0,0 +1,26 @@
1
+ module PrawnCharts
2
+ module Helpers
3
+ def stroke_axis(options={})
4
+ options = { :height => (cursor - 20).to_i,
5
+ :width => bounds.width.to_i
6
+ }.merge(options)
7
+
8
+ dash(1, :space => 4)
9
+ stroke_horizontal_line(-21, options[:width], :at => 0)
10
+ stroke_vertical_line(-21, options[:height], :at => 0)
11
+ undash
12
+
13
+ fill_circle [0, 0], 1
14
+
15
+ (100..options[:width]).step(100) do |point|
16
+ fill_circle [point, 0], 1
17
+ draw_text point, :at => [point-5, -10], :size => 7
18
+ end
19
+
20
+ (100..options[:height]).step(100) do |point|
21
+ fill_circle [0, point], 1
22
+ draw_text point, :at => [-17, point-2], :size => 7
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,23 @@
1
+ module PrawnCharts
2
+ class LinearVerticalDataCollector
3
+ attr_reader :height, :y_labels_units
4
+ def initialize(height, y_labels_units)
5
+ @height = height
6
+ @y_labels_units = y_labels_units
7
+ end
8
+
9
+ def convert_to_pdf(n)
10
+ pdf_points_per_y_unit * n
11
+ end
12
+
13
+ private
14
+
15
+ def pdf_points_per_y_unit
16
+ height.to_f / total_y_units
17
+ end
18
+
19
+ def total_y_units
20
+ y_labels_units.max - y_labels_units.min
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,27 @@
1
+ module PrawnCharts
2
+ class LogVerticalDataCollector
3
+ attr_reader :height, :y_labels_units
4
+ def initialize(height, y_labels_units)
5
+ @height = height
6
+ @y_labels_units = y_labels_units
7
+ end
8
+
9
+ def convert_to_pdf(n)
10
+ Math.log10(n) * pdf_points_per_increment
11
+ end
12
+
13
+ private
14
+
15
+ def number_of_labels
16
+ y_labels_units.length
17
+ end
18
+
19
+ def number_of_increments
20
+ number_of_labels - 1
21
+ end
22
+
23
+ def pdf_points_per_increment
24
+ height.to_f / number_of_increments
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,139 @@
1
+ module PrawnCharts
2
+ module PrawnChartRenderer
3
+
4
+ def draw_graph(input, colors = {})
5
+ @assistant = RendererAssistant.new(input)
6
+ @collector = @assistant.collector
7
+ @colors = colors
8
+ #stroke_axis
9
+ translate(*input[:graph][:starting_point]) do
10
+ draw_rectangle_with_color
11
+ fill_rectangle_with_color
12
+ draw_line_with_color
13
+ draw_dots_with_color
14
+ draw_x_labels_with_color
15
+ draw_y_labels_with_color
16
+ draw_horizontal_lines_with_color
17
+ draw_title_with_color
18
+ draw_x_title_with_color
19
+ draw_y_title_with_color
20
+ end
21
+ end
22
+
23
+ def draw_line(graph_points)
24
+ graph_points.each_index do |i|
25
+ stroke_line(graph_points[i].coordinate, graph_points[i + 1].coordinate) unless graph_points[i + 1].nil?
26
+ end
27
+ end
28
+
29
+ def draw_dots(graph_points, dot_radius)
30
+ graph_points.each do |point|
31
+ fill_circle(point.coordinate, dot_radius)
32
+ end
33
+ end
34
+
35
+ def draw_x_labels(data_points, offset, options)
36
+ label_width = options[:width]
37
+ centering_adjustment = label_width / 2
38
+ translate(0, offset) do
39
+ data_points.each do |data_point|
40
+ text_box(data_point.x_label, {at: [(data_point.x_pdf - centering_adjustment), 0]}.merge(options))
41
+ end
42
+ end
43
+ end
44
+
45
+ def draw_y_labels(label_data, offset, options)
46
+ label_height = options[:height]
47
+ centering_adjustment = label_height / 2
48
+ translate(offset, 0) do
49
+ label_data.each do |data|
50
+ text_box(data[:label], {at: ([0, data[:y] + centering_adjustment]) }.merge(options))
51
+ end
52
+ end
53
+ end
54
+
55
+ def draw_horizontal_lines(data)
56
+ data.each do |start_point, end_point|
57
+ stroke_line(start_point, end_point)
58
+ end
59
+ end
60
+
61
+ def draw_title(translation, title, options)
62
+ translate(*translation) do
63
+ text_box(title, options)
64
+ end
65
+ end
66
+
67
+ def set_color_and_execute(method, component, &block)
68
+ self.send(method, @colors[component]) if @colors[component]
69
+ yield if @assistant.components_to_draw.include?(component)
70
+ set_color_black(method)
71
+ end
72
+
73
+ def set_color_black(method)
74
+ black = "000000"
75
+ self.send(method, black)
76
+ end
77
+
78
+ def draw_rectangle_with_color
79
+ set_color_and_execute(:stroke_color, :rectangle_border) do
80
+ stroke_rectangle([0, @collector.height], @collector.width, @collector.height)
81
+ end
82
+ end
83
+
84
+ def fill_rectangle_with_color
85
+ set_color_and_execute(:fill_color, :rectangle_fill) do
86
+ fill_rectangle([0, @collector.height], @collector.width, @collector.height)
87
+ end
88
+ end
89
+
90
+ def draw_line_with_color
91
+ set_color_and_execute(:stroke_color, :line) do
92
+ draw_line(@collector.graph_points)
93
+ end
94
+ end
95
+
96
+ def draw_dots_with_color
97
+ set_color_and_execute(:fill_color, :dots) do
98
+ draw_dots(@collector.graph_points, @assistant.dot_radius)
99
+ end
100
+ end
101
+
102
+ def draw_x_labels_with_color
103
+ set_color_and_execute(:fill_color, :x_labels) do
104
+ draw_x_labels(@collector.data_points, @assistant.x_labels_offset, @assistant.x_labels_options)
105
+ end
106
+ end
107
+
108
+ def draw_y_labels_with_color
109
+ set_color_and_execute(:fill_color, :y_labels) do
110
+ draw_y_labels(@collector.y_labels, @assistant.y_labels_offset, @assistant.y_labels_options)
111
+ end
112
+ end
113
+
114
+ def draw_horizontal_lines_with_color
115
+ set_color_and_execute(:stroke_color, :horizontal_lines) do
116
+ draw_horizontal_lines(@collector.horizontal_lines)
117
+ end
118
+ end
119
+
120
+ def draw_title_with_color
121
+ set_color_and_execute(:fill_color, :graph_title) do
122
+ draw_title(@assistant.graph_title_translate, @assistant.graph_title, @assistant.graph_title_options)
123
+ end
124
+ end
125
+
126
+ def draw_x_title_with_color
127
+ set_color_and_execute(:fill_color, :x_title) do
128
+ draw_title(@assistant.x_title_translate, @assistant.x_title, @assistant.x_title_options)
129
+ end
130
+ end
131
+
132
+ def draw_y_title_with_color
133
+ set_color_and_execute(:fill_color, :y_title) do
134
+ draw_title(@assistant.y_title_translate, @assistant.y_title, @assistant.y_title_options)
135
+ end
136
+ end
137
+
138
+ end
139
+ end
@@ -0,0 +1,146 @@
1
+ module PrawnCharts
2
+ class RendererAssistant
3
+ # Parses input data and responds with smart defaults or user specified data
4
+ attr_reader :input
5
+ def initialize(input)
6
+ @input = input
7
+ end
8
+
9
+ def collector
10
+ @collector ||= DataCollector.new(input.fetch(:graph))
11
+ end
12
+
13
+ def components_to_draw
14
+ available_components = [:rectangle_border, :rectangle_fill, :line, :dots, :x_labels, :y_labels, :horizontal_lines, :graph_title, :y_title, :x_title]
15
+ specified_components = [:rectangle_border, :rectangle_fill, :line] + @input.keys
16
+ @components_to_draw = available_components & specified_components
17
+ end
18
+
19
+ # dots
20
+ def dot_radius
21
+ return default_dot_radius unless input.dig(:dots, :radius)
22
+ input[:dots][:radius]
23
+ end
24
+
25
+ # graph_title
26
+ def graph_title_translate
27
+ [0, collector.height + offset(:graph_title, default_graph_title_offset)]
28
+ end
29
+
30
+ def graph_title
31
+ raise "Graph must have title" unless input.dig(:graph_title, :title)
32
+ input[:graph_title][:title]
33
+ end
34
+
35
+ def graph_title_options
36
+ return default_graph_title_options unless input.dig(:graph_title, :text_box_options)
37
+ input[:graph_title][:text_box_options]
38
+ end
39
+
40
+ # x_title
41
+ def x_title_translate
42
+ [0, offset(:x_title, default_x_title_offset)]
43
+ end
44
+
45
+ def x_title
46
+ raise "Graph must have title" unless input.dig(:x_title, :title)
47
+ input[:x_title][:title]
48
+ end
49
+
50
+ def x_title_options
51
+ return default_x_title_options unless input.dig(:x_title, :text_box_options)
52
+ input[:x_title][:text_box_options]
53
+ end
54
+
55
+ # y_title
56
+ def y_title_translate
57
+ [offset(:y_title, default_y_title_offset), 0]
58
+ end
59
+
60
+ def y_title
61
+ raise "Graph must have title" unless input.dig(:y_title, :title)
62
+ input[:y_title][:title]
63
+ end
64
+
65
+ def y_title_options
66
+ return default_y_title_options unless input.dig(:y_title, :text_box_options)
67
+ input[:y_title][:text_box_options]
68
+ end
69
+
70
+ # x_labels
71
+ def x_labels_offset
72
+ offset(:x_labels, default_x_labels_offset)
73
+ end
74
+
75
+ def x_labels_options
76
+ return default_x_labels_options unless input.dig(:x_labels, :text_box_options)
77
+ input[:x_labels][:text_box_options]
78
+ end
79
+
80
+ # y_labels
81
+ def y_labels_offset
82
+ offset(:y_labels, default_y_labels_offset)
83
+ end
84
+
85
+ def y_labels_options
86
+ return default_y_labels_options unless input.dig(:y_labels, :text_box_options)
87
+ default_y_labels_options.merge(input[:y_labels][:text_box_options])
88
+ end
89
+
90
+ private
91
+
92
+ def default_dot_radius
93
+ 4
94
+ end
95
+
96
+ def offset(title_type, default_offset)
97
+ return default_offset unless input.dig(title_type, :offset)
98
+ input[title_type][:offset]
99
+ end
100
+
101
+ # graph title
102
+ def default_graph_title_options
103
+ {at: [0, -20], width: collector.width, height: 20, align: :center}
104
+ end
105
+
106
+ def default_graph_title_offset
107
+ 50
108
+ end
109
+
110
+ # x title
111
+ def default_x_title_options
112
+ {at: [0, 20], width: collector.width, height: 20, align: :center}
113
+ end
114
+
115
+ def default_x_title_offset
116
+ -60
117
+ end
118
+
119
+ # y title
120
+ def default_y_title_options
121
+ {at: [0, 0], width: collector.height, height: 20, align: :center, valign: :center, rotate: 90}
122
+ end
123
+
124
+ def default_y_title_offset
125
+ -50
126
+ end
127
+
128
+ # x_labels
129
+ def default_x_labels_offset
130
+ -50
131
+ end
132
+
133
+ def default_x_labels_options
134
+ { :width => 100, :height => 20, align: :center, rotate: 45 }
135
+ end
136
+
137
+ # y_labels
138
+ def default_y_labels_offset
139
+ -40
140
+ end
141
+
142
+ def default_y_labels_options
143
+ { width: 40, :height => 40, valign: :center }
144
+ end
145
+ end
146
+ end
@@ -1,3 +1,3 @@
1
1
  module PrawnCharts
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.5"
3
3
  end
@@ -0,0 +1,64 @@
1
+ require "spec_helper"
2
+
3
+ module PrawnCharts
4
+ describe DataCollector do
5
+
6
+ data = [
7
+ {x_label: "Apr-11", y: 10},
8
+ {x_label: "May-11", y: nil},
9
+ {x_label: "Jun-11", y: 30}
10
+ ]
11
+ input = { :width => 300, :height => 200, :data => data, :y_labels => [0, 10, 20] }
12
+ let(:collector) { DataCollector.new(input) }
13
+
14
+ context "#y_labels" do
15
+ it "returns y_labels and associated y_pdf position" do
16
+ collector.stub(:y_convert_to_pdf).with(0) { "zero" }
17
+ collector.stub(:y_convert_to_pdf).with(10) { "ten" }
18
+ collector.stub(:y_convert_to_pdf).with(20) { "twenty" }
19
+ expected = [{:label=>"0", :y=>"zero"}, {:label=>"10", :y=>"ten"}, {:label=>"20", :y=>"twenty"}]
20
+ expect(collector.y_labels).to eq expected
21
+ end
22
+ end
23
+
24
+ context "#horizontal_lines" do
25
+ it "returns starting and ending points for graph lines at y_labels" do
26
+ collector.stub(:y_convert_to_pdf).with(0) { 10 }
27
+ collector.stub(:y_convert_to_pdf).with(10) { 20 }
28
+ collector.stub(:y_convert_to_pdf).with(20) { 30 }
29
+ expected = [[[0, 10], [300, 10]], [[0, 20], [300, 20]], [[0, 30], [300, 30]]]
30
+ expect(collector.horizontal_lines).to eq expected
31
+ end
32
+ end
33
+
34
+ context "#data_points" do
35
+ it "makes a DataPoint corresponding to every piece of data it is initialized with" do
36
+ expect(collector.data_points).to have(3).things
37
+ end
38
+ end
39
+
40
+ context "#graph_points" do
41
+ it "returns all DataPoints with a y_pdf value that is truthy" do
42
+ data_point_inputs = { x_units: 1, y_units: 2, x_pdf: 12, y_pdf: 16, x_label: "jan 12" }
43
+ dp_1 = DataPoint.new(data_point_inputs)
44
+ dp_2 = DataPoint.new(data_point_inputs)
45
+ dp_3 = DataPoint.new(data_point_inputs.merge({ y_pdf: nil }))
46
+ data_points = [dp_1, dp_2, dp_3]
47
+ collector.stub(:data_points) { data_points }
48
+ expect(collector.graph_points).to eq [dp_1, dp_2]
49
+ end
50
+ end
51
+
52
+ context "#y_convert_to_pdf" do
53
+ it "converts linear y_unit to y_pdf" do
54
+ expect(collector.y_convert_to_pdf(8)).to eq 80
55
+ end
56
+
57
+ it "converts log y_unit to y_pdf" do
58
+ input = { :width => 300, :height => 200, :data => [], :y_labels => [0, 10, 100], :scale => :log }
59
+ collector = DataCollector.new(input)
60
+ expect(collector.y_convert_to_pdf(12)).to be_within(0.1).of 107.9
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,17 @@
1
+ require "spec_helper"
2
+
3
+ module PrawnCharts
4
+ describe LinearVerticalDataCollector do
5
+ context "#convert_to_pdf" do
6
+ it "converts a y_unit point to a y_pdf point" do
7
+ collector = LinearVerticalDataCollector.new(200, [0, 10, 20, 30, 40, 50])
8
+ expect(collector.convert_to_pdf(15)).to eq 60
9
+ end
10
+
11
+ it "accounts for linear scales that don't start at 0" do
12
+ collector = LinearVerticalDataCollector.new(200, [30, 40, 50])
13
+ expect(collector.convert_to_pdf(15)).to eq 150
14
+ end
15
+ end
16
+ end
17
+ end