Robitex's Blog

Ideas in the web

Archivi Categorie: METAPOST

LuaTeX and METAPOST


La libreria MPlib in LuaTeX

METAPOST, il noto linguaggio di disegno vettoriale, è stato incluso in LuaTeX sotto forma della libreria MPlib. Possiamo quindi inserire codice METAPOST direttamente in un sorgente LuaTeX.

Questa modalità, non richiede conversioni del formato d’uscita di METAPOST (.mps) ne la necessità di particolari opzioni di compilazione, mentre per procedere ancora più velocemente con l’unico passo di compilazione, basta utilizzare un editor come TeXWorks, in cui LuaTeX si avvia con un semplice click.
Da riga di comando darete invece la classica istruzione:

... alla riga di comando
... (occorre una installazione di
... TeX Live abbastanza recente)

$ luatex nomefile

Sorgente modello

In concreto, trovate di seguito il template del codice in linguaggio TeX dove si dovrà inserire il codice METAPOST tra le macro \mplibcode e \endmplibcode, con le uniche attenzioni di non lasciare in esso, ne righe vuote ne commenti con il carattere percentuale:

% source template
% LuaTeX with Metapost

\input luamplib.sty
\nopagenumbers

\mplibcode
   <codice METAPOST>
\endmplibcode
\bye

Il codice fornirà il pdf della figura che scontorneremo poi con l’utility pdfcrop (ecco perché si sopprime la numerazione di pagina), per inserire il risultato in altri documenti. Naturalmente, mutatis mutandis, potremo lavorare allo stesso modo ma con LuaLaTeX per avere a disposizione la potenza espressiva di LaTeX.

Un esempio è riportato nel seguente codice, che produce la figura successiva scaricabile come pdf.

% a drawing example

\input luamplib.sty
\nopagenumbers

\mplibcode
beginfig(1);
u := 100mm;
for a = 0 step 15 until 90:
draw origin -- u*dir(a)
  withpen pencircle scaled 2pt
  withcolor red;
endfor;
endfig;
\endmplibcode
\bye

An example of a METAPOST figure building directly in LuaTeX engine.


Non male!
Un saluto.
R.

Carta millimetrata a volontà


Scarica l’articolo nel formato PDF per la stampa

Sommario

Con il pacchetto rilasciato recentemente su CTAN dal nostro Prof. Enrico Gregorio, siamo in grado di produrre un serie di PDF per fogli a carta millimetrata con una facilità sorprendente, utilizzando il codice già predisposto in un precedente post. Buon divertimento.

Divertimento millimetrato

Bene, bene, partiamo con il creare un foglio di carta millimetrata di formato A4 a toni di grigio, ecco il codice opportunamente adattato dal post precedente da compilare (vedi note tecniche più avanti), con l’opzione –shell-escape:

\documentclass[a4paper]{minimal}
\usepackage[margin=0pt]{geometry}
\usepackage[shellescape]{gmp}

\begin{document}

\noindent
\begin{mpost}
   % Foglio di carta millimetrata
   % Origine sistema di riferimento:
   % in alto a sinistra del foglio
   u := 1mm;
   pagewidth := 210;
   pageheight:= 297;

   % funzione di disegno riga verticale
   def vline(expr x)=
      draw (x*u, -sp/2)--(x*u, -pageheight*u+sp/2);
   enddef;

   % funzione di disegno riga orizzontale
   def hline(expr y)=
      draw (sp/2, y*u)--(pagewidth*u-sp/2, y*u);
   enddef;

   % line sottili
   sp:= 0.15pt;
   drawoptions(withpen pencircle scaled sp
               withcolor 0.70white);

   for i=1 upto pageheight-1:
      if not (i mod 5 = 0):
         hline( -i );
      fi;
   endfor;

   for i=1 upto pagewidth-1:
      if not (i mod 5 = 0):
          vline( i );
      fi;
   endfor;

   % linee intermedie
   sp:= 0.30pt;
   drawoptions(withpen pencircle scaled sp
               withcolor 0.55white);

   for i=5 step 10 until pageheight-1:
      hline( -i );
   endfor;

   for i=5 step 10 until pagewidth-1:
      vline( i );
    endfor;

   % linee pesanti ad 1 centimetro
   sp:= 0.42pt;
   drawoptions(withpen pencircle scaled sp
               withcolor 0.45white);

   for i=10 step 10 until pageheight-1:
      hline( -i );
   endfor;

   for i=10 step 10 until pagewidth-1:
      vline( i );
   endfor;
\end{mpost}
\end{document}

Proseguiamo cambiando le dimensioni di pagina da A4 ad A3 modificando l’opzione A4paper in A3paper ed aggiungendo l’opzione landscape per l’orientamento di pagina orizzontale (non che sia fondamentale), ed aggiornando le variabili nel listato Metapost per la larghezza e l’altezza di pagina:

\documentclass[a3paper,landscape]{minimal}
\usepackage[margin=0pt]{geometry}
\usepackage[shellescape]{gmp}

\begin{document}

\noindent
\begin{mpost}
   % Foglio di carta millimetrata
   % Origine sistema di riferimento:
   % in alto a sinistra del foglio
   u := 1mm;
   pageheight:= 297;
   pagewidth := 420;

   % funzione di disegno riga verticale
   def vline(expr x)=
      draw (x*u, -sp/2)--(x*u, -pageheight*u+sp/2);
   enddef;

   % funzione di disegno riga orizzontale
   def hline(expr y)=
      draw (sp/2, y*u)--(pagewidth*u-sp/2, y*u);
   enddef;

   % line sottili
   sp:= 0.15pt;
   drawoptions(withpen pencircle scaled sp
               withcolor 0.70white);

   for i=1 upto pageheight-1:
      if not (i mod 5 = 0):
         hline( -i );
      fi;
   endfor;

   for i=1 upto pagewidth-1:
      if not (i mod 5 = 0):
          vline( i );
      fi;
   endfor;

   % linee intermedie
   sp:= 0.30pt;
   drawoptions(withpen pencircle scaled sp
               withcolor 0.55white);

   for i=5 step 10 until pageheight-1:
      hline( -i );
   endfor;

   for i=5 step 10 until pagewidth-1:
      vline( i );
    endfor;

   % linee pesanti ad 1 centimetro
   sp:= 0.42pt;
   drawoptions(withpen pencircle scaled sp
               withcolor 0.45white);

   for i=10 step 10 until pageheight-1:
      hline( -i );
   endfor;

   for i=10 step 10 until pagewidth-1:
      vline( i );
    endfor;
\end{mpost}
\end{document}

Possiamo naturalmente produrre anche fogli carta millimetrata con rigature colorate. In Metapost vi sono solo una manciata di colori predefiniti, ma possiamo definirne di nuovi a partire da quelli base o per mezzo di componenti di colore RGB oppure anche CMYK, per coprire così tutte le nostre esigenze.

Nel listato precedente per esempio, sostituite alle righe 29, 46, e 59 al posto del colore white, i colori green, blue, o red. La rigatura presenterà automaticamente intensità diverse del colori di base scelto grazie ai moltiplicatori 0.70, 0.55 e 0.45 già utilizzati.

I colori personalizzati possono essere ottenuti creando un nuovo colore RGB di base che, proprio come una variabile modificabile, basterà editare in un unico luogo del codice per ottenere il colore voluto graduato a seconda dell’importanza delle linee della grigliatura millimetrica.

Un nuovo colore si ottiene istanziando una variabile di tipo color, mettendo i tre numeri variabili tra 0 ed 1 delle componenti rosso, verde e blu in una lista tra parentesi tonde:

