colorable 0.1.4 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8551829488f48f304ed2f67f7b3557805257249b
4
- data.tar.gz: e7954149790ea24ad68c0d2874be0c1002a6648a
3
+ metadata.gz: a4005fd6c6a2f7c11c4331bfc96f906c06bce741
4
+ data.tar.gz: 9af36345b9fa55dbaeb639e7c494c25ebd042ece
5
5
  SHA512:
6
- metadata.gz: b16f6cabc48e8b27d57b3da0f968c68520e198dd83078646d2c49056fc55660a9a6ba9eb8ea99babbbf3f951407cb93b859aa67b5d661014c0871eeed901be35
7
- data.tar.gz: 1aa34b017811d2e9428f4fa36e1b0ce8c468eb9e824650db27db4de37b8185cbfd9ff1a31ad9b7b07c871e3538b75bc8c3ae3a6c8241047529db1f0ffd88ab47
6
+ metadata.gz: b6645965c7f1971d803610d21763cbd120f517d2cc2b782a3c4e10e3d8fc64e19f2e95a2bc126288dfbb2a362fb0ac620216bc603f0c743f978ba691491c4004
7
+ data.tar.gz: 969e6b784f5529f579eb9f0364378be285bff790ca81c2b47e45fe8bdc413a83a33fd5452139953c92ad08966987610927c910024ea0fbad4f023fbb360f7bbb
data/README.md CHANGED
@@ -1,7 +1,11 @@
1
1
  # Colorable
2
2
 
3
- Colorable is a color handler written in Ruby which include Color and Colorset classes.
4
- A color object provide a conversion between X11 colorname, RGB, HSB and HEX or other manipulations. a colorset object represent X11 colorset which can be manipulated like enumerable object.
3
+ Colorable is a color handler written in Ruby, which has following functionalities;
4
+
5
+ 1. Color conversion: convertible between X11 colorname, HEX, RGB and HSB values.
6
+ 2. Color composition: a color object can be composible using math operators.
7
+ 3. Color enumeration: a color object can be enumerable within X11 colors.
8
+ 4. Color mode: a color object has a mode which represent output state of the color.
5
9
 
6
10
  ## Installation
7
11
 
@@ -19,119 +23,145 @@ Or install it yourself as:
19
23
 
20
24
  ## Usage
21
25
 
26
+
22
27
  Create a color object:
23
28
 
24
29
  require "colorable"
25
30
  include Colorable
26
31
 
27
- # with a X11 colorname
28
- c = Color.new :lime_green
29
- c.to_s #=> "Lime Green"
30
- c.rgb.to_a #=> [50, 205, 50]
31
- c.hsb.to_a #=> [120, 76, 80]
32
- c.hex.to_s #=> "#32CD32"
33
- c.dark? #=> false
34
-
35
- # with Array of RGB values
36
- c = Color.new [50, 205, 50]
37
- c.to_s #=> "rgb(50,205,50)"
38
- c.name.to_s #=> "Lime Green"
39
- c.hsb.to_a #=> [120, 76, 80]
40
- c.hex.to_s #=> "#32CD32"
41
-
42
- # with a HEX string
43
- c = Color.new '#32CD32'
44
- c.to_s #=> "#32CD32"
45
-
46
- # with a RGB, HSB or HEX object
47
- c = Color.new RGB.new(50, 205, 50)
48
- c = Color.new HSB.new(120, 76, 80)
49
- c = Color.new HEX.new('#32CD32')
50
-
51
- Manipulate color object:
52
-
53
- c = Color.new :lime_green
54
-
55
- c.to_s #=> "Lime Green"
56
- c.rgb.to_a #=> [50, 205, 50]
57
- c.hsb.to_a #=> [120, 76, 80]
58
- c.hex.to_s #=> "#32CD32"
59
- c.dark? #=> false
60
-
61
- # info returns information of the color
62
- c.info #=> {:NAME=>"Lime Green", :RGB=>[50, 205, 50], :HSB=>[120, 76, 80], :HEX=>"#32CD32", :MODE=>:NAME, :DARK=>false}
63
-
64
- # next, prev returns next, prev color object in X11 color sequence
65
- c.next.to_s #=> "Linen"
66
- c.next(2).to_s #=> "Magenta"
67
- c.prev.to_s #=> "Lime"
68
- c.prev(2).to_s #=> "Light Yellow"
69
-
70
- # +, - returns incremented or decremented color object
71
- (c + 1).to_s #=> "Linen"
72
- (c + 2).to_s #=> "Magenta"
73
- (c - 1).to_s #=> "Lime"
74
- (c - 2).to_s #=> "Light Yellow"
75
-
76
- Color object has a mode which represent output mode of the color. Behaviours of `#to_s`, `next`, `prev`, `+`, `-` will be changed based on the mode. You can change the mode with `#mode=` between :NAME, :RGB, :HSB, :HEX.
77
-
78
- c = Color.new 'Lime Green'
79
- c.mode = :NAME
80
- c.to_s #=> "Lime Green"
81
- c.next.to_s #=> "Linen"
32
+ # from a X11 colorname
33
+ Color.new 'Alice Blue' # => #<Colorable::Color 'Alice Blue<rgb(240,248,255)/hsb(208,6,100)/#F0F8FF>'>
82
34
 
