red-colors 0.3.0 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 41b270524325cebd81989dc494c23c3204517e6416d12c860c3bf678004fa0e6
4
- data.tar.gz: 605e7d7c3388fb95231e3fe24e3435799a631d8f8c2a4852741a449942ce7be7
3
+ metadata.gz: febec52016fd90dc6df804546e49649f6e887dbb80a200d95f97ca75cb558f09
4
+ data.tar.gz: 3535fdd548ae4cef6584f3991abca0e1e51e207f2e8e3dac19c0aae12ad44ab2
5
5
  SHA512:
6
- metadata.gz: 28bad73970d8198db20f0605b0766b3aba050a7487380784539a36143daa3b4bbe351776adf6497439648bc37336fce104eda9de40ab10be3d42dc720cfb63d7
7
- data.tar.gz: 6dbc678e7e3f8fa386bc1b5cd0adc941d3636b8a4c44f570f2eab919f41e966fafafb7cbdcbf347e379e7527fdd60923da0d7984764e48fff59df42f80d01d9b
6
+ metadata.gz: beb0692ed1779dbc6c33d6404ceee91c263a602536ad90edc3da61de0236985c4f40591ec38a9d4218972623311a5d218a4c43394617dfe0422e051e2ef415ce
7
+ data.tar.gz: 2aa6b5acaaa962cfee4ca23e315b3e6294eeda1a17937600d4d6ec9cc00c10deaefa8d9099a3e251371b9cf1e7beec9a5b77639499274d0394831c5b7af1f0ed
data/doc/text/news.md ADDED
@@ -0,0 +1,23 @@
1
+ # News
2
+
3
+ ## 0.4.0 - 2024-02-18
4
+
5
+ ### Improvements
6
+
7
+ * Added support for CSS3 syntax.
8
+ * GH-5
9
+ * GH-12
10
+ * Patch by Ben Bowers.
11
+
12
+ * Suppressed warnings.
13
+ * GH-16
14
+ * Reported by rubyFeedback.
15
+
16
+ ### Fixes
17
+
18
+ * Fixed wrong `Colors::Xterm256#to_grey_level` conversion.
19
+
20
+ ### Thanks
21
+
22
+ * Ben Bowers
23
+ * rubyFeedback
@@ -956,7 +956,7 @@ module Colors
956
956
  a_data = data[a_key]
957
957
  b_data = data[b_key]
958
958
  a_listed = a_data.is_a?(Hash) && a_data.key?(:listed)
959
- b_listed = a_data.is_a?(Hash) && a_data.key?(:listed)
959
+ b_listed = b_data.is_a?(Hash) && b_data.key?(:listed)
960
960
  case
961
961
  when !a_listed && b_listed
962
962
  -1
@@ -30,7 +30,6 @@ module Colors
30
30
  end
31
31
 
32
32
  if @registry.key?(name)
33
- existing = @registry[name]
34
33
  if BUILTIN_COLORMAPS.key?(name)
35
34
  unless override_builtin
36
35
  raise ArgumentError,
@@ -150,6 +150,12 @@ module Colors
150
150
  dot_product(RGB2XYZ, srgb_to_linear_srgb(r, g, b))
151
151
  end
152
152
 
153
+ def rgb_to_greyscale(r, g, b)
154
+ r, g, b = srgb_to_linear_srgb(r, g, b)
155
+ a = RGB2XYZ[1]
156
+ (a[0]*r + a[1]*g + a[2]*b).clamp(0, 1)
157
+ end
158
+
153
159
  def rgb_to_xterm256(r, g, b)
154
160
  i = closest_xterm256_rgb_index(r)
155
161
  j = closest_xterm256_rgb_index(g)
data/lib/colors/css.rb ADDED
@@ -0,0 +1,63 @@
1
+ module Colors
2
+ module CSS
3
+ # Factory method for generating RGB/RGBA/HSL/HSLA Objects.
4
+ # Parsing based on spec https://www.w3.org/TR/css-color-3 ; section 4.2
5
+ def self.parse(css_string)
6
+ error_message = "must be a string of `rgb(rr,gg,bb)`, `rgba(rr,gg,bb,aa)`, `hsl(hh,ss,ll)`, or `hsla(hh,ss,ll,aa)` form"
7
+ unless css_string.respond_to?(:to_str)
8
+ raise ArgumentError, "#{error_message}: #{css_string.inspect}"
9
+ end
10
+
11
+ css_string = css_string.to_str.strip
12
+
13
+ matcher = /\A((?:rgb|hsl)a?)\(([^)]+)\)\z/.match(css_string)
14
+ unless matcher
15
+ raise ArgumentError, "#{error_message}: #{css_string.inspect}"
16
+ end
17
+
18
+ color_space, args_string = matcher[1..2]
19
+ args = args_string.strip.split(/\s*,\s*/)
20
+ has_alpha = color_space.end_with?("a") # rgba/hsla
21
+ if has_alpha && args.length != 4
22
+ raise ArgumentError, "Expecting 4 fields for #{color_space}(): #{css_string.inspect}"
23
+ elsif !has_alpha && args.length != 3
24
+ raise ArgumentError, "Expecting 3 fields for #{color_space}(): #{css_string.inspect}"
25
+ end
26
+
27
+ case color_space
28
+ when "rgb", "rgba"
29
+ rgb, alpha = args[0, 3], args[3]
30
+ num_percent_vals = rgb.count {|v| v.end_with?("%") }
31
+ # CSS3 allows RGB values to be specified with all 3 as a percentage "##%"
32
+ # or all as integer values without '%' sign.
33
+ if num_percent_vals.zero?
34
+ # interpret as integer values in range of 0..255
35
+ r, g, b = rgb.map {|strval| strval.to_i.clamp(0, 255) }
36
+ # Note, RGBA.new expects all values to be integers or floats.
37
+ # For this case, we also turn the alpha-value into an int range 0..255
38
+ # to match the r,g,b values.
39
+ a = has_alpha && (alpha.to_r.clamp(0r, 1r) * 255).to_i
40
+ elsif num_percent_vals == 3
41
+ r, g, b = rgb.map {|strval| (strval.to_r / 100).clamp(0r, 1r) }
42
+ a = has_alpha && alpha.to_r.clamp(0r, 1r)
43
+ else
44
+ raise ArgumentError, "Invalid mix of percent and integer values: #{css_string.inspect}"
45
+ end
46
+ return has_alpha ? RGBA.new(r, g, b, a) : RGB.new(r, g, b)
47
+ when "hsl", "hsla"
48
+ hue, sat, light, alpha = *args
49
+ # CSS3 Hue values are an angle, unclear if we should convert to Integer or Rational here.
50
+ h = hue.to_r
51
+ s, l = [sat, light].map {|strval| (strval.to_r / 100).clamp(0r, 1r) }
52
+ if has_alpha
53
+ a = alpha.to_r.clamp(0r, 1r)
54
+ return HSLA.new(h, s, l, a)
55
+ else
56
+ return HSL.new(h, s, l)
57
+ end
58
+ else
59
+ raise ArgumentError, "Unknown color space: #{css_string.inspect}"
60
+ end
61
+ end
62
+ end
63
+ end
@@ -80,8 +80,6 @@ module Colors
80
80
  raise ArgumentError, "data array must consist of 3-length arrays"
81
81
  end
82
82
 
83
- shape = [ary.length, 3]
84
-
85
83
  x, y0, y1 = ary.transpose
86
84
 
87
85
  if x[0] != 0.0 || x[-1] != 1.0
@@ -1,3 +1,3 @@
1
1
  module Colors
2
- VERSION = "0.3.0"
2
+ VERSION = "0.4.0"
3
3
  end
@@ -42,7 +42,8 @@ module Colors
42
42
  def to_grey_level
43
43
  if code < 232
44
44
  r, g, b = to_rgb_components
45
- x, y, z = Convet.rgb_to_xyz(r, g, b)
45
+ y = Convert.rgb_to_greyscale(r, g, b)
46
+ canonicalize_component_to_rational(y, :grey)
46
47
  else
47
48
  grey = 10*(code - 232) + 8
48
49
  canonicalize_component_from_integer(grey, :grey)
data/lib/colors.rb CHANGED
@@ -4,6 +4,7 @@ require_relative "colors/convert"
4
4
  require_relative "colors/helper"
5
5
 
6
6
  require_relative "colors/abstract_color"
7
+ require_relative "colors/css"
7
8
  require_relative "colors/hsl"
8
9
  require_relative "colors/hsla"
9
10
  require_relative "colors/husl"
data/red-colors.gemspec CHANGED
@@ -33,6 +33,7 @@ Gem::Specification.new do |spec|
33
33
  spec.files += Dir.glob("doc/text/*")
34
34
  spec.test_files += Dir.glob("test/**/*")
35
35
 
