|
|
|
|
@@ -327,7 +327,7 @@ void AnnotatedCameraWidget::updateState(const UIState &s) {
|
|
|
|
|
has_eu_speed_limit = (nav_alive && speed_limit_sign == cereal::NavInstruction::SpeedLimitSign::VIENNA);
|
|
|
|
|
is_metric = s.scene.is_metric;
|
|
|
|
|
speedUnit = s.scene.is_metric ? tr("km/h") : tr("mph");
|
|
|
|
|
hideBottomIcons = (cs.getAlertSize() != cereal::ControlsState::AlertSize::NONE);
|
|
|
|
|
hideBottomIcons = (cs.getAlertSize() != cereal::ControlsState::AlertSize::NONE || customSignals && (turnSignalLeft || turnSignalRight));
|
|
|
|
|
status = s.status;
|
|
|
|
|
|
|
|
|
|
// update engageability/experimental mode button
|
|
|
|
|
@@ -489,13 +489,21 @@ void AnnotatedCameraWidget::drawLaneLines(QPainter &painter, const UIState *s) {
|
|
|
|
|
|
|
|
|
|
// 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<3>(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<3>(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]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -504,8 +512,14 @@ void AnnotatedCameraWidget::drawLaneLines(QPainter &painter, const UIState *s) {
|
|
|
|
|
if (sm["controlsState"].getControlsState().getExperimentalMode() || 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;
|
|
|
|
|
for (int 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
|
|
|
|
|
@@ -514,20 +528,33 @@ void AnnotatedCameraWidget::drawLaneLines(QPainter &painter, const UIState *s) {
|
|
|
|
|
// Flip so 0 is bottom of frame
|
|
|
|
|
float lin_grad_point = (height() - scene.track_vertices[i].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.2 and 0.2, resort to the theme color
|
|
|
|
|
if (std::abs(acceleration[i]) < 0.2 && (customColors != 0)) {
|
|
|
|
|
const auto &colorMap = std::get<3>(themeConfiguration[customColors]);
|
|
|
|
|
for (const auto &[position, brush] : colorMap) {
|
|
|
|
|
bg.setColorAt(position, brush.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 auto &colorMap = std::get<3>(themeConfiguration[customColors]);
|
|
|
|
|
for (const auto &[position, brush] : colorMap) {
|
|
|
|
|
bg.setColorAt(position, brush.color());
|
|
|
|
|
}
|
|
|
|
|
} 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));
|
|
|
|
|
@@ -601,8 +628,8 @@ void AnnotatedCameraWidget::drawDriverState(QPainter &painter, const UIState *s)
|
|
|
|
|
void AnnotatedCameraWidget::drawLead(QPainter &painter, const cereal::RadarState::LeadData::Reader &lead_data, const QPointF &vd) {
|
|
|
|
|
painter.save();
|
|
|
|
|
|
|
|
|
|
const float speedBuff = 10.;
|
|
|
|
|
const float leadBuff = 40.;
|
|
|
|
|
const float speedBuff = customColors ? 25. : 10.; // Make the center of the chevron appear sooner if a custom theme is active
|
|
|
|
|
const float leadBuff = customColors ? 100. : 40.; // Make the center of the chevron appear sooner if a custom theme is active
|
|
|
|
|
const float d_rel = lead_data.getDRel();
|
|
|
|
|
const float v_rel = lead_data.getVRel();
|
|
|
|
|
|
|
|
|
|
@@ -628,7 +655,11 @@ void AnnotatedCameraWidget::drawLead(QPainter &painter, const cereal::RadarState
|
|
|
|
|
|
|
|
|
|
// 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<3>(themeConfiguration[customColors]).begin()->second);
|
|
|
|
|
} else {
|
|
|
|
|
painter.setBrush(redColor(fillAlpha));
|
|
|
|
|
}
|
|
|
|
|
painter.drawPolygon(chevron, std::size(chevron));
|
|
|
|
|
|
|
|
|
|
painter.restore();
|
|
|
|
|
@@ -751,6 +782,25 @@ void AnnotatedCameraWidget::initializeFrogPilotWidgets() {
|
|
|
|
|
bottom_layout->addWidget(map_settings_btn_bottom);
|
|
|
|
|
|
|
|
|
|
main_layout->addLayout(bottom_layout);
|
|
|
|
|
|
|
|
|
|
// Custom themes configuration
|
|
|
|
|
themeConfiguration = {
|
|
|
|
|
{1, {"frog_theme", 4, 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", 4, 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", 6, 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))}}}}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Initialize the timer for the turn signal animation
|
|
|
|
|
animationTimer = new QTimer(this);
|
|
|
|
|
connect(animationTimer, &QTimer::timeout, this, [this] {
|
|
|
|
|
animationFrameIndex = (animationFrameIndex + 1) % totalFrames;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AnnotatedCameraWidget::updateFrogPilotWidgets(QPainter &p) {
|
|
|
|
|
@@ -767,17 +817,59 @@ void AnnotatedCameraWidget::updateFrogPilotWidgets(QPainter &p) {
|
|
|
|
|
conditionalSpeedLead = scene.conditional_speed_lead;
|
|
|
|
|
conditionalStatus = scene.conditional_status;
|
|
|
|
|
|
|
|
|
|
customColors = scene.custom_colors;
|
|
|
|
|
|
|
|
|
|
experimentalMode = scene.experimental_mode;
|
|
|
|
|
|
|
|
|
|
turnSignalLeft = scene.turn_signal_left;
|
|
|
|
|
turnSignalRight = scene.turn_signal_right;
|
|
|
|
|
|
|
|
|
|
if (alwaysOnLateral || conditionalExperimental) {
|
|
|
|
|
drawStatusBar(p);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (customSignals && (turnSignalLeft || turnSignalRight)) {
|
|
|
|
|
if (!animationTimer->isActive()) {
|
|
|
|
|
animationTimer->start(totalFrames * 11); // 440 milliseconds per loop; syncs up perfectly with my 2019 Lexus ES 350 turn signal clicks
|
|
|
|
|
}
|
|
|
|
|
drawTurnSignals(p);
|
|
|
|
|
} else if (animationTimer->isActive()) {
|
|
|
|
|
animationTimer->stop();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
map_settings_btn_bottom->setEnabled(map_settings_btn->isEnabled());
|
|
|
|
|
if (map_settings_btn_bottom->isEnabled()) {
|
|
|
|
|
map_settings_btn_bottom->setVisible(!hideBottomIcons);
|
|
|
|
|
bottom_layout->setAlignment(map_settings_btn_bottom, rightHandDM ? Qt::AlignLeft : Qt::AlignRight);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Update the turn signal animation images upon toggle change
|
|
|
|
|
if (customSignals != scene.custom_signals) {
|
|
|
|
|
customSignals = scene.custom_signals;
|
|
|
|
|
|
|
|
|
|
QString theme_path = QString("../frogpilot/assets/custom_themes/%1/images").arg(themeConfiguration.find(customSignals) != themeConfiguration.end() ?
|
|
|
|
|
std::get<0>(themeConfiguration[customSignals]) : "");
|
|
|
|
|
|
|
|
|
|
QStringList imagePaths;
|
|
|
|
|
int availableImages = std::get<1>(themeConfiguration[customSignals]);
|
|
|
|
|
|
|
|
|
|
for (int i = 1; i <= totalFrames; ++i) {
|
|
|
|
|
int imageIndex = ((i - 1) % availableImages) + 1;
|
|
|
|
|
QString imagePath = theme_path + QString("/turn_signal_%1.png").arg(imageIndex);
|
|
|
|
|
imagePaths.push_back(imagePath);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
signalImgVector.clear();
|
|
|
|
|
signalImgVector.reserve(2 * imagePaths.size()); // Reserve space for both regular and flipped images
|
|
|
|
|
for (const QString &imagePath : imagePaths) {
|
|
|
|
|
QPixmap pixmap(imagePath);
|
|
|
|
|
signalImgVector.push_back(pixmap); // Regular image
|
|
|
|
|
signalImgVector.push_back(pixmap.transformed(QTransform().scale(-1, 1))); // Flipped image
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
signalImgVector.push_back(QPixmap(theme_path + "/turn_signal_1_red.png")); // Regular blindspot image
|
|
|
|
|
signalImgVector.push_back(QPixmap(theme_path + "/turn_signal_1_red.png").transformed(QTransform().scale(-1, 1))); // Flipped blindspot image
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AnnotatedCameraWidget::drawStatusBar(QPainter &p) {
|
|
|
|
|
@@ -854,3 +946,35 @@ void AnnotatedCameraWidget::drawStatusBar(QPainter &p) {
|
|
|
|
|
|
|
|
|
|
p.restore();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AnnotatedCameraWidget::drawTurnSignals(QPainter &p) {
|
|
|
|
|
// Declare the turn signal size
|
|
|
|
|
constexpr int signalHeight = 480;
|
|
|
|
|
constexpr int signalWidth = 360;
|
|
|
|
|
|
|
|
|
|
// Enable Antialiasing
|
|
|
|
|
p.setRenderHint(QPainter::Antialiasing);
|
|
|
|
|
|
|
|
|
|
// Calculate the vertical position for the turn signals
|
|
|
|
|
int baseYPosition = (height() - signalHeight) / 2 + (alwaysOnLateral || conditionalExperimental || roadNameUI ? 225 : 300);
|
|
|
|
|
// Calculate the x-coordinates for the turn signals
|
|
|
|
|
int leftSignalXPosition = 75 + width() - signalWidth - 300 * (blindSpotLeft ? 0 : animationFrameIndex);
|
|
|
|
|
int rightSignalXPosition = -75 + 300 * (blindSpotRight ? 0 : animationFrameIndex);
|
|
|
|
|
|
|
|
|
|
// Draw the turn signals
|
|
|
|
|
if (animationFrameIndex < signalImgVector.size()) {
|
|
|
|
|
auto drawSignal = [&](bool signalActivated, int xPosition, bool flip, bool blindspot) {
|
|
|
|
|
if (signalActivated) {
|
|
|
|
|
// Get the appropriate image from the signalImgVector
|
|
|
|
|
int uniqueImages = signalImgVector.size() / 4; // Each image has a regular, flipped, and two blindspot versions
|
|
|
|
|
int index = (blindspot ? 2 * uniqueImages : 2 * animationFrameIndex % totalFrames) + (flip ? 1 : 0);
|
|
|
|
|
QPixmap &signal = signalImgVector[index];
|
|
|
|
|
p.drawPixmap(xPosition, baseYPosition, signalWidth, signalHeight, signal);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Display the animation based on which signal is activated
|
|
|
|
|
drawSignal(turnSignalLeft, leftSignalXPosition, false, blindSpotLeft);
|
|
|
|
|
drawSignal(turnSignalRight, rightSignalXPosition, true, blindSpotRight);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|