Durante lo sviluppo di Cobra Draugths ho avuto la necessità di trovare un profiler per Python che facesse al caso mio. Dato che non c’è un unico strumento adatto ho deciso di spiegare passo passo come ottenere dei dati di profiling veloci, puliti e graficamente guardabili.

È una guida espressa: niente discussioni, niente commenti, solo fatti e procedure.

Continue reading »

 

Nella scorsa parte abbiamo visto come abilitare l’interfaccia di admin. Abbiamo notato però che non tutto era esattamente come ce lo aspettavamo. Per prima cosa tutte le tabelle terminavano con una fastidiosa “s” dovuta al plurale inglese, secondo, gli oggetti venivano visualizzati con un anonimo “Artista Object”, “Genere Object” e così via.

Per sistemare questi punti c’è un modo molto semplice.

  • Specificare il metodo __unicode__.
  • Aggiungere l’opzione verbose_name_plural come parametro della classe Meta di ogni oggetto del modello.
  • Impostare LANGUAGE_CODE = 'it-IT' nel file settings.py

Il metodo unicode specifica come verrà visualizzato un oggetto del modello X al posto della dicitura standard “X Object”. Per chi conosce il python è praticamente equivalente a ridefinire il metodo speciale __str__[/cc] di una classe (il metodo [cci]__str__ dei modelli Django chiamano proprio __unicode__).

La classe Meta, invece, è una classe speciale che permette di passare ai modelli delle opzioni particolari. Vedremo altre opzioni nel corso del tutorial. L’opzione che ci interessa ora è verbose_name_plural : se questo valore è None allora viene usato il suffisso “s” come nel caso inglese, altrimenti viene usato il valore della variabile.

Vediamo quindi qualche esempio:

class Artista(models.Model):
    nome = models.CharField(max_length=50)
    cognome = models.CharField(max_length=50)
    ruoli = models.ManyToManyField(Ruolo)

    def __unicode__(self) :
      return u"%s %s" % (self.nome, self.cognome)

    class Meta:
      verbose_name_plural = "Artisti"

Mostro il caso del modello “Artista”. Allo stesso modo intervenite anche su tutti i modelli restanti. Potete vedere il risultato in figura.

Come potete vedere adesso il risultato è molto più accettabile. La prossima volta vedremo come abbozzare un interfaccia pubblica al nostro sito.

 

Quando si sviluppa un applicazione web creare pannelli di admin è solitamente noioso e fastidioso eppure necessario per permettere, ad esempio, al gestore del negozio online che stiamo progettando di aggiungere oggetti al catalogo senza dover conoscere una riga di codice o del funzionamento di un DB. Per fortuna Django ci offre una valida scorciatoia: creare un interfaccia admin con Django ci costa solamente una decina di righe di codice.

Per prima cosa abilitiamo il pannello di admin modificando un paio di righe:

  • Decommentiamo la riga django.contrib.admin di INSTALLED_APPS in settings.py
  • Decommentiamo from django.contrib import admin e admin.autodiscover() da urls.py
  • Decommentiamo (r'^admin/', include(admin.site.urls)), da urlpatterns in settings.py

Per ultimo creiamo ex-novo un file admin.py nella nostra apps con questo contenuto:

from django.contrib import admin
from musicdb.models import *

admin.site.register(Artista)
admin.site.register(Genere)
admin.site.register(Ruolo)
admin.site.register(Gruppo)
admin.site.register(Album)
admin.site.register(Traccia)

Poi avviamo il server e andiamo su

http://localhost:8000/admin/

Per accedere al favoloso pannello di amministratore.

La schermata di login del pannello di admin!

La schermata principale del pannello di amministrazione!

Semplice, facile ma estremamente potente. :)

