Rollercoaster-Derivatives/addons/easy_charts/control_charts/plotters/pie_plotter.gd

128 lines
3.8 KiB
GDScript

extends FunctionPlotter
class_name PiePlotter
signal point_entered(slice_mid_point, function, props)
signal point_exited(slice_mid_point, function)
var radius_multiplayer: float = 1.0
#### INTERNAL
var box: Rect2
var radius: float
var slices: Array = []
var slices_dirs: PackedVector2Array = []
var focused_point: Point
func _init(function: Function) -> void:
super(function)
pass
func _draw() -> void:
super._draw()
box = get_box()
radius = min(box.size.x, box.size.y) * 0.5 * radius_multiplayer
var total: float = get_total()
var ratios: PackedFloat32Array = get_ratios(total)
sample(radius, box.get_center(), total, ratios)
_draw_pie()
_draw_labels(radius, box.get_center(), ratios)
func get_total() -> float:
# Calculate total and ratios
var total: float = 0.0
for value in function.__x:
total += float(value)
return total
func get_ratios(total: float) -> PackedFloat32Array:
var ratios: PackedFloat32Array = []
for value in function.__x:
ratios.push_back(value / total * 100)
return ratios
func sample(radius: float, center: Vector2, total: float, ratios: PackedFloat32Array) -> void:
# Calculate directions
slices.clear()
slices_dirs = []
var start_angle: float = 0.0
for ratio in ratios:
var end_angle: float = start_angle + (2 * PI * float(ratio) * 0.01)
slices.append(
_calc_circle_arc_poly(
center,
radius,
start_angle,
end_angle
)
)
start_angle = end_angle
for slice in slices:
var mid_point: Vector2 = (slice[-1] + slice[1]) / 2
draw_circle(mid_point, 5, Color.WHITE)
slices_dirs.append(center.direction_to(mid_point))
func _calc_circle_arc_poly(center: Vector2, radius: float, angle_from: float, angle_to: float) -> PackedVector2Array:
var nb_points: int = 64
var points_arc: PackedVector2Array = PackedVector2Array()
points_arc.push_back(center)
for i in range(nb_points + 1):
var angle_point: float = - (PI / 2) + angle_from + i * (angle_to - angle_from) / nb_points
points_arc.push_back(center + (Vector2.RIGHT.rotated(angle_point).normalized() * radius))
return points_arc
func _draw_pie() -> void:
for i in slices.size():
draw_colored_polygon(slices[i], function.get_gradient().sample(float(i) / float(slices.size() - 1)))
draw_polyline(slices[i], Color.WHITE, 2.0, true)
func _draw_labels(radius: float, center: Vector2, ratios: PackedFloat32Array) -> void:
for i in slices_dirs.size():
var ratio_lbl: String = "%.1f%%" % ratios[i]
var value_lbl: String = "(%s)" % function.__x[i]
var position: Vector2 = center + slices_dirs[i] * radius * 0.5
var ratio_lbl_size: Vector2 = get_chart_properties().get_string_size(ratio_lbl)
var value_lbl_size: Vector2 = get_chart_properties().get_string_size(value_lbl)
draw_string(
get_chart_properties().font,
position - Vector2(ratio_lbl_size.x / 2, 0),
ratio_lbl,
HORIZONTAL_ALIGNMENT_CENTER,
ratio_lbl_size.x,
16,
Color.WHITE,
3,
TextServer.DIRECTION_AUTO,
TextServer.ORIENTATION_HORIZONTAL
)
draw_string(
get_chart_properties().font,
position - Vector2(value_lbl_size.x / 2, - value_lbl_size.y),
value_lbl,
HORIZONTAL_ALIGNMENT_CENTER,
value_lbl_size.x,
16,
Color.WHITE,
3,TextServer.DIRECTION_AUTO,TextServer.ORIENTATION_HORIZONTAL
)
func _input(event: InputEvent) -> void:
if event is InputEventMouse:
for i in slices.size():
if Geometry2D.is_point_in_polygon(get_relative_position(event.position), slices[i]):
var point: Point = Point.new(self.box.get_center() + slices_dirs[i] * self.radius * 0.5, { x = function.__x[i], y = function.__y[i] })
if focused_point == point:
return
else:
focused_point = point
emit_signal("point_entered", focused_point, function, { interpolation_index = float(i) / float(slices.size() - 1)})
return
# Mouse is not in any slice's box
emit_signal("point_exited", focused_point, function)
focused_point = null