Merge pull request #23 from fenix-hub/dev

Dev
This commit is contained in:
Nicolò Santilio 2020-11-09 20:15:49 +01:00 committed by GitHub
commit 33ec7df578
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 448 additions and 404 deletions

View File

@ -9,7 +9,7 @@
A library of Charts plotted in Control, 2D and 3D nodes to visualize general purpose datasets. A library of Charts plotted in Control, 2D and 3D nodes to visualize general purpose datasets.
Author: *"Nicolo (fenix) Santilio"* Author: *"Nicolo (fenix) Santilio"*
Version: *0.4.2* Version: *0.4.5*
Wiki: *[wip]* Wiki: *[wip]*
Godot Version: *3.2stable* Godot Version: *3.2stable*

View File

@ -1,5 +1,6 @@
tool tool
extends Chart extends Chart
class_name BarChart
""" """
[BarChart] - General purpose node for Bar Charts [BarChart] - General purpose node for Bar Charts
@ -374,7 +375,7 @@ func draw_chart_outlines():
func create_legend(): func create_legend():
legend.clear() legend.clear()
for function in functions: for function in functions:
var function_legend = FunctionLegend.instance() var function_legend = LegendElement.instance()
var f_name : String var f_name : String
if invert_chart: if invert_chart:
f_name = x_datas[function] as String f_name = x_datas[function] as String

View File

@ -82,10 +82,10 @@ __meta__ = {
[node name="PointData" parent="PointData" index="0"] [node name="PointData" parent="PointData" index="0"]
visible = false visible = false
margin_left = -223.769 margin_left = 41.5506
margin_top = -45.5654 margin_top = -149.501
margin_right = -223.906 margin_right = 41.4136
margin_bottom = -44.7655 margin_bottom = -148.701
theme = SubResource( 1 ) theme = SubResource( 1 )
[editable path="PointData"] [editable path="PointData"]

View File

@ -1,5 +1,6 @@
tool tool
extends Chart2D extends Chart2D
class_name BarChart2D
""" """
[BarChart2D] - General purpose node for Bar Charts [BarChart2D] - General purpose node for Bar Charts

View File

@ -54,5 +54,9 @@ default_color = Color( 0.117647, 0.117647, 0.117647, 1 )
[node name="PointData" parent="PointData" index="0"] [node name="PointData" parent="PointData" index="0"]
visible = false visible = false
margin_left = 48.6217
margin_top = -122.631
margin_right = 48.4856
margin_bottom = -121.831
[editable path="PointData"] [editable path="PointData"]

View File

@ -1,5 +1,6 @@
tool tool
extends Chart extends Chart
class_name LineChart
# [Linechart] - General purpose node for Line Charts # [Linechart] - General purpose node for Line Charts
# A line chart or line plot or line graph or curve chart is a type of chart which # A line chart or line plot or line graph or curve chart is a type of chart which

View File

@ -1,5 +1,6 @@
tool tool
extends Chart2D extends Chart2D
class_name LineChart2D
# [Linechart2D] - General purpose node for Line Charts # [Linechart2D] - General purpose node for Line Charts
# A line chart or line plot or line graph or curve chart is a type of chart which # A line chart or line plot or line graph or curve chart is a type of chart which

View File

@ -19,7 +19,7 @@ A line chart is often used to visualize a trend in data over intervals of time
a time series thus the line is often drawn chronologically. a time series thus the line is often drawn chronologically.
In these cases they are known as run charts." In these cases they are known as run charts."
} }
function_colors = [ "#1e1e1e", "#1e1e1e", "#1e1e1e", "#1e1e1e" ] function_colors = PoolColorArray( 0.117647, 0.117647, 0.117647, 1, 0.117647, 0.117647, 0.117647, 1, 0.117647, 0.117647, 0.117647, 1, 0.117647, 0.117647, 0.117647, 1 )
[node name="Grid" type="Node2D" parent="."] [node name="Grid" type="Node2D" parent="."]
@ -34,7 +34,7 @@ width = 1.0
default_color = Color( 0.792157, 0.792157, 0.792157, 1 ) default_color = Color( 0.792157, 0.792157, 0.792157, 1 )
[node name="Outlines" type="Line2D" parent="."] [node name="Outlines" type="Line2D" parent="."]
points = PoolVector2Array( 0, 0, 2, 0, 2, 2, 0, 2, 0, 0 ) points = PoolVector2Array( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 )
width = 2.0 width = 2.0
default_color = Color( 0.117647, 0.117647, 0.117647, 1 ) default_color = Color( 0.117647, 0.117647, 0.117647, 1 )
@ -55,5 +55,9 @@ default_color = Color( 0.117647, 0.117647, 0.117647, 1 )
[node name="PointData" parent="PointData" index="0"] [node name="PointData" parent="PointData" index="0"]
visible = false visible = false
margin_left = 52.8643
margin_top = -115.56
margin_right = 52.7283
margin_bottom = -114.76
[editable path="PointData"] [editable path="PointData"]

View File

@ -54,9 +54,9 @@ __meta__ = {
[node name="PointData" parent="PointData" index="0"] [node name="PointData" parent="PointData" index="0"]
visible = false visible = false
margin_left = -148.407 margin_left = -449.181
margin_top = -156.174 margin_top = -266.881
margin_right = -148.543 margin_right = -449.318
margin_bottom = -155.375 margin_bottom = -266.082
[editable path="PointData"] [editable path="PointData"]

View File

@ -1,5 +1,6 @@
tool tool
extends Chart extends Chart
class_name PieChart
var should_draw : bool = false var should_draw : bool = false
var area_angles : Array var area_angles : Array
@ -176,9 +177,15 @@ func calculate_coordinates():
func calculate_circle_arc_polygon(center : Vector2, radius : float, angle_from : float, angle_to : float, color : Color) -> PoolVector2Array: func calculate_circle_arc_polygon(center : Vector2, radius : float, angle_from : float, angle_to : float, color : Color) -> PoolVector2Array:
var nb_points : int = 32 var nb_points : int = 32
var points_arc : PoolVector2Array = PoolVector2Array() var points_arc : PoolVector2Array = PoolVector2Array()
# var chord_angle : float = ((angle_to - angle_from)/2)+angle_from
# angle_from += 0.2
# angle_to -= 0.2
# var displacement : Vector2 = Vector2(cos(deg2rad(chord_angle)), sin(deg2rad(chord_angle-180))).normalized()*10
# print(displacement)
# center += displacement
# radius+=displacement.length()
points_arc.push_back(center) points_arc.push_back(center)
var colors : PoolColorArray = PoolColorArray([color]) var colors : PoolColorArray = PoolColorArray([color])
for i in range(nb_points + 1): for i in range(nb_points + 1):
var angle_point = deg2rad(angle_from + i * (angle_to - angle_from) / nb_points - 90) var angle_point = deg2rad(angle_from + i * (angle_to - angle_from) / nb_points - 90)
points_arc.push_back(center + Vector2(cos(angle_point), sin(angle_point)) * radius) points_arc.push_back(center + Vector2(cos(angle_point), sin(angle_point)) * radius)

View File

@ -1,5 +1,6 @@
tool tool
extends Chart extends Chart
class_name RadarChart
""" """
[RadarChart] - General purpose node for Radar Charts [RadarChart] - General purpose node for Radar Charts

View File

@ -21,7 +21,6 @@ uninformative, but various heuristics, such as algorithms that plot data as the
total area, can be applied to sort the variables (axes) into relative positions that reveal 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." distinct correlations, trade-offs, and a multitude of other comparative measures."
} }
delimiter = ";"
Chart_Properties/are_values_columns = false Chart_Properties/are_values_columns = false
Chart_Properties/labels_index = 0 Chart_Properties/labels_index = 0
Chart_Properties/function_names_index = 0 Chart_Properties/function_names_index = 0
@ -78,10 +77,10 @@ __meta__ = {
[node name="PointData" parent="PointData" index="0"] [node name="PointData" parent="PointData" index="0"]
visible = false visible = false
margin_left = -230.138 margin_left = 64.178
margin_top = -125.6 margin_top = -142.43
margin_right = -230.275 margin_right = 64.041
margin_bottom = -124.801 margin_bottom = -141.631
theme = SubResource( 1 ) theme = SubResource( 1 )
[editable path="PointData"] [editable path="PointData"]

View File

@ -1,5 +1,6 @@
tool tool
extends Chart extends Chart
class_name ScatterChart
""" """
[ScatterChart] - General purpose node for Scatter Charts [ScatterChart] - General purpose node for Scatter Charts

View File

@ -76,10 +76,10 @@ __meta__ = {
[node name="PointData" parent="PointData" index="0"] [node name="PointData" parent="PointData" index="0"]
visible = false visible = false
margin_left = -19.0 margin_left = 58.5211
margin_top = -63.0 margin_top = -187.685
margin_right = -19.136 margin_right = 58.3851
margin_bottom = -62.2 margin_bottom = -186.885
theme = SubResource( 1 ) theme = SubResource( 1 )
[editable path="PointData"] [editable path="PointData"]

View File

@ -1,5 +1,6 @@
tool tool
extends Chart2D extends Chart2D
class_name ScatterChart2D
""" """
[ScatterChart2D] - General purpose node for Scatter Charts [ScatterChart2D] - General purpose node for Scatter Charts
@ -113,434 +114,434 @@ signal chart_plotted(chart)
signal point_pressed(point) signal point_pressed(point)
func _point_plotted(): func _point_plotted():
pass pass
func _ready(): func _ready():
_get_children() _get_children()
func _get_children(): func _get_children():
OutlinesTween = $OutlinesTween OutlinesTween = $OutlinesTween
PointTween = $PointTween PointTween = $PointTween
Functions = $Functions Functions = $Functions
GridTween = $GridTween GridTween = $GridTween
PointData = $PointData/PointData PointData = $PointData/PointData
Outlines = $Outlines Outlines = $Outlines
Grid = $Grid Grid = $Grid
func _set_size(size : Vector2): func _set_size(size : Vector2):
SIZE = size SIZE = size
build_chart() build_chart()
if Engine.editor_hint: if Engine.editor_hint:
_get_children() _get_children()
Outlines.set_point_position(0,Vector2(origin.x,0)) Outlines.set_point_position(0,Vector2(origin.x,0))
Outlines.set_point_position(1,Vector2(SIZE.x,0)) Outlines.set_point_position(1,Vector2(SIZE.x,0))
Outlines.set_point_position(2,Vector2(SIZE.x,origin.y)) Outlines.set_point_position(2,Vector2(SIZE.x,origin.y))
Outlines.set_point_position(3,origin) Outlines.set_point_position(3,origin)
Outlines.set_point_position(4,Vector2(origin.x,0)) Outlines.set_point_position(4,Vector2(origin.x,0))
Grid.get_node("VLine").set_point_position(0,Vector2((OFFSET.x+SIZE.x)/2,0)) Grid.get_node("VLine").set_point_position(0,Vector2((OFFSET.x+SIZE.x)/2,0))
Grid.get_node("VLine").set_point_position(1,Vector2((OFFSET.x+SIZE.x)/2,origin.y)) Grid.get_node("VLine").set_point_position(1,Vector2((OFFSET.x+SIZE.x)/2,origin.y))
Grid.get_node("HLine").set_point_position(0,Vector2(origin.x,origin.y/2)) Grid.get_node("HLine").set_point_position(0,Vector2(origin.x,origin.y/2))
Grid.get_node("HLine").set_point_position(1,Vector2(SIZE.x,origin.y/2)) Grid.get_node("HLine").set_point_position(1,Vector2(SIZE.x,origin.y/2))
func clear(): func clear():
Outlines.points = [] Outlines.points = []
Grid.get_node("HLine").queue_free() Grid.get_node("HLine").queue_free()
Grid.get_node("VLine").queue_free() Grid.get_node("VLine").queue_free()
func load_font(): func load_font():
if font != null: if font != null:
font_size = font.get_height() font_size = font.get_height()
var theme : Theme = Theme.new() var theme : Theme = Theme.new()
theme.set_default_font(font) theme.set_default_font(font)
PointData.set_theme(theme) PointData.set_theme(theme)
else: else:
var lbl = Label.new() var lbl = Label.new()
font = lbl.get_font("") font = lbl.get_font("")
lbl.free() lbl.free()
if bold_font != null: if bold_font != null:
PointData.Data.set("custom_fonts/font",bold_font) PointData.Data.set("custom_fonts/font",bold_font)
func _plot(source_ : String, delimiter_ : String, are_values_columns_ : bool, x_values_index_ : int): func _plot(source_ : String, delimiter_ : String, are_values_columns_ : bool, x_values_index_ : int):
randomize() randomize()
clear() clear()
load_font() load_font()
PointData.hide() PointData.hide()
datas = read_datas(source_,delimiter_) datas = read_datas(source_,delimiter_)
count_functions() count_functions()
structure_datas(datas,are_values_columns_,x_values_index_) structure_datas(datas,are_values_columns_,x_values_index_)
build_chart() build_chart()
calculate_pass() calculate_pass()
calculate_coordinates() calculate_coordinates()
calculate_colors() calculate_colors()
draw_chart() draw_chart()
create_legend() create_legend()
emit_signal("chart_plotted", self) emit_signal("chart_plotted", self)
func plot(): func plot():
randomize() randomize()
clear() clear()
load_font() load_font()
PointData.hide() PointData.hide()
if source == "" or source == null: 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) 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 return
datas = read_datas(source,delimiter) datas = read_datas(source,delimiter)
count_functions() count_functions()
structure_datas(datas,are_values_columns,x_values_index) structure_datas(datas,are_values_columns,x_values_index)
build_chart() build_chart()
calculate_pass() calculate_pass()
calculate_coordinates() calculate_coordinates()
calculate_colors() calculate_colors()
draw_chart() draw_chart()
create_legend() create_legend()
emit_signal("chart_plotted", self) emit_signal("chart_plotted", self)
func calculate_colors(): func calculate_colors():
if function_colors.empty() or function_colors.size() < functions: if function_colors.empty() or function_colors.size() < functions:
for function in functions: for function in functions:
function_colors.append(Color("#1e1e1e") as Color) function_colors.append(Color("#1e1e1e") as Color)
func draw_chart(): func draw_chart():
draw_outlines() draw_outlines()
draw_v_grid() draw_v_grid()
draw_h_grid() draw_h_grid()
draw_functions() draw_functions()
func draw_outlines(): func draw_outlines():
if boxed: if boxed:
Outlines.set_default_color(box_color) Outlines.set_default_color(box_color)
OutlinesTween.interpolate_method(Outlines,"add_point", OutlinesTween.interpolate_method(Outlines,"add_point",
Vector2(origin.x,0),Vector2(SIZE.x,0),drawing_duration*0.5,Tween.TRANS_QUINT,Tween.EASE_OUT) Vector2(origin.x,0),Vector2(SIZE.x,0),drawing_duration*0.5,Tween.TRANS_QUINT,Tween.EASE_OUT)
OutlinesTween.start() OutlinesTween.start()
yield(OutlinesTween,"tween_all_completed") yield(OutlinesTween,"tween_all_completed")
OutlinesTween.interpolate_method(Outlines,"add_point", OutlinesTween.interpolate_method(Outlines,"add_point",
Vector2(SIZE.x,0),Vector2(SIZE.x,origin.y),drawing_duration*0.5,Tween.TRANS_QUINT,Tween.EASE_OUT) Vector2(SIZE.x,0),Vector2(SIZE.x,origin.y),drawing_duration*0.5,Tween.TRANS_QUINT,Tween.EASE_OUT)
OutlinesTween.start() OutlinesTween.start()
yield(OutlinesTween,"tween_all_completed") yield(OutlinesTween,"tween_all_completed")
OutlinesTween.interpolate_method(Outlines,"add_point", OutlinesTween.interpolate_method(Outlines,"add_point",
Vector2(SIZE.x,origin.y),origin,drawing_duration*0.5,Tween.TRANS_QUINT,Tween.EASE_OUT) Vector2(SIZE.x,origin.y),origin,drawing_duration*0.5,Tween.TRANS_QUINT,Tween.EASE_OUT)
OutlinesTween.start() OutlinesTween.start()
yield(OutlinesTween,"tween_all_completed") yield(OutlinesTween,"tween_all_completed")
OutlinesTween.interpolate_method(Outlines,"add_point", OutlinesTween.interpolate_method(Outlines,"add_point",
origin,Vector2(origin.x,0),drawing_duration*0.5,Tween.TRANS_QUINT,Tween.EASE_OUT) origin,Vector2(origin.x,0),drawing_duration*0.5,Tween.TRANS_QUINT,Tween.EASE_OUT)
OutlinesTween.start() OutlinesTween.start()
yield(OutlinesTween,"tween_all_completed") yield(OutlinesTween,"tween_all_completed")
func draw_v_grid(): func draw_v_grid():
for p in x_chors.size(): for p in x_chors.size():
var point : Vector2 = origin+Vector2((p)*x_pass,0) var point : Vector2 = origin+Vector2((p)*x_pass,0)
var v_grid : Line2D = Line2D.new() var v_grid : Line2D = Line2D.new()
Grid.add_child(v_grid) Grid.add_child(v_grid)
v_grid.set_width(1) v_grid.set_width(1)
v_grid.set_default_color(v_lines_color) v_grid.set_default_color(v_lines_color)
add_label(point+Vector2(-const_width/2*x_chors[p].length(),font_size/2), x_chors[p]) add_label(point+Vector2(-const_width/2*x_chors[p].length(),font_size/2), x_chors[p])
GridTween.interpolate_method(v_grid,"add_point",point,point-Vector2(0,SIZE.y-OFFSET.y),drawing_duration/(x_chors.size()),Tween.TRANS_EXPO,Tween.EASE_OUT) GridTween.interpolate_method(v_grid,"add_point",point,point-Vector2(0,SIZE.y-OFFSET.y),drawing_duration/(x_chors.size()),Tween.TRANS_EXPO,Tween.EASE_OUT)
GridTween.start() GridTween.start()
yield(GridTween,"tween_all_completed") yield(GridTween,"tween_all_completed")
func draw_h_grid(): func draw_h_grid():
for p in y_chors.size(): for p in y_chors.size():
var point : Vector2 = origin-Vector2(0,(p)*y_pass) var point : Vector2 = origin-Vector2(0,(p)*y_pass)
var h_grid : Line2D = Line2D.new() var h_grid : Line2D = Line2D.new()
Grid.add_child(h_grid) Grid.add_child(h_grid)
h_grid.set_width(1) h_grid.set_width(1)
h_grid.set_default_color(h_lines_color) h_grid.set_default_color(h_lines_color)
add_label(point-Vector2(y_chors[p].length()*const_width+font_size,font_size/2), y_chors[p]) add_label(point-Vector2(y_chors[p].length()*const_width+font_size,font_size/2), y_chors[p])
GridTween.interpolate_method(h_grid,"add_point",point,Vector2(SIZE.x,point.y),drawing_duration/(y_chors.size()),Tween.TRANS_EXPO,Tween.EASE_OUT) GridTween.interpolate_method(h_grid,"add_point",point,Vector2(SIZE.x,point.y),drawing_duration/(y_chors.size()),Tween.TRANS_EXPO,Tween.EASE_OUT)
GridTween.start() GridTween.start()
yield(GridTween,"tween_all_completed") yield(GridTween,"tween_all_completed")
func add_label(point : Vector2, text : String): func add_label(point : Vector2, text : String):
var lbl : Label = Label.new() var lbl : Label = Label.new()
if font != null: if font != null:
lbl.set("custom_fonts/font",font) lbl.set("custom_fonts/font",font)
lbl.set("custom_colors/font_color",font_color) lbl.set("custom_colors/font_color",font_color)
Grid.add_child(lbl) Grid.add_child(lbl)
lbl.rect_position = point lbl.rect_position = point
lbl.set_text(text) lbl.set_text(text)
func draw_functions(): func draw_functions():
for function in point_positions.size(): for function in point_positions.size():
draw_function(function,point_positions[function]) draw_function(function,point_positions[function])
func draw_function(f_index : int, function : Array): func draw_function(f_index : int, function : Array):
var pointv : Point var pointv : Point
for point in function.size(): for point in function.size():
pointv = point_node.instance() pointv = point_node.instance()
Functions.add_child(pointv) Functions.add_child(pointv)
pointv.connect("_mouse_entered",self,"show_data") pointv.connect("_mouse_entered",self,"show_data")
pointv.connect("_mouse_exited",self,"hide_data") pointv.connect("_mouse_exited",self,"hide_data")
pointv.connect("_point_pressed",self,"point_pressed") pointv.connect("_point_pressed",self,"point_pressed")
pointv.create_point(point_shape, function_colors[f_index], Color.white, function[point], pointv.create_point(point_shape, function_colors[f_index], Color.white, function[point],
pointv.format_value(point_values[f_index][point],false,false), pointv.format_value(point_values[f_index][point],false,false),
y_labels[point if invert_chart else f_index] as String) y_labels[point if invert_chart else f_index] as String)
yield(get_tree().create_timer(drawing_duration/function.size()), "timeout") yield(get_tree().create_timer(drawing_duration/function.size()), "timeout")
func read_datas(source : String, delimiter : String): func read_datas(source : String, delimiter : String):
var file : File = File.new() var file : File = File.new()
file.open(source,File.READ) file.open(source,File.READ)
var content : Array var content : Array
while not file.eof_reached(): while not file.eof_reached():
var line : PoolStringArray = file.get_csv_line(delimiter) var line : PoolStringArray = file.get_csv_line(delimiter)
content.append(line) content.append(line)
file.close() file.close()
for data in content: for data in content:
if data.size() < 2: if data.size() < 2:
content.erase(data) content.erase(data)
return content return content
func structure_datas(database : Array, are_values_columns : bool, x_values_index : int): 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 # @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 # @y_values can be either a column or a row relative to y values
self.are_values_columns = are_values_columns self.are_values_columns = are_values_columns
match are_values_columns: match are_values_columns:
true: true:
for row in database.size(): for row in database.size():
var t_vals : Array var t_vals : Array
for column in database[row].size(): for column in database[row].size():
if column == x_values_index: if column == x_values_index:
var x_data = database[row][column] var x_data = database[row][column]
if x_data.is_valid_float() or x_data.is_valid_integer(): if x_data.is_valid_float() or x_data.is_valid_integer():
x_datas.append(x_data as float) x_datas.append(x_data as float)
else: else:
x_datas.append(x_data.replace(",",".") as float) x_datas.append(x_data.replace(",",".") as float)
else: else:
if row != 0: if row != 0:
var y_data = database[row][column] var y_data = database[row][column]
if y_data.is_valid_float() or y_data.is_valid_integer(): if y_data.is_valid_float() or y_data.is_valid_integer():
t_vals.append(y_data as float) t_vals.append(y_data as float)
else: else:
t_vals.append(y_data.replace(",",".") as float) t_vals.append(y_data.replace(",",".") as float)
else: else:
y_labels.append(str(database[row][column])) y_labels.append(str(database[row][column]))
if not t_vals.empty(): if not t_vals.empty():
y_datas.append(t_vals) y_datas.append(t_vals)
x_label = str(x_datas.pop_front()) x_label = str(x_datas.pop_front())
false: false:
for row in database.size(): for row in database.size():
if row == x_values_index: if row == x_values_index:
x_datas = (database[row]) x_datas = (database[row])
x_label = x_datas.pop_front() as String x_label = x_datas.pop_front() as String
else: else:
var values = database[row] as Array var values = database[row] as Array
y_labels.append(values.pop_front() as String) y_labels.append(values.pop_front() as String)
y_datas.append(values) y_datas.append(values)
for data in y_datas: for data in y_datas:
for value in data.size(): for value in data.size():
data[value] = data[value] as float data[value] = data[value] as float
# draw y labels # draw y labels
var to_order : Array var to_order : Array
var to_order_min : Array var to_order_min : Array
for cluster in y_datas.size(): for cluster in y_datas.size():
# define x_chors and y_chors # define x_chors and y_chors
var ordered_cluster = y_datas[cluster] as Array var ordered_cluster = y_datas[cluster] as Array
ordered_cluster.sort() ordered_cluster.sort()
ordered_cluster = PoolIntArray(ordered_cluster) ordered_cluster = PoolIntArray(ordered_cluster)
var margin_max = ordered_cluster[ordered_cluster.size()-1] var margin_max = ordered_cluster[ordered_cluster.size()-1]
var margin_min = ordered_cluster[0] var margin_min = ordered_cluster[0]
to_order.append(margin_max) to_order.append(margin_max)
to_order_min.append(margin_min) to_order_min.append(margin_min)
to_order.sort() to_order.sort()
to_order_min.sort() to_order_min.sort()
var margin = to_order.pop_back() var margin = to_order.pop_back()
if not origin_at_zero: if not origin_at_zero:
y_margin_min = to_order_min.pop_front() y_margin_min = to_order_min.pop_front()
v_dist = y_decim * pow(10.0,str(margin).length()-2) v_dist = y_decim * pow(10.0,str(margin).length()-2)
var multi = 0 var multi = 0
var p = (v_dist*multi) + ((y_margin_min) if not origin_at_zero else 0) var p = (v_dist*multi) + ((y_margin_min) if not origin_at_zero else 0)
y_chors.append(p as String) y_chors.append(p as String)
while p < margin: while p < margin:
multi+=1 multi+=1
p = (v_dist*multi) + ((y_margin_min) if not origin_at_zero else 0) p = (v_dist*multi) + ((y_margin_min) if not origin_at_zero else 0)
y_chors.append(p as String) y_chors.append(p as String)
# draw x_labels # draw x_labels
if not show_x_values_as_labels: if not show_x_values_as_labels:
to_order.clear() to_order.clear()
to_order = x_datas as PoolIntArray to_order = x_datas as PoolIntArray
to_order.sort() to_order.sort()
margin = to_order.pop_back() margin = to_order.pop_back()
if not origin_at_zero: if not origin_at_zero:
x_margin_min = to_order.pop_front() x_margin_min = to_order.pop_front()
h_dist = x_decim * pow(10.0,str(margin).length()-2) h_dist = x_decim * pow(10.0,str(margin).length()-2)
multi = 0 multi = 0
p = (h_dist*multi) + ((x_margin_min) if not origin_at_zero else 0) p = (h_dist*multi) + ((x_margin_min) if not origin_at_zero else 0)
x_labels.append(p as String) x_labels.append(p as String)
while p < margin: while p < margin:
multi+=1 multi+=1
p = (h_dist*multi) + ((x_margin_min) if not origin_at_zero else 0) p = (h_dist*multi) + ((x_margin_min) if not origin_at_zero else 0)
x_labels.append(p as String) x_labels.append(p as String)
func build_chart(): func build_chart():
origin = Vector2(OFFSET.x,SIZE.y-OFFSET.y) origin = Vector2(OFFSET.x,SIZE.y-OFFSET.y)
func calculate_pass(): func calculate_pass():
if invert_chart: if invert_chart:
x_chors = y_labels as PoolStringArray x_chors = y_labels as PoolStringArray
else: else:
if show_x_values_as_labels: if show_x_values_as_labels:
x_chors = x_datas as PoolStringArray x_chors = x_datas as PoolStringArray
else: else:
x_chors = x_labels x_chors = x_labels
# calculate distance in pixel between 2 consecutive values/datas # calculate distance in pixel between 2 consecutive values/datas
x_pass = (SIZE.x - OFFSET.x) / (x_chors.size()-1) x_pass = (SIZE.x - OFFSET.x) / (x_chors.size()-1)
y_pass = origin.y / (y_chors.size()-1) y_pass = origin.y / (y_chors.size()-1)
func calculate_coordinates(): func calculate_coordinates():
x_coordinates.clear() x_coordinates.clear()
y_coordinates.clear() y_coordinates.clear()
point_values.clear() point_values.clear()
point_positions.clear() point_positions.clear()
if invert_chart: if invert_chart:
for column in y_datas[0].size(): for column in y_datas[0].size():
var single_coordinates : Array var single_coordinates : Array
for row in y_datas: for row in y_datas:
if origin_at_zero: if origin_at_zero:
single_coordinates.append((row[column]*y_pass)/v_dist) single_coordinates.append((row[column]*y_pass)/v_dist)
else: else:
single_coordinates.append((row[column] - y_margin_min)*y_pass/v_dist) single_coordinates.append((row[column] - y_margin_min)*y_pass/v_dist)
y_coordinates.append(single_coordinates) y_coordinates.append(single_coordinates)
else: else:
for cluster in y_datas: for cluster in y_datas:
var single_coordinates : Array var single_coordinates : Array
for value in cluster.size(): for value in cluster.size():
if origin_at_zero: if origin_at_zero:
single_coordinates.append((cluster[value]*y_pass)/v_dist) single_coordinates.append((cluster[value]*y_pass)/v_dist)
else: else:
single_coordinates.append((cluster[value] - y_margin_min)*y_pass/v_dist) single_coordinates.append((cluster[value] - y_margin_min)*y_pass/v_dist)
y_coordinates.append(single_coordinates) y_coordinates.append(single_coordinates)
if show_x_values_as_labels: if show_x_values_as_labels:
for x in x_datas.size(): for x in x_datas.size():
x_coordinates.append(x_pass*x) x_coordinates.append(x_pass*x)
else: else:
for x in x_datas.size(): for x in x_datas.size():
if origin_at_zero: if origin_at_zero:
if invert_chart: if invert_chart:
x_coordinates.append(x_pass*x) x_coordinates.append(x_pass*x)
else: else:
x_coordinates.append(x_datas[x]*x_pass/h_dist) x_coordinates.append(x_datas[x]*x_pass/h_dist)
else: else:
x_coordinates.append((x_datas[x] - x_margin_min)*x_pass/h_dist) x_coordinates.append((x_datas[x] - x_margin_min)*x_pass/h_dist)
for f in functions: for f in functions:
point_values.append([]) point_values.append([])
point_positions.append([]) point_positions.append([])
if invert_chart: if invert_chart:
for function in y_coordinates.size(): for function in y_coordinates.size():
for function_value in y_coordinates[function].size(): for function_value in y_coordinates[function].size():
if are_values_columns: if are_values_columns:
point_positions[function_value].append(Vector2(x_coordinates[function]+origin.x, origin.y-y_coordinates[function][function_value])) 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]]) point_values[function_value].append([x_datas[function_value],y_datas[function_value][function]])
else: else:
point_positions[function].append(Vector2(x_coordinates[function_value]+origin.x,origin.y-y_coordinates[function][function_value])) 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]]) point_values[function].append([x_datas[function_value],y_datas[function_value][function]])
else: else:
for cluster in y_coordinates.size(): for cluster in y_coordinates.size():
for y in y_coordinates[cluster].size(): for y in y_coordinates[cluster].size():
if are_values_columns: if are_values_columns:
point_values[y].append([x_datas[cluster],y_datas[cluster][y]]) 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])) point_positions[y].append(Vector2(x_coordinates[cluster]+origin.x,origin.y-y_coordinates[cluster][y]))
else: else:
point_values[cluster].append([x_datas[y],y_datas[cluster][y]]) 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])) point_positions[cluster].append(Vector2(x_coordinates[y]+origin.x,origin.y-y_coordinates[cluster][y]))
func redraw(): func redraw():
build_chart() build_chart()
calculate_pass() calculate_pass()
calculate_coordinates() calculate_coordinates()
update() update()
func show_data(point): func show_data(point):
PointData.update_datas(point) PointData.update_datas(point)
PointData.show() PointData.show()
func hide_data(): func hide_data():
PointData.hide() PointData.hide()
func clear_points(): func clear_points():
function_colors.clear() function_colors.clear()
if Functions.get_children(): if Functions.get_children():
for function in Functions.get_children(): for function in Functions.get_children():
function.queue_free() function.queue_free()
func set_legend(l : Array): func set_legend(l : Array):
legend = l legend = l
func get_legend(): func get_legend():
return legend return legend
func invert_chart(): func invert_chart():
invert_chart = !invert_chart invert_chart = !invert_chart
count_functions() count_functions()
redraw() redraw()
create_legend() create_legend()
func count_functions(): func count_functions():
if are_values_columns: if are_values_columns:
if not invert_chart: if not invert_chart:
functions = datas[0].size()-1 functions = datas[0].size()-1
else: else:
functions = datas.size()-1 functions = datas.size()-1
else: else:
if invert_chart: if invert_chart:
functions = datas[0].size()-1 functions = datas[0].size()-1
else: else:
functions = datas.size()-1 functions = datas.size()-1
func create_legend(): func create_legend():
legend.clear() legend.clear()
for function in functions: for function in functions:
var function_legend = FunctionLegend.instance() var function_legend = FunctionLegend.instance()
var f_name : String var f_name : String
if invert_chart: if invert_chart:
f_name = x_datas[function] as String f_name = x_datas[function] as String
else: else:
f_name = y_labels[function] f_name = y_labels[function]
var legend_font : Font var legend_font : Font
if font != null: if font != null:
legend_font = font legend_font = font
if bold_font != null: if bold_font != null:
legend_font = bold_font legend_font = bold_font
function_legend.create_legend(f_name,function_colors[function],bold_font,font_color) function_legend.create_legend(f_name,function_colors[function],bold_font,font_color)
legend.append(function_legend) legend.append(function_legend)
func apply_template(template_name : int): func apply_template(template_name : int):
template = template_name template = template_name
templates = Utilities._load_templates() templates = Utilities._load_templates()
if template_name!=null: if template_name!=null:
var custom_template = templates.get(templates.keys()[template_name]) var custom_template = templates.get(templates.keys()[template_name])
function_colors = custom_template.function_colors as PoolColorArray function_colors = custom_template.function_colors as PoolColorArray
v_lines_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) h_lines_color = Color(custom_template.h_lines_color)
box_color = Color(custom_template.outline_color) box_color = Color(custom_template.outline_color)
font_color = Color(custom_template.font_color) font_color = Color(custom_template.font_color)
property_list_changed_notify() property_list_changed_notify()
if Engine.editor_hint: if Engine.editor_hint:
_get_children() _get_children()
Outlines.set_default_color(box_color) Outlines.set_default_color(box_color)
Grid.get_node("VLine").set_default_color(v_lines_color) Grid.get_node("VLine").set_default_color(v_lines_color)
Grid.get_node("HLine").set_default_color(h_lines_color) Grid.get_node("HLine").set_default_color(h_lines_color)
func _enter_tree(): func _enter_tree():
_ready() _ready()

