Skip to content

feat: 3D dive views - single-dive, tissue, career terrain, spatial seascape#565

Merged
ericgriffin merged 65 commits into
mainfrom
worktree-dive-3d-view
Jul 12, 2026
Merged

feat: 3D dive views - single-dive, tissue, career terrain, spatial seascape#565
ericgriffin merged 65 commits into
mainfrom
worktree-dive-3d-view

Conversation

@ericgriffin

@ericgriffin ericgriffin commented Jul 11, 2026

Copy link
Copy Markdown
Member

Summary

A 3D visualization platform for dive data, rendered entirely with Flutter's own Canvas.drawVertices (no external 3D engine). One scene-agnostic Scene3d (layers + markers + bounds + scrub path) flows through a single CustomPainter renderer; every scene is just a list of mesh layers plus a cursor path.

Four scenes

  1. Single-dive — the depth-time ribbon with metric coloring, temperature strata, deco ceiling surface, unit-aware grid, and event/photo/gas-switch markers. Explore + time-scrub, embedded preview card on dive detail.
  2. Tissue saturation — a 16-compartment inert-gas "landscape" across a repetitive-dive chain (auto day-chain, surface intervals seam-compressed), replayed through the existing golden-vector-tested BuhlmannAlgorithm. %M-value coloring with the controlling-compartment ridge; helium-split toggle for trimix. Dive/Tissues switcher on the 3D page.
  3. Career terrain — many dives stacked along Z as parallel depth ribbons under a shared time/depth scale, colored by recency or max depth. Reachable from the dive site detail page.
  4. Spatial seascape (Suunto-style) — the swim path dead-reckoned from per-sample compass heading (Add per-sample compass heading capture (DC_SAMPLE_BEARING) #563) + swim-speed estimate, anchored at the entry GPS fix and rubber-banded to the exit fix, threaded through a synthesized seafloor heightmap, viewable above and below a translucent waterline. Honest by construction: "estimated path" and "synthesized seafloor" captions always shown.

Architecture

  • Scene3d generalization: painter/viewport/card are scene-agnostic; SceneBounds carries explicit scene Y and Z ranges so the projector fits any scene's box; ScrubPath carries an optional 3D (Z) cursor track.
  • Every scene's geometry is a pure, isolate-friendly *GeometryService.build() -> Scene3d, unit-tested without GL. The interactive viewport is fully widget-tested (orbit, zoom, reset, marker hit-test, scrub).
  • Dead reckoning, tissue replay, terrain synthesis, chain derivation — all pure services with focused tests.

l10n

34 dive3d keys translated across all 11 locales.

Tests

~130 dive_3d feature tests; the broader suite (dive_log UI, dive_sites, deco, l10n — 1594 tests in the slice run locally) stays green.

Platform status

Pure Dart + Flutter's built-in renderer — no native plugin surface. CI validates all five platform builds.

Remaining / follow-ups

  • Interactive device walkthrough (macOS/iOS/Android).
  • Real bathymetry ingestion (the spatial terrain is explicitly a synthesized placeholder).
  • Photo-viewer integration from dive-scene markers; playback speed toggle; axis text labels.

Design specs: docs/superpowers/specs/2026-07-11-{dive-3d-view,tissue-saturation-3d,career-terrain-3d,spatial-site-3d}-design.md.

@ericgriffin
ericgriffin force-pushed the worktree-dive-3d-view branch from 19e1e9b to 2322d2f Compare July 11, 2026 14:57
@ericgriffin
ericgriffin marked this pull request as ready for review July 11, 2026 15:30
three_js_core + three_js_angle_renderer + three_js_math at 0.3.x/0.0.1.
The umbrella three_js package and three_js_controls are excluded: their
loader chain requires archive ^4 and xml ^7, which conflict with the
repo's excel and DiveCloud zip pins. Orbit controls are hand-rolled
camera math instead (spherical coordinates around a target).

Spike verified on macOS: ANGLE texture creation, vertex-colored indexed
BufferGeometry rendering, and per-frame animation loop all work.
…nter viewport

The software projector/painter pipeline is promoted to the one renderer:
gestures drive the orthographic camera, the scrub cursor moves in a
foregroundPainter keyed to the scrub listenable, and marker taps hit-test
against the same projection. Deletes three_js_core/three_js_math/
three_js_angle_renderer, the flutter_angle fork override, the Windows
longpaths and Android CMake CI workarounds, the libc++_shared pickFirst,
the GL spike, and the GL-failure fallback path (nothing can fail to init).
The viewport is now fully widget-testable; the coverage exclusion is gone
with it.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 116 out of 117 changed files in this pull request and generated 4 comments.

Comment thread lib/features/dive_3d/presentation/renderer/scene_projector.dart Outdated
Comment thread lib/features/dive_3d/presentation/renderer/scene_projector.dart
Comment thread lib/features/dive_3d/presentation/widgets/time_scrub_bar.dart
Comment thread lib/features/dive_3d/application/compare_providers.dart
- SceneProjector: orbit around the scene's actual Y center (bounds.sceneMin/MaxY)
  so custom-Y scenes (tissue) no longer pivot/drift wrong
- TimeScrubBar: pause playback on scrub start so the slider stops fighting the
  animation controller (onScrubStart wired in all three players)
- diveComparisonProfilesProvider: language-neutral '#<diveNumber>' fallback label
  instead of a hard-coded English 'Dive'
Copilot AI review requested due to automatic review settings July 12, 2026 19:57

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 116 out of 117 changed files in this pull request and generated 1 comment.

The title was never rendered; the standalone page shows its title via AppBar
and the Computers mode is labeled by the scene switcher. Removed the param and
its call sites/test args.
Copilot AI review requested due to automatic review settings July 12, 2026 20:12

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 116 out of 117 changed files in this pull request and generated 2 comments.

Comment thread lib/features/dive_3d/presentation/widgets/time_scrub_bar.dart
Comment thread lib/features/dive_3d/application/compare_providers.dart Outdated
…ooltip

- DiveIdSet defensively copies to an unmodifiable list so a later caller
  mutation can't change ==/hashCode and break provider caching (drops const)
- TimeScrubBar play/pause button gets a localized tooltip (dive3d_play/pause,
  translated across all locales) for discoverability + screen readers
Copilot AI review requested due to automatic review settings July 12, 2026 20:39

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 116 out of 117 changed files in this pull request and generated 1 comment.

Comment thread lib/features/dive_3d/presentation/widgets/scene_readout_panel.dart
Copilot AI review requested due to automatic review settings July 12, 2026 20:46
Resolved dive_detail_page.dart: kept the 3D-card removal (single-statement
profile section) and took main's gauge-dive section filter + comment.
Regenerated codegen (mocks/drift) for main's importProfile signature change.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 116 out of 116 changed files in this pull request and generated 1 comment.

Comment thread lib/features/dive_3d/domain/career/career_geometry_service.dart Outdated
Copilot AI review requested due to automatic review settings July 12, 2026 20:58
…bPath)

Delegating to CompareGeometryService gave career a ScrubPath (ridden on the
reference profile), so the viewport drew a stray cursor dot on the static,
timeline-less career scene. Strip it; keep layers/markers/bounds.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 116 out of 116 changed files in this pull request and generated no new comments.

Copilot AI review requested due to automatic review settings July 12, 2026 21:03

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 116 out of 116 changed files in this pull request and generated no new comments.

@ericgriffin ericgriffin changed the title feat: 3D dive views — single-dive, tissue, career terrain, spatial seascape feat: 3D dive views - single-dive, tissue, career terrain, spatial seascape Jul 12, 2026
@ericgriffin
ericgriffin merged commit d32031e into main Jul 12, 2026
20 checks passed
@ericgriffin
ericgriffin deleted the worktree-dive-3d-view branch July 12, 2026 22:19
@github-project-automation github-project-automation Bot moved this from In review to Done in Submersion Release Tracker Jul 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants