From b11db103c84bb15d450e83d7f2b5bd34d8d653a5 Mon Sep 17 00:00:00 2001 From: FrogAi <91348155+FrogAi@users.noreply.github.com> Date: Tue, 27 Feb 2024 16:34:47 -0700 Subject: [PATCH] Customize the model visuals in the onroad UI Added toggles to customize the lane lines, path, road edges, path edges, and an "Unlimited Length" mode that extends the road UI out as far as the model can see. --- common/params.cc | 7 ++++ selfdrive/frogpilot/ui/visual_settings.cc | 46 +++++++++++++++++++++++ selfdrive/frogpilot/ui/visual_settings.h | 2 +- selfdrive/ui/qt/onroad.cc | 38 +++++++++++++++++++ selfdrive/ui/ui.cc | 31 ++++++++++++--- selfdrive/ui/ui.h | 8 ++++ 6 files changed, 126 insertions(+), 6 deletions(-) diff --git a/common/params.cc b/common/params.cc index db38a20..08a4d73 100644 --- a/common/params.cc +++ b/common/params.cc @@ -242,20 +242,27 @@ std::unordered_map keys = { {"DecelerationProfile", PERSISTENT}, {"DisengageVolume", PERSISTENT}, {"DriveStats", PERSISTENT}, + {"DynamicPathWidth", PERSISTENT}, {"EngageVolume", PERSISTENT}, {"FrogPilotTogglesUpdated", PERSISTENT}, {"FrogsGoMoo", PERSISTENT}, {"GoatScream", PERSISTENT}, + {"LaneLinesWidth", PERSISTENT}, {"LateralTune", PERSISTENT}, {"LongitudinalTune", PERSISTENT}, {"ManualUpdateInitiated", CLEAR_ON_MANAGER_START}, + {"ModelUI", PERSISTENT}, + {"PathEdgeWidth", PERSISTENT}, + {"PathWidth", PERSISTENT}, {"PromptVolume", PERSISTENT}, {"PromptDistractedVolume", PERSISTENT}, {"QOLControls", PERSISTENT}, {"QOLVisuals", PERSISTENT}, {"RefuseVolume", PERSISTENT}, + {"RoadEdgesWidth", PERSISTENT}, {"SilentMode", PERSISTENT}, {"StockTune", PERSISTENT}, + {"UnlimitedLength", PERSISTENT}, {"UpdateSchedule", PERSISTENT}, {"UpdateTime", PERSISTENT}, {"WarningSoftVolume", PERSISTENT}, diff --git a/selfdrive/frogpilot/ui/visual_settings.cc b/selfdrive/frogpilot/ui/visual_settings.cc index 610fe4d..036b386 100644 --- a/selfdrive/frogpilot/ui/visual_settings.cc +++ b/selfdrive/frogpilot/ui/visual_settings.cc @@ -25,6 +25,14 @@ FrogPilotVisualsPanel::FrogPilotVisualsPanel(SettingsWindow *parent) : FrogPilot {"AccelerationPath", "Acceleration Path", "Visualize the car's intended acceleration or deceleration with a color-coded path.", ""}, {"BlindSpotPath", "Blind Spot Path", "Visualize your blind spots with a red path when another vehicle is detected nearby.", ""}, + {"ModelUI", "Model UI", "Personalize how the model's visualizations appear on your screen.", "../assets/offroad/icon_calibration.png"}, + {"DynamicPathWidth", "Dynamic Path Width", "Have the path width dynamically adjust based on the current engagement state of openpilot.", ""}, + {"LaneLinesWidth", "Lane Lines", "Adjust the visual thickness of lane lines on your display.\n\nDefault matches the MUTCD average of 4 inches.", ""}, + {"PathEdgeWidth", "Path Edges", "Adjust the width of the path edges shown on your UI to represent different driving modes and statuses.\n\nDefault is 20% of the total path.\n\nBlue = Navigation\nLight Blue = Always On Lateral\nGreen = Default with 'FrogPilot Colors'\nLight Green = Default with stock colors\nOrange = Experimental Mode Active\nYellow = Conditional Overriden", ""}, + {"PathWidth", "Path Width", "Customize the width of the driving path shown on your UI.\n\nDefault matches the width of a 2019 Lexus ES 350.", ""}, + {"RoadEdgesWidth", "Road Edges", "Adjust the visual thickness of road edges on your display.\n\nDefault is 1/2 of the MUTCD average lane line width of 4 inches.", ""}, + {"UnlimitedLength", "'Unlimited' Road UI Length", "Extend the display of the path, lane lines, and road edges as far as the system can detect, providing a more expansive view of the road ahead.", ""}, + {"QOLVisuals", "Quality of Life", "Miscellaneous quality of life changes to improve your overall openpilot experience.", "../frogpilot/assets/toggle_icons/quality_of_life.png"}, {"DriveStats", "Drive Stats In Home Screen", "Display your device's drive stats in the home screen.", ""}, }; @@ -99,6 +107,22 @@ FrogPilotVisualsPanel::FrogPilotVisualsPanel(SettingsWindow *parent) : FrogPilot }); toggle = customUIToggle; + } else if (param == "ModelUI") { + FrogPilotParamManageControl *modelUIToggle = new FrogPilotParamManageControl(param, title, desc, icon, this); + QObject::connect(modelUIToggle, &FrogPilotParamManageControl::manageButtonClicked, this, [this]() { + parentToggleClicked(); + for (auto &[key, toggle] : toggles) { + toggle->setVisible(modelUIKeys.find(key.c_str()) != modelUIKeys.end()); + } + }); + toggle = modelUIToggle; + } else if (param == "LaneLinesWidth" || param == "RoadEdgesWidth") { + toggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 24, std::map(), this, false, " inches"); + } else if (param == "PathEdgeWidth") { + toggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 100, std::map(), this, false, "%"); + } else if (param == "PathWidth") { + toggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 100, std::map(), this, false, " feet", 10); + } else if (param == "QOLVisuals") { FrogPilotParamManageControl *qolToggle = new FrogPilotParamManageControl(param, title, desc, icon, this); QObject::connect(qolToggle, &FrogPilotParamManageControl::manageButtonClicked, this, [this]() { @@ -177,12 +201,34 @@ void FrogPilotVisualsPanel::updateMetric() { if (isMetric != previousIsMetric) { double distanceConversion = isMetric ? INCH_TO_CM : CM_TO_INCH; double speedConversion = isMetric ? FOOT_TO_METER : METER_TO_FOOT; + params.putIntNonBlocking("LaneLinesWidth", std::nearbyint(params.getInt("LaneLinesWidth") * distanceConversion)); + params.putIntNonBlocking("RoadEdgesWidth", std::nearbyint(params.getInt("RoadEdgesWidth") * distanceConversion)); + params.putIntNonBlocking("PathWidth", std::nearbyint(params.getInt("PathWidth") * speedConversion)); } + FrogPilotParamValueControl *laneLinesWidthToggle = static_cast(toggles["LaneLinesWidth"]); + FrogPilotParamValueControl *roadEdgesWidthToggle = static_cast(toggles["RoadEdgesWidth"]); + FrogPilotParamValueControl *pathWidthToggle = static_cast(toggles["PathWidth"]); + if (isMetric) { + laneLinesWidthToggle->setDescription("Customize the lane line width.\n\nDefault matches the Vienna average of 10 centimeters."); + roadEdgesWidthToggle->setDescription("Customize the road edges width.\n\nDefault is 1/2 of the Vienna average lane line width of 10 centimeters."); + + laneLinesWidthToggle->updateControl(0, 60, " centimeters"); + roadEdgesWidthToggle->updateControl(0, 60, " centimeters"); + pathWidthToggle->updateControl(0, 30, " meters", 10); } else { + laneLinesWidthToggle->setDescription("Customize the lane line width.\n\nDefault matches the MUTCD average of 4 inches."); + roadEdgesWidthToggle->setDescription("Customize the road edges width.\n\nDefault is 1/2 of the MUTCD average lane line width of 4 inches."); + + laneLinesWidthToggle->updateControl(0, 24, " inches"); + roadEdgesWidthToggle->updateControl(0, 24, " inches"); + pathWidthToggle->updateControl(0, 100, " feet", 10); } + laneLinesWidthToggle->refresh(); + roadEdgesWidthToggle->refresh(); + previousIsMetric = isMetric; } diff --git a/selfdrive/frogpilot/ui/visual_settings.h b/selfdrive/frogpilot/ui/visual_settings.h index bf4d542..e1541fc 100644 --- a/selfdrive/frogpilot/ui/visual_settings.h +++ b/selfdrive/frogpilot/ui/visual_settings.h @@ -32,7 +32,7 @@ private: std::set customAlertsKeys = {}; std::set customOnroadUIKeys = {"AccelerationPath", "BlindSpotPath"}; std::set customThemeKeys = {"CustomColors", "CustomIcons", "CustomSignals", "CustomSounds"}; - std::set modelUIKeys = {}; + std::set modelUIKeys = {"DynamicPathWidth", "LaneLinesWidth", "PathEdgeWidth", "PathWidth", "RoadEdgesWidth", "UnlimitedLength"}; std::set qolKeys = {"DriveStats"}; std::map toggles; diff --git a/selfdrive/ui/qt/onroad.cc b/selfdrive/ui/qt/onroad.cc index 5c73646..75b3117 100644 --- a/selfdrive/ui/qt/onroad.cc +++ b/selfdrive/ui/qt/onroad.cc @@ -564,6 +564,44 @@ void AnnotatedCameraWidget::drawLaneLines(QPainter &painter, const UIState *s) { painter.setBrush(bg); painter.drawPolygon(scene.track_vertices); + // Create new path with track vertices and track edge vertices + QPainterPath path; + path.addPolygon(scene.track_vertices); + path.addPolygon(scene.track_edge_vertices); + + // Paint path edges + QLinearGradient pe(0, height(), 0, 0); + if (alwaysOnLateralActive) { + pe.setColorAt(0.0, QColor::fromHslF(178 / 360., 0.90, 0.38, 1.0)); + pe.setColorAt(0.5, QColor::fromHslF(178 / 360., 0.90, 0.38, 0.5)); + pe.setColorAt(1.0, QColor::fromHslF(178 / 360., 0.90, 0.38, 0.1)); + } else if (conditionalStatus == 1 || conditionalStatus == 3) { + pe.setColorAt(0.0, QColor::fromHslF(58 / 360., 1.00, 0.50, 1.0)); + pe.setColorAt(0.5, QColor::fromHslF(58 / 360., 1.00, 0.50, 0.5)); + pe.setColorAt(1.0, QColor::fromHslF(58 / 360., 1.00, 0.50, 0.1)); + } else if (experimentalMode) { + pe.setColorAt(0.0, QColor::fromHslF(25 / 360., 0.71, 0.50, 1.0)); + pe.setColorAt(0.5, QColor::fromHslF(25 / 360., 0.71, 0.50, 0.5)); + pe.setColorAt(1.0, QColor::fromHslF(25 / 360., 0.71, 0.50, 0.1)); + } else if (scene.navigate_on_openpilot) { + pe.setColorAt(0.0, QColor::fromHslF(205 / 360., 0.85, 0.56, 1.0)); + pe.setColorAt(0.5, QColor::fromHslF(205 / 360., 0.85, 0.56, 0.5)); + pe.setColorAt(1.0, QColor::fromHslF(205 / 360., 0.85, 0.56, 0.1)); + } else if (customColors != 0) { + const auto &colorMap = std::get<3>(themeConfiguration[customColors]); + for (const auto &[position, brush] : colorMap) { + QColor darkerColor = brush.color().darker(120); + pe.setColorAt(position, darkerColor); + } + } else { + pe.setColorAt(0.0, QColor::fromHslF(148 / 360., 0.94, 0.51, 1.0)); + pe.setColorAt(0.5, QColor::fromHslF(112 / 360., 1.00, 0.68, 0.5)); + pe.setColorAt(1.0, QColor::fromHslF(112 / 360., 1.00, 0.68, 0.1)); + } + + painter.setBrush(pe); + painter.drawPath(path); + // Paint blindspot path if (scene.blind_spot_path) { QLinearGradient bs(0, height(), 0, 0); diff --git a/selfdrive/ui/ui.cc b/selfdrive/ui/ui.cc index da0696e..120750b 100644 --- a/selfdrive/ui/ui.cc +++ b/selfdrive/ui/ui.cc @@ -86,7 +86,8 @@ void update_model(UIState *s, if (plan_position.getX().size() < model.getPosition().getX().size()) { plan_position = model.getPosition(); } - float max_distance = std::clamp(*(plan_position.getX().end() - 1), + float max_distance = scene.unlimited_road_ui_length ? *(plan_position.getX().end() - 1) : + std::clamp(*(plan_position.getX().end() - 1), MIN_DRAW_DISTANCE, MAX_DRAW_DISTANCE); // update lane lines @@ -95,7 +96,7 @@ void update_model(UIState *s, int max_idx = get_path_length_idx(lane_lines[0], max_distance); for (int i = 0; i < std::size(scene.lane_line_vertices); i++) { scene.lane_line_probs[i] = lane_line_probs[i]; - update_line_data(s, lane_lines[i], 0.025 * scene.lane_line_probs[i], 0, &scene.lane_line_vertices[i], max_idx); + update_line_data(s, lane_lines[i], scene.model_ui ? scene.lane_line_width * scene.lane_line_probs[i] : 0.025 * scene.lane_line_probs[i], 0, &scene.lane_line_vertices[i], max_idx); } // update road edges @@ -103,21 +104,33 @@ void update_model(UIState *s, const auto road_edge_stds = model.getRoadEdgeStds(); for (int i = 0; i < std::size(scene.road_edge_vertices); i++) { scene.road_edge_stds[i] = road_edge_stds[i]; - update_line_data(s, road_edges[i], 0.025, 0, &scene.road_edge_vertices[i], max_idx); + update_line_data(s, road_edges[i], scene.model_ui ? scene.road_edge_width : 0.025, 0, &scene.road_edge_vertices[i], max_idx); } // update path + float path; + + if (scene.dynamic_path_width) { + float multiplier = scene.enabled ? 1.0f : scene.always_on_lateral_active ? 0.75f : 0.50f; + path = scene.path_width * multiplier; + } else { + path = scene.path_width; + } + auto lead_one = (*s->sm)["radarState"].getRadarState().getLeadOne(); if (lead_one.getStatus()) { const float lead_d = lead_one.getDRel() * 2.; max_distance = std::clamp((float)(lead_d - fmin(lead_d * 0.35, 10.)), 0.0f, max_distance); } max_idx = get_path_length_idx(plan_position, max_distance); - update_line_data(s, plan_position, 0.9, 1.22, &scene.track_vertices, max_idx, false); + update_line_data(s, plan_position, scene.model_ui ? path * (1 - scene.path_edge_width / 100) : 0.9, 1.22, &scene.track_vertices, max_idx, false); + + // Update path edges + update_line_data(s, plan_position, scene.model_ui ? path : 0, 1.22, &scene.track_edge_vertices, max_idx, false); // Update adjacent paths for (int i = 4; i <= 5; i++) { - update_line_data(s, lane_lines[i], scene.blind_spot_path ? (i == 4 ? scene.lane_width_left : scene.lane_width_right) / 2 : 0, 0, &scene.track_adjacent_vertices[i], max_idx); + update_line_data(s, lane_lines[i], scene.blind_spot_path ? (i == 4 ? scene.lane_width_left : scene.lane_width_right) / 2.0f : 0, 0, &scene.track_adjacent_vertices[i], max_idx); } } @@ -279,6 +292,14 @@ void ui_update_frogpilot_params(UIState *s) { scene.custom_icons = custom_theme ? params.getInt("CustomIcons") : 0; scene.custom_signals = custom_theme ? params.getInt("CustomSignals") : 0; + scene.model_ui = params.getBool("ModelUI"); + scene.dynamic_path_width = scene.model_ui && params.getBool("DynamicPathWidth"); + scene.lane_line_width = params.getInt("LaneLinesWidth") * (scene.is_metric ? 1.0f : INCH_TO_CM) / 200.0f; + scene.path_edge_width = params.getInt("PathEdgeWidth"); + scene.path_width = params.getInt("PathWidth") / 10.0f * (scene.is_metric ? 1.0f : FOOT_TO_METER) / 2.0f; + scene.road_edge_width = params.getInt("RoadEdgesWidth") * (scene.is_metric ? 1.0f : INCH_TO_CM) / 200.0f; + scene.unlimited_road_ui_length = scene.model_ui && params.getBool("UnlimitedLength"); + bool quality_of_life_controls = params.getBool("QOLControls"); bool quality_of_life_visuals = params.getBool("QOLVisuals"); diff --git a/selfdrive/ui/ui.h b/selfdrive/ui/ui.h index b93aa46..bf0eea9 100644 --- a/selfdrive/ui/ui.h +++ b/selfdrive/ui/ui.h @@ -176,13 +176,20 @@ typedef struct UIScene { bool blind_spot_path; bool blind_spot_right; bool conditional_experimental; + bool dynamic_path_width; bool enabled; bool experimental_mode; + bool model_ui; bool turn_signal_left; bool turn_signal_right; + bool unlimited_road_ui_length; + float lane_line_width; float lane_width_left; float lane_width_right; + float path_edge_width; + float path_width; + float road_edge_width; int camera_view; int conditional_speed; @@ -193,6 +200,7 @@ typedef struct UIScene { int custom_signals; QPolygonF track_adjacent_vertices[6]; + QPolygonF track_edge_vertices; } UIScene;