mirror of
https://github.com/kavishdevar/librepods.git
synced 2026-05-26 10:59:41 +00:00
refactor: Add Python type annotations wherever appropriate (#269)
* Add Python type annotations wherever appropriate * Might as well annotate this too
This commit is contained in:
@@ -1,63 +1,43 @@
|
||||
import math
|
||||
import drawille
|
||||
import numpy as np
|
||||
import logging
|
||||
import os
|
||||
from colors import *
|
||||
from drawille import Canvas
|
||||
from logging import Logger, StreamHandler
|
||||
from matplotlib.animation import FuncAnimation
|
||||
from matplotlib.pyplot import Axes, Figure
|
||||
from numpy.typing import NDArray
|
||||
from os import terminal_size as TerminalSize
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
|
||||
class Colors:
|
||||
RESET = "\033[0m"
|
||||
BOLD = "\033[1m"
|
||||
RED = "\033[91m"
|
||||
GREEN = "\033[92m"
|
||||
YELLOW = "\033[93m"
|
||||
BLUE = "\033[94m"
|
||||
MAGENTA = "\033[95m"
|
||||
CYAN = "\033[96m"
|
||||
WHITE = "\033[97m"
|
||||
BG_BLACK = "\033[40m"
|
||||
|
||||
class ColorFormatter(logging.Formatter):
|
||||
FORMATS = {
|
||||
logging.DEBUG: Colors.BLUE + "[%(levelname)s] %(message)s" + Colors.RESET,
|
||||
logging.INFO: Colors.GREEN + "%(message)s" + Colors.RESET,
|
||||
logging.WARNING: Colors.YELLOW + "%(message)s" + Colors.RESET,
|
||||
logging.ERROR: Colors.RED + "[%(levelname)s] %(message)s" + Colors.RESET,
|
||||
logging.CRITICAL: Colors.RED + Colors.BOLD + "[%(levelname)s] %(message)s" + Colors.RESET
|
||||
}
|
||||
|
||||
def format(self, record):
|
||||
log_fmt = self.FORMATS.get(record.levelno)
|
||||
formatter = logging.Formatter(log_fmt, datefmt="%H:%M:%S")
|
||||
return formatter.format(record)
|
||||
|
||||
handler = logging.StreamHandler()
|
||||
handler: StreamHandler = StreamHandler()
|
||||
handler.setFormatter(ColorFormatter())
|
||||
log = logging.getLogger(__name__)
|
||||
log: Logger = logging.getLogger(__name__)
|
||||
log.setLevel(logging.INFO)
|
||||
log.addHandler(handler)
|
||||
log.propagate = False
|
||||
|
||||
|
||||
class HeadOrientation:
|
||||
def __init__(self, use_terminal=False):
|
||||
self.orientation_offset = 5500
|
||||
self.o1_neutral = 19000
|
||||
self.o2_neutral = 0
|
||||
self.o3_neutral = 0
|
||||
self.calibration_samples = []
|
||||
self.calibration_complete = False
|
||||
self.calibration_sample_count = 10
|
||||
self.fig = None
|
||||
self.ax = None
|
||||
self.arrow = None
|
||||
self.animation = None
|
||||
self.use_terminal = use_terminal
|
||||
def __init__(self, use_terminal: bool = False) -> None:
|
||||
self.orientation_offset: int = 5500
|
||||
self.o1_neutral: int = 19000
|
||||
self.o2_neutral: int = 0
|
||||
self.o3_neutral: int = 0
|
||||
self.calibration_samples: List[List[int]] = []
|
||||
self.calibration_complete: bool = False
|
||||
self.calibration_sample_count: int = 10
|
||||
self.fig: Optional[Figure] = None
|
||||
self.ax: Optional[Axes] = None
|
||||
self.arrow: Any = None
|
||||
self.animation: Optional[FuncAnimation] = None
|
||||
self.use_terminal: bool = use_terminal
|
||||
|
||||
def reset_calibration(self):
|
||||
def reset_calibration(self) -> None:
|
||||
self.calibration_samples = []
|
||||
self.calibration_complete = False
|
||||
|
||||
def add_calibration_sample(self, orientation_values):
|
||||
def add_calibration_sample(self, orientation_values: List[int]) -> bool:
|
||||
if len(self.calibration_samples) < self.calibration_sample_count:
|
||||
self.calibration_samples.append(orientation_values)
|
||||
return False
|
||||
@@ -66,57 +46,58 @@ class HeadOrientation:
|
||||
return True
|
||||
return True
|
||||
|
||||
def _calculate_calibration(self):
|
||||
def _calculate_calibration(self) -> None:
|
||||
if len(self.calibration_samples) < 3:
|
||||
log.warning("Not enough calibration samples")
|
||||
return
|
||||
samples = np.array(self.calibration_samples)
|
||||
self.o1_neutral = np.mean(samples[:, 0])
|
||||
avg_o2 = np.mean(samples[:, 1])
|
||||
avg_o3 = np.mean(samples[:, 2])
|
||||
self.o2_neutral = avg_o2
|
||||
self.o3_neutral = avg_o3
|
||||
samples: NDArray[[List[int]]] = np.array(self.calibration_samples)
|
||||
self.o1_neutral: float = np.mean(samples[:, 0])
|
||||
avg_o2: float = np.mean(samples[:, 1])
|
||||
avg_o3: float = np.mean(samples[:, 2])
|
||||
self.o2_neutral: float = avg_o2
|
||||
self.o3_neutral: float = avg_o3
|
||||
log.info("Calibration complete: o1_neutral=%.2f, o2_neutral=%.2f, o3_neutral=%.2f",
|
||||
self.o1_neutral, self.o2_neutral, self.o3_neutral)
|
||||
self.calibration_complete = True
|
||||
|
||||
def calculate_orientation(self, o1, o2, o3):
|
||||
def calculate_orientation(self, o1: float, o2: float, o3: float) -> Dict[str, float]:
|
||||
if not self.calibration_complete:
|
||||
return {'pitch': 0, 'yaw': 0}
|
||||
o1_norm = o1 - self.o1_neutral
|
||||
o2_norm = o2 - self.o2_neutral
|
||||
o3_norm = o3 - self.o3_neutral
|
||||
pitch = (o2_norm + o3_norm) / 2 / 32000 * 180
|
||||
yaw = (o2_norm - o3_norm) / 2 / 32000 * 180
|
||||
o1_norm: float = o1 - self.o1_neutral
|
||||
o2_norm: float = o2 - self.o2_neutral
|
||||
o3_norm: float = o3 - self.o3_neutral
|
||||
pitch: float = (o2_norm + o3_norm) / 2 / 32000 * 180
|
||||
yaw: float = (o2_norm - o3_norm) / 2 / 32000 * 180
|
||||
return {'pitch': pitch, 'yaw': yaw}
|
||||
|
||||
def create_face_art(self, pitch, yaw):
|
||||
def create_face_art(self, pitch: float, yaw: float) -> str:
|
||||
if self.use_terminal:
|
||||
try:
|
||||
ts = os.get_terminal_size()
|
||||
ts: TerminalSize = os.get_terminal_size()
|
||||
width, height = ts.columns, ts.lines * 2
|
||||
except Exception:
|
||||
width, height = 80, 40
|
||||
else:
|
||||
width, height = 80, 40
|
||||
center_x, center_y = width // 2, height // 2
|
||||
radius = (min(width, height) // 2 - 2) // 2
|
||||
pitch_rad = math.radians(pitch)
|
||||
yaw_rad = math.radians(yaw)
|
||||
canvas = drawille.Canvas()
|
||||
def rotate_point(x, y, z, pitch_r, yaw_r):
|
||||
radius: int = (min(width, height) // 2 - 2) // 2
|
||||
pitch_rad: float = math.radians(pitch)
|
||||
yaw_rad: float = math.radians(yaw)
|
||||
canvas: Canvas = Canvas()
|
||||
|
||||
def rotate_point(x: float, y: float, z: float, pitch_r: float, yaw_r: float) -> Tuple[int, int]:
|
||||
cos_y, sin_y = math.cos(yaw_r), math.sin(yaw_r)
|
||||
cos_p, sin_p = math.cos(pitch_r), math.sin(pitch_r)
|
||||
x1 = x * cos_y - z * sin_y
|
||||
z1 = x * sin_y + z * cos_y
|
||||
y1 = y * cos_p - z1 * sin_p
|
||||
z2 = y * sin_p + z1 * cos_p
|
||||
scale = 1 + (z2 / width)
|
||||
x1: float = x * cos_y - z * sin_y
|
||||
z1: float = x * sin_y + z * cos_y
|
||||
y1: float = y * cos_p - z1 * sin_p
|
||||
z2: float = y * sin_p + z1 * cos_p
|
||||
scale: float = 1 + (z2 / width)
|
||||
return int(center_x + x1 * scale), int(center_y + y1 * scale)
|
||||
for angle in range(0, 360, 2):
|
||||
rad = math.radians(angle)
|
||||
x = radius * math.cos(rad)
|
||||
y = radius * math.sin(rad)
|
||||
rad: float = math.radians(angle)
|
||||
x: float = radius * math.cos(rad)
|
||||
y: float = radius * math.sin(rad)
|
||||
x1, y1 = rotate_point(x, y, 0, pitch_rad, yaw_rad)
|
||||
canvas.set(x1, y1)
|
||||
for eye in [(-radius//2, -radius//3, 2), (radius//2, -radius//3, 2)]:
|
||||
@@ -129,14 +110,14 @@ class HeadOrientation:
|
||||
for dx in [-1, 0, 1]:
|
||||
for dy in [-1, 0, 1]:
|
||||
canvas.set(nx + dx, ny + dy)
|
||||
smile_depth = radius // 8
|
||||
mouth_local_y = radius // 4
|
||||
mouth_length = radius
|
||||
smile_depth: int = radius // 8
|
||||
mouth_local_y: int = radius // 4
|
||||
mouth_length: int = radius
|
||||
for x_offset in range(-mouth_length // 2, mouth_length // 2 + 1):
|
||||
norm = abs(x_offset) / (mouth_length / 2)
|
||||
y_offset = int((1 - norm ** 2) * smile_depth)
|
||||
local_x = x_offset
|
||||
local_y = mouth_local_y + y_offset
|
||||
norm: float = abs(x_offset) / (mouth_length / 2)
|
||||
y_offset: int = int((1 - norm ** 2) * smile_depth)
|
||||
local_x: int = x_offset
|
||||
local_y: int = mouth_local_y + y_offset
|
||||
mx, my = rotate_point(local_x, local_y, 0, pitch_rad, yaw_rad)
|
||||
canvas.set(mx, my)
|
||||
return canvas.frame()
|
||||
|
||||
Reference in New Issue
Block a user