19

I have the following figure

enter image description here

I would like to draw portions of circles between some of the red points. More explicitly, I would like to go from ac1 to ab1 and then to ac2 following circle A, then go to bc1 following circle C and to ab2 following circle B and back to ac1 following circle A.

There is probably a solution using the arc operation, but this would require computing the angles for every portion of circle, which can get tedious. Is there a simple way to do that ?

(I was thinking maybe drawing circles with \clip, but I can't figure out how to do it)

Here is my example code

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

\begin{document}

\begin{tikzpicture}
\coordinate (a) at (0,0);
\coordinate (b) at (-1,-1);
\coordinate (c) at (1,-1);

\draw[name path=circleA] (a) circle (1.5cm);
\draw[name path=circleB] (b) circle (1.5cm);
\draw[name path=circleC] (c) circle (1.5cm);

\fill [red, name intersections={of=circleA and circleB,name=intAB}]
(intAB-1) circle (2pt) node[above left] {ab1}
(intAB-2) circle (2pt) node[below right] {ab2};
\fill [red, name intersections={of=circleA and circleC,name=intAC}]
(intAC-1) circle (2pt) node[above right] {ac1}
(intAC-2) circle (2pt) node[below left] {ac2};
\begin{scope}
\clip (a) circle (1.5cm);
\fill [red, name intersections={of=circleB and circleC,name=intBC}]
(intBC-1) circle (2pt) node[below] {bc1}
(intBC-2) circle (2pt) node {bc2};
\end{scope}

\node (A) at ($(a)+(0,1)$) {$A$};
\node (B) at ($(b)+(-1,0)$) {$B$};
\node (C) at ($(c)+(1,0)$) {$C$};
\end{tikzpicture}

\end{document}
5
  • Clips are certainly an easy way to do this, how "clean" do you want the joins to be? Sep 14, 2012 at 15:16
  • In the final document I may not draw the intersection points, so it would be nice if the joints were clean enough so that the line appears continuous.
    – Corentin
    Sep 14, 2012 at 15:34
  • In which case clipping is not the best option. That's useful to know. Sep 14, 2012 at 15:42
  • @AndrewStacey With \pgfpatharcto the joints are perfectly clean, so from a practical point of view I am happy with this solution. However, if you have another method with clips, even if joints do not match so well, I would be glad to have a look at the difference and learn something new..
    – Corentin
    Sep 14, 2012 at 21:21
  • Always worth learning! If no-one else beats me to it (and, everyone else, please do!) then I'll add a clip solution next time I'm on a "proper" computer. Sep 14, 2012 at 22:43

5 Answers 5

15

enter image description here

Here's a solution that uses the nodes that you have defined and the commands

\pgfpointanchor{<node>}{<anchor>}
\pgfpathmoveto{<coordinate>}
\pgfpatharcto{<x-radius>}{<y-radius>}{<rotation>}{<large arc flag>}{<counterclockwise flag>}{<target point>}

The idea is to use \pgfpointanchor to get the coordinates of one the points of intersection. You then use pgfpathmoveto to move there, and then use \pgfpatharcto to draw an arc to the other point of intersection (which you find the coordinates of using \pgfpointanchor again). All of these commands are detailed in the pgf manual.

The new piece I have added to your code is:

% new bit
\pgfsetlinewidth{2pt}
% path between ac1 and ab1
\pgfsetstrokecolor{blue}
\pgfpathmoveto{\pgfpointanchor{intAC-1}{south}}
\pgfpatharcto{1.5cm}{1.5cm}{0}{0}{1}{\pgfpointanchor{intAB-1}{south}}
\pgfusepath{stroke}
% path between ab1 and ac2
\pgfsetstrokecolor{red}
\pgfpathmoveto{\pgfpointanchor{intAB-1}{south}}
\pgfpatharcto{1.5cm}{1.5cm}{0}{0}{1}{\pgfpointanchor{intAC-2}{south}}
\pgfusepath{stroke}
% path between ac2 and bc1
\pgfsetstrokecolor{green}
\pgfpathmoveto{\pgfpointanchor{intAC-2}{south}}
\pgfpatharcto{1.5cm}{1.5cm}{0}{0}{0}{\pgfpointanchor{intBC-1}{south}}
\pgfusepath{stroke}
% path between bc1 and ab2
\pgfsetstrokecolor{yellow}
\pgfpathmoveto{\pgfpointanchor{intBC-1}{south}}
\pgfpatharcto{1.5cm}{1.5cm}{0}{0}{0}{\pgfpointanchor{intAB-2}{south}}
\pgfusepath{stroke}
% path between ab2 and ac1
\pgfsetstrokecolor{orange}
\pgfpathmoveto{\pgfpointanchor{intAB-2}{south}}
\pgfpatharcto{1.5cm}{1.5cm}{0}{0}{1}{\pgfpointanchor{intAC-1}{south}}
\pgfusepath{stroke}

Note that some of the paths are traversed clockwise, and some counter clockwise, determined by the 5th argument to \pgfpatharcto

Here's the complete MWE

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

\begin{document}

\begin{tikzpicture}
\coordinate (a) at (0,0);
\coordinate (b) at (-1,-1);
\coordinate (c) at (1,-1);

\draw[name path=circleA] (a) circle (1.5cm);
\draw[name path=circleB] (b) circle (1.5cm);
\draw[name path=circleC] (c) circle (1.5cm);

\fill [red, name intersections={of=circleA and circleB,name=intAB}]
(intAB-1) circle (2pt) node[above left] {ab1}
(intAB-2) circle (2pt) node[below right] {ab2};
\fill [red, name intersections={of=circleA and circleC,name=intAC}]
(intAC-1) circle (2pt) node[above right] {ac1}
(intAC-2) circle (2pt) node[below left] {ac2};
\begin{scope}
\clip (a) circle (1.5cm);
\fill [red, name intersections={of=circleB and circleC,name=intBC}]
(intBC-1) circle (2pt) node[below] {bc1}
(intBC-2) circle (2pt) node {bc2};
\end{scope}
% new bit
\pgfsetlinewidth{2pt}
% path between ac1 and ab1
\pgfsetstrokecolor{blue}
\pgfpathmoveto{\pgfpointanchor{intAC-1}{south}}
\pgfpatharcto{1.5cm}{1.5cm}{0}{0}{1}{\pgfpointanchor{intAB-1}{south}}
\pgfusepath{stroke}
% path between ab1 and ac2
\pgfsetstrokecolor{red}
\pgfpathmoveto{\pgfpointanchor{intAB-1}{south}}
\pgfpatharcto{1.5cm}{1.5cm}{0}{0}{1}{\pgfpointanchor{intAC-2}{south}}
\pgfusepath{stroke}
% path between ac2 and bc1
\pgfsetstrokecolor{green}
\pgfpathmoveto{\pgfpointanchor{intAC-2}{south}}
\pgfpatharcto{1.5cm}{1.5cm}{0}{0}{0}{\pgfpointanchor{intBC-1}{south}}
\pgfusepath{stroke}
% path between bc1 and ab2
\pgfsetstrokecolor{yellow}
\pgfpathmoveto{\pgfpointanchor{intBC-1}{south}}
\pgfpatharcto{1.5cm}{1.5cm}{0}{0}{0}{\pgfpointanchor{intAB-2}{south}}
\pgfusepath{stroke}
% path between ab2 and ac1
\pgfsetstrokecolor{orange}
\pgfpathmoveto{\pgfpointanchor{intAB-2}{south}}
\pgfpatharcto{1.5cm}{1.5cm}{0}{0}{1}{\pgfpointanchor{intAC-1}{south}}
\pgfusepath{stroke}

\node (A) at ($(a)+(0,1)$) {$A$};
\node (B) at ($(b)+(-1,0)$) {$B$};
\node (C) at ($(c)+(1,0)$) {$C$};
\end{tikzpicture}

\end{document}
1
  • Thanks a lot for your answer, this is what I was looking for. I wasn't aware of the command \pgfpatharcto, which is very useful indeed.
    – Corentin
    Sep 14, 2012 at 21:13
23

Edit: a new version with better jonctions... and new atan2

This is not the first question that asks how to draw an arc between two points on a circle with known center. So I decided to create two new styles to meet this need. Here is an example of use:

\draw (a) to[clockwise arc centered at=c] (b);

This command draws an arc starting at a, ending at b, and centered at c (in fact, ending is on a line through c and b if b is not on the circle centered at c and that goes through a).

There are two styles: clockwise arc centered at and anticlockwise arc centered at.

(Due to rounding errors, always use line join=round to get better connections between some arcs.)

Here's your answer (I slightly modified the code of your MWE) using both styles:

enter image description here

\documentclass[margin=5mm]{standalone}
\usepackage{tikz}
\usetikzlibrary{calc,intersections}

\tikzset{
  anticlockwise arc centered at/.style={
    to path={
      let \p1=(\tikztostart), \p2=(\tikztotarget), \p3=(#1),
      \n{anglestart}={atan2(\y1-\y3,\x1-\x3)},
      \n{angletarget}={atan2(\y2-\y3,\x2-\x3)},
      \n{angletarget}={\n{angletarget} < \n{anglestart} ? \n{angletarget}+360 : \n{angletarget}},
      \n{radius}={veclen(\x1-\x3,\y1-\y3)}
      in arc(\n{anglestart}:\n{angletarget}:\n{radius})  -- (\tikztotarget)
    },
  },
  clockwise arc centered at/.style={
    to path={
      let \p1=(\tikztostart), \p2=(\tikztotarget), \p3=(#1),
      \n{anglestart}={atan2(\y1-\y3,\x1-\x3)},
      \n{angletarget}={atan2(\y2-\y3,\x2-\x3)},
      \n{angletarget}={\n{angletarget} > \n{anglestart} ? \n{angletarget} - 360 : \n{angletarget}},
      \n{radius}={veclen(\x1-\x3,\y1-\y3)}
      in arc(\n{anglestart}:\n{angletarget}:\n{radius})  -- (\tikztotarget)
    },
  },
}

\begin{document}
\begin{tikzpicture}
  % 3 centers (a, b, c)
  \coordinate (a) at (0,0);
  \coordinate (b) at (-1,-1);
  \coordinate (c) at (1,-1);

  % 3 circles
  \draw[name path=circleA] (a) circle (1.5cm);
  \draw[name path=circleB] (b) circle (1.5cm);
  \draw[name path=circleC] (c) circle (1.5cm);

  % label of circles
  \node (A) at ($(a)+(0,1)$) {$A$};
  \node (B) at ($(b)+(-1,0)$) {$B$};
  \node (C) at ($(c)+(1,0)$) {$C$};

  % intersections of circles (A) and (B)
  \path [name intersections={of=circleA and circleB,name=AB}];
  % show them
  \fill[red] (AB-1) circle (2pt) node[above left] {AB-1};
  \fill[red] (AB-2) circle (2pt) node[below right] {AB-2};

  % intersections of circles (A) and (C)
  \path [name intersections={of=circleA and circleC,name=AC}];
  % show them
  \fill[red] (AC-1) circle (2pt) node[above right] {AC-1};
  \fill[red] (AC-2) circle (2pt) node[below left] {AC-2};

  % intersections of circles (B) and (C)
  \path[name intersections={of=circleB and circleC,name=BC}];
  % show them
  \fill[red] (BC-1) circle (2pt) node[above] {BC-1};
  \fill[red] (BC-2) circle (2pt) node[below] {BC-2};


  \draw[line join=round,orange,fill=orange,fill opacity=.5,line width=1pt]
  (AC-2)
  to[clockwise arc centered at=a] (AB-2)
  to[anticlockwise arc centered at=b] (BC-1)
  to[anticlockwise arc centered at=c] (AC-2);

\end{tikzpicture}
\end{document}
3
  • I like this, nice work :)
    – cmhughes
    Sep 14, 2012 at 21:59
  • I think atan2 has (y, x) and not (x, y) as parameters, so they need to be swapped in your answer.
    – corvus_192
    Apr 19, 2022 at 16:59
  • @corvus_192 You are right... in 2022. In 2012, the x and y parameters were not yet reversed in TikZ ! :-) Apr 19, 2022 at 20:52
4

Even though cmhughes has already shown us his version with \pgfpatharcto I want to add a version that TikZ-ifies the \pgfpatharcto command under the new path operator arc to.

The code has been originally developed for another question on TeXwelt.de (German). The only difference is that it uses arc to instead of arc*.

With this operator, the required arc can be drawn (and filled) with

\draw (intAC-1) arc to [arc large] (intAC-2)
                arc to [arc cw]    (intBC-1)
                arc to [arc cw]    (intAB-2)
                arc to []          (intAC-1) -- cycle;

The options

  • arc large and arc small (<large arc flag>) as well as
  • arc cw and arc ccw (<counterclockwise flag>)

correspond to the flags of \pgfpatharcto (argument #4 and #5).

The third argument is used for rotation and can be set with arc rotation (initially 0).

As the precision of \pgfpatharcto is rather bad, the joined close (-- cycle) doesn’t look so good with the default miter line join (but only at 6400 % zoom), I’d use line join=round where this imperfection disappears.

The path operator arc to misses a proper timer (the function that places nodes “along” the path), as a substitute it uses the timer of a straight line (--). The [ ] are mandatory (as can be seen at the fourth occurrence of arc to).

Code

\documentclass[tikz,convert=false]{standalone}
\tikzset{
  arc/ccw/.initial=1,
  arc/large/.initial=0,
  arc ccw/.style={/tikz/arc/ccw=1},
  arc cw/.style={/tikz/arc/ccw=0},
  arc large/.style={/tikz/arc/large=1},
  arc small/.style={/tikz/arc/large=0},
  arc rotation/.initial=0
}
\usetikzlibrary{intersections}
\makeatletter
\def\tikz@arcA rc{\pgfutil@ifnextchar t%
  {\tikz@flush@moveto\tikz@arcB@opt}%  -> our new "arc to"
  {\tikz@flush@moveto\tikz@arc@cont}}% -> our old "arc"
\def\tikz@arcB@opt to#1[#2]{%
  \def\tikz@arcB@options{#2}
  \tikz@do@@arcB}
\def\tikz@do@@arcB{%
  \pgfutil@ifnextchar n{\tikz@collect@label@onpath\tikz@do@@arcB}
    {\pgfutil@ifnextchar c{\tikz@collect@coordinate@onpath\tikz@do@@arcB}
      {\tikz@scan@one@point\tikz@do@arcB}}}
\def\tikz@do@arcB#1{%
  \edef\tikz@timer@start{\noexpand\pgfqpoint{\the\tikz@lastx}{\the\tikz@lasty}}
  \tikz@make@last@position{#1}%
  \edef\tikz@timer@end{\noexpand\pgfqpoint{\the\tikz@lastx}{\the\tikz@lasty}}%
  \iftikz@shapeborder
    \edef\tikz@moveto@waiting{\tikz@shapeborder@name}%
  \fi
  \begingroup
    \tikzset{every arc/.try}%
    \expandafter\tikzset\expandafter{\tikz@arcB@options}%
    \pgfmathparse{\pgfkeysvalueof{/tikz/x radius}}%
    \let\tikz@arc@x\pgfmathresult
    \ifpgfmathunitsdeclared
      \edef\tikz@arc@x{\tikz@arc@x pt}%
    \else
      \pgf@process{\pgfpointxy{\tikz@arc@x}{0}}%
      \pgfmathveclen@{\pgf@x}{\pgf@y}%
      \edef\tikz@arc@x{\pgfmathresult pt}%
    \fi
    \pgfmathparse{\pgfkeysvalueof{/tikz/y radius}}%
    \let\tikz@arc@y\pgfmathresult
    \ifpgfmathunitsdeclared
      \edef\tikz@arc@y{\tikz@arc@y pt}%
    \else
      \pgf@process{\pgfpointxy{0}{\tikz@arc@y}}%
      \pgfmathveclen@{\pgf@x}{\pgf@y}%
      \edef\tikz@arc@y{\pgfmathresult pt}%
    \fi
    \pgfpatharcto{\tikz@arc@x}{\tikz@arc@y}
                 {\pgfkeysvalueof{/tikz/arc rotation}}{\pgfkeysvalueof{/tikz/arc/large}}
                 {\pgfkeysvalueof{/tikz/arc/ccw}}{#1}%
  \endgroup
  \let\tikz@timer=\tikz@timer@line
  \tikz@scan@next@command
}
\makeatother
\begin{document}
\begin{tikzpicture}[radius=1.5]
\draw[name path=circleA] ( 0, 0) coordinate (a) circle [];
\draw[name path=circleB] (-1,-1) coordinate (b) circle [];
\draw[name path=circleC] ( 1,-1) coordinate (c) circle [];

\fill [red, name intersections={of=circleA and circleB,name=intAB}]
     (intAB-1) circle (2pt) node[above left]  {ab1}
     (intAB-2) circle (2pt) node[below right] {ab2};
\fill [red, name intersections={of=circleA and circleC,name=intAC}]
     (intAC-1) circle (2pt) node[above right] {ac1}
     (intAC-2) circle (2pt) node[below left]  {ac2};
\fill [red, name intersections={of=circleB and circleC,name=intBC}]
     (intBC-1) circle (2pt) node[above]       {bc1};


\node (A) at ([shift={((0,1)} ]a) {$A$};
\node (B) at ([shift={((-1,0)}]b) {$B$};
\node (C) at ([shift={((1,0)} ]c) {$C$};

\draw[
  thick,
  line join=round,
  draw=blue,
  fill opacity=.5,
  fill=blue!50
] (intAC-1) arc to [arc large] (intAC-2)
            arc to [arc cw]    (intBC-1)
            arc to [arc cw]    (intAB-2)
            arc to []          (intAC-1) -- cycle;
\end{tikzpicture}
\end{document}

Output

enter image description here

2

I know this is a very old (relatively speaking!) question, but since it was asked then the spath3 library has been developed that can handle this sort of thing quite simply.

\documentclass{article}
%\url{https://tex.stackexchange.com/q/71548/86}
\usepackage{tikz}
\usetikzlibrary{calc,intersections,spath3}

\begin{document}

\begin{tikzpicture}
\coordinate (a) at (0,0);
\coordinate (b) at (-1,-1);
\coordinate (c) at (1,-1);

\draw[spath/save=circleA] (a) circle (1.5cm);
\draw[spath/save=circleB] (b) circle (1.5cm);
\draw[spath/save=circleC] (c) circle (1.5cm);

\tikzset{
  spath/remove empty components=circleA,
  spath/remove empty components=circleB,
  spath/remove empty components=circleC,
  spath/split at intersections={circleA}{circleB},
  spath/split at intersections={circleB}{circleC},
  spath/split at intersections={circleC}{circleA},
  spath/get components of={circleA}\cptsA,
  spath/get components of={circleB}\cptsB,
  spath/get components of={circleC}\cptsC,
}

\draw[
  red,
  thick,
  spath/use=\getComponentOf{\cptsA}{4},
  spath/use={\getComponentOf{\cptsA}{1},weld},
  spath/use={\getComponentOf{\cptsC}{1},weld,reverse},
  spath/use={\getComponentOf{\cptsB}{3},weld,reverse},
  spath/use={\getComponentOf{\cptsA}{3},weld},
  spath/close=current,
];

\fill [red, name intersections={of=circleA and circleB,name=intAB}]
(intAB-1) circle (2pt) node[above left] {ab1}
(intAB-2) circle (2pt) node[below right] {ab2};
\fill [red, name intersections={of=circleA and circleC,name=intAC}]
(intAC-1) circle (2pt) node[above right] {ac1}
(intAC-2) circle (2pt) node[below left] {ac2};
\begin{scope}
\clip (a) circle (1.5cm);
\fill [red, name intersections={of=circleB and circleC,name=intBC}]
(intBC-1) circle (2pt) node[below] {bc1}
(intBC-2) circle (2pt) node[above] {bc2};
\end{scope}

\node (A) at ($(a)+(0,1)$) {$A$};
\node (B) at ($(b)+(-1,0)$) {$B$};
\node (C) at ($(c)+(1,0)$) {$C$};
\end{tikzpicture}

\end{document}

The three circles are split at the places where they intersect with each other and then various of the components are recombined to give the final path. A couple of the components have to be reversed so that they join correctly.

Combined path from segments of the circles.

0

With tkz-euclide

\documentclass{article}
\usepackage{tkz-euclide}
\begin{document}
\begin{tikzpicture}
\tkzDefPoints{0/0/a,-1/-1/b,1/-1/c,1.5/0/x,.5/-1/y,-.5/-1/z}
\tkzDrawCircles(a,x b,y c,z)
\tkzInterCC(a,x)(b,y) \tkzGetPoints{ab1}{ab2}
\tkzInterCC(a,x)(c,z) \tkzGetPoints{ac1}{ac2}
\tkzInterCC(b,y)(c,z) \tkzGetPoints{bc1}{bc2}
\tkzDrawPoints[red](ab1,ab2,ac1,ac2,bc1,bc2)
\tkzDrawArc[red,line width=1pt](a,ab1)(ac2)
\tkzDrawArc[red,line width=1pt](b,ab1)(bc1)
\tkzDrawArc[red,line width=1pt](c,bc1)(ac2)
\tkzDrawPoints[red](ab1,ab2,ac1,ac2,bc1,bc2)
\tkzLabelPoints[red,below right](ab1,ab2,ac1,ac2,bc1,bc2)
\end{tikzpicture}
\end{document}

enter image description here

You must log in to answer this question.

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