update v2

This commit is contained in:
fenix-hub 2023-01-11 12:11:20 +01:00
parent ded2538ef0
commit cb8c52ac35
39 changed files with 432 additions and 4881 deletions

View File

@ -1,468 +0,0 @@
tool
extends Chart2D
class_name ColumnChart2D
"""
[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.
The bars can be plotted vertically or horizontally. A vertical bar chart is sometimes
called a column chart.
A bar graph shows comparisons among discrete categories. One axis of the chart shows
the specific categories being compared, and the other axis represents a measured value.
Some bar graphs present bars clustered in groups of more than one, showing the
values of more than one measured variable.
/ source : Wikipedia /
"""
func _point_plotted():
pass
func _ready():
_get_children()
func _get_children():
OutlinesTween = $OutlinesTween
Functions = $Functions
GridTween = $GridTween
DataTooltip = $CanvasLayer/DataTooltip
Outlines = $Outlines
Grid = $Grid
func _set_size(size : Vector2):
SIZE = size
build_chart()
if Engine.editor_hint:
Outlines.set_point_position(0,Vector2(origin.x,0))
Outlines.set_point_position(1,Vector2(SIZE.x,0))
Outlines.set_point_position(2,Vector2(SIZE.x,origin.y))
Outlines.set_point_position(3,origin)
Outlines.set_point_position(4,Vector2(origin.x,0))
Grid.get_node("VLine").set_point_position(0,Vector2((OFFSET.x+SIZE.x)/2,0))
Grid.get_node("VLine").set_point_position(1,Vector2((OFFSET.x+SIZE.x)/2,origin.y))
Grid.get_node("HLine").set_point_position(0,Vector2(origin.x,origin.y/2))
Grid.get_node("HLine").set_point_position(1,Vector2(SIZE.x,origin.y/2))
func clear():
Outlines.points = []
Grid.get_node("HLine").queue_free()
Grid.get_node("VLine").queue_free()
func load_font():
if font != null:
font_size = font.get_height()
var theme : Theme = Theme.new()
theme.set_default_font(font)
DataTooltip.set_theme(theme)
else:
var lbl = Label.new()
font = lbl.get_font("")
lbl.free()
if bold_font != null:
DataTooltip.Data.set("custom_fonts/font",bold_font)
func _plot(source_ : String, delimiter_ : String, are_values_columns_ : bool, x_values_index_ : int):
randomize()
clear()
load_font()
DataTooltip.hide()
datas = read_data(source_,delimiter_)
count_functions()
structure_data(datas,are_values_columns_,x_values_index_)
build_chart()
calculate_pass()
calculate_coordinates()
calculate_colors()
draw_chart()
create_legend()
emit_signal("chart_plotted", self)
func plot():
randomize()
clear()
load_font()
DataTooltip.hide()
if source == "" or source == null:
ECUtilities._print_message("Can't plot a chart without a Source file. Please, choose it in editor, or use the custom function _plot().",1)
return
datas = read_data(source,delimiter)
count_functions()
structure_data(datas,are_values_columns,x_values_index)
build_chart()
calculate_pass()
calculate_coordinates()
calculate_colors()
draw_chart()
create_legend()
emit_signal("chart_plotted", self)
func calculate_colors():
if function_colors.empty() or function_colors.size() < functions:
for function in functions:
function_colors.append(Color("#1e1e1e"))
func draw_chart():
draw_outlines()
draw_v_grid()
draw_h_grid()
draw_functions()
func draw_outlines():
if boxed:
Outlines.set_default_color(box_color)
OutlinesTween.interpolate_method(Outlines,"add_point",
Vector2(origin.x,0),Vector2(SIZE.x,0),drawing_duration*0.5,Tween.TRANS_QUINT,Tween.EASE_OUT)
OutlinesTween.start()
yield(OutlinesTween,"tween_all_completed")
OutlinesTween.interpolate_method(Outlines,"add_point",
Vector2(SIZE.x,0),Vector2(SIZE.x,origin.y),drawing_duration*0.5,Tween.TRANS_QUINT,Tween.EASE_OUT)
OutlinesTween.start()
yield(OutlinesTween,"tween_all_completed")
OutlinesTween.interpolate_method(Outlines,"add_point",
Vector2(SIZE.x,origin.y),origin,drawing_duration*0.5,Tween.TRANS_QUINT,Tween.EASE_OUT)
OutlinesTween.start()
yield(OutlinesTween,"tween_all_completed")
OutlinesTween.interpolate_method(Outlines,"add_point",
origin,Vector2(origin.x,0),drawing_duration*0.5,Tween.TRANS_QUINT,Tween.EASE_OUT)
OutlinesTween.start()
yield(OutlinesTween,"tween_all_completed")
func draw_v_grid():
for p in x_chors.size():
var point : Vector2 = origin+Vector2((p)*x_pass,0)
var v_grid : Line2D = Line2D.new()
Grid.add_child(v_grid)
v_grid.set_width(1)
v_grid.set_default_color(v_lines_color)
var calculated_gap : float
if not are_values_columns:
calculated_gap = ( y_datas.size() if not invert_chart else y_datas[0].size()+1 )
else:
calculated_gap = ( y_datas.size() if invert_chart else y_datas[0].size()+1 )
add_label(point+Vector2(-const_width/2*x_chors[p].length() + (column_width/2) * calculated_gap + column_gap,font_size/2), x_chors[p])
GridTween.interpolate_method(v_grid,"add_point",Vector2(point.x,origin.y),Vector2(point.x,origin.y-5),drawing_duration/(x_chors.size()),Tween.TRANS_EXPO,Tween.EASE_OUT)
GridTween.start()
yield(GridTween,"tween_all_completed")
func draw_h_grid():
for p in y_chors.size():
var point : Vector2 = origin-Vector2(0,(p)*y_pass)
var h_grid : Line2D = Line2D.new()
Grid.add_child(h_grid)
h_grid.set_width(1)
h_grid.set_default_color(h_lines_color)
add_label(point-Vector2(y_chors[p].length()*const_width+font_size,font_size/2), y_chors[p])
GridTween.interpolate_method(h_grid,"add_point",point,Vector2(SIZE.x,point.y),drawing_duration/(y_chors.size()),Tween.TRANS_EXPO,Tween.EASE_OUT)
GridTween.start()
yield(GridTween,"tween_all_completed")
func add_label(point : Vector2, text : String):
var lbl : Label = Label.new()
if font != null:
lbl.set("custom_fonts/font",font)
lbl.set("custom_colors/font_color",font_color)
Grid.add_child(lbl)
lbl.rect_position = point
lbl.set_text(text)
func draw_functions():
for function in point_positions.size():
draw_function(function,point_positions[function])
func draw_function(f_index : int, function : Array):
var pointv : Point
for point in function.size():
var line : Line2D = Line2D.new()
pointv = point_node.instance()
line.add_child(pointv)
pointv.connect("_mouse_entered",self,"show_data")
pointv.connect("_mouse_exited",self,"hide_data")
pointv.connect("_point_pressed",self,"point_pressed")
pointv.create_point(point_shape, function_colors[f_index], Color.white, function[point],
pointv.format_value(point_values[f_index][point],false,false),
y_labels[point if invert_chart else f_index] as String)
pointv.rect_size.y = origin.y - function[point].y
construct_column(line,f_index,function)
FunctionsTween.interpolate_method(line,"add_point",Vector2(function[point].x,origin.y),function[point],drawing_duration/function.size(),Tween.TRANS_QUINT,Tween.EASE_OUT)
FunctionsTween.start()
yield(FunctionsTween,"tween_all_completed")
func construct_column(line : Line2D, f_index : int, function : Array):
line.set_width(column_width)
line.set_default_color(function_colors[f_index])
line.antialiased = true
Functions.add_child(line,true)
func read_data(source : String, delimiter : String):
var file : File = File.new()
file.open(source,File.READ)
var content : Array
while not file.eof_reached():
var line : PoolStringArray = file.get_csv_line(delimiter)
content.append(line)
file.close()
for data in content:
if data.size() < 2:
content.erase(data)
return content
func structure_data(database : Array, are_values_columns : bool, x_values_index : int):
# @x_values_index can be either a column or a row relative to x values
# @y_values can be either a column or a row relative to y values
self.are_values_columns = are_values_columns
match are_values_columns:
true:
for row in database.size():
var t_vals : Array
for column in database[row].size():
if column == x_values_index:
var x_data = database[row][column]
if x_data.is_valid_float() or x_data.is_valid_integer():
x_datas.append(x_data as float)
else:
x_datas.append(x_data.replace(",",".") as float)
else:
if row != 0:
var y_data = database[row][column]
if y_data.is_valid_float() or y_data.is_valid_integer():
t_vals.append(y_data as float)
else:
t_vals.append(y_data.replace(",",".") as float)
else:
y_labels.append(str(database[row][column]))
if not t_vals.empty():
y_datas.append(t_vals)
x_label = str(x_datas.pop_front())
false:
for row in database.size():
if row == x_values_index:
x_datas = (database[row])
x_label = x_datas.pop_front() as String
else:
var values = database[row] as Array
y_labels.append(values.pop_front() as String)
y_datas.append(values)
for data in y_datas:
for value in data.size():
data[value] = data[value] as float
# draw y labels
var to_order : Array
var to_order_min : Array
for cluster in y_datas.size():
# define x_chors and y_chors
var ordered_cluster = y_datas[cluster] as Array
ordered_cluster.sort()
ordered_cluster = PoolIntArray(ordered_cluster)
var margin_max = ordered_cluster[ordered_cluster.size()-1]
var margin_min = ordered_cluster[0]
to_order.append(margin_max)
to_order_min.append(margin_min)
to_order.sort()
to_order_min.sort()
var margin = to_order.pop_back()
if not origin_at_zero:
y_margin_min = to_order_min.pop_front()
v_dist = y_decim * pow(10.0,str(margin).length()-2)
var multi = 0
var p = (v_dist*multi) + ((y_margin_min) if not origin_at_zero else 0)
y_chors.append(p as String)
while p < margin:
multi+=1
p = (v_dist*multi) + ((y_margin_min) if not origin_at_zero else 0)
y_chors.append(p as String)
# draw x_labels
if not show_x_values_as_labels:
to_order.clear()
to_order = x_datas as PoolIntArray
to_order.sort()
margin = to_order.pop_back()
if not origin_at_zero:
x_margin_min = to_order.pop_front()
h_dist = x_decim * pow(10.0,str(margin).length()-2)
multi = 0
p = (h_dist*multi) + ((x_margin_min) if not origin_at_zero else 0)
x_labels.append(p as String)
while p < margin:
multi+=1
p = (h_dist*multi) + ((x_margin_min) if not origin_at_zero else 0)
x_labels.append(p as String)
func build_chart():
origin = Vector2(OFFSET.x,SIZE.y-OFFSET.y)
func calculate_pass():
if invert_chart:
x_chors = y_labels as PoolStringArray
else:
if show_x_values_as_labels:
x_chors = x_datas as PoolStringArray
else:
x_chors = x_labels
# calculate distance in pixel between 2 consecutive values/datas
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)
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)
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 not 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_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]))
func redraw():
build_chart()
calculate_pass()
calculate_coordinates()
update()
func show_data(point):
DataTooltip.update_datas(point)
DataTooltip.show()
func hide_data():
DataTooltip.hide()
func clean_points():
function_colors.clear()
if Functions.get_children():
for function in Functions.get_children():
function.queue_free()
func set_legend(l : Array):
legend = l
func get_legend():
return legend
func invert_chart():
invert_chart = !invert_chart
count_functions()
redraw()
create_legend()
func count_functions():
if are_values_columns:
if not invert_chart:
functions = datas[0].size()-1
else:
functions = datas.size()-1
else:
if invert_chart:
functions = datas[0].size()-1
else:
functions = datas.size()-1
func create_legend():
legend.clear()
for function in functions:
var function_legend = FunctionLegend.instance()
var f_name : String
if invert_chart:
f_name = x_datas[function] as String
else:
f_name = y_labels[function]
var legend_font : Font
if font != null:
legend_font = font
if bold_font != null:
legend_font = bold_font
function_legend.create_legend(f_name,function_colors[function],bold_font,font_color)
legend.append(function_legend)
func apply_template(template_name : int):
template = template_name
templates = ECUtilities._load_templates()
if template_name!=null:
var custom_template = templates.get(templates.keys()[template_name])
function_colors = custom_template.function_colors as PoolColorArray
v_lines_color = Color(custom_template.v_lines_color)
h_lines_color = Color(custom_template.h_lines_color)
box_color = Color(custom_template.outline_color)
font_color = Color(custom_template.font_color)
property_list_changed_notify()
if Engine.editor_hint:
_get_children()
Outlines.set_default_color(box_color)
Grid.get_node("VLine").set_default_color(v_lines_color)
Grid.get_node("HLine").set_default_color(h_lines_color)
func _enter_tree():
_ready()

View File

@ -1,68 +0,0 @@
[gd_scene load_steps=3 format=2]
[ext_resource path="res://addons/easy_charts/2d_charts/ColumnChart2D/column_chart2D.gd" type="Script" id=1]
[ext_resource path="res://addons/easy_charts/utilities/containers/data_tooltip/data_tooltip.tscn" type="PackedScene" id=2]
[node name="BarChart2D" type="Node2D"]
script = ExtResource( 1 )
__meta__ = {
"_edit_group_": true,
"_editor_description_": "[BarChart2D] - 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.
The bars can be plotted vertically or horizontally. A vertical bar chart is sometimes
called a column chart.
A bar graph shows comparisons among discrete categories. One axis of the chart shows
the specific categories being compared, and the other axis represents a measured value.
Some bar graphs present bars clustered in groups of more than one, showing the
values of more than one measured variable."
}
function_colors = [ "#1e1e1e", "#1e1e1e", "#1e1e1e", "#1e1e1e" ]
[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 = 5.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="CanvasLayer" type="CanvasLayer" parent="."]
[node name="PointData" parent="CanvasLayer" instance=ExtResource( 2 )]
margin_left = 269.278
margin_top = 453.515
margin_right = 289.278
margin_bottom = 463.515
[node name="PointData" parent="CanvasLayer/PointData" index="0"]
visible = false
margin_left = 48.6217
margin_top = -122.631
margin_right = 48.4856
margin_bottom = -121.831
[editable path="CanvasLayer/PointData"]

View File

