5

Is there an easy way to shade a rectangle in tikz so that the color changes not from one to the other but several times in between. Something like a grayscale value changing with the position x like cos(x).

A linear variation instead of a sinusoidal one would be fine: I'd jsut like it to mimic an interference pattern.

4
  • 3
    Put a few shaded rectangles next to each other and that would save you a lot of trouble.
    – percusse
    Sep 18, 2014 at 13:25
  • This is what @percusse meant: \documentclass{article} \usepackage{tikz} \usetikzlibrary{shadings} \begin{document} \begin{tikzpicture} \shade[left color=gray!0,right color=gray!100] (0,0) rectangle (2,1); \shade[left color=gray!0,right color=gray!100] (2,0) rectangle (3,1); \shade[left color=gray!0,right color=gray!100] (3,0) rectangle (6,1); \shade[left color=gray!0,right color=gray!100] (6,0) rectangle (7,1); \end{tikzpicture} \end{document}
    – user11232
    Sep 18, 2014 at 14:04
  • I'm sorry to bother you but I just noticed that you have received answers to your questions but you still haven't accepted any of them. I kindly invite you to revisit your questions and, for each of them, to accept the answer that you consider best solved your problem by clicking the checkmark to its left. In case of doubt, please see How do you accept an answer?. Mar 6, 2015 at 14:08
  • Thanks for reminding me, I'm taking care of it right away. However, regarding this very question, I'm hesitant since I'm pretty satisfied with the answer I gave... Is it good policy to accept your own answer ?
    – wilk
    Mar 6, 2015 at 17:30

3 Answers 3

15

You could try shadings:

\documentclass[tikz,border=5]{standalone}
\begin{document}
\tikz[x=0.125cm,y=0.125cm]
  \foreach \i in {0,1,...,21}
    \path [left color=black, right color=white, shading angle={mod(\i,20)*180+90}]
    (\i*.9,0) rectangle ++(1,10);
\end{document}

enter image description here

Or fadings:

\documentclass[tikz,border=5]{standalone}
\usetikzlibrary{fadings}
\begin{tikzfadingfrompicture}[name=interference]
\foreach \i in {0,1,...,21}
    \path [left color=transparent!0, right color=transparent!100, shading angle={mod(\i,2)*180+90}]
    (\i*.9,0) rectangle ++(1,20);
\end{tikzfadingfrompicture}
\begin{document}
\foreach \i in {0,...,24}{
\begin{tikzpicture}
\clip (-5,-5) rectangle ++(10,10);
\path [fill=black, path fading=interference, fit fading=false, fading transform={rotate=\i*7.2}] (-10,-5]) rectangle ++(20,10);
\path [fill=black, path fading=interference, fit fading=false] (-10,-5) rectangle ++(20,10);
\end{tikzpicture}
}
\end{document}

enter image description here

In both cases using \i*.9 is to workaround some annoying white lines that appear between adjacent shadings which may (or may not) be viewer artifacts.

To change the phase of the interference pattern is tricky to do with shading but with fadings it is fairly easy if the fading is specified over a larger area than the required path. Then the fading transform key can be used to shift the fading that is "seen through" the path.

In the following example the red rectangle illustrates the position of the fading relative to the area that is faded. Also, a line is drawn to fill in the white lines between adjacent shadings instead of the workaround described above to get rid of the white lines that appear between adjacent shadings.

\documentclass[tikz,border=5]{standalone}
\usetikzlibrary{fadings}
\begin{tikzfadingfrompicture}[name=interference]
\foreach \i in {0,1,...,19}{
  \path [left color=transparent!0, right color=transparent!100, shading angle={mod(\i,2)*180+90}]
  (\i,0) rectangle ++(1,10);
  \ifodd\i\else% Fill in gap
    \path [draw=transparent!0] (\i,0) -- ++(0,10);
  \fi
}
\end{tikzfadingfrompicture}
\begin{document}
\foreach \i in {-10,...,10,9,8,...,-9}{%
\begin{tikzpicture}
\path [draw=red, shift=(0:\i/2)] (-10,-5) rectangle (10,5);
\path [fill=black, path fading=interference, fit fading=false, fading transform={shift=(0:\i/2)}] (-5,-5) rectangle (5,5);
\useasboundingbox (-15,-5) rectangle (15,5);
\end{tikzpicture}%
}
\end{document}

enter image description here

Using fadings means you can also do cool stuff like this:

\documentclass[tikz,border=5]{standalone}
\usetikzlibrary{fadings}
\begin{tikzfadingfrompicture}[name=interference]
\foreach \i in {1,...,15}
    \foreach \j in {1,...,25}
       \path [line width=\j, draw=transparent!0,opacity=1/30]
    (0:\i) arc (0:180:\i);
\end{tikzfadingfrompicture}
\begin{document}
\begin{tikzpicture}
\clip (-5,-5) rectangle ++(10,10);
\path [fill=black, path fading=interference, fit fading=false] (-10,-5) rectangle ++(20,10);
\path [fill=black, path fading=interference, fit fading=false, fading transform={rotate=45}] (-10,-5) rectangle ++(20,10);
\end{tikzpicture}

\end{document}

enter image description here

2
  • I've been trying to change shift the whole pattern by an arbitraty amount, by changing (\i*.9,0) to ($\i*(.9,0)+0.9*(0.3,0)$) without any success: the absolute position of the path doesn't matter so it's always gonna start on a black or white line. Any idea how to make the shading start partway between black and white ? Ideally I'd like to be able to choose the value and the sign of the gradient of the transparency of the (0,0) point in the final image.
    – wilk
    Sep 21, 2014 at 15:01
  • @wilk I've updated the answer to illustrate how this can be done with fadings. A similar technique could also be done with shadings and also JLDiaz's answer but the clipping of the required shape would have to be done manually. Sep 21, 2014 at 15:46
5

You can draw yourself the shade pattern, by filling lots of thin rectangles, each one with a solid gray computed via a sin() function:

\usetikzlibrary{calc}

\begin{tikzpicture}
\def\detail{300}
\pgfmathsetmacro{\width}{8/\detail}
\foreach \i in {0,...,\detail} {
  \pgfmathsetmacro{\shade}{45+45*sin(\i*10)}
  \fill[fill=black!\shade!white] (\i*\width,0) rectangle +(\width, 2);
}

\end{tikzpicture}  

Result

Update

Addressing Steve's question in a comment, here is a new version of the code, more customizable and with better variable names:

\usetikzlibrary{calc}

\begin{tikzpicture}

% ---- Customizable section
\def\numsamples{200}   % The greater, the thinner each sample
\def\numcycles{8}      % Number of "white bars" in the result
\def\totalwidth{10}    % Width (in cm) of the resulting box
% ---- End of customizable section

\pgfmathsetmacro{\samplewidth}{\totalwidth/\numsamples}
\pgfmathsetmacro{\frequency}{360/\numsamples*\numcycles}
\foreach \i in {0,...,\numsamples} {
  \pgfmathsetmacro{\shade}{50+50*sin(\i*\frequency)}
  \fill[draw=none, fill=black!\shade!white] (\i*\samplewidth,0) rectangle +(\samplewidth*1.1, 2);
}
\end{tikzpicture} 

The above code produces the following "low res" result:

Low resolution

Increasing \numsamples to 500, you get a better result:

High resoultion

2
  • That's awesome. Would there be a way to increase the samples such that the transition is a bit more gradual? I increased \detail but it made them tiny. Thanks. Sep 19, 2014 at 2:40
  • @SteveHatcher Not sure of what you meant, but I posted an update which I hope addresses your question.
    – JLDiaz
    Sep 19, 2014 at 7:30
3

Actually, using pgfplots and surface plots from gnuplot turned out to be more flexible.

Here is an exemple of the interference pattern of 7 point sources with the following parameters * \Lambda is the wavelength * \DistanceSources is the constant distance between the sources * the amplitude can be chosen with the function "amorti" but will be constant within a radius of \Cutoff from the source * \RetardIIvsI is the constant phase shift between two adjacent sources * \Date is the constant phase added to all sources

\documentclass{standalone}
\usepackage{tikz,pgfplots}
\begin{document}
\begin{tikzpicture}[]
   \pgfmathsetmacro{\xmin}{-}6
   \pgfmathsetmacro{\ymin}{-6}
   \pgfmathsetmacro{\xmax}{6}
   \pgfmathsetmacro{\ymax}{6}
   \pgfmathsetmacro{\Lambda}{.5}
   \pgfmathsetmacro{\DistanceSources}{\Lambda/2}
   \pgfmathsetmacro{\CentreI}{-4*\DistanceSources}
   \pgfmathsetmacro{\CentreII}{\CentreI+\DistanceSources}
   \pgfmathsetmacro{\CentreIII}{\CentreII+\DistanceSources}
   \pgfmathsetmacro{\CentreIV}{\CentreIII+\DistanceSources}
   \pgfmathsetmacro{\CentreV}{\CentreIV+\DistanceSources}
   \pgfmathsetmacro{\CentreVI}{\CentreV+\DistanceSources}
   \pgfmathsetmacro{\CentreVII}{\CentreVI+\DistanceSources}
   \pgfmathsetmacro{\Cutoff}{\Lambda/10}
   \pgfmathsetmacro{\RetardIIvsI}{pi/2}
   \pgfmathsetmacro{\Date}{0}

   \begin{axis}[colormap/blackwhite,
      view ={0}{90},
      xlabel = $x$,
      ylabel = $y$,
      extra x ticks = ,
      extra x tick labels = ,
      extra y ticks = ,
      extra y tick labels = ,
      xmin = \xmin,
      ymin = \ymin,
      xmax = \xmax,
      ymax = \ymax,
      domain = \xmin:\xmax,
      samples = 50
      ]
      \addplot3[surf,shader=interp,raw gnuplot] gnuplot {
          set isosamples 50,50;
          amorti (centre,xy,y,cutoff)= 1;
          interf(centre,date,x,y,cutoff,lambda,retard) =%
          amorti(centre,x,y,cutoff)%
          * cos(date-2*pi*sqrt((x-centre)**2 + y**2)/lambda-retard);
          splot [x=\xmin:\xmax] [y=\ymin:\ymax] 1./7.*(%
             interf(\CentreI,\Date,x,y,\Cutoff,\Lambda,0)%
           + interf(\CentreII,\Date,x,y,\Cutoff,\Lambda,\RetardIIvsI)%
           + interf(\CentreIII,\Date,x,y,\Cutoff,\Lambda,2*\RetardIIvsI)%
           + interf(\CentreIV,\Date,x,y,\Cutoff,\Lambda,3*\RetardIIvsI)%
           + interf(\CentreV,\Date,x,y,\Cutoff,\Lambda,4*\RetardIIvsI)%
           + interf(\CentreVI,\Date,x,y,\Cutoff,\Lambda,5*\RetardIIvsI)%
           + interf(\CentreVII,\Date,x,y,\Cutoff,\Lambda,6*\RetardIIvsI)%
           ) 
          };
      \end{axis}
  \end{tikzpicture}
 \end{document}

enter image description here

You must log in to answer this question.

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