83
- c.mode = :RGB
84
- c.to_s #=> "rgb(50,205,50)"
85
- c.next.to_s #=> "rgb(60,179,113)"
86
- c.next.name.to_s #=> "Medium Sea Green"
87
- (c + 10).to_s #=> "rgb(60,215,60)"
88
- (c + [0, 50, 100]).to_s #=> "rgb(50, 255, 150)"
35
+ # from a HEX string
36
+ Color.new '#F0F8FF' # => #<Colorable::Color 'Alice Blue<rgb(240,248,255)/hsb(208,6,100)/#F0F8FF>'>
89
37
 
90
- Shortcut for creating a color object with #to_color of String, Symbol and Array:
38
+ # from RGB values
39
+ Color.new [240, 248, 255] # => #<Colorable::Color 'Alice Blue<rgb(240,248,255)/hsb(208,6,100)/#F0F8FF>'>
91
40
 
92
- c = "Lime Green".to_color
93
- c.class #=> Colorable::Color
41
+ # from a HSB object
42
+ Color.new HSB.new(208, 6, 100) # => #<Colorable::Color 'Alice Blue<rgb(240,248,255)/hsb(208,6,100)/#F0F8FF>'>
94
43
 
95
- c = :lime_green.to_color
96
- c.class #=> Colorable::Color
44
+ # using #to_color methods
97
45
 
98
- c = "#32CD32".to_color
99
- c.class #=> Colorable::Color
46
+ 'Alice Blue'.to_color # => #<Colorable::Color 'Alice Blue<rgb(240,248,255)/hsb(208,6,100)/#F0F8FF>'>
100
47
 
101
- c = [50, 205, 50].to_color
102
- c.class #=> Colorable::Color
48
+ :alice_blue.to_color # => #<Colorable::Color 'Alice Blue<rgb(240,248,255)/hsb(208,6,100)/#F0F8FF>'>
103
49
 
104
- Create a X11 colorset object:
50
+ '#f0f8ff'.to_color # => #<Colorable::Color 'Alice Blue<rgb(240,248,255)/hsb(208,6,100)/#F0F8FF>'>
105
51
 
106
- include Colorable
52
+ [240, 248, 255].to_color # => #<Colorable::Color 'Alice Blue<rgb(240,248,255)/hsb(208,6,100)/#F0F8FF>'>
53
+
54
+ 0xf0f8ff.to_color # => #<Colorable::Color 'Alice Blue<rgb(240,248,255)/hsb(208,6,100)/#F0F8FF>'>
55
+
56
+
57
+ Color conversion:
58
+
59
+ c = Color.new :alice_blue
60
+
61
+ c.name # => "Alice Blue"
62
+ c.rgb # => [240, 248, 255]
63
+ c.hsb # => [208, 6, 100]
64
+ c.hex # => "#F0F8FF"
65
+ c.dark? # => false
66
+ c.info # => {:name=>"Alice Blue", :rgb=>[240, 248, 255], :hsb=>[208, 6, 100], :hex=>"#F0F8FF", :mode=>:NAME, :dark=>false}
67
+
68
+ [240, 248, 255].to_color.hex # => "#F0F8FF"
69
+ [240, 248, 255].to_color.hsb # => [208, 6, 100]
70
+
71
+
72
+ Color composition:
73
+
74
+ red = Color.new :red
75
+ green = Color.new :green
76
+ blue = Color.new :blue
77
+
78
+ yellow = red + green
79
+ yellow.info # => {:name=>"Yellow", :rgb=>[255, 255, 0], :hsb=>[60, 100, 100], :hex=>"#FFFF00", :mode=>:NAME, :dark=>false}
80
+
81
+ red + blue # => #<Colorable::Color 'Fuchsia<rgb(255,0,255)/hsb(300,100,100)/#FF00FF>'>
82
+
83
+ green + blue # => #<Colorable::Color 'Aqua<rgb(0,255,255)/hsb(180,100,100)/#00FFFF>'>
84
+
85
+ red + green + blue # => #<Colorable::Color 'White<rgb(255,255,255)/hsb(0,0,100)/#FFFFFF>'>
107
86
 
108
- cs = Colorset.new #=> #<Colorset 0/144 pos='Alice Blue/rgb(240,248,255)/hsb(208,6,100)'>
87
+ red - green # => #<Colorable::Color 'Black<rgb(0,0,0)/hsb(0,0,0)/#000000>'>
88
+ red * green # => #<Colorable::Color 'Black<rgb(0,0,0)/hsb(0,0,0)/#000000>'>
89
+ red / green # => #<Colorable::Color 'Yellow<rgb(255,255,0)/hsb(60,100,100)/#FFFF00>'>
90
+
91
+ Color enumeration:
92
+
93
+ c = Color.new :alice_blue
94
+
95
+ c.next # => #<Colorable::Color 'Antique White<rgb(250,235,215)/hsb(35,14,98)/#FAEBD7>'>
96
+ c.next(10) # => #<Colorable::Color 'Blue Violet<rgb(138,43,226)/hsb(271,81,89)/#8A2BE2>'>
97
+
98
+ c.prev # => #<Colorable::Color 'Yellow Green<rgb(154,205,50)/hsb(79,76,80)/#9ACD32>'>
99
+ c.prev(10) # => #<Colorable::Color 'Teal<rgb(0,128,128)/hsb(180,100,50)/#008080>'>
100
+
101
+ c + 1 # => #<Colorable::Color 'Antique White<rgb(250,235,215)/hsb(35,14,98)/#FAEBD7>'>
102
+ c + 10 # => #<Colorable::Color 'Blue Violet<rgb(138,43,226)/hsb(271,81,89)/#8A2BE2>'>
103
+ c - 1 # => #<Colorable::Color 'Yellow Green<rgb(154,205,50)/hsb(79,76,80)/#9ACD32>'>
104
+ c - 10 # => #<Colorable::Color 'Teal<rgb(0,128,128)/hsb(180,100,50)/#008080>'>
105
+
106
+ 10.times.map { c = c.next }.map(&:name) # => ["Antique White", "Aqua", "Aquamarine", "Azure", "Beige", "Bisque", "Black", "Blanched Almond", "Blue", "Blue Violet"]
107
+
108
+
109
+ Color mode:
110
+
111
+ c = Color.new :alice_blue
112
+ c.mode # => :NAME
113
+ c.to_s # => "Alice Blue"
114
+ c.next # => #<Colorable::Color 'Antique White<rgb(250,235,215)/hsb(35,14,98)/#FAEBD7>'>
115
+ c + 1 # => #<Colorable::Color 'Antique White<rgb(250,235,215)/hsb(35,14,98)/#FAEBD7>'>
116
+
117
+ c.mode = :RGB
118
+ c.to_s # => "rgb(240,248,255)"
119
+ c.next # => #<Colorable::Color 'Honeydew<rgb(240,255,240)/hsb(120,6,100)/#F0FFF0>'>
120
+ c + [15, -20, -74] # => #<Colorable::Color 'Moccasin<rgb(255,228,181)/hsb(39,29,100)/#FFE4B5>'>
121
+ c - 20 # => #<Colorable::Color '<rgb(220,228,235)/hsb(208,6,92)/#DCE4EB>'>
122
+
123
+ c.mode = :HSB # !> `*' interpreted as argument prefix
124
+ c.to_s # => "hsb(208,6,100)"
125
+ c.next # => #<Colorable::Color 'Slate Gray<rgb(112,128,144)/hsb(210,22,56)/#708090>'>
126
+ c + [152, 94, 0] # => #<Colorable::Color 'Red<rgb(255,0,0)/hsb(0,100,100)/#FF0000>'>
127
+
128
+ c.mode = :HEX
129
+ c.to_s # => "#F0F8FF"
130
+ c.next # => #<Colorable::Color 'Honeydew<rgb(240,255,240)/hsb(120,6,100)/#F0FFF0>'>
131
+ c + 4 # => #<Colorable::Color '<rgb(244,252,3)/hsb(62,99,99)/#F4FC03>'>
132
+
133
+ Create a X11 Colorset object
134
+
135
+ cs = Colorset.new # => #<Colorable::Colorset 0/144 pos='Alice Blue<rgb(240,248,255)/hsb(208,6,100)>'>
136
+
137
+ # with option
138
+ cs = Colorset.new(order: :RGB) # => #<Colorable::Colorset 0/144 pos='Black<rgb(0,0,0)/hsb(0,0,0)>'>
139
+ cs = Colorset.new(order: :HSB, dir: :-) # => #<Colorable::Colorset 0/144 pos='Light Pink<rgb(255,182,193)/hsb(352,29,100)>'>
109
140
 
110
- # with option
111
- cs = Colorset.new(order: :RGB) #=> #<Colorset 0/144 pos='Black/rgb(0,0,0)/hsb(0,0,0)'>
112
- cs = Colorset.new(order: :HSB, dir: :-) #=> #<Colorset 0/144 pos='Light Pink/rgb(255,182,193)/hsb(352,29,100)'>
113
141
 
114
142
  Manupilate colorset:
115
143
 
116
- cs = Colorset.new
117
- cs.size #=> 144
118
- cs.at.to_s #=> "Alice Blue"
119
- cs.at(1).to_s #=> "Antique White"
120
- cs.at(2).to_s #=> "Aqua"
144
+ cs = Colorset.new
145
+ cs.size # => 144
146
+ cs.at # => #<Colorable::Color 'Alice Blue<rgb(240,248,255)/hsb(208,6,100)/#F0F8FF>'>
147
+ cs.at.to_s # => "Alice Blue"
148
+ cs.at(1).to_s # => "Antique White"
149
+ cs.at(2).to_s # => "Aqua"
150
+
151
+ # next(prev) methods moves cursor position
152
+ cs.next.to_s # => "Antique White"
153
+ cs.at.to_s # => "Antique White"
154
+ cs.next.to_s # => "Aqua"
155
+ cs.at.to_s # => "Aqua"
156
+ cs.rewind
157
+ cs.at.to_s # => "Alice Blue"
121
158
 
