Skip to content

Route page-transition agents around map geometry#102

Merged
SunkenInTime merged 4 commits into
t3code/view-cone-clippingfrom
t3code/agent-page-pathing
Jul 18, 2026
Merged

Route page-transition agents around map geometry#102
SunkenInTime merged 4 commits into
t3code/view-cone-clippingfrom
t3code/agent-page-pathing

Conversation

@SunkenInTime

@SunkenInTime SunkenInTime commented Jul 17, 2026

Copy link
Copy Markdown
Owner

What changed

  • route moved agent markers between strategy pages with bounded A* pathfinding
  • reuse the view-cone collision layers, elevation selection, spatial segment index, and map geometry preload
  • sample every agent path with the existing page-transition easeOutCubic profile so all agents retain the prior animation feel and arrive within the same transition duration
  • keep non-agent page-transition behavior unchanged
  • fall back to direct interpolation when collision geometry is unavailable

Why

Straight-line interpolation lets agents visually run through walls. The stacked view-cone work already provides authoritative, map-aligned collision geometry that can also guide page-transition movement.

Validation

  • focused Flutter analysis passes
  • fvm flutter test --no-pub test/agent_transition_path_test.dart
  • full Flutter suite passed before the easing-only correction (291/291)

Live verification note

No run registry entry exists for E:\Projects\icarus-agent-page-pathing, so there is no running app or DTD/app URI to hot reload. The required global flutter-run-registry skill is also unavailable in this session.

@SunkenInTime
SunkenInTime marked this pull request as ready for review July 17, 2026 00:06

Copy link
Copy Markdown
Owner Author

Implementation is ready on 9a7b129. The full local Flutter suite passes (290/290).

@greptileai please review this stacked diff against t3code/view-cone-clipping.

@greptile-apps

greptile-apps Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This correction commit removes the previously-added reduced-motion branch and aligns agent path sampling with the existing Curves.easeOutCubic progress, so routed agents animate identically to the prior straight-line behavior in timing and feel.

  • agent_path.dart introduces AgentTransitionPath (distance-normalized arc sampling), AgentTransitionPathPlanner (elevation selection via max of start/end, route entry point), and AgentTransitionPathfinder (bounded A* with an octile heuristic, lazy-deletion heap, four-endpoint segment-distance clearance check, and a greedy string-pull smoother). All fallbacks return a two-point direct path so a map geometry failure never breaks navigation.
  • strategy_provider.dart awaits the view-cone geometry before addPostFrameCallback, calls plan() inside the callback, and passes the resulting agentPaths map to the transition notifier. The rest of the changes wire the new field through PageTransitionState and its copyWith/reset paths.
  • page_transition_overlay.dart samples AgentTransitionPath.positionAt(t) at the same eased progress already used for opacity, rotation, and scale lerps; non-agent and non-move entries are unaffected. The bulk of the diff is mechanical Dart formatter reformatting.

Confidence Score: 5/5

Safe to merge — all code paths either produce a correct A*-routed path or degrade gracefully to direct interpolation, and no existing transition behavior is touched.

The clearance check is now symmetric (all four segment endpoints tested via _segmentDistanceSquared), the A* heap is correctly implemented with lazy-deletion, positionAt samples the right segment with proper distance normalization, coordinate spaces are consistent end-to-end, and every early-exit in findPath falls back safely to [start, end]. The head commit removes the reduced-motion branch and routes path sampling through the same eased t as all other lerps — minimal and correct.

No files require special attention. agent_path.dart is the densest file but its invariants are well-covered by the new tests.

Important Files Changed

