diff --git a/common/params_keys.h b/common/params_keys.h index 0b08d8c0c..142b3422c 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -156,6 +156,7 @@ inline static std::unordered_map keys = { {"OffroadMode", {CLEAR_ON_MANAGER_START, BOOL}}, {"QuickBootToggle", {PERSISTENT | BACKUP, BOOL, "0"}}, {"QuietMode", {PERSISTENT | BACKUP, BOOL, "0"}}, + {"RainbowMode", {PERSISTENT | BACKUP, BOOL, "0"}}, {"ShowAdvancedControls", {PERSISTENT | BACKUP, BOOL, "0"}}, // MADS params diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.cc index 818da9b75..dd2f05416 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.cc @@ -28,6 +28,13 @@ VisualsPanel::VisualsPanel(QWidget *parent) : QWidget(parent) { "../assets/offroad/icon_monitoring.png", false, }, + { + "RainbowMode", + tr("Enable Tesla Rainbow Mode"), + RainbowizeWords(tr("A beautiful rainbow effect on the path the model wants to take.")) + "
" + tr("It")+ " " + tr("does not") + " " + tr("affect driving in any way.") + "", + "../assets/offroad/icon_monitoring.png", + false, + }, }; // Add regular toggles first diff --git a/selfdrive/ui/sunnypilot/qt/onroad/model.cc b/selfdrive/ui/sunnypilot/qt/onroad/model.cc index af0177c34..5d92838f8 100644 --- a/selfdrive/ui/sunnypilot/qt/onroad/model.cc +++ b/selfdrive/ui/sunnypilot/qt/onroad/model.cc @@ -48,5 +48,43 @@ void ModelRendererSP::drawPath(QPainter &painter, const cereal::ModelDataV2::Rea painter.drawPolygon(right_blindspot_vertices); } } - ModelRenderer::drawPath(painter, model, surface_rect.height()); + + bool rainbow = Params().getBool("RainbowMode"); + //float v_ego = sm["carState"].getCarState().getVEgo(); + + if (rainbow) { + // Simple time-based animation + float time_offset = std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()).count() / 1000.0f; + + // simple linear gradient from bottom to top + QLinearGradient bg(0, surface_rect.height(), 0, 0); + + // evenly spaced colors across the spectrum + // The animation shifts the entire spectrum smoothly + float animation_speed = 40.0f; // speed vroom vroom + float hue_offset = fmod(time_offset * animation_speed, 360.0f); + + // 6-8 color stops for smooth transitions more color makes it laggy + const int num_stops = 7; + for (int i = 0; i < num_stops; i++) { + float position = static_cast(i) / (num_stops - 1); + + float hue = fmod(hue_offset + position * 360.0f, 360.0f); + float saturation = 0.9f; + float lightness = 0.6f; + + // Alpha fades out towards the far end of the path + float alpha = 0.8f * (1.0f - position * 0.3f); + + QColor color = QColor::fromHslF(hue / 360.0f, saturation, lightness, alpha); + bg.setColorAt(position, color); + } + + painter.setBrush(bg); + painter.drawPolygon(track_vertices); + } else { + // Normal path rendering + ModelRenderer::drawPath(painter, model, surface_rect.height()); + } } diff --git a/selfdrive/ui/sunnypilot/qt/widgets/controls.h b/selfdrive/ui/sunnypilot/qt/widgets/controls.h index a840febfe..03cb46138 100644 --- a/selfdrive/ui/sunnypilot/qt/widgets/controls.h +++ b/selfdrive/ui/sunnypilot/qt/widgets/controls.h @@ -750,3 +750,24 @@ public: setFixedSize(400, 100); } }; + +inline QString RainbowizeWords(const QString &text) { + const QStringList colors = { + "#FF6F61", // soft coral red + "#FFA177", // warm peach + "#FFD966", // soft golden yellow + "#88D498", // mint green + "#6EC6FF", // sky blue + "#A78BFA", // soft lavender + "#F78FB3" // rose pink + }; + + QString result; + QStringList words = text.split(' '); + + for (int i = 0; i < words.size(); ++i) { + result += QString("%2 ").arg(colors[i % colors.size()]).arg(words[i].toHtmlEscaped()); + } + + return result.trimmed(); + }