4

I'm new to TikZ/PGF (a week or so in), and am loving it. I've had good success making figures so far, but have a question about best practices for including multiple copies of a sub-circuit in a single figure. And doing so in a way that allows simple connections to the input/output nodes of the sub-circuit.

As a very rudimentary example (please excuse the free-handed drawing) in which I have a drawing with two repeated sub-circuits (contained within a rectangle).

hand-drawn circuit

So in this example, I'd like to define the amplifier circuit once, and then place it twice in the picture and connect wires to its input and output nodes. Does this require me to develop a library, or can the sub-circuit be written into the .tex file directly and then reused?

I did see the post about using a \newsavebox here, but I'm not sure how I would access the nodes in the circuit using that approach.

Any help is greatly appreciated. Many thanks.

\documentclass{article}
\usepackage{tikz}

\usetikzlibrary{circuits.logic.US,circuits.ee.IEC,positioning,fit}

\begin{document}
\begin{tikzpicture}[circuit ee IEC,circuit logic US,x=3cm,y=2cm,semithick,
    set resistor graphic=var resistor IEC graphic]

  % Box on the left (non circuit stuff)                                     
  \draw (0,0) rectangle (2,2);
  \draw (1,1) rectangle (1.8,1.8);
  \draw (0.3,0.2) rectangle (0.5,0.5);

  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%                        
  % Begin the sub-circuit to be replicated                                  
  \node[contact] (A) at (2.5,0) {};
  \node[contact] [right=1.5cm of A] (B) {};
  \node[contact] [above=1.5cm of B] (C) {};
  \node[contact] [right=1.0cm of B] (D) {};
  \node[contact] [right=1.5cm of D] (E) {};
  \node[buffer gate] [right=0.1 of D] (amp) {};
  % Problem #1:  there is whitespace between the amp output and the line to node E
  \draw (A) to [resistor] (B) (B) to [capacitor] (D) (D) to (amp) (amp) to (E);
  \draw (B) to [resistor] (C);
  % Problem #2: had to tune 0.54 to get vertical line to node E.  (output of amp)
  % Must be a better way ...
  \draw (D) -- ++(0,0.5) to [capacitor] ++(right:0.54) to (E);
  \tikzset{black box/.style={draw=black, rectangle}};
  \node(preampbox) [black box, fit=(A) (C) (E) (amp), inner sep=0.4cm] {};
  %%%%%%%%%%%%%%  end subcircuit  %%%%%%%%%%%%%%%%%%                        

\end{tikzpicture}
\end{document}

code output

  • You may wan't to check this. Possible duplicate? – masu Sep 29 '13 at 17:07
  • Yes, I had linked to that in my original question, but I'm not sure how to access nodes of the circuit from within the savebox. For example, how would I tell TikZ to draw a line that terminates on the input resistor of the sub-circuit? – user37464 Sep 29 '13 at 18:11
  • Why would you use a savebox? Isn't a for loop eligible? – masu Sep 29 '13 at 18:55
  • The link you (and I) posted suggests using a savebox... I could use a \foreach with two iterations (one for each amplifier block). Is that the best way to go in this situation? – user37464 Sep 29 '13 at 20:12
  • I’d to the following: Use relative coordinates to create that sub-path (the whole sub-circuit thing) and save it in an insert path. Then the fit library can be used to draw the box around it. Could you add the the code for the sub-circuit as well as the whole picture? (I’m not so familiar with circuits and won’t look up all the shapes and things.) – Qrrbrbirlbel Sep 29 '13 at 22:12
  • You can create a macro to draw anything relative to a given point. You might even be able to create a node consisting of many nodes (I'll have to do some tests and get back). – John Kormylo Sep 30 '13 at 03:14
  • No nodes consisting of other nodes. Best I can do is create a node with named anchor points from which you can attach components. Frankly, it would be better to assign names to all the components and use their anchor points. – John Kormylo Sep 30 '13 at 03:42
  • You need to make some design decisions. Do you want to be able to rotate it? Do you want to change the labels? Do you want to change the size? Do you want to use (x,y) or {x}{y}? – John Kormylo Sep 30 '13 at 03:55
  • @JohnKormylo yes, I'd like to be able to rotate, and change the labels. It should be scalable along with the rest of the picture. Sorry, I'm familiar with (x,y) but not with {x}{y}...? – user37464 Sep 30 '13 at 04:14

2 Answers2

5

Well, you could just pack the all the \nodes and paths in a macro and re-use it.

With TikZ though, you can save entire paths (and you can do all that on one path) and re-use that later. The starting point for the contact (A) is the current coordinate on the path.

Instead of

(D) edge[to path={-- ++ (up:.5) to[capacitor] ([shift=(up:.5)] E) -- (E)}] (E)

you can also do

(D) edge[to path={-- ++ (up:.5) coordinate (aux) to[capacitor] (aux -| E) -- (E)}] (E)

(The ultimate (E) after the edge option is ignored, by the way.)

Code

\documentclass[tikz]{standalone}
\usetikzlibrary{circuits.logic.US,circuits.ee.IEC,positioning,fit}
\tikzset{
  saveuse path/.code 2 args={%
    \pgfkeysalso{#1/.estyle={/tikz/insert path={#2}}}%
    \global\expandafter\let\csname pgfk@\pgfkeyscurrentpath/.@cmd\expandafter\endcsname % not optimal as it is now global through out the document
                           \csname pgfk@\pgfkeyscurrentpath/.@cmd\endcsname
    \pgfkeysalso{#1}}}
\begin{document}
\begin{tikzpicture}[
  circuit ee IEC,
  circuit logic US,
  x=3cm, y=2cm, semithick,
  set resistor graphic=var resistor IEC graphic
]
  \path (2.5,0)
    [saveuse path={subcirc}{{
      [nodes=contact, node distance=1.5cm]
      node                 (A) {}
      node[right=    of A] (B) {}
      node[above=    of B] (C) {}
      node[right=1cm of B] (D) {}
      node[right=    of D] (E) {} }
    (D)   edge[buffer gate={name=amp}] (E)
    (A)   edge [resistor]  (B)
    (B)   edge [capacitor] (D)
    (B)   edge [resistor]  (C)
    (D) edge[to path={-- ++ (up:.5) to[capacitor] ([shift=(up:.5)] E) -- (E)}] (E)
    node[draw=black, rectangle, fit=(A)(C)(E)(amp.south west)] (box) {}}]
    (A) [late options={alias=A'}]
    (2.5,2) [subcirc]
    (A) [late options={alias=A''}]
  ;

  \draw (A') to[bend left=90] (A'');
\end{tikzpicture}
\end{document}

Output

enter image description here

Qrrbrbirlbel
  • 119,821
3

Drawing the node only produces the outer rectangle, but it is used to define all the internal coordinates and the contact anchor points. \drawMyamp does the internal circuitry. By tradition, the origin is aligned with all of the contact anchor points.

\documentclass{article}
\usepackage{tikz}

\usetikzlibrary{circuits.logic.US,circuits.ee.IEC,positioning,fit}

\newcommand{\UseGlobalScale}
 {\pgfgettransformentries{\xscale}{\@tempa}{\@tempa}{\yscale}{\@tempa}{\@tempa}}

\let\xscale=*
\let\yscale=*

% ***************************** myamp node *********************************
% contact anchor points: left, above, out
% internal anchor points: A, B, C, D, E, F, G

\pgfdeclareshape{myamp}{

\savedmacro{\resize}{   % restore global scale factor
 \if*\xscale \relax \else \pgftransformxscale{\xscale} \fi
 \if*\yscale \relax \else \pgftransformyscale{\yscale} \fi
}

\anchor{center}{\pgfpointorigin}    % within the node, (0,0) is the center

\anchor{text} % this is required to center text in the node (not used here)
 {\pgfpoint{-.5\wd\pgfnodeparttextbox}{-.5\ht\pgfnodeparttextbox}}

\anchor{left}{\pgfpoint{-2cm}{0cm}}
\anchor{above}{\pgfpoint{0cm}{2cm}}
\anchor{out}{\pgfpoint{3cm}{0cm}}

\anchor{A}{\pgfpoint{-1.5cm}{0cm}}
\anchor{B}{\pgfpointorigin}
\anchor{C}{\pgfpoint{0cm}{1.5cm}}
\anchor{D}{\pgfpoint{1cm}{0cm}}
\anchor{E}{\pgfpoint{2.5cm}{0cm}}
\anchor{F}{\pgfpoint{1cm}{1cm}}
\anchor{G}{\pgfpoint{2.5cm}{1cm}}

\foregroundpath{ % draw border 
 \pgfpathrectanglecorners{\pgfpoint{-1.75cm}{-1cm}}{\pgfpoint{2.75cm}{1.75cm}}
 \pgfusepath{draw}}
}

%************************ draw myamp components ****************************
% component names = #1 R1, #1 R2, #1 C1, #1 C2 and #1 opamp

\newcommand{\drawMyamp}[2]{ % #1 = node name, #2 = rotation angle
 \draw 
  node[contact] at(#1.A){}
  node[contact] at(#1.B){}
  node[contact] at(#1.C){}
  node[contact] at(#1.D){}
  node[contact] at(#1.E){}
  node[resistor,rotate=#2](#1 R1) at($(#1.A)!.5!(#1.B)$){}
  node[resistor,rotate={90+#2}](#1 R2) at($(#1.B)!.5!(#1.C)$){}
  node[capacitor,rotate=#2](#1 C1) at($(#1.B)!.5!(#1.D)$){}
  node[capacitor,rotate=#2](#1 C2) at($(#1.F)!.5!(#1.G)$){}
  node[buffer gate,rotate=#2](#1 opamp) at($(#1.D)!.5!(#1.E)$){};
 \draw[thin]
  (#1.left) -- (#1.A) -- (#1 R1.west)
  (#1 R1.east) -- (#1.B) -- (#1 R2.west)
  (#1 R2.east) -- (#1.C) -- (#1.above)
  (#1.B) -- (#1 C1.west)
  (#1 C1.east) -- (#1.D) -- (#1 opamp.west)
  (#1.D) -- (#1.F) -- (#1 C2.west)
  (#1 C2.east) -- (#1.G) -- (#1.E) -- (#1 opamp.east)
  (#1.E) -- (#1.out);
}

%*************************************************************************

\begin{document}

\begin{tikzpicture}[circuit ee IEC,circuit logic US,x=3cm,y=2cm,semithick,
 set resistor graphic=var resistor IEC graphic]

\UseGlobalScale % save scale factor for myamp

% Box on the left (non circuit stuff)                                     
  \draw (0,0) rectangle (2,2);
  \draw (1,1) rectangle (1.8,1.8);
  \draw (0.3,0.2) rectangle (0.5,0.5);

 \draw (3,0) node[myamp,thick,dashed](amp1){};
 \drawMyamp{amp1}{0}

 \draw (3.1,2) node[myamp,color=red,rotate=90](amp2){};
 \drawMyamp{amp2}{90}

 \draw (amp1 R1.south) node[below]{$100 K\Omega$};
\end{tikzpicture}

\end{document}

There are two ways to add labels to the components. You could add a lot of arguments to the macro, or you can put labels in nodes at the anchor points.

John Kormylo
  • 79,712
  • 3
  • 50
  • 120
  • Thank you. This solution relies on circuitikz as well as TikZ. Presumably there's a way to do this in TikZ? – user37464 Sep 30 '13 at 20:38
  • Using Circuitikz is like using a library of Tikz. You lose nothing and gain a lot of components. But yes, the node definition is pure Tikz except for the line width, and the macro can be written using pure Tikz (it just takes longer). – John Kormylo Sep 30 '13 at 20:53
  • Unfortunately, rotating the structure does not rotate the opamp. The only quick fix I can think of is to create a bipole version of the buffer shape. – John Kormylo Oct 01 '13 at 04:06
  • I converted the circuitikz to tikz and added a rotation angle to the macro. – John Kormylo Oct 01 '13 at 12:12
  • Excellent, thanks. A minor typo: \end{circuitikz} should be \end{tikzpicture}. Also although the components in sub-circuit scale (with e.g. [scale=0.25] as an argument to \begin{tikzpicture}, the border does not, nor do the component spacings. – user37464 Oct 01 '13 at 13:58
  • Interesting. I should probably look at the tikx sty files to see how they scale shapes (Aaaaaah!). Circuitikz uses \pgf@circ@Rlen as it's scale factor. – John Kormylo Oct 01 '13 at 19:10
  • Almost forgot: Should I lose the left,above,out anchors? – John Kormylo Oct 01 '13 at 19:13
  • If you put the scale in the node options, e.g. node[myamp,scale=0.25], it scales the border and anchor points, but not the components (as expected). – John Kormylo Oct 01 '13 at 21:16
  • It seems that independent global and node scaling is a "feature" of Tikz. – John Kormylo Oct 02 '13 at 02:35
  • Okay, I changed the answer to use the global scale factor (and ignore the node scale factor). However, you MUST call \pdfgettransformentries before using the myamp node. – John Kormylo Oct 02 '13 at 15:09
  • Now if you call \UseGlobalScale it will use the global scale. Otherwise it will use the node scale factor. – John Kormylo Oct 02 '13 at 19:25