Skip to content

Instantly share code, notes, and snippets.

@Mithradates528
Created September 30, 2025 20:21
Show Gist options
  • Select an option

  • Save Mithradates528/0ea694291cc2e447ce4de9181671f15e to your computer and use it in GitHub Desktop.

Select an option

Save Mithradates528/0ea694291cc2e447ce4de9181671f15e to your computer and use it in GitHub Desktop.
Pie Chart - Godot 3.5
extends Node2D
# Data for the pie chart
export var data := [
{"Name":"Trade", "value": 8.0, "color": Color.burlywood},
{"Name":"Pupets", "value": 16.0, "color": Color.deeppink},
{"Name":"Production", "value": 26.0, "color": Color.greenyellow},
{"Name":"Taxes", "value": 35.0, "color": Color.aqua},
{"Name":"Occupation", "value": 50.0, "color": Color.cornflower}
]
# Chart properties
export var radius = 200
export var center = Vector2(400, 400)
# Animation properties
export var animation_duration = 1.0 # Duration in seconds
export var animation_time = 0.0
export var is_animating = true
# Customizable properties
var font: Font
export var font_size = 15
export var line_width = 3.0
export var line_color = Color(1, 1, 1)
export var use_gradient = false
export var gradient_start_color = Color(0.8, 0.2, 0.2)
export var gradient_end_color = Color(0.2, 0.2, 0.8)
# Hover properties
var hovered_slice = -1
var hover_scale = 1.1
var hover_animation_speed = 0.2
var current_hover_scale = 1.0
func _ready():
# Set up custom font
font = DynamicFont.new()
font.font_data = load("res://")
font.size = font_size
font.use_mipmaps = true
font.use_filter = true
set_process(true)
update()
export var UpdateInRealTime := false
func _process(delta):
if is_animating:
animation_time += delta
if animation_time >= animation_duration:
animation_time = animation_duration
is_animating = false
font.size = 15
update()
elif UpdateInRealTime:
update_data(data)
# Handle hover animation
var target_scale = 1.0
if hovered_slice != -1:
target_scale = hover_scale
current_hover_scale = lerp(current_hover_scale, target_scale, hover_animation_speed)
if abs(current_hover_scale - target_scale) > 0.01:
update()
func _draw():
var total = 0.00001
for item in data:
total += item.value
var current_angle = -PI/2 # Start from the top
var animation_progress = animation_time / animation_duration
for i in range(data.size()):
var item = data[i]
var full_angle = (item.value / total) * PI * 2
var current_slice_angle = full_angle * animation_progress
# Calculate scale for hover effect
var scale = 1.0
if i == hovered_slice:
scale = current_hover_scale
draw_slice(current_angle, current_slice_angle, item.color, scale, i, item.value, total)
# Draw labels
if animation_progress > 0.5: # Only show labels after animation is halfway
var label_angle = current_angle + (current_slice_angle / 2)
var OPRM = 1.15
var inner_point = center + Vector2(cos(label_angle), sin(label_angle)) * (radius * 1.05)
var outer_point = center + Vector2(cos(label_angle), sin(label_angle)) * (radius * OPRM)
# Determine which side of the pie we're on to align text
var is_right_side = cos(label_angle) > 0
# Draw connecting line
# draw_line(inner_point, outer_point, item.color, 2)
# Calculate the horizontal line endpoint
var horizontal_offset = 0 * (1 if is_right_side else -1)
var horizontal_point = Vector2(outer_point.x + horizontal_offset, outer_point.y)
# Draw percentage text
var percentage = str(round(float((item.value / total) * 100)) + (0 if data.find(data[i]) != (data.size()-1) else 1)
) + "%"
var text_position = horizontal_point
if is_right_side:
text_position.x += 5 # Add small padding for right-aligned text
else:
text_position.x -= font.get_string_size(percentage).x + 5 # Left-align text
text_position = Vector2(text_position.x, text_position.y + 6)
if int(percentage) != 0:
draw_string(font, text_position, percentage, item.color)
current_angle += current_slice_angle
func draw_slice(start_angle: float, angle: float, color: Color, scale: float = 1.0, slice_index: int = -1, Value := 1, Total := 1):
var points = PoolVector2Array()
points.push_back(center)
if Value == 0 || Total == 0:
return
var nb_points = round(max(float(32) / (float(Total) / (float(Value)*1.44)),3))
for i in range(nb_points + 1):
var angle_point = start_angle + i * angle / nb_points
var point = center + Vector2(cos(angle_point), sin(angle_point)) * (radius * scale)
points.push_back(point)
# Draw filled slice
var slice_color = color
if use_gradient:
var gradient = Gradient.new()
gradient.add_point(0.0, gradient_start_color)
gradient.add_point(1.0, gradient_end_color)
slice_color = gradient.interpolate(float(slice_index) / (data.size() - 1))
draw_colored_polygon(points, slice_color,PoolVector2Array(),null,null,true)
# Draw border lines
if line_width > 0:
# Draw outer arc
for i in range(nb_points):
var start_point = points[i + 1]
var end_point = points[i + 2]
draw_line(start_point, end_point, line_color, line_width, true)
# Draw lines to center
draw_line(points[1], center, line_color, line_width, true)
draw_line(points[points.size() - 1], center, line_color, line_width, true)
func _input(event):
if event is InputEventMouse:
var mouse_pos = event.position
check_hover(mouse_pos)
func check_hover(mouse_pos: Vector2):
var total = 0.0
for item in data:
total += item.value
var current_angle = -PI/2
var found_hover = false
var previous_hover = hovered_slice # Store the previous hover state
for i in range(data.size()):
var item = data[i]
var angle = (item.value / max(total,1)) * PI * 2
# Check if mouse is within this slice
var distance = mouse_pos.distance_to(center)
if distance <= radius:
var mouse_angle = (mouse_pos - center).angle()
# Normalize angle to be between -PI/2 and 3PI/2
if mouse_angle < -PI/2:
mouse_angle += PI * 2
var slice_start = current_angle
var slice_end = current_angle + angle
if mouse_angle >= slice_start and mouse_angle < slice_end:
if hovered_slice != i:
hovered_slice = i
found_hover = true
break
current_angle += angle
if !found_hover:
hovered_slice = -1
# Force update if we changed hover states
if previous_hover != hovered_slice:
current_hover_scale = 1.0 # Reset scale when changing slices
update()
# Configuration functions
func set_font(new_font: Font):
font = new_font
update()
func set_font_size(size: int):
font_size = size
if font:
font.size = font_size
update()
func set_line_properties(width: float, color: Color):
line_width = width
line_color = color
update()
func set_gradient_colors(start_color: Color, end_color: Color):
gradient_start_color = start_color
gradient_end_color = end_color
update()
func toggle_gradient(enabled: bool):
use_gradient = enabled
update()
func update_data(new_data):
data = new_data
is_animating = true
animation_time = 0.0
update()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment