redgreenblue 0.6.0 → 0.11.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 +4 -4
- data/lib/redgreenblue.rb +17 -13
- data/lib/redgreenblue/24bit.rb +38 -5
- data/lib/redgreenblue/48bit.rb +12 -5
- data/lib/redgreenblue/base.rb +58 -12
- data/lib/redgreenblue/bgr24bit.rb +5 -5
- data/lib/redgreenblue/cie.rb +45 -0
- data/lib/redgreenblue/gamma.rb +44 -0
- data/lib/redgreenblue/gif.rb +3 -2
- data/lib/redgreenblue/gpl.rb +30 -0
- data/lib/redgreenblue/hex.rb +46 -15
- data/lib/redgreenblue/hsb.rb +61 -0
- data/lib/redgreenblue/hsl.rb +96 -0
- data/lib/redgreenblue/hsv.rb +96 -0
- data/lib/redgreenblue/hsx_shared.rb +94 -0
- data/lib/redgreenblue/inspect.rb +69 -0
- data/lib/redgreenblue/int.rb +22 -0
- data/lib/redgreenblue/lazy.rb +44 -2
- data/lib/redgreenblue/math.rb +9 -0
- data/lib/redgreenblue/misc.rb +20 -41
- data/lib/redgreenblue/mix.rb +65 -0
- data/lib/redgreenblue/opt/philipshue.rb +54 -0
- data/lib/redgreenblue/os/mac.rb +24 -10
- data/lib/redgreenblue/ostwald.rb +45 -0
- data/lib/redgreenblue/random.rb +11 -3
- data/lib/redgreenblue/rgb565.rb +9 -10
- data/lib/redgreenblue/terminal.rb +19 -0
- data/lib/redgreenblue/version.rb +9 -1
- data/lib/redgreenblue/web.rb +10 -0
- metadata +23 -6
- data/lib/redgreenblue/nice.rb +0 -7
data/lib/redgreenblue/hex.rb
CHANGED
@@ -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
|
8
|
+
RGB.hex_shorthand hex6
|
6
9
|
else
|
7
|
-
|
10
|
+
hex6
|
8
11
|
end
|
9
12
|
end
|
10
13
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
-
#
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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
|
-
|
27
|
-
|
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
|
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,69 @@
|
|
1
|
+
class RGB
|
2
|
+
|
3
|
+
private
|
4
|
+
|
5
|
+
def _inspect_default(prefix='RGB')
|
6
|
+
"#{prefix} ##{hex} (red=%1.5f green=%1.5f blue=%1.5f)" % [red, green, blue]
|
7
|
+
end
|
8
|
+
|
9
|
+
def _inspect_hex
|
10
|
+
"##{hex}"
|
11
|
+
end
|
12
|
+
|
13
|
+
def _inspect_swatch
|
14
|
+
terminal_background + " \e[0m"
|
15
|
+
end
|
16
|
+
|
17
|
+
def _inspect_short
|
18
|
+
_inspect_swatch + " ##{hex}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def _inspect_simple
|
22
|
+
_inspect_default _inspect_swatch
|
23
|
+
end
|
24
|
+
|
25
|
+
public
|
26
|
+
|
27
|
+
# Returns a programmer-friendly representation of the object.
|
28
|
+
#
|
29
|
+
# You can choose among several inspect styles. See the styles, style, and style= class methods.
|
30
|
+
def inspect
|
31
|
+
send "_inspect_#{self.class.style}"
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns a string representation of the object.
|
35
|
+
def to_s
|
36
|
+
_inspect_default
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns the base inspect style, dependent on the COLORTERM environment variable.
|
40
|
+
def self.base_style
|
41
|
+
if styles.include? ENV['REDGREENBLUE_STYLE']
|
42
|
+
ENV['REDGREENBLUE_STYLE']
|
43
|
+
else
|
44
|
+
if ENV['COLORTERM'] == 'truecolor'
|
45
|
+
'simple'
|
46
|
+
else
|
47
|
+
'default'
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns the current inspect style.
|
53
|
+
def self.style
|
54
|
+
@@style ||= base_style
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns a list of all available inspect styles.
|
58
|
+
def self.styles
|
59
|
+
( self.instance_methods + self.private_instance_methods ).grep( /^_inspect_(.*)/ ) { $1 }.sort
|
60
|
+
end
|
61
|
+
|
62
|
+
# Selects an inspect style.
|
63
|
+
#
|
64
|
+
# Only the first few characters of your preferred style are required.
|
65
|
+
def self.style=(s)
|
66
|
+
@@style = styles.grep( /^#{s.to_s.downcase}/ ).first || style
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|