terminalplot icon indicating copy to clipboard operation
terminalplot copied to clipboard

modified for multiple y series and ansi colors

Open evert-mouw opened this issue 7 years ago • 2 comments

Hi, Yes this is not the correct way to send in changes but I'm not forking the whole project. For my own use I modified your script so I could add up to four data series on the y axis and have colors. I also made a few other small changes. Maybe you like the changes so others could use them as well.

terminalplot2.zip

evert-mouw avatar Oct 14 '18 18:10 evert-mouw

hi @kressi i really like your terminal plotter and very cool addition with color, debug and multiplot abilitys @evert-mouw!

I try to use it as a live serial plotter for sensor data right on a rp2040. therefore I reposition the cursor so the next plot overwrites the previous.

    for w in range(1,rows+4):  # plus 4 to scroll plot to the top or plus 3 to keep plot at position
        print('\033[1A', end='\x1b[2K')

furthermore I a.pop(0) for any value above a certain len(a) to keep the plot scrolling.

https://github.com/kressi/terminalplot/assets/60987359/183c27cd-d250-4f91-a315-169f6e5d25c2

full python

full python

import time
import numpy
import random

def plot(rows, columns, x, a, b=[], c=[], d=[]):
    # offset for caption
    rows -= 4

    colors = [ '\u001b[32;1m', '\u001b[34;1m', '\u001b[35;1m', '\u001b[36;1m', '\u001b[31;1m' ]
    normalcolors = '\u001b[0m'  # undo color

    pointsymbols = [ colors[0] + "●", colors[1] + "⏺", colors[2] + "▆", colors[3] + "█", colors[4] + "-" ] # the last symbol is used for overlapping series
    series = 0
    overlap = 4 # "X"

    # Create empty canvas
    canvas = [[" " for _ in range(columns)] for _ in range(rows)]

    # Scale points such that they fit on canvas
    y = list( a + b + c + d )
    x_max = max( len(a), len(b), len(c), len(d) ) + 1
    if x_max > len(x):
        x_max = len(x)
    x_scale = scaleCalc(x[:x_max], columns)
    x_scaled = scale(x, x_scale)
    y_scale = scaleCalc(y, rows)
    y_smallest = min(y)


    for q in (a, b, c, d):
        series += 1

        # Scale y-axis series points such that they fit on canvas
        q_scaled = scale(q, y_scale, y_smallest)

        # Add scaled points to canvas
        for ix, iq in zip(x_scaled, q_scaled):
            if canvas[rows - iq - 1][ix] == ' ':
                canvas[rows - iq - 1][ix] = pointsymbols[series-1]
            else:
                canvas[rows - iq - 1][ix] = pointsymbols[overlap]  # multiple y values on the same point

    # Print rows of canvas
    for i in range(rows):
        line = ''.join(str(e) for e in canvas[i]).rstrip()
        print(line)
    print(normalcolors.rstrip(), end='')

    # Print scale
    print(f"\n{round(min(x),4)}min/{round(max(x),4)}max x; {round(min(a),4)}min/{round(max(y),4)}max y; {rows}/{columns}aspect")
    
    # reposition cursor
    for w in range(1,rows+4):  # plus 4 to scroll plot to the top or plus 3 to keep plot at position
        print('\033[1A', end='\x1b[2K')

def scaleCalc(x, length):
    s = float(length - 1) / \
        (max(x) - min(x)) if x and max(x) - min(x) != 0 else length
    return s

def scale(x, scale, smallest=None):
    if smallest==None:
        smallest = min(x)
    return [int((i - smallest) * scale) for i in x]

x = []; a = []; b = []; c = []; d = []

i = 1

while True:
    time.sleep(0.1)
    x.append(i)

    a.append(numpy.sin(i*0.3))
    b.append(numpy.cos(i*0.3))
    c.append(numpy.sin(-i*0.3))
    d.append(random.randint(-1,1))
    
    i += 1

    if len(x) >= 20:  # space between points
        x.pop(0); a.pop(0); b.pop(0); c.pop(0); d.pop(0)

    plot(20, 100, x, a, b, c, d)  # determine canvas sice with x, y

crbyxwpzfl avatar Oct 15 '23 20:10 crbyxwpzfl

Thanks a lot for the feedback and the shared example! I will have a closer look at it within the next couple of days.

kressi avatar Oct 16 '23 10:10 kressi