385 lines
16 KiB
HTML
385 lines
16 KiB
HTML
<html>
|
|
|
|
<head>
|
|
<BASE HREF="http://www-msi.ensil.unilim.fr/~maritaud/sunset/GrafX2-fra-OpEngine.html">
|
|
|
|
<meta http-equiv="Content-Type"
|
|
content="text/html; charset=iso-8859-1">
|
|
<meta name="GENERATOR" content="Microsoft FrontPage Express 2.0">
|
|
<title>GrafX2 - Le moteur des opérations</title>
|
|
</head>
|
|
|
|
<body bgcolor="#FFFFFF">
|
|
|
|
<h1 align="center">GrafX2 - Le moteur des opérations</h1>
|
|
|
|
<hr>
|
|
|
|
<h2>Explication du principe :</h2>
|
|
|
|
<p>Qu'appelle-t'on une opération? C'est l'emploi d'un outil de
|
|
dessin. Par exemple, le tracé libre à la main d'un pinceau (ou
|
|
d'une brosse) est une opération. Plus exactement, c'est tout
|
|
comportement de la souris lorsqu'elle se trouve sur les pixels de
|
|
l'image éditée. De ce fait, la gestion de la pipette se fait
|
|
également au travers des opérations.</p>
|
|
|
|
<p>Le moteur chargé de gérer ces opérations est simplifié
|
|
autant que possible. Son principe est le suivant : lorsque
|
|
la souris est au-dessus de l'image, la boucle principale de
|
|
gestion de GrafX2 (située dans MOTEUR.C) détermine la partie
|
|
atomique d'instructions à exécuter, puis l'exécute, pour
|
|
chaque combinaison d'un type d'opération (un entier, que l'on
|
|
nommera pour les besoins de cette page ID_OP), d'un état de la
|
|
souris (état de ses boutons plus exactement, codé sur un entier
|
|
également), et d'un nombre de valeurs présentes dans une pile
|
|
(pas la pile système, mais une pile de mots 16-bits
|
|
spécialement mise en place pour les opérations, afin qu'elles y
|
|
stockent des paramètres pour les opérations suivantes). On
|
|
appelle "partie atomique" une séquence d'instructions
|
|
qui doivent être exécutées intégralement à chaque itération
|
|
de la boucle principale. Elle se contente de ne faire que ce
|
|
qu'elle doit faire face à la situation qu'implique la
|
|
combinaison (ID_OP,Code souris, Etat pile) pour laquelle elle est
|
|
appelée.</p>
|
|
|
|
<p>En fait, le moteur doit être aidé pour déterminer les
|
|
insctructions à exécuter. Chacune de ces parties atomiques est
|
|
écrite sous la forme d'une fonction contenant tout le code qui
|
|
lui est nécessaire. Ces fonctions sont ce que l'on a appelé des
|
|
"fonction_action" (définition dans STRUCT.H), c'est à
|
|
dire une simple fonction qui ne reçoit pas de paramètres et qui
|
|
n'en renvoie pas. Un simple void Ma_fonction(void)...</p>
|
|
|
|
<p>Il n'est pas dans mon intention de vous apprendre à écrire
|
|
ces fonctions atomiques. Dans le cas des opérations les plus
|
|
simples à implémenter selon ce principe, la conception des
|
|
parties atomiques est assez intuitive, mais il arrive dans
|
|
certains cas que la méthode trouve certaines limites, il faut
|
|
alors tricher un peu (mais gentiment! - en plaçant des valeurs
|
|
arbitraires dans la pile uniquement pour permettre la transition
|
|
vers une autre combinaison choisie, donc une autre partie
|
|
atomique, par exemple). Vous pourrez trouver tous les exemples
|
|
que vous voudrez dans le fichier OPERATIO.C.</p>
|
|
|
|
<p>Après avoir écrit les parties atomiques qui vont être
|
|
nécessaires pour la gestion complète d'une opération (dans le
|
|
fichier OPERATIO.C), et avoir renseigné les prototypes de ces
|
|
fonctions (dans OPERATIO.H), il faut indiquer au moteur dans
|
|
quelles conditions utiliser ces fonctions. Cela se fait dans la
|
|
phase d'initialisation du programme (située dans le fichier
|
|
INIT.C), où l'on renseigne chacune des fonctions atomiques, en
|
|
décrivant les valeurs de combinaison qui doivent déclencher
|
|
leur appel. La fonction appelée se contente d'en prendre note en
|
|
remplissant un tableau nommé "Operation" (variable
|
|
globale déclarée dans GLOBAL.H, et indexée par les 3 valeurs
|
|
de la combinaison : ID_OP, code souris, état pile) à l'aide de
|
|
l'adresse de la fonction à appeler. Ce tableau va servir au
|
|
moteur à faire un appel direct de la fonction atomique d'après
|
|
la combinaison, sans avoir à faire toute une série de tests
|
|
(swich/case) fastidieux.</p>
|
|
|
|
<p>Comme nous avons pressenti qu'il arriverait fréquemment que
|
|
des fonctions atomiques auraient besoin systématiquement
|
|
d'effacer la souris pour faire des affichages à l'écran ou dans
|
|
l'image, et de la rafficher ensuite, et dans le souci d'éviter
|
|
de faire abusivement du copier/coller de code, nous avons
|
|
rajouté un booléen dans le tableau Operation qui permet
|
|
d'indiquer que la fonction atomique demande au moteur de se
|
|
charger lui-même de l'effacement et de la restauration du
|
|
curseur de la souris à l'écran. Finalement, je ne suis pas
|
|
certain que cela s'est révelé d'une grande utilité, mais je
|
|
vous le signale tout de même pour que vous compreniez
|
|
l'existance du paramètre "Effacer souris" qu'il faut
|
|
indiquer lorsqu'on renseigne une fonction atomique lors de
|
|
l'initialisation.</p>
|
|
|
|
<p>Il est important de noter qu'une fonction atomique est
|
|
appelée en permanence par la boucle principale, indépendamment
|
|
du fait que la souris change d'état ou non. Ces appels
|
|
répétés permettent par exemple d'avoir un spray qui continue
|
|
à agir lorsqu'on presse le bouton de la souris sans la
|
|
déplacer.</p>
|
|
|
|
<p>De plus, les fonctions atomiques n'ont pas à se soucier du
|
|
positionnement de la souris car dès que la pile contient des
|
|
données (généralement dès le premier clic), la souris est
|
|
limitée par le moteur afin de ne pas sortir de l'image. Le
|
|
moteur s'assure également que la souris ne franchis pas la barre
|
|
de split en mode "loupe". Enfin, n'oubliez pas de vider
|
|
complètement la pile en temps voulu, lorsque l'opération peut
|
|
s'achever! ;-)</p>
|
|
|
|
<hr>
|
|
|
|
<h2>Informations pour l'implémentation :</h2>
|
|
|
|
<ul>
|
|
<li>Le renseignement d'une fonction atomique se fait dans
|
|
INIT.C à l'aide de :</li>
|
|
</ul>
|
|
|
|
<blockquote>
|
|
<p>Initialisation_operation(ID_OP, Etat souris, Taille pile,
|
|
Callback, Effacer souris);</p>
|
|
</blockquote>
|
|
|
|
<ul>
|
|
<li>La pile à disposition des opérations pour le stockage
|
|
des paramètres :<ul>
|
|
<li>Elle contient des mots 16-bits (type word,
|
|
définit dans STRUCT.H)</li>
|
|
<li>Pour y poser une valeur :
|
|
Operation_PUSH(val)</li>
|
|
<li>Pour en retirer une valeur :
|
|
Operation_POP(&var)</li>
|
|
<li>Exemples de valeurs à y mettre :
|
|
coordonnées de souris, de pinceau... des
|
|
couleurs... des valeurs bidons pour changer
|
|
l'état de la pile...<br>
|
|
</li>
|
|
</ul>
|
|
</li>
|
|
<li>A l'intérieur d'une fonction atomique :<ul>
|
|
<li>Si la fonction correspond à la première étape
|
|
d'une opération, appeler
|
|
Initialiser_debut_operation()</li>
|
|
<li>Si la fonction correspond à la première étape
|
|
d'une opération qui peut modifier l'image,
|
|
appeler Backup() avant modification de l'image ;
|
|
ce qui permettra à l'utilisateur d'annuler la
|
|
modification avec "undo" s'il le
|
|
souhaite.</li>
|
|
<li>Pour afficher les coordonnées de la souris,
|
|
appeler Print_coordonnees() sans vous soucier de
|
|
savoir si la barre d'outils est visible. Si vous
|
|
avez besoin d'afficher une coordonnée qui doit
|
|
s'adapter au mode relatif ou absolu (selon le
|
|
paramétrage effectué par l'utilisateur),
|
|
utilisez Aff_coords_rel_ou_abs(RefX,RefY). Si les
|
|
coordonnées sont configurées pour être
|
|
relatives, cette fonction affichera la
|
|
différence entre les coordonnées actuelles du
|
|
pinceau et les coordonnées de référence
|
|
passées en paramètres. Dans le cas d'infos
|
|
spéciales à afficher dans la barre de menu, on
|
|
pourra se servir de la fonction
|
|
Print_dans_menu(texte,position en caractères).</li>
|
|
<li>Si vous avez besoin de vous assurer que les
|
|
boutons de la souris sont relâchés à un moment
|
|
de la fonction, utilisez Attendre_fin_de_click()</li>
|
|
<li>Etudiez OPERATIO.C pour avoir des exemples.<br>
|
|
</li>
|
|
</ul>
|
|
</li>
|
|
<li>Pour démarrer une opération (après avoir cliqué sur
|
|
un bouton de la barre d'outils par exemple), appeler
|
|
Demarrer_pile_operation(ID_OP). Note: il faut que le
|
|
curseur soit caché avant l'appel (et son état n'est pas
|
|
changé après). (voir exemples dans BOUTONS.C)<br>
|
|
</li>
|
|
<li>On peut connaître l'ID_OP de l'opération en cours
|
|
grâce à la variable globale Operation_en_cours<br>
|
|
</li>
|
|
<li>La variable globale Operation_Taille_pile correspond à
|
|
la taille de la pile d'opérations. Elle peut être
|
|
consultée et éventuellement modifiée (même s'il est
|
|
préférable d'utiliser Operation_PUSH et Operation_POP
|
|
dans ce dernier cas).</li>
|
|
</ul>
|
|
|
|
<hr>
|
|
|
|
<p>Comment rajouter une opération?</p>
|
|
<div align="center"><center>
|
|
|
|
<table border="1" width="100%">
|
|
<tr>
|
|
<td width="100%">Dans CONST.H :<ul>
|
|
<li>rajouter un identifiant (ID_OP) dans l'enum
|
|
OPERATIONS (exemple : OPERATION_DUMMY)</li>
|
|
</ul>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td width="100%">Dans GLOBAL.H :<ul>
|
|
<li>rajouter l'identifiant de la forme de curseur à
|
|
utiliser pour cette opération dans la table
|
|
CURSEUR_D_OPERATION[]. Faites bien attention à
|
|
l'insérer au bon endroit (place correspondant à
|
|
ID_OP).</li>
|
|
</ul>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td width="100%">Dans OPERATIO.C :<ul>
|
|
<li>Ecrire chaque fonction atomique de l'opération.</li>
|
|
</ul>
|
|
<blockquote>
|
|
<p>Exemple :</p>
|
|
</blockquote>
|
|
<blockquote>
|
|
<blockquote>
|
|
<p>void Dummy_1_0(void)<br>
|
|
// Opération : OPERATION_DUMMY<br>
|
|
// Click Souris: 1<br>
|
|
// Taille_Pile : 0<br>
|
|
// Souris effacée: Oui (précisé grâce à
|
|
Initialiser_operation() dans INIT.C)<br>
|
|
{<br>
|
|
Initialiser_debut_operation();<br>
|
|
Backup();<br>
|
|
<br>
|
|
// ACTIONS...<br>
|
|
<br>
|
|
Operation_PUSH(une_valeur_nécessaire_pour_les_prochaines_étapes);<br>
|
|
Operation_PUSH(une_autre_valeur_nécessaire_plus_tard);<br>
|
|
}</p>
|
|
</blockquote>
|
|
</blockquote>
|
|
<blockquote>
|
|
<blockquote>
|
|
<p>(Pour cet exemple, l'étape suivante à
|
|
définir sera donc une étape avec la taille de
|
|
pile à 2)</p>
|
|
</blockquote>
|
|
</blockquote>
|
|
<ul>
|
|
<li>Il y a une action par défaut qui se contente
|
|
d'afficher les coordonnées de la souris
|
|
lorsqu'on ne définit pas explicitement de
|
|
fonction atomique gérant une certaine
|
|
combinaison (ID_OP, Etat souris, Taille pile).</li>
|
|
<li>Si l'opération représente une interruption
|
|
temporaire, et qu'il vous paraît juste de
|
|
restaurer l'opération précédente une fois
|
|
qu'elle est terminée (cas de la loupe, de la
|
|
pipette, des prises de brosse, ...), rajouter
|
|
l'ID_OP dans la section correspondante de la
|
|
fonction Demarrer_pile_operation(), en début de
|
|
fichier.</li>
|
|
<li>Si l'opération voit un intérêt à accepter que
|
|
l'utilisateur change de couleur de pinceau en
|
|
cours d'opération (cas du dessin continu,
|
|
discontinu, le spray, et les lignes centrées),
|
|
rajouter l'ID_OP dans la section correspondante
|
|
de la fonction Demarrer_pile_operation(), en
|
|
début de fichier.</li>
|
|
</ul>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td width="100%">Dans OPERATIO.H :<ul>
|
|
<li>Ecrire le prototype des ces fonctions atomiques.</li>
|
|
</ul>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td width="100%">Dans INIT.C :<ul>
|
|
<li>Dans Initialisation_des_operations(), pour chaque
|
|
fonction atomique de l'opération, écrire un
|
|
appel à Initialiser_operation(ID_OP, Etat
|
|
souris, Taille pile, Callback, Effacer souris);<ul>
|
|
<li>ID_OP : identifiant de l'opération
|
|
dont dépend la fonction atomique
|
|
(défini dans CONST.H)</li>
|
|
<li>Etat souris :<ul>
|
|
<li>0 = boutons relachés</li>
|
|
<li>1 = bouton gauche enfoncé</li>
|
|
<li>2 = bouton droit enfoncé</li>
|
|
<li>(note : l'état "boutons
|
|
gauche et droit enfoncés"
|
|
n'existe pas, seuls les 3 états
|
|
ci-dessus sont autorisés)</li>
|
|
</ul>
|
|
</li>
|
|
<li>Taille pile : nombre de paramètres
|
|
dans la pile</li>
|
|
<li>Callback : nom de la fonction
|
|
atomique à appeler pour la combinaison
|
|
des 3 paramètres précédents</li>
|
|
<li>Effacer souris : booléen indiquant
|
|
que le moteur se chargera d'effacer le
|
|
curseur souris avant l'appel à la
|
|
fonction atomique, et de le rafficher
|
|
après sa sortie.</li>
|
|
</ul>
|
|
</li>
|
|
</ul>
|
|
<blockquote>
|
|
<p>Exemple :</p>
|
|
</blockquote>
|
|
<blockquote>
|
|
<blockquote>
|
|
<p>Initialiser_operation(OPERATION_DUMMY, 1, 0,
|
|
Dummy_1_0, 1);</p>
|
|
</blockquote>
|
|
</blockquote>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</center></div>
|
|
</body>
|
|
|
|
<SCRIPT language="Javascript">
|
|
<!--
|
|
|
|
// FILE ARCHIVED ON 20021206090126 AND RETRIEVED FROM THE
|
|
// INTERNET ARCHIVE ON 20070414155905.
|
|
// JAVASCRIPT APPENDED BY WAYBACK MACHINE, COPYRIGHT INTERNET ARCHIVE.
|
|
// ALL OTHER CONTENT MAY ALSO BE PROTECTED BY COPYRIGHT (17 U.S.C.
|
|
// SECTION 108(a)(3)).
|
|
|
|
var sWayBackCGI = "http://web.archive.org/web/20021206090126/";
|
|
|
|
function xResolveUrl(url) {
|
|
var image = new Image();
|
|
image.src = url;
|
|
return image.src;
|
|
}
|
|
function xLateUrl(aCollection, sProp) {
|
|
var i = 0;
|
|
for(i = 0; i < aCollection.length; i++) {
|
|
if (typeof(aCollection[i][sProp]) == "string") {
|
|
if (aCollection[i][sProp].indexOf("mailto:") == -1 &&
|
|
aCollection[i][sProp].indexOf("javascript:") == -1) {
|
|
if(aCollection[i][sProp].indexOf("http") == 0) {
|
|
aCollection[i][sProp] = sWayBackCGI + aCollection[i][sProp];
|
|
} else {
|
|
aCollection[i][sProp] = sWayBackCGI + xResolveUrl(aCollection[i][sProp]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
xLateUrl(document.getElementsByTagName("IMG"),"src");
|
|
xLateUrl(document.getElementsByTagName("A"),"href");
|
|
xLateUrl(document.getElementsByTagName("AREA"),"href");
|
|
xLateUrl(document.getElementsByTagName("OBJECT"),"codebase");
|
|
xLateUrl(document.getElementsByTagName("OBJECT"),"data");
|
|
xLateUrl(document.getElementsByTagName("APPLET"),"codebase");
|
|
xLateUrl(document.getElementsByTagName("APPLET"),"archive");
|
|
xLateUrl(document.getElementsByTagName("EMBED"),"src");
|
|
xLateUrl(document.getElementsByTagName("BODY"),"background");
|
|
var forms = document.getElementsByTagName("FORM");
|
|
if (forms) {
|
|
var j = 0;
|
|
for (j = 0; j < forms.length; j++) {
|
|
f = forms[j];
|
|
if (typeof(f.action) == "string") {
|
|
if(typeof(f.method) == "string") {
|
|
if(typeof(f.method) != "post") {
|
|
f.action = sWayBackCGI + f.action;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-->
|
|
</SCRIPT>
|
|
|
|
</html>
|