Visuals - Custom Themes - Color Theme

Switch out the standard openpilot color scheme with themed colors.

Want to submit your own color scheme? Post it in the 'feature-request' channel in the FrogPilot Discord!
This commit is contained in:
FrogAi 2024-05-12 02:15:50 -07:00
parent 159f53d20c
commit 62e3e7225b
6 changed files with 88 additions and 21 deletions

View File

@ -645,13 +645,21 @@ void AnnotatedCameraWidget::drawLaneLines(QPainter &painter, const UIState *s) {
// lanelines // lanelines
for (int i = 0; i < std::size(scene.lane_line_vertices); ++i) { for (int i = 0; i < std::size(scene.lane_line_vertices); ++i) {
painter.setBrush(QColor::fromRgbF(1.0, 1.0, 1.0, std::clamp<float>(scene.lane_line_probs[i], 0.0, 0.7))); if (customColors != 0) {
painter.setBrush(std::get<2>(themeConfiguration[customColors]).begin()->second);
} else {
painter.setBrush(QColor::fromRgbF(1.0, 1.0, 1.0, std::clamp<float>(scene.lane_line_probs[i], 0.0, 0.7)));
}
painter.drawPolygon(scene.lane_line_vertices[i]); painter.drawPolygon(scene.lane_line_vertices[i]);
} }
// road edges // road edges
for (int i = 0; i < std::size(scene.road_edge_vertices); ++i) { for (int i = 0; i < std::size(scene.road_edge_vertices); ++i) {
painter.setBrush(QColor::fromRgbF(1.0, 0, 0, std::clamp<float>(1.0 - scene.road_edge_stds[i], 0.0, 1.0))); if (customColors != 0) {
painter.setBrush(std::get<2>(themeConfiguration[customColors]).begin()->second);
} else {
painter.setBrush(QColor::fromRgbF(1.0, 0, 0, std::clamp<float>(1.0 - scene.road_edge_stds[i], 0.0, 1.0)));
}
painter.drawPolygon(scene.road_edge_vertices[i]); painter.drawPolygon(scene.road_edge_vertices[i]);
} }
@ -660,8 +668,14 @@ void AnnotatedCameraWidget::drawLaneLines(QPainter &painter, const UIState *s) {
if (experimentalMode || scene.acceleration_path) { if (experimentalMode || scene.acceleration_path) {
// The first half of track_vertices are the points for the right side of the path // The first half of track_vertices are the points for the right side of the path
// and the indices match the positions of accel from uiPlan // and the indices match the positions of accel from uiPlan
const auto &acceleration = sm["uiPlan"].getUiPlan().getAccel(); const auto &acceleration_const = sm["uiPlan"].getUiPlan().getAccel();
const int max_len = std::min<int>(scene.track_vertices.length() / 2, acceleration.size()); const int max_len = std::min<int>(scene.track_vertices.length() / 2, acceleration_const.size());
// Copy of the acceleration vector
std::vector<float> acceleration;
for (int i = 0; i < acceleration_const.size(); i++) {
acceleration.push_back(acceleration_const[i]);
}
for (int i = 0; i < max_len; ++i) { for (int i = 0; i < max_len; ++i) {
// Some points are out of frame // Some points are out of frame
@ -670,20 +684,32 @@ void AnnotatedCameraWidget::drawLaneLines(QPainter &painter, const UIState *s) {
// Flip so 0 is bottom of frame // Flip so 0 is bottom of frame
float lin_grad_point = (height() - scene.track_vertices[i].y()) / height(); float lin_grad_point = (height() - scene.track_vertices[i].y()) / height();
// speed up: 120, slow down: 0 // If acceleration is between -0.25 and 0.25, resort to the theme color
float path_hue = fmax(fmin(60 + acceleration[i] * 35, 120), 0); if (std::abs(acceleration[i]) < 0.2 && (customColors != 0)) {
// FIXME: painter.drawPolygon can be slow if hue is not rounded const std::map<double, QBrush> &colorMap = std::get<2>(themeConfiguration[customColors]);
path_hue = int(path_hue * 100 + 0.5) / 100; for (const std::pair<double, QBrush> &entry : colorMap) {
bg.setColorAt(entry.first, entry.second.color());
}
} else {
// speed up: 120, slow down: 0
float path_hue = fmax(fmin(60 + acceleration[i] * 35, 120), 0);
// FIXME: painter.drawPolygon can be slow if hue is not rounded
path_hue = int(path_hue * 100 + 0.5) / 100;
float saturation = fmin(fabs(acceleration[i] * 1.5), 1); float saturation = fmin(fabs(acceleration[i] * 1.5), 1);
float lightness = util::map_val(saturation, 0.0f, 1.0f, 0.95f, 0.62f); // lighter when grey float lightness = util::map_val(saturation, 0.0f, 1.0f, 0.95f, 0.62f); // lighter when grey
float alpha = util::map_val(lin_grad_point, 0.75f / 2.f, 0.75f, 0.4f, 0.0f); // matches previous alpha fade float alpha = util::map_val(lin_grad_point, 0.75f / 2.f, 0.75f, 0.4f, 0.0f); // matches previous alpha fade
bg.setColorAt(lin_grad_point, QColor::fromHslF(path_hue / 360., saturation, lightness, alpha)); bg.setColorAt(lin_grad_point, QColor::fromHslF(path_hue / 360., saturation, lightness, alpha));
// Skip a point, unless next is last // Skip a point, unless next is last
i += (i + 2) < max_len ? 1 : 0; i += (i + 2) < max_len ? 1 : 0;
}
}
} else if (customColors != 0) {
const std::map<double, QBrush> &colorMap = std::get<2>(themeConfiguration[customColors]);
for (const std::pair<double, QBrush> &entry : colorMap) {
bg.setColorAt(entry.first, entry.second.color());
} }
} else { } else {
bg.setColorAt(0.0, QColor::fromHslF(148 / 360., 0.94, 0.51, 0.4)); bg.setColorAt(0.0, QColor::fromHslF(148 / 360., 0.94, 0.51, 0.4));
bg.setColorAt(0.5, QColor::fromHslF(112 / 360., 1.0, 0.68, 0.35)); bg.setColorAt(0.5, QColor::fromHslF(112 / 360., 1.0, 0.68, 0.35));
@ -791,8 +817,8 @@ void AnnotatedCameraWidget::drawDriverState(QPainter &painter, const UIState *s)
void AnnotatedCameraWidget::drawLead(QPainter &painter, const cereal::ModelDataV2::LeadDataV3::Reader &lead_data, const QPointF &vd, const float v_ego) { void AnnotatedCameraWidget::drawLead(QPainter &painter, const cereal::ModelDataV2::LeadDataV3::Reader &lead_data, const QPointF &vd, const float v_ego) {
painter.save(); painter.save();
const float speedBuff = 10.; const float speedBuff = customColors != 0 ? 25. : 10.; // Make the center of the chevron appear sooner if a custom theme is active
const float leadBuff = 40.; const float leadBuff = customColors != 0 ? 100. : 40.; // Make the center of the chevron appear sooner if a custom theme is active
const float d_rel = lead_data.getX()[0]; const float d_rel = lead_data.getX()[0];
const float v_rel = lead_data.getV()[0] - v_ego; const float v_rel = lead_data.getV()[0] - v_ego;
@ -818,7 +844,11 @@ void AnnotatedCameraWidget::drawLead(QPainter &painter, const cereal::ModelDataV
// chevron // chevron
QPointF chevron[] = {{x + (sz * 1.25), y + sz}, {x, y}, {x - (sz * 1.25), y + sz}}; QPointF chevron[] = {{x + (sz * 1.25), y + sz}, {x, y}, {x - (sz * 1.25), y + sz}};
painter.setBrush(redColor(fillAlpha)); if (customColors != 0) {
painter.setBrush(std::get<2>(themeConfiguration[customColors]).begin()->second);
} else {
painter.setBrush(redColor(fillAlpha));
}
painter.drawPolygon(chevron, std::size(chevron)); painter.drawPolygon(chevron, std::size(chevron));
painter.restore(); painter.restore();
@ -942,6 +972,18 @@ void AnnotatedCameraWidget::initializeFrogPilotWidgets() {
bottom_layout->addWidget(map_settings_btn_bottom); bottom_layout->addWidget(map_settings_btn_bottom);
main_layout->addLayout(bottom_layout); main_layout->addLayout(bottom_layout);
themeConfiguration = {
{1, {"frog_theme", QColor(23, 134, 68, 242), {{0.0, QBrush(QColor::fromHslF(144 / 360., 0.71, 0.31, 0.9))},
{0.5, QBrush(QColor::fromHslF(144 / 360., 0.71, 0.31, 0.5))},
{1.0, QBrush(QColor::fromHslF(144 / 360., 0.71, 0.31, 0.1))}}}},
{2, {"tesla_theme", QColor(0, 72, 255, 255), {{0.0, QBrush(QColor::fromHslF(223 / 360., 1.0, 0.5, 0.9))},
{0.5, QBrush(QColor::fromHslF(223 / 360., 1.0, 0.5, 0.5))},
{1.0, QBrush(QColor::fromHslF(223 / 360., 1.0, 0.5, 0.1))}}}},
{3, {"stalin_theme", QColor(255, 0, 0, 255), {{0.0, QBrush(QColor::fromHslF(0 / 360., 1.0, 0.5, 0.9))},
{0.5, QBrush(QColor::fromHslF(0 / 360., 1.0, 0.5, 0.5))},
{1.0, QBrush(QColor::fromHslF(0 / 360., 1.0, 0.5, 0.1))}}}},
};
} }
void AnnotatedCameraWidget::updateFrogPilotWidgets() { void AnnotatedCameraWidget::updateFrogPilotWidgets() {
@ -980,6 +1022,8 @@ void AnnotatedCameraWidget::updateFrogPilotWidgets() {
cruiseAdjustment = disableSmoothing || !is_cruise_set ? fmax(setSpeed - scene.adjusted_cruise, 0) : fmax(0.25 * (setSpeed - scene.adjusted_cruise) + 0.75 * cruiseAdjustment - 1, 0); cruiseAdjustment = disableSmoothing || !is_cruise_set ? fmax(setSpeed - scene.adjusted_cruise, 0) : fmax(0.25 * (setSpeed - scene.adjusted_cruise) + 0.75 * cruiseAdjustment - 1, 0);
vtscControllingCurve = scene.vtsc_controlling_curve; vtscControllingCurve = scene.vtsc_controlling_curve;
customColors = scene.custom_colors;
experimentalMode = scene.experimental_mode; experimentalMode = scene.experimental_mode;
laneDetectionWidth = scene.lane_detection_width; laneDetectionWidth = scene.lane_detection_width;

View File

@ -229,11 +229,14 @@ private:
int alertSize; int alertSize;
int conditionalStatus; int conditionalStatus;
int customColors;
QString accelerationUnit; QString accelerationUnit;
QString leadDistanceUnit; QString leadDistanceUnit;
QString leadSpeedUnit; QString leadSpeedUnit;
std::unordered_map<int, std::tuple<QString, QColor, std::map<double, QBrush>>> themeConfiguration;
inline QColor blueColor(int alpha = 255) { return QColor(0, 150, 255, alpha); } inline QColor blueColor(int alpha = 255) { return QColor(0, 150, 255, alpha); }
inline QColor greenColor(int alpha = 242) { return QColor(23, 134, 68, alpha); } inline QColor greenColor(int alpha = 242) { return QColor(23, 134, 68, alpha); }

View File

@ -38,6 +38,16 @@ Sidebar::Sidebar(QWidget *parent) : QFrame(parent), onroad(false), flag_pressed(
QObject::connect(uiState(), &UIState::uiUpdate, this, &Sidebar::updateState); QObject::connect(uiState(), &UIState::uiUpdate, this, &Sidebar::updateState);
pm = std::make_unique<PubMaster, const std::initializer_list<const char *>>({"userFlag"}); pm = std::make_unique<PubMaster, const std::initializer_list<const char *>>({"userFlag"});
// FrogPilot variables
themeConfiguration = {
{0, {"stock", {QColor(255, 255, 255)}}},
{1, {"frog_theme", {QColor(23, 134, 68)}}},
{2, {"tesla_theme", {QColor(0, 72, 255)}}},
{3, {"stalin_theme", {QColor(255, 0, 0)}}}
};
currentColors = themeConfiguration[scene.custom_colors].second;
} }
void Sidebar::mousePressEvent(QMouseEvent *event) { void Sidebar::mousePressEvent(QMouseEvent *event) {
@ -80,6 +90,8 @@ void Sidebar::updateState(const UIState &s) {
setProperty("netStrength", strength > 0 ? strength + 1 : 0); setProperty("netStrength", strength > 0 ? strength + 1 : 0);
// FrogPilot properties // FrogPilot properties
currentColors = themeConfiguration[scene.custom_colors].second;
auto frogpilotDeviceState = sm["frogpilotDeviceState"].getFrogpilotDeviceState(); auto frogpilotDeviceState = sm["frogpilotDeviceState"].getFrogpilotDeviceState();
ItemStatus connectStatus; ItemStatus connectStatus;
@ -88,7 +100,7 @@ void Sidebar::updateState(const UIState &s) {
connectStatus = ItemStatus{{tr("CONNECT"), tr("OFFLINE")}, warning_color}; connectStatus = ItemStatus{{tr("CONNECT"), tr("OFFLINE")}, warning_color};
} else { } else {
connectStatus = nanos_since_boot() - last_ping < 80e9 connectStatus = nanos_since_boot() - last_ping < 80e9
? ItemStatus{{tr("CONNECT"), tr("ONLINE")}, good_color} ? ItemStatus{{tr("CONNECT"), tr("ONLINE")}, currentColors[0]}
: ItemStatus{{tr("CONNECT"), tr("ERROR")}, danger_color}; : ItemStatus{{tr("CONNECT"), tr("ERROR")}, danger_color};
} }
setProperty("connectStatus", QVariant::fromValue(connectStatus)); setProperty("connectStatus", QVariant::fromValue(connectStatus));
@ -96,13 +108,13 @@ void Sidebar::updateState(const UIState &s) {
ItemStatus tempStatus = {{tr("TEMP"), tr("HIGH")}, danger_color}; ItemStatus tempStatus = {{tr("TEMP"), tr("HIGH")}, danger_color};
auto ts = deviceState.getThermalStatus(); auto ts = deviceState.getThermalStatus();
if (ts == cereal::DeviceState::ThermalStatus::GREEN) { if (ts == cereal::DeviceState::ThermalStatus::GREEN) {
tempStatus = {{tr("TEMP"), tr("GOOD")}, good_color}; tempStatus = {{tr("TEMP"), tr("GOOD")}, currentColors[0]};
} else if (ts == cereal::DeviceState::ThermalStatus::YELLOW) { } else if (ts == cereal::DeviceState::ThermalStatus::YELLOW) {
tempStatus = {{tr("TEMP"), tr("OK")}, warning_color}; tempStatus = {{tr("TEMP"), tr("OK")}, warning_color};
} }
setProperty("tempStatus", QVariant::fromValue(tempStatus)); setProperty("tempStatus", QVariant::fromValue(tempStatus));
ItemStatus pandaStatus = {{tr("VEHICLE"), tr("ONLINE")}, good_color}; ItemStatus pandaStatus = {{tr("VEHICLE"), tr("ONLINE")}, currentColors[0]};
if (s.scene.pandaType == cereal::PandaState::PandaType::UNKNOWN) { if (s.scene.pandaType == cereal::PandaState::PandaType::UNKNOWN) {
pandaStatus = {{tr("NO"), tr("PANDA")}, danger_color}; pandaStatus = {{tr("NO"), tr("PANDA")}, danger_color};
} else if (s.scene.started && !sm["liveLocationKalman"].getLiveLocationKalman().getGpsOK()) { } else if (s.scene.started && !sm["liveLocationKalman"].getLiveLocationKalman().getGpsOK()) {

View File

@ -63,4 +63,8 @@ private:
// FrogPilot variables // FrogPilot variables
Params params; Params params;
UIScene &scene; UIScene &scene;
std::unordered_map<int, std::pair<QString, std::vector<QColor>>> themeConfiguration;
std::vector<QColor> currentColors;
}; };

View File

@ -314,6 +314,9 @@ void ui_update_frogpilot_params(UIState *s) {
scene.rotating_wheel = custom_onroad_ui && params.getBool("RotatingWheel"); scene.rotating_wheel = custom_onroad_ui && params.getBool("RotatingWheel");
scene.wheel_icon = custom_onroad_ui ? params.getInt("WheelIcon") : 0; scene.wheel_icon = custom_onroad_ui ? params.getInt("WheelIcon") : 0;
bool custom_theme = params.getBool("CustomTheme");
scene.custom_colors = custom_theme ? params.getInt("CustomColors") : 0;
scene.disable_smoothing_mtsc = params.getBool("MTSCEnabled") && params.getBool("DisableMTSCSmoothing"); scene.disable_smoothing_mtsc = params.getBool("MTSCEnabled") && params.getBool("DisableMTSCSmoothing");
scene.disable_smoothing_vtsc = params.getBool("VisionTurnControl") && params.getBool("DisableVTSCSmoothing"); scene.disable_smoothing_vtsc = params.getBool("VisionTurnControl") && params.getBool("DisableVTSCSmoothing");

View File

@ -243,6 +243,7 @@ typedef struct UIScene {
int conditional_speed; int conditional_speed;
int conditional_speed_lead; int conditional_speed_lead;
int conditional_status; int conditional_status;
int custom_colors;
int steering_angle_deg; int steering_angle_deg;
int wheel_icon; int wheel_icon;