Filename Overview
lib/page_transition/agent_path.dart New file: implements AgentTransitionPath (distance-normalized sampling), AgentTransitionPathPlanner (elevation selection, route planning entry), and AgentTransitionPathfinder (A* with lazy-deletion heap, four-endpoint segment distance check, and greedy string-pull smoother). Clearance check is now symmetric. The _smooth fallback to anchor+1 is always safe since consecutive grid nodes were validated during A* expansion.
lib/providers/strategy_provider.dart Adds async geometry load before addPostFrameCallback, then calls AgentTransitionPathPlanner.plan() inside the callback and passes agentPaths to transitionNotifier.start(). Error is swallowed cleanly so a failing asset load degrades to direct interpolation without blocking navigation.
lib/providers/transition_provider.dart Adds agentPaths field to PageTransitionState with a const-empty default, threads it through copyWith, and initialises/clears it in all relevant state transitions (prepare, start, complete). No logic changes to existing transition machinery.
lib/widgets/page_transition_overlay.dart Adds a progress parameter to _buildEntry and, in the TransitionKind.move case, samples the stored AgentTransitionPath at the same easeOutCubic progress used for all other lerps. Falls back cleanly to Offset.lerp when no path entry exists. The rest of the diff is mechanical Dart formatter reformatting.
test/agent_transition_path_test.dart New test file: verifies distance-normalized sampling, progress clamping, A* routing around a wall, and that the smoothed path preserves clearance from wall corners. Covers both the happy path and the main correctness invariants of the pathfinder.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant UI as User navigates page
    participant SP as StrategyProvider
    participant GEO as viewConeGeometryProvider
    participant PP as AgentTransitionPathPlanner
    participant TP as TransitionProvider
    participant OL as PageTransitionOverlay

    UI->>SP: setActivePage(pageID)
    SP->>SP: snapshot prev state
    SP->>GEO: await future (async, catches errors)
    GEO-->>SP: VisionGeometryMap or null on error
    SP->>SP: addPostFrameCallback
    note over SP: next frame
    SP->>SP: _snapshotAllPlaced → entries
    SP->>PP: plan(entries, geometry, isAttack, sizes, coordinateSystem)
    PP->>PP: centerFor(startPos) and centerFor(endPos) in world coords
    PP->>PP: elevationFor → max(start, end) elevation
    PP->>PP: findPath(start, end, layer)
    alt direct path walkable
        PP-->>PP: AgentTransitionPath([start, end])
    else "A* search"
        PP->>PP: _nearestConnectableNode x2
        PP->>PP: "A* with lazy-deletion heap"
        PP->>PP: _reconstruct then _smooth path
        PP-->>PP: AgentTransitionPath(smoothed waypoints)
    end
    PP-->>SP: agentPaths map
    SP->>TP: transitionNotifier.start with agentPaths
    TP->>TP: store agentPaths in PageTransitionState

    loop each animation frame
        OL->>OL: "t = easeOutCubic.transform(controller.value)"
        OL->>TP: ref.read agentPaths[entry.id]
        OL->>OL: agentPath.positionAt(t) → world center
        OL->>OL: "pathTopLeft = center minus virtualOffsetToWorld(agentSize/2)"
        OL->>OL: "_overlayScreenPosition(coordinatePosition = pathTopLeft)"
        OL-->>UI: agent rendered at path-sampled screen position
    end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant UI as User navigates page
    participant SP as StrategyProvider
    participant GEO as viewConeGeometryProvider
    participant PP as AgentTransitionPathPlanner
    participant TP as TransitionProvider
    participant OL as PageTransitionOverlay

    UI->>SP: setActivePage(pageID)
    SP->>SP: snapshot prev state
    SP->>GEO: await future (async, catches errors)
    GEO-->>SP: VisionGeometryMap or null on error
    SP->>SP: addPostFrameCallback
    note over SP: next frame
    SP->>SP: _snapshotAllPlaced → entries
    SP->>PP: plan(entries, geometry, isAttack, sizes, coordinateSystem)
    PP->>PP: centerFor(startPos) and centerFor(endPos) in world coords
    PP->>PP: elevationFor → max(start, end) elevation
    PP->>PP: findPath(start, end, layer)
    alt direct path walkable
        PP-->>PP: AgentTransitionPath([start, end])
    else "A* search"
        PP->>PP: _nearestConnectableNode x2
        PP->>PP: "A* with lazy-deletion heap"
        PP->>PP: _reconstruct then _smooth path
        PP-->>PP: AgentTransitionPath(smoothed waypoints)
    end
    PP-->>SP: agentPaths map
    SP->>TP: transitionNotifier.start with agentPaths
    TP->>TP: store agentPaths in PageTransitionState

    loop each animation frame
        OL->>OL: "t = easeOutCubic.transform(controller.value)"
        OL->>TP: ref.read agentPaths[entry.id]
        OL->>OL: agentPath.positionAt(t) → world center
        OL->>OL: "pathTopLeft = center minus virtualOffsetToWorld(agentSize/2)"
        OL->>OL: "_overlayScreenPosition(coordinatePosition = pathTopLeft)"
        OL-->>UI: agent rendered at path-sampled screen position
    end
