class Palette
Constants
- VERSION
Attributes
default[R]
colors[R]
desaturate_factor[R]
name[R]
Public Class Methods
blend_colors(colors, n_colors=6, as_cmap: false)
click to toggle source
# File lib/palette/colors.rb, line 110 def self.blend_colors(colors, n_colors=6, as_cmap: false) name = "blend" cmap = Colors::LinearSegmentedColormap.new_from_list(name, colors) if as_cmap return cmap else bins = Helper.linspace(0r, 1r, Integer(n_colors)) colors = cmap[bins] return colors end end
cubehelix_colors(n_colors=6, start: 0, rot: 0.4r, gamma: 1.0r, hue: 0.8r, light: 0.85r, dark: 0.15r, reverse: false)
click to toggle source
# File lib/palette/colors.rb, line 56 def self.cubehelix_colors(n_colors=6, start: 0, rot: 0.4r, gamma: 1.0r, hue: 0.8r, light: 0.85r, dark: 0.15r, reverse: false) get_color_function = ->(p0, p1) do color = -> (x) do xg = x ** gamma a = hue * xg * (1 - xg) / 2 phi = 2 * Math::PI * (start / 3 + rot * x) return xg + a * (p0 * Math.cos(phi) + p1 * Math.sin(phi)) end return color end segmented_data = { red: get_color_function.(-0.14861, 1.78277), green: get_color_function.(-0.29227, -0.90649), blue: get_color_function.(1.97294, 0.0), } cmap = Colors::LinearSegmentedColormap.new("cubehelix", segmented_data) x = Helper.linspace(light, dark, n_colors) pal = cmap[x] pal.reverse! if reverse pal end
default=(args)
click to toggle source
# File lib/palette.rb, line 171 def default=(args) @default = case args when Palette args when Array case args[0] when Array Palette.new(*args) else Palette.new(args) end else Palette.new(args) end end
hsl_colors(n_colors=6, h: 3.6r, s: 0.65r, l: 0.6r)
click to toggle source
Get a set of evenly spaced colors in HSL hue space.
@param n_colors
[Integer]
The number of colors in the palette
@param h [Numeric]
The hue value of the first color in degree
@param s [Numeric]
The saturation value of the first color (between 0 and 1)
@param l [Numeric]
The lightness value of the first color (between 0 and 1)
@return [Array<Colors::HSL>]
The array of colors
# File lib/palette/colors.rb, line 23 def self.hsl_colors(n_colors=6, h: 3.6r, s: 0.65r, l: 0.6r) hues = Helper.linspace(0, 1, n_colors + 1)[0...-1] hues.each_index do |i| hues[i] += (h/360r).to_f hues[i] %= 1 hues[i] -= hues[i].to_i end (0...n_colors).map {|i| Colors::HSL.new(hues[i]*360r, s, l) } end
husl_colors(n_colors=6, h: 3.6r, s: 0.9r, l: 0.65r)
click to toggle source
Get a set of evenly spaced colors in HUSL hue space.
@param n_colors
[Integer]
The number of colors in the palette
@param h [Numeric]
The hue value of the first color in degree
@param s [Numeric]
The saturation value of the first color (between 0 and 1)
@param l [Numeric]
The lightness value of the first color (between 0 and 1)
@return [Array<Colors::HSL>]
The array of colors
# File lib/palette/colors.rb, line 46 def self.husl_colors(n_colors=6, h: 3.6r, s: 0.9r, l: 0.65r) hues = Helper.linspace(0, 1, n_colors + 1)[0...-1] hues.each_index do |i| hues[i] += (h/360r).to_f hues[i] %= 1 hues[i] *= 359 end (0...n_colors).map {|i| Colors::HUSL.new(hues[i], s, l) } end
matplotlib_colors(name, n_colors=6, as_cmap: false)
click to toggle source
# File lib/palette/colors.rb, line 82 def self.matplotlib_colors(name, n_colors=6, as_cmap: false) if name.end_with?("_d") sub_name = name[0..-2] if sub_name.end_with?("_r") reverse = true sub_name = sub_name[0..-2] else reverse = false end colors = Palette.new(sub_name, 2).colors colors << Colors::RGB.parse("#333333") colors.reverse! if reverse cmap = blend_cmap(colors, n_colors, as_cmap: true) else cmap = Colors::ColormapRegistry[name] end return cmap if as_cmap bins = if MPL_QUAL_PALS.include?(name) Helper.linspace(0r, 1r, MPL_QUAL_PALS[name])[0, n_colors] else Helper.linspace(0r, 1r, Integer(n_colors) + 2)[1...-1] end colors = cmap[bins] return colors end
new(palette=nil, n_colors=nil, desaturate_factor: nil)
click to toggle source
Return a list of colors defining a color palette
@param palette [nil, String, Palette]
Name of palette or nil to return current palette. If a Palette is given, input colors are used but possibly cycled and desaturated.
@param n_colors
[Integer, nil]
Number of colors in the palette. If `nil`, the default will depend on how `palette` is specified. Named palettes default to 6 colors, but grabbing the current palette or passing in a list of colors will not change the number of colors unless this is specified. Asking for more colors than exist in the palette cause it to cycle.
@param desaturate_factor
[Float, nil]
Propotion to desaturate each color by.
@return [Palette]
Color palette. Behaves like a list.
# File lib/palette.rb, line 28 def initialize(palette=nil, n_colors=nil, desaturate_factor: nil) case when palette.nil? @name = nil palette = Colors::ColorData::DEFAULT_COLOR_CYCLE n_colors ||= palette.length else palette = normalize_palette_name(palette) case palette when String @name = palette # Use all colors in a qualitative palette or 6 of another kind n_colors ||= QUAL_PALETTE_SIZES.fetch(palette, 6) case @name when SEABORN_PALETTES.method(:has_key?).to_proc # NOTE: to_proc needs for Ruby 2.4 palette = self.class.seaborn_colors(@name) when "hls", "HLS", "hsl", "HSL" palette = self.class.hsl_colors(n_colors) when "husl", "HUSL" palette = self.class.husl_colors(n_colors) when /\Ach:/ # Cubehelix palette with params specified in string args, kwargs = parse_cubehelix_args(palette) palette = self.class.cubehelix_colors(n_colors, *args, **kwargs) else begin palette = self.class.matplotlib_colors(palette, n_colors) rescue ArgumentError raise ArgumentError, "#{palette} is not a valid palette name" end end else n_colors ||= palette.length end end if desaturate_factor palette = palette.map {|c| Colors.desaturate(c, desaturate_factor) } end # Always return as many colors as we asked for @colors = palette.cycle.take(n_colors).freeze @desaturate_factor = desaturate_factor end
seaborn_colors(name)
click to toggle source
# File lib/palette/colors.rb, line 4 def self.seaborn_colors(name) SEABORN_PALETTES[name].map do |hex_string| Colors::RGB.parse(hex_string) end end
Public Instance Methods
==(other)
click to toggle source
Two palettes are equal if they have the same colors, even if they have the different names and different desaturate factors.
Calls superclass method
# File lib/palette.rb, line 117 def ==(other) case other when Palette colors == other.colors else super end end
[](i)
click to toggle source
# File lib/palette.rb, line 126 def [](i) @colors[i % n_colors] end
n_colors()
click to toggle source
# File lib/palette.rb, line 111 def n_colors @colors.length end
to_ary()
click to toggle source
# File lib/palette.rb, line 134 def to_ary @colors.dup end
to_colormap(n=self.n_colors)
click to toggle source
# File lib/palette.rb, line 130 def to_colormap(n=self.n_colors) Colors::ListedColormap.new(colors, n_colors: n) end
to_iruby()
click to toggle source
# File lib/palette.rb, line 138 def to_iruby ["image/svg", to_svg] end
to_svg()
click to toggle source
# File lib/palette.rb, line 142 def to_svg w = 55 n = n_colors svg = %Q[<svg width="#{n*w}" height="#{w}">] @colors.each_with_index do |color, i| hex = color.to_rgb.to_hex_string svg << %Q[<rect x="#{i*w}" y="0" width="#{w}" height="#{w}" style="fill:#{hex};] svg << %Q[stroke-width:2;stroke:rgb(255,255,255)"/>] end svg << %Q[</svg>] svg end
Private Instance Methods
normalize_palette_name(palette)
click to toggle source
# File lib/palette.rb, line 155 def normalize_palette_name(palette) case palette when String palette when Symbol palette.to_s else palette.to_str end rescue NoMethodError, TypeError palette end
parse_cubehelix_args(str)
click to toggle source
# File lib/palette.rb, line 73 def parse_cubehelix_args(str) if str.start_with?("ch:") str = str[3..-1] end if str.end_with?("_r") reverse = true str = str[0..-3] else reverse = false end if str.empty? return [], {reverse: reverse} end short_key_map = { "s" => :start, "r" => :rot, "g" => :gamma, "h" => :hue, "l" => :light, "d" => :dark } all_args = str.split(",") args, kwargs = [], {} all_args.each do |a| if a.include? "=" key, value = a.split("=") key = short_key_map.fetch(key.strip, key).to_sym kwargs[key] = Float(value.strip) else args << Float(a.strip) end end kwargs[:reverse] = true if reverse return args, kwargs end