7

I want to create a macro, namely \newpoint{<point name>}{<point style>}, that creates macros like \Point<point name>{<x coord>}{<y coord>}{<label>}.

I've seen How to define a macro to create a new macro with a name passed as its argument? which is half way through as it does not show how to make the created macro receive arguments.

I tried:

\newcommand*{\newpoint}[2]{%
   \tikzset{#1/.style={#2}}%
   \newcounter{point#1}\setcounter{point#1}{0}%
   \def\csname Point#1\endcsname (##1,##2)|##3;{%
        \stepcounter{point#1}\fill[#1] (##1,##2) circle (2pt) node[above](#1-\thepoint#1){##3};}%
}

But it didn't work, appearently Use of \csname doesn't match it's definition and honestly... I don't know what I'm doing anymore.

Here is my M(N)WE:

\documentclass[tikz, border=2mm]{standalone}
\newcommand*{\newpoint}[2]{%
   \tikzset{#1/.style={#2}}%
   \newcounter{point#1}\setcounter{point#1}{0}%
   \def\csname Point#1\endcsname (##1,##2)|##3;{%
        \stepcounter{point#1}\fill[#1] (##1,##2) circle (2pt) node[above](#1-\thepoint#1){##3};}%
}
\newpoint{A}{red}
\begin{document}
 \begin{tikzpicture}
  \PointA(1,2)|A;
 \end{tikzpicture}
\end{document} 
  • 1
    \expandafter\def\csname ..., the counter stuff seems very bad. What exactly are you trying there? – daleif Nov 17 '16 at 12:15
  • This is actually a simplified version of what I'm doing, the macro \PointA does way more than just the circle. But essencially, that's it, it does some drawing and uses automatic naming, therefore the counter is absolutely mantadory here. Using \expandafter gives a new error Undefined \thepoint , which means \thepoint#1 is not working, I may have to change the question... – Guilherme Zanotelli Nov 17 '16 at 12:26
  • really are you sure you want to allocate a new counter for every point (in classic tex for example that would limit you to 100 or so points) or a lot less if you have loaded many packages. – David Carlisle Nov 17 '16 at 12:28
  • @DavidCarlisle, I had no idea about that, but I think my drawing won't have as much as 100 Points. After done with one drawing I reset the counter. Are there better ways to Keep track of the Point numbers? – Guilherme Zanotelli Nov 17 '16 at 12:31
  • @DavidCarlisle Aren't all current LaTeX distributions etex-based, so that you can easily create thousands of counters? – Pieter van Oostrum Nov 17 '16 at 13:14
  • @PietvanOostrum yes sure but still it's almost always the wrong thing to do including here where it appears to be re-allocated each time it is used. – David Carlisle Nov 17 '16 at 13:30
  • @DavidCarlisle No, for each new point a counter is allocated. Then for each use of the point that particular counter is incremented so that the node names will be different, like A-1, A-2, etc. for point A, B-1, B-2, ... for point B, etc. Howver, if necessary this can be done also without more than 1 counter. – Pieter van Oostrum Nov 17 '16 at 13:33
  • I have added that solution (without extra counters) to my first solution. – Pieter van Oostrum Nov 17 '16 at 13:41
  • @PietvanOostrum yes the question got edited:-) – David Carlisle Nov 17 '16 at 14:07
  • @DavidCarlisle I guess your crystal ball is no longer working :) – Pieter van Oostrum Nov 17 '16 at 14:12
  • 1
    @PietvanOostrum at least you got the tick, not egreg, so all is not lost! – David Carlisle Nov 17 '16 at 15:00

2 Answers2

6

As @daleif remarked, put\expandafter before \def. And to get \thepointA you have to use \csname ... \endcsname there. I guess this is what you want:

\newcommand*{\newpoint}[2]{%
   \tikzset{#1/.style={#2}}%
   \newcounter{point#1}\setcounter{point#1}{0}%
   \expandafter\def\csname Point#1\endcsname (##1,##2)|##3;{%
        \stepcounter{point#1}\fill[#1] (##1,##2) circle (2pt) node[above](#1-\csname thepoint#1\endcsname){##3};}%
}

And here is a solution without a real counter for each call of \newpoint, based on the solution with a pseudo-counter by @egreg, but with an increment of the pseudo-counter, each time the \point<name> macro is called.

\newcounter{pointnumber}
\newcommand\StepPointNumber[1]{%
    \setcounter{pointnumber}{\csname number@point#1\endcsname}%
    \stepcounter{pointnumber}
    \expandafter\xdef\csname number@point#1\endcsname{\thepointnumber}%
              \expandafter\show\csname number@point#1\endcsname
}
\newcommand*{\newpoint}[2]{%
   \tikzset{#1/.style={#2}}%
   \expandafter\def\csname number@point#1\endcsname{0}%
   \expandafter\def\csname Point#1\endcsname (##1,##2)|##3;{%
        \StepPointNumber{#1}%
        \fill[#1] (##1,##2) circle (2pt) node[above](#1-\csname number@point#1\endcsname){##3-\csname number@point#1\endcsname};}%
}
  • Would you care to elaborate why using the pseudo counter would be an improvement as to having each \Point having its own counter? To me it seems unlogical. – Guilherme Zanotelli Nov 17 '16 at 13:54
  • As David Carlisle said in the comments, counters are a scarce resource, although in modern LaTeX systems there are 32768 available (of which some 100+ are already in use). So it was mainly an intellectual exercise. But in a very complicated and/or large document with many points (more than 30000) it could be helpful. – Pieter van Oostrum Nov 17 '16 at 14:08
  • Interesting... Although this was more an approach to reduce the preamble size and easily create (if needed be) other \Point like macros, I've been meaning to do something like it for a while, but now I've used to improve my answer in this question. Thank's for the insights! ;D – Guilherme Zanotelli Nov 17 '16 at 14:17
4

I don't see the need to allocate a new counter, unless you want to do arithmetic with it.

The main point, though, is that you need to use \csname in the right way:

\documentclass[tikz, border=2mm]{standalone}

\newcommand*{\newpoint}[2]{%
   \tikzset{#1/.style={#2}}%
   \expandafter\def\csname number@point#1\endcsname{0}%
   \expandafter\def\csname Point#1\endcsname (##1,##2)|##3;{%
        \fill[#1] (##1,##2) circle (2pt) node[above](#1-\csname number@point#1\endcsname){##3};}%
}

\newpoint{A}{red}

\begin{document}

\begin{tikzpicture}
  \PointA(1,2)|A;
\end{tikzpicture}

\end{document}
egreg
  • 1,121,712