View File

@ -46,5 +46,9 @@ default_color = Color( 0.117647, 0.117647, 0.117647, 1 )
[node name="PointData" parent="PointData" index="0"] [node name="PointData" parent="PointData" index="0"]
visible = false visible = false
margin_left = 71.2491
margin_top = -146.673
margin_right = 71.1131
margin_bottom = -145.873
[editable path="PointData"] [editable path="PointData"]

View File

@ -1,5 +1,6 @@
tool tool
extends Spatial extends Spatial
class_name ScatterChart3D
""" """
[ScatterChart] - General purpose node for Scatter Charts [ScatterChart] - General purpose node for Scatter Charts

View File

@ -42,4 +42,11 @@ transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 9, 0 )
[node name="PointData" parent="." instance=ExtResource( 2 )] [node name="PointData" parent="." instance=ExtResource( 2 )]
[node name="PointData" parent="PointData" index="0"]
visible = false
margin_left = 47.2074
margin_top = -150.915
margin_right = 47.0714
margin_bottom = -150.115
[editable path="PointData"] [editable path="PointData"]

View File

@ -1,5 +1,6 @@
tool tool
extends VBoxContainer extends VBoxContainer
class_name LegendElement
onready var Function : Label = $Function onready var Function : Label = $Function
onready var FunctionColor : ColorRect = $Color onready var FunctionColor : ColorRect = $Color
@ -10,25 +11,31 @@ var font_color : Color
var font : Font var font : Font
func _ready(): func _ready():
Function.set("custom_fonts/font",font) Function.set("custom_fonts/font",font)
Function.set("custom_colors/font_color",font_color) Function.set("custom_colors/font_color",font_color)
Function.set_text(function) Function.set_text(function)
FunctionColor.set_frame_color(color) FunctionColor.set_frame_color(color)
func create_legend(text : String, color : Color, font : Font, font_color : Color): func create_legend(text : String, color : Color, font : Font, font_color : Color):
self.function = text self.function = text
self.color = color self.color = color
self.font_color = font_color self.font_color = font_color
self.font = font self.font = font
func set_function( t : String ): func set_function( t : String ):
function = t function = t
func get_function() -> String: func get_function() -> String:
return function return function
func set_function_color( c : Color ): func set_function_color( c : Color ):
color = c color = c
func get_function_color() -> Color: func get_function_color() -> Color:
return color return color
func get_class() -> String:
return "Legend Element"
func _to_string() -> String:
return "%s (%s, %s) " % [get_class(), get_function(), get_function_color().to_html(true)]

