└── addons └── easy_charts ├── control_charts ├── chart.gd ├── chart.gd.uid ├── chart.tscn ├── domain │ ├── chart_axis_domain.gd │ └── chart_axis_domain.gd.uid └── plotters │ ├── area_plotter.gd │ ├── area_plotter.gd.uid │ ├── bar_plotter.gd │ ├── bar_plotter.gd.uid │ ├── function_plotter.gd │ ├── function_plotter.gd.uid │ ├── line_plotter.gd │ ├── line_plotter.gd.uid │ ├── pie_plotter.gd │ ├── pie_plotter.gd.uid │ ├── scatter_plotter.gd │ └── scatter_plotter.gd.uid ├── examples ├── area_chart │ ├── area_chart_example.gd │ ├── area_chart_example.gd.uid │ └── area_chart_example.tscn ├── bar_chart │ ├── bar_chart_example.gd │ ├── bar_chart_example.gd.uid │ └── bar_chart_example.tscn ├── line_chart │ ├── line_chart_example.gd │ ├── line_chart_example.gd.uid │ └── line_chart_example.tscn ├── multiplot │ ├── multiplot_example.gd │ ├── multiplot_example.gd.uid │ └── multiplot_example.tscn ├── pie_chart │ ├── pie_chart_example.gd │ ├── pie_chart_example.gd.uid │ └── pie_chart_example.tscn ├── scatter_chart │ ├── scatter_chart_example.gd │ ├── scatter_chart_example.gd.uid │ └── scatter_chart_example.tscn ├── scatter_chart_discrete │ ├── scatter_chart_discrete.gd │ ├── scatter_chart_discrete.gd.uid │ └── scatter_chart_discrete.tscn └── simple_chart │ ├── simple_chart_example.gd │ ├── simple_chart_example.gd.uid │ └── simple_chart_example.tscn ├── icon.svg ├── plugin.cfg ├── plugin.gd ├── plugin.gd.uid ├── templates.json └── utilities ├── assets └── OpenSans-VariableFont_wdth,wght.ttf ├── classes ├── plotting │ ├── bar.gd │ ├── bar.gd.uid │ ├── chart_properties.gd │ ├── chart_properties.gd.uid │ ├── function.gd │ ├── function.gd.uid │ ├── point.gd │ └── point.gd.uid └── structures │ ├── array_operations.gd │ ├── array_operations.gd.uid │ ├── data_frame.gd │ ├── data_frame.gd.uid │ ├── matrix.gd │ ├── matrix.gd.uid │ ├── matrix_generator.gd │ └── matrix_generator.gd.uid ├── containers ├── canvas │ ├── canvas.gd │ ├── canvas.gd.uid │ └── plot_box │ │ ├── grid_box.gd │ │ ├── grid_box.gd.uid │ │ ├── plot_box.gd │ │ └── plot_box.gd.uid ├── data_tooltip │ ├── data_tooltip.gd │ ├── data_tooltip.gd.uid │ └── data_tooltip.tscn └── legend │ ├── function_label.gd │ ├── function_label.gd.uid │ ├── function_label.tscn │ ├── function_legend.gd │ ├── function_legend.gd.uid │ ├── function_legend.tscn │ ├── function_type.gd │ └── function_type.gd.uid ├── icons └── linechart.svg └── scripts ├── ec_utilities.gd └── ec_utilities.gd.uid /addons/easy_charts/control_charts/chart.gd: -------------------------------------------------------------------------------- 1 | @icon("res://addons/easy_charts/utilities/icons/linechart.svg") 2 | extends PanelContainer 3 | class_name Chart 4 | 5 | @onready var _canvas: Canvas = $Canvas 6 | @onready var plot_box: PlotBox = %PlotBox 7 | @onready var grid_box: GridBox = %GridBox 8 | @onready var functions_box: Control = %FunctionsBox 9 | @onready var function_legend: FunctionLegend = %FunctionLegend 10 | 11 | @onready var _tooltip: DataTooltip = %Tooltip 12 | var _function_of_tooltip: Function = null 13 | 14 | var functions: Array = [] 15 | var x: Array = [] 16 | var y: Array = [] 17 | 18 | var x_labels_function: Callable = Callable() 19 | var y_labels_function: Callable = Callable() 20 | 21 | var x_domain: ChartAxisDomain = null 22 | var y_domain: ChartAxisDomain = null 23 | 24 | var chart_properties: ChartProperties = null 25 | 26 | ########### 27 | 28 | func _ready() -> void: 29 | if theme == null: 30 | theme = Theme.new() 31 | 32 | func plot(functions: Array[Function], properties: ChartProperties = ChartProperties.new()) -> void: 33 | self.functions = functions 34 | self.chart_properties = properties 35 | 36 | theme.set("default_font", self.chart_properties.font) 37 | _canvas.prepare_canvas(self.chart_properties) 38 | plot_box.chart_properties = self.chart_properties 39 | function_legend.chart_properties = self.chart_properties 40 | 41 | load_functions(functions) 42 | 43 | func load_functions(functions: Array[Function]) -> void: 44 | self.x = [] 45 | self.y = [] 46 | 47 | function_legend.clear() 48 | 49 | # Remove existing function_plotters 50 | for function_plotter in functions_box.get_children(): 51 | functions_box.remove_child(function_plotter) 52 | function_plotter.queue_free() 53 | 54 | for function in functions: 55 | # Load x and y values 56 | self.x.append(function.__x) 57 | self.y.append(function.__y) 58 | 59 | # Create FunctionPlotter 60 | var function_plotter := FunctionPlotter.create_for_function(function) 61 | function_plotter.point_entered.connect(_show_tooltip) 62 | function_plotter.point_exited.connect(_hide_tooltip) 63 | functions_box.add_child(function_plotter) 64 | 65 | # Create legend 66 | match function.get_type(): 67 | Function.Type.PIE: 68 | for i in function.__x.size(): 69 | var interp_color: Color = function.get_gradient().sample(float(i) / float(function.__x.size())) 70 | function_legend.add_label(function.get_type(), interp_color, Function.Marker.NONE, function.__y[i]) 71 | _: 72 | function_legend.add_function(function) 73 | 74 | func _draw() -> void: 75 | if (x.size() == 0) or (y.size() == 0) or (x.size() == 1 and x[0].is_empty()) or (y.size() == 1 and y[0].is_empty()): 76 | printerr("Cannot plot an empty function!") 77 | return 78 | 79 | var is_x_fixed: bool = x_domain != null && x_domain.fixed 80 | var is_y_fixed: bool = y_domain != null && y_domain.fixed 81 | 82 | # GridBox 83 | if not is_x_fixed or not is_y_fixed : 84 | if chart_properties.max_samples > 0 : 85 | var _x: Array = [] 86 | var _y: Array = [] 87 | 88 | _x.resize(x.size()) 89 | _y.resize(y.size()) 90 | 91 | for i in x.size(): 92 | if not is_x_fixed: 93 | _x[i] = x[i].slice(max(0, x[i].size() - chart_properties.max_samples), x[i].size()) 94 | if not is_y_fixed: 95 | _y[i] = y[i].slice(max(0, y[i].size() - chart_properties.max_samples), y[i].size()) 96 | 97 | if not is_x_fixed: 98 | x_domain = ChartAxisDomain.from_values(_x, chart_properties.smooth_domain) 99 | if not is_y_fixed: 100 | y_domain = ChartAxisDomain.from_values(_y, chart_properties.smooth_domain) 101 | else: 102 | if not is_x_fixed: 103 | x_domain = ChartAxisDomain.from_values(x, chart_properties.smooth_domain) 104 | if not is_y_fixed: 105 | y_domain = ChartAxisDomain.from_values(y, chart_properties.smooth_domain) 106 | 107 | if !x_domain.is_discrete: 108 | x_domain.set_tick_count(chart_properties.x_scale) 109 | 110 | if x_labels_function: 111 | x_domain.labels_function = x_labels_function 112 | 113 | if !y_domain.is_discrete: 114 | y_domain.set_tick_count(chart_properties.y_scale) 115 | 116 | if y_labels_function: 117 | y_domain.labels_function = y_labels_function 118 | 119 | # Update values for the PlotBox in order to propagate them to the children 120 | update_plotbox(x_domain, y_domain, x_labels_function, y_labels_function) 121 | 122 | # Update GridBox 123 | update_gridbox(x_domain, y_domain, x_labels_function, y_labels_function) 124 | 125 | # Update each FunctionPlotter in FunctionsBox 126 | for function_plotter in functions_box.get_children(): 127 | if function_plotter is FunctionPlotter: 128 | function_plotter.visible = function_plotter.function.get_visibility() 129 | if function_plotter.function.get_visibility(): 130 | function_plotter.update_values(x_domain, y_domain) 131 | 132 | func set_x_domain(lb: Variant, ub: Variant) -> void: 133 | x_domain = ChartAxisDomain.from_bounds(lb, ub) 134 | 135 | func set_y_domain(lb: Variant, ub: Variant) -> void: 136 | y_domain = ChartAxisDomain.from_bounds(lb, ub) 137 | 138 | func update_plotbox(x_domain: ChartAxisDomain, y_domain: ChartAxisDomain, x_labels_function: Callable, y_labels_function: Callable) -> void: 139 | plot_box.box_margins = calculate_plotbox_margins(x_domain, y_domain, y_labels_function) 140 | 141 | func update_gridbox(x_domain: ChartAxisDomain, y_domain: ChartAxisDomain, x_labels_function: Callable, y_labels_function: Callable) -> void: 142 | grid_box.set_domains(x_domain, y_domain) 143 | grid_box.set_labels_functions(x_labels_function, y_labels_function) 144 | grid_box.queue_redraw() 145 | 146 | func calculate_plotbox_margins(x_domain: ChartAxisDomain, y_domain: ChartAxisDomain, y_labels_function: Callable) -> Vector2: 147 | var plotbox_margins: Vector2 = Vector2( 148 | chart_properties.x_tick_size, 149 | chart_properties.y_tick_size 150 | ) 151 | 152 | if chart_properties.show_tick_labels: 153 | var x_ticklabel_size: Vector2 154 | var y_ticklabel_size: Vector2 155 | 156 | var y_max_formatted: String = y_labels_function.call(y_domain.ub) if not y_labels_function.is_null() else \ 157 | ECUtilities._format_value(y_domain.ub, y_domain.has_decimals) 158 | if y_domain.lb < 0: # negative number 159 | var y_min_formatted: String = y_labels_function.call(y_domain.ub) if not y_labels_function.is_null() else \ 160 | ECUtilities._format_value(y_domain.lb, y_domain.has_decimals) 161 | if y_min_formatted.length() >= y_max_formatted.length(): 162 | y_ticklabel_size = chart_properties.get_string_size(y_min_formatted) 163 | else: 164 | y_ticklabel_size = chart_properties.get_string_size(y_max_formatted) 165 | else: 166 | y_ticklabel_size = chart_properties.get_string_size(y_max_formatted) 167 | 168 | plotbox_margins.x += y_ticklabel_size.x + chart_properties.x_ticklabel_space 169 | plotbox_margins.y += ThemeDB.fallback_font_size + chart_properties.y_ticklabel_space 170 | 171 | return plotbox_margins 172 | 173 | func _on_plot_box_resized() -> void: 174 | grid_box.queue_redraw() 175 | for function in functions_box.get_children(): 176 | function.queue_redraw() 177 | 178 | func _show_tooltip(point: Point, function: Function, options: Dictionary = {}) -> void: 179 | var x_value: String = x_domain.get_tick_label(point.value.x, x_labels_function) 180 | var y_value: String = y_domain.get_tick_label(point.value.y, y_labels_function) 181 | var color: Color = function.get_color() if function.get_type() != Function.Type.PIE \ 182 | else function.get_gradient().sample(options.interpolation_index) 183 | _tooltip.show() 184 | _tooltip.update_values(x_value, y_value, function.name, color) 185 | _tooltip.update_position(point.position) 186 | _function_of_tooltip = function 187 | 188 | func _hide_tooltip(point: Point, function: Function) -> void: 189 | if function != _function_of_tooltip: 190 | return 191 | 192 | _tooltip.hide() 193 | -------------------------------------------------------------------------------- /addons/easy_charts/control_charts/chart.gd.uid: -------------------------------------------------------------------------------- 1 | uid://bn7n1q5r4n3jn 2 | -------------------------------------------------------------------------------- /addons/easy_charts/control_charts/chart.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=10 format=3 uid="uid://dlwq4kmdb3bhs"] 2 | 3 | [ext_resource type="Script" uid="uid://bn7n1q5r4n3jn" path="res://addons/easy_charts/control_charts/chart.gd" id="1"] 4 | [ext_resource type="PackedScene" uid="uid://dmesmjhiuqdo4" path="res://addons/easy_charts/utilities/containers/data_tooltip/data_tooltip.tscn" id="2"] 5 | [ext_resource type="Script" uid="uid://d3eig7fx6wtli" path="res://addons/easy_charts/utilities/containers/canvas/canvas.gd" id="3"] 6 | [ext_resource type="Script" uid="uid://8xd8yvw7lumm" path="res://addons/easy_charts/utilities/containers/canvas/plot_box/plot_box.gd" id="4"] 7 | [ext_resource type="Script" uid="uid://doelssxa0y7ap" path="res://addons/easy_charts/utilities/containers/canvas/plot_box/grid_box.gd" id="5"] 8 | [ext_resource type="PackedScene" uid="uid://c6ffuulowjw4g" path="res://addons/easy_charts/utilities/containers/legend/function_legend.tscn" id="6"] 9 | 10 | [sub_resource type="Theme" id="4"] 11 | 12 | [sub_resource type="StyleBoxEmpty" id="8"] 13 | 14 | [sub_resource type="StyleBoxFlat" id="5"] 15 | content_margin_left = 15.0 16 | content_margin_top = 15.0 17 | content_margin_right = 15.0 18 | content_margin_bottom = 15.0 19 | draw_center = false 20 | 21 | [node name="Chart" type="PanelContainer"] 22 | anchors_preset = 15 23 | anchor_right = 1.0 24 | anchor_bottom = 1.0 25 | size_flags_horizontal = 3 26 | size_flags_vertical = 3 27 | mouse_filter = 1 28 | theme = SubResource("4") 29 | theme_override_styles/panel = SubResource("8") 30 | script = ExtResource("1") 31 | 32 | [node name="Canvas" type="PanelContainer" parent="."] 33 | layout_mode = 2 34 | size_flags_horizontal = 3 35 | size_flags_vertical = 3 36 | mouse_filter = 1 37 | theme_override_styles/panel = SubResource("5") 38 | script = ExtResource("3") 39 | 40 | [node name="CanvasContainer" type="VBoxContainer" parent="Canvas"] 41 | layout_mode = 2 42 | size_flags_horizontal = 3 43 | size_flags_vertical = 3 44 | theme_override_constants/separation = 10 45 | 46 | [node name="Title" type="Label" parent="Canvas/CanvasContainer"] 47 | layout_mode = 2 48 | text = "{title}" 49 | 50 | [node name="DataContainer" type="HBoxContainer" parent="Canvas/CanvasContainer"] 51 | layout_mode = 2 52 | size_flags_horizontal = 3 53 | size_flags_vertical = 3 54 | theme_override_constants/separation = 10 55 | 56 | [node name="YLabel" type="Label" parent="Canvas/CanvasContainer/DataContainer"] 57 | layout_mode = 2 58 | text = "{ylabel}" 59 | 60 | [node name="PlotContainer" type="VBoxContainer" parent="Canvas/CanvasContainer/DataContainer"] 61 | layout_mode = 2 62 | size_flags_horizontal = 3 63 | size_flags_vertical = 3 64 | 65 | [node name="PlotBox" type="Control" parent="Canvas/CanvasContainer/DataContainer/PlotContainer"] 66 | unique_name_in_owner = true 67 | clip_contents = true 68 | layout_mode = 2 69 | size_flags_horizontal = 3 70 | size_flags_vertical = 3 71 | script = ExtResource("4") 72 | 73 | [node name="GridBox" type="Control" parent="Canvas/CanvasContainer/DataContainer/PlotContainer/PlotBox"] 74 | unique_name_in_owner = true 75 | anchors_preset = 0 76 | anchor_right = 1.0 77 | anchor_bottom = 1.0 78 | size_flags_horizontal = 3 79 | size_flags_vertical = 3 80 | mouse_filter = 2 81 | script = ExtResource("5") 82 | 83 | [node name="FunctionsBox" type="Control" parent="Canvas/CanvasContainer/DataContainer/PlotContainer/PlotBox"] 84 | unique_name_in_owner = true 85 | anchors_preset = 0 86 | anchor_right = 1.0 87 | anchor_bottom = 1.0 88 | mouse_default_cursor_shape = 3 89 | 90 | [node name="Tooltip" parent="Canvas/CanvasContainer/DataContainer/PlotContainer/PlotBox" instance=ExtResource("2")] 91 | unique_name_in_owner = true 92 | layout_mode = 0 93 | offset_left = -67.0 94 | offset_top = -33.0 95 | offset_right = -17.0 96 | offset_bottom = 30.0 97 | 98 | [node name="XLabel" type="Label" parent="Canvas/CanvasContainer/DataContainer/PlotContainer"] 99 | layout_mode = 2 100 | text = "{xlabel}" 101 | 102 | [node name="FunctionLegend" parent="Canvas/CanvasContainer/DataContainer" instance=ExtResource("6")] 103 | unique_name_in_owner = true 104 | use_parent_material = true 105 | layout_mode = 2 106 | 107 | [connection signal="resized" from="Canvas/CanvasContainer/DataContainer/PlotContainer/PlotBox" to="." method="_on_plot_box_resized"] 108 | -------------------------------------------------------------------------------- /addons/easy_charts/control_charts/domain/chart_axis_domain.gd: -------------------------------------------------------------------------------- 1 | extends RefCounted 2 | class_name ChartAxisDomain 3 | ## Represents the domain for an axis of a chart. 4 | 5 | ## The lower bound value 6 | var lb: Variant 7 | 8 | ## The upper bound value 9 | var ub: Variant 10 | 11 | ## True if any value on the axis has decimal places 12 | var has_decimals: bool 13 | 14 | ## True if the domain has only discrete values. For now, this is only 15 | ## set to true in case the domain contains string values. 16 | var is_discrete: bool 17 | 18 | ## True if the domain was specified via from_bounds(). 19 | var fixed: bool 20 | 21 | ## Callable to overwrite the label generation. 22 | var labels_function: Callable 23 | 24 | var _tick_count: int = -1 25 | 26 | var _string_values: Array 27 | 28 | static func from_bounds(lb: Variant, ub: Variant) -> ChartAxisDomain: 29 | var domain = ChartAxisDomain.new() 30 | domain.lb = lb 31 | domain.ub = ub 32 | domain.has_decimals = ECUtilities._has_decimals([[lb, ub]]) 33 | domain.fixed = true 34 | domain.is_discrete = false 35 | return domain 36 | 37 | static func from_values(value_arrays: Array, smooth_domain: bool) -> ChartAxisDomain: 38 | var domain = ChartAxisDomain.new() 39 | for value_array in value_arrays: 40 | if ECUtilities._contains_string(value_array): 41 | domain.lb = 0.0 42 | domain.ub = (value_array.size()) 43 | domain.has_decimals = false 44 | domain.is_discrete = true 45 | domain.fixed = false 46 | domain._string_values = value_array 47 | domain._tick_count = value_array.size() 48 | return domain 49 | 50 | var min_max: Dictionary = ECUtilities._find_min_max(value_arrays) 51 | if not smooth_domain: 52 | domain.lb = min_max.min 53 | domain.ub = min_max.max 54 | domain.has_decimals = ECUtilities._has_decimals(value_arrays) 55 | domain.is_discrete = false 56 | domain.fixed = false 57 | else: 58 | domain.lb = ECUtilities._round_min(min_max.min) 59 | domain.ub = ECUtilities._round_max(min_max.max) 60 | domain.has_decimals = ECUtilities._has_decimals(value_arrays) 61 | domain.is_discrete = false 62 | domain.fixed = false 63 | 64 | return domain 65 | 66 | func set_tick_count(tick_count: int) -> void: 67 | if is_discrete: 68 | printerr("You cannot set tick count for a discrete chart axis domain") 69 | 70 | _tick_count = tick_count 71 | 72 | func get_tick_labels() -> PackedStringArray: 73 | if !labels_function.is_null(): 74 | return range(_tick_count).map(func(i) -> String: 75 | var value = lerp(lb, ub, float(i) / float(_tick_count)) 76 | return labels_function.call(value) 77 | ) 78 | 79 | if is_discrete: 80 | return _string_values 81 | 82 | return range(_tick_count).map(func(i) -> String: 83 | var value = lerp(lb, ub, float(i) / float(_tick_count)) 84 | return ECUtilities._format_value(value, false) 85 | ) 86 | 87 | func get_tick_label(value: Variant, labels_function: Callable) -> String: 88 | if !labels_function.is_null(): 89 | return labels_function.call(value) 90 | 91 | if is_discrete: 92 | return value 93 | 94 | return ECUtilities._format_value(value, is_discrete) 95 | 96 | func map_to(value_index: int, function_values: Array, to_domain: ChartAxisDomain) -> Variant: 97 | if is_discrete: 98 | return ECUtilities._map_domain(value_index, self, to_domain) 99 | 100 | return ECUtilities._map_domain(function_values[value_index], self, to_domain) 101 | -------------------------------------------------------------------------------- /addons/easy_charts/control_charts/domain/chart_axis_domain.gd.uid: -------------------------------------------------------------------------------- 1 | uid://ccuyrwofsk241 2 | -------------------------------------------------------------------------------- /addons/easy_charts/control_charts/plotters/area_plotter.gd: -------------------------------------------------------------------------------- 1 | extends LinePlotter 2 | class_name AreaPlotter 3 | 4 | var base_color: Color = Color.WHITE 5 | 6 | func _init(function: Function) -> void: 7 | super(function) 8 | 9 | self.base_color = function.get_color() 10 | pass 11 | 12 | func _draw_areas() -> void: 13 | var box: Rect2 = get_box() 14 | var fp_augmented: PackedVector2Array = [] 15 | match function.get_interpolation(): 16 | Function.Interpolation.LINEAR: 17 | fp_augmented = points_positions 18 | Function.Interpolation.STAIR: 19 | fp_augmented = _get_stair_points() 20 | Function.Interpolation.SPLINE: 21 | fp_augmented = _get_spline_points() 22 | Function.Interpolation.NONE, _: 23 | return 24 | 25 | fp_augmented.push_back(Vector2(fp_augmented[-1].x, box.end.y + 80)) 26 | fp_augmented.push_back(Vector2(fp_augmented[0].x, box.end.y + 80)) 27 | 28 | # Precompute the scaling factor for the remap. 29 | var end_y = box.end.y 30 | var pos_y = box.position.y 31 | var scale = 0.5 / (pos_y - end_y) 32 | 33 | # Pre-allocate the PackedColorArray based on the number of points. 34 | var point_count = fp_augmented.size() 35 | var colors = PackedColorArray() 36 | colors.resize(point_count) 37 | 38 | # Compute alpha for each point and assign the color. 39 | for i in range(point_count): 40 | var point: Vector2 = fp_augmented[i] 41 | var alpha: float = (point.y - end_y) * scale 42 | colors[i] = Color(base_color, alpha) 43 | 44 | # Draw the polygon with the computed colors. 45 | draw_polygon(fp_augmented, colors) 46 | 47 | func _draw() -> void: 48 | super._draw() 49 | 50 | #prevent error when drawing with no data. 51 | if points_positions.size() < 2: 52 | printerr("Cannot plot an area with less than two points!") 53 | return 54 | 55 | _draw_areas() 56 | -------------------------------------------------------------------------------- /addons/easy_charts/control_charts/plotters/area_plotter.gd.uid: -------------------------------------------------------------------------------- 1 | uid://dg6thjdl36t01 2 | -------------------------------------------------------------------------------- /addons/easy_charts/control_charts/plotters/bar_plotter.gd: -------------------------------------------------------------------------------- 1 | extends FunctionPlotter 2 | class_name BarPlotter 3 | 4 | 5 | signal point_entered(point, function) 6 | signal point_exited(point, function) 7 | 8 | var bars: PackedVector2Array 9 | var bars_rects: Array 10 | var focused_bar_midpoint: Point 11 | 12 | var bar_size: float 13 | 14 | func _init(function: Function) -> void: 15 | super(function) 16 | self.bar_size = function.props.get("bar_size", 5.0) 17 | 18 | func _draw() -> void: 19 | super._draw() 20 | var box: Rect2 = get_box() 21 | var x_sampled_domain := ChartAxisDomain.from_bounds(box.position.x, box.end.x) 22 | var y_sampled_domain := ChartAxisDomain.from_bounds(box.end.y, box.position.y) 23 | sample(x_sampled_domain, y_sampled_domain) 24 | _draw_bars() 25 | 26 | func sample(x_sampled_domain: ChartAxisDomain, y_sampled_domain: ChartAxisDomain) -> void: 27 | bars = [] 28 | bars_rects = [] 29 | for i in function.__x.size(): 30 | var top: Vector2 = Vector2( 31 | x_domain.map_to(i, function.__x, x_sampled_domain), 32 | y_domain.map_to(i, function.__y, y_sampled_domain) 33 | ) 34 | var base: Vector2 = Vector2(top.x, ECUtilities._map_domain(0.0, y_domain, y_sampled_domain)) 35 | bars.push_back(top) 36 | bars.push_back(base) 37 | bars_rects.append(Rect2(Vector2(top.x - bar_size, top.y), Vector2(bar_size * 2, base.y - top.y))) 38 | 39 | func _draw_bars() -> void: 40 | for bar in bars_rects: 41 | draw_rect(bar, function.get_color()) 42 | 43 | func _input(event: InputEvent) -> void: 44 | if event is InputEventMouse: 45 | for i in bars_rects.size(): 46 | if bars_rects[i].grow(5).abs().has_point(get_relative_position(event.position)): 47 | var point: Point = Point.new(bars_rects[i].get_center(), { x = function.__x[i], y = function.__y[i]}) 48 | if focused_bar_midpoint == point: 49 | return 50 | else: 51 | focused_bar_midpoint = point 52 | emit_signal("point_entered", point, function) 53 | return 54 | # Mouse is not in any point's box 55 | emit_signal("point_exited", focused_bar_midpoint, function) 56 | focused_bar_midpoint = null 57 | -------------------------------------------------------------------------------- /addons/easy_charts/control_charts/plotters/bar_plotter.gd.uid: -------------------------------------------------------------------------------- 1 | uid://cvjd3ae0qlifn 2 | -------------------------------------------------------------------------------- /addons/easy_charts/control_charts/plotters/function_plotter.gd: -------------------------------------------------------------------------------- 1 | extends Control 2 | class_name FunctionPlotter 3 | 4 | var function: Function 5 | var x_domain: ChartAxisDomain 6 | var y_domain: ChartAxisDomain 7 | 8 | static func create_for_function(function: Function) -> FunctionPlotter: 9 | match function.get_type(): 10 | Function.Type.LINE: 11 | return LinePlotter.new(function) 12 | Function.Type.AREA: 13 | return AreaPlotter.new(function) 14 | Function.Type.PIE: 15 | return PiePlotter.new(function) 16 | Function.Type.BAR: 17 | return BarPlotter.new(function) 18 | Function.Type.SCATTER, _: 19 | return ScatterPlotter.new(function) 20 | 21 | func _init(function: Function) -> void: 22 | self.function = function 23 | 24 | func _ready() -> void: 25 | set_process_input(get_chart_properties().interactive) 26 | 27 | func update_values(x_domain: ChartAxisDomain, y_domain: ChartAxisDomain) -> void: 28 | self.visible = self.function.get_visibility() 29 | if not self.function.get_visibility(): 30 | return 31 | self.x_domain = x_domain 32 | self.y_domain = y_domain 33 | queue_redraw() 34 | 35 | func _draw() -> void: 36 | return 37 | 38 | func get_box() -> Rect2: 39 | return get_parent().get_parent().get_plot_box() 40 | 41 | func get_chart_properties() -> ChartProperties: 42 | return get_parent().get_parent().chart_properties 43 | 44 | func get_relative_position(position: Vector2) -> Vector2: 45 | return position - global_position 46 | -------------------------------------------------------------------------------- /addons/easy_charts/control_charts/plotters/function_plotter.gd.uid: -------------------------------------------------------------------------------- 1 | uid://c88cemvig3ion 2 | -------------------------------------------------------------------------------- /addons/easy_charts/control_charts/plotters/line_plotter.gd: -------------------------------------------------------------------------------- 1 | extends ScatterPlotter 2 | class_name LinePlotter 3 | 4 | func _get_spline_points(density: float = 10.0, tension: float = 1) -> PackedVector2Array: 5 | var spline_points: PackedVector2Array = [] 6 | 7 | var augmented_positions: PackedVector2Array = points_positions 8 | var pi: Vector2 = augmented_positions[0] - Vector2(10, -10) 9 | var pf: Vector2 = augmented_positions[augmented_positions.size() - 1] + Vector2(10, 10) 10 | 11 | augmented_positions.insert(0, pi) 12 | augmented_positions.push_back(pf) 13 | 14 | for p in range(1, augmented_positions.size() - 2, 1) : #(inclusive) 15 | for f in range(0, density + 1, 1): 16 | spline_points.append( 17 | augmented_positions[p].cubic_interpolate( 18 | augmented_positions[p + 1], 19 | augmented_positions[p - 1], 20 | augmented_positions[p + 2], 21 | f / density) 22 | ) 23 | 24 | return spline_points 25 | 26 | 27 | func _get_stair_points() -> PackedVector2Array: 28 | var stair_points: PackedVector2Array = points_positions 29 | 30 | for i in range(points_positions.size() - 1, 0, -1): 31 | stair_points.insert(i, Vector2(points_positions[i].x, points_positions[i-1].y)) 32 | 33 | return stair_points 34 | 35 | 36 | func _draw() -> void: 37 | super._draw() 38 | 39 | #prevent error when drawing with no data. 40 | if points_positions.size() < 2: 41 | printerr("Cannot plot a line with less than two points!") 42 | return 43 | 44 | match function.get_interpolation(): 45 | Function.Interpolation.LINEAR: 46 | draw_polyline( 47 | points_positions, 48 | function.get_color(), 49 | function.get_line_width(), 50 | true 51 | ) 52 | Function.Interpolation.STAIR: 53 | draw_polyline( 54 | _get_stair_points(), 55 | function.get_color(), 56 | function.get_line_width(), 57 | true 58 | ) 59 | Function.Interpolation.SPLINE: 60 | draw_polyline( 61 | _get_spline_points(), 62 | function.get_color(), 63 | function.get_line_width(), 64 | true 65 | ) 66 | Function.Interpolation.NONE, _: 67 | pass 68 | -------------------------------------------------------------------------------- /addons/easy_charts/control_charts/plotters/line_plotter.gd.uid: -------------------------------------------------------------------------------- 1 | uid://cvkgvqwi4av7q 2 | -------------------------------------------------------------------------------- /addons/easy_charts/control_charts/plotters/pie_plotter.gd: -------------------------------------------------------------------------------- 1 | extends FunctionPlotter 2 | class_name PiePlotter 3 | 4 | signal point_entered(slice_mid_point, function, props) 5 | signal point_exited(slice_mid_point, function) 6 | 7 | var radius_multiplayer: float = 1.0 8 | 9 | #### INTERNAL 10 | var box: Rect2 11 | var radius: float 12 | 13 | var slices: Array = [] 14 | var slices_dirs: PackedVector2Array = [] 15 | var slices_conc: Array[bool] = [] 16 | 17 | var focused_point: Point 18 | 19 | func _draw() -> void: 20 | super._draw() 21 | box = get_box() 22 | radius = min(box.size.x, box.size.y) * 0.5 * radius_multiplayer 23 | var total: float = get_total() 24 | var ratios: PackedFloat32Array = get_ratios(total) 25 | sample(radius, box.get_center(), total, ratios) 26 | _draw_pie() 27 | _draw_labels(radius, box.get_center(), ratios) 28 | 29 | func get_total() -> float: 30 | # Calculate total and ratios 31 | var total: float = 0.0 32 | for value in function.__x: 33 | total += float(value) 34 | return total 35 | 36 | func get_ratios(total: float) -> PackedFloat32Array: 37 | var ratios: PackedFloat32Array = [] 38 | for value in function.__x: 39 | ratios.push_back(value / total * 100) 40 | return ratios 41 | 42 | func sample(radius: float, center: Vector2, total: float, ratios: PackedFloat32Array) -> void: 43 | # Calculate directions 44 | slices.clear() 45 | slices_dirs = [] 46 | slices_conc = [] 47 | 48 | var start_angle: float = 0.0 49 | for ratio in ratios: 50 | var end_angle: float = start_angle + (2 * PI * float(ratio) * 0.01) 51 | slices.append( 52 | _calc_circle_arc_poly( 53 | center, 54 | radius, 55 | start_angle, 56 | end_angle 57 | ) 58 | ) 59 | slices_conc.append( abs(end_angle - start_angle) >= PI ) 60 | start_angle = end_angle 61 | 62 | for i in slices.size(): 63 | var slice = slices[i] 64 | var mid_point: Vector2 = (slice[-1] + slice[1]) / 2 65 | if (slices_conc[i]): mid_point = (2 * center) - mid_point 66 | draw_circle(mid_point, 1, Color.RED) 67 | slices_dirs.append(center.direction_to(mid_point)) 68 | 69 | func _calc_circle_arc_poly(center: Vector2, radius: float, angle_from: float, angle_to: float) -> PackedVector2Array: 70 | var nb_points: int = 64 71 | var points_arc: PackedVector2Array = PackedVector2Array() 72 | points_arc.push_back(center) 73 | 74 | for i in range(nb_points + 1): 75 | var angle_point: float = - (PI / 2) + angle_from + i * (angle_to - angle_from) / nb_points 76 | points_arc.push_back(center + (Vector2.RIGHT.rotated(angle_point).normalized() * radius)) 77 | 78 | return points_arc 79 | 80 | func _draw_pie() -> void: 81 | for i in slices.size(): 82 | draw_colored_polygon(slices[i], function.get_gradient().sample(float(i) / float(slices.size() - 1))) 83 | draw_polyline(slices[i], Color.WHITE, 2.0, true) 84 | 85 | func _draw_labels(radius: float, center: Vector2, ratios: PackedFloat32Array) -> void: 86 | for i in slices_dirs.size(): 87 | var ratio_lbl: String = "%.1f%%" % ratios[i] 88 | var value_lbl: String = "(%s)" % function.__x[i] 89 | var position: Vector2 = center + slices_dirs[i] * radius * 0.5 90 | var ratio_lbl_size: Vector2 = get_chart_properties().get_string_size(ratio_lbl) 91 | var value_lbl_size: Vector2 = get_chart_properties().get_string_size(value_lbl) 92 | draw_string( 93 | get_chart_properties().font, 94 | position - Vector2(ratio_lbl_size.x / 2, 0), 95 | ratio_lbl, 96 | HORIZONTAL_ALIGNMENT_CENTER, 97 | ratio_lbl_size.x, 98 | 16, 99 | Color.WHITE, 100 | 3, 101 | TextServer.DIRECTION_AUTO, 102 | TextServer.ORIENTATION_HORIZONTAL 103 | ) 104 | draw_string( 105 | get_chart_properties().font, 106 | position - Vector2(value_lbl_size.x / 2, - value_lbl_size.y), 107 | value_lbl, 108 | HORIZONTAL_ALIGNMENT_CENTER, 109 | value_lbl_size.x, 110 | 16, 111 | Color.WHITE, 112 | 3,TextServer.DIRECTION_AUTO,TextServer.ORIENTATION_HORIZONTAL 113 | ) 114 | 115 | func _input(event: InputEvent) -> void: 116 | if event is InputEventMouse: 117 | for i in slices.size(): 118 | if Geometry2D.is_point_in_polygon(get_relative_position(event.position), slices[i]): 119 | var point: Point = Point.new(self.box.get_center() + slices_dirs[i] * self.radius * 0.5, { x = function.__x[i], y = function.__y[i] }) 120 | if focused_point == point: 121 | return 122 | else: 123 | focused_point = point 124 | emit_signal("point_entered", focused_point, function, { interpolation_index = float(i) / float(slices.size() - 1)}) 125 | return 126 | # Mouse is not in any slice's box 127 | emit_signal("point_exited", focused_point, function) 128 | focused_point = null 129 | -------------------------------------------------------------------------------- /addons/easy_charts/control_charts/plotters/pie_plotter.gd.uid: -------------------------------------------------------------------------------- 1 | uid://lltln7lxbs8i 2 | -------------------------------------------------------------------------------- /addons/easy_charts/control_charts/plotters/scatter_plotter.gd: -------------------------------------------------------------------------------- 1 | extends FunctionPlotter 2 | class_name ScatterPlotter 3 | 4 | signal point_entered(point, function) 5 | signal point_exited(point, function) 6 | 7 | var _points: Array[Point] 8 | var points_positions: PackedVector2Array 9 | var focused_point: Point 10 | 11 | var _point_size: float 12 | 13 | func _init(function: Function): 14 | super(function) 15 | _point_size = function.props.get("point_size", 3.0) 16 | 17 | func _draw() -> void: 18 | super._draw() 19 | 20 | _sample() 21 | 22 | if function.get_marker() != Function.Marker.NONE: 23 | for point_position in points_positions: 24 | draw_function_point(point_position) 25 | 26 | func _sample() -> void: 27 | var box: Rect2 = get_box() 28 | var x_sampled_domain := ChartAxisDomain.from_bounds(box.position.x, box.end.x) 29 | var y_sampled_domain := ChartAxisDomain.from_bounds(box.end.y, box.position.y) 30 | 31 | _points = [] 32 | points_positions = [] 33 | 34 | var lower_bound: int = 0 35 | if get_chart_properties().max_samples > 0: 36 | lower_bound = max(0, function.__x.size() - get_chart_properties().max_samples) 37 | 38 | for i in range(lower_bound, function.__x.size()): 39 | var _position: Vector2 = Vector2( 40 | ECUtilities._map_domain(float(function.__x[i]), x_domain, x_sampled_domain), 41 | ECUtilities._map_domain(float(function.__y[i]), y_domain, y_sampled_domain) 42 | ) 43 | 44 | var point = Point.new(_position, { x = function.__x[i], y = function.__y[i] }) 45 | 46 | # Don't sample outside y domain upper and lower bounds 47 | if point.position.y > y_sampled_domain.lb || point.position.y < y_sampled_domain.ub: 48 | continue 49 | 50 | _points.push_back(point) 51 | points_positions.push_back(_position) 52 | 53 | func draw_function_point(point_position: Vector2) -> void: 54 | match function.get_marker(): 55 | Function.Marker.SQUARE: 56 | draw_rect( 57 | Rect2(point_position - (Vector2.ONE * _point_size), (Vector2.ONE * _point_size * 2)), 58 | function.get_color(), true, 1.0 59 | ) 60 | Function.Marker.TRIANGLE: 61 | draw_colored_polygon( 62 | PackedVector2Array([ 63 | point_position + (Vector2.UP * _point_size * 1.3), 64 | point_position + (Vector2.ONE * _point_size * 1.3), 65 | point_position - (Vector2(1, -1) * _point_size * 1.3) 66 | ]), function.get_color(), [], null 67 | ) 68 | Function.Marker.CROSS: 69 | draw_line( 70 | point_position - (Vector2.ONE * _point_size), 71 | point_position + (Vector2.ONE * _point_size), 72 | function.get_color(), _point_size, true 73 | ) 74 | draw_line( 75 | point_position + (Vector2(1, -1) * _point_size), 76 | point_position + (Vector2(-1, 1) * _point_size), 77 | function.get_color(), _point_size / 2, true 78 | ) 79 | Function.Marker.CIRCLE, _: 80 | draw_circle(point_position, _point_size, function.get_color()) 81 | 82 | func _input(event: InputEvent) -> void: 83 | if event is InputEventMouse: 84 | for point in _points: 85 | if Geometry2D.is_point_in_circle(get_relative_position(event.position), point.position, _point_size * 4): 86 | if focused_point == point: 87 | return 88 | else: 89 | focused_point = point 90 | emit_signal("point_entered", point, function) 91 | return 92 | # Mouse is not in any point's box 93 | emit_signal("point_exited", focused_point, function) 94 | focused_point = null 95 | -------------------------------------------------------------------------------- /addons/easy_charts/control_charts/plotters/scatter_plotter.gd.uid: -------------------------------------------------------------------------------- 1 | uid://bx0yko2nptnbh 2 | -------------------------------------------------------------------------------- /addons/easy_charts/examples/area_chart/area_chart_example.gd: -------------------------------------------------------------------------------- 1 | extends Control 2 | 3 | @onready var chart: Chart = $VBoxContainer/Chart 4 | 5 | # This Chart will plot 3 different functions 6 | var f1: Function 7 | 8 | func _ready(): 9 | # Let's create our @x values 10 | var x: Array = ArrayOperations.multiply_float(range(-10, 11, 1), 0.5) 11 | 12 | # And our y values. It can be an n-size array of arrays. 13 | # NOTE: `x.size() == y.size()` or `x.size() == y[n].size()` 14 | var y: Array = ArrayOperations.multiply_int(ArrayOperations.cos(x), 20) 15 | 16 | # Let's customize the chart properties, which specify how the chart 17 | # should look, plus some additional elements like labels, the scale, etc... 18 | var cp: ChartProperties = ChartProperties.new() 19 | cp.colors.frame = Color("#161a1d") 20 | cp.colors.background = Color.TRANSPARENT 21 | cp.colors.grid = Color("#283442") 22 | cp.colors.ticks = Color("#283442") 23 | cp.colors.text = Color.WHITE_SMOKE 24 | cp.draw_bounding_box = false 25 | cp.title = "Air Quality Monitoring" 26 | cp.x_label = "Time" 27 | cp.y_label = "Sensor values" 28 | cp.x_scale = 5 29 | cp.y_scale = 10 30 | cp.interactive = true # false by default, it allows the chart to create a tooltip to show point values 31 | # and interecept clicks on the plot 32 | 33 | # Let's add values to our functions 34 | f1 = Function.new( 35 | x, y, "Pressure", # This will create a function with x and y values taken by the Arrays 36 | # we have created previously. This function will also be named "Pressure" 37 | # as it contains 'pressure' values. 38 | # If set, the name of a function will be used both in the Legend 39 | # (if enabled thourgh ChartProperties) and on the Tooltip (if enabled). 40 | # Let's also provide a dictionary of configuration parameters for this specific function. 41 | { 42 | color = Color("#36a2eb"), # The color associated to this function 43 | marker = Function.Marker.NONE, # The marker that will be displayed for each drawn point (x,y) 44 | # since it is `NONE`, no marker will be shown. 45 | type = Function.Type.AREA, # This defines what kind of plotting will be used, 46 | # in this case it will be an Area Chart. 47 | } 48 | ) 49 | 50 | # Now let's plot our data 51 | chart.plot([f1], cp) 52 | 53 | # Uncommenting this line will show how real time data plotting works 54 | set_process(false) 55 | 56 | 57 | var new_val: float = 4.5 58 | 59 | func _process(delta: float): 60 | # This function updates the values of a function and then updates the plot 61 | new_val += 5 62 | 63 | # we can use the `Function.add_point(x, y)` method to update a function 64 | f1.add_point(new_val, cos(new_val) * 20) 65 | chart.queue_redraw() # This will force the Chart to be updated 66 | 67 | 68 | func _on_CheckButton_pressed(): 69 | set_process(not is_processing()) 70 | -------------------------------------------------------------------------------- /addons/easy_charts/examples/area_chart/area_chart_example.gd.uid: -------------------------------------------------------------------------------- 1 | uid://bcaslhu2t7xr3 2 | -------------------------------------------------------------------------------- /addons/easy_charts/examples/area_chart/area_chart_example.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=3 uid="uid://c2ymglyg812ss"] 2 | 3 | [ext_resource type="Script" uid="uid://bcaslhu2t7xr3" path="res://addons/easy_charts/examples/area_chart/area_chart_example.gd" id="1"] 4 | [ext_resource type="PackedScene" uid="uid://dlwq4kmdb3bhs" path="res://addons/easy_charts/control_charts/chart.tscn" id="2"] 5 | 6 | [sub_resource type="StyleBoxFlat" id="1"] 7 | content_margin_right = 5.0 8 | content_margin_bottom = 5.0 9 | draw_center = false 10 | border_width_right = 2 11 | border_width_bottom = 2 12 | border_color = Color(0, 0, 0, 1) 13 | 14 | [node name="Control" type="Control"] 15 | layout_mode = 3 16 | anchors_preset = 15 17 | anchor_right = 1.0 18 | anchor_bottom = 1.0 19 | grow_horizontal = 2 20 | grow_vertical = 2 21 | script = ExtResource("1") 22 | 23 | [node name="VBoxContainer" type="VBoxContainer" parent="."] 24 | layout_mode = 0 25 | anchor_right = 1.0 26 | anchor_bottom = 1.0 27 | 28 | [node name="CheckButton" type="CheckButton" parent="VBoxContainer"] 29 | layout_mode = 2 30 | theme_override_colors/font_disabled_color = Color(0, 0, 0, 1) 31 | theme_override_colors/font_hover_pressed_color = Color(0, 0, 0, 1) 32 | theme_override_colors/font_hover_color = Color(0, 0, 0, 1) 33 | theme_override_colors/font_color = Color(0, 0, 0, 1) 34 | theme_override_colors/font_focus_color = Color(0, 0, 0, 1) 35 | theme_override_colors/font_pressed_color = Color(0, 0, 0, 1) 36 | text = "Start Relatime Plotting" 37 | 38 | [node name="Chart" parent="VBoxContainer" instance=ExtResource("2")] 39 | layout_mode = 2 40 | 41 | [node name="Label" type="Label" parent="VBoxContainer"] 42 | layout_mode = 2 43 | size_flags_horizontal = 8 44 | theme_override_colors/font_color = Color(0, 0, 0, 1) 45 | theme_override_styles/normal = SubResource("1") 46 | text = "Try to scale the window!" 47 | 48 | [connection signal="pressed" from="VBoxContainer/CheckButton" to="." method="_on_CheckButton_pressed"] 49 | -------------------------------------------------------------------------------- /addons/easy_charts/examples/bar_chart/bar_chart_example.gd: -------------------------------------------------------------------------------- 1 | extends Control 2 | 3 | @onready var chart: Chart = $VBoxContainer/Chart 4 | 5 | func _ready(): 6 | # Let's create our @x values 7 | var x: Array = range(0, 24).map(func(i: int) -> String: return "%d - %d h" % [i, i+1]) 8 | 9 | # And our y values. It can be an n-size array of arrays. 10 | # NOTE: `x.size() == y.size()` or `x.size() == y[n].size()` 11 | var y1: Array = ArrayOperations.add_int(ArrayOperations.multiply_int(range(0, 24), 10), 4) 12 | var y2: Array = ArrayOperations.add_int(ArrayOperations.multiply_int(range(0, 24), 5), 4) 13 | 14 | # Let's customize the chart properties, which specify how the chart 15 | # should look, plus some additional elements like labels, the scale, etc... 16 | var cp: ChartProperties = ChartProperties.new() 17 | cp.colors.frame = Color("#161a1d") 18 | cp.colors.background = Color.TRANSPARENT 19 | cp.colors.grid = Color("#283442") 20 | cp.colors.ticks = Color("#283442") 21 | cp.colors.text = Color.WHITE_SMOKE 22 | cp.y_scale = 10 23 | cp.draw_origin = true 24 | cp.draw_bounding_box = false 25 | cp.draw_vertical_grid = true 26 | cp.interactive = true # false by default, it allows the chart to create a tooltip to show point values 27 | # and interecept clicks on the plot 28 | 29 | # Let's add values to our functions 30 | # This will create a function with x and y values taken by the Arrays 31 | # we have created previously. This function will also be named "Pressure" 32 | # as it contains 'pressure' values. 33 | # If set, the name of a function will be used both in the Legend 34 | # (if enabled thourgh ChartProperties) and on the Tooltip (if enabled). 35 | var f1 = Function.new( 36 | x, y1, "Users", 37 | { 38 | type = Function.Type.BAR, 39 | bar_size = 5, 40 | color = Color.SEA_GREEN, 41 | } 42 | ) 43 | 44 | var f2 = Function.new( 45 | x, y2, "Impressions", 46 | { 47 | type = Function.Type.BAR, 48 | bar_size = 5, 49 | color = Color.SKY_BLUE, 50 | } 51 | ) 52 | 53 | var f3 := Function.new( 54 | x, y1, "Conversions", 55 | { 56 | type = Function.Type.BAR, 57 | bar_size = 5, 58 | color = Color.YELLOW, 59 | } 60 | ) 61 | 62 | var f4 := Function.new( 63 | x, y2, "Clicks", 64 | { 65 | type = Function.Type.BAR, 66 | bar_size = 5, 67 | color = Color.DARK_RED, 68 | } 69 | ) 70 | 71 | # Now let's plot our data 72 | chart.plot([f1, f2, f3, f4], cp) 73 | -------------------------------------------------------------------------------- /addons/easy_charts/examples/bar_chart/bar_chart_example.gd.uid: -------------------------------------------------------------------------------- 1 | uid://b1ioy02qwjlpe 2 | -------------------------------------------------------------------------------- /addons/easy_charts/examples/bar_chart/bar_chart_example.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=3 uid="uid://dn8rdqup8ldnw"] 2 | 3 | [ext_resource type="Script" uid="uid://b1ioy02qwjlpe" path="res://addons/easy_charts/examples/bar_chart/bar_chart_example.gd" id="1"] 4 | [ext_resource type="PackedScene" uid="uid://dlwq4kmdb3bhs" path="res://addons/easy_charts/control_charts/chart.tscn" id="2"] 5 | 6 | [sub_resource type="StyleBoxFlat" id="1"] 7 | content_margin_right = 5.0 8 | content_margin_bottom = 5.0 9 | draw_center = false 10 | border_width_right = 2 11 | border_width_bottom = 2 12 | border_color = Color(0, 0, 0, 1) 13 | 14 | [node name="Control2" type="Control"] 15 | layout_mode = 3 16 | anchors_preset = 15 17 | anchor_right = 1.0 18 | anchor_bottom = 1.0 19 | grow_horizontal = 2 20 | grow_vertical = 2 21 | script = ExtResource("1") 22 | 23 | [node name="VBoxContainer" type="VBoxContainer" parent="."] 24 | layout_mode = 0 25 | anchor_right = 1.0 26 | anchor_bottom = 1.0 27 | 28 | [node name="CheckButton" type="CheckButton" parent="VBoxContainer"] 29 | layout_mode = 2 30 | theme_override_colors/font_disabled_color = Color(0, 0, 0, 1) 31 | theme_override_colors/font_hover_pressed_color = Color(0, 0, 0, 1) 32 | theme_override_colors/font_hover_color = Color(0, 0, 0, 1) 33 | theme_override_colors/font_color = Color(0, 0, 0, 1) 34 | theme_override_colors/font_focus_color = Color(0, 0, 0, 1) 35 | theme_override_colors/font_pressed_color = Color(0, 0, 0, 1) 36 | text = "Start Relatime Plotting" 37 | 38 | [node name="Chart" parent="VBoxContainer" instance=ExtResource("2")] 39 | layout_mode = 2 40 | 41 | [node name="Label" type="Label" parent="VBoxContainer"] 42 | layout_mode = 2 43 | size_flags_horizontal = 8 44 | theme_override_colors/font_color = Color(0, 0, 0, 1) 45 | theme_override_styles/normal = SubResource("1") 46 | text = "Try to scale the window!" 47 | 48 | [connection signal="pressed" from="VBoxContainer/CheckButton" to="." method="_on_CheckButton_pressed"] 49 | -------------------------------------------------------------------------------- /addons/easy_charts/examples/line_chart/line_chart_example.gd: -------------------------------------------------------------------------------- 1 | extends Control 2 | 3 | @onready var chart: Chart = $VBoxContainer/Chart 4 | 5 | # This Chart will plot 3 different functions 6 | var f1: Function 7 | 8 | func _ready(): 9 | # Let's create our @x values 10 | var x: PackedFloat32Array = ArrayOperations.multiply_float(range(-10, 11, 1), 0.5) 11 | 12 | # And our y values. It can be an n-size array of arrays. 13 | # NOTE: `x.size() == y.size()` or `x.size() == y[n].size()` 14 | var y: Array = ArrayOperations.multiply_int(ArrayOperations.cos(x), 20) 15 | 16 | # Let's customize the chart properties, which specify how the chart 17 | # should look, plus some additional elements like labels, the scale, etc... 18 | var cp: ChartProperties = ChartProperties.new() 19 | cp.colors.frame = Color("#161a1d") 20 | cp.colors.background = Color.TRANSPARENT 21 | cp.colors.grid = Color("#283442") 22 | cp.colors.ticks = Color("#283442") 23 | cp.colors.text = Color.WHITE_SMOKE 24 | cp.draw_bounding_box = false 25 | cp.title = "Air Quality Monitoring" 26 | cp.x_label = "Time" 27 | cp.y_label = "Sensor values" 28 | cp.x_scale = 5 29 | cp.y_scale = 10 30 | cp.interactive = true # false by default, it allows the chart to create a tooltip to show point values 31 | # and interecept clicks on the plot 32 | 33 | # Let's add values to our functions 34 | f1 = Function.new( 35 | x, y, "Pressure", # This will create a function with x and y values taken by the Arrays 36 | # we have created previously. This function will also be named "Pressure" 37 | # as it contains 'pressure' values. 38 | # If set, the name of a function will be used both in the Legend 39 | # (if enabled thourgh ChartProperties) and on the Tooltip (if enabled). 40 | # Let's also provide a dictionary of configuration parameters for this specific function. 41 | { 42 | color = Color("#36a2eb"), # The color associated to this function 43 | marker = Function.Marker.CIRCLE, # The marker that will be displayed for each drawn point (x,y) 44 | # since it is `NONE`, no marker will be shown. 45 | type = Function.Type.LINE, # This defines what kind of plotting will be used, 46 | # in this case it will be a Linear Chart. 47 | interpolation = Function.Interpolation.STAIR # Interpolation mode, only used for 48 | # Line Charts and Area Charts. 49 | } 50 | ) 51 | 52 | # Now let's plot our data 53 | chart.plot([f1], cp) 54 | 55 | # Uncommenting this line will show how real time data plotting works 56 | set_process(false) 57 | 58 | 59 | var new_val: float = 4.5 60 | 61 | func _process(delta: float): 62 | # This function updates the values of a function and then updates the plot 63 | new_val += 5 64 | 65 | # we can use the `Function.add_point(x, y)` method to update a function 66 | f1.add_point(new_val, cos(new_val) * 20) 67 | f1.remove_point(0) 68 | chart.queue_redraw() # This will force the Chart to be updated 69 | 70 | 71 | func _on_CheckButton_pressed(): 72 | set_process(not is_processing()) 73 | -------------------------------------------------------------------------------- /addons/easy_charts/examples/line_chart/line_chart_example.gd.uid: -------------------------------------------------------------------------------- 1 | uid://xafq3pfkghx0 2 | -------------------------------------------------------------------------------- /addons/easy_charts/examples/line_chart/line_chart_example.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=3 uid="uid://chcj7up8k8pa8"] 2 | 3 | [ext_resource type="Script" uid="uid://xafq3pfkghx0" path="res://addons/easy_charts/examples/line_chart/line_chart_example.gd" id="1"] 4 | [ext_resource type="PackedScene" uid="uid://dlwq4kmdb3bhs" path="res://addons/easy_charts/control_charts/chart.tscn" id="2"] 5 | 6 | [sub_resource type="StyleBoxFlat" id="1"] 7 | content_margin_right = 5.0 8 | content_margin_bottom = 5.0 9 | draw_center = false 10 | border_width_right = 2 11 | border_width_bottom = 2 12 | border_color = Color(0, 0, 0, 1) 13 | 14 | [node name="Control2" type="Control"] 15 | layout_mode = 3 16 | anchors_preset = 15 17 | anchor_right = 1.0 18 | anchor_bottom = 1.0 19 | grow_horizontal = 2 20 | grow_vertical = 2 21 | script = ExtResource("1") 22 | 23 | [node name="VBoxContainer" type="VBoxContainer" parent="."] 24 | layout_mode = 0 25 | anchor_right = 1.0 26 | anchor_bottom = 1.0 27 | 28 | [node name="CheckButton" type="CheckButton" parent="VBoxContainer"] 29 | layout_mode = 2 30 | theme_override_colors/font_disabled_color = Color(0, 0, 0, 1) 31 | theme_override_colors/font_hover_pressed_color = Color(0, 0, 0, 1) 32 | theme_override_colors/font_hover_color = Color(0, 0, 0, 1) 33 | theme_override_colors/font_color = Color(0, 0, 0, 1) 34 | theme_override_colors/font_focus_color = Color(0, 0, 0, 1) 35 | theme_override_colors/font_pressed_color = Color(0, 0, 0, 1) 36 | text = "Start Relatime Plotting" 37 | 38 | [node name="Chart" parent="VBoxContainer" instance=ExtResource("2")] 39 | layout_mode = 2 40 | 41 | [node name="Label" type="Label" parent="VBoxContainer"] 42 | layout_mode = 2 43 | size_flags_horizontal = 8 44 | theme_override_colors/font_color = Color(0, 0, 0, 1) 45 | theme_override_styles/normal = SubResource("1") 46 | text = "Try to scale the window!" 47 | 48 | [connection signal="pressed" from="VBoxContainer/CheckButton" to="." method="_on_CheckButton_pressed"] 49 | -------------------------------------------------------------------------------- /addons/easy_charts/examples/multiplot/multiplot_example.gd: -------------------------------------------------------------------------------- 1 | extends Control 2 | 3 | @onready var chart: Chart = $VBoxContainer/Chart 4 | 5 | # This Chart will plot 3 different functions 6 | var f1: Function 7 | var f2: Function 8 | var f3: Function 9 | 10 | func _ready(): 11 | # Let's create our @x values 12 | var x: Array = ArrayOperations.multiply_float(range(-10, 11, 1), 0.5) 13 | 14 | # And our y values. It can be an n-size array of arrays. 15 | # NOTE: `x.size() == y.size()` or `x.size() == y[n].size()` 16 | var y: Array = ArrayOperations.multiply_int(ArrayOperations.cos(x), 20) 17 | var y2: Array = ArrayOperations.add_float(ArrayOperations.multiply_int(ArrayOperations.sin(x), 20), 20) 18 | var y3: Array = ArrayOperations.add_float(ArrayOperations.multiply_int(ArrayOperations.cos(x), -5), -3) 19 | 20 | # Let's customize the chart properties, which specify how the chart 21 | # should look, plus some additional elements like labels, the scale, etc... 22 | var cp: ChartProperties = ChartProperties.new() 23 | cp.colors.frame = Color("#161a1d") 24 | cp.colors.background = Color.TRANSPARENT 25 | cp.colors.grid = Color("#283442") 26 | cp.colors.ticks = Color("#283442") 27 | cp.colors.text = Color.WHITE_SMOKE 28 | cp.draw_bounding_box = false 29 | cp.show_legend = true 30 | cp.title = "Air Quality Monitoring" 31 | cp.x_label = "Time" 32 | cp.y_label = "Sensor values" 33 | cp.x_scale = 5 34 | cp.y_scale = 10 35 | cp.interactive = true # false by default, it allows the chart to create a tooltip to show point values 36 | # and interecept clicks on the plot 37 | 38 | # Let's add values to our functions 39 | f1 = Function.new( 40 | x, y, "Pressure", # This will create a function with x and y values taken by the Arrays 41 | # we have created previously. This function will also be named "Pressure" 42 | # as it contains 'pressure' values. 43 | # If set, the name of a function will be used both in the Legend 44 | # (if enabled thourgh ChartProperties) and on the Tooltip (if enabled). 45 | # Let's also provide a dictionary of configuration parameters for this specific function. 46 | { 47 | color = Color("#36a2eb"), # The color associated to this function 48 | marker = Function.Marker.NONE, # The marker that will be displayed for each drawn point (x,y) 49 | # since it is `NONE`, no marker will be shown. 50 | type = Function.Type.AREA, # This defines what kind of plotting will be used, 51 | # in this case it will be an Area Chart. 52 | interpolation = Function.Interpolation.STAIR # Interpolation mode, only used for 53 | # Line Charts and Area Charts. 54 | } 55 | ) 56 | f2 = Function.new(x, y2, "Humidity", { color = Color("#ff6384"), marker = Function.Marker.CROSS }) 57 | f3 = Function.new(x, y3, "CO2", { color = Color.GREEN, marker = Function.Marker.TRIANGLE }) 58 | 59 | # Now let's plot our data 60 | chart.plot([f1, f2, f3], cp) 61 | 62 | # Uncommenting this line will show how real time data plotting works 63 | set_process(false) 64 | 65 | 66 | var new_val: float = 4.5 67 | 68 | func _process(delta: float): 69 | # This function updates the values of a function and then updates the plot 70 | new_val += 5 71 | 72 | # we can use the `Function.add_point(x, y)` method to update a function 73 | f1.add_point(new_val, cos(new_val) * 20) 74 | f2.add_point(new_val, (sin(new_val) * 20) + 20) 75 | f3.add_point(new_val, (cos(new_val) * -5) - 3) 76 | chart.queue_redraw() # This will force the Chart to be updated 77 | 78 | 79 | func _on_CheckButton_pressed(): 80 | set_process(not is_processing()) 81 | -------------------------------------------------------------------------------- /addons/easy_charts/examples/multiplot/multiplot_example.gd.uid: -------------------------------------------------------------------------------- 1 | uid://ouq5e3pbw5c1 2 | -------------------------------------------------------------------------------- /addons/easy_charts/examples/multiplot/multiplot_example.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=3 uid="uid://7v0v5lsl0kqe"] 2 | 3 | [ext_resource type="Script" uid="uid://ouq5e3pbw5c1" path="res://addons/easy_charts/examples/multiplot/multiplot_example.gd" id="1"] 4 | [ext_resource type="PackedScene" uid="uid://dlwq4kmdb3bhs" path="res://addons/easy_charts/control_charts/chart.tscn" id="2"] 5 | 6 | [sub_resource type="StyleBoxFlat" id="1"] 7 | content_margin_right = 5.0 8 | content_margin_bottom = 5.0 9 | draw_center = false 10 | border_width_right = 2 11 | border_width_bottom = 2 12 | border_color = Color(0, 0, 0, 1) 13 | 14 | [node name="Control2" type="Control"] 15 | layout_mode = 3 16 | anchors_preset = 15 17 | anchor_right = 1.0 18 | anchor_bottom = 1.0 19 | grow_horizontal = 2 20 | grow_vertical = 2 21 | script = ExtResource("1") 22 | 23 | [node name="VBoxContainer" type="VBoxContainer" parent="."] 24 | layout_mode = 0 25 | anchor_right = 1.0 26 | anchor_bottom = 1.0 27 | 28 | [node name="CheckButton" type="CheckButton" parent="VBoxContainer"] 29 | layout_mode = 2 30 | theme_override_colors/font_disabled_color = Color(0, 0, 0, 1) 31 | theme_override_colors/font_hover_pressed_color = Color(0, 0, 0, 1) 32 | theme_override_colors/font_hover_color = Color(0, 0, 0, 1) 33 | theme_override_colors/font_color = Color(0, 0, 0, 1) 34 | theme_override_colors/font_focus_color = Color(0, 0, 0, 1) 35 | theme_override_colors/font_pressed_color = Color(0, 0, 0, 1) 36 | text = "Start Relatime Plotting" 37 | 38 | [node name="Chart" parent="VBoxContainer" instance=ExtResource("2")] 39 | layout_mode = 2 40 | 41 | [node name="Label" type="Label" parent="VBoxContainer"] 42 | layout_mode = 2 43 | size_flags_horizontal = 8 44 | theme_override_colors/font_color = Color(0, 0, 0, 1) 45 | theme_override_styles/normal = SubResource("1") 46 | text = "Try to scale the window!" 47 | 48 | [connection signal="pressed" from="VBoxContainer/CheckButton" to="." method="_on_CheckButton_pressed"] 49 | -------------------------------------------------------------------------------- /addons/easy_charts/examples/pie_chart/pie_chart_example.gd: -------------------------------------------------------------------------------- 1 | extends Control 2 | 3 | @onready var chart: Chart = $VBoxContainer/Chart 4 | 5 | # This Chart will plot 3 different functions 6 | var f1: Function 7 | 8 | func _ready(): 9 | # Let's create our @x values 10 | var x: Array = [100, 400] 11 | 12 | # And our y values. It can be an n-size array of arrays. 13 | # NOTE: `x.size() == y.size()` or `x.size() == y[n].size()` 14 | var y: Array = ["Java", "JavaScript", "C++", "GDScript"] 15 | 16 | # Let's customize the chart properties, which specify how the chart 17 | # should look, plus some additional elements like labels, the scale, etc... 18 | var cp: ChartProperties = ChartProperties.new() 19 | cp.colors.frame = Color("#161a1d") 20 | cp.colors.background = Color.TRANSPARENT 21 | cp.colors.grid = Color("#283442") 22 | cp.colors.ticks = Color("#283442") 23 | cp.colors.text = Color.WHITE_SMOKE 24 | cp.draw_bounding_box = false 25 | cp.title = "Users preferences on programming languages" 26 | cp.draw_grid_box = false 27 | cp.show_legend = true 28 | cp.interactive = true # false by default, it allows the chart to create a tooltip to show point values 29 | # and interecept clicks on the plot 30 | 31 | var gradient: Gradient = Gradient.new() 32 | gradient.set_color(0, Color.AQUAMARINE) 33 | gradient.set_color(1, Color.DEEP_PINK) 34 | 35 | # Let's add values to our functions 36 | f1 = Function.new( 37 | x, y, "Language", # This will create a function with x and y values taken by the Arrays 38 | # we have created previously. This function will also be named "Pressure" 39 | # as it contains 'pressure' values. 40 | # If set, the name of a function will be used both in the Legend 41 | # (if enabled thourgh ChartProperties) and on the Tooltip (if enabled). 42 | { 43 | gradient = gradient, 44 | type = Function.Type.PIE 45 | } 46 | ) 47 | 48 | # Now let's plot our data 49 | chart.plot([f1], cp) 50 | 51 | # Uncommenting this line will show how real time data plotting works 52 | set_process(false) 53 | 54 | 55 | var new_val: float = 4.5 56 | 57 | func _process(delta: float): 58 | # This function updates the values of a function and then updates the plot 59 | new_val += 5 60 | 61 | # we can use the `Function.add_point(x, y)` method to update a function 62 | f1.add_point(new_val, cos(new_val) * 20) 63 | chart.queue_redraw() # This will force the Chart to be updated 64 | 65 | 66 | func _on_CheckButton_pressed(): 67 | set_process(not is_processing()) 68 | -------------------------------------------------------------------------------- /addons/easy_charts/examples/pie_chart/pie_chart_example.gd.uid: -------------------------------------------------------------------------------- 1 | uid://btfgl143uq8jc 2 | -------------------------------------------------------------------------------- /addons/easy_charts/examples/pie_chart/pie_chart_example.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=3 uid="uid://be3nkm3rmqe7m"] 2 | 3 | [ext_resource type="Script" uid="uid://btfgl143uq8jc" path="res://addons/easy_charts/examples/pie_chart/pie_chart_example.gd" id="1"] 4 | [ext_resource type="PackedScene" uid="uid://dlwq4kmdb3bhs" path="res://addons/easy_charts/control_charts/chart.tscn" id="2"] 5 | 6 | [sub_resource type="StyleBoxFlat" id="1"] 7 | content_margin_right = 5.0 8 | content_margin_bottom = 5.0 9 | draw_center = false 10 | border_width_right = 2 11 | border_width_bottom = 2 12 | border_color = Color(0, 0, 0, 1) 13 | 14 | [node name="Control2" type="Control"] 15 | layout_mode = 3 16 | anchors_preset = 15 17 | anchor_right = 1.0 18 | anchor_bottom = 1.0 19 | grow_horizontal = 2 20 | grow_vertical = 2 21 | script = ExtResource("1") 22 | 23 | [node name="VBoxContainer" type="VBoxContainer" parent="."] 24 | layout_mode = 0 25 | anchor_right = 1.0 26 | anchor_bottom = 1.0 27 | 28 | [node name="CheckButton" type="CheckButton" parent="VBoxContainer"] 29 | layout_mode = 2 30 | theme_override_colors/font_disabled_color = Color(0, 0, 0, 1) 31 | theme_override_colors/font_hover_pressed_color = Color(0, 0, 0, 1) 32 | theme_override_colors/font_hover_color = Color(0, 0, 0, 1) 33 | theme_override_colors/font_color = Color(0, 0, 0, 1) 34 | theme_override_colors/font_focus_color = Color(0, 0, 0, 1) 35 | theme_override_colors/font_pressed_color = Color(0, 0, 0, 1) 36 | text = "Start Relatime Plotting" 37 | 38 | [node name="Chart" parent="VBoxContainer" instance=ExtResource("2")] 39 | layout_mode = 2 40 | 41 | [node name="Label" type="Label" parent="VBoxContainer"] 42 | layout_mode = 2 43 | size_flags_horizontal = 8 44 | theme_override_colors/font_color = Color(0, 0, 0, 1) 45 | theme_override_styles/normal = SubResource("1") 46 | text = "Try to scale the window!" 47 | 48 | [connection signal="pressed" from="VBoxContainer/CheckButton" to="." method="_on_CheckButton_pressed"] 49 | -------------------------------------------------------------------------------- /addons/easy_charts/examples/scatter_chart/scatter_chart_example.gd: -------------------------------------------------------------------------------- 1 | extends Control 2 | 3 | @onready var chart: Chart = $VBoxContainer/Chart 4 | 5 | # This Chart will plot 3 different functions 6 | var f1: Function 7 | var f2: Function 8 | 9 | func _ready(): 10 | # Let's create our @x values 11 | var x: Array = ArrayOperations.multiply_float(range(-10, 11, 1), 0.5) 12 | 13 | # And our y values. It can be an n-size array of arrays. 14 | # NOTE: `x.size() == y.size()` or `x.size() == y[n].size()` 15 | var y: Array = ArrayOperations.multiply_int(ArrayOperations.cos(x), 20) 16 | var y2: Array = ArrayOperations.add_float(ArrayOperations.multiply_int(ArrayOperations.sin(x), 20), 20) 17 | 18 | # Let's customize the chart properties, which specify how the chart 19 | # should look, plus some additional elements like labels, the scale, etc... 20 | var cp: ChartProperties = ChartProperties.new() 21 | cp.colors.frame = Color("#161a1d") 22 | cp.colors.background = Color.TRANSPARENT 23 | cp.colors.grid = Color("#283442") 24 | cp.colors.ticks = Color("#283442") 25 | cp.colors.text = Color.WHITE_SMOKE 26 | cp.draw_bounding_box = false 27 | cp.title = "Air Quality Monitoring" 28 | cp.x_label = "Time" 29 | cp.y_label = "Sensor values" 30 | cp.x_scale = 5 31 | cp.y_scale = 10 32 | cp.interactive = true # false by default, it allows the chart to create a tooltip to show point values 33 | # and interecept clicks on the plot 34 | 35 | # Let's add values to our functions 36 | f1 = Function.new( 37 | x, y, "Pressure", # This will create a function with x and y values taken by the Arrays 38 | # we have created previously. This function will also be named "Pressure" 39 | # as it contains 'pressure' values. 40 | # If set, the name of a function will be used both in the Legend 41 | # (if enabled thourgh ChartProperties) and on the Tooltip (if enabled). 42 | { color = Color.GREEN, marker = Function.Marker.CIRCLE } 43 | ) 44 | f2 = Function.new(x, y2, "Humidity", { color = Color("#ff6384"), marker = Function.Marker.CROSS }) 45 | 46 | # Now let's plot our data 47 | chart.plot([f1, f2], cp) 48 | 49 | # Uncommenting this line will show how real time data plotting works 50 | set_process(false) 51 | 52 | 53 | var new_val: float = 4.5 54 | 55 | func _process(delta: float): 56 | # This function updates the values of a function and then updates the plot 57 | new_val += 5 58 | 59 | # we can use the `Function.add_point(x, y)` method to update a function 60 | f1.add_point(new_val, cos(new_val) * 20) 61 | f2.add_point(new_val, (sin(new_val) * 20) + 20) 62 | chart.queue_redraw() # This will force the Chart to be updated 63 | 64 | 65 | func _on_CheckButton_pressed(): 66 | set_process(not is_processing()) 67 | -------------------------------------------------------------------------------- /addons/easy_charts/examples/scatter_chart/scatter_chart_example.gd.uid: -------------------------------------------------------------------------------- 1 | uid://drjv3jppfmods 2 | -------------------------------------------------------------------------------- /addons/easy_charts/examples/scatter_chart/scatter_chart_example.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=3 uid="uid://cekkstadxpimf"] 2 | 3 | [ext_resource type="Script" uid="uid://drjv3jppfmods" path="res://addons/easy_charts/examples/scatter_chart/scatter_chart_example.gd" id="1"] 4 | [ext_resource type="PackedScene" uid="uid://dlwq4kmdb3bhs" path="res://addons/easy_charts/control_charts/chart.tscn" id="2"] 5 | 6 | [sub_resource type="StyleBoxFlat" id="1"] 7 | content_margin_right = 5.0 8 | content_margin_bottom = 5.0 9 | draw_center = false 10 | border_width_right = 2 11 | border_width_bottom = 2 12 | border_color = Color(0, 0, 0, 1) 13 | 14 | [node name="Control2" type="Control"] 15 | layout_mode = 3 16 | anchors_preset = 15 17 | anchor_right = 1.0 18 | anchor_bottom = 1.0 19 | grow_horizontal = 2 20 | grow_vertical = 2 21 | script = ExtResource("1") 22 | 23 | [node name="VBoxContainer" type="VBoxContainer" parent="."] 24 | layout_mode = 0 25 | anchor_right = 1.0 26 | anchor_bottom = 1.0 27 | 28 | [node name="CheckButton" type="CheckButton" parent="VBoxContainer"] 29 | layout_mode = 2 30 | theme_override_colors/font_disabled_color = Color(0, 0, 0, 1) 31 | theme_override_colors/font_hover_pressed_color = Color(0, 0, 0, 1) 32 | theme_override_colors/font_hover_color = Color(0, 0, 0, 1) 33 | theme_override_colors/font_color = Color(0, 0, 0, 1) 34 | theme_override_colors/font_focus_color = Color(0, 0, 0, 1) 35 | theme_override_colors/font_pressed_color = Color(0, 0, 0, 1) 36 | text = "Start Relatime Plotting" 37 | 38 | [node name="Chart" parent="VBoxContainer" instance=ExtResource("2")] 39 | layout_mode = 2 40 | 41 | [node name="Label" type="Label" parent="VBoxContainer"] 42 | layout_mode = 2 43 | size_flags_horizontal = 8 44 | theme_override_colors/font_color = Color(0, 0, 0, 1) 45 | theme_override_styles/normal = SubResource("1") 46 | text = "Try to scale the window!" 47 | 48 | [connection signal="pressed" from="VBoxContainer/CheckButton" to="." method="_on_CheckButton_pressed"] 49 | -------------------------------------------------------------------------------- /addons/easy_charts/examples/scatter_chart_discrete/scatter_chart_discrete.gd: -------------------------------------------------------------------------------- 1 | extends Control 2 | 3 | @onready var chart: Chart = $VBoxContainer/Chart 4 | 5 | func _ready(): 6 | # X values will be the hours of the day, starting with 0 ending on 23. 7 | var x: Array = range(0, 24).map(func(i) -> String: return "%d - %d h" % [i, i+1]) 8 | 9 | # Arrays contain how many animals have been seen in each hour. 10 | var blackbird_spots: Array = [0, 0, 0, 0, 0, 0, 0, 4, 5, 3, 6, 0, 0, 0, 2, 0, 0, 4, 5, 0, 0, 0, 0, 0] 11 | var nightingale_spots: Array = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 2, 1, 0, 4, 4, 0, 3, 0, 0] 12 | 13 | # Let's customize the chart properties, which specify how the chart 14 | # should look, plus some additional elements like labels, the scale, etc... 15 | var cp: ChartProperties = ChartProperties.new() 16 | cp.colors.frame = Color("#161a1d") 17 | cp.colors.background = Color.TRANSPARENT 18 | cp.colors.grid = Color("#283442") 19 | cp.colors.ticks = Color("#283442") 20 | cp.colors.text = Color.WHITE_SMOKE 21 | cp.draw_bounding_box = false 22 | cp.title = "Animal spots" 23 | cp.x_label = "Time" 24 | cp.y_label = "Spots" 25 | cp.interactive = true 26 | cp.show_legend = true 27 | 28 | # Let's add values to our functions 29 | var blackbird_function = Function.new( 30 | x, 31 | blackbird_spots, 32 | "Blackbird", 33 | { color = Color.GREEN, marker = Function.Marker.CIRCLE, type = Function.Type.SCATTER } 34 | ) 35 | 36 | var nightingale_function = Function.new( 37 | x, 38 | nightingale_spots, 39 | "Nightingale", 40 | { color = Color.BLUE, marker = Function.Marker.CROSS, type = Function.Type.SCATTER } 41 | ) 42 | 43 | # Configure the y axis. We set the scale and domain in such 44 | # that we get ticks only on integers, not on floats. 45 | # We also configure the label function to not print decimal places. 46 | var y_max_value := 0 47 | for i in range(0, 24): 48 | if blackbird_spots[i] > y_max_value: 49 | y_max_value = blackbird_spots[i] 50 | if nightingale_spots[i] > y_max_value: 51 | y_max_value = nightingale_spots[i] 52 | # Add one or two on top so that we have some nice spacing 53 | y_max_value += 2 if (y_max_value % 2) == 0 else 1 54 | cp.y_scale = y_max_value / 2 55 | chart.set_y_domain(0, y_max_value) 56 | chart.y_labels_function = func(value: float): return str(int(value)) 57 | 58 | # Now let's plot our data 59 | chart.plot([blackbird_function, nightingale_function], cp) 60 | -------------------------------------------------------------------------------- /addons/easy_charts/examples/scatter_chart_discrete/scatter_chart_discrete.gd.uid: -------------------------------------------------------------------------------- 1 | uid://cwjc16brbjkia 2 | -------------------------------------------------------------------------------- /addons/easy_charts/examples/scatter_chart_discrete/scatter_chart_discrete.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=3 uid="uid://duq43d7ll1aah"] 2 | 3 | [ext_resource type="Script" uid="uid://cwjc16brbjkia" path="res://addons/easy_charts/examples/scatter_chart_discrete/scatter_chart_discrete.gd" id="1_3npap"] 4 | [ext_resource type="PackedScene" uid="uid://dlwq4kmdb3bhs" path="res://addons/easy_charts/control_charts/chart.tscn" id="2_pd01x"] 5 | 6 | [sub_resource type="StyleBoxFlat" id="1"] 7 | content_margin_right = 5.0 8 | content_margin_bottom = 5.0 9 | draw_center = false 10 | border_width_right = 2 11 | border_width_bottom = 2 12 | border_color = Color(0, 0, 0, 1) 13 | 14 | [node name="Control2" type="Control"] 15 | layout_mode = 3 16 | anchors_preset = 15 17 | anchor_right = 1.0 18 | anchor_bottom = 1.0 19 | grow_horizontal = 2 20 | grow_vertical = 2 21 | script = ExtResource("1_3npap") 22 | 23 | [node name="VBoxContainer" type="VBoxContainer" parent="."] 24 | layout_mode = 0 25 | anchor_right = 1.0 26 | anchor_bottom = 1.0 27 | 28 | [node name="Chart" parent="VBoxContainer" instance=ExtResource("2_pd01x")] 29 | layout_mode = 2 30 | 31 | [node name="Label" type="Label" parent="VBoxContainer"] 32 | layout_mode = 2 33 | size_flags_horizontal = 8 34 | theme_override_colors/font_color = Color(0, 0, 0, 1) 35 | theme_override_styles/normal = SubResource("1") 36 | text = "Try to scale the window!" 37 | -------------------------------------------------------------------------------- /addons/easy_charts/examples/simple_chart/simple_chart_example.gd: -------------------------------------------------------------------------------- 1 | # This example shows how to instantiate a Chart node at runtime and plot a single function 2 | 3 | extends Control 4 | 5 | @onready var chart_scn: PackedScene = load("res://addons/easy_charts/control_charts/chart.tscn") 6 | var chart: Chart 7 | 8 | # This Chart will plot 1 function 9 | var f1: Function 10 | 11 | func _ready(): 12 | chart = chart_scn.instantiate() 13 | $VBoxContainer.add_child(chart) 14 | 15 | # Let's create our @x values 16 | var x: Array = ArrayOperations.multiply_float(range(-10, 11, 1), 0.5) 17 | 18 | # And our y values. It can be an n-size array of arrays. 19 | # NOTE: `x.size() == y.size()` or `x.size() == y[n].size()` 20 | var y: Array = ArrayOperations.multiply_int(ArrayOperations.cos(x), 20) 21 | 22 | # Let's add values to our functions 23 | f1 = Function.new(x, y, "Pressure", { marker = Function.Marker.CIRCLE }) 24 | 25 | # Set fixed Y domain 26 | chart.set_y_domain(-50, 50) 27 | 28 | # Now let's plot our data 29 | chart.plot([f1]) 30 | 31 | # Uncommenting this line will show how real time data plotting works 32 | set_process(false) 33 | 34 | 35 | var new_val: float = 4.5 36 | 37 | func _process(delta: float): 38 | # This function updates the values of a function and then updates the plot 39 | new_val += 5 40 | 41 | # we can use the `Function.add_point(x, y)` method to update a function 42 | f1.add_point(new_val, cos(new_val) * 20) 43 | chart.queue_redraw() # This will force the Chart to be updated 44 | 45 | 46 | func _on_CheckButton_pressed(): 47 | set_process(not is_processing()) 48 | -------------------------------------------------------------------------------- /addons/easy_charts/examples/simple_chart/simple_chart_example.gd.uid: -------------------------------------------------------------------------------- 1 | uid://dikwjry6sb804 2 | -------------------------------------------------------------------------------- /addons/easy_charts/examples/simple_chart/simple_chart_example.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://v4c2f17q8a1o"] 2 | 3 | [ext_resource type="Script" uid="uid://dikwjry6sb804" path="res://addons/easy_charts/examples/simple_chart/simple_chart_example.gd" id="1"] 4 | 5 | [sub_resource type="StyleBoxFlat" id="1"] 6 | content_margin_right = 5.0 7 | content_margin_bottom = 5.0 8 | draw_center = false 9 | border_width_right = 2 10 | border_width_bottom = 2 11 | border_color = Color(0, 0, 0, 1) 12 | 13 | [node name="Control2" type="Control"] 14 | layout_mode = 3 15 | anchors_preset = 15 16 | anchor_right = 1.0 17 | anchor_bottom = 1.0 18 | grow_horizontal = 2 19 | grow_vertical = 2 20 | script = ExtResource("1") 21 | 22 | [node name="VBoxContainer" type="VBoxContainer" parent="."] 23 | layout_mode = 0 24 | anchor_right = 1.0 25 | anchor_bottom = 1.0 26 | 27 | [node name="CheckButton" type="CheckButton" parent="VBoxContainer"] 28 | layout_mode = 2 29 | theme_override_colors/font_disabled_color = Color(0, 0, 0, 1) 30 | theme_override_colors/font_hover_pressed_color = Color(0, 0, 0, 1) 31 | theme_override_colors/font_hover_color = Color(0, 0, 0, 1) 32 | theme_override_colors/font_color = Color(0, 0, 0, 1) 33 | theme_override_colors/font_focus_color = Color(0, 0, 0, 1) 34 | theme_override_colors/font_pressed_color = Color(0, 0, 0, 1) 35 | text = "Start Relatime Plotting" 36 | 37 | [node name="Label" type="Label" parent="VBoxContainer"] 38 | layout_mode = 2 39 | size_flags_horizontal = 8 40 | theme_override_colors/font_color = Color(0, 0, 0, 1) 41 | theme_override_styles/normal = SubResource("1") 42 | text = "Try to scale the window!" 43 | 44 | [connection signal="pressed" from="VBoxContainer/CheckButton" to="." method="_on_CheckButton_pressed"] 45 | -------------------------------------------------------------------------------- /addons/easy_charts/icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /addons/easy_charts/plugin.cfg: -------------------------------------------------------------------------------- 1 | [plugin] 2 | 3 | name="EasyCharts" 4 | description="" 5 | author="Nicolò \"fenix\" Santilio" 6 | version="14.08.2023" 7 | script="plugin.gd" 8 | -------------------------------------------------------------------------------- /addons/easy_charts/plugin.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends EditorPlugin 3 | 4 | func _enter_tree(): 5 | pass 6 | 7 | func _exit_tree(): 8 | pass 9 | -------------------------------------------------------------------------------- /addons/easy_charts/plugin.gd.uid: -------------------------------------------------------------------------------- 1 | uid://0iisdi8dlupw 2 | -------------------------------------------------------------------------------- /addons/easy_charts/templates.json: -------------------------------------------------------------------------------- 1 | { 2 | "default": 3 | { 4 | "function_colors" : ["#1e1e1e","#1e1e1e","#1e1e1e","#1e1e1e"], 5 | "v_lines_color" : "#cacaca", 6 | "h_lines_color" : "#cacaca", 7 | "outline_color" : "#1e1e1e", 8 | "font_color" : "#1e1e1e" 9 | }, 10 | "clean": 11 | { 12 | "function_colors" : ["#f7aa29","#f4394a","#5a6b7b","#8fbf59","#504538","#B7A99A","#00D795","#FFECCC","#FF8981"], 13 | "v_lines_color" : "#00000000", 14 | "h_lines_color" : "#3cffffff", 15 | "outline_color" : "#00000000", 16 | "font_color" : "#3cffffff" 17 | }, 18 | "gradient": 19 | { 20 | "function_colors" : ["#F7AA29","#B8A806","#79A117","#2C9433","#00854C","#006571","#2F4858","#2a364f","#27294a"], 21 | "v_lines_color" : "#64ffffff", 22 | "h_lines_color" : "#64ffffff", 23 | "outline_color" : "#64ffffff", 24 | "font_color" : "#64ffffff", 25 | }, 26 | "minimal": 27 | { 28 | "function_colors" : ["#1e1e1e","#1e1e1e","#1e1e1e","#1e1e1e"], 29 | "v_lines_color" : "#00000000", 30 | "h_lines_color" : "#00000000", 31 | "outline_color" : "#00000000", 32 | "font_color" : "#00000000" 33 | }, 34 | "invert": 35 | { 36 | "function_colors" : ["#ffffff","#ffffff","#ffffff","#ffffff"], 37 | "v_lines_color" : "#3b3b3b", 38 | "h_lines_color" : "#3b3b3b", 39 | "outline_color" : "#ffffff", 40 | "font_color" : "#ffffff" 41 | }, 42 | } 43 | 44 | 45 | -------------------------------------------------------------------------------- /addons/easy_charts/utilities/assets/OpenSans-VariableFont_wdth,wght.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fenix-hub/godot-engine.easy-charts/6cbff1d93d0e8fcc8d9cbb56f85ee641e52bed5e/addons/easy_charts/utilities/assets/OpenSans-VariableFont_wdth,wght.ttf -------------------------------------------------------------------------------- /addons/easy_charts/utilities/classes/plotting/bar.gd: -------------------------------------------------------------------------------- 1 | extends RefCounted 2 | class_name Bar 3 | 4 | #var rect: Rect2 5 | #var value: Pair 6 | # 7 | #func _init(rect: Rect2, value: Pair = Pair.new()) -> void: 8 | # self.value = value 9 | # self.rect = rect 10 | 11 | func _to_string() -> String: 12 | return "Value: %s\nRect: %s" % [self.value, self.rect] 13 | -------------------------------------------------------------------------------- /addons/easy_charts/utilities/classes/plotting/bar.gd.uid: -------------------------------------------------------------------------------- 1 | uid://b4nla6hrwhv7r 2 | -------------------------------------------------------------------------------- /addons/easy_charts/utilities/classes/plotting/chart_properties.gd: -------------------------------------------------------------------------------- 1 | extends RefCounted 2 | class_name ChartProperties 3 | 4 | var title: String 5 | var x_label: String 6 | var y_label: String 7 | 8 | ## {n}_scale defines in how many sectors the grid will be divided. 9 | ## This can only be used with non-discrete axes. 10 | var x_scale: float = 5 11 | var y_scale: float = 2 12 | 13 | var x_tick_size: float = 7 14 | var x_ticklabel_space: float = 5 15 | var y_tick_size: float = 7 16 | var y_ticklabel_space: float = 5 17 | 18 | ## Scale type, 0 = linear | 1 = logarithmic 19 | var x_scale_type: int = 0 20 | var y_scale_type: int = 0 21 | 22 | var draw_borders: bool = true 23 | var draw_frame: bool = true 24 | var draw_background: bool = true 25 | var draw_bounding_box: bool = true 26 | var draw_vertical_grid: bool = true 27 | var draw_horizontal_grid: bool = true 28 | var draw_ticks: bool = true 29 | var draw_origin: bool = false 30 | var draw_grid_box: bool = true 31 | var show_tick_labels: bool = true 32 | var show_x_label: bool = true 33 | var show_y_label: bool = true 34 | var show_title: bool = true 35 | 36 | ## If true will show the legend of your Chart on the right side of the frame. 37 | var show_legend: bool = false 38 | 39 | ## If true will make the Chart interactive, i.e. the DataTooltip will be displayed when 40 | ## hovering points with your mouse, and mouse_entered and mouse_exited signal will be emitted. 41 | var interactive: bool = false 42 | 43 | ## If true, will smooth the domain lower and upper bounds to the closest rounded value, 44 | ## instead of using precise values. 45 | var smooth_domain: bool = false 46 | 47 | ## If > 0, will limit the amount of points plotted in a Chart, discarding older values. 48 | ## [b]Note:[/b] this parameter will not make the Chart remove points from your Function objects, 49 | ## instead older points will be just ignored. This will make your Function object x and y arrays 50 | ## grow linearly, but won't interfere with your own data. 51 | ## If you instead prefer to improve performances by completely remove older data from your Function 52 | ## object, consider calling the Function.remove_point(0) method before adding a new point and plotting 53 | ## again. 54 | var max_samples: int = 100 55 | 56 | ## Dictionary of colors for all of the Chart elements. 57 | var colors: Dictionary = { 58 | frame = Color.WHITE_SMOKE, 59 | background = Color.WHITE, 60 | borders = Color.RED, 61 | bounding_box = Color.BLACK, 62 | grid = Color.GRAY, 63 | ticks = Color.BLACK, 64 | text = Color.BLACK, 65 | origin = Color.DIM_GRAY 66 | } 67 | 68 | var font: FontFile = load("res://addons/easy_charts/utilities/assets/OpenSans-VariableFont_wdth,wght.ttf") 69 | var font_size: int = 13 70 | 71 | func _init() -> void: 72 | ThemeDB.set_fallback_font(font) 73 | ThemeDB.set_fallback_font_size(font_size) 74 | 75 | func get_string_size(text: String) -> Vector2: 76 | return font.get_string_size(text) 77 | -------------------------------------------------------------------------------- /addons/easy_charts/utilities/classes/plotting/chart_properties.gd.uid: -------------------------------------------------------------------------------- 1 | uid://cqbk7nq5ilrp4 2 | -------------------------------------------------------------------------------- /addons/easy_charts/utilities/classes/plotting/function.gd: -------------------------------------------------------------------------------- 1 | extends RefCounted 2 | class_name Function 3 | 4 | enum Type { 5 | SCATTER, 6 | LINE, 7 | AREA, 8 | PIE, 9 | BAR 10 | } 11 | 12 | enum Interpolation { 13 | NONE, 14 | LINEAR, 15 | STAIR, 16 | SPLINE 17 | } 18 | 19 | # TODO: add new markers, like an empty circle, an empty box, etc. 20 | enum Marker { 21 | NONE, 22 | CIRCLE, 23 | TRIANGLE, 24 | SQUARE, 25 | CROSS 26 | } 27 | 28 | var __x: Array 29 | var __y: Array 30 | var name: String 31 | var props: Dictionary = {} 32 | 33 | func _init(x: Array, y: Array, name: String = "", props: Dictionary = {}) -> void: 34 | self.__x = x.duplicate() 35 | self.__y = y.duplicate() 36 | self.name = name 37 | if not props.is_empty() and props != null: 38 | self.props = props 39 | 40 | func get_point(index: int) -> Array: 41 | return [self.__x[index], self.__y[index]] 42 | 43 | func add_point(x: float, y: float) -> void: 44 | self.__x.append(x) 45 | self.__y.append(y) 46 | 47 | func set_point(index: int, x: float, y: float) -> void: 48 | self.__x[index] = x 49 | self.__y[index] = y 50 | 51 | func remove_point(index: int) -> void: 52 | self.__x.remove_at(index) 53 | self.__y.remove_at(index) 54 | 55 | func pop_back_point() -> void: 56 | self.__x.pop_back() 57 | self.__y.pop_back() 58 | 59 | func pop_front_point() -> void: 60 | self.__x.pop_front() 61 | self.__y.pop_front() 62 | 63 | func count_points() -> int: 64 | return self.__x.size() 65 | 66 | func get_color() -> Color: 67 | return props.get("color", Color.DARK_SLATE_GRAY) 68 | 69 | func get_gradient() -> Gradient: 70 | return props.get("gradient", Gradient.new()) 71 | 72 | func get_marker() -> int: 73 | return props.get("marker", Marker.NONE) 74 | 75 | func get_type() -> int: 76 | return props.get("type", Type.SCATTER) 77 | 78 | func get_interpolation() -> int: 79 | return props.get("interpolation", Interpolation.LINEAR) 80 | 81 | func get_line_width() -> float: 82 | return props.get("line_width", 2.0) 83 | 84 | func get_visibility() -> bool: 85 | return props.get("visible", true) 86 | 87 | func copy() -> Function: 88 | return Function.new( 89 | self.__x.duplicate(), 90 | self.__y.duplicate(), 91 | self.name, 92 | self.props.duplicate(true) 93 | ) 94 | -------------------------------------------------------------------------------- /addons/easy_charts/utilities/classes/plotting/function.gd.uid: -------------------------------------------------------------------------------- 1 | uid://4ekebvnyr8g1 2 | -------------------------------------------------------------------------------- /addons/easy_charts/utilities/classes/plotting/point.gd: -------------------------------------------------------------------------------- 1 | extends RefCounted 2 | class_name Point 3 | 4 | var position: Vector2 5 | var value: Dictionary 6 | 7 | func _init(position: Vector2, value: Dictionary) -> void: 8 | self.position = position 9 | self.value = value 10 | 11 | func _to_string() -> String: 12 | return "Value: %s\nPosition: %s" % [self.value, self.position] 13 | -------------------------------------------------------------------------------- /addons/easy_charts/utilities/classes/plotting/point.gd.uid: -------------------------------------------------------------------------------- 1 | uid://cpnbl1h6tkmbd 2 | -------------------------------------------------------------------------------- /addons/easy_charts/utilities/classes/structures/array_operations.gd: -------------------------------------------------------------------------------- 1 | extends RefCounted 2 | class_name ArrayOperations 3 | 4 | static func add_int(array: Array, _int: int) -> Array: 5 | var t: Array = array.duplicate(true) 6 | for ti in t.size(): 7 | t[ti] = int(t[ti] + _int) 8 | return t 9 | 10 | static func add_float(array: Array, _float: float) -> Array: 11 | var t: Array = array.duplicate(true) 12 | for ti in t.size(): 13 | t[ti] = float(t[ti] + _float) 14 | return t 15 | 16 | static func multiply_int(array: Array, _int: int) -> Array: 17 | var t: Array = array.duplicate(true) 18 | for ti in t.size(): 19 | t[ti] = int(t[ti] * _int) 20 | return t 21 | 22 | static func multiply_float(array: Array, _float: float) -> PackedFloat32Array: 23 | var t: PackedFloat32Array = array.duplicate(true) 24 | for ti in t.size(): 25 | t[ti] = float(t[ti] * _float) 26 | return t 27 | 28 | static func pow(array: Array, _int: int) -> Array: 29 | var t: Array = array.duplicate(true) 30 | for ti in t.size(): 31 | t[ti] = float(pow(t[ti], _int)) 32 | return t 33 | 34 | static func cos(array: Array) -> Array: 35 | var t: Array = array.duplicate(true) 36 | for val in array.size(): 37 | t[val] = cos(t[val]) 38 | return t 39 | 40 | static func sin(array: Array) -> Array: 41 | var t: Array = array.duplicate(true) 42 | for val in array.size(): 43 | t[val] = sin(t[val]) 44 | return t 45 | 46 | static func affix(array: Array, _string: String) -> Array: 47 | var t: Array = array.duplicate(true) 48 | for val in array.size(): 49 | t[val] = str(t[val]) + _string 50 | return t 51 | 52 | static func suffix(array: Array, _string: String) -> Array: 53 | var t: Array = array.duplicate(true) 54 | for val in array.size(): 55 | t[val] = _string + str(t[val]) 56 | return t 57 | -------------------------------------------------------------------------------- /addons/easy_charts/utilities/classes/structures/array_operations.gd.uid: -------------------------------------------------------------------------------- 1 | uid://cdmhpsxlcakle 2 | -------------------------------------------------------------------------------- /addons/easy_charts/utilities/classes/structures/data_frame.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends Resource 3 | class_name DataFrame 4 | 5 | var table_name : String = "" 6 | var labels : PackedStringArray = [] 7 | var headers : PackedStringArray = [] 8 | var datamatrix : Matrix = null 9 | var dataset : Array = [] 10 | 11 | func _init(datamatrix : Matrix, headers : PackedStringArray = [], labels : PackedStringArray = [] , table_name : String = "") -> void: 12 | if datamatrix.is_empty(): datamatrix.resize(labels.size(), headers.size()) 13 | if labels.is_empty() : for label in range(datamatrix.get_size().x) : labels.append(label as String) 14 | if headers.is_empty() : for header in range(datamatrix.get_size().y) : headers.append(MatrixGenerator.get_letter_index(header)) 15 | build_dataframe(datamatrix, headers, labels, table_name) 16 | 17 | func build_dataframe(datamatrix : Matrix, headers : PackedStringArray = [], labels : PackedStringArray = [] , table_name : String = "") -> void: 18 | self.datamatrix = datamatrix 19 | self.headers = headers 20 | self.labels = labels 21 | self.table_name = table_name 22 | self.dataset = build_dataset_from_matrix(datamatrix, headers, labels) 23 | 24 | func build_dataset_from_matrix(datamatrix : Matrix, headers : PackedStringArray, labels : PackedStringArray) -> Array: 25 | var data : Array = datamatrix.to_array() 26 | return build_dataset(data, headers, labels) 27 | 28 | func build_dataset(data : Array, headers : PackedStringArray, labels : PackedStringArray) -> Array: 29 | var dataset : Array = [Array([" "]) + Array(headers)] 30 | for row_i in range(labels.size()): dataset.append(([labels[row_i]] + data[row_i]) if not data.is_empty() else [labels[row_i]]) 31 | return dataset 32 | 33 | func insert_column(column : Array, header : String = "", index : int = dataset[0].size() - 1) -> void: 34 | assert(column.size() == (datamatrix.rows() if not datamatrix.is_empty() else labels.size())) #,"error: the column size must match the dataset column size") 35 | headers.insert(index, header if header != "" else MatrixGenerator.get_letter_index(index)) 36 | datamatrix.insert_column(column, index) 37 | dataset = build_dataset_from_matrix(datamatrix, headers, labels) 38 | 39 | func insert_row(row : Array, label : String = "", index : int = dataset.size() - 1) -> PackedStringArray: 40 | assert(row.size() == (datamatrix.columns() if not datamatrix.is_empty() else headers.size())) #,"error: the row size must match the dataset row size") 41 | labels.insert(index, label if label != "" else str(index)) 42 | datamatrix.insert_row(row, index) 43 | dataset = build_dataset_from_matrix(datamatrix, headers, labels) 44 | return PackedStringArray([label] + row) 45 | 46 | func get_datamatrix() -> Matrix: 47 | return datamatrix 48 | 49 | func get_dataset() -> Array: 50 | return dataset 51 | 52 | func get_labels() -> PackedStringArray: 53 | return labels 54 | 55 | func transpose(): 56 | build_dataframe(MatrixGenerator.transpose(datamatrix), labels, headers, table_name) 57 | 58 | func _to_string() -> String: 59 | var last_string_len : int 60 | for row in dataset: 61 | for column in row: 62 | var string_len : int = str(column).length() 63 | last_string_len = string_len if string_len > last_string_len else last_string_len 64 | var string : String = "" 65 | for row_i in dataset.size(): 66 | for column_i in dataset[row_i].size(): 67 | string+="%*s" % [last_string_len+1, dataset[row_i][column_i]] 68 | string+="\n" 69 | string+="\n['{table_name}' : {rows} rows x {columns} columns]\n".format({ 70 | rows = datamatrix.rows(), 71 | columns = datamatrix.columns(), 72 | table_name = table_name}) 73 | return string 74 | 75 | # ............................................................................... 76 | 77 | # Return a list of headers corresponding to a list of indexes 78 | func get_headers_names(indexes : PackedInt32Array) -> PackedStringArray: 79 | var headers : PackedStringArray = [] 80 | for index in indexes: 81 | headers.append(dataset[0][index]) 82 | return headers 83 | 84 | # Returns the index of an header 85 | func get_column_index(header : String) -> int: 86 | for headers_ix in range(dataset[0].size()): 87 | if dataset[0][headers_ix] == header: 88 | return headers_ix 89 | return -1 90 | 91 | # Get a column by its header 92 | func get_column(header : String) -> Array: 93 | var headers_i : int = get_column_index(header) 94 | if headers_i!=-1: 95 | return datamatrix.get_column(headers_i) 96 | else: 97 | return [] 98 | 99 | # Get a list of columns by their headers 100 | func columns(headers : PackedStringArray) -> Matrix: 101 | var values : Array = [] 102 | for header in headers: 103 | values.append(get_column(header)) 104 | return MatrixGenerator.transpose(Matrix.new(values)) 105 | 106 | 107 | # Get a column by its index 108 | func get_icolumn(index : int) -> Array: 109 | return datamatrix.get_column(index) 110 | 111 | # Get a list of columns by their indexes 112 | func get_icolumns(indexes : PackedInt32Array) -> Array: 113 | var values : Array = [] 114 | for index in indexes: 115 | values.append(datamatrix.get_column(index)) 116 | return values 117 | 118 | # Returns the list of labels corresponding to the list of indexes 119 | func get_labels_names(indexes : PackedInt32Array) -> PackedStringArray: 120 | var headers : PackedStringArray = [] 121 | for index in indexes: 122 | headers.append(dataset[index][0]) 123 | return headers 124 | 125 | # Returns the index of a label 126 | func get_row_index(label : String) -> int: 127 | for row in dataset.size(): 128 | if dataset[row][0] == label: 129 | return row 130 | return -1 131 | 132 | # Get a row by its label 133 | func get_row(label : String) -> Array: 134 | var index : int = get_row_index(label) 135 | if index == -1 : 136 | return [] 137 | else: 138 | return datamatrix.get_row(index) 139 | 140 | # Get a list of rows by their labels 141 | func rows(labels : Array) -> Matrix: 142 | var values : Array = [] 143 | for label in labels: 144 | values.append(get_row(label)) 145 | return Matrix.new(values) 146 | 147 | # Get a row by its index 148 | func get_irow(index : int) -> Array: 149 | return datamatrix.get_row(index) 150 | 151 | # Get a list of rows by their indexes 152 | func get_irows(indexes : PackedInt32Array) -> Array: 153 | var values : Array = [] 154 | for index in indexes: 155 | values.append(datamatrix.get_row(index)) 156 | return values 157 | 158 | # Returns a a group of rows or a group of columns, using indexes or names 159 | # dataset["0;5"] ---> Returns an array containing all rows from the 1st to the 4th 160 | # dataset["0:5"] ---> Returns an array containing all columns from the 1st to the 4th 161 | # dataset["label0;label5"] ---> Returns an array containing all row from the one with label == "label0" to the one with label == "label5" 162 | # dataset["header0:header0"] ---> Returns an array containing all columns from the one with label == "label0" to the one with label == "label5" 163 | func _get(_property : StringName): 164 | # ":" --> Columns 165 | if ":" in _property: 166 | var property : PackedStringArray = _property.split(":") 167 | if (property[0]).is_valid_int(): 168 | if property[1] == "*": 169 | return get_icolumns(range(property[0] as int, headers.size()-1)) 170 | else: 171 | return get_icolumns(range(property[0] as int, property[1] as int +1)) 172 | else: 173 | if property[1] == "*": 174 | return get_icolumns(range(get_column_index(property[0]), headers.size()-1)) 175 | else: 176 | return get_icolumns(range(get_column_index(property[0]), get_column_index(property[1]))) 177 | # ";" --> Rows 178 | elif ";" in _property: 179 | var property : PackedStringArray = _property.split(";") 180 | if (property[0]).is_valid_int(): 181 | return get_irows(range(property[0] as int, property[1] as int + 1 )) 182 | else: 183 | return get_irows(range(get_row_index(property[0]), get_row_index(property[1]))) 184 | elif "," in _property: 185 | var property : PackedStringArray = _property.split(",") 186 | else: 187 | if (_property as String).is_valid_int(): 188 | return get_icolumn(int(_property)) 189 | else: 190 | return get_column(_property) 191 | -------------------------------------------------------------------------------- /addons/easy_charts/utilities/classes/structures/data_frame.gd.uid: -------------------------------------------------------------------------------- 1 | uid://bbr722edegy4c 2 | -------------------------------------------------------------------------------- /addons/easy_charts/utilities/classes/structures/matrix.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends Resource 3 | class_name Matrix 4 | 5 | var values : Array = [] 6 | 7 | func _init(matrix : Array = [], size : int = 0) -> void: 8 | values = matrix 9 | 10 | func insert_row(row : Array, index : int = values.size()) -> void: 11 | if rows() != 0: 12 | assert(row.size() == columns()) #,"the row size must match matrix row size") 13 | values.insert(index, row) 14 | 15 | func update_row(row : Array, index : int) -> void: 16 | assert(rows() > index) #,"the row size must match matrix row size") 17 | values[index] = row 18 | 19 | func remove_row(index: int) -> void: 20 | assert(rows() > index) #,"the row size must match matrix row size") 21 | values.remove_at(index) 22 | 23 | func insert_column(column : Array, index : int = values[0].size()) -> void: 24 | if columns() != 0: 25 | assert(column.size() == rows()) #,"the column size must match matrix column size") 26 | for row_idx in column.size(): 27 | values[row_idx].insert(index, column[row_idx]) 28 | 29 | func update_column(column : Array, index : int) -> void: 30 | assert(columns() > index) #,"the column size must match matrix column size") 31 | for row_idx in column.size(): 32 | values[row_idx][index] = column[row_idx] 33 | 34 | func remove_column(index: int) -> void: 35 | assert(columns() > index) #,"the column index must be at least equals to the rows count") 36 | for row in get_rows(): 37 | row.remove(index) 38 | 39 | func resize(rows: int, columns: int) -> void: 40 | for row in range(rows): 41 | var row_column: Array = [] 42 | row_column.resize(columns) 43 | values.append(row_column) 44 | 45 | func to_array() -> Array: 46 | return values.duplicate(true) 47 | 48 | func get_size() -> Vector2: 49 | return Vector2(rows(), columns()) 50 | 51 | func rows() -> int: 52 | return values.size() 53 | 54 | func columns() -> int: 55 | return values[0].size() if rows() != 0 else 0 56 | 57 | func value(row: int, column: int) -> float: 58 | return values[row][column] 59 | 60 | func set_value(value: float, row: int, column: int) -> void: 61 | values[row][column] = value 62 | 63 | func get_column(column : int) -> Array: 64 | assert(column < columns()) #,"index of the column requested (%s) exceedes matrix columns (%s)"%[column, columns()]) 65 | var column_array : Array = [] 66 | for row in values: 67 | column_array.append(row[column]) 68 | return column_array 69 | 70 | func get_columns(from : int = 0, to : int = columns()-1) -> Array: 71 | var values : Array = [] 72 | for column in range(from, to): 73 | values.append(get_column(column)) 74 | return values 75 | # return MatrixGenerator.from_array(values) 76 | 77 | func get_row(row : int) -> Array: 78 | assert(row < rows()) #,"index of the row requested (%s) exceedes matrix rows (%s)"%[row, rows()]) 79 | return values[row] 80 | 81 | func get_rows(from : int = 0, to : int = rows()-1) -> Array: 82 | return values.slice(from, to) 83 | # return MatrixGenerator.from_array(values) 84 | 85 | func is_empty() -> bool: 86 | return rows() == 0 and columns() == 0 87 | 88 | 89 | func is_square() -> bool: 90 | return columns() == rows() 91 | 92 | 93 | func is_diagonal() -> bool: 94 | if not is_square(): 95 | return false 96 | 97 | for i in rows(): 98 | for j in columns(): 99 | if i != j and values[i][j] != 0: 100 | return false 101 | 102 | return true 103 | 104 | 105 | func is_upper_triangular() -> bool: 106 | if not is_square(): 107 | return false 108 | 109 | for i in rows(): 110 | for j in columns(): 111 | if i > j and values[i][j] != 0: 112 | return false 113 | 114 | return true 115 | 116 | 117 | func is_lower_triangular() -> bool: 118 | if not is_square(): 119 | return false 120 | 121 | for i in rows(): 122 | for j in columns(): 123 | if i < j and values[i][j] != 0: 124 | return false 125 | 126 | return true 127 | 128 | 129 | func is_triangular() -> bool: 130 | return is_upper_triangular() or is_lower_triangular() 131 | 132 | 133 | func is_identity() -> bool: 134 | if not is_diagonal(): 135 | return false 136 | 137 | for i in rows(): 138 | if values[i][i] != 1: 139 | return false 140 | 141 | return true 142 | 143 | func _to_string() -> String: 144 | var last_string_len : int 145 | for row in values: 146 | for column in row: 147 | var string_len : int = str(column).length() 148 | last_string_len = string_len if string_len > last_string_len else last_string_len 149 | var string : String = "\n" 150 | for row_i in values.size(): 151 | for column_i in values[row_i].size(): 152 | string+="%*s" % [last_string_len+1 if column_i!=0 else last_string_len, values[row_i][column_i]] 153 | string+="\n" 154 | return string 155 | 156 | # ---- 157 | func set(position: StringName, value: Variant) -> void: 158 | var t_pos: Array = position.split(",") 159 | values[t_pos[0]][t_pos[1]] = value 160 | 161 | # -------------- 162 | func _get(_property : StringName): 163 | # ":" --> Columns 164 | if ":" in _property: 165 | var property : PackedStringArray = _property.split(":") 166 | var from : PackedStringArray = property[0].split(",") 167 | var to : PackedStringArray = property[1].split(",") 168 | elif "," in _property: 169 | var property : PackedStringArray = _property.split(",") 170 | if property.size() == 2: 171 | return get_row(property[0] as int)[property[1] as int] 172 | else: 173 | if (_property as String).is_valid_int(): 174 | return get_row(int(_property)) 175 | -------------------------------------------------------------------------------- /addons/easy_charts/utilities/classes/structures/matrix.gd.uid: -------------------------------------------------------------------------------- 1 | uid://divbwqjaxgmeh 2 | -------------------------------------------------------------------------------- /addons/easy_charts/utilities/classes/structures/matrix_generator.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends RefCounted 3 | class_name MatrixGenerator 4 | 5 | static func zeros(rows: int, columns: int) -> Matrix: 6 | var zeros: Array = [] 7 | var t_rows: Array = [] 8 | t_rows.resize(columns) 9 | t_rows.fill(0.0) 10 | for row in rows: 11 | zeros.append(t_rows.duplicate()) 12 | return Matrix.new(zeros) 13 | 14 | # Generates a Matrix with random values between [from; to] with a given @size (rows, columns) 15 | static func random_float_range(from : float, to : float, size : Vector2, _seed : int = 1234) -> Matrix: 16 | seed(_seed) 17 | randomize() 18 | var array : Array = [] 19 | for row in range(size.x): 20 | var matrix_row : Array = [] 21 | for column in range(size.y): matrix_row.append(randf_range(from,to)) 22 | array.append(matrix_row) 23 | return Matrix.new(array) 24 | 25 | # Generates a Matrix giving an Array (Array must by Array[Array]) 26 | static func from_array(array : Array = []) -> Matrix: 27 | var matrix : Array = [] 28 | matrix.append(array) 29 | return Matrix.new(matrix) 30 | 31 | # Generates a sub-Matrix giving a Matrix, a @from Array [row_i, column_i] and a @to Array [row_j, column_j] 32 | static func sub_matrix(_matrix : Matrix, from : PackedInt32Array, to : PackedInt32Array) -> Matrix: 33 | assert( not (to[0] > _matrix.rows() or to[1] > _matrix.columns()), 34 | "%s is not an acceptable size for the submatrix, giving a matrix of size %s"%[to, _matrix.get_size()]) 35 | var array : Array = [] 36 | var rows : Array = _matrix.get_rows(from[0], to[0]) 37 | for row in rows: 38 | array.append(row.slice(from[1], to[1])) 39 | return Matrix.new(array) 40 | 41 | # Duplicates a given Matrix 42 | static func duplicate(_matrix : Matrix) -> Matrix: 43 | return Matrix.new(_matrix.to_array().duplicate()) 44 | 45 | # Calculate the determinant of a matrix 46 | static func determinant(matrix: Matrix) -> float: 47 | assert(matrix.is_square()) #,"Expected square matrix") 48 | 49 | var determinant: float = 0.0 50 | 51 | if matrix.rows() == 2 : 52 | determinant = (matrix.value(0, 0) * matrix.value(1, 1)) - (matrix.value(0, 1) * matrix.value(1, 0)) 53 | elif matrix.is_diagonal() or matrix.is_triangular() : 54 | for i in matrix.rows(): 55 | determinant *= matrix.value(i, i) 56 | elif matrix.is_identity() : 57 | determinant = 1.0 58 | else: 59 | # Laplace expansion 60 | var multiplier: float = -1.0 61 | var submatrix: Matrix = sub_matrix(matrix, [1, 0], [matrix.rows(), matrix.columns()]) 62 | for j in matrix.columns() : 63 | var cofactor: Matrix = copy(submatrix) 64 | cofactor.remove_column(j) 65 | multiplier *= -1.0 66 | determinant += multiplier * matrix.value(0, j) * determinant(cofactor) 67 | 68 | return determinant 69 | 70 | 71 | # Calculate the inverse of a Matrix 72 | static func inverse(matrix: Matrix) -> Matrix: 73 | var inverse: Matrix 74 | 75 | # Minors and Cofactors 76 | var minors_cofactors: Matrix = zeros(matrix.rows(), matrix.columns()) 77 | var multiplier: float = -1.0 78 | 79 | for i in minors_cofactors.rows(): 80 | for j in minors_cofactors.columns(): 81 | var t_minor: Matrix = copy(matrix) 82 | t_minor.remove_row(i) 83 | t_minor.remove_column(j) 84 | multiplier *= -1.0 85 | minors_cofactors.set_value(multiplier * determinant(t_minor), i, j) 86 | 87 | var transpose: Matrix = transpose(minors_cofactors) 88 | var determinant: float = determinant(matrix) 89 | 90 | inverse = multiply_float(transpose, 1 / determinant) 91 | 92 | return inverse 93 | 94 | # Transpose a given Matrix 95 | static func transpose(_matrix : Matrix) -> Matrix: 96 | var array : Array = [] 97 | array.resize(_matrix.get_size().y) 98 | var row : Array = [] 99 | row.resize(_matrix.get_size().x) 100 | for x in array.size(): 101 | array[x] = row.duplicate() 102 | for i in range(_matrix.get_size().x): 103 | for j in range(_matrix.get_size().y): 104 | array[j][i] = (_matrix.to_array()[i][j]) 105 | return Matrix.new(array) 106 | 107 | # Calculates the dot product (A*B) matrix between two Matrixes 108 | static func dot(_matrix1 : Matrix, _matrix2 : Matrix) -> Matrix: 109 | if _matrix1.get_size().y != _matrix2.get_size().x: 110 | printerr("matrix1 number of columns: %s must be the same as matrix2 number of rows: %s"%[_matrix1.get_size().y, _matrix2.get_size().x]) 111 | return Matrix.new() 112 | var array : Array = [] 113 | for x in range(_matrix1.get_size().x): 114 | var row : Array = [] 115 | for y in range(_matrix2.get_size().y): 116 | var sum : float 117 | for k in range(_matrix1.get_size().y): 118 | sum += (_matrix1.to_array()[x][k]*_matrix2.to_array()[k][y]) 119 | row.append(sum) 120 | array.append(row) 121 | return Matrix.new(array) 122 | 123 | # Calculates the hadamard (element-wise product) between two Matrixes 124 | static func hadamard(_matrix1 : Matrix, _matrix2 : Matrix) -> Matrix: 125 | if _matrix1.get_size() != _matrix2.get_size(): 126 | printerr("matrix1 size: %s must be the same as matrix2 size: %s"%[_matrix1.get_size(), _matrix2.get_size()]) 127 | return Matrix.new() 128 | var array : Array = [] 129 | for x in range(_matrix1.to_array().size()): 130 | var row : Array = [] 131 | for y in range(_matrix1.to_array()[x].size()): 132 | assert(typeof(_matrix1.to_array()[x][y]) != TYPE_STRING and typeof(_matrix2.to_array()[x][y]) != TYPE_STRING) #,"can't apply operations over a Matrix of Strings") 133 | row.append(_matrix1.to_array()[x][y] * _matrix2.to_array()[x][y]) 134 | array.append(row) 135 | return Matrix.new(array) 136 | 137 | # Multiply a given Matrix for an int value 138 | static func multiply_int(_matrix1 : Matrix, _int : int) -> Matrix: 139 | var array : Array = _matrix1.to_array().duplicate() 140 | for x in range(_matrix1.to_array().size()): 141 | for y in range(_matrix1.to_array()[x].size()): 142 | array[x][y]*=_int 143 | array[x][y] = int(array[x][y]) 144 | return Matrix.new(array) 145 | 146 | # Multiply a given Matrix for a float value 147 | static func multiply_float(_matrix1 : Matrix, _float : float) -> Matrix: 148 | var array : Array = _matrix1.to_array().duplicate() 149 | for x in range(_matrix1.to_array().size()): 150 | for y in range(_matrix1.to_array()[x].size()): 151 | array[x][y]*=_float 152 | return Matrix.new(array) 153 | 154 | 155 | static func copy(matrix: Matrix) -> Matrix: 156 | return Matrix.new(matrix.values.duplicate(true)) 157 | 158 | # ------------------------------------------------------------ 159 | static func get_letter_index(index : int) -> String: 160 | return "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z".split(" ")[index] 161 | -------------------------------------------------------------------------------- /addons/easy_charts/utilities/classes/structures/matrix_generator.gd.uid: -------------------------------------------------------------------------------- 1 | uid://814r3hcgvci3 2 | -------------------------------------------------------------------------------- /addons/easy_charts/utilities/containers/canvas/canvas.gd: -------------------------------------------------------------------------------- 1 | extends Control 2 | class_name Canvas 3 | 4 | @onready var _title_lbl: Label = $CanvasContainer/Title 5 | @onready var _x_lbl: Label = $CanvasContainer/DataContainer/PlotContainer/XLabel 6 | @onready var _y_lbl: Label = $CanvasContainer/DataContainer/YLabel 7 | @onready var _legend: FunctionLegend = $CanvasContainer/DataContainer/FunctionLegend 8 | 9 | func _ready(): 10 | pass # Replace with function body. 11 | 12 | func prepare_canvas(chart_properties: ChartProperties) -> void: 13 | 14 | if chart_properties.draw_frame: 15 | set_color(chart_properties.colors.frame) 16 | set_frame_visible(true) 17 | else: 18 | set_frame_visible(false) 19 | 20 | if chart_properties.show_title: 21 | update_title(chart_properties.title, chart_properties.colors.text) 22 | else: 23 | _title_lbl.hide() 24 | 25 | if chart_properties.show_x_label: 26 | update_x_label(chart_properties.x_label, chart_properties.colors.text) 27 | else: 28 | _x_lbl.hide() 29 | 30 | if chart_properties.show_y_label: 31 | update_y_label(chart_properties.y_label, chart_properties.colors.text, -90) 32 | else: 33 | _y_lbl.hide() 34 | 35 | if chart_properties.show_legend: 36 | _legend.show() 37 | else: 38 | hide_legend() 39 | 40 | func update_title(text: String, color: Color, rotation: float = 0.0) -> void: 41 | _title_lbl.show() 42 | _update_canvas_label(_title_lbl, text, color, rotation) 43 | 44 | func update_y_label(text: String, color: Color, rotation: float = 0.0) -> void: 45 | _y_lbl.show() 46 | _update_canvas_label(_y_lbl, text, color, rotation) 47 | 48 | func update_x_label(text: String, color: Color, rotation: float = 0.0) -> void: 49 | _x_lbl.show() 50 | _update_canvas_label(_x_lbl, text, color, rotation) 51 | 52 | func _update_canvas_label(canvas_label: Label, text: String, color: Color, rotation: float = 0.0) -> void: 53 | canvas_label.set_text(text) 54 | canvas_label.modulate = color 55 | canvas_label.rotation = rotation 56 | 57 | func hide_legend() -> void: 58 | _legend.hide() 59 | 60 | func set_color(color: Color) -> void: 61 | get("theme_override_styles/panel").set("bg_color", color) 62 | 63 | func set_frame_visible(visible: bool) -> void: 64 | get("theme_override_styles/panel").set("draw_center", visible) 65 | -------------------------------------------------------------------------------- /addons/easy_charts/utilities/containers/canvas/canvas.gd.uid: -------------------------------------------------------------------------------- 1 | uid://d3eig7fx6wtli 2 | -------------------------------------------------------------------------------- /addons/easy_charts/utilities/containers/canvas/plot_box/grid_box.gd: -------------------------------------------------------------------------------- 1 | extends Control 2 | class_name GridBox 3 | 4 | var x_domain: ChartAxisDomain = null 5 | var x_labels_function: Callable = Callable() 6 | 7 | var y_domain: ChartAxisDomain = null 8 | var y_labels_function: Callable = Callable() 9 | 10 | var box: Rect2 11 | var plot_box: Rect2 12 | 13 | # Called when the node enters the scene tree for the first time. 14 | func _ready(): 15 | pass # Replace with function body. 16 | 17 | func set_domains(x_domain: ChartAxisDomain, y_domain: ChartAxisDomain) -> void: 18 | self.x_domain = x_domain 19 | self.y_domain = y_domain 20 | 21 | func set_labels_functions(x_labels_function: Callable, y_labels_function: Callable) -> void: 22 | self.x_labels_function = x_labels_function 23 | self.y_labels_function = y_labels_function 24 | 25 | func _draw() -> void: 26 | if get_parent().chart_properties == null: 27 | printerr("Cannot draw GridBox without ChartProperties!") 28 | return 29 | 30 | self.box = get_parent().get_box() 31 | self.plot_box = get_parent().get_plot_box() 32 | 33 | if get_parent().chart_properties.draw_background: 34 | _draw_background() 35 | 36 | if get_parent().chart_properties.draw_grid_box: 37 | _draw_x_ticks() 38 | _draw_y_ticks() 39 | 40 | if get_parent().chart_properties.draw_origin: 41 | _draw_origin() 42 | 43 | if get_parent().chart_properties.draw_bounding_box: 44 | _draw_bounding_box() 45 | 46 | func _draw_background() -> void: 47 | draw_rect(self.box, get_parent().chart_properties.colors.background, true)# false) TODOGODOT4 Antialiasing argument is missing 48 | 49 | func _draw_bounding_box() -> void: 50 | var box: Rect2 = self.box 51 | box.position.y += 1 52 | draw_rect(box, get_parent().chart_properties.colors.bounding_box, false, 1)# true) TODOGODOT4 Antialiasing argument is missing 53 | 54 | func _draw_origin() -> void: 55 | var xorigin: float = ECUtilities._map_domain(0.0, x_domain, ChartAxisDomain.from_bounds(self.plot_box.position.x, self.plot_box.end.x)) 56 | var yorigin: float = ECUtilities._map_domain(0.0, y_domain, ChartAxisDomain.from_bounds(self.plot_box.end.y, self.plot_box.position.y)) 57 | 58 | draw_line(Vector2(xorigin, self.plot_box.position.y), Vector2(xorigin, self.plot_box.position.y + self.plot_box.size.y), get_parent().chart_properties.colors.origin, 1) 59 | draw_line(Vector2(self.plot_box.position.x, yorigin), Vector2(self.plot_box.position.x + self.plot_box.size.x, yorigin), get_parent().chart_properties.colors.origin, 1) 60 | draw_string( 61 | get_parent().chart_properties.font, Vector2(xorigin, yorigin) - Vector2(15, -15), "O", HORIZONTAL_ALIGNMENT_CENTER, -1, 62 | ThemeDB.fallback_font_size, get_parent().chart_properties.colors.text, TextServer.JUSTIFICATION_NONE, TextServer.DIRECTION_AUTO, TextServer.ORIENTATION_HORIZONTAL 63 | ) 64 | 65 | 66 | func _draw_x_ticks() -> void: 67 | var labels = x_domain.get_tick_labels() 68 | var tick_count = labels.size() 69 | 70 | var x_pixel_dist: float = self.plot_box.size.x / tick_count 71 | 72 | var vertical_grid: PackedVector2Array = [] 73 | var vertical_ticks: PackedVector2Array = [] 74 | 75 | for i in range(tick_count): 76 | var x_position: float = (i * x_pixel_dist) + self.plot_box.position.x 77 | 78 | var top: Vector2 = Vector2(x_position, self.box.position.y) 79 | var bottom: Vector2 = Vector2(x_position, self.box.end.y) 80 | 81 | vertical_grid.append(top) 82 | vertical_grid.append(bottom) 83 | 84 | vertical_ticks.append(bottom) 85 | vertical_ticks.append(bottom + Vector2(0, get_parent().chart_properties.x_tick_size)) 86 | 87 | # Draw x tick labels 88 | if get_parent().chart_properties.show_tick_labels: 89 | var label: String = labels[i] 90 | draw_string( 91 | get_parent().chart_properties.font, 92 | _get_x_tick_label_position(bottom, label), 93 | label, 94 | HORIZONTAL_ALIGNMENT_CENTER, 95 | -1, 96 | ThemeDB.fallback_font_size, 97 | get_parent().chart_properties.colors.text, 98 | TextServer.JUSTIFICATION_NONE, 99 | TextServer.DIRECTION_AUTO, 100 | TextServer.ORIENTATION_HORIZONTAL 101 | ) 102 | 103 | # Draw x grid 104 | if get_parent().chart_properties.draw_vertical_grid: 105 | draw_multiline(vertical_grid, get_parent().chart_properties.colors.grid, 1) 106 | 107 | # Draw x ticks 108 | if get_parent().chart_properties.draw_ticks: 109 | draw_multiline(vertical_ticks, get_parent().chart_properties.colors.ticks, 1) 110 | 111 | func _draw_y_ticks() -> void: 112 | var labels = y_domain.get_tick_labels() 113 | var tick_count = labels.size() 114 | var y_pixel_dist: float = self.plot_box.size.y / tick_count 115 | 116 | var horizontal_grid: PackedVector2Array = [] 117 | var horizontal_ticks: PackedVector2Array = [] 118 | 119 | for i in range(tick_count): 120 | var y_sampled_val: float = self.plot_box.size.y - (i * y_pixel_dist) + self.plot_box.position.y 121 | 122 | var left: Vector2 = Vector2(self.box.position.x, y_sampled_val) 123 | var right: Vector2 = Vector2(self.box.end.x, y_sampled_val) 124 | 125 | horizontal_grid.append(left) 126 | horizontal_grid.append(right) 127 | 128 | horizontal_ticks.append(left) 129 | horizontal_ticks.append(left - Vector2(get_parent().chart_properties.y_tick_size, 0)) 130 | 131 | # Draw y tick labels 132 | if get_parent().chart_properties.show_tick_labels: 133 | var label: String = labels[i] 134 | draw_string( 135 | get_parent().chart_properties.font, 136 | _get_y_tick_label_position(left, label), 137 | label, 138 | HORIZONTAL_ALIGNMENT_CENTER, 139 | -1, 140 | ThemeDB.fallback_font_size, 141 | get_parent().chart_properties.colors.text, 142 | TextServer.JUSTIFICATION_NONE, 143 | TextServer.DIRECTION_AUTO, 144 | TextServer.ORIENTATION_HORIZONTAL 145 | ) 146 | 147 | # Draw y grid 148 | if get_parent().chart_properties.draw_horizontal_grid: 149 | draw_multiline(horizontal_grid, get_parent().chart_properties.colors.grid, 1) 150 | 151 | # Draw y ticks 152 | if get_parent().chart_properties.draw_ticks: 153 | draw_multiline(horizontal_ticks, get_parent().chart_properties.colors.ticks, 1) 154 | 155 | 156 | func _get_x_tick_label_position(base_position: Vector2, text: String) -> Vector2: 157 | return base_position + Vector2( 158 | - get_parent().chart_properties.font.get_string_size(text).x / 2, 159 | ThemeDB.fallback_font_size + get_parent().chart_properties.x_tick_size 160 | ) 161 | 162 | func _get_y_tick_label_position(base_position: Vector2, text: String) -> Vector2: 163 | return base_position - Vector2( 164 | get_parent().chart_properties.font.get_string_size(text).x + get_parent().chart_properties.y_tick_size + get_parent().chart_properties.x_ticklabel_space, 165 | - ThemeDB.fallback_font_size * 0.35 166 | ) 167 | -------------------------------------------------------------------------------- /addons/easy_charts/utilities/containers/canvas/plot_box/grid_box.gd.uid: -------------------------------------------------------------------------------- 1 | uid://doelssxa0y7ap 2 | -------------------------------------------------------------------------------- /addons/easy_charts/utilities/containers/canvas/plot_box/plot_box.gd: -------------------------------------------------------------------------------- 1 | extends Control 2 | class_name PlotBox 3 | 4 | # TODO: These signals have been removed. If anyone needs them, we can bring 5 | # them back, but from the Chart, not from the PlotBox. 6 | #signal function_point_entered(point, function) 7 | #signal function_point_exited(point, function) 8 | 9 | var focused_point: Point 10 | var focused_function: Function 11 | 12 | var box_margins: Vector2 # Margins relative to this rect, in order to make space for ticks and tick_labels 13 | var plot_inner_offset: Vector2 = Vector2(15, 15) # How many pixels from the broders should the plot be 14 | 15 | # TODO: Remove 16 | var chart_properties: ChartProperties 17 | 18 | func get_box() -> Rect2: 19 | var box: Rect2 = get_rect() 20 | box.position.x += box_margins.x 21 | # box.position.y += box_margins.y 22 | box.end.x -= box_margins.x 23 | box.end.y -= box_margins.y 24 | return box 25 | 26 | func get_plot_box() -> Rect2: 27 | var inner_box: Rect2 = get_box() 28 | inner_box.position.x += plot_inner_offset.x 29 | inner_box.position.y += plot_inner_offset.y 30 | inner_box.end.x -= plot_inner_offset.x * 2 31 | inner_box.end.y -= plot_inner_offset.y * 2 32 | return inner_box 33 | -------------------------------------------------------------------------------- /addons/easy_charts/utilities/containers/canvas/plot_box/plot_box.gd.uid: -------------------------------------------------------------------------------- 1 | uid://8xd8yvw7lumm 2 | -------------------------------------------------------------------------------- /addons/easy_charts/utilities/containers/data_tooltip/data_tooltip.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends PanelContainer 3 | class_name DataTooltip 4 | 5 | var gap: float = 15 6 | 7 | @onready var x_lbl : Label = $PointData/x 8 | @onready var y_lbl : Label = $PointData/Value/y 9 | @onready var func_lbl : Label = $PointData/Value/Function 10 | @onready var color_rect: Panel = $PointData/Value/Color 11 | 12 | func _ready(): 13 | hide() 14 | update_size() 15 | 16 | func update_position(position: Vector2) -> void: 17 | if (position.x + gap + size.x) > get_parent().size.x: 18 | self.position = position - Vector2(size.x + gap, (get_rect().size.y / 2)) 19 | else: 20 | self.position = position + Vector2(15, - (get_rect().size.y / 2)) 21 | 22 | func set_font(font: FontFile) -> void: 23 | theme.set("default_font", font) 24 | 25 | func update_values(x: String, y: String, function_name: String, color: Color): 26 | x_lbl.set_text(x) 27 | y_lbl.set_text(y) 28 | func_lbl.set_text(function_name) 29 | color_rect.get("theme_override_styles/panel").set("bg_color", color) 30 | 31 | func update_size(): 32 | x_lbl.set_text("") 33 | y_lbl.set_text("") 34 | func_lbl.set_text("") 35 | size = Vector2.ZERO 36 | 37 | func _on_DataTooltip_visibility_changed(): 38 | if not visible: 39 | update_size() 40 | -------------------------------------------------------------------------------- /addons/easy_charts/utilities/containers/data_tooltip/data_tooltip.gd.uid: -------------------------------------------------------------------------------- 1 | uid://bsvy31fx78e5l 2 | -------------------------------------------------------------------------------- /addons/easy_charts/utilities/containers/data_tooltip/data_tooltip.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=5 format=3 uid="uid://dmesmjhiuqdo4"] 2 | 3 | [ext_resource type="Script" uid="uid://bsvy31fx78e5l" path="res://addons/easy_charts/utilities/containers/data_tooltip/data_tooltip.gd" id="1"] 4 | 5 | [sub_resource type="StyleBoxFlat" id="1"] 6 | content_margin_left = 10.0 7 | content_margin_top = 8.0 8 | content_margin_right = 10.0 9 | content_margin_bottom = 8.0 10 | bg_color = Color(0.101961, 0.101961, 0.101961, 0.784314) 11 | border_color = Color(1, 1, 1, 1) 12 | corner_radius_top_left = 8 13 | corner_radius_top_right = 8 14 | corner_radius_bottom_right = 8 15 | corner_radius_bottom_left = 8 16 | corner_detail = 20 17 | anti_aliasing_size = 0.65 18 | 19 | [sub_resource type="StyleBoxFlat" id="2"] 20 | corner_radius_top_left = 5 21 | corner_radius_top_right = 5 22 | corner_radius_bottom_right = 5 23 | corner_radius_bottom_left = 5 24 | corner_detail = 20 25 | anti_aliasing_size = 0.7 26 | 27 | [sub_resource type="StyleBoxEmpty" id="3"] 28 | 29 | [node name="DataTooltip" type="PanelContainer"] 30 | visible = false 31 | offset_right = 20.0 32 | offset_bottom = 16.0 33 | mouse_filter = 2 34 | theme_override_styles/panel = SubResource("1") 35 | script = ExtResource("1") 36 | 37 | [node name="PointData" type="VBoxContainer" parent="."] 38 | offset_left = 10.0 39 | offset_top = 8.0 40 | offset_right = 37.0 41 | offset_bottom = 42.0 42 | grow_horizontal = 2 43 | size_flags_horizontal = 3 44 | theme_override_constants/separation = 1 45 | alignment = 1 46 | __meta__ = { 47 | "_edit_use_anchors_": false 48 | } 49 | 50 | [node name="x" type="Label" parent="PointData"] 51 | offset_top = 2.0 52 | offset_bottom = 16.0 53 | size_flags_horizontal = 0 54 | theme_override_colors/font_color = Color(1, 1, 1, 1) 55 | valign = 1 56 | 57 | [node name="Value" type="HBoxContainer" parent="PointData"] 58 | offset_top = 17.0 59 | offset_right = 27.0 60 | offset_bottom = 31.0 61 | grow_horizontal = 2 62 | size_flags_horizontal = 7 63 | theme_override_constants/separation = 1 64 | 65 | [node name="Color" type="Panel" parent="PointData/Value"] 66 | offset_top = 2.0 67 | offset_right = 10.0 68 | offset_bottom = 12.0 69 | custom_minimum_size = Vector2(10, 10) 70 | size_flags_horizontal = 4 71 | size_flags_vertical = 4 72 | theme_override_styles/panel = SubResource("2") 73 | 74 | [node name="VSeparator" type="VSeparator" parent="PointData/Value"] 75 | offset_left = 11.0 76 | offset_right = 15.0 77 | offset_bottom = 14.0 78 | theme_override_constants/separation = 4 79 | theme_override_styles/separator = SubResource("3") 80 | 81 | [node name="Function" type="Label" parent="PointData/Value"] 82 | offset_left = 16.0 83 | offset_right = 16.0 84 | offset_bottom = 14.0 85 | size_flags_horizontal = 0 86 | size_flags_vertical = 5 87 | valign = 1 88 | 89 | [node name="sep" type="Label" parent="PointData/Value"] 90 | offset_left = 17.0 91 | offset_right = 21.0 92 | offset_bottom = 14.0 93 | size_flags_horizontal = 0 94 | size_flags_vertical = 5 95 | text = ":" 96 | valign = 1 97 | 98 | [node name="VSeparator2" type="VSeparator" parent="PointData/Value"] 99 | offset_left = 22.0 100 | offset_right = 26.0 101 | offset_bottom = 14.0 102 | theme_override_constants/separation = 4 103 | theme_override_styles/separator = SubResource("3") 104 | 105 | [node name="y" type="Label" parent="PointData/Value"] 106 | offset_left = 27.0 107 | offset_right = 27.0 108 | offset_bottom = 14.0 109 | size_flags_horizontal = 0 110 | size_flags_vertical = 5 111 | theme_override_colors/font_color = Color(1, 1, 1, 1) 112 | valign = 1 113 | 114 | [connection signal="visibility_changed" from="." to="." method="_on_DataTooltip_visibility_changed"] 115 | -------------------------------------------------------------------------------- /addons/easy_charts/utilities/containers/legend/function_label.gd: -------------------------------------------------------------------------------- 1 | extends HBoxContainer 2 | class_name FunctionLabel 3 | 4 | @onready var type_lbl: FunctionTypeLabel = $FunctionType 5 | @onready var name_lbl: Label = $FunctionName 6 | 7 | # Called when the node enters the scene tree for the first time. 8 | func _ready(): 9 | pass # Replace with function body. 10 | 11 | func init_label(function: Function) -> void: 12 | type_lbl.type = function.get_type() 13 | type_lbl.color = function.get_color() 14 | type_lbl.marker = function.get_marker() 15 | name_lbl.set_text(function.name) 16 | name_lbl.set("theme_override_colors/font_color", get_parent().chart_properties.colors.text) 17 | 18 | func init_clabel(type: int, color: Color, marker: int, name: String) -> void: 19 | type_lbl.type = type 20 | type_lbl.color = color 21 | type_lbl.marker = marker 22 | name_lbl.set_text(name) 23 | name_lbl.set("theme_override_colors/font_color", get_parent().chart_properties.colors.text) 24 | -------------------------------------------------------------------------------- /addons/easy_charts/utilities/containers/legend/function_label.gd.uid: -------------------------------------------------------------------------------- 1 | uid://ckinjw2oipoxc 2 | -------------------------------------------------------------------------------- /addons/easy_charts/utilities/containers/legend/function_label.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://k2bgv6cwudd0"] 2 | 3 | [ext_resource type="Script" uid="uid://cphcx5cm4i2yx" path="res://addons/easy_charts/utilities/containers/legend/function_type.gd" id="1"] 4 | [ext_resource type="Script" uid="uid://ckinjw2oipoxc" path="res://addons/easy_charts/utilities/containers/legend/function_label.gd" id="2"] 5 | 6 | [node name="FunctionLabel" type="HBoxContainer"] 7 | anchor_right = 1.0 8 | anchor_bottom = 1.0 9 | theme_override_constants/separation = 5 10 | script = ExtResource("2") 11 | 12 | [node name="FunctionType" type="Label" parent="."] 13 | offset_top = 293.0 14 | offset_right = 20.0 15 | offset_bottom = 307.0 16 | custom_minimum_size = Vector2(20, 0) 17 | script = ExtResource("1") 18 | 19 | [node name="FunctionName" type="Label" parent="."] 20 | offset_left = 25.0 21 | offset_top = 293.0 22 | offset_right = 25.0 23 | offset_bottom = 307.0 24 | theme_override_colors/font_color = Color(0, 0, 0, 1) 25 | -------------------------------------------------------------------------------- /addons/easy_charts/utilities/containers/legend/function_legend.gd: -------------------------------------------------------------------------------- 1 | extends VBoxContainer 2 | class_name FunctionLegend 3 | 4 | @onready var f_label_scn: PackedScene = preload("res://addons/easy_charts/utilities/containers/legend/function_label.tscn") 5 | 6 | var chart_properties: ChartProperties 7 | 8 | func _ready() -> void: 9 | pass 10 | 11 | func clear() -> void: 12 | for label in get_children(): 13 | label.queue_free() 14 | 15 | func add_function(function: Function) -> void: 16 | var f_label: FunctionLabel = f_label_scn.instantiate() 17 | add_child(f_label) 18 | f_label.init_label(function) 19 | 20 | func add_label(type: int, color: Color, marker: int, name: String) -> void: 21 | var f_label: FunctionLabel = f_label_scn.instantiate() 22 | add_child(f_label) 23 | f_label.init_clabel(type, color, marker, name) 24 | -------------------------------------------------------------------------------- /addons/easy_charts/utilities/containers/legend/function_legend.gd.uid: -------------------------------------------------------------------------------- 1 | uid://cqfq31uiw71sk 2 | -------------------------------------------------------------------------------- /addons/easy_charts/utilities/containers/legend/function_legend.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://c6ffuulowjw4g"] 2 | 3 | [ext_resource type="Script" uid="uid://cqfq31uiw71sk" path="res://addons/easy_charts/utilities/containers/legend/function_legend.gd" id="1"] 4 | 5 | [node name="FunctionLegend" type="VBoxContainer"] 6 | offset_right = 80.0 7 | offset_bottom = 26.0 8 | script = ExtResource("1") 9 | -------------------------------------------------------------------------------- /addons/easy_charts/utilities/containers/legend/function_type.gd: -------------------------------------------------------------------------------- 1 | extends Label 2 | class_name FunctionTypeLabel 3 | 4 | var type: int 5 | var marker: int 6 | var color: Color 7 | 8 | func _draw() -> void: 9 | var center: Vector2 = get_rect().get_center() 10 | 11 | match self.type: 12 | Function.Type.LINE: 13 | draw_line( 14 | Vector2(get_rect().position.x, center.y), 15 | Vector2(get_rect().end.x, center.y), 16 | color, 3 17 | ) 18 | Function.Type.AREA: 19 | var c2: Color = color 20 | c2.a = 0.3 21 | draw_rect( 22 | Rect2( 23 | Vector2(get_rect().position.x, center.y), 24 | Vector2(get_rect().end.x, get_rect().end.y / 2) 25 | ), 26 | c2, 3 27 | ) 28 | draw_line( 29 | Vector2(get_rect().position.x, center.y), 30 | Vector2(get_rect().end.x, center.y), 31 | color, 3 32 | ) 33 | Function.Type.PIE: 34 | draw_rect( 35 | Rect2(center - (Vector2.ONE * 3), (Vector2.ONE * 3 * 2)), 36 | color, 1.0 37 | ) 38 | Function.Type.SCATTER, _: 39 | pass 40 | 41 | match self.marker: 42 | Function.Marker.NONE: 43 | pass 44 | Function.Marker.SQUARE: 45 | draw_rect( 46 | Rect2(center - (Vector2.ONE * 3), (Vector2.ONE * 3 * 2)), 47 | color, 1.0 48 | ) 49 | Function.Marker.TRIANGLE: 50 | draw_colored_polygon( 51 | PackedVector2Array([ 52 | center + (Vector2.UP * 3 * 1.3), 53 | center + (Vector2.ONE * 3 * 1.3), 54 | center - (Vector2(1, -1) * 3 * 1.3) 55 | ]), color, [], null 56 | ) 57 | Function.Marker.CROSS: 58 | draw_line( 59 | center - (Vector2.ONE * 3), 60 | center + (Vector2.ONE * 3), 61 | color, 3, true 62 | ) 63 | draw_line( 64 | center + (Vector2(1, -1) * 3), 65 | center + (Vector2(-1, 1) * 3), 66 | color, 3 / 2, true 67 | ) 68 | Function.Marker.CIRCLE, _: 69 | draw_circle(center, 3, color) 70 | -------------------------------------------------------------------------------- /addons/easy_charts/utilities/containers/legend/function_type.gd.uid: -------------------------------------------------------------------------------- 1 | uid://cphcx5cm4i2yx 2 | -------------------------------------------------------------------------------- /addons/easy_charts/utilities/icons/linechart.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /addons/easy_charts/utilities/scripts/ec_utilities.gd: -------------------------------------------------------------------------------- 1 | extends RefCounted 2 | class_name ECUtilities 3 | 4 | var alphabet : String = "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z" 5 | 6 | func _ready(): 7 | pass 8 | 9 | static func _map_domain(value: float, from_domain: ChartAxisDomain, to_domain: ChartAxisDomain) -> float: 10 | return remap(value, from_domain.lb, from_domain.ub, to_domain.lb, to_domain.ub) 11 | 12 | static func _format_value(value: float, is_decimal: bool) -> String: 13 | return ("%.2f" if is_decimal else "%s") % snapped(value, 0.01) 14 | 15 | ### Utility Inner functions ### 16 | 17 | static func _contains_string(array: Array) -> bool: 18 | for value in array: 19 | if value is String: 20 | return true 21 | return false 22 | 23 | static func _is_decimal(value: float) -> bool: 24 | return abs(fmod(value, 1)) > 0.0 25 | 26 | static func _has_decimals(values: Array) -> bool: 27 | var temp: Array = values.duplicate(true) 28 | 29 | for dim in temp: 30 | for val in dim: 31 | if val is String: 32 | return false 33 | if abs(fmod(val, 1)) > 0.0: 34 | return true 35 | 36 | return false 37 | 38 | static func _find_min_max(values: Array) -> Dictionary: 39 | var temp: Array = values.duplicate(true) 40 | var _min: float 41 | var _max: float 42 | 43 | var min_ts: Array 44 | var max_ts: Array 45 | for dim in temp: 46 | min_ts.append(dim.min()) 47 | max_ts.append(dim.max()) 48 | _min = min_ts.min() 49 | _max = max_ts.max() 50 | 51 | return { min = _min, max = _max } 52 | 53 | static func _sample_values(values: Array, from_domain: ChartAxisDomain, to_domain: ChartAxisDomain) -> PackedFloat32Array: 54 | if values.is_empty(): 55 | printerr("Trying to plot an empty dataset!") 56 | return PackedFloat32Array() 57 | 58 | # We are not considering String values here!!! 59 | 60 | var sampled: PackedFloat32Array = [] 61 | 62 | for value in values: 63 | sampled.push_back(_map_domain(value, from_domain, to_domain)) 64 | 65 | return sampled 66 | 67 | static func _round_min(val: float) -> float: 68 | return round(val) if abs(val) < 10 else floor(val / 10.0) * 10.0 69 | 70 | static func _round_max(val: float) -> float: 71 | return round(val) if abs(val) < 10 else ceil(val / 10.0) * 10.0 72 | -------------------------------------------------------------------------------- /addons/easy_charts/utilities/scripts/ec_utilities.gd.uid: -------------------------------------------------------------------------------- 1 | uid://v8s4a52cq83s 2 | --------------------------------------------------------------------------------