Files
CydandClaude Opus 4.8 8d2d0b71aa Phase 7: cockpit overlay generator, region extraction, and profile editor
Overlay generator (pure, tested) — RioJoy.Core/Overlay:
- FontFitter ports the legacy calc-fontsize auto-fit search (validated against a
  brute-force oracle); OverlayLayoutEngine ports create-data-layer's fit + h/v
  justification and adds per-region 90 deg CCW rotation; OverlayTemplate/Region +
  OverlayTemplateStore hold cell geometry/colour/rotation (regions.json).
- GoobieDataImporter parses the legacy .data label sheet into label rows.
- src/RioJoy.Overlay: SkiaSharp rasterizer (SkiaTextMeasurer shared with the engine
  so measured layout == drawn output; SkiaOverlayRenderer -> PNG;
  ProfileWallpaperGenerator). Verified end-to-end on the real cockpit art
  (regions.json + riojoy.png + TEST.data) by OverlayRenderIntegrationTests.
- RioJoy.Tray/WallpaperApplier applies via SystemParametersInfo; RioCoordinator
  generates+applies on profile activation (opt-in via AppConfig.OverlayTemplatePath).

Region geometry — tools/XcfRegionExtract parses riojoy.xcf (a GIMP-format binary
reader, no GIMP needed) into the 119-cell regions.json: per-layer offsets/size/
font/colour, with 90 deg CCW rotation on a-00 + b-10..b-1F. Base image riojoy.png.
NOTE: the wallpaper positions are a VGA chroma-split display artifact (6 displays),
not the cockpit's logical layout.

Mapping editor (first cut) — RioJoy.Tray/Editor/ProfileEditorForm renders the
config sheet's logical grid (SheetLayout parses the sheet CSV export). The iRIO
word is edited via ButtonBinding <-> RioMapEntry.Create (no hex bit-twiddling), with
a context-sensitive picker: keyboard keys by name (KeyCatalog VK names), joystick
Button N, hat direction, mouse/RIO-command enum names; modifiers only for keyboard.
Opened from the tray ("Edit profile..."). The sheet-grid layout is a starting point
that needs rework from a better-formatted source.

~220 xUnit tests green. Docs (PLAN.md, README) updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 17:13:48 -05:00

69 lines
2.1 KiB
C#

using RioJoy.Core.Overlay;
using Xunit;
namespace RioJoy.Core.Tests.Overlay;
/// <summary>
/// Regression tests over the committed cockpit <c>regions.json</c> extracted from
/// riojoy.xcf by tools/XcfRegionExtract. Anchors the extracted geometry and proves
/// the asset loads through <see cref="OverlayTemplateStore"/>.
/// </summary>
public class CockpitRegionsTests
{
private static OverlayTemplate Load() =>
OverlayTemplateStore.Load(TestRepo.CustomBackground("regions.json"));
[Fact]
public void Loads_WithExpectedCanvasAndCount()
{
OverlayTemplate t = Load();
Assert.Equal(2720, t.Width);
Assert.Equal(600, t.Height);
Assert.Equal(119, t.Regions.Count);
}
[Fact]
public void HasExpectedButtonCell()
{
OverlayRegion? b00 = Load().FindRegion("b-00");
Assert.NotNull(b00);
Assert.Equal(2558, b00!.X, 6);
Assert.Equal(428, b00.Y, 6);
Assert.Equal(153, b00.Width, 6);
Assert.Equal(53, b00.Height, 6);
Assert.Equal(45, b00.FontSizePx, 6);
Assert.Equal("Sans", b00.FontFamily);
}
[Fact]
public void IncludesNamedControlLabels()
{
OverlayTemplate t = Load();
Assert.NotNull(t.FindRegion("Throttle"));
Assert.NotNull(t.FindRegion("Trigger"));
Assert.NotNull(t.FindRegion("HatUp"));
}
[Fact]
public void VerticalCells_AreTaggedCcw90()
{
OverlayTemplate t = Load();
Assert.Equal(90.0, t.FindRegion("a-00")!.RotationDegrees);
Assert.Equal(90.0, t.FindRegion("b-10")!.RotationDegrees);
Assert.Equal(90.0, t.FindRegion("b-1F")!.RotationDegrees);
Assert.Null(t.FindRegion("b-00")!.RotationDegrees); // a normal upright cell
}
[Fact]
public void AllRegionsLieWithinCanvas()
{
OverlayTemplate t = Load();
Assert.All(t.Regions, r =>
{
Assert.True(r.X >= 0 && r.Y >= 0, $"{r.Name} has negative offset");
Assert.True(r.X + r.Width <= t.Width, $"{r.Name} overflows width");
Assert.True(r.Y + r.Height <= t.Height, $"{r.Name} overflows height");
});
}
}