refactor scatter_chart and line_chart

This commit is contained in:
fenix-hub 2022-01-08 18:46:03 +01:00
parent e3a78ed60a
commit 9d4d31fd2c
67 changed files with 2967 additions and 2650 deletions

28
.gitignore vendored
View File

@ -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_*/

BIN
.tmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -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

View File

@ -1,9 +1,3 @@
<p align="right">
<a href="https://discord.gg/KnJGY9S">
<img src="https://github.com/fenix-hub/ColoredBadges/blob/master/svg/social/discord.svg" alt="react" style="vertical-align:top; margin:6px 4px">
</a>
</p>
# Notice, plugin under refactoring
**This plugin is under refactoring an maintenance. The next official release will contain multiple updates and upgrades.**\
**If you encounter any bug, please contact me on Discord.**

View File

@ -1,469 +0,0 @@
tool
extends Chart2D
class_name LineChart2D
# [Linechart2D] - General purpose node for Line Charts
# A line chart or line plot or line graph or curve chart is a type of chart which
# displays information as a series of data points called 'markers'
# connected by straight line segments.
# It is a basic type of chart common in many fields. It is similar to a scatter plot
# except that the measurement points are ordered (typically by their x-axis value)
# and joined with straight line segments.
# A line chart is often used to visualize a trend in data over intervals of time
# a time series thus the line is often drawn chronologically.
# In these cases they are known as run charts.
# Source: Wikipedia
func _point_plotted():
pass
func _ready():
_get_children()
func _set_size(size: Vector2):
SIZE = size
build_chart()
if Engine.editor_hint:
_get_children()
Outlines.set_point_position(0, Vector2(origin.x, 0))
Outlines.set_point_position(1, Vector2(SIZE.x, 0))
Outlines.set_point_position(2, Vector2(SIZE.x, origin.y))
Outlines.set_point_position(3, origin)
Outlines.set_point_position(4, Vector2(origin.x, 0))
Grid.get_node("VLine").set_point_position(0, Vector2((OFFSET.x + SIZE.x) / 2,0))
Grid.get_node("VLine").set_point_position(1, Vector2((OFFSET.x + SIZE.x) / 2, origin.y))
Grid.get_node("HLine").set_point_position(0, Vector2(origin.x, origin.y / 2))
Grid.get_node("HLine").set_point_position(1, Vector2(SIZE.x, origin.y / 2))
func clear():
Outlines.points = []
Grid.get_node("HLine").queue_free()
Grid.get_node("VLine").queue_free()
func load_font():
if font != null:
font_size = font.get_height()
var theme: Theme = Theme.new()
theme.set_default_font(font)
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:
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)
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

View File

@ -1,448 +0,0 @@
tool
extends Chart2D
class_name ScatterChart2D
"""
[ScatterChart2D] - General purpose node for Scatter Charts
A scatter plot (also called a scatterplot, scatter graph, scatter chart, scattergram, or scatter diagram)
is a type of plot or mathematical diagram using Cartesian coordinates to display values for typically two variables
for a set of data. If the points are coded (color/shape/size), one additional variable can be displayed.
The data are displayed as a collection of points, each having the value of one variable determining the position on
the horizontal axis and the value of the other variable determining the position on the vertical axis.
/ source : Wikipedia /
"""
func _point_plotted():
pass
func _ready():
_get_children()
func _get_children():
OutlinesTween = $OutlinesTween
PointTween = $PointTween
Functions = $Functions
GridTween = $GridTween
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:
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)
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 = ECUtilities._load_templates()
if template_name!=null:
var custom_template = templates.get(templates.keys()[template_name])
function_colors = custom_template.function_colors as PoolColorArray
v_lines_color = Color(custom_template.v_lines_color)
h_lines_color = Color(custom_template.h_lines_color)
box_color = Color(custom_template.outline_color)
font_color = Color(custom_template.font_color)
property_list_changed_notify()
if Engine.editor_hint:
_get_children()
Outlines.set_default_color(box_color)
Grid.get_node("VLine").set_default_color(v_lines_color)
Grid.get_node("HLine").set_default_color(h_lines_color)
func _enter_tree():
_ready()

View File

@ -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:
ECECUtilities._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 = ECECUtilities._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

View File

@ -1,12 +1,7 @@
[gd_scene load_steps=3 format=2]
[ext_resource path="res://addons/easy_charts/2DChart/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 )
@ -55,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"]

View 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

View File

@ -1,10 +1,7 @@
[gd_scene load_steps=3 format=2]
[ext_resource path="res://addons/easy_charts/2DChart/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"]
@ -55,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"]

View 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()

View File

@ -1,9 +1,14 @@
[gd_scene load_steps=3 format=2]
<<<<<<<< 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"]
@ -45,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"]

View File

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

View File

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

View File

@ -1,9 +1,14 @@
[gd_scene load_steps=7 format=2]
<<<<<<<< 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]
@ -43,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"]

View File

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

View File

@ -1,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(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_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()

View File

@ -1,28 +0,0 @@
tool
extends ScatterChartBase
class_name ScatterChart
# [ScatterChart] - General purpose node for Scatter Charts
# A scatter plot (also called a scatterplot, scatter graph, scatter chart,
# scattergram, or scatter diagram) is a type of plot or mathematical diagram
# using Cartesian coordinates to display values for typically two variables
# for a set of data. If the points are coded (color/shape/size), one additional
# variable can be displayed. The data are displayed as a collection of points,
# each having the value of one variable determining the position on the
# horizontal axis and the value of the other variable determining the position
# on the vertical axis.
# Source: Wikipedia
func _get_property_list():
property_list[0].name = "ScatterChart"
return property_list
func _draw():
clear_points()
draw_grid()
draw_chart_outlines()
draw_points()
draw_treshold()

View File

@ -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

View File

@ -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

View File

@ -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])

View File

@ -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()

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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()
@ -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
@ -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"): connect("item_rect_changed", self, "redraw")
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,19 @@ 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:
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
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:
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
data = read_datas(csv_file, _delimiter)
structure_datas(slice_data())
build_chart()
count_functions()
calculate_pass()
calculate_colors()
calculate_coordinates()
set_shapes()
create_legend()
emit_signal("chart_plotted",self)
func plot_from_array(array : Array) -> void:
clean_variables()
clear_points()
load_font()
PointData.hide()
if array.empty():
ECUtilities._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():
ECUtilities._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():
ECUtilities._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():
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])
func plot_placeholder() -> void:
pass
# ................................. Helper Functions .................................
func load_font():
if font != null:
@ -449,7 +410,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 +425,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 +460,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:
@ -720,12 +673,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()

View File

@ -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,7 +128,7 @@ func _get_children():
FunctionsTween = $FunctionsTween
Functions = $Functions
GridTween = $GridTween
PointData = $PointData/PointData
DataTooltip = $DataTooltip/DataTooltip
Outlines = $Outlines
Grid = $Grid
@ -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():

View File

@ -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
@ -272,30 +272,12 @@ 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():
@ -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))

View 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)

View 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)

View File

@ -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)

View File

@ -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"]

View File

@ -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"]

View File

@ -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 )

View File

@ -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]

View File

@ -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(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
},
]
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()

View File

@ -1,9 +1,14 @@
[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/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]
@ -81,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"]

View File

@ -70,7 +70,6 @@ func _set(property, value):
func _draw():
clear_points()
draw_grid()
draw_chart_outlines()
if show_points:

View File

@ -1,8 +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/ControlChart/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]
@ -32,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 )
@ -84,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"]

View File

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

View File

@ -1,8 +1,13 @@
[gd_scene load_steps=3 format=2]
<<<<<<<< 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"]
@ -16,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 )
@ -56,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"]

View 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()

View File

@ -1,18 +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/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
@ -30,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 )
@ -75,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

View File

@ -0,0 +1,29 @@
tool
extends ScatterChartBase
class_name ScatterChart
"""
[ScatterChart] - General purpose node for Scatter Charts
A scatter plot (also called a scatterplot, scatter graph, scatter chart, scattergram, or scatter diagram)
is a type of plot or mathematical diagram using Cartesian coordinates to display values for typically two variables
for a set of data. If the points are coded (color/shape/size), one additional variable can be displayed.
The data are displayed as a collection of points, each having the value of one variable determining the position on
the horizontal axis and the value of the other variable determining the position on the vertical axis.
/ source : Wikipedia /
"""
# ---------------------
func _get_property_list():
property_list[0].name = "ScatterChart"
return property_list
func _draw():
clean_points()
draw_grid()
draw_chart_outlines()
draw_points()
draw_treshold()

View File

@ -1,17 +1,22 @@
[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/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
@ -22,6 +27,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
@ -43,6 +49,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
@ -79,8 +87,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
@ -89,3 +98,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

View File

@ -1,13 +1,3 @@
[remap]
importer="csv_translation"
type="Translation"
valid=false
[deps]
source_file="res://addons/easy_charts/file.samples/linechart (columns).csv"
[params]
compress=true
delimiter=0
importer="keep"

View File

@ -0,0 +1,2 @@
Date; 21/03/03;21/03/2008;21/03/2010;21/03/2011;21/03/2012;21/03/2013;21/03/2014;21/03/2015;21/03/2016;21/03/2018
Score;3;8;20;18;28;31;27;38;36;46
1 Date 21/03/03 21/03/2008 21/03/2010 21/03/2011 21/03/2012 21/03/2013 21/03/2014 21/03/2015 21/03/2016 21/03/2018
2 Score 3 8 20 18 28 31 27 38 36 46

View File

@ -0,0 +1,10 @@
[remap]
importer="csv"
type="TextFile"
[deps]
source_file="res://addons/easy_charts/file.samples/linechart.csv"
[params]

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 764 KiB

BIN
imgs/screensho_2t.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

View 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
View 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>