A perfectly elastic collision between a moving object and a stationary object always results in a 90 degree angle! (except in the degenerate case of a dead-on collision)
I dunno why, but I felt like coding up a simulation of this.
`%Project Title%
`%Source File Name%
`======================
type circ
radius as float
x as float //position
y as float
xv as float //velocity
yv as float
xf as float //force (mass*acceleration)
yf as float
mass as float
col as dword
endtype
type screendot
x as integer
y as integer
col as dword
endtype
global w as integer
global h as integer
global numcirc as integer
global numdots as integer
global springK as float
w=screen width()
h=screen height()
numcirc=1
numdots=-1
dim c(numcirc) as circ
dim d(numdots) as screendot
height as float
height=-50
cleardots()
initialize(height)
calculateLocus(200,.1,4)
sync on
sync rate 0
do
cleardots()
cls
initialize(height)
calculateLocus(200,.01,4)
drawall()
sync
height=height+.5
if height>50 then height=-50
remstart
for m=0 to 40
handleCollision()
integrate(.01)
next m
updateDots()remend
loop
function clearDots()
undim d()
numdots=-1
dim d(numdots) as screendot
endfunction
function initialize(height as float)
springK=1
setCircle(0,w/2-200,h/2-height,5,0,30,1,rgb(255,0,0))
setCircle(1,w/2,h/2,0,0,30,1,rgb(0,255,0))
endfunction
function calculateLocus(time as float, timestep as float,timeperdot as float)
curtime as float
counter as float
curtime=0
counter=0
while curtime<time
curtime=curtime+timestep
counter=counter+timestep
handleCollision()
integrate(timestep)
if counter>=timeperdot
counter=0
updateDots()
endif
endwhile
endfunction
function updateDots()
for n=0 to numcirc
addDot(c(n).x,c(n).y,c(n).col)
next n
endfunction
function addDot(x as integer, y as integer, color as dword)
inc numdots
array insert at bottom d()
d(numdots).x=x
d(numdots).y=y
d(numdots).col=color
endfunction
function handleCollision()
for n=0 to numcirc
for m=0 to numcirc
if n<>m
diffx as float
diffy as float
dist as float
overlap as float
forcemag as float
diffx=c(m).x-c(n).x //difference in position
diffy=c(m).y-c(n).y
dist=sqrt(diffx*diffx+diffy*diffy)
overlap=c(n).radius+c(m).radius-dist
if overlap>0 //if the sum of the radii is greater than the distance
forcemag=-springK*overlap //simple spring repulsion force
diffx=diffx/dist //normalize the direction vector. direction from n to m
diffy=diffy/dist
diffx=diffx*forcemag
diffy=diffy*forcemag
c(n).xf=c(n).xf+diffx //the other object will loop through and change c(m) by the same amount.
c(n).yf=c(n).yf+diffy //it will be recalculated but oh well.
endif
endif
next m
next n
endfunction
function setCircle(n as integer,xpos as float, ypos as float, xvel as float, yvel as float, radius as float, mass as float, color as dword)
c(n).x=xpos
c(n).y=ypos
c(n).xv=xvel
c(n).yv=yvel
c(n).xf=0
c(n).yf=0
c(n).radius=radius
c(n).mass=mass
c(n).col=color
endfunction
function drawall()
for n=0 to numcirc
ink c(n).col,0
circle c(n).x,c(n).y,c(n).radius
next n
for n=0 to numdots
ink d(n).col,0
dot d(n).x,d(n).y
next n
ink rgb(255,255,255),0
endfunction
function integrate(timestep as float)
for n=0 to numcirc
//assume constant force being applied.
//P=P0+V*time+.5*accel*time^2
c(n).x=c(n).x+(c(n).xv+.5*c(n).xf/c(n).mass*timestep)*timestep
c(n).y=c(n).y+(c(n).yv+.5*c(n).yf/c(n).mass*timestep)*timestep
c(n).xv=c(n).xv+c(n).xf/c(n).mass*timestep
c(n).yv=c(n).yv+c(n).yf/c(n).mass*timestep
c(n).xf=0 //and reset the force for updating next time.
c(n).yf=0
next n
endfunction
Each frame is the result of a simulation, and it's actually coded so that you can do this with any number of particles. For example:
`%Project Title%
`%Source File Name%
`======================
type circ
radius as float
x as float //position
y as float
xv as float //velocity
yv as float
xf as float //force (mass*acceleration)
yf as float
mass as float
col as dword
endtype
type screendot
x as integer
y as integer
col as dword
endtype
global w as integer
global h as integer
global numcirc as integer
global numdots as integer
global springK as float
w=screen width()
h=screen height()
numcirc=1
numdots=-1
dim c(numcirc) as circ
dim d(numdots) as screendot
height as float
height=-50
sync on
sync rate 0
initialize(0)
do
cls
handlecollision()
integrate(.01)
drawall()
sync
loop
function clearDots()
undim d()
numdots=-1
dim d(numdots) as screendot
endfunction
function initialize(height as float)
springK=1
undim c()
dim c(20) as circ
numcirc=20
for n=0 to 20
setCircle(n,rnd(w),rnd(h),rnd(1000)/500.0-1,rnd(1000)/500.0-1,rnd(10)+15,rnd(3)+1,rgb(rnd(255),rnd(255),rnd(255)))
next n
endfunction
function calculateLocus(time as float, timestep as float,timeperdot as float)
curtime as float
counter as float
curtime=0
counter=0
while curtime<time
curtime=curtime+timestep
counter=counter+timestep
handleCollision()
integrate(timestep)
if counter>=timeperdot
counter=0
updateDots()
endif
endwhile
endfunction
function updateDots()
for n=0 to numcirc
addDot(c(n).x,c(n).y,c(n).col)
next n
endfunction
function addDot(x as integer, y as integer, color as dword)
inc numdots
array insert at bottom d()
d(numdots).x=x
d(numdots).y=y
d(numdots).col=color
endfunction
function handleCollision()
for n=0 to numcirc
for m=0 to numcirc
if n<>m
diffx as float
diffy as float
dist as float
overlap as float
forcemag as float
diffx=c(m).x-c(n).x //difference in position
diffy=c(m).y-c(n).y
dist=sqrt(diffx*diffx+diffy*diffy)
overlap=c(n).radius+c(m).radius-dist
if overlap>0 //if the sum of the radii is greater than the distance
forcemag=-springK*overlap //simple spring repulsion force
diffx=diffx/dist //normalize the direction vector. direction from n to m
diffy=diffy/dist
diffx=diffx*forcemag
diffy=diffy*forcemag
c(n).xf=c(n).xf+diffx //the other object will loop through and change c(m) by the same amount.
c(n).yf=c(n).yf+diffy //it will be recalculated but oh well.
endif
endif
next m
next n
endfunction
function setCircle(n as integer,xpos as float, ypos as float, xvel as float, yvel as float, radius as float, mass as float, color as dword)
c(n).x=xpos
c(n).y=ypos
c(n).xv=xvel
c(n).yv=yvel
c(n).xf=0
c(n).yf=0
c(n).radius=radius
c(n).mass=mass
c(n).col=color
endfunction
function drawall()
for n=0 to numcirc
ink c(n).col,0
circle c(n).x,c(n).y,c(n).radius
next n
for n=0 to numdots
ink d(n).col,0
dot d(n).x,d(n).y
next n
ink rgb(255,255,255),0
endfunction
function integrate(timestep as float)
for n=0 to numcirc
//assume constant force being applied.
//P=P0+V*time+.5*accel*time^2
c(n).x=c(n).x+(c(n).xv+.5*c(n).xf/c(n).mass*timestep)*timestep
c(n).y=c(n).y+(c(n).yv+.5*c(n).yf/c(n).mass*timestep)*timestep
c(n).xv=c(n).xv+c(n).xf/c(n).mass*timestep
c(n).yv=c(n).yv+c(n).yf/c(n).mass*timestep
c(n).xf=0 //and reset the force for updating next time.
c(n).yf=0
next n
endfunction
[edit]
and so just to make it clear: The first program is like the second program only it's visualized differently. The accuracy & method of calculation is the same.
[edit2]
also, it's really interesting to note that the collision process is handled through force only. There's no rigid body collision, no momentum conservation, only spring forces acting on circles (although conservation of momentum IS implied by the equal/opposite forces). It's a more general method. In fact, this simulation:
http://www.youtube.com/watch?v=P-oc1eYZO2Y uses that method. They LOOK like rigid spheres, but they're just very rigid springs. the problem with this method is that you have to use a small timestep, otherwise it's not very accurate. (the simulation is open source, look at the code if you don't believe me. The collision code is very similar to what I have here, except for the key difference that his code has a dampened spring instead.)