Dalla visualizzazione di una funzione tridimensionale
al classico Mandelbrot' set explorer


Questo è stato il mio primo (e probabilmente ultimo) programma in VB..

Nato inizialmente per la visualizzazione di una funzione tridimensionale, si è poi naturalmente sviluppato per permettere di "esplorare" oggetti più interessanti, come l'insieme di Mandelbrot1 (di cui è riportata sopra un'immagine inconsueta) o altri frattali.
Un explorer dell'insieme di Mandelbrot in versione online (CGI) si trova sulle pagine di David E. Joyce,della Clark University, in Pennsylvania.


il sorgente(the source code)
l'eseguibile per windows (windows binaries)
runtime libraries per MS VB(Msvbvm50 & dlls)
alcune immagini(some pictures)

come si usa

Se non si ha installato un VB (almeno v. 5), oltre all'eseguibile occorre scaricare le runtime libraries del VB...(il link è a una ricerca preimpostata su google che riporta alcuni siti dove trovarle).
Una volta lanciato il programma, appare la form su cui verrà rappresentata graficamente la funzione:

come funziona

Il funzionamento del programma è molto semplice: crea una form e rappresenta graficamente la funzione fnx(x,y) definita all'inizio del programma;
la funzione viene quindi campionata su 5000 punti aventi coordinate scelte a caso all'interno del campo di definizione (da xmin a xmax e da ymin a ymax) per determinare approssimativamente i punti di massimo e minimo;
ad ogni punto del grafico viene quindi assegnato un colore a seconda del valore assunto dalla funzione e proporzionalmente ai valori di massimo e minimo prima calcolati, colore convertito poi da RGB in HSV (ho tradotto le procedure di conversione RGB-HSV e viceversa dal C : un sorgente si trova ad esempio qui ).

Per modificare il programma basta copiare il sorgente in un file .frm (form VB), avendo ovviamente un compilatore VB recente (io ho usato la vers. 6, ma dovrebbe funzionare anche con versioni precedenti);

Le fasi del progetto, in ordine cronologico, sono state grossomodo queste:





il problema del salvataggio delle immagini

Una volta riusciti a visualizzare una funzione, nasce subito anche la necessità di poter salvare l'immagine su un file (possibilmente in un formato compresso), evitando di fare la stampa della schermata e poi "incollarla" in M$ Paint..

ho cercato un po' con altavista e ho trovato un progetto VB che permette di fare una cosa simile..
Nel seguito ho riportato la parte principale dell'articolo che ne spiega l'utilizzo.
Ci sono da notare alcune cose interessanti:




Saving Pictures to JPG Files from VB


excerpt from Steve McMahon's page (also copyright owner of the article)

Introduction
Whilst Visual Basic provides support for loading graphic files in various formats into a StdPicture object, it sadly forgets about all of these when it comes to saving the file again. You normally only have one choice of file format for writing: a BMP at the system colour depth.

Whilst there are various third party controls available to save files in other formats, these often have unacceptable 'per seat' licensing policies and can be incredibly expensive. This article shows how to use a free JPEG DLL library to save VB pictures.

JPEG File Format
The JPEG file format is aimed at providing very high compression levels for photographic quality images. Before deciding whether you want to use JPEG files in any application you write, there are points to consider about how JPEG operates.

Advantages: Disadvantages: The Independent JPEG Group have done a great deal to make the JPEG format much more accessible to developers by providing free, platform independent C source code to implement JPEG load and save functions. Their library is used in Internet Explorer *, Netscape * Navigator and countless other projects (some of which have even have the audacity to charge for free code!), including a JPEG Library DLL, ijl11.dll, available from the Intel* Developer Site.

IJL11.DLL
This DLL makes conversion to and from JPG simpler by working on a DIB byte format. This makes it easier to use for Windows programmers than the Independent JPEG Group's C code, since the input and output format in memory is a standard Windows format. Conversion between VB StdPicture objects and a DIB is achieved using the cDIBSection Class.

From VB
Download the sample project. This contains all the files required. Firstly, unzip Ijl11.zip and place ijl11.dll in your system's path (ideally in the Windows\System directory). Read the licensing agreement included in the zip. If you are going to use this file for a distributed or commercial project you must download a registered copy (free) from Intel at their JPEG Library page. Then there are two VB files needed: mIJL.bas contains the declares for ijl11.dll and four wrapper functions: All these function accept an instance of the cDIBSection Class. The standard versions of the functions accept a file name as an argument, while the Ptr versions accept a long value pointing to a memory buffer and a variable indicating the buffer size.

Although VB can already load a JPG, LoadJPG is provided as it gives a shortcut for loading a JPG file directly into a cDIBSection class without requiring a StdPicture object. LoadJPGFromPtr allows you to load a JPG directly from a byte array containing the JPG, a function which can't be otherwise be achieved easily in VB.

To save a StdPicture object directly, you need to do this:

Dim c As New cDibSection

   ' Convert Picture object to DIBSection:
   c.CreateFromPicture picThis.Picture
   ' Save it:
   SaveJPG c, sFileName


Saving and Loading From Memory Buffers
To Load a JPG from a memory buffer, first you create a byte array containing all the bytes in the JPG, or use the Shell's IMalloc implementation to allocate memory directly and have that contain the JPG bytes. Then you pass the pointer to the data and the size of the buffer into the LoadJPGFromPtr method. If you use a byte array, the pointer is obtained using VB's VarPtr function:

   ' Assuming the byte array is b():
   lPtr = VarPtr(b(0))


To save a JPG to a memory buffer directly, you need to create a byte array or allocate sufficient memory to hold the resultant JPG. Normally this means creating a larger buffer than the resulting JPG. Since JPG always compresses an image, the size is always going to be smaller than the size of an equivalent DIB buffer to hold the bytes. In the sample code I chose 1/4 of the bytes in the DIBSection (m_cDib.Height * m_cDib.BytesPerScanLine / 4). Then you pass a pointer to this buffer and its size to the SaveJPGFromPtr. The method modifies the size variable passed in to reflect the actual size of the JPG created which you can then use to trim down the buffer to size.







In pratica?

Quali sono i passi fondamentali per salvare un'immagine in formato JPG?

Una volta scaricato il progetto SaveJPEG (da cui occorre estrarre la ijl11.dll, mIJL.bas e cDIBSection.cls):
  1. importare il modulo mIntelJPEGLibrary (da mIJL.bas)

  2. importare la classe cDIBSection (da cDIBSection.cls)

  3. dichiarare un oggetto di tipo cDIBSection:
    Dim c As New cDibSection
  4. convertire l'oggetto picture in DIB c.CreateFromPicture picThis.Picture
  5. salvare la DIB SaveJPG c, sFileName

...Sembra facile, ma non lo è... (ho impiegato alcune ore per riuscire a salvare perfettamente una JPG):
prima di tutto, cosa si intende per oggetto picture?

La mia completa ignoranza di VB mi ha fatto inizialmente pensare che si potesse semplicemente scrivere c.CreateFromPicture form1.Picture cioè creare un oggetto DIB a partire dalla proprità Picture della form che ho usato (forse impropriamente) come "piano di lavoro" per il disegno;
ma in questo modo si ottiene soltanto un'immagine completamente nera..
le form hanno però, oltre alla Picture, una proprietà Image: ho provato quindi c.CreateFromPicture form1.Image con identico risultato!

Eppure l'immagine è sulla form (e si vede).. ho provato quindi a "copiare" form1.picture in un oggetto Image: Image1.Picture = Form1.Image
c.CreateFromPicture Image1.Picture
In questo modo sono finalmente riuscito a salvare la JPG, ma la dimensione dell'immagine era sempre 800*600 pixel (uguale alla definizione impostata per lo schermo);
per ottenere un'immagine della dimesione corretta ho dovuto modificare la classe cDIBSection e aggiungere alla CreateFromPicture i parametri per altezza e larghezza: Public Function CreateFromPicture( ByRef picThis As StdPicture, xWidth As Long, xHeight As Long )
quelli che dovevano teoricamente essere passati come proprietà dell'oggetto picture risultavano sempre a zero (e successivamente impostati al default, 800*600 )






Conclusione

Credo che, pur nella sua limitatezza, questo progetto sia già un buon esempio della tecnica di programmazione "per compromessi" tipica di Windows, che offre un rapido ambiente di sviluppo fin quando si tratta di applicazioni semplici e comuni, ma che per applicazioni appena diverse ti lascia (nel caso migliore) in balia di librerie undocumented;
l'articolo trovato su vbaccelerator (come del resto se ne possono trovare su moltissimi altri siti) mostra comunque la tendenza in atto verso l'opensource, anche su piattaforme come Windows, strettamente legate al mondo commerciale.

Un evidente limite del programma consiste nel numero di ingrandimenti possibile (nell'ordine della decina), ed è dovuto alla precisione (grande ma limitata) del tipo numerico Double; l'alternativa più ovvia è l'utilizzo della libreria bcmath di Philip A. Nelson, che consente una precisione limitata solo dalle risorse di sistema;

Altri successivi sviluppi potrebbero essere (quando avrò tempo...):







Glossario

DIB = device-independent bitmap
HSV = hue, saturation, value
RGB = red, green, blue
HLS = hue, lightness, saturation

HLS è un sinonimo meno comune di HSV;

JPEG = Joint Photographic Expert Group, vedi lo standard ISO JPEG, l'Independent JPEG Group
GIF = Graphics Interchange Format, vedi anche burnallgifs
PNG = Portable Network Graphics, vedi l'home page

note

  1. In realtà il cosiddetto insieme "di Mandelbrot" è stato scoperto (e visualizzato graficamente) dal monaco Benedettino Udo di Aachen nel tredicesimo secolo!