Files
Rozpisky/Rozpisky.Tests/SrafaExportTesty.cs
T
marekandClaude Opus 5 1e5a552c13 Oprava: exportovaná šrafa šla ven bez definice vzoru a ve špatném měřítku
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>
2026-09-07 15:08:54 +02:00

112 lines
4.1 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 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);
}
}
}