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 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