Logiciel libre de géométrie, d'analyse et de simulation multiplateforme par Yves Biton

Accueil Tutoriels Export html et LateX

Utiliser MathGraph32 en ligne et interagir avec la figure : Exemple 2 d’utilisation de l’API

modification lundi 20 août 2023.


Avant de lire cet article, vous devez être familier avec le lancement d’une figure MathGraph32 avec son loader. Si vous ne l’êtes pas il est conseillé de consulter cet article.

Il est conseillé aussi de commencer par cet article.

Nous allons partir de la figure ci-dessous et agir sur cette figure avec le player MathGraph32 en utilisant l’API de MathGraph32.

L’API de MathGraph32 permet d’afficher une figure en ligne (player ou éditeur), de lui rajouter des objets et d’ajouter des écouteurs d’événements sur des objets de la figure.


Et voici ci-dessous le code HTML d’une page contenant la figure et le code qui va nous permettre d’agir sur cette page le code est documenté pour vous aider à comprendre comment cela fonctionne).

Cliquez sur le texte sur fond jaune. Comme un écouteur d’événement a été associé à cet objet, cela lance la fonction addPtsAndPoly qui :

En cliquant à l’intérieur de la surface puis en faisant glisser la souris, on translate ainsi le polygone (e qui n’est pas un comportement natif de MathGraph32).

<!DOCTYPE html>
<html>
<head>
   <meta http-equiv="X-UA-Compatible" content="IE=9">
   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
   <meta name="generator" content="MathGraph32"/>
   <title>Feuille de travail dynamique MathGraph32</title>
   <style type="text/css">
       svg {
           -webkit-user-select: none; /* Chrome all / Safari all */
           -moz-user-select: none; /* Firefox all */
           -ms-user-select: none; /* IE 10+ */
           -webkit-tap-highlight-color: transparent; /*Nécessaire sur ipad pour ne pas avoir de rectangle gris sélectionné lors des touch events */
       }
   </style>

</head>
<body>
<div id="mtg32div">
</div>
<script type="text/javascript" src="https://www.mathgraph32.org/js/mtgLoad/mtgLoad.min.js"></script>
<script type="application/javascript">
 (function autostart() {
   const svgOptions = {
     idSvg: "mtg32svg", width: "900", height: "500"
   }

   const mtgOptions = {
     fig: "TWF0aEdyYXBoSmF2YTEuMAAAABM+TMzNAAJmcv###wEA#wEAAAAAAAAAAAUeAAACygAAAQEAAAAAAAAAAQAAADb#####AAAAAQAKQ0NhbGNDb25zdAD#####AAJwaQAWMy4xNDE1OTI2NTM1ODk3OTMyMzg0Nv####8AAAABAApDQ29uc3RhbnRlQAkh+1RELRj#####AAAAAQAKQ1BvaW50QmFzZQD#####AAAAAAAOAAFPAMAoAAAAAAAAAAAAAAAAAAAAAAUAAUB0sAAAAAAAQHGdcKPXCj7#####AAAAAQAUQ0Ryb2l0ZURpcmVjdGlvbkZpeGUA#####wEAAAAADgAAAQAAAAEAAAABAT#wAAAAAAAA#####wAAAAEAD0NQb2ludExpZURyb2l0ZQD#####AAAAAAEOAAFJAMAYAAAAAAAAAAAAAAAAAAAAAAUAAUBB3vnbItDmAAAAAv####8AAAABAAlDRHJvaXRlQUIA#####wAAAAAAEAAAAQAAAAEAAAABAAAAA#####8AAAABABZDRHJvaXRlUGVycGVuZGljdWxhaXJlAP####8AAAAAAA4AAAEAAAABAAAAAQAAAAT#####AAAAAQAJQ0NlcmNsZU9BAP####8BAAAAAAAAAQAAAAEAAAAD#####wAAAAEAEENJbnREcm9pdGVDZXJjbGUA#####wAAAAUAAAAG#####wAAAAEAEENQb2ludExpZUJpcG9pbnQA#####wEAAAAADgAAAQAABQABAAAABwAAAAkA#####wAAAAABDgABSgDAKAAAAAAAAMAQAAAAAAAAAAAFAAIAAAAH#####wAAAAIAB0NSZXBlcmUA#####wDm5uYAA3JlcAABAAAAAQAAAAMAAAAJAQEAAAAAAQAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAT#wAAAAAAAAAAAAAT#wAAAAAAAA#####wAAAAEACkNVbml0ZXhSZXAA#####wAEdW5pdAAAAAr#####AAAAAQALQ0hvbW90aGV0aWUA#####wAAAAH#####AAAAAQAKQ09wZXJhdGlvbgMAAAABP#AAAAAAAAD#####AAAAAQAPQ1Jlc3VsdGF0VmFsZXVyAAAAC#####8AAAABAAtDUG9pbnRJbWFnZQD#####AQAAAAAQAAJXIgEAAAEAAAAAAwAAAAz#####AAAAAQAJQ0xvbmd1ZXVyAP####8AAAABAAAADf####8AAAABAAdDQ2FsY3VsAP####8AB25iZ3JhZHgAAjIwAAAAAUA0AAAAAAAAAAAAEQD#####AAduYmdyYWR5AAIyMAAAAAFANAAAAAAAAP####8AAAABABRDSW1wbGVtZW50YXRpb25Qcm90bwD#####ABRHcmFkdWF0aW9uQXhlc1JlcGVyZQAAABsAAAAIAAAAAwAAAAoAAAAPAAAAEP####8AAAABABNDQWJzY2lzc2VPcmlnaW5lUmVwAAAAABEABWFic29yAAAACv####8AAAABABNDT3Jkb25uZWVPcmlnaW5lUmVwAAAAABEABW9yZG9yAAAACgAAAAsAAAAAEQAGdW5pdGV4AAAACv####8AAAABAApDVW5pdGV5UmVwAAAAABEABnVuaXRleQAAAAr#####AAAAAQAQQ1BvaW50RGFuc1JlcGVyZQAAAAARAAAAAAAOAAABAAAFAAAAAAoAAAAOAAAAEgAAAA4AAAATAAAAFgAAAAARAAAAAAAOAAABAAAFAAAAAAoAAAANAAAAAA4AAAASAAAADgAAABQAAAAOAAAAEwAAABYAAAAAEQAAAAAADgAAAQAABQAAAAAKAAAADgAAABIAAAANAAAAAA4AAAATAAAADgAAABUAAAAMAAAAABEAAAAWAAAADgAAAA8AAAAPAAAAABEAAAAAAA4AAAEAAAUAAAAAFwAAABkAAAAMAAAAABEAAAAWAAAADgAAABAAAAAPAAAAABEAAAAAAA4AAAEAAAUAAAAAGAAAABv#####AAAAAQAIQ1NlZ21lbnQAAAAAEQEAAAAAEAAAAQAAAAEAAAAXAAAAGgAAABcAAAAAEQEAAAAAEAAAAQAAAAEAAAAYAAAAHAAAAAQAAAAAEQEAAAAACwABVwDAFAAAAAAAAMA0AAAAAAAAAAAFAAE#3FZ4mrzfDgAAAB3#####AAAAAgAIQ01lc3VyZVgAAAAAEQAGeENvb3JkAAAACgAAAB8AAAARAAAAABEABWFic3cxAAZ4Q29vcmQAAAAOAAAAIP####8AAAACABJDTGlldU9iamV0UGFyUHRMaWUBAAAAEQBmZmYAAAAAAB8AAAAOAAAADwAAAB8AAAACAAAAHwAAAB8AAAARAAAAABEABWFic3cyAA0yKmFic29yLWFic3cxAAAADQEAAAANAgAAAAFAAAAAAAAAAAAAAA4AAAASAAAADgAAACEAAAAWAAAAABEBAAAAAAsAAAEAAAUAAAAACgAAAA4AAAAjAAAADgAAABMAAAAZAQAAABEAZmZmAAAAAAAkAAAADgAAAA8AAAAfAAAABQAAAB8AAAAgAAAAIQAAACMAAAAkAAAABAAAAAARAQAAAAALAAFSAEAgAAAAAAAAwCAAAAAAAAAAAAUAAT#RG06BtOgfAAAAHv####8AAAACAAhDTWVzdXJlWQAAAAARAAZ5Q29vcmQAAAAKAAAAJgAAABEAAAAAEQAFb3JkcjEABnlDb29yZAAAAA4AAAAnAAAAGQEAAAARAGZmZgAAAAAAJgAAAA4AAAAQAAAAJgAAAAIAAAAmAAAAJgAAABEAAAAAEQAFb3JkcjIADTIqb3Jkb3Itb3JkcjEAAAANAQAAAA0CAAAAAUAAAAAAAAAAAAAADgAAABMAAAAOAAAAKAAAABYAAAAAEQEAAAAACwAAAQAABQAAAAAKAAAADgAAABIAAAAOAAAAKgAAABkBAAAAEQBmZmYAAAAAACsAAAAOAAAAEAAAACYAAAAFAAAAJgAAACcAAAAoAAAAKgAAACv#####AAAAAgAMQ0NvbW1lbnRhaXJlAAAAABEBZmZmAAAAAAAAAAAAQBgAAAAAAAAAAAAAAB8LAAH###8AAAABAAAAAAAAAAEAAAAAAAAAAAALI1ZhbChhYnN3MSkAAAAZAQAAABEAZmZmAAAAAAAtAAAADgAAAA8AAAAfAAAABAAAAB8AAAAgAAAAIQAAAC0AAAAbAAAAABEBZmZmAAAAAAAAAAAAQBgAAAAAAAAAAAAAACQLAAH###8AAAABAAAAAAAAAAEAAAAAAAAAAAALI1ZhbChhYnN3MikAAAAZAQAAABEAZmZmAAAAAAAvAAAADgAAAA8AAAAfAAAABgAAAB8AAAAgAAAAIQAAACMAAAAkAAAALwAAABsAAAAAEQFmZmYAwCAAAAAAAAA#8AAAAAAAAAAAAAAAJgsAAf###wAAAAIAAAABAAAAAQAAAAAAAAAAAAsjVmFsKG9yZHIxKQAAABkBAAAAEQBmZmYAAAAAADEAAAAOAAAAEAAAACYAAAAEAAAAJgAAACcAAAAoAAAAMQAAABsAAAAAEQFmZmYAwBwAAAAAAAAAAAAAAAAAAAAAAAAAKwsAAf###wAAAAIAAAABAAAAAQAAAAAAAAAAAAsjVmFsKG9yZHIyKQAAABkBAAAAEQBmZmYAAAAAADMAAAAOAAAAEAAAACYAAAAGAAAAJgAAACcAAAAoAAAAKgAAACsAAAAz#####wAAAAEADUNQb2ludEJhc2VFbnQA#####wAAAAAAEAABTQAAAAAAAAAAAEAIAAAAAAAAAAABAAEAAAAKQBAAAAAAAABACAAAAAAAAAEBAAAADv##########",
     isEditable: true,
     loadApi: true
   }
   let app
   let polyCaptured = false
   let xmouse, ymouse // Coordonnées du pointeur souris
   mtgLoad("mtg32div", svgOptions, mtgOptions).then(
     (mtgApp) => {
       console.log("mtg32 App loaded", mtgApp)
       app = mtgApp
       addBoutonAndLatex()
     }
   ).catch(
     (error) => {
       if (error) console.error(error)
     }
   )

   function addBoutonAndLatex() {
     app.setApiDoc('mtg32svg') // Il faut informer l'API de la figure sur laquelle on travaille
     // On crée un affichage de texte. La propriété rep (tag du repère étant donnée) sa position sera donnée
     // dans le repère par x et y (sinon ce serait des coordonnées absolues dans le svg)
     app.addText({
       text: 'Créer les points et le polygone',
       rep: 'rep',
       x: -8,
       y: 7,
       transparent: false,
       backgroundColor: 'yellow',
       tag: 'txt1',
       border: '3D'
     })
     // On affecte à l'élément svg représentant ce texte un écouteur d'événement mousedone
     // qui appelle la fonction addPtsAndPoly
     app.addEltListener({
       elt: 'txt1',
       eventName: 'mousedown',
       callBack: addPtsAndPoly
     })
     // On crée les mesures des abscisses et ordonnées du point M
     app.addXMeasure({ a: 'M', rep: 'rep', nameCalc: 'xM'})
     app.addYMeasure({ a: 'M', rep: 'rep', nameCalc: 'yM'})
     // On crée un calcul ayant comme formule xM^2+yM^2
     app.addCalc({ nameCalc: 'long2', formula: 'xM^2+yM^2'})
     // On crée un affichage LaTeX dynamique affichant la valeur de long2 sous une racine carée au-dessus de M
     app.addLinkedLatex({latex: 'OM=\\sqrt{\\Val{long2}}', a: 'M', color: 'maroon', transparent: false, vAlign: 'top', hAlign: 'center', offsetY: -10 })
   }

   function addPtsAndPoly() {
     app.setApiDoc('mtg32svg') // Il faut informer l'API de la figure sur laquelle on travaille
     // On retire le listener sur l'affcihage de texte car un deuxième appel provoquerait des erreurs
     app.removeEltListener({elt: 'txt1', eventName: 'mousedown'})
     app.addText({
       text: 'On peut capturer le polygone ou les points A, B, C et M',
       rep: 'rep',
       transparent: false,
       x: 0,
       y: 7,
       color: 'maroon'
     })
     // On ajoute des points libres. La propriété rep étant donnée (tag du repère)
     // la position initiale des points libres est donnée relativement au repère (x et y)
     // sinon les coordonnées x et y seraient relatives au svg
     app.addFreePoint({rep: 'rep', x: -4, y: -2, pointStyle: 'bigmult', name: 'A', color: 'blue', tag: 'A'})
     app.addFreePoint({rep: 'rep', x: -4, y: 2, pointStyle: 'bigmult', name: 'B', color: 'blue', tag: 'B'})
     // La ligne suivante montre qu'on peut décaler le nom d'un point après sa création
     app.setPointNameOffset({a: 'B', offsetX: -16, offsetY: -20})
     app.addFreePoint({rep: 'rep', x: -1, y: 3, pointStyle: 'bigmult', name: 'C', color: 'blue', tag: 'C'})
     // On crée un polygne passant par ces trois points et on lui affecte le tag polyABC
     app.addPolygon({points: ['A', 'B', 'C'], color: 'red', thickness: 2, lineStyle: '--', tag: 'polyABC'})
     // Ajout d'une marque d'angle non orienté
     app.addAngleMark({ a: 'B', o: 'A', b: 'C', r: 30, thickness: 2, color: 'blue'})
     // Ajout s'une surface à l'intérieur de polygone à laquelle on affecte le tag surfpolyABC
     app.addSurfacePoly({poly: 'polyABC', color: 'purple', tag: 'surfpolyABC'})
     // On affecte à cette surface un écouteur d'événement mousedown
     app.addEltListener({
       elt: 'surfpolyABC', eventName: 'mousedown', callBack: capturePoly
     })
     app.addEltListener({
       elt: 'surfpolyABC', eventName: 'touchstart', callBack: capturePoly
     })
     // Pour la version appli il faut gérer l'événement mouseup sur la surface
     app.addEltListener({
       elt: 'surfpolyABC', eventName: 'mouseup', callBack: mouseUp
     })
     app.addEltListener({
       elt: 'surfpolyABC', eventName: 'touchend', callBack: mouseUp
     })
     app.addEltListener({
       elt: 'surfpolyABC', eventName: 'mousemove', callBack: mouseMove
     })
     app.addEltListener({
       elt: 'surfpolyABC', eventName: 'touchmove', callBack: mouseMove
     })
     // On affecte au svg global de la figure des écouteurs d'événements mousemove et mouseup
     app.addSvgListener({eventName: 'mousemove', callBack: mouseMove})
     app.addSvgListener({eventName: 'touchmove', callBack: mouseMove})
     app.addSvgListener({eventName: 'mouseup', callBack: mouseUp})
     app.addSvgListener({eventName: 'touchend', callBack: mouseUp})
   }

   // Les paramètres x et y de cette fonction de callBack sont les coordonnées du point où l'événement a eu lieu, relatives au svg de la figure
   function capturePoly(ev, x, y) {
     polyCaptured = true
     // On récupère dans x et y les coordonnées du pointeur au clic
     xmouse = x
     ymouse = y
   }
   /**
    * Cette fonction est appelée quand on bouge la souris sur le svg de la figure.
    * @param ev l'événement qui a lancé la foonction de callBack
    * @param x la première coordonnée de l'endroit où l'événement a été lancé relativement au svg de la figure
    * @param y la deuxième coordonnée de l'endroit où l'événement a été lancé relativement au svg de la figure
    */
   function mouseMove(ev, x, y) {
     if (polyCaptured) {
       document.body.style.cursor = 'pointer'
       const newx = x
       const newy = y
       // On calcule l'écart entre l'ancienne position du pointeur souris mémorisée dans x et y et la nouvelle
       const deltax = newx - xmouse
       const deltay = newy - ymouse
       // On actualise les coordonnées mémorisées du pointeur souris
       xmouse = newx
       ymouse = newy
       // On change la couleur de la surface associée au polygone
       app.setColor({ elt: 'surfpolyABC', color: 'green'})
       let pos = app.getPointPosition({a: 'A'})
       // pos contient les coordonnées actuelles du point libre A
       // On décale la position de ce point libre de (deltax, deltay)
       app.setFreePointPosition({a: 'A', x: pos.x + deltax, y: pos.y + deltay})
       // Idem pour les points B et C
       pos = app.getPointPosition({a: 'B'})
       app.setFreePointPosition({a: 'B', x: pos.x + deltax, y: pos.y + deltay})
       pos = app.getPointPosition({a: 'C'})
       app.setFreePointPosition({a: 'C', x: pos.x + deltax, y: pos.y + deltay})
       app.updateFigDisplay()
     }
   }

   /**
    * Fonction appelée quand le bouton de la souris est relaché au-dessus de svg de la figure
    * @param ev l'événement qui a lancé la foonction de callBack
    * @param x la première coordonnée de l'endroit où l'événement a été lancé relativement au svg de la figure
    * @param y la deuxième coordonnée de l'endroit où l'événement a été lancé relativement au svg de la figure
    */
   function mouseUp() {
     if (polyCaptured) {
       document.body.style.cursor = 'default'
       polyCaptured = false
       // On remet la couleur initiale sur la surface associée au polygone
       app.setColor({elt: 'surfpolyABC', color: 'purple'})
     }
   }
 })()
</script>
</body>
</html>

Le comportement de cette page est reconstitué ci-dessous :

En remplaçant le isEditable = false par isEditable = true, et en supprimant les appels à setApiDoc (qui n’a de sens que pour le player) c’est l’éditeur de MathGraph32 qui est disponible comme ci-dessous :

Quelques remarques sur le code JavaScript utilisé :

Dans l’objet mtgOptions passé en paramètre à la fonction mtgLoad, la propriété addApi est à true, ce qui rajoute à l’application MathGraph32 de nouvelles fonctions permettant de rajouter des objets à la figure et d’associer des écouteurs d’événements à certians d’entre-eux.

Quand on crée une figure dans MathGraph32 avec un repère, à partir de la version 7.6 ; ce repère a automatiquement un tag rep qui lui est attribué.

Cela permet d’utiliser ce repère dans certaines fonctions.

Par exemple la ligne de code app.addFreePoint({rep: 'rep', x: -4, y: -2, pointStyle: 'bigmult', name: 'A', color: 'blue', tag: 'A'}) rajoute un point libre nommé A dont les coordonnées initiales sont x et y. Comme la propriété rep est présente et vaut rep (qui est le tag du repère de la figure), la position initiale est comprise relativement à ce repère. En l’absence de la propriété rep, les coordonnées x et y seraient considérées comme relatives au svg de la figure.

La ligne de code app.addAngleMark({ a: 'B', o: 'A', b: 'C', r: 30, thickness: 2, color: 'blue'}) permet de rajouter à la figure une marque d’angle pour l’angle BAC. r fournit son rayon en pixels.

Les fonctions de callBack associées aux écouteurs d’événements prennent 3 paramètres (dans cet ordre :

Nous gérons ici aussi bien les événements mouse que touch et donc nos figures dynamiques marchent aussi sur tablette ou smartphone.

Vous trouverez une documentation complète sur l’API de MathGraph32 (en Anglais) sur cette page.