4

I've started using pic elements to avoid excessive copy-pasting, but I am likely doing something wrong when trying to place such elements using relative coordinates.

The vertical placement on the left works fine. The horizontal placement at the centre is off. The hacky horizontal placement on the right works fine. The red and blue elements are usually invisible and are shown here for debugging purposes.

\documentclass[tikz,border=6pt]{standalone}
\usetikzlibrary{graphs,arrows.meta,positioning}
\tikzset{
  neuron/.style={circle,very thick,minimum size=7.5mm,inner sep=0,outer sep=0,draw},
  contact/.style={neuron,node contents={},minimum size=2mm},
  sum/.style={neuron, minimum size=7.5mm/2, node contents={}},
  gate/.pic={
    \node (-g) [sum, draw=red, very thin];
    \node [sum, xshift=-2pt];
    \draw [thick] (-g.225) -- +(0,7.5mm/4*2^.5);
    \draw (-g.west) -- +(2pt,0);
    \draw ([yshift=.5pt] -g.south) -- +(135:7.5mm/4);
    \draw [{Latex[length=2pt]}-] ([yshift=-.5pt] -g.north) -- +(-135:7.5mm/4);
  },
  node distance=7.5mm,
}
\begin{document}
\begin{tikzpicture}

  % Place nodes, vertical layout
  \pic  (g) {gate};
  \node (B) [contact, left=of g-g, label=\(b\)];
  \node (E) [contact, above=of g-g, label=\(e\)];
  \node (C) [contact, below=of g-g, label=below:\(c\)];

  % Connect nodes
  \graph [use existing nodes]{
    {B, C} -- (g-g) -> E;
  };
  
  % Place nodes, horizontal layout
  \node (c1) [contact, right=of g-g, label=\(c\)];
  \pic  (f1) [transform shape, rotate=90, yscale=-1, right=of c1] {gate};
  
  \node (c2) [contact, right=4*7.5mm of g-g, label=\(c\)];
  \node (s2) [sum, blue, right=of c2];
  \pic  (f2) at (s2) [transform shape, rotate=90, yscale=-1] {gate};

\end{tikzpicture}
\end{document}

enter image description here

5
  • By the way, have yo seen the circuitikz package? It has a bunch of symbols predefined. Nov 20 at 16:40
  • «I can't really follow what the output should be.» The expected output is the one on the right, where the element is placed to the right of c2. Namely, I'd like to choose where the element f-g is placed, which is the -g circle inside the gate pic. Similarly, on the left, I place the contact element according to g-g.
    – Atcold
    Nov 20 at 16:45
  • Yeah, I'm aware of the electronics package. I'm more interested in creating custom elements myself. So, here I was trying to figure out placement without hacks.
    – Atcold
    Nov 20 at 16:47
  • 1
    So, the basic problem is that right=of sets anchor=west and then you rotate the node which rotates it around this anchor, basically. That's why it is higher than you desire. We need another interface for rotation. (And the yscale thing I'd solve by toggling the arrow specification.) Nov 20 at 17:12
  • 1
    An alternative solution would be to use on grid (globally or at least for the gate pic). This will choose the center anchor both for placing the new node as well as the reference anchor. This will make it trivial creating and placing these gates but now the node distance is measured between the center of the nodes and not their borders. Nov 20 at 20:55

1 Answer 1

4

