redgreenblue 0.7.0 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,35 +1,66 @@
1
1
  class RGB
2
2
 
3
+ # Returns a 6-digit hexadecimal string representing the object's red, green, and blue components as 8-bit values.
4
+ #
5
+ # If shorthand is true, returns 3-digit shorthand if possible.
3
6
  def hex(shorthand=false)
4
7
  if shorthand
5
- RGB.hex_shorthand hexadecimal
8
+ RGB.hex_shorthand hex6
6
9
  else
7
- hexadecimal
10
+ hex6
8
11
  end
9
12
  end
10
13
 
11
- def hex=(s)
12
- if ( s =~ /^(#?)(\h\h)(\h\h)(\h\h)$/ ) # 6-digit hex
13
- self.rgb = [ $2.to_i(16), $3.to_i(16), $4.to_i(16) ]
14
- elsif ( s =~ /^(#?)(\h)(\h)(\h)$/ ) # 3-digit hex
15
- self.rgb = [ ($2*2).to_i(16), ($3*2).to_i(16), ($4*2).to_i(16) ]
14
+ # Sets red, green, and blue using a 6- or 3-digit hexadecimal string.
15
+ #
16
+ # The string may include a '#' prefix.
17
+ def hex=(hex_string)
18
+ if rgb = RGB.hex_to_rgb(hex_string)
19
+ self.rgb = rgb
20
+ else
21
+ raise ArgumentError, "'#{hex_string}' is not a usable hexadecimal string"
22
+ end
23
+ end
24
+
25
+
26
+ # Class methods
27
+
28
+
29
+ # Creates a new object from a 6- or 3-digit hexadecimal string representing red, green, and blue.
30
+ #
31
+ # The string may include a '#' prefix.
32
+ def self.hex(hex_string)
33
+ if rgb = RGB.hex_to_rgb(hex_string)
34
+ self.rgb rgb
35
+ else
36
+ raise ArgumentError, "'#{hex_string}' is not a usable hexadecimal string"
16
37
  end
17
38
  end
18
39
 
19
- # factory method
20
- def self.hex(s)
21
- c = self.new
22
- c.hex = s
23
- c
40
+ # Returns a 3-digit shorthand version of a 6-digit hexadecimal string.
41
+ #
42
+ # If a shorthand version is not possible, returns the original string.
43
+ def self.hex_shorthand(hex_string)
44
+ hex_string.sub( /^(#?)(\h)\2(\h)\3(\h)\4$/, '\1\2\3\4' )
24
45
  end
25
46
 
26
- def self.hex_shorthand(h)
27
- h.sub( /^(#?)(\h)\2(\h)\3(\h)\4$/, '\1\2\3\4' )
47
+ # Parses a 6- or 3-digit hexadecimal string.
48
+ # Returns three integers in the range 0..255, or nil if not successful.
49
+ #
50
+ # The string may include a '#' prefix.
51
+ def self.hex_to_rgb(hex_string)
52
+ if hex_string =~ /^(#?)(\h\h)(\h\h)(\h\h)$/
53
+ [$2, $3, $4].map { |h| h.to_i(16) }
54
+ elsif hex_string =~ /^(#?)(\h)(\h)(\h)$/
55
+ [$2, $3, $4].map { |h| (h*2).to_i(16) }
56
+ else
57
+ nil
58
+ end
28
59
  end
29
60
 
30
61
  private
31
62
 
32
- def hexadecimal
63
+ def hex6
33
64
  '%02x%02x%02x' % [ r, g, b ]
34
65
  end
35
66
 
@@ -0,0 +1,61 @@
1
+ require 'redgreenblue/hsv'
2
+
3
+ class RGB
4
+
5
+ #----------------------------------------------------------------------#
6
+ # Class Methods #
7
+ #----------------------------------------------------------------------#
8
+
9
+ class << self
10
+
11
+ # Creates a new RGB object from HSB values: hue (0..360), saturation (0..1), and brightness (0..1).
12
+ alias hsb hsv
13
+
14
+ end
15
+
16
+ #----------------------------------------------------------------------#
17
+ # Instance Methods #
18
+ #----------------------------------------------------------------------#
19
+
20
+ # Returns color as HSB:
21
+ # hue (0..360), saturation (0..1), brightness (0..1).
22
+ # When saturation is 0, hue is nil.
23
+ alias hsb hsv
24
+
25
+ # Returns the object's HSB-hue (0..360).
26
+ alias hsb_h hsv_h
27
+
28
+ # Returns the object's HSB-saturation (0..1).
29
+ alias hsb_s hsv_s
30
+
31
+ # Returns the object's HSB-brightness (0..1).
32
+ alias hsb_b hsv_v
33
+
34
+ # Sets red, green, and blue using HSB values: hue (0..360), saturation (0..1), and brightness (0..1).
35
+ alias hsb= hsv=
36
+
37
+ # Sets HSB-hue to a number of degrees (0..360) or nil.
38
+ #
39
+ # Adjusts red, green, and blue, leaving HSB-saturation and -brightness unchanged.
40
+ # When hue is nil, saturation will be 0.
41
+ alias hsb_h= hsv_h=
42
+
43
+ # Sets HSB-saturation to a number between 0 and 1.
44
+ #
45
+ # Adjusts red, green, and blue, leaving HSB-hue and -brightness unchanged.
46
+ # When saturation is 0, hue will be nil.
47
+ alias hsb_s= hsv_s=
48
+
49
+ # Sets HSB-brightness to a number between 0 and 1.
50
+ #
51
+ # Adjusts red, green, and blue, leaving HSB-hue and -saturation unchanged.
52
+ # When brightness is 0, hue will be nil, and saturation will be 0.
53
+ alias hsb_b= hsv_v=
54
+
55
+ # Sets red, green, and blue by rotating the object's HSB-hue a number of degrees.
56
+ alias hsb_rotate! hsv_rotate!
57
+
58
+ # Creates one or more new RGB objects by rotating this object's HSB-hue a number of degrees.
59
+ alias hsb_rotate hsv_rotate
60
+
61
+ end
@@ -0,0 +1,96 @@
1
+ require 'redgreenblue/hsx_shared'
2
+ require 'redgreenblue/math'
3
+
4
+ class RGB
5
+
6
+ #----------------------------------------------------------------------#
7
+ # Class Methods #
8
+ #----------------------------------------------------------------------#
9
+
10
+ class << self
11
+
12
+ # Creates a new RGB object from HSL values: hue (0..360), saturation (0..1), and lightness (0..1).
13
+ def hsl(*a)
14
+ new hsl_to_values(*a)
15
+ end
16
+
17
+ # Calculates RGB values from HSL.
18
+ # Given hue (0..360), saturation (0..1), and lightness (0..1),
19
+ # returns red, green, and blue as three values between 0 and 1.
20
+ def hsl_to_values(*a)
21
+ hsm_to_values(:hsl, a)
22
+ end
23
+
24
+ end
25
+
26
+ #----------------------------------------------------------------------#
27
+ # Instance Methods #
28
+ #----------------------------------------------------------------------#
29
+
30
+ # Returns color as HSL:
31
+ # hue (0..360), saturation (0..1), lightness (0..1).
32
+ # When saturation is 0, hue is nil.
33
+ def hsl
34
+ hsl_hsv_c[0]
35
+ end
36
+
37
+ # Returns the object's HSL-hue (0..360).
38
+ def hsl_h
39
+ hsl[0]
40
+ end
41
+
42
+ # Returns the object's HSL-saturation (0..1).
43
+ def hsl_s
44
+ hsl[1]
45
+ end
46
+
47
+ # Returns the object's HSL-lightness (0..1).
48
+ def hsl_l
49
+ hsl[2]
50
+ end
51
+
52
+ # Sets red, green, and blue using HSL values: hue (0..360), saturation (0..1), and lightness (0..1).
53
+ def hsl=(*a)
54
+ self.values = RGB.hsl_to_values(*a)
55
+ end
56
+
57
+ # Sets HSL-hue to a number of degrees (0..360) or nil.
58
+ #
59
+ # Adjusts red, green, and blue, leaving saturation and lightness unchanged.
60
+ # When hue is nil, saturation will be 0.
61
+ def hsl_h=(degrees)
62
+ self.hsl = hsl.fill(degrees,0,1)
63
+ end
64
+
65
+ # Sets HSL-saturation to a value between 0 and 1.
66
+ #
67
+ # Adjusts red, green, and blue, leaving hue and lightness unchanged.
68
+ # When saturation is 0, hue will be nil.
69
+ def hsl_s=(value)
70
+ self.hsl = hsl.fill(value ,1,1)
71
+ end
72
+
73
+ # Sets HSL-lightness to a value between 0 and 1.
74
+ #
75
+ # Adjusts red, green, and blue, leaving hue and saturation unchanged.
76
+ # When lightness is 0 or 1, hue will be nil, and saturation will be 0.
77
+ def hsl_l=(value)
78
+ self.hsl = hsl.fill(value ,2,1)
79
+ end
80
+
81
+ # Sets red, green, and blue by rotating the object's HSL-hue a number of degrees.
82
+ def hsl_rotate!(degrees)
83
+ self.hsl = zip_add(hsl, [degrees, 0, 0])
84
+ self
85
+ end
86
+
87
+ # Creates one or more new RGB objects by rotating this object's HSL-hue a number of degrees.
88
+ def hsl_rotate(a_degrees, *b_degrees)
89
+ if a_degrees.class != Array and b_degrees.none?
90
+ RGB.hsl zip_add(hsl, [a_degrees, 0, 0])
91
+ else
92
+ ( [a_degrees] + b_degrees ).flatten.map { |degrees| RGB.hsl zip_add(hsl, [degrees, 0, 0]) }
93
+ end
94
+ end
95
+
96
+ end
@@ -0,0 +1,96 @@
1
+ require 'redgreenblue/hsx_shared'
2
+ require 'redgreenblue/math'
3
+
4
+ class RGB
5
+
6
+ #----------------------------------------------------------------------#
7
+ # Class Methods #
8
+ #----------------------------------------------------------------------#
9
+
10
+ class << self
11
+
12
+ # Creates a new RGB object from HSV values: hue (0..360), saturation (0..1), and value (0..1).
13
+ def hsv(*a)
14
+ new hsv_to_values(*a)
15
+ end
16
+
17
+ # Calculates RGB values from HSV.
18
+ # Given hue (0..360), saturation (0..1), and value (0..1),
19
+ # returns red, green, and blue as three values between 0 and 1.
20
+ def hsv_to_values(*a)
21
+ hsm_to_values(:hsv, a)
22
+ end
23
+
24
+ end
25
+
26
+ #----------------------------------------------------------------------#
27
+ # Instance Methods #
28
+ #----------------------------------------------------------------------#
29
+
30
+ # Returns color as HSV:
31
+ # hue (0..360), saturation (0..1), value (0..1).
32
+ # When saturation is 0, hue is nil.
33
+ def hsv
34
+ hsl_hsv_c[1]
35
+ end
36
+
37
+ # Returns the object's HSV-hue (0..360).
38
+ def hsv_h
39
+ hsv[0]
40
+ end
41
+
42
+ # Returns the object's HSV-saturation (0..1).
43
+ def hsv_s
44
+ hsv[1]
45
+ end
46
+
47
+ # Returns the object's HSV-value (0..1).
48
+ def hsv_v
49
+ hsv[2]
50
+ end
51
+
52
+ # Sets red, green, and blue using HSV values: hue (0..360), saturation (0..1), and value (0..1).
53
+ def hsv=(*a)
54
+ self.values = RGB.hsv_to_values(*a)
55
+ end
56
+
57
+ # Sets HSV-hue to a number of degrees (0..360) or nil.
58
+ #
59
+ # Adjusts red, green, and blue, leaving HSV-saturation and -value unchanged.
60
+ # When hue is nil, saturation will be 0.
61
+ def hsv_h=(degrees)
62
+ self.hsv = hsv.fill(degrees,0,1)
63
+ end
64
+
65
+ # Sets HSV-saturation to a number between 0 and 1.
66
+ #
67
+ # Adjusts red, green, and blue, leaving HSV-hue and -value unchanged.
68
+ # When saturation is 0, hue will be nil.
69
+ def hsv_s=(value)
70
+ self.hsv = hsv.fill(value ,1,1)
71
+ end
72
+
73
+ # Sets HSV-value to a number between 0 and 1.
74
+ #
75
+ # Adjusts red, green, and blue, leaving HSV-hue and -saturation unchanged.
76
+ # When value is 0, hue will be nil, and saturation will be 0.
77
+ def hsv_v=(value)
78
+ self.hsv = hsv.fill(value ,2,1)
79
+ end
80
+
81
+ # Sets red, green, and blue by rotating the object's HSV-hue a number of degrees.
82
+ def hsv_rotate!(degrees)
83
+ self.hsv = zip_add(hsv, [degrees, 0, 0])
84
+ self
85
+ end
86
+
87
+ # Creates one or more new RGB objects by rotating this object's HSV-hue a number of degrees.
88
+ def hsv_rotate(a_degrees, *b_degrees)
89
+ if a_degrees.class != Array and b_degrees.none?
90
+ RGB.hsv zip_add(hsv, [a_degrees, 0, 0])
91
+ else
92
+ ( [a_degrees] + b_degrees ).flatten.map { |degrees| RGB.hsv zip_add(hsv, [degrees, 0, 0]) }
93
+ end
94
+ end
95
+
96
+ end
@@ -0,0 +1,94 @@
1
+ class RGB
2
+
3
+ #----------------------------------------------------------------------#
4
+ # Class Methods #
5
+ #----------------------------------------------------------------------#
6
+
7
+ class << self
8
+
9
+ private
10
+
11
+ # Calculates RGB values from HSL or HSV.
12
+ # With help from:
13
+ # - https://en.wikipedia.org/wiki/HSL_and_HSV
14
+ def hsm_to_values(type, *a)
15
+ raise NotImplementedError unless [:hsl, :hsv].include? type
16
+
17
+ hue, saturation, magnitude = a.flatten
18
+
19
+ if ( saturation == 0 ) or ( ! hue )
20
+ return Array.new(3) { magnitude }
21
+ end
22
+
23
+ hue = hue.modulo 360
24
+
25
+ chroma = case type
26
+ when :hsl
27
+ ( 1 - ( 2 * magnitude - 1 ).abs ) * saturation
28
+ when :hsv
29
+ magnitude * saturation
30
+ end
31
+
32
+ values = [
33
+ chroma,
34
+ ( 1 - ( ( hue / 60.0 ).modulo(2) - 1 ).abs ) * chroma,
35
+ 0
36
+ ]
37
+
38
+ values = case type
39
+ when :hsl
40
+ values.map { |v| ( v + magnitude - chroma / 2.0 ).round(9) }
41
+ when :hsv
42
+ values.map { |v| ( v + magnitude - chroma ).round(9) }
43
+ end
44
+
45
+ # order values according to hue sextant
46
+ [ [0,1,2], [1,0,2], [2,0,1], [2,1,0], [1,2,0], [0,2,1] ][hue.div 60].map { |i| values[i]}
47
+ end
48
+
49
+ end
50
+
51
+ #----------------------------------------------------------------------#
52
+ # Instance Methods #
53
+ #----------------------------------------------------------------------#
54
+
55
+ private
56
+
57
+ # Compute HSL, HSV, and chroma.
58
+ # With help from:
59
+ # - https://en.wikipedia.org/wiki/HSL_and_HSV
60
+ def hsl_hsv_c
61
+ sorted_hash = to_h
62
+ min, max = sorted_hash.values.values_at(2,0)
63
+
64
+ chroma = max - min
65
+
66
+ hue =
67
+ if chroma == 0
68
+ nil
69
+ else
70
+ ( case sorted_hash.keys.first
71
+ when :red
72
+ 60 * ( ( ( green - blue ) / chroma ).modulo 6 )
73
+ when :green
74
+ 60 * ( ( blue - red ) / chroma + 2 )
75
+ when :blue
76
+ 60 * ( ( red - green ) / chroma + 4 )
77
+ end
78
+ ).round(6)
79
+ end
80
+
81
+ lightness = ( min + max ) / 2.0
82
+
83
+ saturation_hsl =
84
+ chroma == 0 ? 0.0 : ( chroma / ( 1 - (2 * lightness - 1).abs ) ).round(9)
85
+
86
+ value = max
87
+
88
+ saturation_hsv =
89
+ value == 0 ? 0.0 : chroma / value
90
+
91
+ [ [hue, saturation_hsl, lightness], [hue, saturation_hsv, value], chroma ]
92
+ end
93
+
94
+ end
@@ -0,0 +1,73 @@
1
+ class RGB
2
+
3
+ private
4
+
5
+ def _inspect_default(prefix='RGB')
6
+ "#{prefix} #{_inspect_hex} (red=%1.5f green=%1.5f blue=%1.5f)" % [red, green, blue] + ( name ? ' ' + name : '' )
7
+ end
8
+
9
+ def _inspect_hex
10
+ (self == snap ? '#' : '~') + hex
11
+ end
12
+
13
+ def _inspect_swatch
14
+ terminal_background + " \e[0m"
15
+ end
16
+
17
+ def _inspect_short
18
+ "#{_inspect_swatch} #{_inspect_hex}"
19
+ end
20
+
21
+ def _inspect_simple
22
+ _inspect_default _inspect_swatch
23
+ end
24
+
25
+ def _inspect_name
26
+ _inspect_swatch + ( name ? ' ' + name : '' )
27
+ end
28
+
29
+ public
30
+
31
+ # Returns a programmer-friendly representation of the object.
32
+ #
33
+ # You can choose among several inspect styles. See the styles, style, and style= class methods.
34
+ def inspect
35
+ send "_inspect_#{self.class.style}"
36
+ end
37
+
38
+ # Returns a string representation of the object.
39
+ def to_s
40
+ _inspect_default
41
+ end
42
+
43
+ # Returns the base inspect style, dependent on the COLORTERM environment variable.
44
+ def self.base_style
45
+ if styles.include? ENV['REDGREENBLUE_STYLE']
46
+ ENV['REDGREENBLUE_STYLE']
47
+ else
48
+ if ENV['COLORTERM'] == 'truecolor'
49
+ 'simple'
50
+ else
51
+ 'default'
52
+ end
53
+ end
54
+ end
55
+
56
+ # Returns the current inspect style.
57
+ def self.style
58
+ @@style ||= base_style
59
+ end
60
+
61
+ # Returns a list of all available inspect styles.
62
+ def self.styles
63
+ ( self.instance_methods + self.private_instance_methods ).grep( /^_inspect_(.*)/ ) { $1 }.sort
64
+ end
65
+
66
+ # Selects an inspect style.
67
+ #
68
+ # Only the first few characters of your preferred style are required.
69
+ def self.style=(s)
70
+ @@style = styles.grep( /^#{s.to_s.downcase}/ ).first || style
71
+ end
72
+
73
+ end