# Microsoft Small Basic

Program Listing:
' A program to demonstrate simple physics
' This program uses a technique verlet integration: http://en.wikipedia.org/wiki/Verlet-Stoermer_integration

' A system is built from particles (rendered as red round rings) and constraints (rendered as green or yellow lines)
' A stick constraint can be used to build a stable square. A square has four particles (one in each corner) and we connect
' these corners with 5 stick constraints like this:
' P1 - C1 - P2
' | \ |
' | \ |
' | \ |
' C4 C5 C2
' | \ |
' | \ |
' | \ |
' P4 -C3 - P3
' The constaints connects two particles and make sure that particles neither get closer or further away from eachother
' We need the C5 constrain to prevent the box from collapsing

' Using these simple building blocks one can build suprisingly complex system that are behaves in way that feels natural

particle_count = 0
constraint_count = 0
relax_count = 3

gravity_x = 0.0
gravity_y = 0.0

box_min_x = -200.0
box_min_y = -200.0
box_max_x = 200.0
box_max_y = 200.0
box_name = ""

particle_visual_size = 10

init()

GraphicsWindow.BackgroundColor = "Black"

While(Mouse.IsRightButtonDown = "False")

current_time_stamp = Clock.ElapsedMilliseconds

time_step()

center_x = GraphicsWindow.Width / 2
center_y = GraphicsWindow.Height / 2

draw_legend()
draw_constraints()
draw_particles()
draw_box()

if Mouse.IsLeftButtonDown Then
gravity_x = 0.02
gravity_y = -0.15
Else
gravity_x = 0.0
gravity_y = 0.15
EndIf

elapsed_time = Clock.ElapsedMilliseconds - current_time_stamp

If elapsed_time < 20 Then
Program.Delay(20 - elapsed_time)
EndIf

EndWhile

Program.End()

' initializes a sample system

Sub init

make_particle()
particle_inverted_mass[pc] = 0.0
particle_x[pc] = -100
particle_y[pc] = -100
put_particle_to_rest()

make_particle()
particle_inverted_mass[pc] = 0.0
particle_x[pc] = 100
particle_y[pc] = -100
put_particle_to_rest()

make_box_mass = 24
make_box_center_x = -50
make_box_center_y = -50
make_box_width = 50
make_box_height = 50
make_box()

left_box_last_particle = pc

make_chain_mass = 10
make_chain_left = 1
make_chain_right = left_box_last_particle - 3
make_chain_slack = 3
make_chain()

make_box_mass = 24
make_box_center_x = 50
make_box_center_y = -50
make_box_width = 50
make_box_height = 50
make_box()

right_box_last_particle = pc

make_chain_mass = 10
make_chain_left = 2
make_chain_right = right_box_last_particle - 2
make_chain_slack = 3
make_chain()

make_box_mass = 24
make_box_center_x = 0
make_box_center_y = 50
make_box_width = 50
make_box_height = 50
make_box()

middle_box_last_particle = pc

make_chain_mass = 10
make_chain_left = left_box_last_particle - 1
make_chain_right = middle_box_last_particle - 3
make_chain_slack = 1.2
make_chain()

make_chain_mass = 10
make_chain_left = right_box_last_particle
make_chain_right = middle_box_last_particle - 2
make_chain_slack = 1.2
make_chain()

EndSub

' Some sub routines to help creating a system

Sub make_particle
particle_count = particle_count + 1
pc = particle_count
particle_inverted_mass[pc] = 1
particle_x[pc] = 0
particle_y[pc] = 0
particle_old_x[pc] = 0
particle_old_y[pc] = 0
particle_name[pc] = ""
EndSub

Sub put_particle_to_rest
particle_old_x[pc] = particle_x[pc]
particle_old_y[pc] = particle_y[pc]
EndSub

Sub make_constraint
constraint_count = constraint_count + 1
cc = constraint_count
constraint_left[cc] = 0
constraint_right[cc] = 0
constraint_length[cc] = 1000000
constraint_is_stick[cc] = 0
constraint_name[cc] = ""
EndSub

Sub make_chain

start_x = particle_x[make_chain_left]
start_y = particle_y[make_chain_left]
delta_x = particle_x[make_chain_right] - start_x
delta_y = particle_y[make_chain_right] - start_y

last_particle = particle_count

