10-29-2025, 10:59 AM
This is a simulation of a solid ball rolling on an inclined slope. The physics is straightforward Newtonian gravitation and laws of motion. The ball rolls as it moves along the slope and this adjusts the rate at which gravitational potential energy is exchanged for kinetic energy. The rolling in this simulation is done with zero friction loss.
The ball is rolling on a circular path, and it thus oscillates from one side to the other. The mathematics to generate the acceleration uses the formula for a simple constant slope, but here the ball position is forced onto the curved slope at every point. This leads to a slight deviation from true energy conservation, and the motion data are reset at each end to keep the ball within stable bounds. In fact this correction is unnoticeable.
The ball, moving to and fro along the curved path, is displayed in the 3D _MAPTRIANGLE space. The y- and z- viewing angles are rotated for a more interesting view.
QB64 3D _MAPTRIANGLE is a method which gives excellent 3D simulation - in this case the ball as well as travelling horizontally is actually moving towards and away from the viewer somewhat. I salute both Mr Newton for his easy-to-use motion equations and the QB64 guru who produced 3D _MAPTRIANGLE, where the programmer does not have to do any of the 3D positional and occluding calculations.
The 3D display mapping is done via two hemispherical surfaces: a near-facing and a far-facing hemisphere, this latter is generally much occluded (but not completely because of the viewing angle). The far-facing hemisphere is a reversed 'copy' of the forward-facing one.
There is a fair bit of calculation effort going on during the running program, and on my Core i5 laptop the CPU and graphics card have a moderate work-out.
The program comes with the Earth as the default sphere, but there are a few others should the user wish to try.
Unzip the contents into a QB64 sub-folder.
Rolling Sphere.zip (Size: 435.29 KB / Downloads: 24)
The ball is rolling on a circular path, and it thus oscillates from one side to the other. The mathematics to generate the acceleration uses the formula for a simple constant slope, but here the ball position is forced onto the curved slope at every point. This leads to a slight deviation from true energy conservation, and the motion data are reset at each end to keep the ball within stable bounds. In fact this correction is unnoticeable.
The ball, moving to and fro along the curved path, is displayed in the 3D _MAPTRIANGLE space. The y- and z- viewing angles are rotated for a more interesting view.
QB64 3D _MAPTRIANGLE is a method which gives excellent 3D simulation - in this case the ball as well as travelling horizontally is actually moving towards and away from the viewer somewhat. I salute both Mr Newton for his easy-to-use motion equations and the QB64 guru who produced 3D _MAPTRIANGLE, where the programmer does not have to do any of the 3D positional and occluding calculations.
The 3D display mapping is done via two hemispherical surfaces: a near-facing and a far-facing hemisphere, this latter is generally much occluded (but not completely because of the viewing angle). The far-facing hemisphere is a reversed 'copy' of the forward-facing one.
There is a fair bit of calculation effort going on during the running program, and on my Core i5 laptop the CPU and graphics card have a moderate work-out.
The program comes with the Earth as the default sphere, but there are a few others should the user wish to try.
Unzip the contents into a QB64 sub-folder.
Rolling Sphere.zip (Size: 435.29 KB / Downloads: 24)
Code: (Select All)
'Rolling Ball Simulation 08/10/25 by Magdha QB64 v2.0
'Ball rolls on a semicircular track under gravity
'Motion governed by Newtonian gravity without friction
'The ball rolls as it moves along
'The display is mapped to the 3D _MAPTRIANGLE space
'Constants and arrays
CONST False = 0, True = NOT False
CONST g# = 9.81, R% = 850, deltaT# = 0.02
CONST Uscreen% = 940, Vscreen% = 800, NoPhis%% = 32, NoAlphas%% = 32
CONST BallRad%% = 70, PerspDist% = 600, YDisp% = 150, XDisp% = 80
CONST Alpha! = _PI / (2 * NoAlphas%%), Phi! = 2 * _PI / NoPhis%%, ImageSize% = 271, YAngle! = 0.25, ZAngle! = 0.1
CONST Utrack% = 12, VTrack% = 200, UStart% = -570, TracksNo% = 110
DIM MapTo3D!(NoPhis%%, 2 * NoAlphas%%, 1, 2)
_TITLE "Rolling Ball Esc to Quit"
'Conversion from 2D into 3D _MAPTRIANGLE co-ordinates
FOR N%% = 0 TO NoPhis%%
FOR M%% = 0 TO NoAlphas%%
'Where it's from (2D)
MapTo3D!(N%%, M%%, 0, 0) = ((ImageSize% - 1) / 2) + ((ImageSize% - 1) / 2) * SIN((_PI / 2) - M%% * Alpha!) * COS(N%% * Phi!)
MapTo3D!(N%%, M%%, 0, 1) = ((ImageSize% - 1) / 2) + ((ImageSize% - 1) / 2) * SIN((_PI / 2) - M%% * Alpha!) * SIN(N%% * Phi!)
'Where it's going to (3D)
MapTo3D!(N%%, M%%, 1, 0) = BallRad%% * SIN((_PI / 2) - M%% * Alpha!) * COS(N%% * Phi!)
MapTo3D!(N%%, M%%, 1, 1) = BallRad%% * SIN((_PI / 2) - M%% * Alpha!) * SIN(N%% * Phi!)
MapTo3D!(N%%, M%%, 1, 2) = BallRad%% * COS((_PI / 2) - M%% * Alpha!)
NEXT M%%
NEXT N%%
'Screen Background
TempImage& = _NEWIMAGE(Uscreen%, Vscreen%, 32)
_DEST TempImage&
COLOR _RGB32(255, 255, 255), _RGB32(10, 60, 60)
CLS
FOR UCol% = 0 TO Uscreen% - 1
FOR VCol% = 0 TO Vscreen% - 1
PSET (UCol%, VCol%), _RGB(100, INT(180 * UCol% / Uscreen%), INT(180 * VCol% / Vscreen%))
NEXT VCol%
NEXT UCol%
Background& = MakeHardware&(TempImage&)
'Track Image
TempImage& = _NEWIMAGE(Utrack%, VTrack%, 32) '!!! TBF
_DEST TempImage&
FOR UCol% = 0 TO Utrack% - 1
FOR VCol% = 0 TO VTrack% - 1
PSET (UCol%, VCol%), _RGB(255 - INT(30 * (SIN(UCol% * _PI / (2 * Utrack%))) * (VCol% / VTrack%)), 253 - INT(30 * (SIN(UCol% * _PI / (2 * Utrack%))) * (VCol% / VTrack%)), 208 - INT(30 * (SIN(UCol% * _PI / (2 * Utrack%))) * (VCol% / VTrack%)))
NEXT VCol%
NEXT UCol%
HalfPipe& = MakeHardware&(TempImage&)
'Sphere images:
Football& = _LOADIMAGE("Football clipart.png", 32)
Stoneball& = _LOADIMAGE("Stone Sphere.png", 32)
Ironball& = _LOADIMAGE("Metal Sphere.png", 32)
Whiteball& = _LOADIMAGE("White Ball.png", 32)
Blueball& = _LOADIMAGE("Blue Ball.png", 32)
Earthball& = _LOADIMAGE("Earth.png", 32)
Spotball& = _NEWIMAGE(271, 271, 32) 'Spotball& was used in testing the program
_DEST Spotball&
COLOR _RGB32(255, 255, 255), _RGB32(90, 0, 90)
CLS
CIRCLE (50, 135), 30
PAINT (50, 135)
CIRCLE (135, 50), 20
PAINT (135, 50)
CIRCLE (135, 135), 30
CIRCLE (135, 135), 60
CIRCLE (135, 135), 90
CIRCLE (135, 135), 120
CIRCLE (135, 135), 133
TempImage& = _NEWIMAGE(ImageSize%, ImageSize%, 32)
_DEST TempImage&
'Choose which image you want (Default is Earth):
'_PUTIMAGE , Spotball&
'_PUTIMAGE , Football&
'_PUTIMAGE , Stoneball&
'_PUTIMAGE , Ironball&
'_PUTIMAGE , Whiteball&
'_PUTIMAGE , Blueball&
_PUTIMAGE , Earthball&
SolidSphere& = MakeHardware&(TempImage&)
'Ball Start Position, Velocity and Rolling Rotation:
Xpos# = 500
Ypos# = R% - SQR((R% * R%) - (Xpos# * Xpos#))
VBall# = 0
BallTwist! = 0
'Screen
SCREEN _NEWIMAGE(Uscreen%, Vscreen%, 32)
_SCREENMOVE 50, 0
_DEST 0
'_DISPLAYORDER _HARDWARE , _SOFTWARE
_DISPLAYORDER _HARDWARE 'Hardware images only
WHILE _KEYHIT <> 27 'Esc to quit
_LIMIT 120 'Frames/Second Rate
'Background
_PUTIMAGE (0, 0), Background&
'Track Image
FOR H% = 0 TO TracksNo%
Ax12! = UStart% + H% * Utrack%
Ay12! = R% - SQR((R% * R%) - (Ax12! * Ax12!))
ThetaTrack! = ATN(Ay12! / Ax12!)
Az11! = -VTrack% / 2
Ax11! = Ax12! + BallRad% * SIN(ThetaTrack!) + XDisp%
Ay11! = Ay12! - BallRad% * COS(ThetaTrack!) - YDisp%
Bx12! = Ax12! + Utrack%
By12! = R% - SQR((R% * R%) - (Bx12! * Bx12!))
ThetaTrack! = ATN(By12! / Bx12!)
Bx11! = Bx12! + BallRad% * SIN(ThetaTrack!) + XDisp%
By11! = By12! - BallRad% * COS(ThetaTrack!) - YDisp%
Bz11! = -VTrack% / 2
Cx11! = Bx11!
Cy11! = By11!
Cz11! = VTrack% / 2
Dx11! = Ax11!
Dy11! = Ay11!
Dz11! = VTrack% / 2
'Rotate around Y-axis
Ax1! = Ax11! * COS(YAngle!) + Az11! * SIN(YAngle!)
Az1! = Az11! * COS(YAngle!) - Ax11! * SIN(YAngle!)
Bx1! = Bx11! * COS(YAngle!) + Bz11! * SIN(YAngle!)
Bz1! = Bz11! * COS(YAngle!) - Bx11! * SIN(YAngle!)
Cx1! = Cx11! * COS(YAngle!) + Cz11! * SIN(YAngle!)
Cz1! = Cz11! * COS(YAngle!) - Cx11! * SIN(YAngle!)
Dx1! = Dx11! * COS(YAngle!) + Dz11! * SIN(YAngle!)
Dz1! = Dz11! * COS(YAngle!) - Dx11! * SIN(YAngle!)
'Rotate around Z-axis
Ax2! = Ax1! * COS(ZAngle!) + Ay11! * SIN(ZAngle!)
Ay2! = Ay11! * COS(ZAngle!) - Ax1! * SIN(ZAngle!)
Bx2! = Bx1! * COS(ZAngle!) + By11! * SIN(ZAngle!)
By2! = By11! * COS(ZAngle!) - Bx1! * SIN(ZAngle!)
Cx2! = Cx1! * COS(ZAngle!) + Cy11! * SIN(ZAngle!)
Cy2! = Cy11! * COS(ZAngle!) - Cx1! * SIN(ZAngle!)
Dx2! = Dx1! * COS(ZAngle!) + Dy11! * SIN(ZAngle!)
Dy2! = Dy11! * COS(ZAngle!) - Dx1! * SIN(ZAngle!)
_MAPTRIANGLE (0, 0)-(Utrack% - 1, 0)-(Utrack% - 1, VTrack% - 1), HalfPipe& TO(Ax2!, Ay2!, Az1! - PerspDist%)-(Bx2!, By2!, Bz1! - PerspDist%)-(Cx2!, Cy2!, Cz1! - PerspDist%)
_MAPTRIANGLE (Utrack% - 1, VTrack% - 1)-(0, VTrack% - 1)-(0, 0), HalfPipe& TO(Cx2!, Cy2!, Cz1! - PerspDist%)-(Dx2!, Dy2!, Dz1! - PerspDist%)-(Ax2!, Ay2!, Az1! - PerspDist%)
NEXT H%
'Map & Display Ball
FOR N%% = 0 TO NoPhis%% - 1 'STEP 2
FOR M%% = 0 TO 2 * NoAlphas%% - 2 'STEP 2
'Map to 3D
Ax11! = MapTo3D!(N%%, M%%, 1, 0)
Ay11! = MapTo3D!(N%%, M%%, 1, 1)
Bx11! = MapTo3D!(N%%, M%% + 1, 1, 0)
By11! = MapTo3D!(N%%, M%% + 1, 1, 1)
Cx11! = MapTo3D!(N%% + 1, M%% + 1, 1, 0)
Cy11! = MapTo3D!(N%% + 1, M%% + 1, 1, 1)
Dx11! = MapTo3D!(N%% + 1, M%%, 1, 0)
Dy11! = MapTo3D!(N%% + 1, M%%, 1, 1)
Ex11! = MapTo3D!(N%%, M%%, 1, 0)
Ey11! = MapTo3D!(N%%, M%%, 1, 1)
Fx11! = MapTo3D!(N%%, M%% + 1, 1, 0)
Fy11! = MapTo3D!(N%%, M%% + 1, 1, 1)
Gx11! = MapTo3D!(N%% + 1, M%% + 1, 1, 0)
Gy11! = MapTo3D!(N%% + 1, M%% + 1, 1, 1)
Hx11! = MapTo3D!(N%% + 1, M%%, 1, 0)
Hy11! = MapTo3D!(N%% + 1, M%%, 1, 1)
'Rotate ball about rolling axis
Ax0! = Ax11! * COS(BallTwist!) + Ay11! * SIN(BallTwist!) + Xpos# + XDisp%
Ay0! = Ay11! * COS(BallTwist!) - Ax11! * SIN(BallTwist!) + Ypos# - YDisp%
Az0! = MapTo3D!(N%%, M%%, 1, 2)
Bx0! = Bx11! * COS(BallTwist!) + By11! * SIN(BallTwist!) + Xpos# + XDisp%
By0! = By11! * COS(BallTwist!) - Bx11! * SIN(BallTwist!) + Ypos# - YDisp%
Bz0! = MapTo3D!(N%%, M%% + 1, 1, 2)
Cx0! = Cx11! * COS(BallTwist!) + Cy11! * SIN(BallTwist!) + Xpos# + XDisp%
Cy0! = Cy11! * COS(BallTwist!) - Cx11! * SIN(BallTwist!) + Ypos# - YDisp%
Cz0! = MapTo3D!(N%% + 1, M%% + 1, 1, 2)
Dx0! = Dx11! * COS(BallTwist!) + Dy11! * SIN(BallTwist!) + Xpos# + XDisp%
Dy0! = Dy11! * COS(BallTwist!) - Dx11! * SIN(BallTwist!) + Ypos# - YDisp%
Dz0! = MapTo3D!(N%% + 1, M%%, 1, 2)
Ex0! = Ex11! * COS(BallTwist!) + Ey11! * SIN(BallTwist!) + Xpos# + XDisp%
Ey0! = Ey11! * COS(BallTwist!) - Ex11! * SIN(BallTwist!) + Ypos# - YDisp%
Ez0! = -MapTo3D!(N%%, M%%, 1, 2)
Fx0! = Fx11! * COS(BallTwist!) + Fy11! * SIN(BallTwist!) + Xpos# + XDisp%
Fy0! = Fy11! * COS(BallTwist!) - Fx11! * SIN(BallTwist!) + Ypos# - YDisp%
Fz0! = -MapTo3D!(N%%, M%% + 1, 1, 2)
Gx0! = Gx11! * COS(BallTwist!) + Gy11! * SIN(BallTwist!) + Xpos# + XDisp%
Gy0! = Gy11! * COS(BallTwist!) - Gx11! * SIN(BallTwist!) + Ypos# - YDisp%
Gz0! = -MapTo3D!(N%% + 1, M%% + 1, 1, 2)
Hx0! = Hx11! * COS(BallTwist!) + Hy11! * SIN(BallTwist!) + Xpos# + XDisp%
Hy0! = Hy11! * COS(BallTwist!) - Hx11! * SIN(BallTwist!) + Ypos# - YDisp%
Hz0! = -MapTo3D!(N%% + 1, M%%, 1, 2)
'Then do orientation rotations:
'Rotate around Y-axis
Ax1! = Ax0! * COS(YAngle!) + Az0! * SIN(YAngle!)
Az1! = Az0! * COS(YAngle!) - Ax0! * SIN(YAngle!)
Bx1! = Bx0! * COS(YAngle!) + Bz0! * SIN(YAngle!)
Bz1! = Bz0! * COS(YAngle!) - Bx0! * SIN(YAngle!)
Cx1! = Cx0! * COS(YAngle!) + Cz0! * SIN(YAngle!)
Cz1! = Cz0! * COS(YAngle!) - Cx0! * SIN(YAngle!)
Dx1! = Dx0! * COS(YAngle!) + Dz0! * SIN(YAngle!)
Dz1! = Dz0! * COS(YAngle!) - Dx0! * SIN(YAngle!)
Ex1! = Ex0! * COS(YAngle!) + Ez0! * SIN(YAngle!)
Ez1! = Ez0! * COS(YAngle!) - Ex0! * SIN(YAngle!)
Fx1! = Fx0! * COS(YAngle!) + Fz0! * SIN(YAngle!)
Fz1! = Fz0! * COS(YAngle!) - Fx0! * SIN(YAngle!)
Gx1! = Gx0! * COS(YAngle!) + Gz0! * SIN(YAngle!)
Gz1! = Gz0! * COS(YAngle!) - Gx0! * SIN(YAngle!)
Hx1! = Hx0! * COS(YAngle!) + Hz0! * SIN(YAngle!)
Hz1! = Hz0! * COS(YAngle!) - Hx0! * SIN(YAngle!)
'Rotate around Z-axis
Ax2! = Ax1! * COS(ZAngle!) + Ay0! * SIN(ZAngle!)
Ay2! = Ay0! * COS(ZAngle!) - Ax1! * SIN(ZAngle!)
Bx2! = Bx1! * COS(ZAngle!) + By0! * SIN(ZAngle!)
By2! = By0! * COS(ZAngle!) - Bx1! * SIN(ZAngle!)
Cx2! = Cx1! * COS(ZAngle!) + Cy0! * SIN(ZAngle!)
Cy2! = Cy0! * COS(ZAngle!) - Cx1! * SIN(ZAngle!)
Dx2! = Dx1! * COS(ZAngle!) + Dy0! * SIN(ZAngle!)
Dy2! = Dy0! * COS(ZAngle!) - Dx1! * SIN(ZAngle!)
Ex2! = Ex1! * COS(ZAngle!) + Ey0! * SIN(ZAngle!)
Ey2! = Ey0! * COS(ZAngle!) - Ex1! * SIN(ZAngle!)
Fx2! = Fx1! * COS(ZAngle!) + Fy0! * SIN(ZAngle!)
Fy2! = Fy0! * COS(ZAngle!) - Fx1! * SIN(ZAngle!)
Gx2! = Gx1! * COS(ZAngle!) + Gy0! * SIN(ZAngle!)
Gy2! = Gy0! * COS(ZAngle!) - Gx1! * SIN(ZAngle!)
Hx2! = Hx1! * COS(ZAngle!) + Hy0! * SIN(ZAngle!)
Hy2! = Hy0! * COS(ZAngle!) - Hx1! * SIN(ZAngle!)
'Now map ball co-ordinates to 3D _MAPTRIANGLE space
'Near hemisphere:
_MAPTRIANGLE (MapTo3D!(N%%, M%%, 0, 0), MapTo3D!(N%%, M%%, 0, 1))-(MapTo3D!(N%%, M%% + 1, 0, 0), MapTo3D!(N%%, M%% + 1, 0, 1))-(MapTo3D!(N%% + 1, M%% + 1, 0, 0), MapTo3D!(N%% + 1, M%% + 1, 0, 1)), SolidSphere& TO(Ax2!, Ay2!, Az1! - PerspDist%)-(Bx2!, By2!, Bz1! - PerspDist%)-(Cx2!, Cy2!, Cz1! - PerspDist%)
_MAPTRIANGLE (MapTo3D!(N%% + 1, M%% + 1, 0, 0), MapTo3D!(N%% + 1, M%% + 1, 0, 1))-(MapTo3D!(N%% + 1, M%%, 0, 0), MapTo3D!(N%% + 1, M%%, 0, 1))-(MapTo3D!(N%%, M%%, 0, 0), MapTo3D!(N%%, M%%, 0, 1)), SolidSphere& TO(Cx2!, Cy2!, Cz1! - PerspDist%)-(Dx2!, Dy2!, Dz1! - PerspDist%)-(Ax2!, Ay2!, Az1! - PerspDist%)
'Far hemisphere:
_MAPTRIANGLE (MapTo3D!(N%%, M%%, 0, 0), MapTo3D!(N%%, M%%, 0, 1))-(MapTo3D!(N%%, M%% + 1, 0, 0), MapTo3D!(N%%, M%% + 1, 0, 1))-(MapTo3D!(N%% + 1, M%% + 1, 0, 0), MapTo3D!(N%% + 1, M%% + 1, 0, 1)), SolidSphere& TO(Ex2!, Ey2!, Ez1! - PerspDist%)-(Fx2!, Fy2!, Fz1! - PerspDist%)-(Gx2!, Gy2!, Gz1! - PerspDist%)
_MAPTRIANGLE (MapTo3D!(N%% + 1, M%% + 1, 0, 0), MapTo3D!(N%% + 1, M%% + 1, 0, 1))-(MapTo3D!(N%% + 1, M%%, 0, 0), MapTo3D!(N%% + 1, M%%, 0, 1))-(MapTo3D!(N%%, M%%, 0, 0), MapTo3D!(N%%, M%%, 0, 1)), SolidSphere& TO(Gx2!, Gy2!, Gz1! - PerspDist%)-(Hx2!, Hy2!, Hz1! - PerspDist%)-(Ex2!, Ey2!, Ez1! - PerspDist%)
NEXT M%%
NEXT N%%
_DISPLAY
'Ball position/speed
Theta# = ATN(Ypos# / Xpos#)
Accn# = 5 * g# * SIN(2 * Theta#) / 7
deltaV# = Accn# * deltaT#
AveV# = VBall# - (deltaV# / 2)
VBall# = VBall# - deltaV#
'Index ball position:
Xpos# = Xpos# + (AveV# * deltaT#) * COS(2 * Theta#)
'Forcing the ball to follow the circular curve does not properly conserve enegy (expected this to be the case)
'So at the extremes, the position and velocity are manually reset:
IF Xpos# < -500 THEN
Xpos# = -500
VBall# = 0
ELSEIF Xpos# > 500 THEN
Xpos# = 500
VBall# = 0
END IF
Ypos# = R% - SQR((R% * R%) - (Xpos# * Xpos#))
'Ball rolling:
BallTwist! = BallTwist! + ((AveV# * deltaT#) / BallRad%%)
IF BallTwist! < -_PI THEN
BallTwist! = _PI
ELSEIF BallTwist! > _PI THEN
BallTwist! = -_PI
END IF
WEND
SYSTEM
FUNCTION MakeHardware& (Imagename&)
MakeHardware& = _COPYIMAGE(Imagename&, 33)
_FREEIMAGE Imagename&
END FUNCTION

