diff --git a/libopenage/CMakeLists.txt b/libopenage/CMakeLists.txt index 51ef5561be..18d33fa922 100644 --- a/libopenage/CMakeLists.txt +++ b/libopenage/CMakeLists.txt @@ -48,6 +48,7 @@ add_subdirectory("rng") add_subdirectory("shader") add_subdirectory("terrain") add_subdirectory("testing") +add_subdirectory("tube") add_subdirectory("unit") add_subdirectory("util") diff --git a/libopenage/curve/CMakeLists.txt b/libopenage/curve/CMakeLists.txt index f03dc732cc..e1eb547e12 100644 --- a/libopenage/curve/CMakeLists.txt +++ b/libopenage/curve/CMakeLists.txt @@ -1,12 +1,16 @@ add_sources(libopenage - internal/keyframe_container.cpp - internal/value_container.cpp - queue.cpp continuous.cpp - discrete.cpp curve.cpp + discrete.cpp + iterator.cpp + map.cpp + map_filter_iterator.cpp + queue.cpp + queue.cpp + queue_filter_iterator.cpp ) -add_subdirectory(test) add_subdirectory(demo) +add_subdirectory(internal) +add_subdirectory(test) diff --git a/libopenage/curve/curve.h b/libopenage/curve/curve.h index b8bcca666b..5513833e14 100644 --- a/libopenage/curve/curve.h +++ b/libopenage/curve/curve.h @@ -2,6 +2,8 @@ #pragma once +#include + namespace openage { namespace curve { diff --git a/libopenage/curve/demo/CMakeLists.txt b/libopenage/curve/demo/CMakeLists.txt index e46acf1aa5..c1545e58c1 100644 --- a/libopenage/curve/demo/CMakeLists.txt +++ b/libopenage/curve/demo/CMakeLists.txt @@ -1,6 +1,7 @@ add_sources (libopenage + aicontroller.cpp + gamestate.cpp + gui.cpp main.cpp physics.cpp - gui.cpp - aicontroller.cpp ) diff --git a/libopenage/curve/demo/gamestate.cpp b/libopenage/curve/demo/gamestate.cpp new file mode 100644 index 0000000000..24df67232e --- /dev/null +++ b/libopenage/curve/demo/gamestate.cpp @@ -0,0 +1,3 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#include "gamestate.h" diff --git a/libopenage/curve/internal/CMakeLists.txt b/libopenage/curve/internal/CMakeLists.txt new file mode 100644 index 0000000000..5a456d2c09 --- /dev/null +++ b/libopenage/curve/internal/CMakeLists.txt @@ -0,0 +1,4 @@ +add_sources(libopenage + keyframe_container.cpp + value_container.cpp +) diff --git a/libopenage/curve/iterator.cpp b/libopenage/curve/iterator.cpp new file mode 100644 index 0000000000..f4d39d93cd --- /dev/null +++ b/libopenage/curve/iterator.cpp @@ -0,0 +1,3 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#include "iterator.h" diff --git a/libopenage/curve/iterator.h b/libopenage/curve/iterator.h index c1af8e282e..ab10e2e60e 100644 --- a/libopenage/curve/iterator.h +++ b/libopenage/curve/iterator.h @@ -10,13 +10,17 @@ namespace curve { template -class CurveIterator : - public std::iterator -{ +class CurveIterator { public: virtual val_t &value() const = 0; virtual bool valid() const = 0; + CurveIterator() : + base{}, + container_end{}, + from{-std::numeric_limits::infinity()}, + to{+std::numeric_limits::infinity()} {} + CurveIterator(const CurveIterator &rhs) : base{rhs.base}, container_end{rhs.container_end}, diff --git a/libopenage/curve/map.cpp b/libopenage/curve/map.cpp new file mode 100644 index 0000000000..252d53e6a0 --- /dev/null +++ b/libopenage/curve/map.cpp @@ -0,0 +1,3 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#include "map.h" diff --git a/libopenage/curve/map.h b/libopenage/curve/map.h index b178e68068..3feb1f1a97 100644 --- a/libopenage/curve/map.h +++ b/libopenage/curve/map.h @@ -2,6 +2,7 @@ #pragma once +#include #include #include "curve.h" diff --git a/libopenage/curve/map_filter_iterator.cpp b/libopenage/curve/map_filter_iterator.cpp new file mode 100644 index 0000000000..e520e50412 --- /dev/null +++ b/libopenage/curve/map_filter_iterator.cpp @@ -0,0 +1,3 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#include "map_filter_iterator.h" diff --git a/libopenage/curve/queue_filter_iterator.cpp b/libopenage/curve/queue_filter_iterator.cpp new file mode 100644 index 0000000000..e7acde6feb --- /dev/null +++ b/libopenage/curve/queue_filter_iterator.cpp @@ -0,0 +1,3 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#include "queue_filter_iterator.h" diff --git a/libopenage/tube/.gitignore b/libopenage/tube/.gitignore new file mode 100644 index 0000000000..5ae0e4943c --- /dev/null +++ b/libopenage/tube/.gitignore @@ -0,0 +1,31 @@ +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +*build* diff --git a/libopenage/tube/CMakeLists.txt b/libopenage/tube/CMakeLists.txt new file mode 100644 index 0000000000..c29c76712d --- /dev/null +++ b/libopenage/tube/CMakeLists.txt @@ -0,0 +1,16 @@ + +add_sources(libopenage + keyframe_container.cpp + #tube_container_iterator.cpp + tube_event.cpp + tube_queue.cpp + tube_object.cpp + tube_objectlist.cpp + tube_continuous.cpp + tube_discrete.cpp + tube.cpp + value_container.cpp +) + +add_subdirectory(test) +add_subdirectory(demo) diff --git a/libopenage/tube/datatypes.md b/libopenage/tube/datatypes.md new file mode 100644 index 0000000000..feade3a0d4 --- /dev/null +++ b/libopenage/tube/datatypes.md @@ -0,0 +1,57 @@ +TUBE DATATYPES +================= + +This document describes the datatypes that should be available within the tubes library. +They consists of simple, single dimensinal types and list types. + +This document is intended for brainstorming on needed datatypes. + +Simple Types +------------ + +Simple types only have one distinct value at a specific point in time. + +Discrete Interpolation +---------------------- + +"Step function" style values. These types adopt the new value exactly at the point in time changed. +They are useful for example for values like unit capacity, hitpoints, resource count, ... + +Linear Interpolation +-------------------- + +Linear connections between two points. These types have to overload the operators + and *, since these +are used to interpolate between t\_n and t\_n+1. These values change consistently over time, if t\_n+1 exists. +These are useful for example for unit position, building progress, ... + +Nyan Values +-------------- + +This container keeps track of nyan objects over time and their respective properties. + +Container Types +=============== + +Container types hold lists of items at any given time. They store a creation and a deletion timestamp for each item, and offer iteration logic to traverse the active elements in the container. + +Map Container +------------- + +The map container stores items based on an unique identifier mapped to an item. It keeps track of the existence of the item. No guaranteed order of items within this container is given (similar to std::unordered_map). +This container is useful for example for unit lists ... + +Set Container +---------------- + +The set container stores items, just as a normal array would. It keeps track of the existence of the items, but does not guarentee any particular ordering (similar to std::unordered_set). +This Container is useful for any non-indexed data structures, for example projectiles. + +Queue Container +--------------- + +The queue container represents a random access queue while keeping the ordering of the queue. +It is usually used for pushing in the back and popping at the front (FIFO-Stlye) but offers random access insertion and deletion as well. +This container is useful for example for action queues and buildung queues. + +TUBE FILES +============ diff --git a/libopenage/tube/demo/CMakeLists.txt b/libopenage/tube/demo/CMakeLists.txt new file mode 100644 index 0000000000..e46acf1aa5 --- /dev/null +++ b/libopenage/tube/demo/CMakeLists.txt @@ -0,0 +1,6 @@ +add_sources (libopenage + main.cpp + physics.cpp + gui.cpp + aicontroller.cpp +) diff --git a/libopenage/tube/demo/aicontroller.cpp b/libopenage/tube/demo/aicontroller.cpp new file mode 100644 index 0000000000..b58e36b860 --- /dev/null +++ b/libopenage/tube/demo/aicontroller.cpp @@ -0,0 +1,30 @@ +// Copyright 2015-2017 the openage authors. See copying.md for legal info. + +#include "aicontroller.h" + +namespace openage { +namespace tubepong { + +std::vector &AIInput::getInputs( + const PongPlayer &player, + const PongBall &ball, + const tube::tube_time_t &now) { + this->event_cache.clear(); + + auto position = player.position.get(now); + + // Yes i know, there is /3 used - instead of the logical /2 - this is to + // create a small safety boundary of 1/3 for enhanced fancyness + + // Ball is below position + if (ball.position.get(now)[1] > position + player.size.get(now) / 3) { + event_cache.push_back(event(player.id, event::DOWN)); + } else if (ball.position.get(now)[1] < position - player.size.get(now) / 3) { + // Ball is above position + event_cache.push_back(event(player.id, event::UP)); + } + + return this->event_cache; +} + +}} // openage::tubepong diff --git a/libopenage/tube/demo/aicontroller.h b/libopenage/tube/demo/aicontroller.h new file mode 100644 index 0000000000..1a8c67100a --- /dev/null +++ b/libopenage/tube/demo/aicontroller.h @@ -0,0 +1,23 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#pragma once + +#include + +#include "gamestate.h" + +namespace openage { +namespace tubepong { + +class AIInput { +public: + std::vector &getInputs( + const PongPlayer &player, + const PongBall &ball, + const tube::tube_time_t &now); + +private: + std::vector event_cache; +}; + +}} // openage::tubepong diff --git a/libopenage/tube/demo/gamestate.h b/libopenage/tube/demo/gamestate.h new file mode 100644 index 0000000000..3ae0674f84 --- /dev/null +++ b/libopenage/tube/demo/gamestate.h @@ -0,0 +1,59 @@ +// Copyright 2015-2017 the openage authors. See copying.md for legal info. + +#pragma once + +#include "../tube_continuous.h" +#include "../tube_discrete.h" +#include "../tube_object.h" +#include "../../util/vector.h" + +namespace openage { +namespace tubepong { + +struct event { + int player; + enum state_e { + UP, DOWN, START, IDLE, LOST + } state; + event(int id, state_e s) : player(id), state(s) {} + event() : player(0), state(IDLE) {} +}; + +class PongPlayer : public tube::TubeObject { +public: + PongPlayer() { + speed.set_drop(0, 1); + position.set_drop(0, 0.5); + lives.set_drop(0, 1); + state.set_drop(0, event(0, event::IDLE)); + size.set_drop(0, 0.1); + y = 0; + id = 0; + } + + tube::Discrete speed; + tube::Continuous position; + tube::Discrete lives; + tube::Discrete state; + tube::Discrete size; + float y; + int id; +}; + +class PongBall : public tube::TubeObject { +public: + tube::Discrete> speed; + tube::Continuous> position; +}; + +class PongState { +public: + PongPlayer p1; + PongPlayer p2; + + PongBall ball; + + util::Vector<2> resolution; +}; + +}} // openage::tubepong diff --git a/libopenage/tube/demo/gui.cpp b/libopenage/tube/demo/gui.cpp new file mode 100644 index 0000000000..604ea91c10 --- /dev/null +++ b/libopenage/tube/demo/gui.cpp @@ -0,0 +1,163 @@ +// Copyright 2015-2017 the openage authors. See copying.md for legal info. + +#include "gui.h" + +#include +#include + +namespace openage { +namespace tubepong { + +std::vector &Gui::getInputs(const PongPlayer &player) { + input_cache.clear(); + event evnt; + evnt.player = player.id; + evnt.state = event::IDLE; + timeout(0); + int c = getch(); + // mvprintw(0,1, "IN: %i", c); + switch (c) { + case KEY_DOWN: + evnt.state = event::DOWN; + input_cache.push_back(evnt); + mvprintw(1, 1, "DOWN"); + break; + case KEY_UP: + evnt.state = event::UP; + input_cache.push_back(evnt); + mvprintw(1, 1, "UP"); + break; + case ' ': evnt.state = event::START; break; + case 27: // esc or alt + endwin(); + exit(0); + default: break; + } + + return input_cache; +} + +enum { + COLOR_PLAYER1 = 1, + COLOR_PLAYER2 = 2, + COLOR_BALL = 3, + COLOR_DEBUG = 4, + + COLOR_0 = 5, + COLOR_1 = 6, + COLOR_2 = 7, + COLOR_3 = 8, + COLOR_4 = 9, +}; + +Gui::Gui() { + initscr(); + start_color(); + init_pair(COLOR_PLAYER1, COLOR_BLUE, COLOR_BLUE); + init_pair(COLOR_PLAYER2, COLOR_RED, COLOR_RED); + init_pair(COLOR_BALL, COLOR_BLUE, COLOR_WHITE); + init_pair(COLOR_DEBUG, COLOR_WHITE, COLOR_BLACK); + init_pair(COLOR_0, COLOR_RED, COLOR_BLACK); + init_pair(COLOR_1, COLOR_GREEN, COLOR_BLACK); + + keypad(stdscr, true); + noecho(); + curs_set(0); + // getmaxyx(stdscr,state.resolution[1], state.resolution[0]); + + attron(COLOR_PAIR(COLOR_DEBUG)); + mvprintw( + 4, 5, " oooooooooo "); + mvprintw( + 5, 5, " 888 888 ooooooo ooooooo oooooooo8 "); + mvprintw( + 6, 5, " 888oooo88 888 888 888 888 888 88o "); + mvprintw( + 7, 5, " 888 888 888 888 888 888oo888o "); + mvprintw( + 8, 5, " o888o 88ooo88 o888o o888o 88 888 "); + mvprintw( + 9, 5, " 888ooo888 "); + attroff(COLOR_PAIR(COLOR_DEBUG)); + + getch(); +} + +void Gui::draw(PongState &state, const tube::tube_time_t &now) { + erase(); + // clear(); + // Print Score + attron(COLOR_PAIR(COLOR_DEBUG)); + getmaxyx(stdscr, state.resolution[1], state.resolution[0]); + attron(COLOR_PAIR(COLOR_DEBUG)); + mvprintw(2, + state.resolution[0] / 2 - 5, + "P1 %i | P2 %i", + state.p1.lives(now), + state.p2.lives(now)); + + mvvline(0, state.resolution[0] / 2, ACS_VLINE, state.resolution[1]); + mvprintw(0, 1, "NOW: %f", now); + mvprintw(1, 1, "SCR: %i | %i", state.resolution[0], state.resolution[1]); + mvprintw(2, + 1, + "P1: %f, %f, %i", + state.p1.position(now), + state.p1.y, + state.p1.state(now).state); + mvprintw(3, + 1, + "P2: %f, %f, %i", + state.p2.position(now), + state.p2.y, + state.p2.state(now).state); + for (int i = 0; i < 1000; i += 100) { + mvprintw(4 + i / 100, + 1, + "BALL in %03i: %f | %f; SPEED: %f | %f", + i, + state.ball.position(now + i)[0], + state.ball.position(now + i)[1], + state.ball.speed(now + i)[0], + state.ball.speed(now + i)[1]); + } + mvprintw(state.resolution[1] - 1, 1, "Press ESC twice to Exit"); + attroff(COLOR_PAIR(COLOR_DEBUG)); + + attron(COLOR_PAIR(COLOR_PLAYER1)); + for (int i = -state.p1.size(now) / 2; i < state.p1.size(now) / 2; i++) { + mvprintw(state.p1.position(now) + i, state.p1.y, "|"); + } + attroff(COLOR_PAIR(COLOR_PLAYER1)); + + attron(COLOR_PAIR(COLOR_PLAYER2)); + for (int i = -state.p2.size(now) / 2; i < state.p2.size(now) / 2; i++) { + mvprintw(state.p2.position(now) + i, state.p2.y, "|"); + } + attroff(COLOR_PAIR(COLOR_PLAYER2)); + + for (int i = 0; i < 9999; ++i) { + draw_ball(state.ball.position(now + i * 50), i); + } + + /*attron(COLOR_PAIR(COLOR_BALL)); + mvprintw(state.ball.position(now)[1], + state.ball.position(now)[0], + "o"); + */ + attroff(COLOR_PAIR(COLOR_BALL)); + refresh(); +} + +void Gui::draw_ball(util::Vector<2> pos, int idx) { + switch (idx) { + case 0: attron(COLOR_PAIR(COLOR_0)); break; + default: + case 1: attron(COLOR_PAIR(COLOR_1)); break; + } + + mvprintw((int)(pos[1]), (int)(pos[0]), "X"); + standend(); +} +} +} // openage::tubepong diff --git a/libopenage/tube/demo/gui.h b/libopenage/tube/demo/gui.h new file mode 100644 index 0000000000..f8d2142ff2 --- /dev/null +++ b/libopenage/tube/demo/gui.h @@ -0,0 +1,25 @@ +// Copyright 2015-2017 the openage authors. See copying.md for legal info. + +#pragma once + +#include + +#include "gamestate.h" + +namespace openage { +namespace tubepong { + + +class Gui { + +public: + Gui(); + std::vector &getInputs(const PongPlayer &player); + void draw(PongState &state, const tube::tube_time_t &now); + void draw_ball(util::Vector<2> ball, int idx); + +private: + std::vector input_cache; +}; + +}} // openage::tubepong diff --git a/libopenage/tube/demo/main.cpp b/libopenage/tube/demo/main.cpp new file mode 100644 index 0000000000..d5ad18a910 --- /dev/null +++ b/libopenage/tube/demo/main.cpp @@ -0,0 +1,83 @@ +// Copyright 2015-2017 the openage authors. See copying.md for legal info. + +#include + +#include + +#include "aicontroller.h" +#include "gamestate.h" +#include "gui.h" +#include "physics.h" + +typedef std::chrono::high_resolution_clock Clock; + +namespace openage { +namespace tubepong { + +int demo() { + // Restart forever + tubepong::Gui gui; + tubepong::Physics phys; + tubepong::AIInput ai; + bool running = true; + + while (running) { + tubepong::PongState state; + tube::tube_time_t now = 1; + + state.p1.lives.set_drop(now, 3); + state.p1.id = 0; + state.p1.size.set_drop(now, 4); + state.p2.lives.set_drop(now, 3); + state.p2.id = 1; + state.p2.size.set_drop(now, 4); + + auto init_speed = + util::Vector<2>(((rand() % 2) * 2 - 1) * (0.1f + rand() % 4) / 70.f, + 0.01f * (rand() % 100) / 70.f); + + gui.draw(state, now); // update gui related parameters + + state.ball.speed.set_drop(now, init_speed); + state.ball.position.set_drop(now, state.resolution * 0.5); + state.p1.position.set_drop(now, state.resolution[1] / 2); + state.p2.position.set_drop(now, state.resolution[1] / 2); + + gui.draw(state, now); // initial drawing with corrected ball + + auto loop_start = Clock::now(); + now += 1; + std::cout << "p1: " << state.p1.lives.get(now) << " p2 " + << state.p2.lives.get(now) << std::endl; + + while (state.p1.lives.get(now) > 0 && state.p2.lives.get(now) > 0) { +#ifdef HUMAN + phys.processInput(state, state.p1, gui.getInputs(state.p1), now); +#else + gui.getInputs(state.p1); + phys.processInput( + state, state.p1, ai.getInputs(state.p1, state.ball, now), now); +#endif + phys.processInput( + state, state.p2, ai.getInputs(state.p2, state.ball, now), now); + + state.p1.y = 0; + state.p2.y = state.resolution[0] - 1; + + phys.update(state, now); + + gui.draw(state, now); + usleep(40000); + + double dt = std::chrono::duration_cast( + (Clock::now() - loop_start)) + .count(); + now += dt; + // now += 40; + loop_start = Clock::now(); + } + } + return 0; +} +} +} // openage::tubepong diff --git a/libopenage/tube/demo/physics.cpp b/libopenage/tube/demo/physics.cpp new file mode 100644 index 0000000000..a91ef7ac70 --- /dev/null +++ b/libopenage/tube/demo/physics.cpp @@ -0,0 +1,138 @@ +// Copyright 2015-2017 the openage authors. See copying.md for legal info. + +#include "physics.h" + +#include + +#include + +namespace openage { +namespace tubepong { + +const float extrapolating_time = 100.0f; +const int init_recursion_limit = 50; + +void Physics::processInput(PongState &state, PongPlayer &player, std::vector &events, const tube::tube_time_t &now) { + for (auto evnt : events) { + //Process only if the future has changed + if (player.state.get(now).state != evnt.state) { + player.state.set_drop(now, evnt); + + switch(evnt.state) { + case event::UP: + case event::DOWN: { + if (evnt.state == event::UP) { + player.speed.set_drop(now, -2); + } else if (evnt.state == event::DOWN) { + player.speed.set_drop(now, 2); + } + player.speed.set_drop(now + extrapolating_time, 0); + + float new_pos = player.position.get(now) + + (player.speed.get(now+extrapolating_time) - player.speed.get(now) / 2 + player.speed.get(now)); + if (new_pos < 0) + new_pos = 0; + if (new_pos > state.resolution[1]) + new_pos = state.resolution[1]; + + player.position.set_drop(now+extrapolating_time, new_pos); + evnt.state = event::IDLE; + player.state.set_drop(now + extrapolating_time, evnt); + break; + } + case event::IDLE: + player.position.set_drop(now+extrapolating_time, + player.position.get(now)); + break; + case event::START: + if (player.state.get(now).state == event::LOST) { + state.ball.position.set_drop(now, state.resolution * 0.5); + } + update_ball(state, now, init_recursion_limit); + break; + default: + break; + } + } + } + } + + void Physics::update(PongState &state, const tube::tube_time_t &now) { + + + auto pos = state.ball.position.get(now); + //Handle panel p1 + if (pos[0] <= 1 + && pos[1] > state.p1.position.get(now) - state.p1.size.get(now) / 2 + && pos[1] < state.p1.position.get(now) + state.p1.size.get(now) / 2 + && state.ball.speed.get(now)[0] < 0) { + //Ball hit the paddel in this frame + auto s = state.ball.speed.get(now); + s[0] *= -1.0; + state.ball.speed.set_drop(now, s); + state.ball.position.set_drop(now, pos); // this line can handle the future! + + update_ball(state, now, init_recursion_limit); + } else if (pos[0] >= state.resolution[0] - 1 + && pos[1] > state.p2.position.get(now) - state.p2.size.get(now) / 2 + && pos[1] < state.p2.position.get(now) + state.p2.size.get(now) / 2 + && state.ball.speed.get(now)[0] > 0) { + //Ball hit the paddel in this frame + auto s = state.ball.speed.get(now); + s[0] *= -1.0; + state.ball.speed.set_drop(now, s); + state.ball.position.set_drop(now, pos); // this line can handle the future! + + update_ball(state, now, init_recursion_limit); + } else if (state.ball.position.needs_update(now)) { + update_ball(state, now, init_recursion_limit); + } + + // Game loose condition + if (pos[0] < 0) { + state.p1.lives.set_drop(now, state.p1.lives.get(now) - 1); + state.p1.state.set_drop(now, event(state.p1.id, event::LOST)); + } + if (pos[0] > state.resolution[0]) { + state.p2.lives.set_drop(now, state.p2.lives.get(now) - 1); + state.p2.state.set_drop(now, event(state.p2.id, event::LOST)); + } + +} + + +void Physics::update_ball(PongState &state, const tube::tube_time_t &now, int recursion_limit) { + //calculate the ball takes to hit a wall. + auto speed = state.ball.speed.get(now); + auto pos = state.ball.position.get(now); + + float ty = 0; + //char mode = ' '; + if (speed [1] > 0) { + ty = (state.resolution[1] - pos[1]) / speed[1]; + //mode = 'v'; + } else if (speed[1] < 0) { + ty = pos[1] / -speed[1]; + //mode = '^'; + } + +/* mvprintw(20 - recursion_limit, 1, "R %i: %c TY %f | pos: %f | %f speed %f | %f res %i | %i", + init_recursion_limit - recursion_limit, mode, + ty, + pos[0], pos[1], + speed[0], speed[1], + state.resolution[0], state.resolution[1] + ); +*/ + if (ty > 0) { + auto hit_pos = pos + speed * ty; + state.ball.position.set_drop(now + ty, hit_pos); + speed[1] *= -1; + state.ball.speed.set_drop(now + ty, speed); + if (recursion_limit > 1) { + update_ball(state, now + ty, recursion_limit - 1); + } + } +} + +}} // openage::tubepong diff --git a/libopenage/tube/demo/physics.h b/libopenage/tube/demo/physics.h new file mode 100644 index 0000000000..02a3a72a82 --- /dev/null +++ b/libopenage/tube/demo/physics.h @@ -0,0 +1,21 @@ +// Copyright 2015-2017 the openage authors. See copying.md for legal info. + +#pragma once + +#include + +#include "gamestate.h" +#include "../tube.h" + +namespace openage { +namespace tubepong { + +class Physics { +public: + void processInput(PongState &, PongPlayer &, std::vector &events, const tube::tube_time_t &now); + void update(PongState &, const tube::tube_time_t &); +protected: + void update_ball(PongState &, const tube::tube_time_t &, int recursion_limit); +}; + +}} // openage::tubepong diff --git a/libopenage/tube/keyframe_container.cpp b/libopenage/tube/keyframe_container.cpp new file mode 100644 index 0000000000..9620527dcb --- /dev/null +++ b/libopenage/tube/keyframe_container.cpp @@ -0,0 +1,8 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#include "keyframe_container.h" + +namespace openage { +namespace tube { + +}} // openage::tube diff --git a/libopenage/tube/keyframe_container.h b/libopenage/tube/keyframe_container.h new file mode 100644 index 0000000000..5928575dfa --- /dev/null +++ b/libopenage/tube/keyframe_container.h @@ -0,0 +1,199 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#pragma once + +#include "tube.h" + +#include "../error/error.h" + +#include +#include +#include +#include + +namespace openage { +namespace tube { + +/** + * A timely ordered list with several management functions + * + * This class manages different time-based management functions for the double- + * linked list approach that lies underneath. It contains a double-linked list + * to be accessed via a non-accurate timing functionality, this means, that for + * getting a value, not the exact timestamp has to be known, it will always return + * the one closest, less or equal to the requested one. + **/ +template +class KeyframeContainer { +public: + /** + * A element of the double-linked list KeyframeContainer + */ + class Keyframe { + public: + Keyframe(const tube_time_t &time) : + time(time) {} + + // Contruct it from time and value + Keyframe(const tube_time_t &time, const _T &value) : + time(time), + value(value) {} + + const tube_time_t time = 0; + _T value = _T(); + }; + + typedef std::list tubecontainer; + typedef typename tubecontainer::const_iterator KeyframeIterator; + KeyframeContainer(); + ~KeyframeContainer(); + + // Get the last element with e->time <= time + KeyframeIterator last(const tube_time_t &time, const KeyframeIterator & hint) const; + KeyframeIterator last(const tube_time_t &time) const { + return this->last(time, this->container.begin()); + } + + /** + * Insert a new element without a hint. + * + * This function is recommended for use, whenever possible, keep a hint to insert + * the data. + */ + KeyframeIterator insert(const Keyframe &value) { + return this->insert(value, this->container.begin()); + } + + /** + * Insert a new element. The hint shall give an approximate location, where the + * inserter will start to look for a insertion point. If a good hint is given, the + * runtime of this function will not be affected by the current history size. + */ + KeyframeIterator insert(const Keyframe &value, const KeyframeIterator &hint); + + /** + * Create and insert a new element without submitting a hint. + * The use of this function is discouraged, use it only, if your really do not + * have the possibility to get a hint + */ + KeyframeIterator insert(const tube_time_t &time, const _T&value) { + return this->insert(Keyframe(time, value), this->container.begin()); + } + /** + * Create and insert a new element. The hint gives an approximate location. + */ + KeyframeIterator insert(const tube_time_t &time, const _T&value, const KeyframeIterator &hint) { + return this->insert(Keyframe(time, value), hint); + } + + /** + * Erase all elements that come after this last valid element. + */ + KeyframeIterator erase_after(KeyframeIterator last_valid); + + /** + * Erase a single element from the tube. + */ + KeyframeIterator erase(KeyframeIterator ); + + KeyframeIterator begin() const { + return container.begin(); + } + + KeyframeIterator end() const { + return container.end(); + } + + void __attribute__ ((noinline)) dump() { + for (auto e : container) { + std::cout << "Element: time: " << e.time << " v: " << e.value << std::endl; + } + } +private: + tubecontainer container; +}; + + +template +KeyframeContainer<_T>::KeyframeContainer() { + //Create a default element at -Inf, that can always be dereferenced - so there will by definition never be + //a element that cannot be dereferenced + this->container.push_back(Keyframe(-std::numeric_limits::infinity(), _T())); +} + +template +KeyframeContainer<_T>::~KeyframeContainer() { +} + +/** + * Select the element that directly preceedes the given timestamp. + * + * Without a hint, start to iterate at the beginning of the buffer, and return + * the element last element before e->time > time. + * This method returns nullptr, if begin->time > time. + **/ +template +typename KeyframeContainer<_T>::KeyframeIterator KeyframeContainer<_T>::last(const tube_time_t &time, const KeyframeIterator &hint) const { + KeyframeIterator e = (hint == this->container.end()) ? this->container.begin() : hint; + + if (this->container.front().time > time) { + // This will never happen due to the container.front->time == -Inf magic! + throw new Error(ERR << "rupture in spacetime detected, tube container is broken"); + } + + // Search in the queue + if (time > e->time) { // the searched element is supposed to be AFTER the hint + // perform the search via ->next + while (e != this->container.end() && time >= e->time) { + e++; + } + e--; + // e is now one of two options: + // 1. e == end: The last element of the queue was smaller than `time` + // 2. e != end: There was a element with `e->time` > `time` + + } else if (time < e->time) { + // the searched element is supposed to be BEFORE the hint + // perform the search via ->prev + while (e != this->container.begin() && time < e->time) { + e--; + } + // e is now one of two options: + // 1. e == begin: The time was before every element in the queue + // 2. e != begin: There was an element with `e->time` > `time` + + } else { + // perform <= search - and return e, whose time is == time. + } + + return e; +} + +/** + * Determine where to insert based on time, and insert + */ +template +typename KeyframeContainer<_T>::KeyframeIterator KeyframeContainer<_T>::insert(const KeyframeContainer<_T>::Keyframe &e, const KeyframeContainer<_T>::KeyframeIterator &hint) { + KeyframeIterator at = this->last(e.time, hint); + at ++; + return this->container.insert(at, e); +} + +/** + * Go from the end to the last_valid element, and call erase on all of them + */ +template +typename KeyframeContainer<_T>::KeyframeIterator KeyframeContainer<_T>::erase_after(KeyframeContainer<_T>::KeyframeIterator last_valid) { + //Delete from the end to (excluded) last_valid + return this->container.erase(++last_valid, container.end()); +} + +/** + * Delete the element from the list and call delete on it. + */ +template +typename KeyframeContainer<_T>::KeyframeIterator KeyframeContainer<_T>::erase(KeyframeContainer<_T>::KeyframeIterator e) { + return this->container.erase(e); +} + +}} // openage::tube diff --git a/libopenage/tube/map_filter_iterator.h b/libopenage/tube/map_filter_iterator.h new file mode 100644 index 0000000000..139253d306 --- /dev/null +++ b/libopenage/tube/map_filter_iterator.h @@ -0,0 +1,99 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#pragma once + +#include +#include + +#include "tube.h" + +namespace openage { +namespace tube { + +template valid_f = valid > +class TubeMapFilterIterator : + virtual public std::iterator, + public TubeIterator +{ +protected: + //typedef typename std::iterator iterator_t; + typedef typename container_t::iterator iterator_t; + iterator_t base; + iterator_t container_end; + + tube_time_t from; + tube_time_t to; + +public: + TubeMapFilterIterator(const iterator_t &base, + const iterator_t &container_end, + const tube_time_t &from, + const tube_time_t &to) : + base{base}, + container_end{container_end}, + from{from}, + to{to} {} + + TubeMapFilterIterator(const TubeMapFilterIterator &rhs) : + base{rhs.base}, + container_end{rhs.container_end}, + from{rhs.from}, + to{rhs.to} {} + + TubeMapFilterIterator &operator =(const TubeMapFilterIterator &rhs) { + this->base = rhs.base; + this->container_end = rhs.container_end; + this->from = rhs.from; + this->to = rhs.to; + return *this; + } + + TubeMapFilterIterator &operator ++() { + do { + ++this->base; + } while (this->base != this->container_end && + (existent_from(this->base->second.value) < from || + existent_until(this->base->second.value) > to)); + return *this; + } + + //We only want to Iterate forward - so maybe delete this? + //timed_iterator &operator --() + //{ + // --base; + // return *this; + //} + + val_t &operator *() const { + return this->base->second.value; + } + + val_t *operator ->() const { + return &**this; + } + + virtual bool operator ==(const TubeMapFilterIterator &rhs) const { + return this->base == rhs.base; + } + + virtual bool operator !=(const TubeMapFilterIterator &rhs) const { + return this->base != rhs.base; + } + + virtual bool valid(const tube_time_t &time) { + return valid_f(base->second.value, time); + } + + val_t &value() override { + return this->base->second.value; + } + + const key_t &key() { + return this->base->first; + } +}; + +}} // openage::tube diff --git a/libopenage/tube/queue_filter_iterator.h b/libopenage/tube/queue_filter_iterator.h new file mode 100644 index 0000000000..f2b047eaae --- /dev/null +++ b/libopenage/tube/queue_filter_iterator.h @@ -0,0 +1,100 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#pragma once + +#include +#include + +#include "tube.h" + +namespace openage { +namespace tube { + +template +class Queue; + +template +class TubeQueueFilterIterator : + virtual public std::iterator, + public TubeIterator +{ + friend class Queue; +protected: + typedef typename container_t::iterator iterator_t; + iterator_t base; + iterator_t container_end; + + tube_time_t from; + tube_time_t to; + +public: + TubeQueueFilterIterator(const iterator_t &base, + const iterator_t &container_end, + const tube_time_t &from, + const tube_time_t &to) : + base{base}, + container_end{container_end}, + from{from}, + to{to} {} + + TubeQueueFilterIterator(const TubeQueueFilterIterator &rhs) : + base{rhs.base}, + container_end{rhs.container_end}, + from{rhs.from}, + to{rhs.to} {} + + TubeQueueFilterIterator &operator =(const TubeQueueFilterIterator &rhs) { + this->base = rhs.base; + this->container_end = rhs.container_end; + this->from = rhs.from; + this->to = rhs.to; + return *this; + } + + TubeQueueFilterIterator &operator ++() { + do { + ++this->base; + } while (this->base != this->container_end && + (this->base->time() < from || + this->base->time() > to)); + return *this; + } + + //We only want to Iterate forward - so maybe delete this? + //timed_iterator &operator --() + //{ + // --base; + // return *this; + //} + + val_t &operator *() const { + return this->base->value; + } + + val_t *operator ->() const { + return &**this; + } + + virtual bool operator ==(const TubeQueueFilterIterator &rhs) const { + return this->base == rhs.base; + } + + virtual bool operator !=(const TubeQueueFilterIterator &rhs) const { + return this->base != rhs.base; + } + + virtual bool valid() const { + + if (this->base != this->container_end) { + return this->base->time() >= from && this->base->time() < to; + } + return false; + } + + val_t &value() override { + return this->base->value; + } +}; + +}} // openage::tube diff --git a/libopenage/tube/test/CMakeLists.txt b/libopenage/tube/test/CMakeLists.txt new file mode 100644 index 0000000000..6e340d6393 --- /dev/null +++ b/libopenage/tube/test/CMakeLists.txt @@ -0,0 +1,10 @@ +link_libraries(tube) + +set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") +set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb") + +add_sources(libopenage + test_container.cpp + test_tube_types.cpp + test_serialization.cpp +) diff --git a/libopenage/tube/test/test_container.cpp b/libopenage/tube/test/test_container.cpp new file mode 100644 index 0000000000..02d7df32d5 --- /dev/null +++ b/libopenage/tube/test/test_container.cpp @@ -0,0 +1,212 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#include "../../testing/testing.h" +#include "../unordered_map.h" +#include "../tube.h" +#include "../tube_continuous.h" +#include "../tube_discrete.h" +#include "../tube_queue.h" + +#include +#include + +namespace openage { +namespace tube { +namespace tests { + +struct map_test_element { + volatile int value; + tube_time_t birth, death; + + map_test_element(int v, tube_time_t b, tube_time_t d) : + value(v), + birth(b), + death(d) {} + + tube_time_t existent_from () const { + return this->birth; + } + tube_time_t existent_until () const { + return this->death; + } + + bool operator != (int rhs) { + return this->value != rhs; + } + +}; + +std::ostream &operator << (std::ostream &o, const map_test_element &e) { + o << e.value; + return o; +} + +template +void dump(const std::map &map) { + for (auto i : map) { + std::cout << i.first << ": " << i.second << std::endl; + } +} + +void test_map() { + UnorderedMap map; + map.insert(0, map_test_element(0, 0, 10)); + map.insert(5, map_test_element(1, 5, 10)); + map.insert(200, map_test_element(2, 100, 200)); + + // Basic tests test lookup in the middle of the range. + { + auto t = map.at(2, 0); //At timestamp 2 element 0 + TESTEQUALS(t.first, true); + TESTEQUALS(t.second.value(), 0); + t = map.at(20, 5); + TESTEQUALS(t.first, false); + } + { + auto t = map.at(7, 5); + TESTEQUALS(t.first, true); + TESTEQUALS(t.second.value(), 1); + t = map.at(20, 5); + TESTEQUALS(t.first, false); + t = map.at(2, 5); + TESTEQUALS(t.first, false); + } + { + auto t = map.at(150, 200); + TESTEQUALS(t.first, true); + TESTEQUALS(t.second.value(), 2); + t = map.at(500, 200); + TESTEQUALS(t.first, false); + t = map.at(5, 200); + TESTEQUALS(t.first, false); + } + // test 2.0: test at the boundaries + { + auto t = map.at(0, 0); + TESTEQUALS(t.first, true); + TESTEQUALS(t.second.value(), 0); + t = map.at(10, 0); + TESTEQUALS(t.first, false); + } + { + auto t = map.at(5, 5); + TESTEQUALS(t.first, true); + TESTEQUALS(t.second.value(), 1); + t = map.at(10, 5); + TESTEQUALS(t.first, false); + } + { + auto t = map.at(100, 200); + TESTEQUALS(t.first, true); + TESTEQUALS(t.second.value(), 2); + t = map.at(200, 200); + TESTEQUALS(t.first, false); + } + // Test 3.0 Iterations + { + // Iteration tests + std::map reference; + reference[0] = 0; + reference[5] = 1; + reference[200] = 2; + for (auto it = map.begin(0); it != map.end(); ++it) { // Get all + auto ri = reference.find(it.key()); + if (ri != reference.end()) { + reference.erase(ri); + } + } + TESTEQUALS(reference.empty(), true); + + reference[5] = 5; + for (auto it = map.begin(1); it != map.end(90); ++it) { + auto ri = reference.find(it.key()); + if (ri != reference.end()) { + reference.erase(ri); + } + } + TESTEQUALS(reference.empty(), true); + + reference[5] = 5; + for (auto it = map.between(1,90); it != map.end(); ++it) { + auto ri = reference.find(it.key()); + if (ri != reference.end()) { + reference.erase(ri); + } + } + TESTEQUALS(reference.empty(), true); + } +} + +void test_list() { + +} + +void test_queue() { + Queue q; + q.insert(0, 1); + q.insert(2, 2); + q.insert(4, 3); + q.insert(10, 4); + q.insert(100001, 5); + TESTEQUALS(*q.begin(0), 1); + TESTEQUALS(*q.begin(1), 2); + TESTEQUALS(*q.begin(2), 2); + TESTEQUALS(*q.begin(3), 3); + TESTEQUALS(*q.begin(4), 3); + TESTEQUALS(*q.begin(5), 4); + TESTEQUALS(*q.begin(10), 4); + TESTEQUALS(*q.begin(12), 5); + TESTEQUALS(*q.begin(100000), 5); + + { + std::set reference = {1,2,3}; + for (auto it = q.between(0,6); it != q.end(); ++it) { + auto ri = reference.find(it.value()); + if (ri != reference.end()) { + reference.erase(ri); + } + } + TESTEQUALS(reference.empty(), true); + } + { + std::set reference = {2,3,4}; + for (auto it = q.between(1,40); it != q.end(); ++it) { + auto ri = reference.find(it.value()); + if (ri != reference.end()) { + reference.erase(ri); + } + } + TESTEQUALS(reference.empty(), true); + } + { + std::set reference = {}; + for (auto it = q.between(30,40); it != q.end(); ++it) { + auto ri = reference.find(it.value()); + if (ri != reference.end()) { + reference.erase(ri); + } + } + TESTEQUALS(reference.empty(), true); + } + { + std::set reference = {1,2,3,4}; + for (auto it = q.between(0,40); it != q.end(); ++it) { + auto ri = reference.find(it.value()); + if (ri != reference.end()) { + reference.erase(ri); + } + } + TESTEQUALS(reference.empty(), true); + } + +} + + +void container() { + test_map(); + test_list(); + test_queue(); +} + + +}}} // openage::tube::tests diff --git a/libopenage/tube/test/test_serialization.cpp b/libopenage/tube/test/test_serialization.cpp new file mode 100644 index 0000000000..af8dd046c7 --- /dev/null +++ b/libopenage/tube/test/test_serialization.cpp @@ -0,0 +1,13 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#include "../../testing/testing.h" + +namespace openage { +namespace tube { +namespace tests { + +void serialization() { + +} + +}}} // openage::tube::tests diff --git a/libopenage/tube/test/test_tube_types.cpp b/libopenage/tube/test/test_tube_types.cpp new file mode 100644 index 0000000000..7b0c2acbb9 --- /dev/null +++ b/libopenage/tube/test/test_tube_types.cpp @@ -0,0 +1,124 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#include "../../testing/testing.h" +#include "../keyframe_container.h" +#include "../tube.h" +#include "../tube_continuous.h" +#include "../tube_discrete.h" + + +namespace openage { +namespace tube { +namespace tests { + +void tube_types() { + // Check the base container type + { + KeyframeContainer c; + auto p0 = c.insert(0, 0); + auto p1 = c.insert(1, 1); + auto p2 = c.insert(10, 2); + + // last function tests without hints + TESTEQUALS(c.last(0)->value, 0); + TESTEQUALS(c.last(1)->value, 1); //last shall give >= not only > ! + TESTEQUALS(c.last(5)->value, 1); + TESTEQUALS(c.last(10)->value, 2); + TESTEQUALS(c.last(47)->value, 2); + + // last() with hints. Yes this can make a difference. we want to be + // absolutely shure! + // hint p1 + TESTEQUALS(c.last(0, p0)->value, 0); + TESTEQUALS(c.last(1, p0)->value, 1); //last shall give >= not only > ! + TESTEQUALS(c.last(5, p0)->value, 1); + TESTEQUALS(c.last(10, p0)->value, 2); + TESTEQUALS(c.last(47, p0)->value, 2); + + TESTEQUALS(c.last(0, p1)->value, 0); + TESTEQUALS(c.last(1, p1)->value, 1); //last shall give >= not only > ! + TESTEQUALS(c.last(5, p1)->value, 1); + TESTEQUALS(c.last(10, p1)->value, 2); + TESTEQUALS(c.last(47, p1)->value, 2); + + TESTEQUALS(c.last(0, p2)->value, 0); + TESTEQUALS(c.last(1, p2)->value, 1); //last shall give >= not only > ! + TESTEQUALS(c.last(5, p2)->value, 1); + TESTEQUALS(c.last(10, p2)->value, 2); + TESTEQUALS(c.last(47, p2)->value, 2); + + // Now test the basic erase() function + c.erase(c.last(1)); + + TESTEQUALS(c.last(1)->value, 0); + TESTEQUALS(c.last(5)->value, 0); + TESTEQUALS(c.last(47)->value, 2); + + c.erase_after(c.last(99)); // we dont want to have the element itself erased! + TESTEQUALS(c.last(47)->value, 2); + + c.erase_after(c.last(5)); // now since 10 > 5, element with value 2 has to be gone + TESTEQUALS(c.last(47)->value, 0); + } + + // Check the Simple Continuous type + { + Continuous c; + c.set_insert(0, 0); + c.set_insert(10, 1); + + TESTEQUALS(c.get(0), 0); + + TESTEQUALS_FLOAT(c.get(1), 0.1, 1e-7); + } + + { + Continuous c; + c.set_insert(0, 0); + c.set_insert(20, 20); + + TESTEQUALS(c.get(0), 0); + TESTEQUALS(c.get(1), 1); + TESTEQUALS(c.get(7), 7); + + c.set_drop(10, 10); + TESTEQUALS(c.get(0), 0); + TESTEQUALS(c.get(1), 1); + TESTEQUALS(c.get(7), 7); + } + //Check the discrete type + { + Discrete c; + c.set_insert(0, 0); + c.set_insert(10, 10); + + TESTEQUALS(c.get(0), 0); + TESTEQUALS(c.get(1), 0); + TESTEQUALS(c.get(11), 10); + + Discrete complex; + + complex.set_insert(0, "Test 0"); + complex.set_insert(10, "Test 10"); + + TESTEQUALS(complex.get(0), "Test 0"); + TESTEQUALS(complex.get(1), "Test 0"); + TESTEQUALS(complex.get(11), "Test 10"); + + } + + //check set_drop + { + Discrete c; + c.set_insert(0, 0); + c.set_insert(1, 1); + c.set_insert(3, 3); + + TESTEQUALS(c.get(3), 3); + + c.set_drop(2, 10); + TESTEQUALS(c.get(3), 10); + } +} + +}}} // openage::tube::tests diff --git a/libopenage/tube/tube.cpp b/libopenage/tube/tube.cpp new file mode 100644 index 0000000000..4462daac9c --- /dev/null +++ b/libopenage/tube/tube.cpp @@ -0,0 +1,8 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#include "tube.h" + +namespace openage { +namespace tube { + +}} // openage::tube diff --git a/libopenage/tube/tube.h b/libopenage/tube/tube.h new file mode 100644 index 0000000000..71089b2446 --- /dev/null +++ b/libopenage/tube/tube.h @@ -0,0 +1,45 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#pragma once + +namespace openage { +namespace tube { + +typedef float tube_time_t; + +template +tube_time_t time(const _T &t) { + return t.time(); +} + +template +tube_time_t existent_from (const _T &t) { + return t.existent_from(); +} + +template +tube_time_t existent_until (const _T &t) { + return t.existent_until(); +} + +template +class TubeIterator { +public: + virtual val_t &value() = 0; +}; + +template +bool valid(const _T &, const tube_time_t &at); + +template +using _valid_function_t = bool (*)(const _T&, const tube_time_t &); + +template +bool valid(const _T &t, + const tube_time_t& time) { + return existent_from(t) <= time && existent_until(t) > time; +} + + + +}} // openage::tube diff --git a/libopenage/tube/tube_container_iterator.cpp b/libopenage/tube/tube_container_iterator.cpp new file mode 100644 index 0000000000..b1d1b98164 --- /dev/null +++ b/libopenage/tube/tube_container_iterator.cpp @@ -0,0 +1,8 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#include "tube_container_iterator.h" + +namespace openage { +namespace tube { + +}} // openage::tube diff --git a/libopenage/tube/tube_container_iterator.h b/libopenage/tube/tube_container_iterator.h new file mode 100644 index 0000000000..cc88233a53 --- /dev/null +++ b/libopenage/tube/tube_container_iterator.h @@ -0,0 +1,89 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#pragma once + +#include "tube.h" + +#include + +namespace openage { +namespace tube { + +template +class timed_iterator : public std::iterator< std::bidirectional_iterator_tag, _Ty > +{ +public: + timed_iterator(typename container::const_iterator base, + tube_time_t reference_time) : + base(base), + now(reference_time) {} + + timed_iterator(const timed_iterator &rhs) : + base(rhs.base), + now(rhs.now) {} + + timed_iterator &operator = (const timed_iterator &rhs) { + this->base = rhs.base; + this->now = rhs.now; + return *this; + } + + timed_iterator operator ++() { + ++base; + return *this; + } + + timed_iterator operator ++(int) { + auto tmp = *this; + ++base; + return tmp; + } + + timed_iterator operator --() { + --base; + return *this; + } + + timed_iterator operator --(int) { + auto tmp = *this; + ++base; + return tmp; + } + + const _Ty &operator *() const { + return *base; + } + + const _Ty *operator ->() const { + return &**base; + } + + tube_time_t time() { + return (*this)->time; + } + + bool operator ==(const timed_iterator<_Ty, container> &rhs) const { + return base == rhs.base; + } + + bool operator !=(const timed_iterator<_Ty, container> &rhs) const { + return base != rhs.base; + } + + bool valid() { + return time() < to; + } + + operator bool() { + return valid(); + } + +protected: + typename container_iterator base; + tube_time_t now; + +}; + +}} // openage::tube diff --git a/libopenage/tube/tube_continuous.cpp b/libopenage/tube/tube_continuous.cpp new file mode 100644 index 0000000000..d61b30475a --- /dev/null +++ b/libopenage/tube/tube_continuous.cpp @@ -0,0 +1,8 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#include "tube_continuous.h" + +namespace openage { +namespace tube { + +}} // openage::tube diff --git a/libopenage/tube/tube_continuous.h b/libopenage/tube/tube_continuous.h new file mode 100644 index 0000000000..c73b7fbb6c --- /dev/null +++ b/libopenage/tube/tube_continuous.h @@ -0,0 +1,45 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#pragma once + +#include "value_container.h" + +#include "../log/log.h" + +namespace openage { +namespace tube { + +template +class Continuous : public ValueContainer<_T> { +public: + _T get(const tube_time_t &) const override; +}; + +template +_T Continuous<_T>::get(const tube_time_t &time) const { + auto e = this->container.last(time, this->last_element); + this->last_element = e; + auto nxt = e; + ++nxt; + + double diff_time = 0; + double offset = time - e->time; + // If we do not have a next (buffer underrun!!) we assign values + if (nxt == this->container.end()) { + log::log(WARN << "Continuous buffer underrun. This might be bad! Assuming constant."); + } else { + diff_time = nxt->time - e->time; + } + + if (nxt == this->container.end() // We do not have next - this is bad + || offset == 0 // we do not have an offset - this is performance + || diff_time == 0) { // we do not have diff - this is division-by-zero-error + return e->value; + } else { + // Fraction between time(now) and time(next) that has elapsed + double elapsed_frac = (double)offset / (double)diff_time; + return e->value + (nxt->value - e->value) * elapsed_frac; + } +} + +}} // openage::tube diff --git a/libopenage/tube/tube_discrete.cpp b/libopenage/tube/tube_discrete.cpp new file mode 100644 index 0000000000..20cb14dadc --- /dev/null +++ b/libopenage/tube/tube_discrete.cpp @@ -0,0 +1,8 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#include "tube_discrete.h" + +namespace openage { +namespace tube { + +}} // openage::tube diff --git a/libopenage/tube/tube_discrete.h b/libopenage/tube/tube_discrete.h new file mode 100644 index 0000000000..fda9f420d5 --- /dev/null +++ b/libopenage/tube/tube_discrete.h @@ -0,0 +1,23 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#pragma once + +#include "value_container.h" + +namespace openage { +namespace tube { + +template +class Discrete : public ValueContainer<_T> { +public: + _T get(const tube_time_t &) const override; +}; + +template +_T Discrete<_T>::get(const tube_time_t &time) const { + auto e = this->container.last(time, this->last_element); + this->last_element = e; // TODO if Cacheing? + return e->value; +} + +}} // openage::tube diff --git a/libopenage/tube/tube_event.cpp b/libopenage/tube/tube_event.cpp new file mode 100644 index 0000000000..c6e585d25a --- /dev/null +++ b/libopenage/tube/tube_event.cpp @@ -0,0 +1,8 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#include "tube_event.h" + +namespace openage { +namespace tube { + +}} // openage::tube diff --git a/libopenage/tube/tube_event.h b/libopenage/tube/tube_event.h new file mode 100644 index 0000000000..2be20e6b2a --- /dev/null +++ b/libopenage/tube/tube_event.h @@ -0,0 +1,8 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#pragma once + +namespace openage { +namespace tube { + +}} // openage::tube diff --git a/libopenage/tube/tube_object.cpp b/libopenage/tube/tube_object.cpp new file mode 100644 index 0000000000..46c07a74d3 --- /dev/null +++ b/libopenage/tube/tube_object.cpp @@ -0,0 +1,8 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#include "tube_object.h" + +namespace openage { +namespace tube { + +}} // openage::tube diff --git a/libopenage/tube/tube_object.h b/libopenage/tube/tube_object.h new file mode 100644 index 0000000000..40f5566a72 --- /dev/null +++ b/libopenage/tube/tube_object.h @@ -0,0 +1,14 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#pragma once + +#include "tube.h" + +namespace openage { +namespace tube { + +class TubeObject { +public: +}; + +}} // openage::tube diff --git a/libopenage/tube/tube_objectlist.cpp b/libopenage/tube/tube_objectlist.cpp new file mode 100644 index 0000000000..b7d52005a9 --- /dev/null +++ b/libopenage/tube/tube_objectlist.cpp @@ -0,0 +1,8 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#include "tube_objectlist.h" + +namespace openage { +namespace tube { + +}} // openage::tube diff --git a/libopenage/tube/tube_objectlist.h b/libopenage/tube/tube_objectlist.h new file mode 100644 index 0000000000..2be20e6b2a --- /dev/null +++ b/libopenage/tube/tube_objectlist.h @@ -0,0 +1,8 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#pragma once + +namespace openage { +namespace tube { + +}} // openage::tube diff --git a/libopenage/tube/tube_queue.cpp b/libopenage/tube/tube_queue.cpp new file mode 100644 index 0000000000..80285e5c2f --- /dev/null +++ b/libopenage/tube/tube_queue.cpp @@ -0,0 +1,8 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#include "tube_queue.h" + +namespace openage { +namespace tube { + +}} // openage::tube diff --git a/libopenage/tube/tube_queue.h b/libopenage/tube/tube_queue.h new file mode 100644 index 0000000000..49416eb955 --- /dev/null +++ b/libopenage/tube/tube_queue.h @@ -0,0 +1,127 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#pragma once + + +#include "tube.h" +#include "queue_filter_iterator.h" + +#include +#include + +namespace openage { +namespace tube { + +template +class Queue { + struct queue_wrapper { + tube_time_t _time; + _T value; + + queue_wrapper(const tube_time_t &time, const _T &value) : + _time{time}, + value{value} {} + + tube_time_t time() { + return _time; + } + }; +public: + typedef typename std::deque container_t; + typedef typename container_t::iterator iterator; + // Reading Access + const _T &front(const tube_time_t &) const { + return container.front(); + } + + // Modifying access + TubeQueueFilterIterator<_T, Queue<_T>> begin( + const tube_time_t &t = -std::numeric_limits::infinity()) + { + for (auto it = this->container.begin(); it != this->container.end(); ++it) { + if (it->time() >= t) { + return TubeQueueFilterIterator<_T, Queue<_T>>( + it, + container.end(), + t, + std::numeric_limits::infinity()); + } + } + + return this->end(t); + } + + TubeQueueFilterIterator<_T, Queue<_T>> end( + const tube_time_t &t = std::numeric_limits::infinity()) + { + return TubeQueueFilterIterator<_T, Queue<_T>>(container.end(), + container.end(), + t, + std::numeric_limits::infinity()); + } + + TubeQueueFilterIterator<_T, Queue<_T>> between( + const tube_time_t &begin = std::numeric_limits::infinity(), + const tube_time_t &end = std::numeric_limits::infinity()) + { + auto it = TubeQueueFilterIterator<_T, Queue<_T>>( + container.begin(), + container.end(), + begin, + end); + if (!it.valid()) { + ++it; + } + return it; + } + + TubeQueueFilterIterator<_T, Queue<_T>> erase(const TubeQueueFilterIterator<_T, Queue<_T>> &t) { + auto it = container.erase(t.base); + auto ct = TubeQueueFilterIterator<_T, Queue<_T>>( + it, + container.end(), + t.from, + t.to); + + if (!ct.valid(t.from)) { + ++ct; + } + return ct; + } + + TubeQueueFilterIterator<_T, Queue<_T>> insert(const tube_time_t & time, const _T &e) { + iterator insertion_point = this->container.end(); + for (auto it = this->container.begin(); it != this->container.end(); ++it) { + if (time < it->time()) { + insertion_point = this->container + .insert(it, queue_wrapper(time, e)); + break; + } + } + if (insertion_point == this->container.end()) { + insertion_point = this->container.insert(this->container.end(), + queue_wrapper(time, e)); + } + + auto ct = TubeQueueFilterIterator<_T, Queue<_T>>( + insertion_point, + container.end(), + time, std::numeric_limits::infinity()); + + if (!ct.valid()) { + ++ct; + } + return ct; + } + + void __attribute__((noinline)) dump() { + for (auto i : container) { + std::cout << i.value << " at " << i.time() << std::endl; + } + } + +private: + container_t container; +}; + +}} // openage::tube diff --git a/libopenage/tube/unordered_map.h b/libopenage/tube/unordered_map.h new file mode 100644 index 0000000000..ca53ae0d55 --- /dev/null +++ b/libopenage/tube/unordered_map.h @@ -0,0 +1,214 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#pragma once + +#include + +#include "tube.h" +#include "map_filter_iterator.h" + +namespace openage { +namespace tube { + +template +class UnorderedMap { + struct map_element { + val_t value; + tube_time_t alive; + tube_time_t dead; + map_element (val_t v, const tube_time_t &a, const tube_time_t &d) + : value(v), + alive(a), + dead(d) {} + }; + std::unordered_map container; + +public: + // Using does not work with templates + typedef typename std::unordered_map::iterator iterator; +// TODO return an std::optional here. + std::pair> operator()(const tube_time_t&, const key_t &); +// TODO return an std::optional here. + std::pair> at(const tube_time_t &, const key_t &); + + TubeMapFilterIterator begin(const tube_time_t &e = std::numeric_limits::infinity()); + TubeMapFilterIterator end(const tube_time_t &e = std::numeric_limits::infinity()); + + + TubeMapFilterIterator insert(const key_t &, const val_t &); + TubeMapFilterIterator insert(const tube_time_t &birth, const key_t &, const val_t &); + TubeMapFilterIterator insert(const tube_time_t &birth, const tube_time_t &death, const key_t &key, const val_t &value); + + TubeMapFilterIterator between(const tube_time_t &start, const tube_time_t &to); + + void birth(const tube_time_t &, const key_t &); + void birth(const tube_time_t &, const TubeMapFilterIterator &); + + void kill(const tube_time_t &, const key_t &); + void kill(const tube_time_t &, const TubeMapFilterIterator &); + + bool is_alive(const tube_time_t &, const key_t &); + bool is_alive(const tube_time_t &, const TubeMapFilterIterator &); + + void clean(const tube_time_t &); // remove all dead elements before that point in time + + void __attribute__((noinline)) dump() { + for (auto i : container) { + std::cout << "Element: " << i.second.value << std::endl;; + } + } +}; + +template +std::pair>> +UnorderedMap::operator()(const tube_time_t &time, + const key_t &key) { + return this->at(time, key); +} + +template +std::pair>> +UnorderedMap::at(const tube_time_t & time, + const key_t & key) { + auto e = this->container.find(key); + if (e != this->container.end() && e->second.alive <= time && e->second.dead >time) { + return std::make_pair( + true, + TubeMapFilterIterator>( + e, + this->container.end(), + time, + std::numeric_limits::infinity())); + } else { + return std::make_pair( + false, + this->end(time)); + } +} + +template +TubeMapFilterIterator> +UnorderedMap::begin(const tube_time_t &time) { + return TubeMapFilterIterator>( + this->container.begin(), + this->container.end(), + time, + std::numeric_limits::infinity()); +} + +template +TubeMapFilterIterator> +UnorderedMap::end(const tube_time_t &time) { + return TubeMapFilterIterator>( + this->container.end(), + this->container.end(), + -std::numeric_limits::infinity(), + time); +} + +template +TubeMapFilterIterator> +UnorderedMap::between(const tube_time_t &from, const tube_time_t &to) { + auto it = TubeMapFilterIterator>( + this->container.begin(), + this->container.end(), + from, + to); + + if (!it.valid(from)) { + ++it; + } + return it; +} + +template +TubeMapFilterIterator> +UnorderedMap::insert(const key_t &key, + const val_t &value) { + return this->insert( + existent_from(value), + existent_until(value), + key, + value); +} + +template +TubeMapFilterIterator> +UnorderedMap::insert(const tube_time_t &alive, + const key_t &key, + const val_t &value) { + return this->insert( + alive, + std::numeric_limits::infinity(), + key, + value); +} + +template +TubeMapFilterIterator> +UnorderedMap::insert(const tube_time_t &alive, + const tube_time_t &dead, + const key_t &key, + const val_t &value) { + map_element e(value, alive, dead); + auto it = this->container.insert(std::make_pair(key, e)); + return TubeMapFilterIterator>( + it.first, + this->container.end(), + alive, + dead); +} + +template +void UnorderedMap::birth(const tube_time_t &time, + const key_t &key) { + auto it = this->container.find(key); + if (it != this->container.end()) { + it->second.alive = time; + } +} + +template +void UnorderedMap::birth(const tube_time_t &time, + const TubeMapFilterIterator &it) { + it->second.alive = time; +} + +template +void UnorderedMap::kill(const tube_time_t &time, + const key_t &key) { + auto it = this->container.find(key); + if (it != this->container.end()) { + it->second.dead = time; + } +} + +template +void UnorderedMap::kill(const tube_time_t &time, + const TubeMapFilterIterator &it) { + it->second.dead = time; +} + +template +bool UnorderedMap::is_alive(const tube_time_t &time, + const key_t &key) { + auto it = this->container.find(key); + if (it != this->container.end()) { + return valid_f(it->second.value, time); + } +} + +template +bool UnorderedMap::is_alive(const tube_time_t &time, + const TubeMapFilterIterator &it) { + return valid_f(it->second.value, time); +} + +template +void UnorderedMap::clean(const tube_time_t &) { + // TODO save everything to a file and be happy. +} + +}} // openage::tube diff --git a/libopenage/tube/value_container.cpp b/libopenage/tube/value_container.cpp new file mode 100644 index 0000000000..a973ab73f1 --- /dev/null +++ b/libopenage/tube/value_container.cpp @@ -0,0 +1,8 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#include "value_container.h" + +namespace openage { +namespace tube { + +}} // openage::tube diff --git a/libopenage/tube/value_container.h b/libopenage/tube/value_container.h new file mode 100644 index 0000000000..cd9ba28a0c --- /dev/null +++ b/libopenage/tube/value_container.h @@ -0,0 +1,55 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#pragma once + +#include "keyframe_container.h" + +namespace openage { +namespace tube { + +template +class ValueContainer { +public: + ValueContainer() : + last_element{container.begin()} {} + + virtual _T get(const tube_time_t &t) const=0; + + virtual _T operator ()(const tube_time_t &now) { + return get(now); + } + + virtual bool needs_update(const tube_time_t &at); +public: + // Inserter mode + virtual void set_drop(const tube_time_t &at, const _T &value); + virtual void set_insert(const tube_time_t &at, const _T &value); + +protected: + KeyframeContainer<_T> container; + mutable typename KeyframeContainer<_T>::KeyframeIterator last_element; +}; + +template +void ValueContainer<_T>::set_drop(const tube_time_t &at, const _T &value) { + auto hint = this->container.erase_after(this->container.last(at, this->last_element)); + container.insert(at, value, hint); + last_element = hint; +} + +template +void ValueContainer<_T>::set_insert(const tube_time_t &at, const _T &value) { + this->container.insert(at, value, this->last_element); +} + +template +bool ValueContainer<_T>::needs_update(const tube_time_t &at) { + auto e = this->container.last(at, this->container.end()); + if (e->time > at || ++e == this->container.end() || e->time > at) { + return true; + } else { + return false; + } +} + +}} // openage::tube