// physics constants float rho = 0.2; // one fith of water float rad = 1; // 1 cm float totalMeters = 30; // 30 cm :) float springl = totalMeters / 4; float springk = 0.8; // spring should be stationary around double length // animation constants float frames = 30; float iterations = 10; // center of gravity and current angle pt cog = new pt(totalMeters/2,totalMeters*0.4); pt springpt = new pt ( totalMeters/2,0); vec velo = new vec(0,0); float angle = 0; float anglevelo = 0; float sc; boolean boundaryCheck = true; void setup () { size(600,600); framerate(floor(frames)); sc = 1.0 / totalMeters * width; } void draw () { background(255); animStep(); if ( mousePressed && !keyPressed) { springpt.setFromMouse(); springpt.mul(1.0/sc); } // draw the two circles pushMatrix(); translate(cog.x*sc, cog.y*sc ); rotate(-angle); translate(-rad*sc,0); scale (sc); ellipseMode (CENTER); strokeWeight(0.01); stroke(0); fill (0,0,255); ellipse (-rad,0,2.0*rad,2.0*rad); fill (255,255,255); ellipse (rad,0,2.0*rad,2.0*rad); line(-rad,0,rad,0); popMatrix(); // draw the spring pushMatrix(); strokeWeight(0.1); vec v = new vec (springpt.x-cog.x-rad,-(springpt.y-cog.y)); float rot = atan2(v.x, v.y); translate (cog.x*sc,cog.y*sc); rotate(rot); translate(-rad*sc,0); scale (sc); float sub = springk*20; line ( rad,0, rad,-v.norm()/sub/2 ); line ( rad,-v.norm()/sub/2, rad-0.5, -v.norm()/sub ); line ( rad,-v.norm()*(1-1/sub/2),rad,-v.norm() ); line ( rad-0.5,-v.norm()*(1-1/sub),rad,-v.norm()*(1-1/sub/2) ); for ( int i =1; i < sub-1; ++i ) { float off1 = -v.norm()/sub*(i); float off2 = -v.norm()/sub*(i+0.5); float off3 = -v.norm()/sub*(i+1); line (rad-0.5, off1, rad+0.5, off2 ); line (rad+0.5, off2, rad-0.5, off3 ); } popMatrix(); } void animStep () { // 1 mm thick float m = 2*rho*PI*sq(rad)*0.1; // use parallel axis rule for both discs float inertia = sq(rad)*m/2*2*(m*sq(rad)); float dt = 1.0 / (float)frames / (float)iterations; for ( int i =0; i < iterations; ++i) { // gravitation vec oldvelo = velo.makeCopy(); float oldanglevelo = anglevelo; velo.y += 9.81 * dt ; // spring force vec v = new vec (springpt.x-cog.x-rad,-(springpt.y-cog.y)); float famount = ((v.norm() - springl) * springk / m)*dt; vec dir = v.makeCopy(); dir.unit(); dir.mul(famount); velo.y -= dir.y; velo.x += dir.x; if (boundaryCheck) { vec oldvelotmp = oldvelo.makeCopy (); oldvelotmp.add(velo); oldvelotmp.mul(0.5*dt); pt cogtmp = cog.makeCopy (); cogtmp.addVec(oldvelotmp); if (cogtmp.x < 2*rad || cogtmp.x > totalMeters-2*rad ) velo.x *= -1; if (cogtmp.y < rad || cogtmp.y > totalMeters-rad ) velo.y *= -1; } oldvelo.add(velo); oldvelo.mul(0.5*dt); cog.addVec(oldvelo); // angular velocity // gravity part anglevelo += -0.0981*sin(3.0*HALF_PI-angle)*rad/inertia*dt; v = new vec (springpt.x-cog.x-rad,-(springpt.y-cog.y)); float an = PI - atan2(v.y,v.x) - angle; anglevelo += famount * sin(an) * rad/inertia*dt; angle += 0.5*(anglevelo+oldanglevelo)*dt; } } void mouseDragged() { if (keyPressed && key == '1') { setcog(); } if (keyPressed && key == '2') { setspring(); } } void mousePressed() { if (keyPressed && key == '1') { setcog(); } if (keyPressed && key == '2') { setspring(); } } void setspring() { springpt.setFromMouse(); springpt.mul(1.0/sc); velo.x = velo.y =0; anglevelo = 0; } void setcog() { cog.setFromMouse(); cog.mul (1.0/sc); velo.x = velo.y =0; angle = anglevelo = 0; } void keyPressed () { if (key == 'k' ) springk *= 2; if (key == 'l') springk *= 0.5; if ( key == 'r' ) rho *= 2; if (key== 't') rho *= 0.5; if (key == 'i') iterations *= 2; if (key == 'o' && iterations > 1) iterations *= 0.5; if (key == 'c' ) boundaryCheck = !boundaryCheck; }