Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

How would one go about drawing light / laser illumination rays using tikz? I would like to draw the schematic of an optical microscope illumination path. Examples of kinds of rays I would like to produce are:

  • A linear gradient from out to in:

olympus example

  • A cone shaped beam with a radial gradient:

olympus example 2

  • The kind of laser illumination I am after is as follows:

olympus laser

Note: I am mainly interested in just the 'rays' in each picture. Not the optical elements or rest of the microscope setup. But they were the only pictures I could find that illustrate the look I am trying to achieve. I want to append them to my highly technical (lol) schematic so far:

\documentclass{article}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}
\draw (-0.8,-0.2) -- (0.8,-0.2) -- (1.2,-0.6) -- (1.2,-1.8) -- (-1.2,-1.8) -- (-1.2,-0.6)--(-0.8,-0.2);
\draw  (-1.8,0.8) rectangle (1.8,0.4);
\draw (-1.2,1.4) -- (-1.6,2) -- (-1.8,3) -- (-1.8,3.8) -- (1.8,3.8) -- (1.8,3) -- (1.6,2) -- (1.2,1.4)--(-1.2,1.4);
\end{tikzpicture}
\end{document}

template

EDIT: Thanks to the responses I have gotten 90% of what I am after:

\documentclass[tikz]{standalone}
\usepgfmodule{nonlineartransformations}
\tikzset{lasernode/.style={
    left color=red,
    right color=red,
    middle color = white
    }
}
\tikzset{lasernode2/.style={
    left color=white,
    right color=white,
    middle color = red
    }
}
\begin{document}
\begin{tikzpicture}
\draw[lasernode] (-3.8,9.4) rectangle (-3.2,6.4);
\draw[lasernode] (-6.8,9) -- (-6.8,7.8) -- (-6.2,6.8) -- (-5.6,7.8) -- (-5.6,9) -- (-6.8,9);
\draw[lasernode2] (-5.4,9) -- (-5.4,7.8) -- (-4.8,6.8) -- (-4.2,7.8) -- (-4.2,9) -- (-5.4,9);
\end{tikzpicture}
\end{document}

laser example

What I want is for the shaded beam to stay perpendicular to the direction of propagation. Example of what I am after made with Photoshop is, but obviously with tikz correctly shading...:

laser

Thanks all

EDIT 2

Thanks to suggestions I am getting very close:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{fadings}
\tikzfading[name=fade right, left color=transparent!100, right color=transparent!10, middle color=transparent!100]
\tikzfading[name=fade rect, left color=transparent!100, right color=transparent!70, middle color=transparent!100]
\begin{document}
\begin{tikzpicture}[fill opacity=0.9,thick, line cap=round,line join=round]
    \fill[red,path fading=fade right, fading angle=-30] (-0.2,1.8) -- (-1.6,3.4) -- (1.2,3.4) -- cycle;
    \fill[red,path fading=fade right, fading angle=210] (-0.2,1.8) -- (-1.6,3.4) -- (1.2,3.4) -- cycle;
    \draw (-1.6,3.4) -- (-0.2,1.8) -- (1.2,3.4);

    \fill [red,path fading=fade rect] (-0.2,3.4) rectangle (1.2,6.4);
    \fill [red,path fading=fade rect, fading angle=180]  (-1.6,3.4) rectangle (-0.2,6.4);
    \draw (-1.6,3.4) -- (-1.6,6.4);
    \draw (1.2,3.4) -- (1.2,6.4);

%    \fill  [blue,path fading=south](-0.8,7.8) rectangle (0.4,-3.4);    

\end{tikzpicture}
\end{document}

enter image description here

I am very happy with it but as you can see my implementation is quite messy. Basically after drawing a triangle I play around with rotations until it matches how I want, and then I draw another triangle behind it with the same rotation mirror vertically. The final problem is matching the horizontal beam up with the rotated one. Right now I am just doing trial and error. Thanks for all getting me onto the right track. If there is a solution to joining them together then I will be satisfied 100%.

share|improve this question
    
1  
Try tikzedt from www.tikzedt.org –  sandu May 30 at 5:44
2  
1  
I think you should start with this one: texample.net/tikz/examples/focused-ion-beam-system –  zeroth May 30 at 9:30
1  

2 Answers 2

up vote 17 down vote accepted
+50

Here is a solution which "fakes" the shading by the method of repeatdly drawing the same shape, thinner and thinner and with a color more and more white:

\usetikzlibrary{calc}

\begin{tikzpicture}[scale=0.3]

\coordinate (top left) at (-1,5);
\coordinate (top right) at (1,5);
\coordinate (tip)   at (0,0);
\coordinate (bottom left) at (-1,1.5);
\coordinate (bottom right) at (1,1.5);

\colorlet{beamcolor}{red!80!orange!70}

\foreach \i in {0,0.01,...,1} {
  \pgfmathsetmacro{\shade}{sqrt(sqrt(\i))*100}
  \fill[white!\shade!beamcolor] 
        ($(top left)!\i!(top left-|tip)$)
     -- ($(bottom left)!\i!(bottom left-|tip)$) 
     -- (tip)
     -- ($(bottom right)!\i!(bottom right-|tip)$)
     -- ($(top right)!\i!(top right-|tip)$)
     -- cycle;
}
\end{tikzpicture}

The loop iterates 100 times, from 0 to 1 at steps of 0.01. The shape is drawn "full width" in the first iteration, with color beamcolor, and each iteration of the loop draws it a little thinner (in a factor of 0.01 each time) and with a color which tends to white slowly.

The result is:

Result

Of course the method is cpu-intensive. If you increase the step from 0.01 to 0.1 for example, then the loop is repeated only ten times, which is faster, but produces a poor result (unless your beam is very thin and the "low resolution" cannot be seen).

Update

Using the same idea, but instead of computing the shape for each iteration, this time we draw the same shape, only x-scaled thinner and thinner (this time I used 0.03 as step)

\usetikzlibrary{calc}

\begin{tikzpicture}
\colorlet{beamcolor}{red!80!orange!70}

\foreach \i in {1, 0.97, ..., 0} {
  \pgfmathsetmacro{\shade}{\i*\i*100}
  \fill[beamcolor!\shade!white, x=\i cm] 
        (-1,5) -- (-1,1.5) -- (0,0) -- (1,1.5) -- (1,5) -- cycle;
}
\end{tikzpicture}

Result2

Update 2. Explanations and more examples

As requested by the OP in a comment, here is a detailed explanation of the code for the last solution.

  • \colorlet serves obviously to define a new color name, allowing to specify the color as a xcolor mixing, which uses the syntax color1!%!color2, being % the desired percentage of color1. The resulting color can be mixed again by appending another !%, etc. If color2 is omitted, it is assumed to be white. So, in our code, beamcolor will be a mix of red and orange (80% red and thus 20% orange), and then this color is mixed again with white (70% of the color and thus 30% of white).

  • \foreach \i in {1, 0.97, ..., 0} declares a loop which uses \i as loop variable. This variable will start with value 1, then, at the next iteration it will be 0.97, and next iterations decrease \i in steps of 0.03. The step is automatically derived from the two first values specified in the braces. When \i reaches 0, the loop ends. In the following code \i is the horizontal scale of the beam, and the amount of beamcolor used to fill.

  • \pgfmathsetmacro{\shade}{\i*\i*100} computes the expression \i*\i*100 and stores the result in the macro named \shade. So, for the first iteration of the loop \shade will be equal to 100, in the next iteration it will be 0.97*0.97*100 which is 94.09 etc. This value is the % of beamcolor present in the shape we will draw later. So, in the first iteration the shape will be filled with a color 100% beamcolor. The next iteration it will be 94.09% beamcolor (and thus 5.91% white), etc.. In the last iteration it will be 0% beamcolor and thus 100% white.

  • \fill[beamcolor!\shade!white, x=\i cm] This will draw a geometric shape and fill it in a solid color (not a gradient!). The color will be a mixing between beamcolor and white, being \shade the amount (in %) of beamcolor. See previous paragraph. The option x=\i cm assigns the value \i cm to vector x, which determines the horizontal scale of the drawing. So, in the first iteration x=1cm, in the next one 'x=0.97cm` etc. This causes the shape drawn to be thinner in each iteration.

  • (-1,5) -- (-1,1.5) -- (0,0) -- (1,1.5) -- (1,5) -- cycle; This is the shape to be drawn. The values of the coordinates do not specify any unit, and thus tikz uses x as unit for the x-coordinate and y (1cm by default) as unit for the y-coordinate. As seen in previous paragraph, the value of x is different for each iteration of the loop.

The key to understand why this code produces the desired result is to realize that the shape being drawn has horizontal symmetry along y-axis. So, reducing x scale the new figure is still symmetric through the same axis.

The following figure in which the loop is repeated only 3 times, and we use very different colors for each filling, could make the process more clear:

