paleta 0.0.2 → 0.0.3
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.
- 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
|