2

When trying to make a sequence of graphs of step functions, I wrote the following code snippet:

\begin{tikzpicture} \pgfmathsetmacro{\n}{2^3}; \pgfmathsetmacro{\step}{1/\n}; \foreach \i in {0,\step,...,(\n-1)*\step} {\draw[-,thick,blue] (\i,{\i*2})--({\i+\step},{\i*2});} \end{tikzpicture} 

The idea is that I would like to define variables \n and \step such that all I have to do is adjust the value \n is set to, and then it will draw a step function in that many equal-sized pieces, blahblahblah. I will use this to make many different pictures within the same document.

However, it seems that defining variables in this way is not legal -- at least, I think so, since when I write this without variables it seems to work fine.

Is there a way to define variables "globally" within a tikzpicture environment?


It suddenly occurs to me ... could I just do the following?

\begin{tikzpicture} \foreach \n in {2^3} { \foreach \step in {1/n} { \foreach \i in {0,\step,...,(\n-1)*\step} {\draw[-,thick,blue] (\i,{\i*2})--({\i+\step},{\i*2});} }} \end{tikzpicture} 

Even if it works, it sure seems like a fragile hack ... and I would guess there is some reason why I've never seen anyone suggest doing this ... although I can't actually name a reason why it should be bad.

Is there a reason why this wouldn't work or is bad?


Update: Tested it out, doesn't quite work although doesn't utterly break, but still trying to understand why it's doing what it's doing.

2
  • 3
    You either have to evaluate (\n-1)*\step in its own macro und use that macro in its place or use parse=true as an option to \foreach which instructs it to evaluate the last element in the list for you. That said, maybe some kind of plot, say the jump mark mid, would be more appropriate for it. Commented May 19, 2023 at 19:22
  • @Qrrbrbirlbel setting parse=true is my favorite solution! Thank you -- if you want to turn it into an answer, I'll accept it. Commented May 19, 2023 at 19:24

2 Answers 2

2

The \foreach loop doesn't evaluate any of its list's entries on its own. And after the ... syntax it needs to “see” a valid number.

In this case, you can use parse = true to force PGFFor to evaluate the last entry in its list.

In the second case, I'm using the use float key from the ext.misc library of my tikz-ext package. Please note that it is used after the variable is declared and has no in <list> part! This key will evaluate all entries for you.

In the third and fourth case, I'm using integer steps and just scale the drawing or the xyz coordinate system accordingly. This allows us to avoid any floating number inaccuracies that might creep in. (Though, they don't in the examples I'm using below.)

In the fifth case, I'm using a plot which does all the math for us. Of course, this is only a valid solution in this example.

Code

\documentclass[tikz]{standalone} \usetikzlibrary{ext.misc} \makeatletter \tikzset{scale xyz/.code=\pgfmathparse{#1}% \pgf@xx\pgfmathresult\pgf@xx \pgf@xy\pgfmathresult\pgf@xy \pgf@yx\pgfmathresult\pgf@yx \pgf@yy\pgfmathresult\pgf@yy \pgf@zx\pgfmathresult\pgf@zx \pgf@zy\pgfmathresult\pgf@zy} \makeatother \newcommand*\myRow[1]{ \node[right] at (0,1) {$n = \pgfmathprint{int(#1)}$}; \pgfmatrixnextcell % Case 1: everything the same but parse the last entry \pgfmathsetmacro{\n}{#1} \pgfmathsetmacro{\step}{1/\n} \foreach[parse=true] \i in {0, \step, ..., (\n-1)*\step} \draw (\i,\i*2) -- +(right:\step); \pgfmatrixnextcell % Case 2: use float = <start> to <end> step <step> \foreach \i[use float = 0 to (#1-1)/#1 step 1/#1] \draw (\i,\i*2) -- +(right:1/#1); \pgfmatrixnextcell % Case 3: use integer steps (and parsing the last entry) % and divide by #1 when drawing \foreach[parse=true] \i in {0,...,#1-1} \draw (\i/#1,2*\i/#1) -- +(right:1/#1); \pgfmatrixnextcell % Case 4: scale xyz coordinate system by 1/#1 % and use integer steps \tikzset{scale xyz = 1/#1}% i.e. scale = 1/8 \foreach[parse=true] \i in {0,...,#1-1} \draw (\i,2*\i) -- +(right:1); \pgfmatrixnextcell % Case 5: Maybe a plot? \draw[scale xyz=1/#1] plot[jump mark left, domain=0:#1, samples=#1+1] (\x,2*\x); \pgfmatrixendrow} \begin{document} \tikz[ row sep=5mm, column sep=5mm, thick, blue, every cell/.style 2 args={ style/.expand twice={\ifnum#2>1 \ifnum#1>1 execute at begin cell={\draw[help lines] (0,0) grid (1,2);}\fi\fi}}] \matrix[row 5/.style=thin, row 1/.style={shift=(right:.5)}]{ &\node{Case 1}; &\node{Case 2}; &\node{Case 3}; &\node{Case 4}; &\node{Case 5};\\ \myRow{2^3} \myRow{3^3} \myRow{5} \myRow{35} \myRow{73}}; \end{document} 

Output

enter image description here

3

I'd use a better floating point system.

\documentclass{article} \usepackage{tikz} \ExplSyntaxOn \NewDocumentCommand{\fpstep}{mmmm} { \fp_step_inline:nnnn { #1 } { #2 } { #3 } { #4 } } \NewDocumentCommand{\definefpvar}{mm} { \fp_zero_new:c { l_addem_fpvar_#1_fp } \fp_set:cn { l_addem_fpvar_#1_fp } { #2 } } \NewExpandableDocumentCommand{\fpvar}{m} { \fp_use:c { l_addem_fpvar_#1_fp } } \ExplSyntaxOff \begin{document} \begin{tikzpicture} \definefpvar{n}{2^3} \definefpvar{step}{1/(\fpvar{n})} \fpstep{0}{\fpvar{step}}{(\fpvar{n}-1)*\fpvar{step}} {\draw[-,thick,blue] (#1,{#1*2})--({#1+\fpvar{step}},{#1*2});} \end{tikzpicture} \end{document} 

Instead of \foreach, that doesn't really behave very accurately with floating point values, the \fp_step_inline:nnnn function is used. In the fourth argument, #1 stands for the current value in the loop. The first argument is the starting point, the second one is the step and the third argument is the final point.

enter image description here

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.