mirror of
https://github.com/dragonpilot/dragonpilot.git
synced 2026-06-27 08:52:05 +08:00
UI: move onroad hud to Qt (#23040)
* onroad hud * improve boudingrect, same as nanvg * QRect * Update selfdrive/ui/paint.cc * stacked with nvgWindow * cleanup ui.h * rename to road_view_layout * no lambda Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com>
This commit is contained in:
+5
-130
@@ -16,16 +16,7 @@
|
||||
#include <nanovg_gl.h>
|
||||
#include <nanovg_gl_utils.h>
|
||||
|
||||
#include "selfdrive/common/util.h"
|
||||
#include "selfdrive/hardware/hw.h"
|
||||
#include "selfdrive/ui/ui.h"
|
||||
|
||||
static void ui_draw_text(const UIState *s, float x, float y, const char *string, float size, NVGcolor color, const char *font_name) {
|
||||
nvgFontFace(s->vg, font_name);
|
||||
nvgFontSize(s->vg, size);
|
||||
nvgFillColor(s->vg, color);
|
||||
nvgText(s->vg, x, y, string, NULL);
|
||||
}
|
||||
|
||||
static void draw_chevron(UIState *s, float x, float y, float sz, NVGcolor fillColor, NVGcolor glowColor) {
|
||||
// glow
|
||||
@@ -49,21 +40,6 @@ static void draw_chevron(UIState *s, float x, float y, float sz, NVGcolor fillCo
|
||||
nvgFill(s->vg);
|
||||
}
|
||||
|
||||
static void ui_draw_circle_image(const UIState *s, int center_x, int center_y, int radius, const char *image, NVGcolor color, float img_alpha) {
|
||||
nvgBeginPath(s->vg);
|
||||
nvgCircle(s->vg, center_x, center_y, radius);
|
||||
nvgFillColor(s->vg, color);
|
||||
nvgFill(s->vg);
|
||||
const int img_size = radius * 1.5;
|
||||
ui_draw_image(s, {center_x - (img_size / 2), center_y - (img_size / 2), img_size, img_size}, image, img_alpha);
|
||||
}
|
||||
|
||||
static void ui_draw_circle_image(const UIState *s, int center_x, int center_y, int radius, const char *image, bool active) {
|
||||
float bg_alpha = active ? 0.3f : 0.1f;
|
||||
float img_alpha = active ? 1.0f : 0.15f;
|
||||
ui_draw_circle_image(s, center_x, center_y, radius, image, nvgRGBA(0, 0, 0, (255 * bg_alpha)), img_alpha);
|
||||
}
|
||||
|
||||
static void draw_lead(UIState *s, const cereal::RadarState::LeadData::Reader &lead_data, const vertex_data &vd) {
|
||||
// Draw lead car indicator
|
||||
auto [x, y] = vd;
|
||||
@@ -151,60 +127,13 @@ static void ui_draw_world(UIState *s) {
|
||||
nvgResetScissor(s->vg);
|
||||
}
|
||||
|
||||
static void ui_draw_vision_maxspeed(UIState *s) {
|
||||
const int SET_SPEED_NA = 255;
|
||||
float maxspeed = (*s->sm)["controlsState"].getControlsState().getVCruise();
|
||||
const bool is_cruise_set = maxspeed != 0 && maxspeed != SET_SPEED_NA;
|
||||
if (is_cruise_set && !s->scene.is_metric) { maxspeed *= KM_TO_MILE; }
|
||||
|
||||
const Rect rect = {bdr_s * 2, int(bdr_s * 1.5), 184, 202};
|
||||
ui_fill_rect(s->vg, rect, COLOR_BLACK_ALPHA(100), 30.);
|
||||
ui_draw_rect(s->vg, rect, COLOR_WHITE_ALPHA(100), 10, 20.);
|
||||
|
||||
nvgTextAlign(s->vg, NVG_ALIGN_CENTER | NVG_ALIGN_BASELINE);
|
||||
ui_draw_text(s, rect.centerX(), 118, "MAX", 26 * 2.5, COLOR_WHITE_ALPHA(is_cruise_set ? 200 : 100), "sans-regular");
|
||||
if (is_cruise_set) {
|
||||
const std::string maxspeed_str = std::to_string((int)std::nearbyint(maxspeed));
|
||||
ui_draw_text(s, rect.centerX(), 212, maxspeed_str.c_str(), 48 * 2.5, COLOR_WHITE, "sans-bold");
|
||||
} else {
|
||||
ui_draw_text(s, rect.centerX(), 212, "N/A", 42 * 2.5, COLOR_WHITE_ALPHA(100), "sans-semibold");
|
||||
}
|
||||
}
|
||||
|
||||
static void ui_draw_vision_speed(UIState *s) {
|
||||
const float speed = std::max(0.0, (*s->sm)["carState"].getCarState().getVEgo() * (s->scene.is_metric ? MS_TO_KPH : MS_TO_MPH));
|
||||
const std::string speed_str = std::to_string((int)std::nearbyint(speed));
|
||||
nvgTextAlign(s->vg, NVG_ALIGN_CENTER | NVG_ALIGN_BASELINE);
|
||||
ui_draw_text(s, s->fb_w/2, 210, speed_str.c_str(), 96 * 2.5, COLOR_WHITE, "sans-bold");
|
||||
ui_draw_text(s, s->fb_w/2, 290, s->scene.is_metric ? "km/h" : "mph", 36 * 2.5, COLOR_WHITE_ALPHA(200), "sans-regular");
|
||||
}
|
||||
|
||||
static void ui_draw_vision_event(UIState *s) {
|
||||
if (s->scene.engageable) {
|
||||
// draw steering wheel
|
||||
const int radius = 96;
|
||||
const int center_x = s->fb_w - radius - bdr_s * 2;
|
||||
const int center_y = radius + (bdr_s * 1.5);
|
||||
const QColor &color = bg_colors[s->status];
|
||||
NVGcolor nvg_color = nvgRGBA(color.red(), color.green(), color.blue(), color.alpha());
|
||||
ui_draw_circle_image(s, center_x, center_y, radius, "wheel", nvg_color, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
static void ui_draw_vision_face(UIState *s) {
|
||||
const int radius = 96;
|
||||
const int center_x = radius + (bdr_s * 2);
|
||||
const int center_y = s->fb_h - footer_h / 2;
|
||||
ui_draw_circle_image(s, center_x, center_y, radius, "driver_face", s->scene.dm_active);
|
||||
}
|
||||
|
||||
static void ui_draw_vision_header(UIState *s) {
|
||||
NVGpaint gradient = nvgLinearGradient(s->vg, 0, header_h - (header_h / 2.5), 0, header_h,
|
||||
nvgRGBAf(0, 0, 0, 0.45), nvgRGBAf(0, 0, 0, 0));
|
||||
ui_fill_rect(s->vg, {0, 0, s->fb_w , header_h}, gradient);
|
||||
ui_draw_vision_maxspeed(s);
|
||||
ui_draw_vision_speed(s);
|
||||
ui_draw_vision_event(s);
|
||||
nvgBeginPath(s->vg);
|
||||
nvgRect(s->vg, 0, 0, s->fb_w, header_h);
|
||||
nvgFillPaint(s->vg, gradient);
|
||||
nvgFill(s->vg);
|
||||
}
|
||||
|
||||
static void ui_draw_vision(UIState *s) {
|
||||
@@ -213,11 +142,8 @@ static void ui_draw_vision(UIState *s) {
|
||||
if (scene->world_objects_visible) {
|
||||
ui_draw_world(s);
|
||||
}
|
||||
// Set Speed, Current Speed, Status/Events
|
||||
// TODO: move this to Qt
|
||||
ui_draw_vision_header(s);
|
||||
if ((*s->sm)["controlsState"].getControlsState().getAlertSize() == cereal::ControlsState::AlertSize::NONE) {
|
||||
ui_draw_vision_face(s);
|
||||
}
|
||||
}
|
||||
|
||||
void ui_draw(UIState *s, int w, int h) {
|
||||
@@ -233,61 +159,10 @@ void ui_draw(UIState *s, int w, int h) {
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
void ui_draw_image(const UIState *s, const Rect &r, const char *name, float alpha) {
|
||||
nvgBeginPath(s->vg);
|
||||
NVGpaint imgPaint = nvgImagePattern(s->vg, r.x, r.y, r.w, r.h, 0, s->images.at(name), alpha);
|
||||
nvgRect(s->vg, r.x, r.y, r.w, r.h);
|
||||
nvgFillPaint(s->vg, imgPaint);
|
||||
nvgFill(s->vg);
|
||||
}
|
||||
|
||||
void ui_draw_rect(NVGcontext *vg, const Rect &r, NVGcolor color, int width, float radius) {
|
||||
nvgBeginPath(vg);
|
||||
radius > 0 ? nvgRoundedRect(vg, r.x, r.y, r.w, r.h, radius) : nvgRect(vg, r.x, r.y, r.w, r.h);
|
||||
nvgStrokeColor(vg, color);
|
||||
nvgStrokeWidth(vg, width);
|
||||
nvgStroke(vg);
|
||||
}
|
||||
|
||||
static inline void fill_rect(NVGcontext *vg, const Rect &r, const NVGcolor *color, const NVGpaint *paint, float radius) {
|
||||
nvgBeginPath(vg);
|
||||
radius > 0 ? nvgRoundedRect(vg, r.x, r.y, r.w, r.h, radius) : nvgRect(vg, r.x, r.y, r.w, r.h);
|
||||
if (color) nvgFillColor(vg, *color);
|
||||
if (paint) nvgFillPaint(vg, *paint);
|
||||
nvgFill(vg);
|
||||
}
|
||||
void ui_fill_rect(NVGcontext *vg, const Rect &r, const NVGcolor &color, float radius) {
|
||||
fill_rect(vg, r, &color, nullptr, radius);
|
||||
}
|
||||
void ui_fill_rect(NVGcontext *vg, const Rect &r, const NVGpaint &paint, float radius) {
|
||||
fill_rect(vg, r, nullptr, &paint, radius);
|
||||
}
|
||||
|
||||
void ui_nvg_init(UIState *s) {
|
||||
// on EON, we enable MSAA
|
||||
s->vg = Hardware::EON() ? nvgCreate(0) : nvgCreate(NVG_ANTIALIAS | NVG_STENCIL_STROKES | NVG_DEBUG);
|
||||
assert(s->vg);
|
||||
|
||||
// init fonts
|
||||
std::pair<const char *, const char *> fonts[] = {
|
||||
{"sans-regular", "../assets/fonts/opensans_regular.ttf"},
|
||||
{"sans-semibold", "../assets/fonts/opensans_semibold.ttf"},
|
||||
{"sans-bold", "../assets/fonts/opensans_bold.ttf"},
|
||||
};
|
||||
for (auto [name, file] : fonts) {
|
||||
int font_id = nvgCreateFont(s->vg, name, file);
|
||||
assert(font_id >= 0);
|
||||
}
|
||||
|
||||
// init images
|
||||
std::vector<std::pair<const char *, const char *>> images = {
|
||||
{"wheel", "../assets/img_chffr_wheel.png"},
|
||||
{"driver_face", "../assets/img_driver_face.png"},
|
||||
};
|
||||
for (auto [name, file] : images) {
|
||||
s->images[name] = nvgCreateImage(s->vg, file, 1);
|
||||
assert(s->images[name] != 0);
|
||||
}
|
||||
}
|
||||
|
||||
void ui_resize(UIState *s, int width, int height) {
|
||||
|
||||
@@ -3,10 +3,6 @@
|
||||
#include "selfdrive/ui/ui.h"
|
||||
|
||||
void ui_draw(UIState *s, int w, int h);
|
||||
void ui_draw_image(const UIState *s, const Rect &r, const char *name, float alpha);
|
||||
void ui_draw_rect(NVGcontext *vg, const Rect &r, NVGcolor color, int width, float radius = 0);
|
||||
void ui_fill_rect(NVGcontext *vg, const Rect &r, const NVGpaint &paint, float radius = 0);
|
||||
void ui_fill_rect(NVGcontext *vg, const Rect &r, const NVGcolor &color, float radius = 0);
|
||||
void ui_nvg_init(UIState *s);
|
||||
void ui_resize(UIState *s, int width, int height);
|
||||
void ui_update_params(UIState *s);
|
||||
|
||||
+100
-1
@@ -17,13 +17,18 @@ OnroadWindow::OnroadWindow(QWidget *parent) : QWidget(parent) {
|
||||
stacked_layout->setStackingMode(QStackedLayout::StackAll);
|
||||
main_layout->addLayout(stacked_layout);
|
||||
|
||||
QStackedLayout *road_view_layout = new QStackedLayout;
|
||||
road_view_layout->setStackingMode(QStackedLayout::StackAll);
|
||||
nvg = new NvgWindow(VISION_STREAM_RGB_BACK, this);
|
||||
road_view_layout->addWidget(nvg);
|
||||
hud = new OnroadHud(this);
|
||||
road_view_layout->addWidget(hud);
|
||||
|
||||
QWidget * split_wrapper = new QWidget;
|
||||
split = new QHBoxLayout(split_wrapper);
|
||||
split->setContentsMargins(0, 0, 0, 0);
|
||||
split->setSpacing(0);
|
||||
split->addWidget(nvg);
|
||||
split->addLayout(road_view_layout);
|
||||
|
||||
stacked_layout->addWidget(split_wrapper);
|
||||
|
||||
@@ -48,6 +53,9 @@ void OnroadWindow::updateState(const UIState &s) {
|
||||
}
|
||||
alerts->updateAlert(alert, bgColor);
|
||||
}
|
||||
|
||||
hud->updateState(s);
|
||||
|
||||
if (bg != bgColor) {
|
||||
// repaint border
|
||||
bg = bgColor;
|
||||
@@ -91,6 +99,7 @@ void OnroadWindow::paintEvent(QPaintEvent *event) {
|
||||
|
||||
// ***** onroad widgets *****
|
||||
|
||||
// OnroadAlerts
|
||||
void OnroadAlerts::updateAlert(const Alert &a, const QColor &color) {
|
||||
if (!alert.equal(a) || color != bg) {
|
||||
alert = a;
|
||||
@@ -150,6 +159,96 @@ void OnroadAlerts::paintEvent(QPaintEvent *event) {
|
||||
}
|
||||
}
|
||||
|
||||
// OnroadHud
|
||||
OnroadHud::OnroadHud(QWidget *parent) : QWidget(parent) {
|
||||
engage_img = QPixmap("../assets/img_chffr_wheel.png").scaled(img_size, img_size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
dm_img = QPixmap("../assets/img_driver_face.png").scaled(img_size, img_size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
|
||||
connect(this, &OnroadHud::valueChanged, [=] { update(); });
|
||||
}
|
||||
|
||||
void OnroadHud::updateState(const UIState &s) {
|
||||
const int SET_SPEED_NA = 255;
|
||||
const SubMaster &sm = *(s.sm);
|
||||
const auto cs = sm["controlsState"].getControlsState();
|
||||
|
||||
float maxspeed = cs.getVCruise();
|
||||
bool cruise_set = maxspeed > 0 && (int)maxspeed != SET_SPEED_NA;
|
||||
if (cruise_set && !s.scene.is_metric) {
|
||||
maxspeed *= KM_TO_MILE;
|
||||
}
|
||||
QString maxspeed_str = cruise_set ? QString::number(std::nearbyint(maxspeed)) : "N/A";
|
||||
float cur_speed = std::max(0.0, sm["carState"].getCarState().getVEgo() * (s.scene.is_metric ? MS_TO_KPH : MS_TO_MPH));
|
||||
|
||||
setProperty("is_cruise_set", cruise_set);
|
||||
setProperty("speed", QString::number(std::nearbyint(cur_speed)));
|
||||
setProperty("maxSpeed", maxspeed_str);
|
||||
setProperty("speedUnit", s.scene.is_metric ? "km/h" : "mph");
|
||||
setProperty("dmActive", sm["driverMonitoringState"].getDriverMonitoringState().getIsActiveMode());
|
||||
setProperty("hideDM", cs.getAlertSize() != cereal::ControlsState::AlertSize::NONE);
|
||||
setProperty("engageable", cs.getEngageable());
|
||||
setProperty("status", s.status);
|
||||
}
|
||||
|
||||
void OnroadHud::paintEvent(QPaintEvent *event) {
|
||||
QPainter p(this);
|
||||
p.setRenderHint(QPainter::Antialiasing);
|
||||
|
||||
// max speed
|
||||
QRect rc(bdr_s * 2, bdr_s * 1.5, 184, 202);
|
||||
p.setPen(QPen(QColor(0xff, 0xff, 0xff, 100), 10));
|
||||
p.setBrush(QColor(0, 0, 0, 100));
|
||||
p.drawRoundedRect(rc, 20, 20);
|
||||
p.setPen(Qt::NoPen);
|
||||
|
||||
configFont(p, "Open Sans", 48, "Regular");
|
||||
drawText(p, rc.center().x(), 118, "MAX", is_cruise_set ? 200 : 100);
|
||||
if (is_cruise_set) {
|
||||
configFont(p, "Open Sans", 88, is_cruise_set ? "Bold" : "SemiBold");
|
||||
drawText(p, rc.center().x(), 212, maxSpeed, 255);
|
||||
} else {
|
||||
configFont(p, "Open Sans", 80, "SemiBold");
|
||||
drawText(p, rc.center().x(), 212, maxSpeed, 100);
|
||||
}
|
||||
|
||||
// current speed
|
||||
configFont(p, "Open Sans", 176, "Bold");
|
||||
drawText(p, rect().center().x(), 210, speed);
|
||||
configFont(p, "Open Sans", 66, "Regular");
|
||||
drawText(p, rect().center().x(), 290, speedUnit, 200);
|
||||
|
||||
// engage-ability icon
|
||||
if (engageable) {
|
||||
drawIcon(p, rect().right() - radius / 2 - bdr_s * 2, radius / 2 + int(bdr_s * 1.5),
|
||||
engage_img, bg_colors[status], 1.0);
|
||||
}
|
||||
|
||||
// dm icon
|
||||
if (!hideDM) {
|
||||
drawIcon(p, radius / 2 + (bdr_s * 2), rect().bottom() - footer_h / 2,
|
||||
dm_img, QColor(0, 0, 0, 70), dmActive ? 1.0 : 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
void OnroadHud::drawText(QPainter &p, int x, int y, const QString &text, int alpha) {
|
||||
QFontMetrics fm(p.font());
|
||||
QRect init_rect = fm.boundingRect(text);
|
||||
QRect real_rect = fm.boundingRect(init_rect, 0, text);
|
||||
real_rect.moveCenter({x, y - real_rect.height() / 2});
|
||||
|
||||
p.setPen(QColor(0xff, 0xff, 0xff, alpha));
|
||||
p.drawText(real_rect.x(), real_rect.bottom(), text);
|
||||
}
|
||||
|
||||
void OnroadHud::drawIcon(QPainter &p, int x, int y, QPixmap &img, QBrush bg, float opacity) {
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(bg);
|
||||
p.drawEllipse(x - radius / 2, y - radius / 2, radius, radius);
|
||||
p.setOpacity(opacity);
|
||||
p.drawPixmap(x - img_size / 2, y - img_size / 2, img);
|
||||
}
|
||||
|
||||
// NvgWindow
|
||||
void NvgWindow::initializeGL() {
|
||||
CameraViewWidget::initializeGL();
|
||||
qInfo() << "OpenGL version:" << QString((const char*)glGetString(GL_VERSION));
|
||||
|
||||
@@ -9,6 +9,43 @@
|
||||
|
||||
// ***** onroad widgets *****
|
||||
|
||||
class OnroadHud : public QWidget {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString speed MEMBER speed NOTIFY valueChanged);
|
||||
Q_PROPERTY(QString speedUnit MEMBER speedUnit NOTIFY valueChanged);
|
||||
Q_PROPERTY(QString maxSpeed MEMBER maxSpeed NOTIFY valueChanged);
|
||||
Q_PROPERTY(bool is_cruise_set MEMBER is_cruise_set NOTIFY valueChanged);
|
||||
Q_PROPERTY(bool engageable MEMBER engageable NOTIFY valueChanged);
|
||||
Q_PROPERTY(bool dmActive MEMBER dmActive NOTIFY valueChanged);
|
||||
Q_PROPERTY(bool hideDM MEMBER hideDM NOTIFY valueChanged);
|
||||
Q_PROPERTY(int status MEMBER status NOTIFY valueChanged);
|
||||
|
||||
public:
|
||||
explicit OnroadHud(QWidget *parent);
|
||||
void updateState(const UIState &s);
|
||||
|
||||
private:
|
||||
void drawIcon(QPainter &p, int x, int y, QPixmap &img, QBrush bg, float opacity);
|
||||
void drawText(QPainter &p, int x, int y, const QString &text, int alpha = 255);
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
|
||||
QPixmap engage_img;
|
||||
QPixmap dm_img;
|
||||
const int radius = 192;
|
||||
const int img_size = (radius / 2) * 1.5;
|
||||
QString speed;
|
||||
QString speedUnit;
|
||||
QString maxSpeed;
|
||||
bool is_cruise_set = false;
|
||||
bool engageable = false;
|
||||
bool dmActive = false;
|
||||
bool hideDM = false;
|
||||
int status = STATUS_DISENGAGED;
|
||||
|
||||
signals:
|
||||
void valueChanged();
|
||||
};
|
||||
|
||||
class OnroadAlerts : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
@@ -49,6 +86,7 @@ public:
|
||||
private:
|
||||
void paintEvent(QPaintEvent *event);
|
||||
void mousePressEvent(QMouseEvent* e) override;
|
||||
OnroadHud *hud;
|
||||
OnroadAlerts *alerts;
|
||||
NvgWindow *nvg;
|
||||
QColor bg = bg_colors[STATUS_DISENGAGED];
|
||||
|
||||
@@ -19,8 +19,6 @@
|
||||
#include "selfdrive/common/params.h"
|
||||
#include "selfdrive/common/util.h"
|
||||
|
||||
#define COLOR_BLACK nvgRGBA(0, 0, 0, 255)
|
||||
#define COLOR_BLACK_ALPHA(x) nvgRGBA(0, 0, 0, x)
|
||||
#define COLOR_WHITE nvgRGBA(255, 255, 255, 255)
|
||||
#define COLOR_WHITE_ALPHA(x) nvgRGBA(255, 255, 255, x)
|
||||
#define COLOR_RED_ALPHA(x) nvgRGBA(201, 34, 49, x)
|
||||
@@ -39,17 +37,6 @@ typedef cereal::CarControl::HUDControl::AudibleAlert AudibleAlert;
|
||||
const float y_offset = Hardware::EON() ? 0.0 : 150.0;
|
||||
const float ZOOM = Hardware::EON() ? 2138.5 : 2912.8;
|
||||
|
||||
typedef struct Rect {
|
||||
int x, y, w, h;
|
||||
int centerX() const { return x + w / 2; }
|
||||
int centerY() const { return y + h / 2; }
|
||||
int right() const { return x + w; }
|
||||
int bottom() const { return y + h; }
|
||||
bool ptInRect(int px, int py) const {
|
||||
return px >= x && px < (x + w) && py >= y && py < (y + h);
|
||||
}
|
||||
} Rect;
|
||||
|
||||
struct Alert {
|
||||
QString text1;
|
||||
QString text2;
|
||||
@@ -136,9 +123,6 @@ typedef struct UIState {
|
||||
int fb_w = 0, fb_h = 0;
|
||||
NVGcontext *vg;
|
||||
|
||||
// images
|
||||
std::map<std::string, int> images;
|
||||
|
||||
std::unique_ptr<SubMaster> sm;
|
||||
|
||||
UIStatus status;
|
||||
|
||||
Reference in New Issue
Block a user