shade 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7e76ed33f294a3425fec4200212fab8ac0779346
4
+ data.tar.gz: 3300038187fe56a73f705cd3fd42a2ecb9de7d84
5
+ SHA512:
6
+ metadata.gz: e30ae1f5c6a901f3733eac133b9f79f62977897fb48b2e0c0beaa57c8eeb8513378cfb53f25426821e63163ce35b85c4cd3bb1831f83cefed65537fa074d19cb
7
+ data.tar.gz: bf5f650f46bc8fa9aaf25494f9b02d5692c5ec37414597543fc58eccb70d68cc6451fb5e00e106e11dfda882d7c28f552d7cc72b57e8fbd80043a24a69dce433
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.idea/
3
+ /.yardoc
4
+ /Gemfile.lock
5
+ /_yardoc/
6
+ /coverage/
7
+ /doc/
8
+ /pkg/
9
+ /spec/reports/
10
+ /tmp/
11
+ *.bundle
12
+ *.so
13
+ *.o
14
+ *.a
15
+ mkmf.log
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+
3
+ rvm:
4
+ - 2.1.2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in shade.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Matthew McEachen
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,55 @@
1
+ # Shade
2
+
3
+ Rubygem to find the closest color from a given palette.
4
+
5
+ [![Build Status](https://api.travis-ci.org/mceachen/shade.png?branch=master)](https://travis-ci.org/mceachen/shade)
6
+ [![Gem Version](https://badge.fury.io/rb/shade.png)](http://rubygems.org/gems/shade)
7
+ [![Code Climate](https://codeclimate.com/github/mceachen/shade.png)](https://codeclimate.com/github/mceachen/shade)
8
+ [![Dependency Status](https://gemnasium.com/mceachen/shade.png)](https://gemnasium.com/mceachen/shade)
9
+
10
+ This was created to help migrate Twitter's Advertising webapp from more than a thousand
11
+ different colors into a small well-considered palette of colors.
12
+
13
+ Both the [CIE76 color difference algorithm](http://en.wikipedia.org/wiki/Color_difference#CIE76), via
14
+ `Shade::Palette.nearest_value`, and the
15
+ [CIE94 color difference algorithm](http://en.wikipedia.org/wiki/Color_difference#CIE94), via
16
+ `Shade::Palette.nearest_value_cie94` implementations are available.
17
+
18
+ The CIE94 implementation is slower, but may have better results, as saturation perception is better
19
+ accounted for in that algorithm.
20
+
21
+ ## Installation
22
+
23
+ Add this line to your application's Gemfile:
24
+
25
+ ```ruby
26
+ gem 'shade'
27
+ ```
28
+
29
+ And then execute:
30
+
31
+ $ bundle
32
+
33
+ Or install it yourself as:
34
+
35
+ $ gem install shade
36
+
37
+ ## Usage
38
+
39
+ ```ruby
40
+ p = Shade::Palette.new do |p|
41
+ p.add('#663399', 'deepPurple')
42
+ p.add('#5BA636', 'darkGreen')
43
+ end
44
+
45
+ p.nearest_value('green')
46
+ => #<struct Shade::Palette::Value name="#5BA636", css_color="darkGreen">
47
+ ```
48
+
49
+ ## Contributing
50
+
51
+ 1. Fork it ( https://github.com/mceachen/shade/fork )
52
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
53
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
54
+ 4. Push to the branch (`git push origin my-new-feature`)
55
+ 5. Create a new Pull Request
@@ -0,0 +1,17 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ require 'yard'
4
+ YARD::Rake::YardocTask.new do |t|
5
+ t.files = ['lib/**/*.rb', 'README.md']
6
+ end
7
+
8
+ require 'rake/testtask'
9
+
10
+ Rake::TestTask.new do |t|
11
+ t.libs.push 'lib'
12
+ t.libs.push 'test'
13
+ t.pattern = 'test/**/*_test.rb'
14
+ t.verbose = true
15
+ end
16
+
17
+ task :default => :test
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'shade'
5
+ require 'shade/cli'
6
+
7
+ Shade::CLI.new(ARGV).convert_files
@@ -0,0 +1,5 @@
1
+ require 'shade/version'
2
+ require 'shade/palette'
3
+
4
+ module Shade
5
+ end
@@ -0,0 +1,76 @@
1
+ require 'optparse'
2
+
3
+ module Shade
4
+ class CLI
5
+ def initialize(argv)
6
+ @verbose = false
7
+ @palette_files = []
8
+ @inputs = option_parser.parse!(argv)
9
+
10
+ if palette.empty?
11
+ puts 'No colors are in the target palette. Please provide at least one valid --palette file.'
12
+ puts option_parser
13
+ exit 1
14
+ end
15
+ end
16
+
17
+ def convert_files(files = @inputs)
18
+ files.each do |file|
19
+ File.foreach(file) do |line|
20
+ m = COLOR_VAR_RE.match(line)
21
+ if m
22
+ var_name, css_color = m.captures
23
+ good = palette.nearest_value(css_color)
24
+ puts "Replace all references of @#{var_name} with @#{good.name} because #{good.css_color} is closest to #{css_color}."
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ # This assumes colors use a variable name, something like:
33
+ # @deepOrange: #ED4E00;
34
+
35
+ COLOR_VAR_RE = /@(\w+):\W*(#\h{3,8}|\w+);/i
36
+
37
+ def palette
38
+ @palette ||= read_palette_files(@palette_files)
39
+ end
40
+
41
+ def read_palette_files(palette_files)
42
+ p = Shade::Palette.new
43
+ palette_files.each do |ea|
44
+ File.foreach(ea) do |line|
45
+ m = COLOR_VAR_RE.match(line)
46
+ if m
47
+ var_name, css_color = m.captures
48
+ p.add(css_color, var_name)
49
+ end
50
+ end
51
+ end
52
+ p
53
+ end
54
+
55
+ def option_parser
56
+ @option_parser ||= OptionParser.new do |opts|
57
+ opts.banner = 'Usage: shade --palette=[LESS-formatted file] [file to translate] ...'
58
+ opts.on('-v', '--[no-]verbose', 'Run verbosely') do |v|
59
+ @verbose = v
60
+ end
61
+ opts.on('-p', '--palette pallete.less',
62
+ 'Read colors from pallete.less as the target palette') do |palette|
63
+ (@palette_files ||= []) << palette
64
+ end
65
+ opts.on_tail('-h', '--help', 'Show this message') do
66
+ puts opts
67
+ exit
68
+ end
69
+ opts.on_tail('--version', 'Show version') do
70
+ puts Shade::VERSION
71
+ exit
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,66 @@
1
+ require 'color'
2
+ require 'geokdtree'
3
+
4
+ module Shade
5
+ class Palette
6
+ def initialize
7
+ @tree = Geokdtree::Tree.new(3)
8
+ @empty = true
9
+ yield self if block_given?
10
+ end
11
+
12
+ def add(css_color, name = nil)
13
+ @empty = false
14
+ value = Value.new(css_color, name)
15
+ @tree.insert(value.coords, value)
16
+ end
17
+
18
+ def empty?
19
+ @empty
20
+ end
21
+
22
+ # This implements the CIE76 color difference algorithm.
23
+ # See http://en.wikipedia.org/wiki/Color_difference#CIE76
24
+ def nearest_value(css_color)
25
+ value = Value.new(css_color)
26
+ result = @tree.nearest(value.coords)
27
+ result.data if result
28
+ end
29
+
30
+ # This implements the CIE94 color difference algorithm.
31
+ # See http://en.wikipedia.org/wiki/Color_difference#CIE94
32
+ # threshold_distance defaults to 40. Perceptible differences are > 2.3
33
+ def nearest_value_cie94(css_color, threshold_distance = 100)
34
+ value = Value.new(css_color)
35
+ result_points = @tree.nearest_range(value.coords, threshold_distance)
36
+ colors_to_values = Hash[result_points.map { |ea| [ea.data.color, ea.data] }]
37
+ best_match_color = value.color.closest_match(colors_to_values.keys)
38
+ colors_to_values[best_match_color]
39
+ end
40
+
41
+ class Value
42
+ attr_reader :css_color, :name
43
+
44
+ def initialize(css_color, name = nil)
45
+ @css_color = css_color
46
+ @name = name
47
+ end
48
+
49
+ def color
50
+ @color ||= Color::RGB.by_css(css_color)
51
+ end
52
+
53
+ def lab
54
+ @lab ||= color.to_lab
55
+ end
56
+
57
+ def coords
58
+ @coords ||= [lab[:L], lab[:a], lab[:b]]
59
+ end
60
+
61
+ def ==(e)
62
+ e.css_color == css_color && e.name == name
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,3 @@
1
+ module Shade
2
+ VERSION = Gem::Version.new('0.0.1')
3
+ end
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'shade/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'shade'
8
+ spec.version = Shade::VERSION
9
+ spec.authors = ['Matthew McEachen']
10
+ spec.email = ['matthew+github@mceachen.org']
11
+ spec.summary = %q{Coalesce colors to a given palette}
12
+ spec.description = %q{Using CIE L*a*b* and kdtrees, take a color (from LESS, SCSS, or other
13
+ inputs) and find the nearest shade of color from a given palette}
14
+ spec.homepage = 'https://github.com/mceachen/shade'
15
+ spec.license = 'MIT'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0")
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
+ spec.require_paths = ['lib']
21
+
22
+ spec.add_runtime_dependency 'geokdtree'
23
+ spec.add_runtime_dependency 'color'
24
+ spec.add_development_dependency 'bundler'
25
+ spec.add_development_dependency 'rake'
26
+ spec.add_development_dependency 'yard'
27
+ spec.add_development_dependency 'appraisal'
28
+ spec.add_development_dependency 'minitest'
29
+ spec.add_development_dependency 'minitest-great_expectations'
30
+ spec.add_development_dependency 'minitest-reporters'
31
+ end
@@ -0,0 +1,10 @@
1
+ begin
2
+ require 'minitest'
3
+ rescue LoadError
4
+ puts 'Failed to load the minitest gem; built-in version will be used.'
5
+ end
6
+ require 'minitest/autorun'
7
+ require 'minitest/great_expectations'
8
+ require 'minitest/reporters'
9
+ require 'shade'
10
+ Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new
@@ -0,0 +1,52 @@
1
+ require 'minitest_helper'
2
+
3
+ describe Shade::Palette do
4
+ describe 'small palette' do
5
+ let(:colors) { %w(purple orange olive crimson darkcyan indianred).shuffle }
6
+ let(:subject) { Shade::Palette.new { |p| colors.each { |ea| p.add(ea, ea.upcase) } } }
7
+ let(:purple) {Shade::Palette::Value.new('purple', 'PURPLE')}
8
+ let(:orange) {Shade::Palette::Value.new('orange', 'ORANGE')}
9
+ let(:crimson) {Shade::Palette::Value.new('crimson', 'CRIMSON')}
10
+ let(:darkcyan) {Shade::Palette::Value.new('darkcyan', 'DARKCYAN')}
11
+ let(:indianred) {Shade::Palette::Value.new('indianred', 'INDIANRED')}
12
+ it 'finds exact match colors' do
13
+ subject.nearest_value('purple').must_equal Shade::Palette::Value.new('purple', 'PURPLE')
14
+ end
15
+ it 'finds nearest by named color' do
16
+ subject.nearest_value('MediumPurple').must_equal purple
17
+ subject.nearest_value('Orchid').must_equal purple
18
+ subject.nearest_value('FireBrick').must_equal crimson
19
+ subject.nearest_value('SandyBrown').must_equal orange
20
+ subject.nearest_value_cie94('SandyBrown').must_equal orange
21
+ subject.nearest_value('MediumSeaGreen').must_equal darkcyan
22
+ subject.nearest_value_cie94('MediumSeaGreen').must_equal darkcyan
23
+ subject.nearest_value('LightCoral').must_equal indianred
24
+ subject.nearest_value_cie94('LightCoral').must_equal indianred
25
+ end
26
+ end
27
+ describe 'large palette' do
28
+ let(:subject) do
29
+ Shade::Palette.new do |p|
30
+ Color::RGB.send(:__by_name).keys.each do |color|
31
+ p.add(color)
32
+ end
33
+ end
34
+ end
35
+ it 'finds the best white' do
36
+ subject.nearest_value("#fff").must_equal Shade::Palette::Value.new('white')
37
+ end
38
+ it 'finds the nearest turquoise' do
39
+ subject.nearest_value("#00B7AF").must_equal Shade::Palette::Value.new('lightseagreen')
40
+ end
41
+ it 'finds the nearest ferrari red' do
42
+ subject.nearest_value("#FF2800").must_equal Shade::Palette::Value.new('red')
43
+ end
44
+ end
45
+ describe 'Fuschias' do
46
+ let(:subject) { Shade::Palette.new { |p| %w(#FF367E #E03894 #B6388B #CE3E7C).each { |ea| p.add(ea) } } }
47
+ it 'finds the most similar fuschia' do
48
+ subject.nearest_value('#E14BAC').must_equal Shade::Palette::Value.new('#E03894')
49
+ subject.nearest_value_cie94('#E14BAC').must_equal Shade::Palette::Value.new('#E03894')
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,32 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>Test</title>
6
+ <style>
7
+ p {padding:1em; font-weight: bold; color: white}
8
+ </style>
9
+ </head>
10
+ <body>
11
+ <h2>Small palette</h2>
12
+ <p style="background-color:olive">olive</p>
13
+ <p style="background-color:mediumseagreen">mediumseagreen</p>
14
+ <p style="background-color:darkcyan">darkcyan</p>
15
+ <p style="background-color:purple">purple</p>
16
+ <p style="background-color:mediumpurple">mediumpurple</p>
17
+ <p style="background-color:orchid">orchid</p>
18
+ <p style="background-color:crimson">crimson</p>
19
+ <p style="background-color:firebrick">firebrick</p>
20
+ <p style="background-color:indianred">indianred</p>
21
+ <p style="background-color:lightcoral">lightcoral</p>
22
+ <p style="background-color:sandybrown">sandybrown</p>
23
+ <p style="background-color:orange">orange</p>
24
+
25
+ <h2>Fuschia</h2>
26
+ <p style="background-color:#FF367E">#FF367E</p>
27
+ <p style="background-color:#E14BAC">#E14BAC</p>
28
+ <p style="background-color:#E03894">#E03894</p>
29
+ <p style="background-color:#B6388B">#B6388B</p>
30
+ <p style="background-color:#CE3E7C">#CE3E7C</p>
31
+ </body>
32
+ </html>
metadata ADDED
@@ -0,0 +1,192 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: shade
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Matthew McEachen
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-07-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: geokdtree
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: color
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: yard
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: appraisal
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: minitest
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: minitest-great_expectations
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: minitest-reporters
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ description: |-
140
+ Using CIE L*a*b* and kdtrees, take a color (from LESS, SCSS, or other
141
+ inputs) and find the nearest shade of color from a given palette
142
+ email:
143
+ - matthew+github@mceachen.org
144
+ executables:
145
+ - shade
146
+ extensions: []
147
+ extra_rdoc_files: []
148
+ files:
149
+ - ".gitignore"
150
+ - ".travis.yml"
151
+ - Gemfile
152
+ - LICENSE.txt
153
+ - README.md
154
+ - Rakefile
155
+ - bin/shade
156
+ - lib/shade.rb
157
+ - lib/shade/cli.rb
158
+ - lib/shade/palette.rb
159
+ - lib/shade/version.rb
160
+ - shade.gemspec
161
+ - test/minitest_helper.rb
162
+ - test/palette_test.rb
163
+ - test/test.html
164
+ homepage: https://github.com/mceachen/shade
165
+ licenses:
166
+ - MIT
167
+ metadata: {}
168
+ post_install_message:
169
+ rdoc_options: []
170
+ require_paths:
171
+ - lib
172
+ required_ruby_version: !ruby/object:Gem::Requirement
173
+ requirements:
174
+ - - ">="
175
+ - !ruby/object:Gem::Version
176
+ version: '0'
177
+ required_rubygems_version: !ruby/object:Gem::Requirement
178
+ requirements:
179
+ - - ">="
180
+ - !ruby/object:Gem::Version
181
+ version: '0'
182
+ requirements: []
183
+ rubyforge_project:
184
+ rubygems_version: 2.4.1
185
+ signing_key:
186
+ specification_version: 4
187
+ summary: Coalesce colors to a given palette
188
+ test_files:
189
+ - test/minitest_helper.rb
190
+ - test/palette_test.rb
191
+ - test/test.html
192
+ has_rdoc: