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?)

5
  • 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. Nov 19, 2015 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, 2015 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... Nov 19, 2015 at 14:17
  • @JohnKormylo How would I add (or multiply) a constant to (current bounding box)? Nov 19, 2015 at 17:51
  • Ok, I got it, never mind! Nov 19, 2015 at 19:15

3 Answers 3

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.

1
  • 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. Nov 20, 2015 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

5
  • Oh man, this is such an elegant code, I can't believe it!! Great! Nov 19, 2015 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. Nov 20, 2015 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. Nov 20, 2015 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! Nov 20, 2015 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. Nov 20, 2015 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}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .