THE SCHEDULE FOR AN ADAPTIVE TERRAIN-HORIZON SWEEP.
Deriving an azimuth/elevation mask from terrain means asking, for one
azimuth, "what is the highest thing I can see along this ray". The naive
answer walks a fixed ladder of ranges. That is what an az-el mask demo did
until 2026-08-15, with 26 log-spaced samples out to 40 km, and it under-
derived badly at coastal sites: the ladder's outer gaps were kilometres
wide, small islands fell BETWEEN two samples, and the mask sat at sea level
with a peak of 0.0 while the renderer — correctly — drew terrain sticking
through the volume.
The fix is not "more samples everywhere". Terrain contributes elevation
`atan(h / d)`, so a metre of height is worth a thousand times more at 10 m
than at 10 km: the sweep must be DENSE CLOSE IN AND ABSENT FAR OUT (owner
2026-08-15). This module is the pure arithmetic of that schedule, separated
from any sampling so the guarantee is assertable without terrain, a network,
or a GL context:
RINGS ARE GEOMETRIC. Ring i sits at `minimumRange * (1 + relativeStep)^i`,
so the radial gap at distance d is exactly `relativeStep * d` — sample
DENSITY falls as 1/d, which is constant angular resolution of the horizon
profile. The gap is also the island guarantee: any feature whose radial
extent along the ray is at least `relativeStep * d` cannot fall between
two rings.
AZIMUTHS TERMINATE. Beyond distance d, the highest elevation ANY terrain
could still contribute is bounded by the tallest ground on Earth seen at
that range, minus how far the Earth has curved away underneath it:
bound(d) = atan((hMax - hObserver) / d) - d / (2 * R)
Once that bound drops to or below the mask already accumulated for an
azimuth, nothing further out can raise it, and sampling that azimuth
outward is provably wasted. A site under a 50-degree valley wall stops at
about 8 km; an open-ocean azimuth with a mask of 0 runs to roughly 340 km,
where the bound crosses zero on curvature alone. By the outer ranges the
sweep is taking no samples at all.
`hMax` defaults to 9,000 m: above Everest (8,849 m), so the bound is
conservative anywhere on Earth, while still being small enough that the
termination bites within a few kilometres at any site with real relief. It
is an option so a caller with a tighter regional maximum can terminate
sooner.
Members
Default Earth radius used for the curvature dip, in meters.
Default hard ceiling on the sweep, in meters. Termination normally stops an
azimuth long before this; it exists so a mask of exactly zero over open
water still ends.
Default conservative ceiling on terrain height anywhere on Earth, in meters.
Default innermost ring, in meters. Closer than this the observer's own mast
dominates and terrain posts are noise.
Default ring-to-ring growth. 0.02 means the radial gap is 2% of the
distance: 2.4 m at the innermost ring, 200 m at 10 km, 800 m at 40 km.
Methods
The highest elevation angle any terrain at or beyond `distance` could still
contribute, in DEGREES. Negative once the Earth has curved the tallest
possible ground below the observer's horizontal.
| Name | Type | Description | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
distance |
number | Distance from the observer, in meters. | ||||||||||||||||
options |
object |
optional
Options.
|
Returns:
The bound, in degrees.
The distance at which an azimuth holding `maskDegrees` becomes exhausted —
the closed form of
TerrainHorizonSweep.isExhausted, useful for
reporting and for asserting the schedule without walking it. Ignores the
curvature dip, so it is an upper bound on the true termination distance.
| Name | Type | Description |
|---|---|---|
maskDegrees |
number | The accumulated mask, in degrees. |
options |
object |
optional
Options, as TerrainHorizonSweep.elevationBound. |
Returns:
The distance in meters, or Infinity when the mask is at or below zero.
Whether an azimuth is EXHAUSTED at `distance`: the mask already accumulated
for it is at least as high as anything further out could contribute, so the
sweep should stop walking that azimuth.
| Name | Type | Description |
|---|---|---|
maskDegrees |
number | The highest elevation seen on this azimuth so far, in degrees. |
distance |
number | The distance just sampled, in meters. |
options |
object |
optional
Options, as TerrainHorizonSweep.elevationBound. |
Returns:
True when no further sample on this azimuth can raise the mask.
The largest radial gap between consecutive rings at a given distance — the
island-catching guarantee. Any terrain feature whose extent along the ray is
at least this wide is sampled at least once.
| Name | Type | Default | Description |
|---|---|---|---|
distance |
number | Distance from the observer, in meters. | |
relativeStep |
number |
TerrainHorizonSweep.DEFAULT_RELATIVE_STEP
|
optional Fractional gap. |
Returns:
The gap, in meters.
The geometric ladder of ring distances, in meters.
| Name | Type | Description | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
options |
object |
optional
Options.
|
Returns:
Ring distances, ascending.