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.

An ordered set of three points uniquely defines a circular arc, and I'd like to be able to draw that arc in TikZ. I know about the arc drawing command, which does the job given a radius, two angles, and one endpoint, but in order to do that I would have to calculate the radius and the angles from the other two points on the arc. I suppose I could whip up some code to do that automatically using the PGF math library, but is there a better way? Or has anyone else already made this into a package/library I could reuse?

share|improve this question
4  
an ordered set of three distinct, non colinear points... –  Seamus May 24 '11 at 11:01
    
@Seamus: an ordered set of 3 non colinear points should be enough. –  Please don't touch Mar 19 '13 at 21:29

6 Answers 6

up vote 15 down vote accepted

Use the following code to draw the arc corresponding to the circle through (1,2), (3,4) and (2,4) and going anticlockwise from (1,2) to (2,4).

\documentclass[a4paper]{article}
\usepackage{tkz-euclide}
\usetkzobj{all} 
\begin{document}
\begin{tikzpicture}
\tkzDefPoint(1,2){A}\tkzDefPoint(3,4){B}\tkzDefPoint(2,4){C}
\tkzCircumCenter(A,B,C)\tkzGetPoint{O}
\tkzDrawArc(O,A)(C)
\end{tikzpicture}
\end{document}
share|improve this answer
    
Much simpler than mine! –  TH. Apr 17 '11 at 21:00
2  
Ah. tkz-euclide requires tikz 2.10. The version in the debian repos is 2.00... –  Seamus May 31 '11 at 11:53
    
@Seamus Debian is also stuck on TeX Live 2009 and doesn't include tlmgr. For a while, Debian was stuck on TeX Live 2007. For this reason Debian TeX packages are not recommended, unless you can live with waiting two years between major updates. –  Sharpie May 31 '11 at 16:54

Keep in mind that I don't actually know TikZ. I did what you said: I used pgf math to work out the values for the angles and the radius and then packaged it up into a simple macro.

