add BarChart

This commit is contained in:
fenix-hub 2023-01-22 16:35:25 +01:00
parent 558e978723
commit 4983f61f5c
2 changed files with 198 additions and 0 deletions

View File

@ -0,0 +1,184 @@
extends Chart
class_name BarChart
signal bar_entered(bar)
# Size of a horizontal vector, which is calculated by `plot_box.size.x / x.size()`
var x_sector_size: float
# List of all unordered bars belonging to this plot
var bars: Array = []
# List of all bars, grouped by function
var function_bars: Array = []
# Currently focused bar
var focused_bar: Bar = null
func _clear_bars() -> void:
bars.clear()
function_bars.clear()
func _clear() -> void:
_clear_bars()
func _calc_x_domain() -> void:
pass
func _sample_x() -> void:
### @sampled_domain, which are the domain relative to the sampled values
### x (real value) --> sampling --> x_sampled (pixel value in canvas)
x_sampled_domain = Pair.new(plot_box.position.x, plot_box.end.x)
# samples
x_sampled = SampledAxis.new(x, x_sampled_domain)
x_sector_size = (x_sampled_domain.right - x_sampled_domain.left) / x.size()
func sort_ascending(a: String, b: String):
if a.length() < b.length():
return true
return false
func _find_longest_x() -> String:
var longest_x: String = ""
var x_str: Array = x.duplicate(true)
x_str.sort_custom(self, "sort_ascending")
return x_str.back()
func _draw_bar(bar: Bar, function_index: int) -> void:
draw_rect(
bar.rect,
chart_properties.get_function_color(function_index),
true,
1,
false
)
func _draw_bars() -> void:
for function in function_bars.size():
for i in range(0, function_bars[function].size()):
_draw_bar(
function_bars[function][i],
function
)
func _get_tick_label(line_index: int, line_value: float) -> String:
return x[line_index]
func _get_vertical_tick_label_pos(base_position: Vector2, text: String) -> Vector2:
return ._get_vertical_tick_label_pos(base_position, text) + Vector2(x_sector_size / 2, 0)
func _draw_vertical_grid() -> void:
# draw vertical lines
# 1. the amount of lines is equals to the X_scale: it identifies in how many sectors the x domain
# should be devided
# 2. calculate the spacing between each line in pixel. It is equals to x_sampled_domain / x_scale
# 3. calculate the offset in the real x domain, which is x_domain / x_scale.
for _x in x.size():
var top: Vector2 = Vector2(
(_x * x_sector_size) + plot_box.position.x,
bounding_box.position.y
)
var bottom: Vector2 = Vector2(
(_x * x_sector_size) + plot_box.position.x,
bounding_box.end.y
)
_draw_vertical_gridline_component(top, bottom, _x, x_sector_size)
### Draw last gridline
var p1: Vector2 = Vector2(
(x.size() * x_sector_size) + plot_box.position.x,
bounding_box.position.y
)
var p2: Vector2 = Vector2(
(x.size() * x_sector_size) + plot_box.position.x,
bounding_box.end.y
)
# Draw V Ticks
if chart_properties.ticks:
_draw_tick(p2, p2 + Vector2(0, _x_tick_size), chart_properties.colors.bounding_box)
# Draw V Grid Lines
if chart_properties.grid:
draw_line(p1, p2, chart_properties.colors.grid, 1, true)
func _calculate_bars() -> void:
var validation: int = _validate_sampled_axis(x_sampled, y_sampled)
if not validation == OK:
printerr("Cannot plot bars for invalid dataset! Error: %s" % validation)
return
if y_sampled.values[0] is Array:
for yxi in y_sampled.values.size():
var _function_bars: Array = []
for i in y_sampled.values[yxi].size():
var real_bar_value: Pair = Pair.new(x[i], y[yxi][i])
var sampled_bar_pos: Vector2 = Vector2(
(x_sector_size * i) + x_sampled_domain.left + (x_sector_size / 2) - (chart_properties.bar_width / 2),
y_sampled.values[yxi][i]
)
var sampled_bar_size: Vector2 = Vector2(
chart_properties.bar_width,
y_sampled_domain.left - y_sampled.values[yxi][i]
)
var bar: Bar = Bar.new(Rect2(sampled_bar_pos, sampled_bar_size), real_bar_value)
_function_bars.append(bar)
bars.append(bar)
function_bars.append(_function_bars)
else:
for i in y_sampled.values.size():
var real_bar_value: Pair = Pair.new(x[i], y[i])
var sampled_bar_pos: Vector2 = Vector2(
(x_sector_size * i) + x_sampled_domain.left + (x_sector_size / 2) - chart_properties.bar_width,
y_sampled.values[i]
)
var sampled_bar_size: Vector2 = Vector2(
chart_properties.bar_width,
y_sampled_domain.left - y_sampled.values[i]
)
var bar: Bar = Bar.new(Rect2(sampled_bar_pos, sampled_bar_size), real_bar_value)
bars.append(bar)
function_bars.append(bars)
func _draw() -> void:
_calculate_bars()
_draw_bars()
func _get_function_bar(bar: Bar) -> int:
var bar_f_index: int = -1
for f_bar in function_bars.size():
var found: int = function_bars[f_bar].find(bar)
if found != -1:
bar_f_index = f_bar
break
return bar_f_index
func _input(event: InputEvent) -> void:
if event is InputEventMouse:
for bar in bars:
if bar.rect.abs().has_point(event.position):
if focused_bar == bar:
return
else:
focused_bar = bar
var func_index: int = _get_function_bar(focused_bar)
$Tooltip.update_values(
str(focused_bar.value.left),
str(focused_bar.value.right),
_get_function_name(func_index),
_get_function_color(func_index)
)
$Tooltip.show()
emit_signal("bar_entered", bar)
return
# Mouse is not in any bar's box
focused_bar = null
$Tooltip.hide()

View File

@ -0,0 +1,14 @@
[gd_scene load_steps=3 format=2]
[ext_resource path="res://addons/easy_charts/control_charts/BarChart/bar_chart.gd" type="Script" id=1]
[ext_resource path="res://addons/easy_charts/control_charts/chart.tscn" type="PackedScene" id=2]
[node name="BarChart" instance=ExtResource( 2 )]
script = ExtResource( 1 )
[node name="Tooltip" parent="." index="1"]
margin_right = 20.0
margin_bottom = 16.0
__meta__ = {
"_edit_use_anchors_": true
}