Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

In a printed circuit board (pcb) layout, the lines can only direct horizontally, vertically and diagonally. Such that every line is a combination of a horizontal/vertical part combined with a diagonal part.

I am looking for a way to implement this knowledge in Tikz. This means that when one for instance draws a line:

\draw (0,0) -- ++(5,2);

This should be drawn as:

\draw (0,0) -- ++(3,0) -- ++(2,2);

Where the horizontal and vertical direction always get priority over the diagonal one. Of course this can easily be implemented when the coordinates are given explicitly. But when for instance the coordinates have names, I don't see a way to do this.

Algorithmically the following should be done: say the two points are (x1,y1) and (x2,y2)

  1. calculate the difference (dx,dy)
  2. if abs(dx) >= abs(dy) do the following
  3. move (x1,y1) line (x1+sgn(dx)*(abs(dx)-abs(dy)),y1) and line (x2,y2).

if abs(dx) < abs(dy) simply swap the x-axis with the y-axis.

If the original line is a perfect diagonal, the result is a diagonal as well (since the horizontal part has a length of zero). In case the line is perfectly horizontal/vertical, the resulting line is horizontal/vertical as well (since the diagonal part has a length equal to zero).

share|improve this question
1  
You explain how to build a horizontal/vertical lines, but can you provide an example of diagonal lines. –  Peter Grill Feb 17 at 2:39
    
Updated the question, is it clear now? –  CommuSoft Feb 17 at 2:44
add comment

2 Answers 2

up vote 11 down vote accepted

The following example defines the macro \drawcircuitline that takes one optional argument for options of draw and two mandatory arguments, the coordinates, either explicit or as node name, in both cases without parentheses. The calculations need the function sign that seems to be missing. Therefore it is defined via \pgfmathdeclarefunction:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc}

\makeatletter
\pgfmathdeclarefunction{sign}{1}{%
  \begingroup
    \ifdim#1pt>\z@
      \def\pgfmathresult{1}%
    \else
      \ifdim#1pt=\z@
        \def\pgfmathresult{0}%
      \else
        \def\pgfmathresult{-1}%
      \fi
    \fi
    \pgfmath@smuggleone\pgfmathresult
  \endgroup
}
\makeatother

\newcommand*{\drawcircuitline}[3][]{%
  \draw[{#1}]
    let \p1=($(#3)-(#2)$),
        \n2={abs(\x1) - abs(\y1)},
        \n3={(\n2 > 0) * sign(\x1) * \n2},
        \n4={(\n2 < 0) * sign(\y1) * (-\n2)},
        \p{m}=(\n{3}, \n{4})
    in
    (#2) -- ++(\p{m}) -- (#3);
}

\begin{document}
  \begin{tikzpicture}[thick]
    \draw[help lines] (-6,-6) grid (6,6);
    \foreach \x/\y in { 4/0, -4/0, 0/4, 0/-4,
         1/1, -1/1, -1/-1, 1/-1, 5/5, -5/5, -5/-5, 5/-5,
         6/3, 3/6, -3/6, -6/3, -6/-3, -3/-6, 3/-6, 6/-3}
      \fill (\x,\y) circle[radius=3pt];
    \coordinate (a) at (1,1);
    \coordinate (b) at (6,3);
    \drawcircuitline{a}{b}
    \drawcircuitline{a}{5,5}
    \drawcircuitline{a}{3,6}
    \drawcircuitline{-1,1}{-3,6}
    \drawcircuitline{-1,1}{-5,5}
    \drawcircuitline{-1,1}{-6,3}
    \drawcircuitline{-1,-1}{-6,-3}
    \drawcircuitline{-1,-1}{-5,-5}
    \drawcircuitline{-1,-1}{-3,-6}
    \drawcircuitline{1,-1}{3,-6}
    \drawcircuitline{1,-1}{5,-5}
    \drawcircuitline{1,-1}{6,-3}
    \drawcircuitline{-4,0}{4,0}
    \drawcircuitline{0,4}{0,-4}
  \end{tikzpicture}
\end{document}  

Result

share|improve this answer
add comment

With a bit of lower level magic a pcb trace "to path" can be created. The starred version (pcb trace*) starts the diagonal part of the path from the opposite end to the un-starred version:

\documentclass[tikz,border=5]{standalone}
\begin{document}
\makeatletter
\tikzset{%
  pcb/.style={
    draw=black,
    ultra thick,
    rounded corners=1ex,
  },
  pcb node/.style={%
    insert path={
      node [shape=circle, fill=white, draw, ultra thick, inner sep=2pt] {}
    }
  },
  pcb trace/.style={%
    to path={%
      \pgfextra{%
        % Get start point
        \tikz@scan@one@point\pgf@process(\tikztostart)%
        \pgf@xc=\pgf@x\pgf@yc=\pgf@y%
        % Get end point
        \tikz@scan@one@point\pgf@process(\tikztotarget)%
        \pgf@xb=\pgf@x\pgf@yb=\pgf@y%
        % Get difference between points
        \advance\pgf@x by-\pgf@xc%
        \advance\pgf@y by-\pgf@yc%
        % Get absolute values
        \pgf@xa=\ifdim0pt>\pgf@x-\fi\pgf@x%
        \pgf@ya=\ifdim0pt>\pgf@y-\fi\pgf@y%
        % For entirely horizontal, vertical and diagonal lines: do nothing.
        \ifdim\pgf@xa=0pt\else%
          \ifdim\pgf@ya=0pt\else%
            \ifdim\pgf@xa=\pgf@ya\else%
              \c@pgf@counta=\ifdim0pt>\pgf@x-\fi1\relax%
              \c@pgf@countb=\ifdim0pt>\pgf@y-\fi1\relax%
              \pgfpathlineto{%
                \ifdim\pgf@xa<\pgf@ya%
                  \ifx#1*%
                    \pgfpoint{\pgf@xb}{\pgf@yc+\c@pgf@countb\pgf@xa}%
                  \else%
                    \pgfpoint{\pgf@xc}{\pgf@yb-\c@pgf@countb\pgf@xa}%
                  \fi%
                \else%
                  \ifx#1*%
                    \pgfpoint{\pgf@xc+\c@pgf@counta\pgf@ya}{\pgf@yb}%
                  \else%
                    \pgfpoint{\pgf@xb-\c@pgf@counta\pgf@ya}{\pgf@yc}%
                  \fi%
                \fi%
              }%
            \fi%
          \fi%
        \fi%
      } 
      -- (\tikztotarget)
    }
  },
  pcb trace*/.style={pcb trace=*}
} 

\begin{tikzpicture}

\foreach \i in {-3,...,3}{
  \path [pcb]  
  (\i*2,7) [pcb node] to [pcb trace*] (\i,0) to [pcb trace] (\i*2,-7) [pcb node];
 \path [pcb]
   (7, \i*2) [pcb node] to [pcb trace*] (0,\i) to [pcb trace] (-7,\i*2) [pcb node];
}

\end{tikzpicture}
\end{document}

enter image description here

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.