diff --git a/doc/code/curves.md b/doc/code/curves.md index ff4bbb248d..e914f8bcfe 100644 --- a/doc/code/curves.md +++ b/doc/code/curves.md @@ -17,6 +17,7 @@ Curves are an integral part of openage's event-based game simulation. 2. [Container](#container) 1. [Queue](#queue) 2. [Unordered Map](#unordered-map) + 3. [Array](#array) ## Motivation @@ -198,7 +199,7 @@ e.g. angles between 0 and 360 degrees. ### Container Container curves are intended for storing changes to collections and containers. -The currently supported containers are `Queue` and `UnorderedMap`. +The currently supported containers are `Queue`, `UnorderedMap` and `Array`. The most important distinction between regular C++ containers and curve containers is that curve containers track the *lifespan* of each element, i.e. their insertion time, @@ -253,3 +254,41 @@ Unordered map curve containers store key-value pairs while additionally keeping track of element insertion time. Requests for a key `k` at time `t` will return the value of `k` at that time. The unordered map can also be iterated over for a specific time `t` which allows access to all key-value pairs that were in the map at time `t`. + + +#### Array + +Array curve containers store a fixed number of `n` elements where `n` is determined at compile-time. +They are the curve equivalent to the `std::array` C++ containers. In comparison to `std::array` each +element in the array curve container is tracked individually over time. Hence, each index is associated +with its own `KeyframeContainer` whose keyframes can be updated independent from other indices. +When a value is added to the `Array` curve at a given index, a new keyframe is added to the respective +`KeyframeContainer` stored at that index. + +**Read** + +Read operations retrieve values for a specific point in time. + +| Method | Description | +| ------------------ | ------------------------------------------------------------------------ | +| `get(t, i)` | Get value of element at index `i` at time <= `t` | +| `get(t)` | Get array of values at time <= `t` | +| `size()` | Get the number of elements in the array | +| `frame(t, i)` | Get the previous keyframe (time and value) at index `i` before or at `t` | +| `next_frame(t, i)` | Get the next keyframe (time and value) at index `i` after `t` | + +**Modify** + +Modify operations insert values for a specific point in time. + +| Method | Description | +| -------------------------- | ------------------------------------------------------------------------------------------ | +| `set_insert(t, i, value)` | Insert a new keyframe(`t`, `value`) at index `i` | +| `set_last(t, i, value)` | Insert a new keyframe(`t`, `value`) at index `i`; delete all keyframes after time `t` | +| `set_replace(t, i, value)` | Insert a new keyframe(`t`, `value`) at index `i`; remove all other keyframes with time `t` | + +**Copy** + +| Method | Description | +| ---------------- | ------------------------------------------------------------------------------------------------ | +| `sync(Curve, t)` | Replace all keyframes from self after time `t` with keyframes from source `Curve` after time `t` | diff --git a/libopenage/curve/CMakeLists.txt b/libopenage/curve/CMakeLists.txt index 623a22a25a..eb52858f43 100644 --- a/libopenage/curve/CMakeLists.txt +++ b/libopenage/curve/CMakeLists.txt @@ -3,16 +3,11 @@ add_sources(libopenage continuous.cpp discrete.cpp discrete_mod.cpp - element_wrapper.cpp interpolated.cpp - iterator.cpp keyframe.cpp keyframe_container.cpp - map.cpp - map_filter_iterator.cpp - queue.cpp - queue_filter_iterator.cpp segmented.cpp ) +add_subdirectory("container") add_subdirectory("tests") diff --git a/libopenage/curve/base_curve.h b/libopenage/curve/base_curve.h index dc58885b98..de5c14201d 100644 --- a/libopenage/curve/base_curve.h +++ b/libopenage/curve/base_curve.h @@ -1,4 +1,4 @@ -// Copyright 2017-2024 the openage authors. See copying.md for legal info. +// Copyright 2017-2025 the openage authors. See copying.md for legal info. #pragma once @@ -47,6 +47,7 @@ class BaseCurve : public event::EventEntity { // registration. If you need to copy a curve, use the sync() method. // TODO: if copying is enabled again, these members have to be reassigned: _id, _idstr, last_element BaseCurve(const BaseCurve &) = delete; + BaseCurve &operator=(const BaseCurve &) = delete; BaseCurve(BaseCurve &&) = default; @@ -245,7 +246,7 @@ template std::pair BaseCurve::frame(const time::time_t &time) const { auto e = this->container.last(time, this->container.size()); auto elem = this->container.get(e); - return std::make_pair(elem.time(), elem.val()); + return elem.as_pair(); } @@ -254,7 +255,7 @@ std::pair BaseCurve::next_frame(const time::time_t &ti auto e = this->container.last(time, this->container.size()); e++; auto elem = this->container.get(e); - return std::make_pair(elem.time(), elem.val()); + return elem.as_pair(); } template diff --git a/libopenage/curve/container/CMakeLists.txt b/libopenage/curve/container/CMakeLists.txt new file mode 100644 index 0000000000..a187e01429 --- /dev/null +++ b/libopenage/curve/container/CMakeLists.txt @@ -0,0 +1,9 @@ +add_sources(libopenage + array.cpp + element_wrapper.cpp + iterator.cpp + map.cpp + map_filter_iterator.cpp + queue.cpp + queue_filter_iterator.cpp +) diff --git a/libopenage/curve/container/array.cpp b/libopenage/curve/container/array.cpp new file mode 100644 index 0000000000..609ee117a3 --- /dev/null +++ b/libopenage/curve/container/array.cpp @@ -0,0 +1,10 @@ +// Copyright 2024-2025 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/container/array.h b/libopenage/curve/container/array.h new file mode 100644 index 0000000000..e980d77915 --- /dev/null +++ b/libopenage/curve/container/array.h @@ -0,0 +1,395 @@ +// Copyright 2024-2025 the openage authors. See copying.md for legal info. + +#pragma once + +#include + +#include "curve/container/iterator.h" +#include "curve/keyframe_container.h" +#include "event/evententity.h" + + +namespace openage { +namespace curve { + +template +constexpr std::array, Size> init_default_vals(const std::array &default_vals) { + std::array, Size> containers; + for (size_t i = 0; i < Size; i++) { + containers[i] = KeyframeContainer(default_vals[i]); + } + return containers; +} + +template +class Array : event::EventEntity { +public: + /// Underlying container type. + using container_t = std::array, Size>; + /// Index type to access elements in the container. + using index_t = typename container_t::size_type; + + /** + * Create a new array curve container. + * + * @param loop Event loop this curve is registered on for notifications. + * @param id Unique identifier for this curve. + * @param idstr Human-readable identifier for this curve. + * @param notifier Function to call when this curve changes. + * @param default_vals Default values for the array elements. + */ + Array(const std::shared_ptr &loop, + size_t id, + const std::string &idstr = "", + const EventEntity::single_change_notifier ¬ifier = nullptr, + const std::array &default_vals = {}) : + EventEntity(loop, notifier), + containers{init_default_vals(default_vals)}, + _id{id}, + _idstr{idstr}, + loop{loop}, + last_elements{} {} + + // prevent copying because it invalidates the usage of unique ids and event + // registration. If you need to copy a curve, use the sync() method. + Array(const Array &) = delete; + Array &operator=(const Array &) = delete; + + Array(Array &&) = default; + + /** + * Get the last element with elem->time <= time. + * + * @param t Time of access. + * @param index Index of the array element. + * + * @return Value of the last element with time <= t. + */ + T at(const time::time_t &t, const index_t index) const; + + /** + * Get all elements at time t. + * + * @param t Time of access. + * + * @return Array of values at time t. + */ + std::array get(const time::time_t &t) const; + + /** + * Get the size of the array. + * + * @return Array size. + */ + consteval size_t size() const; + + /** + * Get the last keyframe value and time with elem->time <= time. + * + * @param t Time of access. + * @param index Index of the array element. + * + * @return Time-value pair of the last keyframe with time <= t. + */ + std::pair frame(const time::time_t &t, const index_t index) const; + + /** + * Get the first keyframe value and time with elem->time > time. + * + * If there is no keyframe with time > t, the behavior is undefined. + * + * @param t Time of access. + * @param index Index of the array element. + * + * @return Time-value pair of the first keyframe with time > t. + */ + std::pair next_frame(const time::time_t &t, const index_t index) const; + + /** + * Insert a new keyframe value at time t. + * + * If there is already a keyframe at time t, the new keyframe is inserted after the existing one. + * + * @param t Time of insertion. + * @param index Index of the array element. + * @param value Keyframe value. + */ + void set_insert(const time::time_t &t, const index_t index, T value); + + /** + * Insert a new keyframe value at time t. Erase all other keyframes with elem->time > t. + * + * @param t Time of insertion. + * @param index Index of the array element. + * @param value Keyframe value. + */ + void set_last(const time::time_t &t, const index_t index, T value); + + /** + * Replace all keyframes at elem->time == t with a new keyframe value. + * + * @param t Time of insertion. + * @param index Index of the array element. + * @param value Keyframe value. + */ + void set_replace(const time::time_t &t, const index_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; + } + + /** + * Array::Iterator is used to iterate over KeyframeContainers contained in a curve at a given time. + */ + class Iterator { + public: + Iterator(Array *curve, const time::time_t &time = time::TIME_MAX, size_t offset = 0) : + curve(curve), time(time), offset(offset) {}; + + /** + * returns a copy of the keyframe at the current offset and time + */ + const T operator*() { + return this->curve->frame(this->time, this->offset).second; + } + + /** + * increments the Iterator to point at the next KeyframeContainer + */ + void operator++() { + this->offset++; + } + + /** + * Compare two Iterators by their offset + */ + bool operator!=(const Array::Iterator &rhs) const { + return this->offset != rhs.offset; + } + + private: + /** + * the curve object that this iterator, iterates over + */ + Array *curve; + + /** + * time at which this iterator is iterating over + */ + time::time_t time; + + /** + * used to index the Curve::Array pointed to by this iterator + */ + size_t offset; + }; + + /** + * iterator pointing to a keyframe of the first KeyframeContainer in the curve at a given time + */ + Iterator begin(const time::time_t &time = time::TIME_MIN); + + /** + * iterator pointing after the last KeyframeContainer in the curve at a given time + */ + Iterator end(const time::time_t &time = time::TIME_MIN); + +private: + /** + * get a copy to the KeyframeContainer at index + */ + KeyframeContainer operator[](index_t index) const { + return this->containers.at(index); + } + + /** + * Containers for each array element. + * + * Each element is managed by a KeyframeContainer. + */ + container_t containers; + + /** + * 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; + + /** + * Cache hints for containers. Stores the index of the last keyframe accessed in each container. + * + * hints is used to speed up the search for keyframes. + * + * mutable as hints are updated by const read-only functions. + */ + mutable std::array::elem_ptr, Size> last_elements = {}; +}; + + +template +std::pair Array::frame(const time::time_t &t, + const index_t index) const { + // find elem_ptr in container to get the last keyframe + auto hint = this->last_elements[index]; + auto frame_index = this->containers.at(index).last(t, hint); + + // update the hint + this->last_elements[index] = frame_index; + + return this->containers.at(index).get(frame_index).as_pair(); +} + +template +std::pair Array::next_frame(const time::time_t &t, + const index_t index) const { + // find elem_ptr in container to get the last keyframe with time <= t + auto hint = this->last_elements[index]; + auto frame_index = this->containers.at(index).last(t, hint); + + // increment the index to get the next keyframe + frame_index++; + + // update the hint + this->last_elements[index] = frame_index; + + return this->containers.at(index).get(frame_index).as_pair(); +} + +template +T Array::at(const time::time_t &t, + const index_t index) const { + // find elem_ptr in container to get the last keyframe with time <= t + auto hint = this->last_elements[index]; + auto e = this->containers.at(index).last(t, hint); + + // update the hint + this->last_elements[index] = e; + + return this->containers.at(index).get(e).val(); +} + +template +std::array Array::get(const time::time_t &t) const { + return [&](std::index_sequence) { + return std::array{this->at(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 index_t index, + T value) { + // find elem_ptr in container to get the last keyframe with time <= t + auto hint = this->last_elements[index]; + auto e = this->containers.at(index).insert_after(Keyframe{t, value}, hint); + + // update the hint + this->last_elements[index] = e; + + this->changes(t); +} + +template +void Array::set_last(const time::time_t &t, + const index_t index, + T value) { + // find elem_ptr in container to get the last keyframe with time <= t + auto hint = this->last_elements[index]; + auto e = this->containers.at(index).last(t, hint); + + // erase max one same-time value + if (this->containers.at(index).get(e).time() == t) { + e--; + } + + // erase all keyframes with time > t + this->containers.at(index).erase_after(e); + + // insert the new keyframe at the end + this->containers.at(index).insert_before(Keyframe{t, value}, e); + + // update the hint + this->last_elements[index] = hint; + + this->changes(t); +} + +template +void Array::set_replace(const time::time_t &t, + const index_t index, + T value) { + // find elem_ptr in container to get the last keyframe with time <= t + auto hint = this->last_elements[index]; + auto e = this->containers.at(index).insert_overwrite(Keyframe{t, value}, hint); + + // update the hint + this->last_elements[index] = e; + + this->changes(t); +} + +template +void Array::sync(const Array &other, + const time::time_t &start) { + for (index_t i = 0; i < Size; i++) { + this->containers[i].sync(other.containers[i], start); + } + + this->changes(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->containers.size()); +} + +} // namespace curve +} // namespace openage diff --git a/libopenage/curve/element_wrapper.cpp b/libopenage/curve/container/element_wrapper.cpp similarity index 68% rename from libopenage/curve/element_wrapper.cpp rename to libopenage/curve/container/element_wrapper.cpp index 5d2eaa08af..bdbca7346f 100644 --- a/libopenage/curve/element_wrapper.cpp +++ b/libopenage/curve/container/element_wrapper.cpp @@ -1,4 +1,4 @@ -// Copyright 2024-2024 the openage authors. See copying.md for legal info. +// Copyright 2024-2025 the openage authors. See copying.md for legal info. #include "element_wrapper.h" diff --git a/libopenage/curve/element_wrapper.h b/libopenage/curve/container/element_wrapper.h similarity index 96% rename from libopenage/curve/element_wrapper.h rename to libopenage/curve/container/element_wrapper.h index 764b61d5cd..0032e4c6fe 100644 --- a/libopenage/curve/element_wrapper.h +++ b/libopenage/curve/container/element_wrapper.h @@ -1,4 +1,4 @@ -// Copyright 2024-2024 the openage authors. See copying.md for legal info. +// Copyright 2024-2025 the openage authors. See copying.md for legal info. #pragma once diff --git a/libopenage/curve/iterator.cpp b/libopenage/curve/container/iterator.cpp similarity index 67% rename from libopenage/curve/iterator.cpp rename to libopenage/curve/container/iterator.cpp index b449d93812..594df3a5b8 100644 --- a/libopenage/curve/iterator.cpp +++ b/libopenage/curve/container/iterator.cpp @@ -1,4 +1,4 @@ -// Copyright 2017-2018 the openage authors. See copying.md for legal info. +// Copyright 2017-2025 the openage authors. See copying.md for legal info. #include "iterator.h" diff --git a/libopenage/curve/iterator.h b/libopenage/curve/container/iterator.h similarity index 97% rename from libopenage/curve/iterator.h rename to libopenage/curve/container/iterator.h index d0346e1b5d..7a4fb82d6b 100644 --- a/libopenage/curve/iterator.h +++ b/libopenage/curve/container/iterator.h @@ -1,4 +1,4 @@ -// Copyright 2017-2024 the openage authors. See copying.md for legal info. +// Copyright 2017-2025 the openage authors. See copying.md for legal info. #pragma once diff --git a/libopenage/curve/map.cpp b/libopenage/curve/container/map.cpp similarity index 66% rename from libopenage/curve/map.cpp rename to libopenage/curve/container/map.cpp index 82b26f9ecc..a2469bfa5f 100644 --- a/libopenage/curve/map.cpp +++ b/libopenage/curve/container/map.cpp @@ -1,4 +1,4 @@ -// Copyright 2017-2018 the openage authors. See copying.md for legal info. +// Copyright 2017-2025 the openage authors. See copying.md for legal info. #include "map.h" diff --git a/libopenage/curve/map.h b/libopenage/curve/container/map.h similarity index 97% rename from libopenage/curve/map.h rename to libopenage/curve/container/map.h index 5a5e0a4d9b..4997824a6d 100644 --- a/libopenage/curve/map.h +++ b/libopenage/curve/container/map.h @@ -1,4 +1,4 @@ -// Copyright 2017-2024 the openage authors. See copying.md for legal info. +// Copyright 2017-2025 the openage authors. See copying.md for legal info. #pragma once @@ -7,8 +7,8 @@ #include #include -#include "curve/map_filter_iterator.h" -#include "curve/element_wrapper.h" +#include "curve/container/element_wrapper.h" +#include "curve/container/map_filter_iterator.h" #include "time/time.h" #include "util/fixed_point.h" @@ -21,7 +21,6 @@ namespace openage::curve { */ template class UnorderedMap { - /** * Data holder. Maps keys to map elements. * Map elements themselves store when they are valid. diff --git a/libopenage/curve/map_filter_iterator.cpp b/libopenage/curve/container/map_filter_iterator.cpp similarity index 68% rename from libopenage/curve/map_filter_iterator.cpp rename to libopenage/curve/container/map_filter_iterator.cpp index da10b01774..bba3ffa478 100644 --- a/libopenage/curve/map_filter_iterator.cpp +++ b/libopenage/curve/container/map_filter_iterator.cpp @@ -1,4 +1,4 @@ -// Copyright 2017-2018 the openage authors. See copying.md for legal info. +// Copyright 2017-2025 the openage authors. See copying.md for legal info. #include "map_filter_iterator.h" diff --git a/libopenage/curve/map_filter_iterator.h b/libopenage/curve/container/map_filter_iterator.h similarity index 93% rename from libopenage/curve/map_filter_iterator.h rename to libopenage/curve/container/map_filter_iterator.h index 3aec2a899e..c9afceee88 100644 --- a/libopenage/curve/map_filter_iterator.h +++ b/libopenage/curve/container/map_filter_iterator.h @@ -1,8 +1,8 @@ -// Copyright 2017-2024 the openage authors. See copying.md for legal info. +// Copyright 2017-2025 the openage authors. See copying.md for legal info. #pragma once -#include "curve/iterator.h" +#include "curve/container/iterator.h" #include "time/time.h" diff --git a/libopenage/curve/queue.cpp b/libopenage/curve/container/queue.cpp similarity index 58% rename from libopenage/curve/queue.cpp rename to libopenage/curve/container/queue.cpp index d994b6c82e..842a140045 100644 --- a/libopenage/curve/queue.cpp +++ b/libopenage/curve/container/queue.cpp @@ -1,4 +1,4 @@ -// Copyright 2017-2018 the openage authors. See copying.md for legal info. +// Copyright 2017-2025 the openage authors. See copying.md for legal info. #include "queue.h" diff --git a/libopenage/curve/queue.h b/libopenage/curve/container/queue.h similarity index 98% rename from libopenage/curve/queue.h rename to libopenage/curve/container/queue.h index 9314dd3a0e..fb32a53cbb 100644 --- a/libopenage/curve/queue.h +++ b/libopenage/curve/container/queue.h @@ -1,4 +1,4 @@ -// Copyright 2017-2024 the openage authors. See copying.md for legal info. +// Copyright 2017-2025 the openage authors. See copying.md for legal info. #pragma once @@ -11,9 +11,9 @@ #include "error/error.h" -#include "curve/iterator.h" -#include "curve/queue_filter_iterator.h" -#include "curve/element_wrapper.h" +#include "curve/container/element_wrapper.h" +#include "curve/container/iterator.h" +#include "curve/container/queue_filter_iterator.h" #include "event/evententity.h" #include "time/time.h" #include "util/fixed_point.h" diff --git a/libopenage/curve/queue_filter_iterator.cpp b/libopenage/curve/container/queue_filter_iterator.cpp similarity index 69% rename from libopenage/curve/queue_filter_iterator.cpp rename to libopenage/curve/container/queue_filter_iterator.cpp index fa0b3ad15a..b4ceb2b7e6 100644 --- a/libopenage/curve/queue_filter_iterator.cpp +++ b/libopenage/curve/container/queue_filter_iterator.cpp @@ -1,4 +1,4 @@ -// Copyright 2017-2018 the openage authors. See copying.md for legal info. +// Copyright 2017-2025 the openage authors. See copying.md for legal info. #include "queue_filter_iterator.h" diff --git a/libopenage/curve/queue_filter_iterator.h b/libopenage/curve/container/queue_filter_iterator.h similarity index 93% rename from libopenage/curve/queue_filter_iterator.h rename to libopenage/curve/container/queue_filter_iterator.h index cf6bc5aa2c..6b2fa471f2 100644 --- a/libopenage/curve/queue_filter_iterator.h +++ b/libopenage/curve/container/queue_filter_iterator.h @@ -1,8 +1,8 @@ -// Copyright 2017-2024 the openage authors. See copying.md for legal info. +// Copyright 2017-2025 the openage authors. See copying.md for legal info. #pragma once -#include "curve/iterator.h" +#include "curve/container/iterator.h" #include "time/time.h" diff --git a/libopenage/curve/discrete.h b/libopenage/curve/discrete.h index 44fe132de6..b9f9b6b00c 100644 --- a/libopenage/curve/discrete.h +++ b/libopenage/curve/discrete.h @@ -1,4 +1,4 @@ -// Copyright 2017-2024 the openage authors. See copying.md for legal info. +// Copyright 2017-2025 the openage authors. See copying.md for legal info. #pragma once @@ -80,7 +80,7 @@ std::pair Discrete::get_time(const time::time_t &time) const this->last_element = e; auto elem = this->container.get(e); - return std::make_pair(elem.time, elem.value); + return elem.as_pair(); } @@ -97,7 +97,7 @@ std::optional> Discrete::get_previous(const time:: e--; auto elem = this->container.get(e); - return std::make_pair(elem.time(), elem.val()); + return elem.as_pair(); } } // namespace openage::curve diff --git a/libopenage/curve/keyframe.h b/libopenage/curve/keyframe.h index e868783cbb..cd2ebf6ced 100644 --- a/libopenage/curve/keyframe.h +++ b/libopenage/curve/keyframe.h @@ -1,4 +1,4 @@ -// Copyright 2019-2024 the openage authors. See copying.md for legal info. +// Copyright 2019-2025 the openage authors. See copying.md for legal info. #pragma once @@ -54,6 +54,15 @@ class Keyframe { return this->value; } + /** + * Get a time-value pair of this keyframe. + * + * @return Keyframe time-value pair. + */ + std::pair as_pair() const { + return {this->timestamp, this->value}; + } + 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/container.cpp b/libopenage/curve/tests/container.cpp index 020e2aad99..16da2476e9 100644 --- a/libopenage/curve/tests/container.cpp +++ b/libopenage/curve/tests/container.cpp @@ -1,4 +1,4 @@ -// Copyright 2017-2024 the openage authors. See copying.md for legal info. +// Copyright 2017-2025 the openage authors. See copying.md for legal info. #include #include @@ -9,11 +9,12 @@ #include #include -#include "curve/iterator.h" -#include "curve/map.h" -#include "curve/map_filter_iterator.h" -#include "curve/queue.h" -#include "curve/queue_filter_iterator.h" +#include "curve/container/array.h" +#include "curve/container/iterator.h" +#include "curve/container/map.h" +#include "curve/container/map_filter_iterator.h" +#include "curve/container/queue.h" +#include "curve/container/queue_filter_iterator.h" #include "event/event_loop.h" #include "testing/testing.h" @@ -56,7 +57,7 @@ void test_map() { // Basic tests test lookup in the middle of the range. { - auto t = map.at(2, 0); //At timestamp 2 element 0 + auto t = map.at(2, 0); // At timestamp 2 element 0 TESTEQUALS(t.has_value(), true); TESTEQUALS(t.value().value(), 0); t = map.at(20, 5); @@ -242,11 +243,115 @@ void test_queue() { TESTEQUALS(q.empty(100001), false); } +void test_array() { + auto loop = std::make_shared(); + + Array a(loop, 0); + a.set_insert(1, 0, 0); + a.set_insert(1, 1, 1); + a.set_insert(1, 2, 2); + a.set_insert(1, 3, 3); + // a = [[0:0, 1:0],[0:0, 1:1],[0:0, 1:2],[0:0, 1:3]] + + // test size + TESTEQUALS(a.size(), 4); + + // extracting array at time t == 1 + auto res = a.get(1); + auto expected = std::array{0, 1, 2, 3}; + TESTEQUALS(res.at(0), expected.at(0)); + TESTEQUALS(res.at(1), expected.at(1)); + TESTEQUALS(res.at(2), expected.at(2)); + TESTEQUALS(res.at(3), expected.at(3)); + TESTEQUALS(res.size(), expected.size()); + + // extracting array at time t == 0 + // array should have default values (== 0) for all keyframes + res = a.get(0); + TESTEQUALS(res.at(0), 0); + TESTEQUALS(res.at(1), 0); + TESTEQUALS(res.at(2), 0); + TESTEQUALS(res.at(3), 0); + + Array other(loop, 0); + other.set_last(0, 0, 999); + other.set_last(0, 1, 999); + other.set_last(0, 2, 999); + other.set_last(0, 3, 999); + + // inserting keyframes at time t == 1 + other.set_insert(1, 0, 4); + other.set_insert(1, 1, 5); + other.set_insert(1, 2, 6); + other.set_insert(1, 3, 7); + // other = [[0:999, 1:4],[0:999, 1:5],[0:999, 1:6],[0:999, 1:7]] + + TESTEQUALS(other.at(0, 0), 999); + TESTEQUALS(other.at(0, 1), 999); + TESTEQUALS(other.at(0, 2), 999); + TESTEQUALS(other.at(0, 3), 999); + TESTEQUALS(other.at(1, 0), 4); + TESTEQUALS(other.at(1, 1), 5); + TESTEQUALS(other.at(1, 2), 6); + TESTEQUALS(other.at(1, 3), 7); + + // sync keyframes from other to a + a.sync(other, 1); + // a = [[0:0, 1:4],[0:0, 1:5],[0:0, 1:6],[0:0, 1:7]] + + res = a.get(0); + TESTEQUALS(res.at(0), 0); + TESTEQUALS(res.at(1), 0); + TESTEQUALS(res.at(2), 0); + TESTEQUALS(res.at(3), 0); + res = a.get(1); + TESTEQUALS(res.at(0), 4); + TESTEQUALS(res.at(1), 5); + TESTEQUALS(res.at(2), 6); + TESTEQUALS(res.at(3), 7); + + // replace keyframes at time t == 2 + a.set_insert(2, 0, 15); + a.set_insert(2, 0, 20); + a.set_replace(2, 0, 25); + TESTEQUALS(a.at(2, 0), 25); + // a = [[0:0, 1:4, 2:25],[0:0, 1:5],[0:0, 1:6],[0:0, 1:7]] + + // set last keyframe at time t == 3 + a.set_insert(3, 0, 30); // a = [[0:0, 1:4, 2:25, 3:30], ... + a.set_insert(4, 0, 40); // a = [[0:0, 1:4, 2:25, 3:30, 4:40], ... + a.set_last(3, 0, 35); // a = [[0:0, 1:4, 2:25, 3:35],... + TESTEQUALS(a.at(4, 0), 35); + // a = [[0:0, 1:4, 2:25, 3:35],[0:0, 1:5],[0:0, 1:6],[0:0, 1:7]] + + // test frame and next_frame + auto frame = a.frame(1, 2); + TESTEQUALS(frame.first, 1); // time + TESTEQUALS(frame.second, 6); // value + + a.set_insert(5, 3, 40); + // a = [[0:0, 1:4, 2:25, 3:35],[0:0, 1:5],[0:0, 1:6],[0:0, 1:7, 5:40]] + auto next_frame = a.next_frame(1, 3); + TESTEQUALS(next_frame.first, 5); // time + TESTEQUALS(next_frame.second, 40); // value + + // Test begin and end + auto it = a.begin(1); + TESTEQUALS(*it, 4); + ++it; + TESTEQUALS(*it, 5); + ++it; + TESTEQUALS(*it, 6); + ++it; + TESTEQUALS(*it, 7); +} + void container() { test_map(); test_list(); test_queue(); + test_array(); } diff --git a/libopenage/curve/tests/curve_types.cpp b/libopenage/curve/tests/curve_types.cpp index 421d3fb4d7..935aa7141d 100644 --- a/libopenage/curve/tests/curve_types.cpp +++ b/libopenage/curve/tests/curve_types.cpp @@ -232,7 +232,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 +257,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 +290,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); diff --git a/libopenage/gamestate/component/api/live.cpp b/libopenage/gamestate/component/api/live.cpp index 01fafde5ea..f7a1f17d98 100644 --- a/libopenage/gamestate/component/api/live.cpp +++ b/libopenage/gamestate/component/api/live.cpp @@ -1,12 +1,12 @@ -// Copyright 2021-2023 the openage authors. See copying.md for legal info. +// Copyright 2021-2025 the openage authors. See copying.md for legal info. #include "live.h" #include +#include "curve/container/iterator.h" +#include "curve/container/map_filter_iterator.h" #include "curve/discrete.h" -#include "curve/iterator.h" -#include "curve/map_filter_iterator.h" #include "gamestate/component/types.h" diff --git a/libopenage/gamestate/component/api/live.h b/libopenage/gamestate/component/api/live.h index 2e1f5e41d5..4916713cdc 100644 --- a/libopenage/gamestate/component/api/live.h +++ b/libopenage/gamestate/component/api/live.h @@ -1,4 +1,4 @@ -// Copyright 2021-2024 the openage authors. See copying.md for legal info. +// Copyright 2021-2025 the openage authors. See copying.md for legal info. #pragma once @@ -7,7 +7,7 @@ #include -#include "curve/map.h" +#include "curve/container/map.h" #include "gamestate/component/api_component.h" #include "gamestate/component/types.h" #include "time/time.h" diff --git a/libopenage/gamestate/component/internal/command_queue.h b/libopenage/gamestate/component/internal/command_queue.h index a7905c4d24..fb3179b470 100644 --- a/libopenage/gamestate/component/internal/command_queue.h +++ b/libopenage/gamestate/component/internal/command_queue.h @@ -1,10 +1,10 @@ -// Copyright 2021-2024 the openage authors. See copying.md for legal info. +// Copyright 2021-2025 the openage authors. See copying.md for legal info. #pragma once #include -#include "curve/queue.h" +#include "curve/container/queue.h" #include "gamestate/component/internal/commands/base_command.h" #include "gamestate/component/internal_component.h" #include "gamestate/component/types.h" diff --git a/libopenage/gamestate/entity_factory.cpp b/libopenage/gamestate/entity_factory.cpp index f2f489206c..6e1a4c825f 100644 --- a/libopenage/gamestate/entity_factory.cpp +++ b/libopenage/gamestate/entity_factory.cpp @@ -1,4 +1,4 @@ -// Copyright 2023-2023 the openage authors. See copying.md for legal info. +// Copyright 2023-2025 the openage authors. See copying.md for legal info. #include "entity_factory.h" @@ -10,8 +10,8 @@ #include "error/error.h" +#include "curve/container/queue.h" #include "curve/discrete.h" -#include "curve/queue.h" #include "event/event_loop.h" #include "gamestate/activity/activity.h" #include "gamestate/activity/condition/command_in_queue.h"