% definizione del colore base
color col;
col := (0.3, 0.5, 0.8);

Le proprietà della penna si cambiano poi sempre al solito modo con il comando drawoptions, per esempio per le righe sottili della griglia:

drawoptions(withpen pencircle scaled sp
            withcolor 0.70col);

Lavorare con i colori è quindi del tutto naturale in Metapost, ed ecco quindi il listato per ottenere una pagina nel formato A4 con linee di un particolare colore RGB e sotto forma di file PDF

\documentclass[a4paper]{minimal}
\usepackage[margin=0pt]{geometry}
\usepackage[shellescape]{gmp}

\begin{document}
\noindent
\begin{mpost}
   % Foglio di carta millimetrata
   % Origine sistema di riferimento:
   % in alto a sinistra del foglio
   u := 1mm;
   pagewidth := 210;
   pageheight:= 297;
   
   % definizione del colore base
   color col;
   col := (0.3, 0.5, 0.8);

   % funzione di disegno riga verticale
   def vline(expr x)=
      draw (x*u, -sp/2)--(x*u, -pageheight*u+sp/2);
   enddef;

   % funzione di disegno riga orizzontale
   def hline(expr y)=
      draw (sp/2, y*u)--(pagewidth*u-sp/2, y*u);
   enddef;

   % line sottili
   sp:= 0.15pt;
   drawoptions(withpen pencircle scaled sp
               withcolor 0.70col);

   for i=1 upto pageheight-1:
      if not (i mod 5 = 0):
         hline( -i );
      fi;
   endfor;

   for i=1 upto pagewidth-1:
      if not (i mod 5 = 0):
          vline( i );
      fi;
   endfor;

   % linee intermedie
   sp:= 0.30pt;
   drawoptions(withpen pencircle scaled sp
               withcolor 0.55col);

   for i=5 step 10 until pageheight-1:
      hline( -i );
   endfor;

   for i=5 step 10 until pagewidth-1:
      vline( i );
    endfor;

   % linee pesanti ad 1 centimetro
   sp:= 0.42pt;
   drawoptions(withpen pencircle scaled sp
               withcolor 0.45col);

   for i=10 step 10 until pageheight-1:
      hline( -i );
   endfor;

   for i=10 step 10 until pagewidth-1:
      vline( i );
    endfor;
\end{mpost}
\end{document}

Scaricamento

Ok adesso, se volete, cliccate su ciascuna immagine presentata per scaricare il file PDF ottenuto dai listati di codice dell’articolo:

Carta millimetrata A4 colore grigio

Carta millimetrata A4 colore grigio

Carta millimetrata A3 colore grigio

Carta millimetrata A3 colore grigio

Carta millimetrata A4 colore blue

Carta millimetrata A4 colore blue

Carta millimetrata A4 colore rosso

Carta millimetrata A4 colore rosso

Carta millimetrata A4 colore verde

Carta millimetrata A4 colore verde

Carta millimetrata A4 colore definito RGB

Carta millimetrata A4 colore definito RGB

Note tecniche

Per ottenere i miei stessi risultati dovete compilare il codice dei vari esercizi proposti, il che significa che per primo dovete aver installato sul vostro sistema (che sia Linux, Windows o Mac) una distribuzione TeX.

Poi, compito usuale per un utente TeX, dovete compilare il sorgente, ovvero salvate un file di testo con estensione .tex contenente il codice, chiamandolo per esempio A4milli.tex, e compilatelo aprendo una finestra di console e digitando il comando (tranquilli di solito per l’utente LaTeX ci pensa l’editor a far compilare il sorgente):

> pdflatex --shell-escape A4milli

Rigraziamenti

Un grazie a te curioso lettore nella speranza che il post ti abbia fatto divertire oltre che ti abbia aiutato, ed al Prof. Enrico Gregorio che, ancora una volta ci ha sorpreso con il suo nuovo pacchetto gmp.
Un saluto.

Un modello di pagina per appunti


Prendere appunti

Per qualsiasi studente prendere appunti si rivela un attività importante per utilizzare al meglio i momenti di spiegazione da parte dei docenti, ma anche i partecipanti ad una conferenza od ad un incontro aziendale possono avvantaggiarsi con l’applicazione di un metodo che per forza di cose deve essere personalizzato.

Vi sono diversi metodi che si traducono in un modello di pagina con cui, durante la lezione, applicare le regole che consistono essenzialmente nel rimanere concentrati capendo i concetti che si stanno ascoltando e nel riportarli per importanza e struttura, e nel ripassare il prima possibile per aggiungere particolari e fissare i contenuti.

Il web è una fonte di informazioni utile, ma occorre sempre sperimentare e raffinare. Molto utili per esempio, sono frecce e schemi anche molto semplici, simboli di abbreviazione ed aree di pagina a diversa priorità.

Il modello che ho messo a punto si suddivide in due aree affiancate: la principale detta Notes è quella dove vengono trascritti i concetti principali eventualmente adottando un identazione (a questo scopo utilizzo dei piccoli puntini sulla riga come vedremo tra poco), ed un area laterale destra detta Secondary dove inserire brevi note su curiosità, dettagli od altri riferimenti.

Download modello pagina singola

Download modello pagina fronte/retro

Codice Metapost

Dall’idea di disegnare pagine di quaderno per la scuola elementare, descritta in questo mio post, ne ho tratto il seguente codice Metapost facilmente personalizzabile che realizza il modello descritto.

Il codice è suddiviso in tre sezioni: l’avvio con la definizione dei principali parametri geometrici del modulo, la sezione delle funzioni ed il disegno vero e proprio degli elementi della pagina. Con questa organizzazione, è semplice modificare un parametro iniziale (per esempio la distanza tra le righe Notes), mentre la definizione di funzioni di disegno rende più compatta e concettuale l’operazione di disegno.

Da notare anche come si è fatto uso del font monospaziato Inconsolata utilizzando nel preambolo l’istruzione verbatimtex (che ho tratto da un post del forum del GuIT), quindi per far girare il codice occorre una distribuzione abbastanza completa del sistema TeX, che naturalmente comprende l’eseguibile di Metapost chiamato mpost.

prologues:=3;

verbatimtex
\documentclass{minimal}
\usepackage{inconsolata}
\nofiles
etex; 

outputtemplate := "%j.mps";

