Class: Graphina::Graph
- Inherits:
-
Object
- Object
- Graphina::Graph
- Includes:
- Formatters, Term::ANSIColor
- Defined in:
- lib/graphina/graph.rb,
lib/graphina/graph/display.rb,
lib/graphina/graph/formatters.rb
Overview
A class that provides graphical display functionality for terminal-based data visualization
The Graph class enables the creation of dynamic, real-time visualizations of data values within a terminal environment. It manages the rendering of graphical representations such as line charts or graphs, updating them continuously based on provided data sources. The class handles terminal control operations, including cursor positioning, color management, and screen clearing to ensure smooth visual updates. It also supports configuration of display parameters like title, formatting strategies for values, update intervals, and color schemes for different data series.
Defined Under Namespace
Modules: Formatters Classes: Display
Instance Attribute Summary collapse
-
#data ⇒ Array<Object>, ...
readonly
private
The data reader method provides access to the data attribute that was set during object initialization.
Instance Method Summary collapse
-
#columns ⇒ Integer
private
The columns method returns the number of columns in the display.
-
#data_range ⇒ Float
private
The data_range method calculates the range of data values by computing the difference between the maximum and minimum values in the data set and converting the result to a float.
-
#draw_graph ⇒ Object
private
Draws the graphical representation of the data on the display.
-
#format_value(value) ⇒ String
private
The format_value method processes a given value using the configured formatting strategy.
-
#full_reset ⇒ Object
private
The full_reset method performs a complete reset of the display and terminal state.
-
#initialize(title:, value: -> i { 0 }, format_value: nil, sleep: 5, true_coloring: true, color: nil, color_secondary: nil, adjust_brightness: :lighten, adjust_brightness_percentage: 15, foreground_color: :white, background_color: :black, resolution: :double) ⇒ Graph
constructor
The initialize method sets up a Graph instance by configuring its display parameters and internal state.
-
#install_handlers ⇒ Object
private
The install_handlers method sets up signal handlers for graceful shutdown and terminal resize handling.
-
#lines ⇒ Integer
private
The lines method returns the number of lines in the display.
-
#normalize_value(value) ⇒ Float, Integer
private
The normalize_value method converts a value to its appropriate numeric representation.
- #perform(*a) ⇒ Object private
-
#perform_display_diff ⇒ Object
private
The perform_display_diff method calculates and displays the difference between the current and previous display states to update only the changed portions of the terminal output.
-
#pick_color ⇒ Term::ANSIColor::Attribute
private
The pick_color method determines and returns an ANSI color attribute based on the configured color setting.
-
#pick_secondary_color(color, adjust_brightness:, adjust_brightness_percentage:) ⇒ Term::ANSIColor::Attribute
private
The pick_secondary_color method determines a secondary color based on a primary color and brightness adjustment parameters It returns the pre-configured secondary color if one exists, otherwise calculates a new color by adjusting the brightness of the primary color.
-
#sleep_duration ⇒ String
private
The sleep_duration method returns a string representation of the configured sleep interval with the ‘s’ suffix appended to indicate seconds.
-
#sleep_now ⇒ Object
private
The sleep_now method calculates and executes a sleep duration based on the configured sleep time and elapsed time since start.
-
#start ⇒ Object
The start method initiates the graphical display process by setting up signal handlers, performing an initial terminal reset, and entering the main update loop.
-
#start_loop ⇒ Object
private
The start_loop method executes a continuous loop to update and display graphical data.
-
#stop ⇒ Object
The stop method terminates the graphical display process by performing a full reset and setting the continue flag to false.
Methods included from Formatters
#as_bytes, #as_celsius, #as_default, #as_hertz, #as_percent, #derive_color_from_string
Constructor Details
#initialize(title:, value: -> i { 0 }, format_value: nil, sleep: 5, true_coloring: true, color: nil, color_secondary: nil, adjust_brightness: :lighten, adjust_brightness_percentage: 15, foreground_color: :white, background_color: :black, resolution: :double) ⇒ Graph
The initialize method sets up a Graph instance by configuring its display parameters and internal state.
This method configures the graph visualization with title, value provider, formatting options, update interval, and color settings. It initializes internal data structures for storing historical values and manages synchronization through a mutex for thread-safe operations.
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/graphina/graph.rb', line 73 def initialize( title:, value: -> i { 0 }, format_value: nil, sleep: 5, true_coloring: true, color: nil, color_secondary: nil, adjust_brightness: :lighten, adjust_brightness_percentage: 15, foreground_color: :white, background_color: :black, resolution: :double ) @title = title @value = value @format_value = format_value sleep = Float(sleep) sleep >= 0 or raise ArgumentError, 'sleep has to be >= 0' @sleep = sleep @continue = false @data = [] @color = color @color_secondary = color_secondary adjust_brightness = adjust_brightness.to_sym if %i[ lighten darken ].include? adjust_brightness @adjust_brightness = adjust_brightness else raise ArgumentError, 'adjust_brightness required to be either :lighten or :darken' end @adjust_brightness_percentage = Float(adjust_brightness_percentage) @foreground_color = foreground_color @background_color = background_color resolution = resolution.to_sym if %i[ single double ].include? resolution @resolution = resolution else raise ArgumentError, 'resolution required to be either :single or :double' end Term::ANSIColor.true_coloring = true_coloring @mutex = Mutex.new end |
Instance Attribute Details
#data ⇒ Array<Object>, ... (readonly, private)
The data reader method provides access to the data attribute that was set during object initialization.
This method returns the value of the data instance variable, which typically contains structured information that has been processed or collected by the object.
274 275 276 |
# File 'lib/graphina/graph.rb', line 274 def data @data end |
Instance Method Details
#columns ⇒ Integer (private)
The columns method returns the number of columns in the display
This method provides access to the horizontal dimension of the graphical display by returning the total number of columns available for rendering content
251 252 253 |
# File 'lib/graphina/graph.rb', line 251 def columns @display.columns end |
#data_range ⇒ Float (private)
The data_range method calculates the range of data values by computing the difference between the maximum and minimum values in the data set and converting the result to a float
188 189 190 |
# File 'lib/graphina/graph.rb', line 188 def data_range (data.max - data.min).abs.to_f end |
#draw_graph ⇒ Object (private)
Draws the graphical representation of the data on the display.
This method renders the data as a graph using Unicode block characters (▀) to achieve 2px vertical resolution in terminal graphics. Each data point is plotted with appropriate color blending for visual appeal.
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 |
# File 'lib/graphina/graph.rb', line 150 def draw_graph y_width = data_range color = pick_color color_secondary = pick_secondary_color( color, adjust_brightness: @adjust_brightness, adjust_brightness_percentage: @adjust_brightness_percentage ) data.each_with_index do |value, i| x = 1 + i + columns - data.size y0 = ((value - data.min) * lines / y_width.to_f) y = lines - y0.round + 1 y.upto(lines) do |iy| if iy > y @display.at(iy, x).on_color(color_secondary).write(' ') else case @resolution when :double fract = 1 - (y0 - y0.floor).abs case when (0...0.5) === fract @display.at(iy, x).on_color(@background_color).color(color).write(?▄) else @display.at(iy, x).on_color(color).color(color_secondary).write(?▄) end else @display.at(iy, x).on_color(color).color(color_secondary).write(' ') end end end end end |
#format_value(value) ⇒ String (private)
The format_value method processes a given value using the configured formatting strategy
This method applies the appropriate formatting to a value based on the @format_value instance variable configuration It supports different formatting approaches including custom Proc objects, String- or Symbol-based method calls, and default formatting
295 296 297 298 299 300 301 302 303 304 |
# File 'lib/graphina/graph.rb', line 295 def format_value(value) case @format_value when Proc @format_value.(value) when Symbol, String send(@format_value, value) else send(:as_default, value) end end |
#full_reset ⇒ Object (private)
The full_reset method performs a complete reset of the display and terminal state
This method synchronizes access to shared resources using a mutex, then executes a series of terminal control operations to reset the terminal state, clear the screen, move the cursor to the home position, and make the cursor visible. It also initializes new display objects with the current terminal dimensions and updates the internal display state.
429 430 431 432 433 434 435 436 437 438 439 440 441 442 |
# File 'lib/graphina/graph.rb', line 429 def full_reset @mutex.synchronize do perform reset, clear_screen, move_home, show_cursor winsize = Tins::Terminal.winsize opts = { color: @foreground_color, on_color: @background_color, } @display = Graphina::Graph::Display.new(*winsize, **opts) @old_display = Graphina::Graph::Display.new(*winsize, **opts) perform @display @full_reset = false end end |
#install_handlers ⇒ Object (private)
The install_handlers method sets up signal handlers for graceful shutdown and terminal resize handling
This method configures two signal handlers: one for the exit hook that performs a full reset, and another for the SIGWINCH signal that handles terminal resize events by setting a flag and displaying a sleeping message
450 451 452 453 454 455 456 |
# File 'lib/graphina/graph.rb', line 450 def install_handlers at_exit { full_reset } trap(:SIGWINCH) do @full_reset = true perform reset, clear_screen, move_home, 'Zzz…' end end |
#lines ⇒ Integer (private)
The lines method returns the number of lines in the display
This method provides access to the vertical dimension of the graphical display by returning the total number of rows available for rendering content
262 263 264 |
# File 'lib/graphina/graph.rb', line 262 def lines @display.lines end |
#normalize_value(value) ⇒ Float, Integer (private)
The normalize_value method converts a value to its appropriate numeric representation
This method takes an input value and normalizes it to either a Float or Integer type depending on its original form. If the value is already a Float, it is returned as-is. For all other types, the method attempts to convert the value to an integer using to_i
411 412 413 414 415 416 417 418 |
# File 'lib/graphina/graph.rb', line 411 def normalize_value(value) case value when Float value else value.to_i end end |
#perform(*a) ⇒ Object (private)
240 241 242 |
# File 'lib/graphina/graph.rb', line 240 def perform(*a) print(*a) end |
#perform_display_diff ⇒ Object (private)
The perform_display_diff method calculates and displays the difference between the current and previous display states to update only the changed portions of the terminal output
This method synchronizes access to shared display resources using a mutex, then compares the current display with the previous state to determine what needs updating. It handles dimension mismatches by resetting the old display, computes the visual difference, and outputs only the modified portions to reduce terminal update overhead
When the DEBUG_BYTESIZE environment variable is set, it also outputs debugging information about the size of the diff and the time elapsed since the last debug output
378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 |
# File 'lib/graphina/graph.rb', line 378 def perform_display_diff @mutex.synchronize do unless @old_display && @old_display.dimensions == @display.dimensions @old_display = @display.dup.clear end diff = @display - @old_display if ENV['DEBUG_BYTESIZE'] unless @last STDERR.puts %w[ size duration ] * ?\t else STDERR.puts [ diff.size, (Time.now - @last).to_f ] * ?\t end @last = Time.now end perform diff @display, @old_display = @old_display.clear, @display perform move_to(lines, columns) end end |
#pick_color ⇒ Term::ANSIColor::Attribute (private)
The pick_color method determines and returns an ANSI color attribute based on the configured color setting
This method evaluates the @color instance variable to decide how to select a color attribute. If @color is a Proc, it invokes the proc with the @title to determine the color. If @color is nil, it derives a color from the title string. Otherwise, it uses the @color value directly as an index into the ANSI color attributes.
316 317 318 319 320 321 322 323 324 325 326 327 |
# File 'lib/graphina/graph.rb', line 316 def pick_color Term::ANSIColor::Attribute[ case @color when Proc @color.(@title) when nil derive_color_from_string(@title) else @color end ] end |
#pick_secondary_color(color, adjust_brightness:, adjust_brightness_percentage:) ⇒ Term::ANSIColor::Attribute (private)
The pick_secondary_color method determines a secondary color based on a primary color and brightness adjustment parameters It returns the pre-configured secondary color if one exists, otherwise calculates a new color by adjusting the brightness of the primary color
342 343 344 345 346 |
# File 'lib/graphina/graph.rb', line 342 def pick_secondary_color(color, adjust_brightness:, adjust_brightness_percentage:) @color_secondary and return @color_secondary color_primary = color.to_rgb_triple.to_hsl_triple color_primary.send(adjust_brightness, adjust_brightness_percentage) rescue color end |
#sleep_duration ⇒ String (private)
The sleep_duration method returns a string representation of the configured sleep interval with the ‘s’ suffix appended to indicate seconds.
280 281 282 |
# File 'lib/graphina/graph.rb', line 280 def sleep_duration "#{@sleep}s" end |
#sleep_now ⇒ Object (private)
The sleep_now method calculates and executes a sleep duration based on the configured sleep time and elapsed time since start
This method determines how long to sleep by calculating the difference between the configured sleep interval and the time elapsed since the last operation started. If no start time is recorded, it uses the full configured sleep duration. The method ensures that negative sleep durations are not used by taking the maximum of the calculated duration and zero.
356 357 358 359 360 361 362 363 |
# File 'lib/graphina/graph.rb', line 356 def sleep_now duration = if @start [ @sleep - (Time.now - @start).to_f, 0 ].max else @sleep end sleep duration end |
#start ⇒ Object
The start method initiates the graphical display process by setting up signal handlers, performing an initial terminal reset, and entering the main update loop
This method serves as the entry point for starting the graph visualization functionality. It configures the necessary signal handlers for graceful shutdown and terminal resizing, performs an initial full reset of the display state, and then begins the continuous loop that updates and renders graphical data.
125 126 127 128 129 |
# File 'lib/graphina/graph.rb', line 125 def start install_handlers full_reset start_loop end |
#start_loop ⇒ Object (private)
The start_loop method executes a continuous loop to update and display graphical data
This method manages the main execution loop for rendering graphical representations of data values over time. It initializes display state, processes incoming data, calculates visual representations, and handles terminal updates while respecting configured timing intervals.
It continuously updates the display and handles data processing in a loop until explicitly stopped.
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 |
# File 'lib/graphina/graph.rb', line 202 def start_loop full_reset @counter = -1 @continue = true while @continue @start = Time.now @full_reset and full_reset perform hide_cursor @data << @value.(@counter += 1) @data = data.last(columns) if data_range.zero? @display.reset.bottom.styled(:bold). write_centered("#@title / #{sleep_duration}"). reset.centered.styled(:italic).write_centered("no data") perform_display_diff sleep_now next end @display.reset draw_graph @display.reset.bottom.styled(:bold). write_centered("#@title #{format_value(data.last)} / #{sleep_duration}") @display.reset.styled(:bold). left.top.write(format_value(data.max)). left.bottom.write(format_value(data.min)) perform_display_diff sleep_now end rescue Interrupt ensure stop end |
#stop ⇒ Object
The stop method terminates the graphical display process by performing a full reset and setting the continue flag to false
This method serves as the shutdown mechanism for the graph visualization functionality. It ensures that all display resources are properly cleaned up and the terminal state is restored to its original condition before stopping the continuous update loop.
138 139 140 141 |
# File 'lib/graphina/graph.rb', line 138 def stop full_reset @continue = false end |