Physics Simulation & Visualization Tool 0.1
A C++ physics simulation engine with real-time 3D visualization
Loading...
Searching...
No Matches
ProblemRouter.cpp
Go to the documentation of this file.
1#include "ProblemRouter.h"
2#include <iostream>
3#include <set>
4
5#include "InterceptSolver.h"
7
8namespace {
9constexpr float kMissingTargetDistance = 99999.0f;
10constexpr double kVelocitySolverTolerance = 1.0e-3;
11constexpr int kVelocitySolverMaxIterations = 30;
12constexpr double kVelocitySolverJacobianStep = 0.01;
13constexpr double kVelocitySolverDamping = 1.0;
14}
15
16ProblemRouter::ProblemRouter(Physics::PhysicsSystem& physics) : physicsSystem(physics) {
17 registerKinematicsProblems();
18}
19
20SolverDecision ProblemRouter::routeProblem(Physics::PhysicsBody* body, const std::unordered_map<std::string,double>& knowns, const std::string& unknown) const {
21 // Case 1: We know all initial conditions
22 if (knowns.find("r0") != knowns.end() && knowns.find("v0") != knowns.end()) {
23 return {SolverMode::SIMULATE, nullptr}; // TODO
24 }
25
26 // Case 2: Unknown requires solving
27 auto solver = makeSolver(body, knowns, unknown);
28 return {SolverMode::SOLVE, std::move(solver)};
29}
30
31std::vector<std::vector<std::string>> ProblemRouter::getRequiredKeys(const std::string &unknown) const {
32 std::vector<std::vector<std::string>> options;
33
34 auto it = solverMap.find(unknown);
35 if (it != solverMap.end()) {
36 // Iterate over all registered ways to solve this problem
37 for (const auto& entry : it->second) {
38 options.push_back(entry.requiredKeys);
39 }
40 }
41 return options;
42}
43
44bool ProblemRouter::areRequirementsMet(const std::vector<std::string>& required, const std::unordered_map<std::string, double>& knowns) const {
45 return std::all_of(required.begin(), required.end(), [&](const std::string& key) {
46 return knowns.find(key) != knowns.end();
47 });
48}
49
50std::unique_ptr<ISolver> ProblemRouter::makeSolver(Physics::PhysicsBody* body, const std::unordered_map<std::string, double> &knowns, const std::string &unknown) const {
51 auto it = solverMap.find(unknown);
52 if (it == solverMap.end()) {
53 std::cerr << "No solver registered for unknown: " << unknown << std::endl;
54 return nullptr;
55 }
56
57 for (const auto& entry : it->second) {
58 if (areRequirementsMet(entry.requiredKeys, knowns)) {
59 return entry.factory(body, knowns);
60 }
61 }
62
63 std::cerr << "Insufficient knowns provided for unknown: " << unknown << std::endl;
64 return nullptr;
65}
66
67void ProblemRouter::registerKinematicsProblems() {
68 constexpr double maxSimTime = 10.0; // Seconds
69
70 // SolverEntry v0Entry;
71 // v0Entry.requiredKeys = {"r0_x","r0_y","r0_z","rT_x","rT_y","rT_z", "T"};
72 // v0Entry.factory = [this](Physics::PhysicsBody* body, const std::unordered_map<std::string,double>& knowns) {
73 // glm::vec3 r0(knowns.at("r0_x"), knowns.at("r0_y"), knowns.at("r0_z"));
74 // glm::vec3 rT(knowns.at("rT_x"), knowns.at("rT_y"), knowns.at("rT_z"));
75 // double T = knowns.at("T");
76 //
77 // auto setter = [=](const glm::vec3& v0){
78 // physicsSystem.reset();
79 // body->setVelocity(v0, BodyLock::LOCK);
80 // };
81 // auto stopCondition = [=]() -> bool {
82 // const auto& frames = body->getAllFrames(BodyLock::LOCK);
83 // if (frames.empty()) return false;
84 //
85 // // Wait until maxSimTime reached
86 // if (physicsSystem.simTime >= T) return true;
87 //
88 // // Also stop if the object is effectively at rest
89 // const glm::vec3& v = frames.back().velocity;
90 // if (glm::length(v) < 1e-3f) return true;
91 //
92 // return false;
93 // };
94 // auto extractVector = [=]() -> glm::vec3 {
95 // const auto& frames = body->getAllFrames(BodyLock::LOCK);
96 // if (frames.empty()) return glm::vec3(0.0f);
97 //
98 // // Take the last frame's position as the output
99 // return frames.back().position;
100 // };
101 //
102 // glm::vec3 target = rT;
103 //
104 // return std::make_unique<VectorRootSolver<glm::vec3, glm::vec3>>(
105 // setter, stopCondition, extractVector, target,
106 // 1e-3, 30, 0.01, 1.0
107 // );
108 // };
109 // solverMap["v0"].push_back(v0Entry);
110 //
111 // SolverEntry tEntry;
112 // tEntry.requiredKeys = {"r0_x","r0_y","r0_z", "v0_x","v0_y","v0_z", "rT_x","rT_y","rT_z"};
113 // tEntry.factory = [this](Physics::PhysicsBody* body, const std::unordered_map<std::string,double>& knowns) {
114 // glm::vec3 r0(knowns.at("r0_x"), knowns.at("r0_y"), knowns.at("r0_z"));
115 // glm::vec3 v0(knowns.at("v0_x"), knowns.at("v0_y"), knowns.at("v0_z"));
116 // glm::vec3 rT(knowns.at("rT_x"), knowns.at("rT_y"), knowns.at("rT_z"));
117 //
118 // physicsSystem.reset();
119 // body->setPosition(r0, BodyLock::LOCK);
120 // body->setVelocity(v0, BodyLock::LOCK);
121 //
122 // auto monitor = [=]() -> float {
123 // glm::vec3 currentPos = body->getPosition(BodyLock::LOCK);
124 // float dist = glm::distance(currentPos, rT);
125 //
126 // std::cout << dist << std::endl;
127 // if (dist < 1e-3) return -1.0f;
128 //
129 // glm::vec3 currentVel = body->getVelocity(BodyLock::LOCK);
130 // glm::vec3 toTarget = rT - currentPos;
131 //
132 // return glm::dot(currentVel, toTarget);
133 // };
134 //
135 // auto timeout = [=]() -> bool {
136 // return physicsSystem.simTime > 60.0f;
137 // };
138 //
139 // return std::make_unique<InterceptSolver>(monitor, timeout);
140 // };
141 // solverMap["T"].push_back(tEntry);
142
143 // TODO: make a helper and make an enum or something for the required keys
144 SolverEntry eventEntry;
145 eventEntry.requiredKeys = {
146 "r0_x", "r0_y", "r0_z",
147 "v0_x", "v0_y", "v0_z",
148 "Stop_SubjectID", "Stop_Prop", "Stop_Op", "Stop_Val",
149 "Stop_TargetID",
150 "Stop_Val_X", "Stop_Val_Y", "Stop_Val_Z"
151 };
152
153 eventEntry.factory = [this](Physics::PhysicsBody* body, const std::unordered_map<std::string, double>& knowns) {
154 glm::vec3 r0(knowns.at("r0_x"), knowns.at("r0_y"), knowns.at("r0_z"));
155 glm::vec3 v0(knowns.at("v0_x"), knowns.at("v0_y"), knowns.at("v0_z"));
156
157 physicsSystem.reset();
158 body->setPosition(r0, BodyLock::LOCK);
159 body->setVelocity(v0, BodyLock::LOCK);
160
161 int subjectID = (int)knowns.at("Stop_SubjectID");
162 int prop = (int)knowns.at("Stop_Prop");
163 int op = (int)knowns.at("Stop_Op");
164 float val = (float)knowns.at("Stop_Val");
165
166 if (subjectID == -1) {
167 std::cout << "Solver Error: No subject selected." << std::endl;
168 auto dummyMonitor = []() { return -1.0f; };
169 auto dummyTimeout = []() { return true; };
170 return std::make_unique<InterceptSolver>(dummyMonitor, dummyTimeout);
171 }
172
173 Physics::PhysicsBody* subject = physicsSystem.getBodyById(subjectID);
174 if (!subject) { /* Handle missing subject safely if needed */ }
175
176 int targetID = (int)knowns.at("Stop_TargetID");
177 glm::vec3 targetPoint(knowns.at("Stop_Val_X"), knowns.at("Stop_Val_Y"), knowns.at("Stop_Val_Z"));
178 Physics::PhysicsBody* targetBody = physicsSystem.getBodyById(targetID);
179
180 auto monitor = [=, this]() -> float {
181 float currentVal = 0.0f;
182
183 switch (prop) {
184 case 0: // Pos Y
185 currentVal = subject->getPosition(BodyLock::LOCK).y;
186 break;
187
188 case 1: // Vel Y
189 currentVal = subject->getVelocity(BodyLock::LOCK).y;
190 break;
191
192 case 2: // Distance to Object
193 {
194 if (targetBody) {
195 auto getClosestOnBody = [&](Physics::PhysicsBody* b, const glm::vec3& targetPos) -> glm::vec3 {
196 if (auto* col = b->getCollider()) {
197 return col->closestPoint(targetPos).point;
198 } else {
199 return b->getPosition(BodyLock::LOCK);
200 }
201 };
202
203 glm::vec3 centerSubject = subject->getPosition(BodyLock::LOCK);
204 glm::vec3 pTarget = getClosestOnBody(targetBody, centerSubject);
205 glm::vec3 pSubject = getClosestOnBody(subject, pTarget);
206
207 currentVal = glm::distance(pSubject, pTarget);
208 } else {
209 currentVal = kMissingTargetDistance;
210 }
211 break;
212 }
213
214 case 3: // Distance to Point
215 currentVal = glm::distance(subject->getPosition(BodyLock::LOCK), targetPoint);
216 break;
217
218 case 4: // Time
219 currentVal = physicsSystem.simTime;
220 break;
221 }
222
223 if (op == 0) {
224 return (currentVal - val);
225 }
226 else {
227 return (val - currentVal);
228 }
229 };
230
231 auto timeout = [=]() { return false; }; // TODO: temporary no timeout
232
233 return std::make_unique<InterceptSolver>(monitor, timeout);
234 };
235 solverMap["Event"].push_back(eventEntry);
236
237 SolverEntry v0Entry;
238 v0Entry.requiredKeys = {
239 "r0_x", "r0_y", "r0_z",
240 "Stop_SubjectID", "Stop_Prop", "Stop_Op", "Stop_Val",
241 "Stop_TargetID",
242 "Stop_Val_X", "Stop_Val_Y", "Stop_Val_Z",
243 "Target_Time"
244 };
245
246 v0Entry.factory = [this](Physics::PhysicsBody* body, const std::unordered_map<std::string, double>& knowns) {
247 int subjectID = (int)knowns.at("Stop_SubjectID");
248 int stopTargetID = (int)knowns.at("Stop_TargetID");
249
250 double targetTime = knowns.count("Target_Time") ? knowns.at("Target_Time") : -1.0;
251
252 Physics::PhysicsBody* subjectBody = physicsSystem.getBodyById(subjectID);
253 Physics::PhysicsBody* targetBody = physicsSystem.getBodyById(stopTargetID);
254
255 glm::vec3 r0(knowns.at("r0_x"), knowns.at("r0_y"), knowns.at("r0_z"));
256 int prop = (int)knowns.at("Stop_Prop");
257 int op = (int)knowns.at("Stop_Op");
258 float val = (float)knowns.at("Stop_Val");
259 glm::vec3 stopTargetPoint(knowns.at("Stop_Val_X"), knowns.at("Stop_Val_Y"), knowns.at("Stop_Val_Z"));
260
261 glm::vec3 targetPos(0.0f);
262 if (prop == 3) {
263 targetPos = stopTargetPoint;
264 } else if (prop == 2 && targetBody) {
265 targetPos = targetBody->getPosition(BodyLock::LOCK);
266 }
267
268 auto setter = [=, this](const glm::vec3& guess_v0) {
269 physicsSystem.reset();
270 body->setVelocity(guess_v0, BodyLock::LOCK);
271 };
272
273 auto stopCondition = [=, this]() -> bool {
274 if (targetTime > 0.0f) {
275 return physicsSystem.simTime >= targetTime;
276 }
277 if (!subjectBody) return true; // Safety check
278
279 float currentVal = 0.0f;
280 switch (prop) {
281 case 0:
282 currentVal = subjectBody->getPosition(BodyLock::LOCK).y;
283 break;
284 case 1:
285 currentVal = subjectBody->getVelocity(BodyLock::LOCK).y;
286 break;
287 case 2:
288 if (targetBody) {
289 currentVal = glm::distance(subjectBody->getPosition(BodyLock::LOCK),
290 targetBody->getPosition(BodyLock::LOCK));
291 } else {
292 currentVal = kMissingTargetDistance;
293 }
294 break;
295 case 3:
296 currentVal = glm::distance(subjectBody->getPosition(BodyLock::LOCK), stopTargetPoint);
297 break;
298 }
299
300 if (op == 0) return (currentVal <= val);
301 else return (currentVal >= val);
302 };
303
304 auto extractor = [=]() -> glm::vec3 {
305 return body->getPosition(BodyLock::LOCK);
306 };
307
308 return std::make_unique<VectorRootSolver<glm::vec3, glm::vec3>>(
309 setter, stopCondition, extractor, targetPos,
310 kVelocitySolverTolerance,
311 kVelocitySolverMaxIterations,
312 kVelocitySolverJacobianStep,
313 kVelocitySolverDamping
314 );
315 };
316
317 solverMap["v0"].push_back(v0Entry);
318}
void setPosition(const glm::vec3 &pos, BodyLock lock)
glm::vec3 getVelocity(BodyLock lock) const
void setVelocity(const glm::vec3 &vel, BodyLock lock)
glm::vec3 getPosition(BodyLock lock) const
virtual Bounding::ICollider * getCollider() const
Definition PhysicsBody.h:83
PhysicsBody * getBodyById(uint32_t id) const
std::vector< std::vector< std::string > > getRequiredKeys(const std::string &unknown) const
ProblemRouter(Physics::PhysicsSystem &physicsSystem)
std::unique_ptr< ISolver > makeSolver(Physics::PhysicsBody *body, const std::unordered_map< std::string, double > &knowns, const std::string &unknown) const
SolverDecision routeProblem(Physics::PhysicsBody *body, const std::unordered_map< std::string, double > &knowns, const std::string &unknown) const