1

I'm trying to plot an image, which is basically a matrix of intensity values. I want the colour scheme to range from black for the lowest to white for the highest value, with a defined colour in the middle. (I based my approach on this answer to a similar question.)

Here is a MWE:

\documentclass[tikz]{standalone}
\begin{document}
    \begin{tikzpicture}[scale=1]
        \foreach \y [count=\n] in { %this is normally a VERY large matrix
            {0,50}, 
            {75,100},
} {\foreach \x [count=\m] in \y {\fill[white!\x!red!\x!black] (1*\n,-1*\m) rectangle ++(1,1);}}
\end{tikzpicture}
\end{document}

The problem is that my values in absolute terms range from 0 to about 400 and in my current approach I need to normalise them to a range from 0 to 100. This technically works, however, this means that I loose 3/4 of my colour information. Unfortunately, the data is very sensitive and I am now looking for a way to use a larger range.

My new approach is dividing the colour scheme into two parts, so that I can use at least values from 0 to 200. However, this does not seem to work, and I am not that proficient in using if-commands in LaTeX. (I am aware of this answer by Christian Feuersänger to the same question reference above, but suffice it to say that I do not fully understand it and therefore struggle with adapting it to my case.)

\documentclass[tikz]{standalone}
\usepackage{calc}
\usepackage{ifthen}
\begin{document}
    \begin{tikzpicture}[scale=1]
        \foreach \y [count=\n] in {  
            {0,100},
            {150,200},
        } 
    {\foreach \x [count=\m] in \y {
        \fill[\ifnum\x<100 {white!\x!red} \else {red!{\x/2}!black}\fi] (1*\n,-1*\m) rectangle ++(1,1);
    }}
    \end{tikzpicture}
\end{document}

Can someone tell me, what my mistake is or maybe even how to solve this more efficiently?

Bernard
  • 271,350
Markus G.
  • 2,735

1 Answers1

1

Not sure if you want to achieve this exact result, but the code with corrections is this one:

\documentclass[tikz]{standalone}
\usetikzlibrary{calc}
\usepackage{xcolor}
\usepackage{ifthen}
\begin{document}
    \begin{tikzpicture}[scale=1]
        \foreach \y [count=\n] in {  
            {0,100},{150,200}}
    {\foreach \x [count=\m] in \y {
    \pgfmathtruncatemacro\myx{\x/2}%
    \xdef\myColor{\ifnum\x<100 white!\x!red\else red!\myx!black\fi}%
        \fill[color={\myColor}] (1*\n,-1*\m) rectangle ++(1,1);
    }}
    \end{tikzpicture}
\end{document}

Edit:

In order to not lose the range (0--400) you can add some green in the red color (half green.. to make it yellowish) and keep the RGB definition (0--255)... So, your range will be 2\times 255 = [0--510]):

\documentclass[tikz]{standalone}
\usetikzlibrary{calc}
\usepackage{xcolor}
\usepackage{ifthen}
\begin{document}
    \begin{tikzpicture}[scale=1]
        \foreach \y [count=\n] in {  
            {0,100, 200},{150,200,400}} 
    {\foreach \x [count=\m] in \y {
    \pgfmathtruncatemacro\myx{\ifnum\x>255{\x/2}\else{\x}\fi}%
    \pgfmathtruncatemacro\myy{\ifnum\x>255 127\else 0\fi}%
    \definecolor{myColor\m}{RGB}{\myx,\myy,0}%
        \fill[color={myColor\m}] (1*\n,-1*\m) rectangle ++(1,1);
    }}
    \end{tikzpicture}
\end{document}

Edit2:

For more "gradient" approach use the functions:

\pgfmathtruncatemacro\myx{\ifnum\x>255{255}\else{\x}\fi}%
\pgfmathtruncatemacro\myy{\ifnum\x>255{\x-255}\else 0\fi}%

enter image description here

koleygr
  • 20,105
  • 1
    For use case specific reasons, I need to use single hue colour schemes, and your first answer works like a charm. The only thing I needed to change was the colour definition to ´´´red!\x!white\else black!\myx!red´´´ in order for the scheme to go from white to red to black, but that was my own mistake. – Markus G. Feb 20 '21 at 12:12
  • 1
    Also, I am not sure, were you get the 240 from. Isn't RGB typically 0--255 and HSB 0--240? Not that it impacts the basic funcationality, I'm just wondering. – Markus G. Feb 20 '21 at 14:01
  • Yes ... You are right @MarkusG. ... My mistake ... I don't remember where I had such a number like 240 in mind ... I will correct that (Thanks!!!!) – koleygr Feb 20 '21 at 14:21
  • I actually realised, there is another tiny logic flaw in your initial code example. If you use x/2 for the second range, the values will range from 100/2 to 200/2, i.e. 50 to 100. To use the entire range, instead of dividing by 2, it makes more sense to subtract 100: \x-100 – Markus G. Feb 24 '21 at 12:47