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.

I would like to create a carc path macro (centered arc, that acts like circle, without changing the current position) in a way to be able to write :

\draw (0,0) carc(30:90:1) carc(30:90:2) carc(30:90:3);

I know how to do it with latex macro :

\def\carc(#1:#2:#3){++(#1:#3) arc(#1:#2:#3) ++(#2+180:#3)}

and then I can use :

\draw (0,0) \carc(30:90:1) \carc(30:90:2) \carc(30:90:3);

But I'm wondering, if there is a way to do this in pure TikZ ?

Is it a way to create new macros that acts like circle, rectangle, arc, ... on paths ?

share|improve this question
3  
No, AFAIK not with hacking/rewriting parts of the parsing code of TikZ. That code is complicated and not meant to be extensible. –  Martin Scharrer Nov 15 '11 at 21:26
1  
That's also why you might want to consider switching to Metapost;). –  mbork Nov 15 '11 at 22:11
1  
Alain Matthes has implemented something like this in tkz-euclide, which is built on TikZ. The centre needs to be a named coordinate/node. With \usepackage{tkz-euclide} \usetkzobj{arcs} in the preamble, and a node named O, you can write \tkzDrawArc[R](O,2cm)(30,90) in a tikzpicture to draw an arc with radius 2 cm from 30° to 90°. –  Torbjørn T. Nov 15 '11 at 23:00
add comment

3 Answers

up vote 9 down vote accepted

Here's an alternative solution that works with

\draw (0,0) carc(30:90:1) carc(30:90:2) carc(30:90:3);

and allows full use of the standard options that arc takes. It uses Jake's trick to extend the parser, but instead of inserting completely new code, we subvert the arc command to do it for us. Essentially, we replace carc by arc, which means that carc can take all the arguments that arc does, but then when arc actually comes to add something to the path (after it has worked out all the coordinates) we sneak in a couple of movetos to ensure that the centre of the path is where we want it to be. This will also work without subverting the parser simply by putting [centre arc] before calling arc. (In fact, carc workds by expanding to [centre arc] arc.)

I still strongly recommend the to path solution. Do not subvert the TikZ code unless you know what you are doing.

\documentclass{article}
%\url{http://tex.stackexchange.com/q/34973/86}

\usepackage{tikz}

\makeatletter
\def\tikz@@@carcfinal#1#2#3{%
  \pgf@process{#2}%
  \advance\pgf@x by \tikz@lastx
  \advance\pgf@y by \tikz@lasty
  \pgfpathmoveto{\pgfqpoint{\the\pgf@x}{\the\pgf@y}}%
  #1%
  \pgfpathmoveto{\pgfqpoint{\the\tikz@lastx}{\the\tikz@lasty}}%
  \let\tikz@@@arcfinal=\carc@orig@@@arcfinal
}

\def\tikz@carcfinal{%
  \tikz@lastxsaved=\tikz@lastx%
  \tikz@lastysaved=\tikz@lasty%
  \let\tikz@arcfinal=\carc@orig@arcfinal
  \tikz@scan@next@command%
}


\let\carc@orig@@@arcfinal=\tikz@@@arcfinal
\let\carc@orig@arcfinal=\tikz@arcfinal

\def\tikz@cchar{% If keyword starts with "c..."
    \pgfutil@ifnextchar i %... starts with "ci..."
        {\tikz@circle}%
        {\pgfutil@ifnextchar h% ... starts with "ch..."
            {\tikz@children}
            {\pgfutil@ifnextchar a % ... starts with "ca..."
                {\carc@call}
                \tikz@cochar
            }
        }
}%

\def\carc@call{\tikzset{centred arc}\tikz@scan@next@command}

\tikzset{
  centred arc/.code={%
    \let\tikz@@@arcfinal=\tikz@@@carcfinal
    \let\tikz@arcfinal=\tikz@carcfinal
  },
}
\makeatother

\begin{document}
\begin{tikzpicture}
\draw (0,0) carc[start angle=30,end angle=90,radius=1] carc (30:90:2) carc(30:90:3);
\end{tikzpicture}
\end{document}

Note the different syntax for the carcs and the varying number of spaces.

Centred arcs

share|improve this answer
    
Thanks Andrew and Jake ! This answers completely my questions. –  Kpym Nov 16 '11 at 22:22
add comment

Martin is right, you need to rewrite parts of the parser. It's not very difficult, however. The parser lives in tikz.code.tex. There's a macro called \tikz@cchar which processes all keywords starting with c. It contains nested \pgfutil@ifnextchar statements that act as a "select" expression: They look at the next letter in the keyword and call the corresponding macro.

Here's the original \tikz@cchar definition:

\def\tikz@cchar{%
  \pgfutil@ifnextchar i{\tikz@circle}%
  {\pgfutil@ifnextchar h{\tikz@children}{\tikz@cochar}}}%

If the character following a c is an i, TikZ assumes you mean the circle keyword. If it is an h, it assumes you want a child in a tree. If it is neither, TikZ assumes you want either a cosine or a coordinate, so it passes control on to the \tikz@cochar, which then decides in a similar manner which of the two keywords are present.

To make the parser recognise carc, we can redefine the \tikz@cchar macro like this:

\makeatletter
\def\tikz@cchar{% If keyword starts with "c..."
    \pgfutil@ifnextchar i %... starts with "ci..."
        {\tikz@circle}%
        {\pgfutil@ifnextchar h% ... starts with "ch..."
            {\tikz@children}
            {\pgfutil@ifnextchar a % ... starts with "ca..."
                {\carc}
                \tikz@cochar
            }
        }
}%

The \carc macro needs to be redefined slightly. The syntax isn't \carc (#1:#2:#3), but rather \carc arc (#1:#2:#3), since the initial c is the entity that actually calls the macro, and the arc is still present in the macro argument. Also, we can't use TikZ commands directly in the code (PGF commands would work). TikZ commands can be used by wrapping them in \tikzset{insert path={<TikZ code>}}, which directly inserts the desired code. Finally, we need to tell the TikZ parser to go back to looking for keywords by issuing a \tikz@scan@next@command at the end of the \carc macro.

Here's a full example:

\documentclass[12pt]{article}
\usepackage{tikz}
\begin{document}

\makeatletter
\def\tikz@cchar{% If keyword starts with "c..."
    \pgfutil@ifnextchar i %... starts with "ci..."
        {\tikz@circle}%
        {\pgfutil@ifnextchar h% ... starts with "ch..."
            {\tikz@children}
            {\pgfutil@ifnextchar a % ... starts with "ca..."
                {\carc}
                \tikz@cochar
            }
        }
}%

\def\carc arc (#1:#2:#3){% The macro will get called after the initial "c" of "carc" is already gobbled up, and only "arc" is left
    \tikzset{insert path={++(#1:#3) arc(#1:#2:#3) ++(#2+180:#3)}}% Inject the TikZ commands
    \tikz@scan@next@command% Return control to the parser
}
\makeatother

\begin{tikzpicture}%
\draw (0,0) carc (30:90:1) carc (30:90:2) carc (30:90:3);
\end{tikzpicture}%
\end{document}
share|improve this answer
    
Thanks Jake ! It works fine, with one exception : I have to put a white space between "carc" and "(", so carc (30:90:1) works, but carc(30:90:1) doesn't. Any idea ? –  Kpym Nov 16 '11 at 7:56
    
@Kpym: You can just say \def\carc arc(#1:#2:#3){... (without the whitespace). –  Jake Nov 16 '11 at 8:00
    
Or you can have \carc have no arguments but simply swallow the arc and its following spaces and then call \c@rc which does the actual processing. –  Andrew Stacey Nov 16 '11 at 9:23
    
@Jake Yes but if I declare \def\carc arc(#1:#2:#3){ I can't use it with space. The question is how to be able to use both ? And even better how to use carc[...] (...) like arc[...] (...) by passing the arguments to the arc macro ? –  Kpym Nov 16 '11 at 9:45
    
@Kpym: with the two stage macro then the first swallows whatever spaces are there in such a way that if there are none, it doesn't complain. Then you can use it both with and without a space. –  Andrew Stacey Nov 16 '11 at 9:50
show 1 more comment

Jake and Martin are right in that if you really absolutely must have

\draw (0,0) carc(30:90:1) carc(30:90:2) carc(30:90:3);

then your only option is to redefine the parser.

However, TikZ does have a standard way of defining quite general alternative paths without necessitating this heavy duty hacking. It's the to path syntax. A to path allows you to specify quite arbitrary code to replace the next bit of the path with something quite complicated. You can search the site for to path to get an idea of what's possible with this.

In your case, the code is quite simple. It does have one sneaky bit: the target of a to path is saved unprocessed which means that we can interpret it how we like. Instead of specifying a coordinate, we use it to specify the parameters for the arc command. We therefore have to parse this data and split it according to how we're going to use it. There are other ways to do this, but as we have to specify a target for a to path, we may as well use that as a way of passing information.

Here's the code. I included your macro for comparison so that you can see that this does do the same. I made two changes when translating your macro to a to path: I used the newer syntax for the arc command and I used the \tikztostart command when ensuring that we return to the place from whence we came. Both are cosmetic changes designed to make the code clearer so that in 6 months time you can still understand what it's doing without having to puzzle it out.

\documentclass{article}

\usepackage{tikz}

\def\carcArgs#1:#2:#3\carcSTOP{%
  \def\carcOne{#1}%
  \def\carcTwo{#2}%
  \def\carcThree{#3}%
}

\tikzset{
  centred arc/.style={%
    to path={%
      \pgfextra{%
        \expandafter\carcArgs\tikztotarget\carcSTOP
      }
      ++(\carcOne:\carcThree) arc[start angle=\carcOne, end angle=\carcTwo, radius=\carcThree] (\tikztostart)
    }
  }
}
\def\carc(#1:#2:#3){++(#1:#3) arc(#1:#2:#3) ++(#2+180:#3)}

\begin{document}
\begin{tikzpicture}
\draw (0,0) -- (1,0) \carc(30:90:1) -- (2,-1);
\begin{scope}[yshift=-2cm,red]
\draw (0,0) -- (1,0) to[centred arc] (30:90:1) -- (2,-1);
\end{scope}
\end{tikzpicture}
\end{document}

Here's the result, the black line is your macro and the red line is mine:

Centred arcs

share|improve this answer
    
This is a nice solution Andrew, but I was searching how to extend TikZ with carc path that works like core macro. The only thing that is missing for me in Jake's solution, is the possibility to put [...] arguments after carc. For me carc is really missing in TikZ. –  Kpym Nov 16 '11 at 10:29
1  
@Kpym: I'm sure that everyone has their own list of things that are missing in TikZ for them. That's why the to path is so useful: maximum flexibility combined with portability. –  Andrew Stacey Nov 16 '11 at 11:03
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.