diff options
Diffstat (limited to 'game/src')
-rw-r--r-- | game/src/Game/GameSession/ProvinceOverviewPanel/ProvinceOverviewPanel.gd | 11 | ||||
-rw-r--r-- | game/src/Game/GameSession/ProvinceOverviewPanel/ProvinceOverviewPanel.tscn | 127 | ||||
-rw-r--r-- | game/src/Game/Theme/PieChart/LayeredChart.gd | 28 | ||||
-rw-r--r-- | game/src/Game/Theme/PieChart/LayeredChart.tscn | 249 | ||||
-rw-r--r-- | game/src/Game/Theme/PieChart/PieChart.gd | 355 | ||||
-rw-r--r-- | game/src/Game/Theme/PieChart/PieChart.gdshader | 87 | ||||
-rw-r--r-- | game/src/Game/Theme/PieChart/PieChart.tscn | 117 | ||||
-rw-r--r-- | game/src/Game/Theme/PieChart/PieChartMat.tres | 21 | ||||
-rw-r--r-- | game/src/Game/Theme/PieChart/chart_test.gd | 32 |
9 files changed, 189 insertions, 838 deletions
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 |