39

If I draw a coil in tikz I usually do something like this:

\documentclass[tikz]{standalone}
\usetikzlibrary{decorations.pathmorphing,patterns}

\begin{document}

\begin{tikzpicture}
\draw[decoration={aspect=0.3, segment length=3.2mm, amplitude=3mm,coil},decorate,opacity=0.9] (1,0) -- (3,0);
\end{tikzpicture}
\end{document}

Which looks like this:

enter image description here

In some cases (for example in eletrodynamics) it might matter if the coil is right or left handed.

How can I modify the code above such that it looks like this:

enter image description here

If you have another, maybe better way to visualize the direction I am interested in it too.

Julia
  • 1,648
  • 1
    The image quality of your second image is not very good. Can you clarify which aspect of it you'd like to replicate in your own image? There seem to be a change in line colour/line width as well as a small gap where the wire overlaps, but it is hard to see clearly in this pixelated image. – samcarter_is_at_topanswers.xyz Sep 06 '18 at 12:43
  • It only matters in a schematic for transformers. One typically adds a dot toward one end of the coil to indicate polarity. – John Kormylo Sep 06 '18 at 12:52
  • @samcarter I want the line with change and the little gap. – Julia Sep 06 '18 at 13:27
  • Are you trying to draw DNAs by the way? :-) – hola Jan 22 '19 at 11:25
  • No, its about coils in EM-circuits for induction experiments. – Julia Feb 05 '19 at 06:37

2 Answers2

68

The starting point of all this is the code in pgflibrarydecorations.pathmorphing.code.tex. I acknowledge a comment by joojaa that pointed out that the halo was not quite right in the original answers. I also appreciate a comment by Robert Harvey, who encouraged me to clean up. What I did is to modify the coil decoration of the decorations.pathmorphing library. There is still a lot of room for improvement. The keys in the beginning allow you to adjust the color, opacity, width of coil and additional width of the halo, I hope the names I gave to these parameters are self-explanatory.

enter image description here

\documentclass[tikz,border=3.14mm]{standalone}
\usetikzlibrary{decorations.pathmorphing}
\pgfkeys{%
/pgf/decoration/.cd,
3d coil color/.store in=\TDCoilColor, 
3d coil color/.initial=black,
3d coil color=black,
3d coil width/.store in=\TDCoilWidth, 
3d coil width/.initial=0.4pt,
3d coil width=0.4pt,
3d coil dist/.store in=\TDCoilDist, 
3d coil dist/.initial=0.6pt,
3d coil dist=0.6pt,
3d coil opacity/.store in=\TDCoilOpacity, 
3d coil opacity/.initial=1,
3d coil opacity=1
}

\makeatletter % https://tex.stackexchange.com/a/219088/121799
\tikzset{get stroke color/.code={%
    \expandafter\global% Jump over, now we have \global
    \expandafter\let% Jump over now we have \global\let
    \expandafter\pgfsavedstrokecolor% Jump we have \global\let\pgf...
    \csname\string\color@pgfstrokecolor\endcsname% Finally expand this and put it at the end 
    },                                           % \global\let\pgf...{} in expanded form 
    restore stroke color/.code={\pgf@setstrokecolor#1},
}
\def\pgfpoint@onthreedcoil#1#2#3{%
  \pgf@x=#1\pgfdecorationsegmentamplitude%
  \pgf@x=\pgfdecorationsegmentaspect\pgf@x%
  \pgf@y=#2\pgfdecorationsegmentamplitude%
  \pgf@xa=0.083333333333\pgfdecorationsegmentlength%
  \advance\pgf@x by#3\pgf@xa%
  \advance\pgf@x by-\initialoffset pt%
}

% coil decoration
%
% Parameters: \pgfdecorationsegmentamplitude, \pgfdecorationsegmentlength,