36
+ spec.add_runtime_dependency("json")
36
37
  spec.add_runtime_dependency("matrix")
37
38
 
38
39
  spec.add_development_dependency("bundler")
data/test/run.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- # TODO
4
- # $VERBOSE = true
3
+ $VERBOSE = true
5
4
 
6
5
  require "pathname"
7
6
 
data/test/test-css.rb ADDED
@@ -0,0 +1,174 @@
1
+ class ColorsCSSParserTest < Test::Unit::TestCase
2
+ include TestHelper
3
+
4
+ sub_test_case("rgb") do
5
+ test("simple in-range integer values") do
6
+ ref = Colors::RGB.new(1, 0, 255)
7
+ css = Colors::CSS.parse("rgb(1,0,255)")
8
+ assert_near(ref, css)
9
+ end
10
+
11
+ test("clamps out-of-range integer values") do
12
+ ref = Colors::RGB.new(255, 0, 0)
13
+ css = Colors::CSS.parse("rgb( 300 ,0,0)")
14
+ assert_near(ref, css)
15
+ css = Colors::CSS.parse("rgb( 255 , -10,0)")
16
+ assert_near(ref, css)
17
+ end
18
+
19
+ test("in-range percent values") do
20
+ ref = Colors::RGB.new(0, 0.55, 1)
21
+ css = Colors::CSS.parse("rgb(0%,55%,100%)")
22
+ assert_near(ref, css)
23
+ end
24
+
25
+ test("clamps out-of-range percent values") do
26
+ ref = Colors::RGB.new(0r, 0r, 1r)
27
+ css = Colors::CSS.parse("rgb(0%,0%,110%)")
28
+ assert_near(ref, css)
29
+ css = Colors::CSS.parse("rgb(-10%,0%,100%)")
30
+ assert_near(ref, css)
31
+ end
32
+
33
+ test("bad input") do
34
+ assert_raises ArgumentError, "mixed pct/int" do
35
+ Colors::CSS.parse("rgb(50%,0 ,0)")
36
+ end
37
+ assert_raises ArgumentError, "too few args" do
38
+ Colors::CSS.parse("rgb(50%,0%)")
39
+ end
40
+ assert_raises ArgumentError, "too many args" do
41
+ Colors::CSS.parse("rgb(50%,0%,6%,0)")
42
+ end
43
+ assert_raises ArgumentError, "missing comma" do
44
+ Colors::CSS.parse("rgb(50% 0% 6%)")
45
+ end
46
+ end
47
+ end
48
+
49
+ sub_test_case("rgba") do
50
+ test("integer values, int alpha") do
51
+ ref = Colors::RGBA.new(1r, 0r, 1r, 1r)
52
+ css = Colors::CSS.parse("rgba(255,0,255,1)")
53
+ assert_near(ref, css)
54
+ ref = Colors::RGBA.new(1r, 0r, 1r, 0r)
55
+ css = Colors::CSS.parse("rgba(255,0,255,0)")
56
+ assert_near(ref, css)
57
+ end
58
+
59
+ test("integer values, float alpha") do
60
+ ref = Colors::RGBA.new(255, 0, 255, 127)
61
+ css = Colors::CSS.parse("rgba(255,0,255 , 0.5)")
62
+ assert_near(ref, css)
63
+ end
64
+
65
+ test("clamp out of range alpha values") do
66
+ ref = Colors::RGBA.new(1r, 0, 1r, 1r)
67
+ css = Colors::CSS.parse("rgba(255,0,255,2.0)")
68
+ assert_near(ref, css)
69
+ ref = Colors::RGBA.new(1r, 127/255r, 123/255r, 0.0)
70
+ css = Colors::CSS.parse("rgba(255, 127, 123,-0.1)")
71
+ assert_near(ref, css)
72
+ end
73
+
74
+ test("percent values, int alpha") do
75
+ ref = Colors::RGBA.new(1r, 0r, 0.25r, 1r)
76
+ css = Colors::CSS.parse("rgba(100%,0%,25%,1)")
77
+ assert_near(ref, css)
78
+ ref = Colors::RGBA.new(1r, 0.33r, 0.02r, 0r)
79
+ css = Colors::CSS.parse("rgba(100% , 33%,2%,0)")
80
+ assert_near(ref, css)
81
+ end
82
+
83
+ test("percent values, float alpha") do
84
+ ref = Colors::RGBA.new(0.5r, 0, 0.06, 1/2r)
85
+ css = Colors::CSS.parse("rgba(50%,0%,6% ,0.5)")
86
+ assert_near(ref, css)
87
+ end
88
+
89
+ test("bad input") do
90
+ assert_raises ArgumentError, "mixed pct/int" do
91
+ Colors::CSS.parse("rgba(50%,0,6 ,0.5)")
92
+ end
93
+ assert_raises ArgumentError, "too few args" do
94
+ Colors::CSS.parse("rgba(50%,0%,6%)")
95
+ end
96
+ assert_raises ArgumentError, "too many args" do
97
+ Colors::CSS.parse("rgba(50%,0%,6%,0.5, 0.5)")
98
+ end
99
+ assert_raises ArgumentError, "missing comma" do
100
+ Colors::CSS.parse("rgba(50%,0%,6% 0.5)")
101
+ end
102
+ end
103
+ end
104
+
105
+ sub_test_case("hsl") do
106
+ test("in-range hsl") do
107
+ ref = Colors::HSL.new(180, 0, 0.5)
108
+ css = Colors::CSS.parse("hsl( 180, 0% , 50% )")
109
+ assert_near(ref, css)
110
+ end
111
+
112
+ test("clamps out-of-range integer values") do
113
+ ref = Colors::HSL.new(180, 0, 0.5)
114
+ css = Colors::CSS.parse("hsl(-180,0%,50%)")
115
+ assert_near(ref, css)
116
+ css = Colors::CSS.parse("hsl(180,-10%,50%)")
117
+ assert_near(ref, css)
118
+ css = Colors::CSS.parse("hsl(540,-10%,50%)")
119
+ assert_near(ref, css)
120
+ ref = Colors::HSL.new(0, 1.0, 1.0)
121
+ css = Colors::CSS.parse("hsl(0,100%,100%)")
122
+ assert_near(ref, css)
123
+ css = Colors::CSS.parse("hsl(360,100%,100%)")
124
+ assert_near(ref, css)
125
+ end
126
+
127
+ test("bad input") do
128
+ assert_raises ArgumentError, "too many args" do
129
+ css = Colors::CSS.parse("hsl( 180, 0% , 50%, 50% )")
130
+ end
131
+ assert_raises ArgumentError, "too few args" do
132
+ css = Colors::CSS.parse("hsl( 180, 0%)")
133
+ end
134
+ assert_raises ArgumentError, "missing comma" do
135
+ css = Colors::CSS.parse("hsl( 180, 0% 50% )")
136
+ end
137
+ end
138
+ end
139
+
140
+ sub_test_case("hsla") do
141
+ test("in-range hsla") do
142
+ ref = Colors::HSLA.new(180, 0, 0.5, 1.0)
143
+ css = Colors::CSS.parse("hsla( 180, 0% , 50%,1.0)")
144
+ assert_near(ref, css)
145
+ end
146
+
147
+ test("clamps out-of-range integer values") do
148
+ ref = Colors::HSLA.new(180, 0, 0.5, 0.1)
149
+ css = Colors::CSS.parse("hsla(-180,0%,50%,0.1)")
150
+ assert_near(ref, css)
151
+ css = Colors::CSS.parse("hsla(180,-10%,50%, 0.1)")
152
+ assert_near(ref, css)
153
+ css = Colors::CSS.parse("hsla(540,-10%,50%, 0.1)")
154
+ assert_near(ref, css)
155
+ ref = Colors::HSLA.new(0, 1.0, 1.0, 0.0)
156
+ css = Colors::CSS.parse("hsla(0,100%,100%,0.0)")
157
+ assert_near(ref, css)
158
+ css = Colors::CSS.parse("hsla(360,100%,100%, 0.0)")
159
+ assert_near(ref, css)
160
+ end
161
+
162
+ test("bad input") do
163
+ assert_raises ArgumentError, "too many args" do
164
+ css = Colors::CSS.parse("hsla( 180, 0% , 50%, 0.0, 0.0 )")
165
+ end
166
+ assert_raises ArgumentError, "too few args" do
167
+ css = Colors::CSS.parse("hsla( 180, 0%, 0.0)")
168
+ end
169
+ assert_raises ArgumentError, "missing comma" do
170
+ css = Colors::CSS.parse("hsla( 180, 0% 50% 0.0 )")
171
+ end
172
+ end
173
+ end
174
+ end
@@ -44,7 +44,9 @@ class ColorsXterm256Test < Test::Unit::TestCase
44
44
  assert { Colors::Xterm256.new(16) != Colors::Xterm256.new(17) }
45
45
  end
46
46
 
47
- data do
47
+ RGB2GREY = [0.21263900587151035754r, 0.71516867876775592746r, 0.07219231536073371500r]
48
+
49
+ data(keep: true) do
48
50
  data_set = {}
49
51
  # colors 16-231 are a 6x6x6 color cube
50
52
  (0...6).each do |r|
@@ -55,7 +57,10 @@ class ColorsXterm256Test < Test::Unit::TestCase
55
57
  green = (g > 0) ? 40*g + 55 : g
56
58
  blue = (b > 0) ? 40*b + 55 : b
57
59
  label = "Color #{code} is rgb(#{red}, #{green}, #{blue})"
58
- data_set[label] = [code, Colors::RGB.new(red, green, blue)]
60
+ rgb = Colors::RGB.new(red, green, blue)
61
+ red, green, blue = Colors::Convert.srgb_to_linear_srgb(*rgb.components)
62
+ grey = RGB2GREY[0]*red + RGB2GREY[1]*green + RGB2GREY[2]*blue
63
+ data_set[label] = [code, rgb, grey]
59
64
  end
60
65
  end
61
66
  end
@@ -63,14 +68,23 @@ class ColorsXterm256Test < Test::Unit::TestCase
63
68
  (0...24).each do |y|
