The source of the difficulty is that ellipses are constructed in a particular way in TikZ. They are paths that start from the x-axis and proceed counter-clockwise around their centre. The vast majority of the time, the exact parametrisation doesn't matter. You appear to have found the one situation where it does!
In the actual question, you only want to be able to mirror the ellipse, and so draw it starting from the negative x-axis (the title of the question suggests a more flexible approach). That's actually not too hard since we can exploit the symmetry of the ellipse. The key is to provide it with a negative x-radius, since then it will start from the negative x-axis (and proceed clockwise, but we could correct for that by negating the y-radius as well). To do this, we interrupt the call from the node shape to the drawing command and flip the sign of the x-radius. The simplest way to do this is to redefine the \pgfpathellipse
macro to do the negation and then call the original macro. The following code does this.
\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{decorations,shapes,decorations.markings}
\makeatletter
\let\origpgfpathellipse=\pgfpathellipse
\def\revpgfpathellipse#1#2#3{%
#2%
\pgf@xa=-\pgf@x
\origpgfpathellipse{#1}{\pgfqpoint{\pgf@xa}{0pt}}{#3}}
\makeatother
\tikzset{
reversed ellipse/.style={
ellipse,
reverse the ellipse%
},
reverse the ellipse/.code={
\let\pgfpathellipse=\revpgfpathellipse
}
}
\begin{document}
\begin{tikzpicture}
\node[ellipse,
draw,
postaction={
decorate,
decoration={
markings,
mark=at position 1 with {
\arrow[line width=5pt,blue]{>}
}
}
}
] at (0,0) {hello world};
\node[reversed ellipse,
draw,
postaction={
decorate,
decoration={
markings,
mark=at position 1 with {
\arrow[line width=5pt,blue]{>}
}
}
}
] at (0,-2) {hello world};
\end{tikzpicture}
\end{document}
Here's the result:
(the arrow got clipped, but you can see where it lies)
TikZ already includes the possibility to insert a separate path in the current one: the edge
.
(Unfortunately, you cannot use pgfonlayer
here. But as the argument append after command
will be executed after the node has been placed, this shouldn’t be an issue here.)
Since the CVS version swapped the arguments to the atan2
function (atan2(x, y)
to atan2(y, x)
), I also included a small block in the preamble to sort this out and define the functions atanXY
and atanYX
.
I also chose to not change the outer sep
s but instead to subtract \pgflinewidth
directly from the radius. It is an annoyance that these values cannot be accessed after the node.
Code
\documentclass[tikz]{standalone}
\usetikzlibrary{calc,shapes.geometric}
\pgfmathparse{atan2(0,1)}
\ifdim\pgfmathresult pt=0pt % atan2(y, x)
\tikzset{declare function={atanXY(\x,\y)=atan2(\y,\x);atanYX(\y,\x)=atan2(\y,\x);}}
\else % atan2(x, y)
\tikzset{declare function={atanXY(\x,\y)=atan2(\x,\y);atanYX(\y,\x)=atan2(\x,\y);}}
\fi
\begin{document}
\begin{tikzpicture}[font=\sffamily\small,
mycylinder/.style={draw, shape=cylinder, aspect=1.5, minimum height=+3cm,
minimum width=+2cm, left color=blue!30, right color=blue!60, middle color=blue!10,
shape border rotate=90, append after command={%
let \p{cyl@center} = ($(\tikzlastnode.before top)!0.5! (\tikzlastnode.after top)$),
\p{cyl@x} = ($(\tikzlastnode.before top)-(\p{cyl@center})$),
\p{cyl@y} = ($(\tikzlastnode.top) -(\p{cyl@center})$)
in (\p{cyl@center}) edge[draw=none, fill=blue!10, to path={
ellipse [x radius=veclen(\p{cyl@x})-1\pgflinewidth,
y radius=veclen(\p{cyl@y})-1\pgflinewidth,
rotate=atanXY(\p{cyl@x})]}] () }}]
\node[mycylinder, label=below:Better?] {};
\end{tikzpicture}
\end{document}
Output
Another idea.
The cylinder
shape already has the option to fill the two parts differently with the options
cylinder body fill=<color>
,
cylinder end fill=<color>
and the switch
cylinder uses custom fill
.
Unfortunately, the shading in
cylinder end fill=blue!10, cylinder uses custom fill,
preaction={draw=red, left color=blue!30, right color=blue!60, middle color=blue!10}
will be drawn on top of the cylinder end fill
(which is done in a behindbackgroundpath
) even though the shading itself is in a preaction
.
It may be possible to solve this with a custom shape.
The keys Cylinder end shade
and Cylinder body shade
actually are implemented by setting their content with \tikzset
and using \tikz@finish
(similar how the backgroundpath
and the foregroundpath
are applied).
Code
\documentclass[tikz]{standalone}
\usetikzlibrary{shapes.geometric}
\pgfset{
Cylinder end fill/.initial=,
Cylinder body fill/.initial=,
Cylinder end shade/.initial=,
Cylinder body shade/.initial=}
\makeatletter
\pgfdeclareshape{Cylinder}{%
\inheritsavedanchors[from=cylinder]%
\inheritbackgroundpath[from=cylinder]%
\inheritanchorborder[from=cylinder]%
\inheritanchor[from=cylinder]{center}\inheritanchor[from=cylinder]{shape center}%
\inheritanchor[from=cylinder]{mid}\inheritanchor[from=cylinder]{mid east}%
\inheritanchor[from=cylinder]{mid west}\inheritanchor[from=cylinder]{base}%
\inheritanchor[from=cylinder]{base east}\inheritanchor[from=cylinder]{base west}%
\inheritanchor[from=cylinder]{north}\inheritanchor[from=cylinder]{south}%
\inheritanchor[from=cylinder]{east}\inheritanchor[from=cylinder]{west}%
\inheritanchor[from=cylinder]{north east}\inheritanchor[from=cylinder]{south west}%
\inheritanchor[from=cylinder]{south east}\inheritanchor[from=cylinder]{north west}%
\inheritanchor[from=cylinder]{before top}\inheritanchor[from=cylinder]{top}%
\inheritanchor[from=cylinder]{after top}\inheritanchor[from=cylinder]{before bottom}%
\inheritanchor[from=cylinder]{bottom}\inheritanchor[from=cylinder]{after bottom}%
\behindbackgroundpath{%
\ifpgfcylinderusescustomfill%
\getcylinderpoints%
\pgf@x\xradius\relax%
\advance\pgf@x-\outersep\relax%
\edef\xradius{\the\pgf@x}%
\pgf@y\yradius\relax%
\advance\pgf@y-\outersep\relax%
\edef\yradius{\the\pgf@y}%
{%
\pgftransformshift{\centerpoint}%
\pgftransformrotate{\rotate}%
\pgfpathmoveto{\afterbottom}%
\pgfpatharc{90}{270}{\xradius and \yradius}%
\pgfpathlineto{\beforetop\pgf@y-\pgf@y}%
\pgfpatharc{270}{90}{\xradius and \yradius}%
\pgfpathclose%
\edef\pgf@temp{\pgfkeysvalueof{/pgf/Cylinder body fill}}%
\ifx\pgf@temp\pgfutil@empty
\edef\pgf@temp{\pgfkeysvalueof{/pgf/Cylinder body shade}}%
\ifx\pgf@temp\pgfutil@empty
\pgfusepath{discard}%
\else % make shading:
\begingroup
\expandafter\tikzset\expandafter{\pgf@temp}
\tikz@finish
\fi
\else
\pgfsetfillcolor{\pgf@temp}%
\pgfusepath{fill}%
\fi
%
\pgfpathmoveto{\beforetop}%
\pgfpatharc{90}{-270}{\xradius and \yradius}%
\pgfpathclose
\edef\pgf@temp{\pgfkeysvalueof{/pgf/Cylinder end fill}}%
\ifx\pgf@temp\pgfutil@empty
\edef\pgf@temp{\pgfkeysvalueof{/pgf/Cylinder end shade}}%
\ifx\pgf@temp\pgfutil@empty
\pgfusepath{discard}%
\else % make shading:
\begingroup
\expandafter\tikzset\expandafter{\pgf@temp}
\tikz@finish
\fi
\else
\pgfsetfillcolor{\pgf@temp}%
\pgfusepath{fill}%
\fi
}%
\fi
}%
}
\makeatother
\begin{document}
\begin{tikzpicture}[font=\sffamily\small, opacity=1,
mycylinder/.style={shape=Cylinder, aspect=1.5, minimum height=+3cm, draw,
cylinder uses custom fill, Cylinder end fill=blue!10,
Cylinder body shade={left color=blue!30, right color=blue!60, middle color=blue!10},
minimum width=+2cm, shape border rotate=90,
}]
\node[mycylinder, label=below:Betterer?] {};
\end{tikzpicture}
\end{document}
Output
Best Answer
For creating a standalone cylinder, without using the predefined TikZ shapes, you can do something like this: