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-06-08 20:02:03 -07:00
parent 5c84c8ded9
commit 2740c83a79
6 changed files with 94 additions and 20 deletions
+63 -17
View File
@@ -277,13 +277,21 @@ void AnnotatedCameraWidget::drawLaneLines(QPainter &painter, const UIState *s, c
// lanelines
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]);
}
// road edges
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]);
}
@@ -292,8 +300,15 @@ void AnnotatedCameraWidget::drawLaneLines(QPainter &painter, const UIState *s, c
if (experimentalMode || scene.acceleration_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
const auto &acceleration = sm["uiPlan"].getUiPlan().getAccel();
const int max_len = std::min<int>(scene.track_vertices.length() / 2, acceleration.size());
const auto &acceleration_const = sm["uiPlan"].getUiPlan().getAccel();
const int max_len = std::min<int>(scene.track_vertices.length() / 2, acceleration_const.size());
// Copy of the acceleration vector
std::vector<float> acceleration;
acceleration.reserve(acceleration_const.size());
for (size_t i = 0; i < acceleration_const.size(); i++) {
acceleration.push_back(acceleration_const[i]);
}
for (int i = 0; i < max_len; ++i) {
// Some points are out of frame
@@ -303,18 +318,31 @@ void AnnotatedCameraWidget::drawLaneLines(QPainter &painter, const UIState *s, c
// Flip so 0 is bottom of frame
float lin_grad_point = (height() - scene.track_vertices[track_idx].y()) / height();
// 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;
// If acceleration is between -0.25 and 0.25, resort to the theme color
if (std::abs(acceleration[i]) < 0.25 && (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 {
// 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 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
bg.setColorAt(lin_grad_point, QColor::fromHslF(path_hue / 360., saturation, lightness, alpha));
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 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));
// Skip a point, unless next is last
i += (i + 2) < max_len ? 1 : 0;
// Skip a point, unless next is last
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 {
@@ -452,8 +480,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) {
painter.save();
const float speedBuff = 10.;
const float leadBuff = 40.;
const float speedBuff = customColors != 0 ? 25. : 10.; // Make the center of the chevron appear sooner if a custom theme is active
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 v_rel = lead_data.getV()[0] - v_ego;
@@ -479,7 +507,11 @@ void AnnotatedCameraWidget::drawLead(QPainter &painter, const cereal::ModelDataV
// chevron
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.restore();
@@ -608,6 +640,18 @@ void AnnotatedCameraWidget::initializeFrogPilotWidgets() {
main_layout->addLayout(bottom_layout);
stopSignImg = loadPixmap("../frogpilot/assets/other_images/stop_sign.png", QSize(img_size, img_size));
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::paintFrogPilotWidgets(QPainter &painter, const UIScene &scene) {
@@ -657,6 +701,8 @@ void AnnotatedCameraWidget::paintFrogPilotWidgets(QPainter &painter, const UISce
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;
customColors = scene.custom_colors;
experimentalMode = scene.experimental_mode;
laneDetectionWidth = scene.lane_detection_width;
@@ -136,6 +136,7 @@ private:
int conditionalSpeed;
int conditionalSpeedLead;
int conditionalStatus;
int customColors;
QPixmap stopSignImg;
@@ -143,6 +144,8 @@ private:
QString leadDistanceUnit;
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 greenColor(int alpha = 242) { return QColor(23, 134, 68, alpha); }
+20 -3
View File
@@ -38,6 +38,19 @@ Sidebar::Sidebar(QWidget *parent) : QFrame(parent), onroad(false), flag_pressed(
QObject::connect(uiState(), &UIState::uiUpdate, this, &Sidebar::updateState);
pm = std::make_unique<PubMaster, const std::initializer_list<const char *>>({"userFlag"});
// FrogPilot variables
UIState *s = uiState();
UIScene &scene = s->scene;
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) {
@@ -80,6 +93,10 @@ void Sidebar::updateState(const UIState &s) {
setProperty("netStrength", strength > 0 ? strength + 1 : 0);
// FrogPilot properties
const UIScene &scene = s.scene;
currentColors = themeConfiguration[scene.custom_colors].second;
auto frogpilotDeviceState = sm["frogpilotDeviceState"].getFrogpilotDeviceState();
ItemStatus connectStatus;
@@ -88,7 +105,7 @@ void Sidebar::updateState(const UIState &s) {
connectStatus = ItemStatus{{tr("CONNECT"), tr("OFFLINE")}, warning_color};
} else {
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};
}
setProperty("connectStatus", QVariant::fromValue(connectStatus));
@@ -96,13 +113,13 @@ void Sidebar::updateState(const UIState &s) {
ItemStatus tempStatus = {{tr("TEMP"), tr("HIGH")}, danger_color};
auto ts = deviceState.getThermalStatus();
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) {
tempStatus = {{tr("TEMP"), tr("OK")}, warning_color};
}
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) {
pandaStatus = {{tr("NO"), tr("PANDA")}, danger_color};
} else if (s.scene.started && !sm["liveLocationKalman"].getLiveLocationKalman().getGpsOK()) {
+4
View File
@@ -62,4 +62,8 @@ private:
// FrogPilot variables
Params params;
std::unordered_map<int, std::pair<QString, std::vector<QColor>>> themeConfiguration;
std::vector<QColor> currentColors;
};
+3
View File
@@ -329,6 +329,9 @@ void ui_update_frogpilot_params(UIState *s, Params &params) {
scene.show_stopping_point_metrics = scene.show_stopping_point && params.getBool("ShowStoppingPointMetrics");
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_vtsc = params.getBool("VisionTurnControl") && params.getBool("DisableVTSCSmoothing");
+1
View File
@@ -185,6 +185,7 @@ typedef struct UIScene {
int conditional_speed;
int conditional_speed_lead;
int conditional_status;
int custom_colors;
int model_length;
int steering_angle_deg;
int tethering_config;