5

I am trying to create a process flow diagram of my rig using LaTex but I am stuck at trying to figure out how to create a semicircular jump at the intersection of two independent lines in the drawing. My current code and output are as follows.

\documentclass[11pt]{standalone}
\usepackage{lmodern}
\renewcommand*{\familydefault}{\sfdefault}
\usepackage{sfmath}
\usepackage[version=4]{mhchem}
\usepackage{tikz} 
\usetikzlibrary{arrows.meta,fit,positioning,intersections,shapes.geometric,
decorations,patterns,decorations.markings}
\tikzset{
every path/.style={thick},
every node/.style={
    font={\normalsize},
    align=center,
    transform shape
    },
}
\begin{document}
\begin{tikzpicture}[node distance=0.75cm]
    \tikzset{
        sv/.pic={
            \def \x {0.43}
            \node[draw,isosceles triangle,fill=none] (a) at (-\x,0) {};
            \node[draw,isosceles triangle,shape border rotate=180,fill=none] at (\x,0){};
            \draw[fill=none] (0,0) circle (0.22);   
        },%shutoff valve
        lifwv/.pic={
            \def \x {1pt}
            \node[draw,circle,minimum size=0.6cm] (a) at (0,0){};
            \node[circle,minimum size=0.4cm] (b) at (0.1,0){};
            \fill (b.90) circle (\x) (b.0) circle (\x) (b.180) circle (\x) (b.270) circle (\x);
            \draw[->] (a.90) -- ++(0,0.5) -- ++(0.5,0) node[right,ellipse,draw,inner sep=0.5pt](mv){M.V.};
            \draw[->] (a.270) --  ++(0,-0.5) -- node[pos=0.5,nosep] (evap){} ++(1,0) -- ++(0,0.815) -- (a.0);
            \node[below right = of evap,draw,yshift=0.25cm] (syp){Pump};
            \draw[->] (syp) -|(evap);           
        },%liquid injection right 4-way valve
        pt/.pic={
            \draw (0,0) -- ++(0.25,0) node[right,draw,circle,minimum size=0.5cm,font=\small,inner sep=0pt](a){P};
        },%pressure transducer
        jump/.pic={
            \filldraw[draw=black,fill=white] (0.15,0) arc (0:180:0.15);
            \draw[red,dashed] (0,0) circle (0.5);
        },
        mfc/.style={
            draw,minimum width=1.75cm,minimum height=0.9cm,
            text height=1.5ex
        },
        nosep/.style={
            inner sep=0pt,
            outer sep=0pt,  
        },%meeting-point
    }
    \node[mfc] (mfc1) {a};
    \node[below = of mfc1,mfc] (mfc2) {\ce{b}};
    \node[below = of mfc2,mfc] (mfc3) {\ce{c}};
    \node[below = of mfc3,mfc] (mfc4) {\ce{d}};
    \node[below = of mfc4,mfc] (mfc5) {\ce{e}};
    \node[below = of mfc5,mfc] (mfc6) {\ce{f}};
    \foreach \y in {1,2,...,6}{
        \draw[->] (mfc\y.east) --++(0.5,0) node[right=12pt] (svmfcc\y){} node[right=24pt](svmfcr\y){};
        \pic at (svmfcc\y){sv}; 
    };
    %line-1
    \draw[->] (svmfcr1) -- node[pos=0.45,nosep](mpl1){} ++(1.5,0) -- pic[pos=0.4,-]{pt}++(0,0.75) -- ++(2,0) pic[right]{lifwv} node[right](l1){};
    \foreach \y in {3,5,6} {
        \draw[->] (svmfcr\y) -- ++(0.75,0); 
    };
    \draw[->] (svmfcr6) ++(0.75,0) -- (mpl1);
    %line-2
    \draw[->] (svmfcr2) -- node[pos=0.75,nosep] (mpl2){} ++(1.5,0) -- pic[-,pos=0.4]{pt}++(0,-0.75) -- ++(2,0) pic[right]{lifwv} node[right](l2){};
    \draw[->] (svmfcr4) -- ++(1.16,0) -- (mpl2);
\draw[->] (svmfcr4) -- pic[pos=0.6,-]{jump} ++(1.16,0) -- (mpl2);
\end{tikzpicture}   
\end{document} 

pfd

I know one way is to define a semicircular pic with a white fill and overlay it at the intersection (as implemented in the current code and highlighted with a red dashed circle). But ideally, I would like to implement a jump with no fill and define it as a marking decoration on the line, just like an arrow decoration that can be defined using decorations.markings library. Is there a way to implement this?

2 Answers 2

5