\foreach \i/\color in {1/green, 0.75/red, 0.25/blue} {
  \fill[\color, x=\i cm] 
        (-1,5) -- (-1,1.5) -- (0,0) -- (1,1.5) -- (1,5) -- cycle;
}

simplified example

Now, some caveats.

The above code works under assumption than the shape is symmetric around y-axis. If you, for example, add 1 to all x-coordinates, this is not longer true, and thus the result is not what it is desired. However, this can be avoided by using xshift option instead of manipulating the coordinates of the shade. Also, the option rotate can be used to rotate the beam around its tip. If both xshift and rotate are to be used, the shifting should happen first.

For example:

\colorlet{beamcolor}{red!80!orange!70}

\foreach \i in {1, 0.97, ..., 0} {
  \pgfmathsetmacro{\shade}{\i*\i*100}
  \fill[beamcolor!\shade!white, x=\i cm, rotate=70] 
        (-1,5) -- (-1,1.5) -- (0,0) -- (1,1.5) -- (1,5) -- cycle;
}

\colorlet{beamcolor}{green!80!orange!70}

\foreach \i in {1, 0.97, ..., 0} {
  \pgfmathsetmacro{\shade}{\i*\i*100}
  \fill[beamcolor!\shade!white, x=\i cm,  xshift=1cm, rotate=-20] 
        (-1,5) -- (-1,1.5) -- (0,0) -- (1,1.5) -- (1,5) -- cycle;
}

\draw (0,0) -- (2,0);

Produces:

Result

Note also that this solution can be easily extended to other beam-like shapes:

\foreach \i in {1, 0.97, ..., 0} {
  \pgfmathsetmacro{\shade}{\i*\i*100}
  \fill[beamcolor!\shade!white, x=\i cm] 
        (-1,5) -- (-1,2.5) -- (-0.8, 1.5) -- (0,0) -- (0.8, 1.5) -- (1,2.5) -- (1,5) -- cycle;
}

Another shape

share|improve this answer
2  
These diagrams are so good I want to cry. Thanks! –  Steve Hatcher Jun 2 at 1:56
1  
Just for my own learning, could you please explain what each line of code does in the second solution? I cant understand how it knows to shade light then dark again. Thanks. Tikz is incredible. –  Steve Hatcher Jun 2 at 3:11
2  
@SteveHatcher See updated answer –  JLDiaz Jun 2 at 8:13
    
mate that's unreal, thanks for going to so much effort. I hope many users see and benefit from this. The level of professionalism achievable by a Tikz'er in the know is phenomenal. By the way I was not holding my bounty for 'ransom' when asking for the additional descriptions. I thought that when I accept the answer the bounty is transferred automatically - not that I had to click it. Once I can award it (5 hours) I will. Thanks! –  Steve Hatcher Jun 2 at 8:27
    
@SteveHatcher Don't worry about the bounty. I didn't provided the explanations for getting it, but in the hope of being useful to others, and to contribute to increase the tikz-savviness of the universe :-) –  JLDiaz Jun 2 at 8:32

We can implement @JLDiaz's clever "fake gradient" idea easily in Metapost as well.

  • Variables of type color are just 3d vectors, and we can interpolate from a to b using the square bracket syntax: p[a,b] returns a vector that is p of the way from a to b.

  • If we make the beams symmetrical about the x-axis we can use yscaled to control the width of the strip we are filling each time. Then we can capture them as picture variables with image() and move them about as needed.

Even with 1000 iterations it seems pretty speedy on my machine, but I can't see much difference on screen with more than 20 iterations.

prologues := 3;
outputtemplate := "%j%c.eps";

beginfig(1);

n := 20; % seems ok up to 1000s on my machine but hard to see difference above 20 on screen
r := 1.2; % controls gradient

path beam_outline, limb; 
limb = origin -- (20,10) --  (40,15)  -- (100, 20) -- (300, 20);
beam_outline = limb -- reverse (limb reflectedabout(left,right)) & cycle;

picture beam, another_beam;
beam = image( for i=n step -1 until 0:
  fill beam_outline yscaled (i/n) withcolor ((i/n)**r)[background,blue];
endfor);
another_beam = image( for i=n step -1 until 0:
  fill beam_outline yscaled (i/n) withcolor ((i/n)**r)[red, background];
endfor);

draw beam rotated 30;
draw another_beam rotated 150 shifted 10 left;

endfig;
end.

On the left we are shading from background to color, and on the right the other way round. Note that all I did was to swap the order of the color and the background in the brackets.

Red and blue beams

share|improve this answer
    
Hi, Thanks. This way of drawing them really is great! –  Steve Hatcher Jun 3 at 2:36

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.