Inserito da: robitex | febbraio 8, 2010

Storia di un libro

Ho recentemente acquistato il libro “Programming in Lua” di Roberto Ierusalimschy. Vorrei raccontarvi i passi tecnologici che hanno portato una copia del libro sulla mia scrivania!!!
Premetto che non ho intenzione di far pubblicità agli autori, od ai vari servizi online che ho utilizzato, ma solo condividere una storia paradigmatica sulla tecnologia che risiede in internet.

Credito per l’acquisto

Qualche tempo fa ho scritto il libro Lo Spazio Carta di AutoCAD e l’ho pubblicato per mezzo del servizio Lulu.com, ovvero un portale che gestisce al posto dell’Autore/Editore gli ordini dei lettori, la stampa in tempo reale del lavoro e la sua spedizione.
Ho creato così un conto PayPal dove vengono accreditati i diritti, ritrovandomi la (ragguardevole) cifra di 20 euro (ed un enorme soddisfazione personale, grazie ragazzi).

L’ordine

Ho quindi ordinato una copia del PIL su Book Depository, un servizio localizzato in Inghilterra. Con pochissimi click ho concluso l’ordine poiché il server di PayPal ha passato in automatico tutti i miei dati a Book Depository, compreso il mio indirizzo che è stato assunto come indirizzo di consegna di default.

La produzione del libro

A questo punto Book Depository ha chiesto una copia all’editore del libro. Si tratta dell’Autore stesso (Ierusalimschy tra l’altro risiede in Brasile) che ha deciso di pubblicare il libro con un servizio simile a Lulu.com, ovvero con Lighting, come spiega nella Premessa del libro stesso. Lighting che si trova in uno stato americano, stampa immediatamente una copia e la rinvia a Book Depository che ha sua volta provvede alla spedizione tramite la Royal Mail inglese.

L’arrivo

Un bel giorno, ecco che il postino suona alla mia porta recapitandomi il libro. Naturalmente ho subito cominciato a leggerlo e noto che la data di pubblicazione è posteriore alla data del mio ordine! Yes, sure.
Ciliegina sulla torta (faccio quindi una rivelazione), scopro che il libro è stato auto-composto dall’Autore con LaTeX.

The cover of the "Programming in Lua"

The cover of the "Programming in Lua"

Conclusioni

La conoscenza si è quindi espressa attraverso una serie di passaggi nel modo digitale, ritrovandosi alla fine nella concretezza di un libro. Quando leggo questo splendido lavoro sul linguaggio Lua non posso non rimanere meravigliato anche per questo, e non posso non raccontarvelo quì sul blog …

Alla prossima.

Inserito da: robitex | gennaio 13, 2010

Sudoku in java Parte 2

Il codice completo

Il codice seguente implementa un applicazione java con output nella console, per la risoluzione del gioco del Sudoku con una funzione ricorsiva, completando il post precedente.

Questa soluzione fa uso di classi enum, costrutto di Java 5 (JDK 1.5) e di operazioni bitmask per incrementare le performance (bit 0 = valore ammesso nella cella, bit =1 valore non ammesso). Infatti potenzialmente passando al codice la griglia vuota, vengono trovate TUTTE le soluzioni possibili del gioco. Peccato che occorrano circa 10.000 anni di lavoro per un PC desktop per terminare il compito…

/*
 * Sudoku.java
 * Solve the game Sudoku with a recursive function.
 *
 * Versione base: 0.1.0.0
 * Date: 10/11/2005 - Start date: 22/08/2005
 *
 * Copyright (C) 2005  Roberto Giacomelli
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

/**
 * The new class Sudoku implements new java feature enum
 * to manage the grid game symbols.
 * Not filled cells are treatment with null reference.
 * Another change to original Sudoku code is bitwise operation
 * for indicates the possible values in an empty cell.
 *
 * Conception: 18 novembre 2005
 * Author: Roberto Giacomelli
 * @param v is an array of int that represent start grid of game
 */
