[futurebasic] Re: bouncing ball challenge

Message: < previous - next > : Reply : Subscribe : Cleanse
Home   : February 2000 : Group Archive : Group : All Groups

From: Robert Purves <robert.purves@...>
Date: Wed, 9 Feb 2000 19:07:19 +1300
>This modified code should solve the problem, it checks to see that a ball
>hits the right ball and also to see if it hits a ball before a wall
>or one wall before the other wall.
>Michael Kluskens

Good code, Michael! In fact, too good.  If my original was 50% acceptable,
your additions bring us up to 95%. With a few tiny fixes, your code works
fine in FBII as well as FB^3 (things like putting on a few missing ! 
suffices in FN Backoff!).

Why too good? Well, your code, unlike my original, is "self-repairing"
in the sense that if two balls should ever overlap, they don't get stuck.
That's good, but it disguises a remaining defect:  balls _can_ get
overlapped after a collision. That's not good physics. In fact 
if you make the following settings in Michael's program
  _bSize  = 20
  _nBalls = 90
  _initSpeed = 40
and stop animation every second or two by holding down the mouse in the
menu bar, you'll quite often see overlapping balls. The reason is that no
test is made for collisions along a moving ball's path, only at the end of
its step.

My fix for that is a replacement for Michael's FN TestCollides, the rest of
the program being unchanged. For reasons unclear, this code does not work
properly in FBII (it runs, but you still get overlaps), so I can by no 
means claim 100% solution at this point, even if the algorithm
is actually correct.

Robert P.

' -- replacement for FN TestCollides in M.S.K.'s 2nd program---
LOCAL FN TestCollides(ballone)
DIM ball1&,ball2&, k, ballOneX!, ballOneY!, dX!, dY!
DIM fraction!' collision's fractional distance from original position
DIM closestCollision!' closest distance to collision with any ball
DIM hitWhat' the ball that this ball actually hit
DIM dangerDistD!' for extended test along route of ball1 (new, by Robert P.)
ball1& = @gBall(ballone)
dangerDistD! = SQR(ball1&.xVF!*ball1&.xVF! + ball1&.yVF!*ball1&.yVF!) +gCollideDist!
ballOneX! = ball1&.xF!
ballOneY! = ball1&.yF!
hitWhat = 0' initialize targeting variables
closestCollision! = 1e9
' check walls first but do not decide yet
LONG IF (ball1&.yVF! <> 0.0)
LONG IF (ball1&.yF! < 0.0)'top bounce
closestCollision! = 1.0 - ball1&.yF! / ball1&.yVF!
hitWhat = -1
XELSE
LONG IF (ball1&.yF! >= gBoundsRect.yBottomp!)'bottom bounce
closestCollision! = 1.0 - (ball1&.yF! - gBoundsRect.yBottomp!) / ball1&.yVF!
hitWhat = -2
END IF
END IF
END IF
LONG IF ball1&.xVF! <> 0.0
LONG IF (ball1&.xF! < 0.0)'left bounce
fraction! = 1.0 - ball1&.xF! / ball1&.xVF!
LONG IF (fraction! < closestCollision!)
closestCollision! = fraction!
hitWhat = -3
END IF
XELSE
LONG IF (ball1&.xF! >= gBoundsRect.xRightp!)'right bounce
fraction! = 1.0 - (ball1&.xF! - gBoundsRect.xRightp!) / ball1&.xVF!
LONG IF (fraction! < closestCollision!)
closestCollision! = fraction!
hitWhat = -4
END IF
END IF
END IF
END IF

' next check all the balls but do not decide yet
FOR k = 1 TO _nBalls
ball2& = @gBall(k)
LONG IF (ABS(ballOneX! - ball2&.xF!) < dangerDistD!)'quickly exclude
LONG IF (ABS(ballOneY! - ball2&.yF!) < dangerDistD!)'quickly exclude
LONG IF (ball1& <> ball2&)' don't compare with self
dX! = ballOneX! - ball2&.xF!
dY! = ballOneY! - ball2&.yF!
LONG IF ((dX!*dX! + dY!*dY!) < gCollideDistSq!)' accurate test at endpoint
fraction! = FN BackOff!(ball1&, ball2&)' fractional distance to contact
LONG IF (fraction! < closestCollision!)
closestCollision! = fraction!
hitWhat = k
END IF

XELSE' new stuff by Robert P.
'look along track for collisions en route to endpoint
DIM ballOneOrigX!, ballOneOrigY! ,ballTwoX!, ballTwoY!, u!, x!, y!
'the track is (ballOneOrigX!,ballOneOrigY!) to (ballOneX!,ballOneY!)
ballOneOrigX! = ballOneX! - ball1&.xVF!
ballOneOrigY! = ballOneY! - ball1&.yVF!
' the point
ballTwoX! = ball2&.xF!
ballTwoY! = ball2&.yF!
dX! = ballOneX! - ballOneOrigX!
dY! = ballOneY! - ballOneOrigY!
u! =((ballTwoX!-ballOneOrigX!)*dX!+(ballTwoY!-ballOneOrigY!)*dY!)/(dX!*dX! + dY!*dY!)
' the meeting point is in the track segment if 0 < u < 1
LONG IF ((u! > 0.0) AND (u! < 1.0))
' shortest line from ball2 to track meets it at(x!,y!)
x! = ballOneOrigX! + u!*(ballOneX! - ballOneOrigX!)
LONG IF (x! >= 0.0) AND (x! < gBoundsRect.xRightp!)' reject out of bounds
y! = ballOneOrigY! + u!*(ballOneY! - ballOneOrigY!)
LONG IF (y! >= 0.0) AND (y! < gBoundsRect.yBottomp!)' reject out of bounds
dX! = x! - ballTwoX!: dY! = y! - ballTwoY!' offsets from track to ball2
' accurate test for contact en route 
LONG IF ((dX!*dX! + dY!*dY!) <= gCollideDistSq!)
dX! = ball1&.xVF!: dY! = ball1&.yVF!' save for restore
' adjust velocity
ball1&.xVF! =  ball1&.xVF! * u!:  ball1&.yVF! =  ball1&.yVF! * u!
'advance temporarily to maximum-overlap place
ball1&.xF! = x!:  ball1&.yF! = y!
' adjusted fractional distance to contact
fraction! = FN BackOff!(ball1&, ball2&)*u!
LONG IF (fraction! < closestCollision!)
closestCollision! = fraction!
hitWhat = k
END IF
ball1&.xF! = ballOneX! : ball1&.yF! = ballOneY!' restore after temp advance
ball1&.xVF! = dX!:  ball1&.yVF! = dY!' restore
END IF
END IF
END IF
END IF
' end of new stuff
END IF
END IF
END IF
END IF
NEXT
' now we decide what we hit
LONG IF hitWhat
' step back to original position and move forward fractional distance tocollision POINT
ball1&.xF! = ball1&.xF! + (closestCollision! - 1.0) * ball1&.xVF!
ball1&.yF! = ball1&.yF! + (closestCollision! - 1.0) * ball1&.yVF!
LONG IF (hitWhat > 0)
ball2& = @gBall(hitWhat)
FN Bounce (ball1&, ball2&)' now handle the right collision
XELSE
LONG IF (hitWhat > -3)
ball1&.yVF! = -ball1&.yVF!
XELSE
ball1&.xVF! = -ball1&.xVF!
END IF
END IF
END IF
END FN
'-----------------------------