From 522662ded6dd2f4160d9701205c1678c32f9dbc8 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Sun, 13 Sep 2026 21:12:44 +0200 Subject: [PATCH] test: extend coverage of testTileTexture --- tests/testTileTexture.cpp | 140 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) diff --git a/tests/testTileTexture.cpp b/tests/testTileTexture.cpp index 7e3c682..056fff1 100644 --- a/tests/testTileTexture.cpp +++ b/tests/testTileTexture.cpp @@ -301,3 +301,143 @@ VF_TEST(writeTileAgreesWithPixelAtEverywhere) { } } +// The atlas is built at sixteen pixels today, but nothing in the palette is +// allowed to depend on that. Alpha rules and contrast have to survive a +// different tile size. +VF_TEST(tilesKeepTheirAlphaAndContrastAtOtherTileSizes) { + const int sizes[] = {8, 16, 32}; + for (int size : sizes) { + TileTextureParams params; + params.tileSize = size; + TileSampler sampler(params); + for (int tile = 0; tile < renderedTiles(sampler); ++tile) { + const TileSpec& spec = sampler.palette().specFor(tile); + int holes = 0; + int solid = 0; + for (int y = 0; y < size; ++y) { + for (int x = 0; x < size; ++x) { + uint8_t alpha = sampler.pixelAt(tile, x, y).a; + if (alpha == 0) ++holes; + if (alpha == 255) ++solid; + } + } + CHECK_EQ(holes + solid, size * size); + if (spec.cutout) { + CHECK(holes > 0); + CHECK(solid > 0); + if (holes == 0 || solid == 0) { + std::printf(" tile %d (%s) at size %d: %d holes, %d solid\n", tile, spec.name, + size, holes, solid); + } + } else { + CHECK_EQ(holes, 0); + } + int spread = interiorRange(sampler, tile).spread(); + CHECK(spread >= 8); + if (spread < 8) { + std::printf(" tile %d (%s) at size %d is flat: spread %d\n", tile, spec.name, size, + spread); + } + } + } +} + +// Boards run one way and their grain runs with them. Flipping the spec's axis +// has to flip which direction the tile varies in, otherwise the plank pattern +// is drawing wallpaper. +VF_TEST(plankGrainFollowsTheBoardDirection) { + TileSampler sampler; + int size = sampler.params().tileSize; + + TileSpec board; + board.name = "testBoard"; + board.pattern = TilePattern::Plank; + board.material = TileMaterial::Wood; + board.base = TileRgb{160, 120, 70}; + board.accent = TileRgb{90, 64, 34}; + board.noise = 0.0f; + board.scale = 0.35f; + board.density = 0.50f; + + TileSpec upright = board; + upright.vertical = true; + + auto axisSpread = [&](const TileSpec& spec, bool alongColumns) { + int lowest = 255; + int highest = 0; + for (int outer = 1; outer < size; ++outer) { + int total = 0; + for (int inner = 1; inner < size; ++inner) { + int x = alongColumns ? outer : inner; + int y = alongColumns ? inner : outer; + total += luminance(sampler.pixelOf(spec, 900, x, y)); + } + int mean = total / (size - 1); + if (mean < lowest) lowest = mean; + if (mean > highest) highest = mean; + } + return highest - lowest; + }; + + int uprightAcrossColumns = axisSpread(upright, true); + int uprightAcrossRows = axisSpread(upright, false); + CHECK(uprightAcrossColumns > uprightAcrossRows); + + int flatAcrossRows = axisSpread(board, false); + int flatAcrossColumns = axisSpread(board, true); + CHECK(flatAcrossRows > flatAcrossColumns); +} + +// A ladder without gaps is a plank. Every cutout lattice has to leave real +// openings and still keep enough bar to stand on. +VF_TEST(latticeTilesLeaveOpeningsBetweenTheirBars) { + TileSampler sampler; + int size = sampler.params().tileSize; + int checked = 0; + for (int tile = 0; tile < renderedTiles(sampler); ++tile) { + const TileSpec& spec = sampler.palette().specFor(tile); + if (spec.pattern != TilePattern::Lattice || !spec.cutout) continue; + ++checked; + int holes = 0; + for (int y = 0; y < size; ++y) { + for (int x = 0; x < size; ++x) { + if (sampler.pixelAt(tile, x, y).a == 0) ++holes; + } + } + float openFraction = static_cast(holes) / static_cast(size * size); + CHECK(openFraction > 0.15f); + CHECK(openFraction < 0.92f); + if (openFraction <= 0.15f || openFraction >= 0.92f) { + std::printf(" tile %d (%s) is %.2f open\n", tile, spec.name, + static_cast(openFraction)); + } + } + CHECK(checked >= 8); +} + +// Veins have to be visible without swallowing the host rock, or every ore looks +// like a solid block of its own metal. +VF_TEST(oreVeinsCoverPartOfTheHostRockWithoutSwallowingIt) { + TileSampler sampler; + int size = sampler.params().tileSize; + int checked = 0; + for (int tile = 0; tile < renderedTiles(sampler); ++tile) { + const TileSpec& spec = sampler.palette().specFor(tile); + if (spec.pattern != TilePattern::OreSpeckle) continue; + ++checked; + int vein = 0; + for (int y = 0; y < size; ++y) { + for (int x = 0; x < size; ++x) { + if (leansToAccent(sampler.pixelAt(tile, x, y), spec)) ++vein; + } + } + float coverage = static_cast(vein) / static_cast(size * size); + CHECK(coverage > 0.02f); + CHECK(coverage < 0.55f); + if (coverage <= 0.02f || coverage >= 0.55f) { + std::printf(" tile %d (%s) vein coverage %.3f\n", tile, spec.name, + static_cast(coverage)); + } + } + CHECK(checked >= 9); +}