From f7864e4755563ce73ddc9e57c19a4603e7b1a4d4 Mon Sep 17 00:00:00 2001 From: fenix-hub Date: Thu, 12 Jan 2023 19:48:57 +0100 Subject: [PATCH] update drawing options + example --- .../ScatterChart/scatter_chart.gd | 34 ++++----- addons/easy_charts/control_charts/chart.gd | 75 +++++++++---------- .../examples/scatter_chart/Control.gd | 49 ++++++++++++ .../examples/scatter_chart/Control.tscn | 49 ++++++++++++ .../classes/plotting/chart_properties.gd | 25 +++++++ .../classes/plotting/drawing_options.gd | 27 ------- .../classes/structures/array_operations.gd | 56 ++++++++++++++ 7 files changed, 232 insertions(+), 83 deletions(-) create mode 100644 addons/easy_charts/examples/scatter_chart/Control.gd create mode 100644 addons/easy_charts/examples/scatter_chart/Control.tscn delete mode 100644 addons/easy_charts/utilities/classes/plotting/drawing_options.gd create mode 100644 addons/easy_charts/utilities/classes/structures/array_operations.gd diff --git a/addons/easy_charts/control_charts/ScatterChart/scatter_chart.gd b/addons/easy_charts/control_charts/ScatterChart/scatter_chart.gd index 0cd40fd..2bb099e 100644 --- a/addons/easy_charts/control_charts/ScatterChart/scatter_chart.gd +++ b/addons/easy_charts/control_charts/ScatterChart/scatter_chart.gd @@ -13,14 +13,12 @@ var _point_box_rad: int = 10 func _ready(): pass -func plot(x: Array, y: Array, drawing_options: DrawingOptions = null, chart_properties: ChartProperties = null) -> void: +func plot(x: Array, y: Array, properties: ChartProperties = self.chart_properties) -> void: self.x = x self.y = y - if chart_properties != null: - self.chart_properties = chart_properties - if drawing_options != null: - self.drawing_options = drawing_options + if properties != null: + self.chart_properties = properties update() @@ -63,29 +61,29 @@ func _input(event: InputEvent): func _draw_point(point: Point, function_index: int) -> void: points.append(point) - match drawing_options.get_point_shape(function_index): + match chart_properties.get_point_shape(function_index): Point.Shape.CIRCLE: - draw_circle(point.position, drawing_options.point_radius, drawing_options.get_function_color(function_index)) + draw_circle(point.position, chart_properties.point_radius, chart_properties.get_function_color(function_index)) Point.Shape.SQUARE: - draw_rect(_get_point_box(point, drawing_options.point_radius), drawing_options.get_function_color(function_index), true, 1.0, false) + draw_rect(_get_point_box(point, chart_properties.point_radius), chart_properties.get_function_color(function_index), true, 1.0, false) Point.Shape.TRIANGLE: draw_colored_polygon( PoolVector2Array([ - point.position + (Vector2.UP * drawing_options.point_radius * 1.3), - point.position + (Vector2.ONE * drawing_options.point_radius * 1.3), - point.position - (Vector2(1, -1) * drawing_options.point_radius * 1.3) - ]), drawing_options.get_function_color(function_index), [], null, null, false + point.position + (Vector2.UP * chart_properties.point_radius * 1.3), + point.position + (Vector2.ONE * chart_properties.point_radius * 1.3), + point.position - (Vector2(1, -1) * chart_properties.point_radius * 1.3) + ]), chart_properties.get_function_color(function_index), [], null, null, false ) Point.Shape.CROSS: draw_line( - point.position - (Vector2.ONE * drawing_options.point_radius), - point.position + (Vector2.ONE * drawing_options.point_radius), - drawing_options.get_function_color(function_index), drawing_options.point_radius, true + point.position - (Vector2.ONE * chart_properties.point_radius), + point.position + (Vector2.ONE * chart_properties.point_radius), + chart_properties.get_function_color(function_index), chart_properties.point_radius, true ) draw_line( - point.position + (Vector2(1, -1) * drawing_options.point_radius), - point.position + (Vector2(-1, 1) * drawing_options.point_radius), - drawing_options.get_function_color(function_index), drawing_options.point_radius / 2, true + point.position + (Vector2(1, -1) * chart_properties.point_radius), + point.position + (Vector2(-1, 1) * chart_properties.point_radius), + chart_properties.get_function_color(function_index), chart_properties.point_radius / 2, true ) # # (debug) diff --git a/addons/easy_charts/control_charts/chart.gd b/addons/easy_charts/control_charts/chart.gd index ebd81bb..0037c4f 100644 --- a/addons/easy_charts/control_charts/chart.gd +++ b/addons/easy_charts/control_charts/chart.gd @@ -14,7 +14,6 @@ var x_labels: Array = [] var y_labels: Array = [] ###### STYLE -var drawing_options: DrawingOptions = DrawingOptions.new() var chart_properties: ChartProperties = ChartProperties.new() #### INTERNAL @@ -46,7 +45,7 @@ var _x_ticklabel_offset: int = 5 # offset only on the X axis var _x_tick_size: int = 7 ########### -func plot(x: Array, y: Array, drawing_options: DrawingOptions = DrawingOptions.new(), chart_properties: ChartProperties = ChartProperties.new()) -> void: +func plot(x: Array, y: Array, properties: ChartProperties = self.chart_properties) -> void: pass func _map_pair(val: float, rel: Pair, ref: Pair) -> float: @@ -132,10 +131,10 @@ func _pre_process_drawings() -> void: var offset: Vector2 = _padding_offset ### if @labels drawing is enabled, calcualte offsets - if drawing_options.labels: + if chart_properties.labels: ### labels (X, Y, Title) - _x_label_size = drawing_options.font.get_string_size(chart_properties.x_label) - _y_label_size = drawing_options.font.get_string_size(chart_properties.y_label) + _x_label_size = chart_properties.font.get_string_size(chart_properties.x_label) + _y_label_size = chart_properties.font.get_string_size(chart_properties.y_label) ### tick labels @@ -144,7 +143,7 @@ func _pre_process_drawings() -> void: # calculate the string length of the largest value on the Y axis. # remember that "-" sign adds additional pixels, and it is relative only to negative numbers! var x_max_formatted: String = ("%.2f" if x_has_decimals else "%s") % x_min_max.right - _x_ticklabel_size = drawing_options.font.get_string_size(x_max_formatted) + _x_ticklabel_size = chart_properties.font.get_string_size(x_max_formatted) offset.y += _x_label_offset + _x_label_size.y + _x_ticklabel_offset + _x_ticklabel_size.y @@ -157,16 +156,16 @@ func _pre_process_drawings() -> void: # negative number var y_min_formatted: String = ("%.2f" if y_has_decimals else "%s") % y_min_max.left if y_min_formatted.length() >= y_max_formatted.length(): - _y_ticklabel_size = drawing_options.font.get_string_size(y_min_formatted) + _y_ticklabel_size = chart_properties.font.get_string_size(y_min_formatted) else: - _y_ticklabel_size = drawing_options.font.get_string_size(y_max_formatted) + _y_ticklabel_size = chart_properties.font.get_string_size(y_max_formatted) else: - _y_ticklabel_size = drawing_options.font.get_string_size(y_max_formatted) + _y_ticklabel_size = chart_properties.font.get_string_size(y_max_formatted) offset.x += _y_label_offset + _y_label_size.y + _y_ticklabel_offset + _y_ticklabel_size.x ### if @ticks drawing is enabled, calculate offsets - if drawing_options.ticks: + if chart_properties.ticks: offset.x += _y_tick_size offset.y += _x_tick_size @@ -204,7 +203,7 @@ func _draw_borders() -> void: draw_rect(node_box, Color.red, false, 1, true) func _draw_bounding_box() -> void: - draw_rect(bounding_box, drawing_options.colors.bounding_box, false, 1, true) + draw_rect(bounding_box, chart_properties.colors.bounding_box, false, 1, true) # # (debug) # var half: Vector2 = (bounding_box.size) / 2 @@ -216,7 +215,7 @@ func _draw_origin() -> void: var yorigin: float = _map_pair(0.0, y_min_max, y_sampled_domain) draw_line(Vector2(xorigin, bounding_box.position.y), Vector2(xorigin, bounding_box.position.y + bounding_box.size.y), Color.black, 1, 0) draw_line(Vector2(bounding_box.position.x, yorigin), Vector2(bounding_box.position.x + bounding_box.size.x, yorigin), Color.black, 1, 0) - draw_string(drawing_options.font, Vector2(xorigin, yorigin) - Vector2(15, -15), "O", drawing_options.colors.bounding_box) + draw_string(chart_properties.font, Vector2(xorigin, yorigin) - Vector2(15, -15), "O", chart_properties.colors.bounding_box) func _draw_background() -> void: draw_rect(node_box, Color.white, true, 1.0, false) @@ -247,7 +246,7 @@ func _draw_grid() -> void: ) # Draw V labels - if drawing_options.labels: + if chart_properties.labels: var tick_lbl: String = "" if x_labels.empty(): tick_lbl = ("%.2f" if x_has_decimals else "%s") % x_val @@ -255,22 +254,22 @@ func _draw_grid() -> void: tick_lbl = x_labels[clamp(x_labels_spacing * _x, 0, x_labels.size() - 1)] draw_string( - drawing_options.font, + chart_properties.font, p2 + Vector2( - - drawing_options.font.get_string_size(tick_lbl).x / 2, + - chart_properties.font.get_string_size(tick_lbl).x / 2, _x_label_size.y + _x_tick_size ), tick_lbl, - drawing_options.colors.bounding_box + chart_properties.colors.bounding_box ) # Draw V Ticks - if drawing_options.ticks: - draw_line(p2, p2 + Vector2(0, _x_tick_size), drawing_options.colors.bounding_box, 1, true) + if chart_properties.ticks: + draw_line(p2, p2 + Vector2(0, _x_tick_size), chart_properties.colors.bounding_box, 1, true) # Draw V Grid Lines - if drawing_options.grid: - draw_line(p1, p2, drawing_options.colors.grid, 1, true) + if chart_properties.grid: + draw_line(p1, p2, chart_properties.colors.grid, 1, true) # draw horizontal lines var h_lines: float = (y_sampled.min_max.right - y_sampled.min_max.left) / (chart_properties.y_scale - 1) @@ -287,7 +286,7 @@ func _draw_grid() -> void: ) # Draw H labels - if drawing_options.labels: + if chart_properties.labels: var tick_lbl: String = "" if y_labels.empty(): tick_lbl = ("%.2f" if y_has_decimals else "%s") % y_val @@ -295,29 +294,29 @@ func _draw_grid() -> void: tick_lbl = y_labels[clamp(y_labels * _y, 0, y_labels.size() - 1)] draw_string( - drawing_options.font, - p1 - Vector2(drawing_options.font.get_string_size(tick_lbl).x + _y_ticklabel_offset + _y_tick_size, - _y_ticklabel_size.y * 0.35), + chart_properties.font, + p1 - Vector2(chart_properties.font.get_string_size(tick_lbl).x + _y_ticklabel_offset + _y_tick_size, - _y_ticklabel_size.y * 0.35), tick_lbl, - drawing_options.colors.bounding_box + chart_properties.colors.bounding_box ) # Draw H Ticks - if drawing_options.ticks: + if chart_properties.ticks: draw_line( p1, p1 - Vector2(_y_tick_size, 0), - drawing_options.colors.bounding_box, 1, true) + chart_properties.colors.bounding_box, 1, true) # Draw H Grid Lines - if drawing_options.grid: - draw_line(p1, p2, drawing_options.colors.grid, 1, true) + if chart_properties.grid: + draw_line(p1, p2, chart_properties.colors.grid, 1, true) func _create_canvas_label(text: String, position: Vector2, rotation: float = 0.0) -> Label: var lbl: Label = Label.new() $Canvas.add_child(lbl) - lbl.set("custom_fonts/font", drawing_options.font) + lbl.set("custom_fonts/font", chart_properties.font) lbl.set_text(text) - lbl.modulate = drawing_options.colors.bounding_box + lbl.modulate = chart_properties.colors.bounding_box lbl.rect_rotation = rotation lbl.rect_position = position return lbl @@ -341,7 +340,7 @@ func _draw_xaxis_label() -> void: func _draw_title() -> void: _create_canvas_label( chart_properties.title, - Vector2(node_box.size.x / 2, _padding_offset.y*2) - (drawing_options.font.get_string_size(chart_properties.title) / 2) + Vector2(node_box.size.x / 2, _padding_offset.y*2) - (chart_properties.font.get_string_size(chart_properties.title) / 2) ) func _clear_points() -> void: @@ -361,27 +360,27 @@ func _draw(): _clear() _pre_process() - if drawing_options.background: + if chart_properties.background: _draw_background() - if drawing_options.borders: + if chart_properties.borders: _draw_borders() - if drawing_options.labels: + if chart_properties.labels: _draw_xaxis_label() _draw_yaxis_label() _draw_title() - if drawing_options.grid or drawing_options.ticks or drawing_options.labels: + if chart_properties.grid or chart_properties.ticks or chart_properties.labels: _draw_grid() - if drawing_options.bounding_box: + if chart_properties.bounding_box: _draw_bounding_box() - if drawing_options.origin: + if chart_properties.origin: _draw_origin() - if drawing_options.points: + if chart_properties.points: _draw_points() func _validate_sampled_axis(x_data: SampledAxis, y_data: SampledAxis) -> int: diff --git a/addons/easy_charts/examples/scatter_chart/Control.gd b/addons/easy_charts/examples/scatter_chart/Control.gd new file mode 100644 index 0000000..ce75c55 --- /dev/null +++ b/addons/easy_charts/examples/scatter_chart/Control.gd @@ -0,0 +1,49 @@ +extends Control + +func _ready(): + # Let's create our @x values + var x: Array = ArrayOperations.multiply_float(range(-10, 10, 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), + ArrayOperations.add_float(ArrayOperations.multiply_int(ArrayOperations.sin(x), 20), 20) + ] + + # Add some labels for the x axis, we don't want to use our x values array + # they will be printed on the chart ticks instead of the value of the x axis. + var x_labels: Array = ArrayOperations.suffix(x, "s") + + # 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.grid = true + cp.origin = false + cp.title = "Air Quality Monitoring" + cp.x_label = ("Time") + cp.x_scale = 10 + cp.y_label = ("Sensor values") + cp.y_scale = 10 + + # Set the x_labels + $ScatterChart.x_labels = x_labels + + # Plot our data + $ScatterChart.plot(x, y, cp) + + # Uncommenting this line will show how real time data plotting works + set_process(false) + +func _process(delta: float): + # This function updates the values of chart x, y, and x_labels array + # and updaptes the plot + var new_val: float = $ScatterChart.x.back() + 1 + $ScatterChart.x.append(new_val) + $ScatterChart.y[0].append(cos(new_val) * 20) + $ScatterChart.y[1].append(20 + sin(new_val) * 20) + $ScatterChart.x_labels.append(str(new_val) + "s") + $ScatterChart.update() + + +func _on_CheckButton_pressed(): + set_process(not is_processing()) diff --git a/addons/easy_charts/examples/scatter_chart/Control.tscn b/addons/easy_charts/examples/scatter_chart/Control.tscn new file mode 100644 index 0000000..85c6da0 --- /dev/null +++ b/addons/easy_charts/examples/scatter_chart/Control.tscn @@ -0,0 +1,49 @@ +[gd_scene load_steps=4 format=2] + +[ext_resource path="res://addons/easy_charts/control_charts/ScatterChart/scatter_chart.tscn" type="PackedScene" id=1] +[ext_resource path="res://addons/easy_charts/examples/scatter_chart/Control.gd" type="Script" id=2] + +[sub_resource type="StyleBoxFlat" id=1] +content_margin_right = 5.0 +content_margin_bottom = 5.0 +draw_center = false +border_width_right = 2 +border_width_bottom = 2 +border_color = Color( 0, 0, 0, 1 ) + +[node name="Control" type="Control"] +anchor_right = 1.0 +anchor_bottom = 1.0 +script = ExtResource( 2 ) +__meta__ = { +"_edit_use_anchors_": true +} + +[node name="ScatterChart" parent="." instance=ExtResource( 1 )] + +[node name="CheckButton" type="CheckButton" parent="."] +margin_right = 223.0 +margin_bottom = 40.0 +custom_colors/font_color_disabled = Color( 0, 0, 0, 1 ) +custom_colors/font_color_focus = Color( 0, 0, 0, 1 ) +custom_colors/font_color_hover_pressed = Color( 0, 0, 0, 1 ) +custom_colors/font_color = Color( 0, 0, 0, 1 ) +custom_colors/font_color_hover = Color( 0, 0, 0, 1 ) +custom_colors/font_color_pressed = Color( 0, 0, 0, 1 ) +text = "Start Relatime Plotting" + +[node name="Label" type="Label" parent="."] +anchor_left = 1.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +margin_left = -159.0 +margin_top = -19.0 +custom_colors/font_color = Color( 0, 0, 0, 1 ) +custom_styles/normal = SubResource( 1 ) +text = "Try to scale the window!" +__meta__ = { +"_edit_lock_": true +} + +[connection signal="pressed" from="CheckButton" to="." method="_on_CheckButton_pressed"] diff --git a/addons/easy_charts/utilities/classes/plotting/chart_properties.gd b/addons/easy_charts/utilities/classes/plotting/chart_properties.gd index fadbd6d..87f8f55 100644 --- a/addons/easy_charts/utilities/classes/plotting/chart_properties.gd +++ b/addons/easy_charts/utilities/classes/plotting/chart_properties.gd @@ -7,3 +7,28 @@ var y_label: String var x_scale: float = 5.0 var y_scale: float = 2.0 + +var points: bool = true +var grid: bool = false +var bounding_box: bool = true +var background: bool = true +var borders: bool = false +var ticks: bool = true +var labels: bool = true +var origin: bool = true + +var colors: Dictionary = { + bounding_box = Color.black, + grid = Color.gray, + functions = [Color.red, Color.green, Color.blue, Color.black] +} + +var shapes: Array = [Point.Shape.CIRCLE, Point.Shape.SQUARE, Point.Shape.TRIANGLE, Point.Shape.CROSS] +var point_radius: float = 3.0 +var font: BitmapFont = Label.new().get_font("") + +func get_function_color(function_index: int) -> Color: + return colors.functions[function_index] if function_index < colors.functions.size() else Color.black + +func get_point_shape(function_index: int) -> int: + return shapes[function_index] if function_index < shapes.size() else Point.Shape.CIRCLE diff --git a/addons/easy_charts/utilities/classes/plotting/drawing_options.gd b/addons/easy_charts/utilities/classes/plotting/drawing_options.gd deleted file mode 100644 index d7ebbce..0000000 --- a/addons/easy_charts/utilities/classes/plotting/drawing_options.gd +++ /dev/null @@ -1,27 +0,0 @@ -extends Reference -class_name DrawingOptions - -var points: bool = true -var grid: bool = false -var bounding_box: bool = true -var background: bool = true -var borders: bool = false -var ticks: bool = true -var labels: bool = true -var origin: bool = true - -var colors: Dictionary = { - bounding_box = Color.black, - grid = Color.gray, - functions = [Color.red, Color.green, Color.blue, Color.black] -} - -var shapes: Array = [Point.Shape.CIRCLE, Point.Shape.SQUARE, Point.Shape.TRIANGLE, Point.Shape.CROSS] -var point_radius: float = 3.0 -var font: BitmapFont = Label.new().get_font("") - -func get_function_color(function_index: int) -> Color: - return colors.functions[function_index] if function_index < colors.functions.size() else Color.black - -func get_point_shape(function_index: int) -> int: - return shapes[function_index] if function_index < shapes.size() else Point.Shape.CIRCLE diff --git a/addons/easy_charts/utilities/classes/structures/array_operations.gd b/addons/easy_charts/utilities/classes/structures/array_operations.gd new file mode 100644 index 0000000..05b3922 --- /dev/null +++ b/addons/easy_charts/utilities/classes/structures/array_operations.gd @@ -0,0 +1,56 @@ +extends Reference +class_name ArrayOperations + +static func add_int(array: Array, _int: int) -> Array: + var t: Array = array.duplicate(true) + for ti in t.size(): + t[ti] = int(t[ti] + _int) + return t + +static func add_float(array: Array, _float: float) -> Array: + var t: Array = array.duplicate(true) + for ti in t.size(): + t[ti] = float(t[ti] + _float) + return t + +static func multiply_int(array: Array, _int: int) -> Array: + var t: Array = array.duplicate(true) + for ti in t.size(): + t[ti] = int(t[ti] * _int) + return t + +static func multiply_float(array: Array, _float: float) -> Array: + var t: Array = array.duplicate(true) + for ti in t.size(): + t[ti] = float(t[ti] * _float) + return t + +static func pow(array: Array, _int: int) -> Array: + var t: Array = array.duplicate(true) + for ti in t.size(): + t[ti] = float(pow(t[ti], _int)) + return t + +static func cos(array: Array) -> Array: + var t: Array = array.duplicate(true) + for val in array.size(): + t[val] = cos(t[val]) + return t + +static func sin(array: Array) -> Array: + var t: Array = array.duplicate(true) + for val in array.size(): + t[val] = sin(t[val]) + return t + +static func affix(array: Array, _string: String) -> Array: + var t: Array = array.duplicate(true) + for val in array.size(): + t[val] = str(t[val]) + _string + return t + +static func suffix(array: Array, _string: String) -> Array: + var t: Array = array.duplicate(true) + for val in array.size(): + t[val] = _string + str(t[val]) + return t