% Modello per appunti
beginfig(1)
   
   %%% sezione definizione costanti dimensionali
   
   % dimensione di pagina
   pagewidth := 210mm;
   pageheight:= 297mm;
   
   % margini delle linee area Notes
   sxNotes := 16mm; % sinistro
   dxNotes := 52mm; % destro
   tpNotes := 23mm; % superiore
   btNotes := 15mm; % inferiore
   
   dxSecondary := 10mm; % bordo destro area secondaria
   gap := 2mm;          % distanza linee Notes e Secondary
   
   % distanza dei punti di livello
   d := 12mm;
   
   % altezza linea principale
   q := 6mm;
   
   %%% sezione definizione funzioni

   % funzione di disegno riga orizzontale
   def notesLine(expr y)=
      % spessore linea
      sp := 0.28pt;
      
      % disegno linea
      draw (sxNotes,y+2.25mm)--
           (sxNotes + sp/2,y)--
           (pagewidth-dxNotes-sp/2,y)
               withpen pencircle scaled sp
               withcolor 0.4white;

      % disegno punti di struttura
      drawdot (sxNotes+d,y)
         withpen pencircle scaled 1.5sp
         withcolor 0.4white;

      drawdot (sxNotes+2d,y)
         withpen pencircle scaled 1.5sp
         withcolor 0.4white;
   enddef;
   
   % funzione di disegno riga area Secondary
   def secondaryLine(expr y)=
      % spessore linea
      sp := 0.2pt;
      
      % disegno linea
      draw (pagewidth-dxNotes-sp/2 + gap,y)--
           (pagewidth-dxSecondary-sp/2,y)
         withpen pencircle scaled sp
         withcolor 0.6white;
   enddef;
   
   def labLine(expr a, txt, l)=
      label.urt(txt, a) withcolor 0.5white;
      
      lg := 1pt;
      draw a+(-lg, 4mm-lg)--
           a-(lg, lg)--
           a+(l, -lg)
           withcolor 0.5white;
   enddef;
   
   
   %%% disegno punti di bounding box pagina
   drawdot (1pt,1pt)
      withpen pencircle scaled 0.5pt
      withcolor white;
   drawdot (pagewidth-1pt,pageheight-1pt)
      withpen pencircle scaled 0.5pt
      withcolor white;
   
   %%% disegno modulo degli appunti
   
   % numero di linee del notes
   nn := floor((pageheight-tpNotes-btNotes)/q);
   
   %
   % Notes: area principale linee orizzontali
   for i=0 upto nn:
      notesLine( btNotes + i * q);
      secondaryLine( btNotes + i * q );
   endfor;
   
   refsx := pagewidth-dxNotes+gap;
   labY    := 10.5mm;
   labGap  := 5mm;
   
   % lesson label
   labLine((refsx,pageheight- labY),
          btex \ttfamily Lesson: etex,
          dxNotes-gap-dxSecondary);

   labLine((refsx,pageheight-labY-labGap),
           btex \ttfamily Date: etex,
          dxNotes-gap-dxSecondary);

   labLine((refsx,pageheight-labY-2*labGap),
           btex \ttfamily Page: etex,
          dxNotes-gap-dxSecondary);
   
   % speaker label

   labLine((sxNotes,pageheight-labY),
           btex \ttfamily Speaker: etex,
           pagewidth-dxNotes-sxNotes);

   labLine((sxNotes,pageheight-labY-labGap),
           btex \ttfamily Istitute: etex,
           pagewidth-dxNotes-sxNotes);

   labLine((sxNotes,pageheight-labY-2*labGap),
           btex \ttfamily Contact info: etex,
           pagewidth-dxNotes-sxNotes);
   
   % disegno circoli di foratura
   circleDist := 80mm;
   circleBottom := (pageheight - 3*circleDist)/2;
   
   for i=0 upto 3:
      draw fullcircle
         scaled 4mm
         shifted (12.75mm, i * circleDist + circleBottom)
         withpen pencircle scaled 0.5pt
         withcolor 0.5 white;
   endfor;
   
   % altezza y dei segni mediani dei fori
   circlei := circleBottom+circleDist/2;
   circleii:= circlei+2*circleDist;
   
   % disegno marche orizzontali di foratura
   draw (11mm, circlei)--(14mm, circlei)
      withpen pencircle scaled 0.5pt
      withcolor 0.5 white;
   draw (11mm, circleii)--(14mm, circleii)
      withpen pencircle scaled 0.5pt
      withcolor 0.5 white;
endfig;
end

Buona lezione…

Righe e quadretti


Download per gli impazienti

Dai link seguenti potete scaricare i fogli pronti per la stampa in formato PDF. Per le personalizzazioni ed i perfezionamenti non vi resta che leggere l’intero post dove vi viene illustrato il codice che li realizza.

Pagina a quadretti
Pagina a righe
Carta Millimetrata

La stampa dei file richiede un attimo di attenzione perché il vostro lettore PDF potrebbe adattare la pagina all’area stampabile che è leggermente più piccola per via dei margini. Meglio allora stampare senza che siano settati parametri di adattamento.
Notate anche che le dimensioni dei due file sono veramente minuscole perché non contengono nessun font.

Per scaricare l’articolo intero in PDF cliccate questo link.

Quadretti

Se siete insegnanti di prima elementare (o genitori), può essere utile poter stampare pagine quadrettate per la matematica od a righe per l’italiano.
Ecco allora un simpatico post per inizio anno, in cui disegneremo con Metapost i due fogli desiderati.

Ho misurato le dimensioni di un quaderno a quadretti A4 di prima elementare ottenendo che un quadretto misura 5 mm: cambiando questo parametro all’inizio del sorgente e ricompilando (vedi l’ultimo paragrafo “Note pratiche”), potete ottenere la dimensione del quadretto che volete.

Quaderno a quadretti

Per prima cosa posizioniamo l’origine del sistema di riferimento della figura in basso a destra.
Il codice Metapost è caratterizzato dalle due funzioni hline() e vline() che disegnano rispettivamente linee orizzontali e verticali prendendo tre parametri: l’ordinata (o l’ascissa) della linea, lo spessore ed il colore di penna da usare (le proprietà della penna passate come argomento saranno utili per la stampa della pagina a righe).
Si noti che le coordinate delle linee sono stati corretti di metà dello spessore di linea per produrre una figura esattamente inclusa in una pagina di formato A4.
Infatti, una linea orizzontale larga quanto il foglio A4 e di ordinata per esempio 100mm si scriverebbe semplicemente come nel frammento di codice seguente, ma così la linea uscirebbe di metà spessore utilizzando una penna pencircle. Riportiamo quindi anche il codice per la linea entro pagina:

% linea sbordante di metà spessore agli estremi
draw (0,100mm) -- (210mm,100mm);

% linea entro i margini di pagina
% assumendo che lo spessore della penna sia 1pt
draw (0.5pt,100mm) -- (210mm-0.5pt,100mm);

La griglia a quadretti sarà poi ottenuta con due cicli for, le cui variabili intere determineranno la posizione della linea sul foglio.

% imposta il nome del file di putput contenente il disegno
% come nomefilesorgente.mps
outputtemplate :="%j.mps";

% Quaderno a quadretti
beginfig(1)
   % dimensione di pagina (A4)
   pagewidth := 210mm;
   pageheight:= 297mm;
   
   % dimensione del quadretto
   q := 5mm;
   
   % numero colonne e righe
   nx := floor(pagewidth/q)-1;
   ny := floor(pageheight/q)-1;
   
   % funzione di disegno riga verticale
   def vline(expr x, sp, col)=
      draw (x,0+sp/2)--(x,pageheight-sp/2)
         withpen pencircle scaled sp
         withcolor col;
   enddef;

   % funzione di disegno riga orizzontale
   def hline(expr y, sp, col)=
      draw (0+sp/2,y)--(pagewidth-sp/2,y)
         withpen pencircle scaled sp
         withcolor col;
   enddef;
   
   % loop di disegno righe verticali
   for i=0 upto nx:
      vline(q/2+i*q, 0.32pt, 0.7white);
   endfor;
   
   % loop di disegno righe orizzontali
   for i=0 upto ny:
      hline(q/2+i*q, 0.32pt, 0.7white);
   endfor;
endfig;
end

Righe

Il quaderno a righe per l’italiano della prima elementare è un po’ più complicato perché le righe orizzontali sono diversamente spaziate. L’altezza di una riga è di 19mm con due spaziature da 7mm per avere una riga centrale di 5mm (la figura in alto illustra questo tipo di spaziatura). Inoltre le due righe verticali esterne sono colorate in rosso cupo.

Quaderno a righe (click to download the PDF file)

Quaderno a righe (click to download the PDF file)

Nel codice seguente si è assunto un sistema di riferimento con origine nell’angolo in alto a sinistra (semplicemente perché ho misurato il margine superiore di 27.5mm).

outputtemplate :="%j.mps";

