From 5e3a2492c7bb73daa4e27398daaf490d09980ff3 Mon Sep 17 00:00:00 2001 From: Jon Santmyer Date: Wed, 22 Jun 2022 17:41:59 -0400 Subject: Base system viewer with data loaded from csv files --- src/system.cpp | 517 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 517 insertions(+) create mode 100644 src/system.cpp (limited to 'src/system.cpp') diff --git a/src/system.cpp b/src/system.cpp new file mode 100644 index 0000000..974d0dc --- /dev/null +++ b/src/system.cpp @@ -0,0 +1,517 @@ +#include "system.hpp" +#include "timeman.hpp" +#include "ecs.hpp" +#include "vex.hpp" +#include "units.hpp" +#include "csv.hpp" +#include "keybind.hpp" +#include "game.hpp" +#include +#include + +static double G = 6.6743 * std::pow(10, -11); + +ecs::Entity & +System::addOrbital(const std::string &name, + const std::string &orbitingName, + unsigned long a, + double e, + unit::Mass m, + unsigned r, + double M, + double w) +{ + M *= (std::numbers::pi / 180.0); + w *= (std::numbers::pi / 180.0); + + SystemTreeNode *treeNode = getNode(orbitingName); + ecs::Entity &newOrbital = m_entityMan.newEntity() + .addComponent(ecs::PositionComponent{vex::vec2{0, 0}}) + .addComponent(ecs::MassComponent{m}) + .addComponent(ecs::OrbitalComponent{.origin = treeNode->entityId, .a = (long)a, .e = e, .w = w, .M = M, .T = 0, .v = 0}) + .addComponent(ecs::RenderCircleComponent{r}) + .addComponent(ecs::NameComponent{name}); + treeNode->children.push_back({newOrbital.id, {}}); + return newOrbital; +} + +System::System() +{ + auto sol = m_entityMan.newEntity() + .addComponent(ecs::PositionComponent{vex::vec2{0, 0}}) + .addComponent(ecs::MassComponent{unit::solMass}) + .addComponent(ecs::RenderCircleComponent{695700}) + .addComponent(ecs::NameComponent{"Sol"}); + m_systemTree.entityId = sol.id; + + csv::CSVFile<',', std::string, std::string, double, double, double, double, double, double> planetData("data/sol_planets.csv"); + for(auto &planet : planetData.get()) { + std::string name = std::get<0>(planet); + std::string orbiting = std::get<1>(planet); + double sma = std::get<2>(planet) * unit::AU; + double e = std::get<3>(planet); + unit::Mass m = unit::earthMass * std::get<4>(planet); + double r = std::get<5>(planet) * unit::earthRad; + double M = std::get<6>(planet); + double w = std::get<7>(planet); + addOrbital(name, orbiting, sma, e, m, r, M, w); + } + + csv::CSVFile<',', std::string, std::string, double, double, double, double, double, double> satelliteData("data/sol_satellites.csv"); + for(auto &satellite : satelliteData.get()) { + std::string name = std::get<0>(satellite); + std::string orbiting = std::get<1>(satellite); + double sma = std::get<2>(satellite) * unit::AU; + double e = std::get<3>(satellite); + unit::Mass m = unit::earthMass * std::get<4>(satellite); + double r = std::get<5>(satellite) * unit::earthRad; + double M = std::get<6>(satellite); + double w = std::get<7>(satellite); + addOrbital(name, orbiting, sma, e, m, r, M, w); + } + + csv::CSVFile<',', std::string, double, double, double, double, double, std::string> asteroidData("data/sol_asteroids.csv"); + for(auto &asteroid : asteroidData.get()) { + std::string name = std::get<0>(asteroid); + if(name == "Missing") name = std::get<6>(asteroid); + addOrbital(name, + "Sol", + (unsigned long)(std::get<1>(asteroid) * unit::AU), + std::get<2>(asteroid), + unit::Mass(0), + (unsigned)std::get<3>(asteroid), + std::get<4>(asteroid), + std::get<5>(asteroid)); + } +} + +constexpr static double tau = std::numbers::pi * 2; + +void +System::tickOrbitals(unit::Time time) +{ + for(ecs::Entity &e : m_entityMan.getWith()) { + auto &oc = e.get(); + auto &pc = e.get(); + auto &mc = e.get(); + + ecs::Entity &o = m_entityMan[oc.origin]; + auto &opc = o.get(); + auto &om = o.get(); + + double e2 = oc.e * oc.e; + double td = (double)time(); + if(oc.T == 0) { + double u = G * (om.mass() + mc.mass()); + double am = (double)oc.a * 1000.0; + oc.T = tau * std::sqrt((am * am * am) / u); + } + double n = tau / oc.T; + double M = oc.M + (n * td); + double E = M; + + int its = 0; + while(true) { + double dE = (E - oc.e * std::sin(E) - M) / (1 - oc.e * std::cos(E)); + E -= dE; + its++; + if(std::abs(dE) < 1e-6) break; + } + + double x = std::cos(E) - oc.e; + double y = std::sin(E) * std::sqrt(1 - e2); + + double v = std::atan2(y, x) + oc.w; + oc.v = v; + + double r = std::sqrt(x*x + y*y) * (double)oc.a; + + vex::vec2 polar(r, v); + pc.position = vex::cartesian(polar) + opc.position; + } + +} + +void +System::update() +{ + tickOrbitals(TimeMan::time()); +} + +ecs::Entity & +System::getBody(std::size_t id) { + return m_entityMan[id]; +} + +System::SystemTreeNode * +System::traverseSystemTree(SystemTreeNode &node, const std::string &name) +{ + SystemTreeNode *found = nullptr; + ecs::Entity &entity = getBody(node.entityId); + auto &namecomp = entity.get(); + if(namecomp.name == name) found = &node; + + for(SystemTreeNode &child : node.children) { + if(found != nullptr) break; + found = traverseSystemTree(child, name); + } + return found; +} + +System::SystemTreeNode * +System::getNode(const std::string &name) +{ + SystemTreeNode *treeRes = traverseSystemTree(m_systemTree, name); + if(treeRes == nullptr) + treeRes = &m_systemTree; + return treeRes; +} + +void +SystemView::view(System *system) +{ + m_system = system; + m_focus = &system->m_systemTree; +} + +void +SystemView::keypress(Camera *camera, int key) +{ + WindowContext &context = Game::contexts(); + if(context.getFocusedString() == WINDOW_SYSTEMVIEW_SEARCH_ID) { + m_focusSearch->keypress(key); + if(!Game::paused()) { + Search *fp = m_focusSearch.release(); + delete fp; + } + } + if(context.getFocusedString() != WINDOW_SYSTEMVIEW_ID) return; + if(key == KeyMan::binds[BIND_SYSTEMVIEW_PANUP].code) camera->move(0, camera->getscale()); + if(key == KeyMan::binds[BIND_SYSTEMVIEW_PANDOWN].code) camera->move(0, -(long)camera->getscale()); + if(key == KeyMan::binds[BIND_SYSTEMVIEW_PANRIGHT].code) camera->move(camera->getscale(), 0); + if(key == KeyMan::binds[BIND_SYSTEMVIEW_PANLEFT].code) camera->move(-(long)camera->getscale(), 0); + if(key == KeyMan::binds[BIND_SYSTEMVIEW_INCSCALE].code) camera->setscale(camera->getscale() * 2); + if(key == KeyMan::binds[BIND_SYSTEMVIEW_DECSCALE].code) camera->setscale(camera->getscale() / 2); + if(key == KeyMan::binds[BIND_SYSTEMVIEW_SEARCH].code) { + Game::setState(Game::State::PAUSED_INPUT); + context.focus(WINDOW_SYSTEMVIEW_SEARCH_ID); + context.setWindowHidden(WINDOW_SYSTEMVIEW_SEARCH_ID, false); + m_focusSearch = std::make_unique(this); + } +} + +void +SystemView::update(Camera *camera) +{ + if(Game::paused()) return; + auto &efoc = m_system->m_entityMan[m_focus->entityId]; + auto &efocp = efoc.get(); + if(efocp.position != camera->getorigin()) { + camera->setorigin(efocp.position); + } +} + +void +SystemView::drawOver(Camera *camera) { + auto &efoc = m_system->m_entityMan[m_focus->entityId]; + auto &efocp = efoc.get(); + auto &efocm = efoc.get(); + + WindowContext &context = Game::contexts(); + Window &infoWindow = context[WINDOW_BODYINFO_ID]; + Window &viewWindow = context[WINDOW_SYSTEMVIEW_ID]; + + infoWindow << straw::clear(' '); + infoWindow << straw::move(0, 0) << "Focus: " << efoc.get().name << '\n'; + if(efoc.contains()) { + auto &efoco = efoc.get(); + ecs::Entity &efoc_origin = m_system->m_entityMan[efoco.origin]; + + infoWindow << "Orbiting: " << efoc_origin.get().name << '\n'; + infoWindow << "Distance: " << std::abs((double)(efocp.position - efoc_origin.get().position).magnitude()) << "km\n"; + infoWindow << "Period: " << efoco.T / unit::DAY_SECONDS << " days\n"; + infoWindow << "Angle: " << efoco.v * (180.0 / std::numbers::pi) << '\n'; + infoWindow << "Eccentricity: " << efoco.e << '\n'; + infoWindow << "Mass: " << efocm.mass() << '\n'; + } + vex::vec2 viewdims( + viewWindow.screen()->getwidth(), + viewWindow.screen()->getheight()); + + viewWindow << straw::move(0, 0) << + straw::setcolor(straw::WHITE, straw::BLACK) << + "Press '" << + KeyMan::translateCode(KeyMan::binds[BIND_SYSTEMVIEW_SEARCH].code) << + "' to change focus"; + + double scale = camera->getscale() * (viewdims[0] / 2.0); + if(scale > 1e7) { + viewWindow << straw::move(0, viewdims[1] - 1) << scale / unit::AU << " AU"; + }else{ + viewWindow << straw::move(0, viewdims[1] - 1) << scale << " km"; + } + +} + +void +SystemView::draw(Camera *camera) +{ + if(m_focusSearch != nullptr) m_focusSearch->draw(); + if(!camera->dirty()) return; + auto &efoc = m_system->m_entityMan[m_focus->entityId]; + auto &efocp = efoc.get(); + auto &efocm = efoc.get(); + + if(efoc.contains()) { + auto &oc = efoc.get(); + auto &om = efoc.get(); + ecs::Entity &origin = m_system->m_entityMan[oc.origin]; + auto pc = origin.get(); + + std::vector> points; + float i = 0.0; + while(i < tau){ + double e2 = oc.e * oc.e; + double M = oc.M + i; + double E = M; + + int its = 0; + while(its < 512) { + double dE = (E - oc.e * std::sin(E) - M) / (1 - oc.e * std::cos(E)); + E -= dE; + its++; + if(std::abs(dE) < 1e-6) break; + } + + double x = (std::cos(E) - oc.e); + double y = (std::sin(E) * std::sqrt(1 - e2)); + + double v = std::atan2(y, x) + oc.w; + double r = std::sqrt(x*x + y*y) * (double)oc.a; + + + vex::vec2 polar{r, v}; + vex::vec2 cart = vex::cartesian(polar) + pc.position; + + points.push_back(cart); + i += 0.01; + } + for(unsigned i = 0; i < points.size(); i++) { + if(i == 0) { + camera->batchShape(shapes::line(points[points.size() - 1], points[i]), straw::color(0, 0, 255), '#'); + }else{ + camera->batchShape(shapes::line(points[i-1], points[i]), straw::color(0, 0, 255), '#'); + } + } + } + + for(ecs::Entity &e : m_system->m_entityMan.getWith()) { + auto &pc = e.get(); + auto &cc = e.get(); + unsigned id = e.id; + + long cr = cc.radius; + if(cr < camera->getscale()) cr = camera->getscale(); + shapes::ellipse circle(pc.position, cr, cr); + + straw::color color = id == m_focus->entityId ? straw::color{255, 255, 0} : straw::WHITE; + if(cc.radius < cr) { + camera->batchShape(circle, color, '*'); + }else{ + camera->batchShape(circle, color, '#'); + } + } +} + +ecs::Entity & +SystemView::getBody(int id) const +{ + return m_system->m_entityMan[id]; +} + +int +SystemView::getBodyIdByName(const std::string &name) +{ + for(ecs::Entity &entity : m_system->m_entityMan.all()) { + int id = entity.id; + if(entity.contains()) { + auto &namecomp = entity.get(); + if(namecomp.name == name) return id; + } + } + return -1; +} + +void +SystemView::Search::addNodeToTree(SystemTreeDisplayNode &root, System::SystemTreeNode *node) +{ + m_displayTreeFlat.emplace_back(&root); + for(auto &child : node->children) + { + ecs::NameComponent &namecomp = m_systemView->m_system->m_entityMan[child.entityId].get(); + root.children.push_back( + {&child, + {}, + &root, + (unsigned)m_displayTreeFlat.size(), + m_query.empty(), + m_query.empty() ? false : namecomp.name.find(m_query) == std::string::npos}); + addNodeToTree(root.children.back(), &child); + } +} + +void +SystemView::Search::rebuild() +{ + System::SystemTreeNode *systemRoot = &m_systemView->m_system->m_systemTree; + m_displayTree = {systemRoot, {}, nullptr, 0, false, false}; + m_displayTreeFlat.clear(); + m_selectionIndex = 0; + + addNodeToTree(m_displayTree, systemRoot); + for(unsigned i = 0; auto *node : m_displayTreeFlat) { + if(m_systemView->m_focus == node->node) { + m_selectionIndex = i; + break; + } + i++; + } + SystemTreeDisplayNode *recurse = m_displayTreeFlat[m_selectionIndex]; + while(recurse->parent != nullptr) { + recurse->parent->collapsed = false; + recurse->parent->hidden = false; + recurse = recurse->parent; + } +} + +void +SystemView::Search::drawNode( + SystemTreeDisplayNode &root, + Window &searchWindow, + unsigned indent) +{ + unsigned windowH = searchWindow.screen()->getheight(); + unsigned cursorY = searchWindow.screen()->getcursory(); + if(cursorY == windowH && root.index > m_selectionIndex) return; + + ecs::NameComponent &namecomp = m_systemView->m_system->m_entityMan[root.node->entityId].get(); + + if(root.index == m_selectionIndex) { + searchWindow << straw::setcolor(straw::BLACK, straw::WHITE); + }else searchWindow << straw::setcolor(straw::WHITE, straw::BLACK); + + + if(!root.hidden) { + if(m_query.empty()) searchWindow << std::string((indent * 4), ' '); + if(root.children.size() > 0) + searchWindow << '[' << (root.collapsed ? '+' : '-') << "] "; + searchWindow << namecomp.name << straw::setcolor(straw::WHITE, straw::BLACK) << '\n'; + } + + if(!root.collapsed) { + for(auto &child : root.children) drawNode(child, searchWindow, indent + 1); + } +} + +SystemView::Search::Search(SystemView *systemView) : + m_systemView(systemView), m_selectionIndex(0), m_dirty(true) +{ + rebuild(); +} + +void +SystemView::Search::finish() +{ + WindowContext &context = Game::contexts(); + Game::setState(Game::State::RUNNING); + context.setWindowHidden(WINDOW_SYSTEMVIEW_SEARCH_ID, true); + context.focus(WINDOW_SYSTEMVIEW_ID); +} + +void +SystemView::Search::keypress(int key) +{ + if(key == KeyMan::binds[BIND_G_ESCAPE].code) { + finish(); + }else + if(key == KeyMan::binds[BIND_SYSTEMVIEW_SEARCH_NEXT].code) { + if(m_selectionIndex == m_displayTreeFlat.size() - 1) return; + for(m_selectionIndex++; m_selectionIndex < m_displayTreeFlat.size() - 1; ++m_selectionIndex) { + if(!m_displayTreeFlat[m_selectionIndex]->hidden) break; + } + if(m_displayTreeFlat[m_selectionIndex]->hidden) m_selectionIndex = 0; + SystemTreeDisplayNode *node = m_displayTreeFlat[m_selectionIndex]; + while(node->parent != nullptr) { + if(!node->parent->collapsed) break; + m_selectionIndex += node->parent->children.size(); + node = node->parent; + } + + m_dirty = true; + }else + if(key == KeyMan::binds[BIND_SYSTEMVIEW_SEARCH_PREV].code) { + if(m_selectionIndex == 0) return; + for(m_selectionIndex--; m_selectionIndex > 0; --m_selectionIndex) { + if(!m_displayTreeFlat[m_selectionIndex]->hidden) break; + } + SystemTreeDisplayNode *node = m_displayTreeFlat[m_selectionIndex]; + while(node->parent != nullptr) { + if(!node->parent->collapsed) break; + m_selectionIndex -= node->parent->children.size(); + node = node->parent; + } + + m_dirty = true; + }else + if(key == KeyMan::binds[BIND_SYSTEMVIEW_SEARCH_TOP].code) { + m_selectionIndex = 0; + for(; m_selectionIndex < m_displayTreeFlat.size(); ++m_selectionIndex) { + if(!m_displayTreeFlat[m_selectionIndex]->hidden) break; + } + m_dirty = true; + }else + if(key == KeyMan::binds[BIND_SYSTEMVIEW_SEARCH_BOTTOM].code) { + m_selectionIndex = m_displayTreeFlat.size() - 1; + for(; m_selectionIndex > 0; --m_selectionIndex) { + if(!m_displayTreeFlat[m_selectionIndex]->hidden) break; + } + m_dirty = true; + }else + if(key == KeyMan::binds[BIND_SYSTEMVIEW_SEARCH_COLLAPSE].code) { + m_displayTreeFlat[m_selectionIndex]->collapsed = !m_displayTreeFlat[m_selectionIndex]->collapsed; + m_dirty = true; + }else + if(key == KeyMan::binds[BIND_G_SELECT].code) { + m_systemView->m_focus = m_displayTreeFlat[m_selectionIndex]->node; + finish(); + }else + { + if(key < 0) return; + switch(key) { + case 127: + case '\b': { + if(!m_query.empty()) m_query.pop_back(); + } break; + default: m_query.push_back((char)key); break; + } + rebuild(); + m_dirty = true; + } +} + +void +SystemView::Search::draw() +{ + if(!m_dirty) return; + + WindowContext &windowContext = Game::contexts(); + Window &searchWindow = windowContext[WINDOW_SYSTEMVIEW_SEARCH_ID]; + + searchWindow << straw::clear(' ') << straw::move(0, 0); + searchWindow << "Query: " << m_query << '\n'; + + drawNode(m_displayTree, searchWindow, 0); + + searchWindow << straw::flush(); + m_dirty = false; +} -- cgit v1.2.1