chroma 0.0.1.alpha.2 → 0.0.1.alpha.3

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.
@@ -3,11 +3,36 @@ module Chroma
3
3
  class << self
4
4
  private
5
5
 
6
- def build(*attrs)
7
- Class.new do
8
- attr_accessor *(attrs + [:a])
6
+ # Builds a new color mode class.
7
+ #
8
+ # @param name [String] the class name
9
+ # @param attrs [Array<Symbol>] the instance attribute names
10
+ # @!macro [attach] build
11
+ # @!parse class $1
12
+ # attr_accessor :$2, :$3, :$4, :a
13
+ #
14
+ # # @param $2 [Numeric]
15
+ # # @param $3 [Numeric]
16
+ # # @param $4 [Numeric]
17
+ # # @param a [Numeric]
18
+ # def initialize(${2-4}, a = 1)
19
+ # @$2, @$3, @$4, @a = $2, $3, $4, a
20
+ # end
21
+ #
22
+ # # Returns the values `$2`, `$3`, `$4`, and `a` as an array.
23
+ # #
24
+ # # @return [Array<Numeric>]
25
+ # def to_a
26
+ # [@$2, @$3, @$4, @a]
27
+ # end
28
+ #
29
+ # alias_method :to_ary, :to_a
30
+ # end
31
+ def build(name, *attrs)
32
+ class_eval <<-EOS
33
+ class #{name}
34
+ attr_accessor #{(attrs + [:a]).map{|attr| ":#{attr}"} * ', '}
9
35
 
