momente şi schiţe de informatică şi matematică
To attain knowledge, write. To attain wisdom, rewrite.

Metafont pentru lemniscata lui Bernoulli (partea a patra)

LaTex | Metafont
2018 jul

Încă mai pregătim, constituirea acelui font pe care îl tot vizăm din [1] încoace…
Tratăm aici o problemă conexă (dar simplă: o construcţie de dreptunghi), ilustrând caracterul descriptiv al limbajului MetaFont şi modul specific de a concepe o "funcţie".

Cum vom crea (v. [1] şi [2]) "cadrul tangent" lemniscatei lui Bernoulli ? (…aici doar am pozat, simbolul pe care l-am creat pentru LaTeX; e o problemă, aceea de a-l disponibiliza ca atare şi pentru MathJax.) Cu alte cuvinte - cum construim (folosind MF) un dreptunghi, ştiind centrul şi un vârf ?

Să observăm întâi că lipseşte ceva, fiindcă "în general" problema ar avea o infinitate de soluţii: un al doilea vârf $\small\mathsf{D}$ îl obţinem în mod unic, simetrizând vârful dat iniţial $\small\mathsf{A}$ faţă de centrul dat $\small\mathsf{Z}$; mai departe însă, un al treilea vârf $\small\mathsf{B}$ se poate alege arbitrar pe unul dintre cele două semicercuri de diametru $\small\mathsf{AD}$ (având atunci, $\small\sphericalangle\mathsf{ABD}=90^{\circ}$; al patrulea vârf $\small\mathsf{C}$ rezultă simetrizând $\small\mathsf{B}$ faţă de $\small\mathsf{Z}$).

Trebuie să mai asumăm o informaţie, pentru ca dreptunghiul $\small\mathsf{ABCD}$ să fie unic determinat - anume, direcţia uneia dintre laturi. Am avea astfel trei parametri: vârful $\small\mathsf{A}$, centrul $\small\mathsf{Z}$ şi direcţia $\delta$; dar punctele şi direcţiile fiind raportate la un sistem de axe rectangulare prestabilit, ar fi suficient să considerăm doar cazul "de bază" $\small\delta=0$, când laturile sunt paralele cu axele de coordonate; pentru orice altă direcţie a laturii, nu avem decât să rotim dreptunghiul "de bază" cu unghiul corespunzător.

Trecând la lucru, constituim într-un director ~/PROOF fişierul "rectang.mf":

vb@Home:~$ mkdir PROOF; cd PROOF/  # înfiinţeză directorul
vb@Home:~/PROOF$ touch rectang.mf  # înfiinţeză fişierul
vb@Home:~/PROOF$ gedit rectang.mf  # editează, conform secvenţelor redate mai jos
vb@Home:~/PROOF$
%% fişierul "rectang.mf"      % dreptunghi (paralel axelor) de centru Z şi vârf A
vardef rectangle(expr A, Z) =
    pair B, C, D; 
    A + C = B + D = 2Z;  % condiţia ca ABCD să fie paralelogram cu centrul Z
    xpart(D) = xpart(A);  % adică AD || Oy
    ypart(A+D) = ypart(B+C);  % dreapta prin mijloacele lui AD şi BC este || Ox
    A -- B -- C -- D --cycle
enddef;

O "funcţie" se introduce prin vardef şi este o secvenţă parametrizată de comenzi astfel încât atunci când va fi invocată prin numele asociat (aici, 'rectangle') şi prin valori corespunzătoare parametrilor prevăzuţi (aici, 'A' şi 'Z') - MF va înlocui acel nume (şi valori) printr-un anumit obiect, specificat în secvenţa respectivă (aici, conturul 'A--B--C--D--cycle'). Comenzile trebuie încheiate cu ';', exceptând însă ultima comandă (cea prin care se constituie obiectul "de returnat"); definiţia se încheie cu enddef (care este tot o "comandă", trebuind încheiată cu ';').

Obs… în subconştient, 'MF' este o asemenea "funcţie": vardef MF="MetaFont" enddef;.

Parametrii trebuie introduşi folosind unul dintre tipurile "generice" predefinite - aici, expr (care încapsulează expresii de orice tip); avem de subînţeles, că parametrii A şi Z trebuie să fie de tip pair (având ca valori puncte $\small(x, y)$; xpart şi ypart dau $\small x$ şi respectiv $\small y$, pentru punctul respectiv).

Definiţia primară vardef z@# = (x@#, y@#) enddef; ne permite să adresăm direct punctele cu care lucrăm (fără a le declara în prealabil) prin variabile al căror nume are prefixul "z" (ca "z1", "z2", sau "za", "z3r", etc.); iar variabilele cu prefixul "x", respectiv "y" (în mod implicit, de tip numeric) vor reprezenta abscisele şi ordonatele acestor puncte. "@", "@#" şi "#@" sunt parametri consideraţi în mod implicit pentru vardef, indicând respectiv locul ("@" se citeşte "at") caracterului curent din numele variabilei, orice caracter următor şi respectiv, orice caracter precedent celui curent.
Obs… Sesizăm aici o bună preocupare pentru notaţia mnemonică a variabilelor şi a parametrilor.
Ar fi de amintit acum, că pentru [1-3] am plecat tot de la o chestiune de notaţie mnemonică - vrând să prezentăm "mnemonic" o anumită proprietate a lemniscatei lui Bernoulli.

MetaFont este un limbaj descriptiv; este suficient (în general vorbind) să formulăm condiţiile –sau ecuaţiile– care trebuie satisfăcute de un obiect sau altul, pentru ca MF să poată determina acel obiect. În cazul de faţă, am descris dreptunghiul cerut ca fiind un paralelogram (diagonalele AC şi BD au acelaşi mijloc, Z) în care D şi A au aceeaşi abscisă (deci AD este paralelă cu $\small Oy$), iar mijlocul lui AD şi mijlocul lui BC au aceeaşi ordonată (deci AB şi DC sunt paralele cu $\small Ox$).

Este important de ştiut că B, C şi D (declarate în interiorul "funcţiei", sau macro-ului definit mai sus) nu sunt variabile "locale" (cum ar fi de exemplu în C++); este şi acesta un motiv pentru care în MF (şi în general, în sistemul TeX) nu se foloseşte "funcţie" (ca în alte limbaje), ci "macro" (şi în loc de "apelarea funcţiei" vorbim de "expandarea macro-ului").

Este drept că am ajuns să-mi dau seama despre acest fapt "pe propria piele". Iniţial folosisem ca de obicei, litere mici pentru variabilele respective; dar compilarea programului în care defineam simbolul lemniscatei (prin macroul standard beginchar) s-a oprit în cursul expandării macro-ului de mai sus (prin care urma să se adauge cadrul tangent), semnalând cumva că este ceva în neregulă cu variabila 'd'… Uitasem că în 'beginchar' deja apare o variabilă internă 'd' (pentru "adâncimea" caracterului; la fel avem 'w' şi 'h', pentru lăţimea şi înălţimea caracterului respectiv); prin urmare, se producea o confuzie între 'd' din macro-ul invocat şi 'd' intern lui 'beginchar' (deci variabilele interne macro-ului sunt tratate –în mod implicit– ca variabile globale, nu locale definiţiei respective). Bineînţeles că "repararea" cea mai simplă a constat în înlocuirea literelor mici (prin care desemnasem punctele) cu majuscule.

Putem testa macro-ul constituit mai sus şi fără a considera o definiţie beginchar...endchar din care să-l invocăm: trebuie să folosim comanda showit, prin care se afişează pe ecran conţinutul variabilei currentpicture (pe care am investigat-o prin show() în [3] - unde am experimentat şi o vizualizare grafică alternativă, folosind R); subliniem că nu are sens să folosim shipit - aceasta produce un fişier de tip "Generic Font" (v. [3]), ori macro-ul pe care vrem să-l testăm acum nu are legătură cu vreun font (încât fişierul rectang.2602gf care ar rezulta este inutilizabil).

Adăugăm în fişierul "rectang.mf" secvenţa următoare şi lansăm compilatorul (mf rectang):

screen_cols := 1000; screen_rows := 600;  
path dr[];  % 'dr0' (sau 'dr[0]'), 'dr1', 'dr2' etc., 
            % devin toate, variabile de tip "path"
dr0 = rectangle((0,0), (40,-60));  % A=(0,0) şi Z=(40,-60)
dr1 = dr0 rotated 30;  % roteşte dreptunghiul "de bază" 
                       % în jurul lui A, cu 30 grade
pickup pencircle; draw dr0;
pickup pensquare; draw dr1;
showit;

Am setat întâi (convenabil) numărul de rânduri şi coloane ale ecranului; am invocat rectangle() pentru a obţine un dreptunghi "de bază" (cu laturile paralele axelor), cu un vârf în punctul (0,0); apoi am rotit dreptunghiul respectiv în jurul originii, cu 30°. Prin draw cele două contururi create s-au înregistrat în currentpicture, reprezentată în final pe ecran de către showit.

Precizăm că showit este definită (ca şi construcţia z@# referită mai sus) în "pachetul" de bază plain.mf (v. [3]), astfel:

def openit = openwindow currentwindow
             from origin to (screen_rows,screen_cols) at (-50,300) enddef;
def showit = openit; let showit=showit_; showit enddef; % first time only
def showit_ = display currentpicture inwindow currentwindow enddef;

ceea ce s-ar "traduce" cam aşa: comanda showit deschide o fereastră grafică (dintre cele 16 posibile, fiindcă currentwindow are valori 0..15), din colţul stânga-sus al ecranului până în colţul dreapta-jos indicat de variabilele screen_rows şi screen_cols şi fixează originea sistemului de coordonate specific lui MF în punctul indicat prin (-50, 300); apoi, transpune cumva în această fereastră, structura de date înregistrată în variabila currentpicture.
Punctul indicat prin (-50, 300) devine punctul de referinţă al caracterului: spre dreapta lui se măsoară lăţimea w, în sus (deasupra liniei de bază) se măsoară înălţimea h, iar dedesubt - adâncimea d a acestuia (să nu uităm că MF este destinat producţiei de caractere, furnizând de regulă un fişier metric din care avem dimensiunile boxei asociate (w, h, d) şi un fişier ".gf" care descrie forma grafică de încastrat în această boxă).
Dar unde este totuşi, pe ecran, acest punct de referinţă? Punctul (-50, 300) este reperat de MF în modul obişnuit în matematică: are abscisa -50 şi ordonata sa este 300 (deci nu este vorba direct, de număr de linii sau coloane de pe ecran); aceasta înseamnă că punctul respectiv este pe a 300-a linie de pixeli (de sus în jos) şi la 50 de pixeli distanţă faţă de marginea stângă a ecranului (dacă translatăm noua origine cu 50 pixeli spre stânga şi cu 300 pixeli în sus - obţinem vechea origine, colţul stânga-sus al ecranului).

vezi Cărţile mele (de programare)

docerpro | Prev | Next