From f0a3c462b628ab1c6c5a04fd2af59f9d73241ec4 Mon Sep 17 00:00:00 2001 From: Mario Date: Thu, 3 Sep 2026 18:31:30 +0200 Subject: [PATCH] Weltkarte Wald: Outline-Fill-Mismatch durch Chaikin-Schrumpfen behoben MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Chaikin corner-cutting bewegt Punkte nach innen → Outline lag kleiner als die Fill-Fläche. Die Wald-Density-Boundary ist bereits glatt (Summe kreisförmiger Kernels), braucht kein Smoothing. - assembleClosedPaths(segs, chaikinIter) extrahiert aus buildSeaCoastPaths: chaikinIter=0 → Pfade ohne Glättung, liegen exakt auf der Fill-Grenze - buildForestOutlinePaths nutzt assembleClosedPaths(..., 0) - buildSeaCoastPaths delegiert weiterhin mit chaikinIter=3 (Küste braucht Glättung wegen der stepped Terrain-Height-Boundary) Co-Authored-By: Claude Sonnet 4.6 --- .../java/de/blight/editor/ui/WorldMapView.java | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 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 f2b90e7..836c16c 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 @@ -1006,6 +1006,14 @@ public class WorldMapView extends VBox { // Verbindet Rohsegmente zu geschlossenen Küstenpfaden. private static List buildSeaCoastPaths(float[][] segs) { + return assembleClosedPaths(segs, 3); + } + + /** + * Verbindet Rohsegmente zu geschlossenen Pfaden. + * chaikinIter=0 → kein Smoothing (für bereits glatte Boundaries wie Wald-Density). + */ + private static List assembleClosedPaths(float[][] segs, int chaikinIter) { if (segs == null || segs.length == 0) { return Collections.emptyList(); } Map> adj = new HashMap<>(segs.length * 4); @@ -1031,7 +1039,8 @@ public class WorldMapView extends VBox { float[][] raw = new float[bwd.size() + fwd.size()][]; for (int i = 0; i < bwd.size(); i++) { raw[i] = bwd.get(bwd.size() - 1 - i); } for (int i = 0; i < fwd.size(); i++) { raw[bwd.size() + i] = fwd.get(i); } - result.add(chaikin(simplifyCoastPath(raw), 3)); + float[][] path = simplifyCoastPath(raw); + result.add(chaikinIter > 0 ? chaikin(path, chaikinIter) : path); } return result; } @@ -1138,9 +1147,11 @@ public class WorldMapView extends VBox { } List segs = new ArrayList<>(); marchingSquares(mask, SIZE, 0.5f, null, null, segs); - List paths = buildSeaCoastPaths(segs.toArray(new float[0][])); + // chaikinIter=0: Density-Boundary ist bereits glatt (kreisförmige Kernels), + // kein Chaikin-Schrumpfen → Outline liegt exakt auf der Fill-Grenze. + List paths = assembleClosedPaths(segs.toArray(new float[0][]), 0); - // Kleinstpolygone filtern: weniger als ~100m Umfang = Artefakt (z.B. 2 isolierte Bäume) + // Kleinstpolygone filtern: weniger als ~60m Umfang = Artefakt (z.B. isolierte Bäume) final float MIN_PERIMETER_SQ = 60f * 60f; return paths.stream().filter(p -> pathPerimeterSq(p) >= MIN_PERIMETER_SQ) .collect(java.util.stream.Collectors.toList());