1+ # Mass-Spring Solids Simulation
2+
3+ import numpy as np # numpy for linear algebra
4+ import pygame # pygame for visualization
5+ pygame .init ()
6+
7+ import square_mesh # square mesh
8+ import time_integrator
9+
10+ # simulation setup
11+ side_len = 1
12+ rho = 1000 # density of square
13+ k = 2e4 # spring stiffness
14+ n_seg = 4 # num of segments per side of the square
15+ h = 0.01 # time step size in s
16+ DBC = [(n_seg + 1 ) * (n_seg + 1 )] # dirichlet node index
17+ DBC_v = [np .array ([0.0 , - 0.5 ])] # dirichlet node velocity
18+ DBC_limit = [np .array ([0.0 , - 0.6 ])] # dirichlet node limit position
19+ ground_n = np .array ([0.0 , 1.0 ]) # normal of the slope
20+ ground_n /= np .linalg .norm (ground_n ) # normalize ground normal vector just in case
21+ ground_o = np .array ([0.0 , - 1.0 ]) # a point on the slope
22+ mu = 0.11 # friction coefficient of the slope
23+
24+ # initialize simulation
25+ [x , e ] = square_mesh .generate (side_len , n_seg ) # node positions and edge node indices
26+ x = np .append (x , [[0.0 , side_len * 0.6 ]], axis = 0 ) # ceil origin (with normal [0.0, -1.0])
27+ v = np .array ([[0.0 , 0.0 ]] * len (x )) # velocity
28+ m = [rho * side_len * side_len / ((n_seg + 1 ) * (n_seg + 1 ))] * len (x ) # calculate node mass evenly
29+ # rest length squared
30+ l2 = []
31+ for i in range (0 , len (e )):
32+ diff = x [e [i ][0 ]] - x [e [i ][1 ]]
33+ l2 .append (diff .dot (diff ))
34+ k = [k ] * len (e ) # spring stiffness
35+ # identify whether a node is Dirichlet
36+ is_DBC = [False ] * len (x )
37+ for i in DBC :
38+ is_DBC [i ] = True
39+ contact_area = [side_len / n_seg ] * len (x ) # perimeter split to each node
40+
41+ # simulation with visualization
42+ resolution = np .array ([900 , 900 ])
43+ offset = resolution / 2
44+ scale = 200
45+ def screen_projection (x ):
46+ return [offset [0 ] + scale * x [0 ], resolution [1 ] - (offset [1 ] + scale * x [1 ])]
47+
48+ time_step = 0
49+ screen = pygame .display .set_mode (resolution )
50+ running = True
51+ while running :
52+ # run until the user asks to quit
53+ for event in pygame .event .get ():
54+ if event .type == pygame .QUIT :
55+ running = False
56+
57+ print ('### Time step' , time_step , '###' )
58+
59+ # fill the background and draw the square
60+ screen .fill ((255 , 255 , 255 ))
61+ pygame .draw .aaline (screen , (0 , 0 , 255 ), screen_projection ([ground_o [0 ] - 3.0 * ground_n [1 ], ground_o [1 ] + 3.0 * ground_n [0 ]]),
62+ screen_projection ([ground_o [0 ] + 3.0 * ground_n [1 ], ground_o [1 ] - 3.0 * ground_n [0 ]])) # ground
63+ pygame .draw .aaline (screen , (0 , 0 , 255 ), screen_projection ([x [- 1 ][0 ] + 3.0 , x [- 1 ][1 ]]),
64+ screen_projection ([x [- 1 ][0 ] - 3.0 , x [- 1 ][1 ]])) # ceil
65+ for eI in e :
66+ pygame .draw .aaline (screen , (0 , 0 , 255 ), screen_projection (x [eI [0 ]]), screen_projection (x [eI [1 ]]))
67+ for xId in range (0 , len (x ) - 1 ):
68+ xI = x [xId ]
69+ pygame .draw .circle (screen , (0 , 0 , 255 ), screen_projection (xI ), 0.1 * side_len / n_seg * scale )
70+
71+ pygame .display .flip () # flip the display
72+
73+ # step forward simulation and wait for screen refresh
74+ [x , v ] = time_integrator .step_forward (x , e , v , m , l2 , k , ground_n , ground_o , contact_area , mu , is_DBC , DBC , DBC_v , DBC_limit , h , 1e-2 )
75+ time_step += 1
76+ pygame .time .wait (int (h * 1000 ))
77+
78+ pygame .quit ()
0 commit comments