COLLISION ÉLASTIQUE DE DEUX DISQUES¶
import urllib
import os
from jupyter_server import serverapp
from selenium import webdriver
#from webdriver_manager.firefox import GeckoDriverManager
#driver = webdriver.Firefox(executable_path=GeckoDriverManager().install())
#driver.get("https://www.google.com")
import numpy as np
import math
from bokeh.layouts import row, column
from bokeh.models import ColumnDataSource, Slider, Button, TextInput, Legend
from bokeh.plotting import figure, show, ColumnDataSource
from bokeh.events import ButtonClick
from bokeh.io import output_notebook, show, export_png
from time import sleep
import imageio
from IPython.display import Image
output_notebook()
# allow exporting video
EXPORT = True
Image("chocs.png")
Deux disques homogènes et de même densité entrent en collision sur une table horizontale où ils se déplacent sans frottements.
Pour chaque disque, on donne:
- Son rayon (radius)
- Les coordonnées x et y initiales de son centre
- La norme de sa vitesse initiale
- L'angle que fait sa vitesse initiale avec l'horizontal $\alpha$
Pour chaque disque: $m = \rho h (d \pi R^2)$
En considérant que les disques ont la même épaisseur h et masse volumique $\rho$, et en prenant $2 \pi \rho h = 1$ $$ m = R^2$$
On prend un temps t divisé en intervalles dt. A chaque instant, la position du centre de disques est donnée par $$ \overrightarrow{c_o c} = \int \overrightarrow{v} dt $$
Pour le calcul des vitesses, on travaille dans le référentiel centre de masse.
Les disques entrent en collision si: $$ (R_1 + R_2)^2 \geq (c_1 c_2)^2$$
Si il a y collision, commes les objets sont des disques, ils subissent une collision frontale par la composante des vitesses colinéaire à $\overrightarrow{c_1 c_2}$ et aucune collision par la composante perpendiculaire à $\overrightarrow{c_1 c_2}$
Le choc se calcule dans le référentiel centre de masse.
Soient $\overrightarrow{v_1}$ et $\overrightarrow{v_2}$ les vitesses avant le choc et $\overrightarrow{v_1^{'}}$ et $\overrightarrow{v_2^{'}}$ après.
$$ \overrightarrow{v_G} = \frac{m_1 \overrightarrow{v_1} + m_2 \overrightarrow{v_2}}{m_1 + m_2}$$
dans le référence centre de masse, les vitesses sont $\overrightarrow{\nu_1}$, $\overrightarrow{\nu_2}$, $\overrightarrow{\nu_1^{'}}$ et $\overrightarrow{\nu_2^{'}}$
$$ \overrightarrow{\nu_1} = \overrightarrow{v_1} - \overrightarrow{v_G}$$
Si on appelle X l'axe ($C_1 C_2)$ et Y l'axe perpendiculaire, après le choc, $\overrightarrow{\nu_1^{'}}$ vitesse de palet 1 s'obtient comme
$$ \overrightarrow{\nu_1^{'}} = -(\overrightarrow{\nu_1} \cdot \overrightarrow{e_x})\overrightarrow{e_x} + (\overrightarrow{\nu_1} \cdot \overrightarrow{e_y} )\overrightarrow{e_y} $$
après le choc,
$$ \overrightarrow{v_1^{'}} = \overrightarrow{\nu_1{'}} + \overrightarrow{v_G}$$
et
$$ \overrightarrow{v_2^{'}} = \overrightarrow{\nu_2{'}} + \overrightarrow{v_G}$$
Ensemble de fonctions pour définir divers paramètres¶
Fonctions pour référence laboratoire¶
# fonction pour calculer la masse
mass = lambda P : 3*np.pi*(1e-3)* P['radius']**2 #height = 3cm V=pi*r**2*h
# fonction pour calculer vitesse en x et y
vel_ = lambda P: (P['v']*np.cos(np.deg2rad(P['alpha'])), P['v']*np.sin(np.deg2rad(P['alpha'])))
# fonction pour calculer la position des disques
new_ = lambda P, dt: (P['x'] + P['vx']*dt, P['y'] + P['vy']*dt)
# fonction pour calculer le centre de gravité du système
get_CG = lambda P1, P2: (P1['x']*P1['m']/(P1['m']+P2['m']) + P2['x']*P2['m']/(P1['m']+P2['m']), \
P1['y']*P1['m']/(P1['m']+P2['m']) + P2['y']*P2['m']/(P1['m']+P2['m']))
# fonction pour calculer la distance entre les deux disques
dist_ = lambda P1, P2: math.sqrt((P1['x'] - P2['x'])**2 + (P1['y'] - P2['y'])**2)
# calculer la projection de la vitesse sur un vecteur
proj_v = lambda P, vecteur: (P['vx']*vecteur[0] + P['vy']*vecteur[1])/(np.linalg.norm(x))**2
proj_v_vec = lambda P, x: proj_v(P,x)*x
# calcul énergie cinétique
e_cinetique = lambda P: 0.5*P['m']*P['v']**2/1000
# calcul velocité après une collision
def new_vels(P1, P2, u):
v1 = np.array((P1['vx'], P1['vy']))
v2 = np.array((P2['vx'], P2['vy']))
v1_f = v1 - 2*P2['m']/(P1['m']+P2['m'])*np.dot((v1-v2), u)/(np.dot(u,u))*u
v2_f = v2 - 2*P1['m']/(P1['m']+P2['m'])*np.dot((v2-v1), -u)/(np.dot(u,u))*(-u)
return v1_f, v2_f
def convert_to_array(P):
for k in ['x', 'init_x', 'y', 'init_y']:
P[k] = np.array(P[k])
Fonctions pour référence centre de gravité¶
# fonction pour calculer la trajectoire
trajCG_ = lambda P, t, dt: [P['x'] + P['CGv_x']*np.linspace(0, t, t/dt)/10, P['y'] + P['CGv_y']*np.linspace(0, t, t/dt)/10]
# fonction pour calculer la position des disques
newCG_ = lambda P, dt: (P['x'] + P['CGv_x']*dt, P['y'] + P['CGv_y']*dt)
# calculer la projection de la vitesse sur un vecteur
projCG_v = lambda P, x: (P['CGv_x']*x[0] + P['CGv_y']*x[1])/(np.linalg.norm(x))**2
def new_velsCG(P1, P2, u):
v1 = np.array((P1['CGv_x'], P1['CGv_y']))
v2 = np.array((P2['CGv_x'], P2['CGv_y']))
v1_f = v1 - 2*P2['m']/(P1['m']+P2['m'])*np.dot((v1-v2), u)/(np.dot(u,u))*u
v2_f = v2 - 2*P1['m']/(P1['m']+P2['m'])*np.dot((v2-v1), -u)/(np.dot(u,u))*(-u)
return v1_f, v2_f
Ensemble de fonctions to mettre à jour divers paramètres¶
Référence laboratoire¶
# Valeurs a t=0
def initialize(t, x_a, x_b, y_a, y_b, v_a, v_b, alpha_a, alpha_b, rad_a, rad_b):
A = {'x': x_a, 'y': y_a, 'v':v_a, 'alpha': alpha_a, 'radius': rad_a, 't':t, 'name':'A'}
B = {'x': x_b, 'y': y_b, 'v':v_b, 'alpha': alpha_b, 'radius': rad_b, 't':t, 'name':'B'}
A['vx'], A['vy'] = vel_(A)
B['vx'], B['vy'] = vel_(B)
A['init_vx'], A['init_vy'] = A['vx'], A['vy']
B['init_vx'], B['init_vy'] = B['vx'], B['vy']
A['m'], B['m'] = mass(A), mass(B)
A['E'], B['E'] = [e_cinetique(A)], [e_cinetique(B)]
A['vitesse'], B['vitesse'] = [A['v']], [B['v']]
CG = {}
CG['m'] = (A['m'] + B['m'])
CG['x'] = (1/CG['m']) * (A['x']*A['m'] + B['x']*B['m'])
CG['y'] = (1/CG['m']) * (A['y']*A['m'] + B['y']*B['m'])
CG['vx'] = (1/CG['m']) * (A['vx']*A['m'] + B['vx']*B['m'])
CG['vy'] = (1/CG['m']) * (A['vy']*A['m'] + B['vy']*B['m'])
return A,B, CG
# Mettre à jour position
def update_position(P1, P2, CG, dt):
""" calculer la nouvelle position"""
P1['x'], P1['y'] = new_(P1, dt)
P2['x'], P2['y'] = new_(P2, dt)
CG['x'], CG['y'] = get_CG(P1, P2)
# Mettre à jour velocité
def update_velocity(P1, P2):
""" calculer la velocité après collision entre les deux particules"""
# calculer vecteur collision
u = np.array([P2['x'] - P1['x'], P2['y'] - P1['y']])
# point collision
p_x = P1['x'] + u[0]*P1['radius']/dist_(P1,P2)
p_y = P1['y'] + u[1]*P1['radius']/dist_(P1,P2)
# mettre à jour velocité après collision
p1_v, p2_v = new_vels(P1, P2, u)
P1['vx'], P1['vy'] = p1_v[0], p1_v[1]
P2['vx'], P2['vy'] = p2_v[0], p2_v[1]
P1['v'] = math.sqrt(P1['vx']**2 + P1['vy']**2)
P2['v'] = math.sqrt(P2['vx']**2 + P2['vy']**2)
def verifier_limites(P, scenario):
""" calculer velocité après collision entre particules et un des limites du scénario"""
col = False # boolean pour indiquer si il y a collision ou pas
sf = 1.1 #safety factor for plotting
# verifier si il y a une collision entre P et limite superiéure
if P['y'] >= scenario['y_MAX'] - P['radius']*sf: # -5 pour épaisseur de la ligne
col = True
u = np.array([0, -1])
w = np.array([1, 0])
# verifier si il y a une collision entre P et limite inferiéure
elif P['y'] <= scenario['y_MIN'] + P['radius']*sf: #+3
col = True
u = np.array([0, 1])
w = np.array([-1, 0])
# verifier si il y a une collision entre P et limite droite
elif P['x'] >= scenario['x_MAX'] - P['radius']*sf: #-3
col = True
u = np.array([-1, 0])
w = np.array([0, 1])
# verifier si il y a une collision entre P et limite gauche
elif P['x'] <= scenario['x_MIN'] + P['radius']*sf: #+3
col = True
u = np.array([1, 0])
w = np.array([0, -1])
# si il y a eu une collision, mettre à jour la velocité
if col:
v = np.array((P['vx'], P['vy']))
v_w = np.dot(v,w)/(np.dot(w,w))*w
v_u = -(v - v_w)
P['vx'], P['vy'] = v_u + v_w
P['v'] = math.sqrt(P['vx']**2 + P['vy']**2)
def update_values_time(P1, P2, CG, dt, scenario):
""" calcules évolution des valeurs dans le temps"""
t = 0
P1['traj'] = [(P1['x'], P1['y'])]
P2['traj'] = [(P2['x'], P2['y'])]
CG['traj'] = [(CG['x'], CG['y'])]
P1['init_x'] = P1['x']
P1['init_y'] = P1['y']
P2['init_x'] = P2['x']
P2['init_y'] = P2['y']
CG['init_x'] = CG['x']
CG['init_y'] = CG['y']
P1['E'], P2['E'] = [e_cinetique(P1)], [e_cinetique(P2)]
P1['vitesse'], P2['vitesse'] = [P1['v']], [P2['v']]
CG['vitesse'] = [(CG['vx'], CG['vy'])]
while t < min(P1['t'], P2['t']):
# calculer prochain possition
update_position(P1, P2, CG, dt)
P1['E'].append(e_cinetique(P1))
P2['E'].append(e_cinetique(P2))
P1['vitesse'].append(P1['v'])
P2['vitesse'].append(P2['v'])
P1['traj'].append((P1['x'], P1['y']))
P2['traj'].append((P2['x'], P2['y']))
CG['traj'].append((CG['x'], CG['y']))
# verifier si il y a une collision entre P1 et P2
if dist_(P1, P2) < (P1['radius']*1.075 + P2['radius']*1.075):
update_velocity(P1, P2)
# verifier si il y a collision avec les limites du scenario
verifier_limites(P1, scenario)
verifier_limites(P2, scenario)
CG_vx = (1/CG['m']) * (P1['vx']*P1['m'] + P2['vx']*P2['m'])
CG_vy = (1/CG['m']) * (P1['vy']*P1['m'] + P2['vy']*P2['m'])
CG['vitesse'].append((CG_vx, CG_vy))
t += dt
P1['traj'] = np.array(P1['traj'])
P2['traj'] = np.array(P2['traj'])
CG['traj'] = np.array(CG['traj'])
P1['E'] = np.array(P1['E'])
P2['E'] = np.array(P2['E'])
P1['vitesse'] = np.array(P1['vitesse'])
P2['vitesse'] = np.array(P2['vitesse'])
def generate_figure(scenario, source_A, source_B, source_A_trj, source_B_trj, source_CG, A, B, CG, alpha):
line_width_grille = 0.5
alpha_grille = 0.3
# Figure référence laboratoire
p = figure(title="Collision", height=448, width=896, y_range=(scenario['y_MIN'], scenario['y_MAX']),
x_range=(scenario['x_MIN'], scenario['x_MAX']),
background_fill_color='#ffffff')
p.toolbar.logo = None
p.toolbar_location = None
# Grille en movement pour video reference CG
x_gridlineh = np.linspace(scenario['x_MIN'], scenario['x_MAX'], 31)
y_gridlinev = np.linspace(scenario['y_MIN'], scenario['y_MAX'], 16)
x_GRID = [scenario['x_MIN'], scenario['x_MAX']]
y_GRID = [scenario['y_MIN'], scenario['y_MAX']]
line_width_grille = 0.5
alpha_grille = 0.25
g_horizontale = []
for idx, y_val in enumerate(y_gridlinev):
if idx == 0 or idx == 15:
g_horizontale.append(p.line(x=x_GRID, y=[y_val]*2, line_color='#000000', \
alpha=1, line_width=line_width_grille*3))
else:
g_horizontale.append(p.line(x=x_GRID, y=[y_val]*2, line_color='#000000', \
alpha=alpha_grille, line_width=line_width_grille))
g_verticale = []
for idx, x_val in enumerate(x_gridlineh):
if idx == 0 or idx == 30:
g_verticale.append(p.line(x=[x_val]*2, y=y_GRID, line_color='#000000', \
alpha=1, line_width=line_width_grille*3))
else:
g_verticale.append(p.line(x=[x_val]*2, y=y_GRID, line_color='#000000', \
alpha=alpha_grille, line_width=line_width_grille))
# limites du scénario
p.line(x=np.linspace(scenario['x_MIN'],scenario['x_MAX'], 3), y=[scenario['y_MIN']]*3, line_color='#000000', line_width=2)
p.line(x=np.linspace(scenario['x_MIN'],scenario['x_MAX'], 3), y=[scenario['y_MAX']]*3, line_color='#000000', line_width=2)
p.line(x=[scenario['x_MIN']]*3, y=np.linspace(scenario['y_MIN'],scenario['y_MAX'], 3), line_color='#000000', line_width=2)
p.line(x=[scenario['x_MAX']]*3, y=np.linspace(scenario['y_MIN'],scenario['y_MAX'], 3), line_color='#000000', line_width=2)
# Disques
P_A = p.circle(x='x', y='y', radius='radius', source=source_A, fill_color='#e32020', line_color='#e32020', alpha=0.8)
P_B = p.circle(x='x', y='y', radius='radius', source=source_B, fill_color='#0ABDE3', line_color='#0ABDE3', alpha=0.8)
# Trajectoire
Traj_A = p.line(x='x_t', y='y_t', source=source_A_trj, color='#e32020', line_dash='dashed')
Traj_B = p.line(x='x_t', y='y_t', source=source_B_trj, color='#0ABDE3', line_dash='dashed')
# Centre de gravité du système de masses
p_CG = p.scatter(x='x', y='y', source=source_CG, size=3, fill_color='#000000', line_color='#000000')
# Vecteur vitesse centre de gravité
l_vel_CG = p.line(x=np.linspace(CG['init_x'], CG['init_x'] + CG['vx']*5, 2), \
y=np.linspace(CG['init_y'], CG['init_y'] + CG['vy']*5, 2), \
line_width=1, line_color='#000000')
# Vecteur vitesse A et B
l_vel_A = p.line(x=np.linspace(A['init_x'], A['init_x'] + A['init_vx']*5, 2), \
y=np.linspace(A['init_y'], A['init_y'] + A['init_vy']*5, 2), \
line_width=1, line_color='#000000')
A_arrow = p.scatter(marker='triangle', x=[A['init_x'] + A['init_vx']*5], y =[A['init_y'] + A['init_vy']*5], \
angle=alpha, color='#000000', size=5)
l_vel_B = p.line(x=np.linspace(B['init_x'], B['init_x'] + B['init_vx']*5, 2), \
y=np.linspace(B['init_y'], B['init_y'] + B['init_vy']*5, 2), \
line_width=1, line_color='#000000')
B_arrow = p.scatter(marker='triangle', x=[B['init_x'] + B['init_vx']*5], y =[B['init_y'] + B['init_vy']*5], \
angle=alpha, color='#000000', size=5)
CG_arrow = p.scatter(marker='triangle', x=[CG['init_x'] + CG['vx']*5], y =[CG['init_y'] + CG['vy']*5], \
angle=alpha, color='#000000', size=5)
legend = Legend(items=[
("A" , [P_A]),
("B" , [P_B]),
("trajectoire A" , [Traj_A]),
("trajectoire B" , [Traj_B]),
('CG', [p_CG]),
('vitesse', [l_vel_CG]),
], location="center")
p.add_layout(legend, 'above')
p.legend.orientation = "horizontal"
p.ygrid.visible = False
p.xgrid.visible = False
return p, g_horizontale, g_verticale, P_A, P_B, Traj_A, Traj_B, p_CG, l_vel_CG, CG_arrow, l_vel_A, A_arrow, l_vel_B, B_arrow
def create_source(A,B,CG):
source_A = ColumnDataSource(data = {'x':[A['init_x']], 'y':[A['init_y']], 'radius':[A['radius']]})
source_B = ColumnDataSource(data = {'x':[B['init_x']], 'y':[B['init_y']], 'radius':[B['radius']]})
source_A_trj = ColumnDataSource(data= {'x_t': A['traj'][:,0], 'y_t': A['traj'][:,1]})
source_B_trj = ColumnDataSource(data= {'x_t': B['traj'][:,0], 'y_t': B['traj'][:,1]})
source_CG = ColumnDataSource(data = {'x': [CG['init_x']], 'y': [CG['init_y']]})
# Vecteur vitesse CG
if CG['init_x'] + CG['vx']*5 != 0:
alpha = np.rad2deg(np.arctan((CG['init_y'] + CG['vy']*5)/(CG['init_x'] + CG['vx']*5)))
else:
alpha = np.rad2deg(np.arctan(10**9))
return source_A, source_B, source_A_trj, source_B_trj, source_CG, alpha
LET'S PLAY!¶
## PLOT
def modify_doc(doc):
# Conditions initiales
dt = 0.005
T = 10
x_a, x_b = -25, 25
y_a, y_b = 0,0
v_a, v_b = 1.5, 1.5 #[cm/s]
alpha_a, alpha_b = 0, 180
rad_a, rad_b = 2.5, 2.5
# définir limits du scenario (cm)
Y_MAX = 50
Y_MIN = -50
X_MAX = 100
X_MIN = -100
scenario = {'y_MAX': Y_MAX, 'y_MIN': Y_MIN, 'x_MAX': X_MAX, 'x_MIN': X_MIN}
# Calculer les valeurs initiales - référence lab
A, B, CG = initialize(T, x_a, x_b, y_a, y_b, v_a, v_b, alpha_a, alpha_b, rad_a, rad_b)
update_values_time(A, B, CG, dt, scenario)
source_A, source_B, source_A_trj, source_B_trj, source_CG, alpha = create_source(A,B,CG)
source_A_cg, source_B_cg, source_A_trj_cg, source_B_trj_cg, source_CG_cg, alpha_cg = create_source(A,B,CG)
source_VG = ColumnDataSource(data = {'x': np.linspace(CG['init_x'], CG['init_x'] + CG['vx']*5, 10), \
'y': np.linspace(CG['init_y'], CG['init_y'] + CG['vy']*5, 10)})
source_E = ColumnDataSource(data={'A': np.linspace(0,A['E'][0],10), 'B': np.linspace(A['E'][0], A['E'][0]+B['E'][0],10),
'vA': np.linspace(0, A['vitesse'][0], 10), 'vB': np.linspace(0, B['vitesse'][0], 10),
'y': [0]*10, 'yA':[4]*10, 'yB':[8]*10})
# Figure Énergie
p_graphs = figure(height=100, width=896, y_range=[-3,3], title='Énergie cinétique [kJ]')
p_graphs.toolbar.logo = None
p_graphs.toolbar_location = None
p_graphs.yaxis.ticker = [0]
p_graphs.yaxis.major_label_overrides = {0: 'Énergie'}
EC_A = p_graphs.line(y='y', x='A', line_width=5, source=source_E, line_color='#e32020')
EC_B = p_graphs.line(y='y', x='B', line_width=5, source=source_E, line_color='#0ABDE3')
line_width_grille = 0.5
alpha_grille = 0.3
p_ref_abs, g_horizontale, g_verticale, P_A, P_B, Traj_A, Traj_B, p_CG, \
l_vel_CG, CG_arrow, l_vel_A, A_arrow, l_vel_B, B_arrow =\
generate_figure(scenario, source_A, source_B, source_A_trj, source_B_trj, source_CG, A, B, CG, alpha)
p_ref_cg, g_horizontale_cg, g_verticale_cg, P_A_cg, P_B_cg, Traj_A_cg, Traj_B_cg, \
p_CG_cg, l_vel_CG_cg, CG_arrow_cg, l_vel_A_cg, A_arrow_cg, l_vel_B_cg, B_arrow_cg =\
generate_figure(scenario, source_A_cg, source_B_cg, source_A_trj_cg, source_B_trj_cg, source_CG_cg, A, B, CG, alpha_cg)
# Définir sliders
slider_Ax = Slider(start=-95, end=95, value=-25, step=1, title='Ax [cm]:')
slider_Bx = Slider(start=-95, end=95, value=25, step=1, title='Bx [cm]:')
slider_Ay = Slider(start=-45, end=45, value=0, step=1, title='Ay [cm]:')
slider_By = Slider(start=-45, end=45, value=0, step=1, title='By [cm]:')
slider_Ar = Slider(start=1, end=5, value=2.5, step=0.1, title='Rayon A [cm]:')
slider_Br = Slider(start=1, end=5, value=2.5, step=0.1, title='Rayon B [cm]:')
slider_Av = Slider(start=-40, end=40, value=15, step=1, title='Vitesse A [cm/s]:')
slider_Bv = Slider(start=-40, end=40, value=15, step=1, title='Vitesse [cm/s]:')
slider_A_alpha = Slider(start=0, end=360, value=0, step=5, title='α A [º]:')
slider_B_alpha = Slider(start=0, end=360, value=180, step=5, title='α B [º]:')
slider_T = Slider(start=2, end=20, value=10, step=1, title='temps simulation')
play = Button(label='play')
reset = Button(label='reset')
export = Button(label='export video')
file_name = TextInput(title='File name', value='collision')
def read_sliders():
x_a = slider_Ax.value
x_b = slider_Bx.value
y_a = slider_Ay.value
y_b = slider_By.value
v_a = slider_Av.value
v_b = slider_Bv.value
alpha_a = slider_A_alpha.value
alpha_b = slider_B_alpha.value
rad_a = slider_Ar.value
rad_b = slider_Br.value
T = slider_T.value
A, B, CG = initialize(T, x_a, x_b, y_a, y_b, v_a, v_b, alpha_a, alpha_b, rad_a, rad_b)
update_values_time(A, B, CG, dt, scenario)
return A, B, CG
def refresh_source(attrname, old, new):
l_vel_CG.visible = True
CG_arrow.visible= True
l_vel_CG_cg.visible = False
CG_arrow_cg.visible= False
Traj_A_cg.visible = False
Traj_B_cg.visible = False
A, B, CG = read_sliders()
source_A.data = {'x':[A['init_x']], 'y':[A['init_y']], 'radius':[A['radius']]}
source_B.data = {'x':[B['init_x']], 'y':[B['init_y']], 'radius':[B['radius']]}
source_A_cg.data = {'x':[A['init_x'] - CG['init_x']], 'y':[A['init_y'] - CG['init_y']], 'radius':[A['radius']]}
source_B_cg.data = {'x':[B['init_x'] - CG['init_x']], 'y':[B['init_y'] - CG['init_y']], 'radius':[B['radius']]}
source_A_trj.data = {'x_t': A['traj'][:,0], 'y_t': A['traj'][:,1]}
source_B_trj.data = {'x_t': B['traj'][:,0], 'y_t': B['traj'][:,1]}
source_A_trj_cg.data = {'x_t': A['traj'][:,0] - CG['traj'][:,0], 'y_t': A['traj'][:,1] - CG['traj'][:,1]}
source_B_trj_cg.data = {'x_t': B['traj'][:,0] - CG['traj'][:,0], 'y_t': B['traj'][:,1] - CG['traj'][:,1]}
source_CG.data = {'x': [CG['init_x']], 'y': [CG['init_y']]}
source_E.data = {'A': np.linspace(0, A['E'][0], 10),
'B': np.linspace(A['E'][0], A['E'][0] + B['E'][0], 10), 'y':[0]*10}
if CG['init_x'] + CG['vx']*5 != 0:
alpha = np.rad2deg(np.arctan((CG['init_y'] + CG['vy']*5)/(CG['init_x'] + CG['vx']*5)))
else:
alpha = np.rad2deg(np.arctan(10**9))
mult = 2
l_vel_CG.data_source.data['x'] = np.linspace(CG['init_x'], CG['init_x'] + CG['vx']*mult, 2)
l_vel_CG.data_source.data['y'] = np.linspace(CG['init_y'], CG['init_y'] + CG['vy']*mult, 2)
CG_arrow.data_source.data['x'] = [CG['init_x'] + CG['vx']*mult]
CG_arrow.data_source.data['y'] = [CG['init_y'] + CG['vy']*mult]
CG_arrow.data_source.data['angle'] = [-alpha]
# vecteur velocité absolute
for l, arrow, S in zip([l_vel_A, l_vel_B], [A_arrow, B_arrow], [A, B]):
l.data_source.data['x'] = np.linspace(S['init_x'], S['init_x'] + S['init_vx']*mult, 2)
l.data_source.data['y'] = np.linspace(S['init_y'], S['init_y'] + S['init_vy']*mult, 2)
arrow.data_source.data['x'] = [S['init_x'] + S['init_vx']*mult]
arrow.data_source.data['y'] = [S['init_y'] + S['init_vy']*mult]
arrow.data_source.data['angle'] = [-alpha]
# vecteur velocité relative au CG
for l, arrow, S in zip([l_vel_A_cg, l_vel_B_cg], [A_arrow_cg, B_arrow_cg], [A, B]):
l.data_source.data['x'] = np.linspace(S['init_x']- CG['init_x'], S['init_x']- CG['init_x'] + (S['init_vx'] - CG['vx'])*mult,2)
l.data_source.data['y'] = np.linspace(S['init_y']- CG['init_y'], S['init_y']- CG['init_y'] + (S['init_vy'] - CG['vy'])*mult,2)
arrow.data_source.data['x'] = [S['init_x']- CG['init_x'] + (S['init_vx'] - CG['vx'])*mult]
arrow.data_source.data['y'] = [S['init_y']- CG['init_y'] + (S['init_vy'] - CG['vy'])*mult]
arrow.data_source.data['angle'] = [-alpha_cg]
for g, g_abs in zip(g_horizontale_cg, g_horizontale):
g.data_source.data['y'] = g_abs.data_source.data['y'] - np.array(CG['init_y'])
for g, g_abs in zip(g_verticale_cg, g_verticale):
g.data_source.data['x'] = g_abs.data_source.data['x'] - np.array(CG['init_x'])
x_horizontale = [g_verticale_cg[0].data_source.data['x'][0], g_verticale_cg[-1].data_source.data['x'][0]]
y_verticale = [g_horizontale_cg[0].data_source.data['y'][0], g_horizontale_cg[-1].data_source.data['y'][0]]
for g in g_horizontale_cg:
g.data_source.data['x'] = x_horizontale
for g in g_verticale_cg:
g.data_source.data['y'] = y_verticale
slider_Ax.on_change('value', refresh_source)
slider_Bx.on_change('value', refresh_source)
slider_Ay.on_change('value', refresh_source)
slider_By.on_change('value', refresh_source)
slider_Av.on_change('value', refresh_source)
slider_Bv.on_change('value', refresh_source)
slider_A_alpha.on_change('value', refresh_source)
slider_B_alpha.on_change('value', refresh_source)
slider_Ar.on_change('value', refresh_source)
slider_Br.on_change('value', refresh_source)
slider_T.on_change('value', refresh_source)
def reset_animation(event):
Traj_A.visible = True
Traj_B.visible = True
p_CG.visible = True
for l in [l_vel_CG, l_vel_A, l_vel_B, l_vel_CG_cg, l_vel_A_cg, l_vel_B_cg]:
l.visible = True
for arrow in [A_arrow, B_arrow, CG_arrow, A_arrow_cg, B_arrow_cg, CG_arrow_cg]:
arrow.visible = True
A, B, CG = read_sliders()
idx = 0
# Grille en movement pour video reference CG
x_gridlineh = np.linspace(scenario['x_MIN'], scenario['x_MAX'], 31)
y_gridlinev = np.linspace(scenario['y_MIN'], scenario['y_MAX'], 16)
for g, y_val in zip(g_horizontale_cg, y_gridlinev):
g.data_source.data['y'] = [y_val]*2
g.data_source.data['x'] = [scenario['x_MIN'], scenario['x_MAX']]
for g, x_val in zip(g_verticale_cg, x_gridlineh):
g.data_source.data['x'] = [x_val]*2
g.data_source.data['y'] = [scenario['y_MIN'], scenario['y_MAX']]
source_A.data = {'x':[A['traj'][idx,0]], 'y':[A['traj'][idx,1]], 'radius':[A['radius']]}
source_A_cg.data = {'x':[A['traj'][idx,0] - CG['traj'][idx, 0]], 'y':[A['traj'][idx,1] - CG['traj'][idx, 1]], 'radius':[A['radius']]}
source_B.data = {'x':[B['traj'][idx,0]], 'y':[B['traj'][idx,1]], 'radius':[B['radius']]}
source_B_cg.data = {'x':[B['traj'][idx,0] - CG['traj'][idx, 0]], 'y':[B['traj'][idx,1] - CG['traj'][idx, 1]], 'radius':[B['radius']]}
source_CG.data = {'x': [CG['traj'][idx, 0]], 'y': [CG['traj'][idx, 1]]}
source_E.data = {'A': np.linspace(0, A['E'][idx], 10),
'B': np.linspace(A['E'][idx], A['E'][idx] + B['E'][idx], 10), 'y':[0]*10}
def play_animation(event):
Traj_A.visible = True
Traj_B.visible = True
p_CG.visible = True
for l in [l_vel_CG, l_vel_A, l_vel_B ]:
l.visible = False
for arrow in [A_arrow, B_arrow, CG_arrow]:
arrow.visible=False
A, B, CG = read_sliders()
# animation référence absolute
t = 0
idx = 0
images = []
sleep(0.1)
while t < A['t']:
# mettre à jour tous les paramètres pour chaque dt
source_A.data = {'x':[A['traj'][idx,0]], 'y':[A['traj'][idx,1]], 'radius':[A['radius']]}
source_B.data = {'x':[B['traj'][idx,0]], 'y':[B['traj'][idx,1]], 'radius':[B['radius']]}
source_CG.data = {'x': [CG['traj'][idx, 0]], 'y': [CG['traj'][idx, 1]]}
source_E.data = {'A': np.linspace(0, A['E'][idx], 10),
'B': np.linspace(A['E'][idx], A['E'][idx] + B['E'][idx], 10), 'y':[0]*10}
t += dt
idx += 1
# animation réfé
t = 0
idx = 0
images = []
sleep(0.1)
Traj_A_cg.visible = False
Traj_B_cg.visible = False
speed = 15
for l in [l_vel_CG_cg, l_vel_A_cg, l_vel_B_cg]:
l.visible = False
for arrow in [A_arrow_cg, B_arrow_cg, CG_arrow_cg]:
arrow.visible=False
while t < A['t']:
# mettre à jour tous les paramètres pour chaque dt
source_A_cg.data = {'x':[A['traj'][idx,0] - CG['traj'][idx, 0]], 'y':[A['traj'][idx,1] - CG['traj'][idx, 1]], 'radius':[A['radius']]}
source_B_cg.data = {'x':[B['traj'][idx,0] - CG['traj'][idx, 0]], 'y':[B['traj'][idx,1] - CG['traj'][idx, 1]], 'radius':[B['radius']]}
source_CG_cg.data = {'x': [CG['traj'][idx, 0] - CG['traj'][idx, 0]], 'y': [CG['traj'][idx, 1] - CG['traj'][idx, 1]]}
for g in g_horizontale_cg:
g.data_source.data['y'] += -CG['vitesse'][idx][1]*dt*speed
for g in g_verticale_cg:
g.data_source.data['x'] += -CG['vitesse'][idx][0]*dt*speed
x_horizontale = [g_verticale_cg[0].data_source.data['x'][0], g_verticale_cg[-1].data_source.data['x'][0]]
y_verticale = [g_horizontale_cg[0].data_source.data['y'][0], g_horizontale_cg[-1].data_source.data['y'][0]]
for g in g_horizontale_cg:
g.data_source.data['x'] = x_horizontale
for g in g_verticale_cg:
g.data_source.data['y'] = y_verticale
t += dt*speed
idx += speed
def record(event):
Traj_A.visible = True
Traj_B.visible = True
p_CG.visible = True
for l in [l_vel_CG, l_vel_A, l_vel_B ]:
l.visible = False
for arrow in [A_arrow, B_arrow, CG_arrow]:
arrow.visible=False
A, B, CG = read_sliders()
with imageio.get_writer(file_name.value+'.mov', mode='I') as writer:
t = 0
idx = 0
images = []
speed = 20
sleep(0.1)
while t < A['t']:
# mettre à jour tous les paramètres pour chaque dt
print('\r Vidéo 1 en cours de génération. {:0.2f} % '.format(100*(t)/A['t']) , end="")
source_A.data = {'x':[A['traj'][idx,0]], 'y':[A['traj'][idx,1]], 'radius':[A['radius']]}
source_B.data = {'x':[B['traj'][idx,0]], 'y':[B['traj'][idx,1]], 'radius':[B['radius']]}
source_CG.data = {'x': [CG['traj'][idx, 0]], 'y': [CG['traj'][idx, 1]]}
source_E.data = {'A': np.linspace(0, A['E'][idx], 10),
'B': np.linspace(A['E'][idx], A['E'][idx] + B['E'][idx], 10), 'y':[0]*10}
t += dt*speed
idx += speed
fn = "plot_" + str(idx) + ".png"
export_png(p_ref_abs, filename = fn)
writer.append_data(imageio.imread(fn))
os.remove(fn)
print("\rLa vidéo '{}' a été générée correctement".format(file_name.value), end="\n")
with imageio.get_writer(file_name.value+'_CG.mov', mode='I') as writer:
t = 0
idx = 0
images = []
sleep(0.1)
Traj_A_cg.visible = False
Traj_B_cg.visible = False
speed = 15
for l in [l_vel_CG_cg, l_vel_A_cg, l_vel_B_cg]:
l.visible = False
for arrow in [A_arrow_cg, B_arrow_cg, CG_arrow_cg]:
arrow.visible=False
while t < A['t']:
# mettre à jour tous les paramètres pour chaque dt
print('\r Vidéo 2 en cours de génération. {:0.2f} % '.format(100*(t)/A['t']) , end="")
source_A_cg.data = {'x':[A['traj'][idx,0] - CG['traj'][idx, 0]], 'y':[A['traj'][idx,1] - CG['traj'][idx, 1]], 'radius':[A['radius']]}
source_B_cg.data = {'x':[B['traj'][idx,0] - CG['traj'][idx, 0]], 'y':[B['traj'][idx,1] - CG['traj'][idx, 1]], 'radius':[B['radius']]}
source_CG_cg.data = {'x': [CG['traj'][idx, 0] - CG['traj'][idx, 0]], 'y': [CG['traj'][idx, 1] - CG['traj'][idx, 1]]}
for g in g_horizontale_cg:
g.data_source.data['y'] += -CG['vitesse'][idx][1]*dt*speed
for g in g_verticale_cg:
g.data_source.data['x'] += -CG['vitesse'][idx][0]*dt*speed
x_horizontale = [g_verticale_cg[0].data_source.data['x'][0], g_verticale_cg[-1].data_source.data['x'][0]]
y_verticale = [g_horizontale_cg[0].data_source.data['y'][0], g_horizontale_cg[-1].data_source.data['y'][0]]
for g in g_horizontale_cg:
g.data_source.data['x'] = x_horizontale
for g in g_verticale_cg:
g.data_source.data['y'] = y_verticale
t += dt*speed
idx += speed
fn = "plot_" + str(idx) + ".png"
export_png(p_ref_cg, filename = fn)
writer.append_data(imageio.imread(fn))
os.remove(fn)
print("\rLa vidéo '{}_CG' a été générée correctement".format(file_name.value), end="\n")
play.on_event(ButtonClick, play_animation)
reset.on_event(ButtonClick, reset_animation)
export.on_event(ButtonClick, record)
if EXPORT:
layout = column(row(export, file_name),\
row(slider_Ax, slider_Bx),\
row(slider_Ay, slider_By), \
row(slider_Ar, slider_Br),\
row(slider_Av, slider_Bv),\
row( slider_A_alpha, slider_B_alpha), \
row(play, reset, slider_T), row(p_ref_abs), row(p_graphs),row(p_ref_cg))
else:
layout = column(row(slider_Ax, slider_Bx),\
row(slider_Ay, slider_By), \
row(slider_Ar, slider_Br),\
row(slider_Av, slider_Bv),\
row( slider_A_alpha, slider_B_alpha), \
row(play, reset, slider_T), row(p_ref_abs), row(p_graphs),row(p_ref_cg))
doc.add_root(layout)
def remote_jupyter_proxy_url(port):
"""
Callable to configure Bokeh's show method when a proxy must be
configured.
If port is None we're asking about the URL
for the origin header.
"""
base_url = os.environ['EXTERNAL_URL']
host = urllib.parse.urlparse(base_url).netloc
# If port is None we're asking for the URL origin
# so return the public hostname.
if port is None:
return host
service_url_path = os.environ['JUPYTERHUB_SERVICE_PREFIX']
proxy_url_path = 'proxy/%d' % port
user_url = urllib.parse.urljoin(base_url, service_url_path)
full_url = urllib.parse.urljoin(user_url, proxy_url_path)
return full_url
def show_document(doc):
servers = list(serverapp.list_running_servers())
if servers:
server = servers[0]
if server['hostname'] == 'localhost':
show(doc)
else:
show(doc, notebook_url=remote_jupyter_proxy_url)
else:
print("No running Jupyter notebook servers found.")
show_document(modify_doc)