Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unexpected behavior when using colorbar as legend #465

Open
BobYue-01 opened this issue Jul 28, 2023 · 4 comments
Open

Unexpected behavior when using colorbar as legend #465

BobYue-01 opened this issue Jul 28, 2023 · 4 comments

Comments

@BobYue-01
Copy link

When using the colorbar as legend key, the ticks on colorbar seem to be incorrect: from the third onwards, they are all the same as the second.

The error is already visible in the official manual (1.18.1, page 293).
1.18.1 Manual, page 293

To make sure that I understood correctly what the key does, I checked version 1.16, which shows the expected result in the manual (1.16, page 290).
1.16 Manual, page 290

@muzimuzhi
Copy link
Member

With the help of git bisect, commit d2fbb2a (fixed SIMPLE scientific notation in tick arguments, 2019-05-30) is the first bad commit.

\documentclass{article}
\usepackage{pgfplots}
\pgfplotsset{compat=newest}

\begin{document}
\pgfplotscolorbardrawstandalone[
    colorbar as legend,
    colorbar style={
        ticklabel style={font=\tiny},
    },
    colormap access=const,
    colormap={CM}{
        of colormap={
            viridis,
            target pos={
                0,200,300,350,375,
                400,700,800,850,1000
            },
            sample for=const,
        },
    },
]
\end{document}

@muzimuzhi
Copy link
Member

muzimuzhi commented Jul 29, 2023

This is caused by interactions between the new extended uniform-loop-list optimization for \pgfplotsforeachungrouped, fpu library, and \pgfplotsarrayselect.

If /pgf/fpu is on, \pgfplotsforeachungrouped \x in {<uniform list>} {<body>} will set \x as a number in fpu representation like 1Y2.0e0] (+2.0 * 10^0). With commit d2fbb2a, the form(s) of <uniform list> was extended from 0,1,...,5 to also recognize 0,...,5. This hurt the use of

\pgfplotsforeachungrouped \x in {0,...,\pgfplots@colorcount} {%
  \pgfplotsarrayselect{\x}\of\positions\to\pgfplotsretval
  % ...
}

in colorbar drawing, since \pgfplotsarrayselect\x\of\<list>\to\<return> requires that \x is an integer assignable to a TeX count.

% accumulate a list of formatted positions into this
% variable:
\def\pgfplots@loc@TMPc{}%
\pgfplotsforeachungrouped \x in {0,...,\pgfplots@colorcount} {%
\pgfplotsarrayselect{\x}\of\positions\to\pgfplotsretval
\edef\pgfplots@loc@TMPa{\noexpand\pgfmathprintnumber{\pgfplotsretval}}
\t@pgfplots@toka=\expandafter{\pgfplots@loc@TMPc}%
\t@pgfplots@tokb=\expandafter{\pgfplots@loc@TMPa}%
\edef\pgfplots@loc@TMPc{\the\t@pgfplots@toka \the\t@pgfplots@tokb,}%
}%

% Returns the #1th element of array #2 into macro #3
% Arguments:
% #1: a count 0,...,N-1 where N is the array size.
% You may specify a number of a count.
% #2: a array
% #3: a macro name
% Example:
% Element 0:
% \pgfplotsarrayselect0\of\foo\to\elem
% \elem
% Element \count1:
% \pgfplotsarrayselect\count1\of\foo\to\elem
\def\pgfplotsarrayselect#1\of#2\to#3{%
\c@pgfplotsarray@tmp=#1\relax
\expandafter\let\expandafter#3\csname\string#2@\the\c@pgfplotsarray@tmp\endcsname%
\ifx#3\relax
\pgfplotsthrow{no such element}{#1}{No such element: \string\pgfplotsarrayselect\the\c@pgfplotsarray@tmp\string\of{\string#2}}\pgfeov%
\fi
}

A quick and very specific workaround

diff --git a/tex/generic/pgfplots/pgfplots.code.tex b/tex/generic/pgfplots/pgfplots.code.tex
index 0b4486b..b72bfa6 100644
--- a/tex/generic/pgfplots/pgfplots.code.tex
+++ b/tex/generic/pgfplots/pgfplots.code.tex
@@ -1368,6 +1368,9 @@
 			% variable:
 			\def\pgfplots@loc@TMPc{}%
 			\pgfplotsforeachungrouped \x in {0,...,\pgfplots@colorcount} {%
+				% ensure \x is assignable to a TeX count,
+				% because \pgfplotsarrayselect will assign \x to a TeX count)
+				\pgfmathfloattoint{\x}\let\x=\pgfmathresult
 				\pgfplotsarrayselect{\x}\of\positions\to\pgfplotsretval
 				\edef\pgfplots@loc@TMPa{\noexpand\pgfmathprintnumber{\pgfplotsretval}}
 				\t@pgfplots@toka=\expandafter{\pgfplots@loc@TMPc}%

I believe a thorough solution is to adapt \pgfplotsforeachungrouped (more specifically, \pgfplotsforeachungroupeduniform@ and the ones it calls) to make it's optimization for uniform-loop-list work with fpu.

\documentclass{article}
\usepackage{pgfplots}
\pgfplotsset{compat=1.18}

\usepackage{xpatch}
\makeatletter
% patch /pgfplots/colorbar as legend/.style={...}
\expandafter\xpatchcmd\csname pgfk@/pgfplots/colorbar as legend/.@cmd\endcsname
  {%
    \pgfplotsarrayselect{\x}\of\positions\to\pgfplotsretval
  }{%
    \pgfmathfloattoint{\x}\let\x=\pgfmathresult
    \pgfplotsarrayselect{\x}\of\positions\to\pgfplotsretval
  }{}{\PatchFailed}
\expandafter\xpatchcmd\csname pgfk@/pgfplots/colorbar as legend/.@body\endcsname
  {%
    \pgfplotsarrayselect{\x}\of\positions\to\pgfplotsretval
  }{%
    \pgfmathfloattoint{\x}\let\x=\pgfmathresult
    \pgfplotsarrayselect{\x}\of\positions\to\pgfplotsretval
  }{}{\PatchFailed}
\makeatother

\begin{document}
\pgfplotscolorbardrawstandalone[
    colorbar as legend,
    colorbar style={
        ticklabel style={font=\tiny},
    },
    colormap access=const,
    colormap={CM}{
        of colormap={
            viridis,
            target pos={
                0,200,300,350,375,
                400,700,800,850,1000
            },
            sample for=const,
        },
    },
]

{
  \pgfset{fpu=false}
  \foreach \x in {1,...,5} {\x, }\par
  \pgfplotsforeachungrouped \x in {1,2,...,5} {\x, }\par
  \pgfplotsforeachungrouped \x in {1,...,5} {\x, }
  
  \pgfset{fpu=true}
  \foreach \x in {1,...,5} {\x, }\par
  \pgfplotsforeachungrouped \x in {1,2,...,5} {\x, }\par % \x in fpu forms
  \pgfplotsforeachungrouped \x in {1,...,5} {\x, }       % \x in fpu forms
}
\end{document}

image

@muzimuzhi
Copy link
Member

muzimuzhi commented Jul 30, 2023

If /pgf/fpu is on, \pgfplotsforeachungrouped \x in {<uniform list>} {<body>} will set \x as a number in fpu representation like 1Y2.0e0] (+2.0 * 10^0).
[...]
I believe a thorough solution is to adapt \pgfplotsforeachungrouped (more specifically, \pgfplotsforeachungroupeduniform@ and the ones it calls) to make it's optimization for uniform-loop-list work with fpu.

It seems this behavior (for <uniform list> with form a,b,...,n) was on purpose, as it was introduced 14 years ago, in commit 063bebf on 2008-12-06. Hence the diff shown in my previous comment is on the right direction.

Alternatively, add a guard at the beginning of \pgfplotsarrayselect? Not necessary. Most uses of \pgfplotsarrayselect don't need such a guard, and there's already a similar fp-to-int guard added in commit 4214bc0 (improved the 'patch table' feature, 2010-06-22).

\pgfmathfloatparsenumber{\pgfplots@current@vertexindex}%
\pgfmathfloattoint\pgfmathresult
\let\pgfplots@current@vertexindex=\pgfmathresult
%
\let\pgfplotsexceptionmsg=\pgfplotsplothandlermesh@patchtable@rowlistener@exceptionmsg
\pgfplotsarrayselect\pgfplots@current@vertexindex\of\pgfplots@verts\to\pgfplots@stored@pt

@muzimuzhi
Copy link
Member

Another option: The \pgfplotsforeachungrouped \x in {0,...,\pgfplots@colorcount} {<loop body>} is so simple that it can be replaced by \pgfutil@loop ... \pgfutil@repeat.

diff --git a/tex/generic/pgfplots/pgfplots.code.tex b/tex/generic/pgfplots/pgfplots.code.tex
index 0b4486b..ba213d0 100644
--- a/tex/generic/pgfplots/pgfplots.code.tex
+++ b/tex/generic/pgfplots/pgfplots.code.tex
@@ -1367,13 +1367,16 @@
 			% accumulate a list of formatted positions into this
 			% variable:
 			\def\pgfplots@loc@TMPc{}%
-			\pgfplotsforeachungrouped \x in {0,...,\pgfplots@colorcount} {%
+			\def\x{0}% or, is \c@pgf@counta safe here?
+			\pgfutil@loop
+			\unless\ifnum\x>\pgfplots@colorcount
 				\pgfplotsarrayselect{\x}\of\positions\to\pgfplotsretval
-				\edef\pgfplots@loc@TMPa{\noexpand\pgfmathprintnumber{\pgfplotsretval}}
+				\edef\pgfplots@loc@TMPa{\noexpand\pgfmathprintnumber{\pgfplotsretval}}%
 				\t@pgfplots@toka=\expandafter{\pgfplots@loc@TMPc}%
 				\t@pgfplots@tokb=\expandafter{\pgfplots@loc@TMPa}%
 				\edef\pgfplots@loc@TMPc{\the\t@pgfplots@toka \the\t@pgfplots@tokb,}%
-			}%
+				\edef\x{\the\numexpr\x+1}%
+			\pgfutil@repeat
 			\t@pgfplots@tokb=\expandafter{\pgfplots@loc@TMPc}%
 			\xdef\pgfplots@glob@TMPa{%
 				\noexpand\pgfplotsset{%

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

2 participants