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.

Is there a simple way with TikZ to align a line to a circle without manually specifying an arc to join two separate lines like in the example below?

\documentclass{article}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}
    \filldraw[black] (0.5,0.5) circle (0.5);
    \draw[-,thick,red] (-1,1) -- (0.5,1);
    \draw[-,thick,red] (1,-1) -- (1,0.5);
    \draw[-,thick,red] (1,0.5) arc (0:90:0.5);
\end{tikzpicture}
\end{document}

Example image

If possible the solution should be a single command producing the following behaviour:

  1. Draw a circle C (specified by its center and radius)
  2. Draw a line with three parts:
    1. First part is the line segment from any point P to a point Q on the perimeter of C.
    2. Second part is the arc of 90 degrees angle along the perimeter of C.
    3. Third part is the line segment away from the arc.

In pseudo code:

\documentclass{article}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}
    \filldraw[black] (0.5,0.5) coordinate (C) circle (0.5);
    \draw[-,thick,red] (-1,1) -- (0.5,1) arc (C,90) -- (1,0.5);
    % The line above would draw a line to (0.5,1), 
    % an arc of 90 degrees around (C) and continue 
    % from there towards (1,0.5)
\end{tikzpicture}
\end{document}

The pseudo code above should result in the same image as above.

Optional features

Even more convenient would be to allow arbitrary angles and to continue the line after the arc with the direction of the tangent at the exit point:

In pseudo code:

\documentclass{article}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}
    \filldraw[black] (0.5,0.5) coordinate (C) circle (0.5);
    \draw[-,thick,red] (-1,1) -- (0.5,1) arc (C,45) -- (1cm);
    % The line above would draw a line to (0.5,1), 
    % an arc of 45 degrees around (C) and continue a 
    % line of 1cm length from there
\end{tikzpicture}
\end{document}
share|improve this question
    
I'm sure it's very possible, but the constraints you're working with aren't really clear from the question. What do you have to start with? A circular node? A circle specified by its centre and radius? A solution would probably boil down to defining a macro to draw the arc in an automated way. –  Hammerite Jan 10 at 20:52
    
Well, I don't have any constraints, but maybe just stick with the circle by center/radius like in the example code. A macro would be fine, although I thought of some syntax magic to bend a line according to point and radius or similar. –  slash Jan 10 at 20:59
    
So... given a circle C (specified by its centre and radius) and a point P outside the circle, draw a curve that has three parts; the first part is the line segment from P to the perimeter of C (at Q, say) that is tangent to C at Q (either choice will do, or provide a way to select one of the two choices); the second part is a quarter-circle arc as shown in the example; the third part is a line segment as shown in the example, of the same length as the first part? –  Hammerite Jan 10 at 21:20
    
Yes, that would be a more accurate description, I will change the question accordingly. The third part (line segment) would not necessarily have to be of the same length as line segment one. –  slash Jan 10 at 21:32

5 Answers 5

up vote 18 down vote accepted

Option rounded corners can be used to draw an arc without specifying the more complex syntax of arc:

\documentclass{article}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}[x=1cm, y=1cm]
    \filldraw[black] (0.5,0.5) circle (0.5);
    \draw[-,thick,red]
      (-1,1) [rounded corners=0.5cm] -- (1,1)
      [sharp corners] -- (1,-1) -- (-1,-1)
    ;
\end{tikzpicture}
\end{document}

Result

  • Option sharp corners switches off the rounding on corners.

Different x and y units

  • Option rounded corners will not work, if the unit in x and y direction is different, because the rounding remains symmetrically.
  • Also the unit must be explicitly specified in the example above.

Option rounded corners internally uses \pgfpoint, by using \pgfpointxy the two issues are addressed:

