From 1b8d7f2ceacac54ca132159fd26c122bef88c9fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Santilio?= Date: Mon, 6 May 2024 09:39:03 +0200 Subject: [PATCH] Fix/240505 (#103) * fix issue #97 * implement function labels * fix #101 * add visibility property. improves #41 * fix #88 remove canvas reference to missing scene --- addons/easy_charts/control_charts/chart.gd | 284 +++++++++-------- addons/easy_charts/control_charts/chart.tscn | 2 +- .../plotters/function_plotter.gd | 21 +- .../plotters/scatter_plotter.gd | 132 ++++---- .../examples/area_chart/Control.gd | 106 +++---- .../examples/line_chart/Control.gd | 112 +++---- .../easy_charts/examples/multiplot/Control.gd | 124 ++++---- .../examples/scatter_chart/Control.gd | 98 +++--- .../classes/plotting/chart_properties.gd | 24 +- .../utilities/classes/plotting/function.gd | 89 +++--- .../utilities/classes/plotting/point.gd | 6 +- .../utilities/containers/canvas/canvas.gd | 78 ++--- .../utilities/containers/canvas/canvas.tscn | 73 ----- .../containers/canvas/plot_box/grid_box.gd | 285 +++++++++--------- .../containers/canvas/plot_box/plot_box.gd | 57 ++-- .../containers/data_tooltip/data_tooltip.gd | 34 +-- .../containers/legend/function_legend.gd | 18 +- .../utilities/scripts/ec_utilities.gd | 90 +++--- icon.svg | 1 + 19 files changed, 788 insertions(+), 846 deletions(-) delete mode 100644 addons/easy_charts/utilities/containers/canvas/canvas.tscn create mode 100644 icon.svg diff --git a/addons/easy_charts/control_charts/chart.gd b/addons/easy_charts/control_charts/chart.gd index cb0ad65..2c82866 100644 --- a/addons/easy_charts/control_charts/chart.gd +++ b/addons/easy_charts/control_charts/chart.gd @@ -12,8 +12,8 @@ var functions: Array = [] var x: Array = [] var y: Array = [] -var x_labels: PackedStringArray = [] -var y_labels: PackedStringArray = [] +var x_labels_function: Callable = Callable() +var y_labels_function: Callable = Callable() var x_domain: Dictionary = {} var y_domain: Dictionary = {} @@ -23,161 +23,159 @@ var chart_properties: ChartProperties = null ########### func _ready() -> void: - if theme == null: - theme = Theme.new() + if theme == null: + theme = Theme.new() func plot(functions: Array[Function], properties: ChartProperties = ChartProperties.new()) -> void: - self.functions = functions - self.chart_properties = properties - - theme.set("default_font", self.chart_properties.font) - _canvas.prepare_canvas(self.chart_properties) - plot_box.chart_properties = self.chart_properties - function_legend.chart_properties = self.chart_properties - - load_functions(functions) + self.functions = functions + self.chart_properties = properties + + theme.set("default_font", self.chart_properties.font) + _canvas.prepare_canvas(self.chart_properties) + plot_box.chart_properties = self.chart_properties + function_legend.chart_properties = self.chart_properties + + load_functions(functions) func get_function_plotter(function: Function) -> FunctionPlotter: - var plotter: FunctionPlotter - match function.get_type(): - Function.Type.LINE: - plotter = LinePlotter.new(function) - Function.Type.AREA: - plotter = AreaPlotter.new(function) - Function.Type.PIE: - plotter = PiePlotter.new(function) - Function.Type.BAR: - plotter = BarPlotter.new(function) - Function.Type.SCATTER, _: - plotter = ScatterPlotter.new(function) - return plotter + var plotter: FunctionPlotter + match function.get_type(): + Function.Type.LINE: + plotter = LinePlotter.new(function) + Function.Type.AREA: + plotter = AreaPlotter.new(function) + Function.Type.PIE: + plotter = PiePlotter.new(function) + Function.Type.BAR: + plotter = BarPlotter.new(function) + Function.Type.SCATTER, _: + plotter = ScatterPlotter.new(function) + return plotter func load_functions(functions: Array[Function]) -> void: - self.x = [] - self.y = [] - - function_legend.clear() - - # Remove existing function_plotters - for function_plotter in functions_box.get_children(): - functions_box.remove_child(function_plotter) - function_plotter.queue_free() - - for function in functions: - # Load x and y values - self.x.append(function.__x) - self.y.append(function.__y) - - # Load Labels - if self.x_labels.is_empty(): - if ECUtilities._contains_string(function.__x): - self.x_labels = function.__x - - # Create FunctionPlotter - var function_plotter: FunctionPlotter = get_function_plotter(function) - function_plotter.connect("point_entered", Callable(plot_box, "_on_point_entered")) - function_plotter.connect("point_exited", Callable(plot_box, "_on_point_exited")) - functions_box.add_child(function_plotter) - - # Create legend - match function.get_type(): - Function.Type.PIE: - for i in function.__x.size(): - var interp_color: Color = function.get_gradient().sample(float(i) / float(function.__x.size())) - function_legend.add_label(function.get_type(), interp_color, Function.Marker.NONE, function.__y[i]) - _: - function_legend.add_function(function) + self.x = [] + self.y = [] + + function_legend.clear() + + # Remove existing function_plotters + for function_plotter in functions_box.get_children(): + functions_box.remove_child(function_plotter) + function_plotter.queue_free() + + for function in functions: + # Load x and y values + self.x.append(function.__x) + self.y.append(function.__y) + + # Create FunctionPlotter + var function_plotter: FunctionPlotter = get_function_plotter(function) + function_plotter.connect("point_entered", Callable(plot_box, "_on_point_entered")) + function_plotter.connect("point_exited", Callable(plot_box, "_on_point_exited")) + functions_box.add_child(function_plotter) + + # Create legend + match function.get_type(): + Function.Type.PIE: + for i in function.__x.size(): + var interp_color: Color = function.get_gradient().sample(float(i) / float(function.__x.size())) + function_legend.add_label(function.get_type(), interp_color, Function.Marker.NONE, function.__y[i]) + _: + function_legend.add_function(function) func _draw() -> void: - if (x.size() == 0) or (y.size() == 0) or (x.size() == 1 and x[0].is_empty()) or (y.size() == 1 and y[0].is_empty()): - printerr("Cannot plot an empty function!") - return - - var is_x_fixed: bool = x_domain.get("fixed", false) - var is_y_fixed: bool = y_domain.get("fixed", false) - - # GridBox - if not is_x_fixed or not is_y_fixed : - if chart_properties.max_samples > 0 : - var _x: Array = [] - var _y: Array = [] - - _x.resize(x.size()) - _y.resize(y.size()) - - for i in x.size(): - if not is_x_fixed: - _x[i] = x[i].slice(max(0, x[i].size() - chart_properties.max_samples), x[i].size()) - if not is_y_fixed: - _y[i] = y[i].slice(max(0, y[i].size() - chart_properties.max_samples), y[i].size()) - - if not is_x_fixed: - x_domain = calculate_domain(_x) - if not is_y_fixed: - y_domain = calculate_domain(_y) - else: - if not is_x_fixed: - x_domain = calculate_domain(x) - if not is_y_fixed: - y_domain = calculate_domain(y) - - - var plotbox_margins: Vector2 = calculate_plotbox_margins(x_domain, y_domain) - - # Update values for the PlotBox in order to propagate them to the children - plot_box.box_margins = plotbox_margins - - # Update GridBox - update_gridbox(x_domain, y_domain, x_labels, y_labels) - - # Update each FunctionPlotter in FunctionsBox - for function_plotter in functions_box.get_children(): - if function_plotter is FunctionPlotter: - function_plotter.update_values(x_domain, y_domain) + if (x.size() == 0) or (y.size() == 0) or (x.size() == 1 and x[0].is_empty()) or (y.size() == 1 and y[0].is_empty()): + printerr("Cannot plot an empty function!") + return + + var is_x_fixed: bool = x_domain.get("fixed", false) + var is_y_fixed: bool = y_domain.get("fixed", false) + + # GridBox + if not is_x_fixed or not is_y_fixed : + if chart_properties.max_samples > 0 : + var _x: Array = [] + var _y: Array = [] + + _x.resize(x.size()) + _y.resize(y.size()) + + for i in x.size(): + if not is_x_fixed: + _x[i] = x[i].slice(max(0, x[i].size() - chart_properties.max_samples), x[i].size()) + if not is_y_fixed: + _y[i] = y[i].slice(max(0, y[i].size() - chart_properties.max_samples), y[i].size()) + + if not is_x_fixed: + x_domain = calculate_domain(_x) + if not is_y_fixed: + y_domain = calculate_domain(_y) + else: + if not is_x_fixed: + x_domain = calculate_domain(x) + if not is_y_fixed: + y_domain = calculate_domain(y) + + # Update values for the PlotBox in order to propagate them to the children + update_plotbox(x_domain, y_domain, x_labels_function, y_labels_function) + + # Update GridBox + update_gridbox(x_domain, y_domain, x_labels_function, y_labels_function) + + # Update each FunctionPlotter in FunctionsBox + for function_plotter in functions_box.get_children(): + if function_plotter is FunctionPlotter: + function_plotter.visible = function_plotter.function.get_visibility() + if function_plotter.function.get_visibility(): + function_plotter.update_values(x_domain, y_domain) func calculate_domain(values: Array) -> Dictionary: - for value_array in values: - if ECUtilities._contains_string(value_array): - return { lb = 0.0, ub = (value_array.size() - 1), has_decimals = false , fixed = false } - var min_max: Dictionary = ECUtilities._find_min_max(values) - - if chart_properties.smooth_domain: - return { lb = min_max.min, ub = min_max.max, has_decimals = ECUtilities._has_decimals(values), fixed = false } - else: - return { lb = ECUtilities._round_min(min_max.min), ub = ECUtilities._round_max(min_max.max), has_decimals = ECUtilities._has_decimals(values) , fixed = false } + for value_array in values: + if ECUtilities._contains_string(value_array): + return { lb = 0.0, ub = (value_array.size() - 1), has_decimals = false , fixed = false } + var min_max: Dictionary = ECUtilities._find_min_max(values) + + if not chart_properties.smooth_domain: + return { lb = min_max.min, ub = min_max.max, has_decimals = ECUtilities._has_decimals(values), fixed = false } + else: + return { lb = ECUtilities._round_min(min_max.min), ub = ECUtilities._round_max(min_max.max), has_decimals = ECUtilities._has_decimals(values) , fixed = false } func set_x_domain(lb: Variant, ub: Variant) -> void: - x_domain = { lb = lb, ub = ub, has_decimals = ECUtilities._has_decimals([lb, ub]), fixed = true } + x_domain = { lb = lb, ub = ub, has_decimals = ECUtilities._has_decimals([lb, ub]), fixed = true } func set_y_domain(lb: Variant, ub: Variant) -> void: - y_domain = { lb = lb, ub = ub, has_decimals = ECUtilities._has_decimals([lb, ub]), fixed = true } + y_domain = { lb = lb, ub = ub, has_decimals = ECUtilities._has_decimals([lb, ub]), fixed = true } -func update_gridbox(x_domain: Dictionary, y_domain: Dictionary, x_labels: PackedStringArray, y_labels: PackedStringArray) -> void: - grid_box.set_domains(x_domain, y_domain) - grid_box.set_labels(x_labels, y_labels) - grid_box.queue_redraw() +func update_plotbox(x_domain: Dictionary, y_domain: Dictionary, x_labels_function: Callable, y_labels_function: Callable) -> void: + plot_box.box_margins = calculate_plotbox_margins(x_domain, y_domain) + plot_box.set_labels_functions(x_labels_function, y_labels_function) + +func update_gridbox(x_domain: Dictionary, y_domain: Dictionary, x_labels_function: Callable, y_labels_function: Callable) -> void: + grid_box.set_domains(x_domain, y_domain) + grid_box.set_labels_functions(x_labels_function, y_labels_function) + grid_box.queue_redraw() func calculate_plotbox_margins(x_domain: Dictionary, y_domain: Dictionary) -> Vector2: - var plotbox_margins: Vector2 = Vector2( - chart_properties.x_tick_size, - chart_properties.y_tick_size - ) - - if chart_properties.show_tick_labels: - var x_ticklabel_size: Vector2 - var y_ticklabel_size: Vector2 - - var y_max_formatted: String = ECUtilities._format_value(y_domain.ub, y_domain.has_decimals) - if y_domain.lb < 0: # negative number - var y_min_formatted: String = ECUtilities._format_value(y_domain.lb, y_domain.has_decimals) - if y_min_formatted.length() >= y_max_formatted.length(): - y_ticklabel_size = chart_properties.get_string_size(y_min_formatted) - else: - y_ticklabel_size = chart_properties.get_string_size(y_max_formatted) - else: - y_ticklabel_size = chart_properties.get_string_size(y_max_formatted) - - plotbox_margins.x += y_ticklabel_size.x + chart_properties.x_ticklabel_space - plotbox_margins.y += ThemeDB.fallback_font_size + chart_properties.y_ticklabel_space - - return plotbox_margins + var plotbox_margins: Vector2 = Vector2( + chart_properties.x_tick_size, + chart_properties.y_tick_size + ) + + if chart_properties.show_tick_labels: + var x_ticklabel_size: Vector2 + var y_ticklabel_size: Vector2 + + var y_max_formatted: String = ECUtilities._format_value(y_domain.ub, y_domain.has_decimals) + if y_domain.lb < 0: # negative number + var y_min_formatted: String = ECUtilities._format_value(y_domain.lb, y_domain.has_decimals) + if y_min_formatted.length() >= y_max_formatted.length(): + y_ticklabel_size = chart_properties.get_string_size(y_min_formatted) + else: + y_ticklabel_size = chart_properties.get_string_size(y_max_formatted) + else: + y_ticklabel_size = chart_properties.get_string_size(y_max_formatted) + + plotbox_margins.x += y_ticklabel_size.x + chart_properties.x_ticklabel_space + plotbox_margins.y += ThemeDB.fallback_font_size + chart_properties.y_ticklabel_space + + return plotbox_margins diff --git a/addons/easy_charts/control_charts/chart.tscn b/addons/easy_charts/control_charts/chart.tscn index 2530101..2d2c788 100644 --- a/addons/easy_charts/control_charts/chart.tscn +++ b/addons/easy_charts/control_charts/chart.tscn @@ -92,7 +92,7 @@ layout_mode = 0 offset_left = -67.0 offset_top = -33.0 offset_right = -17.0 -offset_bottom = 33.0 +offset_bottom = 30.0 [node name="XLabel" type="Label" parent="Canvas/CanvasContainer/DataContainer/PlotContainer"] layout_mode = 2 diff --git a/addons/easy_charts/control_charts/plotters/function_plotter.gd b/addons/easy_charts/control_charts/plotters/function_plotter.gd index 53c667f..59de817 100644 --- a/addons/easy_charts/control_charts/plotters/function_plotter.gd +++ b/addons/easy_charts/control_charts/plotters/function_plotter.gd @@ -6,24 +6,27 @@ var x_domain: Dictionary var y_domain: Dictionary func _init(function: Function) -> void: - self.function = function + self.function = function func _ready() -> void: - set_process_input(get_chart_properties().interactive) + set_process_input(get_chart_properties().interactive) func update_values(x_domain: Dictionary, y_domain: Dictionary) -> void: - self.x_domain = x_domain - self.y_domain = y_domain - queue_redraw() + self.visible = self.function.get_visibility() + if not self.function.get_visibility(): + return + self.x_domain = x_domain + self.y_domain = y_domain + queue_redraw() func _draw() -> void: - pass + return func get_box() -> Rect2: - return get_parent().get_parent().get_plot_box() + return get_parent().get_parent().get_plot_box() func get_chart_properties() -> ChartProperties: - return get_parent().get_parent().chart_properties + return get_parent().get_parent().chart_properties func get_relative_position(position: Vector2) -> Vector2: - return position - global_position + return position - global_position diff --git a/addons/easy_charts/control_charts/plotters/scatter_plotter.gd b/addons/easy_charts/control_charts/plotters/scatter_plotter.gd index bfb23e5..63b75f1 100644 --- a/addons/easy_charts/control_charts/plotters/scatter_plotter.gd +++ b/addons/easy_charts/control_charts/plotters/scatter_plotter.gd @@ -11,77 +11,77 @@ var focused_point: Point var point_size: float func _init(function: Function) -> void: - super(function) - self.point_size = function.props.get("point_size", 3.0) + super(function) + self.point_size = function.props.get("point_size", 3.0) func _draw() -> void: - super._draw() - - var box: Rect2 = get_box() - var x_sampled_domain: Dictionary = { lb = box.position.x, ub = box.end.x } - var y_sampled_domain: Dictionary = { lb = box.end.y, ub = box.position.y } - sample(x_sampled_domain, y_sampled_domain) - - if function.get_marker() != Function.Marker.NONE: - for point in points: - # Don't plot points outside domain upper and lower bounds! - if point.position.y <= y_sampled_domain.lb and point.position.y >= y_sampled_domain.ub: - draw_function_point(point.position) + super._draw() + + var box: Rect2 = get_box() + var x_sampled_domain: Dictionary = { lb = box.position.x, ub = box.end.x } + var y_sampled_domain: Dictionary = { lb = box.end.y, ub = box.position.y } + sample(x_sampled_domain, y_sampled_domain) + + if function.get_marker() != Function.Marker.NONE: + for point in points: + # Don't plot points outside domain upper and lower bounds! + if point.position.y <= y_sampled_domain.lb and point.position.y >= y_sampled_domain.ub: + draw_function_point(point.position) func sample(x_sampled_domain: Dictionary, y_sampled_domain: Dictionary) -> void: - points = [] - points_positions = [] - var lower_bound: int = max(0, function.__x.size() - get_chart_properties().max_samples) \ - #disable sample display limits - if get_chart_properties().max_samples > 0 \ - else 0 - for i in range(lower_bound, function.__x.size()): - var _position: Vector2 = Vector2( - ECUtilities._map_domain(float(function.__x[i]), x_domain, x_sampled_domain), - ECUtilities._map_domain(float(function.__y[i]), y_domain, y_sampled_domain) - ) - points.push_back(Point.new(_position, { x = function.__x[i], y = function.__y[i] })) - points_positions.push_back(_position) + points = [] + points_positions = [] + var lower_bound: int = max(0, function.__x.size() - get_chart_properties().max_samples) \ + #disable sample display limits + if get_chart_properties().max_samples > 0 \ + else 0 + for i in range(lower_bound, function.__x.size()): + var _position: Vector2 = Vector2( + ECUtilities._map_domain(float(function.__x[i]), x_domain, x_sampled_domain), + ECUtilities._map_domain(float(function.__y[i]), y_domain, y_sampled_domain) + ) + points.push_back(Point.new(_position, { x = function.__x[i], y = function.__y[i] })) + points_positions.push_back(_position) func draw_function_point(point_position: Vector2) -> void: - match function.get_marker(): - Function.Marker.SQUARE: - draw_rect( - Rect2(point_position - (Vector2.ONE * point_size), (Vector2.ONE * point_size * 2)), - function.get_color(), true, 1.0 - ) - Function.Marker.TRIANGLE: - draw_colored_polygon( - PackedVector2Array([ - point_position + (Vector2.UP * point_size * 1.3), - point_position + (Vector2.ONE * point_size * 1.3), - point_position - (Vector2(1, -1) * point_size * 1.3) - ]), function.get_color(), [], null - ) - Function.Marker.CROSS: - draw_line( - point_position - (Vector2.ONE * point_size), - point_position + (Vector2.ONE * point_size), - function.get_color(), point_size, true - ) - draw_line( - point_position + (Vector2(1, -1) * point_size), - point_position + (Vector2(-1, 1) * point_size), - function.get_color(), point_size / 2, true - ) - Function.Marker.CIRCLE, _: - draw_circle(point_position, point_size, function.get_color()) + match function.get_marker(): + Function.Marker.SQUARE: + draw_rect( + Rect2(point_position - (Vector2.ONE * point_size), (Vector2.ONE * point_size * 2)), + function.get_color(), true, 1.0 + ) + Function.Marker.TRIANGLE: + draw_colored_polygon( + PackedVector2Array([ + point_position + (Vector2.UP * point_size * 1.3), + point_position + (Vector2.ONE * point_size * 1.3), + point_position - (Vector2(1, -1) * point_size * 1.3) + ]), function.get_color(), [], null + ) + Function.Marker.CROSS: + draw_line( + point_position - (Vector2.ONE * point_size), + point_position + (Vector2.ONE * point_size), + function.get_color(), point_size, true + ) + draw_line( + point_position + (Vector2(1, -1) * point_size), + point_position + (Vector2(-1, 1) * point_size), + function.get_color(), point_size / 2, true + ) + Function.Marker.CIRCLE, _: + draw_circle(point_position, point_size, function.get_color()) func _input(event: InputEvent) -> void: - if event is InputEventMouse: - for point in points: - if Geometry2D.is_point_in_circle(get_relative_position(event.position), point.position, self.point_size * 4): - if focused_point == point: - return - else: - focused_point = point - emit_signal("point_entered", point, function) - return - # Mouse is not in any point's box - emit_signal("point_exited", focused_point, function) - focused_point = null + if event is InputEventMouse: + for point in points: + if Geometry2D.is_point_in_circle(get_relative_position(event.position), point.position, self.point_size * 4): + if focused_point == point: + return + else: + focused_point = point + emit_signal("point_entered", point, function) + return + # Mouse is not in any point's box + emit_signal("point_exited", focused_point, function) + focused_point = null diff --git a/addons/easy_charts/examples/area_chart/Control.gd b/addons/easy_charts/examples/area_chart/Control.gd index 92154a1..76876c6 100644 --- a/addons/easy_charts/examples/area_chart/Control.gd +++ b/addons/easy_charts/examples/area_chart/Control.gd @@ -6,64 +6,64 @@ extends Control var f1: Function func _ready(): - # Let's create our @x values - var x: Array = ArrayOperations.multiply_float(range(-10, 11, 1), 0.5) - - # And our y values. It can be an n-size array of arrays. - # NOTE: `x.size() == y.size()` or `x.size() == y[n].size()` - var y: Array = ArrayOperations.multiply_int(ArrayOperations.cos(x), 20) - - # Let's customize the chart properties, which specify how the chart - # should look, plus some additional elements like labels, the scale, etc... - var cp: ChartProperties = ChartProperties.new() - cp.colors.frame = Color("#161a1d") - cp.colors.background = Color.TRANSPARENT - cp.colors.grid = Color("#283442") - cp.colors.ticks = Color("#283442") - cp.colors.text = Color.WHITE_SMOKE - cp.draw_bounding_box = false - cp.title = "Air Quality Monitoring" - cp.x_label = "Time" - cp.y_label = "Sensor values" - cp.x_scale = 5 - cp.y_scale = 10 - cp.interactive = true # false by default, it allows the chart to create a tooltip to show point values - # and interecept clicks on the plot - - # Let's add values to our functions - f1 = Function.new( - x, y, "Pressure", # This will create a function with x and y values taken by the Arrays - # we have created previously. This function will also be named "Pressure" - # as it contains 'pressure' values. - # If set, the name of a function will be used both in the Legend - # (if enabled thourgh ChartProperties) and on the Tooltip (if enabled). - # Let's also provide a dictionary of configuration parameters for this specific function. - { - color = Color("#36a2eb"), # The color associated to this function - marker = Function.Marker.NONE, # The marker that will be displayed for each drawn point (x,y) - # since it is `NONE`, no marker will be shown. - type = Function.Type.AREA, # This defines what kind of plotting will be used, - # in this case it will be an Area Chart. - } - ) - - # Now let's plot our data - chart.plot([f1], cp) - - # Uncommenting this line will show how real time data plotting works - set_process(false) + # Let's create our @x values + var x: Array = ArrayOperations.multiply_float(range(-10, 11, 1), 0.5) + + # And our y values. It can be an n-size array of arrays. + # NOTE: `x.size() == y.size()` or `x.size() == y[n].size()` + var y: Array = ArrayOperations.multiply_int(ArrayOperations.cos(x), 20) + + # Let's customize the chart properties, which specify how the chart + # should look, plus some additional elements like labels, the scale, etc... + var cp: ChartProperties = ChartProperties.new() + cp.colors.frame = Color("#161a1d") + cp.colors.background = Color.TRANSPARENT + cp.colors.grid = Color("#283442") + cp.colors.ticks = Color("#283442") + cp.colors.text = Color.WHITE_SMOKE + cp.draw_bounding_box = false + cp.title = "Air Quality Monitoring" + cp.x_label = "Time" + cp.y_label = "Sensor values" + cp.x_scale = 5 + cp.y_scale = 10 + cp.interactive = true # false by default, it allows the chart to create a tooltip to show point values + # and interecept clicks on the plot + + # Let's add values to our functions + f1 = Function.new( + x, y, "Pressure", # This will create a function with x and y values taken by the Arrays + # we have created previously. This function will also be named "Pressure" + # as it contains 'pressure' values. + # If set, the name of a function will be used both in the Legend + # (if enabled thourgh ChartProperties) and on the Tooltip (if enabled). + # Let's also provide a dictionary of configuration parameters for this specific function. + { + color = Color("#36a2eb"), # The color associated to this function + marker = Function.Marker.NONE, # The marker that will be displayed for each drawn point (x,y) + # since it is `NONE`, no marker will be shown. + type = Function.Type.AREA, # This defines what kind of plotting will be used, + # in this case it will be an Area Chart. + } + ) + + # Now let's plot our data + chart.plot([f1], cp) + + # Uncommenting this line will show how real time data plotting works + set_process(false) var new_val: float = 4.5 func _process(delta: float): - # This function updates the values of a function and then updates the plot - new_val += 5 - - # we can use the `Function.add_point(x, y)` method to update a function - f1.add_point(new_val, cos(new_val) * 20) - chart.queue_redraw() # This will force the Chart to be updated + # This function updates the values of a function and then updates the plot + new_val += 5 + + # we can use the `Function.add_point(x, y)` method to update a function + f1.add_point(new_val, cos(new_val) * 20) + chart.queue_redraw() # This will force the Chart to be updated func _on_CheckButton_pressed(): - set_process(not is_processing()) + set_process(not is_processing()) diff --git a/addons/easy_charts/examples/line_chart/Control.gd b/addons/easy_charts/examples/line_chart/Control.gd index 9d95f0d..f11e86f 100644 --- a/addons/easy_charts/examples/line_chart/Control.gd +++ b/addons/easy_charts/examples/line_chart/Control.gd @@ -6,67 +6,67 @@ extends Control var f1: Function func _ready(): - # Let's create our @x values - var x: PackedFloat32Array = ArrayOperations.multiply_float(range(-10, 11, 1), 0.5) - - # And our y values. It can be an n-size array of arrays. - # NOTE: `x.size() == y.size()` or `x.size() == y[n].size()` - var y: Array = ArrayOperations.multiply_int(ArrayOperations.cos(x), 20) - - # Let's customize the chart properties, which specify how the chart - # should look, plus some additional elements like labels, the scale, etc... - var cp: ChartProperties = ChartProperties.new() - cp.colors.frame = Color("#161a1d") - cp.colors.background = Color.TRANSPARENT - cp.colors.grid = Color("#283442") - cp.colors.ticks = Color("#283442") - cp.colors.text = Color.WHITE_SMOKE - cp.draw_bounding_box = false - cp.title = "Air Quality Monitoring" - cp.x_label = "Time" - cp.y_label = "Sensor values" - cp.x_scale = 5 - cp.y_scale = 10 - cp.interactive = true # false by default, it allows the chart to create a tooltip to show point values - # and interecept clicks on the plot - - # Let's add values to our functions - f1 = Function.new( - x, y, "Pressure", # This will create a function with x and y values taken by the Arrays - # we have created previously. This function will also be named "Pressure" - # as it contains 'pressure' values. - # If set, the name of a function will be used both in the Legend - # (if enabled thourgh ChartProperties) and on the Tooltip (if enabled). - # Let's also provide a dictionary of configuration parameters for this specific function. - { - color = Color("#36a2eb"), # The color associated to this function - marker = Function.Marker.CIRCLE, # The marker that will be displayed for each drawn point (x,y) - # since it is `NONE`, no marker will be shown. - type = Function.Type.LINE, # This defines what kind of plotting will be used, - # in this case it will be a Linear Chart. - interpolation = Function.Interpolation.STAIR # Interpolation mode, only used for - # Line Charts and Area Charts. - } - ) - - # Now let's plot our data - chart.plot([f1], cp) - - # Uncommenting this line will show how real time data plotting works - set_process(false) + # Let's create our @x values + var x: PackedFloat32Array = ArrayOperations.multiply_float(range(-10, 11, 1), 0.5) + + # And our y values. It can be an n-size array of arrays. + # NOTE: `x.size() == y.size()` or `x.size() == y[n].size()` + var y: Array = ArrayOperations.multiply_int(ArrayOperations.cos(x), 20) + + # Let's customize the chart properties, which specify how the chart + # should look, plus some additional elements like labels, the scale, etc... + var cp: ChartProperties = ChartProperties.new() + cp.colors.frame = Color("#161a1d") + cp.colors.background = Color.TRANSPARENT + cp.colors.grid = Color("#283442") + cp.colors.ticks = Color("#283442") + cp.colors.text = Color.WHITE_SMOKE + cp.draw_bounding_box = false + cp.title = "Air Quality Monitoring" + cp.x_label = "Time" + cp.y_label = "Sensor values" + cp.x_scale = 5 + cp.y_scale = 10 + cp.interactive = true # false by default, it allows the chart to create a tooltip to show point values + # and interecept clicks on the plot + + # Let's add values to our functions + f1 = Function.new( + x, y, "Pressure", # This will create a function with x and y values taken by the Arrays + # we have created previously. This function will also be named "Pressure" + # as it contains 'pressure' values. + # If set, the name of a function will be used both in the Legend + # (if enabled thourgh ChartProperties) and on the Tooltip (if enabled). + # Let's also provide a dictionary of configuration parameters for this specific function. + { + color = Color("#36a2eb"), # The color associated to this function + marker = Function.Marker.CIRCLE, # The marker that will be displayed for each drawn point (x,y) + # since it is `NONE`, no marker will be shown. + type = Function.Type.LINE, # This defines what kind of plotting will be used, + # in this case it will be a Linear Chart. + interpolation = Function.Interpolation.STAIR # Interpolation mode, only used for + # Line Charts and Area Charts. + } + ) + + # Now let's plot our data + chart.plot([f1], cp) + + # Uncommenting this line will show how real time data plotting works + set_process(false) var new_val: float = 4.5 func _process(delta: float): - # This function updates the values of a function and then updates the plot - new_val += 5 - - # we can use the `Function.add_point(x, y)` method to update a function - f1.add_point(new_val, cos(new_val) * 20) - f1.remove_point(0) - chart.queue_redraw() # This will force the Chart to be updated + # This function updates the values of a function and then updates the plot + new_val += 5 + + # we can use the `Function.add_point(x, y)` method to update a function + f1.add_point(new_val, cos(new_val) * 20) + f1.remove_point(0) + chart.queue_redraw() # This will force the Chart to be updated func _on_CheckButton_pressed(): - set_process(not is_processing()) + set_process(not is_processing()) diff --git a/addons/easy_charts/examples/multiplot/Control.gd b/addons/easy_charts/examples/multiplot/Control.gd index c239a0e..4dbff1f 100644 --- a/addons/easy_charts/examples/multiplot/Control.gd +++ b/addons/easy_charts/examples/multiplot/Control.gd @@ -8,73 +8,73 @@ var f2: Function var f3: Function func _ready(): - # Let's create our @x values - var x: Array = ArrayOperations.multiply_float(range(-10, 11, 1), 0.5) - - # And our y values. It can be an n-size array of arrays. - # NOTE: `x.size() == y.size()` or `x.size() == y[n].size()` - var y: Array = ArrayOperations.multiply_int(ArrayOperations.cos(x), 20) - var y2: Array = ArrayOperations.add_float(ArrayOperations.multiply_int(ArrayOperations.sin(x), 20), 20) - var y3: Array = ArrayOperations.add_float(ArrayOperations.multiply_int(ArrayOperations.cos(x), -5), -3) - - # Let's customize the chart properties, which specify how the chart - # should look, plus some additional elements like labels, the scale, etc... - var cp: ChartProperties = ChartProperties.new() - cp.colors.frame = Color("#161a1d") - cp.colors.background = Color.TRANSPARENT - cp.colors.grid = Color("#283442") - cp.colors.ticks = Color("#283442") - cp.colors.text = Color.WHITE_SMOKE - cp.draw_bounding_box = false - cp.show_legend = true - cp.title = "Air Quality Monitoring" - cp.x_label = "Time" - cp.y_label = "Sensor values" - cp.x_scale = 5 - cp.y_scale = 10 - cp.interactive = true # false by default, it allows the chart to create a tooltip to show point values - # and interecept clicks on the plot - - # Let's add values to our functions - f1 = Function.new( - x, y, "Pressure", # This will create a function with x and y values taken by the Arrays - # we have created previously. This function will also be named "Pressure" - # as it contains 'pressure' values. - # If set, the name of a function will be used both in the Legend - # (if enabled thourgh ChartProperties) and on the Tooltip (if enabled). - # Let's also provide a dictionary of configuration parameters for this specific function. - { - color = Color("#36a2eb"), # The color associated to this function - marker = Function.Marker.NONE, # The marker that will be displayed for each drawn point (x,y) - # since it is `NONE`, no marker will be shown. - type = Function.Type.AREA, # This defines what kind of plotting will be used, - # in this case it will be an Area Chart. - interpolation = Function.Interpolation.STAIR # Interpolation mode, only used for - # Line Charts and Area Charts. - } - ) - f2 = Function.new(x, y2, "Humidity", { color = Color("#ff6384"), marker = Function.Marker.CROSS }) - f3 = Function.new(x, y3, "CO2", { color = Color.GREEN, marker = Function.Marker.TRIANGLE }) - - # Now let's plot our data - chart.plot([f1, f2, f3], cp) - - # Uncommenting this line will show how real time data plotting works - set_process(false) + # Let's create our @x values + var x: Array = ArrayOperations.multiply_float(range(-10, 11, 1), 0.5) + + # And our y values. It can be an n-size array of arrays. + # NOTE: `x.size() == y.size()` or `x.size() == y[n].size()` + var y: Array = ArrayOperations.multiply_int(ArrayOperations.cos(x), 20) + var y2: Array = ArrayOperations.add_float(ArrayOperations.multiply_int(ArrayOperations.sin(x), 20), 20) + var y3: Array = ArrayOperations.add_float(ArrayOperations.multiply_int(ArrayOperations.cos(x), -5), -3) + + # Let's customize the chart properties, which specify how the chart + # should look, plus some additional elements like labels, the scale, etc... + var cp: ChartProperties = ChartProperties.new() + cp.colors.frame = Color("#161a1d") + cp.colors.background = Color.TRANSPARENT + cp.colors.grid = Color("#283442") + cp.colors.ticks = Color("#283442") + cp.colors.text = Color.WHITE_SMOKE + cp.draw_bounding_box = false + cp.show_legend = true + cp.title = "Air Quality Monitoring" + cp.x_label = "Time" + cp.y_label = "Sensor values" + cp.x_scale = 5 + cp.y_scale = 10 + cp.interactive = true # false by default, it allows the chart to create a tooltip to show point values + # and interecept clicks on the plot + + # Let's add values to our functions + f1 = Function.new( + x, y, "Pressure", # This will create a function with x and y values taken by the Arrays + # we have created previously. This function will also be named "Pressure" + # as it contains 'pressure' values. + # If set, the name of a function will be used both in the Legend + # (if enabled thourgh ChartProperties) and on the Tooltip (if enabled). + # Let's also provide a dictionary of configuration parameters for this specific function. + { + color = Color("#36a2eb"), # The color associated to this function + marker = Function.Marker.NONE, # The marker that will be displayed for each drawn point (x,y) + # since it is `NONE`, no marker will be shown. + type = Function.Type.AREA, # This defines what kind of plotting will be used, + # in this case it will be an Area Chart. + interpolation = Function.Interpolation.STAIR # Interpolation mode, only used for + # Line Charts and Area Charts. + } + ) + f2 = Function.new(x, y2, "Humidity", { color = Color("#ff6384"), marker = Function.Marker.CROSS }) + f3 = Function.new(x, y3, "CO2", { color = Color.GREEN, marker = Function.Marker.TRIANGLE }) + + # Now let's plot our data + chart.plot([f1, f2, f3], cp) + + # Uncommenting this line will show how real time data plotting works + set_process(false) var new_val: float = 4.5 func _process(delta: float): - # This function updates the values of a function and then updates the plot - new_val += 5 - - # we can use the `Function.add_point(x, y)` method to update a function - f1.add_point(new_val, cos(new_val) * 20) - f2.add_point(new_val, (sin(new_val) * 20) + 20) - f3.add_point(new_val, (cos(new_val) * -5) - 3) - chart.queue_redraw() # This will force the Chart to be updated + # This function updates the values of a function and then updates the plot + new_val += 5 + + # we can use the `Function.add_point(x, y)` method to update a function + f1.add_point(new_val, cos(new_val) * 20) + f2.add_point(new_val, (sin(new_val) * 20) + 20) + f3.add_point(new_val, (cos(new_val) * -5) - 3) + chart.queue_redraw() # This will force the Chart to be updated func _on_CheckButton_pressed(): - set_process(not is_processing()) + set_process(not is_processing()) diff --git a/addons/easy_charts/examples/scatter_chart/Control.gd b/addons/easy_charts/examples/scatter_chart/Control.gd index e2273de..cfa3f1c 100644 --- a/addons/easy_charts/examples/scatter_chart/Control.gd +++ b/addons/easy_charts/examples/scatter_chart/Control.gd @@ -7,60 +7,60 @@ var f1: Function var f2: Function func _ready(): - # Let's create our @x values - var x: Array = ArrayOperations.multiply_float(range(-10, 11, 1), 0.5) - - # And our y values. It can be an n-size array of arrays. - # NOTE: `x.size() == y.size()` or `x.size() == y[n].size()` - var y: Array = ArrayOperations.multiply_int(ArrayOperations.cos(x), 20) - var y2: Array = ArrayOperations.add_float(ArrayOperations.multiply_int(ArrayOperations.sin(x), 20), 20) - - # Let's customize the chart properties, which specify how the chart - # should look, plus some additional elements like labels, the scale, etc... - var cp: ChartProperties = ChartProperties.new() - cp.colors.frame = Color("#161a1d") - cp.colors.background = Color.TRANSPARENT - cp.colors.grid = Color("#283442") - cp.colors.ticks = Color("#283442") - cp.colors.text = Color.WHITE_SMOKE - cp.draw_bounding_box = false - cp.title = "Air Quality Monitoring" - cp.x_label = "Time" - cp.y_label = "Sensor values" - cp.x_scale = 5 - cp.y_scale = 10 - cp.interactive = true # false by default, it allows the chart to create a tooltip to show point values - # and interecept clicks on the plot - - # Let's add values to our functions - f1 = Function.new( - x, y, "Pressure", # This will create a function with x and y values taken by the Arrays - # we have created previously. This function will also be named "Pressure" - # as it contains 'pressure' values. - # If set, the name of a function will be used both in the Legend - # (if enabled thourgh ChartProperties) and on the Tooltip (if enabled). - { color = Color.GREEN, marker = Function.Marker.CIRCLE } - ) - f2 = Function.new(x, y2, "Humidity", { color = Color("#ff6384"), marker = Function.Marker.CROSS }) - - # Now let's plot our data - chart.plot([f1, f2], cp) - - # Uncommenting this line will show how real time data plotting works - set_process(false) + # Let's create our @x values + var x: Array = ArrayOperations.multiply_float(range(-10, 11, 1), 0.5) + + # And our y values. It can be an n-size array of arrays. + # NOTE: `x.size() == y.size()` or `x.size() == y[n].size()` + var y: Array = ArrayOperations.multiply_int(ArrayOperations.cos(x), 20) + var y2: Array = ArrayOperations.add_float(ArrayOperations.multiply_int(ArrayOperations.sin(x), 20), 20) + + # Let's customize the chart properties, which specify how the chart + # should look, plus some additional elements like labels, the scale, etc... + var cp: ChartProperties = ChartProperties.new() + cp.colors.frame = Color("#161a1d") + cp.colors.background = Color.TRANSPARENT + cp.colors.grid = Color("#283442") + cp.colors.ticks = Color("#283442") + cp.colors.text = Color.WHITE_SMOKE + cp.draw_bounding_box = false + cp.title = "Air Quality Monitoring" + cp.x_label = "Time" + cp.y_label = "Sensor values" + cp.x_scale = 5 + cp.y_scale = 10 + cp.interactive = true # false by default, it allows the chart to create a tooltip to show point values + # and interecept clicks on the plot + + # Let's add values to our functions + f1 = Function.new( + x, y, "Pressure", # This will create a function with x and y values taken by the Arrays + # we have created previously. This function will also be named "Pressure" + # as it contains 'pressure' values. + # If set, the name of a function will be used both in the Legend + # (if enabled thourgh ChartProperties) and on the Tooltip (if enabled). + { color = Color.GREEN, marker = Function.Marker.CIRCLE } + ) + f2 = Function.new(x, y2, "Humidity", { color = Color("#ff6384"), marker = Function.Marker.CROSS }) + + # Now let's plot our data + chart.plot([f1, f2], cp) + + # Uncommenting this line will show how real time data plotting works + set_process(false) var new_val: float = 4.5 func _process(delta: float): - # This function updates the values of a function and then updates the plot - new_val += 5 - - # we can use the `Function.add_point(x, y)` method to update a function - f1.add_point(new_val, cos(new_val) * 20) - f2.add_point(new_val, (sin(new_val) * 20) + 20) - chart.queue_redraw() # This will force the Chart to be updated + # This function updates the values of a function and then updates the plot + new_val += 5 + + # we can use the `Function.add_point(x, y)` method to update a function + f1.add_point(new_val, cos(new_val) * 20) + f2.add_point(new_val, (sin(new_val) * 20) + 20) + chart.queue_redraw() # This will force the Chart to be updated func _on_CheckButton_pressed(): - set_process(not is_processing()) + set_process(not is_processing()) diff --git a/addons/easy_charts/utilities/classes/plotting/chart_properties.gd b/addons/easy_charts/utilities/classes/plotting/chart_properties.gd index f506f2e..d687afe 100644 --- a/addons/easy_charts/utilities/classes/plotting/chart_properties.gd +++ b/addons/easy_charts/utilities/classes/plotting/chart_properties.gd @@ -48,28 +48,28 @@ var smooth_domain: bool = false ## instead older points will be just ignored. This will make your Function object x and y arrays ## grow linearly, but won't interfere with your own data. ## If you instead prefer to improve performances by completely remove older data from your Function -## object, consider calling the Function.remote_point(0) method before adding a new point and plotting +## object, consider calling the Function.remove_point(0) method before adding a new point and plotting ## again. var max_samples: int = 100 ## Dictionary of colors for all of the Chart elements. var colors: Dictionary = { - frame = Color.WHITE_SMOKE, - background = Color.WHITE, - borders = Color.RED, - bounding_box = Color.BLACK, - grid = Color.GRAY, - ticks = Color.BLACK, - text = Color.BLACK, - origin = Color.DIM_GRAY + frame = Color.WHITE_SMOKE, + background = Color.WHITE, + borders = Color.RED, + bounding_box = Color.BLACK, + grid = Color.GRAY, + ticks = Color.BLACK, + text = Color.BLACK, + origin = Color.DIM_GRAY } var font: FontFile = load("res://addons/easy_charts/utilities/assets/OpenSans-VariableFont_wdth,wght.ttf") var font_size: int = 13 func _init() -> void: - ThemeDB.set_fallback_font(font) - ThemeDB.set_fallback_font_size(font_size) + ThemeDB.set_fallback_font(font) + ThemeDB.set_fallback_font_size(font_size) func get_string_size(text: String) -> Vector2: - return font.get_string_size(text) + return font.get_string_size(text) diff --git a/addons/easy_charts/utilities/classes/plotting/function.gd b/addons/easy_charts/utilities/classes/plotting/function.gd index 7f191a5..0850740 100644 --- a/addons/easy_charts/utilities/classes/plotting/function.gd +++ b/addons/easy_charts/utilities/classes/plotting/function.gd @@ -2,27 +2,27 @@ extends RefCounted class_name Function enum Type { - SCATTER, - LINE, - AREA, - PIE, - BAR + SCATTER, + LINE, + AREA, + PIE, + BAR } enum Interpolation { - NONE, - LINEAR, - STAIR, - SPLINE + NONE, + LINEAR, + STAIR, + SPLINE } # TODO: add new markers, like an empty circle, an empty box, etc. enum Marker { - NONE, - CIRCLE, - TRIANGLE, - SQUARE, - CROSS + NONE, + CIRCLE, + TRIANGLE, + SQUARE, + CROSS } var __x: Array @@ -31,60 +31,63 @@ var name: String var props: Dictionary = {} func _init(x: Array, y: Array, name: String = "", props: Dictionary = {}) -> void: - self.__x = x.duplicate() - self.__y = y.duplicate() - self.name = name - if not props.is_empty() and props != null: - self.props = props + self.__x = x.duplicate() + self.__y = y.duplicate() + self.name = name + if not props.is_empty() and props != null: + self.props = props func get_point(index: int) -> Array: - return [self.__x[index], self.__y[index]] + return [self.__x[index], self.__y[index]] func add_point(x: float, y: float) -> void: - self.__x.append(x) - self.__y.append(y) + self.__x.append(x) + self.__y.append(y) func set_point(index: int, x: float, y: float) -> void: - self.__x[index] = x - self.__y[index] = y + self.__x[index] = x + self.__y[index] = y func remove_point(index: int) -> void: - self.__x.remove_at(index) - self.__y.remove_at(index) + self.__x.remove_at(index) + self.__y.remove_at(index) func pop_back_point() -> void: - self.__x.pop_back() - self.__y.pop_back() + self.__x.pop_back() + self.__y.pop_back() func pop_front_point() -> void: - self.__x.pop_front() - self.__y.pop_front() + self.__x.pop_front() + self.__y.pop_front() func count_points() -> int: - return self.__x.size() + return self.__x.size() func get_color() -> Color: - return props.get("color", Color.DARK_SLATE_GRAY) + return props.get("color", Color.DARK_SLATE_GRAY) func get_gradient() -> Gradient: - return props.get("gradient", Gradient.new()) + return props.get("gradient", Gradient.new()) func get_marker() -> int: - return props.get("marker", Marker.NONE) + return props.get("marker", Marker.NONE) func get_type() -> int: - return props.get("type", Type.SCATTER) + return props.get("type", Type.SCATTER) func get_interpolation() -> int: - return props.get("interpolation", Interpolation.LINEAR) + return props.get("interpolation", Interpolation.LINEAR) func get_line_width() -> float: - return props.get("line_width", 2.0) + return props.get("line_width", 2.0) + +func get_visibility() -> bool: + return props.get("visible", true) func copy() -> Function: - return Function.new( - self.__x.duplicate(), - self.__y.duplicate(), - self.name, - self.props.duplicate(true) - ) + return Function.new( + self.__x.duplicate(), + self.__y.duplicate(), + self.name, + self.props.duplicate(true) + ) diff --git a/addons/easy_charts/utilities/classes/plotting/point.gd b/addons/easy_charts/utilities/classes/plotting/point.gd index 150aa89..f027726 100644 --- a/addons/easy_charts/utilities/classes/plotting/point.gd +++ b/addons/easy_charts/utilities/classes/plotting/point.gd @@ -5,8 +5,8 @@ var position: Vector2 var value: Dictionary func _init(position: Vector2, value: Dictionary) -> void: - self.position = position - self.value = value + self.position = position + self.value = value func _to_string() -> String: - return "Value: %s\nPosition: %s" % [self.value, self.position] + return "Value: %s\nPosition: %s" % [self.value, self.position] diff --git a/addons/easy_charts/utilities/containers/canvas/canvas.gd b/addons/easy_charts/utilities/containers/canvas/canvas.gd index eefc471..c4f79ea 100644 --- a/addons/easy_charts/utilities/containers/canvas/canvas.gd +++ b/addons/easy_charts/utilities/containers/canvas/canvas.gd @@ -7,58 +7,58 @@ class_name Canvas @onready var _legend: FunctionLegend = $CanvasContainer/DataContainer/FunctionLegend func _ready(): - pass # Replace with function body. + pass # Replace with function body. func prepare_canvas(chart_properties: ChartProperties) -> void: - - if chart_properties.draw_frame: - set_color(chart_properties.colors.frame) - set_frame_visible(true) - else: - set_frame_visible(false) - - if chart_properties.show_title: - update_title(chart_properties.title, chart_properties.colors.text) - else: - _title_lbl.hide() - - if chart_properties.show_x_label: - update_x_label(chart_properties.x_label, chart_properties.colors.text) - else: - _x_lbl.hide() - - if chart_properties.show_y_label: - update_y_label(chart_properties.y_label, chart_properties.colors.text, -90) - else: - _y_lbl.hide() - - if chart_properties.show_legend: - _legend.show() - else: - hide_legend() + + if chart_properties.draw_frame: + set_color(chart_properties.colors.frame) + set_frame_visible(true) + else: + set_frame_visible(false) + + if chart_properties.show_title: + update_title(chart_properties.title, chart_properties.colors.text) + else: + _title_lbl.hide() + + if chart_properties.show_x_label: + update_x_label(chart_properties.x_label, chart_properties.colors.text) + else: + _x_lbl.hide() + + if chart_properties.show_y_label: + update_y_label(chart_properties.y_label, chart_properties.colors.text, -90) + else: + _y_lbl.hide() + + if chart_properties.show_legend: + _legend.show() + else: + hide_legend() func update_title(text: String, color: Color, rotation: float = 0.0) -> void: - _title_lbl.show() - _update_canvas_label(_title_lbl, text, color, rotation) + _title_lbl.show() + _update_canvas_label(_title_lbl, text, color, rotation) func update_y_label(text: String, color: Color, rotation: float = 0.0) -> void: - _y_lbl.show() - _update_canvas_label(_y_lbl, text, color, rotation) + _y_lbl.show() + _update_canvas_label(_y_lbl, text, color, rotation) func update_x_label(text: String, color: Color, rotation: float = 0.0) -> void: - _x_lbl.show() - _update_canvas_label(_x_lbl, text, color, rotation) + _x_lbl.show() + _update_canvas_label(_x_lbl, text, color, rotation) func _update_canvas_label(canvas_label: Label, text: String, color: Color, rotation: float = 0.0) -> void: - canvas_label.set_text(text) - canvas_label.modulate = color - canvas_label.rotation = rotation + canvas_label.set_text(text) + canvas_label.modulate = color + canvas_label.rotation = rotation func hide_legend() -> void: - _legend.hide() + _legend.hide() func set_color(color: Color) -> void: - get("theme_override_styles/panel").set("bg_color", color) + get("theme_override_styles/panel").set("bg_color", color) func set_frame_visible(visible: bool) -> void: - get("theme_override_styles/panel").set("draw_center", visible) + get("theme_override_styles/panel").set("draw_center", visible) diff --git a/addons/easy_charts/utilities/containers/canvas/canvas.tscn b/addons/easy_charts/utilities/containers/canvas/canvas.tscn deleted file mode 100644 index 3850537..0000000 --- a/addons/easy_charts/utilities/containers/canvas/canvas.tscn +++ /dev/null @@ -1,73 +0,0 @@ -[gd_scene load_steps=5 format=2] - -[ext_resource path="res://addons/easy_charts/utilities/containers/canvas/canvas.gd" type="Script" id=1] -[ext_resource path="res://addons/easy_charts/utilities/containers/canvas/plot_box/PlotBox.tscn" type="PackedScene" id=2] - -[sub_resource type="StyleBoxEmpty" id=1] -content_margin_left = 15.0 -content_margin_right = 15.0 -content_margin_top = 15.0 -content_margin_bottom = 15.0 - -[sub_resource type="StyleBoxEmpty" id=3] - -[node name="Canvas" type="PanelContainer"] -anchor_right = 1.0 -anchor_bottom = 1.0 -theme_override_styles/panel = SubResource( 1 ) -script = ExtResource( 1 ) - -[node name="VBoxContainer" type="VBoxContainer" parent="."] -offset_left = 15.0 -offset_top = 15.0 -offset_right = 1009.0 -offset_bottom = 585.0 -size_flags_vertical = 3 - -[node name="Title" type="Label" parent="VBoxContainer"] -offset_right = 994.0 -offset_bottom = 14.0 -text = "{title}" -align = 1 - -[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"] -offset_top = 18.0 -offset_right = 994.0 -offset_bottom = 570.0 -size_flags_vertical = 3 - -[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/HBoxContainer"] -offset_right = 994.0 -offset_bottom = 552.0 -size_flags_horizontal = 3 - -[node name="YLabel" type="Label" parent="VBoxContainer/HBoxContainer/HBoxContainer"] -offset_top = 269.0 -offset_right = 48.0 -offset_bottom = 283.0 -text = "{ylabel}" - -[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/HBoxContainer/HBoxContainer"] -offset_left = 52.0 -offset_right = 990.0 -offset_bottom = 552.0 -size_flags_horizontal = 3 - -[node name="PlotBox" parent="VBoxContainer/HBoxContainer/HBoxContainer/VBoxContainer" instance=ExtResource( 2 )] -anchor_right = 0.0 -anchor_bottom = 0.0 -offset_right = 938.0 -offset_bottom = 534.0 - -[node name="XLabel" type="Label" parent="VBoxContainer/HBoxContainer/HBoxContainer/VBoxContainer"] -offset_top = 538.0 -offset_right = 938.0 -offset_bottom = 552.0 -text = "{xlabel}" -align = 1 - -[node name="Legend" type="PanelContainer" parent="VBoxContainer/HBoxContainer/HBoxContainer"] -offset_left = 994.0 -offset_right = 994.0 -offset_bottom = 552.0 -theme_override_styles/panel = SubResource( 3 ) diff --git a/addons/easy_charts/utilities/containers/canvas/plot_box/grid_box.gd b/addons/easy_charts/utilities/containers/canvas/plot_box/grid_box.gd index 6f97893..bdc2049 100644 --- a/addons/easy_charts/utilities/containers/canvas/plot_box/grid_box.gd +++ b/addons/easy_charts/utilities/containers/canvas/plot_box/grid_box.gd @@ -2,174 +2,175 @@ extends Control class_name GridBox var x_domain: Dictionary = { lb = 0, ub = 0 } -var x_labels: Array +var x_labels_function: Callable = Callable() var y_domain: Dictionary = { lb = 0, ub = 0 } -var y_labels: Array +var y_labels_function: Callable = Callable() var box: Rect2 var plot_box: Rect2 # Called when the node enters the scene tree for the first time. func _ready(): - pass # Replace with function body. + pass # Replace with function body. func set_domains(x_domain: Dictionary, y_domain: Dictionary) -> void: - self.x_domain = x_domain - self.y_domain = y_domain + self.x_domain = x_domain + self.y_domain = y_domain -func set_labels(x_labels: Array, y_labels: Array) -> void: - self.x_labels = x_labels - self.y_labels = y_labels +func set_labels_functions(x_labels_function: Callable, y_labels_function: Callable) -> void: + self.x_labels_function = x_labels_function + self.y_labels_function = y_labels_function func _draw() -> void: - if get_parent().chart_properties == null: - printerr("Cannot draw GridBox without ChartProperties!") - return - - self.box = get_parent().get_box() - self.plot_box = get_parent().get_plot_box() - - if get_parent().chart_properties.draw_background: - _draw_background() - - if get_parent().chart_properties.draw_grid_box: - _draw_vertical_grid() - _draw_horizontal_grid() - - if get_parent().chart_properties.draw_origin: - _draw_origin() - - if get_parent().chart_properties.draw_bounding_box: - _draw_bounding_box() + if get_parent().chart_properties == null: + printerr("Cannot draw GridBox without ChartProperties!") + return + + self.box = get_parent().get_box() + self.plot_box = get_parent().get_plot_box() + + if get_parent().chart_properties.draw_background: + _draw_background() + + if get_parent().chart_properties.draw_grid_box: + _draw_vertical_grid() + _draw_horizontal_grid() + + if get_parent().chart_properties.draw_origin: + _draw_origin() + + if get_parent().chart_properties.draw_bounding_box: + _draw_bounding_box() func _draw_background() -> void: - draw_rect(self.box, get_parent().chart_properties.colors.background, true)# false) TODOGODOT4 Antialiasing argument is missing + draw_rect(self.box, get_parent().chart_properties.colors.background, true)# false) TODOGODOT4 Antialiasing argument is missing func _draw_bounding_box() -> void: - var box: Rect2 = self.box - box.position.y += 1 - draw_rect(box, get_parent().chart_properties.colors.bounding_box, false, 1)# true) TODOGODOT4 Antialiasing argument is missing + var box: Rect2 = self.box + box.position.y += 1 + draw_rect(box, get_parent().chart_properties.colors.bounding_box, false, 1)# true) TODOGODOT4 Antialiasing argument is missing func _draw_origin() -> void: - var xorigin: float = ECUtilities._map_domain(0.0, x_domain, { lb = self.plot_box.position.x, ub = self.plot_box.end.x }) - var yorigin: float = ECUtilities._map_domain(0.0, y_domain, { lb = self.plot_box.end.y, ub = self.plot_box.position.y }) - - draw_line(Vector2(xorigin, self.plot_box.position.y), Vector2(xorigin, self.plot_box.position.y + self.plot_box.size.y), get_parent().chart_properties.colors.origin, 1) - draw_line(Vector2(self.plot_box.position.x, yorigin), Vector2(self.plot_box.position.x + self.plot_box.size.x, yorigin), get_parent().chart_properties.colors.origin, 1) - draw_string( - get_parent().chart_properties.font, Vector2(xorigin, yorigin) - Vector2(15, -15), "O", HORIZONTAL_ALIGNMENT_CENTER, -1, - ThemeDB.fallback_font_size, get_parent().chart_properties.colors.text, TextServer.JUSTIFICATION_NONE, TextServer.DIRECTION_AUTO, TextServer.ORIENTATION_HORIZONTAL - ) + var xorigin: float = ECUtilities._map_domain(0.0, x_domain, { lb = self.plot_box.position.x, ub = self.plot_box.end.x }) + var yorigin: float = ECUtilities._map_domain(0.0, y_domain, { lb = self.plot_box.end.y, ub = self.plot_box.position.y }) + + draw_line(Vector2(xorigin, self.plot_box.position.y), Vector2(xorigin, self.plot_box.position.y + self.plot_box.size.y), get_parent().chart_properties.colors.origin, 1) + draw_line(Vector2(self.plot_box.position.x, yorigin), Vector2(self.plot_box.position.x + self.plot_box.size.x, yorigin), get_parent().chart_properties.colors.origin, 1) + draw_string( + get_parent().chart_properties.font, Vector2(xorigin, yorigin) - Vector2(15, -15), "O", HORIZONTAL_ALIGNMENT_CENTER, -1, + ThemeDB.fallback_font_size, get_parent().chart_properties.colors.text, TextServer.JUSTIFICATION_NONE, TextServer.DIRECTION_AUTO, TextServer.ORIENTATION_HORIZONTAL + ) func _draw_vertical_grid() -> void: - # draw vertical lines - - # 1. the amount of lines is equals to the X_scale: it identifies in how many sectors the x domain - # should be devided - # 2. calculate the spacing between each line in pixel. It is equals to x_sampled_domain / x_scale - # 3. calculate the offset in the real x domain, which is x_domain / x_scale. - var scaler: int = get_parent().chart_properties.x_scale if self.x_labels.is_empty() else (x_labels.size() - 1) - var x_pixel_dist: float = self.plot_box.size.x / scaler - - var vertical_grid: PackedVector2Array = [] - var vertical_ticks: PackedVector2Array = [] - - for _x in (scaler + 1): - var x_sampled_val: float = (_x * x_pixel_dist) + self.plot_box.position.x - var x_val: float = ECUtilities._map_domain(x_sampled_val, { lb = self.plot_box.position.x, ub = self.plot_box.end.x }, x_domain) - - var top: Vector2 = Vector2(x_sampled_val, self.box.position.y) - var bottom: Vector2 = Vector2(x_sampled_val, self.box.end.y) - - vertical_grid.append(top) - vertical_grid.append(bottom) - - vertical_ticks.append(bottom) - vertical_ticks.append(bottom + Vector2(0, get_parent().chart_properties.x_tick_size)) - - # Draw V Tick Labels - if get_parent().chart_properties.show_tick_labels: - var tick_lbl: String = _get_tick_label(_x, x_val, x_domain.has_decimals, self.x_labels) - draw_string( - get_parent().chart_properties.font, - _get_vertical_tick_label_pos(bottom, tick_lbl), - tick_lbl,HORIZONTAL_ALIGNMENT_CENTER, -1, ThemeDB.fallback_font_size, - get_parent().chart_properties.colors.text, TextServer.JUSTIFICATION_NONE, TextServer.DIRECTION_AUTO, - TextServer.ORIENTATION_HORIZONTAL - ) - - # Draw V Grid - if get_parent().chart_properties.draw_vertical_grid: - draw_multiline(vertical_grid, get_parent().chart_properties.colors.grid, 1) - - # Draw V Ticks - if get_parent().chart_properties.draw_ticks: - draw_multiline(vertical_ticks, get_parent().chart_properties.colors.ticks, 1) + # draw vertical lines + + # 1. the amount of lines is equals to the X_scale: it identifies in how many sectors the x domain + # should be devided + # 2. calculate the spacing between each line in pixel. It is equals to x_sampled_domain / x_scale + # 3. calculate the offset in the real x domain, which is x_domain / x_scale. + var scaler: int = get_parent().chart_properties.x_scale + var x_pixel_dist: float = self.plot_box.size.x / scaler + + var vertical_grid: PackedVector2Array = [] + var vertical_ticks: PackedVector2Array = [] + + for _x in (scaler + 1): + var x_sampled_val: float = (_x * x_pixel_dist) + self.plot_box.position.x + var x_val: float = ECUtilities._map_domain(x_sampled_val, { lb = self.plot_box.position.x, ub = self.plot_box.end.x }, x_domain) + + var top: Vector2 = Vector2(x_sampled_val, self.box.position.y) + var bottom: Vector2 = Vector2(x_sampled_val, self.box.end.y) + + vertical_grid.append(top) + vertical_grid.append(bottom) + + vertical_ticks.append(bottom) + vertical_ticks.append(bottom + Vector2(0, get_parent().chart_properties.x_tick_size)) + + # Draw V Tick Labels + if get_parent().chart_properties.show_tick_labels: + var tick_lbl: String = _get_tick_label(_x, x_val, x_domain.has_decimals, self.x_labels_function) + draw_string( + get_parent().chart_properties.font, + _get_vertical_tick_label_pos(bottom, tick_lbl), + tick_lbl,HORIZONTAL_ALIGNMENT_CENTER, -1, ThemeDB.fallback_font_size, + get_parent().chart_properties.colors.text, TextServer.JUSTIFICATION_NONE, TextServer.DIRECTION_AUTO, + TextServer.ORIENTATION_HORIZONTAL + ) + + # Draw V Grid + if get_parent().chart_properties.draw_vertical_grid: + draw_multiline(vertical_grid, get_parent().chart_properties.colors.grid, 1) + + # Draw V Ticks + if get_parent().chart_properties.draw_ticks: + draw_multiline(vertical_ticks, get_parent().chart_properties.colors.ticks, 1) func _draw_horizontal_grid() -> void: - # 1. the amount of lines is equals to the y_scale: it identifies in how many sectors the y domain - # should be devided - # 2. calculate the spacing between each line in pixel. It is equals to y_sampled_domain / y_scale - # 3. calculate the offset in the real y domain, which is y_domain / y_scale. - var y_pixel_dist: float = self.plot_box.size.y / get_parent().chart_properties.y_scale - - var horizontal_grid: PackedVector2Array = [] - var horizontal_ticks: PackedVector2Array = [] - - for _y in get_parent().chart_properties.y_scale + 1: - var y_sampled_val: float = (_y * y_pixel_dist) + self.plot_box.position.y - var y_val: float = ECUtilities._map_domain(y_sampled_val, { lb = self.plot_box.end.y, ub = self.plot_box.position.y }, y_domain) - - var left: Vector2 = Vector2(self.box.position.x, y_sampled_val) - var right: Vector2 = Vector2(self.box.end.x, y_sampled_val) - - horizontal_grid.append(left) - horizontal_grid.append(right) - - horizontal_ticks.append(left) - horizontal_ticks.append(left - Vector2(get_parent().chart_properties.y_tick_size, 0)) - - # Draw H Tick Labels - if get_parent().chart_properties.show_tick_labels: - var tick_lbl: String = _get_tick_label(_y, y_val, y_domain.has_decimals, y_labels) - draw_string( - get_parent().chart_properties.font, - _get_horizontal_tick_label_pos(left, tick_lbl), - tick_lbl, - HORIZONTAL_ALIGNMENT_CENTER, - -1, ThemeDB.fallback_font_size, - get_parent().chart_properties.colors.text, - TextServer.JUSTIFICATION_NONE, TextServer.DIRECTION_AUTO, TextServer.ORIENTATION_HORIZONTAL - ) - - # Draw H Grid - if get_parent().chart_properties.draw_horizontal_grid: - draw_multiline(horizontal_grid, get_parent().chart_properties.colors.grid, 1) - - # Draw H Ticks - if get_parent().chart_properties.draw_ticks: - draw_multiline(horizontal_ticks, get_parent().chart_properties.colors.ticks, 1) - + # 1. the amount of lines is equals to the y_scale: it identifies in how many sectors the y domain + # should be devided + # 2. calculate the spacing between each line in pixel. It is equals to y_sampled_domain / y_scale + # 3. calculate the offset in the real y domain, which is y_domain / y_scale. + var scaler: int = get_parent().chart_properties.y_scale + var y_pixel_dist: float = self.plot_box.size.y / scaler + + var horizontal_grid: PackedVector2Array = [] + var horizontal_ticks: PackedVector2Array = [] + + for _y in (scaler + 1): + var y_sampled_val: float = (_y * y_pixel_dist) + self.plot_box.position.y + var y_val: float = ECUtilities._map_domain(y_sampled_val, { lb = self.plot_box.end.y, ub = self.plot_box.position.y }, y_domain) + + var left: Vector2 = Vector2(self.box.position.x, y_sampled_val) + var right: Vector2 = Vector2(self.box.end.x, y_sampled_val) + + horizontal_grid.append(left) + horizontal_grid.append(right) + + horizontal_ticks.append(left) + horizontal_ticks.append(left - Vector2(get_parent().chart_properties.y_tick_size, 0)) + + # Draw H Tick Labels + if get_parent().chart_properties.show_tick_labels: + var tick_lbl: String = _get_tick_label(_y, y_val, y_domain.has_decimals, y_labels_function) + draw_string( + get_parent().chart_properties.font, + _get_horizontal_tick_label_pos(left, tick_lbl), + tick_lbl, + HORIZONTAL_ALIGNMENT_CENTER, + -1, ThemeDB.fallback_font_size, + get_parent().chart_properties.colors.text, + TextServer.JUSTIFICATION_NONE, TextServer.DIRECTION_AUTO, TextServer.ORIENTATION_HORIZONTAL + ) + + # Draw H Grid + if get_parent().chart_properties.draw_horizontal_grid: + draw_multiline(horizontal_grid, get_parent().chart_properties.colors.grid, 1) + + # Draw H Ticks + if get_parent().chart_properties.draw_ticks: + draw_multiline(horizontal_ticks, get_parent().chart_properties.colors.ticks, 1) + func _get_vertical_tick_label_pos(base_position: Vector2, text: String) -> Vector2: - return base_position + Vector2( - - get_parent().chart_properties.font.get_string_size(text).x / 2, - ThemeDB.fallback_font_size + get_parent().chart_properties.x_tick_size - ) + return base_position + Vector2( + - get_parent().chart_properties.font.get_string_size(text).x / 2, + ThemeDB.fallback_font_size + get_parent().chart_properties.x_tick_size + ) func _get_horizontal_tick_label_pos(base_position: Vector2, text: String) -> Vector2: - return base_position - Vector2( - get_parent().chart_properties.font.get_string_size(text).x + get_parent().chart_properties.y_tick_size + get_parent().chart_properties.x_ticklabel_space, - - ThemeDB.fallback_font_size * 0.35 - ) - -func _get_tick_label(line_index: int, line_value: float, axis_has_decimals: bool, labels: PackedStringArray) -> String: - var tick_lbl: String = "" - if labels.is_empty(): - tick_lbl = ECUtilities._format_value(line_value, axis_has_decimals) - else: - tick_lbl = labels[line_index] - return tick_lbl + return base_position - Vector2( + get_parent().chart_properties.font.get_string_size(text).x + get_parent().chart_properties.y_tick_size + get_parent().chart_properties.x_ticklabel_space, + - ThemeDB.fallback_font_size * 0.35 + ) + +func _get_tick_label(line_index: int, line_value: float, axis_has_decimals: bool, labels_function: Callable) -> String: + var tick_lbl: String = "" + if labels_function.is_null(): + tick_lbl = ECUtilities._format_value(line_value, axis_has_decimals) + else: + tick_lbl = labels_function.call(line_value) + return tick_lbl diff --git a/addons/easy_charts/utilities/containers/canvas/plot_box/plot_box.gd b/addons/easy_charts/utilities/containers/canvas/plot_box/plot_box.gd index 5a58778..cac5626 100644 --- a/addons/easy_charts/utilities/containers/canvas/plot_box/plot_box.gd +++ b/addons/easy_charts/utilities/containers/canvas/plot_box/plot_box.gd @@ -8,40 +8,49 @@ signal function_point_exited(point, function) var focused_point: Point var focused_function: Function +var x_labels_function: Callable = Callable() +var y_labels_function: Callable = Callable() + var box_margins: Vector2 # Margins relative to this rect, in order to make space for ticks and tick_labels var plot_inner_offset: Vector2 = Vector2(15, 15) # How many pixels from the broders should the plot be var chart_properties: ChartProperties +func set_labels_functions(x_labels_function: Callable, y_labels_function: Callable) -> void: + self.x_labels_function = x_labels_function + self.y_labels_function = y_labels_function + func get_box() -> Rect2: - var box: Rect2 = get_rect() - box.position.x += box_margins.x + var box: Rect2 = get_rect() + box.position.x += box_margins.x # box.position.y += box_margins.y - box.end.x -= box_margins.x - box.end.y -= box_margins.y - return box + box.end.x -= box_margins.x + box.end.y -= box_margins.y + return box func get_plot_box() -> Rect2: - var inner_box: Rect2 = get_box() - inner_box.position.x += plot_inner_offset.x - inner_box.position.y += plot_inner_offset.y - inner_box.end.x -= plot_inner_offset.x * 2 - inner_box.end.y -= plot_inner_offset.y * 2 - return inner_box + var inner_box: Rect2 = get_box() + inner_box.position.x += plot_inner_offset.x + inner_box.position.y += plot_inner_offset.y + inner_box.end.x -= plot_inner_offset.x * 2 + inner_box.end.y -= plot_inner_offset.y * 2 + return inner_box func _on_point_entered(point: Point, function: Function, props: Dictionary = {}) -> void: - self.focused_function = function - var x_value: String = point.value.x if point.value.x is String else ECUtilities._format_value(point.value.x, ECUtilities._is_decimal(point.value.x)) - var y_value: String = point.value.y if point.value.y is String else ECUtilities._format_value(point.value.y, ECUtilities._is_decimal(point.value.y)) - var color: Color = function.get_color() if function.get_type() != Function.Type.PIE \ - else function.get_gradient().sample(props.interpolation_index) - tooltip.show() - tooltip.update_values(x_value, y_value, function.name, color) - tooltip.update_position(point.position) - emit_signal("function_point_entered", point, function) + self.focused_function = function + var x_value: String = x_labels_function.call(point.value.x) if not x_labels_function.is_null() else \ + point.value.x if point.value.x is String else ECUtilities._format_value(point.value.x, ECUtilities._is_decimal(point.value.x)) + var y_value: String = y_labels_function.call(point.value.y) if not y_labels_function.is_null() else \ + point.value.y if point.value.y is String else ECUtilities._format_value(point.value.y, ECUtilities._is_decimal(point.value.y)) + var color: Color = function.get_color() if function.get_type() != Function.Type.PIE \ + else function.get_gradient().sample(props.interpolation_index) + tooltip.show() + tooltip.update_values(x_value, y_value, function.name, color) + tooltip.update_position(point.position) + emit_signal("function_point_entered", point, function) func _on_point_exited(point: Point, function: Function) -> void: - if function != self.focused_function: - return - tooltip.hide() - emit_signal("function_point_exited", point, function) + if function != self.focused_function: + return + tooltip.hide() + emit_signal("function_point_exited", point, function) diff --git a/addons/easy_charts/utilities/containers/data_tooltip/data_tooltip.gd b/addons/easy_charts/utilities/containers/data_tooltip/data_tooltip.gd index a9b10ff..be0f40a 100644 --- a/addons/easy_charts/utilities/containers/data_tooltip/data_tooltip.gd +++ b/addons/easy_charts/utilities/containers/data_tooltip/data_tooltip.gd @@ -10,14 +10,14 @@ var gap: float = 15 @onready var color_rect: Panel = $PointData/Value/Color func _ready(): - hide() - update_size() + hide() + update_size() func update_position(position: Vector2) -> void: - if (position.x + gap + size.x) > get_parent().size.x: - self.position = position - Vector2(size.x + gap, (get_rect().size.y / 2)) - else: - self.position = position + Vector2(15, - (get_rect().size.y / 2)) + if (position.x + gap + size.x) > get_parent().size.x: + self.position = position - Vector2(size.x + gap, (get_rect().size.y / 2)) + else: + self.position = position + Vector2(15, - (get_rect().size.y / 2)) #func _process(delta): # if Engine.editor_hint: @@ -25,20 +25,20 @@ func update_position(position: Vector2) -> void: # rect_position = get_global_mouse_position() + Vector2(15, - (get_rect().size.y / 2)) func set_font(font: FontFile) -> void: - theme.set("default_font", font) + theme.set("default_font", font) func update_values(x: String, y: String, function_name: String, color: Color): - x_lbl.set_text(x) - y_lbl.set_text(y) - func_lbl.set_text(function_name) - color_rect.get("theme_override_styles/panel").set("bg_color", color) + x_lbl.set_text(x) + y_lbl.set_text(y) + func_lbl.set_text(function_name) + color_rect.get("theme_override_styles/panel").set("bg_color", color) func update_size(): - x_lbl.set_text("") - y_lbl.set_text("") - func_lbl.set_text("") - size = Vector2.ZERO + x_lbl.set_text("") + y_lbl.set_text("") + func_lbl.set_text("") + size = Vector2.ZERO func _on_DataTooltip_visibility_changed(): - if not visible: - update_size() + if not visible: + update_size() diff --git a/addons/easy_charts/utilities/containers/legend/function_legend.gd b/addons/easy_charts/utilities/containers/legend/function_legend.gd index e2925ab..a5de795 100644 --- a/addons/easy_charts/utilities/containers/legend/function_legend.gd +++ b/addons/easy_charts/utilities/containers/legend/function_legend.gd @@ -6,18 +6,18 @@ class_name FunctionLegend var chart_properties: ChartProperties func _ready() -> void: - pass + pass func clear() -> void: - for label in get_children(): - label.queue_free() + for label in get_children(): + label.queue_free() func add_function(function: Function) -> void: - var f_label: FunctionLabel = f_label_scn.instantiate() - add_child(f_label) - f_label.init_label(function) + var f_label: FunctionLabel = f_label_scn.instantiate() + add_child(f_label) + f_label.init_label(function) func add_label(type: int, color: Color, marker: int, name: String) -> void: - var f_label: FunctionLabel = f_label_scn.instantiate() - add_child(f_label) - f_label.init_clabel(type, color, marker, name) + var f_label: FunctionLabel = f_label_scn.instantiate() + add_child(f_label) + f_label.init_clabel(type, color, marker, name) diff --git a/addons/easy_charts/utilities/scripts/ec_utilities.gd b/addons/easy_charts/utilities/scripts/ec_utilities.gd index 6d137cb..74265dd 100644 --- a/addons/easy_charts/utilities/scripts/ec_utilities.gd +++ b/addons/easy_charts/utilities/scripts/ec_utilities.gd @@ -4,68 +4,68 @@ class_name ECUtilities var alphabet : String = "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z" func _ready(): - pass + pass static func _map_domain(value: float, from_domain: Dictionary, to_domain: Dictionary) -> float: - return remap(value, from_domain.lb, from_domain.ub, to_domain.lb, to_domain.ub) + return remap(value, from_domain.lb, from_domain.ub, to_domain.lb, to_domain.ub) static func _format_value(value: float, is_decimal: bool) -> String: - return ("%.2f" if is_decimal else "%s") % snapped(value, 0.01) + return ("%.2f" if is_decimal else "%s") % snapped(value, 0.01) ### Utility Inner functions ### static func _contains_string(array: Array) -> bool: - for value in array: - if value is String: - return true - return false + for value in array: + if value is String: + return true + return false static func _is_decimal(value: float) -> bool: - return abs(fmod(value, 1)) > 0.0 + return abs(fmod(value, 1)) > 0.0 static func _has_decimals(values: Array) -> bool: - var temp: Array = values.duplicate(true) - - for dim in temp: - for val in dim: - if val is String: - return false - if abs(fmod(val, 1)) > 0.0: - return true - - return false + var temp: Array = values.duplicate(true) + + for dim in temp: + for val in dim: + if val is String: + return false + if abs(fmod(val, 1)) > 0.0: + return true + + return false static func _find_min_max(values: Array) -> Dictionary: - var temp: Array = values.duplicate(true) - var _min: float - var _max: float - - var min_ts: Array - var max_ts: Array - for dim in temp: - min_ts.append(dim.min()) - max_ts.append(dim.max()) - _min = min(min_ts.min(), 0) - _max = max(0, max_ts.max()) - - return { min = _min, max = _max } + var temp: Array = values.duplicate(true) + var _min: float + var _max: float + + var min_ts: Array + var max_ts: Array + for dim in temp: + min_ts.append(dim.min()) + max_ts.append(dim.max()) + _min = min_ts.min() + _max = max_ts.max() + + return { min = _min, max = _max } static func _sample_values(values: Array, from_domain: Dictionary, to_domain: Dictionary) -> PackedFloat32Array: - if values.is_empty(): - printerr("Trying to plot an empty dataset!") - return PackedFloat32Array() - - # We are not considering String values here!!! - - var sampled: PackedFloat32Array = [] - - for value in values: - sampled.push_back(_map_domain(value, from_domain, to_domain)) - - return sampled + if values.is_empty(): + printerr("Trying to plot an empty dataset!") + return PackedFloat32Array() + + # We are not considering String values here!!! + + var sampled: PackedFloat32Array = [] + + for value in values: + sampled.push_back(_map_domain(value, from_domain, to_domain)) + + return sampled static func _round_min(val: float) -> float: - return round(val) if abs(val) < 10 else floor(val / 10.0) * 10.0 + return round(val) if abs(val) < 10 else floor(val / 10.0) * 10.0 static func _round_max(val: float) -> float: - return round(val) if abs(val) < 10 else ceil(val / 10.0) * 10.0 + return round(val) if abs(val) < 10 else ceil(val / 10.0) * 10.0 diff --git a/icon.svg b/icon.svg new file mode 100644 index 0000000..3fe4f4a --- /dev/null +++ b/icon.svg @@ -0,0 +1 @@ +