Weltkarte: Wald-Linien Z-Flip fix + korrekte Layer-Reihenfolge

- buildForestOutlinePaths: Density-Map hat Z invertiert (Nord=mz=0) gegenüber
  Marching-Squares-Koordinaten (Süd=mz=0) → gespiegelte Konturen landeten
  off-screen, kein sichtbarer Wald-Rand. Fix: dz = fdSize-1-mz beim Samplen.
- Render-Reihenfolge korrigiert: Klippen → Wald → Wasser → Areas
  (Wald war über Wasser, sollte darunter liegen)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-09-03 18:20:43 +02:00
parent 8d6da6b013
commit 28cc89dcb1

View File

@@ -456,12 +456,12 @@ public class WorldMapView extends VBox {
} else {
gc.drawImage(mapFxImage, panX, panY, mapFxImage.getWidth() * scale, mapFxImage.getHeight() * scale);
}
// Layer 2: Klippen (Grau-Füllung + Vektor-Kontur)
// Layer 2: Klippen (Grau-Füllung + Schraffierung + Vektor-Kontur)
if (cbCliffs.isSelected()) drawCliffOverlay(gc);
// Layer 3: Küstenlinie (Vektor)
if (cbWater.isSelected()) drawWaterOverlay(gc);
// Layer 4: Wald (Füllung + Vektor-Kontur)
// Layer 3: Wald (Füllung + Vektor-Kontur) – unter Wasser
if (cbForest.isSelected()) drawForestOverlay(gc);
// Layer 4: Küstenlinie + Wasser (Vektor) – deckt Terrain-Features ab
if (cbWater.isSelected()) drawWaterOverlay(gc);
// Layer 5: Areas (Vektoren)
if (cbAreas.isSelected()) drawAreasOverlay(gc);
if (!isFullscreen) drawCameraIndicator(gc);
@@ -1117,21 +1117,17 @@ public class WorldMapView extends VBox {
/** Extrahiert Wald-Konturen aus der Tree-Density-Map als geglättete Weltkoordinaten-Pfade. */
private static List<float[][]> buildForestOutlinePaths(float[] density, int fdSize, float threshold) {
final int SIZE = SEA_MASK_SIZE;
float[] mask = new float[SIZE * SIZE];
for (int py = 0; py < SIZE; py++) {
for (int px = 0; px < SIZE; px++) {
float fx = (float) px / (SIZE - 1) * (fdSize - 1);
float fz = (float) py / (SIZE - 1) * (fdSize - 1);
int sx = Math.min(fdSize - 1, (int) fx), sz = Math.min(fdSize - 1, (int) fz);
float d = density[sz * fdSize + sx];
// Wald-Bereich = "inside" (< threshold 0.5): -1; Nicht-Wald = 1
mask[py * SIZE + px] = d >= threshold ? -1f : 1f;
// Density-Map: mz=0 = Norden (WH-pm.z()=0 → pm.z()=+WH), mz=fdSize-1 = Süden.
// Marching Squares: mz=0 → msW=-WH (Süden). → Z muss gespiegelt werden.
float[] mask = new float[fdSize * fdSize];
for (int mz = 0; mz < fdSize; mz++) {
int dz = fdSize - 1 - mz; // Süden im MS ↔ Süden in der Density-Map
for (int mx = 0; mx < fdSize; mx++) {
mask[mz * fdSize + mx] = density[dz * fdSize + mx] >= threshold ? -1f : 1f;
}
}
List<float[]> segs = new ArrayList<>();
marchingSquares(mask, SIZE, 0.5f, null, null, segs);
marchingSquares(mask, fdSize, 0.5f, null, null, segs);
return buildSeaCoastPaths(segs.toArray(new float[0][]));
}