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.
@@ -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
@@ -3,11 +3,11 @@ class RGB
3
3
  private
4
4
 
5
5
  def _inspect_default(prefix='RGB')
6
- "#{prefix} ##{hex} (red=%1.5f green=%1.5f blue=%1.5f)" % [red, green, blue]
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
- "##{hex}"
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 + " ##{hex}"
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['COLORTERM'] == 'truecolor' ? 'simple' : 'default'
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.
@@ -1,53 +1,73 @@
1
1
  class RGB
2
2
 
3
- # Creates a white RGB object.
4
- def self.white
5
- new(1,1,1)
6
- end
3
+ #----------------------------------------------------------------------#
4
+ # Class Methods #
5
+ #----------------------------------------------------------------------#
7
6
 
8
- # Creates a black RGB object.
9
- def self.black
10
- new(0,0,0)
11
- end
7
+ class << self
12
8
 
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
9
+ # Creates a white RGB object.
10
+ def white
11
+ new(1,1,1)
12
+ end
19
13
 
20
- # Alias gray for grey.
21
- self.singleton_class.send(:alias_method, :gray, :grey)
14
+ # Creates a black RGB object.
15
+ def black
16
+ new(0,0,0)
17
+ end
22
18
 
23
- # Creates a pure red RGB object.
24
- def self.red
25
- new(1,0,0)
26
- end
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
- # Creates a pure green RGB object.
29
- def self.green
30
- new(0,1,0)
31
- end
26
+ # Alias gray for grey.
27
+ alias gray grey
32
28
 
33
- # Creates a pure blue RGB object.
34
- def self.blue
35
- new(0,0,1)
36
- end
29
+ # Creates a pure red RGB object.
30
+ def red
31
+ new(1,0,0)
32
+ end
37
33
 
38
- # Creates a yellow RGB object.
39
- def self.yellow
40
- new(1,1,0)
41
- end
34
+ # Creates a pure green RGB object.
35
+ def green
36
+ new(0,1,0)
37
+ end
42
38
 
43
- # Creates a cyan RGB object.
44
- def self.cyan
45
- new(0,1,1)
46
- end
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,8 @@
1
+ class RGB
2
+
3
+ # Returns the color in the format used by AppleScript (a 48-bit RGB triplet).
4
+ def applescript
5
+ "{%i, %i, %i}" % rrggbb
6
+ end
7
+
8
+ 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
@@ -0,0 +1,9 @@
1
+ class RGB
2
+
3
+ private
4
+
5
+ def zip_add(a,b)
6
+ a.zip(b).map { |ab| ( ab[0] || 0 ) + ab[1] }
7
+ end
8
+
9
+ end
@@ -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
@@ -16,9 +16,13 @@ class RGB
16
16
  mix!(RGB.white, portion)
17
17
  end
18
18
 
19
- # Creates a new RGB object by mixing this object's color with a portion of white.
20
- def whiten(portion=0.5)
21
- mix(RGB.white, portion)
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 a new RGB object by mixing this object's color with a portion of black.
30
- def blacken(portion=0.5)
31
- mix(RGB.black, portion)
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.