Smooth washboard section: half-sine arches instead of spiky zig-zag
This commit is contained in:
parent
3be7b857ed
commit
864e76098a
|
|
@ -38,14 +38,17 @@ const RISE_SHARP = 160;
|
||||||
const AMP_HILLS = 45;
|
const AMP_HILLS = 45;
|
||||||
const DEPTH_DITCH = 130;
|
const DEPTH_DITCH = 130;
|
||||||
|
|
||||||
// Washboard: a repeating corrugated-bump wave (like washboard/"bumpy road"
|
// Washboard: a repeating run of smooth arches (half-sine humps, like a
|
||||||
// track in Trials-style games) over a flat or sloped baseline. The bump
|
// corrugated/washboard road in Trials-style games) over a flat or sloped
|
||||||
// count is derived from the section's width (target WASH_PERIOD base units
|
// baseline. The arch count is derived from the section's width (target
|
||||||
// per bump cycle) so the bumps keep a roughly constant width when the user
|
// WASH_PERIOD base units per arch) so the arches keep a roughly constant
|
||||||
// stretches/contracts a section instead of every bump stretching with it,
|
// width when the user stretches/contracts a section instead of every arch
|
||||||
// and the floor keeps the pattern visible even at the editor's minimum
|
// stretching with it, and the floor keeps the pattern visible even at the
|
||||||
// section width. Amplitude, and for the up/down variants the overall rise
|
// editor's minimum section width. A half-sine arch's steepest tangent is
|
||||||
// too, scale with vScale like every other section's rise/amplitude/depth.
|
// only PI*amplitude/period, so at these numbers each hump reads as a
|
||||||
|
// gentle rounded arch (~40 degrees at its steepest) rather than a spiky
|
||||||
|
// wave. Amplitude, and for the up/down variants the overall rise too,
|
||||||
|
// scale with vScale like every other section's rise/amplitude/depth.
|
||||||
const WASH_AMPLITUDE = 26;
|
const WASH_AMPLITUDE = 26;
|
||||||
const WASH_PERIOD = 100;
|
const WASH_PERIOD = 100;
|
||||||
const WASH_RISE = 120;
|
const WASH_RISE = 120;
|
||||||
|
|
@ -54,11 +57,13 @@ function washBumps(width) {
|
||||||
return Math.max(4, Math.round(width / WASH_PERIOD));
|
return Math.max(4, Math.round(width / WASH_PERIOD));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4 samples per bump cycle: enough that each crest and trough lands exactly
|
// Samples per arch. A half-sine arch only reads as a smooth arch once it's
|
||||||
// on a sample (a washboard tooth made of a couple of straight segments
|
// sampled densely - at 4 samples per arch the points (foot, crest, foot,
|
||||||
// reads as a bumpy road rather than a sawtooth spike), while the default
|
// midline) joined into a zig-zag diamond that looked like sharp spikes
|
||||||
// 700-wide section stays a modest 29 points.
|
// instead of rounded arches, so this is 12: every arch is a run of short
|
||||||
const WASH_SAMPLES_PER_BUMP = 4;
|
// segments that reads as one smooth hump, while the default 700-wide
|
||||||
|
// section stays a modest ~85 points.
|
||||||
|
const WASH_SAMPLES_PER_BUMP = 12;
|
||||||
|
|
||||||
const JUMP_TAKEOFF_FRACTION = 0.45;
|
const JUMP_TAKEOFF_FRACTION = 0.45;
|
||||||
const JUMP_GAP_FRACTION = 0.25;
|
const JUMP_GAP_FRACTION = 0.25;
|
||||||
|
|
@ -103,7 +108,7 @@ function smoothstep(t) {
|
||||||
|
|
||||||
// Samples x from startX to startX+width every `step` (inclusive of both
|
// Samples x from startX to startX+width every `step` (inclusive of both
|
||||||
// ends), calling shape(t) for the y-offset at each sample, t in [0,1]. The
|
// ends), calling shape(t) for the y-offset at each sample, t in [0,1]. The
|
||||||
// washboard passes a custom step (4 samples per bump, see
|
// washboard passes a custom step (samples per arch, see
|
||||||
// WASH_SAMPLES_PER_BUMP); every other generator keeps the POINT_STEP grid.
|
// WASH_SAMPLES_PER_BUMP); every other generator keeps the POINT_STEP grid.
|
||||||
function sampleBlock(startX, startY, width, shape, step = POINT_STEP) {
|
function sampleBlock(startX, startY, width, shape, step = POINT_STEP) {
|
||||||
const points = [];
|
const points = [];
|
||||||
|
|
@ -161,36 +166,36 @@ function ditch(startX, startY, width, vScale) {
|
||||||
return { type: 'simple', points, endY: startY };
|
return { type: 'simple', points, endY: startY };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Washboard (flat): N full sine periods, each one a bump cycle. Sin starts
|
// Washboard (flat): N full half-sine arches (humps). Sin starts and ends
|
||||||
// and ends at zero displacement, so both ends sit exactly on the neighbor
|
// at zero displacement, so both ends sit exactly on the neighbor sections'
|
||||||
// sections' baseline (no seam step) while the interior is the uniform
|
// baseline (no seam step) while the interior is a uniform run of smooth
|
||||||
// corrugated profile.
|
// humps.
|
||||||
function washboard(startX, startY, width, vScale) {
|
function washboard(startX, startY, width, vScale) {
|
||||||
const amp = WASH_AMPLITUDE * vScale;
|
const amp = WASH_AMPLITUDE * vScale;
|
||||||
const bumps = washBumps(width);
|
const bumps = washBumps(width);
|
||||||
const points = sampleBlock(startX, startY, width, (t) => amp * Math.sin(2 * Math.PI * bumps * t), width / (WASH_SAMPLES_PER_BUMP * bumps));
|
const points = sampleBlock(startX, startY, width, (t) => -amp * Math.sin(Math.PI * bumps * t), width / (WASH_SAMPLES_PER_BUMP * bumps));
|
||||||
return { type: 'simple', points, endY: startY };
|
return { type: 'simple', points, endY: startY };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Washboard Up: the same washboard wave riding on an overall incline. The
|
// Washboard Up: the same smooth arches riding on an overall incline. The
|
||||||
// baseline uses smoothstep (zero slope at both ends) like smoothIncline, so
|
// baseline uses smoothstep (zero slope at both ends) like smoothIncline, so
|
||||||
// the section's ends match a flat neighbor's baseline and the climb eases
|
// the section's ends match a flat neighbor's baseline and the climb eases
|
||||||
// in/out instead of ramping at a constant angle under the bumps.
|
// in/out instead of ramping at a constant angle under the arches.
|
||||||
function washboardUp(startX, startY, width, vScale) {
|
function washboardUp(startX, startY, width, vScale) {
|
||||||
const amp = WASH_AMPLITUDE * vScale;
|
const amp = WASH_AMPLITUDE * vScale;
|
||||||
const rise = WASH_RISE * vScale;
|
const rise = WASH_RISE * vScale;
|
||||||
const bumps = washBumps(width);
|
const bumps = washBumps(width);
|
||||||
const points = sampleBlock(startX, startY, width, (t) => -rise * smoothstep(t) + amp * Math.sin(2 * Math.PI * bumps * t), width / (WASH_SAMPLES_PER_BUMP * bumps));
|
const points = sampleBlock(startX, startY, width, (t) => -rise * smoothstep(t) - amp * Math.sin(Math.PI * bumps * t), width / (WASH_SAMPLES_PER_BUMP * bumps));
|
||||||
return { type: 'simple', points, endY: startY - rise };
|
return { type: 'simple', points, endY: startY - rise };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Washboard Down: mirror of washboardUp - a smoothstep decline baseline
|
// Washboard Down: mirror of washboardUp - a smoothstep decline baseline
|
||||||
// with the washboard wave on top.
|
// with the smooth arches on top.
|
||||||
function washboardDown(startX, startY, width, vScale) {
|
function washboardDown(startX, startY, width, vScale) {
|
||||||
const amp = WASH_AMPLITUDE * vScale;
|
const amp = WASH_AMPLITUDE * vScale;
|
||||||
const rise = WASH_RISE * vScale;
|
const rise = WASH_RISE * vScale;
|
||||||
const bumps = washBumps(width);
|
const bumps = washBumps(width);
|
||||||
const points = sampleBlock(startX, startY, width, (t) => rise * smoothstep(t) + amp * Math.sin(2 * Math.PI * bumps * t), width / (WASH_SAMPLES_PER_BUMP * bumps));
|
const points = sampleBlock(startX, startY, width, (t) => rise * smoothstep(t) - amp * Math.sin(Math.PI * bumps * t), width / (WASH_SAMPLES_PER_BUMP * bumps));
|
||||||
return { type: 'simple', points, endY: startY + rise };
|
return { type: 'simple', points, endY: startY + rise };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue