Files
Rozpisky/Rozpisky.Tests/EntityTesty.cs
T
marekandClaude Opus 5 a900acc7bb Entity: rozvinout bloky, doplnit elipsy a hlásit, co program neumí
Switch v Replay neměl default větev: INSERT, ELLIPSE, POINT, SOLID, 3DFACE
a POLYLINE3D propadly bez akce a bez záznamu. V dodávaných souborech žádná
z nich není, ale uživatel si přidává vlastní loga – to nejhorší, co se mu může
stát, je logo, které se nevykreslí a nikdo mu to neřekne.

- INSERT se rozvine rekurzivně (vztažný bod, měřítko os, natočení), s ochranou
  proti bloku vkládajícímu sám sebe. Uvnitř bloku se hladina „0" a barva
  ByBlock berou od vkládající entity – proto jde stejný blok vložit dvakrát
  a pokaždé v jiné barvě.
- TransformRenderer umí celou afinní transformaci, ne jen měřítko a posun.
  Oblouk zůstane obloukem jen u podobnosti bez zrcadlení; jinak by z kružnice
  byla elipsa, kterou kreslicí rozhraní nezná, tak se rozloží na lomenou čáru.
- ELLIPSE, POINT, SOLID, 3DFACE, POLYLINE3D se kreslí. U SOLID pozor na
  prohozený 3. a 4. roh, jinak vyjdou přesýpací hodiny.
- Zbytek se počítá do DxfTemplate.NezpracovaneEntity a souhrn exportu (PDF
  i DXF) ho vypíše. Export se kvůli tomu nezastaví.
- Spline se dělí podle délky (dřív pevných 64 dílků – málo pro dlouhé křivky,
  zbytečně moc pro krátké).
- Verze výstupního DXF se zafixovala na AC1027 (jako zdrojová šablona). Dřív ji
  určoval default ACadSharp, který může upgrade balíčku tiše změnit.

Šablona i všech 9 log projdou s prázdným počítadlem (test). Render log do PNG
je pixel po pixelu shodný s předchozím stavem. 114 testů zelených.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 14:46:02 +02:00

195 lines
6.7 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using ACadSharp;
using ACadSharp.Entities;
using ACadSharp.Tables;
using CSMath;
using Rozpisky.Dxf;
using Rozpisky.Rendering;
using Xunit;
using AcColor = ACadSharp.Color;
namespace Rozpisky.Tests;
/// <summary>
/// Entity, které dřív propadly <c>switch</c>em bez <c>default</c> větve, a tedy zmizely tiše:
/// vložené bloky (INSERT), elipsy, body, vyplněné čtyřúhelníky. Uživatel si přidává vlastní
/// loga, takže „nevykreslí se a nikdo se to nedozví" je nejhorší možné chování.
/// </summary>
public class EntityTesty
{
/// <summary>Přehraje dokument sestavený v paměti přes dočasný soubor (DxfTemplate čte ze souboru).</summary>
private static (RecordingRenderer G, IReadOnlyDictionary<string, int> Nezpracovane) Prehraj(CadDocument doc)
{
string cesta = System.IO.Path.Combine(System.IO.Path.GetTempPath(),
"rozpisky-entity-" + Guid.NewGuid().ToString("N") + ".dxf");
try
{
ACadSharp.IO.DxfWriter.Write(cesta, doc, binary: true);
var tpl = new DxfTemplate(cesta);
var g = new RecordingRenderer();
tpl.Replay(g);
return (g, tpl.NezpracovaneEntity);
}
finally
{
try { System.IO.File.Delete(cesta); } catch (System.IO.IOException) { }
}
}
/// <summary>Dokument s jedním blokem (čtvercová čára 0,010,10) a jeho vložením.</summary>
private static CadDocument SBlokem(Action<Insert> uprav, AcColor? barvaCary = null)
{
var doc = new CadDocument();
var blok = new BlockRecord("ctverec");
blok.Entities.Add(new Line(new XYZ(0, 0, 0), new XYZ(10, 0, 0))
{
Color = barvaCary ?? AcColor.ByLayer,
});
doc.BlockRecords.Add(blok);
var ins = new Insert(blok);
uprav(ins);
doc.Entities.Add(ins);
return doc;
}
[Fact]
public void VsechnaDodavanaDataSeVykresliBezeZbytku()
{
// Šablona i všech 9 log musí projít beze zbytku jinak se v nich něco nekreslí.
var sablona = new DxfTemplate(TestData.Sablona);
sablona.Replay(new RecordingRenderer(), values: null, includeNoPrint: true);
Assert.Empty(sablona.NezpracovaneEntity);
foreach (var logo in TestData.VsechnaLoga)
{
var tpl = new DxfTemplate(TestData.Logo(logo));
tpl.Replay(new RecordingRenderer());
Assert.True(tpl.NezpracovaneEntity.Count == 0,
$"{logo}: nezpracované entity {string.Join(", ", tpl.NezpracovaneEntity.Select(p => $"{p.Key}×{p.Value}"))}");
}
}
[Fact]
public void VlozenyBlokSeRozvineNaSpravnemMiste()
{
var (g, nezpracovane) = Prehraj(SBlokem(ins => ins.InsertPoint = new XYZ(100, 50, 0)));
Assert.Empty(nezpracovane);
Assert.Equal(1, g.LineCount);
Assert.Equal((100, 50), g.Lines[0].Od);
Assert.Equal((110, 50), g.Lines[0].Do);
}
[Fact]
public void MeritkoANatoceniBlokuSeProjevi()
{
var (g, _) = Prehraj(SBlokem(ins =>
{
ins.InsertPoint = new XYZ(0, 0, 0);
ins.XScale = 2;
ins.YScale = 2;
ins.Rotation = Math.PI / 2; // 90° → úsečka z osy X na osu Y
}));
Assert.Equal(1, g.LineCount);
var (od, doB) = g.Lines[0];
Assert.Equal(0, od.X, 6);
Assert.Equal(0, od.Y, 6);
Assert.Equal(0, doB.X, 6);
Assert.Equal(20, doB.Y, 6);
}
[Fact]
public void BarvaByBlockSeVezmeZVkladajiciEntity()
{
// Tohle je smysl ByBlock: stejný blok vložený dvakrát může mít pokaždé jinou barvu.
var (g, _) = Prehraj(SBlokem(
ins => { ins.InsertPoint = new XYZ(0, 0, 0); ins.Color = new AcColor(5); },
barvaCary: AcColor.ByBlock));
// Stavů jsou dva: jeden za samotný INSERT, druhý za úsečku uvnitř bloku zajímá nás ten druhý.
var stav = g.PosledniStav;
Assert.Equal(BarvaDruh.Index, stav.Barva.Druh);
Assert.Equal(5, stav.Barva.Index);
}
[Fact]
public void ElipsaSeVykresliJakoLomenaCara()
{
var doc = new CadDocument();
doc.Entities.Add(new Ellipse
{
Center = new XYZ(0, 0, 0),
MajorAxisEndPoint = new XYZ(10, 0, 0),
RadiusRatio = 0.5,
StartParameter = 0,
EndParameter = 2 * Math.PI,
});
var (g, nezpracovane) = Prehraj(doc);
Assert.Empty(nezpracovane);
var body = Assert.Single(g.Polylines);
Assert.True(body.Count >= 8);
// Body musí ležet na elipse s poloosami 10 a 5.
foreach (var (x, y) in body)
Assert.Equal(1.0, x * x / 100 + y * y / 25, 3);
}
[Fact]
public void SolidSeVyplniVeSpravnemPoradiRohu()
{
// V DXF jsou 3. a 4. roh proti obvodu prohozené při špatném pořadí vyjde přesýpací hodiny.
var doc = new CadDocument();
doc.Entities.Add(new Solid
{
FirstCorner = new XYZ(0, 0, 0),
SecondCorner = new XYZ(10, 0, 0),
ThirdCorner = new XYZ(0, 10, 0),
FourthCorner = new XYZ(10, 10, 0),
});
var (g, nezpracovane) = Prehraj(doc);
Assert.Empty(nezpracovane);
var smycky = Assert.Single(g.Fills);
var body = Assert.Single(smycky);
Assert.Equal(new[] { (0.0, 0.0), (10.0, 0.0), (10.0, 10.0), (0.0, 10.0) },
body.Take(4).Select(p => (Math.Round(p.X, 6), Math.Round(p.Y, 6))));
}
[Fact]
public void NeznamaEntitaSeSpocitaMistoTichehoZahozeni()
{
var doc = new CadDocument();
doc.Entities.Add(new Ray { StartPoint = new XYZ(0, 0, 0), Direction = new XYZ(1, 0, 0) });
var (g, nezpracovane) = Prehraj(doc);
Assert.Empty(g.Polylines);
Assert.Equal(0, g.LineCount);
Assert.Equal(1, nezpracovane.Values.Sum());
}
[Fact]
public void PocitadloSeMeziPruchodyNehromadi()
{
var doc = new CadDocument();
doc.Entities.Add(new Ray { StartPoint = new XYZ(0, 0, 0), Direction = new XYZ(1, 0, 0) });
string cesta = System.IO.Path.Combine(System.IO.Path.GetTempPath(),
"rozpisky-entity-" + Guid.NewGuid().ToString("N") + ".dxf");
try
{
ACadSharp.IO.DxfWriter.Write(cesta, doc, binary: true);
var tpl = new DxfTemplate(cesta);
tpl.Replay(new RecordingRenderer());
tpl.Replay(new RecordingRenderer());
Assert.Equal(1, tpl.NezpracovaneEntity.Values.Sum());
}
finally
{
try { System.IO.File.Delete(cesta); } catch (System.IO.IOException) { }
}
}
}