pigment 0.2.1 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 79ec5aef1ab5b45b400c11d9b56cd5dda30e191962bff97a91bfa9ba461d28ed
4
+ data.tar.gz: f684c0abf44be4e7d466f9acdbb4317510bd3b334004b8168c440dda089c50db
5
+ SHA512:
6
+ metadata.gz: aab005b7c4e84a23a9f1c515d27314f2f994a57c2f7bad76be1dbecc89285b3fe6793244fc414bf8786745212d6292e807a27062ace6be41c637dcc7a161a0be
7
+ data.tar.gz: 868457b116af47ae1c7601c5c541d43926cc5002e109823654ea67c7fb167660132c0fbad6f34ac55e1b0db0f52798f3eef727d27322df5117c3e60df36e658b
data/CHANGELOG.md ADDED
@@ -0,0 +1,18 @@
1
+ # 0.3.1
2
+ - Fixes creating colors with approximation errors:
3
+ ```ruby
4
+ # before 0.3.1
5
+ BrilliantLavender.analogous
6
+ # Invalid Format [0.8063725490196079, 1.0000000000000004, 0.8666666666666667, 1.0]
7
+
8
+ # after 0.3.1
9
+ BrilliantLavender.analogous
10
+ # RGB Color(red: 0.8063725490196079, green: 1.0, blue: 0.8666666666666667, alpha: 1.0)
11
+ ```
12
+
13
+ # 0.3.0
14
+ - New classes per Color Format
15
+ - Pigment::Color::RGB
16
+ - Pigment::Color::HSL
17
+ - Base module for Color Formats Pigment::Color
18
+ - Palette class to handle a collection of colors
data/LICENSE.md ADDED
@@ -0,0 +1,22 @@
1
+ (The MIT License)
2
+
3
+ Copyright © 2021 Pedro Miranda
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ 'Software'), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -6,4 +6,185 @@ To install:
6
6
  gem install pigment
