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.

I have this path, which I would like to style "piecewise" from a single specification, shown on the image below:

test06.png

As the image says, I'd like to decorate segments 2-3 and 5-6, and colorize only 3-4. I'd like to decorate 2-3 with a snake (sinusoidal decoration), which implies that the 1-2 segment going upwards should be drawn invisibly (somewhat like a moveto).

The problem, as visible on the image, is that I get absolutely no decorations on the third picture, which means I didn't get the right syntax for piecewise styling and decorations (if it is even possible).

My question is - how could I achieve the kind of decoration I want, but with on a "single line" (with a single coordinates' path specification)? The MWE so far is below:

% \documentclass{article}
\documentclass[varwidth,tightpage,border=1bp]{standalone}

\usepackage{tikz}
\usetikzlibrary{decorations}
\usetikzlibrary{decorations.pathmorphing} % (solid) snake :)
\usetikzlibrary{decorations.pathreplacing} % show path construction

\pagecolor{yellow!15} % ignored with preview, but not w/ varwidth

\begin{document}

This is the plain linear path:

\begin{tikzpicture}
\draw[]
  (0,0) -- ++(1,0) node[fill,scale=0.5](A){}
  -- +(0,1) -- +(0,-1) -- ++(1,0)
  -- ++(2,1) -- ++(0,-1) -- ++(1,0);
\end{tikzpicture}

This is the path construction:

\gdef\cnt{0}
\begin{tikzpicture}
\draw[decoration={show path construction,
  moveto code={
    \fill [red] (\tikzinputsegmentfirst) circle (2pt);
  },
  lineto code={
    \pgfmathtruncatemacro{\tmp}{\cnt+1} \xdef\cnt{\tmp} %
    \ifnum\cnt=2{\gdef\tw{3pt}}\else{\gdef\tw{1pt}}\fi %
    \ifnum\cnt=3{\gdef\tc{blue!50}}\else{\gdef\tc{blue}}\fi %
    \draw [draw=\tc,line width=\tw,->] (\tikzinputsegmentfirst) -- (\tikzinputsegmentlast) node[fill=none,draw=green,minimum size=2pt,font=\bf]{\tmp};
  },
  closepath code={
    %\draw [draw=none] (\tikzinputsegmentfirst) -- (\tikzinputsegmentlast);
  },
},
decorate]
  (0,0) -- ++(1,0) node[fill,scale=0.5](A){}
  -- +(0,1) -- +(0,-1) -- ++(1,0)
  -- ++(2,1) -- ++(0,-1) -- ++(1,0);
\end{tikzpicture}

Attempt at decorating only segments\\ 2-3 and 5-6, and coloring only 3-4

\begin{tikzpicture}
\draw[]
  (0,0) -- ++(1,0) node[fill,scale=0.5](A){}
  { [draw=none]-- +(0,1) }
  { [decorate,decoration={snake},draw=red]-- +(0,-1) } % 2-3
  { [draw=green]-- ++(1,0) }
  -- ++(2,1)
  { [decorate,decoration={snake},draw=red]-- ++(0,-1) } %5-6
  -- ++(1,0);
\end{tikzpicture}
\end{document}
share|improve this question
1  
You need a meta-decoration. But is it always the same segments with same decorations? –  percusse Jun 6 at 20:10
    
Thanks for the comment, @percusse - indeed, I forgot to specify: it is not always the same segments, they can change arbitrarily - I may want, in principle, say, nth segment, then n+2nd and n+3rd segment, then finally n+10th segment... Cheers! –  sdaau Jun 7 at 8:24

1 Answer 1

Right, I got to a sort of a partial solution, so I'll post it here... This is the output I get with the MWE posted below:

test02b

The first thing to remember is, that if you think that braces {} have any sort of grouping within a \draw path, they do not; you can try [draw=green]{-- ++(1,0)} or { [draw=green]-- ++(1,0)} - but either the color will be ignored, or it will be applied to the path globally.

Second thing is that there is a sort of a way around this with decorations, but not with meta-decorations. The meta, as the regular ones, are basically intended to repeat patterns along a line (in other words, they repeat states); here I want to apply individual styles to specific (i.e. random) line segments; so the full facility like this of decorations is not applicable to this problem.

What is applicable, though, is that there is a decoration=moveto, which actually can be used, as is, to "hide" an arbitrary line segment. The problem is, the syntax directly contradicts what I have in my version of the Tikz & PF manual (Manual for Version 2.10):

