# How to draw a curve in 2D?

Attention Topic was automatically imported from the old Question2Answer platform.

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

But how would one go about drawing that curve?

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!

Ertain | 2018-09-01 18:01

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

Diet Estus | 2018-09-01 18:07

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.

Ertain | 2018-09-01 18:29

It’s in the documentation:

``````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)
``````

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.

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)
``````

Amazing thanks, works brilliantly.

harryfrook | 2020-01-08 00:12

Great job mate! I am also trying to visualize point_in and point_out 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 get_point_in/out()

Suleymanov | 2020-07-18 07:32

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

Ertain | 2021-12-01 04:07

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?

Alaiz | 2022-01-12 12:29

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

quewon | 2022-04-20 09:45

2 Likes

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
``````
``````func _ready():