color-maker 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: 448854ab6021a69144db3a5959dc5c889c685282
4
+ data.tar.gz: 69051f1096a95ae6da1706f405ac1ff16910dcfc
5
+ SHA512:
6
+ metadata.gz: 842806eaa65b94d907e731240c78865096c08a92791ee325bff9cf869a9f08c9f6470260666c52a531e4345c21b3c2381659d0a8c68a8f09fc6e49ac9d9472fc
7
+ data.tar.gz: 5bfc948e3739ba22ca027ee88724cdf0db8edc86210996de60d1c31eff6c12a2ce1a388867aaf92d1cb3a5df5473eabcbbfbd34bb6bea38d5452fbe5acf85b3b
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in nice_color.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Viorel Craescu
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,4 @@
1
+ color-maker
2
+ ===========
3
+
4
+ Color generator for Ruby
@@ -0,0 +1,13 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << 'test'
6
+ t.libs << 'lib'
7
+ t.test_files = FileList['test/*_test.rb']
8
+ end
9
+
10
+ desc 'Run tests'
11
+ task :default => :test
12
+
13
+
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'color/maker/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "color-maker"
8
+ spec.version = Color::Maker::VERSION
9
+ spec.authors = ["Viorel Craescu"]
10
+ spec.email = ["viorel@craescu.com"]
11
+ spec.summary = %q{Color generator}
12
+ spec.description = %q{Color generator}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "color", "~> 1.7"
22
+ spec.add_dependency "activesupport"
23
+ spec.add_development_dependency "yard"
24
+ spec.add_development_dependency "minitest"
25
+ spec.add_development_dependency "bundler", "~> 1.6"
26
+ spec.add_development_dependency "rake", "~> 10.0"
27
+ end
@@ -0,0 +1,10 @@
1
+ require "color/maker/version"
2
+ require 'color/hash'
3
+ require 'color/string'
4
+ require 'color/array'
5
+ require 'color/maker'
6
+ require 'color/maker/support'
7
+ require 'active_support/core_ext/hash/keys'
8
+ require 'active_support/core_ext/object/try'
9
+ require 'color'
10
+ require 'yaml'
@@ -0,0 +1,30 @@
1
+ # Monkey patch Array class with color conversion utility methods
2
+ class Array
3
+ # Converts an array to color
4
+ #
5
+ # @param [Symbol] format color format (:rgb, :hsv, :hsl)
6
+ # @return [Color::RGB]
7
+ # @example
8
+ # [150, 200, 100].to_color(:rgb) #=> Color::RGB
9
+ # [200, 0.9, 0.3].to_color(:hsv) #=> Color::RGB
10
+ # [150, 0.3, 0.1].to_color(:hsl) #=> Color::RGB
11
+ def to_color(format = :rgb)
12
+ format = format.to_sym
13
+ if format == :rgb
14
+ r, g, b = self
15
+ return Color::Maker::Support::rgb_to_color(r: r.to_i, g: g.to_i, b: b.to_i)
16
+ end
17
+
18
+ if format == :hsv
19
+ h, s, v = self
20
+ return Color::Maker::Support::hsv_to_color(h: h.to_f, s: s.to_f, v: v.to_f)
21
+ end
22
+
23
+ if format == :hsl
24
+ h, s, l = self
25
+ return Color::Maker::Support::hsl_to_color(h: h.to_f, s: s.to_f, l: l.to_f)
26
+ end
27
+
28
+ raise "Unknown color format: #{format}"
29
+ end
30
+ end
@@ -0,0 +1,51 @@
1
+ # Monkey patch Hash class with color conversion utility methods
2
+ class Hash
3
+ # Convert an Hash to color
4
+ #
5
+ # @param [Symbol] format color format (:rgb, :hsv, :hsl)
6
+ # @return [Color::RGB]
7
+ # @example
8
+ # { r: 150, g: 100, b: 200 }.to_colour(:rgb) #=> Color::RGB
9
+ # { red: 150, green: 100, blue: 200 }.to_colour(:rgb) #=> Color::RGB
10
+ # { h: 150, s: 100, v: 200 }.to_colour(:hsv) #=> Color::RGB
11
+ # { hue: 150, saturation: 100, value: 200 }.to_colour(:hsv) #=> Color::RGB
12
+ # { hue: 150, saturation: 100, lightness: 200 }.to_colour(:hsl) #=> Color::RGB
13
+ # { h: 150, s: 100, l: 200 }.to_colour(:hsl) #=> Color::RGB
14
+ def to_color(format = :rgb)
15
+ format = format.to_sym
16
+ if format == :rgb
17
+ normalized = Color::Maker::Support.normalize_color_keys(self, format: :rgb)
18
+ r, g, b = normalized[:r].to_i, normalized[:g].to_i, normalized[:b].to_i
19
+ return Color::Maker::Support::rgb_to_color(r: r, g: g, b: b)
20
+ end
21
+
22
+ if format == :hsv
23
+ normalized = Color::Maker::Support.normalize_color_keys(self, format: :hsv)
24
+ h, s, v = normalized[:h].to_f, normalized[:s].to_f, normalized[:v].to_f
25
+ return Color::Maker::Support::hsv_to_color(h: h, s: s, v: v)
26
+ end
27
+
28
+ if format == :hsl
29
+ normalized = Color::Maker::Support.normalize_color_keys(self, format: :hsl)
30
+ h, s, l = normalized[:h].to_f, normalized[:s].to_f, normalized[:l].to_f
31
+ return Color::Maker::Support::hsl_to_color(h: h, s: s, l: l)
32
+ end
33
+
34
+ raise "Unknow color format: #{format}"
35
+ end
36
+
37
+ # Replace keys
38
+ #
39
+ # @param [Symbol, Array, String] needle The value being searched for, otherwise known
40
+ # as the needle. An array may be used to designate multiple needles.
41
+ # @param [Symbol, String] replace The replacement value that replaces found search values.
42
+ # @example
43
+ # { one: 1, two: 2 }.replace_key!([:one, :two], :three) #=> { three: 2 }
44
+ # { one: 1, two: 2 }.replace_key!(:one, :three) #=> { three: 1, two: 2 }
45
+ def replace_key!(needle, replace)
46
+ needle = [needle] unless needle.is_a?(Enumerable)
47
+ needle.each { |key| self[replace] = self.delete(key) if self.key?(key) }
48
+ self
49
+ end
50
+ end
51
+
@@ -0,0 +1,241 @@
1
+ module Color
2
+ # Create random pleasing colors as well as color schemes based on a given color.
3
+ class Maker
4
+ PHI = 0.618033988749895
5
+
6
+ # Value generator, Random class instance
7
+ attr_accessor :generator
8
+
9
+ @colors_path = File.join(File.dirname(__FILE__), '..', 'colors.yml')
10
+
11
+ # @!attribute [r]
12
+ # Default color make options
13
+ @defaults = {
14
+ hue: nil,
15
+ saturation: nil,
16
+ value: nil,
17
+ base_color: nil,
18
+ greyscale: false,
19
+ grayscale: false,
20
+ golden: true,
21
+ random: false,
22
+ count: 1,
23
+ seed: false
24
+ }
25
+
26
+ class << self
27
+ # @!macro color_maker_defaults
28
+ # @option options [Symbol] :hue (nil) By setting the hue, you determine the
29
+ # color. (0..360)
30
+ # @option options [Symbol] :saturation (nil) By setting the saturation, you
31
+ # determine the distance from gray. (0..1.0)
32
+ # @option options [Symbol] :value (nil) By setting the value, you determine
33
+ # the balance between black and white. (0..1.0)
34
+ # @option options [Symbol] :base_color (nil) Setting a base_color (e.g. "pink")
35
+ # will create a random color within the HSV range of the chosen color.
36
+ # Will recognize any of the 146 standard HTML colors, it has a very
37
+ # good memory.
38
+ # @option options [Symbol] :greyscale (false) for the brits - Setting either
39
+ # greyscale or grayscale (but we all know which one is correct) to true
40
+ # will cause all of the colors you generate to be within the grey or gray
41
+ # range. This is effectively the same as setting your saturation to 0.
42
+ # @option options [Symbol] :grayscale (false) for the yanks - Setting either
43
+ # greyscale or grayscale (but we all know which one is correct) to true
44
+ # will cause all of the colors you generate to be within the grey or gray
45
+ # range. This is effectively the same as setting your saturation to 0.
46
+ # @option options [Symbol] :golden (true) Setting golden to true randomizes
47
+ # your hue (overrides hue setting) and makes you a spectacular color
48
+ # based on the golden ratio. It's so good, it's the default. Make sure to
49
+ # turn it off if you want to have more control over your generated colors.
50
+ # @option options [Symbol] :random (false) It will completely randomize the
51
+ # hue, saturation, and value of the colors it makes.
52
+ # @option options [Symbol] :count (1) Setting count to higher than 1 will
53
+ # return an array full of the colors. If you set it to 1, you'll just get
54
+ # the one color! It makes a sort of sense if you think about it.
55
+
56
+ # @!macro color_maker_defaults_rgb
57
+ # @option options [Symbol] :red (nil) red value for the color to start with (0..255)
58
+ # @option options [Symbol] :green (nil) green value for the color to start with (0..255)
59
+ # @option options [Symbol] :blue (nil) blue value for the color to start with (0..255)
60
+
61
+ # @!macro color_maker_defaults_hsl
62
+ # @option options [Symbol] :lightness (nil) the luminosity of the colour (0..1.0)
63
+
64
+ # @!macro color_maker_defaults
65
+ attr_reader :defaults
66
+
67
+ # Memorized colors
68
+ #
69
+ # @return [Hash]
70
+ # @example
71
+ # Color::Maker.colors(:aquamarine) #=> 7FFFD4
72
+ def colors
73
+ return @colors if @colors
74
+ @hex_colors = ::YAML.load_file(@colors_path)
75
+ @hex_colors.symbolize_keys!
76
+ @colors = Hash.new do |h, k|
77
+ if @hex_colors[k]
78
+ h[k] = Color::RGB.by_hex(@hex_colors[k])
79
+ else
80
+ h[k] = Color::RGB.by_name(k)
81
+ end
82
+ end
83
+ end
84
+
85
+ # Return a Color::RGB object
86
+ #
87
+ # @param name [Symbol] css color name or from above list
88
+ # @return Color::RGB
89
+ # @example
90
+ # Color::Maker.by_name(:aquamarine) #=> Color::RGB
91
+ # Color::Maker.by_name(:blue) #=> Color::RGB
92
+ # Color::Maker.by_name(:aliceblue) #=> Color::RGB
93
+ # {include:file:lib/colors.yml}
94
+ def by_name(name)
95
+ self.colors[name.downcase.to_sym]
96
+ end
97
+ end
98
+
99
+ # @!macro color_maker_defaults
100
+ # @!macro color_maker_defaults_rgb
101
+ # @!macro color_maker_defaults_hsl
102
+ def initialize(options = {})
103
+ options = normalize_options(options)
104
+ @options = self.class.defaults.merge(options)
105
+ @generator = Random.new(options[:seed]) rescue Random.new
106
+ end
107
+
108
+ # Generates a new color
109
+ # @param [Hash] options the options to generate the color
110
+ # @!macro color_maker_defaults
111
+ # @!macro color_maker_defaults_rgb
112
+ # @!macro color_maker_defaults_hsl
113
+ # @return Color::RGB
114
+ def make(options = {})
115
+ generator = self.generator
116
+ generator = Random.new(options[:seed]) if options[:seed]
117
+
118
+ options = normalize_options(options)
119
+ options = self.class.defaults.merge(@options.merge(options))
120
+
121
+ base_color = self.class.by_name(options[:base_color]) if options[:base_color]
122
+ colors = []
123
+ options[:count].times do |i|
124
+ if base_color
125
+ base_color = base_color.to_hsl
126
+ base_color = Support::hsl_to_hsv(h: base_color.hue, s: base_color.s, l: base_color.l)
127
+
128
+ hue = generator.rand((base_color[:h] - 5)..(base_color[:h] + 5))
129
+ saturation = generator.rand(0.4..0.85)
130
+ value = generator.rand(0.4..0.85)
131
+ colors << [hue, saturation, value].to_color(:hsv)
132
+ else
133
+ hue = make_hue(options, generator)
134
+ saturation = make_saturation(options, generator)
135
+ value = make_value(options, generator)
136
+
137
+ colors << [hue, saturation, value].to_color(:hsv)
138
+ end
139
+ end
140
+
141
+ colors = colors[0] if colors.size == 1
142
+ colors
143
+ end
144
+
145
+
146
+ private
147
+ def normalize_options(options)
148
+ normalized = {}
149
+
150
+ variants = { hue: [:h], saturation: [:s], value: [:v],
151
+ red: [:r], green: [:g], blue: [:b],
152
+ lightness: [:l, :luminosity, :light],
153
+ greyscale: [:grayscale, :gray] }
154
+
155
+ variants.each do |key, keys|
156
+ found_key = keys.find { |k| options[k] } || key
157
+ normalized[key] ||= options[found_key] if options[found_key]
158
+ options.delete(found_key) if normalized[key]
159
+ end
160
+
161
+ options.each { |key, value| normalized[key] = value unless normalized.key?(key) }
162
+ normalized.merge(extract_color(normalized))
163
+ normalized
164
+ end
165
+
166
+ def extract_color(options)
167
+ formats = { :hsv => [:hue, :saturation, :value],
168
+ :rgb => [:red, :green, :blue ],
169
+ :hsl => [:hue, :saturation, :lightness] }
170
+
171
+ format = formats.map do |key, keys|
172
+ [key, keys.count { |k| options[k] } ]
173
+ end
174
+ format = format.max_by { |v| v.last }.first
175
+
176
+ color = {}
177
+ formats[format].each { |value| color[value] = options[value].to_f }
178
+
179
+ case format
180
+ when :rgb
181
+ color = Support.rgb_to_hsv(color)
182
+ when :hsl
183
+ color = Support.hsl_to_hsv(color)
184
+ end
185
+
186
+ color
187
+ end
188
+
189
+ def make_hue(options = {}, generator = nil)
190
+ generator ||= self.generator
191
+
192
+ if options[:greyscale]
193
+ hue = 0
194
+ elsif options[:golden]
195
+ random_hue = generator.rand(0..360)
196
+ hue = (random_hue + (random_hue / PHI)) % 360
197
+ elsif options[:hue].nil? || options[:random]
198
+ hue = random_hue
199
+ else
200
+ hue = clamp(options[:hue], 0, 360)
201
+ end
202
+ hue
203
+ end
204
+
205
+ def make_saturation(options = {}, generator = nil)
206
+ generator ||= self.generator
207
+
208
+ if options[:greyscale]
209
+ saturation = 0
210
+ elsif options[:random]
211
+ saturation = generator.rand(0..1.0)
212
+ elsif options[:saturation].nil?
213
+ saturation = 0.4
214
+ else
215
+ saturation = clamp(options[:saturation], 0, 1)
216
+ end
217
+
218
+ saturation
219
+ end
220
+
221
+ def make_value(options = {}, generator = nil)
222
+ generator ||= self.generator
223
+
224
+ if options[:random]
225
+ value = self.generator.rand(0..1.0)
226
+ elsif options[:greyscale]
227
+ value = self.generator.rand(0.15..0.75)
228
+ elsif options[:value].nil?
229
+ value = 0.75
230
+ else
231
+ value = clamp(options[:value], 0, 1)
232
+ end
233
+
234
+ value
235
+ end
236
+
237
+ def clamp(num, min, max)
238
+ [min, [num, max].min].max
239
+ end
240
+ end
241
+ end
@@ -0,0 +1,235 @@
1
+ module Color
2
+ class Maker
3
+ # Utilities methods
4
+ module Support
5
+ class << self
6
+ # Convert color from HSV to RGB format
7
+ #
8
+ # @param [Hash] hsv
9
+ # @return [Hash] color in RGB format
10
+ # @example
11
+ # Color::Maker::Support.hsv_to_rgb({ h: 12, s: 0.9, v: 0.2 }) #=> { r: 51, g: 14, b: 5 }
12
+ # Color::Maker::Support.hsv_to_rgb({ hue: 12, saturation: 0.9, value: 0.2 }) #=> { r: 51, g: 14, b: 5 }
13
+ def hsv_to_rgb(hsv)
14
+ hsv = normalize_color_keys(hsv, format: :hsv)
15
+ h, s, v = hsv[:h].to_f / 360.0, hsv[:s].to_f, hsv[:v].to_f
16
+ i = (h * 6).floor
17
+ f = h * 6 - i
18
+ p = v * (1 - s)
19
+ q = v * (1 - f * s)
20
+ t = v * (1 - (1 - f) * s)
21
+ case i % 6
22
+ when 0
23
+ r, g, b = v, t, p
24
+ when 1
25
+ r, g, b = q, v, p
26
+ when 2
27
+ r, g, b = p, v, t
28
+ when 3
29
+ r, g, b = p, q, v
30
+ when 4
31
+ r, g, b = t, p, v
32
+ else
33
+ r, g, b = v, p, q
34
+ end
35
+
36
+ r, g, b = (r * 255).round(0), (g * 255).round(0), (b * 255).round(0)
37
+ { r: r, g: g, b: b }
38
+ end
39
+
40
+ # Convert color from RGB to HSV
41
+ #
42
+ # @param [Hash] rgb
43
+ # @return [Hash] color in HSV format
44
+ # @example
45
+ # Color::Maker::Support.rgb_to_hsv({ r: 15, g: 193, b: 17 }) #=> { h: 120.7, s: 0.922, v: 0.757 }
46
+ # Color::Maker::Support.rgb_to_hsv({ red: 15, green: 193, blue: 17 }) #=> { h: 120.7, s: 0.922, v: 0.757 }
47
+ def rgb_to_hsv(rgb)
48
+ rgb = normalize_color_keys(rgb, format: :rgb)
49
+ r, g, b = rgb[:r].to_f / 255.0, rgb[:g].to_f / 255.0, rgb[:b].to_f / 255.0
50
+ min, max = [r, g, b].min, [r, g, b].max
51
+ delta = max - min
52
+ v = max
53
+
54
+ return { h: 0, s: 0, v: v.round(3) } if max == 0 || delta == 0
55
+
56
+ s = delta / max
57
+ h = 4 + (r - g) / delta
58
+
59
+ if r == max
60
+ h = (g - b) / delta
61
+ elsif g == max
62
+ h = 2 + (b - r) / delta
63
+ end
64
+
65
+ h *= 60
66
+ h += 360 if h < 0
67
+ { h: h.round(1), s: s.round(3), v: v.round(3) }
68
+ end
69
+
70
+ # Convert color from RGB to HSL
71
+ #
72
+ # @param [Hash] rgb
73
+ # @return [Hash] color in HSL format
74
+ # @example
75
+ # Color::Maker::Support.rgb_to_hsl({ r: 15, g: 193, b: 17 }) #=> { h: 120.7, s: 0.922, v: 0.757 }
76
+ # Color::Maker::Support.rgb_to_hsl({ red: 15, green: 193, blue: 17 }) #=> { h: 120.7, s: 0.922, v: 0.757 }
77
+ def rgb_to_hsl(rgb)
78
+ rgb = normalize_color_keys(rgb, format: :rgb)
79
+ r, g, b = rgb[:r].to_f / 255.0, rgb[:g].to_f / 255.0, rgb[:b].to_f / 255.0
80
+ min, max = [r, g, b].min, [r, g, b].max
81
+ delta = max - min
82
+
83
+ l = (min + max) / 2.0
84
+ return { h: 0, s: 0, l: l } if delta == 0
85
+
86
+ s = l < 0.5 ? delta / (max + min) : delta / (2 - max - min)
87
+
88
+ case(max)
89
+ when r
90
+ h = (g - b) / delta + (g < b ? 6 : 0)
91
+ when g
92
+ h = (b - r) / delta + 2
93
+ when b
94
+ h = (r - g) / delta + 4
95
+ end
96
+
97
+ h *= 60
98
+
99
+ { h: h.round(1), s: s.round(3), l: l.round(3) }
100
+ end
101
+
102
+ # Convert color from HSL to RGB
103
+ #
104
+ # @param [Hash] hsl
105
+ # @return [Hash] color in RGB format
106
+ # @example
107
+ # Color::Maker::Support.hsl_to_rgb({ h: 120.7, s: 0.856, l: 0.408 }) #=> { r: 15, g: 193, b: 17 }
108
+ # Color::Maker::Support.hsl_to_rgb({ hue: 120.7, saturation: 0.856, lightness: 0.408 }) #=> { r: 15, g: 193, b: 17 }
109
+ def hsl_to_rgb(hsl)
110
+ hsl = normalize_color_keys(hsl, format: :hsl)
111
+ h, s, l = hsl[:h].to_f / 360.0, hsl[:s].to_f, hsl[:l].to_f
112
+ return { r: l, g: l, b: l } if s == 0
113
+
114
+ q = l < 0.5 ? l * (1 + s) : l + s - l * s
115
+ p = 2 * l - q
116
+
117
+ r = hue_to_rgb(p: p, q: q, t: h + 1 / 3.0) * 255
118
+ g = hue_to_rgb(p: p, q: q, t: h) * 255
119
+ b = hue_to_rgb(p: p, q: q, t: h - 1 / 3.0) * 255
120
+
121
+ { r: r.round(0), g: g.round(0), b: b.round(0) }
122
+ end
123
+
124
+ # Convert color from HSV to HSL
125
+ #
126
+ # @param [Hash] hsv
127
+ # @return [Hash] color in HSL format
128
+ # @example
129
+ # Color::Maker::Support.hsv_to_hsl({ h: 120.7, s: 0.922, v: 0.757 }) #=> { h: 120.7, s: 0.856, v: 0.408 })
130
+ # Color::Maker::Support.hsv_to_hsl({ hue: 120.7, saturation: 0.922, value: 0.757 }) #=> { h: 120.7, s: 0.856, v: 0.408 })
131
+ def hsv_to_hsl(hsv)
132
+ rgb = hsv_to_rgb(hsv)
133
+ rgb_to_hsl(rgb)
134
+ end
135
+
136
+ # Convert color from HSL to HSV
137
+ #
138
+ # @param [Hash] hsl
139
+ # @return [Hash] color in HSL format
140
+ # @example
141
+ # Color::Maker::Support.hsl_to_hsv({ h: 120.7, s: 0.856, l: 0.408 }) #=> { h: 120.7, s: 0.922, v: 0.757 })
142
+ # Color::Maker::Support.hsl_to_hsv({ hue: 120.7, saturation: 0.856, lightness: 0.408 }) #=> { h: 120.7, s: 0.922, v: 0.757 })
143
+ def hsl_to_hsv(hsl)
144
+ rgb = hsl_to_rgb(hsl)
145
+ rgb_to_hsv(rgb)
146
+ end
147
+
148
+ # Convert color from HSL to Color::RGB
149
+ #
150
+ # @param [Hash] hsl
151
+ # @return [Color::RGB] color
152
+ # @example
153
+ # Color::Maker::Support.hsl_to_color({ h: 120.7, s: 0.856, l: 0.408 }) #=> Color::RGB
154
+ # Color::Maker::Support.hsl_to_color({ hue: 120.7, saturation: 0.856, lightness: 0.408 }) #=> Color::RGB
155
+ def hsl_to_color(hsl)
156
+ rgb = hsl_to_rgb(hsl)
157
+ Color::RGB.new(rgb[:r], rgb[:g], rgb[:b])
158
+ end
159
+
160
+ # Convert color from RGB to Color::RGB
161
+ #
162
+ # @param [Hash] rgb
163
+ # @return [Color::RGB] color
164
+ # @example
165
+ # Color::Maker::Support.rgb_to_color({ r: 100, g: 150, b: 250 }) #=> Color::RGB
166
+ # Color::Maker::Support.rgb_to_color({ red: 100, green: 150, blue: 250 }) #=> Color::RGB
167
+ def rgb_to_color(rgb)
168
+ rgb = normalize_color_keys(rgb, format: :rgb)
169
+ Color::RGB.new(rgb[:r], rgb[:g], rgb[:b])
170
+ end
171
+
172
+ # Convert color from HSV to Color::RGB
173
+ #
174
+ # @param [Hash] hsv
175
+ # @return [Color::RGB] color
176
+ def hsv_to_color(hsv)
177
+ rgb = hsv_to_rgb(hsv)
178
+ rgb_to_color(rgb)
179
+ end
180
+
181
+ # Convert color from HEX to Color::RGB
182
+ #
183
+ # @param [Hash] hex
184
+ # @return [Color::RGB] color
185
+ def hex_to_color(hex)
186
+ hex = hex.sub(/0x/, '')
187
+ Color::RGB.by_hex(hex)
188
+ end
189
+
190
+ # Normalize color keys
191
+ #
192
+ # @param [Hash] hash values in HSV, RGB or HSL format
193
+ # @param [Hash] options ({ format: :rgb, short: true })
194
+ # @return [Hash] color keys normalized
195
+ # @example
196
+ # Color::Maker::Support.normalize_color_keys({ r: 100, blue: 100, g: 100 }) #=> { r: 100, b: 100, g: 10 }
197
+ # Color::Maker::Support.normalize_color_keys({ r: 100, blue: 100, g: 100 }, short: false) #=> { red: 100, blue: 100, green: 10 }
198
+ def normalize_color_keys(hash, options = {})
199
+ default_options = { format: :rgb, short: true }
200
+ options = default_options.merge(options)
201
+
202
+ format = options[:format].to_sym
203
+
204
+ keys = {
205
+ rgb: { [:r, :red] => [:r, :red], [:b, :blue] => [:b, :blue], [:g, :green] => [:g, :green] },
206
+ hsv: { [:h, :hue] => [:h, :hue], [:s, :saturation] => [:s, :saturation], [:v, :value] => [:v, :value] },
207
+ hsl: { [:h, :hue] => [:h, :hue], [:s, :saturation] => [:s, :saturation], [:l, :light] => [:l, :lightness, :luminosity, :light] }
208
+ }
209
+
210
+ return unless keys[format]
211
+
212
+ normalized = hash.dup
213
+ keys[format].each do |key, variants|
214
+ default = options[:short] ? key.first : key.last
215
+ normalized.replace_key!(variants, default)
216
+ end
217
+
218
+ normalized
219
+ end
220
+
221
+ private
222
+ def hue_to_rgb(hue)
223
+ p, q, t = hue[:p].to_f, hue[:q].to_f, hue[:t].to_f
224
+ t += 1 if t < 0
225
+ t -= 1 if t > 1
226
+ return p + (q - p) * 6 * t if t < (1 / 6.0)
227
+ return q if t < (1 / 2.0)
228
+ return p + (q - p) * (2 / 3.0 - t) * 6 if t < (2 / 3.0)
229
+ p
230
+ end
231
+
232
+ end
233
+ end
234
+ end
235
+ end