[Resolu] Il ne veut pas de case 10 dans mon paint

Bonjour,

Comme d'habitude j'ai l'habitude de dire c'est le compilateur quand je fais une faute. Et là je bute sur celle là depuis deux semaines (je ne fais pas que ça, mais souvent la nuit porte conseil)

Je joue avec un afficheur graphique sensitif parallèle VMA412 sur une Uno. Je suis en train d'écrire un programme ressemblant au Paint de windowsXP. Le premier avril j'avais fait une video avec ce logiciel, précédé d'un poisson. La première minute montre en effet le chargement de windows qui est complètement bidon, mais la suite est réelle. Je n'ai pas montré la possibilité d'écrire des mots, cela ne fonctionnait pas.

L'écran est le suivant:
paint.gif

Le principe est le suivant: pour choisir une fonction, j'ai 11 boutons radio (quand un s'active, il désactive les autres) et je peux choisir une fonction parmi 11 (crayon, pipette, gomme...). 3 fonctions n'entrent pas en jeu et sont géré par d'autres fonctions : effacement d'écran, sauvegarde et lecture.

Quand je commence à dessiner la fonction onSelectDessin() est appelée, elle travaille tant que le stylet est appuyé, par exemple dessine avec le crayon, indique ou va se faire le cercle ou le rectangle... Quand je soulève le stylet elle fait éventuellement un deuxième travail, trace le cercle ou le rectangle...

La fonction onSelectDessin() se compose principalement d'un switch, qui va différencier ce qu'il y a à faire en fonction de la fonction C'est getRadioValeur() qui retourne un code de 0 à 10. Et j'ai un problème avec la fonction 10 (écrit un texte).

Comme le switch n'exécute pas ce qui est derrière case 10: j'ai doublé le travail à faire avec in if, genre:
switch (getRadioValeur())
{
case 0: ...
case 1: ...
....
case 10: <le code pour écrire des lettres>
}
if (getRadioValeur()==10) <le code pour écrire des lettres>

Voici le code de la fonction onSelectDessin(). J'ai retiré les fonction 0 à 8, la taille du message est trop grosse sinon. Fonction complète attachée. Le code complet ne peut s'éxécuter qu'avec un VMA412 équipé d'un ST7781.

/////////////////////////////////////////////////////////////////////////////
////                                Dessin                                 //
/////////////////////////////////////////////////////////////////////////////
int x2, y2, // Valeur actuelles
    x1, y1; // Valeur une boucle plus tôt
String s; // Pour les tracés de textes
void onSelectDessin() // Appelée quand je commence le dessin
{
  int x = getTouchX(); int y = getTouchY(); // Récupère les coordonnés visées
  x2 = x; y2 = y; // Au départ tous les points sont confondus; x1 et y1 sont initialisés plus loin
  setLimites(68, 2, 317, 196); // A la zone de dessin
  // Initialisations pour certains tracés
  switch (getRadioValeur())
  {
    case 0: // ////////// Crayon //////////
      line(x, y, x, y, couleur_trace); // Initialise pour lineTo
      break;
    case 4: // ////////// Droite //////////
    case 5: // ////////// Cercle //////////
    case 6: // ////////// Rectangle //////////
    case 7: // ////////// Inversion //////////
      setDrawInverse(INVERSE); // On va faire un curseur droite
      break;
  }
  do // On boucle sans passer par le gestionnaire, trop lent
  {
    x1 = x2, y1 = y2; //Sauvegarde de la valeur du coup d'avant
    x2 = getTouchX(); y2 = getTouchY(); // Utile pour la boucle
    //###############################################################################
    // le problème commence ici. Si je veux écrire du texte le bouton Radio 10 est sélectionné
    // et getRadioValeur retourne 10
    //###############################################################################
    Serial.print("Je vais rentrer dans le switch et getRadioValeur() vaut "); Serial.println(getRadioValeur());
    switch (getRadioValeur())
    {
      case 0:
   ......
      case 8: // ////////// Fill //////////
        fill(x2, y2, couleur_trace);
        break;
      case 9: // ////////// Bombe //////////
        float angle = random(10000); angle = angle * PI / 5000;
        byte rayon = random(15);
        point(x2 + rayon * cos(angle), y2 + rayon * sin(angle), couleur_trace);
        break;
      //#######################################################################
      // Normalement je devrais rentrer dans la partie case 10
      //########################################################################
      case 10: // ////////// Lettres //////////
        Serial.println("Je suis entré dans case 10");
        s = clavier40(79, 10, F("Chaine?"));
        setTextSize(clavier40(79, 10, F("Taille?")).toInt());
        setTextColor(couleur_trace);
        setTextCursor(x2, y2);
        text(s);
        Serial.println("J'ai fini case 10");
        break;
        //#########################################################################
        // Fin de la partie case 10
        //#########################################################################
    }

    //#######################################################################
    // Alternative je passe par if (pour vérifier que ce que j'ai écris fonctionne
    //########################################################################
    if (getRadioValeur() == 10)
    {
      Serial.println("Je suis entré par le if");
      s = clavier40(79, 10, F("Chaine?"));
      setTextSize(clavier40(79, 10, F("Taille?")).toInt());
      setTextColor(couleur_trace);
      setTextCursor(x2, y2);
      text(s);
      Serial.println("J'ai fini le if");
    }
    //#######################################################################
    // Fin du if
    //########################################################################


  } while (isTouchTrue()); // Fini si on lâche le stylet
  switch (getRadioValeur()) // Onvient de lâcher le stylet
  {
    case 4: // ////////// Droite //////////
    case 5: // ////////// Cercle //////////
    case 6: // ////////// Rectangle //////////
    case 7: // ////////// Inversion //////////
  }
  setLimites(0, 0, 319, 239);
}