@ -1,469 +0,0 @@
tool
extends Chart2D
class_name LineChart2D
# [Linechart2D] - General purpose node for Line Charts
# A line chart or line plot or line graph or curve chart is a type of chart which
# displays information as a series of data points called 'markers'
# connected by straight line segments.
# It is a basic type of chart common in many fields. It is similar to a scatter plot
# except that the measurement points are ordered (typically by their x-axis value)
# and joined with straight line segments.
# A line chart is often used to visualize a trend in data over intervals of time
# a time series thus the line is often drawn chronologically.
# In these cases they are known as run charts.
# Source: Wikipedia
func _point_plotted():
pass
func _ready():
_get_children()
func _set_size(size: Vector2):
SIZE = size
build_chart()
if Engine.editor_hint:
_get_children()
Outlines.set_point_position(0, Vector2(origin.x, 0))
Outlines.set_point_position(1, Vector2(SIZE.x, 0))
Outlines.set_point_position(2, Vector2(SIZE.x, origin.y))
Outlines.set_point_position(3, origin)
Outlines.set_point_position(4, Vector2(origin.x, 0))
Grid.get_node("VLine").set_point_position(0, Vector2((OFFSET.x + SIZE.x) / 2,0))
Grid.get_node("VLine").set_point_position(1, Vector2((OFFSET.x + SIZE.x) / 2, origin.y))
Grid.get_node("HLine").set_point_position(0, Vector2(origin.x, origin.y / 2))
Grid.get_node("HLine").set_point_position(1, Vector2(SIZE.x, origin.y / 2))
func clear():
Outlines.points = []
Grid.get_node("HLine").queue_free()
Grid.get_node("VLine").queue_free()
func load_font():
if font != null:
font_size = font.get_height()
var theme: Theme = Theme.new()
theme.set_default_font(font)
DataTooltip.set_theme(theme)
else:
var lbl = Label.new()
font = lbl.get_font("")
lbl.free()
if bold_font != null:
DataTooltip.Data.set("custom_fonts/font",bold_font)
func _plot(source: String, delimiter: String, are_values_columns: bool, x_values_index: int):
randomize()
clear()
load_font()
DataTooltip.hide()
datas = read_data(source, delimiter)
count_functions()
structure_data(datas, are_values_columns, x_values_index)
build_chart()
calculate_pass()
calculate_coordinates()
calculate_colors()
draw_chart()
create_legend()
emit_signal("chart_plotted", self)
func plot():
randomize()
clear()
load_font()
DataTooltip.hide()
if source == "" or source == null:
ECUtilities._print_message("Can't plot a chart without a Source file. Please, choose it in editor, or use the custom function _plot().", 1)
return
datas = read_data(source,delimiter)
count_functions()
structure_data(datas, are_values_columns, x_values_index)
build_chart()
calculate_pass()
calculate_coordinates()
calculate_colors()
draw_chart()
create_legend()
emit_signal("chart_plotted", self)
func calculate_colors():
if function_colors.empty() or function_colors.size() < functions:
for function in functions:
function_colors.append(Color("#1e1e1e"))
func draw_chart():
draw_outlines()
draw_v_grid()
draw_h_grid()
draw_functions()
func draw_outlines():
if boxed:
Outlines.set_default_color(box_color)
OutlinesTween.interpolate_method(
Outlines,
"add_point",
Vector2(origin.x, 0),
Vector2(SIZE.x, 0),
drawing_duration * 0.5,
Tween.TRANS_QUINT,
Tween.EASE_OUT)
OutlinesTween.start()
yield(OutlinesTween, "tween_all_completed")
OutlinesTween.interpolate_method(
Outlines,
"add_point",
Vector2(SIZE.x, 0),
Vector2(SIZE.x, origin.y),
drawing_duration * 0.5,
Tween.TRANS_QUINT,
Tween.EASE_OUT)
OutlinesTween.start()
yield(OutlinesTween, "tween_all_completed")
OutlinesTween.interpolate_method(
Outlines,
"add_point",
Vector2(SIZE.x, origin.y),
origin,
drawing_duration * 0.5,
Tween.TRANS_QUINT,
Tween.EASE_OUT)
OutlinesTween.start()
yield(OutlinesTween, "tween_all_completed")
OutlinesTween.interpolate_method(
Outlines,
"add_point",
origin,
Vector2(origin.x, 0),
drawing_duration * 0.5,
Tween.TRANS_QUINT,
Tween.EASE_OUT)
OutlinesTween.start()
yield(OutlinesTween, "tween_all_completed")
func draw_v_grid():
for p in x_chors.size():
var point: Vector2 = origin + Vector2((p) * x_pass, 0)
var v_grid: Line2D = Line2D.new()
Grid.add_child(v_grid)
v_grid.set_width(1)
v_grid.set_default_color(v_lines_color)
add_label(point + Vector2(-const_width / 2 * x_chors[p].length(), font_size / 2), x_chors[p])
GridTween.interpolate_method(
v_grid,
"add_point",
point,
point - Vector2(0, SIZE.y - OFFSET.y),
drawing_duration / (x_chors.size()),
Tween.TRANS_EXPO,
Tween.EASE_OUT)
GridTween.start()
yield(GridTween, "tween_all_completed")
func draw_h_grid():
for p in y_chors.size():
var point: Vector2 = origin - Vector2(0, p * y_pass)
var h_grid: Line2D = Line2D.new()
Grid.add_child(h_grid)
h_grid.set_width(1)
h_grid.set_default_color(h_lines_color)
add_label(point - Vector2(y_chors[p].length() * const_width + font_size, font_size / 2), y_chors[p])
GridTween.interpolate_method(
h_grid,
"add_point",
point,
Vector2(SIZE.x, point.y),
drawing_duration / (y_chors.size()),
Tween.TRANS_EXPO,
Tween.EASE_OUT)
GridTween.start()
yield(GridTween, "tween_all_completed")
func add_label(point: Vector2, text: String):
var lbl: Label = Label.new()
if font != null:
lbl.set("custom_fonts/font", font)
lbl.set("custom_colors/font_color", font_color)
Grid.add_child(lbl)
lbl.rect_position = point
lbl.set_text(text)
func draw_functions():
for function in point_positions.size():
draw_function(function, point_positions[function])
func draw_function(f_index: int, function: Array):
var line: Line2D = Line2D.new()
var backline: Line2D = Line2D.new()
construct_line(line, backline, f_index, function)
var pointv: Point
for point in function.size():
pointv = point_node.instance()
Functions.add_child(pointv)
pointv.connect("_mouse_entered", self, "show_data")
pointv.connect("_mouse_exited", self, "hide_data")
pointv.connect("_point_pressed", self, "point_pressed")
pointv.create_point(
point_shape,
function_colors[f_index],
Color.white, function[point],
pointv.format_value(point_values[f_index][point], false, false),
y_labels[point if invert_chart else f_index] as String)
if point < function.size() - 1:
FunctionsTween.interpolate_method(
line,
"add_point",
function[point],
function[point + 1],
drawing_duration / function.size(),
Tween.TRANS_QUINT,
Tween.EASE_OUT)
FunctionsTween.start()
yield(FunctionsTween, "tween_all_completed")
func construct_line(line: Line2D, backline: Line2D, f_index: int, function: Array):
var midtone = Color(
Color(function_colors[f_index]).r,
Color(function_colors[f_index]).g,
Color(function_colors[f_index]).b,
Color(function_colors[f_index]).a / 2)
backline.set_width(3)
backline.set_default_color(midtone)
backline.antialiased = true
Functions.add_child(backline)
line.set_width(2)
line.set_default_color(function_colors[f_index])
line.antialiased = true
Functions.add_child(line)
func read_data(source: String, delimiter: String):
var file: File = File.new()
file.open(source, File.READ)
var content: Array
while not file.eof_reached():
var line: PoolStringArray = file.get_csv_line(delimiter)
content.append(line)
file.close()
for data in content:
if data.size() < 2:
content.erase(data)
return content
func structure_data(database: Array, are_values_columns: bool, x_values_index: int):
# @x_values_index can be either a column or a row relative to x values
# @y_values can be either a column or a row relative to y values
self.are_values_columns = are_values_columns
match are_values_columns:
true:
for row in database.size():
var t_vals: Array
for column in database[row].size():
if column == x_values_index:
var x_data = database[row][column]
if x_data.is_valid_float() or x_data.is_valid_integer():
x_datas.append(x_data as float)
else:
x_datas.append(x_data.replace(",", ".") as float)
else:
if row != 0:
var y_data = database[row][column]
if y_data.is_valid_float() or y_data.is_valid_integer():
t_vals.append(y_data as float)
else:
t_vals.append(y_data.replace(",", ".") as float)
else:
y_labels.append(str(database[row][column]))
if not t_vals.empty():
y_datas.append(t_vals)
x_label = str(x_datas.pop_front())
false:
for row in database.size():
if row == x_values_index:
x_datas = (database[row])
x_label = x_datas.pop_front() as String
else:
var values = database[row] as Array
y_labels.append(values.pop_front() as String)
y_datas.append(values)
for data in y_datas:
for value in data.size():
data[value] = data[value] as float
# draw y labels
var to_order: Array
var to_order_min: Array
for cluster in y_datas.size():
# define x_chors and y_chors
var ordered_cluster = y_datas[cluster] as Array
ordered_cluster.sort()
ordered_cluster = PoolIntArray(ordered_cluster)
var margin_max = ordered_cluster[ordered_cluster.size() - 1]
var margin_min = ordered_cluster[0]
to_order.append(margin_max)
to_order_min.append(margin_min)
to_order.sort()
to_order_min.sort()
var margin = to_order.pop_back()
if not origin_at_zero:
y_margin_min = to_order_min.pop_front()
v_dist = y_decim * pow(10.0,str(margin).length() - 2)
var multi = 0
var p = (v_dist * multi) + (y_margin_min if not origin_at_zero else 0)
y_chors.append(p as String)
while p < margin:
multi+=1
p = (v_dist * multi) + (y_margin_min if not origin_at_zero else 0)
y_chors.append(p as String)
# draw x_labels
if not show_x_values_as_labels:
to_order.clear()
to_order = x_datas as PoolIntArray
to_order.sort()
margin = to_order.pop_back()
if not origin_at_zero:
x_margin_min = to_order.pop_front()
h_dist = x_decim * pow(10.0, str(margin).length() - 2)
multi = 0
p = (h_dist * multi) + (x_margin_min if not origin_at_zero else 0)
x_labels.append(p as String)
while p < margin:
multi += 1
p = (h_dist * multi) + (x_margin_min if not origin_at_zero else 0)
x_labels.append(p as String)
func build_chart():
origin = Vector2(OFFSET.x, SIZE.y - OFFSET.y)
func calculate_pass():
if invert_chart:
x_chors = y_labels as PoolStringArray
else:
if show_x_values_as_labels:
x_chors = x_datas as PoolStringArray
else:
x_chors = x_labels
# calculate distance in pixel between 2 consecutive values/datas
x_pass = (SIZE.x - OFFSET.x) / (x_chors.size() - 1)
y_pass = origin.y / (y_chors.size() - 1)
func calculate_coordinates():
x_coordinates.clear()
y_coordinates.clear()
point_values.clear()
point_positions.clear()
if invert_chart:
for column in y_datas[0].size():
var single_coordinates: Array
for row in y_datas:
if origin_at_zero:
single_coordinates.append((row[column] * y_pass) / v_dist)
else:
single_coordinates.append((row[column] - y_margin_min) * y_pass / v_dist)
y_coordinates.append(single_coordinates)
else:
for cluster in y_datas:
var single_coordinates: Array
for value in cluster.size():
if origin_at_zero:
single_coordinates.append((cluster[value] * y_pass) / v_dist)
else:
single_coordinates.append((cluster[value] - y_margin_min) * y_pass / v_dist)
y_coordinates.append(single_coordinates)
if show_x_values_as_labels:
for x in x_datas.size():
x_coordinates.append(x_pass * x)
else:
for x in x_datas.size():
if origin_at_zero:
if invert_chart:
x_coordinates.append(x_pass * x)
else:
x_coordinates.append(x_datas[x] * x_pass / h_dist)
else:
x_coordinates.append((x_datas[x] - x_margin_min) * x_pass / h_dist)
for f in functions:
point_values.append([])
point_positions.append([])
if invert_chart:
for function in y_coordinates.size():
for function_value in y_coordinates[function].size():
if are_values_columns:
point_positions[function_value].append(Vector2(
x_coordinates[function] + origin.x,
origin.y - y_coordinates[function][function_value]))
point_values[function_value].append(
[x_datas[function_value], y_datas[function_value][function]])
else:
point_positions[function].append(Vector2(
x_coordinates[function_value] + origin.x,
origin.y - y_coordinates[function][function_value]))
point_values[function].append(
[x_datas[function_value], y_datas[function_value][function]])
else:
for cluster in y_coordinates.size():
for y in y_coordinates[cluster].size():
if are_values_columns:
point_values[y].append([x_datas[cluster],y_datas[cluster][y]])
point_positions[y].append(Vector2(
x_coordinates[cluster] + origin.x,
origin.y - y_coordinates[cluster][y]))
else:
point_values[cluster].append([x_datas[y],y_datas[cluster][y]])
point_positions[cluster].append(Vector2(
x_coordinates[y] + origin.x,
origin.y - y_coordinates[cluster][y]))
func invert_chart():
invert_chart = !invert_chart
count_functions()
redraw()
create_legend()
func _enter_tree():
_ready()
# Signal Repeaters
func point_pressed(point: Point) -> Point:
return point

View File

@ -1,70 +0,0 @@
[gd_scene load_steps=3 format=2]
[ext_resource path="res://addons/easy_charts/2d_charts/LineChart2D/line_chart2D.gd" type="Script" id=1]
[ext_resource path="res://addons/easy_charts/utilities/containers/data_tooltip/data_tooltip.tscn" type="PackedScene" id=2]
[node name="LineChart2D" type="Node2D"]
position = Vector2( -1, 0 )
script = ExtResource( 1 )
__meta__ = {
"_edit_group_": true,
"_editor_description_": "[Linechart2D] - General purpose node for Line Charts
A line chart or line plot or line graph or curve chart is a type of chart which
displays information as a series of data points called 'markers'
connected by straight line segments.
It is a basic type of chart common in many fields. It is similar to a scatter plot
except that the measurement points are ordered (typically by their x-axis value)
and joined with straight line segments.
A line chart is often used to visualize a trend in data over intervals of time
a time series thus the line is often drawn chronologically.
In these cases they are known as run charts."
}
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="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, 0, 0, 0, 0, 0, 0, 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 = 5.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="CanvasLayer" type="CanvasLayer" parent="."]
[node name="DataTooltip" parent="CanvasLayer" instance=ExtResource( 2 )]
margin_left = 286.115
margin_top = 462.494
margin_right = 306.114
margin_bottom = 472.494
[node name="PointData" parent="CanvasLayer/DataTooltip" index="0"]
visible = false
margin_left = 52.8643
margin_top = -115.56
margin_right = 52.7283
margin_bottom = -114.76
[editable path="CanvasLayer/DataTooltip"]

View File