% Quaderno a righe
beginfig(1)
   pagewidth := 210mm;
   pageheight:= 297mm;
   
   h1 := 7mm;
   h2 := 5mm;
   hb := 27.5mm;
   
   x1 := 18mm;
   x2 := x1 + 170mm;
   col := 28mm;
   
   % funzione di disegno riga verticale
   def vline(expr x, sp, col)=
      draw (x,-0-sp/2)--(x,-pageheight+sp/2)
         withpen pencircle scaled sp
         withcolor col;
   enddef;

   % funzione di disegno riga orizzontale
   def hline(expr y, sp, col)=
      draw (0+sp/2,y)--(pagewidth-sp/2,y)
         withpen pencircle scaled sp
         withcolor col;
   enddef;
   
   def row(expr y)=
      hline(y-h1, 0.28pt, 0.7white);
      hline(y-h1-h2, 0.28pt, 0.7white);
      hline(y-2*h1-h2, 0.28pt, 0.7white);
   enddef;
   
   vline(x1, 0.32pt, 0.8red);
   vline(x2, 0.32pt, 0.8red);
   
   hline(-hb, 0.28pt, 0.7white);
   
   for i=0 upto 12:
      row(-hb-i*(2*h1+h2));
   endfor;
   
   for i=0 upto 4:
      vline(x1+col+1mm+i*col, 0.28pt, 0.7white);
   endfor;
endfig;
end

Carta millimetrata

Il formato della carta millimetrata prevede una quadrettatura principale ogni centimetro, una secondaria interna ogni 5mm, e la quadrettatura più sottile ad ogni millimetro. L’origine del sistema di riferimento della figura è sempre in alto a sinistra.

Carta millimetrata (click to download)

Carta millimetrata (click to download)

Prima versione

Nella prima versione del codice l’idea è quella di disegnare le righe orizzontali e verticali nei tre spessori della carta millimetrata con 6 cicli for. Si inizia con le linee sottili poi si reimposta spessore e colore della penna per quelle intermedie e così via. Questo ordine fa in modo che quando le linee verticali ed orizzontali si sovrappongono quelle più scure non vengano segnate con colore meno intenso.
Dal punto di vista del linguaggio di Metapost nel codice si è utilizzato la funzione mod che restituisce il resto della divisione tra i due operandi.

outputtemplate :="%j.mps";

% Foglio di carta millimetrata
%   Origine sistema di riferimento:
%   in alto a sinistra del foglio
beginfig(1)
   u := 1mm;
   pagewidth := 210;
   pageheight:= 297;
      
   % funzione di disegno riga verticale
   def vline(expr x)=
      draw (x*u, -sp/2)--(x*u, -pageheight*u+sp/2);
   enddef;

   % funzione di disegno riga orizzontale
   def hline(expr y)=
      draw (sp/2, y*u)--(pagewidth*u-sp/2, y*u);
   enddef;
   
   % line sottili
   sp:= 0.15pt;
   drawoptions(withpen pencircle scaled sp
               withcolor 0.70white);
   
   for i=1 upto pageheight-1:
      if not (i mod 5 = 0):
         hline( -i );
      fi;
   endfor;
   
   for i=1 upto pagewidth-1:
      if not (i mod 5 = 0):
          vline( i );
      fi;
   endfor;
   
   % linee intermedie
   sp:= 0.30pt;
   drawoptions(withpen pencircle scaled sp
               withcolor 0.55white);
   
   for i=5 step 10 until pageheight-1:
      hline( -i );
   endfor;
   
   for i=5 step 10 until pagewidth-1:
      vline( i );
    endfor;
   
   % linee pesanti ad 1 centimetro
   sp:= 0.42pt;
   drawoptions(withpen pencircle scaled sp
               withcolor 0.45white);
   
   for i=10 step 10 until pageheight-1:
      hline( -i );
   endfor;
   
   for i=10 step 10 until pagewidth-1:
      vline( i );
    endfor;
endfig;
end

Seconda versione

La seconda soluzione utilizza due soli cicli for, uno per le linee orizzontali ed uno per le linee verticali, e lo spessore è di volta in volta regolato da una funzione chiamata importance che in base a quale linea sta per essere disegnata imposta colore e spessore opportuno. La funzione importance rende la soluzione più elegante della precedente con il vantaggio che è immediato regolare il suo costrutto if per ottenere qualsiasi tipo di quadrettatura, per esempio per aggiungere una marcatura ogni 10 centimetri. Tuttavia tracciando prima tutte le righe in una direzione, le righe più chiare nell’altra si sovrappongono a quelle più scure già presenti con un risultato in stampa non perfetto.

outputtemplate :="%j.mps";

% Foglio di carta millimetrata
%   Origine sistema di riferimento:
%   in alto a sinistra del foglio
beginfig(1)
   u := 1mm;
   pagewidth := 210;
   pageheight:= 297;
   
   % funzione di definizione dello spessore
   def importance(expr n)=
      if n mod 10 = 0:
         sp := 0.42pt;
         drawoptions(withcolor 0.425white);
      elseif n mod 5 = 0:
         sp := 0.30pt;
         drawoptions(withcolor 0.550white);
      else:
         sp := 0.15pt;
         drawoptions(withcolor 0.700white);
      fi;
   enddef;
   
   % funzione di disegno riga verticale
   def vline(expr x)=
      importance(x);
      draw (x*u, -sp/2)--(x*u, -pageheight*u+sp/2)
      withpen pencircle scaled sp;
   enddef;

   % funzione di disegno riga orizzontale
   def hline(expr y)=
      importance(y);
      draw (sp/2, y*u)--(pagewidth*u-sp/2, y*u)
      withpen pencircle scaled sp;
   enddef;
   
   for i=1 upto pageheight-1:
      hline( -i );
   endfor;
   
   for i=1 upto pagewidth-1:
      vline( i );
   endfor;
endfig;
end

Note pratiche

Per una prima introduzione a Metapost potete leggerla in questo post, mentre per l’installazione è conveniente installare una distribuzione TeX adatta per il vostro sistema come per esempio TeX Live che vi rifornirà di splendidi programmi liberi e gratuiti come appunto Metapost.
Per installare la TeX Live vi sono molte guide che trovate pure in questo blog, per esempio questa per Ubuntu Linux e questa per Windows.

Per la compilazione dei sorgenti occorre aprire una finestra di comando (o terminale) e digitare il comando mpost nomefile dove nomefile è il file testuale che contiene il codice.
La figura generata da Metapost sarà il file dal nome nomefile.mps che sostanzialmente contiene istruzioni Postscript. Per tradurle nel formato PDF occorre scrivere un file sorgente LaTeX e compilarlo con pdflatex come il seguente (modificare opportunamente il nome del file da includere):

\documentclass{minimal}
% serve l'opzione hiresbb per leggere
% i valori del bounding box di alta
% risoluzione
% l'immagine vettoriale da inserire
% è esattamente un rettangolo A4
\usepackage[hiresbb]{graphicx}
\usepackage[margin=0pt,a4paper]{geometry}
\begin{document}
\noindent\includegraphics{quadri.mps}
\end{document}

In definitiva, la procedura classica per ottenere il PDF comprende due compilazioni la prima con Metapost e la seconda con pdflatex.
Per comodità vi riporto anche uno script in Lua sul cui file basta fare un doppio click per far eseguire tutte le compilazioni (una sorta di make). Perché funzioni i file contenenti il codice Metapost delle figure ed i sorgenti LaTeX per la produzione del PDF, devono essere chiamati come risulta dai commenti iniziali del codice:

#!/usr/bin/lua

--[[
     Quaderno a quadretti:
     quadri.mp (codice Metapost)
     quadri.tex (sorgente LaTeX)

     Quaderno a righe
     righe.mp (codice Metapost)
     righe.tex (sorgente LaTeX)
--]]

-- run metapost (filename without extension):
os.execute("mpost righe")
os.execute("mpost quadri")

-- run pdflatex (filename without extension):
os.execute("pdflatex righe")
os.execute("pdflatex quadri")

-- remove temporary file:
os.remove("righe.log")
os.remove("quadri.log")
os.remove("righe.aux")
os.remove("quadri.aux")
os.remove("righe.mps")
os.remove("quadri.mps")

Alla prossima e Buon Anno 2011!!!

Il triangolo di Tartaglia (Pascal triangle)


Il triangolo di Tartaglia

Eravamo nel lontano 1983 (ricordo l’uscita dell’ultimo album “The final cut” dei Pink Floyd.), quando frequentai il mio primo corso di informatica.
Era da poco uscito l’avanzatissimo personal computer di Olivetti chiamato M20, ed il corso verteva sulla programmazione in linguaggio Basic in ambito gestionale con sessioni di pratica davanti ad alcuni M20 nuovi fiammanti. In altre parole, un’esperienza da ricordare.

Bé direte voi, cosa centra il Triangolo di Tartaglia?
Fatemi continuare!

Al corso ricevetti una serie di dispense sul Basic (che conservo ancora), e la possibilità di effettuare esperimenti reali su macchina.

Il docente era un giovane da non molto laureato in informatica, dai modi di fare gentili e pacati, che esponeva gli argomenti con molta precisione.
Ci faceva ragionare sulla sintassi del Basic disegnando alla lavagna dei diagrammi che rappresentavano vari percorsi alternativi che alla fine producevano l’espressione da inserire nel programma.

Il primo esercizio di programmazione, fu appunto il calcolo e la stampa del triangolo di Tartaglia.

Si doveva utilizzare una struttura dati chiamata array e riempirla della serie numerica del triangolo che, diciamolo, fornisce, tra le molte altre cose, i coefficienti binomiali dell’espressione algebrica (alla riga n):

\displaystyle (a+b)^n

All’epoca del corso non era quindi neanche immaginabile lo sviluppo informatico successivo, ad un corso dove i programmi dovevano essere inseriti ed editati con i numeri di linea, il processore girava a 4 MHz, e l’Hard Disk (quando c’era) poteva superare di poco i 10 MB. Il tutto al costo di ben 16.000.000 di Lire.

Poteva non entrare in Azione Metapost?

L’idea base di Metapost fu resa nota alcuni anni più tardi, nel 1989, e la prima release pubblica risale al 1994.
Questa è un occasione davvero imperdibile: scrivere oggi, con un salto nel tempo, il programma del Triangolo di Tartaglia in Metapost.

L’idea da implementare deve essere, per forza di cose, elegante, almeno per confrontarci con come eravamo allora… (ma anche con il tema del Blog che è “idee nel web”).

   1

  1 1

 1 2 1
 \/
1 3 3 1
...

Un numero nel triangolo si costruisce sommando i due numeri ad esso superiori. Per esempio alla riga quattro, che corrisponde al cubo del binomio, il secondo numero è la somma dei due numeri segnalati dagli slash.

Scartiamo la ricorsione per mantenerci semplici, e valutiamo la seguente idea:
ogni elemento del triangolo si compone di due informazioni: la prima è il valore numerico, la seconda è la coordinata per il posizionamento grafico.

Ogni valore numerico dipende da quelli della riga precedente, ed ad ogni nuova riga troviamo un elemento in più.

Basta dunque lavorare con un array (ancora un array…) chiamato values[], a cui aggiungiamo un elemento alla fine, e lo rielaboriamo tante volte quante sono le righe volute, per trasformarne i valori in quelli corretti di riga.

Abbiamo decritto dunque un classico, due cicli for nidificati, dove nel più interno a partire dalla fine, vengono modificati i valori sommando quelli attuali nell’array (che ancora detengono quelli della riga precedente), e disegnando gli elementi nei punti corretti.

L’informazione della posizione di tracciamento è mantenuta da una variabile punto chiamata pos, che localizza l’ultimo numero sulla riga corrente, mentre la posizione sulla stessa riga è ottenuta sottraendo da pos il vettore (xstep,0) il giusto numero di volte.

Regolando i valori di xstep ed ystep, è possibile aggiustare facilmente l’aspetto del triangolo.

outputtemplate:="tartaglia.mps";

beginfig(1);
   % Tartaglia array values
   numeric values[];
   values[0]=0;

   % triangle deep
   numeric deep;
   deep := 13;

   % position of the triangle top number
   pair pos;
   pos := origin;

   % step among row
   numeric ystep;
   ystep:= 18;
   % step among column
   numeric xstep;
   xstep := 36;

   % main loop
   for k:= 1 upto deep:
      values[k] := 1;
      label("1",pos);

      for i:= k-1 downto 1:
         values[i]:=values[i]+values[i-1];
         label(decimal values[i], pos - (k-i)*(xstep,0));
      endfor;

      pos := pos + (xstep/2,-ystep);
   endfor;
endfig;
end
Il triangolo di Tartaglia

Il triangolo di Tartaglia

Se avete compreso il codice, e quindi probabilmente l’avete anche eseguito (magari cambiando il numero di righe), e ne siete rimasti colpiti dall’eleganza, allora il merito andrà ad oggi, viceversa, andrà ad un magnifico giorno primaverile di 27 anni fa, quando alla lavagna cominciò a comparire un numero alla volta, il triangolo di Tartaglia.

Ciao, alla prossima.

METAPOST ed il prodotto scalare


Se la geometria aiuta

Recentemente riflettevo come raramente il grande pubblico televisivo sente parlare di matematica. Eppure è una scienza fondamentale per l’evoluzione della nostra specie. La tecnologia, dai telefonini alla robotica, la medicina, dagli scenari epidemici alla genetica, l’economia, dalla teoria dei giochi ai mercati finanziari, sono mosse dai numeri e dalle loro relazioni.

Prodotto scalare

Dunque, appena oltre la geometria elementare fatta di triangoli e circonferenze, si trova l’algebra lineare, ovvero il mondo dei vettori.
Una delle prime operazioni che si imparano dei vettori è quella del prodotto scalare, detto così perché riceve due vettori per fornire un numero, che è una grandezza scalare in distinzione da quelle vettoriali.

Si tratta di moltiplicare le lunghezze dei due vettori per il coseno dell’angolo da essi formato. Esiste anche una forma algebrica che consiste nella somma dei prodotti delle corrispondenti componenti dei vettori.

In formule, l’espressione geometrica è:

\displaystyle u \cdot v = uv\cos{\theta}

mentre quella algebrica è:

\displaystyle u \cdot v = u_1v_1+u_2v_2+u_3v_3

L’equazione della retta ed il prodotto scalare

Se l’angolo formato dai vettori è l’angolo retto, il coseno sarà zero e quindi anche il loro prodotto scalare.

Noto quindi un vettore n, i punti Q della retta ortogonale ad n e passante per il punto P soddisfano l’equazione:

\displaystyle n \cdot PQ=0

Se nel piano le componenti di n sono (a,b), quelle di PQ sono (x-x_p,y-y_p), l’equazione della retta si può scrivere nella forma lineare standard (assegnando a c la parte costante dell’espressione):

\displaystyle n \cdot PQ= a(x-x_p)+b(y-y_p)= ax+by+c=0

Applicazione in Metapost

La domanda è già nell’aria: perché non utilizzare le capacità di Metapost di risolvere sistemi lineari per rappresentare una retta a partire da un vettore?

(xpart r - x.P) * xpart n + (ypart r - y.P) * ypart n = 0;
 xpart r = 150;

Il codice completo è il seguente, in cui si è definito una funzione chiamata drawline, che prende un vettore (ed un colore), risolve il sistema come prodotto scalare e disegna il risultato con tanto di etichette.

outputtemplate:="scalar.mps";
beginfig(1);

% solve function to draw the line
def drawline(expr n, col)=
  pair r;

  % linear system
  (xpart r - x.P) * xpart n + (ypart r - y.P) * ypart n = 0;
   xpart r = 150;
  
  drawoptions(withcolor col withpen pencircle scaled 1.4);
  draw (-1.5)[z.P, r] -- 1.28[z.P, r];
  drawarrow origin -- n;
  drawoptions(withcolor black);
  
  % labels
  dotlabel.bot(btex $R$ etex, r);
  label.lft(btex $n$ etex,0.5[origin,n]);
enddef;

% punto di aggancio rette
z.P = (50,100);

pair a;
a := (0,75);

drawline(a rotated  20, blue);
drawline(a rotated -20, 0.7red);
drawline(a rotated -45, green);

dotlabel.bot(btex $P$ etex, z.P);

% ascissa limite
draw (150,-30)--(150,160) dashed evenly;

% draw axis
dim := 200;
dim1 := 12;

drawarrow origin -- (dim,0); % x
drawarrow origin -- (0,dim); % y

label.bot(btex $x$ etex,(dim-dim1,0));
label.lft(btex $y$ etex,(0,dim-dim1));
label.llft(btex $O$ etex,origin);

endfig;
end;
Scalar product: a Metapost example

Scalar product: a Metapost example

Punti e semipiani

Se pensiamo all’equazione della retta nella forma vista del prodotto scalare, è facile rendersi conto che è possibile determinare a quale dei due semipiani appartenga un punto qualsiasi.

Un punto al di sopra della retta (rispetto al vettore (a,b) chiamiamolo “generatore”), formerà un angolo con il vettore generatore inferiore all’angolo retto, per cui il prodotto scalare risulterà positivo. Viceversa se il punto è sotto la retta l’angolo è superiore all’angolo retto pertanto il prodotto scalare sarà negativo.

In definitiva, un punto di coordinate (x,y) sarà sopra o sotto la retta a seconda del segno del valore dell’espressione lineare ax+by+c.

Nello spazio

In “3D” l’espressione lineare ax+by+cz+d=0 è l’equazione del piano ortogonale al vettore (a,b,c), e sottoforma di prodotto scalare permette di capire come assegnare ad un punto qualsiasi il semispazio di appartenenza.
Non male per una banale moltiplicazione.

Ciao, alla prossima.

Metapost: proiezione punto su retta


Potenzialità lineari di Metapost

La sintassi di Metapost è da considerare un po’ datata ed a volte puntigliosa?
Meglio usare programmi di disegno preferibilmente vettoriale?

Oppure possiamo parlare la lingua di Metapost ragionando tranquillamente di relazioni geometriche, semipiani e vettori…

Allora, Metapost parlando, ci proponiamo in questo post di disegnare il punto proiezione di uno dato su un segmento, alla maniera di John Hobby, il creatore originale di questo potente tool basato su Postscript, entrando nei dettagli tecnici.

Un punto qualsiasi

In Metapost per dire che ci stiamo riferendo ad un punto qualsiasi (od anche ad un valore qualsiasi, dipende dal contesto), si usa il termine whatever. Con le informazioni disponibili, Metapost tenterà comunque di individuare un punto esatto (a meno delle approssimazioni numeriche), permettendoci di esprimere facilmente vincoli geometrici.

È anche possibile utilizzare una mediation expression per trovare punti su un segmento. Si premette un numero ai punti tra parentesi quadre, così il punto di mezzo del segmento AB è 0.5[A,B].
Se controllate, abbiamo già fatto uso della mediation in altri post, come per esempio quello riguardante il fiocco di Von Koch, per suddividere il lato in tre parti uguali.

Un punto qualsiasi sulla retta passante per i punti A e B è un’affermazione che si esprime con whatever[A,B].

Rette ortogonali

Che significa in Metapost sottrarre due punti? L’operazione sottrae le coordinate così l’espressione B-A possiamo pensarla come il vettore AB esattamente come se utilizzassimo la notazione comune in Meccanica Razionale, ma con il primo estremo nell’origine.

L’operazione di rotazione di 90° della differenza tra due punti, fornirà quindi il vettore uscente dall’origine e di direzione ortogonale alla retta passante per i due punti. Un breve esempio chiarificatore:

outputtemplate:="ortoa.mps";

beginfig(1);
% draw axis
numeric dim;
dim := 200;
drawarrow origin -- (dim,0); % x
drawarrow origin -- (0,dim); % y

pair a,b;
a := ( 10,32);  % se non sono indicate unità viene
b := (180,80);  % assunto il bp (big point) di Postscript

drawoptions(withcolor red withpen pencircle scaled 2);
drawarrow a--b;
drawarrow origin -- (b-a) dashed evenly;
drawarrow origin -- (b-a) rotated 90 dashed evenly;

% labels
drawoptions(withcolor blue);
dotlabel.bot("A",a);
dotlabel.bot("B",b);

label.bot(btex $x$ etex,(dim,0));
label.lft(btex $y$ etex,(0,dim));
label.llft(btex $O$ etex,origin);
endfig;
end;
A Metapost example on vector subtraction and rotation

A Metapost example on vector subtraction and rotation

Proiezione ortogonale (potenza di Metapost)

Chiamiamo C il punto da proiettare e P il punto di proiezione sulla retta AB.
Allora, se si vuole che il vettore di proiezione CP sia proporzionale al vettore della retta di proiezione scriveremo:
p-c = whatever*(b-a) rotated 90;
Nell’espressione whatever assume il ruolo di un fattore per condizione geometrica di parallelismo dei vettori.

Aggiungiamo il secondo vincolo, ovvero che il punto P appartiene alla retta AB:
p = whatever[a,b];

Adesso Metapost è in grado di risolvere il sistema di equazioni per trovare il punto P.

Codice completo e compilabile

Ricordo che l’output Metapost è direttamente utilizzabile da pdfLaTeX, quindi è possibile includerlo in documento per ottenere il pdf. Ecco quindi il codice immediatamente compilabile, dove la distanza di P dalla retta viene calcolata con la distanza pitagorica implementata in Metapost con l’operatore ++, e stampata sul terminale con il comando show:

outputtemplate:="ortob.mps";

beginfig(1);
% draw axis
numeric dim;
dim := 200;
drawarrow origin -- (dim,0); % x
drawarrow origin -- (0,dim); % y

pair a,b,c,p;
a := ( 25,32);  % se non sono indicate unità viene
b := (180,80);  % assunto il bp (big point) di Postscript
c := (80,180);

p-c = whatever * (b-a) rotated 90;
p = whatever[a,b];

drawoptions(withcolor red withpen pencircle scaled 2);
draw a--b;
draw p--c dashed evenly;

% labels
labeloffset := 6;
drawoptions(withcolor blue);
dotlabel.bot("A",a);
dotlabel.bot("B",b);
dotlabel.top("C",c);
dotlabel.bot("P",p);

label.llft(btex $x$ etex,(dim,0));
label.llft(btex $y$ etex,(0,dim));
label.llft(btex $O$ etex,origin);

