MetaPost

0. Obsah


1. Co je to MetaPost

Je to programovací jazyk určený pro popis obrázků. Výsledným produktem je program v PostScriptu, který vyrenderuje daný obrázek. Na rozdíl od PostScriptu poskytuje mnoho zajímavých funkcí, díky kterým je možno kreslení zjednodušit a zrychlit.

Mezi nejpoužívanější funkce bezesporu patří automatické řešení soustav lineárních rovnic, výpočet průniku křivek, plynulé navazování, automatický výpočet kontrolních bodů Beziérových křivek, aby byla výsledná křivka co nejhezčí, vkládání popisků (je možné formátování TeXem a TROFFem), využití různých stylů kreslení čar, vestavěný makrojazyk,... Programovací jazyk je běžný procedurálního typu, takže je snadno čitelný (možná když si někdo zvykne na PostScript, tak na něj nedá dopustit, ale...)

Vygenerované obrázky je možno použít mnoha způsoby, mimo jiné je možno propojit je s TeXovským textem. Obvykle se sice používá METAFONT, ale pro některé aplikace bude MetaPost jistě vhodnější. Bohužel je ale pak nutno DVI soubor konvertovat do PostScriptu příkazem dvips.
 

2. Jak ho nainstalovat a spustit

Nejjednodušší je nainstalovat MetaPost s některou distribucí TeXu, bezproblémová je např. instalace balíku teTeX (pod Linuxem distribuce Debian, RedHat,...). Program se spouští příkazem mpost, veškeré ovládání je řízeno z příkazové řádky. Program se chová jako kompilátor, z daného vstupního souboru vygeneruje PostScriptový výsledek.

Výstupní soubory je možno prohlížet bez problémů např. programem ghostview, při použitém Debianu 2.1 mi nastaly pouze 2 problémy:
 

2.1. link na TeXovské fonty

ghostscript nenašel TeXové fonty použité v popiskách. Ve standardní instalaci nejsou tyto fonty zaregistrovány. Přepněte se tedy do adresáře /usr/lib/ghostscript/fonts. Do souboru Fontmap je nutno přidat linky na všechny použité TeXové fonty. Pokud se např. PostScriptová konverze PlainTeXových fontů nachází v adresáři /usr/lib/texmf/fonts/type1/bluesky/cm a AmsTeXové fonty v adresáři /usr/lib/texmf/fonts/type1/bluesky/ams, pak je nutno do souboru Fontmap přidat řádek pro každý existující font (nejlépe SEDem):

/cmb10 (/usr/lib/texmf/fonts/type1/bluesky/cm/cmb10.pfb) ;
/cmbsy10 (/usr/lib/texmf/fonts/type1/bluesky/cm/cmbsy10.pfb) ;
/cmbx10 (/usr/lib/texmf/fonts/type1/bluesky/cm/cmbx10.pfb) ;
/cmbx12 (/usr/lib/texmf/fonts/type1/bluesky/cm/cmbx12.pfb) ;
/cmbx5 (/usr/lib/texmf/fonts/type1/bluesky/cm/cmbx5.pfb) ;
...
/lcmssi8 (/usr/lib/texmf/fonts/type1/bluesky/cm/lcmssi8.pfb) ;
/line10 (/usr/lib/texmf/fonts/type1/bluesky/cm/line10.pfb) ;
/linew10 (/usr/lib/texmf/fonts/type1/bluesky/cm/linew10.pfb) ;
/euex10 (/usr/lib/texmf/fonts/type1/bluesky/ams/euex10.pfb) ;
/eufb10 (/usr/lib/texmf/fonts/type1/bluesky/ams/eufb10.pfb) ;
/eufb5 (/usr/lib/texmf/fonts/type1/bluesky/ams/eufb5.pfb) ;
...

Od tohoto okamžiku najde ghostscript v pořádku tyto fonty, ačkoliv jsou nestandardní a nainstalovány ve zvláštním adresáři. Snad není nutné podotknout, že tyto soubory musí být PostScriptové, není možné použít GF či PK soubory. V teTeXové instalaci se nicméně tyto soubory nalézají.
 

2.2. korektní interpretace fontů

Ačkoliv to nikde není výrazně zmíněno, je nutné při používání popisků zapnout speciální proměnnou příkazem prologues:=1; Pokud tak není učiněno, je celý vygenerovaný obrázek přímo nepoužitelný. Není oříznut na nejmenší možný obdélník (místo toho zabírá celou stránku) a pokus o vytištění textu (ať už PostScriptového nebo TeXovského) způsobí chybové hlášení. Výše zmíněný parametr způsobí vygenerování správného inicializačního kódu (Encapsulated Post Script), pak už bude všechno fungovat tak, jak má.

Program obrazek.mp napíšete v libovolném editoru, spustíte mpost obrazek.mp, ten vygeneruje sérii souborů obrazek.1, obrazek.2,... Tyto soubory postupně obsahují PostScriptové kódy všech obrázků v něm obsažených.

3. Možnosti jazyka

3.1. Definice obrázků

Obrázek se uvozuje mezi příkazy beginfig(1) a endfig;. Jednička znamená číslo obrázku. Těchto obrázků je možno v jednom zdrojovém textu uvést více. Text souboru je ukončen jedním zbývajícím end Na počátku souboru se obvykle uvádějí deklarace maker, nastavení speciálních parametrů,...
 

3.2. Definice bodů

Veškeré kreslící operace je možno sice provádět přímo nad danými souřadnicemi, ale vhodnější je nadefinovat si na počátku body a poté je v symbolickém zápise ve zbytku programu používat. Bod se definuje příkazem z1=(3cm,4cm); a podobně. Při zápise je možno využít obvyklých jednotek, indexem může být nejenom číslo, ale třeba token či více indexů. Pozice bodů je možno sčítat, odčítat a násobit reálným číslem. Kromě toho je možno lineárně interpolovat (resp. extrapolovat) pozice 2 bodů výrazem 1/3[z1,z2], nebo -1[z4,z5].

Velice příjemnou funkcí je automatické řešení lineárních rovnic. Operátor rovnítko neplní totiž funkci přiřazení, ale jakési vyjádření přání: chtěl bych, aby se tohle rovnalo. Pokud použijeme proměnné, jejichž hodnota je neznámá, MetaPost si zapamatuje jejich vzájemný vztah a při procházení dalších výrazů tyto rovnosti postupně řeší (eliminuje neznámé). Pokud se ale pokusíme použít neznámou souřadnici při volání některého kreslícího příkazu, ohlásí chybu, stejně jako při neexistenci řešení. Pokud chceme neznámou proměnnou pouze využít k zápisu výrazu, ale nepotřebujeme znát její hodnotu, napíšeme whatever. Příkladem možného použití rovnítka může být:
 

z1-z2=z3-z4=z5-z6=(0,2cm);    %z1 je 2cm nad z2,...
z5=z1+whatever[z1,z2]=z3+whatever[z3,z4];    %z5 je prunikem primek z1--z2 a z3--z4
z0=(0,0); z1=(k,0); z2-z0=(z1-z0) rotated 60;    %rovnostranny trojuhelnik


Při popisování objektů je možno využít lineárních transformací, které můžeme popsat např. složením transformací elementárních, jako jsou scaled (xscaled, yscaled, zscaled), shifted, slanted, rotated. Takto můžeme před nakreslením ještě ztransformovat cestu, popisek, obrázek, bod,...
 

3.3. Kreslení čar a křivek

Lomené čáry se kreslí příkazem draw z1--z2--z3--z4; resp. draw z1--z2--z3--cycle; MetaPost vykreslí čáru daným stylem a perem (viz. dále) a dále už nic nepočítá.

Křivky kreslené MetaPostem jsou Beziérovy kubiky, stejné jako používá PostScript. Na rozdíl od mnoha konkurenčních systémů, v MetaPostu není nutno zadávat kontrolní body. V nejjednodušším případě se pouze zadá seznam bodů, jimiž má křivka procházet, a MetaPost sám podle jakési heuristiky zvolí body tak, aby byla křivka co nejhezčí. Nemusíte se ale bát, že by nám MetaPost něco vnucoval. Jeho chování můžeme ovlivnit nastavením mnoha lidsky srozumitelnými parametry:

  1. Syntaxe kreslícího příkazu je draw z1..z2..z3..z4--z5..z6;
  2. Pokud potřebujeme v daném bodě předepsat sklon křivky, píšeme draw z1..z2{dir 45}..{dir 30}z3{dir 10}..z5; Lze použít předdefinované směru up,down,left,right.
  3. Pokud se nám zdá, že křivka mezi dvěma body příliš plandá, můžeme upravit její napětí příkazem draw z1..tension 3..z2..z3; Pokud chceme křivku napjat jak to jenom jde, napíšeme draw z1..z2...z3..z4; což je zkratka za tension infinity.
  4. Jestliže chceme na konci křivky udělat nějakou kudrlinku, napíšeme draw z1{curl 2}..z2..{curl 1}z3;
  5. Pokud si přesto nevybereme, zůstane nám poslední možnost zadat kontrolní body ručně, což je však málokdy potřeba. Dosáhneme toho příkazem draw z1..controls z2 and z3..z4;


Většinu těchto parametrů využijeme málokdy, MetaPostí heuristika je skutečně vynikající. Zadávat body ručně se mi jeví být až směšné.

MetaPost poskytuje mnoho funkcí pro práci s cestami. O cestě můžeme zjišťovat např. tyto informace:

  1. pozici jednotlivých bodů na ní (parametrizace křivky). Kontrolní body jsou očíslovány přirozenými čísly, ostatní parametry jsou spojitě rozloženy podél křivky. Daný bod zjistíme výrazem point 0.7 of p, kde p je cesta.
  2. délku (počet kontrolních bodů) křivky výrazem length p.
  3. vybrat podcestu jako interval mezi dvěma jejími body (o obecných reálných parametrech). Získáme ji výrazem subpath(1.5,length p) of p.
  4. oříznout cestu po posledním protnutí s jinou cestou výrazem p cutafter q.
  5. zjistit směrový vektor v daném bodě výrazem direction 2 of p.
  6. zjisti parametr bodu průniku 2 cest výrazem p intersectiontimes q (vrátí dvojici parametrů pro obě křivky). Z něj vychází makro intersectionpoint, které vrací přímo souřadnice bodu.
  7. zjistit parametr bodu, ve kterém křivka směřuje daným směrem, pomocí výrazu directiontime (1,1) of p. Analogicky se bod, o který se jedná, získá výrazem directionpoint (1,1) of p.
  8. zjistit skutečnou délku křivky výrazem arclength p.
  9. zjistit parametr, ve kterém dosáhne křivka dané skutečné délky výrazem arctime 6 of p.
Tyto informace můžeme samozřejmě využít i při definici vlastních bodů, v programovacím jazyce. Zde je nejlépe vidět převaha nad ryzím PostScriptem.
 

3.4. Vkládání popisků do obrázku

PostScriptový popisek se vloží příkazem label.bot("X-axis",0.5[z1,z2]); nebo např. label("hello",(2cm,1cm)); Pokud bychom si přáli udělat v daném bodě ještě puntík, použijeme příkaz dotlabel. Popisek se vysází fontem, jehož jméno je uloženo v proměnné defaultfont, změnit jej můžeme např. příkazem defaultfont:="Times-Roman"; Neméně důležitým parametrem je defaultscale.

Pokud chcem vysázet popisek sázecím systémem (např. TeX nebo TROFF), pak místo textu uzavřeného do uvozovek napíšeme text mezi klíčová slova btex a etex. Např. label.lrt(btex \int_0^1x\;{\rm d}x={1\over2} etex rotated 20 scaled 1.44, (3cm,3cm)); MetaPost se postará o vytvoření zdrojového souboru pro sázecí systém, analýzu výstupního (DVI) souboru a převedení na posloupnost PostScriptových příkazů. Díky tomu není problém s výsledkem dále pracovat, např. jej natáčet, zvětšovat nebo měnit barvu.

Pokud potřebujeme TeX nějak inicializovat (natáhnout makra), uvedeme tyto příkazy na začátku ve tvaru verbatimtex \input mymac etex. Nezapomeňte na správné (nenulové) nastavení proměnné prologues, jinak žádné popisky neuvidíte.
 

3.5. Advanced grafika

Uzavřenou cestu (klíčové slovo cycle) můžeme vyplnit příkazem fill p; resp. fill p withcolor red; Pokud potřebujeme vyplnit oblast mezi dvěma křivkami, které se nějak protínají, můžeme s výhodou využít primitiva buildcycle. Např. oblast omezenou 4 křivkami vyplníme šedě pomocí fill buildcycle(p0,q0,p1,q1) withcolot 0.7white; Příkaz filldraw je spojením příkazů fill a draw. Od všech těchto příkazů existuje varianta unfill,... která oblast maže místo kreslení.

Přerušovanou křivku vykreslíme snadno připojením výrazu dashed. Tento výraz může má více variant:

  1. dashed withdots scaled p vyprodukuje tečkovanou čáru
  2. dashed evenly scaled p naopak čáru čárkovanou
  3. Pokud byste chtěli kreslit podle vašeho vlastního stylu, je to trošičku složitější. Nejprve se váš styl vykreslí speciálním příkazem do obrázku, pak se zkonvertuje a až poté se může začít kreslit normální obrázek: draw dashpattern(on 6bp off 4bp on 12bp); resp. dokonce draw dashpattern(atd...) dashed evenly; Nadefinujeme proměnnou typu obrázek picture pict; pict:=currentpicture; a můžeme již čárkovat pomocí draw p dashed pict;
Veškerá tato nastevení je možno nastavit jako default pomocí příkazu drawoptions(dashed evenly withcolor 0.7[red,blue]);

Tvar konců čar je možno modifikovat nastavením linecap:=butt; (resp. squared, rounded). Analogicky tvar napojení čar je možno modifikovat nastavením linejoin:=rounded; (resp. beveled, mitered). Šipky se dají kreslit na rozdíl od METAFONTu přímo primitivem drawarrow, které bere v úvahu zakřivení křivky.

Cesty je možno kreslit také kaligrafickým perem. Příkazem pickup pencircle xscaled 3mm yscaled 0.5mm rotated 60; nadefinujeme pero se sklonem 60 stupňů. Místo pencircle můžeme kreslit libovolným konvexním polygonem, syntaxe zápisu je makepen((-.5,-.5)--(-.5,.5)--(.5,.5)--(.5,-.5)--cycle). MetaPost předdefinuje tato pera: pencircle, pensquare, penrazor. Nejsem si jist, jak řeší MetaPost kolizi mezi použitím kaligrafického pera a aplikací PostScriptových parametrů na vzhled čar.

Oříznutí obrázku se dá docílit příkazem clip currentpicture to p; kde p je uzavřená cesta.

K Plain MetaPostu existuje balík maker nazvaný boxes.mp, který umožňuje pohodlným způsobem kreslit rámečky okolo obrázků, jednoduše je spojovat šipkami,... Balík je ideální pro kreslení grafů, schémat.
Umožňuje kreslit hranaté a oválné boxy, přehledně je propojovat, vkládat elegantně popisky,...

3.6. Datové typy a makrojazyk

Všechno v MetaPostu má svůj datový typ, takže je možné to ukládat pro pozdější zpracování. Datové typy jsou tyto:
  1. numeric pro uložení čísla ve fixed-point aritmetice
  2. pair pro uložení souřadnic bodu
  3. color pro uložení RGB barvy
  4. transform pro lineární transformace
  5. path pro cesty
  6. pen pro definici pera
  7. picture pro uložení vykresleného obrázku


V MetaPostu nechybí ani makrojazyk. Je možno v něm programovat for cykly a if příkazy. Naprosto nedocenitelná věc je skutečnost, že tyto konstrukce nejsou příkazy, ale makra preprocesoru. Není problém použít for cyklus při vyvolávání draw příkazu (např. draw z1 for i=2 upto 10: ..z[i] endfor;), takže grafy funkcí nakreslíme jedním cyklem. To stejné platí i pro příkaz if.

Makra se dají definovat s parametry i bez pomocí příkazu def pero(expr t)=pickup pencircle scaled t; enddef; Makra se dají nastavit, aby se chovala jako unární či binární operátory. Dokonce i takové základní nastavení, jako že z5 je zkratka za (x5,y5), je pouhé makro.

O makrech je toho možné říci velice mnoho. Zde musím čtenáře bohužel odkázat na MetaPost user manual.

4. Ukázky

Veškeré ukázku jsou pro svou rozsáhlost uloženy ve zvláštním souboru metapost-ukazky.html. Pokud si chceš prohlédnout pouze vzhled obrázků a nikoliv jejich zdrojové texty, zobraz si soubor metapost-obrazky.html. Kliknutím na obrázek (v obou souborech) se dostaneš na PostScriptový kód vygenerovaného programu. Zdrojový text v jazyce MetaPost lze prohlédnout pouze na první stránce.

5. Souvislosti s METAFONTem

MetaPost není původní produkt. Již v okolo roku 1977 naprogramoval D.E.Knuth společně s typografickým systémem TeX také program na návrh fontů METAFONT. Program má v názvu slovo META, protože neslouží k návrhu jednoho fontu. Fonty se v něm popisují parametricky (jen CM fonty jsou závislé na 56 parametrech). Mnoho řezů daného fontu je možno vygenerovat ze stejných zdrojových textů pouhou editací několika málo parametrů. Kdo by věřil, že např. typewriter a roman jsou generována ze stejného programu.

Ačkoliv byl METAFONT určen pro generování fontů, jeho možnosti byly natolik široké, že si získal oblibu i jako editor vysoce kvalitních obrázků (tzv. pérovek). Bohužel program má několik závažných omezení, které se musejí hnusným způsobem obcházet (maximální velikost písmene, nesnadné sázení popisků, pražádná podpora barev,...). Kromě toho je výstupem programu bitmapa. U fontu to je možná vhodné, protože požadujeme rychlé zpracování při mnohonásobném použití. U obrázku, který jednou použijeme, je tato vlastnost spíše na závadu. Kvůli obyčejné změně měřítka je nutno celý obrázek rekompilovat (přece ho nebudeme zvětšovat lineárně).

Proto se objevil MetaPost. Autorem MetaPostu je John Hobby z Bellových laboratoří. Pokud jsme schopni vytisknout PostScriptový výsledek (což s ghostscriptem není problém), myslím si, že výhody tohoto programu vysoce převažují nad nevýhodami. Pro kreslení obrázků je MetaPost daleko pohodlnější.

Zajímavé je, že MetaPost vznikl z METAFONTu pouhou modifikací zdrojáku (výpočetních a výstupních rutin). Zůstal zachován parser, makrojazyk,...

MetaPost obsahuje mnoho rozšíření jazyka oproti METAFONTu. Dlužno podotknout, že některé prvky jazyka nebyly v MetaPostu implementovány: např. veškeré bitmapové manipulace, protože PostScript je vektorový jazyk, popis desítek parametrů pro matematické fonty (plus kerningových tabulek), protože program není již určen pro generování fontů,...

6. Literatura

Nejaktuálnější informace naleznete samozřejmě na homepage MetaPostu. Nejlepší (a pravděpodobně jedinou) učebnicí je MetaPost user manual (psáno v PostScriptu; na této adrese je také spousta jiné zajímavé dokumentace). Tento vychází z METAFONT-booku, pro znalého člověka není problém jej za hodinku přelouskat.

O TeXu, METAFONTu, MetaPostu a spol. lze najít spoustu odkazů na http://www.cstug.cz. Další informace o kreslení obrázků v METAFONTu najdete na http://www.cstug.cz/kreslime/index.html.


Robert Špalek