Terraform FAQ, version 0.2, May. 05, 2000, révisée par RNG
Traduction Raymond Ostertag
Ce document contient les conseils utiles pour quiconque est intéressé pour agrandir ou modifier Terraform, ou simplement corriger des bogues. Ce document se concentre sur les types de modifications que je pense que les gens souhaitent le plus réaliser. Ce que vous voulez faire n'est peut-être pas dans ce document, mais vous devriez quand même le lire pour avoir une idée de comment vous pourrez procéder.
Le code source de Terraform est compatible Doxygen . Celà signifie que ses commentaires ont été étiquetté de tel façon que doxygen peut générer une documentation class/code. Pour faire celà, vous avez besoin de :
cd terraform-x.x.x/src doxygen -g doxygen.rcDoxygen génére une documentation Javadoc-style qui rend plus facile la consultation des hiérarchies de classes. Si vous voulez travailler avec Terraform et être capable de consulter le source, je recommende de faire celà comme première étape.
utilisez ceci: | au lieu de ceci: |
---|---|
PTYPE | float, the type used to store Height Field elevation |
UI | unsigned int |
UL | unsigned long |
UF | unsigned float |
UD | unsigned double |
UC | unsigned char |
BYTE | unsigned char |
F | float |
D | Double |
Notez que Terraform est en cours de développement, et n'adhère
pas toujours lui-même à cette ligne de conduite. De futurs
nettoyages du code porteront sur le sujet.
Prefix: | Variable type/class: |
---|---|
p_ | a pointer |
d_ | a non-pointer data member |
b_ | a boolean data member |
s_ | a static data member |
La classe HeightField est dérivée de la classe HeightFieldCore , qui est elle-même dérivée de la classe de référence Matrix2D. La classe HeightField est définie dans le produit fini qui apporte tous les services relatifs au HeightField dont l'application a besoin. La table suivante liste les fonctions membres publiques de HeightField qui ont un intérêt particulier.
Return Type | Name | Purpose |
---|---|---|
PTYPE * | getData() | Returns a pointer to the heightfield data |
PTYPE | getMin() | Returns the minimum elevation level in the heightfield data |
PTYPE | getMax() | Returns the maximum elevation level in the heightfield data |
PTYPE | getSealevel() | Returns the current sealevel setting |
int | getWidth() | Returns the width of the heightfield |
int | getHeight() | Returns the height of the heightfield |
int | getSize() | Returns the total number of elements in the heightfield (width x height) |
PTYPE | getEl(n) | Returns the heightfield elevation at offset (n), which will be in the range of 0 to 1. |
PTYPE | getEl(x, y) | Returns the heightfield elevation at location (x,y), which will be in the range of 0 to 1. |
PTYPE | getElSea(x, y) | Returns the greater of the elevation at (x,y), and the current sealevel setting. |
Return Type | Name | Purpose |
---|---|---|
void | setEl(n) | Sets the heightfield elevation at offset (n). |
void | setEl(x, y) | Sets the heightfield elevation at location (x,y). |
void | setData(PTYPE *hf, int xsize, int ysize, FALSE) | Replaces the existing heightfield with new data. |
La liste des méthodes associées au HeightField donnée
ici n'est pas complète. HeightField.{h,cc} contient d'autres fonctions
qui peuvent être utiles lors de la manipulation de données
de Height Field . Pour plus d'informations, voyez HeightField.{h,cc} and
HeightFieldCore.{h,cc}.
Return Type | Name | Purpose | |
---|---|---|---|
GuiColormap * | getColormap() | Returns the current color map. | |
int | setColormap() | Sets the colormap. | |
GdkColor | getColor2d(x, y) | Returns the color that heightfield location (x,y) will be draw with in the current colormap. | |
GdkColor | getColorElv(Elv) | Returns the color that corresponds to elevation Elv in the current colormap. | |
int | draw() | Draws the heightfield in whatever mode the user has selected. | |
int | getMode() | Returns the current drawing mode. | |
int | setMode(char *m) | Sets the current drawing mode to the mode named in *m | . |
void | setFastWire(bool f) | Turns fast wireframe mode on or off. | |
void | setEyePos(D x, D y, D z) | Sets the view position for 3d drawing modes. | |
void | setLightPos(D x, D y, D z) | Sets the position of the "sun" for 3-d viewing modes. |
Pour plus d'informations, voyez HeightFieldDraw.{h,cc}.
HeightFieldReader est principalement un emplacement où se trouvent les différents fichiers des fonctions de lecture, et où se trouve quelques fonctions d'aide additionnelle qui sont sont utiles pour l'implémentation du support d'un nouveau format de fichier. Les fonctions d'aide utiles sont :
void | checkRead(points_read, npoints) | Check that the number of data points read from the input file matches the number of points in the heightfield. If the file did not contain enough points, set the unassigned points to zero elevation. |
void | readOK() | Copies the input file's name to the heightfield's d_name member, and collects some statistics about the new heightfield. |
De façon similaire, HeightFieldWriter existe principalement pour
les différents fichiers des fonctions d'écriture, et pour
contenir quelques variables membres utiles. A l'inverse de HeightFieldReader,
cette classe n'a pas de fonctions membres d'aide. Vous devriez, toutefois,
faire usage des membres de classe d_Fio pour ouvrir un fichier enregistré,
pour accéder aux informations du nom de fichier, si nécessaire,
et pour fermer le fichier enregistré.
De façon similaire, la méthode HeightFieldExport::exportContourLinesToPS crée un fichier Postscript réprésentant les lignes de contours générées par la commande HF > Contour Line Map ( carte d'isobares ). Toutefois, cette fonction n'écrit seulement que les points sélectionnés du terrain dans le fichier Postscript, et ainsi le résultat n'est pas une représentation exacte du terrain. Un tel fichier Postscript ne devrait pas, en théorie, être utilisé comme un fichier d'entrée par Terraform car il ne contient pas une élévation pour chaque point du terrain.
Si vous souhaitez ajouter une fonction à Terraform qui ne rentre
pas dans cette catégorie "processed data output", vous devrez travailler
principalement dans HeightFieldExport.{h,cc}.
File(s) | Purpose: |
---|---|
FileIO.* | High level file I/O operations. |
GlobalDefs.h | Global definitions, data type defs, etc. |
GlobalSanityCheck.* | Global bailout and warning functions. |
GlobalTrace.* | A Global trace flag mechanism for debug output |
Gui* | Gtk-- support routines, used by other dialog box code. |
HeightField*.* | Core HeightField functionality |
Math*.* | Support for various math functions. |
Matrix2D.h | The base template class used to store, access and alter the HeightField data. |
MenuDefs.* | Constants used by Terraform's menus. |
PrintOptions.* | Constants used to support printing. |
RenderOptions.* | Various options/settings used when rendering. |
TFBaseDialog.*,
TFDialog*.* |
All of Terraform's dialog boxes. |
TFCListMainWin.* | Terraform's main window. |
TFFileRC.* | Implements .rc file support. |
TFOpenGLArea.* | Terraform's (alpha) OpenGL support. |
TFOptions*.* | Run-time and .rc file options support. |
TFPreviewDialog.* | Dialog boxes that show a heightfield preview. |
TFWindow*.* | Code that defines various window types. |
Timer.* | Support for timing and profiling operations. |
agename.* | Support for file versioning. |
flexarray.* | A template-based array type that can grow automatically to accomodate new elements. |
glib*.* | Simple wrappers for some glib classes. |
strrep.* | Memory safe string replace within another string. |
Beaucoup d'autres types de changement, par exemple ajouter une nouvelle opération de terrain comme Cratère ou Erosion, peuvent aussi être implémentées dans la structure du code source Terraform existant. Si votre changement tombe dans la catégorie largement définie comme "quelque chose de pur terrain", alors il est probable que vous travaillerez presque exclusivement avec HeightFieldOps.cc and HeightFieldOps.h.
Si vous êtes allé aussi loin et que vous êtes toujours convaincu que vous avez besoin de créer quelques nouveaux fichiers de code source, pour des raisons de partage du code, en gardant les fonctionnalités isolées dans des modules bien définis, alors c'est OK. Dans ce cas, vous devriez probablement contacter RNG avant de procéder, pour vous assurer qu'il n'a pas déjà pris les noms de fichiers que vous proposez d'utiliser, et pour vous assurez que vos raisons pour ajouter des nouveaux fichiers sources sont sérieuses. Gardez en mémoire que RNG est plus enclin à accepter des changements qui n'impliquent pas des changement radicaux du code de base. Le type de modification qui le plus communément implique d'ajouter des nouveaux fichiers source est celle qui implique de créer des boîtes de dialogue pour interagir avec l'utilisateur. C'est une conséquence des méthodes générales de l'écriture du code qui utilise les outils gdk/GTK. Bien sûr, des nouvelles fonctionnalités impliquent presque toujours un quelconque type d'interaction avec l'utilisateur, ainsi si vous ajoutez des nouvelles fonctionnalités vous pouvez presque parier d'avoir à créer une ou deux boîtes de dialogue et d'avoir quelques fichiers source pour faire celà.
Terraform utilise le système Autoconf pour générer
ses Makefiles, ainsi ajouter de nouveaux fichiers source est facile. Mettez
les simplement dans le répertoire Terraform src/, ajoutez le nom
du ficher à src/Makefile.am et alors relancez le script configure
dans votre répertoire Terraform de façon à générer
de nouveaux Makefiles qui vont reconnaitre vos nouveaux fichiers sources.
Supporter un nouveau format d'importation implique d'écrire une fonction qui analyse ce format, ajouter une détection logique du type du format des fichiers, et ajouter un apple vers votre fonction d'analyse de votre fichier.
Ajoutez votre fonction d'analyse à la classe HeightFieldReader . Donnez lui un nom de la forme "readXXX()", où XXX représente le nouveau format de fichier. Des exemples sont readGIF, readPGM, etc... Vous pouvez aussi implémenter des fonctions d'aide particulières dans la classe HeightFieldReader , si nécessaire, pour assister votre fonction readXXX. Votre fonction aura besoin d'assigner au tableau de PTYPE assez large pour contenir les données du fichier ( par exemple myarray = new PTYPE[width * height];), lire les données du fichier et les convertir en PTYPEs dans l'intervalle 0.0 à 1.0, stocker ces données dans votre tableau, et finallement, votre fonction d'analyse devrait appeler HeightFieldReader::checkRead quand c'est fini de façon à faire un peu de post-processing important sur le nouveau terrain.
Quand vous avez écrit votre fonction d'analyse, vous devez l'inclure
dans la classe HeightFieldIO. Modifiez HeightFieldIO.h pour définir
une nouvelle constante qui représente votre type de fichier, si
un type convenable n'est pas déjà défini. Dans ce
fichier vous trouverez les définitions pour les autres types de
fichier que Terraform supporte déjà. Dans HeightFieldIO.cc,
vous devrez faire deux choses. Premièrement, modifiez la fonction
HeightFieldIO::getFileType() pour associer l'extension du fichier correcte
avec votre type de fichier. Deuxièmement, modifiez la fonction HeightFieldIO::read()
pour ajouter un appel à votre fonction readXXX.
Supporter un nouveau format d'exportation requiert d'écrire une fonction qui convertit les données du terrain en quelque autre format structuré approprié, aussi bien qu'écrire manuellement les informations nécessaires d'entête et de bas de page de fichier, ajouter une détection logique de votre nouveau type de fichier basée sur le nom de fichier que l'utilisateur à choisi pour enregistrer le terrain, et ajouter un appel vers votre fonction d'écriture.
Ajoutez votre fonction d'écriture à la classe HeightFieldWriter.
Donnez lui un nom de la forme "writeXXX()", où XXX
répresente le nouveau format de fichier.
the new file format. Des exemples sont writeTGA, writePGM, etc... Vous
pouvez aussi implémenter des fonctions d'aide particulières
dans la classe HeightFieldWriter , si nécessaire, pour assister
votre fonction writeXXX. Votre fonction devra boucler sur
toutes les données du terrain ( rappel que vous pouvez utiliser
la fonction HeightField::El() pour accéder à ces données),
convertir dans le format approprié à vote type de fichier,
et l'écrire dans un fichier manipulé fourni par la classe
HeightFieldWriter. Les données de terrain que vous travaillerez
seront stockées comme PTYPEs dans l'intervalle 0.0 à 1.0.
Finalement, votre fonction d'écriture devra fermer le fichier manipulé
et appeler HeightField::setSaved(TRUE) quand terminé.
Quand vous avez écrit votre fonction d'écriture, vous
devez l'inclure dans la classe HeightFieldIO. Modifiez HeightFieldIO.h
pour définir une nouvelle constante qui représente votre
type de fichier, si un type convenable n'est pas déjà défini.
Dans ce fichier vous trouverez les définitions pour les autres types
de fichier que Terraform supporte déjà. Dans HeightFieldIO.cc,
vous devrez faire deux choses. Premièrement, modifiez la fonction
HeightFieldIO::getFileType() pour associer l'extension du fichier correcte
avec votre type de fichier. Deuxièmement, modifiez la fonction HeightFieldIO::write()
pour ajouter un appel à votre fonction writeXXX.
int HeightFieldOps::myFunc(args...) { // declare any variables you need PTYPE newElevation; // loop over the terrain and modify it: for(int x=0; x < p_HF->getWidth(); x++) for(int y=0; y < p_HF->getWidth(); y++) { newElevation = ... ; // do something cool... p_HF->setEl(x,y, newElevation); } // any broad-scale changes to the heightfield also need to do the following: p_HF->gatherStatistics(); p_HF->setSaved(FALSE); // return a status code. return(0); }Bien sûr, en fonction de l'algorithme que vous utilisez pour modifier le terrain, votre fonction interne peut ne pas contenir une structure en boucle interne comme celle ci-dessus. Utilisez n'importe quel code dont vous avez besoin pour faire les changements que vous souhaitez au terrain. Quand ce sera fini, appellez p_HF->gatherStatistics(); celà va remettre le terrain entre les valeurs 0..1 et faire quelques vérifications limitées.
Si votre fonction est suffisamment complexe, vous aurez peut-être besoin de créer une boîte de dialogue, tel que décrit ailleurs dans ce document, pour recueillir des paramètres de l'utilisateur avant d'appeler votre fonction. Finalement, vous devez patcher votre fonction ou boîte de dialogue dans le menu contextuel de Terraform, celui qui est montré quand vous cliquez sur une fenêtre d'affichage du terrain. Vous devrez travaillee avec MenuDefs.h, TFWindow.cc, et TFWindowHandler.cc. Si vous ne voulez pas avoir à faire avec le code du menu et du dialogue, vous pouvez écrire à RNG qui jusqu'à présent à montré de la bonne volonté à recevoir du nouveau et son support avec une boîte de dialogue appropriée.
Si vous sentez que vous allez ajouter un dialogue, faites ce qui suit : dans MenuDefs.h, créez une nouvelle constante de la forme MENU_HF_MyID, qui liste la position du menu de votre fonction ou de votre boîte de dialogue. Par exemple, la fonction HF-> rotate est définie comme :
#define MENU_HF_ROTATE "Rotate"Dans la fonction TFWindow::addMenus, ajoutez votre fonction avec les autres fonctions MENU_HF_*, avec un peu de code comme ceci :
s=_(MENU_HF_ROTATE); MenuElem hRotate (s, ACC_C, SigC::bind(SigC::slot(this, &TFWindow::hfMenuCallback), string (s))); mlHF.push_back (hRotate);Aussi, de façon à supporter correctement l'internztionalisation, vous devez ajoutre vos nouveaux textes de menus à la définition de char *foobar au début de TFWindow.cc. Bien que le code de #ifdef ne soit jamais utilisé, GNU gettext s'attend à trouver ce genre de structure de façon à générer proprement les fichiers language/ressource.
Finalement connectez vos articles de menu à la classe TFWindowHandler. Ajoutez une variable membre particulière à la fonction appropriée type pointeur ( par exemple TFPreviewDialog *, TFBaseDialog *, ou juste int * si votre fonction n'utilise pas de boîte de dialogue) pour représenter votre boîte de dialogue ou fonction. Ensuite, ajoutez un if-statement à la fonction TFWindowHandler::hfMenuCallback , avec les autres if- statements lesquels sont sélectionnés parmi les articles de HF menu, comme ceci:
if (!strcmp (menuitem, MENU_HF_MyID)) { if (!p_myDialog) p_myDialog= new TFDialogMyDialog (p_HF, p_HFD); p_myDialog->show (); } else
Ensuite, la classe GlobalSanityCheck fournit des facilités pour que certaines conditions restent vraies et envoie ou imprime un signal si ce n'est pas le cas. GlobalSanityCheck devrait être utilisée pour vérifier la validité des hypothèses que votre code fait avant de tourner. En fait, tandis que vous naviguez à travers le code source de Terraform, vous trouverez des appels à GlobalSanityCheck::warning et GlobalSanityCheck::bailout dans les premières lignes de beaucoup de fonctions.
Finallement, comme une dernière ligne de défense, Terraform implémente les manipulations des exceptions, pour intercepter les différentes erreurs qui vont planter le programme et donner à l'utilisateur les choix sur quoi faire ensuite. Vous ne devriez pas à faire quoi que ce soit de spécial pour pouvoir utiliser cette fonctionnalité, bien que si votre code provoque beaucoup de 'core dumps', vous pourriez bien devenir familier avec le support des manipulations d'exceptions de Terraform. Si vous voulez utiliser cette manipulation d'exceptions incluse, vous devez lancer ./configure --enable-debug pour valider le code de manipulation d'exceptions.
Si vous compilez votre propre exécutable en tout cas, vous pourriez
juste vouloir recompiler avec l'argument -g et ensuite utiliser gdb. Je
préfère celà plutôt que d'utiliser le support
de déboguage.
~/terraform-0.4.6/src/> foreach name (*.orig) foreach? set oldname=`basename $name .orig` foreach? diff $oldname $name > $oldname.diff foreach? end ~/terraform-0.4.6/src/> cd .. ~/terraform-0.4.6/> tar -czf myusername.0.4.6.tgz src/*.diff
~/terraform-0.4.6/> uuencode myusername.0.4.6.tgz myusername.0.4.6.tgz > myusername.0.4.6.uu
~/terraform-0.4.6/> mail -s 'my spiffy terraform mods against v0.x.x' < myusername.0.4.6.tgz