Izdelava interaktivnega grafičnega urejevalnika (Grid Edit - GE)

Matjaž Šubelj, abs.-UNI, KGS


ABSTRACT
There is a geometrical database (GDB) which defines computational volume. An inteactive graphical editor with graphical user interface is needed for visualizing and interactive editing of geometrical database. Development of such editor is described.

Keywords: graphical editor, visualization, computer programming, VTK, Qt

Kazalo

  1. Definicija naloge
  2. Zasnova rešitve
  3. Rešitev
    1. Objektno orientirano programiranje in C++
    2. Grafična knjižnica VTK
    3. GUI knjižnica Qt
    4. Kako vse to spraviti skupaj
    5. Uporaba programa
  4. Zaključek
  5. Literatura

1.  Definicija naloge

Pri računalniških simulacijah toka (CFD) je za pripravo podatkov največ časa namenjenega pripravi kvalitetne diskretizacije oziroma računske mreže. Najbolj zaželene in uporabljane so t.i. blok strukturirane mreže elementov heksaedrov. Priprava geometrije lahko poteka s komercialnimi CAD programi, medtem ko mreženje geometrije obvladuje le malo namenskih programov, ki so dodatno prirejeni za določeno geometrijsko topologijo, npr. oblopatična območja turbostrojev, sesalne cevi, mešalne posode z tipizirano obliko mešala, območja okoli tipiziranih ladijskih trupov itd. Ideja je, da se za poljubno geometrijo, ki je topološko konstantna, izdela šablona razdelitve celotnega računskega območja v podobmočja, ki so topološko kuboidi. Robovi in robne točke teh kuboidov pa morajo biti tako postavljeni, da je kvaliteta generirane mreže kar najboljša (minimalni koti morajo načeloma biti večji od 20 stopinj). Koraki za izdelavo mreže so torej naslednji: Naloga je izdelava omenjenega interaktivnega grafičnega urejevalnika, pri čemer se naj upošteva naslednje:
  1. Načeloma naj bo izbrano grafično okolje X-Windows, uporabljene pa naj bodo grafične knjižnice, ki niso pod (restriktivno) licenco. Vhodni enoti sta standardni miška in tipkovnica.
  2. Interaktivni grafični urejevalnik operira z že obstoječo bazo GDB.
Kratek opis lastnosti baze:

Naj torej že obstaja geometrijska baza podatkov (GDB), ki definira računski prostor (Computational Domain=CD). CD je razdeljen na množico podprostorov, ki so po obliki kuboidi. Geometrija je definirana z vogali (V), robovi (E), površinami (S), volumni (VOL), njihovimi atributi ter medsebojnimi povezavami. Robovi so po matematičnem zapisu tipa B-spline, medtem ko so površine ali tipa B2-spline ali tipa bilinearna interpolacija BILIN(E1,E2,E3,E4). Vsak rob E je poleg kontrolnih točk E.BSPLINE.CP[i] dodatno določen s točkami E.POE[i], skozi katere načeloma poteka Bspline. Nadalje obstaja nabor geometrijskih pogojev (CONSTRAINTS= CON). Za vsak {V,P,E,S} je lahko predpisanih več CON. Npr.: Za Vi je predpisan CONi,j={Vi leži na površini Sk } ter pogoj CONi,j+1 ={Vi je periodičen z Vm}. GDB mora biti konsistentno sestavljena, da so njeni višji topološki elementi odvisni od nižjih. Ob spremembi nekega elementa GDB se mora baza konsistentno popraviti (vse odvisnosti izslediti in ustrezno popraviti). GDB se pri zagonu programa prebere iz datoteke, ki se predhodno formira s generatorjem šablone (Template Generator=TG) za izbran tip problema (šablona za oblopatične CD turbinskih strojev, za CD okoli trupa ladij, ...)

nazaj na vrh

2.  Zasnova rešitve

Grafična aplikacija uporablja grafično knjižnico, ki omogoča risanje v okno. Najbolj razširjena je brez dvoma OpenGL (Open Graphics Library). Ker je njena uporaba precej zahtevna, sem raje uporabil objektno knjižnico VTK (Visualization Toolkit), ki v svojem jedru uporablja ravno tako OpenGL. VTK je open-source, kar pomeni da je izvorna koda je prosto dosegljiva. Tako jo lahko vsakdo uporablja, spreminja in nadgrajuje. Izboljšave in popravki se običajno integrirajo v uradno verzijo knjižnice. Napisana je v jeziku C++, ima pa tudi podporo za interpreterske jezike Tcl, Java in Python. Ker uporablja OpenGL, je knjižnica prenosljiva povsod tja, kjer je na voljo OpenGL. Podpora MS Win in Unix/X11 platform je že vgrajena.
Spletna stran: http://www.kitware.com/vtk.html

Izdelava programa z grafičnim vmesnikom (graphical user interface - GUI) je pogojena z izbiro knjižnice gradnikov (widgets) kot so gumbi, drsniki, menuji ipd. Takih knjižnic obstaja kar nekaj, ločijo pa se po prenosljivosti (eno ali več platformne), možnosti uporabe različnih programskih jezikov, licenci in ne nazadnje po zahtevnosti uporabe. Naslednja tabela prikazuje neketere najbolj razširjene knjižnice (povzeto po Programming Languages mini-HOWTO).

KnjižnicaUporabaLicencaJezikPovezave z drugimi jezikiPlatforma
TkenostavnaFreeTclPerl, Python, drugiUnix, MS Win
GTK+zahtevnaFreeCPerl, C++, Python, drugi?Unix, MS Win v razvoju?
QtzahtevnaFree za Open SourceC++C, Perl, Python, drugi?Unix, MS Win
MotifzahtevnaNon-FreeC/C++Python, drugi?Unix

Tabela 1: Primerjava nekaterih najbolj razširjenih knjižnic grafičnih gradnikov (GUI knjižnice)

Obe knjižnici -grafična in GUI- morata biti dovolj fleksibilni, da se ju da integrirati v eno aplikacijo. Celotno interakcijo z uporabnikom običajno prevzame GUI knjižnica, zato je potreben še nek vezni člen med grafično in GUI knjižnico. Ta člen je lahko del ene ali druge knjižnice ali pa je kar integriran v program. Nadalje potrebuje grafična knjižnica okno v katerega izrisuje. Tako okno je skoraj vedno del grafične knjižnice in postavi se vprašanje kako ga integrirati v grafični vmesnik.

VTK kot grafična knjižnica ne nudi nobene GUI podpore, zato sem moral izbrati GUI knjižnico s katero bi jo lahko čim enostavneje povezal. Na prvi pogled najboljša rešitev je Tk knjižnica, vendar nam uporaba interpreterskega jezika izniči večji del fleksibilnosti VTK-ja, saj smo omejeni le na že s Tcl-jem povezane objekte. Poleg tega tuje izkušnje navajajo, da je kombinacija Tcl+Tk zelo primerna za enostavne aplikacije, medtem ko postane pri velikih projektih implementacija komaj obvladljiva. Od omenjenih GUI knjižnic ostaneta še GTK+ in Qt, ki obe že vsebujeti podporo za OpenGL (gradnik, ki ga lahko uporabimo kot platno za risanje). Ker je VTK open-source, sem na internetu hitro našel več različnih objektov, ki povežejo Qt in VTK, medtem ko za GTK+ iskanje ni bilo uspešno. GTK+ poleg tega tudi (še) nima podpore za MS Win platformo. Končna izbira je torej postala Qt knjižnica, katere edina slaba lastnost je precej draga licenca za razvoj komercialnih programov.
Spletna stran: http://www.trolltech.com

nazaj na vrh

3.  Rešitev

3.1.  Objektno orientirano programiranje in C++

Objektno orientirano programiranje (Object Oriented Programming - OOP) je drugačen pristop k pisanju programov. Osnovna ideja je taka: definirajmo objekt, ki bo vseboval vse potrebne podatke in funkcije, ki nad njimi operirajo. Tak objekt bo samozadosten in lahko predstavlja kakšno stvar iz realnega sveta. Objekte lahko med seboj združujemo v večje, bolj popolne objekte, posamezni objekti pa lahko med seboj komunicirajo s svojimi funkcijami. Da se programer med poplavo objektov ne izgubi, je uvedena hierarhija oziroma dedovanje. Bazni objekt definira najbolj splošne podatke in funkcije, nasledniki pa jih specializirajo ali jim dodajo svoje. Na ta način je razvoj novih objektov močno poenostavljen in skrajšan.

