4

I need to perform computations on the points that belong to a tikz-pgf path. More specifically, I want to know how many points have a y coordinate exceeding a given value, and I want to calculate numerically the surface (integral) of the area below the path.

Alternatively (or additionally): is there a way to evaluate the length of a path and the enclosed surface?

  • When defining a decoration, there is a macro that stores the length of the path. So there is some code in TikZ that can calculate the length of a path. However, I do not know how to access it outside of a decoration definition. – Caramdir Jun 06 '11 at 20:34
  • What do you think of as being a tikz-pgf path? Can you give a pseudo-code of what you would like to be able to do? – Andrew Stacey Jun 06 '11 at 20:41
  • @Caramdir: interesting... I also noted that I can save a path with the intersection name path option: I get a macro called \tikz@intersect@path@name@<name>, but then using this information is not trivial at all... – Marco Lombardi Jun 06 '11 at 20:42
  • @Andrew: In a sense this is part of the question, i.e., can I save a path somehow in tikz? With the intersections package this is possible, is there any other (better?) way of doing it? In general one might need to stroke several time a path w/o using post/pre action (for example because the second time I might want to work with a clipping and stroke only part of a path). – Marco Lombardi Jun 06 '11 at 20:46
  • @Marco: The easiest way to use a path several times is just to store it in a macro (\newcommand\mypath[1][]{\draw[#1,...] ...;}) and then call it several times. – Caramdir Jun 06 '11 at 20:50
  • @Caramdir: yes, but this way the path is recomputed each time, which is not too efficient for complicated paths involving calculations (such as plots). However this is easily solved with \edef, I suppose. – Marco Lombardi Jun 06 '11 at 21:10
  • 1
    @Marco: you can save the internal representation of the path, is that what you mean? \path[save path=\mypath] path-construction; will do that. Then you can reuse it as you like. As part of my calligraphy package, I'm currently writing a set of routines that will manipulate these paths (such as translating them or reversing them). But the best way to save it depends on how you want to use it, which is why I asked for pseudo-code. – Andrew Stacey Jun 06 '11 at 21:17
  • @Andrew: yes, precisely, that is what I meant. I cannot find the save path key documented anywhere in the TikZ manual, or am I missing it? For the use, given a curve C represented as a list of coordinates (x_i,y_i), I want to calculate the number of points such that y_i > threshold and the integral of the curve in the same set (so, in practice, the sum of y_i for all y_i > threshold). – Marco Lombardi Jun 06 '11 at 21:40
  • Can anyone give me a nice formula to calculate the area under a cubic Bézier curve? – Caramdir Jun 06 '11 at 22:10
  • @Andrew: regarding the \path[save path=\mypath] construct, can I then use \mypath with the higher level TikZ interface or am I forced to stick with pgf? In case I can use TikZ, what is the syntax (for example imagine I want to change the color and fill the path). – Marco Lombardi Jun 06 '11 at 22:41
  • 1
    @Marco: No, save path is not documented. I only learnt about it from searching through the TikZ/PGF code for examples of \pgfsyssoftpath@getcurrentpath (which is documented). To use the path with the normal TikZ options, you would need to write a wrapper macro. But that wouldn't be very hard to do. I'll have a go later today if I get a moment. – Andrew Stacey Jun 07 '11 at 06:54
  • @Marco: Now that I've thought a bit more, and looked for easy formulae on the internet, I'm curious as to why you want these numbers for a TikZ path. My instinct is to say that if you need these numbers, then you should build your path first in an external program that can do decent numerical integration and then get it to print the path and numbers in a format easily importable to TeX. Something like the way that TikZ can export plots to gnuplot for the computation but then do the rendering itself. – Andrew Stacey Jun 08 '11 at 10:49
  • @Andrew: well, the answer to your question is a little involved. I am preparing a talk, and I need want to make an interative figure where I show a plot on the right, with a complicated function f(x) that has a few "waves", and then to the right two plots, representing, for each threshold in y, the measure (area) of the set {x | f(x) > y} and the integral of f(x) over the same set. These two quantities have some relevance in the field I work on, star formation in molecular clouds. Anyway, the plots are just for display purpuses, and getting some rounding errors is not a major issue. – Marco Lombardi Jun 08 '11 at 13:14

3 Answers3

4

Here is a proof-of-concept integration macro. The results are given in pt² (divide by 806.56 to get cm²).

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{decorations}

\makeatletter
\def\integrate#1{{
    % Use the facilities in the decoration library to break the path into
    % simple chunks.
    \pgf@decorate@parsesoftpath#1\parsedpath
    \gdef\area{0}
    \xdef\length{\pgf@decorate@totalpathlength}

    \let\pgf@decorate@inputsegmentobject@moveto\relax
    \let\pgf@decorate@inputsegmentobject@lineto\tsx@integrate@lineto
    \let\pgf@decorate@inputsegmentobject@curveto\tsx@integrate@curveto

    \parsedpath
}}

\def\tsx@integrate@lineto#1#2#3{
    #2
    \pgf@xa\pgf@x
    \pgf@ya\pgf@y
    #3
    \pgf@xb\pgf@x
    \pgf@yb\pgf@y
    \pgfmathparse{\area + (\the\pgf@xb-\the\pgf@xa)*0.5*(\the\pgf@ya + \the\pgf@yb)}
    \xdef\area{\pgfmathresult}
}