Loading

Reviews (4): Last reviewed commit: "Match agent transition easing" | Re-trigger Greptile

Comment thread lib/page_transition/agent_path.dart
@greptile-apps

greptile-apps Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR routes agent markers around map geometry during page transitions using a bounded A* pathfinder that reuses the existing view-cone collision layers, elevation selection, and segment index. Non-agent transition behaviour is unchanged, and a direct-lerp fallback is preserved when geometry is unavailable or reduced-motion is enabled.

  • agent_path.dart — new AgentTransitionPath (distance-normalised sampling for constant velocity) and AgentTransitionPathfinder (grid A* with clearance, lazy-deletion heap, and string-pulling smoother) using VisionGeometryLayer directly.
  • page_transition_overlay.dart — adds geometry watching, per-transition path caching keyed by (transitionId, geometry identity, isAttack), and a raw-progress path sampler for move entries; reduce-motion triggers an immediate complete() call.
  • strategy_provider.dart — preloads geometry (awaited, error-swallowed) before the post-frame transition snapshot so the first frame of every transition already has A* paths available.

Confidence Score: 4/5

Safe to merge; all 290 tests pass and non-agent transitions are untouched.

The core pathfinding is correct — the A* heap, lazy deletion, path reconstruction, and string-pulling smoother all behave as expected, and the distance-normalised sampling correctly delivers constant-velocity motion. Two quality gaps exist: the clearance check in _edgeIsWalkable misses wall endpoints close to the interior of a long edge, and _nearestConnectableNode re-scans already-examined inner rings. Neither causes a wall collision or crash.

lib/page_transition/agent_path.dart — specifically _edgeIsWalkable (clearance completeness) and _nearestConnectableNode (scan redundancy).

Important Files Changed

Filename Overview
lib/page_transition/agent_path.dart New file: distance-normalised path type and bounded A* pathfinder over view-cone collision geometry. Logic is sound; one walkability gap in the clearance check (wall endpoints vs. edge interior) and a minor redundancy in the nearest-node search.
lib/widgets/page_transition_overlay.dart Integrates A* paths into the overlay: adds geometry watching, path caching keyed by transition ID + geometry identity, reduce-motion fast-complete, and path-position rendering for move entries. Fallback to straight-line lerp when geometry is unavailable is correctly preserved.
lib/providers/strategy_provider.dart Adds an awaited geometry preload before the post-frame transition snapshot, wrapped in a broad catch so navigation never regresses when assets are missing. Minimal and correct change.
test/agent_transition_path_test.dart New unit tests covering distance sampling, progress clamping, and wall-routing. The wall-routing test constructs a minimal geometry (outer boundary + vertical wall) and asserts the path goes around the wall tip — a good integration-level check.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant SP as StrategyProvider
    participant VCG as viewConeGeometryProvider
    participant PTO as PageTransitionOverlay
    participant APF as AgentTransitionPathfinder

    SP->>VCG: await future (preload geometry)
    VCG-->>SP: VisionGeometryMap (or null)
    SP->>SP: addPostFrameCallback snapshot and start transition

    PTO->>VCG: watch(viewConeGeometryProvider)
    VCG-->>PTO: AsyncData VisionGeometryMap
    PTO->>PTO: _syncAgentPaths(state, geometry, isAttack)
    PTO->>APF: findPath(start, end, layer) per move-entry
    APF-->>PTO: "AgentTransitionPath (A* + string-pull)"

    loop each build frame
        PTO->>PTO: agentPath.positionAt(rawProgress)
        PTO->>PTO: _overlayScreenPosition(pathTopLeft)
    end

    alt reduceMotion
        PTO->>PTO: transitionProvider.complete()
    end
    alt geometry is null
        PTO->>PTO: Offset.lerp(start, end, t) fallback
    end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant SP as StrategyProvider
    participant VCG as viewConeGeometryProvider
    participant PTO as PageTransitionOverlay
    participant APF as AgentTransitionPathfinder

    SP->>VCG: await future (preload geometry)
    VCG-->>SP: VisionGeometryMap (or null)
    SP->>SP: addPostFrameCallback snapshot and start transition

    PTO->>VCG: watch(viewConeGeometryProvider)
    VCG-->>PTO: AsyncData VisionGeometryMap
    PTO->>PTO: _syncAgentPaths(state, geometry, isAttack)
    PTO->>APF: findPath(start, end, layer) per move-entry
    APF-->>PTO: "AgentTransitionPath (A* + string-pull)"

    loop each build frame
        PTO->>PTO: agentPath.positionAt(rawProgress)
        PTO->>PTO: _overlayScreenPosition(pathTopLeft)
    end

    alt reduceMotion
        PTO->>PTO: transitionProvider.complete()
    end
    alt geometry is null
        PTO->>PTO: Offset.lerp(start, end, t) fallback
    end
Loading

Reviews (2): Last reviewed commit: "Route page-transition agents with A-star" | Re-trigger Greptile

Comment thread lib/page_transition/agent_path.dart
Comment on lines +154 to +171
if (distance >= bestDistance ||
!_pointIsWalkable(candidatePoint, layer) ||
!_edgeIsWalkable(point, candidatePoint, layer)) {
continue;
}
best = candidate;
bestDistance = distance;
}
}
if (best != null) return best;
}
return null;
}

bool _pointIsWalkable(Offset point, VisionGeometryLayer layer) {
if (!layer.contains(point)) return false;
if (clearance <= 0) return true;
final nearby = layer.segmentIndex?.queryPoint(point, clearance) ??

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.

P2 Inner rings are re-examined on each radius expansion

The nested loops at radius = r iterate the full square [center±r, center±r], so every cell at radii 0 … r-1 is rechecked. Because _pointIsWalkable calls layer.contains (a winding-number test over all mask segments) and possibly a quadrant-tree query, re-scanning those cells at each successive radius multiplies work that is already done. For typical values (radius up to 3, gridSpacing = 18) the overhead is negligible, but the intent — "search only the new ring when the previous ring found nothing" — would be clearer and more efficient with a ring-only iteration pattern.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Copy link
Copy Markdown
Owner Author

Addressed all current Greptile concerns in 2e1e888:

  • enforce clearance across the full route/wall segment pair, with a wall-corner regression test
  • plan routes during the existing post-frame preparation callback and pass immutable paths into transition state, keeping A* out of overlay build()
  • select the higher inferred/explicit start and destination elevation for conservative collision coverage

Focused analysis is clean and the full Flutter suite passes (291/291).

@greptileai please review the latest head.

Copy link
Copy Markdown
Owner Author

Corrected the transition behavior in a3e34fd:

  • removed the added reduced-motion branch
  • agent path sampling now uses the same existing Curves.easeOutCubic progress as the previous page interpolation

Focused analysis and all agent path tests pass.

@greptileai please review the latest head.

- Delete obsolete skill docs and scripts
- Update agent instructions and lockfile references
@SunkenInTime
SunkenInTime merged commit 2333d1d into t3code/view-cone-clipping Jul 18, 2026
1 check failed
@SunkenInTime
SunkenInTime deleted the t3code/agent-page-pathing branch July 18, 2026 06:59
@SunkenInTime
SunkenInTime restored the t3code/agent-page-pathing branch July 18, 2026 07:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant