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.
| Type | Unit |
|---|---|
WorldCoord | meters (east, north, up) |
GeoCoord::alt | meters above WGS-84 ellipsoid |
ElevationGrid samples | meters |
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
| Renderer | Origin |
|---|---|
| WGPU | MapState::scene_world_origin() (= camera.target_world()) |
| Bevy | Same – 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);| Projection | Latitude accuracy | Longitude accuracy | Altitude |
|---|---|---|---|
| Web Mercator | < 1e-8 deg | < 1e-8 deg | passthrough (exact) |
| Equirectangular | < 1e-8 deg | < 1e-8 deg | passthrough (exact) |
| Globe | < 1e-6 deg | < 1e-6 deg | < 1 m |
| Vertical Perspective | < 1e-5 deg | < 1e-5 deg | best-effort |
§5. Anti-meridian handling
GeoCoord::clamped_mercator()wraps longitude to[-180, 180]via modular arithmetic.+180 degand-180 degproject 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_clampedsaturates latitude to this limit.project_checkedreturnsNoneoutside 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
ElevationGridstores 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_exaggerationscales 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_georound-trip at all zoom levels.- Adjacent tiles share edges with < 1 mm world-space gap.
tile_bounds_worldat 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 output | Consumed 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 mapping | Tile quad vertices |
ElevationGrid | Terrain GPU texture (R32Float) |
VectorMeshData positions | Vector vertex buffers |
ModelInstance position + altitude | Model 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.
| Type | Unit |
|---|---|
WorldCoord | meters (east, north, up) |
GeoCoord::alt | meters above WGS-84 ellipsoid |
ElevationGrid samples | meters |
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
| Renderer | Origin |
|---|---|
| WGPU | MapState::scene_world_origin() (= camera.target_world()) |
| Bevy | Same – 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);| Projection | Latitude accuracy | Longitude accuracy | Altitude |
|---|---|---|---|
| Web Mercator | < 1e-8 deg | < 1e-8 deg | passthrough (exact) |
| Equirectangular | < 1e-8 deg | < 1e-8 deg | passthrough (exact) |
| Globe | < 1e-6 deg | < 1e-6 deg | < 1 m |
| Vertical Perspective | < 1e-5 deg | < 1e-5 deg | best-effort |
§5. Anti-meridian handling
GeoCoord::clamped_mercator()wraps longitude to[-180, 180]via modular arithmetic.+180 degand-180 degproject 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_clampedsaturates latitude to this limit.project_checkedreturnsNoneoutside 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
ElevationGridstores 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_exaggerationscales 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_georound-trip at all zoom levels.- Adjacent tiles share edges with < 1 mm world-space gap.
tile_bounds_worldat 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 output | Consumed 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 mapping | Tile quad vertices |
ElevationGrid | Terrain GPU texture (R32Float) |
VectorMeshData positions | Vector vertex buffers |
ModelInstance position + altitude | Model 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.
| Type | Unit |
|---|---|
WorldCoord | meters (east, north, up) |
GeoCoord::alt | meters above WGS-84 ellipsoid |
ElevationGrid samples | meters |
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
| Renderer | Origin |
|---|---|
| WGPU | MapState::scene_world_origin() (= camera.target_world()) |
| Bevy | Same – 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);| Projection | Latitude accuracy | Longitude accuracy | Altitude |
|---|---|---|---|
| Web Mercator | < 1e-8 deg | < 1e-8 deg | passthrough (exact) |
| Equirectangular | < 1e-8 deg | < 1e-8 deg | passthrough (exact) |
| Globe | < 1e-6 deg | < 1e-6 deg | < 1 m |
| Vertical Perspective | < 1e-5 deg | < 1e-5 deg | best-effort |
§5. Anti-meridian handling
GeoCoord::clamped_mercator()wraps longitude to[-180, 180]via modular arithmetic.+180 degand-180 degproject 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_clampedsaturates latitude to this limit.project_checkedreturnsNoneoutside 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
ElevationGridstores 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_exaggerationscales 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_georound-trip at all zoom levels.- Adjacent tiles share edges with < 1 mm world-space gap.
tile_bounds_worldat 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 output | Consumed 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 mapping | Tile quad vertices |
ElevationGrid | Terrain GPU texture (R32Float) |
VectorMeshData positions | Vector vertex buffers |
ModelInstance position + altitude | Model transform buffers |
Because both renderers apply camera-relative subtraction in f64 before the f32 cast, the precision guarantees are identical.