126 lines
7.5 KiB
Markdown
126 lines
7.5 KiB
Markdown
# 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:
|
||
|
||
```powershell
|
||
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:
|
||
|
||
```powershell
|
||
$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**
|
||
|
||
```powershell
|
||
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:
|
||
|
||
```csharp
|
||
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 32–126. 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:
|
||
|
||
```text
|
||
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
|
||
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.
|