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 @@ -
- - - -
- # 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
+
+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()
+
+
+
+
+