\def\drawcirculararc(#1,#2)(#3,#4)(#5,#6){%
        \pgfmathsetmacro\cA{(#1*#1+#2*#2-#3*#3-#4*#4)/2}%
        \pgfmathsetmacro\cB{(#1*#1+#2*#2-#5*#5-#6*#6)/2}%
        \pgfmathsetmacro\cy{(\cB*(#1-#3)-\cA*(#1-#5))/%
                            ((#2-#6)*(#1-#3)-(#2-#4)*(#1-#5))}%
        \pgfmathsetmacro\cx{(\cA-\cy*(#2-#4))/(#1-#3)}%
        \pgfmathsetmacro\cr{sqrt((#1-\cx)*(#1-\cx)+(#2-\cy)*(#2-\cy))}%
        \pgfmathsetmacro\cA{atan2(#1-\cx,#2-\cy)}%
        \pgfmathsetmacro\cB{atan2(#5-\cx,#6-\cy)}%
        \pgfmathparse{\cB<\cA}%
        \ifnum\pgfmathresult=1
                \pgfmathsetmacro\cB{\cB+360}%
        \fi
        \draw (#1,#2) arc (\cA:\cB:\cr);%
}

It isn't terribly hard to work out the origin and the radius using
r2 = (xi - x)2 + (yi - y)2
for i in {1,2,3}.

The macro assumes that the points are entered counterclockwise. It probably fails in some cases, since I didn't test it very extensively.

I guess I should point out that that the computation of the center of the circle, (\cx,\cy) depends on some quantities not being zero. One can write down three linear equations relating \cx and \cy. Any two of them suffice for solving (of course) the system so there are three possible choices. I didn't check, but I believe it's impossible for the relevant quantities to all be zero (unless your points are colinear, but then you don't have a circle).

If one were so inclined, one could generalize the computation of \cx and \cy depending on which two equations are used and then just pick one for which the denominators are not zero. Since there's a better solution, I didn't bother, but I guess I could if someone actually cares.

share|improve this answer
3  
You can simplify things a bit by using \pgfmathsetmacro{\cx}{...} instead of \pgfmathparse{...}\let\cx\pgfmathresult. –  Matthew Leingang Apr 17 '11 at 10:10
    
@Matthew: Thanks. I've updated it. Using \pgfmathresult over and over seemed really clumsy. I'm glad there's a better way. –  TH. Apr 17 '11 at 10:21
    
@Matthew: cool, I didn't know you could do that! Thanks for the tip. –  David Z Apr 17 '11 at 21:46
    
@TH. As much as I like this idea, it draws the circle in the wrong location for e.g. \drawcirculararc(0,0)(1,0)(1,1). –  David Z Apr 17 '11 at 22:01
    
@David: Yeah. I failed to solve the simple equations correctly last night so the center of the circle is wrong. –  TH. Apr 17 '11 at 22:44

I was browsing in the TikZ/PGF manual when I came across the \pgfpatharcto command. This draws an arc from a point to a specific point, with a given radius (actually, elliptical radii). So once the radius has been figured out then this command can be used to draw the arc. Of course, once one has computed the radius then the angles aren't all that difficult to compute either so this isn't a great solution. Moreover, the manual specifically warns that the computations involved in \pgfpatharcto are unreliable. So I'm posting this partly to highlight the \pgfpatharcto command but mostly to recast the solution as a to path.

\documentclass{article}

\usepackage{tikz}

\makeatletter

\def\bc@save@ctrla#1{
  \def\bc@ctrla{#1}}
\def\bc@save@target#1{
  \def\bc@target{#1}}
\def\bc@save@start#1{
  \def\bc@start{#1}}


\tikzset{%
  arc between/.style={
    to path={
      \pgfextra{
      \edef\bc@@target{(\tikztotarget)}
      \tikz@scan@one@point\bc@save@target\bc@@target\relax
      \edef\bc@@start{(\tikztostart)}
      \tikz@scan@one@point\bc@save@start\bc@@start\relax
      \pgfkeysgetvalue{/tikz/arc between/mid point}{\bc@@ctrla}
      \tikz@scan@one@point\bc@save@ctrla\bc@@ctrla\relax
      \bc@start
      \edef\bc@sx{\the\pgf@x}
      \edef\bc@sy{\the\pgf@y}
      \bc@target
      \edef\bc@tx{\the\pgf@x}
      \edef\bc@ty{\the\pgf@y}
      \bc@ctrla
      \edef\bc@mx{\the\pgf@x}
      \edef\bc@my{\the\pgf@y}
      \pgfmathsetmacro{\bc@a}{veclen(\bc@mx - \bc@sx,\bc@my - \bc@sy)/1cm}
      \pgfmathsetmacro{\bc@b}{veclen(\bc@tx - \bc@mx,\bc@ty - \bc@my)/1cm}
      \pgfmathsetmacro{\bc@c}{veclen(\bc@sx - \bc@tx,\bc@sy - \bc@ty)/1cm}
      \pgfmathsetmacro{\bc@s}{(\bc@a + \bc@b + \bc@c)/2}
      \pgfmathsetmacro{\bc@r}{\bc@a * \bc@b * \bc@c/(4 * sqrt(\bc@s * (\bc@s - \bc@a) * (\bc@s - \bc@b) * (\bc@s - \bc@c)))}
      \pgfpatharcto{\bc@r cm}{\bc@r cm}{0}{0}{0}{\bc@target}
      }
    },
    arc between/.cd,
  },
  arc between/mid point/.initial={},
}



\makeatother

\begin{document}
\begin{tikzpicture}
\draw (3,0) to[arc between, mid point={+(1,1)}] +(2,0);
\fill (3,0) circle[radius=2pt] +(1,1) circle[radius=2pt]  +(2,0) circle[radius=2pt] ;
\end{tikzpicture}
\end{document}

There are a couple of comments on this code. The key for specifying the mid point is actually /tikz/arc between/mid point. The key /tikz/arc between switches to the subtree /tikz/arc between. This means that any subsequent keys are taken relative to this subtree. In Proper Code, there ought to be a fall-through so that any TikZ keys are passed back to the /tikz tree. Secondly, this doesn't update the last position properly, so using relative coordinates after this command will be relative to the starting position. I'm not sure of the best solution to that. Actually, back on that first point. A better method would be to specify the mid point as an argument to the /tikz/arc between key.

Result:

arc between three points

share|improve this answer
    
Am I missing something or is this a terribly complicated way of getting the radius? Can't you just get the distance between the centre of the circle and any of the points and use this as the radius? And the centre of the circle is just the intersection of the perpendicular bisectors of the lines between the points... This could be done with calc and intersection much more easily, surely... –  Seamus May 31 '11 at 14:56
    
@Seamus: Depends what you mean by "complicated". I suspect that the computations needed by calc and intersection to find that radius would be as complicated. Part of the point of this (and I'll admit there isn't much point) is that it is self-contained and does no more than strictly necessary. Computing the centre is unnecessary, ergo: don't do it. –  Loop Space May 31 '11 at 16:59
    
@Seamus: I should also make it clear that this is another of my "If the other answers weren't there, I wouldn't have posted this.". There's a great comment by Dima on one of my old answers, I should look it up and put it somewhere for all to see so that they understand me better! –  Loop Space May 31 '11 at 17:01
    
OK fair enough. But still I think pgfarcto is cool enough that it deserves a nice simple demonstration... –  Seamus May 31 '11 at 18:03
    
@Seamus: In that case, you should ask a question that will lead to a nice simple demonstration of it ...! –  Loop Space May 31 '11 at 18:26

Just another simpler solution with PSTricks.

enter image description here

\documentclass[pstricks,border=3pt]{standalone}
\usepackage{pst-eucl}

\begin{document}
\pspicture(5,5)
    \pstGeonode[PosAngle={-135,45,90}](1,2){A}(3,4){B}(2,4){C}
    \pstCircleABC[DrawCirABC=false]{A}{B}{C}{O}
    \pstArcnOAB{O}{A}{B}
\endpspicture
\end{document}
share|improve this answer
1  
Would the downvoter care to comment? –  Please don't touch Mar 20 '13 at 4:48
    
They usually don't do that because it could spark a wave of hate from you to him (you could make a series of downvoting on all his questions). –  Vladimir Putin Mar 20 '13 at 5:49
    
I couldn't say whether this is a reason for downvoting, but the question did ask for a solution in TikZ. –  David Z Jul 3 '13 at 2:15

Just for completeness, a tikz solution which does not use tikz-euclide, but instead does all the calculations through calc library and let..in syntax. Macro \arcThroughThreePoints requires the three coordinates to be given in counter-clockwise order.

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc}
\newcommand{\arcThroughThreePoints}[4][]{
\coordinate (middle1) at ($(#2)!.5!(#3)$);
\coordinate (middle2) at ($(#3)!.5!(#4)$);
\coordinate (aux1) at ($(middle1)!1!90:(#3)$);
\coordinate (aux2) at ($(middle2)!1!90:(#4)$);
\coordinate (center) at ($(intersection of middle1--aux1 and middle2--aux2)$);
\draw[#1] 
 let \p1=($(#2)-(center)$),
      \p2=($(#4)-(center)$),
      \n0={veclen(\p1)},       % Radius
      \n1={atan2(\x1,\y1)}, % angles
      \n2={atan2(\x2,\y2)},
      \n3={\n2>\n1?\n2:\n2+360}
    in (#2) arc(\n1:\n3:\n0);
}

\begin{document}
\begin{tikzpicture}
\coordinate (A) at (3,1);
\coordinate (B) at (1,2);
\coordinate (C) at (-2,-2);
\arcThroughThreePoints{A}{B}{C};

\foreach \p in {A,B,C,center}
 \fill[red] (\p) circle(2pt);
\end{tikzpicture}
\end{document}

Result

I guess this answer does not qualifies as "the easiest way", as the title of the question requests, but it shows some nifty tricks.

share|improve this answer

The following solution defines a to path arc through that expects one coordinate (including the () as it may preceeded with + or ++).

It avoids

  • tkz-euclide or similar solutions that uses a separated path (which makes it impossible to use the arc as part of a path;
  • the calc library and instead uses PGF’s already present macros \pgfpointlineattime and \pgfpointintersectionofline as well as \pgfmathrotatepointaround and \pgfmathanglebetweenpoints.

The solution provides two styles:

  • arc through ccw draws a counter-clockwise arc and
  • arc through cw draws a clockwise arc.

The point that is given as an argument to both styles is used to calculate the radius. The arc is not necessary drawn through this points, the circle on which the arc lies only goes to this point. This makes it possible to draw the rest of said circle without the need to give another coordinate. (The name arc on circle that goes through would probably be a better name for the styles …)

The coordinate arc through center is defined after the to path and can be used for later reference (as long as no other arc through is drawn).

This solution uses the same calculation algorithm as the one in JLDiazanswer.

Code

\documentclass[tikz]{standalone}
\makeatletter
\tikzset{
  @arc through/.style 2 args={
    to path={
      \pgfextra
        \pgfextract@process\pgf@tostart{\tikz@scan@one@point\pgfutil@firstofone(\tikztostart)\relax}%
        \pgfextract@process\pgf@tothrough{\tikz@scan@one@point\pgfutil@firstofone#1}%
        \pgfextract@process\pgf@totarget{\tikz@scan@one@point\pgfutil@firstofone(\tikztotarget)\relax}%
        \pgfextract@process\pgf@topointMidA{\pgfpointlineattime{.5}{\pgf@tostart}{\pgf@tothrough}}%
        \pgfextract@process\pgf@topointMidB{\pgfpointlineattime{.5}{\pgf@totarget}{\pgf@tothrough}}%
        \pgfextract@process\pgf@tocenter{%
          \pgfpointintersectionoflines{\pgf@topointMidA}
            {\pgfmathrotatepointaround{\pgf@tothrough}{\pgf@topointMidA}{90}}
            {\pgf@topointMidB}{\pgfmathrotatepointaround{\pgf@tothrough}{\pgf@topointMidB}{90}}}%
        \pgfcoordinate{arc through center}{\pgf@tocenter}%
        \pgfpointdiff{\pgf@tocenter}{\pgf@tostart}%
        \pgfmathveclen@{\pgfmath@tonumber\pgf@x}{\pgfmath@tonumber\pgf@y}%
        \edef\pgf@toradius{\pgfmathresult pt}
        \pgfmathanglebetweenpoints{\pgf@tocenter}{\pgf@tostart}%
        \let\pgf@tostartangle\pgfmathresult
        \pgfmathanglebetweenpoints{\pgf@tocenter}{\pgf@totarget}%
        \let\pgf@toendangle\pgfmathresult
        \ifdim\pgf@tostartangle pt>\pgf@toendangle pt\relax
          \pgfmathsetmacro\pgf@tostartangle{\pgf@tostartangle-360}%
        \fi
        #2%
          \pgfmathsetmacro\pgf@toendangle{\pgf@toendangle-360}%
        \fi
      \endpgfextra
      arc [radius=+\pgf@toradius, start angle=\pgf@tostartangle, end angle=\pgf@toendangle] \tikztonodes
    }},
  arc through ccw/.style={@arc through={#1}{\iffalse}},
  arc through cw/.style={@arc through={#1}{\iftrue}},
}
\makeatother
\begin{document}
\begin{tikzpicture}
\coordinate (A) at ( 3, 1); \coordinate (B) at ( 1, 2); \coordinate (C) at (-2,-2);
\draw[ultra thick, draw=blue, fill=blue!50] (A) to[arc through ccw=(B)] (C) -- (arc through center) -- cycle;
\foreach \p in {A,B,C, arc through center} \fill[red] (\p) circle(2pt);
\end{tikzpicture}
\begin{tikzpicture}
\coordinate (A) at ( 3, 1); \coordinate (B) at ( 1, 2); \coordinate (C) at (-2,-2);
\draw[ultra thick, draw=green, fill=green!50] (B) to[arc through ccw=(A)] (C) -- (arc through center) -- cycle;
\draw[ultra thick, draw=blue,  fill=blue!50]  (B) to[arc through cw= (A)] (C) -- (arc through center) -- cycle;
\foreach \p in {A,B,C, arc through center} \fill[red] (\p) circle(2pt);
\end{tikzpicture}
\end{document}

Output

enter image description here

enter image description here

share|improve this answer

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.