class Colors::LinearSegmentedColormap

Attributes

gamma[R]
segmented_data[R]

Public Class Methods

new(name, segmented_data, n_colors: 256, gamma: 1.0) click to toggle source
Calls superclass method Colors::Colormap.new
# File lib/colors/linear_segmented_colormap.rb, line 3
def initialize(name, segmented_data, n_colors: 256, gamma: 1.0)
  super(name, n_colors)

  @monochrome = false
  @segmented_data = segmented_data
  @gamma = gamma
end
new_from_list(name, colors, n_colors: 256, gamma: 1.0) click to toggle source
# File lib/colors/linear_segmented_colormap.rb, line 13
def self.new_from_list(name, colors, n_colors: 256, gamma: 1.0)
  case colors
  when Enumerable
    colors = colors.to_a
  else
    ary = Array.try_convert(colors)
    if ary.nil?
      raise ArgumentError, "colors must be convertible to an array: %p for %s" % [colors, name]
    else
      colors = ary
    end
  end

  case colors[0]
  when Array
    unless colors.all? {|a| a.length == 2 }
      raise ArgumentError, "colors array has invalid items" 
    end
    vals, colors = colors.transpose
  else
    vals = Utils.linspace(0r, 1r, colors.length)
  end

  r, g, b, a = colors.map { |c|
    Utils.make_color(c).to_rgba.components
  }.transpose

  segmented_data = {
    red:   [vals, r, r].transpose,
    green: [vals, g, g].transpose,
    blue:  [vals, b, b].transpose,
    alpha: [vals, a, a].transpose
  }

  new(name, segmented_data, n_colors: n_colors, gamma: gamma)
end

Public Instance Methods

gamma=(val) click to toggle source
# File lib/colors/linear_segmented_colormap.rb, line 50
def gamma=(val)
  @gamma = val
  @initialized = false
end

Private Instance Methods

create_lookup_table(n, data, gamma=1.0) click to toggle source
# File lib/colors/linear_segmented_colormap.rb, line 69
        def create_lookup_table(n, data, gamma=1.0)
  if data.respond_to?(:call)
    xind = Utils.linspace(0r, 1r, n).map {|x| x ** gamma }
    lut = xind.map {|i| data.(i).clamp(0, 1).to_f }
    return lut
  end

  ary = Array.try_convert(data)
  if ary.nil?
    raise ArgumentError, "data must be convertible to an array"
  elsif ary.any? {|sub| sub.length != 3 }
    raise ArgumentError, "data array must consist of 3-length arrays"
  end

  x, y0, y1 = ary.transpose

  if x[0] != 0.0 || x[-1] != 1.0
    raise ArgumentError,
          "data mapping points must start with x=0 and end with x=1"
  end

  unless x.each_cons(2).all? {|a, b| a < b }
    raise ArgumentError,
          "data mapping points must have x in increasing order"
  end

  if n == 1
    # Use the `y = f(x=1)` value for a 1-element lookup table
    lut = [y0[-1]]
  else
    x.map! {|v| v.to_f * (n - 1) }
    xind = Utils.linspace(0r, 1r, n).map {|i| (n - 1) * i ** gamma }
    ind = (0 ... n).map {|i| x.find_index {|v| xind[i] < v } }[1 ... -1]

    distance = ind.map.with_index do |i, j|
      (xind[j+1] - x[i - 1]) / (x[i] - x[i - 1])
    end

    lut = [
      y1[0],
      *ind.map.with_index {|i, j| distance[j] * (y0[i] - y1[i - 1]) + y1[i - 1] },
      y0[-1]
    ]
  end

  return lut.map {|v| v.clamp(0, 1).to_f }
end
init_colormap() click to toggle source
# File lib/colors/linear_segmented_colormap.rb, line 55
        def init_colormap
  red = create_lookup_table(self.n_colors, @segmented_data[:red], @gamma)
  green = create_lookup_table(self.n_colors, @segmented_data[:green], @gamma)
  blue = create_lookup_table(self.n_colors, @segmented_data[:blue], @gamma)
  alpha = if @segmented_data.key?(:alpha)
            create_lookup_table(self.n_colors, @segmented_data[:alpha], @gamma)
          end
  @lookup_table = Array.new(self.n_colors) do |i|
    Colors::RGBA.new(red[i], green[i], blue[i], alpha ? alpha[i] : 1r)
  end
  @initialized = true
  update_extreme_colors
end
make_inverse_func(f) click to toggle source
# File lib/colors/linear_segmented_colormap.rb, line 131
        def make_inverse_func(f)
  ->(x) { f(1 - x) }
end
make_reverse_colormap(name) click to toggle source
# File lib/colors/linear_segmented_colormap.rb, line 117
        def make_reverse_colormap(name)
  segmented_data_r = self.segmented_data.map { |key, data|
    data_r = if data.respond_to?(:call)
               make_inverse_func(data)
             else
               data.reverse_each.map do |x, y0, y1|
                 [1r - x, y1, y0]
               end
             end
    [key, data_r]
  }.to_h
  LinearSegmentedColormap.new(name, segmented_data_r, n_colors: self.n_colors, gamma: self.gamma)
end