@ -1,448 +0,0 @@
tool
extends Chart2D
class_name ScatterChart2D
"""
[ScatterChart2D] - General purpose node for Scatter Charts
A scatter plot (also called a scatterplot, scatter graph, scatter chart, scattergram, or scatter diagram)
is a type of plot or mathematical diagram using Cartesian coordinates to display values for typically two variables
for a set of data. If the points are coded (color/shape/size), one additional variable can be displayed.
The data are displayed as a collection of points, each having the value of one variable determining the position on
the horizontal axis and the value of the other variable determining the position on the vertical axis.
/ source : Wikipedia /
"""
func _point_plotted():
pass
func _ready():
_get_children()
func _get_children():
OutlinesTween = $OutlinesTween
PointTween = $PointTween
Functions = $Functions
GridTween = $GridTween
DataTooltip = $CanvasLayer/PointData
Outlines = $Outlines
Grid = $Grid
func _set_size(size : Vector2):
SIZE = size
build_chart()
if Engine.editor_hint:
_get_children()
Outlines.set_point_position(0,Vector2(origin.x,0))
Outlines.set_point_position(1,Vector2(SIZE.x,0))
Outlines.set_point_position(2,Vector2(SIZE.x,origin.y))
Outlines.set_point_position(3,origin)
Outlines.set_point_position(4,Vector2(origin.x,0))
Grid.get_node("VLine").set_point_position(0,Vector2((OFFSET.x+SIZE.x)/2,0))
Grid.get_node("VLine").set_point_position(1,Vector2((OFFSET.x+SIZE.x)/2,origin.y))
Grid.get_node("HLine").set_point_position(0,Vector2(origin.x,origin.y/2))
Grid.get_node("HLine").set_point_position(1,Vector2(SIZE.x,origin.y/2))
func clear():
Outlines.points = []
Grid.get_node("HLine").queue_free()
Grid.get_node("VLine").queue_free()
func load_font():
if font != null:
font_size = font.get_height()
var theme : Theme = Theme.new()
theme.set_default_font(font)
DataTooltip.set_theme(theme)
else:
var lbl = Label.new()
font = lbl.get_font("")
lbl.free()
if bold_font != null:
DataTooltip.Data.set("custom_fonts/font",bold_font)
func _plot(source_ : String, delimiter_ : String, are_values_columns_ : bool, x_values_index_ : int):
randomize()
clear()
load_font()
DataTooltip.hide()
datas = read_data(source_,delimiter_)
count_functions()
structure_data(datas,are_values_columns_,x_values_index_)
build_chart()
calculate_pass()
calculate_coordinates()
calculate_colors()
draw_chart()
create_legend()
emit_signal("chart_plotted", self)
func plot():
randomize()
clear()
load_font()
DataTooltip.hide()
if source == "" or source == null:
ECUtilities._print_message("Can't plot a chart without a Source file. Please, choose it in editor, or use the custom function _plot().",1)
return
datas = read_data(source,delimiter)
count_functions()
structure_data(datas,are_values_columns,x_values_index)
build_chart()
calculate_pass()
calculate_coordinates()
calculate_colors()
draw_chart()
create_legend()
emit_signal("chart_plotted", self)
func calculate_colors():
if function_colors.empty() or function_colors.size() < functions:
for function in functions:
function_colors.append(Color("#1e1e1e") as Color)
func draw_chart():
draw_outlines()
draw_v_grid()
draw_h_grid()
draw_functions()
func draw_outlines():
if boxed:
Outlines.set_default_color(box_color)
OutlinesTween.interpolate_method(Outlines,"add_point",
Vector2(origin.x,0),Vector2(SIZE.x,0),drawing_duration*0.5,Tween.TRANS_QUINT,Tween.EASE_OUT)
OutlinesTween.start()
yield(OutlinesTween,"tween_all_completed")
OutlinesTween.interpolate_method(Outlines,"add_point",
Vector2(SIZE.x,0),Vector2(SIZE.x,origin.y),drawing_duration*0.5,Tween.TRANS_QUINT,Tween.EASE_OUT)
OutlinesTween.start()
yield(OutlinesTween,"tween_all_completed")
OutlinesTween.interpolate_method(Outlines,"add_point",
Vector2(SIZE.x,origin.y),origin,drawing_duration*0.5,Tween.TRANS_QUINT,Tween.EASE_OUT)
OutlinesTween.start()
yield(OutlinesTween,"tween_all_completed")
OutlinesTween.interpolate_method(Outlines,"add_point",
origin,Vector2(origin.x,0),drawing_duration*0.5,Tween.TRANS_QUINT,Tween.EASE_OUT)
OutlinesTween.start()
yield(OutlinesTween,"tween_all_completed")
func draw_v_grid():
for p in x_chors.size():
var point : Vector2 = origin+Vector2((p)*x_pass,0)
var v_grid : Line2D = Line2D.new()
Grid.add_child(v_grid)
v_grid.set_width(1)
v_grid.set_default_color(v_lines_color)
add_label(point+Vector2(-const_width/2*x_chors[p].length(),font_size/2), x_chors[p])
GridTween.interpolate_method(v_grid,"add_point",point,point-Vector2(0,SIZE.y-OFFSET.y),drawing_duration/(x_chors.size()),Tween.TRANS_EXPO,Tween.EASE_OUT)
GridTween.start()
yield(GridTween,"tween_all_completed")
func draw_h_grid():
for p in y_chors.size():
var point : Vector2 = origin-Vector2(0,(p)*y_pass)
var h_grid : Line2D = Line2D.new()
Grid.add_child(h_grid)
h_grid.set_width(1)
h_grid.set_default_color(h_lines_color)
add_label(point-Vector2(y_chors[p].length()*const_width+font_size,font_size/2), y_chors[p])
GridTween.interpolate_method(h_grid,"add_point",point,Vector2(SIZE.x,point.y),drawing_duration/(y_chors.size()),Tween.TRANS_EXPO,Tween.EASE_OUT)
GridTween.start()
yield(GridTween,"tween_all_completed")
func add_label(point : Vector2, text : String):
var lbl : Label = Label.new()
if font != null:
lbl.set("custom_fonts/font",font)
lbl.set("custom_colors/font_color",font_color)
Grid.add_child(lbl)
lbl.rect_position = point
lbl.set_text(text)
func draw_functions():
for function in point_positions.size():
draw_function(function,point_positions[function])
func draw_function(f_index : int, function : Array):
var pointv : Point
for point in function.size():
pointv = point_node.instance()
Functions.add_child(pointv)
pointv.connect("_mouse_entered",self,"show_data")
pointv.connect("_mouse_exited",self,"hide_data")
pointv.connect("_point_pressed",self,"point_pressed")
pointv.create_point(point_shape, function_colors[f_index], Color.white, function[point],
pointv.format_value(point_values[f_index][point],false,false),
y_labels[point if invert_chart else f_index] as String)
yield(get_tree().create_timer(drawing_duration/function.size()), "timeout")
func read_data(source : String, delimiter : String):
var file : File = File.new()
file.open(source,File.READ)
var content : Array
while not file.eof_reached():
var line : PoolStringArray = file.get_csv_line(delimiter)
content.append(line)
file.close()
for data in content:
if data.size() < 2:
content.erase(data)
return content
func structure_data(database : Array, are_values_columns : bool, x_values_index : int):
# @x_values_index can be either a column or a row relative to x values
# @y_values can be either a column or a row relative to y values
self.are_values_columns = are_values_columns
match are_values_columns:
true:
for row in database.size():
var t_vals : Array
for column in database[row].size():
if column == x_values_index:
var x_data = database[row][column]
if x_data.is_valid_float() or x_data.is_valid_integer():
x_datas.append(x_data as float)
else:
x_datas.append(x_data.replace(",",".") as float)
else:
if row != 0:
var y_data = database[row][column]
if y_data.is_valid_float() or y_data.is_valid_integer():
t_vals.append(y_data as float)
else:
t_vals.append(y_data.replace(",",".") as float)
else:
y_labels.append(str(database[row][column]))
if not t_vals.empty():
y_datas.append(t_vals)
x_label = str(x_datas.pop_front())
false:
for row in database.size():
if row == x_values_index:
x_datas = (database[row])
x_label = x_datas.pop_front() as String
else:
var values = database[row] as Array
y_labels.append(values.pop_front() as String)
y_datas.append(values)
for data in y_datas:
for value in data.size():
data[value] = data[value] as float
# draw y labels
var to_order : Array
var to_order_min : Array
for cluster in y_datas.size():
# define x_chors and y_chors
var ordered_cluster = y_datas[cluster] as Array
ordered_cluster.sort()
ordered_cluster = PoolIntArray(ordered_cluster)
var margin_max = ordered_cluster[ordered_cluster.size()-1]
var margin_min = ordered_cluster[0]
to_order.append(margin_max)
to_order_min.append(margin_min)
to_order.sort()
to_order_min.sort()
var margin = to_order.pop_back()
if not origin_at_zero:
y_margin_min = to_order_min.pop_front()
v_dist = y_decim * pow(10.0,str(margin).length()-2)
var multi = 0
var p = (v_dist*multi) + ((y_margin_min) if not origin_at_zero else 0)
y_chors.append(p as String)
while p < margin:
multi+=1
p = (v_dist*multi) + ((y_margin_min) if not origin_at_zero else 0)
y_chors.append(p as String)
# draw x_labels
if not show_x_values_as_labels:
to_order.clear()
to_order = x_datas as PoolIntArray
to_order.sort()
margin = to_order.pop_back()
if not origin_at_zero:
x_margin_min = to_order.pop_front()
h_dist = x_decim * pow(10.0,str(margin).length()-2)
multi = 0
p = (h_dist*multi) + ((x_margin_min) if not origin_at_zero else 0)
x_labels.append(p as String)
while p < margin:
multi+=1
p = (h_dist*multi) + ((x_margin_min) if not origin_at_zero else 0)
x_labels.append(p as String)
func build_chart():
origin = Vector2(OFFSET.x,SIZE.y-OFFSET.y)
func calculate_pass():
if invert_chart:
x_chors = y_labels as PoolStringArray
else:
if show_x_values_as_labels:
x_chors = x_datas as PoolStringArray
else:
x_chors = x_labels
# calculate distance in pixel between 2 consecutive values/datas
x_pass = (SIZE.x - OFFSET.x) / (x_chors.size()-1)
y_pass = origin.y / (y_chors.size()-1)
func calculate_coordinates():
x_coordinates.clear()
y_coordinates.clear()
point_values.clear()
point_positions.clear()
if invert_chart:
for column in y_datas[0].size():
var single_coordinates : Array
for row in y_datas:
if origin_at_zero:
single_coordinates.append((row[column]*y_pass)/v_dist)
else:
single_coordinates.append((row[column] - y_margin_min)*y_pass/v_dist)
y_coordinates.append(single_coordinates)
else:
for cluster in y_datas:
var single_coordinates : Array
for value in cluster.size():
if origin_at_zero:
single_coordinates.append((cluster[value]*y_pass)/v_dist)
else:
single_coordinates.append((cluster[value] - y_margin_min)*y_pass/v_dist)
y_coordinates.append(single_coordinates)
if show_x_values_as_labels:
for x in x_datas.size():
x_coordinates.append(x_pass*x)
else:
for x in x_datas.size():
if origin_at_zero:
if invert_chart:
x_coordinates.append(x_pass*x)
else:
x_coordinates.append(x_datas[x]*x_pass/h_dist)
else:
x_coordinates.append((x_datas[x] - x_margin_min)*x_pass/h_dist)
for f in functions:
point_values.append([])
point_positions.append([])
if invert_chart:
for function in y_coordinates.size():
for function_value in y_coordinates[function].size():
if are_values_columns:
point_positions[function_value].append(Vector2(x_coordinates[function]+origin.x, origin.y-y_coordinates[function][function_value]))
point_values[function_value].append([x_datas[function_value],y_datas[function_value][function]])
else:
point_positions[function].append(Vector2(x_coordinates[function_value]+origin.x,origin.y-y_coordinates[function][function_value]))
point_values[function].append([x_datas[function_value],y_datas[function_value][function]])
else:
for cluster in y_coordinates.size():
for y in y_coordinates[cluster].size():
if are_values_columns:
point_values[y].append([x_datas[cluster],y_datas[cluster][y]])
point_positions[y].append(Vector2(x_coordinates[cluster]+origin.x,origin.y-y_coordinates[cluster][y]))
else:
point_values[cluster].append([x_datas[y],y_datas[cluster][y]])
point_positions[cluster].append(Vector2(x_coordinates[y]+origin.x,origin.y-y_coordinates[cluster][y]))
func redraw():
build_chart()
calculate_pass()
calculate_coordinates()
update()
func show_data(point):
DataTooltip.update_datas(point)
DataTooltip.show()
func hide_data():
DataTooltip.hide()
func clean_points():
function_colors.clear()
if Functions.get_children():
for function in Functions.get_children():
function.queue_free()
func set_legend(l : Array):
legend = l
func get_legend():
return legend
func invert_chart():
invert_chart = !invert_chart
count_functions()
redraw()
create_legend()
func count_functions():
if are_values_columns:
if not invert_chart:
functions = datas[0].size()-1
else:
functions = datas.size()-1
else:
if invert_chart:
functions = datas[0].size()-1
else:
functions = datas.size()-1
func create_legend():
legend.clear()
for function in functions:
var function_legend = FunctionLegend.instance()
var f_name : String
if invert_chart:
f_name = x_datas[function] as String
else:
f_name = y_labels[function]
var legend_font : Font
if font != null:
legend_font = font
if bold_font != null:
legend_font = bold_font
function_legend.create_legend(f_name,function_colors[function],bold_font,font_color)
legend.append(function_legend)
func apply_template(template_name : int):
template = template_name
templates = ECUtilities._load_templates()
if template_name!=null:
var custom_template = templates.get(templates.keys()[template_name])
function_colors = custom_template.function_colors as PoolColorArray
v_lines_color = Color(custom_template.v_lines_color)
h_lines_color = Color(custom_template.h_lines_color)
box_color = Color(custom_template.outline_color)
font_color = Color(custom_template.font_color)
property_list_changed_notify()
if Engine.editor_hint:
_get_children()
Outlines.set_default_color(box_color)
Grid.get_node("VLine").set_default_color(v_lines_color)
Grid.get_node("HLine").set_default_color(h_lines_color)
func _enter_tree():
_ready()

View File

@ -1,59 +0,0 @@
[gd_scene load_steps=3 format=2]
[ext_resource path="res://addons/easy_charts/2d_charts/ScatterChart2D/scatter_chart2D.gd" type="Script" id=1]
[ext_resource path="res://addons/easy_charts/utilities/containers/data_tooltip/data_tooltip.tscn" type="PackedScene" id=2]
[node name="ScatterChart2D" type="Node2D"]
script = ExtResource( 1 )
__meta__ = {
"_edit_group_": true,
"_editor_description_": "[ScatterChart2D] - General purpose node for Scatter Charts
A scatter plot (also called a scatterplot, scatter graph, scatter chart, scattergram, or scatter diagram)
is a type of plot or mathematical diagram using Cartesian coordinates to display values for typically two variables
for a set of data. If the points are coded (color/shape/size), one additional variable can be displayed.
The data are displayed as a collection of points, each having the value of one variable determining the position on
the horizontal axis and the value of the other variable determining the position on the vertical axis."
}
function_colors = [ "#1e1e1e", "#1e1e1e", "#1e1e1e", "#1e1e1e" ]
[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="OutlinesTween" type="Tween" parent="."]
[node name="GridTween" type="Tween" parent="."]
[node name="PointTween" type="Tween" parent="."]
[node name="CanvasLayer" type="CanvasLayer" parent="."]
[node name="PointData" parent="CanvasLayer" instance=ExtResource( 2 )]
margin_left = -172.972
margin_top = -46.9003
margin_right = -152.972
margin_bottom = -36.9003
[node name="PointData" parent="CanvasLayer/PointData" index="0"]
margin_left = 71.2491
margin_top = -146.673
margin_right = 71.1131
margin_bottom = -145.873
[editable path="CanvasLayer/PointData"]

View File