122
- # next(prev) methods moves cursor position
123
- cs.next.to_s #=> "Antique White"
124
- cs.at.to_s #=> "Antique White"
125
- cs.next.to_s #=> "Aqua"
126
- cs.at.to_s #=> "Aqua"
127
- cs.rewind
128
- cs.at.to_s #=> "Alice Blue"
159
+ cs.map(&:to_s).take(10) # => ["Alice Blue", "Antique White", "Aqua", "Aquamarine", "Azure", "Beige", "Bisque", "Black", "Blanched Almond", "Blue"]
129
160
 
130
- cs.map(&:to_s).take(10) #=> ["Alice Blue", "Antique White", "Aqua", "Aquamarine", "Azure", "Beige", "Bisque", "Black", "Blanched Almond", "Blue"]
161
+ cs.sort_by(&:rgb).take(10).map(&:rgb) # => [[0, 0, 0], [0, 0, 128], [0, 0, 139], [0, 0, 205], [0, 0, 255], [0, 100, 0], [0, 128, 0], [0, 128, 128], [0, 139, 139], [0, 191, 255]]
131
162
 
132
- cs.sort_by(&:rgb).map(&:to_s).take(10) #=> ["Black", "Navy", "Dark Blue", "Medium Blue", "Blue", "Dark Green", "Green2", "Teal", "Dark Cyan", "Deep Sky Blue"]
163
+ cs.sort_by(&:hsb).take(10).map(&:hsb) # => [[0, 0, 0], [0, 0, 41], [0, 0, 50], [0, 0, 66], [0, 0, 75], [0, 0, 75], [0, 0, 83], [0, 0, 86], [0, 0, 96], [0, 0, 100]]
133
164
 
134
- cs.sort_by(&:hsb).map(&:to_s).take(10) #=> ["Black", "Dim Gray", "Gray2", "Dark Gray", "Gray", "Silver", "Light Gray", "Gainsboro", "White Smoke", "White"]
135
165
 
136
166
 
137
167
  ## Contributing
@@ -12,6 +12,10 @@ module Colorable
12
12
 
13
13
  end
14
14
 
15
- [String, Symbol, Array].each do |klass|
16
- klass.class_eval { define_method(:to_color) { Colorable::Color.new self } }
15
+ [String, Symbol, Array, Fixnum].each do |klass|
16
+ klass.class_eval do
17
+ define_method(:to_color) do
18
+ Colorable::Color.new (klass==Fixnum ? self.to_s(16) : self)
19
+ end
20
+ end
17
21
  end
@@ -2,7 +2,6 @@ module Colorable
2
2
  class Color
3
3
  class NameError < StandardError; end
4
4
  include Comparable
5
- attr_reader :name, :rgb
6
5
 
7
6
  # Create a Color object which has several representations of a color.
8
7
  #
@@ -24,7 +23,7 @@ module Colorable
24
23
 
25
24
  # Set output mode.
26
25
  def mode=(mode)
27
- modes = [rgb, hsb, name, hex]
26
+ modes = [_rgb, _hsb, _name, _hex]
28
27
  @mode = modes.detect { |m| m.class.to_s.match /#{mode}/i } || begin
29
28
  raise ArgumentError, "Invalid mode given"
30
29
  end
@@ -34,37 +33,65 @@ module Colorable
34
33
  @mode.to_s
35
34
  end
36
35
 
36
+ def inspect
37
+ "#<%s '%s<%s/%s/%s>'>" % [self.class, _name, _rgb, _hsb, _hex]
38
+ end
39
+
37
40
  # Returns information of the color object
38
41
  def info
39
42
  {
40
- NAME: name.to_s,
41
- RGB: rgb.to_a,
42
- HSB: hsb.to_a,
43
- HEX: hex.to_s,
44
- MODE: mode,
45
- DARK: dark?
43
+ name: name,
44
+ rgb: rgb,
45
+ hsb: hsb,
46
+ hex: hex,
47
+ mode: mode,
48
+ dark: dark?
46
49
  }
47
50
  end
48
51
 
52
+ def _name
53
+ @name
54
+ end
55
+
56
+ def _rgb
57
+ @rgb
58
+ end
59
+
60
+ def _hex
61
+ @hex ||= HEX.new _rgb.to_hex
62
+ end
63
+
64
+ def _hsb
65
+ @hsb ||= HSB.new *_rgb.to_hsb
66
+ end
67
+
68
+ def name
69
+ _name.to_s
70
+ end
71
+
49
72
  def hex
50
- @hex ||= HEX.new rgb.to_hex
73
+ _hex.to_s
74
+ end
75
+
76
+ def rgb
77
+ _rgb.to_a
51
78
  end
52
79
 
53
80
  def hsb
54
- @hsb ||= HSB.new *rgb.to_hsb
81
+ _hsb.to_a
55
82
  end
56
83
  alias :hsv :hsb
57
84
 
58
85
  %w(red green blue).each do |c|
59
- define_method(c) { rgb.send c }
86
+ define_method(c) { _rgb.send c }
60
87
  end
61
88
 
62
89
  %w(hue sat bright).each do |c|
63
- define_method(c) { hsb.send c }
90
+ define_method(c) { _hsb.send c }
64
91
  end
65
92
 
66
93
  def <=>(other)
67
- if [self.name, other.name].any?(&:nil?)
94
+ if [self.name, other.name].any?(&:empty?)
68
95
  self.rgb <=> other.rgb
69
96
  else
70
97
  self.name <=> other.name
@@ -79,6 +106,7 @@ module Colorable
79
106
  idx = @@colorset[mode].find_index(self)
80
107
  @@colorset[mode].at(idx+n).tap{|c| c.mode = mode } if idx
81
108
  end
109
+ alias :succ :next
82
110
 
83
111
  # Returns a previous color object in X11 colors.
84
112
  # The color sequence is determined by its color mode.
@@ -87,21 +115,51 @@ module Colorable
87
115
  end
88
116
 
89
117
  def dark?
90
- !!DARK_COLORS.detect { |d| d == name.to_s }
118
+ !!DARK_COLORS.detect { |d| d == self.name }
91
119
  end
92
120
 
93
- # Returns a color object which has incremented color values.
94
- # Array of values or a Fixnum is acceptable as an argument.
95
- # Which values are affected is determined by its color mode.
96
- def +(arg)
97
- self.class.new @mode + arg
121
+ # Color addition
122
+ #
123
+ # +other+ can be:
124
+ # Color object: apply minimum compositing with its RGBs.
125
+ # Array of values or Fixnum: addiction applies based on its color mode.
126
+ def +(other)
127
+ case other
128
+ when Color
129
+ new_by_composed_rgb(:+, other)
130
+ else
131
+ self.class.new @mode + other
132
+ end
133
+ end
134
+
135
+ # Color subtruction
136
+ #
137
+ # +other+ can be:
138
+ # Color object: apply maximum compositing with its RGBs.
139
+ # Array of values or Fixnum: subtruction applies based on its color mode.
140
+ def -(other)
141
+ case other
142
+ when Color
143
+ new_by_composed_rgb(:-, other)
144
+ else
145
+ self.class.new @mode - other
146
+ end
98
147
  end
99
148
 
100
- # Returns a color object which has decremented color values.
101
- # Array of values or a Fixnum is acceptable as an argument.
102
- # Which values are affected is determined by its color mode.
103
- def -(arg)
104
- self.class.new @mode - arg
149
+ # Color multiplication
150
+ #
151
+ # +other+ should be a Color object.
152
+ # It applies multiply compositing with its RGBs.
153
+ def *(other)
154
+ new_by_composed_rgb(:*, other)
155
+ end
156
+
157
+ # Color division
158
+ #
159
+ # +other+ should be a Color object.
160
+ # It applies screen compositing with its RGBs.
161
+ def /(other)
162
+ new_by_composed_rgb(:/, other)
105
163
  end
106
164
 
107
165
  private
@@ -163,5 +221,10 @@ module Colorable
163
221
  raise NameError, "Invalid color name given" unless res.name
164
222
  end
165
223
  end
224
+
225
+ def new_by_composed_rgb(op, other)
226
+ rgb = self._rgb.send(op, other._rgb)
227
+ self.class.new(rgb).tap { |c| c.mode = self.mode }
228
+ end
166
229
  end
167
230
  end
@@ -1,36 +1,36 @@
1
1
  module Colorable
2
- class ColorSpace
2
+ class ColorSpace
3
3
  include Colorable::Converter
4
- include Comparable
4
+ include Comparable
5
5
 
6
- def <=>(other)
7
- self.to_a <=> other.to_a
8
- end
6
+ def <=>(other)
7
+ self.to_a <=> other.to_a
8
+ end
9
9
 
10
- def move_to_top(idx)
11
- arr = self.to_a
12
- arr.insert 0, arr.delete_at(idx)
13
- end
10
+ def move_to_top(idx)
11
+ arr = self.to_a
12
+ arr.insert 0, arr.delete_at(idx)
13
+ end
14
14
 
15
- def +(arg)
16
- raise "Subclass must implement it"
17
- end
15
+ def +(arg)
16
+ raise "Subclass must implement it"
17
+ end
18
18
 
19
- def -(arg)
20
- arg = arg.is_a?(Fixnum) ? -arg : arg.map(&:-@)
21
- self + arg
22
- end
19
+ def -(arg)
20
+ arg = arg.is_a?(Fixnum) ? -arg : arg.map(&:-@)
21
+ self + arg
22
+ end
23
23
 
24
- def coerce(arg)
25
- [self, arg]
26
- end
24
+ def coerce(arg)
25
+ [self, arg]
26
+ end
27
27
 
28
- def to_s
29
- name = "#{self.class}"[/\w+$/].downcase
28
+ def to_s
29
+ name = "#{self.class}"[/\w+$/].downcase
30
30
  "#{name}(%i,%i,%i)" % to_a
31
- end
31
+ end
32
32
 
33
- private
33
+ private
34
34
  def validate(pattern, data)
35
35
  case Array(data)
36
36
  when Pattern[*Array(pattern)] then data
@@ -38,138 +38,183 @@ module Colorable
38
38
  raise ArgumentError, "'#{data}' is invalid for a #{self.class} value"
39
39
  end
40
40
  end
41
- end
42
-
43
- class RGB < ColorSpace
44
- attr_accessor :rgb, :r, :g, :b
45
- def initialize(r=0,g=0,b=0)
46
- @r, @g, @b = @rgb = validate_rgb([r, g, b])
47
- end
48
- alias :red :r
49
- alias :green :g
50
- alias :blue :b
51
- alias :to_a :rgb
52
-
53
- # Pass Array of [r, g, b] or a Fixnum.
54
- # Returns new RGB object with added RGB.
55
- def +(arg)
56
- arg =
57
- case arg
58
- when Fixnum
59
- [arg] * 3
60
- when Array
61
- raise ArgumentError, "Must be three numbers contained" unless arg.size==3
62
- arg
63
- else
64
- raise ArgumentError, "Accept only Array of three numbers or a Fixnum"
65
- end
66
- new_rgb = self.rgb.zip(arg).map { |x, y| x + y }
67
- self.class.new *new_rgb
68
- end
69
-
70
- def to_name
71
- rgb2name(self.to_a)
72
- end
73
-
74
- def to_hsb
75
- rgb2hsb(self.to_a)
76
- end
77
-
78
- def to_hex
79
- rgb2hex(self.to_a)
80
- end
81
-
82
- private
41
+ end
42
+
43
+ class RGB < ColorSpace
44
+ attr_accessor :rgb, :r, :g, :b
45
+ def initialize(r=0,g=0,b=0)
46
+ @r, @g, @b = @rgb = validate_rgb([r, g, b])
47
+ end
48
+ alias :red :r
49
+ alias :green :g
50
+ alias :blue :b
51
+ alias :to_a :rgb
52
+
53
+ # Color addition
54
+ #
55
+ # +other+ can be:
56
+ # RGB object: apply minimum compositing.
57
+ # Array of RGB values: each values added to each of RGBs.
58
+ # Fixnum: other number added to all of RGBs.
59
+ def +(other)
60
+ rgb =
61
+ case other
62
+ when RGB
63
+ compound_by(other.rgb) { |a, b| [a+b, 255].min }
64
+ when Array
65
+ raise ArgumentError, "Invalid size of Array given" unless other.size==3
66
+ compound_by(other) { |a, b| (a + b) % 256 }
67
+ when Fixnum
68
+ compound_by([other] * 3) { |a, b| (a + b) % 256 }
69
+ else
70
+ raise ArgumentError, "Invalid argument given"
71
+ end
72
+ self.class.new *rgb
73
+ end
74
+
75
+ # Color subtruction
76
+ #
77
+ # +other+ can be:
78
+ # RGB object: apply maximum compositing.
79
+ # Array of RGB values: each values added to each of RGBs.
80
+ # Fixnum: other number added to all of RGBs.
81
+ def -(other)
82
+ case other
83
+ when RGB
84
+ rgb = compound_by(other.rgb) { |a, b| [a+b-255, 0].max }
85
+ self.class.new *rgb
86
+ else
87
+ super
88
+ end
89
+ end
90
+
91
+ # Color multiplication
92
+ #
93
+ # +other+ should be a Color object.
94
+ # It applies multiply compositing.
95
+ def *(other)
96
+ raise ArgumentError, "Invalid argument given" unless other.is_a?(RGB)
97
+ rgb = compound_by(other.rgb) { |a, b| [(a*b/255.0).round, 0].max }
98
+ self.class.new *rgb
99
+ end
100
+
101
+ # Color division
102
+ #
103
+ # +other+ should be a Color object.
104
+ # It applies screen compositing.
105
+ def /(other)
106
+ raise ArgumentError, "Invalid argument given" unless other.is_a?(RGB)
107
+ rgb = compound_by(other.rgb) { |a, b| [a+b-(a*b/255.0).round, 255].min }
108
+ self.class.new *rgb
109
+ end
110
+
111
+ def to_name
112
+ rgb2name(self.to_a)
113
+ end
114
+
115
+ def to_hsb
116
+ rgb2hsb(self.to_a)
117
+ end
118
+
119
+ def to_hex
120
+ rgb2hex(self.to_a)
121
+ end
122
+
123
+ private
83
124
  def validate_rgb(rgb)
84
125
  validate([0..255, 0..255, 0..255], rgb)
85
126
  end
86
- end
87
-
88
- class HSB < ColorSpace
89
- attr_accessor :hsb, :h, :s, :b
90
- def initialize(h=0,s=0,b=0)
91
- @h, @s, @b = @hsb = validate_hsb([h, s, b])
92
- end
93
- alias :hue :h
94
- alias :sat :s
95
- alias :bright :b
96
- alias :to_a :hsb
97
- undef :coerce
98
-
99
- # Pass Array of [h, s, b] or a Fixnum.
100
- # Returns new HSB object with added HSB.
101
- def +(arg)
102
- arg =
103
- case arg
104
- when Array
105
- raise ArgumentError, "Must be three numbers contained" unless arg.size==3
106
- arg
107
- else
108
- raise ArgumentError, "Accept only Array of three numbers"
109
- end
110
- new_hsb = self.hsb.zip(arg).map { |x, y| x + y }
111
- self.class.new *new_hsb
112
- end
113
-
114
- def to_name
115
- rgb2name(self.to_rgb)
116
- end
117
-
118
- def to_rgb
119
- hsb2rgb(self.to_a)
120
- end
121
-
122
- def to_hex
123
- rgb2hex(self.to_rgb)
124
- end
125
-
126
- private
127
+
128
+ def compound_by(rgb, &blk)
129
+ self.rgb.zip(rgb).map(&blk)
130
+ end
131
+ end
132
+
133
+ class HSB < ColorSpace
134
+ attr_accessor :hsb, :h, :s, :b
135
+ def initialize(h=0,s=0,b=0)
136
+ @h, @s, @b = @hsb = validate_hsb([h, s, b])
137
+ end
138
+ alias :hue :h
139
+ alias :sat :s
140
+ alias :bright :b
141
+ alias :to_a :hsb
142
+ undef :coerce
143
+
144
+ # Pass Array of [h, s, b] or a Fixnum.
145
+ # Returns new HSB object with added HSB.
146
+ def +(arg)
147
+ arg =
148
+ case arg
149
+ when Array
150
+ raise ArgumentError, "Must be three numbers contained" unless arg.size==3
151
+ arg
152
+ else
153
+ raise ArgumentError, "Accept only Array of three numbers"
154
+ end
155
+ new_hsb = self.hsb.zip(arg, [360, 101, 101]).map { |x, y, div| (x + y) % div }
156
+ self.class.new *new_hsb
157
+ end
158
+
159
+ def to_name
160
+ rgb2name(self.to_rgb)
161
+ end
162
+
163
+ def to_rgb
164
+ hsb2rgb(self.to_a)
165
+ end
166
+
167
+ def to_hex
168
+ rgb2hex(self.to_rgb)
169
+ end
170
+
171
+ private
127
172
  def validate_hsb(hsb)
128
173
  validate([0..359, 0..100, 0..100], hsb)
129
174
  end
130
- end
175
+ end
131
176
 
132
- class HEX < ColorSpace
133
- attr_reader :hex
134
- def initialize(hex='#FFFFFF')
135
- @hex = validate_hex(hex)
136
- end
137
- alias :to_s :hex
177
+ class HEX < ColorSpace
178
+ attr_reader :hex
179
+ def initialize(hex='#FFFFFF')
180
+ @hex = validate_hex(hex)
181
+ end
182
+ alias :to_s :hex
138
183
 
139
- def to_a
140
- @hex.unpack('A1A2A2A2').drop(1)
141
- end
184
+ def to_a
185
+ @hex.unpack('A1A2A2A2').drop(1)
186
+ end
142
187
 
143
- def +(arg)
144
- build_hex_with(:+, arg)
145
- end
188
+ def +(arg)
189
+ build_hex_with(:+, arg)
190
+ end
146
191
 
147
- def -(arg)
148
- build_hex_with(:-, arg)
149
- end
192
+ def -(arg)
193
+ build_hex_with(:-, arg)
194
+ end
150
195
 
151
- def to_rgb
152
- hex2rgb(self.to_s)
153
- end
196
+ def to_rgb
197
+ hex2rgb(self.to_s)
198
+ end
154
199
 
155
- def to_hsb
156
- rgb2hsb(self.to_rgb)
157
- end
200
+ def to_hsb
201
+ rgb2hsb(self.to_rgb)
202
+ end
158
203
 
159
- def to_name
160
- rgb2name(self.to_rgb)
161
- end
204
+ def to_name
205
+ rgb2name(self.to_rgb)
206
+ end
162
207
 
163
208
  private
164
209
  def validate_hex(hex)
165
- hex = hex.join if hex.is_a?(Array)
210
+ hex = hex.join if hex.is_a?(Array)
166
211
  validate(/^#[0-9A-F]{6}$/i, hex_norm(hex))
167
212
  end
168
213
 
169
214
  def hex_norm(hex)
170
- hex = hex.to_s.sub(/^#/, '').upcase
171
- .sub(/^([0-9A-F])([0-9A-F])([0-9A-F])$/, '\1\1\2\2\3\3')
172
- "##{hex}"
215
+ hex = hex.to_s.sub(/^#/, '').upcase
216
+ .sub(/^([0-9A-F])([0-9A-F])([0-9A-F])$/, '\1\1\2\2\3\3')
217
+ "##{hex}"
173
218
  end
174
219
 
175
220
  def rgb2hex(rgb)
@@ -185,76 +230,76 @@ module Colorable
185
230
  end
186
231
 
187
232
  def build_hex_with(op, arg)
188
- _rgb =
189
- case arg
190
- when Fixnum
191
- [arg] * 3
192
- when String
193
- hex2rgb(validate_hex arg)
194
- else
195
- raise ArgumentError, "Accept only a Hex string or a Fixnum"
196
- end
197
- rgb = hex2rgb(self.hex).zip(_rgb).map { |x, y| x.send(op, y) }
198
- self.class.new rgb2hex(rgb)
199
- end
200
- end
201
-
202
- class NAME < ColorSpace
203
- attr_accessor :name
204
- attr_reader :sym
205
- def initialize(name)
206
- @name = find_name(name)
207
- @sym = nil
208
- end
209
-
210
- alias :to_s :name
211
-
212
- def sym(sep='_')
213
- @name.gsub(/\s/, sep).downcase.intern if @name
214
- end
215
-
216
- def dark?
233
+ _rgb =
234
+ case arg
235
+ when Fixnum
236
+ [arg] * 3
237
+ when String
238
+ hex2rgb(validate_hex arg)
239
+ else
240
+ raise ArgumentError, "Accept only a Hex string or a Fixnum"
241
+ end
242
+ rgb = hex2rgb(self.hex).zip(_rgb).map { |x, y| (x.send(op, y)) % 256 }
243
+ self.class.new rgb2hex(rgb)
244
+ end
245
+ end
246
+
247
+ class NAME < ColorSpace
248
+ attr_accessor :name
249
+ attr_reader :sym
250
+ def initialize(name)
251
+ @name = find_name(name)
252
+ @sym = nil
253
+ end
254
+
255
+ alias :to_s :name
256
+
257
+ def sym(sep='_')
258
+ @name.gsub(/\s/, sep).downcase.intern if @name
259
+ end
260
+
261
+ def dark?
217
262
  DARK_COLORS.detect { |d| d == self.name }
218
- end
219
-
220
- def <=>(other)
221
- self.name <=> other.name
222
- end
223
-
224
- def +(n)
225
- raise ArgumentError, 'Only accept a Fixnum' unless n.is_a?(Fixnum)
226
- pos = COLORNAMES.find_index{|n,_| n==self.name} + n
227
- self.class.new COLORNAMES.at(pos % COLORNAMES.size)[0]
228
- end
229
-
230
- def -(n)
231
- raise ArgumentError, 'Only accept a Fixnum' unless n.is_a?(Fixnum)
232
- self + -n
233
- end
234
-
235
- def coerce(arg)
236
- [self, arg]
237
- end
238
-
239
- def to_rgb
240
- name2rgb(self.to_s)
241
- end
242
-
243
- def to_hsb
244
- rgb2hsb(self.to_rgb)
245
- end
246
-
247
- def to_hex
248
- rgb2hex(self.to_rgb)
249
- end
250
-
251
- private
252
- def find_name(name)
253
- COLORNAMES.map(&:first).detect { |c|
263
+ end
264
+
265
+ def <=>(other)
266
+ self.name <=> other.name
267
+ end
268
+
269
+ def +(n)
270
+ raise ArgumentError, 'Only accept a Fixnum' unless n.is_a?(Fixnum)
271
+ pos = COLORNAMES.find_index{|n,_| n==self.name} + n
272
+ self.class.new COLORNAMES.at(pos % COLORNAMES.size)[0]
273
+ end
274
+
275
+ def -(n)
276
+ raise ArgumentError, 'Only accept a Fixnum' unless n.is_a?(Fixnum)
277
+ self + -n
278
+ end
279
+
280
+ def coerce(arg)
281
+ [self, arg]
282
+ end
283
+
284
+ def to_rgb
285
+ name2rgb(self.to_s)
286
+ end
287
+
288
+ def to_hsb
289
+ rgb2hsb(self.to_rgb)
290
+ end
291
+
292
+ def to_hex
293
+ rgb2hex(self.to_rgb)
294
+ end
295
+
296
+ private
297
+ def find_name(name)
298
+ COLORNAMES.map(&:first).detect { |c|
254
299
  [c, name].same? { |str| "#{str}".gsub(/[_\s]/,'').downcase }
255
- } || begin
256
- raise ArgumentError, "'#{name}' is not in X11 colorset."
257
- end
258
- end
259
- end
300
+ } || begin
301
+ raise ArgumentError, "'#{name}' is not in X11 colorset."
302
+ end
303
+ end
304
+ end
260
305
  end