I am trying to diagram a light path through multiple homogeneous media if the velocity were a function of the height below initial height. This would consist of a path made of straight line segments.

The way I intended on doing this was:

  1. Define a starting coordinate, (A)

  2. Begin the foreach loop

  3. Find the next coordinate, (B), according to Snell's law and kinematics.(Shown below, also where is MathJax?)

  4. Draw path between (A) and (B). Then I would define (A) to have (B)'s value

  5. Continue the loop until it ends

Snell's Law, sin(theta)/v = constant; v = sqrt(h)

Where h is the vertical distance from the starting point and theta is measured from the vertical so I use cos and acos in the calculation since the value \qq is measured off of horizontal.

The following is what I have, but it does not appear to update the value of \qq as it only generates a series of collinear arrows, when they should start to get less steep. I've considered using foreach with evaluate but the first angle is independent of the height and as a consequence, I don't think that will work.



\begin{tikzpicture}[scale=4, >=latex]

\def\len{2} % box height

\def\hstep{0.1} % vert spacing between fluid interfaces
\def\qStart{45} % starting incidence angle

% init iteration variables to initial
\coordinate  (A) at (0, \len) node {A};

% start looping
\foreach \height in {0.1, 0.2, ..., 1.1}{
  % define (B) relative to (A) at some angle at fixed distance (\hstep*\len) below
  \path (A) +(-\qq : {\hstep*\len/sin(\qq)}) coordinate (B); 
  % generate line between the two (for debug, use arrowheads to identify where points are)
  \draw[->] (A) -- (B);
  % update (A) to next point
  \coordinate (A) at (B);
  % recalculate \q according to snell's law using pgf's math functions and \pfgmathsetmacro for real values
  \pgfmathsetmacro{\qq}{ {acos( 
  sqrt( 1+\hstep/\height )*cos(\qq) )} }


  • You need to make the definition global before exiting the loop. (Make sure you aren't using a macro name which already has a definition in LaTeX or your document, as this is for keeps.) For example, \global\let\qqkeep\qq or similar.
    – cfr
    Commented Sep 20, 2017 at 23:52
  • However, there's something wrong with the calculation as it fails trying to divide by zero.
    – cfr
    Commented Sep 21, 2017 at 0:14
  • @cfr thanks for that. Why doesn't it work if I just declare a nonglobal before the loop? Figured the div by 0 out, I was just doing bad math. Commented Sep 21, 2017 at 4:24
  • 1
    tikz ensures that everything inside a \foreach loop is local. This is by design as it makes it harder for tikz xode to break other things. Once you have fixed the mathematics, just replace the \pgfmathsetmacro command with \pgfmathparse{acos( sqrt( 1+\hstep/\height )*cos(\qq) )} }\xdef\qq{\pgfmathresult
    – user30471
    Commented Sep 21, 2017 at 6:01
  • Declaring the non-global before the loop makes sure it is defined, so you won't get an error. But any changes you make inside the loop are local to that particular iteration, so they won't be remembered from one iteration to the next.
    – cfr
    Commented Sep 21, 2017 at 11:47

1 Answer 1


The math library provides a for-loop which doesn't scope each iteration allowing values retain their values between iterations.

Not sure if this is the required output, and I think the cos(\qq) should be cos(\qq)^2 (at least this enables compilation without errors).

  \len = 2;
  \hStep = 0.1;
  \qStart = 45;
  \qq = \qStart;
    \coordinate (A) at (0, \len);
  for \h in {0.1, 0.2, ..., 1.1}{%
      \path (A) ++ (-\qq : {\hStep * \len / (sin(\qq) + 1)}) coordinate (B);
      \draw[->] (A) -- (B);
      \coordinate (A) at (B); 
    \qq = acos(sqrt( 1 + \hStep / \h ) * cos(\qq)^2 );

enter image description here

You must log in to answer this question.

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