using System; using Godot; using IslaApocalypse.Core; namespace IslaApocalypse.Tools { /// /// THE LABELED-REGIONS OVERLAY (chat2/07): the mainland one tint, EACH island component an /// individually distinct colour, the components the speck revert removed in a dim red, the midline /// drawn — so the developer can SEE that components are identified correctly and catch an /// 8-connectivity mislabel (two touching blobs coloured as one, one mass coloured as two). /// /// ⚠ A DIAGNOSTIC, NOT A MAP. It draws the region layer's id map, which is DATA. No hypsometry, no /// hillshade — flat tints on purpose. The relief / grayscale plates stay on the provisional palette. /// Presentation only: it is handed arrays and returns a PNG. /// public static class RegionOverlayRenderer { private static readonly Color Sea = new(0.055f, 0.110f, 0.235f); private static readonly Color Mainland = new(0.340f, 0.380f, 0.330f); private static readonly Color Reverted = new(0.420f, 0.080f, 0.080f); // where a reverted speck WAS (now sea) private static readonly Color Midline = new(0.700f, 0.720f, 0.760f); private static readonly Color Ink = new(0.941f, 0.949f, 0.961f); /// A distinct, saturated colour per island id — golden-angle hue walk, three value steps. public static Color IslandColor(int id) { float hue = (id * 137.508f) % 360f / 360f; float val = 0.70f + 0.15f * (id % 3); float sat = 0.85f - 0.15f * ((id / 3) % 2); return Color.FromHsv(hue, sat, val); } /// The finished field's labeling (post-revert). /// The pre-revert labeling, or null — its reverted components are painted . public static void SavePng(RegionLabels labels, RegionLabels labelsPre, int mapSize, int revertedCount, long thresholdCells, string absolutePath) { var img = Image.CreateEmpty(mapSize, mapSize, false, Image.Format.Rgb8); int n = mapSize; for (int x = 0; x < n; x++) { for (int y = 0; y < n; y++) { int id = labels.Id[x * n + y]; Color c; if (id == 0) { c = Sea; if (labelsPre != null) { int pid = labelsPre.Id[x * n + y]; if (pid != 0 && pid != labelsPre.MainlandId) c = Reverted; // was land, was not mainland, is sea now } } else if (id == labels.MainlandId) c = Mainland; else c = IslandColor(id); img.SetPixel(x, y, c); } } int mid = n / 2; for (int x = 0; x < n; x += 3) img.SetPixel(x, mid, Midline); var (north, south) = RegionLabeling.IslandsByHemisphere(labels); int s = n >= 4096 ? 4 : 3; int lh = TinyFont.Height(s) + 6; TinyFont.Draw(img, "LABELED REGIONS - CLASSIFY FIELD, LAND 8-CONNECTED", 12, 12, s, Ink); TinyFont.Draw(img, $"GREY: MAINLAND (CENTRE COMPONENT{(labels.CentreWasLand ? "" : " - FALLBACK, CENTRE NOT LAND")}) EACH ISLAND: ITS OWN COLOUR", 12, 12 + lh, s, Ink); TinyFont.Draw(img, $"ISLANDS: {labels.IslandCount} ({north} N / {south} S BY CENTROID) DARK RED: {revertedCount} REVERTED < {thresholdCells} CELLS", 12, 12 + lh * 2, s, Ink); TinyFont.Draw(img, "N ABOVE THE LINE - S BELOW - Y RUNS SOUTH", 12, 12 + lh * 3, s, Ink); Error err = img.SavePng(absolutePath); if (err != Error.Ok) GD.PrintErr($"[RegionOverlayRenderer] SavePng failed ({err}) for {absolutePath}"); } } }