@ -1,307 +0,0 @@
tool
extends Spatial
class_name ScatterChart3D
"""
[ScatterChart] - General purpose node for Scatter Charts
A scatter plot (also called a scatterplot, scatter graph, scatter chart, scattergram, or scatter diagram)
is a type of plot or mathematical diagram using Cartesian coordinates to display values for typically two variables
for a set of data. If the points are coded (color/shape/size), one additional variable can be displayed.
The data are displayed as a collection of points, each having the value of one variable determining the position on
the horizontal axis and the value of the other variable determining the position on the vertical axis.
/ source : Wikipedia /
"""
onready var PlaceholderPoint = $Chart/Point
onready var Space = $ImmediateGeometry
onready var DataTooltip = $DataTooltip/DataTooltip
var font_size : float = 16
var const_height : float = font_size/2*font_size/20
var const_width : float = font_size/2
var OFFSET : Vector2 = Vector2(0,0)
#-------------------------------------------------------------------------#
var origin : Vector2
# actual distance between x and y values
var x_pass : float
var y_pass : float
# vertical distance between y consecutive points used for intervals
var v_dist : float
var h_dist : float
# quantization, representing the interval in which values will be displayed
# define values on x an y axis
var x_chors : Array
var y_chors : Array
# actual coordinates of points (in pixel)
var x_coordinates : Array
var y_coordinates : Array
# datas contained in file
var datas : Array
# amount of functions to represent
var functions : int = 0
var x_label : String
var z_label : String
# database values
var x_datas : Array
var z_datas : Array
var y_datas : Array
# labels displayed on chart
var x_labels : Array
var y_labels : Array
var x_margin_min : int = 0
var y_margin_min : int = 0
# actual values of point, from the database
var point_values : Array
# actual position of points in pixel
var point_positions : Array
var legend : Array setget set_legend,get_legend
# ---------------------
export (Vector2) var SIZE : Vector2 = Vector2() setget _set_size
export (String, FILE, "*.txt, *.csv") var source : String = ""
export (String) var delimiter : String = ";"
export (bool) var origin_at_zero : bool = true
export (bool) var are_values_columns : bool = false
export (int,0,100) var x_values_index : int = 0
export (int,0,100) var z_values_index : int = 0
export(bool) var show_x_values_as_labels : bool = true
#export (float,1,20,0.5) var column_width : float = 10
#export (float,0,10,0.5) var column_gap : float = 2
export (float,0.1,10.0) var x_decim : float = 5.0
export (float,0.1,10.0) var y_decim : float = 5.0
export (int,"Dot,Triangle,Square") var point_shape : int = 0
export (PoolColorArray) var function_colors = [Color("#1e1e1e")]
export (Color) var v_lines_color : Color = Color("#cacaca")
export (Color) var h_lines_color : Color = Color("#cacaca")
export (bool) var boxed : bool = true
export (Color) var box_color : Color = Color("#1e1e1e")
export (Font) var font : Font
export (Font) var bold_font : Font
export (Color) var font_color : Color = Color("#1e1e1e")
export (String,"Default","Clean","Gradient","Minimal","Invert") var template : String = "Default" setget apply_template
export (float,0.1,1) var drawing_duration : float = 0.5
export (bool) var invert_chart : bool = false
var templates : Dictionary = {}
signal chart_plotted(chart)
signal point_pressed(point)
func _point_plotted():
pass
func _ready():
pass
func _set_size(size : Vector2):
SIZE = size
# build_chart()
func clear():
pass
func load_font():
if font != null:
font_size = font.get_height()
var theme : Theme = Theme.new()
theme.set_default_font(font)
DataTooltip.set_theme(theme)
else:
var lbl = Label.new()
font = lbl.get_font("")
lbl.free()
if bold_font != null:
DataTooltip.Data.set("custom_fonts/font",bold_font)
func _plot(source_ : String, delimiter_ : String, are_values_columns_ : bool, x_values_index_ : int):
randomize()
clear()
load_font()
DataTooltip.hide()
datas = read_data(source_,delimiter_)
# count_functions()
structure_data(datas,are_values_columns_,x_values_index_)
# build_chart()
# calculate_pass()
# calculate_coordinates()
# calculate_colors()
# draw_chart()
#
# create_legend()
emit_signal("chart_plotted", self)
func plot():
randomize()
clear()
load_font()
DataTooltip.hide()
if source == "" or source == null:
ECUtilities._print_message("Can't plot a chart without a Source file. Please, choose it in editor, or use the custom function _plot().",1)
return
datas = read_data(source,delimiter)
# count_functions()
structure_data(datas,are_values_columns,x_values_index)
# build_chart()
# calculate_pass()
# calculate_coordinates()
# calculate_colors()
# draw_chart()
# create_legend()
emit_signal("chart_plotted", self)
func read_data(source : String, delimiter : String):
var file : File = File.new()
file.open(source,File.READ)
var content : Array
while not file.eof_reached():
var line : PoolStringArray = file.get_csv_line(delimiter)
content.append(line)
file.close()
for data in content:
if data.size() < 2:
content.erase(data)
return content
func structure_data(database : Array, are_values_columns : bool, x_values_index : int):
# @x_values_index can be either a column or a row relative to x values
# @y_values can be either a column or a row relative to y values
self.are_values_columns = are_values_columns
match are_values_columns:
true:
for row in database.size():
var t_vals : Array
for column in database[row].size():
if column == x_values_index:
var x_data = database[row][column]
if x_data.is_valid_float() or x_data.is_valid_integer():
x_datas.append(x_data as float)
else:
x_datas.append(x_data.replace(",",".") as float)
elif column == z_values_index:
var z_data = database[row][column]
if z_data.is_valid_float() or z_data.is_valid_integer():
z_datas.append(z_data as float)
else:
z_datas.append(z_data.replace(",",".") as float)
else:
if row != 0:
var y_data = database[row][column]
if y_data.is_valid_float() or y_data.is_valid_integer():
t_vals.append(y_data as float)
else:
t_vals.append(y_data.replace(",",".") as float)
else:
y_labels.append(str(database[row][column]))
if not t_vals.empty():
y_datas.append(t_vals)
x_label = str(x_datas.pop_front())
z_label = str(z_datas.pop_front())
false:
for row in database.size():
if row == x_values_index:
x_datas = (database[row])
x_label = x_datas.pop_front() as String
else:
var values = database[row] as Array
y_labels.append(values.pop_front() as String)
y_datas.append(values)
for data in y_datas:
for value in data.size():
data[value] = data[value] as float
# draw y labels
var to_order : Array
var to_order_min : Array
for cluster in y_datas.size():
# define x_chors and y_chors
var ordered_cluster = y_datas[cluster] as Array
ordered_cluster.sort()
ordered_cluster = PoolIntArray(ordered_cluster)
var margin_max = ordered_cluster[ordered_cluster.size()-1]
var margin_min = ordered_cluster[0]
to_order.append(margin_max)
to_order_min.append(margin_min)
to_order.sort()
to_order_min.sort()
var margin = to_order.pop_back()
if not origin_at_zero:
y_margin_min = to_order_min.pop_front()
v_dist = y_decim * pow(10.0,str(margin).length()-2)
var multi = 0
var p = (v_dist*multi) + ((y_margin_min) if not origin_at_zero else 0)
y_chors.append(p as String)
while p < margin:
multi+=1
p = (v_dist*multi) + ((y_margin_min) if not origin_at_zero else 0)
y_chors.append(p as String)
# draw x_labels
if not show_x_values_as_labels:
to_order.clear()
to_order = x_datas as PoolIntArray
to_order.sort()
margin = to_order.pop_back()
if not origin_at_zero:
x_margin_min = to_order.pop_front()
h_dist = x_decim * pow(10.0,str(margin).length()-2)
multi = 0
p = (h_dist*multi) + ((x_margin_min) if not origin_at_zero else 0)
x_labels.append(p as String)
while p < margin:
multi+=1
p = (h_dist*multi) + ((x_margin_min) if not origin_at_zero else 0)
x_labels.append(p as String)
func set_legend(l : Array):
legend = l
func get_legend():
return legend
func apply_template(template_name : String):
template = template_name
templates = ECUtilities._load_templates()
if template_name!=null and template_name!="":
var custom_template = templates[template.to_lower()]
function_colors = custom_template.function_colors
v_lines_color = Color(custom_template.v_lines_color)
h_lines_color = Color(custom_template.h_lines_color)
box_color = Color(custom_template.outline_color)
font_color = Color(custom_template.font_color)
property_list_changed_notify()
func _enter_tree():
_ready()

View File

@ -1,59 +0,0 @@
[gd_scene load_steps=7 format=2]
[ext_resource path="res://addons/easy_charts/3d_charts/ScatterChart3D/scatter_chart3D.gd" type="Script" id=1]
[ext_resource path="res://addons/easy_charts/utilities/containers/data_tooltip/data_tooltip.tscn" type="PackedScene" id=2]
[sub_resource type="SpatialMaterial" id=1]
flags_unshaded = true
[sub_resource type="PlaneMesh" id=2]
material = SubResource( 1 )
size = Vector2( 20, 20 )
[sub_resource type="SphereMesh" id=3]
[sub_resource type="SpatialMaterial" id=4]
albedo_color = Color( 0, 1, 0.156863, 1 )
[node name="ScatterChart3D" type="Spatial"]
script = ExtResource( 1 )
function_colors = [ "#1e1e1e", "#1e1e1e", "#1e1e1e", "#1e1e1e" ]
[node name="MeshInstance" type="MeshInstance" parent="."]
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -1, 0 )
mesh = SubResource( 2 )
material/0 = null
[node name="Chart" type="Spatial" parent="."]
[node name="Point" type="MeshInstance" parent="Chart"]
mesh = SubResource( 3 )
material/0 = SubResource( 4 )
[node name="Camera" type="Camera" parent="."]
transform = Transform( 0.707107, -0.40558, 0.579228, 0, 0.819152, 0.573577, -0.707107, -0.40558, 0.579228, 10, 15, 10 )
projection = 1
current = true
size = 30.0
near = 0.01
[node name="OmniLight" type="OmniLight" parent="."]
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 9, 0 )
[node name="CanvasLayer" type="CanvasLayer" parent="."]
[node name="DataTooltip" parent="CanvasLayer" instance=ExtResource( 2 )]
margin_left = 277.135
margin_top = 449.025
margin_right = 276.999
margin_bottom = 449.825
[node name="PointData" parent="CanvasLayer/DataTooltip" index="0"]
visible = false
margin_left = 47.2074
margin_top = -150.915
margin_right = 47.0714
margin_bottom = -150.115
[editable path="CanvasLayer/DataTooltip"]

View File

@ -1,84 +0,0 @@
tool
extends ScatterChartBase
class_name ColumnChart
"""
[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.
The bars can be plotted vertically or horizontally. A vertical bar chart is sometimes
called a column chart.
A bar graph shows comparisons among discrete categories. One axis of the chart shows
the specific categories being compared, and the other axis represents a measured value.
Some bar graphs present bars clustered in groups of more than one, showing the
values of more than one measured variable.
/ source : Wikipedia /
"""
# ---------------------
func build_property_list():
.build_property_list()
property_list.append(
{
"hint": PROPERTY_HINT_RANGE,
"hint_string": "1,20,0.5",
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
"name": "Chart_Properties/column_width",
"type": TYPE_REAL
})
property_list.append(
{
"hint": PROPERTY_HINT_RANGE,
"hint_string": "0,10,0.5",
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
"name": "Chart_Properties/column_gap",
"type": TYPE_REAL
})
func _get_property_list():
property_list[0].name = "ColumnChart"
return property_list
# @Override
func calculate_coordinates():
point_values.clear()
point_positions.clear()
for _i in y_labels.size():
point_values.append([])
point_positions.append([])
for function in y_labels.size():
for val in x_datas[function].size():
var value_x: float = (int(x_datas[function][val]) - x_margin_min) * x_pass / h_dist if h_dist else \
x_chors.find(String(x_datas[function][val])) * x_pass
var value_y: float = (y_datas[function][val] - y_margin_min) * y_pass / v_dist if v_dist else 0
var column_offset: float = column_width/2 + (column_width + column_gap)*function - (column_width + column_gap)*functions/2
point_values[function].append([x_datas[function][val], y_datas[function][val]])
point_positions[function].append(Vector2(value_x + origin.x + column_offset, origin.y - value_y))
func _draw():
draw_grid()
draw_chart_outlines()
if show_points:
draw_points()
draw_columns()
draw_treshold()
func draw_columns():
for function in point_values.size():
for function_point in range(0, point_values[function].size()):
draw_line(
Vector2(point_positions[function][function_point].x, origin.y),
point_positions[function][function_point],
function_colors[function],
column_width,
false)
# draw_string(font, Vector2(point_positions[function][function_point].x, origin.y+10), y_labels[function_point], font_color)

View File

@ -1,104 +0,0 @@
[gd_scene load_steps=4 format=2]
[ext_resource path="res://addons/easy_charts/utilities/containers/data_tooltip/data_tooltip.tscn" type="PackedScene" id=1]
[ext_resource path="res://addons/easy_charts/control_charts/ColumnChart/column_chart.gd" type="Script" id=2]
[sub_resource type="Theme" id=1]
[node name="ColumnChart" type="Control"]
anchor_right = 1.0
anchor_bottom = 1.0
rect_min_size = Vector2( 70, 50 )
mouse_filter = 2
script = ExtResource( 2 )
__meta__ = {
"The file you want to use as a source for your chart. It must be a *.CSV file, or a plain *.TXT file formatted in the right way.": "",
"_edit_use_anchors_": false,
"_editor_description_": "[BarChart] - 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.
The bars can be plotted vertically or horizontally. A vertical bar chart is sometimes
called a column chart.
A bar graph shows comparisons among discrete categories. One axis of the chart shows
the specific categories being compared, and the other axis represents a measured value.
Some bar graphs present bars clustered in groups of more than one, showing the
values of more than one measured variable."
}
Chart_Properties/are_values_columns = false
Chart_Properties/labels_index = 0
Chart_Properties/show_x_values_as_labels = false
Chart_Display/autoscale_x = true
Chart_Display/x_decim = 1.0
Chart_Display/autoscale_y = true
Chart_Display/y_decim = 1.0
Chart_Display/show_points = true
Chart_Style/points_shape = [ 0 ]
Chart_Style/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 )
Chart_Style/box_color = Color( 0.117647, 0.117647, 0.117647, 1 )
Chart_Style/grid_lines_width = 1
Chart_Style/v_lines_color = Color( 0.792157, 0.792157, 0.792157, 1 )
Chart_Style/h_lines_color = Color( 0.792157, 0.792157, 0.792157, 1 )
Chart_Style/font = null
Chart_Style/bold_font = null
Chart_Style/font_color = Color( 0.117647, 0.117647, 0.117647, 1 )
Chart_Style/use_template = true
Chart_Style/template = 0
Chart_Modifiers/treshold = Vector2( 0, 0 )
Chart_Modifiers/only_disp_values = Vector2( 0, 0 )
Chart_Modifiers/invert_chart = false
Chart_Properties/column_width = 10.0
Chart_Properties/column_gap = 2.0
[node name="Background" type="ColorRect" parent="."]
visible = false
show_behind_parent = true
anchor_right = 1.0
anchor_bottom = 1.0
color = Color( 0.882353, 0.882353, 0.882353, 1 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Points" type="Control" parent="."]
anchor_right = 1.0
anchor_bottom = 1.0
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Legend" type="HBoxContainer" parent="."]
visible = false
margin_right = 1024.0
margin_bottom = 64.0
alignment = 1
__meta__ = {
"_edit_use_anchors_": false
}
[node name="ChartName" type="Label" parent="."]
anchor_right = 1.0
anchor_bottom = 0.0266667
align = 1
valign = 1
__meta__ = {
"_edit_use_anchors_": true
}
[node name="CanvasLayer" type="CanvasLayer" parent="."]
[node name="DataTooltip" parent="CanvasLayer" instance=ExtResource( 1 )]
margin_left = -52.7918
margin_top = -28.9529
margin_right = -52.9281
margin_bottom = -28.1529
[node name="PointData" parent="CanvasLayer/DataTooltip" index="0"]
margin_left = -593.381
margin_top = -80.9071
margin_right = -593.517
margin_bottom = -80.107
theme = SubResource( 1 )
[editable path="CanvasLayer/DataTooltip"]

View File

@ -1,59 +0,0 @@
tool
extends ScatterChartBase
class_name LineChart
# [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
# displays information as a series of data points called 'markers'
# connected by straight line segments.
# It is a basic type of chart common in many fields. It is similar to a scatter plot
# except that the measurement points are ordered (typically by their x-axis value)
# and joined with straight line segments.
# A line chart is often used to visualize a trend in data over intervals of time -
# a time series - thus the line is often drawn chronologically.
# In these cases they are known as run charts.
# Source: Wikipedia
func build_property_list():
.build_property_list()
#Find first element of Chart Display
var position
for i in property_list.size():
if property_list[i]["name"].find("Chart_Style") != -1: #Found
position = i
break
property_list.insert(position + 2, #I want it to be below point shape and function colors
{
"hint": PROPERTY_HINT_RANGE,
"hint_string": "1, 100, 1",
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
"name": "Chart_Style/function_line_width",
"type": TYPE_INT
})
func _get_property_list():
property_list[0].name = "LineChart"
return property_list
func _draw():
draw_grid()
draw_chart_outlines()
if show_points:
draw_points()
draw_lines()
draw_treshold()
func draw_lines():
for function in point_values.size():
for function_point in range(1, point_values[function].size()):
draw_line(
point_positions[function][function_point - 1],
point_positions[function][function_point],
function_colors[function],
function_line_width,
false)

View File

@ -1,101 +0,0 @@
[gd_scene load_steps=4 format=2]
[ext_resource path="res://addons/easy_charts/utilities/containers/data_tooltip/data_tooltip.tscn" type="PackedScene" id=1]
[ext_resource path="res://addons/easy_charts/control_charts/LineChart/line_chart.gd" type="Script" id=4]
[sub_resource type="Theme" id=1]
[node name="LineChart" type="Control"]
anchor_right = 1.0
anchor_bottom = 1.0
rect_min_size = Vector2( 70, 50 )
mouse_filter = 2
script = ExtResource( 4 )
__meta__ = {
"_edit_use_anchors_": false,
"_editor_description_": "[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
displays information as a series of data points called 'markers'
connected by straight line segments.
It is a basic type of chart common in many fields. It is similar to a scatter plot
except that the measurement points are ordered (typically by their x-axis value)
and joined with straight line segments.
A line chart is often used to visualize a trend in data over intervals of time
a time series thus the line is often drawn chronologically.
In these cases they are known as run charts."
}
Chart_Properties/are_values_columns = true
Chart_Properties/labels_index = 0
Chart_Display/autoscale_x = true
Chart_Display/x_decim = 1.0
Chart_Display/autoscale_y = true
Chart_Display/y_decim = 1.0
Chart_Display/show_points = true
Chart_Style/points_shape = [ 0 ]
Chart_Style/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 )
Chart_Style/function_line_width = 2
Chart_Style/box_color = Color( 0.117647, 0.117647, 0.117647, 1 )
Chart_Style/grid_lines_width = 1
Chart_Style/v_lines_color = Color( 0.792157, 0.792157, 0.792157, 1 )
Chart_Style/h_lines_color = Color( 0.792157, 0.792157, 0.792157, 1 )
Chart_Style/font = null
Chart_Style/bold_font = null
Chart_Style/font_color = Color( 0.117647, 0.117647, 0.117647, 1 )
Chart_Style/use_template = true
Chart_Style/template = 0
Chart_Modifiers/treshold = Vector2( 0, 0 )
Chart_Modifiers/only_disp_values = Vector2( 0, 0 )
Chart_Modifiers/invert_chart = false
[node name="Background" type="ColorRect" parent="."]
visible = false
show_behind_parent = true
anchor_right = 1.0
anchor_bottom = 1.0
color = Color( 0.882353, 0.882353, 0.882353, 1 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="ChartName" type="Label" parent="."]
visible = false
anchor_right = 1.0
margin_bottom = 14.0
align = 1
valign = 1
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Points" type="Control" parent="."]
anchor_right = 1.0
anchor_bottom = 1.0
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Legend" type="HBoxContainer" parent="."]
visible = false
margin_right = 1024.0
margin_bottom = 64.0
alignment = 1
__meta__ = {
"_edit_use_anchors_": false
}
[node name="CanvasLayer" type="CanvasLayer" parent="."]
[node name="DataTooltip" parent="CanvasLayer" instance=ExtResource( 1 )]
margin_left = -136.674
margin_top = -56.7206
margin_right = -136.81
margin_bottom = -55.9206
[node name="PointData" parent="CanvasLayer/DataTooltip" index="0"]
margin_left = -189.809
margin_top = -60.3698
margin_right = -189.947
margin_bottom = -59.5707
theme = SubResource( 1 )
[editable path="CanvasLayer/DataTooltip"]

