Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

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

pscurve example

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

tikz plot example

(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!

share|improve this question
1  
Could you please add a picture now that you are over 10rep. –  Caramdir Dec 22 '10 at 8:56
1  
and what is the problem to use PSTricks? –  Herbert Dec 22 '10 at 11:02
2  
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
add comment

4 Answers

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:

pscurve emulation

\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.
share|improve this answer
    
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. –  Andrew Stacey Dec 22 '10 at 14:57
1  
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
add comment

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

example.

share|improve this answer
    
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
add comment

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.

share|improve this answer
    
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. –  Andrew Stacey 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. –  Andrew Stacey Dec 22 '10 at 16:52
show 3 more comments
\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}
share|improve this answer
    
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. –  Andrew Stacey 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). –  Andrew Stacey Dec 22 '10 at 16:54
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.