30

Can I modify the existing tikz coil decoration, so that the downstrokes are not drawn in a certain interval, to produce a picture like this?

coil

Edit: Previous attempts, code and MWE has been moved to or superseded by my own answer.

  • I am pretty sure you can, but you should post what you have tried so far, so that those trying to help you have something to start from. – Peter Grill Oct 21 '11 at 23:19
  • Fair enough, TikZ does have a steep learning curve. But to start, see Page 323 of the TikZ manual and get one of the coil examples to compile (I think the 2nd one is good). Figure out the appropriate \usetickzlibrary commands required and try to build a horizontal coil by replacing the (0,0)--(3,1) with (0,0)--(3,0) and eliminating the rest of the path (also eliminate the examplefill). Adjust the coil parameters to get you something close, and place a rectangle on top of it. Now you have something to start with, and people here can work on figuring out hot to hide some of the lines. – Peter Grill Oct 22 '11 at 01:24
  • Ok, so I figured out the packages required: Besides the tikz package, you'll also need \usetikzlibrary{intersections,decorations,decorations.markings,decorations.text,decorations.pathmorphing}. This is the part of these problems that I dislike so it would be immensely helpful when asking the question so that people can focus on the hard part of the question. Oh, intersections isn't really required for the coil example but I suspect that one of the possible solutions will require that. – Peter Grill Oct 22 '11 at 01:33
  • Sorry... I_do_know how to draw the coil, but I do not know how to modify an existing decoration. – hpekristiansen Oct 23 '11 at 12:19
  • You should input it before the \begin{document}. As you are not using it as a TikZ library, you also need to manually load the decorations library (\usetikzlibrary{decorations}) first. Beyond that, you will need to give some indication of the errors that your code is giving before someone can help you. – Andrew Stacey Feb 06 '12 at 08:18
  • @ Andrew Stacey: I have tried both - it gives the same error: ! Package tikz Error: You need to load a decoration library. ,but that is the last thing, that I want since I want the coil from pm.tex, so that I can modify it. – hpekristiansen Feb 06 '12 at 11:40
  • 1
    @Hans-PeterE.Kristiansen For the time being, just load another decoration library (for a robust solution, you would make your pm.tex file a new decoration library). – Andrew Stacey Feb 06 '12 at 12:07
  • @Andrew Stacey: The missing decoration lib error was just preventing me from seeing the real problem. -I can not \input the copied .tex file - see my updated question. Btw - thanks for helping. – hpekristiansen Feb 06 '12 at 12:43
  • 1
    Ah, that's a catcode issue and is because you are merely \inputing the document. Rename your file to tikzlibrarydecorations.mydecorations.code.tex and use \tikzlibrary{decorations.mydecorations} (in addition to another decorations library as I said before). – Andrew Stacey Feb 06 '12 at 13:20
  • An aesthetic note: you should make the "front" of the rod (as seen from the rod's end) bend the same way as the coils do as they pass in front of the rod. Your picture hurt my brain when I saw it, though it took a little time to determine why. :-) – Niel de Beaudrap Feb 06 '12 at 14:10
  • @Andrew Stacey: Cool - it is working. – hpekristiansen Feb 06 '12 at 14:32
  • @Niel de Beaudrap: Yes - I know. It was done quickly(and apparently without thinking) in inkscape, but I do not have the file anymore, so it will not be corrected before it is working in inkscape. – hpekristiansen Feb 06 '12 at 14:35
  • @Andrew Stacey: I am almost finished - see my solution. Do you know how to use my decorations without loading other decorations? - you wrote something about a robust solution!? – hpekristiansen Feb 06 '12 at 16:18
  • Take a look at, say, tikzlibrarydecorations.pathmorphing.code.tex. That loads the PGF decorations module and then the specific decoration code. So put your code in, say, pgflibrarydecorations.mydecorations.code.tex and then create tikzlibrarydecorations.mydecorations.code.tex as an almost exact copy of the pathmorphing one. Then put both files somewhere where TeX will find them. – Andrew Stacey Feb 06 '12 at 19:52
  • This is a bit off topic perhaps, but has anyone noticed that the perspective in above image is backward? – romeovs Feb 06 '12 at 21:18
  • @romeovs: yes -see Niel de Beaudrap's comment – hpekristiansen Feb 06 '12 at 21:34
  • @Hans-PeterE.Kristiansen: Oops, read over that one. Sorry. – romeovs Feb 06 '12 at 21:34

3 Answers3

34

Here is a proof-of-concept solution for drawing the coil by parametrizing it and drawing the front and back parts separately using plot paths:

\documentclass{article}
\usepackage{tikz}

\begin{document}
\begin{tikzpicture}
    % Define a formula for the coil.
    % This is what the numbers mean:
    % 0.3 ... how far the rings are apart
    % 0.4 ... how much from the side the rings are seen (try 0 and the same as the radius)
    % 1.5 ... radius of the rings
    \def\coil#1{
        {0.3 * (2*#1 + \t) + 0.4*sin(\t * pi r))},
        {1.5 * cos(\t * pi r)}
        }

    % Draw the part of the coil behind the rectangle
    \foreach \n in {0,1,...,10} {
        \draw[domain={0:1},smooth,variable=\t,samples=15]
            plot (\coil{\n}); 
        }

    % Draw the rectangle
    \filldraw[fill=white] (-0.5,-1) rectangle (7,1);

    % Draw the part of the coil in front of the rectangle
    \foreach \n in {0,1,...,10} {
        \draw[domain={1:2},smooth,variable=\t,samples=15,
              preaction={draw,white,line width=3pt}     % remove if undesired
             ]
            plot (\coil{\n});
        }
\end{tikzpicture}
\end{document}

result

Caramdir
  • 89,023
  • 26
  • 255
  • 291
27

Code saved in pgflibrarydecorations.mypathmorphing.code.tex

\usepgfmodule{decorations}

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

\pgfdeclaredecoration{coilup}{coil}
{
  \state{coil}[switch if less than=%
    1.5\pgfdecorationsegmentlength+%
    \pgfdecorationsegmentaspect\pgfdecorationsegmentamplitude+%
    \pgfdecorationsegmentaspect\pgfdecorationsegmentamplitude to last,
               width=+\pgfdecorationsegmentlength]
  {
    \pgfpathcurveto
    {\pgfpoint@oncoil{0    }{ 0.555}{1}}
    {\pgfpoint@oncoil{0.445}{ 1    }{2}}
    {\pgfpoint@oncoil{1    }{ 1    }{3}}
    \pgfpathmoveto{\pgfpoint@oncoil{1    }{-1    }{9}}
    \pgfpathcurveto
    {\pgfpoint@oncoil{0.445}{-1    }{10}}
    {\pgfpoint@oncoil{0    }{-0.555}{11}}
    {\pgfpoint@oncoil{0    }{ 0    }{12}}
  }
  \state{last}[width=.5\pgfdecorationsegmentlength+%
    \pgfdecorationsegmentaspect\pgfdecorationsegmentamplitude+%
    \pgfdecorationsegmentaspect\pgfdecorationsegmentamplitude,next state=final]
  {
    \pgfpathcurveto
    {\pgfpoint@oncoil{0    }{ 0.555}{1}}
    {\pgfpoint@oncoil{0.445}{ 1    }{2}}
    {\pgfpoint@oncoil{1    }{ 1    }{3}}
    \pgfpathmoveto{\pgfpoint@oncoil{2    }{ 0    }{6}}
  }
  \state{final}
  {
  \pgfpathmoveto{\pgfpointdecoratedpathlast}
  }
}


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

\pgfdeclaredecoration{coildown}{coil}
{
  \state{coil}[switch if less than=%
    1.5\pgfdecorationsegmentlength+%
    \pgfdecorationsegmentaspect\pgfdecorationsegmentamplitude+%
    \pgfdecorationsegmentaspect\pgfdecorationsegmentamplitude to last,
               width=+\pgfdecorationsegmentlength]
  {
    \pgfpathmoveto{\pgfpoint@oncoil{1    }{1    }{3}}
    \pgfpathcurveto
    {\pgfpoint@oncoil{1.555}{ 1    }{4}}
    {\pgfpoint@oncoil{2    }{ 0.555}{5}}
    {\pgfpoint@oncoil{2    }{ 0    }{6}}
    \pgfpathcurveto
    {\pgfpoint@oncoil{2    }{-0.555}{7}}
    {\pgfpoint@oncoil{1.555}{-1    }{8}}
    {\pgfpoint@oncoil{1    }{-1    }{9}}
  }
  \state{last}[width=.5\pgfdecorationsegmentlength+%
    \pgfdecorationsegmentaspect\pgfdecorationsegmentamplitude+%
    \pgfdecorationsegmentaspect\pgfdecorationsegmentamplitude,next state=final]
  {
    \pgfpathmoveto{\pgfpoint@oncoil{1    }{ 1    }{3}}
    \pgfpathcurveto
    {\pgfpoint@oncoil{1.555}{ 1    }{4}}
    {\pgfpoint@oncoil{2    }{ 0.555}{5}}
    {\pgfpoint@oncoil{2    }{ 0    }{6}}
  }
  \state{final}
  {
  \pgfpathlineto{\pgfpointdecoratedpathlast}
  }
}

\def\pgfpoint@oncoil#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%
}

\endinput

and highlevel wrapper tikzlibrarydecorations.mypathmorphing.code.tex

\usetikzlibrary{decorations}
\usepgflibrary{decorations.mypathmorphing}

\endinput

Usage:

\documentclass{minimal}
\usepackage{tikz}
\usetikzlibrary{decorations.pathmorphing}
\usetikzlibrary{decorations.mypathmorphing}
\begin{document}
\begin{tikzpicture}
\draw [decorate, decoration={coil,     aspect=0.4, amplitude=5 mm,  segment length=4mm}, gray, very thick] (0,0) -- (4,0);
\draw [decorate, decoration={coilup,   aspect=0.4, amplitude=5 mm,  segment length=4mm}, red] (0,0) -- (4,0);
\draw [decorate, decoration={coildown, aspect=0.4, amplitude=5 mm,  segment length=4mm}, blue] (0,0) -- (4,0);
\end{tikzpicture}
\end{document}

color coil

Result:

\documentclass{minimal}
\usepackage{tikz}
\usetikzlibrary{decorations.mypathmorphing}
\begin{document}
\begin{tikzpicture}
\draw [decorate, decoration={coilup, aspect=0.4, amplitude=5 mm,  segment length=4mm}] (0,0) -- (4,0);
\filldraw [fill=white] (-1,-0.4) rectangle (5,0.4);
\draw [decorate, decoration={coildown, aspect=0.4, amplitude=5 mm,  segment length=4mm}] (0,0) -- (4,0);
\end{tikzpicture}
\end{document}

coil wrap


How it was done:

@Jan Hlavacek have helped me to find the right file:

hpek@melda:/usr/local/texlive/2010/texmf-dist/tex/generic/pgf$ grep -R coil .|head -1
./libraries/decorations/pgflibrarydecorations.pathmorphing.code.tex:% coil decoration
hpek@melda:/usr/local/texlive/2010/texmf-dist/tex/generic/pgf$ cp libraries/decorations/pgflibrarydecorations.pathmorphing.code.tex ~/temp/coilMod/

and with help from @Andrew Stacey, I maneged to use the file with \usetikzlibrary{decorations.mydecorations} with the copied code in tikzlibrarydecorations.mydecorations.code.tex. The file was then modified to create the above code.

  • Is it possible to remove the horizontal line at the end? – mas Mar 17 '16 at 13:28
  • 2
    @mas: Yes. -note that this line segment is precisely the same as in the original coil decoration. You have several options to remove it: You can adjust your endpoint to be a integer multiplum of coils. You can hide the line with clip. You can edit the my code -try removing \pgfpathlineto{\pgfpointdecoratedpathlast}. – hpekristiansen Mar 17 '16 at 21:42
9

Pgf does not have any user interface for modifying existing decorations. Your best bet will be creating a new decoration, by starting with the original coil decoration and defining two new decorations, say coil-front and coil-back.

To find the definition of the original coil decoration, cd to your pgf directory (/usr/local/texlive/2011/texmf-dist/tex/generic/pgf/ on my system, yours may be different), then run rgrep coil *, taht will find number of occurrences of the word coil, but luckily all in the same file, libraries/decorations/pgflibrarydecorations.pathmorphing.code.tex. Open that file and search for the word coil. You will then have the original definition of coil:

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

\pgfdeclaredecoration{coil}{coil}
{
  \state{coil}[switch if less than=%
    1.5\pgfdecorationsegmentlength+%
    \pgfdecorationsegmentaspect\pgfdecorationsegmentamplitude+%
    \pgfdecorationsegmentaspect\pgfdecorationsegmentamplitude to last,
               width=+\pgfdecorationsegmentlength]
  {
    \pgfpathcurveto
    {\pgfpoint@oncoil{0    }{ 0.555}{1}}
    {\pgfpoint@oncoil{0.445}{ 1    }{2}}
    {\pgfpoint@oncoil{1    }{ 1    }{3}}
    \pgfpathcurveto
    {\pgfpoint@oncoil{1.555}{ 1    }{4}}
    {\pgfpoint@oncoil{2    }{ 0.555}{5}}
    {\pgfpoint@oncoil{2    }{ 0    }{6}}
    \pgfpathcurveto
    {\pgfpoint@oncoil{2    }{-0.555}{7}}
    {\pgfpoint@oncoil{1.555}{-1    }{8}}
    {\pgfpoint@oncoil{1    }{-1    }{9}}
    \pgfpathcurveto
    {\pgfpoint@oncoil{0.445}{-1    }{10}}
    {\pgfpoint@oncoil{0    }{-0.555}{11}}
    {\pgfpoint@oncoil{0    }{ 0    }{12}}
  }
  \state{last}[width=.5\pgfdecorationsegmentlength+%
    \pgfdecorationsegmentaspect\pgfdecorationsegmentamplitude+%
    \pgfdecorationsegmentaspect\pgfdecorationsegmentamplitude,next state=final]
  {
    \pgfpathcurveto
    {\pgfpoint@oncoil{0    }{ 0.555}{1}}
    {\pgfpoint@oncoil{0.445}{ 1    }{2}}
    {\pgfpoint@oncoil{1    }{ 1    }{3}}
    \pgfpathcurveto
    {\pgfpoint@oncoil{1.555}{ 1    }{4}}
    {\pgfpoint@oncoil{2    }{ 0.555}{5}}
    {\pgfpoint@oncoil{2    }{ 0    }{6}}
  }
  \state{final}
  {
    \pgfpathlineto{\pgfpointdecoratedpathlast}
  }
}

\def\pgfpoint@oncoil#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%
}

You will have to make two copies of that, and modify one to draw only the front, the other one to draw only the back. It will be an interesting exercise in pgf programming.

Jan Hlavacek
  • 19,242
  • hpek@melda:/usr/local/texlive/2010/texmf-dist/tex/generic/pgf$ grep -R coil . ./libraries/decorations/pgflibrarydecorations.pathmorphing.code.tex:% coil decoration – hpekristiansen Oct 23 '11 at 19:42