Skip to main content

Module precision_invariants

Module precision_invariants 

Source
Expand description

§Meter-Based Precision Invariants

This module documents the precision invariants that Rustial maintains across the engine and both renderer paths (WGPU and Bevy). These invariants are validated by tests in support_contract and by headless regression tests in the renderer crates.

§1. Internal units are meters

All internal linear distances, world-space coordinates, and elevation values in the engine are expressed in meters.

TypeUnit
WorldCoordmeters (east, north, up)
GeoCoord::altmeters above WGS-84 ellipsoid
ElevationGrid samplesmeters
Camera::distance()meters
Camera::meters_per_pixel()meters per screen pixel
Tile bounds (tile_bounds_world)meters
Geodesic distances (geodesic_distance)meters

§2. f64 engine, f32 GPU

The engine performs all math in f64. GPU-bound data is cast to f32 only at upload time. This ensures centimetre-scale precision even at world-edge coordinates (x ~ 20 million metres).

§3. Camera-relative rendering

Both renderers use camera-relative vertex positions to avoid catastrophic f32 precision loss at large world coordinates:

GPU vertex position = world_position - camera_target_world()

Because the subtraction happens in f64 before the cast to f32, the resulting f32 values are small (within a few kilometres of the origin) and retain sub-centimetre precision.

§Why this is necessary

At longitude ~180 degrees, the absolute Mercator X coordinate is approximately 20 million metres. A 32-bit float at that magnitude has an epsilon of approximately 2 metres – sub-metre detail would be lost. Camera-relative rendering eliminates this problem.

§What is subtracted

RendererOrigin
WGPUMapState::scene_world_origin() (= camera.target_world())
BevySame – the Bevy camera is at eye_offset(), tiles at (world - origin)

§4. Projection round-trip guarantees

For stable (v1.0) projections, the following round-trip invariant holds at all valid inputs:

let world = projection.project(&geo);
let back  = projection.unproject(&world);
assert!((back.lat - geo.lat).abs() < 1e-8);
assert!((back.lon - geo.lon).abs() < 1e-8);
ProjectionLatitude accuracyLongitude accuracyAltitude
Web Mercator< 1e-8 deg< 1e-8 degpassthrough (exact)
Equirectangular< 1e-8 deg< 1e-8 degpassthrough (exact)
Globe< 1e-6 deg< 1e-6 deg< 1 m
Vertical Perspective< 1e-5 deg< 1e-5 degbest-effort

§5. Anti-meridian handling

  • GeoCoord::clamped_mercator() wraps longitude to [-180, 180] via modular arithmetic.
  • +180 deg and -180 deg project to the same world X (validated by test).
  • Geodesic distance across the dateline follows the short path (e.g. 179 deg E to 179 deg W is approximately 220 km, not 39 800 km).

§6. High-latitude behaviour

  • Web Mercator is valid only within approximately +/-85.051 129 deg latitude.
  • project_clamped saturates latitude to this limit.
  • project_checked returns None outside the limit.
  • Scale factor diverges as sec(lat): at 85 deg it is approximately 11.5.
  • Equirectangular handles poles (+/-90 deg) without singularity.

§7. Large-world numeric stability

  • f64 arithmetic preserves centimetre-scale deltas at the Mercator extent boundary (approximately 20 million metres).
  • Camera-relative f32 vertices preserve sub-centimetre precision for geometry within approximately 100 km of the camera target.
  • At zoom 14 (approximately 10 m per tile pixel), on-screen tile corners have camera-relative f32 error < 1 cm (validated by test).

§8. Terrain elevation

  • ElevationGrid stores heights in f32 metres. The full Earth elevation range (-11 034 m to +8 848 m) survives the f32 cast with < 1 cm error.
  • Bilinear interpolation produces correct intermediate values.
  • Altitude (GeoCoord::alt) passes through projections unchanged.
  • TerrainConfig::vertical_exaggeration scales elevation in the GPU shader, not in the engine grid, preserving raw accuracy.

§9. Scale factor

  • Web Mercator: sec(lat) – unity at the equator, 2.0 at 60 deg.
  • Equirectangular: sec(lat) along X, 1.0 along Y.
  • Camera::meters_per_pixel() uses the scale factor to convert screen resolution to ground resolution.

§10. Tile coordinate contract

  • geo_to_tile / tile_to_geo round-trip at all zoom levels.
  • Adjacent tiles share edges with < 1 mm world-space gap.
  • tile_bounds_world at zoom 0 spans the full Mercator world size.
  • TileId::quadkey() / TileId::from_quadkey() round-trip.
  • Parent / child relationships are consistent.

§11. Renderer parity contract

Both WGPU and Bevy renderers consume the same engine outputs:

Engine outputConsumed by
camera.target_world()Scene origin subtraction
camera.eye_offset()Camera placement
camera.view_matrix(DVec3::ZERO)View matrix (camera-relative)
camera.projection_matrix()Projection matrix
tile_bounds_world + UV mappingTile quad vertices
ElevationGridTerrain GPU texture (R32Float)
VectorMeshData positionsVector vertex buffers
ModelInstance position + altitudeModel transform buffers

Because both renderers apply camera-relative subtraction in f64 before the f32 cast, the precision guarantees are identical.

§Meter-Based Precision Invariants

This module documents the precision invariants that Rustial maintains across the engine and both renderer paths (WGPU and Bevy). These invariants are validated by tests in support_contract and by headless regression tests in the renderer crates.

§1. Internal units are meters

All internal linear distances, world-space coordinates, and elevation values in the engine are expressed in meters.

TypeUnit
WorldCoordmeters (east, north, up)
GeoCoord::altmeters above WGS-84 ellipsoid
ElevationGrid samplesmeters
Camera::distance()meters
Camera::meters_per_pixel()meters per screen pixel
Tile bounds (tile_bounds_world)meters
Geodesic distances (geodesic_distance)meters

§2. f64 engine, f32 GPU

The engine performs all math in f64. GPU-bound data is cast to f32 only at upload time. This ensures centimetre-scale precision even at world-edge coordinates (x ~ 20 million metres).

§3. Camera-relative rendering

Both renderers use camera-relative vertex positions to avoid catastrophic f32 precision loss at large world coordinates:

GPU vertex position = world_position - camera_target_world()

Because the subtraction happens in f64 before the cast to f32, the resulting f32 values are small (within a few kilometres of the origin) and retain sub-centimetre precision.

§Why this is necessary

At longitude ~180 degrees, the absolute Mercator X coordinate is approximately 20 million metres. A 32-bit float at that magnitude has an epsilon of approximately 2 metres – sub-metre detail would be lost. Camera-relative rendering eliminates this problem.

§What is subtracted

RendererOrigin
WGPUMapState::scene_world_origin() (= camera.target_world())
BevySame – the Bevy camera is at eye_offset(), tiles at (world - origin)

§4. Projection round-trip guarantees

For stable (v1.0) projections, the following round-trip invariant holds at all valid inputs:

let world = projection.project(&geo);
let back  = projection.unproject(&world);
assert!((back.lat - geo.lat).abs() < 1e-8);
assert!((back.lon - geo.lon).abs() < 1e-8);
ProjectionLatitude accuracyLongitude accuracyAltitude
Web Mercator< 1e-8 deg< 1e-8 degpassthrough (exact)
Equirectangular< 1e-8 deg< 1e-8 degpassthrough (exact)
Globe< 1e-6 deg< 1e-6 deg< 1 m
Vertical Perspective< 1e-5 deg< 1e-5 degbest-effort

§5. Anti-meridian handling

  • GeoCoord::clamped_mercator() wraps longitude to [-180, 180] via modular arithmetic.
  • +180 deg and -180 deg project to the same world X (validated by test).
  • Geodesic distance across the dateline follows the short path (e.g. 179 deg E to 179 deg W is approximately 220 km, not 39 800 km).