View File

@ -1,232 +0,0 @@
tool
extends Chart
class_name PieChart
var should_draw : bool = false
var area_angles : Array
var slices : Array
var areas : Array
var areas_interacted : Array
class CustomSorter:
static func sort_ascending(a,b):
if a[1] < b[1]:
return true
return false
func _get_property_list():
return [
# Chart Properties
{
"hint": PROPERTY_HINT_NONE,
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
"name": "Chart_Properties/are_values_columns",
"type": TYPE_BOOL
},
{
"hint": PROPERTY_HINT_RANGE,
"hint_string": "-1,100,1",
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
"name": "Chart_Properties/labels_index",
"type": TYPE_INT
},
{
"hint": PROPERTY_HINT_NONE,
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
"name": "Chart_Properties/show_x_values_as_labels",
"type": TYPE_BOOL
},
# Chart Style
{
"hint": PROPERTY_HINT_NONE,
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
"name": "Chart_Style/function_colors",
"type": TYPE_COLOR_ARRAY
},
{
"class_name": "Font",
"hint": PROPERTY_HINT_RESOURCE_TYPE,
"hint_string": "Font",
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
"name": "Chart_Style/font",
"type": TYPE_OBJECT
},
{
"class_name": "Font",
"hint": PROPERTY_HINT_RESOURCE_TYPE,
"hint_string": "Font",
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
"name": "Chart_Style/bold_font",
"type": TYPE_OBJECT
},
{
"hint": PROPERTY_HINT_NONE,
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
"name": "Chart_Style/font_color",
"type": TYPE_COLOR
},
{
"hint": PROPERTY_HINT_ENUM,
"hint_string": PoolStringArray(ECUtilities.templates.keys()).join(","),
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
"name": "Chart_Style/template",
"type": TYPE_INT
},
# Chart Modifiers
{
"hint": PROPERTY_HINT_RANGE,
"hint_string": "0,360",
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
"name": "Chart_Modifiers/rotation",
"type": TYPE_REAL
},
]
# Called when the node enters the scene tree for the first time.
func _ready():
pass
func plot_placeholder() -> void:
data = [
["United States",46],
["Great Britain",27],
["China",26],
["Russia",19],
["Germany",17]
]
function_colors = [
Color.red,
Color.white,
Color.yellow,
Color.green,
Color.blue
]
plot(data)
func structure_data(database: Array):
# @labels_index can be either a column or a row relative to x values
clean_variables()
are_values_columns = invert_chart != are_values_columns
if are_values_columns:
for row in database.size():
var t_vals: Array
for column in database[row].size():
if column == labels_index:
var x_data = database[row][column]
if x_data.is_valid_float() or x_data.is_valid_integer():
x_datas.append(x_data as float)
else:
x_datas.append(x_data.replace(",", ".") as float)
else:
if row != 0:
var y_data = database[row][column]
if y_data.is_valid_float() or y_data.is_valid_integer():
t_vals.append(y_data as float)
else:
t_vals.append(y_data.replace(",", ".") as float)
else:
y_labels.append(str(database[row][column]))
if not t_vals.empty():
y_datas.append(t_vals)
x_label = str(x_datas.pop_front())
else:
for row in database.size():
if row == labels_index:
x_datas = (database[row])
x_label = x_datas.pop_front() as String
else:
var values = database[row] as Array
y_labels.append(values.pop_front() as String)
y_datas.append(values)
for data in y_datas:
for value in data.size():
data[value] = data[value] as float
func build_chart():
SIZE = get_size()
origin = SIZE/2
radius = (SIZE.y/2 - 20) if SIZE.y < SIZE.x else (SIZE.x/2 - 20)
func calculate_pass():
var tot : float
for y_data in y_datas: tot+=y_data[0]
x_pass = 360/tot
func create_legend():
#temporary override due to error
pass
func calculate_coordinates():
area_angles.clear()
slices.clear()
areas.clear()
var from : float = 0.0
var to : float = y_datas[0][0]*x_pass
area_angles.append([from,to])
for info in range(y_datas.size()):
slices.append(Slice.new(y_labels[info], str(y_datas[info][0]), from, to, x_label+" : "+x_datas[0], function_colors[info]))
areas.append(calculate_circle_arc_polygon(origin, radius, from + rotation, to + rotation, function_colors[info]))
from = to
to = (y_datas[info+1][0]*x_pass + from) if info < y_datas.size()-1 else (360)
area_angles.append([from, to])
create_legend()
func calculate_circle_arc_polygon(center : Vector2, radius : float, angle_from : float, angle_to : float, color : Color) -> PoolVector2Array:
var nb_points : int = 32
var points_arc : PoolVector2Array = PoolVector2Array()
# var chord_angle : float = ((angle_to - angle_from)/2)+angle_from
# angle_from += 0.2
# angle_to -= 0.2
# var displacement : Vector2 = Vector2(cos(deg2rad(chord_angle)), sin(deg2rad(chord_angle-180))).normalized()*10
# print(displacement)
# center += displacement
# radius+=displacement.length()
points_arc.push_back(center)
var colors : PoolColorArray = PoolColorArray([color])
for i in range(nb_points + 1):
var angle_point = deg2rad(angle_from + i * (angle_to - angle_from) / nb_points - 90)
points_arc.push_back(center + Vector2(cos(angle_point), sin(angle_point)) * radius)
return points_arc
func _draw():
_draw_areas()
if mouse_on_slice:
_draw_arc(area_angles[mouse_on_area], mouse_on_area)
mouse_on_slice = false
func _draw_arc(arc : Array, index : int):
var temp_color : Color = function_colors[index]
temp_color.a = 0.7
draw_arc(origin, radius + 6, deg2rad(arc[0]-90 + rotation), deg2rad(arc[1]-90 + rotation), 32, temp_color, 4)
func _draw_areas():
for area_idx in range(areas.size()):
draw_polygon(areas[area_idx], [function_colors[area_idx]])
var mouse_on_area : int
var mouse_on_slice : bool = false
func _gui_input(event : InputEvent):
if event is InputEventMouseMotion:
for area_idx in range(areas.size()):
if Geometry.is_point_in_polygon(event.global_position - get_global_transform().origin, areas[area_idx]):
mouse_on_slice = true
mouse_on_area = area_idx
show_slice_data(slices[area_idx])
update()
if not mouse_on_slice:
mouse_on_area = -1
mouse_on_slice = false
hide_data()
update()

View File

@ -1,71 +0,0 @@
[gd_scene load_steps=3 format=2]
[ext_resource path="res://addons/easy_charts/control_charts/PieChart/pie_chart.gd" type="Script" id=1]
[ext_resource path="res://addons/easy_charts/utilities/containers/data_tooltip/data_tooltip.tscn" type="PackedScene" id=2]
[node name="PieChart" type="Control"]
anchor_right = 1.0
anchor_bottom = 0.993333
mouse_filter = 1
script = ExtResource( 1 )
__meta__ = {
"_edit_use_anchors_": true
}
Chart_Properties/are_values_columns = false
Chart_Properties/labels_index = 0
Chart_Properties/show_x_values_as_labels = true
Chart_Style/function_colors = [ Color( 0.117647, 0.117647, 0.117647, 1 ) ]
Chart_Style/font = null
Chart_Style/bold_font = null
Chart_Style/font_color = Color( 0.117647, 0.117647, 0.117647, 1 )
Chart_Style/template = 0
Chart_Modifiers/rotation = 0.0
[node name="Background" type="ColorRect" parent="."]
visible = false
anchor_right = 1.0
anchor_bottom = 1.0
mouse_filter = 1
[node name="Points" type="Control" parent="."]
anchor_right = 1.0
anchor_bottom = 1.0
mouse_filter = 1
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Legend" type="HBoxContainer" parent="."]
visible = false
anchor_right = 1.0
anchor_bottom = 0.106667
__meta__ = {
"_edit_use_anchors_": true
}
[node name="ChartName" type="Label" parent="."]
use_parent_material = true
anchor_right = 1.0
anchor_bottom = 0.0233333
mouse_filter = 1
align = 1
valign = 1
__meta__ = {
"_edit_use_anchors_": true
}
[node name="CanvasLayer" type="CanvasLayer" parent="."]
[node name="DataTooltip" parent="CanvasLayer" instance=ExtResource( 2 )]
margin_left = -492.874
margin_top = 766.681
margin_right = -493.01
margin_bottom = 767.481
[node name="PointData" parent="CanvasLayer/DataTooltip" index="0"]
margin_left = -314.458
margin_top = -189.587
margin_right = -314.559
margin_bottom = -188.787
[editable path="CanvasLayer/DataTooltip"]

View File

@ -1,287 +0,0 @@
tool
extends ScatterChartBase
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(Point.SHAPES.keys()).join(",")],
"name": "Chart_Style/points_shape",
"type": TYPE_ARRAY,
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE
},
{
"hint": PROPERTY_HINT_NONE,
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
"name": "Chart_Style/function_colors",
"type": TYPE_COLOR_ARRAY
},
{
"hint": PROPERTY_HINT_NONE,
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
"name": "Chart_Style/outline_color",
"type": TYPE_COLOR
},
{
"hint": PROPERTY_HINT_NONE,
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
"name": "Chart_Style/grid_color",
"type": TYPE_COLOR
},
{
"class_name": "Font",
"hint": PROPERTY_HINT_RESOURCE_TYPE,
"hint_string": "Font",
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
"name": "Chart_Style/font",
"type": TYPE_OBJECT
},
{
"class_name": "Font",
"hint": PROPERTY_HINT_RESOURCE_TYPE,
"hint_string": "Font",
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
"name": "Chart_Style/bold_font",
"type": TYPE_OBJECT
},
{
"hint": PROPERTY_HINT_NONE,
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
"name": "Chart_Style/font_color",
"type": TYPE_COLOR
},
{
"hint": PROPERTY_HINT_ENUM,
"hint_string": PoolStringArray(ECUtilities.templates.keys()).join(","),
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
"name": "Chart_Style/template",
"type": TYPE_INT
},
{
"hint": PROPERTY_HINT_RANGE,
"hint_string": "0,360",
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
"name": "Chart_Modifiers/rotation",
"type": TYPE_REAL
},
]
#func structure_data(database : Array):
# # @x_values_index can be either a column or a row relative to x values
# # @y_values can be either a column or a row relative to y values
# are_values_columns = invert_chart != are_values_columns
# match are_values_columns:
# true:
# for row in database.size():
# var t_row : Array = []
# for column in database[row].size():
# if row == labels_index:
# if column == function_names_index:
# pass
# else:
# x_labels.append(database[row][column])
# else:
# if column == function_names_index:
# y_labels.append(database[row][column])
# else:
# if typeof(database[row][column]) == TYPE_INT or typeof(database[row][column]) == TYPE_REAL:
# t_row.append(database[row][column] as float)
# else:
# t_row.append(database[row][column].replace(",", ".") as float)
# if not t_row.empty():
# x_datas.append(t_row)
# false:
# for row in database.size():
# if row == function_names_index:
# y_labels = database[row] as PoolStringArray
#
# var x_temp_datas : PoolRealArray = []
# for column in database[row].size():
# if column == labels_index:
# x_labels.append(database[row][column] as String)
# else:
# x_temp_datas.append(database[row][column] as float)
# x_datas.append(x_temp_datas)
#
#
# if labels_index == -1 :
# for data in x_datas[0].size():
# x_labels.append("Element %s" % data)
#
# if function_names_index == -1 :
# for data in x_datas.size():
# y_labels.append("Function %s" % data)
#
#func build_chart():
# SIZE = get_size()
# origin = OFFSET + SIZE/2
#
#
#func calculate_pass() :
# var ordered_max : Array
# for data in x_datas :
# var ordered_data : Array = data.duplicate()
# ordered_data.sort()
# ordered_max.append(ordered_data.pop_back())
# ordered_max.sort()
# var max_value : float = ordered_max.pop_back()
# var dist = full_scale * pow(10.0,str(max_value).length()-2)
# var multi = 0
# var value = dist * multi
# x_chors.append(value as String)
# while value < max_value:
# multi+=1
# value = dist * multi
# x_chors.append(value as String)
var radar_polygon : Array
func calculate_coordinates():
for chor in x_chors.size():
var inner_polyline : PoolVector2Array
var scalar_factor : float = (x_chors[chor] as float/x_chors.back() as float)
for function in functions:
var angle : float = ((2 * PI * function) / functions) - PI /2 + deg2rad(rotation)
var x_coordinate : float = (radius if (not use_height_as_radius and radius<SIZE.y/2) else SIZE.y/2) * scalar_factor * cos(angle) + origin.x
var y_coordinate : float = (radius if (not use_height_as_radius and radius<SIZE.y/2) else SIZE.y/2) * scalar_factor * sin(angle) + origin.y
inner_polyline.append(Vector2(x_coordinate, y_coordinate))
inner_polyline.append(inner_polyline[0])
radar_polygon.append(inner_polyline)
for datas in x_datas:
var function_positions : PoolVector2Array
var function_values : Array
for data in datas.size():
var scalar_factor : float = datas[data] /( x_chors.back() as float)
var angle : float = ((2 * PI * data) / datas.size()) - PI/2 + deg2rad(rotation)
var x_coordinate : float = (radius if (not use_height_as_radius and radius<SIZE.y/2) else SIZE.y/2) * scalar_factor * cos(angle) + origin.x
var y_coordinate : float = (radius if (not use_height_as_radius and radius<SIZE.y/2) else SIZE.y/2) * scalar_factor * sin(angle) + origin.y
function_positions.append(Vector2(x_coordinate,y_coordinate))
function_values.append([x_labels[data], datas[data]])
function_positions.append(function_positions[0])
point_positions.append(function_positions)
point_values.append(function_values)
func _draw():
if Engine.editor_hint:
return
clean_points()
draw_grid()
for function in point_positions.size():
var function_color : Color = function_colors[function]
draw_polygon(point_positions[function], [Color(function_color.r, function_color.g, function_color.b, 0.2)],[],null,null,true)
draw_polyline(point_positions[function], function_color, 2,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])
PointContainer.add_child(point)
func draw_grid():
for polyline in radar_polygon:
draw_polyline(polyline, grid_color, 1, true)
var text : String = x_chors[radar_polygon.find(polyline)] as String
draw_string(font, polyline[0] - Vector2(font.get_string_size(text).x/2,-5), text, font_color)
if not radar_polygon.empty():
draw_polyline(radar_polygon[radar_polygon.size()-1], outline_color, 1, true)
for label in x_labels.size():
var point_array : PoolVector2Array = radar_polygon[radar_polygon.size()-1]
draw_line(origin, point_array[label], grid_color, 1, true)
if point_array[label].x != origin.x:
draw_string(font, point_array[label] - (Vector2(font.get_string_size(x_labels[label]).x+10,(5 if point_array[label].y <= origin.y else -10)) if point_array[label].x <= origin.x else - Vector2(10,(-5 if point_array[label].y <= origin.y else 10))), x_labels[label], font_color)
else:
draw_string(font, point_array[label] - (Vector2(font.get_string_size(x_labels[label]).x/2, 10) if point_array[label].y < origin.x else - Vector2(font.get_string_size(x_labels[label]).x/2, 5)), x_labels[label], font_color)
func create_legend():
pass
# legend.clear()
# for function in functions:
# var function_legend = FunctionLegend.instance()
# var f_name : String = x_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 count_functions():
if x_labels.size():
functions = x_labels.size()

