[1] - e.g. https://en.wikipedia.org/wiki/B%C3%A9zier_curve#Rational_B%C...
Down at the lowest level the engine can only really do straight lines. You may ask for a circle or a bezier, but the engine only approximates this with enough fidelity that you wouldn't notice the flaws. Beziers end up being approximated as a bunch of tiny line segments. Circles and ellipses can be approximated pretty easily as a handful of beziers.
Beziers are particularly useful because they have some handy properties. You don't need to do any trig to calculate them (like you do for arcs). They're extremely simple to compute, and any bezier curve can be split into two smaller bezier curves. That last fact makes it easier to have varying fidelity along the curve.
Source: was on the original SVG Working Group team @ Adobe.
def bezier(t, *handles):
"""Computes a single point of a bezier of any order where 0 <= t <= 1."""
if len(handles) == 1:
return handles[0]
else:
pairs = zip(handles[:-1], handles[1:])
return bezier(t, *[_bezier_interpolate(t, a, b) for a, b in pairs])
def _bezier_interpolate(t, a, b):
return [a * (1-t) + b * t for a, b in zip(a, b)]
assert bezier(0.25, [1, 1, 1], [2, 2, 2], [3, 4, 5]) == [1.5, 1.5625, 1.625]
You can pass it any order of bezier (cubic, quadratic, etc.) for any number of dimensions, and how for along the interpolation you are. It will give you a single point. Connect a bunch together to get your curve. You can actually stop when `len(handles) == 2` and you get line segments instead of points, but it's slightly less accurate.You can see the math for computing a point (or segment) is trivial (addition and multiplication) and it can be easily parallelized. It can even be a one-liner, but that isn't very readable.
Here's a good website for visualizing the math: https://www.summbit.com/blog/bezier-curve-guide/
[0] https://opensource.adobe.com/dc-acrobat-sdk-docs/pdfstandard..., sec. 8.5.2, "Path Construction Operators"