Works out where the lanes of a road rotate into and out of each curve and the cross slope of each lane at any station.
How the precision is kept
Runoff and runout lengths and the transition stations are closed-form 64-bit expressions of the lane width, the rate and the relative gradient. The rate comes from a table by radius, read as a step function without interpolation. The default table and the 0.5 % gradient are placeholders, as the file's header says.
Calculations under this policy
5 calculations in 1 source file. The code is read from the released app when this page loads. It is the calculation people run, not a copy kept for this page.
The rates by radius and the relative gradient a new corridor starts with.
Rates from 2 % to 6 % by radius, one table for feet and one for meters, and a relative gradient of 0.005. The header calls them placeholders for the user to replace with their own design criteria.
A step lookup in a table sorted by radius, largest first: the first row whose radius the curve reaches gives the rate, and null means normal crown. There is no interpolation between rows.
superelev.js
lines 48 to 51
CivilCraft 0.8.1
export function rateFor(R, rate) {
for (const [r, e] of rate) if (R >= r) return e;
return rate.length ? rate[rate.length - 1][1] : null;
}
Runoff, runout and transition stations
64-bit
The lengths and stations of the transition into and out of each curve.
L_r = lane × e ÷ Δ; L_t = (NC ÷ e) × L_r; without a spiral: level at TS − ⅔L_r, full at TS + ⅓L_r
64-bit products and quotients; e is floored at 1e-9 in the runout division. With a spiral at an end the runoff is the spiral's own length. Problem texts round the radius and lengths to 2 decimals; the stations kept in the plan are not rounded.
Linear ramps in 64-bit, with the lengths floored at 1e-9 so the division cannot be by zero. Taking the maximum of the two ramps and −e handles a curve too short to reach full superelevation.
superelev.js
lines 77 to 92
CivilCraft 0.8.1
/** The outside lane's fall-away slope along one curve's transition (+NC outside it). */
function outsideAt(c, sta, nc) {
const s = c.stations, e = c.e;
// a curve too short for full superelevation reaches only part of e: the two ramps meet in the middle
const up = (x) => {
if (x <= s.nc0) return nc;
if (x <= s.lc0) return nc - (nc * (x - s.nc0)) / Math.max(c.Lt1, 1e-9); // runout: NC → 0
return -(e * (x - s.lc0)) / Math.max(c.Lr1, 1e-9); // runoff: 0 → −e
};
const down = (x) => {
if (x >= s.nc1) return nc;
if (x >= s.lc1) return nc - (nc * (s.nc1 - x)) / Math.max(c.Lt2, 1e-9);
return -(e * (s.lc1 - x)) / Math.max(c.Lr2, 1e-9);
};
return Math.max(up(sta), down(sta), -e);
}
Lane slopes at a station
64-bit
The fall-away slope of the left and right lanes.
inside = max(NC, −outside)
Outside every transition both lanes keep the normal crown. Where the transitions of two curves overlap, the later curve's values replace the earlier ones, because the loop runs in order.
superelev.js
lines 94 to 103
CivilCraft 0.8.1
export function slopesAt(plan, sta, nc = 0.02) {
let L = nc, R = nc;
for (const c of plan?.curves || []) {
if (sta <= c.stations.nc0 || sta >= c.stations.nc1) continue;
const o = outsideAt(c, sta, nc);
const inside = Math.max(nc, -o); // the inside lane keeps its crown until the outside one rolls past it, then one plane
if (c.outside === 'L') { L = o; R = inside; } else { R = o; L = inside; }
}
return { L, R };
}
In detail
Method
Runoff L_r = lane × e ÷ Δ and runout L_t = NC ÷ e × L_r; two thirds of the runoff on the tangent and one third on the curve, or the spiral's length where an end has a spiral (the header cites AASHTO's n₁ = 1, b_w = 1).
Number types
64-bit numbers.
Tolerances
Divisors are floored at 1e-9: the rate e, and the runoff and runout lengths. A spiral counts as too short when it is more than 1e-9 under the needed length.
Limits
The rate table is a step lookup. The default rates and gradient are placeholders, not a standard.
Approach: 64-bit, direct
Every value is a 64-bit number from input to result. Formulas are closed form or a direct solve, and every comparison uses a stated tolerance.