diff --git a/addons/easy_charts/Utilities/Containers/.gdignore b/.gitattributes similarity index 100% rename from addons/easy_charts/Utilities/Containers/.gdignore rename to .gitattributes diff --git a/.gitignore b/.gitignore index 0c60b9e..f822639 100644 --- a/.gitignore +++ b/.gitignore @@ -1,20 +1,18 @@ +# Plugin Specific ignores +default_env.tres +/icon.png +/icon.png.import +project.godot +scn/ +/screenshot.png +/snapshot.png +*.tpm + +# Godot-specific ignores .import/ export.cfg export_presets.cfg -# Imported translations (automatically generated from CSV files) -*.translation -# Plugin specific ignores -.github/ -file-editor/ -scn/ -default_env.tres -icon.png -icon.png.import -project.godot + # Mono-specific ignores .mono/ -data_*/ -mono_crash.*.json -# System/tool-specific ignores -.directory -*~ +data_*/ \ No newline at end of file diff --git a/.tmp b/.tmp new file mode 100644 index 0000000..e33057d Binary files /dev/null and b/.tmp differ diff --git a/LICENSE b/LICENSE index 1ee7a79..45a44b7 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2020 fenix-hub +Copyright (c) 2021 Nicolò Santilio Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index acc7f3c..f77c436 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,3 @@ -

- -react - -

