+1 vote

I know it's possible to use `Curve2D` to describe a 2D curve.

But how would one go about drawing that curve?

in Engine

I've been researching this for about a week. ;)

You need to build add points to a `Curve2D` object, then adjust the control points of the curve, and then finally draw the curve. So here's some simple, almost pseudo code:

``````var array_of_line_points # This already has the vectors which describe our line
for point in array_of_line_points:
# The "get_perpendicular_vector()" function returns a vector that's a copy of the point, yet has been slid along a line parallel to two neighboring points. The "distance" variable is how far the control point should be from the originating point.
var control_point1 = get_perpendicular_vector(point, distance)
var control_point2 = get_perpendicular_vector(point, -distance)
# ...In the draw function
func _draw():
draw_polyline(curve.get_baked_points(), red, 2.0)
``````

I got inspiration for creating the curves this way from this webpage on spline interpolation.

I hope this helps!

Thanks, @Ertain! If you post this as an answer, I will select it.

I'd create an answer, but the problem lies in defining the function `get_perpendicular_vector()`. I don't know how to get a perpendicular vector.

May as well promote my comment to an answer.

You need to add points to a `Curve2D` object, then adjust the control points of the curve, and then finally draw the curve. So here's some simple, almost pseudo code:

``````var array_of_line_points # This already has the vectors which describe our line
for point in array_of_line_points:
# The "get_perpendicular_vector()" function returns a vector that's a copy of the point, yet has been slid along a line parallel to two neighboring points. The "distance" variable is how far the control point should be from the originating point.
var control_point1 = get_perpendicular_vector(point, distance)
var control_point2 = get_perpendicular_vector(point, -distance)
# ...In the draw function
func _draw():
draw_polyline(curve.get_baked_points(), red, 2.0)
``````

I got the inspiration for this from Rob Spencer's page on Spline Interpolation. I'd like to make this more thorough (i.e. defining the `get_perpendicular_vector()` function). But this is the best I can currently do.

by (2,678 points)
selected

It's in the documentation:

http://docs.godotengine.org/en/3.0/tutorials/2d/custom_drawing_in_2d.html

``````func draw_circle_arc(center, radius, angle_from, angle_to, color):
var nb_points = 32
var points_arc = PoolVector2Array()

for i in range(nb_points+1):
var angle_point = deg2rad(angle_from + i * (angle_to-angle_from) / nb_points - 90)
points_arc.push_back(center + Vector2(cos(angle_point), sin(angle_point)) * radius)

for index_point in range(nb_points):
draw_line(points_arc[index_point], points_arc[index_point + 1], color)
``````
by (21 points)

Hey I just wrote this node called `SmoothPath` based on Ertain's answer.
First you create straight lines in the editor then press the `Smooth` button in the Inspector.

``````tool
class_name SmoothPath
extends Path2D

export(float) var spline_length = 100
export(bool) var _smooth setget smooth
export(bool) var _straighten setget straighten

func straighten(value):
if not value: return
for i in curve.get_point_count():
curve.set_point_in(i, Vector2())
curve.set_point_out(i, Vector2())

func smooth(value):
if not value: return

var point_count = curve.get_point_count()
for i in point_count:
var spline = _get_spline(i)
curve.set_point_in(i, -spline)
curve.set_point_out(i, spline)

func _get_spline(i):
var last_point = _get_point(i - 1)
var next_point = _get_point(i + 1)
var spline = last_point.direction_to(next_point) * spline_length
return spline

func _get_point(i):
var point_count = curve.get_point_count()
i = wrapi(i, 0, point_count - 1)
return curve.get_point_position(i)

func _draw():
var points = curve.get_baked_points()
if points:
draw_polyline(points, Color.black, 8, true)
``````
by (4,169 points)

Amazing thanks, works brilliantly.

Great job mate! I am also trying to visualize pointin and pointout handles by adding:

``````draw_circle(get_point_in(), 3, Color.white)
draw_circle(get_point_out(), 3, Color.white)
``````

to the _draw() function. But it returns an error. Obviously, it needs to be done differently. I would highly appreciate if you have any idea how it can be done. I even tried this one below still without success:

``````for pc in curve.get_point_count():
var spline = _get_spline(pc)
var point_in = curve.get_point_in(-pc, _get_spline(pc))
var point_out = curve.get_point_out(pc, _get_spline(pc))
draw_circle(point_in, 3, Color.white)
draw_circle(point_out, 3, Color.white)
``````

It returns: Too many arguments for getpointin/out()

Never would have thought of that, Jeans. This may help for some game I'm designing.

Hi thank you for this good piece of script, I wanted to ask you I know to reproduce your smooth curve elsewhere but I do not understand your logic how you do that I want to understand please help me I know what is the control point and how to retrieve the point but how do you do to manipulate so well the control point?

if you don't know, this script has been referenced in a youtube tutorial, how to make water with dynamic waves. i'm not the creator but since they left your credit i just thought i'd drop in to say thanks :)

Late to the game here, but here's a simple function that takes an array of vector2D (e.g. the points of a Line2D) and returns a Curve2D.

``````func array_to_curve(input : Array, dist : float):
#dist determines length of controls, set dist = 0 for no smoothing
var curve = Curve2D.new()

#calculate first point
var start_dir = input[0].direction_to(input[1])
curve.add_point(input[0], - start_dir * dist, start_dir * dist)

#calculate middle points
for i in range(1, input.size() - 1):
var dir = input[i-1].direction_to(input[i+1])
curve.add_point(input[i], -dir * dist, dir * dist)

#calculate last point
var end_dir = input[-1].direction_to(input[-2])
curve.add_point(input[-1], - end_dir * dist, end_dir * dist)

return curve
``````
by (109 points)
``````func _ready():