init
This commit is contained in:
141
addons/curved_lines_2d/drawable_path_2d.gd
Normal file
141
addons/curved_lines_2d/drawable_path_2d.gd
Normal file
@@ -0,0 +1,141 @@
|
||||
@tool
|
||||
extends Path2D
|
||||
## A custom node that extends Path2D so it can be drawn as a Line2D
|
||||
## Original adapted code: https://www.hedberggames.com/blog/rendering-curves-in-godot
|
||||
## @deprecated: Use [ScalableVectorShape2D] instead.
|
||||
class_name DrawablePath2D
|
||||
|
||||
## Emitted when a new set of points was calculated for a connected Line2D, Polygon2D, or CollisionPolygon2D
|
||||
signal path_changed(new_points : PackedVector2Array)
|
||||
|
||||
## This signal is used internally in editor-mode to tell the DrawablePath2D tool that
|
||||
## the instance of assigned Line2D, Polygon2D, or CollisionPolygon2D has changed
|
||||
signal assigned_node_changed()
|
||||
|
||||
|
||||
## The Polygon2D controlled by this Path2D
|
||||
@export var polygon: Polygon2D:
|
||||
set(_poly):
|
||||
polygon = _poly
|
||||
assigned_node_changed.emit()
|
||||
|
||||
## The Line2D controlled by this Path2D
|
||||
@export var line: Line2D:
|
||||
set(_line):
|
||||
line = _line
|
||||
assigned_node_changed.emit()
|
||||
|
||||
## The CollisionPolygon2D controlled by this Path2D
|
||||
@export var collision_polygon: CollisionPolygon2D:
|
||||
set(_poly):
|
||||
collision_polygon = _poly
|
||||
assigned_node_changed.emit()
|
||||
|
||||
## Controls whether the path is treated as static (only update in editor) or dynamic (can be updated during runtime)
|
||||
## If you set this to true, be alert for potential performance issues
|
||||
@export var update_curve_at_runtime: bool = false
|
||||
|
||||
## Controls the paramaters used to divide up the line in segments.
|
||||
## These settings are prefilled with the default values.
|
||||
@export_group("Tesselation settings")
|
||||
## Controls how many subdivisions a curve segment may face before it is considered approximate enough.
|
||||
## Each subdivision splits the segment in half, so the default 5 stages may mean up to 32 subdivisions
|
||||
## per curve segment. Increase with care!
|
||||
@export_range(1, 10) var max_stages : int = 5:
|
||||
set(_max_stages):
|
||||
max_stages = _max_stages
|
||||
assigned_node_changed.emit()
|
||||
## Controls how many degrees the midpoint of a segment may deviate from the real curve, before the
|
||||
## segment has to be subdivided.
|
||||
@export_range(0.0, 180.0) var tolerance_degrees := 4.0:
|
||||
set(_tolerance_degrees):
|
||||
tolerance_degrees = _tolerance_degrees
|
||||
assigned_node_changed.emit()
|
||||
|
||||
var lock_assigned_shapes := true
|
||||
|
||||
# Wire up signals at runtime
|
||||
func _ready():
|
||||
if update_curve_at_runtime:
|
||||
if not curve.changed.is_connected(curve_changed):
|
||||
curve.changed.connect(curve_changed)
|
||||
|
||||
|
||||
# Wire up signals on enter tree for the editor
|
||||
func _enter_tree():
|
||||
if Engine.is_editor_hint():
|
||||
if not curve.changed.is_connected(curve_changed):
|
||||
curve.changed.connect(curve_changed)
|
||||
if not assigned_node_changed.is_connected(_on_assigned_node_changed):
|
||||
assigned_node_changed.connect(_on_assigned_node_changed)
|
||||
# handles update when reparenting
|
||||
if update_curve_at_runtime:
|
||||
if not curve.changed.is_connected(curve_changed):
|
||||
curve.changed.connect(curve_changed)
|
||||
|
||||
|
||||
# Clean up signals (ie. when closing scene) to prevent error messages in the editor
|
||||
func _exit_tree():
|
||||
if curve.changed.is_connected(curve_changed):
|
||||
curve.changed.disconnect(curve_changed)
|
||||
|
||||
|
||||
func _on_assigned_node_changed():
|
||||
if is_instance_valid(line):
|
||||
if lock_assigned_shapes:
|
||||
line.set_meta("_edit_lock_", true)
|
||||
curve_changed()
|
||||
if is_instance_valid(polygon):
|
||||
if lock_assigned_shapes:
|
||||
polygon.set_meta("_edit_lock_", true)
|
||||
curve_changed()
|
||||
if is_instance_valid(collision_polygon):
|
||||
if lock_assigned_shapes:
|
||||
collision_polygon.set_meta("_edit_lock_", true)
|
||||
curve_changed()
|
||||
|
||||
|
||||
# Redraw the line based on the new curve, using its tesselate method
|
||||
func curve_changed():
|
||||
if (not is_instance_valid(line) and not is_instance_valid(polygon)
|
||||
and not is_instance_valid(collision_polygon)
|
||||
and not path_changed.has_connections()):
|
||||
# guard against needlessly invoking expensive tesselate operation
|
||||
return
|
||||
|
||||
var new_points := curve.tessellate(max_stages, tolerance_degrees)
|
||||
# Fixes cases start- and end-node are so close to each other that
|
||||
# polygons won't fill and closed lines won't cap nicely
|
||||
if new_points[0].distance_to(new_points[new_points.size()-1]) < 0.001:
|
||||
new_points.remove_at(new_points.size() - 1)
|
||||
if is_instance_valid(line):
|
||||
line.points = new_points
|
||||
if is_instance_valid(polygon):
|
||||
polygon.polygon = new_points
|
||||
if is_instance_valid(collision_polygon):
|
||||
collision_polygon.polygon = new_points
|
||||
path_changed.emit(new_points)
|
||||
|
||||
|
||||
func get_bounding_rect() -> Rect2:
|
||||
var points := curve.tessellate(max_stages, tolerance_degrees)
|
||||
if points.size() < 1:
|
||||
# Cannot calculate a center for 0 points
|
||||
return Rect2(Vector2.ZERO, Vector2.ZERO)
|
||||
var minx := INF
|
||||
var miny := INF
|
||||
var maxx := -INF
|
||||
var maxy := -INF
|
||||
for p : Vector2 in points:
|
||||
minx = p.x if p.x < minx else minx
|
||||
miny = p.y if p.y < miny else miny
|
||||
maxx = p.x if p.x > maxx else maxx
|
||||
maxy = p.y if p.y > maxy else maxy
|
||||
return Rect2(minx, miny, maxx - minx, maxy - miny)
|
||||
|
||||
|
||||
func set_position_to_center() -> void:
|
||||
var c = get_bounding_rect().get_center()
|
||||
position += c
|
||||
for i in range(curve.get_point_count()):
|
||||
curve.set_point_position(i, curve.get_point_position(i) - c)
|
||||
Reference in New Issue
Block a user