diff --git a/selfdrive/ui/qt/onroad/model.cc b/selfdrive/ui/qt/onroad/model.cc index c709145980cd0d..b39592422687a7 100644 --- a/selfdrive/ui/qt/onroad/model.cc +++ b/selfdrive/ui/qt/onroad/model.cc @@ -135,15 +135,57 @@ void ModelRenderer::drawPath(QPainter &painter, const cereal::ModelDataV2::Reade } } else { - 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(1.0, QColor::fromHslF(112 / 360., 1.0, 0.68, 0.0)); + updatePathGradient(bg); } painter.setBrush(bg); painter.drawPolygon(track_vertices); } +void ModelRenderer::updatePathGradient(QLinearGradient &bg) { + static const QColor throttle_colors[] = { + QColor::fromHslF(148. / 360., 0.94, 0.51, 0.4), + QColor::fromHslF(112. / 360., 1.0, 0.68, 0.35), + QColor::fromHslF(112. / 360., 1.0, 0.68, 0.0)}; + + static const QColor no_throttle_colors[] = { + QColor::fromHslF(148. / 360., 0.0, 0.95, 0.4), + QColor::fromHslF(112. / 360., 0.0, 0.95, 0.35), + QColor::fromHslF(112. / 360., 0.0, 0.95, 0.0), + }; + + // Transition speed; 0.1 corresponds to 0.5 seconds at UI_FREQ + constexpr float transition_speed = 0.1f; + + // Start transition if throttle state changes + bool allow_throttle = (*uiState()->sm)["longitudinalPlan"].getLongitudinalPlan().getAllowThrottle(); + if (allow_throttle != prev_allow_throttle) { + prev_allow_throttle = allow_throttle; + // Invert blend factor for a smooth transition when the state changes mid-animation + blend_factor = std::max(1.0f - blend_factor, 0.0f); + } + + const QColor *begin_colors = allow_throttle ? no_throttle_colors : throttle_colors; + const QColor *end_colors = allow_throttle ? throttle_colors : no_throttle_colors; + if (blend_factor < 1.0f) { + blend_factor = std::min(blend_factor + transition_speed, 1.0f); + } + + // Set gradient colors by blending the start and end colors + bg.setColorAt(0.0f, blendColors(begin_colors[0], end_colors[0], blend_factor)); + bg.setColorAt(0.5f, blendColors(begin_colors[1], end_colors[1], blend_factor)); + bg.setColorAt(1.0f, blendColors(begin_colors[2], end_colors[2], blend_factor)); +} + +QColor ModelRenderer::blendColors(const QColor &start, const QColor &end, float t) { + if (t == 1.0f) return end; + return QColor::fromRgbF( + (1 - t) * start.redF() + t * end.redF(), + (1 - t) * start.greenF() + t * end.greenF(), + (1 - t) * start.blueF() + t * end.blueF(), + (1 - t) * start.alphaF() + t * end.alphaF()); +} + void ModelRenderer::drawLead(QPainter &painter, const cereal::RadarState::LeadData::Reader &lead_data, const QPointF &vd, const QRect &surface_rect) { const float speedBuff = 10.; diff --git a/selfdrive/ui/qt/onroad/model.h b/selfdrive/ui/qt/onroad/model.h index a0476d542ac986..739dbce1c480d5 100644 --- a/selfdrive/ui/qt/onroad/model.h +++ b/selfdrive/ui/qt/onroad/model.h @@ -20,9 +20,13 @@ class ModelRenderer { void update_model(const cereal::ModelDataV2::Reader &model, const cereal::RadarState::LeadData::Reader &lead); void drawLaneLines(QPainter &painter); void drawPath(QPainter &painter, const cereal::ModelDataV2::Reader &model, int height); + void updatePathGradient(QLinearGradient &bg); + QColor blendColors(const QColor &start, const QColor &end, float t); bool longitudinal_control = false; bool experimental_mode = false; + float blend_factor = 1.0f; + bool prev_allow_throttle = false; float lane_line_probs[4] = {}; float road_edge_stds[2] = {}; QPolygonF track_vertices; diff --git a/selfdrive/ui/ui.cc b/selfdrive/ui/ui.cc index b11330a046a097..b6ce6cb02e20f6 100644 --- a/selfdrive/ui/ui.cc +++ b/selfdrive/ui/ui.cc @@ -93,7 +93,7 @@ UIState::UIState(QObject *parent) : QObject(parent) { sm = std::make_unique(std::vector{ "modelV2", "controlsState", "liveCalibration", "radarState", "deviceState", "pandaStates", "carParams", "driverMonitoringState", "carState", "driverStateV2", - "wideRoadCameraState", "managerState", "selfdriveState", + "wideRoadCameraState", "managerState", "selfdriveState", "longitudinalPlan", }); prime_state = new PrimeState(this); language = QString::fromStdString(Params().get("LanguageSetting"));