\pgfdeclaredecoration{3d coil}{initial}
{ 
    \state{initial}[width=0.25*\pgfdecorationsegmentlength+\pgfdecorationsegmentaspect*\pgfdecorationsegmentamplitude,
    next state=coil, persistent precomputation={
    \tikzset{get stroke color}
    \pgfmathsetmacro{\initialoffset}{0.25*\pgfdecorationsegmentlength+\pgfdecorationsegmentaspect*\pgfdecorationsegmentamplitude}
  }]
  {%
    \pgfpathmoveto{\pgfpointorigin} 
    \pgfsetstrokecolor{\TDCoilColor}
    \pgfsetstrokeopacity{\TDCoilOpacity}
    \pgfsetlinewidth{1.5*\TDCoilWidth} 
    \pgfpathcurveto
    {\pgfpoint@oncoil{0    }{ 0.555}{1}}
    {\pgfpoint@oncoil{0.445}{ 1    }{2}}
    {\pgfpoint@oncoil{1    }{ 1    }{3}}
    \pgfusepath{stroke}
    \pgfcoordinate{TD@coilast}{\pgfpoint@oncoil{1    }{ 1    }{3}} 
    }
  \state{coil}[switch if less than=%
    1.25\pgfdecorationsegmentlength+%
    \pgfdecorationsegmentaspect\pgfdecorationsegmentamplitude+%
    \pgfdecorationsegmentaspect\pgfdecorationsegmentamplitude to last,
               width=+\pgfdecorationsegmentlength]
    { % line in the back
    %
    \pgfsetstrokecolor{\TDCoilColor}
    \pgfsetfillcolor{\TDCoilColor}
    \pgfsetstrokeopacity{\TDCoilOpacity}
    \pgfpathmoveto{\pgfpointanchor{TD@coilast}{center}}
    \pgfsetlinewidth{\TDCoilWidth} 
    \pgfpathcurveto
    {\pgfpoint@onthreedcoil{1.555}{ 1    }{4}}
    {\pgfpoint@onthreedcoil{2    }{ 0.555}{5}}
    {\pgfpoint@onthreedcoil{2    }{ 0    }{6}}
    \pgfpathcurveto
    {\pgfpoint@onthreedcoil{2    }{-0.555}{7}}
    {\pgfpoint@onthreedcoil{1.555}{-1    }{8}}
    {\pgfpoint@onthreedcoil{1    }{-1    }{9}}
    \pgfusepath{stroke} 
    %
    % white background for front thick part
    %
    \pgfsetstrokeopacity{1}
    \pgfsetstrokecolor{white}
    \pgfsetfillcolor{white}
    \pgfsetlinewidth{1.5*\TDCoilWidth+1.5*\TDCoilDist}
    \pgfpathmoveto{\pgfpoint@onthreedcoil{1    }{ 1    }{3}}
    \pgfpathmoveto{\pgfpoint@onthreedcoil{1    }{-1    }{9}}
    % draw forward
    \pgfpathcurveto
    {\pgfpoint@onthreedcoil{0.445}{-1    }{10}}
    {\pgfpoint@onthreedcoil{0    }{-0.555}{11.25}}
    {\pgfpoint@onthreedcoil{0    }{ 0    }{12.5}}
    \pgfpathcurveto
    {\pgfpoint@onthreedcoil{0    }{ 0.555}{13.25}}
    {\pgfpoint@onthreedcoil{0.445}{ 1    }{14.25}}
    {\pgfpoint@onthreedcoil{1    }{ 1    }{15}}
    % draw the curve back
    \pgfpathcurveto
    {\pgfpoint@onthreedcoil{0.445}{ 1    }{14}}
    {\pgfpoint@onthreedcoil{0    }{ 0.555}{12.75}}
    {\pgfpoint@onthreedcoil{0    }{ 0    }{11.5}}
    \pgfpathcurveto
    {\pgfpoint@onthreedcoil{0    }{-0.555}{10.75}}
    {\pgfpoint@onthreedcoil{0.445}{-1    }{10}}
    {\pgfpoint@onthreedcoil{1    }{-1    }{9}}
    \pgfusepath{stroke,fill} 
    % 
    % draw the thick foreground path
    %
    \pgfsetstrokecolor{\TDCoilColor}
    \pgfsetfillcolor{\TDCoilColor}
    \pgfsetstrokeopacity{\TDCoilOpacity}
    \pgfpathmoveto{\pgfpoint@onthreedcoil{1    }{ 1    }{3}}
    \pgfsetlinewidth{\TDCoilWidth} 
    % forward shifted +
    \pgfpathmoveto{\pgfpoint@onthreedcoil{1    }{-1    }{9}}
    \pgfpathcurveto
    {\pgfpoint@onthreedcoil{0.445}{-1    }{10}}
    {\pgfpoint@onthreedcoil{0    }{-0.555}{11.25}}
    {\pgfpoint@onthreedcoil{0    }{ 0    }{12.5}}
    \pgfpathcurveto
    {\pgfpoint@onthreedcoil{0    }{ 0.555}{13.25}}
    {\pgfpoint@onthreedcoil{0.445}{ 1    }{14.25}}
    {\pgfpoint@onthreedcoil{1    }{ 1    }{15}}
    % draw the curve back shfted -
    \pgfpathcurveto
    {\pgfpoint@onthreedcoil{0.445}{ 1    }{14}}
    {\pgfpoint@onthreedcoil{0    }{ 0.555}{12.75}}
    {\pgfpoint@onthreedcoil{0    }{ 0    }{11.5}}
    \pgfpathcurveto
    {\pgfpoint@onthreedcoil{0    }{-0.555}{10.75}}
    {\pgfpoint@onthreedcoil{0.445}{-1    }{10}}
    {\pgfpoint@onthreedcoil{1    }{-1    }{9}}
    \pgfusepath{stroke,fill} % <- added
    \pgfcoordinate{TD@coilast}{\pgfpoint@onthreedcoil{1    }{ 1    }{15}} 
  }
  \state{last}[width=.25\pgfdecorationsegmentlength+%
    \pgfdecorationsegmentaspect\pgfdecorationsegmentamplitude+%
    \pgfdecorationsegmentaspect\pgfdecorationsegmentamplitude,next state=final]
  {
    \pgfsetstrokecolor{\TDCoilColor}
    \pgfsetstrokeopacity{\TDCoilOpacity}
    \pgfsetlinewidth{\TDCoilWidth}
    \pgfpathmoveto{\pgfpointanchor{TD@coilast}{center}}
    \pgfpathcurveto
    {\pgfpoint@onthreedcoil{1.555}{ 1    }{4}}
    {\pgfpoint@onthreedcoil{2    }{ 0.555}{5}}
    {\pgfpoint@onthreedcoil{2    }{ 0    }{6}}
  }
  \state{final}
  {
    \pgfpathlineto{\pgfpointdecoratedpathlast}
    \pgfusepath{stroke}
    \tikzset{restore stroke color/.expand once=\pgfsavedstrokecolor}
  }
}
\makeatother

\begin{document}
\begin{tikzpicture}
\draw[decoration={3d coil color=blue,aspect=0.35, segment length=3.1mm, amplitude=3mm,3d coil},
decorate] (0,0) -- (0,3);
\draw[decoration={3d coil color=red,3d coil opacity=0.9,aspect=0.45, segment length=3.1mm, amplitude=3mm,3d coil},
decorate] (2,3) -- (2,0);
\draw[decoration={3d coil color=green!60!black,3d coil opacity=0.9,aspect=0.35, segment length=3.1mm, amplitude=3mm,3d coil},
decorate] (4,3) to[out=0,in=90] (6,0);
\end{tikzpicture}
\end{document}

FUN: The mandatory animation can be made with the same preamble + \usepackage{tikzmarmots} and

\begin{document}
\foreach \X [evaluate=\X as \Y using {sin(\X)}]in {0,10,...,350}
{
\begin{tikzpicture}
\path[use as bounding box] (-1,-0.2) rectangle (1,4);
\draw[decoration={3d coil color=blue,aspect=0.35, segment
length={(1.2+0.7*\Y)*1mm}, amplitude=3mm,3d coil},
decorate] (0,0) -- (0,2);
\begin{scope}[shift={(-0.9,1+0.5*\Y)}]
\marmot[teeth,whiskers]
\end{scope}
\end{tikzpicture}}
\end{document}