§6. High-latitude behaviour

  • Web Mercator is valid only within approximately +/-85.051 129 deg latitude.
  • project_clamped saturates latitude to this limit.
  • project_checked returns None outside the limit.
  • Scale factor diverges as sec(lat): at 85 deg it is approximately 11.5.
  • Equirectangular handles poles (+/-90 deg) without singularity.

§7. Large-world numeric stability

  • f64 arithmetic preserves centimetre-scale deltas at the Mercator extent boundary (approximately 20 million metres).
  • Camera-relative f32 vertices preserve sub-centimetre precision for geometry within approximately 100 km of the camera target.
  • At zoom 14 (approximately 10 m per tile pixel), on-screen tile corners have camera-relative f32 error < 1 cm (validated by test).

§8. Terrain elevation

  • ElevationGrid stores heights in f32 metres. The full Earth elevation range (-11 034 m to +8 848 m) survives the f32 cast with < 1 cm error.
  • Bilinear interpolation produces correct intermediate values.
  • Altitude (GeoCoord::alt) passes through projections unchanged.
  • TerrainConfig::vertical_exaggeration scales elevation in the GPU shader, not in the engine grid, preserving raw accuracy.

§9. Scale factor

  • Web Mercator: sec(lat) – unity at the equator, 2.0 at 60 deg.
  • Equirectangular: sec(lat) along X, 1.0 along Y.
  • Camera::meters_per_pixel() uses the scale factor to convert screen resolution to ground resolution.

§10. Tile coordinate contract

  • geo_to_tile / tile_to_geo round-trip at all zoom levels.
  • Adjacent tiles share edges with < 1 mm world-space gap.
  • tile_bounds_world at zoom 0 spans the full Mercator world size.
  • TileId::quadkey() / TileId::from_quadkey() round-trip.
  • Parent / child relationships are consistent.

§11. Renderer parity contract

Both WGPU and Bevy renderers consume the same engine outputs:

Engine outputConsumed by
camera.target_world()Scene origin subtraction
camera.eye_offset()Camera placement
camera.view_matrix(DVec3::ZERO)View matrix (camera-relative)
camera.projection_matrix()Projection matrix
tile_bounds_world + UV mappingTile quad vertices
ElevationGridTerrain GPU texture (R32Float)
VectorMeshData positionsVector vertex buffers
ModelInstance position + altitudeModel transform buffers

Because both renderers apply camera-relative subtraction in f64 before the f32 cast, the precision guarantees are identical.

§Meter-Based Precision Invariants

This module documents the precision invariants that Rustial maintains across the engine and both renderer paths (WGPU and Bevy). These invariants are validated by tests in support_contract and by headless regression tests in the renderer crates.

§1. Internal units are meters

All internal linear distances, world-space coordinates, and elevation values in the engine are expressed in meters.

TypeUnit
WorldCoordmeters (east, north, up)
GeoCoord::altmeters above WGS-84 ellipsoid
ElevationGrid samplesmeters
Camera::distance()meters
Camera::meters_per_pixel()meters per screen pixel
Tile bounds (tile_bounds_world)meters
Geodesic distances (geodesic_distance)meters

§2. f64 engine, f32 GPU

The engine performs all math in f64. GPU-bound data is cast to f32 only at upload time. This ensures centimetre-scale precision even at world-edge coordinates (x ~ 20 million metres).

§3. Camera-relative rendering

Both renderers use camera-relative vertex positions to avoid catastrophic f32 precision loss at large world coordinates:

GPU vertex position = world_position - camera_target_world()

Because the subtraction happens in f64 before the cast to f32, the resulting f32 values are small (within a few kilometres of the origin) and retain sub-centimetre precision.

§Why this is necessary

