# Drawing complex diagrams with TikZ using gradients

I am relatively new to LaTeX. I don't find it an issue writing documents and so on but when I try to draw diagrams I run into some bother. I was reading an article and found this diagram:

I was also reading about TikZ an have started drawing some diagrams, but nowhere as good as this. So, I was wondering, is it possible to draw something like this, without all the writing with TikZ, i.e., reproduce this as a plain diagram? Or is this beyond the capabilities of TikZ?

-
I would say that this kind of graphic is definitely not beyond the scope of tikZ as it is quite schematic. The question is if it is worth the work to build one such graphic in tikZ. That will definitely depend on your tikZ skills. If you had a whole bunch of quite similar graphics where you could reuse some of the code or build even templates for several features the effort would easier pay off. – Benedikt Bauer Sep 2 '13 at 13:17
Text, arrows, lines and chemical formulas is all made easy by LaTeX and TikZ. I believe, that the hard part will be the complex shadings and the textures. Maybe this can be helpfull!?: tex.stackexchange.com/questions/103980/… – Hans-Peter E. Kristiansen Sep 2 '13 at 13:21

Basic elements of this kind of diagrams with Asymptote, MWE:

% cycle.tex:
\documentclass{article}
\usepackage[inline]{asymptote}
\usepackage{lmodern}
\usepackage{upgreek}

\begin{document}
\begin{figure}
\begin{asy}
import graph;
import roundedpath;
import math;

//texpreamble("\usepackage{upgreek}");
defaultpen(fontsize(10pt));
real sc=2;
unitsize(sc*1bp);

// 1. bounding ellipse
guide ell=(150,60)..(75,120)..(3.4,60)..(75,0)..cycle;

// 2. day
pen penA=rgb(0.773,0.831,0.882);
pen penB=rgb(0.09,0.09,0.09);

pair a=(70,60);
pair b=(100,60);

fill(box((0,0),(90,120)),penA);

// night

fill(box((100,0),(150,120)),black);

// sun
pair sunPos=(51,107);
real sunR=3;
pen sunClr=rgb(0.98,0.973,0.149);
pen BorderPen=rgb(0.145,0.361,0.435)+1bp;

// sun beam
guide sunBeam=(2.5,0)--(Cos(360/16),Sin(360/16))--(Cos(360/16),-Sin(360/16))--cycle;

for(int i=0;i<8;++i){
filldraw(shift(sunPos)*rotate(360/8*i)*scale(sunR)*sunBeam,sunClr,BorderPen);
}

filldraw(shift(sunPos)*scale(sunR)*unitcircle,sunClr,BorderPen);

// water
real wave0=83;
real waveAm=1.5;
real waveT=16;

real f(real x){return wave0+waveAm*sin(2pi/waveT*(x-5)); };
pen waveLinePen=rgb(0.329,0.533,0.675)+1.5bp;
pen waterClr=rgb(0.392,0.588,0.725)+opacity(0.382);

guide water=(150,0)--reverse(graph(f,0,150))--(0,0)--cycle;
filldraw(water,waterClr,waveLinePen);

//=== moon
pen moonLight=rgb(1,1,0.965);

pair[] moonCP={
(104,100),
(116,102),
(119,108),
(106,111),
(112,107),
(112,105),
};

guide moon=moonCP[0]..controls moonCP[1] and moonCP[2] .. moonCP[3]
.. controls moonCP[4] and moonCP[5]..cycle;

filldraw(moon,moonLight,BorderPen);

// bottom

fill(box((10,0),(150,30)),white+opacity(0.3));

// thin film
pen thinFilmPenA=rgb(0.325,0.459,0.416);
pen thinFilmPenB=rgb(0.357,0.514,0.478);

pair[] thinFilmCP={
(9,30),
(16,28),
(20,27),
(25,27),
(34,25),
(48,25),
(60,24),
(68,24),
(89,24),
(109,25),
(122,25),
(135,25),
(138,25),
(141,28),
(143,30),
(139,31),
(134,31),
(131,31),
(129,31),
(131,35),
(130,39),
(126,40),
(122,41),
(118,43),
(114,44),
(111,44),
(108,43),
(105,42),
(103,39),
(101,37),
(98,35),
(96,33),
(93,32),
(83,32),
(75,31),
(67,31),
(63,31),
(60,32),
(57,36),
(54,38),
(51,40),
(47,41),
(44,42),
(40,43),
(36,42),
(34,41),
(31,39),
(29,36),
(26,33),
(24,33),
(22,33),
(16,32),
(13,31),
};

guide thinFilm=graph(thinFilmCP,operator..)..cycle;

filldraw(thinFilm,thinFilmPenB,thinFilmPenA);

// === biofilm
pen bioFilmPenA=rgb(0.325,0.459,0.416);
pen bioFilmPenB=rgb(0.455,0.51,0.404);

pair[] bioFilmCP={
(16,28),
(20,27),
(25,27),
(34,25),
(48,25),
(60,24),
(68,24),
(89,24),
(109,25),
(122,25),
(135,25),
(138,25),
(141,28),
(143,30),
(138,30),
(130,31),
(123,31),
(114,33),
(103,32),
(99,31),
(93,31),
(86,30),
(76,30),
(68,30),
(60,30),
(57,30),
(52,31),
(43,32),
(33,32),
(28,31),
(24,31),
(19,30),
(16,31),
(11,30),
};

guide bioFilm=graph(bioFilmCP,operator..)..cycle;

filldraw(bioFilm,bioFilmPenB,bioFilmPenA);

//=== left stone
pen StonePenA=rgb(0.149,0.145,0.063);
pen StonePenB=rgb(0.302,0.259,0.141);

pair[] leftStoneCP={
(27,30),
(30,29),
(34,29),
(38,29),
(41,29),
(46,29),
(50,29),
(54,30),
(56,31),
(56,33),
(56,36),
(55,38),
(52,39),
(49,40),
(46,40),
(42,41),
(38,41),
(34,41),
(31,39),
(29,36),
(27,33),
};

guide leftStone=graph(leftStoneCP,operator..)..cycle;

filldraw(leftStone,StonePenB,StonePenA);

//== right Stone
pair[] rightStoneCP={
(100,32),
(102,31),
(105,31),
(108,31),
(111,31),
(115,31),
(119,31),
(122,31),
(125,32),
(127,32),
(130,33),
(130,35),
(129,38),
(126,39),
(125,40),
(122,41),
(120,41),
(118,43),
(114,43),
(111,43),
(107,42),
(105,41),
(104,39),
(102,37),
(101,35),
};

guide rightStone=graph(rightStoneCP,operator..)..cycle;

filldraw(rightStone,StonePenB,StonePenA);

// ====
clip(ell);

draw(ell,blue+2bp);

string[] sLabel={
"P>R",
"R>P",
"CO_2",
"O_2",
"Zn^{2+}",
"H_2AsO_4^{-}",
"NO_3^{-}\rightarrow N_2",
"MnO_x^{-}\rightarrow Mn^{2+}",
"FeO_x^{-}\rightarrow Fe^{2+}",
"CO_2",
"O_2",
"Zn^{2+}",
"H_2AsO_4^{-}",
"NH_4^{+}\rightarrow NO_3^{-}",
"Mn^{2+}\rightarrow MnO_x",
"Fe^{2+}\rightarrow FeO_x",
"Fe^{3+}\rightarrow Fe^{2+}",
"DOC\rightarrow DIC",
"\mathit{biofilm}",
};

pair[] labelPos={
(42,35),
(115,37),
(90,64),
(140,64),
(104,59),
(122,58),
(113,24),
(106,17),
(99,11),
(12,64),
(72,64),
(25,56),
(62,49),
(42,21),
(51,15),
(61,9),
(37,77),
(36,72),
(74,27),

};

pen[] labelClr={
white,
white,
white,
white,
white,
white,
white,
white,
white,
black,
black,
black,
black,
black,
black,
black,
black,
black,
black,

};

for(int i=0;i<sLabel.length;++i){
label("$\mathsf{"+sLabel[i]+"}$",labelPos[i],labelClr[i]);
}

string[] xLabel={
"h\upnu\uparrow\uparrow",
"T_{air}\uparrow",
"ET\uparrow",

"pH\uparrow",
"T_{water}\uparrow",
"DO,Eh\uparrow",
"streamflow\uparrow\!or\!\downarrow",

"pH\downarrow",
"T_{water}\downarrow",
"DO,Eh\downarrow",
"streamflow\downarrow\!or\!\uparrow",

"h\upnu\downarrow\downarrow",
"T_{air}\downarrow",
"ET\downarrow",

};

pair[] xlabelPos={
(7,112),(7,107),(7,102),
(-3,23),
(-3,18),
(-3,13),
(-3,8),

(155,23),
(155,18),
(155,13),
(155,8),

(135,112),
(135,107),
(135,102),

};

pair[] xlabelOff={
E,E,E,
E,E,E,E,
W,W,W,W,
E,E,E,
};

for(int i=0;i<xLabel.length;++i){
label("$\mathsf{"+xLabel[i]+"}$",xlabelPos[i],xlabelOff[i],black);
}

// springArrow
pair[] springArrowCP={
(48,102),
(44,100),
(47,99),
(42,97),
(45,95),
(41,92),
(44,90),
(40,88),
(42,85),
(38,80),
};

guide springArrow=roundedpath(graph(springArrowCP,operator--),1);

// thin arrows left

guide[] thinArrowB={
(72,70)..(67,86)..(56,94),
(23,94)..(13,84)..(10,69),
(49,37)..(56,39)..(61,45),
(26,52)..(28,44)..(34,39), // Zn->

};

for(int i=0;i<thinArrowB.length;++i){
}

guide[] thinArrowW={
(90,68)..(95,82)..(107,91)  ,
(119,91)..(132,85)..(141,69),
(124,57)..(122,49)..(116,44),
(112,44)..(107,49)..(105,56),
};

for(int i=0;i<thinArrowW.length;++i){
}

struct hydra{
guide stem,left,right;
pen p;
void operator init(guide stem, guide left,guide right,pen p=currentpen){
this.stem=stem;
this.left=left;
this.right=right;
this.p=p;
}
};

void draw(hydra h){
draw(h.stem, h.p+3bp);
}

hydra hydraRW=hydra(
(142,57)..(140,51)..(137,47)
,(137,47)..(135,39)..(135,29)
,(137,47)..(131,44)..(124,42)
,white
);

guide gtmp=(10,54)..(18,43)..(30,38);
hydra hydraRB=hydra(
subpath(gtmp,0,1)
,subpath(gtmp,1,2)
,(18,43)..(22,35)..(21,28)
,black
);

draw(hydraRW);
draw(hydraRB);

// plant

pair[] plantCP={
(0,0),
(1,1),
(2,2),
(3,3),
(3,4),
(4,6),
(7,6),
(9,5),
(11,7),
(12,9),
(12,11),
(13,14),
(14,16),
(15,17),
(13,16),
(12,15),
(10,13),
(10,11),
(9,9),
(8,7),
(6,8),
(4,8),
(3,7),
(2,6),
};

guide plant=roundedpath(graph(plantCP),0.5)..cycle;
pen plantPenA=rgb(0.133,0.141,0.067)+1bp;
pen plantPenB=rgb(0.294,0.38,0.169)+opacity(0.7);

pair[] plantPos={
(128,33),
(121,41),
(91,32),
(85,32),
(65,31),
(38,39),
(34,43),
(14,31),
};
for(int i=0;i<plantPos.length;++i){
filldraw(shift(plantPos[i])*plant,plantPenB,plantPenA);
}
\end{asy}
\caption{Drawing complex diagrams with \texttt{Asymptote} using gradients}
\end{figure}
\end{document}
%
% To process it with latexmk, create file latexmkrc:
%
%     sub asy {return system("asy '\$_[0]'");}
%
% and run latexmk -pdf cycle.tex.

-
This is great, but does asymptote not require python to run? Is there a method for converting this to latex code? I know it can be done with something like inkscape. – user36060 Sep 3 '13 at 9:28
@user36060: No, Asymptote does not require python to run, it has its own power syntax itself. And this MWE, cycle.tex, is already a valid LaTeX code, as you can see. It can be processed in three steps: pdflatex cycle.tex, asy -f pdf cycle-1.asy pdflatex cycle.tex. Python is needed to run the latexmk script, but this is just for mere convenience. – g.kov Sep 3 '13 at 10:07
I don't understand your three step guide above, this looks like linux? I'm on windows... – user36060 Sep 3 '13 at 13:58
@user36060: Commands pdflatex and asy are part of TeX Live distribution and they are platform-independent. – g.kov Sep 3 '13 at 14:43
By copying this exact example into latex I receive the following error: File asymptote.sty not found. \usepackage. – user36060 Sep 4 '13 at 8:32

As the OP said "without text" and as "a plain diagram", I've left out all the text and the arrows. I'm not at all happy with the reeds/eels, they look a bit rubbish. The main idea was to add a kind of 'texture' to some of the elements.

The first requirement for the textures is this bitmap:

