I had a quick tinker with one of the demo notebooks I’m putting together to try to work through what I think I mean by various takes on the phrase “reproducible educational materials” this morning, so here’s a quick note to keep track of my thinking.
The images are drawn in Jupyter notebooks using tikzmagic loaded as %load_ext tikz_magic
. (The original notebook this post is based on can be found here, although it’s likely subject to significant change…)
The circuitikz
LaTeX package (manual) supports the drawing of electrical circuit diagrams. Circuits are constructed by drawing symbols that connect pairs of Cartesian co-ordinates or that:
%%tikz -p circuitikz -s 0.3 %Draw a resistor labelled R_1 connecting points (0,0) and (2,0) \draw (0,0) to[R, l=$R_1$] (2,0);
The requirement to specify co-ordinates means you need to think about the layout – drafting a circuit on graph paper can help with this.
But things may be simplified in maintenance terms if you label co-ordinates and join those.
For example, consider the following circuit:
This can be drawn according to the following script:
%%tikz -p circuitikz -s 0.3 %Draw a resistor labelled R_1 connecting points (0,0) and (2,0) %Extend the circuit out with a wire to (4,0) \draw (0,0) to[R, l=$R_1$] (2,0) -- (4,0); %Add a capacitor labelled C_1 connecting points (2,0) and (2,-2) \draw (2,0) to[C, l=$C_1$] (2,-2); %Add a wire along the bottom \draw (0,-2) -- (4,-2);
There are a lot of explicitly set co-ordinate values in there, and it can be hard to see what they refer to. Even with such a simple diagram, making changes to it could become problematic.
In the same way that it is good practice to replace inline numerical values in computer programs with named constants or variables, we can start to make the figure more maintainable by naming nodal points and then connecting these named nodes:
%%tikz -p circuitikz -s 0.3 %Define some base component size dimensions \def\componentSize{2} %Define the size of the diagram in terms of component width and height %That is, how many horizontally aligned components wide is the diagram % and how many vertically aligned components high \def\componentWidth{2} \def\componentHeight{1} %Define the y co-ordinate of the top and bottom rails \def\toprail{0} \def\height{\componentSize * \componentHeight} \def\bottomrail{\toprail - \height} %Define the right and left extent x coordinate values \def\leftside{0} \def\width{\componentSize * \componentWidth} \def\rightside{\leftside + \width} %Name the coordinate locations of particular nodes \coordinate (InTop) at (\leftside,\toprail); \coordinate (OutTop) at (\rightside,\toprail); \coordinate (InBottom) at (\leftside,\bottomrail); \coordinate (OutBottom) at (\rightside,\bottomrail); %Draw the top rail %Define a convenience x coordinate as the % vertical aligned to the topmost component out %The number (1) in the product term below is based on % how many components in from the left we are \def\R1outX{1 * \componentSize} %Add a resistor labelled R_1 \coordinate (R1out) at (\R1outX,\toprail); \draw (InTop) to[R, l=$R_1$] (R1out) -- (OutTop); %Add a capacitor labelled C_1 \coordinate (C1out) at (\R1outX,\bottomrail); \draw (R1out) to[C, l=$C_1$] (C1out); %Draw the bottom rail \draw (InBottom) -- (OutBottom);
Some reflections about possible best practice drawn (!) from this:
- define named limits on x values to set the width of the diagram, such as
\leftside
and\rightside
. This can be done by counting the number of components wide the diagram is (if we can assume components have width one). - name the maximum and minimum height (y) values such as
\toprail
and\bottomrail
. Again, counting vertically place components may help. Use relative definitions where possible to make the diagram easier to maintain. - define connections relative to each other to minimise the number of numerical values that need to be set explicitly;
- name points sensibly; if we read the diagram from top left to bottom right, we can make use of easily recognised verticals by using named x coordinate values set relative to the topmost component out x co-ordinates (for example,
\R1outX
) and top leftmost component out y values; full cartesian co-ordinate pairs can then be named relative to nodes associated with top leftmost component outs (for example,\R1out
); - the code to produce the diagram looks like overkill in its length, but lots of could quickly become boilerplate that could potentially be included in a slightly higher level TeX package that bakes more definitions in. Despite the added length, it also makes the script more readable and supports its self-documenting, literate programming style nature.
PS imagining feedback… “Ah yes, but we don’t draw resistors like that, so it’s no good…” ;-)
%%tikz -p circuitikz -s 0.3 %To ward off the "we don't draw resistors like that" cries... \ctikzset{resistor = european} %Draw a resistor labelled R_1 connecting points (0,0) and (2,0) \draw (0,0) to[R, l=$R_1$] (2,0);
And that’s another reason why this approach make sense…