numeric dist;
dist := xpart (c - p) ++ ypart (c - p);
show dist;%   -> 125.10623 bp
endfig;
end;
Metapost figure on point projection

Metapost figure on point projection

Saluti e buona estate!

La curva di Hilbert in METAPOST


Figura misteriosa…

The Hilbert Curve artwork

Si tratta di un quadro in stile geometrico di qualche artista contemporaneo da appendere a casa o al lavoro…? No, è solamente una rappresentazione delle curve di Hilbert dei primi tre livelli eseguita in METAPOST… ah ah ah.

Ancora una figura frattale…

Dopo il triangolo di Sierpinski ed il fiocco di Von Kock, consultabili qui, mi sono riproposto di disegnare la curva di Hilbert, ovvero una curva frattale in grado di riempire un intero quadrato.
Questo tipo di curve è stato studiato dal matematico italiano Giuseppe Peano ed esistono come curva limite di una successione infinita. Si può dimostrare infatti che la curva limite esiste come funzione e ricopre interamente un quadrato nel piano.

Le iterazioni della curva

Il primo passo per costruire la curva di Hilbert è disegnare tre lati consecutivi di un quadrato passanti per i quattro punti base. Su questa prima figura si disegnano gli stessi tre lati di lunghezza pari a metà della precedente, centrandoli sui quattro punti base, ruotandoli e collegandoli con tre segmenti come nella seguente figura.

The recursive drawing of the Hilbert Curve

Nell’immagine sono disegnati sovrapposti i primi due livelli, il primo in colore blu chiaro con i quattro punti base, il secondo in colore blu scuro, con i tre segmenti di collegamento in colore rosso scuro.

Per il terzo livello si procede allo stesso modo, considerando i nuovi punti basi del secondo livello e disegnando i relativi collegamenti. Nell’immagine di apertura, il primo livello è in colore verde, il secondo è in un rosso scuro ed il terzo è in blu.

Contiamo i lati ed i punti della curva

Ad ogni passo della successione i segmenti che compongono la curva si moltiplicano per quattro aggiungendosene tre per i collegamenti. In formule ciò si scrive (dove n è il numero del livello a partire da 1):

\displaystyle L(n)=3\sum_{i=0}^{n-1} 4^i = 4^n - 1.

Il numero dei punti della curva si ricava aggiungendo 1 al numero dei segmenti L(n)+1 = 4^n, dunque la crescita è esponenziale! All’ottava iterazione il numero dei punti è 65536!

Entra in scena METAPOST

Per disegnare i tre lati consecutivi dell’iterazione consideriamo il centro c ed il punto di mezzo del lato centrale x. Analizziamo il seguente codice:

beginfig(1)
  def trelati(expr c, x)=
    pair ax, bx, a, b;
    ax := c  rotatedabout(x, 90);
    bx := c  rotatedabout(x,-90);
    a  := ax shifted 2(c - x);
    b  := bx shifted 2(c - x);

    % disegno linee
    pickup pencircle scaled 5;
    draw a--ax--bx--b;
    drawarrow x--c withcolor .75blue;
  enddef;

   base = 100;
   pair cen, cx;
   cen := (base,base);  % centro
   cx  := (base,0);     % base

   trelati(cen, cx);
   trelati(cen shifted 3cx, cen shifted 4cx);
   trelati((base,-1.5base),(base,-.5base));
   trelati((4base,-1.5base),(3base,-1.5base));
endfig;
end
METAPOST Drawing test

METAPOST Drawing test

I quattro punti base ax, bx, a, b vengono definiti con trasformazioni geometriche dei punti centro e base c, x. Nel codice si mostra come la scelta del vettore che viene evidenziato dalla freccia blu, quindi l’ordine con il quale si passano gli estremi del vettore è essa stessa un informazione essenziale.

La ricorsione risolve la curva

La soluzione ricorsiva che vi presento la trova particolarmente elegante (ed adatta a chiudere l’anno), se non altro perché risolve un primo tentativo che oltre a non funzionare prevedeva altri due punti come argomento della funzione.

%
% Recursive Hilbert function
%

vardef hilbertCurve(expr c, x, level)=
    % the four basepoint
    save hilpath;
    path hilpath;
    save ax, bx, a, b;
    pair ax, bx, a, b;

    ax := c  rotatedabout(x, 90);
    bx := c  rotatedabout(x,-90);
    a  := ax rotatedabout(c ,-90);
    b  := bx rotatedabout(c , 90);

    if level = 1:
        hilpath := a--ax--bx--b;
    else:
        save atemp, btemp, axtemp, bxtemp;
        pair atemp, btemp, axtemp, bxtemp;

	atemp  := 0.25[a,b];
	btemp  := 0.25[b,a];
	axtemp := 1.25[a,ax];
	bxtemp := 1.25[b,bx];

        hilpath :=
        reverse hilbertCurve( a,  atemp, level-1) --
	        hilbertCurve(ax, axtemp, level-1) --
	        hilbertCurve(bx, bxtemp, level-1) --
        reverse hilbertCurve( b,  btemp, level-1);
    fi
    hilpath
enddef;

beginfig(1)
    pickup pencircle scaled 5;
    draw hilbertCurve(origin,(0,-100),4) withcolor red;
endfig;
end

La parola chiave reverse inverte il path e con questo si riesce a disegnare la curva di Hilbert posizionata comunque nel piano e del livello voluto (che dipende dalla memoria disponibile per METAPOST).

Scaricate pure questo file pdf della curva all’iterazione 5.

Buon anno a tutti!!!

Il fiocco di Von Koch in METAPOST


La curva di Von Koch

Il bellissimo fiocco di neve di Niels Fabian Helge von Koch, matematico svedese vissuto a cavallo del 1900, è una delle prime strutture frattali mai descritte.

Naturalmente è utile consultare la pagina web sull’argomento su wikipedia e quella dedicata da Wolfram MathWorld, ma certo non sarà difficile reperire in rete altre notizie se ancora non bastasse.

Si tratta di una curva chiusa di perimetro infinito ma di area finita, molto nota. Potevamo perdere l’occasione di generarla in METAPOST?

Effettivamente la curva si può costruire con il pacchetto METAOBJ, ma come problema ricorsivo non è male da implementare con quello che sappiamo già dai precedenti post.

La costruzione

Per ciascun lato di un triangolo equilatero si trovano i due punti che lo dividono in tre parti uguali, poi si costruisce il triangolo equilatero il cui lato è il segmento centrale che poi si elimina, e si reitera all’infinito su tutti i nuovi segmenti.

Building the Von Koch's curve

Building the Von Koch's curve

Come si vede dalla figura qui sopra il procedimento è semplice, ed avremo potuto introdurlo su un solo segmento. Si vedono le curve di Von Koch di livello 0, 1 e 2, con rispettivamente 3, 12, e 48 segmenti distinti.

METAPOST in action

Diciamo che nella variabile numerica lato memorizziamo la lunghezza del lato del triangolo di base che darà origine alla curva di Von Koch snowflake. Il codice per disegnare l’iterazione successiva deve trovare i due punti intermedi sul lato e costruire il triangolo equilatero sul terzo medio. Ecco il codice METAPOST:

beginfig(1);
numeric lato;
lato := 100;

pair a,b,p,q,r;

a := origin;
b := (lato,0);
p := 1/3[a,b];
q := 1/3[b,a];
r := q rotatedabout(p,-60);

draw a--p--r--q--b
withcolor .625red;
endfig;
end

Abbiamo fatto uso della comoda notazione per ricavare i punti P e Q che tripartiscono il segmento AB disteso sull’asse x e di lunghezza lato, e l’altrettanto comoda funzione rotatedabout() non nativa ma costruita con trasformazioni elementari, con la quale si crea il punto R, vertice del triangolo di prima iterazione.

