paleta 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,4 +1,7 @@
1
1
  .DS_Store
2
2
  .rspec
3
3
  Gemfile.lock
4
- Guardfile
4
+ Guardfile
5
+ *.gem
6
+ .bundle
7
+ tmp
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Jordan Stephens <http://jordanstephens.net>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/lib/paleta.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'paleta/version'
2
2
  require 'paleta/color'
3
+ require 'paleta/multiple_regression'
3
4
  require 'paleta/palette'
4
5
 
5
6
  module Paleta
data/lib/paleta/color.rb CHANGED
@@ -1,7 +1,8 @@
1
1
  module Paleta
2
2
  class Color
3
+ include Math
3
4
 
4
- attr_reader :red, :green, :blue, :hue, :saturation, :lightness
5
+ attr_reader :red, :green, :blue, :hue, :saturation, :lightness, :hex
5
6
 
6
7
  def initialize(red = 0, green = 0, blue = 0)
7
8
  self.red = red
@@ -12,31 +13,77 @@ module Paleta
12
13
  def red=(val)
13
14
  @red = range_validator(val, 0..255)
14
15
  update_hsl
16
+ update_hex
15
17
  end
16
18
 
17
19
  def green=(val)
18
20
  @green = range_validator(val, 0..255)
19
21
  update_hsl
22
+ update_hex
20
23
  end
21
24
 
22
25
  def blue=(val)
23
26
  @blue = range_validator(val, 0..255)
24
27
  update_hsl
28
+ update_hex
25
29
  end
26
30
 
27
31
  def lightness=(val)
28
32
  @lightness = range_validator(val, 0..100)
29
33
  update_rgb
34
+ update_hex
30
35
  end
31
36
 
32
37
  def saturation=(val)
33
38
  @saturation = range_validator(val, 0..100)
34
39
  update_rgb
40
+ update_hex
35
41
  end
36
42
 
37
43
  def hue=(val)
38
44
  @hue = range_validator(val, 0..360)
39
45
  update_rgb
46
+ update_hex
47
+ end
48
+
49
+ def hex=(val)
50
+ if val.length != 6 ||
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")
54
+ raise(ArgumentError, "Invalid Hex String")
55
+ end
56
+ @hex = val.upcase
57
+ @red = val[0..1].hex
58
+ @green = val[2..3].hex
59
+ @blue = val[4..5].hex
60
+ update_hsl
61
+ end
62
+
63
+ def lighten!(percent = 5)
64
+ @lightness += percent
65
+ @lightness = 100 if @lightness > 100
66
+ update_rgb
67
+ update_hex
68
+ end
69
+
70
+ def darken!(percent = 5)
71
+ @lightness -= percent
72
+ @lightness = 0 if @lightness < 0
73
+ update_rgb
74
+ update_hex
75
+ end
76
+
77
+ def invert!
78
+ @red = 255 - @red
79
+ @green = 255 - @green
80
+ @blue = 255 - @blue
81
+ update_hsl
82
+ update_hex
83
+ end
84
+
85
+ def similarity(color)
86
+ sqrt(((@red - color.red) ** 2) + ((@green - color.green) ** 2) + ((@blue - color.blue) ** 2)) / sqrt(3 * (255 ** 2))
40
87
  end
41
88
 
42
89
  private
@@ -50,22 +97,21 @@ module Paleta
50
97
  max = [r, g, b].max
51
98
  delta = max - min
52
99
 
53
- @hue = 0
54
- @saturation = 0
55
- @lightness = (max + min) / 2.0
100
+ h = s = 0
101
+ l = (max + min) / 2.0
56
102
 
57
103
  if delta != 0
58
- @saturation = (@lightness < 0.5) ? delta / (max + min) : delta / (2.0 - max - min)
104
+ s = (l < 0.5) ? delta / (max + min) : delta / (2.0 - max - min)
59
105
  case max
60
- when r; @hue = (g - b) / delta
61
- when g; @hue = 2 + (b - r) / delta
62
- when b; @hue = 4 + (r - g) / delta
106
+ when r; h = (g - b) / delta
107
+ when g; h = 2 + (b - r) / delta
108
+ when b; h = 4 + (r - g) / delta
63
109
  end
64
110
  end
65
- @hue *= 60
111
+ @hue = h * 60
66
112
  @hue += 360 if @hue < 0
67
- @saturation *= 100
68
- @lightness *= 100
113
+ @saturation = s * 100
114
+ @lightness = l * 100
69
115
  end
70
116
 
71
117
  def update_rgb
@@ -75,32 +121,34 @@ module Paleta
75
121
  l = @lightness / 100.0
76
122
 
77
123
  if s == 0
78
- r = l * 255
79
- g = l * 255
80
- b = l * 255
124
+ r = g = b = l * 255
81
125
  else
82
- th = h / 6.0
83
- if l < 0.5
84
- t2 = l * (s + 1)
85
- else
86
- t2 = (l + s) - (l * s)
87
- end
126
+ h /= 6.0
127
+ t2 = l < 0.5 ? l * (s + 1) : (l + s) - (l * s)
88
128
  t1 = 2 * l - t2
89
129
 
90
- tr = th + (1.0 / 3.0)
91
- tg = th
92
- tb = th - (1.0 / 3.0)
130
+ r = h + (1.0 / 3.0)
131
+ g = h
132
+ b = h - (1.0 / 3.0)
93
133
 
94
- tr = hue_calc(tr, t1, t2)
95
- tg = hue_calc(tg, t1, t2)
96
- tb = hue_calc(tb, t1, t2)
134
+ r = hue_calc(r, t1, t2)
135
+ g = hue_calc(g, t1, t2)
136
+ b = hue_calc(b, t1, t2)
97
137
 
98
- @red = tr * 255.0
99
- @green = tg * 255.0
100
- @blue = tb * 255.0
138
+ @red = r * 255.0
139
+ @green = g * 255.0
140
+ @blue = b * 255.0
101
141
  end
102
142
  end
103
143
 
144
+ def update_hex
145
+ r = @red.to_s(16) rescue "00"
146
+ g = @green.to_s(16) rescue "00"
147
+ b = @blue.to_s(16) rescue "00"
148
+ [r, g, b].each { |c| c = "0#{c}" if c.length < 2 }
149
+ @hex = "#{r}#{g}#{b}".upcase
150
+ end
151
+
104
152
  def hue_calc(value, t1, t2)
105
153
  value += 1 if value < 0
106
154
  value -= 1 if value > 1
@@ -0,0 +1,39 @@
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
@@ -1,5 +1,6 @@
1
1
  module Paleta
2
2
  class Palette
3
+ include Math
3
4
 
4
5
  attr_accessor :colors
5
6
 
@@ -18,8 +19,62 @@ module Paleta
18
19
  @colors[i]
19
20
  end
20
21
 
22
+ def size
23
+ @colors.size
24
+ end
25
+
21
26
  def include?(color)
22
27
  @colors.include?(color)
23
28
  end
29
+
30
+ def lighten!(percent = 5)
31
+ @colors.each do |color|
32
+ color.lighten!(percent)
33
+ end
34
+ end
35
+
36
+ def darken!(percent = 5)
37
+ @colors.each do |color|
38
+ color.darken!(percent)
39
+ end
40
+ end
41
+
42
+ def invert!
43
+ @colors.each do |color|
44
+ color.invert!
45
+ end
46
+ end
47
+
48
+ def similarity(palette)
49
+ r1 = self.fit
50
+ r2 = palette.fit
51
+
52
+ a1, a2, b1, b2 = {}, {}, {}, {}
53
+ a1[:r] = 0 * r1.slope[:r] + r1.offset[:r]
54
+ a1[:g] = 0 * r1.slope[:g] + r1.offset[:g]
55
+ a1[:b] = 0 * r1.slope[:b] + r1.offset[:b]
56
+ b1[:r] = 255 * r1.slope[:r] + r1.offset[:r]
57
+ b1[:g] = 255 * r1.slope[:g] + r1.offset[:g]
58
+ b1[:b] = 255 * r1.slope[:b] + r1.offset[:b]
59
+ a2[:r] = 0 * r2.slope[:r] + r2.offset[:r]
60
+ a2[:g] = 0 * r2.slope[:g] + r2.offset[:g]
61
+ a2[:b] = 0 * r2.slope[:b] + r2.offset[:b]
62
+ b2[:r] = 255 * r2.slope[:r] + r2.offset[:r]
63
+ b2[:g] = 255 * r2.slope[:g] + r2.offset[:g]
64
+ b2[:b] = 255 * r2.slope[:b] + r2.offset[:b]
65
+
66
+ d1 = sqrt(((a1[:r] - a2[:r]) ** 2) + ((a1[:g] - a2[:g]) ** 2) + ((a1[:b] - a2[:b]) ** 2)) / sqrt(3 * (65025 ** 2))
67
+ d2 = sqrt(((b1[:r] - b2[:r]) ** 2) + ((b1[:g] - b2[:g]) ** 2) + ((b1[:b] - b2[:b]) ** 2)) / sqrt(3 * (65025 ** 2))
68
+
69
+ d1 + d2
70
+ end
71
+
72
+ def fit
73
+ # create a 3xn matrix where n = @colors.size to represent the set of colors
74
+ reds = @colors.map { |c| c.red }
75
+ greens = @colors.map { |c| c.green }
76
+ blues = @colors.map { |c| c.blue }
77
+ MultipleRegression.new(reds, greens, blues)
78
+ end
24
79
  end
