From 3feff60e247daa026fc2f1bed7e70898d4ed933d Mon Sep 17 00:00:00 2001 From: Mario Date: Thu, 3 Sep 2026 18:46:23 +0200 Subject: [PATCH] Weltkarte Wald: Outline-Versatz durch Koordinaten-Mismatch behoben MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root cause: Fill nutzt fsfz = (WH-wz)/WS * FD (Skala: /FD=512), MS-Grid nutzt msW → / (fdSize-1)=511. Ergibt ~2m Versatz in Kartenmitte. Fix: h-Feld bei SEA_MASK_SIZE mit exakt der Fill-Formel aufbauen (* FD statt * (FD-1)), dann MS mit thr=0 auf diesem h-Feld. Die Isokontur liegt damit pixelgenau auf der Fill-Grenze. Co-Authored-By: Claude Sonnet 4.6 --- .../de/blight/editor/ui/WorldMapView.java | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/blight-editor/src/main/java/de/blight/editor/ui/WorldMapView.java b/blight-editor/src/main/java/de/blight/editor/ui/WorldMapView.java index c267e9e..4f4fa6f 100644 --- a/blight-editor/src/main/java/de/blight/editor/ui/WorldMapView.java +++ b/blight-editor/src/main/java/de/blight/editor/ui/WorldMapView.java @@ -1126,19 +1126,29 @@ public class WorldMapView extends VBox { /** Extrahiert Wald-Konturen aus der Tree-Density-Map als geglättete Weltkoordinaten-Pfade. */ private static List buildForestOutlinePaths(float[] density, int fdSize, float threshold) { - // Z-Flip: Density mz=0=Nord, MS mz=0=Süd. - // h = threshold - density → forest (density > threshold) ergibt h < 0 = "inside" für MS. - // MS läuft direkt auf echten Density-Werten mit thr=0 → exakt dieselbe Isokontur wie - // die Füllung in computeForestPixels (keine binäre Maske, kein Boundary-Versatz). - float[] h = new float[fdSize * fdSize]; - for (int mz = 0; mz < fdSize; mz++) { - int dz = fdSize - 1 - mz; - for (int mx = 0; mx < fdSize; mx++) { - h[mz * fdSize + mx] = threshold - density[dz * fdSize + mx]; + // h-Feld mit EXAKT derselben Koordinatenformel wie computeForestPixels aufbauen: + // fsfz = (WH - wz) / WS * FD (Fill nutzt * FD=512, nicht * (FD-1)=511!) + // Damit liegt die MS-Isokontur (h=0) pixelgenau auf der Fill-Grenze. + final int SIZE = SEA_MASK_SIZE; + final float WH = WorldMapRenderer.WORLD_HALF, WS = WorldMapRenderer.WORLD_SIZE; + float[] h = new float[SIZE * SIZE]; + for (int py = 0; py < SIZE; py++) { + float wz = (float) py / (SIZE - 1) * WS - WH; + float fsfz = Math.max(0f, Math.min(fdSize - 1.001f, (WH - wz) / WS * fdSize)); + int fsz0 = (int) fsfz, fsz1 = Math.min(fsz0 + 1, fdSize - 1); + float fstz = fsfz - fsz0; + for (int px = 0; px < SIZE; px++) { + float wx = (float) px / (SIZE - 1) * WS - WH; + float fsfx = Math.max(0f, Math.min(fdSize - 1.001f, (wx + WH) / WS * fdSize)); + int fsx0 = (int) fsfx, fsx1 = Math.min(fsx0 + 1, fdSize - 1); + float fstx = fsfx - fsx0; + float d = (density[fsz0 * fdSize + fsx0] * (1 - fstx) + density[fsz0 * fdSize + fsx1] * fstx) * (1 - fstz) + + (density[fsz1 * fdSize + fsx0] * (1 - fstx) + density[fsz1 * fdSize + fsx1] * fstx) * fstz; + h[py * SIZE + px] = threshold - d; // <0 = forest (inside), >0 = non-forest } } List segs = new ArrayList<>(); - marchingSquares(h, fdSize, 0f, null, null, segs); + marchingSquares(h, SIZE, 0f, null, null, segs); List paths = assembleClosedPaths(segs.toArray(new float[0][]), 0); final float MIN_PERIMETER_SQ = 60f * 60f;