enter image description here

  • 7
    The decorations in pgf, it's the most difficult, wonderful work, bravo! – AndréC Sep 06 '18 at 16:05
  • @AndréC Merci, but I am actually not really happy, What I would love to achieve is that it is not necessary to set the color, opacity and so on explicitly. And then there are more downsides. Assume I was able to achieve this, then the decomposition of the path in little segments has further downsides such as the fact that a dashed line will not look nice. So there is a lot of room for improvement... –  Sep 06 '18 at 16:09
  • You can draw two lines that overlap when they are thin but then move away from each other on the thicker part so no step function in thickness LIkewise you can draw a white line on that is thicker on the top part so overlap is along path – joojaa Sep 06 '18 at 16:30
  • @marmot its slightly less work. The strokes overlap anyway. So no particular reason to fill stuff except pedantry – joojaa Sep 06 '18 at 16:40
  • @marmot you dont need the duplicate line in the thinner sections, but yeah, Here is what i came up with this using the trick – joojaa Sep 06 '18 at 17:02
  • @joojaa just for the records: I did use fill in my updated answer because otherwise there would be an empty spot. Thanks for pointing out the bug in my original answer, though! –  Sep 06 '18 at 19:36
  • 4
    Another beautiful piece of marmot magic! – Max Sep 07 '18 at 18:04
  • Sorry for asking this question, but your answer is way advanced than what I need and J Leon V is away for days. Could you explain to me how to edit J Leon V's original MWE and make Coil has an input length of the curly path and overall length input, so the final drawing is like straight line then curly path then straight line? – Diaa Dec 17 '18 at 22:24
  • @Diaa There are two MWE's in J. Leon V's answer, which one are you referring to? And can't you just draw the path in three stretches, i.e. ordinary draw, then a decoration, then draw (or is the path to complicated for that)? –  Dec 17 '18 at 22:28
  • I am talking about his first MWE. I don't understand why Coil style draws some straight line at the end while it doesn't at the start. So, I need to tweak it by defining two arguments of the curly length and the overall length, so TikZ will draw equal straight lines at the start and the end. – Diaa Dec 17 '18 at 22:31
  • 1
    @Diaa Coil has a straight line because the length of one coil (i.e. the segment length) does not divide the length of the path. Coil2 uses a different approach in which the length of the coil is set to be 1/10 of the length of the path, which is why there is no straight line piece. To overcome the problem of leftovers you may use Jake's complete sines. –  Dec 17 '18 at 22:39
  • Thanks for your answer, last question if you don't mind. For his Coil2 style, how can I connect it with some straight lines at both ends? I reied drawing lines at the ends, but they pass through the coil center. – Diaa Dec 17 '18 at 22:49
  • 1
    @Diaa One would have to rewrite the style. The "problem" (I use parentheses because this is not at all a criticism of the nice macros) is that the path starts with an offset. If you replace in his code (90: 0.2 and 0.4) by (90: 0.2 and 0.4) coordinate(coilaux1-\pgfkeysvalueof{/pgf/decoration/mark info/sequence number}) and (0,0)++(90: 0.2 and 0.4) by (0,0)++(90: 0.2 and 0.4) coordinate(coilaux0-\pgfkeysvalueof{/pgf/decoration/mark info/sequence number}), you can access the start as (coilaux0-1) and the end as (coilaux1-n) with an appropriate n. –  Dec 17 '18 at 23:20
  • Many thanks for the help, and I am sorry for ruining your beautiful answer :) – Diaa Dec 17 '18 at 23:24
  • When does the Marmot on its spring take off? – Tarass Jan 19 '19 at 09:10
  • @Tarass This is answered here. ;-) –  Jan 19 '19 at 15:04
32

If you put negative values for aspect and amplitude, you can get the mirrored coil, add this in the @marmot's answer may complete the desired output; also I added an option using markings to get some similar drawing that uses scope and yscale to invert the marking, then a new variable to control the coil color,when the path is straight it has good result, markings path lacks bending good results.

EDIT: Added a control for marking step to improve bending results.

RESULT:

enter image description here

MWE:

\documentclass[tikz,border=20pt]{standalone}
\usetikzlibrary{decorations.pathmorphing,decorations.markings}

\begin{document}