Osnova objektnega programiranja so objektno orientirani programski jeziki. Najvidnejši med njimi so Perl, Python, Java in C++. Prvi trije so interpreterski in namenjeni vsak svojemu področju, medtem ko je C++ klasični prevajalni jezik namenjen predvsem pisanju aplikacij.

Qt in VTK knjižnici sta obe napisani v jeziku C++, zato je bila uporaba jezika C++ za razvoj programa logična izbira. C++ se je razvil iz jezika C, ki so mu najprej dodali možnost uporabe razredov -C z razredi-, z nadaljnim razvojem pa se je standardiziral kot C++. Objekti v C++ so spremenljivke določenega tipa. Tipi se definirajo z razredi (class) v katerih je zapisano, katere spremenljivke - člane (members) vsebuje objekt in katere funkcije - metode (methods) ima na voljo. Člani so lahko tudi drugi objekti. Jezik C++ omogoča tudi več stopenj skrivanja članov in metod, ki so na ta način nedosegljivi objektom drugega tipa. Za kratek primer vzemimo točko v 2D prostoru. Definirajmo nov tip Point:

    // deklaracija razreda
    class Point {
    public:
       void  SetX( float xx ) { x = xx; CalcDistance(); };
       void  SetY( float yy ) { y = yy; CalcDistance(); };
       float GetX()           { return x; };
       float GetY()           { return y; };
       float GetD()           { return d; };
       void  Print();
    private:
       float x, y;  // koordinati točke
       float d;     // razdalja do izhodišča
       void  CalcDistance();
    };

    // definicija ostalih metod
    void  Point::CalcDistance()   { d = sqrt( x*x + y*y ); }
    void  Point::Print()          { cout << "x = " << x << endl << "y = " << y << endl; }
Deklaracijo razreda sestavlja rezervirana beseda class, ki ji sledi ime razreda. V zavitih oklepajih nato sledi telo razreda, kjer se nahajajo objekti (spremenljivke), metode (funkcije) in informacije o zaščiti. V programu dostopamo do članov razreda tako, da pred njim napišemo ime objekta in oboje ločimo s piko, npr.:
     Point p;           // deklariramo objekt "p" tipa "Point"

     p.x = 1.05;        // tu prevajalnik javi napako
     p.SetY( 2.3 );
     p.CalcDistance();  // tudi tu prevajalnik javi napako
     p.Print();
Razlog, da prevajalnik v zgornjih dveh vrsticah javi napako, je v zaščiti. Člana x in CalcDistance() stojita pod kvantifikatorjem private, kar pomeni, da lahko do njiju dostopajo samo člani istega razreda in navzven nista vidna. Obseg kvantifikatorjev je do naslednjega kvantifikatorja oziroma do konca telesa razreda. Kvantifikator public pomeni, da so člani dosegljivi tudi zunaj razreda, poleg njega pa obstaja še protected, ki pomeni da so člani dosegljivi v lastnem in v izpeljanih razredih. Zakaj je zaščita pomembna, lahko vidimo iz zgornjega primera: vsakič, ko spremenimo položaj točke se spremeni tudi njena oddaljenost od središča (vsaj načeloma). Če bi lahko zapisali p.x = 1.05 in nato pozabili klicati še p.CalcDistance(), bi razdalja ostala nepremenjena t.j. napačna.

Poglejmo si še dedovanje. Točko lahko prestavimo tudi v prostor, za kar moramo razredu Point dodati še tretjo koordinato. To najlaže storimo tako, da izpeljemo nov razred, ki mu v glavi razreda za osnovo - bazo podamo razred Point:

    class Point3D : public Point {
    public:
       void  SetZ( float zz ) { z = zz; CalcDistance(); };
       float GetZ();          { return z; }
       void  Print();
    private:
       float z;     // tretja koordinata točke
       void  CalcDistance();
    };

    void  Point3D::CalcDistance()   { d = sqrt( x*x + y*y + z*z ); }
    void  Point3D::Print() {
        Point::Print();    // kličemo metodo baznega razreda
                           // ker ima metoda isto ime, pišemo "razred::metoda()"
        cout << "z = " << z << endl;
    }
Če nato v programu napišemo:
     Point3D pt;

     pt.SetX( 1 );
     pt.SetY( 1 );
     pt.SetZ( 1 );
     pt.Print();
     cout << "d = " << pt.GetD() << endl;
dobimo sledeč izpis:
    x = 1
    y = 1
    z = 1
    d = 1.73205
