%-------------------------------------------- % % Package pgfplots % % Provides a user-friendly interface to create function plots (normal % plots, semi-logplots and double-logplots). % % It is based on Till Tantau's PGF package. % % Copyright 2013 by Christian Feuersaenger % % This program is free software: you can redistribute it and/or modify % it under the terms of the GNU General Public License as published by % the Free Software Foundation, either version 3 of the License, or % (at your option) any later version. % % This program is distributed in the hope that it will be useful, % but WITHOUT ANY WARRANTY; without even the implied warranty of % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the % GNU General Public License for more details. % % You should have received a copy of the GNU General Public License % along with this program. If not, see . % %-------------------------------------------- % This library adds support for high-level instructions for "fill area % between two arbitrary plots of functions". % % It activates the syntax \pgfpathfillbetween % where A and B are two plots named by 'name path='. % % In fact, this here is not much more than a low-level invocation of % \pgfpathfillbetween % and a couple of styles. It could become a TikZ library because it % actually works on any two named paths, but it has its restrictions % regarding the supported input paths: both need to be plots of % functions (non-intersecting, should have at most one function value % for each canvas X coord) \pgfutil@IfUndefined{pgfplotsset}{% \pgferror{Please load pgfplots before pgfplots.fillbetween.}% \endinput }{}% \usetikzlibrary{intersections} \usetikzlibrary{decorations.softclip} % COMPATIBILITY WITH PGF RELEASE: \pgfutil@IfUndefined{pgfintersectiongetsolutiontimes}{% \pgfplotsusecompatibilityfile{pgflibraryintersections.code.tex}% }{}% \def\tikz@key@name@path@wrong#1#2{% \tikz@addmode{% \pgfsyssoftpath@getcurrentpath\tikz@intersect@temppath@round% \pgfprocessround\tikz@intersect@temppath@round\tikz@intersect@temppath% \ifx\tikz@intersect@namedpaths\pgfutil@empty% \else% \tikz@intersect@namedpaths% \pgfutil@ifundefined{tikz@intersect@path@name@#1}{}% {% \expandafter\expandafter\expandafter\def\expandafter\expandafter\expandafter\tikz@intersect@@temppath% \expandafter\expandafter\expandafter{\csname tikz@intersect@path@name@#1\endcsname}% \expandafter\expandafter\expandafter\def\expandafter\expandafter\expandafter\tikz@intersect@temppath% \expandafter\expandafter\expandafter{\expandafter\tikz@intersect@temppath\tikz@intersect@temppath}% }% \fi% \tikz@intersect@addto@path@names{#1}{#2}% }% }% \def\tikz@key@name@path@new#1#2{% \tikz@addmode{% \pgfsyssoftpath@getcurrentpath\tikz@intersect@temppath@round% \pgfprocessround\tikz@intersect@temppath@round\tikz@intersect@temppath% \ifx\tikz@intersect@namedpaths\pgfutil@empty% \else% \tikz@intersect@namedpaths% \fi% \tikz@intersect@addto@path@names{#1}{#2}% }% }% \ifx\tikz@key@name@path@wrong\tikz@key@name@path \immediate\write16{Package pgfplots: loading complementary 'name path' implementation for your pgf version...} \let\tikz@key@name@path=\tikz@key@name@path@new \fi % ------------------------------------------------------------ \newif\iftikzfillbetween@optimize@name@intersections \pgfkeys{% /tikz/fill between/of/.code=\tikzlibraryfillbetween@parse#1\pgf@stop, % /tikz/fill between/on layer/.initial=pre main, /tikz/fill between/every segment/.style={}, /tikz/fill between/every odd segment/.style={}, /tikz/fill between/every even segment/.style={}, /tikz/fill between/every last segment/.style={}, % % Allows to add path instructions *after* the segment. % If you want to add some *before* the segment, you can rely on % 'every segment no 0/.style={ ... suitable tikz options ... }' /tikz/fill between/path after segment/.initial={}, % % soft clip={(axis cs:0,0) rectangle (axis cs:1,1)} /tikz/fill between/soft clip/.style={ /tikz/fill between/soft clip first={#1},% /tikz/fill between/soft clip second={#1},% },% /tikz/fill between/soft clip first/.initial=,% /tikz/fill between/soft clip second/.initial=, % % #1: drawing options. /tikz/fill between/@draw style/.style={ /pgf/fill between/result stream/begin/.code={% \gdef\tikzsegmentindex{0}% \xdef\tikzsegmentindices{##1}% \c@pgf@countc=##1 % \advance\c@pgf@countc by-1 % \xdef\tikzsegmentlastindex{\the\c@pgf@countc}% },% /pgf/fill between/result stream/next ready/.code={% \let\pgflibraryfill@path=\pgfretval \def\pgfplots@loc@TMPa{% /tikz/fill between/every segment, #1,% /tikz/fill between/every segment no \tikzsegmentindex/.try, }% \ifodd\tikzsegmentindex\relax \expandafter\def\expandafter\pgfplots@loc@TMPa\expandafter{\pgfplots@loc@TMPa /tikz/fill between/every odd segment, }% \else \expandafter\def\expandafter\pgfplots@loc@TMPa\expandafter{\pgfplots@loc@TMPa /tikz/fill between/every even segment, }% \fi % \ifnum\tikzsegmentindex=\tikzsegmentlastindex\relax \expandafter\def\expandafter\pgfplots@loc@TMPa\expandafter{\pgfplots@loc@TMPa /tikz/fill between/every last segment, }% \fi % \expandafter\fill\expandafter[\pgfplots@loc@TMPa] \pgfextra \pgfsetpathandBB{\pgflibraryfill@path}% \pgfkeysgetvalue{/tikz/fill between/path after segment}\tikz@fillbetween@post@segment \expandafter \endpgfextra \tikz@fillbetween@post@segment ;% \pgfplotsutil@advancestringcounter@global\tikzsegmentindex },% /pgf/fill between/result stream/end/.code=,% }, /tikz/fill between/.search also={/pgf/fill between,/pgfplots}, /tikz/fill between/optimize name intersections/.is if=tikzfillbetween@optimize@name@intersections, % % FIXME : this optimization needs much more work... I believe it % would be stable enough, but it covers too few cases. %/tikz/fill between/optimize name intersections=true, % % % %-------------------------------------------------- % /pgfplots/execute at begin axis@@/.add={% % \def\b@pgfplotslibraryfill@added % }{% % % },% %-------------------------------------------------- } \def\tikzlibraryfillbetween@parse#1 and #2\pgf@stop{% \def\tikz@fillbetween@a{#1}% \def\tikz@fillbetween@b{#2}% }% % \tikzfillbetween[]{} % % must contain 'of= and ' and may configure how the % area is computed. % % affects every drawn region. % \def\tikzfillbetween{\pgfutil@ifnextchar[{\tikzfillbetween@opt}{\tikzfillbetween@opt[]}} \def\tikzfillbetween@opt[#1]#2{% \begingroup \pgfqkeys{/tikz/fill between}{% % prepare the low-level path generation instruction(s): /tikz/fill between/@draw style={#2},% % % set up input options: #1,% }% % automagically try to use the correct layer: \pgfkeysgetvalue{/tikz/fill between/on layer}\tikzlibraryfillbetween@path@layer@name \tikzlibraryfillbetween@determine@layer % \ifx\tikzlibraryfillbetween@path@layer@name\pgfutil@empty \else \pgfonlayer{\tikzlibraryfillbetween@path@layer@name}% \fi % \tikzlibraryfillbetween@path@generatepath % \ifx\tikzlibraryfillbetween@path@layer@name\pgfutil@empty \else \endpgfonlayer% \fi % \endgroup }% % Defines \tikzlibraryfillbetween@path@layer@name \def\tikzlibraryfillbetween@determine@layer{% \ifx\tikzlibraryfillbetween@path@layer@name\pgfutil@empty \else \pgfutil@IfUndefined{pgf@layerlist}{% % hm. No layers active!? A pity... \tikzlibraryfillbetween@path@warn@layer \let\tikzlibraryfillbetween@path@layer@name\pgfutil@empty% }{% \edef\pgfplots@loc@TMPa{\noexpand\pgfutil@in@{\tikzlibraryfillbetween@path@layer@name}}% \expandafter\pgfplots@loc@TMPa\expandafter{\pgf@layerlist}% \ifpgfutil@in@ \else \tikzlibraryfillbetween@path@warn@layer \let\tikzlibraryfillbetween@path@layer@name\pgfutil@empty% \fi }% \fi }% \def\tikzlibraryfillbetween@path@warn@layer{% \pgfplots@warning{'fill between': Could not activate graphics layer '\tikzlibraryfillbetween@path@layer@name'. Filled path will be on top of the other ones. Please ensure that '\tikzlibraryfillbetween@path@layer@name' is somewhere in the layer list (or set '/tikz/fill between/on layer=').}% }% \def\tikzlibraryfillbetween@parse@softclip{% \pgfkeysgetvalue{/tikz/fill between/soft clip first}\pgf@temp \pgfkeysgetvalue{/tikz/fill between/soft clip second}\pgf@tempb \ifx\pgf@temp\pgf@tempb % Ah - both have the same value! \ifx\pgf@temp\pgfutil@empty % ... and both are empty. \pgffillbetweensetsoftclippath{\pgfutil@empty}% \else % ... and both contain some path! Process it (once): \def\tikz@marshal{\tikzlibsoftclip@setkey{\pgffillbetweensetsoftclippath}}% \expandafter\tikz@marshal\pgf@temp\pgf@stop \fi \else % handle 'soft clip first': \ifx\pgf@temp\pgfutil@empty \pgffillbetweensetsoftclippathfirst{\pgfutil@empty}% \else \def\tikz@marshal{\tikzlibsoftclip@setkey{\pgffillbetweensetsoftclippathfirst}}% \expandafter\tikz@marshal\pgf@temp\pgf@stop \fi % % handle 'soft clip second': \pgfkeysgetvalue{/tikz/fill between/soft clip second}\pgf@tempb \ifx\pgf@tempb\pgfutil@empty \pgffillbetweensetsoftclippathsecond{\pgfutil@empty}% \else \def\tikz@marshal{\tikzlibsoftclip@setkey{\pgffillbetweensetsoftclippathsecond}}% \expandafter\tikz@marshal\pgf@tempb\pgf@stop \fi \fi }% \def\tikzlibraryfillbetween@path@generatepath{% % \tikzlibraryfillbetween@parse@softclip % \tikzlibraryfillbetween@path@check \expandafter\let\expandafter\tikz@fillbetween@a@path \csname tikz@intersect@path@name@\tikz@fillbetween@a\endcsname \expandafter\let\expandafter\tikz@fillbetween@b@path \csname tikz@intersect@path@name@\tikz@fillbetween@b\endcsname % \iftikzfillbetween@optimize@name@intersections \ifpgfpathfillbetween@split \tikzfillbetween@optimize@name@intersections\tikz@fillbetween@a\tikz@fillbetween@b \fi \fi % \pgfpathfillbetween{\tikz@fillbetween@a@path}{\tikz@fillbetween@b@path}% }% \def\tikzlibraryfillbetween@path@check{% \pgfutil@IfUndefined{tikz@intersect@path@name@\tikz@fillbetween@a}{% \pgferror {fill between: the mandatory argument 'of= and is missing or has empty arguments. Please ensure that the option has been set and that both path names have been assigned (perhaps you need 'name path global=\tikz@fillbetween@a' somewhere?)}% }{}% \pgfutil@IfUndefined{tikz@intersect@path@name@\tikz@fillbetween@b}{% \pgferror {fill between: the mandatory argument 'of= and is missing or has empty arguments. Please ensure that the option has been set and that both path names have been assigned (perhaps you need 'name path global=\tikz@fillbetween@b' somewhere?)}% }{}% } %----------------------------------------- % % Utilities to work with path segments % %----------------------------------------- % Defines \pgfretval such that it contains the named path '#1' \def\tikzgetnamedpath#1{% \pgfutil@IfUndefined{tikz@intersect@path@name@#1}{% \pgferror{There is no named path called '#1'. Perhaps you misspelled it?}% }{% \expandafter\let\expandafter\pgfretval \csname tikz@intersect@path@name@#1\endcsname }% }% \def\tikznamecurrentpath#1{% \pgfgetpath\pgf@temp \pgfprocessround\pgf@temp\pgf@tempb% \expandafter\global\expandafter\let\csname tikz@intersect@path@name@#1\endcsname=\pgf@tempb }% % --------------------------------------------------------------------------------------- \newif\iftikzpathsegments@reverse \def\tikzlibrarysegments@parse#1 and #2\pgf@stop{% \def\tikz@path@segments@a{#1}% \def\tikz@path@segments@b{#2}% }% \pgfkeys{ % allows to append intersection segments to the current path by % writing % \path[intersection segments={of=first and second, sequence=A0 -- B1 -- B3 A3[reverse] -- A1}]; /tikz/intersection segments/.code={% \tikzpathintersectionsegments[#1]% }, /tikz/segments/of/.code=\tikzlibrarysegments@parse#1\pgf@stop, /tikz/segments/of={} and {}, /tikz/segments/sequence/.initial=A0 -- B1, /tikz/segments/reverse/.is if=tikzpathsegments@reverse, /tikz/segments/reverse/.default=true, } % Programmatic method to do the same as 'intersection % segments={} % % #1: \def\tikzpathintersectionsegments[#1]{% \begingroup \pgfqkeys{/tikz/segments}{#1}% \tikzgetnamedpath{\tikz@path@segments@a}% \let\tikz@path@segments@A=\pgfretval \tikzgetnamedpath{\tikz@path@segments@b}% \let\tikz@path@segments@B=\pgfretval % % this macro will be called whenever we need an intersection % segment. Perhaps we do not need it at all \def\tikz@ensurehascomputedintersection{% % compute intersections using the PGF intersection lib... \pgfintersectionofpaths{\pgfsetpath\tikz@path@segments@A}{\pgfsetpath\tikz@path@segments@B}% % \ifnum\pgfintersectionsolutions=0 % \pgferror{The argument of 'sequence' requests an intersection segment -- but the two input paths do not intersect. Please use A* or B* to select the whole path}% \def\b@tikz@select@all{1}% \else % % ... and compute the intersection *segments* for both input % paths... \pgfcomputeintersectionsegments1% \pgfcomputeintersectionsegments2% \fi \let\tikz@ensurehascomputedintersection=\relax }% % \pgfkeysgetvalue{/tikz/segments/sequence}\tikz@sequence % \def\b@tikz@isect@nextismoveto{1}% \let\pgfpointlastofsetpath=\pgfutil@empty \expandafter\tikzpathintersectionsegments@parse@loop\tikz@sequence\pgf@stop \pgfmath@smuggleone\pgfpointlastofsetpath \endgroup \tikz@make@last@position{\pgfpointlastofsetpath}% }% \def\tikzpathintersectionsegments@parse@loop{% \pgfutil@ifnextchar-{% \tikz@isect@p@lineto }{% \pgfutil@ifnextchar A{% \tikz@isect@p@next }{% \pgfutil@ifnextchar B{% \tikz@isect@p@next }{% \pgfutil@ifnextchar L{% \tikz@isect@p@next }{% \pgfutil@ifnextchar R{% \tikz@isect@p@next }{% \pgfutil@ifnextchar\pgf@stop{% \tikz@isect@finish }{% \tikz@isect@p@error }% }% }% }% }% }% }% \def\tikz@isect@finish\pgf@stop{}% \def\tikz@isect@p@lineto--{% \def\b@tikz@isect@nextismoveto{0}% \tikzpathintersectionsegments@parse@loop }% \def\tikz@isect@p@error#1\pgf@stop{% \pgferror{The argument of 'sequence' has an unexpected format near '#1'. Please write something like A0 -- B1 -- A1}% } \def\tikz@isect@p@next#1#2{% \def\tikz@temp{#2}% \def\tikz@@temp{-}% \ifx\tikz@@temp\tikz@temp % also accept minus signs without curly braces, i.e. % L-2 instead of L{-2} \def\tikz@next{\tikz@isect@p@next@{#1}{#2}}% \else \def\tikz@next{\tikz@isect@p@next@{#1}{#2}{}}% \fi \tikz@next }% \def\tikz@isect@p@next@#1#2#3{% \pgfutil@ifnextchar[{% \tikz@isect@p@next@opt{#1}{#2#3}% }{% \tikz@isect@p@next@opt{#1}{#2#3}[]% }% }% \def\tikz@isect@p@next@opt#1#2[#3]{% \begingroup % % set keys (if any): \def\tikz@temp{#3}% \ifx\tikz@temp\pgfutil@empty \else \pgfqkeys{/tikz/segments}{#3}% \fi % \def\tikz@indexshift{}% \if A#1% % FIRST function (0-based index) \def\tikz@path{1}% \def\tikz@path@ab{A}% \else \if B#1% % SECOND function (0-based index) \def\tikz@path{2}% \def\tikz@path@ab{B}% \else \if L#1% % FIRST function (1-based index) \def\tikz@path{1}% \def\tikz@indexshift{-1}% \def\tikz@path@ab{A}% \else \if R#1% % SECOND function (1-based index) \def\tikz@path{2}% \def\tikz@indexshift{-1}% \def\tikz@path@ab{B}% \else \def\tikz@path{}% \pgferror{The argument of 'sequence' has an unexpected format near '#1#2': expected L#2 or R#2}% \fi \fi \fi \fi % % parse arguments: \def\pgfmathresult{#2}% \def\tikz@temp{*}% \ifx\pgfmathresult\tikz@temp \def\b@tikz@select@all{1}% \else \def\b@tikz@select@all{0}% \tikz@ensurehascomputedintersection \pgfmathparse{round(#2)}% \let\tikz@index=\pgfmathresult \ifx\tikz@indexshift\pgfutil@empty \else \afterassignment\pgfutil@gobble@until@relax \c@pgf@countc=\tikz@index\relax \ifnum\c@pgf@countc<0 \else % the index shift is ONLY for positive numbers: we % want to start indexing at 1, not at 0 -- and the % negative ones start at -1 anyway. \advance\c@pgf@countc by\tikz@indexshift\relax \edef\tikz@index{\the\c@pgf@countc}% \fi \fi \fi % % % PROCESS IT: \ifx\tikz@path\pgfutil@empty \let\pgfpointlastofsetpath=\pgfutil@empty \else \if1\b@tikz@select@all% % ok... select the *entire* path. % #1 = A|B : \expandafter\let\expandafter\pgfretval\csname tikz@path@segments@\tikz@path@ab\endcsname \else \pgfgetintersectionsegmentpath{\tikz@path}{\tikz@index}% \fi \iftikzpathsegments@reverse \pgf@reverse@path\pgfretval \fi \if0\b@tikz@isect@nextismoveto \pgfpathreplacefirstmoveto\pgfretval \fi \pgfaddpathandBB\pgfretval \fi \pgfmath@smuggleone\pgfpointlastofsetpath \endgroup \def\b@tikz@isect@nextismoveto{1}% \tikzpathintersectionsegments@parse@loop }% \def\tikzfillbetween@optimize@name@intersections#1#2{% \edef\tikzfillbetween@precached@intersectionofpaths@A{#1}% \edef\tikzfillbetween@precached@intersectionofpaths@B{#2}% \pgfkeys{% /tikz/name intersections/.add code={% \let\pgfintersectionofpaths=\tikzfillbetween@precached@intersectionofpaths }{% \let\pgfintersectionofpaths=\pgfintersectionofpaths@orig } }% }% \let\pgfintersectionofpaths@orig=\pgfintersectionofpaths \def\tikzfillbetween@precached@intersectionofpaths@log{% \immediate\write-1{fill between: outcome of 'name intersections={of=\tikz@intersect@path@a\space and \tikz@intersect@path@b}' has been computed from available information of fill between}% } \def\tikzfillbetween@precached@intersectionofpaths#1#2{% \def\pgf@loc@TMPa{0}% \ifx\tikz@intersect@path@a\tikzfillbetween@precached@intersectionofpaths@A \ifx\tikz@intersect@path@b\tikzfillbetween@precached@intersectionofpaths@B \def\pgf@loc@TMPa{1}% \fi \fi \ifx\tikz@intersect@path@b\tikzfillbetween@precached@intersectionofpaths@A \ifx\tikz@intersect@path@a\tikzfillbetween@precached@intersectionofpaths@B \def\pgf@loc@TMPa{1}% \fi \fi % \if1\pgf@loc@TMPa \tikzfillbetween@precached@intersectionofpaths@log \relax \else \pgfintersectionofpaths@orig{#1}{#2}% \fi }% \endinput