A part la fonction 10 "écriture de mots", tout fonctionne à merveille. Comme la fonction 10 est doublée, avec le code ci-dessus, quand le stylet pointe, je pensais avoir les messages

Je vais rentrer dans le switch et getRadioValeur() vaut 10
Je suis entré dans case 10
J'ai fini case 10
Je suis entré par le if
J'ai fini le if

et je devrai avoir deux fois la possibilité décrire des lettres. Mais j'ai:

Je vais rentrer dans le switch et getRadioValeur() vaut 10
Je suis entré par le if
J'ai fini le if

Et évidemment je ne peux entrer qu'une fois du texte

La partie concernant la case 10 n'est pas exécutée, je devrai dire n'est pas compilée

  • Si je supprime tout ce qui concerne case 10, le programme fait la même taille (même si je rajoute un asm("nop")!)
  • Si je change les noms des fonction 9 et 10 , la taille du programme ne change pas, la clause case 10 n'est pas compilée
  • Si j'inverse en mettant d'abord case 10 puis case 9, mon programme augmente de taille et la clause case 10 fonctionne (j'avais déja fait ce test mais cela n'avait pas marché il y a 10 jours)
  • A un moment j'avais viré le switch pour le remplacer par des if, c'était pareil, pas de "if 10"

Ya pas deux solutions, j'ai écrit un bug, mais je ne le vois pas. Ou l'ai-je mis?

paint.gif

onSelectDessin.ino (5.59 KB)

Tu es sûr que getradiovaleur() renvoie 10 ?
Et puis à chaque fois que tu en a besoin, tu rappelles la fonction. Il peut y avoir un "effet de bord".
Si cela fait sens, tu devrais faire une seule fois int choix = getradiovaleur(), et ensuite tu travailles avec choix ( switch ( choix ) ... )

Merci pour ta première lecture.

Le code devient (variable de choix rajoutée: fonctionSelectionnee):

/////////////////////////////////////////////////////////////////////////////
////                                Dessin                                 //
/////////////////////////////////////////////////////////////////////////////
int x2, y2, // Valeur actuelles
    x1, y1; // Valeur une boucle plus tôt
