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>
This commit is contained in:
@@ -51,6 +51,6 @@ Needituj produkční data: `Rozpisky/Podklady/Rozpiska.dxf`, `vzorky/schema.dxf`
|
||||
3. Ověř:
|
||||
`dotnet build "Rozpisky.sln" -nologo -v q -clp:ErrorsOnly` a
|
||||
`dotnet test "Rozpisky.Tests\Rozpisky.Tests.csproj" -nologo -v q`
|
||||
(baseline je 114 zelených testů — nesmí ubýt).
|
||||
(baseline je 119 zelených testů — nesmí ubýt).
|
||||
4. V odpovědi vrať: co jsi změnil (soubor:řádek), proč, a výsledek buildu a testů. Když něco
|
||||
neprošlo, řekni to rovnou i s chybovou hláškou — nezakrývej to.
|
||||
|
||||
@@ -18,7 +18,7 @@ dotnet test "C:\Users\marek\_Osobní\C sharp\EXE\Rozpisky\Rozpisky.Tests\Rozpis
|
||||
Testy spusť i tehdy, když build projde s varováními. Když build spadne, testy nespouštěj
|
||||
a rovnou hlas chyby buildu.
|
||||
|
||||
Baseline, se kterou porovnáváš: **build bez chyb a bez varování, 114 testů zelených, 0 přeskočených.**
|
||||
Baseline, se kterou porovnáváš: **build bez chyb a bez varování, 119 testů zelených, 0 přeskočených.**
|
||||
|
||||
Volitelně (jen když o to volající požádá) ověř, že aplikace naběhne:
|
||||
spusť `Rozpisky\bin\Debug\net10.0-windows\Rozpisky.exe`, počkej ~8 s, zkontroluj, že proces
|
||||
|
||||
@@ -36,7 +36,7 @@ nebo `Rozpisky/Xlsx/` jen popiš v odpovědi a nech je na volajícím.
|
||||
začneš měnit UI.
|
||||
2. `MainViewModel.cs` je velký; hledej v něm grepem, nečti ho celý zbytečně.
|
||||
3. Ověř: `dotnet build "Rozpisky.sln" -nologo -v q -clp:ErrorsOnly` a
|
||||
`dotnet test "Rozpisky.Tests\Rozpisky.Tests.csproj" -nologo -v q` (baseline 114 zelených).
|
||||
`dotnet test "Rozpisky.Tests\Rozpisky.Tests.csproj" -nologo -v q` (baseline 119 zelených).
|
||||
Build musí být **bez varování** — chyby v XAML se často projeví jen jako varování.
|
||||
4. WPF chyby v bindingu build nezachytí. Pokud změna může binding rozbít, řekni to explicitně
|
||||
a doporuč ruční spuštění aplikace.
|
||||
|
||||
@@ -11,7 +11,7 @@ dotnet test "Rozpisky.Tests\Rozpisky.Tests.csproj" -nologo -v q
|
||||
dotnet run --project "Rozpisky\Rozpisky.csproj"
|
||||
```
|
||||
|
||||
Baseline: build bez chyb a varování, **114 testů zelených**. Před hlášením hotovo obojí spustit
|
||||
Baseline: build bez chyb a varování, **119 testů zelených**. Před hlášením hotovo obojí spustit
|
||||
(nebo použít `/overit`).
|
||||
|
||||
## Architektura — jediné pravidlo, které se nesmí porušit
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -384,8 +384,23 @@ public sealed partial class DxfTemplate
|
||||
|| vzor.Equals("SOLID", StringComparison.OrdinalIgnoreCase))
|
||||
return default;
|
||||
|
||||
var definice = DefiniceVzoru(hatch.Pattern);
|
||||
return Vypln.Vzorovana(vzor, hatch.PatternScale, hatch.PatternAngle * RadToDeg,
|
||||
SrafaVzor.Cary(hatch.Pattern, smycky));
|
||||
definice, SrafaVzor.Cary(definice, smycky));
|
||||
}
|
||||
|
||||
/// <summary>Definiční čáry vzoru šrafy z entity. DXF je nese uvnitř <c>HATCH</c> (group 78 a dál),
|
||||
/// tedy už v efektivních hodnotách – měřítko a úhel z vlastností šrafy jsou v nich započítané.</summary>
|
||||
private static List<VzorCara> DefiniceVzoru(HatchPattern? vzor)
|
||||
{
|
||||
var vysledek = new List<VzorCara>();
|
||||
foreach (var cara in vzor?.Lines ?? new List<HatchPattern.Line>())
|
||||
vysledek.Add(new VzorCara(
|
||||
cara.Angle * RadToDeg,
|
||||
(cara.BasePoint.X, cara.BasePoint.Y),
|
||||
(cara.Offset.X, cara.Offset.Y),
|
||||
cara.DashLengths?.ToList() ?? new List<double>()));
|
||||
return vysledek;
|
||||
}
|
||||
|
||||
/// <summary>Popis hladiny pro kreslítko – celý, ať se dá ve výstupu postavit znovu
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using ACadSharp.Entities;
|
||||
using Rozpisky.Rendering;
|
||||
|
||||
namespace Rozpisky.Dxf;
|
||||
@@ -19,10 +18,10 @@ internal static class SrafaVzor
|
||||
|
||||
/// <summary>Čáry vzoru uvnitř zadaných smyček. Prázdné, když vzor není použitelný –
|
||||
/// volající pak plochu nechá bez výplně (lepší než ji zaplácnout načerno).</summary>
|
||||
public static IReadOnlyList<Usecka> Cary(HatchPattern? vzor, IReadOnlyList<Contour> smycky)
|
||||
public static IReadOnlyList<Usecka> Cary(IReadOnlyList<VzorCara>? definice, IReadOnlyList<Contour> smycky)
|
||||
{
|
||||
var vysledek = new List<Usecka>();
|
||||
if (vzor?.Lines is not { Count: > 0 }) return vysledek;
|
||||
if (definice is not { Count: > 0 }) return vysledek;
|
||||
|
||||
var polygony = Zplosti(smycky);
|
||||
if (polygony.Count == 0) return vysledek;
|
||||
@@ -30,7 +29,7 @@ internal static class SrafaVzor
|
||||
var (minX, minY, maxX, maxY) = Rozsah(polygony);
|
||||
if (maxX - minX <= 0 || maxY - minY <= 0) return vysledek;
|
||||
|
||||
foreach (var cara in vzor.Lines)
|
||||
foreach (var cara in definice)
|
||||
{
|
||||
PridejCaryVzoru(cara, polygony, minX, minY, maxX, maxY, vysledek);
|
||||
if (vysledek.Count >= MaxCar) break;
|
||||
@@ -42,10 +41,11 @@ internal static class SrafaVzor
|
||||
/// rovnoběžkami je vektor <c>Offset</c> ve světových souřadnicích: jeho kolmá složka určuje
|
||||
/// rozteč, podélná posouvá čárkování. U ANSI31 vychází přesně kolmý na směr 45°.</summary>
|
||||
private static void PridejCaryVzoru(
|
||||
HatchPattern.Line cara, List<List<(double X, double Y)>> polygony,
|
||||
VzorCara cara, List<List<(double X, double Y)>> polygony,
|
||||
double minX, double minY, double maxX, double maxY, List<Usecka> vysledek)
|
||||
{
|
||||
double dx = Math.Cos(cara.Angle), dy = Math.Sin(cara.Angle); // směr čáry
|
||||
double uhel = cara.UhelDeg * Math.PI / 180;
|
||||
double dx = Math.Cos(uhel), dy = Math.Sin(uhel); // směr čáry
|
||||
double nx = -dy, ny = dx; // kolmice
|
||||
|
||||
double ox = cara.Offset.X, oy = cara.Offset.Y;
|
||||
@@ -53,7 +53,7 @@ internal static class SrafaVzor
|
||||
if (Math.Abs(kolma) < 1e-9) return; // rovnoběžky by splynuly
|
||||
|
||||
// Kolik kroků pokryje bbox: promítni jeho rohy na kolmici.
|
||||
double zaklad = cara.BasePoint.X * nx + cara.BasePoint.Y * ny;
|
||||
double zaklad = cara.Zaklad.X * nx + cara.Zaklad.Y * ny;
|
||||
var projekce = new[]
|
||||
{
|
||||
minX * nx + minY * ny, maxX * nx + minY * ny,
|
||||
@@ -66,11 +66,11 @@ internal static class SrafaVzor
|
||||
int od = (int)Math.Floor(kOd) - 1, doK = (int)Math.Ceiling(kDo) + 1;
|
||||
if ((long)doK - od > MaxCar) return; // nesmyslně husté měřítko
|
||||
|
||||
var carky = Carky(cara.DashLengths);
|
||||
var carky = Carky(cara.Carky);
|
||||
|
||||
for (int k = od; k <= doK; k++)
|
||||
{
|
||||
double px = cara.BasePoint.X + k * ox, py = cara.BasePoint.Y + k * oy;
|
||||
double px = cara.Zaklad.X + k * ox, py = cara.Zaklad.Y + k * oy;
|
||||
foreach (var (t1, t2) in Uvnitr(polygony, px, py, dx, dy))
|
||||
foreach (var (u1, u2) in Orez(carky, t1, t2))
|
||||
{
|
||||
|
||||
@@ -165,14 +165,20 @@ public sealed class DxfProfileRenderer : IProfileRenderer
|
||||
if (loops.Count == 0) return;
|
||||
try
|
||||
{
|
||||
// Vzor šrafy se zapisuje zpátky jménem, měřítkem a úhlem – v CADu tak zůstane
|
||||
// editovatelný. Rozvinuté čáry (Vypln.Cary) jsou jen pro rastrové renderery.
|
||||
// Vzor šrafy se zapisuje zpátky i s definičními čarami – DXF je nese uvnitř entity
|
||||
// HATCH, a bez nich si cílový CAD vzor dosadí ze svých .pat, ve svých jednotkách
|
||||
// a měřítku. Rozvinuté čáry (Vypln.Cary) jsou naopak jen pro rastrové renderery.
|
||||
//
|
||||
// Měřítko a úhel zůstávají neutrální (1 a 0), protože definiční čáry jsou už
|
||||
// v efektivních hodnotách – měřítko i natočení jsou v nich započítané. 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 tak vzor s každým kolečkem roztáhl.
|
||||
var hatch = new Hatch
|
||||
{
|
||||
IsSolid = vypln.JeSolid,
|
||||
Pattern = new HatchPattern(vypln.JeSolid ? "SOLID" : vypln.Vzor!),
|
||||
PatternScale = vypln.JeSolid || vypln.Meritko <= 0 ? 1 : vypln.Meritko,
|
||||
PatternAngle = vypln.JeSolid ? 0 : vypln.UhelDeg * Math.PI / 180.0,
|
||||
Pattern = VzorProHatch(vypln),
|
||||
PatternScale = 1,
|
||||
PatternAngle = 0,
|
||||
};
|
||||
foreach (var loop in loops)
|
||||
{
|
||||
@@ -201,6 +207,28 @@ public sealed class DxfProfileRenderer : IProfileRenderer
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Vzor pro entitu HATCH: jméno a definiční čáry. Bez definičních čar má výstup jen
|
||||
/// jméno vzoru a cílový CAD si podle něj dosadí vlastní definici – v jiných jednotkách
|
||||
/// a jiném měřítku, takže šrafa vypadá v každém výkresu jinak.</summary>
|
||||
private static HatchPattern VzorProHatch(Vypln vypln)
|
||||
{
|
||||
var vzor = new HatchPattern(vypln.JeSolid ? "SOLID" : vypln.Vzor!);
|
||||
if (vypln.JeSolid) return vzor;
|
||||
|
||||
foreach (var cara in vypln.Definice ?? Array.Empty<VzorCara>())
|
||||
{
|
||||
var radek = new HatchPattern.Line
|
||||
{
|
||||
Angle = cara.UhelDeg * Math.PI / 180.0,
|
||||
BasePoint = new XY(cara.Zaklad.X, cara.Zaklad.Y),
|
||||
Offset = new XY(cara.Offset.X, cara.Offset.Y),
|
||||
};
|
||||
foreach (var d in cara.Carky) radek.DashLengths.Add(d);
|
||||
vzor.Lines.Add(radek);
|
||||
}
|
||||
return vzor;
|
||||
}
|
||||
|
||||
/// <summary>Směrový vektor osy X textu pro dané natočení – tím se natáčí MTEXT.</summary>
|
||||
private static XYZ SmerOsyX(double rotationDeg)
|
||||
{
|
||||
|
||||
@@ -56,23 +56,37 @@ public readonly record struct Edge(
|
||||
/// <summary>Úsečka ve výkresových mm – čára vzoru šrafy.</summary>
|
||||
public readonly record struct Usecka(double X1, double Y1, double X2, double Y2);
|
||||
|
||||
/// <summary>
|
||||
/// Jedna definiční čára vzoru šrafy = celá rodina rovnoběžek: směr <paramref name="UhelDeg"/>,
|
||||
/// jedna čára prochází <paramref name="Zaklad"/>, sousední jsou posunuté o vektor
|
||||
/// <paramref name="Offset"/> (kolmá složka = rozteč, podélná posouvá čárkování).
|
||||
/// <paramref name="Carky"/> = délky čárek a mezer (záporná = mezera); prázdné = plná čára.
|
||||
/// <para>Tohle je <b>skutečná definice vzoru</b>, ne jen jeho jméno. DXF ji nese uvnitř entity
|
||||
/// <c>HATCH</c>, takže je nutné ji zapsat i do výstupu – jinak si ji cílový CAD dosadí ze svých
|
||||
/// <c>.pat</c> souborů, ve svých jednotkách a měřítku, a šrafa vyjde pokaždé jinak.</para>
|
||||
/// </summary>
|
||||
public readonly record struct VzorCara(
|
||||
double UhelDeg, (double X, double Y) Zaklad, (double X, double Y) Offset, IReadOnlyList<double> Carky);
|
||||
|
||||
/// <summary>
|
||||
/// Druh výplně plochy. <c>default</c> = plná výplň (SOLID).
|
||||
/// <para>Nesolidní šrafa (ANSI31 apod.) se rastrovým rendererům předává už rozvinutá na
|
||||
/// <paramref name="Cary"/>, protože Skia vzory šraf neumí; DXF export naopak <paramref name="Cary"/>
|
||||
/// ignoruje a zapíše zpátky <c>HATCH</c> s původním jménem vzoru, měřítkem a úhlem, aby zůstal
|
||||
/// v CADu editovatelný. Dřív se každá šrafa kreslila jako plná výplň – z logotypu
|
||||
/// <c>md_sfdi.dxf</c> byl černý blok.</para>
|
||||
/// ignoruje a zapíše zpátky <c>HATCH</c> se jménem vzoru, měřítkem, úhlem a <b>definičními čarami</b>
|
||||
/// (<paramref name="Definice"/>), aby zůstal editovatelný a vypadal stejně jako ve zdroji. Dřív se
|
||||
/// každá šrafa kreslila jako plná výplň – z logotypu <c>md_sfdi.dxf</c> byl černý blok.</para>
|
||||
/// </summary>
|
||||
public readonly record struct Vypln(
|
||||
string? Vzor = null, double Meritko = 1, double UhelDeg = 0, IReadOnlyList<Usecka>? Cary = null)
|
||||
string? Vzor = null, double Meritko = 1, double UhelDeg = 0,
|
||||
IReadOnlyList<Usecka>? Cary = null, IReadOnlyList<VzorCara>? Definice = null)
|
||||
{
|
||||
/// <summary>Plná výplň – buď se vzor neuvádí (<c>default</c>), nebo je to SOLID.</summary>
|
||||
public bool JeSolid => string.IsNullOrEmpty(Vzor) || Vzor.Equals("SOLID", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
/// <summary>Šrafa se vzorem, už rozvinutá na čáry pro rastrové renderery.</summary>
|
||||
public static Vypln Vzorovana(string vzor, double meritko, double uhelDeg, IReadOnlyList<Usecka> cary)
|
||||
=> new(vzor, meritko, uhelDeg, cary);
|
||||
/// <summary>Šrafa se vzorem: definice pro DXF a z ní rozvinuté čáry pro rastr.</summary>
|
||||
public static Vypln Vzorovana(string vzor, double meritko, double uhelDeg,
|
||||
IReadOnlyList<VzorCara> definice, IReadOnlyList<Usecka> cary)
|
||||
=> new(vzor, meritko, uhelDeg, cary, definice);
|
||||
}
|
||||
|
||||
/// <summary>Obrys = zřetězené hrany (konec jedné = počátek další). <paramref name="Closed"/> = true →
|
||||
|
||||
@@ -132,14 +132,32 @@ public sealed class TransformRenderer : IProfileRenderer
|
||||
=> _inner.Fill(
|
||||
loops.Select(c => new Contour(
|
||||
_zachovavaOblouky ? c.Edges.Select(T).ToList() : Usecky(c), c.Closed)).ToList(),
|
||||
vypln.Cary is null
|
||||
? vypln
|
||||
: vypln with
|
||||
Vypln(vypln));
|
||||
|
||||
/// <summary>Přemapuje výplň spolu s geometrií. Šrafa se musí zmenšit se svou plochou –
|
||||
/// jinak logo zmenšené do rámečku dostane vzor v původní rozteči a v CADu z něj vyjde
|
||||
/// buď plná plocha, nebo pár čar přes celé logo.</summary>
|
||||
private Vypln Vypln(Vypln v)
|
||||
{
|
||||
Cary = vypln.Cary
|
||||
.Select(u => new Usecka(X(u.X1, u.Y1), Y(u.X1, u.Y1), X(u.X2, u.Y2), Y(u.X2, u.Y2)))
|
||||
.ToList(),
|
||||
});
|
||||
if (v.JeSolid) return v;
|
||||
return v with
|
||||
{
|
||||
Meritko = v.Meritko * _meritko,
|
||||
UhelDeg = v.UhelDeg + _uhelDeg,
|
||||
Cary = v.Cary?.Select(u => new Usecka(X(u.X1, u.Y1), Y(u.X1, u.Y1), X(u.X2, u.Y2), Y(u.X2, u.Y2))).ToList(),
|
||||
Definice = v.Definice?.Select(Vzor).ToList(),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>Definiční čára vzoru: vztažný bod je bod (celá transformace), rozteč je vektor
|
||||
/// (jen lineární část, bez posunu), délky čárek se škálují, směr se otočí.</summary>
|
||||
private VzorCara Vzor(VzorCara c) => c with
|
||||
{
|
||||
UhelDeg = c.UhelDeg + _uhelDeg,
|
||||
Zaklad = (X(c.Zaklad.X, c.Zaklad.Y), Y(c.Zaklad.X, c.Zaklad.Y)),
|
||||
Offset = (_a * c.Offset.X + _c * c.Offset.Y, _b * c.Offset.X + _d * c.Offset.Y),
|
||||
Carky = c.Carky.Select(d => d * _meritko).ToList(),
|
||||
};
|
||||
|
||||
/// <summary>Obrys rozložený na úsečky (pro transformace, které oblouk nezachovají).</summary>
|
||||
private List<Edge> Usecky(Contour c)
|
||||
|
||||
+5
-2
@@ -189,8 +189,11 @@ Implementace:
|
||||
- **Zarovnání textu `Aligned` / `Fit`** se aproximuje na `Left` – text se neroztáhne na cílovou délku.
|
||||
- **Typ čáry** se přenáší jako název, ale rastrové renderery kreslí vždy plnou čáru; čárkované styly
|
||||
se projeví až v DXF (v dodávaných souborech je stejně všude `Continuous`).
|
||||
- **Šrafy** se do DXF zapisují se jménem vzoru, měřítkem a úhlem, ale bez definice čar vzoru – CAD
|
||||
si ji dohledá ve svých `.pat`. Vzor, který cílový CAD nezná, se v něm nevykreslí.
|
||||
- **Šrafy** se do DXF zapisují i s definičními čarami vzoru (DXF je nese uvnitř entity `HATCH`),
|
||||
takže vypadají stejně jako ve zdroji nezávisle na tom, jaké `.pat` a jednotky má cílový CAD.
|
||||
Měřítko a úhel šrafy zůstávají neutrální (1 a 0) – efektivní hodnoty nese samotná definice.
|
||||
Důvod: ACadSharp při zápisu násobí rozteč vzoru hodnotou `PatternScale`, ale při čtení zpátky
|
||||
nedělí, takže s nenulovým měřítkem by se vzor s každým kolečkem roztáhl.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user