I tried to recreate @Andrew's idea as a plot handler. However for some reason I get a different result. Maybe someone can tell me what is wrong (it is late...). Anyway, here is the plot handler:
\makeatletter
\def\pgfplothandlermycurveto{%
\def\pgf@plotstreamstart{%
\global\let\pgf@plotstreampoint=\pgf@plot@mycurveto@handler@initial%
\global\let\pgf@plotstreamspecial=\pgfutil@gobble%
\global\let\pgf@plotstreamend=\pgf@plot@mycurveto@handler@finish%
\global\pgf@plot@startedfalse%
}%
}
\def\pgf@plot@mycurveto@handler@initial#1{%
\pgf@process{#1}%
\pgf@xa=\pgf@x%
\pgf@ya=\pgf@y%
\pgf@plot@first@action{\pgfqpoint{\pgf@xa}{\pgf@ya}}%
\xdef\pgf@plot@mycurveto@first{\noexpand\pgfqpoint{\the\pgf@xa}{\the\pgf@ya}}%
\global\let\pgf@plot@mycurveto@first@support=\pgf@plot@mycurveto@first%
\global\let\pgf@plotstreampoint=\pgf@plot@mycurveto@handler@second%
}
\def\pgf@plot@mycurveto@handler@second#1{%
\pgf@process{#1}%
\xdef\pgf@plot@mycurveto@second{\noexpand\pgfqpoint{\the\pgf@x}{\the\pgf@y}}%
\global\let\pgf@plotstreampoint=\pgf@plot@mycurveto@handler@third%
\global\pgf@plot@startedtrue%
}
\def\pgf@plot@mycurveto@handler@third#1{%
\pgf@process{#1}%
\xdef\pgf@plot@mycurveto@current{\noexpand\pgfqpoint{\the\pgf@x}{\the\pgf@y}}%
% compute midpoint:
\pgf@xa=\pgf@x%
\pgf@ya=\pgf@y%
\pgf@process{\pgf@plot@mycurveto@first}
\advance\pgf@xa by\pgf@x%
\advance\pgf@ya by\pgf@y%
\pgf@xa=0.5\pgf@xa%
\pgf@ya=0.5\pgf@ya%
\pgf@xb=\pgf@xa%
\pgf@yb=\pgf@ya%
% vector from second to midpoint:
\pgf@process{\pgf@plot@mycurveto@second}
\advance\pgf@xa by-\pgf@x%
\advance\pgf@ya by-\pgf@y%
% vector from first to midpoint:
\pgf@process{\pgf@plot@mycurveto@first}
\advance\pgf@xb by-\pgf@x%
\advance\pgf@yb by-\pgf@y%
% normalize
\pgfmathparse{\pgf@plottension / veclen(\pgf@xa,\pgf@ya)}%
\let\pgf@l@dir=\pgfmathresult
\pgfmathsetlength{\pgf@xa}{\pgf@xa * \pgf@l@dir}%
\pgfmathsetlength{\pgf@ya}{\pgf@ya * \pgf@l@dir}%
% compute orientation
\pgfmathparse{\pgf@xa * \pgf@yb - \pgf@ya * \pgf@xb}%
\pgfmathparse{-greater(\pgfmathresult,0) + less(\pgfmathresult,0)}
\pgf@xa=\pgfmathresult\pgf@xa%
\pgf@ya=\pgfmathresult\pgf@ya%
% load second point
\pgf@process{\pgf@plot@mycurveto@second}%
\pgf@xb=\pgf@x%
\pgf@yb=\pgf@y%
\pgf@xc=\pgf@x%
\pgf@yc=\pgf@y%
% compute lengths
\pgf@process{\pgf@plot@mycurveto@first}%
\pgfmathparse{veclen((\pgf@xb-\pgf@x),(\pgf@yb-\pgf@y))}%
\let\pgf@l@first=\pgfmathresult%
\pgf@process{\pgf@plot@mycurveto@current}%
\pgfmathparse{veclen(\pgf@xb-\pgf@x,\pgf@yb-\pgf@y)}%
\let\pgf@l@second=\pgfmathresult%
% first marshal:
\pgfmathaddtolength{\pgf@xb}{-\pgf@l@first * \pgf@ya}%
\pgfmathaddtolength{\pgf@yb}{\pgf@l@first * \pgf@xa}%
\pgfmathaddtolength{\pgf@xc}{\pgf@l@second * \pgf@ya}%
\pgfmathaddtolength{\pgf@yc}{-\pgf@l@second * \pgf@xa}%
\edef\pgf@marshal{\noexpand\pgfpathcurveto{\noexpand\pgf@plot@mycurveto@first@support}%
{\noexpand\pgfqpoint{\the\pgf@xb}{\the\pgf@yb}}{\noexpand\pgf@plot@mycurveto@second}}%
{\pgf@marshal}%
% Prepare next:
\global\let\pgf@plot@mycurveto@first=\pgf@plot@mycurveto@second%
\global\let\pgf@plot@mycurveto@second=\pgf@plot@mycurveto@current%
\xdef\pgf@plot@mycurveto@first@support{\noexpand\pgfqpoint{\the\pgf@xc}{\the\pgf@yc}}%
}
\def\pgf@plot@mycurveto@handler@finish{%
\ifpgf@plot@started%
\pgfpathcurveto{\pgf@plot@mycurveto@first@support}{\pgf@plot@mycurveto@second}{\pgf@plot@mycurveto@second}%
\fi%
}
\tikzoption{mysmooth}[]{\let\tikz@plot@handler=\pgfplothandlermycurveto}
\makeatother
In order to get a single path for the plot, the calculations have to be done by hand without using any additional paths. If you include the above code somewhere after \usepackage{tikz}, you can use the mysmooth option to start the plot handler. It respects the tension and produces a single path.
For example:
\tikz {
\draw plot[mysmooth,mark=x] coordinates {(0,1.3)(0.7,1.8) (3.3,0.5)(4,1.6)(0.4,0.4)};
\draw[red,densely dotted] plot[mysmooth,tension=1.5] coordinates {(0,1.3)(0.7,1.8) (3.3,0.5)(4,1.6)(0.4,0.4)};
\draw[gray,dashed] plot[smooth,tension=0.5] coordinates {(0,1.3)(0.7,1.8) (3.3,0.5)(4,1.6)(0.4,0.4)};
\draw[dotted,gray] (0,0) grid (4,2);
}
produces
.