def bezier_curve(control_points, number_of_curve_points): return [ bezier_point(control_points, t) for t in ( i / (number_of_curve_points - 1) for i in range(number_of_curve_points) ) ] def bezier_point(control_points, t): if len(control_points) == 1: result, = control_points return result control_linestring = zip(control_points[:-1], control_points[1:]) return bezier_point([(1 - t) * p1 + t * p2 for p1, p2 in control_linestring], t)
The elements of
control_points represent the control points of the Bézier curve. They must be of the same type of mutually compatible types fulfilling the following rules:
- points shall all be of the same dimension.
- Multiplying a point by a scalar shall result in a point of the same dimension and with a value according to vector-scalar multiplication (i.e., multiply each of the point's Cartesian coordinates with the scalar)
- Adding two points shall result in a point of the same dimension and with a value according to vector addition (i.e., component-wise addition of the points' Cartesian coordinates)
Some examples that work as
(n, 2), where
nis the number of control points
tuples instead of
lists work, too. Probably any sequencey container will work.)
Why pure Python?
Because I want this to be usable in a QGIS plugin, but SciPy, NumPy, etc. (usually) aren't available to QGIS plugins. As I'm unsure which Python libraries are available in QGIS (and the answer to that seems to be platform-dependent), I'd like to avoid external libraries (those that would have to be installed with
pip or one of its alternatives) completely.
Using standard library functions should be fine, so if any part of the implementation could benefit from them, please point that out.
What I'd like to know in this review
- Could / should the readability and comprehensibility of this implementation be improved?
- Did I go foul of any performance (computation speed, memory usage, etc.) no gos? (It doesn't need to be super-fast, but it shouldn't be unnecessarily slow if I can avoid it.)
- Performance for low degrees (e.g., degree 2, i.e. cubic Bézier with three control points per curve) will probably be more relevant than performance at high degrees (many control points per curve)
- Performance for large outputs (large
number_of_curve_points) might be relevant
- About the destructuring assignment
result, = control_pointsto unpack the single point while at the same time making sure it really is exactly one point
- Is this idiomatic in Python (i.e. "pythonic")?
- Is this readable and comprehensible enough or too obscure?
- Is there any good alternative that's an expression, i.e. that could be used directly in the
returnstatement without going through an assignment? (
control_pointsis an expression but doesn't fail when there's more than one element in