From d2b04f0953fb25da834d89bb5780ca34a231c716 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Santilio?= Date: Thu, 31 Dec 2020 12:29:31 +0100 Subject: [PATCH 01/13] Update plugin.gd --- addons/easy_charts/plugin.gd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/easy_charts/plugin.gd b/addons/easy_charts/plugin.gd index 1c636e2..c6083f7 100644 --- a/addons/easy_charts/plugin.gd +++ b/addons/easy_charts/plugin.gd @@ -4,7 +4,7 @@ extends EditorPlugin func _enter_tree(): add_autoload_singleton("Utilities","res://addons/easy_charts/Utilities/Scripts/utilities.gd") add_custom_type("Chart","Control", load("res://addons/easy_charts/Utilities/Scripts/chart.gd"), preload("Utilities/icons/linechart.svg")) - add_custom_type("Chart2D","Node2D", preload("Utilities/Scripts/chart2d.gd"), preload("Utilities/icons/linechart2d.svg")) + add_custom_type("Chart2D","Node2D", load("res://addons/easy_charts/Utilities/Scripts/chart2d.gd"), preload("Utilities/icons/linechart2d.svg")) func _exit_tree(): remove_custom_type("Chart") From c7d0e56f4edfeb6dec3c2df895a5da6e4f40a92d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Santilio?= Date: Thu, 31 Dec 2020 13:10:20 +0100 Subject: [PATCH 02/13] git update --- .gitignore | 11 +- .../BarChart2D/LineChart2D.tscn.depren | 108 ------- .../LineChart2D/LineChart2D.tscn.depren | 108 ------- addons/easy_charts/RadarChart/RadarChart.gd | 280 ------------------ .../Utilities/Rect/Line-Graph1.png | Bin 71512 -> 0 bytes .../Utilities/Rect/Line-Graph1.png.import | 34 --- .../Utilities/Scripts/ChartObject.gd | 39 --- .../Utilities/Scripts/chart_object.gd | 39 --- 8 files changed, 8 insertions(+), 611 deletions(-) delete mode 100644 addons/easy_charts/BarChart2D/LineChart2D.tscn.depren delete mode 100644 addons/easy_charts/LineChart2D/LineChart2D.tscn.depren delete mode 100644 addons/easy_charts/RadarChart/RadarChart.gd delete mode 100644 addons/easy_charts/Utilities/Rect/Line-Graph1.png delete mode 100644 addons/easy_charts/Utilities/Rect/Line-Graph1.png.import delete mode 100644 addons/easy_charts/Utilities/Scripts/ChartObject.gd delete mode 100644 addons/easy_charts/Utilities/Scripts/chart_object.gd diff --git a/.gitignore b/.gitignore index 5ea1783..0c60b9e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1,20 @@ .import/ export.cfg export_presets.cfg - # Imported translations (automatically generated from CSV files) *.translation - +# Plugin specific ignores +.github/ +file-editor/ +scn/ +default_env.tres +icon.png +icon.png.import +project.godot # Mono-specific ignores .mono/ data_*/ mono_crash.*.json - # System/tool-specific ignores .directory *~ diff --git a/addons/easy_charts/BarChart2D/LineChart2D.tscn.depren b/addons/easy_charts/BarChart2D/LineChart2D.tscn.depren deleted file mode 100644 index aa35599..0000000 --- a/addons/easy_charts/BarChart2D/LineChart2D.tscn.depren +++ /dev/null @@ -1,108 +0,0 @@ -[gd_scene load_steps=4 format=2] - -[ext_resource path="res://addons/easy_charts/LineChart2D/LineChart2D.gd" type="Script" id=1] -[ext_resource path="res://addons/easy_charts/Utilities/Point/PointData.gd" type="Script" id=3] - - - - -[sub_resource type="StyleBoxFlat" id=1] -content_margin_left = 8.0 -content_margin_right = 8.0 -content_margin_top = 5.0 -content_margin_bottom = 5.0 -bg_color = Color( 1, 1, 1, 0 ) -border_width_left = 2 -border_width_top = 2 -border_width_right = 2 -border_width_bottom = 2 -border_color = Color( 1, 1, 1, 1 ) -corner_radius_top_left = 5 -corner_radius_top_right = 5 -corner_radius_bottom_right = 5 -corner_radius_bottom_left = 5 -corner_detail = 20 - -[node name="LineChart2D" type="Node2D"] -script = ExtResource( 1 ) -drawing_duration = 0.3 -font_color = Color( 0.137255, 0.137255, 0.137255, 1 ) - -[node name="Grid" type="Node2D" parent="."] - -[node name="VLine" type="Line2D" parent="Grid"] -points = PoolVector2Array( 0, 0, 0, 0 ) -width = 1.0 -default_color = Color( 0.792157, 0.792157, 0.792157, 1 ) - -[node name="HLine" type="Line2D" parent="Grid"] -points = PoolVector2Array( 0, 0, 0, 0 ) -width = 1.0 -default_color = Color( 0.792157, 0.792157, 0.792157, 1 ) - -[node name="Outlines" type="Line2D" parent="."] -points = PoolVector2Array( 0, 0, 2, 0, 2, 2, 0, 2, 0, 0 ) -width = 2.0 -default_color = Color( 0.117647, 0.117647, 0.117647, 1 ) - -[node name="Functions" type="Node2D" parent="."] - -[node name="Function" type="Line2D" parent="Functions"] -points = PoolVector2Array( 0, 0, 0, 0 ) -width = 2.0 -default_color = Color( 0.117647, 0.117647, 0.117647, 1 ) - -[node name="FunctionsTween" type="Tween" parent="."] - -[node name="OutlinesTween" type="Tween" parent="."] - -[node name="GridTween" type="Tween" parent="."] - -[node name="UI" type="CanvasLayer" parent="."] - -[node name="PointData" type="PanelContainer" parent="UI"] -margin_right = 67.0 -margin_bottom = 38.0 -mouse_filter = 2 -custom_styles/panel = SubResource( 1 ) -script = ExtResource( 3 ) -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="PointData" type="VBoxContainer" parent="UI/PointData"] -margin_left = 8.0 -margin_top = 5.0 -margin_right = 59.0 -margin_bottom = 36.0 -custom_constants/separation = 3 -alignment = 1 -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="Function" type="Label" parent="UI/PointData/PointData"] -margin_right = 51.0 -margin_bottom = 14.0 -align = 1 -valign = 1 - -[node name="Value" type="HBoxContainer" parent="UI/PointData/PointData"] -margin_top = 17.0 -margin_right = 51.0 -margin_bottom = 31.0 - -[node name="x" type="Label" parent="UI/PointData/PointData/Value"] -margin_right = 39.0 -margin_bottom = 14.0 -custom_colors/font_color = Color( 1, 1, 1, 1 ) -text = "Value:" -valign = 1 - -[node name="y" type="Label" parent="UI/PointData/PointData/Value"] -margin_left = 43.0 -margin_right = 51.0 -margin_bottom = 14.0 -custom_colors/font_color = Color( 1, 1, 1, 1 ) -text = "0" -valign = 1 diff --git a/addons/easy_charts/LineChart2D/LineChart2D.tscn.depren b/addons/easy_charts/LineChart2D/LineChart2D.tscn.depren deleted file mode 100644 index aa35599..0000000 --- a/addons/easy_charts/LineChart2D/LineChart2D.tscn.depren +++ /dev/null @@ -1,108 +0,0 @@ -[gd_scene load_steps=4 format=2] - -[ext_resource path="res://addons/easy_charts/LineChart2D/LineChart2D.gd" type="Script" id=1] -[ext_resource path="res://addons/easy_charts/Utilities/Point/PointData.gd" type="Script" id=3] - - - - -[sub_resource type="StyleBoxFlat" id=1] -content_margin_left = 8.0 -content_margin_right = 8.0 -content_margin_top = 5.0 -content_margin_bottom = 5.0 -bg_color = Color( 1, 1, 1, 0 ) -border_width_left = 2 -border_width_top = 2 -border_width_right = 2 -border_width_bottom = 2 -border_color = Color( 1, 1, 1, 1 ) -corner_radius_top_left = 5 -corner_radius_top_right = 5 -corner_radius_bottom_right = 5 -corner_radius_bottom_left = 5 -corner_detail = 20 - -[node name="LineChart2D" type="Node2D"] -script = ExtResource( 1 ) -drawing_duration = 0.3 -font_color = Color( 0.137255, 0.137255, 0.137255, 1 ) - -[node name="Grid" type="Node2D" parent="."] - -[node name="VLine" type="Line2D" parent="Grid"] -points = PoolVector2Array( 0, 0, 0, 0 ) -width = 1.0 -default_color = Color( 0.792157, 0.792157, 0.792157, 1 ) - -[node name="HLine" type="Line2D" parent="Grid"] -points = PoolVector2Array( 0, 0, 0, 0 ) -width = 1.0 -default_color = Color( 0.792157, 0.792157, 0.792157, 1 ) - -[node name="Outlines" type="Line2D" parent="."] -points = PoolVector2Array( 0, 0, 2, 0, 2, 2, 0, 2, 0, 0 ) -width = 2.0 -default_color = Color( 0.117647, 0.117647, 0.117647, 1 ) - -[node name="Functions" type="Node2D" parent="."] - -[node name="Function" type="Line2D" parent="Functions"] -points = PoolVector2Array( 0, 0, 0, 0 ) -width = 2.0 -default_color = Color( 0.117647, 0.117647, 0.117647, 1 ) - -[node name="FunctionsTween" type="Tween" parent="."] - -[node name="OutlinesTween" type="Tween" parent="."] - -[node name="GridTween" type="Tween" parent="."] - -[node name="UI" type="CanvasLayer" parent="."] - -[node name="PointData" type="PanelContainer" parent="UI"] -margin_right = 67.0 -margin_bottom = 38.0 -mouse_filter = 2 -custom_styles/panel = SubResource( 1 ) -script = ExtResource( 3 ) -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="PointData" type="VBoxContainer" parent="UI/PointData"] -margin_left = 8.0 -margin_top = 5.0 -margin_right = 59.0 -margin_bottom = 36.0 -custom_constants/separation = 3 -alignment = 1 -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="Function" type="Label" parent="UI/PointData/PointData"] -margin_right = 51.0 -margin_bottom = 14.0 -align = 1 -valign = 1 - -[node name="Value" type="HBoxContainer" parent="UI/PointData/PointData"] -margin_top = 17.0 -margin_right = 51.0 -margin_bottom = 31.0 - -[node name="x" type="Label" parent="UI/PointData/PointData/Value"] -margin_right = 39.0 -margin_bottom = 14.0 -custom_colors/font_color = Color( 1, 1, 1, 1 ) -text = "Value:" -valign = 1 - -[node name="y" type="Label" parent="UI/PointData/PointData/Value"] -margin_left = 43.0 -margin_right = 51.0 -margin_bottom = 14.0 -custom_colors/font_color = Color( 1, 1, 1, 1 ) -text = "0" -valign = 1 diff --git a/addons/easy_charts/RadarChart/RadarChart.gd b/addons/easy_charts/RadarChart/RadarChart.gd deleted file mode 100644 index 7bec9cb..0000000 --- a/addons/easy_charts/RadarChart/RadarChart.gd +++ /dev/null @@ -1,280 +0,0 @@ -tool -extends Chart -class_name RadarChart - -""" -[RadarChart] - General purpose node for Radar Charts -A radar chart is a graphical method of displaying multivariate data in the form -of a two-dimensional chart of three or more quantitative variables represented on axes -starting from the same point. The relative position and angle of the axes is typically -uninformative, but various heuristics, such as algorithms that plot data as the maximal -total area, can be applied to sort the variables (axes) into relative positions that reveal -distinct correlations, trade-offs, and a multitude of other comparative measures. - -/ source : Wikipedia / -""" - -func _get_property_list(): - return [ - # Chart Properties - { - "hint": PROPERTY_HINT_NONE, - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Properties/are_values_columns", - "type": TYPE_BOOL - }, - { - "hint": PROPERTY_HINT_RANGE, - "hint_string": "-1,100,1", - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Properties/labels_index", - "type": TYPE_INT - }, - { - "hint": PROPERTY_HINT_RANGE, - "hint_string": "-1,100,1", - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Properties/function_names_index", - "type": TYPE_INT - }, - { - "hint": PROPERTY_HINT_NONE, - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Properties/use_height_as_radius", - "type": TYPE_BOOL - }, - { - "hint": PROPERTY_HINT_RANGE, - "hint_string": "0,2000", - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Properties/radius", - "type": TYPE_REAL - }, - - # Chart Display - { - "hint": PROPERTY_HINT_RANGE, - "hint_string": "0.1,100", - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Display/full_scale", - "type": TYPE_REAL - }, - - # Chart Style - { - "hint": 24, - "hint_string": "%d/%d:%s"%[TYPE_INT, PROPERTY_HINT_ENUM, - PoolStringArray(PointShapes.keys()).join(",")], - "name": "Chart_Style/points_shape", - "type": TYPE_ARRAY, - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE - }, - { - "hint": PROPERTY_HINT_NONE, - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Style/function_colors", - "type": TYPE_COLOR_ARRAY - }, - { - "hint": PROPERTY_HINT_NONE, - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Style/outline_color", - "type": TYPE_COLOR - }, - { - "hint": PROPERTY_HINT_NONE, - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Style/grid_color", - "type": TYPE_COLOR - }, - { - "class_name": "Font", - "hint": PROPERTY_HINT_RESOURCE_TYPE, - "hint_string": "Font", - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Style/font", - "type": TYPE_OBJECT - }, - { - "class_name": "Font", - "hint": PROPERTY_HINT_RESOURCE_TYPE, - "hint_string": "Font", - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Style/bold_font", - "type": TYPE_OBJECT - }, - { - "hint": PROPERTY_HINT_NONE, - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Style/font_color", - "type": TYPE_COLOR - }, - { - "hint": PROPERTY_HINT_ENUM, - "hint_string": PoolStringArray(TemplatesNames.keys()).join(","), - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Style/template", - "type": TYPE_INT - }, - { - "hint": PROPERTY_HINT_RANGE, - "hint_string": "0,360", - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Modifiers/rotation", - "type": TYPE_REAL - }, - ] - -func structure_datas(database : Array, are_values_columns : bool, labels_index : int): - # @x_values_index can be either a column or a row relative to x values - # @y_values can be either a column or a row relative to y values - self.labels_index = labels_index - self.are_values_columns = are_values_columns - match are_values_columns: - true: - for row in database.size(): - if row == labels_index: - x_labels = database[row] as PoolStringArray - else: - if database[row].empty() or database[row].size() < 2: - continue - x_datas.append(PoolRealArray(database[row] as Array)) - - for column in database[row].size(): - if column == function_names_index: - y_labels.append(database[row][column]) - false: - for row in database.size(): - if row == function_names_index: - y_labels = database[row] as PoolStringArray - - var x_temp_datas : PoolRealArray = [] - for column in database[row].size(): - if column == labels_index: - x_labels.append(database[row][column] as String) - else: - x_temp_datas.append(database[row][column] as float) - x_datas.append(x_temp_datas) - - if labels_index == -1 : - for data in x_datas[0].size(): - x_labels.append("Element %s" % data) - - if function_names_index == -1 : - for data in x_datas.size(): - y_labels.append("Function %s" % data) - -func build_chart(): - SIZE = get_size() - origin = OFFSET + SIZE/2 - -var radar_polygon : Array - -func calculate_pass() : - var ordered_max : Array - for data in x_datas : - var ordered_data : Array = (data as Array) - ordered_data.sort() - ordered_max.append(ordered_data.pop_back()) - ordered_max.sort() - var max_value : float = ordered_max.pop_back() - var dist = full_scale * pow(10.0,str(max_value).length()-2) - var multi = 0 - var value = dist * multi - x_chors.append(value as String) - while value < max_value: - multi+=1 - value = dist * multi - x_chors.append(value as String) - -func calculate_coordinates(): - for chor in x_chors.size(): - var inner_polyline : PoolVector2Array - var scalar_factor : float = (x_chors[chor] as float/x_chors.back() as float) - for function in functions: - var angle : float = ((2 * PI * function) / functions) - PI /2 + deg2rad(rotation) - var x_coordinate : float = (radius if (not use_height_as_radius and radiusgkYbzuIb}=s6vmTHYLhK2||J>{5Nb4{V< zF*Oq;T2qO!W2n4S6TWJEWz+O+Zeg~EITwVk&j=X-L_W%Sci$2MHarn{W z?thqjfk3C54}ZYzZuu`kpo1LkhYw8N*evgbQ>^U+<_`|`FaJ8L{vB-FbmsRfS2WeH z7&?eYnqJ{KA=8%BW~&u>e%9#Edq4UMj)_-(i=Z$R#vNBS=ijlbbM%X6`-er5E_njE zC3kO?IK&QnZ}o2DnN#d#`hIahEhE4NM_+$)fA}YUxaze3XkKAs!z z19rd zm?s8$@nNq(pICZuUZB_d@tewEg_8P=&S>Uao1GJF4_Qc_)u~36G}zxlZVfVN(XWIo zPVS^J&|+zB(u5t`_R1%zHl;=*(ftebBPJWi8eGZMP8=`n?!;%$ToZkNHc6U@HoBDa z3~M~;EpNaQRq-jlN};Al?Ve%YQR2l0iS9HVvw(4b@(ad?iWWZUG7clP-!%`=_eh@~ z(JR8>vbvbwb&#ZnQnBZ{>&?cx`R7P!N?y5YjlPVj;r=YGqIKT}s&I**&6~`vBPL-i zdyRlboOwkqbi2X2-sh=?ULwuTyw~_t=W)e=;0wGwkNcTNtm=JS+tD8 z3KX;#k|$jiRUW{jyeMJnI8^v_#Zza0329D)ZBKZe5748F9u0U@e=6DaCj6^1xPq-N z-SThCo)f!rqmUus-kYvgqL(6{gIb_GRT-(7VtsV}(SUK8#8**88_h?ykJkns><>o? zs}!y$!Z9eL`+mE#3<}{7bIr!%c?Q_hHU-4Qv;NJGSG5W=^_x6DnOA4yp6fS#W&dTo zxTW8Kp~Z z&x$S_nP@?kyAxY8Y(=ndcT%v3BiX&S#`!W z4)5a>ZX`aX3%LCUwKev-2yN{ZtwEdgXEYA=b&y^%O-yfNL(*}qR?p-MlM;q=EN5G> zX+cV2@4>V+n|tHOv6OD?1?9zZ_PeC-$45(YcDwwU>?8x;)*$>zb1F#*jKfP}MH@H9 z(D&zh)y1YWH9`v4X{kYh`!DaqIMntHb`s2P?}W%KHX_Yzjwu6g^W#C-V0(MlL_O|B ztQ?vB3wjz!Iz=V+I`(fL&nK6u65<5hyF1Ys1*Xpr1p6Ds=MA+T<7RY8?Oql^Ve)7e zruLY~n~dZ+L=twK5a2I&dGmqFue9Nl!EGCilWVIjB!0~ZNDz?{v5E( zzyEV$vF3v!xZRy+YangLet-3cbI8_P>BNIO!6=?r<>iyB_d~s%2@KnH*bRJ^1yv-0 z^w2}NP(lLd`f+C|K7T~nZ*lj?AoI$*DM&@?FufyOp$f)jsKqBN4w$S zFuco8Z~@KGWt=Xt6}5Xhm#Otg3>y>784nlBip^uO1l`)7jD?6|YLQ#ZWAcHI@aeS; zd1

n#F9oYWEA$LgSFlk~p(9wN^gO9P%PVUwTb&U)HZ z3#Or|_rWQzu8B=&A9$7A`yM*K?l_rzQRULvmKHr4HjPvEDi&*iCu8cOTn>EK38Nl>;w&mp?N9)NP5=n~%0s1xKs0 z=|dl4-H(Vp-@=D#3(q=o+yly6XHn+t+>7QXpQZmzz27&m^_BBdKr9ko={fYd9KUnq61yB|WqVVRqKPP^I`bzrkdmOEeGr7?~7}JOp~C#45leS$duCFOch|WAq!E;rKEOcv{L-U0>s0 z>nWqhFR~wg-?+P^+oRt=Ds8B>PHZD_A1v=~LU`Q-`4rBJJ@=-rHkQD5`uxeF7->=N zw*}5689&~%psY6<;>oT{7V5Gm#ik<;J7VUDRq~E*n5tI?ExhyQBFY-+yKufyhajZM zMDi30KS9_tCXbPoC4Is%%LK@-^T|>pFjY3R#`eo^R3qZnic$n1)Ub>gcjj z1RoPLsC~nej;~~b{jay8J(TCP@Ru)0*P{rzIO0C%tyE+y$>(Er&MM+!Yxd(h?gbB? zC?nP5w+=^NGJ>6VuuBRYr_&qWH_mH!-HMB|DArhC5G_l+ZVueZaCQA zExNEa{gvsPxiIdIM%CpOb^;4$aT=}bNZvDI>R@eBWSA9 z^;YJSNf6*k{~(Em*XgKSXd8-F82+0oOi=%U8^2#s&Z(mY~zdO;H?cCeVYK*P!z|CfUv-wlbFB7KEmzs zB^c|qt6q;Bapk>HL_eNCySVeSmF=N?+U`fga$h&;7op@X0NfzKi(P=qIv z%>ZW5JFb<#MTll z)%O~Hmlxe*{}(b4%V?i;o{rTc3NEYmrW`{bjs6;CooFgH5r|&28!}wnw%OP9`_K3~ zwS9oi3>I)dH8IlxOW%J$7dzlcuPhd$W8Hb89;kw`J8iu_y6v7z%h72&v8IT}+?84v zzgwL9;B5v43m}D!x)_vKf8qI|4xf!G7+sN#PYZ%BSlQU$Z4KWyAG#>;b381L`-!vIV&5k?<@Fm zcqRMp6NDY{h9}Q3Fk|Do?;#KVYWN2O7{kS;!_G@2Otd8URe6eH{O^l03m6yNFT%ca zf8gerkKz8;u+i*$!?Mf_waz1@vN+Lq)tIgJXd3f% z_nZNDWb;T|aYNUD_Hg%i;-85FobJb@O9qO0He%0>PqB`0>5p-adkf`si%gmuIU*q4PxpAl~-C9IbR zR{kuEYS6Cwb3CEhSsr+5SBocY@(#33MSp2ExZi68FH+=TJrIeqv1xvj7(RDvE@A*0 zmOg2k-X1T=2R+TyWp=XD zOPT2}7NzA+h2MJwgO%5Zm%IGWO@u)@eVbpR2$t7L#^FT?x>S487%m4<5y{1Vc(K+| ze8EU8ES|=fqnzGQORy(W3Gd=1-Oi!Q7h2XmWPo5}cM7=-lcdOsl z|7y6`CEe;7h|k=n|LGRcu2CXUP3ar2q%FycHp>Xy&WNC6Ki2GV{Vm9D4O!3;<@Tzn z+@*gP(*brr02Pe(?|pV99As`+rgPWQ!mAIyS}NzsSZTO{wb*ZO&&%IF@c3SYg~1MP z`!-Y{yY+m1;#zmQy3D?8`omsilY;DvZkXBl8lRt!XUf=UsDazp;Zzt`^FAy#?akeu z1D9i&J@6b!>7~4`;)~iRYw79RdCd0f^`S2&ti`Y6VB(~f`WJ(=%-Q|M4HN(dw~s3> z#D7=@@rT>h;Tt@sWYI%QZ!V)YMl27gvlMT&;Zx>&^{(1%717aANmNnuJkX2dvHwjkF8v*%ifB1EKLI*G{IQ+sXV znwPiW2VdwZ=dh9-ouQH3U{3OYS*HG73hQ$w0UEF!I!`}+zI&h}KXd7cLR2teMTf;M zy)b!;ilQCQw>QBCdk{LECZ}%f)s!L!_rRNXhXw9HheJn;qKbS<-449s`X4t}(C&x` zsDM|#n+4`7pB+?w#lDp0Jr=qhN~tR;eB6B3?M%&-@1u6@IghAK+C2R)(oN6fSJFEH=U#17Gayg~7Bq@b{KPsNG?&!CUmeFioPl9|`b zkzfV4sfqD2yY|m)pS9_4!jhAn3ua0q#pVO)QbbPG>G=M~19CP9ElIf9jg5NTQ>mV9 z+&jOEOp?8oCgX?CrOl8F|E)st|BgyPk&k$P$tZIGfn_g1t^~Dx1|R8rCL08ThVz|K zx9bP-I22!32+9j&{y9tu^mwr|_|DCt_h6XnmY-{eQ-xV4uxHgyQl+jE_r8B?kdvSo z(wr~#8Vd)qozrOG=XD7~pw5+%hRiBw|9P~WiBpR_Ab}uhIc^RCti&FLpiWxA~SIongnx65Vs~=dkCoxL{7C zY_}Kh!Yp30$DcK1W+}?)11PV{uC4fdJUKgHcyo2KzPH=4N{Yzca7xFxbr%$CEdr8@ zRlDktQ9?n=@SVAUJvtnhOTT{%G#{A__CB%&03{Phs|LTC39UWuwvchEo6|)T;=KQY z$6l^pH73zRX^5)%gt=7R+pM^n*$#lBLo}u~RgxOwVMWef+2#Vm&Qqg8rrp@#{VFRQ z74%#e^1Q3JMfK6k^sO%lEA4~(?WjH3AdzT$Dw?T!5H2#0nxIS9T#fR->6F@u5;1GZ zb=_(>lxwQ&skI>t*zEGO6MeqrQnDFZ=1YY8&*nzz8rqM zl)@>YIJp9%jT84F3m8`4*QD?KUq>6H3apCWahIE@bToEm&UJ@xy!ALlY&OmStotMJ z)*?@;RJNm)ui%^g#_cJxvj3I~(#SxYrk|>4L<4RzG}kHS=$ZL>k?<{c3#NQC|A?kbjE^8L06UdCFxTbN| z51e}aXG!yJCBHNKK(>flBZ*r9tX_;{kY*R1^o^qnZ?pZ$LMTISJBt>_R@!y_uQ)6 zsFh2%N6sb=H9XU2su@u2Q8-JD#F1*J6nNkstI*A4z_0HCxp=WwO+*~Nm)+?*8GCn- z&rqepwl!>cZ<|6HuW~linvzurqM$r~B6klqjd5ss>33s>^6rzWo3kkc%6%|7^cdH? zfAkka$W-2xH8!!keYuwTQ({2VU>ITbN$TC4qZMy{+e#s6qrrjc2mO}IBqD+0TS4Id zD^**S8?u{3FM{9YgGXDnOcVM0#?t%S#wK})uO3RhW*e2&kJ(Y*8q%QK@e6;D>E$02 z%ihq&Z7)skXE~10YRa6`N%2TO;+|XOwxx#fQ!2SQA6)^HNv+E>ej;f(%Yc$h_J zAsNszKxIPhWt=@SH()5FZVmdlto=b-E~we5!QseT_Pvp_(_e&cLmxaRbh+{>7Pf_H zo=Fn*8Usa@f5g-ACoK-vgisQ_tr8j&IR3!imVgN!9H8C4PC)VvyjXzzqk#RyBkAMJ!JUqT+m@O-k#PCIb8U5{ zH`eOPt<2wCE&CmV!ny0&E1tHVdgnom5T|6tKF6XHO+l@CPGex** z@3Go@tVA2e&6J_{uacJd*0d6>M`EE@AVdJ+(#yi7izezke|n@KOf>Beyql|sqZJBD zg@svDp8d(*bul*S?!A$ysfO@&36f~dXnq~I>xm19vdK8`w@7TM`MTh7drzfkwx&uE z9;a%;-iaC9VR)b?^rUqo?c?$=k8mUuv`wMqW}SYonK-GHt>d9m{-XFXRUGFC*{e;7 z2_E<99-%rf@$?yz7T{=6LABF@^oU6?G@5-lEcdw1cD<#4zKHgoTKD>&M z$R0S{Pp{r6sYbJJ7rB-f8JGZDDR1kU+7#Vaw&v@;7aI^XFh1%~Z(F%D8A(>z7y-0E zRy`fJoTLC5giO2kY}D87SJzugn88!$x|JU(a1^>V#2(${C=7MD-Crk0;Qfkci&|#Z zA58IOz0f6ama|tjh?&WZmBCt^yab!Fj<5N;Q!SFnBo-P?h!_+}8L0HqY*9n>y)9Kn zO)!o8e1_jJa=c3}XyCG=n*Z|$31WzspO@iFFTO7Yj1)c|r>h|yU;jlo=R4v1V9 z?qrv2&@Lk>l2HA^m6|3oVjB6(MKbZXBL9M^EheJ?S}%dCFHmDRM@u%S$RzN-YCW6< zJEIk^4V(}5_rf<4#z9N`x{;l)r&8t(vdkUwcYpFRl2cY))G)o@Fhd;)z$*^F!d-K} zh-*%!!MU#Rtu9jMTu%^MaN8+zAFYj<7@M;ggm+)>ZVW;g`D-gI``Uwd}h) zTOpRFJ%fD(U;RnK3|;11lmZKqv9Ins$=sK9yAXjtyPTI<5kBIY#HJoUHDIC;#IdV- zEG^nS^uyz#&(+bQe)3w|?ID?Z+g->CH8DQrHC>)i*2u^yMr+m}S}cv@R72>PBbyhWH7Mv2hjTOMZ&9KH3%-U&zpDe_scg`I{k5|hHlef;+p#aZ zAS4gGa)VpLlyv94i8uTh|8X>NgmL?ZvWN-;FV^iGHd-6{gMWXUVn&6tdnpdlT4qFA zb)}SSv&q592D_1*714^GjYneZC5$&WyHkAkul8&te{XN@oJHB!0sBBR&<6DsVii?z z54Q836KIa84KTQ>{|f&7S=r2ISz?E6){TP=X;=YNRjiwNr*kQ0r#kU_0H#N@jaXG$ zp^xLf_?v#QowHdxp*TFy4Ll9Gir(oge4x59WI2FN8?4x3cu0@ukug6597TGfMeqYe6 zru-%QqUa+z{O6*~&*vZA!6z4GmU8{Z-E2HS(+b3v?;T9r^rgSuB8;xh{-KRrf;%S; zke_T0pNnT!x_$L%Speo<$jl1U$DeQ$rJBH0^$O@2s2ny+D6u4NGdXk|F8IDYv~91i zZ&&H`J+|~(hP;c-1sK|8*X{kwUz{Sw)P_|ibb3Awz9U~n; z_iA9ZIg@{&*O>m}RI-oF#{^M6IZw~s#$)4D^E%~3Ym^6FfaqoEZ|Qm{Be@bd++I)v z{pyGbQ`B#xTbMLb=QPiGB$#@4$;r@MI#24@d@Cmi*jM?!%)(|#ID9GVP_gwNAN<|| z+qW@=qqXZl3aDY`@PmKXr9!2Kgh#G(wyi%p7kiCcRXRQL_pd-dACH{M^RFxq0~J5dmOO54Dt$QqsV{RZVpqT68_H_2--?O~~e&rk4dS*anLW3Qu^dR(4v?pBSK8 zDLPKM8#Y)OVuOimy8z~4{vNnl_UqT_wxK50-59>L{auDzG~H}QcA~}&E;s1)Z5&0s z_XYiGv>{a((9!KIq^(z~xyf?Qg)i#Ti$x$L)~C6m(~l|(2X@c03Nr6b}I zr0^{bjd;?Mc@4r^ZjkAj- zKgtEIM^|-iU z`p|F;v}u4irnI*Wx4LLkRp7Pn2HObU+Id&wJr_zJt6cqam|UGEEfkOBY5F2r2abr$ zfU4`&mvLs*0O~pkDtv3qn~yS3dqc?dYQ9DBL%+C0!`YxP>2wp`OC!fYq^jay`XY_T zKu%T@pu%+lIfvwGQHqupzVwaPS+d4h2xRiGLgptb*Q{ z!Sgsi1gOT9Jp3e#Ikg_;TM0PZ!!mAXWC~=uJ2xNSN|Cnj?Cc5?grjBB_%i}E96^cH zAv3sZz^WJ4*+9Bx6+x14+@*SVVJmIO0}`T5&x3z}Fw?xE4via~5<_ov?VEEe0gHc2 zwKsZTi_7daV=2RKB!=GVQSqA7l|wI=wU0B?%tk=UsNJ|qh)$sav(uF`wM{UKl!#~D zjAvqYnng_q!lo)k@?7cwE;~0d_-zx&3-s!xN#imXMpaNYy6avb_o+Xi>b={wA+$I0 zu1+o!Yk!k&*+xXh1v?KHJ4X9Ii}D`_Fh7ogo>vL)L`^g++SKSdJs*7TI{GD3tycpH zK?6*ik(9^RckoRdl@cdgEVTUv>%mX*0GM|PIHH4HrYo7}AtyhhYIH99rjACUZ+g3$ zWV_m4p9cR^!837$y%o2b`F3Cx%hr@f#4g>6CE>p^y>*o_PTU55WrQ+DicWiYwz$)e z05Ea;22l?}2u(4F*(L%`=u|aG1bVW)t(iFk)Sxy`*tHeSpQUJ^-ETc{<5A{*Vs+f3 zKaF6J=klvBE{~Lly&|zi5~!daAxqwT%?DJG}9Ay_ql<1N<<$v6m5MJ}1hZK_%? z_dPll@4s3%Z-M2`idi0N1xu)zP_aUfA>Wyn&?uTJu+}E9rqImpq1s>_@xa;dADt}Y zJC(ue3BVKstDrVsYEftS44HB!Pn4pI-VRhk9{@r`5grNynEq324Be3WhS9ifiYhBr z6cJK~Tvhm?Qz`&@c@vTmGZ%k0J*!`e*f$tK@TU4Le`b9Kvr#72%2X znQu0$-U{FZJ0;P4v;2({nO-=|Lz6gLk=*7!sQTU=Jd^;m(bEAAB(nU}ozp_{I7Gn4 zwi_<~cB~O^e!GHNY4&^{t0fle_rosPZ*{`pA-f=F=dW|QBYg))bHOdw0@hBt=?OK) zKs#L6-y_r8CeMUjRgv@Y)0||7U6=$pDQgBL>2HE<5mr95R!2KCv7)OR)K5_H4QAzM z6$p!47dWoY4&$2yiK8wL079NumdkY9{p}eI;Q<+VxgS0hXWP8|ulA7=n+IctY-3Xf z#(sIl%209u$p}udzi&kauhi_%HV>_J8DOj+@v_pIf5i;FpUVKZSnYf_Lo7OsE{9Xl zfkQj?sQ>fteE}GA-n>mXaXSZSnb*iqu~LW@Hb>22J`q(8sHMX|dWPVDxwZcsV;Wr` zh4YG=QT8h_AWylbRMb>xb&8H)kQ)EM4TO%!!B_f0)*iz>Zpe5;6d#PQY8}vlE;->S zo~7Z}IL#NVZ(ks?CRm}RHvxU&Wn}1*yT(uSq*p%8Brj&9D6_iTEawr6Bhp$cJjLJZ zrPRHx8X3NQwUkUQMK_jNqY!JGZEq&87C5>^M}8U7`;wIp(gVHp4n9)DTlQE z?voaP2bLZUC@F{>qB3hGYSDn()WhIFqq8fWrMXksXR&8KDW?`BU`OWz5f?r8EdtN!hPp*_p^VlpF>Y2BWE zstPTbbVv~{1X`LYkh?r4Qc6;Mer7?p&NM#}-f91(o zZ7OHT`>e(QemcX?1`vb*ykylu8>H-P4j9zTFV2MQuGL!bqI%YpTopgARj z$WWC7wYbp_O44&hZ^MxIi1sQV>yqL$amJ|;x%a0<8%=f-Kj~fv9yA#HJa;PA^(CuMT4k)sK(;sUXnD{(49)1_9dKn61X1X_`ZK|V-0_Iyk3 zGkEZQBn%_jOMid!OcESm2Z;lJFU~p0WtamR62OSuq1YprG>>SV`=u){Q!YrwXE`77 z*VJKztI=^F_mO>@UO)K`6xZ93NKk(+0+Hh};Z5qs#=X!)Jbc^4{Y1X6uJ$AM`)!it z2^Q~0`H_D`tFXT#fwxHmqE%t$ILbb~=CW*16Z#XG?@kKrl~-QJz5%;ZsJa}6`sUq9qv=iZ z#(SckD~8)ziPnT zTwjRrq}oM05(_^`%Qll^Ph$M8CoqT3hyaL$gQN^+vFuS7tyJ9-?7Dl@{#}xu@Gkh$l^iD`)Aw_pW(hJULCTJ*DQ(^t zop={szZF~8U8~Ky`?+Z`R?c(MYMrq!S&}ED;GV?qwJ!Yyn&m-kG&cyI@Ulw}S-14v zzA7APS#yqLkIYn>%LRAMMMHrK{~ej!xCS%8I&WO~!PpyGd}Lpa@4I}D0xCQUr3C~| z_JQ=H%U^i?*a2K%&Sz8tuz>xSre>yuT#S;17xJxr3%BqswIcg1&yXzIhg}^5?DmGY z$}iE1mM3A>n#h)h@_oepH)a*^Seo7N3iDFpXukG;739@taB)@}L(6S!m@^#gx}vEE z`FUFqd0^xduT<-&LI3nv%77lQAES^%vQLH{U3x4gQc>KcxVTRk{epa4W@T!o4V%u& zCR=RM?R8`*u!99`jL8paEc?&UKq+G?0oMl;SiEZ!(xx`p7_3wHSewbMh$%GI08GyW zfe~v&$hHQPMR)Eo+fcpk8oPx4CnSg?lSOLANd61(VB;V0-X$st;p}wS@q7KCQlk5# zWLR-qJLD&$lvtj;KWZTK1ZP=lQRdlyces*otzM|c_p(sIF1qcuyvtta(ydnb9i}KD zj}@|v4Kzs)2A18)llhrK|B=4B|LOZPO{U%C=W|SIua48j5r(5Q9h=ZqtwAnim!)n1 zO12a(a}G5*<8YgL_iz*?>7P=)8Uy$%@385A0F;Xvrp70zw+SDIh`(&ip*I()QEsVC7NCp6@K})yL1O z3`;;!^nT|xykK5^D%r7c7ev>!Y{?;LxW+;wgki21EACcIGo%`;$B(g?_UwvREEbH6 z=PqDPBByfU`xX|tqRsn>KLXxghhH&2y}iyNMQ@CK*?-zyorNHZInDtSQBdN>CfL2Z+^EHrAJVI|u6=XobU&8iqD=?yb;$$PfxQtB*ElZ|fI? zq@q0@pnv_`5OhYGrxxs;rqgM*aUS05J679}U_41aW{AYRJe!A^N=I zRaPr<)k@EiRBrX|maO!(o^!1kURl|Ge~y8pU#|M(0;*ug=AOLE`88Lu_iX&$&cvOB zPjR)b<`~#&E#oHvguY#hcILYpVNSN0 zG!YR}R6wVjrbEAa*44WuH3l603~Vw{`@!sMe;%}g+ ztoiM|It~#{j^cCwYw#C)X2{A=uXOR_gPB+C>(KkpzXFP54S|~ZX5*mtg-c?_8b?O2Zk%e_8QEkLZoG+6sY4~FG*TqpvloiYg9CB|Bc*gzM=LX)C}UtTvLO5Ue`;}WKxxsJt%-uug%Q;B-Dfvy35{C5$vQ>rX-2kb^|Se zzPIHOGbTL>)-Op*i`NMY=PF!te^X~3Po?Pe>=wB#j9?10ZSS3&#&a`D_gvcN$WmD*An^$(*^VV6t7IP?N#_ z{z^PqbRuRBN7#vJOK-u*EVk15c7>x+If##=U$j4UqC1v;_v(7cd()h-_|Wv-2U>Sl z&BVI?{+pAuA-7w`pQO#L9il!tf_5E`NE4mp1KWMhaDUEmDe5MUXCp?Iifo3km zU%Q*x(jg1vz%9Y~WMYm&(Ein@VFRDZ&r>IE=7Y~2oXhP@jQkvU<8DGf_gRh5NRnuv z=@aJA2SwC4Y1au^C#;jqnDy-R{ZUJD&PM}#*(#f0_M~P%KccNlcq)CzD`kEDp&j6X z#tJ@|I8$m!|A18}IxK|c4$K ztJ~YegFPo@fH|ysc6t#)5Yw^|up09@33ih1H(mK4Q^B(~UIS@E$3^D5#Sh9}p`LZM z7bPA__Wx`{azS`7XZmXgH}e%F6BpC7(R$YPpr?LSn`tO5?|sSoVP}E{^OPJ<1Y~+^ zg-oVO6SZ;_ctQz^;EwRV%$1lg*D{!8pZrR-|^*EXE=nys+mkn zpg%e&wPz+4uJU}lvwwb&7(d3ATALU88gwl*U{-pvqPaO<9T&w#2NNJP0;@Jh?A;YCkE-}Ce+dY|V~ z4RgaHnnRJ5b{_S9@OwalR9@!XtzJk{uze%V(EG);gaOY|Gxn5Qq(pCfCmDXa-9C30 z3Vg2@(Qi ztaQfEn{HMB*#ZcF>-KI}j&3qoM5dqbk@nZ1-T{CqRRe6#(wecVv^eq&VRRQo=|dIfK*)VO$~w4KkI za_x;}g_viMxjOG?Ywm5c8lf)%22<>=>eyCUKxG5F<>l? z*epkbs0xF>eBe9NBzeh?H~(ig_6Rr#(|7EDgoEz<<|i+o6215U;OH#iREFj3N4a^? z1_r=Mp7nSU%fN#q-p&66B}I1}N(&uOfKyoAlE?U^#X@BpS8d!Bjo#(fd|s&1_xl28 zW8Q^zI@`E__JS`O*eL@Ui_({o)@d&6o0>uEE7#NKv)63^*CJ0w7!dY7g-+f0nGiml z4Y5`*S8I%u7HeJp47}nyRMB~l3B!hewGd`j`1M`Bx-0l*AXje@{oKU1`GS%ocUfUS z<0`{W5S}pn{#6G58LzAIE-^XfUA_wG2kTO@{D(mfmr;GXjZsQ=Y9F zu_+3+ke5G~Y?_Ae)`WxtU^+1z*|xH#3@kO2f)}Y`Hi`7j4UZJLO+V@#1U3*cK>SZ0 z8&E8GfC}i0HSIB}SR2`1eQW&HlB`+Slf9qqd=Hob4D9|FwNk!^H1545@w~-nc|twU zZp?*mOd7Tzmx7%}3edoMXETLtN9IRtXK~s3CCyCP(l!b`jBaYK5LEaWg&5^BQ>G9Q zq-qNrny@Q(3uqV#eEvr{pHD7q)Eb~3=P|`vKYAu8I0AQ6u*BJo%CF(&v_4hf(y|x6 zksGCnqXeR~Zf{{90ZtQ!P3e@B&N4xS9K>6frm6T!CFku)&D*!XNDZC>E!8}boz-;c zQr^yUTp&nTyL=c;q-`w0yoi7VhRX#M9L~@ZRGsE{k?t)@Zi_tWR^}0qd|^I3Vc`8S z)^Q;Fyti9`T21jXHwvTK!mXD{-i=eGo;StmNXLAd6>g)EGLC@+qPpu}=DkDNl6{M&h#_w?UcBm1FDK|NvWZYigQ-oWfQa1QV?uY&F zq(4r)lMvd{&OA)mX}E#5SIB0N8aK++x@pobgGf}9+S^CxE}56vKC&MvwJuTf-!fs= z-)3O`2&I+#oxZHtCncmrnIYW7VU=Clqu%%UmXMj1aE{pn)~3Z2v3lMU$#R&Whcz^& zO9c`tvC0qaZPRN=mFa+s)bq|$&0K8^{d~0TKj5Y8?NCwan3iC1YFjrXao^#zu<~FJ zqpG3K%35uVUT;fiK{~=Y1srbD020CSEDqMV<1qHmK{HcgsOovgPr#M z5!RPM%8I){JQF;&4Xe4+=s5X1;m@k=Nx~PdS)k(!n^Pb&oYhT+-&NY)+T;1W##G*> zXL&E_Q&j<(r@IB+^78D0PJ%#pZr^}{+H8o$#|)VS*3Qh8PIlR`K5%M?#qistIMz*2 z7fMJBK2&DTilrKm^%IcQ{+0v%lCqZfkYVIf6X6~N& zcKMBCI#10g;J~fECrQ!$E=3pZOM;3meNTa?K2E6Z`%WStP?PM>UxB6#3jP&z1O#%p z$fJIE@u?8B#w)s`{&+Fo%to8pP7`Ll(Q@8iophY4Jq(Ku$td}4IwVJrQR8S}(4EtE z#}C~!pH&GrKjLPpZ3c^l3*4KS=y2WkNaz;O?4Az*jGx67g0?TBzVj8FGo7pe}K z&1u(AM;O)HUa8sGUL7`)q$M?v)Z9ydEY3W1=-TI1C~W=qyC3gc-p1`kFp-j#5wUHp zY?}9?hA7AIeC2D@<2`>6=t^=S=uZ8SWzZks@C`eq>xlw^j{c|sZRJ+a>v@vWRF*@^ zp)2N#{#E6R-%>$dq2sX+I_rjzqwDZl{-&+a{v9iheapeHp9Zx^TV^AZExay%;3== zWU5~`X|Cv4|E&Z8F$4a8UpWTqYm_pV{=+(qq@{0eQ@t|D(=@8_G`E&t-(yUXqGrCg z8rar>v`3vT=2ODxr%TQI2O!%W`+9ZWrujDm?;cpO=5rZ^;|mxDc8H6XTzjN#VW=#uTnHd zi(dDBeSwm;ar|Ica979TMgnppXW0{jLyd9qB+AU}A}tkYsL@eg^?p3Ca)K;epfu-Eg$w52zmV3 z;(I|GhhF;jPV1m6xn4N$!AOq3`1zYdYM)lJsh{yrr#kHUloDia4gltNPSv<+7InJ16VyjG z6Id5Kzx#&J*~^u=`r>L)L-(~tiT-Y6@PC0)$7ON zIOe^F$E_Pht{=3#z5v4O%8NFkj48ZtfUKSPmBWy=oNzlhOATo=9QBZIxx=sNa6GO!?SlK zucJhlzWXl*?jh)*Zd{l4hK1qX(mzI2PE7jILArcEC+L2Xd|u&}#&tTsd24T)bx?Nm zz*w4#j^7RmWYL^B1qa*dPIyT%vlT0Bsb~`ys6f!Ijk$XHoUh}bu-w`cv$EV07~5U0 zRHUgq)&HoSxc{*%7a>p=3@A?0Z^!P}--8U^U0pHeFmRZPr4{3#r1+9pSKc7D7U&m{ z@oc6TC6&(!Iat3f3g?X0-T5-a=@Om7CKUaT&%A7&8*s$b!k>{OgAiJ0SHJQ5(56n5SR(?!R*x0`M$p>I}Cak6D>3HM@HV%joA2?RErQhZ;++K zapfO9RdnfwKb+mmS2i1xDi7=lbb+Bu*uvHMH#gbLzZo7T;&kSQy;83<9Y@x@zT%6T zu%064!rl6dFy*c#WcfsvH@C}{jSt8tuip4H4sr)JoDvmALq!#kW(t42y*yTtuNw?< zca+BwGmsnwK{CqTMx@E%A2W|x&OT|{fs&yx1U7NOI{|OgeXe8# zJ*L`(R6o$Bu7|54-YvQB&H4VP&6huc6-SRkTAXsX1^!irzP5ekl#2=pnZQyas?K@T zlNMdn^wSv|w8}Q?`x1HCnQ+eD9wU%w7mz!y&gQB~p z&a^dj@9qw22#T75?#@-TRWzn(HC0uVnqmlQcW&u`m}zUCqGmBfx1trKZ45OeltvH{ z5)mQctaSh1bKY~lp5OU$KJ^!AR#u*8t!Lf$bzj$YS4c+It$P}3$**KG(=oyn88Rvc zZC9gCwuA>F=4+HsH-~xSwV`gkK5VoHRImUD2KgV5CluU66sX^Jjk$)j@f$rA0wcm=nAGXr29%6Ekv2n}J~!a)_RMsQol+(`8$? zol}Dn@yOZ|Vk2dljYx2nN57IDFCt1>rMhPLpGJow&Co1#c3=A1)eE>mNVdI?qI zkC|6qRGvUsLPm6R?i?p(c;zWaFZPaW{OD`CL~_*1KbKqRqf^`}@fIbWsb-u}DU7CP z(eHtx4C*1u2Yp;qmjdMFuVH?!E~H;R;QLzb<{<{BRW|I)hoG}5xK+A7lSYL8R3%lE z5PSO&bCpD_;LhJHf(C;VD7$n2u!CGB^Et7N$y-BpG@I1~brg-bJ;$zMgxmqgiSZzO zNq$3VE15<&hHZrC3EBfDR~i`j#;!~JOAA0Tn5p32+y=9?U444x!|SY1z1HigII6xJ zH`isdBdnyP!Q~WrlZ)A604<`Fe7tm+JM#ugQiCMLrsuGexR_tNNkY~im!xpyBNfP2M#*1`Y?F~19OoOEM zV7d2Bk^(ddPSOcIKe3U3El|%cX_TXD5?vu}phMyh2{OukftK6U#`-Y1t~~3sorzCVm7tkRIB}7214&K#o+Sj`=;)P-4%-FRWs_hqCQJN- zfp@>=VLxjbNF|g>*hf-3`0IbQf+ajN{d61`HFa=&Bcy0F_Dy0JV(rh+vuVLXw=*dB7)FE>r+_ub~$U2 zgG}00tqDEjY}_0(m!}6UHmogv^CUn{um{>i)Z~6BJ%7wr0Lduahhm*8@7nJEDR(Vd z>oLx=NMSqw(pfl)!TlA#B^LA#e#of)x zG%=;fc~8YMmyCapxAd);p%7}U#UPsYPji69-tsFu3r-Ta8EW=e<5la^1b%CkDzQdP zbP}eEbLNY%$EImthCE%_J?xmg&U^Z+-_-=icl5%SP4kC1&8xiiOWk)@UK3X7!I_=v z!WAoAEj_Wq2hv&J2w{KVU`=@TA9&g+534{mfPWaJXbjZwPOBo04DBhb9(_A1 z-^11&eXzhg5?`^mTN!F!T(Ma9_FnZ9xF!-v>i3N+ z-|nN=wtv9})~vUV@6{o;3OaiXK6e^g@618$gqB`EP=RJXkKD#&r1>@1PfWYRPtG0; zO#~S;$i?6pgv*{i=?Hz?NY62?5Wd&NTD8{igxR`yyOD=zeq+)T=+5H`80>TBjk$V% z{JpFU#Zns{oqIhq63_y^;idd3J7hYdSHQm6s5#gZl6yR8+~Lidv6W?Bs3XqO3DdUHDu zB>_O=rfHK_4-ZIhnwFe8s`{Ph=-{X)92ysm7&m(jV)FpDlR1~aD37R-ZMgkX& zFLTjE&S4W)T|?4|kzF+gWg0dEDuiXy{a$A5zz&A*Zt<4il558N)D4lL7a4pjY69kQ z6reR6ws6*N6-IODJ>$XRe6rlI6Hv9jOgeqAsmfU9`vb35;c0RZ@7=7}Gh|U}91@>O z&u*s)$334nXv=Ze%(aRejgZG9(cT3si+Wy?ajNaG0Kem>hTpJvA9gP&&UXoM$d`z# zW6>~S$jhf#CS5g0%8txMrLatLN9ORt3y3~DjOt;!BFej=lh&jLB87nqF$6YI_$q*4 zPvs_wW!Gq^(r-IgMry>zlN98d8WUDGV|iw< z89&e2SYk#^Zlg{6TzW+Uj>q(V?-4@QRXedhlHnStZ@2XvVcL^Fu zd3Hv|kph34dvCU>zhbYix`VGO6W#=#uc9X>jZ7kVjliP-onc0)Kcycke~b&8K7HEn zM~(N;vFv-#O3d0}3q|0KxocGI+lAZYhUU;WfK4KhCIT^aobfG?&6UYlX? z8~m5rd)j+Rdc^mDF&F2hG8$T_H!1hj4j1_pdp-Xcx@2Z~Yvj>)=I!LTaHu1C3>USg z-WVmA8V~;@)l|DVmZ;jB9ip&b`cQN2VJXLEM=MSuYqc$6-7y76>m&xFC^VoLVf|nW zlGO&7KANlr4KMV1|NBscUo7h=M?$*nG#MXH61$)Dbt#dL=UE?zA*J@3HxD^rwQ3ED z?Z`CUN5tfNCb>qq65L#-3HB~##L>?$y}xd+iK~U$%$ryn#LRaN#4n5pZW( z-%cz7RaeK>>&P&Ef4>S(Wwx9)RGWv=jqHTY8!@FJ7sI8!)5O}YcrGx)3b7!b(WD2p z1%(^KWyjdMb?^0{ggP7Pe77uyhB2hhVTeCVMouQKx;T2GWv}ge%Fu{^j2>hwp@xgD zQkoy#!`BH>yQCv8sSe{;Dm_$d`?3hJ#}ge@_Saqx zu*2XN;SyzUunrg}!57`m!U+A_h#@v# z?MeoN+asFXXaoJeHpI^bQGK~;ilr`nc%~v1D}@PYWy;tJB%o6uC*Po4H|0sKh*+eT zF?PmH)Uh%y$G;7eseGum@`iRIXeyve{4BBm;LPKvw5pOYgTA}GiM!lp%=CEB$xTlA z)9`Q&2bW$vS4AOc16pLr;ItFcaoZ4md~(c|aOWk>L#O#6n-K9QDeF|NOI4oA$3n** zu{XV3%c23@Bs)Las)OH6WJOPs`hcmNNYFODak&gb^zo`+KKJJ;sFY(Rpl4EIV=ty8 z+t>aevfl?brhF2OB@?Qh_9;-Ej zlQcA3k{{sF5<(_XvXUgAuA}0XU7FIz5mZg|kBN*A6rHSKY9n_jF4`7$t$&qYQ2|9y z?uA_5vL$OPMCKVNT@(4#{*o?EzWprCP66p~Lw)|rWiTsNl)=d)b7Fy;{q%m?AmMsi5TXmU+joIwqMTvltl{xh}ZiED?N*U z0Q7VSKoKh-Y`Y|RM?Up zTeeW{ghvsc#SZo{wz2|Hj9JIV4VvdN9quURqX6ox0uKoLObF+8;u{-dYn7lUukM}d zCW%{0J~&6YcO0^#N=z**Gk_iBY;p?pMcb+V)=fEB*wYVJ5)xgX05(xcNHR7RQRI1wli1=9}OJ#2VB)cX?&hMw^=hPCxLtVQ(Kf#ZM zIDudDBE3&Au(rUB5g#$XUCFf_i^P<*I7zh+;i_;T(tj#!qlBW%paWD($&e^=cd}<=p?&h@>1`y@` zgM0gUWiZ|Z6dEl&?I{?|koCY3M%-PCEM|cDz6CaC39Th5Hm2ti$$1bw#+z)|nR$uI zo8Dt0{!e@aEvX-%ZkkRtdzx^>=SNuE(_I~#_&!Y1%h8dxy%E&FcMmORUJ*YobY1d; zUx1YIg}Frs?@Uy+8{h=r`t3fP%iK9on17F3q-e|deXE@Ce(8@Q`{E*LE3?Z&Op%ey z?0H=20>c|q5+zIX#grx8wsckSF(7+w44z?>yC*zTD6w{DA7ZywtA$7y}WGpRYw(v zkNjkg%ljQtes8MQT(NJjNWxkiE?rr%Ut58sN|ktg^$_WuTd+lF9T&FFeto#$0$YF- zd?YXqDQeMoZiu?-EV%TXa}xQ9rs$(dH)|Sd<8~qFbP=9y%tPpesksnw3D$bVj zUYCaDbgl&Yz57PCwV?RhW)S(``FdrwM*#;}*Sv8H_Ax&-7;dATrcxXbu7#ljHGtD8 zBBCZb*^rjz|~b)_y)Dm@=|CdxR8jCs7VxisnyZ2{n~nFbP@P zjtIK(&85}@+IGLdtq?lMg%gJKA`5GzuDnx+46t zlb#M|b3^f9b|x8gk{wwT-?g_neoYROOH_o}2arRe@(iXlEa=Xx3D?Fk{yOt>sK81x7DVd zxS{%Ht>SIam)jenFfqykn4V(g>xBRpk?xx2jV%-C>+q79FCn2mfa`3oEmz;lsaR6O!kw^149^!=8M!a)zC${Q;1q)nX7G0b8d3pUPUPSYU&VF zF4+I#-@?LX$R-I>c!IaRJ>TnyJaumMs6x#W9PKx}kAM!L&HIh?nJn{NOC_?J>QyxR zA0NA33+W8IK~U<=U)yhnMgnGSPWqX@>d8~S0Al#M8m^zt%en`uT1p0UV*jno{J(0> z$rrt~8_T)h+pyJ{^*`DDNBjLsZ?5ztdXw3$Z`CMKK=D@OPsw7cji}|5^KA##bk-kl zJ9wbNx?a3mmV0+{JeG4KD9NMXe=)LQC@!Y=CaX=9FD_8-< zn5~dFWeX(Ul3*edM1b750qdV)271r)%Nr4e8HabzCTLh%o+oRVi|6*t}-^-0*?qK-mC2RtXkAgw@l4 z`-BSi*Y`or-vGumS|`03$~zALEMZQRkj%;<-Vi}gqrYk&*N>%g&v~xT6LN`tUrjeu z{?9ke&3ldD8j#Q$>%PXxh=4Vym9u!+4*;c=ak?d7z=n>8|$}4 z@;{kz0JC}B0SrsS64lhSFRs0dH+QmLeSCFb%c$LbR0~$d|!>zg_alBl-f3qH|S-!3(PI8J64E3j}}*U!}7Ds z$}*RoQg0Kwjy)8A8ep2v8u;4l<5_Q`!STqfF_khFswzaOE*PCbWMN@MGzJk>|D1-w zc2HSO2*1s_z( z99F~-0zh4;apP|2z@dh~qfDM%B5!-Jow?cD)vwpq$`d<|*u;Wmd)BevqKY&F9HhEDy1M}O7m{EQHQ^NAoKywg1NYQWRS6Cl|M_z4@hS@Ok zR+BrWp-zG39ZiNdwovBWxDe^4T{g^3Qb32IiJ7X}L=!K!^G-I$5-%Yf&hq7rT=Mg^ zRBe4~C;7*I19ItpgsKD<_ioIA=5Z-9NWs1rp8lHo^i@_)Tn7mS3Lx7V8jSeQS@wK@ zmrcCMd3b5u@FoH7syY#nA*x^8nA}<(wkEG;J|e$?(FnA=(*pmrBxcLv59PKJvf_1$ zBK`q}c}h^x?bh^Ga3vlUgbfNCB3?1J5X}*X{;H=9YZAAV-k@M1xU)m2&fJ2(J67Hr z%SS|Sk$4jtVNiBi>ReFJIWwuj=0~r=;k{i}9WGoXtB7S2+gfa4zhl@ay>1 z8X}`ea)f%k59R&g+JVly-6-XGuc(A;88lsOUZKE@$U!-DQ{d4M1Wozg5O4WKS6eF! zX)1;{i(t7r6e?)iXld_E?R|sNGoz^0H(W2{g~mGgO6fQ-O20gT4`li$S_DpAXc)QF zkHa_mt{YYiTy#Vdh9|<{6k?2ov`J?N-ar*b#xizK20y|+CWfgXNd6S#-`~e!>f||1 zMQ*1NqkD&sOc{^ko8-2Vx|eHT^}$P+<-UNb9*)o0X5|`%i2rxYcU}wnP}f@kAM|Zz z$b3BJdOMj0>}BA@u;$5WqL$RbO)v29%a`!1=2eqsv+3zsQ9QA@* ze49_(M!4YG&ecMNcTPX+G}^q>Sf@hM-6<)tCi|dTCAaQ8+C?BqB^m!7TV|ok3$?i- znjM*CJZ>VaPZ)m5e9ce=za!~6_s}6`4P2PMEw+Rp`(0m?^tht=SnWa6Oh`u~&jboo zxHoN;bNeG@V~9pEOqKVfAboeFoHX;|Bf-h(ppQXTf(kpo6ztRChIB6Dse0MF62C9Z zi@QjS{g{Qs;olLK`<+-iwfl+~O^-QY8QXRvJ6Ed9LT4w8og9%C!$&?DUy^cJ7^zyW zPYhDH_WzJC_y5h%H*SlK zt(j84m0Cw68R=_Mdpj}P%C;x$__>CUdJBNWZ!}zAn(bLi^vmT zb8>E#h>aa&P0OzQAstNvc8nZU(jMqmxbm!*fJLZVgvy750axXG+GFYHxfqRb$l!9O z1isi%>zT#{a+O32K~UY34U03y$!ccsYJBLmr)kPi7^R2UUm5@m9``tPsIf+Dc!!hV z>EPSQ*RElsRc3*a<4*R38B#nB?%fFx{WS5j(!keI!A&ziO!-cjTTWPKOL+ffQP8w!N--FuGZ z_LZ9Y<_|db%l#rFssb`%m9JQ~gvRtyNHezijF>xRzq@q@bOGJ30qN5^Rsee_ukD0{ zCFS@4V{7^3w5pl3hgN4Ci zty&<+-N|}ZwdH$5UiR4!_Pe0N<#x@J8rk3KCXKI)z1Zf8IO0Tob7(LY~wHbg_9AIkCn{^E$e*ch;x)Ihk|`JbO8(y&Ok z$jpXd<~dLyReD&s3CgHw^sfilg0q@%>a?@S>YLNitxjy6<+n(iUx>`aHL}m8DnKnp z&-O!sGdnJeNRPlAxHl6xd*5{)OwqT{u+RpCx1%Qgly6lV>a00&A&u`$I)G7d(xe1n zL;_Bu&6DOA8G(;&IKA7Z7QcJMYdPI;0gNu+YtR(&)7zF-F929gS6so67y#8PrXA!e zfQwJjH-|27{+U2CMWH9g5@O_XL*j~sz)!H7Kax3!X zEVel%sjDMthPS`Jm^?j0u`6o$lfIqa=5xvRP5%zgN$iR~l8?%q^g{Dm3TJ_fW>rDD zk(Uc?buemrC-lTFX@I#bSHV0?_bD~Cq4cAUQbRrLCq<-3lABdzrkkg_Z~XO+OC8pn z^iEXQaE=hhxlFCy4opQx3-g+ynpDLvX3li??4du+uZ#qbH|(zqjNYWzxt;j9S)aJr zs%^Hk*{d#}bpa zLQ8iANfpdN{~2E8B^WBDnb*Y9I-HM2eKTy=v!V3tm%NPVoO!ICX@LlM;lSH`W{H0| zG3LFUMbv+hAq&&0AYFsuPdAY>)01mttNg;8+iTYc$TuFW9Es2Dt#@3Zsh<-sCA;6JUoyrG^8txX|A?4- zI#$w2erKmUNxkQ3I{1-E*y%<&`Db09$?{h^$1LIQ=MdT3iEY%kMixafkO#?C_R0zz zOkoK>(d+GM^IowFj?~Q#W>+nnpXPoF*1%?1jc=#-bw>e`XEFMHhyM%c%Ll(*E&Qy9 zq2|VI0YR(ai!)MWfhg5xZhx_cW55$YP0I0jGCcQl+AZCTx%XV_l^zCj$4FqBpDxsO z2|+C!SlBaJ!*jw)yz^DKbm&pg6OPKC8b%zm{3~U?&35UIr-$=%vPm4N-=K_Z#$SyK z6S&ds0Ti(8U!*iZ<=TCv!(Pc^*_}lPXGJ7QY3Xx4IGMK9(qjcY}Bkj@4DHb3kDpvIJB=NAtl=A^@sJ$Aazymg(Z_Vt4>6@ z+W~A4JFu1y+VTX-82ttq<(1dw#9OZ9sWq#0ALU}x$7@X60>-J;p}Q`po4G2UXglZu zC#fef3`Z-*eh(;0D?1YBgPX`c_xV^h%d0DMueLGwh-i)+4_2W?h`1%xQj(koeSxEC z64Fg|rw=YYIB6r?5N#8Lo0|`G-Ao&E|IUMM@NF{Zc*}Ag!>g`Xm}>_bGhUY!zIx6^ z*cI}GQ?b|a!44>icVcCvx9J6HP;{*zNbl}8$VBJDoh`((1csJGZSv7GkP_@*hZVRW zdk;&AjkgpG*kw^BhqJ}p2_1e)!&DYN{aQ22;29iaL!%F9Y-SmPi58pYfu%DDe@*l6 z6t_f^it%+hg^cQcsAv zG>%-6It;F#ky;7`9oEaV^8f->sH!coR~hgU!uK;9dcAfkaPH8k zx`rbC*NwhWBIfRTn1~{_qbZXNnTGR077aV5+0IGh;Xk_y(_!*b>H4J4yywP`zLjed z(%p&+Mj@T0?`LrnN1yrBV$6$`l)&9D-5Gm4AFo;seijC89*U7QOsvw%bX-bJC&w-)S#;k(4+AodI4JV(g#cpl`UGSowjmmvTdy`k z2ct!5g!5a)#x_hv287+b`XywiO)P>M3_<0;-uf@q!i{mUF~FSt#{Z^;``lmVI&1={ z3aQfI&>~(DZviBiDnrmV>3NoUWZ11g`Vfe*QFZVHNcv_+2`DsA;^(JlWE6rI5C|EI@V;TXEpB-1w;rd9L z{wVS0FH@nxI&&>Lx+jE`&hQ_^GXY=`?_CfU)Hq&KhuP1A!jM5OcoFf|QvbcGrL?iU zOTxA0Kq4npwqpAs6D7OooUEy^#jmG}p|j}SheI#zg5i9~Uz)uASM`}IMD|XJ({fz0 z`+HSR;E0Pd$L~I_A}oG z1r>x5Tdc`EYPetco@-Bre?VQ>d)0DIJ=`lbO>ep3&P0z1uYo~ZNXc%*28@!H3YE{E zeeH`r_x@Sk!EW-VajI}G#8E8kUIAX!(g&=hTSMHApwqOPM9~P^3}&#~E(q^p0YVs2 zT=uYIdu*#$WRm^^`5Vo&-kJ4qoX4tper?Grnj*FE(wmE6f^Pz zq+wv@UJX)Umj$!uY9@gN@020^K2Q&-5;2nd8_WZ&MaItsH&;Y@OLpsai54SsZLlr2 ziLfWYy^vN=olT|!e^e8B$olSBrKxk4KsdY_z=(9r3DU&AdH|a4+!TReOg^Aem$H@9 z)z_C7Ci?2aHW?t#g4?59ASzOd46T;fa~EBzZ`m8naYe^{XB1db-r*34nNC3)7q5c3 zuFf^!K-pg6jR^X$<#dj1hEm-D&YC8L7JiT54psH!Ub<8s3}CX89D~5kd3y4bfnE;B z(jRPEy4;RFw`nOspXvUU19nwi0Y==?9i0b?klksZ>;+uj{im_S7AW9aoWnBXNsO2doYjEAMroAlz<$^G)qql# z++t+pg4?nETyYayVo1Y=610E1RZ9!oI!IwJxQhod^cwx>Jr9cgs4vPYk@6ouD%`j^ zB9`%rlbhauT#XhPIZm56zV{p`N2rF5iGgni)egX4Zk)zjeQVKLS;z1=_BuI>-_}!+wkxpC46zh$S+uvZpE%_-nVE zKDZ-Jo#H-8{sa#Lu0EgtfB@55RD95PuS~|%!tu_w?ll;wTNJC?oghrTl=RLDUwG3W z6~#@X|I=`Pu_H>Dc#?L_ggdcrFGti%Dpj*Q0?AK1}>xfDN+z8UIdSw*;(-TUXz-TA3wS;ch5GVZq4NCe0sXa;Tf=nqqX$L1(e6)~Ui`wg59bgV*ni5M zkj}}9y`??dMwbU~P35?kYm&RA3G@Ad|C6hZ$WFRApPxf)Nevcp&D}27^oTYUUXHRn zV4S}A+E;{bI`s$7jST9RW`)_YVVFe^Kt<}a%AK(7)Jnu${AhtxyVSoFLnLQ=>Ur+b3exs8|mhil*WK{dt}#u4toi|=GO5}VCG`O z2ZbWKh{+z$^&(W8TRYc}X7+y>eutW9#&(pC^w;@xKJ7S7f829WjyRFXJYHXx_iJsv zmh_&y%-iOd#cy3DB~Gnf_$1l}ft02xnFf%}UI+xlh+9J4(TSJM<=m5HFnV%l<{tyh zHv|C@#;%h~sY{H>evPy706{Gv7?o)!z-#PF7u^9+Q4jW-N@hZgZ{D)GP7W*f}r4&zYOTB*3~QeS5xzUj7^d6^>_)soCSP6
vDa?NU}lU}!O_+w1d(s7PC|7G1O5726)Z-!bbT}-2DPuG zBUL#84WQ`F=G@^23=zXEIs-CsVTqZcZ)DlL2F$g-}%K({P$RMD7kX+IV1M2v1~_>qo(|triOz1;+%PvmK^oL z@6Kv@&btChc%_e8*n6k?a@|QWe}tvs0o`gY2vSlpy5s)UOn6nKqe%qqU$Sjs=FKK@ zD{pTbyHn3Iv<}2a@F(@7o#W)v9P=6CUo`E6n;%KEC@rd(g_gtPa{0Xtrm<$OGtbGn z!0U($DK%Y{im^g7Wup1|+r+Y!fdlPUj&7aOEIfQnATZD{jWy>}qV8e@-tmF`67CTq z`#=5cJs)SP6?1{cAg@p!(ltXx=TDcoc+nH-SpN9xuch+VUc*TwVc0zq^e+6j$`wd| zkC?l}$1zs8Ee(YqY+Mtt)fzQm67FWbuWNaZW!AB9vRyN%WZuOK0{SS8Nb}!JtjE3o zTG^7dsc#!GR@1bA9ojy9)%?(wwt3zi9U^<0n@RBamC;gXR?U* zn6<1)_r$4v6)dM>&&xtWb5Q)I8d{_;UV(4w*QvsN#&E zSCY(~RkH9N9-XAjL)yGIbJd5n^Zosb+nbuG^3P3;Ewt?>oy}?v+l7>LG85UH9RoZ~Ye_&fpG9xn3#H#}-MBV;gat<;lN8+7_%fG}PyC0FN%Bg{!1)C31++AeJMlzR zW7#WWy>x*~ZPL@G<2^9rx(flj{K$Y1JgN;IN!Y)_ohl-vw>nW*r`QXjNhsGmX8_U9 z>+L`I9LsH+4t+LLV?RM#N+Nvv!Q|E$euGK} z;uK`mpP`!(Hi9z)PBSEMI^!o3f};{q5*LdGRj{Q zeCCL`ClQp(9I^K+BOp$(K#yA`v0c)3CBUJ4Gu^uYHl$~P`Rs%UsedL>rFTJtj%7GB z;Wrc!->HTT7L+(PT2rNb((di(n`k*s);+^sxrW*0Zlv_J>f=PQKe!^^wI+->>z{jx z+$647nm)2sY4Ibwhlg4ZFl|hyw{eb>QO)f{k3F1mP!e%G4lWS8gqXqt=B?k=UqKB>aCF-}6xSz>a8s^^rw!aY$!Ro*^W% ztu}>5Kx7?`QSSH)mvs;3`lOrPf%ughq5eZ!If6iV-lsz69HuT|*pd0?xq=3rF2^-#^`X@l# zOR_i{wf6I$8taueVE7Aw2-|*sM$uwr2h!>_4U8nZKDrntu`X)CV>7iq-1lEC3J}_C zhBHlNn!GD3Knke8Lu^de|AK)G+?(3ZRRn4c$UR^qkz(v+acBwXh?U7jU}XUp-wSPg;+=_Nt8kdO?IW|D_BNtgz}RyHr5|r zV^*|?Sv%yfRhmJSr$0}CrpCUatVRWNAytHKIj6TbS+|sk6f^Sai=11CwUV)`o83bl zO2R!v=8nFi_9y*AqDCmCBN5hkfzBbXI#G}3&#wS-fcrWK)Qq7H(6O z5=-*yt4Slf2Ycn)XZ8ykcv~S)2p)^s`K~Qv2lhy3!sW3bf?`5#c<~KWp#7`x(FZ$ z)Z&M>Xa7~;%&4}&!*{OTrHFcHHKGk|o1&H$LbPYezn0UL*?$Ze-`sr*1NlbIq-@ao zgp}UCBcbglbRmWc>{o{6slBH8nU>ZXN)fz`c;-#HU|x2}j>20DSK=8Yk&dFa_4e+s*C zxHBBjl%=z)OJ7L7=QuHEQ`()+SB&{wso=`}b1bFcAqjhL0v@)&E`Jri3C_$9OpK^E zY>>X7u1yE~Q+tnHm-H8}8F5Prs}|(hw_L==fLVdbJ@ZjDBix9UVLY#MI^UQ;%g0Uf&ko|?73J|+VIi3c`Wa#LifEtjWy>K^NoBuMC!62a zuMe5=)*jz;mc}E@zXT%6Z{XHB1PA!7*qkWy)u?ayKbA6dsU#hJe=u5Yx!aU!tS@=Qb&CBi>Xp>3on)DHPGI<2$J&^h z6Kl1J;Ad+(f0r#dU!wv|ssXMur76INA!_2Y^eHu0IF}RtPu?4#7X>n@CX0M+6())0 z8glZ81X5F}f0WcX&E0S(DRZS?VVJ*ZQ{{HXqtLBNH?@Hb>U+eGlPXBJ8qV7f)Z$z! zP6>3rCBsgJ19{SrsQIOH4HYApe&Ex`7I~Ns6mvtJug!bDlNVPJ+(0KITUS<7PVeCf zAL?JI%*`v&9$d6qcf|i^iUOc`A;Zd#8AB^yYuY{xRDD{Cy0S)wY0nbeqBCe7=6yop zRMrd7^t7aDBGN1ShLttK_3cBO3g*-)ScvLkTaPG@?5K)rwXpQ5}lKz_4XssH+^a*1Q>|2&HFyTSfGLRmw_`&P%+s2R^i?CTO!Ih=X1bH(=~#v{n{#qQ>6hk=-V1i6RSY&4o<~P4 zruBM~GD~Ytk{#x}>m!1-4FNTH>(nKC!FWYTDgIBbEdQ~0qaEO=)f&$7-M&F#;#Eex z1ZzXOpJVBj!Cz#r`s=X*VOOQ*g*}P4g-y#ceZUnqcLp}Ww)!r1{AbR>_aiy50BH$r zhL?g6HAmY%`!T?+>?bWw74D?v*#D0CYEde{BqZ@v7_I(yte>NHF%6H%A~ugMg8btzhrM$ zGP?O<`97fOv;vNQa94tAW=E+PgXW?901>t6j<~YG+KM%LsgaeBoQ(>Z zu2w;g=fQ-xQ%uM^Wj*+Tds+iXPY)2qYpxRehvNaF2dq{yo5wuRu0`YvLDSzdAFRD~ za7)L%3HNi!f-%FUL@E9*zlRStX;*}G26+axLk4r<6im^>^Zdc!unhloVuakgfQCxY z-G&S9K&QIQ013#`hFpL> zRnWDY5x{&yr8OX8CkX9mRDy3HZlKoiHe;i>^2XVMde8YH;w0>l?^a5KBZfL7*46no zDh^08v9Xn#7qvc0-s@ZVZ!<83=*g5B=t5KE%$8?Zj3-~OJT@K8 z6f#cnWRpJJTmJFVQ$bUCj~o%D>sm1&y|9)3^(%{e<$!%HIy;-COS{L#h zkB_oy!D&7dvsuAo5bcN`Mn7pqJ864Wxc2IeGW9hP1LAO5Ytkogp~EIK-qjHNfX4oXwdg=fix?5mp_{ z?M=NFuJk>&^$I!C3G(4Qy!0Knh-K#D8uu;I%xm4GGsr## zY>Tz`+9k}7cgP@QvQWzL^%p-&cMR>(@(z97sN~LxUHPZCq)%0A_(z4o4NR>?wK|U% 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 diff --git a/addons/easy_charts/Utilities/Rect/Line-Graph1.png.import b/addons/easy_charts/Utilities/Rect/Line-Graph1.png.import deleted file mode 100644 index 680e1ba..0000000 --- a/addons/easy_charts/Utilities/Rect/Line-Graph1.png.import +++ /dev/null @@ -1,34 +0,0 @@ -[remap] - -importer="texture" -type="StreamTexture" -path="res://.import/Line-Graph1.png-cf75f72c04876dd893d0cb31b406bc73.stex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://addons/easy_charts/Utilities/Rect/Line-Graph1.png" -dest_files=[ "res://.import/Line-Graph1.png-cf75f72c04876dd893d0cb31b406bc73.stex" ] - -[params] - -compress/mode=0 -compress/lossy_quality=0.7 -compress/hdr_mode=0 -compress/bptc_ldr=0 -compress/normal_map=0 -flags/repeat=0 -flags/filter=true -flags/mipmaps=false -flags/anisotropic=false -flags/srgb=2 -process/fix_alpha_border=true -process/premult_alpha=false -process/HDR_as_SRGB=false -process/invert_color=false -stream=false -size_limit=0 -detect_3d=true -svg/scale=1.0 diff --git a/addons/easy_charts/Utilities/Scripts/ChartObject.gd b/addons/easy_charts/Utilities/Scripts/ChartObject.gd deleted file mode 100644 index 6b92f74..0000000 --- a/addons/easy_charts/Utilities/Scripts/ChartObject.gd +++ /dev/null @@ -1,39 +0,0 @@ -#extends Object -#class_name ChartObject -# -#""" -#[ChartObject] :: Class -# -#this class is used to store all the functions that Chart, Chart2D and Chart3D custom instances -#will share in-between. -#Chart classes will extend this class. -#""" -# -#enum PointShapes { Dot, Triangle, Square, Cross } -#enum TemplatesNames { Default, Clean, Gradient, Minimal, Invert } -# -#class Chart extends Control: -# var CHART_TYPE : String = "Chart" -# enum PointShapes { Dot, Triangle, Square, Cross } -# enum TemplatesNames { Default, Clean, Gradient, Minimal, Invert } -# -# export (PoolColorArray) var function_colors = [Color("#1e1e1e")] -# export (Array, PointShapes) var points_shape : Array = [PointShapes.Dot] -# -# var functions : int = 0 -# -# func calculate_colors(): -# if function_colors.empty() or function_colors.size() < functions: -# for function in functions: -# function_colors.append(Color("#1e1e1e")) -# -# func set_shapes(): -# if points_shape.empty() or points_shape.size() < functions: -# for function in functions: -# points_shape.append(PointShapes.Dot) -# -# -#class Chart2D extends Node2D: -# var CHART_TYPE : String = "Chart2D" -# enum PointShapes { Dot, Triangle, Square, Cross } -# enum TemplatesNames { Default, Clean, Gradient, Minimal, Invert } diff --git a/addons/easy_charts/Utilities/Scripts/chart_object.gd b/addons/easy_charts/Utilities/Scripts/chart_object.gd deleted file mode 100644 index 6b92f74..0000000 --- a/addons/easy_charts/Utilities/Scripts/chart_object.gd +++ /dev/null @@ -1,39 +0,0 @@ -#extends Object -#class_name ChartObject -# -#""" -#[ChartObject] :: Class -# -#this class is used to store all the functions that Chart, Chart2D and Chart3D custom instances -#will share in-between. -#Chart classes will extend this class. -#""" -# -#enum PointShapes { Dot, Triangle, Square, Cross } -#enum TemplatesNames { Default, Clean, Gradient, Minimal, Invert } -# -#class Chart extends Control: -# var CHART_TYPE : String = "Chart" -# enum PointShapes { Dot, Triangle, Square, Cross } -# enum TemplatesNames { Default, Clean, Gradient, Minimal, Invert } -# -# export (PoolColorArray) var function_colors = [Color("#1e1e1e")] -# export (Array, PointShapes) var points_shape : Array = [PointShapes.Dot] -# -# var functions : int = 0 -# -# func calculate_colors(): -# if function_colors.empty() or function_colors.size() < functions: -# for function in functions: -# function_colors.append(Color("#1e1e1e")) -# -# func set_shapes(): -# if points_shape.empty() or points_shape.size() < functions: -# for function in functions: -# points_shape.append(PointShapes.Dot) -# -# -#class Chart2D extends Node2D: -# var CHART_TYPE : String = "Chart2D" -# enum PointShapes { Dot, Triangle, Square, Cross } -# enum TemplatesNames { Default, Clean, Gradient, Minimal, Invert } From 90f3ea8a56f2c387993af9f3ae2426576adeb4bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Santilio?= Date: Thu, 31 Dec 2020 13:21:08 +0100 Subject: [PATCH 03/13] git update 2 --- addons/easy_charts/BarChart2D/BarChart2D.gd | 99 --------- addons/easy_charts/LineChart2D/LineChart2D.gd | 194 ----------------- .../ScatterChart2D/ScatterChart2D.gd | 99 --------- .../easy_charts/Utilities/Scripts/chart2d.gd | 203 +++++++++++++++++- 4 files changed, 202 insertions(+), 393 deletions(-) diff --git a/addons/easy_charts/BarChart2D/BarChart2D.gd b/addons/easy_charts/BarChart2D/BarChart2D.gd index 3093a8a..f3abf1a 100644 --- a/addons/easy_charts/BarChart2D/BarChart2D.gd +++ b/addons/easy_charts/BarChart2D/BarChart2D.gd @@ -17,105 +17,6 @@ values of more than one measured variable. / source : Wikipedia / """ -var OutlinesTween : Tween -var FunctionsTween -var Functions : Node2D -var GridTween : Tween -var PointData : PointData -var Outlines : Line2D -var Grid : Node2D - -var point_node : PackedScene = preload("../Utilities/Point/Point.tscn") -var FunctionLegend : PackedScene = preload("../Utilities/Legend/FunctionLegend.tscn") - -var font_size : float = 16 -var const_height : float = font_size/2*font_size/20 -var const_width : float = font_size/2 - -var OFFSET : Vector2 = Vector2(0,0) - -#-------------------------------------------------------------------------# -var origin : Vector2 - -# actual distance between x and y values -var x_pass : float -var y_pass : float - -# vertical distance between y consecutive points used for intervals -var v_dist : float -var h_dist : float - -# quantization, representing the interval in which values will be displayed - -# define values on x an y axis -var x_chors : Array -var y_chors : Array - -# actual coordinates of points (in pixel) -var x_coordinates : Array -var y_coordinates : Array - -# datas contained in file -var datas : Array - -# amount of functions to represent -var functions : int = 0 - -var x_label : String - -# database values -var x_datas : Array -var y_datas : Array - -# labels displayed on chart -var x_labels : Array -var y_labels : Array - -var x_margin_min : int = 0 -var y_margin_min : int = 0 - -# actual values of point, from the database -var point_values : Array - -# actual position of points in pixel -var point_positions : Array - -var legend : Array setget set_legend,get_legend - -# --------------------- -export (Vector2) var SIZE : Vector2 = Vector2() setget _set_size -export (String, FILE, "*.txt, *.csv") var source : String = "" -export (String) var delimiter : String = ";" -export (bool) var origin_at_zero : bool = true - -export (bool) var are_values_columns : bool = false -export (int,0,100) var x_values_index : int = 0 -export(bool) var show_x_values_as_labels : bool = true - -export (float,1,20,0.5) var column_width : float = 10 -export (float,0,10,0.5) var column_gap : float = 2 - -export (float,0.1,10.0) var x_decim : float = 5.0 -export (float,0.1,10.0) var y_decim : float = 5.0 -export (PointShapes) var point_shape : int = 0 -export (PoolColorArray) var function_colors = [Color("#1e1e1e")] -export (Color) var v_lines_color : Color = Color("#cacaca") -export (Color) var h_lines_color : Color = Color("#cacaca") - -export (bool) var boxed : bool = true -export (Color) var box_color : Color = Color("#1e1e1e") -export (Font) var font : Font -export (Font) var bold_font : Font -export (Color) var font_color : Color = Color("#1e1e1e") -export (TemplatesNames) var template : int = Chart.TemplatesNames.Default setget apply_template -export (float,0.1,1) var drawing_duration : float = 0.5 -export (bool) var invert_chart : bool = false - -var templates : Dictionary = {} - -signal chart_plotted(chart) -signal point_pressed(point) - func _point_plotted(): pass diff --git a/addons/easy_charts/LineChart2D/LineChart2D.gd b/addons/easy_charts/LineChart2D/LineChart2D.gd index e3b0f31..6653c79 100644 --- a/addons/easy_charts/LineChart2D/LineChart2D.gd +++ b/addons/easy_charts/LineChart2D/LineChart2D.gd @@ -14,108 +14,6 @@ class_name LineChart2D # In these cases they are known as run charts. # Source: Wikipedia - -signal chart_plotted(chart) -signal point_pressed(point) - - -const OFFSET: Vector2 = Vector2(0,0) - - -export (Vector2) var SIZE: Vector2 = Vector2() setget _set_size -export (String, FILE, "*.txt, *.csv") var source: String = "" -export (String) var delimiter: String = ";" -export (bool) var origin_at_zero: bool = true - -export (bool) var are_values_columns: bool = false -export (int, 0, 100) var x_values_index: int = 0 -export(bool) var show_x_values_as_labels: bool = true - -#export (float,1,20,0.5) var column_width: float = 10 -#export (float,0,10,0.5) var column_gap: float = 2 - -export (float, 0.1, 10.0) var x_decim: float = 5.0 -export (float, 0.1, 10.0) var y_decim: float = 5.0 -export (PointShapes) var point_shape: int = 0 -export (PoolColorArray) var function_colors = [Color("#1e1e1e")] -export (Color) var v_lines_color: Color = Color("#cacaca") -export (Color) var h_lines_color: Color = Color("#cacaca") - -export (bool) var boxed: bool = true -export (Color) var box_color: Color = Color("#1e1e1e") -export (Font) var font: Font -export (Font) var bold_font: Font -export (Color) var font_color: Color = Color("#1e1e1e") -export (TemplatesNames) var template: int = Chart.TemplatesNames.Default setget apply_template -export (float, 0.1, 1) var drawing_duration: float = 0.5 -export (bool) var invert_chart: bool = false - - -var OutlinesTween: Tween -var FunctionsTween: Tween -var Functions: Node2D -var GridTween: Tween -var PointData: PointData -var Outlines: Line2D -var Grid: Node2D - -var point_node: PackedScene = preload("../Utilities/Point/Point.tscn") -var FunctionLegend: PackedScene = preload("../Utilities/Legend/FunctionLegend.tscn") - -var font_size: float = 16 -var const_height: float = font_size / 2 * font_size / 20 -var const_width: float = font_size / 2 - -var origin: Vector2 - -# actual distance between x and y values -var x_pass: float -var y_pass: float - -# vertical distance between y consecutive points used for intervals -var v_dist: float -var h_dist: float - -# quantization, representing the interval in which values will be displayed - -# define values on x an y axis -var x_chors: Array -var y_chors: Array - -# actual coordinates of points (in pixel) -var x_coordinates: Array -var y_coordinates: Array - -# datas contained in file -var datas: Array - -# amount of functions to represent -var functions: int = 0 - -var x_label: String - -# database values -var x_datas: Array -var y_datas: Array - -# labels displayed on chart -var x_labels: Array -var y_labels: Array - -var x_margin_min: int = 0 -var y_margin_min: int = 0 - -# actual values of point, from the database -var point_values: Array - -# actual position of points in pixel -var point_positions: Array - -var legend: Array setget set_legend, get_legend - -var templates: Dictionary = {} - - func _point_plotted(): pass @@ -124,16 +22,6 @@ func _ready(): _get_children() -func _get_children(): - OutlinesTween = $OutlinesTween - FunctionsTween = $FunctionsTween - Functions = $Functions - GridTween = $GridTween - PointData = $PointData/PointData - Outlines = $Outlines - Grid = $Grid - - func _set_size(size: Vector2): SIZE = size build_chart() @@ -566,94 +454,12 @@ func calculate_coordinates(): origin.y - y_coordinates[cluster][y])) -func redraw(): - build_chart() - calculate_pass() - calculate_coordinates() - update() - - -func show_data(point): - PointData.update_datas(point) - PointData.show() - - -func hide_data(): - PointData.hide() - - -func clear_points(): - function_colors.clear() - if Functions.get_children(): - for function in Functions.get_children(): - function.queue_free() - - -func set_legend(l: Array): - legend = l - - -func get_legend(): - return legend - - func invert_chart(): invert_chart = !invert_chart count_functions() redraw() create_legend() - -func count_functions(): - if are_values_columns: - if not invert_chart: - functions = datas[0].size() - 1 - else: - functions = datas.size() - 1 - else: - if invert_chart: - functions = datas[0].size() - 1 - else: - functions = datas.size() - 1 - - -func create_legend(): - legend.clear() - for function in functions: - var function_legend = FunctionLegend.instance() - var f_name: String - if invert_chart: - f_name = x_datas[function] as String - else: - f_name = y_labels[function] - var legend_font: Font - if font != null: - legend_font = font - if bold_font != null: - legend_font = bold_font - function_legend.create_legend(f_name, function_colors[function], bold_font, font_color) - legend.append(function_legend) - - -func apply_template(template_name: int): - template = template_name - templates = Utilities._load_templates() - if template_name != null: - var custom_template = templates.get(templates.keys()[template_name]) - function_colors = custom_template.function_colors as PoolColorArray - v_lines_color = Color(custom_template.v_lines_color) - h_lines_color = Color(custom_template.h_lines_color) - box_color = Color(custom_template.outline_color) - font_color = Color(custom_template.font_color) - property_list_changed_notify() - - if Engine.editor_hint: - _get_children() - Outlines.set_default_color(box_color) - Grid.get_node("VLine").set_default_color(v_lines_color) - Grid.get_node("HLine").set_default_color(h_lines_color) - - func _enter_tree(): _ready() diff --git a/addons/easy_charts/ScatterChart2D/ScatterChart2D.gd b/addons/easy_charts/ScatterChart2D/ScatterChart2D.gd index 98c6419..aea49f0 100644 --- a/addons/easy_charts/ScatterChart2D/ScatterChart2D.gd +++ b/addons/easy_charts/ScatterChart2D/ScatterChart2D.gd @@ -14,105 +14,6 @@ the horizontal axis and the value of the other variable determining the position / source : Wikipedia / """ -var OutlinesTween : Tween -var PointTween : Tween -var Functions : Node2D -var GridTween : Tween -var PointData : PointData -var Outlines : Line2D -var Grid : Node2D - -var point_node : PackedScene = preload("../Utilities/Point/Point.tscn") -var FunctionLegend : PackedScene = preload("../Utilities/Legend/FunctionLegend.tscn") - -var font_size : float = 16 -var const_height : float = font_size/2*font_size/20 -var const_width : float = font_size/2 - -var OFFSET : Vector2 = Vector2(0,0) - -#-------------------------------------------------------------------------# -var origin : Vector2 - -# actual distance between x and y values -var x_pass : float -var y_pass : float - -# vertical distance between y consecutive points used for intervals -var v_dist : float -var h_dist : float - -# quantization, representing the interval in which values will be displayed - -# define values on x an y axis -var x_chors : Array -var y_chors : Array - -# actual coordinates of points (in pixel) -var x_coordinates : Array -var y_coordinates : Array - -# datas contained in file -var datas : Array - -# amount of functions to represent -var functions : int = 0 - -var x_label : String - -# database values -var x_datas : Array -var y_datas : Array - -# labels displayed on chart -var x_labels : Array -var y_labels : Array - -var x_margin_min : int = 0 -var y_margin_min : int = 0 - -# actual values of point, from the database -var point_values : Array - -# actual position of points in pixel -var point_positions : Array - -var legend : Array setget set_legend,get_legend - -# --------------------- -export (Vector2) var SIZE : Vector2 = Vector2() setget _set_size -export (String, FILE, "*.txt, *.csv") var source : String = "" -export (String) var delimiter : String = ";" -export (bool) var origin_at_zero : bool = true - -export (bool) var are_values_columns : bool = false -export (int,0,100) var x_values_index : int = 0 -export(bool) var show_x_values_as_labels : bool = true - -#export (float,1,20,0.5) var column_width : float = 10 -#export (float,0,10,0.5) var column_gap : float = 2 - -export (float,0.1,10.0) var x_decim : float = 5.0 -export (float,0.1,10.0) var y_decim : float = 5.0 -export (Array, PointShapes) var points_shape : Array = 0 -export (PoolColorArray) var function_colors = [Color("#1e1e1e")] as PoolColorArray -export (Color) var v_lines_color : Color = Color("#cacaca") -export (Color) var h_lines_color : Color = Color("#cacaca") - -export (bool) var boxed : bool = true -export (Color) var box_color : Color = Color("#1e1e1e") -export (Font) var font : Font -export (Font) var bold_font : Font -export (Color) var font_color : Color = Color("#1e1e1e") -export (TemplatesNames) var template : int = TemplatesNames.Default setget apply_template -export (float,0.1,1) var drawing_duration : float = 0.5 -export (bool) var invert_chart : bool = false - -var templates : Dictionary = {} - -signal chart_plotted(chart) -signal point_pressed(point) - func _point_plotted(): pass diff --git a/addons/easy_charts/Utilities/Scripts/chart2d.gd b/addons/easy_charts/Utilities/Scripts/chart2d.gd index e22bb4e..27f6f65 100644 --- a/addons/easy_charts/Utilities/Scripts/chart2d.gd +++ b/addons/easy_charts/Utilities/Scripts/chart2d.gd @@ -5,6 +5,207 @@ class_name Chart2D enum PointShapes { Dot, Triangle, Square, Cross } enum TemplatesNames { Default, Clean, Gradient, Minimal, Invert } + +signal chart_plotted(chart) +signal point_pressed(point) + + +const OFFSET: Vector2 = Vector2(0,0) + + +export (Vector2) var SIZE: Vector2 = Vector2() setget _set_size +export (String, FILE, "*.txt, *.csv") var source: String = "" +export (String) var delimiter: String = ";" +export (bool) var origin_at_zero: bool = true + +export (bool) var are_values_columns: bool = false +export (int, 0, 100) var x_values_index: int = 0 +export(bool) var show_x_values_as_labels: bool = true + +export (float,1,20,0.5) var column_width: float = 10 +export (float,0,10,0.5) var column_gap: float = 2 + +export (float, 0.1, 10.0) var x_decim: float = 5.0 +export (float, 0.1, 10.0) var y_decim: float = 5.0 +export (PointShapes) var point_shape: int = 0 +export (PoolColorArray) var function_colors = [Color("#1e1e1e")] +export (Color) var v_lines_color: Color = Color("#cacaca") +export (Color) var h_lines_color: Color = Color("#cacaca") + +export (bool) var boxed: bool = true +export (Color) var box_color: Color = Color("#1e1e1e") +export (Font) var font: Font +export (Font) var bold_font: Font +export (Color) var font_color: Color = Color("#1e1e1e") +export var template : int = 0 setget apply_template +export (float, 0.1, 1) var drawing_duration: float = 0.5 +export (bool) var invert_chart: bool = false + + +var OutlinesTween: Tween +var FunctionsTween: Tween +var PointTween : Tween +var Functions: Node2D +var GridTween: Tween +var PointData: PointData +var Outlines: Line2D +var Grid: Node2D + +var point_node: PackedScene = preload("../Point/Point.tscn") +var FunctionLegend: PackedScene = preload("../Legend/FunctionLegend.tscn") + +var font_size: float = 16 +var const_height: float = font_size / 2 * font_size / 20 +var const_width: float = font_size / 2 + +var origin: Vector2 + +# actual distance between x and y values +var x_pass: float +var y_pass: float + +# vertical distance between y consecutive points used for intervals +var v_dist: float +var h_dist: float + +# quantization, representing the interval in which values will be displayed + +# define values on x an y axis +var x_chors: Array +var y_chors: Array + +# actual coordinates of points (in pixel) +var x_coordinates: Array +var y_coordinates: Array + +# datas contained in file +var datas: Array + +# amount of functions to represent +var functions: int = 0 + +var x_label: String + +# database values +var x_datas: Array +var y_datas: Array + +# labels displayed on chart +var x_labels: Array +var y_labels: Array + +var x_margin_min: int = 0 +var y_margin_min: int = 0 + +# actual values of point, from the database +var point_values: Array + +# actual position of points in pixel +var point_positions: Array + +var legend: Array setget set_legend, get_legend + +var templates: Dictionary = {} + + # 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 build_chart(): + pass + +func calculate_pass(): + pass + +func calculate_coordinates(): + pass + +func _set_size(v : Vector2): + SIZE = v + +func _get_children(): + OutlinesTween = $OutlinesTween + FunctionsTween = $FunctionsTween + Functions = $Functions + GridTween = $GridTween + PointData = $PointData/PointData + Outlines = $Outlines + Grid = $Grid + +func apply_template(template_name: int): + template = template_name + templates = Utilities._load_templates() + if template_name != null: + var custom_template = templates.get(templates.keys()[template_name]) + function_colors = custom_template.function_colors as PoolColorArray + v_lines_color = Color(custom_template.v_lines_color) + h_lines_color = Color(custom_template.h_lines_color) + box_color = Color(custom_template.outline_color) + font_color = Color(custom_template.font_color) + property_list_changed_notify() + + if Engine.editor_hint: + _get_children() + Outlines.set_default_color(box_color) + Grid.get_node("VLine").set_default_color(v_lines_color) + Grid.get_node("HLine").set_default_color(h_lines_color) + +func redraw(): + build_chart() + calculate_pass() + calculate_coordinates() + update() + + +func show_data(point): + PointData.update_datas(point) + PointData.show() + + +func hide_data(): + PointData.hide() + + +func clear_points(): + function_colors.clear() + if Functions.get_children(): + for function in Functions.get_children(): + function.queue_free() + +func count_functions(): + if are_values_columns: + if not invert_chart: + functions = datas[0].size() - 1 + else: + functions = datas.size() - 1 + else: + if invert_chart: + functions = datas[0].size() - 1 + else: + functions = datas.size() - 1 + +func set_legend(l: Array): + legend = l + + +func get_legend(): + return legend + + +func create_legend(): + legend.clear() + for function in functions: + var function_legend = FunctionLegend.instance() + var f_name: String + if invert_chart: + f_name = x_datas[function] as String + else: + f_name = y_labels[function] + var legend_font: Font + if font != null: + legend_font = font + if bold_font != null: + legend_font = bold_font + function_legend.create_legend(f_name, function_colors[function], bold_font, font_color) + legend.append(function_legend) From d85a85d71e6915b7ae609d18cfdb34a914007f8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Santilio?= Date: Thu, 31 Dec 2020 13:47:14 +0100 Subject: [PATCH 04/13] @invert_chart and @are_values_columns fixed --- addons/easy_charts/LineChart/line_chart.gd | 94 ++++++------------- addons/easy_charts/Utilities/Scripts/chart.gd | 26 ++--- 2 files changed, 40 insertions(+), 80 deletions(-) diff --git a/addons/easy_charts/LineChart/line_chart.gd b/addons/easy_charts/LineChart/line_chart.gd index e379471..8b398f1 100644 --- a/addons/easy_charts/LineChart/line_chart.gd +++ b/addons/easy_charts/LineChart/line_chart.gd @@ -154,14 +154,14 @@ func _get_property_list(): ] -func structure_datas(database: Array, are_values_columns: bool, x_values_index: int): - # @x_values_index can be either a column or a row relative to x values - self.are_values_columns = are_values_columns +func structure_datas(database: Array): + # @labels_index can be either a column or a row relative to x values + are_values_columns = (invert_chart != are_values_columns) if are_values_columns: for row in database.size(): var t_vals: Array for column in database[row].size(): - if column == x_values_index: + if column == labels_index: var x_data = database[row][column] if x_data.is_valid_float() or x_data.is_valid_integer(): x_datas.append(x_data as float) @@ -181,7 +181,7 @@ func structure_datas(database: Array, are_values_columns: bool, x_values_index: x_label = str(x_datas.pop_front()) else: for row in database.size(): - if row == x_values_index: + if row == labels_index: x_datas = (database[row]) x_label = x_datas.pop_front() as String else: @@ -244,13 +244,10 @@ func build_chart(): func calculate_pass(): - if invert_chart: - x_chors = y_labels.duplicate(true) as PoolStringArray + if show_x_values_as_labels: + x_chors = x_datas.duplicate(true) as PoolStringArray else: - if show_x_values_as_labels: - x_chors = x_datas.duplicate(true) as PoolStringArray - else: - x_chors = x_labels.duplicate(true) + x_chors = x_labels.duplicate(true) # calculate distance in pixel between 2 consecutive values/datas x_pass = (SIZE.x - OFFSET.x) / (x_chors.size()-1 if x_chors.size()>1 else x_chors.size() ) @@ -262,25 +259,15 @@ func calculate_coordinates(): y_coordinates.clear() point_values.clear() point_positions.clear() - - if invert_chart: - for column in y_datas[0].size(): - var single_coordinates: Array - for row in y_datas: - if origin_at_zero: - single_coordinates.append((row[column] * y_pass) / v_dist) - else: - single_coordinates.append((row[column] - y_margin_min) * y_pass / v_dist) - y_coordinates.append(single_coordinates) - else: - for cluster in y_datas: - var single_coordinates: Array - for value in cluster.size(): - if origin_at_zero: - single_coordinates.append((cluster[value] * y_pass) / v_dist) - else: - single_coordinates.append((cluster[value] - y_margin_min) * y_pass / v_dist) - y_coordinates.append(single_coordinates) + + for cluster in y_datas: + var single_coordinates: Array + for value in cluster.size(): + if origin_at_zero: + single_coordinates.append((cluster[value] * y_pass) / v_dist) + else: + single_coordinates.append((cluster[value] - y_margin_min) * y_pass / v_dist) + y_coordinates.append(single_coordinates) if show_x_values_as_labels: for x in x_datas.size(): @@ -288,10 +275,7 @@ func calculate_coordinates(): else: for x in x_datas.size(): if origin_at_zero: - if invert_chart: - x_coordinates.append(x_pass * x) - else: - x_coordinates.append(x_datas[x] * x_pass / h_dist) + x_coordinates.append(x_datas[x] * x_pass / h_dist) else: x_coordinates.append((x_datas[x] - x_margin_min) * x_pass / h_dist) @@ -299,33 +283,17 @@ func calculate_coordinates(): point_values.append([]) point_positions.append([]) - if invert_chart: - for function in y_coordinates.size(): - for function_value in y_coordinates[function].size(): - if are_values_columns: - point_positions[function_value].append(Vector2( - x_coordinates[function] + origin.x, - origin.y - y_coordinates[function][function_value])) - point_values[function_value].append( - [x_datas[function_value], y_datas[function_value][function]]) - else: - point_positions[function].append(Vector2( - x_coordinates[function_value] + origin.x, - origin.y - y_coordinates[function][function_value])) - point_values[function].append( - [x_datas[function_value], y_datas[function_value][function]]) - else: - for cluster in y_coordinates.size(): - for y in y_coordinates[cluster].size(): - if are_values_columns: - point_values[y].append([x_datas[cluster], y_datas[cluster][y]]) - point_positions[y].append(Vector2( - x_coordinates[cluster] + origin.x, origin.y - y_coordinates[cluster][y])) - else: - point_values[cluster].append([x_datas[y], y_datas[cluster][y]]) - point_positions[cluster].append(Vector2( - x_coordinates[y] + origin.x, - origin.y - y_coordinates[cluster][y])) + for cluster in y_coordinates.size(): + for y in y_coordinates[cluster].size(): + if are_values_columns: + point_values[y].append([x_datas[cluster], y_datas[cluster][y]]) + point_positions[y].append(Vector2( + x_coordinates[cluster] + origin.x, origin.y - y_coordinates[cluster][y])) + else: + point_values[cluster].append([x_datas[y], y_datas[cluster][y]]) + point_positions[cluster].append(Vector2( + x_coordinates[y] + origin.x, + origin.y - y_coordinates[cluster][y])) func _draw(): clear_points() @@ -346,14 +314,14 @@ func _draw(): point.connect("_point_pressed", self, "point_pressed") point.connect("_mouse_entered", self, "show_data") point.connect("_mouse_exited", self, "hide_data") - point.create_point(points_shape[_function],function_colors[function_point if invert_chart else _function],Color.white,point_positions[_function][function_point],point.format_value(point_values[_function][function_point], false, false),y_labels[function_point if invert_chart else _function] as String) + point.create_point(points_shape[_function],function_colors[_function],Color.white,point_positions[_function][function_point],point.format_value(point_values[_function][function_point], false, false),y_labels[_function] as String) Points.add_child(point) if function_point > 0: draw_line( point_positions[_function][function_point - 1], point_positions[_function][function_point], - function_colors[function_point if invert_chart else _function], + function_colors[_function], 2, false) draw_treshold() diff --git a/addons/easy_charts/Utilities/Scripts/chart.gd b/addons/easy_charts/Utilities/Scripts/chart.gd index 45a99af..435d5c0 100644 --- a/addons/easy_charts/Utilities/Scripts/chart.gd +++ b/addons/easy_charts/Utilities/Scripts/chart.gd @@ -320,7 +320,7 @@ func plot(): data = read_datas(source) - structure_datas(slice_data(),are_values_columns,labels_index) + structure_datas(slice_data()) build_chart() count_functions() calculate_pass() @@ -339,7 +339,7 @@ func plot_from_csv(csv_file : String, _delimiter : String = delimiter): return data = read_datas(csv_file, _delimiter) - structure_datas(slice_data(),are_values_columns,labels_index) + structure_datas(slice_data()) build_chart() count_functions() calculate_pass() @@ -360,7 +360,7 @@ func plot_from_array(array : Array) -> void: return data = array.duplicate() - structure_datas(slice_data(), are_values_columns,labels_index) + structure_datas(slice_data()) build_chart() count_functions() calculate_pass() @@ -385,7 +385,7 @@ func plot_from_dataframe(dataframe : DataFrame) -> void: Utilities._print_message("Can't plot a chart with an empty Array.",1) return - structure_datas(slice_data(),are_values_columns,labels_index) + structure_datas(slice_data()) build_chart() count_functions() calculate_pass() @@ -405,7 +405,7 @@ func update_plot_data(array : Array) -> void: return data.append(array) - structure_datas(slice_data(),are_values_columns,labels_index) + structure_datas(slice_data()) redraw() count_functions() calculate_colors() @@ -463,16 +463,8 @@ func read_datas(source : String, _delimiter : String = delimiter): return content func count_functions(): - if are_values_columns: - if not invert_chart: - functions = data[0].size()-1 - else: - functions = data.size()-1 - else: - if invert_chart: - functions = x_datas.size() - else: - functions = y_datas.size() + if are_values_columns: functions = data[0].size()-1 + else: functions = y_datas.size() func clear_points(): if $Points.get_children(): @@ -497,7 +489,7 @@ func clean_variables(): y_labels.clear() # .................. VIRTUAL FUNCTIONS ......................... -func structure_datas(database : Array, are_values_columns : bool, labels_index : int): +func structure_datas(database : Array): pass func build_chart(): @@ -520,7 +512,7 @@ func create_legend(): else: function_legend = LegendElement.instance() legend.append(function_legend) - var f_name : String = y_labels[function] + var f_name : String = y_labels[function] if not are_values_columns else str(x_datas[function]) var legend_font : Font if font != null: legend_font = font From 215363ab67a64912bc802f0c28bc17c4e690a140 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Santilio?= Date: Thu, 31 Dec 2020 13:56:27 +0100 Subject: [PATCH 05/13] @structure_data changed --- addons/easy_charts/Utilities/Scripts/Chart.gd | 638 ---------------- .../easy_charts/Utilities/Scripts/Chart2D.gd | 10 - addons/easy_charts/Utilities/Scripts/chart.gd | 709 ------------------ .../easy_charts/Utilities/Scripts/chart2d.gd | 211 ------ 4 files changed, 1568 deletions(-) delete mode 100644 addons/easy_charts/Utilities/Scripts/Chart.gd delete mode 100644 addons/easy_charts/Utilities/Scripts/Chart2D.gd delete mode 100644 addons/easy_charts/Utilities/Scripts/chart.gd delete mode 100644 addons/easy_charts/Utilities/Scripts/chart2d.gd diff --git a/addons/easy_charts/Utilities/Scripts/Chart.gd b/addons/easy_charts/Utilities/Scripts/Chart.gd deleted file mode 100644 index ec647c0..0000000 --- a/addons/easy_charts/Utilities/Scripts/Chart.gd +++ /dev/null @@ -1,638 +0,0 @@ -extends Control -class_name Chart - -# Classes -enum TYPES { Line, Bar, Scatter, Radar, Pie } - -# Signals .................................. -signal chart_plotted(chart) # emit when a chart is plotted (static) or updated (dynamic) -signal point_pressed(point) - -# Onready Vars ............................ -onready var PointData = $PointData/PointData -onready var Points = $Points -onready var Legend = $Legend -onready var ChartName : Label = $ChartName - -# Scenes and Reosurces ...................... -var point_node : PackedScene = preload("../Point/Point.tscn") -var LegendElement : PackedScene = preload("../Legend/FunctionLegend.tscn") - -# Enums ..................................... -enum PointShapes { Dot, Triangle, Square, Cross } - -# Shared Variables ......................... -var SIZE : Vector2 = Vector2() -var OFFSET : Vector2 = Vector2(0,0) -var origin : Vector2 - -var font_size : float = 16 -var const_height : float = font_size/2*font_size/20 -var const_width : float = font_size/2 - -# actual distance between x and y values -var x_pass : float -var y_pass : float - -# vertical distance between y consecutive points used for intervals -var v_dist : float -var h_dist : float - -# define values on x an y axis -var x_chors : Array -var y_chors : Array - -# actual coordinates of points (in pixel) -var x_coordinates : Array -var y_coordinates : Array - -# data contained in file -var data : Array - -# amount of functions to represent -var functions : int = 0 - -# database values -var x_datas : Array -var y_datas : Array - -# labels displayed on chart -var x_label : String - -var x_labels : Array -var y_labels : Array - -var x_margin_min : int = 0 -var y_margin_min : int = 0 - -# actual values of point, from the database -var point_values : Array - -# actual position of points in pixel -var point_positions : Array - -var legend : Array setget set_legend,get_legend - -var templates : Dictionary = {} - -# ................... Export Shared Variables .................. -export (String) var chart_name : String = "" setget set_chart_name -export (String, FILE, "*.txt, *.csv") var source : String = "" setget set_source -export (String) var delimiter : String = ";" setget set_delimiter - -var origin_at_zero : bool = true setget set_origin_at_zero#, get_origin_at_zero -var are_values_columns : bool = false setget set_are_values_columns#, get_are_values_columns -var show_x_values_as_labels : bool = true setget set_show_x_values_as_labels#, get_show_x_values_as_labels -var labels_index : int = 0 setget set_labels_index#, get_labels_index -var function_names_index : int = 0 setget set_function_names_index#, get_function_names_index - -# for radar -var use_height_as_radius : bool = false setget set_use_height_as_radius -var radius : float = 150.0 setget _set_radius,get_radius - -# for columns -var column_width : float = 10 setget set_column_width -var column_gap : float = 2 setget set_column_gap - -var full_scale : float = 1.0 setget set_full_scale -var x_decim : float = 5.0 setget set_x_decim -var y_decim : float = 5.0 setget set_y_decim - -var points_shape : Array = [PointShapes.Dot] setget set_points_shape -var function_colors = [Color("#1e1e1e")] setget set_function_colors -var outline_color : Color = Color("#1e1e1e") setget set_outline_color -var box_color : Color = Color("#1e1e1e") setget set_box_color -var v_lines_color : Color = Color("#cacaca") setget set_v_lines_color -var h_lines_color : Color = Color("#cacaca") setget set_h_lines_color -var grid_color : Color = Color("#1e1e1e") setget set_grid_color -var font : Font setget set_font -var bold_font : Font setget set_bold_font -var font_color : Color = Color("#1e1e1e") setget set_font_color - -var template : int = 0 setget set_template - -# modifiers -var rotation : float = 0 setget set_rotation -var invert_chart : bool = false setget set_invert_chart - -static func instance(chart_type : int): - var chart_t : String = Utilities.get_chart_type(chart_type) - var chart : String = "res://addons/easy_charts/%s/%s.tscn" % [chart_t, chart_t] - return load(chart).instance() - -# .......................... Properties Manager .................................... -func _get(property): - match property: - "Chart_Properties/origin_at_zero": - return origin_at_zero - "Chart_Properties/are_values_columns": - return are_values_columns - "Chart_Properties/show_x_values_as_labels": - return show_x_values_as_labels - "Chart_Properties/labels_index": - return labels_index - "Chart_Properties/function_names_index": - return function_names_index - "Chart_Properties/use_height_as_radius": - return use_height_as_radius - "Chart_Properties/radius": - return radius - "Chart_Properties/column_width": - return column_width - "Chart_Properties/column_gap": - return column_gap - - "Chart_Display/full_scale": - return full_scale - "Chart_Display/x_decim": - return x_decim - "Chart_Display/y_decim": - return y_decim - - "Chart_Style/points_shape": - return points_shape - "Chart_Style/function_colors": - return function_colors - "Chart_Style/template": - return template - "Chart_Style/outline_color": - return outline_color - "Chart_Style/grid_color": - return grid_color - "Chart_Style/box_color": - return box_color - "Chart_Style/v_lines_color": - return v_lines_color - "Chart_Style/h_lines_color": - return h_lines_color - "Chart_Style/font": - return font - "Chart_Style/bold_font": - return bold_font - "Chart_Style/font_color": - return font_color - - "Chart_Modifiers/rotation": - return rotation - "Chart_Modifiers/invert_chart": - return invert_chart - -func _set(property, value): - match property: - "Chart_Properties/origin_at_zero": - origin_at_zero = value - return true - "Chart_Properties/are_values_columns": - are_values_columns = value - return true - "Chart_Properties/show_x_values_as_labels": - show_x_values_as_labels = value - return true - "Chart_Properties/labels_index": - labels_index = value - return true - "Chart_Properties/function_names_index": - function_names_index = value - return true - "Chart_Properties/use_height_as_radius": - use_height_as_radius = value - return true - "Chart_Properties/radius": - radius = value - return true - "Chart_Properties/column_width": - column_width = value - return true - "Chart_Properties/column_gap": - column_width = value - return true - - "Chart_Display/full_scale": - full_scale = value - return true - "Chart_Display/x_decim": - x_decim = value - return true - "Chart_Display/y_decim": - y_decim = value - return true - - "Chart_Style/points_shape": - points_shape = value - return true - "Chart_Style/function_colors": - function_colors = value - return true - "Chart_Style/template": - template = value - apply_template(template) - return true - "Chart_Style/outline_color": - outline_color = value - return true - "Chart_Style/grid_color": - grid_color = value - return true - "Chart_Style/box_color": - box_color = value - return true - "Chart_Style/v_lines_color": - v_lines_color = value - return true - "Chart_Style/h_lines_color": - h_lines_color = value - return true - "Chart_Style/font": - font = value - return true - "Chart_Style/bold_font": - bold_font = value - return true - "Chart_Style/font_color": - font_color = value - apply_template(template) - return true - - "Chart_Modifiers/rotation": - rotation = value - return true - "Chart_Modifiers/invert_chart": - invert_chart = value - return true - -func _ready(): - templates = Utilities._load_templates() - -# .......................... Shared Functions and virtuals ........................ -func plot(): - load_font() - PointData.hide() - - if source == "" or source == null: - Utilities._print_message("Can't plot a chart without a Source file. Please, choose it in editor, or use the custom function _plot().",1) - return - - data = read_datas(source) - structure_datas(data.duplicate(true),are_values_columns,labels_index) - build_chart() - count_functions() - calculate_pass() - calculate_colors() - calculate_coordinates() - set_shapes() - create_legend() - emit_signal("chart_plotted",self) - -func plot_from_csv(csv_file : String, _delimiter : String = delimiter): - load_font() - PointData.hide() - - if csv_file == "" or csv_file == null: - Utilities._print_message("Can't plot a chart without a Source file. Please, choose it in editor, or use the custom function _plot().",1) - return - - data = read_datas(csv_file, _delimiter) - structure_datas(data.duplicate(true),are_values_columns,labels_index) - build_chart() - count_functions() - calculate_pass() - calculate_colors() - calculate_coordinates() - set_shapes() - create_legend() - emit_signal("chart_plotted",self) - -func plot_from_array(array : Array) -> void: - load_font() - PointData.hide() - - if array.empty(): - Utilities._print_message("Can't plot a chart without an empty Array.",1) - return - - data = array - structure_datas(data.duplicate(true),are_values_columns,labels_index) - build_chart() - count_functions() - calculate_pass() - calculate_colors() - calculate_coordinates() - set_shapes() - create_legend() - emit_signal("chart_plotted",self) - - if not is_connected("item_rect_changed",self, "redraw"): connect("item_rect_changed", self, "redraw") - -func plot_from_dataframe(dataframe : DataFrame) -> void: - load_font() - PointData.hide() - - data = dataframe.get_dataset() - if data.empty(): - Utilities._print_message("Can't plot a chart without an empty Array.",1) - return - - structure_datas(data.duplicate(true),are_values_columns,labels_index) - build_chart() - count_functions() - calculate_pass() - calculate_colors() - calculate_coordinates() - set_shapes() - create_legend() - emit_signal("chart_plotted",self) - -# Append new data (in array format) to the already plotted data. -# All data are stored. -func update_plot_data(array : Array) -> void: - if array.empty(): - Utilities._print_message("Can't plot a chart without an empty Array.",1) - return - - data.append(array) - structure_datas(data.duplicate(true),are_values_columns,labels_index) - redraw() - count_functions() - calculate_colors() - set_shapes() - create_legend() - emit_signal("chart_plotted",self) - - update() - -func plot_placeholder() -> void: - pass - -func load_font(): - if font != null: - font_size = font.get_height() - var theme : Theme = Theme.new() - theme.set_default_font(font) - set_theme(theme) - - else: - var lbl = Label.new() - font = lbl.get_font("") - lbl.free() - if bold_font != null: - PointData.Data.set("custom_fonts/font",bold_font) - -func calculate_colors(): - if function_colors.size() <= functions: - for function in range(functions - function_colors.size() + 1): function_colors.append(Color(randf(),randf(), randf())) - -func set_shapes(): - if points_shape.empty() or points_shape.size() < functions: - for function in functions: - points_shape.append(PointShapes.Dot) - -func read_datas(source : String, _delimiter : String = delimiter): - var file : File = File.new() - file.open(source,File.READ) - var content : Array - while not file.eof_reached(): - var line : PoolStringArray = file.get_csv_line(_delimiter) - content.append(line) - file.close() - for data in content: - if data.size() < 2 or data.empty(): - content.erase(data) - return content - -func count_functions(): - if are_values_columns: - if not invert_chart: - functions = data[0].size()-1 - else: - functions = data.size()-1 - else: - if invert_chart: - functions = x_datas.size() - else: - functions = y_datas.size() - -func clear_points(): - if $Points.get_children(): - for function in Points.get_children(): - function.queue_free() - for legend in $Legend.get_children(): - legend.queue_free() - -func redraw(): - build_chart() - calculate_pass() - calculate_coordinates() - update() - -func clean_variables(): - x_datas.clear() - y_datas.clear() - x_label = "" - x_labels.clear() - y_labels.clear() - -# .................. VIRTUAL FUNCTIONS ......................... -func structure_datas(database : Array, are_values_columns : bool, labels_index : int): - pass - -func build_chart(): - pass - -func calculate_pass(): - pass - -func calculate_coordinates(): - pass - -func function_colors(): - pass - -func create_legend(): - legend.clear() - for function in functions: - var function_legend : LegendElement = LegendElement.instance() - var f_name : String = y_labels[function] - var legend_font : Font - if font != null: - legend_font = font - if bold_font != null: - legend_font = bold_font - function_legend.create_legend(f_name,function_colors[function],bold_font,font_color) - legend.append(function_legend) - -# ........................... Shared Setters & Getters .............................. -func apply_template(template_name : int): - if Engine.editor_hint: - set_template(template_name) - property_list_changed_notify() - -# !!! API v2 -func set_chart_name(ch_name : String): - chart_name = ch_name - get_node("ChartName").set_text(chart_name) - -# !!! API v2 -func set_source(source_file : String): - source = source_file - -# !!! API v2 -func set_indexes(lb : int = 0, function_names : int = 0): - labels_index = lb - function_names_index = function_names - -# !!! API v2 -func set_radius(use_height : bool = false, f : float = 0): - use_height_as_radius = use_height - radius = f - -# !!! API v2 -func set_chart_colors(f_colors : PoolColorArray, o_color : Color, b_color : Color, g_color : Color, h_lines : Color, v_lines : Color): - function_colors = f_colors - outline_color = o_color - box_color = b_color - grid_color = g_color - h_lines_color = h_lines - v_lines_color = v_lines - -# !!! API v2 -func set_chart_fonts(normal_font : Font, bold_font : Font, f_color : Color = Color.white): - font = normal_font - self.bold_font = bold_font - font_color = f_color - -# !!! API v2 -func set_delimiter(d : String): - delimiter = d - -# ! API -func set_origin_at_zero(b : bool): - origin_at_zero = b - -# ! API -func set_are_values_columns(b : bool): - are_values_columns = b - -# ! API -func set_show_x_values_as_labels(b : bool): - show_x_values_as_labels = b - -func set_labels_index(i : int): - labels_index = i - -func set_function_names_index(i : int): - function_names_index = i - -func set_use_height_as_radius(b : bool): - use_height_as_radius = b - -func _set_radius(r : float): - radius = r - -func get_radius() -> float: - if use_height_as_radius: return get_size().y/2 - else: return radius - -# ! API -func set_column_width(f : float): - column_width = f - -# ! API -func set_column_gap(f : float): - column_gap = f - -# ! API -func set_full_scale(f : float): - full_scale = f - -# ! API -func set_x_decim(f : float): - x_decim = f - -# ! API -func set_y_decim(f : float): - y_decim = f - -# ! API -func set_points_shape(a : Array): - points_shape = a - - -# ! API -func set_function_colors(a : Array): - function_colors = a - -# ! API -func set_outline_color(c : Color): - outline_color = c - -# ! API -func set_box_color(c : Color): - box_color = c - -# ! API -func set_grid_color(c : Color): - grid_color = c - -# ! API -func set_v_lines_color(c : Color): - v_lines_color = c - -# ! API -func set_h_lines_color(c : Color): - h_lines_color = c - -# ! API -func set_font(f : Font): - font = f - -# ! API -func set_bold_font(f : Font): - bold_font = f - -# ! API -func set_font_color(c : Color): - font_color = c - -# ! API -func set_template(template_name : int): - template = template_name - templates = Utilities.templates - if template_name!=null: - var custom_template = templates.get(templates.keys()[template_name]) - function_colors = custom_template.function_colors as PoolColorArray - outline_color = Color(custom_template.outline_color) - box_color = Color(custom_template.outline_color) - grid_color = Color(custom_template.v_lines_color) - v_lines_color = Color(custom_template.v_lines_color) - h_lines_color = Color(custom_template.h_lines_color) - box_color = Color(custom_template.outline_color) - font_color = Color(custom_template.font_color) - -# ! API -func set_rotation(f : float): - rotation = f - -# ! API -func set_invert_chart(b : bool): - invert_chart = b - -func set_legend(l : Array): - legend = l - -func get_legend() -> Array: - return legend - -# ............................. Shared Signals .............................. -func point_pressed(point : Point): - emit_signal("point_pressed",point) - -func show_data(point : Point): - PointData.update_datas(point) - PointData.show() - -func hide_data(): - PointData.hide() - -func show_slice_data(slice : Slice): - PointData.update_slice_datas(slice) - PointData.show() diff --git a/addons/easy_charts/Utilities/Scripts/Chart2D.gd b/addons/easy_charts/Utilities/Scripts/Chart2D.gd deleted file mode 100644 index e22bb4e..0000000 --- a/addons/easy_charts/Utilities/Scripts/Chart2D.gd +++ /dev/null @@ -1,10 +0,0 @@ -tool -extends Node2D -class_name Chart2D - -enum PointShapes { Dot, Triangle, Square, Cross } -enum TemplatesNames { Default, Clean, Gradient, Minimal, Invert } - -# Called when the node enters the scene tree for the first time. -func _ready(): - pass # Replace with function body. diff --git a/addons/easy_charts/Utilities/Scripts/chart.gd b/addons/easy_charts/Utilities/Scripts/chart.gd deleted file mode 100644 index 435d5c0..0000000 --- a/addons/easy_charts/Utilities/Scripts/chart.gd +++ /dev/null @@ -1,709 +0,0 @@ -extends Control -class_name Chart - -# Classes -enum TYPES { Line, Bar, Scatter, Radar, Pie } - -# Signals .................................. -signal chart_plotted(chart) # emit when a chart is plotted (static) or updated (dynamic) -signal point_pressed(point) - -# Onready Vars ............................ -onready var PointData = $PointData/PointData -onready var Points = $Points -onready var Legend = $Legend -onready var ChartName : Label = $ChartName - -# Scenes and Reosurces ...................... -var point_node : PackedScene = preload("../Point/Point.tscn") -var LegendElement : PackedScene = preload("../Legend/FunctionLegend.tscn") - -# Enums ..................................... -enum PointShapes { Dot, Triangle, Square, Cross } - -# Shared Variables ......................... -var SIZE : Vector2 = Vector2() -var OFFSET : Vector2 = Vector2(0,0) -var origin : Vector2 - -var font_size : float = 16 -var const_height : float = font_size/2*font_size/20 -var const_width : float = font_size/2 - -# actual distance between x and y values -var x_pass : float -var y_pass : float - -# vertical distance between y consecutive points used for intervals -var v_dist : float -var h_dist : float - -# define values on x an y axis -var x_chors : Array -var y_chors : Array - -# actual coordinates of points (in pixel) -var x_coordinates : Array -var y_coordinates : Array - -# data contained in file -var data : Array - -# amount of functions to represent -var functions : int = 0 - -# database values -var x_datas : Array -var y_datas : Array - -# labels displayed on chart -var x_label : String - -var x_labels : Array -var y_labels : Array - -var x_margin_min : int = 0 -var y_margin_min : int = 0 - -# actual values of point, from the database -var point_values : Array - -# actual position of points in pixel -var point_positions : Array - -var legend : Array setget set_legend,get_legend - -# ................... Export Shared Variables .................. -export (String) var chart_name : String = "" setget set_chart_name -export (String, FILE, "*.txt, *.csv") var source : String = "" setget set_source -export (String) var delimiter : String = ";" setget set_delimiter - -var origin_at_zero : bool = true setget set_origin_at_zero#, get_origin_at_zero -var are_values_columns : bool = false setget set_are_values_columns#, get_are_values_columns -var show_x_values_as_labels : bool = true setget set_show_x_values_as_labels#, get_show_x_values_as_labels -var labels_index : int = 0 setget set_labels_index#, get_labels_index -var function_names_index : int = 0 setget set_function_names_index#, get_function_names_index - -# for radar -var use_height_as_radius : bool = false setget set_use_height_as_radius -var radius : float = 150.0 setget _set_radius,get_radius - -# for columns -var column_width : float = 10 setget set_column_width -var column_gap : float = 2 setget set_column_gap - -var full_scale : float = 1.0 setget set_full_scale -var x_decim : float = 5.0 setget set_x_decim -var y_decim : float = 1.0 setget set_y_decim - -var points_shape : Array = [PointShapes.Dot] setget set_points_shape -var function_colors = [Color("#1e1e1e")] setget set_function_colors -var outline_color : Color = Color("#1e1e1e") setget set_outline_color -var box_color : Color = Color("#1e1e1e") setget set_box_color -var v_lines_color : Color = Color("#cacaca") setget set_v_lines_color -var h_lines_color : Color = Color("#cacaca") setget set_h_lines_color -var grid_color : Color = Color("#1e1e1e") setget set_grid_color -var font : Font setget set_font -var bold_font : Font setget set_bold_font -var font_color : Color = Color("#1e1e1e") setget set_font_color - -var use_template : bool = true setget set_use_template -var template : int = 0 setget set_template - -# modifiers -var rotation : float = 0 setget set_rotation -var invert_chart : bool = false setget set_invert_chart - -# Only disp a certain range of values: -# (x , 0) -> Only disp first 'x' values -# (0 , y) -> Only disp last 'y' values -# (x , y) -> Only disp values in range [x, y] -# (0 , 0) -> Disp all values (full range) -var only_disp_values : Vector2 = Vector2(0,0) setget set_only_disp_values - -# A vector representing limit values (both on x and y axis) you want to stay over or below -# The treshold value is always relative to your dataset values -# ex. if your dataset is [ [100,100], [300,300] ] a proper treshold would be (200,200) -var treshold : Vector2 setget set_treshold - -# A vector representing @treshold coordinates in its relative chart -# only used to draw treshold values -var treshold_draw : Vector2 - -# !! API v2 -static func instance(chart_type : int): - var chart_t : String = Utilities.get_chart_type(chart_type) - var chart : String = "res://addons/easy_charts/%s/%s.tscn" % [chart_t, chart_t] - return load(chart).instance() - -# .......................... Properties Manager .................................... -func _get(property): - match property: - "Chart_Properties/origin_at_zero": - return origin_at_zero - "Chart_Properties/are_values_columns": - return are_values_columns - "Chart_Properties/show_x_values_as_labels": - return show_x_values_as_labels - "Chart_Properties/labels_index": - return labels_index - "Chart_Properties/function_names_index": - return function_names_index - "Chart_Properties/use_height_as_radius": - return use_height_as_radius - "Chart_Properties/radius": - return radius - "Chart_Properties/column_width": - return column_width - "Chart_Properties/column_gap": - return column_gap - - "Chart_Display/full_scale": - return full_scale - "Chart_Display/x_decim": - return x_decim - "Chart_Display/y_decim": - return y_decim - - "Chart_Style/points_shape": - return points_shape - "Chart_Style/function_colors": - return function_colors - "Chart_Style/template": - return template - "Chart_Style/use_template": - return use_template - "Chart_Style/outline_color": - return outline_color - "Chart_Style/grid_color": - return grid_color - "Chart_Style/box_color": - return box_color - "Chart_Style/v_lines_color": - return v_lines_color - "Chart_Style/h_lines_color": - return h_lines_color - "Chart_Style/font": - return font - "Chart_Style/bold_font": - return bold_font - "Chart_Style/font_color": - return font_color - - "Chart_Modifiers/treshold": - return treshold - "Chart_Modifiers/only_disp_values": - return only_disp_values - "Chart_Modifiers/rotation": - return rotation - "Chart_Modifiers/invert_chart": - return invert_chart - -func _set(property, value): - match property: - "Chart_Properties/origin_at_zero": - origin_at_zero = value - return true - "Chart_Properties/are_values_columns": - are_values_columns = value - return true - "Chart_Properties/show_x_values_as_labels": - show_x_values_as_labels = value - return true - "Chart_Properties/labels_index": - labels_index = value - return true - "Chart_Properties/function_names_index": - function_names_index = value - return true - "Chart_Properties/use_height_as_radius": - use_height_as_radius = value - return true - "Chart_Properties/radius": - radius = value - return true - "Chart_Properties/column_width": - column_width = value - return true - "Chart_Properties/column_gap": - column_gap = value - return true - - "Chart_Display/full_scale": - full_scale = value - return true - "Chart_Display/x_decim": - x_decim = value - return true - "Chart_Display/y_decim": - y_decim = value - return true - - "Chart_Style/points_shape": - points_shape = value - return true - "Chart_Style/function_colors": - function_colors = value - return true - "Chart_Style/use_template": - use_template = value - return true - "Chart_Style/template": - template = value - apply_template(template) - return true - "Chart_Style/outline_color": - outline_color = value - return true - "Chart_Style/grid_color": - grid_color = value - return true - "Chart_Style/box_color": - box_color = value - return true - "Chart_Style/v_lines_color": - v_lines_color = value - return true - "Chart_Style/h_lines_color": - h_lines_color = value - return true - "Chart_Style/font": - font = value - return true - "Chart_Style/bold_font": - bold_font = value - return true - "Chart_Style/font_color": - font_color = value -# apply_template(template) - return true - - "Chart_Modifiers/treshold": - treshold = value - return true - "Chart_Modifiers/only_disp_values": - only_disp_values = value - return true - "Chart_Modifiers/rotation": - rotation = value - return true - "Chart_Modifiers/invert_chart": - invert_chart = value - return true - -# .......................... Shared Functions and virtuals ........................ -func slice_data() -> Array: - var data_to_display : Array - data_to_display.resize(data.size()) - if only_disp_values == Vector2(0,0) : - data_to_display = data.duplicate(true) - elif only_disp_values.x == 0 and only_disp_values.y < data[0].size(): - for row_idx in data.size(): - data_to_display[row_idx] = [data[row_idx][0]] + data[row_idx].slice(data[row_idx].size()-only_disp_values.y, data[row_idx].size()-1) - elif only_disp_values.y == 0 and only_disp_values.x < data[0].size(): - for row_idx in data.size(): - data_to_display[row_idx] = [data[row_idx][0]] + data[row_idx].slice(1, only_disp_values.x) - elif only_disp_values.x != 0 and only_disp_values.y != 0 and only_disp_values.y < data[0].size() and only_disp_values.x < data[0].size(): - for row_idx in data.size(): - data_to_display[row_idx] = [data[row_idx][0]] + data[row_idx].slice(only_disp_values.x, data[row_idx].size()-only_disp_values.y) - else: - data_to_display = data.duplicate(true) - return data_to_display - -func plot(): - load_font() - PointData.hide() - - if source == "" or source == null: - Utilities._print_message("Can't plot a chart without a Source file. Please, choose it in editor, or use the custom function _plot().",1) - return - - - data = read_datas(source) - structure_datas(slice_data()) - build_chart() - count_functions() - calculate_pass() - calculate_colors() - calculate_coordinates() - set_shapes() - create_legend() - emit_signal("chart_plotted",self) - -func plot_from_csv(csv_file : String, _delimiter : String = delimiter): - load_font() - PointData.hide() - - if csv_file == "" or csv_file == null: - Utilities._print_message("Can't plot a chart without a Source file. Please, choose it in editor, or use the custom function _plot().",1) - return - - data = read_datas(csv_file, _delimiter) - structure_datas(slice_data()) - build_chart() - count_functions() - calculate_pass() - calculate_colors() - calculate_coordinates() - set_shapes() - create_legend() - emit_signal("chart_plotted",self) - -func plot_from_array(array : Array) -> void: - clean_variables() - clear_points() - load_font() - PointData.hide() - - if array.empty(): - Utilities._print_message("Can't plot a chart with an empty Array.",1) - return - - data = array.duplicate() - structure_datas(slice_data()) - build_chart() - count_functions() - calculate_pass() - calculate_colors() - calculate_coordinates() - set_shapes() - create_legend() - emit_signal("chart_plotted",self) - - if not is_connected("item_rect_changed",self, "redraw"): connect("item_rect_changed", self, "redraw") - -func plot_from_dataframe(dataframe : DataFrame) -> void: - clean_variables() - clear_points() - load_font() - load_font() - PointData.hide() - - data = dataframe.get_dataframe().duplicate(true) - - if data.empty(): - Utilities._print_message("Can't plot a chart with an empty Array.",1) - return - - structure_datas(slice_data()) - build_chart() - count_functions() - calculate_pass() - calculate_colors() - calculate_coordinates() - set_shapes() - create_legend() - emit_signal("chart_plotted",self) - - if not is_connected("item_rect_changed",self, "redraw"): connect("item_rect_changed", self, "redraw") - -# Append new data (in array format) to the already plotted data. -# All data are stored. -func update_plot_data(array : Array) -> void: - if array.empty(): - Utilities._print_message("Can't plot a chart with an empty Array.",1) - return - - data.append(array) - structure_datas(slice_data()) - redraw() - count_functions() - calculate_colors() - set_shapes() - create_legend() - emit_signal("chart_plotted",self) - - update() - -# Append a new column to data -func append_new_column(dataset : Array, column : Array): - if column.empty(): - Utilities._print_message("Can't update plot with an empty row.",1) - return - for value_idx in column.size(): - dataset[value_idx].append(column[value_idx]) - -func plot_placeholder() -> void: - pass - -func load_font(): - if font != null: - font_size = font.get_height() - var theme : Theme = Theme.new() - theme.set_default_font(font) - set_theme(theme) - - else: - var lbl = Label.new() - font = lbl.get_font("") - lbl.free() - if bold_font != null: - PointData.Data.set("custom_fonts/font",bold_font) - -func calculate_colors(): - if function_colors.size() < functions: - for function in range(functions - function_colors.size() + 1): function_colors.append(Color(randf(),randf(), randf())) - -func set_shapes(): - if points_shape.empty() or points_shape.size() < functions: - for function in functions: - points_shape.append(PointShapes.Dot) - -func read_datas(source : String, _delimiter : String = delimiter): - var file : File = File.new() - file.open(source,File.READ) - var content : Array - while not file.eof_reached(): - var line : PoolStringArray = file.get_csv_line(_delimiter) - content.append(line) - file.close() - for data in content: - if data.size() < 2 or data.empty(): - content.erase(data) - return content - -func count_functions(): - if are_values_columns: functions = data[0].size()-1 - else: functions = y_datas.size() - -func clear_points(): - if $Points.get_children(): - for function in Points.get_children(): - function.queue_free() - for legend in $Legend.get_children(): - legend.queue_free() - -func redraw(): - build_chart() - calculate_pass() - calculate_coordinates() - update() - -func clean_variables(): - x_chors.clear() - y_chors.clear() - x_datas.clear() - y_datas.clear() - x_label = "" - x_labels.clear() - y_labels.clear() - -# .................. VIRTUAL FUNCTIONS ......................... -func structure_datas(database : Array): - pass - -func build_chart(): - pass - -func calculate_pass(): - pass - -func calculate_coordinates(): - pass - -func function_colors(): - pass - -func create_legend(): - for function in functions: - var function_legend : LegendElement - if legend.size() > function: - function_legend = legend[function] - else: - function_legend = LegendElement.instance() - legend.append(function_legend) - var f_name : String = y_labels[function] if not are_values_columns else str(x_datas[function]) - var legend_font : Font - if font != null: - legend_font = font - if bold_font != null: - legend_font = bold_font - function_legend.create_legend(f_name,function_colors[function],bold_font,font_color) - -# ........................... Shared Setters & Getters .............................. -func apply_template(template_name : int): - if Engine.editor_hint: - set_template(template_name) - property_list_changed_notify() - -# !!! API v2 -func set_chart_name(ch_name : String): - chart_name = ch_name - get_node("ChartName").set_text(chart_name) - -# !!! API v2 -func set_source(source_file : String): - source = source_file - -# !!! API v2 -func set_indexes(lb : int = 0, function_names : int = 0): - labels_index = lb - function_names_index = function_names - -# !!! API v2 -func set_radius(use_height : bool = false, f : float = 0): - use_height_as_radius = use_height - radius = f - -# !!! API v2 -func set_chart_colors(f_colors : PoolColorArray, o_color : Color, b_color : Color, g_color : Color, h_lines : Color, v_lines : Color): - function_colors = f_colors - outline_color = o_color - box_color = b_color - grid_color = g_color - h_lines_color = h_lines - v_lines_color = v_lines - -# !!! API v2 -func set_chart_fonts(normal_font : Font, bold_font : Font, f_color : Color = Color.white): - font = normal_font - self.bold_font = bold_font - font_color = f_color - -# !!! API v2 -func set_delimiter(d : String): - delimiter = d - -# ! API -func set_origin_at_zero(b : bool): - origin_at_zero = b - -# ! API -func set_are_values_columns(b : bool): - are_values_columns = b - -# ! API -func set_show_x_values_as_labels(b : bool): - show_x_values_as_labels = b - -func set_labels_index(i : int): - labels_index = i - -func set_function_names_index(i : int): - function_names_index = i - -func set_use_height_as_radius(b : bool): - use_height_as_radius = b - -func _set_radius(r : float): - radius = r - -func get_radius() -> float: - if use_height_as_radius: return get_size().y/2 - else: return radius - -# ! API -func set_column_width(f : float): - column_width = f - -# ! API -func set_column_gap(f : float): - column_gap = f - -# ! API -func set_full_scale(f : float): - full_scale = f - -# ! API -func set_x_decim(f : float): - x_decim = f - -# ! API -func set_y_decim(f : float): - y_decim = f - -# ! API -func set_points_shape(a : Array): - points_shape = a - - -# ! API -func set_function_colors(a : PoolColorArray): - function_colors = a - -# ! API -func set_outline_color(c : Color): - outline_color = c - -# ! API -func set_box_color(c : Color): - box_color = c - -# ! API -func set_grid_color(c : Color): - grid_color = c - -# ! API -func set_v_lines_color(c : Color): - v_lines_color = c - -# ! API -func set_h_lines_color(c : Color): - h_lines_color = c - -# ! API -func set_font(f : Font): - font = f - -# ! API -func set_bold_font(f : Font): - bold_font = f - -# ! API -func set_font_color(c : Color): - font_color = c - -func set_use_template(use : bool): - use_template = use - -# ! API -func set_template(template_name : int): - if not use_template: return - template = template_name - if template_name!=null: - var custom_template = Utilities.templates.get(Utilities.templates.keys()[template_name]) - function_colors = custom_template.function_colors as PoolColorArray - outline_color = Color(custom_template.outline_color) - box_color = Color(custom_template.outline_color) - grid_color = Color(custom_template.v_lines_color) - v_lines_color = Color(custom_template.v_lines_color) - h_lines_color = Color(custom_template.h_lines_color) - box_color = Color(custom_template.outline_color) - font_color = Color(custom_template.font_color) - -# ! API -func set_rotation(f : float): - rotation = f - -# ! API -func set_invert_chart(b : bool): - invert_chart = b - -# ! API -func set_treshold(t : Vector2): - treshold = t - -# ! API -func set_only_disp_values(v : Vector2): - only_disp_values = v - -func set_legend(l : Array): - legend = l - -func get_legend() -> Array: - return legend - -# ............................. Shared Signals .............................. -func point_pressed(point : Point): - emit_signal("point_pressed",point) - -func show_data(point : Point): - PointData.update_datas(point) - PointData.show() - -func hide_data(): - PointData.hide() - -func show_slice_data(slice : Slice): - PointData.update_slice_datas(slice) - PointData.show() diff --git a/addons/easy_charts/Utilities/Scripts/chart2d.gd b/addons/easy_charts/Utilities/Scripts/chart2d.gd deleted file mode 100644 index 27f6f65..0000000 --- a/addons/easy_charts/Utilities/Scripts/chart2d.gd +++ /dev/null @@ -1,211 +0,0 @@ -tool -extends Node2D -class_name Chart2D - -enum PointShapes { Dot, Triangle, Square, Cross } -enum TemplatesNames { Default, Clean, Gradient, Minimal, Invert } - - -signal chart_plotted(chart) -signal point_pressed(point) - - -const OFFSET: Vector2 = Vector2(0,0) - - -export (Vector2) var SIZE: Vector2 = Vector2() setget _set_size -export (String, FILE, "*.txt, *.csv") var source: String = "" -export (String) var delimiter: String = ";" -export (bool) var origin_at_zero: bool = true - -export (bool) var are_values_columns: bool = false -export (int, 0, 100) var x_values_index: int = 0 -export(bool) var show_x_values_as_labels: bool = true - -export (float,1,20,0.5) var column_width: float = 10 -export (float,0,10,0.5) var column_gap: float = 2 - -export (float, 0.1, 10.0) var x_decim: float = 5.0 -export (float, 0.1, 10.0) var y_decim: float = 5.0 -export (PointShapes) var point_shape: int = 0 -export (PoolColorArray) var function_colors = [Color("#1e1e1e")] -export (Color) var v_lines_color: Color = Color("#cacaca") -export (Color) var h_lines_color: Color = Color("#cacaca") - -export (bool) var boxed: bool = true -export (Color) var box_color: Color = Color("#1e1e1e") -export (Font) var font: Font -export (Font) var bold_font: Font -export (Color) var font_color: Color = Color("#1e1e1e") -export var template : int = 0 setget apply_template -export (float, 0.1, 1) var drawing_duration: float = 0.5 -export (bool) var invert_chart: bool = false - - -var OutlinesTween: Tween -var FunctionsTween: Tween -var PointTween : Tween -var Functions: Node2D -var GridTween: Tween -var PointData: PointData -var Outlines: Line2D -var Grid: Node2D - -var point_node: PackedScene = preload("../Point/Point.tscn") -var FunctionLegend: PackedScene = preload("../Legend/FunctionLegend.tscn") - -var font_size: float = 16 -var const_height: float = font_size / 2 * font_size / 20 -var const_width: float = font_size / 2 - -var origin: Vector2 - -# actual distance between x and y values -var x_pass: float -var y_pass: float - -# vertical distance between y consecutive points used for intervals -var v_dist: float -var h_dist: float - -# quantization, representing the interval in which values will be displayed - -# define values on x an y axis -var x_chors: Array -var y_chors: Array - -# actual coordinates of points (in pixel) -var x_coordinates: Array -var y_coordinates: Array - -# datas contained in file -var datas: Array - -# amount of functions to represent -var functions: int = 0 - -var x_label: String - -# database values -var x_datas: Array -var y_datas: Array - -# labels displayed on chart -var x_labels: Array -var y_labels: Array - -var x_margin_min: int = 0 -var y_margin_min: int = 0 - -# actual values of point, from the database -var point_values: Array - -# actual position of points in pixel -var point_positions: Array - -var legend: Array setget set_legend, get_legend - -var templates: Dictionary = {} - - -# Called when the node enters the scene tree for the first time. -func _ready(): - pass # Replace with function body. - -func build_chart(): - pass - -func calculate_pass(): - pass - -func calculate_coordinates(): - pass - -func _set_size(v : Vector2): - SIZE = v - -func _get_children(): - OutlinesTween = $OutlinesTween - FunctionsTween = $FunctionsTween - Functions = $Functions - GridTween = $GridTween - PointData = $PointData/PointData - Outlines = $Outlines - Grid = $Grid - -func apply_template(template_name: int): - template = template_name - templates = Utilities._load_templates() - if template_name != null: - var custom_template = templates.get(templates.keys()[template_name]) - function_colors = custom_template.function_colors as PoolColorArray - v_lines_color = Color(custom_template.v_lines_color) - h_lines_color = Color(custom_template.h_lines_color) - box_color = Color(custom_template.outline_color) - font_color = Color(custom_template.font_color) - property_list_changed_notify() - - if Engine.editor_hint: - _get_children() - Outlines.set_default_color(box_color) - Grid.get_node("VLine").set_default_color(v_lines_color) - Grid.get_node("HLine").set_default_color(h_lines_color) - -func redraw(): - build_chart() - calculate_pass() - calculate_coordinates() - update() - - -func show_data(point): - PointData.update_datas(point) - PointData.show() - - -func hide_data(): - PointData.hide() - - -func clear_points(): - function_colors.clear() - if Functions.get_children(): - for function in Functions.get_children(): - function.queue_free() - -func count_functions(): - if are_values_columns: - if not invert_chart: - functions = datas[0].size() - 1 - else: - functions = datas.size() - 1 - else: - if invert_chart: - functions = datas[0].size() - 1 - else: - functions = datas.size() - 1 - -func set_legend(l: Array): - legend = l - - -func get_legend(): - return legend - - -func create_legend(): - legend.clear() - for function in functions: - var function_legend = FunctionLegend.instance() - var f_name: String - if invert_chart: - f_name = x_datas[function] as String - else: - f_name = y_labels[function] - var legend_font: Font - if font != null: - legend_font = font - if bold_font != null: - legend_font = bold_font - function_legend.create_legend(f_name, function_colors[function], bold_font, font_color) - legend.append(function_legend) From 8646fcb6c617a10fce5a526b95a7c43210d1355c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Santilio?= Date: Thu, 31 Dec 2020 13:58:25 +0100 Subject: [PATCH 06/13] @structure_data changed --- addons/easy_charts/Utilities/Scripts/chart.gd | 709 ++++++++++++++++++ .../easy_charts/Utilities/Scripts/chart2d.gd | 211 ++++++ 2 files changed, 920 insertions(+) create mode 100644 addons/easy_charts/Utilities/Scripts/chart.gd create mode 100644 addons/easy_charts/Utilities/Scripts/chart2d.gd diff --git a/addons/easy_charts/Utilities/Scripts/chart.gd b/addons/easy_charts/Utilities/Scripts/chart.gd new file mode 100644 index 0000000..435d5c0 --- /dev/null +++ b/addons/easy_charts/Utilities/Scripts/chart.gd @@ -0,0 +1,709 @@ +extends Control +class_name Chart + +# Classes +enum TYPES { Line, Bar, Scatter, Radar, Pie } + +# Signals .................................. +signal chart_plotted(chart) # emit when a chart is plotted (static) or updated (dynamic) +signal point_pressed(point) + +# Onready Vars ............................ +onready var PointData = $PointData/PointData +onready var Points = $Points +onready var Legend = $Legend +onready var ChartName : Label = $ChartName + +# Scenes and Reosurces ...................... +var point_node : PackedScene = preload("../Point/Point.tscn") +var LegendElement : PackedScene = preload("../Legend/FunctionLegend.tscn") + +# Enums ..................................... +enum PointShapes { Dot, Triangle, Square, Cross } + +# Shared Variables ......................... +var SIZE : Vector2 = Vector2() +var OFFSET : Vector2 = Vector2(0,0) +var origin : Vector2 + +var font_size : float = 16 +var const_height : float = font_size/2*font_size/20 +var const_width : float = font_size/2 + +# actual distance between x and y values +var x_pass : float +var y_pass : float + +# vertical distance between y consecutive points used for intervals +var v_dist : float +var h_dist : float + +# define values on x an y axis +var x_chors : Array +var y_chors : Array + +# actual coordinates of points (in pixel) +var x_coordinates : Array +var y_coordinates : Array + +# data contained in file +var data : Array + +# amount of functions to represent +var functions : int = 0 + +# database values +var x_datas : Array +var y_datas : Array + +# labels displayed on chart +var x_label : String + +var x_labels : Array +var y_labels : Array + +var x_margin_min : int = 0 +var y_margin_min : int = 0 + +# actual values of point, from the database +var point_values : Array + +# actual position of points in pixel +var point_positions : Array + +var legend : Array setget set_legend,get_legend + +# ................... Export Shared Variables .................. +export (String) var chart_name : String = "" setget set_chart_name +export (String, FILE, "*.txt, *.csv") var source : String = "" setget set_source +export (String) var delimiter : String = ";" setget set_delimiter + +var origin_at_zero : bool = true setget set_origin_at_zero#, get_origin_at_zero +var are_values_columns : bool = false setget set_are_values_columns#, get_are_values_columns +var show_x_values_as_labels : bool = true setget set_show_x_values_as_labels#, get_show_x_values_as_labels +var labels_index : int = 0 setget set_labels_index#, get_labels_index +var function_names_index : int = 0 setget set_function_names_index#, get_function_names_index + +# for radar +var use_height_as_radius : bool = false setget set_use_height_as_radius +var radius : float = 150.0 setget _set_radius,get_radius + +# for columns +var column_width : float = 10 setget set_column_width +var column_gap : float = 2 setget set_column_gap + +var full_scale : float = 1.0 setget set_full_scale +var x_decim : float = 5.0 setget set_x_decim +var y_decim : float = 1.0 setget set_y_decim + +var points_shape : Array = [PointShapes.Dot] setget set_points_shape +var function_colors = [Color("#1e1e1e")] setget set_function_colors +var outline_color : Color = Color("#1e1e1e") setget set_outline_color +var box_color : Color = Color("#1e1e1e") setget set_box_color +var v_lines_color : Color = Color("#cacaca") setget set_v_lines_color +var h_lines_color : Color = Color("#cacaca") setget set_h_lines_color +var grid_color : Color = Color("#1e1e1e") setget set_grid_color +var font : Font setget set_font +var bold_font : Font setget set_bold_font +var font_color : Color = Color("#1e1e1e") setget set_font_color + +var use_template : bool = true setget set_use_template +var template : int = 0 setget set_template + +# modifiers +var rotation : float = 0 setget set_rotation +var invert_chart : bool = false setget set_invert_chart + +# Only disp a certain range of values: +# (x , 0) -> Only disp first 'x' values +# (0 , y) -> Only disp last 'y' values +# (x , y) -> Only disp values in range [x, y] +# (0 , 0) -> Disp all values (full range) +var only_disp_values : Vector2 = Vector2(0,0) setget set_only_disp_values + +# A vector representing limit values (both on x and y axis) you want to stay over or below +# The treshold value is always relative to your dataset values +# ex. if your dataset is [ [100,100], [300,300] ] a proper treshold would be (200,200) +var treshold : Vector2 setget set_treshold + +# A vector representing @treshold coordinates in its relative chart +# only used to draw treshold values +var treshold_draw : Vector2 + +# !! API v2 +static func instance(chart_type : int): + var chart_t : String = Utilities.get_chart_type(chart_type) + var chart : String = "res://addons/easy_charts/%s/%s.tscn" % [chart_t, chart_t] + return load(chart).instance() + +# .......................... Properties Manager .................................... +func _get(property): + match property: + "Chart_Properties/origin_at_zero": + return origin_at_zero + "Chart_Properties/are_values_columns": + return are_values_columns + "Chart_Properties/show_x_values_as_labels": + return show_x_values_as_labels + "Chart_Properties/labels_index": + return labels_index + "Chart_Properties/function_names_index": + return function_names_index + "Chart_Properties/use_height_as_radius": + return use_height_as_radius + "Chart_Properties/radius": + return radius + "Chart_Properties/column_width": + return column_width + "Chart_Properties/column_gap": + return column_gap + + "Chart_Display/full_scale": + return full_scale + "Chart_Display/x_decim": + return x_decim + "Chart_Display/y_decim": + return y_decim + + "Chart_Style/points_shape": + return points_shape + "Chart_Style/function_colors": + return function_colors + "Chart_Style/template": + return template + "Chart_Style/use_template": + return use_template + "Chart_Style/outline_color": + return outline_color + "Chart_Style/grid_color": + return grid_color + "Chart_Style/box_color": + return box_color + "Chart_Style/v_lines_color": + return v_lines_color + "Chart_Style/h_lines_color": + return h_lines_color + "Chart_Style/font": + return font + "Chart_Style/bold_font": + return bold_font + "Chart_Style/font_color": + return font_color + + "Chart_Modifiers/treshold": + return treshold + "Chart_Modifiers/only_disp_values": + return only_disp_values + "Chart_Modifiers/rotation": + return rotation + "Chart_Modifiers/invert_chart": + return invert_chart + +func _set(property, value): + match property: + "Chart_Properties/origin_at_zero": + origin_at_zero = value + return true + "Chart_Properties/are_values_columns": + are_values_columns = value + return true + "Chart_Properties/show_x_values_as_labels": + show_x_values_as_labels = value + return true + "Chart_Properties/labels_index": + labels_index = value + return true + "Chart_Properties/function_names_index": + function_names_index = value + return true + "Chart_Properties/use_height_as_radius": + use_height_as_radius = value + return true + "Chart_Properties/radius": + radius = value + return true + "Chart_Properties/column_width": + column_width = value + return true + "Chart_Properties/column_gap": + column_gap = value + return true + + "Chart_Display/full_scale": + full_scale = value + return true + "Chart_Display/x_decim": + x_decim = value + return true + "Chart_Display/y_decim": + y_decim = value + return true + + "Chart_Style/points_shape": + points_shape = value + return true + "Chart_Style/function_colors": + function_colors = value + return true + "Chart_Style/use_template": + use_template = value + return true + "Chart_Style/template": + template = value + apply_template(template) + return true + "Chart_Style/outline_color": + outline_color = value + return true + "Chart_Style/grid_color": + grid_color = value + return true + "Chart_Style/box_color": + box_color = value + return true + "Chart_Style/v_lines_color": + v_lines_color = value + return true + "Chart_Style/h_lines_color": + h_lines_color = value + return true + "Chart_Style/font": + font = value + return true + "Chart_Style/bold_font": + bold_font = value + return true + "Chart_Style/font_color": + font_color = value +# apply_template(template) + return true + + "Chart_Modifiers/treshold": + treshold = value + return true + "Chart_Modifiers/only_disp_values": + only_disp_values = value + return true + "Chart_Modifiers/rotation": + rotation = value + return true + "Chart_Modifiers/invert_chart": + invert_chart = value + return true + +# .......................... Shared Functions and virtuals ........................ +func slice_data() -> Array: + var data_to_display : Array + data_to_display.resize(data.size()) + if only_disp_values == Vector2(0,0) : + data_to_display = data.duplicate(true) + elif only_disp_values.x == 0 and only_disp_values.y < data[0].size(): + for row_idx in data.size(): + data_to_display[row_idx] = [data[row_idx][0]] + data[row_idx].slice(data[row_idx].size()-only_disp_values.y, data[row_idx].size()-1) + elif only_disp_values.y == 0 and only_disp_values.x < data[0].size(): + for row_idx in data.size(): + data_to_display[row_idx] = [data[row_idx][0]] + data[row_idx].slice(1, only_disp_values.x) + elif only_disp_values.x != 0 and only_disp_values.y != 0 and only_disp_values.y < data[0].size() and only_disp_values.x < data[0].size(): + for row_idx in data.size(): + data_to_display[row_idx] = [data[row_idx][0]] + data[row_idx].slice(only_disp_values.x, data[row_idx].size()-only_disp_values.y) + else: + data_to_display = data.duplicate(true) + return data_to_display + +func plot(): + load_font() + PointData.hide() + + if source == "" or source == null: + Utilities._print_message("Can't plot a chart without a Source file. Please, choose it in editor, or use the custom function _plot().",1) + return + + + data = read_datas(source) + structure_datas(slice_data()) + build_chart() + count_functions() + calculate_pass() + calculate_colors() + calculate_coordinates() + set_shapes() + create_legend() + emit_signal("chart_plotted",self) + +func plot_from_csv(csv_file : String, _delimiter : String = delimiter): + load_font() + PointData.hide() + + if csv_file == "" or csv_file == null: + Utilities._print_message("Can't plot a chart without a Source file. Please, choose it in editor, or use the custom function _plot().",1) + return + + data = read_datas(csv_file, _delimiter) + structure_datas(slice_data()) + build_chart() + count_functions() + calculate_pass() + calculate_colors() + calculate_coordinates() + set_shapes() + create_legend() + emit_signal("chart_plotted",self) + +func plot_from_array(array : Array) -> void: + clean_variables() + clear_points() + load_font() + PointData.hide() + + if array.empty(): + Utilities._print_message("Can't plot a chart with an empty Array.",1) + return + + data = array.duplicate() + structure_datas(slice_data()) + build_chart() + count_functions() + calculate_pass() + calculate_colors() + calculate_coordinates() + set_shapes() + create_legend() + emit_signal("chart_plotted",self) + + if not is_connected("item_rect_changed",self, "redraw"): connect("item_rect_changed", self, "redraw") + +func plot_from_dataframe(dataframe : DataFrame) -> void: + clean_variables() + clear_points() + load_font() + load_font() + PointData.hide() + + data = dataframe.get_dataframe().duplicate(true) + + if data.empty(): + Utilities._print_message("Can't plot a chart with an empty Array.",1) + return + + structure_datas(slice_data()) + build_chart() + count_functions() + calculate_pass() + calculate_colors() + calculate_coordinates() + set_shapes() + create_legend() + emit_signal("chart_plotted",self) + + if not is_connected("item_rect_changed",self, "redraw"): connect("item_rect_changed", self, "redraw") + +# Append new data (in array format) to the already plotted data. +# All data are stored. +func update_plot_data(array : Array) -> void: + if array.empty(): + Utilities._print_message("Can't plot a chart with an empty Array.",1) + return + + data.append(array) + structure_datas(slice_data()) + redraw() + count_functions() + calculate_colors() + set_shapes() + create_legend() + emit_signal("chart_plotted",self) + + update() + +# Append a new column to data +func append_new_column(dataset : Array, column : Array): + if column.empty(): + Utilities._print_message("Can't update plot with an empty row.",1) + return + for value_idx in column.size(): + dataset[value_idx].append(column[value_idx]) + +func plot_placeholder() -> void: + pass + +func load_font(): + if font != null: + font_size = font.get_height() + var theme : Theme = Theme.new() + theme.set_default_font(font) + set_theme(theme) + + else: + var lbl = Label.new() + font = lbl.get_font("") + lbl.free() + if bold_font != null: + PointData.Data.set("custom_fonts/font",bold_font) + +func calculate_colors(): + if function_colors.size() < functions: + for function in range(functions - function_colors.size() + 1): function_colors.append(Color(randf(),randf(), randf())) + +func set_shapes(): + if points_shape.empty() or points_shape.size() < functions: + for function in functions: + points_shape.append(PointShapes.Dot) + +func read_datas(source : String, _delimiter : String = delimiter): + var file : File = File.new() + file.open(source,File.READ) + var content : Array + while not file.eof_reached(): + var line : PoolStringArray = file.get_csv_line(_delimiter) + content.append(line) + file.close() + for data in content: + if data.size() < 2 or data.empty(): + content.erase(data) + return content + +func count_functions(): + if are_values_columns: functions = data[0].size()-1 + else: functions = y_datas.size() + +func clear_points(): + if $Points.get_children(): + for function in Points.get_children(): + function.queue_free() + for legend in $Legend.get_children(): + legend.queue_free() + +func redraw(): + build_chart() + calculate_pass() + calculate_coordinates() + update() + +func clean_variables(): + x_chors.clear() + y_chors.clear() + x_datas.clear() + y_datas.clear() + x_label = "" + x_labels.clear() + y_labels.clear() + +# .................. VIRTUAL FUNCTIONS ......................... +func structure_datas(database : Array): + pass + +func build_chart(): + pass + +func calculate_pass(): + pass + +func calculate_coordinates(): + pass + +func function_colors(): + pass + +func create_legend(): + for function in functions: + var function_legend : LegendElement + if legend.size() > function: + function_legend = legend[function] + else: + function_legend = LegendElement.instance() + legend.append(function_legend) + var f_name : String = y_labels[function] if not are_values_columns else str(x_datas[function]) + var legend_font : Font + if font != null: + legend_font = font + if bold_font != null: + legend_font = bold_font + function_legend.create_legend(f_name,function_colors[function],bold_font,font_color) + +# ........................... Shared Setters & Getters .............................. +func apply_template(template_name : int): + if Engine.editor_hint: + set_template(template_name) + property_list_changed_notify() + +# !!! API v2 +func set_chart_name(ch_name : String): + chart_name = ch_name + get_node("ChartName").set_text(chart_name) + +# !!! API v2 +func set_source(source_file : String): + source = source_file + +# !!! API v2 +func set_indexes(lb : int = 0, function_names : int = 0): + labels_index = lb + function_names_index = function_names + +# !!! API v2 +func set_radius(use_height : bool = false, f : float = 0): + use_height_as_radius = use_height + radius = f + +# !!! API v2 +func set_chart_colors(f_colors : PoolColorArray, o_color : Color, b_color : Color, g_color : Color, h_lines : Color, v_lines : Color): + function_colors = f_colors + outline_color = o_color + box_color = b_color + grid_color = g_color + h_lines_color = h_lines + v_lines_color = v_lines + +# !!! API v2 +func set_chart_fonts(normal_font : Font, bold_font : Font, f_color : Color = Color.white): + font = normal_font + self.bold_font = bold_font + font_color = f_color + +# !!! API v2 +func set_delimiter(d : String): + delimiter = d + +# ! API +func set_origin_at_zero(b : bool): + origin_at_zero = b + +# ! API +func set_are_values_columns(b : bool): + are_values_columns = b + +# ! API +func set_show_x_values_as_labels(b : bool): + show_x_values_as_labels = b + +func set_labels_index(i : int): + labels_index = i + +func set_function_names_index(i : int): + function_names_index = i + +func set_use_height_as_radius(b : bool): + use_height_as_radius = b + +func _set_radius(r : float): + radius = r + +func get_radius() -> float: + if use_height_as_radius: return get_size().y/2 + else: return radius + +# ! API +func set_column_width(f : float): + column_width = f + +# ! API +func set_column_gap(f : float): + column_gap = f + +# ! API +func set_full_scale(f : float): + full_scale = f + +# ! API +func set_x_decim(f : float): + x_decim = f + +# ! API +func set_y_decim(f : float): + y_decim = f + +# ! API +func set_points_shape(a : Array): + points_shape = a + + +# ! API +func set_function_colors(a : PoolColorArray): + function_colors = a + +# ! API +func set_outline_color(c : Color): + outline_color = c + +# ! API +func set_box_color(c : Color): + box_color = c + +# ! API +func set_grid_color(c : Color): + grid_color = c + +# ! API +func set_v_lines_color(c : Color): + v_lines_color = c + +# ! API +func set_h_lines_color(c : Color): + h_lines_color = c + +# ! API +func set_font(f : Font): + font = f + +# ! API +func set_bold_font(f : Font): + bold_font = f + +# ! API +func set_font_color(c : Color): + font_color = c + +func set_use_template(use : bool): + use_template = use + +# ! API +func set_template(template_name : int): + if not use_template: return + template = template_name + if template_name!=null: + var custom_template = Utilities.templates.get(Utilities.templates.keys()[template_name]) + function_colors = custom_template.function_colors as PoolColorArray + outline_color = Color(custom_template.outline_color) + box_color = Color(custom_template.outline_color) + grid_color = Color(custom_template.v_lines_color) + v_lines_color = Color(custom_template.v_lines_color) + h_lines_color = Color(custom_template.h_lines_color) + box_color = Color(custom_template.outline_color) + font_color = Color(custom_template.font_color) + +# ! API +func set_rotation(f : float): + rotation = f + +# ! API +func set_invert_chart(b : bool): + invert_chart = b + +# ! API +func set_treshold(t : Vector2): + treshold = t + +# ! API +func set_only_disp_values(v : Vector2): + only_disp_values = v + +func set_legend(l : Array): + legend = l + +func get_legend() -> Array: + return legend + +# ............................. Shared Signals .............................. +func point_pressed(point : Point): + emit_signal("point_pressed",point) + +func show_data(point : Point): + PointData.update_datas(point) + PointData.show() + +func hide_data(): + PointData.hide() + +func show_slice_data(slice : Slice): + PointData.update_slice_datas(slice) + PointData.show() diff --git a/addons/easy_charts/Utilities/Scripts/chart2d.gd b/addons/easy_charts/Utilities/Scripts/chart2d.gd new file mode 100644 index 0000000..27f6f65 --- /dev/null +++ b/addons/easy_charts/Utilities/Scripts/chart2d.gd @@ -0,0 +1,211 @@ +tool +extends Node2D +class_name Chart2D + +enum PointShapes { Dot, Triangle, Square, Cross } +enum TemplatesNames { Default, Clean, Gradient, Minimal, Invert } + + +signal chart_plotted(chart) +signal point_pressed(point) + + +const OFFSET: Vector2 = Vector2(0,0) + + +export (Vector2) var SIZE: Vector2 = Vector2() setget _set_size +export (String, FILE, "*.txt, *.csv") var source: String = "" +export (String) var delimiter: String = ";" +export (bool) var origin_at_zero: bool = true + +export (bool) var are_values_columns: bool = false +export (int, 0, 100) var x_values_index: int = 0 +export(bool) var show_x_values_as_labels: bool = true + +export (float,1,20,0.5) var column_width: float = 10 +export (float,0,10,0.5) var column_gap: float = 2 + +export (float, 0.1, 10.0) var x_decim: float = 5.0 +export (float, 0.1, 10.0) var y_decim: float = 5.0 +export (PointShapes) var point_shape: int = 0 +export (PoolColorArray) var function_colors = [Color("#1e1e1e")] +export (Color) var v_lines_color: Color = Color("#cacaca") +export (Color) var h_lines_color: Color = Color("#cacaca") + +export (bool) var boxed: bool = true +export (Color) var box_color: Color = Color("#1e1e1e") +export (Font) var font: Font +export (Font) var bold_font: Font +export (Color) var font_color: Color = Color("#1e1e1e") +export var template : int = 0 setget apply_template +export (float, 0.1, 1) var drawing_duration: float = 0.5 +export (bool) var invert_chart: bool = false + + +var OutlinesTween: Tween +var FunctionsTween: Tween +var PointTween : Tween +var Functions: Node2D +var GridTween: Tween +var PointData: PointData +var Outlines: Line2D +var Grid: Node2D + +var point_node: PackedScene = preload("../Point/Point.tscn") +var FunctionLegend: PackedScene = preload("../Legend/FunctionLegend.tscn") + +var font_size: float = 16 +var const_height: float = font_size / 2 * font_size / 20 +var const_width: float = font_size / 2 + +var origin: Vector2 + +# actual distance between x and y values +var x_pass: float +var y_pass: float + +# vertical distance between y consecutive points used for intervals +var v_dist: float +var h_dist: float + +# quantization, representing the interval in which values will be displayed + +# define values on x an y axis +var x_chors: Array +var y_chors: Array + +# actual coordinates of points (in pixel) +var x_coordinates: Array +var y_coordinates: Array + +# datas contained in file +var datas: Array + +# amount of functions to represent +var functions: int = 0 + +var x_label: String + +# database values +var x_datas: Array +var y_datas: Array + +# labels displayed on chart +var x_labels: Array +var y_labels: Array + +var x_margin_min: int = 0 +var y_margin_min: int = 0 + +# actual values of point, from the database +var point_values: Array + +# actual position of points in pixel +var point_positions: Array + +var legend: Array setget set_legend, get_legend + +var templates: Dictionary = {} + + +# Called when the node enters the scene tree for the first time. +func _ready(): + pass # Replace with function body. + +func build_chart(): + pass + +func calculate_pass(): + pass + +func calculate_coordinates(): + pass + +func _set_size(v : Vector2): + SIZE = v + +func _get_children(): + OutlinesTween = $OutlinesTween + FunctionsTween = $FunctionsTween + Functions = $Functions + GridTween = $GridTween + PointData = $PointData/PointData + Outlines = $Outlines + Grid = $Grid + +func apply_template(template_name: int): + template = template_name + templates = Utilities._load_templates() + if template_name != null: + var custom_template = templates.get(templates.keys()[template_name]) + function_colors = custom_template.function_colors as PoolColorArray + v_lines_color = Color(custom_template.v_lines_color) + h_lines_color = Color(custom_template.h_lines_color) + box_color = Color(custom_template.outline_color) + font_color = Color(custom_template.font_color) + property_list_changed_notify() + + if Engine.editor_hint: + _get_children() + Outlines.set_default_color(box_color) + Grid.get_node("VLine").set_default_color(v_lines_color) + Grid.get_node("HLine").set_default_color(h_lines_color) + +func redraw(): + build_chart() + calculate_pass() + calculate_coordinates() + update() + + +func show_data(point): + PointData.update_datas(point) + PointData.show() + + +func hide_data(): + PointData.hide() + + +func clear_points(): + function_colors.clear() + if Functions.get_children(): + for function in Functions.get_children(): + function.queue_free() + +func count_functions(): + if are_values_columns: + if not invert_chart: + functions = datas[0].size() - 1 + else: + functions = datas.size() - 1 + else: + if invert_chart: + functions = datas[0].size() - 1 + else: + functions = datas.size() - 1 + +func set_legend(l: Array): + legend = l + + +func get_legend(): + return legend + + +func create_legend(): + legend.clear() + for function in functions: + var function_legend = FunctionLegend.instance() + var f_name: String + if invert_chart: + f_name = x_datas[function] as String + else: + f_name = y_labels[function] + var legend_font: Font + if font != null: + legend_font = font + if bold_font != null: + legend_font = bold_font + function_legend.create_legend(f_name, function_colors[function], bold_font, font_color) + legend.append(function_legend) From c9023e2dacc889a89dc09a8b550bd9e003014362 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Santilio?= Date: Thu, 31 Dec 2020 15:02:54 +0100 Subject: [PATCH 07/13] update samples --- addons/easy_charts/BarChart/bar_chart.gd | 90 +++++++------------ addons/easy_charts/file.samples/2_columns.csv | 10 --- .../file.samples/2_columns.csv.import | 10 --- .../easy_charts/file.samples/datas.csv.import | 10 --- addons/easy_charts/file.samples/datas2.csv | 5 -- .../file.samples/datas2.csv.import | 10 --- .../file.samples/datas_on_columns.csv.import | 10 --- .../file.samples/datas_on_rows.csv | 5 -- .../file.samples/datas_on_rows.csv.import | 10 --- .../file.samples/gei_jam#2columns.csv | 3 - .../file.samples/gei_jam#2columns.csv.import | 10 --- .../godot_engine_italia_jam#2.csv | 6 -- .../godot_engine_italia_jam#2.csv.import | 10 --- .../{datas.csv => linechart (columns).csv} | 0 .../linechart (columns).csv.import | 10 +++ ...as_on_columns.csv => linechart (rows).csv} | 0 .../file.samples/linechart (rows).csv.import | 10 +++ 17 files changed, 52 insertions(+), 157 deletions(-) delete mode 100644 addons/easy_charts/file.samples/2_columns.csv delete mode 100644 addons/easy_charts/file.samples/2_columns.csv.import delete mode 100644 addons/easy_charts/file.samples/datas.csv.import delete mode 100644 addons/easy_charts/file.samples/datas2.csv delete mode 100644 addons/easy_charts/file.samples/datas2.csv.import delete mode 100644 addons/easy_charts/file.samples/datas_on_columns.csv.import delete mode 100644 addons/easy_charts/file.samples/datas_on_rows.csv delete mode 100644 addons/easy_charts/file.samples/datas_on_rows.csv.import delete mode 100644 addons/easy_charts/file.samples/gei_jam#2columns.csv delete mode 100644 addons/easy_charts/file.samples/gei_jam#2columns.csv.import delete mode 100644 addons/easy_charts/file.samples/godot_engine_italia_jam#2.csv delete mode 100644 addons/easy_charts/file.samples/godot_engine_italia_jam#2.csv.import rename addons/easy_charts/file.samples/{datas.csv => linechart (columns).csv} (100%) create mode 100644 addons/easy_charts/file.samples/linechart (columns).csv.import rename addons/easy_charts/file.samples/{datas_on_columns.csv => linechart (rows).csv} (100%) create mode 100644 addons/easy_charts/file.samples/linechart (rows).csv.import diff --git a/addons/easy_charts/BarChart/bar_chart.gd b/addons/easy_charts/BarChart/bar_chart.gd index 8b00363..e291192 100644 --- a/addons/easy_charts/BarChart/bar_chart.gd +++ b/addons/easy_charts/BarChart/bar_chart.gd @@ -160,14 +160,14 @@ func build_chart(): SIZE = get_size() origin = Vector2(OFFSET.x,SIZE.y-OFFSET.y) -func structure_datas(database : Array, are_values_columns : bool, x_values_index : int): - # @x_values_index can be either a column or a row relative to x values - self.are_values_columns = are_values_columns +func structure_datas(database : Array): + # @labels_index can be either a column or a row relative to x values + are_values_columns = (invert_chart != are_values_columns) if are_values_columns: for row in database.size(): var t_vals: Array for column in database[row].size(): - if column == x_values_index: + if column == labels_index: var x_data = database[row][column] if x_data.is_valid_float() or x_data.is_valid_integer(): x_datas.append(x_data as float) @@ -187,7 +187,7 @@ func structure_datas(database : Array, are_values_columns : bool, x_values_index x_label = str(x_datas.pop_front()) else: for row in database.size(): - if row == x_values_index: + if row == labels_index: x_datas = (database[row]) x_label = x_datas.pop_front() as String else: @@ -246,19 +246,16 @@ func structure_datas(database : Array, are_values_columns : bool, x_values_index func calculate_pass(): - if invert_chart: - x_chors = y_labels as PoolStringArray + if show_x_values_as_labels: + x_chors = x_datas as PoolStringArray else: - if show_x_values_as_labels: - x_chors = x_datas as PoolStringArray - else: - x_chors = x_labels + x_chors = x_labels # calculate distance in pixel between 2 consecutive values/datas if not are_values_columns: - x_pass = (SIZE.x - OFFSET.x*2 - (column_width) * ( y_datas.size() if not invert_chart else y_datas[0].size()+1 ) - column_gap - column_width/2) / ((x_chors.size()-1) if x_chors.size()!=1 else 1) + x_pass = (SIZE.x - OFFSET.x*2 - (column_width) * ( y_datas.size()) - column_gap - column_width/2) / ((x_chors.size()-1) if x_chors.size()!=1 else 1) else: - x_pass = (SIZE.x - OFFSET.x*2 - (column_width) * ( y_datas.size() if invert_chart else y_datas[0].size()+1 ) - column_gap - column_width/2) / (x_chors.size()-1) + x_pass = (SIZE.x - OFFSET.x*2 - (column_width) * ( y_datas[0].size()+1 ) - column_gap - column_width/2) / (x_chors.size()-1) y_pass = (origin.y - ChartName.get_rect().size.y*2) / (y_chors.size() - 1) func calculate_coordinates(): @@ -266,25 +263,15 @@ func calculate_coordinates(): y_coordinates.clear() point_values.clear() point_positions.clear() - - if invert_chart: - for column in y_datas[0].size(): - var single_coordinates : Array - for row in y_datas: - if origin_at_zero: - single_coordinates.append((row[column]*y_pass)/v_dist) - else: - single_coordinates.append((row[column] - y_margin_min)*y_pass/v_dist) - y_coordinates.append(single_coordinates) - else: - for cluster in y_datas: - var single_coordinates : Array - for value in cluster.size(): - if origin_at_zero: - single_coordinates.append((cluster[value]*y_pass)/v_dist) - else: - single_coordinates.append((cluster[value] - y_margin_min)*y_pass/v_dist) - y_coordinates.append(single_coordinates) + + for cluster in y_datas: + var single_coordinates : Array + for value in cluster.size(): + if origin_at_zero: + single_coordinates.append((cluster[value]*y_pass)/v_dist) + else: + single_coordinates.append((cluster[value] - y_margin_min)*y_pass/v_dist) + y_coordinates.append(single_coordinates) if show_x_values_as_labels: for x in x_datas.size(): @@ -292,10 +279,7 @@ func calculate_coordinates(): else: for x in x_datas.size(): if origin_at_zero: - if not invert_chart: - x_coordinates.append(x_pass*x) - else: - x_coordinates.append(x*x_pass/h_dist) + x_coordinates.append(x_pass*x) else: x_coordinates.append((x_datas[x] - x_margin_min)*x_pass/h_dist) @@ -303,24 +287,14 @@ func calculate_coordinates(): point_values.append([]) point_positions.append([]) - if invert_chart: - for function in y_coordinates.size(): - for function_value in y_coordinates[function].size(): - if are_values_columns: - point_values[function].append([x_datas[function_value],y_datas[function_value][function]]) - point_positions[function].append(Vector2(OFFSET.x/2 + column_width/2 + (column_width + column_gap)*function + x_coordinates[function_value]+origin.x,origin.y-y_coordinates[function][function_value])) - else: - point_positions[function].append(Vector2(OFFSET.x/2 + column_width/2 + (column_width + column_gap)*function + x_coordinates[function_value]+origin.x,origin.y-y_coordinates[function][function_value])) - point_values[function].append([x_datas[function_value],y_datas[function_value][function]]) - else: - for cluster in y_coordinates.size(): - for y in y_coordinates[cluster].size(): - if are_values_columns: - point_positions[y].append(Vector2(OFFSET.x/2 + column_width/2 + (column_width + column_gap)*y + x_coordinates[cluster] + origin.x, origin.y-y_coordinates[cluster][y])) - point_values[y].append([x_datas[cluster],y_datas[cluster][y]]) - else: - point_values[cluster].append([x_datas[y],y_datas[cluster][y]]) - point_positions[cluster].append(Vector2(OFFSET.x/2 + column_width/2 + (column_width + column_gap)*cluster + x_coordinates[y]+origin.x,origin.y-y_coordinates[cluster][y])) + for cluster in y_coordinates.size(): + for y in y_coordinates[cluster].size(): + if are_values_columns: + point_positions[y].append(Vector2(OFFSET.x/2 + column_width/2 + (column_width + column_gap)*y + x_coordinates[cluster] + origin.x, origin.y-y_coordinates[cluster][y])) + point_values[y].append([x_datas[cluster],y_datas[cluster][y]]) + else: + point_values[cluster].append([x_datas[y],y_datas[cluster][y]]) + point_positions[cluster].append(Vector2(OFFSET.x/2 + column_width/2 + (column_width + column_gap)*cluster + x_coordinates[y]+origin.x,origin.y-y_coordinates[cluster][y])) func _draw(): clear_points() @@ -342,10 +316,10 @@ func _draw(): point.connect("_mouse_entered",self,"show_data") point.connect("_mouse_exited",self,"hide_data") - point.create_point(points_shape[_function], function_colors[function_point if invert_chart else _function], + point.create_point(points_shape[_function], function_colors[_function], Color.white, point_positions[_function][function_point] + Vector2(0,7), point.format_value(point_values[_function][function_point], false, false), - y_labels[function_point if invert_chart else _function] as String) + y_labels[_function] as String) PointContainer.add_child(point) point.rect_size.y = origin.y - point_positions[_function][function_point].y draw_line( Vector2(point_positions[_function][function_point].x, origin.y), @@ -362,9 +336,9 @@ func draw_grid(): draw_line(point-Vector2(0,5),point,v_lines_color,1,true) var calculated_gap : float if not are_values_columns: - calculated_gap = ( y_datas.size() if not invert_chart else y_datas[0].size()+1 ) + calculated_gap = ( y_datas.size() ) else: - calculated_gap = ( y_datas.size() if invert_chart else y_datas[0].size()+1 ) + calculated_gap = ( y_datas[0].size()+1 ) draw_string( font, point + Vector2(-const_width/2*x_chors[p].length() + (column_width + column_gap) * functions, font_size), diff --git a/addons/easy_charts/file.samples/2_columns.csv b/addons/easy_charts/file.samples/2_columns.csv deleted file mode 100644 index 936a280..0000000 --- a/addons/easy_charts/file.samples/2_columns.csv +++ /dev/null @@ -1,10 +0,0 @@ -Year;Column 1 -2009;36200 -2010;36600 -2011;37500 -2012;38700 -2013;39600 -2014;40500 -2015;41200 -2016;41803 -2017;42600 diff --git a/addons/easy_charts/file.samples/2_columns.csv.import b/addons/easy_charts/file.samples/2_columns.csv.import deleted file mode 100644 index a005d65..0000000 --- a/addons/easy_charts/file.samples/2_columns.csv.import +++ /dev/null @@ -1,10 +0,0 @@ -[remap] - -importer="csv" -type="TextFile" - -[deps] - -source_file="res://addons/easy_charts/file.samples/2_columns.csv" -[params] - diff --git a/addons/easy_charts/file.samples/datas.csv.import b/addons/easy_charts/file.samples/datas.csv.import deleted file mode 100644 index aeeda06..0000000 --- a/addons/easy_charts/file.samples/datas.csv.import +++ /dev/null @@ -1,10 +0,0 @@ -[remap] - -importer="csv" -type="TextFile" - -[deps] - -source_file="res://addons/easy_charts/file.samples/datas.csv" -[params] - diff --git a/addons/easy_charts/file.samples/datas2.csv b/addons/easy_charts/file.samples/datas2.csv deleted file mode 100644 index f8939cb..0000000 --- a/addons/easy_charts/file.samples/datas2.csv +++ /dev/null @@ -1,5 +0,0 @@ -Year;2009;2010;2011;2012;2013;2014;2015;2016;2017 -Column 1;36200;36600;37500;38700;39600;40500;41200;41803;42600 -Column 2;27200;27800;28500;29400;30200;30900;31500;31931;32600 -Column 3;26200;26600;27500;28700;29600;20500;21200;21803;22600 -Column 4;17200;17800;18500;19400;10200;10900;11500;11931;12600 diff --git a/addons/easy_charts/file.samples/datas2.csv.import b/addons/easy_charts/file.samples/datas2.csv.import deleted file mode 100644 index cc1d366..0000000 --- a/addons/easy_charts/file.samples/datas2.csv.import +++ /dev/null @@ -1,10 +0,0 @@ -[remap] - -importer="csv" -type="TextFile" - -[deps] - -source_file="res://addons/easy_charts/file.samples/datas2.csv" -[params] - diff --git a/addons/easy_charts/file.samples/datas_on_columns.csv.import b/addons/easy_charts/file.samples/datas_on_columns.csv.import deleted file mode 100644 index 22f1cc0..0000000 --- a/addons/easy_charts/file.samples/datas_on_columns.csv.import +++ /dev/null @@ -1,10 +0,0 @@ -[remap] - -importer="csv" -type="TextFile" - -[deps] - -source_file="res://addons/easy_charts/file.samples/datas_on_columns.csv" -[params] - diff --git a/addons/easy_charts/file.samples/datas_on_rows.csv b/addons/easy_charts/file.samples/datas_on_rows.csv deleted file mode 100644 index b3bd3a9..0000000 --- a/addons/easy_charts/file.samples/datas_on_rows.csv +++ /dev/null @@ -1,5 +0,0 @@ -Year;2009;2010;2011;2012;2013;2014;2015;2016;2017 -Column 1;36200;36600;37500;38700;39600;40500;41200;41803;42600 -Column 2;27200;27800;28500;29400;30200;30900;31500;31931;32600 -Column 3;26200;26600;27500;28700;29600;20500;21200;21803;22600 -Column 4;17200;17800;18500;19400;10200;10900;11500;11931;12600 diff --git a/addons/easy_charts/file.samples/datas_on_rows.csv.import b/addons/easy_charts/file.samples/datas_on_rows.csv.import deleted file mode 100644 index 865b2ef..0000000 --- a/addons/easy_charts/file.samples/datas_on_rows.csv.import +++ /dev/null @@ -1,10 +0,0 @@ -[remap] - -importer="csv" -type="TextFile" - -[deps] - -source_file="res://addons/easy_charts/file.samples/datas_on_rows.csv" -[params] - diff --git a/addons/easy_charts/file.samples/gei_jam#2columns.csv b/addons/easy_charts/file.samples/gei_jam#2columns.csv deleted file mode 100644 index 6475b30..0000000 --- a/addons/easy_charts/file.samples/gei_jam#2columns.csv +++ /dev/null @@ -1,3 +0,0 @@ -Meccaniche;deve essere possibile rallentare/fermare il tempo;deve essere basato su una griglia;deve essere un gioco a tentativi;deve implementare della magia;non deve contenere istruzioni -Valori;22;13;20;20;15 -Valori2;22;13;20;20;15 diff --git a/addons/easy_charts/file.samples/gei_jam#2columns.csv.import b/addons/easy_charts/file.samples/gei_jam#2columns.csv.import deleted file mode 100644 index b661865..0000000 --- a/addons/easy_charts/file.samples/gei_jam#2columns.csv.import +++ /dev/null @@ -1,10 +0,0 @@ -[remap] - -importer="csv" -type="TextFile" - -[deps] - -source_file="res://addons/easy_charts/file.samples/gei_jam#2columns.csv" -[params] - diff --git a/addons/easy_charts/file.samples/godot_engine_italia_jam#2.csv b/addons/easy_charts/file.samples/godot_engine_italia_jam#2.csv deleted file mode 100644 index 1b1178f..0000000 --- a/addons/easy_charts/file.samples/godot_engine_italia_jam#2.csv +++ /dev/null @@ -1,6 +0,0 @@ -Meccaniche;Voti -deve essere possibile rallentare/fermare il tempo;22 -deve essere basato su una griglia;13 -deve essere un gioco a tentativi;20 -deve implementare della magia;20 -non deve contenere istruzioni;15 diff --git a/addons/easy_charts/file.samples/godot_engine_italia_jam#2.csv.import b/addons/easy_charts/file.samples/godot_engine_italia_jam#2.csv.import deleted file mode 100644 index be821ee..0000000 --- a/addons/easy_charts/file.samples/godot_engine_italia_jam#2.csv.import +++ /dev/null @@ -1,10 +0,0 @@ -[remap] - -importer="csv" -type="TextFile" - -[deps] - -source_file="res://addons/easy_charts/file.samples/godot_engine_italia_jam#2.csv" -[params] - diff --git a/addons/easy_charts/file.samples/datas.csv b/addons/easy_charts/file.samples/linechart (columns).csv similarity index 100% rename from addons/easy_charts/file.samples/datas.csv rename to addons/easy_charts/file.samples/linechart (columns).csv diff --git a/addons/easy_charts/file.samples/linechart (columns).csv.import b/addons/easy_charts/file.samples/linechart (columns).csv.import new file mode 100644 index 0000000..9c7c145 --- /dev/null +++ b/addons/easy_charts/file.samples/linechart (columns).csv.import @@ -0,0 +1,10 @@ +[remap] + +importer="csv" +type="TextFile" + +[deps] + +source_file="res://addons/easy_charts/file.samples/linechart (columns).csv" +[params] + diff --git a/addons/easy_charts/file.samples/datas_on_columns.csv b/addons/easy_charts/file.samples/linechart (rows).csv similarity index 100% rename from addons/easy_charts/file.samples/datas_on_columns.csv rename to addons/easy_charts/file.samples/linechart (rows).csv diff --git a/addons/easy_charts/file.samples/linechart (rows).csv.import b/addons/easy_charts/file.samples/linechart (rows).csv.import new file mode 100644 index 0000000..9b1b8f7 --- /dev/null +++ b/addons/easy_charts/file.samples/linechart (rows).csv.import @@ -0,0 +1,10 @@ +[remap] + +importer="csv" +type="TextFile" + +[deps] + +source_file="res://addons/easy_charts/file.samples/linechart (rows).csv" +[params] + From 79661f34d4626231f1ae186dbd9abec16a056a6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Santilio?= Date: Thu, 31 Dec 2020 15:07:26 +0100 Subject: [PATCH 08/13] renamed BarChart -> ColumnChart --- .../BarChart.tscn => ColumnChart/ColumnChart.tscn} | 12 ++++++------ .../bar_chart.gd => ColumnChart/column_chart.gd} | 6 +++--- .../BarChart2D.gd => ColumnChart2D/ColumnChart2D.gd} | 4 ++-- .../ColumnChart2D.tscn} | 4 +++- .../Utilities/Containers/ChartContainer2D.gd | 4 ++-- addons/easy_charts/Utilities/Scripts/utilities.gd | 2 +- 6 files changed, 17 insertions(+), 15 deletions(-) rename addons/easy_charts/{BarChart/BarChart.tscn => ColumnChart/ColumnChart.tscn} (92%) rename addons/easy_charts/{BarChart/bar_chart.gd => ColumnChart/column_chart.gd} (99%) rename addons/easy_charts/{BarChart2D/BarChart2D.gd => ColumnChart2D/ColumnChart2D.gd} (99%) rename addons/easy_charts/{BarChart2D/BarChart2D.tscn => ColumnChart2D/ColumnChart2D.tscn} (95%) diff --git a/addons/easy_charts/BarChart/BarChart.tscn b/addons/easy_charts/ColumnChart/ColumnChart.tscn similarity index 92% rename from addons/easy_charts/BarChart/BarChart.tscn rename to addons/easy_charts/ColumnChart/ColumnChart.tscn index 06f25e9..05c57bb 100644 --- a/addons/easy_charts/BarChart/BarChart.tscn +++ b/addons/easy_charts/ColumnChart/ColumnChart.tscn @@ -1,11 +1,11 @@ [gd_scene load_steps=4 format=2] [ext_resource path="res://addons/easy_charts/Utilities/Point/PointData.tscn" type="PackedScene" id=1] -[ext_resource path="res://addons/easy_charts/BarChart/bar_chart.gd" type="Script" id=2] +[ext_resource path="res://addons/easy_charts/ColumnChart/column_chart.gd" type="Script" id=2] [sub_resource type="Theme" id=1] -[node name="BarChart" type="Control"] +[node name="ColumnChart" type="Control"] anchor_right = 1.0 anchor_bottom = 1.0 rect_min_size = Vector2( 70, 50 ) @@ -82,10 +82,10 @@ __meta__ = { [node name="PointData" parent="." instance=ExtResource( 1 )] [node name="PointData" parent="PointData" index="0"] -margin_left = -32.6676 -margin_top = -84.4238 -margin_right = -32.8033 -margin_bottom = -83.6237 +margin_left = -593.381 +margin_top = -80.9071 +margin_right = -593.517 +margin_bottom = -80.107 theme = SubResource( 1 ) [editable path="PointData"] diff --git a/addons/easy_charts/BarChart/bar_chart.gd b/addons/easy_charts/ColumnChart/column_chart.gd similarity index 99% rename from addons/easy_charts/BarChart/bar_chart.gd rename to addons/easy_charts/ColumnChart/column_chart.gd index e291192..9fe5a6e 100644 --- a/addons/easy_charts/BarChart/bar_chart.gd +++ b/addons/easy_charts/ColumnChart/column_chart.gd @@ -1,9 +1,9 @@ tool extends Chart -class_name BarChart +class_name ColumnChart """ -[BarChart] - General purpose node for Bar Charts +[ColumnChart] - General purpose node for Column Charts A bar chart or bar graph is a chart or graph that presents categorical data with rectangular bars with heights or lengths proportional to the values that they represent. @@ -25,7 +25,7 @@ func _get_property_list(): { "hint": PROPERTY_HINT_NONE, "usage": PROPERTY_USAGE_CATEGORY, - "name": "BarChart", + "name": "ColumnChart", "type": TYPE_STRING }, { diff --git a/addons/easy_charts/BarChart2D/BarChart2D.gd b/addons/easy_charts/ColumnChart2D/ColumnChart2D.gd similarity index 99% rename from addons/easy_charts/BarChart2D/BarChart2D.gd rename to addons/easy_charts/ColumnChart2D/ColumnChart2D.gd index f3abf1a..c835080 100644 --- a/addons/easy_charts/BarChart2D/BarChart2D.gd +++ b/addons/easy_charts/ColumnChart2D/ColumnChart2D.gd @@ -1,9 +1,9 @@ tool extends Chart2D -class_name BarChart2D +class_name ColumnChart2D """ -[BarChart2D] - General purpose node for Bar Charts +[ColumnChart2D] - General purpose node for Bar Charts A bar chart or bar graph is a chart or graph that presents categorical data with rectangular bars with heights or lengths proportional to the values that they represent. diff --git a/addons/easy_charts/BarChart2D/BarChart2D.tscn b/addons/easy_charts/ColumnChart2D/ColumnChart2D.tscn similarity index 95% rename from addons/easy_charts/BarChart2D/BarChart2D.tscn rename to addons/easy_charts/ColumnChart2D/ColumnChart2D.tscn index 273481e..616e09a 100644 --- a/addons/easy_charts/BarChart2D/BarChart2D.tscn +++ b/addons/easy_charts/ColumnChart2D/ColumnChart2D.tscn @@ -1,8 +1,10 @@ [gd_scene load_steps=3 format=2] -[ext_resource path="res://addons/easy_charts/BarChart2D/BarChart2D.gd" type="Script" id=1] +[ext_resource path="res://addons/easy_charts/ColumnChart2D/ColumnChart2D.gd" type="Script" id=1] [ext_resource path="res://addons/easy_charts/Utilities/Point/PointData.tscn" type="PackedScene" id=2] + + [node name="BarChart2D" type="Node2D"] script = ExtResource( 1 ) __meta__ = { diff --git a/addons/easy_charts/Utilities/Containers/ChartContainer2D.gd b/addons/easy_charts/Utilities/Containers/ChartContainer2D.gd index 5fec906..bc23830 100644 --- a/addons/easy_charts/Utilities/Containers/ChartContainer2D.gd +++ b/addons/easy_charts/Utilities/Containers/ChartContainer2D.gd @@ -2,9 +2,9 @@ tool extends Node2D var LineChart = preload("LineChart2D/LineChart2D.tscn") -var ColumnChart = preload("BarChart2D/BarChart2D.tscn") +var ColumnChart = preload("ColumnChart2D/ColumnChart2D.tscn") -export (String,"None","LineChart2D","BarChart2D") var chart_type : String setget set_type,get_type +export (String,"None","LineChart2D","ColumnChart2D") var chart_type : String setget set_type,get_type var chart : Node2D setget set_chart,get_chart # Called when the node enters the scene tree for the first time. diff --git a/addons/easy_charts/Utilities/Scripts/utilities.gd b/addons/easy_charts/Utilities/Scripts/utilities.gd index 836b796..d4eabb8 100644 --- a/addons/easy_charts/Utilities/Scripts/utilities.gd +++ b/addons/easy_charts/Utilities/Scripts/utilities.gd @@ -5,7 +5,7 @@ var plugin_name : String = "Easy Charts" var templates : Dictionary = {} var chart_types : Dictionary = { 0:"LineChart", - 1:"BarChart", + 1:"ColumnChart", 2:"ScatterChart", 3:"RadarChart", 4:"PieChart" From dd6562bcba12be6b92e14244c3dd61157552a79b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Santilio?= Date: Thu, 31 Dec 2020 15:17:38 +0100 Subject: [PATCH 09/13] Control charts update --- addons/easy_charts/PieChart/pie_chart.gd | 10 +- addons/easy_charts/RadarChart/radar_chart.gd | 5 +- .../easy_charts/ScatterChart/ScatterChart.gd | 343 ------------------ .../ScatterChart/ScatterChart.tscn | 11 +- .../easy_charts/ScatterChart/scatter_chart.gd | 317 ++++++++++++++++ .../{scatter.csv => scatter (columns).csv} | 202 +++++------ .../file.samples/scatter (columns).csv.import | 10 + 7 files changed, 440 insertions(+), 458 deletions(-) delete mode 100644 addons/easy_charts/ScatterChart/ScatterChart.gd create mode 100644 addons/easy_charts/ScatterChart/scatter_chart.gd rename addons/easy_charts/file.samples/{scatter.csv => scatter (columns).csv} (97%) create mode 100644 addons/easy_charts/file.samples/scatter (columns).csv.import diff --git a/addons/easy_charts/PieChart/pie_chart.gd b/addons/easy_charts/PieChart/pie_chart.gd index efe2ed1..5dcff66 100644 --- a/addons/easy_charts/PieChart/pie_chart.gd +++ b/addons/easy_charts/PieChart/pie_chart.gd @@ -108,15 +108,15 @@ func plot_placeholder() -> void: ] plot_from_array(data) -func structure_datas(database: Array, are_values_columns: bool, x_values_index: int): - # @x_values_index can be either a column or a row relative to x values +func structure_datas(database: Array): + # @labels_index can be either a column or a row relative to x values clean_variables() - self.are_values_columns = are_values_columns + are_values_columns = invert_chart != are_values_columns if are_values_columns: for row in database.size(): var t_vals: Array for column in database[row].size(): - if column == x_values_index: + if column == labels_index: var x_data = database[row][column] if x_data.is_valid_float() or x_data.is_valid_integer(): x_datas.append(x_data as float) @@ -136,7 +136,7 @@ func structure_datas(database: Array, are_values_columns: bool, x_values_index: x_label = str(x_datas.pop_front()) else: for row in database.size(): - if row == x_values_index: + if row == labels_index: x_datas = (database[row]) x_label = x_datas.pop_front() as String else: diff --git a/addons/easy_charts/RadarChart/radar_chart.gd b/addons/easy_charts/RadarChart/radar_chart.gd index 6f7bd29..c2c2391 100644 --- a/addons/easy_charts/RadarChart/radar_chart.gd +++ b/addons/easy_charts/RadarChart/radar_chart.gd @@ -125,11 +125,10 @@ func _get_property_list(): }, ] -func structure_datas(database : Array, are_values_columns : bool, labels_index : int): +func structure_datas(database : Array): # @x_values_index can be either a column or a row relative to x values # @y_values can be either a column or a row relative to y values - self.labels_index = labels_index - self.are_values_columns = are_values_columns + are_values_columns = invert_chart != are_values_columns match are_values_columns: true: for row in database.size(): diff --git a/addons/easy_charts/ScatterChart/ScatterChart.gd b/addons/easy_charts/ScatterChart/ScatterChart.gd deleted file mode 100644 index edbb97f..0000000 --- a/addons/easy_charts/ScatterChart/ScatterChart.gd +++ /dev/null @@ -1,343 +0,0 @@ -tool -extends Chart -class_name ScatterChart - -""" -[ScatterChart] - General purpose node for Scatter Charts - -A scatter plot (also called a scatterplot, scatter graph, scatter chart, scattergram, or scatter diagram) - is a type of plot or mathematical diagram using Cartesian coordinates to display values for typically two variables -for a set of data. If the points are coded (color/shape/size), one additional variable can be displayed. -The data are displayed as a collection of points, each having the value of one variable determining the position on -the horizontal axis and the value of the other variable determining the position on the vertical axis. - -/ source : Wikipedia / -""" - -# --------------------- - -func _get_property_list(): - return [ - # Chart Properties - { - "hint": PROPERTY_HINT_NONE, - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Properties/are_values_columns", - "type": TYPE_BOOL - }, - { - "hint": PROPERTY_HINT_RANGE, - "hint_string": "-1,100,1", - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Properties/labels_index", - "type": TYPE_INT - }, - { - "hint": PROPERTY_HINT_NONE, - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Properties/show_x_values_as_labels", - "type": TYPE_BOOL - }, - - # Chart Display - { - "hint": PROPERTY_HINT_RANGE, - "hint_string": "0.1,10", - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Display/x_decim", - "type": TYPE_REAL - }, - { - "hint": PROPERTY_HINT_RANGE, - "hint_string": "0.1,10", - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Display/y_decim", - "type": TYPE_REAL - }, - - # Chart Style - { - "hint": 24, - "hint_string": "%d/%d:%s"%[TYPE_INT, PROPERTY_HINT_ENUM, - PoolStringArray(PointShapes.keys()).join(",")], - "name": "Chart_Style/points_shape", - "type": TYPE_ARRAY, - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE - }, - { - "hint": PROPERTY_HINT_NONE, - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Style/function_colors", - "type": TYPE_COLOR_ARRAY - }, - { - "hint": PROPERTY_HINT_NONE, - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Style/box_color", - "type": TYPE_COLOR - }, - { - "hint": PROPERTY_HINT_NONE, - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Style/v_lines_color", - "type": TYPE_COLOR - }, - { - "hint": PROPERTY_HINT_NONE, - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Style/h_lines_color", - "type": TYPE_COLOR - }, - { - "class_name": "Font", - "hint": PROPERTY_HINT_RESOURCE_TYPE, - "hint_string": "Font", - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Style/font", - "type": TYPE_OBJECT - }, - { - "class_name": "Font", - "hint": PROPERTY_HINT_RESOURCE_TYPE, - "hint_string": "Font", - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Style/bold_font", - "type": TYPE_OBJECT - }, - { - "hint": PROPERTY_HINT_NONE, - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Style/font_color", - "type": TYPE_COLOR - }, - { - "hint": PROPERTY_HINT_ENUM, - "hint_string": PoolStringArray(Utilities.templates.keys()).join(","), - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Style/template", - "type": TYPE_INT - }, - - # Chart Modifiers - { - "hint": PROPERTY_HINT_NONE, - "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, - "name": "Chart_Modifiers/invert_chart", - "type": TYPE_BOOL - }, - ] - -func structure_datas(database : Array, are_values_columns : bool, x_values_index : int): - # @x_values_index can be either a column or a row relative to x values - # @y_values can be either a column or a row relative to y values - self.are_values_columns = are_values_columns - match are_values_columns: - true: - for row in database.size(): - var t_vals : Array - for column in database[row].size(): - if column == x_values_index: - var x_data = database[row][column] - if x_data.is_valid_float() or x_data.is_valid_integer(): - x_datas.append(x_data as float) - else: - x_datas.append(x_data.replace(",",".") as float) - else: - if row != 0: - var y_data = database[row][column] - if y_data.is_valid_float() or y_data.is_valid_integer(): - t_vals.append(y_data as float) - else: - t_vals.append(y_data.replace(",",".") as float) - else: - y_labels.append(str(database[row][column])) - if not t_vals.empty(): - y_datas.append(t_vals) - x_label = str(x_datas.pop_front()) - false: - for row in database.size(): - if row == x_values_index: - x_datas = (database[row]) - x_label = x_datas.pop_front() as String - else: - var values = database[row] as Array - y_labels.append(values.pop_front() as String) - y_datas.append(values) - for data in y_datas: - for value in data.size(): - data[value] = data[value] as float - - # draw y labels - var to_order : Array - var to_order_min : Array - for cluster in y_datas.size(): - # define x_chors and y_chors - var ordered_cluster = y_datas[cluster] as Array - ordered_cluster.sort() - ordered_cluster = PoolIntArray(ordered_cluster) - var margin_max = ordered_cluster[ordered_cluster.size()-1] - var margin_min = ordered_cluster[0] - to_order.append(margin_max) - to_order_min.append(margin_min) - - to_order.sort() - to_order_min.sort() - var margin = to_order.pop_back() - if not origin_at_zero: - y_margin_min = to_order_min.pop_front() - v_dist = y_decim * pow(10.0,str(margin).length()-2) - var multi = 0 - var p = (v_dist*multi) + ((y_margin_min) if not origin_at_zero else 0) - y_chors.append(p as String) - while p < margin: - multi+=1 - p = (v_dist*multi) + ((y_margin_min) if not origin_at_zero else 0) - y_chors.append(p as String) - - # draw x_labels - if not show_x_values_as_labels: - to_order.clear() - to_order = x_datas as PoolIntArray - - to_order.sort() - margin = to_order.pop_back() - if not origin_at_zero: - x_margin_min = to_order.pop_front() - h_dist = x_decim * pow(10.0,str(margin).length()-2) - multi = 0 - p = (h_dist*multi) + ((x_margin_min) if not origin_at_zero else 0) - x_labels.append(p as String) - while p < margin: - multi+=1 - p = (h_dist*multi) + ((x_margin_min) if not origin_at_zero else 0) - x_labels.append(p as String) - -func build_chart(): - SIZE = get_size() - origin = Vector2(OFFSET.x,SIZE.y-OFFSET.y) - -func calculate_pass(): - if invert_chart: - x_chors = y_labels as PoolStringArray - else: - if show_x_values_as_labels: - x_chors = x_datas as PoolStringArray - else: - x_chors = x_labels - - # calculate distance in pixel between 2 consecutive values/datas - x_pass = (SIZE.x - OFFSET.x) / (x_chors.size()-1) - y_pass = origin.y / (y_chors.size()-1) - -func calculate_coordinates(): - x_coordinates.clear() - y_coordinates.clear() - point_values.clear() - point_positions.clear() - - if invert_chart: - for column in y_datas[0].size(): - var single_coordinates : Array - for row in y_datas: - if origin_at_zero: - single_coordinates.append((row[column]*y_pass)/v_dist) - else: - single_coordinates.append((row[column] - y_margin_min)*y_pass/v_dist) - y_coordinates.append(single_coordinates) - else: - for cluster in y_datas: - var single_coordinates : Array - for value in cluster.size(): - if origin_at_zero: - single_coordinates.append((cluster[value]*y_pass)/v_dist) - else: - single_coordinates.append((cluster[value] - y_margin_min)*y_pass/v_dist) - y_coordinates.append(single_coordinates) - - if show_x_values_as_labels: - for x in x_datas.size(): - x_coordinates.append(x_pass*x) - else: - for x in x_datas.size(): - if origin_at_zero: - if invert_chart: - x_coordinates.append(x_pass*x) - else: - x_coordinates.append(x_datas[x]*x_pass/h_dist) - else: - x_coordinates.append((x_datas[x] - x_margin_min)*x_pass/h_dist) - - for f in functions: - point_values.append([]) - point_positions.append([]) - - if invert_chart: - for function in y_coordinates.size(): - for function_value in y_coordinates[function].size(): - if are_values_columns: - point_positions[function_value].append(Vector2(x_coordinates[function]+origin.x, origin.y-y_coordinates[function][function_value])) - point_values[function_value].append([x_datas[function_value],y_datas[function_value][function]]) - else: - point_positions[function].append(Vector2(x_coordinates[function_value]+origin.x,origin.y-y_coordinates[function][function_value])) - point_values[function].append([x_datas[function_value],y_datas[function_value][function]]) - else: - for cluster in y_coordinates.size(): - for y in y_coordinates[cluster].size(): - if are_values_columns: - point_values[y].append([x_datas[cluster],y_datas[cluster][y]]) - point_positions[y].append(Vector2(x_coordinates[cluster]+origin.x,origin.y-y_coordinates[cluster][y])) - else: - point_values[cluster].append([x_datas[y],y_datas[cluster][y]]) - point_positions[cluster].append(Vector2(x_coordinates[y]+origin.x,origin.y-y_coordinates[cluster][y])) - -func _draw(): - clear_points() - - draw_grid() - draw_chart_outlines() - - var defined_colors : bool = false - if function_colors.size(): - defined_colors = true - - for _function in point_values.size(): - var PointContainer : Control = Control.new() - Points.add_child(PointContainer) - - for function_point in point_values[_function].size(): - var point : Point = point_node.instance() - point.connect("_point_pressed",self,"point_pressed") - point.connect("_mouse_entered",self,"show_data") - point.connect("_mouse_exited",self,"hide_data") - - point.create_point(points_shape[_function], function_colors[function_point if invert_chart else _function], - Color.white, point_positions[_function][function_point], - point.format_value(point_values[_function][function_point], false, false), - y_labels[function_point if invert_chart else _function] as String) - - PointContainer.add_child(point) - -func draw_grid(): - # ascisse - for p in x_chors.size(): - var point : Vector2 = origin+Vector2((p)*x_pass,0) - # v grid - draw_line(point,point-Vector2(0,SIZE.y-OFFSET.y),v_lines_color,0.2,true) - # ascisse - draw_line(point-Vector2(0,5),point,v_lines_color,1,true) - draw_string(font,point+Vector2(-const_width/2*x_chors[p].length(),font_size+const_height),x_chors[p],font_color) - - # ordinate - for p in y_chors.size(): - var point : Vector2 = origin-Vector2(0,(p)*y_pass) - # h grid - draw_line(point,point+Vector2(SIZE.x-OFFSET.x,0),h_lines_color,0.2,true) - # ordinate - draw_line(point,point+Vector2(5,0),h_lines_color,1,true) - draw_string(font,point-Vector2(y_chors[p].length()*const_width+font_size,-const_height),y_chors[p],font_color) - -func draw_chart_outlines(): - draw_line(origin,SIZE-Vector2(0,OFFSET.y),box_color,1,true) - draw_line(origin,Vector2(OFFSET.x,0),box_color,1,true) - draw_line(Vector2(OFFSET.x,0),Vector2(SIZE.x,0),box_color,1,true) - draw_line(Vector2(SIZE.x,0),SIZE-Vector2(0,OFFSET.y),box_color,1,true) diff --git a/addons/easy_charts/ScatterChart/ScatterChart.tscn b/addons/easy_charts/ScatterChart/ScatterChart.tscn index ba5579d..ff0225e 100644 --- a/addons/easy_charts/ScatterChart/ScatterChart.tscn +++ b/addons/easy_charts/ScatterChart/ScatterChart.tscn @@ -1,7 +1,7 @@ [gd_scene load_steps=4 format=2] [ext_resource path="res://addons/easy_charts/Utilities/Point/PointData.tscn" type="PackedScene" id=1] -[ext_resource path="res://addons/easy_charts/ScatterChart/ScatterChart.gd" type="Script" id=2] +[ext_resource path="res://addons/easy_charts/ScatterChart/scatter_chart.gd" type="Script" id=2] [sub_resource type="Theme" id=1] @@ -75,11 +75,10 @@ __meta__ = { [node name="PointData" parent="." instance=ExtResource( 1 )] [node name="PointData" parent="PointData" index="0"] -visible = false -margin_left = 58.5211 -margin_top = -187.685 -margin_right = 58.3851 -margin_bottom = -186.885 +margin_left = -311.73 +margin_top = -167.672 +margin_right = -311.866 +margin_bottom = -166.872 theme = SubResource( 1 ) [editable path="PointData"] diff --git a/addons/easy_charts/ScatterChart/scatter_chart.gd b/addons/easy_charts/ScatterChart/scatter_chart.gd new file mode 100644 index 0000000..8d5b283 --- /dev/null +++ b/addons/easy_charts/ScatterChart/scatter_chart.gd @@ -0,0 +1,317 @@ +tool +extends Chart +class_name ScatterChart + +""" +[ScatterChart] - General purpose node for Scatter Charts + +A scatter plot (also called a scatterplot, scatter graph, scatter chart, scattergram, or scatter diagram) + is a type of plot or mathematical diagram using Cartesian coordinates to display values for typically two variables +for a set of data. If the points are coded (color/shape/size), one additional variable can be displayed. +The data are displayed as a collection of points, each having the value of one variable determining the position on +the horizontal axis and the value of the other variable determining the position on the vertical axis. + +/ source : Wikipedia / +""" + +# --------------------- + +func _get_property_list(): + return [ + # Chart Properties + { + "hint": PROPERTY_HINT_NONE, + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, + "name": "Chart_Properties/are_values_columns", + "type": TYPE_BOOL + }, + { + "hint": PROPERTY_HINT_RANGE, + "hint_string": "-1,100,1", + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, + "name": "Chart_Properties/labels_index", + "type": TYPE_INT + }, + { + "hint": PROPERTY_HINT_NONE, + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, + "name": "Chart_Properties/show_x_values_as_labels", + "type": TYPE_BOOL + }, + + # Chart Display + { + "hint": PROPERTY_HINT_RANGE, + "hint_string": "0.1,10", + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, + "name": "Chart_Display/x_decim", + "type": TYPE_REAL + }, + { + "hint": PROPERTY_HINT_RANGE, + "hint_string": "0.1,10", + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, + "name": "Chart_Display/y_decim", + "type": TYPE_REAL + }, + + # Chart Style + { + "hint": 24, + "hint_string": "%d/%d:%s"%[TYPE_INT, PROPERTY_HINT_ENUM, + PoolStringArray(PointShapes.keys()).join(",")], + "name": "Chart_Style/points_shape", + "type": TYPE_ARRAY, + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE + }, + { + "hint": PROPERTY_HINT_NONE, + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, + "name": "Chart_Style/function_colors", + "type": TYPE_COLOR_ARRAY + }, + { + "hint": PROPERTY_HINT_NONE, + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, + "name": "Chart_Style/box_color", + "type": TYPE_COLOR + }, + { + "hint": PROPERTY_HINT_NONE, + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, + "name": "Chart_Style/v_lines_color", + "type": TYPE_COLOR + }, + { + "hint": PROPERTY_HINT_NONE, + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, + "name": "Chart_Style/h_lines_color", + "type": TYPE_COLOR + }, + { + "class_name": "Font", + "hint": PROPERTY_HINT_RESOURCE_TYPE, + "hint_string": "Font", + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, + "name": "Chart_Style/font", + "type": TYPE_OBJECT + }, + { + "class_name": "Font", + "hint": PROPERTY_HINT_RESOURCE_TYPE, + "hint_string": "Font", + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, + "name": "Chart_Style/bold_font", + "type": TYPE_OBJECT + }, + { + "hint": PROPERTY_HINT_NONE, + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, + "name": "Chart_Style/font_color", + "type": TYPE_COLOR + }, + { + "hint": PROPERTY_HINT_ENUM, + "hint_string": PoolStringArray(Utilities.templates.keys()).join(","), + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, + "name": "Chart_Style/template", + "type": TYPE_INT + }, + + # Chart Modifiers + { + "hint": PROPERTY_HINT_NONE, + "usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE, + "name": "Chart_Modifiers/invert_chart", + "type": TYPE_BOOL + }, + ] + +func structure_datas(database : Array): + # @labels_index can be either a column or a row relative to x values + # @y_values can be either a column or a row relative to y values + are_values_columns = invert_chart != are_values_columns + match are_values_columns: + true: + for row in database.size(): + var t_vals : Array + for column in database[row].size(): + if column == labels_index: + var x_data = database[row][column] + if x_data.is_valid_float() or x_data.is_valid_integer(): + x_datas.append(x_data as float) + else: + x_datas.append(x_data.replace(",",".") as float) + else: + if row != 0: + var y_data = database[row][column] + if y_data.is_valid_float() or y_data.is_valid_integer(): + t_vals.append(y_data as float) + else: + t_vals.append(y_data.replace(",",".") as float) + else: + y_labels.append(str(database[row][column])) + if not t_vals.empty(): + y_datas.append(t_vals) + x_label = str(x_datas.pop_front()) + false: + for row in database.size(): + if row == labels_index: + x_datas = (database[row]) + x_label = x_datas.pop_front() as String + else: + var values = database[row] as Array + y_labels.append(values.pop_front() as String) + y_datas.append(values) + for data in y_datas: + for value in data.size(): + data[value] = data[value] as float + + # draw y labels + var to_order : Array + var to_order_min : Array + for cluster in y_datas.size(): + # define x_chors and y_chors + var ordered_cluster = y_datas[cluster] as Array + ordered_cluster.sort() + ordered_cluster = PoolIntArray(ordered_cluster) + var margin_max = ordered_cluster[ordered_cluster.size()-1] + var margin_min = ordered_cluster[0] + to_order.append(margin_max) + to_order_min.append(margin_min) + + to_order.sort() + to_order_min.sort() + var margin = to_order.pop_back() + if not origin_at_zero: + y_margin_min = to_order_min.pop_front() + v_dist = y_decim * pow(10.0,str(margin).length()-2) + var multi = 0 + var p = (v_dist*multi) + ((y_margin_min) if not origin_at_zero else 0) + y_chors.append(p as String) + while p < margin: + multi+=1 + p = (v_dist*multi) + ((y_margin_min) if not origin_at_zero else 0) + y_chors.append(p as String) + + # draw x_labels + if not show_x_values_as_labels: + to_order.clear() + to_order = x_datas as PoolIntArray + + to_order.sort() + margin = to_order.pop_back() + if not origin_at_zero: + x_margin_min = to_order.pop_front() + h_dist = x_decim * pow(10.0,str(margin).length()-2) + multi = 0 + p = (h_dist*multi) + ((x_margin_min) if not origin_at_zero else 0) + x_labels.append(p as String) + while p < margin: + multi+=1 + p = (h_dist*multi) + ((x_margin_min) if not origin_at_zero else 0) + x_labels.append(p as String) + +func build_chart(): + SIZE = get_size() + origin = Vector2(OFFSET.x,SIZE.y-OFFSET.y) + +func calculate_pass(): + if show_x_values_as_labels: + x_chors = x_datas as PoolStringArray + else: + x_chors = x_labels + + # calculate distance in pixel between 2 consecutive values/datas + x_pass = (SIZE.x - OFFSET.x) / (x_chors.size()-1) + y_pass = origin.y / (y_chors.size()-1) + +func calculate_coordinates(): + x_coordinates.clear() + y_coordinates.clear() + point_values.clear() + point_positions.clear() + + for cluster in y_datas: + var single_coordinates : Array + for value in cluster.size(): + if origin_at_zero: + single_coordinates.append((cluster[value]*y_pass)/v_dist) + else: + single_coordinates.append((cluster[value] - y_margin_min)*y_pass/v_dist) + y_coordinates.append(single_coordinates) + + if show_x_values_as_labels: + for x in x_datas.size(): + x_coordinates.append(x_pass*x) + else: + for x in x_datas.size(): + if origin_at_zero: + x_coordinates.append(x_datas[x]*x_pass/h_dist) + else: + x_coordinates.append((x_datas[x] - x_margin_min)*x_pass/h_dist) + + for f in functions: + point_values.append([]) + point_positions.append([]) + + for cluster in y_coordinates.size(): + for y in y_coordinates[cluster].size(): + if are_values_columns: + point_values[y].append([x_datas[cluster],y_datas[cluster][y]]) + point_positions[y].append(Vector2(x_coordinates[cluster]+origin.x,origin.y-y_coordinates[cluster][y])) + else: + point_values[cluster].append([x_datas[y],y_datas[cluster][y]]) + point_positions[cluster].append(Vector2(x_coordinates[y]+origin.x,origin.y-y_coordinates[cluster][y])) + +func _draw(): + clear_points() + + draw_grid() + draw_chart_outlines() + + var defined_colors : bool = false + if function_colors.size(): + defined_colors = true + + for _function in point_values.size(): + var PointContainer : Control = Control.new() + Points.add_child(PointContainer) + + for function_point in point_values[_function].size(): + var point : Point = point_node.instance() + point.connect("_point_pressed",self,"point_pressed") + point.connect("_mouse_entered",self,"show_data") + point.connect("_mouse_exited",self,"hide_data") + + point.create_point(points_shape[_function], function_colors[_function], + Color.white, point_positions[_function][function_point], + point.format_value(point_values[_function][function_point], false, false), + y_labels[_function] as String) + + PointContainer.add_child(point) + +func draw_grid(): + # ascisse + for p in x_chors.size(): + var point : Vector2 = origin+Vector2((p)*x_pass,0) + # v grid + draw_line(point,point-Vector2(0,SIZE.y-OFFSET.y),v_lines_color,0.2,true) + # ascisse + draw_line(point-Vector2(0,5),point,v_lines_color,1,true) + draw_string(font,point+Vector2(-const_width/2*x_chors[p].length(),font_size+const_height),x_chors[p],font_color) + + # ordinate + for p in y_chors.size(): + var point : Vector2 = origin-Vector2(0,(p)*y_pass) + # h grid + draw_line(point,point+Vector2(SIZE.x-OFFSET.x,0),h_lines_color,0.2,true) + # ordinate + draw_line(point,point+Vector2(5,0),h_lines_color,1,true) + draw_string(font,point-Vector2(y_chors[p].length()*const_width+font_size,-const_height),y_chors[p],font_color) + +func draw_chart_outlines(): + draw_line(origin,SIZE-Vector2(0,OFFSET.y),box_color,1,true) + draw_line(origin,Vector2(OFFSET.x,0),box_color,1,true) + draw_line(Vector2(OFFSET.x,0),Vector2(SIZE.x,0),box_color,1,true) + draw_line(Vector2(SIZE.x,0),SIZE-Vector2(0,OFFSET.y),box_color,1,true) diff --git a/addons/easy_charts/file.samples/scatter.csv b/addons/easy_charts/file.samples/scatter (columns).csv similarity index 97% rename from addons/easy_charts/file.samples/scatter.csv rename to addons/easy_charts/file.samples/scatter (columns).csv index 20fb926..8143bf9 100644 --- a/addons/easy_charts/file.samples/scatter.csv +++ b/addons/easy_charts/file.samples/scatter (columns).csv @@ -1,101 +1,101 @@ -x;y1;y2 -50,05732879;116,61234;90,55501121 -53,23295081;105,6457011;47,41275029 -47,28686102;95,61795061;36,33108959 -53,0250493;106,4552515;85,4302022 -57,10271278;111,9088163;39,80610352 -40,71992494;78,27201391;79,55208897 -55,62752569;122,7859036;94,15837791 -54,17819455;107,9817124;39,80351785 -40,8610371;107,92736;61,0663229 -54,06159551;110,0647495;93,00315399 -64,35130431;134,5556319;114,2043276 -49,29181834;88,18806126;64,89624292 -44,63823505;102,8364528;108,1982178 -58,62487836;126,0319541;78,40707574 -55,78566879;95,81224088;75,02657209 -50,76570445;94,73143882;39,96573437 -48,45339259;92,86957776;88,41618517 -49,40446173;70,21723661;9,81277488 -48,34604978;97,87616091;40,53011113 -43,62470151;103,7308337;97,10613219 -50,8877046;117,1558657;62,2681611 -52,25082012;103,3922334;79,14141328 -50,35929987;87,68936817;19,3300683 -46,42761861;90,1655428;67,73792419 -55,67926036;93,17073084;69,49147048 -61,72779383;142,8458546;106,1180608 -58,99808851;102,3529041;27,35481559 -43,6620999;77,36405233;17,70195243 -55,42639088;121,0878726;78,66148172 -58,79311097;111,8698686;49,07675763 -50,71073988;106,5406487;97,82990882 -45,57346035;104,7456111;56,17215075 -45,7981314;83,9963622;75,1982308 -46,46484131;82,94617575;77,48133444 -57,9492853;144,9994608;129,0501755 -48,3999722;72,83510082;30,43512862 -54,2097076;114,7785416;82,568834 -46,67548966;95,25014621;37,57465655 -38,21698894;88,21423442;64,99724548 -51,95614673;90,93548401;66,97933728 -51,22522594;105,1106925;101,8854666 -52,84741658;105,0503069;75,20289032 -54,78984594;109,9873973;87,19755136 -52,00296104;101,442706;89,43974496 -51,43150193;88,40178257;36,97028064 -47,40407943;89,44141735;55,03733792 -51,6749185;96,54147702;31,86655852 -40,74049925;83,85842913;79,11792988 -49,82155418;112,9093356;96,08778142 -57,57763531;115,7709369;72,19330159 -51,49652924;127,9054098;118,4088806 -53,10710725;112,6337002;90,52659295 -42,93645994;102,8111834;102,8747235 -44,14066275;78,88897631;41,74831356 -46,60936983;98,64046333;54,0310935 -47,41415307;95,89594769;58,48179462 -47,99032677;106,3655192;102,3751924 -50,68360992;106,9507457;44,26713578 -50,57070899;83,19613908;61,62543009 -57,14992785;115,5596634;40,40973555 -50,45105658;97,6950217;64,24396512 -46,76779029;99,20006513;60,43227484 -50,49802495;99,43092076;87,93289581 -51,52523583;84,24653091;17,72129508 -40,72692657;102,1715902;67,44466363 -54,96034411;111,5346596;82,57431549 -48,86213774;101,4305303;58,56839256 -52,76166432;108,021472;60,25980768 -55,46249302;83,51709356;51,05460054 -51,78186354;79,45870906;11,67684552 -54,7256505;108,6386184;58,9129679 -55,03288163;112,1224042;72,08952257 -52,83806054;104,53718;101,6991195 -45,86692682;91,39643691;29,52951009 -49,67284006;110,1908352;88,51799514 -53,18552185;119,0135337;113,8280119 -41,69070011;62,78988486;53,09918475 -53,36320564;118,5841731;108,2209675 -44,27369117;74,58882658;69,31513541 -49,92198979;76,93363417;52,01164438 -44,91221355;82,33766098;44,42544743 -37,92316535;68,0076039;17,08443855 -50,10889447;103,6209931;49,51209863 -44,20348231;84,19989748;57,99641517 -57,11433271;102,0528226;84,93848989 -41,90764861;109,1360517;50,22840309 -51,88278094;89,01870776;69,13592682 -44,31355843;81,19982464;76,88626621 -44,25547817;72,64564157;53,3901634 -48,93179315;107,7773458;40,84555265 -39,36849865;59,39799005;68,0294914 -53,33338181;99,04871654;92,71533473 -61,63696872;129,3884947;76,75152598 -46,40148646;102,9511032;92,54961674 -43,81949012;75,15673812;44,337248 -53,78046359;85,87008695;50,08962336 -41,27977392;60,49700348;50,21722956 -52,32206122;106,8462547;57,52419348 -41,36660384;105,0465099;78,67990606 -47,62423286;96,62455823;84,00032537 +x;y1;y2 +50,05732879;116,61234;90,55501121 +53,23295081;105,6457011;47,41275029 +47,28686102;95,61795061;36,33108959 +53,0250493;106,4552515;85,4302022 +57,10271278;111,9088163;39,80610352 +40,71992494;78,27201391;79,55208897 +55,62752569;122,7859036;94,15837791 +54,17819455;107,9817124;39,80351785 +40,8610371;107,92736;61,0663229 +54,06159551;110,0647495;93,00315399 +64,35130431;134,5556319;114,2043276 +49,29181834;88,18806126;64,89624292 +44,63823505;102,8364528;108,1982178 +58,62487836;126,0319541;78,40707574 +55,78566879;95,81224088;75,02657209 +50,76570445;94,73143882;39,96573437 +48,45339259;92,86957776;88,41618517 +49,40446173;70,21723661;9,81277488 +48,34604978;97,87616091;40,53011113 +43,62470151;103,7308337;97,10613219 +50,8877046;117,1558657;62,2681611 +52,25082012;103,3922334;79,14141328 +50,35929987;87,68936817;19,3300683 +46,42761861;90,1655428;67,73792419 +55,67926036;93,17073084;69,49147048 +61,72779383;142,8458546;106,1180608 +58,99808851;102,3529041;27,35481559 +43,6620999;77,36405233;17,70195243 +55,42639088;121,0878726;78,66148172 +58,79311097;111,8698686;49,07675763 +50,71073988;106,5406487;97,82990882 +45,57346035;104,7456111;56,17215075 +45,7981314;83,9963622;75,1982308 +46,46484131;82,94617575;77,48133444 +57,9492853;144,9994608;129,0501755 +48,3999722;72,83510082;30,43512862 +54,2097076;114,7785416;82,568834 +46,67548966;95,25014621;37,57465655 +38,21698894;88,21423442;64,99724548 +51,95614673;90,93548401;66,97933728 +51,22522594;105,1106925;101,8854666 +52,84741658;105,0503069;75,20289032 +54,78984594;109,9873973;87,19755136 +52,00296104;101,442706;89,43974496 +51,43150193;88,40178257;36,97028064 +47,40407943;89,44141735;55,03733792 +51,6749185;96,54147702;31,86655852 +40,74049925;83,85842913;79,11792988 +49,82155418;112,9093356;96,08778142 +57,57763531;115,7709369;72,19330159 +51,49652924;127,9054098;118,4088806 +53,10710725;112,6337002;90,52659295 +42,93645994;102,8111834;102,8747235 +44,14066275;78,88897631;41,74831356 +46,60936983;98,64046333;54,0310935 +47,41415307;95,89594769;58,48179462 +47,99032677;106,3655192;102,3751924 +50,68360992;106,9507457;44,26713578 +50,57070899;83,19613908;61,62543009 +57,14992785;115,5596634;40,40973555 +50,45105658;97,6950217;64,24396512 +46,76779029;99,20006513;60,43227484 +50,49802495;99,43092076;87,93289581 +51,52523583;84,24653091;17,72129508 +40,72692657;102,1715902;67,44466363 +54,96034411;111,5346596;82,57431549 +48,86213774;101,4305303;58,56839256 +52,76166432;108,021472;60,25980768 +55,46249302;83,51709356;51,05460054 +51,78186354;79,45870906;11,67684552 +54,7256505;108,6386184;58,9129679 +55,03288163;112,1224042;72,08952257 +52,83806054;104,53718;101,6991195 +45,86692682;91,39643691;29,52951009 +49,67284006;110,1908352;88,51799514 +53,18552185;119,0135337;113,8280119 +41,69070011;62,78988486;53,09918475 +53,36320564;118,5841731;108,2209675 +44,27369117;74,58882658;69,31513541 +49,92198979;76,93363417;52,01164438 +44,91221355;82,33766098;44,42544743 +37,92316535;68,0076039;17,08443855 +50,10889447;103,6209931;49,51209863 +44,20348231;84,19989748;57,99641517 +57,11433271;102,0528226;84,93848989 +41,90764861;109,1360517;50,22840309 +51,88278094;89,01870776;69,13592682 +44,31355843;81,19982464;76,88626621 +44,25547817;72,64564157;53,3901634 +48,93179315;107,7773458;40,84555265 +39,36849865;59,39799005;68,0294914 +53,33338181;99,04871654;92,71533473 +61,63696872;129,3884947;76,75152598 +46,40148646;102,9511032;92,54961674 +43,81949012;75,15673812;44,337248 +53,78046359;85,87008695;50,08962336 +41,27977392;60,49700348;50,21722956 +52,32206122;106,8462547;57,52419348 +41,36660384;105,0465099;78,67990606 +47,62423286;96,62455823;84,00032537 diff --git a/addons/easy_charts/file.samples/scatter (columns).csv.import b/addons/easy_charts/file.samples/scatter (columns).csv.import new file mode 100644 index 0000000..997c493 --- /dev/null +++ b/addons/easy_charts/file.samples/scatter (columns).csv.import @@ -0,0 +1,10 @@ +[remap] + +importer="csv" +type="TextFile" + +[deps] + +source_file="res://addons/easy_charts/file.samples/scatter (columns).csv" +[params] + From f714b948e33b11c900fbd5651eb64dcf880cea65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Santilio?= Date: Thu, 31 Dec 2020 17:43:37 +0100 Subject: [PATCH 10/13] update v0.5.3 --- README.md | 21 +++------------------ imgs/real_time_line.gif | Bin 0 -> 159335 bytes 2 files changed, 3 insertions(+), 18 deletions(-) create mode 100644 imgs/real_time_line.gif diff --git a/README.md b/README.md index 1c4abb7..3634716 100644 --- a/README.md +++ b/README.md @@ -2,17 +2,13 @@
react - - # Easy Charts A library of Charts plotted in Control, 2D and 3D nodes to visualize general purpose datasets. - Author: *"Nicolo (fenix) Santilio"* -Version: *0.5.0* +Version: *0.5.3* Wiki: *[wip](https://github.com/fenix-hub/godot-engine.easy-charts/wiki)* Godot Version: *3.2stable* - ## What is this? *Easy Charts* is a collection of Control, 2D and 3D nodes to plot charts. This plugin was born from the personal necessity to plot some charts and tables for my university degree project. @@ -21,53 +17,42 @@ Charts are really useful when it comes to visually represent values in a powerfu If you need to plot a chart with some values in it and just take a screenshot, or use it in your Godot Engine's game or project, you've come to the right place. ![st](imgs/startup_company2.jpg) [*Startup Company*](https://store.steampowered.com/app/606800/Startup_Company/) - ![wa](imgs/workplace_analytics.png) [*Microsoft Workplace Analytics*](https://www.microsoft.com/microsoft-365/partners/workplaceanalytics) - - ## How does it work? There is a [WIKI](https://github.com/fenix-hub/godot-engine.easy-charts/wiki) with some tutorials, even if it is a work in progress. I'll make some videos as soon as possible. - # Available Charts and when to use them This library offers a set of charts for each main Godot Node: - **Control Nodes:** "Control Charts" are fast Charts that can be plotted in a Control space, such as UIs or Control user interactable areas. They offer basic Control properties, such as Margins, size inheritance and control. No animations, no real time changes, just charts. - **2D Nodes:** "2D Charts" are a set of Charts which can be Used in 2D spaces. They offer additional tools, such as animations and real time changes in editor. They can be used to implement more aesthetic charts in 2D contexts. - **[wip] 3D Nodes:** "3D Charts" are a set of Charts which can be Used in both 2D and 3D spaces. They offer the possibility to plot 3D datasets, which are common in machine learning contexts or just data analysis. A Camera Control will also be available, which can be used to move around the chart. - *Available Charts*: - LineChart [Control, 2D, wip 3D] -- BarChart [Control, 2D, wipr 3D] +- ColumnChart [Control, 2D, wipr 3D] - ScatterChart [wip Control, wip 2D, 3D] - Piechart [Control] - RadarChart [Control] - *Work in progress*: - Area Chart - Donut Chart -- Radar Chart - Bubble Chart - Parliament Chart - ### Some Examples +![example_LineChart_realtime](imgs/real_time_line.gif) ![example_Piechart](imgs/pie_chart_realtime.gif) ![exampleradar](imgs/radar.png) ![example01](imgs/scatter.gif) ![example02](imgs/example02.png) ![example03](imgs/example03.gif) - ##### Some references for charts and plots [Flourish](https://app.flourish.studio/projects) [Chart.js](https://www.chartjs.org/samples/latest/) [Google Charts](https://developers.google.com/chart) - - # Disclaimer This addon was built for a **personal use** intention. It was released as an open source plugin in the hope that it could be useful to the Godot Engine Community. As a "work in progress" project, there is *no warranty* for any eventual issue and bug that may broke your project. I don't assume any responsibility for possible corruptions of your project. It is always advisable to keep a copy of your project and check any changes you make in your Github repository. - ----------------- > This text file was created via [TextEditor Integration](https://github.com/fenix-hub/godot-engine.text-editor) inside Godot Engine's Editor. > This text file was pushed via [GitHub Integration](https://github.com/fenix-hub/godot-engine.github-integretion) inside Godot Engine's Editor. diff --git a/imgs/real_time_line.gif b/imgs/real_time_line.gif new file mode 100644 index 0000000000000000000000000000000000000000..9665cfe215f9fc25e19fb73614181b5a01b0eefa GIT binary patch literal 159335 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℞2{t8QPA-r;r&6y@I(`&EkCbC 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 literal 0 HcmV?d00001 From b25e692f140af2d231c718ed712b0651e1f87a9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Santilio?= Date: Thu, 31 Dec 2020 17:45:50 +0100 Subject: [PATCH 11/13] update v0.5.3 readme --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3634716..626160d 100644 --- a/README.md +++ b/README.md @@ -26,14 +26,14 @@ I'll make some videos as soon as possible. This library offers a set of charts for each main Godot Node: - **Control Nodes:** "Control Charts" are fast Charts that can be plotted in a Control space, such as UIs or Control user interactable areas. They offer basic Control properties, such as Margins, size inheritance and control. No animations, no real time changes, just charts. - **2D Nodes:** "2D Charts" are a set of Charts which can be Used in 2D spaces. They offer additional tools, such as animations and real time changes in editor. They can be used to implement more aesthetic charts in 2D contexts. -- **[wip] 3D Nodes:** "3D Charts" are a set of Charts which can be Used in both 2D and 3D spaces. They offer the possibility to plot 3D datasets, which are common in machine learning contexts or just data analysis. A Camera Control will also be available, which can be used to move around the chart. -*Available Charts*: +- **[wip] 3D Nodes:** "3D Charts" are a set of Charts which can be Used in both 2D and 3D spaces. They offer the possibility to plot 3D datasets, which are common in machine learning contexts or just data analysis. A Camera Control will also be available, which can be used to move around the chart. +### Available Charts - LineChart [Control, 2D, wip 3D] - ColumnChart [Control, 2D, wipr 3D] - ScatterChart [wip Control, wip 2D, 3D] - Piechart [Control] - RadarChart [Control] -*Work in progress*: +### WIP Charts - Area Chart - Donut Chart - Bubble Chart From 9928b6f6893890f53515617f3e85982f46f75db3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Santilio?= Date: Thu, 31 Dec 2020 17:49:06 +0100 Subject: [PATCH 12/13] Update README.md --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 626160d..b6f21ba 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,10 @@ -*Want to stay updated or having an issue?* - - react + + +react + + # Easy Charts A library of Charts plotted in Control, 2D and 3D nodes to visualize general purpose datasets. Author: *"Nicolo (fenix) Santilio"* From a4a1afc816c0219736378c96beb4a92d67ba9341 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Santilio?= Date: Thu, 31 Dec 2020 17:49:59 +0100 Subject: [PATCH 13/13] Update README.md --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b6f21ba..f3936c8 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ - - +

+ react +