View File

@ -1,80 +0,0 @@
[gd_scene load_steps=3 format=2]
[ext_resource path="res://addons/easy_charts/control_charts/RadarChart/radar_chart.gd" type="Script" id=1]
[ext_resource path="res://addons/easy_charts/utilities/containers/data_tooltip/data_tooltip.tscn" type="PackedScene" id=2]
[node name="RadarChart" type="Control"]
anchor_right = 1.0
anchor_bottom = 1.0
rect_min_size = Vector2( 70, 50 )
mouse_filter = 2
script = ExtResource( 1 )
__meta__ = {
"_edit_use_anchors_": false,
"_editor_description_": "[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."
}
Chart_Properties/are_values_columns = false
Chart_Properties/labels_index = 0
Chart_Properties/function_names_index = 0
Chart_Properties/use_height_as_radius = false
Chart_Properties/radius = 150.0
Chart_Display/full_scale = 1.0
Chart_Style/points_shape = [ 0 ]
Chart_Style/function_colors = [ Color( 0.117647, 0.117647, 0.117647, 1 ) ]
Chart_Style/outline_color = Color( 0.117647, 0.117647, 0.117647, 1 )
Chart_Style/grid_color = Color( 0.117647, 0.117647, 0.117647, 1 )
Chart_Style/font = null
Chart_Style/bold_font = null
Chart_Style/font_color = Color( 0.117647, 0.117647, 0.117647, 1 )
Chart_Style/template = 0
Chart_Modifiers/rotation = 0.0
[node name="Background" type="ColorRect" parent="."]
visible = false
show_behind_parent = true
anchor_right = 1.0
anchor_bottom = 1.0
color = Color( 0.882353, 0.882353, 0.882353, 1 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Points" type="Control" parent="."]
margin_right = 40.0
margin_bottom = 40.0
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Legend" type="HBoxContainer" parent="."]
visible = false
margin_right = 1024.0
margin_bottom = 64.0
alignment = 1
__meta__ = {
"_edit_use_anchors_": false
}
[node name="ChartName" type="Label" parent="."]
anchor_right = 1.0
anchor_bottom = 0.053
margin_bottom = 0.199999
align = 1
valign = 1
__meta__ = {
"_edit_use_anchors_": true
}
[node name="CanvasLayer" type="CanvasLayer" parent="."]
[node name="DataTooltip" parent="CanvasLayer" instance=ExtResource( 2 )]
margin_left = -15.644
margin_top = -15.8509
margin_right = -15.78
margin_bottom = -15.0509

View File

@ -1,28 +1,44 @@
tool
extends ScatterChartBase
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
# Called when the node enters the scene tree for the first time.
func _ready():
pass
func plot(x: Array, y: Array) -> void:
self.x = x
self.y = y
func _get_property_list():
property_list[0].name = "ScatterChart"
return property_list
_clear()
_pre_process()
update()
func _draw_point(point: Point, function_index: int) -> void:
var point_container: PointContainer = point_container_scene.instance()
$"%Points".add_child(point_container)
point_container.set_point(
point,
drawing_options.colors.functions[function_index],
drawing_options.shapes[function_index]
)
func _draw():
clean_points()
draw_grid()
draw_chart_outlines()
draw_points()
draw_treshold()
func _draw_points() -> void:
var validation: int = _validate_sampled_axis(x_sampled, y_sampled)
if not validation == OK:
printerr("Cannot plot points for invalid dataset! Error: %s" % validation)
return
if y_sampled.values[0] is Array:
for yxi in y_sampled.values.size():
for i in y_sampled.values[yxi].size():
var real_point_val: Pair = Pair.new(x[i], y[yxi][i])
var sampled_point_pos: Vector2 = Vector2(x_sampled.values[i], y_sampled.values[yxi][i])
var point: Point = Point.new(sampled_point_pos, real_point_val)
_draw_point(point, yxi)
else:
for i in y_sampled.values.size():
var real_point_val: Pair = Pair.new(x[i], y[i])
var sampled_point_pos: Vector2 = Vector2(x_sampled.values[i], y_sampled.values[i])
var point: Point = Point.new(sampled_point_pos, real_point_val)
_draw_point(point, i)

View File

@ -1,65 +1,13 @@
[gd_scene load_steps=3 format=2]
[gd_scene load_steps=2 format=2]
[ext_resource path="res://addons/easy_charts/control_charts/ScatterChart/scatter_chart.gd" type="Script" id=1]
[ext_resource path="res://addons/easy_charts/utilities/containers/data_tooltip/data_tooltip.tscn" type="PackedScene" id=2]
[node name="ScatterChart" type="Control"]
anchor_right = 1.0
anchor_bottom = 1.0
rect_min_size = Vector2( 70, 50 )
mouse_filter = 2
script = ExtResource( 1 )
__meta__ = {
"_edit_use_anchors_": false,
"_editor_description_": "[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."
}
[node name="Background" type="ColorRect" parent="."]
visible = false
show_behind_parent = true
anchor_right = 1.0
anchor_bottom = 1.0
color = Color( 0.882353, 0.882353, 0.882353, 1 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Points" type="Control" parent="."]
margin_right = 40.0
margin_bottom = 40.0
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Legend" type="HBoxContainer" parent="."]
visible = false
margin_right = 1024.0
margin_bottom = 64.0
alignment = 1
__meta__ = {
"_edit_use_anchors_": false
}
[node name="ChartName" type="Label" parent="."]
unique_name_in_owner = true
anchor_right = 1.0
margin_bottom = 14.0
align = 1
valign = 1
__meta__ = {
"_edit_use_anchors_": false
}
[node name="CanvasLayer" type="CanvasLayer" parent="."]
[node name="DataTooltip" parent="CanvasLayer" instance=ExtResource( 2 )]
margin_left = 264.788
margin_top = 446.78
margin_right = 264.65
margin_bottom = 447.58
anchor_bottom = 1.0

View File

@ -0,0 +1,228 @@
extends Control
class_name Chart
var x: Array
var y: Array
var x_sampled: SampledAxis
var y_sampled: SampledAxis
#
var x_scale: float = 5.0
var y_scale: float = 5.0
###### STYLE
var drawing_options: DrawingOptions = DrawingOptions.new()
#### INTERNAL
# The bounding_box of the chart
var node_box: Rect2
var bounding_box: Rect2
# The Reference Rectangle to plot samples
# It is the @bounding_box Rectangle inverted on the Y axis
var ref_x: Pair
var ref_y: Pair
var ref_rect: Rect2
var _padding_offset: Vector2 = Vector2(70.0, 70.0)
var _internal_offset: Vector2 = Vector2(15.0, 15.0)
var point_container_scene: PackedScene = preload("res://addons/easy_charts/utilities/containers/point_container/point_container.tscn")
###########
func plot(x: Array, y: Array) -> void:
pass
func _sample_values(values: Array, ref_values: Pair) -> SampledAxis:
if values.empty():
printerr("Trying to plot an empty dataset!")
return SampledAxis.new()
if values[0] is Array:
if values.size() > 1:
for dim in values:
if values[0].size() != dim.size():
printerr("Cannot plot a dataset with dimensions of different size!")
return SampledAxis.new()
var temp: Array = values.duplicate(true)
var _min: float
var _max: float
var rels: Array = []
if temp[0] is Array:
var min_ts: Array
var max_ts: Array
for dim in temp:
min_ts.append(dim.min())
max_ts.append(dim.max())
_min = min_ts.min()
_max = max_ts.max()
for t_dim in temp:
var rels_t: Array = []
for val in t_dim:
rels_t.append(range_lerp(val, _min, _max, ref_values.left, ref_values.right))
rels.append(rels_t)
else:
_min = temp.min()
_max = temp.max()
for val in temp:
rels.append(range_lerp(val, _min, _max, ref_values.left, ref_values.right))
return SampledAxis.new(rels, Pair.new(_min, _max))
func _pre_process() -> void:
var t_gr: Rect2 = get_global_rect()
# node box
node_box = Rect2(Vector2.ZERO, t_gr.size - t_gr.position)
# bounding_box
bounding_box = Rect2(
Vector2.ZERO + _padding_offset,
t_gr.size - t_gr.position - (_padding_offset*2)
)
# reference rectangle
ref_x = Pair.new(bounding_box.position.x + _internal_offset.x, bounding_box.position.x + bounding_box.size.x - _internal_offset.y)
ref_y = Pair.new(bounding_box.size.y + bounding_box.position.y - _internal_offset.x, bounding_box.position.y + _internal_offset.y)
ref_rect = Rect2(
Vector2(ref_x.left, ref_y.left),
Vector2(ref_x.right, ref_y.right)
)
# samples
x_sampled = _sample_values(x, ref_x)
y_sampled = _sample_values(y, ref_y)
func _draw_borders() -> void:
draw_rect(node_box, Color.red, false, 1, true)
func _draw_bounding_box() -> void:
draw_rect(bounding_box, drawing_options.colors.bounding_box, false, 1, true)
func _draw_points() -> void:
pass
func _draw_background() -> void:
draw_rect(node_box, Color.white, true, 1.0, true)
func _draw_grid() -> void:
var validation: int = _validate_sampled_axis(x_sampled, y_sampled)
if not validation == OK:
printerr("Cannot draw grid for invalid dataset! Error: %s" % validation)
return
# draw vertical lines
var v_lines: float = (x_sampled.min_max.right - x_sampled.min_max.left) / x_scale
for _x in x_scale+1:
var x_val: float = _x * v_lines + x_sampled.min_max.left
var p1: Vector2 = Vector2(
range_lerp(x_val, x_sampled.min_max.left, x_sampled.min_max.right, ref_x.left, ref_x.right),
bounding_box.position.y
)
var p2: Vector2 = Vector2(
range_lerp(x_val, x_sampled.min_max.left, x_sampled.min_max.right, ref_x.left, ref_x.right),
bounding_box.size.y + bounding_box.position.y
)
# Draw V Grid Lines
if drawing_options.grid:
draw_line(p1, p2, drawing_options.colors.grid, 1, true)
# Draw V Ticks
if drawing_options.ticks:
p1.y = p2.y
p2.y += 8.0
draw_line(p1, p2, drawing_options.colors.bounding_box, 1, true)
# Draw V labels
if drawing_options.labels:
draw_string(
drawing_options.font,
p2 + Vector2(-drawing_options.font.get_string_size(str(x_val)).x * 0.5, 15.0),
str(x_val),
drawing_options.colors.bounding_box
)
# draw horizontal lines
var h_lines: float = (y_sampled.min_max.right - y_sampled.min_max.left) / y_scale
for _y in y_scale+1:
var y_val: float = _y * h_lines + y_sampled.min_max.left
var p1: Vector2 = Vector2(
bounding_box.position.x,
range_lerp(y_val, y_sampled.min_max.left, y_sampled.min_max.right, ref_y.left, ref_y.right)
)
var p2: Vector2 = Vector2(
bounding_box.size.x + bounding_box.position.x,
range_lerp(y_val, y_sampled.min_max.left, y_sampled.min_max.right, ref_y.left, ref_y.right)
)
# Draw H Grid Lines
if drawing_options.grid:
draw_line(p1, p2, drawing_options.colors.grid, 1, true)
# Draw H Ticks
if drawing_options.ticks:
p2.x = p1.x - 8.0
draw_line(p1, p2, drawing_options.colors.bounding_box, 1, true)
# Draw H labels
if drawing_options.labels:
draw_string(
drawing_options.font,
p2 - Vector2(drawing_options.font.get_string_size(str(y_val)).x + 5.0, -drawing_options.font.get_string_size(str(y_val)).y * 0.35),
str(y_val),
drawing_options.colors.bounding_box
)
func _clear_points() -> void:
for point in $Points.get_children():
point.queue_free()
func _clear() -> void:
_clear_points()
func _draw():
if drawing_options.background:
_draw_background()
if drawing_options.borders:
_draw_borders()
if drawing_options.grid or drawing_options.ticks or drawing_options.labels:
_draw_grid()
if drawing_options.bounding_box:
_draw_bounding_box()
if drawing_options.points:
_draw_points()
# if drawing_options.labels:
# _draw_labels()
func _validate_sampled_axis(x_data: SampledAxis, y_data: SampledAxis) -> int:
var error: int = 0 # OK
if x_data.values.empty() or y_data.values.empty():
# Either there are no X or Y
error = 1
if y_data.values[0] is Array:
for dim in y_data.values:
if dim.size() != x_data.values.size():
error = 3 # one of Y dim has not X length
break
else:
if y_data.values.size() != x_data.values.size():
# X and Y samples don't have same length
error = 2
return error

View File

@ -1,10 +0,0 @@
Year;Column 1;Column 2;Column 3;Column 4
2009;36200;27200;26200;17200
2010;36600;27800;26600;17800
2011;37500;28500;27500;18500
2012;38700;29400;28700;19400
2013;39600;30200;29600;10200
2014;40500;30900;20500;10900
2015;41200;31500;21200;11500
2016;41803;31931;21803;11931
2017;42600;32600;22600;12600

View File

@ -1,6 +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

View File

@ -1,8 +0,0 @@
Country,Votes
United States,46
Great Britain,27
China,26
Russia,19
Germany,17
Italy,34
Sweden,83

View File

@ -1,4 +0,0 @@
Eating;Drinking;Running;Sleeping;Coding
30;50;40;100;95
90;34;100;50;23

View File

@ -1,101 +0,0 @@
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

View File

@ -3,5 +3,5 @@
name="EasyCharts"
description=""
author="Nicolò \"fenix\" Santilio"
version="0.5.6"
version="2.0.0"
script="plugin.gd"

View File

@ -3,11 +3,7 @@ extends EditorPlugin
func _enter_tree():
add_autoload_singleton("ECUtilities","res://addons/easy_charts/utilities/scripts/ec_utilities.gd")
add_custom_type("Chart","Control", load("res://addons/easy_charts/utilities/classes/base/chart.gd"), preload("utilities/icons/linechart.svg"))
add_custom_type("Chart2D","Node2D", load("res://addons/easy_charts/utilities/classes/base/chart2d.gd"), preload("utilities/icons/linechart2d.svg"))
func _exit_tree():
remove_custom_type("Chart")
remove_custom_type("Chart2D")
remove_autoload_singleton("ECUtilities")

View File

@ -1,723 +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 data_tooltip = $CanvasLayer/DataTooltip
onready var Points = $Points
onready var Legend = $Legend
onready var ChartName : Label = $ChartName
# Scenes and Reosurces ......................
var point_node : PackedScene = preload("../../components/point/point.tscn")
var legend_element : PackedScene = preload("../../containers/legend/function_legend.tscn")
# 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
# If using a Dataframe
var dataframe: DataFrame
# 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 : float = 0
var y_margin_min : float = 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, *.res") var source : String = "" setget set_source
export (String) var delimiter : String = ";" setget set_delimiter
var origin_at_zero : bool = false setget set_origin_at_zero#, get_origin_at_zero
var are_values_columns : bool = true setget set_are_values_columns#, get_are_values_columns
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 function_line_width : int = 2
var column_width : float = 10 setget set_column_width
var column_gap : float = 2 setget set_column_gap
# Calculations of decim and its relation with number of tics: https://www.desmos.com/calculator/jeiceaswiy
var full_scale : float = 1.0 setget set_full_scale
var x_decim : float = 1.0 setget set_x_decim
var y_decim : float = 1.0 setget set_y_decim
var points_shape : Array = [Point.SHAPES.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 grid_lines_width : int = 1 setget set_grid_lines_width
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 show_points := true
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
# Custom parameters for plot display
var tic_length : int = 5 setget set_tic_length # Length of the bar indicating a tic
var label_displacement : int = 4 setget set_label_displacement # Separation between the label and both the axis and the edge border
var property_list: Array = []
# ..........................................
# !! API v2
static func instance(chart_type : int):
var chart_t : Array = ECUtilities.get_chart_type(chart_type)
var chart : String = "res://addons/easy_charts/control_charts/%s/%s.tscn" % [chart_t[0], chart_t[1]]
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/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_Style/function_line_width":
return function_line_width
"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/grid_lines_width":
return grid_lines_width
"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/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_Style/function_line_width":
function_line_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/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/grid_lines_width":
grid_lines_width = 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
func _init():
build_property_list()
func _ready():
load_font()
# .......................... Shared Functions and virtuals ........................
# Structure and Display a new plot if a dataset source is given
# both through APIs or from Inspector
func plot(_dataset: Array = read_data(source, delimiter)) -> void:
clean_variables()
clean_points()
data_tooltip.hide()
if _dataset.empty():
ECUtilities._print_message("Can't plot a chart with an empty Array.",1)
return
are_values_columns = invert_chart != are_values_columns
# Read the dataset in the proper way
var database : Array = _dataset \
if not are_values_columns \
else MatrixGenerator.transpose(Matrix.new(_dataset)).to_array()
data = slice_data(database)
structure_data(data)
compute_display()
display_plot()
emit_signal("chart_plotted",self)
if not is_connected("item_rect_changed", self, "redraw_plot"): connect("item_rect_changed", self, "redraw_plot")
func plot_from_source(file : String, _delimiter : String = delimiter) -> void:
if source == "" or source == null:
ECUtilities._print_message("Can't plot a chart without a Source file. Please, choose it in editor, or use the custom function _plot().",1)
return
plot(read_data(file, _delimiter))
func plot_from_dataframe(dataframe : DataFrame) -> void:
assert(dataframe.headers.size() > 1 or dataframe.labels.size() > 1,
"Cannot plot a dataframe of size %sx%s"%[dataframe.headers.size(), dataframe.labels.size()])
self.dataframe = dataframe
plot(dataframe.get_dataset())
func plot_placeholder() -> void:
pass
# Append new data (in array format) to the already plotted data.
# The new data will be appended as a new row of the dataset.
# All data are stored.
func update_plot(new_data : Array = []) -> void:
if not new_data.empty(): data.append(new_data)
plot(data if dataframe == null else dataframe.get_dataset().duplicate(true))
update()
# Append a new column to data
func append_new_column(dataset : Array, column : Array):
if column.empty():
ECUtilities._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])
# ...................... Dataset Manipulation Functions .........................
func read_data(source : String, _delimiter : String = delimiter):
assert(source != "" and source != null, "A source file must be specified")
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)
if line.empty() or line.size() < 2:
continue
content.append(line)
file.close()
return content.duplicate(true)
func slice_x(x_data : Array) -> Array:
return [x_data[0]] + Array(x_data).slice(x_data.size() - only_disp_values.x, x_data.size() -1 )
func slice_y(y_data : Array) -> Array:
return [y_data[0]] + y_data.slice(y_data.size()-only_disp_values.y, y_data.size()-1)
## TODO: Data should not be sliced!!
## Instead, only_disp_values should affect all the `range()` to plot points
func slice_data(database : Array) -> Array:
var data_to_display : Array = database
if only_disp_values.y < database.size() and only_disp_values.y !=0:
data_to_display = slice_y(database)
if only_disp_values.x < database[0].size() and only_disp_values.x !=0:
for row_idx in database.size():
data_to_display[row_idx] = slice_x(database[row_idx])
return data_to_display.duplicate(true)
# ................................. Display and Draw functions .......................
func compute_display():
count_functions()
calculate_colors()
set_shapes()
create_legend()
func display_plot():
build_chart()
calculate_pass()
calculate_coordinates()
func redraw_plot():
data_tooltip.hide()
clean_points()
display_plot()
update()
# ................................. Helper Functions .................................
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:
data_tooltip.Data.set("custom_fonts/font", bold_font)
else:
bold_font = font
func count_functions():
if are_values_columns: functions = x_labels.size()
else: functions = y_labels.size()
func calculate_colors():
if function_colors.size() < functions:
for function in range(functions - function_colors.size()): 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(Point.SHAPES.Dot)
# Create the legend of the current plot
func create_legend():
for function in functions:
var function_legend : LegendElement
if legend.size() > function:
function_legend = legend[function]
else:
function_legend = legend_element.instance()
legend.append(function_legend)
var f_name : String = y_labels[function] if are_values_columns else str(x_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)
func clean_points():
for function in Points.get_children():
function.free()
for legend in Legend.get_children():
legend.free()
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 build_property_list():
pass
func calculate_tics():
pass
# Structure the dataset in order to be plotted
func structure_data(database : Array):
pass
# Calculate borders, size and origin in order to display the plot
func build_chart():
pass
# Calculate the pass, necessary to correctly draw the points
func calculate_pass():
pass
# Calculate Points' coordinates in order to display them
func calculate_coordinates():
pass
# Calculate or assign to each function a color
func function_colors():
pass
# ........................... Shared Setters & Getters ..............................
func apply_template(template_name : int):
if Engine.editor_hint:
set_template(template_name)
property_list_changed_notify()
func set_data(data : Array) -> void:
self.data = data
func set_dataframe(dataframe : DataFrame) -> void:
self.dataframe = dataframe
# !!! 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
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_tic_length(i: int):
tic_length = i
# ! API
func set_label_displacement(i:int):
label_displacement = i
# ! API
func set_grid_color(c : Color):
grid_color = c
# ! API
func set_grid_lines_width(i : int):
grid_lines_width = i
# ! 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 = ECUtilities.templates.get(ECUtilities.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):
data_tooltip.update_datas(point)
data_tooltip.show()
func hide_data():
data_tooltip.hide()
func show_slice_data(slice : Slice):
data_tooltip.update_slice_datas(slice)
data_tooltip.show()

