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>
This commit is contained in:
@@ -0,0 +1,194 @@
|
||||
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) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user