Tuttavia ci sono ancora alcuni difetti. Ad esempio Django pluralizza le parole aggiungendo il suffisso “s” alla parola come nel caso inglese, cosa che non va bene in italiano; inoltre se apriamo una tabella ci troveremo davanti tanti oggetti anonimi come “Artista object”, “Gruppo object” e così via mentre noi vogliamo che ci indichi, ad esempio, nome e cognome di un artista o il nome del gruppo.

Rimuoveremo tutti questi piccoli difetti la prossima volta con alcune piccole modifiche al modello.

 

Come ogni applicazione che si rispetti, abbiamo bisogno di impostare e generare la struttura dati del nostro programma. La primissima cosa da fare è quella di istruire Django ad utilizzare un database.

Per questa guida sceglieremo PostgreSQL. Per configurarlo potete seguire la guida per installare PostgreSQL.

Una volta installato e configurato il database apriamo il file settings.py e cerchiamo la riga DATABASES e modifichiamola nel modo seguente:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'musicdb',
        'USER': 'vostro_nome',
        'PASSWORD': 'password',
        'HOST': '',
        'PORT': '',
    }
}

Dove vostro_nome e password sono l’username e la password che avete impostato nella fase di configurazione del database. Inoltre dovete creare preventivamente un database di nome “musicdb”. Per completezza spieghiamo tutti i parametri:

  • ENGINE è il modulo di django di backend che si interfaccia con lo specifico database. Scegliamo quello del nostro database.
  • NAME è il nome del database o del file nel caso di SQLite.
  • USER è il nome utente per l’accesso al db (non serve nel caso di SQLite).
  • PASSWORD è la password di accesso.
  • HOST e PORT sono i dati che indicano la posizione e la porta di accesso a db nel caso il database non sia sulla stessa macchina del server.

Assicuratevi anche che sia installato il pacchetto:

python-psycopg2

Necessario per far interfacciare python al DB.

A questo punto non ci resta che creare lo schema del database. Per fare ciò apriamo il file models.py e inseriamo una cosa del tipo:

from django.db import models

# Create your models here.
class Ruolo(models.Model) :
    descrizione = models.CharField(max_length=30)

class Artista(models.Model):
    nome = models.CharField(max_length=50)
    cognome = models.CharField(max_length=50)
    ruoli = models.ManyToManyField(Ruolo)

class Gruppo(models.Model):
    nome = models.CharField(max_length=50)
    membri = models.ManyToManyField(Artista)

class Genere(models.Model):
    descrizione = models.CharField(max_length=30)

class Traccia(models.Model):
    nome = models.CharField(max_length=50)
    durata = models.TimeField()

class Album(models.Model):
    nome = models.CharField(max_length=50)
    gruppo = models.ForeignKey(Gruppo)
    genere = models.ForeignKey(Genere)
    data = models.DateField()
    tracce = models.ManyToManyField(Traccia)

Questo file specifica la struttura delle tabelle del database. Tutto senza scrivere una sola riga di SQL. Vediamo un po il significato di alcuni campi:

  • CharField è un campo di testo semplice. Il parametro max_length specifica il numero massimo di caratteri memorizzabili nel campo.
  • ForeignKey specifica un collegamento uno-a-molti verso un altra tabella. Ad esempio un Album ha un unico Gruppo che lo suona ma un Gruppo può avere uno o più Album nella propria discografia.
  • DateField specifica una data.
  • TimeField specifica un tempo.
  • ManyToMany specifica un collegamento molti-a-molti verso un altra tabella. Ad esempio un Gruppo può avere uno o più Artisti che ne fanno parte, ma un Artista può partecipare a uno o più gruppi.

Ora, rendiamo questo modello un database vero e proprio con il comando:

python manage.py syncdb

Se tutto è andato a buon fine e il modello è valido ci verrà chiesto se vogliamo creare una zona del database per gli utenti. Diciamo di si. Ci verrà chiesto di creare il super-user, creiamolo indicando un nome e la password.

A questo punto possiamo provare il nostro database.

Apriamo la shell di django con

python manage.py shell

E all’interno digitiamo:

from musicdb.models import *
freddie = Autore(nome="Freddie, cognome="Mercury")
freddie.save()

Con pgAdmin3 possiamo facilmente vedere che il cantante è stato aggiunto nella giusta tabella.

Ebbene si. Freddie c'è.

 

Continuiamo il nostro tutorial che ci porterà a implementare una semplice applicazione web per gestire la nostra collezione musicale.

Ora che la base funziona dobbiamo iniziare a scrivere qualcosa di più complesso che una misera pagina di default. A questo scopo ci servono le viste. Le viste non sono altro che quella parte di programma che si occupa di recuperare tutte le informazioni necessarie alla visualizzazione della pagina che poi verranno passate ad un template in modo da essere trasformate in pagine web a tutti gli effetti. Solitamente le viste interrogano un database per recuperare i dati, questa volta invece cominceremo da una semplice vista di test.

La prima cosa da fare è creare una nuova applicazione che chiameremo musicdb in quanto sarà il centro vero e proprio della web application. Un sito complesso è solitamente composto da una o più applicazioni collegate fra loro. Piccoli siti, come quello che stiamo per fare, utilizzano solo un applicazione. Per fare questo, all’interno della cartella principale del sito, digitiamo:

django-admin startapp musicdb

Questo creerà un ulteriore cartella che contiene altri 4 file.

  • __init__.py Sempre il solito file init.
  • models.py Contiene i “modelli” ovvero la struttura del database espressa in python tramite le API di Django.
  • tests.py Contiene dei test per valutare la correttezza di query, associazioni, modelli e viste.
  • views.py Contiene le viste, ovvero quelle funzioni che si occupano di processare le richieste dell’utente generando dei contenuti (tramite i template) che verranno poi inviati al browser.

La seconda cosa da fare è aggiungere la nostra applicazione alle applicazioni del sito. Andiamo nel file “settings.py” e aggiungete ‘musicdb’ alle applicazioni in questo modo:

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    # Uncomment the next line to enable the admin:
    # 'django.contrib.admin',
    'musicdb',
)

Poi dobbiamo scrivere una vista di prova. Andiamo nel file “views.py” dell’applicazione musicdb e inseriamo il seguente codice:

from django.http import HttpResponse

def view_test(request) :
    return HttpResponse("
<h1>Ciao! Questa è la tua applicazione!</h1>
"
)

Ora, dobbiamo solo collegare un URL a questa vista e quindi andiamo nel file “urls.py” e modifichiamo “urlpatterns” in questo modo:

urlpatterns = patterns('',
    # Example:
    # (r'^musicz/', include('musicz.foo.urls')),

    # Uncomment the admin/doc line below and add 'django.contrib.admindocs'
    # to INSTALLED_APPS to enable admin documentation:
    # (r'^admin/doc/', include('django.contrib.admindocs.urls')),

    # Uncomment the next line to enable the admin:
    # (r'^admin/', include(admin.site.urls)),
    ( r'^test/$', "musicdb.views.view_test"),
)

Il primo elemento della tupla è una espressione regolare che serve ad intercettare l’url e a indirizzarlo alla funzione indicata come secondo elemento della tupla.

A questo punto non ci resta che testare tutto. Avviamo il server (python manage.py runserver) e andiamo all’indirizzo http://localhost:8000/test/. Quello che ci troviamo davanti dovrebbe essere una pagina come quella in figura.

Che bello! Funziona!

 

Vi propongo passo passo un tutorial che ci porterà a realizzare una pagina web dinamica contenente informazioni sulla nostra collezione musicale. In questo modo potrete vedere come prende forma un progetto utilizzando quella meraviglia tecnologica che è Django. Gli unici requisiti sono la conoscenza del Python nella sua forma base (for, if, classi, funzioni, tuple, liste e dizionari). Non ci resta che iniziare a impostare la cartella del progetto e verificare che tutto funzioni.

La prima cosa da fare è andare in una cartella e digitare:

django-admin startproject musicz

Verrà creata una cartella di nome musicz che contiene 4 file.

  • __init__.py è il file che indica all’interprete python di trattare la cartella come un package. Il file è solitamente vuoto e vuoto deve solitamente rimanere.
  • manage.py è un file di servizio che vi offre un interfaccia molto comoda ad alcune “utility” di Django. Ne faremo largamente uso da ora in avanti.
  • setting.py è il file che contiene tutti i settaggi che faremo all’applicazione.
  • urls.py è il file che contiene la specifica mappatura degli urls verso le funzioni del programma.

La cartella contenente i nostri 4 file.

Se tutto è stato fatto correttamente vi basterà digitare

python manage.py runserver

Per avviare il web-server di prova. A questo punto non vi resta che andare all’indirizzo http://localhost:8000/ per vedere un rassicurante messaggio da parte di Django.

Django ci dice che va tutto ok!

 

Come già saprà chi mi segue su Twitter, questa settimana in uno dei miei lampi mi è venuta l’idea di approcciarmi alle web application. Quando uno si affaccia a questo panorama la scelta sembra quasi obbligata: PHP. Tuttavia non avevo voglia di mettermi a studiare intensamente il PHP, sia perché lo trovo sintatticamente bruttino sia per voglia di esplorare degli orizzonti più nascosti.

Così, conoscendo il Python piuttosto bene e conoscendo l’esistenza di Django, mi sono chiesto: perché non approfondire un po questo framework? Detto fatto.

La prima cosa da fare era girare su Google per cercare una specie di guida introduttiva. Ne ho trovate parecchie, tuttavia nessuna che spiegasse in realtà come si potesse installare Django su linux senza perderci mezza giornata. Ho dovuto svelare l’arcano leggendo sui forum e scoprire che è una cavolata immensa:

apt-get install python-django

Fine. Nulla più nulla meno. Nessuna configurazione astrusa, compilazioni varie e configurazioni di server. Basta digitare il comando e il sistema fa tutto da solo. Una volta installato possiamo provarlo. Andiamo in una cartella e digitiamo:

django-admin startproject esempio

Poi entriamo nella cartella e digitiamo:

python manage.py runserver

Per avviare il nostro leggero server di sviluppo (non serve configurare server imponenti come Apache, Django offre di suo un server per provare i vostri siti). Quindi andiamo alla pagina http://127.0.0.1:8000/ per ricevere la conferma che Django è perfettamente installato e configurato.

Tutto qui. Per chi volesse imparare ad usare Django (oltre che a installarlo) può visitare questo ottimo libro on-line: http://www.djangobook.com/en/2.0/

 

Quello che segue è un altro piccolo esempio di come si possano scrivere programmi che implementano agenti intelligenti in una manciata di righe.

Supponiamo di voler replicare digitalmente il comportamento di una formica che insegue una traccia di ferormone fino ad un obiettivo. La traccia, per semplicità, non si sovrappone ed è sempre divisa da almeno una casella da un altra porzione di traccia.

Il comportamento di questa formica può essere emulato facendo uso di un agente reattivo con un bit di stato. Questi agenti hanno dei dati in ingresso che elaborano e trasformano in una serie di azione seguendo una o più regole di produzione. La nostra formica, ad esempio, può percepire solo se nella casella di fronte a lei c’è la traccia di ferormone oppure no.

La nostra formica è capace di effettuare solo le seguenti azioni:

  • Avanzare: avanza di una casella.
  • Ruotare a destra: ruota a destra.
  • Ruotare a sinistra: ruota a sinistra.
  • On: attiva il bit di stato.
  • Off: disattiva il bi di stato.
  • Sensi: riceve le informazioni sensoriali (nel nostro caso solo la presenza di ferormone nella casella di fronte) mettendo a Vero o Falso la variabile ferormone

A questo corrispondono le seguenti regole di produzioni.

  • S1: Se ferormone = True e stato = 0 -> Avanzare
  • S2: Se ferormone = False e stato = 0 -> On e Ruota a destra
  • S3: Se ferormone = True e stato = 1 -> Off e Avanzare
  • S4: Se ferormone = False e stato = 1 -> Ruota a destra x 2 e Off

Tutto questo è impleementato nel seguente codice:

# -*- coding: utf-8 -*-
"""
Questo modulo implementa una formica in grado di seguire
un percorso più un metodo per generare un percorso
casuale.
"""

import random

def generate_map(x, y) :
  """
  Genera un percorso casuale su una mappa X x Y.

    Args:
      X (int) larghezza della mappa
      Y (int) altezza della mappa

    Return:
      Una griglia X x Y contenente un percorso per la formica digitale.
  """

  mappa = [[0 for i in range(x)] for j in range(y) ] # Inizializza mappa vuota.
  i , j = 1, 1 # Posizione iniziale.
  mappa[j][i] = 1
  avaiable = ['right'] # Il primo passo va eseguito sempre a destra per semplicità.

  while len(avaiable) > 0 :
    next = random.sample(avaiable,1)

    if next[0] == 'up' :
      j -= 1
    elif next[0] == 'down' :
      j += 1
    elif next[0] == 'left' :
      i -= 1
    elif next[0] == 'right' :
      i += 1

    mappa[j][i] = 1

    avaiable = pick_avaiable(mappa,i,j)

  else :

    mappa[j][i] = 2 # Alla fine setta il punto obiettivo.

  return mappa

def pick_avaiable(mappa, i, j) :
  """
  Funzione ausiliaria per la generazione della mappa.
  Data una mappa e la posizione attuale restituisce le direzioni
  percorribili in accordo con la regola di non sovrapposizione.

    Args:
      mappa (mappa) La mappa corrente
      i (int) attuale posizione x
      j (int) attuale posizione y

    Return:
      Una lista di possibili direzioni.
  """

  new_avaiable = []

  if j > 1 :
    t = mappa[j-1][i-1] == 0 and mappa[j-1][i] == 0 and mappa[j-1][i+1] == 0
    t = t and mappa[j-2][i-1] == 0 and mappa[j-2][i] == 0 and mappa[j-2][i+1] == 0
    if t:
      new_avaiable.append('up')
  if j < (len(mappa) - 2) :     t = mappa[j+1][i-1] == 0 and mappa[j+1][i] == 0 and mappa[j+1][i+1] == 0     t = t and mappa[j+2][i-1] == 0 and mappa[j+2][i] == 0 and mappa[j+2][i+1] == 0     if t:       new_avaiable.append('down')   if i &gt; 1 :
    t = mappa[j-1][i-1] == 0 and mappa[j][i-1] == 0 and mappa[j+1][i-1] == 0
    t = t and mappa[j-1][i-2] == 0 and mappa[j][i-2] == 0 and mappa[j+1][i-2] == 0
    if t:
      new_avaiable.append('left')
  if i < (len(mappa[0]) - 2) :
    t = mappa[j-1][i+1] == 0 and mappa[j][i+1] == 0 and mappa[j+1][i+1] == 0
    t = t and mappa[j-1][i+2] == 0 and mappa[j][i+2] == 0 and mappa[j+1][i+2] == 0
    if t:
      new_avaiable.append('right')

  return new_avaiable

class Ant(object) :
  """
  Questa classe implementa la formica digitale.
  """


  VERSO = {'up': 0, 'right': 1, 'down': 2, 'left':3}

  def __init__(self, mappa) :
    self.x = 1
    self.y = 1
    self.mappa = mappa
    self.bit = False
    self.verso = self.VERSO['right']

  def r_rotate(self) :
    """
    Ruota la formica a destra.
    """

    self.verso += 1
    if self.verso == 4 :
      self.verso = 0

  def l_rotate(self) :
    """
    Ruota la formica a sinistra. Non usato attualmente.
    """

    self.verso -= 1
    if self.verso == -1 :
      self.verso = 3

  def forward(self) :
    """
    Fa avanzare di una casela la formica.
    """

    if self.verso == self.VERSO['up'] :
      self.y -= 1
    if self.verso == self.VERSO['down'] :
      self.y += 1
    if self.verso == self.VERSO['left'] :
      self.x -= 1
    if self.verso == self.VERSO['right'] :
      self.x += 1    

  def est_ferormone(self) :
    """
    Restituisce 1 se la casella daavanti al formica contiene
    del ferormone, 2 se è la casella obiettivo e 0 se la cella è vuota.
    """

    if self.verso == self.VERSO['up'] :
      return self.mappa[self.y-1][self.x]
    if self.verso == self.VERSO['down'] :
      return self.mappa[self.y+1][self.x]
    if self.verso == self.VERSO['left'] :
      return self.mappa[self.y][self.x -1]
    if self.verso == self.VERSO['right'] :
      return self.mappa[self.y][self.x + 1]

  def move(self) :
    """
    Cervello della formica. Elabora il comportamento in base
    alle regole di produzione sotto elencate.
    """

    if self.est_ferormone() == 2 :
      self.forward()
      return 'COMPLETE'
    if self.est_ferormone() and not self.bit :
      self.forward()
      return 'S1'
    if not self.est_ferormone() and not self.bit :
      self.bit = True
      self.r_rotate()
      return 'S2'
    if self.est_ferormone() and self.bit :
      self.bit = False
      self.forward()
      return 'S3'
    if not self.est_ferormone() and self.bit :
      self.r_rotate()
      self.r_rotate()
      self.bit = False
      return 'S4'

  def print_map_ant(self) :
    """
    Stampa la mappa del percorso contrassegnando
    la formica con una X.
    """

    print "-"*(len(self.mappa[0])*3)
    for j in range(len(self.mappa)) :
      for i in range(len(self.mappa[j])) :
        if i == self.x and j == self.y :
          print 'X ',
        elif self.mappa[j][i] == 0 :
          print "  ",
        else :
          print "%d " % self.mappa[j][i],
      print ":"
    print "-"*(len(self.mappa[0])*3)

Il codice contiene, oltre alla classe Ant che implementa la formica, una funzione per generare un percorso casuale che rispetta le regole che abbiamo fissato. Ad ogni chiamata del metodo move la formica digitale elabora i dati a sua disposizione e restituisce la regola di produzione prescelta (oppure COMPLETE se la formica ha raggiunto l’obbiettivo posto alla fine della traccia).

Inoltre la formica ha un metodo che stampa a schermo l’intera mappa del mondo, con la traccia e la posizione attuale della stessa (segnata con una X).

È un esempio carino che magari può essere espanso a piacimento, sia offrendo un output grafico più carino sia rendendo auomatica l’esecuzione dei vari move.

Sperando possiate trovare il codice interessante vi auguro una buona serata. :D

UPDATE:

Potete scaricare il file da qui:Ant.py

 

Questa mattina stavo rivedendo lo script che ho postato ieri. L’ho ottimizzato un po’ e ho tolto una 30-ina di righe. Dopodiché mi sono messo a fare alcuni test prestazionali provando ad addestrare una rete di circa 5000 neuroni disposti in vari modi (da 3 strati da 2000 fino a 250 strati da 20).

Le prestazioni non erano mozzafiato e questo lo sapevo già, il python non è il linguaggio più indicato per applicazioni fortemente CPU bound.

Tuttavia la sorpresa l’ho avuta quando ho testato lo script con la nuova versione di Python (la 3.1) e costatando una diminuzione delle prestazioni di circa il 50%.

Cinquantapercento! Mica bruscolini!

