Šablona i všech 9 log jsou zapsané výhradně indexovanými barvami AutoCADu (group 62) – true color v nich není ani jednou. Kreslicí rozhraní přitom neslo jen `int rgb`, takže se index i ByLayer ztratily už při čtení a export zapisoval všechno jako natvrdo RGB (group 420). Výkres v CADu pak přestal reagovat na tabulku barev a barva hladiny se rozpadla na kopie na entitách. `IProfileRenderer.Layer(name, rgb, mm)` se mění na `Stav(in KresliciStav)`. Stav nese barvu i s původem (`Barva`: ACI index / true color / ByLayer / ByBlock), typ čáry, tloušťku a celý popis hladiny (`HladinaStav`) – hladina se ve výstupu staví z tabulky hladin, ne z první entity, která na ní přistála. Rámeček `!!NOPRINT` proto zůstane netisknutelný. Paleta ACI je vlastní (`AciPaleta`), ne z ACadSharp: ta se od AutoCADu liší u 33 indexů, mimo jiné u 8 (použitý v šabloně) a u posledního indexu většiny desítek odstínů, kde vrací barvu ze sousední skupiny – 29 a 39 tam dokonce dostanou tutéž barvu. Rozsah 10–249 se dopočítává, ne vypisuje. Co ověřují nové testy (93 zelených): - paleta proti AutoCADu i proti ACadSharp, se seznamem známých odchylek, - rozřešení ByLayer/ByBlock, tloušťky a typu čáry, - kolečko šablona → export → načtení: žádná entita není true color, indexy odpovídají zdroji, ByLayer zůstane ByLayer, hladiny mají barvu i příznak tisku ze zdrojové tabulky. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
99 lines
4.4 KiB
C#
99 lines
4.4 KiB
C#
namespace Rozpisky.Rendering;
|
||
|
||
/// <summary>
|
||
/// Paleta indexovaných barev AutoCADu (ACI 0–255). Index je to, co nese DXF group 62; na RGB se
|
||
/// překládá až při rasterizaci (náhled, PNG, PDF) – do DXF se zapisuje zpátky jako <b>číslo</b>,
|
||
/// takže se cestou tam a zpět nic neztratí.
|
||
/// <para>Rozsah 10–249 je dopočítaný, ne vypsaný: 24 odstínů po 15° × 5 jasů, každý ve variantě
|
||
/// sytá / poloviční sytost. Krajní hodnoty (0–9, 250–255) jsou tabulkou, protože do schématu
|
||
/// nezapadají.</para>
|
||
/// <para><b>Proč ne <c>ACadSharp.Color.GetIndexRGB</c>:</b> ta se od AutoCADu liší u indexů
|
||
/// 8 a 9 (o dva stupně šedi), u 250–254 a u posledního indexu každé skupiny odstínů (19, 29, …,
|
||
/// 249) – tam vrací hodnotu ze sousední skupiny, takže např. 29 a 39 dostanou stejnou barvu.
|
||
/// Index 8 je přitom v <c>Rozpiska.dxf</c> použitý. Viz <c>AciPaletaTesty</c>.</para>
|
||
/// </summary>
|
||
public static class AciPaleta
|
||
{
|
||
/// <summary>ACI 0–9. Index 0 (ByBlock) a 7 se kreslí podle pozadí – tady černá / bílá.</summary>
|
||
private static readonly int[] Zakladni =
|
||
{
|
||
0x000000, // 0 – ByBlock, samostatně se nekreslí
|
||
0xFF0000, 0xFFFF00, 0x00FF00, 0x00FFFF, 0x0000FF, 0xFF00FF, 0xFFFFFF,
|
||
0x414141, // 8 – tmavá šedá (ACadSharp tu má 0x808080)
|
||
0x808080, // 9 – šedá (ACadSharp tu má 0xC0C0C0)
|
||
};
|
||
|
||
/// <summary>ACI 250–255 – šedý klín zakončený bílou.</summary>
|
||
private static readonly int[] Sede = { 0x333333, 0x505050, 0x696969, 0x828282, 0xBEBEBE, 0xFFFFFF };
|
||
|
||
/// <summary>Jas pro pozici v desetici: pětice 100 / 65 / 50 / 30 / 15 %, každá dvakrát
|
||
/// (sudá pozice = plná sytost, lichá = poloviční).</summary>
|
||
private static readonly int[] Jasy = { 0xFF, 0xFF, 0xA5, 0xA5, 0x7F, 0x7F, 0x4C, 0x4C, 0x26, 0x26 };
|
||
|
||
/// <summary>RGB 0xRRGGBB pro daný ACI index. Mimo rozsah 0–255 → černá.</summary>
|
||
public static int Rgb(int index)
|
||
{
|
||
if (index is < 0 or > 255) return 0x000000;
|
||
if (index <= 9) return Zakladni[index];
|
||
if (index >= 250) return Sede[index - 250];
|
||
return Odstin(index);
|
||
}
|
||
|
||
/// <summary>Nejbližší ACI index k RGB (pro cizí soubory zapsané v true color). Prohledává
|
||
/// 1–255 podle eukleidovské vzdálenosti v RGB – hrubé, ale pro převod stačí.</summary>
|
||
public static byte NejblizsiIndex(int rgb)
|
||
{
|
||
int r = (rgb >> 16) & 0xFF, g = (rgb >> 8) & 0xFF, b = rgb & 0xFF;
|
||
byte nej = 7;
|
||
long nejVzdalenost = long.MaxValue;
|
||
for (int i = 1; i <= 255; i++)
|
||
{
|
||
int c = Rgb(i);
|
||
long dr = r - ((c >> 16) & 0xFF), dg = g - ((c >> 8) & 0xFF), db = b - (c & 0xFF);
|
||
long d = dr * dr + dg * dg + db * db;
|
||
if (d >= nejVzdalenost) continue;
|
||
nejVzdalenost = d;
|
||
nej = (byte)i;
|
||
if (d == 0) break;
|
||
}
|
||
return nej;
|
||
}
|
||
|
||
/// <summary>Barva odstínové části palety (10–249): odstín podle desítky, jas a sytost
|
||
/// podle pozice v ní. Lichá pozice = tatáž barva smíchaná do poloviny s jasem
|
||
/// (odbarvená), proto <c>c + (jas − c) / 2</c>.</summary>
|
||
private static int Odstin(int index)
|
||
{
|
||
int skupina = (index - 10) / 10; // 0–23 → úhel odstínu 0°, 15°, … 345°
|
||
int pozice = (index - 10) % 10; // 0–9 → jas a sytost
|
||
int jas = Jasy[pozice];
|
||
var (r, g, b) = Hsv(skupina * 15.0, jas);
|
||
if ((pozice & 1) == 1)
|
||
{
|
||
r += (jas - r) / 2;
|
||
g += (jas - g) / 2;
|
||
b += (jas - b) / 2;
|
||
}
|
||
return (r << 16) | (g << 8) | b;
|
||
}
|
||
|
||
/// <summary>Plně sytá barva daného odstínu a jasu (HSV se saturací 1). Dělení na šestiny
|
||
/// kruhu; v každé roste jeden kanál a klesá jiný. Ořezává se dolů, jako to dělá AutoCAD.</summary>
|
||
private static (int R, int G, int B) Hsv(double uhelDeg, int jas)
|
||
{
|
||
int sestina = (int)(uhelDeg / 60);
|
||
double podil = uhelDeg / 60 - sestina;
|
||
int rostouci = (int)(jas * podil);
|
||
int klesajici = (int)(jas * (1 - podil));
|
||
return sestina switch
|
||
{
|
||
0 => (jas, rostouci, 0),
|
||
1 => (klesajici, jas, 0),
|
||
2 => (0, jas, rostouci),
|
||
3 => (0, klesajici, jas),
|
||
4 => (rostouci, 0, jas),
|
||
_ => (jas, 0, klesajici),
|
||
};
|
||
}
|
||
}
|