View File

@ -1,209 +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 DataTooltip: DataTooltip
var Outlines: Line2D
var Grid: Node2D
var point_node: PackedScene = preload("res://addons/easy_charts/utilities/components/point/point.tscn")
var FunctionLegend: PackedScene = preload("res://addons/easy_charts/utilities/containers/legend/function_legend.tscn")
var font_size: float = 16
var const_height: float = font_size / 2 * font_size / 20
var const_width: float = font_size / 2
var 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
DataTooltip = $DataTooltip/DataTooltip
Outlines = $Outlines
Grid = $Grid
func apply_template(template_name: int):
template = template_name
templates = ECUtilities._load_templates()
if template_name != null:
var custom_template = templates.get(templates.keys()[template_name])
function_colors = custom_template.function_colors as PoolColorArray
v_lines_color = Color(custom_template.v_lines_color)
h_lines_color = Color(custom_template.h_lines_color)
box_color = Color(custom_template.outline_color)
font_color = Color(custom_template.font_color)
property_list_changed_notify()
if Engine.editor_hint:
_get_children()
Outlines.set_default_color(box_color)
Grid.get_node("VLine").set_default_color(v_lines_color)
Grid.get_node("HLine").set_default_color(h_lines_color)
func redraw():
build_chart()
calculate_pass()
calculate_coordinates()
update()
func show_data(point):
DataTooltip.update_datas(point)
DataTooltip.show()
func hide_data():
DataTooltip.hide()
func clean_points():
function_colors.clear()
if Functions.get_children():
for function in Functions.get_children():
function.queue_free()
func 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)

View File

@ -1,596 +0,0 @@
extends Chart
class_name ScatterChartBase
# Base Class to be inherited by any chart that wants to plot points or series
# of points in a two-variable space. It handles basic data structure and grid
# layout and leaves to child classes the more specific behaviour.
var x_values := []
var y_values := []
#Stored in the form of [[min_func1, min_func2, min_func3, ...], [max_func1, max_func2, ...]]
var x_domain := [[], []]
var y_domain := [[], []]
var x_range : PoolRealArray = [0, 0]
var y_range : PoolRealArray = [0, 0]
var autoscale_x = true
var autoscale_y = true
func build_property_list():
property_list.clear()
# Chart Properties
property_list.append(
{
"hint": PROPERTY_HINT_NONE,
"usage": PROPERTY_USAGE_CATEGORY,
"name": get_name(),
"type": TYPE_STRING
})
property_list.append(
{
"hint": PROPERTY_HINT_NONE,
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
"name": "Chart_Properties/are_values_columns",
"type": TYPE_BOOL
})
property_list.append(
{
"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
})
# Chart Display
property_list.append(
{
"hint": PROPERTY_HINT_NONE,
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
"name": "Chart_Display/autoscale_x",
"type": TYPE_BOOL
})
if not autoscale_x:
property_list.append(
{
"hint": PROPERTY_HINT_NONE,
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
"name": "Chart_Display/min_x_range",
"type": TYPE_REAL
})
property_list.append(
{
"hint": PROPERTY_HINT_NONE,
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
"name": "Chart_Display/max_x_range",
"type": TYPE_REAL
})
property_list.append(
{
"hint": PROPERTY_HINT_RANGE,
"hint_string": "0.001, 10",
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
"name": "Chart_Display/x_decim",
"type": TYPE_REAL
})
property_list.append(
{
"hint": PROPERTY_HINT_NONE,
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
"name": "Chart_Display/autoscale_y",
"type": TYPE_BOOL
})
if not autoscale_y:
property_list.append(
{
"hint": PROPERTY_HINT_NONE,
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
"name": "Chart_Display/min_y_range",
"type": TYPE_REAL
})
property_list.append(
{
"hint": PROPERTY_HINT_NONE,
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
"name": "Chart_Display/max_y_range",
"type": TYPE_REAL
})
property_list.append(
{
"hint": PROPERTY_HINT_RANGE,
"hint_string": "0.001, 10",
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
"name": "Chart_Display/y_decim",
"type": TYPE_REAL
})
property_list.append(
{
"hint": PROPERTY_HINT_NONE,
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
"name": "Chart_Display/show_points",
"type": TYPE_BOOL
}
)
# Chart Style
property_list.append(
{
"hint": 24,
"hint_string": ("%d/%d:%s"
%[TYPE_INT, PROPERTY_HINT_ENUM,
PoolStringArray(Point.SHAPES.keys()).join(",")]),
"name": "Chart_Style/points_shape",
"type": TYPE_ARRAY,
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE
})
property_list.append(
{
"hint": PROPERTY_HINT_NONE,
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
"name": "Chart_Style/function_colors",
"type": TYPE_COLOR_ARRAY
})
property_list.append(
{
"hint": PROPERTY_HINT_NONE,
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
"name": "Chart_Style/box_color",
"type": TYPE_COLOR
})
property_list.append(
{
"hint": PROPERTY_HINT_RANGE,
"hint_string": "1, 100, 1",
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
"name": "Chart_Style/grid_lines_width",
"type": TYPE_INT
})
property_list.append(
{
"hint": PROPERTY_HINT_NONE,
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
"name": "Chart_Style/v_lines_color",
"type": TYPE_COLOR
})
property_list.append(
{
"hint": PROPERTY_HINT_NONE,
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
"name": "Chart_Style/h_lines_color",
"type": TYPE_COLOR
})
property_list.append(
{
"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
})
property_list.append(
{
"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
})
property_list.append(
{
"hint": PROPERTY_HINT_NONE,
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
"name": "Chart_Style/font_color",
"type": TYPE_COLOR
})
property_list.append(
{
"hint": PROPERTY_HINT_NONE,
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
"name": "Chart_Style/use_template",
"type": TYPE_BOOL
})
property_list.append(
{
"hint": PROPERTY_HINT_ENUM,
"hint_string": PoolStringArray(ECUtilities.templates.keys()).join(","),
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
"name": "Chart_Style/template",
"type": TYPE_INT
})
# Chart Modifiers
property_list.append(
{
"hint": PROPERTY_HINT_NONE,
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
"name": "Chart_Modifiers/treshold",
"type": TYPE_VECTOR2
})
property_list.append(
{
"hint": PROPERTY_HINT_NONE,
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
"name": "Chart_Modifiers/only_disp_values",
"type": TYPE_VECTOR2
})
property_list.append(
{
"hint": PROPERTY_HINT_NONE,
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
"name": "Chart_Modifiers/invert_chart",
"type": TYPE_BOOL
})
func _set(property, value):
match property:
"Chart_Display/show_points":
show_points = value
return true
"Chart_Display/autoscale_x":
autoscale_x = value
build_property_list()
property_list_changed_notify()
return true
"Chart_Display/autoscale_y":
autoscale_y = value
build_property_list()
property_list_changed_notify()
return true
"Chart_Display/min_x_range":
x_range[0] = value
return true
"Chart_Display/max_x_range":
x_range[1] = value
return true
"Chart_Display/min_y_range":
y_range[0] = value
return true
"Chart_Display/max_y_range":
y_range[1] = value
return true
func _get(property):
match property:
"Chart_Display/show_points":
return show_points
"Chart_Display/autoscale_x":
return autoscale_x
"Chart_Display/autoscale_y":
return autoscale_y
"Chart_Display/min_x_range":
return x_range[0]
"Chart_Display/max_x_range":
return x_range[1]
"Chart_Display/min_y_range":
return y_range[0]
"Chart_Display/max_y_range":
return y_range[1]
func plot_function(id: String, x:Array, y:Array, param_dic := {}):
# Add a function to the chart. If no identifier (label) is given a generic one
# is generated.
# param_dic is a dictionary with specific parameters to this curve
if x.empty() or y.empty():
ECUtilities._print_message("Can't plot a chart with an empty Array.",1)
return
elif x.size() != y.size():
ECUtilities._print_message("Can't plot a chart with x and y having different number of elements.",1)
return
id = generate_identifier() if id.empty() else id
if y_labels.has(id):
ECUtilities._print_message("The identifier %s is already used. Please use a different one." % id,1)
return
y_domain[0].append(null)
y_domain[1].append(null)
x_domain[0].append(null)
x_domain[1].append(null)
x_values.append_array(x)
y_values = y
populate_x_datas()
populate_y_datas()
calculate_range(id)
calculate_tics()
redraw_plot()
update()
func _slice():
if only_disp_values.x < x_values.size() and only_disp_values.x != 0:
x_values.pop_front()
for y_data in y_datas:
y_data.pop_front()
func update_functions(new_x, new_y : Array, param_dic : = {}) :
assert(new_y.size() == y_labels.size(),
"new values array size (%s) must match Labels size (%s)"%[new_y.size(), y_labels.size()])
_slice()
x_values.append(new_x)
for function in y_datas.size():
y_datas[function].append(new_y[function])
calculate_range(y_labels[function])
calculate_tics()
redraw_plot()
update()
func update_function(id: String, new_x, new_y, param_dic := {}) -> void:
var function = y_labels.find(id)
if function == -1: #Not found
ECUtilities._print_message("The identifier %s does not exist." % id,1)
return
_slice()
for y_data_i in range(0, y_datas.size()):
if y_data_i == function: y_datas[y_data_i].append(new_y)
else: y_datas[y_data_i].append(y_datas[y_data_i][y_datas[y_data_i].size()-1])
calculate_range(id)
calculate_tics()
redraw_plot()
update()
func delete_function(id: String):
var function = y_labels.find(id)
if function == -1: #Not found
ECUtilities._print_message("The identifier %s does not exist." % id,1)
return
# y_labels.remove(function)
x_datas.remove(function)
y_datas.remove(function)
y_domain[0].remove(function)
y_domain[1].remove(function)
x_domain[0].remove(function)
x_domain[1].remove(function)
function_colors.remove(function)
calculate_tics()
redraw_plot()
update()
func generate_identifier():
#TODO: Check if the identifier generated already exist (given by the user)
return "f%d" % (y_labels.size() + 1)
func populate_x_datas():
x_labels = x_values
x_datas.append(x_values)
func populate_y_datas():
y_labels.append(y_values.pop_front() as String)
for val in y_values.size():
y_values[val] = y_values[val] as float
y_datas.append(y_values)
func structure_data(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
#This is done to make sure this arrays are empty on subsecuent calls of this function.
#This function is called from the "old" methods such as plot_from_array and
#for the moment it doesn't clean this variables on clean_variable.
x_domain = [[], []]
y_domain = [[], []]
var database_size = range(database.size())
if database_size.has(labels_index):
x_values = database[labels_index]
x_label = x_values.pop_front() as String
database_size.erase(labels_index) #Remove x row from the iterator
for size in database_size:
populate_x_datas()
y_values = database[size]
populate_y_datas()
for function in y_labels:
y_domain[0].append(null)
y_domain[1].append(null)
x_domain[0].append(null)
x_domain[1].append(null)
calculate_range(function)
calculate_tics()
func calculate_range(id):
# Calculate the domain of the given function in the x and y axis
# and updates the range value
var function = y_labels.find(id)
y_domain[0][function] = y_datas[function].min()
y_domain[1][function] = y_datas[function].max()
x_domain[0][function] = x_datas[function].min()
x_domain[1][function] = x_datas[function].max()
func calculate_tics():
y_chors.clear()
x_chors.clear()
# Chose the min/max from all functions
if autoscale_x:
x_range = [x_domain[0].min() if not origin_at_zero else 0, x_domain[1].max()]
if autoscale_y:
y_range = [y_domain[0].min() if not origin_at_zero else 0, y_domain[1].max()]
y_margin_min = y_range[0]
var y_margin_max = y_range[1]
v_dist = y_decim * pow(10.0, calculate_position_significant_figure(y_margin_max - y_margin_min) - 1)
# There are three cases of min/max:
# For +/+ and -/- we just do the usual and draw tics from min to max
# But for the -/+ we do in two times to force the 0 to appear so it is
# easier to read. Then we draw the negative from 0 to min and the positives
# from 0 to max without drawing the 0 again
if y_margin_min < 0 and y_margin_max >= 0:
calculate_interval_tics(0, y_margin_min, -v_dist, y_chors) #Negative tics
calculate_interval_tics(0, y_margin_max, v_dist, y_chors, false) #Positive tics
y_chors.sort()
y_margin_min = min(y_margin_min, y_chors[0])
else:
calculate_interval_tics(y_margin_min, y_margin_max, v_dist, y_chors)
for i in y_chors.size():
y_chors[i] = String(y_chors[i]) #Can't cast directly on calculate_interval_tics because it mess up with the sorting
x_chors = x_labels.duplicate(true)
func build_chart():
var longest_y_tic = 0
for y_tic in y_chors:
var length = font.get_string_size(str(y_tic)).x
if length > longest_y_tic:
longest_y_tic = length
OFFSET.x = longest_y_tic + tic_length + 2 * label_displacement
OFFSET.y = font.get_height() + tic_length + label_displacement
SIZE = get_size() - Vector2(OFFSET.x, 0)
origin = Vector2(OFFSET.x, SIZE.y - OFFSET.y)
func count_functions():
functions = y_labels.size()
# Calculate distance in pixel between 2 consecutive values/datas
func calculate_pass():
if x_chors.size() > 0:
x_pass = (SIZE.x - OFFSET.x) / (x_chors.size() - 1 if x_chors.size() > 1 else x_chors.size())
if y_chors.size() > 0:
y_pass = (origin.y) / (y_chors.size() - 1 if y_chors.size() > 1 else y_chors.size())
# Calculate all Points' coordinates in the dataset
# and display them inside the chart
func calculate_coordinates():
point_values.clear()
point_positions.clear()
for _i in y_labels.size():
point_values.append([])
point_positions.append([])
for function in y_labels.size():
for val in x_datas[function].size():
var value_x = (int(x_datas[function][val]) - x_margin_min) * x_pass / h_dist if h_dist else \
val * x_pass
var value_y = (y_datas[function][val] - y_margin_min) * y_pass / v_dist if v_dist else 0
point_values[function].append([x_datas[function][val], y_datas[function][val]])
point_positions[function].append(Vector2(value_x + origin.x, origin.y - value_y))
# Draw the grid lines for the chart
func draw_grid():
# ascisse
for p in x_chors.size():
var point : Vector2 = origin + Vector2(p * x_pass, 0)
var size_text : Vector2 = font.get_string_size(str(x_chors[p]))
# v grid
draw_line(point, point - Vector2(0, SIZE.y - OFFSET.y), v_lines_color, grid_lines_width, true)
# ascisse
draw_line(point + Vector2(0, tic_length), point, v_lines_color, grid_lines_width, true)
draw_string(font, point + Vector2(-size_text.x / 2, size_text.y + tic_length),
str(x_chors[p]), font_color)
# ordinate
for p in y_chors.size():
var point : Vector2 = origin - Vector2(0, p * y_pass)
var size_text := Vector2(font.get_string_size(y_chors[p]).x, font.get_ascent()) #The y should be ascent instead full height to get correctly centered
# h grid
draw_line(point, point + Vector2(SIZE.x - OFFSET.x, 0), h_lines_color, grid_lines_width, true)
# ordinate
draw_line(point, point + Vector2(-tic_length, 0), h_lines_color, grid_lines_width, true)
draw_string(font, point + Vector2(-size_text.x - tic_length - label_displacement,
size_text.y / 2), y_chors[p], font_color)
# Draw chart outlines containing the current plot
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)
# Draw the points using their coordinates and information,
# inside a PointContainer
func draw_points():
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])
PointContainer.add_child(point)
# Draw the tresholds (if set)
func draw_treshold():
if v_dist != 0:
treshold_draw = Vector2((treshold.x * x_pass) + origin.x ,origin.y - ((treshold.y * y_pass)/v_dist))
if treshold.y != 0:
draw_line(Vector2(origin.x, treshold_draw.y), Vector2(SIZE.x, treshold_draw.y), Color.red, 0.4, true)
if treshold.x != 0:
draw_line(Vector2(treshold_draw.x, 0), Vector2(treshold_draw.x, SIZE.y - OFFSET.y), Color.red, 0.4, true)
func calculate_position_significant_figure(number):
return floor(log(abs(number))/log(10) + 1) #If number = 0 Godot returns -#INF and it behaves correctly on the pow call on calculate_tics
func calculate_interval_tics(v_from:float, v_to:float, dist:float, chords:Array, include_first := true):
# Appends to array chords the tics calculated between v_from and v_to with
# a given distance between tics.
#include_first is used to tell if v_from should be appended or ignored
var multi = 0
var p = (dist * multi) + v_from
var missing_tics = p < v_to if dist > 0 else p > v_to
if include_first:
chords.append(p)
while missing_tics:
multi += 1
p = (dist * multi) + v_from
missing_tics = p < v_to if dist > 0 else p > v_to
chords.append(p)
func _to_string() -> String:
return \
"X DATA: %s\n" % str(x_datas) + \
"Y DATA: %s\n" % str(y_datas) + \
"X LABELS: %s\n" % str(x_labels) + \
"Y LABELS: %s\n" % str(y_labels)

