From 10548b56986b5d1bf7de0d4bc10331f3c6beeaeb Mon Sep 17 00:00:00 2001 From: Josef Marek Date: Mon, 7 Sep 2026 14:49:34 +0200 Subject: [PATCH] =?UTF-8?q?Doku:=20kreslic=C3=AD=20rozhran=C3=AD=20po=20zm?= =?UTF-8?q?=C4=9Bn=C3=A1ch,=20tabulka=20entit=20a=20zn=C3=A1m=C3=A1=20omez?= =?UTF-8?q?en=C3=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ROZPISKY.md §3 popisuje nové rozhraní (KresliciStav, Vypln, rozšířený TextStyle) a hlavně *proč* barva nese původ, ne jen RGB. Přibyla tabulka DXF entit, které se čtou, a seznam známých omezení — inline kódy MTEXTu, zarovnání Aligned/Fit, typy čar, dohledávání vzoru šrafy v CADu. Opraveno tvrzení, že tloušťka čáry je jen Layer.LineWeight; zapisuje se i na entitu. CLAUDE.md doplněno druhé pravidlo architektury: co má přežít cestu DXF → model → DXF, musí protéct rozhraním — obcházet ho zpátky na CadDocument šablony je porušení toho prvního. Baseline testů 57 → 114 (i v subagentech). Co-Authored-By: Claude Opus 5 --- .claude/agents/render-dxf.md | 2 +- .claude/agents/verifikator.md | 2 +- .claude/agents/wpf-ui.md | 2 +- CLAUDE.md | 13 +++-- docs/ROZPISKY.md | 93 +++++++++++++++++++++++++++++------ 5 files changed, 92 insertions(+), 20 deletions(-) diff --git a/.claude/agents/render-dxf.md b/.claude/agents/render-dxf.md index 9b1454a..63b20a1 100644 --- a/.claude/agents/render-dxf.md +++ b/.claude/agents/render-dxf.md @@ -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 57 zelených testů — nesmí ubýt). + (baseline je 114 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. diff --git a/.claude/agents/verifikator.md b/.claude/agents/verifikator.md index 42103e6..5536cd7 100644 --- a/.claude/agents/verifikator.md +++ b/.claude/agents/verifikator.md @@ -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í, 57 testů zelených, 0 přeskočených.** +Baseline, se kterou porovnáváš: **build bez chyb a bez varování, 114 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 diff --git a/.claude/agents/wpf-ui.md b/.claude/agents/wpf-ui.md index 3c5a2d9..a207945 100644 --- a/.claude/agents/wpf-ui.md +++ b/.claude/agents/wpf-ui.md @@ -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 57 zelených). + `dotnet test "Rozpisky.Tests\Rozpisky.Tests.csproj" -nologo -v q` (baseline 114 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. diff --git a/CLAUDE.md b/CLAUDE.md index b1daf53..f696ac9 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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í, **57 testů zelených**. Před hlášením hotovo obojí spustit +Baseline: build bez chyb a varování, **114 testů zelených**. Před hlášením hotovo obojí spustit (nebo použít `/overit`). ## Architektura — jediné pravidlo, které se nesmí porušit @@ -21,8 +21,15 @@ Tatáž kresba teče na obrazovku, do PNG, do PDF i do DXF. **Nový výstupní f `IProfileRenderer`, nikdy nový kreslicí kód.** Čtečka [`DxfTemplate`](Rozpisky/Dxf/DxfTemplate.cs) jen čte DXF šablonu a „přehrává" ji do tohoto rozhraní. -Podrobnosti (vrstvy, NuGet API, pravidla zápisu DXF) — [`ROZPISKY.md`](docs/ROZPISKY.md). Nečíst celý, -jen relevantní kapitolu. +Z toho plyne druhé pravidlo: **co má přežít cestu DXF → model → DXF, musí protéct rozhraním.** +Zdrojové soubory jsou zapsané indexovanými barvami AutoCADu, takže stav kresby +([`KresliciStav`](Rozpisky/Rendering/KresliciStav.cs)) nese barvu **i s původem** (ACI index / +true color / ByLayer / ByBlock), název textového stylu a vzor šrafy — ne jen výsledné RGB. +Nová vlastnost, kterou má export zachovat, patří do rozhraní; obcházet ho stranou (sáhnout +z rendereru zpátky na `CadDocument` šablony) je porušení prvního pravidla. + +Podrobnosti (vrstvy, NuGet API, pravidla zápisu DXF, tabulka čtených entit a známá omezení) — +[`ROZPISKY.md`](docs/ROZPISKY.md). Nečíst celý, jen relevantní kapitolu. ## Konvence diff --git a/docs/ROZPISKY.md b/docs/ROZPISKY.md index b93ecfd..68dd564 100644 --- a/docs/ROZPISKY.md +++ b/docs/ROZPISKY.md @@ -32,8 +32,13 @@ zvláštní `PlatformTarget` se nenastavuje. ### Zápis DXF – co je potřeba vědět -- **Tloušťka čáry** = `Layer.LineWeight` (enum, hodnota v 1/100 mm – standardní hodnoty: - `0,5,9,13,15,18,20,25,30,35,40,50,53,60,70,80,90,100,106,120,140,158,200,211`). +- **Tloušťka čáry** je enum v 1/100 mm (standardní hodnoty + `0,5,9,13,15,18,20,25,30,35,40,50,53,60,70,80,90,100,106,120,140,158,200,211`); zapisuje se + na hladinu (`Layer.LineWeight`) i na entitu, která ji přebíjí. Milimetry z kreslicího rozhraní + se přichytí k nejbližší standardní hodnotě. +- **Barva** se zapisuje ve stejném druhu, v jakém přišla ze zdroje: indexovaná číslem (group 62), + přímá jako RGB (group 420), `ByLayer`/`ByBlock` odkazem. Zplošťování na RGB by výkres v CADu + odřízlo od tabulky barev. - **Diakritika** se do DXF ukládá jako Unicode escape `\U+XXXX` (např. `š` → `\U+0161`) – AutoCAD i ostatní čtečky to čtou správně. - Pole, která byla v šabloně `ATTDEF`, se při vyplnění zapisují jako obyčejné `TEXT`/`MTEXT` entity @@ -50,13 +55,16 @@ Aplikace je členěná dle vrstev (MVVM + služby): ``` Rozpisky/ ├─ Rendering/ ← kreslicí vrstva (nezávislá na cíli) -│ ├─ IProfileRenderer.cs ← kreslicí rozhraní + HAlign/VAlign + TextStyle +│ ├─ IProfileRenderer.cs ← kreslicí rozhraní + HAlign/VAlign + TextStyle + Vypln │ ├─ SkiaProfileRenderer.cs ← výstup přes SkiaSharp (obrazovka / PNG / PDF) │ ├─ DxfProfileRenderer.cs ← výstup do DXF (ACadSharp) │ ├─ BoundsRenderer.cs ← spočítá bbox kresby (fit / umístění na A4) -│ └─ TransformRenderer.cs ← dekorátor: měřítko + posun (fit fragmentů/loga) +│ ├─ TransformRenderer.cs ← dekorátor: afinní transformace (fit loga, rozvinutí bloku) +│ ├─ KresliciStav.cs ← barva s původem (ACI/RGB/ByLayer), hladina, typ čáry, tloušťka +│ └─ AciPaleta.cs ← indexované barvy AutoCADu (ACI 0–255) → RGB ├─ Dxf/ ← čtení šablony, mapování hodnot, kódy │ ├─ DxfTemplate.cs ← načte DXF šablonu a přehraje ji do IProfileRenderer +│ ├─ SrafaVzor.cs ← rozvine vzor šrafy (ANSI31 apod.) na čáry │ ├─ RozpiskaRenderer.cs ← spojuje šablonu + hodnoty + exportéry (fasáda pro UI) │ ├─ RozpiskaValues.cs ← „tag DXF → hodnota" z projektu/přílohy/číselníků (reflexí dle mapovani.json) │ ├─ MapovaniStore.cs ← načtení mapování z mapovani.json (config vedle .exe) @@ -95,37 +103,94 @@ Vše se kreslí ve **výkresových milimetrech**, osa **Y nahoru** (jako v CAD/D public enum HAlign { Left, Center, Right } public enum VAlign { Baseline, Bottom, Middle, Top } -public readonly record struct TextStyle(string? FontFamily = null, bool Bold = false, - bool Italic = false, string? FontFile = null); +public enum BarvaDruh { ByLayer, ByBlock, Index, True } +public readonly record struct Barva(BarvaDruh Druh, byte Index, int Rgb); +public readonly record struct HladinaStav(string Nazev, Barva Barva, string TypCary = "Continuous", + double TloustkaMm = 0, bool Tisknout = true, bool Zapnuta = true); +public readonly record struct KresliciStav(HladinaStav Hladina, Barva Barva, + string? TypCary = null, double? TloustkaMm = null); + +public readonly record struct TextStyle(string? FontFamily = null, bool Bold = false, bool Italic = false, + string? FontFile = null, double ObliqueDeg = 0, + string? Nazev = null, bool JeShx = false); + +public readonly record struct Usecka(double X1, double Y1, double X2, double Y2); +public readonly record struct Vypln(string? Vzor = null, double Meritko = 1, double UhelDeg = 0, + IReadOnlyList? Cary = null); public interface IProfileRenderer { - void Layer(string name, int rgb, double lineWeightMm = 0.25); // barva 0xRRGGBB, tloušťka ByLayer + void Stav(in KresliciStav s); void Line(double x1, double y1, double x2, double y2); void Polyline(IReadOnlyList<(double X, double Y)> pts, bool closed = false); void Circle(double cx, double cy, double r); + void Stroke(Contour c); // obrys se zachovanými oblouky void Text(double x, double y, string text, double heightMm, HAlign h = HAlign.Left, VAlign v = VAlign.Baseline, double rotationDeg = 0, TextStyle style = default, double widthFactor = 1, double maxWidthMm = 0); - void Fill(IReadOnlyList> loops); // HATCH výplně (even-odd) + void Fill(IReadOnlyList loops, Vypln vypln = default); // HATCH výplně (even-odd) } ``` -- `rgb` je barva `0xRRGGBB` rozbalená z DXF (ByLayer i index/true-color). Bílá se kreslí černě - (bílá na bílém papíře by zmizela). +**Proč `KresliciStav` a ne `int rgb`.** Šablona i všech 9 log jsou zapsané výhradně +**indexovanými barvami AutoCADu** (group 62). Kdyby rozhraní neslo jen výsledné RGB, export by +z nich udělal natvrdo true color (group 420), výkres by v CADu přestal reagovat na tabulku barev +a `ByLayer` by se rozpadlo na kopie barvy hladiny. Barva proto nese **původ**, ne jen hodnotu: +rastrové renderery si vyžádají hotové RGB přes `RgbProKresbu()`, DXF export zapíše původní druh. +Ze stejného důvodu se přenáší celý popis hladiny – ve výstupu se staví z tabulky hladin, ne +z první entity, která na ní přistála. + +- **Paleta ACI** ([`AciPaleta`](../Rozpisky/Rendering/AciPaleta.cs)) je vlastní, ne z ACadSharp: + ta se od AutoCADu liší u 33 indexů, mimo jiné u 8 (v šabloně použitý) a u posledního indexu + většiny desítek odstínů, kde vrací barvu ze sousední skupiny (29 a 39 mají dokonce tutéž). + Rozsah 10–249 se dopočítává, ne vypisuje: 24 odstínů × 5 jasů × 2 sytosti. +- `TextStyle.Nazev` = jméno stylu ve zdroji („Popis základní"). Nese se dál, aby ho export zapsal + beze změny – jeden zdrojový styl = jeden styl ve výstupu. - `widthFactor` = horizontální měřítko písma (DXF width factor). `maxWidthMm` = šířka pro zalomení víceřádkového textu (0 = jen explicitní zlomy). +- **Bílá se kreslí černě** (bílá na bílém papíře by zmizela) – jen v rastrových rendererech; + do DXF jde barva beze změny. Implementace: - **`SkiaProfileRenderer`** – kreslí na `SKCanvas`. Dostane funkci `Map(mmX, mmY) → bod plátna` a `pxPerMm`. Text vzpřímeně (rotace opačně, plátno má Y dolů), fonty ze stylu/souboru s fallbackem. Je `IDisposable` (uvolní typefaces) – používat přes `using`. -- **`DxfProfileRenderer`** – pro každou hladinu založí `Layer` (barva + tloušťka), entity přidává do - `Document`. Výsledek zapíše `DxfWriter.Write(path, renderer.Document, binary: true)`. +- **`DxfProfileRenderer`** – staví ACadSharp entity do `Document`; hladiny, textové styly i barvy + podle zdroje. Verze výstupu je zafixovaná (`VerzeVystupu`), ať ji neurčí default balíčku. + Výsledek zapíše `DxfWriter.Write(path, renderer.Document, binary: true)`. - **`BoundsRenderer`** – nic nekreslí, jen sčítá min/max souřadnic (fit / vystředění na A4). -- **`TransformRenderer`** – dekorátor: na souřadnice aplikuje uniformní měřítko + posun. Slouží - k umístění vkládaného DXF fragmentu (logo / schéma) do vymezeného rámečku. +- **`TransformRenderer`** – dekorátor s afinní transformací: měřítko + posun (umístění loga do + rámečku), nebo měřítko os + natočení + posun (rozvinutí bloku, `ProBlok`). Oblouk zůstane + obloukem jen u podobnosti bez zrcadlení – jinak by z kružnice byla elipsa, kterou rozhraní nezná, + a tvar se rozloží na lomenou čáru. + +### Které DXF entity se čtou + +| Entita | Jak se kreslí | +|---|---| +| `LINE`, `CIRCLE`, `ARC` | přímo; oblouk analyticky, hladký v každém přiblížení | +| `LWPOLYLINE`, `POLYLINE` (2D) | lomená čára, s `bulge` jako obrys s kruhovými oblouky | +| `POLYLINE` (3D) | promítnuto do roviny XY | +| `SPLINE` | lomená čára, dílek ≈ 0,3 mm podle délky řídicího polygonu | +| `ELLIPSE` | lomená čára (rozhraní zná jen kruhové oblouky) | +| `TEXT`, `MTEXT`, `ATTDEF` | text se stylem, zarovnáním, sklonem a šířkovým faktorem | +| `HATCH` | plná výplň, nebo vzor rozvinutý na čáry ([`SrafaVzor`](../Rozpisky/Dxf/SrafaVzor.cs)) | +| `SOLID`, `3DFACE` | vyplněný čtyřúhelník (v DXF jsou 3. a 4. roh prohozené) | +| `POINT` | tečka | +| `INSERT` | rekurzivně rozvinutý blok: vztažný bod, měřítko os, natočení, `ByBlock` barvy | +| ostatní | nekreslí se, ale **spočítá se** do `DxfTemplate.NezpracovaneEntity`; souhrn exportu to vypíše | + +### Známá omezení + +- **Vnitřní formátovací kódy MTEXTu** (`\H` výška, `\C` barva, `\f` font, `\S` zlomek) se ztrácejí – + čte se `MText.PlainText`, který je odstraňuje. Zvlášť se čte jen `\W` (šířkový faktor), protože + ho ACadSharp v `PlainText` nechává a jinak by se vykreslil jako text. +- **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í. ---