colorable 0.1.4 → 0.2.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.
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