diff options
23 files changed, 188 insertions, 121 deletions
diff --git a/src/openvic-simulation/country/Country.cpp b/src/openvic-simulation/country/Country.cpp index 463d1bf..2b022b0 100644 --- a/src/openvic-simulation/country/Country.cpp +++ b/src/openvic-simulation/country/Country.cpp @@ -24,17 +24,33 @@ CountryParty::CountryParty( policies { std::move(new_policies) } {} Country::Country( - std::string_view new_identifier, colour_t new_colour, GraphicalCultureType const& new_graphical_culture, - IdentifierRegistry<CountryParty>&& new_parties, unit_names_map_t&& new_unit_names, bool new_dynamic_tag, - government_colour_map_t&& new_alternative_colours -) : HasIdentifierAndColour { new_identifier, new_colour, false }, graphical_culture { new_graphical_culture }, - parties { std::move(new_parties) }, unit_names { std::move(new_unit_names) }, dynamic_tag { new_dynamic_tag }, - alternative_colours { std::move(new_alternative_colours) } {} + std::string_view new_identifier, + colour_t new_colour, + GraphicalCultureType const& new_graphical_culture, + IdentifierRegistry<CountryParty>&& new_parties, + unit_names_map_t&& new_unit_names, + bool new_dynamic_tag, + government_colour_map_t&& new_alternative_colours, + colour_t new_primary_unit_colour, + colour_t new_secondary_unit_colour, + colour_t new_tertiary_unit_colour +) : HasIdentifierAndColour { + new_identifier, new_colour, false }, + graphical_culture { new_graphical_culture }, + parties { std::move(new_parties) }, + unit_names { std::move(new_unit_names) }, + dynamic_tag { new_dynamic_tag }, + alternative_colours { std::move(new_alternative_colours) }, + primary_unit_colour { new_primary_unit_colour }, + secondary_unit_colour { new_secondary_unit_colour }, + tertiary_unit_colour { new_tertiary_unit_colour } + {} bool CountryManager::add_country( std::string_view identifier, colour_t colour, GraphicalCultureType const* graphical_culture, IdentifierRegistry<CountryParty>&& parties, Country::unit_names_map_t&& unit_names, bool dynamic_tag, - Country::government_colour_map_t&& alternative_colours + Country::government_colour_map_t&& alternative_colours, + colour_t primary_unit_colour, colour_t secondary_unit_colour, colour_t tertiary_unit_colour ) { if (identifier.empty()) { Logger::error("Invalid country identifier - empty!"); @@ -53,7 +69,7 @@ bool CountryManager::add_country( return countries.add_item({ identifier, colour, *graphical_culture, std::move(parties), std::move(unit_names), dynamic_tag, - std::move(alternative_colours) + std::move(alternative_colours), primary_unit_colour, secondary_unit_colour, tertiary_unit_colour }); } @@ -101,6 +117,16 @@ bool CountryManager::load_countries(GameManager const& game_manager, Dataloader return ret; } +bool CountryManager::load_country_colours(ast::NodeCPtr root){ + return countries.expect_item_dictionary([](Country& country, ast::NodeCPtr colour_node) -> bool { + return expect_dictionary_keys( + "color1", ONE_EXACTLY, expect_colour(assign_variable_callback(country.primary_unit_colour)), + "color2", ONE_EXACTLY, expect_colour(assign_variable_callback(country.secondary_unit_colour)), + "color3", ONE_EXACTLY, expect_colour(assign_variable_callback(country.tertiary_unit_colour)) + )(colour_node); + })(root); +} + node_callback_t CountryManager::load_country_party( PoliticsManager const& politics_manager, IdentifierRegistry<CountryParty>& country_parties ) const { @@ -180,7 +206,7 @@ bool CountryManager::load_country_data_file( ret &= add_country( name, colour, graphical_culture, std::move(parties), std::move(unit_names), is_dynamic, - std::move(alternative_colours) + std::move(alternative_colours), colour_t::null(), colour_t::null(), colour_t::null() ); return ret; } diff --git a/src/openvic-simulation/country/Country.hpp b/src/openvic-simulation/country/Country.hpp index 5f60534..fd6fa7e 100644 --- a/src/openvic-simulation/country/Country.hpp +++ b/src/openvic-simulation/country/Country.hpp @@ -63,11 +63,16 @@ namespace OpenVic { const unit_names_map_t PROPERTY(unit_names); const bool PROPERTY_CUSTOM_PREFIX(dynamic_tag, is); const government_colour_map_t PROPERTY(alternative_colours); + colour_t PROPERTY(primary_unit_colour); + colour_t PROPERTY(secondary_unit_colour); + colour_t PROPERTY(tertiary_unit_colour); + // Unit colours not const due to being added after construction Country( std::string_view new_identifier, colour_t new_colour, GraphicalCultureType const& new_graphical_culture, IdentifierRegistry<CountryParty>&& new_parties, unit_names_map_t&& new_unit_names, bool new_dynamic_tag, - government_colour_map_t&& new_alternative_colours + government_colour_map_t&& new_alternative_colours, + colour_t new_primary_unit_colour, colour_t new_secondary_unit_colour, colour_t new_tertiary_unit_colour ); public: @@ -88,9 +93,12 @@ namespace OpenVic { bool add_country( std::string_view identifier, colour_t colour, GraphicalCultureType const* graphical_culture, IdentifierRegistry<CountryParty>&& parties, Country::unit_names_map_t&& unit_names, bool dynamic_tag, - Country::government_colour_map_t&& alternative_colours + Country::government_colour_map_t&& alternative_colours, + colour_t primary_unit_colour, colour_t secondary_unit_colour, colour_t tertiary_unit_colour ); + bool load_country_colours(ast::NodeCPtr root); + bool load_countries(GameManager const& game_manager, Dataloader const& dataloader, ast::NodeCPtr root); bool load_country_data_file( GameManager const& game_manager, std::string_view name, bool is_dynamic, ast::NodeCPtr root diff --git a/src/openvic-simulation/dataloader/Dataloader.cpp b/src/openvic-simulation/dataloader/Dataloader.cpp index e1bf093..b7ccba3 100644 --- a/src/openvic-simulation/dataloader/Dataloader.cpp +++ b/src/openvic-simulation/dataloader/Dataloader.cpp @@ -822,6 +822,7 @@ bool Dataloader::load_defines(GameManager& game_manager) { static constexpr std::string_view buildings_file = "common/buildings.txt"; static constexpr std::string_view bookmark_file = "common/bookmarks.txt"; static constexpr std::string_view countries_file = "common/countries.txt"; + static constexpr std::string_view country_colours_file = "common/country_colors.txt"; static constexpr std::string_view culture_file = "common/cultures.txt"; static constexpr std::string_view governments_file = "common/governments.txt"; static constexpr std::string_view graphical_culture_type_file = "common/graphicalculturetype.txt"; @@ -999,6 +1000,12 @@ bool Dataloader::load_defines(GameManager& game_manager) { Logger::error("Failed to load countries!"); ret = false; } + if (!game_manager.get_country_manager().load_country_colours( + parse_defines(lookup_file(country_colours_file)).get_file_node() + )) { + Logger::error("Failed to load country colours!"); + ret = false; + } if (!game_manager.get_pop_manager().get_culture_manager().load_culture_file( game_manager.get_country_manager(), parse_defines(lookup_file(culture_file)).get_file_node() )) { diff --git a/src/openvic-simulation/interface/GFX.cpp b/src/openvic-simulation/interface/GFX.cpp index ff2737c..06d4cd3 100644 --- a/src/openvic-simulation/interface/GFX.cpp +++ b/src/openvic-simulation/interface/GFX.cpp @@ -13,11 +13,11 @@ Font::Font( node_callback_t Sprite::expect_sprites(length_callback_t length_callback, callback_t<std::unique_ptr<Sprite>&&> callback) { return expect_dictionary_keys_and_length( length_callback, - "spriteType", ZERO_OR_MORE, _expect_instance<Sprite, TextureSprite>(callback), + "spriteType", ZERO_OR_MORE, _expect_instance<Sprite, IconTextureSprite>(callback), "progressbartype", ZERO_OR_MORE, _expect_instance<Sprite, ProgressBar>(callback), "PieChartType", ZERO_OR_MORE, _expect_instance<Sprite, PieChart>(callback), "LineChartType", ZERO_OR_MORE, _expect_instance<Sprite, LineChart>(callback), - "textSpriteType", ZERO_OR_MORE, _expect_instance<Sprite, TextureSprite>(callback), + "textSpriteType", ZERO_OR_MORE, _expect_instance<Sprite, IconTextureSprite>(callback), "maskedShieldType", ZERO_OR_MORE, _expect_instance<Sprite, MaskedFlag>(callback), "tileSpriteType", ZERO_OR_MORE, _expect_instance<Sprite, TileTextureSprite>(callback), "corneredTileSpriteType", ZERO_OR_MORE, _expect_instance<Sprite, CorneredTileTextureSprite>(callback), @@ -28,49 +28,51 @@ node_callback_t Sprite::expect_sprites(length_callback_t length_callback, callba ); } -TextureSprite::TextureSprite() : texture_file {}, no_of_frames { NO_FRAMES } {} +TextureSprite::TextureSprite() : texture_file {} {} bool TextureSprite::_fill_key_map(case_insensitive_key_map_t& key_map) { bool ret = Sprite::_fill_key_map(key_map); ret &= add_key_map_entries(key_map, "texturefile", ZERO_OR_ONE, expect_string(assign_variable_callback_string(texture_file)), - "noOfFrames", ZERO_OR_ONE, expect_uint(assign_variable_callback(no_of_frames)), "norefcount", ZERO_OR_ONE, success_callback, - "effectFile", ZERO_OR_ONE, success_callback, "allwaystransparent", ZERO_OR_ONE, success_callback, + "loadType", ZERO_OR_ONE, success_callback + ); + return ret; +} + +IconTextureSprite::IconTextureSprite() : no_of_frames { NO_FRAMES } {} + +bool IconTextureSprite::_fill_key_map(case_insensitive_key_map_t& key_map) { + bool ret = TextureSprite::_fill_key_map(key_map); + ret &= add_key_map_entries(key_map, + "noOfFrames", ZERO_OR_ONE, expect_uint(assign_variable_callback(no_of_frames)), + + "effectFile", ZERO_OR_ONE, success_callback, "transparencecheck", ZERO_OR_ONE, success_callback, - "loadType", ZERO_OR_ONE, success_callback, "clicksound", ZERO_OR_ONE, success_callback ); return ret; } -TileTextureSprite::TileTextureSprite() : texture_file {}, size {} {} +TileTextureSprite::TileTextureSprite() : size {} {} bool TileTextureSprite::_fill_key_map(case_insensitive_key_map_t& key_map) { - bool ret = Sprite::_fill_key_map(key_map); + bool ret = TextureSprite::_fill_key_map(key_map); ret &= add_key_map_entries(key_map, - "texturefile", ONE_EXACTLY, expect_string(assign_variable_callback_string(texture_file)), - "size", ONE_EXACTLY, expect_ivec2(assign_variable_callback(size)), - - "norefcount", ZERO_OR_ONE, success_callback, - "loadType", ZERO_OR_ONE, success_callback + "size", ONE_EXACTLY, expect_ivec2(assign_variable_callback(size)) ); return ret; } -CorneredTileTextureSprite::CorneredTileTextureSprite() : texture_file {}, size {}, border_size {} {} +CorneredTileTextureSprite::CorneredTileTextureSprite() : size {}, border_size {} {} bool CorneredTileTextureSprite::_fill_key_map(case_insensitive_key_map_t& key_map) { - bool ret = Sprite::_fill_key_map(key_map); + bool ret = TextureSprite::_fill_key_map(key_map); ret &= add_key_map_entries(key_map, - "texturefile", ZERO_OR_ONE, expect_string(assign_variable_callback_string(texture_file)), "size", ONE_EXACTLY, expect_ivec2(assign_variable_callback(size)), - "borderSize", ONE_EXACTLY, expect_ivec2(assign_variable_callback(border_size)), - - "allwaystransparent", ZERO_OR_ONE, success_callback, - "loadType", ZERO_OR_ONE, success_callback + "borderSize", ONE_EXACTLY, expect_ivec2(assign_variable_callback(border_size)) ); return ret; } diff --git a/src/openvic-simulation/interface/GFX.hpp b/src/openvic-simulation/interface/GFX.hpp index 108ecb6..49691c1 100644 --- a/src/openvic-simulation/interface/GFX.hpp +++ b/src/openvic-simulation/interface/GFX.hpp @@ -46,13 +46,8 @@ namespace OpenVic::GFX { ); }; - class TextureSprite final : public Sprite { - friend std::unique_ptr<TextureSprite> std::make_unique<TextureSprite>(); - + class TextureSprite : public Sprite { std::string PROPERTY(texture_file); - frame_t PROPERTY(no_of_frames); - - // TODO - effectFile, allwaystransparent protected: TextureSprite(); @@ -63,13 +58,30 @@ namespace OpenVic::GFX { TextureSprite(TextureSprite&&) = default; virtual ~TextureSprite() = default; + OV_DETAIL_GET_BASE_TYPE(TextureSprite) OV_DETAIL_GET_TYPE }; - class TileTextureSprite final : public Sprite { + class IconTextureSprite final : public TextureSprite { + friend std::unique_ptr<IconTextureSprite> std::make_unique<IconTextureSprite>(); + + frame_t PROPERTY(no_of_frames); + + protected: + IconTextureSprite(); + + bool _fill_key_map(NodeTools::case_insensitive_key_map_t& key_map) override; + + public: + IconTextureSprite(IconTextureSprite&&) = default; + virtual ~IconTextureSprite() = default; + + OV_DETAIL_GET_TYPE + }; + + class TileTextureSprite final : public TextureSprite { friend std::unique_ptr<TileTextureSprite> std::make_unique<TileTextureSprite>(); - std::string PROPERTY(texture_file); ivec2_t PROPERTY(size); protected: @@ -84,10 +96,9 @@ namespace OpenVic::GFX { OV_DETAIL_GET_TYPE }; - class CorneredTileTextureSprite final : public Sprite { + class CorneredTileTextureSprite final : public TextureSprite { friend std::unique_ptr<CorneredTileTextureSprite> std::make_unique<CorneredTileTextureSprite>(); - std::string PROPERTY(texture_file); ivec2_t PROPERTY(size); ivec2_t PROPERTY(border_size); diff --git a/src/openvic-simulation/interface/GUI.cpp b/src/openvic-simulation/interface/GUI.cpp index 94e1fe3..ae4cf0e 100644 --- a/src/openvic-simulation/interface/GUI.cpp +++ b/src/openvic-simulation/interface/GUI.cpp @@ -240,7 +240,7 @@ bool TextEditBox::_fill_key_map(NodeTools::case_insensitive_key_map_t& key_map, Scrollbar::Scrollbar() : slider_button_name {}, track_button_name {}, less_button_name{}, more_button_name {}, size {}, border_size {}, - min_value {}, max_value {}, step_size {}, start_value {}, horizontal { false }, use_range_limit { false }, + min_value {}, max_value {}, step_size {}, start_value {}, horizontal { false }, range_limited { false }, range_limit_min {}, range_limit_max {}, range_limit_min_icon_name {}, range_limit_max_icon_name {} { scrollbar_elements.reserve(4); /* Space for 4 buttons, might need 2 more for range limit icons. */ } @@ -262,7 +262,7 @@ bool Scrollbar::_fill_key_map(NodeTools::case_insensitive_key_map_t& key_map, UI "stepSize", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(step_size)), "startValue", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(start_value)), "horizontal", ONE_EXACTLY, expect_int_bool(assign_variable_callback(horizontal)), - "useRangeLimit", ZERO_OR_ONE, expect_bool(assign_variable_callback(use_range_limit)), + "useRangeLimit", ZERO_OR_ONE, expect_bool(assign_variable_callback(range_limited)), "rangeLimitMin", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(range_limit_min)), "rangeLimitMax", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(range_limit_max)), "rangeLimitMinIcon", ZERO_OR_ONE, expect_string(assign_variable_callback_string(range_limit_min_icon_name)), diff --git a/src/openvic-simulation/interface/GUI.hpp b/src/openvic-simulation/interface/GUI.hpp index f044f7d..7551d1a 100644 --- a/src/openvic-simulation/interface/GUI.hpp +++ b/src/openvic-simulation/interface/GUI.hpp @@ -296,7 +296,7 @@ namespace OpenVic::GUI { fixed_point_t PROPERTY(start_value); bool PROPERTY_CUSTOM_PREFIX(horizontal, is) - bool PROPERTY(use_range_limit); + bool PROPERTY_CUSTOM_PREFIX(range_limited, is); fixed_point_t PROPERTY(range_limit_min); fixed_point_t PROPERTY(range_limit_max); std::string PROPERTY(range_limit_min_icon_name); @@ -314,8 +314,8 @@ namespace OpenVic::GUI { Scrollbar(Scrollbar&&) = default; virtual ~Scrollbar() = default; - Button const* get_slider_button() const; - Button const* get_track_button() const; + Button const* get_slider_button() const; /* The button you grab and move up and down the scrollbar. */ + Button const* get_track_button() const; /* The track/background the slider button moves along. */ Button const* get_less_button() const; Button const* get_more_button() const; diff --git a/src/openvic-simulation/testing/Requirement.cpp b/src/openvic-simulation/testing/Requirement.cpp index e7d03e5..eb48f5b 100644 --- a/src/openvic-simulation/testing/Requirement.cpp +++ b/src/openvic-simulation/testing/Requirement.cpp @@ -6,3 +6,11 @@ void Requirement::set_pass(bool in_pass) { pass = in_pass; set_tested(true); // Ever setting a pass condition implies it has been tested } + +void Requirement::set_target_value(std::string_view new_target_value) { + target_value = new_target_value; +} + +void Requirement::set_actual_value(std::string_view new_actual_value) { + actual_value = new_actual_value; +} diff --git a/src/openvic-simulation/testing/Requirement.hpp b/src/openvic-simulation/testing/Requirement.hpp index e91fa79..85a1573 100644 --- a/src/openvic-simulation/testing/Requirement.hpp +++ b/src/openvic-simulation/testing/Requirement.hpp @@ -7,15 +7,15 @@ namespace OpenVic { class Requirement { // Loaded during construction - std::string PROPERTY_RW(id); - std::string PROPERTY_RW(text); - std::string PROPERTY_RW(acceptance_criteria); + std::string PROPERTY(id); + std::string PROPERTY(text); + std::string PROPERTY(acceptance_criteria); bool PROPERTY(pass); bool PROPERTY_RW(tested); // Initialised and used during script execution - std::string PROPERTY_RW(target_value); - std::string PROPERTY_RW(actual_value); + std::string PROPERTY(target_value); + std::string PROPERTY(actual_value); public: Requirement(std::string in_id, std::string in_text, std::string in_acceptance_criteria) { @@ -27,5 +27,7 @@ namespace OpenVic { } void set_pass(bool in_pass); + void set_target_value(std::string_view new_target_value); + void set_actual_value(std::string_view new_actual_value); }; } diff --git a/src/openvic-simulation/testing/TestScript.cpp b/src/openvic-simulation/testing/TestScript.cpp index ab0bfb9..13858fb 100644 --- a/src/openvic-simulation/testing/TestScript.cpp +++ b/src/openvic-simulation/testing/TestScript.cpp @@ -2,6 +2,8 @@ using namespace OpenVic; +TestScript::TestScript(std::string_view new_script_name) : script_name { new_script_name } {} + // Getters std::vector<Requirement*> TestScript::get_requirements() { return requirements; diff --git a/src/openvic-simulation/testing/TestScript.hpp b/src/openvic-simulation/testing/TestScript.hpp index b41246e..fdf23a5 100644 --- a/src/openvic-simulation/testing/TestScript.hpp +++ b/src/openvic-simulation/testing/TestScript.hpp @@ -10,9 +10,11 @@ namespace OpenVic { std::vector<Requirement*> requirements = std::vector<Requirement*>(); GameManager* PROPERTY_RW(game_manager); - std::string PROPERTY_RW(script_name); + std::string PROPERTY(script_name); public: + TestScript(std::string_view new_script_name); + // expects an overriden method that performs arbitrary code execution // so that each script uniquely performs tests // for both requirement adding to script and to execute code diff --git a/src/openvic-simulation/testing/test_scripts/A_001_file_tests.cpp b/src/openvic-simulation/testing/test_scripts/A_001_file_tests.cpp index e24d44c..213da76 100644 --- a/src/openvic-simulation/testing/test_scripts/A_001_file_tests.cpp +++ b/src/openvic-simulation/testing/test_scripts/A_001_file_tests.cpp @@ -5,8 +5,7 @@ namespace OpenVic { class A_001_file_tests : public TestScript { public: - A_001_file_tests() { - set_script_name("A_001_file_tests"); + A_001_file_tests() : TestScript { "A_001_file_tests" } { add_requirements(); } diff --git a/src/openvic-simulation/testing/test_scripts/A_002_economy_tests.cpp b/src/openvic-simulation/testing/test_scripts/A_002_economy_tests.cpp index 4bff710..e811144 100644 --- a/src/openvic-simulation/testing/test_scripts/A_002_economy_tests.cpp +++ b/src/openvic-simulation/testing/test_scripts/A_002_economy_tests.cpp @@ -5,8 +5,7 @@ namespace OpenVic { class A_002_economy_tests : public TestScript { public: - A_002_economy_tests() { - set_script_name("A_002_economy_tests"); + A_002_economy_tests() : TestScript { "A_002_economy_tests" } { add_requirements(); } diff --git a/src/openvic-simulation/testing/test_scripts/A_003_military_unit_tests.cpp b/src/openvic-simulation/testing/test_scripts/A_003_military_unit_tests.cpp index cf572d0..b61abb4 100644 --- a/src/openvic-simulation/testing/test_scripts/A_003_military_unit_tests.cpp +++ b/src/openvic-simulation/testing/test_scripts/A_003_military_unit_tests.cpp @@ -5,8 +5,7 @@ namespace OpenVic { class A_003_military_unit_tests : public TestScript { public: - A_003_military_unit_tests() { - set_script_name("A_003_military_unit_tests"); + A_003_military_unit_tests() : TestScript { "A_003_military_unit_tests" } { add_requirements(); } diff --git a/src/openvic-simulation/testing/test_scripts/A_004_networking_tests.cpp b/src/openvic-simulation/testing/test_scripts/A_004_networking_tests.cpp index 0e05540..984a3c2 100644 --- a/src/openvic-simulation/testing/test_scripts/A_004_networking_tests.cpp +++ b/src/openvic-simulation/testing/test_scripts/A_004_networking_tests.cpp @@ -5,8 +5,7 @@ namespace OpenVic { class A_004_networking_tests : public TestScript { public: - A_004_networking_tests() { - set_script_name("A_004_networking_tests"); + A_004_networking_tests() : TestScript { "A_004_networking_tests" } { add_requirements(); } diff --git a/src/openvic-simulation/testing/test_scripts/A_005_nation_tests.cpp b/src/openvic-simulation/testing/test_scripts/A_005_nation_tests.cpp index 6f91ac2..bfa8a59 100644 --- a/src/openvic-simulation/testing/test_scripts/A_005_nation_tests.cpp +++ b/src/openvic-simulation/testing/test_scripts/A_005_nation_tests.cpp @@ -5,8 +5,7 @@ namespace OpenVic { class A_005_nation_tests : public TestScript { public: - A_005_nation_tests() { - set_script_name("A_005_nation_tests"); + A_005_nation_tests() : TestScript { "A_005_nation_tests" } { add_requirements(); } diff --git a/src/openvic-simulation/testing/test_scripts/A_006_politics_tests.cpp b/src/openvic-simulation/testing/test_scripts/A_006_politics_tests.cpp index 091075c..f9e93d9 100644 --- a/src/openvic-simulation/testing/test_scripts/A_006_politics_tests.cpp +++ b/src/openvic-simulation/testing/test_scripts/A_006_politics_tests.cpp @@ -5,8 +5,7 @@ namespace OpenVic { class A_006_politics_tests : public TestScript { public: - A_006_politics_tests() { - set_script_name("A_006_politics_tests"); + A_006_politics_tests() : TestScript { "A_006_politics_tests" } { add_requirements(); } diff --git a/src/openvic-simulation/types/fixed_point/FixedPoint.hpp b/src/openvic-simulation/types/fixed_point/FixedPoint.hpp index 6a47194..95d2759 100644 --- a/src/openvic-simulation/types/fixed_point/FixedPoint.hpp +++ b/src/openvic-simulation/types/fixed_point/FixedPoint.hpp @@ -6,7 +6,7 @@ #include <sstream> #include <string_view> -#include "openvic-simulation/types/fixed_point/FixedPointLUT.hpp" +#include "openvic-simulation/utility/Getters.hpp" #include "openvic-simulation/utility/Logger.hpp" #include "openvic-simulation/utility/NumberUtils.hpp" #include "openvic-simulation/utility/StringUtils.hpp" @@ -18,14 +18,32 @@ namespace OpenVic { static constexpr size_t SIZE = 8; - static constexpr int32_t PRECISION = FPLUT::SIN_LUT_PRECISION; + static constexpr int32_t PRECISION = 16; static constexpr int64_t ONE = 1 << PRECISION; + static constexpr int64_t FRAC_MASK = ONE - 1; + /* Fixed points represent any base 2 number with 48 bits above the point and 16 bits below it. + * - Any number expressible as n / 2^16 where n is an int64_t can be converted to a fixed point exactly. + * - If a number cannot be expressed as an integer divided by 2^16, it will be rounded down to the first + * representable number below it. For example: 0.01 = 655.36 / 2^16 becomes 0.0099945068359375 = 655 / 2^16. + * - While numbers with an integer part greater than 2^32 can be represented, they do not work properly with + * multiplication and division which involve an intermediate value that is 2^16 times larger than the result. + * For numbers with integer part smaller than 2^32 the top 16 bits prevent this intermediate value from + * overflowing. For larger values this will not work and the result will be missing its most significant bits. */ + + private: + int64_t PROPERTY_RW_CUSTOM_NAME(value, get_raw_value, set_raw_value); + + /* Sin lookup table */ + #include "openvic-simulation/types/fixed_point/FixedPointLUT_sin.hpp" + + static_assert(SIN_LUT_PRECISION == PRECISION); + + public: constexpr fixed_point_t() : value { 0 } {} constexpr fixed_point_t(int64_t new_value) : value { new_value } {} constexpr fixed_point_t(int32_t new_value) : value { static_cast<int64_t>(new_value) << PRECISION } {} - // Trivial destructor constexpr ~fixed_point_t() = default; static constexpr fixed_point_t max() { @@ -200,6 +218,23 @@ namespace OpenVic { return static_cast<int64_t>(178145LL); } + constexpr fixed_point_t sin() const { + int64_t num = (*this % pi2() * one_div_pi2()).get_raw_value(); + + const bool negative = num < 0; + if (negative) { + num = -num; + } + + const int64_t index = num >> SIN_LUT_SHIFT; + const int64_t a = SIN_LUT[index]; + const int64_t b = SIN_LUT[index + 1]; + + const int64_t fraction = (num - (index << SIN_LUT_SHIFT)) << SIN_LUT_COUNT_LOG2; + const int64_t result = a + (((b - a) * fraction) >> SIN_LUT_PRECISION); + return !negative ? result : -result; + } + constexpr bool is_negative() const { return value < 0; } @@ -216,23 +251,35 @@ namespace OpenVic { // Doesn't account for sign, so -n.abc -> 1 - 0.abc constexpr fixed_point_t get_frac() const { - return value & (ONE - 1); + return value & FRAC_MASK; } constexpr bool is_integer() const { return get_frac() == 0; } - constexpr int64_t to_int64_t() const { - return value >> PRECISION; + constexpr fixed_point_t floor() const { + return value & ~FRAC_MASK; } - constexpr void set_raw_value(int64_t value) { - this->value = value; + constexpr fixed_point_t ceil() const { + return floor() + !is_integer(); } - constexpr int64_t get_raw_value() const { - return value; + /* WARNING: the results of these rounding functions are affected by the accuracy of base 2 fixed point numbers, + * for example 1.0 rounded to a multiple of 0.01 is 0.99945068359375 for down and 1.0094451904296875 for up. */ + constexpr fixed_point_t round_down_to_multiple(fixed_point_t factor) const { + const fixed_point_t remainder = *this % factor; + return *this - remainder - (remainder < 0 ? factor : _0()); + } + + constexpr fixed_point_t round_up_to_multiple(fixed_point_t factor) const { + const fixed_point_t remainder = *this % factor; + return *this - remainder + (remainder > 0 ? factor : _0()); + } + + constexpr int64_t to_int64_t() const { + return value >> PRECISION; } constexpr int32_t to_int32_t() const { @@ -251,13 +298,13 @@ namespace OpenVic { return value / static_cast<double>(ONE); } - constexpr float to_double_rounded() const { + constexpr double to_double_rounded() const { return NumberUtils::round_to_int64((value / static_cast<double>(ONE)) * 100000.0) / 100000.0; } static std::ostream& print(std::ostream& stream, fixed_point_t val, size_t decimal_places = 0) { if (decimal_places > 0) { - fixed_point_t err = fixed_point_t::_0_50(); + fixed_point_t err = _0_50(); for (size_t i = decimal_places; i > 0; --i) { err /= 10; } @@ -612,8 +659,6 @@ namespace OpenVic { } private: - int64_t value; - static constexpr fixed_point_t parse_integer(char const* str, char const* const end, bool* successful) { int64_t parsed_value = StringUtils::string_to_int64(str, end, successful, 10); return parse(parsed_value); diff --git a/src/openvic-simulation/types/fixed_point/FixedPointLUT.hpp b/src/openvic-simulation/types/fixed_point/FixedPointLUT.hpp deleted file mode 100644 index a5d585f..0000000 --- a/src/openvic-simulation/types/fixed_point/FixedPointLUT.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include <cstdint> - -namespace OpenVic::FPLUT { - -#include "openvic-simulation/types/fixed_point/FixedPointLUT_sin.hpp" - - constexpr int32_t SHIFT = SIN_LUT_PRECISION - SIN_LUT_COUNT_LOG2; - - 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)) << SIN_LUT_COUNT_LOG2; - int64_t result = a + (((b - a) * fraction) >> SIN_LUT_PRECISION); - return result * sign; - } -} diff --git a/src/openvic-simulation/types/fixed_point/FixedPointLUT_sin.hpp b/src/openvic-simulation/types/fixed_point/FixedPointLUT_sin.hpp index 0c75efe..2b935a3 100644 --- a/src/openvic-simulation/types/fixed_point/FixedPointLUT_sin.hpp +++ b/src/openvic-simulation/types/fixed_point/FixedPointLUT_sin.hpp @@ -1,9 +1,7 @@ -#pragma once - -#include <cstdint> static constexpr int32_t SIN_LUT_PRECISION = 16; static constexpr int32_t SIN_LUT_COUNT_LOG2 = 9; +static constexpr int32_t SIN_LUT_SHIFT = SIN_LUT_PRECISION - SIN_LUT_COUNT_LOG2; static constexpr int64_t SIN_LUT[(1 << SIN_LUT_COUNT_LOG2) + 1] = { 0, 804, 1608, 2412, 3216, 4019, 4821, 5623, 6424, 7224, 8022, 8820, 9616, 10411, 11204, 11996, diff --git a/src/openvic-simulation/types/fixed_point/FixedPointMath.hpp b/src/openvic-simulation/types/fixed_point/FixedPointMath.hpp deleted file mode 100644 index 6cdb926..0000000 --- a/src/openvic-simulation/types/fixed_point/FixedPointMath.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include "openvic-simulation/types/fixed_point/FixedPoint.hpp" - -namespace OpenVic::FPMath { - constexpr fixed_point_t sin(fixed_point_t number) { - number %= fixed_point_t::pi2(); - number *= fixed_point_t::one_div_pi2(); - return FPLUT::sin(number.get_raw_value()); - } -} diff --git a/src/openvic-simulation/types/fixed_point/lut_generator/lut_generator.py b/src/openvic-simulation/types/fixed_point/lut_generator/lut_generator.py index 8ae7a32..e20eaa3 100644 --- a/src/openvic-simulation/types/fixed_point/lut_generator/lut_generator.py +++ b/src/openvic-simulation/types/fixed_point/lut_generator/lut_generator.py @@ -20,12 +20,10 @@ def generate_sin_lut(precision : int, count_log2 : int): SinLut.append(SinLut[0]) output = [ - "#pragma once", - "", - "#include <cstdint>", "", f"static constexpr int32_t SIN_LUT_PRECISION = {precision};", f"static constexpr int32_t SIN_LUT_COUNT_LOG2 = {count_log2};", + "static constexpr int32_t SIN_LUT_SHIFT = SIN_LUT_PRECISION - SIN_LUT_COUNT_LOG2;", "", "static constexpr int64_t SIN_LUT[(1 << SIN_LUT_COUNT_LOG2) + 1] = {" ] diff --git a/src/openvic-simulation/utility/Getters.hpp b/src/openvic-simulation/utility/Getters.hpp index fa76e74..0a6917c 100644 --- a/src/openvic-simulation/utility/Getters.hpp +++ b/src/openvic-simulation/utility/Getters.hpp @@ -121,7 +121,7 @@ ACCESS: #define PROPERTY_RW_FULL(NAME, GETTER_NAME, SETTER_NAME, ACCESS) \ PROPERTY_FULL(NAME, GETTER_NAME, ACCESS) \ public: \ - void SETTER_NAME(decltype(NAME) new_##NAME) { \ + constexpr void SETTER_NAME(decltype(NAME) new_##NAME) { \ NAME = new_##NAME; \ } \ ACCESS: |