4

based on this nice question and its answers, I have this little code:

%
% circled steps (with labels to reference them!)
%
\newcommand*\circled[2][]{\tikz[baseline=(char.base)]{
            \node[shape=circle,draw,inner sep=1pt,#1] (char) {#2};}}
\newcounter{cstepcnt}
\newcommand{\startcstep}{\setcounter{cstepcnt}{0}}
\newcommand{\cstep}{%
    \refstepcounter{cstepcnt}%
    \circled[red, font=\scriptsize]{\arabic{cstepcnt}}% this is pretty dangerous
}

This let me mark part of text, or equation, or whatever with small circled numbers that I can refer to later (with \label and \ref), like:

enter image description here

This in nice, and a better solution than used \textcircled{} (the numbers are badly centered, especially if they're bigger than 9, and there is no flexibility in the shape or colors), but I know this is living dangerously when I use it into a picture; sometime it works, some time it explodes (because, I know, you should never nest tikzpictures).

Is there a way to obtain the same flexibility? I mean, havinf a kind of "circled number steps" that I can use inside and outside tikzpictures?

This is a MWE, where all is working, because I was not able to find a simple example where it breaks... but it breaks sometime, giving very big or small circles.

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage{tikz}
%
\newcommand*\circled[2][]{\tikz[baseline=(char.base)]{
            \node[shape=circle,draw,inner sep=1pt,#1] (char) {#2};}}
\newcounter{cstepcnt}
\newcommand{\startcstep}{\setcounter{cstepcnt}{0}}
\newcommand{\cstep}{%
    \refstepcounter{cstepcnt}%
    \circled[red, font=\scriptsize]{\arabic{cstepcnt}}% this is pretty dangerous
}
\renewcommand{\thecstepcnt}{\textbf{\arabic{cstepcnt}:}}
\begin{document}
one text here \cstep{} some text here \cstep\label{c:one}.

\begin{tikzpicture}
    \draw (0,0) node[draw](A){text\cstep\label{c:inner}};
\end{tikzpicture}

And then ref~\ref{c:one} and \ref{c:inner}.
\end{document}

enter image description here

Rmano
  • 40,848
  • 3
  • 64
  • 125
  • 1
    AFAIK there is no really safe way of nesting tikzpictures. Of course, you could make the above more likely to be stable by adding several keys like minimum width=0pt,minimum height=0pt,minimum size=0pt and so on, which may prevent them from growing too large. But still you will not be safe from some every path/.append style=..., nor every node/.append style=... unless you locally reset all those as well. –  Jun 18 '19 at 19:08
  • @marmot, thanks --- I suspected it. Maybe I should make the circle with some other graphic package... would be stable if I use the old picture one? Will investigate. – Rmano Jun 18 '19 at 20:05
  • Aren't you the world expert in declaring new shapes? I suspect it would be much simpler to copy the circle node shape and make it much less susceptible to external directives. You could just look up the ellipse node shape from pgflibraryshapes.geometric.code.tex and kick out all the adjustments. I do not see how then the size could be adjusted by some key, simply because you do no longer look at these keys. (This is also a conceivable way to find out what keys the shape looks at.) –  Jun 18 '19 at 20:12
  • @marmot thanks for the compliment, but I don't think so ;-)... anyway it's a good idea. Thanks again! – Rmano Jun 18 '19 at 20:17

2 Answers2

3

Revised version after your feedback. This is an attempt of an answer but no guarantee that it works. Why should one not nest tikzpictures? The main reason is that the pgf keys of the ambient tikzpicture will affect the shapes of the inner ones. One way to evade this is to create "new" shapes that merely differ from the standard ones by the pgf keys they use. So I copied the ellipse shape from pgflibraryshapes.geometric.code.tex, dropped some pgf key and the inner sep sit now in the directory Rmano such that you can still change it from outside if needed. This shape is used at the pgf level. There is no mediator like the TikZ \node command which could obscure things. Unfortunately one has to work a bit harder to implement baseline.

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage{tikz}
\makeatletter
\pgfdeclareshape{stubborn ellipse}
%
% Draws a circle around the text
%
{%
  \savedanchor\centerpoint{%
    \pgf@x=.5\wd\pgfnodeparttextbox%
    \pgf@y=.5\ht\pgfnodeparttextbox%
    \advance\pgf@y by-.5\dp\pgfnodeparttextbox%
  }%
  \savedanchor\radius{%
    %
    % Calculate ``height radius''
    %
    \pgfmathsetlength\pgf@yb{\pgfkeysvalueof{/Rmano/inner ysep}}%
    \advance\pgf@y by\pgf@yb%
    %
    % Calculate ``width radius''
    %
    \pgf@x=.5\wd\pgfnodeparttextbox%
    \pgfmathsetlength\pgf@xb{\pgfkeysvalueof{/Rmano/inner xsep}}%
    \advance\pgf@x by\pgf@xb%
    %
    % Adjust
    %
    \pgf@x=1.4142136\pgf@x%
    \pgf@y=1.4142136\pgf@y%
  }%

  %
  % Anchors
  %
  \anchor{center}{\centerpoint}%
  \anchor{mid}{\centerpoint\pgfmathsetlength\pgf@y{.5ex}}%
  \anchor{base}{\centerpoint\pgf@y=0pt}%
  \anchor{north}
  {
    \pgf@process{\radius}
    \pgf@ya=\pgf@y%
    \pgf@process{\centerpoint}
    \advance\pgf@y by\pgf@ya
  }%
  \anchor{south}
  {
    \pgf@process{\radius}
    \pgf@ya=\pgf@y%
    \pgf@process{\centerpoint}
    \advance\pgf@y by-\pgf@ya
  }%
  \anchor{west}
  {
    \pgf@process{\radius}
    \pgf@xa=\pgf@x%
    \pgf@process{\centerpoint}
    \advance\pgf@x by-\pgf@xa
  }%
  \anchor{mid west}
  {%
    \pgf@process{\radius}
    \pgf@xa=\pgf@x%
    \pgf@process{\centerpoint}
    \advance\pgf@x by-\pgf@xa%
    \pgfmathsetlength\pgf@y{.5ex}
  }%
  \anchor{base west}
  {%
    \pgf@process{\radius}
    \pgf@xa=\pgf@x%
    \pgf@process{\centerpoint}
    \advance\pgf@x by-\pgf@xa%
    \pgf@y=0pt
  }%
  \anchor{north west}
  {
    \pgf@process{\radius}
    \pgf@xa=\pgf@x%
    \pgf@ya=\pgf@y%
    \pgf@process{\centerpoint}
    \advance\pgf@x by-0.707107\pgf@xa
    \advance\pgf@y by0.707107\pgf@ya
  }%
  \anchor{south west}
  {
    \pgf@process{\radius}
    \pgf@xa=\pgf@x%
    \pgf@ya=\pgf@y%
    \pgf@process{\centerpoint}
    \advance\pgf@x by-0.707107\pgf@xa
    \advance\pgf@y by-0.707107\pgf@ya
  }%
  \anchor{east}
  {%
    \pgf@process{\radius}
    \pgf@xa=\pgf@x%
    \pgf@process{\centerpoint}
    \advance\pgf@x by\pgf@xa
  }%
  \anchor{mid east}
  {%
    \pgf@process{\radius}
    \pgf@xa=\pgf@x%
    \pgf@process{\centerpoint}
    \advance\pgf@x by\pgf@xa%
    \pgfmathsetlength\pgf@y{.5ex}
  }%
  \anchor{base east}
  {%
    \pgf@process{\radius}
    \pgf@xa=\pgf@x%
    \pgf@process{\centerpoint}
    \advance\pgf@x by\pgf@xa%
    \pgf@y=0pt
  }%
  \anchor{north east}
  {
    \pgf@process{\radius}
    \pgf@xa=\pgf@x%
    \pgf@ya=\pgf@y%
    \pgf@process{\centerpoint}
    \advance\pgf@x by0.707107\pgf@xa
    \advance\pgf@y by0.707107\pgf@ya
  }%
  \anchor{south east}
  {
    \pgf@process{\radius}
    \pgf@xa=\pgf@x%
    \pgf@ya=\pgf@y%
    \pgf@process{\centerpoint}
    \advance\pgf@x by0.707107\pgf@xa
    \advance\pgf@y by-0.707107\pgf@ya
  }%
  \anchorborder{
    \edef\pgf@marshal{%
      \noexpand\pgfpointborderellipse
      {\noexpand\pgfqpoint{\the\pgf@x}{\the\pgf@y}}
      {\noexpand\radius}%
    }%
    \pgf@marshal%
    \pgf@xa=\pgf@x%
    \pgf@ya=\pgf@y%
    \centerpoint%
    \advance\pgf@x by\pgf@xa%
    \advance\pgf@y by\pgf@ya%
  }%

  %
  % Background path
  %
  \backgroundpath
  {
    \pgf@process{\radius}%
    \pgfutil@tempdima=\pgf@x%
    \pgfutil@tempdimb=\pgf@y%
    \advance\pgfutil@tempdima by-\pgf@xb%
    \advance\pgfutil@tempdimb by-\pgf@yb%
    \pgfpathellipse{\centerpoint}{\pgfqpoint{\pgfutil@tempdima}{0pt}}{\pgfqpoint{0pt}{\pgfutil@tempdimb}}%
  }%
}%
\pgfkeys{/Rmano/inner ysep/.initial=2pt,/Rmano/inner xsep/.initial=2pt}
%
\newcommand*\circled[2][]{\setbox0\hbox{#1}%
\pgf@xa=\ht0%
\advance\pgf@xa by \pgfkeysvalueof{/Rmano/inner ysep}%
\pgf@ya=0.4142136\pgf@xa%
\raisebox{-\pgf@ya}{\textcolor{red}{\begin{pgfpicture}%
\pgfnode{stubborn ellipse}{center}{#2}{#1}{\pgfusepath{stroke}}%
\end{pgfpicture}}}}
\makeatother
\newcounter{cstepcnt}
\newcommand{\startcstep}{\setcounter{cstepcnt}{0}}
\newcommand{\cstep}{%
    \refstepcounter{cstepcnt}%
    \typeout{\number\value{cstepcnt}}%
    \circled[cnode-\number\value{cstepcnt}]{\number\value{cstepcnt}}% this is pretty dangerous
}
\renewcommand{\thecstepcnt}{\textbf{\arabic{cstepcnt}:}}
\begin{document}
one text here \cstep{} some text here \cstep\label{c:one}.

\begin{tikzpicture}
    \draw (0,0) node[draw](A){text\cstep\label{c:inner}};
\end{tikzpicture}

And then ref~\ref{c:one} and \ref{c:inner}.
\foreach \X in {1,...,12} {\cstep{}}.
\end{document}

enter image description here

(Looking at this \raisebox hackery one can be really glad that TikZ comes with simple keys like baseline which allow one to avoid all this mess. ;-)

  • 1
    Version 2 works! Version 1 will explode, just try \draw (0,0) node[draw, text width=5cm](A){text\cstep\label{c:inner}}; --- I think the problem is that in the node there is the parttextbox thing which is not really controllable (and I'd like it to be, sometime, to make magic like undo negative xscale or things like that. Thanks a lot! What about a minipackage? ;-) – Rmano Jun 19 '19 at 07:51
  • @Rmano Thanks for letting me know! I see, text width enters twice, which makes perfect sense. (I guess it is not really nice enough to become a package on its own but may be useful for package authors.) –  Jun 19 '19 at 14:10
  • I posted a streamlined version for anyone interested! – Rmano Jun 19 '19 at 18:49
  • Ok, it still will explode if you have a baseline=4cm in your external tikzpicture. I think it's not worthwhile to fight more... – Rmano Jun 20 '19 at 08:01
1

Really, it seems almost impossible to safely nest pgfpictures (as is clearly stated everywhere). So I implemented a different strategy that you can see in this answer to the original question: https://tex.stackexchange.com/a/496665/38080

old answer: works sometime

Ok --- I simplified the wonderful @marmot answer ... here is a small package:

warning: it still explodes sometime (don't know why, no time to dig on). Definitely you should not nest tikz or pgf pictures...

% save as marmotcsteps.sty
\NeedsTeXFormat{LaTeX2e}[1996/06/01]
\ProvidesPackage{marmotcsteps}[2019/06/19 Circled steps that you can reference and use in and outside tikz v1]
\RequirePackage{tikz}
% invented by @marmot: https://tex.stackexchange.com/a/496379/38080
% streamlined by @Rmano

\pgfdeclareshape{stubborn ellipse} % Draws a circle around the text {% \savedanchor\centerpoint{% \pgf@x=.5\wd\pgfnodeparttextbox% \pgf@y=.5\ht\pgfnodeparttextbox% \advance\pgf@y by-.5\dp\pgfnodeparttextbox% }% \savedanchor\radius{% % Calculate height radius'' \pgf@y=.5\ht\pgfnodeparttextbox \pgfmathsetlength\pgf@yb{\pgfkeysvalueof{/csteps/inner ysep}}% \advance\pgf@y by\pgf@yb% % Calculatewidth radius'' \pgf@x=.5\wd\pgfnodeparttextbox% \pgfmathsetlength\pgf@xb{\pgfkeysvalueof{/csteps/inner xsep}}% \advance\pgf@x by\pgf@xb% % Adjust so that it's a circle for 1-digit numbers \ifdim\pgf@x<\pgf@y\pgf@x=\pgf@y\fi }% % Anchors \anchor{center}{\centerpoint}% % Background path \backgroundpath {% \pgf@process{\radius}% \pgfutil@tempdima=\pgf@x% \pgfutil@tempdimb=\pgf@y% \pgfpathellipse{\centerpoint}{\pgfqpoint{\pgfutil@tempdima}{0pt}}{\pgfqpoint{0pt}{\pgfutil@tempdimb}}% }% }% \newcommand*\marmotcircled[1]{\setbox0\hbox{#1}% \pgf@ya=\pgfkeysvalueof{/csteps/inner ysep}% \raisebox{-\pgf@ya}{% \begin{pgfpicture}\pgfsetcolor{\pgfkeysvalueof{/csteps/outer color}}% \pgfnode{stubborn ellipse}{center}{% \textcolor{\pgfkeysvalueof{/csteps/inner color}}{#1}}{}{\pgfusepath{stroke}}% \end{pgfpicture}% }} \newcounter{cstepcnt} \newcommand{\startcstep}{\setcounter{cstepcnt}{0}} \newcommand{\cstep}{% \refstepcounter{cstepcnt}% \typeout{\number\value{cstepcnt}}% \marmotcircled{\number\value{cstepcnt}}% this is pretty dangerous } \renewcommand{\thecstepcnt}{\textbf{\arabic{cstepcnt}:}} % \pgfkeys{/csteps/inner ysep/.initial=1pt, /csteps/inner xsep/.initial=1pt, /csteps/inner color/.initial=red, /csteps/outer color/.initial=blue, } %

and you can use it as:

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage{tikz}
%
\usepackage{marmotcsteps}

\begin{document} one text here \cstep{} some text here \cstep\label{c:one}.

\begin{tikzpicture} \draw (0,0) nodedraw, text width=5cm{text\cstep\label{c:inner}}; \draw (4,0) nodedraw, color=green{text\cstep\label{c:inner2}}; \end{tikzpicture}

And then \foreach \i in {0,...,12} {\cstep\ }

And then ref~\ref{c:one} and \ref{c:inner}. \end{document}

giving:

enter image description here

Rmano
  • 40,848
  • 3
  • 64
  • 125
  • +1. One should always leave the package writing to those who know what they are doing. ;-) –  Jun 19 '19 at 18:50
  • @marmot --- thanks! I finally have a solution that works in every case I checked, please look at it https://tex.stackexchange.com/a/496665/38080 – Rmano Jun 20 '19 at 16:58