Skip to content

Commit c748f7a

Browse files
committed
0_getting_started
1 parent b3513f7 commit c748f7a

5 files changed

Lines changed: 228 additions & 0 deletions

File tree

0_getting_started/readme.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Getting Started with Solid Simulation
2+
3+
Free fall, spring, and mass-spring solid simulation with symplectic Euler time integration.
4+
5+
## Dependencies
6+
```
7+
pip install numpy scipy pygame
8+
```
9+
10+
## Run
11+
Free falling particle:
12+
```
13+
python simulator0.py
14+
```
15+
Hanging particle:
16+
```
17+
python simulator1.py
18+
```
19+
Hanging square:
20+
```
21+
python simulator2.py
22+
```

0_getting_started/simulator0.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Free Fall Simulation
2+
3+
import math
4+
import numpy as np # for vector data structure and computations
5+
import pygame # for visualization
6+
7+
# simulation setup
8+
m = 1000 # mass of particle
9+
x = np.array([0.0, 0.0]) # position of particle
10+
v = np.array([0.1, 0.1]) # velocity of particle
11+
g = np.array([0, -9.81]) # gravitational acceleration
12+
h = 0.1 # time step size in seconds
13+
14+
# visualization/rendering setup
15+
pygame.init()
16+
render_FPS = 100 # number of frames to render per second
17+
resolution = np.array([900, 900]) # visualization window size in pixels
18+
offset = resolution / 2 # offset between window coordinates and simulated coordinates
19+
scale = 200 # scale between window coordinates and simulated coordinates
20+
def screen_projection(x): # convert simulated coordinates to window coordinates
21+
return [offset[0] + scale * x[0], resolution[1] - (offset[1] + scale * x[1])]
22+
screen = pygame.display.set_mode(resolution) # initialize visualizer
23+
24+
time_step = 0 # the number of the current time step
25+
running = True # flag indicating whether the simulation is still running
26+
while running:
27+
# run until the user asks to quit
28+
for event in pygame.event.get():
29+
if event.type == pygame.QUIT:
30+
running = False
31+
32+
# update the frame to display according to render_FPS
33+
if time_step % int(math.ceil((1.0 / render_FPS) / h)) == 0:
34+
# fill the background with white color, display simulation time at the top,
35+
# render a floor at y=-1, and render the particle as a circle:
36+
screen.fill((255, 255, 255))
37+
pygame.display.set_caption('Current time: ' + f'{time_step * h: .2f}s')
38+
pygame.draw.aaline(screen, (0, 0, 255), screen_projection([-2, -1]), screen_projection([2, -1]))
39+
pygame.draw.circle(screen, (0, 0, 255), screen_projection(x), 0.1 * scale)
40+
pygame.display.flip() # flip the display
41+
pygame.time.wait(int(1000.0 / render_FPS)) # wait to render the next frame
42+
43+
# step forward the simulation by updating particle velocity and position
44+
v += h * g / m
45+
x += h * v
46+
47+
# pause the simulation when the particle touches on the ground
48+
if x[1] <= -1:
49+
input()
50+
break
51+
52+
time_step += 1 # update time step counter

0_getting_started/simulator1.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Spring Simulation
2+
3+
import math
4+
import numpy as np # for vector data structure and computations
5+
import pygame # for visualization
6+
7+
# simulation setup
8+
m = 1000 # mass of particle
9+
x = np.array([0.1, 0.0]) # position of particle
10+
v = np.array([0.0, 0.0]) # velocity of particle
11+
g = np.array([0, -9.81]) # gravitational acceleration
12+
spring_rest_len = 0.3 # rest length of the spring ###
13+
spring_stiffness = 1000 # stiffness of the spring ###
14+
h = 0.1 # time step size in seconds
15+
16+
# visualization/rendering setup
17+
pygame.init()
18+
render_FPS = 100 # number of frames to render per second
19+
resolution = np.array([900, 900]) # visualization window size in pixels
20+
offset = resolution / 2 # offset between window coordinates and simulated coordinates
21+
scale = 200 # scale between window coordinates and simulated coordinates
22+
def screen_projection(x): # convert simulated coordinates to window coordinates
23+
return [offset[0] + scale * x[0], resolution[1] - (offset[1] + scale * x[1])]
24+
screen = pygame.display.set_mode(resolution) # initialize visualizer
25+
26+
time_step = 0 # the number of the current time step
27+
running = True # flag indicating whether the simulation is still running
28+
while running:
29+
# run until the user asks to quit
30+
for event in pygame.event.get():
31+
if event.type == pygame.QUIT:
32+
running = False
33+
34+
# update the frame to display according to render_FPS
35+
if time_step % int(math.ceil((1.0 / render_FPS) / h)) == 0:
36+
# fill the background with white color, display simulation time at the top,
37+
# render a floor at y=-1, draw the spring segment, and render the particle as a circle:
38+
screen.fill((255, 255, 255))
39+
pygame.display.set_caption('Current time: ' + f'{time_step * h: .2f}s')
40+
pygame.draw.aaline(screen, (0, 0, 255), screen_projection([-2, -1]), screen_projection([2, -1]))
41+
pygame.draw.aaline(screen, (0, 0, 255), screen_projection([0, 0]), screen_projection(x)) ###
42+
pygame.draw.circle(screen, (0, 0, 255), screen_projection(x), 0.1 * scale)
43+
pygame.display.flip() # flip the display
44+
pygame.time.wait(int(1000.0 / render_FPS)) # wait to render the next frame
45+
46+
# step forward the simulation by updating particle velocity and position
47+
spring_cur_len = math.sqrt(x[0] * x[0] + x[1] * x[1]) ###
48+
spring_displacement = spring_cur_len - spring_rest_len ###
49+
spring_force = -spring_stiffness * spring_displacement * (x / spring_cur_len) ###
50+
v += h * (g + spring_force) / m
51+
x += h * v
52+
53+
# pause the simulation when the particle touches on the ground
54+
if x[1] <= -1:
55+
input()
56+
break
57+
58+
time_step += 1 # update time step counter

0_getting_started/simulator2.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# Mass-Spring Solid Simulation
2+
3+
import math
4+
import numpy as np # for vector data structure and computations
5+
import pygame # for visualization
6+
import square_mesh # for generating a square mesh
7+
8+
# simulation setup
9+
m = 1000 # mass of each particle
10+
side_length = 1 # side length of the square
11+
n_seg = 4 # number of springs per side of the square
12+
[x, e] = square_mesh.generate(side_length, n_seg) # array of particle positions and springs ###
13+
v = np.array([[0.0, 0.0]] * len(x)) # velocity array of particles ###
14+
g = np.array([0, -9.81]) # gravitational acceleration
15+
spring_rest_len = [] # rest length array of the springs ###
16+
for i in range(0, len(e)): # calculate the rest length of each spring
17+
spring_vec = x[e[i][0]] - x[e[i][1]] # the vector connecting two ends of spring i
18+
spring_rest_len.append(math.sqrt(spring_vec[0] * spring_vec[0] + spring_vec[1] * spring_vec[1]))
19+
spring_stiffness = 1000 # stiffness of the spring
20+
h = 0.1 # time step size in seconds
21+
22+
# visualization/rendering setup
23+
pygame.init()
24+
render_FPS = 100 # number of frames to render per second
25+
resolution = np.array([900, 900]) # visualization window size in pixels
26+
offset = resolution / 2 # offset between window coordinates and simulated coordinates
27+
scale = 200 # scale between window coordinates and simulated coordinates
28+
def screen_projection(x): # convert simulated coordinates to window coordinates
29+
return [offset[0] + scale * x[0], resolution[1] - (offset[1] + scale * x[1])]
30+
screen = pygame.display.set_mode(resolution) # initialize visualizer
31+
32+
time_step = 0 # the number of the current time step
33+
running = True # flag indicating whether the simulation is still running
34+
while running:
35+
# run until the user asks to quit
36+
for event in pygame.event.get():
37+
if event.type == pygame.QUIT:
38+
running = False
39+
40+
# update the frame to display according to render_FPS
41+
if time_step % int(math.ceil((1.0 / render_FPS) / h)) == 0:
42+
# fill the background with white color, display simulation time at the top,
43+
# draw each spring segment, and render each particle as a circle:
44+
screen.fill((255, 255, 255))
45+
pygame.display.set_caption('Current time: ' + f'{time_step * h: .2f}s')
46+
for i in range(0, len(e)): ###
47+
pygame.draw.aaline(screen, (0, 0, 255), screen_projection(x[e[i][0]]), screen_projection(x[e[i][1]]))
48+
for i in range(0, len(x)): ###
49+
pygame.draw.circle(screen, (0, 0, 255), screen_projection(x[i]), 0.02 * scale)
50+
pygame.display.flip() # flip the display
51+
pygame.time.wait(int(1000.0 / render_FPS)) # wait to render the next frame
52+
53+
# step forward the simulation by updating particle velocity and position ###
54+
for i in range(0, len(e)):
55+
# calculate elasticity force of spring i:
56+
spring_vec = x[e[i][0]] - x[e[i][1]]
57+
spring_cur_len = math.sqrt(spring_vec[0] * spring_vec[0] + spring_vec[1] * spring_vec[1])
58+
spring_displacement = spring_cur_len - spring_rest_len[i]
59+
spring_force = -spring_stiffness * spring_displacement * (spring_vec / spring_cur_len)
60+
# update the velocity of the two ends of spring i
61+
v[e[i][0]] += h * (g + spring_force) / m
62+
v[e[i][1]] += h * (g - spring_force) / m
63+
# fix the top left and top right corner by setting velocity to 0:
64+
v[n_seg] = v[(n_seg + 1) * (n_seg + 1) - 1] = np.array([0, 0])
65+
# update the position of each particle:
66+
for i in range(0, len(x)):
67+
x[i] += h * v[i]
68+
69+
time_step += 1 # update time step counter

0_getting_started/square_mesh.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import numpy as np
2+
3+
def generate(side_length, n_seg):
4+
# sample nodes uniformly on a square
5+
x = np.array([[0.0, 0.0]] * ((n_seg + 1) ** 2))
6+
step = side_length / n_seg
7+
for i in range(0, n_seg + 1):
8+
for j in range(0, n_seg + 1):
9+
x[i * (n_seg + 1) + j] = [-side_length / 2 + i * step, -side_length / 2 + j * step]
10+
11+
# connect the nodes with edges
12+
e = []
13+
# horizontal edges
14+
for i in range(0, n_seg):
15+
for j in range(0, n_seg + 1):
16+
e.append([i * (n_seg + 1) + j, (i + 1) * (n_seg + 1) + j])
17+
# vertical edges
18+
for i in range(0, n_seg + 1):
19+
for j in range(0, n_seg):
20+
e.append([i * (n_seg + 1) + j, i * (n_seg + 1) + j + 1])
21+
# diagonals
22+
for i in range(0, n_seg):
23+
for j in range(0, n_seg):
24+
e.append([i * (n_seg + 1) + j, (i + 1) * (n_seg + 1) + j + 1])
25+
e.append([(i + 1) * (n_seg + 1) + j, i * (n_seg + 1) + j + 1])
26+
27+
return [x, e]

0 commit comments

Comments
 (0)