diff options
Diffstat (limited to 'include')
-rw-r--r-- | include/camera.hpp | 96 | ||||
-rw-r--r-- | include/csv.hpp | 115 | ||||
-rw-r--r-- | include/ecs.hpp | 139 | ||||
-rw-r--r-- | include/entitycomponents.hpp | 58 | ||||
-rw-r--r-- | include/game.hpp | 52 | ||||
-rw-r--r-- | include/input.hpp | 71 | ||||
-rw-r--r-- | include/keybind.hpp | 71 | ||||
-rw-r--r-- | include/shape.hpp | 197 | ||||
-rw-r--r-- | include/straw.hpp | 312 | ||||
-rw-r--r-- | include/system.hpp | 82 | ||||
-rw-r--r-- | include/timeman.hpp | 27 | ||||
-rw-r--r-- | include/units.hpp | 166 | ||||
-rw-r--r-- | include/util.hpp | 45 | ||||
-rw-r--r-- | include/vex.hpp | 104 | ||||
-rw-r--r-- | include/window.hpp | 69 |
15 files changed, 1604 insertions, 0 deletions
diff --git a/include/camera.hpp b/include/camera.hpp new file mode 100644 index 0000000..9a8bc00 --- /dev/null +++ b/include/camera.hpp @@ -0,0 +1,96 @@ +#ifndef CAMERA_HPP +#define CAMERA_HPP 1 + +#include "vex.hpp" +#include "shape.hpp" +#include "straw.hpp" + +#include <vector> +#include <concepts> +#include <algorithm> + +class Camera; +struct RenderBatchEntry { + shapes::shapes_variant<long> shape; + straw::color fg, bg; + char c; + + template<class T> + constexpr RenderBatchEntry(T Shape) : shape(Shape), fg(straw::WHITE), bg(straw::BLACK), c('#') {} + template<class T> + constexpr RenderBatchEntry(T Shape, char C) : shape(Shape), fg(straw::WHITE), bg(straw::BLACK), c(C) {} + template<class T> + constexpr RenderBatchEntry(T Shape, straw::color Fg, char C) : shape(Shape), fg(Fg), bg(straw::BLACK), c(C) {} + template<class T> + constexpr RenderBatchEntry(T Shape, straw::color Fg, straw::color Bg, char C) : shape(Shape), fg(Fg), bg(Bg), c(C) {} + + void translate(Camera *camera); + void plot(Camera *camera); +private: + void plotPoint(shapes::point<long> point, Camera *camera); + void plotLine(shapes::line<long> line, Camera *camera); + void plotRectangle(shapes::rectangle<long> rectangle, Camera *camera); + void plotCircle(shapes::circle<long> circle, Camera *camera); + void plotEllipse(shapes::ellipse<long> ellipse, Camera *camera); +}; + +class Camera +{ + friend struct RenderBatchEntry; + + vex::vec2<long> m_position; + vex::vec2<long> m_origin; + shapes::rectangle<long> m_frustum; + + std::vector<RenderBatchEntry> m_shapeBatch; + + long m_scale; + bool m_dirty; + + straw::screen<char> *m_viewport; + + void updateFrustum(); + + template<class T> + void translateShape(T &shape); +public: + Camera(straw::screen<char> *viewport) : + m_position(0, 0), + m_origin(0, 0), + m_frustum(0, 0, 0, 0), + m_scale(16384), + m_dirty(true), + m_viewport(viewport) {} + ~Camera() {} + + constexpr long getscale() const { return m_scale; } + constexpr vex::vec2<long> getpos() const { return m_position; } + constexpr vex::vec2<long> getorigin() const { return m_origin; } + + constexpr bool dirty() const { return m_dirty; } + void markDirty() { m_dirty = true; } + + void zoom(long by) { m_scale = std::max((unsigned)((int)m_scale + by), (unsigned)1); m_dirty = true; } + void setscale(long scale) { m_scale = std::max(scale, (long)1); m_dirty = true; } + + void move(const vex::vec2<long> &pos) { m_position += pos; m_dirty = true; } + void move(long x, long y) { move(vex::vec2<long>(x, y)); } + + void setpos(const vex::vec2<long> &pos) { m_position = pos; m_dirty = true; } + void setpos(long x, long y) { setpos(vex::vec2<long>(x, y)); } + + void setorigin(const vex::vec2<long> &origin) { m_origin = origin; m_dirty = true; } + + template<class T> + void batchShape(T shape) { m_shapeBatch.emplace_back(shape); } + template<class T> + void batchShape(T shape, char c) { m_shapeBatch.emplace_back(shape, c); } + template<class T> + void batchShape(T shape, straw::color fg, char c) { m_shapeBatch.emplace_back(shape, fg, c); } + template<class T> + void batchShape(T shape, straw::color fg, straw::color bg, char c) { m_shapeBatch.emplace_back(shape, fg, bg, c); } + + void draw(); +}; + +#endif diff --git a/include/csv.hpp b/include/csv.hpp new file mode 100644 index 0000000..18e30b0 --- /dev/null +++ b/include/csv.hpp @@ -0,0 +1,115 @@ +#ifndef CSV_HPP +#define CSV_HPP 1 + +#include <tuple> +#include <string> +#include <vector> +#include <sstream> +#include <fstream> +#include <iostream> + +namespace csv { + +template<char T> +struct csv_delim : std::ctype<char> { + csv_delim() : std::ctype<char>(get_table()) {} + static mask const *get_table() { + static mask rc[table_size]; + rc[T] = std::ctype_base::space; + rc['\n'] = std::ctype_base::space; + return &rc[0]; + } +}; + +template<char Seperator, typename... Vals> +class CSVFile { +public: + struct CSVLine { + std::tuple<Vals...> data; + + CSVLine(const std::string &line) { + std::istringstream in(line); + in.imbue(std::locale(std::locale(), new csv_delim<Seperator>)); + read_elements(in, std::make_index_sequence<sizeof...(Vals)>{}); + } + + template<std::size_t... I> + void read_elements(std::istream &in, std::index_sequence<I...>) { + std::initializer_list<bool>{read_element(in, std::get<I>(data))...}; + } + + template<typename T> + bool read_element(std::istream &in, T &value) { + in >> value; return true; + } + }; + struct Reader { + private: + std::vector<CSVLine> m_lines; + public: + Reader() {} + Reader(CSVFile *parent) { + for(std::string line; std::getline(parent->m_rfile, line);) { + m_lines.emplace_back(line); + } + } + std::vector<std::tuple<Vals...>> get() const { + std::vector<std::tuple<Vals...>> r; + for(auto &line : m_lines) r.push_back(line.data); + return r; + } + + }; + struct Writer { + private: + std::vector<std::tuple<Vals...>> m_data; + public: + void put(std::tuple<Vals...> line) { m_data.push_back(line); } + void write(CSVFile *parent) { + for(auto &line : m_data) { + write_line(parent, line, std::make_index_sequence<sizeof...(Vals)>{}); + parent->m_wfile.seekp(-1, std::ios_base::end); + parent->m_wfile << "\n"; + } + } + template<std::size_t... I> + void write_line(CSVFile *parent, std::tuple<Vals...> line, std::index_sequence<I...>) { + std::initializer_list<bool>{write_element(parent, std::get<I>(line))...}; + } + template<typename T> + bool write_element(CSVFile *parent, T &value) { + parent->m_wfile << value << Seperator; return true; + } + }; + + friend struct Reader; + friend struct Writer; + + CSVFile(const std::string &path, bool write = false) { + //std::ios_base::iostate emask = m_file.exceptions() | std::ios::failbit; + if(!write) { + m_rfile.open(path); + m_reader = Reader(this); + }else{ + m_wfile.open(path); + } + } + ~CSVFile() { m_rfile.close(); m_wfile.close(); } + + const std::tuple<Vals...> operator[](std::size_t i) { return m_reader.m_lines[i].data; } + std::size_t size() const { return m_reader.m_lines.size(); } + + std::vector<std::tuple<Vals...>> get() const { return m_reader.get(); } + void put(std::tuple<Vals...> vals) { m_writer.put(vals); } + void write() { m_writer.write(this); } + +protected: + std::ifstream m_rfile; + std::ofstream m_wfile; + Reader m_reader; + Writer m_writer; +}; + +} + +#endif diff --git a/include/ecs.hpp b/include/ecs.hpp new file mode 100644 index 0000000..ab5e2b2 --- /dev/null +++ b/include/ecs.hpp @@ -0,0 +1,139 @@ +#ifndef ECS_HPP +#define ECS_HPP 1 + +#include "entitycomponents.hpp" +#include "util.hpp" +#include <bitset> +#include <typeindex> +#include <queue> +#include <cassert> +#include <ranges> +#include <span> +#include <iostream> + +namespace ecs { + +struct ComponentContainer { + std::vector<component_variant> packed{}; + std::vector<int> sparse{}; + std::queue<unsigned> free{}; + + void resize(size_t n) { sparse.resize(n, -1); } + + template<component_type T> + void insert(unsigned id, const T &c) { + assert(sparse.size() > id); + if(free.empty()) { + sparse[id] = (int)packed.size(); + packed.push_back(c); + }else { + sparse[id] = (int)free.front(); + packed[sparse[id]] = c; + free.pop(); + } + } + + void remove(unsigned id) { + assert(sparse.size() > id); + if(sparse[id] > -1) { + free.push((unsigned)sparse[id]); + } + sparse[id] = -1; + } + + template<component_type T> + T &reduce(unsigned id) { + assert(sparse.size() > id); + assert(sparse[id] > -1); + return std::get<T>(packed[sparse[id]]); + } +}; + +class EntityMan; +struct Entity { + EntityMan *man; + component_sig sig{}; + unsigned id; + + Entity(const Entity &e) = default; + Entity(EntityMan *Man, unsigned Id) : + man(Man), sig(0), id(Id) {} + + + template<component_type T> + constexpr bool contains() { return sig.test(get_index<T, component_variant>()); } + + template<component_type T> + T &get(); + + template<component_type T> + Entity &addComponent(const T &component); +}; + +class EntityMan { + std::array<ComponentContainer, std::variant_size_v<component_variant>> m_components; + std::vector<Entity> m_entities; + std::queue<unsigned> m_free; +public: + Entity &operator[](std::size_t i) { return m_entities[i]; } + + Entity newEntity() { + std::size_t newid = 0; + if(!m_free.empty()) { + newid = m_free.front(); + m_free.pop(); + }else{ + newid = m_entities.size(); + m_entities.emplace_back(this, newid); + for(auto &container : m_components) container.resize(m_entities.size()); + } + return m_entities[newid]; + } + + void deleteEntity(Entity &e) { + for(auto &container : m_components) container.remove(e.id); + e.sig.reset(); + m_free.push(e.id); + } + + template<component_type T> + T &get(const Entity &e) { + constexpr unsigned cid = get_index<T, component_variant>(); + return m_components[cid].reduce<T>(e.id); + } + + template<component_type T> + auto getWith() { + constexpr unsigned cid = get_index<T, component_variant>(); + auto e_hc = [](Entity const e) { return e.sig[cid]; }; + return std::ranges::views::filter(m_entities, e_hc); + } + + std::span<Entity> all() { + return std::span<Entity>(m_entities.begin(), m_entities.end()); + } + + std::size_t size() { return m_entities.size(); } + + template<component_type T> + Entity &addComponent(Entity &entity, const T& component) { + constexpr unsigned cid = get_index<T, component_variant>(); + m_components[cid].insert(entity.id, component); + m_entities[entity.id].sig[cid] = 1; + return entity; + } +}; + +template<component_type T> +T &Entity::get() { + return man->get<T>(*this); +} + +template<component_type T> +Entity &Entity::addComponent(const T& component) { + return man->addComponent(*this, component); +} + +} + +#endif diff --git a/include/entitycomponents.hpp b/include/entitycomponents.hpp new file mode 100644 index 0000000..a263ebf --- /dev/null +++ b/include/entitycomponents.hpp @@ -0,0 +1,58 @@ +#ifndef ENTITY_COMPONENT_HPP +#define ENTITY_COMPONENT_HPP 1 + +#include "vex.hpp" +#include "units.hpp" +#include <bitset> + +namespace ecs { +struct PositionComponent { + vex::vec2<long> position{}; +}; + +struct VelocityComponent { + vex::vec2<long> velocity{}; +}; + +struct MassComponent { + unit::Mass mass; +}; + +struct NameComponent { + std::string name; +}; + +struct OrbitalComponent { + unsigned origin; + long a; + double e; + double w; + double M; + double T; + double v; +}; + +struct RenderCircleComponent { + unsigned radius; +}; + +using component_variant = std::variant< + std::monostate, + PositionComponent, + VelocityComponent, + MassComponent, + NameComponent, + OrbitalComponent, + RenderCircleComponent +>; + +using component_sig = + std::bitset<std::variant_size_v<component_variant>>; + +template<typename T> +concept component_type = + is_variant_v<T, component_variant>; + +} + +#endif diff --git a/include/game.hpp b/include/game.hpp new file mode 100644 index 0000000..9454318 --- /dev/null +++ b/include/game.hpp @@ -0,0 +1,52 @@ +#ifndef GAME_HPP +#define GAME_HPP 1 + +#include "window.hpp" +#include "camera.hpp" +#include "ecs.hpp" +#include "system.hpp" +#include "timeman.hpp" +#include "input.hpp" + +#include <memory> + +#define WINCTX_GAME "Game" + +class Game +{ +public: + enum class State { + STOPPED, RUNNING, RUNNING_INPUT, PAUSED, PAUSED_INPUT + }; + + static void setup(unsigned w, unsigned h); + static void cleanup(); + + static void turn(); + static void setState(State state) { m_state = state; } + static void setContext(const std::string &id) { m_currentContext = id; } + + static bool running() { return m_state != State::STOPPED; } + static bool paused() { return m_state == State::PAUSED || m_state == State::PAUSED_INPUT; } + static bool inputMode() { return m_state == State::RUNNING_INPUT || m_state == State::PAUSED_INPUT; } + + struct WindowContexts { + WindowContext &operator[](const std::string &id) { return Game::m_contexts.at(id); } + WindowContext &operator()() { return Game::m_contexts.at(Game::m_currentContext); } + }; + static WindowContexts contexts; +private: + static std::unordered_map<std::string, WindowContext> m_contexts; + static std::string m_currentContext; + + static std::unique_ptr<Camera> m_camera; + static std::unique_ptr<System> m_system; + static SystemView m_systemView; + + static input::Context m_inputContext; + + static double m_delta; + static State m_state; +}; + +#endif diff --git a/include/input.hpp b/include/input.hpp new file mode 100644 index 0000000..ed5155c --- /dev/null +++ b/include/input.hpp @@ -0,0 +1,71 @@ +#ifndef INPUT_HPP +#define INPUT_HPP 1 + +namespace input +{ + +enum extkeys { + CTRL_RANGE_START = 256, + CTRL_KEY_ARROWUP = 321, + CTRL_KEY_ARROWDOWN, + CTRL_KEY_ARROWRIGHT, + CTRL_KEY_ARROWLEFT, + CTRL_KEY_END = 326, + CTRL_KEY_HOME = 328, + CTRL_KEY_PAGEUP, + CTRL_KEY_PAGEDOWN +}; + +#ifdef __unix__ +#include <termios.h> +#include <unistd.h> + +class Context +{ + struct termios m_termold; + struct termios m_termnow; +public: + Context() { + tcgetattr(STDIN_FILENO, &m_termold); + m_termnow = m_termold; + } + ~Context() { + tcsetattr(STDIN_FILENO, TCSADRAIN, &m_termold); + } + + void echo(bool mode) { + if(mode){ + m_termnow.c_lflag |= ECHO; + }else{ + m_termnow.c_lflag &= ~ECHO; + } + tcsetattr(STDIN_FILENO, TCSANOW, &m_termnow); + } + + void canon(bool mode) { + if(mode) { + m_termnow.c_lflag |= ICANON; + }else{ + m_termnow.c_lflag &= ~ICANON; + } + tcsetattr(STDIN_FILENO, TCSANOW, &m_termnow); + } + + void cbreak(bool mode) { + if(mode){ + m_termnow.c_cc[VMIN] = m_termold.c_cc[VMIN]; + m_termnow.c_cc[VTIME] = m_termold.c_cc[VTIME]; + }else{ + m_termnow.c_cc[VMIN] = 1; + m_termnow.c_cc[VTIME] = 0; + } + tcsetattr(STDIN_FILENO, TCSANOW, &m_termnow); + } +}; + +#endif + +extern int getcode(); +} + +#endif diff --git a/include/keybind.hpp b/include/keybind.hpp new file mode 100644 index 0000000..6541a21 --- /dev/null +++ b/include/keybind.hpp @@ -0,0 +1,71 @@ +#ifndef KEYBIND_HPP +#define KEYBIND_HPP 1 + +#include <string> +#include <vector> +#include <algorithm> +#include <unordered_map> + +#define CTX_GLOBAL "Global" +#define CTX_SYSTEMVIEW "System View" +#define CTX_TIMEMAN "Time Manager" + +#define BIND_G_QUIT "G_Quit" +#define BIND_G_NEXTWIN "G_NextWindow" +#define BIND_G_PREVWIN "G_PrevWindow" +#define BIND_G_EDITBINDS "G_EditBinds" +#define BIND_G_ESCAPE "G_Escape" +#define BIND_G_SELECT "G_Select" + +#define BIND_SYSTEMVIEW_PANUP "Systemview_PanUp" +#define BIND_SYSTEMVIEW_PANDOWN "Systemview_PanDown" +#define BIND_SYSTEMVIEW_PANLEFT "Systemview_PanLeft" +#define BIND_SYSTEMVIEW_PANRIGHT "Systemview_PanRight" +#define BIND_SYSTEMVIEW_DECSCALE "Systemview_DecScale" +#define BIND_SYSTEMVIEW_INCSCALE "Systemview_IncScale" +#define BIND_SYSTEMVIEW_SEARCH "Systemview_Search" + +#define BIND_SYSTEMVIEW_SEARCH_PREV "Systemview_Search_Prev" +#define BIND_SYSTEMVIEW_SEARCH_NEXT "Systemview_Search_Next" +#define BIND_SYSTEMVIEW_SEARCH_TOP "Systemview_Search_Top" +#define BIND_SYSTEMVIEW_SEARCH_BOTTOM "Systemview_Search_Bottom" +#define BIND_SYSTEMVIEW_SEARCH_COLLAPSE "Systemview_Search_Collapse" + +#define BIND_TIMEMAN_STEP "Timeman_Step" +#define BIND_TIMEMAN_INCSTEP "Timeman_IncStep" +#define BIND_TIMEMAN_DECSTEP "Timeman_DecStep" +#define BIND_TIMEMAN_TOGGLEAUTO "Timeman_ToggleAuto" + +class KeyMan { +public: + struct Bind { + int code; + std::string name; + std::string ctx; + std::string desc; + }; + + struct Binds { + std::vector<Bind> operator()() { + std::vector<Bind> bindList; + std::transform( + KeyMan::m_keybinds.begin(), + KeyMan::m_keybinds.end(), + std::back_inserter(bindList), [](auto &pair){return pair.second;}); + return bindList; + } + Bind &operator[](const std::string &name) { return KeyMan::m_keybinds[name]; } + }; + static Binds binds; + + static void registerBind(int def, const std::string &name, const std::string &context, const std::string &desc); + static void loadKeybindsFrom(const std::string &csvPath); + static void writeKeybindsTo(const std::string &csvPath); + static std::string translateCode(int code); + +private: + static std::unordered_map<std::string, Bind> m_keybinds; + static std::unordered_map<std::string, std::string> m_keybindContexts; +}; + +#endif diff --git a/include/shape.hpp b/include/shape.hpp new file mode 100644 index 0000000..0d9db96 --- /dev/null +++ b/include/shape.hpp @@ -0,0 +1,197 @@ +#ifndef SHAPE_HPP +#define SHAPE_HPP 1 + +#include "vex.hpp" +#include <iostream> +#include <variant> + +namespace shapes { + +template<vex::arithmetic T> +struct shape +{ + vex::vec2<T> position; + + constexpr shape(const vex::vec2<T> p) : position(p) {} + constexpr shape(T x, T y) : position(x, y) {} + + virtual void scale(T by) = 0; + virtual void translate(vex::vec2<T> by) { position += by; } +}; + +template<vex::arithmetic T> +struct point : public shape<T> +{ + constexpr point(T x, T y) : shape<T>(vex::vec2<T>(x, y)) {} + constexpr point(const vex::vec2<T> &pos) : shape<T>(pos) {} + + void scale(T by) override { (void)by; } +}; + +template<vex::arithmetic T> +struct line : public shape<T> +{ + vex::vec2<T> end; + constexpr line(T x, T y, T z, T w) : shape<T>(vex::vec2<T>(x, y)), end(z, w){} + constexpr line(const vex::vec2<T> &pos, const vex::vec2<T> End) : shape<T>(pos), end(End) {} + + void translate(vex::vec2<T> by) + { + this->position += by; + end += by; + } + void scale(T by) override { + end = vex::vec2<T>( + (T)std::floor((double)end[0] / (double)by), + (T)std::floor((double)end[1] / (double)by)); + } +}; + +template<vex::arithmetic T> +struct circle : public shape<T> +{ + T radius; + constexpr circle(T x, T y, T r) : shape<T>(vex::vec2<T>(x, y)), radius(r) {} + constexpr circle(const vex::vec2<T> &pos, T r) : shape<T>(pos), radius(r) {} + + void scale(T by) override { radius /= by; } +}; + +template<vex::arithmetic T> +struct ellipse : public shape<T> +{ + T a, b; + constexpr ellipse(T x, T y, T A, T B) : shape<T>(x, y), a(A), b(B) {} + constexpr ellipse(const vex::vec2<T> &pos, T A, T B) : shape<T>(pos), a(A), b(B) {} + + void scale(T by) override {a /= by; b /= by; } +}; + +template<vex::arithmetic T> +struct rectangle : public shape<T> +{ + vex::vec2<T> bounds; + constexpr rectangle(T x, T y, T w, T h) : shape<T>(vex::vec2<T>(x, y)), bounds(w, h) {} + constexpr rectangle(const vex::vec2<T> &pos, const vex::vec2<T> &wh) : shape<T>(pos), bounds(wh) {} + + void scale(T by) override { bounds /= by; } +}; + +template<vex::arithmetic T> +using shapes_variant = std::variant< + point<T>, line<T>, circle<T>, ellipse<T>, rectangle<T> +>; + +template<vex::arithmetic L, vex::arithmetic R> +static bool +intersects(const circle<L> &lhs, const point<R> &rhs) +{ + vex::vec2<L> dist = rhs.position - lhs.position; + //std::cout << dist.magnitude() << ' ' << lhs.radius << ' ' << dist.magnitude() - lhs.radius << std::endl; + return dist.magnitude() < lhs.radius; +} +template<vex::arithmetic L, vex::arithmetic R> +static bool intersects(const point<L> &lhs, const circle<R> &rhs) { return intersects(rhs, lhs); } + +template<vex::arithmetic L, vex::arithmetic R> +static bool +intersects(const ellipse<L> &lhs, const point<R> &rhs) +{ + auto ds = lhs.position - rhs.position; + auto d2 = ds * ds; + L a2 = lhs.a * lhs.a; + L b2 = lhs.b * lhs.b; + + return (((double)d2[0] / a2) + ((double)d2[1] / b2)) <= 1; +} +template<vex::arithmetic L, vex::arithmetic R> +static bool intersects(const point<L> &lhs, const ellipse<R> &rhs) { return intersects(rhs, lhs); } + +template<vex::arithmetic L, vex::arithmetic R> +static bool +intersects(const rectangle<L> &lhs, const point<R> &rhs) +{ + vex::vec2<L> corner = lhs.position + lhs.bounds; + return (rhs.position[0] > lhs.position[0] && rhs.position[0] < corner[0] && + rhs.position[1] > lhs.position[1] && rhs.position[1] < corner[1]); +} +template<vex::arithmetic L, vex::arithmetic R> +static bool intersects(const point<L> &lhs, const rectangle<R> &rhs) { return intersects(rhs, lhs); } + +template<vex::arithmetic L, vex::arithmetic R> +static bool +intersects(const line<L> &lhs, const line<R> &rhs) +{ + double x1 = lhs.position[0], x2 = lhs.end[0], x3 = rhs.position[0], x4 = rhs.end[0]; + double y1 = lhs.position[1], y2 = lhs.end[1], y3 = rhs.position[1], y4 = rhs.end[1]; + double uA = ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1)); + double uB = ((x2-x1)*(y1-y3) - (y2-y1)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1)); + + if(uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1) return true; + return false; +} + +template<vex::arithmetic L, vex::arithmetic R> +static bool +intersects(const rectangle<L> &lhs, const line<R> &rhs) +{ + vex::vec2<L> corner = lhs.position + lhs.bounds; + line<R> l(lhs.position, lhs.position + vex::vec2<R>(0, lhs.bounds[1])); + line<R> r(lhs.position + vex::vec2<R>(lhs.bounds[0], 0), corner); + line<R> d(lhs.position, lhs.position + vex::vec2<R>(lhs.bounds[0], 0)); + line<R> u(lhs.position + vex::vec2<R>(0, lhs.bounds[1]), corner); + + if(intersects(rhs, l) || intersects(rhs, r) || intersects(rhs, d) || intersects(rhs, u)) return true; + return (rhs.position[0] > lhs.position[0] && rhs.position[0] < corner[0] && + rhs.position[1] > lhs.position[1] && rhs.position[1] < corner[1]) && + (rhs.end[0] > lhs.position[0] && rhs.end[0] < corner[0] && + rhs.end[1] > lhs.position[1] && rhs.end[1] < corner[1]); +} +template<vex::arithmetic L, vex::arithmetic R> +static bool intersects(const line<L> &lhs, const rectangle<R> &rhs) { return intersects(rhs, lhs); } + +template<vex::arithmetic L, vex::arithmetic R> +static bool +intersects(const rectangle<L> &lhs, const circle<R> &rhs) +{ + vex::vec2<L> dist{ + rhs.position[0] - std::max(lhs.position[0], std::min(rhs.position[0], lhs.position[0] + lhs.bounds[0])), + rhs.position[1] - std::max(lhs.position[1], std::min(rhs.position[1], lhs.position[1] + lhs.bounds[1])), + }; + return (dist[0] * dist[0]) + (dist[1] * dist[1]) < (rhs.radius * rhs.radius); +} +template<vex::arithmetic L, vex::arithmetic R> +static bool intersects(const circle<L> &lhs, const rectangle<R> &rhs) { return intersects(rhs, lhs); } + +template<vex::arithmetic L, vex::arithmetic R> +static bool +intersects(const rectangle<L> &lhs, const ellipse<R> &rhs) +{ + vex::vec2<L> dist{ + rhs.position[0] - std::max(lhs.position[0], std::min(rhs.position[0], lhs.position[0] + lhs.bounds[0])), + rhs.position[1] - std::max(lhs.position[1], std::min(rhs.position[1], lhs.position[1] + lhs.bounds[1])), + }; + vex::vec2<R> ab2 { + std::max<R>(1, rhs.a * rhs.a), + std::max<R>(1, rhs.b * rhs.b) + }; + return ((dist[0] * dist[0]) / ab2[0]) + ((dist[1] * dist[1]) / ab2[1]) <= 1.0; +} +template<vex::arithmetic L, vex::arithmetic R> +static bool intersects(const ellipse<L> &lhs, const rectangle<R> &rhs) { return intersects(rhs, lhs); } + +template<vex::arithmetic L, vex::arithmetic R> +static bool +intersects(const rectangle<L> &lhs, const rectangle<R> &rhs) +{ + return ( + (lhs.position[0] < rhs.position[0] + rhs.bounds[0]) && + (lhs.position[0] + lhs.bounds[0] > rhs.position[0]) && + (lhs.position[1] + lhs.bounds[1] < rhs.position[1]) && + (lhs.position[1] > rhs.position[1] + rhs.position[1]) + ); +} + +} + +#endif diff --git a/include/straw.hpp b/include/straw.hpp new file mode 100644 index 0000000..c147b06 --- /dev/null +++ b/include/straw.hpp @@ -0,0 +1,312 @@ +#ifndef STRAW_HPP +#define STRAW_HPP 1 + +#include <sstream> +#include <cstdint> +#include <cstddef> +#include <vector> +#include <span> +#include <string> +#include <concepts> +#include <cassert> +#include <iostream> +#include <algorithm> + +namespace straw +{ + +struct color { + std::uint8_t r{}, g{}, b{}; + constexpr color(std::uint8_t R, std::uint8_t G, std::uint8_t B) : r(R), g(G), b(B) {} + constexpr color(std::uint8_t A) : r(A), g(A), b(A) {} + + constexpr uint32_t single() { return (uint32_t)r << 16 | (uint32_t)g << 8 | (uint32_t)b; } + + constexpr bool operator==(const color &o) const = default; +}; + +static constexpr color WHITE{255, 255, 255}; +static constexpr color BLACK{0, 0, 0}; + +struct attribs { + color bg{0, 0, 0}, fg{255, 255, 255}; + bool bold, underline; + constexpr attribs() = default; + constexpr attribs(color Fg) : fg(Fg) {} + constexpr attribs(color Bg, color Fg) : bg(Bg), fg(Fg) {} + constexpr attribs(color Bg, color Fg, bool B, bool U) : bg(Bg), fg(Fg), bold(B), underline(U) {} + + constexpr bool operator==(const attribs &o) const = default; +}; + +template<typename T> +concept char_type = std::integral<T>; + +template<char_type chartype> +struct cell { + chartype chr{}; + attribs attr{}; + + constexpr cell() = default; + constexpr cell(chartype Chr) : chr(Chr) {} + constexpr cell(chartype Chr, color Fg) : chr(Chr), attr(Fg) {} + constexpr cell(chartype Chr, color Bg, color Fg) : chr(Chr), attr(Bg, Fg) {} + constexpr cell(chartype Chr, color Bg, color Fg, bool B, bool U) : chr(Chr), attr(Bg, Fg, B, U) {} + constexpr cell(chartype Chr, attribs Attr) : chr(Chr), attr(Attr) {} + + constexpr bool operator==(const cell &o) const = default; +}; + +const std::string ANSI_ESCAPE = "\E["; +static void ANSI_MOVE(unsigned x, unsigned y) { std::cout << ANSI_ESCAPE << y+1 << ';' << x+1 << 'H' << std::flush; } +static void ANSI_COLOR_FG(color c) { std::cout << ANSI_ESCAPE << "38;2;" << (unsigned)c.r << ';' << (unsigned)c.g << ';' << (unsigned)c.b << 'm' << std::flush; } +static void ANSI_COLOR_BG(color c) { std::cout << ANSI_ESCAPE << "48;2;" << (unsigned)c.r << ';' << (unsigned)c.g << ';' << (unsigned)c.b << 'm' << std::flush; } + +class screen_command_base {}; + +template<char_type chartype> +class screen { +public: + explicit screen(unsigned X, unsigned Y, unsigned W, unsigned H, chartype C, color B, color F) : + m_x(X), m_y(Y), m_width(W), m_height(H), m_front(W * H, cell{C, B, F}), m_back(m_front), + m_cursorAttribs(B,F), m_fillChar(C) + { + redraw(); + } + + explicit screen(unsigned X, unsigned Y, unsigned W, unsigned H, chartype C) : + screen(X, Y, W, H, C, color{0, 0, 0}, color{255, 255, 255}) {} + explicit screen(unsigned X, unsigned Y, unsigned W, unsigned H) : + screen(X, Y, W, H, ' ') {} + + constexpr void setcursorxy(unsigned x, unsigned y) { m_cursorX = x; m_cursorY = y; } + constexpr void setcursorfg(uint8_t r, uint8_t g, uint8_t b) { m_cursorAttribs.fg = color{r, g, b}; } + constexpr void setcursorbg(uint8_t r, uint8_t g, uint8_t b) { m_cursorAttribs.bg = color{r, g, b}; } + constexpr void setcursorfg(color c) { m_cursorAttribs.fg = c; } + constexpr void setcursorbg(color c) { m_cursorAttribs.bg = c; } + constexpr void setcursorbold(bool bold) { m_cursorAttribs.bold = bold; } + constexpr void setcursorunderline(bool underline) { m_cursorAttribs.underline = underline; } + + constexpr unsigned getcursorx() { return m_cursorX; } + constexpr unsigned getcursory() { return m_cursorY; } + constexpr unsigned getx() { return m_x; } + constexpr unsigned gety() { return m_y; } + constexpr unsigned getwidth() { return m_width; } + constexpr unsigned getheight() { return m_height; } + + void clear(const chartype c) { std::fill(m_front.begin(), m_front.end(), cell{c, m_cursorAttribs}); } + + void clearrow(unsigned y, const chartype c) { + assert(y < m_height); + std::fill(m_front.begin() + (y * m_width), + m_front.begin() + (y * m_width) + m_width, + cell{c, m_cursorAttribs}); + } + + void scroll() { + m_cursorY = m_height - 1; + std::copy(m_front.begin() + m_width, m_front.end(), m_front.begin()); + std::fill(m_front.end() - m_width, m_front.end(), cell{m_fillChar, m_cursorAttribs}); + } + + void setc(unsigned x, unsigned y, chartype c) { + assert(x < m_width); + assert(y < m_height); + m_front[x + (y * m_width)] = cell{c, m_cursorAttribs}; + } + + void putc(chartype c){ + if(m_cursorY == m_height) { + scroll(); + } + switch(c) { + case '\n': + m_cursorX = 0; m_cursorY++; + break; + default: + (*this)[m_cursorY][m_cursorX++] = + cell{c, m_cursorAttribs}; + break; + } + if(m_cursorX == m_width) { + m_cursorY++; + m_cursorX = 0; + } + } + + void puts(const std::basic_string<chartype> &s){ + for(const chartype &c : s) this->putc(c); + } + + void redraw() { + attribs cattr = (*this)[0][0].attr; + ANSI_COLOR_BG(cattr.bg); + ANSI_COLOR_FG(cattr.fg); + for(unsigned y = 0; y < m_height; y++) { + ANSI_MOVE(m_x, m_y + y); + for(cell c : (*this)[y]) { + if(cattr != c.attr) { + cattr = c.attr; + ANSI_COLOR_FG(cattr.fg); + ANSI_COLOR_BG(cattr.bg); + } + std::cout << c.chr; + } + std::cout.clear(); + } + m_back = m_front; + } + + void flush() { + attribs currAttr = m_front[0].attr; + ANSI_COLOR_FG(currAttr.fg); + ANSI_COLOR_BG(currAttr.bg); + for(unsigned y = 0; y < m_height; y++) { + auto backspan = std::span<cell<chartype>>( + m_back.begin() + (y * m_width), + m_back.begin() + (y * m_width) + m_width); + auto frontspan = (*this)[y]; + bool change = false; + if(std::equal(backspan.begin(), backspan.end(), frontspan.begin(), frontspan.end())) continue; + for(unsigned x = 0; x < m_width; x++) { + cell c = frontspan[x]; + if(c == backspan[x]) continue; + ANSI_MOVE(x + m_x, y + m_y); + if(c.attr != currAttr) { + ANSI_COLOR_FG(c.attr.fg); + ANSI_COLOR_BG(c.attr.bg); + currAttr = c.attr; + } + change = true; + std::cout << c.chr; + } + if(change) std::cout.clear(); + } + m_back = m_front; + } + + std::span<cell<chartype>> operator[](std::size_t i) { + assert(i < m_width * m_height); + return std::span<cell<chartype>>( + m_front.begin() + (i * m_width), + m_front.begin() + (i * m_width) + m_width); + } + + template<typename T> + requires(!std::is_base_of<screen_command_base, T>::value) + friend screen &operator<<(screen &o, const T &rhs) { + std::basic_stringstream<chartype> ss; + ss << rhs; + o.puts(ss.str()); + return o; + } + +private: + unsigned m_x{}, m_y{}; + unsigned m_width{}, m_height{}; + + std::vector<cell<chartype>> m_front; + std::vector<cell<chartype>> m_back; + + unsigned m_cursorX{}, m_cursorY{}; + attribs m_cursorAttribs; + chartype m_fillChar; +}; + +struct screen_command_flush : public screen_command_base { + explicit screen_command_flush() = default; + template<char_type T> + friend screen<T> &operator<<(screen<T> &o, const screen_command_flush &cmd) { + (void)cmd; + o.flush(); + return o; + } +}; +[[nodiscard]] static screen_command_flush flush() { return screen_command_flush{}; } + +struct screen_command_redraw : public screen_command_base { + explicit screen_command_redraw() = default; + template<char_type T> + friend screen<T> &operator<<(screen<T> &o, const screen_command_redraw &cmd) { + (void)cmd; + o.redraw(); + return o; + } +}; +[[nodiscard]] static screen_command_redraw redraw() { return screen_command_redraw{}; } + +template<char_type chartype> +struct screen_command_clear : public screen_command_base { + chartype c; + explicit screen_command_clear(chartype C) : c(C) {} + friend screen<chartype> &operator<<(screen<chartype> &o, const screen_command_clear &cmd) { + o.clear(cmd.c); + return o; + } +}; +template<char_type chartype> +[[nodiscard]] static screen_command_clear<chartype> clear() { return screen_command_clear{chartype{}}; } +template<char_type chartype> +[[nodiscard]] static screen_command_clear<chartype> clear(chartype c) { return screen_command_clear{c}; } + +struct screen_command_move : public screen_command_base { + unsigned x, y; + explicit screen_command_move(unsigned X, unsigned Y) : x(X), y(Y) {} + template<char_type T> + friend screen<T> &operator<<(screen<T> &o, const screen_command_move &cmd) { + o.setcursorxy(cmd.x, cmd.y); + return o; + } +}; +[[nodiscard]] static screen_command_move move(unsigned x, unsigned y) { return screen_command_move{x, y}; } + +template<char_type chartype> +struct screen_command_plot : public screen_command_base { + chartype c; + unsigned x, y; + explicit screen_command_plot(unsigned X, unsigned Y, chartype C) : c(C), x(X), y(Y) {} + friend screen<chartype> &operator<<(screen<chartype> &o, const screen_command_plot &cmd) { + o.setc(cmd.x, cmd.y, cmd.c); + return o; + } +}; +template<char_type T> +[[nodiscard]] static screen_command_plot<T> plot(unsigned x, unsigned y, T c) { return screen_command_plot{x, y, c}; } + +struct screen_command_recolor : public screen_command_base { + color fg, bg; + bool rfg, rbg; + explicit screen_command_recolor(color Fg, bool side) : fg(Fg), bg(Fg), rfg(side ? true : false), rbg(side ? false : true) {} + explicit screen_command_recolor(color Fg, color Bg) : fg(Fg), bg(Bg), rfg(true), rbg(true) {} + + template<char_type T> + friend screen<T> &operator<<(screen<T> &o, const screen_command_recolor &cmd) { + if(cmd.rbg) o.setcursorbg(cmd.bg); + if(cmd.rfg) o.setcursorfg(cmd.fg); + return o; + } +}; +[[nodiscard]] static screen_command_recolor setfg(uint8_t r, uint8_t g, uint8_t b) { return screen_command_recolor{color{r, g, b}, true}; } +[[nodiscard]] static screen_command_recolor setbg(uint8_t r, uint8_t g, uint8_t b) { return screen_command_recolor{color{r, g, b}, false}; } +[[nodiscard]] static screen_command_recolor setcolor(color fg, color bg) { return screen_command_recolor{fg, bg}; } +[[nodiscard]] static screen_command_recolor resetcolor() { return screen_command_recolor{WHITE, BLACK}; } + +template<char_type chartype> +struct screen_command_fillrow : public screen_command_base { + unsigned row; + chartype c; + + friend screen<chartype> &operator<<(screen<chartype> &o, const screen_command_fillrow &cmd) { + o.clearrow(cmd.row, cmd.c); + return o; + } +}; + +template<char_type T> +[[nodiscard]] static screen_command_fillrow<T> fillrow(unsigned row) { return screen_command_fillrow<T>{.row = row, .c = ' '}; } +template<char_type T> +[[nodiscard]] static screen_command_fillrow<T> fillrow(unsigned row, T c) { return screen_command_fillrow<T>{.row = row, .c = c}; } + +} + +#endif diff --git a/include/system.hpp b/include/system.hpp new file mode 100644 index 0000000..9de10bc --- /dev/null +++ b/include/system.hpp @@ -0,0 +1,82 @@ +#ifndef SYSTEM_HPP +#define SYSTEM_HPP 1 + +#include "ecs.hpp" +#include "camera.hpp" +#include "window.hpp" + +#include <list> + +class System { +private: + friend class SystemView; + struct SystemTreeNode { + unsigned entityId; + std::list<SystemTreeNode> children; + }; + SystemTreeNode m_systemTree; + ecs::EntityMan m_entityMan; + + ecs::Entity &addOrbital(const std::string &name, const std::string &orbitingName, unsigned long a, double e, unit::Mass m, unsigned r, double M, double w); + void tickOrbitals(unit::Time time); + + SystemTreeNode *traverseSystemTree(SystemTreeNode &node, const std::string &name); + SystemTreeNode *getNode(const std::string &name); +public: + System(); + + void update(); + + ecs::Entity &getBody(std::size_t id); +}; + +class SystemView { +private: + System *m_system; + System::SystemTreeNode *m_focus; + + class Search { + private: + struct SystemTreeDisplayNode { + System::SystemTreeNode *node; + std::list<SystemTreeDisplayNode> children; + SystemTreeDisplayNode *parent; + + unsigned index; + bool collapsed; + bool hidden; + }; + SystemView *m_systemView; + SystemTreeDisplayNode m_displayTree; + std::vector<SystemTreeDisplayNode*> m_displayTreeFlat; + + unsigned m_selectionIndex; + std::string m_query; + bool m_dirty; + + void addNodeToTree(SystemTreeDisplayNode &root, System::SystemTreeNode *node); + void drawNode(SystemTreeDisplayNode &root, Window &searchWindow, unsigned indent); + void rebuild(); + public: + Search(SystemView *systemView); + + void finish(); + void keypress(int key); + void draw(); + }; + std::unique_ptr<Search> m_focusSearch; +public: + SystemView(System *system) : m_system(system), m_focusSearch(nullptr) {} + + void keypress(Camera *camera, int key); + void update(Camera *camera); + void draw(Camera *camera); + void drawOver(Camera *camera); + + void view(System *system); + + ecs::Entity &getBody(int id) const; + int getBodyIdByName(const std::string &name); +}; + +#endif diff --git a/include/timeman.hpp b/include/timeman.hpp new file mode 100644 index 0000000..ac5113e --- /dev/null +++ b/include/timeman.hpp @@ -0,0 +1,27 @@ +#ifndef TIME_MANAGER_H +#define TIME_MANAGER_H 1 + +#include "util.hpp" +#include "units.hpp" +#include "straw.hpp" +#include "window.hpp" + +class TimeMan { + static unit::Time m_time; + static unit::Time m_step; + static bool m_auto; + static bool m_changed; +public: + + static void init(); + + static void update(int c); + static void draw(); + + static unit::Time time() { return m_time; } + static bool automatic() { return m_auto; } + static void interrupt() { m_auto = false; } + static bool changed() { return m_changed; } +}; + +#endif diff --git a/include/units.hpp b/include/units.hpp new file mode 100644 index 0000000..5f2fd67 --- /dev/null +++ b/include/units.hpp @@ -0,0 +1,166 @@ +#ifndef UNIT_HPP +#define UNIT_HPP 1 + +#include "util.hpp" +#include <string> + +namespace unit { + +constexpr long MINUTE_SECONDS = 60; +constexpr long HOUR_SECONDS = 3600; +constexpr long DAY_SECONDS = 86400; +constexpr long WEEK_SECONDS = 604800; +constexpr long YEAR_SECONDS = 31556952; +constexpr long CYEAR_SECONDS = 31536000; + +class Mass { + double m_kg; +public: + explicit constexpr Mass(double kg) : m_kg(kg) {} + constexpr ~Mass() = default; + + constexpr double operator()() { return m_kg; } + + constexpr Mass &operator+=(const Mass &rhs) { this->m_kg += rhs.m_kg; return *this; } + constexpr Mass &operator-=(const Mass &rhs) { this->m_kg -= rhs.m_kg; return *this; } + constexpr Mass &operator/=(const Mass &rhs) { this->m_kg *= rhs.m_kg; return *this; } + constexpr Mass &operator*=(const Mass &rhs) { this->m_kg /= rhs.m_kg; return *this; } + + friend Mass operator+(Mass lhs, const Mass &rhs) { lhs += rhs; return lhs; } + friend Mass operator-(Mass lhs, const Mass &rhs) { lhs -= rhs; return lhs; } + friend Mass operator*(Mass lhs, const Mass &rhs) { lhs *= rhs; return lhs; } + friend Mass operator/(Mass lhs, const Mass &rhs) { lhs /= rhs; return lhs; } + + friend inline bool operator<=>(const Mass &lhs, const Mass &rhs) = default; + + constexpr Mass &operator+=(const double &rhs) { this->m_kg += rhs; return *this; } + constexpr Mass &operator-=(const double &rhs) { this->m_kg -= rhs; return *this; } + constexpr Mass &operator*=(const double &rhs) { this->m_kg *= rhs; return *this; } + constexpr Mass &operator/=(const double &rhs) { this->m_kg /= rhs; return *this; } + + friend Mass operator+(Mass lhs, const double &rhs) { lhs += rhs; return lhs; } + friend Mass operator-(Mass lhs, const double &rhs) { lhs -= rhs; return lhs; } + friend Mass operator*(Mass lhs, const double &rhs) { lhs *= rhs; return lhs; } + friend Mass operator/(Mass lhs, const double &rhs) { lhs /= rhs; return lhs; } +}; + +class Time { + long m_seconds; +public: + static constexpr unsigned char month_days[12] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 + }; + static constexpr unsigned char month_days_leap[12] = { + 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 + }; + static const inline std::string month_str[12] = { + "January", "Febuary", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" + }; + + explicit constexpr Time(long seconds) : m_seconds(seconds) {} + + constexpr long operator()() { return m_seconds; } + + constexpr Time &operator+=(const Time &rhs) { this->m_seconds += rhs.m_seconds; return *this; } + constexpr Time &operator-=(const Time &rhs) { this->m_seconds -= rhs.m_seconds; return *this; } + constexpr Time &operator/=(const Time &rhs) { this->m_seconds *= rhs.m_seconds; return *this; } + constexpr Time &operator*=(const Time &rhs) { this->m_seconds /= rhs.m_seconds; return *this; } + + friend Time operator+(Time lhs, const Time &rhs) { lhs += rhs; return lhs; } + friend Time operator-(Time lhs, const Time &rhs) { lhs -= rhs; return lhs; } + friend Time operator*(Time lhs, const Time &rhs) { lhs *= rhs; return lhs; } + friend Time operator/(Time lhs, const Time &rhs) { lhs /= rhs; return lhs; } + + friend inline bool operator<=>(const Time &lhs, const Time &rhs) = default; + + constexpr Time &operator+=(const long &rhs) { this->m_seconds += rhs; return *this; } + constexpr Time &operator-=(const long &rhs) { this->m_seconds -= rhs; return *this; } + constexpr Time &operator*=(const long &rhs) { this->m_seconds *= rhs; return *this; } + constexpr Time &operator/=(const long &rhs) { this->m_seconds /= rhs; return *this; } + + friend Time operator+(Time lhs, const long &rhs) { lhs += rhs; return lhs; } + friend Time operator-(Time lhs, const long &rhs) { lhs -= rhs; return lhs; } + friend Time operator*(Time lhs, const long &rhs) { lhs *= rhs; return lhs; } + friend Time operator/(Time lhs, const long &rhs) { lhs /= rhs; return lhs; } + + constexpr bool leap_year() { return (m_seconds % YEAR_SECONDS) % 4 == 0; } + + constexpr Time current_year() { + return Time((m_seconds % YEAR_SECONDS) + (m_seconds < 0 ? YEAR_SECONDS : 0)); + } + constexpr Time current_month() { + Time year = current_year(); + if(year.m_seconds < 0) { + year.m_seconds = YEAR_SECONDS + year.m_seconds; + } + unsigned month = 0; + const unsigned char *month_days_view = month_days; + if(years() % 4 == 0 && years() % 100 == 0 && years() % 400 != 0) month_days_view = month_days_leap; + for(month; year.days() > month_days_view[month]; month = (month + 1) % 12) year -= month_days_view[month] * DAY_SECONDS; + return year; + } + constexpr Time current_week() { return Time(m_seconds % WEEK_SECONDS); } + constexpr Time current_day() { return Time(m_seconds % DAY_SECONDS); } + constexpr Time current_hour() { return Time(m_seconds % HOUR_SECONDS); } + constexpr Time current_minute() { return Time(m_seconds % MINUTE_SECONDS); } + + constexpr long seconds() { return m_seconds; } + constexpr long minutes() { return m_seconds / MINUTE_SECONDS; } + constexpr long hours() { return m_seconds / HOUR_SECONDS; } + constexpr long days() { return m_seconds / DAY_SECONDS; } + constexpr long weeks() { return m_seconds / WEEK_SECONDS; } + constexpr long months() { + Time copy(*this); + if(copy.m_seconds < 0) { + copy.m_seconds = -copy.m_seconds; + } + unsigned month = 0; + const unsigned char *month_days_view = month_days; + if(years() % 4 == 0) month_days_view = month_days_leap; + for(month; copy.days() > month_days_view[month]; month = (month + 1) % 12) copy -= month_days_view[month] * DAY_SECONDS; + return month; + } + constexpr long years() { return ((m_seconds < 0 ? m_seconds - CYEAR_SECONDS : m_seconds) / CYEAR_SECONDS) + 2000; } + constexpr long real_years() { return (m_seconds < 0 ? m_seconds - YEAR_SECONDS : m_seconds) / YEAR_SECONDS; } + + /* %% = '%' + * %Y = real year + * %C = calendar year + * %S = month string + * %M = month + * %W = week + * %D = day + * %H = hour + * %m = minute + * %s = second + * */ + std::string format(const char *fmt); +}; + +constexpr long Mm = 1000; +constexpr long Gm = cxpow_v<long, 10, 6>; +constexpr long Tm = cxpow_v<long, 10, 9>; +constexpr long Pm = cxpow_v<long, 10, 12>; +constexpr long Em = cxpow_v<long, 10, 15>; +constexpr long Zm = cxpow_v<long, 10, 18>; + +constexpr long AU = 149597871; + +constexpr Mass kg{1}; +constexpr Mass Mg{cxpow_v<double, 10, 3>}; +constexpr Mass Gg{cxpow_v<double, 10, 6>}; +constexpr Mass Tg{cxpow_v<double, 10, 9>}; +constexpr Mass Pg{cxpow_v<double, 10, 12>}; +constexpr Mass Eg{cxpow_v<double, 10, 15>}; +constexpr Mass Zg{cxpow_v<double, 10, 18>}; +constexpr Mass Yg{cxpow_v<double, 10, 21>}; + +constexpr Mass lunarMass{7.342 * cxpow_v<double, 10, 22>}; +constexpr Mass earthMass{5.97237 * cxpow_v<double, 10, 24>}; +constexpr Mass jovMass{1.89813 * cxpow_v<double, 10, 27>}; +constexpr Mass solMass{1.98847 * cxpow_v<double, 10, 30>}; + +constexpr double earthRad = 6371; +} + +#endif diff --git a/include/util.hpp b/include/util.hpp new file mode 100644 index 0000000..3c795d1 --- /dev/null +++ b/include/util.hpp @@ -0,0 +1,45 @@ +#include <type_traits> +#ifndef UTIL_HPP +#define UTIL_HPP 1 + +#include <variant> +#include <cstddef> + +#if UNICODE == 1 +using screenchr = wchar_t; +#else +using screenchr = char; +#endif + +/*Shamelessly stolen from stackoverflow + * https://stackoverflow.com/questions/52303316/get-index-by-type-in-stdvariant + * by Barry*/ +template <typename> struct tag { }; +template <typename T, typename V> +struct get_index; +template <typename T, typename... Ts> +struct get_index<T, std::variant<Ts...>> + : std::integral_constant<size_t, std::variant<tag<Ts>...>(tag<T>()).index()> +{ }; + +template<typename T, typename variant_type> +struct is_variant : std::false_type {}; +template<typename T, typename ... Vs> +struct is_variant<T, std::variant<Vs...>> + : std::disjunction<std::is_same<T, Vs>...> {}; +template<typename T, typename variant_type> +constexpr bool is_variant_v = is_variant<T, variant_type>::value; + +template<typename T, unsigned base, unsigned p> +struct cxpow { static constexpr T value = (T)base * cxpow<T, base, p - 1>::value; }; +template<typename T, unsigned base> +struct cxpow<T, base, 0> { static constexpr T value = 1; }; +template<typename T, unsigned base, unsigned p> +constexpr T cxpow_v = cxpow<T, base, p>::value; + +template<typename T, unsigned base, int p> +struct cxrt { static constexpr T value = (T)base / cxpow<T, base, -p>::value; }; +template<typename T, unsigned base, int p> +constexpr T cxrt_v = cxrt<T, base, p>::value; + +#endif diff --git a/include/vex.hpp b/include/vex.hpp new file mode 100644 index 0000000..0a3bb24 --- /dev/null +++ b/include/vex.hpp @@ -0,0 +1,104 @@ +#ifndef VEX_H +#define VEX_H 1 + +#include <array> +#include <cmath> +#include <cstddef> +#include <ostream> +#include <concepts> + +namespace vex +{ + +template<typename T> +concept arithmetic = std::is_arithmetic<T>::value; + +template<arithmetic T, unsigned D> + requires (D > 1) +struct vec_dimd +{ + std::array<T, D> v; + + explicit vec_dimd() = default; + template<typename ...Args> + explicit vec_dimd(Args&&... args) : v{args...} {} + explicit vec_dimd(T args[D]) : v(args) {} + explicit vec_dimd(T fill) { v.fill(fill); } + + T operator[](std::size_t i) { return v[i]; } + T operator[](std::size_t i) const { return v[i]; } + + vec_dimd<T, D> operator+=(const vec_dimd<T, D> &rhs) { for(size_t i = 0; i < D; i++) v[i] += rhs.v[i]; return *this; } + vec_dimd<T, D> operator-=(const vec_dimd<T, D> &rhs) { for(size_t i = 0; i < D; i++) v[i] -= rhs.v[i]; return *this; } + vec_dimd<T, D> operator*=(const vec_dimd<T, D> &rhs) { for(size_t i = 0; i < D; i++) v[i] *= rhs.v[i]; return *this; } + vec_dimd<T, D> operator/=(const vec_dimd<T, D> &rhs) { for(size_t i = 0; i < D; i++) v[i] /= rhs.v[i]; return *this; } + + vec_dimd<T, D> operator*=(const T &rhs) { for(size_t i = 0; i < D; i++) v[i] *= rhs; return *this; } + vec_dimd<T, D> operator/=(const T &rhs) { for(size_t i = 0; i < D; i++) v[i] /= rhs; return *this; } + + vec_dimd<T, D> operator-() { return vec_dimd<T,D>(0) - *this; } + + auto operator<=>(const vec_dimd<T,D> &rhs) const { return magnitude() <=> rhs.magnitude(); } + bool operator==(const vec_dimd<T,D> &rhs) const { for(unsigned i = 0; i < D; i++) if(v[i] != rhs.v[i]) return false; return true; } + + friend vec_dimd<T, D> operator+(vec_dimd<T, D> lhs, const vec_dimd<T, D> &rhs) { lhs += rhs; return lhs; } + friend vec_dimd<T, D> operator-(vec_dimd<T, D> lhs, const vec_dimd<T, D> &rhs) { lhs -= rhs; return lhs; } + friend vec_dimd<T, D> operator*(vec_dimd<T, D> lhs, const vec_dimd<T, D> &rhs) { lhs *= rhs; return lhs; } + friend vec_dimd<T, D> operator/(vec_dimd<T, D> lhs, const vec_dimd<T, D> &rhs) { lhs /= rhs; return lhs; } + + friend vec_dimd<T, D> operator*(vec_dimd<T, D> lhs, const T &rhs) { lhs *= rhs; return lhs; } + friend vec_dimd<T, D> operator/(vec_dimd<T, D> lhs, const T &rhs) { lhs /= rhs; return lhs; } + + friend std::ostream &operator<<(std::ostream &os, const vec_dimd<T,D> obj) { + os << '{'; + for(std::size_t i = 0; i < D; i++) os << obj.v[i] << ((i < (D - 1)) ? ',' : '}'); + return os; + } + + /*Finds the distance from the origin to the ray cast in D dimension space using components of vec*/ + T sqrMagnitude() const { T t{}; for(size_t i = 0; i < D; i++) t += v[i] * v[i]; return t; } + T magnitude() const { return std::sqrt(sqrMagnitude()); } + + /*Finds the dot product of itself and another vec of same T and D*/ + T dot(const vec_dimd<T, D> &b) const { T t; for(size_t i = 0; i < D; i++) t += (v[i] * b.v[i]); return t; } + friend T dot(const vec_dimd<T,D> &lhs, const vec_dimd<T,D> &rhs) { return lhs.dot(rhs); } + + vex::vec_dimd<T, D> normalize() const { return *this / magnitude(); } + vex::vec_dimd<T, D> abs() const { vex::vec_dimd<T, D> copy(*this); for(unsigned i = 0; i < D; i++) copy.v[i] = std::abs(copy.v[i]); return copy; } + + auto cross(const vec_dimd<T,D> &o) const { + if constexpr(D == 2) return v[0]*o[0] - v[1]*o[1]; + if constexpr(D == 3) return vec_dimd<T, D>{v[1]*o[2]-v[2]*o[1], v[2]*o[0]-v[0]*o[2],v[0]*o[1]-v[1]*o[0]}; + } +}; + +template<arithmetic T> +using vec2 = vec_dimd<T, 2>; + +template<arithmetic T> +using vec3 = vec_dimd<T, 3>; + +/*(x, y) -> (r, theta)*/ +template<arithmetic R, arithmetic P> +static vec2<R> +polar(const vec2<P> &in) +{ + return vec2<R>( + std::sqrt(in[0] * in[0] + in[1] * in[1]), + std::atan2(in[1], in[0]) + ); +} +/*(r, theta) -> (x, y)*/ +template<arithmetic R, arithmetic P> +static vec2<R> +cartesian(const vec2<P> &in) +{ + return vec2<R>( + (R)(cos(in[1]) * in[0]), + (R)(sin(in[1]) * in[0]) + ); +} + +}; + +#endif diff --git a/include/window.hpp b/include/window.hpp new file mode 100644 index 0000000..78001ea --- /dev/null +++ b/include/window.hpp @@ -0,0 +1,69 @@ +#ifndef WINDOW_HPP +#define WINDOW_HPP 1 + +#include "util.hpp" +#include "straw.hpp" +#include <memory> +#include <unordered_map> + +//Wrapper for screens that act like windows +class Window { + straw::screen<screenchr> m_border; + straw::screen<screenchr> m_screen; + std::string m_title; + bool m_hidden; +public: + explicit Window(const std::string Title, unsigned X, unsigned Y, unsigned W, unsigned H, bool hidden = false) : + m_border(X, Y, W, 1), m_screen(X, Y+1, W, H-1), m_title(Title), m_hidden(hidden) {} + ~Window() = default; + + template<typename T> + friend Window &operator<<(Window &o, const T &t) { + o.m_screen << t; + return o; + } + + straw::screen<screenchr> *screen() { return &m_screen; } + std::string title() const { return m_title; } + + void draw(bool focus); + + void setHidden(bool mode) { m_hidden = mode; } + bool hidden() const { return m_hidden; } +}; + +#define WINDOW_SYSTEMVIEW_ID "Systemview" +#define WINDOW_SYSTEMVIEW_SEARCH_ID "SystemviewSearch" +#define WINDOW_BODYINFO_ID "Bodyinfo" +#define WINDOW_EVENTS_ID "Events" +#define WINDOW_TIMEMAN_ID "Timeman" + +class WindowContext { + std::unordered_map<std::string, Window> m_windows; + std::vector<std::string> m_windowOrder; + unsigned m_focus; +public: + WindowContext() : m_focus(0) {} + ~WindowContext() {} + + void registerWindow(const std::string &id, + const std::string &title, + unsigned x, unsigned y, + unsigned w, unsigned h, + bool hidden = false); + + Window &operator[](const std::string &id) { return m_windows.at(id); } + Window &operator[](unsigned id) { return m_windows.at(m_windowOrder[id]); } + Window &operator()() { return m_windows.at(m_windowOrder[m_focus]); } + + void update(int code); + void draw(); + + void setWindowHidden(const std::string &id, bool mode); + void focus(const std::string &id); + + unsigned getFocused() { return m_focus; } + std::string getFocusedString() { return m_windowOrder[m_focus]; } +}; + +#endif |