Kot vidimo je izpeljani razred podedoval vse člane baznega razreda in zato lahko celo nastopa namesto njega. Dedovanje je izredno pomembno za pisanje knjižnic kot sta Qt in VTK, saj nam ni potrebno pisati delov iste kode. Z dedovanjem lahko vzpostavimo komunikacijo med objekti, ki dedujejo od znanega baznega razreda. C++ omogoča tudi deklaracijo abstraktnih razredov, katerih objekti ne morejo obstajati. Abstraktni razred samo določa zunanjo podobo, ki je skupna vsem njegovim dedičem, katerih objekti lahko obstajajo.

To je le delček sposobnosti jezika C++, ki pa že pokaže njegovo uporabnost. Omenim naj še možnost preobložitve operatorjev, ki močno prispevajo k preglednosti kode. Recimo da smo definirali razred complex, ki ponazarja kompleksna števila. Če poleg razreda definiramo še operator seštevanja '+' za kompleksna števila, lahko v programu pišemo:

    complex a( 10,0 );  // a.re = 10, a.im = 0
    complex b( 1,2 );
    complex c;

    c = a + b;          // c.re = 11, c.im = 2
Še nekaj kar nisem omenil: zapis complex a( 10,0 ); pomeni, da smo deklarirali objekt a razreda complex in hkrati inicializirali dva njegova člana. Zapis namreč predstavlja klic konstruktorja - metode, ki nosi isto ime kot razred in je kot vse ostale metode deklarirana v telesu razreda. Definicija konstruktorja pa določa katere člane inicializira in na kakšen način.

nazaj na vrh

3.2.  Grafična knjižnica VTK

Kot že rečeno je VTK objektna knjižnica zgrajena okrog knjižnice OpenGL. Namenjena je hitremu razvoju zahtevnih grafičnih aplikacij za vizualizacijo in obdelavo slik. Kljub svoji obsežnosti in kompleksnosti je njena uporaba precej enostavna potem ko se seznanimo z njeno zasnovo in načinom delovanja.

Gledano poenostavljeno, imamo na voljo nekaj osnovnih tipov gradnikov, ki jih med seboj povežemo v grafični cevovod. Ti osnovni tipi so Source, Filter, Mapper in Actor. Med seboj si podajajo podatkovne objekte in jih povežemo na način Filter->SetInput( Source->GetOutput() ). Grafični cevovd se prične s Source objekti. Ti priskrbijo podatke, ki jih vizualiziramo, tako da jih generirajo ali pa jih preberejo iz datoteke. Filter objekti podatke prečistijo, poenostavijo, jim kaj dodajo ali odvzamejo in nam na ta način omogočijo kontrolo nad podatki. Filter objekte lahko tudi kar izpustimo iz grafičnega cevovoda. Mapper objekti (med drugim) predstavljajo geometrijo, ki ponazarja podatke. Pri Actor objektih se cevovod zaključi. Actor objekti združujejo informacije o transformacijah in videzu predmeta, ki ga prikazujejo.

Grafični cevovod
Slika 1: Grafični cevovod knjižnice VTK in njegovo izvajanje

Običajno vzpostavimo grafični cevovod za vsak predmet, ki ga prikazujemo. Cevovodi si lahko delijo objekte, lahko jih združujemo ali vejimo. Način izvajanja cevovoda je naslednji: vsak objekt, skozi katerega potujejo podatki, ima tudi notranji zapis zadnjega časa izvajanja in čas zadnje spremembe objekta. Preden se podatki prikažejo na zaslonu, vsak objekt preveri ali se je kaj spremenil od zadnjega časa izvajanja. Če se je, potem podatke še enkrat procesira. Npr. nekemu filtru smo vključili drugačen način obdelave podatkov. Naslednjič, preden se bodo podatki začeli prikazovati na zaslon, se bo filter ponovno izvedel, prav tako pa tudi vsi objekti do konca cevovoda, ker bodo vsi dobili nove podatke. V tem primeru se edino Source objektu ne bo potrebno ponovno izvajati, ker se je sprememba zgodila v cevovodu za njim.

Oglejmo si kratek primer uporabe knjižnice:

    int main() {
        // ustvarimo risarja, risarsko okno in interaktorja za uporabnika
        vtkRenderer *ren = vtkRenderer::New();
        vtkRenderWindow *renWindow = vtkRenderWindow::New();
          renWindow->AddRenderer( ren );
        vtkRenderWindowInteractor *iren = vtkRenderWindowInteractor::New();
          iren->SetRenderWindow( renWindow );

        // ustvarimo igralca z geometrijo osemstrane piramide
        vtkConeSource *cone = vtkConeSource::New();
          cone->SetResolution( 8 );
        vtkPolyDataMapper *coneMapper = vtkPolyDataMapper::New();
          coneMapper->SetInput( cone->GetOutput() );
        vtkActor *coneActor = vtkActor::New();
          coneActor->SetMapper( coneMapper );
          coneActor->GetProperty()->SetColor( 0,1,1 ); // zeleno modra barva piramide

        // risarju povemo katerega igralca naj riše
        ren->AddActor( coneActor );
        ren->SetBackground( 1,1,1 ); // bela barva ozadja

        // uporabniku omogočimo interakcijo z igralcem
        iren->Start();

        // počistimo za seboj
        ren->Delete();
        renWindow->Delete();
        iren->Delete();
        cone->Delete();
        coneMapper->Delete();
        coneActor->Delete();
        return 0;
    }
Na sredini je lepo vidno kako zgradimo grafični cevovod. V njem ni nobenega Filter objekta, ker ga samo za prikaz piramide ne potrebujemo. Ko program prevedemo in poženemo, se nam odpre naslednje okno:

Piramida
Slika 2: Primer uporabe VTK knjižnice - osemstrana piramida

Z miško lahko piramido interaktivno rotiramo, prestavljamo in povečujemo ali pomanjšujemo. Kakšno interakcijo in na kakšen se bo le-ta izvajala, določa tip interaktorja, ki ga uporabimo v programu.

nazaj na vrh

3.3.  GUI knjižnica Qt

Qt je več platformna, objektno orientirana knjižnica napisana v C++. Omogoča hitro in enostavno razvijanje aplikacij z grafičnim vmesnikom, ki tako nudi videz (in delovanje) profesionalnega izdelka. Knjižnica je izdelek podjetja Troll Tech iz Norveške, ki jo distribuira pod dvema licencama: Qt Professional Edition je namenjena razvoju komercialnih programov za vse platforme in vključuje brazplačno nadgradnjo ter tehnično podporo, ter Qt Free Edition, ki je namenjena razvoju free oz. open-source programov in je omejena le na Unix/X11 platformo. Kljub precej dragi licenci za razvoj komercialnih programov, se je knjižnica zelo uveljavila, predvsem med razvijalci, ki potrebujejo prenosljivost in med razvijalci open-source programov. Qt je tudi osnova zelo popularnega namiznega okolja KDE na Linux sistemih (okenski okvir na zgornji sliki je del tega okolja).

Eden od razlogov za tako uspešnost knjižnice je gotovo signal/slot mehanizem, ki omogoča komunikacijo med najrazličnejšimi objekti. Na vsak slot lahko pripeljemo enega ali več signalov, ki so posledica uporabnikovih akcij. Če npr. kliknemo na nek gumb, potem bo gumb oddal signal, ki bo morebitnemu sprejemniku signala povedal, da je bil gumb pritisnjen. Sprejemnik lahko nato primerno odgovori na uporabnikov klik. Vsakemu objektu oz. razredu, ki ga na novo deklariramo, lahko definiramo poljubno mnogo signalov in/ali slotov.

Drugi razlog za uspešnost knjižnice pa je veliko število že narejenih objektov, ki jih moramo samo še uporabiti. Recimo, da želimo uporabnika vprašati za ime datoteke, potem ko klikne na gumb "Open". Vse kar moramo storiti je, da gumb povežemo z naslednjo funkcijo:

    void openFile()
    {
        QString filename;  // ime datoteke

        filename = QFileDialog::getOpenFileName( QString::null, "*.cpp" );

        if ( filename.isEmpty() )
            {
            // uporabnik je pritisnil "Cancel" ali Esc
            return;
            }

        open( filename ); // funkcija, ki datoteko prebere 
    }
Ko uporabnik klikne na gumb, se odpre naslednje okno:

QFileDialog
Slika 3: Primer uporabe Qt knjižnice - QFileDialog

Uporabnik lahko sedaj poišče željeno datoteko in če bo kliknil na gumb "Open", se bo datoteka tudi prebrala.

