Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New features #35

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions example/ex_line_plot.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/bin/env ruby
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New example for lineplot with different canvases including :block

require_relative "../lib/unicode_plot"

# example of line plots using different renderers
UnicodePlot.lineplot([1, 2, 7], [9, -6, 8], title: "Default Lineplot").render
UnicodePlot.lineplot([1, 2, 7], [9, -6, 8], title: "Ascii Lineplot", canvas: :ascii).render
UnicodePlot.lineplot([1, 2, 7], [9, -6, 8], title: "Dot Lineplot", canvas: :dot).render
UnicodePlot.lineplot([1, 2, 7], [9, -6, 8], title: "Block Lineplot", canvas: :block).render
23 changes: 23 additions & 0 deletions example/ex_staircase_plot.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/bin/env ruby
require_relative "../lib/unicode_plot"

# single plot at a time
UnicodePlot.stairs([1, 2, 4, 7, 8], [1, 3, 4, 2, 7], style: :post).render

# pre: style
UnicodePlot.stairs([1, 2, 4, 7, 8], [1, 3, 4, 2, 7], style: :pre).render

# Another single plot with title
UnicodePlot.stairs([2, 3, 5, 6, 9], [2, 5, 3, 4, 2], title: "My Staircase Plot").render

# Two plots at a time.
# Using an explicit limit because data for the 2nd plot is outside the bounds from the 1st plot
plot = UnicodePlot.stairs([1, 2, 4, 7, 8], [1, 3, 4, 2, 7], title: "Two Staircases", xlim: [1,10] )
UnicodePlot.stairs!(plot, [0, 3, 5, 6, 9], [2, 5, 3, 4, 0])
plot.render

plot = UnicodePlot.stairs([1, 2, 4, 7, 8], [1, 3, 4, 2, 7], title: "Two Staircases", xlim: [1,10] , canvas: :block)
UnicodePlot.stairs!(plot, [0, 3, 5, 6, 9], [2, 5, 3, 4, 0])
plot.render


9 changes: 9 additions & 0 deletions example/issue32.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/env ruby
require_relative "../lib/unicode_plot"

ys = [261, 272, 277, 283, 289, 294, 298, 305, 309, 314, 319, 320, 322, 323, 324]
#ys = [283, 289, 294, 298, 305]
xs = ys.size.times.to_a

UnicodePlot.lineplot( xs, ys, height: 26, ylim: [0, 700]).render
UnicodePlot.lineplot( xs, ys, height: 26, ylim: [0, 700], canvas: :block).render
16 changes: 16 additions & 0 deletions example/issue34.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/bin/env ruby
require_relative "../lib/unicode_plot"

# example of using the 256 color pallette
cmax = 255
# dummy

plt = UnicodePlot.lineplot([0,0],[0,0], title: "Bar", color: 0, xlim: [0,31], ylim: [0,31], width: 33, height: 33)
(0..cmax).each do |colornum|
x1 = (colornum / 32).floor * 4
x2 = x1 + 3
y = colornum % 32
UnicodePlot.lineplot!(plt, [x1, x2],[y, y], color: colornum)
end
plt.render

43 changes: 24 additions & 19 deletions lib/unicode_plot.rb
Original file line number Diff line number Diff line change
@@ -1,25 +1,30 @@
require 'stringio'

require 'unicode_plot/version'
require_relative './unicode_plot/version'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

converted to relative requires because it makes development easier for me, but can revert if you prefer normal require.


require 'unicode_plot/utils'
require 'unicode_plot/styled_printer'
require 'unicode_plot/value_transformer'
require 'unicode_plot/renderer'
require_relative './unicode_plot/utils'
require_relative './unicode_plot/styled_printer'
require_relative './unicode_plot/value_transformer'
require_relative './unicode_plot/renderer'

require 'unicode_plot/canvas'
require 'unicode_plot/braille_canvas'
require 'unicode_plot/density_canvas'
require 'unicode_plot/lookup_canvas'
require 'unicode_plot/ascii_canvas'
require 'unicode_plot/dot_canvas'
require_relative './unicode_plot/canvas'
require_relative './unicode_plot/braille_canvas'
require_relative './unicode_plot/density_canvas'
require_relative './unicode_plot/lookup_canvas'
require_relative './unicode_plot/ascii_canvas'
require_relative './unicode_plot/dot_canvas'
require_relative './unicode_plot/block_canvas'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new block canvas


require 'unicode_plot/plot'
require 'unicode_plot/grid_plot'
require_relative './unicode_plot/plot'
require_relative './unicode_plot/grid_plot'

require_relative './unicode_plot/barplot'
require_relative './unicode_plot/boxplot'
require_relative './unicode_plot/densityplot'
require_relative './unicode_plot/lineplot'
require_relative './unicode_plot/histogram'
require_relative './unicode_plot/scatterplot'

