pigment 0.1.10 → 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,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: dda5e6ade89b5949326fcb411e9aa8d99065fa511d3baeebb08e1cfb6199e84e
4
+ data.tar.gz: '058d8b2c5e47edfe74d716688ab99e33b99b51c46773dd0600c43c4755010b2e'
5
+ SHA512:
6
+ metadata.gz: 409765f525e55b3b63fd2ce3fe1e54af1fb070600c4f049a7992d984bd20b0b7f08bbdedb08f5702d6a9f53b50f035c4e25cff4013d741175f5615f889b1febd
7
+ data.tar.gz: ab38122e3bbd72c5af6f78a451ae885b52673ec3583a05d346af1bd9c81bd8d844d0dea757fb8e5a5eb7a111452815a192cfebd079304707128a35b45ced28d6
@@ -0,0 +1,6 @@
1
+ # 0.3.0
2
+ - New classes per Color Format
3
+ - Pigment::Color::RGB
4
+ - Pigment::Color::HSL
5
+ - Base module for Color Formats Pigment::Color
6
+ - Palette class to handle a collection of colors
@@ -0,0 +1,22 @@
1
+ (The MIT License)
2
+
3
+ Copyright © 2020 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
@@ -1,309 +1,3 @@
1
- module Pigment
2
- VERSION = '0.1.10'
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.
31
- %w'r g b a'.each_with_index do |m, i|
32
- define_method("#{m}", ->() { color[i] })
33
- end
34
-
35
- %w'h s l'.each_with_index do |m, i|
36
- define_method("#{m}", ->() { hsl[i] })
37
- end
38
-
39
- # Set alpha value. Only accepts float values
40
- # @param [Float] alpha
41
- def a=(alpha)
42
- @color[3] = alpha if alpha.is_a?(Float) && (0.0..1.0).include?(alpha)
43
- end
44
-
45
- # Returns an array with the rgb components
46
- # @return [Array]
47
- def rgb
48
- @color[0, 3]
49
- end
50
-
51
- def hsl
52
- to_hsl unless @hsl
53
- @hsl
54
- end
55
-
56
- # Return specified color by its name from the named_colors hash.
57
- # @param [Symbol] name
58
- # @return [Color]
59
- def self.[](name)
60
- @named_colors[name]
61
- end
62
-
63
- # Add name to a color , add it to the named_colors hash and defines a constant.
64
- # @param [Array of Strings] names
65
- # @param [Color] color
66
- def self.[]=(*names, color)
67
- color = new(color) unless color.is_a? Color
68
- names.each do |name|
69
- @named_colors[name.downcase] = color
70
- const_set("#{name}".to_sym, color)
71
- end
72
- end
73
-
74
- # Return all the named_colors as an array.
75
- def self.named_colors
76
- @named_colors.keys
77
- end
78
-
79
- # Return all the named_colors as a sorted array.
80
- def self.named_colors_sorted
81
- named_colors.sort
82
- end
83
-
84
-
85
- # Suppress an array of floats by dividing by the greatest color component.
86
- # @param [Array] color
87
- # @return [Array]
88
- def self.suppress(color)
89
- color.map! { |c| c / color.max } unless (0.0..1.0).include?(color.max)
90
- color
91
- end
92
-
93
- # Creates a Color form the HSL color System. It's mostly used to calculate harmonic colors.
94
- # @param [Float] h
95
- # @param [Float] s
96
- # @param [Float] l
97
- # @return [Color]
98
- def self.from_hsl(h, s, l)
99
- return new(l, l, l) if s == 0
100
- v2 = l < 0.5 ? l * (1 + s) : (l + s) - (s * l)
101
- v1 = 2 * l - v2
102
- color = [h + (1 / 3.0), h, h - (1 / 3.0)].map do |hv|
103
- case
104
- when hv < 0 then hv += 1
105
- when hv > 1 then hv -= 1
106
- when 6 * hv < 1 then v1 +(v2 - v1) * 6 * hv
107
- when 2 * hv < 1 then v2
108
- when 3 * hv < 2 then v1 + (v2 - v1) * ((2 / 3.0)- hv) * 6
109
- else v1
110
- end
111
- end
112
- new(*color)
113
- end
114
-
115
- # Same as inverse.
116
- def -@
117
- inv
118
- end
119
-
120
- # Sums all the two colors components. If any component gets out of the 0 to 1.0 range its suppressed.
121
- # @param [Numeric] color
122
- # @return [Color]
123
- def +(color)
124
- case color
125
- when Color
126
- self.class.new(*self.class.suppress([color.rgb, rgb].transpose.map! { |c, d| c + d }))
127
- else
128
- raise ArgumentError, "Expecting Color. Given #{color.class}"
129
- end
130
- end
131
-
132
- # Subtracts all the two color components. If any component gets out of the 0 to 1.0 range its suppressed.
133
- # If tone component gets lower than 0 it acts like its dealing with the inverse component -> 1 - component
134
- # @param [Numeric] color
135
- # @return [Color]
136
- def -(color)
137
- case color
138
- when Color
139
- self.class.new(*self.class.suppress([rgb, color.rgb].transpose.map! do |c, d|
140
- e = c - d
141
- e >= 0 ? e : e = 1 + e
142
- e
143
- end))
144
- else
145
- raise ArgumentError, "Expecting color. Given #{color.class}"
146
- end
147
- end
148
-
149
- # Multiplies all the color components by n. If any component gets out of the 0 to 1.0 range its suppressed.
150
- # @param [Numeric] n
151
- # @return [Color]
152
- def *(n)
153
- case n
154
- when Numeric
155
- n = rgb.map { |c| c * n.to_f }
156
- self.class.new(*self.class.suppress(n))
157
- else
158
- raise ArgumentError, "Expecting Numeric. Given #{n.class}"
159
- end
160
- end
161
-
162
- # Divides all the color components by n. If any component gets out of the 0 to 1.0 range its suppressed.
163
- # @param [Numeric] n
164
- # @return [Color]
165
- def /(n)
166
- case n
167
- when Numeric
168
- n = rgb.map { |c| c * n.to_f }
169
- self.class.new(*self.class.suppress(n))
170
- else
171
- raise ArgumentError, "Expecting Numeric. Given #{n.class}"
172
- end
173
- end
174
-
175
- # Test if two colors are equal
176
- # @param [Color] color
177
- # @return [Boolean]
178
- def ==(color)
179
- color.is_a?(Color) && color.rgb == rgb
180
- end
181
-
182
- # Converts a color to its grayscale correspondent
183
- # @return [Color]
184
- def grayscale
185
- r = g = b = (self.r + self.g + self.b)/3
186
- self.class.new(r, g, b)
187
- end
188
-
189
- # Calculates the harmonic colors. Type can be :triadic, :split, :analogous, :complement or :complementary
190
- # @param [Symbol] type
191
- # @return [Color, Array of Colors]
192
- def harmonize(type = :triadic)
193
- color.to_hsl unless @hsl
194
- case type
195
- when :triadic
196
- [self.class.from_hsl(h - 5 / 12.0, s, l), self.class.from_hsl(h + 1 / 3.0, s, l)]
197
- when :split
198
- [self.class.from_hsl(h - 5 / 12.0, s, l), self.class.from_hsl(h + 5 / 12.0, s, l)]
199
- when :analogous
200
- [self.class.from_hsl(h - 1 / 12.0, s, l), self.class.from_hsl(h + 1 / 12.0, s, l)]
201
- when :complement, :complementary
202
- inv
203
- else
204
- raise ArgumentError, "Expected :triadic, :split, :analogous, :complement or :complementary. Given #{type}"
205
- end
206
- end
207
-
208
- # Interpolates two colors. Amount must be an float between -1.0 and 1.0
209
- # @param [Color] color
210
- # @param [Float] amount
211
- def interpolate(color, amount = 0.5)
212
- if color.is_a? Color && (-1.0..1.0).include?(amount)
213
- n = [rgb, color.rgb].transpose.map! { |c, d| c + amount * (d - c) }
214
- self.class.new(*self.class.suppress(n))
215
- else
216
- raise ArgumentError
217
- end
218
- end
219
-
220
- # Returns the Invert color
221
- # @return [Color]
222
- def inverse
223
- self.class.new(*rgb.map { |c| 1.0 - c })
224
- end
225
-
226
- # Returns a new Color without the given channels
227
- # @param [Array of Symbols] channels
228
- # @return [Color]
229
- def remove_channels(*channels)
230
- r = 0 if channels.include? :r
231
- g = 0 if channels.include? :g
232
- b = 0 if channels.include? :b
233
- self.class.new(r, g, b)
234
- end
235
-
236
- # Creates an instance variable to keep the HSL values of a RGB color.
237
- # @return [Array]
238
- def to_hsl
239
- min = rgb.min
240
- max = rgb.max
241
- delta = (max - min)
242
- l = (max + min) / 2.0
243
-
244
- s = if delta == 0.0 # close to 0.0, so it's a grey
245
- h = 0
246
- 0
247
- elsif l < 0.5
248
- delta / (max + min)
249
- else
250
- delta / (2.0 - max - min)
251
- end
252
-
253
- h = if r == max
254
- h = ((g - b) / delta) / 6.0
255
- h += 1.0 if g < b
256
- h
257
- elsif g == max
258
- ((b - r) / delta) / 6.0 + (1.0 / 3.0)
259
- elsif b == max
260
- ((r - g) / delta) / 6.0 + (2.0 / 3.0)
261
- end
262
-
263
- h += 1 if h < 0
264
- h -= 1 if h > 1
265
- @hsl = [h, s, l]
266
- self
267
- end
268
-
269
- # Returns an array of the color components. Alpha value is passed as well if with_alpha is set to true.
270
- # @param [Boolean] with_alpha
271
- # @return [Array]
272
- def to_floats(with_alpha = false)
273
- with_alpha ? @color.dup : rgb
274
- end
275
-
276
- # Returns an array of the color components. Alpha value is passed as well if with_alpha is set to true.
277
- # @param [Boolean] with_alpha
278
- # @return [Array]
279
- def to_ints(with_alpha = false)
280
- to_floats(with_alpha).map { |v| Integer(v * 255) }
281
- end
282
-
283
- # Returns an array of the color components. Alpha value is passed as well if with_alpha is set to true.
284
- # @param [Boolean] with_alpha
285
- # @return [Array]
286
- def to_hex(with_alpha = false)
287
- to_ints(with_alpha).map { |v| '%02x' % v }.join
288
- end
289
-
290
- def to_s
291
- "Color(r=#{r}, g=#{g}, b=#{b}, a=#{a}#{", [h=#{h}, s=#{s}, l=#{l}]" if @hsl})"
292
- end
293
-
294
- alias_method :alpha=, :a=
295
- alias_method :inv, :inverse
296
- alias_method :invert, :inverse
297
- alias_method :complementary, :inverse
298
- alias_method :mix, :interpolate
299
- alias_method :red, :r
300
- alias_method :green, :g
301
- alias_method :blue, :b
302
- alias_method :alpha, :a
303
- alias_method :to_a, :to_floats
304
- alias_method :to_ary, :to_floats
305
- alias_method :to_f, :to_floats
306
- end
307
- end
308
-
309
- require_relative 'colors.rb'
1
+ require_relative 'pigment/version'
2
+ require_relative 'pigment/float_snap'
3
+ require_relative 'pigment/color/invalid_color_format_error'