25
80
  end
@@ -1,3 +1,3 @@
1
1
  module Paleta
2
- VERSION = '0.0.1'
2
+ VERSION = '0.0.2'
3
3
  end
data/paleta.gemspec CHANGED
@@ -4,8 +4,8 @@ require File.expand_path('../lib/paleta/version', __FILE__)
4
4
  Gem::Specification.new do |gem|
5
5
  gem.authors = ['Jordan Stephens']
6
6
  gem.email = ['iam@jordanstephens.net']
7
- gem.description = 'color palette gem'
8
- gem.summary = 'color palette gem'
7
+ gem.description = 'A gem for working with color palettes'
8
+ gem.summary = 'A little library for creating, manipulating and comparing colors and color palettes'
9
9
  gem.homepage = 'http://jordanstephens.net'
10
10
 
11
11
  gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
data/readme.markdown ADDED
@@ -0,0 +1,86 @@
1
+ # Paleta
2
+
3
+ A gem for working with color palettes
4
+
5
+ ## Installation
6
+
7
+ To install, run
8
+
9
+ $ gem install paleta
10
+
11
+ Or, add this to your application's Gemfile
12
+
13
+ ``` ruby
14
+ gem 'paleta'
15
+ ```
16
+
17
+ and run
18
+
19
+ $ bundle
20
+
21
+ ## Usage
22
+
23
+ ### Color
24
+
25
+ # create a color with RGB components
26
+ color = Paleta::Color.new(94, 161, 235)
27
+
28
+ # access component values
29
+ color.red # => 94
30
+ color.green # => 161
31
+ color.blue # => 235
32
+
33
+ # HSL components are maintained too!
34
+ color.hue # => 211.48936170212767
35
+ color.saturation # => 77.90055248618782
36
+ color.lightness # => 64.50980392156862
37
+
38
+ # a HEX value is also maintained for each Color
39
+ color.hex # => "5EA1EB"
40
+
41
+ # lighten by a percentage
42
+ color.lighten!(10)
43
+
44
+ # darken by a percentage
45
+ color.darken!(10)
46
+
47
+ # invert a color
48
+ color.invert!
49
+
50
+ # calculate similarity between Colors
51
+ # Color#similarity calculates the similarity between two colors and returns a
52
+ # value in 0..1, with 0 being identical and 1 being as dissimilar as possible
53
+ color2 = Paleta::Color.new(237, 172, 33)
54
+ color.similarity(color2) # => 0.5609077061558945
55
+
56
+ ### Palette
57
+
58
+ # add Colors to a Palette
59
+ color1 = Paleta::Color.new(13, 57, 182)
60
+ color2 = Paleta::Color.new(94, 161, 235)
61
+ palette = Paleta::Palette.new(c1, c2)
62
+
63
+ # retreive a Color from a Palette
64
+ palette[1] # => color2
65
+
66
+ # lighten and darken an entire Palette by a percentage
67
+ palette.lighten!(15)
68
+ palette.darken!(20)
69
+
70
+ # invert each color in a Palette
71
+ palette.invert!
72
+
73
+ # calculate similarity of two Palettes
74
+ color1 = Paleta::Color.new(13, 57, 182)
75
+ color2 = Paleta::Color.new(237, 172, 33)
76
+ palette1 = Paleta::Palette.new(color1, color2)
77
+
78
+ color3 = Paleta::Color.new(13, 57, 182)
79
+ color4 = Paleta::Color.new(94, 161, 235)
80
+ palette2 = Paleta::Palette.new(color3, color4)
81
+
82
+ palette1.similarity(palette2) # => 0.0046992695975874915
83
+
84
+ See the [documentation](http://rubydoc.info/gems/paleta/ "Documentation").
85
+
86
+
@@ -56,4 +56,74 @@ describe Paleta::Color do
56
56
  color.green.to_i.should == 195
57
57
  color.blue.to_i.should == 178
58
58
  end
59
+
60
+ it "should lighten by a percentage, " do
61
+ color = Paleta::Color.new(94, 161, 235)
62
+ lightness = color.lightness
63
+ color.lighten!
64
+ color.lightness.should == lightness + 5
65
+ lightness = color.lightness
66
+ color.lighten!(20)
67
+ color.lightness.should == lightness + 20
68
+ end
69
+
70
+ it "should quietly maintain a maximum of 100 when lightening" do
71
+ color = Paleta::Color.new(94, 161, 235)
72
+ color.lighten!(300)
73
+ color.lightness.should == 100
74
+ end
75
+
76
+ it "should darken by a percentage" do
77
+ color = Paleta::Color.new(94, 161, 235)
78
+ lightness = color.lightness
79
+ color.darken!
80
+ color.lightness.should == lightness - 5
81
+ lightness = color.lightness
82
+ color.darken!(20)
83
+ color.lightness.should == lightness - 20
84
+ end
85
+
86
+ it "should quietly maintain a minimum of 0 when darkening" do
87
+ color = Paleta::Color.new(94, 161, 235)
88
+ color.darken!(300)
89
+ color.lightness.should == 0
90
+ end
91
+
92
+ it "should invert" do
93
+ color = Paleta::Color.new(94, 161, 235)
94
+ color.invert!
95
+ color.red.should == 161
96
+ color.green.should == 94
97
+ color.blue.should == 20
98
+ end
99
+
100
+ it "should calculate its similarity to another Color" do
101
+ color1 = Paleta::Color.new(94, 161, 235)
102
+ color2 = Paleta::Color.new(237, 172, 33)
103
+ color1.similarity(color2).round(5).should == 0.56091
104
+
105
+ color1 = Paleta::Color.new(237, 172, 33)
106
+ color2 = Paleta::Color.new(237, 172, 33)
107
+ color1.similarity(color2).should == 0
108
+
109
+ color1 = Paleta::Color.new(0, 0, 0)
110
+ color2 = Paleta::Color.new(255, 255, 255)
111
+ color1.similarity(color2).should == 1
112
+ end
113
+
114
+ it "should maintain its HEX value" do
115
+ color = Paleta::Color.new(94, 161, 235)
116
+ color.hex.should == "5EA1EB"
117
+ end
118
+
119
+ it "should update its HSB and RGB components when its HEX value is updated" do
120
+ color = Paleta::Color.new
121
+ color.hex = "ffffff"
122
+ color.red.should == 255
123
+ color.green.should == 255
124
+ color.blue.should == 255
125
+ color.hue.should == 0
126
+ color.saturation.should == 0
127
+ color.lightness.should == 100
128
+ end
59
129
  end
@@ -28,4 +28,84 @@ describe Paleta::Palette do
28
28
  palette[0].should == c1
29
29
  palette[1].should be_nil
30
30
  end
31
+
32
+ it "should lighten each color in a palette by a percentage" do
33
+ c1 = Paleta::Color.new(13, 57, 182)
34
+ c2 = Paleta::Color.new(94, 161, 235)
35
+ palette = Paleta::Palette.new(c1, c2)
36
+ lightness1 = c1.lightness
37
+ lightness2 = c2.lightness
38
+ percent = 20
39
+ palette.lighten!(percent)
40
+ palette[0].lightness.should == lightness1 + percent
41
+ palette[1].lightness.should == lightness2 + percent
42
+ end
43
+
44
+ it "should darken each color in a palette by a percentage" do
45
+ c1 = Paleta::Color.new(13, 57, 182)
46
+ c2 = Paleta::Color.new(94, 161, 235)
47
+ palette = Paleta::Palette.new(c1, c2)
48
+ lightness1 = c1.lightness
49
+ lightness2 = c2.lightness
50
+ percent = 20
51
+ palette.darken!(percent)
52
+ palette[0].lightness.should == lightness1 - percent
53
+ palette[1].lightness.should == lightness2 - percent
54
+ end
55
+
56
+ it "should invert each color in a palette" do
57
+ c1 = Paleta::Color.new(13, 57, 182)
58
+ c2 = Paleta::Color.new(94, 161, 235)
59
+ palette = Paleta::Palette.new(c1, c2)
60
+ palette.invert!
61
+ palette[0].red.should == 242
62
+ palette[0].green.should == 198
63
+ palette[0].blue.should == 73
64
+ palette[1].red.should == 161
65
+ palette[1].green.should == 94
66
+ palette[1].blue.should == 20
67
+ end
68
+
69
+ it "should calculate a multiple regression over each Color in the Palette in RGB space" do
70
+ c1 = Paleta::Color.new(13, 57, 182)
71
+ c2 = Paleta::Color.new(94, 161, 235)
72
+ c3 = Paleta::Color.new(237, 172, 33)
73
+ palette = Paleta::Palette.new(c1, c2, c3)
74
+ r = palette.fit
75
+ r.slope[:r].should == 0.4632575855725132
76
+ r.slope[:g].should == -0.5730072013906133
77
+ r.slope[:b].should == 0.06837037037037037
78
+ r.offset[:r].should == 76.87979685435182
79
+ r.offset[:g].should == 224.49093618077973
80
+ r.offset[:b].should == 104.41111111111111
81
+ end
82
+
83
+ it "should calculate its similarity to another Palette" do
84
+ c1 = Paleta::Color.new(0, 0, 0)
85
+ p1 = Paleta::Palette.new(c1)
86
+
87
+ c2 = Paleta::Color.new(255, 255, 255)
88
+ p2 = Paleta::Palette.new(c2)
89
+
90
+ p1.similarity(p2).should == 1
91
+
92
+ c3 = Paleta::Color.new(0, 0, 0)
93
+ c4 = Paleta::Color.new(255, 255, 255)
94
+ p3 = Paleta::Palette.new(c3, c4)
95
+
96
+ c5 = Paleta::Color.new(0, 0, 0)
97
+ c6 = Paleta::Color.new(255, 255, 255)
98
+ p4 = Paleta::Palette.new(c5, c6)
99
+ p3.similarity(p4).should == 0
100
+
101
+
102
+ c7 = Paleta::Color.new(13, 57, 182)
103
+ c8 = Paleta::Color.new(237, 172, 33)
104
+ p5 = Paleta::Palette.new(c7, c8)
105
+
106
+ c9 = Paleta::Color.new(13, 57, 182)
107
+ c10 = Paleta::Color.new(94, 161, 235)
108
+ p6 = Paleta::Palette.new(c9, c10)
109
+ p5.similarity(p6).round(5).should == 0.0047
110
+ end
31
111
  end
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.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,9 +9,9 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-01-02 00:00:00.000000000Z
12
+ date: 2012-01-06 00:00:00.000000000Z
13
13
  dependencies: []
14
- description: color palette gem
14
+ description: A gem for working with color palettes
15
15
  email:
16
16
  - iam@jordanstephens.net
17
17
  executables: []
@@ -20,11 +20,14 @@ extra_rdoc_files: []
20
20
  files:
21
21
  - .gitignore
22
22
  - Gemfile
23
+ - LICENSE
23
24
  - lib/paleta.rb
24
25
  - lib/paleta/color.rb
26
+ - lib/paleta/multiple_regression.rb
25
27
  - lib/paleta/palette.rb
26
28
  - lib/paleta/version.rb
27
29
  - paleta.gemspec
30
+ - readme.markdown
28
31
  - spec/models/color_spec.rb
29
32
  - spec/models/palette_spec.rb
30
33
  - spec/spec_helper.rb
@@ -51,7 +54,8 @@ rubyforge_project:
51
54
  rubygems_version: 1.8.10
52
55
  signing_key:
53
56
  specification_version: 3
54
- summary: color palette gem
57
+ summary: A little library for creating, manipulating and comparing colors and color
58
+ palettes
55
59
  test_files:
56
60
  - spec/models/color_spec.rb
57
61
  - spec/models/palette_spec.rb