From efa88c722fcb6c8fea7a86e1b3b8a83f1f59eb31 Mon Sep 17 00:00:00 2001 From: Hop311 Date: Thu, 24 Aug 2023 00:32:23 +0100 Subject: Big Dataloader Commit (openvic-simulation) --- src/openvic/types/Colour.hpp | 27 + src/openvic/types/Date.cpp | 240 +++++++++ src/openvic/types/Date.hpp | 90 ++++ src/openvic/types/IdentifierRegistry.cpp | 37 ++ src/openvic/types/IdentifierRegistry.hpp | 142 ++++++ src/openvic/types/Return.hpp | 8 + src/openvic/types/fixed_point/FP.hpp | 563 +++++++++++++++++++++ src/openvic/types/fixed_point/FPLUT.hpp | 33 ++ src/openvic/types/fixed_point/FPLUT_sin_512.hpp | 58 +++ src/openvic/types/fixed_point/FPMath.hpp | 11 + .../fixed_point/lut_generator/lut_generator.py | 39 ++ 11 files changed, 1248 insertions(+) create mode 100644 src/openvic/types/Colour.hpp create mode 100644 src/openvic/types/Date.cpp create mode 100644 src/openvic/types/Date.hpp create mode 100644 src/openvic/types/IdentifierRegistry.cpp create mode 100644 src/openvic/types/IdentifierRegistry.hpp create mode 100644 src/openvic/types/Return.hpp create mode 100644 src/openvic/types/fixed_point/FP.hpp create mode 100644 src/openvic/types/fixed_point/FPLUT.hpp create mode 100644 src/openvic/types/fixed_point/FPLUT_sin_512.hpp create mode 100644 src/openvic/types/fixed_point/FPMath.hpp create mode 100644 src/openvic/types/fixed_point/lut_generator/lut_generator.py (limited to 'src/openvic/types') diff --git a/src/openvic/types/Colour.hpp b/src/openvic/types/Colour.hpp new file mode 100644 index 0000000..fdac40e --- /dev/null +++ b/src/openvic/types/Colour.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +namespace OpenVic { + // Represents a 24-bit RGB integer OR a 32-bit ARGB integer + using colour_t = uint32_t; + /* When colour_t is used as an identifier, NULL_COLOUR is disallowed + * and should be reserved as an error value. + * When colour_t is used in a purely graphical context, NULL_COLOUR + * should be allowed. + */ + static constexpr colour_t NULL_COLOUR = 0, FULL_COLOUR = 0xFF, MAX_COLOUR_RGB = 0xFFFFFF; + constexpr colour_t float_to_colour_byte(float f, float min = 0.0f, float max = 1.0f) { + return static_cast(std::clamp(min + f * (max - min), min, max) * 255.0f); + } + constexpr colour_t fraction_to_colour_byte(int n, int d, float min = 0.0f, float max = 1.0f) { + return float_to_colour_byte(static_cast(n) / static_cast(d), min, max); + } + constexpr colour_t float_to_alpha_value(float a) { + return float_to_colour_byte(a) << 24; + } + constexpr float colour_byte_to_float(colour_t colour) { + return std::clamp(static_cast(colour) / 255.0f, 0.0f, 1.0f); + } +} diff --git a/src/openvic/types/Date.cpp b/src/openvic/types/Date.cpp new file mode 100644 index 0000000..b6e1367 --- /dev/null +++ b/src/openvic/types/Date.cpp @@ -0,0 +1,240 @@ +#include "Date.hpp" + +#include +#include +#include +#include + +#include "openvic/utility/Logger.hpp" + +using namespace OpenVic; + +Timespan::Timespan(day_t value) : days { value } {} + +bool Timespan::operator<(Timespan other) const { return days < other.days; }; +bool Timespan::operator>(Timespan other) const { return days > other.days; }; +bool Timespan::operator<=(Timespan other) const { return days <= other.days; }; +bool Timespan::operator>=(Timespan other) const { return days >= other.days; }; +bool Timespan::operator==(Timespan other) const { return days == other.days; }; +bool Timespan::operator!=(Timespan other) const { return days != other.days; }; + +Timespan Timespan::operator+(Timespan other) const { return days + other.days; } + +Timespan Timespan::operator-(Timespan other) const { return days - other.days; } + +Timespan Timespan::operator*(day_t factor) const { return days * factor; } + +Timespan Timespan::operator/(day_t factor) const { return days / factor; } + +Timespan& Timespan::operator+=(Timespan other) { + days += other.days; + return *this; +} + +Timespan& Timespan::operator-=(Timespan other) { + days -= other.days; + return *this; +} + +Timespan& Timespan::operator++() { + days++; + return *this; +} + +Timespan Timespan::operator++(int) { + Timespan old = *this; + ++(*this); + return old; +} + +Timespan::operator day_t() const { + return days; +} + +Timespan::operator double() const { + return days; +} + +Timespan::operator std::string() const { + return std::to_string(days); +} + +std::ostream& OpenVic::operator<<(std::ostream& out, Timespan const& timespan) { + return out << static_cast(timespan); +} + +Timespan Date::_dateToTimespan(year_t year, month_t month, day_t day) { + month = std::clamp(month, 1, MONTHS_IN_YEAR); + day = std::clamp(day, 1, DAYS_IN_MONTH[month - 1]); + return year * DAYS_IN_YEAR + DAYS_UP_TO_MONTH[month - 1] + day - 1; +} + +Timespan::day_t const* Date::DAYS_UP_TO_MONTH = generate_days_up_to_month(); + +Timespan::day_t const* Date::generate_days_up_to_month() { + static Timespan::day_t days_up_to_month[MONTHS_IN_YEAR]; + Timespan::day_t days = 0; + for (int month = 0; month < MONTHS_IN_YEAR; + days_up_to_month[month] = days, days += DAYS_IN_MONTH[month++]); + assert(days == DAYS_IN_YEAR); + return days_up_to_month; +} + +Date::month_t const* Date::MONTH_FROM_DAY_IN_YEAR = generate_month_from_day_in_year(); + +Date::month_t const* Date::generate_month_from_day_in_year() { + static month_t month_from_day_in_year[DAYS_IN_YEAR]; + Timespan::day_t days_left = 0; + for (int day = 0, month = 0; day < DAYS_IN_YEAR; + days_left = (days_left > 0 ? days_left : DAYS_IN_MONTH[month++]) - 1, + month_from_day_in_year[day++] = month); + assert(days_left == 0); + assert(month_from_day_in_year[DAYS_IN_YEAR - 1] == MONTHS_IN_YEAR); + return month_from_day_in_year; +} + +Date::Date(Timespan total_days) : timespan { total_days } { + if (timespan < 0) { + Logger::error("Invalid timespan for date: ", timespan, " (cannot be negative)"); + timespan = 0; + } +} + +Date::Date(year_t year, month_t month, day_t day) : timespan { _dateToTimespan(year, month, day) } {} + +Date::year_t Date::getYear() const { + return static_cast(timespan) / DAYS_IN_YEAR; +} + +Date::month_t Date::getMonth() const { + return MONTH_FROM_DAY_IN_YEAR[static_cast(timespan) % DAYS_IN_YEAR]; +} + +Date::day_t Date::getDay() const { + return (static_cast(timespan) % DAYS_IN_YEAR) - DAYS_UP_TO_MONTH[getMonth() - 1] + 1; +} + +bool Date::operator<(Date other) const { return timespan < other.timespan; }; +bool Date::operator>(Date other) const { return timespan > other.timespan; }; +bool Date::operator<=(Date other) const { return timespan <= other.timespan; }; +bool Date::operator>=(Date other) const { return timespan >= other.timespan; }; +bool Date::operator==(Date other) const { return timespan == other.timespan; }; +bool Date::operator!=(Date other) const { return timespan != other.timespan; }; + +Date Date::operator+(Timespan other) const { return timespan + other; } + +Timespan Date::operator-(Date other) const { return timespan - other.timespan; } + +Date& Date::operator+=(Timespan other) { + timespan += other; + return *this; +} + +Date& Date::operator-=(Timespan other) { + timespan -= other; + return *this; +} + +Date& Date::operator++() { + timespan++; + return *this; +} + +Date Date::operator++(int) { + Date old = *this; + ++(*this); + return old; +} + +Date::operator std::string() const { + std::stringstream ss; + ss << *this; + return ss.str(); +} + +std::ostream& OpenVic::operator<<(std::ostream& out, Date const& date) { + return out << static_cast(date.getYear()) << '.' << static_cast(date.getMonth()) << '.' << static_cast(date.getDay()); +} + +// Parsed from string of the form YYYY.MM.DD +Date Date::from_string(const std::string_view date, bool* successful) { + if (successful != nullptr) *successful = true; + size_t first_pos = 0; + while (first_pos < date.length() && std::isdigit(date[first_pos])) { + first_pos++; + } + + if (first_pos == 0) { + Logger::error("Failed to find year digits in date: ", date); + if (successful != nullptr) *successful = false; + return {}; + } + + int val = 0; + char const* start = date.data(); + char const* end = start + first_pos; + std::from_chars_result result = std::from_chars(start, end, val); + if (result.ec != std::errc{} || result.ptr != end || val < 0 || val >= 1 << (8 * sizeof(year_t))) { + Logger::error("Failed to read year: ", date); + if (successful != nullptr) *successful = false; + return {}; + } + year_t year = val; + month_t month = 1; + day_t day = 1; + if (first_pos < date.length()) { + if (date[first_pos] == '.') { + size_t second_pos = ++first_pos; + while (second_pos < date.length() && std::isdigit(date[second_pos])) { + second_pos++; + } + if (first_pos == second_pos) { + Logger::error("Failed to find month digits in date: ", date); + if (successful != nullptr) *successful = false; + } else { + start = date.data() + first_pos; + end = date.data() + second_pos; + result = std::from_chars(start, end, val); + if (result.ec != std::errc{} || result.ptr != end || val < 1 || val > MONTHS_IN_YEAR) { + Logger::error("Failed to read month: ", date); + if (successful != nullptr) *successful = false; + } else { + month = val; + if (second_pos < date.length()) { + if (date[second_pos] == '.') { + size_t third_pos = ++second_pos; + while (third_pos < date.length() && std::isdigit(date[third_pos])) { + third_pos++; + } + if (second_pos == third_pos) { + Logger::error("Failed to find day digits in date: ", date); + if (successful != nullptr) *successful = false; + } else { + start = date.data() + second_pos; + end = date.data() + third_pos; + result = std::from_chars(start, end, val); + if (result.ec != std::errc{} || result.ptr != end || val < 1 || val > DAYS_IN_MONTH[month - 1]) { + Logger::error("Failed to read day: ", date); + if (successful != nullptr) *successful = false; + } else { + day = val; + if (third_pos < date.length()) { + Logger::error("Unexpected string \"", date.substr(third_pos), "\" at the end of date ", date); + if (successful != nullptr) *successful = false; + } + } + } + } else { + Logger::error("Unexpected character \"", date[second_pos], "\" in month of date ", date); + if (successful != nullptr) *successful = false; + } + } + } + } + } else { + Logger::error("Unexpected character \"", date[first_pos], "\" in year of date ", date); + if (successful != nullptr) *successful = false; + } + } + return _dateToTimespan(year, month, day); +}; diff --git a/src/openvic/types/Date.hpp b/src/openvic/types/Date.hpp new file mode 100644 index 0000000..ca5645c --- /dev/null +++ b/src/openvic/types/Date.hpp @@ -0,0 +1,90 @@ +#pragma once + +#include +#include +#include + +namespace OpenVic { + // A relative period between points in time, measured in days + struct Timespan { + using day_t = int64_t; + + private: + day_t days; + + public: + Timespan(day_t value = 0); + + bool operator<(Timespan other) const; + bool operator>(Timespan other) const; + bool operator<=(Timespan other) const; + bool operator>=(Timespan other) const; + bool operator==(Timespan other) const; + bool operator!=(Timespan other) const; + + Timespan operator+(Timespan other) const; + Timespan operator-(Timespan other) const; + Timespan operator*(day_t factor) const; + Timespan operator/(day_t factor) const; + Timespan& operator+=(Timespan other); + Timespan& operator-=(Timespan other); + Timespan& operator++(); + Timespan operator++(int); + + explicit operator day_t() const; + explicit operator double() const; + explicit operator std::string() const; + }; + std::ostream& operator<<(std::ostream& out, Timespan const& timespan); + + // Represents an in-game date + // Note: Current implementation does not account for leap-years, or dates before Year 0 + struct Date { + using year_t = uint16_t; + using month_t = uint8_t; + using day_t = uint8_t; + + static constexpr Timespan::day_t MONTHS_IN_YEAR = 12; + static constexpr Timespan::day_t DAYS_IN_YEAR = 365; + static constexpr Timespan::day_t DAYS_IN_MONTH[MONTHS_IN_YEAR] { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + static Timespan::day_t const* DAYS_UP_TO_MONTH; + static month_t const* MONTH_FROM_DAY_IN_YEAR; + + private: + // Number of days since Jan 1st, Year 0 + Timespan timespan; + + static Timespan _dateToTimespan(year_t year, month_t month, day_t day); + static Timespan::day_t const* generate_days_up_to_month(); + static month_t const* generate_month_from_day_in_year(); + + public: + // The Timespan is considered to be the number of days since Jan 1st, Year 0 + Date(Timespan total_days); + // Year month day specification + Date(year_t year = 0, month_t month = 1, day_t day = 1); + + year_t getYear() const; + month_t getMonth() const; + day_t getDay() const; + + bool operator<(Date other) const; + bool operator>(Date other) const; + bool operator<=(Date other) const; + bool operator>=(Date other) const; + bool operator==(Date other) const; + bool operator!=(Date other) const; + + Date operator+(Timespan other) const; + Timespan operator-(Date other) const; + Date& operator+=(Timespan other); + Date& operator-=(Timespan other); + Date& operator++(); + Date operator++(int); + + explicit operator std::string() const; + // Parsed from string of the form YYYY.MM.DD + static Date from_string(const std::string_view date, bool* successful = nullptr); + }; + std::ostream& operator<<(std::ostream& out, Date const& date); +} diff --git a/src/openvic/types/IdentifierRegistry.cpp b/src/openvic/types/IdentifierRegistry.cpp new file mode 100644 index 0000000..97a1ff5 --- /dev/null +++ b/src/openvic/types/IdentifierRegistry.cpp @@ -0,0 +1,37 @@ +#include "IdentifierRegistry.hpp" + +#include +#include +#include + +using namespace OpenVic; + +HasIdentifier::HasIdentifier(const std::string_view new_identifier) + : identifier { new_identifier } { + assert(!identifier.empty()); +} + +std::string const& HasIdentifier::get_identifier() const { + return identifier; +} + +HasColour::HasColour(colour_t const new_colour, bool can_be_null) : colour(new_colour) { + assert((can_be_null || colour != NULL_COLOUR) && colour <= MAX_COLOUR_RGB); +} + +colour_t HasColour::get_colour() const { return colour; } + +std::string HasColour::colour_to_hex_string(colour_t const colour) { + std::ostringstream stream; + stream << std::hex << std::setfill('0') << std::setw(6) << colour; + return stream.str(); +} + +std::string HasColour::colour_to_hex_string() const { + return colour_to_hex_string(colour); +} + +HasIdentifierAndColour::HasIdentifierAndColour(const std::string_view new_identifier, + const colour_t new_colour, bool can_be_null) + : HasIdentifier { new_identifier }, + HasColour { new_colour, can_be_null } {} diff --git a/src/openvic/types/IdentifierRegistry.hpp b/src/openvic/types/IdentifierRegistry.hpp new file mode 100644 index 0000000..e5ac94b --- /dev/null +++ b/src/openvic/types/IdentifierRegistry.hpp @@ -0,0 +1,142 @@ +#pragma once + +#include +#include +#include + +#include "openvic/types/Colour.hpp" +#include "openvic/types/Return.hpp" +#include "openvic/utility/Logger.hpp" + +namespace OpenVic { + /* + * Base class for objects with a non-empty string identifier, + * uniquely named instances of which can be entered into an + * IdentifierRegistry instance. + */ + class HasIdentifier { + const std::string identifier; + + protected: + HasIdentifier(const std::string_view new_identifier); + + public: + HasIdentifier(HasIdentifier const&) = delete; + HasIdentifier(HasIdentifier&&) = default; + HasIdentifier& operator=(HasIdentifier const&) = delete; + HasIdentifier& operator=(HasIdentifier&&) = delete; + + std::string const& get_identifier() const; + }; + + /* + * Base class for objects with associated colour information. + */ + class HasColour { + const colour_t colour; + + protected: + HasColour(const colour_t new_colour, bool can_be_null); + + public: + HasColour(HasColour const&) = delete; + HasColour(HasColour&&) = default; + HasColour& operator=(HasColour const&) = delete; + HasColour& operator=(HasColour&&) = delete; + + colour_t get_colour() const; + std::string colour_to_hex_string() const; + static std::string colour_to_hex_string(colour_t const colour); + }; + + /* + * Base class for objects with a unique string identifier + * and associated colour information. + */ + class HasIdentifierAndColour : public HasIdentifier, public HasColour { + protected: + HasIdentifierAndColour(const std::string_view new_identifier, const colour_t new_colour, bool can_be_null); + + public: + HasIdentifierAndColour(HasIdentifierAndColour const&) = delete; + HasIdentifierAndColour(HasIdentifierAndColour&&) = default; + HasIdentifierAndColour& operator=(HasIdentifierAndColour const&) = delete; + HasIdentifierAndColour& operator=(HasIdentifierAndColour&&) = delete; + }; + + using distribution_t = std::unordered_map; + + /* + * Template for a list of objects with unique string identifiers that can + * be locked to prevent any further additions. The template argument T is + * the type of object that the registry will store, and the second part ensures + * that HasIdentifier is a base class of T. + */ + template::value>::type* = nullptr> + class IdentifierRegistry { + using identifier_index_map_t = std::map>; + + const std::string name; + std::vector items; + bool locked = false; + identifier_index_map_t identifier_index_map; + + public: + IdentifierRegistry(const std::string_view new_name) : name { new_name } {} + return_t add_item(T&& item) { + if (locked) { + Logger::error("Cannot add item to the ", name, " registry - locked!"); + return FAILURE; + } + T const* old_item = get_item_by_identifier(item.get_identifier()); + if (old_item != nullptr) { + Logger::error("Cannot add item to the ", name, " registry - an item with the identifier \"", item.get_identifier(), "\" already exists!"); + return FAILURE; + } + identifier_index_map[item.get_identifier()] = items.size(); + items.push_back(std::move(item)); + return SUCCESS; + } + void lock(bool log = true) { + if (locked) { + Logger::error("Failed to lock ", name, " registry - already locked!"); + } else { + locked = true; + if (log) Logger::info("Locked ", name, " registry after registering ", get_item_count(), " items"); + } + } + bool is_locked() const { + return locked; + } + void reset() { + identifier_index_map.clear(); + items.clear(); + locked = false; + } + size_t get_item_count() const { + return items.size(); + } + T* get_item_by_identifier(const std::string_view identifier) { + const identifier_index_map_t::const_iterator it = identifier_index_map.find(identifier); + if (it != identifier_index_map.end()) return &items[it->second]; + return nullptr; + } + T const* get_item_by_identifier(const std::string_view identifier) const { + const identifier_index_map_t::const_iterator it = identifier_index_map.find(identifier); + if (it != identifier_index_map.end()) return &items[it->second]; + return nullptr; + } + T* get_item_by_index(size_t index) { + return index < items.size() ? &items[index] : nullptr; + } + T const* get_item_by_index(size_t index) const { + return index < items.size() ? &items[index] : nullptr; + } + std::vector& get_items() { + return items; + } + std::vector const& get_items() const { + return items; + } + }; +} diff --git a/src/openvic/types/Return.hpp b/src/openvic/types/Return.hpp new file mode 100644 index 0000000..04c3734 --- /dev/null +++ b/src/openvic/types/Return.hpp @@ -0,0 +1,8 @@ +#pragma once + +namespace OpenVic { + using return_t = bool; + + // This mirrors godot::Error, where `OK = 0` and `FAILED = 1`. + constexpr return_t SUCCESS = false, FAILURE = true; +} diff --git a/src/openvic/types/fixed_point/FP.hpp b/src/openvic/types/fixed_point/FP.hpp new file mode 100644 index 0000000..8b32906 --- /dev/null +++ b/src/openvic/types/fixed_point/FP.hpp @@ -0,0 +1,563 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "FPLUT.hpp" +#include "openvic/utility/FloatUtils.hpp" +#include "openvic/utility/StringUtils.hpp" +#include "openvic/utility/Logger.hpp" + +namespace OpenVic { + struct FP { + static constexpr size_t SIZE = 8; + + constexpr FP() : value { 0 } {} + constexpr FP(int64_t new_value) : value { new_value } {} + constexpr FP(int32_t new_value) : value { static_cast(new_value) << FPLUT::PRECISION } {} + + // Trivial destructor + ~FP() = default; + + static constexpr FP max() { + return std::numeric_limits::max(); + } + + static constexpr FP min() { + return std::numeric_limits::min(); + } + + static constexpr FP usable_max() { + return static_cast(2147483648LL); + } + + static constexpr FP usable_min() { + return -usable_max(); + } + + static constexpr FP _0() { + return 0; + } + + static constexpr FP _1() { + return 1; + } + + static constexpr FP _2() { + return 2; + } + + static constexpr FP _3() { + return 3; + } + + static constexpr FP _4() { + return 4; + } + + static constexpr FP _5() { + return 5; + } + + static constexpr FP _6() { + return 6; + } + + static constexpr FP _7() { + return 7; + } + + static constexpr FP _8() { + return 8; + } + + static constexpr FP _9() { + return 9; + } + + static constexpr FP _10() { + return 10; + } + + static constexpr FP _50() { + return 50; + } + + static constexpr FP _100() { + return 100; + } + + static constexpr FP _200() { + return 200; + } + + static constexpr FP _0_01() { + return _1() / _100(); + } + + static constexpr FP _0_02() { + return _0_01() * 2; + } + + static constexpr FP _0_03() { + return _0_01() * 3; + } + + static constexpr FP _0_04() { + return _0_01() * 4; + } + + static constexpr FP _0_05() { + return _0_01() * 5; + } + + static constexpr FP _0_10() { + return _1() / 10; + } + + static constexpr FP _0_20() { + return _0_10() * 2; + } + + static constexpr FP _0_25() { + return _1() / 4; + } + + static constexpr FP _0_33() { + return _1() / 3; + } + + static constexpr FP _0_50() { + return _1() / 2; + } + + static constexpr FP _0_75() { + return _1() - _0_25(); + } + + static constexpr FP _0_95() { + return _1() - _0_05(); + } + + static constexpr FP _0_99() { + return _1() - _0_01(); + } + + static constexpr FP _1_01() { + return _1() + _0_01(); + } + + static constexpr FP _1_10() { + return _1() + _0_10(); + } + + static constexpr FP _1_50() { + return _1() + _0_50(); + } + + static constexpr FP minus_one() { + return -1; + } + + static constexpr FP pi() { + return static_cast(205887LL); + } + + static constexpr FP pi2() { + return pi() * 2; + } + + static constexpr FP pi_quarter() { + return pi() / 4; + } + + static constexpr FP pi_half() { + return pi() / 2; + } + + static constexpr FP one_div_pi2() { + return 1 / pi2(); + } + + static constexpr FP deg2rad() { + return static_cast(1143LL); + } + + static constexpr FP rad2deg() { + return static_cast(3754936LL); + } + + static constexpr FP e() { + return static_cast(178145LL); + } + + constexpr FP abs() const { + return value >= 0 ? value : -value; + } + + // Not including sign, so -1.1 -> 0.1 + constexpr FP get_frac() const { + return abs().value & ((1 << FPLUT::PRECISION) - 1); + } + + constexpr int64_t to_int64_t() const { + return value >> FPLUT::PRECISION; + } + + constexpr void set_raw_value(int64_t value) { + this->value = value; + } + + constexpr int64_t get_raw_value() const { + return value; + } + + constexpr int32_t to_int32_t() const { + return static_cast(value >> FPLUT::PRECISION); + } + + constexpr float to_float() const { + return value / 65536.0F; + } + + constexpr float to_float_rounded() const { + return static_cast(FloatUtils::round_to_int64((value / 65536.0F) * 100000.0F)) / 100000.0F; + } + + constexpr float to_double() const { + return value / 65536.0; + } + + constexpr float to_double_rounded() const { + return FloatUtils::round_to_int64((value / 65536.0) * 100000.0) / 100000.0; + } + + std::string to_string() const { + std::string str = std::to_string(to_int64_t()) + "."; + FP frac_part = get_frac(); + do { + frac_part.value *= 10; + str += std::to_string(frac_part.to_int64_t()); + frac_part = frac_part.get_frac(); + } while (frac_part > 0); + return str; + } + + // Deterministic + static constexpr FP parse_raw(int64_t value) { + return value; + } + + // Deterministic + static constexpr FP parse(int64_t value) { + return value << FPLUT::PRECISION; + } + + // Deterministic + static constexpr FP parse(const char* str, const char* end, bool *successful = nullptr) { + if (successful != nullptr) *successful = false; + + if (str == nullptr || str >= end) { + return _0(); + } + + bool negative = false; + + if (*str == '-') { + negative = true; + ++str; + if (str == end) return _0(); + } + + const char* dot_pointer = str; + while (*dot_pointer != '.' && ++dot_pointer != end); + + if (dot_pointer == str && dot_pointer + 1 == end) { + // Invalid: ".", "+." or "-." + return _0(); + } + + FP result = _0(); + if (successful != nullptr) *successful = true; + + if (dot_pointer != str) { + // Non-empty integer part + bool int_successful = false; + result += parse_integer(str, dot_pointer, &int_successful); + if (!int_successful && successful != nullptr) *successful = false; + } + + if (dot_pointer + 1 < end) { + // Non-empty fractional part + bool frac_successful = false; + result += parse_fraction(dot_pointer + 1, end, &frac_successful); + if (!frac_successful && successful != nullptr) *successful = false; + } + + return negative ? -result : result; + } + + static constexpr FP parse(const char* str, size_t length, bool *successful = nullptr) { + return parse(str, str + length, successful); + } + + // Not Deterministic + static constexpr FP parse_unsafe(float value) { + return static_cast(value * FPLUT::ONE + 0.5f * (value < 0 ? -1 : 1)); + } + + // Not Deterministic + static FP parse_unsafe(const char* value) { + char* endpointer; + double double_value = std::strtod(value, &endpointer); + + if (*endpointer != '\0') { + Logger::error("Unsafe fixed point parse failed to parse the end of a string: \"", endpointer, "\""); + } + + int64_t integer_value = static_cast(double_value * FPLUT::ONE + 0.5 * (double_value < 0 ? -1 : 1)); + + return integer_value; + } + + constexpr operator int32_t() const { + return to_int32_t(); + } + + constexpr operator int64_t() const { + return to_int64_t(); + } + + constexpr operator float() const { + return to_float(); + } + + constexpr operator double() const { + return to_double(); + } + + operator std::string() const { + return to_string(); + } + + friend std::ostream& operator<<(std::ostream& stream, const FP& obj) { + return stream << obj.to_string(); + } + + constexpr friend FP operator-(const FP& obj) { + return -obj.value; + } + + constexpr friend FP operator+(const FP& obj) { + return +obj.value; + } + + constexpr friend FP operator+(const FP& lhs, const FP& rhs) { + return lhs.value + rhs.value; + } + + constexpr friend FP operator+(const FP& lhs, const int32_t& rhs) { + return lhs.value + (static_cast(rhs) << FPLUT::PRECISION); + } + + constexpr friend FP operator+(const int32_t& lhs, const FP& rhs) { + return (static_cast(lhs) << FPLUT::PRECISION) + rhs.value; + } + + constexpr FP operator+=(const FP& obj) { + value += obj.value; + return *this; + } + + constexpr FP operator+=(const int32_t& obj) { + value += (static_cast(obj) << FPLUT::PRECISION); + return *this; + } + + constexpr friend FP operator-(const FP& lhs, const FP& rhs) { + return lhs.value - rhs.value; + } + + constexpr friend FP operator-(const FP& lhs, const int32_t& rhs) { + return lhs.value - (static_cast(rhs) << FPLUT::PRECISION); + } + + constexpr friend FP operator-(const int32_t& lhs, const FP& rhs) { + return (static_cast(lhs) << FPLUT::PRECISION) - rhs.value; + } + + constexpr FP operator-=(const FP& obj) { + value -= obj.value; + return *this; + } + + constexpr FP operator-=(const int32_t& obj) { + value -= (static_cast(obj) << FPLUT::PRECISION); + return *this; + } + + constexpr friend FP operator*(const FP& lhs, const FP& rhs) { + return lhs.value * rhs.value >> FPLUT::PRECISION; + } + + constexpr friend FP operator*(const FP& lhs, const int32_t& rhs) { + return lhs.value * rhs; + } + + constexpr friend FP operator*(const int32_t& lhs, const FP& rhs) { + return lhs * rhs.value; + } + + constexpr FP operator*=(const FP& obj) { + value *= obj.value >> FPLUT::PRECISION; + return *this; + } + + constexpr FP operator*=(const int32_t& obj) { + value *= obj; + return *this; + } + + constexpr friend FP operator/(const FP& lhs, const FP& rhs) { + return (lhs.value << FPLUT::PRECISION) / rhs.value; + } + + constexpr friend FP operator/(const FP& lhs, const int32_t& rhs) { + return lhs.value / rhs; + } + + constexpr friend FP operator/(const int32_t& lhs, const FP& rhs) { + return (static_cast(lhs) << (2 / FPLUT::PRECISION)) / rhs.value; + } + + constexpr FP operator/=(const FP& obj) { + value = (value << FPLUT::PRECISION) / obj.value; + return *this; + } + + constexpr FP operator/=(const int32_t& obj) { + value /= obj; + return *this; + } + + constexpr friend FP operator%(const FP& lhs, const FP& rhs) { + return lhs.value % rhs.value; + } + + constexpr friend FP operator%(const FP& lhs, const int32_t& rhs) { + return lhs.value % (static_cast(rhs) << FPLUT::PRECISION); + } + + constexpr friend FP operator%(const int32_t& lhs, const FP& rhs) { + return (static_cast(lhs) << FPLUT::PRECISION) % rhs.value; + } + + constexpr FP operator%=(const FP& obj) { + value %= obj.value; + return *this; + } + + constexpr FP operator%=(const int32_t& obj) { + value %= (static_cast(obj) << FPLUT::PRECISION); + return *this; + } + + constexpr friend bool operator<(const FP& lhs, const FP& rhs) { + return lhs.value < rhs.value; + } + + constexpr friend bool operator<(const FP& lhs, const int32_t& rhs) { + return lhs.value < static_cast(rhs) << FPLUT::PRECISION; + } + + constexpr friend bool operator<(const int32_t& lhs, const FP& rhs) { + return static_cast(lhs) << FPLUT::PRECISION < rhs.value; + } + + constexpr friend bool operator<=(const FP& lhs, const FP& rhs) { + return lhs.value <= rhs.value; + } + + constexpr friend bool operator<=(const FP& lhs, const int32_t& rhs) { + return lhs.value <= static_cast(rhs) << FPLUT::PRECISION; + } + + constexpr friend bool operator<=(const int32_t& lhs, const FP& rhs) { + return static_cast(lhs) << FPLUT::PRECISION <= rhs.value; + } + + constexpr friend bool operator>(const FP& lhs, const FP& rhs) { + return lhs.value > rhs.value; + } + + constexpr friend bool operator>(const FP& lhs, const int32_t& rhs) { + return lhs.value > static_cast(rhs) << FPLUT::PRECISION; + } + + constexpr friend bool operator>(const int32_t& lhs, const FP& rhs) { + return static_cast(lhs) << FPLUT::PRECISION > rhs.value; + } + + constexpr friend bool operator>=(const FP& lhs, const FP& rhs) { + return lhs.value >= rhs.value; + } + + constexpr friend bool operator>=(const FP& lhs, const int32_t& rhs) { + return lhs.value >= static_cast(rhs) << FPLUT::PRECISION; + } + + constexpr friend bool operator>=(const int32_t& lhs, const FP& rhs) { + return static_cast(lhs) << FPLUT::PRECISION >= rhs.value; + } + + constexpr friend bool operator==(const FP& lhs, const FP& rhs) { + return lhs.value == rhs.value; + } + + constexpr friend bool operator==(const FP& lhs, const int32_t& rhs) { + return lhs.value == static_cast(rhs) << FPLUT::PRECISION; + } + + constexpr friend bool operator==(const int32_t& lhs, const FP& rhs) { + return static_cast(lhs) << FPLUT::PRECISION == rhs.value; + } + + constexpr friend bool operator!=(const FP& lhs, const FP& rhs) { + return lhs.value != rhs.value; + } + + constexpr friend bool operator!=(const FP& lhs, const int32_t& rhs) { + return lhs.value != static_cast(rhs) << FPLUT::PRECISION; + } + + constexpr friend bool operator!=(const int32_t& lhs, const FP& rhs) { + return static_cast(lhs) << FPLUT::PRECISION != rhs.value; + } + + + private: + int64_t value; + + static constexpr FP parse_integer(const char* str, const char* end, bool* successful) { + int64_t parsed_value = StringUtils::string_to_int64(str, end, successful, 10); + return parse(parsed_value); + } + + static constexpr FP parse_fraction(const char* str, const char* end, bool* successful) { + constexpr size_t READ_SIZE = 5; + if (str + READ_SIZE < end) { + Logger::error("Fixed point fraction parse will only use the first ", READ_SIZE, + " characters of ", std::string_view { str, static_cast(end - str) }); + end = str + READ_SIZE; + } + int64_t parsed_value = StringUtils::string_to_int64(str, end, successful, 10); + return parse_raw(parsed_value * 65536 / 100000); + } + }; + + static_assert(sizeof(FP) == FP::SIZE, "FP is not 8 bytes"); +} diff --git a/src/openvic/types/fixed_point/FPLUT.hpp b/src/openvic/types/fixed_point/FPLUT.hpp new file mode 100644 index 0000000..f9b21ab --- /dev/null +++ b/src/openvic/types/fixed_point/FPLUT.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "FPLUT_sin_512.hpp" + +namespace OpenVic::FPLUT { + constexpr int32_t PRECISION = 16; + constexpr int64_t ONE = 1 << PRECISION; + + // The LUT index is between 0 and 2^16, the index table goes until 512, if we shift by 7 our index will be between 0 and 511 + constexpr int32_t SHIFT = 16 - 9; + + constexpr int64_t sin(int64_t value) { + int sign = 1; + if (value < 0) { + value = -value; + sign = -1; + } + + int index = value >> SHIFT; + int64_t a = SIN_LUT[index]; + int64_t b = SIN_LUT[index + 1]; + int64_t fraction = (value - (index << SHIFT)) << 9; + int64_t result = a + (((b - a) * fraction) >> PRECISION); + return result * sign; + } +} diff --git a/src/openvic/types/fixed_point/FPLUT_sin_512.hpp b/src/openvic/types/fixed_point/FPLUT_sin_512.hpp new file mode 100644 index 0000000..1af0380 --- /dev/null +++ b/src/openvic/types/fixed_point/FPLUT_sin_512.hpp @@ -0,0 +1,58 @@ +#pragma once + +#include + +static constexpr int32_t SIN_LUT[] = { + 0, 804, 1608, 2412, 3216, 4019, 4821, 5623, 6424, 7224, + 8022, 8820, 9616, 10411, 11204, 11996, 12785, 13573, 14359, 15143, + 15924, 16703, 17479, 18253, 19024, 19792, 20557, 21320, 22078, 22834, + 23586, 24335, 25080, 25821, 26558, 27291, 28020, 28745, 29466, 30182, + 30893, 31600, 32303, 33000, 33692, 34380, 35062, 35738, 36410, 37076, + 37736, 38391, 39040, 39683, 40320, 40951, 41576, 42194, 42806, 43412, + 44011, 44604, 45190, 45769, 46341, 46906, 47464, 48015, 48559, 49095, + 49624, 50146, 50660, 51166, 51665, 52156, 52639, 53114, 53581, 54040, + 54491, 54934, 55368, 55794, 56212, 56621, 57022, 57414, 57798, 58172, + 58538, 58896, 59244, 59583, 59914, 60235, 60547, 60851, 61145, 61429, + 61705, 61971, 62228, 62476, 62714, 62943, 63162, 63372, 63572, 63763, + 63944, 64115, 64277, 64429, 64571, 64704, 64827, 64940, 65043, 65137, + 65220, 65294, 65358, 65413, 65457, 65492, 65516, 65531, 65536, 65531, + 65516, 65492, 65457, 65413, 65358, 65294, 65220, 65137, 65043, 64940, + 64827, 64704, 64571, 64429, 64277, 64115, 63944, 63763, 63572, 63372, + 63162, 62943, 62714, 62476, 62228, 61971, 61705, 61429, 61145, 60851, + 60547, 60235, 59914, 59583, 59244, 58896, 58538, 58172, 57798, 57414, + 57022, 56621, 56212, 55794, 55368, 54934, 54491, 54040, 53581, 53114, + 52639, 52156, 51665, 51166, 50660, 50146, 49624, 49095, 48559, 48015, + 47464, 46906, 46341, 45769, 45190, 44604, 44011, 43412, 42806, 42194, + 41576, 40951, 40320, 39683, 39040, 38391, 37736, 37076, 36410, 35738, + 35062, 34380, 33692, 33000, 32303, 31600, 30893, 30182, 29466, 28745, + 28020, 27291, 26558, 25821, 25080, 24335, 23586, 22834, 22078, 21320, + 20557, 19792, 19024, 18253, 17479, 16703, 15924, 15143, 14359, 13573, + 12785, 11996, 11204, 10411, 9616, 8820, 8022, 7224, 6424, 5623, + 4821, 4019, 3216, 2412, 1608, 804, 0, -804, -1608, -2412, + -3216, -4019, -4821, -5623, -6424, -7224, -8022, -8820, -9616, -10411, + -11204, -11996, -12785, -13573, -14359, -15143, -15924, -16703, -17479, -18253, + -19024, -19792, -20557, -21320, -22078, -22834, -23586, -24335, -25080, -25821, + -26558, -27291, -28020, -28745, -29466, -30182, -30893, -31600, -32303, -33000, + -33692, -34380, -35062, -35738, -36410, -37076, -37736, -38391, -39040, -39683, + -40320, -40951, -41576, -42194, -42806, -43412, -44011, -44604, -45190, -45769, + -46341, -46906, -47464, -48015, -48559, -49095, -49624, -50146, -50660, -51166, + -51665, -52156, -52639, -53114, -53581, -54040, -54491, -54934, -55368, -55794, + -56212, -56621, -57022, -57414, -57798, -58172, -58538, -58896, -59244, -59583, + -59914, -60235, -60547, -60851, -61145, -61429, -61705, -61971, -62228, -62476, + -62714, -62943, -63162, -63372, -63572, -63763, -63944, -64115, -64277, -64429, + -64571, -64704, -64827, -64940, -65043, -65137, -65220, -65294, -65358, -65413, + -65457, -65492, -65516, -65531, -65536, -65531, -65516, -65492, -65457, -65413, + -65358, -65294, -65220, -65137, -65043, -64940, -64827, -64704, -64571, -64429, + -64277, -64115, -63944, -63763, -63572, -63372, -63162, -62943, -62714, -62476, + -62228, -61971, -61705, -61429, -61145, -60851, -60547, -60235, -59914, -59583, + -59244, -58896, -58538, -58172, -57798, -57414, -57022, -56621, -56212, -55794, + -55368, -54934, -54491, -54040, -53581, -53114, -52639, -52156, -51665, -51166, + -50660, -50146, -49624, -49095, -48559, -48015, -47464, -46906, -46341, -45769, + -45190, -44604, -44011, -43412, -42806, -42194, -41576, -40951, -40320, -39683, + -39040, -38391, -37736, -37076, -36410, -35738, -35062, -34380, -33692, -33000, + -32303, -31600, -30893, -30182, -29466, -28745, -28020, -27291, -26558, -25821, + -25080, -24335, -23586, -22834, -22078, -21320, -20557, -19792, -19024, -18253, + -17479, -16703, -15924, -15143, -14359, -13573, -12785, -11996, -11204, -10411, + -9616, -8820, -8022, -7224, -6424, -5623, -4821, -4019, -3216, -2412, + -1608, -804, 0 +}; diff --git a/src/openvic/types/fixed_point/FPMath.hpp b/src/openvic/types/fixed_point/FPMath.hpp new file mode 100644 index 0000000..965d63c --- /dev/null +++ b/src/openvic/types/fixed_point/FPMath.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include "FP.hpp" + +namespace OpenVic::FPMath { + constexpr FP sin(FP number) { + number %= FP::pi2(); + number *= FP::one_div_pi2(); + return FPLUT::sin(number.get_raw_value()); + } +} diff --git a/src/openvic/types/fixed_point/lut_generator/lut_generator.py b/src/openvic/types/fixed_point/lut_generator/lut_generator.py new file mode 100644 index 0000000..5c5ba8b --- /dev/null +++ b/src/openvic/types/fixed_point/lut_generator/lut_generator.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python +import math + +PRECISION = 16 +ONE = 1 << PRECISION +SIN_VALUE_COUNT = 512 + +SinLut = [] + +for i in range(SIN_VALUE_COUNT): + angle = 2 * math.pi * i / SIN_VALUE_COUNT + + sin_value = math.sin(angle) + moved_sin = sin_value * ONE + rounded_sin = int(moved_sin + 0.5) if moved_sin > 0 else int(moved_sin - 0.5) + SinLut.append(rounded_sin) + +SinLut.append(SinLut[0]) + +output = [ +"""#pragma once + +#include + +static constexpr int32_t SIN_LUT[] = {""" +] + +lines = [SinLut[i:i+10] for i in range(0, len(SinLut), 10)] + +for line in lines: + output.append("\t" + ", ".join(str(value) for value in line) + ",") + +output[-1] = output[-1][:-1] # Remove the last comma +output.append("};\n") + +cpp_code = "\n".join(output) + +with open(f"FPLUT_sin_{SIN_VALUE_COUNT}.hpp", "w", newline="\n") as file: + file.write(cpp_code) -- cgit v1.2.3-56-ga3b1