# Discretize line directions in Tikz

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

• You explain how to build a horizontal/vertical lines, but can you provide an example of diagonal lines. – Peter Grill Feb 17 '14 at 2:39
• Updated the question, is it clear now? – Willem Van Onsem Feb 17 '14 at 2:44

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}
\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}


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
% 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}