Un interrupteur à 2 états

Ce didacticiel suppose que vous connaissiez la définitions des différents noeuds, events et spécifications de base du VRML97.
Vous pourrez trouver toutes ces infos (en français) sur le site de 3Dnet.
N'oubliez pas que tous les ROUTE doivent se trouver tout à la fin du fichier.

But :

Dans cet exercice, je vous propose de réaliser un interrupteur avec conservation d'un état. Par défaut, il sera de couleur rouge (éteint). Si on clique une fois sur l'interrupteur, il devient vert(allumé). Si on recliques dessus, il redevient rouge (éteins).

En pratique :

Pour l'interrupteur, nous utiliserons une simple boite.

Shape {
         geometry Box {
         }
}           

Sauvegardez le fichier avec l'extension .wrl et doubles-cliquez dessus. Vous voyez une boite blanche pas très jolie. Pour lui donner une plus belle allure, ajoutons lui une apparence et un matériau.

Shape {
           geometry Box {
           }
           appearance Appearance {
                  material Material {
                  diffuseColor 1 0  0
                  }
           }
 }

Nous avons une jolie boite rouge vif.
Maintenant, nous devons créer un TouchSensor, lequel détectera si nous cliquons sur la boite.

TouchSensor {}

Nous devons associer ce TouchSensor à la boite. Sans cela, le touch sensor réagir avec tous les objets de la scène, ce qui peut être ennuyeux. Pour celà, nous allons faire du Sensor le "frère" de la boite (enfant du même parent) . Créons d'abord le parent : un simple noeud Transform.

Transform {
children[]
}

Assignons lui ses enfants :

Transform {   # noeud parent
children[
TouchSensor {} # sensor, premier enfant
Shape { # boite, 2ème enfant geometry Box { } appearance Appearance { material Material { diffuseColor 1 0 0 } } }
] # fin des enfants
}

Maintenant, nous allons lier les éléments entre eux. Mais pour cela, il nous faut les nommer avec la commande DEF.
Tout d'abord, le TouchSensor :

DEF capteur TouchSensor {}

Puis le matériau, puisque nous allons jouer sur la couleur.

material DEF mat Material {
	diffuseColor 1 0 0 
}

Nous utilisons l'évenement touchTime du capteur pour changer la couleur. Le problème, c'est que cet évènement est de type SFTime et qu'il est incompatible avec le type SFColor de la couleur diffuse de notre matériau mat. Nous allons donc utilisez un noeud Script pour convertir d'un type à l'autre.

Script {}

Nous le nommons lui aussi car il fait partie des éléments à lier.

DEF change Script {}

Ce script va recevoir l'évènement SFTime émis par le capteur via un eventIn.

DEF change Script {
   eventIn SFTime touch          
}

Pour lier le SFTime du capteur au script "change", utilisons la commande ROUTE.

ROUTE capteur.touchTime TO change.touch

Le script change va émettre en sortie un évènement de type SFColor via un eventOut.

DEF change Script { 
eventIn SFTime touch
eventOut SFColor couleur
}

Nous routons cet évènement vers la couleur diffuse du matériau mat

ROUTE change.couleur TO mat.diffuseColor

Bien. Définissons maintenant le corps de notre script. C'est un script javascript, avec une fonction touch qui s'exécute lorsque l'évènement touchTime du capteur se produit. Le script reçoit cet évènement via l'eventIn touch que nous avons définis.

DEF change Script {
      eventIn SFTime touch
      eventOut SFColor couleur
      url "javascript: function touch(){
      }"
}

Notre fonction doit émettre la couleur en eventOut. Il faut mettre la composante R de la couleur à 1 et la composante V à 0 :

DEF change Script {
    eventIn SFTime touch
    eventOut SFColor couleur
    url "javascript: function touch(){
        couleur[0] = 0 ;
        couleur[1] = 1 ;
    }"
}

Bon, lorsqu'on clique sur la boite, elle devient verte. Mais si on recliques dessus, elle reste verte! C'est parce que le script ne sait pas que la boite est déjà verte. Nous allons ajouter un champs SFBool qui enregistrera cet état. Par défaut, l'état sera False (éteins... ou rouge).

field SFBool etat FALSE

Pour faire l'enregistrement de l'état, nous changeons la valeur de celui-ci lorsqu'on clique sur la boite. Autrement dit, lorsqu'on active la fonction touch du script

DEF change Script {
    eventIn SFTime touch
    field SFBool etat FALSE
    eventOut SFColor couleur
    url "javascript: function touch(){
         etat = !etat ;
         couleur[0] = 0 ;
         couleur[1] = 1 ;
    }"
}

Actuellement, la fonction change la variable de l'état et l'eventOut de la couleur.

Maintenant que nous pouvons avoir accès à l'état de notre interrupteur, nous pouvons déterminer deux cas:
- l'interrupteur est éteins et il est de couleur rouge,
- il est allumé et de couleur verte.
Nous savons que l'interrupteur ne peut pas être allumé et éteins simultanément. Ce qui veut dire que SI il est éteins, il est rouge, SINON il est vert. En javascript, c'est l'instruction IF qui se charge de ça :

DEF change Script {
   eventIn SFTime touch
   field SFBool etat FALSE
   eventOut SFColor couleur
   url "javascript: function touch(){
   etat = !etat ;
      if (etat){
         couleur[0] = 0 ;
         couleur[1] = 1 ;
      }
      else {
         couleur[0] = 1 ;
         couleur[1] = 0 ;
      }
}" }

Voir la scène Voir le source

Pour aller plus loin :

Plutôt que de créer une eventOut SFColor que nous routons vers le noeud matériau, nous pouvons utiliser directement le noeud matériau comme champ dans notre script. Nous accédons au propriété diffuscolor et emissivecolor directement au sein du script. Du coup nous n'avons plus besoin de l'eventOut ni de router celui-ci.

DEF change Script {
   eventIn SFTime touch
   field SFBool etat FALSE
   field SFNode mat USE mat
   url "javascript: function touch(){
      etat = !etat ;
       if (etat){
           mat.diffuseColor [0] = 0 ;
           mat.diffuseColor [1] = 1 ;
       }
       else {
           mat.diffuseColor [0] = 1 ;
           mat.diffuseColor [1] = 0 ;
       }
}" } ROUTE capteur.touchTime TO change.touch

Voir la scène Voir le source

Note : vous pouvez utilisez ce principe de conservation d'état pour de multiple chose. Il suffit de changer le type d'eventOut et les routages. De même, vous pouvez déclencher ce comportement à partir de plusieurs types d'évenements et pas seulement d'un temps...

Valid XHTML 1.0 Strict Valid CSS!