String s; // Pour les tracés de textes
void onSelectDessin() // x y: valeur à l'entrée dans la fonction
{
  int x = getTouchX(); int y = getTouchY(); // Récupère les coordonnés visées
  x2 = x; y2 = y; // Au départ tous les points sont confondus; x1 et y1 sont initialisés plus loin
  setLimites(68, 2, 317, 196); // A la zone de dessin
  // Initialisations pour certains tracés
  byte fonctionSelectionnee = getRadioValeur();
  switch (fonctionSelectionnee)
  {
    case 0: // ////////// Crayon //////////
      line(x, y, x, y, couleur_trace); // Initialise pour lineTo
      break;
    case 4: // ////////// Droite //////////
    case 5: // ////////// Cercle //////////
    case 6: // ////////// Rectangle //////////
    case 7: // ////////// Inversion //////////
      setDrawInverse(INVERSE); // On va faire un curseur droite
      break;
  }
  do // On boucle sans passer par le gestionnaire, trop lent
  {
    x1 = x2, y1 = y2; //Sauvegarde de la valeur du coup d'avant
    x2 = getTouchX(); y2 = getTouchY(); // Utile pour la boucle
    //###############################################################################
    // le problème commence ici. Si je veux écrire du texte le bouton Radio 10 est sélectionné
    // et getRadioValeur retourne 10
    //###############################################################################
    Serial.print("Je vais rentrer dans le switch et fonctionSelectionnee vaut "); Serial.println(fonctionSelectionnee);
    switch (fonctionSelectionnee)
    {
      case 0: // ////////// Crayon //////////
        lineTo(x2, y2);
        break;
      case 1: // ////////// Pipette //////////
        setLimites(0, 0, 319, 239); // Pour pouvoir remplir la palette du choix
        if (dessineEfface) fill(69, 213, couleur_trace = getPoint(x, y)); // On change la couleur du dessin
        else fill(82, 227, couleur_fond = getPoint(x, y)); // On change la couleur du fond
        break;
      case 2: // ////////// Pinceau //////////
      case 3: // ////////// Gomme //////////
        fillCircle(x2, y2, 3, fonctionSelectionnee == 2 ? couleur_trace : couleur_fond); // Couleur fond ou forme
        break;
      case 4: // ////////// Droite //////////
      case 5: // ////////// Cercle //////////
        line(x, y, x1, y1); // Efface l'ancienne
        line(x, y, x2, y2); // Dessine la nouvelle
        break;
      case 6: // ////////// Rectangle //////////
      case 7: // ////////// Inversion //////////
        rect(x, y, x1, y1); // Efface l'ancien
        rect(x, y, x2, y2); // Dessine le nouveau
        break;
      case 8: // ////////// Fill //////////
        fill(x2, y2, couleur_trace);
        break;
      case 9: // ////////// Bombe //////////
        float angle = random(10000); angle = angle * PI / 5000;
        byte rayon = random(15);
        point(x2 + rayon * cos(angle), y2 + rayon * sin(angle), couleur_trace);
        break;
      //#######################################################################
      // Normalement je devrais rentrer dans la partie case 10
      //########################################################################
      case 10: // ////////// Lettres //////////
        Serial.println("Je suis entré dans case 10");
        s = clavier40(79, 10, F("Chaine?"));
        setTextSize(clavier40(79, 10, F("Taille?")).toInt());
        setTextColor(couleur_trace);
        setTextCursor(x2, y2);
        text(s);
        Serial.println("J'ai fini case 10");
        break;
        //#########################################################################
        // Fin de la partie case 10
        //#########################################################################
    }

    //#######################################################################
    // Alternative je passe par if (pour vérifier que ce que j'ai écris fonctionne
    //########################################################################
    if (fonctionSelectionnee == 10)
    {
      Serial.println("Je suis entré par le if");
      s = clavier40(79, 10, F("Chaine?"));
      setTextSize(clavier40(79, 10, F("Taille?")).toInt());
      setTextColor(couleur_trace);
      setTextCursor(x2, y2);
      text(s);
      Serial.println("J'ai fini le if");
    }
    //#######################################################################
    // Fin du if
    //########################################################################


  } while (isTouchTrue()); // Fini si on lâche le stylet
  switch (fonctionSelectionnee) // Onvient de lâcher le stylet
  {
    case 4: // ////////// Droite //////////
      setDrawInverse(NORMAL); // Pour dessiner
      line(x, y, x2, y2, couleur_trace); // Tracé
      break;
    case 5: // ////////// Cercle //////////
      line(x, y, x2, y2); // Effacel'ancienne à cause des cercles
      setDrawInverse(NORMAL); // Pour dessiner
      circle((x + x2) / 2, (y + y2) / 2, sqrt(pow(x - x2, 2) + pow(y - y2, 2)) / 2, couleur_trace); // Tracé
      break;
    case 6: // ////////// Rectangle //////////
      setDrawInverse(NORMAL); // Pour dessiner
      rect(x, y, x2, y2, couleur_trace); // Tracé du rectangle
      break;
    case 7: // ////////// Inversion //////////
      fillRect(x, y, x2, y2); // Inversion du rectangle
      setDrawInverse(NORMAL); // Pour dessiner
      break;
  }
  setLimites(0, 0, 319, 239);
}

Mais la console renvoie:

Je vais rentrer dans le switch et fonctionSelectionnee vaut 10
Je suis entré par le if
J'ai fini le if

Et le dessin ne se fait qu'une fois.

getRadioValeur() n'apparait plus maintenant qu'une seule fois, ligne 13, dans toute la fonction dessin. Cette fonction renvoie la valeur du bouton fonction sélectionné. Elle renvoie la même valeur tant que je n'ai pas choisi autre chose que la fonction texte.

Mais si sa valeur changeait, cela n’expliquerait pas pourquoi si je supprime toute la partie case 10 le programme généré ne change pas de taille. Comme il est le compilateur semble ne pas créer cette partie de code.

Quand je dis semble, c'est que si ce que je ne suis sûr de rien... sauf qu'il y a un bug.

Si les blocs d'instructions dans un case sont un peu long, il est conseillé de les mettre entre accolades (y.c. le break). Je ne sais pas si ça crée ton problème, mais tu peux essayer de les ajouter

lesept:
Si les blocs d'instructions dans un case sont un peu long, il est conseillé de les mettre entre accolades (y.c. le break). Je ne sais pas si ça crée ton problème, mais tu peux essayer de les ajouter

Salut , ou bien de decouper cette partie en sous fonction , on y voit desuite plus clair .

un case renvoie a une sous fonction . ca facilite la lecture , la comprehension et l' ecriture du code .

il est toujours possible apres , de supprimer les sous fonctions et de remettre dans le swith case le code qu ' elle contenait en lieu et place de leurs appels respectifs !

Merci à @lesept qui ne m'a pas donné la solution, mais qui m'a permis de la trouver!

J'avais déjà essayé de faire un switch avec 20 case en occupant 90% de la mémoire, ça a toujours fonctionné, et il n'y avait pas accolades.

Bien sûr rajouter les accolades, je pensais que cela ne servait à rien, mais j'ai fait l'essai, j'ai mis des accolades à toutes les clauses case, sauf si j'ai:
case 4: // ////////// Droite //////////
case 5: // ////////// Cercle //////////
{
line(x, y, x1, y1); // Efface l'ancienne
line(x, y, x2, y2); // Dessine la nouvelle
break;
}
où je n'ai pas mis d’accolades pour le case 4. Je savais bien que cela ne fonctionnerait pas, mais j'ai perdu, tout fonctionnait. Alors j'ai repris mon case 10, je lui ai enlevé les accolades, et ... ça fonctionnait toujours. Après moults essais, il faut des accolades non pas pour le case 10, mais pour le case 9!

Explication: le case 9 contient la ligne:
float angle = random(10000); angle = angle * PI / 5000;
et angle est donc local et il est initialisé dans le case 9. Mais la porté de angle est celle du bloc qui le définit. Il est donc dans la porté de case 10 qui connait angle (même si il ne l'utilise pas), mais la déclaration d'angle ne fonctionne pas car angle doit être initialisé (c'est sa définition) mais le compilateur ne peut pas l'initialiser car le code est ailleurs.

On ne peut pas avoir une variable locale dans un case si sa porté va au delà. En mettant des accolades pour les instruction du case 9, angle devient local uniquement pour le case 9, et plus après.

Je retiens ce qui est obligatoire et que par paresse je ferai:

  • pour case, il n'y a pas besoin d’accolades sauf si on veut avoir une variable locale
  • le dernier break ne sert strictement à rien
    Il y a aussi que chaque fois que je fais le contraire, j'ai des lignes inutiles en plus. Et puis c'est aussi des habitudes.

Mais si on met des accolades et un break à la fin:

  • ça marche dans tous les cas
  • ça marche si on n'a pas tout compris
  • ça ne prend pas d'octets supplémentaires

Les docs sur le C ne m'ont jamais mis les accolades, mais la référence Adruino les met.

Si les blocs d'instructions dans un case sont un peu long

je ne pense pas que ce soit le cas, 8 des 13 case on entre 0 et 3 lignes de code, break inclus. C'est case 10 le plus gros avec 5 lignes.

couper cette partie en sous fonction , on y voit de suite plus clair

pas si sûr ici si je prends les case 2 et 3:

     case 2: // ////////// Pinceau //////////
      case 3: // ////////// Gomme //////////
        {
          fillCircle(x2, y2, 3, fonctionSelectionnee == 2 ? couleur_trace : couleur_fond); // Couleur fond ou forme
          break;
        }

Ou bien ne mettre qu'une seule fonction? Je l’appelle pinceauGomme?

J'avais aussi la possibilité que chaque bouton appelle sa propre fonction, mais il y aurait eu beaucoup de doublons, et je travaille avec un Uno! C'est d'ailleurs fait pour trois fonctions qui ne ressemblent pas aux autres.

Salut , ou bien de decouper cette partie en sous fonction...
il est toujours possible apres , de supprimer les sous fonctions et de remettre dans le swith...

Pour des tests, oui, mais là je savais que le code fonctionnait, je l'ai doublé après le switch

J'avais fait une erreur (comme d'hab), mais je voulais savoir où! Maintenant, c'est un peu la faute du compilateur qui voit une erreur et qui ne me supprime le code sans me le dire (faut bien accuser les autres!)

je ne savais pas que des accolades au milieu d ' une fonction , rendait une variable localisé a entre les accolades , ca me parait etonant cette histoire .

Mais l ' important c ' est que ca marche :wink:

une variable est locale la ou on la declare , pas là ou on lui affecte une valeur .

je n ' ai pas tout compris là :

Explication: le case 9 contient la ligne:
float angle = random(10000); angle = angle * PI / 5000;
et angle est donc local et il est initialisé dans le case 9. Mais la porté de angle est celle du bloc qui le définit. Il est donc dans la porté de case 10 qui connait angle (même si il ne l'utilise pas), mais la déclaration d'angle ne fonctionne pas car angle doit être initialisé (c'est sa définition) mais le compilateur ne peut pas l'initialiser car le code est ailleurs.

ca donne plutot l' impression que case9 n ' a pas acces a la variable angle , car elle n ' est pas declare / initialise dans la fonction ou se trouve le switch , mais plutot dans une autre fonction , mais ce n' est pas le cas , du coup je reste etonne .

petite remarque , dans votre switch case , vous n ' avez pas mis d ' action par defaut default : break;

je ne savais pas que des accolades au milieu d ' une fonction , rendait une variable localisé a entre les accolades , ca me parait etonant cette histoire .

Une variable a la porté du bloc qui la déclare. Si on la met au début de programme, elle est locale jusqu'à la fin, c'est a dire partout. On dit qu'elle est globale.

Si on écrit

for (int i=0; i<10; i++) Serial.println(i);
Serial.println(i); // Juste pour savoir avec quelle valeur i reste

Cela fait une erreur de compilation car i est déclaré dans la boucle for et est donc local à la boucle. Si on en sort i n'existe plus. Et {} permet de définir un bloc et les variables déclarées dedans ne peuvent pas sortir (ont une portée qui ne dépasse pas). Essaie:

void setup()
{
  {int x=10;}
  Serial.begin(115200);
  Serial.println(x);
}
void loop()
{
}

La porté de x c'est les accolades.
On pourrait rajouter des accolades un peu n'importe où. Ou rajouter des ; après une instruction (pas au milieu ce qu'on voit parfois après if (condition)):

void setup()
{
  int x=10;
  {Serial.begin(115200);;;;;;;}
  Serial.println(x);
}
void loop()
{
}

ca donne plutot l' impression que case9 n ' a pas acces a la variable angle , car elle n ' est pas declare / initialise dans la fonction ou se trouve le switch , mais plutot dans une autre fonction , mais ce n' est pas le cas , du coup je reste etonne .

la variable angle existe à partir de sa définition float angle... jusqu'à la sortie du bloc normalement } si j'écris correctement. Cette variable n'est pas utilisée ni déclarée ailleurs. Avoir une variable très locale qui est détruite de suite ne fractionne pas la pile.

vous n ' avez pas mis d ' action par defaut default : break;

Il n'y a pas d'autre possibilité que mes 11 boutons, cela ne sert à rien. Cela fait une ligne de plus (je pense que cela ne coûte pas d'octets de code. Mais si cela ne sert à rien, je ne le mets pas. Par ailleurs si un jour j'en mettais une je ne mettrai jamais le break qui fait sortir de la boucle à un moment où de toutes façon je sors.

apres reflexion sur les accolades , ca parait logique .

void mafonction () {

int x=0;

}
les accolades definissent un bloc d' instruction que ce soit dans une fonction , un switch , une boucle ou ailleurs , ce qui m ' a parut etonnant c ' est qu on definnissent un bloc d ' instruction qui ne fait reference a rien .

Pourtant au debut des cours en C , c ' est ce qu ' on nous apprend , mais ca remonte a loin ca ::slight_smile:

Donc vous avez entierement raison !

Parfois des rappels de base s ' averent efficaces :stuck_out_tongue: ca peut toujours servir , la preuve avec votre code !

apres reflexion sur les accolades , ca parait logique

Oui, mais moi,je ne le suis pas assez. Il m'a fallu plus de 15 jours et de l'aide pour trouver.