Skip to content

feat(core): Add pitch, bearing, and cursor-position zoom to GlobeView#10207

Open
charlieforward9 wants to merge 1 commit intovisgl:masterfrom
NEW-HEAT:feat/globe-pitch-bearing
Open

feat(core): Add pitch, bearing, and cursor-position zoom to GlobeView#10207
charlieforward9 wants to merge 1 commit intovisgl:masterfrom
NEW-HEAT:feat/globe-pitch-bearing

Conversation

@charlieforward9
Copy link
Copy Markdown
Collaborator

@charlieforward9 charlieforward9 commented Apr 13, 2026

Summary

Adds pitch (tilt) and bearing (rotation) support to GlobeView, along with cursor-position zoom — closing the interaction gap between GlobeView and MapView / Google Earth.

Before: GlobeView was locked to a top-down view. No camera tilt, no rotation, scroll-wheel zoom always targeted screen center.
After: Right-click drag tilts, two-finger rotate turns, scroll-wheel zoom follows the cursor. Existing code with pitch=0, bearing=0 (the default) produces bit-identical output.

Globe.pitch.bear.zoom.mov

What changed

1. Pitch & Bearing (GlobeViewport, GlobeView, GlobeController)

The view matrix uses a lookAt-based approach that composes four transformations:

lookAt(eye=[0, -altitude, 0], up=[0,0,1])   // camera along -Y
  → rotateX(-pitch)                          // tilt away from nadir
  → rotateY(-bearing)                        // rotate around surface normal
  → rotateX(lat) · rotateZ(-lng) · scale     // globe orientation (unchanged)

Because the Viewport base class subtracts center before applying this matrix, the target lat/lng always projects to screen center regardless of pitch/bearing — no special-casing needed.

Component Change
GlobeViewport pitch, bearing constructor options; rewritten view matrix; far-plane adjustment (farZ / cos(pitch)) to prevent clipping at steep angles
GlobeView pitch, bearing, minPitch (0), maxPitch (60) view state props
GlobeController Enables dragRotate / touchRotate; pitch clamping, bearing normalization, transition interpolation for new props

2. Cursor-Position Zoom (GlobeState.zoom)

GlobeState.zoom() now unprojects the cursor to get its lng/lat, computes the new zoom level, then calls panByPosition in a new anchor mode (no startPixel) to keep that coordinate under the cursor. Falls back to center zoom when the cursor is off the globe surface.

This matches the existing MapView scroll-zoom behavior and makes the globe feel dramatically more responsive at high zoom levels.


Backward Compatibility

  • Default pitch=0, bearing=0 produces identical projections to the current implementation (tested)
  • No new dependencies
  • GlobeView API is additive only — no breaking changes

Tests

  • Projection: pitch, bearing, and combined pitch+bearing all keep center at screen center
  • Bearing direction parity: at bearing=90, North is left and East is up — cross-checked against WebMercatorViewport to ensure convention matches
  • Constraints: bearing normalization (270 → -90), pitch clamping to maxPitch, shortestPathFrom bearing wrapping through ±180
  • Backward compat: explicit pitch=0, bearing=0 produces identical pixelProjectionMatrix as omitting them

Docs

  • globe-view.md: added bearing, pitch, maxPitch, minPitch to view state; removed "No support for rotation" limitation
  • globe-controller.md: updated dragRotate / touchRotate defaults to true

Test Plan

  • yarn test node — all viewport, view-state, and controller tests pass
  • test/apps/globe — interactive demo with pitch: 30, right-click drag to rotate, scroll to zoom toward cursor
  • Visual regression review (screenshot diff)

@coveralls
Copy link
Copy Markdown

coveralls commented Apr 13, 2026

Coverage Status

coverage: 81.605% (+0.02%) from 81.588% — NEW-HEAT:feat/globe-pitch-bearing into visgl:master

Add camera tilt (pitch) and rotation (bearing) support to GlobeView,
matching Google Maps/Earth interaction model. Uses a lookAt-based view
matrix that correctly composes pitch and bearing rotations while keeping
the target (lat/lng) at screen center.

Also makes scroll-wheel zoom target the mouse cursor position instead of
always zooming to screen center (matching MapView behavior), and fixes
unproject ray-sphere intersection for rays that miss the globe surface.

Changes:
- GlobeViewport: lookAt view matrix with pitch/bearing, cursor-anchored zoom
- GlobeController: dragRotate/touchRotate, pitch/bearing constraints
- GlobeView: pitch/bearing/minPitch/maxPitch view state props
- Tests for pitch/bearing transitions and viewport calculations

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@charlieforward9 charlieforward9 force-pushed the feat/globe-pitch-bearing branch from 51f8f62 to 39baee9 Compare April 16, 2026 17:04
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.

Tracker: GlobeView graduation

2 participants