pigment 0.2.1 → 0.3.1

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,158 @@
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
+ # @return [Pigment::Color::HSL]
57
+ # @raise [InvalidColorFormatError]
58
+ def initialize(hue, saturation, lightness, alpha = 1.0)
59
+ @hue = hue % 1.0
60
+ @saturation = saturation.to_f.snap
61
+ @lightness = lightness.to_f.snap
62
+ @alpha = alpha.to_f.snap
63
+
64
+ color = to_f(with_alpha: true)
65
+ raise InvalidColorFormatError, color unless color.all? { |c| c.between?(0.0, 1.0) }
66
+ end
67
+
68
+ # @return [Pigment::Color::HSL] the grayscale correspondent of the color
69
+ def grayscale
70
+ self.class.new(@hue, 0, @lightness, @alpha)
71
+ end
72
+
73
+ # @return [Boolean] true if saturation is 0, false otherwise
74
+ def grayscale?
75
+ @saturation == 0
76
+ end
77
+
78
+ # @return [Array<Pigment::Color>] An array with the triadic colors of the color
79
+ def triadic
80
+ [self.class.new(@hue + 1 / 3.0, s, l), self.class.new(@hue + 2 / 3.0, s, l)]
81
+ end
82
+
83
+ # @return [Array<Pigment::Color>] An array with the split colors of the color
84
+ def split
85
+ [self.class.new(@hue - 5 / 12.0, s, l), self.class.new(@hue + 5 / 12.0, s, l)]
86
+ end
87
+
88
+ # @return [Array<Pigment::Color>] An array with the analogous colors of the color
89
+ def analogous
90
+ [self.class.new(@hue - 1 / 12.0, s, l), self.class.new(@hue + 1 / 12.0, s, l)]
91
+ end
92
+
93
+ # @return [Array<Pigment::Color>] An array with the tetradic colors of the color
94
+ def tetradic
95
+ [
96
+ self.class.new(@hue + 1 / 4.0, @saturation, @lightness),
97
+ self.class.new(@hue + 1 / 2.0, @saturation, @lightness),
98
+ self.class.new(@hue + 3 / 4.0, @saturation, @lightness)
99
+ ]
100
+ end
101
+
102
+ # @return [Array<Pigment::Color>] An array with the rectangular colors of the color
103
+ def rectangular
104
+ [
105
+ self.class.new(@hue + 1 / 6.0, @saturation, @lightness),
106
+ self.class.new(@hue + 1 / 2.0, @saturation, @lightness),
107
+ self.class.new(@hue + 2 / 3.0, @saturation, @lightness)
108
+ ]
109
+ end
110
+
111
+ # @return [Array<Pigment::Color>] An array with the tertiary colors of the color
112
+ def tertiary
113
+ [
114
+ self.class.new(@hue + 1 / 6.0, @saturation, @lightness),
115
+ self.class.new(@hue + 1 / 3.0, @saturation, @lightness),
116
+ self.class.new(@hue + 1 / 2.0, @saturation, @lightness),
117
+ self.class.new(@hue + 2 / 3.0, @saturation, @lightness),
118
+ self.class.new(@hue + 5 / 6.0, @saturation, @lightness),
119
+ ]
120
+ end
121
+
122
+ # @param [Boolean] with_alpha
123
+ # @return [Array<Float>] an array with the color components
124
+ def to_a(with_alpha: true)
125
+ with_alpha ? [@hue, @saturation, @lightness, @alpha] : [@hue, @saturation, @lightness]
126
+ end
127
+
128
+ # @return [String] the string representation of a hsl color
129
+ def to_s
130
+ "HSL Color(hue: #{hue}, saturation: #{saturation}, lightness: #{lightness}, alpha: #{alpha})"
131
+ end
132
+
133
+ # @param [Object] other
134
+ # @return [Boolean] wether the color is equal to other object
135
+ def ==(other)
136
+ other = Pigment::Color::HSL.convert(other)
137
+ other.hue.snap == @hue.snap &&
138
+ other.saturation.snap == @saturation.snap &&
139
+ other.lightness.snap == @lightness.snap &&
140
+ other.alpha.snap == @alpha.snap
141
+ end
142
+
143
+ alias_method :a, :alpha
144
+ alias_method :h, :hue
145
+ alias_method :s, :saturation
146
+ alias_method :l, :lightness
147
+
148
+ include Pigment::Color
149
+
150
+ private
151
+ # @return [Array] an array with the respective hsla components
152
+ def method_missing(method, *args)
153
+ super unless method =~ /^[ahsl]+$/ && args.empty?
154
+ method.to_s.each_char.map { |component| send(component) }
155
+ end
156
+ end
157
+ end
158
+ 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,283 @@
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
+ # @return [Pigment::Color]
16
+ # @raise [InvalidColorFormatError]
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, Float] hex
27
+ # @return [Pigment::Color::RGB]
28
+ # @raise [InvalidColorFormatError]
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
+ # @return [Pigment::Color::RGB]
44
+ # @raise [InvalidColorFormatError]
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
+ # @return [Pigment::Color::RGB]
130
+ # @raise [InvalidColorFormatError]
131
+ def initialize(red, green, blue, alpha = 1.0)
132
+ @red = red.to_f.snap
133
+ @green = green.to_f.snap
134
+ @blue = blue.to_f.snap
135
+ @alpha = alpha.to_f
136
+
137
+ color = to_floats(with_alpha: true)
138
+ raise InvalidColorFormatError, color unless color.all? { |c| c.between?(0.0, 1.0) }
139
+ end
140
+
141
+ # Sums all the two colors components. If any component gets out of the 0 to 1.0 range it gets suppressed.
142
+ # @param [Pigment::Color] color
143
+ # @return [Pigment::Color::RGB]
144
+ # @raise [InvalidColorFormatError]
145
+ def +(color)
146
+ raise InvalidColorFormatError, color unless color.is_a?(Pigment::Color)
147
+ color = color.into(self.class)
148
+ color = [
149
+ @red + color.red,
150
+ @green + color.green,
151
+ @blue + color.blue
152
+ ]
153
+
154
+ self.class.new(*self.class.suppress(color), @alpha)
155
+ end
156
+
157
+ # Subtracts all the two color components. If any component gets out of the 0 to 1.0 range it gets suppressed.
158
+ # Tone component will be 0 if it gets lower than 0
159
+ # @param [Pigment::Color] color
160
+ # @return [Pigment::Color::RGB]
161
+ # @raise [InvalidColorFormatError]
162
+ def -(color)
163
+ raise InvalidColorFormatError, color unless color.is_a?(Pigment::Color)
164
+ color = color.into(self.class)
165
+ self.class.new(*self.class.suppress([
166
+ @red - color.red,
167
+ @green - color.green,
168
+ @blue - color.blue
169
+ ].map { |c| c >= 0 ? c : 0 }), @alpha)
170
+ end
171
+
172
+ # Multiplies all the color components by n. If any component gets out of the 0 to 1.0 range it gets suppressed.
173
+ # @param [Numeric] n
174
+ # @return [Pigment::Color::RGB]
175
+ def *(n)
176
+ raise ArgumentError, "Expecting Numeric. Given #{n.class}" unless n.is_a? Numeric
177
+ n = rgb.map { |c| c * n.to_f }
178
+ self.class.new(*self.class.suppress(n), @alpha)
179
+ end
180
+
181
+ # Divides all the color components by n. If any component gets out of the 0 to 1.0 range it gets suppressed.
182
+ # @param [Numeric] n
183
+ # @return [Pigment::Color::RGB]
184
+ def /(n)
185
+ raise ArgumentError, "Expecting Numeric. Given #{n.class}" unless n.is_a? Numeric
186
+ n = rgb.map { |c| c / n.to_f }
187
+ self.class.new(*self.class.suppress(n), @alpha)
188
+ end
189
+
190
+ # Test if two colors are equal
191
+ # @param [Pigment::Color] other
192
+ # @return [Boolean]
193
+ def ==(other)
194
+ return false unless other.is_a?(Pigment::Color)
195
+ other = Pigment::Color::RGB.convert(other)
196
+ other.red.snap == @red.snap &&
197
+ other.blue.snap == @blue.snap &&
198
+ other.green.snap == @green.snap &&
199
+ other.alpha.snap == @alpha.snap
200
+ end
201
+
202
+ # @return [Pigment::Color::RGB] the grayscale correspondent of the color
203
+ def grayscale
204
+ gray = (@red + @green + @blue) / 3.0
205
+ self.class.new(gray, gray, gray, @alpha)
206
+ end
207
+
208
+ # @return [Boolean] true if all components are the same, false otherwise
209
+ def grayscale?
210
+ @red == @green && @green == @blue
211
+ end
212
+
213
+ # Interpolates two colors. Amount must be an float between -1.0 and 1.0
214
+ # @param [Pigment::Color] color
215
+ # @param [Float] amount
216
+ # @return [Pigment::Color::RGB]
217
+ # @raise [InvalidColorFormatError]
218
+ def interpolate(color, amount = 0.5)
219
+ raise InvalidColorFormatError, color unless color.is_a?(Pigment::Color)
220
+ raise ArgumentError, "Amount must be an float between -1.0 and 1.0, got #{amount}" unless (-1.0..1.0).include?(amount)
221
+ color = color.into(Pigment::Color::RGB)
222
+ n = [rgb, color.rgb].transpose.map! { |c, d| c + amount * (d - c) }
223
+ self.class.new(*self.class.suppress(n))
224
+ end
225
+
226
+ # @return [RGB] the Invert color
227
+ def inverse
228
+ self.class.new(*rgb.map { |c| 1.0 - c }, @alpha)
229
+ end
230
+
231
+
232
+ # @return [Array<Pigment::Color>] An array of the triadic colors from self
233
+ def triadic
234
+ [self.class.new(@blue, @red, @green, @alpha), self.class.new(@green, @blue, @red, @alpha)]
235
+ end
236
+
237
+ # @param [Boolean] with_alpha
238
+ # @return [Array<Float>] an array of the color components. Alpha value is passed as well if with_alpha is set to true.
239
+ def to_a(with_alpha: true)
240
+ with_alpha ? [@red, @green, @blue, @alpha] : [@red, @green, @blue]
241
+ end
242
+
243
+ # @param [Boolean] with_alpha
244
+ # @return [Array<Integer>] an array of the color components. Alpha value is passed as well if with_alpha is set to true.
245
+ def to_ints(with_alpha: true)
246
+ to_a(with_alpha: with_alpha).map { |v| (v * 255).to_i }
247
+ end
248
+
249
+ # @param [Boolean] with_alpha
250
+ # @return [String] an hexadecimal representation of the color components. Alpha value is passed as well if
251
+ # with_alpha is set to true.
252
+ def to_hex(with_alpha: true)
253
+ to_ints(with_alpha: with_alpha).map { |v| '%02x' % v }.join
254
+ end
255
+
256
+ # @return [Array<Float>] an array with the red, green and blue color components
257
+ def rgb
258
+ to_a(with_alpha: false)
259
+ end
260
+
261
+ # @return [String] the string representation of a hsl color
262
+ def to_s
263
+ "RGB Color(red: #{red}, green: #{green}, blue: #{blue}, alpha: #{alpha})"
264
+ end
265
+
266
+ alias_method :mix, :interpolate
267
+ alias_method :r, :red
268
+ alias_method :g, :green
269
+ alias_method :b, :blue
270
+ alias_method :a, :alpha
271
+ alias_method :rgba, :to_a
272
+
273
+ include Pigment::Color
274
+
275
+ private
276
+ # @return [Array<Float>] an array with the respective rgba components
277
+ def method_missing(method, *args)
278
+ return super unless method =~ /^[abgr]+$/ && args.empty?
279
+ method.size.times.map{ |i| send(method[i]) }
280
+ end
281
+ end
282
+ end
283
+ end