The root of your problem is that right=of sets the anchor=westand then you basically rotate the node around that anchor (because that's the anchor you place the node with at a specific position). I'll define anchor rotate that also sets rotate but also adjust the anchor to be rotated. The \tikz@polar@dir@… macros are used to reverse the anchor names like west and south east into values like 180 and 315.
(The macros are actually used for a polar coordinate like (south east:1) but oh well. The macro \tikz@on@text (which just expands to center) is also used to check whether the anchor is set to center to adjust the anchor.)

I've adjusted the gate pic a bit to not rely so much on hard-coded numbers regarding the size of the circle but instead use other means to find start or end of lines. Now, the hard-coded values are basically in relation to line width of the thick unnamed circle.

The point of outer sep=+.28pt is so that lines connecting to -g will stop just right before the thick circle. Similarly, the 1.3pt in the last two paths are used to get on the inner side of the thick circle. (This could also be solved by proper math but trigonometry is no fun.)

Code

\documentclass[tikz,border=6pt]{standalone}
\usetikzlibrary{graphs, arrows.meta, positioning}
\tikzset{
  neuron/.style ={% let's not set outer sep to zero
    shape=circle, very thick, minimum size=7.5mm, inner sep=+0pt, draw},
  contact/.style={
    neuron, minimum size=  2mm,   node contents=},
  sum/.style    ={
    neuron, minimum size=7.5mm/2, node contents=},
  node distance=7.5mm,
  % tip specification for gate pic:
  gate pic arrow/.tip={Latex[length=2pt]},
  pics/@gate/.style n args={3}{code={
    % place the auxiliary node with no path and guesstimated outer sep
    % but use anchor rotate which adjusts the given anchor
    % so that the node is rotated #1 around its center (and not the anchor)
    \node [name=-g, sum, path only, outer sep=+.28pt, anchor rotate={#1}];
    % shift the real circle in relation to #1
    \node at (-g.center) [anchor=center, sum, shift=(#1:-2pt)];
    % use the auxiliary node to draw the thick line → no calculation
    % shorten to offset outer sep of -g
    % an auxiliary coordinate is placed midway between them …
    \draw[shorten >=+.4pt, shorten <=+.4pt, thick]
      (-g.225) -- coordinate[midway] (@)(-g.135);
    % … which we use to connect the line from west → no calculation
    \draw (-g.west) -- (@);
    % line cap=round to hide imperfections
    % use #1 to specify shift for south/north
    %   [could have also used ($(-g.south)!1pt!(-g.center)$) or
    %                         ($(-g.north)!1pt!(-g.center)$)    ]
    % save that starting point and find intersection with thick line
    % by providing a direction → no calculation (yay)
    \draw[line cap=round, #2] ([shift=(#1+90:1.3pt)] -g.south) coordinate (@)
      -- (intersection of @--{[shift=(#1+135:1cm)]@} and -g.225---g.135);
    \draw[line cap=round, #3] ([shift=(#1-90:1.3pt)] -g.north) coordinate (@)
      -- (intersection of @--{[shift=(#1+225:1cm)]@} and -g.225---g.135);
  }},
  % user-interface: gate = <rotation> and reversed gate' = <rotation>
  pics/gate/.style ={@gate={#1}{               }{gate pic arrow-}},
  pics/gate'/.style={@gate={#1}{gate pic arrow-}{               }},
}
\makeatletter
\tikzset{
  anchor rotate/.code=% will fail with anchor set to base/mid or similar
    \tikzset{rotate={#1}}%
    \ifx\tikz@anchor\tikz@on@text % no change when set anchor is center
    \else
      \pgfutil@IfUndefined{tikz@polar@dir@\tikz@anchor}
        {\pgfmathsetmacro\tikz@anchor{\tikz@anchor-#1}}
        {\pgfmathsetmacro\tikz@anchor{%
          \csname tikz@polar@dir@\tikz@anchor\endcsname-#1}}%
    \fi}
\makeatother
\begin{document}
\begin{tikzpicture}
% Place nodes, vertical layout
\pic  (g) {gate=0};
\node (B) [contact, left=of g-g, label=\(b\)];
\node (E) [contact, above=of g-g, label=\(e\)];
\node (C) [contact, below=of g-g, label=below:\(c\)];

% Connect nodes
\graph [use existing nodes]{
  {B, C} -- (g-g) -> E;
};

% Place nodes, horizontal layout
\node (c1) [contact, right=of g-g, label=\(c\)];
\pic  (f1) [right=of c1] {gate'=90};

\draw[red, <->] (c1) -- (f1-g);
\end{tikzpicture}
\end{document}

Output

enter image description here

9
  • Of course, this assumes that the center anchor is actually in the center and the reverse function from anchor to angle is correct (which it isn't for a rectangle node), for a simple circle this will be the case though. Nov 20 at 19:03
  • Wow, I'm impressed. I'm still trying to understand what you wrote. 1. How did you know the pic was rotated on its anchor? 2. Why non-zero outer sep (I use it extensively throughout my book so that elements are placed like on a grid without keeping in consideration the border thickness)? 3. What's the @gate and @? 4. anchor rotate sets rotate before playing with the anchors, so I'm not sure how it works…
    – Atcold
    Nov 20 at 22:51
  • 1
    Not the pic is rotated, its content is. Not the pic is placed but its nodes are. The pic is just an interface to define a prefix here. If you specify rotate=90, anchor=west, the node will be rotated and placed with its west anchor on the at part. right=of … sets anchor=west, at=(….east), xshift=<node distance>. Just \node[draw] (O) at (0,0) {O}; \node[draw, right=of O, rotate=90] {Foobar};. (It might be easier to see this with rectangular nodes.) @gate is the “real” pic where the first argument is the rotation, and the other are options for the last two paths. Nov 20 at 23:15
  • 1
    But you would want to use gate and gate' which sets these two arguments so that you don't have to yscale=-1 anything. The following node also inherits right=of …. I overwrite this with using an explicit at, an explicit anchor and also shifting it by 2pt but dependent on the #1 rotation argument. The node has no name and will not be referenced later, so of course it has no influence. (I rather would connect the two last paths to the inside of this node but that will involve trigonometry or the intersections library to do it proper.) Nov 20 at 23:18
  • 1
    @Atcold You usually want an outer sep (usually of .5\pgflinewidth which is the default) so that lines connecting to these nodes just touch the border and not protrude it. This is certainly true for the contacts. The invisible node is a special case here but since it is invisible we can use an outer sep that makes it so that the other lines look good in relation to the thick circle. The @ coordinates are auxiliary coordinates (there are three different onces), no special reason for its name. Nov 20 at 23:24

You must log in to answer this question.

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