15

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:

picture

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?

2
  • 2
    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. Sep 2, 2013 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/… Sep 2, 2013 at 13:21

4 Answers 4

27

enter image description here

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`.
9
  • 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, 2013 at 9:28
  • 1
    @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, 2013 at 10:07
  • I don't understand your three step guide above, this looks like linux? I'm on windows...
    – user36060
    Sep 3, 2013 at 13:58
  • @user36060: Commands pdflatex and asy are part of TeX Live distribution and they are platform-independent.
    – g.kov
    Sep 3, 2013 at 14:43
  • By copying this exact example into latex I receive the following error: File asymptote.sty not found. \usepackage.
    – user36060
    Sep 4, 2013 at 8:32
10

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:

enter image description here

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}

enter image description here

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.

4
7

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}

enter image description here

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

2
  • 1
    There's a glitch on the left of the picture: the cause is the bad filling I did. Sep 2, 2013 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. Oct 20, 2014 at 6:25
0

Although there is an accepted answer, you might consider an alternative - draw it in Inkscape:

This hybrid approach enables you to use the strengths of Inkscape and LaTeX, and it will probably take less time than it takes to implement this with TikZ.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .