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

Accueil Tutoriels l’API de MathGraph32

Programmer une figure en Python : simuler un billard dans un polygone

modification mercredi 2 février 2025.


Depuis la version 7.9.1, MathGraph32 permet de créer des objets dans une figure en langage Python.

La figure ci-dessous crée cinq points libres A, B, C , D et E fournissant un polygone 5 côtés puis appelle de façon itérative une fonction intSeg.

Si vous changez le code Python ci-dessous, cliquez sur le bouton Exécuter en dessous de ce cadre d’édition.

Vous trouverez en bas de cette page le code HTML ayant permis de la mettre en ligne.

Pour fonctionner correctement, cette figure a besoin d’un repère qui est fourni par le paramètre fig de la balise (code Base 64).

Pour obtenir la suite du trajet des rebonds, cliquez sur les boutons + et - en bas et à droite qui font incrémenter ou décrémenter la variable p (valeur maxi de p : 50).

A noter que la figure obtenue est dynamique : vous pouvez capturer les points A, B, C, D, E M et N.

Les points A, B, C et D sont des points libres dont les coordonnées initiales dans le repère sont données lignes 62 à 65.

Si vous voulez que ces points soient fixes, remplacez ces quatre lignes par les lignes suivantes (en modifiant les coordonnées à votre guise) :

A = addPointXY(-10, 8, 'A')
B = addPointXY(10, 8, 'B')
C = addPointXY(10, -8, 'C')
D = addPointXY(0, -2, 'D')
E = addPointXY(-10, -8, 'E')

Notez que, une fois le code Python exécuté, cliquer sur le bouton Télécharger la figure vous permet d’enregistrer la figure MathGraph32 que vous pourrez ensuite ouvrir avec le logiciel.

En utilisant l’outil Protocole vous verrez que cette figure est formée de plus de 3000 objets dont certains sont des matrices. Pourtant cette figure reste fluide !

Voici le code html qui a permis de mettre cette figure en ligne :

