redgreenblue 0.9.0 → 0.14.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 +20 -20
- data/lib/redgreenblue/24bit.rb +11 -0
- data/lib/redgreenblue/base.rb +1 -1
- data/lib/redgreenblue/{cie.rb → cie_1931.rb} +15 -9
- data/lib/redgreenblue/cie_1976.rb +105 -0
- data/lib/redgreenblue/gpl.rb +94 -0
- 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/hwb.rb +14 -0
- data/lib/redgreenblue/inspect.rb +16 -4
- data/lib/redgreenblue/lazy.rb +59 -39
- data/lib/redgreenblue/mac.rb +8 -0
- data/lib/redgreenblue/match.rb +21 -0
- data/lib/redgreenblue/math.rb +9 -0
- data/lib/redgreenblue/misc.rb +19 -0
- data/lib/redgreenblue/mix.rb +14 -6
- data/lib/redgreenblue/name.rb +13 -0
- data/lib/redgreenblue/opt/philipshue.rb +15 -5
- data/lib/redgreenblue/os/mac.rb +4 -4
- data/lib/redgreenblue/ostwald.rb +45 -0
- data/lib/redgreenblue/palettes/css.gpl +164 -0
- data/lib/redgreenblue/rgb565.rb +0 -10
- data/lib/redgreenblue/version.rb +1 -1
- data/lib/redgreenblue/view.rb +34 -0
- data/lib/redgreenblue/web.rb +99 -0
- metadata +25 -10
- data/lib/redgreenblue/hsl_hsv.rb +0 -59
@@ -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,14 @@
|
|
1
|
+
class RGB
|
2
|
+
|
3
|
+
# Returns the color's hue (0..360), whiteness (0..1), and blackness (0..1), as defined by the HWB color model.
|
4
|
+
#
|
5
|
+
# For achromatic colors, hue is nil.
|
6
|
+
#
|
7
|
+
# Based on:
|
8
|
+
# - http://alvyray.com/Papers/CG/HWB_JGTv208.pdf (PDF)
|
9
|
+
# - https://en.wikipedia.org/wiki/HWB_color_model
|
10
|
+
def hwb
|
11
|
+
[ hsv_h, cwk[1,2] ].flatten
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
data/lib/redgreenblue/inspect.rb
CHANGED
@@ -3,11 +3,11 @@ class RGB
|
|
3
3
|
private
|
4
4
|
|
5
5
|
def _inspect_default(prefix='RGB')
|
6
|
-
"#{prefix}
|
6
|
+
"#{prefix} #{_inspect_hex} (red=%1.5f green=%1.5f blue=%1.5f)" % [red, green, blue] + ( name ? ' ' + name : '' )
|
7
7
|
end
|
8
8
|
|
9
9
|
def _inspect_hex
|
10
|
-
|
10
|
+
(self == snap ? '#' : '~') + hex
|
11
11
|
end
|
12
12
|
|
13
13
|
def _inspect_swatch
|
@@ -15,13 +15,17 @@ class RGB
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def _inspect_short
|
18
|
-
_inspect_swatch
|
18
|
+
"#{_inspect_swatch} #{_inspect_hex}"
|
19
19
|
end
|
20
20
|
|
21
21
|
def _inspect_simple
|
22
22
|
_inspect_default _inspect_swatch
|
23
23
|
end
|
24
24
|
|
25
|
+
def _inspect_name
|
26
|
+
_inspect_swatch + ( name ? ' ' + name : '' )
|
27
|
+
end
|
28
|
+
|
25
29
|
public
|
26
30
|
|
27
31
|
# Returns a programmer-friendly representation of the object.
|
@@ -38,7 +42,15 @@ class RGB
|
|
38
42
|
|
39
43
|
# Returns the base inspect style, dependent on the COLORTERM environment variable.
|
40
44
|
def self.base_style
|
41
|
-
ENV['
|
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
|
42
54
|
end
|
43
55
|
|
44
56
|
# Returns the current inspect style.
|
data/lib/redgreenblue/lazy.rb
CHANGED
@@ -1,53 +1,73 @@
|
|
1
1
|
class RGB
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
end
|
3
|
+
#----------------------------------------------------------------------#
|
4
|
+
# Class Methods #
|
5
|
+
#----------------------------------------------------------------------#
|
7
6
|
|
8
|
-
|
9
|
-
def self.black
|
10
|
-
new(0,0,0)
|
11
|
-
end
|
7
|
+
class << self
|
12
8
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
new(lightness, lightness, lightness)
|
18
|
-
end
|
9
|
+
# Creates a white RGB object.
|
10
|
+
def white
|
11
|
+
new(1,1,1)
|
12
|
+
end
|
19
13
|
|
20
|
-
|
21
|
-
|
14
|
+
# Creates a black RGB object.
|
15
|
+
def black
|
16
|
+
new(0,0,0)
|
17
|
+
end
|
22
18
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
19
|
+
# Creates a grey RGB object. Defaults to lightness 0.5, a middle grey. Black equals grey(0), white equals grey(1).
|
20
|
+
#
|
21
|
+
# ::gray is an alias for ::grey.
|
22
|
+
def grey(lightness=0.5)
|
23
|
+
new(lightness, lightness, lightness)
|
24
|
+
end
|
27
25
|
|
28
|
-
|
29
|
-
|
30
|
-
new(0,1,0)
|
31
|
-
end
|
26
|
+
# Alias gray for grey.
|
27
|
+
alias gray grey
|
32
28
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
29
|
+
# Creates a pure red RGB object.
|
30
|
+
def red
|
31
|
+
new(1,0,0)
|
32
|
+
end
|
37
33
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
34
|
+
# Creates a pure green RGB object.
|
35
|
+
def green
|
36
|
+
new(0,1,0)
|
37
|
+
end
|
42
38
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
39
|
+
# Creates a pure blue RGB object.
|
40
|
+
def blue
|
41
|
+
new(0,0,1)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Creates a yellow RGB object.
|
45
|
+
def yellow
|
46
|
+
new(1,1,0)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Creates a cyan RGB object.
|
50
|
+
def cyan
|
51
|
+
new(0,1,1)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Creates a magenta RGB object.
|
55
|
+
def magenta
|
56
|
+
new(1,0,1)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns the 8 corners of the RGB cube.
|
60
|
+
def corners
|
61
|
+
[ black, red, yellow, green, cyan, blue, magenta, white ]
|
62
|
+
end
|
63
|
+
|
64
|
+
# Returns the centre of the RGB cube.
|
65
|
+
def centre
|
66
|
+
grey
|
67
|
+
end
|
68
|
+
|
69
|
+
alias center centre
|
47
70
|
|
48
|
-
# Creates a magenta RGB object.
|
49
|
-
def self.magenta
|
50
|
-
new(1,0,1)
|
51
71
|
end
|
52
72
|
|
53
73
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class RGB
|
2
|
+
|
3
|
+
# Matches this color to a set of colors by calculating their euclidean distance.
|
4
|
+
#
|
5
|
+
# Returns the given set of colors with their distance from this color, sorted by distance (nearest color first).
|
6
|
+
# @example What is nearer: red, grey or white?
|
7
|
+
# RGB.hex('f9c').match_distance [RGB.red, RGB.grey, RGB.white]
|
8
|
+
def match_distance(others)
|
9
|
+
others.map { |c| [ c, distance(c) ] }.sort_by { |a| a[1] }
|
10
|
+
end
|
11
|
+
|
12
|
+
# Matches this color to a set of colors using the CIE 1976 delta E formula.
|
13
|
+
#
|
14
|
+
# Returns the given set of colors with their difference from this color, sorted by difference (nearest color first).
|
15
|
+
# @example Find the 3 nearest CSS named colors
|
16
|
+
# RGB.hex('f9c').match_de76(RGB.css).first(3)
|
17
|
+
def match_de76(others)
|
18
|
+
others.map { |c| [ c, de76(c) ] }.sort_by { |a| a[1] }
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
data/lib/redgreenblue/misc.rb
CHANGED
@@ -11,6 +11,12 @@ class RGB
|
|
11
11
|
dup.invert!
|
12
12
|
end
|
13
13
|
|
14
|
+
# Returns true when this is an achromatic color: red, green, and blue have equal values.
|
15
|
+
# Otherwise false.
|
16
|
+
def achromatic?
|
17
|
+
values.min == values.max
|
18
|
+
end
|
19
|
+
|
14
20
|
# Returns an array of RGB objects for all possible ways in which the red, green, and blue values of this object can be exchanged.
|
15
21
|
#
|
16
22
|
# Example: RGB.red.permutation returns [ RGB.red, RGB.green, RGB.blue ].
|
@@ -30,4 +36,17 @@ class RGB
|
|
30
36
|
RGB.new(v[0].red, v[1].green, v[2].blue)
|
31
37
|
end
|
32
38
|
|
39
|
+
# Returns the euclidean distance between this color and another color.
|
40
|
+
#
|
41
|
+
# When you imagine a color as a point in a 3-dimensional space,
|
42
|
+
# the dimensions being red, green, and blue,
|
43
|
+
# this is the distance between two colors.
|
44
|
+
def distance(another)
|
45
|
+
(
|
46
|
+
( (another.red - red ) ** 2) +
|
47
|
+
( (another.green - green) ** 2) +
|
48
|
+
( (another.blue - blue ) ** 2)
|
49
|
+
) ** (1/2.0)
|
50
|
+
end
|
51
|
+
|
33
52
|
end
|
data/lib/redgreenblue/mix.rb
CHANGED
@@ -16,9 +16,13 @@ class RGB
|
|
16
16
|
mix!(RGB.white, portion)
|
17
17
|
end
|
18
18
|
|
19
|
-
# Creates
|
20
|
-
def whiten(portion=0.5)
|
21
|
-
|
19
|
+
# Creates one or more new RGB objects by mixing this object's color with a portion of white.
|
20
|
+
def whiten(portion=0.5, *portions)
|
21
|
+
if (portion.class != Array) and portions.none?
|
22
|
+
mix(RGB.white, portion)
|
23
|
+
else
|
24
|
+
( [portion].flatten + portions ).map { |p| mix(RGB.white, p) }
|
25
|
+
end
|
22
26
|
end
|
23
27
|
|
24
28
|
# Changes the object's color by mixing it with a portion of black.
|
@@ -26,9 +30,13 @@ class RGB
|
|
26
30
|
mix!(RGB.black, portion)
|
27
31
|
end
|
28
32
|
|
29
|
-
# Creates
|
30
|
-
def blacken(portion=0.5)
|
31
|
-
|
33
|
+
# Creates one or more new RGB objects by mixing this object's color with a portion of black.
|
34
|
+
def blacken(portion=0.5, *portions)
|
35
|
+
if (portion.class != Array) and portions.none?
|
36
|
+
mix(RGB.black, portion)
|
37
|
+
else
|
38
|
+
( [portion].flatten + portions ).map { |p| mix(RGB.black, p) }
|
39
|
+
end
|
32
40
|
end
|
33
41
|
|
34
42
|
# Returns a set of colors between this color and another. That other color is included.
|