pigment 0.2.3 → 0.3.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.
@@ -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