64
69
  code = 232 + y
65
70
  level = 10*y + 8
71
+ grey = level/255r
66
72
  label = "Color #{code} is gray(#{level})"
67
- data_set[label] = [code, Colors::RGB.new(level, level, level)]
73
+ data_set[label] = [code, Colors::RGB.new(level, level, level), grey]
68
74
  end
69
75
  data_set
70
76
  end
77
+
71
78
  def test_to_rgb(data)
72
- code, rgb = data
79
+ code, rgb, _grey = data
73
80
  assert_equal(rgb,
74
81
  Colors::Xterm256.new(code).to_rgb)
75
82
  end
83
+
84
+ def test_to_grey_level(data)
85
+ code, _rgb, grey = data
86
+ assert_in_delta(grey,
87
+ Colors::Xterm256.new(code).to_grey_level,
88
+ 1e-12)
89
+ end
76
90
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: red-colors
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kenta Murata
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-06-14 00:00:00.000000000 Z
11
+ date: 2024-02-18 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: json
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: matrix
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -124,6 +138,7 @@ files:
124
138
  - data/colormaps/twilight.json
125
139
  - data/colormaps/viridis.json
126
140
  - data/colormaps/vlag.json
141
+ - doc/text/news.md
127
142
  - lib/colors.rb
128
143
  - lib/colors/abstract_color.rb
129
144
  - lib/colors/alpha_component.rb
@@ -134,6 +149,7 @@ files:
134
149
  - lib/colors/colormap_data/seaborn_builtin.rb
135
150
  - lib/colors/colormap_registry.rb
136
151
  - lib/colors/convert.rb
152
+ - lib/colors/css.rb
137
153
  - lib/colors/helper.rb
138
154
  - lib/colors/hsl.rb
139
155
  - lib/colors/hsla.rb
@@ -151,6 +167,7 @@ files:
151
167
  - red-colors.gemspec
152
168
  - test/helper.rb
153
169
  - test/run.rb
170
+ - test/test-css.rb
154
171
  - test/test-hsl.rb
155
172
  - test/test-hsla.rb
156
173
  - test/test-husl.rb
@@ -165,7 +182,7 @@ homepage: https://github.com/red-data-tools/red-colors
165
182
  licenses:
166
183
  - MIT
167
184
  metadata: {}
168
- post_install_message:
185
+ post_install_message:
169
186
  rdoc_options: []
170
187
  require_paths:
171
188
  - lib
@@ -180,14 +197,15 @@ required_rubygems_version: !ruby/object:Gem::Requirement
180
197
  - !ruby/object:Gem::Version
181
198
  version: '0'
182
199
  requirements: []
183
- rubygems_version: 3.2.3
184
- signing_key:
200
+ rubygems_version: 3.5.1
201
+ signing_key:
185
202
  specification_version: 4
186
203
  summary: Red Colors provides a wide array of features for dealing with colors. This
187
204
  includes conversion between colorspaces, desaturation, and parsing colors.
188
205
  test_files:
189
206
  - test/helper.rb
190
207
  - test/run.rb
208
+ - test/test-css.rb
191
209
  - test/test-hsl.rb
192
210
  - test/test-hsla.rb
193
211
  - test/test-husl.rb