which I just generated using Inkscape. This should be saved as sand.png

The rest looks like this:

\documentclass[border=5pt]{standalone}

\usepackage{tikz}
\usetikzlibrary{decorations.pathmorphing}
\usetikzlibrary{calc}

\definecolor{wave}{HTML}{54888C}

\definecolor{sea day}{HTML}{80A0C0}
\definecolor{sea night}{HTML}{314D6D}

\definecolor{sky day}{HTML}{DDE4EA}
\definecolor{sky night}{HTML}{000000}

\definecolor{biofilm day}{HTML}{818A70}
\definecolor{biofilm night}{HTML}{4F583E}

\definecolor{biofilm top day}{HTML}{6A8279}
\definecolor{biofilm top night}{HTML}{48625F}

\definecolor{seabed day}{HTML}{B4B4BB}
\definecolor{seabed night}{HTML}{494949}

\definecolor{rock}{HTML}{44401F}
\definecolor{reed}{HTML}{4F5F2A}

color(0bp)=(day); color(50bp)=(day); color(60bp)=(night); color(100bp)=(night)%
}%

\tikzset{
\colorlet{day}{#1 day}%
\colorlet{night}{#1 night}%
},
make sandy/.style={
postaction={
clip,
postaction={
path picture={
% Very crude tiling method. Am sure there is an easier way.
\path  let \p1=(path picture bounding box.south west),
\p2=(path picture bounding box.north east) in
[shift={(path picture bounding box.south west)},x=#1,y=#1]
\pgfextra
\pgfmathtruncatemacro\nx{ceil(\x2-\x1)/#1}
\pgfmathtruncatemacro\ny{ceil(\y2-\y1)/#1}
\pgfmathsetlengthmacro\size{#1}
\endpgfextra
\foreach \x in {0,...,\nx}{
\foreach \y in {0,...,\ny}{
(\x,\y)
node [inner sep=0pt, anchor=south west, opacity=0.125]{%
\pgfimage[interpolate=true, width=\size,height=\size]{sand.png}
}
}
};
}
}
}
},
make sandy/.default=32,
sky/.style={
},
sea/.style={
decoration={snake, segment length=1cm, amplitude=0.125cm},
postaction={draw=wave, ultra thick}
},
sea bed/.style={
make sandy=32
},
biofilm decoration/.style={
decoration={random steps, segment length=0.25cm, amplitude=0.0625cm, raise=0.125cm},
},
biofilm top/.style={
biofilm decoration,
decorate,
rounded corners=0.125cm,
},
biofilm/.style={
rounded corners=0.125cm,
biofilm decoration,
make sandy=64
},
rock/.style={
draw=rock!75!black,
very thick,
fill=rock,
rounded corners=0.25cm,
make sandy=128
},
reed/.style={
rounded corners=0.0625cm,
draw=reed!75!black,
thick,
fill=reed,
insert path={
{ ([shift={#1}]0,0) [x=0.75cm,y=0.75cm]  --
++(0.375,0.75) -- ++(0.375,-0.0625) -- ++(0.225,0.75) --
++(0.25,0.25) -- ++(-0.125,-0.25) -- ++(-0.25,-0.875) --
++(-0.375,0.0625) -- ++(-0.375,-0.75) -- cycle
}
}
},
moon/.style={
fill=white,
draw=sea night,
very thick
},
sun/.style={
fill=yellow,
draw=sea night,
very thick
}
}
\begin{document}

\begin{tikzpicture}[line join=round]

\begin{scope}

\path [sky] (-5,0)  rectangle (5,4);

\path [sea]
(-5,-2) -- (-5,1.5) decorate  { -- (5,1.5) } -- (5,-2) -- cycle;

\path [sea bed]
(-5,-4)  rectangle (5,-1.75);

\path [biofilm top, yshift=-1.75cm]
(-10,0) -- (-3.5,0) --
++(0.5,0.75) -- ++(0.75,0) -- ++(0.75,-0.5) -- ++(0.75,-0.25) --
(1.375,0) --
++(0.5,0.75) -- ++(0.75,0) -- ++(0.75,-0.5) -- ++(0.75,-0.25) --
(10,0) |- (-10,-0.25) -- cycle;

\path [biofilm]
(-5,-1.75) -- (-5,-2.5) decorate  { -- (5,-2.5) } -- (5,-1.75) -- cycle;

\path [rock, yshift=-1.75cm]
(-3.375,0) -- ++(0.375,0.75) -- ++(0.625,0) -- ++(0.75,-0.375) --
++(0.25,-0.5) -- ++(-0.5,-0.0)  --  ++(-0.375,-0.125) -- cycle
(1.5,0) -- ++(0.375,0.75) -- ++(0.625,0) -- ++(0.75,-0.375) --
++(0.25,-0.5) -- ++(-0.5,-0.0)  --  ++(-0.375,-0.125) -- cycle;

% Reeds or eels?
\foreach \p in {(-4.25,0), (-3,0.75), (-2.75,0.5), (-1,0), (0.5,0.125), (1,0.125), (3,0.5), (3.5,0.125)}
\path [shift={(0,-1.75)}, reed={\p}];

\path [moon] (2,3.5) arc (90:-90:0.25 and 0.375) arc (-90:90:0.5 and 0.375);

\path [sun, shift={(-2,3)}] ++(-22.5:0.25)
\foreach \a [evaluate={\r=mod(\a,2)/4+0.25;}]in {0,...,15}{ -- (\a*22.5-22.5:\r)} -- cycle;

\end{scope}

\end{tikzpicture}
\end{document}


Note, there maybe issues with some viewers or image converters on Linux which depend on old or oldish versions of poppler. Sometimes the textures will not be shown.

-
My answer to TikZ: Define pattern with reference to external picture might make the image pattern easier. The first revision actually used the same approach. – Qrrbrbirlbel Sep 4 '13 at 11:12
@Qrrbrbirlbel The easiest to use that image as a mask. – percusse Sep 4 '13 at 12:27
Dimension too large (10,0) |- (-10,-0.25) -- cycle; – user36060 Sep 4 '13 at 15:28
@user36060 no errors on 200 successive compilations. It may depend on the seed which can be set manually. – Mark Wibrow Sep 4 '13 at 16:25

Just as a start, this answer tries to provide the very basic concepts to accomplish this kind of drawings, thus it's far from being good.

First, the gradient. TikZ provides the fadings library which is of help in this case. Indeed, one may notice that it is possible to decouple into two parts the gradient: an constant one in the night part and a second one in the day part in which the gradient is uniform. For this reason I would define two fadings:

\begin{tikzfadingfrompicture}[name=fade left]
right color=transparent!75] (0,0) rectangle (2,2);

right color=transparent!65] (0,0) rectangle (2,2);


to be applied to the ellipse as:

\draw[thick,blue] (0,0) ellipse [x radius=4cm,y radius=3cm]; % draw the ellipse

% using rectangles for simplicity: the fading is applied here


For simplicity I used rectangles: if one really needs to draw this, it worth to find the right shape (a sort of ellipse cut) to avoid the \clip part.

Second: putting the backgrounds. There are several backgrounds so that library is absolutely needed.

So, my start would be:

\documentclass[tikz,border=2mm]{standalone}
\usepackage{tikz}
\usetikzlibrary{backgrounds,
decorations.pathmorphing,
patterns,
positioning,
shapes.geometric}

\pgfdeclarelayer{foreground}
\pgfsetlayers{background,main,foreground}

right color=transparent!75] (0,0) rectangle (2,2);

right color=transparent!65] (0,0) rectangle (2,2);

\begin{document}

\begin{tikzpicture}

\begin{scope}[on background layer]
% have to clip again

\fill (1.5,0)rectangle(4,3);
\node[semicircle,very thick,blue!60!cyan!80!gray,draw,fill=white,rotate=-94] at (2,2){};

\fill[blue!60!cyan!80!gray,decorate,decoration={snake,segment length=7mm}](4.5,0.5)--(-4.5,0.5)--(-4.7,-1.25)--(4.7,-1.25);

\fill[green!40!gray](4.5,-0.5)--(-4.5,-0.5)(-4,-1)arc(242:298:8.5cm);

\fill[gray,
opacity=0.2](-4,-1)arc(242:298:8.5cm)--++(0,-2)-|(-4,-1);
\end{scope}

\end{tikzpicture}
\end{document}


The layer foreground is not yet used, but should be necessary later on.

-
There's a glitch on the left of the picture: the cause is the bad filling I did. – Claudio Fiandrino Sep 2 '13 at 14:09
Thank you for downvoting. Don't be shy and try at least to provide an explanation for this: admittedly the result is not as good as the one of Mark, but intentionally this answer was explicitly given as "starting point". Look the first sentence. – Claudio Fiandrino Oct 20 '14 at 6:25