At longitude ~180 degrees, the absolute Mercator X coordinate is approximately 20 million metres. A 32-bit float at that magnitude has an epsilon of approximately 2 metres – sub-metre detail would be lost. Camera-relative rendering eliminates this problem.

§What is subtracted

RendererOrigin
WGPUMapState::scene_world_origin() (= camera.target_world())
BevySame – the Bevy camera is at eye_offset(), tiles at (world - origin)

§4. Projection round-trip guarantees

For stable (v1.0) projections, the following round-trip invariant holds at all valid inputs:

let world = projection.project(&geo);
let back  = projection.unproject(&world);
assert!((back.lat - geo.lat).abs() < 1e-8);
assert!((back.lon - geo.lon).abs() < 1e-8);
ProjectionLatitude accuracyLongitude accuracyAltitude
Web Mercator< 1e-8 deg< 1e-8 degpassthrough (exact)
Equirectangular< 1e-8 deg< 1e-8 degpassthrough (exact)
Globe< 1e-6 deg< 1e-6 deg< 1 m
Vertical Perspective< 1e-5 deg< 1e-5 degbest-effort

§5. Anti-meridian handling

  • GeoCoord::clamped_mercator() wraps longitude to [-180, 180] via modular arithmetic.
  • +180 deg and -180 deg project to the same world X (validated by test).
  • Geodesic distance across the dateline follows the short path (e.g. 179 deg E to 179 deg W is approximately 220 km, not 39 800 km).

§6. High-latitude behaviour

  • Web Mercator is valid only within approximately +/-85.051 129 deg latitude.
  • project_clamped saturates latitude to this limit.
  • project_checked returns None outside the limit.
  • Scale factor diverges as sec(lat): at 85 deg it is approximately 11.5.
  • Equirectangular handles poles (+/-90 deg) without singularity.

§7. Large-world numeric stability

  • f64 arithmetic preserves centimetre-scale deltas at the Mercator extent boundary (approximately 20 million metres).
  • Camera-relative f32 vertices preserve sub-centimetre precision for geometry within approximately 100 km of the camera target.
  • At zoom 14 (approximately 10 m per tile pixel), on-screen tile corners have camera-relative f32 error < 1 cm (validated by test).

§8. Terrain elevation

  • ElevationGrid stores heights in f32 metres. The full Earth elevation range (-11 034 m to +8 848 m) survives the f32 cast with < 1 cm error.
  • Bilinear interpolation produces correct intermediate values.
  • Altitude (GeoCoord::alt) passes through projections unchanged.
  • TerrainConfig::vertical_exaggeration scales elevation in the GPU shader, not in the engine grid, preserving raw accuracy.

§9. Scale factor

  • Web Mercator: sec(lat) – unity at the equator, 2.0 at 60 deg.
  • Equirectangular: sec(lat) along X, 1.0 along Y.
  • Camera::meters_per_pixel() uses the scale factor to convert screen resolution to ground resolution.

§10. Tile coordinate contract

  • geo_to_tile / tile_to_geo round-trip at all zoom levels.
  • Adjacent tiles share edges with < 1 mm world-space gap.
  • tile_bounds_world at zoom 0 spans the full Mercator world size.
  • TileId::quadkey() / TileId::from_quadkey() round-trip.
  • Parent / child relationships are consistent.

§11. Renderer parity contract

Both WGPU and Bevy renderers consume the same engine outputs:

Engine outputConsumed by
camera.target_world()Scene origin subtraction
camera.eye_offset()Camera placement
camera.view_matrix(DVec3::ZERO)View matrix (camera-relative)
camera.projection_matrix()Projection matrix
tile_bounds_world + UV mappingTile quad vertices
ElevationGridTerrain GPU texture (R32Float)
VectorMeshData positionsVector vertex buffers
ModelInstance position + altitudeModel transform buffers

Because both renderers apply camera-relative subtraction in f64 before the f32 cast, the precision guarantees are identical.