The first part is the original answer, at the bottom a more flexible implementation can be found.


Here's a different approach, using a custom to path. Add \usetikzlibrary{calc}, and define jump as

jump/.style={
     to path={
         let \p1=(\tikztostart),\p2=(\tikztotarget),\n1={atan2(\y2-\y1,\x2-\x1)} in
         (\tikztostart) -- ($($(\tikztostart)!#1!(\tikztotarget)$)!0.15cm!(\tikztostart)$)
         arc[start angle=\n1+180,end angle=\n1,radius=0.15cm] -- (\tikztotarget)}
},
jump/.default={0.5}

Use this as

\draw (a) to[jump] (b);

The optional argument to the style defines where along the line the bump occurs, default is halfway. Minimal example:

\documentclass[border=5mm]{standalone}
\usepackage{tikz}
\usetikzlibrary{calc}
\tikzset{
  jump/.style={
     to path={
         let \p1=(\tikztostart),\p2=(\tikztotarget),\n1={atan2(\y2-\y1,\x2-\x1)} in
         (\tikztostart) -- ($($(\tikztostart)!#1!(\tikztotarget)$)!0.15cm!(\tikztostart)$)
         arc[start angle=\n1+180,end angle=\n1,radius=0.15cm] -- (\tikztotarget)}
  },
  jump/.default={0.5}
}
\begin{document}
\begin{tikzpicture}
\fill [blue!10] (-0.5,-0.5) rectangle (2.5,1.5);
\draw (0,1) to[jump] ++(0,-1) to[jump=0.2] ++(2,0) to[jump=0.7] ++(-1,1);
\end{tikzpicture}
\end{document}

Which produces this:

output of above code

It might not be immediately obvious what happens, so some explanations might be in order:

I suggest reading section 14.13 The To Path Operation in the TikZ manual (for version 3.0.1a), if you know nothing about to paths.

The to path key is used to create a custom path between two points. In that context, the macros \tikztostart and \tikztotarget represent, as expected, the start and end point. So in \draw (a) to (b);, \tikztostart is a, and \tikztotarget is b.

The let part at the start is used to calculate the angle between the start and end, which is saved in \n1. This syntax is described in section 14.15 The Let Operation in the manual.

The next few lines define the line that is drawn itself, and the structure is (a) -- (b) arc[...] -- (c).

The second of those coordinates is defined using the syntax of the calc library, described in section 13.5 Coordinate Calculations in the manual:

($($(\tikztostart)!#1!(\tikztotarget)$)!0.15cm!(\tikztostart)$)

This is a nested coordinate calculation. #1 represents the argument to the style, with default value 0.5. So by default ($(\tikztostart)!#1!(\tikztotarget)$) is the coordinate that is halfway between start and end. The syntax ($(a)!0.15cm!(b)$) means the point that is 0.15cm away from a, towards b. Hence the total effect of the complete calculation, is to get the coordinate that is 0.15cm from the halfway point, back towards the start.

From that point, draw the arc using the calculated angle, and the same 0.15cm as radius:

arc[start angle=\n1+180,end angle=\n1,radius=0.15cm]

and finish off by drawing the last straight line, -- (\tikztotarget);.

Implemented in your (not so minimal) example:

\documentclass[11pt]{standalone}
\usepackage{lmodern}
\renewcommand*{\familydefault}{\sfdefault}
\usepackage{sfmath}
\usepackage[version=4]{mhchem}
\usepackage{tikz} 
\usetikzlibrary{arrows.meta,fit,positioning,intersections,shapes.geometric,
decorations,patterns,decorations.markings,
calc % <-- added
} 

\tikzset{
every path/.style={thick},
every node/.style={
    font={\normalsize},
    align=center,
    transform shape
    },
}
\begin{document}
\begin{tikzpicture}[
        node distance=0.75cm,
        sv/.pic={
            \def \x {0.43}
            \node[draw,isosceles triangle,fill=none] (a) at (-\x,0) {};
            \node[draw,isosceles triangle,shape border rotate=180,fill=none] at (\x,0){};
            \draw[fill=none] (0,0) circle (0.22);   
        },%shutoff valve
        lifwv/.pic={
            \def \x {1pt}
            \node[draw,circle,minimum size=0.6cm] (a) at (0,0){};
            \node[circle,minimum size=0.4cm] (b) at (0.1,0){};
            \fill (b.90) circle (\x) (b.0) circle (\x) (b.180) circle (\x) (b.270) circle (\x);
            \draw[->] (a.90) -- ++(0,0.5) -- ++(0.5,0) node[right,ellipse,draw,inner sep=0.5pt](mv){M.V.};
            \draw[->] (a.270) --  ++(0,-0.5) -- node[pos=0.5,nosep] (evap){} ++(1,0) -- ++(0,0.815) -- (a.0);
            \node[below right = of evap,draw,yshift=0.25cm] (syp){Pump};
            \draw[->] (syp) -|(evap);           
        },%liquid injection right 4-way valve
        pt/.pic={
            \draw (0,0) -- ++(0.25,0) node[right,draw,circle,minimum size=0.5cm,font=\small,inner sep=0pt](a){P};
        },%pressure transducer
        jump/.style={
            to path={
             let \p1=(\tikztostart),\p2=(\tikztotarget),\n1={atan2(\y2-\y1,\x2-\x1)} in
             (\tikztostart) -- ($($(\tikztostart)!#1!(\tikztotarget)$)!0.15cm!(\tikztostart)$)
             arc[start angle=\n1+180,end angle=\n1,radius=0.15cm] -- (\tikztotarget)}
        },
        jump/.default={0.5},
        mfc/.style={
            draw,minimum width=1.75cm,minimum height=0.9cm,
            text height=1.5ex
        },
        nosep/.style={
            inner sep=0pt,
            outer sep=0pt,  
        },%meeting-point
]


    \node[mfc] (mfc1) {a};
    \node[below = of mfc1,mfc] (mfc2) {\ce{b}};
    \node[below = of mfc2,mfc] (mfc3) {\ce{c}};
    \node[below = of mfc3,mfc] (mfc4) {\ce{d}};
    \node[below = of mfc4,mfc] (mfc5) {\ce{e}};
    \node[below = of mfc5,mfc] (mfc6) {\ce{f}};
    \foreach \y in {1,2,...,6}{
        \draw[->] (mfc\y.east) --++(0.5,0) node[right=12pt] (svmfcc\y){} node[right=24pt](svmfcr\y){};
        \pic at (svmfcc\y){sv}; 
    };
    %line-1
    \draw[->] (svmfcr1) -- node[pos=0.45,nosep](mpl1){} ++(1.5,0) -- pic[pos=0.4,-]{pt}++(0,0.75) -- ++(2,0) pic[right]{lifwv} node[right](l1){};
    \foreach \y in {3,5,6} {
        \draw[->] (svmfcr\y) -- ++(0.75,0); 
    };
    \draw[->] (svmfcr6) ++(0.75,0) -- (mpl1);
    %line-2
    \draw[->] (svmfcr2) -- node[pos=0.75,nosep] (mpl2){} ++(1.5,0) -- pic[-,pos=0.4]{pt}++(0,-0.75) -- ++(2,0) pic[right]{lifwv} node[right](l2){};

    \draw[->] (svmfcr4) to[jump=0.65] ++(1.16,0) -- (mpl2);
\end{tikzpicture}   
\end{document} 

output of above code

More flexible approach

Here is a variation that lets you choose between -- paths and -|/|- paths, as well as setting the position, radius and orientation of the jump.

Style names can of course be changed. By default, to[jump] draws a straight line with the jump in the middle. Use for example to[jump={-u|}] to get a -| path with the jump in the horizontal part of the path. More examples in the code, with comments.

output of code

\documentclass[border=5mm]{standalone}
\usepackage{tikz}
\usetikzlibrary{calc}

% initial value
\pgfmathsetmacro{\jumpswap}{1}
\tikzset{
  % set up keys for radius, position, swap
  jump radius/.estore in=\jumpradius,
  jump pos/.estore in=\jumppos,
  jump swap/.code={\pgfmathsetmacro{\jumpswap}{\jumpswap*-1}},
  jump radius=0.15cm,
  jump pos=0.5,
  % set up styles for the various to-paths
  -u-/.style={ % straight line
     to path={
         let \p1=(\tikztostart),\p2=(\tikztotarget),\n1={atan2(\y2-\y1,\x2-\x1)} in
         (\p1) -- ($($(\p1)!\jumppos!(\p2)$)!\jumpradius!(\p1)$)
         arc[start angle=\n1+180,delta angle=-180*\jumpswap,radius=\jumpradius] -- (\p2)}
  },
  -u|/.style={ % -| path with jump on horizontal leg
     to path={
         let \p1=(\tikztostart),\p2=(\tikztostart-|\tikztotarget), \n1={atan2(\y2-\y1,\x2-\x1)} in
         (\p1) -- ($($(\p1)!\jumppos!(\p2)$)!\jumpradius!(\p1)$)
         arc[start angle=\n1+180,delta angle=-180*\jumpswap,radius=\jumpradius] --(\p2) -- (\tikztotarget)}
     },
  |u-/.style={ % |- path with jump on vertical leg
     to path={
         let \p1=(\tikztostart),\p2=(\tikztostart|-\tikztotarget), \n1={atan2(\y2-\y1,\x2-\x1)} in
         (\p1) -- ($($(\p1)!\jumppos!(\p2)$)!\jumpradius!(\p1)$)
         arc[start angle=\n1+180,delta angle=-180*\jumpswap,radius=\jumpradius] -- (\p2) -- (\tikztotarget)}
     },
  -|u/.style={ % -| path with jump on vertical leg
     to path={
         let \p1=(\tikztostart-|\tikztotarget),\p2=(\tikztotarget), \n1={atan2(\y2-\y1,\x2-\x1)} in
         (\tikztostart) -- (\p1) -- ($($(\p1)!\jumppos!(\p2)$)!\jumpradius!(\p1)$)
         arc[start angle=\n1+180,delta angle=-180*\jumpswap,radius=\jumpradius] -- (\p2)}
     },
  |-u/.style={ % |- path with jump on horizontal leg
     to path={
         let \p1=(\tikztostart|-\tikztotarget),\p2=(\tikztotarget), \n1={atan2(\y2-\y1,\x2-\x1)} in
         (\tikztostart) -- (\p1) -- ($($(\p1)!\jumppos!(\p2)$)!\jumpradius!(\p1)$)
         arc[start angle=\n1+180,delta angle=-180*\jumpswap,radius=\jumpradius] -- (\p2)}
     },
  % define the jump style, set it to use straight line by default
  jump/.style={-u-,#1},
  jump/.default={}
}
\begin{document}
\begin{tikzpicture}[
  % use nodes with this style to highlight where segments meet
  dot/.style={node contents={},inner sep=1pt,circle,fill=red}
]
\fill [blue!10] (-0.5,-2.5) rectangle (5.5,1.5);
\draw (0,0)
  % default bump
  to[jump]                                   ++(1,0)   node[dot]
  % place bump on other side of line
  to[jump={jump swap}]                       ++(1,0)   node[dot]  
  % -| path, larger bump on horizontal leg
  to[jump={-u|,jump radius=0.5cm}]           ++(2,1)   node[dot]  
  % -| path, bump at pos=0.7 along vertical leg
  to[jump={-|u,jump pos=0.7}]                ++(1,-1)  node[dot]  
  % |- path, bump along vertical leg
  to[jump={|u-}]                             ++(-2,-1) node[dot] 
  % |- path, larger bump on horizontal leg, other side of line
  to[jump={|-u,jump radius=0.5cm,jump swap}] ++(-3,-1) node[dot]
  to[jump]                                   ++(1,1)   node[dot]
  to[jump={jump radius=3mm}]                 ++(-1,1)  node[dot]; 
\end{tikzpicture}
\end{document}
5
  • That was a very insightful approach and thanks for the elaborate explanation. I look forward to using the to paths more often in the future for these kinds of situations. Nov 6, 2017 at 0:50
  • To add to this solution, can it also be implemented in situations where one uses -| or |- operation to draw perpendicular paths or in other words, is there an equivalent style option that can be passed to the to operation alongside jump in order to draw the perpendicular paths? Nov 6, 2017 at 1:07
  • @scorpionwars First thing that comes to mind: Probably have to make a separate style for that, with a different definition of the to-path. I.e. the path should have the structure (a) -- (b) arc[..] (a-|c) -- (c). (Alternatively with |-, and alternatively with (a-|c) before (b), depending on the case.) Nov 6, 2017 at 1:24
  • @scorpionwars See the last addition to the answer. Nov 6, 2017 at 12:01
  • That's exactly I was looking for, thanks again! Nov 6, 2017 at 19:43
4

idea: draw in two steps:

  • first determine position of jump
  • then draw to jump and then from jum further

for this you need to redesign pic jump. see demo below:

\documentclass[tikz, margin=3mm]{standalone}

\begin{document}
\begin{tikzpicture}[
        jump/.pic = {
            \draw[line cap=rect]  (0.15,0)    coordinate (-out)
                          arc (0:180:0.15)    coordinate (-in);
                    },
                    ]
\draw[red]  (1.5,-1) -- + (0,2);
\path (0,0) -- pic (aux) {jump} (3,0);
\draw (0,0) -- (aux-in) (aux-out) -- (3,0);
\end{tikzpicture}
\end{document}

enter image description here

above idea integrated in your mwe:

\documentclass[11pt]{standalone}
\usepackage{lmodern}
\renewcommand*{\familydefault}{\sfdefault}
\usepackage{sfmath}
\usepackage[version=4]{mhchem}
\usepackage{tikz}
\usetikzlibrary{arrows.meta,fit,positioning,intersections,shapes.geometric,
decorations,patterns,decorations.markings}
\tikzset{
every path/.style={thick},
every node/.style={
    font={\normalsize},
    align=center,
    transform shape
    },
}
\begin{document}
\begin{tikzpicture}[node distance=0.75cm]
\tikzset{
    sv/.pic={
        \def \x {0.43}
        \node[draw,isosceles triangle,fill=none] (a) at (-\x,0) {};
        \node[draw,isosceles triangle,shape border rotate=180,fill=none] at (\x,0){};
        \draw[fill=none] (0,0) circle (0.22);
    },%shutoff valve
    lifwv/.pic={
        \def \x {1pt}
        \node[draw,circle,minimum size=0.6cm] (a) at (0,0){};
        \node[circle,minimum size=0.4cm] (b) at (0.1,0){};
        \fill (b.90) circle (\x) (b.0) circle (\x) (b.180) circle (\x) (b.270) circle (\x);
        \draw[->] (a.90) -- ++(0,0.5) -- ++(0.5,0) node[right,ellipse,draw,inner sep=0.5pt](mv){M.V.};
        \draw[->] (a.270) --  ++(0,-0.5) -- node[pos=0.5,nosep] (evap){} ++(1,0) -- ++(0,0.815) -- (a.0);
        \node[below right = of evap,draw,yshift=0.25cm] (syp){Pump};
        \draw[->] (syp) -|(evap);
    },%liquid injection right 4-way valve
    pt/.pic={
        \draw (0,0) -- ++(0.25,0) node[right,draw,circle,minimum size=0.5cm,font=\small,inner sep=0pt](a){P};
    },%pressure transducer
%---------------------------------------------------------------%
    jump/.pic = {                                   %%%% changed
        \draw[line cap=rect]  (0.15,0)      coordinate (-out)
                          arc (0:180:0.15)  coordinate (-in);
                },
%---------------------------------------------------------------%
    mfc/.style={
        draw,minimum width=1.75cm,minimum height=0.9cm,
        text height=1.5ex
    },
    nosep/.style={
        inner sep=0pt,
        outer sep=0pt,
    },%meeting-point
}
\node[mfc] (mfc1) {a};
\node[below = of mfc1,mfc] (mfc2) {\ce{b}};
\node[below = of mfc2,mfc] (mfc3) {\ce{c}};
\node[below = of mfc3,mfc] (mfc4) {\ce{d}};
\node[below = of mfc4,mfc] (mfc5) {\ce{e}};
\node[below = of mfc5,mfc] (mfc6) {\ce{f}};
\foreach \y in {1,2,...,6}{
    \draw[->] (mfc\y.east) --++(0.5,0) node[right=12pt] (svmfcc\y){} node[right=24pt](svmfcr\y){};
    \pic at (svmfcc\y){sv};
};
%line-1
\draw[->] (svmfcr1) -- node[pos=0.45,nosep](mpl1){} ++(1.5,0) -- pic[pos=0.4,-]{pt}++(0,0.75) -- ++(2,0) pic[right]{lifwv} node[right](l1){};
\foreach \y in {3,5,6} {
    \draw[->] (svmfcr\y) -- ++(0.75,0);
};
\draw[->] (svmfcr6) ++(0.75,0) -- (mpl1);
%line-2
\draw[->] (svmfcr2) -- node[pos=0.75,nosep] (mpl2){} ++(1.5,0) -- pic[-,pos=0.4]{pt}++(0,-0.75) -- ++(2,0) pic[right]{lifwv} node[right](l2){};
%---------------------------------------------------------------%
\path   (svmfcr4) -- pic[pos=0.6] (aux) {jump} ++(1.16,0);  %%%% added
\draw[->] (svmfcr4) -- (aux-in) (aux-out) -| (mpl2);        %%%% changed
%---------------------------------------------------------------%
\end{tikzpicture}
\end{document}

enter image description here

2
  • Thanks for sharing the approach. I was thinking of implementing something similar but didn't realize that you could actually call nodes/coordinates defined within a pic outside of its definition by using the two identifiers, local and global. I still, however, don't quite fully understand the role of line cap=rect style option used in the drawing the arc. Is it there for a more smoother intersection of the arc path and straight line path drawn to and from the arc ends? Nov 6, 2017 at 1:01
  • 1
    line cap=rect make connection between line and pic "joined" (sine it prolong arc for half of line width). for test remove this option and see difference.
    – Zarko
    Nov 6, 2017 at 1:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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