color-maker 0.0.1

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