pigment 0.2.3 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,195 @@
1
+ require_relative '../pigment'
2
+
3
+ module Pigment
4
+ # Represent an abstract color in any format
5
+ module Color
6
+ class << self
7
+ # @param [#to_f] red
8
+ # @param [#to_f] blue
9
+ # @param [#to_f] green
10
+ # @param [#to_f] alpha
11
+ # @return [Pigment::Color::RGB]
12
+ def rgba(red, blue, green, alpha = 1.0)
13
+ Pigment::Color::RGB.new(red, blue, green, alpha)
14
+ end
15
+
16
+ # @param [Integer] hue between 0 and 360 degrees
17
+ # @param [#to_f] saturation between 0.0 and 1.0
18
+ # @param [#to_f] lightness between 0.0 and 1.0
19
+ # @param [#to_f] alpha between 0.0 and 1.0
20
+ # @return [Pigment::Color::HSL]
21
+ def hsla(hue, saturation, lightness, alpha = 1.0)
22
+ Pigment::Color::HSL.from_hue_angle(hue, saturation, lightness, alpha)
23
+ end
24
+
25
+ alias_method :rgb, :rgba
26
+ alias_method :hsl, :hsla
27
+
28
+ # sets aliases for any color format
29
+ # @param [Class] klass
30
+ def included(klass)
31
+ klass.alias_method :gray?, :grayscale?
32
+ klass.alias_method :-@, :inverse
33
+ klass.alias_method :inv, :inverse
34
+ klass.alias_method :invert, :inverse
35
+ klass.alias_method :complementary, :inverse
36
+ klass.alias_method :complement, :inverse
37
+ klass.alias_method :to_floats, :to_a
38
+ klass.alias_method :to_f, :to_a
39
+ klass.alias_method :inspect, :to_s
40
+ end
41
+ end
42
+
43
+ # @param [Class] color_type
44
+ # @return [Pigment::Color]
45
+ def into(color_type)
46
+ color_type.convert(self)
47
+ end
48
+
49
+ # @return [Pigment::Color]
50
+ def dup
51
+ self.class.new(*to_a)
52
+ end
53
+
54
+ # @return [Pigment::Color]
55
+ def inverse
56
+ into(Pigment::Color::RGB).inverse.into(self.class)
57
+ end
58
+
59
+ # @param [Object] other
60
+ # @return [Boolean]
61
+ def inverse?(other)
62
+ other.is_a?(Pigment::Color) && self.inverse == other
63
+ end
64
+
65
+ # @return [Array<Pigment::Color>]
66
+ def triadic
67
+ into(Pigment::Color::RGB).triadic.map { |color| color.into(self.class) }
68
+ end
69
+
70
+ # @param [Object] other
71
+ # @return [Boolean]
72
+ def triadic_of?(other)
73
+ other.into(self.class)
74
+ other.triadic.include?(self)
75
+ end
76
+
77
+ # @param [Array<Object>] others
78
+ # @return [Boolean]
79
+ def triadic_include?(*others)
80
+ colors = triadic
81
+ others.all? { |color| colors.include?(color) }
82
+ end
83
+
84
+ # @return [Array<Pigment::Color>]
85
+ def split
86
+ into(Pigment::Color::HSL).split.map { |color| color.into(self.class) }
87
+ end
88
+
89
+ # @param [Object] other
90
+ # @return [Boolean]
91
+ def split_of?(other)
92
+ other.split.include?(self)
93
+ end
94
+
95
+ # @param [Array<Object>] others
96
+ # @return [Boolean]
97
+ def split_include?(*others)
98
+ colors = split
99
+ others.all? { |color| colors.include?(color) }
100
+ end
101
+
102
+ # @return [Array<Pigment::Color>]
103
+ def analogous
104
+ into(Pigment::Color::HSL).analogous.map { |color| color.into(self.class) }
105
+ end
106
+
107
+ # @param [Object] other
108
+ # @return [Boolean]
109
+ def analogous_of?(other)
110
+ other.analogous.include?(self)
111
+ end
112
+
113
+ # @param [Array<Object>] others
114
+ # @return [Boolean]
115
+ def analogous_include?(*others)
116
+ colors = analogous
117
+ others.all? { |color| colors.include?(color) }
118
+ end
119
+
120
+ # @return [Array<Pigment::Color>]
121
+ def tetradic
122
+ into(Pigment::Color::HSL).tetradic.map { |color| color.into(self.class) }
123
+ end
124
+
125
+ # @param [Object] other
126
+ # @return [Boolean]
127
+ def tetradic_of?(other)
128
+ other.tetradic.include?(self)
129
+ end
130
+
131
+ # @param [Array<Object>] others
132
+ # @return [Boolean]
133
+ def tetradic_include?(*others)
134
+ others.all? { |color| tetradic.include?(color) }
135
+ end
136
+
137
+ # @return [Array<Pigment::Color>]
138
+ def rectangular
139
+ into(Pigment::Color::HSL).rectangular.map { |color| color.into(self.class) }
140
+ end
141
+
142
+ # @param [Object] other
143
+ # @return [Boolean]
144
+ def rectangular_of?(other)
145
+ other.rectangular.include?(self)
146
+ end
147
+
148
+ # @param [Array<Object>] others
149
+ # @return [Boolean]
150
+ def rectangular_include?(*others)
151
+ colors = rectangular
152
+ others.all? { |color| colors.include?(color) }
153
+ end
154
+
155
+ # @return [Array<Pigment::Color>]
156
+ def tertiary
157
+ into(Pigment::Color::HSL).tertiary.map { |color| color.into(self.class) }
158
+ end
159
+
160
+ # @param [Object] other
161
+ # @return [Boolean]
162
+ def tertiary_of?(other)
163
+ other.tertiary.include?(self)
164
+ end
165
+
166
+ # @param [Array<Object>] others
167
+ # @return [Boolean]
168
+ def tertiary_include?(*others)
169
+ colors = tertiary
170
+ others.all? { |color| colors.include?(color) }
171
+ end
172
+
173
+ # @param [Boolean] with_alpha
174
+ # @return [String]
175
+ def to_html(with_alpha: false)
176
+ "##{to_hex(with_alpha: with_alpha)}"
177
+ end
178
+
179
+ # @param [Boolean] with_alpha
180
+ # @return [Array<Integer>]
181
+ def to_ints(with_alpha: true)
182
+ into(Pigment::Color::RGB).to_ints(with_alpha: with_alpha)
183
+ end
184
+
185
+ # @param [Boolean] with_alpha
186
+ # @return [String]
187
+ def to_hex(with_alpha: true)
188
+ into(Pigment::Color::RGB).to_hex(with_alpha: with_alpha)
189
+ end
190
+ end
191
+ end
192
+
193
+ require_relative 'color/rgb'
194
+ require_relative 'color/hsl'
195
+
@@ -0,0 +1,154 @@
1
+ require_relative '../color'
2
+
3
+ module Pigment
4
+ module Color
5
+ # Represent a color in the HSL Format
6
+ class HSL
7
+ using FloatSnap
8
+
9
+ # @return [Float]
10
+ attr_reader :hue, :saturation, :lightness, :alpha
11
+
12
+ class << self
13
+
14
+ # Converts a color into HSL format from any possible format, given they know how to convert to RGB
15
+ # @param [Pigment::Color] color
16
+ # @return [Pigment::Color::HSL]
17
+ def convert(color)
18
+ return color if color.is_a?(self)
19
+ color = color.into(Pigment::Color::RGB)
20
+ rgb = color.rgb
21
+ r, g, b = rgb
22
+
23
+ min = rgb.min
24
+ max = rgb.max
25
+ chroma = max - min
26
+ sum = max + min
27
+ l = sum / 2.0
28
+
29
+ return new(0, 0, l, color.alpha) if chroma == 0.0
30
+
31
+ s = l > 0.5 ? chroma / (2.0 - sum) : chroma / sum
32
+
33
+ h = case max
34
+ when r then ((g - b) / chroma) / 6 + (g < b && 1 || 0)
35
+ when g then ((b - r) / chroma) / 6.0 + (1.0 / 3.0)
36
+ when b then ((r - g) / chroma) / 6.0 + (2.0 / 3.0)
37
+ end
38
+
39
+ new(h, s, l, color.alpha)
40
+ end
41
+
42
+ # @param [Integer] hue between 0 and 360 degrees
43
+ # @param [#to_f] saturation between 0.0 and 1.0
44
+ # @param [#to_f] lightness between 0.0 and 1.0
45
+ # @param [#to_f] alpha between 0.0 and 1.0
46
+ # @return [Pigment::Color::HSL]
47
+ def from_hue_angle(hue, saturation, lightness, alpha = 1.0)
48
+ new(hue / 360.0, saturation, lightness, alpha)
49
+ end
50
+ end
51
+
52
+ # @param [#to_f] hue between 0.0 and 1.0 mapping to 0 to 360 degrees
53
+ # @param [#to_f] saturation between 0.0 and 1.0
54
+ # @param [#to_f] lightness between 0.0 and 1.0
55
+ # @param [#to_f] alpha between 0.0 and 1.0
56
+ # @raise [InvalidColorFormatError]
57
+ # @return [Pigment::Color::HSL]
58
+ def initialize(hue, saturation, lightness, alpha = 1.0)
59
+ @hue, @saturation, @lightness, @alpha = hue % 1.0, saturation.to_f, lightness.to_f, alpha.to_f
60
+ color = to_f(with_alpha: true)
61
+ raise InvalidColorFormatError, color unless color.all? { |c| c.between?(0.0, 1.0) }
62
+ end
63
+
64
+ # @return [Pigment::Color::HSL] the grayscale correspondent of the color
65
+ def grayscale
66
+ self.class.new(@hue, 0, @lightness, @alpha)
67
+ end
68
+
69
+ # @return [Boolean] true if saturation is 0, false otherwise
70
+ def grayscale?
71
+ @saturation == 0
72
+ end
73
+
74
+ # @return [Array<Pigment::Color>] An array with the triadic colors of the color
75
+ def triadic
76
+ [self.class.new(@hue + 1 / 3.0, s, l), self.class.new(@hue + 2 / 3.0, s, l)]
77
+ end
78
+
79
+ # @return [Array<Pigment::Color>] An array with the split colors of the color
80
+ def split
81
+ [self.class.new(@hue - 5 / 12.0, s, l), self.class.new(@hue + 5 / 12.0, s, l)]
82
+ end
83
+
84
+ # @return [Array<Pigment::Color>] An array with the analogous colors of the color
85
+ def analogous
86
+ [self.class.new(@hue - 1 / 12.0, s, l), self.class.new(@hue + 1 / 12.0, s, l)]
87
+ end
88
+
89
+ # @return [Array<Pigment::Color>] An array with the tetradic colors of the color
90
+ def tetradic
91
+ [
92
+ self.class.new(@hue + 1 / 4.0, @saturation, @lightness),
93
+ self.class.new(@hue + 1 / 2.0, @saturation, @lightness),
94
+ self.class.new(@hue + 3 / 4.0, @saturation, @lightness)
95
+ ]
96
+ end
97
+
98
+ # @return [Array<Pigment::Color>] An array with the rectangular colors of the color
99
+ def rectangular
100
+ [
101
+ self.class.new(@hue + 1 / 6.0, @saturation, @lightness),
102
+ self.class.new(@hue + 1 / 2.0, @saturation, @lightness),
103
+ self.class.new(@hue + 2 / 3.0, @saturation, @lightness)
104
+ ]
105
+ end
106
+
107
+ # @return [Array<Pigment::Color>] An array with the tertiary colors of the color
108
+ def tertiary
109
+ [
110
+ self.class.new(@hue + 1 / 6.0, @saturation, @lightness),
111
+ self.class.new(@hue + 1 / 3.0, @saturation, @lightness),
112
+ self.class.new(@hue + 1 / 2.0, @saturation, @lightness),
113
+ self.class.new(@hue + 2 / 3.0, @saturation, @lightness),
114
+ self.class.new(@hue + 5 / 6.0, @saturation, @lightness),
115
+ ]
116
+ end
117
+
118
+ # @param [Boolean] with_alpha
119
+ # @return [Array<Float>] an array with the color components
120
+ def to_a(with_alpha: true)
121
+ with_alpha ? [@hue, @saturation, @lightness, @alpha] : [@hue, @saturation, @lightness]
122
+ end
123
+
124
+ # @return [String] the string representation of a hsl color
125
+ def to_s
126
+ "HSL Color(hue: #{hue}, saturation: #{saturation}, lightness: #{lightness}, alpha: #{alpha})"
127
+ end
128
+
129
+ # @param [Object] other
130
+ # @return [Boolean] wether the color is equal to other object
131
+ def ==(other)
132
+ other = Pigment::Color::HSL.convert(other)
133
+ other.hue.snap == @hue.snap &&
134
+ other.saturation.snap == @saturation.snap &&
135
+ other.lightness.snap == @lightness.snap &&
136
+ other.alpha.snap == @alpha.snap
137
+ end
138
+
139
+ alias_method :a, :alpha
140
+ alias_method :h, :hue
141
+ alias_method :s, :saturation
142
+ alias_method :l, :lightness
143
+
144
+ include Pigment::Color
145
+
146
+ private
147
+ # @return [Array] an array with the respective hsla components
148
+ def method_missing(method, *args)
149
+ super unless method =~ /^[ahsl]+$/ && args.empty?
150
+ method.to_s.each_char.map { |component| send(component) }
151
+ end
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,10 @@
1
+ module Pigment
2
+ module Color
3
+ # Error raised when an invalid color format is used
4
+ class InvalidColorFormatError < ArgumentError
5
+ def initialize(object)
6
+ super("Invalid Format #{object.inspect}")
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,279 @@
1
+ require_relative '../color'
2
+
3
+ module Pigment
4
+ module Color
5
+ # Represent a color in the RGB Format
6
+ class RGB
7
+ using FloatSnap
8
+
9
+ # @return [Float]
10
+ attr_reader :red, :green, :blue, :alpha
11
+
12
+ class << self
13
+ # Converts a color into RGB format from any possible format
14
+ # @param [Pigment::Color] color
15
+ # @raise [InvalidColorFormatError]
16
+ # @return [Pigment::Color]
17
+ def convert(color)
18
+ case color
19
+ when RGB then color
20
+ when HSL then from_hsl(*color)
21
+ else raise InvalidColorFormatError, color
22
+ end
23
+ end
24
+
25
+ # Creates a Pigment::Color::RGB from an HTML Hex code String
26
+ # @param [String, Integer] hex
27
+ # @raise [InvalidColorFormatError]
28
+ # @return [Pigment::Color::RGB]
29
+ def from_hex(hex)
30
+ case hex
31
+ when String then from_hex_string(hex)
32
+ when Integer then from_hex_integer(hex)
33
+ when Float then from_hex_integer(hex.round)
34
+ else raise InvalidColorFormatError, hex
35
+ end
36
+ end
37
+
38
+ # Creates a Pigment::Color from a group of rgba Integers
39
+ # @param [Integer] red
40
+ # @param [Integer] green
41
+ # @param [Integer] blue
42
+ # @param [Integer] alpha
43
+ # @raise [InvalidColorFormatError]
44
+ # @return [Pigment::Color::RGB]
45
+ def from_rgba_integers(red, green, blue, alpha = 255)
46
+ color = [red, green, blue, alpha]
47
+ raise InvalidColorFormatError, color unless color.all? do |c|
48
+ (0..255).include? c
49
+ end
50
+ new(*color.map { |c| c / 255.0 })
51
+ end
52
+
53
+ # @param [Integer] red
54
+ # @param [Integer] green
55
+ # @param [Integer] blue
56
+ # @return [Pigment::Color::RGB]
57
+ def from_rgb_integers(red, green, blue)
58
+ from_rgba_integers(red, green, blue)
59
+ end
60
+
61
+ # @return [Pigment::Color::RGB] a random generated color
62
+ def random
63
+ new(rand(0.0..1.0), rand(0.0..1.0), rand(0.0..1.0))
64
+ end
65
+
66
+ # Suppress an array of floats by dividing by the greatest color component.
67
+ # @param [Array] color
68
+ # @return [Array]
69
+ def suppress(color)
70
+ return color.map { |c| c / color.max } unless color.max.between?(0.0, 1.0)
71
+ color
72
+ end
73
+
74
+ private
75
+ # @param [Integer] hex
76
+ # @return [Pigment::Color::RGB]
77
+ def from_hex_integer(hex)
78
+ raise InvalidColorFormatError, hex unless hex.is_a?(Numeric) && hex.between?(0, 0xFFFFFFFF)
79
+ red = (hex >> 24 & 0xFF)
80
+ green = (hex >> 16 & 0xFF)
81
+ blue = (hex >> 8 & 0xFF)
82
+ alpha = (hex & 0xFF)
83
+ from_rgba_integers(red, green, blue, alpha)
84
+ end
85
+
86
+ # @param [String] hex
87
+ # @raise [InvalidColorFormatError]
88
+ # @return [Pigment::Color::RGB]
89
+ def from_hex_string(hex)
90
+ matches = hex.match(/^#?(?<r>\h{2})(?<g>\h{2})(?<b>\h{2})(?<a>\h{2})?$/)&.named_captures
91
+ raise InvalidColorFormatError, hex unless matches
92
+ matches["a"] ||= 'FF'
93
+ new(*matches.values.map { |value| value.to_i(16) / 255.0 })
94
+ end
95
+
96
+ # Creates a Pigment::Color::RGB form the HSL color System. It's mostly used to calculate harmonic colors.
97
+ # @param [#to_f] hue between 0.0 and 1.0
98
+ # @param [#to_f] saturation between 0.0 and 1.0
99
+ # @param [#to_f] lightness between 0.0 and 1.0
100
+ # @param [#to_f] alpha between 0.0 and 1.0
101
+ # @return [Pigment::Color::RGB]
102
+ def from_hsl(hue, saturation, lightness, alpha = 1.0)
103
+ return new(lightness, lightness, lightness, alpha) if saturation == 0
104
+ v2 = lightness < 0.5 ? lightness * (1 + saturation) : lightness + saturation - saturation * lightness
105
+ v1 = 2 * lightness - v2
106
+ color = [hue + (1 / 3.0), hue, hue - (1 / 3.0)].map do |hv|
107
+ hv = hv < 0 ? hv + 1
108
+ : hv > 1 ? hv - 1
109
+ : hv
110
+
111
+ case
112
+ when 6 * hv < 1 then v1 + (v2 - v1) * 6 * hv
113
+ when 2 * hv < 1 then v2
114
+ when 3 * hv < 2 then v1 + (v2 - v1) * ((2 / 3.0) - hv) * 6
115
+ else v1
116
+ end
117
+ end
118
+ color << alpha
119
+ new(*color.map { |c| c.round(2) })
120
+ end
121
+ end
122
+
123
+ # Pigment uses sRGB or sRGBA as the default color system
124
+ # Pigment::Color::RGB is represented as an array of floats, which are ranged from 0.0 to 1.0
125
+ # @param [Float, Integer] red between 0.0 and 1.0
126
+ # @param [Float, Integer] green between 0.0 and 1.0
127
+ # @param [Float, Integer] blue between 0.0 and 1.0
128
+ # @param [Float, Integer] alpha between 0.0 and 1.0
129
+ # @raise [InvalidColorFormatError]
130
+ # @return [Pigment::Color::RGB]
131
+ def initialize(red, green, blue, alpha = 1.0)
132
+ @red, @green, @blue, @alpha = red.to_f, green.to_f, blue.to_f, alpha.to_f
133
+ color = to_floats(with_alpha: true)
134
+ raise InvalidColorFormatError, color unless color.all? { |c| c.between?(0.0, 1.0) }
135
+ end
136
+
137
+ # Sums all the two colors components. If any component gets out of the 0 to 1.0 range it gets suppressed.
138
+ # @param [Pigment::Color] color
139
+ # @raise [InvalidColorFormatError]
140
+ # @return [Pigment::Color::RGB]
141
+ def +(color)
142
+ raise InvalidColorFormatError, color unless color.is_a?(Pigment::Color)
143
+ color = color.into(self.class)
144
+ color = [
145
+ @red + color.red,
146
+ @green + color.green,
147
+ @blue + color.blue
148
+ ]
149
+
150
+ self.class.new(*self.class.suppress(color), @alpha)
151
+ end
152
+
153
+ # Subtracts all the two color components. If any component gets out of the 0 to 1.0 range it gets suppressed.
154
+ # Tone component will be 0 if it gets lower than 0
155
+ # @param [Pigment::Color] color
156
+ # @raise [InvalidColorFormatError]
157
+ # @return [Pigment::Color::RGB]
158
+ def -(color)
159
+ raise InvalidColorFormatError, color unless color.is_a?(Pigment::Color)
160
+ color = color.into(self.class)
161
+ self.class.new(*self.class.suppress([
162
+ @red - color.red,
163
+ @green - color.green,
164
+ @blue - color.blue
165
+ ].map { |c| c >= 0 ? c : 0 }), @alpha)
166
+ end
167
+
168
+ # Multiplies all the color components by n. If any component gets out of the 0 to 1.0 range it gets suppressed.
169
+ # @param [Numeric] n
170
+ # @return [Pigment::Color::RGB]
171
+ def *(n)
172
+ raise ArgumentError, "Expecting Numeric. Given #{n.class}" unless n.is_a? Numeric
173
+ n = rgb.map { |c| c * n.to_f }
174
+ self.class.new(*self.class.suppress(n), @alpha)
175
+ end
176
+
177
+ # Divides all the color components by n. If any component gets out of the 0 to 1.0 range it gets suppressed.
178
+ # @param [Numeric] n
179
+ # @return [Pigment::Color::RGB]
180
+ def /(n)
181
+ raise ArgumentError, "Expecting Numeric. Given #{n.class}" unless n.is_a? Numeric
182
+ n = rgb.map { |c| c / n.to_f }
183
+ self.class.new(*self.class.suppress(n), @alpha)
184
+ end
185
+
186
+ # Test if two colors are equal
187
+ # @param [Pigment::Color] other
188
+ # @return [Boolean]
189
+ def ==(other)
190
+ return false unless other.is_a?(Pigment::Color)
191
+ other = Pigment::Color::RGB.convert(other)
192
+ other.red.snap == @red.snap &&
193
+ other.blue.snap == @blue.snap &&
194
+ other.green.snap == @green.snap &&
195
+ other.alpha.snap == @alpha.snap
196
+ end
197
+
198
+ # @return [Pigment::Color::RGB] the grayscale correspondent of the color
199
+ def grayscale
200
+ gray = (@red + @green + @blue) / 3.0
201
+ self.class.new(gray, gray, gray, @alpha)
202
+ end
203
+
204
+ # @return [Boolean] true if all components are the same, false otherwise
205
+ def grayscale?
206
+ @red == @green && @green == @blue
207
+ end
208
+
209
+ # Interpolates two colors. Amount must be an float between -1.0 and 1.0
210
+ # @param [Pigment::Color] color
211
+ # @param [Float] amount
212
+ # @raise [InvalidColorFormatError]
213
+ # @return [Pigment::Color::RGB]
214
+ def interpolate(color, amount = 0.5)
215
+ raise InvalidColorFormatError, color unless color.is_a?(Pigment::Color)
216
+ raise ArgumentError, "Amount must be an float between -1.0 and 1.0, got #{amount}" unless (-1.0..1.0).include?(amount)
217
+ color = color.into(Pigment::Color::RGB)
218
+ n = [rgb, color.rgb].transpose.map! { |c, d| c + amount * (d - c) }
219
+ self.class.new(*self.class.suppress(n))
220
+ end
221
+
222
+ # @return [RGB] the Invert color
223
+ def inverse
224
+ self.class.new(*rgb.map { |c| 1.0 - c }, @alpha)
225
+ end
226
+
227
+
228
+ # @return [Array<Pigment::Color>] An array of the triadic colors from self
229
+ def triadic
230
+ [self.class.new(@blue, @red, @green, @alpha), self.class.new(@green, @blue, @red, @alpha)]
231
+ end
232
+
233
+ # @param [Boolean] with_alpha
234
+ # @return [Array<Float>] an array of the color components. Alpha value is passed as well if with_alpha is set to true.
235
+ def to_a(with_alpha: true)
236
+ with_alpha ? [@red, @green, @blue, @alpha] : [@red, @green, @blue]
237
+ end
238
+
239
+ # @param [Boolean] with_alpha
240
+ # @return [Array<Integer>] an array of the color components. Alpha value is passed as well if with_alpha is set to true.
241
+ def to_ints(with_alpha: true)
242
+ to_a(with_alpha: with_alpha).map { |v| (v * 255).to_i }
243
+ end
244
+
245
+ # @param [Boolean] with_alpha
246
+ # @return [String] an hexadecimal representation of the color components. Alpha value is passed as well if
247
+ # with_alpha is set to true.
248
+ def to_hex(with_alpha: true)
249
+ to_ints(with_alpha: with_alpha).map { |v| '%02x' % v }.join
250
+ end
251
+
252
+ # @return [Array<Float>] an array with the red, green and blue color components
253
+ def rgb
254
+ to_a(with_alpha: false)
255
+ end
256
+
257
+ # @return [String] the string representation of a hsl color
258
+ def to_s
259
+ "RGB Color(red: #{red}, green: #{green}, blue: #{blue}, alpha: #{alpha})"
260
+ end
261
+
262
+ alias_method :mix, :interpolate
263
+ alias_method :r, :red
264
+ alias_method :g, :green
265
+ alias_method :b, :blue
266
+ alias_method :a, :alpha
267
+ alias_method :rgba, :to_a
268
+
269
+ include Pigment::Color
270
+
271
+ private
272
+ # @return [Array<Float>] an array with the respective rgba components
273
+ def method_missing(method, *args)
274
+ return super unless method =~ /^[abgr]+$/ && args.empty?
275
+ method.size.times.map{ |i| send(method[i]) }
276
+ end
277
+ end
278
+ end
279
+ end