You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1002 lines
34 KiB
Python

#!/usr/bin/env python3
from tkinter import *
from numpy import *
from struct import *
import getopt
import sys
from tkinter import filedialog as fd
from tkinter import messagebox as ms
#####################################################################
#
# rimbalzi.py 2022.06.08 Sergio Steffe' Pisa
# port in pyton del programma rimbalzi per Mac in Tthink-C del 1993,
# scritto come esercizio di python del corso di
# Algoritmi di Spettrografia del Prof. Giovanni Moruzzi 2021/22.
#
#
####################################################################
# versioni:
# versione 1.0: 2022.06.08 versione iniziale
# versione 1.1: 2022.06.16
# pulizia dal software delle righe usate per il debugging
# aggiunti tasti SaveAll e LoadAll, aggiornato help
# aggiunta scalatura dei dati dei files tra -s e -b e normale
# aggiunte finestre apertura files e segnalazione errori io files
# aggiunta logica per disabilitare/abilitare i bottoni
# il Fast puo' dare lampeggiamenti del disegno sul canvas
# versione 1.2: 2022.06.22
# corretto errore in riga 580 di calcola: era scritto denom=yyas*xxs invece di +
# essendo un fattore di normalizzazione saltavano fuori errori solo nelle rare
# condizioni in cui veniva denom=0 !
# versione 1.3: 2022.07.01
# corretto misprints qui e li, ingranditi i punti di ancora
# versione 1.3c: 2022.07.01
# versione per cellulari: effetto ottico per confermare il grab e punti piu' grossi.
#
###################################################################
# rimbalzi accetta i flag -s (small) -b (big) -S (Slow) -F (Fast) -v (versione) e -h (help)
#
def main(argv):
global big,small,Slow,Fast
try:
opts,args = getopt.getopt(argv,"bhsSFv")
except getopt.GetoptError:
print ('rimbalzi:\n\
unknown option !\n\
-b big\n\
-s small\n\
-S Slow\n\
-F Fast\n\
-v version\n\
-h help\n')
exit(1)
big=False
small=False
Slow=False
Fast=False
for opt,arg in opts:
if opt == '-h':
print ('rimbalzi:\n\
-b big\n\
-s small\n\
-S Slow\n\
-F Fast\n\
-v version\n\
-h help\n')
exit()
elif opt in ('-b'):
big=True
elif opt in ('-s'):
small=True
elif opt in ('-S'):
Slow=True
elif opt in ('-F'):
Fast=True
elif opt in ('-v'):
print('rimbalzi 1.3 2022.07.01\n')
exit()
#----------------------------------------
if __name__ == "__main__":
main(sys.argv[1:])
#---------------------------------------
#
# senza la parte con le chiamate a getopt occorre definire tra le globali anche
# big=False/True e small=False/True e Slow=False/True
#
# globali
RunAll=True
IsSetup=True # nel setup si possono modificare le posizioni
RunMotion=False # nel runmotion non si possono modificare le posizioni
GetData=False # per inserimento diretto numeri nelle finestre
CenterGrabbed=RadiusGrabbed=PointGrabbed=VectorGrabbed=False # per modificare graficamente le posizioni
IsLoadedall=False # i dati e le traiettorie sono stati caricati da file.
IsLoaded=False # i dati iniziali sono stati caricati da file
# due canvas e una zona comandi e finestre dati
# canvas cerchi
# quadrato di lato cl; il centro e' Ox,Oy; il cerchio fisso ha lato CR=cl/2
# se x,y sono coordinate naturali, vanno disegnate in Ox+x,Oy-y ! e analogamente vanno convertiti gli eventi.
#
cl=800 # per canvas cerchi, quadrata, di lato cl
if big:
cl=1200
if small:
cl=400
CR=cl/2 # raggio cerchio fisso inscritto
Ox=400 # centro del canvasc
Oy=400 # centro del canvasc
if big:
Ox=600
Oy=600
if small:
Ox=200
Oy=200
c1=cl/5 # lunghezza vettore iniziale
cp=5 # raggio punti degli handle
# per canvas fasi : due quadrati 400x400 per rappresentare 0~180 x -90~+90 posizione e angolo rimbalzo.
fw=400
fh=800 #fh=cl
#
if big:
fw=600
fh=1200 #fh=cl
if small:
fw=200
fh=400 #fh=cl
#----------------------
Cr=cl/4 #raggio cerchio interno
Ca=cl/8 #centro cerchio interno
X0=x0=-cl/4 #punto iniziale (copia fissa e copia operativa)
Y0=y0=-cl/4 #punto iniziale (copia fissa e copia operativa)
xx=0 #variabili per il calcolo del rimbalzo
yy=0 #variabili per il calcolo del rimbalzo
angolo=0
angolov=0
#X0,Y0,VX0,VY0 sono quelli del save e load e appaiono nelle caselle.
#x0,y0,vx0,vy0 sono usati nei calcoli e cambiano man mano,
# e sono usati da start e restart.
vx0=30 #velocita iniziale
vy0=30 #velocita iniziale
# normalizzazione velocita
vs=sqrt(vx0*vx0+vy0*vy0)
VX0=vx0=vx0/vs
VY0=vy0=vy0/vs
#
rr=[] #punti di rimbalzo nel canvas cerchi - coppie x,y
ff=[] #punti di rimbalzo nel canvas fasi - coppie angolo, angolov
rextern=0 # numero di rimbalzi su cerchio esterno
rintern=0 # numero di rimbalzi su cerchio interno
#
# ............................................................ InRange
def InRange(xmin,x,xmax):
if x>=xmax:
return xmax
if x<=xmin:
return xmin
return x
# ................................................. Start/Stop motion
def StartStop():
global RunMotion,IsSetup,IsLoadedall,IsLoaded
RunMotion=not RunMotion
if RunMotion:
StartButton["text"]="Stop"
else:
if IsSetup:
StartButton["text"]="Start"
else:
StartButton["text"]="Restart"
# ...................................................... Exit program
def StopAll():
global RunAll
RunAll=False
#........................................................
def Setup():
global IsSetup,rr,ff,RunMotion,x0,y0,xx,yy,vx0,vy0,rr,ff,rextern,rintern,cl,X0,Y0,VX0,VY0,IsLoadedall,IsLoaded
IsLoaded=False
IsLoadedall=False
if RunMotion==False:
IsSetup=True
xx=0
yy=0
x0=X0 #riinizializzazione valori operativi
y0=Y0
vx0=VX0
vy0=VY0
rr=[]
ff=[]
rextern=0
rintern=0
#
Entryrext.delete(0,END)
Entryrint.delete(0,END)
Entryrtot.delete(0,END)
Entryrrapp.delete(0,END)
#
StartButton["text"]="Start"
#.......................................................
def Help():
newWindow=Toplevel(root)
newWindow.title("Help")
newWindow.geometry("600x600")
Label(newWindow,text="Rimbalzi in una corona circolare asimmetrica. v.1.1\n\
\n\
Nella fase di SETUP, usando il mouse (cursore blu) sui punti neri, si puo' spostare\n\
il centro del cerchio interno e cambiarne il raggio e posizionare\n\
il vettore iniziale su cui iniziera' a muoversi un punto.\n\
In alternativa, invece di fissare punto e vettore iniziale, si puo' scegliere\n\
una 'fase' di partenza (cursore rosso) e in tal caso appena si clicca il\n\
programma effettua uno START partendo dal punto della circonferenza scelto e con\n\
l'angolo scelto con la normale al cerchio.(preview in verde mentre si muove il cursore)\n\
All'apertura il programma e' nel modo di SETUP\n\
\n\
Allo START il punto inizia a rimbalzare elasticamente sulle due circonferenze.\n\
Si puo' fermare con STOP e rilanciare con RESTART, ma se si chiama il SETUP\n\
i dati dei rimbalzi vengono cancellati e riinizializzati.\n\
\n\
SAVE salva i dati di partenza (centro e raggio del cerchio interno Ca e Cr, \n\
e posizione e velocita' iniziale del punto, X0,Y0,VX0,VY0) nel file \n\
rimbalzi-data.txt in formato binario, e LOAD li carica da detto file.\n\
\n\
E' possibile durante il SETUP immettere direttamente questi dati da tastiera,\n\
ma occorre dare un ENTER per farli accettare dal programma\n\
\n\
SAVEALL salva tutto nel file rimbalzi-alldata.txt in formato binario,\n\
e LOADALL li carica da detto file. (tasti non disponibili nel formato small (-s))\n\
\n\
Nella parte di sinistra si vedono i cerchi e le traiettorie del punto\n\
in movimento. Nella parte di destra sia per il cerchio EXTerno che per\n\
quello INTerno sono segnati i punti di contatto sul cerchio (tra 0 e 360 gradi)\n\
e l'angolo di rimbalzo rispetto la normale (tra -90 e +90 gradi).\n\
\n\
Questo programma e' il port in python3 con tkinter dell'analogo programma\n\
scritto per il macintosh nel 1993 in Tkink-C.\n\
\n\
Sergio Steffe' Pisa 16/6/2022 ").pack()
#......................................................Save CR,Cr,Ca,X0,Y0,VX0,VY0
def Save():
global CR,Cr,Ca,X0,Y0,VX0,VY0
try:
hnd=fd.asksaveasfile(mode="wb")
s=pack("7d",CR,Cr,Ca,X0,Y0,VX0,VY0)
hnd.write(s)
except :
ms.showerror(title='errore',message='non ho potuto salvare i dati')
# uuu=sys.exc_info()[0]
# ms.showerror(title='errore',message=uuu)
finally:
hnd.close()
#.......................................................Load CR,Cr,Ca,X0,Y0,VX0,VY0
def Load():
global CR,Cr,Ca,x0,y0,vx0,vy0,CR,X0,Y0,VX0,VY0,IsLoaded,rr,ff,rextern,rintern,RunMotion,IsLoaded,IsSetup,IsLoadedall
filename=fd.askopenfilename()
if len(filename)==0:
return None
try:
hnd=open(filename,"rb")
s=hnd.read(7*8) # 8 bytes ogni numero, 7 numeri
xin=array(unpack('d'*7, s))
# ora vanno controllati i limiti per ciascuna variabile:
# se fuori limiti viene fatta una scelta diversa.
CRR=xin[0] # serve per scalare i dati al CR effettivo (che varia a seconda di Small, Medium o Big)
if CRR==0:
ms.showerror(title='errore',message='CR=0 ! Dati inconsistenti.')
return None
z=CR/CRR # fattore di scala: con CR tra 200 e 600 puo' andare da 3 a 1/3
if (z<0.2)or(z>5):
ms.showerror(title='errore',message='fattore di scala inconsistente.')
return None
Crr=xin[1]*z
Crrr=InRange(0,Crr,CR*0.98) # non vogliamo cerchi quasi uguali
Caa=xin[2]*z
Ca=InRange(Crrr-CR,Caa,CR-Crrr)
Cr=InRange(0,Crr,min(CR-Ca,CR+Ca))
EntryCa.delete(0,END)
EntryCa.insert(0,'{:.5f}'.format(Ca))
EntryCr.delete(0,END)
EntryCr.insert(0,'{:.5f}'.format(Cr))
x0u=(xin[3])*z
y0u=(xin[4])*z
# controllare nan error !
su=sqrt(x0u*x0u+y0u*y0u)
if (su>1200)or(su<300):
ms.showerror(title='errore',message='punto iniziale inconsistente.')
return None
if su>CR:
x0u=x0u*CR/su
y0u=y0u*CR/su
su=sqrt(x0u*x0u+(y0u-Ca)*(y0u-Ca))
if su<Cr:
x0u=x0u*Cr/su
y0u=Ca+(y0u-Ca)*Cr/su
X0=x0=x0u
Y0=y0=y0u
vx0=xin[5]
vy0=xin[6]
su=sqrt(vx0*vx0+vy0*vy0)
if su==0:
VX0=vx0=1/sqrt(2)
VY0=vy0=1/sqrt(2)
su=1
else:
VX0=vx0=vx0/su
VY0=vy0=vy0/su
rr=[]
ff=[]
rextern=rintern=0
IsLoaded=True
IsLoadedall=False
IsSetup=True
RunMotion=False
StartButton["text"]="Start"
except :
ms.showerror(title='errore',message='non ho potuto leggere i dati')
finally :
hnd.close()
#....................................................... SaveAll
def SaveAll():
global CR,Ca,Cr,X0,Y0,VX0,VY0,rr,ff,angolo,angolov,rextern,rintern,x0,y0,vx0,vy0
#filename=fd.askdirectory()
#filename=fd.asksaveasfile()
#hnd=open(filename,"wb")
try:
hnd=fd.asksaveasfile(mode="wb")
s=pack("13d",CR,Cr,Ca,X0,Y0,VX0,VY0,x0,y0,vx0,vy0,angolo,angolov)
hnd.write(s)
nr=len(rr)
nf=len(ff)
s=pack("4i",rextern,rintern,nr,nf)
hnd.write(s)
s=pack(nr*"d",*rr)
hnd.write(s)
s=pack(nf*"d",*ff)
hnd.write(s)
except:
ms.showerror(title='errore',message='non ho potuto salvare i dati')
finally:
hnd.close()
#....................................................... LoadAll
def LoadAll():
global CR,Ca,Cr,X0,Y0,VX0,VY0,rr,ff,angolo,angolov,rextern,rintern,x0,y0,vx0,vy0,RunMotion,IsSetup,IsLoadedall,RunMotion
filename=fd.askopenfilename()
if len(filename)==0:
return None
try:
hnd=open(filename,"rb")
s=hnd.read(13*8) # 8 bytes ogni numero double real, 13 numeri
xin=array(unpack('d'*13, s))
CRR=xin[0]
if CRR==0:
ms.showerror(title='errore',message='CR=0 ! Dati inconsistenti')
return
z=CR/CRR # fattore di scala. Supponiamo inoltre che fh=cl cosi vale sia per rr che per ff
# qui andrebbe controllata la coerenza dei dati
Cr=xin[1]*z
Ca=xin[2]*z
X0=xin[3]*z
Y0=xin[4]*z
VX0=xin[5]
VY0=xin[6]
x0 = xin[7]*z
y0 = xin[8]*z
vx0 = xin[9]
vy0 = xin[10]
angolo = xin[11]
angolov = xin[12]
s=hnd.read(4*4)
xin=array(unpack('i'*4, s))
rextern = int(xin[0])
rintern = int(xin[1])
nr= int(xin[2])
nf= int(xin[3])
s=hnd.read(nr*8)
rr=list(unpack('d'*nr,s))
s=hnd.read(nf*8)
ff=list(unpack('d'*nf,s))
#problema: porebbe capitare angolo=angolov=rextern=rintern=nr=nf=0 se non e' stato fatto un run dopo il setup
#
if nr>=2:
IsLoadedall=True
IsSetup=False
StartButton["text"]="Restart"
#vanno riscalati rr e ff !
for i in range(len(ff)):
ff[i]=ff[i]*z
for i in range(len(rr)):
rr[i]=rr[i]*z
else:
IsLoaded=True
IsSetup=True
StartButton["text"]="Start"
RunMotion=False
rr=[]
ff=[]
rextern=rintern=angolo=angolov=0
except:
ms.showerror(title='errore',message='non ho potuto caricare i dati')
# contyrollare di non aver sporcato dei dati ...
finally:
hnd.close()
# ...................................................... Read entries
def ReadData(*arg):
global GetData,IsSetup
if IsSetup:
GetData=True
#........................................................Grab center
def GrabSome(event):
global CenterGrabbed,RadiusGrabbed,PointGrabbed,VectorGrabbed,Ox,Oy,Ca,Cr,x0,y0,vx0,vy0,IsSetup,cp
if IsSetup:
CenterGrabbed=((Ox-event.x)**2+(Oy-Ca -event.y)**2)<cp*cp
if not CenterGrabbed:
RadiusGrabbed=((Ox-event.x+Cr/sqrt(2))**2+(Oy-Ca-event.y+Cr/sqrt(2))**2)<cp*cp
if not RadiusGrabbed:
PointGrabbed=((Ox+x0-event.x)**2+(Oy-y0-event.y))**2 <cp*cp
if not PointGrabbed:
VectorGrabbed=((Ox+x0-event.x+vx0*c1)**2 + (Oy-y0+-event.y-vy0*c1)**2)<cp*cp
#........................................................Release center
def ReleaseSome(event):
global CenterGrabbed,RadiusGrabbed,PointGrabbed,VectorGrabbed
CenterGRabbed=False
RadiusGrabbed=False
PointGrabbed=False
VectorGrabbed=False
MouseChange=True
#........................................................
def DragSome(event):
global CenterGrabbed,RadiusGrabbed,PointGrabbed,VectorGrabbed,Ox,Oy,Ca,Cr,x0,y0,vx0,vy0,c1,X0,Y0,VX0,VY0,IsSetup,CR
if IsSetup:
if CenterGrabbed:
Caa=Oy-event.y
Ca=InRange(Cr-CR,Caa,CR-Cr)
EntryCa.delete(0,END)
EntryCa.insert(0,'{:.5f}'.format(Ca/CR))
if RadiusGrabbed:
Crr=sqrt((Ox-event.x)**2+(Oy-event.y-Ca)**2)
Cr=InRange(0,Crr,min(CR-Ca,CR+Ca))
EntryCr.delete(0,END)
EntryCr.insert(0,'{:.5f}'.format(Cr/CR))
if PointGrabbed:
x0u=event.x-Ox
y0u=Oy-event.y
su=sqrt(x0u*x0u+y0u*y0u)
if su>CR:
x0u=x0u*CR/su
y0u=y0u*CR/su
su=sqrt(x0u*x0u+(y0u-Ca)*(y0u-Ca))
if su<Cr:
x0u=x0u*Cr/su
y0u=Ca+(y0u-Ca)*Cr/su
X0=x0=x0u
Y0=y0=y0u
Entryx0.delete(0,END)
Entryx0.insert(0,'{:.5f}'.format(x0/CR))
Entryy0.delete(0,END)
Entryy0.insert(0,'{:.5f}'.format(y0/CR))
if VectorGrabbed:
evx=InRange(0,event.x,2*Ox)
evy=InRange(0,event.y,2*Oy)
vx0=evx-x0-Ox
vy0=Oy-y0-evy
c1=sqrt(vx0*vx0+vy0*vy0)
VX0=vx0=vx0/c1
VY0=vy0=vy0/c1
Entryvx0.delete(0,END)
Entryvx0.insert(0,'{:.5f}'.format(vx0))
Entryvy0.delete(0,END)
Entryvy0.insert(0,'{:.5f}'.format(vy0))
#.......................................................
def Clicca(event):
global x0,y0,vx0,vy0,fw,fh,CR,Cr,Ca,IsSetup,Cinterno,RunMotion,rintern,rextern,X0,Y0,VX0,VY0,CR
if IsSetup:
# partenza da punto scelto su canvasf
ff.append(event.x)
ff.append(event.y)
if event.y > fw:
Cinterno=True
rintern=1
rextern=0
aavv=event.y-fw
aaff=event.x*2*pi/fw
aavv=(aavv-fw/2)*pi/fw
x0=Cr*sin(aaff)
y0=Ca+Cr*cos(aaff)
vx0=sin(aaff+aavv)
vy0=cos(aaff+aavv)
rintern=1
rextern=0
else:
Cinterno=False
rintern=0
rextern=1
aavv=event.y
aaff=event.x*2*pi/fw
aavv=(aavv-fw/2)*pi/fw
x0=CR*sin(aaff)
y0=CR*cos(aaff)
vx0=-sin(aaff+aavv)
vy0=-cos(aaff+aavv)
rr.append(Ox+x0)
rr.append(Oy-y0)
X0=x0
Y0=y0
VX0=vx0
VY0=vy0
IsSetup=False
RunMotion=True
StartButton["text"]="Stop"
#------------------------------------------------------
def Mostra(event):
global fw,fh,IsSetup,Cinterno,RunMotion,CR,Ca,Cr,x1,y1,vx1,vy1,cp
if event.y > fw:
aavv=event.y-fw
cinterno=True
else:
aavv=event.y
cinterno=False
aaff=event.x*360/fw
aavv=(aavv-fw/2)*180/fw
Entryposang.delete(0,END)
Entryposang.insert(0,'{:.5f}'.format(aaff))
Entryvelang.delete(0,END)
Entryvelang.insert(0,'{:.5f}'.format(aavv))
# qui mostro il punto partenza e il vettore nel canvas dei cerchi
# ma solo nel SETUP:
if IsSetup:
if cinterno:
x1=Ox+Cr*sin(aaff*pi/180)
y1=Oy-Ca-Cr*cos(aaff*pi/180)
x2=Ox+(Cr+CR*0.1)*sin(aaff*pi/180)
y2=Oy-Ca-(Cr+CR*0.1)*cos(aaff*pi/180)
x3=Ox+Cr*sin(aaff*pi/180)+CR*0.1*sin(aaff*pi/180+aavv*pi/180)
y3=Oy-Ca-Cr*cos(aaff*pi/180)- CR*0.1*cos(aaff*pi/180+aavv*pi/180)
else:
x1=Ox+CR*sin(aaff*pi/180)
y1=Oy-CR*cos(aaff*pi/180)
x2=Ox+CR*sin(aaff*pi/180)*0.9
y2=Oy-CR*cos(aaff*pi/180)*0.9
x3=Ox+CR*sin(aaff*pi/180)-CR*0.1*sin(aaff*pi/180+aavv*pi/180)
y3=Oy-CR*cos(aaff*pi/180)+CR*0.1*cos(aaff*pi/180+aavv*pi/180)
# canvasc.create_oval(x1-cp,y1-cp,x1+cp,y1+cp,fill="green") evidenzia il punto del cerchio
canvasc.create_line(x1,y1,x2,y2,fill="red") # perpendicolare
canvasc.create_line(x1,y1,x3,y3,fill="green",arrow=LAST)
#.......................................................
def calcola():
global x0,y0,vx0,vy0,CR,Cr,Ca,xx,yy,vxx,vyy,Cinterno,fw,angolo,angolov,rextern,rintern
# posizione iniziale x0,y0, velocita iniziale vx0 vy0
# CR raggio esterno, Cr raggio interno, Ca posizione centro interno
# punto di rimbalzo xx,yy con velocita vxx,vyy
# Cinterno = True se rimbalza sul cerchio interno e non esterno
# Il calcolo del rimbalzo non usa la trigonometria, che serve solo a visualizzare le fasi
#print(x0,y0,vx0,vy0,CR,Cr,Ca)
vs=sqrt(vx0*vx0+vy0*vy0)
provv1=(x0*vx0+y0*vy0)/vs
delta1=provv1*provv1-x0*x0-y0*y0+CR*CR
t1= -provv1+sqrt(delta1)
t2= -provv1-sqrt(delta1)
tm12=max(t1,t2)
tm=tm12
# istante contatto circonferenza esterna se non esistesse interna
alfa=0
Cinterno=False
#
provv2=provv1-Ca*vy0/vs
delta2=provv2*provv2-x0*x0-y0*y0-Ca*Ca+2*y0*Ca+Cr*Cr
if delta2 > 0 : # la retta interseca la circonferenza interna
t3=-provv2+sqrt(delta2)
t4=-provv2-sqrt(delta2)
if ((t3<0) or (t4<0)) : # ma a tempi negativi - rimbalzo su circonferenza esterna
alfa=0
tm=tm12
else:
tm34=min(t3,t4)
if (tm34<0): # accade solo se sto partendo dalla circonferenza interna
alfa=0
tm=tm12
else:
alfa=Ca
Cinterno=True
tm=tm34
if delta2==0 :
alfa=0
tm=tm12
xx=x0+vx0*tm/vs
yy=y0+vy0*tm/vs
yya=yy-alfa
yyas=yya*yya
xxs=xx*xx
denom=yyas+xxs
vyy=(vy0*(xxs-yyas)-2*vx0*xx*yya)/denom/vs
vxx=(vx0*(yyas-xxs)-2*vy0*xx*yya)/denom/vs
denom=sqrt(vxx*vxx+vyy*vyy)
vxx=vxx/denom
vyy=vyy/denom
# calcolo fasi : in alto cerchio esterno, in basso cerchio interno
if Cinterno:
angolo=arctan2(xx,yy-Ca)
else:
angolo=arctan2(xx,yy)
if angolo <0:
angolo=angolo+2*pi
# ora angolo sta tra 0 e 2*pi e rappresenta il punto di rimbalzo.
angolov=arctan2(vxx,vyy)-angolo
angolov=arcsin(sin(angolov))
# ora angolov sta tra -pi/2 e +pi/2
# va cambiato segno di angolov esterno (perche esterno punta dentro)
if Cinterno==False:
angolov=-angolov
if Cinterno:
angolov=(angolov+pi/2)*fw/pi + fw
else:
angolov=(angolov+pi/2)*fw/pi
angolo=angolo*fw/(2*pi)
# angolov ora sta tra 0 e fw per cerchio esterno, tra fw e 2*fw per cerchio interno
# angolo sta tra 0 e fw
# aggiorno il numero dei rimbalzi fatti
if Cinterno:
rintern=rintern+1
else:
rextern=rextern+1
###########################################################################
#
# ................................................ Create root window
root=Tk()
root.title("rimbalzi")
root.resizable(width=False,height=False)
root.bind('<Return>',ReadData) #occorre dare un ENTER per fare leggere i dati
# ......................................... Add canvas to root window
# ..................canvasc per i cerchi e canvasf per le fasi
canvasc=Canvas(root, width=cl, height=cl, background='white')
canvasc.grid(row=0,column=0)
# ..................................................... mouse buttons
canvasc.bind("<Button-1>",GrabSome)
canvasc.bind("<B1-Motion>",DragSome)
canvasc.bind("<ButtonRelease-1>",ReleaseSome)
#
canvasf=Canvas(root, width=fw, height=fh, background='white')
canvasf.grid(row=0,column=1)
#
canvasf.bind("<Button-1>",Clicca)
canvasf.bind("<Motion>",Mostra)
#
toolbar=Frame(root)
toolbar.grid(row=0,column=2,sticky=N)
# ................................................... Toolbar buttons
SetupButton=Button(toolbar,text="Setup",command=Setup,width=7)
SetupButton.grid(row=0,column=0)
StartButton=Button(toolbar,text="Start",command=StartStop,width=7)
StartButton.grid(row=0,column=1)
SaveButton=Button(toolbar, text="Save", command=Save,width=7)
SaveButton.grid(row=1,column=0)
LoadButton=Button(toolbar, text="Load", command=Load,width=7)
LoadButton.grid(row=1,column=1)
# nella versione small non c'e' spazio per i distanziatori
if small==False:
Labspazio1=Label(toolbar,text=" ")
Labspazio1.grid(row=2,column=0)
#
# ........................................ Toolbar labels and entries
LabCr=Label(toolbar,text="Cr/CR")
LabCr.grid(row=3,column=0)
EntryCr=Entry(toolbar,bd=5,width=10)
EntryCr.grid(row=3,column=1)
#
LabCa=Label(toolbar,text="Ca/CR")
LabCa.grid(row=4,column=0)
EntryCa=Entry(toolbar,bd=5,width=10)
EntryCa.grid(row=4,column=1)
#
Labx0=Label(toolbar,text="X0/CR")
Labx0.grid(row=5,column=0)
Entryx0=Entry(toolbar,bd=5,width=10)
Entryx0.grid(row=5,column=1)
#
Laby0=Label(toolbar,text="Y0/CR")
Laby0.grid(row=6,column=0)
Entryy0=Entry(toolbar,bd=5,width=10)
Entryy0.grid(row=6,column=1)
#
Labvx0=Label(toolbar,text="VX0")
Labvx0.grid(row=7,column=0)
Entryvx0=Entry(toolbar,bd=5,width=10)
Entryvx0.grid(row=7,column=1)
#
Labvy0=Label(toolbar,text="VY0")
Labvy0.grid(row=8,column=0)
Entryvy0=Entry(toolbar,bd=5,width=10)
Entryvy0.grid(row=8,column=1)
# nella versione small non c'e' spazio per i distanziatori
if small==False:
Labspazio2=Label(toolbar,text=" ")
Labspazio2.grid(row=9,column=0)
#
CloseButton=Button(toolbar, text="Quit", command=StopAll,width=7)
CloseButton.grid(row=10,column=0)
HelpButton=Button(toolbar, text="Help", command=Help,width=7)
HelpButton.grid(row=10,column=1)
# nella versione small non c'e' spazio per i distanziatori
if small==False:
Labspazio3=Label(toolbar,text=" ")
Labspazio3.grid(row=11,column=0)
#
Labrext=Label(toolbar,text="rimb. ext")
Labrext.grid(row=12,column=0)
Entryrext=Entry(toolbar,bd=5,width=10)
Entryrext.grid(row=12,column=1)
Labrint=Label(toolbar,text="rimb. int")
Labrint.grid(row=13,column=0)
Entryrint=Entry(toolbar,bd=5,width=10)
Entryrint.grid(row=13,column=1)
#
Labrtot=Label(toolbar,text="rimb. tot")
Labrtot.grid(row=14,column=0)
Entryrtot=Entry(toolbar,bd=5,width=10)
Entryrtot.grid(row=14,column=1)
Labrrapp=Label(toolbar,text="rimb. int %")
Labrrapp.grid(row=15,column=0)
Entryrrapp=Entry(toolbar,bd=5,width=10)
Entryrrapp.grid(row=15,column=1)
# nella versione small non c'e' spazio per i distanziatori
if small==False:
Labspazio4=Label(toolbar,text=" ")
Labspazio4.grid(row=16,column=0)
#
Labposang=Label(toolbar,text="posiz. angolare")
Labposang.grid(row=17,column=0)
Entryposang=Entry(toolbar,bd=5,width=6)
Entryposang.grid(row=17,column=1)
Labvelang=Label(toolbar,text="angolo rimbalzo")
Labvelang.grid(row=18,column=0)
Entryvelang=Entry(toolbar,bd=5,width=6)
Entryvelang.grid(row=18,column=1)
#
# nella versione small non c'e' spazio per questi bottoni
if small==False:
Labspazio5=Label(toolbar,text=" ")
Labspazio5.grid(row=19,column=0)
#
SaveAllButton=Button(toolbar, text="SaveAll", command=SaveAll,width=7)
SaveAllButton.grid(row=20,column=0)
LoadAllButton=Button(toolbar, text="LoadAll", command=LoadAll,width=7)
LoadAllButton.grid(row=20,column=1)
#
# scrittura valori iniziali
EntryCa.insert(0,'{:.5f}'.format(Ca/CR))
EntryCr.insert(0,'{:.5f}'.format(Cr/CR))
Entryx0.insert(0,'{:.5f}'.format(X0/CR))
Entryy0.insert(0,'{:.5f}'.format(Y0/CR))
Entryvx0.insert(0,'{:.5f}'.format(VX0))
Entryvy0.insert(0,'{:.5f}'.format(VY0))
#......................................................... Variables
delay=20 #milliseconds
if Slow:
delay=200
if Fast:
delay=5
while RunAll:
# .............................................. Draw on canvas ---- Main Loop
#
# codiche che abilita o disabilita i bottoni a secondo delle variabili
if RunMotion:
# mentre gira non voglio poter entrare in setup, save, saveall, load, loadall : restano lo stop, lo help e il quit
SetupButton['state']=DISABLED
SaveButton['state']=DISABLED
SaveAllButton['state']=DISABLED
LoadButton['state']=DISABLED
LoadAllButton['state']=DISABLED
else:
SetupButton['state']=NORMAL
SaveButton['state']=NORMAL
SaveAllButton['state']=NORMAL
LoadButton['state']=NORMAL
LoadAllButton['state']=NORMAL
if IsLoadedall or IsLoaded:
canvasf.delete(ALL)
canvasc.delete(ALL)
# canvasc.create_oval(Ox-cp,Oy-Ca-cp,Ox+cp,Oy-Ca+cp,fill="black") # centro cerchio interno, mobile
# canvasc.create_oval(Ox-cp+Cr/sqrt(2),Oy-Ca-cp+Cr/sqrt(2),Ox+cp+Cr/sqrt(2),Oy-Ca+cp+Cr/sqrt(2),fill="black") # maniglia per cambiare raggio
# canvasc.create_oval(Ox+x0-cp,Oy-y0-cp,Ox+x0+cp,Oy-y0+cp,fill="black") # maniglia per spostare il punto di partenza
# canvasc.create_oval(Ox+x0-cp+vx0*c1,Oy-y0-cp-vy0*c1,Ox+x0+cp+vx0*c1,Oy-y0+cp-vy0*c1,fill="black") # maniglia per spostare il vettore di partenza
if IsLoaded:
canvasc.create_line(Ox+x0,Oy-y0,Ox+x0+vx0*c1,Oy-y0-vy0*c1,arrow=LAST) # freccia del vettore iniziale
canvasc.config(cursor="target blue") #cursore tondo blu sul canvas dei cerchi
canvasf.config(cursor="target red") #cursore tondo rosso sul canvas delle fasi
if IsSetup:
canvasf.delete(ALL)
canvasc.delete(ALL)
canvasc.create_oval(Ox-cp,Oy-Ca-cp,Ox+cp,Oy-Ca+cp,fill="black") # centro cerchio interno, mobile
canvasc.create_oval(Ox-cp+Cr/sqrt(2),Oy-Ca-cp+Cr/sqrt(2),Ox+cp+Cr/sqrt(2),Oy-Ca+cp+Cr/sqrt(2),fill="black") # maniglia per cambiare raggio
canvasc.create_oval(Ox+x0-cp,Oy-y0-cp,Ox+x0+cp,Oy-y0+cp,fill="black") # maniglia per spostare il punto di partenza
canvasc.create_oval(Ox+x0-cp+vx0*c1,Oy-y0-cp-vy0*c1,Ox+x0+cp+vx0*c1,Oy-y0+cp-vy0*c1,fill="black") # maniglia per spostare il vettore di partenza
canvasc.create_line(Ox+x0,Oy-y0,Ox+x0+vx0*c1,Oy-y0-vy0*c1,arrow=LAST) # freccia del vettore iniziale
canvasc.config(cursor="target blue") #cursore tondo blu sul canvas dei cerchi
canvasf.config(cursor="target red") #cursore tondo rosso sul canvas delle fasi
if GetData: #input dei dati permesso solo nella fase di SETUP
# va controllato che i dati siano consistenti
try:
eCa=float(EntryCa.get())
except ValueError:
pass
try:
eCr=float(EntryCr.get())
except ValueError:
pass
try:
ex0=float(Entryx0.get())
except ValueError:
pass
try:
ey0=float(Entryy0.get())
except ValueError:
pass
try:
evx0=float(Entryvx0.get())
except ValueError:
pass
try:
evy0=float(Entryvy0.get())
except ValueError:
pass
Ca=InRange(Cr-CR,eCa,CR-Cr)
Cr=InRange(0,eCr,min(CR-Ca,CR+Ca))
su=sqrt(ex0*ex0+ey0*ey0)
if su>CR:
ex0=ex0*CR/su
ey0=ey0*CR/su
su=sqrt(ex0*ex0+(ey0-Ca)*(ey0-Ca))
if su<Cr:
ex0=ex0*Cr/su
ey0=Ca+(ey0-Ca)*Cr/su
x0=X0=ex0
y0=Y0=ey0
if (evx0*evx0+evy0*evy0==0):
evx0=evy0=1.0
su=sqrt(evx0*evx0+evy0*evy0)
vx0=VX0=evx0/su
vy0=VY0=evy0/su
# ................................ Write variable values into entries
EntryCa.delete(0,END)
EntryCa.insert(0,'{:.5f}'.format(Ca/CR))
EntryCr.delete(0,END)
EntryCr.insert(0,'{:.5f}'.format(Cr/CR))
Entryx0.delete(0,END)
Entryx0.insert(0,'{:.5f}'.format(X0/CR))
Entryy0.delete(0,END)
Entryy0.insert(0,'{:.5f}'.format(Y0/CR))
Entryvx0.delete(0,END)
Entryvx0.insert(0,'{:.5f}'.format(VX0))
Entryvy0.delete(0,END)
Entryvy0.insert(0,'{:.5f}'.format(VY0))
GetData=False #fine della fase di letture dei dati
#
if RunMotion:
if IsSetup or IsLoaded:
IsSetup = False # se si clicca il cursore rosso durante il setup, IsSetup e RunMotion sono entrambi True!
IsLoaded = False # anche se si passa a dare Load e poi Start
rr.append(Ox+x0)
rr.append(Oy-y0)
calcola()
rr.append(Ox+xx)
rr.append(Oy-yy)
ff.append(angolo)
ff.append(angolov)
x0=xx
y0=yy
vx0=vxx
vy0=vyy
canvasc.delete(ALL) ##########################################################<<<<<<<<<<<<<<<<<<<<
canvasc.create_line(rr)
# questo e' comando che disegna tutte le traiettorie del rimbalzo !
Entryrext.delete(0,END)
Entryrext.insert(0,'{:10d}'.format(rextern))
Entryrint.delete(0,END)
Entryrint.insert(0,'{:10d}'.format(rintern))
Entryrtot.delete(0,END)
Entryrtot.insert(0,'{:10d}'.format(rintern+rextern))
Entryrrapp.delete(0,END)
Entryrrapp.insert(0,'{:.8f}'.format(rintern/(rintern+rextern)))
EntryCa.delete(0,END)
EntryCa.insert(0,'{:.5f}'.format(Ca/CR))
EntryCr.delete(0,END)
EntryCr.insert(0,'{:.5f}'.format(Cr/CR))
Entryx0.delete(0,END)
Entryx0.insert(0,'{:.5f}'.format(X0/CR))
Entryy0.delete(0,END)
Entryy0.insert(0,'{:.5f}'.format(Y0/CR))
Entryvx0.delete(0,END)
Entryvx0.insert(0,'{:.5f}'.format(VX0))
Entryvy0.delete(0,END)
Entryvy0.insert(0,'{:.5f}'.format(VY0))
#
canvasf.delete(ALL) ##########################################################<<<<<<<<<<<<<<<<<<<<
i=0
while i < len(ff):
canvasf.create_oval(ff[i],ff[i+1],ff[i],ff[i+1]) # sembra sia il solo modo per disegnare un singolo punto in tkinter !
i=i+2
canvasf.create_oval(ff[len(ff)-2]-cp,ff[len(ff)-1]-cp,ff[len(ff)-2]+cp,ff[len(ff)-1]+cp,fill="yellow") #il punto attuale evidenziato in giallo
# ora vanno disegnati i vari elementi geometrici sui due canvas !
#
canvasc.create_oval(1,1,2*CR,2*CR,outline="red") # cerchio esterno, fisso
# tics per il cerchio esterno, ogni 45 gradi
for i in range(0,8):
f=i*pi/4
canvasc.create_line(Ox+CR*cos(f),Oy-CR*sin(f),Ox+CR*cos(f)*0.96,Oy-CR*sin(f)*0.96,fill="red")
canvasc.create_text(Ox+CR*0.95,Oy,text='90',font=('Times','12', 'italic'))
canvasc.create_text(Ox,Oy+CR*0.95,text='180',font=('Times','12', 'italic'))
canvasc.create_text(Ox-CR*0.95,Oy,text='270',font=('Times','12', 'italic'))
canvasc.create_text(Ox,Oy-CR*0.95,text='0-360',font=('Times','12', 'italic'))
canvasc.create_oval(Ox-Cr,Oy-Ca-Cr,Ox+Cr,Oy-Ca+Cr,outline="red") # cerchio interno, mobile
for i in range(0,8):
f=i*pi/4
canvasc.create_line(Ox+Cr*cos(f),Oy-Ca-Cr*sin(f),Ox+Cr*cos(f)*1.06,Oy-Ca-Cr*sin(f)*1.06,fill="red")
# canvas delle fasi
canvasf.create_line(0,fw,fw,fw,fill="red") # separatore tra le due fasi
canvasf.create_line(1,0,1,2*fw,fill="red")
canvasf.create_line(fw,0,fw,2*fw,fill="red")
canvasf.create_line(1,1,fw,1,fill="red")
canvasf.create_line(1,2*fw,fw,2*fw,fill="red")
canvasf.create_text(20,20,text='EXT',font=('Times','12', 'italic')) # circonferenza esterna
canvasf.create_text(20,20+fw,text='INT',font=('Times','12', 'italic')) # circonferenza interna
# tics e scala per il punto sulle circonferenze
canvasf.create_text(3*cp,fw-3*cp,text='0',font=('Times','12', 'italic'))
canvasf.create_line(fw/2,0,fw/2,2*fw,fill="blue")
canvasf.create_text(fw/2,fw-3*cp,text='180',font=('Times','12', 'italic'))
canvasf.create_line(fw/4,0,fw/4,2*fw,fill="blue")
canvasf.create_text(fw/4,fw-3*cp,text='90',font=('Times','12', 'italic'))
canvasf.create_line(3*fw/4,0,3*fw/4,2*fw,fill="blue")
canvasf.create_text(3*fw/4,fw-3*cp,text='270',font=('Times','12', 'italic'))
canvasf.create_text(fw-4*cp,fw-3*cp,text='360',font=('Times','12', 'italic'))
# tics e scala per l'angolo di rimbalzo rispetto la normale delle circonferenze
canvasf.create_line(0,fw/2,fw,fw/2,fill="blue")
canvasf.create_text(7*cp,fw/2,text='0',font=('Times','12', 'italic'))
canvasf.create_line(0,fw+fw/2,fw,fw+fw/2,fill="blue")
canvasf.create_text(7*cp,fw+fw/2,text='0',font=('Times','12', 'italic'))
#
canvasf.create_line(0,fw/4,fw,fw/4,fill="blue")
canvasf.create_text(7*cp,fw/4-2*cp,text='-45',font=('Times','12', 'italic'))
canvasf.create_line(0,fw+fw/4,fw,fw+fw/4,fill="blue")
canvasf.create_text(7*cp,fw+fw/4-2*cp,text='-45',font=('Times','12', 'italic'))
#
canvasf.create_line(0,3*fw/4,fw,3*fw/4,fill="blue")
canvasf.create_text(7*cp,3*fw/4-cp,text='+45',font=('Times','12', 'italic'))
canvasf.create_line(0,fw+3*fw/4,fw,fw+3*fw/4,fill="blue")
canvasf.create_text(7*cp,fw+3*fw/4-cp,text='+45',font=('Times','12', 'italic'))
#
if IsLoaded:
EntryCa.delete(0,END)
EntryCa.insert(0,'{:.5f}'.format(Ca/CR))
EntryCr.delete(0,END)
EntryCr.insert(0,'{:.5f}'.format(Cr/CR))
Entryx0.delete(0,END)
Entryx0.insert(0,'{:.5f}'.format(X0/CR))
Entryy0.delete(0,END)
Entryy0.insert(0,'{:.5f}'.format(Y0/CR))
Entryvx0.delete(0,END)
Entryvx0.insert(0,'{:.5f}'.format(VX0))
Entryvy0.delete(0,END)
Entryvy0.insert(0,'{:.5f}'.format(VY0))
if IsLoadedall:
canvasc.create_line(rr)
i=0
while i < len(ff):
canvasf.create_oval(ff[i],ff[i+1],ff[i],ff[i+1]) # sembra sia il solo modo per disegnare un singolo punto in tkinter !
i=i+2
canvasf.create_oval(ff[len(ff)-2]-cp,ff[len(ff)-1]-cp,ff[len(ff)-2]+cp,ff[len(ff)-1]+cp,fill="yellow") #il punto attuale evidenziato in giallo
Entryrext.delete(0,END)
Entryrext.insert(0,'{:10d}'.format(rextern))
Entryrint.delete(0,END)
Entryrint.insert(0,'{:10d}'.format(rintern))
Entryrtot.delete(0,END)
Entryrtot.insert(0,'{:10d}'.format(rintern+rextern))
Entryrrapp.delete(0,END)
Entryrrapp.insert(0,'{:.8f}'.format(rintern/(rintern+rextern)))
EntryCa.delete(0,END)
EntryCa.insert(0,'{:.25}'.format(Ca/CR))
EntryCr.delete(0,END)
EntryCr.insert(0,'{:.25}'.format(Cr/CR))
Entryx0.delete(0,END)
Entryx0.insert(0,'{:.25}'.format(X0/CR))
Entryy0.delete(0,END)
Entryy0.insert(0,'{:.25}'.format(Y0/CR))
Entryvx0.delete(0,END)
Entryvx0.insert(0,'{:.5f}'.format(VX0))
Entryvy0.delete(0,END)
Entryvy0.insert(0,'{:.5f}'.format(VY0))
#
canvasf.update()
canvasc.update()
#................................................. Wait delay time
canvasc.after(delay)
canvasf.after(delay)
#-------------------------
root.destroy()
#
##############################################
# todo:
# reverse motion
# clear trajectories
# clear phases
# colorize trajectories
###############################################