class Plotrb::Scale

Scales are functions that transform a domain of data values to a range of

visual values.

See {github.com/trifacta/vega/wiki/Scales}

Constants

RANGE_LITERALS
SCALE_PROPERTIES

@!attributes type

@return [Symbol] the type of the scale
TIME_SCALE_NICE
TYPES

Public Class Methods

new(type=:linear, &block) click to toggle source
# File lib/plotrb/scales.rb, line 28
def initialize(type=:linear, &block)
  @type = type
  case @type
    when :ordinal
      set_ordinal_scale_attributes
    when :time, :utc
      set_time_scale_attributes
    else
      set_quantitative_scale_attributes
  end
  set_common_scale_attributes
  ::Plotrb::Kernel.scales << self
  self.instance_eval(&block) if block_given?
  self
end

Public Instance Methods

method_missing(method, *args, &block) click to toggle source
Calls superclass method Plotrb::Kernel#method_missing
# File lib/plotrb/scales.rb, line 48
def method_missing(method, *args, &block)
  case method.to_s
    when /in_(\w+)s$/ # set @nice for time and utc type, eg. in_seconds
      if TIME_SCALE_NICE.include?($1.to_sym)
        self.nice($1.to_sym, &block)
      else
        super
      end
    when /to_(\w+)$/ # set range literals, eg. to_more_colors
      if RANGE_LITERALS.include?($1.to_sym)
        self.range($1.to_sym, &block)
      else
        super
      end
    else
      super
  end
end
type() click to toggle source
# File lib/plotrb/scales.rb, line 44
def type
  @type
end

Private Instance Methods

attribute_post_processing() click to toggle source
# File lib/plotrb/scales.rb, line 170
def attribute_post_processing
  process_name
  process_type
  process_domain
  process_domain_min
  process_domain_max
  process_range
end
get_data_ref_from_data(data) click to toggle source
# File lib/plotrb/scales.rb, line 279
def get_data_ref_from_data(data)
  if data.values.is_a?(Array)
    ::Plotrb::Scale::DataRef.new.data(data.name).field('data')
  else
    ::Plotrb::Scale::DataRef.new.data(data.name).field('index')
  end
end
get_data_ref_from_string(ref) click to toggle source
# File lib/plotrb/scales.rb, line 259
def get_data_ref_from_string(ref)
  source, field = ref.split('.', 2)
  data = ::Plotrb::Kernel.find_data(source)
  if field.nil?
    if data && data.values.is_a?(Array)
      ::Plotrb::Scale::DataRef.new.data(source).field('data')
    else
      ::Plotrb::Scale::DataRef.new.data(source).field('index')
    end
  elsif field == 'index'
    ::Plotrb::Scale::DataRef.new.data(source).field('index')
  else
    if data.extra_fields.include?(field.to_sym)
      ::Plotrb::Scale::DataRef.new.data(source).field(field)
    else
      ::Plotrb::Scale::DataRef.new.data(source).field("data.#{field}")
    end
  end
end
is_data_ref?(ref) click to toggle source
# File lib/plotrb/scales.rb, line 287
def is_data_ref?(ref)
  return false unless ref.is_a?(String)
  source, _ = ref.split('.', 2)
  not ::Plotrb::Kernel.find_data(source).nil?
end
process_domain() click to toggle source
# File lib/plotrb/scales.rb, line 194
def process_domain
  return unless @domain
  case @domain
    when String
      @domain = get_data_ref_from_string(@domain)
    when ::Plotrb::Data
      @domain = get_data_ref_from_data(@domain)
    when Array
      if @domain.all? { |d| is_data_ref?(d) }
        fields = @domain.collect { |d| get_data_ref_from_string(d) }
        @domain = {:fields => fields}
      else
        # leave as it is
      end
    when ::Plotrb::Scale::DataRef
      # leave as it is
    else
      raise ArgumentError, 'Unsupported Scale domain type'
  end
end
process_domain_max() click to toggle source
# File lib/plotrb/scales.rb, line 237
def process_domain_max
  # only for quantitative domain
  return unless @domain_max && !%i(ordinal time utc).include?(@type)
  case @domain_max
    when String
      @domain_max = get_data_ref_from_string(@domain_max)
    when ::Plotrb::Data
      @domain_max = get_data_ref_from_data(@domain_max)
    when Array
      if @domain_max.all? { |d| is_data_ref?(d) }
        fields = @domain_max.collect { |d| get_data_ref_from_string(d) }
        @domain_max = {:fields => fields}
      else
        raise ArgumentError, 'Unsupported Scale domain_max type'
      end
    when Numeric
      # leave as it is
    else
      raise ArgumentError, 'Unsupported Scale domain_max type'
  end
end
process_domain_min() click to toggle source
# File lib/plotrb/scales.rb, line 215
def process_domain_min
  # only for quantitative domain
  return unless @domain_min && !%i(ordinal time utc).include?(@type)
  case @domain_min
    when String
      @domain_min = get_data_ref_from_string(@domain_min)
    when ::Plotrb::Data
      @domain_min = get_data_ref_from_data(@domain_min)
    when Array
      if @domain_min.all? { |d| is_data_ref?(d) }
        fields = @domain_min.collect { |d| get_data_ref_from_string(d) }
        @domain_min = {:fields => fields}
      else
        raise ArgumentError, 'Unsupported Scale domain_min type'
      end
    when Numeric
      # leave as it is
    else
      raise ArgumentError, 'Unsupported Scale domain_min type'
  end