- # Notice, plugin under refactoring **This plugin is under refactoring an maintenance. The next official release will contain multiple updates and upgrades.**\ **If you encounter any bug, please contact me on Discord.** diff --git a/addons/easy_charts/2DChart/LineChart2D/line_chart2D.gd b/addons/easy_charts/2DChart/LineChart2D/line_chart2D.gd deleted file mode 100644 index e4089df..0000000 --- a/addons/easy_charts/2DChart/LineChart2D/line_chart2D.gd +++ /dev/null @@ -1,469 +0,0 @@ -tool -extends Chart2D -class_name LineChart2D - -# [Linechart2D] - General purpose node for Line Charts -# A line chart or line plot or line graph or curve chart is a type of chart which -# displays information as a series of data points called 'markers' -# connected by straight line segments. -# It is a basic type of chart common in many fields. It is similar to a scatter plot -# except that the measurement points are ordered (typically by their x-axis value) -# and joined with straight line segments. -# A line chart is often used to visualize a trend in data over intervals of time – -# a time series – thus the line is often drawn chronologically. -# In these cases they are known as run charts. -# Source: Wikipedia - -func _point_plotted(): - pass - - -func _ready(): - _get_children() - - -func _set_size(size: Vector2): - SIZE = size - build_chart() - if Engine.editor_hint: - _get_children() - Outlines.set_point_position(0, Vector2(origin.x, 0)) - Outlines.set_point_position(1, Vector2(SIZE.x, 0)) - Outlines.set_point_position(2, Vector2(SIZE.x, origin.y)) - Outlines.set_point_position(3, origin) - Outlines.set_point_position(4, Vector2(origin.x, 0)) - - Grid.get_node("VLine").set_point_position(0, Vector2((OFFSET.x + SIZE.x) / 2,0)) - Grid.get_node("VLine").set_point_position(1, Vector2((OFFSET.x + SIZE.x) / 2, origin.y)) - Grid.get_node("HLine").set_point_position(0, Vector2(origin.x, origin.y / 2)) - Grid.get_node("HLine").set_point_position(1, Vector2(SIZE.x, origin.y / 2)) - - -func clear(): - Outlines.points = [] - Grid.get_node("HLine").queue_free() - Grid.get_node("VLine").queue_free() - - -func load_font(): - if font != null: - font_size = font.get_height() - var theme: Theme = Theme.new() - theme.set_default_font(font) - PointData.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 _plot(source: String, delimiter: String, are_values_columns: bool, x_values_index: int): - randomize() - - clear() - - load_font() - PointData.hide() - - datas = read_datas(source, delimiter) - count_functions() - structure_datas(datas, are_values_columns, x_values_index) - build_chart() - calculate_pass() - calculate_coordinates() - calculate_colors() - draw_chart() - - create_legend() - emit_signal("chart_plotted", self) - - -func plot(): - randomize() - - clear() - - load_font() - PointData.hide() - - if source == "" or source == null: - ECUtilities._print_message("Can't plot a chart without a Source file. Please, choose it in editor, or use the custom function _plot().", 1) - return - datas = read_datas(source,delimiter) - count_functions() - structure_datas(datas, are_values_columns, x_values_index) - build_chart() - calculate_pass() - calculate_coordinates() - calculate_colors() - draw_chart() - - create_legend() - emit_signal("chart_plotted", self) - - -func calculate_colors(): - if function_colors.empty() or function_colors.size() < functions: - for function in functions: - function_colors.append(Color("#1e1e1e")) - - -func draw_chart(): - draw_outlines() - draw_v_grid() - draw_h_grid() - draw_functions() - - -func draw_outlines(): - if boxed: - Outlines.set_default_color(box_color) - OutlinesTween.interpolate_method( - Outlines, - "add_point", - Vector2(origin.x, 0), - Vector2(SIZE.x, 0), - drawing_duration * 0.5, - Tween.TRANS_QUINT, - Tween.EASE_OUT) - OutlinesTween.start() - yield(OutlinesTween, "tween_all_completed") - OutlinesTween.interpolate_method( - Outlines, - "add_point", - Vector2(SIZE.x, 0), - Vector2(SIZE.x, origin.y), - drawing_duration * 0.5, - Tween.TRANS_QUINT, - Tween.EASE_OUT) - OutlinesTween.start() - yield(OutlinesTween, "tween_all_completed") - - OutlinesTween.interpolate_method( - Outlines, - "add_point", - Vector2(SIZE.x, origin.y), - origin, - drawing_duration * 0.5, - Tween.TRANS_QUINT, - Tween.EASE_OUT) - OutlinesTween.start() - yield(OutlinesTween, "tween_all_completed") - OutlinesTween.interpolate_method( - Outlines, - "add_point", - origin, - Vector2(origin.x, 0), - drawing_duration * 0.5, - Tween.TRANS_QUINT, - Tween.EASE_OUT) - OutlinesTween.start() - yield(OutlinesTween, "tween_all_completed") - - -func draw_v_grid(): - for p in x_chors.size(): - var point: Vector2 = origin + Vector2((p) * x_pass, 0) - var v_grid: Line2D = Line2D.new() - Grid.add_child(v_grid) - v_grid.set_width(1) - v_grid.set_default_color(v_lines_color) - add_label(point + Vector2(-const_width / 2 * x_chors[p].length(), font_size / 2), x_chors[p]) - GridTween.interpolate_method( - v_grid, - "add_point", - point, - point - Vector2(0, SIZE.y - OFFSET.y), - drawing_duration / (x_chors.size()), - Tween.TRANS_EXPO, - Tween.EASE_OUT) - GridTween.start() - yield(GridTween, "tween_all_completed") - - -func draw_h_grid(): - for p in y_chors.size(): - var point: Vector2 = origin - Vector2(0, p * y_pass) - var h_grid: Line2D = Line2D.new() - Grid.add_child(h_grid) - h_grid.set_width(1) - h_grid.set_default_color(h_lines_color) - add_label(point - Vector2(y_chors[p].length() * const_width + font_size, font_size / 2), y_chors[p]) - GridTween.interpolate_method( - h_grid, - "add_point", - point, - Vector2(SIZE.x, point.y), - drawing_duration / (y_chors.size()), - Tween.TRANS_EXPO, - Tween.EASE_OUT) - GridTween.start() - yield(GridTween, "tween_all_completed") - - -func add_label(point: Vector2, text: String): - var lbl: Label = Label.new() - if font != null: - lbl.set("custom_fonts/font", font) - lbl.set("custom_colors/font_color", font_color) - Grid.add_child(lbl) - lbl.rect_position = point - lbl.set_text(text) - - -func draw_functions(): - for function in point_positions.size(): - draw_function(function, point_positions[function]) - - -func draw_function(f_index: int, function: Array): - var line: Line2D = Line2D.new() - var backline: Line2D = Line2D.new() - construct_line(line, backline, f_index, function) - var pointv: Point - for point in function.size(): - pointv = point_node.instance() - Functions.add_child(pointv) - pointv.connect("_mouse_entered", self, "show_data") - pointv.connect("_mouse_exited", self, "hide_data") - pointv.connect("_point_pressed", self, "point_pressed") - pointv.create_point( - point_shape, - function_colors[f_index], - Color.white, function[point], - pointv.format_value(point_values[f_index][point], false, false), - y_labels[point if invert_chart else f_index] as String) - if point < function.size() - 1: - FunctionsTween.interpolate_method( - line, - "add_point", - function[point], - function[point + 1], - drawing_duration / function.size(), - Tween.TRANS_QUINT, - Tween.EASE_OUT) - FunctionsTween.start() - yield(FunctionsTween, "tween_all_completed") - - -func construct_line(line: Line2D, backline: Line2D, f_index: int, function: Array): - var midtone = Color( - Color(function_colors[f_index]).r, - Color(function_colors[f_index]).g, - Color(function_colors[f_index]).b, - Color(function_colors[f_index]).a / 2) - backline.set_width(3) - backline.set_default_color(midtone) - backline.antialiased = true - Functions.add_child(backline) - line.set_width(2) - line.set_default_color(function_colors[f_index]) - line.antialiased = true - Functions.add_child(line) - - -func read_datas(source: String, delimiter: String): - 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: - content.erase(data) - return content - - -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 build_chart(): - origin = Vector2(OFFSET.x, SIZE.y - OFFSET.y) - - -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 - x_pass = (SIZE.x - OFFSET.x) / (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 invert_chart: - x_coordinates.append(x_pass * x) - else: - x_coordinates.append(x_datas[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_positions[function_value].append(Vector2( - x_coordinates[function] + origin.x, - origin.y - y_coordinates[function][function_value])) - point_values[function_value].append( - [x_datas[function_value], y_datas[function_value][function]]) - else: - point_positions[function].append(Vector2( - 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_values[y].append([x_datas[cluster],y_datas[cluster][y]]) - point_positions[y].append(Vector2( - x_coordinates[cluster] + origin.x, - origin.y - y_coordinates[cluster][y])) - else: - point_values[cluster].append([x_datas[y],y_datas[cluster][y]]) - point_positions[cluster].append(Vector2( - x_coordinates[y] + origin.x, - origin.y - y_coordinates[cluster][y])) - - -func invert_chart(): - invert_chart = !invert_chart - count_functions() - redraw() - create_legend() - -func _enter_tree(): - _ready() - - -# Signal Repeaters -func point_pressed(point: Point) -> Point: - return point diff --git a/addons/easy_charts/2DChart/ScatterChart2D/scatter_chart2D.gd b/addons/easy_charts/2DChart/ScatterChart2D/scatter_chart2D.gd deleted file mode 100644 index 82228e4..0000000 --- a/addons/easy_charts/2DChart/ScatterChart2D/scatter_chart2D.gd +++ /dev/null @@ -1,448 +0,0 @@ -tool -extends Chart2D -class_name ScatterChart2D - -""" -[ScatterChart2D] - General purpose node for Scatter Charts - -A scatter plot (also called a scatterplot, scatter graph, scatter chart, scattergram, or scatter diagram) - is a type of plot or mathematical diagram using Cartesian coordinates to display values for typically two variables -for a set of data. If the points are coded (color/shape/size), one additional variable can be displayed. -The data are displayed as a collection of points, each having the value of one variable determining the position on -the horizontal axis and the value of the other variable determining the position on the vertical axis. - -/ source : Wikipedia / -""" - -func _point_plotted(): - pass - -func _ready(): - _get_children() - -func _get_children(): - OutlinesTween = $OutlinesTween - PointTween = $PointTween - Functions = $Functions - GridTween = $GridTween - PointData = $PointData/PointData - Outlines = $Outlines - Grid = $Grid - -func _set_size(size : Vector2): - SIZE = size - build_chart() - if Engine.editor_hint: - _get_children() - Outlines.set_point_position(0,Vector2(origin.x,0)) - Outlines.set_point_position(1,Vector2(SIZE.x,0)) - Outlines.set_point_position(2,Vector2(SIZE.x,origin.y)) - Outlines.set_point_position(3,origin) - Outlines.set_point_position(4,Vector2(origin.x,0)) - - Grid.get_node("VLine").set_point_position(0,Vector2((OFFSET.x+SIZE.x)/2,0)) - Grid.get_node("VLine").set_point_position(1,Vector2((OFFSET.x+SIZE.x)/2,origin.y)) - Grid.get_node("HLine").set_point_position(0,Vector2(origin.x,origin.y/2)) - Grid.get_node("HLine").set_point_position(1,Vector2(SIZE.x,origin.y/2)) - -func clear(): - Outlines.points = [] - Grid.get_node("HLine").queue_free() - Grid.get_node("VLine").queue_free() - -func load_font(): - if font != null: - font_size = font.get_height() - var theme : Theme = Theme.new() - theme.set_default_font(font) - PointData.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 _plot(source_ : String, delimiter_ : String, are_values_columns_ : bool, x_values_index_ : int): - randomize() - - clear() - - load_font() - PointData.hide() - - datas = read_datas(source_,delimiter_) - count_functions() - structure_datas(datas,are_values_columns_,x_values_index_) - build_chart() - calculate_pass() - calculate_coordinates() - calculate_colors() - draw_chart() - - create_legend() - emit_signal("chart_plotted", self) - -func plot(): - randomize() - - clear() - - load_font() - PointData.hide() - - if source == "" or source == null: - ECUtilities._print_message("Can't plot a chart without a Source file. Please, choose it in editor, or use the custom function _plot().",1) - return - datas = read_datas(source,delimiter) - count_functions() - structure_datas(datas,are_values_columns,x_values_index) - build_chart() - calculate_pass() - calculate_coordinates() - calculate_colors() - draw_chart() - - create_legend() - emit_signal("chart_plotted", self) - -func calculate_colors(): - if function_colors.empty() or function_colors.size() < functions: - for function in functions: - function_colors.append(Color("#1e1e1e") as Color) - -func draw_chart(): - draw_outlines() - draw_v_grid() - draw_h_grid() - draw_functions() - -func draw_outlines(): - if boxed: - Outlines.set_default_color(box_color) - OutlinesTween.interpolate_method(Outlines,"add_point", - Vector2(origin.x,0),Vector2(SIZE.x,0),drawing_duration*0.5,Tween.TRANS_QUINT,Tween.EASE_OUT) - OutlinesTween.start() - yield(OutlinesTween,"tween_all_completed") - OutlinesTween.interpolate_method(Outlines,"add_point", - Vector2(SIZE.x,0),Vector2(SIZE.x,origin.y),drawing_duration*0.5,Tween.TRANS_QUINT,Tween.EASE_OUT) - OutlinesTween.start() - yield(OutlinesTween,"tween_all_completed") - OutlinesTween.interpolate_method(Outlines,"add_point", - Vector2(SIZE.x,origin.y),origin,drawing_duration*0.5,Tween.TRANS_QUINT,Tween.EASE_OUT) - OutlinesTween.start() - yield(OutlinesTween,"tween_all_completed") - OutlinesTween.interpolate_method(Outlines,"add_point", - origin,Vector2(origin.x,0),drawing_duration*0.5,Tween.TRANS_QUINT,Tween.EASE_OUT) - OutlinesTween.start() - yield(OutlinesTween,"tween_all_completed") - -func draw_v_grid(): - for p in x_chors.size(): - var point : Vector2 = origin+Vector2((p)*x_pass,0) - var v_grid : Line2D = Line2D.new() - Grid.add_child(v_grid) - v_grid.set_width(1) - v_grid.set_default_color(v_lines_color) - add_label(point+Vector2(-const_width/2*x_chors[p].length(),font_size/2), x_chors[p]) - GridTween.interpolate_method(v_grid,"add_point",point,point-Vector2(0,SIZE.y-OFFSET.y),drawing_duration/(x_chors.size()),Tween.TRANS_EXPO,Tween.EASE_OUT) - GridTween.start() - yield(GridTween,"tween_all_completed") - -func draw_h_grid(): - for p in y_chors.size(): - var point : Vector2 = origin-Vector2(0,(p)*y_pass) - var h_grid : Line2D = Line2D.new() - Grid.add_child(h_grid) - h_grid.set_width(1) - h_grid.set_default_color(h_lines_color) - add_label(point-Vector2(y_chors[p].length()*const_width+font_size,font_size/2), y_chors[p]) - GridTween.interpolate_method(h_grid,"add_point",point,Vector2(SIZE.x,point.y),drawing_duration/(y_chors.size()),Tween.TRANS_EXPO,Tween.EASE_OUT) - GridTween.start() - yield(GridTween,"tween_all_completed") - - -func add_label(point : Vector2, text : String): - var lbl : Label = Label.new() - if font != null: - lbl.set("custom_fonts/font",font) - lbl.set("custom_colors/font_color",font_color) - Grid.add_child(lbl) - lbl.rect_position = point - lbl.set_text(text) - -func draw_functions(): - for function in point_positions.size(): - draw_function(function,point_positions[function]) - -func draw_function(f_index : int, function : Array): - var pointv : Point - for point in function.size(): - pointv = point_node.instance() - Functions.add_child(pointv) - pointv.connect("_mouse_entered",self,"show_data") - pointv.connect("_mouse_exited",self,"hide_data") - pointv.connect("_point_pressed",self,"point_pressed") - pointv.create_point(point_shape, function_colors[f_index], Color.white, function[point], - pointv.format_value(point_values[f_index][point],false,false), - y_labels[point if invert_chart else f_index] as String) - yield(get_tree().create_timer(drawing_duration/function.size()), "timeout") - - -func read_datas(source : String, delimiter : String): - 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: - content.erase(data) - return content - -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 build_chart(): - origin = Vector2(OFFSET.x,SIZE.y-OFFSET.y) - -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 - x_pass = (SIZE.x - OFFSET.x) / (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 invert_chart: - x_coordinates.append(x_pass*x) - else: - x_coordinates.append(x_datas[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_positions[function_value].append(Vector2(x_coordinates[function]+origin.x, origin.y-y_coordinates[function][function_value])) - point_values[function_value].append([x_datas[function_value],y_datas[function_value][function]]) - else: - point_positions[function].append(Vector2(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_values[y].append([x_datas[cluster],y_datas[cluster][y]]) - point_positions[y].append(Vector2(x_coordinates[cluster]+origin.x,origin.y-y_coordinates[cluster][y])) - else: - point_values[cluster].append([x_datas[y],y_datas[cluster][y]]) - point_positions[cluster].append(Vector2(x_coordinates[y]+origin.x,origin.y-y_coordinates[cluster][y])) - -func redraw(): - build_chart() - calculate_pass() - calculate_coordinates() - update() - - -func show_data(point): - PointData.update_datas(point) - PointData.show() - -func hide_data(): - PointData.hide() - -func clear_points(): - function_colors.clear() - if Functions.get_children(): - for function in Functions.get_children(): - function.queue_free() - -func set_legend(l : Array): - legend = l - -func get_legend(): - return legend - -func invert_chart(): - invert_chart = !invert_chart - count_functions() - redraw() - create_legend() - -func count_functions(): - if are_values_columns: - if not invert_chart: - functions = datas[0].size()-1 - else: - functions = datas.size()-1 - else: - if invert_chart: - functions = datas[0].size()-1 - else: - functions = datas.size()-1 - -func create_legend(): - legend.clear() - for function in functions: - var function_legend = FunctionLegend.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) - -func apply_template(template_name : int): - template = template_name - templates = ECUtilities._load_templates() - if template_name!=null: - var custom_template = templates.get(templates.keys()[template_name]) - function_colors = custom_template.function_colors as PoolColorArray - 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) - property_list_changed_notify() - - if Engine.editor_hint: - _get_children() - Outlines.set_default_color(box_color) - Grid.get_node("VLine").set_default_color(v_lines_color) - Grid.get_node("HLine").set_default_color(h_lines_color) - - -func _enter_tree(): - _ready() diff --git a/addons/easy_charts/2DChart/ColumnChart2D/column_chart2D.gd b/addons/easy_charts/2d_charts/ColumnChart2D/column_chart2D.gd similarity index 94% rename from addons/easy_charts/2DChart/ColumnChart2D/column_chart2D.gd rename to addons/easy_charts/2d_charts/ColumnChart2D/column_chart2D.gd index 45dd9d5..262e9e1 100644 --- a/addons/easy_charts/2DChart/ColumnChart2D/column_chart2D.gd +++ b/addons/easy_charts/2d_charts/ColumnChart2D/column_chart2D.gd @@ -27,7 +27,7 @@ func _get_children(): OutlinesTween = $OutlinesTween Functions = $Functions GridTween = $GridTween - PointData = $PointData/PointData + DataTooltip = $CanvasLayer/DataTooltip Outlines = $Outlines Grid = $Grid @@ -56,13 +56,13 @@ func load_font(): font_size = font.get_height() var theme : Theme = Theme.new() theme.set_default_font(font) - PointData.set_theme(theme) + DataTooltip.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) + DataTooltip.Data.set("custom_fonts/font",bold_font) func _plot(source_ : String, delimiter_ : String, are_values_columns_ : bool, x_values_index_ : int): randomize() @@ -70,11 +70,11 @@ func _plot(source_ : String, delimiter_ : String, are_values_columns_ : bool, x_ clear() load_font() - PointData.hide() + DataTooltip.hide() - datas = read_datas(source_,delimiter_) + datas = read_data(source_,delimiter_) count_functions() - structure_datas(datas,are_values_columns_,x_values_index_) + structure_data(datas,are_values_columns_,x_values_index_) build_chart() calculate_pass() calculate_coordinates() @@ -90,14 +90,14 @@ func plot(): clear() load_font() - PointData.hide() + DataTooltip.hide() if source == "" or source == null: - ECECUtilities._print_message("Can't plot a chart without a Source file. Please, choose it in editor, or use the custom function _plot().",1) + ECUtilities._print_message("Can't plot a chart without a Source file. Please, choose it in editor, or use the custom function _plot().",1) return - datas = read_datas(source,delimiter) + datas = read_data(source,delimiter) count_functions() - structure_datas(datas,are_values_columns,x_values_index) + structure_data(datas,are_values_columns,x_values_index) build_chart() calculate_pass() calculate_coordinates() @@ -207,7 +207,7 @@ func construct_column(line : Line2D, f_index : int, function : Array): line.antialiased = true Functions.add_child(line,true) -func read_datas(source : String, delimiter : String): +func read_data(source : String, delimiter : String): var file : File = File.new() file.open(source,File.READ) var content : Array @@ -220,7 +220,7 @@ func read_datas(source : String, delimiter : String): content.erase(data) return content -func structure_datas(database : Array, are_values_columns : bool, x_values_index : int): +func structure_data(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 @@ -393,13 +393,13 @@ func redraw(): func show_data(point): - PointData.update_datas(point) - PointData.show() + DataTooltip.update_datas(point) + DataTooltip.show() func hide_data(): - PointData.hide() + DataTooltip.hide() -func clear_points(): +func clean_points(): function_colors.clear() if Functions.get_children(): for function in Functions.get_children(): @@ -448,7 +448,7 @@ func create_legend(): func apply_template(template_name : int): template = template_name - templates = ECECUtilities._load_templates() + templates = ECUtilities._load_templates() if template_name!=null: var custom_template = templates.get(templates.keys()[template_name]) function_colors = custom_template.function_colors as PoolColorArray diff --git a/addons/easy_charts/2DChart/ColumnChart2D/column_chart2D.tscn b/addons/easy_charts/2d_charts/ColumnChart2D/column_chart2D.tscn similarity index 77% rename from addons/easy_charts/2DChart/ColumnChart2D/column_chart2D.tscn rename to addons/easy_charts/2d_charts/ColumnChart2D/column_chart2D.tscn index b50955f..6993cd5 100644 --- a/addons/easy_charts/2DChart/ColumnChart2D/column_chart2D.tscn +++ b/addons/easy_charts/2d_charts/ColumnChart2D/column_chart2D.tscn @@ -1,12 +1,7 @@ [gd_scene load_steps=3 format=2] -[ext_resource path="res://addons/easy_charts/2DChart/ColumnChart2D/column_chart2D.gd" type="Script" id=1] -[ext_resource path="res://addons/easy_charts/Utilities/Point/point_data.tscn" type="PackedScene" id=2] - - - - - +[ext_resource path="res://addons/easy_charts/2d_charts/ColumnChart2D/column_chart2D.gd" type="Script" id=1] +[ext_resource path="res://addons/easy_charts/utilities/containers/data_tooltip/data_tooltip.tscn" type="PackedScene" id=2] [node name="BarChart2D" type="Node2D"] script = ExtResource( 1 ) @@ -55,13 +50,19 @@ default_color = Color( 0.117647, 0.117647, 0.117647, 1 ) [node name="GridTween" type="Tween" parent="."] -[node name="PointData" parent="." instance=ExtResource( 2 )] +[node name="CanvasLayer" type="CanvasLayer" parent="."] -[node name="PointData" parent="PointData" index="0"] +[node name="PointData" parent="CanvasLayer" instance=ExtResource( 2 )] +margin_left = 269.278 +margin_top = 453.515 +margin_right = 289.278 +margin_bottom = 463.515 + +[node name="PointData" parent="CanvasLayer/PointData" index="0"] visible = false margin_left = 48.6217 margin_top = -122.631 margin_right = 48.4856 margin_bottom = -121.831 -[editable path="PointData"] +[editable path="CanvasLayer/PointData"] diff --git a/addons/easy_charts/2d_charts/LineChart2D/line_chart2D.gd b/addons/easy_charts/2d_charts/LineChart2D/line_chart2D.gd new file mode 100644 index 0000000..23d1b79 --- /dev/null +++ b/addons/easy_charts/2d_charts/LineChart2D/line_chart2D.gd @@ -0,0 +1,469 @@ +tool +extends Chart2D +class_name LineChart2D + +# [Linechart2D] - General purpose node for Line Charts +# A line chart or line plot or line graph or curve chart is a type of chart which +# displays information as a series of data points called 'markers' +# connected by straight line segments. +# It is a basic type of chart common in many fields. It is similar to a scatter plot +# except that the measurement points are ordered (typically by their x-axis value) +# and joined with straight line segments. +# A line chart is often used to visualize a trend in data over intervals of time – +# a time series – thus the line is often drawn chronologically. +# In these cases they are known as run charts. +# Source: Wikipedia + +func _point_plotted(): + pass + + +func _ready(): + _get_children() + + +func _set_size(size: Vector2): + SIZE = size + build_chart() + if Engine.editor_hint: + _get_children() + Outlines.set_point_position(0, Vector2(origin.x, 0)) + Outlines.set_point_position(1, Vector2(SIZE.x, 0)) + Outlines.set_point_position(2, Vector2(SIZE.x, origin.y)) + Outlines.set_point_position(3, origin) + Outlines.set_point_position(4, Vector2(origin.x, 0)) + + Grid.get_node("VLine").set_point_position(0, Vector2((OFFSET.x + SIZE.x) / 2,0)) + Grid.get_node("VLine").set_point_position(1, Vector2((OFFSET.x + SIZE.x) / 2, origin.y)) + Grid.get_node("HLine").set_point_position(0, Vector2(origin.x, origin.y / 2)) + Grid.get_node("HLine").set_point_position(1, Vector2(SIZE.x, origin.y / 2)) + + +func clear(): + Outlines.points = [] + Grid.get_node("HLine").queue_free() + Grid.get_node("VLine").queue_free() + + +func load_font(): + if font != null: + font_size = font.get_height() + var theme: Theme = Theme.new() + theme.set_default_font(font) + DataTooltip.set_theme(theme) + else: + var lbl = Label.new() + font = lbl.get_font("") + lbl.free() + if bold_font != null: + DataTooltip.Data.set("custom_fonts/font",bold_font) + + +func _plot(source: String, delimiter: String, are_values_columns: bool, x_values_index: int): + randomize() + + clear() + + load_font() + DataTooltip.hide() + + datas = read_data(source, delimiter) + count_functions() + structure_data(datas, are_values_columns, x_values_index) + build_chart() + calculate_pass() + calculate_coordinates() + calculate_colors() + draw_chart() + + create_legend() + emit_signal("chart_plotted", self) + + +func plot(): + randomize() + + clear() + + load_font() + DataTooltip.hide() + + if source == "" or source == null: + ECUtilities._print_message("Can't plot a chart without a Source file. Please, choose it in editor, or use the custom function _plot().", 1) + return + datas = read_data(source,delimiter) + count_functions() + structure_data(datas, are_values_columns, x_values_index) + build_chart() + calculate_pass() + calculate_coordinates() + calculate_colors() + draw_chart() + + create_legend() + emit_signal("chart_plotted", self) + + +func calculate_colors(): + if function_colors.empty() or function_colors.size() < functions: + for function in functions: + function_colors.append(Color("#1e1e1e")) + + +func draw_chart(): + draw_outlines() + draw_v_grid() + draw_h_grid() + draw_functions() + + +func draw_outlines(): + if boxed: + Outlines.set_default_color(box_color) + OutlinesTween.interpolate_method( + Outlines, + "add_point", + Vector2(origin.x, 0), + Vector2(SIZE.x, 0), + drawing_duration * 0.5, + Tween.TRANS_QUINT, + Tween.EASE_OUT) + OutlinesTween.start() + yield(OutlinesTween, "tween_all_completed") + OutlinesTween.interpolate_method( + Outlines, + "add_point", + Vector2(SIZE.x, 0), + Vector2(SIZE.x, origin.y), + drawing_duration * 0.5, + Tween.TRANS_QUINT, + Tween.EASE_OUT) + OutlinesTween.start() + yield(OutlinesTween, "tween_all_completed") + + OutlinesTween.interpolate_method( + Outlines, + "add_point", + Vector2(SIZE.x, origin.y), + origin, + drawing_duration * 0.5, + Tween.TRANS_QUINT, + Tween.EASE_OUT) + OutlinesTween.start() + yield(OutlinesTween, "tween_all_completed") + OutlinesTween.interpolate_method( + Outlines, + "add_point", + origin, + Vector2(origin.x, 0), + drawing_duration * 0.5, + Tween.TRANS_QUINT, + Tween.EASE_OUT) + OutlinesTween.start() + yield(OutlinesTween, "tween_all_completed") + + +func draw_v_grid(): + for p in x_chors.size(): + var point: Vector2 = origin + Vector2((p) * x_pass, 0) + var v_grid: Line2D = Line2D.new() + Grid.add_child(v_grid) + v_grid.set_width(1) + v_grid.set_default_color(v_lines_color) + add_label(point + Vector2(-const_width / 2 * x_chors[p].length(), font_size / 2), x_chors[p]) + GridTween.interpolate_method( + v_grid, + "add_point", + point, + point - Vector2(0, SIZE.y - OFFSET.y), + drawing_duration / (x_chors.size()), + Tween.TRANS_EXPO, + Tween.EASE_OUT) + GridTween.start() + yield(GridTween, "tween_all_completed") + + +func draw_h_grid(): + for p in y_chors.size(): + var point: Vector2 = origin - Vector2(0, p * y_pass) + var h_grid: Line2D = Line2D.new() + Grid.add_child(h_grid) + h_grid.set_width(1) + h_grid.set_default_color(h_lines_color) + add_label(point - Vector2(y_chors[p].length() * const_width + font_size, font_size / 2), y_chors[p]) + GridTween.interpolate_method( + h_grid, + "add_point", + point, + Vector2(SIZE.x, point.y), + drawing_duration / (y_chors.size()), + Tween.TRANS_EXPO, + Tween.EASE_OUT) + GridTween.start() + yield(GridTween, "tween_all_completed") + + +func add_label(point: Vector2, text: String): + var lbl: Label = Label.new() + if font != null: + lbl.set("custom_fonts/font", font) + lbl.set("custom_colors/font_color", font_color) + Grid.add_child(lbl) + lbl.rect_position = point + lbl.set_text(text) + + +func draw_functions(): + for function in point_positions.size(): + draw_function(function, point_positions[function]) + + +func draw_function(f_index: int, function: Array): + var line: Line2D = Line2D.new() + var backline: Line2D = Line2D.new() + construct_line(line, backline, f_index, function) + var pointv: Point + for point in function.size(): + pointv = point_node.instance() + Functions.add_child(pointv) + pointv.connect("_mouse_entered", self, "show_data") + pointv.connect("_mouse_exited", self, "hide_data") + pointv.connect("_point_pressed", self, "point_pressed") + pointv.create_point( + point_shape, + function_colors[f_index], + Color.white, function[point], + pointv.format_value(point_values[f_index][point], false, false), + y_labels[point if invert_chart else f_index] as String) + if point < function.size() - 1: + FunctionsTween.interpolate_method( + line, + "add_point", + function[point], + function[point + 1], + drawing_duration / function.size(), + Tween.TRANS_QUINT, + Tween.EASE_OUT) + FunctionsTween.start() + yield(FunctionsTween, "tween_all_completed") + + +func construct_line(line: Line2D, backline: Line2D, f_index: int, function: Array): + var midtone = Color( + Color(function_colors[f_index]).r, + Color(function_colors[f_index]).g, + Color(function_colors[f_index]).b, + Color(function_colors[f_index]).a / 2) + backline.set_width(3) + backline.set_default_color(midtone) + backline.antialiased = true + Functions.add_child(backline) + line.set_width(2) + line.set_default_color(function_colors[f_index]) + line.antialiased = true + Functions.add_child(line) + + +func read_data(source: String, delimiter: String): + 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: + content.erase(data) + return content + + +func structure_data(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 build_chart(): + origin = Vector2(OFFSET.x, SIZE.y - OFFSET.y) + + +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 + x_pass = (SIZE.x - OFFSET.x) / (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 invert_chart: + x_coordinates.append(x_pass * x) + else: + x_coordinates.append(x_datas[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_positions[function_value].append(Vector2( + x_coordinates[function] + origin.x, + origin.y - y_coordinates[function][function_value])) + point_values[function_value].append( + [x_datas[function_value], y_datas[function_value][function]]) + else: + point_positions[function].append(Vector2( + 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_values[y].append([x_datas[cluster],y_datas[cluster][y]]) + point_positions[y].append(Vector2( + x_coordinates[cluster] + origin.x, + origin.y - y_coordinates[cluster][y])) + else: + point_values[cluster].append([x_datas[y],y_datas[cluster][y]]) + point_positions[cluster].append(Vector2( + x_coordinates[y] + origin.x, + origin.y - y_coordinates[cluster][y])) + + +func invert_chart(): + invert_chart = !invert_chart + count_functions() + redraw() + create_legend() + +func _enter_tree(): + _ready() + + +# Signal Repeaters +func point_pressed(point: Point) -> Point: + return point diff --git a/addons/easy_charts/2DChart/LineChart2D/line_chart2D.tscn b/addons/easy_charts/2d_charts/LineChart2D/line_chart2D.tscn similarity index 78% rename from addons/easy_charts/2DChart/LineChart2D/line_chart2D.tscn rename to addons/easy_charts/2d_charts/LineChart2D/line_chart2D.tscn index 95949e7..b3ef8a7 100644 --- a/addons/easy_charts/2DChart/LineChart2D/line_chart2D.tscn +++ b/addons/easy_charts/2d_charts/LineChart2D/line_chart2D.tscn @@ -1,10 +1,7 @@ [gd_scene load_steps=3 format=2] -[ext_resource path="res://addons/easy_charts/2DChart/LineChart2D/line_chart2D.gd" type="Script" id=1] -[ext_resource path="res://addons/easy_charts/Utilities/Point/point_data.tscn" type="PackedScene" id=2] - - - +[ext_resource path="res://addons/easy_charts/2d_charts/LineChart2D/line_chart2D.gd" type="Script" id=1] +[ext_resource path="res://addons/easy_charts/utilities/containers/data_tooltip/data_tooltip.tscn" type="PackedScene" id=2] [node name="LineChart2D" type="Node2D"] @@ -55,13 +52,19 @@ default_color = Color( 0.117647, 0.117647, 0.117647, 1 ) [node name="GridTween" type="Tween" parent="."] -[node name="PointData" parent="." instance=ExtResource( 2 )] +[node name="CanvasLayer" type="CanvasLayer" parent="."] -[node name="PointData" parent="PointData" index="0"] +[node name="DataTooltip" parent="CanvasLayer" instance=ExtResource( 2 )] +margin_left = 286.115 +margin_top = 462.494 +margin_right = 306.114 +margin_bottom = 472.494 + +[node name="PointData" parent="CanvasLayer/DataTooltip" index="0"] visible = false margin_left = 52.8643 margin_top = -115.56 margin_right = 52.7283 margin_bottom = -114.76 -[editable path="PointData"] +[editable path="CanvasLayer/DataTooltip"] diff --git a/addons/easy_charts/2d_charts/ScatterChart2D/scatter_chart2D.gd b/addons/easy_charts/2d_charts/ScatterChart2D/scatter_chart2D.gd new file mode 100644 index 0000000..c1c66b6 --- /dev/null +++ b/addons/easy_charts/2d_charts/ScatterChart2D/scatter_chart2D.gd @@ -0,0 +1,448 @@ +tool +extends Chart2D +class_name ScatterChart2D + +""" +[ScatterChart2D] - General purpose node for Scatter Charts + +A scatter plot (also called a scatterplot, scatter graph, scatter chart, scattergram, or scatter diagram) +is a type of plot or mathematical diagram using Cartesian coordinates to display values for typically two variables +for a set of data. If the points are coded (color/shape/size), one additional variable can be displayed. +The data are displayed as a collection of points, each having the value of one variable determining the position on +the horizontal axis and the value of the other variable determining the position on the vertical axis. + +/ source : Wikipedia / +""" + +func _point_plotted(): + pass + +func _ready(): + _get_children() + +func _get_children(): + OutlinesTween = $OutlinesTween + PointTween = $PointTween + Functions = $Functions + GridTween = $GridTween + DataTooltip = $CanvasLayer/DataTooltip + Outlines = $Outlines + Grid = $Grid + +func _set_size(size : Vector2): + SIZE = size + build_chart() + if Engine.editor_hint: + _get_children() + Outlines.set_point_position(0,Vector2(origin.x,0)) + Outlines.set_point_position(1,Vector2(SIZE.x,0)) + Outlines.set_point_position(2,Vector2(SIZE.x,origin.y)) + Outlines.set_point_position(3,origin) + Outlines.set_point_position(4,Vector2(origin.x,0)) + + Grid.get_node("VLine").set_point_position(0,Vector2((OFFSET.x+SIZE.x)/2,0)) + Grid.get_node("VLine").set_point_position(1,Vector2((OFFSET.x+SIZE.x)/2,origin.y)) + Grid.get_node("HLine").set_point_position(0,Vector2(origin.x,origin.y/2)) + Grid.get_node("HLine").set_point_position(1,Vector2(SIZE.x,origin.y/2)) + +func clear(): + Outlines.points = [] + Grid.get_node("HLine").queue_free() + Grid.get_node("VLine").queue_free() + +func load_font(): + if font != null: + font_size = font.get_height() + var theme : Theme = Theme.new() + theme.set_default_font(font) + DataTooltip.set_theme(theme) + else: + var lbl = Label.new() + font = lbl.get_font("") + lbl.free() + if bold_font != null: + DataTooltip.Data.set("custom_fonts/font",bold_font) + +func _plot(source_ : String, delimiter_ : String, are_values_columns_ : bool, x_values_index_ : int): + randomize() + + clear() + + load_font() + DataTooltip.hide() + + datas = read_data(source_,delimiter_) + count_functions() + structure_data(datas,are_values_columns_,x_values_index_) + build_chart() + calculate_pass() + calculate_coordinates() + calculate_colors() + draw_chart() + + create_legend() + emit_signal("chart_plotted", self) + +func plot(): + randomize() + + clear() + + load_font() + DataTooltip.hide() + + if source == "" or source == null: + ECUtilities._print_message("Can't plot a chart without a Source file. Please, choose it in editor, or use the custom function _plot().",1) + return + datas = read_data(source,delimiter) + count_functions() + structure_data(datas,are_values_columns,x_values_index) + build_chart() + calculate_pass() + calculate_coordinates() + calculate_colors() + draw_chart() + + create_legend() + emit_signal("chart_plotted", self) + +func calculate_colors(): + if function_colors.empty() or function_colors.size() < functions: + for function in functions: + function_colors.append(Color("#1e1e1e") as Color) + +func draw_chart(): + draw_outlines() + draw_v_grid() + draw_h_grid() + draw_functions() + +func draw_outlines(): + if boxed: + Outlines.set_default_color(box_color) + OutlinesTween.interpolate_method(Outlines,"add_point", + Vector2(origin.x,0),Vector2(SIZE.x,0),drawing_duration*0.5,Tween.TRANS_QUINT,Tween.EASE_OUT) + OutlinesTween.start() + yield(OutlinesTween,"tween_all_completed") + OutlinesTween.interpolate_method(Outlines,"add_point", + Vector2(SIZE.x,0),Vector2(SIZE.x,origin.y),drawing_duration*0.5,Tween.TRANS_QUINT,Tween.EASE_OUT) + OutlinesTween.start() + yield(OutlinesTween,"tween_all_completed") + OutlinesTween.interpolate_method(Outlines,"add_point", + Vector2(SIZE.x,origin.y),origin,drawing_duration*0.5,Tween.TRANS_QUINT,Tween.EASE_OUT) + OutlinesTween.start() + yield(OutlinesTween,"tween_all_completed") + OutlinesTween.interpolate_method(Outlines,"add_point", + origin,Vector2(origin.x,0),drawing_duration*0.5,Tween.TRANS_QUINT,Tween.EASE_OUT) + OutlinesTween.start() + yield(OutlinesTween,"tween_all_completed") + +func draw_v_grid(): + for p in x_chors.size(): + var point : Vector2 = origin+Vector2((p)*x_pass,0) + var v_grid : Line2D = Line2D.new() + Grid.add_child(v_grid) + v_grid.set_width(1) + v_grid.set_default_color(v_lines_color) + add_label(point+Vector2(-const_width/2*x_chors[p].length(),font_size/2), x_chors[p]) + GridTween.interpolate_method(v_grid,"add_point",point,point-Vector2(0,SIZE.y-OFFSET.y),drawing_duration/(x_chors.size()),Tween.TRANS_EXPO,Tween.EASE_OUT) + GridTween.start() + yield(GridTween,"tween_all_completed") + +func draw_h_grid(): + for p in y_chors.size(): + var point : Vector2 = origin-Vector2(0,(p)*y_pass) + var h_grid : Line2D = Line2D.new() + Grid.add_child(h_grid) + h_grid.set_width(1) + h_grid.set_default_color(h_lines_color) + add_label(point-Vector2(y_chors[p].length()*const_width+font_size,font_size/2), y_chors[p]) + GridTween.interpolate_method(h_grid,"add_point",point,Vector2(SIZE.x,point.y),drawing_duration/(y_chors.size()),Tween.TRANS_EXPO,Tween.EASE_OUT) + GridTween.start() + yield(GridTween,"tween_all_completed") + + +func add_label(point : Vector2, text : String): + var lbl : Label = Label.new() + if font != null: + lbl.set("custom_fonts/font",font) + lbl.set("custom_colors/font_color",font_color) + Grid.add_child(lbl) + lbl.rect_position = point + lbl.set_text(text) + +func draw_functions(): + for function in point_positions.size(): + draw_function(function,point_positions[function]) + +func draw_function(f_index : int, function : Array): + var pointv : Point + for point in function.size(): + pointv = point_node.instance() + Functions.add_child(pointv) + pointv.connect("_mouse_entered",self,"show_data") + pointv.connect("_mouse_exited",self,"hide_data") + pointv.connect("_point_pressed",self,"point_pressed") + pointv.create_point(point_shape, function_colors[f_index], Color.white, function[point], + pointv.format_value(point_values[f_index][point],false,false), + y_labels[point if invert_chart else f_index] as String) + yield(get_tree().create_timer(drawing_duration/function.size()), "timeout") + + +func read_data(source : String, delimiter : String): + 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: + content.erase(data) + return content + +func structure_data(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 build_chart(): + origin = Vector2(OFFSET.x,SIZE.y-OFFSET.y) + +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 + x_pass = (SIZE.x - OFFSET.x) / (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 invert_chart: + x_coordinates.append(x_pass*x) + else: + x_coordinates.append(x_datas[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_positions[function_value].append(Vector2(x_coordinates[function]+origin.x, origin.y-y_coordinates[function][function_value])) + point_values[function_value].append([x_datas[function_value],y_datas[function_value][function]]) + else: + point_positions[function].append(Vector2(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_values[y].append([x_datas[cluster],y_datas[cluster][y]]) + point_positions[y].append(Vector2(x_coordinates[cluster]+origin.x,origin.y-y_coordinates[cluster][y])) + else: + point_values[cluster].append([x_datas[y],y_datas[cluster][y]]) + point_positions[cluster].append(Vector2(x_coordinates[y]+origin.x,origin.y-y_coordinates[cluster][y])) + +func redraw(): + build_chart() + calculate_pass() + calculate_coordinates() + update() + + +func show_data(point): + DataTooltip.update_datas(point) + DataTooltip.show() + +func hide_data(): + DataTooltip.hide() + +func clean_points(): + function_colors.clear() + if Functions.get_children(): + for function in Functions.get_children(): + function.queue_free() + +func set_legend(l : Array): + legend = l + +func get_legend(): + return legend + +func invert_chart(): + invert_chart = !invert_chart + count_functions() + redraw() + create_legend() + +func count_functions(): + if are_values_columns: + if not invert_chart: + functions = datas[0].size()-1 + else: + functions = datas.size()-1 + else: + if invert_chart: + functions = datas[0].size()-1 + else: + functions = datas.size()-1 + +func create_legend(): + legend.clear() + for function in functions: + var function_legend = FunctionLegend.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) + +func apply_template(template_name : int): + template = template_name + templates = ECUtilities._load_templates() + if template_name!=null: + var custom_template = templates.get(templates.keys()[template_name]) + function_colors = custom_template.function_colors as PoolColorArray + 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) + property_list_changed_notify() + + if Engine.editor_hint: + _get_children() + Outlines.set_default_color(box_color) + Grid.get_node("VLine").set_default_color(v_lines_color) + Grid.get_node("HLine").set_default_color(h_lines_color) + + +func _enter_tree(): + _ready() diff --git a/addons/easy_charts/2DChart/ScatterChart2D/scatter_chart2D.tscn b/addons/easy_charts/2d_charts/ScatterChart2D/scatter_chart2D.tscn similarity index 72% rename from addons/easy_charts/2DChart/ScatterChart2D/scatter_chart2D.tscn rename to addons/easy_charts/2d_charts/ScatterChart2D/scatter_chart2D.tscn index 2ad509f..97a2105 100644 --- a/addons/easy_charts/2DChart/ScatterChart2D/scatter_chart2D.tscn +++ b/addons/easy_charts/2d_charts/ScatterChart2D/scatter_chart2D.tscn @@ -1,9 +1,14 @@ [gd_scene load_steps=3 format=2] +<<<<<<<< HEAD:addons/easy_charts/2DChart/ScatterChart2D/scatter_chart2D.tscn [ext_resource path="res://addons/easy_charts/2DChart/ScatterChart2D/scatter_chart2D.gd" type="Script" id=1] [ext_resource path="res://addons/easy_charts/Utilities/Point/point_data.tscn" type="PackedScene" id=2] +======== +[ext_resource path="res://addons/easy_charts/2d_charts/ScatterChart2D/scatter_chart2D.gd" type="Script" id=1] +[ext_resource path="res://addons/easy_charts/utilities/containers/data_tooltip/data_tooltip.tscn" type="PackedScene" id=2] +>>>>>>>> dev:addons/easy_charts/2d_charts/ScatterChart2D/scatter_chart2D.tscn [node name="ScatterChart2D" type="Node2D"] @@ -45,13 +50,19 @@ default_color = Color( 0.117647, 0.117647, 0.117647, 1 ) [node name="PointTween" type="Tween" parent="."] -[node name="PointData" parent="." instance=ExtResource( 2 )] +[node name="CanvasLayer" type="CanvasLayer" parent="."] -[node name="PointData" parent="PointData" index="0"] +[node name="PointData" parent="CanvasLayer" instance=ExtResource( 2 )] +margin_left = -172.972 +margin_top = -46.9003 +margin_right = -152.972 +margin_bottom = -36.9003 + +[node name="PointData" parent="CanvasLayer/PointData" index="0"] visible = false margin_left = 71.2491 margin_top = -146.673 margin_right = 71.1131 margin_bottom = -145.873 -[editable path="PointData"] +[editable path="CanvasLayer/PointData"] diff --git a/addons/easy_charts/3DChart/ScatterChart3D/scatter_chart3D.gd b/addons/easy_charts/3DChart/ScatterChart3D/scatter_chart3D.gd deleted file mode 100644 index f72e718..0000000 --- a/addons/easy_charts/3DChart/ScatterChart3D/scatter_chart3D.gd +++ /dev/null @@ -1,309 +0,0 @@ -tool -extends Spatial -class_name ScatterChart3D - -""" -[ScatterChart] - General purpose node for Scatter Charts - -A scatter plot (also called a scatterplot, scatter graph, scatter chart, scattergram, or scatter diagram) - is a type of plot or mathematical diagram using Cartesian coordinates to display values for typically two variables -for a set of data. If the points are coded (color/shape/size), one additional variable can be displayed. -The data are displayed as a collection of points, each having the value of one variable determining the position on -the horizontal axis and the value of the other variable determining the position on the vertical axis. - -/ source : Wikipedia / -""" - -onready var PlaceholderPoint = $Chart/Point -onready var Space = $ImmediateGeometry -onready var PointData = $PointData/PointData - -var point_node : PackedScene = preload("../Utilities/Point/Point.tscn") -var FunctionLegend : PackedScene = preload("../Utilities/Legend/function_legend.tscn") - -var font_size : float = 16 -var const_height : float = font_size/2*font_size/20 -var const_width : float = font_size/2 - -var OFFSET : Vector2 = Vector2(0,0) - -#-------------------------------------------------------------------------# -var origin : Vector2 - -# 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 - -# quantization, representing the interval in which values will be displayed - -# 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 - -# datas contained in file -var datas : Array - -# amount of functions to represent -var functions : int = 0 - -var x_label : String -var z_label : String - -# database values -var x_datas : Array -var z_datas : Array -var y_datas : Array - -# labels displayed on chart -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 (Vector2) var SIZE : Vector2 = Vector2() setget _set_size -export (String, FILE, "*.txt, *.csv") var source : String = "" -export (String) var delimiter : String = ";" -export (bool) var origin_at_zero : bool = true - -export (bool) var are_values_columns : bool = false -export (int,0,100) var x_values_index : int = 0 -export (int,0,100) var z_values_index : int = 0 -export(bool) var show_x_values_as_labels : bool = true - -#export (float,1,20,0.5) var column_width : float = 10 -#export (float,0,10,0.5) var column_gap : float = 2 - -export (float,0.1,10.0) var x_decim : float = 5.0 -export (float,0.1,10.0) var y_decim : float = 5.0 -export (int,"Dot,Triangle,Square") var point_shape : int = 0 -export (PoolColorArray) var function_colors = [Color("#1e1e1e")] -export (Color) var v_lines_color : Color = Color("#cacaca") -export (Color) var h_lines_color : Color = Color("#cacaca") - -export (bool) var boxed : bool = true -export (Color) var box_color : Color = Color("#1e1e1e") -export (Font) var font : Font -export (Font) var bold_font : Font -export (Color) var font_color : Color = Color("#1e1e1e") -export (String,"Default","Clean","Gradient","Minimal","Invert") var template : String = "Default" setget apply_template -export (float,0.1,1) var drawing_duration : float = 0.5 -export (bool) var invert_chart : bool = false - -var templates : Dictionary = {} - -signal chart_plotted(chart) -signal point_pressed(point) - -func _point_plotted(): - pass - -func _ready(): - pass - -func _set_size(size : Vector2): - SIZE = size -# build_chart() - -func clear(): - pass - -func load_font(): - if font != null: - font_size = font.get_height() - var theme : Theme = Theme.new() - theme.set_default_font(font) - PointData.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 _plot(source_ : String, delimiter_ : String, are_values_columns_ : bool, x_values_index_ : int): - randomize() - - clear() - - load_font() - PointData.hide() - - datas = read_datas(source_,delimiter_) -# count_functions() - structure_datas(datas,are_values_columns_,x_values_index_) -# build_chart() -# calculate_pass() -# calculate_coordinates() -# calculate_colors() -# draw_chart() -# -# create_legend() - emit_signal("chart_plotted", self) - -func plot(): - randomize() - - clear() - - load_font() - PointData.hide() - - if source == "" or source == null: - ECUtilities._print_message("Can't plot a chart without a Source file. Please, choose it in editor, or use the custom function _plot().",1) - return - datas = read_datas(source,delimiter) -# count_functions() - structure_datas(datas,are_values_columns,x_values_index) -# build_chart() -# calculate_pass() -# calculate_coordinates() -# calculate_colors() -# draw_chart() - -# create_legend() - emit_signal("chart_plotted", self) - - -func read_datas(source : String, delimiter : String): - 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: - content.erase(data) - return content - -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) - elif column == z_values_index: - var z_data = database[row][column] - if z_data.is_valid_float() or z_data.is_valid_integer(): - z_datas.append(z_data as float) - else: - z_datas.append(z_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()) - z_label = str(z_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 set_legend(l : Array): - legend = l - -func get_legend(): - return legend - -func apply_template(template_name : String): - template = template_name - templates = ECUtilities._load_templates() - if template_name!=null and template_name!="": - var custom_template = templates[template.to_lower()] - function_colors = custom_template.function_colors - 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) - property_list_changed_notify() - -func _enter_tree(): - _ready() diff --git a/addons/easy_charts/3d_charts/ScatterChart3D/scatter_chart3D.gd b/addons/easy_charts/3d_charts/ScatterChart3D/scatter_chart3D.gd new file mode 100644 index 0000000..93c2bb1 --- /dev/null +++ b/addons/easy_charts/3d_charts/ScatterChart3D/scatter_chart3D.gd @@ -0,0 +1,309 @@ +tool +extends Spatial +class_name ScatterChart3D + +""" +[ScatterChart] - General purpose node for Scatter Charts + +A scatter plot (also called a scatterplot, scatter graph, scatter chart, scattergram, or scatter diagram) + is a type of plot or mathematical diagram using Cartesian coordinates to display values for typically two variables +for a set of data. If the points are coded (color/shape/size), one additional variable can be displayed. +The data are displayed as a collection of points, each having the value of one variable determining the position on +the horizontal axis and the value of the other variable determining the position on the vertical axis. + +/ source : Wikipedia / +""" + +onready var PlaceholderPoint = $Chart/Point +onready var Space = $ImmediateGeometry +onready var DataTooltip = $DataTooltip/DataTooltip + +var point_node : PackedScene = preload("res://addons/easy_charts/utilities/components/point/point.gd") +var FunctionLegend : PackedScene = preload("res://addons/easy_charts/utilities/containers/legend/function_legend.gd") + +var font_size : float = 16 +var const_height : float = font_size/2*font_size/20 +var const_width : float = font_size/2 + +var OFFSET : Vector2 = Vector2(0,0) + +#-------------------------------------------------------------------------# +var origin : Vector2 + +# 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 + +# quantization, representing the interval in which values will be displayed + +# 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 + +# datas contained in file +var datas : Array + +# amount of functions to represent +var functions : int = 0 + +var x_label : String +var z_label : String + +# database values +var x_datas : Array +var z_datas : Array +var y_datas : Array + +# labels displayed on chart +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 (Vector2) var SIZE : Vector2 = Vector2() setget _set_size +export (String, FILE, "*.txt, *.csv") var source : String = "" +export (String) var delimiter : String = ";" +export (bool) var origin_at_zero : bool = true + +export (bool) var are_values_columns : bool = false +export (int,0,100) var x_values_index : int = 0 +export (int,0,100) var z_values_index : int = 0 +export(bool) var show_x_values_as_labels : bool = true + +#export (float,1,20,0.5) var column_width : float = 10 +#export (float,0,10,0.5) var column_gap : float = 2 + +export (float,0.1,10.0) var x_decim : float = 5.0 +export (float,0.1,10.0) var y_decim : float = 5.0 +export (int,"Dot,Triangle,Square") var point_shape : int = 0 +export (PoolColorArray) var function_colors = [Color("#1e1e1e")] +export (Color) var v_lines_color : Color = Color("#cacaca") +export (Color) var h_lines_color : Color = Color("#cacaca") + +export (bool) var boxed : bool = true +export (Color) var box_color : Color = Color("#1e1e1e") +export (Font) var font : Font +export (Font) var bold_font : Font +export (Color) var font_color : Color = Color("#1e1e1e") +export (String,"Default","Clean","Gradient","Minimal","Invert") var template : String = "Default" setget apply_template +export (float,0.1,1) var drawing_duration : float = 0.5 +export (bool) var invert_chart : bool = false + +var templates : Dictionary = {} + +signal chart_plotted(chart) +signal point_pressed(point) + +func _point_plotted(): + pass + +func _ready(): + pass + +func _set_size(size : Vector2): + SIZE = size +# build_chart() + +func clear(): + pass + +func load_font(): + if font != null: + font_size = font.get_height() + var theme : Theme = Theme.new() + theme.set_default_font(font) + DataTooltip.set_theme(theme) + else: + var lbl = Label.new() + font = lbl.get_font("") + lbl.free() + if bold_font != null: + DataTooltip.Data.set("custom_fonts/font",bold_font) + +func _plot(source_ : String, delimiter_ : String, are_values_columns_ : bool, x_values_index_ : int): + randomize() + + clear() + + load_font() + DataTooltip.hide() + + datas = read_data(source_,delimiter_) +# count_functions() + structure_data(datas,are_values_columns_,x_values_index_) +# build_chart() +# calculate_pass() +# calculate_coordinates() +# calculate_colors() +# draw_chart() +# +# create_legend() + emit_signal("chart_plotted", self) + +func plot(): + randomize() + + clear() + + load_font() + DataTooltip.hide() + + if source == "" or source == null: + ECUtilities._print_message("Can't plot a chart without a Source file. Please, choose it in editor, or use the custom function _plot().",1) + return + datas = read_data(source,delimiter) +# count_functions() + structure_data(datas,are_values_columns,x_values_index) +# build_chart() +# calculate_pass() +# calculate_coordinates() +# calculate_colors() +# draw_chart() + +# create_legend() + emit_signal("chart_plotted", self) + + +func read_data(source : String, delimiter : String): + 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: + content.erase(data) + return content + +func structure_data(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) + elif column == z_values_index: + var z_data = database[row][column] + if z_data.is_valid_float() or z_data.is_valid_integer(): + z_datas.append(z_data as float) + else: + z_datas.append(z_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()) + z_label = str(z_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 set_legend(l : Array): + legend = l + +func get_legend(): + return legend + +func apply_template(template_name : String): + template = template_name + templates = ECUtilities._load_templates() + if template_name!=null and template_name!="": + var custom_template = templates[template.to_lower()] + function_colors = custom_template.function_colors + 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) + property_list_changed_notify() + +func _enter_tree(): + _ready() diff --git a/addons/easy_charts/3DChart/ScatterChart3D/scatter_chart3D.tscn b/addons/easy_charts/3d_charts/ScatterChart3D/scatter_chart3D.tscn similarity index 66% rename from addons/easy_charts/3DChart/ScatterChart3D/scatter_chart3D.tscn rename to addons/easy_charts/3d_charts/ScatterChart3D/scatter_chart3D.tscn index e784140..1e60c58 100644 --- a/addons/easy_charts/3DChart/ScatterChart3D/scatter_chart3D.tscn +++ b/addons/easy_charts/3d_charts/ScatterChart3D/scatter_chart3D.tscn @@ -1,9 +1,14 @@ [gd_scene load_steps=7 format=2] +<<<<<<<< HEAD:addons/easy_charts/3DChart/ScatterChart3D/scatter_chart3D.tscn [ext_resource path="res://addons/easy_charts/3DChart/ScatterChart3D/scatter_chart3D.gd" type="Script" id=1] [ext_resource path="res://addons/easy_charts/Utilities/Point/point_data.tscn" type="PackedScene" id=2] +======== +[ext_resource path="res://addons/easy_charts/3d_charts/ScatterChart3D/scatter_chart3D.gd" type="Script" id=1] +[ext_resource path="res://addons/easy_charts/utilities/containers/data_tooltip/data_tooltip.tscn" type="PackedScene" id=2] +>>>>>>>> dev:addons/easy_charts/3d_charts/ScatterChart3D/scatter_chart3D.tscn [sub_resource type="SpatialMaterial" id=1] @@ -43,13 +48,19 @@ near = 0.01 [node name="OmniLight" type="OmniLight" parent="."] transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 9, 0 ) -[node name="PointData" parent="." instance=ExtResource( 2 )] +[node name="CanvasLayer" type="CanvasLayer" parent="."] -[node name="PointData" parent="PointData" index="0"] +[node name="DataTooltip" parent="CanvasLayer" instance=ExtResource( 2 )] +margin_left = 277.135 +margin_top = 449.025 +margin_right = 276.999 +margin_bottom = 449.825 + +[node name="PointData" parent="CanvasLayer/DataTooltip" index="0"] visible = false margin_left = 47.2074 margin_top = -150.915 margin_right = 47.0714 margin_bottom = -150.115 -[editable path="PointData"] +[editable path="CanvasLayer/DataTooltip"] diff --git a/addons/easy_charts/ControlChart/PieChart/pie_chart.gd b/addons/easy_charts/ControlChart/PieChart/pie_chart.gd deleted file mode 100644 index 1192ea0..0000000 --- a/addons/easy_charts/ControlChart/PieChart/pie_chart.gd +++ /dev/null @@ -1,226 +0,0 @@ -tool -extends Chart -class_name PieChart - -var should_draw : bool = false -var area_angles : Array -var slices : Array -var areas : Array -var areas_interacted : Array - -class CustomSorter: - static func sort_ascending(a,b): - if a[1] < b[1]: - return true - return false - -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 - }, - - # Chart Style - { - "hint": PROPERTY_HINT_NONE, - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Style/function_colors", - "type": TYPE_COLOR_ARRAY - }, - { - "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(ECUtilities.templates.keys()).join(","), - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Style/template", - "type": TYPE_INT - }, - - # Chart Modifiers - { - "hint": PROPERTY_HINT_RANGE, - "hint_string": "0,360", - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Modifiers/rotation", - "type": TYPE_REAL - }, - ] - - - -# Called when the node enters the scene tree for the first time. -func _ready(): - pass - -func plot_placeholder() -> void: - data = [ - ["United States",46], - ["Great Britain",27], - ["China",26], - ["Russia",19], - ["Germany",17] - ] - - function_colors = [ - Color.red, - Color.white, - Color.yellow, - Color.green, - Color.blue - ] - plot_from_array(data) - -func structure_datas(database: Array): - # @labels_index can be either a column or a row relative to x values - clean_variables() - are_values_columns = invert_chart != are_values_columns - if are_values_columns: - for row in database.size(): - var t_vals: Array - for column in database[row].size(): - if column == labels_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()) - else: - for row in database.size(): - if row == labels_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 - - -func build_chart(): - SIZE = get_size() - origin = SIZE/2 - radius = (SIZE.y/2 - 20) if SIZE.y < SIZE.x else (SIZE.x/2 - 20) - -func calculate_pass(): - var tot : float - for y_data in y_datas: tot+=y_data[0] - x_pass = 360/tot - -func calculate_coordinates(): - area_angles.clear() - slices.clear() - areas.clear() - var from : float = 0.0 - var to : float = y_datas[0][0]*x_pass - area_angles.append([from,to]) - for info in range(y_datas.size()): - slices.append(Slice.new(y_labels[info], str(y_datas[info][0]), from, to, x_label+" : "+x_datas[0], function_colors[info])) - areas.append(calculate_circle_arc_polygon(origin, radius, from + rotation, to + rotation, function_colors[info])) - from = to - to = (y_datas[info+1][0]*x_pass + from) if info < y_datas.size()-1 else (360) - area_angles.append([from, to]) - - create_legend() - -func calculate_circle_arc_polygon(center : Vector2, radius : float, angle_from : float, angle_to : float, color : Color) -> PoolVector2Array: - var nb_points : int = 32 - var points_arc : PoolVector2Array = PoolVector2Array() -# var chord_angle : float = ((angle_to - angle_from)/2)+angle_from -# angle_from += 0.2 -# angle_to -= 0.2 -# var displacement : Vector2 = Vector2(cos(deg2rad(chord_angle)), sin(deg2rad(chord_angle-180))).normalized()*10 -# print(displacement) -# center += displacement -# radius+=displacement.length() - points_arc.push_back(center) - var colors : PoolColorArray = PoolColorArray([color]) - for i in range(nb_points + 1): - var angle_point = deg2rad(angle_from + i * (angle_to - angle_from) / nb_points - 90) - points_arc.push_back(center + Vector2(cos(angle_point), sin(angle_point)) * radius) - return points_arc - -func _draw(): - _draw_areas() - - if mouse_on_slice: - _draw_arc(area_angles[mouse_on_area], mouse_on_area) - mouse_on_slice = false - -func _draw_arc(arc : Array, index : int): - var temp_color : Color = function_colors[index] - temp_color.a = 0.7 - draw_arc(origin, radius + 6, deg2rad(arc[0]-90 + rotation), deg2rad(arc[1]-90 + rotation), 32, temp_color, 4) - -func _draw_areas(): - for area_idx in range(areas.size()): - draw_polygon(areas[area_idx], [function_colors[area_idx]]) - -var mouse_on_area : int -var mouse_on_slice : bool = false - -func _gui_input(event : InputEvent): - if event is InputEventMouseMotion: - for area_idx in range(areas.size()): - if Geometry.is_point_in_polygon(event.global_position - get_global_transform().origin, areas[area_idx]): - mouse_on_slice = true - mouse_on_area = area_idx - show_slice_data(slices[area_idx]) - update() - - if not mouse_on_slice: - mouse_on_area = -1 - mouse_on_slice = false - hide_data() - update() diff --git a/addons/easy_charts/ControlChart/RadarChart/radar_chart.gd b/addons/easy_charts/ControlChart/RadarChart/radar_chart.gd deleted file mode 100644 index f266c96..0000000 --- a/addons/easy_charts/ControlChart/RadarChart/radar_chart.gd +++ /dev/null @@ -1,287 +0,0 @@ -tool -extends Chart -class_name RadarChart - -""" -[RadarChart] - General purpose node for Radar Charts -A radar chart is a graphical method of displaying multivariate data in the form -of a two-dimensional chart of three or more quantitative variables represented on axes -starting from the same point. The relative position and angle of the axes is typically -uninformative, but various heuristics, such as algorithms that plot data as the maximal -total area, can be applied to sort the variables (axes) into relative positions that reveal -distinct correlations, trade-offs, and a multitude of other comparative measures. - -/ 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_RANGE, - "hint_string": "-1,100,1", - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Properties/function_names_index", - "type": TYPE_INT - }, - { - "hint": PROPERTY_HINT_NONE, - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Properties/use_height_as_radius", - "type": TYPE_BOOL - }, - { - "hint": PROPERTY_HINT_RANGE, - "hint_string": "0,2000", - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Properties/radius", - "type": TYPE_REAL - }, - - # Chart Display - { - "hint": PROPERTY_HINT_RANGE, - "hint_string": "0.1,100", - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Display/full_scale", - "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/outline_color", - "type": TYPE_COLOR - }, - { - "hint": PROPERTY_HINT_NONE, - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Style/grid_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(ECUtilities.templates.keys()).join(","), - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Style/template", - "type": TYPE_INT - }, - { - "hint": PROPERTY_HINT_RANGE, - "hint_string": "0,360", - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Modifiers/rotation", - "type": TYPE_REAL - }, - ] - -func structure_datas(database : Array): - # @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 - are_values_columns = invert_chart != are_values_columns - match are_values_columns: - true: - for row in database.size(): - var t_row : Array = [] - for column in database[row].size(): - if row == labels_index: - if column == function_names_index: - pass - else: - x_labels.append(database[row][column]) - else: - if column == function_names_index: - y_labels.append(database[row][column]) - else: - if typeof(database[row][column]) == TYPE_INT or typeof(database[row][column]) == TYPE_REAL: - t_row.append(database[row][column] as float) - else: - t_row.append(database[row][column].replace(",", ".") as float) - if not t_row.empty(): - x_datas.append(t_row) - false: - for row in database.size(): - if row == function_names_index: - y_labels = database[row] as PoolStringArray - - var x_temp_datas : PoolRealArray = [] - for column in database[row].size(): - if column == labels_index: - x_labels.append(database[row][column] as String) - else: - x_temp_datas.append(database[row][column] as float) - x_datas.append(x_temp_datas) - - - if labels_index == -1 : - for data in x_datas[0].size(): - x_labels.append("Element %s" % data) - - if function_names_index == -1 : - for data in x_datas.size(): - y_labels.append("Function %s" % data) - -func build_chart(): - SIZE = get_size() - origin = OFFSET + SIZE/2 - -var radar_polygon : Array - -func calculate_pass() : - var ordered_max : Array - for data in x_datas : - var ordered_data : Array = data.duplicate() - ordered_data.sort() - ordered_max.append(ordered_data.pop_back()) - ordered_max.sort() - var max_value : float = ordered_max.pop_back() - var dist = full_scale * pow(10.0,str(max_value).length()-2) - var multi = 0 - var value = dist * multi - x_chors.append(value as String) - while value < max_value: - multi+=1 - value = dist * multi - x_chors.append(value as String) - -func calculate_coordinates(): - for chor in x_chors.size(): - var inner_polyline : PoolVector2Array - var scalar_factor : float = (x_chors[chor] as float/x_chors.back() as float) - for function in functions: - var angle : float = ((2 * PI * function) / functions) - PI /2 + deg2rad(rotation) - var x_coordinate : float = (radius if (not use_height_as_radius and radius void: - if data_matrix.get_size()[1] == header.size(): header.insert(0,"") - if index.empty() : for value in range(data_matrix.get_size().x) : index.append("f%s"%value) - self._data_matrix = data_matrix - self._index = index - self._header = header - self._dataset = build_dataset(data_matrix.to_array(), index, header) - self._dataframe = build_dataframe_from_matrix(data_matrix, index, header) - -func build_dataset(data : Array, index : PoolStringArray, header : PoolStringArray) -> Array: - var dataset : Array = [Array(header)] - for i in range(index.size()): - var set : Array = data[i].duplicate() - set.insert(0, index[i]) - dataset.append(set) - return dataset - -func build_dataframe(data : Array, index : PoolStringArray, header : PoolStringArray) -> Array: - var dataframe : Array = [Array(header)] - for row_i in range(data.size()): dataframe.append([index[row_i]]+data[row_i]) - return dataframe - -func build_dataframe_from_matrix(data_matrix : Matrix, index : PoolStringArray, header : PoolStringArray) -> Array: - 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 - -func get_dataset() -> Array: - return _dataset - -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 = "" - 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]] - string+="\n" - return string - -# ............................................................................... -func get_column_h(header : String) -> Array: - var header_i : int = -1 - var array : Array = [] - for header_ix in range(_dataframe[0].size()): - if _dataframe[0][header_ix] == header: header_i = header_ix; continue - if header_i!=-1: for row in _dataframe: array.append(row[header_i]) - return array - -func get_row_i(index : String) -> Array: - var index_i : int - for row in _dataframe: if row[0] == index: return row - return [] - -func _get(_property : String): - if _property.split(";").size() == 2: - var property : PoolStringArray = _property.split(";") - pass - elif _property.split(":").size() == 2: - var property : PoolStringArray = _property.split(":") - if int(property[0]) == 0: return get_row_i(property[1]) - else: return get_column_h(property[1]) - - - - - diff --git a/addons/easy_charts/Utilities/Data/matrix.gd b/addons/easy_charts/Utilities/Data/matrix.gd deleted file mode 100644 index 659649c..0000000 --- a/addons/easy_charts/Utilities/Data/matrix.gd +++ /dev/null @@ -1,61 +0,0 @@ -tool -extends Reference -class_name Matrix - -var _matrix : Array = [] - -func _init(matrix : Array = []) -> void: - _matrix = matrix - -func _to_string() -> String: - var last_string_len : int - for row in _matrix: - 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" - for row_i in _matrix.size(): - for column_i in _matrix[row_i].size(): - string+="%*s" % [last_string_len+1 if column_i!=0 else last_string_len, _matrix[row_i][column_i]] - string+="\n" - 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() - -func get_size() -> Vector2: - return Vector2(_matrix.size(), _matrix[0].size()) - -func get_column(column : int) -> Array: - if column >= get_size()[1]: printerr("error") - var column_array : Array = [] - for row in _matrix: column_array.append(row[column]) - return column_array - -func get_row(row : int) -> Array: - if row >= get_size()[0]: printerr("error") - return _matrix[row] -# -#func multiply_int(_int : int) -> void: -# _matrix = MatrixGenerator.multiply_int(self, _int).to_array() -# -#func multiply_float(_float : int) -> void: -# _matrix = MatrixGenerator.multiply_float(self, _float).to_array() - - - - - - - - - diff --git a/addons/easy_charts/Utilities/Legend/color_label.tscn b/addons/easy_charts/Utilities/Legend/color_label.tscn deleted file mode 100644 index 12f3ccb..0000000 --- a/addons/easy_charts/Utilities/Legend/color_label.tscn +++ /dev/null @@ -1,12 +0,0 @@ -[gd_scene load_steps=2 format=2] - -[ext_resource path="res://addons/easy_charts/ChartContainer.gd" type="Script" id=1] - -[node name="Control" type="ColorRect"] -margin_right = 15.0 -margin_bottom = 3.0 -rect_min_size = Vector2( 15, 3 ) -script = ExtResource( 1 ) -__meta__ = { -"_edit_use_anchors_": false -} diff --git a/addons/easy_charts/Utilities/Point/point_data.gd b/addons/easy_charts/Utilities/Point/point_data.gd deleted file mode 100644 index 180be55..0000000 --- a/addons/easy_charts/Utilities/Point/point_data.gd +++ /dev/null @@ -1,69 +0,0 @@ -tool -extends PanelContainer -class_name PointData - -var value : String = "" -var position : Vector2 = Vector2() - -var OFFSET : Vector2 = Vector2(15,35) -var GAP : Vector2 = Vector2(0,15) - -onready var Data : Label = $PointData/Value/x -onready var Value : Label = $PointData/Value/y -onready var Function : Label = $PointData/Function - -func _ready(): - hide() - -func _process(delta): - if get_global_mouse_position().y > OFFSET.y + GAP.y: - rect_position = get_global_mouse_position() - OFFSET - GAP - else: - rect_position = get_global_mouse_position() + GAP*5 - OFFSET - -func update_datas(point : Control): - update_size() - - get("custom_styles/panel").set("bg_color",point.color) - - var font_color : Color - if point.color.g < 0.75: - font_color = Color(1,1,1,1) - else: - font_color = Color(0,0,0,1) - Data.set("custom_colors/font_color",font_color) - Value.set("custom_colors/font_color",font_color) - Function.set("custom_colors/font_color",font_color) - get("custom_styles/panel").set("border_color",font_color) - - Data.set_text(point.point_value[0]+":") - Value.set_text(point.point_value[1]) - Function.set_text(point.function) - update() - show() - -func update_slice_datas(slice : Slice): - update_size() - - get("custom_styles/panel").set("bg_color",slice.color) - - var font_color : Color - if slice.color.g < 0.75: - font_color = Color(1,1,1,1) - else: - font_color = Color(0,0,0,1) - Data.set("custom_colors/font_color",font_color) - Value.set("custom_colors/font_color",font_color) - Function.set("custom_colors/font_color",font_color) - get("custom_styles/panel").set("border_color",font_color) - - Data.set_text(slice.x_value+":") - Value.set_text(slice.y_value) - Function.set_text(slice.function) - update() - show() - -func update_size(): - OFFSET.x = get_size().x/2 - OFFSET.y = get_size().y - GAP.y = OFFSET.y/3 diff --git a/addons/easy_charts/Utilities/Point/point_data.tscn b/addons/easy_charts/Utilities/Point/point_data.tscn deleted file mode 100644 index fb13e30..0000000 --- a/addons/easy_charts/Utilities/Point/point_data.tscn +++ /dev/null @@ -1,85 +0,0 @@ -[gd_scene load_steps=3 format=2] - -[ext_resource path="res://addons/easy_charts/Utilities/Point/point_data.gd" type="Script" id=1] - - -[sub_resource type="StyleBoxFlat" id=1] -content_margin_left = 10.0 -content_margin_right = 10.0 -content_margin_top = 5.0 -content_margin_bottom = 5.0 -bg_color = Color( 1, 1, 1, 0 ) -border_width_left = 2 -border_width_top = 2 -border_width_right = 2 -border_width_bottom = 2 -border_color = Color( 1, 1, 1, 1 ) -corner_radius_top_left = 5 -corner_radius_top_right = 5 -corner_radius_bottom_right = 5 -corner_radius_bottom_left = 5 -corner_detail = 20 - -[node name="CanvasLayer" type="CanvasLayer"] - -[node name="PointData" type="PanelContainer" parent="."] -visible = false -anchor_right = 0.0694688 -anchor_bottom = 0.067 -margin_left = 79.7858 -margin_top = -250.75 -margin_right = 79.6496 -margin_bottom = -249.95 -grow_horizontal = 2 -mouse_filter = 2 -custom_styles/panel = SubResource( 1 ) -script = ExtResource( 1 ) -__meta__ = { -"_edit_use_anchors_": true -} - -[node name="PointData" type="VBoxContainer" parent="PointData"] -margin_left = 10.0 -margin_top = 5.0 -margin_right = 61.0 -margin_bottom = 36.0 -grow_horizontal = 2 -size_flags_horizontal = 3 -custom_constants/separation = 3 -alignment = 1 -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="Function" type="Label" parent="PointData/PointData"] -margin_right = 51.0 -margin_bottom = 14.0 -size_flags_horizontal = 3 -align = 1 -valign = 1 - -[node name="Value" type="HBoxContainer" parent="PointData/PointData"] -margin_top = 17.0 -margin_right = 51.0 -margin_bottom = 31.0 -grow_horizontal = 2 -size_flags_horizontal = 6 -alignment = 1 - -[node name="x" type="Label" parent="PointData/PointData/Value"] -margin_right = 39.0 -margin_bottom = 14.0 -size_flags_horizontal = 3 -custom_colors/font_color = Color( 1, 1, 1, 1 ) -text = "Value:" -align = 2 -valign = 1 - -[node name="y" type="Label" parent="PointData/PointData/Value"] -margin_left = 43.0 -margin_right = 51.0 -margin_bottom = 14.0 -size_flags_horizontal = 3 -custom_colors/font_color = Color( 1, 1, 1, 1 ) -text = "0" -valign = 1 diff --git a/addons/easy_charts/Utilities/Rect/.gdignore b/addons/easy_charts/Utilities/Rect/.gdignore deleted file mode 100644 index e69de29..0000000 diff --git a/addons/easy_charts/Utilities/Scripts/chart.gd b/addons/easy_charts/Utilities/classes/base/chart.gd similarity index 86% rename from addons/easy_charts/Utilities/Scripts/chart.gd rename to addons/easy_charts/Utilities/classes/base/chart.gd index eac36fd..ba4ab21 100644 --- a/addons/easy_charts/Utilities/Scripts/chart.gd +++ b/addons/easy_charts/Utilities/classes/base/chart.gd @@ -9,17 +9,14 @@ signal chart_plotted(chart) # emit when a chart is plotted (static) or updated ( signal point_pressed(point) # Onready Vars ............................ -onready var PointData = $PointData/PointData +onready var data_tooltip = $CanvasLayer/DataTooltip 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/function_legend.tscn") - -# Enums ..................................... -enum PointShapes { Dot, Triangle, Square, Cross } +var point_node : PackedScene = preload("../../components/point/point.tscn") +var legend_element : PackedScene = preload("../../containers/legend/function_legend.tscn") # Shared Variables ......................... var SIZE : Vector2 = Vector2() @@ -97,7 +94,7 @@ var full_scale : float = 1.0 setget set_full_scale var x_decim : float = 1.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 points_shape : Array = [Point.SHAPES.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 @@ -303,7 +300,74 @@ func _set(property, value): return true # .......................... Shared Functions and virtuals ........................ -func slice_data() -> Array: + +# Structure and Display a new plot if a dataset source is given +# both through APIs or from Inspector +func plot(_dataset: Array = read_data(source, delimiter)) -> void: + clean_variables() + clean_points() + load_font() + data_tooltip.hide() + + if source == "" or source == null: + ECUtilities._print_message("Can't plot a chart without a Source file. Please, choose it in editor, or use the custom function _plot().",1) + return + + if _dataset.empty(): + ECUtilities._print_message("Can't plot a chart with an empty Array.",1) + return + + data = _dataset.duplicate(true) + structure_data(slice_data(data)) + compute_display() + display_plot() + emit_signal("chart_plotted",self) + if not is_connected("item_rect_changed",self, "redraw"): connect("item_rect_changed", self, "redraw") + +func plot_from_source(file : String, _delimiter : String = delimiter) -> void: + plot(read_data(file, _delimiter)) + +func plot_from_dataframe(dataframe : DataFrame) -> void: + plot(dataframe.get_dataframe()) + +func plot_placeholder() -> void: + pass + +# Append new data (in array format) to the already plotted data. +# The new data will be appended as a new row of the dataset. +# All data are stored. +func update_plot(new_data : Array) -> void: + if new_data.empty(): + ECUtilities._print_message("Can't plot a chart with an empty Array.",1) + return + + data.append(new_data.duplicate(true)) + plot(data) + +# Append a new column to data +func append_new_column(dataset : Array, column : Array): + if column.empty(): + ECUtilities._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]) + +# ...................... Dataset Manipulation Functions ......................... + +func read_data(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.duplicate(true) + +func slice_data(data: Array) -> Array: var data_to_display : Array data_to_display.resize(data.size()) if only_disp_values == Vector2(0,0) : @@ -321,122 +385,19 @@ func slice_data() -> Array: data_to_display = data.duplicate(true) return data_to_display -func plot(): - load_font() - PointData.hide() - - if source == "" or source == null: - ECUtilities._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()) +# .................. Display and Draw functions ....................... +func compute_display(): + count_functions() + calculate_colors() + set_shapes() + create_legend() + +func display_plot(): 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): - clean_variables() - clear_points() - load_font() - PointData.hide() - - if csv_file == "" or csv_file == null: - ECUtilities._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()) - 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(): - ECUtilities._print_message("Can't plot a chart with an empty Array.",1) - return - - data = array.duplicate(true) - structure_datas(slice_data()) - 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() - PointData.hide() - - data = dataframe.get_dataframe().duplicate(true) - - if data.empty(): - ECUtilities._print_message("Can't plot a chart with an empty Array.",1) - return - - structure_datas(slice_data()) - 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(): - ECUtilities._print_message("Can't plot a chart with an empty Array.",1) - return - - data.append(array) - structure_datas(slice_data()) - 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(): - ECUtilities._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 +# ................................. Helper Functions ................................. func load_font(): if font != null: @@ -449,7 +410,13 @@ func load_font(): font = lbl.get_font("") lbl.free() if bold_font != null: - PointData.Data.set("custom_fonts/font",bold_font) + data_tooltip.Data.set("custom_fonts/font", bold_font) + else: + bold_font = font + +func count_functions(): + if are_values_columns: functions = data[0].size()-1 + else: functions = y_datas.size() func calculate_colors(): if function_colors.size() < functions: @@ -458,37 +425,31 @@ func calculate_colors(): func set_shapes(): if points_shape.empty() or points_shape.size() < functions: for function in functions: - points_shape.append(PointShapes.Dot) + points_shape.append(Point.SHAPES.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 +# Create the legend of the current plot +func create_legend(): + for function in functions: + var function_legend : LegendElement + if legend.size() > function: + function_legend = legend[function] + else: + function_legend = legend_element.instance() + legend.append(function_legend) + var f_name : String = y_labels[function] if not are_values_columns else str(x_datas[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) -func count_functions(): - if are_values_columns: functions = data[0].size()-1 - else: functions = y_datas.size() - -func clear_points(): +func clean_points(): for function in Points.get_children(): function.free() for legend in Legend.get_children(): legend.free() -func redraw(): - build_chart() - calculate_pass() - calculate_coordinates() - update() - func clean_variables(): x_chors.clear() y_chors.clear() @@ -499,37 +460,29 @@ func clean_variables(): y_labels.clear() # .................. VIRTUAL FUNCTIONS ......................... -func structure_datas(database : Array): +func calculate_tics(): pass +# Structure the dataset in order to be plotted +func structure_data(database : Array): + pass + +# Calculate borders, size and origin in order to display the plot func build_chart(): pass +# Calculate the pass, necessary to correctly draw the points func calculate_pass(): pass +# Calculate Points' coordinates in order to display them func calculate_coordinates(): pass +# Calculate or assign to each function a color 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] if not are_values_columns else str(x_datas[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: @@ -720,12 +673,12 @@ func point_pressed(point : Point): emit_signal("point_pressed",point) func show_data(point : Point): - PointData.update_datas(point) - PointData.show() + data_tooltip.update_datas(point) + data_tooltip.show() func hide_data(): - PointData.hide() + data_tooltip.hide() func show_slice_data(slice : Slice): - PointData.update_slice_datas(slice) - PointData.show() + data_tooltip.update_slice_datas(slice) + data_tooltip.show() diff --git a/addons/easy_charts/Utilities/Scripts/chart2d.gd b/addons/easy_charts/Utilities/classes/base/chart2d.gd similarity index 92% rename from addons/easy_charts/Utilities/Scripts/chart2d.gd rename to addons/easy_charts/Utilities/classes/base/chart2d.gd index 5cfc393..6dbe3e0 100644 --- a/addons/easy_charts/Utilities/Scripts/chart2d.gd +++ b/addons/easy_charts/Utilities/classes/base/chart2d.gd @@ -5,7 +5,6 @@ class_name Chart2D enum PointShapes { Dot, Triangle, Square, Cross } enum TemplatesNames { Default, Clean, Gradient, Minimal, Invert } - signal chart_plotted(chart) signal point_pressed(point) @@ -47,12 +46,12 @@ var FunctionsTween: Tween var PointTween : Tween var Functions: Node2D var GridTween: Tween -var PointData: PointData +var DataTooltip: DataTooltip var Outlines: Line2D var Grid: Node2D -var point_node: PackedScene = preload("../Point/point.tscn") -var FunctionLegend: PackedScene = preload("../Legend/function_legend.tscn") +var point_node: PackedScene = preload("res://addons/easy_charts/utilities/components/point/point.tscn") +var FunctionLegend: PackedScene = preload("res://addons/easy_charts/utilities/containers/legend/function_legend.tscn") var font_size: float = 16 var const_height: float = font_size / 2 * font_size / 20 @@ -129,7 +128,7 @@ func _get_children(): FunctionsTween = $FunctionsTween Functions = $Functions GridTween = $GridTween - PointData = $PointData/PointData + DataTooltip = $DataTooltip/DataTooltip Outlines = $Outlines Grid = $Grid @@ -144,7 +143,6 @@ func apply_template(template_name: int): box_color = Color(custom_template.outline_color) font_color = Color(custom_template.font_color) property_list_changed_notify() - if Engine.editor_hint: _get_children() Outlines.set_default_color(box_color) @@ -159,15 +157,15 @@ func redraw(): func show_data(point): - PointData.update_datas(point) - PointData.show() + DataTooltip.update_datas(point) + DataTooltip.show() func hide_data(): - PointData.hide() + DataTooltip.hide() -func clear_points(): +func clean_points(): function_colors.clear() if Functions.get_children(): for function in Functions.get_children(): diff --git a/addons/easy_charts/Utilities/Scripts/scatter_chart_base.gd b/addons/easy_charts/Utilities/classes/base/scatter_chart_base.gd similarity index 96% rename from addons/easy_charts/Utilities/Scripts/scatter_chart_base.gd rename to addons/easy_charts/Utilities/classes/base/scatter_chart_base.gd index 40b32d3..e6eb9bb 100644 --- a/addons/easy_charts/Utilities/Scripts/scatter_chart_base.gd +++ b/addons/easy_charts/Utilities/classes/base/scatter_chart_base.gd @@ -9,13 +9,13 @@ class_name ScatterChartBase var x_domain := [[], []] var y_domain := [[], []] -var x_range := [0, 0] -var y_range := [0, 0] +var x_range : PoolRealArray = [0, 0] +var y_range : PoolRealArray = [0, 0] var autoscale_x = true var autoscale_y = true -var property_list = [] +var property_list: Array = [] func _init(): @@ -125,7 +125,7 @@ func build_property_list(): "hint": 24, "hint_string": ("%d/%d:%s" %[TYPE_INT, PROPERTY_HINT_ENUM, - PoolStringArray(PointShapes.keys()).join(",")]), + PoolStringArray(Point.SHAPES.keys()).join(",")]), "name": "Chart_Style/points_shape", "type": TYPE_ARRAY, "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE @@ -272,30 +272,12 @@ func _get(property): "Chart_Display/max_y_range": return y_range[1] - -func plot(): - # Overwrites the method on Chart to make a reusable piece to be used internally - # to do all calculations needed to replot. - calculate_tics() - 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_function(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 - load_font() - PointData.hide() + data_tooltip.hide() var id := "" if x.empty() or y.empty(): @@ -381,7 +363,7 @@ func generate_identifier(): return "f%d" % (y_labels.size() + 1) -func structure_datas(database : Array): +func structure_data(database : Array): # @labels_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 @@ -540,12 +522,14 @@ func count_functions(): functions = y_labels.size() +# Calculate distance in pixel between 2 consecutive values/datas 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 - ChartName.get_rect().size.y * 2) / (y_chors.size() - 1 if y_chors.size() > 1 else y_chors.size()) +# Calculate all Points' coordinates in the dataset +# and display them inside the chart func calculate_coordinates(): point_values.clear() point_positions.clear() @@ -563,8 +547,7 @@ func calculate_coordinates(): point_values[function].append([x_datas[function][val], y_datas[function][val]]) point_positions[function].append(Vector2(value_x + origin.x, origin.y - value_y)) - - +# Draw the grid lines for the chart func draw_grid(): # ascisse for p in x_chors.size(): @@ -590,13 +573,15 @@ func draw_grid(): size_text.y / 2), y_chors[p], font_color) +# Draw chart outlines containing the current plot 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) - +# Draw the points using their coordinates and information, +# inside a PointContainer func draw_points(): for function in point_values.size(): var PointContainer : Control = Control.new() @@ -615,7 +600,7 @@ func draw_points(): PointContainer.add_child(point) - +# Draw the tresholds (if set) func draw_treshold(): if v_dist != 0: treshold_draw = Vector2((treshold.x * x_pass) + origin.x ,origin.y - ((treshold.y * y_pass)/v_dist)) diff --git a/addons/easy_charts/Utilities/classes/structures/data_frame.gd b/addons/easy_charts/Utilities/classes/structures/data_frame.gd new file mode 100644 index 0000000..71782a5 --- /dev/null +++ b/addons/easy_charts/Utilities/classes/structures/data_frame.gd @@ -0,0 +1,190 @@ +tool +extends Resource +class_name DataFrame + +var table_name : String = "" +var labels : PoolStringArray = [] +var headers : PoolStringArray = [] +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(ECUtilities.get_letter_index(header)) + build_dataframe(datamatrix, headers, labels, table_name) + +func build_dataframe(datamatrix : Matrix, headers : PoolStringArray = [], labels : PoolStringArray = [] , table_name : String = "") -> void: + self.datamatrix = datamatrix + self.table_name = table_name + self.labels = labels + self.headers = headers + self.dataset = build_dataset_from_matrix(datamatrix, headers, labels) + +func build_dataset(data : Array, headers : PoolStringArray, labels : PoolStringArray) -> Array: + var dataset : Array = [Array(headers)] + for row_i in range(data.size()): dataset.append([labels[row_i]]+data[row_i]) + return dataset + +func build_dataset_from_matrix(datamatrix : Matrix, headers : PoolStringArray, labels : PoolStringArray) -> Array: + var data : Array = datamatrix.to_array() + return build_dataset(data, headers, labels) + +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 ECUtilities.get_letter_index(index)) + 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()) -> void: + assert(row.size() == datamatrix.columns(), "error: the row size must match the dataset row size") + labels.insert(index-1, label if label != "" else (index-1) as String) + datamatrix.insert_row(row, index-1) + dataset = build_dataset_from_matrix(datamatrix, headers, labels) + +func get_datamatrix() -> Matrix: + return datamatrix + +func get_dataset() -> Array: + return dataset + +func get_labels() -> PoolStringArray: + return labels + +func transpose(): + build_dataframe(MatrixGenerator.transpose(datamatrix), labels, headers, table_name) + +func _to_string() -> String: + var last_string_len : int + for row in dataset: + 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 = "" + for row_i in dataset.size(): + if row_i == 0: + string+="%*s" % [last_string_len+1, ""] + for column_i in dataset[row_i].size(): + string+="%*s" % [last_string_len+1, dataset[row_i][column_i]] + string+="\n" + string+="\n['{table_name}' : {rows} rows x {columns} columns]\n".format({ + rows = datamatrix.rows(), + columns = datamatrix.columns(), + table_name = table_name}) + return string + +# ............................................................................... + +# Return a list of headers corresponding to a list of indexes +func get_headers_names(indexes : PoolIntArray) -> PoolStringArray: + var headers : PoolStringArray = [] + for index in indexes: + headers.append(dataset[0][index]) + return headers + +# Returns the index of an header +func get_column_index(header : String) -> int: + for headers_ix in range(dataset[0].size()): + if dataset[0][headers_ix] == header: + return headers_ix + return -1 + +# Get a column by its header +func get_column(header : String) -> Array: + var headers_i : int = get_column_index(header) + if headers_i!=-1: + return datamatrix.get_column(headers_i) + else: + return [] + +# Get a list of columns by their headers +func columns(headers : PoolStringArray) -> Matrix: + var values : Array = [] + for header in headers: + values.append(get_column(header)) + return MatrixGenerator.transpose(Matrix.new(values)) + + +# Get a column by its index +func get_icolumn(index : int) -> Array: + return datamatrix.get_column(index) + +# Get a list of columns by their indexes +func get_icolumns(indexes : PoolIntArray) -> Array: + var values : Array = [] + for index in indexes: + values.append(datamatrix.get_column(index)) + return values + +# Returns the list of labels corresponding to the list of indexes +func get_labels_names(indexes : PoolIntArray) -> PoolStringArray: + var headers : PoolStringArray = [] + for index in indexes: + headers.append(dataset[index][0]) + return headers + +# Returns the index of a label +func get_row_index(label : String) -> int: + for row in dataset.size(): + if dataset[row][0] == label: + return row + return -1 + +# Get a row by its label +func get_row(label : String) -> Array: + var index : int = get_row_index(label) + if index == -1 : + return [] + else: + return datamatrix.get_row(index) + +# Get a list of rows by their labels +func rows(labels : Array) -> Matrix: + var values : Array = [] + for label in labels: + values.append(get_row(label)) + return Matrix.new(values) + +# Get a row by its index +func get_irow(index : int) -> Array: + return datamatrix.get_row(index) + +# Get a list of rows by their indexes +func get_irows(indexes : PoolIntArray) -> Array: + var values : Array = [] + for index in indexes: + values.append(datamatrix.get_row(index)) + return values + +# Returns a a group of rows or a group of columns, using indexes or names +# dataset["0;5"] ---> Returns an array containing all rows from the 1st to the 4th +# dataset["0:5"] ---> Returns an array containing all columns from the 1st to the 4th +# dataset["label0;label5"] ---> Returns an array containing all row from the one with label == "label0" to the one with label == "label5" +# dataset["header0:header0"] ---> Returns an array containing all columns from the one with label == "label0" to the one with label == "label5" +func _get(_property : String): + # ":" --> Columns + if ":" in _property: + var property : PoolStringArray = _property.split(":") + if (property[0]).is_valid_integer(): + if property[1] == "*": + return get_icolumns(range(property[0] as int, headers.size()-1)) + else: + return get_icolumns(range(property[0] as int, property[1] as int +1)) + else: + if property[1] == "*": + return get_icolumns(range(get_column_index(property[0]), headers.size()-1)) + else: + return get_icolumns(range(get_column_index(property[0]), get_column_index(property[1]))) + # ";" --> Rows + elif ";" in _property: + var property : PoolStringArray = _property.split(";") + if (property[0]).is_valid_integer(): + return get_irows(range(property[0] as int, property[1] as int + 1 )) + else: + return get_irows(range(get_row_index(property[0]), get_row_index(property[1]))) + elif "," in _property: + var property : PoolStringArray = _property.split(",") + else: + if (_property as String).is_valid_integer(): + return get_icolumn(_property as int) + else: + return get_column(_property) diff --git a/addons/easy_charts/Utilities/classes/structures/matrix.gd b/addons/easy_charts/Utilities/classes/structures/matrix.gd new file mode 100644 index 0000000..68cd4e7 --- /dev/null +++ b/addons/easy_charts/Utilities/classes/structures/matrix.gd @@ -0,0 +1,85 @@ +tool +extends Resource +class_name Matrix + +var values : Array = [] + +func _init(matrix : Array = []) -> void: + values = matrix + +func insert_row(row : Array, index : int = values.size()) -> void: + assert(row.size() == values[0].size(), "the row size must match matrix row size") + values.insert(index, row) + +func insert_column(column : Array, index : int = values[0].size()) -> void: + assert(column.size() == values.size(), "the column size must match matrix column size") + for row_idx in column.size(): + values[row_idx].insert(index, column[row_idx]) + +func to_array() -> Array: + return values.duplicate(true) + +func get_size() -> Vector2: + return Vector2(values.size(), values[0].size()) + +func rows() -> int: + return values.size() + +func columns() -> int: + return values[0].size() + +func get_column(column : int) -> Array: + assert(column < columns(), "index of the column requested (%s) exceedes matrix columns (%s)"%[column, columns()]) + var column_array : Array = [] + for row in values: + column_array.append(row[column]) + return column_array + +func get_columns(from : int, to : int) -> Array: + var values : Array = [] + for column in range(from, to): + values.append(get_column(column)) + return values +# return MatrixGenerator.from_array(values) + +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: + return values.slice(from, to) +# return MatrixGenerator.from_array(values) + +func _to_string() -> String: + var last_string_len : int + for row in values: + 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" + for row_i in values.size(): + for column_i in values[row_i].size(): + string+="%*s" % [last_string_len+1 if column_i!=0 else last_string_len, values[row_i][column_i]] + string+="\n" + return string + +# -------------- +func _get(_property : String): + # ":" --> Columns + if ":" in _property: + var property : PoolStringArray = _property.split(":") + var from : PoolStringArray = property[0].split(",") + var to : PoolStringArray = property[1].split(",") + elif "," in _property: + var property : PoolStringArray = _property.split(",") + if property.size() == 2: + return get_row(property[0] as int)[property[1] as int] + else: + if (_property as String).is_valid_integer(): + return get_row(_property as int) + + + + + + diff --git a/addons/easy_charts/Utilities/Data/matrix_generator.gd b/addons/easy_charts/Utilities/classes/structures/matrix_generator.gd similarity index 82% rename from addons/easy_charts/Utilities/Data/matrix_generator.gd rename to addons/easy_charts/Utilities/classes/structures/matrix_generator.gd index faa71d4..1bad6dc 100644 --- a/addons/easy_charts/Utilities/Data/matrix_generator.gd +++ b/addons/easy_charts/Utilities/classes/structures/matrix_generator.gd @@ -3,7 +3,7 @@ extends Reference class_name MatrixGenerator # Generates a Matrix with random values between [from; to] with a given @size (rows, columns) -static func random_float_range(size : Vector2, from : float, to : float, _seed : int = 1234) -> Matrix: +static func random_float_range(from : float, to : float, size : Vector2, _seed : int = 1234) -> Matrix: seed(_seed) randomize() var array : Array = [] @@ -20,12 +20,13 @@ static func from_array(array : Array = []) -> Matrix: return Matrix.new(matrix) # Generates a sub-Matrix giving a Matrix, a @from Array [row_i, column_i] and a @to Array [row_j, column_j] -static func sub_matrix(_matrix : Matrix, from : Array, to : Array) -> Matrix: - if to[0] > _matrix.get_size().x or to[1] > _matrix.get_size().y: - printerr("%s is not an acceptable size for the submatrix, giving a matrix of size %s"%[to, _matrix.get_size()]) - return Matrix.new() +static func sub_matrix(_matrix : Matrix, from : PoolIntArray, to : PoolIntArray) -> Matrix: + assert( not (to[0] > _matrix.rows() or to[1] > _matrix.columns()), + "%s is not an acceptable size for the submatrix, giving a matrix of size %s"%[to, _matrix.get_size()]) var array : Array = [] - for rows_i in range(from[0],to[0]): array.append(_matrix.to_array()[rows_i].slice(from[1], to[1])) + var rows : Array = _matrix.get_rows(from[0], to[0]) + for row in rows: + array.append(row.slice(from[1], to[1])) return Matrix.new(array) # Duplicates a given Matrix @@ -70,6 +71,7 @@ static func hadamard(_matrix1 : Matrix, _matrix2 : Matrix) -> Matrix: for x in range(_matrix1.to_array().size()): var row : Array = [] for y in range(_matrix1.to_array()[x].size()): + 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") row.append(_matrix1.to_array()[x][y] * _matrix2.to_array()[x][y]) array.append(row) return Matrix.new(array) @@ -90,6 +92,3 @@ static func multiply_float(_matrix1 : Matrix, _float : float) -> Matrix: for y in range(_matrix1.to_array()[x].size()): array[x][y]*=_float return Matrix.new(array) - - - diff --git a/addons/easy_charts/Utilities/Point/point.gd b/addons/easy_charts/Utilities/components/point/point.gd similarity index 100% rename from addons/easy_charts/Utilities/Point/point.gd rename to addons/easy_charts/Utilities/components/point/point.gd diff --git a/addons/easy_charts/Utilities/Point/point.tscn b/addons/easy_charts/Utilities/components/point/point.tscn similarity index 83% rename from addons/easy_charts/Utilities/Point/point.tscn rename to addons/easy_charts/Utilities/components/point/point.tscn index 7ebbd0d..81ba26a 100644 --- a/addons/easy_charts/Utilities/Point/point.tscn +++ b/addons/easy_charts/Utilities/components/point/point.tscn @@ -1,7 +1,6 @@ [gd_scene load_steps=2 format=2] -[ext_resource path="res://addons/easy_charts/Utilities/Point/point.gd" type="Script" id=1] - +[ext_resource path="res://addons/easy_charts/utilities/components/point/point.gd" type="Script" id=1] [node name="Point" type="Control"] margin_left = -13.0 @@ -13,6 +12,7 @@ script = ExtResource( 1 ) __meta__ = { "_edit_use_anchors_": false } + [connection signal="gui_input" from="." to="." method="_on_Point_gui_input"] [connection signal="mouse_entered" from="." to="." method="_on_Point_mouse_entered"] [connection signal="mouse_exited" from="." to="." method="_on_Point_mouse_exited"] diff --git a/addons/easy_charts/Utilities/Rect/Rect.gd b/addons/easy_charts/Utilities/components/rect/rect.gd similarity index 100% rename from addons/easy_charts/Utilities/Rect/Rect.gd rename to addons/easy_charts/Utilities/components/rect/rect.gd diff --git a/addons/easy_charts/Utilities/Rect/Rect.tscn b/addons/easy_charts/Utilities/components/rect/rect.tscn similarity index 75% rename from addons/easy_charts/Utilities/Rect/Rect.tscn rename to addons/easy_charts/Utilities/components/rect/rect.tscn index 466bbb9..60509e0 100644 --- a/addons/easy_charts/Utilities/Rect/Rect.tscn +++ b/addons/easy_charts/Utilities/components/rect/rect.tscn @@ -1,11 +1,12 @@ [gd_scene load_steps=2 format=2] -[ext_resource path="res://addons/easy_charts/Utilities/Rect/Rect.gd" type="Script" id=1] +[ext_resource path="res://addons/easy_charts/utilities/components/rect/rect.gd" type="Script" id=1] [node name="Rect" type="Control"] script = ExtResource( 1 ) __meta__ = { "_edit_use_anchors_": false } + [connection signal="mouse_entered" from="." to="." method="_on_Rect_mouse_entered"] [connection signal="mouse_exited" from="." to="." method="_on_Rect_mouse_exited"] diff --git a/addons/easy_charts/Utilities/Slice/slice.gd b/addons/easy_charts/Utilities/components/slice/slice.gd similarity index 100% rename from addons/easy_charts/Utilities/Slice/slice.gd rename to addons/easy_charts/Utilities/components/slice/slice.gd diff --git a/addons/easy_charts/Utilities/Legend/function_legend.gd b/addons/easy_charts/Utilities/containers/legend/function_legend.gd similarity index 100% rename from addons/easy_charts/Utilities/Legend/function_legend.gd rename to addons/easy_charts/Utilities/containers/legend/function_legend.gd diff --git a/addons/easy_charts/Utilities/Legend/function_legend.tscn b/addons/easy_charts/Utilities/containers/legend/function_legend.tscn similarity index 66% rename from addons/easy_charts/Utilities/Legend/function_legend.tscn rename to addons/easy_charts/Utilities/containers/legend/function_legend.tscn index 3b86a6a..8ee6c53 100644 --- a/addons/easy_charts/Utilities/Legend/function_legend.tscn +++ b/addons/easy_charts/Utilities/containers/legend/function_legend.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=2 format=2] -[ext_resource path="res://addons/easy_charts/Utilities/Legend/function_legend.gd" type="Script" id=1] +[ext_resource path="res://addons/easy_charts/utilities/containers/legend/function_legend.gd" type="Script" id=1] [node name="FunctionLegend" type="VBoxContainer"] margin_right = 80.0 @@ -15,8 +15,7 @@ __meta__ = { margin_top = 2.0 margin_right = 80.0 margin_bottom = 16.0 -custom_colors/font_color = Color( 0.184314, 0.192157, 0.262745, 1 ) -text = "Function" +custom_colors/font_color = Color( 0, 0, 0, 1 ) align = 1 valign = 1 @@ -25,4 +24,4 @@ margin_top = 20.0 margin_right = 80.0 margin_bottom = 23.0 rect_min_size = Vector2( 15, 3 ) -color = Color( 0.184314, 0.192157, 0.262745, 1 ) +color = Color( 0, 0, 0, 1 ) diff --git a/addons/easy_charts/Utilities/Scripts/utilities.gd b/addons/easy_charts/Utilities/scripts/ec_utilities.gd similarity index 84% rename from addons/easy_charts/Utilities/Scripts/utilities.gd rename to addons/easy_charts/Utilities/scripts/ec_utilities.gd index d4eabb8..d25d83b 100644 --- a/addons/easy_charts/Utilities/Scripts/utilities.gd +++ b/addons/easy_charts/Utilities/scripts/ec_utilities.gd @@ -1,6 +1,8 @@ tool extends Node +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" + var plugin_name : String = "Easy Charts" var templates : Dictionary = {} var chart_types : Dictionary = { @@ -14,8 +16,6 @@ var chart_types : Dictionary = { func _ready(): templates = _load_templates() -# _print_message("Templates loaded") - func _print_message(message : String, type : int = 0): match type: 0: @@ -35,3 +35,6 @@ func get_template(template_index : int): func get_chart_type(chart_type : int): return chart_types.get(chart_types.keys()[chart_type]) + +func get_letter_index(index : int) -> String: + return alphabet.split(" ")[index] diff --git a/addons/easy_charts/ControlChart/ColumnChart/column_chart.gd b/addons/easy_charts/control_charts/ColumnChart/column_chart.gd similarity index 65% rename from addons/easy_charts/ControlChart/ColumnChart/column_chart.gd rename to addons/easy_charts/control_charts/ColumnChart/column_chart.gd index cd07cdb..f922886 100644 --- a/addons/easy_charts/ControlChart/ColumnChart/column_chart.gd +++ b/addons/easy_charts/control_charts/ColumnChart/column_chart.gd @@ -20,147 +20,147 @@ values of more than one measured variable. # --------------------- func _get_property_list(): - return [ - # Chart Properties - { - "hint": PROPERTY_HINT_NONE, - "usage": PROPERTY_USAGE_CATEGORY, - "name": "ColumnChart", - "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(ECUtilities.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 - }, - ] + return [ + # Chart Properties + { + "hint": PROPERTY_HINT_NONE, + "usage": PROPERTY_USAGE_CATEGORY, + "name": "ColumnChart", + "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(Point.SHAPES.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(ECUtilities.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): +func structure_data(database : Array): # @labels_index can be either a column or a row relative to x values are_values_columns = (invert_chart != are_values_columns) if are_values_columns: @@ -297,7 +297,7 @@ func calculate_coordinates(): 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() + clean_points() draw_grid() draw_chart_outlines() diff --git a/addons/easy_charts/ControlChart/ColumnChart/column_chart.tscn b/addons/easy_charts/control_charts/ColumnChart/column_chart.tscn similarity index 80% rename from addons/easy_charts/ControlChart/ColumnChart/column_chart.tscn rename to addons/easy_charts/control_charts/ColumnChart/column_chart.tscn index 8ebc5a5..777a742 100644 --- a/addons/easy_charts/ControlChart/ColumnChart/column_chart.tscn +++ b/addons/easy_charts/control_charts/ColumnChart/column_chart.tscn @@ -1,9 +1,14 @@ [gd_scene load_steps=4 format=2] +<<<<<<<< HEAD:addons/easy_charts/ControlChart/ColumnChart/column_chart.tscn [ext_resource path="res://addons/easy_charts/Utilities/Point/point_data.tscn" type="PackedScene" id=1] [ext_resource path="res://addons/easy_charts/ControlChart/ColumnChart/column_chart.gd" type="Script" id=2] +======== +[ext_resource path="res://addons/easy_charts/utilities/containers/data_tooltip/data_tooltip.tscn" type="PackedScene" id=1] +[ext_resource path="res://addons/easy_charts/control_charts/ColumnChart/column_chart.gd" type="Script" id=2] +>>>>>>>> dev:addons/easy_charts/control_charts/ColumnChart/column_chart.tscn [sub_resource type="Theme" id=1] @@ -81,13 +86,19 @@ __meta__ = { "_edit_use_anchors_": true } -[node name="PointData" parent="." instance=ExtResource( 1 )] +[node name="CanvasLayer" type="CanvasLayer" parent="."] -[node name="PointData" parent="PointData" index="0"] +[node name="DataTooltip" parent="CanvasLayer" instance=ExtResource( 1 )] +margin_left = -455.833 +margin_top = 690.354 +margin_right = -455.969 +margin_bottom = 691.154 + +[node name="PointData" parent="CanvasLayer/DataTooltip" index="0"] margin_left = -593.381 margin_top = -80.9071 margin_right = -593.517 margin_bottom = -80.107 theme = SubResource( 1 ) -[editable path="PointData"] +[editable path="CanvasLayer/DataTooltip"] diff --git a/addons/easy_charts/ControlChart/LineChart/line_chart.gd b/addons/easy_charts/control_charts/LineChart/line_chart.gd similarity index 99% rename from addons/easy_charts/ControlChart/LineChart/line_chart.gd rename to addons/easy_charts/control_charts/LineChart/line_chart.gd index 6622bfe..45e758f 100644 --- a/addons/easy_charts/ControlChart/LineChart/line_chart.gd +++ b/addons/easy_charts/control_charts/LineChart/line_chart.gd @@ -70,7 +70,6 @@ func _set(property, value): func _draw(): - clear_points() draw_grid() draw_chart_outlines() if show_points: diff --git a/addons/easy_charts/ControlChart/LineChart/line_chart.tscn b/addons/easy_charts/control_charts/LineChart/line_chart.tscn similarity index 79% rename from addons/easy_charts/ControlChart/LineChart/line_chart.tscn rename to addons/easy_charts/control_charts/LineChart/line_chart.tscn index 4298044..f3f1d3a 100644 --- a/addons/easy_charts/ControlChart/LineChart/line_chart.tscn +++ b/addons/easy_charts/control_charts/LineChart/line_chart.tscn @@ -1,8 +1,7 @@ [gd_scene load_steps=4 format=2] -[ext_resource path="res://addons/easy_charts/Utilities/Point/point_data.tscn" type="PackedScene" id=1] -[ext_resource path="res://addons/easy_charts/ControlChart/LineChart/line_chart.gd" type="Script" id=4] - +[ext_resource path="res://addons/easy_charts/utilities/containers/data_tooltip/data_tooltip.tscn" type="PackedScene" id=1] +[ext_resource path="res://addons/easy_charts/control_charts/LineChart/line_chart.gd" type="Script" id=4] [sub_resource type="Theme" id=1] @@ -32,7 +31,7 @@ Chart_Display/autoscale_x = true Chart_Display/x_decim = 1.0 Chart_Display/autoscale_y = true Chart_Display/y_decim = 1.0 -Chart_Style/points_shape = [ ] +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 Chart_Style/box_color = Color( 0.117647, 0.117647, 0.117647, 1 ) @@ -84,13 +83,19 @@ __meta__ = { "_edit_use_anchors_": false } -[node name="PointData" parent="." instance=ExtResource( 1 )] +[node name="CanvasLayer" type="CanvasLayer" parent="."] -[node name="PointData" parent="PointData" index="0"] -margin_left = -458.75 -margin_top = -164.504 -margin_right = -458.886 -margin_bottom = -163.704 +[node name="DataTooltip" parent="CanvasLayer" instance=ExtResource( 1 )] +margin_left = -50.9019 +margin_top = -37.7724 +margin_right = -51.0379 +margin_bottom = -36.9724 + +[node name="PointData" parent="CanvasLayer/DataTooltip" index="0"] +margin_left = -189.809 +margin_top = -60.3698 +margin_right = -189.947 +margin_bottom = -59.5707 theme = SubResource( 1 ) -[editable path="PointData"] +[editable path="CanvasLayer/DataTooltip"] diff --git a/addons/easy_charts/control_charts/PieChart/pie_chart.gd b/addons/easy_charts/control_charts/PieChart/pie_chart.gd new file mode 100644 index 0000000..60588ec --- /dev/null +++ b/addons/easy_charts/control_charts/PieChart/pie_chart.gd @@ -0,0 +1,226 @@ +tool +extends Chart +class_name PieChart + +var should_draw : bool = false +var area_angles : Array +var slices : Array +var areas : Array +var areas_interacted : Array + +class CustomSorter: + static func sort_ascending(a,b): + if a[1] < b[1]: + return true + return false + +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 + }, + + # Chart Style + { + "hint": PROPERTY_HINT_NONE, + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, + "name": "Chart_Style/function_colors", + "type": TYPE_COLOR_ARRAY + }, + { + "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(ECUtilities.templates.keys()).join(","), + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, + "name": "Chart_Style/template", + "type": TYPE_INT + }, + + # Chart Modifiers + { + "hint": PROPERTY_HINT_RANGE, + "hint_string": "0,360", + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, + "name": "Chart_Modifiers/rotation", + "type": TYPE_REAL + }, + ] + + + +# Called when the node enters the scene tree for the first time. +func _ready(): + pass + +func plot_placeholder() -> void: + data = [ + ["United States",46], + ["Great Britain",27], + ["China",26], + ["Russia",19], + ["Germany",17] + ] + + function_colors = [ + Color.red, + Color.white, + Color.yellow, + Color.green, + Color.blue + ] + plot_from_array(data) + +func structure_data(database: Array): + # @labels_index can be either a column or a row relative to x values + clean_variables() + are_values_columns = invert_chart != are_values_columns + if are_values_columns: + for row in database.size(): + var t_vals: Array + for column in database[row].size(): + if column == labels_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()) + else: + for row in database.size(): + if row == labels_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 + + +func build_chart(): + SIZE = get_size() + origin = SIZE/2 + radius = (SIZE.y/2 - 20) if SIZE.y < SIZE.x else (SIZE.x/2 - 20) + +func calculate_pass(): + var tot : float + for y_data in y_datas: tot+=y_data[0] + x_pass = 360/tot + +func calculate_coordinates(): + area_angles.clear() + slices.clear() + areas.clear() + var from : float = 0.0 + var to : float = y_datas[0][0]*x_pass + area_angles.append([from,to]) + for info in range(y_datas.size()): + slices.append(Slice.new(y_labels[info], str(y_datas[info][0]), from, to, x_label+" : "+x_datas[0], function_colors[info])) + areas.append(calculate_circle_arc_polygon(origin, radius, from + rotation, to + rotation, function_colors[info])) + from = to + to = (y_datas[info+1][0]*x_pass + from) if info < y_datas.size()-1 else (360) + area_angles.append([from, to]) + + create_legend() + +func calculate_circle_arc_polygon(center : Vector2, radius : float, angle_from : float, angle_to : float, color : Color) -> PoolVector2Array: + var nb_points : int = 32 + var points_arc : PoolVector2Array = PoolVector2Array() +# var chord_angle : float = ((angle_to - angle_from)/2)+angle_from +# angle_from += 0.2 +# angle_to -= 0.2 +# var displacement : Vector2 = Vector2(cos(deg2rad(chord_angle)), sin(deg2rad(chord_angle-180))).normalized()*10 +# print(displacement) +# center += displacement +# radius+=displacement.length() + points_arc.push_back(center) + var colors : PoolColorArray = PoolColorArray([color]) + for i in range(nb_points + 1): + var angle_point = deg2rad(angle_from + i * (angle_to - angle_from) / nb_points - 90) + points_arc.push_back(center + Vector2(cos(angle_point), sin(angle_point)) * radius) + return points_arc + +func _draw(): + _draw_areas() + + if mouse_on_slice: + _draw_arc(area_angles[mouse_on_area], mouse_on_area) + mouse_on_slice = false + +func _draw_arc(arc : Array, index : int): + var temp_color : Color = function_colors[index] + temp_color.a = 0.7 + draw_arc(origin, radius + 6, deg2rad(arc[0]-90 + rotation), deg2rad(arc[1]-90 + rotation), 32, temp_color, 4) + +func _draw_areas(): + for area_idx in range(areas.size()): + draw_polygon(areas[area_idx], [function_colors[area_idx]]) + +var mouse_on_area : int +var mouse_on_slice : bool = false + +func _gui_input(event : InputEvent): + if event is InputEventMouseMotion: + for area_idx in range(areas.size()): + if Geometry.is_point_in_polygon(event.global_position - get_global_transform().origin, areas[area_idx]): + mouse_on_slice = true + mouse_on_area = area_idx + show_slice_data(slices[area_idx]) + update() + + if not mouse_on_slice: + mouse_on_area = -1 + mouse_on_slice = false + hide_data() + update() diff --git a/addons/easy_charts/ControlChart/PieChart/pie_chart.tscn b/addons/easy_charts/control_charts/PieChart/pie_chart.tscn similarity index 64% rename from addons/easy_charts/ControlChart/PieChart/pie_chart.tscn rename to addons/easy_charts/control_charts/PieChart/pie_chart.tscn index a8e5b44..a0de0f2 100644 --- a/addons/easy_charts/ControlChart/PieChart/pie_chart.tscn +++ b/addons/easy_charts/control_charts/PieChart/pie_chart.tscn @@ -1,8 +1,13 @@ [gd_scene load_steps=3 format=2] +<<<<<<<< HEAD:addons/easy_charts/ControlChart/PieChart/pie_chart.tscn [ext_resource path="res://addons/easy_charts/ControlChart/PieChart/pie_chart.gd" type="Script" id=1] [ext_resource path="res://addons/easy_charts/Utilities/Point/point_data.tscn" type="PackedScene" id=2] +======== +[ext_resource path="res://addons/easy_charts/control_charts/PieChart/pie_chart.gd" type="Script" id=1] +[ext_resource path="res://addons/easy_charts/utilities/containers/data_tooltip/data_tooltip.tscn" type="PackedScene" id=2] +>>>>>>>> dev:addons/easy_charts/control_charts/PieChart/pie_chart.tscn [node name="PieChart" type="Control"] @@ -16,7 +21,7 @@ __meta__ = { Chart_Properties/are_values_columns = false Chart_Properties/labels_index = 0 Chart_Properties/show_x_values_as_labels = true -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_colors = [ Color( 0.117647, 0.117647, 0.117647, 1 ) ] Chart_Style/font = null Chart_Style/bold_font = null Chart_Style/font_color = Color( 0.117647, 0.117647, 0.117647, 1 ) @@ -56,12 +61,18 @@ __meta__ = { "_edit_use_anchors_": true } -[node name="PointData" parent="." instance=ExtResource( 2 )] +[node name="CanvasLayer" type="CanvasLayer" parent="."] -[node name="PointData" parent="PointData" index="0"] +[node name="DataTooltip" parent="CanvasLayer" instance=ExtResource( 2 )] +margin_left = -492.874 +margin_top = 766.681 +margin_right = -493.01 +margin_bottom = 767.481 + +[node name="PointData" parent="CanvasLayer/DataTooltip" index="0"] margin_left = -314.458 margin_top = -189.587 margin_right = -314.559 margin_bottom = -188.787 -[editable path="PointData"] +[editable path="CanvasLayer/DataTooltip"] diff --git a/addons/easy_charts/control_charts/RadarChart/radar_chart.gd b/addons/easy_charts/control_charts/RadarChart/radar_chart.gd new file mode 100644 index 0000000..08b33da --- /dev/null +++ b/addons/easy_charts/control_charts/RadarChart/radar_chart.gd @@ -0,0 +1,286 @@ +tool +extends Chart +class_name RadarChart + +""" +[RadarChart] - General purpose node for Radar Charts +A radar chart is a graphical method of displaying multivariate data in the form +of a two-dimensional chart of three or more quantitative variables represented on axes +starting from the same point. The relative position and angle of the axes is typically +uninformative, but various heuristics, such as algorithms that plot data as the maximal +total area, can be applied to sort the variables (axes) into relative positions that reveal +distinct correlations, trade-offs, and a multitude of other comparative measures. + +/ 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_RANGE, + "hint_string": "-1,100,1", + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, + "name": "Chart_Properties/function_names_index", + "type": TYPE_INT + }, + { + "hint": PROPERTY_HINT_NONE, + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, + "name": "Chart_Properties/use_height_as_radius", + "type": TYPE_BOOL + }, + { + "hint": PROPERTY_HINT_RANGE, + "hint_string": "0,2000", + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, + "name": "Chart_Properties/radius", + "type": TYPE_REAL + }, + + # Chart Display + { + "hint": PROPERTY_HINT_RANGE, + "hint_string": "0.1,100", + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, + "name": "Chart_Display/full_scale", + "type": TYPE_REAL + }, + + # Chart Style + { + "hint": 24, + "hint_string": "%d/%d:%s"%[TYPE_INT, PROPERTY_HINT_ENUM, + PoolStringArray(Point.SHAPES.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/outline_color", + "type": TYPE_COLOR + }, + { + "hint": PROPERTY_HINT_NONE, + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, + "name": "Chart_Style/grid_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(ECUtilities.templates.keys()).join(","), + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, + "name": "Chart_Style/template", + "type": TYPE_INT + }, + { + "hint": PROPERTY_HINT_RANGE, + "hint_string": "0,360", + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, + "name": "Chart_Modifiers/rotation", + "type": TYPE_REAL + }, + ] + +func structure_data(database : Array): + # @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 + are_values_columns = invert_chart != are_values_columns + match are_values_columns: + true: + for row in database.size(): + var t_row : Array = [] + for column in database[row].size(): + if row == labels_index: + if column == function_names_index: + pass + else: + x_labels.append(database[row][column]) + else: + if column == function_names_index: + y_labels.append(database[row][column]) + else: + if typeof(database[row][column]) == TYPE_INT or typeof(database[row][column]) == TYPE_REAL: + t_row.append(database[row][column] as float) + else: + t_row.append(database[row][column].replace(",", ".") as float) + if not t_row.empty(): + x_datas.append(t_row) + false: + for row in database.size(): + if row == function_names_index: + y_labels = database[row] as PoolStringArray + + var x_temp_datas : PoolRealArray = [] + for column in database[row].size(): + if column == labels_index: + x_labels.append(database[row][column] as String) + else: + x_temp_datas.append(database[row][column] as float) + x_datas.append(x_temp_datas) + + + if labels_index == -1 : + for data in x_datas[0].size(): + x_labels.append("Element %s" % data) + + if function_names_index == -1 : + for data in x_datas.size(): + y_labels.append("Function %s" % data) + +func build_chart(): + SIZE = get_size() + origin = OFFSET + SIZE/2 + +var radar_polygon : Array + +func calculate_pass() : + var ordered_max : Array + for data in x_datas : + var ordered_data : Array = data.duplicate() + ordered_data.sort() + ordered_max.append(ordered_data.pop_back()) + ordered_max.sort() + var max_value : float = ordered_max.pop_back() + var dist = full_scale * pow(10.0,str(max_value).length()-2) + var multi = 0 + var value = dist * multi + x_chors.append(value as String) + while value < max_value: + multi+=1 + value = dist * multi + x_chors.append(value as String) + +func calculate_coordinates(): + for chor in x_chors.size(): + var inner_polyline : PoolVector2Array + var scalar_factor : float = (x_chors[chor] as float/x_chors.back() as float) + for function in functions: + var angle : float = ((2 * PI * function) / functions) - PI /2 + deg2rad(rotation) + var x_coordinate : float = (radius if (not use_height_as_radius and radius>>>>>>> dev:addons/easy_charts/control_charts/RadarChart/radar_chart.tscn [node name="RadarChart" type="Control"] anchor_right = 1.0 anchor_bottom = 1.0 rect_min_size = Vector2( 70, 50 ) mouse_filter = 2 -script = ExtResource( 2 ) +script = ExtResource( 1 ) __meta__ = { "_edit_use_anchors_": false, "_editor_description_": "[RadarChart] - General purpose node for Radar Charts @@ -30,9 +35,9 @@ Chart_Properties/use_height_as_radius = false Chart_Properties/radius = 150.0 Chart_Display/full_scale = 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/function_colors = [ Color( 0.117647, 0.117647, 0.117647, 1 ) ] Chart_Style/outline_color = Color( 0.117647, 0.117647, 0.117647, 1 ) -Chart_Style/grid_color = Color( 0.792157, 0.792157, 0.792157, 1 ) +Chart_Style/grid_color = Color( 0.117647, 0.117647, 0.117647, 1 ) Chart_Style/font = null Chart_Style/bold_font = null Chart_Style/font_color = Color( 0.117647, 0.117647, 0.117647, 1 ) @@ -75,14 +80,10 @@ __meta__ = { "_edit_use_anchors_": true } -[node name="PointData" parent="." instance=ExtResource( 1 )] +[node name="CanvasLayer" type="CanvasLayer" parent="."] -[node name="PointData" parent="PointData" index="0"] -visible = false -margin_left = -448.644 -margin_top = 721.085 -margin_right = -448.781 -margin_bottom = 721.884 -theme = SubResource( 1 ) - -[editable path="PointData"] +[node name="DataTooltip" parent="CanvasLayer" instance=ExtResource( 2 )] +margin_left = -473.792 +margin_top = 853.111 +margin_right = -473.928 +margin_bottom = 853.911 diff --git a/addons/easy_charts/control_charts/ScatterChart/scatter_chart.gd b/addons/easy_charts/control_charts/ScatterChart/scatter_chart.gd new file mode 100644 index 0000000..f12ac48 --- /dev/null +++ b/addons/easy_charts/control_charts/ScatterChart/scatter_chart.gd @@ -0,0 +1,29 @@ +tool +extends ScatterChartBase +class_name ScatterChart + +""" +[ScatterChart] - General purpose node for Scatter Charts + +A scatter plot (also called a scatterplot, scatter graph, scatter chart, scattergram, or scatter diagram) + is a type of plot or mathematical diagram using Cartesian coordinates to display values for typically two variables +for a set of data. If the points are coded (color/shape/size), one additional variable can be displayed. +The data are displayed as a collection of points, each having the value of one variable determining the position on +the horizontal axis and the value of the other variable determining the position on the vertical axis. + +/ source : Wikipedia / +""" + +# --------------------- + +func _get_property_list(): + property_list[0].name = "ScatterChart" + return property_list + + +func _draw(): + clean_points() + draw_grid() + draw_chart_outlines() + draw_points() + draw_treshold() diff --git a/addons/easy_charts/ControlChart/ScatterChart/scatter_chart.tscn b/addons/easy_charts/control_charts/ScatterChart/scatter_chart.tscn similarity index 73% rename from addons/easy_charts/ControlChart/ScatterChart/scatter_chart.tscn rename to addons/easy_charts/control_charts/ScatterChart/scatter_chart.tscn index b9ca693..c8e91e9 100644 --- a/addons/easy_charts/ControlChart/ScatterChart/scatter_chart.tscn +++ b/addons/easy_charts/control_charts/ScatterChart/scatter_chart.tscn @@ -1,17 +1,22 @@ -[gd_scene load_steps=4 format=2] +[gd_scene load_steps=3 format=2] +<<<<<<<< HEAD:addons/easy_charts/ControlChart/ScatterChart/scatter_chart.tscn [ext_resource path="res://addons/easy_charts/Utilities/Point/point_data.tscn" type="PackedScene" id=1] [ext_resource path="res://addons/easy_charts/ControlChart/ScatterChart/scatter_chart.gd" type="Script" id=2] [sub_resource type="Theme" id=1] +======== +[ext_resource path="res://addons/easy_charts/control_charts/ScatterChart/scatter_chart.gd" type="Script" id=1] +[ext_resource path="res://addons/easy_charts/utilities/containers/data_tooltip/data_tooltip.tscn" type="PackedScene" id=2] +>>>>>>>> dev:addons/easy_charts/control_charts/ScatterChart/scatter_chart.tscn [node name="ScatterChart" type="Control"] anchor_right = 1.0 anchor_bottom = 1.0 rect_min_size = Vector2( 70, 50 ) mouse_filter = 2 -script = ExtResource( 2 ) +script = ExtResource( 1 ) __meta__ = { "_edit_use_anchors_": false, "_editor_description_": "[ScatterChart] - General purpose node for Scatter Charts @@ -22,6 +27,7 @@ for a set of data. If the points are coded (color/shape/size), one additional va The data are displayed as a collection of points, each having the value of one variable determining the position on the horizontal axis and the value of the other variable determining the position on the vertical axis." } +<<<<<<<< HEAD:addons/easy_charts/ControlChart/ScatterChart/scatter_chart.tscn Chart_Properties/are_values_columns = false Chart_Properties/labels_index = 0 Chart_Properties/show_x_values_as_labels = false @@ -43,6 +49,8 @@ Chart_Style/template = 0 Chart_Modifiers/treshold = Vector2( 0, 0 ) Chart_Modifiers/only_disp_values = Vector2( 0, 0 ) Chart_Modifiers/invert_chart = false +======== +>>>>>>>> dev:addons/easy_charts/control_charts/ScatterChart/scatter_chart.tscn [node name="Background" type="ColorRect" parent="."] visible = false @@ -79,8 +87,9 @@ __meta__ = { "_edit_use_anchors_": false } -[node name="PointData" parent="." instance=ExtResource( 1 )] +[node name="CanvasLayer" type="CanvasLayer" parent="."] +<<<<<<<< HEAD:addons/easy_charts/ControlChart/ScatterChart/scatter_chart.tscn [node name="PointData" parent="PointData" index="0"] margin_left = -510.384 margin_top = -148.79 @@ -89,3 +98,10 @@ margin_bottom = -147.99 theme = SubResource( 1 ) [editable path="PointData"] +======== +[node name="DataTooltip" parent="CanvasLayer" instance=ExtResource( 2 )] +margin_left = 264.788 +margin_top = 446.78 +margin_right = 264.65 +margin_bottom = 447.58 +>>>>>>>> dev:addons/easy_charts/control_charts/ScatterChart/scatter_chart.tscn diff --git a/addons/easy_charts/file.samples/linechart (columns).csv.import b/addons/easy_charts/file.samples/linechart (columns).csv.import index 15bbc92..8dd0c09 100644 --- a/addons/easy_charts/file.samples/linechart (columns).csv.import +++ b/addons/easy_charts/file.samples/linechart (columns).csv.import @@ -1,13 +1,3 @@ [remap] -importer="csv_translation" -type="Translation" -valid=false - -[deps] - -source_file="res://addons/easy_charts/file.samples/linechart (columns).csv" -[params] - -compress=true -delimiter=0 +importer="keep" diff --git a/addons/easy_charts/file.samples/linechart.csv b/addons/easy_charts/file.samples/linechart.csv new file mode 100644 index 0000000..412e7e8 --- /dev/null +++ b/addons/easy_charts/file.samples/linechart.csv @@ -0,0 +1,2 @@ +Date; 21/03/03;21/03/2008;21/03/2010;21/03/2011;21/03/2012;21/03/2013;21/03/2014;21/03/2015;21/03/2016;21/03/2018 +Score;3;8;20;18;28;31;27;38;36;46 diff --git a/addons/easy_charts/file.samples/linechart.csv.import b/addons/easy_charts/file.samples/linechart.csv.import new file mode 100644 index 0000000..239c88e --- /dev/null +++ b/addons/easy_charts/file.samples/linechart.csv.import @@ -0,0 +1,10 @@ +[remap] + +importer="csv" +type="TextFile" + +[deps] + +source_file="res://addons/easy_charts/file.samples/linechart.csv" +[params] + diff --git a/addons/easy_charts/plugin.gd b/addons/easy_charts/plugin.gd index c6083f7..0a88807 100644 --- a/addons/easy_charts/plugin.gd +++ b/addons/easy_charts/plugin.gd @@ -2,11 +2,12 @@ tool extends EditorPlugin func _enter_tree(): - add_autoload_singleton("Utilities","res://addons/easy_charts/Utilities/Scripts/utilities.gd") - add_custom_type("Chart","Control", load("res://addons/easy_charts/Utilities/Scripts/chart.gd"), preload("Utilities/icons/linechart.svg")) - add_custom_type("Chart2D","Node2D", load("res://addons/easy_charts/Utilities/Scripts/chart2d.gd"), preload("Utilities/icons/linechart2d.svg")) + add_autoload_singleton("ECUtilities","res://addons/easy_charts/utilities/scripts/ec_utilities.gd") + add_custom_type("Chart","Control", load("res://addons/easy_charts/utilities/classes/base/chart.gd"), preload("utilities/icons/linechart.svg")) + add_custom_type("Chart2D","Node2D", load("res://addons/easy_charts/utilities/classes/base/chart2d.gd"), preload("utilities/icons/linechart2d.svg")) func _exit_tree(): remove_custom_type("Chart") remove_custom_type("Chart2D") - remove_autoload_singleton("Utilities") + remove_autoload_singleton("ECUtilities") + diff --git a/imgs/code_snapshot.gif b/imgs/code_snapshot.gif new file mode 100644 index 0000000..9602bf0 Binary files /dev/null and b/imgs/code_snapshot.gif differ diff --git a/imgs/screensho_2t.png b/imgs/screensho_2t.png new file mode 100644 index 0000000..446ac32 Binary files /dev/null and b/imgs/screensho_2t.png differ diff --git a/imgs/screensho_2t.png.import b/imgs/screensho_2t.png.import new file mode 100644 index 0000000..e4c5d86 --- /dev/null +++ b/imgs/screensho_2t.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="StreamTexture" +path="res://.import/screensho_2t.png-a3057a4df668383211639c7b4209d3cf.stex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://imgs/screensho_2t.png" +dest_files=[ "res://.import/screensho_2t.png-a3057a4df668383211639c7b4209d3cf.stex" ] + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_mode=0 +compress/bptc_ldr=0 +compress/normal_map=0 +flags/repeat=0 +flags/filter=true +flags/mipmaps=false +flags/anisotropic=false +flags/srgb=2 +process/fix_alpha_border=true +process/premult_alpha=false +process/HDR_as_SRGB=false +process/invert_color=false +stream=false +size_limit=0 +detect_3d=true +svg/scale=1.0 diff --git a/imgs/screenshot.png b/imgs/screenshot.png new file mode 100644 index 0000000..212aded Binary files /dev/null and b/imgs/screenshot.png differ diff --git a/imgs/screenshot.png.import b/imgs/screenshot.png.import new file mode 100644 index 0000000..4af980c --- /dev/null +++ b/imgs/screenshot.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="StreamTexture" +path="res://.import/screenshot.png-836091f94baad9c66f52d1d068ff5e95.stex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://imgs/screenshot.png" +dest_files=[ "res://.import/screenshot.png-836091f94baad9c66f52d1d068ff5e95.stex" ] + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_mode=0 +compress/bptc_ldr=0 +compress/normal_map=0 +flags/repeat=0 +flags/filter=true +flags/mipmaps=false +flags/anisotropic=false +flags/srgb=2 +process/fix_alpha_border=true +process/premult_alpha=false +process/HDR_as_SRGB=false +process/invert_color=false +stream=false +size_limit=0 +detect_3d=true +svg/scale=1.0 diff --git a/imgs/screenshot_2.png b/imgs/screenshot_2.png new file mode 100644 index 0000000..c611474 Binary files /dev/null and b/imgs/screenshot_2.png differ diff --git a/imgs/screenshot_2.png.import b/imgs/screenshot_2.png.import new file mode 100644 index 0000000..858f739 --- /dev/null +++ b/imgs/screenshot_2.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="StreamTexture" +path="res://.import/screenshot_2.png-00ef4b140fda2b59dc0a00f84f140a14.stex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://imgs/screenshot_2.png" +dest_files=[ "res://.import/screenshot_2.png-00ef4b140fda2b59dc0a00f84f140a14.stex" ] + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_mode=0 +compress/bptc_ldr=0 +compress/normal_map=0 +flags/repeat=0 +flags/filter=true +flags/mipmaps=false +flags/anisotropic=false +flags/srgb=2 +process/fix_alpha_border=true +process/premult_alpha=false +process/HDR_as_SRGB=false +process/invert_color=false +stream=false +size_limit=0 +detect_3d=true +svg/scale=1.0 diff --git a/imgs/screenshot_3.png b/imgs/screenshot_3.png new file mode 100644 index 0000000..446ac32 Binary files /dev/null and b/imgs/screenshot_3.png differ diff --git a/imgs/screenshot_3.png.import b/imgs/screenshot_3.png.import new file mode 100644 index 0000000..7eb4cac --- /dev/null +++ b/imgs/screenshot_3.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="StreamTexture" +path="res://.import/screenshot_3.png-6cf7b262e447a7e1897953df89f52cf7.stex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://imgs/screenshot_3.png" +dest_files=[ "res://.import/screenshot_3.png-6cf7b262e447a7e1897953df89f52cf7.stex" ] + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_mode=0 +compress/bptc_ldr=0 +compress/normal_map=0 +flags/repeat=0 +flags/filter=true +flags/mipmaps=false +flags/anisotropic=false +flags/srgb=2 +process/fix_alpha_border=true +process/premult_alpha=false +process/HDR_as_SRGB=false +process/invert_color=false +stream=false +size_limit=0 +detect_3d=true +svg/scale=1.0 diff --git a/imgs/screenshot_4.png b/imgs/screenshot_4.png new file mode 100644 index 0000000..3732303 Binary files /dev/null and b/imgs/screenshot_4.png differ diff --git a/imgs/screenshot_4.png.import b/imgs/screenshot_4.png.import new file mode 100644 index 0000000..8c54121 --- /dev/null +++ b/imgs/screenshot_4.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="StreamTexture" +path="res://.import/screenshot_4.png-c6adff16fa5e61b3ce72172e14b45dd7.stex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://imgs/screenshot_4.png" +dest_files=[ "res://.import/screenshot_4.png-c6adff16fa5e61b3ce72172e14b45dd7.stex" ] + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_mode=0 +compress/bptc_ldr=0 +compress/normal_map=0 +flags/repeat=0 +flags/filter=true +flags/mipmaps=false +flags/anisotropic=false +flags/srgb=2 +process/fix_alpha_border=true +process/premult_alpha=false +process/HDR_as_SRGB=false +process/invert_color=false +stream=false +size_limit=0 +detect_3d=true +svg/scale=1.0 diff --git a/imgs/snapshot.html b/imgs/snapshot.html new file mode 100644 index 0000000..07f3eee --- /dev/null +++ b/imgs/snapshot.html @@ -0,0 +1,278 @@ + +
+
+tool
+class_name CodeSnapshotInstance
+extends Control
+
+onready var viewport_container : ViewportContainer = $VSplitContainer/ViewportContainer
+onready var viewport : Viewport = viewport_container.get_child(0)
+onready var snapshot_container : ColorRect = viewport.get_node("Background")
+onready var settings_container : VBoxContainer = $VSplitContainer/Settings
+
+onready var script_container : PanelContainer = snapshot_container.get_node("ScriptContainer")
+onready var script_edit : TextEdit = script_container.get_node("VBox/ScriptEdit")
+onready var template_menu : PopupMenu = settings_container.get_node("Template/TemplateMenu").get_popup()
+onready var colors_list : VBoxContainer = settings_container.get_node("Colors/ColorsList")
+onready var properties_list : VBoxContainer = settings_container.get_node("Properties/PropertiesList")
+onready var from : LineEdit = settings_container.get_node("Properties/PropertiesList/from_line_to_line/from")
+onready var to : LineEdit = settings_container.get_node("Properties/PropertiesList/from_line_to_line/to")
+onready var save : FileDialog = $Save
+
+var script_editor : ScriptEditor
+var editor_settings : EditorSettings
+
+var template_dir : String = "res://addons/code-snapshot/godot-syntax-themes/"
+var templates : PoolStringArray
+var template_file : String = "Darcula"
+
+var keywords : Array = ["self", "if", "else", "elif", "or", "and", "yield","func", "onready", "export", "var", "tool", "extends", "void", "null", "true", "false", "class_name", "print", "return", "pass", "match", "in", "define", "const"]
+var types : PoolStringArray = ClassDB.get_class_list()
+var from_line_to : Array = [-1, -1]
+var lines_count_as_min_size : bool = true
+var exporting_extension : String 
+var path_to_save : String
+
+func set_editor_settings(settings : EditorSettings):
+	editor_settings = settings
+
+func set_script_editor(editor : ScriptEditor):
+	script_editor = editor
+	script_editor.connect("editor_script_changed", self, "_on_script_changed")
+	_on_script_changed(script_editor.get_current_script())
+
+func hide_nodes():
+	colors_list.hide()
+	properties_list.hide()
+
+func _ready() -> void:
+	yield(get_tree(),"idle_frame")
+#	viewport.set_process_input(true)
+	script_edit.set_text("")
+	template_menu.connect("index_pressed", self, "_on_index_pressed")
+	hide_nodes()
+	load_templates()
+	if script_editor != null : _on_script_changed(script_editor.get_current_script())
+
+func load_templates():
+	templates = []
+	template_menu.clear()
+	var dir = Directory.new()
+	if dir.open(template_dir) == OK:
+		dir.list_dir_begin()
+		var file_name = dir.get_next()
+		while file_name != "":
+			if not dir.current_is_dir() and file_name.get_extension() == "tet":
+				templates.append(file_name.get_basename())
+				template_menu.add_item(file_name.get_basename())
+			file_name = dir.get_next()
+	else:
+			print("An error occurred when trying to access the path.")
+	_on_index_pressed(0)
+
+func _on_index_pressed(index : int):
+	apply_template(templates[index])
+	$VSplitContainer/Settings/Template/TemplateMenu.set_text("> "+templates[index])
+
+func apply_template(template : String):
+	var config = ConfigFile.new()
+	var err = config.load(template_dir+"%s.tet"%template)
+	if err == OK: # If not, something went wrong with the file loading
+		set_script_box_color(config.get_value("color_theme", "background_color"))
+		for setting in config.get_section_keys("color_theme"):
+			if setting == "gdscript/function_definition_color" : 
+				add_function_definition_color(config.get_value("color_theme",setting))
+				set_node_color("function_definition_color", config, "gdscript/")
+			if setting == "keyword_color": add_keywords_color(config.get_value("color_theme",setting))
+			if setting == "comment_color": add_comment_color(config.get_value("color_theme", setting))
+			if setting == "string_color" : add_string_color(config.get_value("color_theme", setting))
+			if setting == "engine_type_color" : add_engine_type_color(config.get_value("color_theme",setting))
+			if setting == "gdscript/node_path_color" : add_node_path_color(config.get_value("color_theme",setting))
+			if setting == "text_color" : set_text_color(config.get_value("color_theme",setting))
+			script_edit.set("custom_colors/"+setting, config.get_value("color_theme",setting))
+			set_node_color(setting, config)
+
+func set_node_color(setting : String, config : ConfigFile, category : String = ""):
+	if colors_list.get_node_or_null("%s/Color"%setting) != null : colors_list.get_node_or_null("%s/Color"%setting).color = config.get_value("color_theme",category+setting)
+
+func add_node_path_color(color : Color):
+	script_edit.add_color_region("$","", color, false)
+
+func add_engine_type_color(color : Color):
+	for type in types: script_edit.add_keyword_color(type, color)
+
+func add_function_definition_color(color : Color):
+	script_edit.add_color_region("func ","", color)
+
+func set_script_edit_size(size : Vector2):
+	script_container.rect_size = size
+
+func set_background_color(color : Color):
+	snapshot_container.color = color
+
+# Script Box Color
+func set_script_box_color(color : Color):
+	script_container.get("custom_styles/panel").set("bg_color", color)
+	set_script_background_color(color)
+
+func get_script_box_color() -> String:
+	return "#"+script_container.get("custom_styles/panel").get("bg_color").to_html(false)
+# ................
+
+func set_member_variable_color(color : Color):
+	script_edit.set("custom_colors/member_variable_color", color)
+
+func set_function_color(color : Color):
+	script_edit.set("custom_colors/function_color", color)
+
+# Script Background color .....
+func set_script_background_color(color : Color):
+	script_edit.set("custom_colors/background_color", color)
+
+func get_script_background_color() -> String:
+	return "#"+script_edit.get("custom_colors/background_color").to_html(false)
+# ..............
+
+func set_number_color(color : Color):
+	script_edit.set("custom_colors/number_color", color)
+
+# Text Color .....
+func set_text_color(color : Color):
+	script_edit.set("custom_colors/font_color", color)
+
+func get_text_color() -> String:
+	return "#"+script_edit.get("custom_colors/font_color").to_html(false)
+# ................
+
+func add_keywords_color(color : Color):
+	for keyword in keywords:
+		script_edit.add_keyword_color(keyword,color)
+
+func add_comment_color(color : Color):
+	script_edit.add_color_region("#","",color,true)
+
+func add_string_color(color : Color):
+	script_edit.add_color_region('"','"',color,false)
+
+func set_from_line_to(from : int, to : int):
+	from_line_to = [from, to]
+
+var current_script : Script
+
+func _on_script_changed(script : Script):
+	if script == null or script_edit == null: return
+	current_script = script
+	var code : String = script.get_source_code()
+	var code_array : Array = code.c_unescape().split("
+")
+	code = PoolStringArray(code_array.slice(from_line_to[0]-1 if from_line_to[0] != -1 and from_line_to[0] < code_array.size()-1 else 0, from_line_to[1]-1 if from_line_to[1] != -1 and from_line_to[1] < code_array.size()-1 else code_array.size()-1)).join("
+")
+	script_edit.set_text(code)
+	change_frame_min_size()
+
+func change_frame_min_size():
+	if lines_count_as_min_size:
+		script_edit.rect_min_size.y = script_edit.get_line_count() * (editor_settings.get_setting("interface/editor/code_font_size") + 6) if script_edit.get_line_count() <= 35 else 132
+		viewport_container.rect_min_size.y = script_edit.rect_min_size.y + 168
+	else:
+		script_edit.rect_min_size.y = 0
+		viewport_container.rect_min_size.y = 300
+
+func _on_ColorsBtn_toggled(button_pressed : bool):
+	colors_list.visible = button_pressed
+	if button_pressed: $VSplitContainer/Settings/Colors/ColorsBtn.set_text("v Colors")
+	else: $VSplitContainer/Settings/Colors/ColorsBtn.set_text("> Colors")
+
+func _on_PropertiesBtn_toggled(button_pressed : bool):
+	properties_list.visible = button_pressed
+	if button_pressed: $VSplitContainer/Settings/Properties/PropertiesBtn.set_text("v Properties")
+	else: $VSplitContainer/Settings/Properties/PropertiesBtn.set_text("> Properties")
+
+
+
+
+func _on_export_confirmed(path : String):
+	path_to_save = path
+
+func _on_Save_hide():
+	match exporting_extension:
+		"png":
+			var image : Image = viewport.get_texture().get_data()
+			image.flip_y()
+			image.save_png(path_to_save)
+		"html":
+			var file : File = File.new()
+			file.open(path_to_save, File.WRITE)
+			var content : String = '
+'
+			content+='
+
+'
+			content+=script_edit.text+"
+"
+			content+='
+
' + file.store_line(content) + file.close() + + +func _on_autowrap_value_toggled(button_pressed): + script_edit.wrap_enabled = button_pressed + script_edit.update() + + +func _on_draw_tabs_value_toggled(button_pressed): + script_edit.draw_tabs = button_pressed + +func _on_from_text_changed(new_text): + if new_text.is_valid_integer(): + if int(new_text) == 0: + new_text = str(-1) + from.set_text(new_text) + from_line_to[0] = int(new_text) + _on_script_changed(current_script) + + + +func _on_to_text_changed(new_text): + if new_text.is_valid_integer(): + if int(new_text) == 0: + new_text = str(-1) + to.set_text(new_text) + from_line_to[1] = int(new_text) + _on_script_changed(current_script) + + +func _on_Reload_pressed(): + _on_script_changed(current_script) + + +func _on_minimap_draw_value_toggled(button_pressed): + script_edit.minimap_draw = button_pressed + + +func _on_ViewportContainer_item_rect_changed(): + snapshot_container.rect_size = viewport_container.rect_size + + +func _on_minsie_value_toggled(button_pressed): + lines_count_as_min_size = button_pressed + change_frame_min_size() + +func _on_ExportPNG_pressed(): + exporting_extension = "png" + save.filters = ["*.png ; Portable Network Graphics"] + save.current_file = "snapshot.png" + save.popup() + + +func _on_ExportHTML_pressed(): + exporting_extension = "html" + save.filters = ["*.html ; HyperText Markup Language"] + save.current_file = "snapshot.html" + save.popup() + + + +
+