From 24c8a2ad56fa29b0285333aad5b7430c7882a476 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Santilio?= Date: Wed, 30 Dec 2020 15:28:15 +0100 Subject: [PATCH 1/8] plugin.cfg v0.5.0 --- addons/easy_charts/plugin.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/easy_charts/plugin.cfg b/addons/easy_charts/plugin.cfg index 3aa7d38..44f49ce 100644 --- a/addons/easy_charts/plugin.cfg +++ b/addons/easy_charts/plugin.cfg @@ -3,5 +3,5 @@ name="EasyCharts" description="" author="Nicolò \"fenix\" Santilio" -version="0.4.6" +version="0.5.0" script="plugin.gd" From 899c34d9fae718455acd4cea00ad8bd6cf02623f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Santilio?= Date: Wed, 30 Dec 2020 15:29:05 +0100 Subject: [PATCH 2/8] Add files via upload --- .../easy_charts/Utilities/Data/data_frame.gd | 18 +- addons/easy_charts/Utilities/Data/matrix.gd | 6 + .../Utilities/Data/matrix_generator.gd | 2 +- addons/easy_charts/Utilities/Scripts/chart.gd | 717 ++++++++++++++++++ .../easy_charts/Utilities/Scripts/chart2d.gd | 10 + .../Utilities/Scripts/chart_object.gd | 39 + 6 files changed, 790 insertions(+), 2 deletions(-) create mode 100644 addons/easy_charts/Utilities/Scripts/chart.gd create mode 100644 addons/easy_charts/Utilities/Scripts/chart2d.gd create mode 100644 addons/easy_charts/Utilities/Scripts/chart_object.gd diff --git a/addons/easy_charts/Utilities/Data/data_frame.gd b/addons/easy_charts/Utilities/Data/data_frame.gd index 8dbdc88..df6037b 100644 --- a/addons/easy_charts/Utilities/Data/data_frame.gd +++ b/addons/easy_charts/Utilities/Data/data_frame.gd @@ -34,6 +34,21 @@ func build_dataframe_from_matrix(data_matrix : Matrix, index : PoolStringArray, var data : Array = data_matrix.to_array() return build_dataframe(data, index, header) +func insert_column(header_n : String, column : Array, index : int = _dataframe[0].size()) -> void: + assert(column.size()+1 == _dataframe.size(), "error: the column size must match the dataframe column size") + _header.insert(index, header_n) + _data_matrix.insert_column(column, index-1) + self._dataframe = build_dataframe_from_matrix(_data_matrix, _index, _header) + +func insert_row(index_n : String, row : Array, index : int = _dataframe.size()) -> void: + assert(row.size()+1 == _dataframe[0].size(), "error: the row size must match the dataframe row size") + _index.insert(index-1, index_n) + _data_matrix.insert_row(row, index-1) + self._dataframe = build_dataframe_from_matrix(_data_matrix, _index, _header) + +func get_matrix() -> Matrix: + return _data_matrix + func get_dataframe() -> Array: return _dataframe @@ -43,13 +58,14 @@ func get_dataset() -> Array: func get_index() -> PoolStringArray: return _index + func _to_string() -> String: var last_string_len : int for row in _dataframe: for column in row: var string_len : int = str(column).length() last_string_len = string_len if string_len > last_string_len else last_string_len - var string : String = "\n" + var string : String = "" for row_i in _dataframe.size(): for column_i in _dataframe[row_i].size(): string+="%*s" % [last_string_len+1, _dataframe[row_i][column_i]] diff --git a/addons/easy_charts/Utilities/Data/matrix.gd b/addons/easy_charts/Utilities/Data/matrix.gd index 718ecbc..659649c 100644 --- a/addons/easy_charts/Utilities/Data/matrix.gd +++ b/addons/easy_charts/Utilities/Data/matrix.gd @@ -21,8 +21,14 @@ func _to_string() -> String: return string func insert_row(row : Array, index : int = _matrix.size()) -> void: + assert(row.size() == _matrix[0].size(), "error: the row size must match matrix row size") _matrix.insert(index, row) +func insert_column(column : Array, index : int = _matrix[0].size()) -> void: + assert(column.size() == _matrix.size(), "error: the column size must match matrix column size") + for row_idx in column.size(): + _matrix[row_idx].insert(index, column[row_idx]) + func to_array() -> Array: return _matrix.duplicate() diff --git a/addons/easy_charts/Utilities/Data/matrix_generator.gd b/addons/easy_charts/Utilities/Data/matrix_generator.gd index eac59df..faa71d4 100644 --- a/addons/easy_charts/Utilities/Data/matrix_generator.gd +++ b/addons/easy_charts/Utilities/Data/matrix_generator.gd @@ -14,7 +14,7 @@ static func random_float_range(size : Vector2, from : float, to : float, _seed : return Matrix.new(array) # Generates a Matrix giving an Array (Array must by Array[Array]) -static func from_array(array : Array) -> Matrix: +static func from_array(array : Array = []) -> Matrix: var matrix : Array = [] matrix.append(array) return Matrix.new(matrix) diff --git a/addons/easy_charts/Utilities/Scripts/chart.gd b/addons/easy_charts/Utilities/Scripts/chart.gd new file mode 100644 index 0000000..45a99af --- /dev/null +++ b/addons/easy_charts/Utilities/Scripts/chart.gd @@ -0,0 +1,717 @@ +extends Control +class_name Chart + +# Classes +enum TYPES { Line, Bar, Scatter, Radar, Pie } + +# Signals .................................. +signal chart_plotted(chart) # emit when a chart is plotted (static) or updated (dynamic) +signal point_pressed(point) + +# Onready Vars ............................ +onready var PointData = $PointData/PointData +onready var Points = $Points +onready var Legend = $Legend +onready var ChartName : Label = $ChartName + +# Scenes and Reosurces ...................... +var point_node : PackedScene = preload("../Point/Point.tscn") +var LegendElement : PackedScene = preload("../Legend/FunctionLegend.tscn") + +# Enums ..................................... +enum PointShapes { Dot, Triangle, Square, Cross } + +# Shared Variables ......................... +var SIZE : Vector2 = Vector2() +var OFFSET : Vector2 = Vector2(0,0) +var origin : Vector2 + +var font_size : float = 16 +var const_height : float = font_size/2*font_size/20 +var const_width : float = font_size/2 + +# actual distance between x and y values +var x_pass : float +var y_pass : float + +# vertical distance between y consecutive points used for intervals +var v_dist : float +var h_dist : float + +# define values on x an y axis +var x_chors : Array +var y_chors : Array + +# actual coordinates of points (in pixel) +var x_coordinates : Array +var y_coordinates : Array + +# data contained in file +var data : Array + +# amount of functions to represent +var functions : int = 0 + +# database values +var x_datas : Array +var y_datas : Array + +# labels displayed on chart +var x_label : String + +var x_labels : Array +var y_labels : Array + +var x_margin_min : int = 0 +var y_margin_min : int = 0 + +# actual values of point, from the database +var point_values : Array + +# actual position of points in pixel +var point_positions : Array + +var legend : Array setget set_legend,get_legend + +# ................... Export Shared Variables .................. +export (String) var chart_name : String = "" setget set_chart_name +export (String, FILE, "*.txt, *.csv") var source : String = "" setget set_source +export (String) var delimiter : String = ";" setget set_delimiter + +var origin_at_zero : bool = true setget set_origin_at_zero#, get_origin_at_zero +var are_values_columns : bool = false setget set_are_values_columns#, get_are_values_columns +var show_x_values_as_labels : bool = true setget set_show_x_values_as_labels#, get_show_x_values_as_labels +var labels_index : int = 0 setget set_labels_index#, get_labels_index +var function_names_index : int = 0 setget set_function_names_index#, get_function_names_index + +# for radar +var use_height_as_radius : bool = false setget set_use_height_as_radius +var radius : float = 150.0 setget _set_radius,get_radius + +# for columns +var column_width : float = 10 setget set_column_width +var column_gap : float = 2 setget set_column_gap + +var full_scale : float = 1.0 setget set_full_scale +var x_decim : float = 5.0 setget set_x_decim +var y_decim : float = 1.0 setget set_y_decim + +var points_shape : Array = [PointShapes.Dot] setget set_points_shape +var function_colors = [Color("#1e1e1e")] setget set_function_colors +var outline_color : Color = Color("#1e1e1e") setget set_outline_color +var box_color : Color = Color("#1e1e1e") setget set_box_color +var v_lines_color : Color = Color("#cacaca") setget set_v_lines_color +var h_lines_color : Color = Color("#cacaca") setget set_h_lines_color +var grid_color : Color = Color("#1e1e1e") setget set_grid_color +var font : Font setget set_font +var bold_font : Font setget set_bold_font +var font_color : Color = Color("#1e1e1e") setget set_font_color + +var use_template : bool = true setget set_use_template +var template : int = 0 setget set_template + +# modifiers +var rotation : float = 0 setget set_rotation +var invert_chart : bool = false setget set_invert_chart + +# Only disp a certain range of values: +# (x , 0) -> Only disp first 'x' values +# (0 , y) -> Only disp last 'y' values +# (x , y) -> Only disp values in range [x, y] +# (0 , 0) -> Disp all values (full range) +var only_disp_values : Vector2 = Vector2(0,0) setget set_only_disp_values + +# A vector representing limit values (both on x and y axis) you want to stay over or below +# The treshold value is always relative to your dataset values +# ex. if your dataset is [ [100,100], [300,300] ] a proper treshold would be (200,200) +var treshold : Vector2 setget set_treshold + +# A vector representing @treshold coordinates in its relative chart +# only used to draw treshold values +var treshold_draw : Vector2 + +# !! API v2 +static func instance(chart_type : int): + var chart_t : String = Utilities.get_chart_type(chart_type) + var chart : String = "res://addons/easy_charts/%s/%s.tscn" % [chart_t, chart_t] + return load(chart).instance() + +# .......................... Properties Manager .................................... +func _get(property): + match property: + "Chart_Properties/origin_at_zero": + return origin_at_zero + "Chart_Properties/are_values_columns": + return are_values_columns + "Chart_Properties/show_x_values_as_labels": + return show_x_values_as_labels + "Chart_Properties/labels_index": + return labels_index + "Chart_Properties/function_names_index": + return function_names_index + "Chart_Properties/use_height_as_radius": + return use_height_as_radius + "Chart_Properties/radius": + return radius + "Chart_Properties/column_width": + return column_width + "Chart_Properties/column_gap": + return column_gap + + "Chart_Display/full_scale": + return full_scale + "Chart_Display/x_decim": + return x_decim + "Chart_Display/y_decim": + return y_decim + + "Chart_Style/points_shape": + return points_shape + "Chart_Style/function_colors": + return function_colors + "Chart_Style/template": + return template + "Chart_Style/use_template": + return use_template + "Chart_Style/outline_color": + return outline_color + "Chart_Style/grid_color": + return grid_color + "Chart_Style/box_color": + return box_color + "Chart_Style/v_lines_color": + return v_lines_color + "Chart_Style/h_lines_color": + return h_lines_color + "Chart_Style/font": + return font + "Chart_Style/bold_font": + return bold_font + "Chart_Style/font_color": + return font_color + + "Chart_Modifiers/treshold": + return treshold + "Chart_Modifiers/only_disp_values": + return only_disp_values + "Chart_Modifiers/rotation": + return rotation + "Chart_Modifiers/invert_chart": + return invert_chart + +func _set(property, value): + match property: + "Chart_Properties/origin_at_zero": + origin_at_zero = value + return true + "Chart_Properties/are_values_columns": + are_values_columns = value + return true + "Chart_Properties/show_x_values_as_labels": + show_x_values_as_labels = value + return true + "Chart_Properties/labels_index": + labels_index = value + return true + "Chart_Properties/function_names_index": + function_names_index = value + return true + "Chart_Properties/use_height_as_radius": + use_height_as_radius = value + return true + "Chart_Properties/radius": + radius = value + return true + "Chart_Properties/column_width": + column_width = value + return true + "Chart_Properties/column_gap": + column_gap = value + return true + + "Chart_Display/full_scale": + full_scale = value + return true + "Chart_Display/x_decim": + x_decim = value + return true + "Chart_Display/y_decim": + y_decim = value + return true + + "Chart_Style/points_shape": + points_shape = value + return true + "Chart_Style/function_colors": + function_colors = value + return true + "Chart_Style/use_template": + use_template = value + return true + "Chart_Style/template": + template = value + apply_template(template) + return true + "Chart_Style/outline_color": + outline_color = value + return true + "Chart_Style/grid_color": + grid_color = value + return true + "Chart_Style/box_color": + box_color = value + return true + "Chart_Style/v_lines_color": + v_lines_color = value + return true + "Chart_Style/h_lines_color": + h_lines_color = value + return true + "Chart_Style/font": + font = value + return true + "Chart_Style/bold_font": + bold_font = value + return true + "Chart_Style/font_color": + font_color = value +# apply_template(template) + return true + + "Chart_Modifiers/treshold": + treshold = value + return true + "Chart_Modifiers/only_disp_values": + only_disp_values = value + return true + "Chart_Modifiers/rotation": + rotation = value + return true + "Chart_Modifiers/invert_chart": + invert_chart = value + return true + +# .......................... Shared Functions and virtuals ........................ +func slice_data() -> Array: + var data_to_display : Array + data_to_display.resize(data.size()) + if only_disp_values == Vector2(0,0) : + data_to_display = data.duplicate(true) + elif only_disp_values.x == 0 and only_disp_values.y < data[0].size(): + for row_idx in data.size(): + data_to_display[row_idx] = [data[row_idx][0]] + data[row_idx].slice(data[row_idx].size()-only_disp_values.y, data[row_idx].size()-1) + elif only_disp_values.y == 0 and only_disp_values.x < data[0].size(): + for row_idx in data.size(): + data_to_display[row_idx] = [data[row_idx][0]] + data[row_idx].slice(1, only_disp_values.x) + elif only_disp_values.x != 0 and only_disp_values.y != 0 and only_disp_values.y < data[0].size() and only_disp_values.x < data[0].size(): + for row_idx in data.size(): + data_to_display[row_idx] = [data[row_idx][0]] + data[row_idx].slice(only_disp_values.x, data[row_idx].size()-only_disp_values.y) + else: + data_to_display = data.duplicate(true) + return data_to_display + +func plot(): + load_font() + PointData.hide() + + if source == "" or source == null: + Utilities._print_message("Can't plot a chart without a Source file. Please, choose it in editor, or use the custom function _plot().",1) + return + + + data = read_datas(source) + structure_datas(slice_data(),are_values_columns,labels_index) + build_chart() + count_functions() + calculate_pass() + calculate_colors() + calculate_coordinates() + set_shapes() + create_legend() + emit_signal("chart_plotted",self) + +func plot_from_csv(csv_file : String, _delimiter : String = delimiter): + load_font() + PointData.hide() + + if csv_file == "" or csv_file == null: + Utilities._print_message("Can't plot a chart without a Source file. Please, choose it in editor, or use the custom function _plot().",1) + return + + data = read_datas(csv_file, _delimiter) + structure_datas(slice_data(),are_values_columns,labels_index) + build_chart() + count_functions() + calculate_pass() + calculate_colors() + calculate_coordinates() + set_shapes() + create_legend() + emit_signal("chart_plotted",self) + +func plot_from_array(array : Array) -> void: + clean_variables() + clear_points() + load_font() + PointData.hide() + + if array.empty(): + Utilities._print_message("Can't plot a chart with an empty Array.",1) + return + + data = array.duplicate() + structure_datas(slice_data(), are_values_columns,labels_index) + build_chart() + count_functions() + calculate_pass() + calculate_colors() + calculate_coordinates() + set_shapes() + create_legend() + emit_signal("chart_plotted",self) + + if not is_connected("item_rect_changed",self, "redraw"): connect("item_rect_changed", self, "redraw") + +func plot_from_dataframe(dataframe : DataFrame) -> void: + clean_variables() + clear_points() + load_font() + load_font() + PointData.hide() + + data = dataframe.get_dataframe().duplicate(true) + + if data.empty(): + Utilities._print_message("Can't plot a chart with an empty Array.",1) + return + + structure_datas(slice_data(),are_values_columns,labels_index) + build_chart() + count_functions() + calculate_pass() + calculate_colors() + calculate_coordinates() + set_shapes() + create_legend() + emit_signal("chart_plotted",self) + + if not is_connected("item_rect_changed",self, "redraw"): connect("item_rect_changed", self, "redraw") + +# Append new data (in array format) to the already plotted data. +# All data are stored. +func update_plot_data(array : Array) -> void: + if array.empty(): + Utilities._print_message("Can't plot a chart with an empty Array.",1) + return + + data.append(array) + structure_datas(slice_data(),are_values_columns,labels_index) + redraw() + count_functions() + calculate_colors() + set_shapes() + create_legend() + emit_signal("chart_plotted",self) + + update() + +# Append a new column to data +func append_new_column(dataset : Array, column : Array): + if column.empty(): + Utilities._print_message("Can't update plot with an empty row.",1) + return + for value_idx in column.size(): + dataset[value_idx].append(column[value_idx]) + +func plot_placeholder() -> void: + pass + +func load_font(): + if font != null: + font_size = font.get_height() + var theme : Theme = Theme.new() + theme.set_default_font(font) + set_theme(theme) + + else: + var lbl = Label.new() + font = lbl.get_font("") + lbl.free() + if bold_font != null: + PointData.Data.set("custom_fonts/font",bold_font) + +func calculate_colors(): + if function_colors.size() < functions: + for function in range(functions - function_colors.size() + 1): function_colors.append(Color(randf(),randf(), randf())) + +func set_shapes(): + if points_shape.empty() or points_shape.size() < functions: + for function in functions: + points_shape.append(PointShapes.Dot) + +func read_datas(source : String, _delimiter : String = delimiter): + var file : File = File.new() + file.open(source,File.READ) + var content : Array + while not file.eof_reached(): + var line : PoolStringArray = file.get_csv_line(_delimiter) + content.append(line) + file.close() + for data in content: + if data.size() < 2 or data.empty(): + content.erase(data) + return content + +func count_functions(): + if are_values_columns: + if not invert_chart: + functions = data[0].size()-1 + else: + functions = data.size()-1 + else: + if invert_chart: + functions = x_datas.size() + else: + functions = y_datas.size() + +func clear_points(): + if $Points.get_children(): + for function in Points.get_children(): + function.queue_free() + for legend in $Legend.get_children(): + legend.queue_free() + +func redraw(): + build_chart() + calculate_pass() + calculate_coordinates() + update() + +func clean_variables(): + x_chors.clear() + y_chors.clear() + x_datas.clear() + y_datas.clear() + x_label = "" + x_labels.clear() + y_labels.clear() + +# .................. VIRTUAL FUNCTIONS ......................... +func structure_datas(database : Array, are_values_columns : bool, labels_index : int): + pass + +func build_chart(): + pass + +func calculate_pass(): + pass + +func calculate_coordinates(): + pass + +func function_colors(): + pass + +func create_legend(): + for function in functions: + var function_legend : LegendElement + if legend.size() > function: + function_legend = legend[function] + else: + function_legend = LegendElement.instance() + legend.append(function_legend) + var f_name : String = y_labels[function] + var legend_font : Font + if font != null: + legend_font = font + if bold_font != null: + legend_font = bold_font + function_legend.create_legend(f_name,function_colors[function],bold_font,font_color) + +# ........................... Shared Setters & Getters .............................. +func apply_template(template_name : int): + if Engine.editor_hint: + set_template(template_name) + property_list_changed_notify() + +# !!! API v2 +func set_chart_name(ch_name : String): + chart_name = ch_name + get_node("ChartName").set_text(chart_name) + +# !!! API v2 +func set_source(source_file : String): + source = source_file + +# !!! API v2 +func set_indexes(lb : int = 0, function_names : int = 0): + labels_index = lb + function_names_index = function_names + +# !!! API v2 +func set_radius(use_height : bool = false, f : float = 0): + use_height_as_radius = use_height + radius = f + +# !!! API v2 +func set_chart_colors(f_colors : PoolColorArray, o_color : Color, b_color : Color, g_color : Color, h_lines : Color, v_lines : Color): + function_colors = f_colors + outline_color = o_color + box_color = b_color + grid_color = g_color + h_lines_color = h_lines + v_lines_color = v_lines + +# !!! API v2 +func set_chart_fonts(normal_font : Font, bold_font : Font, f_color : Color = Color.white): + font = normal_font + self.bold_font = bold_font + font_color = f_color + +# !!! API v2 +func set_delimiter(d : String): + delimiter = d + +# ! API +func set_origin_at_zero(b : bool): + origin_at_zero = b + +# ! API +func set_are_values_columns(b : bool): + are_values_columns = b + +# ! API +func set_show_x_values_as_labels(b : bool): + show_x_values_as_labels = b + +func set_labels_index(i : int): + labels_index = i + +func set_function_names_index(i : int): + function_names_index = i + +func set_use_height_as_radius(b : bool): + use_height_as_radius = b + +func _set_radius(r : float): + radius = r + +func get_radius() -> float: + if use_height_as_radius: return get_size().y/2 + else: return radius + +# ! API +func set_column_width(f : float): + column_width = f + +# ! API +func set_column_gap(f : float): + column_gap = f + +# ! API +func set_full_scale(f : float): + full_scale = f + +# ! API +func set_x_decim(f : float): + x_decim = f + +# ! API +func set_y_decim(f : float): + y_decim = f + +# ! API +func set_points_shape(a : Array): + points_shape = a + + +# ! API +func set_function_colors(a : PoolColorArray): + function_colors = a + +# ! API +func set_outline_color(c : Color): + outline_color = c + +# ! API +func set_box_color(c : Color): + box_color = c + +# ! API +func set_grid_color(c : Color): + grid_color = c + +# ! API +func set_v_lines_color(c : Color): + v_lines_color = c + +# ! API +func set_h_lines_color(c : Color): + h_lines_color = c + +# ! API +func set_font(f : Font): + font = f + +# ! API +func set_bold_font(f : Font): + bold_font = f + +# ! API +func set_font_color(c : Color): + font_color = c + +func set_use_template(use : bool): + use_template = use + +# ! API +func set_template(template_name : int): + if not use_template: return + template = template_name + if template_name!=null: + var custom_template = Utilities.templates.get(Utilities.templates.keys()[template_name]) + function_colors = custom_template.function_colors as PoolColorArray + outline_color = Color(custom_template.outline_color) + box_color = Color(custom_template.outline_color) + grid_color = Color(custom_template.v_lines_color) + v_lines_color = Color(custom_template.v_lines_color) + h_lines_color = Color(custom_template.h_lines_color) + box_color = Color(custom_template.outline_color) + font_color = Color(custom_template.font_color) + +# ! API +func set_rotation(f : float): + rotation = f + +# ! API +func set_invert_chart(b : bool): + invert_chart = b + +# ! API +func set_treshold(t : Vector2): + treshold = t + +# ! API +func set_only_disp_values(v : Vector2): + only_disp_values = v + +func set_legend(l : Array): + legend = l + +func get_legend() -> Array: + return legend + +# ............................. Shared Signals .............................. +func point_pressed(point : Point): + emit_signal("point_pressed",point) + +func show_data(point : Point): + PointData.update_datas(point) + PointData.show() + +func hide_data(): + PointData.hide() + +func show_slice_data(slice : Slice): + PointData.update_slice_datas(slice) + PointData.show() diff --git a/addons/easy_charts/Utilities/Scripts/chart2d.gd b/addons/easy_charts/Utilities/Scripts/chart2d.gd new file mode 100644 index 0000000..e22bb4e --- /dev/null +++ b/addons/easy_charts/Utilities/Scripts/chart2d.gd @@ -0,0 +1,10 @@ +tool +extends Node2D +class_name Chart2D + +enum PointShapes { Dot, Triangle, Square, Cross } +enum TemplatesNames { Default, Clean, Gradient, Minimal, Invert } + +# Called when the node enters the scene tree for the first time. +func _ready(): + pass # Replace with function body. diff --git a/addons/easy_charts/Utilities/Scripts/chart_object.gd b/addons/easy_charts/Utilities/Scripts/chart_object.gd new file mode 100644 index 0000000..6b92f74 --- /dev/null +++ b/addons/easy_charts/Utilities/Scripts/chart_object.gd @@ -0,0 +1,39 @@ +#extends Object +#class_name ChartObject +# +#""" +#[ChartObject] :: Class +# +#this class is used to store all the functions that Chart, Chart2D and Chart3D custom instances +#will share in-between. +#Chart classes will extend this class. +#""" +# +#enum PointShapes { Dot, Triangle, Square, Cross } +#enum TemplatesNames { Default, Clean, Gradient, Minimal, Invert } +# +#class Chart extends Control: +# var CHART_TYPE : String = "Chart" +# enum PointShapes { Dot, Triangle, Square, Cross } +# enum TemplatesNames { Default, Clean, Gradient, Minimal, Invert } +# +# export (PoolColorArray) var function_colors = [Color("#1e1e1e")] +# export (Array, PointShapes) var points_shape : Array = [PointShapes.Dot] +# +# var functions : int = 0 +# +# func calculate_colors(): +# if function_colors.empty() or function_colors.size() < functions: +# for function in functions: +# function_colors.append(Color("#1e1e1e")) +# +# func set_shapes(): +# if points_shape.empty() or points_shape.size() < functions: +# for function in functions: +# points_shape.append(PointShapes.Dot) +# +# +#class Chart2D extends Node2D: +# var CHART_TYPE : String = "Chart2D" +# enum PointShapes { Dot, Triangle, Square, Cross } +# enum TemplatesNames { Default, Clean, Gradient, Minimal, Invert } From b16e9ab259d54d60bb1c76bb01b9d8aa28809518 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Santilio?= Date: Wed, 30 Dec 2020 15:30:42 +0100 Subject: [PATCH 3/8] Add files via upload --- addons/easy_charts/BarChart/BarChart.tscn | 18 +- addons/easy_charts/BarChart/bar_chart.gd | 392 ++++++++++++++++++++ addons/easy_charts/LineChart/LineChart.tscn | 49 +-- addons/easy_charts/LineChart/line_chart.gd | 76 ++-- 4 files changed, 476 insertions(+), 59 deletions(-) create mode 100644 addons/easy_charts/BarChart/bar_chart.gd diff --git a/addons/easy_charts/BarChart/BarChart.tscn b/addons/easy_charts/BarChart/BarChart.tscn index fea8476..06f25e9 100644 --- a/addons/easy_charts/BarChart/BarChart.tscn +++ b/addons/easy_charts/BarChart/BarChart.tscn @@ -1,7 +1,7 @@ [gd_scene load_steps=4 format=2] [ext_resource path="res://addons/easy_charts/Utilities/Point/PointData.tscn" type="PackedScene" id=1] -[ext_resource path="res://addons/easy_charts/BarChart/BarChart.gd" type="Script" id=2] +[ext_resource path="res://addons/easy_charts/BarChart/bar_chart.gd" type="Script" id=2] [sub_resource type="Theme" id=1] @@ -31,7 +31,7 @@ Chart_Properties/show_x_values_as_labels = true Chart_Properties/column_width = 2.0 Chart_Properties/column_gap = 2.0 Chart_Display/x_decim = 5.0 -Chart_Display/y_decim = 5.0 +Chart_Display/y_decim = 1.0 Chart_Style/points_shape = [ 0 ] Chart_Style/function_colors = PoolColorArray( 0.117647, 0.117647, 0.117647, 1, 0.117647, 0.117647, 0.117647, 1, 0.117647, 0.117647, 0.117647, 1, 0.117647, 0.117647, 0.117647, 1 ) Chart_Style/box_color = Color( 0.117647, 0.117647, 0.117647, 1 ) @@ -40,6 +40,7 @@ Chart_Style/h_lines_color = Color( 0.792157, 0.792157, 0.792157, 1 ) Chart_Style/font = null Chart_Style/bold_font = null Chart_Style/font_color = Color( 0.117647, 0.117647, 0.117647, 1 ) +Chart_Style/use_template = true Chart_Style/template = 0 Chart_Modifiers/invert_chart = false @@ -54,8 +55,8 @@ __meta__ = { } [node name="Points" type="Control" parent="."] -margin_right = 40.0 -margin_bottom = 40.0 +anchor_right = 1.0 +anchor_bottom = 1.0 __meta__ = { "_edit_use_anchors_": false } @@ -81,11 +82,10 @@ __meta__ = { [node name="PointData" parent="." instance=ExtResource( 1 )] [node name="PointData" parent="PointData" index="0"] -visible = false -margin_left = 41.5506 -margin_top = -149.501 -margin_right = 41.4136 -margin_bottom = -148.701 +margin_left = -32.6676 +margin_top = -84.4238 +margin_right = -32.8033 +margin_bottom = -83.6237 theme = SubResource( 1 ) [editable path="PointData"] diff --git a/addons/easy_charts/BarChart/bar_chart.gd b/addons/easy_charts/BarChart/bar_chart.gd new file mode 100644 index 0000000..8b00363 --- /dev/null +++ b/addons/easy_charts/BarChart/bar_chart.gd @@ -0,0 +1,392 @@ +tool +extends Chart +class_name BarChart + +""" +[BarChart] - General purpose node for Bar Charts + +A bar chart or bar graph is a chart or graph that presents categorical data with +rectangular bars with heights or lengths proportional to the values that they represent. +The bars can be plotted vertically or horizontally. A vertical bar chart is sometimes +called a column chart. +A bar graph shows comparisons among discrete categories. One axis of the chart shows +the specific categories being compared, and the other axis represents a measured value. +Some bar graphs present bars clustered in groups of more than one, showing the +values of more than one measured variable. + +/ source : Wikipedia / +""" + +# --------------------- + +func _get_property_list(): + return [ + # Chart Properties + { + "hint": PROPERTY_HINT_NONE, + "usage": PROPERTY_USAGE_CATEGORY, + "name": "BarChart", + "type": TYPE_STRING + }, + { + "hint": PROPERTY_HINT_NONE, + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, + "name": "Chart_Properties/are_values_columns", + "type": TYPE_BOOL + }, + { + "hint": PROPERTY_HINT_RANGE, + "hint_string": "-1,100,1", + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, + "name": "Chart_Properties/labels_index", + "type": TYPE_INT + }, + { + "hint": PROPERTY_HINT_NONE, + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, + "name": "Chart_Properties/show_x_values_as_labels", + "type": TYPE_BOOL + }, + { + "hint": PROPERTY_HINT_RANGE, + "hint_string": "1,20,0.5", + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, + "name": "Chart_Properties/column_width", + "type": TYPE_REAL + }, + { + "hint": PROPERTY_HINT_RANGE, + "hint_string": "0,10,0.5", + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, + "name": "Chart_Properties/column_gap", + "type": TYPE_REAL + }, + + # Chart Display + { + "hint": PROPERTY_HINT_RANGE, + "hint_string": "0.1,10", + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, + "name": "Chart_Display/x_decim", + "type": TYPE_REAL + }, + { + "hint": PROPERTY_HINT_RANGE, + "hint_string": "0.001,1,0.001", + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, + "name": "Chart_Display/y_decim", + "type": TYPE_REAL + }, + + # Chart Style + { + "hint": 24, + "hint_string": "%d/%d:%s"%[TYPE_INT, PROPERTY_HINT_ENUM, + PoolStringArray(PointShapes.keys()).join(",")], + "name": "Chart_Style/points_shape", + "type": TYPE_ARRAY, + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE + }, + { + "hint": PROPERTY_HINT_NONE, + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, + "name": "Chart_Style/function_colors", + "type": TYPE_COLOR_ARRAY + }, + { + "hint": PROPERTY_HINT_NONE, + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, + "name": "Chart_Style/box_color", + "type": TYPE_COLOR + }, + { + "hint": PROPERTY_HINT_NONE, + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, + "name": "Chart_Style/v_lines_color", + "type": TYPE_COLOR + }, + { + "hint": PROPERTY_HINT_NONE, + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, + "name": "Chart_Style/h_lines_color", + "type": TYPE_COLOR + }, + { + "class_name": "Font", + "hint": PROPERTY_HINT_RESOURCE_TYPE, + "hint_string": "Font", + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, + "name": "Chart_Style/font", + "type": TYPE_OBJECT + }, + { + "class_name": "Font", + "hint": PROPERTY_HINT_RESOURCE_TYPE, + "hint_string": "Font", + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, + "name": "Chart_Style/bold_font", + "type": TYPE_OBJECT + }, + { + "hint": PROPERTY_HINT_NONE, + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, + "name": "Chart_Style/font_color", + "type": TYPE_COLOR + }, + { + "hint": PROPERTY_HINT_NONE, + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, + "name": "Chart_Style/use_template", + "type": TYPE_BOOL + }, + { + "hint": PROPERTY_HINT_ENUM, + "hint_string": PoolStringArray(Utilities.templates.keys()).join(","), + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, + "name": "Chart_Style/template", + "type": TYPE_INT + }, + + # Chart Modifiers + { + "hint": PROPERTY_HINT_NONE, + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, + "name": "Chart_Modifiers/invert_chart", + "type": TYPE_BOOL + }, + ] + +func build_chart(): + SIZE = get_size() + origin = Vector2(OFFSET.x,SIZE.y-OFFSET.y) + +func structure_datas(database : Array, are_values_columns : bool, x_values_index : int): + # @x_values_index can be either a column or a row relative to x values + self.are_values_columns = are_values_columns + if are_values_columns: + for row in database.size(): + var t_vals: Array + for column in database[row].size(): + if column == x_values_index: + var x_data = database[row][column] + if x_data.is_valid_float() or x_data.is_valid_integer(): + x_datas.append(x_data as float) + else: + x_datas.append(x_data) + else: + if row != 0: + var y_data = database[row][column] + if y_data.is_valid_float() or y_data.is_valid_integer(): + t_vals.append(y_data as float) + else: + t_vals.append(y_data.replace(",", ".") as float) + else: + y_labels.append(str(database[row][column])) + if not t_vals.empty(): + y_datas.append(t_vals) + x_label = str(x_datas.pop_front()) + else: + for row in database.size(): + if row == x_values_index: + x_datas = (database[row]) + x_label = x_datas.pop_front() as String + else: + var values = database[row] as Array + y_labels.append(values.pop_front() as String) + y_datas.append(values) + for data in y_datas: + for value in data.size(): + data[value] = data[value] as float + + # draw y labels + var to_order: Array + var to_order_min: Array + for cluster in y_datas.size(): + # define x_chors and y_chors + var ordered_cluster = y_datas[cluster].duplicate() as Array + ordered_cluster.sort() + var margin_max = ordered_cluster[ordered_cluster.size() - 1] + var margin_min = ordered_cluster[0] + to_order.append(margin_max) + to_order_min.append(margin_min) + + to_order.sort() + to_order_min.sort() + var margin = to_order.pop_back() + if not origin_at_zero: + y_margin_min = to_order_min.pop_front() + v_dist = y_decim * pow(10.0, str(margin).length()-1) #* pow(10.0, (str(margin).length() - 2 if typeof(margin) == TYPE_INT else str(margin).length() - str(y_decim).length() )) + var multi = 0 + var p = (v_dist * multi) + ((y_margin_min) if not origin_at_zero else 0) + y_chors.append(p as String) + while p < margin: + multi += 1 + p = (v_dist * multi) + ((y_margin_min) if not origin_at_zero else 0) + y_chors.append(p as String) + + # draw x_labels + if not show_x_values_as_labels: + to_order.clear() + to_order = x_datas.duplicate() as PoolIntArray + to_order.sort() + margin = to_order.pop_back() + if not origin_at_zero: + x_margin_min = to_order.pop_front() + h_dist = x_decim * pow(10.0, str(margin).length() - 2) + multi = 0 + p = (h_dist * multi) + ((x_margin_min) if not origin_at_zero else 0) + x_labels.append(p as String) + while p < margin: + multi += 1 + p = (h_dist * multi) + ((x_margin_min) if not origin_at_zero else 0) + x_labels.append(p as String) + + OFFSET.x = (str(margin).length()) * font_size + OFFSET.y = font_size * 2 + + +func calculate_pass(): + if invert_chart: + x_chors = y_labels as PoolStringArray + else: + if show_x_values_as_labels: + x_chors = x_datas as PoolStringArray + else: + x_chors = x_labels + + # calculate distance in pixel between 2 consecutive values/datas + if not are_values_columns: + x_pass = (SIZE.x - OFFSET.x*2 - (column_width) * ( y_datas.size() if not invert_chart else y_datas[0].size()+1 ) - column_gap - column_width/2) / ((x_chors.size()-1) if x_chors.size()!=1 else 1) + else: + x_pass = (SIZE.x - OFFSET.x*2 - (column_width) * ( y_datas.size() if invert_chart else y_datas[0].size()+1 ) - column_gap - column_width/2) / (x_chors.size()-1) + y_pass = (origin.y - ChartName.get_rect().size.y*2) / (y_chors.size() - 1) + +func calculate_coordinates(): + x_coordinates.clear() + y_coordinates.clear() + point_values.clear() + point_positions.clear() + + if invert_chart: + for column in y_datas[0].size(): + var single_coordinates : Array + for row in y_datas: + if origin_at_zero: + single_coordinates.append((row[column]*y_pass)/v_dist) + else: + single_coordinates.append((row[column] - y_margin_min)*y_pass/v_dist) + y_coordinates.append(single_coordinates) + else: + for cluster in y_datas: + var single_coordinates : Array + for value in cluster.size(): + if origin_at_zero: + single_coordinates.append((cluster[value]*y_pass)/v_dist) + else: + single_coordinates.append((cluster[value] - y_margin_min)*y_pass/v_dist) + y_coordinates.append(single_coordinates) + + if show_x_values_as_labels: + for x in x_datas.size(): + x_coordinates.append(x_pass*x) + else: + for x in x_datas.size(): + if origin_at_zero: + if not invert_chart: + x_coordinates.append(x_pass*x) + else: + x_coordinates.append(x*x_pass/h_dist) + else: + x_coordinates.append((x_datas[x] - x_margin_min)*x_pass/h_dist) + + for f in functions: + point_values.append([]) + point_positions.append([]) + + if invert_chart: + for function in y_coordinates.size(): + for function_value in y_coordinates[function].size(): + if are_values_columns: + point_values[function].append([x_datas[function_value],y_datas[function_value][function]]) + point_positions[function].append(Vector2(OFFSET.x/2 + column_width/2 + (column_width + column_gap)*function + x_coordinates[function_value]+origin.x,origin.y-y_coordinates[function][function_value])) + else: + point_positions[function].append(Vector2(OFFSET.x/2 + column_width/2 + (column_width + column_gap)*function + x_coordinates[function_value]+origin.x,origin.y-y_coordinates[function][function_value])) + point_values[function].append([x_datas[function_value],y_datas[function_value][function]]) + else: + for cluster in y_coordinates.size(): + for y in y_coordinates[cluster].size(): + if are_values_columns: + point_positions[y].append(Vector2(OFFSET.x/2 + column_width/2 + (column_width + column_gap)*y + x_coordinates[cluster] + origin.x, origin.y-y_coordinates[cluster][y])) + point_values[y].append([x_datas[cluster],y_datas[cluster][y]]) + else: + point_values[cluster].append([x_datas[y],y_datas[cluster][y]]) + point_positions[cluster].append(Vector2(OFFSET.x/2 + column_width/2 + (column_width + column_gap)*cluster + x_coordinates[y]+origin.x,origin.y-y_coordinates[cluster][y])) + +func _draw(): + clear_points() + + draw_grid() + draw_chart_outlines() + + var defined_colors : bool = false + if function_colors.size(): + defined_colors = true + + for _function in point_values.size(): + var PointContainer : Control = Control.new() + Points.add_child(PointContainer) + + for function_point in point_values[_function].size(): + var point : Point = point_node.instance() + point.connect("_point_pressed",self,"point_pressed") + point.connect("_mouse_entered",self,"show_data") + point.connect("_mouse_exited",self,"hide_data") + + point.create_point(points_shape[_function], function_colors[function_point if invert_chart else _function], + Color.white, point_positions[_function][function_point] + Vector2(0,7), + point.format_value(point_values[_function][function_point], false, false), + y_labels[function_point if invert_chart else _function] as String) + PointContainer.add_child(point) + point.rect_size.y = origin.y - point_positions[_function][function_point].y + draw_line( Vector2(point_positions[_function][function_point].x, origin.y), + point_positions[_function][function_point], function_colors[_function], column_width, true) +# draw_string(font, Vector2(point_positions[_function][function_point].x, origin.y+10), y_labels[function_point], font_color) + +func draw_grid(): + # ascisse + for p in x_chors.size(): + var point : Vector2 = origin+Vector2((p)*x_pass,0) + # v grid + draw_line(point,point-Vector2(0,SIZE.y-OFFSET.y),v_lines_color,0.2,true) + # ascisse + draw_line(point-Vector2(0,5),point,v_lines_color,1,true) + var calculated_gap : float + if not are_values_columns: + calculated_gap = ( y_datas.size() if not invert_chart else y_datas[0].size()+1 ) + else: + calculated_gap = ( y_datas.size() if invert_chart else y_datas[0].size()+1 ) + draw_string( + font, + point + Vector2(-const_width/2*x_chors[p].length() + (column_width + column_gap) * functions, font_size), + x_chors[p], + font_color) + + # ordinate + for p in y_chors.size(): + var point : Vector2 = origin-Vector2(0,(p)*y_pass) + # h grid + draw_line(point,point+Vector2(SIZE.x-OFFSET.x,0),h_lines_color,0.2,true) + # ordinate + draw_line(point,point+Vector2(5,0),h_lines_color,1,true) + draw_string( + font, + point - Vector2(y_chors[p].length() * const_width + font_size, -const_height), + y_chors[p], + font_color) + +func draw_chart_outlines(): +# if boxed: + draw_line(Vector2(origin.x,0),Vector2(SIZE.x,0),box_color,1,true) + draw_line(Vector2(SIZE.x,0),Vector2(SIZE.x,origin.y),box_color,1,true) + draw_line(Vector2(SIZE.x,origin.y),origin,box_color,1,true) + draw_line(origin,Vector2(origin.x,0),box_color,1,true) diff --git a/addons/easy_charts/LineChart/LineChart.tscn b/addons/easy_charts/LineChart/LineChart.tscn index e964306..b7e9cf1 100644 --- a/addons/easy_charts/LineChart/LineChart.tscn +++ b/addons/easy_charts/LineChart/LineChart.tscn @@ -3,7 +3,6 @@ [ext_resource path="res://addons/easy_charts/Utilities/Point/PointData.tscn" type="PackedScene" id=1] [ext_resource path="res://addons/easy_charts/LineChart/line_chart.gd" type="Script" id=4] - [sub_resource type="Theme" id=1] [node name="LineChart" type="Control"] @@ -29,16 +28,19 @@ Chart_Properties/are_values_columns = false Chart_Properties/labels_index = 0 Chart_Properties/show_x_values_as_labels = true Chart_Display/x_decim = 5.0 -Chart_Display/y_decim = 5.0 +Chart_Display/y_decim = 1.0 Chart_Style/points_shape = [ 0 ] -Chart_Style/function_colors = [ "#1e1e1e", "#1e1e1e", "#1e1e1e", "#1e1e1e" ] +Chart_Style/function_colors = [ Color( 0.117647, 0.117647, 0.117647, 1 ) ] Chart_Style/box_color = Color( 0.117647, 0.117647, 0.117647, 1 ) Chart_Style/v_lines_color = Color( 0.792157, 0.792157, 0.792157, 1 ) Chart_Style/h_lines_color = Color( 0.792157, 0.792157, 0.792157, 1 ) Chart_Style/font = null Chart_Style/bold_font = null Chart_Style/font_color = Color( 0.117647, 0.117647, 0.117647, 1 ) +Chart_Style/use_template = true Chart_Style/template = 0 +Chart_Modifiers/treshold = Vector2( 0, 0 ) +Chart_Modifiers/only_disp_values = Vector2( 0, 0 ) Chart_Modifiers/invert_chart = false [node name="Background" type="ColorRect" parent="."] @@ -51,22 +53,6 @@ __meta__ = { "_edit_use_anchors_": false } -[node name="Points" type="Control" parent="."] -margin_right = 40.0 -margin_bottom = 40.0 -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="Legend" type="HBoxContainer" parent="."] -visible = false -margin_right = 1024.0 -margin_bottom = 64.0 -alignment = 1 -__meta__ = { -"_edit_use_anchors_": false -} - [node name="ChartName" type="Label" parent="."] anchor_right = 1.0 margin_bottom = 14.0 @@ -76,14 +62,29 @@ __meta__ = { "_edit_use_anchors_": false } +[node name="Points" type="Control" parent="."] +anchor_right = 1.0 +anchor_bottom = 1.0 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="Legend" type="HBoxContainer" parent="."] +visible = false +margin_right = 1024.0 +margin_bottom = 64.0 +alignment = 1 +__meta__ = { +"_edit_use_anchors_": false +} + [node name="PointData" parent="." instance=ExtResource( 1 )] [node name="PointData" parent="PointData" index="0"] -visible = false -margin_left = 431.058 -margin_top = 13.8323 -margin_right = 430.922 -margin_bottom = 14.6323 +margin_left = -257.531 +margin_top = -244.08 +margin_right = -257.667 +margin_bottom = -243.28 theme = SubResource( 1 ) [editable path="PointData"] diff --git a/addons/easy_charts/LineChart/line_chart.gd b/addons/easy_charts/LineChart/line_chart.gd index fac5cec..e379471 100644 --- a/addons/easy_charts/LineChart/line_chart.gd +++ b/addons/easy_charts/LineChart/line_chart.gd @@ -18,6 +18,12 @@ class_name LineChart func _get_property_list(): return [ # Chart Properties + { + "hint": PROPERTY_HINT_NONE, + "usage": PROPERTY_USAGE_CATEGORY, + "name": "LineChart", + "type": TYPE_STRING + }, { "hint": PROPERTY_HINT_NONE, "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, @@ -48,7 +54,7 @@ func _get_property_list(): }, { "hint": PROPERTY_HINT_RANGE, - "hint_string": "0.1,100", + "hint_string": "0.001,1,0.001", "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, "name": "Chart_Display/y_decim", "type": TYPE_REAL @@ -112,6 +118,12 @@ func _get_property_list(): "name": "Chart_Style/font_color", "type": TYPE_COLOR }, + { + "hint": PROPERTY_HINT_NONE, + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, + "name": "Chart_Style/use_template", + "type": TYPE_BOOL + }, { "hint": PROPERTY_HINT_ENUM, "hint_string": PoolStringArray(Utilities.templates.keys()).join(","), @@ -121,6 +133,18 @@ func _get_property_list(): }, # Chart Modifiers + { + "hint": PROPERTY_HINT_NONE, + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, + "name": "Chart_Modifiers/treshold", + "type": TYPE_VECTOR2 + }, + { + "hint": PROPERTY_HINT_NONE, + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, + "name": "Chart_Modifiers/only_disp_values", + "type": TYPE_VECTOR2 + }, { "hint": PROPERTY_HINT_NONE, "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, @@ -173,7 +197,7 @@ func structure_datas(database: Array, are_values_columns: bool, x_values_index: var to_order_min: Array for cluster in y_datas.size(): # define x_chors and y_chors - var ordered_cluster = y_datas[cluster] as Array + var ordered_cluster = y_datas[cluster].duplicate() as Array ordered_cluster.sort() var margin_max = ordered_cluster[ordered_cluster.size() - 1] var margin_min = ordered_cluster[0] @@ -185,7 +209,7 @@ func structure_datas(database: Array, are_values_columns: bool, x_values_index: var margin = to_order.pop_back() if not origin_at_zero: y_margin_min = to_order_min.pop_front() - v_dist = y_decim * pow(10.0, (str(margin).length() - 2 if typeof(margin) == TYPE_INT else str(margin).length() - 4 )) + v_dist = y_decim * pow(10.0, str(margin).length()-1) #* pow(10.0, (str(margin).length() - 2 if typeof(margin) == TYPE_INT else str(margin).length() - str(y_decim).length() )) var multi = 0 var p = (v_dist * multi) + ((y_margin_min) if not origin_at_zero else 0) y_chors.append(p as String) @@ -197,8 +221,7 @@ func structure_datas(database: Array, are_values_columns: bool, x_values_index: # draw x_labels if not show_x_values_as_labels: to_order.clear() - to_order = x_datas as PoolIntArray - + to_order = x_datas.duplicate() as PoolIntArray to_order.sort() margin = to_order.pop_back() if not origin_at_zero: @@ -211,10 +234,12 @@ func structure_datas(database: Array, are_values_columns: bool, x_values_index: multi += 1 p = (h_dist * multi) + ((x_margin_min) if not origin_at_zero else 0) x_labels.append(p as String) - + + OFFSET.x = (str(margin).length()) * font_size + OFFSET.y = font_size * 2 func build_chart(): - SIZE = get_size() + SIZE = get_size() - Vector2(OFFSET.x,0) origin = Vector2(OFFSET.x, SIZE.y - OFFSET.y) @@ -229,7 +254,7 @@ func calculate_pass(): # calculate distance in pixel between 2 consecutive values/datas x_pass = (SIZE.x - OFFSET.x) / (x_chors.size()-1 if x_chors.size()>1 else x_chors.size() ) - y_pass = origin.y / (y_chors.size() - 1) + y_pass = (origin.y - ChartName.get_rect().size.y*2) / (y_chors.size() - 1) func calculate_coordinates(): @@ -302,36 +327,28 @@ func calculate_coordinates(): x_coordinates[y] + origin.x, origin.y - y_coordinates[cluster][y])) - func _draw(): clear_points() - draw_grid() draw_chart_outlines() - + var defined_colors: bool = false if function_colors.size(): defined_colors = true - + + if Points.get_child_count() > 0 : + for point in Points.get_children(): + point.queue_free() + for _function in point_values.size(): - var point_container: Control = Control.new() - Points.add_child(point_container) - for function_point in point_values[_function].size(): var point: Point = point_node.instance() point.connect("_point_pressed", self, "point_pressed") point.connect("_mouse_entered", self, "show_data") point.connect("_mouse_exited", self, "hide_data") + point.create_point(points_shape[_function],function_colors[function_point if invert_chart else _function],Color.white,point_positions[_function][function_point],point.format_value(point_values[_function][function_point], false, false),y_labels[function_point if invert_chart else _function] as String) - point.create_point( - points_shape[_function], - function_colors[function_point if invert_chart else _function], - Color.white, - point_positions[_function][function_point], - point.format_value(point_values[_function][function_point], false, false), - y_labels[function_point if invert_chart else _function] as String) - - point_container.add_child(point) + Points.add_child(point) if function_point > 0: draw_line( point_positions[_function][function_point - 1], @@ -339,7 +356,7 @@ func _draw(): function_colors[function_point if invert_chart else _function], 2, false) - + draw_treshold() func draw_grid(): # ascisse @@ -368,9 +385,16 @@ func draw_grid(): y_chors[p], font_color) - func draw_chart_outlines(): draw_line(origin, SIZE - Vector2(0, OFFSET.y), box_color, 1, true) draw_line(origin, Vector2(OFFSET.x, 0), box_color, 1, true) draw_line(Vector2(OFFSET.x, 0), Vector2(SIZE.x, 0), box_color, 1, true) draw_line(Vector2(SIZE.x, 0), SIZE - Vector2(0, OFFSET.y), box_color, 1, true) + +func draw_treshold(): + if v_dist != 0: + treshold_draw = Vector2((treshold.x * x_pass) + origin.x ,origin.y - ((treshold.y * y_pass)/v_dist)) + if treshold.y != 0: + draw_line(Vector2(origin.x, treshold_draw.y), Vector2(SIZE.x, treshold_draw.y), Color.red, 0.4, true) + if treshold.x != 0: + draw_line(Vector2(treshold_draw.x, 0), Vector2(treshold_draw.x, SIZE.y - OFFSET.y), Color.red, 0.4, true) From 6986840501c5e2d9cf4b551e292c3bd3cfb4959e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Santilio?= Date: Wed, 30 Dec 2020 15:31:09 +0100 Subject: [PATCH 4/8] Delete BarChart.gd --- addons/easy_charts/BarChart/BarChart.gd | 391 ------------------------ 1 file changed, 391 deletions(-) delete mode 100644 addons/easy_charts/BarChart/BarChart.gd diff --git a/addons/easy_charts/BarChart/BarChart.gd b/addons/easy_charts/BarChart/BarChart.gd deleted file mode 100644 index 80ac306..0000000 --- a/addons/easy_charts/BarChart/BarChart.gd +++ /dev/null @@ -1,391 +0,0 @@ -tool -extends Chart -class_name BarChart - -""" -[BarChart] - General purpose node for Bar Charts - -A bar chart or bar graph is a chart or graph that presents categorical data with -rectangular bars with heights or lengths proportional to the values that they represent. -The bars can be plotted vertically or horizontally. A vertical bar chart is sometimes -called a column chart. -A bar graph shows comparisons among discrete categories. One axis of the chart shows -the specific categories being compared, and the other axis represents a measured value. -Some bar graphs present bars clustered in groups of more than one, showing the -values of more than one measured variable. - -/ source : Wikipedia / -""" - -# --------------------- - -func _get_property_list(): - return [ - # Chart Properties - { - "hint": PROPERTY_HINT_NONE, - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Properties/are_values_columns", - "type": TYPE_BOOL - }, - { - "hint": PROPERTY_HINT_RANGE, - "hint_string": "-1,100,1", - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Properties/labels_index", - "type": TYPE_INT - }, - { - "hint": PROPERTY_HINT_NONE, - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Properties/show_x_values_as_labels", - "type": TYPE_BOOL - }, - { - "hint": PROPERTY_HINT_RANGE, - "hint_string": "1,20,0.5", - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Properties/column_width", - "type": TYPE_REAL - }, - { - "hint": PROPERTY_HINT_RANGE, - "hint_string": "0,10,0.5", - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Properties/column_gap", - "type": TYPE_REAL - }, - - # Chart Display - { - "hint": PROPERTY_HINT_RANGE, - "hint_string": "0.1,10", - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Display/x_decim", - "type": TYPE_REAL - }, - { - "hint": PROPERTY_HINT_RANGE, - "hint_string": "0.1,10", - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Display/y_decim", - "type": TYPE_REAL - }, - - # Chart Style - { - "hint": 24, - "hint_string": "%d/%d:%s"%[TYPE_INT, PROPERTY_HINT_ENUM, - PoolStringArray(PointShapes.keys()).join(",")], - "name": "Chart_Style/points_shape", - "type": TYPE_ARRAY, - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE - }, - { - "hint": PROPERTY_HINT_NONE, - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Style/function_colors", - "type": TYPE_COLOR_ARRAY - }, - { - "hint": PROPERTY_HINT_NONE, - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Style/box_color", - "type": TYPE_COLOR - }, - { - "hint": PROPERTY_HINT_NONE, - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Style/v_lines_color", - "type": TYPE_COLOR - }, - { - "hint": PROPERTY_HINT_NONE, - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Style/h_lines_color", - "type": TYPE_COLOR - }, - { - "class_name": "Font", - "hint": PROPERTY_HINT_RESOURCE_TYPE, - "hint_string": "Font", - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Style/font", - "type": TYPE_OBJECT - }, - { - "class_name": "Font", - "hint": PROPERTY_HINT_RESOURCE_TYPE, - "hint_string": "Font", - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Style/bold_font", - "type": TYPE_OBJECT - }, - { - "hint": PROPERTY_HINT_NONE, - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Style/font_color", - "type": TYPE_COLOR - }, - { - "hint": PROPERTY_HINT_ENUM, - "hint_string": PoolStringArray(Utilities.templatess.keys()).join(","), - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Style/template", - "type": TYPE_INT - }, - - # Chart Modifiers - { - "hint": PROPERTY_HINT_NONE, - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Modifiers/invert_chart", - "type": TYPE_BOOL - }, - ] - -func build_chart(): - SIZE = get_size() - origin = Vector2(OFFSET.x,SIZE.y-OFFSET.y) - -func structure_datas(database : Array, are_values_columns : bool, x_values_index : int): - # @x_values_index can be either a column or a row relative to x values - # @y_values can be either a column or a row relative to y values - self.are_values_columns = are_values_columns - match are_values_columns: - true: - for row in database.size(): - var t_vals : Array - for column in database[row].size(): - if column == x_values_index: - var x_data = database[row][column] - if x_data.is_valid_float() or x_data.is_valid_integer(): - x_datas.append(x_data as float) - else: - x_datas.append(x_data.replace(",",".") as float) - else: - if row != 0: - var y_data = database[row][column] - if y_data.is_valid_float() or y_data.is_valid_integer(): - t_vals.append(y_data as float) - else: - t_vals.append(y_data.replace(",",".") as float) - else: - y_labels.append(str(database[row][column])) - if not t_vals.empty(): - y_datas.append(t_vals) - x_label = str(x_datas.pop_front()) - false: - for row in database.size(): - if row == x_values_index: - x_datas = (database[row]) - x_label = x_datas.pop_front() as String - else: - var values = database[row] as Array - y_labels.append(values.pop_front() as String) - y_datas.append(values) - for data in y_datas: - for value in data.size(): - data[value] = data[value] as float - - # draw y labels - var to_order : Array - var to_order_min : Array - for cluster in y_datas.size(): - # define x_chors and y_chors - var ordered_cluster = y_datas[cluster] as Array - ordered_cluster.sort() - ordered_cluster = PoolIntArray(ordered_cluster) - var margin_max = ordered_cluster[ordered_cluster.size()-1] - var margin_min = ordered_cluster[0] - to_order.append(margin_max) - to_order_min.append(margin_min) - - to_order.sort() - to_order_min.sort() - var margin = to_order.pop_back() - if not origin_at_zero: - y_margin_min = to_order_min.pop_front() - v_dist = y_decim * pow(10.0,str(margin).length()-2) - var multi = 0 - var p = (v_dist*multi) + ((y_margin_min) if not origin_at_zero else 0) - y_chors.append(p as String) - while p < margin: - multi+=1 - p = (v_dist*multi) + ((y_margin_min) if not origin_at_zero else 0) - y_chors.append(p as String) - - # draw x_labels - if not show_x_values_as_labels: - to_order.clear() - to_order = x_datas as PoolIntArray - - to_order.sort() - margin = to_order.pop_back() - if not origin_at_zero: - x_margin_min = to_order.pop_front() - h_dist = x_decim * pow(10.0,str(margin).length()-2) - multi = 0 - p = (h_dist*multi) + ((x_margin_min) if not origin_at_zero else 0) - x_labels.append(p as String) - while p < margin: - multi+=1 - p = (h_dist*multi) + ((x_margin_min) if not origin_at_zero else 0) - x_labels.append(p as String) - - -func calculate_pass(): - if invert_chart: - x_chors = y_labels as PoolStringArray - else: - if show_x_values_as_labels: - x_chors = x_datas as PoolStringArray - else: - x_chors = x_labels - - # calculate distance in pixel between 2 consecutive values/datas - if not are_values_columns: - x_pass = (SIZE.x - OFFSET.x*2 - (column_width) * ( y_datas.size() if not invert_chart else y_datas[0].size()+1 ) - column_gap - column_width/2) / ((x_chors.size()-1) if x_chors.size()!=1 else 1) - else: - x_pass = (SIZE.x - OFFSET.x*2 - (column_width) * ( y_datas.size() if invert_chart else y_datas[0].size()+1 ) - column_gap - column_width/2) / (x_chors.size()-1) - y_pass = origin.y / (y_chors.size()-1) - -func calculate_coordinates(): - x_coordinates.clear() - y_coordinates.clear() - point_values.clear() - point_positions.clear() - - if invert_chart: - for column in y_datas[0].size(): - var single_coordinates : Array - for row in y_datas: - if origin_at_zero: - single_coordinates.append((row[column]*y_pass)/v_dist) - else: - single_coordinates.append((row[column] - y_margin_min)*y_pass/v_dist) - y_coordinates.append(single_coordinates) - else: - for cluster in y_datas: - var single_coordinates : Array - for value in cluster.size(): - if origin_at_zero: - single_coordinates.append((cluster[value]*y_pass)/v_dist) - else: - single_coordinates.append((cluster[value] - y_margin_min)*y_pass/v_dist) - y_coordinates.append(single_coordinates) - - if show_x_values_as_labels: - for x in x_datas.size(): - x_coordinates.append(x_pass*x) - else: - for x in x_datas.size(): - if origin_at_zero: - if not invert_chart: - x_coordinates.append(x_pass*x) - else: - x_coordinates.append(x*x_pass/h_dist) - else: - x_coordinates.append((x_datas[x] - x_margin_min)*x_pass/h_dist) - - for f in functions: - point_values.append([]) - point_positions.append([]) - - if invert_chart: - for function in y_coordinates.size(): - for function_value in y_coordinates[function].size(): - if are_values_columns: - point_values[function].append([x_datas[function_value],y_datas[function_value][function]]) - point_positions[function].append(Vector2(OFFSET.x/2 + column_width/2 + (column_width + column_gap)*function + x_coordinates[function_value]+origin.x,origin.y-y_coordinates[function][function_value])) - else: - point_positions[function].append(Vector2(OFFSET.x/2 + column_width/2 + (column_width + column_gap)*function + x_coordinates[function_value]+origin.x,origin.y-y_coordinates[function][function_value])) - point_values[function].append([x_datas[function_value],y_datas[function_value][function]]) - else: - for cluster in y_coordinates.size(): - for y in y_coordinates[cluster].size(): - if are_values_columns: - point_positions[y].append(Vector2(OFFSET.x/2 + column_width/2 + (column_width + column_gap)*y + x_coordinates[cluster] + origin.x, origin.y-y_coordinates[cluster][y])) - point_values[y].append([x_datas[cluster],y_datas[cluster][y]]) - else: - point_values[cluster].append([x_datas[y],y_datas[cluster][y]]) - point_positions[cluster].append(Vector2(OFFSET.x/2 + column_width/2 + (column_width + column_gap)*cluster + x_coordinates[y]+origin.x,origin.y-y_coordinates[cluster][y])) - -func _draw(): - clear_points() - - draw_grid() - draw_chart_outlines() - - var defined_colors : bool = false - if function_colors.size(): - defined_colors = true - - for _function in point_values.size(): - var PointContainer : Control = Control.new() - Points.add_child(PointContainer) - - for function_point in point_values[_function].size(): - var point : Point = point_node.instance() - point.connect("_point_pressed",self,"point_pressed") - point.connect("_mouse_entered",self,"show_data") - point.connect("_mouse_exited",self,"hide_data") - - point.create_point(points_shape[_function], function_colors[function_point if invert_chart else _function], - Color.white, point_positions[_function][function_point], - point.format_value(point_values[_function][function_point], false, false), - y_labels[function_point if invert_chart else _function] as String) - PointContainer.add_child(point) - point.rect_size.y = origin.y - point_positions[_function][function_point].y - draw_line( Vector2(point_positions[_function][function_point].x, origin.y), - point_positions[_function][function_point], function_colors[_function], column_width, true) -# draw_string(font, Vector2(point_positions[_function][function_point].x, origin.y+10), y_labels[function_point], font_color) - -func draw_grid(): - # ascisse - for p in x_chors.size(): - var point : Vector2 = origin+Vector2((p)*x_pass,0) - # v grid - draw_line(point,point-Vector2(0,SIZE.y-OFFSET.y),v_lines_color,0.2,true) - # ascisse - draw_line(point-Vector2(0,5),point,v_lines_color,1,true) - var calculated_gap : float - if not are_values_columns: - calculated_gap = ( y_datas.size() if not invert_chart else y_datas[0].size()+1 ) - else: - calculated_gap = ( y_datas.size() if invert_chart else y_datas[0].size()+1 ) - draw_string(font,point+Vector2(-const_width/2*x_chors[p].length() + (column_width/2) * calculated_gap + column_gap,font_size),x_chors[p],font_color) - - # ordinate - for p in y_chors.size(): - var point : Vector2 = origin-Vector2(0,(p)*y_pass) - # h grid - draw_line(point,point+Vector2(SIZE.x-OFFSET.x,0),h_lines_color,0.2,true) - # ordinate - draw_line(point,point+Vector2(5,0),h_lines_color,1,true) - draw_string(font,point-Vector2(y_chors[p].length()*const_width+font_size,font_size/2),y_chors[p],font_color) - -func draw_chart_outlines(): -# if boxed: - draw_line(Vector2(origin.x,0),Vector2(SIZE.x,0),box_color,1,true) - draw_line(Vector2(SIZE.x,0),Vector2(SIZE.x,origin.y),box_color,1,true) - draw_line(Vector2(SIZE.x,origin.y),origin,box_color,1,true) - draw_line(origin,Vector2(origin.x,0),box_color,1,true) - -func create_legend(): - legend.clear() - for function in functions: - var function_legend = LegendElement.instance() - var f_name : String - if invert_chart: - f_name = x_datas[function] as String - else: - f_name = y_labels[function] - var legend_font : Font - if font != null: - legend_font = font - if bold_font != null: - legend_font = bold_font - function_legend.create_legend(f_name,function_colors[function],bold_font,font_color) - legend.append(function_legend) - From eecf010218f5b3297b42bf9b2fd7a4ed70979f30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Santilio?= Date: Wed, 30 Dec 2020 15:33:45 +0100 Subject: [PATCH 5/8] Add files via upload From 757e99aaf96af1951a0838e23a86f32a6aa927ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Santilio?= Date: Wed, 30 Dec 2020 15:34:13 +0100 Subject: [PATCH 6/8] Add files via upload --- addons/easy_charts/plugin.gd | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/addons/easy_charts/plugin.gd b/addons/easy_charts/plugin.gd index 09bc947..1c636e2 100644 --- a/addons/easy_charts/plugin.gd +++ b/addons/easy_charts/plugin.gd @@ -3,12 +3,10 @@ extends EditorPlugin func _enter_tree(): add_autoload_singleton("Utilities","res://addons/easy_charts/Utilities/Scripts/utilities.gd") -# add_custom_type("ChartObject","Node", load("res://addons/easy_charts/Utilities/Scripts/ChartObject.gd"), preload("Utilities/icons/linechart.svg")) - add_custom_type("Chart","Control", load("res://addons/easy_charts/Utilities/Scripts/Chart.gd"), preload("Utilities/icons/linechart.svg")) - add_custom_type("Chart2D","Node2D", preload("Utilities/Scripts/Chart2D.gd"), preload("Utilities/icons/linechart2d.svg")) + add_custom_type("Chart","Control", load("res://addons/easy_charts/Utilities/Scripts/chart.gd"), preload("Utilities/icons/linechart.svg")) + add_custom_type("Chart2D","Node2D", preload("Utilities/Scripts/chart2d.gd"), preload("Utilities/icons/linechart2d.svg")) func _exit_tree(): remove_custom_type("Chart") remove_custom_type("Chart2D") -# remove_custom_type("ChartObject") remove_autoload_singleton("Utilities") From 7dbb37995def565b984b3bc702b7005b8172910f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Santilio?= Date: Wed, 30 Dec 2020 15:34:31 +0100 Subject: [PATCH 7/8] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7e65cc4..f2b5402 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ A library of Charts plotted in Control, 2D and 3D nodes to visualize general purpose datasets. Author: *"Nicolo (fenix) Santilio"* -Version: *0.4.5* +Version: *0.5.0* Wiki: *[wip](https://github.com/fenix-hub/godot-engine.easy-charts/wiki)* Godot Version: *3.2stable* From 4ddbac132964f19ccd8d7092340eee7b59443217 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Santilio?= Date: Wed, 30 Dec 2020 15:36:19 +0100 Subject: [PATCH 8/8] Update README.md --- README.md | 99 ++----------------------------------------------------- 1 file changed, 3 insertions(+), 96 deletions(-) diff --git a/README.md b/README.md index f2b5402..df57798 100644 --- a/README.md +++ b/README.md @@ -27,102 +27,9 @@ If you need to plot a chart with some values in it and just take a screenshot, o ## How does it work? -*Easy Charts* contains a collection of nodes for each main node in Godot: Control Nodes, 2D Nodes and 3D Spatials. -To plot a chart you only need to: -1. Save in your project a .CSV file containing the table of values you want to represent, just like the following one: -![example1](imgs/EXCEL_mWtvuI90D0.png) -or, of course, the inverted one: -![example2](imgs/EXCEL_Fa2iiie9qC.png) - -2. In Godot, choose a Chart you'd like to plot. For istance, let's take a BarChart2D, specifically used to plot chart in a Node2D. You can instance it as a Child Scene or drag it from the Explorer: -![example3](imgs/scene1.png) -![example4](imgs/scene2.png) - -3. Once in the tree, move it around as a normal Node, and set it's values directly in editor, like so: -![example5](imgs/editor_gif.gif) -You can directly select the file you want to plot from the editor, and change its values as you prefer. Most of the features will be displayed in real time in Editor while you are editing them. - -4. With just one line of code, you will be able to plot the chart. Use the line of code `$BarChart2D.plot()` to plot the chart with the properties edited in the editor. -![code1](imgs/code.png) -Running the project like this, will produce this chart: -![example6](imgs/chart_gif.gif) - -5. Moving the cursor around you will be able to see a floating box with the values contained in the chart: -![example7](imgs/values.gif) - -## Chart Tools -Chart Tools are a set of utilities parallel to the plot itself. -![signals](imgs/signals.png) -Available Chart Tools are: -- *Legend Output*: once a chart is plotted, you can get an output legend connecting the right signal, or just using custom functions. A legend is a simple `Array` of custom Control nodes, which contains informations about the chart for each function plotted. -Once plotted, the chart will emit a custom `chart_plotted( chart )` signal. To get the legend, you just can connect this signal, or use the `get_legend` functions afterwards. A `legend` variable can then be used to print out or represent in a full customizable way the legend of functions. For example, these lines of codes: -![code1](imgs/legend_code.png) -![tree1](imgs/tree_legend.png) -You can get this result: -![debug](imgs/debug_legend.png) -![editor2](imgs/editor_legend.png) - -- *Point Value*: as you can see, each chart is pretty much interactable, giving you the opportunity to visualize the point represented with hovering it with your mouse. But what if you want to use its values for your own purposes? No problem, with the use of the signal `point_pressed ( point : Point )` you will be able to get all the informations about a point you clicked on in the chart. -![codep](imgs/code_point.png) -![debug1](imgs/debug_point.png) - -### Table of features - -**Chart** -| Function | Description | -| ------------- | ------------- | -|`plot()`|Plot the selected chart with in editor properties| -|`plot_from_csv(csv_file : String, _delimiter : String = delimiter)`|Plot the selected chart defining the source and delimiter from code| -|`plot_from_array(array : Array)`|Plot the selected chart giving an Array of values as a source| -|`update_plot_data(array : Array)`|Update plot data giving an additional entry to the source| -|`get_legend()`|Get the legend of the current chart. Returns an **Array** containing control nodes, which can be added as children | -|*API v2*|*additional functions to define chart's properties from code. Hardcoded properties will overwrite the ones given from editor*| -|`set_chart_name(ch_name : String)`|Set the displayed name of the chart| -|`set_source(source_file : String)`|Set the source of the chart to plot| -|`set_delimiter(delimiter : String)`|Set the delimiter to read data from a CSV file| -|`set_indexes(lb : int = 0, function_names : int = 0)`|Set the optional indexes of chart's data| -|`set_radius(use_height : bool = false, f : float = 0)`|Set the radius of the chart, if it is required. If `use_height` is true, the height of the chart will be used.| -|`set_chart_colors(f_colors : PoolColorArray, o_color : Color, b_color : Color, g_color : Color, h_lines : Color, v_lines : Color)`|Set the colors of the chart| -|`set_chart_fonts(normal_font : Font, bold_font : Font, f_color : Color = Color.white)`|Set fonts and font's color for the chart| -|`set_source(source_file : String)`|Set the source of the chart to plot| - -| Signals | Description | -| ------------- | ------------- | -|`chart_plotted(chart : Node)`|Returns the plotted Chart, which is a Node (Control, 2D Node, 3D Node)| -|`point_pressed(point : Point)`|Returns the point pressed with Left Click, which is a custom class *Point*| - - -**Legend** -| Function | Description | -| ------------- | ------------- | -|`get_function() -> Array`|Get the function's name, also visible in legend| -|`get_function_color() -> Color`|Get the function's color, also visible in legend| - - -**Point** -| Function | Description | -| ------------- | ------------- | -|`get_value() -> Array`|Get the point values, which are mapped coordinates in the plot. Returns a 2D array, with [0] and [1] as x and y values| -|`get_function() -> String`|Get the point's function name, also visible in legend and chart| -|`get_color_point() -> Color`|Get the point Color, also visible in legend and chart| - - -**About templates** -Templates are defined in a .json file, and the format is pretty straight forward. -However, adding custom templates is not yet recommended, since it would require to edit the Charts scripts. Templates are anyway accessible and customizable. -``` -/addons/easy_charts/templates.json - -"default": -{ -"function_colors" : ["#1e1e1e","#1e1e1e","#1e1e1e","#1e1e1e"], -"v_lines_color" : "#cacaca", -"h_lines_color" : "#cacaca", -"outline_color" : "#1e1e1e", -"font_color" : "#1e1e1e" -}, -``` - +There is a [WIKI](https://github.com/fenix-hub/godot-engine.easy-charts/wiki) with some tutorials, even if it is a work in progress. +I'll make some videos as soon as possible. + # Available Charts and when to use them This library offers a set of charts for each main Godot Node: - **Control Nodes:** "Control Charts" are fast Charts that can be plotted in a Control space, such as UIs or Control user interactable areas. They offer basic Control properties, such as Margins, size inheritance and control. No animations, no real time changes, just charts.