5

When I use a decoration on objects that are closed, the merge point is usually ugly. The same problems also appears with arrows, even with the pre option: sometimes the zigzag stops "close" to the arrow symbol, and sometimes it stops far from the arrow symbol.

Any idea how I could correct this? I understand that for a fixed segment length, there is no real solution, but I'm thinking that if we allow the segment length to move a bit, then we could find the exact segment length to have a nice merge point.

enter image description here

MWE:

\documentclass[beamer,tikz,preview]{standalone}
\usetikzlibrary{positioning,decorations.pathreplacing,decorations.pathmorphing,shapes.geometric}
\begin{document}
\begin{standaloneframe}
  \begin{tikzpicture}[main style/.style={
        ellipse,draw,fill=blue!30,decorate,
        decoration={zigzag,segment length=1.1mm,amplitude=.5mm}
      }]
    % Ok
    \node[main style] at (0,0) {ABC};
  \end{tikzpicture}
\end{standaloneframe}
\end{document}

-- EDIT -- After marmot suggestion, I tried the new complete sines version to see if in the snake decoration it would looks nicer, but the drawing looks also a bit weird, especially in the east/west part. Note however that the "joints" looks better and that it may be enough for the zigzag style.

enter image description here

\documentclass[a4paper,12pt]{article}
\usepackage{tikz}
\usetikzlibrary{decorations,shapes.geometric,decorations.pathreplacing,decorations.pathmorphing}
\begin{document}

\pgfdeclaredecoration{complete sines}{initial}
{
    \state{initial}[
        width=+0pt,
        next state=sine,
        persistent precomputation={\pgfmathsetmacro\matchinglength{
            \pgfdecoratedinputsegmentlength / int(\pgfdecoratedinputsegmentlength/\pgfdecorationsegmentlength)}
            \setlength{\pgfdecorationsegmentlength}{\matchinglength pt}
        }] {}
    \state{sine}[width=\pgfdecorationsegmentlength]{
        \pgfpathsine{\pgfpoint{0.25\pgfdecorationsegmentlength}{0.5\pgfdecorationsegmentamplitude}}
        \pgfpathcosine{\pgfpoint{0.25\pgfdecorationsegmentlength}{-0.5\pgfdecorationsegmentamplitude}}
        \pgfpathsine{\pgfpoint{0.25\pgfdecorationsegmentlength}{-0.5\pgfdecorationsegmentamplitude}}
        \pgfpathcosine{\pgfpoint{0.25\pgfdecorationsegmentlength}{0.5\pgfdecorationsegmentamplitude}}
}
    \state{final}{}
}

\begin{tikzpicture}[
    every node/.style={
        decoration={
          complete sines,
          % zigzag,
            segment length=1mm,
            amplitude=1mm
        },
        decorate
    }]
\node[ellipse,draw]{Hello};
\end{tikzpicture}
\end{document}
  • 1
    Did you try the complete sines and try to rewrite them for zigzag (if that's not already done)? – user121799 Aug 24 '18 at 14:11
  • @marmot: Thanks, see my edit. Note however that I'm not sure to have the skill to adapt the decoration to zigzag, latex language does not like me I think ;-) But I think I'll give it a try. – tobiasBora Aug 24 '18 at 14:32
8

Here is a version that does complete zigzag instead of Jake's complete sines, which this answer is based on. EDIT: Special zigzag for closed cycles added. 2nd EDIT: special zigzag that arguably works better with at higher curvatures added. The "problem" in the standard zigzags is that the peak of the zigzag is shifted in tangent space. On the other hand, in the "tobias zigzag" decoration it is just above (or below) the origin in tangent space.

enter image description here

MWE

\documentclass[beamer,tikz,preview]{standalone}
\usetikzlibrary{positioning,decorations.pathreplacing,decorations.pathmorphing,shapes.geometric}
% https://tex.stackexchange.com/a/25689/121799
\pgfdeclaredecoration{complete sines}{initial}
{
    \state{initial}[
        width=+0pt,
        next state=sine,
        persistent precomputation={\pgfmathsetmacro\matchinglength{
            \pgfdecoratedinputsegmentlength / int(\pgfdecoratedinputsegmentlength/\pgfdecorationsegmentlength)}
            \setlength{\pgfdecorationsegmentlength}{\matchinglength pt}
        }] {}
    \state{sine}[width=\pgfdecorationsegmentlength]{
        \pgfpathsine{\pgfpoint{0.25\pgfdecorationsegmentlength}{0.5\pgfdecorationsegmentamplitude}}
        \pgfpathcosine{\pgfpoint{0.25\pgfdecorationsegmentlength}{-0.5\pgfdecorationsegmentamplitude}}
        \pgfpathsine{\pgfpoint{0.25\pgfdecorationsegmentlength}{-0.5\pgfdecorationsegmentamplitude}}
        \pgfpathcosine{\pgfpoint{0.25\pgfdecorationsegmentlength}{0.5\pgfdecorationsegmentamplitude}}
}
    \state{final}{}
}

\pgfdeclaredecoration{complete zigzag}{initial}{
\state{initial}[
        width=+0pt,
        next state=half up,
        persistent precomputation={\pgfmathsetmacro\matchinglength{
            \pgfdecoratedinputsegmentlength / int(\pgfdecoratedinputsegmentlength/\pgfdecorationsegmentlength)}
            \setlength{\pgfdecorationsegmentlength}{\matchinglength pt}
        }] {}
  \state{half up}[
        width=+.25\pgfdecorationsegmentlength,
        next state=big down]
        {\pgfpathlineto{\pgfqpoint{.25\pgfdecorationsegmentlength}{\pgfdecorationsegmentamplitude}}
    }
  \state{big down}[switch if less than=+.5\pgfdecorationsegmentlength to center finish,
                   width=+.5\pgfdecorationsegmentlength,
                   next state=big up]
  {
    \pgfpathlineto{\pgfqpoint{.5\pgfdecorationsegmentlength}{-\pgfdecorationsegmentamplitude}}
  }
  \state{big up}[switch if less than=+.5\pgfdecorationsegmentlength to center finish,
                 width=+.5\pgfdecorationsegmentlength,
                 next state=big down]
  {
    \pgfpathlineto{\pgfqpoint{.5\pgfdecorationsegmentlength}{\pgfdecorationsegmentamplitude}}
  }
  \state{center finish}[width=0pt, next state=final]{
  }
  \state{final}
  {
    \pgfpathlineto{\pgfpointdecoratedpathlast}
  }
}

\pgfdeclaredecoration{zigzag cycle}{initial}{
\state{initial}[
        width=+0pt,
        next state=half up,
        persistent precomputation={\pgfmathsetmacro\matchinglength{
            \pgfdecoratedinputsegmentlength / int(\pgfdecoratedinputsegmentlength/\pgfdecorationsegmentlength)}
            \setlength{\pgfdecorationsegmentlength}{\matchinglength pt}
        }] {}
  \state{half up}[
        width=+.25\pgfdecorationsegmentlength,
        next state=big down]
        {\pgfcoordinate{zigzag-cycle-start}{\pgfqpoint{.25\pgfdecorationsegmentlength}{\pgfdecorationsegmentamplitude}}
        \pgfpathmoveto{\pgfqpoint{.25\pgfdecorationsegmentlength}{\pgfdecorationsegmentamplitude}}
    }
  \state{big down}[switch if less than=+.5\pgfdecorationsegmentlength to center finish,
                   width=+.5\pgfdecorationsegmentlength,
                   next state=big up]
  {
    \pgfpathlineto{\pgfqpoint{.5\pgfdecorationsegmentlength}{-\pgfdecorationsegmentamplitude}}
  }
  \state{big up}[switch if less than=+.5\pgfdecorationsegmentlength to center finish,
                 width=+.5\pgfdecorationsegmentlength,
                 next state=big down]
  {
    \pgfpathlineto{\pgfqpoint{.5\pgfdecorationsegmentlength}{\pgfdecorationsegmentamplitude}}
  }
  \state{center finish}[width=0pt, next state=final]{
  }
  \state{final}
  {
    \pgfpathlineto{\pgfpointanchor{zigzag-cycle-start}{center}}
  }
}

\pgfdeclaredecoration{tobias zigzag cycle}{initial}{
\state{initial}[
        width=+0pt,
        next state=big down,
        persistent precomputation={
            \pgfmathsetmacro{\myint}{int(\pgfdecoratedinputsegmentlength/\pgfdecorationsegmentlength)}
            \ifodd\myint
             \pgfmathsetmacro\matchinglength{
             \pgfdecoratedinputsegmentlength / int(1+\pgfdecoratedinputsegmentlength/\pgfdecorationsegmentlength)}
            \else
             \pgfmathsetmacro\matchinglength{
             \pgfdecoratedinputsegmentlength / int(\pgfdecoratedinputsegmentlength/\pgfdecorationsegmentlength)}
            \fi
            \setlength{\pgfdecorationsegmentlength}{\matchinglength pt}
            \pgfmathsetmacro{\myint}{int(\pgfdecoratedinputsegmentlength/\pgfdecorationsegmentlength)}
        }] {
        \pgfcoordinate{zigzag-cycle-start}{\pgfqpoint{0pt}{-\pgfdecorationsegmentamplitude}}
        \pgfpathmoveto{\pgfqpoint{0pt}{-\pgfdecorationsegmentamplitude}}
        }
  \state{big down}[switch if less than=+.5\pgfdecorationsegmentlength to center finish,
                   width=+.5\pgfdecorationsegmentlength,
                   next state=big up]
  {
    \pgfpathlineto{\pgfqpoint{0pt}{-\pgfdecorationsegmentamplitude}}
  }
  \state{big up}[switch if less than=+.5\pgfdecorationsegmentlength to center finish,
                 width=+.5\pgfdecorationsegmentlength,
                 next state=big down]
  {
    \pgfpathlineto{\pgfqpoint{0pt}{\pgfdecorationsegmentamplitude}}
  }
  \state{center finish}[width=0pt, next state=final]{
   % this state is unecessary at the moment
  }
  \state{final}
  {
    \pgfpathlineto{\pgfpointanchor{zigzag-cycle-start}{center}}
  }
}

\begin{document}
\begin{standaloneframe}
  \begin{tikzpicture}[main style/.style={
        ellipse,draw,fill=blue!30,decorate,
        decoration={zigzag,segment length=1.1mm,amplitude=.5mm}
      },
      complete main style/.style={
        ellipse,draw,fill=blue!30,decorate,
        decoration={complete zigzag,segment length=1.1mm,amplitude=.5mm}
      },
      cyclic main style/.style={
        ellipse,draw,fill=blue!30,decorate,
        decoration={zigzag cycle,segment length=1.1mm,amplitude=.5mm}
      },
      cyclic tobias style/.style={
        ellipse,draw,fill=blue!30,decorate,
        decoration={tobias zigzag cycle,segment length=1.1mm,amplitude=.5mm}
      }]

    \node[main style] at (0,0) {ABC};
    \node[main style] at (5,0) {normal zigzag};

    \node[complete main style] at (0,-1) {ABC};
    \node[complete main style] at (5,-1) {complete zigzag};

    \node[cyclic main style] at (0,-2) {ABC};
    \node[cyclic main style] at (5,-2) {zigzag cycle};

    \node[cyclic tobias style] at (0,-3) {ABC};
    \node[cyclic tobias style] at (5,-3) {tobias zigzag};
  \end{tikzpicture}

  \begin{tikzpicture}[font=\sffamily]
   \draw (0,0) arc(135:45:{2*sqrt(2)}) coordinate[midway](X);
   \draw[blue] ([xshift=-2cm]X) -- ++(4,0) node[right]{tangent};
   \draw[red] (X) -- ++ (0.6,1) node[right]{standard}-- ++ (0.6,-1);
   \draw[green!60!black] ([xshift=-0.6cm]X) -- ++ (0.6,1) node[left]{tobias} -- ++ (0.6,-1);
  \end{tikzpicture}
\end{standaloneframe}
\end{document}

enter image description here

  • You're amazing, thank you so much for all your help. This code is also inspiring, and I'll try to dig into the details at some point. The only sad thing is that the angles on "sharp" curves (like here on the right and left side) have strange angles, and are not really regular. But I guess that it would require much work to fix this because you need to know "in advance" where the point will be, and I may try to do it myself as an exercice later, but I just want to make sure, if there is any fundamental reason that would make this problem unfeasible: – tobiasBora Aug 25 '18 at 8:00
  • especially, is it possible to get the tangeant/normal of a path at a specific point? – tobiasBora Aug 25 '18 at 8:01
  • 1
    @tobiasBora The decorations are automatically in tangent space. That is, at a given point the stretch between \pgfpoint{0pt}{0pt} and \pgfpoint{1pt}{0pt} is automatically along the tangent and the stretch between \pgfpoint{0pt}{0pt} and \pgfpoint{0pt}{1pt} is normal. The issue, however, is that each state has a finite size. While these statements are true at the left end of of the state, they are no longer true at the other points. Therefore the angles are "weird". You may fix this using a large number of \pgfnodes, but that's rather complicated. – user121799 Aug 25 '18 at 10:24
  • 1
    @tobiasBora I added a new version that does not suffer from the "shifting in tangent space" problem. – user121799 Aug 26 '18 at 16:49
  • you are amazing, thank you very much, the result looks perfect ;-) And thanks a lot for the drawing explanation ! – tobiasBora Aug 31 '18 at 8:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.