\documentclass{article}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}[x=2cm,y=1cm]
    \filldraw[black] (0.5,0.5) circle (0.5);
    \draw[-,thick,red]
      (-1,1) \pgfextra{\pgfsetcornersarced{\pgfpointxy{.5}{.5}}} -- (1,1)
      [sharp corners] -- (1,-1) -- (-1,-1)
    ;
\end{tikzpicture}
\end{document}

Or a better readability is achieved by defining a new option:

\documentclass{article}
\usepackage{tikz}
\tikzoption{circle corner}{\pgfsetcornersarced{\pgfpointxy{#1}{#1}}}
\begin{document}
\begin{tikzpicture}[x=2cm,y=1cm]
    \filldraw[black] (0.5,0.5) circle (0.5);
    \draw[-,thick,red]
      (-1,1) [circle corner=0.5] -- (1,1)
      [sharp corners] -- (1,-1) -- (-1,-1)
    ;
\end{tikzpicture}
\end{document}

Result

share|improve this answer

A recommended solution with PSTricks.

Still version

With pst-eucl:

\documentclass[pstricks,border=12pt]{standalone}
\usepackage{pst-eucl}
\psset{PointName=none,PointSymbol=none,linewidth=3\pslinewidth}
\begin{document}
\begin{pspicture}[showgrid](9,7)
    \pstGeonode(4,4){O}(1,2){A}(8,1){B}
    \pstCircleOA[Radius=\pstDistVal{2}]{O}{}
    \pstMiddleAB{A}{O}{A'}
    \pstInterCC[RadiusA=\pstDistVal{2},RadiusB=\pstDistAB{A'}{O}]{O}{}{A'}{}{M1}{M2}
    \pstMiddleAB{B}{O}{B'}
    \pstInterCC[RadiusA=\pstDistVal{2},RadiusB=\pstDistAB{B'}{O}]{O}{}{B'}{}{N1}{N2}    
    \psset{linecolor=red}
    \psline(A)(M2)
    \psline(B)(N1)
    \psarc[origin={O}](O){2}{(N1)}{(M2)}
\end{pspicture}
\end{document}

With pstricks-add (shorter):

\documentclass[pstricks,border=12pt]{standalone}
\usepackage{pstricks-add}
\psset{dimen=medusa,linewidth=3\pslinewidth}
\begin{document}
\begin{pspicture}[showgrid](9,7)
    \pnodes(4,4){O}(1,2){A}(8,1){B}
    \pscircle(O){2}
    \psCircleTangents(A)(O){2}
    \pnodes(CircleT2){M}
    \psCircleTangents(B)(O){2}
    \pnodes(CircleT1){N}
    \psset{linecolor=red}
    \psline(A)(M)
    \psline(B)(N)
    \psarc[origin={O}](O){2}{(N)}{(M)}
\end{pspicture}
\end{document}

enter image description here

Animated version

With pst-eucl:

\documentclass[pstricks,border=12pt]{standalone}
\usepackage{pst-eucl}
\psset{PointName=none,PointSymbol=none,linewidth=3\pslinewidth}
\begin{document}
\multido{\r=0.0+.5}{18}{%
\begin{pspicture}[showgrid](9,7)
    \pstGeonode(4,4){O}(1,2){A}(\r,1){B}
    \pstCircleOA[Radius=\pstDistVal{2}]{O}{}
    \pstMiddleAB{A}{O}{A'}
    \pstInterCC[RadiusA=\pstDistVal{2},RadiusB=\pstDistAB{A'}{O}]{O}{}{A'}{}{M1}{M2}
    \pstMiddleAB{B}{O}{B'}
    \pstInterCC[RadiusA=\pstDistVal{2},RadiusB=\pstDistAB{B'}{O}]{O}{}{B'}{}{N1}{N2}    
    \psset{linecolor=red}
    \psline(A)(M2)
    \psline(B)(N1)
    \psarc[origin={O}](O){2}{(N1)}{(M2)}
\end{pspicture}}
\end{document}

With pstricks-add (shorter):

\documentclass[pstricks,border=12pt]{standalone}
\usepackage{pstricks-add}
\psset{dimen=medusa,linewidth=3\pslinewidth}
\begin{document}
\multido{\r=0.0+.5}{18}{%
\begin{pspicture}[showgrid](9,7)
    \pnodes(4,4){O}(1,2){A}(\r,1){B}
    \pscircle(O){2}
    \psCircleTangents(A)(O){2}
    \pnodes(CircleT2){M}
    \psCircleTangents(B)(O){2}
    \pnodes(CircleT1){N}
    \psset{linecolor=red}
    \psline(A)(M)
    \psline(B)(N)
    \psarc[origin={O}](O){2}{(N)}{(M)}
\end{pspicture}}
\end{document}

enter image description here

Notes:

For multiple invocation of \psCircleTangents, the output node(s) CircleT1 and/or CircleT2 must be saved or buffered or backed up in temporary node(s) first before the next call of \psCircleTangents because \psCircleTangents outputs the tangent points with the same names.

share|improve this answer

Here is a TikZ solution via three new to path:

  1. cw wrap around wraps around the circle in clockkwise direction.
  2. acw wrap around wraps around the circle in anticlockkwise direction.
  3. wrap around chooses the better direction between clockwise and anticlockkwise.

The parameters are the same for these three options: {centerbyradius}. An error occurs when no solution exists.

Examples of use (a and b are the start and end points):

\draw (a) to[wrap around=c by 1cm] (b);
\draw (a) to[cw wrap around=c by 1cm] (b);
\draw (a) to[acw wrap around=c by 1cm] (b);

Some results (orange: clockwise, lime: anticlockwise, black: best):

enter image description here

The code:

\documentclass[tikz]{standalone}
\usetikzlibrary{calc}

\makeatletter
\tikzset{
  cw wrap around/.style args={#1 by #2}{
    to path={
      let
      \p1=(\tikztostart), \p2=(\tikztotarget), \p3=(#1),
      \n1={veclen(\x3-\x1,\y3-\y1)}, \n2={veclen(\x3-\x2,\y3-\y2)}, \n3={#2},
      \n{test}={((\n1 < \n3)||(\n2 < \n3))?1:0}
      in
      \pgfextra{
        \pgfmathtruncatemacro\val{\n{test}}
        \ifnum\val=1 %
        \@latex@error{radius too long to wrap!}{}
        \fi
        \pgfinterruptpath
        \node[line width=0,circle,minimum size=2*\n3,inner sep=0] (cir) at (#1) {};
        \coordinate (i1) at (tangent cs:node=cir,point={(\p1)},solution=1);
        \coordinate (i2) at (tangent cs:node=cir,point={(\p2)},solution=2);
        \endpgfinterruptpath
      }
      let \p{i1}=(i1), \p{i2}=(i2) in
      \pgfextra{
        \pgfmathsetmacro\angstart{atan2(\y{i1}-\y1,\x{i1}-\x1)-90}
        \pgfmathsetmacro\angend{atan2(\y{i2}-\y2,\x{i2}-\x2)+90}
        \pgfmathsetmacro\angend{\angend<\angstart?\angend+360:\angend}
        \pgfmathsetmacro\angend{\angend>\angstart+360?\angend-360:\angend}
      }
      -- (i1) arc[start angle=\angstart,end angle=\angend,radius=\n3] --(\p2)
    },
  },
  acw wrap around/.style args={#1 by #2}{
    to path={
      let
      \p1=(\tikztostart), \p2=(\tikztotarget), \p3=(#1),
      \n1={veclen(\x3-\x1,\y3-\y1)}, \n2={veclen(\x3-\x2,\y3-\y2)}, \n3={#2},
      \n{test}={((\n1 < \n3)||(\n2 < \n3))?1:0}
      in
      \pgfextra{
        \pgfmathtruncatemacro\val{\n{test}}
        \ifnum\val=1 %
        \@latex@error{radius too long to wrap!}{}
        \fi
        \pgfinterruptpath
        \node[line width=0,circle,minimum size=2*\n3,inner sep=0] (cir) at (#1) {};
        \coordinate (i1) at (tangent cs:node=cir,point={(\p1)},solution=2);
        \coordinate (i2) at (tangent cs:node=cir,point={(\p2)},solution=1);
        \endpgfinterruptpath
      }
      let \p{i1}=(i1), \p{i2}=(i2) in
      \pgfextra{
        \pgfmathsetmacro\angstart{atan2(\y{i1}-\y1,\x{i1}-\x1)+90}
        \pgfmathsetmacro\angend{atan2(\y{i2}-\y2,\x{i2}-\x2)-90}
        \pgfmathsetmacro\angend{\angend>\angstart?\angend-360:\angend}
        \pgfmathsetmacro\angend{\angend<\angstart-360?\angend+360:\angend}
      }
      -- (i1) arc[start angle=\angstart,end angle=\angend,radius=\n3] --(\p2)
    },
  },
  wrap around/.style args={#1 by #2}{
    to path={
      let
      \p1=(\tikztostart), \p2=(\tikztotarget), \p3=(#1),
      \n1={veclen(\x3-\x1,\y3-\y1)}, \n2={veclen(\x3-\x2,\y3-\y2)}, \n3={#2},
      \n{test}={((\n1 < \n3)||(\n2 < \n3))?1:0}
      in
      \pgfextra{
        \pgfmathtruncatemacro\val{\n{test}}
        \ifnum\val=1 %
        \@latex@error{radius too long to wrap!}{}
        \fi
        \pgfinterruptpath
        \node[line width=0,circle,minimum size=2*\n3,inner sep=0] (cir) at (#1) {};
        \coordinate (i1cw) at (tangent cs:node=cir,point={(\p1)},solution=1);
        \coordinate (i1acw) at (tangent cs:node=cir,point={(\p1)},solution=2);
        \coordinate (i2cw) at (tangent cs:node=cir,point={(\p2)},solution=2);
        \coordinate (i2acw) at (tangent cs:node=cir,point={(\p2)},solution=1);
        \endpgfinterruptpath
      }
      let
      \p{i1cw}=(i1cw), \p{i2cw}=(i2cw),
      \p{i1acw}=(i1acw), \p{i2acw}=(i2acw)
      in
      \pgfextra{
        % acw
        \pgfmathsetmacro\angstartacw{atan2(\y{i1acw}-\y1,\x{i1acw}-\x1)+90}
        \pgfmathsetmacro\angendacw{atan2(\y{i2acw}-\y2,\x{i2acw}-\x2)-90}
        \pgfmathsetmacro\angendacw{\angendacw>\angstartacw?\angendacw-360:\angendacw}
        \pgfmathsetmacro\angendacw{\angendacw<\angstartacw-360?\angendacw+360:\angendacw}
        % cw
        \pgfmathsetmacro\angstartcw{atan2(\y{i1cw}-\y1,\x{i1cw}-\x1)-90}
        \pgfmathsetmacro\angendcw{atan2(\y{i2cw}-\y2,\x{i2cw}-\x2)+90}
        \pgfmathsetmacro\angendcw{\angendcw<\angstartcw?\angendcw+360:\angendcw}
        \pgfmathsetmacro\angendcw{\angendcw>\angstartcw+360?\angendcw-360:\angendcw}
        % test
        \pgfmathtruncatemacro\difftest{(\angendcw-\angstartcw<\angstartacw-\angendacw)?1:0}
        \ifnum\difftest=1 %
        % choice: cw
        \pgfinterruptpath
        \coordinate (i1) at (i1cw);
        \coordinate (i2) at (i2cw);
        \endpgfinterruptpath
        \pgfmathsetmacro\angstart{\angstartcw}
        \pgfmathsetmacro\angend{\angendcw}
        \else
        % choice: acw
        \pgfinterruptpath
        \coordinate (i1) at (i1acw);
        \coordinate (i2) at (i2acw);
        \endpgfinterruptpath
        \pgfmathsetmacro\angstart{\angstartacw}
        \pgfmathsetmacro\angend{\angendacw}
        \fi
      }
      -- (i1) arc[start angle=\angstart,end angle=\angend,radius=\n3] --(\p2)
    },
  },
}
\makeatother

\begin{document}
\foreach \i in {1,...,36}{
  \begin{tikzpicture}
    \pgfmathsetmacro\rnda{rnd*360}
    \pgfmathsetmacro\rndb{rnd*360}
    \pgfmathsetmacro\rndc{rnd*4mm}
    % \pgfmathsetmacro\rnda{\i}
    % \pgfmathsetmacro\rndb{0}
    % \pgfmathsetmacro\rndc{1cm}
    \coordinate (a) at (\rnda:2cm);
    \coordinate (b) at (\rndb:2cm);
    \coordinate (c) at (0,0);
    \pgfmathsetmacro\radius{\rndc};

    \draw[gray] (c) circle(\radius pt);
    \draw[orange,line width=.8pt] (a) to[cw wrap around={c by \radius pt}] (b);
    \draw[lime,line width=.8pt] (a) to[acw wrap around={c by \radius pt}] (b);
    \draw[black] (a) to[wrap around={c by \radius pt}] (b);

    \path circle(2cm);
  \end{tikzpicture}
}
\end{document}
share|improve this answer

Perhaps not as versatile as the other answers, but just another way of doing things. It requires TikZ 3.0.0:

\documentclass[tikz,border=0.125cm]{standalone}
\usepackage{tikz}
\usetikzlibrary{calc}
\usetikzlibrary{math}
\begin{document}
\tikzset{x=1pt,y=1pt}
\tikzmath{%
  coordinate \p,\q,\c;
  for \y in {-60,-50,...,70,60,...,-50}{
    \p1 = (60,\y);
    \p2 = (0,-60);  
    \c = (10,10);
    \r = 20;
    for \i in {1,2}{
      \l{\i} = veclen(\py{\i}-\cy, \px{\i}-\cx);
      \a{\i} = atan2(\py{\i}-\cy, \px{\i}-\cx);
      \b{\i} = acos(\r/\l{\i});
    };
    \s = \a1+\b1;
    \t = \a2-\b2;
    if \t<\s then {
      \t = \t + 360;
    };
    {
      \begin{tikzpicture}
        \useasboundingbox (-10,-60) rectangle (60,70);
        \draw (\c) circle [radius=\r];
        \draw [red] (\p1) -- ($(\c)+(\s:\r)$) arc (\s:\t:\r) -- (\p2);
      \end{tikzpicture}
    };
  };
}
\end{document}

enter image description here

share|improve this answer

We can do this neatly in Metapost as well. The fullcircle path defined by plain Metapost has 8 (more or less) evenly spaced points around it. Point 0 corresponds to 3 o'clock, point 2 to 12 o'clock, and so on. The direction macro provides you with the tangent vector.

beginfig(1);
path C; C = fullcircle scaled 20;
a = 0.4; b = 2.6;
z0 = point a of C - 20*(unitvector direction a of C);
z1 = point b of C + 20*(unitvector direction b of C);
fill C withcolor .7white;
drawarrow z0 -- subpath (a,b) of C -- z1 withcolor .637 red;
endfig;

Vary a and b in this example to get the angle you desire.

a=0.4 and b=2.6 produces this: enter image description here

a=0 and b=2 produces this: enter image description here

a=1 and b=7 this: enter image description here

and so on.

If you prefer to wrap around an ellipse, try something like C = fullcircle xscaled 20 yscaled 10.

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.