dyi 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/COPYING +674 -0
  2. data/README +28 -0
  3. data/examples/class_diagram.rb +151 -0
  4. data/examples/data/03311056.xlsx +0 -0
  5. data/examples/data/currency.xlsx +0 -0
  6. data/examples/data/money.csv +12 -0
  7. data/examples/line_and_bar.rb +26 -0
  8. data/examples/line_chart.rb +30 -0
  9. data/examples/logo.rb +68 -0
  10. data/examples/pie_chart.rb +19 -0
  11. data/examples/simple_shapes.rb +15 -0
  12. data/lib/dyi.rb +49 -0
  13. data/lib/dyi/chart.rb +34 -0
  14. data/lib/dyi/chart/array_reader.rb +136 -0
  15. data/lib/dyi/chart/base.rb +580 -0
  16. data/lib/dyi/chart/csv_reader.rb +93 -0
  17. data/lib/dyi/chart/excel_reader.rb +100 -0
  18. data/lib/dyi/chart/line_chart.rb +468 -0
  19. data/lib/dyi/chart/pie_chart.rb +141 -0
  20. data/lib/dyi/chart/table.rb +201 -0
  21. data/lib/dyi/color.rb +218 -0
  22. data/lib/dyi/coordinate.rb +224 -0
  23. data/lib/dyi/drawing.rb +32 -0
  24. data/lib/dyi/drawing/canvas.rb +100 -0
  25. data/lib/dyi/drawing/clipping.rb +61 -0
  26. data/lib/dyi/drawing/color_effect.rb +118 -0
  27. data/lib/dyi/drawing/filter.rb +74 -0
  28. data/lib/dyi/drawing/pen.rb +231 -0
  29. data/lib/dyi/drawing/pen_3d.rb +270 -0
  30. data/lib/dyi/font.rb +132 -0
  31. data/lib/dyi/formatter.rb +36 -0
  32. data/lib/dyi/formatter/base.rb +245 -0
  33. data/lib/dyi/formatter/emf_formatter.rb +253 -0
  34. data/lib/dyi/formatter/eps_formatter.rb +397 -0
  35. data/lib/dyi/formatter/svg_formatter.rb +260 -0
  36. data/lib/dyi/formatter/svg_reader.rb +113 -0
  37. data/lib/dyi/formatter/xaml_formatter.rb +317 -0
  38. data/lib/dyi/length.rb +399 -0
  39. data/lib/dyi/matrix.rb +122 -0
  40. data/lib/dyi/painting.rb +177 -0
  41. data/lib/dyi/shape.rb +1332 -0
  42. data/lib/dyi/svg_element.rb +149 -0
  43. data/lib/dyi/type.rb +104 -0
  44. data/lib/ironruby.rb +326 -0
  45. data/lib/util.rb +231 -0
  46. data/test/path_command_test.rb +217 -0
  47. data/test/test_length.rb +91 -0
  48. metadata +114 -0
@@ -0,0 +1,141 @@
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 PieChart < Base
26
+ include Legend
27
+ attr_reader :chart_canvas, :data_label_canvas, :legend_canvas
28
+
29
+ opt_accessor :center_point, {:type => :point, :default_method => :default_center_point}
30
+ opt_accessor :chart_radius_x, {:type => :length, :default_method => :default_chart_radius_x}
31
+ opt_accessor :chart_radius_y, {:type => :length, :default_method => :default_chart_radius_y}
32
+ opt_accessor :represent_3d, {:type => :boolean}
33
+ opt_accessor :_3d_settings, {:type => :hash, :default => {}, :keys => [:dy], :item_type => :float}
34
+ opt_accessor :chart_colors, {:type => :array, :item_type => :color}
35
+ opt_accessor :chart_stroke_color, {:type => :color}
36
+ opt_accessor :chart_stroke_width, {:type => :float, :default => 1}
37
+ opt_accessor :moved_elements, {:type => :array, :item_type => :float}
38
+ opt_accessor :show_data_label, {:type => :boolean, :default => true}
39
+ opt_accessor :data_label_position, {:type => :float, :default => 0.8}
40
+ opt_accessor :data_label_font, {:type => :font}
41
+ opt_accessor :data_label_format, {:type => :string, :default => "{name}\n{value}"}
42
+ opt_accessor :hide_data_label_ratio, {:type => :float, :default => 0.00}
43
+
44
+ def back_translate_value
45
+ {:dy => (Length.new_or_nil(_3d_settings[:dy]) || chart_radius_y.quo(2))}
46
+ end
47
+
48
+ private
49
+
50
+ def default_center_point #:nodoc:
51
+ Coordinate.new(coordinate = ([width, height].min).quo(2), coordinate)
52
+ end
53
+
54
+ def default_chart_radius_x #:nodoc:
55
+ [[width, height].min / 2 - Length.new(32), [width, height].min * 0.4].max
56
+ end
57
+
58
+ def default_chart_radius_y #:nodoc:
59
+ represent_3d? ? chart_radius_x.quo(2) : chart_radius_x
60
+ end
61
+
62
+ def default_csv_format #:nodoc:
63
+ [:$name, 0, :$color]
64
+ end
65
+
66
+ def default_legend_point #:nodoc:
67
+ Coordinate.new(width < height ? [width * 0.1, width] : [height, height * 0.1])
68
+ end
69
+
70
+ def default_legend_format #:nodoc:
71
+ "{name}\t{percent}"
72
+ end
73
+
74
+ def create_vector_image #:nodoc:
75
+ if represent_3d?
76
+ brush = Drawing::ColumnBrush.new(back_translate_value.merge(chart_stroke_color ? {:stroke_width => chart_stroke_width, :stroke => chart_stroke_color} : {}))
77
+ else
78
+ brush = Drawing::Brush.new(chart_stroke_color ? {:stroke_width => chart_stroke_width, :stroke => chart_stroke_color, :stroke_miterlimit => chart_stroke_width / 2.0} : {})
79
+ end
80
+ @chart_canvas = Shape::ShapeGroup.draw_on(@canvas)
81
+ @data_label_canvas = Shape::ShapeGroup.draw_on(@canvas)
82
+ @legend_canvas = Shape::ShapeGroup.draw_on(@canvas)
83
+ total_value = data.column_values(0).inject(0.0) {|sum, value| sum + (value || 0)}
84
+ accumulation = 0.0
85
+ accumulations = []
86
+ stop_index = -1
87
+ data.column_values(0).each_with_index do |value, i|
88
+ accumulations.push(accumulation)
89
+ if value && total_value > (accumulation + value) * 2 && value != 0.0
90
+ # brush.color = data[:$color] && data[:$color][i] || chart_color(i)
91
+ brush.color = chart_color(i)
92
+ name = data.row_title(i)
93
+ draw_chart(brush, name, value, accumulation, total_value, i)
94
+ stop_index = i
95
+ end
96
+ accumulation += value if value
97
+ end
98
+ (data.column_values(0).size - 1).downto(stop_index + 1) do |i|
99
+ value = data.column_values(0)[i]
100
+ if value && value != 0.0
101
+ # brush.color = data[:$color] && data[:$color][i] || chart_color(i)
102
+ brush.color = chart_color(i)
103
+ name = data.row_title(i)
104
+ draw_chart(brush, name, value, accumulations[i], total_value, i)
105
+ end
106
+ end
107
+ # draw_legend(data.row_titles, nil, nil, data[:$color])
108
+ draw_legend(data.row_titles, nil, nil, nil)
109
+ end
110
+
111
+ def draw_chart(brush, name, value, accumulation, total_value, index) #:nodoc:
112
+ canvas = Shape::ShapeGroup.draw_on(@chart_canvas)
113
+ brush.draw_sector(
114
+ canvas,
115
+ center_point,
116
+ chart_radius_x,
117
+ chart_radius_y,
118
+ accumulation * 360.0 / total_value - 90,
119
+ value * 360.0 / total_value)
120
+
121
+ if moved_elements && (dr = moved_elements[index])
122
+ canvas.translate(
123
+ chart_radius_x * dr * Math.cos(((accumulation * 2.0 + value) / total_value - 0.5) * Math::PI),
124
+ chart_radius_y * dr * Math.sin(((accumulation * 2.0 + value) / total_value - 0.5) * Math::PI))
125
+ end
126
+
127
+ ratio = value.to_f.quo(total_value)
128
+ if show_data_label?
129
+ legend_point = Coordinate.new(
130
+ chart_radius_x * (data_label_position + (dr || 0)) * Math.cos(((accumulation * 2.0 + value) / total_value - 0.5) * Math::PI),
131
+ chart_radius_y * (data_label_position + (dr || 0)) * Math.sin(((accumulation * 2.0 + value) / total_value - 0.5) * Math::PI))
132
+ Drawing::Pen.black_pen(:font => legend_font).draw_text(
133
+ @data_label_canvas,
134
+ center_point + legend_point,
135
+ data_label_format.gsub(/\{name\}/, name).gsub(/\{value\}/, value.to_s).gsub(/\{percent\}/, '%.1f%' % (ratio * 100.0).to_s),
136
+ :text_anchor => 'middle', :font => data_label_font)
137
+ end if hide_data_label_ratio < ratio
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,201 @@
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 Table < Base
26
+ attr_reader :frame_canvas, :data_canvas
27
+
28
+ def font
29
+ @options[:font]
30
+ end
31
+
32
+ def font=(font)
33
+ if font && !font.empty?
34
+ @options[:font] = Font.new(font)
35
+ else
36
+ @options.delete(:font)
37
+ end
38
+ font
39
+ end
40
+
41
+ def row_height
42
+ @options[:row_height] ||= (font && font.draw_size || Font::DEFAULT_SIZE) * 1.4
43
+ end
44
+
45
+ def row_height=(height)
46
+ if heights
47
+ @options[:row_height] = Length.new(height)
48
+ else
49
+ @options.delete(:row_height)
50
+ end
51
+ point
52
+ end
53
+
54
+ def column_widths
55
+ @options[:column_widths]
56
+ end
57
+
58
+ def column_widths=(widths)
59
+ if widths.kind_of?(Array)
60
+ @options[:column_widths] = widths.map {|width| Length.new_or_nil(width)}
61
+ elsif widths
62
+ @options[:column_widths] = Length.new(widths)
63
+ else
64
+ @options.delete(:column_widths)
65
+ end
66
+ point
67
+ end
68
+
69
+ def column_width(index)
70
+ case column_widths
71
+ when Length then column_widths
72
+ when Array then column_widths[index]
73
+ else width / series.size
74
+ end
75
+ end
76
+
77
+ def table_width
78
+ series.inject(Length.new(0)) do |width, i|
79
+ width + column_width(i)
80
+ end
81
+ end
82
+
83
+ def table_height
84
+ row_height * data[series.first].size
85
+ end
86
+
87
+ def horizontal_positions
88
+ @options[:horizontal_positions]
89
+ end
90
+
91
+ def horizontal_positions=(positions)
92
+ if positions && !positions.empty?
93
+ @options[:horizontal_positions] = positions
94
+ else
95
+ @options.delete(:horizontal_positions)
96
+ end
97
+ positions
98
+ end
99
+
100
+ def horizontal_position(index)
101
+ @options[:horizontal_positions] && @options[:horizontal_positions][index]
102
+ end
103
+
104
+ def column_colors
105
+ @options[:column_colors]
106
+ end
107
+
108
+ def column_colors=(colors)
109
+ if colors && !colors.empty?
110
+ @options[:column_colors] = colors
111
+ else
112
+ @options.delete(:column_colors)
113
+ end
114
+ colors
115
+ end
116
+ =begin
117
+ def precedence_attribute
118
+ @options[:precedence] || :column
119
+ end
120
+
121
+ def precedence=(row_or_column)
122
+ case row_or_column.to_sym
123
+ when :column then @options[:precedence] = :column
124
+ when :row then @options[:precedence] = :row
125
+ when nil, false then @options.delete(:precedence)
126
+ else raise ArgumentError, "\"#{row_or_column}\" is invalid value"
127
+ end
128
+ row_or_column
129
+ end
130
+ =end
131
+ private
132
+
133
+ def options #:nodoc:
134
+ @options
135
+ end
136
+
137
+ def default_csv_format #:nodoc:
138
+ [0, 1]
139
+ end
140
+
141
+ def convert_data(value) #:nodoc:
142
+ value.strip
143
+ end
144
+
145
+ def create_vector_image #:nodoc:
146
+ pen = Drawing::Pen.new
147
+ @frame_canvas = Shape::ShapeGroup.draw_on(@canvas)
148
+ @data_canvas = Shape::ShapeGroup.draw_on(@canvas)
149
+ draw_frame(pen)
150
+ draw_data(pen)
151
+ end
152
+
153
+ def draw_frame(pen) #:nodoc:
154
+ w = table_width
155
+ h = table_height
156
+
157
+ draw_column_colors
158
+
159
+ pen.draw_rectangle(@frame_canvas, [0, 0], w, h)
160
+ (1...data[series.first].size).each do |i|
161
+ pen.draw_line(@frame_canvas, [0, row_height * i], [w, row_height * i])
162
+ end
163
+ series.inject(Length.new(0)) do |x, j|
164
+ pen.draw_line(@frame_canvas, [x, 0], [x, h]) if x.nonzero?
165
+ x + column_width(j)
166
+ end
167
+ end
168
+
169
+ def draw_column_colors #:nodoc:
170
+ if column_colors
171
+ brush = Drawing::Brush.new
172
+ h = table_height
173
+
174
+ series.inject(Length.new(0)) do |x, j|
175
+ if column_colors[j]
176
+ brush.color = Color.new(column_colors[j])
177
+ brush.draw_rectangle(@frame_canvas, [x, 0], column_width(j), h)
178
+ x + column_width(j)
179
+ end
180
+ end
181
+ end
182
+ end
183
+
184
+ def draw_data(pen) #:nodoc:
185
+ cell_margin = (row_height - (font && font.draw_size || Font::DEFAULT_SIZE)) / 2
186
+ series.inject(cell_margin) do |x, column_index|
187
+ y = row_height - cell_margin
188
+ data[column_index].each do |value|
189
+ case horizontal_position(column_index)
190
+ when 'middle' then pen.draw_text(@data_canvas, [x + column_width(column_index) / 2 - cell_margin, y], value, :text_anchor=>'middle')
191
+ when 'end' then pen.draw_text(@data_canvas, [x + column_width(column_index) - cell_margin * 2, y], value, :text_anchor=>'end')
192
+ else pen.draw_text(@data_canvas, [x, y], value)
193
+ end
194
+ y += row_height
195
+ end
196
+ x + column_width(column_index)
197
+ end
198
+ end
199
+ end
200
+ end
201
+ end
@@ -0,0 +1,218 @@
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
+
24
+ class Color
25
+ @@named_colors = {'aliceblue'=>[240,248,255],'antiquewhite'=>[250,235,215],'aqua'=>[0,255,255],'aquamarine'=>[127,255,212],'azure'=>[240,255,255],'beige'=>[245,245,220],'bisque'=>[255,228,196],'black'=>[0,0,0],'blanchedalmond'=>[255,235,205],'blue'=>[0,0,255],'blueviolet'=>[138,43,226],'brown'=>[165,42,42],'burlywood'=>[222,184,135],'cadetblue'=>[95,158,160],'chartreuse'=>[127,255,0],'chocolate'=>[210,105,30],'coral'=>[255,127,80],'cornflowerblue'=>[100,149,237],'cornsilk'=>[255,248,220],'crimson'=>[220,20,60],'cyan'=>[0,255,255],'darkblue'=>[0,0,139],'darkcyan'=>[0,139,139],'darkgoldenrod'=>[184,134,11],'darkgray'=>[169,169,169],'darkgreen'=>[0,100,0],'darkgrey'=>[169,169,169],'darkkhaki'=>[189,183,107],'darkmagenta'=>[139,0,139],'darkolivegreen'=>[85,107,47],'darkorange'=>[255,140,0],'darkorchid'=>[153,50,204],'darkred'=>[139,0,0],'darksalmon'=>[233,150,122],'darkseagreen'=>[143,188,143],'darkslateblue'=>[72,61,139],'darkslategray'=>[47,79,79],'darkslategrey'=>[47,79,79],'darkturquoise'=>[0,206,209],'darkviolet'=>[148,0,211],'deeppink'=>[255,20,147],'deepskyblue'=>[0,191,255],'dimgray'=>[105,105,105],'dimgrey'=>[105,105,105],'dodgerblue'=>[30,144,255],'firebrick'=>[178,34,34],'floralwhite'=>[255,250,240],'forestgreen'=>[34,139,34],'fuchsia'=>[255,0,255],'gainsboro'=>[220,220,220],'ghostwhite'=>[248,248,255],'gold'=>[255,215,0],'goldenrod'=>[218,165,32],'gray'=>[128,128,128],'grey'=>[128,128,128],'green'=>[0,128,0],'greenyellow'=>[173,255,47],'honeydew'=>[240,255,240],'hotpink'=>[255,105,180],'indianred'=>[205,92,92],'indigo'=>[75,0,130],'ivory'=>[255,255,240],'khaki'=>[240,230,140],'lavender'=>[230,230,250],'lavenderblush'=>[255,240,245],'lawngreen'=>[124,252,0],'lemonchiffon'=>[255,250,205],'lightblue'=>[173,216,230],'lightcoral'=>[240,128,128],'lightcyan'=>[224,255,255],'lightgoldenrodyellow'=>[250,250,210],'lightgray'=>[211,211,211],'lightgreen'=>[144,238,144],'lightgrey'=>[211,211,211],'lightpink'=>[255,182,193],'lightsalmon'=>[255,160,122],'lightseagreen'=>[32,178,170],'lightskyblue'=>[135,206,250],'lightslategray'=>[119,136,153],'lightslategrey'=>[119,136,153],'lightsteelblue'=>[176,196,222],'lightyellow'=>[255,255,224],'lime'=>[0,255,0],'limegreen'=>[50,205,50],'linen'=>[250,240,230],'magenta'=>[255,0,255],'maroon'=>[128,0,0],'mediumaquamarine'=>[102,205,170],'mediumblue'=>[0,0,205],'mediumorchid'=>[186,85,211],'mediumpurple'=>[147,112,219],'mediumseagreen'=>[60,179,113],'mediumslateblue'=>[123,104,238],'mediumspringgreen'=>[0,250,154],'mediumturquoise'=>[72,209,204],'mediumvioletred'=>[199,21,133],'midnightblue'=>[25,25,112],'mintcream'=>[245,255,250],'mistyrose'=>[255,228,225],'moccasin'=>[255,228,181],'navajowhite'=>[255,222,173],'navy'=>[0,0,128],'oldlace'=>[253,245,230],'olive'=>[128,128,0],'olivedrab'=>[107,142,35],'orange'=>[255,165,0],'orangered'=>[255,69,0],'orchid'=>[218,112,214],'palegoldenrod'=>[238,232,170],'palegreen'=>[152,251,152],'paleturquoise'=>[175,238,238],'palevioletred'=>[219,112,147],'papayawhip'=>[255,239,213],'peachpuff'=>[255,218,185],'peru'=>[205,133,63],'pink'=>[255,192,203],'plum'=>[221,160,221],'powderblue'=>[176,224,230],'purple'=>[128,0,128],'red'=>[255,0,0],'rosybrown'=>[188,143,143],'royalblue'=>[65,105,225],'saddlebrown'=>[139,69,19],'salmon'=>[250,128,114],'sandybrown'=>[244,164,96],'seagreen'=>[46,139,87],'seashell'=>[255,245,238],'sienna'=>[160,82,45],'silver'=>[192,192,192],'skyblue'=>[135,206,235],'slateblue'=>[106,90,205],'slategray'=>[112,128,144],'slategrey'=>[112,128,144],'snow'=>[255,250,250],'springgreen'=>[0,255,127],'steelblue'=>[70,130,180],'tan'=>[210,180,140],'teal'=>[0,128,128],'thistle'=>[216,191,216],'tomato'=>[255,99,71],'turquoise'=>[64,224,208],'violet'=>[238,130,238],'wheat'=>[245,222,179],'white'=>[255,255,255],'whitesmoke'=>[245,245,245],'yellow'=>[255,255,0],'yellowgreen'=>[154,205,50]}
26
+ @@default_format = ['rgb(%d,%d,%d)', false]
27
+ attr_reader :name
28
+
29
+ # :call-seq:
30
+ # new (color_string)
31
+ # new (rgb_array)
32
+ # new (rgb_hash)
33
+ # new (red, green, blue)
34
+ # new (color)
35
+ def initialize(*args)
36
+ case args.size
37
+ when 1 then color = args.first
38
+ when 3 then color = args
39
+ else raise ArgumentError, "wrong number of arguments (#{args.size} for #{args.size == 0 ? 1 : 3})"
40
+ end
41
+
42
+ case color
43
+ when Color
44
+ @r = color.red
45
+ @g = color.green
46
+ @b = color.blue
47
+ @name = color.name
48
+ when Array
49
+ raise ArgumentError, "illegal size of Array: #{color.size}" if color.size != 3
50
+ @r, @g, @b = color.map {|c| c.to_i & 0xff}
51
+ @name = nil
52
+ when Hash
53
+ @r, @g, @b = [:r, :g, :b].map {|key| color[key].to_i & 0xff}
54
+ @name = nil
55
+ when String, Symbol
56
+ color = color.to_s
57
+ if color =~ /^\s*#([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})\s*$/ # #ffffff
58
+ @r, @g, @b = [$1, $2, $3].map {|s| s.hex}
59
+ @name = nil
60
+ elsif color =~ /^\s*#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])\s*$/ # #fff
61
+ @r, @g, @b = [$1, $2, $3].map {|s| (s * 2).hex}
62
+ @name = nil
63
+ elsif color =~ /^\s*rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)\s*$/ # rgb(255,255,255)
64
+ @r, @g, @b = [$1, $2, $3].map {|s| s.to_i & 0xff}
65
+ @name = nil
66
+ elsif color =~ /^\s*rgb\s*\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)\s*$/ # rgb(100%,100%,100%)
67
+ @r, @g, @b = [$1, $2, $3].map {|s| (0xff * s.to_f / 100).to_i & 0xff}
68
+ @name = nil
69
+ else
70
+ raise ArgumentError, "argument is empty" if color.size == 0
71
+ raise ArgumentError, "`#{color}' is unknown color" unless rgb = @@named_colors[color.downcase]
72
+ @r, @g, @b = rgb
73
+ @name = color.downcase
74
+ end
75
+ else
76
+ raise TypeError, "#{color.class} can't be coerced into #{self.class}"
77
+ end
78
+ end
79
+
80
+ def ==(other)
81
+ return false unless other.instance_of?(self.class)
82
+ @r == other.red && @g == other.green && @b = other.blue
83
+ end
84
+
85
+ def eql?(other)
86
+ return false unless self.class == other.class
87
+ self == other
88
+ end
89
+
90
+ def hash #:nodoc:
91
+ (@r << 16) + (@g << 8) + @b
92
+ end
93
+
94
+ def named?
95
+ not @name.nil?
96
+ end
97
+
98
+ def red
99
+ @r
100
+ end
101
+
102
+ def green
103
+ @g
104
+ end
105
+
106
+ def blue
107
+ @b
108
+ end
109
+
110
+ def r_red
111
+ @r.quo(0.255).floor.quo(1000.0)
112
+ end
113
+
114
+ def r_green
115
+ @g.quo(0.255).floor.quo(1000.0)
116
+ end
117
+
118
+ def r_blue
119
+ @b.quo(0.255).floor.quo(1000.0)
120
+ end
121
+
122
+ def merge(other, ratio)
123
+ raise ArgumentError, "ratio should be number between 0 and 1" if ratio < 0 || 1 < ratio
124
+ other = self.class.new(other)
125
+ r = @r * (1.0 - ratio) + other.red * ratio
126
+ g = @g * (1.0 - ratio) + other.green * ratio
127
+ b = @b * (1.0 - ratio) + other.blue * ratio
128
+ self.class.new(r,g,b)
129
+ end
130
+
131
+ def color?
132
+ true
133
+ end
134
+
135
+ # :call-seq:
136
+ # to_s ()
137
+ # to_s (fmt, disp_ratio=false)
138
+ def to_s(*args)
139
+ args[0] = self.class.check_format(args[0]) if args[0]
140
+ args = @@default_format if args.empty?
141
+ args[0] % (args[1] ? [r_red, r_green, r_blue] : [@r,@g,@b])
142
+ end
143
+
144
+ def to_s16(opacity=nil)
145
+ opacity ? '#%02X%02X%02X%02X' % [0xff && (0xff * opacity), @r,@g,@b] : to_s('#%02X%02X%02X')
146
+ end
147
+
148
+ class << self
149
+
150
+ public
151
+
152
+ def new(*args) #:nodoc:
153
+ if args.size == 1
154
+ case args.first
155
+ when self
156
+ return args.first
157
+ when String, Symbol
158
+ if color = named_color(args.first)
159
+ return color
160
+ end
161
+ end
162
+ end
163
+ super
164
+ end
165
+
166
+ def new_or_nil(*args)
167
+ (args.size == 1 && args.first.nil?) ? nil : new(*args)
168
+ end
169
+
170
+ def method_missing(name, *args) #:nodoc:
171
+ if args.size == 0 && color = named_color(name)
172
+ instance_eval %{
173
+ def self.#{name}
174
+ @#{name}
175
+ end
176
+ }
177
+ return color
178
+ end
179
+ super
180
+ end
181
+
182
+ def set_default_format(fmt, disp_ratio=false)
183
+ org_format = @@default_format
184
+ @@default_format = [check_format(fmt), disp_ratio]
185
+ if block_given?
186
+ yield
187
+ @@default_format = org_format
188
+ end
189
+ end
190
+
191
+ def default_format
192
+ @@default_format
193
+ end
194
+
195
+ def check_format(fmt) #:nodoc:
196
+ begin
197
+ (fmt = fmt.to_s) % [0.0, 0.0, 0.0]
198
+ fmt
199
+ rescue
200
+ raise ArgumentError, "wrong format: `#{fmt}'"
201
+ end
202
+ end
203
+
204
+ private
205
+
206
+ def named_color(name) #:nodoc:
207
+ name = name.to_s.downcase
208
+ color = instance_variable_get('@' + name) rescue (return nil)
209
+ return color if color
210
+ if @@named_colors[name]
211
+ color = allocate
212
+ color.__send__(:initialize, name)
213
+ instance_variable_set('@' + name, color)
214
+ end
215
+ end
216
+ end
217
+ end
218
+ end