Files
Rozpisky/docs/ROZPISKY.md
T
marekandClaude Opus 5 ba52486f53 Dokumentace k rozvržení podkladů a vrstvám číselníků
ROZPISKY.md §2 a §8, ROZHRANI.md §3.4 (a odškrtnutá otevřená otázka v §9),
CLAUDE.md. Baseline testů v promptech subagentů 36 -> 50.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 10:50:23 +02:00

14 KiB
Raw Blame History

Rozpisky generátor výkresových rohových razítek (C# / .NET)

Standalone WPF aplikace (náhrada původního excelového nástroje „Rozpisky 1.0.8"). Spravuje projekt, seznam příloh, revize a číselníky, a z DXF šablony rozpisky generuje náhled, PDF na A4, hromadný DXF (pole jako TEXT/MTEXT entity), seznam příloh do XLSX a přes připojený CAD i tisk rozvržení.

Celé kreslení stojí na jedné myšlence: existuje jedno kreslicí rozhraní (IProfileRenderer „kam se kreslí") a několik jeho implementací. Tatáž kresba teče na obrazovku (náhled), do PNG, do PDF i do DXF. Čtečka šablony (DxfTemplate) jen čte DXF a „přehrává" ho do tohoto rozhraní.


1. NuGet balíčky

Cílový framework: net10.0-windows, UseWPF=true, platforma AnyCPU. Připojení na běžící CAD a MS Excel jde přes COM (pozdní vazba přes dynamic) bitness běhového procesu se řídí hostitelem, zvláštní PlatformTarget se nenastavuje.

Balíček Verze K čemu Klíčové API
SkiaSharp 2.88.8 2D rasterizace + PDF. Kreslení geometrie/textu/obrázků; export PNG i PDF. SKCanvas, SKPaint, SKPath, SKBitmap, SKImage, SKTypeface, SKDocument.CreatePdf, SKFileWStream
SkiaSharp.Views.WPF 2.88.8 WPF plátno pro živý náhled (zoom/posun). Jen pro GUI headless export ho nepotřebuje. SKElement, OnPaintSurface(SKPaintSurfaceEventArgs)
ACadSharp 3.6.29 Čtení i zápis DXF (novější verze, výplně HATCH, XRecord metadata atributů). Nahradilo dřívější netDxf. DxfReader.Read, DxfWriter.Write, CadDocument, ACadSharp.Entities.*, ACadSharp.Tables.TextStyle
ClosedXML 0.105.0 Vyplnění mustru „Seznam příloh" (SEZNAM.xlsx) při exportu seznamu. XLWorkbook, IXLWorksheet, Cell(...).Value, SaveAs

Pozn. k ACadSharp: na rozdíl od netDxf vystaví font ze stylu i box-šířku víceřádkových atributů (XRecord, group 41), takže jde dlouhý text v poli správně zalomit. DXF se zapisuje binárně (DxfWriter.Write(..., binary: true)) výrazně menší soubor. Převod XLSX → PDF (tisk seznamu) jde přes nainstalovaný MS Excel (COM), ne přes ACadSharp/ClosedXML.

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).
  • 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 (žádné atributy bloku).
  • Objekty v netisknutelných hladinách (PlotFlag=0) se nevykreslují, ale ví se o nich kvůli místům pro loga (viz DxfTemplate.PlaceholderHladiny).

2. Struktura kódu

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
│  ├─ 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)
├─ Dxf/                       ← čtení šablony, mapování hodnot, kódy
│  ├─ DxfTemplate.cs          ← načte DXF šablonu a přehraje ji do IProfileRenderer
│  ├─ 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)
│  ├─ KodPrilohy.cs           ← 47-poziční strojový kód přílohy (manuál SŽ kap. 3)
│  ├─ Exporters.cs            ← PngA4 / PdfA4 / RenderView (kreslí libovolný Action<IProfileRenderer>)
│  └─ LogaResolver.cs         ← dohledání DXF log/schémat k příloze
├─ Cad/                       ← připojení na běžící CAD (COM) + aktualizace/tisk rozvržení
├─ Xlsx/                      ← export seznamu příloh (ClosedXML) + XLSX→PDF (Excel COM)
├─ Data/                      ← persistence: .rzp projekt, číselníky, atomický zápis configů
├─ Models/                    ← datový model (Projekt, Priloha, Revize, Ciselniky, Nastaveni…)
├─ ViewModels/                ← MainViewModel (stav, akce, dirty-flag, export)
├─ Views/ + Themes/ + Theming/ + Behaviors/  ← WPF UI (dark theme, grid chování)
├─ App.xaml(.cs)             ← vstupní bod + globální záchytná síť výjimek (error.log)
└─ MainWindow.xaml(.cs)      ← hlavní okno

Datové soubory jsou rozdělené podle toho, kdo je vlastní:

Kde Co Kdo zapisuje
vedle .exe (zdroj Rozpisky/Podklady/) Rozpiska.dxf, SEZNAM.xlsx, ciselniky.vychozi.json, mapovani.json, loga/, manuál SŽ (PDF) nikdo dodává se s programem, každá instalace přepíše
%APPDATA%\Rozpisky (UzivatelskaData) ciselniky.user.json, cad.json, layout.json, error.log, tisk_diag.log aplikace za běhu
kdekoli u uživatele projekty .rzp aplikace na vyžádání

Rozdělení existuje proto, aby přeinstalace novou verzí (nebo .exe v Program Files, kam se nesmí psát) nesmazala nic, co si uživatel nastřádal. Portable režim se zapíná souborem portable.txt vedle .exe pak se všechno vrátí do složky aplikace.


3. Kreslicí rozhraní

Vše se kreslí ve výkresových milimetrech, osa Y nahoru (jako v CAD/DXF).

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 interface IProfileRenderer
{
    void Layer(string name, int rgb, double lineWeightMm = 0.25); // barva 0xRRGGBB, tloušťka ByLayer
    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 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<IReadOnlyList<(double X, double Y)>> loops); // 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).
  • widthFactor = horizontální měřítko písma (DXF width factor). maxWidthMm = šířka pro zalomení víceřádkového textu (0 = jen explicitní zlomy).

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).
  • 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.

4. Čtečka šablony a fasáda pro UI

Nízkoúrovňová čtečka DxfTemplate:

var tpl = new DxfTemplate("Rozpiska.dxf");
tpl.Replay(g);                         // NÁHLED: pole se vykreslí jako jejich názvy (tagy)
tpl.Replay(g, values);                 // VYPLNĚNÍ: pole = hodnota; nevyplněné = prázdné
tpl.Replay(g, values, includeNoPrint); // + volitelně A4 rámeček !!NOPRINT (pro DXF export/tisk)

Replay přehraje statickou geometrii (Line/Polyline), popisky (Text/MText) a pole (ATTDEF z model space) do rozhraní. SheetFrame vrací A4 rámeček šablony (referenční obdélník pro vystředění), Placeholders vrací rámečky pro loga/schéma podle vyhrazených hladin, Bounds bbox kresby (pro fit vkládaného fragmentu).

Fasáda pro UI RozpiskaRenderer spojuje šablonu, mapování hodnot a exportéry na jedno místo (šablona se cachuje):

var r = new RozpiskaRenderer(Nastaveni.SablonaDxf);
Action<IProfileRenderer> draw = r.BuildDraw(projekt, priloha, revize, ciselniky,
                                            vyplnene: true, loga, ramecekA4: false);
r.ExportPdf(projekt, priloha, revize, ciselniky, "rozpiska.pdf", loga);   // A4 PDF vč. log
r.ExportPng(projekt, priloha, revize, ciselniky, "nahled.png",  loga);   // A4 PNG

Hodnoty polí sestavuje RozpiskaValues: mapování „pole programu → DXF atribut" není v kódu, čte se z mapovani.json a hodnoty se dotahují reflexí. K tomu se algoritmicky doplní 47-poziční strojový kód (K1K47, viz KodPrilohy).


5. Export na A4 (Exporters)

Exporters vykreslí libovolný Action<IProfileRenderer>:

Exporters.PngA4(draw, "rozpiska.png", dpi: 200, frame: sheetFrame);  // A4 raster
Exporters.PdfA4(draw, "rozpiska.pdf", frame: sheetFrame);            // A4 vektor (PDF, 1:1)
Exporters.RenderView(canvas, wpx, hpx, draw, frame, zoom, panX, panY); // živý náhled na SKElement

Princip A4: referenční obdélník (frame = A4 rámeček šablony, jinak BoundsRenderer) se mapuje 1:1 a vystředí na papír; zmenší se jen tehdy, když se do tisknutelné plochy nevejde. PDF se kreslí přes SKDocument.CreatePdf (1 mm = 72/25,4 bodu; text vektorový, tloušťky v reálných mm).


6. Demo data (DemoData)

DemoData naplní úvodní stav při startu: SeedProjekt(vm) (ukázkový projekt + přílohy), SeedCiselniky(c) a SeedKraje(c) (výchozí číselníky, pokud ciselniky.json chybí).


7. Toky exportu (přehled)

Akce Vstup Výstup Přes
Náhled rozpisky příloha okno se SKElement RozpiskaRenderer.BuildDrawExporters.RenderView
Export do PDF (výběr/vše) přílohy Rozpisky\*.pdf RozpiskaRenderer.ExportPdf (per-item, chyby se sbírají)
Export do DXF (hromadně) přílohy __ Otevřená\_rozpisky.dxf dlaždicová mřížka do DxfProfileRendererDxfWriter.Write
Export seznamu objekt SEZNAM_<objekt>.xlsx vedle .rzp SeznamExporter (ClosedXML mustr)
Tisk seznamu dřív exportovaný XLSX Přílohy\*.pdf ExcelToPdf (MS Excel COM)
Aktualizace/tisk rozvržení přílohy přejmenování/tisk v CADu Cad/CadLayoutService, Cad/CadTiskService (COM)

Aplikace nemá CLI režim vše jede z GUI. Neočekávané výjimky zachytává globální síť v App.xaml.cs a zapisuje je do error.log vedle .exe.


8. Persistence a robustnost

  • Projekt .rzp (ProjektStore) JSON s exkluzivním zámkem drženým po dobu otevření (ostatní procesy smí jen číst). Zápis serializuje před useknutím souboru, aby pád nezanechal prázdný soubor.
  • JSON configy (ciselniky.user.json, cad.json, layout.json v uživatelské složce, mapovani.json vedle .exe) jdou přes JsonConfigFile: atomický zápis (přes .tmp) a při poškozeném souboru záloha do .bak + upozornění (místo tichého přepsání výchozími daty).
  • Číselníky ve dvou vrstvách (CiselnikyStore) dodávaná ciselniky.vychozi.json (jen ke čtení) a uživatelská ciselniky.user.json, kde je uložený jen rozdíl proti ní (přidané/upravené položky + klíče skrytých). Nová verze programu tak smí dodávaný číselník měnit, aniž by uživatel přišel o svoje záznamy.
  • Migrace starých poloh (Migrace) data zapisovaná dřív vedle .exe se při startu přenesou do uživatelské složky; originál se jen přejmenuje na .migrovano.
  • Zamčený cíl exportu před zápisem PDF/XLSX/DXF se ověří, zda soubor nedrží jiný program (Excel, prohlížeč PDF), a nabídne Pokračovat/Přerušit.

9. Závislost na šabloně

Šablona je libovolný DXF, kde:

  • rám a mřížku tvoří LINE / LWPOLYLINE,
  • pevné popisky jsou TEXT / MTEXT,
  • vyplňovaná pole jsou ATTDEF v model space (jejich Tag = klíč do hodnot),
  • placeholdery pro loga jsou uzavřené polylinie na vyhrazených netisknutelných hladinách (Rozpiska_logo1…, Rozpiska_Orientační schéma) logo/schéma se fitne do jejich ohraničení, samotný placeholder se nekreslí (PlotFlag=0),
  • A4 hranici papíru vymezuje rámeček na hladině !!NOPRINT.

Tím je celý vzhled rozpisky daný datovým souborem a mapováním (Rozpiska.dxf + mapovani.json), ne kódem pro jinou rozpisku stačí jiný DXF a mapování.