mirror of
https://github.com/Relintai/pandemonium_engine_easy_charts.git
synced 2025-03-14 18:28:50 +01:00
1st Refactor (#53)
fix #48 #52 #46 * Refactor so it works with the new data format. Restructured some functions in smaller pieces and changed it so they can work with data in the new format. Main way to use it will be with the new plot_function but old functions support is requiered through structure_datas (WIP) * structure_datas rework structure_datas structure the data to match the new format. * Add new plotting methods Also renamed identifiers array to the already existing y_labels and a fix to correctly calculate tics. * Add autoscale or user-defined range * Add color definition from plot_function call Introduction of a param_dic parameter on plot_function that allows for specific parameters definitions for that curve only without changing the full Chart. Also moved the label identier string to a parameter on this dicionary so it can be called without specifying a label name. * Fix representation of negative values Using negative numbers should now work on both axis. For this I created two new methods calcualte_interval_tics and calculate_number_integer_digits to avoid code repetition. I'd like to work more on this, since now the representation is correct but can look very weird for some values with a lot of empty space on the chart. * Correctly calculates tics for numbers between 0 and 1 Also some small fixes to update_function and delete_function. Changed the tic little line to point outside the chart instead of inside so it doesn't overlap with the grid line. * Correctly (almost) center axis labels Needs a little of research for the vertical centering since get_string_size doesn't behave as expected. * Add some style representation customization Width of the grid and function lines. Made drawing points for LineChart optional (especially useful when using a huge number of points to avoid overclustering). For this I had to rewrite the draw_lines function to work similar to draw_points using point_positions instead of information of the point nodes. The string position of the labels is correctly calculated now. It uses a new variable: label_displacement so it don't look too close to the axis and the border. Set some default values that make more sense to the instanciable scenes. * Fix clearing of data structures with multiple calls to plot_from_x * Fix show_x_values_as_labels Correctly sets the x position when show_x_values_as_labels is active. * Fix show_x_values_as_labels when the label is a String * restructure folder * refactor scatter_chart and line_chart * finish 1st refactoring * finish 1st refactoring * finish 1st refactoring * remove .tmp * fix min errors Co-authored-by: Jorge <63685920+JFerrerBeired@users.noreply.github.com>
This commit is contained in:
parent
1e388f49a0
commit
7c73e92f8f
28
.gitignore
vendored
28
.gitignore
vendored
@ -1,20 +1,18 @@
|
||||
# Plugin Specific ignores
|
||||
default_env.tres
|
||||
/icon.png
|
||||
/icon.png.import
|
||||
project.godot
|
||||
scn/
|
||||
/screenshot.png
|
||||
/snapshot.png
|
||||
*.tpm
|
||||
|
||||
# Godot-specific ignores
|
||||
.import/
|
||||
export.cfg
|
||||
export_presets.cfg
|
||||
# Imported translations (automatically generated from CSV files)
|
||||
*.translation
|
||||
# Plugin specific ignores
|
||||
.github/
|
||||
file-editor/
|
||||
scn/
|
||||
default_env.tres
|
||||
icon.png
|
||||
icon.png.import
|
||||
project.godot
|
||||
|
||||
# Mono-specific ignores
|
||||
.mono/
|
||||
data_*/
|
||||
mono_crash.*.json
|
||||
# System/tool-specific ignores
|
||||
.directory
|
||||
*~
|
||||
data_*/
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 fenix-hub
|
||||
Copyright (c) 2021 Nicolò Santilio
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -27,7 +27,7 @@ func _get_children():
|
||||
OutlinesTween = $OutlinesTween
|
||||
Functions = $Functions
|
||||
GridTween = $GridTween
|
||||
PointData = $PointData/PointData
|
||||
DataTooltip = $CanvasLayer/DataTooltip
|
||||
Outlines = $Outlines
|
||||
Grid = $Grid
|
||||
|
||||
@ -56,13 +56,13 @@ func load_font():
|
||||
font_size = font.get_height()
|
||||
var theme : Theme = Theme.new()
|
||||
theme.set_default_font(font)
|
||||
PointData.set_theme(theme)
|
||||
DataTooltip.set_theme(theme)
|
||||
else:
|
||||
var lbl = Label.new()
|
||||
font = lbl.get_font("")
|
||||
lbl.free()
|
||||
if bold_font != null:
|
||||
PointData.Data.set("custom_fonts/font",bold_font)
|
||||
DataTooltip.Data.set("custom_fonts/font",bold_font)
|
||||
|
||||
func _plot(source_ : String, delimiter_ : String, are_values_columns_ : bool, x_values_index_ : int):
|
||||
randomize()
|
||||
@ -70,11 +70,11 @@ func _plot(source_ : String, delimiter_ : String, are_values_columns_ : bool, x_
|
||||
clear()
|
||||
|
||||
load_font()
|
||||
PointData.hide()
|
||||
DataTooltip.hide()
|
||||
|
||||
datas = read_datas(source_,delimiter_)
|
||||
datas = read_data(source_,delimiter_)
|
||||
count_functions()
|
||||
structure_datas(datas,are_values_columns_,x_values_index_)
|
||||
structure_data(datas,are_values_columns_,x_values_index_)
|
||||
build_chart()
|
||||
calculate_pass()
|
||||
calculate_coordinates()
|
||||
@ -90,14 +90,14 @@ func plot():
|
||||
clear()
|
||||
|
||||
load_font()
|
||||
PointData.hide()
|
||||
DataTooltip.hide()
|
||||
|
||||
if source == "" or source == null:
|
||||
Utilities._print_message("Can't plot a chart without a Source file. Please, choose it in editor, or use the custom function _plot().",1)
|
||||
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_datas(source,delimiter)
|
||||
datas = read_data(source,delimiter)
|
||||
count_functions()
|
||||
structure_datas(datas,are_values_columns,x_values_index)
|
||||
structure_data(datas,are_values_columns,x_values_index)
|
||||
build_chart()
|
||||
calculate_pass()
|
||||
calculate_coordinates()
|
||||
@ -207,7 +207,7 @@ func construct_column(line : Line2D, f_index : int, function : Array):
|
||||
line.antialiased = true
|
||||
Functions.add_child(line,true)
|
||||
|
||||
func read_datas(source : String, delimiter : String):
|
||||
func read_data(source : String, delimiter : String):
|
||||
var file : File = File.new()
|
||||
file.open(source,File.READ)
|
||||
var content : Array
|
||||
@ -220,7 +220,7 @@ func read_datas(source : String, delimiter : String):
|
||||
content.erase(data)
|
||||
return content
|
||||
|
||||
func structure_datas(database : Array, are_values_columns : bool, x_values_index : int):
|
||||
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
|
||||
@ -393,13 +393,13 @@ func redraw():
|
||||
|
||||
|
||||
func show_data(point):
|
||||
PointData.update_datas(point)
|
||||
PointData.show()
|
||||
DataTooltip.update_datas(point)
|
||||
DataTooltip.show()
|
||||
|
||||
func hide_data():
|
||||
PointData.hide()
|
||||
DataTooltip.hide()
|
||||
|
||||
func clear_points():
|
||||
func clean_points():
|
||||
function_colors.clear()
|
||||
if Functions.get_children():
|
||||
for function in Functions.get_children():
|
||||
@ -448,7 +448,7 @@ func create_legend():
|
||||
|
||||
func apply_template(template_name : int):
|
||||
template = template_name
|
||||
templates = Utilities._load_templates()
|
||||
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
|
@ -1,11 +1,7 @@
|
||||
[gd_scene load_steps=3 format=2]
|
||||
|
||||
[ext_resource path="res://addons/easy_charts/ColumnChart2D/column_chart2D.gd" type="Script" id=1]
|
||||
[ext_resource path="res://addons/easy_charts/Utilities/Point/point_data.tscn" type="PackedScene" id=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 )
|
||||
@ -54,13 +50,19 @@ default_color = Color( 0.117647, 0.117647, 0.117647, 1 )
|
||||
|
||||
[node name="GridTween" type="Tween" parent="."]
|
||||
|
||||
[node name="PointData" parent="." instance=ExtResource( 2 )]
|
||||
[node name="CanvasLayer" type="CanvasLayer" parent="."]
|
||||
|
||||
[node name="PointData" parent="PointData" index="0"]
|
||||
[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="PointData"]
|
||||
[editable path="CanvasLayer/PointData"]
|
469
addons/easy_charts/2d_charts/LineChart2D/line_chart2D.gd
Normal file
469
addons/easy_charts/2d_charts/LineChart2D/line_chart2D.gd
Normal file
@ -0,0 +1,469 @@
|
||||
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
|
@ -1,9 +1,7 @@
|
||||
[gd_scene load_steps=3 format=2]
|
||||
|
||||
[ext_resource path="res://addons/easy_charts/LineChart2D/line_chart2D.gd" type="Script" id=1]
|
||||
[ext_resource path="res://addons/easy_charts/Utilities/Point/point_data.tscn" type="PackedScene" id=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"]
|
||||
@ -54,13 +52,19 @@ default_color = Color( 0.117647, 0.117647, 0.117647, 1 )
|
||||
|
||||
[node name="GridTween" type="Tween" parent="."]
|
||||
|
||||
[node name="PointData" parent="." instance=ExtResource( 2 )]
|
||||
[node name="CanvasLayer" type="CanvasLayer" parent="."]
|
||||
|
||||
[node name="PointData" parent="PointData" index="0"]
|
||||
[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="PointData"]
|
||||
[editable path="CanvasLayer/DataTooltip"]
|
448
addons/easy_charts/2d_charts/ScatterChart2D/scatter_chart2D.gd
Normal file
448
addons/easy_charts/2d_charts/ScatterChart2D/scatter_chart2D.gd
Normal file
@ -0,0 +1,448 @@
|
||||
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/DataTooltip
|
||||
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()
|
@ -1,9 +1,15 @@
|
||||
[gd_scene load_steps=3 format=2]
|
||||
|
||||
[ext_resource path="res://addons/easy_charts/ScatterChart2D/scatter_chart2D.gd" type="Script" id=1]
|
||||
<<<<<<<< HEAD:addons/easy_charts/2DChart/ScatterChart2D/scatter_chart2D.tscn
|
||||
[ext_resource path="res://addons/easy_charts/2DChart/ScatterChart2D/scatter_chart2D.gd" type="Script" id=1]
|
||||
[ext_resource path="res://addons/easy_charts/Utilities/Point/point_data.tscn" type="PackedScene" id=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]
|
||||
>>>>>>>> dev:addons/easy_charts/2d_charts/ScatterChart2D/scatter_chart2D.tscn
|
||||
|
||||
|
||||
[node name="ScatterChart2D" type="Node2D"]
|
||||
script = ExtResource( 1 )
|
||||
@ -44,13 +50,19 @@ default_color = Color( 0.117647, 0.117647, 0.117647, 1 )
|
||||
|
||||
[node name="PointTween" type="Tween" parent="."]
|
||||
|
||||
[node name="PointData" parent="." instance=ExtResource( 2 )]
|
||||
[node name="CanvasLayer" type="CanvasLayer" parent="."]
|
||||
|
||||
[node name="PointData" parent="PointData" index="0"]
|
||||
[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"]
|
||||
visible = false
|
||||
margin_left = 71.2491
|
||||
margin_top = -146.673
|
||||
margin_right = 71.1131
|
||||
margin_bottom = -145.873
|
||||
|
||||
[editable path="PointData"]
|
||||
[editable path="CanvasLayer/PointData"]
|
@ -16,10 +16,8 @@ the horizontal axis and the value of the other variable determining the position
|
||||
|
||||
onready var PlaceholderPoint = $Chart/Point
|
||||
onready var Space = $ImmediateGeometry
|
||||
onready var PointData = $PointData/PointData
|
||||
onready var DataTooltip = $DataTooltip/DataTooltip
|
||||
|
||||
var point_node : PackedScene = preload("../Utilities/Point/Point.tscn")
|
||||
var FunctionLegend : PackedScene = preload("../Utilities/Legend/function_legend.tscn")
|
||||
|
||||
var font_size : float = 16
|
||||
var const_height : float = font_size/2*font_size/20
|
||||
@ -130,13 +128,13 @@ func load_font():
|
||||
font_size = font.get_height()
|
||||
var theme : Theme = Theme.new()
|
||||
theme.set_default_font(font)
|
||||
PointData.set_theme(theme)
|
||||
DataTooltip.set_theme(theme)
|
||||
else:
|
||||
var lbl = Label.new()
|
||||
font = lbl.get_font("")
|
||||
lbl.free()
|
||||
if bold_font != null:
|
||||
PointData.Data.set("custom_fonts/font",bold_font)
|
||||
DataTooltip.Data.set("custom_fonts/font",bold_font)
|
||||
|
||||
func _plot(source_ : String, delimiter_ : String, are_values_columns_ : bool, x_values_index_ : int):
|
||||
randomize()
|
||||
@ -144,11 +142,11 @@ func _plot(source_ : String, delimiter_ : String, are_values_columns_ : bool, x_
|
||||
clear()
|
||||
|
||||
load_font()
|
||||
PointData.hide()
|
||||
DataTooltip.hide()
|
||||
|
||||
datas = read_datas(source_,delimiter_)
|
||||
datas = read_data(source_,delimiter_)
|
||||
# count_functions()
|
||||
structure_datas(datas,are_values_columns_,x_values_index_)
|
||||
structure_data(datas,are_values_columns_,x_values_index_)
|
||||
# build_chart()
|
||||
# calculate_pass()
|
||||
# calculate_coordinates()
|
||||
@ -164,14 +162,14 @@ func plot():
|
||||
clear()
|
||||
|
||||
load_font()
|
||||
PointData.hide()
|
||||
DataTooltip.hide()
|
||||
|
||||
if source == "" or source == null:
|
||||
Utilities._print_message("Can't plot a chart without a Source file. Please, choose it in editor, or use the custom function _plot().",1)
|
||||
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_datas(source,delimiter)
|
||||
datas = read_data(source,delimiter)
|
||||
# count_functions()
|
||||
structure_datas(datas,are_values_columns,x_values_index)
|
||||
structure_data(datas,are_values_columns,x_values_index)
|
||||
# build_chart()
|
||||
# calculate_pass()
|
||||
# calculate_coordinates()
|
||||
@ -182,7 +180,7 @@ func plot():
|
||||
emit_signal("chart_plotted", self)
|
||||
|
||||
|
||||
func read_datas(source : String, delimiter : String):
|
||||
func read_data(source : String, delimiter : String):
|
||||
var file : File = File.new()
|
||||
file.open(source,File.READ)
|
||||
var content : Array
|
||||
@ -195,7 +193,7 @@ func read_datas(source : String, delimiter : String):
|
||||
content.erase(data)
|
||||
return content
|
||||
|
||||
func structure_datas(database : Array, are_values_columns : bool, x_values_index : int):
|
||||
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
|
||||
@ -295,7 +293,7 @@ func get_legend():
|
||||
|
||||
func apply_template(template_name : String):
|
||||
template = template_name
|
||||
templates = Utilities._load_templates()
|
||||
templates = ECUtilities._load_templates()
|
||||
if template_name!=null and template_name!="":
|
||||
var custom_template = templates[template.to_lower()]
|
||||
function_colors = custom_template.function_colors
|
@ -1,9 +1,15 @@
|
||||
[gd_scene load_steps=7 format=2]
|
||||
|
||||
[ext_resource path="res://addons/easy_charts/ScatterChart3D/scatter_chart3D.gd" type="Script" id=1]
|
||||
<<<<<<<< HEAD:addons/easy_charts/3DChart/ScatterChart3D/scatter_chart3D.tscn
|
||||
[ext_resource path="res://addons/easy_charts/3DChart/ScatterChart3D/scatter_chart3D.gd" type="Script" id=1]
|
||||
[ext_resource path="res://addons/easy_charts/Utilities/Point/point_data.tscn" type="PackedScene" id=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]
|
||||
>>>>>>>> dev:addons/easy_charts/3d_charts/ScatterChart3D/scatter_chart3D.tscn
|
||||
|
||||
|
||||
[sub_resource type="SpatialMaterial" id=1]
|
||||
flags_unshaded = true
|
||||
@ -42,13 +48,19 @@ 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="PointData" parent="." instance=ExtResource( 2 )]
|
||||
[node name="CanvasLayer" type="CanvasLayer" parent="."]
|
||||
|
||||
[node name="PointData" parent="PointData" index="0"]
|
||||
[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="PointData"]
|
||||
[editable path="CanvasLayer/DataTooltip"]
|
@ -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)
|
||||
PointData.set_theme(theme)
|
||||
else:
|
||||
var lbl = Label.new()
|
||||
font = lbl.get_font("")
|
||||
lbl.free()
|
||||
if bold_font != null:
|
||||
PointData.Data.set("custom_fonts/font",bold_font)
|
||||
|
||||
|
||||
func _plot(source: String, delimiter: String, are_values_columns: bool, x_values_index: int):
|
||||
randomize()
|
||||
|
||||
clear()
|
||||
|
||||
load_font()
|
||||
PointData.hide()
|
||||
|
||||
datas = read_datas(source, delimiter)
|
||||
count_functions()
|
||||
structure_datas(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()
|
||||
PointData.hide()
|
||||
|
||||
if source == "" or source == null:
|
||||
Utilities._print_message("Can't plot a chart without a Source file. Please, choose it in editor, or use the custom function _plot().", 1)
|
||||
return
|
||||
datas = read_datas(source,delimiter)
|
||||
count_functions()
|
||||
structure_datas(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_datas(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_datas(database: Array, are_values_columns: bool, x_values_index: int):
|
||||
# @x_values_index can be either a column or a row relative to x values
|
||||
# @y_values can be either a column or a row relative to y values
|
||||
self.are_values_columns = are_values_columns
|
||||
match are_values_columns:
|
||||
true:
|
||||
for row in database.size():
|
||||
var t_vals: Array
|
||||
for column in database[row].size():
|
||||
if column == x_values_index:
|
||||
var x_data = database[row][column]
|
||||
if x_data.is_valid_float() or x_data.is_valid_integer():
|
||||
x_datas.append(x_data as float)
|
||||
else:
|
||||
x_datas.append(x_data.replace(",", ".") as float)
|
||||
else:
|
||||
if row != 0:
|
||||
var y_data = database[row][column]
|
||||
if y_data.is_valid_float() or y_data.is_valid_integer():
|
||||
t_vals.append(y_data as float)
|
||||
else:
|
||||
t_vals.append(y_data.replace(",", ".") as float)
|
||||
else:
|
||||
y_labels.append(str(database[row][column]))
|
||||
if not t_vals.empty():
|
||||
y_datas.append(t_vals)
|
||||
x_label = str(x_datas.pop_front())
|
||||
false:
|
||||
for row in database.size():
|
||||
if row == x_values_index:
|
||||
x_datas = (database[row])
|
||||
x_label = x_datas.pop_front() as String
|
||||
else:
|
||||
var values = database[row] as Array
|
||||
y_labels.append(values.pop_front() as String)
|
||||
y_datas.append(values)
|
||||
for data in y_datas:
|
||||
for value in data.size():
|
||||
data[value] = data[value] as float
|
||||
|
||||
# draw y labels
|
||||
var to_order: Array
|
||||
var to_order_min: Array
|
||||
for cluster in y_datas.size():
|
||||
# define x_chors and y_chors
|
||||
var ordered_cluster = y_datas[cluster] as Array
|
||||
ordered_cluster.sort()
|
||||
ordered_cluster = PoolIntArray(ordered_cluster)
|
||||
var margin_max = ordered_cluster[ordered_cluster.size() - 1]
|
||||
var margin_min = ordered_cluster[0]
|
||||
to_order.append(margin_max)
|
||||
to_order_min.append(margin_min)
|
||||
|
||||
to_order.sort()
|
||||
to_order_min.sort()
|
||||
var margin = to_order.pop_back()
|
||||
if not origin_at_zero:
|
||||
y_margin_min = to_order_min.pop_front()
|
||||
v_dist = y_decim * pow(10.0,str(margin).length() - 2)
|
||||
var multi = 0
|
||||
var p = (v_dist * multi) + (y_margin_min if not origin_at_zero else 0)
|
||||
y_chors.append(p as String)
|
||||
while p < margin:
|
||||
multi+=1
|
||||
p = (v_dist * multi) + (y_margin_min if not origin_at_zero else 0)
|
||||
y_chors.append(p as String)
|
||||
|
||||
# draw x_labels
|
||||
if not show_x_values_as_labels:
|
||||
to_order.clear()
|
||||
to_order = x_datas as PoolIntArray
|
||||
|
||||
to_order.sort()
|
||||
margin = to_order.pop_back()
|
||||
if not origin_at_zero:
|
||||
x_margin_min = to_order.pop_front()
|
||||
h_dist = x_decim * pow(10.0, str(margin).length() - 2)
|
||||
multi = 0
|
||||
p = (h_dist * multi) + (x_margin_min if not origin_at_zero else 0)
|
||||
x_labels.append(p as String)
|
||||
while p < margin:
|
||||
multi += 1
|
||||
p = (h_dist * multi) + (x_margin_min if not origin_at_zero else 0)
|
||||
x_labels.append(p as String)
|
||||
|
||||
|
||||
func build_chart():
|
||||
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
|
@ -1,287 +0,0 @@
|
||||
tool
|
||||
extends Chart
|
||||
class_name RadarChart
|
||||
|
||||
"""
|
||||
[RadarChart] - General purpose node for Radar Charts
|
||||
A radar chart is a graphical method of displaying multivariate data in the form
|
||||
of a two-dimensional chart of three or more quantitative variables represented on axes
|
||||
starting from the same point. The relative position and angle of the axes is typically
|
||||
uninformative, but various heuristics, such as algorithms that plot data as the maximal
|
||||
total area, can be applied to sort the variables (axes) into relative positions that reveal
|
||||
distinct correlations, trade-offs, and a multitude of other comparative measures.
|
||||
|
||||
/ source : Wikipedia /
|
||||
"""
|
||||
|
||||
func _get_property_list():
|
||||
return [
|
||||
# Chart Properties
|
||||
{
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Properties/are_values_columns",
|
||||
"type": TYPE_BOOL
|
||||
},
|
||||
{
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "-1,100,1",
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Properties/labels_index",
|
||||
"type": TYPE_INT
|
||||
},
|
||||
{
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "-1,100,1",
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Properties/function_names_index",
|
||||
"type": TYPE_INT
|
||||
},
|
||||
{
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Properties/use_height_as_radius",
|
||||
"type": TYPE_BOOL
|
||||
},
|
||||
{
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "0,2000",
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Properties/radius",
|
||||
"type": TYPE_REAL
|
||||
},
|
||||
|
||||
# Chart Display
|
||||
{
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "0.1,100",
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Display/full_scale",
|
||||
"type": TYPE_REAL
|
||||
},
|
||||
|
||||
# Chart Style
|
||||
{
|
||||
"hint": 24,
|
||||
"hint_string": "%d/%d:%s"%[TYPE_INT, PROPERTY_HINT_ENUM,
|
||||
PoolStringArray(PointShapes.keys()).join(",")],
|
||||
"name": "Chart_Style/points_shape",
|
||||
"type": TYPE_ARRAY,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE
|
||||
},
|
||||
{
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Style/function_colors",
|
||||
"type": TYPE_COLOR_ARRAY
|
||||
},
|
||||
{
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Style/outline_color",
|
||||
"type": TYPE_COLOR
|
||||
},
|
||||
{
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Style/grid_color",
|
||||
"type": TYPE_COLOR
|
||||
},
|
||||
{
|
||||
"class_name": "Font",
|
||||
"hint": PROPERTY_HINT_RESOURCE_TYPE,
|
||||
"hint_string": "Font",
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Style/font",
|
||||
"type": TYPE_OBJECT
|
||||
},
|
||||
{
|
||||
"class_name": "Font",
|
||||
"hint": PROPERTY_HINT_RESOURCE_TYPE,
|
||||
"hint_string": "Font",
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Style/bold_font",
|
||||
"type": TYPE_OBJECT
|
||||
},
|
||||
{
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Style/font_color",
|
||||
"type": TYPE_COLOR
|
||||
},
|
||||
{
|
||||
"hint": PROPERTY_HINT_ENUM,
|
||||
"hint_string": PoolStringArray(Utilities.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_datas(database : Array):
|
||||
# @x_values_index can be either a column or a row relative to x values
|
||||
# @y_values can be either a column or a row relative to y values
|
||||
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
|
||||
|
||||
var radar_polygon : Array
|
||||
|
||||
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)
|
||||
|
||||
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
|
||||
|
||||
clear_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])
|
||||
# str("Function %s"%_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()
|
@ -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
|
||||
PointData = $PointData/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)
|
||||
PointData.set_theme(theme)
|
||||
else:
|
||||
var lbl = Label.new()
|
||||
font = lbl.get_font("")
|
||||
lbl.free()
|
||||
if bold_font != null:
|
||||
PointData.Data.set("custom_fonts/font",bold_font)
|
||||
|
||||
func _plot(source_ : String, delimiter_ : String, are_values_columns_ : bool, x_values_index_ : int):
|
||||
randomize()
|
||||
|
||||
clear()
|
||||
|
||||
load_font()
|
||||
PointData.hide()
|
||||
|
||||
datas = read_datas(source_,delimiter_)
|
||||
count_functions()
|
||||
structure_datas(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()
|
||||
PointData.hide()
|
||||
|
||||
if source == "" or source == null:
|
||||
Utilities._print_message("Can't plot a chart without a Source file. Please, choose it in editor, or use the custom function _plot().",1)
|
||||
return
|
||||
datas = read_datas(source,delimiter)
|
||||
count_functions()
|
||||
structure_datas(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_datas(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_datas(database : Array, are_values_columns : bool, x_values_index : int):
|
||||
# @x_values_index can be either a column or a row relative to x values
|
||||
# @y_values can be either a column or a row relative to y values
|
||||
self.are_values_columns = are_values_columns
|
||||
match are_values_columns:
|
||||
true:
|
||||
for row in database.size():
|
||||
var t_vals : Array
|
||||
for column in database[row].size():
|
||||
if column == x_values_index:
|
||||
var x_data = database[row][column]
|
||||
if x_data.is_valid_float() or x_data.is_valid_integer():
|
||||
x_datas.append(x_data as float)
|
||||
else:
|
||||
x_datas.append(x_data.replace(",",".") as float)
|
||||
else:
|
||||
if row != 0:
|
||||
var y_data = database[row][column]
|
||||
if y_data.is_valid_float() or y_data.is_valid_integer():
|
||||
t_vals.append(y_data as float)
|
||||
else:
|
||||
t_vals.append(y_data.replace(",",".") as float)
|
||||
else:
|
||||
y_labels.append(str(database[row][column]))
|
||||
if not t_vals.empty():
|
||||
y_datas.append(t_vals)
|
||||
x_label = str(x_datas.pop_front())
|
||||
false:
|
||||
for row in database.size():
|
||||
if row == x_values_index:
|
||||
x_datas = (database[row])
|
||||
x_label = x_datas.pop_front() as String
|
||||
else:
|
||||
var values = database[row] as Array
|
||||
y_labels.append(values.pop_front() as String)
|
||||
y_datas.append(values)
|
||||
for data in y_datas:
|
||||
for value in data.size():
|
||||
data[value] = data[value] as float
|
||||
|
||||
# draw y labels
|
||||
var to_order : Array
|
||||
var to_order_min : Array
|
||||
for cluster in y_datas.size():
|
||||
# define x_chors and y_chors
|
||||
var ordered_cluster = y_datas[cluster] as Array
|
||||
ordered_cluster.sort()
|
||||
ordered_cluster = PoolIntArray(ordered_cluster)
|
||||
var margin_max = ordered_cluster[ordered_cluster.size()-1]
|
||||
var margin_min = ordered_cluster[0]
|
||||
to_order.append(margin_max)
|
||||
to_order_min.append(margin_min)
|
||||
|
||||
to_order.sort()
|
||||
to_order_min.sort()
|
||||
var margin = to_order.pop_back()
|
||||
if not origin_at_zero:
|
||||
y_margin_min = to_order_min.pop_front()
|
||||
v_dist = y_decim * pow(10.0,str(margin).length()-2)
|
||||
var multi = 0
|
||||
var p = (v_dist*multi) + ((y_margin_min) if not origin_at_zero else 0)
|
||||
y_chors.append(p as String)
|
||||
while p < margin:
|
||||
multi+=1
|
||||
p = (v_dist*multi) + ((y_margin_min) if not origin_at_zero else 0)
|
||||
y_chors.append(p as String)
|
||||
|
||||
# draw x_labels
|
||||
if not show_x_values_as_labels:
|
||||
to_order.clear()
|
||||
to_order = x_datas as PoolIntArray
|
||||
|
||||
to_order.sort()
|
||||
margin = to_order.pop_back()
|
||||
if not origin_at_zero:
|
||||
x_margin_min = to_order.pop_front()
|
||||
h_dist = x_decim * pow(10.0,str(margin).length()-2)
|
||||
multi = 0
|
||||
p = (h_dist*multi) + ((x_margin_min) if not origin_at_zero else 0)
|
||||
x_labels.append(p as String)
|
||||
while p < margin:
|
||||
multi+=1
|
||||
p = (h_dist*multi) + ((x_margin_min) if not origin_at_zero else 0)
|
||||
x_labels.append(p as String)
|
||||
|
||||
func build_chart():
|
||||
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):
|
||||
PointData.update_datas(point)
|
||||
PointData.show()
|
||||
|
||||
func hide_data():
|
||||
PointData.hide()
|
||||
|
||||
func clear_points():
|
||||
function_colors.clear()
|
||||
if Functions.get_children():
|
||||
for function in Functions.get_children():
|
||||
function.queue_free()
|
||||
|
||||
func set_legend(l : Array):
|
||||
legend = l
|
||||
|
||||
func get_legend():
|
||||
return legend
|
||||
|
||||
func invert_chart():
|
||||
invert_chart = !invert_chart
|
||||
count_functions()
|
||||
redraw()
|
||||
create_legend()
|
||||
|
||||
func count_functions():
|
||||
if are_values_columns:
|
||||
if not invert_chart:
|
||||
functions = datas[0].size()-1
|
||||
else:
|
||||
functions = datas.size()-1
|
||||
else:
|
||||
if invert_chart:
|
||||
functions = datas[0].size()-1
|
||||
else:
|
||||
functions = datas.size()-1
|
||||
|
||||
func create_legend():
|
||||
legend.clear()
|
||||
for function in functions:
|
||||
var function_legend = FunctionLegend.instance()
|
||||
var f_name : String
|
||||
if invert_chart:
|
||||
f_name = x_datas[function] as String
|
||||
else:
|
||||
f_name = y_labels[function]
|
||||
var legend_font : Font
|
||||
if font != null:
|
||||
legend_font = font
|
||||
if bold_font != null:
|
||||
legend_font = bold_font
|
||||
function_legend.create_legend(f_name,function_colors[function],bold_font,font_color)
|
||||
legend.append(function_legend)
|
||||
|
||||
func apply_template(template_name : int):
|
||||
template = template_name
|
||||
templates = Utilities._load_templates()
|
||||
if template_name!=null:
|
||||
var custom_template = templates.get(templates.keys()[template_name])
|
||||
function_colors = custom_template.function_colors as PoolColorArray
|
||||
v_lines_color = Color(custom_template.v_lines_color)
|
||||
h_lines_color = Color(custom_template.h_lines_color)
|
||||
box_color = Color(custom_template.outline_color)
|
||||
font_color = Color(custom_template.font_color)
|
||||
property_list_changed_notify()
|
||||
|
||||
if Engine.editor_hint:
|
||||
_get_children()
|
||||
Outlines.set_default_color(box_color)
|
||||
Grid.get_node("VLine").set_default_color(v_lines_color)
|
||||
Grid.get_node("HLine").set_default_color(h_lines_color)
|
||||
|
||||
|
||||
func _enter_tree():
|
||||
_ready()
|
@ -1,40 +0,0 @@
|
||||
tool
|
||||
extends Container
|
||||
|
||||
var LineChart = preload("LineChart/LineChart.tscn")
|
||||
|
||||
export (String,"None","LineChart","BoxChart") var chart_type : String setget set_type,get_type
|
||||
var chart : Control setget set_chart,get_chart
|
||||
var templates : Dictionary
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready():
|
||||
set_chart(get_child(0))
|
||||
var template_file : File = File.new()
|
||||
template_file.open("res://addons/easy_charts/templates.json",File.READ)
|
||||
templates = JSON.parse(template_file.get_as_text()).get_result()
|
||||
template_file.close()
|
||||
|
||||
func set_type(type : String):
|
||||
chart_type = type
|
||||
var new_node
|
||||
if get_children().size():
|
||||
for child in get_children():
|
||||
child.queue_free()
|
||||
if Engine.editor_hint:
|
||||
match type:
|
||||
"LineChart":
|
||||
new_node = LineChart.instance()
|
||||
add_child(new_node)
|
||||
new_node.set_owner(owner)
|
||||
"None":
|
||||
set_chart(null)
|
||||
|
||||
func get_type():
|
||||
return chart_type
|
||||
|
||||
func set_chart(ch : Control):
|
||||
chart = ch
|
||||
|
||||
func get_chart():
|
||||
return chart
|
@ -1,40 +0,0 @@
|
||||
tool
|
||||
extends Node2D
|
||||
|
||||
var LineChart = preload("LineChart2D/LineChart2D.tscn")
|
||||
var ColumnChart = preload("ColumnChart2D/ColumnChart2D.tscn")
|
||||
|
||||
export (String,"None","LineChart2D","ColumnChart2D") var chart_type : String setget set_type,get_type
|
||||
var chart : Node2D setget set_chart,get_chart
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready():
|
||||
set_chart(get_child(0))
|
||||
|
||||
func set_type(type : String):
|
||||
chart_type = type
|
||||
var new_node
|
||||
if get_children().size():
|
||||
for child in get_children():
|
||||
child.queue_free()
|
||||
if Engine.editor_hint:
|
||||
match type:
|
||||
"LineChart2D":
|
||||
new_node = LineChart.instance()
|
||||
add_child(new_node)
|
||||
new_node.set_owner(owner)
|
||||
"ColumnChart2D":
|
||||
new_node = ColumnChart.instance()
|
||||
add_child(new_node)
|
||||
new_node.set_owner(owner)
|
||||
"None":
|
||||
set_chart(null)
|
||||
|
||||
func get_type():
|
||||
return chart_type
|
||||
|
||||
func set_chart(ch : Node2D):
|
||||
chart = ch
|
||||
|
||||
func get_chart():
|
||||
return chart
|
@ -1,101 +0,0 @@
|
||||
tool
|
||||
extends Reference
|
||||
class_name DataFrame
|
||||
|
||||
var _data_matrix : Matrix
|
||||
var _index : PoolStringArray = []
|
||||
var _header : PoolStringArray = []
|
||||
var _dataframe : Array = []
|
||||
var _dataset : Array = []
|
||||
|
||||
func _init(data_matrix : Matrix, index : PoolStringArray, header : PoolStringArray) -> void:
|
||||
if data_matrix.get_size()[1] == header.size(): header.insert(0,"")
|
||||
if index.empty() : for value in range(data_matrix.get_size().x) : index.append("f%s"%value)
|
||||
self._data_matrix = data_matrix
|
||||
self._index = index
|
||||
self._header = header
|
||||
self._dataset = build_dataset(data_matrix.to_array(), index, header)
|
||||
self._dataframe = build_dataframe_from_matrix(data_matrix, index, header)
|
||||
|
||||
func build_dataset(data : Array, index : PoolStringArray, header : PoolStringArray) -> Array:
|
||||
var dataset : Array = [Array(header)]
|
||||
for i in range(index.size()):
|
||||
var set : Array = data[i].duplicate()
|
||||
set.insert(0, index[i])
|
||||
dataset.append(set)
|
||||
return dataset
|
||||
|
||||
func build_dataframe(data : Array, index : PoolStringArray, header : PoolStringArray) -> Array:
|
||||
var dataframe : Array = [Array(header)]
|
||||
for row_i in range(data.size()): dataframe.append([index[row_i]]+data[row_i])
|
||||
return dataframe
|
||||
|
||||
func build_dataframe_from_matrix(data_matrix : Matrix, index : PoolStringArray, header : PoolStringArray) -> Array:
|
||||
var data : Array = data_matrix.to_array()
|
||||
return build_dataframe(data, index, header)
|
||||
|
||||
func insert_column(header_n : String, column : Array, index : int = _dataframe[0].size()) -> void:
|
||||
assert(column.size()+1 == _dataframe.size(), "error: the column size must match the dataframe column size")
|
||||
_header.insert(index, header_n)
|
||||
_data_matrix.insert_column(column, index-1)
|
||||
self._dataframe = build_dataframe_from_matrix(_data_matrix, _index, _header)
|
||||
|
||||
func insert_row(index_n : String, row : Array, index : int = _dataframe.size()) -> void:
|
||||
assert(row.size()+1 == _dataframe[0].size(), "error: the row size must match the dataframe row size")
|
||||
_index.insert(index-1, index_n)
|
||||
_data_matrix.insert_row(row, index-1)
|
||||
self._dataframe = build_dataframe_from_matrix(_data_matrix, _index, _header)
|
||||
|
||||
func get_matrix() -> Matrix:
|
||||
return _data_matrix
|
||||
|
||||
func get_dataframe() -> Array:
|
||||
return _dataframe
|
||||
|
||||
func get_dataset() -> Array:
|
||||
return _dataset
|
||||
|
||||
func get_index() -> PoolStringArray:
|
||||
return _index
|
||||
|
||||
|
||||
func _to_string() -> String:
|
||||
var last_string_len : int
|
||||
for row in _dataframe:
|
||||
for column in row:
|
||||
var string_len : int = str(column).length()
|
||||
last_string_len = string_len if string_len > last_string_len else last_string_len
|
||||
var string : String = ""
|
||||
for row_i in _dataframe.size():
|
||||
for column_i in _dataframe[row_i].size():
|
||||
string+="%*s" % [last_string_len+1, _dataframe[row_i][column_i]]
|
||||
string+="\n"
|
||||
return string
|
||||
|
||||
# ...............................................................................
|
||||
func get_column_h(header : String) -> Array:
|
||||
var header_i : int = -1
|
||||
var array : Array = []
|
||||
for header_ix in range(_dataframe[0].size()):
|
||||
if _dataframe[0][header_ix] == header: header_i = header_ix; continue
|
||||
if header_i!=-1: for row in _dataframe: array.append(row[header_i])
|
||||
return array
|
||||
|
||||
func get_row_i(index : String) -> Array:
|
||||
var index_i : int
|
||||
for row in _dataframe: if row[0] == index: return row
|
||||
return []
|
||||
|
||||
func _get(_property : String):
|
||||
if _property.split(";").size() == 2:
|
||||
var property : PoolStringArray = _property.split(";")
|
||||
pass
|
||||
elif _property.split(":").size() == 2:
|
||||
var property : PoolStringArray = _property.split(":")
|
||||
if int(property[0]) == 0: return get_row_i(property[1])
|
||||
else: return get_column_h(property[1])
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,61 +0,0 @@
|
||||
tool
|
||||
extends Reference
|
||||
class_name Matrix
|
||||
|
||||
var _matrix : Array = []
|
||||
|
||||
func _init(matrix : Array = []) -> void:
|
||||
_matrix = matrix
|
||||
|
||||
func _to_string() -> String:
|
||||
var last_string_len : int
|
||||
for row in _matrix:
|
||||
for column in row:
|
||||
var string_len : int = str(column).length()
|
||||
last_string_len = string_len if string_len > last_string_len else last_string_len
|
||||
var string : String = "\n"
|
||||
for row_i in _matrix.size():
|
||||
for column_i in _matrix[row_i].size():
|
||||
string+="%*s" % [last_string_len+1 if column_i!=0 else last_string_len, _matrix[row_i][column_i]]
|
||||
string+="\n"
|
||||
return string
|
||||
|
||||
func insert_row(row : Array, index : int = _matrix.size()) -> void:
|
||||
assert(row.size() == _matrix[0].size(), "error: the row size must match matrix row size")
|
||||
_matrix.insert(index, row)
|
||||
|
||||
func insert_column(column : Array, index : int = _matrix[0].size()) -> void:
|
||||
assert(column.size() == _matrix.size(), "error: the column size must match matrix column size")
|
||||
for row_idx in column.size():
|
||||
_matrix[row_idx].insert(index, column[row_idx])
|
||||
|
||||
func to_array() -> Array:
|
||||
return _matrix.duplicate()
|
||||
|
||||
func get_size() -> Vector2:
|
||||
return Vector2(_matrix.size(), _matrix[0].size())
|
||||
|
||||
func get_column(column : int) -> Array:
|
||||
if column >= get_size()[1]: printerr("error")
|
||||
var column_array : Array = []
|
||||
for row in _matrix: column_array.append(row[column])
|
||||
return column_array
|
||||
|
||||
func get_row(row : int) -> Array:
|
||||
if row >= get_size()[0]: printerr("error")
|
||||
return _matrix[row]
|
||||
#
|
||||
#func multiply_int(_int : int) -> void:
|
||||
# _matrix = MatrixGenerator.multiply_int(self, _int).to_array()
|
||||
#
|
||||
#func multiply_float(_float : int) -> void:
|
||||
# _matrix = MatrixGenerator.multiply_float(self, _float).to_array()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,12 +0,0 @@
|
||||
[gd_scene load_steps=2 format=2]
|
||||
|
||||
[ext_resource path="res://addons/easy_charts/ChartContainer.gd" type="Script" id=1]
|
||||
|
||||
[node name="Control" type="ColorRect"]
|
||||
margin_right = 15.0
|
||||
margin_bottom = 3.0
|
||||
rect_min_size = Vector2( 15, 3 )
|
||||
script = ExtResource( 1 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
tool
|
||||
extends PanelContainer
|
||||
class_name PointData
|
||||
|
||||
var value : String = ""
|
||||
var position : Vector2 = Vector2()
|
||||
|
||||
var OFFSET : Vector2 = Vector2(15,35)
|
||||
var GAP : Vector2 = Vector2(0,15)
|
||||
|
||||
onready var Data : Label = $PointData/Value/x
|
||||
onready var Value : Label = $PointData/Value/y
|
||||
onready var Function : Label = $PointData/Function
|
||||
|
||||
func _ready():
|
||||
hide()
|
||||
|
||||
func _process(delta):
|
||||
if get_global_mouse_position().y > OFFSET.y + GAP.y:
|
||||
rect_position = get_global_mouse_position() - OFFSET - GAP
|
||||
else:
|
||||
rect_position = get_global_mouse_position() + GAP*5 - OFFSET
|
||||
|
||||
func update_datas(point : Control):
|
||||
update_size()
|
||||
|
||||
get("custom_styles/panel").set("bg_color",point.color)
|
||||
|
||||
var font_color : Color
|
||||
if point.color.g < 0.75:
|
||||
font_color = Color(1,1,1,1)
|
||||
else:
|
||||
font_color = Color(0,0,0,1)
|
||||
Data.set("custom_colors/font_color",font_color)
|
||||
Value.set("custom_colors/font_color",font_color)
|
||||
Function.set("custom_colors/font_color",font_color)
|
||||
get("custom_styles/panel").set("border_color",font_color)
|
||||
|
||||
Data.set_text(point.point_value[0]+":")
|
||||
Value.set_text(point.point_value[1])
|
||||
Function.set_text(point.function)
|
||||
update()
|
||||
show()
|
||||
|
||||
func update_slice_datas(slice : Slice):
|
||||
update_size()
|
||||
|
||||
get("custom_styles/panel").set("bg_color",slice.color)
|
||||
|
||||
var font_color : Color
|
||||
if slice.color.g < 0.75:
|
||||
font_color = Color(1,1,1,1)
|
||||
else:
|
||||
font_color = Color(0,0,0,1)
|
||||
Data.set("custom_colors/font_color",font_color)
|
||||
Value.set("custom_colors/font_color",font_color)
|
||||
Function.set("custom_colors/font_color",font_color)
|
||||
get("custom_styles/panel").set("border_color",font_color)
|
||||
|
||||
Data.set_text(slice.x_value+":")
|
||||
Value.set_text(slice.y_value)
|
||||
Function.set_text(slice.function)
|
||||
update()
|
||||
show()
|
||||
|
||||
func update_size():
|
||||
OFFSET.x = get_size().x/2
|
||||
OFFSET.y = get_size().y
|
||||
GAP.y = OFFSET.y/3
|
@ -1,85 +0,0 @@
|
||||
[gd_scene load_steps=3 format=2]
|
||||
|
||||
[ext_resource path="res://addons/easy_charts/Utilities/Point/point_data.gd" type="Script" id=1]
|
||||
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id=1]
|
||||
content_margin_left = 10.0
|
||||
content_margin_right = 10.0
|
||||
content_margin_top = 5.0
|
||||
content_margin_bottom = 5.0
|
||||
bg_color = Color( 1, 1, 1, 0 )
|
||||
border_width_left = 2
|
||||
border_width_top = 2
|
||||
border_width_right = 2
|
||||
border_width_bottom = 2
|
||||
border_color = Color( 1, 1, 1, 1 )
|
||||
corner_radius_top_left = 5
|
||||
corner_radius_top_right = 5
|
||||
corner_radius_bottom_right = 5
|
||||
corner_radius_bottom_left = 5
|
||||
corner_detail = 20
|
||||
|
||||
[node name="CanvasLayer" type="CanvasLayer"]
|
||||
|
||||
[node name="PointData" type="PanelContainer" parent="."]
|
||||
visible = false
|
||||
anchor_right = 0.0694688
|
||||
anchor_bottom = 0.067
|
||||
margin_left = 79.7858
|
||||
margin_top = -250.75
|
||||
margin_right = 79.6496
|
||||
margin_bottom = -249.95
|
||||
grow_horizontal = 2
|
||||
mouse_filter = 2
|
||||
custom_styles/panel = SubResource( 1 )
|
||||
script = ExtResource( 1 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": true
|
||||
}
|
||||
|
||||
[node name="PointData" type="VBoxContainer" parent="PointData"]
|
||||
margin_left = 10.0
|
||||
margin_top = 5.0
|
||||
margin_right = 61.0
|
||||
margin_bottom = 36.0
|
||||
grow_horizontal = 2
|
||||
size_flags_horizontal = 3
|
||||
custom_constants/separation = 3
|
||||
alignment = 1
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="Function" type="Label" parent="PointData/PointData"]
|
||||
margin_right = 51.0
|
||||
margin_bottom = 14.0
|
||||
size_flags_horizontal = 3
|
||||
align = 1
|
||||
valign = 1
|
||||
|
||||
[node name="Value" type="HBoxContainer" parent="PointData/PointData"]
|
||||
margin_top = 17.0
|
||||
margin_right = 51.0
|
||||
margin_bottom = 31.0
|
||||
grow_horizontal = 2
|
||||
size_flags_horizontal = 6
|
||||
alignment = 1
|
||||
|
||||
[node name="x" type="Label" parent="PointData/PointData/Value"]
|
||||
margin_right = 39.0
|
||||
margin_bottom = 14.0
|
||||
size_flags_horizontal = 3
|
||||
custom_colors/font_color = Color( 1, 1, 1, 1 )
|
||||
text = "Value:"
|
||||
align = 2
|
||||
valign = 1
|
||||
|
||||
[node name="y" type="Label" parent="PointData/PointData/Value"]
|
||||
margin_left = 43.0
|
||||
margin_right = 51.0
|
||||
margin_bottom = 14.0
|
||||
size_flags_horizontal = 3
|
||||
custom_colors/font_color = Color( 1, 1, 1, 1 )
|
||||
text = "0"
|
||||
valign = 1
|
@ -9,17 +9,14 @@ signal chart_plotted(chart) # emit when a chart is plotted (static) or updated (
|
||||
signal point_pressed(point)
|
||||
|
||||
# Onready Vars ............................
|
||||
onready var PointData = $PointData/PointData
|
||||
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("../Point/point.tscn")
|
||||
var LegendElement : PackedScene = preload("../Legend/function_legend.tscn")
|
||||
|
||||
# Enums .....................................
|
||||
enum PointShapes { Dot, Triangle, Square, Cross }
|
||||
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()
|
||||
@ -75,7 +72,7 @@ var legend : Array setget set_legend,get_legend
|
||||
|
||||
# ................... Export Shared Variables ..................
|
||||
export (String) var chart_name : String = "" setget set_chart_name
|
||||
export (String, FILE, "*.txt, *.csv") var source : String = "" setget set_source
|
||||
export (String, 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
|
||||
@ -97,7 +94,7 @@ 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 = [PointShapes.Dot] setget set_points_shape
|
||||
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
|
||||
@ -138,7 +135,7 @@ var label_displacement : int = 4 setget set_label_displacement # Separation betw
|
||||
|
||||
# !! API v2
|
||||
static func instance(chart_type : int):
|
||||
var chart_t : String = Utilities.get_chart_type(chart_type)
|
||||
var chart_t : String = ECUtilities.get_chart_type(chart_type)
|
||||
var chart : String = "res://addons/easy_charts/%s/%s.tscn" % [chart_t, chart_t]
|
||||
return load(chart).instance()
|
||||
|
||||
@ -303,7 +300,74 @@ func _set(property, value):
|
||||
return true
|
||||
|
||||
# .......................... Shared Functions and virtuals ........................
|
||||
func slice_data() -> Array:
|
||||
|
||||
# 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()
|
||||
load_font()
|
||||
data_tooltip.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
|
||||
|
||||
if _dataset.empty():
|
||||
ECUtilities._print_message("Can't plot a chart with an empty Array.",1)
|
||||
return
|
||||
|
||||
data = _dataset.duplicate(true)
|
||||
structure_data(slice_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:
|
||||
plot(read_data(file, _delimiter))
|
||||
|
||||
func plot_from_dataframe(dataframe : DataFrame) -> void:
|
||||
plot(dataframe.get_dataframe())
|
||||
|
||||
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 new_data.empty():
|
||||
ECUtilities._print_message("Can't plot a chart with an empty Array.",1)
|
||||
return
|
||||
|
||||
data.append(new_data.duplicate(true))
|
||||
plot(data)
|
||||
|
||||
# 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):
|
||||
var file : File = File.new()
|
||||
file.open(source,File.READ)
|
||||
var content : Array
|
||||
while not file.eof_reached():
|
||||
var line : PoolStringArray = file.get_csv_line(_delimiter)
|
||||
content.append(line)
|
||||
file.close()
|
||||
for data in content:
|
||||
if data.size() < 2 or data.empty():
|
||||
content.erase(data)
|
||||
return content.duplicate(true)
|
||||
|
||||
func slice_data(data: Array) -> Array:
|
||||
var data_to_display : Array
|
||||
data_to_display.resize(data.size())
|
||||
if only_disp_values == Vector2(0,0) :
|
||||
@ -321,122 +385,23 @@ func slice_data() -> Array:
|
||||
data_to_display = data.duplicate(true)
|
||||
return data_to_display
|
||||
|
||||
func plot():
|
||||
load_font()
|
||||
PointData.hide()
|
||||
|
||||
if source == "" or source == null:
|
||||
Utilities._print_message("Can't plot a chart without a Source file. Please, choose it in editor, or use the custom function _plot().",1)
|
||||
return
|
||||
|
||||
|
||||
data = read_datas(source)
|
||||
structure_datas(slice_data())
|
||||
# ................................. Display and Draw functions .......................
|
||||
func compute_display():
|
||||
count_functions()
|
||||
calculate_colors()
|
||||
set_shapes()
|
||||
create_legend()
|
||||
|
||||
func display_plot():
|
||||
build_chart()
|
||||
count_functions()
|
||||
calculate_pass()
|
||||
calculate_colors()
|
||||
calculate_coordinates()
|
||||
set_shapes()
|
||||
create_legend()
|
||||
emit_signal("chart_plotted",self)
|
||||
|
||||
func plot_from_csv(csv_file : String, _delimiter : String = delimiter):
|
||||
clean_variables()
|
||||
clear_points()
|
||||
load_font()
|
||||
PointData.hide()
|
||||
|
||||
if csv_file == "" or csv_file == null:
|
||||
Utilities._print_message("Can't plot a chart without a Source file. Please, choose it in editor, or use the custom function _plot().",1)
|
||||
return
|
||||
|
||||
data = read_datas(csv_file, _delimiter)
|
||||
structure_datas(slice_data())
|
||||
build_chart()
|
||||
count_functions()
|
||||
calculate_pass()
|
||||
calculate_colors()
|
||||
calculate_coordinates()
|
||||
set_shapes()
|
||||
create_legend()
|
||||
emit_signal("chart_plotted",self)
|
||||
func redraw_plot():
|
||||
clean_points()
|
||||
display_plot()
|
||||
|
||||
func plot_from_array(array : Array) -> void:
|
||||
clean_variables()
|
||||
clear_points()
|
||||
load_font()
|
||||
PointData.hide()
|
||||
|
||||
if array.empty():
|
||||
Utilities._print_message("Can't plot a chart with an empty Array.",1)
|
||||
return
|
||||
|
||||
data = array.duplicate(true)
|
||||
structure_datas(slice_data())
|
||||
build_chart()
|
||||
count_functions()
|
||||
calculate_pass()
|
||||
calculate_colors()
|
||||
calculate_coordinates()
|
||||
set_shapes()
|
||||
create_legend()
|
||||
emit_signal("chart_plotted",self)
|
||||
|
||||
if not is_connected("item_rect_changed",self, "redraw"): connect("item_rect_changed", self, "redraw")
|
||||
|
||||
func plot_from_dataframe(dataframe : DataFrame) -> void:
|
||||
clean_variables()
|
||||
clear_points()
|
||||
load_font()
|
||||
PointData.hide()
|
||||
|
||||
data = dataframe.get_dataframe().duplicate(true)
|
||||
|
||||
if data.empty():
|
||||
Utilities._print_message("Can't plot a chart with an empty Array.",1)
|
||||
return
|
||||
|
||||
structure_datas(slice_data())
|
||||
build_chart()
|
||||
count_functions()
|
||||
calculate_pass()
|
||||
calculate_colors()
|
||||
calculate_coordinates()
|
||||
set_shapes()
|
||||
create_legend()
|
||||
emit_signal("chart_plotted",self)
|
||||
|
||||
if not is_connected("item_rect_changed",self, "redraw"): connect("item_rect_changed", self, "redraw")
|
||||
|
||||
# Append new data (in array format) to the already plotted data.
|
||||
# All data are stored.
|
||||
func update_plot_data(array : Array) -> void:
|
||||
if array.empty():
|
||||
Utilities._print_message("Can't plot a chart with an empty Array.",1)
|
||||
return
|
||||
|
||||
data.append(array)
|
||||
structure_datas(slice_data())
|
||||
redraw()
|
||||
count_functions()
|
||||
calculate_colors()
|
||||
set_shapes()
|
||||
create_legend()
|
||||
emit_signal("chart_plotted",self)
|
||||
|
||||
update()
|
||||
|
||||
# Append a new column to data
|
||||
func append_new_column(dataset : Array, column : Array):
|
||||
if column.empty():
|
||||
Utilities._print_message("Can't update plot with an empty row.",1)
|
||||
return
|
||||
for value_idx in column.size():
|
||||
dataset[value_idx].append(column[value_idx])
|
||||
|
||||
func plot_placeholder() -> void:
|
||||
pass
|
||||
# ................................. Helper Functions .................................
|
||||
|
||||
func load_font():
|
||||
if font != null:
|
||||
@ -449,7 +414,13 @@ func load_font():
|
||||
font = lbl.get_font("")
|
||||
lbl.free()
|
||||
if bold_font != null:
|
||||
PointData.Data.set("custom_fonts/font",bold_font)
|
||||
data_tooltip.Data.set("custom_fonts/font", bold_font)
|
||||
else:
|
||||
bold_font = font
|
||||
|
||||
func count_functions():
|
||||
if are_values_columns: functions = data[0].size()-1
|
||||
else: functions = y_datas.size()
|
||||
|
||||
func calculate_colors():
|
||||
if function_colors.size() < functions:
|
||||
@ -458,37 +429,31 @@ func calculate_colors():
|
||||
func set_shapes():
|
||||
if points_shape.empty() or points_shape.size() < functions:
|
||||
for function in functions:
|
||||
points_shape.append(PointShapes.Dot)
|
||||
points_shape.append(Point.SHAPES.Dot)
|
||||
|
||||
func read_datas(source : String, _delimiter : String = delimiter):
|
||||
var file : File = File.new()
|
||||
file.open(source,File.READ)
|
||||
var content : Array
|
||||
while not file.eof_reached():
|
||||
var line : PoolStringArray = file.get_csv_line(_delimiter)
|
||||
content.append(line)
|
||||
file.close()
|
||||
for data in content:
|
||||
if data.size() < 2 or data.empty():
|
||||
content.erase(data)
|
||||
return content
|
||||
# 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 not are_values_columns else str(x_datas[function])
|
||||
var legend_font : Font
|
||||
if font != null:
|
||||
legend_font = font
|
||||
if bold_font != null:
|
||||
legend_font = bold_font
|
||||
function_legend.create_legend(f_name,function_colors[function],bold_font,font_color)
|
||||
|
||||
func count_functions():
|
||||
if are_values_columns: functions = data[0].size()-1
|
||||
else: functions = y_datas.size()
|
||||
|
||||
func clear_points():
|
||||
func clean_points():
|
||||
for function in Points.get_children():
|
||||
function.free()
|
||||
for legend in Legend.get_children():
|
||||
legend.free()
|
||||
|
||||
func redraw():
|
||||
build_chart()
|
||||
calculate_pass()
|
||||
calculate_coordinates()
|
||||
update()
|
||||
|
||||
func clean_variables():
|
||||
x_chors.clear()
|
||||
y_chors.clear()
|
||||
@ -499,37 +464,29 @@ func clean_variables():
|
||||
y_labels.clear()
|
||||
|
||||
# .................. VIRTUAL FUNCTIONS .........................
|
||||
func structure_datas(database : Array):
|
||||
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
|
||||
|
||||
func create_legend():
|
||||
for function in functions:
|
||||
var function_legend : LegendElement
|
||||
if legend.size() > function:
|
||||
function_legend = legend[function]
|
||||
else:
|
||||
function_legend = LegendElement.instance()
|
||||
legend.append(function_legend)
|
||||
var f_name : String = y_labels[function] if not are_values_columns else str(x_datas[function])
|
||||
var legend_font : Font
|
||||
if font != null:
|
||||
legend_font = font
|
||||
if bold_font != null:
|
||||
legend_font = bold_font
|
||||
function_legend.create_legend(f_name,function_colors[function],bold_font,font_color)
|
||||
|
||||
# ........................... Shared Setters & Getters ..............................
|
||||
func apply_template(template_name : int):
|
||||
if Engine.editor_hint:
|
||||
@ -683,7 +640,7 @@ func set_template(template_name : int):
|
||||
if not use_template: return
|
||||
template = template_name
|
||||
if template_name!=null:
|
||||
var custom_template = Utilities.templates.get(Utilities.templates.keys()[template_name])
|
||||
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)
|
||||
@ -720,12 +677,12 @@ func point_pressed(point : Point):
|
||||
emit_signal("point_pressed",point)
|
||||
|
||||
func show_data(point : Point):
|
||||
PointData.update_datas(point)
|
||||
PointData.show()
|
||||
data_tooltip.update_datas(point)
|
||||
data_tooltip.show()
|
||||
|
||||
func hide_data():
|
||||
PointData.hide()
|
||||
data_tooltip.hide()
|
||||
|
||||
func show_slice_data(slice : Slice):
|
||||
PointData.update_slice_datas(slice)
|
||||
PointData.show()
|
||||
data_tooltip.update_slice_datas(slice)
|
||||
data_tooltip.show()
|
@ -5,7 +5,6 @@ class_name Chart2D
|
||||
enum PointShapes { Dot, Triangle, Square, Cross }
|
||||
enum TemplatesNames { Default, Clean, Gradient, Minimal, Invert }
|
||||
|
||||
|
||||
signal chart_plotted(chart)
|
||||
signal point_pressed(point)
|
||||
|
||||
@ -47,12 +46,12 @@ var FunctionsTween: Tween
|
||||
var PointTween : Tween
|
||||
var Functions: Node2D
|
||||
var GridTween: Tween
|
||||
var PointData: PointData
|
||||
var DataTooltip: DataTooltip
|
||||
var Outlines: Line2D
|
||||
var Grid: Node2D
|
||||
|
||||
var point_node: PackedScene = preload("../Point/point.tscn")
|
||||
var FunctionLegend: PackedScene = preload("../Legend/function_legend.tscn")
|
||||
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
|
||||
@ -129,13 +128,13 @@ func _get_children():
|
||||
FunctionsTween = $FunctionsTween
|
||||
Functions = $Functions
|
||||
GridTween = $GridTween
|
||||
PointData = $PointData/PointData
|
||||
DataTooltip = $DataTooltip/DataTooltip
|
||||
Outlines = $Outlines
|
||||
Grid = $Grid
|
||||
|
||||
func apply_template(template_name: int):
|
||||
template = template_name
|
||||
templates = Utilities._load_templates()
|
||||
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
|
||||
@ -144,7 +143,6 @@ func apply_template(template_name: int):
|
||||
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)
|
||||
@ -159,15 +157,15 @@ func redraw():
|
||||
|
||||
|
||||
func show_data(point):
|
||||
PointData.update_datas(point)
|
||||
PointData.show()
|
||||
DataTooltip.update_datas(point)
|
||||
DataTooltip.show()
|
||||
|
||||
|
||||
func hide_data():
|
||||
PointData.hide()
|
||||
DataTooltip.hide()
|
||||
|
||||
|
||||
func clear_points():
|
||||
func clean_points():
|
||||
function_colors.clear()
|
||||
if Functions.get_children():
|
||||
for function in Functions.get_children():
|
@ -9,13 +9,13 @@ class_name ScatterChartBase
|
||||
var x_domain := [[], []]
|
||||
var y_domain := [[], []]
|
||||
|
||||
var x_range := [0, 0]
|
||||
var y_range := [0, 0]
|
||||
var x_range : PoolRealArray = [0, 0]
|
||||
var y_range : PoolRealArray = [0, 0]
|
||||
var autoscale_x = true
|
||||
var autoscale_y = true
|
||||
|
||||
|
||||
var property_list = []
|
||||
var property_list: Array = []
|
||||
|
||||
|
||||
func _init():
|
||||
@ -125,7 +125,7 @@ func build_property_list():
|
||||
"hint": 24,
|
||||
"hint_string": ("%d/%d:%s"
|
||||
%[TYPE_INT, PROPERTY_HINT_ENUM,
|
||||
PoolStringArray(PointShapes.keys()).join(",")]),
|
||||
PoolStringArray(Point.SHAPES.keys()).join(",")]),
|
||||
"name": "Chart_Style/points_shape",
|
||||
"type": TYPE_ARRAY,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE
|
||||
@ -201,7 +201,7 @@ func build_property_list():
|
||||
property_list.append(
|
||||
{
|
||||
"hint": PROPERTY_HINT_ENUM,
|
||||
"hint_string": PoolStringArray(Utilities.templates.keys()).join(","),
|
||||
"hint_string": PoolStringArray(ECUtilities.templates.keys()).join(","),
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Style/template",
|
||||
"type": TYPE_INT
|
||||
@ -272,37 +272,19 @@ func _get(property):
|
||||
"Chart_Display/max_y_range":
|
||||
return y_range[1]
|
||||
|
||||
|
||||
func plot():
|
||||
# Overwrites the method on Chart to make a reusable piece to be used internally
|
||||
# to do all calculations needed to replot.
|
||||
calculate_tics()
|
||||
build_chart()
|
||||
count_functions()
|
||||
calculate_pass()
|
||||
calculate_colors()
|
||||
calculate_coordinates()
|
||||
set_shapes()
|
||||
create_legend()
|
||||
emit_signal("chart_plotted",self)
|
||||
|
||||
if not is_connected("item_rect_changed",self, "redraw"): connect("item_rect_changed", self, "redraw")
|
||||
|
||||
|
||||
func plot_function(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
|
||||
|
||||
load_font()
|
||||
PointData.hide()
|
||||
data_tooltip.hide()
|
||||
var id := ""
|
||||
|
||||
if x.empty() or y.empty():
|
||||
Utilities._print_message("Can't plot a chart with an empty Array.",1)
|
||||
ECUtilities._print_message("Can't plot a chart with an empty Array.",1)
|
||||
return
|
||||
elif x.size() != y.size():
|
||||
Utilities._print_message("Can't plot a chart with x and y having different number of elements.",1)
|
||||
ECUtilities._print_message("Can't plot a chart with x and y having different number of elements.",1)
|
||||
return
|
||||
|
||||
for param in param_dic.keys():
|
||||
@ -318,7 +300,7 @@ func plot_function(x:Array, y:Array, param_dic := {}):
|
||||
id = generate_identifier() if id.empty() else id
|
||||
|
||||
if y_labels.has(id):
|
||||
Utilities._print_message("The identifier %s is already used. Please use a different one." % id,1)
|
||||
ECUtilities._print_message("The identifier %s is already used. Please use a different one." % id,1)
|
||||
return
|
||||
|
||||
y_domain[0].append(null)
|
||||
@ -338,7 +320,7 @@ func update_function(id:String, x:Array, y:Array, param_dic := {}):
|
||||
var function = y_labels.find(id)
|
||||
|
||||
if function == -1: #Not found
|
||||
Utilities._print_message("The identifier %s does not exist." % id,1)
|
||||
ECUtilities._print_message("The identifier %s does not exist." % id,1)
|
||||
return
|
||||
|
||||
for param in param_dic.keys():
|
||||
@ -360,7 +342,7 @@ func delete_function(id:String):
|
||||
var function = y_labels.find(id)
|
||||
|
||||
if function == -1: #Not found
|
||||
Utilities._print_message("The identifier %s does not exist." % id,1)
|
||||
ECUtilities._print_message("The identifier %s does not exist." % id,1)
|
||||
return
|
||||
|
||||
y_labels.remove(function)
|
||||
@ -381,7 +363,7 @@ func generate_identifier():
|
||||
return "f%d" % (y_labels.size() + 1)
|
||||
|
||||
|
||||
func structure_datas(database : Array):
|
||||
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
|
||||
|
||||
@ -540,12 +522,14 @@ func count_functions():
|
||||
functions = y_labels.size()
|
||||
|
||||
|
||||
# Calculate distance in pixel between 2 consecutive values/datas
|
||||
func calculate_pass():
|
||||
# Calculate distance in pixel between 2 consecutive values/datas
|
||||
x_pass = (SIZE.x - OFFSET.x) / (x_chors.size() - 1 if x_chors.size() > 1 else x_chors.size())
|
||||
y_pass = (origin.y - ChartName.get_rect().size.y * 2) / (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()
|
||||
@ -563,8 +547,7 @@ func calculate_coordinates():
|
||||
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():
|
||||
@ -590,13 +573,15 @@ func draw_grid():
|
||||
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()
|
||||
@ -615,7 +600,7 @@ func draw_points():
|
||||
|
||||
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))
|
190
addons/easy_charts/Utilities/classes/structures/data_frame.gd
Normal file
190
addons/easy_charts/Utilities/classes/structures/data_frame.gd
Normal file
@ -0,0 +1,190 @@
|
||||
tool
|
||||
extends Resource
|
||||
class_name DataFrame
|
||||
|
||||
var table_name : String = ""
|
||||
var labels : PoolStringArray = []
|
||||
var headers : PoolStringArray = []
|
||||
var datamatrix : Matrix = null
|
||||
var dataset : Array = []
|
||||
|
||||
func _init(datamatrix : Matrix, headers : PoolStringArray = [], labels : PoolStringArray = [] , table_name : String = "") -> void:
|
||||
if labels.empty() : for label in range(datamatrix.get_size().x) : labels.append(label as String)
|
||||
if headers.empty() : for header in range(datamatrix.get_size().y) : headers.append(ECUtilities.get_letter_index(header))
|
||||
build_dataframe(datamatrix, headers, labels, table_name)
|
||||
|
||||
func build_dataframe(datamatrix : Matrix, headers : PoolStringArray = [], labels : PoolStringArray = [] , table_name : String = "") -> void:
|
||||
self.datamatrix = datamatrix
|
||||
self.table_name = table_name
|
||||
self.labels = labels
|
||||
self.headers = headers
|
||||
self.dataset = build_dataset_from_matrix(datamatrix, headers, labels)
|
||||
|
||||
func build_dataset(data : Array, headers : PoolStringArray, labels : PoolStringArray) -> Array:
|
||||
var dataset : Array = [Array(headers)]
|
||||
for row_i in range(data.size()): dataset.append([labels[row_i]]+data[row_i])
|
||||
return dataset
|
||||
|
||||
func build_dataset_from_matrix(datamatrix : Matrix, headers : PoolStringArray, labels : PoolStringArray) -> Array:
|
||||
var data : Array = datamatrix.to_array()
|
||||
return build_dataset(data, headers, labels)
|
||||
|
||||
func insert_column(column : Array, header : String = "", index : int = dataset[0].size()) -> void:
|
||||
assert(column.size() == datamatrix.rows(), "error: the column size must match the dataset column size")
|
||||
headers.insert(index, header if header != "" else ECUtilities.get_letter_index(index))
|
||||
datamatrix.insert_column(column, index-1)
|
||||
dataset = build_dataset_from_matrix(datamatrix, headers, labels)
|
||||
|
||||
func insert_row(row : Array, label : String = "", index : int = dataset.size()) -> void:
|
||||
assert(row.size() == datamatrix.columns(), "error: the row size must match the dataset row size")
|
||||
labels.insert(index-1, label if label != "" else (index-1) as String)
|
||||
datamatrix.insert_row(row, index-1)
|
||||
dataset = build_dataset_from_matrix(datamatrix, headers, labels)
|
||||
|
||||
func get_datamatrix() -> Matrix:
|
||||
return datamatrix
|
||||
|
||||
func get_dataset() -> Array:
|
||||
return dataset
|
||||
|
||||
func get_labels() -> PoolStringArray:
|
||||
return labels
|
||||
|
||||
func transpose():
|
||||
build_dataframe(MatrixGenerator.transpose(datamatrix), labels, headers, table_name)
|
||||
|
||||
func _to_string() -> String:
|
||||
var last_string_len : int
|
||||
for row in dataset:
|
||||
for column in row:
|
||||
var string_len : int = str(column).length()
|
||||
last_string_len = string_len if string_len > last_string_len else last_string_len
|
||||
var string : String = ""
|
||||
for row_i in dataset.size():
|
||||
if row_i == 0:
|
||||
string+="%*s" % [last_string_len+1, ""]
|
||||
for column_i in dataset[row_i].size():
|
||||
string+="%*s" % [last_string_len+1, dataset[row_i][column_i]]
|
||||
string+="\n"
|
||||
string+="\n['{table_name}' : {rows} rows x {columns} columns]\n".format({
|
||||
rows = datamatrix.rows(),
|
||||
columns = datamatrix.columns(),
|
||||
table_name = table_name})
|
||||
return string
|
||||
|
||||
# ...............................................................................
|
||||
|
||||
# Return a list of headers corresponding to a list of indexes
|
||||
func get_headers_names(indexes : PoolIntArray) -> PoolStringArray:
|
||||
var headers : PoolStringArray = []
|
||||
for index in indexes:
|
||||
headers.append(dataset[0][index])
|
||||
return headers
|
||||
|
||||
# Returns the index of an header
|
||||
func get_column_index(header : String) -> int:
|
||||
for headers_ix in range(dataset[0].size()):
|
||||
if dataset[0][headers_ix] == header:
|
||||
return headers_ix
|
||||
return -1
|
||||
|
||||
# Get a column by its header
|
||||
func get_column(header : String) -> Array:
|
||||
var headers_i : int = get_column_index(header)
|
||||
if headers_i!=-1:
|
||||
return datamatrix.get_column(headers_i)
|
||||
else:
|
||||
return []
|
||||
|
||||
# Get a list of columns by their headers
|
||||
func columns(headers : PoolStringArray) -> Matrix:
|
||||
var values : Array = []
|
||||
for header in headers:
|
||||
values.append(get_column(header))
|
||||
return MatrixGenerator.transpose(Matrix.new(values))
|
||||
|
||||
|
||||
# Get a column by its index
|
||||
func get_icolumn(index : int) -> Array:
|
||||
return datamatrix.get_column(index)
|
||||
|
||||
# Get a list of columns by their indexes
|
||||
func get_icolumns(indexes : PoolIntArray) -> Array:
|
||||
var values : Array = []
|
||||
for index in indexes:
|
||||
values.append(datamatrix.get_column(index))
|
||||
return values
|
||||
|
||||
# Returns the list of labels corresponding to the list of indexes
|
||||
func get_labels_names(indexes : PoolIntArray) -> PoolStringArray:
|
||||
var headers : PoolStringArray = []
|
||||
for index in indexes:
|
||||
headers.append(dataset[index][0])
|
||||
return headers
|
||||
|
||||
# Returns the index of a label
|
||||
func get_row_index(label : String) -> int:
|
||||
for row in dataset.size():
|
||||
if dataset[row][0] == label:
|
||||
return row
|
||||
return -1
|
||||
|
||||
# Get a row by its label
|
||||
func get_row(label : String) -> Array:
|
||||
var index : int = get_row_index(label)
|
||||
if index == -1 :
|
||||
return []
|
||||
else:
|
||||
return datamatrix.get_row(index)
|
||||
|
||||
# Get a list of rows by their labels
|
||||
func rows(labels : Array) -> Matrix:
|
||||
var values : Array = []
|
||||
for label in labels:
|
||||
values.append(get_row(label))
|
||||
return Matrix.new(values)
|
||||
|
||||
# Get a row by its index
|
||||
func get_irow(index : int) -> Array:
|
||||
return datamatrix.get_row(index)
|
||||
|
||||
# Get a list of rows by their indexes
|
||||
func get_irows(indexes : PoolIntArray) -> Array:
|
||||
var values : Array = []
|
||||
for index in indexes:
|
||||
values.append(datamatrix.get_row(index))
|
||||
return values
|
||||
|
||||
# Returns a a group of rows or a group of columns, using indexes or names
|
||||
# dataset["0;5"] ---> Returns an array containing all rows from the 1st to the 4th
|
||||
# dataset["0:5"] ---> Returns an array containing all columns from the 1st to the 4th
|
||||
# dataset["label0;label5"] ---> Returns an array containing all row from the one with label == "label0" to the one with label == "label5"
|
||||
# dataset["header0:header0"] ---> Returns an array containing all columns from the one with label == "label0" to the one with label == "label5"
|
||||
func _get(_property : String):
|
||||
# ":" --> Columns
|
||||
if ":" in _property:
|
||||
var property : PoolStringArray = _property.split(":")
|
||||
if (property[0]).is_valid_integer():
|
||||
if property[1] == "*":
|
||||
return get_icolumns(range(property[0] as int, headers.size()-1))
|
||||
else:
|
||||
return get_icolumns(range(property[0] as int, property[1] as int +1))
|
||||
else:
|
||||
if property[1] == "*":
|
||||
return get_icolumns(range(get_column_index(property[0]), headers.size()-1))
|
||||
else:
|
||||
return get_icolumns(range(get_column_index(property[0]), get_column_index(property[1])))
|
||||
# ";" --> Rows
|
||||
elif ";" in _property:
|
||||
var property : PoolStringArray = _property.split(";")
|
||||
if (property[0]).is_valid_integer():
|
||||
return get_irows(range(property[0] as int, property[1] as int + 1 ))
|
||||
else:
|
||||
return get_irows(range(get_row_index(property[0]), get_row_index(property[1])))
|
||||
elif "," in _property:
|
||||
var property : PoolStringArray = _property.split(",")
|
||||
else:
|
||||
if (_property as String).is_valid_integer():
|
||||
return get_icolumn(_property as int)
|
||||
else:
|
||||
return get_column(_property)
|
85
addons/easy_charts/Utilities/classes/structures/matrix.gd
Normal file
85
addons/easy_charts/Utilities/classes/structures/matrix.gd
Normal file
@ -0,0 +1,85 @@
|
||||
tool
|
||||
extends Resource
|
||||
class_name Matrix
|
||||
|
||||
var values : Array = []
|
||||
|
||||
func _init(matrix : Array = []) -> void:
|
||||
values = matrix
|
||||
|
||||
func insert_row(row : Array, index : int = values.size()) -> void:
|
||||
assert(row.size() == values[0].size(), "the row size must match matrix row size")
|
||||
values.insert(index, row)
|
||||
|
||||
func insert_column(column : Array, index : int = values[0].size()) -> void:
|
||||
assert(column.size() == values.size(), "the column size must match matrix column size")
|
||||
for row_idx in column.size():
|
||||
values[row_idx].insert(index, column[row_idx])
|
||||
|
||||
func to_array() -> Array:
|
||||
return values.duplicate(true)
|
||||
|
||||
func get_size() -> Vector2:
|
||||
return Vector2(values.size(), values[0].size())
|
||||
|
||||
func rows() -> int:
|
||||
return values.size()
|
||||
|
||||
func columns() -> int:
|
||||
return values[0].size()
|
||||
|
||||
func get_column(column : int) -> Array:
|
||||
assert(column < columns(), "index of the column requested (%s) exceedes matrix columns (%s)"%[column, columns()])
|
||||
var column_array : Array = []
|
||||
for row in values:
|
||||
column_array.append(row[column])
|
||||
return column_array
|
||||
|
||||
func get_columns(from : int, to : int) -> Array:
|
||||
var values : Array = []
|
||||
for column in range(from, to):
|
||||
values.append(get_column(column))
|
||||
return values
|
||||
# return MatrixGenerator.from_array(values)
|
||||
|
||||
func get_row(row : int) -> Array:
|
||||
assert(row < rows(), "index of the row requested (%s) exceedes matrix rows (%s)"%[row, rows()])
|
||||
return values[row]
|
||||
|
||||
func get_rows(from : int, to : int) -> Array:
|
||||
return values.slice(from, to)
|
||||
# return MatrixGenerator.from_array(values)
|
||||
|
||||
func _to_string() -> String:
|
||||
var last_string_len : int
|
||||
for row in values:
|
||||
for column in row:
|
||||
var string_len : int = str(column).length()
|
||||
last_string_len = string_len if string_len > last_string_len else last_string_len
|
||||
var string : String = "\n"
|
||||
for row_i in values.size():
|
||||
for column_i in values[row_i].size():
|
||||
string+="%*s" % [last_string_len+1 if column_i!=0 else last_string_len, values[row_i][column_i]]
|
||||
string+="\n"
|
||||
return string
|
||||
|
||||
# --------------
|
||||
func _get(_property : String):
|
||||
# ":" --> Columns
|
||||
if ":" in _property:
|
||||
var property : PoolStringArray = _property.split(":")
|
||||
var from : PoolStringArray = property[0].split(",")
|
||||
var to : PoolStringArray = property[1].split(",")
|
||||
elif "," in _property:
|
||||
var property : PoolStringArray = _property.split(",")
|
||||
if property.size() == 2:
|
||||
return get_row(property[0] as int)[property[1] as int]
|
||||
else:
|
||||
if (_property as String).is_valid_integer():
|
||||
return get_row(_property as int)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -3,7 +3,7 @@ extends Reference
|
||||
class_name MatrixGenerator
|
||||
|
||||
# Generates a Matrix with random values between [from; to] with a given @size (rows, columns)
|
||||
static func random_float_range(size : Vector2, from : float, to : float, _seed : int = 1234) -> Matrix:
|
||||
static func random_float_range(from : float, to : float, size : Vector2, _seed : int = 1234) -> Matrix:
|
||||
seed(_seed)
|
||||
randomize()
|
||||
var array : Array = []
|
||||
@ -20,12 +20,13 @@ static func from_array(array : Array = []) -> Matrix:
|
||||
return Matrix.new(matrix)
|
||||
|
||||
# Generates a sub-Matrix giving a Matrix, a @from Array [row_i, column_i] and a @to Array [row_j, column_j]
|
||||
static func sub_matrix(_matrix : Matrix, from : Array, to : Array) -> Matrix:
|
||||
if to[0] > _matrix.get_size().x or to[1] > _matrix.get_size().y:
|
||||
printerr("%s is not an acceptable size for the submatrix, giving a matrix of size %s"%[to, _matrix.get_size()])
|
||||
return Matrix.new()
|
||||
static func sub_matrix(_matrix : Matrix, from : PoolIntArray, to : PoolIntArray) -> Matrix:
|
||||
assert( not (to[0] > _matrix.rows() or to[1] > _matrix.columns()),
|
||||
"%s is not an acceptable size for the submatrix, giving a matrix of size %s"%[to, _matrix.get_size()])
|
||||
var array : Array = []
|
||||
for rows_i in range(from[0],to[0]): array.append(_matrix.to_array()[rows_i].slice(from[1], to[1]))
|
||||
var rows : Array = _matrix.get_rows(from[0], to[0])
|
||||
for row in rows:
|
||||
array.append(row.slice(from[1], to[1]))
|
||||
return Matrix.new(array)
|
||||
|
||||
# Duplicates a given Matrix
|
||||
@ -70,6 +71,7 @@ static func hadamard(_matrix1 : Matrix, _matrix2 : Matrix) -> Matrix:
|
||||
for x in range(_matrix1.to_array().size()):
|
||||
var row : Array = []
|
||||
for y in range(_matrix1.to_array()[x].size()):
|
||||
assert(typeof(_matrix1.to_array()[x][y]) != TYPE_STRING and typeof(_matrix2.to_array()[x][y]) != TYPE_STRING, "can't apply operations over a Matrix of Strings")
|
||||
row.append(_matrix1.to_array()[x][y] * _matrix2.to_array()[x][y])
|
||||
array.append(row)
|
||||
return Matrix.new(array)
|
||||
@ -90,6 +92,3 @@ static func multiply_float(_matrix1 : Matrix, _float : float) -> Matrix:
|
||||
for y in range(_matrix1.to_array()[x].size()):
|
||||
array[x][y]*=_float
|
||||
return Matrix.new(array)
|
||||
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
[gd_scene load_steps=2 format=2]
|
||||
|
||||
[ext_resource path="res://addons/easy_charts/Utilities/Point/point.gd" type="Script" id=1]
|
||||
|
||||
[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
|
||||
@ -13,6 +12,7 @@ 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"]
|
@ -1,11 +1,12 @@
|
||||
[gd_scene load_steps=2 format=2]
|
||||
|
||||
[ext_resource path="res://addons/easy_charts/Utilities/Rect/Rect.gd" type="Script" id=1]
|
||||
[ext_resource path="res://addons/easy_charts/utilities/components/rect/rect.gd" type="Script" id=1]
|
||||
|
||||
[node name="Rect" type="Control"]
|
||||
script = ExtResource( 1 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[connection signal="mouse_entered" from="." to="." method="_on_Rect_mouse_entered"]
|
||||
[connection signal="mouse_exited" from="." to="." method="_on_Rect_mouse_exited"]
|
@ -1,6 +1,6 @@
|
||||
[gd_scene load_steps=2 format=2]
|
||||
|
||||
[ext_resource path="res://addons/easy_charts/Utilities/Legend/function_legend.gd" type="Script" id=1]
|
||||
[ext_resource path="res://addons/easy_charts/utilities/containers/legend/function_legend.gd" type="Script" id=1]
|
||||
|
||||
[node name="FunctionLegend" type="VBoxContainer"]
|
||||
margin_right = 80.0
|
||||
@ -15,8 +15,7 @@ __meta__ = {
|
||||
margin_top = 2.0
|
||||
margin_right = 80.0
|
||||
margin_bottom = 16.0
|
||||
custom_colors/font_color = Color( 0.184314, 0.192157, 0.262745, 1 )
|
||||
text = "Function"
|
||||
custom_colors/font_color = Color( 0, 0, 0, 1 )
|
||||
align = 1
|
||||
valign = 1
|
||||
|
||||
@ -25,4 +24,4 @@ margin_top = 20.0
|
||||
margin_right = 80.0
|
||||
margin_bottom = 23.0
|
||||
rect_min_size = Vector2( 15, 3 )
|
||||
color = Color( 0.184314, 0.192157, 0.262745, 1 )
|
||||
color = Color( 0, 0, 0, 1 )
|
@ -2,15 +2,15 @@
|
||||
|
||||
importer="texture"
|
||||
type="StreamTexture"
|
||||
path="res://.import/linechart.svg-1b22ec0a8537b638d716ea5c462d4141.stex"
|
||||
path="res://.import/linechart.svg-922834f0462a2c88be644081c47c63ad.stex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/easy_charts/Utilities/icons/linechart.svg"
|
||||
dest_files=[ "res://.import/linechart.svg-1b22ec0a8537b638d716ea5c462d4141.stex" ]
|
||||
source_file="res://addons/easy_charts/utilities/icons/linechart.svg"
|
||||
dest_files=[ "res://.import/linechart.svg-922834f0462a2c88be644081c47c63ad.stex" ]
|
||||
|
||||
[params]
|
||||
|
||||
@ -28,6 +28,7 @@ process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/HDR_as_SRGB=false
|
||||
process/invert_color=false
|
||||
process/normal_map_invert_y=false
|
||||
stream=false
|
||||
size_limit=0
|
||||
detect_3d=false
|
||||
|
@ -2,15 +2,15 @@
|
||||
|
||||
importer="texture"
|
||||
type="StreamTexture"
|
||||
path="res://.import/linechart2d.svg-af92a4d6767d218934fb7a6905401722.stex"
|
||||
path="res://.import/linechart2d.svg-1067b05eddcc451c2fc80a8734aa8056.stex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/easy_charts/Utilities/icons/linechart2d.svg"
|
||||
dest_files=[ "res://.import/linechart2d.svg-af92a4d6767d218934fb7a6905401722.stex" ]
|
||||
source_file="res://addons/easy_charts/utilities/icons/linechart2d.svg"
|
||||
dest_files=[ "res://.import/linechart2d.svg-1067b05eddcc451c2fc80a8734aa8056.stex" ]
|
||||
|
||||
[params]
|
||||
|
||||
@ -28,6 +28,7 @@ process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/HDR_as_SRGB=false
|
||||
process/invert_color=false
|
||||
process/normal_map_invert_y=false
|
||||
stream=false
|
||||
size_limit=0
|
||||
detect_3d=true
|
||||
|
@ -1,6 +1,8 @@
|
||||
tool
|
||||
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 = {
|
||||
@ -14,8 +16,6 @@ var chart_types : Dictionary = {
|
||||
func _ready():
|
||||
templates = _load_templates()
|
||||
|
||||
# _print_message("Templates loaded")
|
||||
|
||||
func _print_message(message : String, type : int = 0):
|
||||
match type:
|
||||
0:
|
||||
@ -35,3 +35,6 @@ func get_template(template_index : int):
|
||||
|
||||
func get_chart_type(chart_type : int):
|
||||
return chart_types.get(chart_types.keys()[chart_type])
|
||||
|
||||
func get_letter_index(index : int) -> String:
|
||||
return alphabet.split(" ")[index]
|
@ -20,147 +20,147 @@ values of more than one measured variable.
|
||||
# ---------------------
|
||||
|
||||
func _get_property_list():
|
||||
return [
|
||||
# Chart Properties
|
||||
{
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_CATEGORY,
|
||||
"name": "ColumnChart",
|
||||
"type": TYPE_STRING
|
||||
},
|
||||
{
|
||||
"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
|
||||
},
|
||||
{
|
||||
"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
|
||||
},
|
||||
{
|
||||
"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
|
||||
},
|
||||
|
||||
# Chart Display
|
||||
{
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "0.1,10",
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Display/x_decim",
|
||||
"type": TYPE_REAL
|
||||
},
|
||||
{
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "0.001,1,0.001",
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Display/y_decim",
|
||||
"type": TYPE_REAL
|
||||
},
|
||||
|
||||
# Chart Style
|
||||
{
|
||||
"hint": 24,
|
||||
"hint_string": "%d/%d:%s"%[TYPE_INT, PROPERTY_HINT_ENUM,
|
||||
PoolStringArray(PointShapes.keys()).join(",")],
|
||||
"name": "Chart_Style/points_shape",
|
||||
"type": TYPE_ARRAY,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE
|
||||
},
|
||||
{
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Style/function_colors",
|
||||
"type": TYPE_COLOR_ARRAY
|
||||
},
|
||||
{
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Style/box_color",
|
||||
"type": TYPE_COLOR
|
||||
},
|
||||
{
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Style/v_lines_color",
|
||||
"type": TYPE_COLOR
|
||||
},
|
||||
{
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Style/h_lines_color",
|
||||
"type": TYPE_COLOR
|
||||
},
|
||||
{
|
||||
"class_name": "Font",
|
||||
"hint": PROPERTY_HINT_RESOURCE_TYPE,
|
||||
"hint_string": "Font",
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Style/font",
|
||||
"type": TYPE_OBJECT
|
||||
},
|
||||
{
|
||||
"class_name": "Font",
|
||||
"hint": PROPERTY_HINT_RESOURCE_TYPE,
|
||||
"hint_string": "Font",
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Style/bold_font",
|
||||
"type": TYPE_OBJECT
|
||||
},
|
||||
{
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Style/font_color",
|
||||
"type": TYPE_COLOR
|
||||
},
|
||||
{
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Style/use_template",
|
||||
"type": TYPE_BOOL
|
||||
},
|
||||
{
|
||||
"hint": PROPERTY_HINT_ENUM,
|
||||
"hint_string": PoolStringArray(Utilities.templates.keys()).join(","),
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Style/template",
|
||||
"type": TYPE_INT
|
||||
},
|
||||
|
||||
# Chart Modifiers
|
||||
{
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Modifiers/invert_chart",
|
||||
"type": TYPE_BOOL
|
||||
},
|
||||
]
|
||||
return [
|
||||
# Chart Properties
|
||||
{
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_CATEGORY,
|
||||
"name": "ColumnChart",
|
||||
"type": TYPE_STRING
|
||||
},
|
||||
{
|
||||
"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
|
||||
},
|
||||
{
|
||||
"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
|
||||
},
|
||||
{
|
||||
"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
|
||||
},
|
||||
|
||||
# Chart Display
|
||||
{
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "0.1,10",
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Display/x_decim",
|
||||
"type": TYPE_REAL
|
||||
},
|
||||
{
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "0.001,1,0.001",
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Display/y_decim",
|
||||
"type": TYPE_REAL
|
||||
},
|
||||
|
||||
# Chart Style
|
||||
{
|
||||
"hint": 24,
|
||||
"hint_string": "%d/%d:%s"%[TYPE_INT, PROPERTY_HINT_ENUM,
|
||||
PoolStringArray(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/box_color",
|
||||
"type": TYPE_COLOR
|
||||
},
|
||||
{
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Style/v_lines_color",
|
||||
"type": TYPE_COLOR
|
||||
},
|
||||
{
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Style/h_lines_color",
|
||||
"type": TYPE_COLOR
|
||||
},
|
||||
{
|
||||
"class_name": "Font",
|
||||
"hint": PROPERTY_HINT_RESOURCE_TYPE,
|
||||
"hint_string": "Font",
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Style/font",
|
||||
"type": TYPE_OBJECT
|
||||
},
|
||||
{
|
||||
"class_name": "Font",
|
||||
"hint": PROPERTY_HINT_RESOURCE_TYPE,
|
||||
"hint_string": "Font",
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Style/bold_font",
|
||||
"type": TYPE_OBJECT
|
||||
},
|
||||
{
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Style/font_color",
|
||||
"type": TYPE_COLOR
|
||||
},
|
||||
{
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Style/use_template",
|
||||
"type": TYPE_BOOL
|
||||
},
|
||||
{
|
||||
"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_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Modifiers/invert_chart",
|
||||
"type": TYPE_BOOL
|
||||
},
|
||||
]
|
||||
|
||||
func build_chart():
|
||||
SIZE = get_size()
|
||||
origin = Vector2(OFFSET.x,SIZE.y-OFFSET.y)
|
||||
|
||||
func structure_datas(database : Array):
|
||||
func structure_data(database : Array):
|
||||
# @labels_index can be either a column or a row relative to x values
|
||||
are_values_columns = (invert_chart != are_values_columns)
|
||||
if are_values_columns:
|
||||
@ -297,7 +297,7 @@ func calculate_coordinates():
|
||||
point_positions[cluster].append(Vector2(OFFSET.x/2 + column_width/2 + (column_width + column_gap)*cluster + x_coordinates[y]+origin.x,origin.y-y_coordinates[cluster][y]))
|
||||
|
||||
func _draw():
|
||||
clear_points()
|
||||
clean_points()
|
||||
|
||||
draw_grid()
|
||||
draw_chart_outlines()
|
@ -1,9 +1,15 @@
|
||||
[gd_scene load_steps=4 format=2]
|
||||
|
||||
<<<<<<<< HEAD:addons/easy_charts/ControlChart/ColumnChart/column_chart.tscn
|
||||
[ext_resource path="res://addons/easy_charts/Utilities/Point/point_data.tscn" type="PackedScene" id=1]
|
||||
[ext_resource path="res://addons/easy_charts/ColumnChart/column_chart.gd" type="Script" id=2]
|
||||
[ext_resource path="res://addons/easy_charts/ControlChart/ColumnChart/column_chart.gd" type="Script" id=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]
|
||||
>>>>>>>> dev:addons/easy_charts/control_charts/ColumnChart/column_chart.tscn
|
||||
|
||||
[sub_resource type="Theme" id=1]
|
||||
|
||||
[node name="ColumnChart" type="Control"]
|
||||
@ -80,13 +86,19 @@ __meta__ = {
|
||||
"_edit_use_anchors_": true
|
||||
}
|
||||
|
||||
[node name="PointData" parent="." instance=ExtResource( 1 )]
|
||||
[node name="CanvasLayer" type="CanvasLayer" parent="."]
|
||||
|
||||
[node name="PointData" parent="PointData" index="0"]
|
||||
[node name="DataTooltip" parent="CanvasLayer" instance=ExtResource( 1 )]
|
||||
margin_left = -455.833
|
||||
margin_top = 690.354
|
||||
margin_right = -455.969
|
||||
margin_bottom = 691.154
|
||||
|
||||
[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="PointData"]
|
||||
[editable path="CanvasLayer/DataTooltip"]
|
@ -70,7 +70,6 @@ func _set(property, value):
|
||||
|
||||
|
||||
func _draw():
|
||||
clear_points()
|
||||
draw_grid()
|
||||
draw_chart_outlines()
|
||||
if show_points:
|
@ -1,7 +1,7 @@
|
||||
[gd_scene load_steps=4 format=2]
|
||||
|
||||
[ext_resource path="res://addons/easy_charts/Utilities/Point/point_data.tscn" type="PackedScene" id=1]
|
||||
[ext_resource path="res://addons/easy_charts/LineChart/line_chart.gd" type="Script" id=4]
|
||||
[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]
|
||||
|
||||
@ -31,7 +31,7 @@ Chart_Display/autoscale_x = true
|
||||
Chart_Display/x_decim = 1.0
|
||||
Chart_Display/autoscale_y = true
|
||||
Chart_Display/y_decim = 1.0
|
||||
Chart_Style/points_shape = [ ]
|
||||
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 )
|
||||
@ -83,13 +83,19 @@ __meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="PointData" parent="." instance=ExtResource( 1 )]
|
||||
[node name="CanvasLayer" type="CanvasLayer" parent="."]
|
||||
|
||||
[node name="PointData" parent="PointData" index="0"]
|
||||
margin_left = -458.75
|
||||
margin_top = -164.504
|
||||
margin_right = -458.886
|
||||
margin_bottom = -163.704
|
||||
[node name="DataTooltip" parent="CanvasLayer" instance=ExtResource( 1 )]
|
||||
margin_left = -50.9019
|
||||
margin_top = -37.7724
|
||||
margin_right = -51.0379
|
||||
margin_bottom = -36.9724
|
||||
|
||||
[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="PointData"]
|
||||
[editable path="CanvasLayer/DataTooltip"]
|
@ -68,7 +68,7 @@ func _get_property_list():
|
||||
},
|
||||
{
|
||||
"hint": PROPERTY_HINT_ENUM,
|
||||
"hint_string": PoolStringArray(Utilities.templates.keys()).join(","),
|
||||
"hint_string": PoolStringArray(ECUtilities.templates.keys()).join(","),
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Style/template",
|
||||
"type": TYPE_INT
|
||||
@ -106,9 +106,9 @@ func plot_placeholder() -> void:
|
||||
Color.green,
|
||||
Color.blue
|
||||
]
|
||||
plot_from_array(data)
|
||||
plot(data)
|
||||
|
||||
func structure_datas(database: Array):
|
||||
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
|
@ -1,8 +1,14 @@
|
||||
[gd_scene load_steps=3 format=2]
|
||||
|
||||
[ext_resource path="res://addons/easy_charts/PieChart/pie_chart.gd" type="Script" id=1]
|
||||
<<<<<<<< HEAD:addons/easy_charts/ControlChart/PieChart/pie_chart.tscn
|
||||
[ext_resource path="res://addons/easy_charts/ControlChart/PieChart/pie_chart.gd" type="Script" id=1]
|
||||
[ext_resource path="res://addons/easy_charts/Utilities/Point/point_data.tscn" type="PackedScene" id=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]
|
||||
>>>>>>>> dev:addons/easy_charts/control_charts/PieChart/pie_chart.tscn
|
||||
|
||||
|
||||
[node name="PieChart" type="Control"]
|
||||
anchor_right = 1.0
|
||||
@ -15,7 +21,7 @@ __meta__ = {
|
||||
Chart_Properties/are_values_columns = false
|
||||
Chart_Properties/labels_index = 0
|
||||
Chart_Properties/show_x_values_as_labels = true
|
||||
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_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 )
|
||||
@ -55,12 +61,18 @@ __meta__ = {
|
||||
"_edit_use_anchors_": true
|
||||
}
|
||||
|
||||
[node name="PointData" parent="." instance=ExtResource( 2 )]
|
||||
[node name="CanvasLayer" type="CanvasLayer" parent="."]
|
||||
|
||||
[node name="PointData" parent="PointData" index="0"]
|
||||
[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="PointData"]
|
||||
[editable path="CanvasLayer/DataTooltip"]
|
286
addons/easy_charts/control_charts/RadarChart/radar_chart.gd
Normal file
286
addons/easy_charts/control_charts/RadarChart/radar_chart.gd
Normal file
@ -0,0 +1,286 @@
|
||||
tool
|
||||
extends Chart
|
||||
class_name RadarChart
|
||||
|
||||
"""
|
||||
[RadarChart] - General purpose node for Radar Charts
|
||||
A radar chart is a graphical method of displaying multivariate data in the form
|
||||
of a two-dimensional chart of three or more quantitative variables represented on axes
|
||||
starting from the same point. The relative position and angle of the axes is typically
|
||||
uninformative, but various heuristics, such as algorithms that plot data as the maximal
|
||||
total area, can be applied to sort the variables (axes) into relative positions that reveal
|
||||
distinct correlations, trade-offs, and a multitude of other comparative measures.
|
||||
|
||||
/ source : Wikipedia /
|
||||
"""
|
||||
|
||||
func _get_property_list():
|
||||
return [
|
||||
# Chart Properties
|
||||
{
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Properties/are_values_columns",
|
||||
"type": TYPE_BOOL
|
||||
},
|
||||
{
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "-1,100,1",
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Properties/labels_index",
|
||||
"type": TYPE_INT
|
||||
},
|
||||
{
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "-1,100,1",
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Properties/function_names_index",
|
||||
"type": TYPE_INT
|
||||
},
|
||||
{
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Properties/use_height_as_radius",
|
||||
"type": TYPE_BOOL
|
||||
},
|
||||
{
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "0,2000",
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Properties/radius",
|
||||
"type": TYPE_REAL
|
||||
},
|
||||
|
||||
# Chart Display
|
||||
{
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "0.1,100",
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Display/full_scale",
|
||||
"type": TYPE_REAL
|
||||
},
|
||||
|
||||
# Chart Style
|
||||
{
|
||||
"hint": 24,
|
||||
"hint_string": "%d/%d:%s"%[TYPE_INT, PROPERTY_HINT_ENUM,
|
||||
PoolStringArray(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
|
||||
|
||||
var radar_polygon : Array
|
||||
|
||||
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)
|
||||
|
||||
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()
|
@ -1,17 +1,23 @@
|
||||
[gd_scene load_steps=4 format=2]
|
||||
[gd_scene load_steps=3 format=2]
|
||||
|
||||
<<<<<<<< HEAD:addons/easy_charts/ControlChart/RadarChart/radar_chart.tscn
|
||||
[ext_resource path="res://addons/easy_charts/Utilities/Point/point_data.tscn" type="PackedScene" id=1]
|
||||
[ext_resource path="res://addons/easy_charts/RadarChart/radar_chart.gd" type="Script" id=2]
|
||||
[ext_resource path="res://addons/easy_charts/ControlChart/RadarChart/radar_chart.gd" type="Script" id=2]
|
||||
|
||||
|
||||
|
||||
[sub_resource type="Theme" id=1]
|
||||
========
|
||||
[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]
|
||||
>>>>>>>> dev:addons/easy_charts/control_charts/RadarChart/radar_chart.tscn
|
||||
|
||||
[node name="RadarChart" type="Control"]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
rect_min_size = Vector2( 70, 50 )
|
||||
mouse_filter = 2
|
||||
script = ExtResource( 2 )
|
||||
script = ExtResource( 1 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false,
|
||||
"_editor_description_": "[RadarChart] - General purpose node for Radar Charts
|
||||
@ -29,9 +35,9 @@ 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 = 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_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.792157, 0.792157, 0.792157, 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 )
|
||||
@ -74,14 +80,10 @@ __meta__ = {
|
||||
"_edit_use_anchors_": true
|
||||
}
|
||||
|
||||
[node name="PointData" parent="." instance=ExtResource( 1 )]
|
||||
[node name="CanvasLayer" type="CanvasLayer" parent="."]
|
||||
|
||||
[node name="PointData" parent="PointData" index="0"]
|
||||
visible = false
|
||||
margin_left = -448.644
|
||||
margin_top = 721.085
|
||||
margin_right = -448.781
|
||||
margin_bottom = 721.884
|
||||
theme = SubResource( 1 )
|
||||
|
||||
[editable path="PointData"]
|
||||
[node name="DataTooltip" parent="CanvasLayer" instance=ExtResource( 2 )]
|
||||
margin_left = -473.792
|
||||
margin_top = 853.111
|
||||
margin_right = -473.928
|
||||
margin_bottom = 853.911
|
@ -21,7 +21,7 @@ func _get_property_list():
|
||||
|
||||
|
||||
func _draw():
|
||||
clear_points()
|
||||
clean_points()
|
||||
draw_grid()
|
||||
draw_chart_outlines()
|
||||
draw_points()
|
@ -1,16 +1,21 @@
|
||||
[gd_scene load_steps=4 format=2]
|
||||
[gd_scene load_steps=3 format=2]
|
||||
|
||||
<<<<<<<< HEAD:addons/easy_charts/ControlChart/ScatterChart/scatter_chart.tscn
|
||||
[ext_resource path="res://addons/easy_charts/Utilities/Point/point_data.tscn" type="PackedScene" id=1]
|
||||
[ext_resource path="res://addons/easy_charts/ScatterChart/scatter_chart.gd" type="Script" id=2]
|
||||
[ext_resource path="res://addons/easy_charts/ControlChart/ScatterChart/scatter_chart.gd" type="Script" id=2]
|
||||
|
||||
[sub_resource type="Theme" id=1]
|
||||
========
|
||||
[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]
|
||||
>>>>>>>> dev:addons/easy_charts/control_charts/ScatterChart/scatter_chart.tscn
|
||||
|
||||
[node name="ScatterChart" type="Control"]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
rect_min_size = Vector2( 70, 50 )
|
||||
mouse_filter = 2
|
||||
script = ExtResource( 2 )
|
||||
script = ExtResource( 1 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false,
|
||||
"_editor_description_": "[ScatterChart] - General purpose node for Scatter Charts
|
||||
@ -21,6 +26,7 @@ for a set of data. If the points are coded (color/shape/size), one additional va
|
||||
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."
|
||||
}
|
||||
<<<<<<<< HEAD:addons/easy_charts/ControlChart/ScatterChart/scatter_chart.tscn
|
||||
Chart_Properties/are_values_columns = false
|
||||
Chart_Properties/labels_index = 0
|
||||
Chart_Properties/show_x_values_as_labels = false
|
||||
@ -42,6 +48,8 @@ Chart_Style/template = 0
|
||||
Chart_Modifiers/treshold = Vector2( 0, 0 )
|
||||
Chart_Modifiers/only_disp_values = Vector2( 0, 0 )
|
||||
Chart_Modifiers/invert_chart = false
|
||||
========
|
||||
>>>>>>>> dev:addons/easy_charts/control_charts/ScatterChart/scatter_chart.tscn
|
||||
|
||||
[node name="Background" type="ColorRect" parent="."]
|
||||
visible = false
|
||||
@ -78,8 +86,9 @@ __meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="PointData" parent="." instance=ExtResource( 1 )]
|
||||
[node name="CanvasLayer" type="CanvasLayer" parent="."]
|
||||
|
||||
<<<<<<<< HEAD:addons/easy_charts/ControlChart/ScatterChart/scatter_chart.tscn
|
||||
[node name="PointData" parent="PointData" index="0"]
|
||||
margin_left = -510.384
|
||||
margin_top = -148.79
|
||||
@ -88,3 +97,10 @@ margin_bottom = -147.99
|
||||
theme = SubResource( 1 )
|
||||
|
||||
[editable path="PointData"]
|
||||
========
|
||||
[node name="DataTooltip" parent="CanvasLayer" instance=ExtResource( 2 )]
|
||||
margin_left = 264.788
|
||||
margin_top = 446.78
|
||||
margin_right = 264.65
|
||||
margin_bottom = 447.58
|
||||
>>>>>>>> dev:addons/easy_charts/control_charts/ScatterChart/scatter_chart.tscn
|
@ -1,10 +0,0 @@
|
||||
[remap]
|
||||
|
||||
importer="csv"
|
||||
type="TextFile"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/easy_charts/file.samples/linechart (columns).csv"
|
||||
[params]
|
||||
|
@ -1,10 +0,0 @@
|
||||
[remap]
|
||||
|
||||
importer="csv"
|
||||
type="TextFile"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/easy_charts/file.samples/linechart (rows).csv"
|
||||
[params]
|
||||
|
@ -1,10 +0,0 @@
|
||||
[remap]
|
||||
|
||||
importer="csv"
|
||||
type="TextFile"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/easy_charts/file.samples/pie.csv"
|
||||
[params]
|
||||
|
@ -1,10 +0,0 @@
|
||||
[remap]
|
||||
|
||||
importer="csv"
|
||||
type="TextFile"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/easy_charts/file.samples/radar.csv"
|
||||
[params]
|
||||
|
@ -1,10 +0,0 @@
|
||||
[remap]
|
||||
|
||||
importer="csv"
|
||||
type="TextFile"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/easy_charts/file.samples/scatter (columns).csv"
|
||||
[params]
|
||||
|
@ -1,10 +0,0 @@
|
||||
[remap]
|
||||
|
||||
importer="csv"
|
||||
type="TextFile"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/easy_charts/file.samples/scatter.csv"
|
||||
[params]
|
||||
|
@ -28,6 +28,7 @@ process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/HDR_as_SRGB=false
|
||||
process/invert_color=false
|
||||
process/normal_map_invert_y=false
|
||||
stream=false
|
||||
size_limit=0
|
||||
detect_3d=true
|
||||
|
@ -2,11 +2,12 @@ tool
|
||||
extends EditorPlugin
|
||||
|
||||
func _enter_tree():
|
||||
add_autoload_singleton("Utilities","res://addons/easy_charts/Utilities/Scripts/utilities.gd")
|
||||
add_custom_type("Chart","Control", load("res://addons/easy_charts/Utilities/Scripts/chart.gd"), preload("Utilities/icons/linechart.svg"))
|
||||
add_custom_type("Chart2D","Node2D", load("res://addons/easy_charts/Utilities/Scripts/chart2d.gd"), preload("Utilities/icons/linechart2d.svg"))
|
||||
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("Utilities")
|
||||
remove_autoload_singleton("ECUtilities")
|
||||
|
||||
|
BIN
imgs/code_snapshot.gif
Normal file
BIN
imgs/code_snapshot.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 764 KiB |
BIN
imgs/screensho_2t.png
Normal file
BIN
imgs/screensho_2t.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
34
imgs/screensho_2t.png.import
Normal file
34
imgs/screensho_2t.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="StreamTexture"
|
||||
path="res://.import/screensho_2t.png-a3057a4df668383211639c7b4209d3cf.stex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://imgs/screensho_2t.png"
|
||||
dest_files=[ "res://.import/screensho_2t.png-a3057a4df668383211639c7b4209d3cf.stex" ]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_mode=0
|
||||
compress/bptc_ldr=0
|
||||
compress/normal_map=0
|
||||
flags/repeat=0
|
||||
flags/filter=true
|
||||
flags/mipmaps=false
|
||||
flags/anisotropic=false
|
||||
flags/srgb=2
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/HDR_as_SRGB=false
|
||||
process/invert_color=false
|
||||
stream=false
|
||||
size_limit=0
|
||||
detect_3d=true
|
||||
svg/scale=1.0
|
BIN
imgs/screenshot.png
Normal file
BIN
imgs/screenshot.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
34
imgs/screenshot.png.import
Normal file
34
imgs/screenshot.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="StreamTexture"
|
||||
path="res://.import/screenshot.png-836091f94baad9c66f52d1d068ff5e95.stex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://imgs/screenshot.png"
|
||||
dest_files=[ "res://.import/screenshot.png-836091f94baad9c66f52d1d068ff5e95.stex" ]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_mode=0
|
||||
compress/bptc_ldr=0
|
||||
compress/normal_map=0
|
||||
flags/repeat=0
|
||||
flags/filter=true
|
||||
flags/mipmaps=false
|
||||
flags/anisotropic=false
|
||||
flags/srgb=2
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/HDR_as_SRGB=false
|
||||
process/invert_color=false
|
||||
stream=false
|
||||
size_limit=0
|
||||
detect_3d=true
|
||||
svg/scale=1.0
|
BIN
imgs/screenshot_2.png
Normal file
BIN
imgs/screenshot_2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 37 KiB |
34
imgs/screenshot_2.png.import
Normal file
34
imgs/screenshot_2.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="StreamTexture"
|
||||
path="res://.import/screenshot_2.png-00ef4b140fda2b59dc0a00f84f140a14.stex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://imgs/screenshot_2.png"
|
||||
dest_files=[ "res://.import/screenshot_2.png-00ef4b140fda2b59dc0a00f84f140a14.stex" ]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_mode=0
|
||||
compress/bptc_ldr=0
|
||||
compress/normal_map=0
|
||||
flags/repeat=0
|
||||
flags/filter=true
|
||||
flags/mipmaps=false
|
||||
flags/anisotropic=false
|
||||
flags/srgb=2
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/HDR_as_SRGB=false
|
||||
process/invert_color=false
|
||||
stream=false
|
||||
size_limit=0
|
||||
detect_3d=true
|
||||
svg/scale=1.0
|
BIN
imgs/screenshot_3.png
Normal file
BIN
imgs/screenshot_3.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
34
imgs/screenshot_3.png.import
Normal file
34
imgs/screenshot_3.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="StreamTexture"
|
||||
path="res://.import/screenshot_3.png-6cf7b262e447a7e1897953df89f52cf7.stex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://imgs/screenshot_3.png"
|
||||
dest_files=[ "res://.import/screenshot_3.png-6cf7b262e447a7e1897953df89f52cf7.stex" ]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_mode=0
|
||||
compress/bptc_ldr=0
|
||||
compress/normal_map=0
|
||||
flags/repeat=0
|
||||
flags/filter=true
|
||||
flags/mipmaps=false
|
||||
flags/anisotropic=false
|
||||
flags/srgb=2
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/HDR_as_SRGB=false
|
||||
process/invert_color=false
|
||||
stream=false
|
||||
size_limit=0
|
||||
detect_3d=true
|
||||
svg/scale=1.0
|
BIN
imgs/screenshot_4.png
Normal file
BIN
imgs/screenshot_4.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.8 KiB |
34
imgs/screenshot_4.png.import
Normal file
34
imgs/screenshot_4.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="StreamTexture"
|
||||
path="res://.import/screenshot_4.png-c6adff16fa5e61b3ce72172e14b45dd7.stex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://imgs/screenshot_4.png"
|
||||
dest_files=[ "res://.import/screenshot_4.png-c6adff16fa5e61b3ce72172e14b45dd7.stex" ]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_mode=0
|
||||
compress/bptc_ldr=0
|
||||
compress/normal_map=0
|
||||
flags/repeat=0
|
||||
flags/filter=true
|
||||
flags/mipmaps=false
|
||||
flags/anisotropic=false
|
||||
flags/srgb=2
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/HDR_as_SRGB=false
|
||||
process/invert_color=false
|
||||
stream=false
|
||||
size_limit=0
|
||||
detect_3d=true
|
||||
svg/scale=1.0
|
278
imgs/snapshot.html
Normal file
278
imgs/snapshot.html
Normal file
@ -0,0 +1,278 @@
|
||||
<!DOCTYPE html>
|
||||
<pre>
|
||||
<code>
|
||||
tool
|
||||
class_name CodeSnapshotInstance
|
||||
extends Control
|
||||
|
||||
onready var viewport_container : ViewportContainer = $VSplitContainer/ViewportContainer
|
||||
onready var viewport : Viewport = viewport_container.get_child(0)
|
||||
onready var snapshot_container : ColorRect = viewport.get_node("Background")
|
||||
onready var settings_container : VBoxContainer = $VSplitContainer/Settings
|
||||
|
||||
onready var script_container : PanelContainer = snapshot_container.get_node("ScriptContainer")
|
||||
onready var script_edit : TextEdit = script_container.get_node("VBox/ScriptEdit")
|
||||
onready var template_menu : PopupMenu = settings_container.get_node("Template/TemplateMenu").get_popup()
|
||||
onready var colors_list : VBoxContainer = settings_container.get_node("Colors/ColorsList")
|
||||
onready var properties_list : VBoxContainer = settings_container.get_node("Properties/PropertiesList")
|
||||
onready var from : LineEdit = settings_container.get_node("Properties/PropertiesList/from_line_to_line/from")
|
||||
onready var to : LineEdit = settings_container.get_node("Properties/PropertiesList/from_line_to_line/to")
|
||||
onready var save : FileDialog = $Save
|
||||
|
||||
var script_editor : ScriptEditor
|
||||
var editor_settings : EditorSettings
|
||||
|
||||
var template_dir : String = "res://addons/code-snapshot/godot-syntax-themes/"
|
||||
var templates : PoolStringArray
|
||||
var template_file : String = "Darcula"
|
||||
|
||||
var keywords : Array = ["self", "if", "else", "elif", "or", "and", "yield","func", "onready", "export", "var", "tool", "extends", "void", "null", "true", "false", "class_name", "print", "return", "pass", "match", "in", "define", "const"]
|
||||
var types : PoolStringArray = ClassDB.get_class_list()
|
||||
var from_line_to : Array = [-1, -1]
|
||||
var lines_count_as_min_size : bool = true
|
||||
var exporting_extension : String
|
||||
var path_to_save : String
|
||||
|
||||
func set_editor_settings(settings : EditorSettings):
|
||||
editor_settings = settings
|
||||
|
||||
func set_script_editor(editor : ScriptEditor):
|
||||
script_editor = editor
|
||||
script_editor.connect("editor_script_changed", self, "_on_script_changed")
|
||||
_on_script_changed(script_editor.get_current_script())
|
||||
|
||||
func hide_nodes():
|
||||
colors_list.hide()
|
||||
properties_list.hide()
|
||||
|
||||
func _ready() -> void:
|
||||
yield(get_tree(),"idle_frame")
|
||||
# viewport.set_process_input(true)
|
||||
script_edit.set_text("")
|
||||
template_menu.connect("index_pressed", self, "_on_index_pressed")
|
||||
hide_nodes()
|
||||
load_templates()
|
||||
if script_editor != null : _on_script_changed(script_editor.get_current_script())
|
||||
|
||||
func load_templates():
|
||||
templates = []
|
||||
template_menu.clear()
|
||||
var dir = Directory.new()
|
||||
if dir.open(template_dir) == OK:
|
||||
dir.list_dir_begin()
|
||||
var file_name = dir.get_next()
|
||||
while file_name != "":
|
||||
if not dir.current_is_dir() and file_name.get_extension() == "tet":
|
||||
templates.append(file_name.get_basename())
|
||||
template_menu.add_item(file_name.get_basename())
|
||||
file_name = dir.get_next()
|
||||
else:
|
||||
print("An error occurred when trying to access the path.")
|
||||
_on_index_pressed(0)
|
||||
|
||||
func _on_index_pressed(index : int):
|
||||
apply_template(templates[index])
|
||||
$VSplitContainer/Settings/Template/TemplateMenu.set_text("> "+templates[index])
|
||||
|
||||
func apply_template(template : String):
|
||||
var config = ConfigFile.new()
|
||||
var err = config.load(template_dir+"%s.tet"%template)
|
||||
if err == OK: # If not, something went wrong with the file loading
|
||||
set_script_box_color(config.get_value("color_theme", "background_color"))
|
||||
for setting in config.get_section_keys("color_theme"):
|
||||
if setting == "gdscript/function_definition_color" :
|
||||
add_function_definition_color(config.get_value("color_theme",setting))
|
||||
set_node_color("function_definition_color", config, "gdscript/")
|
||||
if setting == "keyword_color": add_keywords_color(config.get_value("color_theme",setting))
|
||||
if setting == "comment_color": add_comment_color(config.get_value("color_theme", setting))
|
||||
if setting == "string_color" : add_string_color(config.get_value("color_theme", setting))
|
||||
if setting == "engine_type_color" : add_engine_type_color(config.get_value("color_theme",setting))
|
||||
if setting == "gdscript/node_path_color" : add_node_path_color(config.get_value("color_theme",setting))
|
||||
if setting == "text_color" : set_text_color(config.get_value("color_theme",setting))
|
||||
script_edit.set("custom_colors/"+setting, config.get_value("color_theme",setting))
|
||||
set_node_color(setting, config)
|
||||
|
||||
func set_node_color(setting : String, config : ConfigFile, category : String = ""):
|
||||
if colors_list.get_node_or_null("%s/Color"%setting) != null : colors_list.get_node_or_null("%s/Color"%setting).color = config.get_value("color_theme",category+setting)
|
||||
|
||||
func add_node_path_color(color : Color):
|
||||
script_edit.add_color_region("$","", color, false)
|
||||
|
||||
func add_engine_type_color(color : Color):
|
||||
for type in types: script_edit.add_keyword_color(type, color)
|
||||
|
||||
func add_function_definition_color(color : Color):
|
||||
script_edit.add_color_region("func ","", color)
|
||||
|
||||
func set_script_edit_size(size : Vector2):
|
||||
script_container.rect_size = size
|
||||
|
||||
func set_background_color(color : Color):
|
||||
snapshot_container.color = color
|
||||
|
||||
# Script Box Color
|
||||
func set_script_box_color(color : Color):
|
||||
script_container.get("custom_styles/panel").set("bg_color", color)
|
||||
set_script_background_color(color)
|
||||
|
||||
func get_script_box_color() -> String:
|
||||
return "#"+script_container.get("custom_styles/panel").get("bg_color").to_html(false)
|
||||
# ................
|
||||
|
||||
func set_member_variable_color(color : Color):
|
||||
script_edit.set("custom_colors/member_variable_color", color)
|
||||
|
||||
func set_function_color(color : Color):
|
||||
script_edit.set("custom_colors/function_color", color)
|
||||
|
||||
# Script Background color .....
|
||||
func set_script_background_color(color : Color):
|
||||
script_edit.set("custom_colors/background_color", color)
|
||||
|
||||
func get_script_background_color() -> String:
|
||||
return "#"+script_edit.get("custom_colors/background_color").to_html(false)
|
||||
# ..............
|
||||
|
||||
func set_number_color(color : Color):
|
||||
script_edit.set("custom_colors/number_color", color)
|
||||
|
||||
# Text Color .....
|
||||
func set_text_color(color : Color):
|
||||
script_edit.set("custom_colors/font_color", color)
|
||||
|
||||
func get_text_color() -> String:
|
||||
return "#"+script_edit.get("custom_colors/font_color").to_html(false)
|
||||
# ................
|
||||
|
||||
func add_keywords_color(color : Color):
|
||||
for keyword in keywords:
|
||||
script_edit.add_keyword_color(keyword,color)
|
||||
|
||||
func add_comment_color(color : Color):
|
||||
script_edit.add_color_region("#","",color,true)
|
||||
|
||||
func add_string_color(color : Color):
|
||||
script_edit.add_color_region('"','"',color,false)
|
||||
|
||||
func set_from_line_to(from : int, to : int):
|
||||
from_line_to = [from, to]
|
||||
|
||||
var current_script : Script
|
||||
|
||||
func _on_script_changed(script : Script):
|
||||
if script == null or script_edit == null: return
|
||||
current_script = script
|
||||
var code : String = script.get_source_code()
|
||||
var code_array : Array = code.c_unescape().split("
|
||||
")
|
||||
code = PoolStringArray(code_array.slice(from_line_to[0]-1 if from_line_to[0] != -1 and from_line_to[0] < code_array.size()-1 else 0, from_line_to[1]-1 if from_line_to[1] != -1 and from_line_to[1] < code_array.size()-1 else code_array.size()-1)).join("
|
||||
")
|
||||
script_edit.set_text(code)
|
||||
change_frame_min_size()
|
||||
|
||||
func change_frame_min_size():
|
||||
if lines_count_as_min_size:
|
||||
script_edit.rect_min_size.y = script_edit.get_line_count() * (editor_settings.get_setting("interface/editor/code_font_size") + 6) if script_edit.get_line_count() <= 35 else 132
|
||||
viewport_container.rect_min_size.y = script_edit.rect_min_size.y + 168
|
||||
else:
|
||||
script_edit.rect_min_size.y = 0
|
||||
viewport_container.rect_min_size.y = 300
|
||||
|
||||
func _on_ColorsBtn_toggled(button_pressed : bool):
|
||||
colors_list.visible = button_pressed
|
||||
if button_pressed: $VSplitContainer/Settings/Colors/ColorsBtn.set_text("v Colors")
|
||||
else: $VSplitContainer/Settings/Colors/ColorsBtn.set_text("> Colors")
|
||||
|
||||
func _on_PropertiesBtn_toggled(button_pressed : bool):
|
||||
properties_list.visible = button_pressed
|
||||
if button_pressed: $VSplitContainer/Settings/Properties/PropertiesBtn.set_text("v Properties")
|
||||
else: $VSplitContainer/Settings/Properties/PropertiesBtn.set_text("> Properties")
|
||||
|
||||
|
||||
|
||||
|
||||
func _on_export_confirmed(path : String):
|
||||
path_to_save = path
|
||||
|
||||
func _on_Save_hide():
|
||||
match exporting_extension:
|
||||
"png":
|
||||
var image : Image = viewport.get_texture().get_data()
|
||||
image.flip_y()
|
||||
image.save_png(path_to_save)
|
||||
"html":
|
||||
var file : File = File.new()
|
||||
file.open(path_to_save, File.WRITE)
|
||||
var content : String = '<!DOCTYPE html>
|
||||
'
|
||||
content+='<pre>
|
||||
<code>
|
||||
'
|
||||
content+=script_edit.text+"
|
||||
"
|
||||
content+='</code>
|
||||
</pre>'
|
||||
file.store_line(content)
|
||||
file.close()
|
||||
|
||||
|
||||
func _on_autowrap_value_toggled(button_pressed):
|
||||
script_edit.wrap_enabled = button_pressed
|
||||
script_edit.update()
|
||||
|
||||
|
||||
func _on_draw_tabs_value_toggled(button_pressed):
|
||||
script_edit.draw_tabs = button_pressed
|
||||
|
||||
func _on_from_text_changed(new_text):
|
||||
if new_text.is_valid_integer():
|
||||
if int(new_text) == 0:
|
||||
new_text = str(-1)
|
||||
from.set_text(new_text)
|
||||
from_line_to[0] = int(new_text)
|
||||
_on_script_changed(current_script)
|
||||
|
||||
|
||||
|
||||
func _on_to_text_changed(new_text):
|
||||
if new_text.is_valid_integer():
|
||||
if int(new_text) == 0:
|
||||
new_text = str(-1)
|
||||
to.set_text(new_text)
|
||||
from_line_to[1] = int(new_text)
|
||||
_on_script_changed(current_script)
|
||||
|
||||
|
||||
func _on_Reload_pressed():
|
||||
_on_script_changed(current_script)
|
||||
|
||||
|
||||
func _on_minimap_draw_value_toggled(button_pressed):
|
||||
script_edit.minimap_draw = button_pressed
|
||||
|
||||
|
||||
func _on_ViewportContainer_item_rect_changed():
|
||||
snapshot_container.rect_size = viewport_container.rect_size
|
||||
|
||||
|
||||
func _on_minsie_value_toggled(button_pressed):
|
||||
lines_count_as_min_size = button_pressed
|
||||
change_frame_min_size()
|
||||
|
||||
func _on_ExportPNG_pressed():
|
||||
exporting_extension = "png"
|
||||
save.filters = ["*.png ; Portable Network Graphics"]
|
||||
save.current_file = "snapshot.png"
|
||||
save.popup()
|
||||
|
||||
|
||||
func _on_ExportHTML_pressed():
|
||||
exporting_extension = "html"
|
||||
save.filters = ["*.html ; HyperText Markup Language"]
|
||||
save.current_file = "snapshot.html"
|
||||
save.popup()
|
||||
|
||||
|
||||
|
||||
</code>
|
||||
</pre>
|
Loading…
Reference in New Issue
Block a user