Files
sunnypilot-tesla/selfdrive/ui/sunnypilot/onroad/turn_signal.py
Jason Wen d5b25e14fd Merge branch 'upstream/openpilot/master' into sync-20260317
# Conflicts:
#	.github/workflows/auto_pr_review.yaml
#	.gitignore
#	opendbc_repo
#	panda
#	selfdrive/ui/mici/layouts/home.py
#	selfdrive/ui/mici/layouts/onboarding.py
#	selfdrive/ui/mici/layouts/settings/device.py
#	selfdrive/ui/tests/diff/replay.py
#	selfdrive/ui/translations/app_fr.po
#	system/ui/mici_setup.py
Sync: `commaai/opendbc:master` → `sunnypilot/opendbc:master`
Sync: `commaai/panda:master` → `sunnypilot/panda:master`
2026-03-17 23:02:10 -04:00

120 lines
3.9 KiB
Python

"""
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 pyray as rl
import time
from dataclasses import dataclass
from openpilot.selfdrive.ui.ui_state import ui_state
from openpilot.selfdrive.ui.mici.onroad.alert_renderer import IconSide, TURN_SIGNAL_BLINK_PERIOD
from openpilot.system.ui.lib.application import gui_app
from openpilot.system.ui.widgets import Widget
from openpilot.common.filter_simple import FirstOrderFilter
@dataclass(frozen=True)
class TurnSignalConfig:
left_x: int = 80
left_y: int = 190
right_x: int = 80
right_y: int = 190
size: int = 150
class TurnSignalWidget(Widget):
def __init__(self, direction: IconSide):
super().__init__()
self._direction = direction
self._active = False
self._type = 'signal'
self._turn_signal_timer = 0.0
self._turn_signal_alpha_filter = FirstOrderFilter(0.0, 0.3, 1 / gui_app.target_fps)
self._signal_texture = gui_app.texture('icons_mici/onroad/turn_signal_left.png', 120, 109, flip_x=(direction == IconSide.right))
self._blind_spot_texture = gui_app.texture('icons_mici/onroad/blind_spot_left.png', 120, 109, flip_x=(direction == IconSide.right))
self._texture = self._signal_texture
def _render(self, _):
if not self._active:
return
if self._type == 'signal':
if time.monotonic() - self._turn_signal_timer > TURN_SIGNAL_BLINK_PERIOD:
self._turn_signal_timer = time.monotonic()
self._turn_signal_alpha_filter.x = 255 * 2
else:
self._turn_signal_alpha_filter.update(255 * 0.2)
icon_alpha = int(min(self._turn_signal_alpha_filter.x, 255))
else:
icon_alpha = 255
self._texture = self._blind_spot_texture if self._type == 'blind_spot' else self._signal_texture
if self._texture:
pos_x = self._rect.x + (self._rect.width - self._texture.width) / 2
pos_y = self._rect.y + (self._rect.height - self._texture.height) / 2
color = rl.Color(255, 255, 255, icon_alpha)
rl.draw_texture_ex(self._texture, rl.Vector2(pos_x, pos_y), 0.0, 1.0, color)
def activate(self, _type: str = 'signal'):
if not self._active or self._type != _type:
self._turn_signal_timer = 0.0
self._active = True
self._type = _type
def deactivate(self):
self._active = False
self._turn_signal_timer = 0.0
class TurnSignalController:
def __init__(self):
self._config = TurnSignalConfig()
self._left_signal = TurnSignalWidget(direction=IconSide.left)
self._right_signal = TurnSignalWidget(direction=IconSide.right)
@staticmethod
def _update_signal(signal, blindspot, blinker):
if ui_state.blindspot and blindspot:
signal.activate('blind_spot')
elif ui_state.turn_signals and blinker:
signal.activate('signal')
else:
signal.deactivate()
def update(self):
CS = ui_state.sm['carState']
self._update_signal(self._left_signal, CS.leftBlindspot, CS.leftBlinker)
self._update_signal(self._right_signal, CS.rightBlindspot, CS.rightBlinker)
def render(self, rect: rl.Rectangle):
if not ui_state.turn_signals and not ui_state.blindspot:
return
x = rect.x + rect.width / 2
left_x = x - self._config.left_x - self._config.size
left_y = rect.y + self._config.left_y
right_x = x + self._config.right_x
right_y = rect.y + self._config.right_y
if self._left_signal._active:
self._left_signal.render(rl.Rectangle(left_x, left_y, self._config.size, self._config.size))
if self._right_signal._active:
self._right_signal.render(rl.Rectangle(right_x, right_y, self._config.size, self._config.size))
@property
def config(self) -> TurnSignalConfig:
return self._config
@config.setter
def config(self, new_config: TurnSignalConfig):
self._config = new_config