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
@@ -0,0 +1,22 @@
|
|
1
|
+
class RGB
|
2
|
+
|
3
|
+
# Returns the color as a 24-bit integer in the range 0..16777215.
|
4
|
+
def to_i
|
5
|
+
( r << 16 ) + ( g << 8 ) + b
|
6
|
+
end
|
7
|
+
|
8
|
+
# Creates a new RGB object from a 24-bit integer in the range 0..16777215.
|
9
|
+
def self.at(number)
|
10
|
+
n = number.to_i
|
11
|
+
if (0..16777215) === n
|
12
|
+
rgb(
|
13
|
+
( n & 0xff0000 ) >> 16,
|
14
|
+
( n & 0x00ff00 ) >> 8,
|
15
|
+
( n & 0x0000ff )
|
16
|
+
)
|
17
|
+
else
|
18
|
+
raise ArgumentError, "Argument '#{number}' not in range 0..16777215"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
data/lib/redgreenblue/lazy.rb
CHANGED
@@ -1,11 +1,53 @@
|
|
1
1
|
class RGB
|
2
2
|
|
3
|
+
# Creates a white RGB object.
|
3
4
|
def self.white
|
4
|
-
new(
|
5
|
+
new(1,1,1)
|
5
6
|
end
|
6
7
|
|
8
|
+
# Creates a black RGB object.
|
7
9
|
def self.black
|
8
|
-
new(
|
10
|
+
new(0,0,0)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Creates a grey RGB object. Defaults to lightness 0.5, a middle grey. Black equals grey(0), white equals grey(1).
|
14
|
+
#
|
15
|
+
# ::gray is an alias for ::grey.
|
16
|
+
def self.grey(lightness=0.5)
|
17
|
+
new(lightness, lightness, lightness)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Alias gray for grey.
|
21
|
+
self.singleton_class.send(:alias_method, :gray, :grey)
|
22
|
+
|
23
|
+
# Creates a pure red RGB object.
|
24
|
+
def self.red
|
25
|
+
new(1,0,0)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Creates a pure green RGB object.
|
29
|
+
def self.green
|
30
|
+
new(0,1,0)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Creates a pure blue RGB object.
|
34
|
+
def self.blue
|
35
|
+
new(0,0,1)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Creates a yellow RGB object.
|
39
|
+
def self.yellow
|
40
|
+
new(1,1,0)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Creates a cyan RGB object.
|
44
|
+
def self.cyan
|
45
|
+
new(0,1,1)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Creates a magenta RGB object.
|
49
|
+
def self.magenta
|
50
|
+
new(1,0,1)
|
9
51
|
end
|
10
52
|
|
11
53
|
end
|
data/lib/redgreenblue/misc.rb
CHANGED
@@ -1,60 +1,39 @@
|
|
1
1
|
class RGB
|
2
2
|
|
3
|
-
#
|
4
|
-
|
5
|
-
# Mix with second RGB color.
|
6
|
-
# p denotes portion of the mixed-in color to be used.
|
7
|
-
# p=0 delivers pure original color (no change),
|
8
|
-
# p=1 delivers pure mixed-in color.
|
9
|
-
|
10
|
-
def mix!(color,p=0.5)
|
11
|
-
self.values = mix_values(color.values, p)
|
12
|
-
self
|
13
|
-
end
|
14
|
-
|
15
|
-
def mix(color,p=0.5)
|
16
|
-
RGB.new mix_values(color.values, p)
|
17
|
-
end
|
18
|
-
|
19
|
-
# Invert
|
20
|
-
|
3
|
+
# Inverts the object's color.
|
21
4
|
def invert!
|
22
|
-
self.values = values.map { |v| 1-v }
|
5
|
+
self.values = values.map { |v| 1 - v }
|
23
6
|
self
|
24
7
|
end
|
25
8
|
|
9
|
+
# Creates a new RGB object with the inverted color of this RGB object.
|
26
10
|
def invert
|
27
11
|
dup.invert!
|
28
12
|
end
|
29
13
|
|
30
|
-
#
|
31
|
-
|
32
|
-
def
|
33
|
-
|
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
|
34
18
|
end
|
35
19
|
|
36
|
-
|
37
|
-
|
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.
|
21
|
+
#
|
22
|
+
# Example: RGB.red.permutation returns [ RGB.red, RGB.green, RGB.blue ].
|
23
|
+
# See also: shuffle.
|
24
|
+
def permutation
|
25
|
+
values.permutation.to_a.uniq.map { |v| RGB.new v }
|
38
26
|
end
|
39
27
|
|
40
|
-
#
|
41
|
-
|
42
|
-
|
43
|
-
mix!(RGB.black, p)
|
28
|
+
# Returns an array of three RGB objects, for the red, green, and blue components of this object.
|
29
|
+
def components
|
30
|
+
[ RGB.new(red,0,0), RGB.new(0,green,0), RGB.new(0,0,blue) ]
|
44
31
|
end
|
45
32
|
|
46
|
-
|
47
|
-
|
33
|
+
# Creates a new RGB object from three RGB objects representing the red, green, and blue components.
|
34
|
+
def self.assemble(*a)
|
35
|
+
v = a.flatten
|
36
|
+
RGB.new(v[0].red, v[1].green, v[2].blue)
|
48
37
|
end
|
49
38
|
|
50
|
-
private
|
51
|
-
|
52
|
-
def mix_values(v, p)
|
53
|
-
raise(ArgumentError, "Portion '#{p}' not in range (0..1)") unless (0..1).include? p
|
54
|
-
[
|
55
|
-
( red * (1-p) ) + ( v[0] * p ),
|
56
|
-
( green * (1-p) ) + ( v[1] * p ),
|
57
|
-
( blue * (1-p) ) + ( v[2] * p )
|
58
|
-
]
|
59
|
-
end
|
60
39
|
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
class RGB
|
2
|
+
|
3
|
+
# Changes the object's color by mixing it with a portion of another RGB color.
|
4
|
+
def mix!(another,portion=0.5)
|
5
|
+
self.values = mix_values(another.values, portion)
|
6
|
+
self
|
7
|
+
end
|
8
|
+
|
9
|
+
# Creates a new RGB object by mixing this object's color with a portion of another RGB color.
|
10
|
+
def mix(another,portion=0.5)
|
11
|
+
RGB.new mix_values(another.values, portion)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Changes the object's color by mixing it with a portion of white.
|
15
|
+
def whiten!(portion=0.5)
|
16
|
+
mix!(RGB.white, portion)
|
17
|
+
end
|
18
|
+
|
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
|
26
|
+
end
|
27
|
+
|
28
|
+
# Changes the object's color by mixing it with a portion of black.
|
29
|
+
def blacken!(portion=0.5)
|
30
|
+
mix!(RGB.black, portion)
|
31
|
+
end
|
32
|
+
|
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
|
40
|
+
end
|
41
|
+
|
42
|
+
# Returns a set of colors between this color and another. That other color is included.
|
43
|
+
#
|
44
|
+
# The resulting colors are spaced evenly in the RGB color space using a straightforward calculation.
|
45
|
+
# You will likely experience these colors as not exactly evenly spaced.
|
46
|
+
def steps(another,step_count=1,include_begin=false)
|
47
|
+
# origin (self, optional)
|
48
|
+
( include_begin ? [self.dup] : [] ) +
|
49
|
+
# ...plus intermediate colors
|
50
|
+
(1..step_count-1).map { |c| mix(another, c.to_f/step_count) } +
|
51
|
+
# ...plus destination color
|
52
|
+
[another.dup]
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def mix_values(some_values, portion)
|
58
|
+
raise(ArgumentError, "Portion '#{portion}' not in range (0..1)") unless (0..1).include? portion
|
59
|
+
[
|
60
|
+
( red * (1 - portion) ) + ( some_values[0] * portion ),
|
61
|
+
( green * (1 - portion) ) + ( some_values[1] * portion ),
|
62
|
+
( blue * (1 - portion) ) + ( some_values[2] * portion )
|
63
|
+
]
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# Optional support for Philips Hue lights.
|
2
|
+
#
|
3
|
+
# Conforms to Bridge API 1.35 for Philips Hue, published 20-Nov-2019.
|
4
|
+
|
5
|
+
# Automatically load core RGB class before loading options.
|
6
|
+
require 'redgreenblue'
|
7
|
+
|
8
|
+
class RGB
|
9
|
+
|
10
|
+
# Only available when optional support for Philips Hue lights is loaded.
|
11
|
+
#
|
12
|
+
# Returns a hash with the arguments required by the Philips Hue API,
|
13
|
+
# to set a light to this RGB object's hue, saturation and brightness.
|
14
|
+
#
|
15
|
+
# Formatted as JSON, this hash can be sent to a bridge to set a light's state.
|
16
|
+
#
|
17
|
+
# See (regrettably requires registration):
|
18
|
+
# - https://developers.meethue.com/develop/hue-api/
|
19
|
+
#
|
20
|
+
# @example Load
|
21
|
+
# require 'redgreenblue/opt/philipshue'
|
22
|
+
# @example Use
|
23
|
+
# RGB.magenta.to_philips_hue_api_hsb_arguments
|
24
|
+
# => {"on"=>true, "bri"=>254, "hue"=>54613, "sat"=>254}
|
25
|
+
# @return [Hash] API arguments
|
26
|
+
def to_philips_hue_api_hsb_arguments(black_off=true)
|
27
|
+
my_hsb = hsb
|
28
|
+
|
29
|
+
# Black means 'off'
|
30
|
+
if black_off and ( my_hsb[2] == 0 )
|
31
|
+
{ 'on' => false }
|
32
|
+
|
33
|
+
else
|
34
|
+
{
|
35
|
+
|
36
|
+
# On/Off state of the light
|
37
|
+
'on' => true,
|
38
|
+
|
39
|
+
# Brightness 1..254
|
40
|
+
# Note: a brightness of 1 will not switch the light off
|
41
|
+
'bri' => ( my_hsb[2] * 253 + 1 ).round,
|
42
|
+
|
43
|
+
# Hue 0..65535
|
44
|
+
'hue' => (( my_hsb[0] || 0 ) * 65535 / 360 ).round,
|
45
|
+
|
46
|
+
# Saturation 0..254
|
47
|
+
'sat' => ( my_hsb[1] * 254 ).round
|
48
|
+
|
49
|
+
}
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
data/lib/redgreenblue/os/mac.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
class RGB
|
2
2
|
|
3
|
+
# On Mac OS, shows the color picker to choose a color for the RGB object.
|
4
|
+
# Not available on other platforms.
|
3
5
|
def pick
|
4
6
|
result = RGB.mac_choose(rrggbb)
|
5
7
|
if result
|
@@ -7,10 +9,12 @@ class RGB
|
|
7
9
|
end
|
8
10
|
end
|
9
11
|
|
10
|
-
#
|
11
|
-
|
12
|
-
|
13
|
-
|
12
|
+
# On Mac OS, shows the color picker and creates an RGB object with the chosen color.
|
13
|
+
# Not available on other platforms.
|
14
|
+
#
|
15
|
+
# If no default color is specified, the picker defaults to a middle grey.
|
16
|
+
def self.pick(default_color=RGB.new)
|
17
|
+
result = RGB.mac_choose(default_color.rrggbb)
|
14
18
|
if result
|
15
19
|
RGB.rrggbb result
|
16
20
|
else
|
@@ -20,15 +24,25 @@ class RGB
|
|
20
24
|
|
21
25
|
private
|
22
26
|
|
23
|
-
#
|
24
|
-
# - requires a 48-bit RGB triplet [rr, gg, bb] for default choice.
|
25
|
-
#
|
26
|
-
# - Returns nil when <cancel> is clicked or <esc> key hit.
|
27
|
-
# - Otherwise returns 48-bit RGB triplet [rr, gg, bb].
|
27
|
+
# Uses Applescript to call the color picker on Mac OS.
|
28
|
+
# - requires a 48-bit RGB triplet [rr, gg, bb] for default choice.
|
29
|
+
#
|
30
|
+
# - Returns nil when <cancel> is clicked or <esc> key hit.
|
31
|
+
# - Otherwise returns 48-bit RGB triplet [rr, gg, bb].
|
32
|
+
#
|
33
|
+
# Applescript command documented here:
|
34
|
+
# Standard Additions -> User Interaction -> choose color
|
28
35
|
def self.mac_choose(color)
|
29
36
|
|
37
|
+
app = case ENV['TERM_PROGRAM']
|
38
|
+
when /iTerm\.app/
|
39
|
+
'iTerm'
|
40
|
+
else
|
41
|
+
'Terminal'
|
42
|
+
end
|
43
|
+
|
30
44
|
script = <<~ENDSCRIPT
|
31
|
-
tell application "
|
45
|
+
tell application "#{app}"
|
32
46
|
try
|
33
47
|
return choose color default color { #{color[0]}, #{color[1]}, #{color[2]} }
|
34
48
|
on error
|
@@ -0,0 +1,45 @@
|
|
1
|
+
class RGB
|
2
|
+
|
3
|
+
# Returns a new RGB object with this color's Ostwald full-color,
|
4
|
+
# or nil for achromatic colors (white, greys, and black).
|
5
|
+
#
|
6
|
+
# The resulting color contains no white or black,
|
7
|
+
# i.e. it has at least one RGB component set to 0, and at least one set to maximum.
|
8
|
+
#
|
9
|
+
# This is identical (barring very small rounding errors)
|
10
|
+
# to setting HSL-saturation to 1, and -lightness to 0.5
|
11
|
+
#
|
12
|
+
# Based on:
|
13
|
+
# - Color for the Sciences, pp. 575–591
|
14
|
+
# - https://lirias.kuleuven.be/retrieve/306124 (PDF)
|
15
|
+
def ostwald_color
|
16
|
+
white_portion = values.min
|
17
|
+
color_portion = values.max - white_portion
|
18
|
+
|
19
|
+
if color_portion == 0
|
20
|
+
nil
|
21
|
+
else
|
22
|
+
RGB.new( values.map { |v| ( ( v - white_portion ) / color_portion ).round(6) } )
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns the portions of Ostwald full-color, white, and black, which constitute this color.
|
27
|
+
#
|
28
|
+
# The sum of these three numbers equals 1.
|
29
|
+
#
|
30
|
+
# #cwk is an alias for #ostwald_cwk.
|
31
|
+
#
|
32
|
+
# Based on:
|
33
|
+
# - Color for the Sciences, pp. 575–591
|
34
|
+
# - https://lirias.kuleuven.be/retrieve/306124 (PDF)
|
35
|
+
def ostwald_cwk
|
36
|
+
[
|
37
|
+
color_portion = values.max - values.min,
|
38
|
+
white_portion = values.min,
|
39
|
+
1 - color_portion - white_portion
|
40
|
+
]
|
41
|
+
end
|
42
|
+
|
43
|
+
alias cwk ostwald_cwk
|
44
|
+
|
45
|
+
end
|
data/lib/redgreenblue/random.rb
CHANGED
@@ -1,17 +1,25 @@
|
|
1
1
|
class RGB
|
2
2
|
|
3
|
+
# Shuffles the object's red, green, and blue values.
|
3
4
|
def shuffle!
|
4
|
-
self.
|
5
|
+
self.values = values.shuffle
|
5
6
|
self
|
6
7
|
end
|
7
8
|
|
9
|
+
# Creates a new RGB object with this object's red, green, and blue values shuffled.
|
10
|
+
def shuffle
|
11
|
+
RGB.new values.shuffle
|
12
|
+
end
|
13
|
+
|
14
|
+
# Assigns random values to red, green, and blue.
|
8
15
|
def randomize!
|
9
|
-
self.
|
16
|
+
self.values = Kernel::rand, Kernel::rand, Kernel::rand
|
10
17
|
self
|
11
18
|
end
|
12
19
|
|
20
|
+
# Creates a new RGB object with random red, green, and blue values.
|
13
21
|
def self.rand
|
14
|
-
new(
|
22
|
+
new(Kernel::rand, Kernel::rand, Kernel::rand)
|
15
23
|
end
|
16
24
|
|
17
25
|
end
|
data/lib/redgreenblue/rgb565.rb
CHANGED
@@ -1,25 +1,24 @@
|
|
1
1
|
class RGB
|
2
2
|
|
3
|
+
# Returns the color in 16-bit RGB565 format.
|
3
4
|
def rgb565
|
4
5
|
[((r >> 3) << 11) + ((g >> 2) << 5) + (b >> 3)].pack 'S<'
|
5
6
|
end
|
6
7
|
|
7
|
-
#
|
8
|
-
|
9
|
-
|
8
|
+
# Sets the color from 16-bit RGB565 data.
|
9
|
+
# With help from:
|
10
|
+
# - https://stackoverflow.com/questions/2442576/
|
11
|
+
def rgb565=(rgb565_string)
|
12
|
+
v = ( rgb565_string.unpack "S<" )[0]
|
10
13
|
self.r = ( ( v & 0xf800 ) >> 11 ) << 3
|
11
14
|
self.g = ( ( v & 0x07e0 ) >> 5 ) << 2
|
12
15
|
self.b = ( ( v & 0x001f ) ) << 3
|
13
16
|
end
|
14
17
|
|
15
|
-
|
16
|
-
|
17
|
-
end
|
18
|
-
|
19
|
-
# factory method
|
20
|
-
def self.rgb565(s)
|
18
|
+
# Creates a new RGB color from 16-bit RGB565 data.
|
19
|
+
def self.rgb565(rgb565_string)
|
21
20
|
c = self.new
|
22
|
-
c.rgb565 =
|
21
|
+
c.rgb565 = rgb565_string
|
23
22
|
c
|
24
23
|
end
|
25
24
|
|