paleta 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/paleta/color.rb +42 -12
- data/lib/paleta/core_ext/math.rb +50 -0
- data/lib/paleta/palette.rb +31 -18
- data/lib/paleta/version.rb +1 -1
- data/lib/paleta.rb +0 -1
- data/readme.markdown +21 -8
- data/spec/models/color_spec.rb +22 -1
- data/spec/models/palette_spec.rb +30 -6
- metadata +3 -3
- data/lib/paleta/multiple_regression.rb +0 -39
data/lib/paleta/color.rb
CHANGED
@@ -1,15 +1,48 @@
|
|
1
|
+
require 'paleta/core_ext/math'
|
2
|
+
|
1
3
|
module Paleta
|
2
4
|
class Color
|
3
5
|
include Math
|
4
6
|
|
5
7
|
attr_reader :red, :green, :blue, :hue, :saturation, :lightness, :hex
|
6
8
|
|
7
|
-
def initialize(
|
9
|
+
def initialize(*args)
|
10
|
+
|
11
|
+
# example: new(:hex, "336699")
|
12
|
+
if args.length == 2 && args[0] == :hex && args[1].is_a?(String)
|
13
|
+
hex_init(args[1])
|
14
|
+
# example: new(235, 129, 74)
|
15
|
+
elsif args.length == 3 && args[0].is_a?(Numeric) && args[1].is_a?(Numeric) && args[2].is_a?(Numeric)
|
16
|
+
rgb_init(args[0], args[1], args[2])
|
17
|
+
elsif args.length == 4 && [:rgb, :hsl].include?(args[0]) && args[1].is_a?(Numeric) && args[2].is_a?(Numeric) && args[3].is_a?(Numeric)
|
18
|
+
# example: new(:hsl, 320, 96, 74)
|
19
|
+
rgb_init(args[1], args[2], args[3]) if args[0] == :rgb
|
20
|
+
# example: new(:rgb, 235, 129, 74)
|
21
|
+
hsl_init(args[1], args[2], args[3]) if args[0] == :hsl
|
22
|
+
elsif args.length == 0
|
23
|
+
# example: new()
|
24
|
+
rgb_init(0, 0, 0)
|
25
|
+
else
|
26
|
+
raise(ArgumentError, "Invalid arguments")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def rgb_init(red = 0, green = 0, blue = 0)
|
8
31
|
self.red = red
|
9
32
|
self.green = green
|
10
33
|
self.blue = blue
|
11
34
|
end
|
12
35
|
|
36
|
+
def hsl_init(hue = 0, saturation = 0, lightness = 0)
|
37
|
+
self.hue = hue
|
38
|
+
self.saturation = saturation
|
39
|
+
self.lightness = lightness
|
40
|
+
end
|
41
|
+
|
42
|
+
def hex_init(val = "000000")
|
43
|
+
self.hex = val
|
44
|
+
end
|
45
|
+
|
13
46
|
def red=(val)
|
14
47
|
@red = range_validator(val, 0..255)
|
15
48
|
update_hsl
|
@@ -47,10 +80,7 @@ module Paleta
|
|
47
80
|
end
|
48
81
|
|
49
82
|
def hex=(val)
|
50
|
-
|
51
|
-
(val[0..1] == 0 && val[0..1] != "00") ||
|
52
|
-
(val[2..3] == 0 && val[2..3] != "00") ||
|
53
|
-
(val[4..5] == 0 && val[4..5] != "00")
|
83
|
+
unless val.length == 6 && /^[[:xdigit:]]+$/ === val
|
54
84
|
raise(ArgumentError, "Invalid Hex String")
|
55
85
|
end
|
56
86
|
@hex = val.upcase
|
@@ -83,15 +113,15 @@ module Paleta
|
|
83
113
|
end
|
84
114
|
|
85
115
|
def similarity(color)
|
86
|
-
|
116
|
+
distance({ :r => @red, :g => @green, :b => @blue}, { :r => color.red, :g => color.green, :b => color.blue}) / sqrt(3 * (255 ** 2))
|
87
117
|
end
|
88
118
|
|
89
119
|
private
|
90
120
|
|
91
121
|
def update_hsl
|
92
|
-
r = @red / 255.0 rescue 0
|
93
|
-
g = @green / 255.0 rescue 0
|
94
|
-
b = @blue / 255.0 rescue 0
|
122
|
+
r = @red / 255.0 rescue 0.0
|
123
|
+
g = @green / 255.0 rescue 0.0
|
124
|
+
b = @blue / 255.0 rescue 0.0
|
95
125
|
|
96
126
|
min = [r, g, b].min
|
97
127
|
max = [r, g, b].max
|
@@ -116,9 +146,9 @@ module Paleta
|
|
116
146
|
|
117
147
|
def update_rgb
|
118
148
|
|
119
|
-
h = @hue / 360.0
|
120
|
-
s = @saturation / 100.0
|
121
|
-
l = @lightness / 100.0
|
149
|
+
h = @hue / 360.0 rescue 0
|
150
|
+
s = @saturation / 100.0 rescue 0
|
151
|
+
l = @lightness / 100.0 rescue 0
|
122
152
|
|
123
153
|
if s == 0
|
124
154
|
r = g = b = l * 255
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Math
|
2
|
+
def distance(a, b)
|
3
|
+
unless (a.is_a?(Hash) && b.is_a?(Hash) && a.keys == b.keys) || (a.is_a?(Array) && b.is_a?(Array) && a.size == b.size)
|
4
|
+
raise ArgumentError, "Arguments must be Hashes with identical keys or Arrays of the same size"
|
5
|
+
end
|
6
|
+
sum = 0
|
7
|
+
a.keys.each { |k| sum += (a[k] - b[k]) ** 2 } if a.is_a?(Hash)
|
8
|
+
a.each_with_index { |v, i| sum += (a[i] - b[i]) ** 2 } if a.is_a?(Array)
|
9
|
+
sqrt(sum)
|
10
|
+
end
|
11
|
+
|
12
|
+
class MultipleRegression
|
13
|
+
attr_accessor :slope, :offset
|
14
|
+
|
15
|
+
def initialize dr, dg, db
|
16
|
+
|
17
|
+
@slope, @offset = {}, {}
|
18
|
+
@size = dr.size
|
19
|
+
raise "arguments not same length!" unless @size == dg.size && @size == db.size
|
20
|
+
|
21
|
+
if @size == 1
|
22
|
+
@slope = { :r => dr[0], :g => dg[0], :b => db[0] }
|
23
|
+
@offset = { :r => 0, :g => 0, :b => 0 }
|
24
|
+
return
|
25
|
+
end
|
26
|
+
|
27
|
+
srr = sgg = sbb = srg = sbr = sgb = sr = sb = sg = 0
|
28
|
+
dr.zip(dg,db).each do |r,g,b|
|
29
|
+
srr += r ** 2
|
30
|
+
sgg += g ** 2
|
31
|
+
srg += r * g
|
32
|
+
sbr += b * r
|
33
|
+
sgb += g * b
|
34
|
+
sr += r
|
35
|
+
sb += g
|
36
|
+
sg += b
|
37
|
+
end
|
38
|
+
|
39
|
+
@slope[:r] = ( @size * srg - sr * sb ) / ( @size * srr - sr ** 2 ).to_f
|
40
|
+
@slope[:g] = ( @size * sgb - sb * sg ) / ( @size * sgg - sb ** 2 ).to_f
|
41
|
+
@slope[:b] = ( @size * sgb - sb * sg ) / ( @size * sbb - sg ** 2 ).to_f
|
42
|
+
|
43
|
+
@offset[:r] = (sb - @slope[:r] * sr) / @size
|
44
|
+
@offset[:g] = (sg - @slope[:g] * sb) / @size
|
45
|
+
@offset[:b] = (sr - @slope[:b] * sg) / @size
|
46
|
+
|
47
|
+
return
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/lib/paleta/palette.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'paleta/core_ext/math'
|
2
|
+
|
1
3
|
module Paleta
|
2
4
|
class Palette
|
3
5
|
include Math
|
@@ -15,6 +17,18 @@ module Paleta
|
|
15
17
|
color.is_a?(Color) ? @colors << color : raise(ArgumentError, "Passed argument is not a Color")
|
16
18
|
end
|
17
19
|
|
20
|
+
def push(color)
|
21
|
+
self << color
|
22
|
+
end
|
23
|
+
|
24
|
+
def pop
|
25
|
+
@colors.pop
|
26
|
+
end
|
27
|
+
|
28
|
+
def delete_at(i = 0)
|
29
|
+
@colors.delete_at(i)
|
30
|
+
end
|
31
|
+
|
18
32
|
def [](i)
|
19
33
|
@colors[i]
|
20
34
|
end
|
@@ -46,25 +60,24 @@ module Paleta
|
|
46
60
|
end
|
47
61
|
|
48
62
|
def similarity(palette)
|
49
|
-
|
50
|
-
|
63
|
+
r, a, b = [], [], []
|
64
|
+
(0..1).each { |i| a[i], b[i] = {}, {} }
|
51
65
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
d2 = sqrt(((b1[:r] - b2[:r]) ** 2) + ((b1[:g] - b2[:g]) ** 2) + ((b1[:b] - b2[:b]) ** 2)) / sqrt(3 * (65025 ** 2))
|
66
|
+
# r[i] is the Math::MultipleRegression of the Palette in RGB space
|
67
|
+
r[0] = self.fit
|
68
|
+
r[1] = palette.fit
|
69
|
+
|
70
|
+
[0, 1].each do |i|
|
71
|
+
[:r, :g, :b].each do |k|
|
72
|
+
a[i][k] = 0 * r[i].slope[k] + r[i].offset[k]
|
73
|
+
b[i][k] = 255 * r[i].slope[k] + r[i].offset[k]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
d_max = sqrt(3 * (65025 ** 2))
|
78
|
+
|
79
|
+
d1 = distance(a[0], a[1]) / d_max
|
80
|
+
d2 = distance(b[0], b[1]) / d_max
|
68
81
|
|
69
82
|
d1 + d2
|
70
83
|
end
|
data/lib/paleta/version.rb
CHANGED
data/lib/paleta.rb
CHANGED
data/readme.markdown
CHANGED
@@ -22,7 +22,16 @@ and run
|
|
22
22
|
|
23
23
|
### Color
|
24
24
|
|
25
|
-
|
25
|
+
# create a color with HSL components
|
26
|
+
color = Paleta::Color.new(:hsl, 280, 37, 68)
|
27
|
+
|
28
|
+
# create a color with RGB components
|
29
|
+
color = Paleta::Color.new(:rgb, 94, 161, 235)
|
30
|
+
|
31
|
+
# create a color with a HEX value
|
32
|
+
color = Paleta::Color.new(:hex, "5EA1EB")
|
33
|
+
|
34
|
+
# creating a color with no flag defaults to RGB components
|
26
35
|
color = Paleta::Color.new(94, 161, 235)
|
27
36
|
|
28
37
|
# access component values
|
@@ -58,11 +67,15 @@ and run
|
|
58
67
|
# add Colors to a Palette
|
59
68
|
color1 = Paleta::Color.new(13, 57, 182)
|
60
69
|
color2 = Paleta::Color.new(94, 161, 235)
|
61
|
-
|
70
|
+
color3 = Paleta::Color.new(237, 182, 17)
|
71
|
+
palette = Paleta::Palette.new(color1, color2, color3)
|
62
72
|
|
63
73
|
# retreive a Color from a Palette
|
64
74
|
palette[1] # => color2
|
65
75
|
|
76
|
+
# remove a Color from a Palette by index
|
77
|
+
palette.delete_at(2)
|
78
|
+
|
66
79
|
# lighten and darken an entire Palette by a percentage
|
67
80
|
palette.lighten!(15)
|
68
81
|
palette.darken!(20)
|
@@ -71,13 +84,13 @@ and run
|
|
71
84
|
palette.invert!
|
72
85
|
|
73
86
|
# calculate similarity of two Palettes
|
74
|
-
|
75
|
-
|
76
|
-
palette1 = Paleta::Palette.new(
|
87
|
+
color4 = Paleta::Color.new(13, 57, 182)
|
88
|
+
color5 = Paleta::Color.new(237, 172, 33)
|
89
|
+
palette1 = Paleta::Palette.new(color4, color5)
|
77
90
|
|
78
|
-
|
79
|
-
|
80
|
-
palette2 = Paleta::Palette.new(
|
91
|
+
color6 = Paleta::Color.new(13, 57, 182)
|
92
|
+
color7 = Paleta::Color.new(94, 161, 235)
|
93
|
+
palette2 = Paleta::Palette.new(color6, color7)
|
81
94
|
|
82
95
|
palette1.similarity(palette2) # => 0.0046992695975874915
|
83
96
|
|
data/spec/models/color_spec.rb
CHANGED
@@ -1,13 +1,34 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Paleta::Color do
|
4
|
-
it "should initialize with components in 0..255" do
|
4
|
+
it "should initialize with RGB components in 0..255" do
|
5
5
|
color = Paleta::Color.new(94, 161, 235)
|
6
6
|
color.red.should == 94
|
7
7
|
color.green.should == 161
|
8
8
|
color.blue.should == 235
|
9
9
|
end
|
10
10
|
|
11
|
+
it "should initialize with the :hex flag and a valid 6 character HEX string" do
|
12
|
+
color = Paleta::Color.new(:hex, "5EA1EB")
|
13
|
+
color.red.should == 94
|
14
|
+
color.green.should == 161
|
15
|
+
color.blue.should == 235
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should initialize with the :hsl flag, hue in 0..360, and saturation and lightness in 0..100" do
|
19
|
+
color = Paleta::Color.new(:hsl, 280, 37, 68)
|
20
|
+
color.hue.should == 280
|
21
|
+
color.saturation.should == 37
|
22
|
+
color.lightness.should == 68
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should initialize with the :rgb flag with RGB components in 0..255" do
|
26
|
+
color = Paleta::Color.new(:rgb, 94, 161, 235)
|
27
|
+
color.red.should == 94
|
28
|
+
color.green.should == 161
|
29
|
+
color.blue.should == 235
|
30
|
+
end
|
31
|
+
|
11
32
|
it "should not initialize with components not in 0..255" do
|
12
33
|
expect{ Paleta::Color.new(-74, 333, 4321) }.to raise_error
|
13
34
|
end
|
data/spec/models/palette_spec.rb
CHANGED
@@ -14,7 +14,7 @@ describe Paleta::Palette do
|
|
14
14
|
expect{ Paleta::Palette.new(c1, c2) }.to raise_error
|
15
15
|
end
|
16
16
|
|
17
|
-
it "should add
|
17
|
+
it "should add Colors" do
|
18
18
|
c1 = Paleta::Color.new(13, 57, 182)
|
19
19
|
c2 = Paleta::Color.new(94, 161, 235)
|
20
20
|
palette = Paleta::Palette.new(c1)
|
@@ -22,14 +22,39 @@ describe Paleta::Palette do
|
|
22
22
|
palette.include?(c2).should be_true
|
23
23
|
end
|
24
24
|
|
25
|
-
it "should
|
25
|
+
it "should add Colors with push" do
|
26
|
+
c1 = Paleta::Color.new(13, 57, 182)
|
27
|
+
c2 = Paleta::Color.new(94, 161, 235)
|
28
|
+
palette = Paleta::Palette.new(c1)
|
29
|
+
palette.push(c2)
|
30
|
+
palette[1].should == c2
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should remove the last Color with pop" do
|
34
|
+
c1 = Paleta::Color.new(13, 57, 182)
|
35
|
+
c2 = Paleta::Color.new(94, 161, 235)
|
36
|
+
palette = Paleta::Palette.new(c1, c2)
|
37
|
+
c = palette.pop()
|
38
|
+
c.should == c2
|
39
|
+
palette.include?(c2).should be_false
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should remove Colors by index" do
|
43
|
+
c1 = Paleta::Color.new(13, 57, 182)
|
44
|
+
c2 = Paleta::Color.new(94, 161, 235)
|
45
|
+
palette = Paleta::Palette.new(c1, c2)
|
46
|
+
palette.delete_at(0)
|
47
|
+
palette.include?(c1).should be_false
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should allow array-style accessing of Colors" do
|
26
51
|
c1 = Paleta::Color.new(13, 57, 182)
|
27
52
|
palette = Paleta::Palette.new(c1)
|
28
53
|
palette[0].should == c1
|
29
54
|
palette[1].should be_nil
|
30
55
|
end
|
31
56
|
|
32
|
-
it "should lighten each
|
57
|
+
it "should lighten each Color in a Palette by a percentage" do
|
33
58
|
c1 = Paleta::Color.new(13, 57, 182)
|
34
59
|
c2 = Paleta::Color.new(94, 161, 235)
|
35
60
|
palette = Paleta::Palette.new(c1, c2)
|
@@ -41,7 +66,7 @@ describe Paleta::Palette do
|
|
41
66
|
palette[1].lightness.should == lightness2 + percent
|
42
67
|
end
|
43
68
|
|
44
|
-
it "should darken each
|
69
|
+
it "should darken each Color in a Palette by a percentage" do
|
45
70
|
c1 = Paleta::Color.new(13, 57, 182)
|
46
71
|
c2 = Paleta::Color.new(94, 161, 235)
|
47
72
|
palette = Paleta::Palette.new(c1, c2)
|
@@ -53,7 +78,7 @@ describe Paleta::Palette do
|
|
53
78
|
palette[1].lightness.should == lightness2 - percent
|
54
79
|
end
|
55
80
|
|
56
|
-
it "should invert each
|
81
|
+
it "should invert each Color in a Palette" do
|
57
82
|
c1 = Paleta::Color.new(13, 57, 182)
|
58
83
|
c2 = Paleta::Color.new(94, 161, 235)
|
59
84
|
palette = Paleta::Palette.new(c1, c2)
|
@@ -98,7 +123,6 @@ describe Paleta::Palette do
|
|
98
123
|
p4 = Paleta::Palette.new(c5, c6)
|
99
124
|
p3.similarity(p4).should == 0
|
100
125
|
|
101
|
-
|
102
126
|
c7 = Paleta::Color.new(13, 57, 182)
|
103
127
|
c8 = Paleta::Color.new(237, 172, 33)
|
104
128
|
p5 = Paleta::Palette.new(c7, c8)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: paleta
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-01-
|
12
|
+
date: 2012-01-07 00:00:00.000000000Z
|
13
13
|
dependencies: []
|
14
14
|
description: A gem for working with color palettes
|
15
15
|
email:
|
@@ -23,7 +23,7 @@ files:
|
|
23
23
|
- LICENSE
|
24
24
|
- lib/paleta.rb
|
25
25
|
- lib/paleta/color.rb
|
26
|
-
- lib/paleta/
|
26
|
+
- lib/paleta/core_ext/math.rb
|
27
27
|
- lib/paleta/palette.rb
|
28
28
|
- lib/paleta/version.rb
|
29
29
|
- paleta.gemspec
|
@@ -1,39 +0,0 @@
|
|
1
|
-
class MultipleRegression
|
2
|
-
attr_accessor :slope, :offset
|
3
|
-
|
4
|
-
def initialize dr, dg, db
|
5
|
-
|
6
|
-
@size = dr.size
|
7
|
-
raise "arguments not same length!" unless @size == dg.size && @size == db.size
|
8
|
-
|
9
|
-
if @size == 1
|
10
|
-
@slope = { :r => dr[0], :g => dg[0], :b => db[0] }
|
11
|
-
@offset = { :r => 0, :g => 0, :b => 0 }
|
12
|
-
return
|
13
|
-
end
|
14
|
-
|
15
|
-
srr = sgg = sbb = srg = sbr = sgb = sr = sb = sg = 0
|
16
|
-
dr.zip(dg,db).each do |r,g,b|
|
17
|
-
srr += r ** 2
|
18
|
-
sgg += g ** 2
|
19
|
-
srg += r * g
|
20
|
-
sbr += b * r
|
21
|
-
sgb += g * b
|
22
|
-
sr += r
|
23
|
-
sb += g
|
24
|
-
sg += b
|
25
|
-
end
|
26
|
-
|
27
|
-
slope_rg = ( @size * srg - sr * sb ) / ( @size * srr - sr ** 2 ).to_f
|
28
|
-
slope_gb = ( @size * sgb - sb * sg ) / ( @size * sgg - sb ** 2 ).to_f
|
29
|
-
slope_br = ( @size * sgb - sb * sg ) / ( @size * sbb - sg ** 2 ).to_f
|
30
|
-
|
31
|
-
offset_rg = (sb - slope_rg * sr) / @size
|
32
|
-
offset_gb = (sg - slope_gb * sb) / @size
|
33
|
-
offset_br = (sr - slope_br * sg) / @size
|
34
|
-
|
35
|
-
@slope = { :r => slope_rg, :g => slope_gb, :b => slope_br }
|
36
|
-
@offset = { :r => offset_rg, :g => offset_gb, :b => offset_br }
|
37
|
-
return
|
38
|
-
end
|
39
|
-
end
|