6

Let's say I have such a simple tikz "contour" object defined with \draw:

\documentclass{standalone}

\usepackage{tikz}
    \tikzset{x=1pt, y=1pt, z=1pt}

\begin{document}

\def\object{
    \draw[blue, thick, -stealth] (0,  0)
                     .. controls (5, -5)
                             and (10, 5)
                              .. (15, 0);
}

\begin{tikzpicture}
    \object
\end{tikzpicture}

\end{document}

enter image description here

Is there a way I could convert it to an actual \filled path? Drawing it is easy with \draw, but transforming it (in particular, fading it) would be more easy if it were drawn with \fill.. but what a pain would it be to design it with \fill!

I am aware that the problem is that, since it is declared as a path, it has no width in theory. The thing is that it does have a width orelse we could not see it. The question is: how to take this actual width into account and turn it into an explicit path?

Put it another way: how would you draw a black outline around this object?

(Yes, I am thinking about Inkscape's object to path or convert to path feature.)


[EDIT] If my understanding of this related question is correct, I would need to highjack the code from stealth in a heavy \makeatletter .. \makeatother section so that I could retrieve the actual contour of the whole arrow.

However, I am actually using Stealth from arrows.meta, and I would not like the code to be rewritten if I fancy change Stealth to Latex. What I am looking for is a more generic solution to retrieve the actual path primitive built when I type: \draw[blue, thick, -stealth] (0, 0) .. controls (5, -5) and (10, 5) .. (15, 0); It must be computed at some point internally otherwise the compilation would be meaningless. How can I get this intermediate result?


In an ideal world, I would type:

\getActualPath{\object}{\result}

and \result would then be magically expanded as:

\path[cm={{0.8,0.0,0.0,-0.8,(-0.398,5.38)}}]
    (17.3457,1.2305) -- (16.9707,3.8555) .. controls (16.4721,3.4476) and
    (15.9195,3.2113) .. (15.3516,3.1621) .. controls (14.6806,3.1040) and
    (14.0125,3.2715) .. (13.3496,3.5488) .. controls (12.0238,4.1035) and
    (10.6766,5.1107) .. (9.2793,6.0215) .. controls (7.8820,6.9323) and
    (6.4476,7.7369) .. (5.0586,7.9473) .. controls (3.6696,8.1576) and
    (2.3182,7.8417) .. (0.8496,6.3730) -- (0.1465,7.0762) .. controls
    (1.7912,8.7209) and (3.5542,9.1820) .. (5.2070,8.9316) .. controls
    (6.8598,8.6813) and (8.3943,7.7895) .. (9.8242,6.8574) .. controls
    (11.2541,5.9253) and (12.5908,4.9452) .. (13.7344,4.4668) .. controls
    (14.3062,4.2276) and (14.8182,4.1155) .. (15.2656,4.1543) .. controls
    (15.6068,4.1839) and (15.9194,4.2931) .. (16.2363,4.5293) --
    (13.6836,4.8926) -- (19.1777,6.7246) -- (17.3457,1.2305) -- cycle

so that

\begin{tikzpicture}
    \result[draw, fill=blue, line width=.1pt];
\end{tikzpicture}

would draw

enter image description here

and, with \usetikzlibrary{fadings},

\begin{tikzpicture}
    \result[path fading=west, fill=blue];
\end{tikzpicture}

would draw

enter image description here

and

\begin{tikzpicture}
    \result[clip];
    \node () at (8, 5) {\includegraphics[width=50pt]{example-image-a}};
\end{tikzpicture}

would draw

enter image description here

.. would this not be neat?

iago-lito
  • 1,472
  • \newcommand\object[1][draw]{\path[blue, thick, -stealth, #1] (0, 0) .. controls (5, -5) and (10, 5) .. (15, 0);} and then \object[fill]? – Henri Menke Apr 04 '16 at 10:56
  • Or \def\object{[blue, thick, -stealth] (0, 0) .. controls (5, -5) and (10, 5) .. (15, 0);} and then \draw\object and \fill\object. – Henri Menke Apr 04 '16 at 10:57
  • Yes, I have tried it, and it does exactly what I expect. I don't know what you expect though, since your question is unclear to me. – Henri Menke Apr 04 '16 at 11:06
  • 1
  • @HenriMenke For example, I may wish to 1) draw a black outline all around the blue arrow, like you would do with a pencil.. or 2) clip a random picture so that it would be shaped like this arrow.. or 3) shade this arrow to that it would fade away going west.. For all these purposes, I would need the actual contour object to be a path, not just (0, 0) .. controls (5, -5) and (10, 5) .. (15, 0). – iago-lito Apr 04 '16 at 11:11
  • @HenriMenke Possibly indeed. I'll get into this :) – iago-lito Apr 04 '16 at 11:12
  • 1
    As I understand it from LoopSpace's answer, this is simply not possible for arrow heads. Unless the introduction of arrows.meta has changed things? But that answer suggests it can be done for paths. I don't think this is a duplicate now it has been edited. It may well not be possible, though. That is, there may well be no solution which works as easily as you hope. – cfr Apr 04 '16 at 11:54
  • @cfr too bad.. well, this is only 2016, right ? ;) What would you think of as a workaround then? – iago-lito Apr 04 '16 at 12:04
  • It's your question ;). And I don't know what difference arrows.meta makes. – cfr Apr 04 '16 at 13:04
  • You can fade it as a path, but I guess you know that? – cfr Apr 04 '16 at 22:24

1 Answers1

2

For fading, there really isn't a problem as we can fade a path or use a scope fading to apply whatever fading effects we wish to any path, even if the path is merely drawn rather than filled. We can apply this equally to paths specified and drawn within the scope and to predefined paths such as \object. For example

\begin{scope}[opacity=1,transparency group] \path [scope fading=west] (0,-2) rectangle (3,1); \object \draw [green, ultra thick, -{Stealth[bend]}] (0,1) .. controls (1,-1) and (2,1) .. (3,1); \end{scope}

There is one caveat to this which is that the method works fine when the arrows are heading in the direction of opacity, but less well when the arrows head for transparency. That is, if we add

    \draw [magenta, ultra thick, {Stealth[bend]}-] (0,-1) .. controls (1,-1) and (2,1) .. (3,-1);

to our scope, the results will be less satisfactory. How much of an issue this is depends on the amount of transparency, type of arrow tip and size of arrow tip.

For greater flexibility, you can define \object to take an optional argument allowing you to write, for example,

\object[path fading=east]

or whatever.

This also permits a rather fiddly and not entirely satisfactory workaround for the outlining issue because we can say, for example

    \object[double=blue, draw=black, line width=.4pt, double distance=1.2pt, -{Stealth[sep=-9.5pt,fill=blue,width=1.7ex,length=2.1ex,bend]}]

to approximate an outlined version of our arrow. This can be additionally faded if placed within the scope we defined above. However, the solution is fiddly and the outline of the arrow tip is drawn completely which we would prefer not to happen.

The results look as follows:

tweaking arrows

\documentclass[tikz,multi,border=10pt]{standalone}
\usetikzlibrary{bending,arrows.meta,fadings}
\begin{document}
\newcommand*\object[1][]{%
  \draw [blue, ultra thick, -{Stealth[bend]}, #1] (0,0) .. controls (1,-1) and (2,1) .. (3,0);
}
\begin{tikzpicture}
  \begin{scope}[opacity=1,transparency group]
    \path [scope fading=west] (0,-2) rectangle (3,1);
    \scoped[yshift=-15mm]{\object[double=blue, draw=black, line width=.4pt, double distance=1.2pt, -{Stealth[sep=-9.5pt,fill=blue,width=1.7ex,length=2.1ex,bend]}]}
    \object
    \draw [green, ultra thick, -{Stealth[bend]}] (0,1) .. controls (1,-1) and (2,1) .. (3,1);
    \draw [magenta, ultra thick, {Stealth[bend]}-] (0,-1) .. controls (1,-1) and (2,1) .. (3,-1);
  \end{scope}
\end{tikzpicture}
\end{document}
cfr
  • 198,882
  • This is a quite simple workaround for the first two use cases, thanks :) I cannot mark the answer as accepted, but why not gathering here other neat workarounds? I have just added another use case for my utopic \getActualPath command. – iago-lito Apr 05 '16 at 06:30
  • Interestingly, the fading is not visible with Evince, which is the reason why I could not get scope fading working when I first tried it.. crab! – iago-lito Apr 05 '16 at 06:45
  • Strange. The screenshots are from Okular and I thought both were poppler based. – cfr Apr 05 '16 at 11:58
  • I thought so as well. I migrated towards Okular just to get your example working.. and the rendering is now awful even though fading is supported.. compiling the latest version of poppler had no magic effect. Any idea how to tell Okular which renderer to use? – iago-lito Apr 05 '16 at 12:01
  • Not really. Mine just works. (Or I'm less particular about rendering.) I imagine it is a question of what Okular is compiled with rather than anything else. I have version 15.12.3 of Okular with 0.41.0 of the Poppler-QT4 bindings and 0.41.0 of Poppler. (Arch is mostly using QT5 stuff, but some things still need QT4.) – cfr Apr 05 '16 at 12:11