aboutsummaryrefslogtreecommitdiff
path: root/extension/src
diff options
context:
space:
mode:
author Hop311 <hop3114@gmail.com>2023-08-10 13:00:54 +0200
committer Hop311 <hop3114@gmail.com>2023-08-10 13:00:54 +0200
commit4c43951e70aaa2e7265d3b3f3c4964c048b9328d (patch)
treec6d46df1686710ccacd63ceb16d687cb55877457 /extension/src
parent505176d9cabe76cff7cdac6b4d4ef1c77ccb00d9 (diff)
PieChart data and image now come from c++ layer
Diffstat (limited to 'extension/src')
-rw-r--r--extension/src/GameSingleton.cpp81
-rw-r--r--extension/src/GameSingleton.hpp16
-rw-r--r--extension/src/Utilities.cpp90
-rw-r--r--extension/src/Utilities.hpp10
4 files changed, 191 insertions, 6 deletions
diff --git a/extension/src/GameSingleton.cpp b/extension/src/GameSingleton.cpp
index efacb8e..baf4d44 100644
--- a/extension/src/GameSingleton.cpp
+++ b/extension/src/GameSingleton.cpp
@@ -1,7 +1,5 @@
#include "GameSingleton.hpp"
-#include <cassert>
-
#include <godot_cpp/variant/utility_functions.hpp>
#include "openvic/utility/Logger.hpp"
@@ -13,8 +11,7 @@ using namespace OpenVic;
TerrainVariant::TerrainVariant(std::string const& new_identfier,
colour_t new_colour, Ref<Image> const& new_image)
- : HasIdentifier { new_identfier },
- HasColour { new_colour, true },
+ : HasIdentifierAndColour { new_identfier, new_colour, true },
image { new_image } {}
Ref<Image> TerrainVariant::get_image() const {
@@ -75,6 +72,9 @@ void GameSingleton::_bind_methods() {
ClassDB::bind_static_method("GameSingleton", D_METHOD("get_province_info_region_key"), &GameSingleton::get_province_info_region_key);
ClassDB::bind_static_method("GameSingleton", D_METHOD("get_province_info_life_rating_key"), &GameSingleton::get_province_info_life_rating_key);
ClassDB::bind_static_method("GameSingleton", D_METHOD("get_province_info_total_population_key"), &GameSingleton::get_province_info_total_population_key);
+ ClassDB::bind_static_method("GameSingleton", D_METHOD("get_province_info_pop_types_key"), &GameSingleton::get_province_info_pop_types_key);
+ ClassDB::bind_static_method("GameSingleton", D_METHOD("get_province_info_pop_ideologies_key"), &GameSingleton::get_province_info_pop_ideologies_key);
+ ClassDB::bind_static_method("GameSingleton", D_METHOD("get_province_info_pop_cultures_key"), &GameSingleton::get_province_info_pop_cultures_key);
ClassDB::bind_static_method("GameSingleton", D_METHOD("get_province_info_rgo_key"), &GameSingleton::get_province_info_rgo_key);
ClassDB::bind_static_method("GameSingleton", D_METHOD("get_province_info_buildings_key"), &GameSingleton::get_province_info_buildings_key);
@@ -84,6 +84,25 @@ void GameSingleton::_bind_methods() {
ClassDB::bind_static_method("GameSingleton", D_METHOD("get_building_info_start_date_key"), &GameSingleton::get_building_info_start_date_key);
ClassDB::bind_static_method("GameSingleton", D_METHOD("get_building_info_end_date_key"), &GameSingleton::get_building_info_end_date_key);
ClassDB::bind_static_method("GameSingleton", D_METHOD("get_building_info_expansion_progress_key"), &GameSingleton::get_building_info_expansion_progress_key);
+
+ ClassDB::bind_static_method("GameSingleton", D_METHOD("get_culture_info_size_key"), &GameSingleton::get_piechart_info_size_key);
+ ClassDB::bind_static_method("GameSingleton", D_METHOD("get_culture_info_colour_key"), &GameSingleton::get_piechart_info_colour_key);
+
+ ClassDB::bind_static_method("GameSingleton", D_METHOD("draw_pie_chart", "image", "stopAngles", "colours", "radius",
+ "shadow_displacement", "shadow_tightness", "shadow_radius", "shadow_thickness",
+ "trim_colour", "trim_size", "gradient_falloff", "gradient_base",
+ "donut", "donut_inner_trim", "donut_inner_radius"), &GameSingleton::draw_pie_chart);
+}
+
+void GameSingleton::draw_pie_chart(Ref<Image> image,
+ Array const& stopAngles, Array const& colours, float radius,
+ Vector2 shadow_displacement, float shadow_tightness, float shadow_radius, float shadow_thickness,
+ Color trim_colour, float trim_size, float gradient_falloff, float gradient_base,
+ bool donut, bool donut_inner_trim, float donut_inner_radius) {
+
+ OpenVic::draw_pie_chart(image, stopAngles, colours, radius, shadow_displacement, shadow_tightness, shadow_radius, shadow_thickness,
+ trim_colour, trim_size, gradient_falloff, gradient_base,
+ donut, donut_inner_trim, donut_inner_radius);
}
GameSingleton* GameSingleton::get_singleton() {
@@ -153,11 +172,25 @@ Error GameSingleton::_load_hardcoded_defines() {
}
return HIGH_ALPHA_VALUE | val;
}
- return HIGH_ALPHA_VALUE;
+ return NULL_COLOUR;
} },
{ "mapmode_population",
[](Map const& map, Province const& province) -> colour_t {
return HIGH_ALPHA_VALUE | (fraction_to_colour_byte(province.get_total_population(), map.get_highest_province_population() + 1, 0.1f, 1.0f) << 8);
+ } },
+ { "mapmode_culture",
+ [](Map const& map, Province const& province) -> colour_t {
+ distribution_t const& cultures = province.get_culture_distribution();
+ if (!cultures.empty()) {
+ // This breaks if replaced with distribution_t::value_type, something
+ // about operator=(volatile const&) being deleted.
+ std::pair<HasIdentifierAndColour const*, float> culture = *cultures.begin();
+ for (distribution_t::value_type const p : cultures) {
+ if (p.second > culture.second) culture = p;
+ }
+ return HIGH_ALPHA_VALUE | culture.first->get_colour();
+ }
+ return NULL_COLOUR;
} }
};
for (mapmode_t const& mapmode : mapmodes)
@@ -208,6 +241,18 @@ StringName const& GameSingleton::get_province_info_total_population_key() {
static const StringName key = "total_population";
return key;
}
+StringName const& GameSingleton::get_province_info_pop_types_key() {
+ static const StringName key = "pop_types";
+ return key;
+}
+StringName const& GameSingleton::get_province_info_pop_ideologies_key() {
+ static const StringName key = "pop_ideologies";
+ return key;
+}
+StringName const& GameSingleton::get_province_info_pop_cultures_key() {
+ static const StringName key = "pop_cultures";
+ return key;
+}
StringName const& GameSingleton::get_province_info_rgo_key() {
static const StringName key = "rgo";
return key;
@@ -242,6 +287,26 @@ StringName const& GameSingleton::get_building_info_expansion_progress_key() {
return key;
}
+StringName const& GameSingleton::get_piechart_info_size_key() {
+ static const StringName key = "size";
+ return key;
+}
+StringName const& GameSingleton::get_piechart_info_colour_key() {
+ static const StringName key = "colour";
+ return key;
+}
+
+Dictionary GameSingleton::_distribution_to_dictionary(distribution_t const& dist) const {
+ Dictionary dict;
+ for (distribution_t::value_type const& p : dist) {
+ Dictionary sub_dict;
+ sub_dict[get_piechart_info_size_key()] = p.second;
+ sub_dict[get_piechart_info_colour_key()] = to_godot_color(p.first->get_colour());
+ dict[std_to_godot_string(p.first->get_identifier())] = sub_dict;
+ }
+ return dict;
+}
+
Dictionary GameSingleton::get_province_info_from_index(int32_t index) const {
Province const* province = game_manager.map.get_province_by_index(index);
if (province == nullptr) return {};
@@ -257,6 +322,12 @@ Dictionary GameSingleton::get_province_info_from_index(int32_t index) const {
ret[get_province_info_life_rating_key()] = province->get_life_rating();
ret[get_province_info_total_population_key()] = province->get_total_population();
+ distribution_t const& pop_types = province->get_pop_type_distribution();
+ if (!pop_types.empty()) ret[get_province_info_pop_types_key()] = _distribution_to_dictionary(pop_types);
+ //distribution_t const& ideologies = province->get_ideology_distribution();
+ //if (!ideologies.empty()) ret[get_province_info_pop_ideologies_key()] = _distribution_to_dictionary(ideologies);
+ distribution_t const& cultures = province->get_culture_distribution();
+ if (!cultures.empty()) ret[get_province_info_pop_cultures_key()] = _distribution_to_dictionary(cultures);
std::vector<Building> const& buildings = province->get_buildings();
if (!buildings.empty()) {
diff --git a/extension/src/GameSingleton.hpp b/extension/src/GameSingleton.hpp
index 23eb334..c4e90e0 100644
--- a/extension/src/GameSingleton.hpp
+++ b/extension/src/GameSingleton.hpp
@@ -6,7 +6,7 @@
#include "openvic/GameManager.hpp"
namespace OpenVic {
- struct TerrainVariant : HasIdentifier, HasColour {
+ struct TerrainVariant : HasIdentifierAndColour {
friend class GameSingleton;
private:
@@ -64,10 +64,18 @@ namespace OpenVic {
godot::Error _update_colour_image();
void _on_state_updated();
+ godot::Dictionary _distribution_to_dictionary(distribution_t const& dist) const;
+
protected:
static void _bind_methods();
public:
+ static void draw_pie_chart(godot::Ref<godot::Image> image,
+ godot::Array const& stopAngles, godot::Array const& colours, float radius,
+ godot::Vector2 shadow_displacement, float shadow_tightness, float shadow_radius, float shadow_thickness,
+ godot::Color trim_colour, float trim_size, float gradient_falloff, float gradient_base,
+ bool donut, bool donut_inner_trim, float donut_inner_radius);
+
static GameSingleton* get_singleton();
GameSingleton();
@@ -106,6 +114,9 @@ namespace OpenVic {
static godot::StringName const& get_province_info_region_key();
static godot::StringName const& get_province_info_life_rating_key();
static godot::StringName const& get_province_info_total_population_key();
+ static godot::StringName const& get_province_info_pop_types_key();
+ static godot::StringName const& get_province_info_pop_ideologies_key();
+ static godot::StringName const& get_province_info_pop_cultures_key();
static godot::StringName const& get_province_info_rgo_key();
static godot::StringName const& get_province_info_buildings_key();
@@ -116,6 +127,9 @@ namespace OpenVic {
static godot::StringName const& get_building_info_end_date_key();
static godot::StringName const& get_building_info_expansion_progress_key();
+ static godot::StringName const& get_piechart_info_size_key();
+ static godot::StringName const& get_piechart_info_colour_key();
+
/* Get info to display in Province Overview Panel, packaged in
* a Dictionary using the StringNames above as keys.
*/
diff --git a/extension/src/Utilities.cpp b/extension/src/Utilities.cpp
index a912490..f09eae0 100644
--- a/extension/src/Utilities.cpp
+++ b/extension/src/Utilities.cpp
@@ -1,5 +1,7 @@
#include "Utilities.hpp"
+#include <numbers>
+
#include <godot_cpp/classes/resource_loader.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
@@ -14,3 +16,91 @@ Ref<Image> OpenVic::load_godot_image(String const& path) {
return Image::load_from_file(path);
}
}
+
+// Get the polar coordinates of a pixel relative to the center
+static Vector2 getPolar(Vector2 UVin, Vector2 center) {
+ Vector2 relcoord = (UVin-center);
+ float dist = relcoord.length();
+ float theta = std::numbers::pi / 2 + atan2(relcoord.y, relcoord.x);
+ if (theta < 0.0f) theta += std::numbers::pi * 2;
+ return { dist, theta };
+}
+
+// From thebookofshaders, returns a gradient falloff
+static inline float parabola(float base, float x, float k){
+ return powf(base * x * (1.0 - x), k);
+}
+
+static inline float parabola_shadow(float base, float x){
+ return base * x * x;
+}
+
+static Color pie_chart_fragment(Vector2 UV, float radius, Array const& stopAngles, Array const& colours,
+ Vector2 shadow_displacement, float shadow_tightness, float shadow_radius, float shadow_thickness,
+ Color trim_colour, float trim_size, float gradient_falloff, float gradient_base,
+ bool donut, bool donut_inner_trim, float donut_inner_radius) {
+
+ Vector2 coords = getPolar(UV, { 0.5, 0.5 });
+ float dist = coords.x;
+ float theta = coords.y;
+
+ Vector2 shadow_polar = getPolar(UV, shadow_displacement);
+ float shadow_peak = radius + (radius - donut_inner_radius) / 2.0;
+ float shadow_gradient = shadow_thickness + parabola_shadow(shadow_tightness * -10.0, shadow_polar.x + shadow_peak - shadow_radius);
+
+ // Inner hole of the donut => make it transparent
+ if (donut && dist <= donut_inner_radius) {
+ return { 0.1, 0.1, 0.1, shadow_gradient };
+ }
+ // Inner trim
+ else if (donut && donut_inner_trim && dist <= donut_inner_radius + trim_size) {
+ return { trim_colour, 1.0 };
+ }
+ // Interior
+ else if (dist <= radius-trim_size) {
+ Color col { 1.0f, 0.0f, 0.0f };
+ for (int i = 0; i < stopAngles.size(); i++){
+ if (theta <= float(stopAngles[i])) {
+ col = colours[i];
+ break;
+ }
+ }
+ float gradient = parabola(gradient_base, dist, gradient_falloff);
+ return { col * (1.0 - gradient), 1.0 };
+ }
+ // Outer trim
+ else if (dist <= radius) {
+ return { trim_colour, 1.0 };
+ }
+ // Outside the circle
+ else{
+ return { 0.1, 0.1, 0.1, shadow_gradient };
+ }
+}
+
+void OpenVic::draw_pie_chart(Ref<Image> image,
+ Array const& stopAngles, Array const& colours, float radius,
+ Vector2 shadow_displacement, float shadow_tightness, float shadow_radius, float shadow_thickness,
+ Color trim_colour, float trim_size, float gradient_falloff, float gradient_base,
+ bool donut, bool donut_inner_trim, float donut_inner_radius) {
+
+ ERR_FAIL_NULL_EDMSG(image, "Cannot draw pie chart to null image.");
+ const int32_t width = image->get_width();
+ const int32_t height = image->get_height();
+ ERR_FAIL_COND_EDMSG(width <= 0 || height <= 0, "Cannot draw pie chart to empty image.");
+ if (width != height) {
+ UtilityFunctions::push_warning("Drawing pie chart to non-square image: ", width, "x", height);
+ }
+ const int32_t size = std::min(width, height);
+ for (int32_t y = 0; y < size; ++y) {
+ for (int32_t x = 0; x < size; ++x) {
+ image->set_pixel(x, y, pie_chart_fragment(
+ { static_cast<float>(x) / static_cast<float>(size),
+ static_cast<float>(y) / static_cast<float>(size) },
+ radius, stopAngles, colours,
+ shadow_displacement, shadow_tightness, shadow_radius, shadow_thickness,
+ trim_colour, trim_size, gradient_falloff, gradient_base,
+ donut, donut_inner_trim, donut_inner_radius));
+ }
+ }
+}
diff --git a/extension/src/Utilities.hpp b/extension/src/Utilities.hpp
index 681b893..e8796e9 100644
--- a/extension/src/Utilities.hpp
+++ b/extension/src/Utilities.hpp
@@ -16,5 +16,15 @@ namespace OpenVic {
return str.c_str();
}
+ inline godot::Color to_godot_color(colour_t colour) {
+ return { colour_byte_to_float((colour >> 16) & 0xFF), colour_byte_to_float((colour >> 8) & 0xFF), colour_byte_to_float(colour & 0xFF) };
+ }
+
godot::Ref<godot::Image> load_godot_image(godot::String const& path);
+
+ void draw_pie_chart(godot::Ref<godot::Image> image,
+ godot::Array const& stopAngles, godot::Array const& colours, float radius,
+ godot::Vector2 shadow_displacement, float shadow_tightness, float shadow_radius, float shadow_thickness,
+ godot::Color trim_colour, float trim_size, float gradient_falloff, float gradient_base,
+ bool donut, bool donut_inner_trim, float donut_inner_radius);
}