# new!
require_relative './unicode_plot/stairs'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new stair plot


require 'unicode_plot/barplot'
require 'unicode_plot/boxplot'
require 'unicode_plot/densityplot'
require 'unicode_plot/lineplot'
require 'unicode_plot/histogram'
require 'unicode_plot/scatterplot'
38 changes: 38 additions & 0 deletions lib/unicode_plot/block_canvas.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# coding: utf-8

=begin
The `BlockCanvas` is also Unicode-based.
It has half the resolution of the `BrailleCanvas`.
In contrast to BrailleCanvas, the pixels don't
have visible spacing between them.
This canvas effectively turns every character
into 4 pixels that can individually be manipulated
using binary operations.
=end

module UnicodePlot
class BlockCanvas < LookupCanvas
X_PIXEL_PER_CHAR = 2
Y_PIXEL_PER_CHAR = 2

def initialize(width, height, fill_char=0, **kw)
super(width, height,
X_PIXEL_PER_CHAR,
Y_PIXEL_PER_CHAR,
fill_char,
**kw)
end

BLOCK_SIGNS = [ [0b1000, 0b0010].freeze,
[0b0100, 0b0001].freeze
].freeze

BLOCK_DECODE = [' ', '▗', '▖', '▄',
'▝', '▐', '▞', '▟',
'▘', '▚', '▌', '▙',
'▀', '▜', '▛', '█' ].freeze

def lookup_encode(x,y) ; BLOCK_SIGNS[x][y] ; end
def lookup_decode(x) ; BLOCK_DECODE[x] ; end
end
end
20 changes: 12 additions & 8 deletions lib/unicode_plot/braille_canvas.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,21 @@ def pixel!(pixel_x, pixel_y, color)
pixel_x -= 1 unless pixel_x < pixel_width
pixel_y -= 1 unless pixel_y < pixel_height
tx = pixel_x.fdiv(pixel_width) * width
char_x = tx.floor + 1
char_x_off = pixel_x % X_PIXEL_PER_CHAR + 1
char_x += 1 if char_x < tx.round + 1 && char_x_off == 1

char_y = (pixel_y.fdiv(pixel_height) * height).floor + 1
char_y_off = pixel_y % Y_PIXEL_PER_CHAR + 1
char_x = tx.floor
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is both a fix for interpolation issue #32 but also removes several of the +1 / -1 manipulations needed which were there because the original UnicodePlots.jl (Julia) uses 1-based array indexing.

char_x_off = pixel_x % X_PIXEL_PER_CHAR
char_x += 1 if char_x < tx.round && char_x_off == 0

index = index_at(char_x - 1, char_y - 1)
char_y_off = pixel_y % Y_PIXEL_PER_CHAR
char_y = (pixel_y - char_y_off) / Y_PIXEL_PER_CHAR

index = index_at(char_x, char_y)
if index
@grid[index] = (@grid[index].ord | BRAILLE_SIGNS[char_x_off - 1][char_y_off - 1]).chr(Encoding::UTF_8)
@colors[index] |= COLOR_ENCODE[color]
@grid[index] = (@grid[index].ord | BRAILLE_SIGNS[char_x_off][char_y_off]).chr(Encoding::UTF_8)
# If we can fetch color from color-encode then or with the existing color.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Color handling done using fetch where the numeric color is used if the symbol lookup is unsuccessful.
This allows support of the 256 color palette that is in the styled-print.
Please note that if 256 color is used, the mixing (OR'ing) functionality doesn't not work properly.

# this works for cases where color is 0..7 and we implement a 'mixer'
# if color is beyond 7, then use it directly.
@colors[index] |= COLOR_ENCODE.fetch(color, color)
end
color
end
Expand Down
2 changes: 2 additions & 0 deletions lib/unicode_plot/canvas.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ def self.create(canvas_type, width, height, **kw)
BrailleCanvas.new(width, height, **kw)
when :density
DensityCanvas.new(width, height, **kw)
when :block
BlockCanvas.new(width, height, **kw)
when :dot
DotCanvas.new(width, height, **kw)
else
Expand Down
114 changes: 114 additions & 0 deletions lib/unicode_plot/heatmap_canvas.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# coding: utf-8
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, did not mean to check this file in yet, heatmap support is not finished.

require 'paint'

# The `HeatmapCanvas` is also Unicode-based.
# It has a half the resolution of the `BlockCanvas`.
# This canvas effectively turns every character
# into two pixels (top and bottom).
module UnicodePlot
class HeatmapCanvas < LookupCanvas
# grid::Array{UInt8,2}
# colors::Array{UInt8,2}
# pixel_width::Int
# pixel_height::Int
# origin_x::Float64
# origin_y::Float64
# width::Float64
# height::Float64
X_PIXEL_PER_CHAR = 1
Y_PIXEL_PER_CHAR = 2

HALF_BLOCK = '▄'
HEATMAP_ENCODE = [[0, 0], [1, 1]].freeze
HEATMAP_DECODE = [HALF_BLOCK, HALF_BLOCK].freeze

# @inline nrows(c::HeatmapCanvas) = div(size(grid(c), 2) + 1, 2)
def initialize(width, height, **kw)
super(width, height,
width * X_PIXEL_PER_CHAR,
height * Y_PIXEL_PER_CHAR,
"\u{2800}",
x_pixel_per_char: X_PIXEL_PER_CHAR,
y_pixel_per_char: Y_PIXEL_PER_CHAR,
**kw)
end

def print_row(out, row_index)
unless 0 <= row_index && row_index < height
raise ArgumentError, "row_index out of bounds"
end
y = 2 * row_index
# extend the plot upwards by half a row
y -= 1 if height.odd?

is_color = out.isatty
(0 ... width).each do |x|
if is_color
fgcol = color_at(x,y)
if (y - 1) > 0
bgcol = color_at(x, y - 1)
out.print Paint[BorderMaps::BORDER_SOLID[:l], :light_black]
out.print Paint[HALF_BLOCK, fgcol, bgcol]
# for odd numbers of rows, only print the foreground for the top row
else
out.print Paint[HALF_BLOCK, fgcol]
end
else
out.print HALF_BLOCK
end
end
if is_color
out.print Paint[:reset]
end
end

def print_color_barrow(out, row_index, colormap, border, lim, plot_padding, zlabel)
b = BorderMaps::BORDER_MAP[border]
min_z, max_z = lim[0..1]
if row_index == 1
# print top border and maximum z value
out.print Paint[ b[:tl], :light_black]
out.print Paint[ b[:t], :light_black]
out.print Paint[ b[:t], :light_black]
out.print Paint[ b[:tr], :light_black]
max_z_str = max_z.is_a?(Integer) ? max_z : float_round_log10(max_z)
out.print plot_padding
out.print Paint[io, max_z_str, :light_black]
elsif row_index == height
# print bottom border and minimum z value
out.print Paint[ b[:bl], :light_black]
out.print Paint[ b[:b], :light_black]
out.print Paint[ b[:b], :light_black]
out.print Paint[ b[:br], :light_black]
min_z_str = min_z.is_a?(Integer) ? min_z : float_round_log10(min_z)
out.print plot_padding
out.print Paint[min_z_str, :light_black]
else
# print gradient
out.print Paint[ b[:l], :light_black]
# if min and max are the same, single color
if min_z == max_z
bgcol = colormap(1, 1, 1)
fgcol = bgcol
# otherwise, blend from min to max
else
n = 2*(nrows(c) - 2)
r = row_index - 2
bgcol = colormap(n - (2*r), 1, n)
fgcol = colormap(n - (2*r + 1), 1, n)
end
out.print Paint[HALF_BLOCK, fgcol, bgcol]
out.print HALF_BLOCK
out.print Paint[:reset]
out.print Paint[ b[:r], :light_black]

# print z label
if row_index == div(nrows(c), 2) + 1
out.print(plot_padding)
out.print(zlabel)
end
end
end
end
end

16 changes: 8 additions & 8 deletions lib/unicode_plot/lookup_canvas.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,17 @@ def pixel!(pixel_x, pixel_y, color)
pixel_y -= 1 unless pixel_y < pixel_height

tx = pixel_x.fdiv(pixel_width) * width
char_x = tx.floor + 1
char_x_off = pixel_x % x_pixel_per_char + 1
char_x += 1 if char_x < tx.round + 1 && char_x_off == 1
char_x = tx.floor
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

simlar to canvas.rb, we have

char_x_off = pixel_x % x_pixel_per_char
char_x += 1 if char_x < tx.round && char_x_off == 0

char_y = (pixel_y.fdiv(pixel_height) * height).floor + 1
char_y_off = pixel_y % y_pixel_per_char + 1
char_y_off = pixel_y % y_pixel_per_char
char_y = (pixel_y - char_y_off) / y_pixel_per_char

index = index_at(char_x - 1, char_y - 1)
index = index_at(char_x, char_y)
if index
@grid[index] |= lookup_encode(char_x_off - 1, char_y_off - 1)
@colors[index] |= COLOR_ENCODE[color]
@grid[index] |= lookup_encode(char_x_off, char_y_off)
@colors[index] |= COLOR_ENCODE.fetch(color, color)
end
end

Expand Down
Loading