let my={} function init(wd,ht){let version='0.35' my.wd=typeof wd!=='undefined'?wd:360 my.ht=typeof ht!=='undefined'?ht:(my.wd*7)/4 let s='' s+='
' s+='' s+='' s+='
' let props=[['mass','Mass','onMassChg','#6600cc','kg',0.5],['rad','Radius','onRadChg','#0000ff','m',0.14],['grav','Gravity','onGravChg','orangered','m/s2',0],['dense','Air/Water/Oil','onDenseChg','yellowgreen','kg/m3',0],] s+='
' for(let i=0;i' s+='
'+p[1]+':
' s+='' s+='
1
' s+='
'} s+='
' s+='' s+='' s+='
0
' s+='
0
' s+=wrap({cls:'copyrt',pos:'abs',style:'left:35px; bottom:3px'},`© 2022 Rod Pierce v${version}`) s+='' docInsert(s) my.can=new Can('canvas1',my.wd,my.ht,2) my.can2=new Can('canvas2',my.wd,my.ht,2) my.clrs=[['Blue','#0000FF'],['Red','#FF0000'],['Black','#000000'],['Green','#00cc00'],['Orange','#FFA500'],['Slate Blue','#6A5ACD'],['Lime','#00FF00'],['Spring Green','#00FF7F'],['Teal','#008080'],['Gold','#ffd700'],['Med Purple','#aa00aa'],['Light Blue','#ADD8E6'],['Navy','#000080'],['Purple','#800080'],['Dark SeaGreen','#8FBC8F'],] clrNum=0 let el2=my.can2.el el2.addEventListener('touchstart',ontouchstart,false) el2.addEventListener('touchmove',ontouchmove,false) el2.addEventListener('touchend',ontouchend,false) el2.addEventListener('mousedown',onMouseDown,false) window.addEventListener('mousemove',onMouseMove,false) window.addEventListener('mouseup',onMouseUp,false) my.maxBalln=100 my.holeRad=36 let gap=7 let tHt=20 let midMult=3 my.pockets=[{x:-gap,y:-gap,xt:0,yt:tHt},{x:my.wd+gap,y:-gap,xt:my.wd-tHt*0.8,yt:tHt},{x:-gap*midMult,y:my.ht/2,xt:0,yt:my.ht/2+tHt/2},{x:my.wd+gap*midMult,y:my.ht/2,xt:my.wd-tHt*0.8,yt:my.ht/2+tHt/2},{x:-gap,y:my.ht+gap,xt:0,yt:my.ht},{x:my.wd+gap,y:my.ht+gap,xt:my.wd-tHt*0.8,yt:my.ht},] midY=my.ht/2+30 timeIncr=24*3600 univTime=0 gFact=6.673e-11 extents=[0e10,0e10,2e10,2e10] newExtents=[0,0,1e10,1e10] clrNum=0 playQ=false m=1e24 massNum=10 radNum=10 my.elasticity=0.95 grav=9.8 dense=1 my.frameRate=1/40 my.balls=[] my.newball=new Ball() my.drag={n:0,onQ:false,holdX:0,holdY:0} my.mouse={x:0,y:0,isDown:false} let g=my.can.g g.fillStyle='red' g.strokeStyle='#000000' for(let i=0;imy.ht-ball.radius){ball.vx*=0.995 ball.vy*=-my.elasticity ball.y=my.ht-ball.radius wallQ=true} if(ball.ymy.wd-ball.radius){ball.vy*=0.995 ball.vx*=-my.elasticity ball.x=my.wd-ball.radius wallQ=true} if(ball.x0){let norm=new Pt(b.x-a.x,b.y-a.y) let ang=Math.atan2(norm.y,norm.x) if(1==0){my.can2.clear() let g2=my.can2.g g2.beginPath() g2.lineWidth=1 g2.strokeStyle=a.clr g2.arc(a.x,a.y,a.radius,0,Math.PI*2,true) g2.stroke() g2.closePath() g2.beginPath() g2.strokeStyle=b.clr g2.arc(b.x,b.y,b.radius,0,Math.PI*2,true) g2.stroke() g2.closePath() g2.beginPath() g2.strokeStyle='black' g2.moveTo(a.x,a.y) g2.lineTo(b.x,b.y) g2.stroke() g2.closePath() g2.fillText(ang.toPrecision(4),a.x,a.y)} let aVel=new Pt(a.vx,a.vy) aVel.rotateMe(ang) let bVel=new Pt(b.vx,b.vy) bVel.rotateMe(ang) let va=(my.elasticity*b.mass*(bVel.x-aVel.x)+a.mass*aVel.x+b.mass*bVel.x)/(a.mass+b.mass) let vb=(my.elasticity*a.mass*(aVel.x-bVel.x)+a.mass*aVel.x+b.mass*bVel.x)/(a.mass+b.mass) aVel.x=va bVel.x=vb aVel.rotateMe(-ang) a.vx=aVel.x a.vy=aVel.y bVel.rotateMe(-ang) b.vx=bVel.x b.vy=bVel.y let olap=radTot-Math.sqrt(dSqr) olap*=0.5/2 let aPos=new Pt(a.x,a.y) aPos.rotateMe(ang) aPos.x-=olap let bPos=new Pt(b.x,b.y) bPos.rotateMe(ang) bPos.x+=olap aPos.rotateMe(-ang) a.x=aPos.x a.y=aPos.y bPos.rotateMe(-ang) b.x=bPos.x b.y=bPos.y a.collQ=true b.collQ=true}}}} function drawBall(ball){let g=my.can.g g.beginPath() let alpha=Math.min(1,Math.log(ball.mass)*0.16-0.1) if(1==0){g.fillStyle=hex2rgba(ball.clr,alpha) g.arc(ball.x,ball.y,ball.radius,0,Math.PI*2,true) g.fill() g.closePath()}else{g.ball(ball,ball.x,ball.y)}} function applyDrag(v,r,mass){let Cd=0.47 let A=(Math.PI*r*r)/10000 let F=0.5*Cd*A*dense*v*v let a=F/mass let vd=a*my.frameRate vd=Math.min(Math.abs(vd),Math.abs(v)) vd=Math.abs(vd)*sign(v) v-=vd return v} function sign(n){return n==0?0:n/Math.abs(n)} function ontouchstart(ev){let touch=ev.targetTouches[0] ev.clientX=touch.clientX ev.clientY=touch.clientY ev.touchQ=true onMouseDown(ev)} function ontouchmove(ev){let touch=ev.targetTouches[0] ev.clientX=touch.clientX ev.clientY=touch.clientY ev.touchQ=true onMouseMove(ev)} function ontouchend(){my.drag.onQ=false} function onMouseDown(ev){let[mouseX,mouseY]=my.can.mousePos(ev) my.mouse.x=mouseX my.mouse.y=mouseY my.mouse.isDown=true my.newball=new Ball() my.newball.x=mouseX my.newball.y=mouseY my.newball.mass=massNum my.newball.radius=radNum clrNum=++clrNum%my.clrs.length my.newball.clr=my.clrs[clrNum][1] drawBall(my.newball) my.drag.onQ=true if(ev.preventDefault){ev.preventDefault()} return false} function onMouseMove(ev){let[mouseX,mouseY]=my.can.mousePos(ev) if(my.drag.onQ){let g2=my.can2.g my.can2.clear() g2.lineWidth=1 g2.strokeStyle='blue' let len=dist(my.mouse.x-mouseX,my.mouse.y-mouseY) let ang=Math.atan2(-(my.mouse.y-mouseY),my.mouse.x-mouseX) g2.drawCue(my.mouse.x,my.mouse.y,len,ang)} if(ev.preventDefault){ev.preventDefault()} return false} function onMouseUp(ev){let[mouseX,mouseY]=my.can.mousePos(ev) my.drag.onQ=false my.can2.clear() let xm=mouseX let ym=mouseY my.veln=1 my.mouse.isDown=false my.newball.vx=(my.newball.x-mouseX)*0.05 my.newball.vy=(my.newball.y-mouseY)*0.05 my.newball.mass=massNum my.newball.radius=radNum my.balls.push(my.newball) for(let i=0;i=0;i--){pts.push([pts[i][0],-pts[i][1]])}} g.beginPath() for(let i=0;i{if(cls.length==0)cls='btn' return '