Route Smoothing

"/home/yossef/notes/git/projects/maplibra/modules/Route Smoothing.md"

path: maplibra/modules/Route Smoothing.md

- **fileName**: Route Smoothing
- **Created on**: 2026-06-16 00:48:42

Route Smoothing

Purpose: How raw A* paths are transformed into human-like walking routes.

Related: ../Home.md, ./Pathfinding.md, ../flows/Wall Detection.md, ../decisions/Post-A-Star Staircase Straightening.md, ../decisions/Safe Direct Route Smoothing.md, ../decisions/Elastic Route Smoothing.md

How The Pipeline Works (End-to-End)

When a user drops a pin and requests a route, this is what happens:

  1. A returns graph node IDs* — e.g. ["L1:42", "L1:43", "L1:44", ...]
  2. route-features.js looks up coordinates for each node → [[lng, lat], [lng, lat], ...]
  3. removeDuplicateCoords strips exact duplicates (can happen at door transitions)
  4. straightenVisibleRoute finds farthest direct walkable shortcut across open areas (Stage -1)
  5. relaxVisibleRoute pulls remaining interior points toward safe straight chords when direct shortcutting is blocked
  6. straightenWalkableStaircase tries to skip shallow grid staircases by taking direct walkable shortcuts
  7. straightenCorridorZigzag projects sharp back-and-forth patterns onto straight lines
  8. simplifyRouteSegment removes redundant points that barely deviate from a straight line
  9. smoothRouteSegment greedily skips intermediate nodes when a direct segment is walkable and saves distance
  10. curveRouteSegment replaces sharp corner points with cubic Bezier curve segments
  11. smoothRouteHuman applies per-corner Chaikin rounding where each segment validates walkable

Each stage only replaces the working path if the entire new path passes isRouteSegmentWalkable(). If validation fails, the stage is skipped and the previous path is kept. This means safety is the default — a stage only "wins" when it produces a strictly valid, smoother path.

Pipeline

Node IDs → Coords lookup → Visible direct smoothing → Elastic relaxation → Staircase straightening → Corridor zigzag straightening → Simplification → Shortcut smoothing → Bezier curving → Enhanced smoothing

Stage -1: Visible Direct Smoothing (straightenVisibleRoute)

Finds the farthest later route point that can be reached by a fully validated straight segment. This is the main pass that makes open-area routes look direct like Mappedin while still blocking wall crossings.

Safety rules:

Stage 0: Elastic Route Relaxation (relaxVisibleRoute)

Pulls remaining interior points toward the chord between protected anchors after direct visible shortcuts fail. Each moved point is accepted only when both adjacent segments pass full isSegmentWalkable() validation, and the pipeline still validates the whole candidate route with isRouteSegmentWalkable().

Safety rules:

Stage 1: Walkable Staircase Straightening (straightenWalkableStaircase)

Added to reduce screenshot-like shallow grid staircases after A* but before the older smoothing passes.

This is a conservative post-A* cleanup. It does not change graph generation, WASM routing, binary graph layout, or worker route search.

Stage 1a: Aggressive Straightening (aggressiveWalkableStraightener)

Attempts to remove remaining intermediate points by finding the farthest walkable jump from each current anchor. It runs on the current working route, not the original raw graph route, so it cannot reintroduce zigzags already reduced by visible or elastic smoothing. The pipeline only accepts this stage when it removes points and the full candidate route remains walkable.

Stage 2: Corridor Zigzag Straightening (straightenCorridorZigzag)

Projects sharp zigzag clusters onto the entry-to-exit line when every projected segment remains walkable.

This catches sharper back-and-forth patterns, while straightenWalkableStaircase catches shallower grid staircases.

Stage 2b: Local Zigzag Cleanup (removeZigzagPoints)

removeZigzagPoints() detects short dogleg windows after A* and before final curve smoothing. It only removes intermediate points when the direct shortcut stays walkable and the pattern has enough turn/deviation evidence to be a zigzag rather than a real corridor corner.

Safety rules:

Stage 3: Douglas-Peucker Simplification (simplifyRouteSegment)

Removes points that do not deviate enough from the straight line between neighbors. Uses projected meter-space distance with configurable tolerance.

Stage 4: Greedy Shortcut (smoothRouteSegment)

Iterates from anchor to end, tries to skip intermediate nodes:

Stage 5: Bezier Curving (curveRouteSegment)

Rounds sharp corners with cubic Bezier curves:

Stage 6: Enhanced Smoothing (smoothRouteHuman)

Optional Chaikin/Catmull-Rom smoothing:

Walkability Validation

Every smoothing shortcut and curve is validated by isSegmentWalkable:

Blocker System

Blockers are geometry features buffered outward:

Indexed via SpatialBlockerIndex (grid cells ~5.5m). Point-in-polygon check against buffered features.

Configuration Parameters

All values live as properties on the controller context (e.g. pathfindingController). Set them before calling buildRouteFeatures().

Property Default Range What It Controls
smoothRoute true boolean Master switch — disables the entire pipeline when false
smoothRouteMode 'simplify' 'none', 'stringPull', 'chaikin', 'catmullRom', 'simplify' Which smoothing family to use
smoothVisibleRoute enabled unless false boolean Enables full-validation direct route smoothing
smoothElasticRoute enabled unless false boolean Enables elastic post-visibility route relaxation
smoothAggressiveStraightener enabled unless false boolean Enables longest-walkable-jump straightening stage
smoothWalkableStaircase enabled unless false boolean Enables shallow staircase straightening stage
smoothCorridorZigzag enabled unless false boolean Enables corridor zigzag projection stage
smoothTolerance 0.6 0+ meters Douglas-Peucker simplification threshold
smoothShortcutMinMeters 0.05 0+ meters Greedy shortcut: minimum saved distance to accept a skip
smoothShortcutMinRatio 0.01 0–0.9 Greedy shortcut: minimum saved distance ratio to accept a skip
smoothCurveEnabled true boolean Master switch for Bezier curving
smoothCurveMinTurnDegrees 3 1–120° Only curve corners sharper than this
smoothCurveMaxDeviationMeters 2.5 0.1+ meters Hard cap on curve radius
smoothCurveFactor 0.49 0.1–0.6 Curve radius as fraction of the shorter adjacent segment
smoothCurveMinMeters 0.05 0.03+ meters Smallest curve radius that will be applied
smoothCurveSamples 24 4+ Bezier subdivision count — higher = smoother visual curve
smoothCurveOnTouch true boolean Allows Bezier route curving on touch devices unless explicitly disabled
isTouchMode auto-detected boolean Used with smoothCurveOnTouch for mobile curve gating
smoothWallClearance 0.25 0+ meters Runtime wall buffer used during smoothing
smoothObjectClearance 0.2 0+ meters Runtime object buffer used during smoothing
smoothNonwalkableClearance 0.2 0+ meters Runtime nonwalkable buffer used during smoothing

Tuning Guide

Path looks too jagged / too many sharp corners:

Path looks too straight / crosses through narrow gaps it shouldn't:

Path takes unnecessary detours instead of direct lines:

Mobile performance is poor:

Important Files