For i=1 To number_of_link_particles - 1
make_particle()
particle_x[pc] = start_x + delta_x * i / number_of_link_particles
particle_y[pc] = start_y + delta_y * i / number_of_link_particles
put_particle_to_rest()
EndFor

connect_particles_left = make_chain_left
connect_particles_right = last_particle + 1
connect_particles_slack = make_chain_slack
connect_particles_is_stick = 0
connect_particles()

For i=1 To make_chain_number_of_links - 2
connect_particles_left = last_particle + i
connect_particles_right = last_particle + i + 1
connect_particles_slack = make_chain_slack
connect_particles_is_stick = 0
connect_particles()
EndFor

connect_particles_left = last_particle + make_chain_number_of_links - 1
connect_particles_right = make_chain_right
connect_particles_slack = make_chain_slack
connect_particles_is_stick = 0
connect_particles()
EndSub

Sub connect_particles
calculate_particle_distance_left = connect_particles_left
calculate_particle_distance_right = connect_particles_right
calculate_particle_distance()

make_constraint()
constraint_left[cc] = connect_particles_left
constraint_right[cc] = connect_particles_right
constraint_length[cc] = connect_particles_slack * particle_distance
constraint_is_stick[cc] = connect_particles_is_stick

EndSub

Sub make_box
particle_mass = make_box_mass / 4

last_particle = particle_count

make_particle()
particle_inverted_mass[pc] = 1 / particle_mass
particle_x[pc] = make_box_center_x - make_box_width / 2
particle_y[pc] = make_box_center_y - make_box_height / 2
put_particle_to_rest()

make_particle()
particle_inverted_mass[pc] = 1 / particle_mass
particle_x[pc] = make_box_center_x + make_box_width / 2
particle_y[pc] = make_box_center_y - make_box_height / 2
put_particle_to_rest()

make_particle()
particle_inverted_mass[pc] = 1 / particle_mass
particle_x[pc] = make_box_center_x + make_box_width / 2
particle_y[pc] = make_box_center_y + make_box_height / 2
put_particle_to_rest()

make_particle()
particle_inverted_mass[pc] = 1 / particle_mass
particle_x[pc] = make_box_center_x - make_box_width / 2
particle_y[pc] = make_box_center_y + make_box_height / 2
put_particle_to_rest()

connect_particles_left = last_particle + 1
connect_particles_right = last_particle + 2
connect_particles_slack = 1
connect_particles_is_stick = 1
connect_particles()

connect_particles_left = last_particle + 2
connect_particles_right = last_particle + 3
connect_particles_slack = 1
connect_particles_is_stick = 1
connect_particles()

connect_particles_left = last_particle + 3
connect_particles_right = last_particle + 4
connect_particles_slack = 1
connect_particles_is_stick = 1
connect_particles()

connect_particles_left = last_particle + 4
connect_particles_right = last_particle + 1
connect_particles_slack = 1
connect_particles_is_stick = 1
connect_particles()

connect_particles_left = last_particle + 1
connect_particles_right = last_particle + 3
connect_particles_slack = 1
connect_particles_is_stick = 1
connect_particles()
EndSub

Sub calculate_particle_distance
left_x = particle_x[calculate_particle_distance_left]
left_y = particle_y[calculate_particle_distance_left]

right_x = particle_x[calculate_particle_distance_right]
right_y = particle_y[calculate_particle_distance_right]

delta_x = right_x - left_x
delta_y = right_y - left_y

length_squared = delta_x * delta_x + delta_y * delta_y
length = Math.SquareRoot(length_squared)

particle_distance = length
EndSub

' Subroutines to help drawing system
Sub draw_legend
If legend_name = "" Then
GraphicsWindow.BrushColor="Gray"
legend_name = Shapes.AddText("Left Mouse Button - Invert Gravity, Right Mouse Button - Quit")
EndIf
EndSub

Sub draw_box
If box_name = "" Then
GraphicsWindow.PenColor = "White"
GraphicsWindow.BrushColor = "Transparent"
box_name = Shapes.AddRectangle(box_max_x - box_min_x, box_max_y - box_min_y)
EndIf
Shapes.Move(box_name, box_min_x + center_x, box_min_y + center_y)
EndSub

Sub draw_particles
GraphicsWindow.PenColor = "Red"
GraphicsWindow.BrushColor = "Gray"
For i = 1 To particle_count
x = particle_x[i]
y = particle_y[i]
name = particle_name[i]
If name = "" Then
particle_name[i] = name
EndIf

