FAQ: Hacking Terraform

# FAQ: Modifier Terraform

Terraform FAQ, version 0.1, Dec. 02, 1999 par cloister@hhhh.org

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.

- 1. Classes majeures and types de données

- 1.1 Types de données simples
- 1.2 Conventions de nommage
- 1.3 La classe HeightField
- 1.4 La classe HeightFieldDraw
- 1.5 Les classes HeightFieldReader / HeightFieldWriter
- 1.6 La classe HeightFieldExport

- 2. Un tour rapide du code source de Terraform
- 3. Ajouter de nouveaux fichiers sources
- 4. Ajouter un nouveau type d'import (par exemple étendre Fichier->Ouvrir)
- 5. Ajouter un nouveau type d'export (par exemple étendre Fichier->Enregistrer sous)
- 6. Modifier le terrain
- 7. Ajouter un nouveau générateur de terrain
- 8. Support de déboguage
- 9. Soumettre vos changements

# 1: Classes majeures et types de données

Cette section décrit les classes centrales C++ et les types de données sur lesquelles repose Terraform. Pour faire du travail sérieux avec Terraform, vous aurez besoin d'être familier avec ces types de façon à faire que votre code compile facilement, interagisse correctement avec le reste de Terraform, et maintenir le niveau de portabilité de Terraform.

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.rc

Doxygen 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.

## 1.1: Types de données simples

Comme beaucoup de projets de logiciels vastes qui essayent de ne pas dépendre de définitions spécifiques d'une machine - ou d'un compilateur - pour les types de bases, Terraform définit quelques synonymes pour les types basiques pour éviter l'utilisation potentielle de mots clés non portables. Heureusement, il y en a quelques uns pour vous pour garder le traçage. Faites un effort pour utiliser ces synonymes dans votre code, ainsi votre code restera portable. Ces synonymes sont définis dans GlobalDefs.h et HeightFieldCore.h :

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.

## 1.2: Conventions de nommage

Typiquement, les membres d'un classe sont préfixés par un texte court qui identifie le type/classe de la variable comme ce qui suit :

Prefix: | Variable type/class:
--- | ---
p_ | a pointer
d_ | a non-pointer data member
b_ | a boolean data member
s_ | a static data member

## 1.3: La classe HeightField

La classe HeightField stocke les données d'élévation pour les terrains, ainsi que vous l'attendiez. Les données et fonctions membres définies dans la classe HeightField elle-même sont largement concernés avec l'I/O des fichiers, le nommage, le support de la fonction Annuler, etc ... Il y a peu dans le HeightField lui-même qui est intéressant du point de vue des opérations qui peuvent modifier ou vous informer sur les données du terrain lui-même.

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.

### Fonctions membres 'Accessor' :

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.

### Fonctions membres 'Mutator' :

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}.

## 1.4: La classe HeightFieldDraw

La classe HeightFieldDraw encapsule les opérations ayant à voir avec le tracé du terrain à l'écran. Si vous souhaitez ajouter un nouveau mode d'affichage, tel qu'une vue rendue avec POV-Ray, vous devez complétez HeightFieldDraw.{h,cc}. Quelques fonctions membres dans la classe HeightFieldOps reçoivent des pointeurs HeightFieldDraw comme arguments, car ces fonctions ont occasionnellement besoin d'utiliser des informations sur les réglages en cours du rendu, particulièrement les couleurs. La table suivante liste les fonctions membres publiques de HeightFieldDraw qui ont un intérêt particulier :

### Fonctions membres :

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}.

## 1.5: Les classes HeightFieldReader / HeightFieldWriter

Ces classes encapsulent les fonctionnalités associées à l'écriture et la lecture des terrains sous différents formats de fichiers. Les formats actuellement supportés sont Poskanzer Gray Map (.pgm), Targa (.tga), Matlab (.mat), Octave ASCII (.oct), United States Geological Survey DEM format (.dem), GTOPO (.gtop), Portable Network Graphics (.png), Tagged Image File Format (.tiff), and Windows Bitmap (.bmp). Si vous souhaitez un support pour un format additionnel ou améliorer le support pour l'un de ces formats, vous serez amené à améliorer les classes HeightFieldReader et/ou HeightFieldWriter. Notez que tous ces formats ne sont pas actuellement supportés pour à la fois la lecture et l'écriture.

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é.

