This seems to be a largely unsolved problem within TeX.SE Questions and Answers.
- Percusse produced a usable solution
- Johannes asks a similar question but for shapes
A few questions that skirt the issue or are relevant to improve this answer are also present.
- This tackles shading, for example
Extending Percusse's code and wrapping it into a set of TikZ/PGF keys makes it useful to others. I have created to keys diffuse gradient
and diffuse falloff
which draw shaded lines by retracing the path multiple times.
\documentclass[tikz]{standalone}
\usepackage{graphicx}
\usetikzlibrary{positioning}
\tikzset{
diffuse color/.initial = black, % (1) The color value does not carry through to the pre- and post- actions
}
\tikzset{
linear opacity/.initial=0.5, % Initial value must be defined here to define the next key
linear stroke/.style = { % Define a style to draw a diffused sstroke
preaction={ % This uses preactions to draw the gradiens
draw=\pgfkeysvalueof{/tikz/diffuse color}, % Draw and colour the path (See 1). This is disabled by `/tikz/diffuse gradient' (See 2)
line width = (2.0-#1)*\pgflinewidth, % Vary the Line width for each stroke, change 2.0 to 1.0 to normalize scaling
opacity=\pgfkeysvalueof{/tikz/linear opacity}}}, % Keep resetting the opacity for each line, there is probably a cleaner means of setting this (See 3)
diffuse gradient/.style={ % This style executes `/tikz/diffuse' stroke multiple times to achieve the gradient effect
draw = none, % (2) Disable the default draw operation
linear opacity=#1, % (3) Set a consistent value for opacity. In retrospect one could have simply assigned this to /tikz/opacity
linear stroke/.list={0.0,#1,...,1.0}}, % Draw the line multiple times. Ideally we would use (1.0-1/#1) as the final value and (1/#1) as the stepsize
diffuse gradient/.default=1, % Set an initial step size
}
\tikzset{
non-linear stroke/.style = { % Define a style to draw a diffused sstroke
preaction={ % This uses preactions to draw the gradiens
draw=\pgfkeysvalueof{/tikz/diffuse color}, % Draw and colour the path (See 1). This is disabled by `/tikz/diffuse falloff' (See 4)
line width = (2.0-#1)*\pgflinewidth, % Vary the Line width for each stroke, change 2.0 to 1.0 to normalize scaling
opacity=#1}}, % Vary the opacity for each stroke
diffuse falloff/.style={ % This style executes `/tikz/diffuse' stroke multiple times to achieve the gradient effect
draw = none, % (4) Disable the default draw operation
non-linear stroke/.list={0.0,#1,...,1.0}}, % Draw the line multiple times. Ideally we would use (1.0-1/#1) as the final value and (1/#1) as the stepsize
diffuse falloff/.default=1, % Set an initial step size
}
\begin{document}
\begin{tikzpicture}
\draw [line width = 5pt, diffuse gradient= 0.5] (0, 3em) ++ (-1, -1) -- coordinate (A) ++ (1,1);
\draw [line width = 5pt, diffuse gradient=0.05] (0, 0em) ++ (-1, -1) -- coordinate (B) ++ (1,1);
\draw [line width = 5pt, diffuse gradient=0.25,postaction={draw, red}] (0,-3em) ++ (-1, -1) -- coordinate (C) ++ (1,1);
\node[right=of A] {Coarse Gradient};
\node[right=of B] {Fine Gradient};
\node[right=of C] {Enforced Line};
\end{tikzpicture}
\begin{tikzpicture}
\draw [line width = 5pt, diffuse falloff= 0.3] (0, 3em) ++ (-1, -1) -- coordinate (A) ++ (1,1);
\draw [line width = 5pt, diffuse falloff=0.05] (0, 0em) ++ (-1, -1) -- coordinate (B) ++ (1,1);
\draw [line width = 5pt, diffuse falloff=0.25,postaction={draw, red}] (0,-3em) ++ (-1, -1) -- coordinate (C) ++ (1,1);
\node[right=of A] {Coarse Gradient};
\node[right=of B] {Fine Gradient};
\node[right=of C] {Enforced Line};
\end{tikzpicture}
\end{document}
The code is well annotated so that one may follow what is going on (This is more for my own reference then anything else). The color assignment is a work around. Note that the colour comes out slighlty faded when using the diffuse gradient
shader, I'm not entirely sure why this is. The coarse and fine gradient lines in the example output should both be black along the center, the latter is gray.
For the diffuse falloff
the line is more sharply defined, this probably gives a better "tube". The original colour is also preserved.
The enforced line is really just for show and highlights differences in the shading strategies. The red indicates where the usual line might be drawn, this is really a bit construed as the multiplicative constant, 2.0, doubles the actual line width. This should be set to 1.0 if one uses the code, or convert this into a parameter, line spread
.
The manner in which the shading is drawn allows for natural looking intersections as shown below
and of course an animation.
The ripples are done with the following :
\foreach \n in {0,0.025,...,1} {
\begin{tikzpicture}
\fill [inner color=blue!40!black, outer color=blue!60!black] circle (4em);
\begin{scope}
\path [clip] circle (4em);
\pgfmathsetmacro{\rA}{0.0 + mod(\n,1)}
\pgfmathsetmacro{\xA}{ 75 - 5*mod(\n,1)}
\draw [diffuse color = blue!\xA!black, line width = 2pt, diffuse falloff = 0.25] (0,0) circle (\rA em);
\pgfmathsetmacro{\rB}{1.0 + mod(\n,1)}
\pgfmathsetmacro{\xB}{ 70 - 5*mod(\n,1)}
\draw [diffuse color = blue!\xB!black, line width = 2pt, diffuse falloff = 0.25] (0,0) circle (\rB em);
\pgfmathsetmacro{\rC}{2.0 + mod(\n,1)}
\pgfmathsetmacro{\xC}{ 65 - 5*mod(\n,1)}
\draw [diffuse color = blue!\xC!black, line width = 2pt, diffuse falloff = 0.25] (0,0) circle (\rC em);
\pgfmathsetmacro{\rD}{3.0 + mod(\n,1)}
\pgfmathsetmacro{\xD}{ 60 - 5*mod(\n,1)}
\draw [diffuse color = blue!\xD!black, line width = 2pt, diffuse gradient= 0.25] (0,0) circle (\rD em);
\end{scope}
\end{tikzpicture}
}
Update :
The tubes are drawn using the following code.
\begin{tikzpicture}[
detail/.style = {
preaction={draw, every diffuse, line width=2.1*\pgflinewidth, black},
preaction={draw, every diffuse, line width=2*\pgflinewidth, red},
diffuse color=red!50!blue,
diffuse gradient=0.06,
rounded corners=0.75em,
},
square tube/.style={
line width = 0.25em,
every diffuse/.append style={line cap=rect},
detail
},
round tube/.style={
line width = 0.25em,
every diffuse/.append style={line cap=round},
detail
}
]
\draw[square tube] ( 0em, -1em) -| ( 2em, 1em) ( 3em,-1.5em) -- ( 3em,1em);
\draw[round tube] ( 0em, -1em) -| (-2em, 1em) (-3em,-1.5em) -- (-3em,1em);
% \draw [thick, diffuse falloff= 0.3] (120:-120:1em);
\end{tikzpicture}
@cfr Everyone is a critic ;P