From ef76c04470394360eac9f3e3227b712726342a88 Mon Sep 17 00:00:00 2001 From: fenix-hub Date: Mon, 10 Jan 2022 00:01:10 +0100 Subject: [PATCH] update scatter_chart_base --- .../control_charts/LineChart/line_chart.tscn | 11 +- .../utilities/classes/base/chart.gd | 6 +- .../classes/base/scatter_chart_base.gd | 111 ++++++++++-------- .../classes/structures/data_frame.gd | 13 +- .../utilities/classes/structures/matrix.gd | 23 +++- 5 files changed, 102 insertions(+), 62 deletions(-) diff --git a/addons/easy_charts/control_charts/LineChart/line_chart.tscn b/addons/easy_charts/control_charts/LineChart/line_chart.tscn index f3f1d3a..73554e9 100644 --- a/addons/easy_charts/control_charts/LineChart/line_chart.tscn +++ b/addons/easy_charts/control_charts/LineChart/line_chart.tscn @@ -26,11 +26,11 @@ In these cases they are known as run charts." } Chart_Properties/are_values_columns = false Chart_Properties/labels_index = 0 -Chart_Properties/show_x_values_as_labels = false Chart_Display/autoscale_x = true Chart_Display/x_decim = 1.0 Chart_Display/autoscale_y = true Chart_Display/y_decim = 1.0 +Chart_Display/show_points = true 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/function_line_width = 2 @@ -46,7 +46,6 @@ Chart_Style/template = 0 Chart_Modifiers/treshold = Vector2( 0, 0 ) Chart_Modifiers/only_disp_values = Vector2( 0, 0 ) Chart_Modifiers/invert_chart = false -Chart_Display/show_points = true [node name="Background" type="ColorRect" parent="."] visible = false @@ -86,10 +85,10 @@ __meta__ = { [node name="CanvasLayer" type="CanvasLayer" parent="."] [node name="DataTooltip" parent="CanvasLayer" instance=ExtResource( 1 )] -margin_left = -50.9019 -margin_top = -37.7724 -margin_right = -51.0379 -margin_bottom = -36.9724 +margin_left = -124.653 +margin_top = -56.7206 +margin_right = -124.789 +margin_bottom = -55.9206 [node name="PointData" parent="CanvasLayer/DataTooltip" index="0"] margin_left = -189.809 diff --git a/addons/easy_charts/utilities/classes/base/chart.gd b/addons/easy_charts/utilities/classes/base/chart.gd index a77c872..436a1d2 100644 --- a/addons/easy_charts/utilities/classes/base/chart.gd +++ b/addons/easy_charts/utilities/classes/base/chart.gd @@ -317,6 +317,9 @@ func _set(property, value): func _init(): build_property_list() +func _ready(): + load_font() + # .......................... Shared Functions and virtuals ........................ # Structure and Display a new plot if a dataset source is given @@ -324,7 +327,6 @@ func _init(): func plot(_dataset: Array = read_data(source, delimiter)) -> void: clean_variables() clean_points() - load_font() data_tooltip.hide() if _dataset.empty(): @@ -418,8 +420,10 @@ func display_plot(): calculate_coordinates() func redraw_plot(): + data_tooltip.hide() clean_points() display_plot() + update() # ................................. Helper Functions ................................. diff --git a/addons/easy_charts/utilities/classes/base/scatter_chart_base.gd b/addons/easy_charts/utilities/classes/base/scatter_chart_base.gd index 5152073..205d7b0 100644 --- a/addons/easy_charts/utilities/classes/base/scatter_chart_base.gd +++ b/addons/easy_charts/utilities/classes/base/scatter_chart_base.gd @@ -5,6 +5,8 @@ class_name ScatterChartBase # of points in a two-variable space. It handles basic data structure and grid # layout and leaves to child classes the more specific behaviour. +var x_values := [] +var y_values := [] #Stored in the form of [[min_func1, min_func2, min_func3, ...], [max_func1, max_func2, ...]] var x_domain := [[], []] var y_domain := [[], []] @@ -272,14 +274,11 @@ func _get(property): "Chart_Display/max_y_range": return y_range[1] -func plot_function(x:Array, y:Array, param_dic := {}): +func plot_function(id: String, x:Array, y:Array, param_dic := {}): # Add a function to the chart. If no identifier (label) is given a generic one # is generated. # param_dic is a dictionary with specific parameters to this curve - data_tooltip.hide() - var id := "" - if x.empty() or y.empty(): ECUtilities._print_message("Can't plot a chart with an empty Array.",1) return @@ -287,16 +286,6 @@ func plot_function(x:Array, y:Array, param_dic := {}): ECUtilities._print_message("Can't plot a chart with x and y having different number of elements.",1) return - for param in param_dic.keys(): - match param: - "label": - id = param_dic[param] - "color": - if function_colors.size() < functions + 1: #There is going to be a new function - function_colors.append(param_dic[param]) - else: - function_colors[functions] = param_dic[param] - id = generate_identifier() if id.empty() else id if y_labels.has(id): @@ -308,15 +297,33 @@ func plot_function(x:Array, y:Array, param_dic := {}): x_domain[0].append(null) x_domain[1].append(null) - x_datas.append(x) - y_datas.append(y) - y_labels.append(id) + x_values.append_array(x) + y_values = y + + populate_x_datas() + populate_y_datas() calculate_range(id) - plot() + calculate_tics() + redraw_plot() + update() +func update_functions(new_x, new_y : Array, param_dic : = {}) : + x_values.append(new_x) + for function in y_labels.size(): + _update_function(y_labels[function], new_y[function]) + calculate_tics() + redraw_plot() + update() -func update_function(id:String, x:Array, y:Array, param_dic := {}): +func update_function(id: String, new_x, new_y, param_dic := {}) -> void: + x_values.append(new_x) + _update_function(id, new_y, param_dic) + calculate_tics() + redraw_plot() + update() + +func _update_function(id: String, new_y, param_dic := {}): var function = y_labels.find(id) if function == -1: #Not found @@ -330,22 +337,20 @@ func update_function(id:String, x:Array, y:Array, param_dic := {}): "color": function_colors[function] = param_dic[param] - x_datas[function] = x - y_datas[function] = y + for y_data_i in range(0, y_datas.size()): + if y_data_i == function: y_datas[y_data_i].append(new_y) + else: y_datas[y_data_i].append(y_datas[y_data_i][y_datas[y_data_i].size()-1]) calculate_range(id) - plot() - update() - -func delete_function(id:String): +func delete_function(id: String): var function = y_labels.find(id) if function == -1: #Not found ECUtilities._print_message("The identifier %s does not exist." % id,1) return - y_labels.remove(function) +# y_labels.remove(function) x_datas.remove(function) y_datas.remove(function) y_domain[0].remove(function) @@ -354,14 +359,25 @@ func delete_function(id:String): x_domain[1].remove(function) function_colors.remove(function) - plot() + calculate_tics() + redraw_plot() update() - func generate_identifier(): #TODO: Check if the identifier generated already exist (given by the user) return "f%d" % (y_labels.size() + 1) +func populate_x_datas(): + x_labels = x_values + x_datas.append(x_values) + +func populate_y_datas(): + y_labels.append(y_values.duplicate(true).pop_front() as String) + + for val in y_values.size(): + y_values[val] = y_values[val] as float + + y_datas.append(y_values) func structure_data(database : Array): # @labels_index can be either a column or a row relative to x values @@ -371,8 +387,6 @@ func structure_data(database : Array): #This function is called from the "old" methods such as plot_from_array and #for the moment it doesn't clean this variables on clean_variable. - var x_values := [] - var y_values := [] x_domain = [[], []] y_domain = [[], []] @@ -380,22 +394,11 @@ func structure_data(database : Array): if database_size.has(labels_index): x_values = database[labels_index] x_label = x_values.pop_front() as String - x_labels = x_values database_size.erase(labels_index) #Remove x row from the iterator - for row in database_size: - y_values = database[row] as Array - y_labels.append(y_values.pop_front() as String) - - for val in y_values.size(): - y_values[val] = y_values[val] as float - - y_datas.append(y_values) - - for x_value in x_values: - if str(x_value).is_valid_float(): - x_datas.append(x_values if not x_values.empty() else range(y_values.size())) - else: - x_datas.append(x_labels) + for size in database_size: + populate_x_datas() + y_values = database[size] + populate_y_datas() for function in y_labels: y_domain[0].append(null) @@ -449,13 +452,13 @@ func calculate_tics(): calculate_interval_tics(y_margin_min, y_margin_max, v_dist, y_chors) for i in y_chors.size(): y_chors[i] = String(y_chors[i]) #Can't cast directly on calculate_interval_tics because it mess up with the sorting - x_chors = x_labels + x_chors = x_labels.duplicate(true) func build_chart(): var longest_y_tic = 0 for y_tic in y_chors: - var length = font.get_string_size(y_tic).x + var length = font.get_string_size(str(y_tic)).x if length > longest_y_tic: longest_y_tic = length @@ -489,7 +492,7 @@ func calculate_coordinates(): for function in y_labels.size(): for val in x_datas[function].size(): var value_x = (int(x_datas[function][val]) - x_margin_min) * x_pass / h_dist if h_dist else \ - x_chors.find(String(x_datas[function][val])) * x_pass + val * x_pass var value_y = (y_datas[function][val] - y_margin_min) * y_pass / v_dist if v_dist else 0 point_values[function].append([x_datas[function][val], y_datas[function][val]]) @@ -500,13 +503,13 @@ func draw_grid(): # ascisse for p in x_chors.size(): var point : Vector2 = origin + Vector2(p * x_pass, 0) - var size_text : Vector2 = font.get_string_size(x_chors[p]) + var size_text : Vector2 = font.get_string_size(str(x_chors[p])) # v grid draw_line(point, point - Vector2(0, SIZE.y - OFFSET.y), v_lines_color, grid_lines_width, true) # ascisse draw_line(point + Vector2(0, tic_length), point, v_lines_color, grid_lines_width, true) draw_string(font, point + Vector2(-size_text.x / 2, size_text.y + tic_length), - x_chors[p], font_color) + str(x_chors[p]), font_color) # ordinate for p in y_chors.size(): @@ -578,3 +581,11 @@ func calculate_interval_tics(v_from:float, v_to:float, dist:float, chords:Array, p = (dist * multi) + v_from missing_tics = p < v_to if dist > 0 else p > v_to chords.append(p) + + +func _to_string() -> String: + return \ + "X DATA: %s\n" % str(x_datas) + \ + "Y DATA: %s\n" % str(y_datas) + \ + "X LABELS: %s\n" % str(x_labels) + \ + "Y LABELS: %s\n" % str(y_labels) diff --git a/addons/easy_charts/utilities/classes/structures/data_frame.gd b/addons/easy_charts/utilities/classes/structures/data_frame.gd index 4d001d6..1ea6dcb 100644 --- a/addons/easy_charts/utilities/classes/structures/data_frame.gd +++ b/addons/easy_charts/utilities/classes/structures/data_frame.gd @@ -9,8 +9,9 @@ var datamatrix : Matrix = null var dataset : Array = [] func _init(datamatrix : Matrix, headers : PoolStringArray = [], labels : PoolStringArray = [] , table_name : String = "") -> void: - if labels.empty() : for label in range(datamatrix.get_size().x) : labels.append(label as String) if headers.empty() : for header in range(datamatrix.get_size().y) : headers.append(MatrixGenerator.get_letter_index(header)) + if labels.empty() : for label in range(datamatrix.get_size().x) : labels.append(label as String) + if datamatrix.empty(): datamatrix.resize(labels.size(), headers.size()) build_dataframe(datamatrix, headers, labels, table_name) func build_dataframe(datamatrix : Matrix, headers : PoolStringArray = [], labels : PoolStringArray = [] , table_name : String = "") -> void: @@ -32,12 +33,18 @@ func build_dataset_from_matrix(datamatrix : Matrix, headers : PoolStringArray, l func insert_column(column : Array, header : String = "", index : int = dataset[0].size()) -> void: assert(column.size() == datamatrix.rows(), "error: the column size must match the dataset column size") headers.insert(index, header if header != "" else MatrixGenerator.get_letter_index(index)) - datamatrix.insert_column(column, index-1) + if datamatrix.get_column(index-2 if index > 2 else 0).count(null) == datamatrix.rows(): + datamatrix.update_column(column, index-2) + else: + datamatrix.insert_column(column, index-1) dataset = build_dataset_from_matrix(datamatrix, headers, labels) func insert_row(row : Array, label : String = "", index : int = dataset.size()) -> PoolStringArray: labels.insert(index-1, label if label != "" else (index-1) as String) - datamatrix.insert_row(row, index-1) + if datamatrix.get_row(index-2 if index > 2 else 0).count(null) == datamatrix.columns(): + datamatrix.update_row(row, index-2 if index > 2 else 0) + else: + datamatrix.insert_row(row, index-1) dataset = build_dataset_from_matrix(datamatrix, headers, labels) return PoolStringArray([label] + row) diff --git a/addons/easy_charts/utilities/classes/structures/matrix.gd b/addons/easy_charts/utilities/classes/structures/matrix.gd index b9ac4d0..064a6c4 100644 --- a/addons/easy_charts/utilities/classes/structures/matrix.gd +++ b/addons/easy_charts/utilities/classes/structures/matrix.gd @@ -12,12 +12,28 @@ func insert_row(row : Array, index : int = values.size()) -> void: assert(row.size() == columns(), "the row size must match matrix row size") values.insert(index, row) +func update_row(row : Array, index : int) -> void: + assert(rows() > index, "the row size must match matrix row size") + values[index] = row + func insert_column(column : Array, index : int = values[0].size()) -> void: if columns() != 0: assert(column.size() == rows(), "the column size must match matrix column size") for row_idx in column.size(): values[row_idx].insert(index, column[row_idx]) +func update_column(column : Array, index : int) -> void: + assert(columns() > index, "the column size must match matrix column size") + for row_idx in column.size(): + values[row_idx][index] = column[row_idx] + +func resize(rows: int, columns: int) -> void: + for row in range(rows): + var value_row := [] + for column in range(columns): + value_row.append(null) + values.append(value_row) + func to_array() -> Array: return values.duplicate(true) @@ -37,7 +53,7 @@ func get_column(column : int) -> Array: column_array.append(row[column]) return column_array -func get_columns(from : int, to : int) -> Array: +func get_columns(from : int = 0, to : int = columns()-1) -> Array: var values : Array = [] for column in range(from, to): values.append(get_column(column)) @@ -48,10 +64,13 @@ func get_row(row : int) -> Array: assert(row < rows(), "index of the row requested (%s) exceedes matrix rows (%s)"%[row, rows()]) return values[row] -func get_rows(from : int, to : int) -> Array: +func get_rows(from : int = 0, to : int = rows()-1) -> Array: return values.slice(from, to) # return MatrixGenerator.from_array(values) +func empty() -> bool: + return rows() == 0 and columns() == 0 + func _to_string() -> String: var last_string_len : int for row in values: