mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-19 05:26:47 +00:00
fix(tours): a lake leg along the shore stays along it when it starts at the bank
WATER-005 found the shore style crossing straight over like the direct one. Pulling a cell path straight allowed a shortcut through water as costly as its dearer end, and since the clearance ramp a cell at the bank costs up to five times, so a leg from a river mouth lifted that ceiling above the open water and was pulled across the lake. A shortcut is now held to the cost of the open water alone (the style's band and the boat's reach); how near the bank it may come was already held by the path it replaces. The search costs are unchanged. Measured on the test lake and a larger one with a bay and a spit: direct, canoe and SUP legs are exactly as before; along the shore the leg no longer crosses (median 98 to 161 m off the bank, was up to 500), and no more samples on land than before.
This commit is contained in:
@@ -63,17 +63,33 @@ interface Grid {
|
||||
weight: (cell: number) => number;
|
||||
}
|
||||
|
||||
/** With `shore`, what open water costs on top: nothing up to SHORE_NEAR_M, rising to SHORE_WEIGHT at SHORE_FAR_M. */
|
||||
function bandOf(shore: number, profile: LakeProfile): number {
|
||||
if (profile.style !== 'shore') return 0;
|
||||
return SHORE_WEIGHT * Math.min(1, Math.max(0, (shore - SHORE_NEAR_M) / (SHORE_FAR_M - SHORE_NEAR_M)));
|
||||
}
|
||||
|
||||
function weightOf(shore: number, profile: LakeProfile): number {
|
||||
let weight = 1;
|
||||
const clearance = CLEARANCE_M[profile.style];
|
||||
if (shore < clearance) weight += CLEARANCE_WEIGHT * (1 - shore / clearance) ** 2;
|
||||
if (profile.style === 'shore') {
|
||||
weight += SHORE_WEIGHT * Math.min(1, Math.max(0, (shore - SHORE_NEAR_M) / (SHORE_FAR_M - SHORE_NEAR_M)));
|
||||
}
|
||||
weight += bandOf(shore, profile);
|
||||
if (shore > profile.maxCrossingM / 2) weight *= OVER_LIMIT_WEIGHT;
|
||||
return weight;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link weightOf} without the clearance: what the water costs for how far
|
||||
* out it lies. A shortcut is held to this, and to the path's own distance
|
||||
* from the shore (see `pull`), so a leg that starts or ends at the bank, as
|
||||
* every one from a river does, cannot lift the ceiling with the bank's cost
|
||||
* and pull a route along the shore straight across the open water.
|
||||
*/
|
||||
function openWeightOf(shore: number, profile: LakeProfile): number {
|
||||
const weight = 1 + bandOf(shore, profile);
|
||||
return shore > profile.maxCrossingM / 2 ? weight * OVER_LIMIT_WEIGHT : weight;
|
||||
}
|
||||
|
||||
function step(grid: Grid, at: number, each: (next: number, length: number) => void): void {
|
||||
const { width, height, cell } = grid;
|
||||
const col = at % width;
|
||||
@@ -244,11 +260,6 @@ export class LakeField {
|
||||
return d;
|
||||
}
|
||||
|
||||
/** What crossing a cell costs per metre, for this profile. */
|
||||
weight(cell: number, profile: LakeProfile): number {
|
||||
return weightOf(this.shoreDistance()[cell] as number, profile);
|
||||
}
|
||||
|
||||
private fine(profile: LakeProfile): Grid {
|
||||
const shore = this.shoreDistance();
|
||||
return {
|
||||
@@ -322,8 +333,8 @@ export class LakeField {
|
||||
/**
|
||||
* The line from one cell to another as a paddler would take it: the
|
||||
* cheapest cell path, then pulled straight wherever a straight line stays on
|
||||
* water no more costly than the cells it joins and no nearer the shore than
|
||||
* the path it replaces.
|
||||
* open water no more costly than the cells it joins and no nearer the shore
|
||||
* than the path it replaces.
|
||||
*/
|
||||
path(from: number, to: number, profile: LakeProfile): Array<[number, number]> | null {
|
||||
const cells = astar(this.fine(profile), from, to);
|
||||
@@ -356,25 +367,26 @@ export class LakeField {
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a straight line between two cells runs on water no more costly
|
||||
* than its ends and nowhere nearer the shore than `floor` (less half a cell,
|
||||
* for the line's rounding onto cells).
|
||||
* Whether a straight line between two cells runs on open water no more
|
||||
* costly than its ends ({@link openWeightOf}) and nowhere nearer the shore
|
||||
* than `floor` (less half a cell, for the line's rounding onto cells).
|
||||
*/
|
||||
private clear(a: number, b: number, profile: LakeProfile, floor: number): boolean {
|
||||
const shore = this.shoreDistance();
|
||||
const open = (cell: number) => openWeightOf(shore[cell] as number, profile);
|
||||
const least = floor - this.lake.cell / 2;
|
||||
const { width } = this.lake;
|
||||
const ac = a % width;
|
||||
const ar = (a - ac) / width;
|
||||
const bc = b % width;
|
||||
const br = (b - bc) / width;
|
||||
const ceiling = Math.max(this.weight(a, profile), this.weight(b, profile)) * 1.05;
|
||||
const ceiling = Math.max(open(a), open(b)) * 1.05;
|
||||
const steps = Math.max(Math.abs(bc - ac), Math.abs(br - ar)) * 2;
|
||||
for (let s = 1; s < steps; s++) {
|
||||
const c = Math.round(ac + ((bc - ac) * s) / steps);
|
||||
const r = Math.round(ar + ((br - ar) * s) / steps);
|
||||
const cell = r * width + c;
|
||||
if (!this.isWater(cell) || this.weight(cell, profile) > ceiling || (shore[cell] as number) < least) return false;
|
||||
if (!this.isWater(cell) || open(cell) > ceiling || (shore[cell] as number) < least) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user