public class Sudoku {

    public enum      gridSymbol {
    // non è possibile inserire l'enum in grid
   // perché in una inner class non sono
   // ammessi metodi static.
        A, B, C, D, E, F, G, H, I ;
    }

    private Grid  startGame;
    public  int   solutionsCount;

    /**
     * Costruttore unico della classe Sudoku
     */
    public Sudoku(int[] v) {
        startGame = new Grid( v );
    }

    /**
     * Wrapper method to calling recursive function
     */
    public void solve() {
            solutionsCount = solveRecursive( startGame );
    }

    /**
     * Solve game with a recursive method
     * and return the number of grid's solutions.
     */
    private int solveRecursive( Grid PlayGame ) {
        if ( !(PlayGame.isCoherence() ) ) {
            return 0 ;
        }

        PlayGame.fillOnes();

        if (PlayGame.isSolution()) {
            PlayGame.print();
            return 1 ;
        }

        int posMinCell = PlayGame.minPossibilityCell();
        int cellPossibility = PlayGame.avalaibleValues[ posMinCell ];
        int solCount = 0;

        for (int val = 0; val < 9; ++val) {
            if ( ( cellPossibility & 1 ) == 0 ) {
               Grid Game1 = new Grid( PlayGame );
               Game1.setCell(posMinCell , gridSymbol.values()[val]);
               Game1.updatePossibility( posMinCell );
                solCount += solveRecursive( Game1 );
            }
            cellPossibility = cellPossibility >> 1;
        }
        return solCount;
    }

    public void printGrid() {
        startGame.print();
    }

    /**
    * Grid inner class:
    * store data game and esposed essential methods
    * to solve game in a recursive manner.
    */
    private class Grid {
        // store grid values (enum type)
        private gridSymbol[] Cells;
      // store not empty cell count of possibility values
        private int[] avalaibleValuesCount;

      // zero is 'possible all values'
      // eight is 'possible only one value'
        private int[] avalaibleValues;
       // store not empty cell possibility values
      // an int is bitwise intended

        public Grid( int[] vector ) {
        // costruttore standard
            Cells = new gridSymbol[ 81 ];
            avalaibleValuesCount = new int[81];
            avalaibleValues = new int[81];
       // zero are default value

            for (int i=0 ; i<81 ; ++i ) {
                if ( vector[ i ] != 0 ) {
                    Cells[i] = gridSymbol.values()[vector[i] - 1];
                }
            }

            // build array avalaible...
            for (int i = 0; i<81; ++i){
               if (Cells[ i ] != null) {
                   updatePossibility( i );
               }
            }
        }

        public Grid( Grid BaseGame ) {
          // costructor overload
            Cells = new gridSymbol[ 81 ];
            avalaibleValuesCount = new int[ 81 ];
            avalaibleValues = new int[ 81 ];

            //~ Cells = ( gridSymbol[] ) BaseGame.Cells.clone();
        // cast from object to array
            //~ avalaibleValuesCount = ( int[] ) BaseGame.avalaibleValuesCount.clone();
            //~ avalaibleValues = ( int[] ) BaseGame.avalaibleValues.clone();

            // code equivalent to clone() array method
            for (int i=0 ; i<81 ; ++i){
                Cells[ i ] = BaseGame.Cells[ i ];
           // copy grid values
                avalaibleValuesCount[ i ] = BaseGame.avalaibleValuesCount[ i ];
                avalaibleValues[ i ] = BaseGame.avalaibleValues[ i ];
            }
        }

