red-colors 0.2.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 +4 -4
- data/data/colormaps/cividis.json +258 -0
- data/data/colormaps/coolwarm.json +107 -0
- data/data/colormaps/crest.json +258 -0
- data/data/colormaps/flare.json +258 -0
- data/data/colormaps/gist_earth.json +49 -0
- data/data/colormaps/gist_ncar.json +55 -0
- data/data/colormaps/icefire.json +258 -0
- data/data/colormaps/inferno.json +258 -0
- data/data/colormaps/magma.json +258 -0
- data/data/colormaps/mako.json +258 -0
- data/data/colormaps/nipy_spectral.json +71 -0
- data/data/colormaps/pink.json +200 -0
- data/data/colormaps/plasma.json +258 -0
- data/data/colormaps/rocket.json +258 -0
- data/data/colormaps/turbo.json +258 -0
- data/data/colormaps/twilight.json +512 -0
- data/data/colormaps/viridis.json +258 -0
- data/data/colormaps/vlag.json +258 -0
- data/doc/text/news.md +23 -0
- data/lib/colors/colormap.rb +12 -0
- data/lib/colors/colormap_data/matplotlib_builtin.rb +990 -0
- data/lib/colors/colormap_data/seaborn_builtin.rb +10 -0
- data/lib/colors/colormap_data.rb +44 -0
- data/lib/colors/colormap_registry.rb +61 -0
- data/lib/colors/convert.rb +6 -0
- data/lib/colors/css.rb +63 -0
- data/lib/colors/helper.rb +2 -1
- data/lib/colors/linear_segmented_colormap.rb +19 -3
- data/lib/colors/listed_colormap.rb +4 -0
- data/lib/colors/version.rb +1 -1
- data/lib/colors/xterm256.rb +2 -1
- data/lib/colors.rb +4 -0
- data/red-colors.gemspec +2 -0
- data/test/run.rb +1 -2
- data/test/test-css.rb +174 -0
- data/test/test-linear-segmented-colormap.rb +19 -0
- data/test/test-listed-colormap.rb +19 -0
- data/test/test-xterm256.rb +18 -4
- metadata +46 -6
@@ -0,0 +1,10 @@
|
|
1
|
+
module Colors
|
2
|
+
module ColormapRegistry
|
3
|
+
register_listed_colormap("rocket")
|
4
|
+
register_listed_colormap("mako")
|
5
|
+
register_listed_colormap("icefire")
|
6
|
+
register_listed_colormap("vlag")
|
7
|
+
register_listed_colormap("flare")
|
8
|
+
register_listed_colormap("crest")
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require "json"
|
2
|
+
require "pathname"
|
3
|
+
|
4
|
+
module Colors
|
5
|
+
module ColormapRegistry
|
6
|
+
BUILTIN_COLORMAPS = {}
|
7
|
+
LUT_SIZE = 512
|
8
|
+
|
9
|
+
top_dir = Pathname.new(__dir__).parent.parent
|
10
|
+
@colormaps_dir = top_dir.join("data", "colormaps")
|
11
|
+
|
12
|
+
def self.load_colormap_data(name)
|
13
|
+
path = @colormaps_dir.join("#{name}.json")
|
14
|
+
json = File.read(path)
|
15
|
+
JSON.load(json, nil, symbolize_names: true, create_additions: false)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.register_listed_colormap(name, data=nil)
|
19
|
+
data = load_colormap_data(name) if data.nil?
|
20
|
+
colors = data.map {|r, g, b| Colors::RGB.new(r, g, b) }
|
21
|
+
BUILTIN_COLORMAPS[name] = ListedColormap.new(colors, name: name)
|
22
|
+
end
|
23
|
+
|
24
|
+
require_relative "colormap_data/matplotlib_builtin.rb"
|
25
|
+
require_relative "colormap_data/seaborn_builtin.rb"
|
26
|
+
|
27
|
+
class << self
|
28
|
+
undef load_colormap_data
|
29
|
+
undef register_listed_colormap
|
30
|
+
end
|
31
|
+
|
32
|
+
# Generate reversed colormaps
|
33
|
+
cmaps_r = BUILTIN_COLORMAPS.each_value.map(&:reversed)
|
34
|
+
cmaps_r.each do |cmap_r|
|
35
|
+
BUILTIN_COLORMAPS[cmap_r.name] = cmap_r
|
36
|
+
end
|
37
|
+
|
38
|
+
BUILTIN_COLORMAPS.freeze
|
39
|
+
|
40
|
+
BUILTIN_COLORMAPS.each do |name, cmap|
|
41
|
+
@registry[name] = cmap
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Colors
|
2
|
+
module ColormapRegistry
|
3
|
+
@registry = {}
|
4
|
+
|
5
|
+
def self.[](name)
|
6
|
+
return name if name.is_a?(Colormap)
|
7
|
+
|
8
|
+
name = String.try_convert(name)
|
9
|
+
if @registry.key?(name)
|
10
|
+
return @registry[name]
|
11
|
+
else
|
12
|
+
raise ArgumentError, "Unknown colormap name: %p" % name
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.register(cmap, name: nil, override_builtin: false)
|
17
|
+
case name
|
18
|
+
when String, Symbol
|
19
|
+
name = name.to_s
|
20
|
+
when nil
|
21
|
+
name = cmap.name
|
22
|
+
if name.nil?
|
23
|
+
raise ArgumentError, "`name` cannot be omitted for unnamed colormaps"
|
24
|
+
end
|
25
|
+
else
|
26
|
+
name = String.try_convert(name)
|
27
|
+
if name.nil?
|
28
|
+
raise ArgumentError, "`name` must be convertible to a String by to_str"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
if @registry.key?(name)
|
33
|
+
if BUILTIN_COLORMAPS.key?(name)
|
34
|
+
unless override_builtin
|
35
|
+
raise ArgumentError,
|
36
|
+
"Trying to re-register a builtin colormap: %p" % name
|
37
|
+
end
|
38
|
+
end
|
39
|
+
warn "Trying to re-register the colormap %p which already exists" % name
|
40
|
+
end
|
41
|
+
|
42
|
+
unless cmap.is_a?(Colormap)
|
43
|
+
raise ArgumentError,
|
44
|
+
"Invalid value for registering a colormap (%p for a Colormap)" % cmap
|
45
|
+
end
|
46
|
+
|
47
|
+
@registry[name] = cmap
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.unregister(name)
|
51
|
+
if @registry.key?(name)
|
52
|
+
if BUILTIN_COLORMAPS.key?(name)
|
53
|
+
raise ArgumentError,
|
54
|
+
"Unable to unregister the colormap %p which is a builtin colormap" % name
|
55
|
+
end
|
56
|
+
else
|
57
|
+
@registry.delete(name)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/lib/colors/convert.rb
CHANGED
@@ -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
|
data/lib/colors/helper.rb
CHANGED
@@ -7,7 +7,8 @@ module Colors
|
|
7
7
|
|
8
8
|
private def check_range(value, range, name)
|
9
9
|
return value if range.cover?(value)
|
10
|
-
check_fail ArgumentError,
|
10
|
+
check_fail ArgumentError,
|
11
|
+
"#{name} must be in #{range}, but %p is given" % value
|
11
12
|
end
|
12
13
|
|
13
14
|
private def check_fail(exc_class, message)
|
@@ -17,7 +17,7 @@ module Colors
|
|
17
17
|
else
|
18
18
|
ary = Array.try_convert(colors)
|
19
19
|
if ary.nil?
|
20
|
-
raise ArgumentError, "colors must be convertible to an array"
|
20
|
+
raise ArgumentError, "colors must be convertible to an array: %p for %s" % [colors, name]
|
21
21
|
else
|
22
22
|
colors = ary
|
23
23
|
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
|
@@ -115,5 +113,23 @@ module Colors
|
|
115
113
|
|
116
114
|
return lut.map {|v| v.clamp(0, 1).to_f }
|
117
115
|
end
|
116
|
+
|
117
|
+
private def make_reverse_colormap(name)
|
118
|
+
segmented_data_r = self.segmented_data.map { |key, data|
|
119
|
+
data_r = if data.respond_to?(:call)
|
120
|
+
make_inverse_func(data)
|
121
|
+
else
|
122
|
+
data.reverse_each.map do |x, y0, y1|
|
123
|
+
[1r - x, y1, y0]
|
124
|
+
end
|
125
|
+
end
|
126
|
+
[key, data_r]
|
127
|
+
}.to_h
|
128
|
+
LinearSegmentedColormap.new(name, segmented_data_r, n_colors: self.n_colors, gamma: self.gamma)
|
129
|
+
end
|
130
|
+
|
131
|
+
private def make_inverse_func(f)
|
132
|
+
->(x) { f(1 - x) }
|
133
|
+
end
|
118
134
|
end
|
119
135
|
end
|
data/lib/colors/version.rb
CHANGED
data/lib/colors/xterm256.rb
CHANGED
@@ -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
|
-
|
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"
|
@@ -19,6 +20,7 @@ require_relative "colors/named_colors"
|
|
19
20
|
require_relative "colors/colormap"
|
20
21
|
require_relative "colors/listed_colormap"
|
21
22
|
require_relative "colors/linear_segmented_colormap"
|
23
|
+
require_relative "colors/colormap_registry"
|
22
24
|
|
23
25
|
module Colors
|
24
26
|
# ITU-R BT.709 D65 white point
|
@@ -37,3 +39,5 @@ module Colors
|
|
37
39
|
NamedColors[name]
|
38
40
|
end
|
39
41
|
end
|
42
|
+
|
43
|
+
require_relative "colors/colormap_data"
|
data/red-colors.gemspec
CHANGED
@@ -27,11 +27,13 @@ Gem::Specification.new do |spec|
|
|
27
27
|
"#{spec.name}.gemspec",
|
28
28
|
]
|
29
29
|
spec.files += [".yardopts"]
|
30
|
+
spec.files += Dir.glob("data/colormaps/*.json")
|
30
31
|
spec.files += Dir.glob("lib/**/*.rb")
|
31
32
|
spec.files += Dir.glob("image/*.*")
|
32
33
|
spec.files += Dir.glob("doc/text/*")
|
33
34
|
spec.test_files += Dir.glob("test/**/*")
|
34
35
|
|
36
|
+
spec.add_runtime_dependency("json")
|
35
37
|
spec.add_runtime_dependency("matrix")
|
36
38
|
|
37
39
|
spec.add_development_dependency("bundler")
|
data/test/run.rb
CHANGED
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
|
@@ -115,5 +115,24 @@ class ColorsLinearSegmentedColormapTest < Test::Unit::TestCase
|
|
115
115
|
after
|
116
116
|
])
|
117
117
|
end
|
118
|
+
|
119
|
+
def test_reversed
|
120
|
+
cm = Colors::LinearSegmentedColormap.new_from_list("four", [:red, :magenta, :green, :yellow, :blue], n_colors: 5)
|
121
|
+
cm.under_color = :black
|
122
|
+
cm.over_color = :white
|
123
|
+
rev = cm.reversed
|
124
|
+
assert_equal([
|
125
|
+
"four_r",
|
126
|
+
cm[[0, 1, 2, 3, 4]],
|
127
|
+
Colors[:black],
|
128
|
+
Colors[:white]
|
129
|
+
],
|
130
|
+
[
|
131
|
+
rev.name,
|
132
|
+
rev[[4, 3, 2, 1, 0]],
|
133
|
+
rev.under_color,
|
134
|
+
rev.over_color
|
135
|
+
])
|
136
|
+
end
|
118
137
|
end
|
119
138
|
|
@@ -112,4 +112,23 @@ class ColorsListedColormapTest < Test::Unit::TestCase
|
|
112
112
|
after
|
113
113
|
])
|
114
114
|
end
|
115
|
+
|
116
|
+
def test_reversed
|
117
|
+
cm = Colors::ListedColormap.new([:red, :green, :blue], name: "three")
|
118
|
+
cm.under_color = :orange
|
119
|
+
cm.over_color = :yellow
|
120
|
+
rev = cm.reversed
|
121
|
+
assert_equal([
|
122
|
+
"three_r",
|
123
|
+
cm[[0, 1, 2]],
|
124
|
+
Colors[:orange],
|
125
|
+
Colors[:yellow]
|
126
|
+
],
|
127
|
+
[
|
128
|
+
rev.name,
|
129
|
+
rev[[2, 1, 0]],
|
130
|
+
rev.under_color,
|
131
|
+
rev.over_color
|
132
|
+
])
|
133
|
+
end
|
115
134
|
end
|
data/test/test-xterm256.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|