5

I am defining a shape which can be drawn in normal or inverted mode. In normal mode, the border lines and inner lines are drawn with the current stoke color, while the shape is filled with the current fill color. In inverted mode, the border lines is still drawn with the current stoke color, but the shape is filled with the current stroke color and inner lines are drawn with the current fill color.

Here is a MWE:

\documentclass[varwidth,border=50]{standalone}

\usepackage{tikz}

\makeatother

\pgfkeys{%
  /pgf/invert/.initial=no,%
  /pgf/invert/.default=yes,%
}

\pgfdeclareshape{test}{%
  \inheritsavedanchors[from=circle]%
  \inheritanchorborder[from=circle]%
  \inheritanchor[from=circle]{center}%
  %
  \backgroundpath{%
    \pgfpathcircle{\pgfpointorigin}{5em}%
  }%
  %
  \beforebackgroundpath{%
    %\def\strokecolor{\string\color@pgfstrokecolor}%
    %\def\fillcolor{\string\color@pgffillcolor}%
    %\global\let\strokecolor\csname\string\color@pgfstrokecolor\endcsname%
    %\global\let\fillcolor\csname\string\color@pgffillcolor\endcsname%
    \def\borderColor{\strokecolor}%
    \ifnum\pdfstrcmp{\pgfkeysvalueof{/pgf/invert}}{no}=0%
      \def\fillColor{\fillcolor}%
      \def\innerColor{\strokecolor}%
    \else%
      \def\fillColor{\strokecolor}%
      \def\innerColor{\fillcolor}%
    \fi%
    \pgfsetlinewidth{.5em}%
    \pgfsetstrokecolor{\borderColor}%
    \pgfsetfillcolor{\fillColor}%
    \pgfpathcircle{\pgfpointorigin}{5em}%
    \pgfusepath{fill,stroke}%
    \pgfsetstrokecolor{\innerColor}%
    \pgfmoveto{\pgfpointpolar{135}{4em}}%
    \pgflineto{\pgfpointpolar{-45}{4em}}%
    \pgfusepath{stroke}%
  }%
}

\makeatletter

\begin{document}

  \begin{tikzpicture}
    % In this MWE, I'll use "\strokecolor" and "\fillcolor" in this example
    % instead of the commands I'm looking for to retrieve the current stroke
    % and fill colors

    \begin{scope}[draw=black, fill=white]
      \def\strokecolor{black}
      \def\fillcolor{white}

      \node[test] at (0em, 0em) {};
      \node[test, invert] at (12em, 0em) {};
    \end{scope}

    \begin{scope}[draw=blue, fill=red]
      \def\strokecolor{blue}
      \def\fillcolor{red}

      \node[test] at (0em, 12em) {};
      \node[test, invert] at (12em, 12em) {};
    \end{scope}
  \end{tikzpicture}

\end{document}

Here is the intended result:

MWE result

I tried to use the "\color@pgfstrokecolor" and "\color@pgffillcolor". However, I then get the following error:

