mirror of
https://protopirate.net/ProtoPirate/ProtoPirate.git
synced 2026-08-05 09:49:22 +00:00
Add animated radar to receiver waiting screen
Replaces the static waiting screen in the receiver view with an animated radar visualization featuring expanding waves, a rotating sweep line, and pulsing effects. Also refactors and animates the RSSI indicator, updates status bar layout, and adds an activity pulse when receiving. Removes the old static RSSI drawing function and introduces an animation frame counter to support the new visuals.
This commit is contained in:
Vendored
BIN
Binary file not shown.
+145
-30
@@ -4,6 +4,7 @@
|
||||
#include <input/input.h>
|
||||
#include <gui/elements.h>
|
||||
#include <furi.h>
|
||||
#include <math.h>
|
||||
|
||||
#define FRAME_HEIGHT 12
|
||||
#define MAX_LEN_PX 112
|
||||
@@ -34,6 +35,7 @@ typedef struct {
|
||||
bool external_radio;
|
||||
ProtoPirateLock lock;
|
||||
uint8_t lock_count;
|
||||
uint8_t animation_frame;
|
||||
} ProtoPirateReceiverModel;
|
||||
|
||||
void protopirate_view_receiver_set_rssi(ProtoPirateReceiver* receiver, float rssi) {
|
||||
@@ -133,27 +135,14 @@ static void protopirate_view_receiver_draw_frame(Canvas* canvas, uint16_t idx, b
|
||||
canvas_draw_dot(canvas, scrollbar ? 121 : 126, (0 + idx * FRAME_HEIGHT) + 11);
|
||||
}
|
||||
|
||||
static void protopirate_view_receiver_draw_rssi(Canvas* canvas, ProtoPirateReceiverModel* model) {
|
||||
uint8_t x = 58;
|
||||
uint8_t y = 51;
|
||||
float rssi = model->rssi;
|
||||
|
||||
if(rssi >= -60.0f) {
|
||||
canvas_draw_box(canvas, x + 8, y + 2, 4, 5);
|
||||
}
|
||||
if(rssi >= -70.0f) {
|
||||
canvas_draw_box(canvas, x + 4, y + 4, 4, 3);
|
||||
}
|
||||
if(rssi >= -80.0f) {
|
||||
canvas_draw_box(canvas, x, y + 6, 4, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void protopirate_view_receiver_draw(Canvas* canvas, ProtoPirateReceiverModel* model) {
|
||||
canvas_clear(canvas);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
|
||||
// Increment animation frame
|
||||
model->animation_frame = (model->animation_frame + 1) % 96;
|
||||
|
||||
size_t item_count = ProtoPirateReceiverMenuItemArray_size(model->history_item_arr);
|
||||
bool scrollbar = item_count > MENU_ITEMS;
|
||||
|
||||
@@ -161,6 +150,7 @@ void protopirate_view_receiver_draw(Canvas* canvas, ProtoPirateReceiverModel* mo
|
||||
str_buff = furi_string_alloc();
|
||||
|
||||
if(item_count > 0) {
|
||||
// Draw received items list
|
||||
size_t shift_position = model->list_offset;
|
||||
|
||||
for(size_t i = 0; i < MIN(item_count, MENU_ITEMS); i++) {
|
||||
@@ -184,34 +174,158 @@ void protopirate_view_receiver_draw(Canvas* canvas, ProtoPirateReceiverModel* mo
|
||||
elements_scrollbar_pos(canvas, 128, 0, 49, shift_position, item_count);
|
||||
}
|
||||
} else {
|
||||
canvas_draw_str_aligned(canvas, 64, 14, AlignCenter, AlignCenter, "ProtoPirate");
|
||||
canvas_draw_str_aligned(canvas, 64, 26, AlignCenter, AlignCenter, "Waiting for signal...");
|
||||
canvas_draw_str_aligned(canvas, 64, 38, AlignCenter, AlignCenter, "<- to Config");
|
||||
// Cool animated radar with expanding dots
|
||||
int center_x = 64;
|
||||
int center_y = 22;
|
||||
|
||||
// Three waves of expanding circles with different speeds
|
||||
for(int wave = 0; wave < 3; wave++) {
|
||||
// Calculate radius for this wave with offset
|
||||
int base_radius = ((model->animation_frame + wave * 32) % 96) / 3;
|
||||
|
||||
if(base_radius < 28) {
|
||||
// Calculate fade based on distance from center
|
||||
int dot_density = 24 - (base_radius / 2);
|
||||
|
||||
// Draw circle with dots
|
||||
for(int angle = 0; angle < 360; angle += (360 / dot_density)) {
|
||||
float rad = (angle + wave * 15) * 3.14159 / 180.0;
|
||||
int x = center_x + base_radius * cosf(rad);
|
||||
int y = center_y + base_radius * sinf(rad);
|
||||
|
||||
// Only draw if within bounds and create fade effect
|
||||
if(x > 0 && x < 128 && y > 0 && y < 48) {
|
||||
// Dots get smaller/fade as they expand
|
||||
if(base_radius < 10) {
|
||||
canvas_draw_dot(canvas, x, y);
|
||||
// Double dot for inner circles for brightness
|
||||
if(base_radius < 5) {
|
||||
canvas_draw_dot(canvas, x+1, y);
|
||||
}
|
||||
} else if(base_radius < 20) {
|
||||
// Skip some dots for fade effect
|
||||
if(angle % 30 == 0) {
|
||||
canvas_draw_dot(canvas, x, y);
|
||||
}
|
||||
} else {
|
||||
// Very sparse dots at edge
|
||||
if(angle % 60 == 0) {
|
||||
canvas_draw_dot(canvas, x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Static guide circles (very faint)
|
||||
for(int angle = 0; angle < 360; angle += 45) {
|
||||
float rad = angle * 3.14159 / 180.0;
|
||||
canvas_draw_dot(canvas, center_x + 15 * cosf(rad), center_y + 15 * sinf(rad));
|
||||
}
|
||||
|
||||
// Rotating sweep line with glow effect
|
||||
float sweep_angle = (model->animation_frame * 3.75) * 3.14159 / 180.0;
|
||||
|
||||
// Main sweep line
|
||||
int sweep_x = center_x + 22 * cosf(sweep_angle);
|
||||
int sweep_y = center_y + 22 * sinf(sweep_angle);
|
||||
canvas_draw_line(canvas, center_x, center_y, sweep_x, sweep_y);
|
||||
|
||||
// Sweep "glow" - additional lines at slight offsets
|
||||
float glow_angle1 = sweep_angle - 0.05;
|
||||
float glow_angle2 = sweep_angle + 0.05;
|
||||
canvas_draw_line(canvas, center_x, center_y,
|
||||
center_x + 20 * cosf(glow_angle1),
|
||||
center_y + 20 * sinf(glow_angle1));
|
||||
canvas_draw_line(canvas, center_x, center_y,
|
||||
center_x + 20 * cosf(glow_angle2),
|
||||
center_y + 20 * sinf(glow_angle2));
|
||||
|
||||
// Sweep trail (fading dots)
|
||||
for(int i = 1; i <= 12; i++) {
|
||||
float trail_angle = sweep_angle - (i * 0.15);
|
||||
int trail_radius = 22 - i;
|
||||
if(trail_radius > 0) {
|
||||
int trail_x = center_x + trail_radius * cosf(trail_angle);
|
||||
int trail_y = center_y + trail_radius * sinf(trail_angle);
|
||||
// Only draw every other dot in trail for fade effect
|
||||
if(i % 2 == 0 || i < 4) {
|
||||
canvas_draw_dot(canvas, trail_x, trail_y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pulsing center
|
||||
int pulse = (model->animation_frame % 32);
|
||||
if(pulse < 16) {
|
||||
canvas_draw_disc(canvas, center_x, center_y, 2);
|
||||
} else {
|
||||
canvas_draw_circle(canvas, center_x, center_y, 2);
|
||||
}
|
||||
if(pulse < 8 || (pulse > 16 && pulse < 24)) {
|
||||
canvas_draw_dot(canvas, center_x, center_y);
|
||||
}
|
||||
|
||||
// Left-aligned config hint
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str(canvas, 2, 45, "< Config");
|
||||
}
|
||||
|
||||
furi_string_free(str_buff);
|
||||
|
||||
// Status bar separator
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_draw_line(canvas, 0, 48, 127, 48);
|
||||
|
||||
// Draw status bar
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str(canvas, 2, 58, furi_string_get_cstr(model->frequency_str));
|
||||
canvas_draw_str(canvas, 40, 58, furi_string_get_cstr(model->preset_str));
|
||||
|
||||
// Activity indicator - pulsing when receiving
|
||||
if(model->rssi > -90) {
|
||||
int pulse = model->animation_frame % 16;
|
||||
if(pulse < 8) {
|
||||
canvas_draw_disc(canvas, 2, 54, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Frequency
|
||||
canvas_draw_str(canvas, 5, 58, furi_string_get_cstr(model->frequency_str));
|
||||
|
||||
// Preset
|
||||
canvas_draw_str(canvas, 44, 58, furi_string_get_cstr(model->preset_str));
|
||||
|
||||
// History counter
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 110, 58, AlignCenter, AlignBottom, furi_string_get_cstr(model->history_stat_str));
|
||||
canvas, 108, 58, AlignCenter, AlignBottom, furi_string_get_cstr(model->history_stat_str));
|
||||
|
||||
// Draw RSSI
|
||||
protopirate_view_receiver_draw_rssi(canvas, model);
|
||||
// Draw RSSI indicator with animation
|
||||
uint8_t x = 70;
|
||||
uint8_t y = 51;
|
||||
float rssi = model->rssi;
|
||||
|
||||
// Draw external radio indicator if needed
|
||||
if(model->external_radio) {
|
||||
canvas_draw_str(canvas, 100, 58, "Ext");
|
||||
if(rssi >= -80.0f) {
|
||||
canvas_draw_box(canvas, x, y + 5, 3, 2);
|
||||
}
|
||||
if(rssi >= -70.0f) {
|
||||
canvas_draw_box(canvas, x + 4, y + 3, 3, 4);
|
||||
}
|
||||
if(rssi >= -60.0f) {
|
||||
canvas_draw_box(canvas, x + 8, y + 1, 3, 6);
|
||||
// Pulse effect for strong signal
|
||||
if(model->animation_frame % 24 < 12) {
|
||||
canvas_draw_frame(canvas, x + 7, y, 5, 8);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw lock indicator
|
||||
// External radio indicator
|
||||
if(model->external_radio) {
|
||||
canvas_draw_str(canvas, 116, 58, "E");
|
||||
}
|
||||
|
||||
// Lock indicator
|
||||
if(model->lock == ProtoPirateLockOn) {
|
||||
canvas_draw_str(canvas, 119, 58, "L");
|
||||
canvas_draw_str(canvas, 122, 58, "L");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -367,6 +481,7 @@ ProtoPirateReceiver* protopirate_view_receiver_alloc() {
|
||||
model->external_radio = false;
|
||||
model->lock = ProtoPirateLockOff;
|
||||
model->lock_count = 0;
|
||||
model->animation_frame = 0;
|
||||
},
|
||||
true);
|
||||
|
||||
@@ -424,4 +539,4 @@ void protopirate_view_receiver_set_idx_menu(ProtoPirateReceiver* receiver, uint1
|
||||
},
|
||||
true);
|
||||
protopirate_view_receiver_update_offset(receiver);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user