View File

@ -1,8 +1,10 @@
extends Control extends Control
class_name Chart class_name Chart
# Classes
# Signals .................................. # Signals ..................................
signal chart_plotted(chart) signal chart_plotted(chart) # emit when a chart is plotted (static) or updated (dynamic)
signal point_pressed(point) signal point_pressed(point)
# Onready Vars ............................ # Onready Vars ............................
@ -13,7 +15,7 @@ onready var ChartName : Label = $ChartName
# Scenes and Reosurces ...................... # Scenes and Reosurces ......................
var point_node : PackedScene = preload("../Point/Point.tscn") var point_node : PackedScene = preload("../Point/Point.tscn")
var FunctionLegend : PackedScene = preload("../Legend/FunctionLegend.tscn") var LegendElement : PackedScene = preload("../Legend/FunctionLegend.tscn")
# Enums ..................................... # Enums .....................................
enum PointShapes { Dot, Triangle, Square, Cross } enum PointShapes { Dot, Triangle, Square, Cross }
@ -315,6 +317,8 @@ func plot_from_array(array : Array) -> void:
create_legend() create_legend()
emit_signal("chart_plotted",self) 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: func update_plot_data(array : Array) -> void:
if array.empty(): if array.empty():
Utilities._print_message("Can't plot a chart without an empty Array.",1) Utilities._print_message("Can't plot a chart without an empty Array.",1)
@ -421,7 +425,7 @@ func function_colors():
func create_legend(): func create_legend():
legend.clear() legend.clear()
for function in functions: for function in functions:
var function_legend = FunctionLegend.instance() var function_legend : LegendElement = LegendElement.instance()
var f_name : String = y_labels[function] var f_name : String = y_labels[function]
var legend_font : Font var legend_font : Font
if font != null: if font != null:
@ -590,7 +594,7 @@ func set_invert_chart(b : bool):
func set_legend(l : Array): func set_legend(l : Array):
legend = l legend = l
func get_legend(): func get_legend() -> Array:
return legend return legend
# ............................. Shared Signals .............................. # ............................. Shared Signals ..............................

View File

@ -5,4 +5,4 @@ China,26
Russia,19 Russia,19
Germany,17 Germany,17
Italy,34 Italy,34
Sweden,83 Sweden,83
1 Country Votes
5 Russia 19
6 Germany 17
7 Italy 34
8 Sweden 83