Skip to content

feat(map): AR mesh view - #7659

Draft
jamesarich wants to merge 14 commits into
mainfrom
feat/mesh-view
Draft

jamesarich wants to merge 14 commits into
mainfrom
feat/mesh-view

Conversation

@jamesarich

@jamesarich jamesarich commented Oct 17, 2026 •

Copy link
Copy Markdown
Collaborator

Finding a node on foot means flipping between the map and the compass. This adds a camera mesh view: point the phone at the horizon and see nodes, waypoints, traceroutes and tracks where they actually are, with how sure each position is.

🌟 New Features

  • AR mesh view (feature/map/.../hud), opened from the map's top bar, a node's position section and compass sheet, the node list's long-press menu, a traceroute's map, and the position log. The node list menu also gains Open Compass.
  • Nodes as their chips and waypoints as their emoji, placed by bearing and elevation over a CameraX preview. Elevation compares altitudes in one frame (ellipsoid, then sea level, then the node's geoid separation).
  • Precision as a fading zone from both positions' accuracy and the heading error, following the map's precision circle setting; the phone's own fix is averaged while still and shown live; targets too close to point at go on a heading-up radar, which also draws the open route or track.
  • Off-screen targets pin to the edge you'd turn or tilt toward. A focused target gets a "Finding" label, a reticle, a turn hint, a haptic tick per step closer and a pulse on lock.
  • Traceroutes as lifted arcs, forward up and return down, weighted and labeled by link SNR, with dashed skips over unpositioned hops. Tracks as breadcrumbs fading with age. Geofences as walls on the ground.
  • Opened for a purpose it shows only that: a node from its detail, compass or the node list, a node and its track from the position log, a traceroute's destination and hops from its map. The map's entry shows the whole mesh, with the map's filter sheet and a 50 km / 30 mi range toggle.
  • Heading on google comes from the Fused Orientation Provider, with its live heading error; fdroid and phones without Play services use the rotation vector, then the geomagnetic rotation vector on phones with no gyroscope.
  • First-run coaching; a camera that fails to open says so and the view keeps working as a compass; location warnings carry their permission card or a settings button; a reminder to look around while walking; the screen may sleep again after ten minutes untouched; a hot phone slows its sensors and, when critical, releases the camera.
  • TalkBack hears each chip's name, distance and clock position, nearest straight ahead first; labels sit on scrims dark enough for 4.5:1 over a bright sky; stale positions say how old they are.
  • A waypoint's dialog hands Edit off to the map.

🛠️ Refactoring & Architecture

  • Node map filter rules move from feature:map to core:model (NodeMapFilter); MapNodePolicy delegates to it.
  • PhoneLocationProvider and MagneticFieldProvider move from feature:node to core:repository, their Android implementations to core:data.
  • The compass shares the mesh view's position uncertainty and fix time helpers (core:model PositionPrecision).
  • rememberMeshHudSupported() in core:ui; MapRoute.MeshHud route; NodesRoute.NodeDetail(openCompass); MapPrefs.meshHudCoachSeen.
  • FusedOrientationSource bound per flavor, so AndroidCameraPoseProvider stays the one pose provider.
  • The mesh view uses center_focus_strong; view_in_ar stays with the 3D model viewer in feat(node): view a device's 3D model, and place it in AR on the google flavor #7639.

Deferred: the ARCore Geospatial API (Cloud project, privacy text, camera frames to Google, no F-Droid), adding FUSED_PROVIDER to the phone's location request until it is measured against the compass jitter fix, and a "Find" action on the map, which has no node info window to hold one.

Testing Performed

  • New: HudGeometryTest, HudTargetsTest, HudPathsTest, HudRadarTest, HudEdgesTest, HudChipsTest, FixAveragerTest, OneEuroQuaternionFilterTest, MeshHudViewModelTest, DeviceAttitudeTest, MeshHudBackStackTest; MapRoute.MeshHud round trip in NavigationConfigTest.
  • Baseline with -PwarningsAsErrors=true (spotless, detekt, detektTypeResolved, assembleDebug, test, allTests, kmpSmokeCompile).
  • On a Pixel 9 Pro, indoors: chips, compass tape, edge arrows, radar and live precision; a remote node's traceroute and position log resolve their hops and points. Not yet checked on device: outdoor alignment, the Fused Orientation Provider heading against the compass, traceroute arcs in the camera view, geofence walls, TalkBack.

@coderabbitai

coderabbitai Bot commented Oct 17, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added desktop Desktop target enhancement New feature or request labels Oct 17, 2026
The map's node filters become a plain NodeMapFilter predicate over Node, so a view outside feature:map can apply the
same rules. MapNodePolicy delegates to it, and MapFilterPrefs converts to it directly.
…to core

The compass's phone location and declination providers become core:repository interfaces with their Android
implementations in core:data, so a view outside feature:node can read the phone's fix and true north the same way.
Point the back camera at the horizon and see the mesh in place: nodes as their chips and waypoints as their emoji,
by bearing and elevation, over a CameraX preview. It opens from the map for everything in range, from node detail and
the compass for one node, from a traceroute's map to draw that route in the air, and from the position log to draw
that node's track.

The pose is the rotation vector's camera axes, smoothed with a 1€ filter, and the field of view comes from the lens
calibration and the stream crop. Elevation compares altitudes in one frame. Precision draws as a fading zone from both
positions' accuracy and the heading error. The phone's own fix is averaged while it is still and shown live, and
targets too close to point at go on a heading-up radar. Off-screen targets pin to the edge to turn or tilt toward, a
focused target gets a reticle, a turn hint and a haptic lock, and dragging the view corrects the compass. Traceroutes
draw as lifted arcs, forward up and return down, weighted by each link's SNR; tracks as breadcrumbs fading with age;
geofences as walls on the ground. It follows the map's filters and a range toggle.

The compass shares the mesh view's position uncertainty and fix time rules.
From node detail or the compass the view shows that node alone; from the position log, that node and its track; from
a traceroute, its destination and hops. Waypoints, geofences, other nodes and the range toggle stay out. Opened from
the map it still shows everything the filters allow.
A scoped view whose nodes all sit inside their own uncertainty showed only radar dots, so a traceroute or track looked
empty. The radar now draws both in plan view, and a node with no live position takes the one its route or track
recorded. The view no longer reports an empty mesh before its first update.

Also: the return leg's stem from the viewer, scoped views ignore the range limit, AltitudeConverterCompat for sea
level altitude, the geofence foot line clipped at the near plane, and an "Open waypoint" click label.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

desktop Desktop target enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant