Rollercoaster-Derivatives/addons/easy_charts/control_charts/plotters/scatter_plotter.gd

88 lines
3.5 KiB
GDScript

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