kodachroma 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE.txt +20 -0
- data/README.org +401 -0
- data/lib/kodachroma/color/attributes.rb +59 -0
- data/lib/kodachroma/color/modifiers.rb +124 -0
- data/lib/kodachroma/color/serializers.rb +184 -0
- data/lib/kodachroma/color.rb +112 -0
- data/lib/kodachroma/color_modes.rb +55 -0
- data/lib/kodachroma/converters/base.rb +34 -0
- data/lib/kodachroma/converters/hsl_converter.rb +55 -0
- data/lib/kodachroma/converters/hsv_converter.rb +49 -0
- data/lib/kodachroma/converters/rgb_converter.rb +72 -0
- data/lib/kodachroma/errors.rb +8 -0
- data/lib/kodachroma/harmonies.rb +139 -0
- data/lib/kodachroma/helpers/bounders.rb +50 -0
- data/lib/kodachroma/palette_builder.rb +80 -0
- data/lib/kodachroma/rgb_generator/base.rb +10 -0
- data/lib/kodachroma/rgb_generator/from_hex_string_values.rb +63 -0
- data/lib/kodachroma/rgb_generator/from_hsl.rb +19 -0
- data/lib/kodachroma/rgb_generator/from_hsl_values.rb +25 -0
- data/lib/kodachroma/rgb_generator/from_hsv.rb +19 -0
- data/lib/kodachroma/rgb_generator/from_hsv_values.rb +25 -0
- data/lib/kodachroma/rgb_generator/from_rgb.rb +19 -0
- data/lib/kodachroma/rgb_generator/from_rgb_values.rb +27 -0
- data/lib/kodachroma/rgb_generator/from_string.rb +89 -0
- data/lib/kodachroma/rgb_generator.rb +38 -0
- data/lib/kodachroma/version.rb +4 -0
- data/lib/kodachroma.rb +130 -0
- data/lib/support/named_colors.yml +149 -0
- metadata +186 -0
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Kodachroma
|
3
|
+
module RgbGenerator
|
4
|
+
class FromHexStringValues < Base
|
5
|
+
# @param format [Symbol] color format
|
6
|
+
# @param r [String] red value
|
7
|
+
# @param g [String] green value
|
8
|
+
# @param b [String] blue value
|
9
|
+
# @param a [String] alpha value
|
10
|
+
def initialize(format, r, g, b, a = 'ff')
|
11
|
+
@format = format || :hex
|
12
|
+
@r = r
|
13
|
+
@g = g
|
14
|
+
@b = b
|
15
|
+
@a = a
|
16
|
+
end
|
17
|
+
|
18
|
+
# Generates a {ColorModes::Rgb}.
|
19
|
+
# @return [ColorModes::Rgb]
|
20
|
+
def generate
|
21
|
+
r, g, b = [@r, @g, @b].map { |n| n.to_i(16) }
|
22
|
+
a = @a.to_i(16) / 255.0
|
23
|
+
[ColorModes::Rgb.new(r, g, b, a), @format]
|
24
|
+
end
|
25
|
+
|
26
|
+
class << self
|
27
|
+
# Generates a {ColorModes::Rgb} from 3-character hexadecimal.
|
28
|
+
# @return [ColorModes::Rgb]
|
29
|
+
#
|
30
|
+
# @param format [Symbol] color format
|
31
|
+
# @param r [String] red value
|
32
|
+
# @param g [String] green value
|
33
|
+
# @param b [String] blue value
|
34
|
+
def from_hex3(format, r, g, b)
|
35
|
+
new(format || :hex3, r * 2, g * 2, b * 2)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Generates a {ColorModes::Rgb} from 6-character hexadecimal.
|
39
|
+
# @return [ColorModes::Rgb]
|
40
|
+
#
|
41
|
+
# @param format [Symbol] color format
|
42
|
+
# @param r [String] red value
|
43
|
+
# @param g [String] green value
|
44
|
+
# @param b [String] blue value
|
45
|
+
def from_hex6(format, r, g, b)
|
46
|
+
new(format, r, g, b)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Generates a {ColorModes::Rgb} from 8-character hexadecimal.
|
50
|
+
# @return [ColorModes::Rgb]
|
51
|
+
#
|
52
|
+
# @param format [Symbol] color format
|
53
|
+
# @param r [String] red value
|
54
|
+
# @param g [String] green value
|
55
|
+
# @param b [String] blue value
|
56
|
+
# @param a [String] alpha value
|
57
|
+
def from_hex8(format, a, r, g, b)
|
58
|
+
new(format || :hex8, r, g, b, a)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Kodachroma
|
3
|
+
module RgbGenerator
|
4
|
+
class FromHsl < Base
|
5
|
+
# @param format [Symbol] color format
|
6
|
+
# @param hsl [ColorModes::Hsl]
|
7
|
+
def initialize(format, hsl)
|
8
|
+
@format = format
|
9
|
+
@hsl = hsl
|
10
|
+
end
|
11
|
+
|
12
|
+
# Generates a {ColorModes::Rgb}.
|
13
|
+
# @return [ColorModes::Rgb]
|
14
|
+
def generate
|
15
|
+
FromHslValues.new(@format, *@hsl.to_a).generate
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Kodachroma
|
3
|
+
module RgbGenerator
|
4
|
+
class FromHslValues < Base
|
5
|
+
# @param format [Symbol] color format
|
6
|
+
# @param h [String, Numeric] hue value
|
7
|
+
# @param s [String, Numeric] saturation value
|
8
|
+
# @param l [String, Numeric] lightness value
|
9
|
+
# @param a [String, Numeric] alpha value
|
10
|
+
def initialize(format, h, s, l, a = 1)
|
11
|
+
s = to_percentage(s)
|
12
|
+
l = to_percentage(l)
|
13
|
+
|
14
|
+
@format = format || :hsl
|
15
|
+
@hsl = ColorModes::Hsl.new(h, s, l, a)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Generates a {ColorModes::Rgb}.
|
19
|
+
# @return [ColorModes::Rgb]
|
20
|
+
def generate
|
21
|
+
[Converters::RgbConverter.convert_hsl(@hsl), @format]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Kodachroma
|
3
|
+
module RgbGenerator
|
4
|
+
class FromHsv < Base
|
5
|
+
# @param format [Symbol] color format
|
6
|
+
# @param hsv [ColorModes::Hsv]
|
7
|
+
def initialize(format, hsv)
|
8
|
+
@format = format
|
9
|
+
@hsv = hsv
|
10
|
+
end
|
11
|
+
|
12
|
+
# Generates a {ColorModes::Rgb}.
|
13
|
+
# @return [ColorModes::Rgb]
|
14
|
+
def generate
|
15
|
+
FromHsvValues.new(@format, *@hsv.to_a).generate
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Kodachroma
|
3
|
+
module RgbGenerator
|
4
|
+
class FromHsvValues < Base
|
5
|
+
# @param format [Symbol] color format
|
6
|
+
# @param h [String, Numeric] hue value
|
7
|
+
# @param s [String, Numeric] saturation value
|
8
|
+
# @param v [String, Numeric] value value
|
9
|
+
# @param a [String, Numeric] alpha value
|
10
|
+
def initialize(format, h, s, v, a = 1)
|
11
|
+
s = to_percentage(s)
|
12
|
+
v = to_percentage(v)
|
13
|
+
|
14
|
+
@format = format || :hsv
|
15
|
+
@hsv = ColorModes::Hsv.new(h, s, v, a)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Generates a {ColorModes::Rgb}.
|
19
|
+
# @return [ColorModes::Rgb]
|
20
|
+
def generate
|
21
|
+
[Converters::RgbConverter.convert_hsv(@hsv), @format]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Kodachroma
|
3
|
+
module RgbGenerator
|
4
|
+
class FromRgb < Base
|
5
|
+
# @param format [Symbol] color format
|
6
|
+
# @param rgb [ColorModes::Rgb]
|
7
|
+
def initialize(format, rgb)
|
8
|
+
@format = format
|
9
|
+
@rgb = rgb
|
10
|
+
end
|
11
|
+
|
12
|
+
# Generates a {ColorModes::Rgb}.
|
13
|
+
# @return [ColorModes::Rgb]
|
14
|
+
def generate
|
15
|
+
FromRgbValues.new(@format, @rgb.r, @rgb.g, @rgb.b, @rgb.a).generate
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Kodachroma
|
3
|
+
module RgbGenerator
|
4
|
+
class FromRgbValues < Base
|
5
|
+
# @param format [Symbol] color format
|
6
|
+
# @param r [String, Numeric] red value
|
7
|
+
# @param g [String, Numeric] green value
|
8
|
+
# @param b [String, Numeric] blue value
|
9
|
+
# @param a [String, Numeric] alpha value
|
10
|
+
def initialize(format, r, g, b, a = 1)
|
11
|
+
@format = format || :rgb
|
12
|
+
@r = r
|
13
|
+
@g = g
|
14
|
+
@b = b
|
15
|
+
@a = a
|
16
|
+
end
|
17
|
+
|
18
|
+
# Generates a {ColorModes::Rgb}.
|
19
|
+
# @return [ColorModes::Rgb]
|
20
|
+
def generate
|
21
|
+
r, g, b = [@r, @g, @b].map { |n| bound01(n, 255) * 255 }
|
22
|
+
a = bound_alpha(@a)
|
23
|
+
[ColorModes::Rgb.new(r, g, b, a), @format]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Kodachroma
|
3
|
+
module RgbGenerator
|
4
|
+
class FromString < Base
|
5
|
+
# Returns the regex matchers and rgb generation classes for various
|
6
|
+
# string color formats.
|
7
|
+
#
|
8
|
+
# @api private
|
9
|
+
# @return [Hash<Symbol, Hash>]
|
10
|
+
def self.matchers
|
11
|
+
@matchers ||= begin
|
12
|
+
# TinyColor.js matchers
|
13
|
+
css_int = '[-\\+]?\\d+%?'
|
14
|
+
css_num = '[-\\+]?\\d*\\.\\d+%?'
|
15
|
+
css_unit = "(?:#{ css_num })|(?:#{ css_int })"
|
16
|
+
permissive_prefix = '[\\s|\\(]+('
|
17
|
+
permissive_delim = ')[,|\\s]+('
|
18
|
+
permissive_suffix = ')\\s*\\)?'
|
19
|
+
permissive_match3 = "#{ permissive_prefix }#{ [css_unit] * 3 * permissive_delim }#{ permissive_suffix }"
|
20
|
+
permissive_match4 = "#{ permissive_prefix }#{ [css_unit] * 4 * permissive_delim }#{ permissive_suffix }"
|
21
|
+
hex_match = '[0-9a-fA-F]'
|
22
|
+
|
23
|
+
{
|
24
|
+
rgb: { regex: /rgb#{ permissive_match3 }/, class_name: :FromRgbValues },
|
25
|
+
rgba: { regex: /rgba#{ permissive_match4 }/, class_name: :FromRgbValues },
|
26
|
+
hsl: { regex: /hsl#{ permissive_match3 }/, class_name: :FromHslValues },
|
27
|
+
hsla: { regex: /hsla#{ permissive_match4 }/, class_name: :FromHslValues },
|
28
|
+
hsv: { regex: /hsv#{ permissive_match3 }/, class_name: :FromHsvValues },
|
29
|
+
hsva: { regex: /hsva#{ permissive_match4 }/, class_name: :FromHsvValues },
|
30
|
+
hex3: { regex: /^#?#{ "(#{ hex_match }{1})" * 3 }$/, class_name: :FromHexStringValues, builder: :from_hex3 },
|
31
|
+
hex6: { regex: /^#?#{ "(#{ hex_match }{2})" * 3 }$/, class_name: :FromHexStringValues, builder: :from_hex6 },
|
32
|
+
hex8: { regex: /^#?#{ "(#{ hex_match }{2})" * 4 }$/, class_name: :FromHexStringValues, builder: :from_hex8 }
|
33
|
+
}.freeze
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# @param format [Symbol] unused
|
38
|
+
# @param input [String] input to parse
|
39
|
+
def initialize(_format, input)
|
40
|
+
@input = normalize_input(input)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Generates a {ColorModes::Rgb}.
|
44
|
+
# @return [ColorModes::Rgb]
|
45
|
+
def generate
|
46
|
+
get_generator.generate
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def get_generator
|
52
|
+
if color = Kodachroma.hex_from_name(@input)
|
53
|
+
format = :name
|
54
|
+
elsif @input == 'transparent'
|
55
|
+
return FromRgbValues.new(:name, 0, 0, 0, 0)
|
56
|
+
else
|
57
|
+
format = nil
|
58
|
+
color = @input
|
59
|
+
end
|
60
|
+
|
61
|
+
match = nil
|
62
|
+
|
63
|
+
_, hash = matchers.find do |_, h|
|
64
|
+
!(match = h[:regex].match(color)).nil?
|
65
|
+
end
|
66
|
+
|
67
|
+
if match.nil?
|
68
|
+
raise Errors::UnrecognizedColor, "Unrecognized color `#{ color }'"
|
69
|
+
end
|
70
|
+
|
71
|
+
build_generator(match[1..-1], hash[:class_name], hash[:builder], format)
|
72
|
+
end
|
73
|
+
|
74
|
+
def build_generator(args, class_name, builder, format)
|
75
|
+
builder ||= :new
|
76
|
+
klass = RgbGenerator.const_get(class_name)
|
77
|
+
klass.__send__(builder, *([format] + args))
|
78
|
+
end
|
79
|
+
|
80
|
+
def normalize_input(input)
|
81
|
+
input.strip.downcase
|
82
|
+
end
|
83
|
+
|
84
|
+
def matchers
|
85
|
+
self.class.matchers
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Kodachroma
|
3
|
+
# Main module to generate an instance of {ColorModes::Rgb} from several
|
4
|
+
# possible inputs.
|
5
|
+
module RgbGenerator
|
6
|
+
class << self
|
7
|
+
# Generates an instance of {ColorModes::Rgb} as well as color format
|
8
|
+
# symbol.
|
9
|
+
#
|
10
|
+
# @param input [String, ColorModes::Rgb, ColorModes::Hsl, ColorModes::Hsv]
|
11
|
+
# @return [[ColorModes::Rgb, Symbol]]
|
12
|
+
def generate_rgb_and_format(input)
|
13
|
+
get_generator(input).generate.tap do |(rgb)|
|
14
|
+
rgb.r = round(rgb.r)
|
15
|
+
rgb.g = round(rgb.g)
|
16
|
+
rgb.b = round(rgb.b)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def get_generator(input)
|
23
|
+
klass = case input
|
24
|
+
when String then FromString
|
25
|
+
when ColorModes::Hsl then FromHsl
|
26
|
+
when ColorModes::Hsv then FromHsv
|
27
|
+
when ColorModes::Rgb then FromRgb
|
28
|
+
end
|
29
|
+
|
30
|
+
klass.new(nil, input)
|
31
|
+
end
|
32
|
+
|
33
|
+
def round(n)
|
34
|
+
n < 1 ? n.round : n
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/kodachroma.rb
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# General
|
3
|
+
require 'kodachroma/version'
|
4
|
+
require 'kodachroma/errors'
|
5
|
+
require 'yaml'
|
6
|
+
|
7
|
+
# Modules
|
8
|
+
require 'kodachroma/helpers/bounders'
|
9
|
+
|
10
|
+
# Color
|
11
|
+
require 'kodachroma/color/attributes'
|
12
|
+
require 'kodachroma/color/serializers'
|
13
|
+
require 'kodachroma/color/modifiers'
|
14
|
+
require 'kodachroma/color'
|
15
|
+
require 'kodachroma/color_modes'
|
16
|
+
|
17
|
+
# Palettes
|
18
|
+
require 'kodachroma/harmonies'
|
19
|
+
require 'kodachroma/palette_builder'
|
20
|
+
|
21
|
+
# RGB Generators
|
22
|
+
require 'kodachroma/rgb_generator'
|
23
|
+
require 'kodachroma/rgb_generator/base'
|
24
|
+
require 'kodachroma/rgb_generator/from_string'
|
25
|
+
require 'kodachroma/rgb_generator/from_rgb_values'
|
26
|
+
require 'kodachroma/rgb_generator/from_rgb'
|
27
|
+
require 'kodachroma/rgb_generator/from_hsl_values'
|
28
|
+
require 'kodachroma/rgb_generator/from_hsl'
|
29
|
+
require 'kodachroma/rgb_generator/from_hsv_values'
|
30
|
+
require 'kodachroma/rgb_generator/from_hsv'
|
31
|
+
require 'kodachroma/rgb_generator/from_hex_string_values'
|
32
|
+
|
33
|
+
# Converters
|
34
|
+
require 'kodachroma/converters/base'
|
35
|
+
require 'kodachroma/converters/rgb_converter'
|
36
|
+
require 'kodachroma/converters/hsl_converter'
|
37
|
+
require 'kodachroma/converters/hsv_converter'
|
38
|
+
|
39
|
+
# The main module.
|
40
|
+
module Kodachroma
|
41
|
+
class << self
|
42
|
+
# Returns a new instance of color. Supports hexadecimal, rgb, rgba, hsl,
|
43
|
+
# hsla, hsv, hsva, and named color formats.
|
44
|
+
#
|
45
|
+
# @api public
|
46
|
+
#
|
47
|
+
# @example
|
48
|
+
# Kodachroma.paint('red')
|
49
|
+
# Kodachroma.paint('#f00')
|
50
|
+
# Kodachroma.paint('#ff0000')
|
51
|
+
# Kodachroma.paint('rgb(255, 0, 0)')
|
52
|
+
# Kodachroma.paint('hsl(0, 100%, 50%)')
|
53
|
+
# Kodachroma.paint('hsv(0, 100%, 100%)')
|
54
|
+
#
|
55
|
+
# @param input [String] the color
|
56
|
+
# @return [Color] an instance of {Color}
|
57
|
+
def paint(input)
|
58
|
+
Color.new(input)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Returns the hexadecimal string representation of a named color and nil
|
62
|
+
# if no match is found. Favors 3-character hexadecimal if possible.
|
63
|
+
#
|
64
|
+
# @example
|
65
|
+
# Kodachroma.hex_from_name('red') #=> 'f00'
|
66
|
+
# Kodachroma.hex_from_name('aliceblue') #=> 'f0f8ff'
|
67
|
+
# Kodachroma.hex_from_name('foo') #=> nil
|
68
|
+
#
|
69
|
+
# @param name [String] the color name
|
70
|
+
# @return [String, nil] the color as a string hexadecimal or nil
|
71
|
+
def hex_from_name(name)
|
72
|
+
named_colors_map[name]
|
73
|
+
end
|
74
|
+
|
75
|
+
# Returns the color name of a hexadecimal color if available and nil if no
|
76
|
+
# match is found. Requires 3-character hexadecimal input for applicable
|
77
|
+
# colors.
|
78
|
+
#
|
79
|
+
# @example
|
80
|
+
# Kodachroma.name_from_hex('f00') #=> 'red'
|
81
|
+
# Kodachroma.name_from_hex('f0f8ff') #=> 'aliceblue'
|
82
|
+
# Kodachroma.name_from_hex('123123') #=> nil
|
83
|
+
#
|
84
|
+
# @param hex [String] the hexadecimal color
|
85
|
+
# @return [String, nil] the color name or nil
|
86
|
+
def name_from_hex(hex)
|
87
|
+
hex_named_colors_map[hex]
|
88
|
+
end
|
89
|
+
|
90
|
+
# Defines a custom palette for use by {Color#palette}. Uses a DSL inside
|
91
|
+
# `block` that mirrors the methods in {Color::Modifiers}.
|
92
|
+
#
|
93
|
+
# @example
|
94
|
+
# 'red'.paint.palette.respond_to? :my_palette #=> false
|
95
|
+
#
|
96
|
+
# Kodachroma.define_palette :my_palette do
|
97
|
+
# spin 60
|
98
|
+
# spin 120
|
99
|
+
# spin 240
|
100
|
+
# end
|
101
|
+
#
|
102
|
+
# 'red'.paint.palette.respond_to? :my_palette #=> true
|
103
|
+
#
|
104
|
+
# @param name [Symbol, String] the name of the custom palette
|
105
|
+
# @param block [Proc] the palette definition block
|
106
|
+
# @raise [Errors::PaletteDefinedError] if the palette is already defined
|
107
|
+
# @return [Symbol, String] the name of the custom palette
|
108
|
+
def define_palette(name, &block)
|
109
|
+
if Harmonies.method_defined? name
|
110
|
+
raise Errors::PaletteDefinedError, "Palette `#{ name }' already exists"
|
111
|
+
end
|
112
|
+
|
113
|
+
palette_evaluator = PaletteBuilder.build(&block)
|
114
|
+
|
115
|
+
Harmonies.send(:define_method, name) do
|
116
|
+
palette_evaluator.evaluate(@color)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
def hex_named_colors_map
|
123
|
+
@hex_named_colors_map ||= named_colors_map.invert
|
124
|
+
end
|
125
|
+
|
126
|
+
def named_colors_map
|
127
|
+
@named_colors ||= YAML.load_file(File.expand_path('support/named_colors.yml', __dir__))
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|