1

I am trying to get a gradient to work with 3 distinct positions by using \pgfdeclarehorizontalshading. I have read the docs here and it seems rather straightforward that the different colors should be positioned at the exact widths indicated. Unfortunately, in practice I do not get those results. I have also adapted my code from another answer here, but for some reason I cannot extrapolate from this answer for my needs. Here is a MWE and the relevant code pasted below:

MWE on Overleaf

\documentclass[12pt]{article}
\usepackage{tikz}
\usepackage{calc}
\newlength{\mymargin}
\setlength{\mymargin}{0.125in}
\usepackage
[
  paperwidth=11in,paperheight=17in,layoutwidth=10.75in,layoutheight=16.75in,
  left=\mymargin,right=\mymargin,top=\mymargin,bottom=\mymargin,
  bindingoffset=0in,landscape=true,marginparwidth=0in,marginparsep=0in
]{geometry}

\pgfdeclarehorizontalshading{grad}{.5in}{
    color(0cm)=(white);
    color(2.5cm)=(white);
    color(5cm)=(pink);
    color(8cm)=(black);
    color(10cm)=(black)
}

\begin{document}
    \begin{tikzpicture}[remember picture,overlay,every node/.style={inner sep=0,outer sep=0}]
        \fill[shading=grad,shading angle=0] (current page.north west) ++ (1in,-3in) rectangle ++ (15in,-3in);
    \end{tikzpicture}
\end{document}

Result: enter image description here

The reasons why I am not satisfied with this are:

  • First, it does not go from pure white to pure pink to pure black, at least not in the portion that is visible, and changing the size of this rectangle does not seem to do the trick.
  • My understanding is that the positions of the different colors act as percentages, since from what I read the 0 is the left and whatever the last value is (as they are sorted ascendingly) is the right-most value. However, this does not seem to be the case as when I change units from cm to in the actual gradient changes, which I do not understand.
  • Third, I am not certain as to why, in the answer referenced above, the same color is repeated: is that to get a pure color for a given space? because if so (which seems to make sense) then I cannot seem to reproduce this feature in my example, as is plain to see...
  • Finally, I see that by adjusting the height (second parameter of the \pgfdeclarehorizontalshading command), I can get the gradient to fill the whole rectangle, but I am still surprised as to why the rendering is the way it is, with a black band at the bottom?

My final goal is thus to understand exactly how I can define N=3 or more colors such that a ruler, say in cm, spaced into those N segments has the peak color at those given limits exactly. Thanks!

[EDIT 1] It has been suggested by @Ulrike that the units should be in bp and sum to 100bp, which might be the case. However, this does not, on its own, seem to solve the problem. Here is an example:

\pgfdeclarehorizontalshading{grad}{100bp}{
    color(0bp)=(white);
    color(25bp)=(white);
    color(49bp)=(pink);
    color(50bp)=(red);
    color(51bp)=(pink);
    color(80bp)=(black);
    color(100bp)=(black)
}
    \begin{tikzpicture}[remember picture,overlay,every node/.style={inner sep=0,outer sep=0}]
        \fill[shading=grad,shading angle=0] (current page.north west) ++ (2cm,-4cm) rectangle ++ (10cm,-1cm);
    \end{tikzpicture}

This produces the following screenshot, in which the red zone is not centered: Screenshot of a shading using bp units

4
  • Do you want to achieve some kind of colormap?
    – Tom
    Jun 4, 2022 at 23:59
  • @Tom No, I would like to have something like: color1 for the first 10%, gradient from color1 to color2 for the next 10%, color2 for the following 10%, gradient from color2 to color3 for the following 10%, etc. Except that, instead of percentages, I wish to give exact positions (in inches) within the paper.
    – sg1234
    Jun 5, 2022 at 0:52
  • From my understanding, Whatever how long you will draw., when you declare them you just use the total 2cm length. So if you want 10% for each segments, you just do color(0cm)=white; color(0.2cm)=white; color(0.4cm)=pink; color(0.4cm)=pink etc... until 2cm.
    – Tom
    Jun 5, 2022 at 1:19
  • @Tom that is what I thought as well, but as you can tell from the screenshot that does not seem to be the case in my example?
    – sg1234
    Jun 5, 2022 at 1:27

2 Answers 2

1

you should declare shadings with a total size of 100bp (at least the width for horizontal shadings). It is not very explizit in the documentation, but the description of \pgfshadepath says that Now, the shading name should be a shading whose width and height are 100 bp and the calculation how to place a shading into a path is based on this assumption.

If one changes your width values to bp

\documentclass[12pt]{article}
\usepackage{tikz}
\usepackage{calc}
\newlength{\mymargin}
\setlength{\mymargin}{0.125in}
\usepackage
[
  paperwidth=11in,paperheight=17in,layoutwidth=10.75in,layoutheight=16.75in,
  left=\mymargin,right=\mymargin,top=\mymargin,bottom=\mymargin,
  bindingoffset=0in,landscape=true,marginparwidth=0in,marginparsep=0in
]{geometry}

\pgfdeclarehorizontalshading{grad}{100bp}{
    color(0cm)=(white);
    color(25bp)=(white);
    color(50bp)=(pink);
    color(80bp)=(black);
    color(100bp)=(black)
}

\begin{document}
    \begin{tikzpicture}[remember picture,overlay,every node/.style={inner sep=0,outer sep=0}]
        \fill[shading=grad,shading angle=0] (current page.north west) ++ (1in,-3in) rectangle ++ (15in,-3in);
    \end{tikzpicture}
\end{document}

you get

enter image description here

The rules how a shading fills a path means that you should also be careful not to add "unfilled" bits to the path. Compare this two outputs

\documentclass[12pt]{article}
\usepackage{tikz}
\usetikzlibrary{backgrounds}
\usepackage{calc}
\newlength{\mymargin}
\setlength{\mymargin}{0.125in}
\usepackage
[
  paperwidth=11in,paperheight=17in,layoutwidth=10.75in,layoutheight=16.75in,
  left=\mymargin,right=\mymargin,top=\mymargin,bottom=\mymargin,
  bindingoffset=0in,landscape=true,marginparwidth=0in,marginparsep=0in
]{geometry}

\pgfdeclarehorizontalshading{grad}{100bp}{
    color(0bp)=(white);
    color(25bp)=(white);
    color(49bp)=(pink);
    color(50bp)=(red);
    color(51bp)=(pink);
    color(80bp)=(black);
    color(100bp)=(black)
}

\begin{document}
    \begin{tikzpicture}[show background rectangle,remember picture,every node/.style={inner sep=0,outer sep=0}]
        \fill[shading=grad,shading angle=0] (current page.north west) ++ (1in,-3in) rectangle ++ (15in,-3in);
    \end{tikzpicture}
    
    \begin{tikzpicture}[show background rectangle,remember picture,every node/.style={inner sep=0,outer sep=0}]
        \fill[shading=grad,shading angle=0] ([xshift=1in,yshift=-6in]current page.north west) rectangle ++ (15in,-3in);
    \end{tikzpicture}
\end{document}

enter image description here

3
  • Unfortunately, although it looks like it works, it does not produce what I expect (and I think what I expect is fair :) ). This is well illustrated if you try to show the 50% zone by adding " color(49bp)=(pink);color(50bp)=(red);color(51bp)=(pink); " instead of the portion for the pink color; in this case, the red central zone will not be centered. I will update my question to be more precise.
    – sg1234
    Jun 6, 2022 at 22:22
  • @sg1234 I added an edit. Jun 7, 2022 at 7:29
  • I can confirm that this is now correct: the answer involves 3 aspects, the first of which I knew about and the next two which you solved: 1. the visible boundaries (without rotation) are [25%,75%], 2. use 100bp as the full scale (and us bp as intermediary units), and 3. use xshift/yshift (rather than the + or ++ "moveTo" syntax) to change coordinates.
    – sg1234
    Jun 7, 2022 at 14:56
1

Something like this:

\documentclass[12pt]{article}
\usepackage{tikz}
\usepackage{calc}
\newlength{\mymargin}
\setlength{\mymargin}{0.125in}
\usepackage
[
  paperwidth=11in,paperheight=17in,layoutwidth=10.75in,layoutheight=16.75in,
  left=\mymargin,right=\mymargin,top=\mymargin,bottom=\mymargin,
  bindingoffset=0in,landscape=true,marginparwidth=0in,marginparsep=0in
]{geometry}

\pgfdeclarehorizontalshading{grad1}{1in}{
    color(0cm)=(white);
    color(0.1cm)=(white);
    color(0.2cm)=(red);
    color(0.3cm)=(red);
    color(0.4cm)=(orange);
    color(0.5cm)=(orange);
    color(0.6cm)=(yellow);
    color(0.7cm)=(yellow);
    color(0.8cm)=(lime);
    color(0.9cm)=(lime);
    color(1cm)=(green);
    color(1.1cm)=(green);
    color(1.2cm)=(blue);
    color(1.3cm)=(blue);
    color(1.4cm)=(purple);
    color(1.5cm)=(purple);
    color(1.6cm)=(violet);
    color(1.7cm)=(violet);
    color(1.8cm)=(black);
    color(1.9cm)=(black);
    color(2cm)=(black)
}
\begin{document}
    \begin{tikzpicture}[remember picture,overlay,every node/.style={inner sep=0,outer sep=0}]
    \fill[shading=grad1,shading angle=0] ([shift={(3in,-5in)}]current page.north west) rectangle +(10in,-1in);
    \end{tikzpicture}
\end{document

enter image description here

To achieve what said in comment to control the each length of the segments you could do this manfully. However, it looks not too good in the transition of each color. I don't have better solution right now.

\documentclass[12pt]{article}
\usepackage{tikz}
\usepackage{calc}
\newlength{\mymargin}
\setlength{\mymargin}{0.125in}
\usepackage
[
  paperwidth=11in,paperheight=17in,layoutwidth=10.75in,layoutheight=16.75in,
  left=\mymargin,right=\mymargin,top=\mymargin,bottom=\mymargin,
  bindingoffset=0in,landscape=true,marginparwidth=0in,marginparsep=0in
]{geometry}

\pgfdeclarehorizontalshading{grad1}{1in}{
    color(0cm)=(pink);
    color(2cm)=(red)
}
\pgfdeclarehorizontalshading{grad2}{1in}{
    color(0cm)=(red);
    color(2cm)=(green)
}
\pgfdeclarehorizontalshading{grad3}{1in}{
    color(0cm)=(green);
    color(2cm)=(lime)
}
\pgfdeclarehorizontalshading{grad4}{1in}{
    color(0cm)=(lime);
    color(2cm)=(yellow)
}
\pgfdeclarehorizontalshading{grad5}{1in}{
    color(0cm)=(yellow);
    color(2cm)=(blue)
}

\begin{document}
\begin{tikzpicture}[remember picture,overlay,every node/.style={inner sep=0,outer sep=0}]
    \fill[pink] ([shift={(1in,-2.5in)}]current page.north west) rectangle +(1in,1in);
    \shade [shading=grad1,shading angle=0] ([shift={(2in,-2.5in)}]current page.north west) rectangle +(1in,1in);
    
    \fill[red] ([shift={(3in,-2.5in)}]current page.north west) rectangle +(1in,1in);
    \shade [shading=grad2,shading angle=0] ([shift={(4in,-2.5in)}]current page.north west) rectangle +(1in,1in);
    
    \fill[green] ([shift={(5in,-2.5in)}]current page.north west) rectangle +(1in,1in);
    \shade [shading=grad3,shading angle=0] ([shift={(6in,-2.5in)}]current page.north west) rectangle +(1in,1in);
    
    \fill [lime] ([shift={(7in,-2.5in)}]current page.north west) rectangle +(1in,1in);
    \shade [shading=grad4,shading angle=0] ([shift={(8in,-2.5in)}]current page.north west) rectangle +(1in,1in);
    
    \fill [yellow] ([shift={(9in,-2.5in)}]current page.north west) rectangle +(1in,1in);
    \shade [shading=grad5,shading angle=0] ([shift={(10in,-2.5in)}]current page.north west) rectangle +(1in,1in);
    
    \fill [blue] ([shift={(11in,-2.5in)}]current page.north west) rectangle +(1in,1in);
    
    \foreach \i in {0,1,...,11}
    \draw ([shift={(1in+\i in,-2.5in)}]current page.north west) -- ([shift={(1in+\i in,-3in)}]current page.north west) node[below=4pt,font=\Huge]{\i in};
\end{tikzpicture}
\end{document}

enter image description here

3
  • your example works as is, but again I do not understand how to modify it. Please see a version I just did over here: overleaf.com/project/6299093f4d5cdfd8f06140b9 as you can see, the borders are black, and I cannot understand how to control the portion shaded vs the width of the rectangle: is it a percentage? a unit?
    – sg1234
    Jun 5, 2022 at 1:53
  • It said I don’t permission for that link.
    – Tom
    Jun 5, 2022 at 1:57
  • Sorry, you must use the link in my question not the one in my previous comment: overleaf.com/read/mnxnkkvsnnkk
    – sg1234
    Jun 5, 2022 at 2:49

You must log in to answer this question.

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