Changer de coordonnées dans un canvas

Changement de repère


L'élément canvas de html5 est une zone délimitée de pixels manipulables. Pour ce faire, il faut avant toute chose être capable de situer un ou des pixels dans cette zone.
Par défaut les coordonnées des pixels dans l'élément canvas ont comme origine le coin supérieur gauche de l'élément, et la direction des axes respecte le sens de lecture naturel: de la gauche vers la droite pour 1ère coordonnée (abscisse) et de haut en bas pour la 2ème coordonnée (ordonnée):


Ce repère n'est pas forcément le plus naturel à utiliser: on peut être plus habituer à compter positivement en ordonnée vers le haut, à avoir et utiliser des symétries, donc aussi par exemple des coordonnées négatives, à avoir l'origine de notre repère au centre, … c'est-à-dire à utiliser plutôt ce type de repère et de coordonnées:

ou encore, par exemple et suivant les valeurs minimales et maximales en abscisse et ordonnées, l'origine du repère est dans la zone:

\begin{pspicture}(-1,-1)(10,6)
\pspolygon(0,0)(8,0)(8,5)(0,5)
\psline[linewidth=3pt](0,1.75)(0,2.25)\rput(0,2.4){\large$x_\text{min}$}
\psline[linewidth=3pt](8,1.75)(8,2.25)\rput(8,2.4){\large$x_\text{max}$}
\psline[linewidth=3pt](2.75,0)(3.25,0)\rput(2.4,0.15){\large$y_\text{min}$}
\psline[linewidth=3pt](2.75,5)(3.25,5)\rput(2.4,4.85){\large$y_\text{max}$}
\psline[linewidth=2pt,linecolor=blue,arrowsize=8pt]{->}(2.7,2.2)(2.7,3)
\rput(2.4,2.4){\large\blue y}
\psline[linewidth=2pt,linecolor=blue,arrowsize=8pt]{->}(3.2,1.6)(5,1.6)
\rput(3.8,1.3){\large\blue x}
\psline[linestyle=dashed](3,0)(3,5)\psline[linestyle=dashed](0,2)(8,2)
\psline[linecolor=blue,linewidth=2.5pt](2.77,2)(3.25,2)
\psline[linecolor=blue,linewidth=2.5pt](3.01,1.75)(3.01,2.25)
\rput(2.5,1.6){\large\blue$\left( 0;0\rp$}
\psline[linewidth=2pt,linecolor=red,linestyle=dotted](5,2)(5,3)(3,3)
\rput(5.2,3.3){\large\red$\left( x;y\rp$}
\end{pspicture}


Dans la suite, les coordonnées "naturelles" de canvas sont notées en majuscules: X et Y, tandis que celles redéfinies sont notées en minuscules: x et y.

Les relations de passage d'un système de coordonnées à l'autre sont alors:
\la\begin{array}{ll}
x&=x_{min}+X\dfrac{x_{max}-x_{min}}{Width}\\[.6cm]
y&=y_{min}+\left( Height-Y\rp\dfrac{y_{max}-y_{min}}{Height}
\enar\right.


ou inversement:

\la\begin{array}{ll}
X&=Width\tm\dfrac{x-x_{min}}{x_{max}-x_{min}}\\[.6cm]
Y&=Height-Height\tm\dfrac{y-y_{min}}{y_{max}-y_{min}}
=Height\tm\dfrac{y_{max}-y}{y_{max}-y_{min}}\enar\right.

Dans le code python qui suit, quatre fonctions opèrent ces changements de coordonnées (en rouge dans le code ci-dessous, et respectant les notations précédentes, minuscules et majuscules). En prime, l'affichage des nouveaux axes et des valeurs minimales et maximales, ainsi que la gestion de la position de la souris lors d'un clic avec affichage des coordonnées de cette position, bien sûr dans les deux systèmes de coordonnées.
# -*- coding: utf-8 -*-
from tkinter import *
gui=Tk()

Width=300
Height=300
cnv=Canvas(gui,width=Width,height=Height,bg="white")
cnv.pack()

xmin=-10
xmax=10
ymin=-10
ymax=10

# Les quatres fonctions pour changer de coordoonées
def cnv2x(X): 
    return X*(xmax-xmin)/Width+xmin
def cnv2y(Y): 
    return (Height-Y)*(ymax-ymin)/Height+ymin
def x2cnv(x): 
    return Width*(x-xmin)/(xmax-xmin)
def y2cnv(y): 
    return Height-Height*(y-ymin)/(ymax-ymin)


# On dessine les nouveaux axes:
cnv.create_line(x2cnv(xmin),y2cnv(0),x2cnv(xmax),y2cnv(0))
cnv.create_line(x2cnv(0),y2cnv(ymin),x2cnv(0),y2cnv(ymax))


# On gère le click et l'affichage des coordonnées
# dans le repère par défaut de canvas et le nouveau
def paint( event ):
    global cr
    x=event.x
    y=event.y
    cnv.delete(cr)
    cr=cnv.create_oval(x-2,y-2,x+2,y+2,fill="red")
    ligne1="( "+str(x)+" , "+str(y)+")"
    ligne2="( "+str(int(cnv2x(x)*100)/100)+" , "+str(int(cnv2y(y)*100)/100)+" )"
    message['text']= ligne1+" \n "+ligne2

cr=cnv.create_oval(0,0,0,0)


# Lien (bind) entre click et la fonction d'affichage paint
cnv.bind( "", paint )

message = Label( gui, text = "Click to affich \n coordinates" )
message.pack( side = BOTTOM )

gui.mainloop()
pour obtenir:

Tracer de la courbe représentative d'une fonction

Par exemple pour la fonction carré, on trace la parabole dans un système de coordonnées "plus naturel" que celui proposé par défaut par canvas:
from tkinter import *
gui=Tk()

Width=500
Height=500
cnv=Canvas(gui,width=Width,height=Height,bg="white")
cnv.pack()

xmin=-10
xmax=10
ymin=-2
ymax=20

def cnv2x(X): 
    return X*(xmax-xmin)/Width+xmin
def cnv2y(Y): 
    return (Height-Y)*(ymax-ymin)/Height+ymin
def x2cnv(x): 
    return Width*(x-xmin)/(xmax-xmin)
def y2cnv(y): 
    return Height-Height*(y-ymin)/(ymax-ymin)


# On dessine les nouveaux axes:
cnv.create_line(x2cnv(xmin),y2cnv(0),x2cnv(xmax),y2cnv(0))
cnv.create_line(x2cnv(0),y2cnv(ymin),x2cnv(0),y2cnv(ymax))

N=1000
dx=8/N
x=-4
def f(x):
    return x**2
for i in range(N):
    x=x+dx;
    X=x2cnv(x)
    Y=y2cnv(f(x))
    cnv.create_rectangle(X,Y,X+1,Y+1)

gui.mainloop()
qui permet d'obtenir:

Voir aussi:
LongPage: h2: 2 - h3: 0