Fix coastline water side flipping on rotation

Root cause: pairEdges returned edges sorted numerically, but
getVerticesOnSide interprets "CW walk from edge2 to edge1" —
when the pair order flipped (e.g., [NE,NW] vs [E,SE]), the
CW walk went the long way vs short way, swapping water/land.

Fix: normalize edge pair so CW distance from e1→e2 is always
≤3 steps (the short arc). This makes waterSide=1 consistently
mean the same geometric side at every rotation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Axel Meyer
2026-04-07 11:22:52 +00:00
parent ce95930b87
commit 9a61647b4a

View File

@@ -377,8 +377,11 @@ function drawBezierRoute(
/**
* Coastline: routes edge-to-edge, fills one side with water.
* waterSide is stored on the feature: 1 = CW side, -1 = CCW side.
* This is stable across rotations — no heuristic needed.
* waterSide is stored on the feature: 1 or -1.
*
* Key fix: normalize edge pair order so CW distance from e1→e2
* is always the SHORT way (≤3 steps). This makes waterSide=1
* consistently mean "the short/concave side" at every rotation.
*/
function drawCoastlineFeature(
ctx: CanvasRenderingContext2D,
@@ -397,16 +400,25 @@ function drawCoastlineFeature(
for (const pair of pairs) {
if (pair.length === 2) {
const p1 = edgeMidpoints[pair[0]];
const p2 = edgeMidpoints[pair[1]];
// Normalize order: ensure CW distance from e1→e2 is ≤3
let e1 = pair[0];
let e2 = pair[1];
const cwDist = ((e2 - e1) + 6) % 6;
if (cwDist > 3) {
// Swap so the short CW arc is from e1 to e2
[e1, e2] = [e2, e1];
}
const p1 = edgeMidpoints[e1];
const p2 = edgeMidpoints[e2];
const cp = { x: cx, y: cy };
// Fill the water side using the explicit waterSide value
// Fill the water side
ctx.beginPath();
ctx.moveTo(p1.x, p1.y);
ctx.quadraticCurveTo(cp.x, cp.y, p2.x, p2.y);
const waterVerts = getVerticesOnSide(pair[0], pair[1], waterSide, vertices);
const waterVerts = getVerticesOnSide(e1, e2, waterSide, vertices);
for (const v of waterVerts) {
ctx.lineTo(v.x, v.y);
}