style_train 0.3.2 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/lib/style_train.rb +0 -7
- data/lib/style_train/color.rb +769 -97
- data/lib/style_train/sheet.rb +6 -2
- data/lib/style_train/support/numbers.rb +6 -2
- data/spec/color_spec.rb +1447 -0
- data/spec/numbers_spec.rb +13 -1
- data/spec/sheet_spec.rb +24 -10
- data/spec/themed_sheet_spec.rb +2 -2
- data/style_train.gemspec +4 -17
- metadata +7 -20
- data/lib/style_train/color_types/color_type.rb +0 -233
- data/lib/style_train/color_types/hex_color.rb +0 -44
- data/lib/style_train/color_types/hsl_color.rb +0 -5
- data/lib/style_train/color_types/keyword_color.rb +0 -192
- data/lib/style_train/color_types/rgb_color.rb +0 -57
- data/spec/color/color_spec.rb +0 -252
- data/spec/color/color_type_spec.rb +0 -389
- data/spec/color/hex_color_spec.rb +0 -160
- data/spec/color/keyword_color_spec.rb +0 -56
- data/spec/color/rgb_color_spec.rb +0 -75
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.4.0
|
data/lib/style_train.rb
CHANGED
@@ -16,14 +16,7 @@ module StyleTrain
|
|
16
16
|
end
|
17
17
|
|
18
18
|
# The color load!
|
19
|
-
require 'style_train/color_types/color_type'
|
20
|
-
require 'style_train/color_types/rgb_color'
|
21
|
-
require 'style_train/color_types/hsl_color'
|
22
|
-
require 'style_train/color_types/keyword_color'
|
23
|
-
require 'style_train/color_types/hex_color'
|
24
19
|
require "style_train/color"
|
25
|
-
|
26
|
-
# Big Picture Stuff, sheets, themes, etc
|
27
20
|
require 'style_train/style'
|
28
21
|
require "style_train/sheet"
|
29
22
|
require 'style_train/theme'
|
data/lib/style_train/color.rb
CHANGED
@@ -1,92 +1,507 @@
|
|
1
|
-
# This is a delegatory class that passes most everything to the particular ColorType descendant
|
2
1
|
module StyleTrain
|
3
2
|
class Color
|
4
|
-
attr_accessor :
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
end
|
24
|
-
|
25
|
-
def
|
26
|
-
|
27
|
-
end
|
28
|
-
|
29
|
-
def
|
30
|
-
|
31
|
-
end
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
3
|
+
attr_accessor :type, :r, :g, :b,
|
4
|
+
:hex, :hex_6, :h, :s, :l
|
5
|
+
attr_reader :alpha
|
6
|
+
attr_writer :mix_ratio
|
7
|
+
|
8
|
+
def initialize(*args)
|
9
|
+
if type = args.last.is_a?(Hash) && args.last[:type]
|
10
|
+
build_for_type(type, args)
|
11
|
+
else
|
12
|
+
autodetect(args)
|
13
|
+
end
|
14
|
+
|
15
|
+
if non_null?
|
16
|
+
if (opts = args.last) && args.last.is_a?(Hash)
|
17
|
+
self.alpha = opts[:alpha]
|
18
|
+
self.background = opts[:background] if opts[:background]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
self.alpha = 1.0 if alpha.nil? && non_null?
|
22
|
+
end
|
23
|
+
|
24
|
+
def transparent?
|
25
|
+
type == :transparent
|
26
|
+
end
|
27
|
+
|
28
|
+
def non_null?
|
29
|
+
!transparent?
|
30
|
+
end
|
31
|
+
|
32
|
+
def build_for_type(type, args)
|
33
|
+
self.type = type
|
34
|
+
color = if type == :transparent
|
35
|
+
build_transparent(args)
|
36
|
+
elsif type == :keyword
|
37
|
+
build_keyword(args)
|
38
|
+
elsif type == :hex
|
39
|
+
build_hex(args)
|
40
|
+
elsif type == :rgb
|
41
|
+
build_rgb(args)
|
42
|
+
elsif type == :hsl
|
43
|
+
build_hsl(args)
|
44
|
+
end
|
45
|
+
raise ArgumentError, "Unable to build a color of type #{type}, with arguments: #{args.inspect}" unless color
|
46
|
+
end
|
47
|
+
|
48
|
+
def autodetect(args)
|
49
|
+
build_transparent(args) ||
|
50
|
+
build_keyword(args) ||
|
51
|
+
build_hex(args) ||
|
52
|
+
build_rgb(args) ||
|
53
|
+
build_hsl(args) || raise( AutodetectionError )
|
54
|
+
end
|
55
|
+
|
56
|
+
def build_transparent( args )
|
57
|
+
if [:transparent, 'transparent'].include?(args.first)
|
58
|
+
self.type = :transparent
|
59
|
+
self.r = self.g = self.b = 0
|
60
|
+
self.alpha = 0.0
|
61
|
+
self
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def build_keyword( args )
|
66
|
+
if (color = args.first).is_a?(Symbol)
|
67
|
+
self.type = :keyword
|
68
|
+
raise KeywordError, "Color #{color.inspect} not found as a keyword" unless rgb = KEYWORD_MAP[color]
|
69
|
+
self.r = rgb[0]/255.0
|
70
|
+
self.g = rgb[1]/255.0
|
71
|
+
self.b = rgb[2]/255.0
|
72
|
+
self
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def build_hex(args)
|
77
|
+
if args.size < 3 && args.first.to_s.match(/^#?([a-f0-9]{3}|[a-f0-9]{6})$/i)
|
78
|
+
self.type = :hex
|
79
|
+
self.hex = "#{$1}"
|
80
|
+
self.hex_6 = self.class.normalize_hex(hex)
|
81
|
+
self.r = ((self.hex_6 / 0x10000) & 0xff) / 255.0
|
82
|
+
self.g = ((self.hex_6 / 0x100) & 0xff) / 255.0
|
83
|
+
self.b = (self.hex_6 & 0xff) /255.0
|
84
|
+
self
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def build_rgb(args)
|
89
|
+
array = args[0..2].compact
|
90
|
+
first_value = self.class.normalize_for_rgb(array.first) rescue nil
|
91
|
+
return nil unless array.size == 3 && first_value
|
92
|
+
|
93
|
+
self.type = :rgb
|
94
|
+
self.r = first_value
|
95
|
+
self.g = self.class.normalize_for_rgb(array[1])
|
96
|
+
self.b = self.class.normalize_for_rgb(array[2])
|
97
|
+
self
|
98
|
+
end
|
99
|
+
|
100
|
+
def build_hsl(args)
|
101
|
+
array = args[0..2].compact
|
102
|
+
hue = self.class.normalize_hue(array.first) rescue nil
|
103
|
+
return nil unless array.size == 3 && hue
|
104
|
+
|
105
|
+
self.type = :hsl
|
106
|
+
self.h = hue
|
107
|
+
self.s = self.class.normalize_for_hsl(array[1])
|
108
|
+
self.l = self.class.normalize_for_hsl(array[2])
|
109
|
+
build_rgb_from_hsl
|
110
|
+
|
111
|
+
self
|
112
|
+
end
|
113
|
+
|
114
|
+
def self.normalize_hue(value)
|
115
|
+
if value.is_a?(String)
|
116
|
+
if value.match(/degrees$/)
|
117
|
+
normalize_degrees(value)
|
118
|
+
else
|
119
|
+
normalize_percentage(value) / 100.0
|
120
|
+
end
|
121
|
+
elsif value.is_a?(Float)
|
122
|
+
normalize_ratio(value)
|
123
|
+
else
|
124
|
+
normalize_degrees(value) # hue does not come it byte values
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.normalize_for_hsl(value)
|
129
|
+
if value.is_a?(String)
|
130
|
+
normalize_percentage(value)/100.0 if value.match(/%$/)
|
131
|
+
elsif value.is_a?(Float)
|
132
|
+
normalize_ratio(value)
|
133
|
+
else # integer
|
134
|
+
normalize_byte(value)/255.0
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def build_rgb_from_hsl
|
139
|
+
self.r = hsl_value_to_rgb( h + 1/3.0 )
|
140
|
+
self.g = hsl_value_to_rgb( h )
|
141
|
+
self.b = hsl_value_to_rgb( h - 1/3.0 )
|
142
|
+
end
|
143
|
+
|
144
|
+
def hsl_value_to_rgb( value=[h,s,l] )
|
145
|
+
value = value + 1 if value < 0
|
146
|
+
value = value - 1 if value > 1
|
147
|
+
if value * 6 < 1
|
148
|
+
hsl_median_1 + (hsl_median_2 - hsl_median_1)*value*6
|
149
|
+
elsif value * 2 < 1
|
150
|
+
hsl_median_2
|
151
|
+
elsif value * 3 < 2
|
152
|
+
hsl_median_1 + (hsl_median_2 - hsl_median_1)*(2/3.0 - value)*6
|
153
|
+
else
|
154
|
+
hsl_median_1
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def hsl_median_2
|
159
|
+
l <= 0.5 ? l*(s+1) : l+s-l*s
|
160
|
+
end
|
161
|
+
|
162
|
+
def hsl_median_1
|
163
|
+
l*2-hsl_median_2
|
164
|
+
end
|
165
|
+
|
166
|
+
# CLASS LEVEL VALUE NORMALIZERS -----------------------------------------------
|
167
|
+
def self.normalize_for_rgb(value)
|
168
|
+
if value.is_a?(String)
|
169
|
+
if value.match(percentage_regex)
|
170
|
+
normalize_percentage(value)/100.0
|
171
|
+
elsif value.match(degree_regex)
|
172
|
+
return nil
|
173
|
+
else
|
174
|
+
normalize_byte(value)/255.0
|
175
|
+
end
|
176
|
+
elsif value.is_a?(Float)
|
177
|
+
normalize_ratio(value)
|
178
|
+
else # integer
|
179
|
+
normalize_byte(value)/255.0
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def self.normalize_percentage(value)
|
184
|
+
if value.class == String && value.match(percentage_regex)
|
185
|
+
value = $1
|
186
|
+
end
|
187
|
+
value = value.to_f
|
188
|
+
raise PercentageError if value > 100.0 || value < 0.0
|
189
|
+
value
|
190
|
+
end
|
191
|
+
|
192
|
+
def self.percentage_regex
|
193
|
+
/^([\d.]*)%$/
|
194
|
+
end
|
195
|
+
|
196
|
+
def self.normalize_degrees(value)
|
197
|
+
return nil if value.is_a?(String) && !value.match(degree_regex)
|
198
|
+
(value.to_i % 360)/360.0
|
199
|
+
end
|
200
|
+
|
201
|
+
def self.degree_regex
|
202
|
+
/degrees$/
|
203
|
+
end
|
204
|
+
|
205
|
+
def self.normalize_byte(value)
|
206
|
+
raise ByteError if value > 255 || value < 0
|
207
|
+
value
|
208
|
+
end
|
209
|
+
|
210
|
+
def self.normalize_ratio(value)
|
211
|
+
raise RatioError if value > 1 || value < 0
|
212
|
+
value
|
213
|
+
end
|
214
|
+
|
215
|
+
def self.ratio_to_byte(value)
|
216
|
+
raise RatioError if value < 0 || value > 1.0
|
217
|
+
percentage_to_byte(value*100)
|
218
|
+
end
|
219
|
+
|
220
|
+
def self.normalize_hex( hex )
|
221
|
+
expanded = ""
|
222
|
+
if hex.size == 3
|
223
|
+
hex.each_char{|c| expanded << c*2}
|
224
|
+
else
|
225
|
+
expanded = hex
|
226
|
+
end
|
227
|
+
|
228
|
+
"0x#{expanded}".to_i(16)
|
229
|
+
end
|
230
|
+
|
231
|
+
def self.percentage_to_byte(value)
|
232
|
+
(normalize_percentage(value) * 2.55 ).round
|
233
|
+
end
|
234
|
+
|
235
|
+
# OPTIONAL PROPERTIES ----------------------------------------------
|
236
|
+
def alpha=(value)
|
237
|
+
if value
|
238
|
+
@alpha = if value.is_a?(String) || value.is_a?(Fixnum)
|
239
|
+
self.class.normalize_percentage(value)/100.0
|
240
|
+
else
|
241
|
+
self.class.normalize_ratio(value)
|
38
242
|
end
|
39
|
-
|
40
|
-
end
|
41
|
-
|
42
|
-
def
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
end
|
49
|
-
|
50
|
-
def =~( color )
|
51
|
-
if color.is_a?(Color)
|
52
|
-
self.delegate =~ color.delegate
|
53
|
-
elsif color.is_a?( ColorType )
|
54
|
-
self.delegate =~ color
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def ==( color )
|
59
|
-
color.is_a?( Color ) && self =~ color && self.background == color.background
|
60
|
-
end
|
61
|
-
|
62
|
-
# rendering
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
def background=(value)
|
247
|
+
@background = if value.is_a?(self.class)
|
248
|
+
value
|
249
|
+
else
|
250
|
+
self.class.new(*value)
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
63
254
|
def background
|
64
|
-
@background
|
65
|
-
end
|
66
|
-
|
67
|
-
|
68
|
-
|
255
|
+
@background || self.class.new(:white) unless type == :transparent
|
256
|
+
end
|
257
|
+
|
258
|
+
# COMPARITORS ------------------------------------------------------
|
259
|
+
COMPARE_TOLERANCE = 0.001
|
260
|
+
|
261
|
+
def ==(other)
|
262
|
+
self =~ other &&
|
263
|
+
self.alpha == other.alpha
|
264
|
+
end
|
265
|
+
|
266
|
+
def ===(other)
|
267
|
+
self.object_id == other.object_id
|
268
|
+
end
|
269
|
+
|
270
|
+
def =~(other)
|
271
|
+
(self.r - other.r).abs < COMPARE_TOLERANCE &&
|
272
|
+
(self.g - other.g).abs < COMPARE_TOLERANCE&&
|
273
|
+
(self.b - other.b).abs < COMPARE_TOLERANCE
|
274
|
+
end
|
275
|
+
|
276
|
+
# CONVERSIONS ------------------------------------------------------
|
277
|
+
def set_hsl
|
278
|
+
rgb = [r, g, b]
|
279
|
+
max = rgb.max
|
280
|
+
min = rgb.min
|
281
|
+
avg = ( max + min )/2.0
|
282
|
+
diff = ( max - min ).to_f
|
283
|
+
lightness = avg
|
284
|
+
|
285
|
+
if max == min # achromatic
|
286
|
+
hue = 0
|
287
|
+
saturation = 0
|
288
|
+
else
|
289
|
+
saturation = if avg > 0.6
|
290
|
+
lightness / ( 2 - max - min )
|
291
|
+
else
|
292
|
+
diff / ( max + min )
|
293
|
+
end
|
294
|
+
|
295
|
+
diff_r = (((max - r) / 6) + (diff / 2)) / diff;
|
296
|
+
diff_g = (((max - g) / 6) + (diff / 2)) / diff;
|
297
|
+
diff_b = (((max - b) / 6) + (diff / 2)) / diff;
|
298
|
+
|
299
|
+
hue = if r == max
|
300
|
+
diff_b - diff_g
|
301
|
+
elsif g == max
|
302
|
+
1/3.0 + diff_r - diff_b
|
303
|
+
else
|
304
|
+
2/3.0 + diff_g - diff_r
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
|
309
|
+
hue += 1 if hue < 0
|
310
|
+
hue -= 1 if hue > 1
|
311
|
+
|
312
|
+
self.type = :hsl
|
313
|
+
self.h = hue
|
314
|
+
self.s = saturation
|
315
|
+
self.l = lightness
|
316
|
+
|
317
|
+
self
|
318
|
+
end
|
319
|
+
|
320
|
+
def set_rgb
|
321
|
+
self.type = :rgb
|
322
|
+
end
|
323
|
+
|
324
|
+
def set_hex
|
325
|
+
self.type = :hex
|
326
|
+
self.hex = ""
|
327
|
+
self.hex << sprintf( "%02X", r*255 );
|
328
|
+
self.hex << sprintf( "%02X", g*255 );
|
329
|
+
self.hex << sprintf( "%02X", b*255 );
|
330
|
+
self.hex_6 = self.class.normalize_hex(hex)
|
331
|
+
end
|
332
|
+
|
333
|
+
def hsl_to_rgb
|
334
|
+
self.hex = nil
|
335
|
+
self.hex_6 = nil
|
336
|
+
build_rgb_from_hsl
|
337
|
+
self
|
338
|
+
end
|
339
|
+
|
340
|
+
# TRANSFORMATIONS --------------------------------------------
|
341
|
+
def reset_for_shift
|
342
|
+
self.hex = nil
|
343
|
+
self.hex_6 = nil
|
344
|
+
set_hsl unless :type == :hsl
|
345
|
+
end
|
346
|
+
|
347
|
+
def shift!(value)
|
348
|
+
reset_for_shift
|
349
|
+
value = value/100.0 if value.is_a?(Integer)
|
350
|
+
value = self.class.normalize_percentage(value)/100.0 if value.is_a?(String)
|
351
|
+
value = self.class.normalize_ratio(value)
|
352
|
+
yield(value)
|
353
|
+
hsl_to_rgb
|
354
|
+
end
|
355
|
+
|
356
|
+
def lighten(value=0.1)
|
357
|
+
color = self.dup
|
358
|
+
color.lighten!(value)
|
359
|
+
end
|
360
|
+
|
361
|
+
def lighten!(value=0.1)
|
362
|
+
shift!(value) {|v| self.l = [1.0, l + v].min }
|
363
|
+
end
|
364
|
+
|
365
|
+
def darken(value=0.1)
|
366
|
+
color = self.dup
|
367
|
+
color.darken!(value)
|
368
|
+
end
|
369
|
+
|
370
|
+
def darken!(value=0.1)
|
371
|
+
shift!(value) { |v| self.l = [0.0, l - v].max }
|
372
|
+
end
|
373
|
+
|
374
|
+
def saturate(value=0.1)
|
375
|
+
color = self.dup
|
376
|
+
color.saturate!(value)
|
377
|
+
end
|
378
|
+
|
379
|
+
def saturate!(value=0.1)
|
380
|
+
shift!(value) { |v| self.s = [1.0, s + v].min }
|
381
|
+
end
|
382
|
+
|
383
|
+
def dull(value=0.1)
|
384
|
+
color = self.dup
|
385
|
+
color.dull!(value)
|
386
|
+
end
|
387
|
+
|
388
|
+
def dull!(value=0.1)
|
389
|
+
shift!(value) { |v| self.s = [0.0, s - v].max }
|
390
|
+
end
|
391
|
+
|
392
|
+
alias :brighten :saturate
|
393
|
+
alias :brighten! :saturate!
|
394
|
+
alias :desaturate :dull
|
395
|
+
alias :desaturate! :dull!
|
396
|
+
|
397
|
+
def shift_hue!(value)
|
398
|
+
reset_for_shift
|
399
|
+
value = self.class.normalize_hue(value)
|
400
|
+
yield(value)
|
401
|
+
hsl_to_rgb
|
402
|
+
end
|
403
|
+
|
404
|
+
def rotate(value=10.degrees)
|
405
|
+
color = self.dup
|
406
|
+
color.rotate!(value)
|
407
|
+
end
|
408
|
+
|
409
|
+
def rotate!(value=10.degrees)
|
410
|
+
shift_hue!(value) {|v| self.h = (v + h) % 1 }
|
411
|
+
end
|
412
|
+
|
413
|
+
def compliment
|
414
|
+
rotate(0.5)
|
415
|
+
end
|
69
416
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
else
|
78
|
-
Color.new(opts)
|
417
|
+
def dial(divisions=3, offset = 0.0)
|
418
|
+
divisions = divisions.to_i
|
419
|
+
offset = self.class.normalize_hue(offset)
|
420
|
+
set = []
|
421
|
+
(1..divisions).each do |n|
|
422
|
+
amount = (offset + (n-1)/divisions.to_f)%1
|
423
|
+
set << rotate(amount)
|
79
424
|
end
|
80
|
-
|
81
|
-
|
425
|
+
set
|
426
|
+
end
|
427
|
+
|
428
|
+
def triangulate(offset = 0.0)
|
429
|
+
dial(3, offset)
|
430
|
+
end
|
431
|
+
|
432
|
+
COLDEST_HUE = 240/360.0
|
433
|
+
WARMEST_HUE = 0.0
|
434
|
+
|
435
|
+
def rotate_in_temperature!(value)
|
436
|
+
set_hsl
|
437
|
+
value = self.class.normalize_hue(value)
|
438
|
+
unless [COLDEST_HUE, WARMEST_HUE].include?(h)
|
439
|
+
new_h = yield(value)
|
440
|
+
rotate!((new_h - h)%1)
|
441
|
+
end
|
442
|
+
self
|
443
|
+
end
|
444
|
+
|
445
|
+
def warmer!(value=10.degrees)
|
446
|
+
rotate_in_temperature!(value) do |v|
|
447
|
+
if h < COLDEST_HUE
|
448
|
+
[WARMEST_HUE, (h - v)].max.abs
|
449
|
+
else
|
450
|
+
[1.0-WARMEST_HUE, (h + v)].min.abs
|
451
|
+
end
|
452
|
+
end
|
453
|
+
end
|
454
|
+
|
455
|
+
def cooler!(value=10.degrees)
|
456
|
+
rotate_in_temperature!(value) do |v|
|
457
|
+
new_h = if h < COLDEST_HUE
|
458
|
+
[COLDEST_HUE, (h + v)].min.abs
|
459
|
+
else
|
460
|
+
[COLDEST_HUE, (h - v)].max.abs
|
461
|
+
end
|
462
|
+
end
|
463
|
+
end
|
464
|
+
|
465
|
+
def warmer(value=10.degrees)
|
466
|
+
color = self.dup
|
467
|
+
color.warmer!(value)
|
468
|
+
end
|
469
|
+
|
470
|
+
def cooler(value=10.degrees)
|
471
|
+
color = self.dup
|
472
|
+
color.cooler!(value)
|
473
|
+
end
|
474
|
+
|
475
|
+
# COMBINING COLORS -------------------------------------------
|
476
|
+
def mix(other, ratio=nil)
|
477
|
+
ratio = 1-ratio if ratio
|
478
|
+
this_ratio = mix_ratio
|
479
|
+
ratio ||= this_ratio
|
480
|
+
other_ratio = other.mix_ratio
|
481
|
+
ratio ||= other_ratio ? 1-other_ratio : 0.5
|
482
|
+
|
483
|
+
color = self.dup
|
484
|
+
color.r = self.class.mix(color.r, other.r, ratio)
|
485
|
+
color.g = self.class.mix(color.g, other.g, ratio)
|
486
|
+
color.b = self.class.mix(color.b, other.b, ratio)
|
487
|
+
color.alpha = self.class.mix(color.alpha, other.alpha, ratio)
|
488
|
+
color
|
489
|
+
end
|
82
490
|
|
83
|
-
|
84
|
-
|
491
|
+
def layer(other)
|
492
|
+
color = self.dup
|
493
|
+
ratio = 1 - other.alpha
|
494
|
+
color.r = self.class.mix(color.r, other.r, ratio)
|
495
|
+
color.g = self.class.mix(color.g, other.g, ratio)
|
496
|
+
color.b = self.class.mix(color.b, other.b, ratio)
|
497
|
+
color.alpha = self.class.blend_alphas( color.alpha, other.alpha )
|
498
|
+
|
499
|
+
color
|
85
500
|
end
|
86
501
|
|
87
502
|
def step(end_color, steps=10)
|
88
|
-
start_color = self.
|
89
|
-
end_color = end_color.
|
503
|
+
start_color = self.dup
|
504
|
+
end_color = end_color.dup
|
90
505
|
array = [start_color]
|
91
506
|
(2..(steps-1)).each do |number|
|
92
507
|
ratio = 1 - (steps-number)/steps.to_f
|
@@ -94,22 +509,279 @@ module StyleTrain
|
|
94
509
|
end
|
95
510
|
array << end_color
|
96
511
|
array
|
97
|
-
end
|
98
|
-
|
99
|
-
def
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
end
|
512
|
+
end
|
513
|
+
|
514
|
+
def flatten
|
515
|
+
background.alpha = 1.0
|
516
|
+
background.layer(self)
|
517
|
+
end
|
518
|
+
|
519
|
+
def percent(value)
|
520
|
+
self.mix_ratio = self.class.normalize_percentage(value)/100.0
|
521
|
+
self
|
522
|
+
end
|
523
|
+
|
524
|
+
def mix_ratio
|
525
|
+
ratio = @mix_ratio
|
526
|
+
self.mix_ratio = nil
|
527
|
+
ratio
|
528
|
+
end
|
529
|
+
|
530
|
+
def self.mix(value_1, value_2, ratio)
|
531
|
+
value_1*(ratio) + value_2*(1-ratio)
|
532
|
+
end
|
533
|
+
|
534
|
+
def self.blend_alphas(first, second)
|
535
|
+
base, blender = first > second ? [first, second] : [second, first]
|
536
|
+
difference = 1.0 - base
|
537
|
+
base + difference * blender
|
538
|
+
end
|
539
|
+
|
540
|
+
|
541
|
+
# RENDERING -----------------------------------------------------
|
542
|
+
def inspect
|
543
|
+
"#<#{self.class}:#{self.object_id} @alpha=#{self.alpha} @r=#{self.r} @g=#{self.g} @b=#{self.b}>"
|
544
|
+
end
|
545
|
+
|
546
|
+
def render(as=self.class.render_as)
|
547
|
+
if as == :hsl
|
548
|
+
render_as_hsl
|
549
|
+
elsif as == :hsla
|
550
|
+
render_as_hsla
|
551
|
+
elsif as == :rgb
|
552
|
+
render_as_rgb
|
553
|
+
elsif as == :rgba
|
554
|
+
render_as_rgba
|
555
|
+
elsif as == :flat
|
556
|
+
render_as_flat
|
557
|
+
else
|
558
|
+
render_as_hex
|
559
|
+
end
|
560
|
+
end
|
561
|
+
|
562
|
+
alias :to_s :render
|
563
|
+
|
564
|
+
def render_as_flat
|
565
|
+
color = flatten
|
566
|
+
color.render
|
567
|
+
end
|
568
|
+
|
569
|
+
def render_as_hex
|
570
|
+
if alpha < 1
|
571
|
+
render_as_rgba
|
572
|
+
else
|
573
|
+
set_hex
|
574
|
+
"##{hex}"
|
575
|
+
end
|
576
|
+
end
|
577
|
+
|
578
|
+
def render_as_rgb
|
579
|
+
if alpha < 1
|
580
|
+
render_as_rgba
|
581
|
+
else
|
582
|
+
"rgb(#{r*100}%, #{g*100}%, #{b*100}%)"
|
583
|
+
end
|
584
|
+
end
|
585
|
+
|
586
|
+
def render_as_rgba
|
587
|
+
"rgba(#{r*100}%, #{g*100}%, #{b*100}%, #{alpha})"
|
588
|
+
end
|
589
|
+
|
590
|
+
def render_as_hsl
|
591
|
+
if alpha < 1
|
592
|
+
render_as_hsla
|
593
|
+
else
|
594
|
+
set_hsl
|
595
|
+
"hsl(#{(h*360).round}, #{s*100}%, #{l*100}%)"
|
596
|
+
end
|
597
|
+
end
|
598
|
+
|
599
|
+
def render_as_hsla
|
600
|
+
"hsla(#{(h*360).round}, #{s*100}%, #{l*100}%, #{alpha})"
|
601
|
+
end
|
602
|
+
|
603
|
+
def self.render_as
|
604
|
+
@render_as || :hex
|
605
|
+
end
|
606
|
+
|
607
|
+
def self.render_as=(value)
|
608
|
+
@render_as = value
|
609
|
+
end
|
610
|
+
|
611
|
+
KEYWORD_MAP = {
|
612
|
+
:aliceblue => [240, 248, 255],
|
613
|
+
:antiquewhite => [250, 235, 215],
|
614
|
+
:aqua => [0, 255, 255],
|
615
|
+
:aquamarine => [127, 255, 212],
|
616
|
+
:azure => [240, 255, 255],
|
617
|
+
:beige => [245, 245, 220],
|
618
|
+
:bisque => [255, 228, 196],
|
619
|
+
:black => [0, 0, 0],
|
620
|
+
:blanchedalmond => [255, 235, 205],
|
621
|
+
:blue => [0, 0, 255],
|
622
|
+
:blueviolet => [138, 43, 226],
|
623
|
+
:brown => [165, 42, 42],
|
624
|
+
:burlywood => [222, 184, 135],
|
625
|
+
:cadetblue => [95, 158, 160],
|
626
|
+
:chartreuse => [127, 255, 0],
|
627
|
+
:chocolate => [210, 105, 30],
|
628
|
+
:coral => [255, 127, 80],
|
629
|
+
:cornflowerblue => [100, 149, 237],
|
630
|
+
:cornsilk => [255, 248, 220],
|
631
|
+
:crimson => [220, 20, 60],
|
632
|
+
:cyan => [0, 255, 255],
|
633
|
+
:darkblue => [0, 0, 139],
|
634
|
+
:darkcyan => [0, 139, 139],
|
635
|
+
:darkgoldenrod => [184, 134, 11],
|
636
|
+
:darkgray => [169, 169, 169],
|
637
|
+
:darkgreen => [0, 100, 0],
|
638
|
+
:darkgrey => [169, 169, 169],
|
639
|
+
:darkkhaki => [189, 183, 107],
|
640
|
+
:darkmagenta => [139, 0, 139],
|
641
|
+
:darkolivegreen => [85, 107, 47],
|
642
|
+
:darkorange => [255, 140, 0],
|
643
|
+
:darkorchid => [153, 50, 204],
|
644
|
+
:darkred => [139, 0, 0],
|
645
|
+
:darksalmon => [233, 150, 122],
|
646
|
+
:darkseagreen => [143, 188, 143],
|
647
|
+
:darkslateblue => [72, 61, 139],
|
648
|
+
:darkslategray => [47, 79, 79],
|
649
|
+
:darkslategrey => [47, 79, 79],
|
650
|
+
:darkturquoise => [0, 206, 209],
|
651
|
+
:darkviolet => [148, 0, 211],
|
652
|
+
:deeppink => [255, 20, 147],
|
653
|
+
:deepskyblue => [0, 191, 255],
|
654
|
+
:dimgray => [105, 105, 105],
|
655
|
+
:dimgrey => [105, 105, 105],
|
656
|
+
:dodgerblue => [30, 144, 255],
|
657
|
+
:firebrick => [178, 34, 34],
|
658
|
+
:floralwhite => [255, 250, 240],
|
659
|
+
:forestgreen => [34, 139, 34],
|
660
|
+
:fuchsia => [255, 0, 255],
|
661
|
+
:gainsboro => [220, 220, 220],
|
662
|
+
:ghostwhite => [248, 248, 255],
|
663
|
+
:gold => [255, 215, 0],
|
664
|
+
:goldenrod => [218, 165, 32],
|
665
|
+
:gray => [128, 128, 128],
|
666
|
+
:green => [0, 128, 0],
|
667
|
+
:greenyellow => [173, 255, 47],
|
668
|
+
:grey => [128, 128, 128],
|
669
|
+
:honeydew => [240, 255, 240],
|
670
|
+
:hotpink => [255, 105, 180],
|
671
|
+
:indianred => [205, 92, 92],
|
672
|
+
:indigo => [75, 0, 130],
|
673
|
+
:ivory => [255, 255, 240],
|
674
|
+
:khaki => [240, 230, 140],
|
675
|
+
:lavender => [230, 230, 250],
|
676
|
+
:lavenderblush => [255, 240, 245],
|
677
|
+
:lawngreen => [124, 252, 0],
|
678
|
+
:lemonchiffon => [255, 250, 205],
|
679
|
+
:lightblue => [173, 216, 230],
|
680
|
+
:lightcoral => [240, 128, 128],
|
681
|
+
:lightcyan => [224, 255, 255],
|
682
|
+
:lightgoldenrodyellow => [250, 250, 210],
|
683
|
+
:lightgray => [211, 211, 211],
|
684
|
+
:lightgreen => [144, 238, 144],
|
685
|
+
:lightgrey => [211, 211, 211],
|
686
|
+
:lightpink => [255, 182, 193],
|
687
|
+
:lightsalmon => [255, 160, 122],
|
688
|
+
:lightseagreen => [32, 178, 170],
|
689
|
+
:lightskyblue => [135, 206, 250],
|
690
|
+
:lightslategray => [119, 136, 153],
|
691
|
+
:lightslategrey => [119, 136, 153],
|
692
|
+
:lightsteelblue => [176, 196, 222],
|
693
|
+
:lightyellow => [255, 255, 224],
|
694
|
+
:lime => [0, 255, 0],
|
695
|
+
:limegreen => [50, 205, 50],
|
696
|
+
:linen => [250, 240, 230],
|
697
|
+
:magenta => [255, 0, 255],
|
698
|
+
:maroon => [128, 0, 0],
|
699
|
+
:mediumaquamarine => [102, 205, 170],
|
700
|
+
:mediumblue => [0, 0, 205],
|
701
|
+
:mediumorchid => [186, 85, 211],
|
702
|
+
:mediumpurple => [147, 112, 219],
|
703
|
+
:mediumseagreen => [60, 179, 113],
|
704
|
+
:mediumslateblue => [123, 104, 238],
|
705
|
+
:mediumspringgreen => [0, 250, 154],
|
706
|
+
:mediumturquoise => [72, 209, 204],
|
707
|
+
:mediumvioletred => [199, 21, 133],
|
708
|
+
:midnightblue => [25, 25, 112],
|
709
|
+
:mintcream => [245, 255, 250],
|
710
|
+
:mistyrose => [255, 228, 225],
|
711
|
+
:moccasin => [255, 228, 181],
|
712
|
+
:navajowhite => [255, 222, 173],
|
713
|
+
:navy => [0, 0, 128],
|
714
|
+
:oldlace => [253, 245, 230],
|
715
|
+
:olive => [128, 128, 0],
|
716
|
+
:olivedrab => [107, 142, 35],
|
717
|
+
:orange => [255, 165, 0],
|
718
|
+
:orangered => [255, 69, 0],
|
719
|
+
:orchid => [218, 112, 214],
|
720
|
+
:palegoldenrod => [238, 232, 170],
|
721
|
+
:palegreen => [152, 251, 152],
|
722
|
+
:paleturquoise => [175, 238, 238],
|
723
|
+
:palevioletred => [219, 112, 147],
|
724
|
+
:papayawhip => [255, 239, 213],
|
725
|
+
:peachpuff => [255, 218, 185],
|
726
|
+
:peru => [205, 133, 63],
|
727
|
+
:pink => [255, 192, 203],
|
728
|
+
:plum => [221, 160, 221],
|
729
|
+
:powderblue => [176, 224, 230],
|
730
|
+
:purple => [128, 0, 128],
|
731
|
+
:red => [255, 0, 0],
|
732
|
+
:rosybrown => [188, 143, 143],
|
733
|
+
:royalblue => [65, 105, 225],
|
734
|
+
:saddlebrown => [139, 69, 19],
|
735
|
+
:salmon => [250, 128, 114],
|
736
|
+
:sandybrown => [244, 164, 96],
|
737
|
+
:seagreen => [46, 139, 87],
|
738
|
+
:seashell => [255, 245, 238],
|
739
|
+
:sienna => [160, 82, 45],
|
740
|
+
:silver => [192, 192, 192],
|
741
|
+
:skyblue => [135, 206, 235],
|
742
|
+
:slateblue => [106, 90, 205],
|
743
|
+
:slategray => [112, 128, 144],
|
744
|
+
:slategrey => [112, 128, 144],
|
745
|
+
:snow => [255, 250, 250],
|
746
|
+
:springgreen => [0, 255, 127],
|
747
|
+
:steelblue => [70, 130, 180],
|
748
|
+
:tan => [210, 180, 140],
|
749
|
+
:teal => [0, 128, 128],
|
750
|
+
:thistle => [216, 191, 216],
|
751
|
+
:tomato => [255, 99, 71],
|
752
|
+
:turquoise => [64, 224, 208],
|
753
|
+
:violet => [238, 130, 238],
|
754
|
+
:wheat => [245, 222, 179],
|
755
|
+
:white => [255, 255, 255],
|
756
|
+
:whitesmoke => [245, 245, 245],
|
757
|
+
:yellow => [255, 255, 0],
|
758
|
+
:yellowgreen => [154, 205, 50]
|
759
|
+
}
|
760
|
+
|
761
|
+
class PercentageError < ArgumentError
|
762
|
+
def message
|
763
|
+
@message ||= 'Percentages must be between 0 and 100'
|
764
|
+
end
|
765
|
+
end
|
766
|
+
|
767
|
+
class KeywordError < ArgumentError; end
|
768
|
+
|
769
|
+
class RatioError < ArgumentError
|
770
|
+
def message
|
771
|
+
@message ||= "Ratios must be a decimal value between 0 and 1"
|
772
|
+
end
|
773
|
+
end
|
774
|
+
|
775
|
+
class ByteError < ArgumentError
|
776
|
+
def message
|
777
|
+
@message ||= "Bytes must be between 0 and 255"
|
778
|
+
end
|
779
|
+
end
|
780
|
+
|
781
|
+
class AutodetectionError < ArgumentError
|
782
|
+
def message
|
783
|
+
@message ||= "Unable to determine color type"
|
784
|
+
end
|
785
|
+
end
|
114
786
|
end
|
115
|
-
end
|
787
|
+
end
|