4

I'm trying to output low-level tikz commands from another program, as a generic way to draw arbitrary lines (and points etc.). I am not familiar enough with the syntax to know where to look for good examples (and the manual is, well, a bit overwhelming at first).

In pseudo-code, I'm hoping to achieve the following:

% define some arrays of numbers and parameters
x = {x1, x2, x3, ..., xn}
y = {y1, y2, y3, ..., yn}
c = {c1, c2, c3, ..., cn}
w = {w1, w2, w3, ..., wn}

% set the start coordinate
current_point = (x(1), y(1))
% loop over all arrays to draw a path
for each (i = 1:n){
 draw [colour = c(i), line width = w(i)] current_point -- ++ (x(i), y(i)) coordinate current_point;
}

I think that would be a sufficiently general syntax, yet still readable. I am however stuck with the syntax to define multiple arrays and reuse them in a foreach loop. Here's my failed attempt (should show a sine wave),

\documentclass[tikz]{standalone}
\usepackage{tikz}

\begin{document}

\begin{tikzpicture}[x=1cm,y=0.4cm]
    \def\tabx{{0,0.06,0.13,0.19,0.25,0.32,0.38,0.44,0.51,0.57,0.63,0.7,0.76,0.83,0.89,0.95,1.02,1.08,1.14,1.21,1.27,1.33,1.4,1.46,1.52,1.59,1.65,1.71,1.78,1.84,1.9,1.97,2.03,2.09,2.16,2.22,2.28,2.35,2.41,2.48,2.54,2.6,2.67,2.73,2.79,2.86,2.92,2.98,3.05,3.11,3.17,3.24,3.3,3.36,3.43,3.49,3.55,3.62,3.68,3.74,3.81,3.87,3.93,4,4.06,4.13,4.19,4.25,4.32,4.38,4.44,4.51,4.57,4.63,4.7,4.76,4.82,4.89,4.95,5.01,5.08,5.14,5.2,5.27,5.33,5.39,5.46,5.52,5.59,5.65,5.71,5.78,5.84,5.9,5.97,6.03,6.09,6.16,6.22,6.28}};

\def\taby{{0,0.95,1.86,2.7,3.45,4.07,4.55,4.86,4.99,4.95,4.73,4.33,3.78,3.09,2.29,1.41,0.48,-0.48,-1.41,-2.29,-3.09,-3.78,-4.33,-4.73,-4.95,-4.99,-4.86,-4.55,-4.07,-3.45,-2.7,-1.86,-0.95,0,0.95,1.86,2.7,3.45,4.07,4.55,4.86,4.99,4.95,4.73,4.33,3.78,3.09,2.29,1.41,0.48,-0.48,-1.41,-2.29,-3.09,-3.78,-4.33,-4.73,-4.95,-4.99,-4.86,-4.55,-4.07,-3.45,-2.7,-1.86,-0.95,0,0.95,1.86,2.7,3.45,4.07,4.55,4.86,4.99,4.95,4.73,4.33,3.78,3.09,2.29,1.41,0.48,-0.48,-1.41,-2.29,-3.09,-3.78,-4.33,-4.73,-4.95,-4.99,-4.86,-4.55,-4.07,-3.45,-2.7,-1.86,-0.95,0}};
    \coordinate (current point) at (0,0);
    \foreach[count=\i, evaluate=\i as \y using \taby(\i), evaluate=\i as \lw using \i/10] \x in \tabx
    {
    \draw[red!\i,line width=\lw] (current point) -- ++ (\x,\y) coordinate (current point);
    }

\end{tikzpicture}
\end{document}

My preference would be to keep the array variables self-contained in the tex file, rather than a separate text file, but if there's a clear advantage to doing that it could also be an option.

Note: I am aware of pgfplots and other high-level libraries, but would rather avoid them here because I want to keep a lower-level control to add arbitrary elements, rather than relying on the design of pre-built plotting commands, symbols, etc.

Happy to be directed at an existing question or a specific section of the manual; I found quite a few related references but couldn't place all the pieces together.

  • Is tabx supposed to be \tabx? But it is just a single item, I think, as you've enclosed it in brackets. I don't think you can loop through an array directly like that. – cfr Feb 17 '17 at 23:57
3

You need something more like the following.

  • You need to pass a simple list to the \foreach loop to parse. You can't pass an array here because the list will only contain a single item: the array. Instead, just pass a list of integers to be used as indexes for accessing the arrays.

  • You need to use square brackets - not round - to access an indexed element of an array.

\documentclass[tikz]{standalone}
\begin{document}
\begin{tikzpicture}[x=1cm,y=0.4cm]
  \def\tabx{%
    {%
      0,0.01057775,0.02115551,0.03173326,0.04231101,0.05288877,0.06346652,0.07404427,%
      0.08462202,0.09519978,0.1057775,0.1163553,0.126933,0.1375108,0.1480885,0.1586663,0.169244,%
      0.1798218,0.1903996,0.2009773,0.2115551,0.2221328,0.2327106,0.2432883,0.2538661,0.2644438,%
      0.2750216,0.2855993,0.2961771,0.3067548,0.3173326,0.3279103,0.3384881,0.3490659,0.3596436,%
      0.3702214,0.3807991,0.3913769,0.4019546,0.4125324,0.4231101,0.4336879,0.4442656,0.4548434,%
      0.4654211,0.4759989,%
      0.4865766,0.4971544,0.5077321,0.5183099,0.5288877,0.5394654,0.5500432,0.5606209,0.5711987,0.5817764,%
      0.5923542,0.6029319,0.6135097,0.6240874,0.6346652,0.6452429,0.6558207,0.6663984,0.6769762,%
      0.6875539,0.6981317,0.7087095,0.7192872,0.729865,0.7404427,0.7510205,0.7615982,0.772176,%
      0.7827537,0.7933315,0.8039092,0.814487,0.8250647,0.8356425,0.8462202,0.856798,0.8673757,0.8779535,%
      0.8885313,0.899109,0.9096868,0.9202645,0.9308423,0.94142,0.9519978,0.9625755,0.9731533,0.983731,%
      0.9943088,1.004887,1.015464,1.026042,1.03662,1.047198%
    }%
  }
  \def\taby{%
    {%
      0,0.06342392,0.1265925,0.1892512,0.251148,0.3120334,0.3716625,0.4297949,0.4861967,%
      0.5406408,0.5929079,0.6427876,0.690079,0.7345917,0.7761465,0.814576,0.8497254,0.8814534,0.909632,%
      0.9341479,0.9549022,0.9718116,0.9848078,0.9938385,0.9988673,0.9998741,0.9968548,0.9898214,0.9788024,%
      0.9638422,0.9450008,0.9223543,0.8959938,0.8660254,0.8325699,0.7957618,0.7557496,0.7126942,0.666769,%
      0.618159,0.5670599,0.5136774,0.4582265,0.4009305,0.3420201,0.2817326,0.2203105,0.1580014,0.09505604,%
      0.03172793,-0.03172793,-0.09505604,-0.1580014,-0.2203105,-0.2817326,-0.3420201,-0.4009305,-0.4582265,%
      -0.5136774,-0.5670599,-0.618159,-0.666769,-0.7126942,-0.7557496,-0.7957618,-0.8325699,-0.8660254,%
      -0.8959938,-0.9223543,-0.9450008,-0.9638422,-0.9788024,-0.9898214,-0.9968548,-0.9998741,-0.9988673,%
      -0.9938385,-0.9848078,-0.9718116,-0.9549022,-0.9341479,-0.909632,-0.8814534,-0.8497254,-0.814576,%
      -0.7761465,-0.7345917,-0.690079,-0.6427876,-0.5929079,-0.5406408,-0.4861967,-0.4297949,-0.3716625,%
      -0.3120334,-0.251148,-0.1892512,-0.1265925,-0.06342392,-0.000000027563173%
    }%
  }
  \coordinate (p O) at (0,0);
  \foreach \i [remember=\i as \ilast (initially O), evaluate=\i as \y using {\taby[\i]}, evaluate=\i as \x using {\tabx[\i]}, evaluate=\i as \lw using \i/10] in {0,...,99}
  {
    \draw [red!\i, line width=\lw] (p \ilast) -- ++(\x,\y) coordinate (p \i);
  }
\end{tikzpicture}
\end{document}

sin wave

Note, however, that there is no point giving TikZ data to this number of decimal places. It will just increase parsing time, without improving accuracy.

EDIT

Here's an extended example in response to the request for one showing how to use a loop within a path.

\pdfminorversion=7
% ateb: http://tex.stackexchange.com/a/354419/ addaswyd o gwestiwn baptiste: http://tex.stackexchange.com/q/354406/
\documentclass[tikz,border=10pt]{standalone}
\begin{document}
\begin{tikzpicture}[x=1cm,y=0.4cm]
  \def\tabx{%
    {%
      0,0.01057775,0.02115551,0.03173326,0.04231101,0.05288877,0.06346652,0.07404427,%
      0.08462202,0.09519978,0.1057775,0.1163553,0.126933,0.1375108,0.1480885,0.1586663,0.169244,%
      0.1798218,0.1903996,0.2009773,0.2115551,0.2221328,0.2327106,0.2432883,0.2538661,0.2644438,%
      0.2750216,0.2855993,0.2961771,0.3067548,0.3173326,0.3279103,0.3384881,0.3490659,0.3596436,%
      0.3702214,0.3807991,0.3913769,0.4019546,0.4125324,0.4231101,0.4336879,0.4442656,0.4548434,%
      0.4654211,0.4759989,%
      0.4865766,0.4971544,0.5077321,0.5183099,0.5288877,0.5394654,0.5500432,0.5606209,0.5711987,0.5817764,%
      0.5923542,0.6029319,0.6135097,0.6240874,0.6346652,0.6452429,0.6558207,0.6663984,0.6769762,%
      0.6875539,0.6981317,0.7087095,0.7192872,0.729865,0.7404427,0.7510205,0.7615982,0.772176,%
      0.7827537,0.7933315,0.8039092,0.814487,0.8250647,0.8356425,0.8462202,0.856798,0.8673757,0.8779535,%
      0.8885313,0.899109,0.9096868,0.9202645,0.9308423,0.94142,0.9519978,0.9625755,0.9731533,0.983731,%
      0.9943088,1.004887,1.015464,1.026042,1.03662,1.047198%
    }%
  }
  \def\taby{%
    {%
      0,0.06342392,0.1265925,0.1892512,0.251148,0.3120334,0.3716625,0.4297949,0.4861967,%
      0.5406408,0.5929079,0.6427876,0.690079,0.7345917,0.7761465,0.814576,0.8497254,0.8814534,0.909632,%
      0.9341479,0.9549022,0.9718116,0.9848078,0.9938385,0.9988673,0.9998741,0.9968548,0.9898214,0.9788024,%
      0.9638422,0.9450008,0.9223543,0.8959938,0.8660254,0.8325699,0.7957618,0.7557496,0.7126942,0.666769,%
      0.618159,0.5670599,0.5136774,0.4582265,0.4009305,0.3420201,0.2817326,0.2203105,0.1580014,0.09505604,%
      0.03172793,-0.03172793,-0.09505604,-0.1580014,-0.2203105,-0.2817326,-0.3420201,-0.4009305,-0.4582265,%
      -0.5136774,-0.5670599,-0.618159,-0.666769,-0.7126942,-0.7557496,-0.7957618,-0.8325699,-0.8660254,%
      -0.8959938,-0.9223543,-0.9450008,-0.9638422,-0.9788024,-0.9898214,-0.9968548,-0.9998741,-0.9988673,%
      -0.9938385,-0.9848078,-0.9718116,-0.9549022,-0.9341479,-0.909632,-0.8814534,-0.8497254,-0.814576,%
      -0.7761465,-0.7345917,-0.690079,-0.6427876,-0.5929079,-0.5406408,-0.4861967,-0.4297949,-0.3716625,%
      -0.3120334,-0.251148,-0.1892512,-0.1265925,-0.06342392,-0.000000027563173%
    }%
  }

  \coordinate (p O) at (0,0);
  \foreach \i [remember=\i as \ilast (initially O), evaluate=\i as \y using {\taby[\i]}, evaluate=\i as \x using {\tabx[\i]}, evaluate=\i as \lw using \i/10] in {0,...,99}
  {
    \draw [red!\i, line width=\lw] (p \ilast) -- ++(\x,\y) coordinate (p \i);
  }

  \draw [green!75!black] (0,-1) \foreach \i [evaluate=\i as \y using {\taby[\i]}, evaluate=\i as \x using {\tabx[\i]}] in {0,...,99} { -- ++(\x,\y) };

  \draw  (0,-2) coordinate (p O) \foreach \i [remember=\i as \ilast (initially O), evaluate=\i as \y using {\taby[\i]}, evaluate=\i as \x using {\tabx[\i]}, evaluate=\i as \lw using \i/10] in {0,...,99}
  {
    (p \ilast) edge [blue!\i, line width=\lw] coordinate [pos=1] (p \i) ++(\x,\y)
  };
\end{tikzpicture}
\end{document}

The red curve uses a loop over paths, one per iteration, as before. The lower green and blue curves use loops within paths, one per coordinate.

loop variations

The green curve has to use the same options for all parts of the path, so the colour and width are static. In this case, we don't need to remember coordinates by naming them, because we are drawing a continuous path and are wherever we've got to.

The blue curve works around the same-options restriction by using the edge operation, which breaks off the current path to make a diversion. In this case, however, we are back to needing to remember the coordinates by naming them, as the path resumes from where it began when the edge is complete.

  • Note that each segment of the curve is drawn separately here. If you want a continuous path, you can use a loop within the scope of the \path command rather than using a new path in each step of the loop. In this case, you would not need to remember the last point from iteration to iteration. – cfr Feb 18 '17 at 0:36
  • That's great, thank you! Would you mind adding an example of a foreach loop within the scope of a path? I'm trying to decide which of the two is better, including in situations where the line type may vary between nodes. – baptiste Feb 18 '17 at 5:56
  • @baptiste Please see edit. – cfr Feb 19 '17 at 20:54
  • Thanks again, that's great. I'm trying to get rid off the ++'s (I misunderstood them at first) to get a proper sine wave, but the last example is proving tricky (I can only get it to remember the wrong point somehow). I'm editing the question with more sensible numbers. – baptiste Feb 20 '17 at 7:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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