        public boolean isCoherence() {
            for (int valueCount : avalaibleValuesCount ) {
                if ( valueCount>8) {
                    return false;
                }
            }

            // array booleaneani per il controllo
            // di congruenza base (applicazione delle regole base)
            boolean[] h = new boolean[9];
            boolean[] v = new boolean[9];
            boolean[] q = new boolean[9];

            int rowIndex ;
            int colIndex ;
            int squareIndex ;
            int CellValue;

            // check di coerenza sulle regole base
            for ( int i = 0 ; i<9 ; ++i) {
                for ( int j = 0 ; j<9 ; ++j) {
                    rowIndex = i*9 + j;
                    colIndex = i + 9*j;
                    squareIndex = (i/3)*27 + i*3 -(i/3)*9 + j + (j/3)*6;

                    if ( Cells[ rowIndex ] != null ) {                 // per le righe
                        CellValue = Cells[ rowIndex ].ordinal();
                        if ( h[ CellValue ] ) {
                            return false;
                        } else {
                            h[ CellValue ] = true;
                        }
                    }

                    if ( Cells[ colIndex ] != null ) {                 // per le colonne
                        CellValue = Cells[ colIndex ].ordinal();
                        if ( v[ CellValue ] ) {
                            return false;
                        } else {
                            v[ CellValue ] = true;
                        }
                    }

                    if ( Cells[ squareIndex ] != null ) {                 // per i quadri
                        CellValue = Cells[ squareIndex ].ordinal();
                        if ( q[ CellValue ] ) {
                            return false;
                        } else {
                            q[ CellValue ] = true;
                        }
                    }
                }

                java.util.Arrays.fill( h , false );
                java.util.Arrays.fill( v , false );
                java.util.Arrays.fill( q , false );
            }

            // se tutti i controlli hanno dato esito positivo:
            return true;
        }

        public void updatePossibility( int PosCell ) {   // shift bitwise operator
            final int BITMASK = 1 << Cells[ PosCell ].ordinal();
           // initial cell pos in row
            final int H = PosCell / 9;
          // initial cell pos in column
            final int V = PosCell - H * 9;
          // initial cell pos in square
            final int Q = (H/3)*27 + (V/3)*3;

            // cell position in row
            int row;
            // cell position in column
            int column;
            // cell position in square
            int square;

            for ( int t = 0 ; t<9 ; ++t){
                row = H*9 + t;
                column = V + t*9;
                square = Q + t + (t/3)*6;

                if (Cells[ row ] == null) {
                    if ( ( avalaibleValues[ row ] & BITMASK ) == 0 ) {
                        avalaibleValues[ row ] = ( avalaibleValues[ row ] | BITMASK );
                        avalaibleValuesCount[ row ] += 1;
                    }
                }
                if (Cells[ column ] == null) {
                    if ( ( avalaibleValues[ column ] & BITMASK ) == 0 ) {
                        avalaibleValues[ column ] = ( avalaibleValues[ column ] | BITMASK );
                        avalaibleValuesCount[ column ] += 1;
                    }
                }
                if (Cells[ square ] == null) {
                    if ( ( avalaibleValues[ square ] & BITMASK ) == 0) {
                        avalaibleValues[ square ] = ( avalaibleValues[ square ] | BITMASK );
                        avalaibleValuesCount[ square ] += 1;
                    }
                }
            }
        }

        public void fillOnes() {
            int index = 0;          // cycle index
            int i = 0;              // inner counter
            int temp;               // temp variable shifted to right

            while (index<81) {
                if ( Cells[ index ] == null ) {
                    if ( avalaibleValuesCount[ index ] == 8 ) {
                        temp = avalaibleValues[ index ];
                        while ( (temp & 1) == 1 ) {
                            temp = temp >> 1 ;
                            ++i;
                        }
                        Cells[ index ] = gridSymbol.values()[ i ];
                        i = 0 ;
                        updatePossibility( index );
                        index = 0;
                    }
                }
               ++index;
            }
        }

        public boolean isSolution() {
            for ( gridSymbol index : Cells ) {
                    if ( index == null) {
                        return false;
                    }
            }
            return true;
        }

        public void setCell(int cellPosition , gridSymbol cellValue) {
            Cells[ cellPosition ] = cellValue;
        }

        public int minPossibilityCell() {
            int possibility = 0;
            int pos = 0;
            for (int i = 0; i < 81; ++i) {
                if (Cells[ i ] == null) {
                    if ( avalaibleValuesCount[ i ] > possibility ) {
                        possibility = avalaibleValuesCount[ i ];
                        pos = i;
                    }
                }
            }
            return pos;
        }

        public void print() {
            for(int i = 0 ; i<81 ; ++i){
                if ( Cells[ i ] == null ) {
                    System.out.print( 0 );
                } else {
                    System.out.print( Cells[ i ].ordinal() + 1 );
                }

                if ( (i+1) % 27 == 0 ){
                    System.out.println();
                    System.out.println();
                } else if ( (i+1) % 9 == 0 ) {
                    System.out.println();
                } else if ( (i+1) % 3 == 0 ) {
                    System.out.print("  ");
                } else {
                    System.out.print(" ");
                }
            }
        }
    }
}

La classe Sudoku può essere utilizzata in questo modo:

// Performance del seguente programma:
// circa 6.000 solutionspersec on T1000 (Athlon 1 GHZ)
// circa 15.000 solutionspersec on Pentium 4 HT 3 GHZ
//
public class TestConsole {
    public static void main( String[] args ) {
        int[] diabolic = {  0,1,4,  0,0,0,  9,5,0,
                                 0,0,8,  0,4,0,  2,0,0,
                                 0,9,0,  5,0,3,  0,6,0,

                                 0,0,0,  3,8,4,  0,0,0,
                                 7,0,0,  0,0,0,  0,0,6,
                                 0,0,0,  6,9,7,  0,0,0,

                                 0,4,0,  2,0,1,  0,8,0,
                                 0,0,1,  0,6,0,  3,0,0,
                                 0,5,2,  0,0,0,  6,1,0};

            System.out.println("Start game...");
            Sudoku Game = new Sudoku( diabolic );
            Game.printGrid();
	    Game.solve();
	    System.out.println("Done");
            System.out.println("First Solution of the game:");
            System.out.println("Number of Solutions: " + Game.solutionsCount );
            System.out.println();
        }
    }

Il codice deve essere compilato installando un JDK versione 1.5 o seguente.
Alla prossima.

Inserito da: robitex | gennaio 10, 2010

Differenza tra due date con LuaTeX

Il problema

Alcune volte ci troviamo a dover calcolare la differenza in giorni che intercorre tra due date. Se stiamo lavorando in un Word processor occorre rivolgersi ad uno strumento esterno come un foglio di calcolo, mentre con gli strumenti di tipografia asincrona (LaTeX e compagnia), linguaggi naturalmente estensibili, una macro risolverebbe brillantemente questo problema.

Una soluzione in Lua

I linguaggi come LaTeX o ConTeXt sono attualmente basati su TeX, il motore di composizione di Donald Knuth, perfetti per produrre documenti di elevata qualità, ma proprio per questo, non così facili da programmare.

Da qualche tempo il mondo TeX si sta preparando ad accogliere un nuovo motore di composizione basato sia su TeX stesso sia su Lua, un efficiente linguaggio di scripting portabile ed estensibile a sua volta: luatex.

Attualmente luatex è in fase di sviluppo, ma se sul vostro sistema è installata una distribuzione recente di LaTeX come la TeX Live 2009, probabilmente è già a vostra disposizione.

Utilizziamo la funzione os.time() della operating system library di Lua per tradurre le date in un unico valore numerico, il numero di secondi trascorsi da un certo epoch. A questo punto basta fare la differenza ed esprimere il risultato in giorni anziché in secondi ed il gioco è fatto. Ecco il frammento di codice:

local date1 = os.time({day=1,month=1,year=2010})
local date2 = os.time({day=28,month=7,year=2010})
local secperdays = 24*60*60
local daysdiff = (date2 - date1) / secperdays
print("Days difference: " .. daysdiff)

La funzione os.time() accetta una tabella con le tre chiavi day, month ed year. Nel codice abbiamo dunque racchiuso tra parentesi graffe la tabella nella constructor expression prevista dal linguaggio Lua.

Una soluzione in luatex

In luatex scriveremo dunque una macro utilizzando il classico comando \def per cui si prevede due argomenti: la data precedente e poi quella successiva dell’intervallo temporale:

\def\datediff#1#2{\directlua{
    local date1 = os.time({#1})
    local date2 = os.time({#2})
    local secperdays = 24*60*60
    local daysdiff = (date2 - date1) / secperdays

    tex.print(math.floor(daysdiff))}}

% inizio documento
Dalla mia nascita ad oggi sono trascorsi esattamente
\datediff{year=1900,month=1,day=1}{year=2010,month=1,day=10}
giorni.

Eppure mi pare un numero del tutto privo di emozioni...
\end

Salvate il codice in un file con estensione .tex e compilate il tutto con il comando:

luatex nomefile.tex

Nella directory trovere il file pdf di output con il risultato.
Le funzioni della libreria standard di Lua sono facili da usare e generalmente il codice è compatto ed efficiente. Così è facile prevedere una stagione ancora più ricca per gli utenti di LaTeX e compagnia.

Alla prossima. Gulp.

Inserito da: robitex | dicembre 31, 2009

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!!!

Inserito da: robitex | dicembre 21, 2009

Buon Natale 2009

Snow on the beach

Snow on the beach

Ecco come appare la spiaggia a pochi giorni dal Natale, ed era parecchio tempo che non si vedeva uno spettacolo simile da queste parti. Un ottima occasione per augurare a tutto il mondo Buon Natale.

Buon Natale!!!

Inserito da: robitex | novembre 23, 2009

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 sul tratto centrale che si eliminerà, e si reitera all’infinito.

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.

Inserito da: robitex | novembre 13, 2009

Dalla Pretesting alla TeX Live 2009

Ciao a te,
con l’arrivo della versione ufficiale della TeX Live 2009, l’11 novembre scorso, la versione Pretesting resa pubblica a scopi di test e feedback, ha perso i suoi repository.

Gli utenti che hanno installato la TeX Live Pretesting cosa devono fare?
La risposta è moooolto semplice e non è installare tutto da zero, ma basta rilocalizzare il repository!.

Dalla TeX Live Pretesting alla 2009 senza passare dal VIA!

Dunque, intanto sistemate sul Desktop in modo che stia comoda, una finestra di console (che abbiate a che fare con l’Explorer di Windows o con il DE di Linux è la stessa cosa).
Possibilmente sorseggiando un buon te, selezionate il mirror CTAN di cui vi fidate per esempio ftp.snt.utwente.nl con il comando (servono i diritti di root):

tlmgr option repository ftp://ftp.snt.utwente.nl/pub/software/tex/systems/texlive/tlnet

From TeX Live Pretesting to TeX Live 2009

From TeX Live Pretesting to TeX Live 2009, tlmgr command

Benvenuti nella TeX Live 2009!!!

Inserito da: robitex | novembre 7, 2009

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!

Inserito da: robitex | novembre 5, 2009

Introduzione a METAPOST

METAPOST

Il linguaggio METAPOST possiede alcune caratteristiche peculiari come la possibilità di risolvere sistemi di equazioni lineari, ma la cosa che mi ha convinto a provare il babbo di Asymptote è stata la decisione degli sviluppatori di luatex di includerlo come libreria nativa con il nome di MPlib.

La sintassi di METAPOST sviluppato originariamente da John D. Hobby, è piuttosto asciutta, del resto deriva dal METAFONT l’originale programma di creazione di font tipografici dello stesso Donald Knuth, creatore di TeX, a differenza del quale produce un output Postscript (quindi il nome), digeribile anche da pdflatex (i disegni possono essere inseriti nel documento direttamente senza conversioni).

Naturalmente una qualsiasi distribuzione LaTeX (che sia TeX Live, MiKTeX o MacTeX), installarerà sul vostro sistema (che sia Linux, Windows o MacOS), la più recente versione di METAPOST.

Primo codice

La funzione primitiva per disegnare è chiamata (ovviamente) draw ed accetta come argomenti percorsi ed anche intere figure chiamate picture. Per esempio per disegnare un rettangolo 36×28 mm si scrive:

draw origin -- (36mm,0) -- (36mm,28mm) -- (0,28mm) -- cycle;

od anche (aggiungendo un po’ di colore):

u = 1mm;
draw origin -- (36u,0) -- (36u,28u) -- (0,28u) -- cycle
withcolor .625red;

I punti del tipo pair vengono istanziati descrivendone le coordinate tra parentesi tonde ed equiparando l’idea di punto a vettore con il primo estremo nell’origine, così da poter effettuare trasformazioni geometriche come traslazioni (chiave shifted) oppure rotazioni (chiave rotated) od ancora operazioni di scala (chiavi scaled, xscaled, yscaled, zscaled).

Definiamo il rettangolo come percorso e disegnamolo due volte, la prima con riepimento grigio e la seconda solo con il contorno ma traslato del vettore (2mm,2mm), vediamo l’esempio (completando l’esempio con le istruzioni iniziali e finali indispensabili per la corretta compilazione):

% prima prova con METAPOST
beginfig(1);
u = 1.0mm;
path p;
p = origin -- (36u,0) -- (36u,28u) -- (0,28u) -- cycle;

fill p shifted (2u,2u) withcolor .75white;
draw p withcolor .625red;

endfig;
end

ed ecco il risultato:

First test of the METAPOST language

First test of the METAPOST language

Il parametro 1 passato alla funzione beginfig() non è altro che il numero che viene assegnato come estensione al file del disegno. Vengono infatti creati tanti file di output quanti ambienti beginfig()…endfig vi sono nel sorgente e quel numero serve appunto a numerarli.

Il codice va inserito in un file di testo con estensione .mp e poi compilato per ottenere i file dei disegni (il piccolo grande editor SciTE può essere di (grande) aiuto, come pure un visualizzatore Postscript).

Come vedete ci sono i punti e virgola finali che consentono di impaginare il sorgente come si desidera, i commenti che iniziano con il simbolo di percento, ed una sinteticità espressiva generale che in qualche modo esalta la potenza grafica di METAPOST (non lasciatevi ingannare dalla sua modestia…).

Esiste anche la possibilità di instanziare il vettore unitario con direzione specificata per mezzo della funzione dir() che accetta il valore dell’angolo con verso antiorario dall’asse x (così il punto dir(0) è del tutto equivalente al punto (1,0) ).

Per individuare un punto su un segmento è possibile utilizzare l’espressione factor[p1,p2], se factor = 0.50 verrà restituito il punto di mezzo.

Ecco l’ultimo esempio:

% seconda prova con METAPOST
beginfig(1);
% disegnamo un triangolo equilatero
u = 5 mm;

pair A; A=origin;
pair B; B=(7u,0);
pair C; C=(dir(60) scaled 7u);

pair m; m = 0.5[B,C];
pair g; g = 2/3[A,m];

path p; p = A--B--C--cycle;

% regolazione dello spessore della penna
pickup pencircle scaled 4pt;

draw p
withcolor .625red;
fill p scaled .8 shifted .2 g
withcolor .565blue;

endfig;
end

More difficult METAPOST example

More difficult METAPOST example

Bene, ora ne sappiamo abbastanza per procedere, ma mi raccomando, la comprensione dei concetti qui esposti è molto superiore se eseguirete anche degli esercizi con METAPOST, pertanto fateli!!! Ne ricaverete ottime basi e grande soddisfazione.

Happy METAPOST!

Inserito da: robitex | novembre 4, 2009

Testare i server europei di CTAN

L’idea

Qualche tempo fa, durante i test della TeX Live 2009, la distribuzione TeX di imminente rilascio, proposi agli sviluppatori di dotare lo script tlmgr della possibilità di scaricare i file da più di un mirror CTAN allo stesso tempo per superare eventuali problemi con il server abituale (in quel momento il server ufficiale italiano era lentissimo).

L’idea venne rifiutata adducendo problemi tecnici, allora non ci rimane che ingegnarci per rispondere alla domanda qual’è il server mirror più veloce da settare come fonte di default per la nostra distribuzione?

netselect

L’utility netselect fa al caso nostro: prende in input una serie di url server e ne stila una classifica impietosa!!. Naturalmente il risultato non ha un valore assoluto poiché dipende dalla connessione che si sta utilizzando e dal traffico in quel momento presente, ecc.

CTAN test server

CTAN european server test result

Installiamolo in un attimo

Apriamo un terminale e digitiamo con i diritti di root:
apt-get install netselect

naturalmente stiamo parlando di un sistema Debian Like, per esempio Ubuntu.

Test sui server CTAN!

Copiate in un file il testo seguente, non è altro che il lancio di netselect su TUTTI i server europei di CTAN alla data di oggi (ci vuole un po’ di pazienza ma la faticaccia l’ho fatta io per voi :-) ), rendetegli i permessi di esecuzione e lanciatelo da terminale.

#!/bin/sh
netselect -vvv ftp.univie.ac.at gd.tuwien.ac.at mirrors.dotsrc.org \
mirrors.dotsrc.org ftp.linux.ee ftp.funet.fi ctan.mines-albi.fr \
distrib-coffee.ipsl.jussieu.fr distrib-coffee.ipsl.jussieu.fr \
ftp.inria.fr ftp.oleane.net mirrors.ircam.fr \
ftp.fernuni-hagen.de ftp.fu-berlin.de ftp.gwdg.de \
ftp.join.uni-muenster.de \
ftp.mpi-sb.mpg.de ftp.tu-chemnitz.de ftp.uni-erlangen.de \
sunsite.informatik.rwth-aachen.de ftp.cc.uoc.gr ftp.ntua.gr \
ftp.uoi.gr ftp.sztaki.hu ftp.heanet.ie \
ftp.heanet.ie ftp.uniRoma2.it ftp.fagskolen.gjovik.no \
ftp.fagskolen.gjovik.no ftp.gust.org.pl ftp.tpnet.pl \
mirror.icis.pcz.pl ftp.piotrkosoft.net \
piotrkosoft.net sunsite.icm.edu.pl ftp.di.uminho.pt \
ftp.eq.uc.pt ftp.ist.utl.pt neacm.fe.up.pt ftp.roedu.net \
ftp.chg.ru ftp.vsu.ru mirror.macomnet.net tex.ihep.su \
ftp.gui.uva.es ftp.rediris.es ftp.udc.es mirror.switch.ch \
ftp.cstug.cz ftp.cvut.cz ftp.ntg.nl www.cs.ruu.nl \
ftp.snt.utwente.nl anorien.csc.warwick.ac.uk \
anorien.csc.warwick.ac.uk mirror.ox.ac.uk

Chiamate il file contenente il comando con il nome di mirtest.sh e lanciatelo come root da terminale con l’istruzione: sudo ./mirtest.sh, ecco fatto.

Risultato

Anche in questo caso l’Italia non brilla, con un solo server ufficiale sito a Roma (punteggio 413), ma sarete senz’altro curiosi di conoscere il vincitore:

The winner is: ftp.snt.utwente.nl !!!

Il server ha ottenuto un punteggio di 165.
Fate anche voi la prova riportando qui un commento con il vostro vincitore.
Alla prossima! e buona gara.

Articoli precedenti »

Categorie