|
@@ -1,11 +1,8 @@
|
|
.. _doc_beziers_and_curves:
|
|
.. _doc_beziers_and_curves:
|
|
|
|
|
|
-Beziers, Curves and Paths
|
|
|
|
|
|
+Beziers, curves and paths
|
|
=========================
|
|
=========================
|
|
|
|
|
|
-Introduction
|
|
|
|
-~~~~~~~~~~~~
|
|
|
|
-
|
|
|
|
Bezier curves are a mathematical approximation to natural shapes. Their goal is to represent a curve with
|
|
Bezier curves are a mathematical approximation to natural shapes. Their goal is to represent a curve with
|
|
as little information as possible, and with a high level of flexibility.
|
|
as little information as possible, and with a high level of flexibility.
|
|
|
|
|
|
@@ -34,7 +31,7 @@ To draw the curve between them, just interpolate the two segments that form betw
|
|
var q0 = p0.linear_interpolate(p1, t)
|
|
var q0 = p0.linear_interpolate(p1, t)
|
|
var q1 = p1.linear_interpolate(p2, t)
|
|
var q1 = p1.linear_interpolate(p2, t)
|
|
|
|
|
|
-This will reduce the points from 3 to 2. Do the same process with *q0* and *q1* to obtain a single point *r*.
|
|
|
|
|
|
+This will reduce the points from 3 to 2. Do the same process with ``q0`` and ``q1`` to obtain a single point ``r``.
|
|
|
|
|
|
.. tabs::
|
|
.. tabs::
|
|
.. code-tab:: gdscript GDScript
|
|
.. code-tab:: gdscript GDScript
|
|
@@ -42,7 +39,7 @@ This will reduce the points from 3 to 2. Do the same process with *q0* and *q1*
|
|
var r = q0.linear_interpolate(q1, t)
|
|
var r = q0.linear_interpolate(q1, t)
|
|
return r
|
|
return r
|
|
|
|
|
|
-Finally, this point fill follow the curve when t goes from 0 to 1. This type of curve is called *Quadratic Bezier*.
|
|
|
|
|
|
+Finally, this point fill follow the curve when ``t`` goes from 0 to 1. This type of curve is called *Quadratic Bezier*.
|
|
|
|
|
|
.. image:: img/bezier_quadratic_points2.gif
|
|
.. image:: img/bezier_quadratic_points2.gif
|
|
|
|
|
|
@@ -55,12 +52,12 @@ Let's add one more point and make it four.
|
|
|
|
|
|
.. image:: img/bezier_cubic_points.png
|
|
.. image:: img/bezier_cubic_points.png
|
|
|
|
|
|
-Then let's modify the function to take four points as an input, *p0, p1, p2* and *p3*:
|
|
|
|
|
|
+Then let's modify the function to take four points as an input, ``p0``, ``p1``, ``p2`` and ``p3``:
|
|
|
|
|
|
.. tabs::
|
|
.. tabs::
|
|
.. code-tab:: gdscript GDScript
|
|
.. code-tab:: gdscript GDScript
|
|
|
|
|
|
- func _cubic_bezier(p0 : Vector2,p1 : Vector2,p2 : Vector2,p3 : Vector2 ,t : float):
|
|
|
|
|
|
+ func _cubic_bezier(p0: Vector2, p1: Vector2, p2: Vector2, p3: Vector2, t: float):
|
|
|
|
|
|
Interpolate then into three points:
|
|
Interpolate then into three points:
|
|
|
|
|
|
@@ -93,17 +90,17 @@ The result will be a smooth curve interpolating between all four points:
|
|
|
|
|
|
*(Image credit: Wikipedia)*
|
|
*(Image credit: Wikipedia)*
|
|
|
|
|
|
-.. note:: For 3D, it's exactly the same, just change Vector2 into Vector3.
|
|
|
|
|
|
+.. note:: For 3D, it's exactly the same, just change ``Vector2`` to ``Vector3``.
|
|
|
|
|
|
Control point form
|
|
Control point form
|
|
------------------
|
|
------------------
|
|
|
|
|
|
-Now, let's take these points and change the way we understand them. Instead of having p0, p1, p2 and p3, we will store them as:
|
|
|
|
|
|
+Now, let's take these points and change the way we understand them. Instead of having ``p0``, ``p1``, ``p2`` and ``p3``, we will store them as:
|
|
|
|
|
|
-* **POINT0** = **P0**: Is the first point, the source
|
|
|
|
-* **CONTROL0** = **P1** - **P0**: Is a relative vector for the first control point
|
|
|
|
-* **CONTROL1** = **P3** - **P2**: Is a relative vector for the second control point
|
|
|
|
-* **POINT1** = **P3**: Is the second point, the destination
|
|
|
|
|
|
+* ``POINT0 = P0``: Is the first point, the source
|
|
|
|
+* ``CONTROL0 = P1 - P0``: Is a relative vector for the first control point
|
|
|
|
+* ``CONTROL1 = P3 - P2``: Is a relative vector for the second control point
|
|
|
|
+* ``POINT1 = P3``: Is the second point, the destination
|
|
|
|
|
|
This way, we have two points and two control points (which are relative vectors to the respective points). If visualized, they will look a lot more familiar:
|
|
This way, we have two points and two control points (which are relative vectors to the respective points). If visualized, they will look a lot more familiar:
|
|
|
|
|
|
@@ -125,7 +122,7 @@ Using them, however, may not be completely obvious, so following is a descriptio
|
|
Evaluating
|
|
Evaluating
|
|
----------
|
|
----------
|
|
|
|
|
|
-Just evaluating them may be an option, but in most cases it's not very useful. The big drawback with Bezier curves is that if you traverse them at constant speed, from *t=0* to *t=1*, the actual interpolation will *not* move at constant speed. The speed is also an interpolation between the distances between points p0, p1, p2 and p3 and there is not a mathematically simple way to traverse the curve at constant speed.
|
|
|
|
|
|
+Just evaluating them may be an option, but in most cases it's not very useful. The big drawback with Bezier curves is that if you traverse them at constant speed, from ``t = 0`` to ``t = 1``, the actual interpolation will *not* move at constant speed. The speed is also an interpolation between the distances between points ``p0``, ``p1``, ``p2`` and ``p3`` and there is not a mathematically simple way to traverse the curve at constant speed.
|
|
|
|
|
|
Let's do a simple example with the following pseudocode:
|
|
Let's do a simple example with the following pseudocode:
|
|
|
|
|
|
@@ -141,23 +138,23 @@ Let's do a simple example with the following pseudocode:
|
|
|
|
|
|
.. image:: img/bezier_interpolation_speed.gif
|
|
.. image:: img/bezier_interpolation_speed.gif
|
|
|
|
|
|
-As you can see, the speed (in pixels per second) of the circle varies, even though *t* is increased at constant speed. This makes beziers difficult to use for anything practical out of the box.
|
|
|
|
|
|
+As you can see, the speed (in pixels per second) of the circle varies, even though ``t`` is increased at constant speed. This makes beziers difficult to use for anything practical out of the box.
|
|
|
|
|
|
Drawing
|
|
Drawing
|
|
-------
|
|
-------
|
|
|
|
|
|
Drawing beziers (or objects based on the curve) is a very common use case, but it's also not easy. For pretty much any case, Bezier curves need to be converted to some sort of segments. This is normally difficult, however, without creating a very high amount of them.
|
|
Drawing beziers (or objects based on the curve) is a very common use case, but it's also not easy. For pretty much any case, Bezier curves need to be converted to some sort of segments. This is normally difficult, however, without creating a very high amount of them.
|
|
|
|
|
|
-The reason is that some sections of a curve (specifically, corners) may requiere considerable amounts of points, while other sections may not:
|
|
|
|
|
|
+The reason is that some sections of a curve (specifically, corners) may require considerable amounts of points, while other sections may not:
|
|
|
|
|
|
.. image:: img/bezier_point_amount.png
|
|
.. image:: img/bezier_point_amount.png
|
|
|
|
|
|
-Additionally, if both control points were 0,0 (remember they are relative vectors), the Bezier curve would just be a straight line (so drawing a high amount of points would be wasteful).
|
|
|
|
|
|
+Additionally, if both control points were ``0, 0`` (remember they are relative vectors), the Bezier curve would just be a straight line (so drawing a high amount of points would be wasteful).
|
|
|
|
|
|
Before drawing Bezier curves, *tesselation* is required. This is often done with a recursive or divide and conquer function that splits the curve until the curvature amount becomes less than a certain threshold.
|
|
Before drawing Bezier curves, *tesselation* is required. This is often done with a recursive or divide and conquer function that splits the curve until the curvature amount becomes less than a certain threshold.
|
|
|
|
|
|
The *Curve* classes provide this via the
|
|
The *Curve* classes provide this via the
|
|
-:ref:`Curve2D.tessellate()<class_Curve2D_method_tessellate>` function (which receives optional *stages* of recursion and angle *tolerance* arguments). This way, drawing something based on a curve is easier.
|
|
|
|
|
|
+:ref:`Curve2D.tessellate() <class_Curve2D_method_tessellate>` function (which receives optional ``stages`` of recursion and angle ``tolerance`` arguments). This way, drawing something based on a curve is easier.
|
|
|
|
|
|
Traversal
|
|
Traversal
|
|
---------
|
|
---------
|