Naslednji razlog za priljubljenost je verjetno podpora internacionalizaciji. Prej omenjeno namizno okolje KDE je že prevedeno v več jezikov, med drugim tudi v slovenščino. Če bi to uporabili pri zgornjem primeru, bi npr. na gumbih namesto "Open" in "Cancel" pisalo "Odpri" in "Prekliči".

nazaj na vrh

3.4.  Kako vse to spraviti skupaj

Kot prvo sem moral združiti VTK in Qt v navidez povsem enotno aplikacijo. Na internetu sem našel kar nekaj primerov objektov, ki so bolj ali manj uspešno opravljali svojo nalogo. Odločil sem se rešitev Matthiasa Koeniga, ki je po mojem mnenju edina pravilna.

Če pogledamo dokumentacijo VTK knjižnice, vidimo da sta vtkRenderWindow in vtkRenderWindowInteractor edina objekta (razreda), ki neposredno komunicirata z okenskim okoljem (oz. njuna naslednika vtkXRenderWindow in vtkXRenderWindowInteractor za X11 standarden Xt-GUI). Prvi priskrbi risarsko okno, drugi pa sprejema uporabnikove akcije (premikanje miške, pritiski tipk na tipkovnici). Ker bomo mi uporabili Qt-GUI, potrebujemo torej nekaj v smislu vtkQtRenderWindow in vtkQtRenderWindowInteractor, ki bosta komunicirala s Qt objekti, ki bodo predstavljali ogrodje aplikacije. Prav to pa je napisal Matthias Koenig. Razred vtkQtRenderWindow nasledi QGLWidget, ki je Qt-jevo risarsko okno za OpenGL, in vtkRenderWindow, da ga VTK objekti lahko uporabljajo. vtkQtRenderWindowInteractor ima ravno tako dve bazi, to sta QObject, ki je bazni razred vseh Qt objektov, in vtkRenderWindowInteractor, spet da ga VTK objekti lahko uporabljajo. Ker v Qt sprejemajo uporabnikove akcije samo QWidget objekti (in njegovi nasledniki - kar je tudi QGLWidget, ni pa QObject), vtkQtRenderWindow samo posreduje te akcije vtkQtRenderWindowInteractor-ju. QWidget objekti so namreč vidni deli grafičnega vmesnika (zato tudi edini lahko sprejemajo uporabnikove akcije), kar pa vtkRenderWindowInteractor po svoji vlogi ni. Na ta način dobimo kar najmanjšo kodo, ki je pregledna in popolnoma združljiva z obema knjižnicama. Vse kar se spremeni je, da namesto vtkRenderWindow in vtkRenderWindowInteractor uporabimo njuni Qt različici in vključimo vtkQtRenderWindow med ostale vidne objekte uporabniškega vmesnika. Rešitve drugih avtorjev so bile precej manj pregledne (najmanj to) in so običajno kar združile funkcionalnost več VTK objektov v en skupen VTK-Qt objekt. Taka rešitev pa je s stališča vzdrževanje kode in nadgradnje knjižnic popolnoma nesprejemljiva. Sicer tudi Koenigova rešitev ni bila popolna. Manjkala je podpora tekstnim anotacijam, kar je standardna možnost VTK-ja. Z nekaterimi popravki mi je uspelo to pomankljivost odpraviti.

Pisanje aplikacij s Qt uporabniškim vmesnikom izgleda nekako takole: definiramo nov QWidget objekt (gradnik), ki bo nosilec vmesnika in bo aplikacijo s svojimi metodami pravzaprav poganjal; v njem definiramo ostale gradnike (menuje, gumbe itd.) in jih razporedimo; povežemo vse gradnike preko njihovih signalov in slotov z metodami našega gradnika oz. med seboj; definiramo še glavno funkcijo main() v kateri ustvarimo objekt QApplication, ki skrbi za glavno izvajalno zanko (main event loop), ustvarimo naš gradnik in ga podamo prvemu kot glavni gradnik aplikacije.

Glavnemu gradniku naše aplikacije sem dal ime GEgui. V njem sem definiral tri gradnike, ki sestavljajo uporabniški vmesnik:

