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>
195 lines
6.7 KiB
C#
195 lines
6.7 KiB
C#
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,0–10,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) { }
|
||
}
|
||
}
|
||
}
|