Commit Graph
5 Commits
Author SHA1 Message Date
Constantin LeueandClaude Sonnet 5 dc8e6f9d6e Add location search, home/fit map buttons, and slim OSM attribution
- TopNavBar: Nominatim geocoding search field plus Home (center on first
  waypoint) and Fit (zoom to mission bounds) buttons (HTML prototype:
  #searchWrap/#homeBtn/#fitBtn). Both show a SnackBar instead of doing
  nothing when there are no waypoints yet.
- Renders as its own bar just below TopModeBar rather than fused into the
  same pill: TopModeBar lives in AppShell, a layer above PlanScreen, which
  owns the MapController these actions actually need. Fusing them would
  need lifting the MapController to shared state - reasonable follow-up
  if pixel-fidelity with the prototype's single bar matters later, but
  not necessary for the functionality itself.
- Replaced SimpleAttributionWidget with a compact custom attribution: OSM's
  tile usage policy requires visible attribution to stay, so instead of
  removing it (as first asked) I shrank it and dropped the "flutter_map |"
  prefix per the user's follow-up choice, so it reads cleanly against the
  now-opaque bottom bar instead of looking like a second banner.

Added 5 tests (nav bar renders, home/fit SnackBars with no waypoints,
empty search is a no-op). All 26 tests and flutter analyze pass. Verified
on the Pixel_10a emulator: searched "Berlin" and confirmed the map flew
there, dropped a waypoint, panned far away, and confirmed Home re-centers
and snaps onto it exactly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-28 09:06:16 +02:00
Constantin LeueandClaude Sonnet 5 b93bbab23b Add fillet flight path geometry and Alt/Speed wheels
- RouteGeometry (lib/domain/mission/route_geometry.dart): physically
  grounded flight path - straight segments + tangential arcs at each
  course change, sized from the drone's minimum turn radius (doc 3.7/4.6),
  replacing a naive spline that would suggest unrealistic turn radii.
  Marks a vertex "bad" (turn angle >= 160°, tangent length exceeding 90%
  of either adjacent leg, or exceeding the waypoint's catch radius) and a
  leg "bad" when the required climb/descent rate exceeds the drone's
  max climb/descent rate. Pure Dart, no Flutter dependency, so it stays
  usable if the mission domain is ever split into its own package (4.22).
  Wind-based turn-radius correction from the prototype isn't ported yet -
  the wind system (doc 3.8) doesn't exist in the Flutter app.
- MissionMap now draws each RouteSegment as its own Polyline, colored red
  when bad instead of a single plain white line; waypoint markers turn red
  too when their vertex is bad.
- ValueWheel: vertical drag-to-adjust tape control (HTML prototype's
  #altWheel/#spdWheel), custom-painted tick marks, blue center indicator,
  gradient fade at top/bottom. Wired into PlanScreen on both screen edges
  with Alt/Speed readouts.
- curAlt/curSpeed now live in PlanScreen state instead of fixed constants:
  dropping a new waypoint uses whatever the wheels are currently set to;
  entering editing mode on an existing waypoint loads its values into the
  wheels; adjusting a wheel while editing writes live into that waypoint
  (provider gained setAltitude/setSpeed to match the existing
  moveWaypoint/toggleAction pattern).

Added dedicated unit tests for the geometry (empty list, straight line,
feasible 90° turn producing a line-arc-line segment sequence, an
infeasible near-180° turn, and an infeasible descent rate) rather than
relying on eyeballing it on the emulator - this is exactly the kind of
ported-math correctness that's hard to verify visually but easy to get
subtly wrong. All 11 tests (previous 6 + these 5) and flutter analyze
pass. Also manually verified the wheels on the Pixel_10a emulator: drag
changes the value and the on-screen readout in real time.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-27 23:13:24 +02:00
Constantin LeueandClaude Sonnet 5 6dcb765b80 Add waypoint route line, snap-to-edit, and halo action menu
Full reticle interaction state machine (idle/snapped/editing), matching
the HTML prototype's mode variable:

- idle: reticle shows "+", tap drops a new waypoint at the map center.
- snapped: after any pan ends, if the map center lands within the
  reticle's on-screen radius (converted to meters at the current
  zoom/latitude) of an existing waypoint, the view auto-recenters onto it
  and the reticle switches to a pencil icon. Tap to enter editing.
- editing: reticle shows a checkmark; panning the map now live-updates the
  target waypoint's position (drag-to-reposition) instead of moving the
  camera "away" from it, and the HaloMenu radial menu appears.

HaloMenu: three ring-wedge hit regions (custom ClipPath/CustomClipper,
angles/radii taken from the prototype's buildRingWedgePath) for
Loiter/Landing/Remove, with Material icons standing in for the
demonstrator's hand-drawn SVGs. Loiter/Landing toggle the waypoint's
action and drop back to snapped; Remove deletes it and returns to idle.

MissionMap now draws a straight white polyline connecting all waypoints
in order (the physically-grounded straight-line + fillet-arc geometry
from doc 3.7/4.6 is still open - this is a straight-line placeholder) plus
a dashed rubber-band preview from the last waypoint to the live map center
while idle.

Note on porting from Leaflet: flutter_map's MapController.move() emits
MapEventMove, not MapEventMoveEnd, so (unlike the prototype's panTo()) the
auto-recenter-on-snap doesn't re-trigger snap detection - no "autoPanning"
guard flag was needed.

Verified with flutter analyze, flutter test (three new tests: halo menu
open/close, remove, loiter toggle - the wedge tap tests had to target the
icon's exact center via tapAt(), since a wedge's bounding-box center falls
outside its actual donut-segment hit area), and a full manual pass on the
Pixel_10a emulator: idle -> add -> snap (auto-recenter confirmed) -> edit
-> drag-reposition (confirmed via re-snap after further panning showing
the moved position, not the original one) -> halo remove -> back to idle,
plus the connecting line rendering correctly between two waypoints set
far apart.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-27 22:56:06 +02:00
Constantin LeueandClaude Sonnet 5 db6b46140c Add center reticle control for dropping waypoints
- ReticleButton: fixed-at-screen-center "+" circle (HTML prototype:
  #centerCluster/#reticleBtn), tapping it drops a waypoint at the current
  map center.
- CurrentMissionNotifier (Riverpod): holds the waypoint list for the
  mission being edited. Reactive single-value state per doc 4.16, so
  Riverpod rather than a new Bloc/Cubit.
- MissionMap now renders each waypoint as a CircleLayer marker (white
  border, dark fill, matching the prototype's L.circleMarker styling).
- PlanScreen owns the MapController needed to read the camera center on
  tap, and wires the reticle to the provider.

Default altitude/speed/catch-radius (60m / 15 m/s / 60m) are hardcoded to
the T1 Ranger default profile values from the prototype for now - TODO
once drone profile settings (doc 3.5) exist.

Verified with flutter analyze, flutter test (incl. a new test covering the
add-waypoint path), and a real run on the Pixel_10a emulator: panned the
map and dropped two waypoints, both markers stayed correctly georeferenced.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-27 22:21:35 +02:00
Constantin LeueandClaude Sonnet 5 02dd1bc11a Lock app to landscape fullscreen and add first OSM map
- Lock orientation to landscape (sensorLandscape in the manifest,
  SystemChrome.setPreferredOrientations in main.dart) and enable immersive
  fullscreen, matching the prototype's full-bleed map UI (doc 7.7).
- Add MissionMap widget: flutter_map + standard OSM raster tiles, same
  default center/zoom as the HTML prototype (Amsterdam, zoom 16), OSM
  attribution bottom-left. PlanScreen now renders it full-screen instead of
  the placeholder text.
- Add the release-manifest INTERNET permission (previously only present in
  the debug manifest, which would have silently broken tile loading and any
  future network calls in release builds).
- Update the smoke test to check for the FlutterMap widget instead of the
  removed placeholder text.

Verified with flutter analyze, flutter test, and a real run on the
Pixel_10a emulator (fullscreen landscape confirmed, tiles loading).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-27 07:27:15 +02:00