! Package xcolor Error: Undefined color `\color@pgfstrokecolor'.

How can I retrieve the current stroke and fill colors in a PGF shape declaration ?

3
  • I think that instead of trying to invert some given colors, it would be easier to declare two different nodes/shapes: the original and the inverted. would it be possible?
    – Ignasi
    Jul 25, 2016 at 9:52
  • @Ignasi I do not want to provide the stroke and fill colors. I want the shape to retrieve them from the context. I used "\def\strokecolor{...}" and "\def\fillcolor{...}" in the MWE to simulate accessing the current stroke and fill colors thru the commands \strokecolor and \fillcolor. Once my problem is solved, I wont need those commands anymore.
    – Gus
    Jul 25, 2016 at 19:27

4 Answers 4

3

I used percusse's trick in the answer at How to save the current color in PGF/TikZ?.

Note also that the OP wrongly reversed the sense of \makeatletter and \makeatother which would never allow a solution, until corrected.

\documentclass[varwidth,border=50]{standalone}
\usepackage{tikz}
\makeatletter
\tikzset{%
  get stroke color/.code={%
    \expandafter\global% Jump over, now we have \global
    \expandafter\let% Jump over now we have \global\let
    \expandafter\pgfsavedstrokecolor% Jump we have \global\let\pgf...
    \csname\string\color@pgfstrokecolor\endcsname% Finally expand this and put it at the end 
    },                                           % \global\let\pgf...{} in expanded form 
  restore stroke color/.code={\pgf@setstrokecolor#1},
  get fill color/.code={%
    \expandafter\global% Jump over, now we have \global
    \expandafter\let% Jump over now we have \global\let
    \expandafter\pgfsavedfillcolor% Jump we have \global\let\pgf...
    \csname\string\color@pgffillcolor\endcsname% Finally expand this and put it at the end 
    },                                           % \global\let\pgf...{} in expanded form 
  restore fill color/.code={\pgf@setfillcolor#1},
}

\pgfkeys{%
  /pgf/invert/.initial=no,%
  /pgf/invert/.default=yes,%
}

\pgfdeclareshape{test}{%
  \inheritsavedanchors[from=circle]%
  \inheritanchorborder[from=circle]%
  \inheritanchor[from=circle]{center}%
  %
  \backgroundpath{%
    \pgfpathcircle{\pgfpointorigin}{5em}%
  }%
  %
  \beforebackgroundpath{%
    \def\borderColor{\strokecolor}%
    \ifnum\pdfstrcmp{\pgfkeysvalueof{/pgf/invert}}{no}=0%
%      \tikzset{restore fill color/.expand once=\pgfsavedfillcolor}
%      \tikzset{restore stroke color/.expand once=\pgfsavedstrokecolor}
    \else%
      \tikzset{restore stroke color/.expand once=\pgfsavedstrokecolor}
      \tikzset{restore fill color/.expand once=\pgfsavedstrokecolor}
    \fi%
    \pgfsetlinewidth{.5em}%
    \pgfpathcircle{\pgfpointorigin}{5em}%
    \pgfusepath{fill,stroke}%
    \ifnum\pdfstrcmp{\pgfkeysvalueof{/pgf/invert}}{no}=0%
%      \tikzset{restore stroke color/.expand once=\pgfsavedstrokecolor}
    \else%
      \tikzset{restore stroke color/.expand once=\pgfsavedfillcolor}
    \fi%
    \pgfmoveto{\pgfpointpolar{135}{4em}}%
    \pgflineto{\pgfpointpolar{-45}{4em}}%
    \pgfusepath{stroke}%
  }%
}
\makeatother
\begin{document}

  \begin{tikzpicture}
    \begin{scope}[draw=black, fill=white]
      \tikzset{get stroke color}
      \tikzset{get fill color}
      \node[test] at (0em, 0em) {};
      \node[test, invert] at (12em, 0em) {};
    \end{scope}

    \begin{scope}[draw=blue, fill=red]
      \tikzset{get stroke color}
      \tikzset{get fill color}
      \node[test] at (0em, 12em) {};
      \node[test, invert] at (12em, 12em) {};
    \end{scope}
  \end{tikzpicture}
\end{document}

enter image description here

2

This is not an answer to OP question, but an example for my comment.

As I don't know how to solve Gus' question, an alternative could be to define two different kind of nodes/shapes, one defined with "normal" colors and a second one with "inverted" colors. This way it's not necessary to discover what colors were used.

Instead of a new shape I've used a style.

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

\begin{document}
\begin{tikzpicture}[test/.style 2 args={draw=#1, fill=#2, line width=1mm, circle, 
      path picture={\draw[#1] ([shift={(-45:2em)}]path picture bounding box.north west)
              --([shift={(135:2em)}]path picture bounding box.south east);}},
      test inverted/.style 2 args={draw=#1, fill=#1, circle, line width=1mm, 
       path picture={\draw[#2] ([shift={(-45:2em)}]path picture bounding box.north west)
        --([shift={(135:2em)}]path picture bounding box.south east);}}]

\node[test={blue}{red}, minimum size=2cm] (A) {}; 
\node[test inverted={blue}{red}, minimum size=2cm, right= of A] (B) {}; 

\node[test={black}{white}, minimum size=2cm, above=of A] (C) {}; 
\node[test inverted={black}{white}, minimum size=2cm, right= of C] (D) {}; 
\end{tikzpicture}
\end{document}

enter image description here

1

An alternative solution to save current stroke/fill colors:

\tikzset{
  /tikz/save current fill color/.style={/utils/exec={\colorlet{#1}{\tikz@fillcolor}}},
  /tikz/save current stroke color/.style={/utils/exec={\colorlet{#1}{\tikz@strokecolor}}},
}

It allows to save the current fill/stroke color through xcolor, so it can be used elsewhere, e.g. to automatically generate different shades. For example, one can do \tikzset{save current fill color=savedcolor} to get the current fill color, or use it in a command such as \node[save current fill color=savedcolor,text=savedcolor] {text written in current fill color};.

Example:

\documentclass[varwidth,border=50]{standalone}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage{tikz}

\begin{document}

\makeatletter
\tikzset{
  /tikz/save current fill color/.style={/utils/exec={\colorlet{#1}{\tikz@fillcolor}}},
  /tikz/save current stroke color/.style={/utils/exec={\colorlet{#1}{\tikz@strokecolor}}},
}
\makeatother

\begin{tikzpicture}
    \begin{scope}[draw=black,fill=red,ultra thick]
        \fill[fill opacity=0.5] (0,0) rectangle (1,1);
        \draw (1/2,1/2) -- (1/2,3/2);
        \path[save current fill color=mycolor,draw=mycolor] (1/2,1/2) -- (3/2,1/2);
        % Next line would produce "Package xcolor Error: Undefined color `mycolor'."
        % \path[draw=mycolor] (0,0) -- (0,0);
        \tikzset{save current fill color=savedcolor}
        \node[text=savedcolor] at (2,1/2) {text};
    \end{scope}
