mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-07-17 23:32:07 +08:00
UI: new set speed design, show speed limits (#24736)
* basic US design * place based on center position * fix typo * eu sign without rounded box * same as steering wheel icon * proper rounded bottom for eu sign * add border * proper placement/sizes * needs to be semi bold * color changes * only when engaged * move helpers into util.h * Fix MAX placement * only change color when at least 5 over * implement override state * pixel perfect spacing around us sign old-commit-hash: d528cd556848404891e28b3342d146498e3991cc
This commit is contained in:
+140
-22
@@ -174,17 +174,24 @@ void NvgWindow::updateState(const UIState &s) {
|
||||
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;
|
||||
float set_speed = cs.getVCruise();
|
||||
bool cruise_set = set_speed > 0 && (int)set_speed != SET_SPEED_NA;
|
||||
if (cruise_set && !s.scene.is_metric) {
|
||||
maxspeed *= KM_TO_MILE;
|
||||
set_speed *= 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));
|
||||
|
||||
auto speed_limit_sign = sm["navInstruction"].getNavInstruction().getSpeedLimitSign();
|
||||
float speed_limit = sm["navInstruction"].getValid() ? sm["navInstruction"].getNavInstruction().getSpeedLimit() : 0.0;
|
||||
speed_limit *= (s.scene.is_metric ? MS_TO_KPH : MS_TO_MPH);
|
||||
|
||||
setProperty("speedLimit", speed_limit);
|
||||
setProperty("has_us_speed_limit", speed_limit > 1 && speed_limit_sign == cereal::NavInstruction::SpeedLimitSign::MUTCD);
|
||||
setProperty("has_eu_speed_limit", speed_limit > 1 && speed_limit_sign == cereal::NavInstruction::SpeedLimitSign::VIENNA);
|
||||
|
||||
setProperty("is_cruise_set", cruise_set);
|
||||
setProperty("speed", QString::number(std::nearbyint(cur_speed)));
|
||||
setProperty("maxSpeed", maxspeed_str);
|
||||
setProperty("speed", cur_speed);
|
||||
setProperty("setSpeed", set_speed);
|
||||
setProperty("speedUnit", s.scene.is_metric ? "km/h" : "mph");
|
||||
setProperty("hideDM", cs.getAlertSize() != cereal::ControlsState::AlertSize::NONE);
|
||||
setProperty("status", s.status);
|
||||
@@ -205,26 +212,139 @@ void NvgWindow::drawHud(QPainter &p) {
|
||||
bg.setColorAt(1, QColor::fromRgbF(0, 0, 0, 0));
|
||||
p.fillRect(0, 0, width(), header_h, bg);
|
||||
|
||||
// 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);
|
||||
QString speedLimitStr = QString::number(std::nearbyint(speedLimit));
|
||||
QString speedStr = QString::number(std::nearbyint(speed));
|
||||
QString setSpeedStr = is_cruise_set ? QString::number(std::nearbyint(setSpeed)) : "–";
|
||||
|
||||
configFont(p, "Open Sans", 48, "Regular");
|
||||
drawText(p, rc.center().x(), 118, "MAX", is_cruise_set ? 200 : 100);
|
||||
// Draw outer box + border to contain set speed and speed limit
|
||||
int default_rect_width = 172;
|
||||
int rect_width = default_rect_width;
|
||||
if (has_us_speed_limit && speedLimitStr.size() >= 3) rect_width = 223;
|
||||
else if (has_eu_speed_limit) rect_width = 200;
|
||||
|
||||
int rect_height = 204;
|
||||
if (has_us_speed_limit) rect_height = 402;
|
||||
else if (has_eu_speed_limit) rect_height = 392;
|
||||
|
||||
int top_radius = 32;
|
||||
int bottom_radius = has_eu_speed_limit ? 100 : 32;
|
||||
|
||||
QRect set_speed_rect(60 + default_rect_width / 2 - rect_width / 2, 45, rect_width, rect_height);
|
||||
p.setPen(QPen(QColor(255, 255, 255, 75), 6));
|
||||
p.setBrush(QColor(0, 0, 0, 166));
|
||||
drawRoundedRect(p, set_speed_rect, top_radius, top_radius, bottom_radius, bottom_radius);
|
||||
|
||||
// Draw set speed
|
||||
if (is_cruise_set) {
|
||||
configFont(p, "Open Sans", 88, "Bold");
|
||||
drawText(p, rc.center().x(), 212, maxSpeed, 255);
|
||||
if (speedLimit > 0 && status != STATUS_DISENGAGED && status != STATUS_OVERRIDE) {
|
||||
p.setPen(interpColor(
|
||||
setSpeed,
|
||||
{speedLimit + 5, speedLimit + 15, speedLimit + 25},
|
||||
{QColor(0xff, 0xff, 0xff, 0xff), QColor(0xff, 0x95, 0x00, 0xff), QColor(0xff, 0x00, 0x00, 0xff)}
|
||||
));
|
||||
} else {
|
||||
p.setPen(QColor(0xff, 0xff, 0xff, 0xff));
|
||||
}
|
||||
} else {
|
||||
configFont(p, "Open Sans", 80, "SemiBold");
|
||||
drawText(p, rc.center().x(), 212, maxSpeed, 100);
|
||||
p.setPen(QColor(0x72, 0x72, 0x72, 0xff));
|
||||
}
|
||||
configFont(p, "Open Sans", 90, "Bold");
|
||||
QRect speed_rect = getTextRect(p, Qt::AlignCenter, setSpeedStr);
|
||||
speed_rect.moveCenter({set_speed_rect.center().x(), 0});
|
||||
speed_rect.moveTop(set_speed_rect.top() + 8);
|
||||
p.drawText(speed_rect, Qt::AlignCenter, setSpeedStr);
|
||||
|
||||
// Draw MAX
|
||||
if (is_cruise_set) {
|
||||
if (status == STATUS_DISENGAGED) {
|
||||
p.setPen(QColor(0xff, 0xff, 0xff, 0xff));
|
||||
} else if (status == STATUS_OVERRIDE) {
|
||||
p.setPen(QColor(0x91, 0x9b, 0x95, 0xff));
|
||||
} else if (speedLimit > 0) {
|
||||
p.setPen(interpColor(
|
||||
setSpeed,
|
||||
{speedLimit + 5, speedLimit + 15, speedLimit + 25},
|
||||
{QColor(0x80, 0xd8, 0xa6, 0xff), QColor(0xff, 0xe4, 0xbf, 0xff), QColor(0xff, 0xbf, 0xbf, 0xff)}
|
||||
));
|
||||
} else {
|
||||
p.setPen(QColor(0x80, 0xd8, 0xa6, 0xff));
|
||||
}
|
||||
} else {
|
||||
p.setPen(QColor(0xa6, 0xa6, 0xa6, 0xff));
|
||||
}
|
||||
configFont(p, "Open Sans", 40, "SemiBold");
|
||||
QRect max_rect = getTextRect(p, Qt::AlignCenter, "MAX");
|
||||
max_rect.moveCenter({set_speed_rect.center().x(), 0});
|
||||
max_rect.moveTop(set_speed_rect.top() + 123);
|
||||
p.drawText(max_rect, Qt::AlignCenter, "MAX");
|
||||
|
||||
// US/Canada (MUTCD style) sign
|
||||
if (has_us_speed_limit) {
|
||||
const int border_width = 6;
|
||||
const int sign_width = (speedLimitStr.size() >= 3) ? 199 : 148;
|
||||
const int sign_height = 186;
|
||||
|
||||
// White outer square
|
||||
QRect sign_rect_outer(set_speed_rect.left() + 12, set_speed_rect.bottom() - 11 - sign_height, sign_width, sign_height);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(QColor(255, 255, 255, 255));
|
||||
p.drawRoundedRect(sign_rect_outer, 24, 24);
|
||||
|
||||
// Smaller white square with black border
|
||||
QRect sign_rect(sign_rect_outer.left() + 1.5 * border_width, sign_rect_outer.top() + 1.5 * border_width, sign_width - 3 * border_width, sign_height - 3 * border_width);
|
||||
p.setPen(QPen(QColor(0, 0, 0, 255), border_width));
|
||||
p.setBrush(QColor(255, 255, 255, 255));
|
||||
p.drawRoundedRect(sign_rect, 16, 16);
|
||||
|
||||
// "SPEED"
|
||||
configFont(p, "Open Sans", 28, "SemiBold");
|
||||
QRect text_speed_rect = getTextRect(p, Qt::AlignCenter, "SPEED");
|
||||
text_speed_rect.moveCenter({sign_rect.center().x(), 0});
|
||||
text_speed_rect.moveTop(sign_rect_outer.top() + 20);
|
||||
p.drawText(text_speed_rect, Qt::AlignCenter, "SPEED");
|
||||
|
||||
// "LIMIT"
|
||||
QRect text_limit_rect = getTextRect(p, Qt::AlignCenter, "LIMIT");
|
||||
text_limit_rect.moveCenter({sign_rect.center().x(), 0});
|
||||
text_limit_rect.moveTop(sign_rect_outer.top() + 48);
|
||||
p.drawText(text_limit_rect, Qt::AlignCenter, "LIMIT");
|
||||
|
||||
// Speed limit value
|
||||
configFont(p, "Open Sans", 70, "Bold");
|
||||
QRect speed_limit_rect = getTextRect(p, Qt::AlignCenter, speedLimitStr);
|
||||
speed_limit_rect.moveCenter({sign_rect.center().x(), 0});
|
||||
speed_limit_rect.moveTop(sign_rect.top() + 70);
|
||||
p.drawText(speed_limit_rect, Qt::AlignCenter, speedLimitStr);
|
||||
}
|
||||
|
||||
// EU (Vienna style) sign
|
||||
if (has_eu_speed_limit) {
|
||||
int outer_radius = 176 / 2;
|
||||
int inner_radius_1 = outer_radius - 6; // White outer border
|
||||
int inner_radius_2 = inner_radius_1 - 20; // Red circle
|
||||
|
||||
// Draw white circle with red border
|
||||
QPoint center(set_speed_rect.center().x() + 1, set_speed_rect.top() + 204 + outer_radius);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(QColor(255, 255, 255, 255));
|
||||
p.drawEllipse(center, outer_radius, outer_radius);
|
||||
p.setBrush(QColor(255, 0, 0, 255));
|
||||
p.drawEllipse(center, inner_radius_1, inner_radius_1);
|
||||
p.setBrush(QColor(255, 255, 255, 255));
|
||||
p.drawEllipse(center, inner_radius_2, inner_radius_2);
|
||||
|
||||
// Speed limit value
|
||||
int font_size = (speedLimitStr.size() >= 3) ? 62 : 70;
|
||||
configFont(p, "Open Sans", font_size, "Bold");
|
||||
QRect speed_limit_rect = getTextRect(p, Qt::AlignCenter, speedLimitStr);
|
||||
speed_limit_rect.moveCenter(center);
|
||||
p.setPen(QColor(0, 0, 0, 255));
|
||||
p.drawText(speed_limit_rect, Qt::AlignCenter, speedLimitStr);
|
||||
}
|
||||
|
||||
// current speed
|
||||
configFont(p, "Open Sans", 176, "Bold");
|
||||
drawText(p, rect().center().x(), 210, speed);
|
||||
drawText(p, rect().center().x(), 210, speedStr);
|
||||
configFont(p, "Open Sans", 66, "Regular");
|
||||
drawText(p, rect().center().x(), 290, speedUnit, 200);
|
||||
|
||||
@@ -243,9 +363,7 @@ void NvgWindow::drawHud(QPainter &p) {
|
||||
}
|
||||
|
||||
void NvgWindow::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);
|
||||
QRect real_rect = getTextRect(p, 0, text);
|
||||
real_rect.moveCenter({x, y - real_rect.height() / 2});
|
||||
|
||||
p.setPen(QColor(0xff, 0xff, 0xff, alpha));
|
||||
|
||||
@@ -27,10 +27,14 @@ private:
|
||||
// container window for the NVG UI
|
||||
class NvgWindow : public CameraViewWidget {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString speed MEMBER speed);
|
||||
Q_PROPERTY(float speed MEMBER speed);
|
||||
Q_PROPERTY(QString speedUnit MEMBER speedUnit);
|
||||
Q_PROPERTY(QString maxSpeed MEMBER maxSpeed);
|
||||
Q_PROPERTY(float setSpeed MEMBER setSpeed);
|
||||
Q_PROPERTY(float speedLimit MEMBER speedLimit);
|
||||
Q_PROPERTY(bool is_cruise_set MEMBER is_cruise_set);
|
||||
Q_PROPERTY(bool has_eu_speed_limit MEMBER has_eu_speed_limit);
|
||||
Q_PROPERTY(bool has_us_speed_limit MEMBER has_us_speed_limit);
|
||||
|
||||
Q_PROPERTY(bool engageable MEMBER engageable);
|
||||
Q_PROPERTY(bool dmActive MEMBER dmActive);
|
||||
Q_PROPERTY(bool hideDM MEMBER hideDM);
|
||||
@@ -48,13 +52,16 @@ private:
|
||||
QPixmap dm_img;
|
||||
const int radius = 192;
|
||||
const int img_size = (radius / 2) * 1.5;
|
||||
QString speed;
|
||||
float speed;
|
||||
QString speedUnit;
|
||||
QString maxSpeed;
|
||||
float setSpeed;
|
||||
float speedLimit;
|
||||
bool is_cruise_set = false;
|
||||
bool engageable = false;
|
||||
bool dmActive = false;
|
||||
bool hideDM = false;
|
||||
bool has_us_speed_limit = false;
|
||||
bool has_eu_speed_limit = false;
|
||||
int status = STATUS_DISENGAGED;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -135,3 +135,64 @@ QPixmap loadPixmap(const QString &fileName, const QSize &size, Qt::AspectRatioMo
|
||||
return QPixmap(fileName).scaled(size, aspectRatioMode, Qt::SmoothTransformation);
|
||||
}
|
||||
}
|
||||
|
||||
QRect getTextRect(QPainter &p, int flags, QString text) {
|
||||
QFontMetrics fm(p.font());
|
||||
QRect init_rect = fm.boundingRect(text);
|
||||
return fm.boundingRect(init_rect, flags, text);
|
||||
}
|
||||
|
||||
void drawRoundedRect(QPainter &painter, const QRectF &rect, qreal xRadiusTop, qreal yRadiusTop, qreal xRadiusBottom, qreal yRadiusBottom){
|
||||
qreal w_2 = rect.width() / 2;
|
||||
qreal h_2 = rect.height() / 2;
|
||||
|
||||
xRadiusTop = 100 * qMin(xRadiusTop, w_2) / w_2;
|
||||
yRadiusTop = 100 * qMin(yRadiusTop, h_2) / h_2;
|
||||
|
||||
xRadiusBottom = 100 * qMin(xRadiusBottom, w_2) / w_2;
|
||||
yRadiusBottom = 100 * qMin(yRadiusBottom, h_2) / h_2;
|
||||
|
||||
qreal x = rect.x();
|
||||
qreal y = rect.y();
|
||||
qreal w = rect.width();
|
||||
qreal h = rect.height();
|
||||
|
||||
qreal rxx2Top = w*xRadiusTop/100;
|
||||
qreal ryy2Top = h*yRadiusTop/100;
|
||||
|
||||
qreal rxx2Bottom = w*xRadiusBottom/100;
|
||||
qreal ryy2Bottom = h*yRadiusBottom/100;
|
||||
|
||||
QPainterPath path;
|
||||
path.arcMoveTo(x, y, rxx2Top, ryy2Top, 180);
|
||||
path.arcTo(x, y, rxx2Top, ryy2Top, 180, -90);
|
||||
path.arcTo(x+w-rxx2Top, y, rxx2Top, ryy2Top, 90, -90);
|
||||
path.arcTo(x+w-rxx2Bottom, y+h-ryy2Bottom, rxx2Bottom, ryy2Bottom, 0, -90);
|
||||
path.arcTo(x, y+h-ryy2Bottom, rxx2Bottom, ryy2Bottom, 270, -90);
|
||||
path.closeSubpath();
|
||||
|
||||
painter.drawPath(path);
|
||||
}
|
||||
|
||||
QColor interpColor(float xv, std::vector<float> xp, std::vector<QColor> fp) {
|
||||
assert(xp.size() == fp.size());
|
||||
|
||||
int N = xp.size();
|
||||
int hi = 0;
|
||||
|
||||
while (hi < N and xv > xp[hi]) hi++;
|
||||
int low = hi - 1;
|
||||
|
||||
if (hi == N && xv > xp[low]) {
|
||||
return fp[fp.size() - 1];
|
||||
} else if (hi == 0){
|
||||
return fp[0];
|
||||
} else {
|
||||
return QColor(
|
||||
(xv - xp[low]) * (fp[hi].red() - fp[low].red()) / (xp[hi] - xp[low]) + fp[low].red(),
|
||||
(xv - xp[low]) * (fp[hi].green() - fp[low].green()) / (xp[hi] - xp[low]) + fp[low].green(),
|
||||
(xv - xp[low]) * (fp[hi].blue() - fp[low].blue()) / (xp[hi] - xp[low]) + fp[low].blue(),
|
||||
(xv - xp[low]) * (fp[hi].alpha() - fp[low].alpha()) / (xp[hi] - xp[low]) + fp[low].alpha()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,3 +22,7 @@ void swagLogMessageHandler(QtMsgType type, const QMessageLogContext &context, co
|
||||
void initApp(int argc, char *argv[]);
|
||||
QWidget* topWidget (QWidget* widget);
|
||||
QPixmap loadPixmap(const QString &fileName, const QSize &size = {}, Qt::AspectRatioMode aspectRatioMode = Qt::KeepAspectRatio);
|
||||
|
||||
QRect getTextRect(QPainter &p, int flags, QString text);
|
||||
void drawRoundedRect(QPainter &painter, const QRectF &rect, qreal xRadiusTop, qreal yRadiusTop, qreal xRadiusBottom, qreal yRadiusBottom);
|
||||
QColor interpColor(float xv, std::vector<float> xp, std::vector<QColor> fp);
|
||||
|
||||
Reference in New Issue
Block a user