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.
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