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