\end{tikzpicture}


\end{document}

produces

Result of the example code

0

Following @Steven_B._Segletes answer, I came up with the following simplified solution that answers to my question.

\documentclass[varwidth,border=50]{standalone}

\usepackage{tikz}

\makeatletter

\pgfkeys{%
  /pgf/invert/.initial=no,%
  /pgf/invert/.default=yes,%
}

\pgfdeclareshape{test}{%
  \inheritsavedanchors[from=circle]%
  \inheritanchorborder[from=circle]%
  \inheritanchor[from=circle]{center}%
  %
  \backgroundpath{%
    \pgfpathcircle{\pgfpointorigin}{5em}%
  }%
  %
  \beforebackgroundpath{%
    \expandafter\let\expandafter\pgfsavedstrokecolor\csname\string\color@pgfstrokecolor\endcsname%
    \expandafter\let\expandafter\pgfsavedfillcolor\csname\string\color@pgffillcolor\endcsname%
    %
    \ifnum\pdfstrcmp{\pgfkeysvalueof{/pgf/invert}}{yes}=0%
      \expandafter\pgf@setfillcolor\pgfsavedstrokecolor%
    \fi%
    %
    \pgfsetlinewidth{.5em}%
    \pgfpathcircle{\pgfpointorigin}{5em}%
    \pgfusepath{fill,stroke}%
    %
    \ifnum\pdfstrcmp{\pgfkeysvalueof{/pgf/invert}}{yes}=0%
      \expandafter\pgf@setstrokecolor\pgfsavedfillcolor%
    \fi%
    \pgfmoveto{\pgfpointpolar{135}{4em}}%
    \pgflineto{\pgfpointpolar{-45}{4em}}%
    \pgfusepath{stroke}%
  }%
}

\makeatother

\begin{document}

  \begin{tikzpicture}
    \begin{scope}[draw=black, fill=white]
      \node[test] at (0em, 0em) {};
      \node[test, invert] at (12em, 0em) {};
    \end{scope}

    \begin{scope}[draw=blue, fill=red]
      \node[test] at (0em, 12em) {};
      \node[test, invert] at (12em, 12em) {};
    \end{scope}
  \end{tikzpicture}

\end{document}

The two following lines are still (potentially uselessly) a bit long, but I do not know how to reduce them.

\expandafter\let\expandafter\pgfsavedstrokecolor\csname\string\color@pgfstrokecolor\endcsname%
\expandafter\let\expandafter\pgfsavedfillcolor\csname\string\color@pgffillcolor\endcsname%

You must log in to answer this question.

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