Nahlásil uživatel: šrafa v exportovaném DXF nedrží měřítko, takže na první pohled ani nevypadá jako šrafa, a v každém výkresu se chová jinak. Dvě příčiny: 1. Do výstupu šlo jen *jméno* vzoru (ANSI31), bez definičních čar — ověřeno na exportovaném souboru: definicnichCar=0. DXF přitom nese definici vzoru uvnitř entity HATCH; bez ní si ji cílový CAD dosadí ze svých .pat, ve svých jednotkách a měřítku. Odtud „v každém výkresu jinak". Vypln proto nese VzorCara (úhel, vztažný bod, rozteč, čárkování) a export ji zapisuje. 2. Vzor se neškáloval spolu s geometrií. Logo se do rámečku rozpisky vejde jen zmenšené (typicky ~0,1×), ale rozteč vzoru zůstávala původní — v CADu z toho byla buď plná plocha, nebo pár čar přes celé logo. TransformRenderer teď mapuje i definici: vztažný bod celou transformací, rozteč jako vektor (bez posunu), délky čárek a měřítko × měřítko transformace. Měřítko a úhel se do HATCH zapisují neutrální (1 a 0) a efektivní hodnoty nese definice. ACadSharp totiž při zápisu násobí rozteč hodnotou PatternScale, ale při čtení zpátky nedělí — s nenulovým měřítkem by se vzor s každým kolečkem desetkrát roztáhl (změřeno). Po opravě je rozteč v exportu shodná se zdrojem 1:1 a při zmenšení loga 0,1× klesne desetkrát, takže počet čar přes logo zůstává stejný. Náhledy všech 9 log jsou pixel po pixelu beze změny. 119 testů zelených. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
112 lines
4.1 KiB
C#
112 lines
4.1 KiB
C#
using System.IO;
|
||
using ACadSharp.Entities;
|
||
using ACadSharp.IO;
|
||
using Rozpisky.Dxf;
|
||
using Rozpisky.Rendering;
|
||
using Xunit;
|
||
|
||
namespace Rozpisky.Tests;
|
||
|
||
/// <summary>
|
||
/// Šrafa v exportovaném DXF. Vzor musí jít ven i s <b>definičními čarami</b> – DXF je nese uvnitř
|
||
/// entity <c>HATCH</c>, a bez nich si ho cílový CAD dosadí ze svých <c>.pat</c>, ve svých
|
||
/// jednotkách a měřítku. Pak vypadá v každém výkresu jinak a většinou tak hustě, že to ani
|
||
/// nevypadá jako šrafa. A protože se logo do rámečku zmenšuje, musí se s ním zmenšit i vzor.
|
||
/// </summary>
|
||
public class SrafaExportTesty : IDisposable
|
||
{
|
||
private readonly string _soubor = Path.Combine(Path.GetTempPath(), "rozpisky-srafa-" + Guid.NewGuid().ToString("N") + ".dxf");
|
||
|
||
public void Dispose()
|
||
{
|
||
try { File.Delete(_soubor); } catch (IOException) { }
|
||
GC.SuppressFinalize(this);
|
||
}
|
||
|
||
/// <summary>Přehraje logo do DXF (volitelně přes zmenšení do rámečku) a načte výsledek zpět.</summary>
|
||
private List<Hatch> Kolecko(double meritko)
|
||
{
|
||
var dxf = new DxfProfileRenderer();
|
||
IProfileRenderer cil = meritko == 1 ? dxf : new TransformRenderer(dxf, meritko, 0, 0);
|
||
new DxfTemplate(TestData.Logo("md_sfdi.dxf")).Replay(cil);
|
||
DxfWriter.Write(_soubor, dxf.Document, binary: true);
|
||
return DxfReader.Read(_soubor).Entities.OfType<Hatch>().ToList();
|
||
}
|
||
|
||
/// <summary>Rozteč rovnoběžek vzoru = kolmá složka posunu mezi sousedními čarami.</summary>
|
||
private static double Roztec(HatchPattern.Line cara)
|
||
{
|
||
double nx = -Math.Sin(cara.Angle), ny = Math.Cos(cara.Angle);
|
||
return Math.Abs(cara.Offset.X * nx + cara.Offset.Y * ny);
|
||
}
|
||
|
||
private static double RoztecZdroje()
|
||
{
|
||
var zdroj = DxfReader.Read(TestData.Logo("md_sfdi.dxf"))
|
||
.Entities.OfType<Hatch>().First(h => !h.IsSolid);
|
||
return Roztec(zdroj.Pattern!.Lines[0]);
|
||
}
|
||
|
||
[Fact]
|
||
public void ExportovanaSrafaNeseDefiniciVzoru()
|
||
{
|
||
var srafy = Kolecko(meritko: 1).Where(h => !h.IsSolid).ToList();
|
||
|
||
Assert.NotEmpty(srafy);
|
||
foreach (var h in srafy)
|
||
{
|
||
Assert.Equal("ANSI31", h.Pattern?.Name);
|
||
Assert.NotNull(h.Pattern);
|
||
Assert.NotEmpty(h.Pattern!.Lines); // tohle chybělo: vzor šel ven jen jménem
|
||
}
|
||
}
|
||
|
||
[Fact]
|
||
public void RoztecVzoruOdpovidaZdroji()
|
||
{
|
||
var srafa = Kolecko(meritko: 1).First(h => !h.IsSolid);
|
||
Assert.Equal(RoztecZdroje(), Roztec(srafa.Pattern!.Lines[0]), 9);
|
||
}
|
||
|
||
[Fact]
|
||
public void ZmensenimLogaSeZmensiIVzor()
|
||
{
|
||
// Logo se do rámečku rozpisky vejde jen zmenšené. Kdyby si vzor nechal původní rozteč,
|
||
// vyšla by v CADu buď plná plocha, nebo pár čar přes celé logo.
|
||
const double meritko = 0.1;
|
||
var srafa = Kolecko(meritko).First(h => !h.IsSolid);
|
||
|
||
Assert.Equal(RoztecZdroje() * meritko, Roztec(srafa.Pattern!.Lines[0]), 9);
|
||
}
|
||
|
||
[Fact]
|
||
public void MeritkoAUhelZustavajiNeutralni()
|
||
{
|
||
// ACadSharp při zápisu násobí rozteč vzoru hodnotou PatternScale, ale při čtení zpátky
|
||
// nedělí – s nenulovým měřítkem by se vzor s každým kolečkem roztáhl. Efektivní hodnoty
|
||
// proto nese samotná definice a měřítko s úhlem zůstávají neutrální.
|
||
var srafa = Kolecko(meritko: 1).First(h => !h.IsSolid);
|
||
|
||
Assert.Equal(1, srafa.PatternScale, 9);
|
||
Assert.Equal(0, srafa.PatternAngle, 9);
|
||
}
|
||
|
||
[Fact]
|
||
public void PlnaVyplnZustavaSolidBezVzoru()
|
||
{
|
||
// md_sfdi má všechny šrafy vzorované, plné výplně jsou v ostatních logách.
|
||
var dxf = new DxfProfileRenderer();
|
||
new DxfTemplate(TestData.Logo("exprojekt.dxf")).Replay(dxf);
|
||
DxfWriter.Write(_soubor, dxf.Document, binary: true);
|
||
var solidni = DxfReader.Read(_soubor).Entities.OfType<Hatch>().ToList();
|
||
|
||
Assert.NotEmpty(solidni);
|
||
foreach (var h in solidni)
|
||
{
|
||
Assert.True(h.IsSolid);
|
||
Assert.Equal("SOLID", h.Pattern?.Name);
|
||
Assert.Empty(h.Pattern!.Lines);
|
||
}
|
||
}
|
||
}
|