12

Inspired by a recent question on stackoverflow about how to draw maps of transportation networks, I'm wondering how to draw paths that consists of vertical, horizontal and diagonal segments.

Just like one can use |- to compose the path of vertical and horizontal segments, I'd like to have a method to automatically replace all square corners with diagonal lines (it is not important if the diagonal segment is at the start, end or in the middle of the path, all would be fine -- bonus points if the inclination if the diagonal segments remains constant)

For illustration: I'd like to automatically draw the red path in the following image without manually adding the break points

enter image description here

Points that have a larger vertical than horizontal distance could be connected like this:

enter image description here

MWE:

\documentclass{standalone}

\usepackage{tikz}

\begin{document}

\begin{tikzpicture}

\coordinate (Marmot) at (0,1);
\coordinate (Duck) at (2,0);

\draw (Marmot) -- (Duck);
\draw (Marmot) |- (Duck);

\draw[red] (Marmot) -- (0.5,1) -- (1.5,0) -- (Duck);

\node[rotate=45,anchor=west] at (Marmot) {Marmot Burrow};
\draw (Marmot) circle (0.05);

\node[rotate=45,anchor=west] at (Duck) {Duck Pond};
\draw (Duck) circle (0.05);

\end{tikzpicture}

\end{document}
11
  • 1
    The question is a bit unclear. Would you like to have the red path generated automatically just like the black ones? Commented Mar 7, 2019 at 10:16
  • 1
    @Superuser27 Yes, I'm looking for a way to automatically draw the red path - I tried to clarify the question. Commented Mar 7, 2019 at 10:20
  • Should the inclination be a modifiable parameter or do you always want an angle of say 45°?
    – AndréC
    Commented Mar 7, 2019 at 11:12
  • 1
    @koleygr I would be totally satisfied with 45 degree. Commented Mar 7, 2019 at 12:16
  • 2
    Welcome @semcarter...
    – koleygr
    Commented Mar 7, 2019 at 13:35

4 Answers 4

9

I felt like I want to slightly adapt this code to the somewhat more general requirements. This answer comes with a modified connect with angle style which can be used like this:

\draw[blue] (Marmot) to[connect with angle=-60] (Duck);

It does all the cases automatically, and is arguably more TikZy than writing a macro. This is also because you can combine such paths as in

\draw[orange] (Duck) to[connect with angle=125] (Marmot)
to[connect with angle=115] (Koala);

These paths can then define contours of something you want to fill, shade or clip against, or compute intersections with.

Here is the MWE.

\documentclass[border=3.14mm,tikz]{standalone}
\usepackage{tikzducks,tikzlings}
\usetikzlibrary{calc}
\tikzset{connect with angle/.style={to path={%
let \p1=(\tikztostart),\p2=(\tikztotarget),\n1={sin(#1-atan2(\y2-\y1,\x2-\x1))} in 
\ifdim\n1>0pt
-- ++(0,{((\y2-\y1)-(\x2-\x1)*tan(#1))/2}) 
-- ++({(\x2-\x1)},{(\x2-\x1)*tan(#1)})
-- (\tikztotarget)
\else
-- ++({((\x2-\x1)-(\y2-\y1)*cot(#1))/2},0) 
-- ++({(\y2-\y1)*cot(#1)},{\y2-\y1})
-- (\tikztotarget)
\fi}}}
\newsavebox{\Duck}
\newsavebox{\Koala}
\newsavebox{\Marmot}
\sbox{\Duck}{\tikz{\duck}}
\sbox{\Koala}{\tikz{\koala}}
\sbox{\Marmot}{\tikz{\marmot}}
\begin{document}
  \begin{tikzpicture}
    \coordinate[label=below:marmot] (Marmot) at (0,1);
    \coordinate[label=below:duck] (Duck) at (2,0);
    \coordinate[label=below:koala] (Koala) at (-2,3);
    \draw (Marmot) -- (Duck);
    \draw (Marmot) |- (Duck);
    \node[rotate=-10,anchor=south] at (Marmot) {\usebox\Marmot};
    \draw (Marmot) circle (0.05);
    \node[rotate=-45,anchor=south] at (Duck) {\usebox\Duck};
    \draw (Duck) circle (0.05);
    \node[rotate=10,anchor=south] at (Koala) {\usebox\Koala};
    \draw (Koala) circle (0.05);
    \draw[red] (Duck) to[connect with angle=135] (Marmot);
    \draw[blue] (Marmot) to[connect with angle=-60] (Duck);
    \draw[red] (Marmot) to[connect with angle=120] (Koala);
    \draw[blue] (Koala) to[connect with angle=-80] (Marmot);
    \draw[orange,fill=red,fill opacity=0.2] (Duck) to[connect with angle=125] (Marmot)
    to[connect with angle=115] (Koala) |-cycle;
  \end{tikzpicture}
\end{document}

enter image description here

1
7

Edit:

With 45 degrees angle (but if their angle is already 45,135,225 or 315 it will give a straight line)

I used the command of @AlanMatthes from here

\documentclass{standalone}

\usepackage{tikz}
\usetikzlibrary{calc}

\newcommand{\tikzAngleOfLine}{\tikz@AngleOfLine}
  \def\tikz@AngleOfLine(#1)(#2)#3{%
  \pgfmathanglebetweenpoints{%
    \pgfpointanchor{#1}{center}}{%
    \pgfpointanchor{#2}{center}}
  \pgfmathsetmacro{#3}{\pgfmathresult}%
  }


%Command \diagconnect
% #1 is the arguments of \draw command like, red, thick etc
% #2 is the optional argument of the fraction of the horizontal distance for the break according to the horizontal distance of the points
% #3 and #4 are the points to be connected
\newcommand{\diagconnect}[3][-,red,thick]{
\tikzAngleOfLine(#2)(#3){\Angle}
\xdef\PerfectAngle{0}
\foreach \x in {45,135,225,315} {\ifdim\dimexpr \Angle pt\relax=\dimexpr\x pt\relax\xdef\PerfectAngle{1}\fi}
\ifnum\PerfectAngle=0
\ifdim\dimexpr\Angle pt \relax< \dimexpr90 pt\relax\relax
\draw[#1] let\p1=(#2),\p2=(#3) in (#2)-- ($(#2)+({(\x2-\x1)-abs(\x2-\x1)/(\x2-\x1)*abs(\y2-\y1))/2},0)$)--($(#3)-({(\x2-\x1)-abs(\x2-\x1)/(\x2-\x1)*abs(\y2-\y1))/2},0)$)--(#3);
\else 
\draw[#1] let\p1=(#2),\p2=(#3) in (#2)-- ($(#2)+(0,{(\x2-\x1)-abs(\x2-\x1)/(\x2-\x1)*abs(\y2-\y1))/2})$)--($(#3)-(0,{(\x2-\x1)-abs(\x2-\x1)/(\x2-\x1)*abs(\y2-\y1))/2})$)--(#3);
\fi
\else
\draw[#1] (#2)--(#3);
\fi
}
\begin{document}

\begin{tikzpicture}

\node[rotate=45,anchor=west] (Point1) at (0,0){Point 1};
\node[rotate=45,anchor=west] (Point2) at (1,3) {Point 2};

\node[rotate=45,anchor=west] (Point3) at (4,0){Point 3};
\node[rotate=45,anchor=west] (Point4) at (7,1) {Point 4};

\diagconnect{Point1}{Point2};

\diagconnect{Point3}{Point4}

\node[rotate=45,anchor=west] (Point5) at (0,-4){Point 5};
\node[rotate=45,anchor=west] (Point6) at (2,-6) {Point 6};

\node[rotate=45,anchor=west] (Point7) at (4,-4){Point 7};
\node[rotate=45,anchor=west] (Point8) at (5,-7) {Point 8};

\diagconnect{Point5}{Point6};

\diagconnect{Point7}{Point8}

\node[rotate=45,anchor=west] (Point9) at (9,0){Point 9};
\node[rotate=45,anchor=west] (Point10) at (9,2) {Point 10};

\node[rotate=45,anchor=west] (Point11) at (7,-4){Point 11};
\node[rotate=45,anchor=west] (Point12) at (9,-4) {Point 12};

\diagconnect{Point9}{Point10};

\diagconnect{Point11}{Point12}
\end{tikzpicture}

\end{document}

enter image description here

Old answer (but useful in other cases):

You can define a newcommand with an extra argument (except the \draw options that is optional and the two points that are required) that will be the fraction of the horizontal distance of the points that you want this "break".

\documentclass{standalone}

\usepackage{tikz}
\usetikzlibrary{calc}
%Command \diagconnect
% #1 is the arguments of \draw command like, red, thick etc
% #2 is the optional argument of the fraction of the horizontal distance for the break according to the horizontal distance of the points
% #3 and #4 are the points to be connected
\newcommand{\diagconnect}[4][]{\draw[#1] let\p1=(#3),\p2=(#4) in (#3)-- ($(#3)+({abs(\x2-\x1)/(\x2-\x1)*abs(\x2-\x1)*#2},0)$)--($(#4)-({abs(\x2-\x1)/(\x2-\x1)*abs(\x2-\x1)*#2},0)$)--(#4);}
\begin{document}

\begin{tikzpicture}

\coordinate (Marmot) at (0,1);
\coordinate (Duck) ate (2,0);

\draw (Marmot) -- (Duck);
\draw (Marmot) |- (Duck);

%\draw[red] (Marmot) -- (0.5,1) -- (1.5,0) -- (Duck);
\diagconnect[thick,red]{0.3}{Marmot}{Duck}
\node[rotate=45,anchor=west] at (Marmot) {Marmot Burrow};
\draw (Marmot) circle (0.05);

\node[rotate=45,anchor=west] at (Duck) {Duck Pond};
\draw (Duck) circle (0.05);

\end{tikzpicture}

\end{document}

Output:

enter image description here

3
  • Nice idea! Can the angle of the diagonal line be fixed to 45 degree (if the points are further apart the horizontal lines can be longer)? Commented Mar 7, 2019 at 11:08
  • Sorry, Didn't notice that you want the degree as an argument... but for smaller horizontal distance than vertical (between this points) it will going back before go diagonal... Are you sure you want something like this? Edit: May be you want the line to start vertical in this case... Working on it
    – koleygr
    Commented Mar 7, 2019 at 11:15
  • Please don't apologise, my question was not that clear. For smaller horizontal distances I would like to a connection like the following sketch: i.sstatic.net/3VWIG.png Commented Mar 7, 2019 at 11:20
4

Based on @koleygr 's answer I changed the code a bit to always draw a 45° connection and adapt to which point is more left/right in the picture (or higher/lower). Unfortunately I couldn't come up with a way to do it horizontally and vertically in an automated fashion, but I provided the two codes for \hconnect and \vconnect. Maybe someone more experienced with macros and conditional statements can figure out a way to do it even better :)

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{calc}
\newcommand{\vconnect}[3][]{
    \draw[#1] let\p1=(#2),\p2=(#3) in (#2)-- ($(#2)+(0,{((\y2-\y1)/abs(\y2-\y1))*(abs(\y2-\y1)-abs(\x2-\x1))*0.5})$)--($(#3)-(0,{((\y2-\y1)/abs(\y2-\y1))*(abs(\y2-\y1)-abs(\x2-\x1))*0.5})$)--(#3);
}
\newcommand{\hconnect}[3][]{
    \draw[#1] let\p1=(#2),\p2=(#3) in (#2)-- ($(#2)+({((\x2-\x1)/abs(\x2-\x1))*(abs(\x2-\x1)-abs(\y2-\y1))*0.5},0)$)--($(#3)-({((\x2-\x1)/abs(\x2-\x1))*(abs(\x2-\x1)-abs(\y2-\y1))*0.5},0)$)--(#3);
}

\begin{document}

    \begin{tikzpicture}

    \coordinate (Marmot) at (0,2);
    \coordinate (Duck) at (1,0);
    \coordinate (Deer) at (2,3);

%   \draw (Marmot) -- (Duck);
%   \draw (Marmot) |- (Duck);

    %\draw[red] (Marmot) -- (0.5,1) -- (1.5,0) -- (Duck);
    \vconnect[thick,red]{Marmot}{Duck}
    \hconnect[thick,red]{Marmot}{Deer}


    \node[rotate=45,anchor=west] at (Marmot) {Marmot Burrow};
    \draw (Marmot) circle (0.05);

    \node[rotate=45,anchor=west] at (Deer) {Deer Lake};
    \draw (Deer) circle (0.05);

    \node[rotate=45,anchor=west] at (Duck) {Duck Pond};
    \draw (Duck) circle (0.05);

    \end{tikzpicture}

\end{document}

Produces:

enter image description here

0
4
+50

If you only connect named coordinates (as you do) you can very easily use transformations and normal orthogonal path operations like -| and |- as well as |-| and -|-.

Code

\documentclass[tikz, convert]{standalone}
\usetikzlibrary{ext.paths.ortho}
\tikzset{
  vertical   slanted by/.style={xslant=tan(-(#1))},
  horizontal slanted by/.style={yslant=tan(#1)}}
\begin{document}
\begin{tikzpicture}[
  Label/.style={label={[rotate=45, anchor=west]above right:{#1}}},
  dot/.style={circle, inner sep=+.35mm, draw}
]
\coordinate[dot, Label = Marmot Burrow] (Marmot) at (0,1);
\coordinate[dot, Label = Duck Pond]     (Duck)   at (2,0);

\draw (Marmot) -- (Duck);
\draw (Marmot) |- (Duck);

\draw[red] (Marmot) -- (0.5,1) -- (1.5,0) -- (Duck);

\begin{scope}[dashed, thick]
  \draw[blue,  vertical slanted by = 45] (Marmot) -|- (Duck);
  \draw[green, vertical slanted by = 30] (Marmot) |-| (Duck);
\end{scope}
\draw[densely dashed, thick, yellow!50!black, rounded corners,
   horizontal slanted by = 15] (Duck) |- (Marmot);
\end{tikzpicture}
\end{document}

Output

enter image description here

1

You must log in to answer this question.

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