diff --git a/common/params.cc b/common/params.cc index 37de56a..41b7a23 100644 --- a/common/params.cc +++ b/common/params.cc @@ -304,6 +304,7 @@ std::unordered_map keys = { {"RoadEdgesWidth", PERSISTENT}, {"RoadName", PERSISTENT}, {"RoadNameUI", PERSISTENT}, + {"RotatingWheel", PERSISTENT}, {"SchedulePending", PERSISTENT}, {"ScreenBrightness", PERSISTENT}, {"SearchInput", PERSISTENT}, diff --git a/selfdrive/frogpilot/assets/toggle_icons/icon_rotate.png b/selfdrive/frogpilot/assets/toggle_icons/icon_rotate.png new file mode 100644 index 0000000..1503308 Binary files /dev/null and b/selfdrive/frogpilot/assets/toggle_icons/icon_rotate.png differ diff --git a/selfdrive/frogpilot/ui/visual_settings.cc b/selfdrive/frogpilot/ui/visual_settings.cc index 240a964..ba4fb27 100644 --- a/selfdrive/frogpilot/ui/visual_settings.cc +++ b/selfdrive/frogpilot/ui/visual_settings.cc @@ -106,8 +106,8 @@ FrogPilotVisualsPanel::FrogPilotVisualsPanel(SettingsWindow *parent) : FrogPilot toggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 101, brightnessLabels, this, false); } else if (param == "WheelIcon") { - std::vector wheelToggles{}; - std::vector wheelToggleNames{}; + std::vector wheelToggles{"RotatingWheel"}; + std::vector wheelToggleNames{tr("Rotating")}; std::map steeringWheelLabels = {{0, "Stock"}, {1, "Lexus"}, {2, "Toyota"}, {3, "Frog"}, {4, "Rocket"}, {5, "Hyundai"}, {6, "Stalin"}}; toggle = new FrogPilotParamValueToggleControl(param, title, desc, icon, 0, 6, steeringWheelLabels, this, true, "", 1, wheelToggles, wheelToggleNames); diff --git a/selfdrive/ui/qt/onroad.cc b/selfdrive/ui/qt/onroad.cc index b4c1fe5..ce5aeda 100644 --- a/selfdrive/ui/qt/onroad.cc +++ b/selfdrive/ui/qt/onroad.cc @@ -29,6 +29,21 @@ static void drawIcon(QPainter &p, const QPoint ¢er, const QPixmap &img, cons p.setOpacity(1.0); } +static void drawIconRotate(QPainter &p, const QPoint ¢er, const QPixmap &img, const QBrush &bg, float opacity, const int angle) { + p.setRenderHint(QPainter::Antialiasing); + p.setOpacity(1.0); // bg dictates opacity of ellipse + p.setPen(Qt::NoPen); + p.setBrush(bg); + p.drawEllipse(center, btn_size / 2, btn_size / 2); + p.save(); + p.translate(center); + p.rotate(-angle); + p.setOpacity(opacity); + p.drawPixmap(-QPoint(img.width() / 2, img.height() / 2), img); + p.setOpacity(1.0); + p.restore(); +} + OnroadWindow::OnroadWindow(QWidget *parent) : QWidget(parent), scene(uiState()->scene) { QVBoxLayout *main_layout = new QVBoxLayout(this); main_layout->setMargin(UI_BORDER_SIZE); @@ -370,9 +385,16 @@ void ExperimentalButton::updateState(const UIState &s, bool leadInfo) { } // FrogPilot variables + rotatingWheel = scene.rotating_wheel; wheelIcon = scene.wheel_icon; y_offset = leadInfo ? 10 : 0; + + // Update the icon so the steering wheel rotates in real time + if (rotatingWheel && steeringAngleDeg != scene.steering_angle_deg) { + steeringAngleDeg = scene.steering_angle_deg; + update(); + } } void ExperimentalButton::paintEvent(QPaintEvent *event) { @@ -389,7 +411,11 @@ void ExperimentalButton::paintEvent(QPaintEvent *event) { QColor(0, 0, 0, 166)); if (!scene.show_driver_camera) { - drawIcon(p, QPoint(btn_size / 2, btn_size / 2 + y_offset), img, background_color, (isDown() || (!engageable && !scene.always_on_lateral_active)) ? 0.6 : 1.0); + if (rotatingWheel) { + drawIconRotate(p, QPoint(btn_size / 2, btn_size / 2 + y_offset), img, background_color, (isDown() || (!engageable && !scene.always_on_lateral_active)) ? 0.6 : 1.0, steeringAngleDeg); + } else { + drawIcon(p, QPoint(btn_size / 2, btn_size / 2 + y_offset), img, background_color, (isDown() || (!engageable && !scene.always_on_lateral_active)) ? 0.6 : 1.0); + } } } diff --git a/selfdrive/ui/qt/onroad.h b/selfdrive/ui/qt/onroad.h index f285914..b456e44 100644 --- a/selfdrive/ui/qt/onroad.h +++ b/selfdrive/ui/qt/onroad.h @@ -83,6 +83,8 @@ private: std::map wheelImages; + bool rotatingWheel; + int steeringAngleDeg; int wheelIcon; int y_offset; }; diff --git a/selfdrive/ui/ui.cc b/selfdrive/ui/ui.cc index e63c9ae..37adbbb 100644 --- a/selfdrive/ui/ui.cc +++ b/selfdrive/ui/ui.cc @@ -222,6 +222,9 @@ static void update_state(UIState *s) { scene.turn_signal_left = carState.getLeftBlinker(); scene.turn_signal_right = carState.getRightBlinker(); } + if (scene.rotating_wheel) { + scene.steering_angle_deg = carState.getSteeringAngleDeg(); + } if (scene.driver_camera) { scene.show_driver_camera = carState.getGearShifter() == cereal::CarState::GearShifter::REVERSE; } @@ -316,6 +319,7 @@ void ui_update_params(UIState *s) { scene.driver_camera = params.getBool("DriverCamera"); scene.experimental_mode_via_press = params.getBool("ExperimentalModeViaPress"); scene.mute_dm = params.getBool("FireTheBabysitter") && params.getBool("MuteDM"); + scene.rotating_wheel = params.getBool("RotatingWheel"); scene.screen_brightness = params.getInt("ScreenBrightness"); scene.wheel_icon = params.getInt("WheelIcon"); } diff --git a/selfdrive/ui/ui.h b/selfdrive/ui/ui.h index 7a3b3bc..2c91907 100644 --- a/selfdrive/ui/ui.h +++ b/selfdrive/ui/ui.h @@ -190,6 +190,7 @@ typedef struct UIScene { bool model_ui; bool mute_dm; bool road_name_ui; + bool rotating_wheel; bool show_driver_camera; bool show_fps; bool tethering_enabled; @@ -215,6 +216,7 @@ typedef struct UIScene { int obstacle_distance; int obstacle_distance_stock; int screen_brightness; + int steering_angle_deg; int stopped_equivalence; int wheel_icon; QPolygonF track_adjacent_vertices[6];