mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-10-09 11:16:39 +00:00
143 lines
5.5 KiB
C++
143 lines
5.5 KiB
C++
#pragma once
|
|
// Home > Compass -- ui-new's Tools > Compass. The course over ground from GPS
|
|
// movement (no magnetometer; the Core's CourseEngine, trail or not) on a dial
|
|
// that turns so the heading is up under a fixed pointer, with the degrees and
|
|
// cardinal beside it. Standing still, the heading is undefined: the dial greys
|
|
// out and a hint says to move.
|
|
//
|
|
// Single-TU fragment: included by ui-lvgl/UITask.cpp after DiagScreen.h.
|
|
|
|
namespace compassview {
|
|
|
|
static lv_obj_t* s_dial = nullptr;
|
|
static lv_obj_t* s_deg = nullptr;
|
|
static lv_obj_t* s_degsign = nullptr;
|
|
static lv_obj_t* s_card = nullptr;
|
|
static lv_obj_t* s_hint = nullptr;
|
|
static int s_cog = 0;
|
|
static bool s_have = false;
|
|
static const int DIAL = 176;
|
|
|
|
} // namespace compassview
|
|
|
|
static void onOpenCompass(lv_event_t* e) { (void)e; s_ui->showCompass(); }
|
|
|
|
// The dial, drawn over its round background: ticks every 10 degrees (longer
|
|
// every 30, longest at the cardinals), N / E / S / W and the 30-degree
|
|
// numbers, all turned by the heading; the fixed pointer at the top.
|
|
static void onCompassDraw(lv_event_t* e) {
|
|
using namespace compassview;
|
|
lv_obj_t* o = (lv_obj_t*)lv_event_get_target(e);
|
|
lv_layer_t* layer = lv_event_get_layer(e);
|
|
lv_area_t a;
|
|
lv_obj_get_coords(o, &a);
|
|
float cx = (a.x1 + a.x2) / 2.0f, cy = (a.y1 + a.y2) / 2.0f;
|
|
float r = DIAL / 2.0f - 14; // the ring; the pointer sits outside it
|
|
uint32_t ink = s_have ? theme::TEXT : theme::TEXT_MUTED;
|
|
static const char* LBL[12] = { "N", "30", "60", "E", "120", "150", "S", "210", "240", "W", "300", "330" };
|
|
|
|
lv_draw_line_dsc_t ld;
|
|
lv_draw_line_dsc_init(&ld);
|
|
lv_draw_label_dsc_t td;
|
|
lv_draw_label_dsc_init(&td);
|
|
td.align = LV_TEXT_ALIGN_CENTER;
|
|
for (int deg = 0; deg < 360; deg += 10) {
|
|
float ang = (deg - s_cog) * (float)M_PI / 180.0f;
|
|
float sx = sinf(ang), sy = -cosf(ang);
|
|
bool card = deg % 90 == 0, major = deg % 30 == 0;
|
|
float len = card ? 12 : major ? 9 : 5;
|
|
ld.width = card ? 3 : major ? 2 : 1;
|
|
ld.color = lv_color_hex(ink);
|
|
ld.p1.x = cx + sx * r; ld.p1.y = cy + sy * r;
|
|
ld.p2.x = cx + sx * (r - len); ld.p2.y = cy + sy * (r - len);
|
|
lv_draw_line(layer, &ld);
|
|
if (!major) continue;
|
|
td.text = LBL[deg / 30];
|
|
td.font = card ? THEME_FONT_LARGE : THEME_FONT_SMALL;
|
|
td.color = lv_color_hex(deg == 0 && s_have ? theme::FAIL : card ? ink : theme::TEXT_MUTED);
|
|
float lr = r - (card ? 26 : 22);
|
|
int32_t h = lv_font_get_line_height(td.font);
|
|
int32_t px = (int32_t)(cx + sx * lr), py = (int32_t)(cy + sy * lr);
|
|
lv_area_t la = { px - 20, py - h / 2, px + 20, py + h / 2 };
|
|
lv_draw_label(layer, &td, &la);
|
|
}
|
|
|
|
// Fixed pointer: points down at the ring, the heading always under it.
|
|
lv_draw_triangle_dsc_t tri;
|
|
lv_draw_triangle_dsc_init(&tri);
|
|
tri.bg_color = lv_color_hex(theme::ACCENT);
|
|
tri.bg_opa = LV_OPA_COVER;
|
|
tri.p[0].x = cx - 8; tri.p[0].y = cy - r - 12;
|
|
tri.p[1].x = cx + 8; tri.p[1].y = cy - r - 12;
|
|
tri.p[2].x = cx; tri.p[2].y = cy - r + 2;
|
|
lv_draw_triangle(layer, &tri);
|
|
}
|
|
|
|
void UITask::showCompass() {
|
|
_screen = SCR_COMPASS;
|
|
buildCompass();
|
|
}
|
|
|
|
void UITask::buildCompass() {
|
|
using namespace compassview;
|
|
lv_obj_t* body = newScreen("Compass", true);
|
|
lv_obj_set_flex_flow(body, LV_FLEX_FLOW_ROW);
|
|
lv_obj_set_flex_align(body, LV_FLEX_ALIGN_SPACE_EVENLY, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
|
|
lv_obj_remove_flag(body, LV_OBJ_FLAG_SCROLLABLE);
|
|
|
|
s_dial = lv_obj_create(body);
|
|
styleSurface(s_dial, theme::BG);
|
|
lv_obj_remove_flag(s_dial, LV_OBJ_FLAG_SCROLLABLE);
|
|
lv_obj_set_size(s_dial, DIAL, DIAL);
|
|
lv_obj_set_style_radius(s_dial, LV_RADIUS_CIRCLE, 0);
|
|
lv_obj_add_event_cb(s_dial, onCompassDraw, LV_EVENT_DRAW_MAIN_END, NULL);
|
|
|
|
lv_obj_t* col = lv_obj_create(body);
|
|
lv_obj_remove_style_all(col);
|
|
lv_obj_set_size(col, 110, LV_SIZE_CONTENT);
|
|
lv_obj_set_flex_flow(col, LV_FLEX_FLOW_COLUMN);
|
|
lv_obj_set_flex_align(col, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
|
|
lv_obj_set_style_pad_row(col, 2, 0);
|
|
lv_obj_t* num = lv_obj_create(col); // "044" + a raised degree sign (ui_font_40 is digits only)
|
|
lv_obj_remove_style_all(num);
|
|
lv_obj_set_size(num, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
|
|
lv_obj_set_flex_flow(num, LV_FLEX_FLOW_ROW);
|
|
s_deg = label(num, "", THEME_FONT_CLOCK, theme::TEXT);
|
|
s_degsign = label(num, "", THEME_FONT_LARGE, theme::TEXT);
|
|
s_card = label(col, "", THEME_FONT_LARGE, theme::ACCENT);
|
|
s_hint = label(col, "", THEME_FONT_SMALL, theme::TEXT_MUTED);
|
|
lv_label_set_long_mode(s_hint, LV_LABEL_LONG_WRAP);
|
|
lv_obj_set_width(s_hint, LV_PCT(100));
|
|
lv_obj_set_style_text_align(s_hint, LV_TEXT_ALIGN_CENTER, 0);
|
|
refreshCompass();
|
|
}
|
|
|
|
// From loop(), once a second.
|
|
void UITask::refreshCompass() {
|
|
using namespace compassview;
|
|
if (_screen != SCR_COMPASS || !s_dial) return;
|
|
int32_t lat, lon;
|
|
int cog = 0;
|
|
const char* hint = nullptr;
|
|
if (!_core->course.currentLocation(lat, lon))
|
|
hint = _core->gpsAvailable() && !_core->gpsEnabled() ? "GPS is off - turn it on in Settings" : "Waiting for a GPS fix";
|
|
else if (!_core->course.currentCourse(cog)) hint = "Move to set the heading";
|
|
bool have = hint == nullptr;
|
|
if (have != s_have || cog != s_cog) {
|
|
s_have = have;
|
|
if (have) s_cog = cog;
|
|
lv_obj_invalidate(s_dial);
|
|
}
|
|
if (have) {
|
|
lv_label_set_text_fmt(s_deg, "%03d", cog);
|
|
lv_label_set_text(s_degsign, "\xC2\xB0");
|
|
lv_label_set_text(s_card, geo::bearingCardinal(cog));
|
|
lv_label_set_text(s_hint, "Course over ground");
|
|
} else {
|
|
lv_label_set_text(s_deg, "");
|
|
lv_label_set_text(s_degsign, "");
|
|
lv_label_set_text(s_card, "--");
|
|
lv_label_set_text(s_hint, hint);
|
|
}
|
|
}
|