class TTY::Sparkline
Responsible for drawing sparkline in a terminal
@api public
Constants
- BARS
- EMPTY
- MAX_BUFFER_SIZE
- NEWLINE
- NON_NUMERIC_CONVERSIONS
- SPACE
- VERSION
Attributes
The drawing cursor
@return [TTY::Cursor]
@api public
The chart height in terminal lines
@return [Integer]
@api public
The left position
@return [Integer]
@api public
The custom maximum value used for scaling bars
@return [Numeric]
@api public
The custom minimum value used for scaling bars
@return [Numeric]
@api public
The top position
@return [Integer]
@api public
The chart maximum width in terminal columns
@return [Integer]
@api public
Public Class Methods
Create a Sparkline
instance
@param [Array<Numeric>] data
the data to chart
@param [Integer] top
the top position
@param [Integer] left
the left position
@param [Integer] height
the height in terminal lines
@param [Integer] width
the maximum width in terminal columns
@param [Numeric] min
the custom minimum value
@param [Numeric] max
the custom maximum value
@param [Array<String>] bars
the bars used for display
@param [Integer] buffer_size
the maximum buffer size
@param [Symbol] non_numeric
the replacement for a non-numeric value
@api public
# File lib/tty/sparkline.rb, line 99 def initialize(data = [], top: nil, left: nil, height: 1, width: nil, min: nil, max: nil, bars: BARS, buffer_size: MAX_BUFFER_SIZE, non_numeric: :empty) check_minmax(min, max) if min && max check_non_numeric(non_numeric) @data = Array(data).dup @cached_data_size = @data.size @top = top @left = left @height = height @width = width @min = min @max = max @bars = bars @num_of_bars = bars.size @buffer_size = buffer_size @non_numeric = non_numeric @filter = ->(value) { value.is_a?(::Numeric) } @cursor = TTY::Cursor end
Public Instance Methods
Append value(s)
@example
sparkline.push(1, 2, 3, 4)
@example
sparkline << 1 << 2 << 3 << 4
@param [Array<Numeric>] nums
@return [self]
@api public
# File lib/tty/sparkline.rb, line 134 def push(*nums) @data.push(*nums) @cached_data_size += nums.size if (overflow = @cached_data_size - @buffer_size) > 0 @data.shift(overflow) @cached_data_size -= overflow end self end
Render data as a sparkline chart
@example
sparkline.render
@param [Integer] min
the minimum value to display
@param [Integer] max
the maximum value to display
@return [String]
the rendered sparkline chart
@api public
# File lib/tty/sparkline.rb, line 171 def render(min: nil, max: nil) return EMPTY if @data.empty? buffer = [] calc_min, calc_max = data_minmax(min, max) check_minmax(calc_min, calc_max) height.times do |y| buffer << position(y) if position? @data[data_range].each.with_index do |value, x| bar_index = clamp_and_scale(value, calc_min, calc_max) bar = convert_to_bar(bar_index, height - 1 - y) bar = yield(value, bar, x, y) if block_given? buffer << bar end buffer << NEWLINE unless y == height - 1 end buffer.join end
The number of values
@return [Integer]
@api public
# File lib/tty/sparkline.rb, line 153 def size @cached_data_size end
Private Instance Methods
Check maximum isn't less than minimum
@raise [Error]
@api private
# File lib/tty/sparkline.rb, line 214 def check_minmax(min, max) return if min <= max raise Error, "maximum value cannot be less than minimum" end
Check whether non_numeric has a valid conversion type
@param [Symbol] type
the type of conversion
@raise [Error]
@api private
# File lib/tty/sparkline.rb, line 228 def check_non_numeric(type) return if NON_NUMERIC_CONVERSIONS.include?(type) raise Error, "unknown non_numeric value: #{type.inspect}" end
Clamp value and scale it against height and number of bars
@param [Object] value
the value to clamp and scale
@param [Integer] min
the minimum value
@param [Integer] max
the maximum value
@return [Integer]
@api private
# File lib/tty/sparkline.rb, line 283 def clamp_and_scale(value, min, max) return value unless value.is_a?(Numeric) clamped_value = value > max ? max : (value < min ? min : value) reduced_value = max == min ? clamped_value : clamped_value - min reduced_max = max == min ? (max.zero? ? 1 : max) : max - min reduced_value * height * (@num_of_bars - 1) / reduced_max end
Convert non-numeric value into display string
@return [String]
@api private
# File lib/tty/sparkline.rb, line 319 def convert_non_numeric case @non_numeric when :empty SPACE when :ignore EMPTY when :minimum @bars[0] end end
Convert an index to a bar representation
@param [Integer] bar_index
the bar index within bars
@param [Integer] offset
the offset from the bottom
@return [String]
the rendered bar
@api private
# File lib/tty/sparkline.rb, line 303 def convert_to_bar(bar_index, offset) return convert_non_numeric unless bar_index.is_a?(Numeric) if bar_index >= offset * @num_of_bars bar_index -= offset * @num_of_bars @bars[bar_index >= @num_of_bars ? -1 : bar_index] else SPACE end end
Find the minimum and maximum value in the data
@param [Integer] min
the custom minimum value
@param [Integer] max
the custom maximum value
@return [Array<Numeric, Numeric>]
@api private
# File lib/tty/sparkline.rb, line 204 def data_minmax(min, max) calc_min, calc_max = @data.select(&@filter).minmax [min || @min || calc_min, max || @max || calc_max] end
Find a range of data values matching width
@return [Range]
@api private
# File lib/tty/sparkline.rb, line 263 def data_range start_index = 0 if width && width < @cached_data_size start_index = @cached_data_size - width end start_index..-1 end
Find a position at which to display this chart
@return [String]
@api private
# File lib/tty/sparkline.rb, line 248 def position(offset = 0) if left && top cursor.move_to(left, top + offset) elsif left cursor.column(left) elsif top cursor.row(top + offset) end end
Check whether or not to position this chart
@return [Boolean]
@api private
# File lib/tty/sparkline.rb, line 239 def position? top || left end