aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
m---------extension/deps/openvic-simulation0
-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
-rw-r--r--game/localisation/en_GB/mapmodes.csv1
-rw-r--r--game/localisation/en_GB/menus.csv3
-rw-r--r--game/src/Game/GameSession/ProvinceOverviewPanel/ProvinceOverviewPanel.gd11
-rw-r--r--game/src/Game/GameSession/ProvinceOverviewPanel/ProvinceOverviewPanel.tscn127
-rw-r--r--game/src/Game/Theme/PieChart/LayeredChart.gd28
-rw-r--r--game/src/Game/Theme/PieChart/LayeredChart.tscn249
-rw-r--r--game/src/Game/Theme/PieChart/PieChart.gd355
-rw-r--r--game/src/Game/Theme/PieChart/PieChart.gdshader87
-rw-r--r--game/src/Game/Theme/PieChart/PieChart.tscn117
-rw-r--r--game/src/Game/Theme/PieChart/PieChartMat.tres21
-rw-r--r--game/src/Game/Theme/PieChart/chart_test.gd32
16 files changed, 384 insertions, 844 deletions
diff --git a/extension/deps/openvic-simulation b/extension/deps/openvic-simulation
-Subproject 8a08be3e7e8477973e243716d431ad7117acfa4
+Subproject 538e7dc4ec44c4d09a6a654f10229e6392653a5
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);
}
diff --git a/game/localisation/en_GB/mapmodes.csv b/game/localisation/en_GB/mapmodes.csv
index 3b7fd52..8fa5798 100644
--- a/game/localisation/en_GB/mapmodes.csv
+++ b/game/localisation/en_GB/mapmodes.csv
@@ -7,3 +7,4 @@ mapmode_index;Index
mapmode_rgo;RGO
mapmode_infrastructure;Infrastructure
mapmode_population;Population Density
+mapmode_culture;Nationality
diff --git a/game/localisation/en_GB/menus.csv b/game/localisation/en_GB/menus.csv
index 1d16af6..57994ad 100644
--- a/game/localisation/en_GB/menus.csv
+++ b/game/localisation/en_GB/menus.csv
@@ -131,3 +131,6 @@ building_fort;Fort
building_naval_base;Naval Base
building_railroad;Railroad
EXPAND_PROVINCE_BUILDING;Expand
+
+;; Pie Chart
+PIECHART_TOOLTIP_NO_DATA;No data
diff --git a/game/src/Game/GameSession/ProvinceOverviewPanel/ProvinceOverviewPanel.gd b/game/src/Game/GameSession/ProvinceOverviewPanel/ProvinceOverviewPanel.gd
index 04a035c..0220be2 100644
--- a/game/src/Game/GameSession/ProvinceOverviewPanel/ProvinceOverviewPanel.gd
+++ b/game/src/Game/GameSession/ProvinceOverviewPanel/ProvinceOverviewPanel.gd
@@ -7,6 +7,8 @@ extends PanelContainer
@export var _rgo_icon_texture_rect : TextureRect
@export var _rgo_name_label : Label
@export var _buildings_container : Container
+@export var _pop_type_chart : PieChart
+@export var _pop_ideology_chart : PieChart
@export var _pop_culture_chart : PieChart
const _missing_suffix : String = "_MISSING"
@@ -114,6 +116,10 @@ func _update_info() -> void:
_total_population_label.text = Localisation.tr_number(_province_info.get(GameSingleton.get_province_info_total_population_key(), 0))
+ _pop_type_chart.set_to_distribution(_province_info.get(GameSingleton.get_province_info_pop_types_key(), {}))
+ _pop_ideology_chart.set_to_distribution(_province_info.get(GameSingleton.get_province_info_pop_ideologies_key(), {}))
+ _pop_culture_chart.set_to_distribution(_province_info.get(GameSingleton.get_province_info_pop_cultures_key(), {}))
+
_rgo_name_label.text = _province_info.get(GameSingleton.get_province_info_rgo_key(),
GameSingleton.get_province_info_rgo_key() + _missing_suffix)
_rgo_icon_texture_rect.texture = GameSingleton.get_good_icon_texture(_rgo_name_label.text)
@@ -122,11 +128,6 @@ func _update_info() -> void:
for i in max(buildings.size(), _building_rows.size()):
_set_building_row(i, buildings[i] if i < buildings.size() else {})
- #PLACEHOLDER for updating piechart
- _pop_culture_chart.addOrReplaceLabel("NORTH_GERMAN",50,"North German Culture",Color.DIM_GRAY)
- _pop_culture_chart.addOrReplaceLabel("FRENCH",25,"French Culture",Color.BLUE)
- _pop_culture_chart.addOrReplaceLabel("SOUTH_GERMAN",7,"South German Culture",Color.FIREBRICK)
-
show()
else:
hide()
diff --git a/game/src/Game/GameSession/ProvinceOverviewPanel/ProvinceOverviewPanel.tscn b/game/src/Game/GameSession/ProvinceOverviewPanel/ProvinceOverviewPanel.tscn
index 9b4c45b..7e49ac8 100644
--- a/game/src/Game/GameSession/ProvinceOverviewPanel/ProvinceOverviewPanel.tscn
+++ b/game/src/Game/GameSession/ProvinceOverviewPanel/ProvinceOverviewPanel.tscn
@@ -1,118 +1,9 @@
-[gd_scene load_steps=6 format=3 uid="uid://byq323jbel48u"]
+[gd_scene load_steps=3 format=3 uid="uid://byq323jbel48u"]
[ext_resource type="Script" path="res://src/Game/GameSession/ProvinceOverviewPanel/ProvinceOverviewPanel.gd" id="1_3n8k5"]
[ext_resource type="PackedScene" uid="uid://cr7p1k2xm7mum" path="res://src/Game/Theme/PieChart/PieChart.tscn" id="2_3oytt"]
-[sub_resource type="Shader" id="Shader_2k3yf"]
-code = "shader_type canvas_item;
-
-// The center in UV coordinates, which will always
-//be 0.5, the actual radius will be controlled by the control node
-//const vec2 center = vec2(0.5,0.5);
-uniform float radius = 0.4;
-
-//shadow
-uniform vec2 shadow_displacement = vec2(0.75,0.75);
-uniform float shadow_tightness = 10;
-uniform float shadow_radius = 0.7;
-uniform float shadow_thickness = 1.0;
-
-// Control of the slices
-uniform float stopAngles[5];
-uniform vec3 colours[5];
-
-// Trim
-uniform vec3 trim_colour;
-uniform float trim_size = 0.05;
-
-// The center is spotlighted by the gradient,
-//control its size and falloff with these
-uniform float gradient_falloff = 3.6;
-uniform float gradient_base = 3.1;
-
-// control whether this is a donut instead of a pie chart
-uniform bool donut = false;
-uniform bool donut_inner_trim = false;
-uniform float donut_inner_radius = 0.15;
-
-// get the polar coordinates of a pixel relative to the center
-vec2 getPolar(vec2 UVin, vec2 center){
- vec2 relcoord = (UVin-center);
- float dist = length(relcoord);
- float theta = PI/2.0 + atan((relcoord.y)/(relcoord.x));
- if(UVin.x < 0.5){
- theta += PI;
- }
- return vec2(dist,theta);
-}
-
-// from thebookofshaders, returns a gradient falloff
-float parabola( float base, float x, float k ){
- return pow( base*x*(1.0-x), k );
-}
-
-float parabola_shadow(float base, float x){
- return base*x*x;
-}
-
-void fragment() {
- vec2 coords = getPolar(UV,vec2(0.5,0.5));
- float dist = coords.x;
- float theta = coords.y;
-
- vec2 shadow_polar = getPolar(UV,vec2(0.0+shadow_displacement.x,0.0+shadow_displacement.y));
- 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){
- COLOR = vec4(0.1,0.1,0.1,shadow_gradient);
- }
- // inner trim
- else if(donut && donut_inner_trim && dist <= donut_inner_radius + trim_size){
- COLOR = vec4(trim_colour,1.0);
- }
- // interior
- else if(dist <= radius-trim_size){
- for(int i=0;i<stopAngles.length();i++){
- if(theta <= stopAngles[i]){
- float gradient = parabola(gradient_base,dist,gradient_falloff);
- COLOR = vec4(colours[i]*(1.0-gradient),1.0);
- break;
- }
- }
- }
- // outer trim
- else if(dist <= radius){
- COLOR = vec4(trim_colour,1.0);
- }
- //outside the circle
- else{
- COLOR = vec4(0.1,0.1,0.1,shadow_gradient);
- }
-}"
-
-[sub_resource type="ShaderMaterial" id="ShaderMaterial_3ep5m"]
-resource_name = "Piechart_shader"
-shader = SubResource("Shader_2k3yf")
-shader_parameter/radius = 0.4
-shader_parameter/shadow_displacement = Vector2(0.6, 0.6)
-shader_parameter/shadow_tightness = 1.0
-shader_parameter/shadow_radius = 0.6
-shader_parameter/shadow_thickness = 1.0
-shader_parameter/stopAngles = []
-shader_parameter/colours = []
-shader_parameter/trim_colour = Vector3(0, 0, 0)
-shader_parameter/trim_size = 0.02
-shader_parameter/gradient_falloff = 3.6
-shader_parameter/gradient_base = 3.1
-shader_parameter/donut = false
-shader_parameter/donut_inner_trim = true
-shader_parameter/donut_inner_radius = 0.25
-
-[sub_resource type="CanvasTexture" id="CanvasTexture_udhyk"]
-
-[node name="ProvinceOverviewPanel" type="PanelContainer" node_paths=PackedStringArray("_province_name_label", "_region_name_label", "_life_rating_bar", "_total_population_label", "_rgo_icon_texture_rect", "_rgo_name_label", "_buildings_container", "_pop_culture_chart")]
+[node name="ProvinceOverviewPanel" type="PanelContainer" node_paths=PackedStringArray("_province_name_label", "_region_name_label", "_life_rating_bar", "_total_population_label", "_rgo_icon_texture_rect", "_rgo_name_label", "_buildings_container", "_pop_type_chart", "_pop_ideology_chart", "_pop_culture_chart")]
editor_description = "UI-56"
anchors_preset = 2
anchor_top = 1.0
@@ -129,7 +20,9 @@ _total_population_label = NodePath("PanelList/InteractList/TotalPopulation")
_rgo_icon_texture_rect = NodePath("PanelList/InteractList/RGOInfo/RGOIcon")
_rgo_name_label = NodePath("PanelList/InteractList/RGOInfo/RGOName")
_buildings_container = NodePath("PanelList/InteractList/BuildingsContainer")
-_pop_culture_chart = NodePath("PanelList/InteractList/PopStats/PieChart")
+_pop_type_chart = NodePath("PanelList/InteractList/PopStats/PopTypeChart")
+_pop_ideology_chart = NodePath("PanelList/InteractList/PopStats/PopIdeologyChart")
+_pop_culture_chart = NodePath("PanelList/InteractList/PopStats/PopCultureChart")
[node name="PanelList" type="VBoxContainer" parent="."]
layout_mode = 2
@@ -202,10 +95,14 @@ mouse_filter = 1
[node name="PopStats" type="HBoxContainer" parent="PanelList/InteractList"]
layout_mode = 2
-[node name="PieChart" parent="PanelList/InteractList/PopStats" instance=ExtResource("2_3oytt")]
-material = SubResource("ShaderMaterial_3ep5m")
+[node name="PopTypeChart" parent="PanelList/InteractList/PopStats" instance=ExtResource("2_3oytt")]
+layout_mode = 2
+
+[node name="PopIdeologyChart" parent="PanelList/InteractList/PopStats" instance=ExtResource("2_3oytt")]
+layout_mode = 2
+
+[node name="PopCultureChart" parent="PanelList/InteractList/PopStats" instance=ExtResource("2_3oytt")]
layout_mode = 2
-texture = SubResource("CanvasTexture_udhyk")
[node name="HSeparator3" type="HSeparator" parent="PanelList/InteractList"]
layout_mode = 2
diff --git a/game/src/Game/Theme/PieChart/LayeredChart.gd b/game/src/Game/Theme/PieChart/LayeredChart.gd
deleted file mode 100644
index 7bc69c8..0000000
--- a/game/src/Game/Theme/PieChart/LayeredChart.gd
+++ /dev/null
@@ -1,28 +0,0 @@
-extends CenterContainer
-
-var overlapping_charts:Array = []
-
-# Called when the node enters the scene tree for the first time.
-func _ready():
- for child in get_children():
- if child is PieChart:
- overlapping_charts.push_back(child)
-
-#Process mouse to select the appropriate tooltip for the slice
-func _gui_input(event:InputEvent):
- if event is InputEventMouse:
- var pos = event.position
- var handled:bool = false
- var x = overlapping_charts.size()
- #process the charts in reverse order (overlying charts first)
- #as you can't actually make the inner chart(s) smaller with a centerContainer
- for i in range(x):
- var chart = overlapping_charts[x-(i+1)]
- if not handled:
- handled = chart.handleTooltip(pos)
- else:
- chart.RichTooltip.visible = false
-
-func _on_mouse_exited():
- for chart in overlapping_charts:
- chart.RichTooltip.visible = false
diff --git a/game/src/Game/Theme/PieChart/LayeredChart.tscn b/game/src/Game/Theme/PieChart/LayeredChart.tscn
deleted file mode 100644
index 9382483..0000000
--- a/game/src/Game/Theme/PieChart/LayeredChart.tscn
+++ /dev/null
@@ -1,249 +0,0 @@
-[gd_scene load_steps=9 format=3 uid="uid://ct48qux7spi6u"]
-
-[ext_resource type="PackedScene" uid="uid://cr7p1k2xm7mum" path="res://src/Game/Theme/PieChart/PieChart.tscn" id="1_cdu1k"]
-[ext_resource type="Script" path="res://src/Game/Theme/PieChart/LayeredChart.gd" id="1_wxoci"]
-
-[sub_resource type="Shader" id="Shader_ojesc"]
-code = "shader_type canvas_item;
-
-// The center in UV coordinates, which will always
-//be 0.5, the actual radius will be controlled by the control node
-//const vec2 center = vec2(0.5,0.5);
-uniform float radius = 0.4;
-
-//shadow
-uniform vec2 shadow_displacement = vec2(0.75,0.75);
-uniform float shadow_tightness = 10;
-uniform float shadow_radius = 0.7;
-uniform float shadow_thickness = 1.0;
-
-// Control of the slices
-uniform float stopAngles[5];
-uniform vec3 colours[5];
-
-// Trim
-uniform vec3 trim_colour;
-uniform float trim_size = 0.05;
-
-// The center is spotlighted by the gradient,
-//control its size and falloff with these
-uniform float gradient_falloff = 3.6;
-uniform float gradient_base = 3.1;
-
-// control whether this is a donut instead of a pie chart
-uniform bool donut = false;
-uniform bool donut_inner_trim = false;
-uniform float donut_inner_radius = 0.15;
-
-// get the polar coordinates of a pixel relative to the center
-vec2 getPolar(vec2 UVin, vec2 center){
- vec2 relcoord = (UVin-center);
- float dist = length(relcoord);
- float theta = PI/2.0 + atan((relcoord.y)/(relcoord.x));
- if(UVin.x < 0.5){
- theta += PI;
- }
- return vec2(dist,theta);
-}
-
-// from thebookofshaders, returns a gradient falloff
-float parabola( float base, float x, float k ){
- return pow( base*x*(1.0-x), k );
-}
-
-float parabola_shadow(float base, float x){
- return base*x*x;
-}
-
-void fragment() {
- vec2 coords = getPolar(UV,vec2(0.5,0.5));
- float dist = coords.x;
- float theta = coords.y;
-
- vec2 shadow_polar = getPolar(UV,vec2(0.0+shadow_displacement.x,0.0+shadow_displacement.y));
- 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){
- COLOR = vec4(0.1,0.1,0.1,shadow_gradient);
- }
- // inner trim
- else if(donut && donut_inner_trim && dist <= donut_inner_radius + trim_size){
- COLOR = vec4(trim_colour,1.0);
- }
- // interior
- else if(dist <= radius-trim_size){
- for(int i=0;i<stopAngles.length();i++){
- if(theta <= stopAngles[i]){
- float gradient = parabola(gradient_base,dist,gradient_falloff);
- COLOR = vec4(colours[i]*(1.0-gradient),1.0);
- break;
- }
- }
- }
- // outer trim
- else if(dist <= radius){
- COLOR = vec4(trim_colour,1.0);
- }
- //outside the circle
- else{
- COLOR = vec4(0.1,0.1,0.1,shadow_gradient);
- }
-}"
-
-[sub_resource type="ShaderMaterial" id="ShaderMaterial_6ril8"]
-resource_name = "Piechart_shader"
-shader = SubResource("Shader_ojesc")
-shader_parameter/radius = 0.4
-shader_parameter/shadow_displacement = Vector2(0.6, 0.6)
-shader_parameter/shadow_tightness = 2.0
-shader_parameter/shadow_radius = 0.75
-shader_parameter/shadow_thickness = 1.0
-shader_parameter/stopAngles = [3.14159, 5.02655, 6.28319]
-shader_parameter/colours = [Vector3(0, 0, 1), Vector3(1, 1, 0), Vector3(0.4, 0, 0.6)]
-shader_parameter/trim_colour = Vector3(0, 0, 0)
-shader_parameter/trim_size = 0.02
-shader_parameter/gradient_falloff = 3.6
-shader_parameter/gradient_base = 3.1
-shader_parameter/donut = false
-shader_parameter/donut_inner_trim = true
-shader_parameter/donut_inner_radius = 0.051
-
-[sub_resource type="CanvasTexture" id="CanvasTexture_pwdgy"]
-
-[sub_resource type="Shader" id="Shader_vq8ad"]
-code = "shader_type canvas_item;
-
-// The center in UV coordinates, which will always
-//be 0.5, the actual radius will be controlled by the control node
-//const vec2 center = vec2(0.5,0.5);
-uniform float radius = 0.4;
-
-//shadow
-uniform vec2 shadow_displacement = vec2(0.75,0.75);
-uniform float shadow_tightness = 10;
-uniform float shadow_radius = 0.7;
-uniform float shadow_thickness = 1.0;
-
-// Control of the slices
-uniform float stopAngles[5];
-uniform vec3 colours[5];
-
-// Trim
-uniform vec3 trim_colour;
-uniform float trim_size = 0.05;
-
-// The center is spotlighted by the gradient,
-//control its size and falloff with these
-uniform float gradient_falloff = 3.6;
-uniform float gradient_base = 3.1;
-
-// control whether this is a donut instead of a pie chart
-uniform bool donut = false;
-uniform bool donut_inner_trim = false;
-uniform float donut_inner_radius = 0.15;
-
-// get the polar coordinates of a pixel relative to the center
-vec2 getPolar(vec2 UVin, vec2 center){
- vec2 relcoord = (UVin-center);
- float dist = length(relcoord);
- float theta = PI/2.0 + atan((relcoord.y)/(relcoord.x));
- if(UVin.x < 0.5){
- theta += PI;
- }
- return vec2(dist,theta);
-}
-
-// from thebookofshaders, returns a gradient falloff
-float parabola( float base, float x, float k ){
- return pow( base*x*(1.0-x), k );
-}
-
-float parabola_shadow(float base, float x){
- return base*x*x;
-}
-
-void fragment() {
- vec2 coords = getPolar(UV,vec2(0.5,0.5));
- float dist = coords.x;
- float theta = coords.y;
-
- vec2 shadow_polar = getPolar(UV,vec2(0.0+shadow_displacement.x,0.0+shadow_displacement.y));
- 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){
- COLOR = vec4(0.1,0.1,0.1,shadow_gradient);
- }
- // inner trim
- else if(donut && donut_inner_trim && dist <= donut_inner_radius + trim_size){
- COLOR = vec4(trim_colour,1.0);
- }
- // interior
- else if(dist <= radius-trim_size){
- for(int i=0;i<stopAngles.length();i++){
- if(theta <= stopAngles[i]){
- float gradient = parabola(gradient_base,dist,gradient_falloff);
- COLOR = vec4(colours[i]*(1.0-gradient),1.0);
- break;
- }
- }
- }
- // outer trim
- else if(dist <= radius){
- COLOR = vec4(trim_colour,1.0);
- }
- //outside the circle
- else{
- COLOR = vec4(0.1,0.1,0.1,shadow_gradient);
- }
-}"
-
-[sub_resource type="ShaderMaterial" id="ShaderMaterial_5ilmo"]
-resource_name = "Piechart_shader"
-shader = SubResource("Shader_vq8ad")
-shader_parameter/radius = 0.4
-shader_parameter/shadow_displacement = Vector2(0.75, 0.75)
-shader_parameter/shadow_tightness = 10.0
-shader_parameter/shadow_radius = 0.0
-shader_parameter/shadow_thickness = 1.0
-shader_parameter/stopAngles = [3.14159, 5.02655, 6.28319]
-shader_parameter/colours = [Vector3(0, 0, 1), Vector3(1, 1, 0), Vector3(0.4, 0, 0.6)]
-shader_parameter/trim_colour = Vector3(0, 0, 0)
-shader_parameter/trim_size = 0.02
-shader_parameter/gradient_falloff = 3.6
-shader_parameter/gradient_base = 3.1
-shader_parameter/donut = true
-shader_parameter/donut_inner_trim = true
-shader_parameter/donut_inner_radius = 0.2
-
-[sub_resource type="CanvasTexture" id="CanvasTexture_x3pc8"]
-
-[node name="LayeredChart" type="CenterContainer"]
-offset_right = 50.0
-offset_bottom = 50.0
-script = ExtResource("1_wxoci")
-
-[node name="PieChart" parent="." instance=ExtResource("1_cdu1k")]
-material = SubResource("ShaderMaterial_6ril8")
-layout_mode = 2
-size_flags_horizontal = 4
-size_flags_vertical = 4
-texture = SubResource("CanvasTexture_pwdgy")
-donut_inner_radius = 0.102
-shadow_focus = 2.0
-shadow_radius = 0.75
-
-[node name="PieChart2" parent="." instance=ExtResource("1_cdu1k")]
-material = SubResource("ShaderMaterial_5ilmo")
-layout_mode = 2
-size_flags_horizontal = 4
-size_flags_vertical = 4
-texture = SubResource("CanvasTexture_x3pc8")
-donut = true
-donut_inner_radius = 0.4
-shadow_displacement = Vector2(0.75, 0.75)
-shadow_focus = 10.0
-shadow_radius = 0.0
diff --git a/game/src/Game/Theme/PieChart/PieChart.gd b/game/src/Game/Theme/PieChart/PieChart.gd
index aba1f81..b06dacf 100644
--- a/game/src/Game/Theme/PieChart/PieChart.gd
+++ b/game/src/Game/Theme/PieChart/PieChart.gd
@@ -5,231 +5,212 @@ class_name PieChart
@export var donut : bool = false
-@export_range(0.0,1.0) var donut_inner_radius : float = 0.5
-@export_range(0.0,0.5) var radius : float = 0.4
-@export var shadow_displacement : Vector2 = Vector2(0.6,0.6)
-@export var shadow_focus : float = 1.0
+@export_range(0.0, 1.0) var donut_inner_radius : float = 0.5
+@export_range(0.0, 0.5) var radius : float = 0.4
+@export var shadow_displacement : Vector2 = Vector2(0.55, 0.6)
+@export var shadow_tightness : float = 1.0
@export var shadow_radius : float = 0.6
@export var shadow_thickness : float = 1.0
-@export var trim_colour : Color = Color(0.0,0.0,0.0)
-@export_range(0.0,1.0) var trim_size : float = 0.02
+@export var trim_colour : Color = Color(0.0, 0.0, 0.0)
+@export_range(0.0, 1.0) var trim_size : float = 0.02
@export var donut_inner_trim : bool = true
@export var slice_gradient_falloff : float = 3.6
@export var slice_gradient_base : float = 3.1
-#@onready
-@export var RichTooltip : RichTextLabel# = $RichToolTip
+@export var _rich_tooltip : RichTextLabel
+var _pie_chart_image : Image
-#a data class for the pie chart
+# A data class for the pie chart
class SliceData:
- #primary properties, change these to change
- #the displayed piechart
- var colour:Color = Color(1.0,0.0,0.0):
- get:
- return colour
- set(value):
- colour = value
- var tooltip:String = "DEFAULT":
- get:
- return tooltip
- set(value):
- tooltip = value
- var quantity:float = -1:
- get:
- return quantity
- set(value):
- quantity = value
- #derived properties, don't set from an external script
- var final_angle:float = -1:
- get:
- return final_angle
- set(value):
- final_angle = value
- var percentage:float = 0:
+ extends RefCounted
+ # Primary properties, change these to change
+ # the displayed piechart
+ var colour : Color = Color(1.0, 0.0, 0.0)
+ var tooltip : String = ""
+ var quantity : float = -1
+ # Derived properties, don't set from an external script
+ var final_angle : float = -1
+ var percentage : float = 0:
get:
return percentage
set(value):
- percentage = clampf(value,0,1)
+ percentage = clampf(value, 0, 1)
- func _init(quantityIn:float,tooltipIn:String,colourIn:Color):
+ func _init(quantityIn : float, tooltipIn : String, colourIn : Color):
colour = colourIn
tooltip = tooltipIn
quantity = quantityIn
-#The key of an entry of this dictionnary should be an easy to reference constant
-#The tooltip label is what the user will actually read
-var slices: Dictionary = {
+# The key of an entry of this dictionary should be an easy to reference constant
+# The tooltip label is what the user will actually read
+var _slices : Dictionary = {}
+
+# Slice keys/labels in the order they should be displayed
+var _slice_order : Array = []
-}
-#example slices:
+# Example slices:
"""
- "label1":SliceData.new(5,"Conservative",Color(0.0,0.0,1.0)),
- "label2":SliceData.new(3,"Liberal",Color(1.0,1.0,0.0)),
- "label3":SliceData.new(2,"Reactionary",Color(0.4,0.0,0.6))
+ "label1": SliceData.new(5, "Conservative", Color(0.0, 0.0, 1.0)),
+ "label2": SliceData.new(3, "Liberal", Color(1.0, 1.0, 0.0)),
+ "label3": SliceData.new(2, "Reactionary", Color(0.4, 0.0, 0.6))
"""
-#These functions are the interface a developer will use to update the piechart
-#The piechart will only redraw once one of these has been triggered
-func addOrReplaceLabel(labelName:String,quantity:float,tooltip:String,colour:Color=Color(0.0,0.0,0.0)) -> void:
- slices[labelName] = SliceData.new(quantity,tooltip,colour)
+# These functions are the interface a developer will use to update the piechart
+# The piechart will only redraw once one of these has been triggered
+func add_or_replace_label(labelName : String, quantity : float, tooltip : String, colour : Color = Color(0.0, 0.0, 0.0)) -> void:
+ _slices[labelName] = SliceData.new(quantity, tooltip, colour)
+ if _slice_order.find(labelName) == -1:
+ _slice_order.push_back(labelName)
_recalculate()
-func updateLabelQuantity(labelName:String,quantity:float) -> void:
- if slices.has(labelName):
- slices[labelName].quantity = quantity
+func update_label_quantity(labelName : String, quantity : float) -> void:
+ if _slices.has(labelName):
+ _slices[labelName].quantity = quantity
_recalculate()
-func updateLabelColour(labelName:String,colour:Color) -> void:
- if slices.has(labelName):
- slices[labelName].colour = colour
+func update_label_colour(labelName : String, colour : Color) -> void:
+ if _slices.has(labelName):
+ _slices[labelName].colour = colour
_recalculate()
-func updateLabelTooltip(labelName:String,tooltip:String) -> void:
- if slices.has(labelName):
- slices[labelName].tooltip = tooltip
+func update_label_tooltip(labelName : String, tooltip : String) -> void:
+ if _slices.has(labelName):
+ _slices[labelName].tooltip = tooltip
+
+func remove_label(labelName : String) -> bool:
+ if _slices.erase(labelName):
+ var index := _slice_order.find(labelName)
+ if index == -1:
+ push_error("Slice in dictionary but not order list: ", labelName)
+ else:
+ _slice_order.remove_at(index)
+ _recalculate()
+ return true
+ return false
-func RemoveLabel(labelName:String) -> bool:
- var out = slices.erase(labelName)
+func clear_slices() -> void:
+ _slices.clear()
+ _slice_order.clear()
+
+# Distribution dictionary of the form:
+# { "<label>": { "size": <quantity>, "colour": <colour> } }
+func set_to_distribution(dist : Dictionary) -> void:
+ clear_slices()
+ for key in dist:
+ var entry : Dictionary = dist[key]
+ _slices[key] = SliceData.new(entry[GameSingleton.get_culture_info_size_key()], key, entry[GameSingleton.get_culture_info_colour_key()])
+ _slice_order = _slices.keys()
+ sort_slices()
+
+# Sorted by quantity, smallest to largest, so that the smallest slice
+# is to the left of a radial line straight upwards
+func sort_slices() -> void:
+ _slice_order.sort_custom(func (a, b): return _slices[a].quantity < _slices[b].quantity)
_recalculate()
- return out
-
-#Perhaps in the future, a method to reorder the labels?
-
-
-#In editor only, force the shader parameters to update whenever _draw
-#is called so developers can see their changes
-#otherwise, for performance, reduce the number of material resets
-func _draw():
- if Engine.is_editor_hint():
- if not material:
- _reset_material()
- _setShaderParams()
- _recalculate()
func _ready():
- _reset_material()
- _setShaderParams()
-
-func _reset_material():
- texture = CanvasTexture.new()
- var mat_res = load("res://src/Game/Theme/PieChart/PieChartMat.tres")
- material = mat_res.duplicate(true)
- custom_minimum_size = Vector2(50.0,50.0)
- size_flags_horizontal = Control.SIZE_SHRINK_CENTER
- size_flags_vertical = Control.SIZE_SHRINK_CENTER
- _recalculate()
+ if not Engine.is_editor_hint():
+ const size : int = 256
+ _pie_chart_image = Image.create(size, size, false, Image.FORMAT_RGBA8)
+ texture = ImageTexture.create_from_image(_pie_chart_image)
+ _recalculate()
-func _setShaderParams():
- material.set_shader_parameter("donut",donut)
- material.set_shader_parameter("donut_inner_trim",donut_inner_trim)
- material.set_shader_parameter("radius",radius)
- material.set_shader_parameter("donut_inner_radius",donut_inner_radius/2.0)
-
- material.set_shader_parameter("trim_colour",Vector3(trim_colour.r,trim_colour.g,trim_colour.b))
- material.set_shader_parameter("trim_size",trim_size)
- material.set_shader_parameter("gradient_falloff",slice_gradient_falloff)
- material.set_shader_parameter("gradient_base",slice_gradient_base)
-
- material.set_shader_parameter("shadow_displacement",shadow_displacement)
- material.set_shader_parameter("shadow_tightness",shadow_focus)
- material.set_shader_parameter("shadow_radius",shadow_radius)
- material.set_shader_parameter("shadow_thickness",shadow_thickness)
-
-
-#Update the slice angles based on the new slice data
+# Update the slice angles based on the new slice data
func _recalculate() -> void:
- #where the slices are the public interface, these are the actual paramters
- #which will be sent to the shader
- var angles: Array = []
- var colours: Array = []
-
- var total:float = 0
- for slice in slices.values():
- total += slice.quantity
-
- var current_arc_start:float = 0
- var current_arc_finish:float = 0
-
- for slice in slices.values():
- slice.percentage = slice.quantity / total
- var rads_to_cover:float = slice.percentage * 2.0*PI
- current_arc_finish = current_arc_start + rads_to_cover
- slice.final_angle = current_arc_finish
- current_arc_start = current_arc_finish
- angles.push_back(current_arc_finish)
- colours.push_back(Vector3(slice.colour.r,slice.colour.g,slice.colour.b) )
- material.set_shader_parameter("stopAngles",angles)
- material.set_shader_parameter("colours",colours)
-
-
-#Process mouse to select the appropriate tooltip for the slice
-func _gui_input(event:InputEvent):
+ # Where the slices are the public interface, these are the actual paramters
+ # which will be sent to the shader/draw function
+ var angles : Array = []
+ var colours : Array = []
+
+ var total : float = 0
+ for label in _slice_order:
+ var quantity : float = _slices[label].quantity
+ if quantity > 0:
+ total += quantity
+
+ var current_arc_start : float = 0
+ var current_arc_finish : float = 0
+
+ for label in _slice_order:
+ var slice : SliceData = _slices[label]
+ if slice.quantity > 0:
+ slice.percentage = slice.quantity / total
+ var rads_to_cover : float = slice.percentage * 2.0 * PI
+ current_arc_finish = current_arc_start + rads_to_cover
+ slice.final_angle = current_arc_finish
+ current_arc_start = current_arc_finish
+ angles.push_back(current_arc_finish)
+ colours.push_back(slice.colour)
+
+ GameSingleton.draw_pie_chart(_pie_chart_image, angles, colours, radius, shadow_displacement, shadow_tightness, shadow_radius, shadow_thickness,
+ trim_colour, trim_size, slice_gradient_falloff, slice_gradient_base, donut, donut_inner_trim, donut_inner_radius / 2)
+ texture.set_image(_pie_chart_image)
+
+# Process mouse to select the appropriate tooltip for the slice
+func _gui_input(event : InputEvent):
if event is InputEventMouse:
- var pos = event.position
- var _handled:bool = _handleTooltip(pos)
+ var pos : Vector2 = event.position
+ var _handled : bool = _handle_tooltip(pos)
func _on_mouse_exited():
- RichTooltip.visible = false
-
-#takes a mouse position, and sets an appropriate tooltip for the slice the mouse
-#is hovered over. Returns a boolean on whether the tooltip was handled.
-func _handleTooltip(pos:Vector2) -> bool:
- #is it within the circle?
- var center = Vector2(size.x/2.0, size.x/2.0)
- var radius = size.x/2.0
- var distance = center.distance_to(pos)
- #print(distance >= donut_inner_radius/2.0)
- var real_donut_inner_radius:float = radius * donut_inner_radius
+ _rich_tooltip.visible = false
+
+# Takes a mouse position, and sets an appropriate tooltip for the slice the mouse
+# is hovered over. Returns a boolean on whether the tooltip was handled.
+func _handle_tooltip(pos : Vector2) -> bool:
+ # Is it within the circle?
+ var radius := size.x / 2.0
+ var center := Vector2(radius, radius)
+ var distance := center.distance_to(pos)
+ var real_donut_inner_radius : float = radius * donut_inner_radius
if distance <= radius and (not donut or distance >= real_donut_inner_radius):
- var angle = _convertAngle(center.angle_to_point(pos))
- for label in slices.keys():
- var slice = slices.get(label)
- if angle <= slice.final_angle:
- RichTooltip.visible = true
- RichTooltip.text = _createTooltip(label)
- RichTooltip.position = pos + Vector2(5,5) + get_global_rect().position #get_global_rect().position +
- RichTooltip.reset_size()
-
- return true
+ if _slice_order.is_empty():
+ _rich_tooltip.text = "PIECHART_TOOLTIP_NO_DATA"
+ else:
+ var angle := _convert_angle(center.angle_to_point(pos))
+ var selected_label : String = ""
+ for label in _slice_order:
+ if angle <= _slices[label].final_angle:
+ if not selected_label or _slices[label].final_angle < _slices[selected_label].final_angle:
+ selected_label = label
+ if not selected_label:
+ selected_label = _slice_order[0]
+ _rich_tooltip.text = _create_tooltip(selected_label)
+ _rich_tooltip.visible = true
+ _rich_tooltip.position = pos + Vector2(5, 5) + get_global_rect().position
+ _rich_tooltip.reset_size()
else:
- #Technically the corners of the bounding box
- #are part of the chart, but we don't want a tooltip there
- RichTooltip.visible = false
- return false
-
-#create a list of all the values and percentages
-# but with the hovered one on top and highlighted
-func _createTooltip(labelHovered:String) -> String:
- var tooltip:String = ""
- var hoveredSlice = slices.get(labelHovered)
- var formatted_percent = _formatpercent(hoveredSlice.percentage)
- #TOOD: perhaps this is a bit much, but final feedback should determine this
- tooltip += "[font_size=10][i][u][b]>> {name} {percentage}% <<[/b][/u][/i]".format(
- {"name":hoveredSlice.tooltip,"percentage":formatted_percent})
-
- for label in slices.keys():
- if label == labelHovered: continue
- var slice = slices.get(label)
- var percent = _formatpercent(slice.percentage)
- tooltip += "\n{name} {percentage}%".format(
- {"name":slice.tooltip,"percentage":percent})
- tooltip += "[/font_size]"
- return tooltip
-
-#angle from center.angle_to_point is measured from the +x axis
-#, but the chart starts from +y
-#the input angle is also -180 to 180, where we want 0 to 360
-func _convertAngle(angleIn:float) -> float:
- #make the angle start from +y, range is now -90 to 270
- var angle = angleIn + PI/2.0
- #adjust range to be 0 to 360
+ # Technically the corners of the bounding box
+ # are part of the chart, but we don't want a tooltip there
+ _rich_tooltip.visible = false
+ return _rich_tooltip.visible
+
+# Create a list of all the values and percentages
+# with the hovered one highlighted
+func _create_tooltip(labelHovered : String) -> String:
+ var slice_tooltips : PackedStringArray
+ for label in _slice_order:
+ var slice : SliceData = _slices.get(label)
+ var percent := _format_percent(slice.percentage)
+ var entry : String = "%s %s%%" % [label, percent]
+ if label == labelHovered:
+ entry = "[i][u][b]>>%s<<[/b][/u][/i]" % entry
+ slice_tooltips.push_back(entry)
+ # Slices are ordered smallest to largest, but here we want the opposite
+ slice_tooltips.reverse()
+ return "[font_size=10]%s[/font_size]" % "\n".join(slice_tooltips)
+
+# Angle from center.angle_to_point is measured from the +x axis,
+# but the chart starts from +y
+# The input angle is also -180 to 180, where we want 0 to 360
+func _convert_angle(angleIn : float) -> float:
+ # Make the angle start from +y, range is now -90 to 270
+ var angle := angleIn + PI / 2.0
+ # Adjust range to be 0 to 360
if angle < 0:
- angle = 2.0*PI + angle
+ angle += 2.0 * PI
return angle
-
-func _formatpercent(percentIn:float) -> float:
- return snappedf((percentIn * 100),0.1)
-
-
+func _format_percent(percentIn : float) -> float:
+ return snappedf((percentIn * 100), 0.1)
diff --git a/game/src/Game/Theme/PieChart/PieChart.gdshader b/game/src/Game/Theme/PieChart/PieChart.gdshader
deleted file mode 100644
index 1707b3d..0000000
--- a/game/src/Game/Theme/PieChart/PieChart.gdshader
+++ /dev/null
@@ -1,87 +0,0 @@
-shader_type canvas_item;
-
-// The center in UV coordinates, which will always
-//be 0.5, the actual radius will be controlled by the control node
-//const vec2 center = vec2(0.5,0.5);
-uniform float radius = 0.4;
-
-//shadow
-uniform vec2 shadow_displacement = vec2(0.75,0.75);
-uniform float shadow_tightness = 10;
-uniform float shadow_radius = 0.7;
-uniform float shadow_thickness = 1.0;
-
-// Control of the slices
-uniform float stopAngles[5];
-uniform vec3 colours[5];
-
-// Trim
-uniform vec3 trim_colour;
-uniform float trim_size = 0.05;
-
-// The center is spotlighted by the gradient,
-//control its size and falloff with these
-uniform float gradient_falloff = 3.6;
-uniform float gradient_base = 3.1;
-
-// control whether this is a donut instead of a pie chart
-uniform bool donut = false;
-uniform bool donut_inner_trim = false;
-uniform float donut_inner_radius = 0.15;
-
-// get the polar coordinates of a pixel relative to the center
-vec2 getPolar(vec2 UVin, vec2 center){
- vec2 relcoord = (UVin-center);
- float dist = length(relcoord);
- float theta = PI/2.0 + atan((relcoord.y)/(relcoord.x));
- if(UVin.x < 0.5){
- theta += PI;
- }
- return vec2(dist,theta);
-}
-
-// from thebookofshaders, returns a gradient falloff
-float parabola( float base, float x, float k ){
- return pow( base*x*(1.0-x), k );
-}
-
-float parabola_shadow(float base, float x){
- return base*x*x;
-}
-
-void fragment() {
- vec2 coords = getPolar(UV,vec2(0.5,0.5));
- float dist = coords.x;
- float theta = coords.y;
-
- vec2 shadow_polar = getPolar(UV,vec2(0.0+shadow_displacement.x,0.0+shadow_displacement.y));
- 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){
- COLOR = vec4(0.1,0.1,0.1,shadow_gradient);
- }
- // inner trim
- else if(donut && donut_inner_trim && dist <= donut_inner_radius + trim_size){
- COLOR = vec4(trim_colour,1.0);
- }
- // interior
- else if(dist <= radius-trim_size){
- for(int i=0;i<stopAngles.length();i++){
- if(theta <= stopAngles[i]){
- float gradient = parabola(gradient_base,dist,gradient_falloff);
- COLOR = vec4(colours[i]*(1.0-gradient),1.0);
- break;
- }
- }
- }
- // outer trim
- else if(dist <= radius){
- COLOR = vec4(trim_colour,1.0);
- }
- //outside the circle
- else{
- COLOR = vec4(0.1,0.1,0.1,shadow_gradient);
- }
-} \ No newline at end of file
diff --git a/game/src/Game/Theme/PieChart/PieChart.tscn b/game/src/Game/Theme/PieChart/PieChart.tscn
index a0d136e..18fb1f1 100644
--- a/game/src/Game/Theme/PieChart/PieChart.tscn
+++ b/game/src/Game/Theme/PieChart/PieChart.tscn
@@ -1,118 +1,8 @@
-[gd_scene load_steps=5 format=3 uid="uid://cr7p1k2xm7mum"]
+[gd_scene load_steps=2 format=3 uid="uid://cr7p1k2xm7mum"]
[ext_resource type="Script" path="res://src/Game/Theme/PieChart/PieChart.gd" id="2_ub6u3"]
-[sub_resource type="Shader" id="Shader_0kffx"]
-code = "shader_type canvas_item;
-
-// The center in UV coordinates, which will always
-//be 0.5, the actual radius will be controlled by the control node
-//const vec2 center = vec2(0.5,0.5);
-uniform float radius = 0.4;
-
-//shadow
-uniform vec2 shadow_displacement = vec2(0.75,0.75);
-uniform float shadow_tightness = 10;
-uniform float shadow_radius = 0.7;
-uniform float shadow_thickness = 1.0;
-
-// Control of the slices
-uniform float stopAngles[5];
-uniform vec3 colours[5];
-
-// Trim
-uniform vec3 trim_colour;
-uniform float trim_size = 0.05;
-
-// The center is spotlighted by the gradient,
-//control its size and falloff with these
-uniform float gradient_falloff = 3.6;
-uniform float gradient_base = 3.1;
-
-// control whether this is a donut instead of a pie chart
-uniform bool donut = false;
-uniform bool donut_inner_trim = false;
-uniform float donut_inner_radius = 0.15;
-
-// get the polar coordinates of a pixel relative to the center
-vec2 getPolar(vec2 UVin, vec2 center){
- vec2 relcoord = (UVin-center);
- float dist = length(relcoord);
- float theta = PI/2.0 + atan((relcoord.y)/(relcoord.x));
- if(UVin.x < 0.5){
- theta += PI;
- }
- return vec2(dist,theta);
-}
-
-// from thebookofshaders, returns a gradient falloff
-float parabola( float base, float x, float k ){
- return pow( base*x*(1.0-x), k );
-}
-
-float parabola_shadow(float base, float x){
- return base*x*x;
-}
-
-void fragment() {
- vec2 coords = getPolar(UV,vec2(0.5,0.5));
- float dist = coords.x;
- float theta = coords.y;
-
- vec2 shadow_polar = getPolar(UV,vec2(0.0+shadow_displacement.x,0.0+shadow_displacement.y));
- 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){
- COLOR = vec4(0.1,0.1,0.1,shadow_gradient);
- }
- // inner trim
- else if(donut && donut_inner_trim && dist <= donut_inner_radius + trim_size){
- COLOR = vec4(trim_colour,1.0);
- }
- // interior
- else if(dist <= radius-trim_size){
- for(int i=0;i<stopAngles.length();i++){
- if(theta <= stopAngles[i]){
- float gradient = parabola(gradient_base,dist,gradient_falloff);
- COLOR = vec4(colours[i]*(1.0-gradient),1.0);
- break;
- }
- }
- }
- // outer trim
- else if(dist <= radius){
- COLOR = vec4(trim_colour,1.0);
- }
- //outside the circle
- else{
- COLOR = vec4(0.1,0.1,0.1,shadow_gradient);
- }
-}"
-
-[sub_resource type="ShaderMaterial" id="ShaderMaterial_aadl6"]
-resource_name = "Piechart_shader"
-shader = SubResource("Shader_0kffx")
-shader_parameter/radius = 0.4
-shader_parameter/shadow_displacement = Vector2(0.6, 0.6)
-shader_parameter/shadow_tightness = 1.0
-shader_parameter/shadow_radius = 0.6
-shader_parameter/shadow_thickness = 1.0
-shader_parameter/stopAngles = [3.14159, 5.02655, 6.28319]
-shader_parameter/colours = [Vector3(0, 0, 1), Vector3(1, 1, 0), Vector3(0.4, 0, 0.6)]
-shader_parameter/trim_colour = Vector3(0, 0, 0)
-shader_parameter/trim_size = 0.02
-shader_parameter/gradient_falloff = 3.6
-shader_parameter/gradient_base = 3.1
-shader_parameter/donut = false
-shader_parameter/donut_inner_trim = true
-shader_parameter/donut_inner_radius = 0.25
-
-[sub_resource type="CanvasTexture" id="CanvasTexture_xntmh"]
-
-[node name="PieChart" type="TextureRect" node_paths=PackedStringArray("RichTooltip")]
-material = SubResource("ShaderMaterial_aadl6")
+[node name="PieChart" type="TextureRect" node_paths=PackedStringArray("_rich_tooltip")]
custom_minimum_size = Vector2(50, 50)
anchors_preset = -1
anchor_right = 0.039
@@ -121,10 +11,9 @@ offset_right = -32.92
offset_bottom = -34.68
size_flags_horizontal = 4
size_flags_vertical = 4
-texture = SubResource("CanvasTexture_xntmh")
expand_mode = 3
script = ExtResource("2_ub6u3")
-RichTooltip = NodePath("RichToolTip")
+_rich_tooltip = NodePath("RichToolTip")
[node name="RichToolTip" type="RichTextLabel" parent="."]
visible = false
diff --git a/game/src/Game/Theme/PieChart/PieChartMat.tres b/game/src/Game/Theme/PieChart/PieChartMat.tres
deleted file mode 100644
index 449c7ba..0000000
--- a/game/src/Game/Theme/PieChart/PieChartMat.tres
+++ /dev/null
@@ -1,21 +0,0 @@
-[gd_resource type="ShaderMaterial" load_steps=2 format=3 uid="uid://bl2arkbisbk8f"]
-
-[ext_resource type="Shader" path="res://src/Game/Theme/PieChart/PieChart.gdshader" id="1_j23w0"]
-
-[resource]
-resource_name = "Piechart_shader"
-shader = ExtResource("1_j23w0")
-shader_parameter/radius = 0.5
-shader_parameter/shadow_displacement = Vector2(0.75, 0.75)
-shader_parameter/shadow_tightness = 10.0
-shader_parameter/shadow_radius = 0.8
-shader_parameter/shadow_thickness = 1.0
-shader_parameter/stopAngles = [3.14159, 5.02655, 6.28319]
-shader_parameter/colours = [Vector3(0, 0, 1), Vector3(1, 1, 0), Vector3(0.4, 0, 0.6)]
-shader_parameter/trim_colour = Vector3(0, 0, 0)
-shader_parameter/trim_size = 0.02
-shader_parameter/gradient_falloff = 3.6
-shader_parameter/gradient_base = 3.1
-shader_parameter/donut = false
-shader_parameter/donut_inner_trim = true
-shader_parameter/donut_inner_radius = 0.25
diff --git a/game/src/Game/Theme/PieChart/chart_test.gd b/game/src/Game/Theme/PieChart/chart_test.gd
deleted file mode 100644
index 8a3b865..0000000
--- a/game/src/Game/Theme/PieChart/chart_test.gd
+++ /dev/null
@@ -1,32 +0,0 @@
-extends Control
-
-
-
-# Called when the node enters the scene tree for the first time.
-func _ready():
- var chart:PieChart = $LayeredChart/OuterPieChart
- chart.addOrReplaceLabel("label4",3,"Socialist",Color(1.0,0.0,0.0))
-
- var chart2:PieChart = $PieChart3
- chart2.RemoveLabel("label1")
- chart2.RemoveLabel("label2")
- chart2.RemoveLabel("label3")
- chart2.RemoveLabel("label4")
-
- #$PieChart
- #$PieChart2
- #$PieChart/PieChart2
- var chart3:PieChart = $LayeredChart/InnerPieChart
- chart3.RemoveLabel("label1")
- chart3.RemoveLabel("label2")
- chart3.RemoveLabel("label3")
- chart3.addOrReplaceLabel("a",3,"",Color(1.0,0.0,0.0))
- chart3.addOrReplaceLabel("b",3,"Y",Color(0.0,1.0,0.0))
- chart3.addOrReplaceLabel("c",3,"Z",Color(0.0,0.0,1.0))
-
-
- var chart4:PieChart = $PieChart4
- chart4.addOrReplaceLabel("hi",3,"antidisenstablishmentarianist antidisenstablishmentarianism",Color(0.0,0.5,0.5))
-# Called every frame. 'delta' is the elapsed time since the previous frame.
-func _process(delta):
- pass