kodachroma 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+ module Kodachroma
3
+ module RgbGenerator
4
+ # Base rgb generator class.
5
+ # @abstract
6
+ class Base
7
+ include Helpers::Bounders
8
+ end
9
+ end
10
+ end
@@ -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
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ module Kodachroma
3
+ VERSION = '1.0.0'
4
+ 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