## 1.6: La classe HeightFieldExport

La classe HeightFieldExport est là pour contenir des fonctions qui produisent quelques types de données "dérivée" en sortie. C'est difficile de donner une définition claire des "procédés" qui ne font pas parti des fonctionnalités délivrées par HeightFieldWriter, mais en général HeightFieldWriter devrait être utilisé pour créer_exactes_représentations du terrain qui peuvent, au moins en théorie, être relues par Terraform ou être utilisées dans des programmes du même type. HeightFieldExport, en comparaison, est plus implicitement une transformation à "sens-unique". Par exemple, la méthode HeightFieldExport::renderPOV crée des fichiers d'import POV-Ray basés sur les données du terrain et puis lance POV-Ray pour faire le rendu du terrain. Le fichier d'export POV-Ray est dérivé des données du terrain mais n'est pas utilisable comme format d'entrée par Terraform. De façon analogue, le format généré par POV-Ray n'est pas exploitable par Terraform, en théorie.

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}.

# 2: Un tour rapide du code source de Terraform

La table suivante liste une brève description des fichiers, ou groupes de fichiers, dans l'arborescence des sources de Terraform.

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.

# 3: Ajouter de nouveaux fichiers source

Si vos changements sont mineurs, tels qu'enlever un bogue, vous devriez vous efforcer de travailler avec le jeu existant de fichiers de code source. A moins que le bogue soit un défaut fondamental de conception ou quelque chose d'aussi important, vous n'avez pas à ajouter ou enlever de fichiers source. Si vous estimez être en présence d'un bogue d'une portée aussi significative, vous devriez probablement contacter RNG avant de procéder.

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.

# 4: Ajouter un nouveau type d'import (par exemple étendre Fichier->Ouvrir)

Ajouter le support pour un nouveau format, si vous souhaitez lire des terrains depuis quelque type de fichier que Terraform ne supporte pas actuellement, n'est pas difficile. Le mécanisme d'importation de Terraform est bien conçu pour supporter des types additionnelles. Vous travaillerez avec les classes HeightFieldReader et HeightFieldIO, définies dans HeightFieldReader.{h,cc} et HeightFieldIO.{h,cc}.

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.

# 5: Ajouter un nouveau type d'export (par exemple étendre Fichier->Enregistrer sous )

Ajouter le support pour un nouveau format, si vous souhaitez écrire des terrains depuis quelque type de fichier que Terraform ne supporte pas actuellement, n'est pas difficile. Le mécanisme d'exportation de Terraform est bien conçu pour supporter des types additionnelles. Vous travaillerez avec les classes HeightFieldWriter et HeightFieldIO, définies dans HeightFieldWriter.{h,cc} et HeightFieldIO.{h,cc}.

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.

# 6: Modifier le terrain

Une classe de Terraform simple à modifier est celle des fonctions qui font quelque chose à un terrain existant, tel qu'ajouter un cratère, inverser le terrain, l'agrandir ou le rapetisser, etc... Ces fonctions se trouvent typiquement dans la classe HeightFieldOps, qui est l'endroit où vous devriez implémenter de telles fonctions. Ajoutez votre fonction comme une nouvelle fonction membre publique dans HeightFieldOps.h, et mettez votre fonction dans HeightFieldOps.cc. Votre fonction devra avoir cette structure générale :

    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

# 7: Ajouter un nouveau générateur de terrain

Actuellement Terraform supporte deux générateurs de terrain différents, le générateur de synthèse spectrale et le générateur de subdivision. Pour ajouter un nouveau générateur de terrain, faites ce qui suit :

Créez une classe pour encapsuler votre générateur.
  Votre classe doit supporter une méthode, generate() en plus d'un constructeur et destructeur. Le constructeur recevra à pointeur HeightField que vous devrez enregistrer pour l'utiliser dans votre fonction génératrice. Votre fonction génératrice devra établir les membres de terrain d_xsize, d_ysize, et d_size (souvebez vous que, d_size est toujours équale à d_xsize*d_ysize) , et créez un tableau de PTYPEs de d_size (par exmeple myarray = new PTYPE[d_size];). Remplissez ce tableau avec des valeurs entre 0.0 à 1.0 par quoi que ce soit que vous voulez, ensuite assignez le membre de terrain d_hf à votre tableau (par exemple d_hf = myarray;).
Patchez votre générateur dans HeightFieldGenRandom.{h,cc}
  La classe HeightFieldGenRandom choisit simplement aléatoirement parmi les autres générateurs de terrain. Ainsi, vous devez renseigner HeightFieldGenRandom sur votre nouveau générateur de tel façon à ce qu'il soit disponible lorsque l'utilisateur choisit de générer un terran aléatoire.
Ajoutez votre générateur à la liste du sous-menu Fichier > Nouveau des générateurs de terrain.
  Vous devrez modifier MenuDefs.h, et TFWindowMain.*. Si votre générateur de terrain est suffisamment complexe pour requérir des données de l'utilisatuer, vous aurez aussi besoin d'ajouter une paire de fichiers TFDialogGen*.{h.cc} pour implémenter une boîte de dialogue pour que l'utilisateur puisse choisir les paramètres pour votre générateur de terrain. Voyez TFDialogGenSubdiv.* pour un bon exemple sur comment faire celà. Pour les générateurs de terrain qui nécessitent une boîte de dialogue, cette boîte de dialogue sera responsable pour créer un nouvel objet de votre classe génératrice, obtenir les paramètres de l'utilisatuer, et passer ces paramètres à votre fonction génératrice. Si votre générateur est suffisamment simple pour ne pas avoir besoin de paramères, le code que vous allez ajouter à TFWindowMain pour implémenter votre sous-menu Fichier > Nouveau devra créer le nouvel objet générateur et appeler sa fonction génératrice.

# 8: Support de déboguage

Heureusement pour vous, Terraform vient avec des mécanismes de déboguage assez robustes déjà mis en place. Premièrement, la classe GlobalTrace vous permet de régler ( et contrôler ) différents niveaux de sortie de traçages, pour vous donner un contrôle facile sur la quantité d'informations de déboguage que votre code génère. Cette classe contient aussi la méthode trace(), qui écrit vers la sortie standard si le niveau de déboguage correspond au niveau spécifié dans l'appel à trace().

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.

# 9: Soumettre vos changements

Une fois que vous avez implémenté toutes vos modifications, que vous les avez validez dans la version la plus récente de Terraform, et débogué votre travail, vous devriez soumettre votre nouveau code à la Terraform Mailing List. De cette façon les utilisateurs peuvent voir et commenter votre code, et RNG peut, s'il le trouve valable, ajouter votre code à la distribution principale de Terraform. Voici une façon de faire celà, en utilisant tcsh, Il y a plein d'autres façons bien sûr :

1. Faites un fichier tar gzippé (.tgz) contenant les diifs entre chaque fichier changé et la version originale de ces fichiers. Notez qu'à ce point ce serait opportun si vous gardiez une copie des fichiers originaux, dans quelque autre fichier. Chaque fois que je modifie un fichier source de Terraform, je le copie toujours par sécurité sous le nom "original_nom.orig", comme dans "HeightFieldExport.cc.orig". Laissez-moi dire encore qu'il est hautement recommandable que votre diffs soit fait à partir de la version la plus récente du code source.

    ~/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

2. Uuencode le fichier .tgz :

    ~/terraform-0.4.6/> uuencode myusername.0.4.6.tgz myusername.0.4.6.tgz > myusername.0.4.6.uu

3. Postez vos changements à la terraform mailing list:

    ~/terraform-0.4.6/> mail -s 'my spiffy terraform mods against v0.x.x' < myusername.0.4.6.tgz

Notez que votre fichier tar devrait être nommé avec votre nom d'utilisateur et la version de Terraform à laquelle vos changements s'appliquent, comme dans l'exemple précédent. De plus, si vous avez fait des changements conséquents à n'importe quelle fichier source de Terraform tel qu'un diffs devienne impraticable, alors vous pouvez choisir d'inclure simplement le fichier complet dans le fichier .tgz. Toutefois, s'il vous plaît, utilisez diffs aussi souvent que possible.