xxxxxxxxxx
// requires https://cdn.jsdelivr.net/npm/tweakpane@3.0.5/dist/tweakpane.min.js
//*** Résonance d'un pendule élastique de pulsation propre w0=2π rad/s.
//*** méthode d'intégration : euler
//*** Pour la paramétrisation et la mise en équation voir cahier perso.
// © J.Roussel 13/12/2021
// moteur
var rm;//rayon du disque moteur
var a;//amplitude de l'excentrique
var b;//rayon de la poulie
var c;//ditance excentrique-poulie
var theta;//angle de rotation de l'excentrique
var freq;//(en tours par seconde)
var L;//longueur du fil
var zC;//hauteur du point de fixation du ressort
//ressort
var leq,l; //longueur à l'équilibre et à l'instant t du ressort
var lambda;//coef d'amortissement
//------ intégrateur ----
var h;//pas temorel
var S=[2];//vecteur d'état (S[0]=lt+zc et s[1]=dlt/dt+dzc/dt)
//------ animation
var tmin, tmax;//range du graphe
let G=[];
let N;
var FS1, FS2; //taille de police
//GUI
let state=true;//booleén donnant l'état on/off du moteur
let info=false;//booleéen pour afficher les infos
let pause=false;//boolén pour mettre en pause
const PARAMS = {
fréquence: 1.05, // slider fréquence
amortissement: .35, // slider lambda
};
const pane = new Tweakpane.Pane({
title: 'PARAMÈTRES',
expanded: false,
//container: document.getElementById('control'),
});
const moteurfolder = pane.addFolder({title: "EXCITATION", expanded: true});
moteurfolder.addInput(PARAMS, "fréquence", { min: .25, max:3,step:.05}).on('change',() =>{changeFreq();});
const btn = moteurfolder.addButton({title: 'ON',label: 'État',});
btn.on("click", () => {changeState();});
const oscillateurfolder = pane.addFolder({title: "RESONATEUR", expanded: true});
oscillateurfolder.addInput(PARAMS, "amortissement", {min:.2,max:2,step:.1}).on('change',() =>{changeLambda();});
const animfolder=pane.addFolder({title:"ANIMATION",expanded:true});
const infobtn = animfolder.addButton({title: 'Afficher',label:'Infos'});
infobtn.on("click", () => {toggleInfo();});
const pausebtn = animfolder.addButton({title: 'PAUSE (P)'});
pausebtn.on("click", () => {togglePause();});
const savebtn = animfolder.addButton({title: 'Copie d\'écran',});
savebtn.on("click", () => {save('myCanvas.jpg');});
function setup() {
createCanvas(windowWidth, 9*windowWidth/16);
frameRate(50); // 50 frames per second
h=1/50;
freq=1.05;
lambda=0.35;
InitialiseMoteur();
InitialiseString();
InitialiseGraphe();
}
function draw() {
// background(32);
background(40,40,45);
displayTitle();
displayGraphe(tmin,tmax);
Euler();
displayMoteur();
if(state){
theta+=TWO_PI*freq*h;}
tmin+=h;
tmax+=h;
if (info) {displayInfo();}
}
function InitialiseMoteur(){
FS1 = '.03'*width;
FS2 = .5*FS1;
rm='.02'*width;
a='.5'*rm;
b=a;
c='.4'*width;
L=c+4*rm;
theta=HALF_PI;
}
function InitialiseString(){
leq=height/3;
zC=a*cos(theta)+L-c-b*HALF_PI;
S[0]=leq+zC;
S[1]=0;
}
function InitialiseGraphe(){
tmin=-16.5;
tmax=5.5;
N=-tmin/h;
for (let i=0;i<N;i+=1){
G[i]=S[0];
}
}
function displayTitle(){
textSize(FS1);
textAlign(LEFT, TOP);
noStroke();
fill(255, 255);
textLeading(26);
text("RESONANCE", rm, 5);
fill(255, 100);
text("RESONANCE", rm+1, 6);
fill(255, 127);
textSize(FS2);
text("Jimmy Roussel, 2022", rm,6+FS1);
textSize(FS1/2); fill(30,134,193);
text("femto-physique.fr",rm, 6+FS1+FS2 );
}
function displayMoteur() {
var epsilon,xcontact,ycontact;//point de contact au niveau de la poulie
var xA,yA;
push();
translate(width/3,1.5*rm);//origine au centre de l'excentrique
stroke(200);
fill(124);
strokeWeight(1);
rect(-5*rm,-1.5*rm,6.5*rm,3*rm);
fill(255,255,255);
circle(0,0,2*rm);
push();
rotate(-theta);
fill(0);
line(-rm,0,rm,0);
circle(a,0,7);
pop();
strokeWeight(2); stroke(124);
if (!state) {fill(200);} else {fill(220, 255, 220);}
rect(-'4.5'*rm, -'.75'*rm, 3*rm,1.5*rm);
fill(51, 102, 153);
noStroke();
textAlign(CENTER, CENTER);textSize(FS2);
text(nf(freq, 1, 2) +" Hz",-3*rm,0);
//poulie
translate(c,0);//origine au centre de la poulie
epsilon=(-a*sin(theta)+b)/(c-a*cos(theta));
fill(255);
stroke(200);
circle(0,0,2*b);
line(-b,0,b,0);
//calcul du contact avec la poulie
xcontact=-b*sin(epsilon);
ycontact=-b*cos(epsilon);
xA=-c+a*cos(theta);
yA=-a*sin(theta);
//calcul de la position de l'extrémité C du fil
zC=L-c-b*HALF_PI+ a*cos(theta);
stroke(100);
strokeWeight(2);
line(xcontact,ycontact,xA,yA);
arc(0,0,2*b,2*b,-HALF_PI-epsilon,0);
line(b,0,b,zC);
ellipse(b,zC,4,4);
displayRessort(b,zC,10,S[0]-zC,floor(width/50));
pop();
}
//représente un ressort de longueur lS dont l'extrméité haute se situe en (xS,yS)
function displayRessort(xS,yS,rayon,lS,Nspires){
stroke(100);strokeWeight(2);fill(100);circle(xS,yS+lS,20);
noFill();
let thetamax=Nspires*TWO_PI;
beginShape();
for (let x=0; x<=thetamax; x+=.1) {
vertex(xS+rayon*sin(x),yS+(lS-20)*x/thetamax+.5*rayon*(1-cos(x)));
}
endShape();
line(xS,yS+lS,xS,yS+lS-20);
stroke(51, 102, 153);strokeWeight(5);
point(xS,yS+lS);
}
function displayGraphe(t1,t2){
var x;
noStroke();fill(220, 255, 220);
rect(0,5*rm,width,height-10*rm);
textAlign(LEFT, TOP);
textSize(FS2);
fill(128);
for (let i=int(t1); i<=int(t2); i+=1) {
stroke(192);strokeWeight(1);
x=map(i,t1,t2,0,width);
if(i%10==0){noStroke();text(i +" s",x+5,5*rm);strokeWeight(3);stroke(192);}
line(x,5*rm,x,height-5*rm);
}
strokeWeight(1);
line(0,leq+L-c-b*HALF_PI+1.5*rm,width,leq+L-c-b*HALF_PI+1.5*rm);
for (let j=-15; j<=15; j++) {line(0,leq+L-c-b*HALF_PI+1.5*rm+j*a,width,leq+L-c-b*HALF_PI+1.5*rm+j*a);}
G.splice(0,1);
G.push(S[0]);
//stroke(255, 157, 0);
stroke(51, 102, 153);noFill();
beginShape();
for (let x=0; x<=N; x++) {
vertex(map(x,0,N,0,width/3+c+b),G[x]+1.5*rm);
}
endShape();
}
//***** Intégrateur Euler explicite *****
function Euler() {
S[0]+=h*S[1];
S[1]+=h*(-2*lambda*S[1]-4*sq(PI)*(S[0]-leq-zC));
}
function changeState(){
state=!state;
if(state){btn.title='ON'}else{btn.title='OFF'};
}
function changeFreq() {
freq=PARAMS.fréquence;
}
function changeLambda() {
lambda=PARAMS.amortissement;
}
function toggleInfo() {
info=!info;
if(info){infobtn.title='Masquer'}else{infobtn.title='Afficher'};
}
function togglePause() {
pause=!pause;
if(pause){noLoop();pausebtn.title='PLAY (P)'}else{loop();pausebtn.title='PAUSE (P)'};
}
function displayInfo() {
textAlign(LEFT, BOTTOM);
noStroke();
fill(55,20,20);
textSize(FS2);
textAlign(LEFT);
text(" CONTEXTE :\n Un pendule élastique de fréquence propre fixée à 1 Hz\n est mis en oscillation forcée à l'aide d'un moteur à excentrique.\n",2*rm,height/2);
textAlign(LEFT, TOP);
text("L'équation différentielle du mouvement est du type\n\n x''(t)+ 2\u03BB x'(t)+ \u03C9\u00B2 x(t)=\u03C9\u00B2 e(t)\n\n où e(t) est le déplacement du point d'attache\n et \u03C9 la pulsation propre (2π rad/s)",2*rm,height/2);
}
function windowResized() {
resizeCanvas(windowWidth, 9*windowWidth/16);
InitialiseMoteur();
InitialiseString();
InitialiseGraphe();
}
function keyPressed() {
if (key =='p' || key == 'P') {togglePause();}
}