dyi 0.0.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.
- data/COPYING +674 -0
- data/README +28 -0
- data/examples/class_diagram.rb +151 -0
- data/examples/data/03311056.xlsx +0 -0
- data/examples/data/currency.xlsx +0 -0
- data/examples/data/money.csv +12 -0
- data/examples/line_and_bar.rb +26 -0
- data/examples/line_chart.rb +30 -0
- data/examples/logo.rb +68 -0
- data/examples/pie_chart.rb +19 -0
- data/examples/simple_shapes.rb +15 -0
- data/lib/dyi.rb +49 -0
- data/lib/dyi/chart.rb +34 -0
- data/lib/dyi/chart/array_reader.rb +136 -0
- data/lib/dyi/chart/base.rb +580 -0
- data/lib/dyi/chart/csv_reader.rb +93 -0
- data/lib/dyi/chart/excel_reader.rb +100 -0
- data/lib/dyi/chart/line_chart.rb +468 -0
- data/lib/dyi/chart/pie_chart.rb +141 -0
- data/lib/dyi/chart/table.rb +201 -0
- data/lib/dyi/color.rb +218 -0
- data/lib/dyi/coordinate.rb +224 -0
- data/lib/dyi/drawing.rb +32 -0
- data/lib/dyi/drawing/canvas.rb +100 -0
- data/lib/dyi/drawing/clipping.rb +61 -0
- data/lib/dyi/drawing/color_effect.rb +118 -0
- data/lib/dyi/drawing/filter.rb +74 -0
- data/lib/dyi/drawing/pen.rb +231 -0
- data/lib/dyi/drawing/pen_3d.rb +270 -0
- data/lib/dyi/font.rb +132 -0
- data/lib/dyi/formatter.rb +36 -0
- data/lib/dyi/formatter/base.rb +245 -0
- data/lib/dyi/formatter/emf_formatter.rb +253 -0
- data/lib/dyi/formatter/eps_formatter.rb +397 -0
- data/lib/dyi/formatter/svg_formatter.rb +260 -0
- data/lib/dyi/formatter/svg_reader.rb +113 -0
- data/lib/dyi/formatter/xaml_formatter.rb +317 -0
- data/lib/dyi/length.rb +399 -0
- data/lib/dyi/matrix.rb +122 -0
- data/lib/dyi/painting.rb +177 -0
- data/lib/dyi/shape.rb +1332 -0
- data/lib/dyi/svg_element.rb +149 -0
- data/lib/dyi/type.rb +104 -0
- data/lib/ironruby.rb +326 -0
- data/lib/util.rb +231 -0
- data/test/path_command_test.rb +217 -0
- data/test/test_length.rb +91 -0
- metadata +114 -0
@@ -0,0 +1,93 @@
|
|
1
|
+
# -*- encoding: UTF-8 -*-
|
2
|
+
|
3
|
+
# Copyright (c) 2009-2011 Sound-F Co., Ltd. All rights reserved.
|
4
|
+
#
|
5
|
+
# Author:: Mamoru Yuo
|
6
|
+
#
|
7
|
+
# This file is part of DYI.
|
8
|
+
#
|
9
|
+
# DYI is free software: you can redistribute it and/or modify it
|
10
|
+
# under the terms of the GNU General Public License as published by
|
11
|
+
# the Free Software Foundation, either version 3 of the License, or
|
12
|
+
# (at your option) any later version.
|
13
|
+
#
|
14
|
+
# DYI is distributed in the hope that it will be useful,
|
15
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
16
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
17
|
+
# GNU General Public License for more details.
|
18
|
+
#
|
19
|
+
# You should have received a copy of the GNU General Public License
|
20
|
+
# along with DYI. If not, see <http://www.gnu.org/licenses/>.
|
21
|
+
|
22
|
+
require 'csv'
|
23
|
+
require 'date'
|
24
|
+
require 'bigdecimal'
|
25
|
+
require 'nkf'
|
26
|
+
|
27
|
+
module DYI #:nodoc:
|
28
|
+
module Chart #:nodoc:
|
29
|
+
|
30
|
+
class CsvReader < ArrayReader
|
31
|
+
def read(path, options={})
|
32
|
+
options = options.dup
|
33
|
+
@date_format = options.delete(:date_format)
|
34
|
+
@datetime_format = options.delete(:datetime_format)
|
35
|
+
nkf_options =
|
36
|
+
case (options[:encode] || :utf8).to_sym
|
37
|
+
when :utf8 then nil
|
38
|
+
when :sjis then '-w -S -m0 -x --cp932'
|
39
|
+
when :euc then '-w -E -m0 -x --cp932'
|
40
|
+
when :jis then '-w -J -m0 -x'
|
41
|
+
when :utf16 then '-w -W16 -m0 -x'
|
42
|
+
else raise ArgumentError,"Unknown encode: `#{@encode}'"
|
43
|
+
end
|
44
|
+
parsed_array =
|
45
|
+
if RUBY_VERSION >= '1.9'
|
46
|
+
CSV.parse(nkf_options ? NKF.nkf(nkf_options, IO.read(path)) : IO.read(path), :col_sep => options[:col_sep] || ',', :row_sep => options[:row_sep] || :auto)
|
47
|
+
else
|
48
|
+
CSV.parse(nkf_options ? NKF.nkf(nkf_options, IO.read(path)) : IO.read(path), options[:col_sep], options[:row_sep])
|
49
|
+
end
|
50
|
+
super(parsed_array, options)
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def primitive_value(value, type)
|
56
|
+
if type.is_a?(Symbol) || type.is_a?(String)
|
57
|
+
case type.to_sym
|
58
|
+
when :string
|
59
|
+
value || ''
|
60
|
+
when :number, :decimal
|
61
|
+
value ? BigDecimal.new(value) : nil
|
62
|
+
when :float
|
63
|
+
value ? value.to_f : nil
|
64
|
+
when :integer
|
65
|
+
value ? value.to_i : nil
|
66
|
+
when :date
|
67
|
+
return nil if value.nil?
|
68
|
+
@date_format ? Date.strptime(value, @date_format) : Date.parse(value)
|
69
|
+
when :datetime
|
70
|
+
return nil if value.nil?
|
71
|
+
@datetime_format ? DateTime.strptime(value, @datetime_format) : DateTime.parse(value)
|
72
|
+
when :boolean
|
73
|
+
value ? true : value
|
74
|
+
else
|
75
|
+
value ? value.to_f : nil
|
76
|
+
end
|
77
|
+
else
|
78
|
+
value ? value.to_f : nil
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def primitive_title_value(value, type=nil)
|
83
|
+
value
|
84
|
+
end
|
85
|
+
|
86
|
+
class << self
|
87
|
+
def read(path, options={})
|
88
|
+
new.read(path, options)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# -*- encoding: UTF-8 -*-
|
2
|
+
|
3
|
+
# Copyright (c) 2009-2011 Sound-F Co., Ltd. All rights reserved.
|
4
|
+
#
|
5
|
+
# Author:: Mamoru Yuo
|
6
|
+
#
|
7
|
+
# This file is part of DYI.
|
8
|
+
#
|
9
|
+
# DYI is free software: you can redistribute it and/or modify it
|
10
|
+
# under the terms of the GNU General Public License as published by
|
11
|
+
# the Free Software Foundation, either version 3 of the License, or
|
12
|
+
# (at your option) any later version.
|
13
|
+
#
|
14
|
+
# DYI is distributed in the hope that it will be useful,
|
15
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
16
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
17
|
+
# GNU General Public License for more details.
|
18
|
+
#
|
19
|
+
# You should have received a copy of the GNU General Public License
|
20
|
+
# along with DYI. If not, see <http://www.gnu.org/licenses/>.
|
21
|
+
|
22
|
+
begin
|
23
|
+
require 'win32ole' # for Windows
|
24
|
+
rescue LoadError
|
25
|
+
# do notiong
|
26
|
+
end
|
27
|
+
|
28
|
+
require 'date'
|
29
|
+
require 'nkf'
|
30
|
+
|
31
|
+
module DYI #:nodoc:
|
32
|
+
module Chart #:nodoc:
|
33
|
+
|
34
|
+
class ExcelReader < ArrayReader
|
35
|
+
OFFSET = DateTime.now.offset
|
36
|
+
|
37
|
+
def read(path, options={})
|
38
|
+
if defined? WIN32OLE
|
39
|
+
# for Windows
|
40
|
+
path = WIN32OLE.new('Scripting.FileSystemObject').getAbsolutePathName(path)
|
41
|
+
excel = WIN32OLE.new('Excel.Application')
|
42
|
+
book = excel.workbooks.open(path)
|
43
|
+
sheet = book.worksheets.item(options[:sheet] || 1)
|
44
|
+
range = sheet.usedRange
|
45
|
+
sheet_values = sheet.range(sheet.cells(1,1), sheet.cells(range.end(4).row, range.end(2).column)).value
|
46
|
+
else
|
47
|
+
# except Windows
|
48
|
+
raise NotImplementedError, 'win32ole is not installed'
|
49
|
+
end
|
50
|
+
|
51
|
+
begin
|
52
|
+
super(sheet_values, options)
|
53
|
+
ensure
|
54
|
+
if defined? WIN32OLE
|
55
|
+
book.close(false)
|
56
|
+
excel.quit
|
57
|
+
excel = sheet = nil
|
58
|
+
end
|
59
|
+
book = sheet_values = nil
|
60
|
+
GC.start
|
61
|
+
end
|
62
|
+
self
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def primitive_value(value, type=nil)
|
68
|
+
if defined? WIN32OLE
|
69
|
+
# for Windows
|
70
|
+
case value
|
71
|
+
when String
|
72
|
+
if value =~ %r(^(\d{4})/(\d{2})/(\d{2}) (\d{2}):(\d{2}):(\d{2})$)
|
73
|
+
DateTime.new($1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, OFFSET)
|
74
|
+
elsif value.size == 0
|
75
|
+
nil
|
76
|
+
else
|
77
|
+
NKF.nkf('-w -S -m0 -x --cp932', value)
|
78
|
+
end
|
79
|
+
when Numeric, nil, true, false
|
80
|
+
value
|
81
|
+
when System::DateTime
|
82
|
+
# for IronRuby
|
83
|
+
DateTime.new(value.Year, value.Month, value.Day, value.Hour, value.Minute, value.Second, OFFSET)
|
84
|
+
else
|
85
|
+
value
|
86
|
+
end rescue value
|
87
|
+
else
|
88
|
+
# except Windows
|
89
|
+
raise NotImplementedError, 'win32ole is not installed'
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
class << self
|
94
|
+
def read(path, options={})
|
95
|
+
new.read(path, options)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,468 @@
|
|
1
|
+
# -*- encoding: UTF-8 -*-
|
2
|
+
|
3
|
+
# Copyright (c) 2009-2011 Sound-F Co., Ltd. All rights reserved.
|
4
|
+
#
|
5
|
+
# Author:: Mamoru Yuo
|
6
|
+
#
|
7
|
+
# This file is part of DYI.
|
8
|
+
#
|
9
|
+
# DYI is free software: you can redistribute it and/or modify it
|
10
|
+
# under the terms of the GNU General Public License as published by
|
11
|
+
# the Free Software Foundation, either version 3 of the License, or
|
12
|
+
# (at your option) any later version.
|
13
|
+
#
|
14
|
+
# DYI is distributed in the hope that it will be useful,
|
15
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
16
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
17
|
+
# GNU General Public License for more details.
|
18
|
+
#
|
19
|
+
# You should have received a copy of the GNU General Public License
|
20
|
+
# along with DYI. If not, see <http://www.gnu.org/licenses/>.
|
21
|
+
|
22
|
+
module DYI #:nodoc:
|
23
|
+
module Chart #:nodoc:
|
24
|
+
|
25
|
+
class LineChart < Base
|
26
|
+
include AxisUtil
|
27
|
+
include Legend
|
28
|
+
CHART_TYPES = [:line, :area, :bar, :stackedbar]
|
29
|
+
attr_reader :axis_back_canvas, :chart_back_canvas, :scale_canvas, :chart_front_canvas, :axis_front_canvas, :legend_canvas
|
30
|
+
|
31
|
+
opt_accessor :chart_margins, {:type => :hash, :default => {}, :keys => [:top,:right,:bottom,:left], :item_type => :length}
|
32
|
+
opt_accessor :chart_colors, {:type => :array, :item_type => :color}
|
33
|
+
opt_accessor :chart_type, {:type => :symbol, :default => :line, :valid_values => CHART_TYPES}
|
34
|
+
opt_accessor :chart_types, {:type => :array, :item_type => :symbol, :valid_values => CHART_TYPES.clone.unshift('')}
|
35
|
+
opt_accessor :represent_3d, {:type => :boolean}
|
36
|
+
opt_accessor :_3d_settings, {:type => :hash, :default => {}, :keys => [:background_opacity,:dx,:dy], :item_type => :float}
|
37
|
+
opt_accessor :show_dropshadow, {:type => :boolean}
|
38
|
+
opt_accessor :dropshadow_settings, {:type => :hash, :default => {}, :keys => [:blur_std,:dx,:dy], :item_type => :float}
|
39
|
+
opt_accessor :x_axis_type, {:type => :symbol, :default => :range, :valid_values => [:point,:range]}
|
40
|
+
opt_accessor :line_width, {:type => :integer}
|
41
|
+
opt_accessor :bar_width_ratio, {:type => :float, :default => 0.6, :range => 0..1}
|
42
|
+
opt_accessor :main_y_axis, {:type => :symbol, :default => :left, :valid_values => [:left,:right]}
|
43
|
+
opt_accessor :axis_font, {:type => :font}
|
44
|
+
opt_accessor :axis_settings, {:type => :hash, :default => {}, :keys => [:max, :min, :min_scale_value, :scale_count, :scale_interval], :item_type => :integer}
|
45
|
+
opt_accessor :x_axis_format, {:type => :string}
|
46
|
+
opt_accessor :axis_format, {:type => :string}
|
47
|
+
opt_accessor :use_y_second_axises, {:type => :array, :item_type => :boolean}
|
48
|
+
opt_accessor :second_axis_settings, {:type => :hash, :default => {}, :keys => [:max, :min], :item_type => :integer}
|
49
|
+
opt_accessor :second_axis_format, {:type => :string, :default_method => :axis_format}
|
50
|
+
opt_accessor :max_x_label_count, {:type => :integer, :default_proc => proc{|c| c.chart_width.div(Length.new(96))}}
|
51
|
+
opt_accessor :show_x_labels, {:type => :boolean, :default => true}
|
52
|
+
opt_accessor :legend_texts, {:type => :array, :item_type => :string}
|
53
|
+
opt_accessor :data_columns, {:type => :array, :item_type => :integer}
|
54
|
+
opt_accessor :use_effect, {:type => :boolean, :default => true}
|
55
|
+
opt_accessor :bar_seriese_interval, {:type => :float, :default => 0.3}
|
56
|
+
opt_accessor :color_columns, {:type => :array, :item_type => :integer}
|
57
|
+
|
58
|
+
def margin_top
|
59
|
+
chart_margins[:top] || Length.new(16)
|
60
|
+
end
|
61
|
+
|
62
|
+
def margin_right
|
63
|
+
chart_margins[:right] || Length.new(64)
|
64
|
+
end
|
65
|
+
|
66
|
+
def margin_bottom
|
67
|
+
chart_margins[:bottom] || Length.new(32)
|
68
|
+
end
|
69
|
+
|
70
|
+
def margin_left
|
71
|
+
chart_margins[:left] || Length.new(64)
|
72
|
+
end
|
73
|
+
|
74
|
+
alias __org_chart_type__ chart_type
|
75
|
+
|
76
|
+
def chart_type(index = nil)
|
77
|
+
if index
|
78
|
+
(chart_types && chart_types[index]) || __org_chart_type__
|
79
|
+
else
|
80
|
+
__org_chart_type__
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def s_3d_pen_options
|
85
|
+
{:background_opacity => 0.3}.merge(_3d_settings)
|
86
|
+
end
|
87
|
+
|
88
|
+
def back_translate_value
|
89
|
+
{
|
90
|
+
:dx => (Length.new_or_nil(_3d_settings[:dx]) || Length.new(24)),
|
91
|
+
:dy => (Length.new_or_nil(_3d_settings[:dy]) || Length.new(-8))
|
92
|
+
}
|
93
|
+
end
|
94
|
+
|
95
|
+
def dropshadow_blur_std
|
96
|
+
dropshadow_settings[:blur_std] || 4
|
97
|
+
end
|
98
|
+
|
99
|
+
def dropshadow_dx
|
100
|
+
dropshadow_settings[:dx] || back_translate_value[:dx] / 2
|
101
|
+
end
|
102
|
+
|
103
|
+
def dropshadow_dy
|
104
|
+
dropshadow_settings[:dy] || back_translate_value[:dy] / 2
|
105
|
+
end
|
106
|
+
|
107
|
+
def use_y_second_axis?(index = nil)
|
108
|
+
if index
|
109
|
+
use_y_second_axises && use_y_second_axises[index]
|
110
|
+
else
|
111
|
+
use_y_second_axises && use_y_second_axises.any?
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def chart_width
|
116
|
+
width - margin_left - margin_right
|
117
|
+
end
|
118
|
+
|
119
|
+
def chart_height
|
120
|
+
height - margin_top - margin_bottom
|
121
|
+
end
|
122
|
+
|
123
|
+
def line_chart_pen(color)
|
124
|
+
if represent_3d?
|
125
|
+
Drawing::CubicPen.new({:color => color, :width => line_width}.merge(s_3d_pen_options))
|
126
|
+
else
|
127
|
+
Drawing::Pen.new(:color => color, :width => line_width, :stroke_linecap => 'square')
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def bar_chart_brush(color, bar_width=nil)
|
132
|
+
if represent_3d?
|
133
|
+
bar_width ||= chart_width * bar_width_ratio / data.column_values(data_columns[0]).size
|
134
|
+
Drawing::CylinderBrush.new(:color => color, :ry => bar_width * (back_translate_value[:dy] * bar_width_ratio).quo(back_translate_value[:dx] * 2))
|
135
|
+
else
|
136
|
+
Drawing::Brush.new(:color => color)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def area_chart_brush(color)
|
141
|
+
Drawing::Brush.new(:color => color)
|
142
|
+
end
|
143
|
+
|
144
|
+
private
|
145
|
+
|
146
|
+
def default_legend_point #:nodoc:
|
147
|
+
Coordinate.new(margin_left, 0)
|
148
|
+
end
|
149
|
+
|
150
|
+
def create_vector_image #:nodoc:
|
151
|
+
init_container
|
152
|
+
|
153
|
+
main_series_data = []
|
154
|
+
sub_series_data = []
|
155
|
+
@bar_series = []
|
156
|
+
data_columns.size.times do |i|
|
157
|
+
main_series_data.push(*data.column_values(data_columns[i])) unless use_y_second_axis?(i)
|
158
|
+
sub_series_data.push(*data.column_values(data_columns[i])) if use_y_second_axis?(i)
|
159
|
+
@bar_series.push(i) if chart_type(i) == :bar
|
160
|
+
end
|
161
|
+
settings =
|
162
|
+
moderate_axis(
|
163
|
+
main_series_data,
|
164
|
+
chart_height,
|
165
|
+
axis_settings[:min],
|
166
|
+
axis_settings[:max],
|
167
|
+
axis_settings[:scale_count])
|
168
|
+
sub_settings =
|
169
|
+
moderate_sub_axis(
|
170
|
+
sub_series_data,
|
171
|
+
settings,
|
172
|
+
second_axis_settings[:min],
|
173
|
+
second_axis_settings[:max]) if use_y_second_axis?
|
174
|
+
|
175
|
+
data_columns.size.times do |i|
|
176
|
+
draw_chart(data_columns[i], chart_type(i), chart_color(i), use_y_second_axis?(i) ? sub_settings : settings) if chart_type(i) == :stackedbar
|
177
|
+
end
|
178
|
+
(data_columns.size-1).downto(0) do |i|
|
179
|
+
draw_chart(data_columns[i], chart_type(i), chart_color(i), use_y_second_axis?(i) ? sub_settings : settings) if chart_type(i) != :stackedbar
|
180
|
+
end
|
181
|
+
|
182
|
+
draw_axis(settings, sub_settings)
|
183
|
+
texts = legend_texts || data_columns.map{|i| data.column_title(i)}
|
184
|
+
draw_legend(texts, legend_shapes)
|
185
|
+
end
|
186
|
+
|
187
|
+
def init_container #:nodoc:
|
188
|
+
# mask = Drawing::ColorEffect::Mask.new(@canvas)
|
189
|
+
# mask.add_shapes(Shape::Rectangle.new(Drawing::Brush.new(:color => '#FFFFFF'), [margin_left, margin_top], chart_width, chart_height))
|
190
|
+
@axis_back_canvas = Shape::ShapeGroup.draw_on(@canvas) unless @axis_back_canvas
|
191
|
+
# @chart_front_canvas = Shape::ShapeGroup.draw_on(@canvas, :mask => "url(##{mask.id})") unless @chart_front_canvas
|
192
|
+
@chart_back_canvas = Shape::ShapeGroup.draw_on(@canvas) unless @chart_back_canvas
|
193
|
+
@scale_canvas = Shape::ShapeGroup.draw_on(@canvas) unless @scale_canvas
|
194
|
+
@chart_front_canvas = Shape::ShapeGroup.draw_on(@canvas) unless @chart_front_canvas
|
195
|
+
@axis_front_canvas = Shape::ShapeGroup.draw_on(@canvas) unless @axis_front_canvas
|
196
|
+
@legend_canvas = Shape::ShapeGroup.draw_on(@canvas) unless @legend_canvas
|
197
|
+
@chart_options = {}
|
198
|
+
# @chart_options[:filter] = "url(##{Drawing::Filter::DropShadow.new(@canvas, dropshadow_blur_std, dropshadow_dx, dropshadow_dy).id})" if show_dropshadow?
|
199
|
+
chart_clip = Drawing::Clipping.new(Shape::Rectangle.new([margin_left, margin_top], width - margin_left - margin_right, height - margin_top - margin_bottom))
|
200
|
+
@chart_back_canvas.set_clipping(chart_clip)
|
201
|
+
@chart_front_canvas.set_clipping(chart_clip)
|
202
|
+
end
|
203
|
+
|
204
|
+
def draw_axis(settings, sub_settings) #:nodoc:
|
205
|
+
line_options = {:linecap => 'square'}
|
206
|
+
line_pen = represent_3d? ? Drawing::CubicPen.new(line_options.merge(s_3d_pen_options)) : Drawing::Pen.new(line_options)
|
207
|
+
sub_pen = represent_3d? ? Drawing::Pen.new : line_pen
|
208
|
+
text_pen = Drawing::Pen.new(:font => axis_font)
|
209
|
+
text_margin = axis_font && (axis_font.draw_size.quo(4)) || Font::DEFAULT_SIZE.quo(4)
|
210
|
+
|
211
|
+
draw_y_axis(line_pen)
|
212
|
+
draw_x_axis(line_pen, sub_pen, text_pen, text_margin)
|
213
|
+
draw_scale(sub_pen, text_pen, settings, sub_settings, text_margin)
|
214
|
+
end
|
215
|
+
|
216
|
+
def draw_y_axis(pen) #:nodoc:
|
217
|
+
if use_y_second_axis? || main_y_axis == :left
|
218
|
+
start_point = [margin_left, height - margin_bottom]
|
219
|
+
end_point = [margin_left, margin_top]
|
220
|
+
pen.draw_line(represent_3d? ? @axis_back_canvas : @axis_front_canvas, start_point, end_point)
|
221
|
+
end
|
222
|
+
|
223
|
+
if use_y_second_axis? || main_y_axis == :right
|
224
|
+
start_point = [width - margin_right, height - margin_bottom]
|
225
|
+
end_point = [width - margin_right, margin_top]
|
226
|
+
pen.draw_line(@axis_front_canvas, start_point, end_point)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
def draw_scale(line_pen, text_pen, settings, sub_settings, text_margin) #:nodoc:
|
231
|
+
if settings[:min] == settings[:min_scale_value] - settings[:scale_interval]
|
232
|
+
y = value_position_on_chart(margin_top, settings, settings[:min], true)
|
233
|
+
if use_y_second_axis? || main_y_axis == :left
|
234
|
+
text_pen.draw_text(
|
235
|
+
@axis_front_canvas,
|
236
|
+
[margin_left - text_margin, y],
|
237
|
+
main_y_axis == :left ? settings[:min] : sub_settings[:min],
|
238
|
+
:alignment_baseline=>'middle',
|
239
|
+
:text_anchor=>'end',
|
240
|
+
:format => (main_y_axis == :left ? axis_format : second_axis_format))
|
241
|
+
end
|
242
|
+
if use_y_second_axis? || main_y_axis == :right
|
243
|
+
text_pen.draw_text(
|
244
|
+
@axis_front_canvas,
|
245
|
+
[width - margin_right + text_margin, y],
|
246
|
+
main_y_axis == :right ? settings[:min] : sub_settings[:min],
|
247
|
+
:alignment_baseline=>'middle',
|
248
|
+
:format => (main_y_axis == :right ? axis_format : second_axis_format))
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
if represent_3d? && (use_y_second_axis? || main_y_axis == :left)
|
253
|
+
line_pen.draw_line(
|
254
|
+
@axis_back_canvas,
|
255
|
+
[margin_left, height - margin_bottom],
|
256
|
+
[margin_left + back_translate_value[:dx], height - margin_bottom + back_translate_value[:dy]])
|
257
|
+
end
|
258
|
+
|
259
|
+
sub_axis_value = sub_settings[:min_scale_value] if use_y_second_axis?
|
260
|
+
settings[:min_scale_value].step(settings[:max], settings[:scale_interval]) do |value|
|
261
|
+
y = value_position_on_chart(margin_top, settings, value, true)
|
262
|
+
|
263
|
+
if settings[:min] != value
|
264
|
+
if represent_3d?
|
265
|
+
if use_y_second_axis? || main_y_axis == :left
|
266
|
+
draw_y_scale_line(
|
267
|
+
line_pen,
|
268
|
+
[margin_left, y],
|
269
|
+
[margin_left + back_translate_value[:dx], y + back_translate_value[:dy]])
|
270
|
+
end
|
271
|
+
draw_y_scale_line(
|
272
|
+
line_pen,
|
273
|
+
[margin_left + back_translate_value[:dx], y + back_translate_value[:dy]],
|
274
|
+
[width - margin_right + back_translate_value[:dx], y + back_translate_value[:dy]])
|
275
|
+
if use_y_second_axis? || main_y_axis == :right
|
276
|
+
draw_y_scale_line(
|
277
|
+
line_pen,
|
278
|
+
[width - margin_right + back_translate_value[:dx], y + back_translate_value[:dy]],
|
279
|
+
[width - margin_right, y])
|
280
|
+
end
|
281
|
+
else
|
282
|
+
line_pen.dasharray = '2,6'
|
283
|
+
draw_y_scale_line(line_pen, [margin_left, y], [width - margin_right, y])
|
284
|
+
line_pen.dasharray = nil
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
if use_y_second_axis? || main_y_axis == :left
|
289
|
+
text_pen.draw_text(
|
290
|
+
@axis_back_canvas,
|
291
|
+
[margin_left - text_margin, y],
|
292
|
+
main_y_axis == :left ? value : sub_axis_value,
|
293
|
+
:alignment_baseline=>'middle',
|
294
|
+
:text_anchor=>'end',
|
295
|
+
:font=>axis_font,
|
296
|
+
:format => (main_y_axis == :left ? axis_format : second_axis_format))
|
297
|
+
end
|
298
|
+
|
299
|
+
if use_y_second_axis? || main_y_axis == :right
|
300
|
+
text_pen.draw_text(
|
301
|
+
@axis_front_canvas,
|
302
|
+
[width - margin_right + text_margin, y],
|
303
|
+
main_y_axis == :right ? value : sub_axis_value,
|
304
|
+
:alignment_baseline=>'middle',
|
305
|
+
:font=>axis_font,
|
306
|
+
:format => (main_y_axis == :right ? axis_format : second_axis_format))
|
307
|
+
end
|
308
|
+
sub_axis_value += sub_settings[:scale_interval] if use_y_second_axis?
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
def draw_y_scale_line(pen, left_point, right_point)
|
313
|
+
pen.draw_line(@scale_canvas, left_point, right_point)
|
314
|
+
end
|
315
|
+
|
316
|
+
def needs_x_scale?(i)
|
317
|
+
return true if data.row_count < max_x_label_count
|
318
|
+
i % ((data.row_count - 1) / [max_x_label_count - 1, 1].max) == 0
|
319
|
+
end
|
320
|
+
|
321
|
+
def draw_x_axis(main_pen, sub_pen, text_pen, text_margin) #:nodoc:
|
322
|
+
main_pen.draw_line(represent_3d? ? @axis_back_canvas : @axis_front_canvas, [margin_left, height - margin_bottom], [width - margin_right, height - margin_bottom])
|
323
|
+
|
324
|
+
data.row_count.times do |i|
|
325
|
+
next unless needs_x_scale?(i)
|
326
|
+
text_x = order_position_on_chart(margin_left, chart_width, data.row_count, i, x_axis_type)
|
327
|
+
scale_x = x_axis_type == :range ? order_position_on_chart(margin_left, chart_width, data.row_count + 1, i) : text_x
|
328
|
+
text_pen.draw_text(
|
329
|
+
@axis_front_canvas,
|
330
|
+
[text_x, height - margin_bottom + text_margin],
|
331
|
+
format_x_label(data.row_title(i)),
|
332
|
+
:text_anchor => 'middle', :alignment_baseline => 'top') if show_x_labels?
|
333
|
+
|
334
|
+
sub_pen.draw_line_on_direction(
|
335
|
+
@axis_front_canvas,
|
336
|
+
[scale_x, height - margin_bottom],
|
337
|
+
0,
|
338
|
+
-(axis_font ? axis_font.draw_size : Font::DEFAULT_SIZE) * 0.5) if i > 0 && i < data.row_count - (x_axis_type == :range ? 0 : 1)
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
def draw_chart(id, chart_type, color, settings) #:nodoc:
|
343
|
+
case chart_type
|
344
|
+
when :line then draw_line(id, color, settings)
|
345
|
+
when :area then draw_area(id, color, settings)
|
346
|
+
when :bar then draw_bar(id, color, settings)
|
347
|
+
when :stackedbar then draw_stackedbar(id, color, settings)
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
def draw_line(id, color, settings) #:nodoc:
|
352
|
+
values = data.column_values(id)
|
353
|
+
return if values.compact.size == 0
|
354
|
+
first_index = values.each_with_index {|value, i| break i if value}
|
355
|
+
pen_options = {:color => color, :width => line_width}
|
356
|
+
pen = line_chart_pen(color)
|
357
|
+
|
358
|
+
x = order_position_on_chart(margin_left, chart_width, values.size, first_index, x_axis_type)
|
359
|
+
y = value_position_on_chart(margin_top, settings, values[first_index], true)
|
360
|
+
pen.linejoin = 'bevel'
|
361
|
+
pen.draw_polyline(@chart_front_canvas, [x, y], @chart_options) {|polyline|
|
362
|
+
((first_index + 1)...values.size).each do |i|
|
363
|
+
x = order_position_on_chart(margin_left, chart_width, values.size, i, x_axis_type)
|
364
|
+
y = value_position_on_chart(margin_top, settings, values[i], true)
|
365
|
+
polyline.line_to([x, y])
|
366
|
+
end
|
367
|
+
}
|
368
|
+
pen.linejoin = 'bevel'
|
369
|
+
end
|
370
|
+
|
371
|
+
def draw_area(id, color, settings) #:nodoc:
|
372
|
+
values = data.column_values(id)
|
373
|
+
return if values.compact.size == 0
|
374
|
+
first_index = values.each_with_index {|value, i| break i if value}
|
375
|
+
brush = area_chart_brush(color)
|
376
|
+
|
377
|
+
x = order_position_on_chart(margin_left, chart_width, values.size, first_index, x_axis_type)
|
378
|
+
y = value_position_on_chart(margin_top, settings, settings[:min], true)
|
379
|
+
polygone = brush.draw_polygon(@chart_front_canvas, [x, y], @chart_options) {|polygon|
|
380
|
+
(first_index...values.size).each do |i|
|
381
|
+
x = order_position_on_chart(margin_left, chart_width, values.size, i, x_axis_type)
|
382
|
+
y = value_position_on_chart(margin_top, settings, values[i], true)
|
383
|
+
polygon.line_to([x, y])
|
384
|
+
end
|
385
|
+
y = value_position_on_chart(margin_top, settings, settings[:min], true)
|
386
|
+
polygon.line_to([x, y])
|
387
|
+
}
|
388
|
+
polygone.translate(back_translate_value[:dx], back_translate_value[:dy]) if represent_3d?
|
389
|
+
end
|
390
|
+
|
391
|
+
def draw_bar(id, color, settings) #:nodoc:
|
392
|
+
bar_group = Shape::ShapeGroup.new(@chart_options).draw_on(@chart_front_canvas)
|
393
|
+
values = data.column_values(id)
|
394
|
+
return if values.compact.size == 0
|
395
|
+
bar_width = chart_width * bar_width_ratio / values.size / (@bar_series.size + (@bar_series.size - 1) * bar_seriese_interval)
|
396
|
+
|
397
|
+
brush = bar_chart_brush(color, bar_width)
|
398
|
+
|
399
|
+
values.each_with_index do |value, i|
|
400
|
+
next if value.nil?
|
401
|
+
x = order_position_on_chart(margin_left, chart_width, values.size, i, x_axis_type, bar_width_ratio) + bar_width * (1 + bar_seriese_interval) * @bar_series.index(id)
|
402
|
+
y = value_position_on_chart(margin_top, settings, value, true)
|
403
|
+
brush = bar_chart_brush(data[i, color_columns[id]], bar_width) if color_columns && color_columns[id]
|
404
|
+
brush.draw_rectangle(bar_group, [x, y], bar_width, height - margin_bottom - y)
|
405
|
+
end
|
406
|
+
bar_group.translate(back_translate_value[:dx] / 2, back_translate_value[:dy] / 2) if represent_3d?
|
407
|
+
end
|
408
|
+
|
409
|
+
def draw_stackedbar(id, color, settings) #:nodoc:
|
410
|
+
bar_group = Shape::ShapeGroup.new(@chart_options).draw_on(@chart_front_canvas)
|
411
|
+
|
412
|
+
values = data.column_values(id)
|
413
|
+
return if values.compact.size == 0
|
414
|
+
bar_width = chart_width * bar_width_ratio / values.size
|
415
|
+
|
416
|
+
brush = bar_chart_brush(color, bar_width)
|
417
|
+
|
418
|
+
@stacked_values ||= []
|
419
|
+
|
420
|
+
values.each_with_index do |value, i|
|
421
|
+
@stacked_values[i] ||= 0
|
422
|
+
next if value.nil?
|
423
|
+
x = order_position_on_chart(margin_left, chart_width, values.size, i, x_axis_type, bar_width_ratio)
|
424
|
+
y = value_position_on_chart(margin_top, settings, (@stacked_values[i] += value), true)
|
425
|
+
bar_height = value_position_on_chart(margin_top, settings, (@stacked_values[i] - value), true) - y
|
426
|
+
# brush.color = data[:$color][i] if data[:$color]
|
427
|
+
brush.draw_rectangle(bar_group, [x, y], bar_width, bar_height)
|
428
|
+
end
|
429
|
+
bar_group.translate(back_translate_value[:dx] / 2, back_translate_value[:dy] / 2) if represent_3d?
|
430
|
+
end
|
431
|
+
|
432
|
+
def legend_shapes #:nodoc:
|
433
|
+
result = []
|
434
|
+
data_columns.each_with_index do |id, index|
|
435
|
+
result <<
|
436
|
+
case chart_type(index)
|
437
|
+
when :line, nil
|
438
|
+
Shape::Line.create_on_start_end(
|
439
|
+
[legend_font_size * 0.2, legend_font_size * (1.2 * index + 0.8)],
|
440
|
+
[legend_font_size, legend_font_size * (1.2 * index + 0.8)],
|
441
|
+
:painting => {:stroke => chart_color(index), :stroke_width => line_width})
|
442
|
+
when :bar, :stackedbar
|
443
|
+
nil
|
444
|
+
when :area
|
445
|
+
nil
|
446
|
+
else
|
447
|
+
nil
|
448
|
+
end
|
449
|
+
end
|
450
|
+
result
|
451
|
+
end
|
452
|
+
|
453
|
+
def format_x_label(obj)
|
454
|
+
if x_axis_format
|
455
|
+
if obj.kind_of?(Numeric)
|
456
|
+
obj.strfnum(x_axis_format)
|
457
|
+
elsif obj.respond_to?(:strftime)
|
458
|
+
obj.strftime(x_axis_format)
|
459
|
+
else
|
460
|
+
obj.to_s
|
461
|
+
end
|
462
|
+
else
|
463
|
+
obj.to_s
|
464
|
+
end
|
465
|
+
end
|
466
|
+
end
|
467
|
+
end
|
468
|
+
end
|