Baseline: současný stav projektu Rozpisky

První commit — WPF aplikace pro generování výkresových rohových razítek
(.NET 10, SkiaSharp, ACadSharp, ClosedXML) včetně xUnit testů, DXF šablony,
log a dokumentace. Build výstupy (bin/, obj/, .vs/) jsou ignorovány.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-07 09:45:49 +02:00
co-authored by Claude Opus 5
commit a6377ab583
107 changed files with 349485 additions and 0 deletions
+96
View File
@@ -0,0 +1,96 @@
using Rozpisky.Dxf;
using Xunit;
namespace Rozpisky.Tests;
/// <summary>
/// Šrafy (HATCH) s hranovými hranicemi (úsečky, oblouky, elipsy) dřív se tiše přeskočily,
/// takže tečky a oblé výplně v logách chyběly. Hodnoty jsou předpočítané ze skutečných DXF.
/// </summary>
public class HatchTesty
{
private static RecordingRenderer Prehraj(string logo)
{
var rec = new RecordingRenderer();
new DxfTemplate(TestData.Logo(logo)).Replay(rec);
return rec;
}
private static (double W, double H, double Cx, double Cy) Bbox(IReadOnlyList<(double X, double Y)> loop)
{
double ax = double.MaxValue, ay = double.MaxValue, bx = double.MinValue, by = double.MinValue;
foreach (var (x, y) in loop)
{
ax = Math.Min(ax, x); ay = Math.Min(ay, y);
bx = Math.Max(bx, x); by = Math.Max(by, y);
}
return (bx - ax, by - ay, (ax + bx) / 2, (ay + by) / 2);
}
[Fact]
public void Tesia_TeckaZKruhoveHrany_SeVyplni()
{
var rec = Prehraj("tesia.dxf");
// 2 SOLID hatche: obrys žárovky (polyline s bulge) + tečka (plná kruhová hrana r≈0,78).
var smycky = rec.Fills.SelectMany(f => f).ToList();
Assert.Equal(2, smycky.Count);
// Tečka = menší smyčka; průměr ≈ 1,56 mm (plný kruh z jediné obloukové hrany).
var tecka = smycky.OrderBy(l => Bbox(l).W).First();
var (w, h, _, _) = Bbox(tecka);
Assert.InRange(w, 1.50, 1.62);
Assert.InRange(h, 1.50, 1.62);
}
[Fact]
public void SudopBrno_MichaneUseckoveAObloukoveHrany_DajiUzavreneSmycky()
{
var rec = Prehraj("sudopBrno.dxf");
// 6 SOLID šraf, všechny jen s hranovými cestami (úsečky + oblouky) → dřív 0 výplní.
// Referenční bboxy spočtené nezávisle hustým vzorkováním raw dat hran (512 dílků/oblouk).
Assert.Equal(6, rec.Fills.Count);
var smycky = rec.Fills.SelectMany(f => f).ToList();
Assert.Equal(7, smycky.Count); // jeden hatch má 2 cesty, ostatní po jedné
Assert.All(smycky, l => Assert.True(l.Count >= 3));
// Nezávisle na pořadí (draw order přes SortEntitiesTable může hatche přeuspořádat) ověříme,
// že existují smyčky s referenčními bboxy včetně plné kružnice r=3,825 (7,649²) z jediné
// obloukové hrany a řetězů úsečka+oblouk vč. ccw=false (zrcadlené úhly). Chybné znaménko/
// otočení hrany nebo ztracená kružnice by referenci znatelně změnily.
MaSmyckuBbox(smycky, 7.649, 7.649);
MaSmyckuBbox(smycky, 5.571, 0.626);
MaSmyckuBbox(smycky, 2.092, 0.066);
MaSmyckuBbox(smycky, 7.807, 1.085);
MaSmyckuBbox(smycky, 11.290, 1.733);
}
private static void MaSmyckuBbox(IReadOnlyList<IReadOnlyList<(double X, double Y)>> smycky, double refW, double refH)
=> Assert.Contains(smycky, l =>
{
var (w, h, _, _) = Bbox(l);
return w >= refW - 0.1 && w <= refW + 0.02 && h >= refH - 0.1 && h <= refH + 0.02;
});
[Fact]
public void Signalprojekt_KruhovaAEliptickaHrana_SeVyplni()
{
var rec = Prehraj("signalprojekt.dxf");
Assert.Equal(2, rec.Fills.Count);
var smycky = rec.Fills.SelectMany(f => f).ToList();
// Eliptická cesta: poloosy 1,16 a 0,759·1,16 → bbox ≈ 1,76 × 2,32 (hlavní osa svisle).
Assert.Contains(smycky, l =>
{
var (w, h, _, _) = Bbox(l);
double lo = Math.Min(w, h), hi = Math.Max(w, h);
return Math.Abs(hi - 2.32) < 0.05 && Math.Abs(lo - 1.76) < 0.05;
});
}
[Fact]
public void PolylinioveHranice_BezRegrese()
{
// exprojekt: 3 SOLID šrafy čistě s polyline hranicemi musí zůstat beze změny.
var rec = Prehraj("exprojekt.dxf");
Assert.Equal(3, rec.Fills.Count);
Assert.All(rec.Fills, f => Assert.All(f, l => Assert.True(l.Count >= 3)));
}
}