From 6069692f150e0daf6881f38b8919b34d23f83c91 Mon Sep 17 00:00:00 2001 From: FrogAi <91348155+FrogAi@users.noreply.github.com> Date: Sat, 11 May 2024 12:45:23 -0700 Subject: [PATCH] Visuals - Custom Onroad UI - Compass Add a compass to the onroad UI. --- .../assets/other_images/compass_inner.png | Bin 0 -> 43168 bytes selfdrive/ui/qt/onroad.cc | 124 +++++++++++++++++- selfdrive/ui/qt/onroad.h | 26 ++++ selfdrive/ui/ui.cc | 7 + selfdrive/ui/ui.h | 2 + 5 files changed, 156 insertions(+), 3 deletions(-) create mode 100644 selfdrive/frogpilot/assets/other_images/compass_inner.png diff --git a/selfdrive/frogpilot/assets/other_images/compass_inner.png b/selfdrive/frogpilot/assets/other_images/compass_inner.png new file mode 100644 index 0000000000000000000000000000000000000000..aa2d45196b39ca547b96b3bb50d0ede28451301c GIT binary patch literal 43168 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 JtaD0e0sxv8bKC#` literal 0 HcmV?d00001 diff --git a/selfdrive/ui/qt/onroad.cc b/selfdrive/ui/qt/onroad.cc index 695ee44..332ea42 100644 --- a/selfdrive/ui/qt/onroad.cc +++ b/selfdrive/ui/qt/onroad.cc @@ -401,8 +401,8 @@ void AnnotatedCameraWidget::updateState(const UIState &s) { // hide map settings button for alerts and flip for right hand DM if (map_settings_btn->isEnabled()) { - map_settings_btn->setVisible(!hideBottomIcons); - main_layout->setAlignment(map_settings_btn, (rightHandDM ? Qt::AlignLeft : Qt::AlignRight) | Qt::AlignBottom); + map_settings_btn->setVisible(!hideBottomIcons && compass); + main_layout->setAlignment(map_settings_btn, (rightHandDM ? Qt::AlignLeft : Qt::AlignRight) | Qt::AlignTop); } // Update FrogPilot widgets @@ -832,6 +832,9 @@ void AnnotatedCameraWidget::initializeFrogPilotWidgets() { QSpacerItem *spacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum); bottom_layout->addItem(spacer); + compass_img = new Compass(this); + bottom_layout->addWidget(compass_img); + map_settings_btn_bottom = new MapSettingsButton(this); bottom_layout->addWidget(map_settings_btn_bottom); @@ -862,6 +865,8 @@ void AnnotatedCameraWidget::updateFrogPilotWidgets() { alwaysOnLateralActive = scene.always_on_lateral_active; showAlwaysOnLateralStatusBar = scene.show_aol_status_bar; + compass = scene.compass; + conditionalStatus = scene.conditional_status; showConditionalExperimentalStatusBar = scene.show_cem_status_bar; @@ -893,6 +898,13 @@ void AnnotatedCameraWidget::paintFrogPilotWidgets(QPainter &p) { drawSLCConfirmation(p); } + bool enableCompass = compass && !hideBottomIcons; + compass_img->setVisible(enableCompass); + if (enableCompass) { + compass_img->updateState(); + bottom_layout->setAlignment(compass_img, (rightHandDM ? Qt::AlignLeft : Qt::AlignRight)); + } + bool enableDistanceButton = onroadDistanceButton && !hideBottomIcons; distance_btn->setVisible(enableDistanceButton); if (enableDistanceButton) { @@ -902,11 +914,117 @@ void AnnotatedCameraWidget::paintFrogPilotWidgets(QPainter &p) { map_settings_btn_bottom->setEnabled(map_settings_btn->isEnabled()); if (map_settings_btn_bottom->isEnabled()) { - map_settings_btn_bottom->setVisible(!hideBottomIcons); + map_settings_btn_bottom->setVisible(!hideBottomIcons && !compass); bottom_layout->setAlignment(map_settings_btn_bottom, rightHandDM ? Qt::AlignLeft : Qt::AlignRight); } } +Compass::Compass(QWidget *parent) : QWidget(parent), scene(uiState()->scene) { + setFixedSize(btn_size * 1.5, btn_size * 1.5); + + compassSize = btn_size; + circleOffset = compassSize / 2; + degreeLabelOffset = circleOffset + 25; + innerCompass = compassSize / 2; + + x = (btn_size * 1.5) / 2 + 20; + y = (btn_size * 1.5) / 2; + + compassInnerImg = loadPixmap("../frogpilot/assets/other_images/compass_inner.png", QSize(compassSize / 1.75, compassSize / 1.75)); + + staticElements = QPixmap(size()); + staticElements.fill(Qt::transparent); + QPainter p(&staticElements); + + p.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); + + QPen whitePen(Qt::white, 2); + p.setPen(whitePen); + + p.setOpacity(1.0); + p.setBrush(QColor(0, 0, 0, 100)); + p.drawEllipse(x - circleOffset, y - circleOffset, circleOffset * 2, circleOffset * 2); + + p.setBrush(Qt::NoBrush); + p.drawEllipse(x - (innerCompass + 5), y - (innerCompass + 5), (innerCompass + 5) * 2, (innerCompass + 5) * 2); + p.drawEllipse(x - degreeLabelOffset, y - degreeLabelOffset, degreeLabelOffset * 2, degreeLabelOffset * 2); + + QPainterPath outerCircle, innerCircle; + outerCircle.addEllipse(x - degreeLabelOffset, y - degreeLabelOffset, degreeLabelOffset * 2, degreeLabelOffset * 2); + innerCircle.addEllipse(x - circleOffset, y - circleOffset, compassSize, compassSize); + p.fillPath(outerCircle.subtracted(innerCircle), Qt::black); +} + +void Compass::updateState() { + if (bearingDeg != scene.bearing_deg) { + update(); + bearingDeg = scene.bearing_deg; + } +} + +void Compass::paintEvent(QPaintEvent *event) { + QPainter p(this); + p.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); + + bearingDeg = fmod(bearingDeg, 360); + if (bearingDeg < 0) { + bearingDeg += 360; + } + + p.drawPixmap(0, 0, staticElements); + + p.translate(x, y); + p.rotate(bearingDeg); + p.drawPixmap(-compassInnerImg.width() / 2, -compassInnerImg.height() / 2, compassInnerImg); + p.rotate(-bearingDeg); + p.translate(-x, -y); + + QFont font = InterFont(10, QFont::Normal); + for (int i = 0; i < 360; i += 15) { + bool isBold = abs(i - bearingDeg) <= 7; + font.setWeight(isBold ? QFont::Bold : QFont::Normal); + p.setFont(font); + p.setPen(QPen(Qt::white, i % 90 == 0 ? 2 : 1)); + + p.save(); + p.translate(x, y); + p.rotate(i); + p.drawLine(0, -(compassSize / 2 - (i % 90 == 0 ? 12 : 8)), 0, -(compassSize / 2)); + p.translate(0, -(compassSize / 2 + 12)); + p.rotate(-i); + p.drawText(QRect(-20, -10, 40, 20), Qt::AlignCenter, QString::number(i)); + p.restore(); + } + + p.setFont(InterFont(20, QFont::Bold)); + std::map, int, QColor>> directionInfo = { + {"N", {{292.5, 67.5}, Qt::AlignTop | Qt::AlignHCenter, Qt::white}}, + {"E", {{22.5, 157.5}, Qt::AlignRight | Qt::AlignVCenter, Qt::white}}, + {"S", {{112.5, 247.5}, Qt::AlignBottom | Qt::AlignHCenter, Qt::white}}, + {"W", {{202.5, 337.5}, Qt::AlignLeft | Qt::AlignVCenter, Qt::white}} + }; + int directionOffset = 20; + + for (auto &item : directionInfo) { + QString direction = item.first; + auto &[range, alignmentFlag, color] = item.second; + auto &[minRange, maxRange] = range; + + QRect textRect(x - innerCompass + directionOffset, y - innerCompass + directionOffset, innerCompass * 2 - 2 * directionOffset, innerCompass * 2 - 2 * directionOffset); + + bool isInRange = false; + if (minRange > maxRange) { + isInRange = bearingDeg >= minRange || bearingDeg <= maxRange; + } else { + isInRange = bearingDeg >= minRange && bearingDeg <= maxRange; + } + + p.setOpacity(isInRange ? 1.0 : 0.2); + p.setPen(QPen(color)); + p.drawText(textRect, alignmentFlag, direction); + } +} + DistanceButton::DistanceButton(QWidget *parent) : QPushButton(parent), scene(uiState()->scene) { setFixedSize(btn_size * 1.5, btn_size * 1.5); diff --git a/selfdrive/ui/qt/onroad.h b/selfdrive/ui/qt/onroad.h index d54538d..425dd4a 100644 --- a/selfdrive/ui/qt/onroad.h +++ b/selfdrive/ui/qt/onroad.h @@ -34,6 +34,30 @@ private: UIScene &scene; }; +class Compass : public QWidget { +public: + explicit Compass(QWidget *parent = nullptr); + + void updateState(); + +protected: + void paintEvent(QPaintEvent *event) override; + +private: + UIScene &scene; + + int bearingDeg; + int circleOffset; + int compassSize; + int degreeLabelOffset; + int innerCompass; + int x; + int y; + + QPixmap compassInnerImg; + QPixmap staticElements; +}; + class DistanceButton : public QPushButton { public: explicit DistanceButton(QWidget *parent = nullptr); @@ -143,11 +167,13 @@ private: Params paramsMemory{"/dev/shm/params"}; UIScene &scene; + Compass *compass_img; DistanceButton *distance_btn; QHBoxLayout *bottom_layout; bool alwaysOnLateralActive; + bool compass; bool experimentalMode; bool mapOpen; bool onroadDistanceButton; diff --git a/selfdrive/ui/ui.cc b/selfdrive/ui/ui.cc index 06d7d33..1a6b40d 100644 --- a/selfdrive/ui/ui.cc +++ b/selfdrive/ui/ui.cc @@ -248,6 +248,10 @@ static void update_state(UIState *s) { } if (sm.updated("liveLocationKalman")) { auto liveLocationKalman = sm["liveLocationKalman"].getLiveLocationKalman(); + auto orientation = liveLocationKalman.getCalibratedOrientationNED(); + if (orientation.getValid()) { + scene.bearing_deg = RAD2DEG(orientation.getValue()[2]); + } } if (sm.updated("liveTorqueParameters")) { auto torque_params = sm["liveTorqueParameters"].getLiveTorqueParameters(); @@ -284,6 +288,9 @@ void ui_update_frogpilot_params(UIState *s) { scene.conditional_speed_lead = scene.conditional_experimental ? params.getInt("CESpeedLead") : 0; scene.show_cem_status_bar = scene.conditional_experimental && !params.getBool("HideCEMStatusBar"); + bool custom_onroad_ui = params.getBool("CustomUI"); + scene.compass = custom_onroad_ui && params.getBool("Compass"); + scene.disable_smoothing_mtsc = params.getBool("MTSCEnabled") && params.getBool("DisableMTSCSmoothing"); scene.disable_smoothing_vtsc = params.getBool("VisionTurnControl") && params.getBool("DisableVTSCSmoothing"); diff --git a/selfdrive/ui/ui.h b/selfdrive/ui/ui.h index 81776ca..bd97413 100644 --- a/selfdrive/ui/ui.h +++ b/selfdrive/ui/ui.h @@ -187,6 +187,7 @@ typedef struct UIScene { // FrogPilot variables bool always_on_lateral_active; + bool compass; bool conditional_experimental; bool disable_smoothing_mtsc; bool disable_smoothing_vtsc; @@ -222,6 +223,7 @@ typedef struct UIScene { float unconfirmed_speed_limit; int alert_size; + int bearing_deg; int conditional_speed; int conditional_speed_lead; int conditional_status;