0 votes

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

But how would one go about drawing that curve?

asked Sep 1, 2018 in Engine by Diet Estus (1,504 points)

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)
  curve.add_point(point, control_point1, control_point2)
# ...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.

3 Answers

+2 votes
Best answer

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)
  curve.add_point(point, control_point1, control_point2)
# ...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.

answered Sep 1, 2018 by Ertain (1,389 points)
selected Sep 2, 2018 by Diet Estus
0 votes

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)
answered Sep 1, 2018 by squished-01 (21 points)
+1 vote

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.

SmoothPath Preview

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)
answered Dec 15, 2019 by Dlean Jeans (3,917 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()

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.