From 19a7d1d5d71a857d942835e27ee195c09cc54414 Mon Sep 17 00:00:00 2001 From: Kumar <36933347+rav4kumar@users.noreply.github.com> Date: Tue, 30 Dec 2025 22:27:53 -0700 Subject: [PATCH] [TIZI/TICI] ui: update dmoji position and Developer UI adjustments (#1601) * ui: improve layout and centering of bottom developer UI elements * int * less is more, y'all * always show actual lat for all cars * lint * perfect * cleanup * too long * inherit * remove unused * inir * need to fix * final --------- Co-authored-by: Jason Wen --- selfdrive/ui/onroad/augmented_road_view.py | 1 + .../onroad/developer_ui/__init__.py | 51 +++++++++++++------ .../onroad/developer_ui/elements.py | 21 ++++++++ .../ui/sunnypilot/onroad/driver_state.py | 49 ++++++++++++++++++ 4 files changed, 107 insertions(+), 15 deletions(-) create mode 100644 selfdrive/ui/sunnypilot/onroad/driver_state.py diff --git a/selfdrive/ui/onroad/augmented_road_view.py b/selfdrive/ui/onroad/augmented_road_view.py index 1d66379f9..a1d10193a 100644 --- a/selfdrive/ui/onroad/augmented_road_view.py +++ b/selfdrive/ui/onroad/augmented_road_view.py @@ -16,6 +16,7 @@ from openpilot.common.transformations.orientation import rot_from_euler if gui_app.sunnypilot_ui(): from openpilot.selfdrive.ui.sunnypilot.onroad.hud_renderer import HudRendererSP as HudRenderer + from openpilot.selfdrive.ui.sunnypilot.onroad.driver_state import DriverStateRendererSP as DriverStateRenderer from openpilot.selfdrive.ui.sunnypilot.onroad.augmented_road_view import BORDER_COLORS_SP diff --git a/selfdrive/ui/sunnypilot/onroad/developer_ui/__init__.py b/selfdrive/ui/sunnypilot/onroad/developer_ui/__init__.py index 14a224ae7..441a169d2 100644 --- a/selfdrive/ui/sunnypilot/onroad/developer_ui/__init__.py +++ b/selfdrive/ui/sunnypilot/onroad/developer_ui/__init__.py @@ -22,6 +22,7 @@ class DeveloperUiRenderer(Widget): DEV_UI_RIGHT = 1 DEV_UI_BOTTOM = 2 DEV_UI_BOTH = 3 + BOTTOM_BAR_HEIGHT = 61 def __init__(self): super().__init__() @@ -43,6 +44,12 @@ class DeveloperUiRenderer(Widget): self.bearing_elem = BearingDegElement() self.altitude_elem = AltitudeElement() + @staticmethod + def get_bottom_dev_ui_offset(): + if ui_state.developer_ui in (DeveloperUiRenderer.DEV_UI_BOTTOM, DeveloperUiRenderer.DEV_UI_BOTH): + return DeveloperUiRenderer.BOTTOM_BAR_HEIGHT + return 0 + def _update_state(self) -> None: self.dev_ui_mode = ui_state.developer_ui @@ -78,10 +85,11 @@ class DeveloperUiRenderer(Widget): ] if controls_state.lateralControlState.which() == 'torqueState': elements.append(self.desired_lat_accel_elem.update(sm, ui_state.is_metric)) - elements.append(self.actual_lat_accel_elem.update(sm, ui_state.is_metric)) else: elements.append(self.desired_steer_elem.update(sm, ui_state.is_metric)) + elements.append(self.actual_lat_accel_elem.update(sm, ui_state.is_metric)) + current_y = y for element in elements: current_y += self._draw_right_dev_ui_element(x, current_y, element) @@ -105,7 +113,7 @@ class DeveloperUiRenderer(Widget): if element.unit: units_height = measure_text_cached(self._font_bold, element.unit, unit_size, 0).x - units_x = x + container_width - 10 + units_x = x + container_width units_y = y + (value_size / 2) + (units_height / 2) rl.draw_text_pro(self._font_bold, element.unit, rl.Vector2(units_x, units_y), rl.Vector2(0, 0), -90.0, unit_size, 0, rl.WHITE) @@ -143,22 +151,35 @@ class DeveloperUiRenderer(Widget): if sm.valid['gpsLocationExternal'] or sm.valid['gpsLocation']: elements.append(self.altitude_elem.update(sm, ui_state.is_metric)) - current_x = int(rect.x + 90) - center_y = y + bar_height // 2 - for element in elements: - current_x += self._draw_bottom_dev_ui_element(current_x, center_y, element) + if not elements: + return - def _draw_bottom_dev_ui_element(self, x: int, y: int, element: UiElement) -> int: font_size = 38 + element_widths = [] + for element in elements: + element.measure(self._font_bold, font_size) + element_widths.append(element.total_width) - label_text = f"{element.label} " - label_width = measure_text_cached(self._font_bold, label_text, font_size, 0).x - rl.draw_text_ex(self._font_bold, label_text, rl.Vector2(x, y - font_size // 2), font_size, 0, rl.WHITE) + total_element_width = sum(element_widths) + num_gaps = len(elements) + 1 + available_width = rect.width + gap_width = (available_width - total_element_width) / num_gaps - value_width = measure_text_cached(self._font_bold, element.value, font_size, 0).x - rl.draw_text_ex(self._font_bold, element.value, rl.Vector2(x + label_width + 10, y - font_size // 2), font_size, 0, element.color) + center_y = y + bar_height // 2 + current_x = rect.x + gap_width + + for i, element in enumerate(elements): + element_center_x = int(current_x + element_widths[i] / 2) + self._draw_bottom_dev_ui_element(element_center_x, center_y, element) + current_x += element_widths[i] + gap_width + + def _draw_bottom_dev_ui_element(self, center_x: int, y: int, element: UiElement) -> None: + font_size = 38 + start_x = center_x - element.total_width / 2 + + rl.draw_text_ex(self._font_bold, element.label_text, rl.Vector2(start_x, y - font_size // 2), font_size, 0, rl.WHITE) + rl.draw_text_ex(self._font_bold, element.val_text, rl.Vector2(start_x + element.label_width, y - font_size // 2), font_size, 0, element.color) if element.unit: - rl.draw_text_ex(self._font_bold, element.unit, rl.Vector2(x + label_width + value_width + 20, y - font_size // 2), font_size, 0, rl.WHITE) - - return 400 + rl.draw_text_ex(self._font_bold, element.unit_text, rl.Vector2(start_x + element.label_width + element.val_width, y - font_size // 2), + font_size, 0, rl.WHITE) diff --git a/selfdrive/ui/sunnypilot/onroad/developer_ui/elements.py b/selfdrive/ui/sunnypilot/onroad/developer_ui/elements.py index e8daca886..652469bdd 100644 --- a/selfdrive/ui/sunnypilot/onroad/developer_ui/elements.py +++ b/selfdrive/ui/sunnypilot/onroad/developer_ui/elements.py @@ -10,12 +10,33 @@ from dataclasses import dataclass from openpilot.common.constants import CV +from openpilot.system.ui.lib.text_measure import measure_text_cached + + @dataclass class UiElement: value: str label: str unit: str color: rl.Color + val_text: str = "" + label_text: str = "" + unit_text: str = "" + val_width: float = 0.0 + label_width: float = 0.0 + unit_width: float = 0.0 + total_width: float = 0.0 + + def measure(self, font, font_size: int): + self.label_text = f"{self.label} " + self.val_text = self.value + self.unit_text = f" {self.unit}" if self.unit else "" + + self.label_width = measure_text_cached(font, self.label_text, font_size, 0).x + self.val_width = measure_text_cached(font, self.val_text, font_size, 0).x + self.unit_width = measure_text_cached(font, self.unit_text, font_size, 0).x if self.unit else 0 + + self.total_width = self.label_width + self.val_width + self.unit_width class LeadInfoElement: diff --git a/selfdrive/ui/sunnypilot/onroad/driver_state.py b/selfdrive/ui/sunnypilot/onroad/driver_state.py new file mode 100644 index 000000000..4f2d264ee --- /dev/null +++ b/selfdrive/ui/sunnypilot/onroad/driver_state.py @@ -0,0 +1,49 @@ +""" +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +This file is part of sunnypilot and is licensed under the MIT License. +See the LICENSE.md file in the root directory for more details. +""" +import numpy as np + +from openpilot.selfdrive.ui import UI_BORDER_SIZE +from openpilot.selfdrive.ui.onroad.driver_state import DriverStateRenderer, BTN_SIZE, ARC_LENGTH +from openpilot.selfdrive.ui.sunnypilot.onroad.developer_ui import DeveloperUiRenderer + + +class DriverStateRendererSP(DriverStateRenderer): + def __init__(self): + super().__init__() + self.dev_ui_offset = DeveloperUiRenderer.get_bottom_dev_ui_offset() + + def _pre_calculate_drawing_elements(self): + """Pre-calculate all drawing elements based on the current rectangle""" + # Calculate icon position (bottom-left or bottom-right) + width, height = self._rect.width, self._rect.height + offset = UI_BORDER_SIZE + BTN_SIZE // 2 + self.position_x = self._rect.x + (width - offset if self.is_rhd else offset) + self.position_y = self._rect.y + height - offset - self.dev_ui_offset + + # Pre-calculate the face lines positions + positioned_keypoints = self.face_keypoints_transformed + np.array([self.position_x, self.position_y]) + for i in range(len(positioned_keypoints)): + self.face_lines[i].x = positioned_keypoints[i][0] + self.face_lines[i].y = positioned_keypoints[i][1] + + # Calculate arc dimensions based on head rotation + delta_x = -self.driver_pose_sins[1] * ARC_LENGTH / 2.0 # Horizontal movement + delta_y = -self.driver_pose_sins[0] * ARC_LENGTH / 2.0 # Vertical movement + + # Horizontal arc + h_width = abs(delta_x) + self.h_arc_data = self._calculate_arc_data( + delta_x, h_width, self.position_x, self.position_y - ARC_LENGTH / 2, + self.driver_pose_sins[1], self.driver_pose_diff[1], is_horizontal=True + ) + + # Vertical arc + v_height = abs(delta_y) + self.v_arc_data = self._calculate_arc_data( + delta_y, v_height, self.position_x - ARC_LENGTH / 2, self.position_y, + self.driver_pose_sins[0], self.driver_pose_diff[0], is_horizontal=False + )