\def\tsx@integrate@curveto#1#2#3#4#5{
    #2
    \pgf@xa\pgf@x
    \pgf@ya\pgf@y
    #3
    \pgf@xb\pgf@x
    \pgf@yb\pgf@y
    #4
    \pgf@xc\pgf@x
    \pgf@yc\pgf@y
    #5

    % Use Green's theorem to calculate the area: ∫_D dA = -∫_{∂D} y dx.
    % This probably does not work in all cases.
    % Also this has the problem that there are larger numbers in between. 
    % Probably should use Lua for that.
    \pgfmathparse{\area + 
        % integral over the curve
        %(3 Subscript[x, 3] (Subscript[y, 1] + Subscript[y, 2] - 2 Subscript[y, 4]) - 3 Subscript[x, 2] (-2 Subscript[y, 1] + Subscript[y, 3] + Subscript[y, 4]) - Subscript[x, 1] (10 Subscript[y, 1] + 6 Subscript[y, 2] + 3 Subscript[y, 3] + Subscript[y, 4]) + Subscript[x, 4] (Subscript[y, 1] + 3 Subscript[y, 2] + 6 Subscript[y, 3] + 10 Subscript[y, 4]))/20
        +(3*\pgf@xc*(\pgf@ya + \pgf@yb -2*\pgf@y)                                   - 3*\pgf@xb*(-2*\pgf@ya + \pgf@yc + \pgf@y)                                  - \pgf@xa*(10*\pgf@ya + 6*\pgf@yb + 3*\pgf@yc + \pgf@y)                                          + \pgf@x*(\pgf@ya + 3*\pgf@yb + 6*\pgf@yc + 10*\pgf@y))/20
        % integral over the line between the endpoints
        -(\pgf@y*(\pgf@xa - \pgf@x) + 0.5*(\pgf@x-\pgf@xa)*(\pgf@y-\pgf@ya))
        % area between that line and the x-axis
        -(\pgf@x-\pgf@xa)*0.5*(\pgf@ya + \pgf@y)
        }
    \xdef\area{\pgfmathresult}
}

\makeatother
\begin{document}

\begin{tikzpicture}
    \draw[save path=\mypath] plot[domain=0:1,smooth] (\x,{\x*\x});
    \integrate{\mypath}
    \show\area
    \show\length
\end{tikzpicture}
\end{document}

The curvto integration calculation has an issue with orientations (for example the area of a circle comes out as -r²π; I chose the negative sign to make integrals over plots work) and large intermediate values (integrating the above example to 1.1 is already too much).

Caramdir
  • 89,023
  • 26
  • 255
  • 291
  • I wrote a LuaTeX-implementation of the code. It can (currently) be found at http://bazaar.launchpad.net/~tex-sx/tex-sx/development/view/head:/tikz-integration.sty. – Caramdir Jun 08 '11 at 04:29
4

Right, here's something along the lines of an answer. I was implementing some of this stuff as part of the TeX-SX calligraphy package that involved manipulating soft paths and I'd begun spinning off the soft path manipulation into its own package file anyway. Here seems as good a place to record that as any!

The relevant file is spath.dtx and is available from the TeX-SX package project. Also useful is the file spath_test.tex because I haven't written any documentation yet! To produce the style file, run tex spath.dtx (if there were any documentation you'd get that from pdflatex spath.dtx).

It's an object-oriented approach, using Till Tantau's OO implementation that is bundled with PGF (so you might need the most recent version of PGF for it to work). Here's some sample code:

\documentclass{article}
\usepackage{tikz}
\usepackage{spath}

\begin{document}
\begin{tikzpicture}
\path[save path=\tmppath] (0,0) -- (1,1) (2,1) .. controls (3,1) and (4,2) .. (5,2);
\show\tmppath
\pgfoonew \mypath =new spath(\tmppath)
\mypath.show(path)
\mypath.length()
\mypath.initial point()
\mypath.show(initial point)
\mypath.final point()
\mypath.show(final point)
\mypath.show(length)
\mypath.translate path(\trpath,1cm,1cm)
\mypath.show(path)
\trpath.show(path)
\mypath.concatenate(\catpath,\trpath)
\catpath.show(path)
\mypath.weld(,\trpath)
\mypath.show(path)
\mypath.use path with tikz(draw,red,line width=.5cm)
\mypath.use path(stroke)
\end{tikzpicture}
\end{document}

We begin by defining a path using normal TikZ commands and saving it as \tmppath. Then we define a new instance of the spath class and initialise it with \tmppath. We do various manipulations on it, and find out some facts about it, before finally using it. The last two, \mypath.use path with tikz(options) and \mypath.use path, are the actual rendering commands. The first uses the same methods as TikZ for rendering the path so can take any TikZ options (I don't guarantee 100% that they will all work, but they should). The second uses the underlying PGF system so takes one of the basic options: stroke or fill.

This doesn't really qualify as an answer to your question as none of the things that you ask for are implemented (note that length refers to the number of commands in the soft path, not the path length). But it wouldn't be hard to adapt either of the two already-given answers to this setting. It does answer the question in the comments about reusing a path, though.

Andrew Stacey
  • 153,724
  • 43
  • 389
  • 751
  • Great, the commands you mention are indeed very useful to manipulate paths, something that I missed from TikZ. Are you thinking of adding them to TikZ development too? It would be useful to have them integrated in this system. – Marco Lombardi Jun 08 '11 at 13:07
1

As a partial answer to my own question, here is a simple macro that allows one to perform a given operation on the points of a path previously saved using the path name mechanism.

\makeatletter
\def\everypathpoint#1#2{%
  \begingroup
    \expandafter\let\expandafter\pgfsyssoftpath@movetotoken\csname #2\endcsname
    \expandafter\let\expandafter\pgfsyssoftpath@linetotoken\csname #2\endcsname
    \expandafter\show\csname #2\endcsname
    \@nameuse{tikz@intersect@path@name@#1}%
  \endgroup
}%
\makeatother 

This macro could be used, for example, together with the code

  \newcount\mycount
  \mycount=0
  \def\myaction#1#2{\ifdim #2>0.5cm\global\advance\mycount by 1\fi}%

  \draw[thick, name path=f] plot (\x, \y{\x});
  \everypathpoint{f}{myaction}%

This code just calculates the number of points of the path above a given value, but any other operation would be allowed (including area calculations). It remains to be seen the number of elements a path can be made (I bet more than the minimal ones I considered here, moveto and lineto).

  • 1
    If you look in the PGF manual at the relevant chapter (forget which, but it's at the end) then you'll see that there aren't that many tokens: moveto, lineto, curveto, curvetosupporta, curvetosupportb, rect, rectsize, and closepath (if I remember right). – Andrew Stacey Jun 06 '11 at 21:32