Shapes.Move(name, x + center_x - particle_visual_size / 2, y + center_y - particle_visual_size / 2)
EndFor
EndSub

Sub draw_constraints
For i = 1 To constraint_count
left = constraint_left[i]
right = constraint_right[i]
If left > 0 And left <= particle_count And right > 0 And right <= particle_count Then
left_x = particle_x[left]
left_y = particle_y[left]

right_x = particle_x[right]
right_y = particle_y[right]

If constraint_is_stick[i] <> 0 Then
GraphicsWindow.PenColor = "Yellow"
Else
GraphicsWindow.PenColor = "Green"
EndIf

' This way of drawing constraints seem overly complicated it turns out using
' Move/Zoom/Rotate is more effecient than clear the GraphicsWindow and drawing the lines anew

clength = constraint_length[i]

name = constraint_name[i]
If name = "" Then
name = Shapes.AddLine(0, 0, clength, 0)
constraint_name[i] = name
EndIf

x = left_x + center_x
y = left_y + center_y
w = right_x + center_x
u = right_y + center_y

diffx = w - x
diffy = u - y

length_squared = diffx * diffx + diffy * diffy
length = Math.SquareRoot(length_squared)

zoom = length / clength

angle = 0

If diffx > 0 Then
ElseIf diffx < 0 Then
ElseIf diffy > 0 Then
Else
EndIf

compensate_x = -(1 - Math.Cos(rad_angle)) * clength / 2
compensate_y = Math.Sin(rad_angle) * clength / 2

Shapes.Move(name, left_x + center_x + compensate_x, left_y + center_y + compensate_y)
Shapes.Rotate(name, angle)

If zoom < 0.1 Then
Shapes.HideShape(name)
ElseIf zoom > 20 Then
Shapes.HideShape(name)
Else
Shapes.ShowShape(name)
Shapes.Zoom(name, zoom, zoom)
EndIf

constraint_name[i] = name
EndIf

EndFor
EndSub

' time_step moves all particles forward in time
Sub time_step
apply_inertia()

For relax_iter=1 To relax_count
reduce_the_tension_in_the_system()
make_sure_particles_is_inside_box()
EndFor
EndSub

' Gives particles inertia
Sub apply_inertia
' x' = x + (x - x_old) + gravity
For i = 1 To particle_count

' Inverted mass = 0 indicates that the particle is immoveable
inverted_mass = particle_inverted_mass[i]

If inverted_mass > 0 Then
x = particle_x[i]
y = particle_y[i]

new_x = x + x - particle_old_x[i] + gravity_x
new_y = y + y - particle_old_y[i] + gravity_y

particle_old_x[i] = x
particle_old_y[i] = y

particle_x[i] = new_x
particle_y[i] = new_y
EndIf
EndFor
EndSub

' This makes sure the particles never leave the box
Sub make_sure_particles_is_inside_box
For i = 1 To particle_count
particle_x[i] = Math.Min(Math.Max (particle_x[i], box_min_x), box_max_x)
particle_y[i] = Math.Min(Math.Max (particle_y[i], box_min_y), box_max_y)
EndFor
EndSub

' This makes sure that if stick constraint or rope constraint is tense we reduce tension by moving the particles closer or further away
' from eachother
Sub reduce_the_tension_in_the_system
For i = 1 To constraint_count
left = constraint_left[i]
right = constraint_right[i]
If left > 0 And left <= particle_count And right > 0 And right <= particle_count Then

left_inverted_mass = particle_inverted_mass[left]
left_x = particle_x[left]
left_y = particle_y[left]

right_inverted_mass = particle_inverted_mass[right]
right_x = particle_x[right]
right_y = particle_y[right]

delta_x = left_x - right_x
delta_y = left_y - right_y

length_squared = delta_x * delta_x + delta_y * delta_y
length = Math.SquareRoot(length_squared)

length_diff = length - constraint_length[i]

If constraint_is_stick[i] <> 0 or length_diff > 0 Then
scale = length_diff / (length * (left_inverted_mass + right_inverted_mass))

particle_x[left] = left_x - left_inverted_mass * scale * delta_x
particle_y[left] = left_y - left_inverted_mass * scale * delta_y

particle_x[right] = right_x + right_inverted_mass * scale * delta_x
particle_y[right] = right_y + right_inverted_mass * scale * delta_y
Endif

EndIf
EndFor
EndSub