Indeed, the following two commands have the same effect:
1. \path decorate[ options ] { path };
2. \path [decorate, options ] path ;

Well, at least in my version, that is - luckily - incorrect; if you write:

  • decorate[decoration=moveto]{ -- +(0,1) } - then the decoration will be applied only to the line segment inside the braces, which I want
  • [decorate,decoration=moveto]{ -- +(0,1) } - then either the decoration will be ignored, or will be applied globally

So, at least for hiding, and decorations like snake with no other demands, this works. But can we apply individual styling? For the most part, entering color or line width options inside the decoration [] options will be ignored (or applied globally).

So, in the code below, there is a custom decoration called example. It basically jumps over its {initial} state, and it goes in the {final} directly. There, first it outputs the path built up to that point using \pgfusepath - however, one should take note that \pgfusepath kills the global path up to that point, so you have to at least manually moveto a point, to allow the further path building constructs to work. Then the "current" stroke color is saved, and the new one is read from options set previously (see Reading - and effect - of (default) tikz/pgf keys? to see for pitfalls in understanding how global TikZ option keys may work).

And then set; then the line segment is finished, then \pgfusepath is called again so the path is output styled - but this kills the global path again, so again we have to at least move. Then, since we also want to reset back to original color there, we re-set back to the default settings set previously - but again we have to call \pgfusepath to set the new color ( :) ), and since this destroys the global path, we have to moveto at least once after that. Whew! :D

And that finally allows for individual styling of line segments - but it's not perfect; if you specify e.g. \draw[line width=1pt], you'll see it's only applied to the final segments (obviously, because of all of the destruction introduced by \pgfusepath).

Here is the code producing the image above:

\documentclass[varwidth,tightpage,border=1bp]{standalone}

\usepackage{tikz}
\usetikzlibrary{scopes} %% nope
\usetikzlibrary{decorations}
\usetikzlibrary{decorations.pathmorphing} % (solid) snake :)
\usetikzlibrary{decorations.pathreplacing} % show path construction

\pagecolor{yellow!15} % ignored with preview, but not w/ varwidth


\makeatletter
\global\let\tmpmc\relax \global\let\tmpcc\relax
\pgfdeclaredecoration{example}{initial}
{
  % {initial} is done for as long
  % as \pgfdecoratedremainingdistance < width;
  % but for 0pt it loops indefinitely!
  % > "First, this option causes an immediate switch
  % > to the state final if the remaining distance
  % > on the input path is less than dimension."
  % When \pgfdecoratedremainingdistance+1pt, indeed
  % it jumps immediately, nothing inside executes;
  \state{initial}[width=\pgfdecoratedremainingdistance+1pt]
  {
    % this will never print
    \typeout{decoration{example} INITIAL} %
  }
  \state{final}
  {
    % example for printing coordinates of point:
    % (note, here \pgfpointdecoratedinputsegmentlast
    %  is same as \pgfpointdecoratedpathlast)
    \pgfpointdecoratedpathlast    % first go here, at this point
    \pgfgetlastxy{\dcplx}{\dcply} % grab coords in macros
    \typeout{dcpl \dcplx, \dcply} % print coords to stdout
    %
    % NOTE:
    % > "\pgfusepath{ actions } Applies the given
    % > actions  to the current path. Afterwards,
    % > the current path is (globally) empty."
    % print all the path, up to now, with default (black) color
    \pgfusepath{draw,stroke} %
    %
    % remember the current (old), set the new color
    \makeatletter %
    \typeout{\string\color@pgf@tempcolor,           % \color@pgf@tempcolor,
      \csname\string\color@pgf@tempcolor\endcsname} % 0 g 0 G
    \extractcolorspec{pgf@tempcolor}{\tmpc} %
    \extractcolorspecs{pgf@tempcolor}{\tmpmc}{\tmpcc} %
    \typeout{tmpc \tmpc}                  % tmpc {gray}{0}
    \typeout{tmpmc \tmpmc, tmpcc \tmpcc}  % tmpmc gray, tmpcc 0
    \makeatother %
    % get the new color from previously set key:
    \pgfkeysgetvalue{/tikz/draw}{\tmpdraw} %
    \typeout{tmpI \tmpdraw} % tmpI green
    % finally, set new color as stroke color
    % (green!50 works here; note black!0 is transparent!)
    \pgfsetstrokecolor{\tmpdraw} %
    %
    % since now after \pgfusepath, the global path is dead/cleaned;
    % have to reinitialize the global path first by a moveto
    % (else the lineto after, doesn't know what to lineto from)
    % happily, here the coord system is local to the segment,
    % so 0.0pt refers to the first point of the segment
    \pgfpathmoveto{\pgfpoint{0pt}{0pt}}
    \pgfpathlineto{\pgfpointdecoratedpathlast} %
    %
    % must usepath again, to output stroked
    \pgfusepath{draw,stroke} %
    %
    % now global path is dead again - so move to the
    % end point; but we'll have to reset the color...
    %\color[gray]{0.5}  % this resets the stroke color to default,
                        % but does not set it! ok syntax, complains
                        % if range not [0,1]
    %\colorlet{tmpold}[gray]{0.5} % no can do
    %\definecolor{tmpold}{gray}{0.5} % ok
    \definecolor{tmpold}{\tmpmc}{\tmpcc} %
    \pgfsetstrokecolor{tmpold} %
    \pgfpathmoveto{\pgfpointdecoratedpathlast}
    % moveto is enough here, no need for \pgfpathlineto
    % must usepath again, to output stroked
    % (print all up to now with black)
    \pgfusepath{draw,stroke} %
    %
    % now global path is dead again - move to the end
    \pgfpathmoveto{\pgfpointdecoratedpathlast}
    % now further subpaths can continue to
    % append to the last point here...
  }
}
\makeatother

\begin{document}

This is the plain linear path:

\begin{tikzpicture}
\draw[]
  (0,0) -- ++(1,0) node[fill,scale=0.5](A){}
  -- +(0,1) -- +(0,-1) -- ++(1,0)
  -- ++(2,1) -- ++(0,-1) -- ++(1,0);
    \pgfkeysgetvalue{/tikz/color}{\tmp}\typeout{tmpX \tmp \meaning\tmp}
\end{tikzpicture}

This is the path construction:

\gdef\cnt{0}
\begin{tikzpicture}
\draw[decoration={show path construction,
  moveto code={
    \fill [red] (\tikzinputsegmentfirst) circle (2pt);
  },
  lineto code={
    \pgfmathtruncatemacro{\tmp}{\cnt+1} \xdef\cnt{\tmp} %
    \ifnum\cnt=2{\gdef\tw{3pt}}\else{\gdef\tw{1pt}}\fi %
    \ifnum\cnt=3{\gdef\tc{blue!50}}\else{\gdef\tc{blue}}\fi %
    \draw [draw=\tc,line width=\tw,->] (\tikzinputsegmentfirst) -- (\tikzinputsegmentlast) node[fill=none,draw=green,minimum size=2pt,font=\bf]{\tmp};
  },
  closepath code={
    %\draw [draw=none] (\tikzinputsegmentfirst) -- (\tikzinputsegmentlast);
  },
},
decorate]
  (0,0) -- ++(1,0) node[fill,scale=0.5](A){}
  -- +(0,1) -- +(0,-1) -- ++(1,0)
  -- ++(2,1) -- ++(0,-1) -- ++(1,0);
\end{tikzpicture}

Attempt at decorating only segments\\ 2-3 and 5-6, and coloring only 3-4

% \edef\dcA{decorate[decoration={lineto,},green]} % color no effect here
\def\dcA{decorate[decoration={example,/tikz/draw/.initial=green,},every path/.append style={line width=1pt}]} % color ok, line width no effect here

\begin{tikzpicture}
\draw[line width=1pt]
  % start here:
        (0,0)
  % (default) line to here:
                                    -- ++(1,0)
  % place node:
        node[fill,scale=0.5](A){}
  % line, but hidden (moveto):
       decorate[decoration=moveto]{ --  +(0,1) }
  % only this segment, decorate snake:
     decorate[decoration={snake,}]{ --  +(0,-1) } % 2-3
  % only this segment, decorate example (green):
                              \dcA{ -- ++(1,0) }
  % (default) line to here:
                                    -- ++(2,1)
  % only this segment, decorate snake:
  % [decorate,decoration={snake},red] out here
  %  would be applied globally! (but only from
  %  the end of example decoration)
  % with decorate[decoration={snake},red],
  %  the red is ignored.
  decorate[decoration={snake},red]{ -- ++(0,-1) } %5-6
  % (default) line to here:
                                    -- ++(1,0)
  ;
\end{tikzpicture}
\end{document}
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.