7
7
  ```
8
8
 
9
- <a href='http://www.pledgie.com/campaigns/18945'><img alt='Click here to lend your support to: pigment (ruby gem) and make a donation at www.pledgie.com !' src='http://www.pledgie.com/campaigns/18945.png?skin_name=chrome' border='0' /></a>
9
+ ## Usage
10
+ ### RGB Color Format
11
+ The RGB color format is represented with four components between 0.0 and 1.0, representing 0 to 255
12
+ - `@red`
13
+ - `@green`
14
+ - `@blue`
15
+ - `@alpha`
16
+
17
+ ```ruby
18
+ require 'pigment/color/rgb'
19
+
20
+ red = Pigment::Color::RGB.new(1.0, 0.0, 0.0)
21
+ # RGB Color(red: 1.0, green: 0.0, blue: 0.0, alpha: 0.0)
22
+
23
+ red.to_a
24
+ # [1.0, 0.0, 0.0, 1.0]
25
+
26
+ semitransparent_red = Pigment::Color::RGB.new(1.0, 0.0, 0.0, 0.5)
27
+ # RGB Color(red: 1.0, green: 0.0, blue: 0.0, alpha: 0.5)
28
+
29
+ red + semitransparent_red
30
+ # RGB Color(red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0)
31
+
32
+ semitransparent_red + red
33
+ # RGB Color(red: 1.0, green: 0.0, blue: 0.0, alpha: 0.5)
34
+
35
+ red - semitransparent_red
36
+ # RGB Color(red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0)
37
+
38
+ half_red = red / 2
39
+ # RGB Color(red: 0.5, green: 0.0, blue: 0.0, alpha: 1.0)
40
+
41
+ half_red * 2 == red
42
+ # true
43
+
44
+ gray50 = Pigment::Color::RGB.new(1.0, 0.5, 0.0).grayscale
45
+ # RGB Color(red: 0.5, green: 0.5, blue: 0.5, alpha: 1.0)
46
+
47
+ gray50.grayscale?
48
+ # true
49
+
50
+ cyan = red.inverse
51
+ # RGB Color(red: 0.0, green: 1.0, blue: 1.0, alpha: 1.0)
52
+
53
+ green, blue = red.triadic
54
+ # [RGB Color(red: 0.0, green: 1.0, blue: 0.0, alpha: 1.0), RGB Color(red: 0.0, green: 0.0, blue: 1.0, alpha: 1.0)]
55
+
56
+ red.to_hex
57
+ # '0xff0000ff'
58
+
59
+ red.to_hex(with_alpha: false)
60
+ # '0xff0000'
61
+
62
+ Pigment::Color::RGB.convert(Pigment::Color::HSL.new(0.0, 1, 0.5))
63
+ # RGB Color(red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0)
64
+ ```
65
+
66
+ ### HSL Color Format
67
+
68
+ The HSL color format is represented with
69
+
70
+ - `@hue` - a value from 0.0 to 1.0, representing 0 to 360 degrees
71
+ - `@saturation` - a value from 0.0 to 1.0
72
+ - `@lightness` - a value from 0.0 to 1.0
73
+ - `@alpha` - a value from 0.0 to 1.0
74
+
75
+ This format is useful to calculate related colors:
76
+
77
+ - triadic
78
+ - split
79
+ - analogous
80
+ - tetradic
81
+ - rectangular
82
+ - tertiary
83
+
84
+ ```ruby
85
+ require 'pigment/color/hsl'
86
+
87
+ hsl_red = Pigment::Color::HSL.convert(red)
88
+ # HSL Color(hue: 0.0, saturation: 1.0, lightness: 0.5, alpha: 1.0)
89
+
90
+ hsl_red == Pigment::Color::HSL(0.0, 1, 0.5)
91
+ # HSL Color(hue: 0.0, saturation: 1.0, lightness: 0.5, alpha: 1.0)
92
+
93
+ hsl_red.triadic
94
+ hsl_red.split
95
+ hsl_red.analogous
96
+ hsl_red.tetradic
97
+ hsl_red.rectangular
98
+ hsl_red.tertiary
99
+
100
+ ```
101
+
102
+ ### Color Module
103
+
104
+ The color module works as the base for any possible color format, implementing common methods
105
+
106
+ ```ruby
107
+ red.to_html
108
+ # '#ff0000'
109
+
110
+ red.into(Pigment::HSL)
111
+ # HSL Color(hue: 0.0, saturation: 1.0, lightness: 0.5, alpha: 1.0)
112
+
113
+ red.inverse?(cyan)
114
+ # true
115
+
116
+ red.triadic_of?(blue)
117
+ # true
118
+
119
+ red.triadic_include?(green, blue)
120
+ # true
121
+
122
+ red.split_of?
123
+ red.split_include?
124
+ red.analogous_of?
125
+ red.analogous_include?
126
+ red.tetradic_of?
127
+ red.tetradic_include?
128
+ red.rectangular_of?
129
+ red.rectangular_include?
130
+ red.tertiary_of?
131
+ red.tertiary_include?
132
+ # of -> checks if self is included in argument result
133
+ # include -> checks if arguments are included in self result
134
+ ```
135
+
136
+ ### Color Palette
137
+
138
+ ```ruby
139
+ require 'pigment/palette'
140
+
141
+ pallete = Pigment::Palette.new(
142
+ red: red,
143
+ blue: blue,
144
+ )
145
+
146
+ pallete[:red]
147
+ # RGB Color(red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0)
148
+
149
+ pallete[:green] = green
150
+ # RGB Color(red: 0.0, green: 1.0, blue: 0.0, alpha: 1.0)
151
+
152
+ # Pigment::Palette is enumerable
153
+
154
+ palette.map.to_a
155
+ # [RGB Color(red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0), RGB Color(red: 0.0, green: 0.0, blue: 1.0, alpha: 1.0), RGB Color(red: 0.0, green: 1.0, blue: 0.0, alpha: 1.0)]
156
+ ```
157
+
158
+ ### Default Color Palette
159
+
160
+ ```ruby
161
+ require 'pigment/default_rgb_palette'
162
+
163
+ Pigment::Palette::RGB::DEFAULT[:Red]
164
+ # RGB Color(red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0)
165
+
166
+
167
+ Pigment::Palette::RGB::Green
168
+ # RGB Color(red: 0.0, green: 1.0, blue: 0.0, alpha: 1.0)
169
+
170
+ Pigment::Palette::RGB.Blue
171
+ # RGB Color(red: 0.0, green: 0.0, blue: 1.0, alpha: 1.0)
172
+ ```
173
+
174
+ ## Changes
175
+ See [Changelog](CHANGELOG.md)
176
+
177
+
178
+ ## Future Work
179
+ - [ ] new color formats:
180
+ - [ ] Y'CbCr
181
+ - [ ] RYB
182
+ - [ ] CMYK
183
+ - [ ] redesign of Base Color module to support Additive color formats
184
+ - [ ] Palette Loader to load palette files
185
+
186
+ ## License
187
+ See [License](LICENSE.md)
188
+
189
+ ## Related interests
190
+ - [color-names](https://github.com/meodai/color-names): javascript collection of 25586 color names
data/lib/pigment.rb CHANGED
@@ -1,307 +1,3 @@
1
- module Pigment
2
- VERSION = '0.2.1'
3
-
4
- class Color
5
-
6
- @named_colors = {}
7
- attr_reader :color, :hsl
8
-
9
- # Pigment uses sRGB or sRGBA as the default color system
10
- # color can be a hexadecimal code preceded by a '#' like '#FF4CB2' or a array of floats (1.0, 0.3, 0.7)
11
- # or an array of integers between 0 and 255 (153, 255, 31)
12
- # @param [String, Array] color
13
- def initialize(*color)
14
- @color = case
15
- when color[0] =~ /^#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})?$/
16
- [$1, $2, $3, $4 || 'ff'].map { |v| v.to_i(16) / 255.0 }
17
- when color.is_a?(Array) && (color.all? { |c| (0.0..1.0).include? c }) && color.length == 3
18
- [color, 1.0].flatten!
19
- when color.is_a?(Array) && (color.all? { |c| (0..255).include? c && c.is_a?(Integer) } ) && color.length == 3
20
- [color.map { |c| c / 255.0 }, 1.0].flatten!
21
- when color.is_a?(Array) && (color.all? { |c| (0.0..1.0).include? c }) && color.length == 4
22
- color
23
- when color.is_a?(Array) && (color.all? { |c| (0..255).include? c && c.is_a?(Integer) } ) && color.length == 4
24
- color.map { |c| c / 255.0 }
25
- else
26
- raise ArgumentError, "Expected String or Array with length 3 or 4. Given #{color.class} with length = #{color.length}"
27
- end
28
- end
29
-
30
- # Selectors and Setters.
31
- %w'r g b a'.each_with_index do |m, i|
32
- define_method("#{m}", ->() { @color[i] })
33
- define_method("#{m}=", ->(value) { @color[i] = value if value.is_a?(Float) && (0.0..1.0).include?(value) })
34
- end
35
-
36
- %w'h s l'.each_with_index do |m, i|
37
- define_method("#{m}", ->() { hsl[i] })
38
- end
39
-
40
-
41
- # Returns an array with the rgb components
42
- # @return [Array]
43
- def rgb
44
- @color[0, 3]
45
- end
46
-
47
- # Return specified color by its name from the named_colors hash.
48
- # @param [Symbol] name
49
- # @return [Color]
50
- def self.[](name)
51
- @named_colors[name]
52
- end
53
-
54
- # Add name to a color , add it to the named_colors hash and defines a constant.
55
- # @param [Array of Strings] names
56
- # @param [Color] color
57
- def self.[]=(*names, color)
58
- color = new(color) unless color.is_a? Color
59
- names.each do |name|
60
- @named_colors[name.downcase] = color
61
- const_set("#{name}".to_sym, color)
62
- end
63
- end
64
-
65
- # Return all the named_colors as an array.
66
- def self.named_colors
67
- @named_colors.keys
68
- end
69
-
70
- # Return all the named_colors as a sorted array.
71
- def self.named_colors_sorted
72
- named_colors.sort
73
- end
74
-
75
-
76
- # Suppress an array of floats by dividing by the greatest color component.
77
- # @param [Array] color
78
- # @return [Array]
79
- def self.suppress(color)
80
- color.map! { |c| c / color.max } unless (0.0..1.0).include?(color.max)
81
- color
82
- end
83
-
84
- # Creates a Color form the HSL color System. It's mostly used to calculate harmonic colors.
85
- # @param [Float] h
86
- # @param [Float] s
87
- # @param [Float] l
88
- # @return [Color]
89
- def self.from_hsl(h, s, l)
90
- return new(l, l, l) if s == 0
91
- v2 = l < 0.5 ? l * (1 + s) : (l + s) - (s * l)
92
- v1 = 2 * l - v2
93
- color = [h + (1 / 3.0), h, h - (1 / 3.0)].map do |hv|
94
- case
95
- when hv < 0 then hv += 1
96
- when hv > 1 then hv -= 1
97
- when 6 * hv < 1 then v1 +(v2 - v1) * 6 * hv
98
- when 2 * hv < 1 then v2
99
- when 3 * hv < 2 then v1 + (v2 - v1) * ((2 / 3.0)- hv) * 6
100
- else v1
101
- end
102
- end
103
- new(*color)
104
- end
105
-
106
- # Same as inverse.
107
- def -@
108
- inv
109
- end
110
-
111
- # Sums all the two colors components. If any component gets out of the 0 to 1.0 range its suppressed.
112
- # @param [Numeric] color
113
- # @return [Color]
114
- def +(color)
115
- case color
116
- when Color
117
- self.class.new(*self.class.suppress([color.rgb, rgb].transpose.map! { |c, d| c + d }))
118
- else
119
- raise ArgumentError, "Expecting Color. Given #{color.class}"
120
- end
121
- end
122
-
123
- # Subtracts all the two color components. If any component gets out of the 0 to 1.0 range its suppressed.
124
- # If tone component gets lower than 0 it acts like its dealing with the inverse component -> 1 - component
125
- # @param [Numeric] color
126
- # @return [Color]
127
- def -(color)
128
- case color
129
- when Color
130
- self.class.new(*self.class.suppress([rgb, color.rgb].transpose.map! do |c, d|
131
- e = c - d
132
- e >= 0 ? e : e = 1 + e
133
- e
134
- end))
135
- else
136
- raise ArgumentError, "Expecting color. Given #{color.class}"
137
- end
138
- end
139
-
140
- # Multiplies all the color components by n. If any component gets out of the 0 to 1.0 range its suppressed.
141
- # @param [Numeric] n
142
- # @return [Color]
143
- def *(n)
144
- case n
145
- when Numeric
146
- n = rgb.map { |c| c * n.to_f }
147
- self.class.new(*self.class.suppress(n))
148
- else
149
- raise ArgumentError, "Expecting Numeric. Given #{n.class}"
150
- end
151
- end
152
-
153
- # Divides all the color components by n. If any component gets out of the 0 to 1.0 range its suppressed.
154
- # @param [Numeric] n
155
- # @return [Color]
156
- def /(n)
157
- case n
158
- when Numeric
159
- n = rgb.map { |c| c * n.to_f }
160
- self.class.new(*self.class.suppress(n))
161
- else
162
- raise ArgumentError, "Expecting Numeric. Given #{n.class}"
163
- end
164
- end
165
-
166
- # Test if two colors are equal
167
- # @param [Color] color
168
- # @return [Boolean]
169
- def ==(color)
170
- color.is_a?(Color) && color.rgb == rgb
171
- end
172
-
173
- # @return [Color]
174
- def dup
175
- self.class.new(*@color)
176
- end
177
-
178
- # Converts a color to its grayscale correspondent
179
- # @return [Color]
180
- def grayscale
181
- r = g = b = (self.r + self.g + self.b)/3
182
- self.class.new(r, g, b)
183
- end
184
-
185
- # Calculates the harmonic colors. Type can be :triadic, :split, :analogous, :complement or :complementary
186
- # @param [Symbol] type
187
- # @return [Color, Array of Colors]
188
- def harmonize(type = :triadic)
189
- color.to_hsl unless @hsl
190
- case type
191
- when :triadic
192
- [self.class.from_hsl(h - 5 / 12.0, s, l), self.class.from_hsl(h + 1 / 3.0, s, l)]
193
- when :split
194
- [self.class.from_hsl(h - 5 / 12.0, s, l), self.class.from_hsl(h + 5 / 12.0, s, l)]
195
- when :analogous
196
- [self.class.from_hsl(h - 1 / 12.0, s, l), self.class.from_hsl(h + 1 / 12.0, s, l)]
197
- when :complement, :complementary
198
- inv
199
- else
200
- raise ArgumentError, "Expected :triadic, :split, :analogous, :complement or :complementary. Given #{type}"
201
- end
202
- end
203
-
204
- # Interpolates two colors. Amount must be an float between -1.0 and 1.0
205
- # @param [Color] color
206
- # @param [Float] amount
207
- def interpolate(color, amount = 0.5)
208
- if color.is_a?(Color) && (-1.0..1.0).include?(amount)
209
- n = [rgb, color.rgb].transpose.map! { |c, d| c + amount * (d - c) }
210
- self.class.new(*self.class.suppress(n))
211
- else
212
- raise ArgumentError
213
- end
214
- end
215
-
216
- # Returns the Invert color
217
- # @return [Color]
218
- def inverse
219
- self.class.new(*rgb.map { |c| 1.0 - c })
220
- end
221
-
222
- # Returns a new Color without the given channels
223
- # @param [Array of Symbols] channels
224
- # @return [Color]
225
- def remove_channels(*channels)
226
- color = self.class.new(r, g, b, a)
227
- %w'r g b a'.each do |attr|
228
- color.send("#{attr}=", 0) if channels.include? attr.to_sym
229
- end
230
- color
231
- end
232
-
233
- # Creates an instance variable to keep the HSL values of a RGB color.
234
- # @return [Array]
235
- def to_hsl
236
- min = rgb.min
237
- max = rgb.max
238
- delta = (max - min)
239
- l = (max + min) / 2.0
240
-
241
- s = if delta == 0.0 # close to 0.0, so it's a grey
242
- h = 0
243
- 0
244
- elsif l < 0.5
245
- delta / (max + min)
246
- else
247
- delta / (2.0 - max - min)
248
- end
249
-
250
- h = if r == max
251
- h = ((g - b) / delta) / 6.0
252
- h += 1.0 if g < b
253
- h
254
- elsif g == max
255
- ((b - r) / delta) / 6.0 + (1.0 / 3.0)
256
- elsif b == max
257
- ((r - g) / delta) / 6.0 + (2.0 / 3.0)
258
- end
259
-
260
- h += 1 if h < 0
261
- h -= 1 if h > 1
262
- @hsl = [h, s, l]
263
- self
264
- end
265
-
266
- # Returns an array of the color components. Alpha value is passed as well if with_alpha is set to true.
267
- # @param [Boolean] with_alpha
268
- # @return [Array]
269
- def to_floats(with_alpha = true)
270
- with_alpha ? @color.dup : rgb
271
- end
272
-
273
- # Returns an array of the color components. Alpha value is passed as well if with_alpha is set to true.
274
- # @param [Boolean] with_alpha
275
- # @return [Array]
276
- def to_ints(with_alpha = true)
277
- to_floats(with_alpha).map { |v| Integer(v * 255) }
278
- end
279
-
280
- # Returns an array of the color components. Alpha value is passed as well if with_alpha is set to true.
281
- # @param [Boolean] with_alpha
282
- # @return [Array]
283
- def to_hex(with_alpha = true)
284
- to_ints(with_alpha).map { |v| '%02x' % v }.join
285
- end
286
-
287
- def to_s
288
- "Color(r=#{r}, g=#{g}, b=#{b}, a=#{a}#{", [h=#{h}, s=#{s}, l=#{l}]" if @hsl})"
289
- end
290
-
291
- alias_method :alpha=, :a=
292
- alias_method :inv, :inverse
293
- alias_method :invert, :inverse
294
- alias_method :complementary, :inverse
295
- alias_method :mix, :interpolate
296
- alias_method :red, :r
297
- alias_method :green, :g
298
- alias_method :blue, :b
299
- alias_method :alpha, :a
300
- alias_method :to_a, :to_floats
301
- alias_method :to_ary, :to_floats
302
- alias_method :to_f, :to_floats
303
- alias_method :to_hsl, :hsl
304
- end
305
- end
306
-
307
- require_relative 'colors.rb'
1
+ require_relative 'pigment/version'
2
+ require_relative 'pigment/float_snap'
3
+ require_relative 'pigment/color/invalid_color_format_error'