\begin{tikzpicture}[
    CoilColor/.store in=\coilcolor,CoilColor=black,
    Step/.store in=\Step,Step=0.1,
    Coil/.style={
        double=black,
        draw=gray!50,
        decoration={
            #1,
            segment length=3mm,
            coil
            },
            decorate,
    },
    Coil2/.style={
        decorate,
        decoration={
            markings,
            mark= between positions 0 and 1 step \Step
            with {
                \begin{scope}[yscale=#1]
                \draw[xshift=9.2,fill,\coilcolor!70!black]
                    (0,0)++(-135: 0.2 and 0.4) 
                        .. controls +(-0.2,0) and +(-0.3,0) .. (90: 0.2 and 0.4)
                        .. controls +(-0.33,0) and +(-0.23,0) .. (-135: 0.2 and 0.4);                       
                \draw[white,line width=2pt]
                    (0,0)++(90: 0.2 and 0.4) 
                        .. controls +(0.3,0) and +(0.2,0) .. (-45: 0.2 and 0.4);
                \draw[fill=\coilcolor,\coilcolor]
                    (0,0)++(90: 0.2 and 0.4) 
                        .. controls +(0.3,0) and +(0.2,0) .. (-45: 0.2 and 0.4)
                        .. controls +(0.25,0) and +(0.35,0) .. (90: 0.2 and 0.4);
                \end{scope}                     
                    }
              }
        }
]
\draw[Coil={aspect=-0.3,amplitude=-3mm},blue] (0,0) -- ++ (0,-3);
\draw[Coil={aspect=0.3,amplitude=3mm},red] (1.5,0) -- ++ (0,-3);
\draw[Coil={aspect=0.3,amplitude=3mm},green!50!black] (3,0) arc (90:0:3);
\draw[Coil={aspect=-0.3,amplitude=-3mm}] (3,1) arc (90:0:4);

\draw[Coil2=-1,CoilColor=blue] (0,-4) -- ++ (0,-3);
\draw[Coil2=1,CoilColor=red] (1.5,-4) -- ++ (0,-3);
\draw[Coil2=-1,CoilColor=green!50!black,Step=0.065] (3,-4) arc (90:0:3);
\draw[Coil2=1,Step=0.048] (3,-3) arc (90:0:4);

\end{tikzpicture}
\end{document}

UPDATE: Acording to @marmot's suggestion closing the gaps and drawing all the variations, using \pgfdecoratedpathlength, /pgf/decoration/mark info/sequence number.

RESULT: enter image description here

MWE:

\documentclass[tikz,border=20pt]{standalone}
\usetikzlibrary{decorations.markings}

\begin{document}

\begin{tikzpicture}[
    CoilColor/.store in=\coilcolor,CoilColor=black,
    Step/.store in=\Step,Step=0.1,
    Width/.store in=\Width,Width=0.4,
    Coil2/.style={
        decorate,
        decoration={
            markings,
            mark= between positions 0 and 1 step \Step 
            with {
                \begin{scope}[yscale=#1]
                    \pgfmathparse{int(\pgfdecoratedpathlength/28.45*100*\Step)}
                    \edef\Hight{\pgfmathresult}
                    \ifnum\pgfkeysvalueof{/pgf/decoration/mark info/sequence number}=1
                        \path (0,0)++(90: \Hight/200 and \Width) coordinate (b);
                    \fi
                    \ifnum\pgfkeysvalueof{/pgf/decoration/mark info/sequence number}>1
                        \coordinate (b) at (d);
                    \fi
                    \path (b) arc (90:-135: \Hight/200 and \Width) coordinate (a);
                    \path (b) arc (90:-45: \Hight/200 and \Width) coordinate (c);
                    \path (b)++(\Hight/100,0) coordinate (d);
                    \draw[fill,\coilcolor!70!black]
                        (c)
                            .. controls +(-0.175,0) and +(-0.275,0) .. (d)
                            .. controls +(-0.325,0) and +(-0.225,0) .. (c);
                    \draw[white,line width=2pt]
                        (b)
                            .. controls +(0.3,0) and +(0.2,0) .. (c);
                    \draw[fill,\coilcolor]
                        (b)
                            .. controls +(0.275,0) and +(0.175,0) .. (c)
                            .. controls +(0.225,0) and +(0.325,0) .. (b);
                \end{scope}
            }
        }
    }
]

\draw[Coil2=-1,CoilColor=red,Step=0.15] (0.5,0) -- ++ (0,-3);
\draw[Coil2=1.5,CoilColor=magenta] (1.5,0) -- ++ (0,-3);

\draw[Coil2=1,CoilColor=green!70!black,Step=0.02] (0,-4) 
    to [in=90,out=0] ++(2.5,-1)
    to [in=180,out=-90] ++(2.5,-1)
    to [in=-90,out=0] ++(2,1.5)
    to [in=-90,out=90] ++(0.5,3) arc (0:90:2);

\draw[Coil2=-1,CoilColor=green!50!cyan,Step=0.02] (0,-5) 
    to [in=90,out=0] ++(1.5,-1)
    to [in=180,out=-90] ++(4,-1)
    to [in=-90,out=0] ++(3,2.5)
    to [in=-90,out=90] ++(0.5,4.5) arc (0:90:2);

\draw[Coil2=1,CoilColor=cyan!30!blue,Step=0.05] (5.7,-2) arc (360:0:1.5);
\draw[Coil2=-1,CoilColor=cyan!70!blue,Step=0.05] (6.5,-2) arc (360:0:1.5);

\end{tikzpicture}
\end{document}
J Leon V.
  • 11,533
  • 16
  • 47