Non rimane che costruire la funzione ricorsiva.
Faremo così: accetteremo in input i punti estremi del segmento ed il livello fino a cui la funzione disegnarà la curva di Koch, e su questo baseremo il controllo di terminazione della ricorsione:

se il livello richiesto è zero, allora disegna il segmento AB, altrimenti trova i punti intermedi P, Q ed R e lancia la funzione stessa sui quattro sotto lati AP, PR, RQ e QB.

Invece di elaborare oggetti path lavoriamo così più semplicemente sui punti vertice della curva. Lo stack delle chiamate ricorsive ci darà gratis l’ordine di disegno tra i punti fino al livello desiderato.

Il numero dei segmenti disegnati n è una funzione esponziale del livello v, si ha infatti (per il numero dei segmenti della la curva di Kock completa moltiplicare ulteriormente per 3):

\displaystyle n = 4^v

Ecco il codice completo:

beginfig(1);
pickup pencircle scaled .75pt;

vardef koch(expr aa,zz,level)=
save p,q,r;
pair p,q,r;

if level=0:
draw aa -- zz
withcolor .625red;
else:
p := 1/3[aa,zz];
q := 1/3[zz,aa];
r := q rotatedabout(p,-60);

koch(aa, p,level-1);
koch( p, r,level-1);
koch( r, q,level-1);
koch( q,zz,level-1);
fi
enddef;

numeric lato ;
numeric n;

lato := 420pt;
n := 5;
koch( origin   , (lato,0)     , n);
koch( (lato,0) , lato*dir(60) , n);
koch( lato*dir(60) , origin  , n);

endfig;
end

dove abbiamo utilizzato una macro vardef dando istruzione di utilizzo di p, q, ed r come variabili locali (comando save).

Pronti a scaricare il risultato nel formato PDF? Fate click allora sull’immagine sottostante.

A Von Koch flake with level 5

A Von Koch flake with level 5

Alla prossima.

Il triangolo di Sierpiński con METAPOST


La ricorsione è sempre la via migliore?

A not recursive algorithm for the Sierpinski fractal triangle

A not recursive algorithm for the Sierpinski fractal triangle

Sembra che il miglior modo di affrontare una struttura ricorsiva come il triangolo del matematico polacco sia appunto un algoritmo ricorsivo.

Tuttavia leggendo il codice relativo nel post predecente ho cominciato a ragionare sulla possibilità di chiamare ricorsivamente la funzione di disegno nominata sierpinski() una sola volta anziché tre volte, una per ciascun triangolo del livello inferiore.

L’idea che mi è venuta è quella di disegnare il triangolo dal basso, triplicando per ciascun livello successivo il disegno. Tuttavia questo si può elegantemente fare con un ciclo iterativo semplice!

Dunque vi propongo una seconda soluzione (scritta in METAPOST) in cui il disegno è ottenuto con un normalissimo ciclo for.

Il codice spiegato passo passo

Come d’abitudine propongo l’implementazione dell’idea sviluppandola di pari passo con il codice, seguito dal disegno che ne deriva.

Per prima cosa disegniamo il primo triangolo equilatero (per una breve e mirata introduzione a METAPOST potete far riferimento utile al post precedente su questo stesso blog).

beginfig(1);

u = 40 mm;
fill origin -- (u,0) -- u * dir(60)-- cycle
withcolor .625red;
drawarrow origin -- u * dir(60)
withpen pencircle scaled 5 pt;
endfig;
end

An example with the dir() METAPOST function

An example with the dir() METAPOST function

Impostare una variabile unità qui settata a 40 mm è molto comodo, ma quello che dovreste notare di questo codice è l’uso della funzione dir() per fornire al path le coordinate del vertice alto del triangolo equilatero. La funzione restituisce un vettore unitario che forma l’angolo specificato con l’asse x di senso antiorario con centro nell’origine.
Questo vettore viene visualizzato con il comando drawarrow seguente.

METAPOST disegna il vettore restituito da dir() come un punto corrispondente al secondo estremo dello stesso. Del resto sarebbe scomodo se nel path andasse il vettore piuttosto che il punto.

Nel prossimo esempio possiamo vedere l’uso del costrutto for impiegando la funzione dir() a riprova che dir(angolo) produce il disegno di un punto:

beginfig(2);
numeric u;
u = 40 mm;

for ang = 0 step 15 until 345:
draw u * dir(ang)
withpen pencircle scaled 12 pt;
endfor;
endfig;

A polar example with dir() METAPOST function

A polar example with dir() METAPOST function

Per andare avanti sul nostro obbiettivo Sierpinski, occorre spiegare il concetto di picture. I comandi grafici non fanno altro che plottare disegni in una picture. Ci sono sempre disponibili due oggetti picture, quella corrente detta currentpicture e quella vuota detta nullpicture. Un po’ di codice chiarirà di cosa si tratta:

beginfig(3);
% set the thin of the pen
pickup pencircle scaled 3pt;

draw fullcircle scaled 80pt
withcolor 0.5 green;

picture pic;
pic := currentpicture;
currentpicture := nullpicture;

draw pic;
draw pic shifted (80pt,0);
endfig;

The METAPOST picture concept at work

The METAPOST picture concept at work

All’oggetto pic di tipo picture viene assegnato il disegno corrente, che in quel momento contiene un cerchio verde, poi il disegno viene cancellato. In questo momento il cerchio verde si trova memorizzato in un solo luogo: la picture pic.
Allora possiamo tornare a riempire currentpicture disegnando prima pic nella sua posizione e poi in quella traslata a destra.

L’idea iterativa Sierpinski

% set the unit size
u = 10 mm;

% a picture object
picture sier;

sier:= currentpicture;
draw sier shifted (u,0);
draw sier shifted (dir(60) scaled u);

The basic idea for a iterative Sierpinski drawing method

The basic idea for a iterative Sierpinski drawing method

Questo frammento di codice è il cuore del programma che disegna il triangolo frattale in maniera iterativa e non ricorsiva.
Si presuppone che la currentpicture contenga all’inizio il triangolino di base molto piccolo. Dapprima si memorizza il disegno corrente nella variabile sier, di tipo picture naturalmente, e semplicemente l’aggiungiamo al disegno corrente stesso prima a destra e poi in alto a 60° al posto giusto.

In altre parole come si vede in figura, il triangolo rosso viene memorizzato in una picture e poi viene riaggiunto due volte nella posizione dei triangoli blu. Basterà iterare all’infinito questo procedimento!

Il ciclo for Sierpinski

beginfig(4);
% set the unit size
u = 0.65 mm;

% draw the basic smallest triangle
fill origin--(u,0)-- dir(60) scaled u --cycle
withcolor .625red;

% a picture object
picture sier;

% recursive level
level = 8;

% main drawing cycle
for i=0 upto level-1:
tmp := u * (2**i);
sier:= currentpicture;
draw sier shifted (tmp,0);
draw sier shifted (dir(60) scaled tmp);
endfor;

endfig;

Adesso il codice è completo. L’operazione precedente di copia a destra ed a 60° è inserita nel ciclo iterativo for che produce il triangolo del livello desiderato (ho scelto il livello 8).
Aggiungo che ad un dato livello i la copia del disegno deve esser fatta ad una distanza di u \cdot 2^i, poichè ogni volta che si sale di un livello, il lato del triangolo raddoppia (il doppio asterisco significa quindi elevamento a potenza).

Alla prossima!

%d blogger hanno fatto clic su Mi Piace per questo: