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 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(red = 0, green = 0, blue = 0)
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
- 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")
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
- sqrt(((@red - color.red) ** 2) + ((@green - color.green) ** 2) + ((@blue - color.blue) ** 2)) / sqrt(3 * (255 ** 2))
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
@@ -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
- r1 = self.fit
50
- r2 = palette.fit
63
+ r, a, b = [], [], []
64
+ (0..1).each { |i| a[i], b[i] = {}, {} }
51
65
 
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))
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
@@ -1,3 +1,3 @@
1
1
  module Paleta
2
- VERSION = '0.0.2'
2
+ VERSION = '0.0.3'
3
3
  end
data/lib/paleta.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  require 'paleta/version'
2
2
  require 'paleta/color'
3
- require 'paleta/multiple_regression'
4
3
  require 'paleta/palette'
5
4
 
6
5
  module Paleta
data/readme.markdown CHANGED
@@ -22,7 +22,16 @@ and run
22
22
 
23
23
  ### Color
24
24
 
25
- # create a color with RGB components
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
- palette = Paleta::Palette.new(c1, c2)
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
- color1 = Paleta::Color.new(13, 57, 182)
75
- color2 = Paleta::Color.new(237, 172, 33)
76
- palette1 = Paleta::Palette.new(color1, color2)
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
- color3 = Paleta::Color.new(13, 57, 182)
79
- color4 = Paleta::Color.new(94, 161, 235)
80
- palette2 = Paleta::Palette.new(color3, color4)
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
 
@@ -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
@@ -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 colors to an initialized palette" do
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 allow array-style accessing of colors" do
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 color in a palette by a percentage" do
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 color in a palette by a percentage" do
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 color in a palette" do
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.2
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-06 00:00:00.000000000Z
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/multiple_regression.rb
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