@tool extends TextureRect 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.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 donut_inner_trim : bool = true @export var slice_gradient_falloff : float = 3.6 @export var slice_gradient_base : float = 3.1 @export var _rich_tooltip : RichTextLabel var _pie_chart_image : Image # A data class for the pie chart class SliceData: 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) func _init(quantityIn : float, tooltipIn : String, colourIn : Color): colour = colourIn tooltip = tooltipIn quantity = quantityIn # 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: """ "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 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 update_label_quantity(labelName : String, quantity : float) -> void: if _slices.has(labelName): _slices[labelName].quantity = quantity _recalculate() func update_label_colour(labelName : String, colour : Color) -> void: if _slices.has(labelName): _slices[labelName].colour = colour _recalculate() 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 clear_slices() -> void: _slices.clear() _slice_order.clear() # Distribution dictionary of the form: # { "