Visuals - Custom Themes - Turn Signals
Add themed animation for your turn signals. Want to submit your own turn signal animation? Post it in the 'feature-request' channel in the FrogPilot Discord!
After Width: | Height: | Size: 63 KiB |
After Width: | Height: | Size: 37 KiB |
After Width: | Height: | Size: 76 KiB |
After Width: | Height: | Size: 83 KiB |
After Width: | Height: | Size: 86 KiB |
After Width: | Height: | Size: 64 KiB |
After Width: | Height: | Size: 58 KiB |
After Width: | Height: | Size: 62 KiB |
After Width: | Height: | Size: 55 KiB |
After Width: | Height: | Size: 92 KiB |
After Width: | Height: | Size: 155 KiB |
After Width: | Height: | Size: 150 KiB |
After Width: | Height: | Size: 153 KiB |
After Width: | Height: | Size: 156 KiB |
After Width: | Height: | Size: 156 KiB |
|
@ -77,7 +77,7 @@ void AnnotatedCameraWidget::updateState(const UIState &s) {
|
|||
has_eu_speed_limit = (nav_alive && speed_limit_sign == cereal::NavInstruction::SpeedLimitSign::VIENNA) && !(speedLimitController && !useViennaSLCSign) || (speedLimitController && useViennaSLCSign);
|
||||
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 != 0 && (turnSignalLeft || turnSignalRight));
|
||||
status = s.status;
|
||||
|
||||
// update engageability/experimental mode button
|
||||
|
@ -626,6 +626,11 @@ void AnnotatedCameraWidget::initializeFrogPilotWidgets() {
|
|||
{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))}}}},
|
||||
};
|
||||
|
||||
animationTimer = new QTimer(this);
|
||||
connect(animationTimer, &QTimer::timeout, this, [this] {
|
||||
animationFrameIndex = (animationFrameIndex + 1) % totalFrames;
|
||||
});
|
||||
}
|
||||
|
||||
void AnnotatedCameraWidget::updateFrogPilotWidgets(const UIScene &scene) {
|
||||
|
@ -709,6 +714,39 @@ void AnnotatedCameraWidget::updateFrogPilotWidgets(const UIScene &scene) {
|
|||
useViennaSLCSign = scene.use_vienna_slc_sign;
|
||||
|
||||
trafficModeActive = scene.traffic_mode_active;
|
||||
|
||||
turnSignalLeft = scene.turn_signal_left;
|
||||
turnSignalRight = scene.turn_signal_right;
|
||||
|
||||
if (customSignals != scene.custom_signals) {
|
||||
customSignals = scene.custom_signals;
|
||||
|
||||
QString themePath;
|
||||
|
||||
themePath = QString("../frogpilot/assets/custom_themes/%1/images").arg(
|
||||
themeConfiguration.find(customSignals) != themeConfiguration.end() ?
|
||||
std::get<0>(themeConfiguration[customSignals]) : "");
|
||||
|
||||
const QStringList imagePaths = {
|
||||
themePath + "/turn_signal_1.png",
|
||||
themePath + "/turn_signal_2.png",
|
||||
themePath + "/turn_signal_3.png",
|
||||
themePath + "/turn_signal_4.png"
|
||||
};
|
||||
|
||||
signalImgVector.clear();
|
||||
signalImgVector.reserve(2 * imagePaths.size() + 2);
|
||||
|
||||
for (const QString &imagePath : imagePaths) {
|
||||
QPixmap pixmap(imagePath);
|
||||
signalImgVector.push_back(pixmap);
|
||||
signalImgVector.push_back(pixmap.transformed(QTransform().scale(-1, 1)));
|
||||
}
|
||||
|
||||
const QPixmap blindSpotPixmap(themePath + "/turn_signal_1_red.png");
|
||||
signalImgVector.push_back(blindSpotPixmap);
|
||||
signalImgVector.push_back(blindSpotPixmap.transformed(QTransform().scale(-1, 1)));
|
||||
}
|
||||
}
|
||||
|
||||
void AnnotatedCameraWidget::paintFrogPilotWidgets(QPainter &p) {
|
||||
|
@ -716,6 +754,15 @@ void AnnotatedCameraWidget::paintFrogPilotWidgets(QPainter &p) {
|
|||
drawStatusBar(p);
|
||||
}
|
||||
|
||||
if (customSignals != 0 && (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 && !compass);
|
||||
|
@ -993,3 +1040,28 @@ void AnnotatedCameraWidget::drawStatusBar(QPainter &p) {
|
|||
|
||||
p.restore();
|
||||
}
|
||||
|
||||
void AnnotatedCameraWidget::drawTurnSignals(QPainter &p) {
|
||||
constexpr int signalHeight = 480;
|
||||
constexpr int signalWidth = 360;
|
||||
|
||||
p.setRenderHint(QPainter::Antialiasing);
|
||||
|
||||
int baseYPosition = (height() - signalHeight) / 2 + (showAlwaysOnLateralStatusBar || showConditionalExperimentalStatusBar || roadNameUI ? 225 : 300) - alertSize;
|
||||
int leftSignalXPosition = 75 + width() - signalWidth - 300 * (blindSpotLeft ? 0 : animationFrameIndex);
|
||||
int rightSignalXPosition = -75 + 300 * (blindSpotRight ? 0 : animationFrameIndex);
|
||||
|
||||
if (animationFrameIndex < signalImgVector.size()) {
|
||||
auto drawSignal = [&](bool signalActivated, int xPosition, bool flip, bool blindspot) {
|
||||
if (signalActivated) {
|
||||
int uniqueImages = signalImgVector.size() / 4;
|
||||
int index = (blindspot ? 2 * uniqueImages : 2 * animationFrameIndex % totalFrames) + (flip ? 1 : 0);
|
||||
QPixmap &signal = signalImgVector[index];
|
||||
p.drawPixmap(xPosition, baseYPosition, signalWidth, signalHeight, signal);
|
||||
}
|
||||
};
|
||||
|
||||
drawSignal(turnSignalLeft, leftSignalXPosition, false, blindSpotLeft);
|
||||
drawSignal(turnSignalRight, rightSignalXPosition, true, blindSpotRight);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -94,6 +94,7 @@ private:
|
|||
|
||||
void drawSLCConfirmation(QPainter &p);
|
||||
void drawStatusBar(QPainter &p);
|
||||
void drawTurnSignals(QPainter &p);
|
||||
|
||||
// FrogPilot variables
|
||||
Params paramsMemory{"/dev/shm/params"};
|
||||
|
@ -120,6 +121,8 @@ private:
|
|||
bool speedLimitChanged;
|
||||
bool speedLimitController;
|
||||
bool trafficModeActive;
|
||||
bool turnSignalLeft;
|
||||
bool turnSignalRight;
|
||||
bool useViennaSLCSign;
|
||||
bool vtscControllingCurve;
|
||||
|
||||
|
@ -138,12 +141,19 @@ private:
|
|||
int conditionalSpeedLead;
|
||||
int conditionalStatus;
|
||||
int customColors;
|
||||
int customSignals;
|
||||
int totalFrames = 8;
|
||||
|
||||
QString accelerationUnit;
|
||||
QString leadDistanceUnit;
|
||||
QString leadSpeedUnit;
|
||||
|
||||
size_t animationFrameIndex;
|
||||
|
||||
std::unordered_map<int, std::tuple<QString, QColor, std::map<double, QBrush>>> themeConfiguration;
|
||||
std::vector<QPixmap> signalImgVector;
|
||||
|
||||
QTimer *animationTimer;
|
||||
|
||||
inline QColor blueColor(int alpha = 255) { return QColor(0, 150, 255, alpha); }
|
||||
inline QColor greenColor(int alpha = 242) { return QColor(23, 134, 68, alpha); }
|
||||
|
|
|
@ -224,6 +224,8 @@ static void update_state(UIState *s) {
|
|||
scene.reverse = carState.getGearShifter() == cereal::CarState::GearShifter::REVERSE;
|
||||
scene.standstill = carState.getStandstill() && !scene.reverse;
|
||||
scene.steering_angle_deg = -carState.getSteeringAngleDeg();
|
||||
scene.turn_signal_left = carState.getLeftBlinker();
|
||||
scene.turn_signal_right = carState.getRightBlinker();
|
||||
}
|
||||
if (sm.updated("controlsState")) {
|
||||
auto controlsState = sm["controlsState"].getControlsState();
|
||||
|
@ -318,6 +320,7 @@ void ui_update_frogpilot_params(UIState *s) {
|
|||
bool custom_theme = params.getBool("CustomTheme");
|
||||
scene.custom_colors = custom_theme ? params.getInt("CustomColors") : 0;
|
||||
scene.custom_icons = custom_theme ? params.getInt("CustomIcons") : 0;
|
||||
scene.custom_signals = custom_theme ? params.getInt("CustomSignals") : 0;
|
||||
|
||||
scene.disable_smoothing_mtsc = params.getBool("MTSCEnabled") && params.getBool("DisableMTSCSmoothing");
|
||||
scene.disable_smoothing_vtsc = params.getBool("VisionTurnControl") && params.getBool("DisableVTSCSmoothing");
|
||||
|
|
|
@ -160,6 +160,8 @@ typedef struct UIScene {
|
|||
bool tethering_enabled;
|
||||
bool traffic_mode;
|
||||
bool traffic_mode_active;
|
||||
bool turn_signal_left;
|
||||
bool turn_signal_right;
|
||||
bool use_kaofui_icons;
|
||||
bool use_vienna_slc_sign;
|
||||
bool vtsc_controlling_curve;
|
||||
|
@ -181,6 +183,7 @@ typedef struct UIScene {
|
|||
int conditional_status;
|
||||
int custom_colors;
|
||||
int custom_icons;
|
||||
int custom_signals;
|
||||
int steering_angle_deg;
|
||||
int tethering_config;
|
||||
int wheel_icon;
|
||||
|
|