tammoippen / plotille

Plot in the terminal using braille dots.
MIT License
398 stars 17 forks source link

Two plots side-by-side #38

Closed xtotdam closed 3 years ago

xtotdam commented 3 years ago

Hi! I found this package very useful, thank you!

There is one thing I actually try to figure out without any success. How do I print two plots side-by-side, I mean like :framed_picture: :framed_picture:

Here is my code, but I have no success in making all lines same length:(

import os, sys

import plotille
import numpy as np

x = np.linspace(0, 1, 100)

y = 2*x
plot1 = plotille.plot(x, y,
    lc='red', height=5, width=40, X_label='x', Y_label='T',
    x_min=0, x_max=1, y_min=np.min(y), y_max=np.max(y))

y = np.exp(-x)
plot2 = plotille.plot(x, y,
    lc='green', height=5, width=40, X_label='t', Y_label='T',
    x_min=0, x_max=1, y_min=np.min(y), y_max=np.max(y))

print(plot1)
print(plot2)

plot = (os.linesep).join(l1.ljust(60) + l2.ljust(60) for (l1, l2) in zip(plot1.split(os.linesep), plot2.split(os.linesep)))

print(plot)

and here is what I get изображение

tammoippen commented 3 years ago

Hello @xtotdam ,

Welcome to the realm of unicode and console escape sequences. We cannot simply calculate the line-length here, as each visible 'character' can consist of up to 3 byte (dots are \u2800 - \u28FF see wikipedia) and if colors are involved, each character is surrounded by quite some ANSI escape codes. Hence, we need to do some manual computation:

import os, sys

import plotille
import numpy as np

x = np.linspace(0, 1, 100)
rows = 5
columns = 40

y = 2*x
plot1 = plotille.plot(x, y,
    lc='red', height=rows, width=columns, X_label='x', Y_label='T',
    x_min=0, x_max=1, y_min=np.min(y), y_max=np.max(y))

y = np.exp(-x)
plot2 = plotille.plot(x, y,
    lc='green', height=rows, width=columns, X_label='t', Y_label='T',
    x_min=0, x_max=1, y_min=np.min(y), y_max=np.max(y))

print(plot1)
print(plot2)
print()

lines = plot1.split(os.linesep)
# last line is actually visually longest
max_line = len(lines[-1])

# Arrow up
lines[0] += " " * (max_line - len(lines[0])) + "| "
# max value line
lines[1] += " " * (max_line - len(lines[1])) + "| "
# canvas
# y-axis takes up 13 characters
for row_idx in range(rows):
    lines[2 + row_idx] += " " * (max_line - 13 - columns) + "| "
# x-axis
lines[-2] += " " * (max_line - len(lines[-2])) + "| "
lines[-1] += " " * (max_line - len(lines[-1])) + "| "

plot = (os.linesep).join(l1 + l2 for (l1, l2) in zip(lines, plot2.split(os.linesep)))

print(plot)

2021-05-31 09_23_52-TOTEM ❐ 0 ● 1 fish

xtotdam commented 3 years ago

Hmm, I see, I underestimated the hidden complexity of using unicode characters. Does the Y-axis always take up 13 characters? And on 'completely' unrelated topic, maybe precision here should be altered. I mean for the left plot only one digit after a dot will be sufficient. But anyway, big thanks!

tammoippen commented 3 years ago

Hmm, I see, I underestimated the hidden complexity of using unicode characters.

I think the most impact here actually do have the ansi escape characters; unicode will be correctly identified as one character from python, but when i looked at the bytes, there were quite some more.

Does the Y-axis always take up 13 characters?

Yes: 10 characters for the number formatting and 3 for the ' | ' at the end. See here: https://github.com/tammoippen/plotille/blob/master/plotille/_figure.py#L203

And on 'completely' unrelated topic, maybe precision here should be altered. I mean for the left plot only one digit after a dot will be sufficient.

Sounds good and should be not so difficult. I am open for PR 😄 Otherwise, it will probably take me some time.

tammoippen commented 3 years ago

closing.