View File

@ -0,0 +1,20 @@
extends Reference
class_name DrawingOptions
var points: bool = true
var grid: bool = false
var bounding_box: bool = true
var background: bool = true
var borders: bool = false
var ticks: bool = true
var labels: bool = true
var colors: Dictionary = {
bounding_box = Color.black,
grid = Color.gray,
functions = [Color.red, Color.green, Color.blue]
}
var shapes: Array = [PointContainer.PointShape.CIRCLE, PointContainer.PointShape.SQUARE, PointContainer.PointShape.SQUARE]
var point_radius: float = 3.0
var font: Font = Label.new().get_font("")

View File

@ -0,0 +1,13 @@
tool
extends Reference
class_name Point
var position: Vector2
var value: Pair
func _init(position: Vector2, value: Pair) -> void:
self.value = value
self.position = position
func _to_string() -> String:
return "Value: %s\nPosition: %s" % [self.value, self.position]

View File

@ -0,0 +1,13 @@
extends Reference
class_name SampledAxis
var values: Array
var min_max: Pair
func _init(values: Array = [], min_max: Pair = Pair.new()) -> void:
self.values = values
self.min_max = min_max
func _to_string() -> String:
return "values: %s\nmin: %s, max: %s" % [self.values, self.min_max.left, self.min_max.right]

View File

@ -0,0 +1,18 @@
"""
A class representing a Pair (or Tuple) of values.
It is a lightweight class that can easily replace the improper and/or
unnecessary usage of a 2d Array (ex. `var arr: Array = [0.5, 0.6]`)
or of a Vector2 (ex. `var v2: Vector2 = Vector2(0.6, 0.8)`).
"""
extends Reference
class_name Pair
var left: float
var right: float
func _init(left: float = 0.0, right: float = 0.0) -> void:
self.left = left
self.right = right
func _to_string() -> String:
return "[%s, %s]" % [self.left, self.right]

View File

@ -1,127 +0,0 @@
tool
extends Control
class_name Point
const OFFSET : Vector2 = Vector2(13,13)
var point_value : Array setget set_value,get_value
var point_position : Vector2
var color : Color setget set_color_point, get_color_point
var color_outline : Color
var function : String setget set_function, get_function
var mouse_entered : bool = false
enum SHAPES {
Dot, Triangle, Square, Cross
}
var shape : int = 0 setget set_shape, get_shape
signal _mouse_entered(point)
signal _mouse_exited()
signal _point_pressed(point)
# Called when the node enters the scene tree for the first time.
func _ready():
pass # Replace with function body.
func _draw():
if mouse_entered:
draw_point(7,color_outline)
draw_point(5,color)
func draw_point(size : float, color : Color):
var factor : float
match shape:
SHAPES.Dot:
draw_circle(OFFSET, size, color)
SHAPES.Triangle:
size+=6
factor = 2
draw_colored_polygon([
OFFSET-Vector2(0,size/factor), OFFSET+Vector2(1,1)*size/factor, OFFSET-Vector2(1,-1)*size/factor
], color,[],null,null,false)
SHAPES.Square:
size+=4
factor = 2
draw_colored_polygon([
OFFSET-Vector2(1,1)*size/factor, OFFSET-Vector2(-1,1)*size/factor, OFFSET+Vector2(1,1)*size/factor, OFFSET-Vector2(1,-1)*size/factor
], color,[],null,null,false)
SHAPES.Cross:
size+=2
draw_line(OFFSET-Vector2(size,0), OFFSET+Vector2(size,0), color, size-5, true)
draw_line(OFFSET-Vector2(0,size), OFFSET+Vector2(0,size), color, size-5, true)
func create_point(shape : int, color : Color, color_outline : Color, position : Vector2, value : Array, function : String):
self.shape = shape
self.color = color
self.color_outline = color_outline
self.point_position = position
self.rect_position = point_position - OFFSET
self.point_value = value
self.function = function
func _on_Point_mouse_entered():
mouse_entered = true
emit_signal("_mouse_entered",self)
update()
func _on_Point_mouse_exited():
mouse_entered = false
emit_signal("_mouse_exited")
update()
func _on_Point_gui_input(event):
if event is InputEventMouseButton:
if event.is_pressed():
if event.button_index == 1:
emit_signal("_point_pressed",self)
func format_value(v : Array, format_x : bool, format_y : bool):
var x : String = str(v[0])
var y : String = str(v[1])
if format_x:
x = format(v[1])
if format_y:
y = format(v[1])
return [x,y]
func format(n):
n = str(n)
var size = n.length()
var s
for i in range(size):
if((size - i) % 3 == 0 and i > 0):
s = str(s,",", n[i])
else:
s = str(s,n[i])
return s.replace("Null","")
func set_value( v : Array = [] ) :
point_value = v
func set_color_point( c : Color ):
color = c
func set_function( f : String ):
function = f
func set_shape(s : int):
shape = s
# Public Getters
func get_value() -> Array:
return point_value
func get_color_point() -> Color:
return color
func get_function() -> String:
return function
func get_shape() -> int:
return shape

View File

@ -1,18 +0,0 @@
[gd_scene load_steps=2 format=2]
[ext_resource path="res://addons/easy_charts/utilities/components/point/point.gd" type="Script" id=1]
[node name="Point" type="Control"]
margin_left = -13.0
margin_top = -13.0
margin_right = 13.0
margin_bottom = 13.0
rect_min_size = Vector2( 16, 16 )
script = ExtResource( 1 )
__meta__ = {
"_edit_use_anchors_": false
}
[connection signal="gui_input" from="." to="." method="_on_Point_gui_input"]
[connection signal="mouse_entered" from="." to="." method="_on_Point_mouse_entered"]
[connection signal="mouse_exited" from="." to="." method="_on_Point_mouse_exited"]

View File

@ -23,10 +23,10 @@ corner_detail = 20
visible = false
anchor_right = 0.0694688
anchor_bottom = 0.067
margin_left = -185.084
margin_top = 338.399
margin_right = -185.221
margin_bottom = 339.2
margin_left = -269.77
margin_top = 276.615
margin_right = -269.907
margin_bottom = 277.416
grow_horizontal = 2
mouse_filter = 2
custom_styles/panel = SubResource( 1 )

View File

@ -0,0 +1,57 @@
extends Control
class_name PointContainer
enum PointShape {
CIRCLE,
TRIANGLE,
SQUARE,
CROSS
}
var point: Point
var color: Color
var radius: float
var shape: int
var label: String
func _ready():
pass
func set_point(point: Point, color: Color = Color.black, shape: int = PointShape.CIRCLE, radius: float = 3.0) -> void:
self.point = point
self.color = color
self.shape = shape
self.radius = radius
auto_pos(self.point.position)
func auto_pos(pos: Vector2) -> void:
self.rect_position += pos
func _draw_bounding_box() -> void:
var t_gr: Rect2 = get_global_rect()
draw_rect(Rect2(Vector2.ZERO, get_rect().size), Color.black, false, 1, true)
func _draw_label() -> void:
var lbl: Label = Label.new()
add_child(lbl)
lbl.rect_position += self.rect_size
lbl.text = str(label)
func _draw_point() -> void:
var point_rel_pos: Vector2 = self.rect_size * 0.5
match self.shape:
PointShape.CIRCLE:
draw_circle(point_rel_pos, self.radius, self.color)
PointShape.SQUARE:
draw_rect(Rect2(point_rel_pos * 0.5, point_rel_pos), self.color, true, 1.0, true)
func _draw():
# _draw_bounding_box()
_draw_point()
# _draw_label()
func _on_Control_mouse_entered():
print("hello %s " % point)

View File

@ -0,0 +1,12 @@
[gd_scene load_steps=2 format=2]
[ext_resource path="res://addons/easy_charts/utilities/containers/point_container/point_container.gd" type="Script" id=1]
[node name="PointContainer" type="Control"]
margin_left = -8.0
margin_top = -8.0
margin_right = 8.0
margin_bottom = 8.0
script = ExtResource( 1 )
[connection signal="mouse_entered" from="." to="." method="_on_Control_mouse_entered"]

View File

@ -3,46 +3,44 @@ extends Node
var alphabet : String = "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z"
var plugin_name : String = "Easy Charts"
var templates : Dictionary = {}
var chart_types : Dictionary = {
0:["LineChart","line_chart"],
1:["ColumnChart","column_chart"],
2:["ScatterChart","scatter_chart"],
3:["RadarChart","radar_chart"],
4:["PieChart","pie_chart"]
}
func _ready():
templates = _load_templates()
pass
# templates = _load_templates()
func _print_message(message : String, type : int = 0):
match type:
0:
print("[%s] => %s" % [plugin_name, message])
1:
printerr("ERROR: [%s] => %s" % [plugin_name, message])
pass
# match type:
# 0:
# print("[%s] => %s" % [plugin_name, message])
# 1:
# printerr("ERROR: [%s] => %s" % [plugin_name, message])
func _load_templates() -> Dictionary:
var template_file : File = File.new()
template_file.open("res://addons/easy_charts/templates.json",File.READ)
var templates = JSON.parse(template_file.get_as_text()).get_result()
template_file.close()
return templates
pass
return {}
# var template_file : File = File.new()
# template_file.open("res://addons/easy_charts/templates.json",File.READ)
# var templates = JSON.parse(template_file.get_as_text()).get_result()
# template_file.close()
# return templates
func get_template(template_index : int):
return templates.get(templates.keys()[template_index])
pass
# return templates.get(templates.keys()[template_index])
func get_chart_type(chart_type : int) -> Array:
return chart_types.get(chart_types.keys()[chart_type])
pass
return []
# return chart_types.get(chart_types.keys()[chart_type])
func get_letter_index(index : int) -> String:
return alphabet.split(" ")[index]
func save_dataframe_to_file(dataframe: DataFrame, path: String, delimiter: String = ";") -> void:
var file = File.new()
file.open(path, File.WRITE)
for row in dataframe.get_dataset():
file.store_line(PoolStringArray(row).join(delimiter))
file.close()
pass
# var file = File.new()
# file.open(path, File.WRITE)
# for row in dataframe.get_dataset():
# file.store_line(PoolStringArray(row).join(delimiter))
# file.close()