end
process_name() click to toggle source
# File lib/plotrb/scales.rb, line 179
def process_name
  if @name.nil? || @name.strip.empty?
    raise ArgumentError, 'Name missing for Scale object'
  end
  if ::Plotrb::Kernel.duplicate_scale?(@name)
    raise ArgumentError, 'Duplicate names for Scale object'
  end
end
process_range() click to toggle source
# File lib/plotrb/scales.rb, line 293
def process_range
  return unless @range
  case @range
    when String, Symbol
      @range = range_literal(@range)
    when Array
      #leave as it is
    else
      raise ArgumentError, 'Unsupported Scale range type'
  end
end
process_type() click to toggle source
# File lib/plotrb/scales.rb, line 188
def process_type
  unless TYPES.include?(@type)
    raise ArgumentError, 'Invalid Scale type'
  end
end
range_literal(literal) click to toggle source
# File lib/plotrb/scales.rb, line 305
def range_literal(literal)
  case literal
    when :colors
      :category10
    when :more_colors
      :category20
    when :width, :height, :shapes, :category10, :category20
      literal
    else
      raise ArgumentError, 'Invalid Scale range'
  end
end
set_common_scale_attributes() click to toggle source
# File lib/plotrb/scales.rb, line 69
def set_common_scale_attributes
  # @!attributes name
  #   @return [String] the name of the scale
  # @!attributes domain
  #   @return [Array(Numeric, Numeric), Array, String] the domain of the
  #     scale, representing the set of data values
  # @!attributes domain_min
  #   @return [Numeric, String] the minimum value in the scale domain
  # @!attributes domain_max
  #   @return [Numeric, String] the maximum value in the scale domain
  # @!attributes range
  #   @return [Array(Numeric, Numeric), Array, String] the range of the
  #     scale, representing the set of visual values
  # @!attributes range_min
  #   @return [Numeric, String] the minimum value in the scale range
  # @!attributes range_max
  #   @return [Numeric, String] the maximum value in the scale range
  # @!attributes reverse
  #   @return [Boolean] whether flips the scale range
  # @!attributes round
  #   @return [Boolean] whether rounds numeric output values to integers
  define_single_val_attributes(:name, :domain, :domain_max, :domain_min,
                               :range, :range_max, :range_min)
  define_boolean_attributes(:reverse, :round)
  self.singleton_class.class_eval {
    alias_method :from, :domain
    alias_method :to, :range
  }
end
set_ordinal_scale_attributes() click to toggle source
# File lib/plotrb/scales.rb, line 99
def set_ordinal_scale_attributes
  # @!attributes points
  #   @return [Boolean] whether distributes the ordinal values over a
  #     quantitative range at uniformly spaced points or bands
  # @!attributes padding
  #   @return [Numeric] the spacing among ordinal elements in the scale range
  # @!attributes sort
  #   @return [Boolean] whether values in the scale domain will be sorted
  #     according to their natural order
  add_attributes(:points, :padding, :sort)
  define_boolean_attributes(:points, :sort)
  define_single_val_attribute(:padding)
  define_singleton_method(:bands) do |&block|
    @points = false
    self.instance_eval(&block) if block
    self
  end
  define_singleton_method(:bands?) do
    !@points
  end
  self.singleton_class.class_eval {
    alias_method :as_bands, :bands
    alias_method :as_bands?, :bands?
    alias_method :as_points, :points
    alias_method :as_points?, :points?
  }
end
set_quantitative_scale_attributes() click to toggle source
# File lib/plotrb/scales.rb, line 139
def set_quantitative_scale_attributes
  # @!attributes clamp
  #   @return [Boolean] whether clamps values that exceed the data domain
  #     to either to minimum or maximum range value
  # @!attributes nice
  #   @return [Boolean] scale domain in a more human-friendly
  #     value range
  # @!attributes exponent
  #   @return [Numeric] the exponent of the scale transformation
  # @!attributes zero
  #   @return [Boolean] whether zero baseline value is included
  add_attributes(:clamp, :exponent, :nice, :zero)
  define_boolean_attributes(:clamp, :nice, :zero)
  define_single_val_attribute(:exponent)
  define_singleton_method(:exclude_zero) do |&block|
    @zero = false
    self.instance_eval(&block) if block
    self
  end
  define_singleton_method(:exclude_zero?) do
    !@zero
  end
  self.singleton_class.class_eval {
    alias_method :nicely, :nice
    alias_method :nicely?, :nice?
    alias_method :include_zero, :zero
    alias_method :include_zero?, :zero?
    alias_method :in_exponent, :exponent
  }
end
set_time_scale_attributes() click to toggle source
# File lib/plotrb/scales.rb, line 127
def set_time_scale_attributes
  # @!attributes clamp
  #   @return [Boolean] whether clamps values that exceed the data domain
  #     to either to minimum or maximum range value
  # @!attributes nice
  #   @return [Symbol, Boolean, nil] scale domain in a more human-friendly
  #     value range
  add_attributes(:clamp, :nice)
  define_boolean_attribute(:clamp)
  define_single_val_attribute(:nice)
end