Files
ParkingRobot/docs/superpowers/plans/2026-07-23-trap-map-pure-png.md
T

7.5 KiB
Raw Blame History

TrapMap Managed PNG Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Remove TrapMap's platform-specific drawing dependency and produce the same bounded 300 DPI PNG with a managed encoder that runs inside Clumsy.

Architecture: Keep TrapMapImageExporter.ExportIfEnabled and its file-reservation/publication behavior. Replace only the renderer with an internal RGBA raster surface, primitive drawing functions, a compact embedded bitmap font, and StbImageWriteSharp for PNG encoding; insert the 300 DPI pHYs chunk after encoding.

Tech Stack: C# 10, .NET Standard 2.0, StbImageWriteSharp 1.16.7, PowerShell contract/PNG parsing tests.

Global Constraints

  • Do not inspect or modify TrajPlanner.
  • Do not commit or stage any file.
  • Do not change grid construction, Painter behavior, movement behavior, switches, output location, naming, 300 DPI, 4 pixels per cell, 4000-pixel edge limit, or 50 MiB limit.
  • The only new image package is StbImageWriteSharp version 1.16.7; do not add native assets or another graphics package.
  • TrapMapImageExporter must have no runtime reference to System.Drawing.Common or System.Drawing.
  • Preserve collision-safe temporary-file reservation, encoded-size validation, atomic publication, and contained export failures.

Task 1: Replace System.Drawing rendering with a managed Stb PNG encoder

Files:

  • Modify: ClumsyPilot/ClumsyPilot.csproj
  • Modify: ClumsyPilot/TrapMapImageExporter.cs
  • Modify: ClumsyPilot/tests/verify_trapmap_image.ps1
  • Modify: docs/superpowers/specs/2026-07-22-trap-map-image-and-console-design.md
  • Modify: docs/superpowers/plans/2026-07-22-trap-map-image-and-console.md

Interfaces:

  • Preserve: TrapMapImageExporter.ExportIfEnabled(bool, TrapMapImageExportRequest) and all public request/result properties and constants.

  • Add only private implementation units: RgbaSurface, integer drawing helpers, bitmap-font helpers, and PngWriter.

  • RenderToTemporaryPng continues to consume the existing request/dimensions and write to the already exclusively reserved stream.

  • Step 1: Add dependency-removal and PNG-structure assertions

Update verify_trapmap_image.ps1 before production code. Require that:

if ($project.PackageReference.Include -contains 'System.Drawing.Common') {
    throw 'TrapMap must not depend on System.Drawing.Common.'
}
if ($project.Target.Name -contains 'DeployFrameworkDrawingRuntime') {
    throw 'Legacy drawing-runtime deployment target remains.'
}
if (-not ($project.PackageReference | Where-Object {
    $_.Include -eq 'StbImageWriteSharp' -and $_.Version -eq '1.16.7'
})) { throw 'Exact managed PNG package is missing.' }
if ($exporterSource -match 'System\.Drawing|\bBitmap\b|\bGraphics\b|ImageFormat') {
    throw 'Exporter still uses the external drawing API.'
}

Parse the generated PNG without loading a drawing assembly. Verify signature, one IHDR, one pHYs, one or more IDAT, and IEND; verify every chunk CRC. Assert IHDR width/height and RGBA8 fields and pHYs values 11811,11811,1. Decode representative pixels with a test-only PNG decoder or the Stb package and reuse the existing color/Y-inversion assertions.

Add a clean-output assertion after build:

$drawingDll = Join-Path (Split-Path -Parent $AssemblyPath) 'System.Drawing.Common.dll'
if (Test-Path -LiteralPath $drawingDll) {
    throw 'System.Drawing.Common.dll must not be deployed for TrapMap.'
}
  • Step 2: Run RED verification
dotnet build ClumsyPilot\ClumsyPilot.csproj --no-restore -v:minimal
powershell -ExecutionPolicy Bypass -File ClumsyPilot\tests\verify_trapmap_image.ps1

Expected: the image test fails because the package, deployment target, using System.Drawing, and renderer still exist.

  • Step 3: Remove the drawing package and deployment target

Delete the System.Drawing.Common PackageReference and the entire DeployFrameworkDrawingRuntime target. Add <PackageReference Include="StbImageWriteSharp" Version="1.16.7" />. Do not change the target framework or other references. Ensure a clean build cannot retain the old DLL: the verification command must remove only ClumsyPilot/bin/Debug/netstandard2.0/System.Drawing.Common.dll before rebuilding, after resolving and validating that exact path is under the project output directory.

  • Step 4: Implement the RGBA raster surface

Replace drawing types with a private surface backed by byte[] in RGBA order. Required primitives and semantics:

SetPixel(int x, int y, byte r, byte g, byte b, byte a = 255);
FillRectangle(int x, int y, int width, int height, Color32 color);
DrawLine(int x0, int y0, int x1, int y1, Color32 color, int thickness);
DrawRectangle(int x, int y, int width, int height, Color32 color, int thickness);
DrawCircle(int centerX, int centerY, int radius, Color32 color, int thickness);
FillCircle(int centerX, int centerY, int radius, Color32 color);
FillPolygon(PointD[] points, Color32 color);

Clip every primitive to the surface. Use pre-clipped Bresenham lines, scale-normalized scanline polygon filling, and a canvas-clipped bounded circle scan whose work is proportional to visible rows/columns rather than radius. Preserve exact white, red, LightGray (211,211,211), black, blue, and LimeGreen (0,255,0) colors. Convert vehicle/workstation world positions using the existing discrete-grid-aligned transform.

  • Step 5: Implement deterministic bitmap text

Embed a private 5×7 ASCII glyph table for code points 32126. Draw scaled glyphs using integer pixels; unsupported characters render as ?. Use a 2× scale for header text and 1× scale for the workstation label. Keep all five header baselines inside HeaderHeightPixels=140 with fixed non-overlapping line boxes. Continue building the same five metadata lines, including occupancy rate; sanitize only the exported header text, not logs.

  • Step 6: Encode PNG and add 300 DPI metadata

Use StbImageWriteSharp.ImageWriter.WritePng to encode the RGBA buffer. Then insert:

pHYs: X=11811, Y=11811, unit=1

Encode into a temporary MemoryStream, validate the PNG signature and first IHDR chunk, then copy the signature+IHDR, append the 13-byte pHYs chunk, and copy the remaining encoded chunks. Use big-endian integers and standard CRC-32 over pHYs+data. Do not close the caller-owned reserved stream before the existing file-size/atomic-move flow finishes.

  • Step 7: Update documentation and run GREEN verification

Remove all claims that TrapMap deploys or requires System.Drawing.Common; document the pure C# encoder and BCL-only runtime.

Run from a clean output state:

powershell -ExecutionPolicy Bypass -File ClumsyPilot\tests\verify_trapmap_inputs.ps1
dotnet build ClumsyPilot\ClumsyPilot.csproj --no-restore -v:minimal
powershell -ExecutionPolicy Bypass -File ClumsyPilot\tests\verify_trapmap_grid.ps1
powershell -ExecutionPolicy Bypass -File ClumsyPilot\tests\verify_trapmap_lifecycle.ps1
powershell -ExecutionPolicy Bypass -File ClumsyPilot\tests\verify_trapmap_image.ps1
git diff --check
$staged = git diff --cached --name-only; if ($staged) { throw "Unexpected staged files: $staged" }

Expected: build succeeds without a drawing DLL in output; all tests pass; PNG parser reports correct dimensions, CRCs, 300 DPI metadata, RGBA pixels, unique filenames, file size at or below 50 MiB, and no temporary files.