This question led to a new package:
hobby
I found Metapost best for drawing complex smooth curves (i.e., Bezier, splines), since you do not have to directly specify Bezier control points. Unfortunately, I need to use TikZ for my current project exclusively; drawing (closed) curves in TikZ is tedious and very time-consuming task. So I combined the "power" of Metapost with TikZ into the following workflow:
- Draw closed curve in Metapost.
- Open the generated Postscript file in a text editor and manually extract control points.
- Paste extracted points into a TikZ figure and modify the PGF/TikZ expressions to draw curve.
Pasted below is a reproducible example to illustrate the described approach.
%% Construct curve in Metapost
beginfig(1)
draw (0,0) .. (60,40) .. (40,90) .. (10,70) .. (30,50) .. cycle;
endfig;
end
%% Extract control points from postscript file
newpath 0 0 moveto
5.18756 -26.8353 60.36073 -18.40036 60 40 curveto
59.87714 59.889 57.33896 81.64203 40 90 curveto
22.39987 98.48387 4.72404 84.46368 10 70 curveto
13.38637 60.7165 26.35591 59.1351 30 50 curveto
39.19409 26.95198 -4.10555 21.23804 0 0 curveto closepath stroke
%% Create Tikz figure in pdfLaTeX
\documentclass{standalone}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}[scale=0.1]
\draw (0, 0) .. controls (5.18756, -26.8353) and (60.36073, -18.40036)
.. (60, 40) .. controls (59.87714, 59.889) and (57.33896, 81.64203)
.. (40, 90) .. controls (22.39987, 98.48387) and (4.72404, 84.46368)
.. (10, 70) .. controls (13.38637, 60.7165) and (26.35591, 59.1351)
.. (30, 50) .. controls (39.19409, 26.95198) and (-4.10555, 21.23804)
.. (0, 0);
\end{tikzpicture}
\end{document}
This approach works if you have to draw a curve or two, but becomes tedious with more curves. I wonder if there is a simpler way which avoids manual copy-paste repetitions from file to file? Maybe the most elegant solution should be a simple C/C++/… program, but I cant't find the implementation of Hobby's algorithm used by Metapost to calculate Bezier control points. Any ideas would be greatly appreciated.
Addition by Jake:
Here's a comparison of the path resulting from Hobby's algorithm (thick black line) and the \draw plot [smooth]
algorithm (orange line). In my opinion, the result from Hobby's algorithm is clearly superior in this case.
Best Answer
Just for fun, I decided to implement Hobby's algorithm in pure Python (well, not pure, I had to use numpy module to solve a linear system of equations).
Currently, my code works on simple paths, in which all joins are "curved" (i.e: "..") and no directions are specified at the knots. However, tension can be specified at each segment, and even as a "global" value to apply to the whole path. The path can be cyclic or open, and in the later it is also possible to specify initial and final curl.
The module can be called from LaTeX, using python.sty package or even better, using the technique demonstrated by Martin in another answer to this same question.
Adapting Martin's code to this case, the following example shows how to use the python script:
Note that options passed to mppath are en general tikz options, but two new options are also available:
tension
, which applies the given tension to all the path, andcurl
which applies the given curl to both ends of an open path.Running the above example through
pdflatex -shell-escape
produces the following output:The python code of this module is below. The details of the algorithm were obtained from the "METAFONT: The program" book. Currently the class design of the python code is prepared to deal with more complex kind of paths, but I did not have time to implement the part which breaks the path into "idependendty solvable" subpaths (this would be at knots which do not have smooth curvature, or at which the path changes from curved to straight). I tried to document the code as much as I can, so that anyone could improve it.
Update
The code supports now tension at each segment, or as a global option for the path. Also changed the way of calling it from latex, using Martin's technique.