red-colors 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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