Drawing complex diagrams with TikZ using gradients
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);
axialshade(box((0,0),(100,120)),penA,a, extenda=false,penB,b, extendb=false);
// 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);
draw(springArrow,black+0.8bp,Arrow(HookHead,size=3));
// 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){
draw(thinArrowB[i],black+0.8bp,Arrow(HookHead,size=3));
}
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){
draw(thinArrowW[i],white+0.8bp,Arrow(HookHead,size=3));
}
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);
draw(h.left, h.p+3bp,Arrow(HookHead,size=10,filltype=Fill));
draw(h.right,h.p+3bp,Arrow(HookHead,size=10,filltype=Fill));
}
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]'");}
% add_cus_dep("asy","eps",0,"asy");
% add_cus_dep("asy","pdf",0,"asy");
% add_cus_dep("asy","tex",0,"asy");
%
% and run `latexmk -pdf cycle.tex`.
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}
\pgfdeclarehorizontalshading[day,night]{day to night}{100bp}{%
color(0bp)=(day); color(50bp)=(day); color(60bp)=(night); color(100bp)=(night)%
}%
\tikzset{
day to night shading/.code={
\colorlet{day}{#1 day}%
\colorlet{night}{#1 night}%
\tikzset{shading=day to 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={
day to night shading=sky,
},
sea/.style={
day to night shading=sea,
decoration={snake, segment length=1cm, amplitude=0.125cm},
postaction={draw=wave, ultra thick}
},
sea bed/.style={
day to night shading=seabed,
make sandy=32
},
biofilm decoration/.style={
decoration={random steps, segment length=0.25cm, amplitude=0.0625cm, raise=0.125cm},
},
biofilm top/.style={
day to night shading=biofilm top,
biofilm decoration,
decorate,
rounded corners=0.125cm,
},
biofilm/.style={
day to night shading=biofilm,
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}
\clip ellipse [x radius=5, y radius=4];
\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;
\path [sun] (-2,3) circle [radius=0.25];
\end{scope}
\draw [ultra thick, draw=sea night!75!black] ellipse [x radius=5, y radius=4];
\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.
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]
\shade[left color=transparent!100,
right color=transparent!75] (0,0) rectangle (2,2);
\end{tikzfadingfrompicture}
\begin{tikzfadingfrompicture}[name=uniform fade]
\shade[left color=transparent!65,
right color=transparent!65] (0,0) rectangle (2,2);
\end{tikzfadingfrompicture}
to be applied to the ellipse as:
\draw[thick,blue] (0,0) ellipse [x radius=4cm,y radius=3cm]; % draw the ellipse
\clip (0,0) ellipse [x radius=4cm,y radius=3cm]; % cut everything else
% using rectangles for simplicity: the fading is applied here
\fill [path fading=fade left,black] (1.5,-3) rectangle (-3,3);
\fill [path fading=uniform fade,black] (1.5,-3) rectangle (4,3);
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,
fadings,
patterns,
positioning,
shapes.geometric}
\pgfdeclarelayer{foreground}
\pgfsetlayers{background,main,foreground}
\begin{tikzfadingfrompicture}[name=fade left]
\shade[left color=transparent!100,
right color=transparent!75] (0,0) rectangle (2,2);
\end{tikzfadingfrompicture}
\begin{tikzfadingfrompicture}[name=uniform fade]
\shade[left color=transparent!65,
right color=transparent!65] (0,0) rectangle (2,2);
\end{tikzfadingfrompicture}
\begin{document}
\begin{tikzpicture}
\draw[thick,blue] (0,0) ellipse [x radius=4cm,y radius=3cm];
\clip (0,0) ellipse [x radius=4cm,y radius=3cm];
\fill [path fading=fade left,black] (1.5,-3) rectangle (-4,3);
\fill [path fading=uniform fade,black] (1.5,-3) rectangle (4,3);
\begin{scope}[on background layer]
% have to clip again
\clip (0,0) ellipse [x radius=4cm,y radius=3cm];
\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.