paleta 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|