10
- class_eval <<-EOS
11
36
  def initialize(#{attrs * ', '}, a = 1)
12
37
  #{attrs.map{|attr| "@#{attr}"} * ', '}, @a = #{attrs * ', '}, a
13
38
  end
@@ -17,13 +42,15 @@ module Chroma
17
42
  end
18
43
 
19
44
  alias_method :to_ary, :to_a
20
- EOS
21
- end
45
+ end
46
+ EOS
22
47
  end
23
48
  end
24
49
 
25
- Rgb = build :r, :g, :b
26
- Hsl = build :h, :s, :l
27
- Hsv = build :h, :s, :v
50
+ private
51
+
52
+ build 'Rgb', :r, :g, :b
53
+ build 'Hsl', :h, :s, :l
54
+ build 'Hsv', :h, :s, :v
28
55
  end
29
56
  end
@@ -1,20 +1,30 @@
1
1
  module Chroma
2
2
  module Converters
3
+ # Base class for converting one color mode to another.
4
+ # @abstract
3
5
  class Base
4
6
  include Helpers::Bounders
5
7
 
8
+ # @param input [ColorModes::Rgb, ColorModes::Hsl, ColorModes::Hsv]
9
+ # @return [Base]
6
10
  def initialize(input)
7
11
  @input = input
8
12
  end
9
13
 
14
+ # @param rgb [ColorModes::Rgb]
15
+ # @return [ColorModes::Rgb, ColorModes::Hsl, ColorModes::Hsv]
10
16
  def self.convert_rgb(rgb)
11
17
  new(rgb).convert_rgb
12
18
  end
13
19
 
20
+ # @param hsl [ColorModes::Hsl]
21
+ # @return [ColorModes::Rgb, ColorModes::Hsl, ColorModes::Hsv]
14
22
  def self.convert_hsl(hsl)
15
23
  new(hsl).convert_hsl
16
24
  end
17
25
 
26
+ # @param hsv [ColorModes::Hsv]
27
+ # @return [ColorModes::Rgb, ColorModes::Hsl, ColorModes::Hsv]
18
28
  def self.convert_hsv(hsv)
19
29
  new(hsv).convert_hsv
20
30
  end
@@ -1,6 +1,9 @@
1
1
  module Chroma
2
2
  module Converters
3
+ # Class to convert a color mode to {ColorModes::Hsl}.
3
4
  class HslConverter < Base
5
+ # Convert rgb to hsl.
6
+ # @return [ColorModes::Hsl]
4
7
  def convert_rgb
5
8
  r = bound01(@input.r, 255)
6
9
  g = bound01(@input.g, 255)
@@ -35,10 +38,14 @@ module Chroma
35
38
  ColorModes::Hsl.new(h * 360, s, l, @input.a)
36
39
  end
37
40
 
41
+ # Returns @input because it's the same color mode.
42
+ # @return [ColorModes::Hsl]
38
43
  def convert_hsl
39
44
  @input
40
45
  end
41
46
 
47
+ # Convert hsv to hsl.
48
+ # @return [ColorModes::Hsl]
42
49
  def convert_hsv
43
50
  HsvConverter.convert_rgb(RgbConverter.convert_hsl(@input))
44
51
  end
@@ -1,6 +1,9 @@
1
1
  module Chroma
2
2
  module Converters
3
+ # Class to convert a color mode to {ColorModes::Hsl}.
3
4
  class HsvConverter < Base
5
+ # Convert rgb to hsv.
6
+ # @return [ColorModes::Hsv]
4
7
  def convert_rgb
5
8
  r = bound01(@input.r, 255)
6
9
  g = bound01(@input.g, 255)
@@ -29,10 +32,14 @@ module Chroma
29
32
  ColorModes::Hsv.new(h * 360, s, v, @input.a)
30
33
  end
31
34
 
35
+ # Convert hsl to hsv.
36
+ # @return [ColorModes::Hsv]
32
37
  def convert_hsl
33
38
  HslConverter.convert_rgb(RgbConverter.convert_hsv(@input))
34
39
  end
35
40
 
41
+ # Returns @input because it's the same color mode.
42
+ # @return [ColorModes::Hsv]
36
43
  def convert_hsv
37
44
  @input
38
45
  end
@@ -1,10 +1,15 @@
1
1
  module Chroma
2
2
  module Converters
3
+ # Class to convert a color mode to {ColorModes::Rgb}.
3
4
  class RgbConverter < Base
5
+ # Returns @input because it's the same color mode.
6
+ # @return [ColorModes::Rgb]
4
7
  def convert_rgb
5
8
  @input
6
9
  end
7
10
 
11
+ # Convert hsl to rgb.
12
+ # @return [ColorModes::Rgb]
8
13
  def convert_hsl
9
14
  h, s, l = @input
10
15
 
@@ -25,6 +30,8 @@ module Chroma
25
30
  ColorModes::Rgb.new(r, g, b, bound_alpha(@input.a))
26
31
  end
27
32
 
33
+ # Convert hsv to rgb.
34
+ # @return [ColorModes::Rgb]
28
35
  def convert_hsv
29
36
  h, s, v = @input
30
37
 
@@ -0,0 +1,6 @@
1
+ module Chroma
2
+ module Errors
3
+ class PaletteDefinedError < StandardError; end
4
+ class UnrecognizedColor < StandardError; end
5
+ end
6
+ end
@@ -1,4 +1,15 @@
1
1
  class String
2
+ # Creates {Chroma::Color} directly from a string representing a color.
3
+ #
4
+ # @example
5
+ # 'red'.paint
6
+ # '#f00'.paint
7
+ # '#ff0000'.paint
8
+ # 'rgb(255, 0, 0)'.paint
9
+ # 'hsl(0, 100%, 50%)'.paint
10
+ # 'hsv(0, 100%, 100%)'.paint
11
+ #
12
+ # @return [Chroma::Color]
2
13
  def paint
3
14
  Chroma.paint(self)
4
15
  end
@@ -1,55 +1,138 @@
1
1
  module Chroma
2
+ # Class to hold all palette methods.
2
3
  class Harmonies
4
+ # @param color [Color]
3
5
  def initialize(color)
4
6
  @color = color
5
7
  end
6
8
 
7
- def complement
8
- [@color, @color.complement]
9
+ # Generate a complement palette.
10
+ #
11
+ # @example
12
+ # 'red'.paint.palette.complement #=> [red, cyan]
13
+ # 'red'.paint.palette.complement(as: :name) #=> ['red', 'cyan']
14
+ # 'red'.paint.palette.complement(as: :hex) #=> ['#ff0000', '#00ffff']
15
+ #
16
+ # @param options [Hash]
17
+ # @option options :as [Symbol] (nil) optional format to output colors as strings
18
+ # @return [Array<Color>, Array<String>] depending on presence of `options[:as]`
19
+ def complement(options = {})
20
+ with_reformat([@color, @color.complement], options[:as])
9
21
  end
10
22
 
11
- def triad
12
- hsl_map([0, 120, 240])
23
+ # Generate a triad palette.
24
+ #
25
+ # @example
26
+ # 'red'.paint.palette.triad #=> [red, lime, blue]
27
+ # 'red'.paint.palette.triad(as: :name) #=> ['red', 'lime', 'blue']
28
+ # 'red'.paint.palette.triad(as: :hex) #=> ['#ff0000', '#00ff00', '#0000ff']
29
+ #
30
+ # @param options [Hash]
31
+ # @option options :as [Symbol] (nil) optional format to output colors as strings
32
+ # @return [Array<Color>, Array<String>] depending on presence of `options[:as]`
33
+ def triad(options = {})
34
+ hsl_map([0, 120, 240], options)
13
35
  end
14
36
 
15
- def tetrad
16
- hsl_map([0, 90, 180, 270])
37
+ # Generate a tetrad palette.
38
+ #
39
+ # @example
40
+ # 'red'.paint.palette.tetrad #=> [red, #80ff00, cyan, #7f00ff]
41
+ # 'red'.paint.palette.tetrad(as: :name) #=> ['red', '#80ff00', 'cyan', '#7f00ff']
42
+ # 'red'.paint.palette.tetrad(as: :hex) #=> ['#ff0000', '#80ff00', '#00ffff', '#7f00ff']
43
+ #
44
+ # @param options [Hash]
45
+ # @option options :as [Symbol] (nil) optional format to output colors as strings
46
+ # @return [Array<Color>, Array<String>] depending on presence of `options[:as]`
47
+ def tetrad(options = {})
48
+ hsl_map([0, 90, 180, 270], options)
17
49
  end
18
50
 
19
- def split_complement
20
- hsl_map([0, 72, 216])
51
+ # Generate a split complement palette.
52
+ #
53
+ # @example
54
+ # 'red'.paint.palette.split_complement #=> [red, #ccff00, #0066ff]
55
+ # 'red'.paint.palette.split_complement(as: :name) #=> ['red', '#ccff00', '#0066ff']
56
+ # 'red'.paint.palette.split_complement(as: :hex) #=> ['#ff0000', '#ccff00', '#0066ff']
57
+ #
58
+ # @param options [Hash]
59
+ # @option options :as [Symbol] (nil) optional format to output colors as strings
60
+ # @return [Array<Color>, Array<String>] depending on presence of `options[:as]`
61
+ def split_complement(options = {})
62
+ hsl_map([0, 72, 216], options)
21
63
  end
22
64
 
23
- def analogous(results = 6, slices = 30)
65
+ # Generate an analogous palette.
66
+ #
67
+ # @example
68
+ # 'red'.paint.palette.analogous #=> [red, #ff0066, #ff0033, red, #ff3300, #ff6600]
69
+ # 'red'.paint.palette.analogous(as: :hex) #=> ['#f00', '#f06', '#f03', '#f00', '#f30', '#f60']
70
+ # 'red'.paint.palette.analogous(results: 3) #=> [red, #ff001a, #ff1a00]
71
+ # 'red'.paint.palette.analogous(results: 3, slices: 60) #=> [red, #ff000d, #ff0d00]
72
+ #
73
+ # @param options [Hash]
74
+ # @option options :results [Symbol] (6) number of results to return
75
+ # @option options :slices [Symbol] (30)
76
+ # the angle in degrees to slice the hue circle per color
77
+ # @option options :as [Symbol] (nil) optional format to output colors as strings
78
+ # @return [Array<Color>, Array<String>] depending on presence of `options[:as]`
79
+ def analogous(options = {})
80
+ results = options[:results] || 6
81
+ slices = options[:slices] || 30
82
+
24
83
  hsl = @color.hsl
25
84
  part = 360 / slices
26
85
  hsl.h = ((hsl.h - (part * results >> 1)) + 720) % 360
27
86
 
28
- (results - 1).times.reduce([@color]) do |arr, n|
87
+ palette = (results - 1).times.reduce([@color]) do |arr, n|
29
88
  hsl.h = (hsl.h + part) % 360
30
- arr << Color.new(hsl)
89
+ arr << Color.new(hsl, @color.format)
31
90
  end
91
+
92
+ with_reformat(palette, options[:as])
32
93
  end
33
94
 
34
- def monochromatic(results = 6)
95
+ # Generate a monochromatic palette.
96
+ #
97
+ # @example
98
+ # 'red'.paint.palette.monochromatic #=> [red, #2a0000, #550000, maroon, #aa0000, #d40000]
99
+ # 'red'.paint.palette.monochromatic(as: :hex) #=> ['#ff0000', '#2a0000', '#550000', '#800000', '#aa0000', '#d40000']
100
+ # 'red'.paint.palette.monochromatic(results: 3) #=> [red, #550000, #aa0000]
101
+ #
102
+ # @param options [Hash]
103
+ # @option options :results [Symbol] (6) number of results to return
104
+ # @option options :as [Symbol] (nil) optional format to output colors as strings
105
+ # @return [Array<Color>, Array<String>] depending on presence of `options[:as]`
106
+ def monochromatic(options = {})
107
+ results = options[:results] || 6
108
+
35
109
  h, s, v = @color.hsv
36
110
  modification = 1.0 / results
37
111
 
38
- results.times.map do
39
- Color.new(ColorModes::Hsv.new(h, s, v)).tap do
112
+ palette = results.times.map do
113
+ Color.new(ColorModes::Hsv.new(h, s, v), @color.format).tap do
40
114
  v = (v + modification) % 1
41
115
  end
42
116
  end
117
+
118
+ with_reformat(palette, options[:as])
43
119
  end
44
120
 
45
121
  private
46
122
 
47
- def hsl_map(degrees)
123
+ def with_reformat(palette, as)
124
+ palette.map! { |color| color.to_s(as) } unless as.nil?
125
+ palette
126
+ end
127
+
128
+ def hsl_map(degrees, options)
48
129
  h, s, l = @color.hsl
49
130
 
50
- degrees.map do |deg|
51
- Color.new(ColorModes::Hsl.new((h + deg) % 360, s, l))
131
+ degrees.map! do |deg|
132
+ Color.new(ColorModes::Hsl.new((h + deg) % 360, s, l), @color.format)
52
133
  end
134
+
135
+ with_reformat(degrees, options[:as])
53
136
  end
54
137
  end
55
138
  end
@@ -1,26 +1,44 @@
1
1
  module Chroma
2
2
  module Helpers
3
3
  module Bounders
4
+ # Bounds a value `n` that is from `0` to `max` to `0` to `1`.
5
+ #
6
+ # @param n [Numeric, String]
7
+ # @param max [Fixnum]
8
+ # @return [Float]
4
9
  def bound01(n, max)
5
10
  is_percent = n.to_s.include? '%'
6
11
  n = [max, [0, n.to_f].max].min
7
- n = (n * max).to_i / 100 if is_percent
12
+ n = (n * max).to_i / 100.0 if is_percent
8
13
 
9
14
  return 1 if (n - max).abs < 0.000001
10
15
 
11
16
  (n % max) / max.to_f
12
17
  end
13
18
 
19
+ # Ensure alpha value `a` is between `0` and `1`.
20
+ #
21
+ # @param a [Numeric, String] alpha value
22
+ # @return [Numeric]
14
23
  def bound_alpha(a)
15
24
  a = a.to_f
16
25
  a = 1 if a < 0 || a > 1
17
26
  a
18
27
  end
19
28
 
29
+ # Ensures a number between `0` and `1`. Returns `n` if it is between `0`
30
+ # and `1`.
31
+ #
32
+ # @param n [Numeric]
33
+ # @return [Numeric]
20
34
  def clamp01(n)
21
35
  [1, [0, n].max].min
22
36
  end
23
37
 
38
+ # Converts `n` to a percentage type value.
39
+ #
40
+ # @param n [Numeric, String]
41
+ # @return [String, Float]
24
42
  def to_percentage(n)
25
43
  n = n.to_f
26
44
  n = "#{n * 100}%" if n <= 1
@@ -1,14 +1,25 @@
1
1
  module Chroma
2
+ # Class internally used to build custom palettes from {Chroma.define_palette}.
2
3
  class PaletteBuilder
4
+ # Wrapper to instantiate a new instance of {PaletteBuilder} and call its
5
+ # {PaletteBuilder#build} method.
6
+ #
7
+ # @param name [Symbol, String] the name of the custom palette
8
+ # @param block [Proc] the palette definition block
9
+ # @return [Symbol, String] the name of the custom palette
3
10
  def self.build(name, &block)
4
11
  new(name, &block).build
5
12
  end
6
13
 
14
+ # @param name [Symbol, String] the name of the custom palette
15
+ # @param block [Proc] the palette definition block
7
16
  def initialize(name, &block)
8
17
  @name = name
9
18
  @block = block
10
19
  end
11
20
 
21
+ # Build the custom palette by defining a new method on {Harmonies}.
22
+ # @return [Symbol, String] the name of the custom palette
12
23
  def build
13
24
  dsl = PaletteBuilderDsl.new
14
25
  dsl.instance_eval(&@block)
@@ -23,6 +34,7 @@ module Chroma
23
34
 
24
35
  private
25
36
 
37
+ # Internal class for palette building DSL syntax.
26
38
  class PaletteBuilderDsl
27
39
  attr_reader :conversions
28
40
 
@@ -36,6 +48,8 @@ module Chroma
36
48
  end
37
49
  end
38
50
 
51
+ # Internal class to represent color modification calls in the palette
52
+ # builder DSL definition syntax.
39
53
  class ColorCalls
40
54
  attr_reader :name, :args
41
55
 
@@ -1,6 +1,13 @@
1
1
  module Chroma
2
+ # Main module to generate an instance of {ColorModes::Rgb} from several
3
+ # possible inputs.
2
4
  module RgbGenerator
3
5
  class << self
6
+ # Generates an instance of {ColorModes::Rgb} as well as color format
7
+ # symbol.
8
+ #
9
+ # @param input [String, ColorModes::Rgb, ColorModes::Hsl, ColorModes::Hsv]
10
+ # @return [[ColorModes::Rgb, Symbol]]
4
11
  def generate_rgb_and_format(input)
5
12
  get_generator(input).generate.tap do |(rgb)|
6
13
  rgb.r = round(rgb.r)
@@ -23,12 +30,7 @@ module Chroma
23
30
  end
24
31
 
25
32
  def round(n)
26
- if n < 1
27
- n.round
28
- else
29
- #(n * 100).round / 100
30
- n
31
- end
33
+ n < 1 ? n.round : n
32
34
  end
33
35
  end
34
36
  end