Ho rifatto il conto con vari test (sempre utilizzando il mio script) e il risultato era sempre di questa entità. Le cose sono due:

  1. L’interprete di python3.1 è proprio più lento di suo. In questo caso il problema verrà fuori quando si passerà definitivamente da 2.6 a 3.
  2. Alcune funzioni che nella 2.6 vanno a meraviglia nella 3.1 fanno schifo. In questo caso la cosiddetta migrazione da 2.6 a 3 potrebbe essere meno facile del previsto.

In attesa  di risposte vi chiedo se voi sapete qualcosa di più al riguardo. Se volete poi posso fornire altri dettagli sui vari test.

UPDATE 19/07/10

Ho ripetuto i test su un’altra distribuzione (sidux) e questa volta i risultati sono un po’ diversi. La versione 3 di python è sempre più lenta ma in percentuali accettabili (5% circa).

Inoltre ho provato anche con psyco ottimizzando del 75% le prestazioni del programma.

 

Abbiamo visto come si può disegnare qualsivoglia figura inserendo i vari vertici della figura manualmente. Abbiamo visto anche che prima di inserire un vertice dobbiamo prepararlo impostando, ad esempio, il colore, il vettore normale (vedremo a cosa serve quando parleremo della luce), le texture e via dicendo. Questo può sembrarvi estremamente macchinoso… e lo è: dobbiamo usare circa 3-4 istruzioni per inserire ogni vertice! Un metodo decisamente inadatto per inserire oggetti formati da centinaia di vertici!

Anche volendo disegnare un semplice cubo (quindi con la miseria di 8 vertici) ci rendiamo conto che ogni vertice andrebbe specificato ben 3 volte (uno per ogni faccia a cui appartiene) con inutile spreco di prestazioni!

Ovviamente esiste la soluzione: usare gli array. Tramite gli array possiamo inserire con una manciata di comandi intere liste di vertici. Ma andiamo per passi.

1 – ABILITARE GLI ARRAY

Come gran parte degli stati di OpenGL gli array sono disabilitati di default. Esistono 8 diversi array da abilitare:

  • GL_VERTEX_ARRAY
  • GL_COLOR_ARRAY
  • GL_SECOND_COLOR_ARRAY
  • GL_INDEX_ARRAY
  • GL_NORMAL_ARRAY
  • GL_FOG_COORDINATE_ARRAY
  • GL_TEXTURE_COORDINATE_ARRAY
  • GL_EDGE_FLAG_ARRAY

Ovviamente non ci serviranno tutte adesso e possiamo limitarci, ad esempio, solamente a vertici e colori. Possiamo quindi abilitare gli array che ci interessano con glEnableClientState() come abbiamo visto in precedenza.

2 – INSERIRE DATI NEGLI ARRAY

Una volta che gli array sono abilitati bisogna inserire dei dati al loro interno. Esiste una funzione per ogni tipo di array. Noi però per il momento vedremo solamente le funzioni che ci interessano:

  • glVertexPointer(size, type, stride, pointer) è la funzione che permette di inserire un vettore all’interno dell’array dei vertici. size è semplicemente il numero di coordinate per vertice (può valere solo 2,3 o 4). type indica il tipo con cui sono specificati i vertici (GL_INT_, GL_FLOAT e così via). stride è un valore che indica l’offeset fra vertici consecutivi all’interno del vettore. Questo parametro è solitamente 0 per array che contengono solamente vertici. pointer è invece il puntatore al vettore che contiene i vertici da inserire nell’array.
  • glColorPointer(size, type, stride, pointer) è analoga a sopra ma serve per impostare il vettore dei colori.

3 – UTILIZZARE GLI ARRAY

Esistono diverse funzioni per utilizzare i dati presenti negli array. La prima funzione base è glArrayElement(k). Questa funzione non fa altro che inserire il k-esimo elemento dell’array. Facciamo un esempio. Supponiamo di avere due array vertex e color correttamente inizializzati e inseriti negli array di OpenGL.

A questo punto i due codici sono equivalenti:

glBegin(GL_TRIANGLES)
glArrayElement(2)
glArrayElement(3)
glArrayElement(5)
glEnd()
glBegin(GL_TRIANGLES)
glColor3f(color[2*3], color[2*3 + 1], color[2*3+2])
glVertex2f(color[2*2], color[2*2 + 1])
glColor3f(color[3*3], color[3*3 + 1], color[3*3+2])
glVertex2f(color[3*2], color[3*2 + 1])
glColor3f(color[5*3], color[5*3 + 1], color[5*3+2])
glVertex2f(color[5*2], color[5*2 + 1])
glEnd()

Come potete vedere c’è un bel risparmio. Notate anche che il vertice k-esimo viene colorato con il colore k-esimo.

La seconda funzione che vediamo ci permette di semplificare il tutto ancora di più. Tale funzione è glDrawElements(mode, count, type, indices). Questa funzione è del tutto equivalente a questo codice:

glBegin(mode)
for i in range(count) :
    glArrayElement(indices[i])
glEnd()

Il parametro indices, come avrete notato, è un array di indici. Se indices contiene [0, 4, 6] verranno inseriti in sequenza il vertice 0, 4 e 6 contenuto nell’array di OpenGL. Notate anche che è un errore inserire glDrawElements() all’interno di un blocco glBegin/glEnd.

Una terza funzione è glDrawArray(mode, first, count). Questa funzione è equivalente a:

glBegin(mode)
for i in range(count) :
    glArrayElement(first + i)
glEnd()

La differenza principale con glDrawElements riguarda la scelta degli indici: mentre glDrawElements permette di campionare gli indici in ordine sparso all’interno dell’array di OpenGL, glDrawArray permette solamente di disegnare un gruppo di vertici contiguo.

Per il momento basta così. Vi lascio con un piccolissimo esempio pratico:

from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *

def init():
    glClearColor(0, 0, 0, 0)
    glShadeModel(GL_SMOOTH)

def test():
    glutInit()

def display():
    glClear(GL_COLOR_BUFFER_BIT)

    glEnableClientState(GL_VERTEX_ARRAY)
    glEnableClientState(GL_COLOR_ARRAY)

    vertici = [25, 25,
        100, 325,
        175, 25,
        175, 325,
        250, 25,
        325, 325]

    colori = [1, 0.2, 0.2,
        0.2, 0.2, 1,
        0.8, 1, 0.2,
        0.75, 0.75, 0.75,
        0.35, 0.35, 0.35,
        0.5, 0.5, 0.5]

    glColorPointer(3, GL_FLOAT, 0, colori)
    glVertexPointer(2, GL_INT, 0, vertici)

    indici = [0, 1, 2, 3, 4, 5]

    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, indici)

    glDisableClientState(GL_COLOR_ARRAY)
    glDisableClientState(GL_VERTEX_ARRAY)
    glFlush()

def reshape(w, h):
    glViewport(0, 0, w, h)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    gluOrtho2D(0, w, 0, h)

if __name__ == '__main__':
    glutInit()
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB)
    glutInitWindowSize(350, 350)
    glutInitWindowPosition(100, 100)
    glutCreateWindow("Array: Due Triangoli")
    init()
    glutDisplayFunc(display)
    glutReshapeFunc(reshape)
    glutMainLoop()

L’esempio è piuttosto semplice alla luce della spiegazione.

Con questo si conclude la prima parte del corso dedicata ai vertici. Nella prossima vedremo come manipolare la “telecamera” e gli oggetti. Analizzeremo quindi i 3 aspetti fondamentali della visualizzazione: composizione, inquadratura e proiezione.

Vi ricordo che per ulteriori chiarimenti sono sempre disponibile nei commenti, su LQH e se ci sono potete abusare di me all’indirizzo Jabber: thek3nger@jabber.linux.it

© 2008-2012 SlashCode Suffusion theme by Sayontan Sinha