<script type = "text/mtgPython" id = "PythonCodeId">
# Fonction recherchant l'intersection de da demi-droite passant par pt et ayant un vecteur directeur de coordonnées (a, b) avec le côtés du polygone
# a et b sont deux calculs réels de la figure avec les quatre segments AB, BC, CD et DA et renvoyant un point confondu
# avec celui des quatre points d'intersection avec les quatre segments qui existe
def intSeg(pt, a, b, ind):
    # On calcule les coordonnées de pt
    addXMeasure(pt, f'xpt{ind}')
    addYMeasure(pt, f'ypt{ind}')
    trans = addTranslationxy(a, b)
    ptim = addPointIm(pt, trans)
    setHidden(ptim)
    ray = addRay(pt, ptim)
    setHidden(ray)
    size = len(tabPt) - 1 # -1 car in a bouclé le tabelau en rajoutant le point de départ
    # Pour savoir si pt est sur un des segments bords du polygone on crée des mesures d'abscisses de pt par rapport aux extrémités des segments
    # et des tests d'existence de ces mesures
    tabMesAbs = []
    tabTestMesAbs = []
    tabTestAbs = []
    for i in range(size):
        absMes = addAbsMeasure(pt, tabPt[i], tabPt[i + 1], f'mesAbs{ind}{i}')
        tabMesAbs.append(absMes)
        tabTestMesAbs.append(addTest(absMes, f'testMesAbs{ind}{i}'))
        # Les test ci-dessous vaudront 1 si l'abscisse existe et est entre 0 et 1 et 0 sinon
        tabTestAbs.append(addCalc(f'testAbs{ind}{i}', f'si(testMesAbs{ind}{i},mesAbs{ind}{i}>=0&mesAbs{ind}{i}<=1,0)'))
    # On crée un tableau formé des points d'intersection de la demi-droite ray avec chacun des segments du polygone
    tabInt = []
    for i in range(size):
        point = addIntLineLine(ray, tabSeg[i])
        tabInt.append(point)
        setHidden(point)
    # On crée un tableau formé des abscisses des points d'intersections
    tabAbs = []
    for i in range(size):
        tabAbs.append(addXMeasure(tabInt[i],f'xint{ind}{i}'))
    # On crée un tableau formé des ordonnées des points d'intersections
    tabOrd = []
    for i in range(size):
        tabOrd.append(addYMeasure(tabInt[i],f'yint{ind}{i}'))
    # On crée un tableau formé des tests d'existence des abscisses pour savoir si les points existent
    tabTest = []
    for i in range(size):
        tabTest.append(addTest(tabAbs[i], f'test{ind}{i}'))
    # On va créer une matrice à qautre colonnes dont la première colonne contiendra le carré des distances entre pt
    # et les points d'intersection de la demi-droite ray avec les côtés du polygone que ray rencontre à condition que pt ne soit pas sur ces segments
    # les troisièmes et quatrièmes colonnes contenant les coordonnées de la première extrémité du segment correspondant
    mat = []
    matvalid = []
    for i in range(size):
        matvalid.append(f'test{ind}{i}&(1-testAbs{ind}{i})')
        lig = [f'si(matvalid{ind}(1,{i}+1),(xint{ind}{i}-xpt{ind})^2+(yint{ind}{i}-ypt{ind})^2,10^99)',  f'si(matvalid{ind}(1,{i}+1),xint{ind}{i},0)', f'si(matvalid{ind}(1,{i}+1),yint{ind}{i},0)', f'matAbs(1,{i} + 1)', f'matOrd(1,{i} + 1)']
        mat.append(lig)
    # La matrice V est une matrice ligne de par exemple 5 éléments si le polygone a 5 côtés
    # Chacune de ses cellules conteint 1 si la demidroite rencontre le segment si si le point pt n'est pas sur ce segment
    V = addMatrix(f'matvalid{ind}', [matvalid])
    # Le calcul suivant contientdra la formule 1/0 si aucun point de rencontre entre la demi-droite et les segments n'est valide et 1 sinon
    addCalc(f'valid{ind}', f'si(somme(matvalid{ind}(1,k), k, 1, {size}, 1)>0,1, 1/0)')
    A = addMatrix(f'A{ind}',mat)
    # La matrice B va contenir les lignes de la matrice A triés par sa première colonne en ordre croissant
    B = addCalcMat(f'B{ind}', f'sortbycol(A{ind}, 1)')
    xptret = addCalc(f'xptret{ind}', f'B{ind}(1,2)*valid{ind}') # Ce calcul n'existera pas si aucun point de rencontre entre la demi-droite et les segments n'est valide
    yptret = addCalc(f'yptret{ind}', f'B{ind}(1,3)')
    ptret = addPointXY(xptret, yptret) # Le poijnt de rencontre entre la demi-droite et le segment le plus proche
    addVector(pt,ptret, 'red')
    extrem = addPointXY(f'B{ind}(1,4)', f'B{ind}(1,5)') #extrem est un point confondu avec une des extrémités du segment que la demi droite [pt, N) rencontre]
    setHidden(extrem)
    seg = addSegment(extrem, ptret) # segment partant du point d'intersection et le joignant à une des extémités du segment que la demi-droite rencontre
    setHidden(seg)
    perp = addLinePerp(ptret, seg) #perepndiculaire au segment rencontré au point de rencontre
    setHidden(perp)
    ptsym = addImPointSymAx(pt, perp) #Symétrique du point pt par rapport à la perpendiculaire
    setHidden(ptsym)
    xptsym = addXMeasure(ptsym, f'xptsym{ind}')
    yptsym = addYMeasure(ptsym, f'yptsym{ind}')
    xret = addCalc(f'xret{ind}', f'xptsym{ind}-xptret{ind}')
    yret = addCalc(f'yret{ind}', f'yptsym{ind}-yptret{ind}')
    # On renvoie le point de rencontre avec un des quatre segments et les coordonnés d'un vecteur joigant le point de rencontre au symétrique
    return [ptret, xret, yret]
    
p = addVariable('p', 12, 0, 50, 1, True)
O = getPointByName('O') # L'origine du repère
A = addFreePoint(-10, 8, 'A')
B = addFreePoint(10, 8, 'B')
C = addFreePoint(10, -8, 'C')
D = addFreePoint(0, -2, 'D')
E = addFreePoint(-10, -8, 'E')
setPointNameOffset(A, -15, - 20)
setPointNameOffset(B, 5, - 20)
tabPt = [A, B, C, D, E]
# On referme le tableau de points
tabPt += [A]
tabSeg = []
tabAbs = []
tabOrd = []
for i in range(len(tabPt) - 1):
    pt1 = tabPt[i]
    pt2 = tabPt[i + 1]
    tabAbs.append(addXMeasure(pt1, f'x{i}'))
    tabOrd.append(addYMeasure(pt1, f'y{i}'))
    tabSeg.append(addSegment(pt1, pt2))
# On crée deux matrices contenant les abscisses pour l'une et les ordonnées pour l'autre
addMatrix('matAbs', [tabAbs])
addMatrix('matOrd', [tabOrd])
pol = addPolygon(tabPt)
surf = addSurface(pol, 'green')
setHidden(pol)
M = addFreePoint(-7, -2, 'M', 'blue', 'X')
N = addFreePoint(-5, 4, 'N')
xM = addXMeasure(M, 'xM')
yM = addYMeasure(M, 'yM')
xN = addXMeasure(N, 'xN')
yN = addYMeasure(N, 'yN')
a = addCalc('a', 'xN-xM')
b = addCalc('b', 'yN-yM') # Le vecteur de coordonnées (a; b) donne la direction de la demi-droite [M,N)
point = M
x = a
y = b
for i in range(50):
    # On crée un point image par l'homthétie de centre O et rapport 1/(i<=p). Quand i > p , le test (i<=p) renvoie comme valeur 0
    # et le quotient 1/'i<=p) n'existe pas. Ansi le point et les suivants n'existeront pas pour i > p
    # On changer la valeur de p en cliquant sur les boutons + et - associs à la variable p en bas et à droite.
    pointIm = addImPointDilation(point, O, f'1/({i}<=p)', f'N{i}')
    setHidden(pointIm)
    [point, x, y] = intSeg(pointIm, x, y, i)
</script>
<script async src="https://www.mathgraph32.org/js/mtgLoad/mathgraphElements.js"></script>
<mathgraph-player width="800" height="600" python-code-id="PythonCodeId" fig = "TWF0aEdyYXBoSmF2YTEuMAAAABUAAmZy####AQD#AQAAAAAAAAAABQUAAALHAAABAQAAAAAAAAABAAAANf####8AAAABAApDQ2FsY0NvbnN0AP####8AAnBpABYzLjE0MTU5MjY1MzU4OTc5MzIzODQ2#####wAAAAEACkNDb25zdGFudGVACSH7VEQtGP####8AAAABAApDUG9pbnRCYXNlAP####8AAAAAP4AAAAAOAAFPAMAoAAAAAAAAAAAAAAAAAAAAAAUAAUB5AAAAAAAAQHLKPXCj1wr#####AAAAAQAUQ0Ryb2l0ZURpcmVjdGlvbkZpeGUA#####wEAAAA#gAAAAA4AAAEAAAABAAAAAQE#8AAAAAAAAP####8AAAABAA9DUG9pbnRMaWVEcm9pdGUA#####wAAAAA#gAAAAQ4AAUkAwBgAAAAAAAAAAAAAAAAAAAAABQABQD0AAAAAAAAAAAAC#####wAAAAEACUNEcm9pdGVBQgD#####AAAAAD+AAAAAEAAAAQAAAAEAAAABAAAAA#####8AAAABABZDRHJvaXRlUGVycGVuZGljdWxhaXJlAP####8AAAAAP4AAAAAOAAABAAAAAQAAAAEAAAAE#####wAAAAEACUNDZXJjbGVPQQD#####AQAAAD+AAAAAAAABAAAAAQAAAAP#####AAAAAQAQQ0ludERyb2l0ZUNlcmNsZQD#####AAAABQAAAAb#####AAAAAQAQQ1BvaW50TGllQmlwb2ludAD#####AQAAAD+AAAAADgAAAQAABQABAAAABwAAAAkA#####wAAAAA#gAAAAQ4AAUoAwCgAAAAAAADAEAAAAAAAAAAABQACAAAAB#####8AAAACAAdDUmVwZXJlAP####8A5ubmP4AAAAADcmVwAAEAAAABAAAAAwAAAAkBAQAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAAAAAABP#AAAAAAAAAAAAABP#AAAAAAAAD#####AAAAAQAKQ1VuaXRleFJlcAD#####AAR1bml0AAAACv####8AAAABAAtDSG9tb3RoZXRpZQD#####AAAAAf####8AAAABAApDT3BlcmF0aW9uAwAAAAE#8AAAAAAAAP####8AAAABAA9DUmVzdWx0YXRWYWxldXIAAAAL#####wAAAAEAC0NQb2ludEltYWdlAP####8BAAAAP4AAAAAQAAJXIgEAAAEAAAAAAwAAAAz#####AAAAAQAJQ0xvbmd1ZXVyAP####8AAAABAAAADf####8AAAABAAdDQ2FsY3VsAP####8AB25iZ3JhZHgAAjIwAAAAAUA0AAAAAAAAAAAAEQD#####AAduYmdyYWR5AAIyMAAAAAFANAAAAAAAAP####8AAAABABRDSW1wbGVtZW50YXRpb25Qcm90bwD#####ABRHcmFkdWF0aW9uQXhlc1JlcGVyZQAAABsAAAAIAAAAAwAAAAoAAAAPAAAAEP####8AAAABABNDQWJzY2lzc2VPcmlnaW5lUmVwAAAAABEABWFic29yAAAACv####8AAAABABNDT3Jkb25uZWVPcmlnaW5lUmVwAAAAABEABW9yZG9yAAAACgAAAAsAAAAAEQAGdW5pdGV4AAAACv####8AAAABAApDVW5pdGV5UmVwAAAAABEABnVuaXRleQAAAAr#####AAAAAQAQQ1BvaW50RGFuc1JlcGVyZQAAAAARAAAAAD+AAAAADgAAAQAABQAAAAAKAAAADgAAABIAAAAOAAAAEwAAABYAAAAAEQAAAAA#gAAAAA4AAAEAAAUAAAAACgAAAA0AAAAADgAAABIAAAAOAAAAFAAAAA4AAAATAAAAFgAAAAARAAAAAD+AAAAADgAAAQAABQAAAAAKAAAADgAAABIAAAANAAAAAA4AAAATAAAADgAAABUAAAAMAAAAABEAAAAWAAAADgAAAA8AAAAPAAAAABEAAAAAP4AAAAAOAAABAAAFAAAAABcAAAAZAAAADAAAAAARAAAAFgAAAA4AAAAQAAAADwAAAAARAAAAAD+AAAAADgAAAQAABQAAAAAYAAAAG#####8AAAABAAhDU2VnbWVudAAAAAARAQAAAD+AAAAAEAAAAQAAAAEAAAAXAAAAGgAAABcAAAAAEQEAAAA#gAAAABAAAAEAAAABAAAAGAAAABwAAAAEAAAAABEBAAAAP4AAAAALAAFXAMAUAAAAAAAAwDQAAAAAAAAAAAUAAT#cVniavN8OAAAAHf####8AAAACAAhDTWVzdXJlWAAAAAARAAZ4Q29vcmQAAAAKAAAAHwAAABEAAAAAEQAFYWJzdzEABnhDb29yZAAAAA4AAAAg#####wAAAAIAEkNMaWV1T2JqZXRQYXJQdExpZQEAAAARAGZmZj+AAAAAAAAAAB8AAAAOAAAADwAAAB8AAAACAAAAHwAAAB8AAAARAAAAABEABWFic3cyAA0yKmFic29yLWFic3cxAAAADQEAAAANAgAAAAFAAAAAAAAAAAAAAA4AAAASAAAADgAAACEAAAAWAAAAABEBAAAAP4AAAAALAAABAAAFAAAAAAoAAAAOAAAAIwAAAA4AAAATAAAAGQEAAAARAGZmZj+AAAAAAAAAACQAAAAOAAAADwAAAB8AAAAFAAAAHwAAACAAAAAhAAAAIwAAACQAAAAEAAAAABEBAAAAP4AAAAALAAFSAEAgAAAAAAAAwCAAAAAAAAAAAAUAAT#RG06BtOgfAAAAHv####8AAAACAAhDTWVzdXJlWQAAAAARAAZ5Q29vcmQAAAAKAAAAJgAAABEAAAAAEQAFb3JkcjEABnlDb29yZAAAAA4AAAAnAAAAGQEAAAARAGZmZj+AAAAAAAAAACYAAAAOAAAAEAAAACYAAAACAAAAJgAAACYAAAARAAAAABEABW9yZHIyAA0yKm9yZG9yLW9yZHIxAAAADQEAAAANAgAAAAFAAAAAAAAAAAAAAA4AAAATAAAADgAAACgAAAAWAAAAABEBAAAAP4AAAAALAAABAAAFAAAAAAoAAAAOAAAAEgAAAA4AAAAqAAAAGQEAAAARAGZmZj+AAAAAAAAAACsAAAAOAAAAEAAAACYAAAAFAAAAJgAAACcAAAAoAAAAKgAAACv#####AAAAAgAMQ0NvbW1lbnRhaXJlAAAAABEBZmZmP4AAAAAAAAAAAAAAAEAYAAAAAAAAAAAAAAAfCwAB####AAAAAQAAAAAAAAABAAAAAAAAAAAAAAsjVmFsKGFic3cxKQAAABkBAAAAEQBmZmY#gAAAAAAAAAAtAAAADgAAAA8AAAAfAAAABAAAAB8AAAAgAAAAIQAAAC0AAAAbAAAAABEBZmZmP4AAAAAAAAAAAAAAAEAYAAAAAAAAAAAAAAAkCwAB####AAAAAQAAAAAAAAABAAAAAAAAAAAAAAsjVmFsKGFic3cyKQAAABkBAAAAEQBmZmY#gAAAAAAAAAAvAAAADgAAAA8AAAAfAAAABgAAAB8AAAAgAAAAIQAAACMAAAAkAAAALwAAABsAAAAAEQFmZmY#gAAAAMAgAAAAAAAAP#AAAAAAAAAAAAAAACYLAAH###8AAAACAAAAAQAAAAEAAAAAAAAAAAAACyNWYWwob3JkcjEpAAAAGQEAAAARAGZmZj+AAAAAAAAAADEAAAAOAAAAEAAAACYAAAAEAAAAJgAAACcAAAAoAAAAMQAAABsAAAAAEQFmZmY#gAAAAMAcAAAAAAAAAAAAAAAAAAAAAAAAACsLAAH###8AAAACAAAAAQAAAAEAAAAAAAAAAAAACyNWYWwob3JkcjIpAAAAGQEAAAARAGZmZj+AAAAAAAAAADMAAAAOAAAAEAAAACYAAAAGAAAAJgAAACcAAAAoAAAAKgAAACsAAAAzAAAADv##########" >
</mathgraph-player>