4

I'm very new to circuitikz (started today and still figuring out if it's the right tool for me).

I want to build a framework to automatically create logic circuits - including wiring - from solely a description of which elements are connected to which other elements.

I managed to draw the below diagram with circuitikz which shows what output I'm looking for (it's important that backwards edges are supprted, although the one below does not make too much sense):

enter image description here

The information which should be provided in my framework to draw this circuit is something like this: "The first AND's output is connected to the OR's first input etc." But it should not have to state how the wires are going. (The circuits are not supposed to be very complicated though, so I'm not looking for sophisticated routing algorithms.)

This is the actual latex code I used:

\documentclass{article}
\usepackage{circuitikz}

\begin{document}
    \begin{circuitikz} \draw
        (0,2) node[european and port] (myand1) {}
        (0,0) node[european and port] (myand2) {}
        (2,1) node[european or port] (myor) {}
        (myand1.out) -- (myor.in 1)
        (myand2.out) -- (myor.in 2)
        (myor.out) -- ++(right:0mm) |- ++(up:18mm) -| (myand1.in 1);
    \end{circuitikz}
\end{document}

This code depends on the absolute values for routing the backwards edge:

... |- ++(up:18mm) -| ...

However, if I want to create the Latex code automatically from whatever circuit description I get, I would have to provide a more generic value than 18 in this and similar situations.

So my question is: Is there a way to either let circuitikz automatically do this routing or at least get a more insightful value for how far up the edge has to go, maybe how high the highest element so far drawn is placed, so to make the wire go around it - or something like that?

(Btw. I already read quite a lot in the manuals of both circuitikz and tikz, but there is a lot of stuff in there, so maybe somebody can provide a hint?)

  • 2
    The (current bounding box) will tell you how far you have to go to not hit anything, but not how far just to miss only part. – John Kormylo Nov 19 '15 at 13:53
  • 1
    If you need to miss only part and you have things regularly spaced, you might be able to use the fit library to find a point above a group of nodes, for example. Not sure if the graph drawing stuff could be used for this or not. ?? There are also the positioning and calc libraries, or just ([yshift=5pt]<node name>), for example.. – cfr Nov 19 '15 at 14:06
  • I tried both the (current bounding box) and the ([yshift=5pt]<node name>) approach, and they both work in principle - thanks! I'll look into the libraries, too. I also thought about using the graph drawing algorithms of tikz - but i'm far to rookie to know how (if) this would go... Nevertheless, doing it by hand seems to be quite a pain... – lukas.coenig Nov 19 '15 at 14:17
  • @JohnKormylo How would I add (or multiply) a constant to (current bounding box)? – lukas.coenig Nov 19 '15 at 17:51
  • Ok, I got it, never mind! – lukas.coenig Nov 19 '15 at 19:15
5

If you don't like using absolute coordinates, why not go all the way? I defined \minsep as a dimen so than one could use 2\minsep etc.

\documentclass[border=2mm]{standalone}
\usepackage{circuitikz}
\usetikzlibrary{calc}

\newlength{\minsep}% minimum separation
\minsep=2mm

\begin{document}
    \begin{circuitikz} \draw
        node[european and port] (myand1) {}
        (current bounding box.south) node[european and port,below={\minsep}] (myand2) {}
        (current bounding box.east) node[european or port,right] (myor) {}
        (myand1.out) -- (myor.in 1)
        (myand2.out) -- (myor.in 2)
        (myor.out) |- ($(current bounding box.north)+(0,\minsep)$) -| (myand1.in 1);
    \end{circuitikz}
\end{document}

circuit with minsep

The beauty of the (current bounding box) is that it grows automatically.

  • Also a great solution, and by growing automatically it solves the follow-up question, too, that I posted as a comment to Tom Bombadil's solution. – lukas.coenig Nov 20 '15 at 17:55
6

Here's a solution using the fit library. I defined a command that automates this, but like this will only work for "back" paths in left-to-right diagrams. Depicted here are three variants:

  • going above
  • going below
  • going above, but with more obstacles

Code

\documentclass[border=2mm]{standalone}
\usepackage{circuitikz}
\usetikzlibrary{fit}
\usepackage{xstring}
\usepackage{xifthen}

\begin{document}

\newcommand{\fittingboxconnection}[5][]%
% [1]: further nodes for fitting in parenthesis like (this)(or)(that)
%  2 : exit
%  3 : entry
%  4 : inner sep
%  5 : which way to take ("up", down otherwise)
{   \StrBefore{#2}{.}[\firstnode]
    \StrBefore{#3}{.}[\secondnode]
    \ifthenelse{\isempty{#1}}
    {   \node[fit=(\firstnode)(\secondnode), inner sep=#4] (tempfittingnode) {};
    }
    {   \node[fit=(\firstnode)(\secondnode)#1, inner sep=#4] (tempfittingnode) {};
    }
    \ifthenelse{\equal{#5}{up}}
    {   \xdef\tempusedway{north east}
    }
    {   \xdef\tempusedway{south east}
    }
    \draw (#2) -| (tempfittingnode.\tempusedway) -| (#3);
}

\foreach \variation in
{   {   \fittingboxconnection{myor.out}{myand1.in 1}{3mm}{up}},
    {   \fittingboxconnection{myor.out}{myand2.in 2}{3mm}{down}},
    {   \node[circle, draw] at (1.5,2.5) (obstacle1) {X};
        \node[circle, draw] at (2.5,2) (obstacle2) {X};
        \fittingboxconnection[(obstacle1)(obstacle2)]{myor.out}{myand1.in 1}{3mm}{up}
    }
}
{   \begin{circuitikz}
        \node[european and port] (myand1) at (0,2) {};
        \node[european and port] (myand2) at (0,0) {};
        \node[european or port] (myor) at (2,1) {};
        \draw (myand1.out) |- (myor.in 1)
            (myand2.out) |- (myor.in 2);

        \variation
    \end{circuitikz}
}

\end{document}

Output

enter image description here

  • Oh man, this is such an elegant code, I can't believe it!! Great! – lukas.coenig Nov 19 '15 at 20:27
  • Just one follow-up question: is it possible with this approach to include lines (wires) as obstacles as well such that they are surrounded in a distance of 3mm by other lines too? Otherwise it can happen that several wires overlap and cannot be distinguished. – lukas.coenig Nov 20 '15 at 8:30
  • 1
    @lukas.coenig: I think you have to plant a node (or coordinate) or probably more than one on the wire, or if the additional distance is alright, choose a bigger inner sep for later paths. But ideally, if you need help with that, you can ask a follow-up question with updated code and requirements that links to this one as reference, as questions here should usually concern one specific problem. – Tom Bombadil Nov 20 '15 at 9:35
  • As John Kormylo's answer solves this problem, too, I accepted his answer. Hope you don't consider it inpolite - I certainly learned a lot by all the answers! – lukas.coenig Nov 20 '15 at 17:57
  • 3
    @lukas.coenig: Of cause not! You should always accept the answer that helped you the most, and for future visitors with similar problems it's probably nice to have multiple solutions to choose from. – Tom Bombadil Nov 20 '15 at 18:21
1

I finally solved it using the bounding-box approach on the tikz side (I did not use circuitikz after all as it does not allow for more than two inputs per gate). First I place the nodes using GraphViz as a preprocessor, then the wires are drawn following John Kormylo's suggestion. It's still quite rudimentary, but for small circuits it works fine. You can try it yourself here:

http://www.xwizard.de:8080/Wizz?template=ID-8558#Output

The circuit shown there is the one I posted in my question as an example. As you can see, GraphViz arranged the nodes in a different way which might be more suitable from some graph drawing perspective. You can also try different circuits by changing the script and clicking on "Draw!".

To see the actual generated LaTeX tikz code, click on the "Plain generator code" button.

The code generated for the example from my question is:

\documentclass[tightpage]{standalone}
\usepackage{tikz}
\usetikzlibrary{circuits.logic.IEC}

\begin{document}
\pagestyle{empty}
\begin{tikzpicture}[circuit logic IEC, every circuit symbol/.style={}]\node[label={below:$a_1$}, and gate, inputs={nn}] (a1) at (2.375,1.125) {};
\node[label={below:$A$}] (A) at (0.375,2.0) {};
\node[label={below:$a_2$}, and gate, inputs={nn}] (a2) at (6.375,1.125) {};
\node[label={below:$B$}] (B) at (0.375,0.25) {};
\node[label={below:$C$}] (C) at (4.375,2.875) {};
\node[label={below:$o$}, or gate, inputs={nn}] (o) at (4.375,1.125) {};
  \coordinate [label=left:$~$] (oi2) at ($ (o.input 2) - (0.3, 0)$);
  \draw (a1.east) -- ++(right:3mm) -- (oi2) -- (o.input 2);
  \coordinate [label=left:$~$] (a1i1) at ($ (a1.input 1) - (0.3, 0)$);
  \draw (A.east) -- ++(right:3mm) -- (a1i1) -- (a1.input 1);
  \coordinate [label=left:$~$] (oi1) at ($ (o.input 1) - (0.3, 0)$);
\coordinate [label=left:$~$] (be1) at ($(current bounding box.east)+(0.3, 0)$);
\coordinate [label=left:$~$] (bv1) at ($(current bounding box.north)+(0,0.3)$);
  \draw let \p1 = (bv1), \p2 = (a2.east), \p3 = (be1) in (a2.east) -- (\x3, \y2) |- (\x2, \y1) -| (oi1) -- (o.input 1);
  \coordinate [label=left:$~$] (a1i2) at ($ (a1.input 2) - (0.3, 0)$);
  \draw (B.east) -- ++(right:3mm) -- (a1i2) -- (a1.input 2);
  \coordinate [label=left:$~$] (a2i1) at ($ (a2.input 1) - (0.3, 0)$);
  \draw (C.east) -- ++(right:3mm) -- (a2i1) -- (a2.input 1);
  \coordinate [label=left:$~$] (a2i2) at ($ (a2.input 2) - (0.3, 0)$);
  \draw (o.east) -- ++(right:3mm) -- (a2i2) -- (a2.input 2);
\end{tikzpicture}
\end{document}

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.