From 6e00f22ce4e1eb7e5c202e6d1f91b6f986f9ff4d Mon Sep 17 00:00:00 2001 From: Travis Hunter Date: Fri, 31 Mar 2023 18:38:35 -0600 Subject: [PATCH] WIP --- .gitignore | 2 + QtRocket.cpp | 18 +- QtRocket.h | 30 +-- QtRocket.ui | 116 ---------- gui/AboutWindow.cpp | 21 ++ gui/AboutWindow.h | 25 +++ gui/AboutWindow.ui | 48 +++++ gui/MainWindow.cpp | 130 ++++++++++++ gui/MainWindow.h | 28 +++ gui/MainWindow.ui | 279 +++++++++++++++++++++++++ gui/RocketTreeView.cpp | 6 + gui/RocketTreeView.h | 18 ++ qcustomplot.cpp => gui/qcustomplot.cpp | 0 qcustomplot.h => gui/qcustomplot.h | 0 main.cpp | 4 +- qtrocket.pro | 19 +- qtrocket.qrc | 11 +- resources/qtrocket.png | Bin 31670 -> 0 bytes resources/rocket64.png | Bin 0 -> 3316 bytes sim/DESolver.h | 3 +- sim/RK4Solver.cpp | 10 +- sim/RK4Solver.h | 9 +- sim/StateData.cpp | 6 + sim/StateData.h | 29 +++ utils/Logger.h | 2 +- utils/ThreadPool.cpp | 6 + utils/ThreadPool.h | 17 ++ utils/math/Vector3.h | 2 +- 28 files changed, 680 insertions(+), 159 deletions(-) delete mode 100644 QtRocket.ui create mode 100644 gui/AboutWindow.cpp create mode 100644 gui/AboutWindow.h create mode 100644 gui/AboutWindow.ui create mode 100644 gui/MainWindow.cpp create mode 100644 gui/MainWindow.h create mode 100644 gui/MainWindow.ui create mode 100644 gui/RocketTreeView.cpp create mode 100644 gui/RocketTreeView.h rename qcustomplot.cpp => gui/qcustomplot.cpp (100%) rename qcustomplot.h => gui/qcustomplot.h (100%) delete mode 100644 resources/qtrocket.png create mode 100644 resources/rocket64.png create mode 100644 sim/StateData.cpp create mode 100644 sim/StateData.h create mode 100644 utils/ThreadPool.cpp create mode 100644 utils/ThreadPool.h diff --git a/.gitignore b/.gitignore index dba50a0..0067128 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,5 @@ build/ # IDE .vscode/qtrocket.pro.user qtrocket.pro.user +.qmake.stash + diff --git a/QtRocket.cpp b/QtRocket.cpp index 69454b9..caef6ff 100644 --- a/QtRocket.cpp +++ b/QtRocket.cpp @@ -1,15 +1,17 @@ #include "QtRocket.h" -#include "ui_QtRocket.h" -QtRocket::QtRocket(QWidget *parent) - : QMainWindow(parent) - , ui(new Ui::QtRocket) +QtRocket* QtRocket::instance = nullptr; + +QtRocket* QtRocket::getInstance() { - ui->setupUi(this); + if(!instance) + { + instance = new QtRocket(); + } + return instance; } -QtRocket::~QtRocket() +QtRocket::QtRocket() { - delete ui; + logger = utils::Logger::getInstance(); } - diff --git a/QtRocket.h b/QtRocket.h index a68464b..33e5216 100644 --- a/QtRocket.h +++ b/QtRocket.h @@ -1,21 +1,27 @@ #ifndef QTROCKET_H #define QTROCKET_H -#include +#include "utils/Logger.h" +#include "gui/MainWindow.h" -QT_BEGIN_NAMESPACE -namespace Ui { class QtRocket; } -QT_END_NAMESPACE - -class QtRocket : public QMainWindow +/** + * @brief The QtRocket class is the master controller for the QtRocket application. + * It is the singleton that controls the interaction of the various components of + * the QtRocket program + */ +class QtRocket { - Q_OBJECT - public: - QtRocket(QWidget *parent = nullptr); - ~QtRocket(); - + static QtRocket* getInstance(); private: - Ui::QtRocket *ui; + QtRocket(); + + static QtRocket* instance; + + utils::Logger* logger; + + + }; + #endif // QTROCKET_H diff --git a/QtRocket.ui b/QtRocket.ui deleted file mode 100644 index 4dc9439..0000000 --- a/QtRocket.ui +++ /dev/null @@ -1,116 +0,0 @@ - - - QtRocket - - - - 0 - 0 - 1031 - 694 - - - - QtRocket - - - - :/qtrocket.png:/qtrocket.png - - - - - - - Qt::Vertical - - - - - 0 - 2 - - - - Qt::Horizontal - - - - - 0 - 0 - - - - - - - 2 - 0 - - - - - - 30 - 20 - 80 - 25 - - - - PushButton - - - - - - 30 - 70 - 80 - 25 - - - - PushButton - - - - - - - - 0 - 1 - - - - - - - - - - - 0 - 0 - 1031 - 22 - - - - - - - - QCustomPlot - QWidget -
qcustomplot.h
- 1 -
-
- - - - -
diff --git a/gui/AboutWindow.cpp b/gui/AboutWindow.cpp new file mode 100644 index 0000000..4fc59fe --- /dev/null +++ b/gui/AboutWindow.cpp @@ -0,0 +1,21 @@ +#include "AboutWindow.h" +#include "ui_AboutWindow.h" + +AboutWindow::AboutWindow(QWidget *parent) : + QDialog(parent), + ui(new Ui::AboutWindow) +{ + ui->setupUi(this); + + setWindowTitle(QString("About QtRocket")); +} + +AboutWindow::~AboutWindow() +{ + delete ui; +} + +void AboutWindow::on_pushButton_clicked() +{ + this->close(); +} diff --git a/gui/AboutWindow.h b/gui/AboutWindow.h new file mode 100644 index 0000000..8e05a10 --- /dev/null +++ b/gui/AboutWindow.h @@ -0,0 +1,25 @@ +#ifndef ABOUTWINDOW_H +#define ABOUTWINDOW_H + +#include + +namespace Ui { +class AboutWindow; +} + +class AboutWindow : public QDialog +{ + Q_OBJECT + +public: + explicit AboutWindow(QWidget *parent = nullptr); + ~AboutWindow(); + +private slots: + void on_pushButton_clicked(); + +private: + Ui::AboutWindow *ui; +}; + +#endif // ABOUTWINDOW_H diff --git a/gui/AboutWindow.ui b/gui/AboutWindow.ui new file mode 100644 index 0000000..9293576 --- /dev/null +++ b/gui/AboutWindow.ui @@ -0,0 +1,48 @@ + + + AboutWindow + + + + 0 + 0 + 400 + 300 + + + + Dialog + + + + + 80 + 60 + 261 + 151 + + + + true + + + Copyright (c) 2023 by Travis Hunter + + + + + + 250 + 240 + 80 + 25 + + + + OK + + + + + + diff --git a/gui/MainWindow.cpp b/gui/MainWindow.cpp new file mode 100644 index 0000000..93a8feb --- /dev/null +++ b/gui/MainWindow.cpp @@ -0,0 +1,130 @@ +#include "MainWindow.h" +#include "ui_MainWindow.h" +#include "AboutWindow.h" + +#include "utils/math/Vector3.h" +#include "sim/RK4Solver.h" + +#include + +#include +#include + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + + +void MainWindow::on_actionAbout_triggered() +{ + AboutWindow about; + about.setModal(true); + about.exec(); + +} + + +void MainWindow::on_testButton1_clicked() +{ + auto& plot = ui->plotWindow; + // generate some data: + QVector x(101), y(101); // initialize with entries 0..100 + for (int i=0; i<101; ++i) + { + x[i] = i/50.0 - 1; // x goes from -1 to 1 + y[i] = x[i]*x[i]; // let's plot a quadratic function + } + // create graph and assign data to it: + plot->addGraph(); + plot->graph(0)->setData(x, y); + // give the axes some labels: + plot->xAxis->setLabel("x"); + plot->yAxis->setLabel("y"); + // set axes ranges, so we see all data: + plot->xAxis->setRange(-1, 1); + plot->yAxis->setRange(0, 1); + plot->replot(); + +} + + +void MainWindow::on_testButton2_clicked() +{ + // Get the initial conditions + double initialVelocity = + ui->rocketPartButtons->findChild(QString("initialVelocity"))->text().toDouble(); + + double mass = + ui->rocketPartButtons->findChild(QString("mass"))->text().toDouble(); + + double initialAngle = + ui->rocketPartButtons->findChild(QString("initialAngle"))->text().toDouble(); + + double dragCoeff = + ui->rocketPartButtons->findChild(QString("dragCoeff"))->text().toDouble(); + + double initialVelocityX = initialVelocity * std::cos(initialAngle / 57.2958); + double initialVelocityY = initialVelocity * std::sin(initialAngle / 57.2958); + math::Vector3 initialVelVector(initialVelocityX, initialVelocityY, 0.0); + std::vector position; + position.emplace_back(0.0, 0.0, 0.0); + + std::vector velocity; + velocity.push_back(initialVelVector); + + double ts = 0.01; + + sim::RK4Solver velXSolver([=](double x, double t) -> double { return 0.0; }); + velXSolver.setTimeStep(ts); + sim::RK4Solver velYSolver([=](double y, double t) -> double { return -9.8; }); + velYSolver.setTimeStep(ts); + + sim::RK4Solver posXSolver([=](double x, double t) -> double { return velXSolver.step(x, t); }); + posXSolver.setTimeStep(ts); + sim::RK4Solver posYSolver([=](double y, double t) -> double { return velYSolver.step(x, t); }); + posYSolver.setTimeStep(ts); + + + // These can be solved independently for now. Maybe figure out how to merge them later + size_t maxTs = std::ceil(100.0 / ts); + QTextStream cout(stdout); + cout << "Initial X velocity: " << initialVelocityX << "\n"; + cout << "Initial Y velocity: " << initialVelocityY << "\n"; + for(size_t i = 0; i < maxTs; ++i) + { + position.emplace_back(posXSolver.step(position[i].getX1(), i * ts), + posYSolver.step(position[i].getX2(), i * ts), + 0.0); + + cout << "(" << position[i].getX1() << ", " << position[i].getX2() << ")\n"; + + } + + auto& plot = ui->plotWindow; + // generate some data: + QVector x(position.size()), y(position.size()); + for (int i = 0; i < x.size(); ++i) + { + x[i] = position[i].getX1(); + y[i] = position[i].getX2(); + } + // create graph and assign data to it: + plot->addGraph(); + plot->graph(0)->setData(x, y); + // give the axes some labels: + plot->xAxis->setLabel("x"); + plot->yAxis->setLabel("y"); + // set axes ranges, so we see all data: + plot->xAxis->setRange(*std::min_element(std::begin(x), std::end(x)), *std::max_element(std::begin(x), std::end(x))); + plot->yAxis->setRange(*std::min_element(std::begin(y), std::end(y)), *std::max_element(std::begin(y), std::end(y))); + plot->replot(); +} + diff --git a/gui/MainWindow.h b/gui/MainWindow.h new file mode 100644 index 0000000..767356f --- /dev/null +++ b/gui/MainWindow.h @@ -0,0 +1,28 @@ +#ifndef QTROCKET_H +#define QTROCKET_H + +#include + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void on_actionAbout_triggered(); + + void on_testButton1_clicked(); + + void on_testButton2_clicked(); + +private: + Ui::MainWindow* ui; +}; +#endif // QTROCKET_H diff --git a/gui/MainWindow.ui b/gui/MainWindow.ui new file mode 100644 index 0000000..25efea6 --- /dev/null +++ b/gui/MainWindow.ui @@ -0,0 +1,279 @@ + + + MainWindow + + + + 0 + 0 + 1031 + 694 + + + + QtRocket + + + + :/images/resources/rocket64.png:/images/resources/rocket64.png + + + + + + 6 + 6 + 1021 + 631 + + + + + 0 + 0 + + + + Qt::Vertical + + + true + + + + Qt::Horizontal + + + true + + + + + 0 + 0 + + + + + + + 2 + 0 + + + + + + 30 + 20 + 80 + 25 + + + + + 0 + 0 + + + + TestButton1 + + + + + + 30 + 70 + 191 + 25 + + + + + 0 + 0 + + + + Calculate Ballistic Trajectory + + + + + + 260 + 80 + 161 + 120 + + + + + + + Velocity + + + + + + + + + + Angle + + + + + + + + + + mass + + + + + + + + + + Cd + + + + + + + + + + + + + + + + + 0 + 0 + 1031 + 22 + + + + + File + + + + + + + + + + + + Edit + + + + + Tools + + + + + Help + + + + + + + + + + + + + .. + + + About + + + + + + .. + + + New + + + + + + .. + + + Open + + + + + + .. + + + Save + + + + + Close + + + + + + .. + + + Quit + + + + + + .. + + + Save As + + + + + + RocketTreeView + QTreeView +
gui/RocketTreeView.h
+
+ + QCustomPlot + QWidget +
gui/qcustomplot.h
+ 1 +
+
+ + + + +
diff --git a/gui/RocketTreeView.cpp b/gui/RocketTreeView.cpp new file mode 100644 index 0000000..7052800 --- /dev/null +++ b/gui/RocketTreeView.cpp @@ -0,0 +1,6 @@ +#include "RocketTreeView.h" + +RocketTreeView::RocketTreeView(QWidget* parent) : QTreeView(parent) +{ + +} diff --git a/gui/RocketTreeView.h b/gui/RocketTreeView.h new file mode 100644 index 0000000..82b72d8 --- /dev/null +++ b/gui/RocketTreeView.h @@ -0,0 +1,18 @@ +#ifndef ROCKETTREEVIEW_H +#define ROCKETTREEVIEW_H + +#include + +/** + * @brief RocketTreeView basically just renames QTreeView with a specific + * memorable name. + */ +class RocketTreeView : public QTreeView +{ + Q_OBJECT + +public: + RocketTreeView(QWidget* parent = nullptr); +}; + +#endif // ROCKETTREEVIEW_H diff --git a/qcustomplot.cpp b/gui/qcustomplot.cpp similarity index 100% rename from qcustomplot.cpp rename to gui/qcustomplot.cpp diff --git a/qcustomplot.h b/gui/qcustomplot.h similarity index 100% rename from qcustomplot.h rename to gui/qcustomplot.h diff --git a/main.cpp b/main.cpp index e0c4bb5..db1c204 100644 --- a/main.cpp +++ b/main.cpp @@ -1,4 +1,4 @@ -#include "QtRocket.h" +#include "gui/MainWindow.h" #include #include @@ -24,7 +24,7 @@ int main(int argc, char *argv[]) } // Go! - QtRocket w; + MainWindow w; w.show(); return a.exec(); } diff --git a/qtrocket.pro b/qtrocket.pro index 5ede81c..ebddb06 100644 --- a/qtrocket.pro +++ b/qtrocket.pro @@ -9,33 +9,41 @@ CONFIG += c++17 #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 SOURCES += \ - main.cpp \ QtRocket.cpp \ + gui/AboutWindow.cpp \ + gui/qcustomplot.cpp \ + main.cpp \ + gui/RocketTreeView.cpp \ + gui/MainWindow.cpp \ model/Motor.cpp \ model/MotorCase.cpp \ model/Thrustcurve.cpp \ - qcustomplot.cpp \ sim/AtmosphericModel.cpp \ sim/GravityModel.cpp \ sim/Propagator.cpp \ sim/RK4Solver.cpp \ sim/SphericalGeoidModel.cpp \ sim/SphericalGravityModel.cpp \ + sim/StateData.cpp \ sim/USStandardAtmosphere.cpp \ sim/WindModel.cpp \ utils/BinMap.cpp \ utils/CurlConnection.cpp \ utils/Logger.cpp \ + utils/ThreadPool.cpp \ utils/ThrustCurveAPI.cpp \ utils/math/Quaternion.cpp \ utils/math/Vector3.cpp HEADERS += \ QtRocket.h \ + gui/AboutWindow.h \ + gui/RocketTreeView.h \ + gui/MainWindow.h \ + gui/qcustomplot.h \ model/Motor.h \ model/MotorCase.h \ model/Thrustcurve.h \ - qcustomplot.h \ sim/AtmosphericModel.h \ sim/DESolver.h \ sim/GeoidModel.h \ @@ -44,18 +52,21 @@ HEADERS += \ sim/RK4Solver.h \ sim/SphericalGeoidModel.h \ sim/SphericalGravityModel.h \ + sim/StateData.h \ sim/USStandardAtmosphere.h \ sim/WindModel.h \ utils/BinMap.h \ utils/CurlConnection.h \ utils/Logger.h \ + utils/ThreadPool.h \ utils/ThrustCurveAPI.h \ utils/math/Constants.h \ utils/math/Quaternion.h \ utils/math/Vector3.h FORMS += \ - QtRocket.ui + gui/AboutWindow.ui \ + gui/MainWindow.ui TRANSLATIONS += \ qtrocket_en_US.ts diff --git a/qtrocket.qrc b/qtrocket.qrc index 32c6ffd..a00b530 100644 --- a/qtrocket.qrc +++ b/qtrocket.qrc @@ -1,7 +1,6 @@ - - - - resources/qtrocket.png - + + + + resources/rocket64.png + - diff --git a/resources/qtrocket.png b/resources/qtrocket.png deleted file mode 100644 index b41bf2bd5d2dcd5bd8501a42fa8c3ca29f6ad98c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 31670 zcmeFZ`9IWq_&!duG%Bh&?V$xTMG`esV>=}c#zfhRvLzXcM#jEObwq_hnX#2I*(zfx z*`2aRA`00mYe?Bb@wuPVdA~p3KjHhs=Z90B9y71kbGh&9zOL)O^$&%7d=+LhMnFJd z)d{_$#sUJ%F2H}4V$0xf`Z3RA1q6KjP8`)fb?xkE!isxmPRIOq6Ap<~e*Nj)g90JN zu)rN;!Q+RtxBO)<863K4+4hy^q&lY47^_8rd&&zAo_!* zBpH5{5fqb$-|h;8u7lt71S(g+Z@UEgmcwsr1KrVzS7@oh7Xp{du;G8Smi@o4{l6pm z|6!D9iO)5-`9BO04-7+LE0UPBIK|b)CtdsW&o}?sA_>i36J}-etpW4gfcZMJ(g#ah z1x}+dU%4xlsVgV#H}&?3-}IWxr=S-Chi*Wj-mLuM}7E{q?0Al({9M!D5`IOj8&xuMyT(-4eqGmic>KBDa5G0gR-tf|SO0pc_1j;84-g*s zg-l1ij9UKV@V3%D$vw(Xed9G>S2`PT71=gFD>rCN-hcD)O1r>X_Dwxb_Dj4nd`F6U}yVLbqmuHJ1Y{DY%!rQsQB)7%r#d`vJqHxlWw%I95`;00- z^r*r|o#mOw_beses;;7R;0@))s6!~yFUp2Kk1UahCi6s@06uss@;F&UZ{ z1dP!wW%Ru-Tg;Cn@a}oa>MusUf}0kVVg~OX%QNH_XZO#^95AoHqdr_P^P^s=Y@$+p z@b&%4f29{G0aF4;g&?b#{e-f)2&TcL%Zod*we~r``Zg}@7g@TkjlW=ic(|Z#_FnX0 z$WP_L2txOB``V~iw>Bu5KkXgOeRF+yxdU22nBCF(q5Er|ext?4HpOJU(@$qEjp+RI z$mFnV-omk=!-4k-mU%6!uYFo!H?6mS4lnmrcE$GL zPwVWC;Cp*ial-leE$hj4hrA08?vu9H9QZZG)hxO8{geLQ!Y{>>jfcnbT3#!`%Lx%k~tKcUuYN5*ih{ia)Qtyg^U^V6+28VjS48=H&{X$zu#`6l|$JMTQH`*>MY zEq%^&IZ697I*z((cH^9k|7-8b1_Gq&w6E3SfW=?x9s>&2&nZhLf;9+?#J+#@c=NU^ z>!iZYGrCnnrlupZCuH!S%2xRAxLfw*OG|o!Mj!K8{tsm?TVh8ab5i?RkN@Et#*d4C zkX~db5Zt;~$g^#`OBV@3lFQ+76X!$gZbq-!9e+m-ueP0m5%l_&w zFHMqm42{$?ypHT`l_}-%BOdFcar2@i1lfW2)*i3^GHY@j`&j64M|1JoWYu(;G@k$k zUJp-uD^F)Tzq=(HJsq{nfU6t zz7k|!H2F~NFP#94zyu2SBX^YJd*<7oh0AU7+pqEZX6#8N+504=6B`0zZzw)6KFs+x zwSxO1yT6*9 z?zPIBjTSo|)_UcVYxAamYzVfwy*(hBf~otFp4TGJ9w|8JsBtsYq*3&;GeUyr?%TWC z5`BNvsyM!GO2P^pfymoP3qQJPEJZ$EH))6(TNS{x@1WO`oPWkJemybPa2;cM6%^3w zIxQEJx$zQLW<+Z@FQ>mgk;1&S_r*=B%N^p(2Wi`i12-Xn$|-g)n|~hke{e!KTFPlR zEGSC&hbB>M)fvSHB#7b+{Ps@Ijyvi?liIWZ$yS+1)$G9puWq<2BelNe;bK905Wy?) z5PI~sYiiD_!&K7~Y1wgEx65XBs(R+%+&#C_MV>{LWzX1=dweCuAq-!9uiAodD7^6Y z0ZPwblW&qB4Dnp{l`oOU;|ZCXh63hji@J$E_y2Ttk$4^qXn-v~t0o3P=-p3i-azbEC!n^L8I& z-frKicS&`QYFcQ(iOah6=4kR!W`9c?IcYf0$;EF{JE+3)fOM*q2fVn@W8k%VU+B8< znKa`N0KZ4Sg$1qvdmcd7WHVdZ&T8DG>R*%=}gY1WzE&`qq*|xZSVGg@) z?3YiC_a*AJ=d|}(3iV^PYtdPq{1cUN$)h%HGA|pLFyv!Ez?M~^_7Bt+FE_n>Yjn^$C&-m zdC^Z?UB^6ci!r1<CU;nCS#bQy&uWdSY@_3w!yp_=;IU%j3G&|;wx@(`pKJ)rXZRWs{ z$=51I>ix|7Do&pY)`teUFKcoPw^MnbbBwNvdHFuK?n1}jmNtFUqsF9vzY!;-A!|BL z{1{eEn7MhE{yKdikaUpaa`JRT~A*zeE0ARw{%WDVH<6(U*xvBAyQMCicBcG%X|GFbSZzQt#Z)$Q@ zg2-I4$(axHR2N92prc`DrpdR|-JbV}96+mddVTNQ(Nh6Bf*TpyuQg&duFpsx^ZN19ndhx$NxIchnOqH|eE5b)Oe;0osD<9H0#u zPS5VP`oPnFSGOdY{Mqxta24@KayDmEV zZ5c&`7b3*ED$TP#ftd#1CPa1yZw1@}f3 z@^uDscvcz>EQg+2iNpQT4_it&I-6ICFOY{g`cIsp3}CsnKpO{dSGkeavj1jK>Gf)Cz@yGP?V1$8xHrd_J7J zvAto%B~-h1!!<}Uj1q?~VN{l_Gi}h=Y$!~GlXku%_T7%GF8%T!=C!^&pYDWy?frGm zp`bMotn9q4c7EhtI)ZoNMg*TLcd1I+PlZ&<=S`-DU%YrZGdd=A7?_uG<%yGIE474d zAKqZsdUAVE`9Dk(BjPl=91xLQL)P_bww55E`NXyPVQ$2!8f8mNQcY}#Uh635E%T=V z#G~))^tJfXwvuI)`1cm|IC)uPvRYs0!`p{O0)l{mPDGv7#|+$h(0Fep$Ud?8=oL{h;+Ue)fidM0i-b z+_nY)cc1M>*W~Lm^gMj^g~A)AWTEzq_JteqXF8U?saWMJe#R^A5sr^ z-iz68pRZ2@{~W{GDj(wf(8G_C_7claK0K>+U}5|KTuBRvtPy2?ZZ-Tx?r~IxBe|Nt ziwzu!iyMUNJ$g$Wq`eKZZP|*@b%8+@KlSf-oVs(+TSiaLXme)#!>*k=zE)q^5*Z{l zZ&@yuoRJyR4|)w2{`Wu3e+^iD|r`H+Ku*$FXt{qiv%z8ZWwqZr~NEn)hcr! z1S94&e%dwF-B>hu4TRNb+0x=YiDhd6!9xU3e|;;PZQ$A=q|Mv%XuC?D#%s}?(jGs2 zDOzgjSD#q+F|X#Il2Y{=x851c)~kre%Ak`H*vL_>)QZbvYkntoFzxaTO&i{hSF@q^ zgydfp<4$gJdh9NBtv9IXTWQf^RN!`~jO7BhF9YSBTV=CH4?PtPwy&?KyPht${cn6f zL#1gea~Jzv&_0*8EOfU3IvpNwh|1E-H^LGPt1pOr(>xiS_amxp_Cl^wm)7@t60h;> zsuPD9CdAKqduE@1DG78pesef~Jkq`Kp7Q735DAdjCI5XtqN*n3t&p9UeRL5!_3GJX zbg~tb+HAeZZDIxPyQ_z>jc)#vELyEkhP$!%-8GGnYxcn7l>^KJT+5#>>sy}ucB5?a zeVLsbkF5K`O5Mgh&Wegjm`lFa_sc9Zv+eAjfsfS|25NUS=7#{;JRv#clYDuL9f9YB zp{#kNz&9oKKTmfIez!}}rupEnw{p47Ysq^hew=AtzmA;0?bW8^ISk10`!}}HKKL(; z-8hP|g}hRY16&+}oK@Me<|(T%F3&Q3a}{Yjd%$KpL#0=(b;s8m(*A13YTnXL=4$DK z36`^~!A&yq8!_P~(lZu+{CWM~%Eo=Utj7B?K2ZgIad|IEW$q$dxoaB#+GDEM_p8yX zbe&`)7zr(B?Gtvfr58R_Pnn6W+yj8MIV_KLWZla(SRc6yfpX5WdotINCpURu2EkJ5 z$C5j$H|=BtP%cr6#;#XbYN3+0qmbq?ZP1k9wK(2*I3VEtnSkdD)Q3DF)pakkro^~P z)p*9_LeEm^+D1`nUkV0URFg4Jd>M>MahspPYmlw;H%30DxPB`a+lWdzfI3vRj`5;z ztNWHOhwP9$&yMzeG1t;bU1;Ct_3QkNZHtrJzWr=C+%tD6mKf)-q*U(@za}`8NpG6_ zTvM|Y9{$VO{!2i0#6tb^%K=@^+?a2E--b3hWgT)#RG*J<%Q_Tc&pEI)6(e~qGJj55 zl6fq$(pf{Fq;}eslIp(KG7qP~PWt&$UWOfrIFwrAGuC^Xr7ts`OIESJePVwfsrSF{ zP}azEI+QVIV=>ct+kH`^ez9o{`C)v36roXU(B{4Q=cP7tyMFPr`wsd48j5$hO}@8p z{=tTb$N6nThrWiB6OYix3m*<95a!E;EGy<;lH%O2m?8h3e ze5J&5@1}pvKG&C;Zn?fuXtkOx=o#}Dp6QFmK`Bu9v_9|C{4HHHKN8^o)hdALpxMYv zvTqNtZ&%`tnr-W|FPgJT>KYXre1uk~)PkHhJYSIC}F5}rj4=mS`FJ2>5B2OqbwB?bE z_+X6L+53CgARrK?rIwwt#UT!>5PrmWuv{q4sa+y>v{oUSHmR9AO5iW1$Bxz_Hkfom zEv8GgvXqb_3|GSE?v9EX_-bIEjvjV`Q;nSjXi&O)zI(O72 zcNAYwZ6=OQ(sFmQ?Fma+qiJi%omA@6iXq)C*=rj&lwF(RC~fn5wMX~ahx1bho^0~# z`l|cvK z67Jp&Nvyzrf7VA)e6W%suJmGPN0R$E_$fGvWhjb8zP}u8?`hJNiF>z@esgi=wN4f>2&r(w}6GA!#(QriL>G3&p!`A z>3VOY`NFr*v^eZ;da;!NRSVsz#3mh{tOqJ5IwtW`Vuiu+@&nC@r&=lXZC^J+F% zu2_arRkEjscbs>L@s#Chv}a7NtA*E0DZg)C52NK^U9)8YQz6n@ zySxWauPgM8-J4C)KB2bsvs%;mrpfJhQSR4saix(R?h8Q|ugUine~Hphbx)*^q`x~Y z-nSfs>Pugb;~W3yc7&x@o5gptbd)PMITLzj3f^v`nY$9aK3<+G`R3P6l;5x0&P<&o~Q&J@+~X3G1xs=00C&llQ=~c+GLKx!r<+$ zECS-c7o9NT(NXM54GK}wVP-4=-L>{Et#_Q3lA z%}tsY7sKbG2j19tooQB}UfW+Im7c#LOnBzO#=b9+mVbT9TbK!V-wW<$u=w76mFjNY zM|QHoiKxr6$i1yGe?d$h>c8NH<+Dx}0misGYU$@~9aNEV=Y}Ph(X^9>lhv=K4^}#V zY5R5cv&EBA%Uh}9vh2pc+B6rx-|?`z2=c7)!G~kOat0H99xJk2(hNh~mmzt)lrGD7 zK$1Avt!7Uc!(uuO53K)^%hFYgXxsei!1dM-rVlH0|Jk5)w)IhOb>d3W#q@mnkV}1@ zx8HSaCtuXG?PC*t3f43NgX|U?Ja=Q8V<_;1S=GvaURWZwuC?x|32rGePUl>ag&#(? zmKv`qGnTgI%+3}>6+_a zJK3muuW8(*;<&NzW5Cky?d(8=WKQ;&V;v2~cG}x5fM54%hC_!NSIB|4OJOw2JmM$|Skh)ynkfWoND0U+E1zcT3+9$lY*vsO>3| zWW=VLoGu!qyzDgeQNVRmo6Ya8A@f)ue7bz=txP)`2*u}G^CvQGN=DAGBzh~wk1IX5 zedl46nIW;;yL_0vl9!9)9jp;UsB0fO`RL*89ep3|?hyYgSVon2tTRCf&pb`bg$=|= zZ4^C01F!88VK^~%G;Qu{6*Zo4jMk#Jzuj=M^tF%bx7nB{*3+TWvP#OkORp^cDr5UR zF6J6`$*uojlW=I$OAGAjEuPTI+1z&oEEJ9DbR)k{>ymy>+|XRvw|vibw!qW|a>@r< z3pwR!SC#_fQ!a~!Io58tS=^WwT^0P=N`+)VYbh4zv8q0AE^@Z-yy#DU1kq7PXZcdLG6IEbCE&KwWMsbh-LZti16X}g4 z$c3qO_jPjgCDt0_$VDe+^9pIXl~@dV<$ie61|CZ{yOvry#mUsT*Kly*s~qc>_rr+A z=M^GLC5!V*Ze3TC4v(oGzIooOXiL%k`M~JIA&eP*dqO_)*2~E1halH&|IOJ4O?ugw%f>z3+M?U?zLhSFLnlp=j0n}x z7KpU&=8#lKl!ka?TosCrx0fabe~u0u`g$aHrlahvOSVnv&@s(`KJlf`&p)3ZclKDv zZv5T)ag)7~NV^60qNdtJxzKF8leCyTB9&7&{L?6-%^_Z8hwG^1;c#K~F*yz;e60!Q zRk1iJWn=omZ|{q}q{M%GXjt-nd&^(>T;-dyKPv-lJQ|Y1rI)5&-w)cxR{kr0MyT;# ztqj~srFXfYsP7LD$$+^3v9!gemsnC%S89$-0jm(tR4$qeU|srAw#5~U@=Fs7)9;tE zpR&sK(>y9em(1b8x22cdK36aO6jr)+r7B#y>zbXf)J__pWEteT^dkaeZ2{}Zdiy8c3eh^>W27FzwKqf@od5R&aV+<1*59Fk`UGJ_eMaZvawVKTy}}r)(^8e%Yz_+#tn({j;-?sMy(Hv$1Hda>Jr$!+~1r=04x^5eF8p zMjY^0qkh!9vt{;Y-;K`Mywd*Gj0drBA)@RVd&xWRAbKOQpP+9+jBww4pw&ii9}``( zP(GJ(Wy~=M8^&X?byhH?zNAsYyR3a&c`VHhG>=xIVtd2o&;GWd9`^RHYHDVW>y~;A zr8N3yyBhgI}zbWu1r+GUv52mas_!hutj~-Q440e z3D+NGdd+bSkVJ^aoHfzM$U08TMF^iLve?g9izD77r~H!VQVxL(FmDn+`?QXFyQ}5# zkl5g?uCCO9O7;Cex)c^>f+(b={SGc?5{W(>QeXf?ai3cT*uF&jdMr%W*`K*8m zT_*_zm`>_6#?gkg=dh$|>JRg~1*~m-zPNg+*Sv+?QP#pY=~8Y!Yi3jwN%1%dwSN1i z#f5KgEU;qoGAL7SUr(c)ALZ}vJ`3H-aub}<&79=w>Z;(ah(ZE|THuYkm~f-I@(2$? zLvZX~CVC89^ya>o7qR>l-WC#%){Pbtq{OJCsod3BL(AR6*zZU%ix9rrX$a4G&3h8< z?$}OT82`%R#?0o?B#+6e4GYR&75Q%81UDHrBUFy!WScXd8M)>gDm(gVlqizK6%!n9 zs$6CL7lV`|HuPj;oE=1qIi(u0T*9HsLWTPA2Hq3L_AZM8`vHT&BiSV+1L2dfW27_% zMYldh4LLxZr3r~8m^xSqmnwDgAS23!K=|0?Lhx3of!Z;f7kvhgEt-}1 zC&pnkaV90lr(zqevev3`5&$3OVZe)P6n{Xk9HeH`-S$fo6g_I$R9iN(Z76%P_u&`e^aej4NO z9MLyVy7Nd9#ds$5PktnoaT|C6uX%5r+MRMoXB3LAKI?jv)J8C>*T!0&^#kj0La0$l z;)r{X-)w)a%pbUYNA|_Y+M<=hiX8~`sR(<2V#C~~9s97PSz>k`D`{A4P|>1b%1-*T zf5%6k4vSpJ%fTiZy)W(gmd>MuC_L7c&}uHpCkPa}?WkRG*GevBhrUFcm6A36ZoH0# z;~n7@Mc&6_yJ8&(PVI&kw12ChSuGJsOOGLZeCOFWXXnINvwyY$tbo(3_G{u_ zi6ax`00-mXP2^o)w=-W0c_e4&lXNb37y=$;;29oQ3R_u;*s#4scT^>7?X+(YdrQl3vGQ!uYA=)Gr3hMxh4 z=+|fJ3>UBn`Veqtj_rS0(AvM$X3Um*8|nX*|0{4^<%(U9@)vg$>#gcNGL)Y0R{#yGEkd@LOZakSBatCtA5qdydiRO{D|7OTv*)O8D$a{Fa4SBpLi z@|o|W$QQ;qWm~j`&dkz6jLt+Yh5yIl=9KFtEGcB*>St8I1Ftk%3*_3`<(Po zf!UTr-<%yJ!SunxQbr!|_;D%6I3Fjq@%=p)0{7i>x=9-4iav^1SHc(y^u3#MS+{Da zj_nMKgPQe?-aQNUnu^m>6GTPflaC&(pUp!CY4YAM4#l+H&Q~RdFW4pdNr_TnC|9{ul zjh-Z8jI$^BRc?yb`A3Op9`Jj_%*~R2Tl^xor+Rci)`E62Ho*V;T#>-Hh`;?<5C`P-U8%r^0fjh!GY`8 z{*TW$zmJ!z8}2EG_()txj2(C0&MrsNKygZu5miK2aW~2wO-s;O5hr!6DwvIDO2tTp zBR03A$eS9Q(3->IIZRQ6GB8TPnwQif zu>B{G^$flqqhq0VaQ#b~$LjV5g~J;4xBc7e(~OeHzw>yrV5zeKRURdhjB%M`jyXt{ z?DUDGd0g4YFwUDwem!F7Oc-kpY0V_qkt7s~ysPeY9ob%gyMgzRH=2w$xuBpoBh{O@ zUJWfkIVP2|mF6*)pN|CfrK0vgEU7<};4oaV0WwyNv14@L#I_=JmHITN_7n@?_072g(k^xVS?_~JE%n_~l`3WptDQyXh||K^VNDs(Qr zQd;~XuUWV-UNmVg8M^Mz`y=#H9oXSo@o>a|J1B*`yyv9CAI7I zHh8ScDf&J~g3)W<=R>qfF&@jJz$n)5e6-=d^~L+kSnL4#RJ9x~~+g%@(>SyYBUA*w>U>SC~D4a>IKPURJB^WiacK;ivlQWhE&oIGd#eYYI#KZ*AghFgxMsr9G|GNBWC{alg zu&PJ!v`2P?+3^nz0sATo-|9YP@h|J*1#65fFmhtH3S%u)v#sJheTm%`CEiZQ)%<8J zX7qhM7WAeQ5RD(7*%34yPk9)q#VOf5E-M!ZM>)MYj7zD^B>2Q4v8u{=pf7O*&#Y-> zk&9|)^BS9l5c6r-OH?a{FUL3}z2yRU;+=}ti3Br`YguAp^M`B#5&wt{<4(!QG1l79WlV6eBT~wv=8(SuvZ8hF z@0VlGXNm_3pOqD0aPKv7OW_5Mz$^!xf%IQ?{cDHU$O&L4s%+ zf)chFo{u2?(Nr(-+li;F-3Xeo`YAun+#c^H4xKF-&f`}aSyQ*he3Ar?!Anvg26ipS z13KW2U4-XC*zAtg$uV=&MI0m76T@k1W!1rZne|20fWI9mn%m!#Q?%5yw?eRmwKQQ# z#YhPOe^O%TphKL_?k?_x&5os=p}XgtrrUB6DL4a}^Ps{-IPlZUR0_Dv!p69BNL}s# ztElm!2`;fYWEr9m33l2AU8z_SY%sEmBdV7Ycpj}z56T<-y^m$mMe^evj7pAZ0;;|f zz!S{ey0>ga=owv4t;><&u`;H5#cYNJN9FVcl{N@qn%`ha<<#rnoMXBSND!`hRl&Z5`8@ihh8YSGt7^p76nMtCO9Tg0^Zju|0S=10 z&`LaWFY>g2%FUSHP0*>$2b0q3Z_KN`MBG!lN;9>)*_EKq;JIG{K!XBPLnVJZTQYYt zTsUYgoP}PR8=hG{(g0}AAW1C4Gk;_fV!Ddkc`SYm-t9V4;4?kY-G#?KlEcA(d7%An zeQ+#P@Cau=p)fK{&`B?z>J|ToBRVLCP-+;~76T+lCSt?{%rNTnlujflTBxO(Zqop% z&2g=)ie{mOMAHr^(*036IWo?KlXQU*p(k4enr|8b!GdA{!J6GY6(M{&Mn^e777-PX zkX8P1aj5e4<|JzkS2!sY{nISeWCPi~tJfgqSC%ibY^G!wQK#sVAaR*e5FD}n5yJSQ z6L{txYdqG1?!Fa{nb^w_(puI)N;%rmP@CAw>Nmkb5R(whf-`bKp$0y;5`7Mx59ILD z$2dezz!H)znUZ1a)G88(8b%sSD0G2~B2T0Q&ZiU5@^Tw=XeJTD$2ts8(<#R=oYGd7 zMiJfwSK2H@M#)?Pd(1#ZLjkG|R9t`|Qk+yMq(`jO2R!re;mYA$$g*JAlM?Ld=-WYT zSPQi(m2zrWn~v#!k_ceS5&e-^U@$Dmp}@;W(8I9cdYsH8_}OVg(CDs#nybbz{(D#( z^f9m|A4{fGU=_NIBrTNbcZ_i}BZfW1F?*w7ZC?ySU!n@tgYqPuw=@;MX~%qi=wd2d z!~%VRq5{}zFxCRE+$@e^N~xKEX9O4gM7TQm1N9KP{5mWK4oU(5W)W#gI0>63Ey)q7H9VifkuS_WlMi@SAIYC+`bM$W$ z9G~Xx=70QY*B7LV8=FG{c%uUirYIrQ>P_hGcX7m@Vxw}8HiMhJ9g|A=NK=txe9B`P z8m8ciEc~a_TB!3RiGk`EDH+DH5uiJPacM}22<;3rKEQD&gY+^cNzilYAbxziq0XY$ zFm?XH6V)y^@A};;%pb-seiwrXKDd;&NEFslK}ftXa~nvbeA1V&$Mj3RF{dO?l_TTt z=&<0BGQeN)k$ppW=DX$)a37!++=Y^+vTyxSS~NQ7A4t-WQ?}lM==ghrzO3BP9+?t$ z3N=osBA=q$zErBBk{>HFnlt|#CW<31h@sID-ds@D$C@*$d4bv zN_c^-zaBwwThsSl{xq41n1ju*_L4DfE|c(^rox7WOtvW(r5oeo=qS9dq?(sN=1~Z+ z=&d!_I+JM*Wf`qS9o7nF=@D@49U%$NBCNisNW6li{5UsEub90*3g3HV8~xg~v{s0WLk@i0)^6QXqn7-Lo=Q$`?T8 ze+$W#;fn9@h2QA1uA2a)ilH-+}4?$nawNc zIt8ri&}<%jy8*^7wKf3#LN3{td=^m;VQLVT^v28$0z$*C-H?PZd*CXn?rP({1H5acX2kvybS@*XgVk(t zJvBCB*h$Usv2=#i2PiOr%|V!4SPiFY`W6gD@G0Z~SX+QbMCuH;Bf7>1-(Iv}f!Vxd z(cWVsS`eohQ_0{QSkomVbxv4wGCEpBB%wp3K>Q#LuwvAERpfetr7@)63@|eb`rSC4bo*&Xpyue)#Wknt z`^4dMo3zv_o<_3(E$<`91a-LLNw;d)Mof7U z+*#ho7}wVv@*GPt7}l0y{8+UI`Lz2{8Q6z|2UAQG7`!^FX$(BMeE}QhK)99N&7_%d zDC27@fr1rsMAiJ6wsVcKic`KhOaz zY*-N1HPH;J>!0S3i)L=I3dHB-Ch!Pm$V+rd&$+@x5hb;KsqUD5(PJjKjz94v7yx62rp+|zqN^g2^?w^53k7gP#RP2gkbV*N7I3xg4~{9&Nx z2r?CbJ^-)dO=+c}A{q~*X64RRq!gw_3DmYL9MJ>W-Fb*9fn|7G%BrMr$$VH4UQyQT zAbLHrltUS9Wf93K&q)%b$K9SChHJ?V1h?CJi4-K`^=s!WOQ0mgs8upTaeD+Y9l&QFWqD=22I!Ezl#(GnxY z$9-sJ+CJAaOu10hK8s}cVI;U^oTtuMPQFA0SWfT&+JN#bWBZI0&vqD;#z$UQivx0(oi9)(>k!Unam0VWem@&q>> z37+|?KXfV1bKH3hh4@yWn-3t&B_Yf)^hl(|-7&HNkmh%Dr;?9iIA2B@GJt~sV_Lac zlHBA!ikKC2Q=#S|XCMUHeHT$tm+_iT1AF^%_b$ZC>R%vLbp_LJ4#%u5pmem!zYY+- z9WI?BgUn3RC2iE}{ zwl`>>NU3QKfuB%uju4)2QB0$QXagm%hxo{TdVrYd(*cF6`XM|Dyu>EUQz+WKjkXl9@>3}^ z>foQkfwx8w^OTkB406-ugkj3_F$jiKQ#U9}uE<$9fhP|AhRa8hh06QJt?jIW_=;~VOQh*Cv%0UHIL7{Br?GdW=j{C;To1NXI%VHiU(FJSeX@pA+^ ztZ*l7&^Ay^ok9Ip9|gVaZiTx#ClrYj+1=IwzfWfps*Q11v;JTY?1!nN?x4WfFT@eG zZ7Tn|Ht+7)REjSuZT7?3et1a28hwOWPy%+L8hwcaI-#PlqB-O@pqj{XG5HJI4Z-pG z0BWQhm2+(pO5#X#c2IL6k`O1_g>?14Rqzh?a{YCA5dd$XHrz}=9%uPZ5B%Xs&`|}F ze+w?N;4S}_AWqNFZI8a?#zSEI7efzBkXA>PLKG^5BAjt%FX}MzP_h4-6>Wz&pA+K3 zG>mL99Y`%Kby+4soGJBj%}KhuAiSao(8E+2R{K9o_5#_ngR!=d?s0lp`&cHS7De~S zILZcZ$XQo_+K#`Upw*s&rU7brWs*{+?55?~4e#q{1D7CN3ksg0$T6B72!5#YgSU`R z&xO#-ohm$0Hiwph?7 zWb3Kj=i3`V&XZEgslkHFo}6r2dkWK^qR9J3eH(~wH_07{KAR(2fhEBc_}SA1cndth zQUYLR_tH$XgXj1{)g_O;>iddD%P-YVjTVcWB`|LBGGnt2eTNAIq#0&_3!E+4WRP;I zl&)e;2a&4)T^2QD#PEfWZD4slR&|4H#QQF_T6U*~%83&X)d}67G$j0wP|<0FwV+Y7 zsgxUGfN+*=r~TLC?&9~FP$>XuBT@B)+Ty#MwvL}TMs@mjKR5Ql@D1XmrWjcYy(yn= z@}Z#|dAk4FY1hFSIppj?K%F}!fXD80hr=q-bKZ3(%XG8Qpg#969;*)#Q9f3;MWR=D zc;dY9xM+mX_K2W| zV2jgoZ85LTmH?#@9vxslZw`5YX958f83p-iy}K$H5=Q|-(3XoFyQeH$$sqUD!r9EK z34EfCa>jiovIWAo>UMw~NLn&=>y`~wp&&4U5d2r*>=VUvVjN|6k)4cBdr(sHP2evh zkatilK|+iq9^~KM>oL9!fH=6wda=QB0q1Z{aOJKbw1Vvu@J!%lsmRkzCrPkp^ZJ^F zkfZ{sfY9iI__f-D@k{evqeb6>RMQef!`cmPP+deF>a_2-(rMAiF^718ZF=*KGkfF< zeTn@~Btu|tiC9Cn+UQcRPN%9ai$3LRL@O2KA+O!dkYzewn zpB3|@o6i$$6&S|ALi1Q)$o$8-Q?Ye|cA128goO3NME@8jt0yX5;pT+NfYw&X{ecej#$z7~p>`Zb zhV$J5dTtTgS`A-if-qWtmCq6RM7wFT411NZowjge*`;9~D<~&96&q$*0*!2B0rScE z9faejkf)fAh~w>{R74&-mY*$JB}`M922XcIX9EUk5CV)GxIa5>Su?i|T1zQ+)d$pz z0uIN3$kdf#8@~L|pw!J_2+I48Vo5OUwhImZ7^}(qpxkc$*2=P(ydw;?UtmkIFH3CH zG04OXnPH>a7+KDg%mKy^ThHN&ZQdNw4MoUz1vG#Pgf8r|t-_;nd<@vj(chdCP|FK0 z47v&ccA#9~c5|AAZ1@?a7wCR)bt(nJ22$ZnAas8?_eYXfX;giHv%UNn?hC$V`!Hg2 zK7Eomj;IgFjJAAW-vj?d%MEWJ3J3{@=310f*Q}@okO<@%u2evIvv3P*aiw=5hB_oYc$zbaf3C(U&dP@C1g9hDz;v5%$FJF;qFAtp(^$!@T0L z7-rxsLQ1BiVz?h<7jUf$N|KGb6V;lEAzcQwu5PF*Uce4jwy z=oWOA7mZM-5zJZ;N{1Prl!%Y6BJMthC4ppsZd*aH8LdYP!zo|EH)Q~TTnVQ!vaM7w zv=&=gjXBBpT3H>`+u?f1h_i>O=LFO@bN9DK>eO~4oP#JjSm>j_p_(Ql3&P+;3U&|Z zQsxuSo|Z#MT*3YQAr5L4w~91@qxQlC0e}5~?{#QC265aLL|h40f5sA#LcJox{w)Y7 z_b4a%X_o=`|CxkmJsRhx;iTaf_>gxQxSv7FE|>?Y3I_6e1iHtNjbMGFGYEX|C5;lc z-GjybE}n z`>8)_FAs&8K|;nj8Y1 zx16c+XUM;xf&u&<$qrzIT5GSR%&tbuv?nCxAc1-cT5Zr&4^>7jd01|^UdpL_x=JZx zC*Wu2X0@`ck=xQaU|{{ieaEQW#@#aP5-@t+wcNszY^UMzVC#VU8ef9)YM-V#eV=RA zRBxO5y5q;6seSKV0UYOAxl74$w3WY7p-m0+8QPVI32DQq&G?And!nlnDtS{eQr26| z=uf?>she9>onD1C7#+C~<^L8qmJFM?43hLVh#qJmB3HMbOJSnnm|%`56hCOFL?h+| za8v*WIsB=H%A|G zOmG`R4goJ&1@S39GjFDn#@;>g7qAo!&E24|lmet%65975)F+Q?nc^D0lLp&im^XzDPpL)m7sgM%6FQo zWIA9Phi|U{kpYvncBH|fB1gu7pbtjs`SjHy45ZB3#Eo&nWMMq>DFQGq@?8J}M_>d8 z5pEEf$ygG&YcV=2wqB6Gut&0y9t5RF8TAg^t5Ne3G<(?N?yLf2<$zTPl+J)D^}73V zaCT&Ai1zj-7#%(k?9;K#sn-OT4bvj%Bamn>+5LMO-}Y=2Sg9u2xLI4>bp#&;6%+Us zKjBilROdIi0gnAC&Tl!Q(AH!lV6i@jxEOdtA-_1Hg`y!k$$naDUzgEx%l|P-$$G&B zISk(_V5x#>)ew~l4)LD|$*`Lqh^r250j~bkM$eEqtX&`I-X>fXY>XHy zL;~Xbe=zDY6j7w(YhXYs4?+Pvo$i+Fkch)UFnXacaYZ6;UET^Yr!y#bE&rQ8gvmaD zmYB!gD?33>7QXi|+G0a@e|-ACi{`KTfR>s|#sBiT57g_(J~zFGsvfk<_|S+qqe~{~ z;GwCQ0s;fve~zfKXvYdf*w#Z)mf!+B0MnV{j}1cC2le>cKq({AZ3u0mTBIPaqm~duJP7xl;rccStY8}=V z4|F$`Fvg|J;~Afj$MZFpC5x;|K#vYEh>-AR*1tq8w`6SHNjp4~f3zVwwoO6qrYnJq zen#!z7Lazd+&KOU$tmxOkwX*<4Y0hU!`kB(Ip@uOCkU1I!s<3cv&;wtI)FF`03py$ z?Q0sZLuYm?EWbjy(n9$uE1f%!ik-7U&Dj!|jBO5YWqHZ-O=k$&ep>WuDOxc4sO)p# z7vPpb*a4*aqSWR@wX(JqZ7QQr{()2I#i3LpZA0e6)D6TY$h6#7vxZ^H$)pnV#8;uXn~8{yybIsGF#AnEt-zpho?cgLgw zRI=4B=Oeci{MnmZ+Z*^UHB|A>2=D-dnyGD;^t(Rcv=O&m0`UL>d&7g{CtIIXgwT!d zKs14uV1iqPxFR>+pVo(&Qj zyiKSn>Huoh?=9$(9urU3t(1iFDWF1s2>ynh_8AoApcMFMp8Vs#MOi$bvVE5LvE>Ka z5e)14U4K0!8Ft|na)LlbpFoTXS~Ae(-6FmlnpYhlnV=u~%#!v;<({IuYq|bOQd zhMOrb-fn}TJZrSDZeY)uFIrjEw?W`od%*Pv(Sm~nD%-6seIIsyMd*%tw`huw^c%6K z^l|6UFh=vuWmx+)Ea-xUWMf6LgQMzpB!W=kLje3elTc)WhWQV)nV+Wj8bN1vxR@J5 zYq<~ICeXP!Mst00$h} zt_$^Ty(ZEaJ|oYFvM2nD#}?96fbv|Kc9i9aP7-0n$Odi?PF8i3m{aG4=bN(kFwQ^e z{zxr)+=>h#BHwXi>)-(ia&XAg5BP%~s_Vs^SkAU>oJ)VEYyfN??PBcznMv4k8lJch zw>*ADsE7H5nh0t;>PWzFi3J)$7DNNHP{8=730mY7ZL&ZaT{UDnIRzLGheC(WmPc|z za*&qV4QA=bA-FW?x$!AfmCOv(dlE*r6g7;u7oDj52L2js$b-+v2T z<@d;Rbx(O~n8__y*$usn(yI4|h?>TwY_`9ohPQ_9mAvN@Ql@WA7WCAzs zHK(|lg7^nR;rUdrf(w6l=U3;gQw5_Fr)S*8fiV_qbd)fP^Kk@zbPNhTlr)HJ5O2s~ zAt5e&?N@1oYs$qA6nJ9bKnWHSUcg%r0?#O*RwFqDhRVRQf>|>T8t?&fPaUq%ZUAG4 z*hFqx4vz4C_}OEX=03Y3hXJ;4{Cg@OMk>MsIB_?AwXk+26v0qON5c<}XatBSRHA#} z4(eoDPZgi5k9>3b-XF)`VNlSj1ZF;Lc*u?`tR#W<-J0AS^3g8Or0l1_&~{kis!N1~ zDHwP&b*qDMr7pmZ2$-6C7Jw{H@IX9as^>p1Ztt>oD3=v(+sRgJs!1)jDL?;4rmpvpcP6F%d{~8D$h#$$;-}%wzx-Wq7EgLF!05TkY|U+a3FFF!J(kVLGf|u{Vx=4z{tZiVTK3o z>#6IFpd%pkkOpcJEHHk3pbl%Rn7S2Y{V7%7U?aSv9fkwgdMX4bv=r=CjkU5~8RM>? zbYY=kT_2qrLSF6ChBKXLj1qPJnr-3JXAA zP$!BohNTWvjSfG;!rajc&d_GE(tV^%8nIzU2VgFdoT3W73Rl8TX!}PXHMx=w2nW$K zBnxL125qtH43CHdZ6BN?4s7K^r~zto9QtpG7x_*EYMa{kDVQSKpooJ&La!7FU5Dj9 zg^_z-?Hf-z(QH0YesG=5=rRls-v2l-mL*Uw!R+*Jg-LOQew(5oJcP!Umw@vfW@4`P zen_iQ-T&856%I0|;P7FL7Ye;JG9fc8|b_>{Cj-p zU;1nOYWLp-81cuT;0K!8p$0@*(VKWVG7G>O;2$Q^*3N(*4m%)kaBt$lhLQ`3N@&40 z7}-k%`GxAgVb_Y!}?6qPXwfByIL_KB>aPI|+SRy&7!Waqb8`oO=M z20tI~QI`r-a6YsJO=FVkMjD{{HfIt%#t^iy}!@ zwGm2fCk=^8-ik5_Nwgs$l0(WV(uR&ik;;r8$}y5tQ|(aEL1{+{RVwKG)lzQ*y ztA5w}FT8tQdtH0&HDz_x$kFeTp_$U14+xx>Sh1rVX2^^7JK;<;P??r=4tgb zmdR?#yVU=Rd+b>R@6?1wlmNt$I)MQ|1w_-el3I-d+8Y<~%hHBjmtq32QBVR?2@Dh= z-u>VSNzZL}mHULH@C1bf~IfN~Ps{1rJsxB8cW|wt9|PurE{x_Ci0-hjwH8D*ZVxla=8q*k8@Wiu^iAnLz24L%?IMkNJ$R@LMu&=eUlTM|I5-es36g;8ZeYxm zd@Ea7Qfu9685afNQhzJ$f3DuX^~4)O=BwZNx4-bQl2Cwy6>Mf0Lm1utK~D#eyMO0E zW)a#<6yBgDr2$etnl={KvN2{|mpX^Q@Pd}mN9(uuVq@o5tWs#tbsX(_acl6JPte_I zye7|F+Ea-|lVQ!xt-QN(hPqQv)pHmQ4#S01TfSkea=#2g-K1<2i}1%(3N9PM4wSl@RMutv;UZ403@XL+*sy&eVsFEUpCc|ns;Z4qKE|aW;=Ae z7!^CR2=%EKyE~?}4LzW*>cwgs$Pb(j^Y>09`O6r!K-9D^PlZDh*plKL&10TG9!@b`KU! ze8cw12rRegvk)Z)Om^^q8~PIJTgUR#ajLAVe1FtYy@&|9_9_vQ*E_Fqa2G`!x)q1E z5nCPyn<*|8a;6X6kudI0lOG*z`#zc=xBe`sb5QOJwc>qM0~%e=Wpw?=mMINTz3D`qxfO!g z!Q`@KG?dW}K($qcS?i`<&a%I~bkie0hMzZ(-X=#)kgUmtwo69=4^>YcL z{$5>-F_Z3YFxv%#OjrY zSuCokF&nM*#z{_JLS(p(&oOzb>qG8-X$vnir+mjwGtK`2%-xt3LNhLaI?w_xS5X>w zAJ?Tu(WJVd<)!c4Uw$C?<^0xx`5vsW&n5hQ5Fu}b{`-dNalP|qvn&DNM(Nh`d* zo%oc~arO3_Uwdve;VG}Sf*Oa^v+P61{fpGL;#`Aqu2u-dYD_4%ZnkLaB6-1jc{o1k z=a^4w3zyu^JYYX7zhh(43ZD&^H*M=v{=0dhs#ijgA0jk{gBYW8sr*6A!^A$d-{8|d zvyuJshakC?+;p4@oM+VM1`q&V0W{-)J<`s;822#ptmoHn?Waw!SnMHaz8!6?J!)4L zyrsWOFgK4}2TVnvaDiN>-4IA2@@b%uoyk0FalVrI<(4Jk2sE1<@rJR2X{=MvhwNuP zJ6AeVFS8u?PN@%N_fA&55rk&d1lOOm46%G?@F`ttq+XuZI{pjK%w4ml^_*+R!?uKm z%a1Am>?6E06g||K6tX>9RT~AYvQCRNoEVd7$xD0cO8;qb{MIY;M}ReuaH3Y>Qylv! zH=~9JBC8htEMl*Fd)3JSmWYTtlpH4)25Tfr1msIR>00i>U$6;CZ!uzd34tNqnZXec z?jr~GdYO*sT@4l}J9s&_@`qKT4|3!?mSbS7ZIlhc940+9omMuaN5|RLeBIetG)>E5 z(b^O3XQ~$_?qbFI7I|Xd!v~0vQ0zP+D({$Z$fN)Khs@wWsI!r5O0nbvJ5B_GN*Cif zE>Ta={hq=VVj=_T892wpX}6;EwW@QKrbK`u9eJ+g$&BSUiJXRxn&G^rx5&P%MfQUf zD!@PY3J-)p+5Ezr&%Bf59}PLp>RQtjbB7ZGIr}b z&4~?9yiI&trTJi_j+KijB5}d>TR#ouj~o!gQqpSS#Y1*g@jF>xF!v2_VW*w{Y(nJC zn7v=RXG>)bzY?q{|GN6`?~+09hCVH}w)Ih-iw+Rvh!B4#)~Y!Q)q~bny6sO7H?F!N z;L=QIM&)4)f-ePNTpBEWpEO2`istorgdqs|hf4HaHS~V5 zFjxDwlq@ApPded^5$>yTHaSpp!3g)@#UuR-avXGrZFUe!wKc@j zO#1#hA54Z_Sc1xpm(40Vtt}$Ha@A^X8f#L+-wyJ!AcLU~bs3L|FeGFzB^W8L4?Yt) zCMAeA)S%g*GYvO@L|bcj7y-bH_MHtecT0s%<5fi^aJj~M`MH14(9%7vKlNhq>raA# zz$qerEA=G~9Xzg?(n@GHNT~?t_+m|eGep)}sk**aKwDpq$}$iK#YyMeha zn`^6E+qPfi9_ED#F_YE@_s4#fvof&LZ}cA2B**~h8u!y_t%I+1n{^!orwrLQdeErux%27Q1`A7Z zN%s_wJiA`AYnRX0P5&rqq3f^K}bTU3U`sTy2A++yMRJ0$Skc#~e@3OmA)x=gf0Y zEr>4?2W8%6%}mrm3yeqh;&0s}egArhqE5o35({Hz)~fcBZTjJ?1A_v~s)S47Ct}O1 z%F)2#+RgI`1KLTT|Mun~C~QiWk379PaKa-t^rDNr4ZEf7U|7eL#09U$voE)7TdWCC zrCCa|ab?dFsCpS&r#y6;5fl8lHxm=oqfq9L~ z6JMuyUa@0WMf>afFi7yHj;3bJPUbw3E3|wTVxVQ8?idQCAPq(kY zUub@C+d#&<&YW=54QZuvfHljVlm^PZ50C81^4+1UJ%9^yDcD;8Q;Pn9!yre^h+6a( zcx7tRhT1FiAh7|`?-s|;ZfrXA#xN(qU+|tHMngdJo}ansFm-fjH^≺_{`m-s4Kr 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 J=9@Sj{$DOO$Aka? diff --git a/resources/rocket64.png b/resources/rocket64.png new file mode 100644 index 0000000000000000000000000000000000000000..a97b07c9466c51ae36d304886389b332ea3e1b4b GIT binary patch literal 3316 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 mz8H73kFSHf*Z&n!P=qNe{+pRPv|FY!fWD3~yj& d) : de(d) {} + RK4Solver(std::function d) : de(d) {} virtual ~RK4Solver() {} - virtual double step(double curVal, double t, double dt) override; + void setTimeStep(double inTs) override { dt = inTs; } + double step(double curVal, double t) const override; private: std::function de; - double k1, k2, k3, k4; + //double k1, k2, k3, k4; + + double dt; }; diff --git a/sim/StateData.cpp b/sim/StateData.cpp new file mode 100644 index 0000000..2af9702 --- /dev/null +++ b/sim/StateData.cpp @@ -0,0 +1,6 @@ +#include "StateData.h" + +StateData::StateData() +{ + +} diff --git a/sim/StateData.h b/sim/StateData.h new file mode 100644 index 0000000..4af6ac2 --- /dev/null +++ b/sim/StateData.h @@ -0,0 +1,29 @@ +#ifndef STATEDATA_H +#define STATEDATA_H + +#include "utils/math/Vector3.h" + +/** + * @brief The StateData class holds physical state data. Things such as position, velocity, + * and acceleration of the center of mass, as well as orientation and orientation + * change rates. + */ +class StateData +{ +public: + StateData(); + +private: + math::Vector3 position; + math::Vector3 velocity; + math::Vector3 acceleration; + + math::Vector3 orientation; // roll, pitch, yaw + math::Vector3 orientationVelocity; // roll-rate, pitch-rate, yaw-rate + // Necessary? + //math::Vector3 orientationAccel; + + +}; + +#endif // STATEDATA_H diff --git a/utils/Logger.h b/utils/Logger.h index 95b9d1e..d716df7 100644 --- a/utils/Logger.h +++ b/utils/Logger.h @@ -24,6 +24,7 @@ public: }; static Logger* getInstance(); + ~Logger(); Logger(Logger const&) = delete; Logger(Logger&&) = delete; @@ -54,7 +55,6 @@ private: static Logger* instance; std::mutex mtx; Logger(); - ~Logger(); }; } // namespace utils diff --git a/utils/ThreadPool.cpp b/utils/ThreadPool.cpp new file mode 100644 index 0000000..6f03add --- /dev/null +++ b/utils/ThreadPool.cpp @@ -0,0 +1,6 @@ +#include "ThreadPool.h" + +ThreadPool::ThreadPool() +{ + +} diff --git a/utils/ThreadPool.h b/utils/ThreadPool.h new file mode 100644 index 0000000..fd75a4e --- /dev/null +++ b/utils/ThreadPool.h @@ -0,0 +1,17 @@ +#ifndef THREADPOOL_H +#define THREADPOOL_H + +#include + +/** + * @brief A basic ThreadPool class + */ +class ThreadPool +{ +public: + ThreadPool(); +private: + std::atomic_bool done; +}; + +#endif // THREADPOOL_H diff --git a/utils/math/Vector3.h b/utils/math/Vector3.h index 5dc7f39..6a42472 100644 --- a/utils/math/Vector3.h +++ b/utils/math/Vector3.h @@ -29,7 +29,7 @@ public: double getX3() { return x3; } -private: +//private: double x1; double x2; double x3;