diff --git a/kglobe.py b/kglobe.py index 7d76f92..dc00989 100644 --- a/kglobe.py +++ b/kglobe.py @@ -10,6 +10,11 @@ import re import requests import argparse from io import BytesIO +import asyncio +import sys +import termios +import tty +import select # TODO: Color arches based on latency # TODO: Interactive globe (spin w/ keys) @@ -17,6 +22,67 @@ from io import BytesIO # TODO: Image spacing # TODO: Async rendering? +class TerminalPlotter(pv.Plotter): + def __init__(self, width, height, **kwargs): + super().__init__(off_screen=True, window_size=(height, width), **kwargs) + self._running = True + h_pix, _ = kitty.get_terminal_cell_size() + self.rows, _ = kitty.get_terminal_size() + self.start_y, self.start_x = kitty.get_position() + # the image requires height/cell_height lines + self.needed_lines = math.ceil(height/h_pix) + self.set_background([0.0, 1.0, 0.0]) + #if self.start_y == self.rows: + if self.rows - self.start_y < self.needed_lines: + self.set_background([1.0, 0.0, 0.0]) + missing = self.needed_lines - (self.rows - self.start_y) + self.start_y -= missing + #self.start_y -= + #print("\n" * self.needed_lines, end="") + print("\n" * self.needed_lines, end="") + kitty.set_position(self.start_y, self.start_x) + + def render_to_kitty(self): + import io + self.render() + buf = io.BytesIO() + self.screenshot(buf, transparent_background=True) + kitty.draw_to_terminal(buf) + print('y:', self.start_y, 'rows:', self.rows, end='') + kitty.set_position(self.start_y, self.start_x) + + async def _input_loop(self): + fd = sys.stdin.fileno() + old_settings = termios.tcgetattr(fd) + try: + tty.setcbreak(fd) + while self._running: + rlist, _, _ = select.select([sys.stdin], [], [], 0) + if rlist: + c = sys.stdin.read(1) + if c == 'q': # quit on q + self._running = False + else: + self._handle_key(c) + await asyncio.sleep(0.01) + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + + def _handle_key(self, c): + # Simple interaction: rotate camera with a/s keys + if c == 'a': + self.camera.Azimuth(10) + elif c == 'd': + self.camera.Azimuth(-10) + elif c == 'w': + self.camera.Elevation(10) + elif c == 's': + self.camera.Elevation(-10) + self.render_to_kitty() + + async def run(self): + self.render_to_kitty() + await self._input_loop() # Convert lat/lon to Cartesian coordinates def latlon_to_xyz(lat: float, lon: float, radius=1.0): @@ -84,7 +150,6 @@ def main(): ) parser.add_argument("-t", "--traceroute", default=None) - parser.add_argument("-e", "--external", action="store_true") args = parser.parse_args() @@ -114,7 +179,9 @@ def main(): # Convert to 3D coordinates points_3d = [latlon_to_xyz(lat, lon) for lat, lon in locations] - pl: pv.Plotter = pv.Plotter(off_screen=(not args.external)) + #pl: pv.Plotter = pv.Plotter(off_screen=(not args.external)) + height, width = kitty.get_terminal_size_pixel() + pl = TerminalPlotter(450, 450) pl.add_mesh(globe, color="tan", smooth_shading=True, texture=tex, show_edges=False) for pt in points_3d: @@ -125,26 +192,9 @@ def main(): line = pv.lines_from_points(arch, close=False) pl.add_mesh(line, color="red", line_width=2) - kitty.hide_cursor() - y, x = kitty.get_position() - - height, width = kitty.get_terminal_size_pixel() - h_pix, w_pix = kitty.get_terminal_cell_size() - - # the image requires height/cell_height lines - needed_lines = round(height/h_pix) - print("\n" * needed_lines, end="") - + #kitty.hide_cursor() try: - if not args.external: - while True: - pl.camera.Azimuth(1) - buf: BytesIO = BytesIO() - pl.screenshot(buf, transparent_background=True, window_size=(height, width)) - kitty.set_position(y - needed_lines, 0) - kitty.draw_to_terminal(buf) - else: - pl.show() + asyncio.run(pl.run()) finally: kitty.show_cursor() diff --git a/kitty.py b/kitty.py index 146b469..9725618 100755 --- a/kitty.py +++ b/kitty.py @@ -125,7 +125,7 @@ def get_position() -> tuple[int, int]: if __name__ == "__main__": import pyvista as pv - cols, rows = get_terminal_size() + rows, cols = get_terminal_size() v_pix, h_pix = get_terminal_cell_size() height, width = get_terminal_size_pixel() @@ -133,6 +133,9 @@ if __name__ == "__main__": print(f'Terminal has {rows} rows, {cols} cols = {rows * cols} cells') print(f'Cell size: {h_pix}x{v_pix} px') print(f'Dimensions: {width}x{height} px') + print('Cursor is at y, x:', get_position()) + import time + time.sleep(2) new_lines = int((height/v_pix)-6)