paleta 0.2.0 → 0.2.1
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.
- checksums.yaml +7 -0
- data/Gemfile +1 -3
- data/lib/paleta/color.rb +40 -38
- data/lib/paleta/core_ext/math.rb +7 -7
- data/lib/paleta/palette.rb +51 -52
- data/lib/paleta/version.rb +1 -1
- data/paleta.gemspec +4 -0
- data/readme.markdown +130 -83
- metadata +51 -12
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b03ada62d667c325f97922bd22b05801aff9cccd
|
4
|
+
data.tar.gz: cb7d2375cfcf0ac8cceb96719d1c9cdb13b5af23
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2a4cd1d750c2f78091b15b904d0d2e599dea95d91922a32b494ecd559bf5fa0eb504cb53e2ca735688efcab05195f573a5944eacaa37cef455d0430282873dbb
|
7
|
+
data.tar.gz: 1eeac708731584488a6a1611a14f3ba7417f3de5693f5144602cee107c43fe6d3b48b296c8dbd411a04561b46cead354641d77262f2aa8608f7a0a299292ad57
|
data/Gemfile
CHANGED
data/lib/paleta/color.rb
CHANGED
@@ -4,9 +4,9 @@ module Paleta
|
|
4
4
|
# Represents a color
|
5
5
|
class Color
|
6
6
|
include Math
|
7
|
-
|
7
|
+
|
8
8
|
attr_reader :red, :green, :blue, :hue, :saturation, :lightness, :hex
|
9
|
-
|
9
|
+
|
10
10
|
# Initailize a {Color}
|
11
11
|
#
|
12
12
|
# @overload initialize()
|
@@ -36,7 +36,6 @@ module Paleta
|
|
36
36
|
#
|
37
37
|
# @return [Color] A new instance of {Color}
|
38
38
|
def initialize(*args)
|
39
|
-
|
40
39
|
if args.length == 1 && args[0].is_a?(Color)
|
41
40
|
args[0].instance_variables.each do |key|
|
42
41
|
self.send("#{key[1..key.length]}=".to_sym, args[0].send("#{key[1..key.length]}"))
|
@@ -59,43 +58,43 @@ module Paleta
|
|
59
58
|
raise(ArgumentError, "Invalid arguments")
|
60
59
|
end
|
61
60
|
end
|
62
|
-
|
61
|
+
|
63
62
|
def red=(val)
|
64
63
|
@red = range_validator(val, 0..255)
|
65
64
|
update_hsl
|
66
65
|
update_hex
|
67
66
|
end
|
68
|
-
|
67
|
+
|
69
68
|
def green=(val)
|
70
69
|
@green = range_validator(val, 0..255)
|
71
70
|
update_hsl
|
72
71
|
update_hex
|
73
72
|
end
|
74
|
-
|
73
|
+
|
75
74
|
def blue=(val)
|
76
75
|
@blue = range_validator(val, 0..255)
|
77
76
|
update_hsl
|
78
77
|
update_hex
|
79
78
|
end
|
80
|
-
|
79
|
+
|
81
80
|
def lightness=(val)
|
82
81
|
@lightness = range_validator(val, 0..100)
|
83
82
|
update_rgb
|
84
83
|
update_hex
|
85
84
|
end
|
86
|
-
|
85
|
+
|
87
86
|
def saturation=(val)
|
88
87
|
@saturation = range_validator(val, 0..100)
|
89
88
|
update_rgb
|
90
89
|
update_hex
|
91
90
|
end
|
92
|
-
|
91
|
+
|
93
92
|
def hue=(val)
|
94
93
|
@hue = range_validator(val, 0..360)
|
95
94
|
update_rgb
|
96
95
|
update_hex
|
97
96
|
end
|
98
|
-
|
97
|
+
|
99
98
|
def hex=(val)
|
100
99
|
raise(ArgumentError, "Invalid Hex String") unless val.length == 6 && /^[[:xdigit:]]+$/ === val
|
101
100
|
@hex = val.upcase
|
@@ -104,23 +103,23 @@ module Paleta
|
|
104
103
|
@blue = val[4..5].hex
|
105
104
|
update_hsl
|
106
105
|
end
|
107
|
-
|
106
|
+
|
108
107
|
# Determine the equality of the receiver and another {Color}
|
109
108
|
# @param [Color] color color to compare
|
110
109
|
# @return [Boolean]
|
111
110
|
def ==(color)
|
112
111
|
color.is_a?(Color) ? (self.hex == color.hex) : false
|
113
112
|
end
|
114
|
-
|
113
|
+
|
115
114
|
# Create a copy of the receiver and lighten it by a percentage
|
116
115
|
# @param [Number] percentage percentage by which to lighten the {Color}
|
117
116
|
# @return [Color] a lightened copy of the receiver
|
118
117
|
def lighten(percentage = 5)
|
119
|
-
copy = self.
|
118
|
+
copy = self.dup
|
120
119
|
copy.lighten!(percentage)
|
121
120
|
copy
|
122
121
|
end
|
123
|
-
|
122
|
+
|
124
123
|
# Lighten the receiver by a percentage
|
125
124
|
# @param [Number] percentage percentage by which to lighten the {Color}
|
126
125
|
# @return [Color] self
|
@@ -131,16 +130,16 @@ module Paleta
|
|
131
130
|
update_hex
|
132
131
|
self
|
133
132
|
end
|
134
|
-
|
133
|
+
|
135
134
|
# Create a copy of the receiver and darken it by a percentage
|
136
135
|
# @param [Number] percentage percentage by which to darken the {Color}
|
137
136
|
# @return [Color] a darkened copy of the receiver
|
138
137
|
def darken(percentage = 5)
|
139
|
-
copy = self.
|
138
|
+
copy = self.dup
|
140
139
|
copy.darken!(percentage)
|
141
140
|
copy
|
142
141
|
end
|
143
|
-
|
142
|
+
|
144
143
|
# Darken the receiver by a percentage
|
145
144
|
# @param [Number] percentage percentage by which to darken the {Color}
|
146
145
|
# @return [Color] self
|
@@ -151,7 +150,7 @@ module Paleta
|
|
151
150
|
update_hex
|
152
151
|
self
|
153
152
|
end
|
154
|
-
|
153
|
+
|
155
154
|
# Create a copy of the receiver and invert it
|
156
155
|
# @return [Color] an inverted copy of the receiver
|
157
156
|
def invert
|
@@ -159,7 +158,7 @@ module Paleta
|
|
159
158
|
copy.invert!
|
160
159
|
copy
|
161
160
|
end
|
162
|
-
|
161
|
+
|
163
162
|
# Invert the receiver
|
164
163
|
# @return [Color] self
|
165
164
|
def invert!
|
@@ -170,7 +169,7 @@ module Paleta
|
|
170
169
|
update_hex
|
171
170
|
self
|
172
171
|
end
|
173
|
-
|
172
|
+
|
174
173
|
# Create a copy of the receiver and desaturate it
|
175
174
|
# @return [Color] a desaturated copy of the receiver
|
176
175
|
def desaturate
|
@@ -187,7 +186,7 @@ module Paleta
|
|
187
186
|
update_hex
|
188
187
|
self
|
189
188
|
end
|
190
|
-
|
189
|
+
|
191
190
|
# Create a new {Color} that is the complement of the receiver
|
192
191
|
# @return [Color] a desaturated copy of the receiver
|
193
192
|
def complement
|
@@ -195,7 +194,7 @@ module Paleta
|
|
195
194
|
copy.complement!
|
196
195
|
copy
|
197
196
|
end
|
198
|
-
|
197
|
+
|
199
198
|
# Turn the receiver into it's complement
|
200
199
|
# @return [Color] self
|
201
200
|
def complement!
|
@@ -204,14 +203,14 @@ module Paleta
|
|
204
203
|
update_hex
|
205
204
|
self
|
206
205
|
end
|
207
|
-
|
206
|
+
|
208
207
|
# Calculate the similarity between the receiver and another {Color}
|
209
208
|
# @param [Color] color color to calculate the similarity to
|
210
209
|
# @return [Number] a value in [0..1] with 0 being identical and 1 being as dissimilar as possible
|
211
210
|
def similarity(color)
|
212
211
|
distance({ :r => @red, :g => @green, :b => @blue}, { :r => color.red, :g => color.green, :b => color.blue}) / sqrt(3 * (255 ** 2))
|
213
212
|
end
|
214
|
-
|
213
|
+
|
215
214
|
# Return an array representation of a {Color} instance,
|
216
215
|
# @param [Symbol] model the color model, should be :rgb or :hsl
|
217
216
|
# @return [Array] an array of component values
|
@@ -226,25 +225,25 @@ module Paleta
|
|
226
225
|
end
|
227
226
|
array
|
228
227
|
end
|
229
|
-
|
228
|
+
|
230
229
|
private
|
231
|
-
|
230
|
+
|
232
231
|
def rgb_init(red = 0, green = 0, blue = 0)
|
233
232
|
self.red = red
|
234
233
|
self.green = green
|
235
234
|
self.blue = blue
|
236
235
|
end
|
237
|
-
|
236
|
+
|
238
237
|
def hsl_init(hue = 0, saturation = 0, lightness = 0)
|
239
238
|
self.hue = hue
|
240
239
|
self.saturation = saturation
|
241
240
|
self.lightness = lightness
|
242
241
|
end
|
243
|
-
|
242
|
+
|
244
243
|
def hex_init(val = "000000")
|
245
244
|
self.hex = val
|
246
245
|
end
|
247
|
-
|
246
|
+
|
248
247
|
def update_hsl
|
249
248
|
r = @red / 255.0 rescue 0.0
|
250
249
|
g = @green / 255.0 rescue 0.0
|
@@ -253,10 +252,14 @@ module Paleta
|
|
253
252
|
min = [r, g, b].min
|
254
253
|
max = [r, g, b].max
|
255
254
|
delta = max - min
|
256
|
-
|
255
|
+
|
257
256
|
h = 0
|
258
257
|
l = (max + min) / 2.0
|
259
|
-
|
258
|
+
if ![1, 0].include?(l) && delta / 2.0 == l
|
259
|
+
s = 1.0
|
260
|
+
else
|
261
|
+
s = ((l == 0 || l == 1) ? 0 : (delta / (1 - (2 * l - 1).abs)))
|
262
|
+
end
|
260
263
|
|
261
264
|
if delta != 0
|
262
265
|
case max
|
@@ -265,15 +268,14 @@ module Paleta
|
|
265
268
|
when b; h = ((r - g) / delta) + 4
|
266
269
|
end
|
267
270
|
end
|
268
|
-
|
271
|
+
|
269
272
|
@hue = h * 60
|
270
273
|
@hue += 360 if @hue < 0
|
271
274
|
@saturation = s * 100
|
272
275
|
@lightness = l * 100
|
273
276
|
end
|
274
|
-
|
277
|
+
|
275
278
|
def update_rgb
|
276
|
-
|
277
279
|
h = @hue / 60.0 rescue 0.0
|
278
280
|
s = @saturation / 100.0 rescue 0.0
|
279
281
|
l = @lightness / 100.0 rescue 0.0
|
@@ -281,7 +283,7 @@ module Paleta
|
|
281
283
|
d1 = (1 - (2 * l - 1).abs) * s
|
282
284
|
d2 = d1 * (1 - (h % 2 - 1).abs)
|
283
285
|
d3 = l - (d1 / 2.0)
|
284
|
-
|
286
|
+
|
285
287
|
case h.to_i
|
286
288
|
when 0; @red, @green, @blue = d1, d2, 0
|
287
289
|
when 1; @red, @green, @blue = d2, d1, 0
|
@@ -291,12 +293,12 @@ module Paleta
|
|
291
293
|
when 5; @red, @green, @blue = d1, 0, d2
|
292
294
|
else; @red, @green, @blue = 0, 0, 0
|
293
295
|
end
|
294
|
-
|
296
|
+
|
295
297
|
@red = 255 * (@red + d3)
|
296
298
|
@green = 255 * (@green + d3)
|
297
299
|
@blue = 255 * (@blue + d3)
|
298
300
|
end
|
299
|
-
|
301
|
+
|
300
302
|
def update_hex
|
301
303
|
r = @red.to_i.to_s(16) rescue "00"
|
302
304
|
g = @green.to_i.to_s(16) rescue "00"
|
@@ -306,7 +308,7 @@ module Paleta
|
|
306
308
|
b = "0#{b}" if b.length < 2
|
307
309
|
@hex = "#{r}#{g}#{b}".upcase
|
308
310
|
end
|
309
|
-
|
311
|
+
|
310
312
|
def range_validator(val, range)
|
311
313
|
range.include?(val) ? val : raise(ArgumentError, "Component range exceeded")
|
312
314
|
end
|
data/lib/paleta/core_ext/math.rb
CHANGED
@@ -8,20 +8,20 @@ module Math
|
|
8
8
|
a.each_with_index { |v, i| sum += (a[i] - b[i]) ** 2 } if a.is_a?(Array)
|
9
9
|
sqrt(sum)
|
10
10
|
end
|
11
|
-
|
11
|
+
|
12
12
|
def multiple_regression(dx, dy, dz)
|
13
13
|
regression = {}
|
14
14
|
regression[:slope], regression[:offset] = {}, {}
|
15
15
|
size = dx.size
|
16
|
-
|
16
|
+
|
17
17
|
raise "arguments not same length!" unless size == dy.size && size == dz.size
|
18
|
-
|
18
|
+
|
19
19
|
if size == 1
|
20
20
|
regression[:slope] = { :x => dx[0], :y => dy[0], :z => dz[0] }
|
21
21
|
regression[:offset] = { :x => 0, :y => 0, :z => 0 }
|
22
22
|
return regression
|
23
23
|
end
|
24
|
-
|
24
|
+
|
25
25
|
sxx = syy = szz = sxy = szx = syz = sx = sy = sz = 0
|
26
26
|
dx.zip(dy, dz).each do |x, y, z|
|
27
27
|
sxx += x ** 2
|
@@ -34,15 +34,15 @@ module Math
|
|
34
34
|
sy += y
|
35
35
|
sz += z
|
36
36
|
end
|
37
|
-
|
37
|
+
|
38
38
|
regression[:slope][:x] = ( size * sxy - sx * sz ) / ( size * sxx - sx ** 2 ).to_f
|
39
39
|
regression[:slope][:y] = ( size * syz - sz * sy ) / ( size * syy - sz ** 2 ).to_f
|
40
40
|
regression[:slope][:z] = ( size * syz - sz * sy ) / ( size * szz - sy ** 2 ).to_f
|
41
|
-
|
41
|
+
|
42
42
|
regression[:offset][:x] = (sz - regression[:slope][:x] * sx) / size
|
43
43
|
regression[:offset][:y] = (sy - regression[:slope][:y] * sz) / size
|
44
44
|
regression[:offset][:z] = (sx - regression[:slope][:z] * sy) / size
|
45
|
-
|
45
|
+
|
46
46
|
regression
|
47
47
|
end
|
48
48
|
end
|
data/lib/paleta/palette.rb
CHANGED
@@ -1,24 +1,23 @@
|
|
1
1
|
require 'paleta/core_ext/math'
|
2
2
|
|
3
3
|
module Paleta
|
4
|
-
|
5
4
|
module MagickDependent
|
6
5
|
def self.included(klass)
|
7
|
-
require 'RMagick'
|
6
|
+
require 'RMagick' unless defined?(Magick)
|
8
7
|
klass.extend(ClassMethods)
|
9
8
|
rescue LoadError
|
10
9
|
puts "You must install RMagick to use Palette.generate(:from => :image, ...)"
|
11
10
|
end
|
12
|
-
|
11
|
+
|
13
12
|
module ClassMethods
|
14
13
|
def generate_from_image(path, size = 5)
|
15
14
|
include Magick
|
16
15
|
begin
|
17
16
|
image = Magick::ImageList.new(path)
|
18
|
-
|
17
|
+
|
19
18
|
# quantize image to the nearest power of 2 greater the desired palette size
|
20
19
|
quantized_image = image.quantize((Math.sqrt(size).ceil ** 2), Magick::RGBColorspace)
|
21
|
-
colors = quantized_image.color_histogram.sort { |a, b| b[1] <=> a[1] }[0..(size - 1)].map do |color|
|
20
|
+
colors = quantized_image.color_histogram.sort { |a, b| b[1] <=> a[1] }[0..(size - 1)].map do |color|
|
22
21
|
Paleta::Color.new(color[0].red / 256, color[0].green / 256, color[0].blue / 256)
|
23
22
|
end
|
24
23
|
return Paleta::Palette.new(colors)
|
@@ -28,15 +27,15 @@ module Paleta
|
|
28
27
|
end
|
29
28
|
end
|
30
29
|
end
|
31
|
-
|
30
|
+
|
32
31
|
# Represents a palette, a collection of {Color}s
|
33
32
|
class Palette
|
34
33
|
include Math
|
35
34
|
include Enumerable
|
36
35
|
include MagickDependent
|
37
|
-
|
36
|
+
|
38
37
|
attr_accessor :colors
|
39
|
-
|
38
|
+
|
40
39
|
# Initialize a {Palette} from a list of {Color}s
|
41
40
|
# @param [Array] colors a list of {Color}s to include in the {Palette}
|
42
41
|
# @return [Palette] A new instance of {Palette}
|
@@ -45,7 +44,7 @@ module Paleta
|
|
45
44
|
colors = (args.length == 1 && args[0].is_a?(Array)) ? args[0] : args
|
46
45
|
colors.each { |color| self << color }
|
47
46
|
end
|
48
|
-
|
47
|
+
|
49
48
|
# Add a {Color} to the {Palette}
|
50
49
|
# @overload <<(color)
|
51
50
|
# @param [Color] color a {Color} to add to the receiver
|
@@ -63,7 +62,7 @@ module Paleta
|
|
63
62
|
end
|
64
63
|
self
|
65
64
|
end
|
66
|
-
|
65
|
+
|
67
66
|
# Add a {Color} to the {Palette}
|
68
67
|
# @overload push(color)
|
69
68
|
# @param [Color] color a {Color} to add to the receiver
|
@@ -74,56 +73,56 @@ module Paleta
|
|
74
73
|
def push(obj)
|
75
74
|
self << obj
|
76
75
|
end
|
77
|
-
|
76
|
+
|
78
77
|
# Remove the most recently added {Color} from the receiver
|
79
78
|
def pop
|
80
79
|
@colors.pop
|
81
80
|
end
|
82
|
-
|
81
|
+
|
83
82
|
# Remove a {Color} from the receiver by index
|
84
83
|
# @param [Number] index the index at which to remove a {Color}
|
85
84
|
def delete_at(index = 0)
|
86
85
|
@colors.delete_at(index)
|
87
86
|
end
|
88
|
-
|
87
|
+
|
89
88
|
# Access a {Color} in the receiver by index
|
90
89
|
# @param [Number] index the index at which to access a {Color}
|
91
90
|
def [](index)
|
92
91
|
@colors[index]
|
93
92
|
end
|
94
|
-
|
93
|
+
|
95
94
|
# The number of {Color}s in the {Palette}
|
96
95
|
# @return [Number] the number of {Color}s in the receiver
|
97
96
|
def size
|
98
97
|
@colors.size
|
99
98
|
end
|
100
|
-
|
99
|
+
|
101
100
|
# Iterate through each {Color} in the {Palette}
|
102
101
|
def each
|
103
102
|
@colors.each { |c| yield c }
|
104
103
|
end
|
105
|
-
|
104
|
+
|
106
105
|
# Create a new instance of {Palette} that is a sorted copy of the receiver
|
107
106
|
# @return [Palette] a new instance of {Palette}
|
108
107
|
def sort(&blk)
|
109
108
|
@colors.sort(&blk)
|
110
109
|
Paleta::Palette.new(@colors)
|
111
110
|
end
|
112
|
-
|
111
|
+
|
113
112
|
# Sort the {Color}s in the receiver
|
114
113
|
# return [Palette] self
|
115
114
|
def sort!(&blk)
|
116
115
|
@colors.sort!(&blk)
|
117
116
|
self
|
118
117
|
end
|
119
|
-
|
118
|
+
|
120
119
|
# Test if a {Color} exists in the receiver
|
121
120
|
# @param [Color] color color to test for inclusion in the {Palette}
|
122
121
|
# @return [Boolean]
|
123
122
|
def include?(color)
|
124
123
|
@colors.include?(color)
|
125
124
|
end
|
126
|
-
|
125
|
+
|
127
126
|
# Lighen each {Color} in the receiver by a percentage
|
128
127
|
# @param [Number] percentage percentage by which to lighten each {Color} in the receiver
|
129
128
|
# @return [Palette] self
|
@@ -131,7 +130,7 @@ module Paleta
|
|
131
130
|
@colors.each { |color| color.lighten!(percentage) }
|
132
131
|
self
|
133
132
|
end
|
134
|
-
|
133
|
+
|
135
134
|
# Lighen each {Color} in the receiver by a percentage
|
136
135
|
# @param [Number] percentage percentage by which to lighten each {Color} in the receiver
|
137
136
|
# @return [Palette] self
|
@@ -139,40 +138,40 @@ module Paleta
|
|
139
138
|
@colors.each { |color| color.darken!(percentage) }
|
140
139
|
self
|
141
140
|
end
|
142
|
-
|
141
|
+
|
143
142
|
# Invert each {Color} in the receiver by a percentage
|
144
143
|
# @return [Palette] self
|
145
144
|
def invert!
|
146
145
|
@colors.each { |color| color.invert! }
|
147
146
|
self
|
148
147
|
end
|
149
|
-
|
148
|
+
|
150
149
|
# Calculate the similarity between the receiver and another {Palette}
|
151
150
|
# @param [Palette] palette palette to calculate the similarity to
|
152
151
|
# @return [Number] a value in [0..1] with 0 being identical and 1 being as dissimilar as possible
|
153
152
|
def similarity(palette)
|
154
153
|
r, a, b = [], [], []
|
155
154
|
(0..1).each { |i| a[i], b[i] = {}, {} }
|
156
|
-
|
155
|
+
|
157
156
|
# r[i] is a hash of the multiple regression of the Palette in RGB space
|
158
157
|
r[0] = fit
|
159
158
|
r[1] = palette.fit
|
160
|
-
|
159
|
+
|
161
160
|
[0, 1].each do |i|
|
162
161
|
[:x, :y, :z].each do |k|
|
163
162
|
a[i][k] = 0 * r[i][:slope][k] + r[i][:offset][k]
|
164
163
|
b[i][k] = 255 * r[i][:slope][k] + r[i][:offset][k]
|
165
164
|
end
|
166
165
|
end
|
167
|
-
|
166
|
+
|
168
167
|
d_max = sqrt(3 * (65025 ** 2))
|
169
|
-
|
168
|
+
|
170
169
|
d1 = distance(a[0], a[1]) / d_max
|
171
170
|
d2 = distance(b[0], b[1]) / d_max
|
172
|
-
|
171
|
+
|
173
172
|
d1 + d2
|
174
173
|
end
|
175
|
-
|
174
|
+
|
176
175
|
# Generate a {Palette} from a seed {Color}
|
177
176
|
# @param [Hash] opts the options with which to generate a new {Palette}
|
178
177
|
# @option opts [Symbol] :type the type of palette to generate
|
@@ -182,25 +181,25 @@ module Paleta
|
|
182
181
|
# @option opts [Number] :size the number of {Color}s to generate for the {Palette}
|
183
182
|
# @return [Palette] A new instance of {Palette}
|
184
183
|
def self.generate(opts = {})
|
185
|
-
|
184
|
+
|
186
185
|
size = opts[:size] || 5
|
187
|
-
|
186
|
+
|
188
187
|
if !opts[:type].nil? && opts[:type].to_sym == :random
|
189
188
|
return self.generate_random_from_color(opts[:color], size)
|
190
189
|
end
|
191
|
-
|
190
|
+
|
192
191
|
unless (opts[:from].to_sym == :color && !opts[:color].nil?) || (opts[:from].to_sym == :image && !opts[:image].nil?)
|
193
192
|
return raise(ArgumentError, 'You must pass :from and it must be either :color or :image, then you must pass :image => "/path/to/img" or :color => color')
|
194
193
|
end
|
195
|
-
|
194
|
+
|
196
195
|
if opts[:from].to_sym == :image
|
197
196
|
path = opts[:image]
|
198
197
|
return self.generate_from_image(path, size)
|
199
198
|
end
|
200
|
-
|
199
|
+
|
201
200
|
color = opts[:color]
|
202
201
|
type = opts[:type] || :shades
|
203
|
-
|
202
|
+
|
204
203
|
case type
|
205
204
|
when :analogous; self.generate_analogous_from_color(color, size)
|
206
205
|
when :complementary; self.generate_complementary_from_color(color, size)
|
@@ -212,7 +211,7 @@ module Paleta
|
|
212
211
|
else raise(ArgumentError, "Palette type is not defined. Try :analogous, :monochromatic, :shades, or :random")
|
213
212
|
end
|
214
213
|
end
|
215
|
-
|
214
|
+
|
216
215
|
# Return an array representation of a {Palette} instance,
|
217
216
|
# @param [Symbol] model the color model, should be :rgb, :hsl, or :hex
|
218
217
|
# @return [Array] an Array of Arrays where each sub-Array is a representation of a {Color} object in a {Palette} instance
|
@@ -227,9 +226,9 @@ module Paleta
|
|
227
226
|
end
|
228
227
|
array
|
229
228
|
end
|
230
|
-
|
229
|
+
|
231
230
|
private
|
232
|
-
|
231
|
+
|
233
232
|
def self.generate_analogous_from_color(color, size)
|
234
233
|
raise(ArgumentError, "Passed argument is not a Color") unless color.is_a?(Color)
|
235
234
|
palette = self.new(color)
|
@@ -248,14 +247,14 @@ module Paleta
|
|
248
247
|
end
|
249
248
|
palette.sort! { |a, b| a.hue <=> b.hue }
|
250
249
|
end
|
251
|
-
|
250
|
+
|
252
251
|
def self.generate_complementary_from_color(color, size)
|
253
252
|
raise(ArgumentError, "Passed argument is not a Color") unless color.is_a?(Color)
|
254
253
|
complement = color.complement
|
255
254
|
palette = self.new(color, complement)
|
256
255
|
add_monochromatic_in_hues_of_color(palette, color, size)
|
257
256
|
end
|
258
|
-
|
257
|
+
|
259
258
|
def self.generate_triad_from_color(color, size)
|
260
259
|
raise(ArgumentError, "Passed argument is not a Color") unless color.is_a?(Color)
|
261
260
|
color2 = Paleta::Color.new(:hsl, (color.hue + 120) % 360, color.saturation, color.lightness)
|
@@ -263,7 +262,7 @@ module Paleta
|
|
263
262
|
palette = self.new(color, color2, color3)
|
264
263
|
add_monochromatic_in_hues_of_color(palette, color, size)
|
265
264
|
end
|
266
|
-
|
265
|
+
|
267
266
|
def self.generate_tetrad_from_color(color, size)
|
268
267
|
raise(ArgumentError, "Passed argument is not a Color") unless color.is_a?(Color)
|
269
268
|
color2 = Paleta::Color.new(:hsl, (color.hue + 90) % 360, color.saturation, color.lightness)
|
@@ -272,7 +271,7 @@ module Paleta
|
|
272
271
|
palette = self.new(color, color2, color3, color4)
|
273
272
|
add_monochromatic_in_hues_of_color(palette, color, size)
|
274
273
|
end
|
275
|
-
|
274
|
+
|
276
275
|
def self.generate_monochromatic_from_color(color, size)
|
277
276
|
raise(ArgumentError, "Passed argument is not a Color") unless color.is_a?(Color)
|
278
277
|
palette = self.new(color)
|
@@ -290,7 +289,7 @@ module Paleta
|
|
290
289
|
end
|
291
290
|
palette.sort! { |a, b| a.saturation <=> b.saturation }
|
292
291
|
end
|
293
|
-
|
292
|
+
|
294
293
|
def self.generate_shades_from_color(color, size)
|
295
294
|
raise(ArgumentError, "Passed argument is not a Color") unless color.is_a?(Color)
|
296
295
|
palette = self.new(color)
|
@@ -298,17 +297,17 @@ module Paleta
|
|
298
297
|
lightness = color.lightness
|
299
298
|
d = :down
|
300
299
|
until palette.size == size
|
301
|
-
lightness -= step if d == :down
|
302
|
-
lightness += step if d == :up
|
303
|
-
palette << Paleta::Color.new(:hsl, color.hue, color.saturation, lightness)
|
304
300
|
if lightness - step < 0
|
305
301
|
d = :up
|
306
302
|
lightness = color.lightness
|
307
303
|
end
|
304
|
+
lightness -= step if d == :down
|
305
|
+
lightness += step if d == :up
|
306
|
+
palette << Paleta::Color.new(:hsl, color.hue, color.saturation, lightness)
|
308
307
|
end
|
309
308
|
palette.sort! { |a, b| a.lightness <=> b.lightness }
|
310
309
|
end
|
311
|
-
|
310
|
+
|
312
311
|
def self.generate_split_complement_from_color(color, size)
|
313
312
|
raise(ArgumentError, "Passed argument is not a Color") unless color.is_a?(Color)
|
314
313
|
color2 = Paleta::Color.new(:hsl, (color.hue + 150) % 360, color.saturation, color.lightness)
|
@@ -316,7 +315,7 @@ module Paleta
|
|
316
315
|
palette = self.new(color, color2, color3)
|
317
316
|
add_monochromatic_in_hues_of_color(palette, color, size)
|
318
317
|
end
|
319
|
-
|
318
|
+
|
320
319
|
def self.generate_random_from_color(color = nil, size)
|
321
320
|
palette = color.is_a?(Color) ? self.new(color) : self.new
|
322
321
|
r = Random.new(Time.now.sec)
|
@@ -325,14 +324,14 @@ module Paleta
|
|
325
324
|
end
|
326
325
|
palette
|
327
326
|
end
|
328
|
-
|
327
|
+
|
329
328
|
def self.add_monochromatic_in_hues_of_color(palette, color, size)
|
330
|
-
raise(ArgumentError, "Second argument is not a Color") unless color.is_a?(Color)
|
329
|
+
raise(ArgumentError, "Second argument is not a Color") unless color.is_a?(Color)
|
331
330
|
hues = palette.map { |c| c.hue }
|
332
331
|
step = ugap = dgap = 100 / size
|
333
332
|
i = j = 0
|
334
333
|
saturation = color.saturation
|
335
|
-
until palette.size == size
|
334
|
+
until palette.size == size
|
336
335
|
if color.saturation + ugap < 100
|
337
336
|
saturation = color.saturation + ugap
|
338
337
|
ugap += step
|
@@ -340,13 +339,13 @@ module Paleta
|
|
340
339
|
saturation = color.saturation - dgap
|
341
340
|
dgap += step
|
342
341
|
end if j == 3 || j == 1
|
343
|
-
new_color = Paleta::Color.new(:hsl, hues[i], saturation, color.lightness)
|
342
|
+
new_color = Paleta::Color.new(:hsl, hues[i], saturation, color.lightness)
|
344
343
|
palette << new_color unless palette.include?(new_color)
|
345
344
|
i += 1; j += 1; i %= hues.size; j %= (2 * hues.size)
|
346
345
|
end
|
347
346
|
palette.sort! { |a, b| a.saturation <=> b.saturation }
|
348
347
|
end
|
349
|
-
|
348
|
+
|
350
349
|
def fit
|
351
350
|
# create a 3xn matrix where n = @colors.size to represent the set of colors
|
352
351
|
reds = @colors.map { |c| c.red }
|
data/lib/paleta/version.rb
CHANGED
data/paleta.gemspec
CHANGED
@@ -14,4 +14,8 @@ Gem::Specification.new do |gem|
|
|
14
14
|
gem.name = 'paleta'
|
15
15
|
gem.require_paths = ['lib']
|
16
16
|
gem.version = Paleta::VERSION
|
17
|
+
|
18
|
+
gem.add_development_dependency "rspec", "~> 2.8"
|
19
|
+
gem.add_development_dependency "guard-rspec", "~> 1.2"
|
20
|
+
gem.add_development_dependency "pry-byebug", "~> 2.0"
|
17
21
|
end
|
data/readme.markdown
CHANGED
@@ -27,63 +27,82 @@ Paleta allows users to create Color objects. Color objects can be defined by HSL
|
|
27
27
|
#### Creating Colors
|
28
28
|
|
29
29
|
Colors can be created using RGB or HSL components, or by using a HEX value by passing in a flag of the desired format as the first parameter. If no format flag is used, RGB is assumed.
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
color = Paleta::Color.new(:hsl, 280, 37, 68)
|
33
|
+
color = Paleta::Color.new(:rgb, 94, 161, 235)
|
34
|
+
color = Paleta::Color.new(:hex, "5EA1EB")
|
35
|
+
|
36
|
+
# creating a Color with no flag defaults to RGB components
|
37
|
+
|
38
|
+
color = Paleta::Color.new(94, 161, 235)
|
39
|
+
```
|
40
|
+
|
38
41
|
Individual component values can be accessed by name
|
39
42
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
43
|
+
```ruby
|
44
|
+
color.red # => 94
|
45
|
+
color.green # => 161
|
46
|
+
color.blue # => 235
|
47
|
+
color.hue # => 211.48936170212767
|
48
|
+
color.saturation # => 77.90055248618782
|
49
|
+
color.lightness # => 64.50980392156862
|
50
|
+
color.hex # => "5EA1EB"
|
51
|
+
```
|
48
52
|
|
49
53
|
Get an array representation of a Color
|
50
54
|
|
51
|
-
|
52
|
-
|
53
|
-
|
55
|
+
```ruby
|
56
|
+
c = Paleta::Color.new(30, 90, 120)
|
57
|
+
c.to_array(:rgb) # => [30, 90, 120]
|
58
|
+
```
|
59
|
+
|
54
60
|
#### Manipulating Colors
|
55
61
|
|
62
|
+
|
56
63
|
Colors can be lightened or darkened by a percentage
|
57
64
|
|
58
|
-
|
59
|
-
|
60
|
-
|
65
|
+
```ruby
|
66
|
+
color.lighten!(10)
|
67
|
+
color.darken!(30)
|
68
|
+
```
|
69
|
+
|
61
70
|
Colors can be desaturated
|
62
71
|
|
63
|
-
|
64
|
-
|
72
|
+
```ruby
|
73
|
+
color.desaturate!
|
74
|
+
```
|
75
|
+
|
65
76
|
Colors can be turned into their complement Colors
|
66
77
|
|
67
|
-
|
78
|
+
```ruby
|
79
|
+
color.complement!
|
80
|
+
```
|
68
81
|
|
69
82
|
Colors can be inverted
|
70
83
|
|
71
|
-
|
72
|
-
|
84
|
+
```ruby
|
85
|
+
color.invert!
|
86
|
+
```
|
87
|
+
|
73
88
|
**Note** all of the previous methods directly manipulate the object on which they were called. If you would like to create a new Color object that is a copy of the original color (but with the desired manipulation), call the desired method without the trailing bang `!`.
|
74
89
|
|
75
90
|
For example, lets create a new Color that is the complement of a Color we have already defined.
|
76
91
|
|
77
|
-
|
78
|
-
|
92
|
+
```ruby
|
93
|
+
new_color = color.complement
|
94
|
+
```
|
95
|
+
|
79
96
|
#### Comparing Colors
|
80
97
|
|
81
98
|
Colors can calculate their similarity to other Colors. The `similarity` method returns a value between 0 and 1, with 0 being identical and 1 being as dissimilar as possible.
|
82
99
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
100
|
+
```ruby
|
101
|
+
color = Paleta::Color.new(:hsl, 280, 37, 68)
|
102
|
+
color2 = Paleta::Color.new(237, 172, 33)
|
103
|
+
color.similarity(color2) # => 0.4100287904421024
|
104
|
+
```
|
105
|
+
|
87
106
|
### Palette
|
88
107
|
|
89
108
|
Palettes are collections of Colors, they share many common Array methods such as `push`, `pop`, `sort`, `include?` and `each`. Palettes also allow collections of Colors to be manipulated as a whole and to be compared to each other.
|
@@ -92,99 +111,127 @@ Palettes are collections of Colors, they share many common Array methods such as
|
|
92
111
|
|
93
112
|
Palettes can be created by passing a list of Colors to the Palette constructor, or on the fly with `push` and `<<`.
|
94
113
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
114
|
+
```ruby
|
115
|
+
color1 = Paleta::Color.new(13, 57, 182)
|
116
|
+
color2 = Paleta::Color.new(94, 161, 235)
|
117
|
+
color3 = Paleta::Color.new(237, 182, 17)
|
118
|
+
palette = Paleta::Palette.new(color1, color2)
|
119
|
+
|
120
|
+
palette << color3
|
121
|
+
```
|
122
|
+
|
102
123
|
#### Retrieving and Removing Colors from Palettes
|
103
124
|
|
104
125
|
Colors can be accessed and removed by index.
|
105
126
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
127
|
+
```ruby
|
128
|
+
palette[1] # => color2
|
129
|
+
|
130
|
+
palette.delete_at(2)
|
131
|
+
```
|
132
|
+
|
110
133
|
Get an array representation of a Palette
|
111
134
|
|
112
|
-
|
113
|
-
|
114
|
-
|
135
|
+
```ruby
|
136
|
+
c1 = Paleta::Color.new(13, 57, 182)
|
137
|
+
c2 = Paleta::Color.new(94, 161, 235)
|
138
|
+
palette = Paleta::Palette.new(c1, c2)
|
115
139
|
|
116
|
-
|
140
|
+
palette.to_array(:rgb) # => [[13, 57, 182], [94, 161, 235]]
|
141
|
+
```
|
117
142
|
|
118
143
|
#### Manipulating Palettes
|
119
144
|
|
120
145
|
Palettes can be lightened, darkened or inverted as a whole.
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
146
|
+
|
147
|
+
```ruby
|
148
|
+
palette.lighten!(15)
|
149
|
+
palette.darken!(20)
|
150
|
+
palette.invert!
|
151
|
+
```
|
152
|
+
|
126
153
|
#### Comparing Palettes
|
127
154
|
|
128
155
|
Palettes can calculate their similarity to other Palettes by using the `similarity` method. Just as with `Color#similarity`, this method returns a value between 0 and 1, with 0 being identical and 1 being as dissimilar as possible.
|
129
156
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
157
|
+
```ruby
|
158
|
+
color1 = Paleta::Color.new(13, 57, 182)
|
159
|
+
color2 = Paleta::Color.new(237, 172, 33)
|
160
|
+
palette1 = Paleta::Palette.new(color1, color2)
|
161
|
+
|
162
|
+
color3 = Paleta::Color.new(13, 57, 182)
|
163
|
+
color4 = Paleta::Color.new(94, 161, 235)
|
164
|
+
palette2 = Paleta::Palette.new(color3, color4)
|
165
|
+
|
166
|
+
palette1.similarity(palette2) # => 0.0046992695975874915
|
167
|
+
```
|
137
168
|
|
138
|
-
palette1.similarity(palette2) # => 0.0046992695975874915
|
139
|
-
|
140
169
|
#### Generating Palettes
|
141
170
|
|
142
171
|
Palettes can be generated from a "seed" Color or from an image by using the `generate` method.
|
143
172
|
|
144
173
|
**Generate a Palette of shades from a Color**
|
145
174
|
|
146
|
-
|
147
|
-
|
148
|
-
|
175
|
+
```ruby
|
176
|
+
color = Paleta::Color.new(:hex, "ff0000")
|
177
|
+
palette = Paleta::Palette.generate(:type => :shades, :from => :color, :size => 5)
|
178
|
+
```
|
179
|
+
|
149
180
|
**Generate a Palette of analogous Colors from a Color**
|
150
181
|
|
151
|
-
|
152
|
-
|
153
|
-
|
182
|
+
```ruby
|
183
|
+
color = Paleta::Color.new(:hex, "0066cc")
|
184
|
+
palette = Paleta::Palette.generate(:type => :analogous, :from => :color, :size => 5)
|
185
|
+
```
|
186
|
+
|
154
187
|
**Generate a Palette of monochromatic Colors from a Color**
|
155
188
|
|
156
|
-
|
157
|
-
|
158
|
-
|
189
|
+
```ruby
|
190
|
+
color = Paleta::Color.new(:hex, "336699")
|
191
|
+
palette = Paleta::Palette.generate(:type => :monochromatic, :from => :color, :size => 5)
|
192
|
+
```
|
193
|
+
|
159
194
|
**Generate a Palette of complementary Colors from a Color**
|
160
195
|
|
161
|
-
|
162
|
-
|
196
|
+
```ruby
|
197
|
+
color = Paleta::Color.new(:hex, "0000ff")
|
198
|
+
palette = Paleta::Palette.generate(:type => :complementary, :from => :color, :size => 5)
|
199
|
+
```
|
163
200
|
|
164
201
|
**Generate a Palette of split-complement Colors from a Color**
|
165
202
|
|
166
|
-
|
167
|
-
|
203
|
+
```ruby
|
204
|
+
color = Paleta::Color.new(:hex, "006699")
|
205
|
+
palette = Paleta::Palette.generate(:type => :split_complement, :from => :color, :size => 5)
|
206
|
+
```
|
168
207
|
|
169
208
|
**Generate a Palette of triad Colors from a Color**
|
170
209
|
|
171
|
-
|
172
|
-
|
210
|
+
```ruby
|
211
|
+
color = Paleta::Color.new(:hex, "006699")
|
212
|
+
palette = Paleta::Palette.generate(:type => :triad, :from => :color, :size => 5)
|
213
|
+
```
|
173
214
|
|
174
215
|
**Generate a Palette of tetrad Colors from a Color**
|
175
216
|
|
176
|
-
|
177
|
-
|
178
|
-
|
217
|
+
```ruby
|
218
|
+
color = Paleta::Color.new(:hex, "dd5533")
|
219
|
+
palette = Paleta::Palette.generate(:type => :tetrad, :from => :color, :size => 5)
|
220
|
+
```
|
221
|
+
|
179
222
|
**Generate a random Palette**
|
180
223
|
|
181
|
-
|
182
|
-
|
224
|
+
```ruby
|
225
|
+
palette = Paleta::Palette.generate(:type => :random, :size => 5)
|
226
|
+
```
|
227
|
+
|
183
228
|
Palettes can also be generated from a seed image
|
184
229
|
|
185
230
|
**Generate a Palette from an image**
|
186
231
|
|
187
|
-
|
232
|
+
```ruby
|
233
|
+
palette = Paleta::Palette.generate(:from => :image, :image => "/path/to/image.jpg", :size => 5)
|
234
|
+
```
|
188
235
|
|
189
236
|
***
|
190
237
|
|
metadata
CHANGED
@@ -1,16 +1,57 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: paleta
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
5
|
-
prerelease:
|
4
|
+
version: 0.2.1
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Jordan Stephens
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
13
|
-
dependencies:
|
11
|
+
date: 2014-12-23 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.8'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.8'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: guard-rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.2'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.2'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: pry-byebug
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '2.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.0'
|
14
55
|
description: A gem for working with color palettes
|
15
56
|
email:
|
16
57
|
- iam@jordanstephens.net
|
@@ -18,7 +59,7 @@ executables: []
|
|
18
59
|
extensions: []
|
19
60
|
extra_rdoc_files: []
|
20
61
|
files:
|
21
|
-
- .gitignore
|
62
|
+
- ".gitignore"
|
22
63
|
- Gemfile
|
23
64
|
- LICENSE
|
24
65
|
- lib/paleta.rb
|
@@ -34,27 +75,26 @@ files:
|
|
34
75
|
- spec/spec_helper.rb
|
35
76
|
homepage: http://rubygems.org/gems/paleta
|
36
77
|
licenses: []
|
78
|
+
metadata: {}
|
37
79
|
post_install_message:
|
38
80
|
rdoc_options: []
|
39
81
|
require_paths:
|
40
82
|
- lib
|
41
83
|
required_ruby_version: !ruby/object:Gem::Requirement
|
42
|
-
none: false
|
43
84
|
requirements:
|
44
|
-
- -
|
85
|
+
- - ">="
|
45
86
|
- !ruby/object:Gem::Version
|
46
87
|
version: '0'
|
47
88
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
48
|
-
none: false
|
49
89
|
requirements:
|
50
|
-
- -
|
90
|
+
- - ">="
|
51
91
|
- !ruby/object:Gem::Version
|
52
92
|
version: '0'
|
53
93
|
requirements: []
|
54
94
|
rubyforge_project:
|
55
|
-
rubygems_version:
|
95
|
+
rubygems_version: 2.2.2
|
56
96
|
signing_key:
|
57
|
-
specification_version:
|
97
|
+
specification_version: 4
|
58
98
|
summary: A little library for creating, manipulating and comparing colors and color
|
59
99
|
palettes
|
60
100
|
test_files:
|
@@ -62,4 +102,3 @@ test_files:
|
|
62
102
|
- spec/models/color_spec.rb
|
63
103
|
- spec/models/palette_spec.rb
|
64
104
|
- spec/spec_helper.rb
|
65
|
-
has_rdoc:
|