# Can we imitate the behavior of \pscurve in TikZ?

Here it is with the pictures.

PSTricks has the very neat command \pscurve. The manual gives the example

\pscurve[showpoints=true]{<->}(0,1.3)(0.7,1.8) (3.3,0.5)(4,1.6)(0.4,0.4)


which yields

Does somebody knows how to get the same behavior in TikZ?

I have tried using all sorts of techniques including

\tikz \draw plot[smooth] coordinates {(0,1.3) (0.7,1.8) (3.3,0.5) (4,1.6) (0.4,0.4)};


which gives me

(I did try to play with the tension!). The resulting curve always looks like a succession of straight segments with rounded corners.

Thank you for the points!

-
Could you please add a picture now that you are over 10rep. –  Caramdir Dec 22 '10 at 8:56
and what is the problem to use PSTricks? –  Herbert Dec 22 '10 at 11:02
PSTricks is fine and there is no special problem about it, but TikZ makes some things easier. I am just trying to have the best of both worlds. –  Jacques Cremer Dec 22 '10 at 12:39

As far as I'm aware, this can't be done "out of the box". However, with a little programming then there's no reason why it can't. Exactly what programming is needed depends a little on the exact specifications of the problem: is it to exactly replicate pstricks' behaviour, or to simply do something similar? If the former, then you will need to dig into the pstricks code and extract the formula of how it decides where to draw the lines. If the latter, then a little experimenting can produce a reasonable result.

I don't guarantee that the following will produce a nice smooth curve through any arbitrary family of points, but it is a reasonable method. The drawing command is done by bezier curves between the specified points. The difficulty is working out decent control points. For this, one needs to look ahead to the next point and work out a suitable pair of control points. What I chose to do was to imagine a line joining the prior and next points, take it's midpoint, then join that to the current point. Taking the orthogonal line to this through the current point gave me the direction of my control points. The length is then proportional to the distance between the current point and the prior/next point (as appropriate).

For the control points specified, this gives me:

\documentclass{minimal}

\usepackage{tikz}
\usetikzlibrary{calc}

\begin{document}

\begin{tikzpicture}
\let\pcoord\relax
\let\tcoord\relax
\foreach [count=\num] \coord in {
(0,1.3),
(0.7,1.8),
(3.3,0.5),
(4,1.6),
(0.4,0.4)
} {
\ifx\pcoord\relax
\global\let\pcoord\coord
\path \pcoord coordinate (c1);
\else
\ifx\tcoord\relax
\global\let\tcoord\coord
\else
\path \pcoord coordinate (p);
\path \tcoord coordinate (t);
\path \coord coordinate (n);
\path ($(p)!.75!(n)$) coordinate (m);
\path ($(t)!1cm!90:(m)$) coordinate (r);
\path ($(t)-(p)$);
\pgfgetlastxy{\xx}{\yy}
\pgfmathsetmacro{\len}{.5*veclen(\xx,\yy)}
\path ($(t)!(p)!(r)$) coordinate (rp);
\path ($(t)!\len pt!(rp)$) coordinate (c2);
\draw (p) .. controls (c1) and (c2) .. (t);
\path ($(t)-(n)$);
\pgfgetlastxy{\xx}{\yy}
\pgfmathsetmacro{\len}{.5*veclen(\xx,\yy)}
\path ($(t)!(n)!(r)$) coordinate (rn);
\path ($(t)!\len pt!(rn)$) coordinate (c1);
\global\let\pcoord\tcoord
\global\let\tcoord\coord
\fi
\fi
}
\draw (t) .. controls (c1) and (n) .. (n);

\end{tikzpicture}

\end{document}


(If done "in anger", one should probably worry about bounding boxes. I didn't.)

1. It would probably be better to use the tangent of the circumcircle to compute the direction of the control points (that would make the first one look better).
2. It would also be better to make the whole thing one path rather than a concatenation of several paths.
-
compare your solution with: \pscurve(0,2)(0.7,2.8)(3.3,2.5)(4,2.6)(0.4,1.4) PSTricks didn't use directly bezier curves –  Herbert Dec 22 '10 at 13:05
why not use splines? they don't need any control points.. –  Yossi Farjoun Dec 22 '10 at 14:04
whom do you ask? –  Herbert Dec 22 '10 at 14:40
@Herbert,@Yossi: The problem wasn't properly specified as to whether to exactly replicate the PSTricks behaviour or to draw a nice curvy line. As I didn't feel like digging in the PSTricks code to find out how you do it, I chose to interpret it as the latter. Indeed, I don't see the point in exact replication as surely the purpose of this construction is not to draw an exact curve but something that "looks nice". The choice of beziers was determined by how TikZ draws curved lines, nothing else. –  Loop Space Dec 22 '10 at 14:57
Andrew, you interpreted the question exactly as I meant it. It was not a question of replicating the behaviour of PSTricks, but rather to have a quick and dirty way to draw nice curvy curves for figures such as demand and supply diagrams. –  Jacques Cremer Dec 22 '10 at 18:10

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}
\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}
% vector from first to midpoint:
\pgf@process{\pgf@plot@mycurveto@first}
% 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%
\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:
\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

.

-
Thank you this looks great. I will look at it and try to figure out what Andrew and you did at the beginning of next week ... although it may be beyond my competence level. –  Jacques Cremer Dec 23 '10 at 8:29

I just tried the following solutions which compiles and gives the "right" output: put a PSTricks picture inside a TikZ node (it is Christmas time and miracles abound).

\documentclass[10pt]{article}

\usepackage{pstricks,tikz}
\begin{document}

\begin{tikzpicture}
\node {%
\begin{pspicture}(4,2)
\pscurve[showpoints=true]{<->}(0,1.3)(0.7,1.8) (3.3,0.5)(4,1.6)(0.4,0.4)
\end{pspicture}%
};
\draw [red] (0,0) -- (1,1);
\end{tikzpicture}
\end{document}


The red line is here just to make absolutely sure that we can mix PSTricks objects and Tikz objects.

I assume that if we try fancier things we might get into conflicts, but at least for simple things this seems to be a solution.

And thanks Herbert - it is your question which gave me the idea to try this hack.

-
there cannot be any conflict on TeX side ... –  Herbert Dec 22 '10 at 13:27
Jacques, this looks like it is just a comment on Herbert's answer. If you just want to say that Herbert's answer works for you then you should leave a comment on his answer; otherwise it looks a bit confusing. –  Loop Space Dec 22 '10 at 14:52
No, I wrote my answer and Herbert's answer appeared afterwards. I assumed that by mistake he did some copy and paste of my code. –  Jacques Cremer Dec 22 '10 at 15:40
@Jacques: no, I modified your code to show, that you do not need a pspciture environment –  Herbert Dec 22 '10 at 15:53
@Jacques, @Herbert: My apologies, the timestamps are not sufficiently fine that I could see which came first. Herbert, it would be nice if you could add that to your answer, otherwise it's not clear why you've posted it and others may make the same mistake that I did. –  Loop Space Dec 22 '10 at 16:52
\documentclass[10pt]{article}

\usepackage{pstricks,tikz}
\begin{document}

\begin{tikzpicture}(4,2)
\node at (0,0){%
\pscurve[showpoints]{<->}(0,1.3)(0.7,1.8) (3.3,0.5)(4,1.6)(0.4,0.4)
};
\draw [red] (0,0) -- (1,1);
\end{tikzpicture}
\end{document}

-
If this is a reasonable answer to the question then I don't like the question! I interpreted it as meaning "Only using TikZ", not "Mixing PSTricks and TikZ". For example, one can't refer to parts of the PSTricks curve using TikZ commands, and can't apply TikZ stuff (such as decorations) to the PSTricks curve. –  Loop Space Dec 22 '10 at 14:59
@Andrew: I also can use tikz drawings inside PSTricks –  Herbert Dec 22 '10 at 15:51
I'm sure that you can and that's great. But unless the two have some compatibility that I'm unaware of, when one is included in the other then the outer one cannot refer to the detail of the inner one but must just regard it as a black box. That's not true mixing (if mixing were the intent of the question, which I'm unsure of). –  Loop Space Dec 22 '10 at 16:54