kodachroma 1.0.0
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.
- 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
|