forked from greedythib/CME212-2020
-
Notifications
You must be signed in to change notification settings - Fork 0
/
mass_spring.cpp
176 lines (141 loc) · 5.23 KB
/
mass_spring.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
/**
* @file mass_spring.cpp
* Implementation of mass-spring system using Graph
*
* @brief Reads in two files specified on the command line.
* First file: 3D Points (one per line) defined by three doubles
* Second file: Tetrahedra (one per line) defined by 4 indices into the point
* list
*/
#include <fstream>
#include <chrono>
#include <thread>
#include "CME212/SFML_Viewer.hpp"
#include "CME212/Util.hpp"
#include "CME212/Color.hpp"
#include "CME212/Point.hpp"
#include "Graph.hpp"
// Gravity in meters/sec^2
static constexpr double grav = 9.81;
/** Custom structure of data to store with Nodes */
struct NodeData {
Point vel; //< Node velocity
double mass; //< Node mass
NodeData() : vel(0), mass(1) {}
};
// Define the Graph type
using GraphType = Graph<NodeData>;
using Node = typename GraphType::node_type;
using Edge = typename GraphType::edge_type;
/** Change a graph's nodes according to a step of the symplectic Euler
* method with the given node force.
* @param[in,out] g Graph
* @param[in] t The current time (useful for time-dependent forces)
* @param[in] dt The time step
* @param[in] force Function object defining the force per node
* @return the next time step (usually @a t + @a dt)
*
* @tparam G::node_value_type supports ???????? YOU CHOOSE
* @tparam F is a function object called as @a force(n, @a t),
* where n is a node of the graph and @a t is the current time.
* @a force must return a Point representing the force vector on
* Node n at time @a t.
*/
template <typename G, typename F>
double symp_euler_step(G& g, double t, double dt, F force) {
// Compute the t+dt position
for (auto it = g.node_begin(); it != g.node_end(); ++it) {
auto n = *it;
// Update the position of the node according to its velocity
// x^{n+1} = x^{n} + v^{n} * dt
n.position() += n.value().vel * dt;
}
// Compute the t+dt velocity
for (auto it = g.node_begin(); it != g.node_end(); ++it) {
auto n = *it;
// v^{n+1} = v^{n} + F(x^{n+1},t) * dt / m
n.value().vel += force(n, t) * (dt / n.value().mass);
}
return t + dt;
}
/** Force function object for HW2 #1. */
struct Problem1Force {
/** Return the force applying to @a n at time @a t.
*
* For HW2 #1, this is a combination of mass-spring force and gravity,
* except that points at (0, 0, 0) and (1, 0, 0) never move. We can
* model that by returning a zero-valued force. */
template <typename NODE>
Point operator()(NODE n, double t) {
// HW2 #1: YOUR CODE HERE
(void) n; (void) t; (void) grav; // silence compiler warnings
return Point(0);
}
};
int main(int argc, char** argv)
{
// Check arguments
if (argc < 3) {
std::cerr << "Usage: " << argv[0] << " NODES_FILE TETS_FILE\n";
exit(1);
}
// Construct an empty graph
GraphType graph;
// Create a nodes_file from the first input argument
std::ifstream nodes_file(argv[1]);
// Interpret each line of the nodes_file as a 3D Point and add to the Graph
Point p;
std::vector<typename GraphType::node_type> nodes;
while (CME212::getline_parsed(nodes_file, p))
nodes.push_back(graph.add_node(p));
// Create a tets_file from the second input argument
std::ifstream tets_file(argv[2]);
// Interpret each line of the tets_file as four ints which refer to nodes
std::array<int,4> t;
while (CME212::getline_parsed(tets_file, t)) {
graph.add_edge(nodes[t[0]], nodes[t[1]]);
graph.add_edge(nodes[t[0]], nodes[t[2]]);
#if 0
// Diagonal edges: include as of HW2 #2
graph.add_edge(nodes[t[0]], nodes[t[3]]);
graph.add_edge(nodes[t[1]], nodes[t[2]]);
#endif
graph.add_edge(nodes[t[1]], nodes[t[3]]);
graph.add_edge(nodes[t[2]], nodes[t[3]]);
}
// HW2 #1 YOUR CODE HERE
// Set initial conditions for your nodes, if necessary.
// Print out the stats
std::cout << graph.num_nodes() << " " << graph.num_edges() << std::endl;
// Launch the Viewer
CME212::SFML_Viewer viewer;
auto node_map = viewer.empty_node_map(graph);
viewer.add_nodes(graph.node_begin(), graph.node_end(), node_map);
viewer.add_edges(graph.edge_begin(), graph.edge_end(), node_map);
viewer.center_view();
// We want viewer interaction and the simulation at the same time
// Viewer is thread-safe, so launch the simulation in a child thread
bool interrupt_sim_thread = false;
auto sim_thread = std::thread([&](){
// Begin the mass-spring simulation
double dt = 0.001;
double t_start = 0;
double t_end = 5.0;
for (double t = t_start; t < t_end && !interrupt_sim_thread; t += dt) {
//std::cout << "t = " << t << std::endl;
symp_euler_step(graph, t, dt, Problem1Force());
// Update viewer with nodes' new positions
viewer.add_nodes(graph.node_begin(), graph.node_end(), node_map);
viewer.set_label(t);
// These lines slow down the animation for small graphs, like grid0_*.
// Feel free to remove them or tweak the constants.
if (graph.size() < 100)
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
}); // simulation thread
viewer.event_loop();
// If we return from the event loop, we've killed the window.
interrupt_sim_thread = true;
sim_thread.join();
return 0;
}