ControlPanel je prav tako QWidget gradnik, ki pa ima samo nalogo združevanja gumbov in menuja v kompaktno obliko. VTK grafični cevovod je implementiran v glavnem gradniku GEgui - v konstruktorju ga inicializiramo, v destruktorju ga zbrišemo. V datoteki v kateri je definiran glavni gradnik, gegui.cpp, so napisane še pomožne funkcije, ki ne pripadajo nobenemu razredu, in jih uporabljajo nekateri objekti v grafičnem cevovodu. Te funkcije odgovarjajo na nekatere interakcije uporabnika (izbiranje geometrije, premikanje vozlišč) in so, recimo temu, zunanji del grafičnega cevovoda.

Naslednje VTK objekte sem moral prilagoditi našemu namenu oziroma jih napisati na novo:

Ostala dokumentacija:

nazaj na vrh

3.5.  Uporaba programa

Pri zagonu programa se avtomatsko naloži datoteka z geometrijsko bazo podatkov gdb.dat, če jo program najde v trenutnem direktoriju. Če datoteke ne najde, samo prikaže prazno okno. Datoteko lahko naložimo tudi preko menija File->Open.

Grid Edit
Slika 3: Izgled programa z naloženo datoteko (označen je rob e38)

Geometrijo lahko s pritiskom na tipko 'F1' in hkratnim premikanjem miške poljubno vrtimo, s tipkama 'F2' in 'F3' ter premikanjem miške, pa transliramo in zoomiramo. Tipka 'r' resetira pogled. Gumb v zgornjem desnem kotu prikaže geometrijo s prosojnimi površinami, tako da se lepo vidijo tudi notranje površine. Gumb zgoraj levo pa nam odpre dodatne izbire, s katerimi lahko označimo vse primitive na geometriji. Če z miško kliknemo na enega izmed primitivov (vozlišče, rob ali ploskev) se nam v spodnjem oknu izpišejo vse informacije ki so o njem znane, na geometriji pa se prikaže oznaka z imenom. Na zgornji sliki je tako označeno vozlišče. Vozlišča lahko premikamo po površini, ki je zapisana v GDB. Na vozlišče kliknemo dvakrat, da se nam prikaže ročaj za premikanje - poln rdeč kvadrat. Ročaj predstavlja vozlišče in ga lahko z miško povlečemo na drugo lokacijo. Pri tem se bo vozlišče premikalo po predpisani površini. Med premikanjem lahko uporabimo tipke F1-3, da spremenimo pogled. Vozlišče ostane označeno z ročajem vse dokler ne kliknemo drugam. Preden bomo zapustili program, nas bo le-ta opozoril na morebitne neshranjene spremembe na geometriji.

nazaj na vrh

4.  Zaključek

Veliko časa sem porabil za seznanjenje s knjižnico VTK. Kljub temu, da dobimo zraven ogromno primerov uporabe, sem le s težavo ugotovil na kakšen način moram oziroma lahko generiram vtkPolyData objekte, da dobim željeno sliko. Največji krivec temu je skopa dokumentacija, ki je na nekaterih mestih dvoumna. V veliko pomoč bi bil tudi seznam filtrov glede na njihovo namembnost. Nekajkrat se mi je namreč zgodilo, da sem "ponovno izumil kolo". V poplavi objektov hitro spregledaš takega, ki bi ustrezal tvojemu namenu. Še vedno pa nisem povsem seznanjen z izvajanjem filtrov oz. grafičnega cevovoda. Velikokrat je bila edina rešitev seciranje kakega podobnega objekta in nato kopiranje nekaterih delov kode. Kar nekaj časa sem tudi porabil, da sem implementiral izbiranje primitivov geometrije na do uporabnika prijazen način. Težava je bila v tem, da vtkPicker vrača vedno proti kameri najbližji primitiv, kar je pomenilo, da vozlišča praktično ni bilo mogoče izbrati, saj je bil kateri od robov vedno bližji.

S Qt knjižnico pravzaprav ni bilo težav. Je odlično dokumentirana, z veliko primeri in celo učbenikom. Enostavna je za uporabo, saj nam ni potrebno skrbeti niti za čiščenje objektov, potem ko jih več ne potrebujemo. Objekti se namreč sami uničijo, če opazijo, da jih nihče več ne potrebuje.

nazaj na vrh

5.  Literatura

[1]  M. Prtenjak:  C++ za velike in male,  Desk  1995
[2]  W. J. Schroeder, K. M. Martin, L. S. Avila, C. C. Law: The VTK User's Guide,  Kitware, Inc.  2000

nazaj na vrh