Added basic coaster physics and graph
This commit is contained in:
parent
f8ffcbb6be
commit
c91cb455c2
3
.github/FUNDING.yml
vendored
Normal file
3
.github/FUNDING.yml
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# These are supported funding model platforms
|
||||
|
||||
github: fenix-hub
|
||||
32
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
32
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: "[BUG]"
|
||||
labels: bug
|
||||
assignees: fenix-hub
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. Windows]
|
||||
- Godot Engine Version [e.g. 3.2.3rc6]
|
||||
- Plugin Version [e.g. v1.7.0]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
17
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
17
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: "[FEATURE]"
|
||||
labels: enhancement
|
||||
assignees: fenix-hub
|
||||
|
||||
---
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe an implementation suggestion**
|
||||
A clear and concise description of any implementation method you'd like to suggest. It may be technical or just theoretical.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
83
Cart/cart.gd
83
Cart/cart.gd
|
|
@ -5,21 +5,86 @@ class_name Cart
|
|||
|
||||
|
||||
var going = false
|
||||
var progress = 0
|
||||
var progress : float = 0
|
||||
var velocity = 0
|
||||
var initial_velocity = 10
|
||||
var speed_multiplier = 0.1
|
||||
var initial_height = 0
|
||||
var last_velocity = 0
|
||||
|
||||
var time = 0
|
||||
var function : Function
|
||||
var first_tick = false
|
||||
|
||||
@export var chart : Chart
|
||||
|
||||
func _ready():
|
||||
hide()
|
||||
function = Function.new(
|
||||
[0],
|
||||
[0],
|
||||
"Acceleration",
|
||||
{
|
||||
type = Function.Type.LINE,
|
||||
marker = Function.Marker.SQUARE,
|
||||
color = Color.WHITE,
|
||||
interpolation = Function.Interpolation.SPLINE
|
||||
}
|
||||
)
|
||||
var cp: ChartProperties = ChartProperties.new()
|
||||
cp.colors.frame = Color("#161a1d")
|
||||
cp.colors.background = Color.TRANSPARENT
|
||||
cp.colors.grid = Color("#283442")
|
||||
cp.colors.ticks = Color("#283442")
|
||||
cp.colors.text = Color.WHITE_SMOKE
|
||||
cp.draw_bounding_box = false
|
||||
cp.title = "Roller Coaster Acceleration"
|
||||
cp.x_label = "Time"
|
||||
cp.y_label = "Acceleration (m/s^2)"
|
||||
cp.x_scale = 5
|
||||
cp.y_scale = 10
|
||||
cp.interactive = true
|
||||
cp.max_samples = 1000
|
||||
|
||||
chart.plot([function], cp)
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
if len(spline.curve_points) > 1:
|
||||
position = spline.curve_points[progress]
|
||||
rotation = (spline.curve_points[progress+1] - position).angle()
|
||||
func start():
|
||||
first_tick = true
|
||||
for i in range(1000):
|
||||
function.remove_point(0)
|
||||
|
||||
if not going:
|
||||
return
|
||||
initial_height = position.y - initial_velocity
|
||||
last_velocity = sqrt(2*9.8*(position.y-initial_height))*speed_multiplier
|
||||
velocity = 0
|
||||
going = true
|
||||
time = 0
|
||||
show()
|
||||
|
||||
|
||||
progress += 1
|
||||
if progress >= len(spline.curve_points)-1:
|
||||
func _physics_process(delta: float) -> void:
|
||||
var new_point = spline.get_point_at(progress)
|
||||
|
||||
if new_point != null:
|
||||
position = new_point
|
||||
else:
|
||||
going = false
|
||||
hide()
|
||||
progress = 0
|
||||
|
||||
if not going:
|
||||
return
|
||||
|
||||
last_velocity = velocity
|
||||
velocity = sqrt(2*9.8*(position.y-initial_height))*speed_multiplier
|
||||
progress += velocity
|
||||
time += delta
|
||||
|
||||
if not first_tick:
|
||||
function.add_point(time, velocity-last_velocity)
|
||||
|
||||
chart.queue_redraw()
|
||||
|
||||
if first_tick:
|
||||
first_tick = false
|
||||
|
||||
|
||||
|
|
|
|||
21
LICENSE
Normal file
21
LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2024 周公不解梦
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
74
README.md
Normal file
74
README.md
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
<img src="easy_charts.svg" align="middle">
|
||||
|
||||
> Charts for Godot Engine, made easy.
|
||||
|
||||
> **Note**
|
||||
Looking for 3.x version? 👉 [3.x](https://github.com/fenix-hub/godot-engine.easy-charts/tree/godot-3)
|
||||
|
||||
## How does it work?
|
||||
There is a [WIKI](https://github.com/fenix-hub/godot-engine.easy-charts/wiki) with some tutorials, even if it is a work in progress.
|
||||
You can also find practical examples in `addons/easy_charts/examples/`.
|
||||
|
||||
# Available Charts and when to use them
|
||||
This library offers a set of charts for each main Godot Node:
|
||||
-  **Control Charts:** Fast Charts plotted in a Control Node. They offer basic Control properties, such as Margins, size inheritance and control. No animations, best suited for UIs that rely on Control Node structures.
|
||||
- **2D Charts:** plotted in 2D Nodes. They offer additional tools, such as animations. They can be used to implement more aesthetic charts in 2D contexts. Compatibility not guaranteed in Canvas and Control nodes.
|
||||
- **3D Charts:** Plotted using 3D nodes, but can be used both in 2D and 3D spaces. They offer the possibility to plot 3D datasets, which are common in machine learning contexts or just data analysis. A Camera Control will also be available, which can be used to move around the chart.
|
||||
|
||||
### Available Charts
|
||||
| | Control | 2D | 3D |
|
||||
|--------------|---------|----|----|
|
||||
| ScatterChart | ✅ | ❌ | ❌ |
|
||||
| LineChart | ✅ | ❌ | ❌ |
|
||||
| BarChart | ✅ | ❌ | ❌ |
|
||||
| AreaChart | ✅ | ❌ | ❌ |
|
||||
| PieChart | ✅ | ❌ | ❌ |
|
||||
| RadarChart | ❌ | ❌ | ❌ |
|
||||
| BubbleChart | ❌ | ❌ | ❌ |
|
||||
| DonutChart | ❌ | ❌ | ❌ |
|
||||
| ParliamentChart | ❌ | ❌ | ❌ |
|
||||
| SunburstChart | ❌ | ❌ | ❌ |
|
||||
|
||||
### Some Examples
|
||||
<details>
|
||||
<summary>Realtime LineChart</summary>
|
||||
|
||||

|
||||
</details>
|
||||
<details>
|
||||
<summary>Realtime PieChart</summary>
|
||||
|
||||

|
||||
</details>
|
||||
<details>
|
||||
<summary>RadarChart</summary>
|
||||
|
||||

|
||||
</details>
|
||||
<details>
|
||||
<summary>ScatterChart</summary>
|
||||
|
||||

|
||||
</details>
|
||||
<details>
|
||||
<summary>Composite Chart</summary>
|
||||
|
||||

|
||||
</details>
|
||||
<details>
|
||||
<summary>Multiplot</summary>
|
||||
|
||||

|
||||
</details>
|
||||
|
||||
##### Some references for charts and plots
|
||||
[Flourish](https://app.flourish.studio/projects)
|
||||
[Chart.js](https://www.chartjs.org/samples/latest/)
|
||||
[Google Charts](https://developers.google.com/chart)
|
||||
[plotly](https://plotly.com)
|
||||
[matplotlib](https://matplotlib.org)
|
||||
|
||||
> **Warning**
|
||||
This addon was built for a **personal use** intention. It was released as an open source plugin in the hope that it could be useful to the Godot Engine Community.
|
||||
As a "work in progress" project, there is *no warranty* for any eventual issue and bug that may broke your project.
|
||||
I don't assume any responsibility for possible corruptions of your project. It is always advisable to keep a copy of your project and check any changes you make in your Github repository.
|
||||
|
|
@ -5,3 +5,21 @@ class_name Spline
|
|||
var points : PackedVector2Array
|
||||
|
||||
var curve_points : PackedVector2Array
|
||||
|
||||
func get_point_at(distance : float):
|
||||
var current_point : int = 0
|
||||
while true:
|
||||
if current_point >= len(curve_points)-1:
|
||||
return null
|
||||
|
||||
if distance > curve_points[current_point].distance_to(curve_points[current_point + 1]):
|
||||
distance -= curve_points[current_point].distance_to(curve_points[current_point + 1])
|
||||
else:
|
||||
return lerp(curve_points[current_point],curve_points[current_point+1], distance/(curve_points[current_point].distance_to(curve_points[current_point + 1])))
|
||||
|
||||
current_point += 1
|
||||
|
||||
if distance < 0:
|
||||
break
|
||||
|
||||
return curve_points[current_point]
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ extends Control
|
|||
@export var cart : Cart
|
||||
@export var control_point_scene : PackedScene
|
||||
|
||||
@export var steps : int = 10
|
||||
@onready var check_button : CheckButton = $HBoxContainer/CheckButton
|
||||
|
||||
@export var steps : int = 100
|
||||
|
||||
var editing = true
|
||||
|
||||
|
|
@ -30,6 +32,7 @@ func _process(delta: float) -> void:
|
|||
add_child(new_control_point)
|
||||
update()
|
||||
|
||||
|
||||
func update():
|
||||
spline.points = []
|
||||
for child in get_children():
|
||||
|
|
@ -45,7 +48,7 @@ func update():
|
|||
var b3 : Vector2 = spline.points[i+3]
|
||||
|
||||
var t : float = 0
|
||||
while t < 0.99:
|
||||
while t < 1:
|
||||
# B-spline calculations
|
||||
var calculated_position =\
|
||||
((-b0+3*b1-3*b2+b3)*(t**3) +\
|
||||
|
|
@ -53,19 +56,22 @@ func update():
|
|||
(-3*b0+3*b2)*t +\
|
||||
(b0+4*b1+b2))/6
|
||||
spline.curve_points.append(calculated_position)
|
||||
t += 0.1
|
||||
t += 1.0/steps
|
||||
queue_redraw()
|
||||
|
||||
|
||||
func _on_check_button_toggled(toggled_on: bool) -> void:
|
||||
func set_editing(toggled_on: bool):
|
||||
editing = toggled_on
|
||||
for child in get_children():
|
||||
if not child is ControlPoint:
|
||||
continue
|
||||
child.visible = editing
|
||||
check_button.button_pressed = toggled_on
|
||||
update()
|
||||
|
||||
func _on_check_button_toggled(toggled_on: bool) -> void:
|
||||
set_editing(toggled_on)
|
||||
|
||||
|
||||
func _on_start_pressed() -> void:
|
||||
cart.going = true
|
||||
cart.show()
|
||||
set_editing(false)
|
||||
cart.start()
|
||||
|
|
|
|||
181
addons/easy_charts/control_charts/chart.gd
Normal file
181
addons/easy_charts/control_charts/chart.gd
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
@icon("res://addons/easy_charts/utilities/icons/linechart.svg")
|
||||
extends PanelContainer
|
||||
class_name Chart
|
||||
|
||||
@onready var _canvas: Canvas = $Canvas
|
||||
@onready var plot_box: PlotBox = $"%PlotBox"
|
||||
@onready var grid_box: GridBox = $"%GridBox"
|
||||
@onready var functions_box: Control = $"%FunctionsBox"
|
||||
@onready var function_legend: FunctionLegend = $"%FunctionLegend"
|
||||
|
||||
var functions: Array = []
|
||||
var x: Array = []
|
||||
var y: Array = []
|
||||
|
||||
var x_labels_function: Callable = Callable()
|
||||
var y_labels_function: Callable = Callable()
|
||||
|
||||
var x_domain: Dictionary = {}
|
||||
var y_domain: Dictionary = {}
|
||||
|
||||
var chart_properties: ChartProperties = null
|
||||
|
||||
###########
|
||||
|
||||
func _ready() -> void:
|
||||
if theme == null:
|
||||
theme = Theme.new()
|
||||
|
||||
func plot(functions: Array[Function], properties: ChartProperties = ChartProperties.new()) -> void:
|
||||
self.functions = functions
|
||||
self.chart_properties = properties
|
||||
|
||||
theme.set("default_font", self.chart_properties.font)
|
||||
_canvas.prepare_canvas(self.chart_properties)
|
||||
plot_box.chart_properties = self.chart_properties
|
||||
function_legend.chart_properties = self.chart_properties
|
||||
|
||||
load_functions(functions)
|
||||
|
||||
func get_function_plotter(function: Function) -> FunctionPlotter:
|
||||
var plotter: FunctionPlotter
|
||||
match function.get_type():
|
||||
Function.Type.LINE:
|
||||
plotter = LinePlotter.new(function)
|
||||
Function.Type.AREA:
|
||||
plotter = AreaPlotter.new(function)
|
||||
Function.Type.PIE:
|
||||
plotter = PiePlotter.new(function)
|
||||
Function.Type.BAR:
|
||||
plotter = BarPlotter.new(function)
|
||||
Function.Type.SCATTER, _:
|
||||
plotter = ScatterPlotter.new(function)
|
||||
return plotter
|
||||
|
||||
func load_functions(functions: Array[Function]) -> void:
|
||||
self.x = []
|
||||
self.y = []
|
||||
|
||||
function_legend.clear()
|
||||
|
||||
# Remove existing function_plotters
|
||||
for function_plotter in functions_box.get_children():
|
||||
functions_box.remove_child(function_plotter)
|
||||
function_plotter.queue_free()
|
||||
|
||||
for function in functions:
|
||||
# Load x and y values
|
||||
self.x.append(function.__x)
|
||||
self.y.append(function.__y)
|
||||
|
||||
# Create FunctionPlotter
|
||||
var function_plotter: FunctionPlotter = get_function_plotter(function)
|
||||
function_plotter.connect("point_entered", Callable(plot_box, "_on_point_entered"))
|
||||
function_plotter.connect("point_exited", Callable(plot_box, "_on_point_exited"))
|
||||
functions_box.add_child(function_plotter)
|
||||
|
||||
# Create legend
|
||||
match function.get_type():
|
||||
Function.Type.PIE:
|
||||
for i in function.__x.size():
|
||||
var interp_color: Color = function.get_gradient().sample(float(i) / float(function.__x.size()))
|
||||
function_legend.add_label(function.get_type(), interp_color, Function.Marker.NONE, function.__y[i])
|
||||
_:
|
||||
function_legend.add_function(function)
|
||||
|
||||
func _draw() -> void:
|
||||
if (x.size() == 0) or (y.size() == 0) or (x.size() == 1 and x[0].is_empty()) or (y.size() == 1 and y[0].is_empty()):
|
||||
printerr("Cannot plot an empty function!")
|
||||
return
|
||||
|
||||
var is_x_fixed: bool = x_domain.get("fixed", false)
|
||||
var is_y_fixed: bool = y_domain.get("fixed", false)
|
||||
|
||||
# GridBox
|
||||
if not is_x_fixed or not is_y_fixed :
|
||||
if chart_properties.max_samples > 0 :
|
||||
var _x: Array = []
|
||||
var _y: Array = []
|
||||
|
||||
_x.resize(x.size())
|
||||
_y.resize(y.size())
|
||||
|
||||
for i in x.size():
|
||||
if not is_x_fixed:
|
||||
_x[i] = x[i].slice(max(0, x[i].size() - chart_properties.max_samples), x[i].size())
|
||||
if not is_y_fixed:
|
||||
_y[i] = y[i].slice(max(0, y[i].size() - chart_properties.max_samples), y[i].size())
|
||||
|
||||
if not is_x_fixed:
|
||||
x_domain = calculate_domain(_x)
|
||||
if not is_y_fixed:
|
||||
y_domain = calculate_domain(_y)
|
||||
else:
|
||||
if not is_x_fixed:
|
||||
x_domain = calculate_domain(x)
|
||||
if not is_y_fixed:
|
||||
y_domain = calculate_domain(y)
|
||||
|
||||
# Update values for the PlotBox in order to propagate them to the children
|
||||
update_plotbox(x_domain, y_domain, x_labels_function, y_labels_function)
|
||||
|
||||
# Update GridBox
|
||||
update_gridbox(x_domain, y_domain, x_labels_function, y_labels_function)
|
||||
|
||||
# Update each FunctionPlotter in FunctionsBox
|
||||
for function_plotter in functions_box.get_children():
|
||||
if function_plotter is FunctionPlotter:
|
||||
function_plotter.visible = function_plotter.function.get_visibility()
|
||||
if function_plotter.function.get_visibility():
|
||||
function_plotter.update_values(x_domain, y_domain)
|
||||
|
||||
func calculate_domain(values: Array) -> Dictionary:
|
||||
for value_array in values:
|
||||
if ECUtilities._contains_string(value_array):
|
||||
return { lb = 0.0, ub = (value_array.size() - 1), has_decimals = false , fixed = false }
|
||||
var min_max: Dictionary = ECUtilities._find_min_max(values)
|
||||
|
||||
if not chart_properties.smooth_domain:
|
||||
return { lb = min_max.min, ub = min_max.max, has_decimals = ECUtilities._has_decimals(values), fixed = false }
|
||||
else:
|
||||
return { lb = ECUtilities._round_min(min_max.min), ub = ECUtilities._round_max(min_max.max), has_decimals = ECUtilities._has_decimals(values) , fixed = false }
|
||||
|
||||
func set_x_domain(lb: Variant, ub: Variant) -> void:
|
||||
x_domain = { lb = lb, ub = ub, has_decimals = ECUtilities._has_decimals([lb, ub]), fixed = true }
|
||||
|
||||
func set_y_domain(lb: Variant, ub: Variant) -> void:
|
||||
y_domain = { lb = lb, ub = ub, has_decimals = ECUtilities._has_decimals([lb, ub]), fixed = true }
|
||||
|
||||
func update_plotbox(x_domain: Dictionary, y_domain: Dictionary, x_labels_function: Callable, y_labels_function: Callable) -> void:
|
||||
plot_box.box_margins = calculate_plotbox_margins(x_domain, y_domain)
|
||||
plot_box.set_labels_functions(x_labels_function, y_labels_function)
|
||||
|
||||
func update_gridbox(x_domain: Dictionary, y_domain: Dictionary, x_labels_function: Callable, y_labels_function: Callable) -> void:
|
||||
grid_box.set_domains(x_domain, y_domain)
|
||||
grid_box.set_labels_functions(x_labels_function, y_labels_function)
|
||||
grid_box.queue_redraw()
|
||||
|
||||
func calculate_plotbox_margins(x_domain: Dictionary, y_domain: Dictionary) -> Vector2:
|
||||
var plotbox_margins: Vector2 = Vector2(
|
||||
chart_properties.x_tick_size,
|
||||
chart_properties.y_tick_size
|
||||
)
|
||||
|
||||
if chart_properties.show_tick_labels:
|
||||
var x_ticklabel_size: Vector2
|
||||
var y_ticklabel_size: Vector2
|
||||
|
||||
var y_max_formatted: String = ECUtilities._format_value(y_domain.ub, y_domain.has_decimals)
|
||||
if y_domain.lb < 0: # negative number
|
||||
var y_min_formatted: String = ECUtilities._format_value(y_domain.lb, y_domain.has_decimals)
|
||||
if y_min_formatted.length() >= y_max_formatted.length():
|
||||
y_ticklabel_size = chart_properties.get_string_size(y_min_formatted)
|
||||
else:
|
||||
y_ticklabel_size = chart_properties.get_string_size(y_max_formatted)
|
||||
else:
|
||||
y_ticklabel_size = chart_properties.get_string_size(y_max_formatted)
|
||||
|
||||
plotbox_margins.x += y_ticklabel_size.x + chart_properties.x_ticklabel_space
|
||||
plotbox_margins.y += ThemeDB.fallback_font_size + chart_properties.y_ticklabel_space
|
||||
|
||||
return plotbox_margins
|
||||
1
addons/easy_charts/control_charts/chart.gd.uid
Normal file
1
addons/easy_charts/control_charts/chart.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://0pw0m252abmq
|
||||
104
addons/easy_charts/control_charts/chart.tscn
Normal file
104
addons/easy_charts/control_charts/chart.tscn
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
[gd_scene format=3 uid="uid://dlwq4kmdb3bhs"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://0pw0m252abmq" path="res://addons/easy_charts/control_charts/chart.gd" id="1"]
|
||||
[ext_resource type="PackedScene" path="res://addons/easy_charts/utilities/containers/data_tooltip/data_tooltip.tscn" id="2"]
|
||||
[ext_resource type="Script" uid="uid://d0q44x2e5wxym" path="res://addons/easy_charts/utilities/containers/canvas/canvas.gd" id="3"]
|
||||
[ext_resource type="Script" uid="uid://bt0h3q1ocyhu8" path="res://addons/easy_charts/utilities/containers/canvas/plot_box/plot_box.gd" id="4"]
|
||||
[ext_resource type="Script" uid="uid://oql4qoedksyg" path="res://addons/easy_charts/utilities/containers/canvas/plot_box/grid_box.gd" id="5"]
|
||||
[ext_resource type="PackedScene" path="res://addons/easy_charts/utilities/containers/legend/function_legend.tscn" id="6"]
|
||||
|
||||
[sub_resource type="Theme" id="4"]
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="8"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="5"]
|
||||
content_margin_left = 15.0
|
||||
content_margin_top = 15.0
|
||||
content_margin_right = 15.0
|
||||
content_margin_bottom = 15.0
|
||||
draw_center = false
|
||||
|
||||
[node name="Chart" type="PanelContainer" unique_id=1786449154]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
mouse_filter = 1
|
||||
theme = SubResource("4")
|
||||
theme_override_styles/panel = SubResource("8")
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="Canvas" type="PanelContainer" parent="." unique_id=1915297635]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
mouse_filter = 1
|
||||
theme_override_styles/panel = SubResource("5")
|
||||
script = ExtResource("3")
|
||||
|
||||
[node name="CanvasContainer" type="VBoxContainer" parent="Canvas" unique_id=217645487]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
theme_override_constants/separation = 10
|
||||
|
||||
[node name="Title" type="Label" parent="Canvas/CanvasContainer" unique_id=2011788518]
|
||||
layout_mode = 2
|
||||
text = "{title}"
|
||||
|
||||
[node name="DataContainer" type="HBoxContainer" parent="Canvas/CanvasContainer" unique_id=1255003439]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
theme_override_constants/separation = 10
|
||||
|
||||
[node name="YLabel" type="Label" parent="Canvas/CanvasContainer/DataContainer" unique_id=1497055450]
|
||||
layout_mode = 2
|
||||
text = "{ylabel}"
|
||||
|
||||
[node name="PlotContainer" type="VBoxContainer" parent="Canvas/CanvasContainer/DataContainer" unique_id=462879481]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="PlotBox" type="Control" parent="Canvas/CanvasContainer/DataContainer/PlotContainer" unique_id=1084807178]
|
||||
unique_name_in_owner = true
|
||||
clip_contents = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
script = ExtResource("4")
|
||||
|
||||
[node name="GridBox" type="Control" parent="Canvas/CanvasContainer/DataContainer/PlotContainer/PlotBox" unique_id=956292477]
|
||||
unique_name_in_owner = true
|
||||
anchors_preset = 0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
mouse_filter = 2
|
||||
script = ExtResource("5")
|
||||
|
||||
[node name="FunctionsBox" type="Control" parent="Canvas/CanvasContainer/DataContainer/PlotContainer/PlotBox" unique_id=248020886]
|
||||
unique_name_in_owner = true
|
||||
anchors_preset = 0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
mouse_default_cursor_shape = 3
|
||||
|
||||
[node name="Tooltip" parent="Canvas/CanvasContainer/DataContainer/PlotContainer/PlotBox" unique_id=918153509 instance=ExtResource("2")]
|
||||
layout_mode = 0
|
||||
offset_left = -67.0
|
||||
offset_top = -33.0
|
||||
offset_right = -17.0
|
||||
offset_bottom = 30.0
|
||||
|
||||
[node name="XLabel" type="Label" parent="Canvas/CanvasContainer/DataContainer/PlotContainer" unique_id=1121436047]
|
||||
layout_mode = 2
|
||||
text = "{xlabel}"
|
||||
|
||||
[node name="FunctionLegend" parent="Canvas/CanvasContainer/DataContainer" unique_id=1840811922 instance=ExtResource("6")]
|
||||
unique_name_in_owner = true
|
||||
use_parent_material = true
|
||||
layout_mode = 2
|
||||
33
addons/easy_charts/control_charts/plotters/area_plotter.gd
Normal file
33
addons/easy_charts/control_charts/plotters/area_plotter.gd
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
extends LinePlotter
|
||||
class_name AreaPlotter
|
||||
|
||||
func _init(function: Function) -> void:
|
||||
super(function)
|
||||
pass
|
||||
|
||||
func _draw_areas() -> void:
|
||||
var box: Rect2 = get_box()
|
||||
var fp_augmented: PackedVector2Array = []
|
||||
match function.get_interpolation():
|
||||
Function.Interpolation.LINEAR:
|
||||
fp_augmented = points_positions
|
||||
Function.Interpolation.STAIR:
|
||||
fp_augmented = _get_stair_points()
|
||||
Function.Interpolation.SPLINE:
|
||||
fp_augmented = _get_spline_points()
|
||||
Function.Interpolation.NONE, _:
|
||||
return
|
||||
|
||||
fp_augmented.push_back(Vector2(fp_augmented[-1].x, box.end.y + 80))
|
||||
fp_augmented.push_back(Vector2(fp_augmented[0].x, box.end.y + 80))
|
||||
|
||||
var base_color: Color = function.get_color()
|
||||
var colors: PackedColorArray = []
|
||||
for point in fp_augmented:
|
||||
base_color.a = remap(point.y, box.end.y, box.position.y, 0.0, 0.5)
|
||||
colors.push_back(base_color)
|
||||
draw_polygon(fp_augmented, colors)
|
||||
|
||||
func _draw() -> void:
|
||||
super._draw()
|
||||
_draw_areas()
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://bwpt6ptm4bf7k
|
||||
56
addons/easy_charts/control_charts/plotters/bar_plotter.gd
Normal file
56
addons/easy_charts/control_charts/plotters/bar_plotter.gd
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
extends FunctionPlotter
|
||||
class_name BarPlotter
|
||||
|
||||
|
||||
signal point_entered(point, function)
|
||||
signal point_exited(point, function)
|
||||
|
||||
var bars: PackedVector2Array
|
||||
var bars_rects: Array
|
||||
var focused_bar_midpoint: Point
|
||||
|
||||
var bar_size: float
|
||||
|
||||
func _init(function: Function) -> void:
|
||||
super(function)
|
||||
self.bar_size = function.props.get("bar_size", 5.0)
|
||||
|
||||
func _draw() -> void:
|
||||
super._draw()
|
||||
var box: Rect2 = get_box()
|
||||
var x_sampled_domain: Dictionary = { lb = box.position.x, ub = box.end.x }
|
||||
var y_sampled_domain: Dictionary = { lb = box.end.y, ub = box.position.y }
|
||||
sample(x_sampled_domain, y_sampled_domain)
|
||||
_draw_bars()
|
||||
|
||||
func sample(x_sampled_domain: Dictionary, y_sampled_domain: Dictionary) -> void:
|
||||
bars = []
|
||||
bars_rects = []
|
||||
for i in function.__x.size():
|
||||
var top: Vector2 = Vector2(
|
||||
ECUtilities._map_domain(i, x_domain, x_sampled_domain),
|
||||
ECUtilities._map_domain(function.__y[i], y_domain, y_sampled_domain)
|
||||
)
|
||||
var base: Vector2 = Vector2(top.x, ECUtilities._map_domain(0.0, y_domain, y_sampled_domain))
|
||||
bars.push_back(top)
|
||||
bars.push_back(base)
|
||||
bars_rects.append(Rect2(Vector2(top.x - bar_size, top.y), Vector2(bar_size * 2, base.y - top.y)))
|
||||
|
||||
func _draw_bars() -> void:
|
||||
for bar in bars_rects:
|
||||
draw_rect(bar, function.get_color())
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
if event is InputEventMouse:
|
||||
for i in bars_rects.size():
|
||||
if bars_rects[i].grow(5).abs().has_point(get_relative_position(event.position)):
|
||||
var point: Point = Point.new(bars_rects[i].get_center(), { x = function.__x[i], y = function.__y[i]})
|
||||
if focused_bar_midpoint == point:
|
||||
return
|
||||
else:
|
||||
focused_bar_midpoint = point
|
||||
emit_signal("point_entered", point, function)
|
||||
return
|
||||
# Mouse is not in any point's box
|
||||
emit_signal("point_exited", focused_bar_midpoint, function)
|
||||
focused_bar_midpoint = null
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://dgfyhejfleesp
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
extends Control
|
||||
class_name FunctionPlotter
|
||||
|
||||
var function: Function
|
||||
var x_domain: Dictionary
|
||||
var y_domain: Dictionary
|
||||
|
||||
func _init(function: Function) -> void:
|
||||
self.function = function
|
||||
|
||||
func _ready() -> void:
|
||||
set_process_input(get_chart_properties().interactive)
|
||||
|
||||
func update_values(x_domain: Dictionary, y_domain: Dictionary) -> void:
|
||||
self.visible = self.function.get_visibility()
|
||||
if not self.function.get_visibility():
|
||||
return
|
||||
self.x_domain = x_domain
|
||||
self.y_domain = y_domain
|
||||
queue_redraw()
|
||||
|
||||
func _draw() -> void:
|
||||
return
|
||||
|
||||
func get_box() -> Rect2:
|
||||
return get_parent().get_parent().get_plot_box()
|
||||
|
||||
func get_chart_properties() -> ChartProperties:
|
||||
return get_parent().get_parent().chart_properties
|
||||
|
||||
func get_relative_position(position: Vector2) -> Vector2:
|
||||
return position - global_position
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://dy7l5qjo078mc
|
||||
70
addons/easy_charts/control_charts/plotters/line_plotter.gd
Normal file
70
addons/easy_charts/control_charts/plotters/line_plotter.gd
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
extends ScatterPlotter
|
||||
class_name LinePlotter
|
||||
|
||||
func _init(function: Function) -> void:
|
||||
super(function)
|
||||
|
||||
func _get_spline_points(density: float = 10.0, tension: float = 1) -> PackedVector2Array:
|
||||
var spline_points: PackedVector2Array = []
|
||||
|
||||
var augmented: PackedVector2Array = points_positions
|
||||
var pi: Vector2 = augmented[0] - Vector2(10, -10)
|
||||
var pf: Vector2 = augmented[augmented.size() - 1] + Vector2(10, 10)
|
||||
|
||||
augmented.insert(0, pi)
|
||||
augmented.push_back(pf)
|
||||
|
||||
for p in range(1, augmented.size() - 2, 1) : #(inclusive)
|
||||
for f in range(0, density + 1, 1):
|
||||
spline_points.append(
|
||||
augmented[p].cubic_interpolate(
|
||||
augmented[p + 1],
|
||||
augmented[p - 1],
|
||||
augmented[p + 2],
|
||||
f / density)
|
||||
)
|
||||
|
||||
return spline_points
|
||||
|
||||
|
||||
func _get_stair_points() -> PackedVector2Array:
|
||||
var stair_points: PackedVector2Array = points_positions
|
||||
|
||||
for i in range(points_positions.size() - 1, 0, -1):
|
||||
stair_points.insert(i, Vector2(points_positions[i].x, points_positions[i-1].y))
|
||||
|
||||
return stair_points
|
||||
|
||||
|
||||
func _draw() -> void:
|
||||
super._draw()
|
||||
|
||||
#prevent error when drawing with no data.
|
||||
if points_positions.size() < 2:
|
||||
printerr("Cannot plot a line with less than two points!")
|
||||
return
|
||||
|
||||
match function.get_interpolation():
|
||||
Function.Interpolation.LINEAR:
|
||||
draw_polyline(
|
||||
points_positions,
|
||||
function.get_color(),
|
||||
function.get_line_width(),
|
||||
true
|
||||
)
|
||||
Function.Interpolation.STAIR:
|
||||
draw_polyline(
|
||||
_get_stair_points(),
|
||||
function.get_color(),
|
||||
function.get_line_width(),
|
||||
true
|
||||
)
|
||||
Function.Interpolation.SPLINE:
|
||||
draw_polyline(
|
||||
_get_spline_points(),
|
||||
function.get_color(),
|
||||
function.get_line_width(),
|
||||
true
|
||||
)
|
||||
Function.Interpolation.NONE, _:
|
||||
pass
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://bv0cpnwt5cn64
|
||||
127
addons/easy_charts/control_charts/plotters/pie_plotter.gd
Normal file
127
addons/easy_charts/control_charts/plotters/pie_plotter.gd
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
extends FunctionPlotter
|
||||
class_name PiePlotter
|
||||
|
||||
signal point_entered(slice_mid_point, function, props)
|
||||
signal point_exited(slice_mid_point, function)
|
||||
|
||||
var radius_multiplayer: float = 1.0
|
||||
|
||||
#### INTERNAL
|
||||
var box: Rect2
|
||||
var radius: float
|
||||
|
||||
var slices: Array = []
|
||||
var slices_dirs: PackedVector2Array = []
|
||||
|
||||
var focused_point: Point
|
||||
|
||||
func _init(function: Function) -> void:
|
||||
super(function)
|
||||
pass
|
||||
|
||||
func _draw() -> void:
|
||||
super._draw()
|
||||
box = get_box()
|
||||
radius = min(box.size.x, box.size.y) * 0.5 * radius_multiplayer
|
||||
var total: float = get_total()
|
||||
var ratios: PackedFloat32Array = get_ratios(total)
|
||||
sample(radius, box.get_center(), total, ratios)
|
||||
_draw_pie()
|
||||
_draw_labels(radius, box.get_center(), ratios)
|
||||
|
||||
func get_total() -> float:
|
||||
# Calculate total and ratios
|
||||
var total: float = 0.0
|
||||
for value in function.__x:
|
||||
total += float(value)
|
||||
return total
|
||||
|
||||
func get_ratios(total: float) -> PackedFloat32Array:
|
||||
var ratios: PackedFloat32Array = []
|
||||
for value in function.__x:
|
||||
ratios.push_back(value / total * 100)
|
||||
return ratios
|
||||
|
||||
func sample(radius: float, center: Vector2, total: float, ratios: PackedFloat32Array) -> void:
|
||||
# Calculate directions
|
||||
slices.clear()
|
||||
slices_dirs = []
|
||||
|
||||
var start_angle: float = 0.0
|
||||
for ratio in ratios:
|
||||
var end_angle: float = start_angle + (2 * PI * float(ratio) * 0.01)
|
||||
slices.append(
|
||||
_calc_circle_arc_poly(
|
||||
center,
|
||||
radius,
|
||||
start_angle,
|
||||
end_angle
|
||||
)
|
||||
)
|
||||
start_angle = end_angle
|
||||
|
||||
for slice in slices:
|
||||
var mid_point: Vector2 = (slice[-1] + slice[1]) / 2
|
||||
draw_circle(mid_point, 5, Color.WHITE)
|
||||
slices_dirs.append(center.direction_to(mid_point))
|
||||
|
||||
func _calc_circle_arc_poly(center: Vector2, radius: float, angle_from: float, angle_to: float) -> PackedVector2Array:
|
||||
var nb_points: int = 64
|
||||
var points_arc: PackedVector2Array = PackedVector2Array()
|
||||
points_arc.push_back(center)
|
||||
|
||||
for i in range(nb_points + 1):
|
||||
var angle_point: float = - (PI / 2) + angle_from + i * (angle_to - angle_from) / nb_points
|
||||
points_arc.push_back(center + (Vector2.RIGHT.rotated(angle_point).normalized() * radius))
|
||||
|
||||
return points_arc
|
||||
|
||||
func _draw_pie() -> void:
|
||||
for i in slices.size():
|
||||
draw_colored_polygon(slices[i], function.get_gradient().sample(float(i) / float(slices.size() - 1)))
|
||||
draw_polyline(slices[i], Color.WHITE, 2.0, true)
|
||||
|
||||
func _draw_labels(radius: float, center: Vector2, ratios: PackedFloat32Array) -> void:
|
||||
for i in slices_dirs.size():
|
||||
var ratio_lbl: String = "%.1f%%" % ratios[i]
|
||||
var value_lbl: String = "(%s)" % function.__x[i]
|
||||
var position: Vector2 = center + slices_dirs[i] * radius * 0.5
|
||||
var ratio_lbl_size: Vector2 = get_chart_properties().get_string_size(ratio_lbl)
|
||||
var value_lbl_size: Vector2 = get_chart_properties().get_string_size(value_lbl)
|
||||
draw_string(
|
||||
get_chart_properties().font,
|
||||
position - Vector2(ratio_lbl_size.x / 2, 0),
|
||||
ratio_lbl,
|
||||
HORIZONTAL_ALIGNMENT_CENTER,
|
||||
ratio_lbl_size.x,
|
||||
16,
|
||||
Color.WHITE,
|
||||
3,
|
||||
TextServer.DIRECTION_AUTO,
|
||||
TextServer.ORIENTATION_HORIZONTAL
|
||||
)
|
||||
draw_string(
|
||||
get_chart_properties().font,
|
||||
position - Vector2(value_lbl_size.x / 2, - value_lbl_size.y),
|
||||
value_lbl,
|
||||
HORIZONTAL_ALIGNMENT_CENTER,
|
||||
value_lbl_size.x,
|
||||
16,
|
||||
Color.WHITE,
|
||||
3,TextServer.DIRECTION_AUTO,TextServer.ORIENTATION_HORIZONTAL
|
||||
)
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
if event is InputEventMouse:
|
||||
for i in slices.size():
|
||||
if Geometry2D.is_point_in_polygon(get_relative_position(event.position), slices[i]):
|
||||
var point: Point = Point.new(self.box.get_center() + slices_dirs[i] * self.radius * 0.5, { x = function.__x[i], y = function.__y[i] })
|
||||
if focused_point == point:
|
||||
return
|
||||
else:
|
||||
focused_point = point
|
||||
emit_signal("point_entered", focused_point, function, { interpolation_index = float(i) / float(slices.size() - 1)})
|
||||
return
|
||||
# Mouse is not in any slice's box
|
||||
emit_signal("point_exited", focused_point, function)
|
||||
focused_point = null
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://bkthewhd8mb
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
extends FunctionPlotter
|
||||
class_name ScatterPlotter
|
||||
|
||||
signal point_entered(point, function)
|
||||
signal point_exited(point, function)
|
||||
|
||||
var points: Array[Point]
|
||||
var points_positions: PackedVector2Array
|
||||
var focused_point: Point
|
||||
|
||||
var point_size: float
|
||||
|
||||
func _init(function: Function) -> void:
|
||||
super(function)
|
||||
self.point_size = function.props.get("point_size", 3.0)
|
||||
|
||||
func _draw() -> void:
|
||||
super._draw()
|
||||
|
||||
var box: Rect2 = get_box()
|
||||
var x_sampled_domain: Dictionary = { lb = box.position.x, ub = box.end.x }
|
||||
var y_sampled_domain: Dictionary = { lb = box.end.y, ub = box.position.y }
|
||||
sample(x_sampled_domain, y_sampled_domain)
|
||||
|
||||
if function.get_marker() != Function.Marker.NONE:
|
||||
for point in points:
|
||||
# Don't plot points outside domain upper and lower bounds!
|
||||
if point.position.y <= y_sampled_domain.lb and point.position.y >= y_sampled_domain.ub:
|
||||
draw_function_point(point.position)
|
||||
|
||||
func sample(x_sampled_domain: Dictionary, y_sampled_domain: Dictionary) -> void:
|
||||
points = []
|
||||
points_positions = []
|
||||
var lower_bound: int = max(0, function.__x.size() - get_chart_properties().max_samples) \
|
||||
#disable sample display limits
|
||||
if get_chart_properties().max_samples > 0 \
|
||||
else 0
|
||||
for i in range(lower_bound, function.__x.size()):
|
||||
var _position: Vector2 = Vector2(
|
||||
ECUtilities._map_domain(float(function.__x[i]), x_domain, x_sampled_domain),
|
||||
ECUtilities._map_domain(float(function.__y[i]), y_domain, y_sampled_domain)
|
||||
)
|
||||
points.push_back(Point.new(_position, { x = function.__x[i], y = function.__y[i] }))
|
||||
points_positions.push_back(_position)
|
||||
|
||||
func draw_function_point(point_position: Vector2) -> void:
|
||||
match function.get_marker():
|
||||
Function.Marker.SQUARE:
|
||||
draw_rect(
|
||||
Rect2(point_position - (Vector2.ONE * point_size), (Vector2.ONE * point_size * 2)),
|
||||
function.get_color(), true, 1.0
|
||||
)
|
||||
Function.Marker.TRIANGLE:
|
||||
draw_colored_polygon(
|
||||
PackedVector2Array([
|
||||
point_position + (Vector2.UP * point_size * 1.3),
|
||||
point_position + (Vector2.ONE * point_size * 1.3),
|
||||
point_position - (Vector2(1, -1) * point_size * 1.3)
|
||||
]), function.get_color(), [], null
|
||||
)
|
||||
Function.Marker.CROSS:
|
||||
draw_line(
|
||||
point_position - (Vector2.ONE * point_size),
|
||||
point_position + (Vector2.ONE * point_size),
|
||||
function.get_color(), point_size, true
|
||||
)
|
||||
draw_line(
|
||||
point_position + (Vector2(1, -1) * point_size),
|
||||
point_position + (Vector2(-1, 1) * point_size),
|
||||
function.get_color(), point_size / 2, true
|
||||
)
|
||||
Function.Marker.CIRCLE, _:
|
||||
draw_circle(point_position, point_size, function.get_color())
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
if event is InputEventMouse:
|
||||
for point in points:
|
||||
if Geometry2D.is_point_in_circle(get_relative_position(event.position), point.position, self.point_size * 4):
|
||||
if focused_point == point:
|
||||
return
|
||||
else:
|
||||
focused_point = point
|
||||
emit_signal("point_entered", point, function)
|
||||
return
|
||||
# Mouse is not in any point's box
|
||||
emit_signal("point_exited", focused_point, function)
|
||||
focused_point = null
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://t4lofek667rq
|
||||
69
addons/easy_charts/examples/area_chart/Control.gd
Normal file
69
addons/easy_charts/examples/area_chart/Control.gd
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
extends Control
|
||||
|
||||
@onready var chart: Chart = $VBoxContainer/Chart
|
||||
|
||||
# This Chart will plot 3 different functions
|
||||
var f1: Function
|
||||
|
||||
func _ready():
|
||||
# Let's create our @x values
|
||||
var x: Array = ArrayOperations.multiply_float(range(-10, 11, 1), 0.5)
|
||||
|
||||
# And our y values. It can be an n-size array of arrays.
|
||||
# NOTE: `x.size() == y.size()` or `x.size() == y[n].size()`
|
||||
var y: Array = ArrayOperations.multiply_int(ArrayOperations.cos(x), 20)
|
||||
|
||||
# Let's customize the chart properties, which specify how the chart
|
||||
# should look, plus some additional elements like labels, the scale, etc...
|
||||
var cp: ChartProperties = ChartProperties.new()
|
||||
cp.colors.frame = Color("#161a1d")
|
||||
cp.colors.background = Color.TRANSPARENT
|
||||
cp.colors.grid = Color("#283442")
|
||||
cp.colors.ticks = Color("#283442")
|
||||
cp.colors.text = Color.WHITE_SMOKE
|
||||
cp.draw_bounding_box = false
|
||||
cp.title = "Air Quality Monitoring"
|
||||
cp.x_label = "Time"
|
||||
cp.y_label = "Sensor values"
|
||||
cp.x_scale = 5
|
||||
cp.y_scale = 10
|
||||
cp.interactive = true # false by default, it allows the chart to create a tooltip to show point values
|
||||
# and interecept clicks on the plot
|
||||
|
||||
# Let's add values to our functions
|
||||
f1 = Function.new(
|
||||
x, y, "Pressure", # This will create a function with x and y values taken by the Arrays
|
||||
# we have created previously. This function will also be named "Pressure"
|
||||
# as it contains 'pressure' values.
|
||||
# If set, the name of a function will be used both in the Legend
|
||||
# (if enabled thourgh ChartProperties) and on the Tooltip (if enabled).
|
||||
# Let's also provide a dictionary of configuration parameters for this specific function.
|
||||
{
|
||||
color = Color("#36a2eb"), # The color associated to this function
|
||||
marker = Function.Marker.NONE, # The marker that will be displayed for each drawn point (x,y)
|
||||
# since it is `NONE`, no marker will be shown.
|
||||
type = Function.Type.AREA, # This defines what kind of plotting will be used,
|
||||
# in this case it will be an Area Chart.
|
||||
}
|
||||
)
|
||||
|
||||
# Now let's plot our data
|
||||
chart.plot([f1], cp)
|
||||
|
||||
# Uncommenting this line will show how real time data plotting works
|
||||
set_process(false)
|
||||
|
||||
|
||||
var new_val: float = 4.5
|
||||
|
||||
func _process(delta: float):
|
||||
# This function updates the values of a function and then updates the plot
|
||||
new_val += 5
|
||||
|
||||
# we can use the `Function.add_point(x, y)` method to update a function
|
||||
f1.add_point(new_val, cos(new_val) * 20)
|
||||
chart.queue_redraw() # This will force the Chart to be updated
|
||||
|
||||
|
||||
func _on_CheckButton_pressed():
|
||||
set_process(not is_processing())
|
||||
1
addons/easy_charts/examples/area_chart/Control.gd.uid
Normal file
1
addons/easy_charts/examples/area_chart/Control.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://lyegfrrwjwij
|
||||
48
addons/easy_charts/examples/area_chart/Control.tscn
Normal file
48
addons/easy_charts/examples/area_chart/Control.tscn
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
[gd_scene load_steps=4 format=3 uid="uid://c2ymglyg812ss"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/easy_charts/examples/area_chart/Control.gd" id="1"]
|
||||
[ext_resource type="PackedScene" uid="uid://dlwq4kmdb3bhs" path="res://addons/easy_charts/control_charts/chart.tscn" id="2"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="1"]
|
||||
content_margin_right = 5.0
|
||||
content_margin_bottom = 5.0
|
||||
draw_center = false
|
||||
border_width_right = 2
|
||||
border_width_bottom = 2
|
||||
border_color = Color(0, 0, 0, 1)
|
||||
|
||||
[node name="Control" type="Control"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
||||
layout_mode = 0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
|
||||
[node name="CheckButton" type="CheckButton" parent="VBoxContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_pressed_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_hover_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_hover_pressed_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_focus_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_disabled_color = Color(0, 0, 0, 1)
|
||||
text = "Start Relatime Plotting"
|
||||
|
||||
[node name="Chart" parent="VBoxContainer" instance=ExtResource("2")]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 8
|
||||
theme_override_colors/font_color = Color(0, 0, 0, 1)
|
||||
theme_override_styles/normal = SubResource("1")
|
||||
text = "Try to scale the window!"
|
||||
|
||||
[connection signal="pressed" from="VBoxContainer/CheckButton" to="." method="_on_CheckButton_pressed"]
|
||||
63
addons/easy_charts/examples/bar_chart/Control.gd
Normal file
63
addons/easy_charts/examples/bar_chart/Control.gd
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
extends Control
|
||||
|
||||
@onready var chart: Chart = $VBoxContainer/Chart
|
||||
|
||||
# This Chart will plot 3 different functions
|
||||
var f1: Function
|
||||
|
||||
func _ready():
|
||||
# Let's create our @x values
|
||||
var x: Array = ["Day 1", "Day 2", "Day 3", "Day 4"]
|
||||
|
||||
# And our y values. It can be an n-size array of arrays.
|
||||
# NOTE: `x.size() == y.size()` or `x.size() == y[n].size()`
|
||||
var y: Array = [20, 10, 50, 30]
|
||||
|
||||
# Let's customize the chart properties, which specify how the chart
|
||||
# should look, plus some additional elements like labels, the scale, etc...
|
||||
var cp: ChartProperties = ChartProperties.new()
|
||||
cp.colors.frame = Color("#161a1d")
|
||||
cp.colors.background = Color.TRANSPARENT
|
||||
cp.colors.grid = Color("#283442")
|
||||
cp.colors.ticks = Color("#283442")
|
||||
cp.colors.text = Color.WHITE_SMOKE
|
||||
cp.y_scale = 10
|
||||
cp.draw_origin = true
|
||||
cp.draw_bounding_box = false
|
||||
cp.draw_vertical_grid = false
|
||||
cp.interactive = true # false by default, it allows the chart to create a tooltip to show point values
|
||||
# and interecept clicks on the plot
|
||||
|
||||
# Let's add values to our functions
|
||||
f1 = Function.new(
|
||||
x, y, "User", # This will create a function with x and y values taken by the Arrays
|
||||
# we have created previously. This function will also be named "Pressure"
|
||||
# as it contains 'pressure' values.
|
||||
# If set, the name of a function will be used both in the Legend
|
||||
# (if enabled thourgh ChartProperties) and on the Tooltip (if enabled).
|
||||
{
|
||||
type = Function.Type.BAR,
|
||||
bar_size = 5
|
||||
}
|
||||
)
|
||||
|
||||
# Now let's plot our data
|
||||
chart.plot([f1], cp)
|
||||
|
||||
# Uncommenting this line will show how real time data plotting works
|
||||
set_process(false)
|
||||
|
||||
|
||||
var new_val: float = 4.5
|
||||
|
||||
func _process(delta: float):
|
||||
# This function updates the values of a function and then updates the plot
|
||||
new_val += 5
|
||||
|
||||
# we can use the `Function.add_point(x, y)` method to update a function
|
||||
f1.add_point(new_val, cos(new_val) * 20)
|
||||
chart.queue_redraw() # This will force the Chart to be updated
|
||||
|
||||
|
||||
func _on_CheckButton_pressed():
|
||||
set_process(not is_processing())
|
||||
1
addons/easy_charts/examples/bar_chart/Control.gd.uid
Normal file
1
addons/easy_charts/examples/bar_chart/Control.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://xrknxgy8l3ev
|
||||
48
addons/easy_charts/examples/bar_chart/Control.tscn
Normal file
48
addons/easy_charts/examples/bar_chart/Control.tscn
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
[gd_scene load_steps=4 format=3 uid="uid://dbtgnlgjeoxj4"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/easy_charts/examples/bar_chart/Control.gd" id="1"]
|
||||
[ext_resource type="PackedScene" uid="uid://dlwq4kmdb3bhs" path="res://addons/easy_charts/control_charts/chart.tscn" id="2"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="1"]
|
||||
content_margin_right = 5.0
|
||||
content_margin_bottom = 5.0
|
||||
draw_center = false
|
||||
border_width_right = 2
|
||||
border_width_bottom = 2
|
||||
border_color = Color(0, 0, 0, 1)
|
||||
|
||||
[node name="Control2" type="Control"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
||||
layout_mode = 0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
|
||||
[node name="CheckButton" type="CheckButton" parent="VBoxContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_pressed_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_hover_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_hover_pressed_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_focus_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_disabled_color = Color(0, 0, 0, 1)
|
||||
text = "Start Relatime Plotting"
|
||||
|
||||
[node name="Chart" parent="VBoxContainer" instance=ExtResource("2")]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 8
|
||||
theme_override_colors/font_color = Color(0, 0, 0, 1)
|
||||
theme_override_styles/normal = SubResource("1")
|
||||
text = "Try to scale the window!"
|
||||
|
||||
[connection signal="pressed" from="VBoxContainer/CheckButton" to="." method="_on_CheckButton_pressed"]
|
||||
72
addons/easy_charts/examples/line_chart/Control.gd
Normal file
72
addons/easy_charts/examples/line_chart/Control.gd
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
extends Control
|
||||
|
||||
@onready var chart: Chart = $VBoxContainer/Chart
|
||||
|
||||
# This Chart will plot 3 different functions
|
||||
var f1: Function
|
||||
|
||||
func _ready():
|
||||
# Let's create our @x values
|
||||
var x: PackedFloat32Array = ArrayOperations.multiply_float(range(-10, 11, 1), 0.5)
|
||||
|
||||
# And our y values. It can be an n-size array of arrays.
|
||||
# NOTE: `x.size() == y.size()` or `x.size() == y[n].size()`
|
||||
var y: Array = ArrayOperations.multiply_int(ArrayOperations.cos(x), 20)
|
||||
|
||||
# Let's customize the chart properties, which specify how the chart
|
||||
# should look, plus some additional elements like labels, the scale, etc...
|
||||
var cp: ChartProperties = ChartProperties.new()
|
||||
cp.colors.frame = Color("#161a1d")
|
||||
cp.colors.background = Color.TRANSPARENT
|
||||
cp.colors.grid = Color("#283442")
|
||||
cp.colors.ticks = Color("#283442")
|
||||
cp.colors.text = Color.WHITE_SMOKE
|
||||
cp.draw_bounding_box = false
|
||||
cp.title = "Air Quality Monitoring"
|
||||
cp.x_label = "Time"
|
||||
cp.y_label = "Sensor values"
|
||||
cp.x_scale = 5
|
||||
cp.y_scale = 10
|
||||
cp.interactive = true # false by default, it allows the chart to create a tooltip to show point values
|
||||
# and interecept clicks on the plot
|
||||
|
||||
# Let's add values to our functions
|
||||
f1 = Function.new(
|
||||
x, y, "Pressure", # This will create a function with x and y values taken by the Arrays
|
||||
# we have created previously. This function will also be named "Pressure"
|
||||
# as it contains 'pressure' values.
|
||||
# If set, the name of a function will be used both in the Legend
|
||||
# (if enabled thourgh ChartProperties) and on the Tooltip (if enabled).
|
||||
# Let's also provide a dictionary of configuration parameters for this specific function.
|
||||
{
|
||||
color = Color("#36a2eb"), # The color associated to this function
|
||||
marker = Function.Marker.CIRCLE, # The marker that will be displayed for each drawn point (x,y)
|
||||
# since it is `NONE`, no marker will be shown.
|
||||
type = Function.Type.LINE, # This defines what kind of plotting will be used,
|
||||
# in this case it will be a Linear Chart.
|
||||
interpolation = Function.Interpolation.STAIR # Interpolation mode, only used for
|
||||
# Line Charts and Area Charts.
|
||||
}
|
||||
)
|
||||
|
||||
# Now let's plot our data
|
||||
chart.plot([f1], cp)
|
||||
|
||||
# Uncommenting this line will show how real time data plotting works
|
||||
set_process(false)
|
||||
|
||||
|
||||
var new_val: float = 4.5
|
||||
|
||||
func _process(delta: float):
|
||||
# This function updates the values of a function and then updates the plot
|
||||
new_val += 5
|
||||
|
||||
# we can use the `Function.add_point(x, y)` method to update a function
|
||||
f1.add_point(new_val, cos(new_val) * 20)
|
||||
f1.remove_point(0)
|
||||
chart.queue_redraw() # This will force the Chart to be updated
|
||||
|
||||
|
||||
func _on_CheckButton_pressed():
|
||||
set_process(not is_processing())
|
||||
1
addons/easy_charts/examples/line_chart/Control.gd.uid
Normal file
1
addons/easy_charts/examples/line_chart/Control.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://b3f6ye7dq4g6m
|
||||
48
addons/easy_charts/examples/line_chart/Control.tscn
Normal file
48
addons/easy_charts/examples/line_chart/Control.tscn
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
[gd_scene format=3 uid="uid://chcj7up8k8pa8"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://b3f6ye7dq4g6m" path="res://addons/easy_charts/examples/line_chart/Control.gd" id="1"]
|
||||
[ext_resource type="PackedScene" uid="uid://dlwq4kmdb3bhs" path="res://addons/easy_charts/control_charts/chart.tscn" id="2"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="1"]
|
||||
content_margin_right = 5.0
|
||||
content_margin_bottom = 5.0
|
||||
draw_center = false
|
||||
border_width_right = 2
|
||||
border_width_bottom = 2
|
||||
border_color = Color(0, 0, 0, 1)
|
||||
|
||||
[node name="Control2" type="Control" unique_id=1432132673]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="." unique_id=420636511]
|
||||
layout_mode = 0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
|
||||
[node name="CheckButton" type="CheckButton" parent="VBoxContainer" unique_id=1087670003]
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_focus_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_pressed_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_hover_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_hover_pressed_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_disabled_color = Color(0, 0, 0, 1)
|
||||
text = "Start Relatime Plotting"
|
||||
|
||||
[node name="Chart" parent="VBoxContainer" unique_id=1336462756 instance=ExtResource("2")]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="VBoxContainer" unique_id=1068372980]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 8
|
||||
theme_override_colors/font_color = Color(0, 0, 0, 1)
|
||||
theme_override_styles/normal = SubResource("1")
|
||||
text = "Try to scale the window!"
|
||||
|
||||
[connection signal="pressed" from="VBoxContainer/CheckButton" to="." method="_on_CheckButton_pressed"]
|
||||
80
addons/easy_charts/examples/multiplot/Control.gd
Normal file
80
addons/easy_charts/examples/multiplot/Control.gd
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
extends Control
|
||||
|
||||
@onready var chart: Chart = $VBoxContainer/Chart
|
||||
|
||||
# This Chart will plot 3 different functions
|
||||
var f1: Function
|
||||
var f2: Function
|
||||
var f3: Function
|
||||
|
||||
func _ready():
|
||||
# Let's create our @x values
|
||||
var x: Array = ArrayOperations.multiply_float(range(-10, 11, 1), 0.5)
|
||||
|
||||
# And our y values. It can be an n-size array of arrays.
|
||||
# NOTE: `x.size() == y.size()` or `x.size() == y[n].size()`
|
||||
var y: Array = ArrayOperations.multiply_int(ArrayOperations.cos(x), 20)
|
||||
var y2: Array = ArrayOperations.add_float(ArrayOperations.multiply_int(ArrayOperations.sin(x), 20), 20)
|
||||
var y3: Array = ArrayOperations.add_float(ArrayOperations.multiply_int(ArrayOperations.cos(x), -5), -3)
|
||||
|
||||
# Let's customize the chart properties, which specify how the chart
|
||||
# should look, plus some additional elements like labels, the scale, etc...
|
||||
var cp: ChartProperties = ChartProperties.new()
|
||||
cp.colors.frame = Color("#161a1d")
|
||||
cp.colors.background = Color.TRANSPARENT
|
||||
cp.colors.grid = Color("#283442")
|
||||
cp.colors.ticks = Color("#283442")
|
||||
cp.colors.text = Color.WHITE_SMOKE
|
||||
cp.draw_bounding_box = false
|
||||
cp.show_legend = true
|
||||
cp.title = "Air Quality Monitoring"
|
||||
cp.x_label = "Time"
|
||||
cp.y_label = "Sensor values"
|
||||
cp.x_scale = 5
|
||||
cp.y_scale = 10
|
||||
cp.interactive = true # false by default, it allows the chart to create a tooltip to show point values
|
||||
# and interecept clicks on the plot
|
||||
|
||||
# Let's add values to our functions
|
||||
f1 = Function.new(
|
||||
x, y, "Pressure", # This will create a function with x and y values taken by the Arrays
|
||||
# we have created previously. This function will also be named "Pressure"
|
||||
# as it contains 'pressure' values.
|
||||
# If set, the name of a function will be used both in the Legend
|
||||
# (if enabled thourgh ChartProperties) and on the Tooltip (if enabled).
|
||||
# Let's also provide a dictionary of configuration parameters for this specific function.
|
||||
{
|
||||
color = Color("#36a2eb"), # The color associated to this function
|
||||
marker = Function.Marker.NONE, # The marker that will be displayed for each drawn point (x,y)
|
||||
# since it is `NONE`, no marker will be shown.
|
||||
type = Function.Type.AREA, # This defines what kind of plotting will be used,
|
||||
# in this case it will be an Area Chart.
|
||||
interpolation = Function.Interpolation.STAIR # Interpolation mode, only used for
|
||||
# Line Charts and Area Charts.
|
||||
}
|
||||
)
|
||||
f2 = Function.new(x, y2, "Humidity", { color = Color("#ff6384"), marker = Function.Marker.CROSS })
|
||||
f3 = Function.new(x, y3, "CO2", { color = Color.GREEN, marker = Function.Marker.TRIANGLE })
|
||||
|
||||
# Now let's plot our data
|
||||
chart.plot([f1, f2, f3], cp)
|
||||
|
||||
# Uncommenting this line will show how real time data plotting works
|
||||
set_process(false)
|
||||
|
||||
|
||||
var new_val: float = 4.5
|
||||
|
||||
func _process(delta: float):
|
||||
# This function updates the values of a function and then updates the plot
|
||||
new_val += 5
|
||||
|
||||
# we can use the `Function.add_point(x, y)` method to update a function
|
||||
f1.add_point(new_val, cos(new_val) * 20)
|
||||
f2.add_point(new_val, (sin(new_val) * 20) + 20)
|
||||
f3.add_point(new_val, (cos(new_val) * -5) - 3)
|
||||
chart.queue_redraw() # This will force the Chart to be updated
|
||||
|
||||
|
||||
func _on_CheckButton_pressed():
|
||||
set_process(not is_processing())
|
||||
1
addons/easy_charts/examples/multiplot/Control.gd.uid
Normal file
1
addons/easy_charts/examples/multiplot/Control.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://gjg5j18ou3f1
|
||||
48
addons/easy_charts/examples/multiplot/Control.tscn
Normal file
48
addons/easy_charts/examples/multiplot/Control.tscn
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
[gd_scene load_steps=4 format=3 uid="uid://7v0v5lsl0kqe"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/easy_charts/examples/multiplot/Control.gd" id="1"]
|
||||
[ext_resource type="PackedScene" uid="uid://dlwq4kmdb3bhs" path="res://addons/easy_charts/control_charts/chart.tscn" id="2"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="1"]
|
||||
content_margin_right = 5.0
|
||||
content_margin_bottom = 5.0
|
||||
draw_center = false
|
||||
border_width_right = 2
|
||||
border_width_bottom = 2
|
||||
border_color = Color(0, 0, 0, 1)
|
||||
|
||||
[node name="Control2" type="Control"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
||||
layout_mode = 0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
|
||||
[node name="CheckButton" type="CheckButton" parent="VBoxContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_pressed_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_hover_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_hover_pressed_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_focus_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_disabled_color = Color(0, 0, 0, 1)
|
||||
text = "Start Relatime Plotting"
|
||||
|
||||
[node name="Chart" parent="VBoxContainer" instance=ExtResource("2")]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 8
|
||||
theme_override_colors/font_color = Color(0, 0, 0, 1)
|
||||
theme_override_styles/normal = SubResource("1")
|
||||
text = "Try to scale the window!"
|
||||
|
||||
[connection signal="pressed" from="VBoxContainer/CheckButton" to="." method="_on_CheckButton_pressed"]
|
||||
67
addons/easy_charts/examples/pie_chart/Control.gd
Normal file
67
addons/easy_charts/examples/pie_chart/Control.gd
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
extends Control
|
||||
|
||||
@onready var chart: Chart = $VBoxContainer/Chart
|
||||
|
||||
# This Chart will plot 3 different functions
|
||||
var f1: Function
|
||||
|
||||
func _ready():
|
||||
# Let's create our @x values
|
||||
var x: Array = [10, 20, 30, 40]
|
||||
|
||||
# And our y values. It can be an n-size array of arrays.
|
||||
# NOTE: `x.size() == y.size()` or `x.size() == y[n].size()`
|
||||
var y: Array = ["Java", "JavaScript", "C++", "GDScript"]
|
||||
|
||||
# Let's customize the chart properties, which specify how the chart
|
||||
# should look, plus some additional elements like labels, the scale, etc...
|
||||
var cp: ChartProperties = ChartProperties.new()
|
||||
cp.colors.frame = Color("#161a1d")
|
||||
cp.colors.background = Color.TRANSPARENT
|
||||
cp.colors.grid = Color("#283442")
|
||||
cp.colors.ticks = Color("#283442")
|
||||
cp.colors.text = Color.WHITE_SMOKE
|
||||
cp.draw_bounding_box = false
|
||||
cp.title = "Users preferences on programming languages"
|
||||
cp.draw_grid_box = false
|
||||
cp.show_legend = true
|
||||
cp.interactive = true # false by default, it allows the chart to create a tooltip to show point values
|
||||
# and interecept clicks on the plot
|
||||
|
||||
var gradient: Gradient = Gradient.new()
|
||||
gradient.set_color(0, Color.AQUAMARINE)
|
||||
gradient.set_color(1, Color.DEEP_PINK)
|
||||
|
||||
# Let's add values to our functions
|
||||
f1 = Function.new(
|
||||
x, y, "Language", # This will create a function with x and y values taken by the Arrays
|
||||
# we have created previously. This function will also be named "Pressure"
|
||||
# as it contains 'pressure' values.
|
||||
# If set, the name of a function will be used both in the Legend
|
||||
# (if enabled thourgh ChartProperties) and on the Tooltip (if enabled).
|
||||
{
|
||||
gradient = gradient,
|
||||
type = Function.Type.PIE
|
||||
}
|
||||
)
|
||||
|
||||
# Now let's plot our data
|
||||
chart.plot([f1], cp)
|
||||
|
||||
# Uncommenting this line will show how real time data plotting works
|
||||
set_process(false)
|
||||
|
||||
|
||||
var new_val: float = 4.5
|
||||
|
||||
func _process(delta: float):
|
||||
# This function updates the values of a function and then updates the plot
|
||||
new_val += 5
|
||||
|
||||
# we can use the `Function.add_point(x, y)` method to update a function
|
||||
f1.add_point(new_val, cos(new_val) * 20)
|
||||
chart.queue_redraw() # This will force the Chart to be updated
|
||||
|
||||
|
||||
func _on_CheckButton_pressed():
|
||||
set_process(not is_processing())
|
||||
1
addons/easy_charts/examples/pie_chart/Control.gd.uid
Normal file
1
addons/easy_charts/examples/pie_chart/Control.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://db3v84wxna0nn
|
||||
48
addons/easy_charts/examples/pie_chart/Control.tscn
Normal file
48
addons/easy_charts/examples/pie_chart/Control.tscn
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
[gd_scene load_steps=4 format=3 uid="uid://be3nkm3rmqe7m"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/easy_charts/examples/pie_chart/Control.gd" id="1"]
|
||||
[ext_resource type="PackedScene" uid="uid://dlwq4kmdb3bhs" path="res://addons/easy_charts/control_charts/chart.tscn" id="2"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="1"]
|
||||
content_margin_right = 5.0
|
||||
content_margin_bottom = 5.0
|
||||
draw_center = false
|
||||
border_width_right = 2
|
||||
border_width_bottom = 2
|
||||
border_color = Color(0, 0, 0, 1)
|
||||
|
||||
[node name="Control2" type="Control"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
||||
layout_mode = 0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
|
||||
[node name="CheckButton" type="CheckButton" parent="VBoxContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_pressed_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_hover_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_hover_pressed_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_focus_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_disabled_color = Color(0, 0, 0, 1)
|
||||
text = "Start Relatime Plotting"
|
||||
|
||||
[node name="Chart" parent="VBoxContainer" instance=ExtResource("2")]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 8
|
||||
theme_override_colors/font_color = Color(0, 0, 0, 1)
|
||||
theme_override_styles/normal = SubResource("1")
|
||||
text = "Try to scale the window!"
|
||||
|
||||
[connection signal="pressed" from="VBoxContainer/CheckButton" to="." method="_on_CheckButton_pressed"]
|
||||
66
addons/easy_charts/examples/scatter_chart/Control.gd
Normal file
66
addons/easy_charts/examples/scatter_chart/Control.gd
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
extends Control
|
||||
|
||||
@onready var chart: Chart = $VBoxContainer/Chart
|
||||
|
||||
# This Chart will plot 3 different functions
|
||||
var f1: Function
|
||||
var f2: Function
|
||||
|
||||
func _ready():
|
||||
# Let's create our @x values
|
||||
var x: Array = ArrayOperations.multiply_float(range(-10, 11, 1), 0.5)
|
||||
|
||||
# And our y values. It can be an n-size array of arrays.
|
||||
# NOTE: `x.size() == y.size()` or `x.size() == y[n].size()`
|
||||
var y: Array = ArrayOperations.multiply_int(ArrayOperations.cos(x), 20)
|
||||
var y2: Array = ArrayOperations.add_float(ArrayOperations.multiply_int(ArrayOperations.sin(x), 20), 20)
|
||||
|
||||
# Let's customize the chart properties, which specify how the chart
|
||||
# should look, plus some additional elements like labels, the scale, etc...
|
||||
var cp: ChartProperties = ChartProperties.new()
|
||||
cp.colors.frame = Color("#161a1d")
|
||||
cp.colors.background = Color.TRANSPARENT
|
||||
cp.colors.grid = Color("#283442")
|
||||
cp.colors.ticks = Color("#283442")
|
||||
cp.colors.text = Color.WHITE_SMOKE
|
||||
cp.draw_bounding_box = false
|
||||
cp.title = "Air Quality Monitoring"
|
||||
cp.x_label = "Time"
|
||||
cp.y_label = "Sensor values"
|
||||
cp.x_scale = 5
|
||||
cp.y_scale = 10
|
||||
cp.interactive = true # false by default, it allows the chart to create a tooltip to show point values
|
||||
# and interecept clicks on the plot
|
||||
|
||||
# Let's add values to our functions
|
||||
f1 = Function.new(
|
||||
x, y, "Pressure", # This will create a function with x and y values taken by the Arrays
|
||||
# we have created previously. This function will also be named "Pressure"
|
||||
# as it contains 'pressure' values.
|
||||
# If set, the name of a function will be used both in the Legend
|
||||
# (if enabled thourgh ChartProperties) and on the Tooltip (if enabled).
|
||||
{ color = Color.GREEN, marker = Function.Marker.CIRCLE }
|
||||
)
|
||||
f2 = Function.new(x, y2, "Humidity", { color = Color("#ff6384"), marker = Function.Marker.CROSS })
|
||||
|
||||
# Now let's plot our data
|
||||
chart.plot([f1, f2], cp)
|
||||
|
||||
# Uncommenting this line will show how real time data plotting works
|
||||
set_process(false)
|
||||
|
||||
|
||||
var new_val: float = 4.5
|
||||
|
||||
func _process(delta: float):
|
||||
# This function updates the values of a function and then updates the plot
|
||||
new_val += 5
|
||||
|
||||
# we can use the `Function.add_point(x, y)` method to update a function
|
||||
f1.add_point(new_val, cos(new_val) * 20)
|
||||
f2.add_point(new_val, (sin(new_val) * 20) + 20)
|
||||
chart.queue_redraw() # This will force the Chart to be updated
|
||||
|
||||
|
||||
func _on_CheckButton_pressed():
|
||||
set_process(not is_processing())
|
||||
1
addons/easy_charts/examples/scatter_chart/Control.gd.uid
Normal file
1
addons/easy_charts/examples/scatter_chart/Control.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://nmgyex22cpp4
|
||||
48
addons/easy_charts/examples/scatter_chart/Control.tscn
Normal file
48
addons/easy_charts/examples/scatter_chart/Control.tscn
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
[gd_scene load_steps=4 format=3 uid="uid://cekkstadxpimf"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/easy_charts/examples/scatter_chart/Control.gd" id="1"]
|
||||
[ext_resource type="PackedScene" uid="uid://dlwq4kmdb3bhs" path="res://addons/easy_charts/control_charts/chart.tscn" id="2"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="1"]
|
||||
content_margin_right = 5.0
|
||||
content_margin_bottom = 5.0
|
||||
draw_center = false
|
||||
border_width_right = 2
|
||||
border_width_bottom = 2
|
||||
border_color = Color(0, 0, 0, 1)
|
||||
|
||||
[node name="Control2" type="Control"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
||||
layout_mode = 0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
|
||||
[node name="CheckButton" type="CheckButton" parent="VBoxContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_pressed_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_hover_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_hover_pressed_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_focus_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_disabled_color = Color(0, 0, 0, 1)
|
||||
text = "Start Relatime Plotting"
|
||||
|
||||
[node name="Chart" parent="VBoxContainer" instance=ExtResource("2")]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 8
|
||||
theme_override_colors/font_color = Color(0, 0, 0, 1)
|
||||
theme_override_styles/normal = SubResource("1")
|
||||
text = "Try to scale the window!"
|
||||
|
||||
[connection signal="pressed" from="VBoxContainer/CheckButton" to="." method="_on_CheckButton_pressed"]
|
||||
47
addons/easy_charts/examples/simple_chart/Control.gd
Normal file
47
addons/easy_charts/examples/simple_chart/Control.gd
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
# This example shows how to instantiate a Chart node at runtime and plot a single function
|
||||
|
||||
extends Control
|
||||
|
||||
@onready var chart_scn: PackedScene = load("res://addons/easy_charts/control_charts/chart.tscn")
|
||||
var chart: Chart
|
||||
|
||||
# This Chart will plot 1 function
|
||||
var f1: Function
|
||||
|
||||
func _ready():
|
||||
chart = chart_scn.instantiate()
|
||||
$VBoxContainer.add_child(chart)
|
||||
|
||||
# Let's create our @x values
|
||||
var x: Array = ArrayOperations.multiply_float(range(-10, 11, 1), 0.5)
|
||||
|
||||
# And our y values. It can be an n-size array of arrays.
|
||||
# NOTE: `x.size() == y.size()` or `x.size() == y[n].size()`
|
||||
var y: Array = ArrayOperations.multiply_int(ArrayOperations.cos(x), 20)
|
||||
|
||||
# Let's add values to our functions
|
||||
f1 = Function.new(x, y, "Pressure", { marker = Function.Marker.CIRCLE })
|
||||
|
||||
# Set fixed Y domain
|
||||
chart.set_y_domain(-50, 50)
|
||||
|
||||
# Now let's plot our data
|
||||
chart.plot([f1])
|
||||
|
||||
# Uncommenting this line will show how real time data plotting works
|
||||
set_process(false)
|
||||
|
||||
|
||||
var new_val: float = 4.5
|
||||
|
||||
func _process(delta: float):
|
||||
# This function updates the values of a function and then updates the plot
|
||||
new_val += 5
|
||||
|
||||
# we can use the `Function.add_point(x, y)` method to update a function
|
||||
f1.add_point(new_val, cos(new_val) * 20)
|
||||
chart.queue_redraw() # This will force the Chart to be updated
|
||||
|
||||
|
||||
func _on_CheckButton_pressed():
|
||||
set_process(not is_processing())
|
||||
1
addons/easy_charts/examples/simple_chart/Control.gd.uid
Normal file
1
addons/easy_charts/examples/simple_chart/Control.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://dtvd8j3myl3fh
|
||||
44
addons/easy_charts/examples/simple_chart/Control.tscn
Normal file
44
addons/easy_charts/examples/simple_chart/Control.tscn
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
[gd_scene load_steps=3 format=3 uid="uid://v4c2f17q8a1o"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/easy_charts/examples/simple_chart/Control.gd" id="1"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="1"]
|
||||
content_margin_right = 5.0
|
||||
content_margin_bottom = 5.0
|
||||
draw_center = false
|
||||
border_width_right = 2
|
||||
border_width_bottom = 2
|
||||
border_color = Color(0, 0, 0, 1)
|
||||
|
||||
[node name="Control2" type="Control"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
||||
layout_mode = 0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
|
||||
[node name="CheckButton" type="CheckButton" parent="VBoxContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_colors/font_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_pressed_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_hover_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_hover_pressed_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_focus_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_disabled_color = Color(0, 0, 0, 1)
|
||||
text = "Start Relatime Plotting"
|
||||
|
||||
[node name="Label" type="Label" parent="VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 8
|
||||
theme_override_colors/font_color = Color(0, 0, 0, 1)
|
||||
theme_override_styles/normal = SubResource("1")
|
||||
text = "Try to scale the window!"
|
||||
|
||||
[connection signal="pressed" from="VBoxContainer/CheckButton" to="." method="_on_CheckButton_pressed"]
|
||||
1
addons/easy_charts/icon.svg
Normal file
1
addons/easy_charts/icon.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns:xlink="http://www.w3.org/1999/xlink" width="282" xmlns="http://www.w3.org/2000/svg" height="283" id="screenshot-b8eadac8-0d0a-803f-8001-f21d3cdd64ab" viewBox="-0 -28.069 282 283" style="-webkit-print-color-adjust: exact;" fill="none" version="1.1"><g id="shape-b8eadac8-0d0a-803f-8001-f21d3cdd64ab" rx="0" ry="0"><g id="shape-b8eadac8-0d0a-803f-8001-f21d3cdd64ac"><g id="fills-b8eadac8-0d0a-803f-8001-f21d3cdd64ac"><path rx="0" ry="0" d="M109.571,9.931C137.517,9.931,100.264,111.526,122.198,132.482C145.546,154.789,244.294,116.502,244,143.181C243.256,210.759,189.641,254.931,122.198,254.931C54.755,254.931,0,200.064,0,132.482C0,64.9,42.128,9.931,109.571,9.931Z" style="fill: rgb(83, 126, 255); fill-opacity: 1;"/></g></g><g id="shape-b8eadac8-0d0a-803f-8001-f21d3cdd64ad"><g id="fills-b8eadac8-0d0a-803f-8001-f21d3cdd64ad"><path rx="0" ry="0" d="M175.542,0C149.482,1.117,140.591,15.797,137.561,37.981L175.542,0ZM191.08,0.936L136.216,55.801C136.025,61.771,136.001,68.07,136,74.618L205.696,4.922C201.025,3.12,196.136,1.774,191.08,0.936ZM216.799,10.293L136.158,90.935C136.386,98.109,136.94,103.445,138.281,107.414L227.652,18.042C224.271,15.153,220.64,12.557,216.799,10.293ZM235.913,26.256L146.049,116.12C149.806,117.738,154.968,118.423,162.059,118.712L243.735,37.035C241.43,33.211,238.81,29.605,235.913,26.256ZM249.229,48.016L178.324,118.921C178.488,118.921,178.653,118.921,178.818,118.921C185.068,118.921,191.144,118.96,196.959,118.887L253.502,62.344C252.524,57.381,251.084,52.589,249.229,48.016ZM254.999,77.322L214.206,118.114C224.287,117.188,233.037,115.226,239.821,111.101L249.685,101.237C253.115,95.481,255,87.858,255,77.764C255,77.617,255,77.469,254.999,77.322Z" style="fill: rgb(51, 51, 51); fill-opacity: 1;"/></g></g></g></svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
43
addons/easy_charts/icon.svg.import
Normal file
43
addons/easy_charts/icon.svg.import
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://bxynlfljql6so"
|
||||
path="res://.godot/imported/icon.svg-655bc7c28f15e7cab48b85d562ef0068.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/easy_charts/icon.svg"
|
||||
dest_files=["res://.godot/imported/icon.svg-655bc7c28f15e7cab48b85d562ef0068.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
||||
7
addons/easy_charts/plugin.cfg
Normal file
7
addons/easy_charts/plugin.cfg
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
[plugin]
|
||||
|
||||
name="EasyCharts"
|
||||
description=""
|
||||
author="Nicolò \"fenix\" Santilio"
|
||||
version="14.08.2023"
|
||||
script="plugin.gd"
|
||||
8
addons/easy_charts/plugin.gd
Normal file
8
addons/easy_charts/plugin.gd
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
@tool
|
||||
extends EditorPlugin
|
||||
|
||||
func _enter_tree():
|
||||
pass
|
||||
|
||||
func _exit_tree():
|
||||
pass
|
||||
1
addons/easy_charts/plugin.gd.uid
Normal file
1
addons/easy_charts/plugin.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://cu4dkywehlo0g
|
||||
44
addons/easy_charts/templates.json
Normal file
44
addons/easy_charts/templates.json
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
{
|
||||
"default":
|
||||
{
|
||||
"function_colors" : ["#1e1e1e","#1e1e1e","#1e1e1e","#1e1e1e"],
|
||||
"v_lines_color" : "#cacaca",
|
||||
"h_lines_color" : "#cacaca",
|
||||
"outline_color" : "#1e1e1e",
|
||||
"font_color" : "#1e1e1e"
|
||||
},
|
||||
"clean":
|
||||
{
|
||||
"function_colors" : ["#f7aa29","#f4394a","#5a6b7b","#8fbf59","#504538","#B7A99A","#00D795","#FFECCC","#FF8981"],
|
||||
"v_lines_color" : "#00000000",
|
||||
"h_lines_color" : "#3cffffff",
|
||||
"outline_color" : "#00000000",
|
||||
"font_color" : "#3cffffff"
|
||||
},
|
||||
"gradient":
|
||||
{
|
||||
"function_colors" : ["#F7AA29","#B8A806","#79A117","#2C9433","#00854C","#006571","#2F4858","#2a364f","#27294a"],
|
||||
"v_lines_color" : "#64ffffff",
|
||||
"h_lines_color" : "#64ffffff",
|
||||
"outline_color" : "#64ffffff",
|
||||
"font_color" : "#64ffffff",
|
||||
},
|
||||
"minimal":
|
||||
{
|
||||
"function_colors" : ["#1e1e1e","#1e1e1e","#1e1e1e","#1e1e1e"],
|
||||
"v_lines_color" : "#00000000",
|
||||
"h_lines_color" : "#00000000",
|
||||
"outline_color" : "#00000000",
|
||||
"font_color" : "#00000000"
|
||||
},
|
||||
"invert":
|
||||
{
|
||||
"function_colors" : ["#ffffff","#ffffff","#ffffff","#ffffff"],
|
||||
"v_lines_color" : "#3b3b3b",
|
||||
"h_lines_color" : "#3b3b3b",
|
||||
"outline_color" : "#ffffff",
|
||||
"font_color" : "#ffffff"
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
|
|
@ -0,0 +1,36 @@
|
|||
[remap]
|
||||
|
||||
importer="font_data_dynamic"
|
||||
type="FontFile"
|
||||
uid="uid://dv41l4rmsac4o"
|
||||
path="res://.godot/imported/OpenSans-VariableFont_wdth,wght.ttf-d05790715e89c94bb2a3b1122054f8d1.fontdata"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/easy_charts/utilities/assets/OpenSans-VariableFont_wdth,wght.ttf"
|
||||
dest_files=["res://.godot/imported/OpenSans-VariableFont_wdth,wght.ttf-d05790715e89c94bb2a3b1122054f8d1.fontdata"]
|
||||
|
||||
[params]
|
||||
|
||||
Rendering=null
|
||||
antialiasing=1
|
||||
generate_mipmaps=false
|
||||
disable_embedded_bitmaps=true
|
||||
multichannel_signed_distance_field=false
|
||||
msdf_pixel_range=8
|
||||
msdf_size=48
|
||||
allow_system_fallback=true
|
||||
force_autohinter=false
|
||||
modulate_color_glyphs=false
|
||||
hinting=1
|
||||
subpixel_positioning=4
|
||||
keep_rounding_remainders=true
|
||||
oversampling=0.0
|
||||
Fallbacks=null
|
||||
fallbacks=[]
|
||||
Compress=null
|
||||
compress=true
|
||||
preload=[]
|
||||
language_support={}
|
||||
script_support={}
|
||||
opentype_features={}
|
||||
12
addons/easy_charts/utilities/classes/plotting/bar.gd
Normal file
12
addons/easy_charts/utilities/classes/plotting/bar.gd
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
extends RefCounted
|
||||
class_name Bar
|
||||
|
||||
#var rect: Rect2
|
||||
#var value: Pair
|
||||
#
|
||||
#func _init(rect: Rect2, value: Pair = Pair.new()) -> void:
|
||||
# self.value = value
|
||||
# self.rect = rect
|
||||
|
||||
func _to_string() -> String:
|
||||
return "Value: %s\nRect: %s" % [self.value, self.rect]
|
||||
1
addons/easy_charts/utilities/classes/plotting/bar.gd.uid
Normal file
1
addons/easy_charts/utilities/classes/plotting/bar.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://6465cllnm5b7
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
extends RefCounted
|
||||
class_name ChartProperties
|
||||
|
||||
var title: String
|
||||
var x_label: String
|
||||
var y_label: String
|
||||
|
||||
## {n}_scale defines in how many sectors the grid will be divided.
|
||||
var x_scale: float = 5.0
|
||||
var y_scale: float = 2.0
|
||||
|
||||
var x_tick_size: float = 7
|
||||
var x_ticklabel_space: float = 5
|
||||
var y_tick_size: float = 7
|
||||
var y_ticklabel_space: float = 5
|
||||
|
||||
## Scale type, 0 = linear | 1 = logarithmic
|
||||
var x_scale_type: int = 0
|
||||
var y_scale_type: int = 0
|
||||
|
||||
var draw_borders: bool = true
|
||||
var draw_frame: bool = true
|
||||
var draw_background: bool = true
|
||||
var draw_bounding_box: bool = true
|
||||
var draw_vertical_grid: bool = true
|
||||
var draw_horizontal_grid: bool = true
|
||||
var draw_ticks: bool = true
|
||||
var draw_origin: bool = false
|
||||
var draw_grid_box: bool = true
|
||||
var show_tick_labels: bool = true
|
||||
var show_x_label: bool = true
|
||||
var show_y_label: bool = true
|
||||
var show_title: bool = true
|
||||
|
||||
## If true will show the legend of your Chart on the right side of the frame.
|
||||
var show_legend: bool = false
|
||||
|
||||
## If true will make the Chart interactive, i.e. the DataTooltip will be displayed when
|
||||
## hovering points with your mouse, and mouse_entered and mouse_exited signal will be emitted.
|
||||
var interactive: bool = false
|
||||
|
||||
## If true, will smooth the domain lower and upper bounds to the closest rounded value,
|
||||
## instead of using precise values.
|
||||
var smooth_domain: bool = false
|
||||
|
||||
## If > 0, will limit the amount of points plotted in a Chart, discarding older values.
|
||||
## [b]Note:[/b] this parameter will not make the Chart remove points from your Function objects,
|
||||
## instead older points will be just ignored. This will make your Function object x and y arrays
|
||||
## grow linearly, but won't interfere with your own data.
|
||||
## If you instead prefer to improve performances by completely remove older data from your Function
|
||||
## object, consider calling the Function.remove_point(0) method before adding a new point and plotting
|
||||
## again.
|
||||
var max_samples: int = 100
|
||||
|
||||
## Dictionary of colors for all of the Chart elements.
|
||||
var colors: Dictionary = {
|
||||
frame = Color.WHITE_SMOKE,
|
||||
background = Color.WHITE,
|
||||
borders = Color.RED,
|
||||
bounding_box = Color.BLACK,
|
||||
grid = Color.GRAY,
|
||||
ticks = Color.BLACK,
|
||||
text = Color.BLACK,
|
||||
origin = Color.DIM_GRAY
|
||||
}
|
||||
|
||||
var font: FontFile = load("res://addons/easy_charts/utilities/assets/OpenSans-VariableFont_wdth,wght.ttf")
|
||||
var font_size: int = 13
|
||||
|
||||
func _init() -> void:
|
||||
ThemeDB.set_fallback_font(font)
|
||||
ThemeDB.set_fallback_font_size(font_size)
|
||||
|
||||
func get_string_size(text: String) -> Vector2:
|
||||
return font.get_string_size(text)
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://dtlrom7yygrt8
|
||||
93
addons/easy_charts/utilities/classes/plotting/function.gd
Normal file
93
addons/easy_charts/utilities/classes/plotting/function.gd
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
extends RefCounted
|
||||
class_name Function
|
||||
|
||||
enum Type {
|
||||
SCATTER,
|
||||
LINE,
|
||||
AREA,
|
||||
PIE,
|
||||
BAR
|
||||
}
|
||||
|
||||
enum Interpolation {
|
||||
NONE,
|
||||
LINEAR,
|
||||
STAIR,
|
||||
SPLINE
|
||||
}
|
||||
|
||||
# TODO: add new markers, like an empty circle, an empty box, etc.
|
||||
enum Marker {
|
||||
NONE,
|
||||
CIRCLE,
|
||||
TRIANGLE,
|
||||
SQUARE,
|
||||
CROSS
|
||||
}
|
||||
|
||||
var __x: Array
|
||||
var __y: Array
|
||||
var name: String
|
||||
var props: Dictionary = {}
|
||||
|
||||
func _init(x: Array, y: Array, name: String = "", props: Dictionary = {}) -> void:
|
||||
self.__x = x.duplicate()
|
||||
self.__y = y.duplicate()
|
||||
self.name = name
|
||||
if not props.is_empty() and props != null:
|
||||
self.props = props
|
||||
|
||||
func get_point(index: int) -> Array:
|
||||
return [self.__x[index], self.__y[index]]
|
||||
|
||||
func add_point(x: float, y: float) -> void:
|
||||
self.__x.append(x)
|
||||
self.__y.append(y)
|
||||
|
||||
func set_point(index: int, x: float, y: float) -> void:
|
||||
self.__x[index] = x
|
||||
self.__y[index] = y
|
||||
|
||||
func remove_point(index: int) -> void:
|
||||
self.__x.remove_at(index)
|
||||
self.__y.remove_at(index)
|
||||
|
||||
func pop_back_point() -> void:
|
||||
self.__x.pop_back()
|
||||
self.__y.pop_back()
|
||||
|
||||
func pop_front_point() -> void:
|
||||
self.__x.pop_front()
|
||||
self.__y.pop_front()
|
||||
|
||||
func count_points() -> int:
|
||||
return self.__x.size()
|
||||
|
||||
func get_color() -> Color:
|
||||
return props.get("color", Color.DARK_SLATE_GRAY)
|
||||
|
||||
func get_gradient() -> Gradient:
|
||||
return props.get("gradient", Gradient.new())
|
||||
|
||||
func get_marker() -> int:
|
||||
return props.get("marker", Marker.NONE)
|
||||
|
||||
func get_type() -> int:
|
||||
return props.get("type", Type.SCATTER)
|
||||
|
||||
func get_interpolation() -> int:
|
||||
return props.get("interpolation", Interpolation.LINEAR)
|
||||
|
||||
func get_line_width() -> float:
|
||||
return props.get("line_width", 2.0)
|
||||
|
||||
func get_visibility() -> bool:
|
||||
return props.get("visible", true)
|
||||
|
||||
func copy() -> Function:
|
||||
return Function.new(
|
||||
self.__x.duplicate(),
|
||||
self.__y.duplicate(),
|
||||
self.name,
|
||||
self.props.duplicate(true)
|
||||
)
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://bv8gpeqso3ojt
|
||||
12
addons/easy_charts/utilities/classes/plotting/point.gd
Normal file
12
addons/easy_charts/utilities/classes/plotting/point.gd
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
extends RefCounted
|
||||
class_name Point
|
||||
|
||||
var position: Vector2
|
||||
var value: Dictionary
|
||||
|
||||
func _init(position: Vector2, value: Dictionary) -> void:
|
||||
self.position = position
|
||||
self.value = value
|
||||
|
||||
func _to_string() -> String:
|
||||
return "Value: %s\nPosition: %s" % [self.value, self.position]
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://d2hychsr02l0c
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
extends RefCounted
|
||||
class_name ArrayOperations
|
||||
|
||||
static func add_int(array: Array, _int: int) -> Array:
|
||||
var t: Array = array.duplicate(true)
|
||||
for ti in t.size():
|
||||
t[ti] = int(t[ti] + _int)
|
||||
return t
|
||||
|
||||
static func add_float(array: Array, _float: float) -> Array:
|
||||
var t: Array = array.duplicate(true)
|
||||
for ti in t.size():
|
||||
t[ti] = float(t[ti] + _float)
|
||||
return t
|
||||
|
||||
static func multiply_int(array: Array, _int: int) -> Array:
|
||||
var t: Array = array.duplicate(true)
|
||||
for ti in t.size():
|
||||
t[ti] = int(t[ti] * _int)
|
||||
return t
|
||||
|
||||
static func multiply_float(array: Array, _float: float) -> PackedFloat32Array:
|
||||
var t: PackedFloat32Array = array.duplicate(true)
|
||||
for ti in t.size():
|
||||
t[ti] = float(t[ti] * _float)
|
||||
return t
|
||||
|
||||
static func pow(array: Array, _int: int) -> Array:
|
||||
var t: Array = array.duplicate(true)
|
||||
for ti in t.size():
|
||||
t[ti] = float(pow(t[ti], _int))
|
||||
return t
|
||||
|
||||
static func cos(array: Array) -> Array:
|
||||
var t: Array = array.duplicate(true)
|
||||
for val in array.size():
|
||||
t[val] = cos(t[val])
|
||||
return t
|
||||
|
||||
static func sin(array: Array) -> Array:
|
||||
var t: Array = array.duplicate(true)
|
||||
for val in array.size():
|
||||
t[val] = sin(t[val])
|
||||
return t
|
||||
|
||||
static func affix(array: Array, _string: String) -> Array:
|
||||
var t: Array = array.duplicate(true)
|
||||
for val in array.size():
|
||||
t[val] = str(t[val]) + _string
|
||||
return t
|
||||
|
||||
static func suffix(array: Array, _string: String) -> Array:
|
||||
var t: Array = array.duplicate(true)
|
||||
for val in array.size():
|
||||
t[val] = _string + str(t[val])
|
||||
return t
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://d36mcc8nlkx4x
|
||||
190
addons/easy_charts/utilities/classes/structures/data_frame.gd
Normal file
190
addons/easy_charts/utilities/classes/structures/data_frame.gd
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
@tool
|
||||
extends Resource
|
||||
class_name DataFrame
|
||||
|
||||
var table_name : String = ""
|
||||
var labels : PackedStringArray = []
|
||||
var headers : PackedStringArray = []
|
||||
var datamatrix : Matrix = null
|
||||
var dataset : Array = []
|
||||
|
||||
func _init(datamatrix : Matrix, headers : PackedStringArray = [], labels : PackedStringArray = [] , table_name : String = "") -> void:
|
||||
if datamatrix.is_empty(): datamatrix.resize(labels.size(), headers.size())
|
||||
if labels.is_empty() : for label in range(datamatrix.get_size().x) : labels.append(label as String)
|
||||
if headers.is_empty() : for header in range(datamatrix.get_size().y) : headers.append(MatrixGenerator.get_letter_index(header))
|
||||
build_dataframe(datamatrix, headers, labels, table_name)
|
||||
|
||||
func build_dataframe(datamatrix : Matrix, headers : PackedStringArray = [], labels : PackedStringArray = [] , table_name : String = "") -> void:
|
||||
self.datamatrix = datamatrix
|
||||
self.headers = headers
|
||||
self.labels = labels
|
||||
self.table_name = table_name
|
||||
self.dataset = build_dataset_from_matrix(datamatrix, headers, labels)
|
||||
|
||||
func build_dataset_from_matrix(datamatrix : Matrix, headers : PackedStringArray, labels : PackedStringArray) -> Array:
|
||||
var data : Array = datamatrix.to_array()
|
||||
return build_dataset(data, headers, labels)
|
||||
|
||||
func build_dataset(data : Array, headers : PackedStringArray, labels : PackedStringArray) -> Array:
|
||||
var dataset : Array = [Array([" "]) + Array(headers)]
|
||||
for row_i in range(labels.size()): dataset.append(([labels[row_i]] + data[row_i]) if not data.is_empty() else [labels[row_i]])
|
||||
return dataset
|
||||
|
||||
func insert_column(column : Array, header : String = "", index : int = dataset[0].size() - 1) -> void:
|
||||
assert(column.size() == (datamatrix.rows() if not datamatrix.is_empty() else labels.size())) #,"error: the column size must match the dataset column size")
|
||||
headers.insert(index, header if header != "" else MatrixGenerator.get_letter_index(index))
|
||||
datamatrix.insert_column(column, index)
|
||||
dataset = build_dataset_from_matrix(datamatrix, headers, labels)
|
||||
|
||||
func insert_row(row : Array, label : String = "", index : int = dataset.size() - 1) -> PackedStringArray:
|
||||
assert(row.size() == (datamatrix.columns() if not datamatrix.is_empty() else headers.size())) #,"error: the row size must match the dataset row size")
|
||||
labels.insert(index, label if label != "" else str(index))
|
||||
datamatrix.insert_row(row, index)
|
||||
dataset = build_dataset_from_matrix(datamatrix, headers, labels)
|
||||
return PackedStringArray([label] + row)
|
||||
|
||||
func get_datamatrix() -> Matrix:
|
||||
return datamatrix
|
||||
|
||||
func get_dataset() -> Array:
|
||||
return dataset
|
||||
|
||||
func get_labels() -> PackedStringArray:
|
||||
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():
|
||||
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 : PackedInt32Array) -> PackedStringArray:
|
||||
var headers : PackedStringArray = []
|
||||
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 : PackedStringArray) -> 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 : PackedInt32Array) -> 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 : PackedInt32Array) -> PackedStringArray:
|
||||
var headers : PackedStringArray = []
|
||||
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 : PackedInt32Array) -> 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 : StringName):
|
||||
# ":" --> Columns
|
||||
if ":" in _property:
|
||||
var property : PackedStringArray = _property.split(":")
|
||||
if (property[0]).is_valid_int():
|
||||
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 : PackedStringArray = _property.split(";")
|
||||
if (property[0]).is_valid_int():
|
||||
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 : PackedStringArray = _property.split(",")
|
||||
else:
|
||||
if (_property as String).is_valid_int():
|
||||
return get_icolumn(int(_property))
|
||||
else:
|
||||
return get_column(_property)
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://clmkup3i4vomr
|
||||
174
addons/easy_charts/utilities/classes/structures/matrix.gd
Normal file
174
addons/easy_charts/utilities/classes/structures/matrix.gd
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
@tool
|
||||
extends Resource
|
||||
class_name Matrix
|
||||
|
||||
var values : Array = []
|
||||
|
||||
func _init(matrix : Array = [], size : int = 0) -> void:
|
||||
values = matrix
|
||||
|
||||
func insert_row(row : Array, index : int = values.size()) -> void:
|
||||
if rows() != 0:
|
||||
assert(row.size() == columns()) #,"the row size must match matrix row size")
|
||||
values.insert(index, row)
|
||||
|
||||
func update_row(row : Array, index : int) -> void:
|
||||
assert(rows() > index) #,"the row size must match matrix row size")
|
||||
values[index] = row
|
||||
|
||||
func remove_row(index: int) -> void:
|
||||
assert(rows() > index) #,"the row size must match matrix row size")
|
||||
values.remove_at(index)
|
||||
|
||||
func insert_column(column : Array, index : int = values[0].size()) -> void:
|
||||
if columns() != 0:
|
||||
assert(column.size() == rows()) #,"the column size must match matrix column size")
|
||||
for row_idx in column.size():
|
||||
values[row_idx].insert(index, column[row_idx])
|
||||
|
||||
func update_column(column : Array, index : int) -> void:
|
||||
assert(columns() > index) #,"the column size must match matrix column size")
|
||||
for row_idx in column.size():
|
||||
values[row_idx][index] = column[row_idx]
|
||||
|
||||
func remove_column(index: int) -> void:
|
||||
assert(columns() > index) #,"the column index must be at least equals to the rows count")
|
||||
for row in get_rows():
|
||||
row.remove(index)
|
||||
|
||||
func resize(rows: int, columns: int) -> void:
|
||||
for row in range(rows):
|
||||
var row_column: Array = []
|
||||
row_column.resize(columns)
|
||||
values.append(row_column)
|
||||
|
||||
func to_array() -> Array:
|
||||
return values.duplicate(true)
|
||||
|
||||
func get_size() -> Vector2:
|
||||
return Vector2(rows(), columns())
|
||||
|
||||
func rows() -> int:
|
||||
return values.size()
|
||||
|
||||
func columns() -> int:
|
||||
return values[0].size() if rows() != 0 else 0
|
||||
|
||||
func value(row: int, column: int) -> float:
|
||||
return values[row][column]
|
||||
|
||||
func set_value(value: float, row: int, column: int) -> void:
|
||||
values[row][column] = value
|
||||
|
||||
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 = 0, to : int = columns()-1) -> 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 = 0, to : int = rows()-1) -> Array:
|
||||
return values.slice(from, to)
|
||||
# return MatrixGenerator.from_array(values)
|
||||
|
||||
func is_empty() -> bool:
|
||||
return rows() == 0 and columns() == 0
|
||||
|
||||
|
||||
func is_square() -> bool:
|
||||
return columns() == rows()
|
||||
|
||||
|
||||
func is_diagonal() -> bool:
|
||||
if not is_square():
|
||||
return false
|
||||
|
||||
for i in rows():
|
||||
for j in columns():
|
||||
if i != j and values[i][j] != 0:
|
||||
return false
|
||||
|
||||
return true
|
||||
|
||||
|
||||
func is_upper_triangular() -> bool:
|
||||
if not is_square():
|
||||
return false
|
||||
|
||||
for i in rows():
|
||||
for j in columns():
|
||||
if i > j and values[i][j] != 0:
|
||||
return false
|
||||
|
||||
return true
|
||||
|
||||
|
||||
func is_lower_triangular() -> bool:
|
||||
if not is_square():
|
||||
return false
|
||||
|
||||
for i in rows():
|
||||
for j in columns():
|
||||
if i < j and values[i][j] != 0:
|
||||
return false
|
||||
|
||||
return true
|
||||
|
||||
|
||||
func is_triangular() -> bool:
|
||||
return is_upper_triangular() or is_lower_triangular()
|
||||
|
||||
|
||||
func is_identity() -> bool:
|
||||
if not is_diagonal():
|
||||
return false
|
||||
|
||||
for i in rows():
|
||||
if values[i][i] != 1:
|
||||
return false
|
||||
|
||||
return true
|
||||
|
||||
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 set(position: StringName, value: Variant) -> void:
|
||||
var t_pos: Array = position.split(",")
|
||||
values[t_pos[0]][t_pos[1]] = value
|
||||
|
||||
# --------------
|
||||
func _get(_property : StringName):
|
||||
# ":" --> Columns
|
||||
if ":" in _property:
|
||||
var property : PackedStringArray = _property.split(":")
|
||||
var from : PackedStringArray = property[0].split(",")
|
||||
var to : PackedStringArray = property[1].split(",")
|
||||
elif "," in _property:
|
||||
var property : PackedStringArray = _property.split(",")
|
||||
if property.size() == 2:
|
||||
return get_row(property[0] as int)[property[1] as int]
|
||||
else:
|
||||
if (_property as String).is_valid_int():
|
||||
return get_row(int(_property))
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://c1k3thpb7ihaw
|
||||
|
|
@ -0,0 +1,160 @@
|
|||
@tool
|
||||
extends RefCounted
|
||||
class_name MatrixGenerator
|
||||
|
||||
static func zeros(rows: int, columns: int) -> Matrix:
|
||||
var zeros: Array = []
|
||||
var t_rows: Array = []
|
||||
t_rows.resize(columns)
|
||||
t_rows.fill(0.0)
|
||||
for row in rows:
|
||||
zeros.append(t_rows.duplicate())
|
||||
return Matrix.new(zeros)
|
||||
|
||||
# Generates a Matrix with random values between [from; to] with a given @size (rows, columns)
|
||||
static func random_float_range(from : float, to : float, size : Vector2, _seed : int = 1234) -> Matrix:
|
||||
seed(_seed)
|
||||
randomize()
|
||||
var array : Array = []
|
||||
for row in range(size.x):
|
||||
var matrix_row : Array = []
|
||||
for column in range(size.y): matrix_row.append(randf_range(from,to))
|
||||
array.append(matrix_row)
|
||||
return Matrix.new(array)
|
||||
|
||||
# Generates a Matrix giving an Array (Array must by Array[Array])
|
||||
static func from_array(array : Array = []) -> Matrix:
|
||||
var matrix : Array = []
|
||||
matrix.append(array)
|
||||
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 : PackedInt32Array, to : PackedInt32Array) -> 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 = []
|
||||
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
|
||||
static func duplicate(_matrix : Matrix) -> Matrix:
|
||||
return Matrix.new(_matrix.to_array().duplicate())
|
||||
|
||||
# Calculate the determinant of a matrix
|
||||
static func determinant(matrix: Matrix) -> float:
|
||||
assert(matrix.is_square()) #,"Expected square matrix")
|
||||
|
||||
var determinant: float = 0.0
|
||||
|
||||
if matrix.rows() == 2 :
|
||||
determinant = (matrix.value(0, 0) * matrix.value(1, 1)) - (matrix.value(0, 1) * matrix.value(1, 0))
|
||||
elif matrix.is_diagonal() or matrix.is_triangular() :
|
||||
for i in matrix.rows():
|
||||
determinant *= matrix.value(i, i)
|
||||
elif matrix.is_identity() :
|
||||
determinant = 1.0
|
||||
else:
|
||||
# Laplace expansion
|
||||
var multiplier: float = -1.0
|
||||
var submatrix: Matrix = sub_matrix(matrix, [1, 0], [matrix.rows(), matrix.columns()])
|
||||
for j in matrix.columns() :
|
||||
var cofactor: Matrix = copy(submatrix)
|
||||
cofactor.remove_column(j)
|
||||
multiplier *= -1.0
|
||||
determinant += multiplier * matrix.value(0, j) * determinant(cofactor)
|
||||
|
||||
return determinant
|
||||
|
||||
|
||||
# Calculate the inverse of a Matrix
|
||||
static func inverse(matrix: Matrix) -> Matrix:
|
||||
var inverse: Matrix
|
||||
|
||||
# Minors and Cofactors
|
||||
var minors_cofactors: Matrix = zeros(matrix.rows(), matrix.columns())
|
||||
var multiplier: float = -1.0
|
||||
|
||||
for i in minors_cofactors.rows():
|
||||
for j in minors_cofactors.columns():
|
||||
var t_minor: Matrix = copy(matrix)
|
||||
t_minor.remove_row(i)
|
||||
t_minor.remove_column(j)
|
||||
multiplier *= -1.0
|
||||
minors_cofactors.set_value(multiplier * determinant(t_minor), i, j)
|
||||
|
||||
var transpose: Matrix = transpose(minors_cofactors)
|
||||
var determinant: float = determinant(matrix)
|
||||
|
||||
inverse = multiply_float(transpose, 1 / determinant)
|
||||
|
||||
return inverse
|
||||
|
||||
# Transpose a given Matrix
|
||||
static func transpose(_matrix : Matrix) -> Matrix:
|
||||
var array : Array = []
|
||||
array.resize(_matrix.get_size().y)
|
||||
var row : Array = []
|
||||
row.resize(_matrix.get_size().x)
|
||||
for x in array.size():
|
||||
array[x] = row.duplicate()
|
||||
for i in range(_matrix.get_size().x):
|
||||
for j in range(_matrix.get_size().y):
|
||||
array[j][i] = (_matrix.to_array()[i][j])
|
||||
return Matrix.new(array)
|
||||
|
||||
# Calculates the dot product (A*B) matrix between two Matrixes
|
||||
static func dot(_matrix1 : Matrix, _matrix2 : Matrix) -> Matrix:
|
||||
if _matrix1.get_size().y != _matrix2.get_size().x:
|
||||
printerr("matrix1 number of columns: %s must be the same as matrix2 number of rows: %s"%[_matrix1.get_size().y, _matrix2.get_size().x])
|
||||
return Matrix.new()
|
||||
var array : Array = []
|
||||
for x in range(_matrix1.get_size().x):
|
||||
var row : Array = []
|
||||
for y in range(_matrix2.get_size().y):
|
||||
var sum : float
|
||||
for k in range(_matrix1.get_size().y):
|
||||
sum += (_matrix1.to_array()[x][k]*_matrix2.to_array()[k][y])
|
||||
row.append(sum)
|
||||
array.append(row)
|
||||
return Matrix.new(array)
|
||||
|
||||
# Calculates the hadamard (element-wise product) between two Matrixes
|
||||
static func hadamard(_matrix1 : Matrix, _matrix2 : Matrix) -> Matrix:
|
||||
if _matrix1.get_size() != _matrix2.get_size():
|
||||
printerr("matrix1 size: %s must be the same as matrix2 size: %s"%[_matrix1.get_size(), _matrix2.get_size()])
|
||||
return Matrix.new()
|
||||
var array : Array = []
|
||||
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)
|
||||
|
||||
# Multiply a given Matrix for an int value
|
||||
static func multiply_int(_matrix1 : Matrix, _int : int) -> Matrix:
|
||||
var array : Array = _matrix1.to_array().duplicate()
|
||||
for x in range(_matrix1.to_array().size()):
|
||||
for y in range(_matrix1.to_array()[x].size()):
|
||||
array[x][y]*=_int
|
||||
array[x][y] = int(array[x][y])
|
||||
return Matrix.new(array)
|
||||
|
||||
# Multiply a given Matrix for a float value
|
||||
static func multiply_float(_matrix1 : Matrix, _float : float) -> Matrix:
|
||||
var array : Array = _matrix1.to_array().duplicate()
|
||||
for x in range(_matrix1.to_array().size()):
|
||||
for y in range(_matrix1.to_array()[x].size()):
|
||||
array[x][y]*=_float
|
||||
return Matrix.new(array)
|
||||
|
||||
|
||||
static func copy(matrix: Matrix) -> Matrix:
|
||||
return Matrix.new(matrix.values.duplicate(true))
|
||||
|
||||
# ------------------------------------------------------------
|
||||
static func get_letter_index(index : int) -> String:
|
||||
return "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".split(" ")[index]
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://ds4sjjjv10ywy
|
||||
64
addons/easy_charts/utilities/containers/canvas/canvas.gd
Normal file
64
addons/easy_charts/utilities/containers/canvas/canvas.gd
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
extends Control
|
||||
class_name Canvas
|
||||
|
||||
@onready var _title_lbl: Label = $CanvasContainer/Title
|
||||
@onready var _x_lbl: Label = $CanvasContainer/DataContainer/PlotContainer/XLabel
|
||||
@onready var _y_lbl: Label = $CanvasContainer/DataContainer/YLabel
|
||||
@onready var _legend: FunctionLegend = $CanvasContainer/DataContainer/FunctionLegend
|
||||
|
||||
func _ready():
|
||||
pass # Replace with function body.
|
||||
|
||||
func prepare_canvas(chart_properties: ChartProperties) -> void:
|
||||
|
||||
if chart_properties.draw_frame:
|
||||
set_color(chart_properties.colors.frame)
|
||||
set_frame_visible(true)
|
||||
else:
|
||||
set_frame_visible(false)
|
||||
|
||||
if chart_properties.show_title:
|
||||
update_title(chart_properties.title, chart_properties.colors.text)
|
||||
else:
|
||||
_title_lbl.hide()
|
||||
|
||||
if chart_properties.show_x_label:
|
||||
update_x_label(chart_properties.x_label, chart_properties.colors.text)
|
||||
else:
|
||||
_x_lbl.hide()
|
||||
|
||||
if chart_properties.show_y_label:
|
||||
update_y_label(chart_properties.y_label, chart_properties.colors.text, -90)
|
||||
else:
|
||||
_y_lbl.hide()
|
||||
|
||||
if chart_properties.show_legend:
|
||||
_legend.show()
|
||||
else:
|
||||
hide_legend()
|
||||
|
||||
func update_title(text: String, color: Color, rotation: float = 0.0) -> void:
|
||||
_title_lbl.show()
|
||||
_update_canvas_label(_title_lbl, text, color, rotation)
|
||||
|
||||
func update_y_label(text: String, color: Color, rotation: float = 0.0) -> void:
|
||||
_y_lbl.show()
|
||||
_update_canvas_label(_y_lbl, text, color, rotation)
|
||||
|
||||
func update_x_label(text: String, color: Color, rotation: float = 0.0) -> void:
|
||||
_x_lbl.show()
|
||||
_update_canvas_label(_x_lbl, text, color, rotation)
|
||||
|
||||
func _update_canvas_label(canvas_label: Label, text: String, color: Color, rotation: float = 0.0) -> void:
|
||||
canvas_label.set_text(text)
|
||||
canvas_label.modulate = color
|
||||
canvas_label.rotation = rotation
|
||||
|
||||
func hide_legend() -> void:
|
||||
_legend.hide()
|
||||
|
||||
func set_color(color: Color) -> void:
|
||||
get("theme_override_styles/panel").set("bg_color", color)
|
||||
|
||||
func set_frame_visible(visible: bool) -> void:
|
||||
get("theme_override_styles/panel").set("draw_center", visible)
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://d0q44x2e5wxym
|
||||
|
|
@ -0,0 +1,176 @@
|
|||
extends Control
|
||||
class_name GridBox
|
||||
|
||||
var x_domain: Dictionary = { lb = 0, ub = 0 }
|
||||
var x_labels_function: Callable = Callable()
|
||||
|
||||
var y_domain: Dictionary = { lb = 0, ub = 0 }
|
||||
var y_labels_function: Callable = Callable()
|
||||
|
||||
var box: Rect2
|
||||
var plot_box: Rect2
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready():
|
||||
pass # Replace with function body.
|
||||
|
||||
func set_domains(x_domain: Dictionary, y_domain: Dictionary) -> void:
|
||||
self.x_domain = x_domain
|
||||
self.y_domain = y_domain
|
||||
|
||||
func set_labels_functions(x_labels_function: Callable, y_labels_function: Callable) -> void:
|
||||
self.x_labels_function = x_labels_function
|
||||
self.y_labels_function = y_labels_function
|
||||
|
||||
func _draw() -> void:
|
||||
if get_parent().chart_properties == null:
|
||||
printerr("Cannot draw GridBox without ChartProperties!")
|
||||
return
|
||||
|
||||
self.box = get_parent().get_box()
|
||||
self.plot_box = get_parent().get_plot_box()
|
||||
|
||||
if get_parent().chart_properties.draw_background:
|
||||
_draw_background()
|
||||
|
||||
if get_parent().chart_properties.draw_grid_box:
|
||||
_draw_vertical_grid()
|
||||
_draw_horizontal_grid()
|
||||
|
||||
if get_parent().chart_properties.draw_origin:
|
||||
_draw_origin()
|
||||
|
||||
if get_parent().chart_properties.draw_bounding_box:
|
||||
_draw_bounding_box()
|
||||
|
||||
func _draw_background() -> void:
|
||||
draw_rect(self.box, get_parent().chart_properties.colors.background, true)# false) TODOGODOT4 Antialiasing argument is missing
|
||||
|
||||
func _draw_bounding_box() -> void:
|
||||
var box: Rect2 = self.box
|
||||
box.position.y += 1
|
||||
draw_rect(box, get_parent().chart_properties.colors.bounding_box, false, 1)# true) TODOGODOT4 Antialiasing argument is missing
|
||||
|
||||
func _draw_origin() -> void:
|
||||
var xorigin: float = ECUtilities._map_domain(0.0, x_domain, { lb = self.plot_box.position.x, ub = self.plot_box.end.x })
|
||||
var yorigin: float = ECUtilities._map_domain(0.0, y_domain, { lb = self.plot_box.end.y, ub = self.plot_box.position.y })
|
||||
|
||||
draw_line(Vector2(xorigin, self.plot_box.position.y), Vector2(xorigin, self.plot_box.position.y + self.plot_box.size.y), get_parent().chart_properties.colors.origin, 1)
|
||||
draw_line(Vector2(self.plot_box.position.x, yorigin), Vector2(self.plot_box.position.x + self.plot_box.size.x, yorigin), get_parent().chart_properties.colors.origin, 1)
|
||||
draw_string(
|
||||
get_parent().chart_properties.font, Vector2(xorigin, yorigin) - Vector2(15, -15), "O", HORIZONTAL_ALIGNMENT_CENTER, -1,
|
||||
ThemeDB.fallback_font_size, get_parent().chart_properties.colors.text, TextServer.JUSTIFICATION_NONE, TextServer.DIRECTION_AUTO, TextServer.ORIENTATION_HORIZONTAL
|
||||
)
|
||||
|
||||
|
||||
func _draw_vertical_grid() -> void:
|
||||
# draw vertical lines
|
||||
|
||||
# 1. the amount of lines is equals to the X_scale: it identifies in how many sectors the x domain
|
||||
# should be devided
|
||||
# 2. calculate the spacing between each line in pixel. It is equals to x_sampled_domain / x_scale
|
||||
# 3. calculate the offset in the real x domain, which is x_domain / x_scale.
|
||||
var scaler: int = get_parent().chart_properties.x_scale
|
||||
var x_pixel_dist: float = self.plot_box.size.x / scaler
|
||||
|
||||
var vertical_grid: PackedVector2Array = []
|
||||
var vertical_ticks: PackedVector2Array = []
|
||||
|
||||
for _x in (scaler + 1):
|
||||
var x_sampled_val: float = (_x * x_pixel_dist) + self.plot_box.position.x
|
||||
var x_val: float = ECUtilities._map_domain(x_sampled_val, { lb = self.plot_box.position.x, ub = self.plot_box.end.x }, x_domain)
|
||||
|
||||
var top: Vector2 = Vector2(x_sampled_val, self.box.position.y)
|
||||
var bottom: Vector2 = Vector2(x_sampled_val, self.box.end.y)
|
||||
|
||||
vertical_grid.append(top)
|
||||
vertical_grid.append(bottom)
|
||||
|
||||
vertical_ticks.append(bottom)
|
||||
vertical_ticks.append(bottom + Vector2(0, get_parent().chart_properties.x_tick_size))
|
||||
|
||||
# Draw V Tick Labels
|
||||
if get_parent().chart_properties.show_tick_labels:
|
||||
var tick_lbl: String = _get_tick_label(_x, x_val, x_domain.has_decimals, self.x_labels_function)
|
||||
draw_string(
|
||||
get_parent().chart_properties.font,
|
||||
_get_vertical_tick_label_pos(bottom, tick_lbl),
|
||||
tick_lbl,HORIZONTAL_ALIGNMENT_CENTER, -1, ThemeDB.fallback_font_size,
|
||||
get_parent().chart_properties.colors.text, TextServer.JUSTIFICATION_NONE, TextServer.DIRECTION_AUTO,
|
||||
TextServer.ORIENTATION_HORIZONTAL
|
||||
)
|
||||
|
||||
# Draw V Grid
|
||||
if get_parent().chart_properties.draw_vertical_grid:
|
||||
draw_multiline(vertical_grid, get_parent().chart_properties.colors.grid, 1)
|
||||
|
||||
# Draw V Ticks
|
||||
if get_parent().chart_properties.draw_ticks:
|
||||
draw_multiline(vertical_ticks, get_parent().chart_properties.colors.ticks, 1)
|
||||
|
||||
|
||||
func _draw_horizontal_grid() -> void:
|
||||
# 1. the amount of lines is equals to the y_scale: it identifies in how many sectors the y domain
|
||||
# should be devided
|
||||
# 2. calculate the spacing between each line in pixel. It is equals to y_sampled_domain / y_scale
|
||||
# 3. calculate the offset in the real y domain, which is y_domain / y_scale.
|
||||
var scaler: int = get_parent().chart_properties.y_scale
|
||||
var y_pixel_dist: float = self.plot_box.size.y / scaler
|
||||
|
||||
var horizontal_grid: PackedVector2Array = []
|
||||
var horizontal_ticks: PackedVector2Array = []
|
||||
|
||||
for _y in (scaler + 1):
|
||||
var y_sampled_val: float = (_y * y_pixel_dist) + self.plot_box.position.y
|
||||
var y_val: float = ECUtilities._map_domain(y_sampled_val, { lb = self.plot_box.end.y, ub = self.plot_box.position.y }, y_domain)
|
||||
|
||||
var left: Vector2 = Vector2(self.box.position.x, y_sampled_val)
|
||||
var right: Vector2 = Vector2(self.box.end.x, y_sampled_val)
|
||||
|
||||
horizontal_grid.append(left)
|
||||
horizontal_grid.append(right)
|
||||
|
||||
horizontal_ticks.append(left)
|
||||
horizontal_ticks.append(left - Vector2(get_parent().chart_properties.y_tick_size, 0))
|
||||
|
||||
# Draw H Tick Labels
|
||||
if get_parent().chart_properties.show_tick_labels:
|
||||
var tick_lbl: String = _get_tick_label(_y, y_val, y_domain.has_decimals, y_labels_function)
|
||||
draw_string(
|
||||
get_parent().chart_properties.font,
|
||||
_get_horizontal_tick_label_pos(left, tick_lbl),
|
||||
tick_lbl,
|
||||
HORIZONTAL_ALIGNMENT_CENTER,
|
||||
-1, ThemeDB.fallback_font_size,
|
||||
get_parent().chart_properties.colors.text,
|
||||
TextServer.JUSTIFICATION_NONE, TextServer.DIRECTION_AUTO, TextServer.ORIENTATION_HORIZONTAL
|
||||
)
|
||||
|
||||
# Draw H Grid
|
||||
if get_parent().chart_properties.draw_horizontal_grid:
|
||||
draw_multiline(horizontal_grid, get_parent().chart_properties.colors.grid, 1)
|
||||
|
||||
# Draw H Ticks
|
||||
if get_parent().chart_properties.draw_ticks:
|
||||
draw_multiline(horizontal_ticks, get_parent().chart_properties.colors.ticks, 1)
|
||||
|
||||
|
||||
func _get_vertical_tick_label_pos(base_position: Vector2, text: String) -> Vector2:
|
||||
return base_position + Vector2(
|
||||
- get_parent().chart_properties.font.get_string_size(text).x / 2,
|
||||
ThemeDB.fallback_font_size + get_parent().chart_properties.x_tick_size
|
||||
)
|
||||
|
||||
func _get_horizontal_tick_label_pos(base_position: Vector2, text: String) -> Vector2:
|
||||
return base_position - Vector2(
|
||||
get_parent().chart_properties.font.get_string_size(text).x + get_parent().chart_properties.y_tick_size + get_parent().chart_properties.x_ticklabel_space,
|
||||
- ThemeDB.fallback_font_size * 0.35
|
||||
)
|
||||
|
||||
func _get_tick_label(line_index: int, line_value: float, axis_has_decimals: bool, labels_function: Callable) -> String:
|
||||
var tick_lbl: String = ""
|
||||
if labels_function.is_null():
|
||||
tick_lbl = ECUtilities._format_value(line_value, axis_has_decimals)
|
||||
else:
|
||||
tick_lbl = labels_function.call(line_value)
|
||||
return tick_lbl
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://oql4qoedksyg
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
extends Control
|
||||
class_name PlotBox
|
||||
|
||||
signal function_point_entered(point, function)
|
||||
signal function_point_exited(point, function)
|
||||
@onready var tooltip: DataTooltip = $Tooltip
|
||||
|
||||
var focused_point: Point
|
||||
var focused_function: Function
|
||||
|
||||
var x_labels_function: Callable = Callable()
|
||||
var y_labels_function: Callable = Callable()
|
||||
|
||||
var box_margins: Vector2 # Margins relative to this rect, in order to make space for ticks and tick_labels
|
||||
var plot_inner_offset: Vector2 = Vector2(15, 15) # How many pixels from the broders should the plot be
|
||||
|
||||
var chart_properties: ChartProperties
|
||||
|
||||
func set_labels_functions(x_labels_function: Callable, y_labels_function: Callable) -> void:
|
||||
self.x_labels_function = x_labels_function
|
||||
self.y_labels_function = y_labels_function
|
||||
|
||||
func get_box() -> Rect2:
|
||||
var box: Rect2 = get_rect()
|
||||
box.position.x += box_margins.x
|
||||
# box.position.y += box_margins.y
|
||||
box.end.x -= box_margins.x
|
||||
box.end.y -= box_margins.y
|
||||
return box
|
||||
|
||||
func get_plot_box() -> Rect2:
|
||||
var inner_box: Rect2 = get_box()
|
||||
inner_box.position.x += plot_inner_offset.x
|
||||
inner_box.position.y += plot_inner_offset.y
|
||||
inner_box.end.x -= plot_inner_offset.x * 2
|
||||
inner_box.end.y -= plot_inner_offset.y * 2
|
||||
return inner_box
|
||||
|
||||
func _on_point_entered(point: Point, function: Function, props: Dictionary = {}) -> void:
|
||||
self.focused_function = function
|
||||
var x_value: String = x_labels_function.call(point.value.x) if not x_labels_function.is_null() else \
|
||||
point.value.x if point.value.x is String else ECUtilities._format_value(point.value.x, ECUtilities._is_decimal(point.value.x))
|
||||
var y_value: String = y_labels_function.call(point.value.y) if not y_labels_function.is_null() else \
|
||||
point.value.y if point.value.y is String else ECUtilities._format_value(point.value.y, ECUtilities._is_decimal(point.value.y))
|
||||
var color: Color = function.get_color() if function.get_type() != Function.Type.PIE \
|
||||
else function.get_gradient().sample(props.interpolation_index)
|
||||
tooltip.show()
|
||||
tooltip.update_values(x_value, y_value, function.name, color)
|
||||
tooltip.update_position(point.position)
|
||||
emit_signal("function_point_entered", point, function)
|
||||
|
||||
func _on_point_exited(point: Point, function: Function) -> void:
|
||||
if function != self.focused_function:
|
||||
return
|
||||
tooltip.hide()
|
||||
emit_signal("function_point_exited", point, function)
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://bt0h3q1ocyhu8
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
@tool
|
||||
extends PanelContainer
|
||||
class_name DataTooltip
|
||||
|
||||
var gap: float = 15
|
||||
|
||||
@onready var x_lbl : Label = $PointData/x
|
||||
@onready var y_lbl : Label = $PointData/Value/y
|
||||
@onready var func_lbl : Label = $PointData/Value/Function
|
||||
@onready var color_rect: Panel = $PointData/Value/Color
|
||||
|
||||
func _ready():
|
||||
hide()
|
||||
update_size()
|
||||
|
||||
func update_position(position: Vector2) -> void:
|
||||
if (position.x + gap + size.x) > get_parent().size.x:
|
||||
self.position = position - Vector2(size.x + gap, (get_rect().size.y / 2))
|
||||
else:
|
||||
self.position = position + Vector2(15, - (get_rect().size.y / 2))
|
||||
|
||||
#func _process(delta):
|
||||
# if Engine.editor_hint:
|
||||
# return
|
||||
# rect_position = get_global_mouse_position() + Vector2(15, - (get_rect().size.y / 2))
|
||||
|
||||
func set_font(font: FontFile) -> void:
|
||||
theme.set("default_font", font)
|
||||
|
||||
func update_values(x: String, y: String, function_name: String, color: Color):
|
||||
x_lbl.set_text(x)
|
||||
y_lbl.set_text(y)
|
||||
func_lbl.set_text(function_name)
|
||||
color_rect.get("theme_override_styles/panel").set("bg_color", color)
|
||||
|
||||
func update_size():
|
||||
x_lbl.set_text("")
|
||||
y_lbl.set_text("")
|
||||
func_lbl.set_text("")
|
||||
size = Vector2.ZERO
|
||||
|
||||
func _on_DataTooltip_visibility_changed():
|
||||
if not visible:
|
||||
update_size()
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://b13bsc7on5q6e
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
[gd_scene load_steps=5 format=2]
|
||||
|
||||
[ext_resource path="res://addons/easy_charts/utilities/containers/data_tooltip/data_tooltip.gd" type="Script" id=1]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id=1]
|
||||
content_margin_left = 10.0
|
||||
content_margin_right = 10.0
|
||||
content_margin_top = 8.0
|
||||
content_margin_bottom = 8.0
|
||||
bg_color = Color( 0.101961, 0.101961, 0.101961, 0.784314 )
|
||||
border_color = Color( 1, 1, 1, 1 )
|
||||
corner_radius_top_left = 8
|
||||
corner_radius_top_right = 8
|
||||
corner_radius_bottom_right = 8
|
||||
corner_radius_bottom_left = 8
|
||||
corner_detail = 20
|
||||
anti_aliasing_size = 0.65
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id=2]
|
||||
corner_radius_top_left = 5
|
||||
corner_radius_top_right = 5
|
||||
corner_radius_bottom_right = 5
|
||||
corner_radius_bottom_left = 5
|
||||
corner_detail = 20
|
||||
anti_aliasing_size = 0.7
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id=3]
|
||||
|
||||
[node name="DataTooltip" type="PanelContainer"]
|
||||
visible = false
|
||||
offset_right = 20.0
|
||||
offset_bottom = 16.0
|
||||
mouse_filter = 2
|
||||
theme_override_styles/panel = SubResource( 1 )
|
||||
script = ExtResource( 1 )
|
||||
|
||||
[node name="PointData" type="VBoxContainer" parent="."]
|
||||
offset_left = 10.0
|
||||
offset_top = 8.0
|
||||
offset_right = 37.0
|
||||
offset_bottom = 42.0
|
||||
grow_horizontal = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_override_constants/separation = 1
|
||||
alignment = 1
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="x" type="Label" parent="PointData"]
|
||||
offset_top = 2.0
|
||||
offset_bottom = 16.0
|
||||
size_flags_horizontal = 0
|
||||
theme_override_colors/font_color = Color( 1, 1, 1, 1 )
|
||||
valign = 1
|
||||
|
||||
[node name="Value" type="HBoxContainer" parent="PointData"]
|
||||
offset_top = 17.0
|
||||
offset_right = 27.0
|
||||
offset_bottom = 31.0
|
||||
grow_horizontal = 2
|
||||
size_flags_horizontal = 7
|
||||
theme_override_constants/separation = 1
|
||||
|
||||
[node name="Color" type="Panel" parent="PointData/Value"]
|
||||
offset_top = 2.0
|
||||
offset_right = 10.0
|
||||
offset_bottom = 12.0
|
||||
custom_minimum_size = Vector2( 10, 10 )
|
||||
size_flags_horizontal = 4
|
||||
size_flags_vertical = 4
|
||||
theme_override_styles/panel = SubResource( 2 )
|
||||
|
||||
[node name="VSeparator" type="VSeparator" parent="PointData/Value"]
|
||||
offset_left = 11.0
|
||||
offset_right = 15.0
|
||||
offset_bottom = 14.0
|
||||
theme_override_constants/separation = 4
|
||||
theme_override_styles/separator = SubResource( 3 )
|
||||
|
||||
[node name="Function" type="Label" parent="PointData/Value"]
|
||||
offset_left = 16.0
|
||||
offset_right = 16.0
|
||||
offset_bottom = 14.0
|
||||
size_flags_horizontal = 0
|
||||
size_flags_vertical = 5
|
||||
valign = 1
|
||||
|
||||
[node name="sep" type="Label" parent="PointData/Value"]
|
||||
offset_left = 17.0
|
||||
offset_right = 21.0
|
||||
offset_bottom = 14.0
|
||||
size_flags_horizontal = 0
|
||||
size_flags_vertical = 5
|
||||
text = ":"
|
||||
valign = 1
|
||||
|
||||
[node name="VSeparator2" type="VSeparator" parent="PointData/Value"]
|
||||
offset_left = 22.0
|
||||
offset_right = 26.0
|
||||
offset_bottom = 14.0
|
||||
theme_override_constants/separation = 4
|
||||
theme_override_styles/separator = SubResource( 3 )
|
||||
|
||||
[node name="y" type="Label" parent="PointData/Value"]
|
||||
offset_left = 27.0
|
||||
offset_right = 27.0
|
||||
offset_bottom = 14.0
|
||||
size_flags_horizontal = 0
|
||||
size_flags_vertical = 5
|
||||
theme_override_colors/font_color = Color( 1, 1, 1, 1 )
|
||||
valign = 1
|
||||
|
||||
[connection signal="visibility_changed" from="." to="." method="_on_DataTooltip_visibility_changed"]
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
extends HBoxContainer
|
||||
class_name FunctionLabel
|
||||
|
||||
@onready var type_lbl: FunctionTypeLabel = $FunctionType
|
||||
@onready var name_lbl: Label = $FunctionName
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready():
|
||||
pass # Replace with function body.
|
||||
|
||||
func init_label(function: Function) -> void:
|
||||
type_lbl.type = function.get_type()
|
||||
type_lbl.color = function.get_color()
|
||||
type_lbl.marker = function.get_marker()
|
||||
name_lbl.set_text(function.name)
|
||||
name_lbl.set("theme_override_colors/font_color", get_parent().chart_properties.colors.text)
|
||||
|
||||
func init_clabel(type: int, color: Color, marker: int, name: String) -> void:
|
||||
type_lbl.type = type
|
||||
type_lbl.color = color
|
||||
type_lbl.marker = marker
|
||||
name_lbl.set_text(name)
|
||||
name_lbl.set("theme_override_colors/font_color", get_parent().chart_properties.colors.text)
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://bkdv3quptaxde
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
[gd_scene load_steps=3 format=2]
|
||||
|
||||
[ext_resource path="res://addons/easy_charts/utilities/containers/legend/function_type.gd" type="Script" id=1]
|
||||
[ext_resource path="res://addons/easy_charts/utilities/containers/legend/function_label.gd" type="Script" id=2]
|
||||
|
||||
[node name="FunctionLabel" type="HBoxContainer"]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
theme_override_constants/separation = 5
|
||||
script = ExtResource( 2 )
|
||||
|
||||
[node name="FunctionType" type="Label" parent="."]
|
||||
offset_top = 293.0
|
||||
offset_right = 20.0
|
||||
offset_bottom = 307.0
|
||||
custom_minimum_size = Vector2( 20, 0 )
|
||||
script = ExtResource( 1 )
|
||||
|
||||
[node name="FunctionName" type="Label" parent="."]
|
||||
offset_left = 25.0
|
||||
offset_top = 293.0
|
||||
offset_right = 25.0
|
||||
offset_bottom = 307.0
|
||||
theme_override_colors/font_color = Color( 0, 0, 0, 1 )
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
extends VBoxContainer
|
||||
class_name FunctionLegend
|
||||
|
||||
@onready var f_label_scn: PackedScene = preload("res://addons/easy_charts/utilities/containers/legend/function_label.tscn")
|
||||
|
||||
var chart_properties: ChartProperties
|
||||
|
||||
func _ready() -> void:
|
||||
pass
|
||||
|
||||
func clear() -> void:
|
||||
for label in get_children():
|
||||
label.queue_free()
|
||||
|
||||
func add_function(function: Function) -> void:
|
||||
var f_label: FunctionLabel = f_label_scn.instantiate()
|
||||
add_child(f_label)
|
||||
f_label.init_label(function)
|
||||
|
||||
func add_label(type: int, color: Color, marker: int, name: String) -> void:
|
||||
var f_label: FunctionLabel = f_label_scn.instantiate()
|
||||
add_child(f_label)
|
||||
f_label.init_clabel(type, color, marker, name)
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://b8cjm6kaq00kl
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
[gd_scene load_steps=2 format=2]
|
||||
|
||||
[ext_resource path="res://addons/easy_charts/utilities/containers/legend/function_legend.gd" type="Script" id=1]
|
||||
|
||||
[node name="FunctionLegend" type="VBoxContainer"]
|
||||
offset_right = 80.0
|
||||
offset_bottom = 26.0
|
||||
script = ExtResource( 1 )
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
extends Label
|
||||
class_name FunctionTypeLabel
|
||||
|
||||
var type: int
|
||||
var marker: int
|
||||
var color: Color
|
||||
|
||||
func _draw() -> void:
|
||||
var center: Vector2 = get_rect().get_center()
|
||||
|
||||
match self.type:
|
||||
Function.Type.LINE:
|
||||
draw_line(
|
||||
Vector2(get_rect().position.x, center.y),
|
||||
Vector2(get_rect().end.x, center.y),
|
||||
color, 3
|
||||
)
|
||||
Function.Type.AREA:
|
||||
var c2: Color = color
|
||||
c2.a = 0.3
|
||||
draw_rect(
|
||||
Rect2(
|
||||
Vector2(get_rect().position.x, center.y),
|
||||
Vector2(get_rect().end.x, get_rect().end.y / 2)
|
||||
),
|
||||
c2, 3
|
||||
)
|
||||
draw_line(
|
||||
Vector2(get_rect().position.x, center.y),
|
||||
Vector2(get_rect().end.x, center.y),
|
||||
color, 3
|
||||
)
|
||||
Function.Type.PIE:
|
||||
draw_rect(
|
||||
Rect2(center - (Vector2.ONE * 3), (Vector2.ONE * 3 * 2)),
|
||||
color, true, 1.0
|
||||
)
|
||||
Function.Type.SCATTER, _:
|
||||
pass
|
||||
|
||||
match self.marker:
|
||||
Function.Marker.NONE:
|
||||
pass
|
||||
Function.Marker.SQUARE:
|
||||
draw_rect(
|
||||
Rect2(center - (Vector2.ONE * 3), (Vector2.ONE * 3 * 2)),
|
||||
color, true, 1.0
|
||||
)
|
||||
Function.Marker.TRIANGLE:
|
||||
draw_colored_polygon(
|
||||
PackedVector2Array([
|
||||
center + (Vector2.UP * 3 * 1.3),
|
||||
center + (Vector2.ONE * 3 * 1.3),
|
||||
center - (Vector2(1, -1) * 3 * 1.3)
|
||||
]), color, [], null
|
||||
)
|
||||
Function.Marker.CROSS:
|
||||
draw_line(
|
||||
center - (Vector2.ONE * 3),
|
||||
center + (Vector2.ONE * 3),
|
||||
color, 3, true
|
||||
)
|
||||
draw_line(
|
||||
center + (Vector2(1, -1) * 3),
|
||||
center + (Vector2(-1, 1) * 3),
|
||||
color, 3 / 2, true
|
||||
)
|
||||
Function.Marker.CIRCLE, _:
|
||||
draw_circle(center, 3, color)
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://chxuvkwtm4y6u
|
||||
1
addons/easy_charts/utilities/icons/linechart.svg
Normal file
1
addons/easy_charts/utilities/icons/linechart.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns:xlink="http://www.w3.org/1999/xlink" width="24.276" xmlns="http://www.w3.org/2000/svg" height="24.276" id="screenshot-fea98781-87c2-80d4-8002-03a4453d007c" viewBox="-0.138 -0.138 24.276 24.276" style="-webkit-print-color-adjust: exact;" fill="none" version="1.1"><g id="shape-fea98781-87c2-80d4-8002-03a4453d007c" width="12" version="1.1" height="12" rx="0" ry="0" style="fill: rgb(0, 0, 0);"><g id="shape-fea98781-87c2-80d4-8002-03a4453ee7a8"><defs id="defs2" rx="0" ry="0" style="fill: rgb(0, 0, 0);"/></g><g id="shape-fea98781-87c2-80d4-8002-03a4453f6292" rx="0" ry="0" style="fill: rgb(0, 0, 0);"><g id="shape-fea98781-87c2-80d4-8002-03a44542b380"><g class="fills" id="fills-fea98781-87c2-80d4-8002-03a44542b380"><rect rx="0" ry="0" x="0" y="0" transform="matrix(1.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000)" width="3" height="23.999930224010313" style="stroke: none; stroke-miterlimit: 4; stroke-dasharray: none; fill: rgb(165, 239, 172); fill-opacity: 1;"/></g><g id="strokes-fea98781-87c2-80d4-8002-03a44542b380" class="strokes"><g class="stroke-shape"><rect rx="0" ry="0" x="0" y="0" transform="matrix(1.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000)" width="3" height="23.999930224010313" style="stroke: none; stroke-miterlimit: 4; stroke-dasharray: none; fill: none; stroke-width: 0.275386; stroke-opacity: 1;"/></g></g></g><g id="shape-fea98781-87c2-80d4-8002-03a44542dd7d"><g class="fills" id="fills-fea98781-87c2-80d4-8002-03a44542dd7d"><rect rx="0" ry="0" x="0" y="21" transform="matrix(1.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000)" width="24" height="3" style="stroke: none; stroke-miterlimit: 4; stroke-dasharray: none; fill: rgb(165, 239, 172); fill-opacity: 1;"/></g><g id="strokes-fea98781-87c2-80d4-8002-03a44542dd7d" class="strokes"><g class="stroke-shape"><rect rx="0" ry="0" x="0" y="21" transform="matrix(1.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000)" width="24" height="3" style="stroke: none; stroke-miterlimit: 4; stroke-dasharray: none; fill: none; stroke-width: 0.276348; stroke-opacity: 1;"/></g></g></g><g id="shape-fea98781-87c2-80d4-8002-03a44543293d"><g class="fills" id="fills-fea98781-87c2-80d4-8002-03a44543293d"><rect rx="1.5" ry="1.5" x="7.400090781751828" y="10.320378028651703" transform="matrix(-0.485732, -0.874108, 0.895244, -0.445577, 7.469654, 27.707754)" width="9.499999999999886" height="3" style="fill-rule: evenodd; stroke: none; stroke-miterlimit: 4; stroke-dasharray: none; fill: rgb(165, 239, 172); fill-opacity: 1;"/></g></g><g id="shape-fea98781-87c2-80d4-8002-03a44543a59d"><g class="fills" id="fills-fea98781-87c2-80d4-8002-03a44543a59d"><rect rx="1.5" ry="1.5" x="9.916580393732545" y="9.051625054708893" transform="matrix(0.493265, -0.869879, -0.891361, -0.453294, 17.850846, 29.832525)" width="13.5" height="3" style="fill-rule: evenodd; stroke: none; stroke-miterlimit: 4; stroke-dasharray: none; fill: rgb(165, 239, 172); fill-opacity: 1;"/></g></g><g id="shape-fea98781-87c2-80d4-8002-03a44544bca7"><g class="fills" id="fills-fea98781-87c2-80d4-8002-03a44544bca7"><rect rx="1.5" ry="1.5" x="2.4233153669828766" y="11.181745974957721" transform="matrix(0.493265, -0.869879, -0.891361, -0.453294, 15.445718, 25.540104)" width="11.499999999999943" height="3" style="fill-rule: evenodd; stroke: none; stroke-miterlimit: 4; stroke-dasharray: none; fill: rgb(165, 239, 172); fill-opacity: 1;"/></g></g><g id="shape-fea98781-87c2-80d4-8002-03a44546f189"><g class="fills" id="fills-fea98781-87c2-80d4-8002-03a44546f189"><path rx="0" ry="0" d="M20.752,0.000C19.889,-0.001,19.062,0.341,18.452,0.951C17.842,1.561,17.500,2.388,17.500,3.250C17.500,4.112,17.842,4.939,18.452,5.549C19.062,6.159,19.889,6.501,20.752,6.500C21.614,6.500,22.440,6.157,23.049,5.548C23.658,4.938,24.000,4.112,24.000,3.250C24.000,2.388,23.658,1.562,23.049,0.952C22.440,0.343,21.614,0.000,20.752,0.000ZZM20.752,1.680C21.160,1.680,21.551,1.845,21.839,2.140C22.127,2.434,22.289,2.834,22.289,3.250C22.289,3.666,22.127,4.066,21.839,4.360C21.551,4.655,21.160,4.820,20.752,4.820C20.344,4.820,19.953,4.655,19.665,4.360C19.377,4.066,19.215,3.666,19.215,3.250C19.215,2.834,19.377,2.434,19.665,2.140C19.953,1.845,20.344,1.680,20.752,1.680ZZ" style="fill-rule: evenodd; stroke: none; stroke-miterlimit: 4; stroke-dasharray: none; fill: rgb(165, 239, 172); fill-opacity: 1;"/></g></g></g></g></svg>
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
43
addons/easy_charts/utilities/icons/linechart.svg.import
Normal file
43
addons/easy_charts/utilities/icons/linechart.svg.import
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cbpprqhqhq1xp"
|
||||
path="res://.godot/imported/linechart.svg-922834f0462a2c88be644081c47c63ad.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/easy_charts/utilities/icons/linechart.svg"
|
||||
dest_files=["res://.godot/imported/linechart.svg-922834f0462a2c88be644081c47c63ad.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
||||
71
addons/easy_charts/utilities/scripts/ec_utilities.gd
Normal file
71
addons/easy_charts/utilities/scripts/ec_utilities.gd
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
extends RefCounted
|
||||
class_name ECUtilities
|
||||
|
||||
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"
|
||||
|
||||
func _ready():
|
||||
pass
|
||||
|
||||
static func _map_domain(value: float, from_domain: Dictionary, to_domain: Dictionary) -> float:
|
||||
return remap(value, from_domain.lb, from_domain.ub, to_domain.lb, to_domain.ub)
|
||||
|
||||
static func _format_value(value: float, is_decimal: bool) -> String:
|
||||
return ("%.2f" if is_decimal else "%s") % snapped(value, 0.01)
|
||||
|
||||
### Utility Inner functions ###
|
||||
|
||||
static func _contains_string(array: Array) -> bool:
|
||||
for value in array:
|
||||
if value is String:
|
||||
return true
|
||||
return false
|
||||
|
||||
static func _is_decimal(value: float) -> bool:
|
||||
return abs(fmod(value, 1)) > 0.0
|
||||
|
||||
static func _has_decimals(values: Array) -> bool:
|
||||
var temp: Array = values.duplicate(true)
|
||||
|
||||
for dim in temp:
|
||||
for val in dim:
|
||||
if val is String:
|
||||
return false
|
||||
if abs(fmod(val, 1)) > 0.0:
|
||||
return true
|
||||
|
||||
return false
|
||||
|
||||
static func _find_min_max(values: Array) -> Dictionary:
|
||||
var temp: Array = values.duplicate(true)
|
||||
var _min: float
|
||||
var _max: float
|
||||
|
||||
var min_ts: Array
|
||||
var max_ts: Array
|
||||
for dim in temp:
|
||||
min_ts.append(dim.min())
|
||||
max_ts.append(dim.max())
|
||||
_min = min_ts.min()
|
||||
_max = max_ts.max()
|
||||
|
||||
return { min = _min, max = _max }
|
||||
|
||||
static func _sample_values(values: Array, from_domain: Dictionary, to_domain: Dictionary) -> PackedFloat32Array:
|
||||
if values.is_empty():
|
||||
printerr("Trying to plot an empty dataset!")
|
||||
return PackedFloat32Array()
|
||||
|
||||
# We are not considering String values here!!!
|
||||
|
||||
var sampled: PackedFloat32Array = []
|
||||
|
||||
for value in values:
|
||||
sampled.push_back(_map_domain(value, from_domain, to_domain))
|
||||
|
||||
return sampled
|
||||
|
||||
static func _round_min(val: float) -> float:
|
||||
return round(val) if abs(val) < 10 else floor(val / 10.0) * 10.0
|
||||
|
||||
static func _round_max(val: float) -> float:
|
||||
return round(val) if abs(val) < 10 else ceil(val / 10.0) * 10.0
|
||||
1
addons/easy_charts/utilities/scripts/ec_utilities.gd.uid
Normal file
1
addons/easy_charts/utilities/scripts/ec_utilities.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://dc2s3hmwcx2p0
|
||||
41
easy_charts.svg
Normal file
41
easy_charts.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 71 KiB |
43
easy_charts.svg.import
Normal file
43
easy_charts.svg.import
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://mxify2wd8r0m"
|
||||
path="res://.godot/imported/easy_charts.svg-5963eccbc8d55a9c4bb3a7a394bde2c4.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://easy_charts.svg"
|
||||
dest_files=["res://.godot/imported/easy_charts.svg-5963eccbc8d55a9c4bb3a7a394bde2c4.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
||||
33
examples/plot_sint.gd
Normal file
33
examples/plot_sint.gd
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
@tool
|
||||
extends Control
|
||||
|
||||
var plot_sin
|
||||
var x = 0.0
|
||||
var draw_enabled = false:
|
||||
set(value):
|
||||
draw_enabled = value
|
||||
# if is_instance_valid($Graph2D):
|
||||
# $Graph2D.background_color = Color.SLATE_GRAY if draw_enabled else Color.BLACK
|
||||
|
||||
func _ready():
|
||||
plot_sin = $Graph2D.add_plot_item("Sin(x)", Color.RED, 0.5)
|
||||
|
||||
func _process(_delta):
|
||||
if draw_enabled:
|
||||
var y: float = sin(x)
|
||||
plot_sin.add_point(Vector2(x,y))
|
||||
x += 0.1
|
||||
|
||||
if draw_enabled and x > $Graph2D.x_max:
|
||||
draw_enabled = false
|
||||
|
||||
func _on_draw_button_pressed() -> void:
|
||||
draw_enabled = true
|
||||
plot_sin.remove_all()
|
||||
x = 0.0
|
||||
|
||||
|
||||
func _on_clear_button_pressed() -> void:
|
||||
draw_enabled = false
|
||||
plot_sin.remove_all()
|
||||
x = 0.0
|
||||
1
examples/plot_sint.gd.uid
Normal file
1
examples/plot_sint.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://dun708xbgrl0u
|
||||
59
examples/plot_sint.tscn
Normal file
59
examples/plot_sint.tscn
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
[gd_scene load_steps=3 format=3 uid="uid://dwuptr3eos1kt"]
|
||||
|
||||
[ext_resource type="Script" path="res://examples/plot_sint.gd" id="1_nl7wu"]
|
||||
[ext_resource type="Script" path="res://addons/graph_2d/graph_2d.gd" id="2_o0pag"]
|
||||
|
||||
[node name="PlotSint" type="Control"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("1_nl7wu")
|
||||
|
||||
[node name="Graph2D" type="Control" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("2_o0pag")
|
||||
x_label = "t[s]"
|
||||
y_min = -2.0
|
||||
y_max = 2.0
|
||||
y_label = "y"
|
||||
grid_horizontal_visible = true
|
||||
grid_vertical_visible = true
|
||||
metadata/_edit_layout_mode = 0
|
||||
metadata/_edit_use_custom_anchors = false
|
||||
|
||||
[node name="DrawButton" type="Button" parent="Graph2D"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 5
|
||||
anchor_left = 0.5
|
||||
anchor_right = 0.5
|
||||
offset_left = -48.0
|
||||
offset_right = 48.0
|
||||
offset_bottom = 31.0
|
||||
grow_horizontal = 2
|
||||
text = "Draw Sin(x)"
|
||||
metadata/_edit_layout_mode = 0
|
||||
metadata/_edit_use_custom_anchors = false
|
||||
|
||||
[node name="ClearButton" type="Button" parent="Graph2D"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 5
|
||||
anchor_left = 0.5
|
||||
anchor_right = 0.5
|
||||
offset_left = 58.0
|
||||
offset_right = 107.0
|
||||
offset_bottom = 31.0
|
||||
grow_horizontal = 2
|
||||
text = "Clear"
|
||||
metadata/_edit_layout_mode = 0
|
||||
metadata/_edit_use_custom_anchors = false
|
||||
|
||||
[connection signal="pressed" from="Graph2D/DrawButton" to="." method="_on_draw_button_pressed"]
|
||||
[connection signal="pressed" from="Graph2D/ClearButton" to="." method="_on_clear_button_pressed"]
|
||||
16
examples/single_plot.gd
Normal file
16
examples/single_plot.gd
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
extends Control
|
||||
|
||||
func _on_add_plot_pressed() -> void:
|
||||
var my_plot = $Graph2D.add_plot_item(
|
||||
"Plot %d" % [$Graph2D.count()],
|
||||
[Color.RED, Color.GREEN, Color.BLUE][$Graph2D.count() % 3],
|
||||
[1.0, 3.0, 5.0].pick_random()
|
||||
)
|
||||
|
||||
for x in range(0, 11, 1):
|
||||
var y = randf_range(-50, 50)
|
||||
my_plot.add_point(Vector2(x, y))
|
||||
|
||||
|
||||
func _on_remove_all_plots_pressed() -> void:
|
||||
$Graph2D.remove_all()
|
||||
1
examples/single_plot.gd.uid
Normal file
1
examples/single_plot.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://b4bpgh57roql0
|
||||
48
examples/single_plot.tscn
Normal file
48
examples/single_plot.tscn
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
[gd_scene load_steps=3 format=3 uid="uid://br4viqfrbdlpe"]
|
||||
|
||||
[ext_resource type="Script" path="res://examples/single_plot.gd" id="1_qmytq"]
|
||||
[ext_resource type="Script" path="res://addons/graph_2d/graph_2d.gd" id="2_2ul57"]
|
||||
|
||||
[node name="SinglePlot" type="Control"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("1_qmytq")
|
||||
|
||||
[node name="Graph2D" type="Control" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("2_2ul57")
|
||||
x_label = "Time(s)"
|
||||
y_min = -53.0
|
||||
y_max = 50.0
|
||||
y_label = "Y"
|
||||
background_color = Color(0.0941176, 0.227451, 0.4, 1)
|
||||
grid_horizontal_visible = true
|
||||
grid_vertical_visible = true
|
||||
|
||||
[node name="AddPlot" type="Button" parent="."]
|
||||
layout_mode = 0
|
||||
offset_left = 280.0
|
||||
offset_top = 32.0
|
||||
offset_right = 393.0
|
||||
offset_bottom = 63.0
|
||||
text = "Add New Plot"
|
||||
|
||||
[node name="RemoveAllPlots" type="Button" parent="."]
|
||||
layout_mode = 0
|
||||
offset_left = 400.0
|
||||
offset_top = 32.0
|
||||
offset_right = 537.0
|
||||
offset_bottom = 63.0
|
||||
text = "Remove All Plots"
|
||||
|
||||
[connection signal="pressed" from="AddPlot" to="." method="_on_add_plot_pressed"]
|
||||
[connection signal="pressed" from="RemoveAllPlots" to="." method="_on_remove_all_plots_pressed"]
|
||||
0
imgs/.gdignore
Normal file
0
imgs/.gdignore
Normal file
BIN
imgs/example03.gif
Normal file
BIN
imgs/example03.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 379 KiB |
BIN
imgs/multiplot.png
Normal file
BIN
imgs/multiplot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 72 KiB |
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue