21

How do you do program simple functions using the TikZ arithmetic engine?

I love the idea of using TikZ in order to “program” my pictures, being able to write code once and reuse, get precise coordinates and intersections, being able to change just one number and get the whole picture to automagically update whatever depended on that number. That is amazing.

However, maybe because I am extremely used to more traditional programming languages, and I think in a more procedural (rather than a “macro expansiony” way), I tend to get quickly frustrated as soon as I need even basic stuff such as an if or a while loop. I got hold of the TikZ \foreach command which works wonders; but why aren't similar commands for if and while defined? (Or there are, but I'm missing them?)

You will probably point me to other packages that do define such commands, but I find it non-trivial (and headache producing) to write the correct code to switch between TikZ arithmetic expressions and strings, tokens, (un)expanded macros or whatever the hell these packages do actually compare in their conditionals.

This is what I think should be essential to have:

  • \ifmath{expr}{true}{false} — evaluates expr and if true (e.g. not 0), continues executing the code in the true part; otherwise it continues with the false part.
  • \whilemath{expr}{code} — repeatedly executes code while the expr yields a true value.

I had sort of written versions of these, but I would like to see what you can come up with.

Now, that was the basic, but what I would really really be able to do, is to easily define new functions that can be also used within an expr. For example, suppose that I want to write a wee tiny function that simply takes two numbers and returns their gcd. This is some simple pseudocode of what I want to write:

function gcd(a, b) {
  while (b) {
    t = b;
    b = a mod b;
    a = t;
  }
  return a;
}

How would I go about to write such kind of code in TikZ to compute gcd, and furthermore, being able to write things like 4 + gcd(15,6) as further TikZ expressions? Please note, I'm not asking how to write some token-expansion magic to compute the gcd of two numbers; I'm asking whether if it is possible to actually write simple functions such like this one (using plain if's, while's and assignments).

And now that I'm in the mood of wishing for everything, how do you deal with variables? In TikZ examples I've seen its common to simply store values inside macros, and TikZ expressions seem to be pretty happy working with those. However, I also had to write my own command to evaluate a TikZ expr and store the result in a macro of my choosing (\pgfmathsetmacro didn't work as expected, for example \pgfmathsetmacro{\a}{int(0)}\a produces 0.0 rather than 0).

Furthermore, by using just plain macro names, like \v, I worry all the time that I might accidentally redefine some important pre-existing macro; but I don't want to call all my variables \mya, \myb, \myc. Sometimes I feel temped to change the cat code of : so that :a, :b, :c become macro names... but experience tells me that every time one messes with cat-codes everything breaks.

9
  • 2
    I kid you not, at some point I actually wrote perl code to spit TikZ code, because I wasn't able to write a while loop! Apr 25, 2012 at 18:47
  • 1
    Regarding the trailing .0: There's \pgfmathtruncatemacro if you don't want the trailing .0 that \pgfmathsetmacro produces. Similarly, there's \pgfmathaddtocounter, \pgfmathsetlength, and a whole lot more, described in the pgfmanual.
    – Jake
    Apr 25, 2012 at 18:49
  • 1
    You might want to have a look at perltex. ctan.org/tex-archive/macros/latex/contrib/perltex
    – amorua
    Apr 25, 2012 at 19:00
  • @Jake, I was aware of \pgfmathtrucatemacro; but I was hoping for a single macro that I could use to get both results with and without decimals, depending on the expression (e.g. using integer or real division, explicitly calling int, etc.) Apr 25, 2012 at 21:05
  • @amorua, thanks for the tip. It looks interesting and quite useful. However, because I think it should be possible, I would rather stay within the world of TikZ/LaTeX. Apr 25, 2012 at 21:27

2 Answers 2

6

Thanks all for the comments, and Altermundus for your alternative answer. However, I really think that for users that would like to enjoy a higher level of abstraction (as opposed to low-level macro hackery) a much better interface for defining such simple functions could be implemented. We don't really have to go down to low-level TeX coding.

For the moment I'll forget about the issue with variable namespaces, but with your help and some tinkering with macros, reading the TikZ sources and lots of trial and error, I was already able to provide something much closer to what I'm looking for.

\documentclass{article}
\usepackage{tikz}
\usepackage{etoolbox} % for \ifdefstring

\makeatletter
\newcommand{\setmath}[2]{\pgfmathparse{#2}\edef#1{\pgfmathresult}}
\newcommand{\domath}[1]{\pgfmathparse{#1}\pgfmathresult}
\newcommand{\ifmath}[1]{\pgfmathparse{equal(#1,0)}\ifdefstring\pgfmathresult{0}}
\newcommand{\whilemath}[2]{\ifmath{#1}{#2\whilemath{#1}{#2}}{}}
\newcommand{\returnmath}[1]{\pgfmathparse{#1}\pgfmath@smuggleone\pgfmathresult}
\newcommand{\newmathfunction}[3]{\pgfmathdeclarefunction{#1}{#2}{\begingroup#3\endgroup}}
\makeatother

\newmathfunction{mygcd}{2}{%
  \setmath\a{#1}%
  \setmath\b{#2}%
  \whilemath{\b}{%
    \setmath\t{\b}%
    \setmath\b{mod(\a,\b)}%
    \setmath\a{\t}%
  }%
  \returnmath{int(\a)}%
}

\begin{document}
\setmath\a{15}
\setmath\b{6}
gcd(\a,\b) = \domath{mygcd(\a,\b)}.
\end{document}

Now you can actually read the code to compute the gcd, which was pretty much translated line by line from my original pseudocode. I know that this might be grossly inefficient but, who cares, I just want to compute the coordinates for a few points, I'm not trying to do sophisticated physics simulations here.

I would greatly appreciate any comments and suggestions to the programming interface that I propose; in particular using etoolbox just to compare against the token 0 seems like an overkill, but etoolbox was something that I could also easily learn.

2
  • Be careful, your code gives an error with pgf 2.1 cvs because gcd is defined ! May 9, 2012 at 16:00
  • Grr! Thanks for letting me now :) I'll just rename it to mygcd, cause I think it is a good example of "programming" in TikZ May 9, 2012 at 19:00
5

I I think you get a bad request. I will try to explain correctly my point of view with my poor english language.

The answer to your question is lua and luatex. If you want to program something inside TeX, I think it's the answer.

pgf/tikz is very well integrated to TeX because pgf is built with TeX. The problems with that, are that pgf has not a great power and then it's difficult to create sophisticated tools because the tools are very slow and sometimes imprecise.

If we look at your example gcd, I'm the author of \pgfmathgcd{x}{y} ( you need to use pgf 2.1 cvs to use it). Now you can write 4+gcd(15,4.

How this function is programmed ? with TeX !

\pgfmathdeclarefunction{gcd}{2}{%
  \begingroup
    \pgfmathcontinuelooptrue
    \pgfmath@xa=#1pt
    \pgfmath@xb=#2pt
    \ifdim\pgfmath@xa=0pt 
      \pgfmathcontinueloopfalse
      \pgfmath@xa=\pgfmath@xb
    \fi 
    \ifdim\pgfmath@xb=0pt
      \pgfmathcontinueloopfalse
      \pgfmath@xb=\pgfmath@xa
    \fi
    \ifdim\pgfmath@xa<0pt
      \pgfmath@xa=-\pgfmath@xa
    \fi
    \ifdim\pgfmath@xb<0pt
      \pgfmath@xb=-\pgfmath@xb
    \fi
    \loop
      \ifpgfmathcontinueloop 
      \ifdim\pgfmath@xa=\pgfmath@xb
        \pgfmathcontinueloopfalse
      \else
        \ifdim\pgfmath@xa>\pgfmath@xb 
          \advance\pgfmath@xa by-\pgfmath@xb
        \else 
          \advance\pgfmath@xb by-\pgfmath@xa
        \fi
      \fi
    \repeat
    \pgfmathparse{int(\pgfmath@xa)}%
    \pgfmath@smuggleone\pgfmathresult
  \endgroup}

Ok it's not funny, but you need to program with tex, not latex or something else , but tex !! and you need to create a function compatible with other pgfmath functions. if and while are here \ifdim and loop. It's rudimentary but like Knuth writes : TeX is not exactly a programming language like C or Pascal.

Actually if you look at the cvs version you can see a new library luamath. I think with luamath and lua, it would be funny and efficient to program math inside TikZ.

In the cvs version now you have gcd(x,y), isodd(x) , iseven(x) and isprime(x). I created these functions because I needed them for my package tkz-berge.

The last remark, pgf is portable and if you create new tools for programming, your tools needs to be compatible.

I forgot some technical remarks :

1) Like Jake writes you have \pgfmathtruncatemacro

2) if you want to use \x or \a, it's possible if you write inside a tex's group. If you look inside pgfornament.sty you will see code like this :

\def\pgf@@ornament#1{%
\begingroup
\def\i{\pgfusepath{clip}}%
\def\k{\pgfusepath{stroke}}%
\let\o\pgfpathclose
\let\s\pgfusepathqfillstroke
\def\p ##1##2{\pgfqpoint{##1bp}{##2bp}}%
\def\m ##1 ##2 {\pgfpathmoveto{\p{##1}{##2}}}%
\def\l ##1 ##2 {\pgfpathlineto{\p{##1}{##2}}}%
\def\r ##1 ##2 ##3 ##4 {\pgfpathrectangle{\p{##1}{##2}}{\p{##3}{##4}}}%
\def\c ##1 ##2 ##3 ##4 ##5 ##6 {%
\pgfpathcurveto{\p{##1}{##2}}{\p{##3}{##4}}{\p{##5}{##6}}}%
\@@input \OrnamentsFamily#1.pgf%
\endgroup}% 

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .