From 0e637b0804c3c687fe0815cac287f6f9c05f9b7c Mon Sep 17 00:00:00 2001 From: Jeremiah Morgan Date: Fri, 13 Dec 2024 16:17:45 +0000 Subject: [PATCH] impliment curve::array --- libopenage/curve/CMakeLists.txt | 1 + libopenage/curve/array.cpp | 10 + libopenage/curve/array.h | 244 +++++++++++++++++++++++++ libopenage/curve/keyframe.h | 9 + libopenage/curve/keyframe_container.h | 2 - libopenage/curve/tests/curve_types.cpp | 87 ++++++++- 6 files changed, 348 insertions(+), 5 deletions(-) create mode 100644 libopenage/curve/array.cpp create mode 100644 libopenage/curve/array.h diff --git a/libopenage/curve/CMakeLists.txt b/libopenage/curve/CMakeLists.txt index 1aa1efb55b..0b800ced83 100644 --- a/libopenage/curve/CMakeLists.txt +++ b/libopenage/curve/CMakeLists.txt @@ -1,4 +1,5 @@ add_sources(libopenage + array.cpp base_curve.cpp continuous.cpp discrete.cpp diff --git a/libopenage/curve/array.cpp b/libopenage/curve/array.cpp new file mode 100644 index 0000000000..97e07033ce --- /dev/null +++ b/libopenage/curve/array.cpp @@ -0,0 +1,10 @@ +// Copyright 2024-2024 the openage authors. See copying.md for legal info. + + +#include "array.h" + +namespace openage::curve { + +// This file is intended to be empty + +} // openage::curve diff --git a/libopenage/curve/array.h b/libopenage/curve/array.h new file mode 100644 index 0000000000..7076082459 --- /dev/null +++ b/libopenage/curve/array.h @@ -0,0 +1,244 @@ +// Copyright 2024-2024 the openage authors. See copying.md for legal info. + +#pragma once + +#include + +#include "curve/keyframe_container.h" +#include "event/evententity.h" + + +// remember to update docs +namespace openage { +namespace curve { + +template +class Array : event::EventEntity { +public: + Array(const std::shared_ptr &loop, + size_t id, + const std::string &idstr = "", + const EventEntity::single_change_notifier ¬ifier = nullptr) : + EventEntity(loop, notifier), _id{id}, _idstr{idstr}, loop{loop}{} + + Array(const Array &) = delete; + + + /** + * Get the last element with elem->time <= time from + * the keyfram container at a given index + */ + T get(const time::time_t &t, const size_t index) const; + + + /** + * Get an array of the last elements with elem->time <= time from + * all keyfram containers contained within this array curve + */ + std::array get_all(const time::time_t &t) const; + + + consteval size_t size() const; + + /** + * Get the last element and associated time which has elem->time <= time from + * the keyfram container at a given index + */ + std::pair frame(const time::time_t &t, const size_t index) const; + + + /** + * Get the first element and associated time which has elem->time >= time from + * the keyfram container at a given index + */ + std::pair next_frame(const time::time_t &t, const size_t index) const; + + + /** + * Insert a new keyframe value at time t at index + */ + void set_insert(const time::time_t &t, const size_t index, T value); + + + /** + * Insert a new keyframe value at time t; delete all keyframes after time t at index + */ + void set_last(const time::time_t &t, const size_t index, T value); + + + /** + * Insert a new keyframe value at time t at index i; remove all other keyframes with time t at index i + */ + void set_replace(const time::time_t &t, const size_t index, T value); + + + /** + * Copy keyframes from another container to this container. + * + * Replaces all keyframes beginning at t >= start with keyframes from \p other. + * + * @param other Curve that keyframes are copied from. + * @param start Start time at which keyframes are replaced (default = -INF). + * Using the default value replaces ALL keyframes of \p this with + * the keyframes of \p other. + */ + void sync(const Array &other, const time::time_t &start); + + + /** + * Get the identifier of this curve. + * + * @return Identifier. + */ + size_t id() const override { + return this->_id; + } + + /** + * Get the human-readable identifier of this curve. + * + * @return Human-readable identifier. + */ + std::string idstr() const override { + if (this->_idstr.size() == 0) { + return std::to_string(this->id()); + } + return this->_idstr; + } + + + KeyframeContainer& operator[] (size_t index) + { + return this->container[index]; + } + + KeyframeContainer operator[] (size_t index) const + { + return this->container[index]; + } + + class Iterator { + public: + Iterator(Array *curve, const time::time_t &time = time::TIME_MAX, size_t offset = 0) : + curve(curve), time(time), offset(offset) {}; + + const T operator*() { + return curve->frame(this->time, this->offset).second; + } + + void operator++() { + this->offset++; + } + + bool operator!=(const Array::Iterator &rhs) const { + return this->offset != rhs.offset; + } + + + private: + size_t offset; + Array *curve; + time::time_t time; + }; + + + Iterator begin(const time::time_t &time = time::TIME_MAX); + + Iterator end(const time::time_t &time = time::TIME_MAX); + + +private: + std::array, Size> container; + + //hint for KeyframeContainer operations + mutable std::array last_element = {}; + + /** + * Identifier for the container + */ + const size_t _id; + + /** + * Human-readable identifier for the container + */ + const std::string _idstr; + + /** + * The eventloop this curve was registered to + */ + const std::shared_ptr loop; +}; + + +template +std::pair Array::frame(const time::time_t &t, const size_t index) const { + size_t frmae_index = container[index].last(t, this->last_element[index]); + this->last_element[index] = frmae_index; + return container[index].get(frmae_index).make_pair(); +} + +template +std::pair Array::next_frame(const time::time_t &t, const size_t index) const { + size_t frmae_index = container[index].last(t, this->last_element[index]); + this->last_element[index] = frmae_index; + return container[index].get(frmae_index + 1).make_pair(); +} + +template +T Array::get(const time::time_t &t, const size_t index) const { + return this->frame(t, index).second; +} + +template +std::array Array::get_all(const time::time_t &t) const { + return [&](std::index_sequence) { + return std::array{this->get(t, I)...}; + }(std::make_index_sequence{}); +} + +template +consteval size_t Array::size() const { + return Size; +} + + +template +void Array::set_insert(const time::time_t &t, const size_t index, T value) { + this->last_element[index] = this->container[index].insert_after(Keyframe(t, value), this->last_element[index]); +} + + +template +void Array::set_last(const time::time_t &t, const size_t index, T value) { + + size_t frame_index = this->container[index].insert_after(Keyframe(t, value), this->last_element[index]); + this->last_element[index] = frame_index; + this->container[index].erase_after(frame_index); +} + + +template +void Array::set_replace(const time::time_t &t, const size_t index, T value) { + this->container[index].insert_overwrite(Keyframe(t, value), this->last_element[index]); +} + +template +void Array::sync(const Array &other, const time::time_t &start) { + for (int i = 0; i < Size; i++) { + this->container[i].sync(other.container[i], start); + } +} + +template +typename Array::Iterator Array::begin(const time::time_t &time) { + return Array::Iterator(this, time); +} + + +template +typename Array::Iterator Array::end(const time::time_t &time) { + return Array::Iterator(this, time, this->container.size()); +} + +} // namespace curve +} // namespace openage diff --git a/libopenage/curve/keyframe.h b/libopenage/curve/keyframe.h index e868783cbb..a34b0c6262 100644 --- a/libopenage/curve/keyframe.h +++ b/libopenage/curve/keyframe.h @@ -54,6 +54,15 @@ class Keyframe { return this->value; } + /** + * Get the value and timestamp of the keyframe in form of std::pair + * @return keyframe pair + */ + std::pair make_pair() const + { + return {time(), val()}; + } + public: /** * Value of the keyframe. diff --git a/libopenage/curve/keyframe_container.h b/libopenage/curve/keyframe_container.h index d9bb9a0bbd..8434942058 100644 --- a/libopenage/curve/keyframe_container.h +++ b/libopenage/curve/keyframe_container.h @@ -279,8 +279,6 @@ class KeyframeContainer { * Replaces all keyframes beginning at t >= start with keyframes from \p other. * * @param other Curve that keyframes are copied from. - * @param converter Function that converts the value type of \p other to the - * value type of \p this. * @param start Start time at which keyframes are replaced (default = -INF). * Using the default value replaces ALL keyframes of \p this with * the keyframes of \p other. diff --git a/libopenage/curve/tests/curve_types.cpp b/libopenage/curve/tests/curve_types.cpp index 421d3fb4d7..6c754e5df9 100644 --- a/libopenage/curve/tests/curve_types.cpp +++ b/libopenage/curve/tests/curve_types.cpp @@ -5,6 +5,7 @@ #include #include +#include "curve/array.h" #include "curve/continuous.h" #include "curve/discrete.h" #include "curve/discrete_mod.h" @@ -232,7 +233,7 @@ void curve_types() { TESTEQUALS(c.get(8), 4); } - //Check the discrete type + // Check the discrete type { auto f = std::make_shared(); Discrete c(f, 0); @@ -257,7 +258,7 @@ void curve_types() { TESTEQUALS(complex.get(10), "Test 10"); } - //Check the discrete mod type + // Check the discrete mod type { auto f = std::make_shared(); DiscreteMod c(f, 0); @@ -290,7 +291,7 @@ void curve_types() { TESTEQUALS(c.get_mod(15, 0), 0); } - //check set_last + // check set_last { auto f = std::make_shared(); Discrete c(f, 0); @@ -386,6 +387,86 @@ void curve_types() { TESTEQUALS(c.get(1), 0); TESTEQUALS(c.get(5), 0); } + + { // array + auto f = std::make_shared(); + + Array a(f,0); + a.set_insert(time::time_t(1), 0, 0); + a.set_insert(time::time_t(1), 1, 1); + a.set_insert(time::time_t(1), 2, 2); + a.set_insert(time::time_t(1), 3, 3); + //a = [[0:0, 1:0],[0:0, 1:1],[0:0, 1:2],[0:0, 1:3]] + + auto res = a.get_all(time::time_t(1)); + TESTEQUALS(res[0], 0); + TESTEQUALS(res[1], 1); + TESTEQUALS(res[2], 2); + TESTEQUALS(res[3], 3); + + Array other(f,0); + other[0].last(999); + other[1].last(999); + other[2].last(999); + other[3].last(999); + other.set_insert(time::time_t(1), 0, 4); + other.set_insert(time::time_t(1), 1, 5); + other.set_insert(time::time_t(1), 2, 6); + other.set_insert(time::time_t(1), 3, 7); + //other = [[0:999, 1:4],[0:999, 1:5],[0:999, 1:6],[0:999, 1:7]] + + + a.sync(other, time::time_t(1)); + //a = [[0:0, 1:4],[0:0, 1:5],[0:0, 1:6],[0:0, 1:7]] + + res = a.get_all(time::time_t(0)); + TESTEQUALS(res[0], 0); + TESTEQUALS(res[1], 0); + TESTEQUALS(res[2], 0); + TESTEQUALS(res[3], 0); + res = a.get_all(time::time_t(1)); + TESTEQUALS(res[0], 4); + TESTEQUALS(res[1], 5); + TESTEQUALS(res[2], 6); + TESTEQUALS(res[3], 7); + + // Additional tests + a.set_insert(time::time_t(2), 0, 15); + a.set_insert(time::time_t(2), 0, 20); + a.set_replace(time::time_t(2), 0, 25); + TESTEQUALS(a.get(time::time_t(2), 0), 25); + // a = [[0:0, 1:4, 2:25],[0:0, 1:5],[0:0, 1:6],[0:0, 1:7]] + + a.set_insert(time::time_t(3), 0, 30); + a.set_insert(time::time_t(4), 0, 40); + a.set_last(time::time_t(3), 0, 35); + TESTEQUALS(a.get(time::time_t(3), 0), 35); + // a = [[0:0, 1:4, 2:25, 3:35],[0:0, 1:5],[0:0, 1:6],[0:0, 1:7]] + + auto frame = a.frame(time::time_t(1), 2); + TESTEQUALS(frame.second, 6); + TESTEQUALS(frame.first, time::time_t(1)); + + a.set_insert(time::time_t(5), 3, 40); + auto next_frame = a.next_frame(time::time_t(1), 3); + TESTEQUALS(next_frame.second, 40); + TESTEQUALS(next_frame.first, time::time_t(5)); + + // Test operator[] + TESTEQUALS(a[0].get(a[0].last(time::time_t(2))).val(), 25); + TESTEQUALS(a[1].get(a[1].last(time::time_t(2))).val(), 5); + + // Test begin and end + auto it = a.begin(time::time_t(1)); + TESTEQUALS(*it, 4); + ++it; + TESTEQUALS(*it, 5); + ++it; + TESTEQUALS(*it, 6); + ++it; + TESTEQUALS(*it, 7); + ++it; + } } } // namespace openage::curve::tests