21

I defined a new shape that draws a flower with predefined filling and coloring:

\makeatletter
\pgfdeclareshape{flower}{
  \savedanchor{\center}{%
    \pgfpointorigin}
  \anchor{center}{\center}
\backgroundpath{%  
\begingroup
  \tikz@mode
    \iftikz@mode@draw
      \foreach \a in {60,120,...,360} {
        \pgftransformrotate{\a}
        \pgfpathellipse{\pgfpointxy{0}{0.7}}{\pgfpointxy{0.25}{0}}{\pgfpointxy{0}{0.5}}
      }
      \pgfpathcircle{\pgfpoint{0}{0}}{0.4cm}

      \let\my@color\tikz@strokecolor!60!\tikz@fillcolor
      \foreach \a in {30,60,...,360} {%
        \pgftransformrotate{\a+7}%
        \pgfpathmoveto{\pgfpointxy{0}{0.1}}
        \def\y{0.6+0.05*rand}
        \pgfpathlineto{\pgfpointxy{0}{\y}}
        \pgfpathcircle{\pgfpointxy{0}{\y}}{0.05cm}
      }
    \fi
    \iftikz@mode@fill
      \begin{scope}
        \foreach \a in {60,120,...,360} {
          \pgftransformrotate{\a}
          \pgfpathellipse{\pgfpointxy{0}{0.7}}{\pgfpointxy{0.25}{0}}{\pgfpointxy{0}{0.5}}
        }
        \pgfusepath{clip}
        \pgfpathcircle{\pgfpointxy{0}{0}}{1.2cm}
        \pgfutil@colorlet{tikz@radial@outer}{\tikz@fillcolor!70}
        \pgfutil@colorlet{tikz@radial@inner}{\tikz@fillcolor!60!\tikz@strokecolor}
        \pgfshadepath{radial}{0}
        \pgfusepath{}
      \end{scope}
      % \tikz@mode@clipfalse

      \let\my@strokecolor\tikz@strokecolor
      \let\my@fillcolor\tikz@fillcolor

      \pgfsetfillcolor{\my@strokecolor}
      \pgfpathcircle{\pgfpointxy{0}{0}}{0.4cm}
      \pgfusepath{fill}          

      \foreach \a in {30,60,...,360} {%
        \pgftransformrotate{\a+7}%
        \pgfpathmoveto{\pgfpointxy{0}{0.1}}
        \def\y{0.5+0.05*rand}
        \pgfpathlineto{\pgfpointxy{0}{\y}}
        \pgfpathcircle{\pgfpointxy{0}{\y}}{0.05cm}
      }
      \let\my@color\my@fillcolor
      \pgfutil@colorlet{\my@color}{\my@strokecolor!60!\my@fillcolor}
      \pgfsetstrokecolor{\my@color}
      \pgfsetfillcolor{\my@color}
      \pgfusepath{stroke,fill}
    \fi
   \endgroup
 }
}
\makeatother

which results in a nicely filled and colored flower when used as:

\node[flower, fill=pink, draw=purple] {};

Flower

Still, I have several problems and questions, for which I am not able to find an answer myself.

1) if I do not use \begin{scope} and \end{scope}, I do not know how to stop clipping which I do in the filling section. Is there a way to avoid using scopes? Or it is completely fine to have them there?

2) In the drawing of stamens, their height can be slightly changed using random function

    \def\y{0.6+0.05*rand}
    \pgfpathlineto{\pgfpointxy{0}{\y}}
    \pgfpathcircle{\pgfpointxy{0}{\y}}{0.05cm}

but each time the value of \y is recalculated and I do not know how to fix it here.

3) If I do not specify the colors of stroke and fill

fill=pink, draw=purple

I get compiling errors. I even get errors if I use

\node[flower, fill, color=red] {};

In that case, how can I use the default colors?

4) I would like to define a new decoration that would decorate a path with flowers of random size, moreover I expect these flowers to be exactly the same as on the picture above. Here is the code of my decoration:

\pgfdeclaredecoration{flowers}{initial}{
  \state{initial}[width=1cm]
{
\pgfmathparse{round(rnd*90)}
\pgftransformrotate{\pgfmathresult}
\pgfmathparse{0.2+rand*0.1}
\pgftransformscale{\pgfmathresult}
\pgfsetlinewidth{\pgfmathresult*\pgflinewidth}
\pgfsetcolor{pink}
\pgfsetfillcolor{pink}
\pgfsetstrokecolor{purple}
\pgfnode{flower}{center}{}{}{\pgfusepath{stroke,fill}}
}
\state{final}
{
  \pgfpathmoveto{\pgfpointdecoratedpathlast}
}
}

My first problem is that I get "plain" flowers by calling

\draw[decorate, decoration={flowers}] (-4,-3) -- (4,-3);

Line decorated with flowers

instead of a nice filling/coloring. What should I modify in the definition of flower?

Then, I would prefer to use random step between the flowers, how it can be done?

  • 1
    Very nice question! One immediate problem I can spot is that draw key should always be given with a color which might give some headache later. Also now you should be able to post images. – percusse Apr 22 '13 at 14:10
  • Hi everybody! I inserted the images now. @percusse, cannot I check if the stroke and fill colors are defined, and if not set their values myself? – Elena Apr 22 '13 at 15:01
  • A few notes: The PGF manual notes to \beckgroundpath: If you want to specify that something should “always” happen when this shape is drawn (for example, if the shape is a stop-sign, we always want it to be filled with a red color), you can use commands like \beforebackgroundpath, explained below. — Could you describe what you intended with your code? If draw and fill is both active you actually over-fill all that stuff that is drawn previously. – Qrrbrbirlbel Apr 22 '13 at 15:08
  • 1
    Also: \let\my@color\tikz@strokecolor!60!\tikz@fillcolor won’t work (but you don’t even use it their). Use \edef\pgf@tempa{\tikz@strokecolor!60!\tikz@fillcolor} \let\my@color\pgf@tempa instead. Later you do \let\my@color\my@fillcolor, even though you overwrite \my@color in the very next line. — You can check with \ifx\tikz@fillcolor\pgfutil@empty whether a color is set. – Qrrbrbirlbel Apr 22 '13 at 15:15
  • @Qrrbrbirlbel Yes, you are right, \let\my@color\tikz@strokecolor!60!\tikz@fillcolor can be simply removed. As for the second time \let\my@color occurs, it was not working without it. This is really useful how I can check if a color is set, thanks. With my code I want to do the following: each time fill is active, I want the shape to be filled in a special way, exactly as it is in the first picture. Which actually works if I draw and fill a node of shape flower. It only does not work in the decoration. – Elena Apr 22 '13 at 15:25
12

This is probably still a little way off because there are a few places where I'm not sure what you're after.

  1. if I do not use \begin{scope} and \end{scope}, I do not know how to stop clipping which I do in the filling section. Is there a way to avoid using scopes? Or it is completely fine to have them there?

    You have to limit the clip somehow and scopes are the right way to do this. You can use the lower-level \pgfscope and \endpgfscope to be consistent with your use of the \pgf... commands.

  2. In the drawing of stamens, their height can be slightly changed using random function

    \def\y{0.6+0.05*rand}
    \pgfpathlineto{\pgfpointxy{0}{\y}}
    \pgfpathcircle{\pgfpointxy{0}{\y}}{0.05cm}
    

    but each time the value of \y is recalculated and I do not know how to fix it here.

    I see several uses of this code and I'm guessing that you want them all to be mutually consistent. There are two problems: one with the code above and one with the global reuse. With the code above then \y gets computed twice and each time it uses a different random number. So instead of \def\y{...} you want to make \y the result of that computation. For this, use \pgfmathsetmacro\y{...}. The second problem is addressed by resetting the random seed for \pgfmath at appropriate places. This is done using \pgfmathsetseed.

  3. If I do not specify the colors of stroke and fill

    fill=pink, draw=purple

    I get compiling errors. I even get errors if I use

    \node[flower, fill, color=red] {};

    In that case, how can I use the default colors?

    TikZ only sets \tikz@strokecolor and \tikz@fillcolor if the draw or fill options were given explicit colours. A general colour is saved as tikz@color (note that this is a colour name not a macro) so you need to use that. I recommend putting a "get the right colours" bit at the top of your code and then using your own names. As I said in my comment, I'm not completely clear as to which colours correspond to which actions (fill, stroke, fade) on the parts of the flower so my code (below) will probably get this wrong.

  4. I would like to define a new decoration that would decorate a path with flowers of random size, moreover I expect these flowers to be exactly the same as on the picture above. Here is the code of my decoration:

    My first problem is that I get "plain" flowers by calling instead of a nice filling/coloring. What should I modify in the definition of flower?

    Then, I would prefer to use random step between the flowers, how it can be done?

    The plain flowers comes from the fact that you are defining the colours in the decoration using low-level commands (\pgfsetstrokecolor) but extracting the colours from the TikZ level commands (\tikz@strokecolor). There isn't, as far as I can see, a way to extract the current stroke colour so we have to set them using the TikZ method. The same goes for the stroke and fill actions. As you're using \tikz@mode@... in your code you have to use the TikZ method of specifying draw and fill.

    The width is evaluated afresh each time so you can set it to a new dimen and then adjust this dimension on each run.

Here's my adaptation of your code implementing all the above. As I've said, I don't think I get quite what you're looking for in a few places but hopefully it is close enough that you can see where to go next.

\documentclass{article}
%\url{http://tex.stackexchange.com/q/110047/86}
\usepackage{tikz}
\usetikzlibrary{decorations.pathreplacing}

\makeatletter

\def\pgfmathgetseed#1{%
  \let#1=\pgfmath@rnd@z}

\pgfdeclareshape{flower}{
  \savedanchor{\center}{%
    \pgfpointorigin}
  \anchor{center}{\center}
\backgroundpath{%  
\begingroup
    \ifx\tikz@strokecolor\pgfutil@empty
    \def\my@strokecolor{tikz@color}
    \else
    \let\my@strokecolor\tikz@strokecolor
    \fi
    \ifx\tikz@fillcolor\pgfutil@empty
    \def\my@fillcolor{tikz@color}
    \else
    \let\my@fillcolor\tikz@fillcolor
    \fi
    \pgfmathgetseed{\flower@seed}
  \tikz@mode
    \iftikz@mode@draw
      \foreach \a in {60,120,...,360} {
        \pgftransformrotate{\a}
        \pgfpathellipse{\pgfpointxy{0}{0.7}}{\pgfpointxy{0.25}{0}}{\pgfpointxy{0}{0.5}}
      }
      \pgfpathcircle{\pgfpoint{0}{0}}{0.4cm}
    \pgfmathsetseed{\flower@seed}
      \foreach \a in {30,60,...,360} {%
        \pgftransformrotate{\a+7}%
        \pgfpathmoveto{\pgfpointxy{0}{0.1}}
        \pgfmathsetmacro\y{0.6+0.05*rand}
        \pgfpathlineto{\pgfpointxy{0}{\y}}
        \pgfpathcircle{\pgfpointxy{0}{\y}}{0.05cm}
      }
    \pgfsetstrokecolor{\my@strokecolor}
      \pgfusepath{stroke}
    \fi
    \iftikz@mode@fill
    \pgfscope
        \foreach \a in {60,120,...,360} {
          \pgftransformrotate{\a}
          \pgfpathellipse{\pgfpointxy{0}{0.7}}{\pgfpointxy{0.25}{0}}{\pgfpointxy{0}{0.5}}
        }
        \pgfusepath{clip}
        \pgfpathcircle{\pgfpointxy{0}{0}}{1.2cm}
        \pgfutil@colorlet{tikz@radial@outer}{\my@fillcolor!70}
        \pgfutil@colorlet{tikz@radial@inner}{\my@fillcolor!60!\my@strokecolor}
        \pgfshadepath{radial}{0}
        \pgfusepath{discard}
      \endpgfscope
      % \tikz@mode@clipfalse

      \pgfsetfillcolor{\my@strokecolor}
      \pgfpathcircle{\pgfpointxy{0}{0}}{0.4cm}
      \pgfusepath{fill}          

    \pgfmathsetseed{\flower@seed}
      \foreach \a in {30,60,...,360} {%
        \pgftransformrotate{\a+7}%
        \pgfpathmoveto{\pgfpointxy{0}{0.1}}
        \pgfmathsetmacro\y{0.6+0.05*rand}
        \pgfpathlineto{\pgfpointxy{0}{\y}}
        \pgfpathcircle{\pgfpointxy{0}{\y}}{0.05cm}
      }
      \let\my@color\my@fillcolor
      \pgfutil@colorlet{\my@color}{\my@strokecolor!60!\my@fillcolor}
      \pgfsetstrokecolor{\my@color}
      \pgfsetfillcolor{\my@color}
      \pgfusepath{stroke,fill}
    \fi
   \endgroup
 }
}

\newdimen\flower@sep
\flower@sep=1cm\relax

\pgfdeclaredecoration{flowers}{initial}{
  \state{initial}[width=\flower@sep]
{
\pgfmathparse{round(rnd*90)}
\pgftransformrotate{\pgfmathresult}
\pgfmathparse{0.2+rand*0.1}
\pgftransformscale{\pgfmathresult}
\pgfsetlinewidth{\pgfmathresult*\pgflinewidth}
\def\tikz@strokecolor{purple}
\def\tikz@fillcolor{pink}
\tikz@mode@drawtrue
\tikz@mode@filltrue
\pgfnode{flower}{center}{}{}{}
\pgfmathsetlength\flower@sep{1cm + rand*0.5cm}
\global\flower@sep=\flower@sep
}
\state{final}
{
  \pgfpathmoveto{\pgfpointdecoratedpathlast}
}
}


\makeatother

\begin{document}

\tikz \node[flower, fill=pink, draw=purple] {};

\tikz \node[flower, fill, color=red] {};


\tikz \draw[decorate, decoration={flowers}] (-4,-3) -- (4,-3);

\end{document}

Flowers


Update I've gone through the code a little and removed some of the repetition and tidied up a few things. Comments in code.

\documentclass{article}
%\url{http://tex.stackexchange.com/q/110047/86}
\usepackage{tikz}
\usetikzlibrary{decorations.pathreplacing}

\makeatletter

\tikzset{
  flower shade colour/.store in=\my@shadecolour,
  flower shade color/.store in=\my@shadecolour,
  flower shade colour=white
}

\pgfdeclareshape{flower}{
  \savedanchor{\center}{%
    \pgfpointorigin}
  \anchor{center}{\center}
%
% We use \beforebackgroundpath since we're handling the stroke and
% fills ourselves.
%
\beforebackgroundpath{%
\begingroup
%
% Do our very best to set the colours.
%
% Was a stroke colour set explicitly (via draw=<colour>)?
    \ifx\tikz@strokecolor\pgfutil@empty
% No, so test if a general colour was set.
    \@ifundefinedcolor{tikz@color}{%
% No, so take the current colour.
    \def\my@strokecolor{.}%
      }{%
% General colour was set so use that
    \def\my@strokecolor{tikz@color}%
      }%
    \else
% Stroke colour was set, so use that
    \let\my@strokecolor\tikz@strokecolor
    \fi
%
% Now the same for the fill colour.
% Was a fill colour set explicitly?
    \ifx\tikz@fillcolor\pgfutil@empty
% No, so was a general colour set?
    \@ifundefinedcolor{tikz@color}{%
% No, so reuse the stroke colour.
    \let\my@fillcolor\my@strokecolor
      }{%
% General colour was set, use that.
    \def\my@fillcolor{tikz@color}%
      }%
    \else
% Specific fill colour was set, so use that.
    \let\my@fillcolor\tikz@fillcolor
    \fi

%
% Test for the mode
%
    \tikz@mode
%
% Draw the petals.
%
    \foreach \a in {60,120,...,360} {
          \pgftransformrotate{\a}
      \pgfpathellipse{\pgfpointxy{0}{0.7}}{\pgfpointxy{0.25}{0}}{\pgfpointxy{0}{0.5}}
        }
%
% If fill is requested, shade them using the fill colour and the
% (extra) shade colour.
%
    \iftikz@mode@fill
        \pgfutil@colorlet{tikz@radial@outer}{\my@fillcolor}
        \pgfutil@colorlet{tikz@radial@inner}{\my@shadecolour}
        \pgfshadepath{radial}{0}
        \pgfusepath{discard}
    \else
    \iftikz@mode@draw
%
% If fill mode was not requested but draw mode was, stroke the path.
%
    \pgfusepath{stroke}
    \fi
    \fi

%
% Now the centre of the flower; filled if requested, if not filled
% then drawn if requested.
%
    \pgfpathcircle{\pgfpointxy{0}{0}}{0.4cm}
    \iftikz@mode@fill
      \pgfsetfillcolor{\my@strokecolor}
      \pgfusepath{fill}          
    \else
    \iftikz@mode@draw
      \pgfsetstrokecolor{\my@strokecolor}
    \pgfusepath{stroke}
    \fi
    \fi

%
% Lastly the stamen.
% If fill was requested then we also stroke the paths (to get the
% stalk part - biology was never my strong point).
% Note that we push the circles out by their radius so that if
% drawn then it still looks right.
%
      \foreach \a in {30,60,...,360} {%
        \pgftransformrotate{\a+7}%
        \pgfpathmoveto{\pgfpointxy{0}{0.1}}
        \pgfmathsetmacro\y{0.6+0.05*rand}
        \pgfpathlineto{\pgfpointxy{0}{\y}}
        \pgfpathcircle{\pgfpointxy{0}{\y + 0.05}}{0.05cm}
      }
      \let\my@color\my@fillcolor
      \pgfutil@colorlet{\my@color}{\my@strokecolor!60!\my@fillcolor}
      \pgfsetstrokecolor{\my@color}
    \iftikz@mode@fill
      \pgfsetfillcolor{\my@color}
      \pgfusepath{stroke,fill}
    \else
    \iftikz@mode@draw
    \pgfusepath{stroke}
    \fi
    \fi

   \endgroup
 }
}

%
% This holds our state widths for the decoration so that we
% can vary them each time.
%
\newdimen\flower@sep
\flower@sep=1cm\relax

\pgfdeclaredecoration{flowers}{initial}{
  \state{initial}[width=\flower@sep]
{
\pgfmathparse{round(rnd*90)}
\pgftransformrotate{\pgfmathresult}
\pgfmathparse{0.2+rand*0.1}
\pgftransformscale{\pgfmathresult}
\pgfsetlinewidth{\pgfmathresult*\pgflinewidth}
%
% These should be customisable, I guess.
%
\def\tikz@strokecolor{purple}
\def\tikz@fillcolor{pink}
\tikz@mode@drawtrue
\tikz@mode@filltrue
\pgfnode{flower}{center}{}{}{}
%
% Reset the width
%
\pgfmathsetlength\flower@sep{1cm + rand*0.5cm}
\global\flower@sep=\flower@sep
}
\state{final}
{
  \pgfpathmoveto{\pgfpointdecoratedpathlast}
}
}


\makeatother

\begin{document}

\tikz \node[flower, fill=pink, draw=purple] {};

\tikz \node[flower, fill, color=red] {};

\tikz \node[flower, draw=red] {};


\tikz \draw[decorate, decoration={flowers}] (-4,-3) -- (4,-3);

\end{document}

More flowers

| improve this answer | |
  • Thanks a lot, you provided me with all answers I needed! The way you suggested to handle the colors is exactly what I intended. Some minor comments: it turned out that I needed only to use \pgfmathsetmacro, without setting the seed, and in the section \tikz@mode@draw, \pgfusepath{stroke} is not really needed. As for your comment to question 4, in fact, my use of some commands is not very conscious: they are there from different pieces that I found in internet or in the manual. – Elena Apr 23 '13 at 8:19
  • @Elena Glad to have helped. I'm curious about the \pgfusepath{stroke} comment. Without that, I don't see what that first segment does. A path is constructed but not used, so it actually gets added to the clip path. You probably don't notice this most likely because the default clip method means that it is effectively ignored. Also, if you do use this path then you need to save the random seed so that the drawn and filled parts match up, if you don't then you're right that you don't need to save the seed. – Andrew Stacey Apr 23 '13 at 8:26
  • I see now what you mean. The thing is that I don't want to draw that path when the filling option is on, it should be drawn only when the filling option is off. I will then just use the seed for the case clipping might go wrong. One more question, what is the meaning of \makeatletter and \makeatother? – Elena Apr 23 '13 at 8:37
  • @Elena Ah, so it's an "either-or". Then you should put the draw part in an \else - if I get a minute later on I'll try mocking up some code that will do that. For \makeatletter and \makeatother I recommend reading tex.stackexchange.com/q/8351/86 – Andrew Stacey Apr 23 '13 at 10:51
  • @Elena See revision. – Andrew Stacey Apr 23 '13 at 12:34

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.