diff --git a/include/ui/radar_projection.h b/include/ui/radar_projection.h new file mode 100644 index 000000000..4669a21f1 --- /dev/null +++ b/include/ui/radar_projection.h @@ -0,0 +1,20 @@ +#pragma once + +namespace ui::radar { + +constexpr float kKmPerDeg = 111.0f; +constexpr float kDegToRad = 3.14159265f / 180.0f; + +/** Offset of lat/lon from the radar center, in km. */ +void offsetKmFromCenter(float lat, float lon, float* dx_km, float* dy_km, + float* dist_km); + +/** Flat lat/lon as x/y: 1 deg ~ 111 km, north = screen up. */ +void latLonToScreen(float lat, float lon, int* out_x, int* out_y); + +int distSqFromCenter(int x, int y); + +/** Pull (x1,y1) back along the segment from (x0,y0) until inside the ring. */ +void clipPointToOuterRing(int x0, int y0, int* x1, int* y1); + +} // namespace ui::radar diff --git a/src/ui/radar_display.cpp b/src/ui/radar_display.cpp index 3ececd6e8..2a61c9bb5 100644 --- a/src/ui/radar_display.cpp +++ b/src/ui/radar_display.cpp @@ -11,6 +11,7 @@ #include "hardware/display_font.h" #include "services/adsb_client.h" #include "services/radar_location.h" +#include "ui/radar_projection.h" #include "ui/radar_range.h" #include "ui/radar_theme.h" #include "ui/runway_overlay.h" @@ -33,6 +34,11 @@ uint16_t kColorRunwayLabel = 0x7DFF; namespace { +using radar::clipPointToOuterRing; +using radar::distSqFromCenter; +using radar::latLonToScreen; +using radar::offsetKmFromCenter; + bool s_label_metrics_ready = false; bool s_cardinal_use_vlw = false; bool s_scale_use_vlw = false; @@ -197,22 +203,6 @@ void initPalette() { radar::kRunwayLabelB); } -constexpr float kKmPerDeg = 111.0f; -constexpr float kDegToRad = 3.14159265f / 180.0f; - -void offsetKmFromCenter(float lat, float lon, float* dx_km, float* dy_km, - float* dist_km) { - // Longitude degrees shrink toward the poles; scale by cos(latitude) so - // east-west distance isn't overstated away from the equator. - const float center_lat_rad = - static_cast(services::location::lat()) * kDegToRad; - *dx_km = static_cast(lon - services::location::lon()) * kKmPerDeg * - cosf(center_lat_rad); - *dy_km = - static_cast(lat - services::location::lat()) * kKmPerDeg; - *dist_km = sqrtf((*dx_km) * (*dx_km) + (*dy_km) * (*dy_km)); -} - float innerRingMaxKm() { const float outer_km = radar::rangeCurrent().outer_km; return outer_km * (static_cast(radar::kGridOuterRadius - @@ -220,28 +210,8 @@ float innerRingMaxKm() { static_cast(radar::kGridOuterRadius)); } -/** Flat lat/lon as x/y: 1° ≈ 111 km, north = screen up. */ -void latLonToScreen(float lat, float lon, int* out_x, int* out_y) { - const float outer_km = radar::rangeCurrent().outer_km; - const float px_per_km = static_cast(radar::kGridOuterRadius) / outer_km; - - float dx_km = 0.0f; - float dy_km = 0.0f; - float dist_km = 0.0f; - offsetKmFromCenter(lat, lon, &dx_km, &dy_km, &dist_km); - - *out_x = radar::kCenterX + static_cast(lroundf(dx_km * px_per_km)); - *out_y = radar::kCenterY - static_cast(lroundf(dy_km * px_per_km)); -} - bool isInsideOuterRingKm(float dist_km) { return dist_km <= innerRingMaxKm(); } -int distSqFromCenter(int x, int y) { - const int dx = x - radar::kCenterX; - const int dy = y - radar::kCenterY; - return dx * dx + dy * dy; -} - bool isInsideOuterRing(int x, int y) { const int max_r = radar::kGridOuterRadius - radar::kAircraftInsideRingInsetPx; return distSqFromCenter(x, y) <= max_r * max_r; @@ -275,33 +245,6 @@ void drawBeyondRingDot(int x, int y) { radar::kColorAircraft); } -void clipPointToOuterRing(int x0, int y0, int* x1, int* y1) { - const int max_r = radar::kGridOuterRadius; - const int max_r_sq = max_r * max_r; - if (distSqFromCenter(*x1, *y1) <= max_r_sq) { - return; - } - - const int dx = *x1 - x0; - const int dy = *y1 - y0; - float t = 1.0f; - for (int step = 0; step < 20; ++step) { - const int px = x0 + static_cast(lroundf(dx * t)); - const int py = y0 + static_cast(lroundf(dy * t)); - if (distSqFromCenter(px, py) <= max_r_sq) { - *x1 = px; - *y1 = py; - return; - } - t -= 0.05f; - if (t <= 0.0f) { - *x1 = x0; - *y1 = y0; - return; - } - } -} - int speedLineLengthPx(float gs_knots) { if (gs_knots <= 0.0f) { return 0; diff --git a/src/ui/radar_projection.cpp b/src/ui/radar_projection.cpp new file mode 100644 index 000000000..33230abc8 --- /dev/null +++ b/src/ui/radar_projection.cpp @@ -0,0 +1,69 @@ +#include "ui/radar_projection.h" + +#include + +#include "services/radar_location.h" +#include "ui/radar_range.h" +#include "ui/radar_theme.h" + +namespace ui::radar { + +void offsetKmFromCenter(float lat, float lon, float* dx_km, float* dy_km, + float* dist_km) { + // Longitude degrees shrink toward the poles; scale by cos(latitude) so + // east-west distance isn't overstated away from the equator. + const float center_lat_rad = + static_cast(services::location::lat()) * kDegToRad; + *dx_km = static_cast(lon - services::location::lon()) * kKmPerDeg * + cosf(center_lat_rad); + *dy_km = static_cast(lat - services::location::lat()) * kKmPerDeg; + *dist_km = sqrtf((*dx_km) * (*dx_km) + (*dy_km) * (*dy_km)); +} + +void latLonToScreen(float lat, float lon, int* out_x, int* out_y) { + const float outer_km = rangeCurrent().outer_km; + const float px_per_km = static_cast(kGridOuterRadius) / outer_km; + + float dx_km = 0.0f; + float dy_km = 0.0f; + float dist_km = 0.0f; + offsetKmFromCenter(lat, lon, &dx_km, &dy_km, &dist_km); + + *out_x = kCenterX + static_cast(lroundf(dx_km * px_per_km)); + *out_y = kCenterY - static_cast(lroundf(dy_km * px_per_km)); +} + +int distSqFromCenter(int x, int y) { + const int dx = x - kCenterX; + const int dy = y - kCenterY; + return dx * dx + dy * dy; +} + +void clipPointToOuterRing(int x0, int y0, int* x1, int* y1) { + const int max_r = kGridOuterRadius; + const int max_r_sq = max_r * max_r; + if (distSqFromCenter(*x1, *y1) <= max_r_sq) { + return; + } + + const int dx = *x1 - x0; + const int dy = *y1 - y0; + float t = 1.0f; + for (int step = 0; step < 20; ++step) { + const int px = x0 + static_cast(lroundf(dx * t)); + const int py = y0 + static_cast(lroundf(dy * t)); + if (distSqFromCenter(px, py) <= max_r_sq) { + *x1 = px; + *y1 = py; + return; + } + t -= 0.05f; + if (t <= 0.0f) { + *x1 = x0; + *y1 = y0; + return; + } + } +} + +} // namespace ui::radar diff --git a/src/ui/runway_overlay.cpp b/src/ui/runway_overlay.cpp index d32828551..41ed50928 100644 --- a/src/ui/runway_overlay.cpp +++ b/src/ui/runway_overlay.cpp @@ -8,14 +8,18 @@ #include "data/large_airports.h" #include "hardware/display_font.h" #include "services/radar_location.h" +#include "ui/radar_projection.h" #include "ui/radar_range.h" #include "ui/radar_theme.h" namespace ui::runway { namespace { -constexpr float kKmPerDeg = 111.0f; -constexpr float kDegToRad = 3.14159265f / 180.0f; +using radar::clipPointToOuterRing; +using radar::distSqFromCenter; +using radar::latLonToScreen; +using radar::offsetKmFromCenter; + constexpr size_t kMaxAirportLabels = 32; bool s_in_range[data::large_airports::kAirportCount]; @@ -71,66 +75,6 @@ void applyRunwayLabelStyle(lgfx::LGFXBase& gfx) { float e7ToDeg(int32_t e7) { return static_cast(e7) * 1e-7f; } -void offsetKmFromCenter(float lat, float lon, float* dx_km, float* dy_km, - float* dist_km) { - // Longitude degrees shrink toward the poles; scale by cos(latitude) so - // east-west distance isn't overstated away from the equator. - const float center_lat_rad = - static_cast(services::location::lat()) * kDegToRad; - *dx_km = static_cast(lon - services::location::lon()) * kKmPerDeg * - cosf(center_lat_rad); - *dy_km = - static_cast(lat - services::location::lat()) * kKmPerDeg; - *dist_km = sqrtf((*dx_km) * (*dx_km) + (*dy_km) * (*dy_km)); -} - -void latLonToScreen(float lat, float lon, int* out_x, int* out_y) { - const float outer_km = radar::rangeCurrent().outer_km; - const float px_per_km = - static_cast(radar::kGridOuterRadius) / outer_km; - - float dx_km = 0.0f; - float dy_km = 0.0f; - float dist_km = 0.0f; - offsetKmFromCenter(lat, lon, &dx_km, &dy_km, &dist_km); - - *out_x = radar::kCenterX + static_cast(lroundf(dx_km * px_per_km)); - *out_y = radar::kCenterY - static_cast(lroundf(dy_km * px_per_km)); -} - -int distSqFromCenter(int x, int y) { - const int dx = x - radar::kCenterX; - const int dy = y - radar::kCenterY; - return dx * dx + dy * dy; -} - -void clipPointToOuterRing(int x0, int y0, int* x1, int* y1) { - const int max_r = radar::kGridOuterRadius; - const int max_r_sq = max_r * max_r; - if (distSqFromCenter(*x1, *y1) <= max_r_sq) { - return; - } - - const int dx = *x1 - x0; - const int dy = *y1 - y0; - float t = 1.0f; - for (int step = 0; step < 20; ++step) { - const int px = x0 + static_cast(lroundf(dx * t)); - const int py = y0 + static_cast(lroundf(dy * t)); - if (distSqFromCenter(px, py) <= max_r_sq) { - *x1 = px; - *y1 = py; - return; - } - t -= 0.05f; - if (t <= 0.0f) { - *x1 = x0; - *y1 = y0; - return; - } - } -} - bool segmentIntersectsDisc(int x0, int y0, int x1, int y1) { const int cx = radar::kCenterX; const int cy = radar::kCenterY;