mirror of
https://github.com/Relintai/pandemonium_engine_easy_charts.git
synced 2025-01-10 15:09:45 +01:00
Scatter (#51)
* Refactor so it works with the new data format. Restructured some functions in smaller pieces and changed it so they can work with data in the new format. Main way to use it will be with the new plot_function but old functions support is requiered through structure_datas (WIP) * structure_datas rework structure_datas structure the data to match the new format. * Add new plotting methods Also renamed identifiers array to the already existing y_labels and a fix to correctly calculate tics. * Add autoscale or user-defined range * Add color definition from plot_function call Introduction of a param_dic parameter on plot_function that allows for specific parameters definitions for that curve only without changing the full Chart. Also moved the label identier string to a parameter on this dicionary so it can be called without specifying a label name. * Fix representation of negative values Using negative numbers should now work on both axis. For this I created two new methods calcualte_interval_tics and calculate_number_integer_digits to avoid code repetition. I'd like to work more on this, since now the representation is correct but can look very weird for some values with a lot of empty space on the chart. * Correctly calculates tics for numbers between 0 and 1 Also some small fixes to update_function and delete_function. Changed the tic little line to point outside the chart instead of inside so it doesn't overlap with the grid line. * Correctly (almost) center axis labels Needs a little of research for the vertical centering since get_string_size doesn't behave as expected. * Add some style representation customization Width of the grid and function lines. Made drawing points for LineChart optional (especially useful when using a huge number of points to avoid overclustering). For this I had to rewrite the draw_lines function to work similar to draw_points using point_positions instead of information of the point nodes. The string position of the labels is correctly calculated now. It uses a new variable: label_displacement so it don't look too close to the axis and the border. Set some default values that make more sense to the instanciable scenes. * Fix clearing of data structures with multiple calls to plot_from_x * Fix show_x_values_as_labels Correctly sets the x position when show_x_values_as_labels is active. * Fix show_x_values_as_labels when the label is a String Co-authored-by: Jorge <63685920+JFerrerBeired@users.noreply.github.com>
This commit is contained in:
parent
02621e8437
commit
1e388f49a0
@ -14,29 +14,77 @@ class_name LineChart
|
||||
# In these cases they are known as run charts.
|
||||
# Source: Wikipedia
|
||||
|
||||
var show_points := true
|
||||
var function_line_width : int = 2
|
||||
|
||||
|
||||
func build_property_list():
|
||||
.build_property_list()
|
||||
|
||||
property_list.append(
|
||||
{
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Display/show_points",
|
||||
"type": TYPE_BOOL
|
||||
})
|
||||
|
||||
#Find first element of Chart Display
|
||||
var position
|
||||
for i in property_list.size():
|
||||
if property_list[i]["name"].find("Chart_Style") != -1: #Found
|
||||
position = i
|
||||
break
|
||||
|
||||
property_list.insert(position + 2, #I want it to be below point shape and function colors
|
||||
{
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "1, 100, 1",
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Style/function_line_width",
|
||||
"type": TYPE_INT
|
||||
})
|
||||
|
||||
|
||||
func _get_property_list():
|
||||
property_list[0].name = "LineChart"
|
||||
return property_list
|
||||
property_list[0].name = "LineChart"
|
||||
return property_list
|
||||
|
||||
|
||||
func _get(property):
|
||||
match property:
|
||||
"Chart_Display/show_points":
|
||||
return show_points
|
||||
"Chart_Style/function_line_width":
|
||||
return function_line_width
|
||||
|
||||
|
||||
func _set(property, value):
|
||||
match property:
|
||||
"Chart_Display/show_points":
|
||||
show_points = value
|
||||
return true
|
||||
"Chart_Style/function_line_width":
|
||||
function_line_width = value
|
||||
return true
|
||||
|
||||
|
||||
func _draw():
|
||||
clear_points()
|
||||
draw_grid()
|
||||
draw_chart_outlines()
|
||||
draw_points()
|
||||
if show_points:
|
||||
draw_points()
|
||||
draw_lines()
|
||||
draw_treshold()
|
||||
|
||||
|
||||
func draw_lines():
|
||||
var _function = 0
|
||||
for PointContainer in Points.get_children(): #Each function is stored in a different PointContainer
|
||||
for function_point in range(1, PointContainer.get_children().size()):
|
||||
for function in point_values.size():
|
||||
for function_point in range(1, point_values[function].size()):
|
||||
draw_line(
|
||||
point_positions[_function][function_point - 1],
|
||||
point_positions[_function][function_point],
|
||||
function_colors[_function],
|
||||
2,
|
||||
false)
|
||||
_function += 1
|
||||
point_positions[function][function_point - 1],
|
||||
point_positions[function][function_point],
|
||||
function_colors[function],
|
||||
function_line_width,
|
||||
false)
|
||||
|
@ -3,7 +3,6 @@
|
||||
[ext_resource path="res://addons/easy_charts/Utilities/Point/point_data.tscn" type="PackedScene" id=1]
|
||||
[ext_resource path="res://addons/easy_charts/LineChart/line_chart.gd" type="Script" id=4]
|
||||
|
||||
|
||||
[sub_resource type="Theme" id=1]
|
||||
|
||||
[node name="LineChart" type="Control"]
|
||||
@ -27,12 +26,16 @@ In these cases they are known as run charts."
|
||||
}
|
||||
Chart_Properties/are_values_columns = false
|
||||
Chart_Properties/labels_index = 0
|
||||
Chart_Properties/show_x_values_as_labels = true
|
||||
Chart_Display/x_decim = 5.0
|
||||
Chart_Properties/show_x_values_as_labels = false
|
||||
Chart_Display/autoscale_x = true
|
||||
Chart_Display/x_decim = 1.0
|
||||
Chart_Display/autoscale_y = true
|
||||
Chart_Display/y_decim = 1.0
|
||||
Chart_Style/points_shape = [ 0 ]
|
||||
Chart_Style/function_colors = [ Color( 0.117647, 0.117647, 0.117647, 1 ) ]
|
||||
Chart_Style/points_shape = [ ]
|
||||
Chart_Style/function_colors = PoolColorArray( 0.117647, 0.117647, 0.117647, 1, 0.117647, 0.117647, 0.117647, 1, 0.117647, 0.117647, 0.117647, 1, 0.117647, 0.117647, 0.117647, 1 )
|
||||
Chart_Style/function_line_width = 2
|
||||
Chart_Style/box_color = Color( 0.117647, 0.117647, 0.117647, 1 )
|
||||
Chart_Style/grid_lines_width = 1
|
||||
Chart_Style/v_lines_color = Color( 0.792157, 0.792157, 0.792157, 1 )
|
||||
Chart_Style/h_lines_color = Color( 0.792157, 0.792157, 0.792157, 1 )
|
||||
Chart_Style/font = null
|
||||
@ -43,6 +46,7 @@ Chart_Style/template = 0
|
||||
Chart_Modifiers/treshold = Vector2( 0, 0 )
|
||||
Chart_Modifiers/only_disp_values = Vector2( 0, 0 )
|
||||
Chart_Modifiers/invert_chart = false
|
||||
Chart_Display/show_points = true
|
||||
|
||||
[node name="Background" type="ColorRect" parent="."]
|
||||
visible = false
|
||||
@ -82,10 +86,10 @@ __meta__ = {
|
||||
[node name="PointData" parent="." instance=ExtResource( 1 )]
|
||||
|
||||
[node name="PointData" parent="PointData" index="0"]
|
||||
margin_left = -257.531
|
||||
margin_top = -244.08
|
||||
margin_right = -257.667
|
||||
margin_bottom = -243.28
|
||||
margin_left = -458.75
|
||||
margin_top = -164.504
|
||||
margin_right = -458.886
|
||||
margin_bottom = -163.704
|
||||
theme = SubResource( 1 )
|
||||
|
||||
[editable path="PointData"]
|
||||
|
@ -2,24 +2,22 @@ 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 /
|
||||
"""
|
||||
|
||||
# ---------------------
|
||||
|
||||
# [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
|
||||
property_list[0].name = "ScatterChart"
|
||||
return property_list
|
||||
|
||||
|
||||
func _draw():
|
||||
|
@ -3,7 +3,6 @@
|
||||
[ext_resource path="res://addons/easy_charts/Utilities/Point/point_data.tscn" type="PackedScene" id=1]
|
||||
[ext_resource path="res://addons/easy_charts/ScatterChart/scatter_chart.gd" type="Script" id=2]
|
||||
|
||||
|
||||
[sub_resource type="Theme" id=1]
|
||||
|
||||
[node name="ScatterChart" type="Control"]
|
||||
@ -24,18 +23,24 @@ the horizontal axis and the value of the other variable determining the position
|
||||
}
|
||||
Chart_Properties/are_values_columns = false
|
||||
Chart_Properties/labels_index = 0
|
||||
Chart_Properties/show_x_values_as_labels = true
|
||||
Chart_Display/x_decim = 5.0
|
||||
Chart_Display/y_decim = 5.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_Properties/show_x_values_as_labels = false
|
||||
Chart_Display/autoscale_x = true
|
||||
Chart_Display/x_decim = 1.0
|
||||
Chart_Display/autoscale_y = true
|
||||
Chart_Display/y_decim = 1.0
|
||||
Chart_Style/points_shape = [ ]
|
||||
Chart_Style/function_colors = PoolColorArray( )
|
||||
Chart_Style/box_color = Color( 0.117647, 0.117647, 0.117647, 1 )
|
||||
Chart_Style/grid_lines_width = 1
|
||||
Chart_Style/v_lines_color = Color( 0.792157, 0.792157, 0.792157, 1 )
|
||||
Chart_Style/h_lines_color = Color( 0.792157, 0.792157, 0.792157, 1 )
|
||||
Chart_Style/font = null
|
||||
Chart_Style/bold_font = null
|
||||
Chart_Style/font_color = Color( 0.117647, 0.117647, 0.117647, 1 )
|
||||
Chart_Style/use_template = true
|
||||
Chart_Style/template = 0
|
||||
Chart_Modifiers/treshold = Vector2( 0, 0 )
|
||||
Chart_Modifiers/only_disp_values = Vector2( 0, 0 )
|
||||
Chart_Modifiers/invert_chart = false
|
||||
|
||||
[node name="Background" type="ColorRect" parent="."]
|
||||
@ -76,10 +81,10 @@ __meta__ = {
|
||||
[node name="PointData" parent="." instance=ExtResource( 1 )]
|
||||
|
||||
[node name="PointData" parent="PointData" index="0"]
|
||||
margin_left = -311.73
|
||||
margin_top = -167.672
|
||||
margin_right = -311.866
|
||||
margin_bottom = -166.872
|
||||
margin_left = -510.384
|
||||
margin_top = -148.79
|
||||
margin_right = -510.52
|
||||
margin_bottom = -147.99
|
||||
theme = SubResource( 1 )
|
||||
|
||||
[editable path="PointData"]
|
||||
|
@ -62,8 +62,8 @@ var x_label : String
|
||||
var x_labels : Array
|
||||
var y_labels : Array
|
||||
|
||||
var x_margin_min : int = 0
|
||||
var y_margin_min : int = 0
|
||||
var x_margin_min : float = 0
|
||||
var y_margin_min : float = 0
|
||||
|
||||
# actual values of point, from the database
|
||||
var point_values : Array
|
||||
@ -78,9 +78,9 @@ export (String) var chart_name : String = "" setget set_chart_name
|
||||
export (String, FILE, "*.txt, *.csv") var source : String = "" setget set_source
|
||||
export (String) var delimiter : String = ";" setget set_delimiter
|
||||
|
||||
var origin_at_zero : bool = true setget set_origin_at_zero#, get_origin_at_zero
|
||||
var origin_at_zero : bool = false setget set_origin_at_zero#, get_origin_at_zero
|
||||
var are_values_columns : bool = false setget set_are_values_columns#, get_are_values_columns
|
||||
var show_x_values_as_labels : bool = true setget set_show_x_values_as_labels#, get_show_x_values_as_labels
|
||||
var show_x_values_as_labels : bool = false setget set_show_x_values_as_labels#, get_show_x_values_as_labels
|
||||
var labels_index : int = 0 setget set_labels_index#, get_labels_index
|
||||
var function_names_index : int = 0 setget set_function_names_index#, get_function_names_index
|
||||
|
||||
@ -94,13 +94,14 @@ var column_gap : float = 2 setget set_column_gap
|
||||
|
||||
# Calculations of decim and its relation with number of tics: https://www.desmos.com/calculator/jeiceaswiy
|
||||
var full_scale : float = 1.0 setget set_full_scale
|
||||
var x_decim : float = 5.0 setget set_x_decim
|
||||
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 function_colors = [Color("#1e1e1e")] setget set_function_colors
|
||||
var outline_color : Color = Color("#1e1e1e") setget set_outline_color
|
||||
var box_color : Color = Color("#1e1e1e") setget set_box_color
|
||||
var grid_lines_width : int = 1 setget set_grid_lines_width
|
||||
var v_lines_color : Color = Color("#cacaca") setget set_v_lines_color
|
||||
var h_lines_color : Color = Color("#cacaca") setget set_h_lines_color
|
||||
var grid_color : Color = Color("#1e1e1e") setget set_grid_color
|
||||
@ -131,6 +132,10 @@ var treshold : Vector2 setget set_treshold
|
||||
# only used to draw treshold values
|
||||
var treshold_draw : Vector2
|
||||
|
||||
# Custom parameters for plot display
|
||||
var tic_length : int = 5 setget set_tic_length # Length of the bar indicating a tic
|
||||
var label_displacement : int = 4 setget set_label_displacement # Separation between the label and both the axis and the edge border
|
||||
|
||||
# !! API v2
|
||||
static func instance(chart_type : int):
|
||||
var chart_t : String = Utilities.get_chart_type(chart_type)
|
||||
@ -180,6 +185,8 @@ func _get(property):
|
||||
return grid_color
|
||||
"Chart_Style/box_color":
|
||||
return box_color
|
||||
"Chart_Style/grid_lines_width":
|
||||
return grid_lines_width
|
||||
"Chart_Style/v_lines_color":
|
||||
return v_lines_color
|
||||
"Chart_Style/h_lines_color":
|
||||
@ -262,6 +269,9 @@ func _set(property, value):
|
||||
"Chart_Style/box_color":
|
||||
box_color = value
|
||||
return true
|
||||
"Chart_Style/grid_lines_width":
|
||||
grid_lines_width = value
|
||||
return true
|
||||
"Chart_Style/v_lines_color":
|
||||
v_lines_color = value
|
||||
return true
|
||||
@ -332,6 +342,8 @@ func plot():
|
||||
emit_signal("chart_plotted",self)
|
||||
|
||||
func plot_from_csv(csv_file : String, _delimiter : String = delimiter):
|
||||
clean_variables()
|
||||
clear_points()
|
||||
load_font()
|
||||
PointData.hide()
|
||||
|
||||
@ -377,7 +389,6 @@ func plot_from_dataframe(dataframe : DataFrame) -> void:
|
||||
clean_variables()
|
||||
clear_points()
|
||||
load_font()
|
||||
load_font()
|
||||
PointData.hide()
|
||||
|
||||
data = dataframe.get_dataframe().duplicate(true)
|
||||
@ -433,7 +444,6 @@ func load_font():
|
||||
var theme : Theme = Theme.new()
|
||||
theme.set_default_font(font)
|
||||
set_theme(theme)
|
||||
|
||||
else:
|
||||
var lbl = Label.new()
|
||||
font = lbl.get_font("")
|
||||
@ -443,7 +453,7 @@ func load_font():
|
||||
|
||||
func calculate_colors():
|
||||
if function_colors.size() < functions:
|
||||
for function in range(functions - function_colors.size() + 1): function_colors.append(Color(randf(),randf(), randf()))
|
||||
for function in range(functions - function_colors.size()): function_colors.append(Color(randf(),randf(), randf()))
|
||||
|
||||
func set_shapes():
|
||||
if points_shape.empty() or points_shape.size() < functions:
|
||||
@ -629,10 +639,22 @@ func set_outline_color(c : Color):
|
||||
func set_box_color(c : Color):
|
||||
box_color = c
|
||||
|
||||
# ! API
|
||||
func set_tic_length(i: int):
|
||||
tic_length = i
|
||||
|
||||
# ! API
|
||||
func set_label_displacement(i:int):
|
||||
label_displacement = i
|
||||
|
||||
# ! API
|
||||
func set_grid_color(c : Color):
|
||||
grid_color = c
|
||||
|
||||
# ! API
|
||||
func set_grid_lines_width(i : int):
|
||||
grid_lines_width = i
|
||||
|
||||
# ! API
|
||||
func set_v_lines_color(c : Color):
|
||||
v_lines_color = c
|
||||
|
@ -1,4 +1,3 @@
|
||||
tool
|
||||
extends Chart
|
||||
class_name ScatterChartBase
|
||||
|
||||
@ -6,51 +5,122 @@ class_name ScatterChartBase
|
||||
# of points in a two-variable space. It handles basic data structure and grid
|
||||
# layout and leaves to child classes the more specific behaviour.
|
||||
|
||||
var property_list = [
|
||||
#Stored in the form of [[min_func1, min_func2, min_func3, ...], [max_func1, max_func2, ...]]
|
||||
var x_domain := [[], []]
|
||||
var y_domain := [[], []]
|
||||
|
||||
var x_range := [0, 0]
|
||||
var y_range := [0, 0]
|
||||
var autoscale_x = true
|
||||
var autoscale_y = true
|
||||
|
||||
|
||||
var property_list = []
|
||||
|
||||
|
||||
func _init():
|
||||
build_property_list()
|
||||
|
||||
|
||||
func build_property_list():
|
||||
property_list.clear()
|
||||
|
||||
# Chart Properties
|
||||
property_list.append(
|
||||
{
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_CATEGORY,
|
||||
"name": "ScatterChartBase", #TODO Changue this in the child classes
|
||||
"name": "ScatterChartBase",
|
||||
"type": TYPE_STRING
|
||||
},
|
||||
})
|
||||
property_list.append(
|
||||
{
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Properties/are_values_columns",
|
||||
"type": TYPE_BOOL
|
||||
},
|
||||
})
|
||||
property_list.append(
|
||||
{
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "-1,100,1",
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Properties/labels_index",
|
||||
"type": TYPE_INT
|
||||
},
|
||||
})
|
||||
property_list.append(
|
||||
{
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Properties/show_x_values_as_labels",
|
||||
"type": TYPE_BOOL
|
||||
},
|
||||
})
|
||||
|
||||
# Chart Display
|
||||
property_list.append(
|
||||
{
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Display/autoscale_x",
|
||||
"type": TYPE_BOOL
|
||||
})
|
||||
if not autoscale_x:
|
||||
property_list.append(
|
||||
{
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Display/min_x_range",
|
||||
"type": TYPE_REAL
|
||||
})
|
||||
property_list.append(
|
||||
{
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Display/max_x_range",
|
||||
"type": TYPE_REAL
|
||||
})
|
||||
property_list.append(
|
||||
{
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "0.001, 10",
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Display/x_decim",
|
||||
"type": TYPE_REAL
|
||||
},
|
||||
})
|
||||
property_list.append(
|
||||
{
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Display/autoscale_y",
|
||||
"type": TYPE_BOOL
|
||||
})
|
||||
if not autoscale_y:
|
||||
property_list.append(
|
||||
{
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Display/min_y_range",
|
||||
"type": TYPE_REAL
|
||||
})
|
||||
property_list.append(
|
||||
{
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Display/max_y_range",
|
||||
"type": TYPE_REAL
|
||||
})
|
||||
property_list.append(
|
||||
{
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "0.001, 10",
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Display/y_decim",
|
||||
"type": TYPE_REAL
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
# Chart Style
|
||||
property_list.append(
|
||||
{
|
||||
"hint": 24,
|
||||
"hint_string": ("%d/%d:%s"
|
||||
@ -59,31 +129,44 @@ var property_list = [
|
||||
"name": "Chart_Style/points_shape",
|
||||
"type": TYPE_ARRAY,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE
|
||||
},
|
||||
})
|
||||
property_list.append(
|
||||
{
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Style/function_colors",
|
||||
"type": TYPE_COLOR_ARRAY
|
||||
},
|
||||
})
|
||||
property_list.append(
|
||||
{
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Style/box_color",
|
||||
"type": TYPE_COLOR
|
||||
},
|
||||
})
|
||||
property_list.append(
|
||||
{
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "1, 100, 1",
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Style/grid_lines_width",
|
||||
"type": TYPE_INT
|
||||
})
|
||||
property_list.append(
|
||||
{
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Style/v_lines_color",
|
||||
"type": TYPE_COLOR
|
||||
},
|
||||
})
|
||||
property_list.append(
|
||||
{
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Style/h_lines_color",
|
||||
"type": TYPE_COLOR
|
||||
},
|
||||
})
|
||||
property_list.append(
|
||||
{
|
||||
"class_name": "Font",
|
||||
"hint": PROPERTY_HINT_RESOURCE_TYPE,
|
||||
@ -91,7 +174,8 @@ var property_list = [
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Style/font",
|
||||
"type": TYPE_OBJECT
|
||||
},
|
||||
})
|
||||
property_list.append(
|
||||
{
|
||||
"class_name": "Font",
|
||||
"hint": PROPERTY_HINT_RESOURCE_TYPE,
|
||||
@ -99,225 +183,435 @@ var property_list = [
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Style/bold_font",
|
||||
"type": TYPE_OBJECT
|
||||
},
|
||||
})
|
||||
property_list.append(
|
||||
{
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Style/font_color",
|
||||
"type": TYPE_COLOR
|
||||
},
|
||||
})
|
||||
property_list.append(
|
||||
{
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Style/use_template",
|
||||
"type": TYPE_BOOL
|
||||
},
|
||||
})
|
||||
property_list.append(
|
||||
{
|
||||
"hint": PROPERTY_HINT_ENUM,
|
||||
"hint_string": PoolStringArray(Utilities.templates.keys()).join(","),
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Style/template",
|
||||
"type": TYPE_INT
|
||||
},
|
||||
})
|
||||
|
||||
# Chart Modifiers
|
||||
property_list.append(
|
||||
{
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Modifiers/treshold",
|
||||
"type": TYPE_VECTOR2
|
||||
},
|
||||
})
|
||||
property_list.append(
|
||||
{
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Modifiers/only_disp_values",
|
||||
"type": TYPE_VECTOR2
|
||||
},
|
||||
})
|
||||
property_list.append(
|
||||
{
|
||||
"hint": PROPERTY_HINT_NONE,
|
||||
"usage": PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE,
|
||||
"name": "Chart_Modifiers/invert_chart",
|
||||
"type": TYPE_BOOL
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
|
||||
func _set(property, value):
|
||||
match property:
|
||||
"Chart_Display/autoscale_x":
|
||||
autoscale_x = value
|
||||
build_property_list()
|
||||
property_list_changed_notify()
|
||||
return true
|
||||
"Chart_Display/autoscale_y":
|
||||
autoscale_y = value
|
||||
build_property_list()
|
||||
property_list_changed_notify()
|
||||
return true
|
||||
"Chart_Display/min_x_range":
|
||||
x_range[0] = value
|
||||
return true
|
||||
"Chart_Display/max_x_range":
|
||||
x_range[1] = value
|
||||
return true
|
||||
"Chart_Display/min_y_range":
|
||||
y_range[0] = value
|
||||
return true
|
||||
"Chart_Display/max_y_range":
|
||||
y_range[1] = value
|
||||
return true
|
||||
|
||||
|
||||
func _get(property):
|
||||
match property:
|
||||
"Chart_Display/autoscale_x":
|
||||
return autoscale_x
|
||||
"Chart_Display/autoscale_y":
|
||||
return autoscale_y
|
||||
"Chart_Display/min_x_range":
|
||||
return x_range[0]
|
||||
"Chart_Display/max_x_range":
|
||||
return x_range[1]
|
||||
"Chart_Display/min_y_range":
|
||||
return y_range[0]
|
||||
"Chart_Display/max_y_range":
|
||||
return y_range[1]
|
||||
|
||||
|
||||
func plot():
|
||||
# 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()
|
||||
var id := ""
|
||||
|
||||
if x.empty() or y.empty():
|
||||
Utilities._print_message("Can't plot a chart with an empty Array.",1)
|
||||
return
|
||||
elif x.size() != y.size():
|
||||
Utilities._print_message("Can't plot a chart with x and y having different number of elements.",1)
|
||||
return
|
||||
|
||||
for param in param_dic.keys():
|
||||
match param:
|
||||
"label":
|
||||
id = param_dic[param]
|
||||
"color":
|
||||
if function_colors.size() < functions + 1: #There is going to be a new function
|
||||
function_colors.append(param_dic[param])
|
||||
else:
|
||||
function_colors[functions] = param_dic[param]
|
||||
|
||||
id = generate_identifier() if id.empty() else id
|
||||
|
||||
if y_labels.has(id):
|
||||
Utilities._print_message("The identifier %s is already used. Please use a different one." % id,1)
|
||||
return
|
||||
|
||||
y_domain[0].append(null)
|
||||
y_domain[1].append(null)
|
||||
x_domain[0].append(null)
|
||||
x_domain[1].append(null)
|
||||
|
||||
x_datas.append(x)
|
||||
y_datas.append(y)
|
||||
y_labels.append(id)
|
||||
|
||||
calculate_range(id)
|
||||
plot()
|
||||
|
||||
|
||||
func update_function(id:String, x:Array, y:Array, param_dic := {}):
|
||||
var function = y_labels.find(id)
|
||||
|
||||
if function == -1: #Not found
|
||||
Utilities._print_message("The identifier %s does not exist." % id,1)
|
||||
return
|
||||
|
||||
for param in param_dic.keys():
|
||||
match param:
|
||||
"label":
|
||||
y_labels[function] = param_dic[param]
|
||||
"color":
|
||||
function_colors[function] = param_dic[param]
|
||||
|
||||
x_datas[function] = x
|
||||
y_datas[function] = y
|
||||
|
||||
calculate_range(id)
|
||||
plot()
|
||||
update()
|
||||
|
||||
|
||||
func delete_function(id:String):
|
||||
var function = y_labels.find(id)
|
||||
|
||||
if function == -1: #Not found
|
||||
Utilities._print_message("The identifier %s does not exist." % id,1)
|
||||
return
|
||||
|
||||
y_labels.remove(function)
|
||||
x_datas.remove(function)
|
||||
y_datas.remove(function)
|
||||
y_domain[0].remove(function)
|
||||
y_domain[1].remove(function)
|
||||
x_domain[0].remove(function)
|
||||
x_domain[1].remove(function)
|
||||
function_colors.remove(function)
|
||||
|
||||
plot()
|
||||
update()
|
||||
|
||||
|
||||
func generate_identifier():
|
||||
#TODO: Check if the identifier generated already exist (given by the user)
|
||||
return "f%d" % (y_labels.size() + 1)
|
||||
|
||||
|
||||
func structure_datas(database : Array):
|
||||
# @labels_index can be either a column or a row relative to x values
|
||||
# @y_values can be either a column or a row relative to y values
|
||||
|
||||
#This is done to make sure this arrays are empty on subsecuent calls of this function.
|
||||
#This function is called from the "old" methods such as plot_from_array and
|
||||
#for the moment it doesn't clean this variables on clean_variable.
|
||||
x_domain = [[], []]
|
||||
y_domain = [[], []]
|
||||
|
||||
are_values_columns = invert_chart != are_values_columns
|
||||
var x_values := []
|
||||
|
||||
if are_values_columns:
|
||||
var y_values := []
|
||||
var y_columns = database[0].size()
|
||||
if range(database.size()).has(labels_index): # x column is present
|
||||
y_columns -= 1
|
||||
else:
|
||||
x_values = range(database.size()) #If no x column is given, a generic one is generated
|
||||
x_values.push_front("")
|
||||
|
||||
for _i in y_columns: #Resize to number of y columns
|
||||
y_values.append([])
|
||||
|
||||
for row in database.size():
|
||||
var t_vals : Array
|
||||
var y_column = 0
|
||||
for column in database[row].size():
|
||||
if column == labels_index:
|
||||
var x_data = database[row][column]
|
||||
if typeof(x_data) == TYPE_INT or typeof(x_data) == TYPE_REAL:
|
||||
x_datas.append(x_data as float)
|
||||
x_values.append(x_data as float)
|
||||
else:
|
||||
x_datas.append(x_data.replace(",", ".") as float)
|
||||
x_values.append(x_data.replace(",", ".") as float)
|
||||
else:
|
||||
if row != 0:
|
||||
var y_data = database[row][column]
|
||||
if typeof(y_data) == TYPE_INT or typeof(y_data) == TYPE_REAL:
|
||||
t_vals.append(y_data as float)
|
||||
if typeof(y_data) == TYPE_INT or typeof(y_data) == TYPE_REAL:
|
||||
y_values[y_column].append(y_data as float)
|
||||
else:
|
||||
t_vals.append(y_data.replace(",",".") as float)
|
||||
y_values[y_column].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())
|
||||
y_column += 1
|
||||
|
||||
x_label = str(x_values.pop_front())
|
||||
for function in y_values.size():
|
||||
y_datas.append(y_values[function])
|
||||
x_datas.append(x_values)
|
||||
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
|
||||
var database_size = range(database.size())
|
||||
if database_size.has(labels_index):
|
||||
x_values = database[labels_index]
|
||||
x_label = x_values.pop_front() as String
|
||||
database_size.erase(labels_index) #Remove x row from the iterator
|
||||
|
||||
for row in database_size:
|
||||
var y_values = database[row] as Array
|
||||
y_labels.append(y_values.pop_front() as String)
|
||||
|
||||
for val in y_values.size():
|
||||
y_values[val] = y_values[val] as float
|
||||
|
||||
y_datas.append(y_values)
|
||||
x_datas.append(x_values if not x_values.empty() else range(y_values.size()))
|
||||
|
||||
# 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 margin_max = y_datas[cluster].max()
|
||||
var margin_min = y_datas[cluster].min()
|
||||
to_order.append(margin_max)
|
||||
to_order_min.append(margin_min)
|
||||
|
||||
var y_margin_max = to_order.max()
|
||||
y_margin_min = to_order_min.min() if not origin_at_zero else 0
|
||||
v_dist = y_decim * pow(10.0, str(y_margin_max).split(".")[0].length() - 1)
|
||||
var multi = 0
|
||||
var p = (v_dist * multi) + y_margin_min
|
||||
y_chors.append(p as String)
|
||||
while p < y_margin_max:
|
||||
multi += 1
|
||||
p = (v_dist * multi) + y_margin_min
|
||||
y_chors.append(p as String)
|
||||
for function in y_labels:
|
||||
y_domain[0].append(null)
|
||||
y_domain[1].append(null)
|
||||
x_domain[0].append(null)
|
||||
x_domain[1].append(null)
|
||||
calculate_range(function)
|
||||
|
||||
# draw x_labels
|
||||
to_order.clear()
|
||||
to_order = x_datas
|
||||
var x_margin_max = to_order.max()
|
||||
x_margin_min = to_order.min() if not origin_at_zero else 0
|
||||
if not show_x_values_as_labels:
|
||||
h_dist = x_decim * pow(10.0, str(x_margin_max).split(".")[0].length() - 1)
|
||||
multi = 0
|
||||
p = (h_dist * multi) + x_margin_min
|
||||
x_labels.append(p as String)
|
||||
while p < x_margin_max:
|
||||
multi += 1
|
||||
p = (h_dist * multi) + x_margin_min
|
||||
x_labels.append(p as String)
|
||||
calculate_tics()
|
||||
|
||||
OFFSET.x = str(y_margin_max).length() * font_size
|
||||
OFFSET.y = font_size * 2
|
||||
|
||||
func calculate_range(id):
|
||||
# Calculate the domain of the given function in the x and y axis
|
||||
# and updates the range value
|
||||
|
||||
var function = y_labels.find(id)
|
||||
|
||||
y_domain[0][function] = y_datas[function].min()
|
||||
y_domain[1][function] = y_datas[function].max()
|
||||
|
||||
x_domain[0][function] = x_datas[function].min()
|
||||
x_domain[1][function] = x_datas[function].max()
|
||||
|
||||
|
||||
func calculate_tics():
|
||||
y_chors.clear()
|
||||
x_chors.clear()
|
||||
|
||||
# Chose the min/max from all functions
|
||||
if autoscale_x:
|
||||
x_range = [x_domain[0].min() if not origin_at_zero else 0, x_domain[1].max()]
|
||||
if autoscale_y:
|
||||
y_range = [y_domain[0].min() if not origin_at_zero else 0, y_domain[1].max()]
|
||||
|
||||
|
||||
y_margin_min = y_range[0]
|
||||
var y_margin_max = y_range[1]
|
||||
v_dist = y_decim * pow(10.0, calculate_position_significant_figure(y_margin_max - y_margin_min) - 1)
|
||||
|
||||
# There are three cases of min/max:
|
||||
# For +/+ and -/- we just do the usual and draw tics from min to max
|
||||
# But for the -/+ we do in two times to force the 0 to appear so it is
|
||||
# easier to read. Then we draw the negative from 0 to min and the positives
|
||||
# from 0 to max without drawing the 0 again
|
||||
if y_margin_min < 0 and y_margin_max >= 0:
|
||||
calculate_interval_tics(0, y_margin_min, -v_dist, y_chors) #Negative tics
|
||||
calculate_interval_tics(0, y_margin_max, v_dist, y_chors, false) #Positive tics
|
||||
y_chors.sort()
|
||||
y_margin_min = min(y_margin_min, y_chors[0])
|
||||
else:
|
||||
calculate_interval_tics(y_margin_min, y_margin_max, v_dist, y_chors)
|
||||
for i in y_chors.size():
|
||||
y_chors[i] = String(y_chors[i]) #Can't cast directly on calculate_interval_tics because it mess up with the sorting
|
||||
|
||||
if not show_x_values_as_labels:
|
||||
x_margin_min = x_range[0]
|
||||
var x_margin_max = x_range[1]
|
||||
h_dist = x_decim * pow(10.0, calculate_position_significant_figure(x_margin_max - x_margin_min) - 1)
|
||||
|
||||
if x_margin_min < 0 and x_margin_max >= 0:
|
||||
calculate_interval_tics(0, x_margin_min, -h_dist, x_labels) #Negative tics
|
||||
calculate_interval_tics(0, x_margin_max, h_dist, x_labels, false) #Positive tics
|
||||
x_labels.sort()
|
||||
x_margin_min = min(x_margin_min, x_labels[0])
|
||||
else:
|
||||
calculate_interval_tics(x_margin_min, x_margin_max, h_dist, x_labels)
|
||||
for i in x_labels.size():
|
||||
x_labels[i] = String(x_labels[i])
|
||||
x_chors = x_labels
|
||||
else:
|
||||
for function in y_labels.size():
|
||||
for value in x_datas[function]:
|
||||
if not x_chors.has(value as String): #Don't append repeated values
|
||||
x_chors.append(value as String)
|
||||
|
||||
|
||||
func build_chart():
|
||||
var longest_y_tic = 0
|
||||
for y_tic in y_chors:
|
||||
var length = font.get_string_size(y_tic).x
|
||||
if length > longest_y_tic:
|
||||
longest_y_tic = length
|
||||
|
||||
OFFSET.x = longest_y_tic + tic_length + 2 * label_displacement
|
||||
OFFSET.y = font.get_height() + tic_length + label_displacement
|
||||
|
||||
SIZE = get_size() - Vector2(OFFSET.x, 0)
|
||||
origin = Vector2(OFFSET.x, SIZE.y - OFFSET.y)
|
||||
|
||||
|
||||
func count_functions():
|
||||
functions = y_labels.size()
|
||||
|
||||
|
||||
func calculate_pass():
|
||||
if show_x_values_as_labels:
|
||||
x_chors = x_datas.duplicate(true) as PoolStringArray
|
||||
else:
|
||||
x_chors = x_labels
|
||||
|
||||
# calculate distance in pixel between 2 consecutive values/datas
|
||||
# Calculate distance in pixel between 2 consecutive values/datas
|
||||
x_pass = (SIZE.x - OFFSET.x) / (x_chors.size() - 1 if x_chors.size() > 1 else x_chors.size())
|
||||
y_pass = (origin.y - ChartName.get_rect().size.y * 2) / (y_chors.size() - 1)
|
||||
y_pass = (origin.y - ChartName.get_rect().size.y * 2) / (y_chors.size() - 1 if y_chors.size() > 1 else y_chors.size())
|
||||
|
||||
|
||||
func calculate_coordinates():
|
||||
x_coordinates.clear()
|
||||
y_coordinates.clear()
|
||||
point_values.clear()
|
||||
point_positions.clear()
|
||||
|
||||
for cluster in y_datas:
|
||||
var single_coordinates : Array
|
||||
for value in cluster.size():
|
||||
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():
|
||||
x_coordinates.append((x_datas[x] - x_margin_min) * x_pass / h_dist)
|
||||
|
||||
for f in functions:
|
||||
for _i in y_labels.size():
|
||||
point_values.append([])
|
||||
point_positions.append([])
|
||||
|
||||
for cluster in y_coordinates.size():
|
||||
for y in y_coordinates[cluster].size():
|
||||
if are_values_columns:
|
||||
point_values[y].append([x_datas[cluster], y_datas[cluster][y]])
|
||||
point_positions[y].append(Vector2(x_coordinates[cluster] + origin.x,
|
||||
origin.y - y_coordinates[cluster][y]))
|
||||
else:
|
||||
point_values[cluster].append([x_datas[y], y_datas[cluster][y]])
|
||||
point_positions[cluster].append(Vector2(x_coordinates[y] + origin.x,
|
||||
origin.y - y_coordinates[cluster][y]))
|
||||
for function in y_labels.size():
|
||||
for val in x_datas[function].size():
|
||||
var value_x = (x_datas[function][val] - x_margin_min) * x_pass / h_dist if h_dist else 0 \
|
||||
if not show_x_values_as_labels else x_chors.find(String(x_datas[function][val])) * x_pass
|
||||
var value_y = (y_datas[function][val] - y_margin_min) * y_pass / v_dist if v_dist else 0
|
||||
|
||||
point_values[function].append([x_datas[function][val], y_datas[function][val]])
|
||||
point_positions[function].append(Vector2(value_x + origin.x, origin.y - value_y))
|
||||
|
||||
|
||||
|
||||
func draw_grid():
|
||||
# ascisse
|
||||
for p in x_chors.size():
|
||||
var point : Vector2 = origin+Vector2((p)*x_pass,0)
|
||||
var point : Vector2 = origin + Vector2(p * x_pass, 0)
|
||||
var size_text : Vector2 = font.get_string_size(x_chors[p])
|
||||
# v grid
|
||||
draw_line(point,point-Vector2(0,SIZE.y-OFFSET.y),v_lines_color,0.2,true)
|
||||
draw_line(point, point - Vector2(0, SIZE.y - OFFSET.y), v_lines_color, grid_lines_width, true)
|
||||
# ascisse
|
||||
draw_line(point-Vector2(0,5),point,v_lines_color,1,true)
|
||||
draw_string(font,point+Vector2(-const_width/2*x_chors[p].length(),font_size+const_height),x_chors[p],font_color)
|
||||
draw_line(point + Vector2(0, tic_length), point, v_lines_color, grid_lines_width, true)
|
||||
draw_string(font, point + Vector2(-size_text.x / 2, size_text.y + tic_length),
|
||||
x_chors[p], font_color)
|
||||
|
||||
# ordinate
|
||||
for p in y_chors.size():
|
||||
var point : Vector2 = origin-Vector2(0,(p)*y_pass)
|
||||
var point : Vector2 = origin - Vector2(0, p * y_pass)
|
||||
var size_text := Vector2(font.get_string_size(y_chors[p]).x, font.get_ascent()) #The y should be ascent instead full height to get correctly centered
|
||||
|
||||
# h grid
|
||||
draw_line(point,point+Vector2(SIZE.x-OFFSET.x,0),h_lines_color,0.2,true)
|
||||
draw_line(point, point + Vector2(SIZE.x - OFFSET.x, 0), h_lines_color, grid_lines_width, true)
|
||||
# ordinate
|
||||
draw_line(point,point+Vector2(5,0),h_lines_color,1,true)
|
||||
draw_string(font,point-Vector2(y_chors[p].length()*const_width+font_size,-const_height),y_chors[p],font_color)
|
||||
draw_line(point, point + Vector2(-tic_length, 0), h_lines_color, grid_lines_width, true)
|
||||
draw_string(font, point + Vector2(-size_text.x - tic_length - label_displacement,
|
||||
size_text.y / 2), y_chors[p], font_color)
|
||||
|
||||
|
||||
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_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)
|
||||
|
||||
|
||||
func draw_points():
|
||||
var defined_colors : bool = false
|
||||
if function_colors.size():
|
||||
defined_colors = true
|
||||
|
||||
for _function in point_values.size():
|
||||
for function in point_values.size():
|
||||
var PointContainer : Control = Control.new()
|
||||
Points.add_child(PointContainer)
|
||||
|
||||
for function_point in point_values[_function].size():
|
||||
for function_point in point_values[function].size():
|
||||
var point : Point = point_node.instance()
|
||||
point.connect("_point_pressed",self,"point_pressed")
|
||||
point.connect("_mouse_entered",self,"show_data")
|
||||
point.connect("_mouse_exited",self,"hide_data")
|
||||
|
||||
point.create_point(points_shape[_function], function_colors[_function],
|
||||
Color.white, point_positions[_function][function_point],
|
||||
point.format_value(point_values[_function][function_point], false, false),
|
||||
y_labels[_function] as String)
|
||||
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)
|
||||
|
||||
@ -329,3 +623,25 @@ func draw_treshold():
|
||||
draw_line(Vector2(origin.x, treshold_draw.y), Vector2(SIZE.x, treshold_draw.y), Color.red, 0.4, true)
|
||||
if treshold.x != 0:
|
||||
draw_line(Vector2(treshold_draw.x, 0), Vector2(treshold_draw.x, SIZE.y - OFFSET.y), Color.red, 0.4, true)
|
||||
|
||||
|
||||
func calculate_position_significant_figure(number):
|
||||
return floor(log(abs(number))/log(10) + 1) #If number = 0 Godot returns -#INF and it behaves correctly on the pow call on calculate_tics
|
||||
|
||||
|
||||
func calculate_interval_tics(v_from:float, v_to:float, dist:float, chords:Array, include_first := true):
|
||||
# Appends to array chords the tics calculated between v_from and v_to with
|
||||
# a given distance between tics.
|
||||
#include_first is used to tell if v_from should be appended or ignored
|
||||
|
||||
var multi = 0
|
||||
var p = (dist * multi) + v_from
|
||||
var missing_tics = p < v_to if dist > 0 else p > v_to
|
||||
if include_first:
|
||||
chords.append(p)
|
||||
|
||||
while missing_tics:
|
||||
multi += 1
|
||||
p = (dist * multi) + v_from
|
||||
missing_tics = p < v_to if dist > 0 else p > v_to
|
||||
chords.append(p)
|
||||
|
Loading…
Reference in New Issue
Block a user