From 580a384585ecc80778ebca5845ce3bd7ae60cb48 Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Thu, 28 Jan 2021 03:01:03 +0000 Subject: CTAN sync 202101280301 --- graphics/pgf/contrib/sankey/sankey.sty | 722 +++++++++++++++++++++++++++++++++ 1 file changed, 722 insertions(+) create mode 100644 graphics/pgf/contrib/sankey/sankey.sty (limited to 'graphics/pgf/contrib/sankey/sankey.sty') diff --git a/graphics/pgf/contrib/sankey/sankey.sty b/graphics/pgf/contrib/sankey/sankey.sty new file mode 100644 index 0000000000..4c28a1fe0d --- /dev/null +++ b/graphics/pgf/contrib/sankey/sankey.sty @@ -0,0 +1,722 @@ +% Copyright (C) 2016-2021 by Paul Gaborit +% +% This file may be distributed and/or modified +% +% 1. under the LaTeX Project Public License and/or +% +% 2. under the GNU Public License. + +\def\sankey@version{2.0} +\def\sankey@date{2021/01/27} +\ProvidesPackage{sankey}[\sankey@date\space\sankey@version\space sankey package] + +\RequirePackage{xparse} +\RequirePackage{xfp} +\RequirePackage{tikz} +\usetikzlibrary{calc,decorations.markings,dubins} +\usepackage{etoolbox} + +% add a new layer to debug sankey diagrams +\pgfdeclarelayer{background} +\pgfdeclarelayer{foreground} +\pgfdeclarelayer{sankeydebug} +\pgfsetlayers{background,main,foreground,sankeydebug} + +% flag to debug sankey diagrams +\newtoggle{sankey debug} +\newtoggle{sankey node start} +\newtoggle{sankey node end} + +% macros to store and to retrieve the orientation and the quantity +% associated with each sankey node + +\def\sankeysetnodeqty#1#2{\expandafter\xdef\csname sankey@node@#1@qty\endcsname{#2}} + +\def\sankeygetnodeqty#1{% + \ifcsmacro{sankey@node@#1@qty}{% + \csname sankey@node@#1@qty\endcsname% + }{% + \PackageWarning{sankey}{Unknown sankey node '#1'}% + }% +} + +\def\sankeysetnodeorient#1#2{\expandafter\xdef\csname sankey@node@#1@orient\endcsname{#2}} + +\def\sankeygetnodeorient#1{% + \ifcsmacro{sankey@node@#1@orient}{% + \csname sankey@node@#1@orient\endcsname% + }{% + \PackageWarning{sankey}{Unknown sankey node '#1'}% + }% +} + +\def\sankeysetstartfill#1#2{\expandafter\gdef\csname sankey@start@fill@#1\endcsname{#2}} +\def\sankeysetstartdraw#1#2{\expandafter\gdef\csname sankey@start@draw@#1\endcsname{#2}} +\def\sankeysetendfill#1#2{\expandafter\gdef\csname sankey@end@fill@#1\endcsname{#2}} +\def\sankeysetenddraw#1#2{\expandafter\gdef\csname sankey@end@draw@#1\endcsname{#2}} +% +\def\sankeygetstartfill#1{\csname sankey@start@fill@#1\endcsname} +\def\sankeygetstartdraw#1{\csname sankey@start@draw@#1\endcsname} +\def\sankeygetendfill#1{\csname sankey@end@fill@#1\endcsname} +\def\sankeygetenddraw#1{\csname sankey@end@draw@#1\endcsname} + +% new shape +\pgfdeclareshape{sankey node}{ + \inheritsavedanchors[from=rectangle] + \inheritanchor[from=rectangle]{center} + %\inheritanchorborder[from=rectangle] + \anchor{left}{\pgf@process{\northeast}} + \anchor{right}{\pgf@process{\southwest}} +} + +% pgfkeys family 'sankey' +\pgfkeys{/sankey/.is family} + +\NewDocumentCommand\sankeyset{m}{\pgfkeys{sankey,#1}} + +\sankeyset{ + ratio length/.code={ + \pgfmathsetmacro\sankeytotallen{#1} + \edef\sankeytotallen{\sankeytotallen pt} + }, + ratio length/.value required, + ratio quantity/.code={\edef\sankeytotalqty{\fpeval{#1}}}, + ratio quantity/.value required, + ratio/.style args={#1/#2}{ + ratio length=#1, + ratio quantity=#2, + }, + ratio/.value required, + minimum radius/.estore in=\sankeyminradius, + minimum radius/.value required, + outin steps/.estore in=\sankeystepoutin, + outon steps/.value required, + % + debug/.is choice, + debug/true/.code={\toggletrue{sankey debug}}, + debug/false/.code={\togglefalse{sankey debug}}, + debug/.default=true, + % + start style/.is choice, + % + end style/.is choice, + % default values + default parameters/.style={ + % default values + ratio=1cm/10, + minimum radius=5mm,% + outin steps=10, + debug=false, + start style=none, + end style=none, + }, + % to make node + node parameters/.style={ + quantity/.code={% + %\typeout{quantity=##1}% + \edef\qty{\fpeval{##1}}% + %\typeout{qty=\qty}% + }, + quantity/.value required, + angle/.code={% + %\typeout{angle:##1}% + \pgfmathsetmacro\orient{##1}% + }, + angle/.value required, + name/.store in=\name, + name/.value required, + at/.store in=\pos, + at/.value required, + as/.style={ + name=##1, + quantity=\sankeygetnodeqty{##1}, + angle=\sankeygetnodeorient{##1}, + at={##1.center}, + }, + as/.value required, + % + anchor/.is choice, + anchor/left/.code={\def\sankeyanchor{left}}, + anchor/right/.code={\def\sankeyanchor{right}}, + anchor/center/.code={\def\sankeyanchor{center}}, + anchor=center, + % + start/.is choice, + start/true/.code={\toggletrue{sankey node start}}, + start/false/.code={\togglefalse{sankey node start}}, + start/.default=true, + start=false, + % + end/.is choice, + end/true/.code={\toggletrue{sankey node end}}, + end/false/.code={\togglefalse{sankey node end}}, + end/.default=true, + end=false, + }, +} + +% some styles +\sankeyset{ + node/.style={ + shape=sankey node, + inner sep=0,minimum height={\sankeyqtytolen{#1}}, + minimum width=0,draw=none,line width=0pt, + node contents={}, + }, + fill/.style={line width=0pt,fill=white}, + draw/.style={draw=black,line width=.4pt}, + % debug color used by all debug macros + debug color/.style={/utils/exec={\colorlet{debug color}{#1}}}, + debug color=red!75!black, + % debug line between left and right anchors + debug line/.style={draw=debug color,|-|}, + % debug line between center and label + debug normal/.style={draw=debug color}, + % debug node label + debug label/.style={ + draw, + font=\ttfamily\tiny, + text=debug color,text opacity=1, + inner sep=.1em, + fill=white,fill opacity=1, + rounded corners=.1em, + }, +} + +\sankeyset{ + new start style/.code n args={3}{% name, fill path, draw path + \sankeysetstartfill{#1}{#2} + \sankeysetstartdraw{#1}{#3} + \sankeyset{start style/#1/.code={\def\sankeystartstyle{#1}}} + }, + new end style/.code n args={3}{% name, fill path, draw path + \sankeysetendfill{#1}{#2} + \sankeysetenddraw{#1}{#3} + \sankeyset{end style/#1/.code={\def\sankeyendstyle{#1}}} + }, +} + +% the new 'sankeydiagram' environment +\NewDocumentEnvironment{sankeydiagram}{O{}+b}{ + + \newcommand\sankeyarc[2]{% sn, en + \begingroup + \pgfmathsetmacro\startangle{\sankeygetnodeorient{##1}} + \pgfmathsetmacro\endangle{\sankeygetnodeorient{##2}} + \pgfmathsetmacro\rotateangle{\endangle-\startangle} + \pgfmathtruncatemacro\acwrotate{\rotateangle>0?1:0} + \path let + \p1=(##1.left),\p2=(##1.right), + \p3=(##2.left),\p4=(##2.right), + \n1={\sankeyqtytolen{\sankeygetnodeqty{##1}}}, + \n{maxr}={\sankeyminradius+\n1}, + \n{minr}={\sankeyminradius} + in \pgfextra{ + \ifnumequal{\acwrotate}{1}{ + \begin{pgfinterruptpath} + % fill the region + \path[/sankey/fill] + (\p1) arc(\startangle-90:\endangle-90:\n{minr}) -- (\p3) -- + (\p4) arc(\endangle-90:\startangle-90:\n{maxr}) -- (\p2) -- cycle; + % draw left and right borders + \path[/sankey/draw] + (\p1) arc(\startangle-90:\endangle-90:\n{minr}) + (\p4) arc(\endangle-90:\startangle-90:\n{maxr}); + \end{pgfinterruptpath} + }{ + \begin{pgfinterruptpath} + % fill the region + \path[/sankey/fill] + (\p1) arc(\startangle+90:\endangle+90:\n{maxr}) -- (\p3) -- + (\p4) arc(\endangle+90:\startangle+90:\n{minr}) -- (\p2) -- cycle; + % draw left and right borders + \path[/sankey/draw] + (\p1) arc(\startangle+90:\endangle+90:\n{maxr}) + (\p4) arc(\endangle+90:\startangle+90:\n{minr}); + \end{pgfinterruptpath} + } + }; + \endgroup + } + + \newcommand\sankeymakenodenodebug{ + \begingroup + \sankeysetnodeqty{\name}{\qty} + \sankeysetnodeorient{\name}{\orient} + % \typeout{\qty,\orient} + \ifundef{\sankeyanchor}{\def\sankeyanchor{center}}{} + \node[/sankey/node=\qty,rotate=\orient,at/.expanded={(\pos)},name=\name,anchor=\sankeyanchor]; + \endgroup + } + + \newcommand\sankeymakenode{ + \begingroup + \sankeymakenodenodebug{} + \iftoggle{sankey debug}{ + \begin{pgfonlayer}{sankeydebug} + \pgfset{ + number format/relative*=-1, + number format/precision=1, + number format/fixed, + } + \path[/sankey/debug line] (\name.left) -- (\name.right); + \pgfmathsetmacro{\len}{\sankeyqtytolen{\qty}/3} + \path[/sankey/debug normal] (\name.center) + -- ($(\name.center)!\len pt!90:(\name.right)$) + node[/sankey/debug label,rotate=\orient+90,anchor=north] {\name:\pgfmathprintnumber{\qty}}; + \end{pgfonlayer} + }{} + \endgroup + } + + \newcommand\sankeynodenodebug[1]{ + \begingroup + \sankeyset{node parameters,##1} + %\typeout{sankeynodenodebug: '\name' at(\pos) angle(\orient) qty(\qty)} + \sankeymakenodenodebug{} + \endgroup + } + + % ============================================================ + % user commands + % ============================================================ + + \NewDocumentCommand\sankeydubins{O{}mm}{% options, sn, en + \begingroup + \sankeyset{##1} + \pgfmathsetmacro\startangle{\sankeygetnodeorient{##2}} + \pgfmathsetmacro\endangle{\sankeygetnodeorient{##3}} + \edef\sqty{\sankeygetnodeqty{##2}} + \edef\eqty{\sankeygetnodeqty{##3}} + \ifdefstrequal{\sqty}{\eqty}{}{ + \PackageError{sankey}% + {^^J*** \string\sankeyoutin: quantities differ between ##2 (\sqty) and ##3 (\eqty)^^J}% + {The quantities of the two Sankey nodes must be equal.} + } + \pgfmathsetmacro\qty{\sankeygetnodeqty{##2}} + \pgfmathsetmacro\width{\sankeyqtytolen{\qty}} + %\typeout{startangle:\startangle,endangle:\endangle,qty:\qty,width:\width} + \dubinspathset{ + sankey/.style={ + start point=##2.center,start angle=\startangle, + end point=##3.center,end angle=\endangle, + minimum radius=\sankeyminradius + .5 * \width pt, + }, + } + \dubinspathcalc{sankey,store=sankey} + + \dubinspathset{ + left border/.style={ + sankey, use store=sankey, + left and right minimum radii= + {\sankeyminradius} and {\sankeyminradius + \width pt}, + }, + right border/.style={ + sankey, use store=sankey, + left and right minimum radii= + {\sankeyminradius + \width pt} and {\sankeyminradius}, + }, + } + + \path let + \p1=(##2.left),\p2=(##2.right), + \p3=(##3.left),\p4=(##3.right) + in \pgfextra{ + \begin{pgfinterruptpath} + % fill the region + \path[/sankey/fill] + (\p1) + \dubinspath{left border} + -- (\p3) -- + (\p4) \dubinspath{right border,reverse} + -- (\p2) -- cycle; + + % draw left and right borders + \path[/sankey/draw] + (\p1) + \dubinspath{left border} + (\p2) + \dubinspath{right border} + ; + \end{pgfinterruptpath} + }; + \endgroup + } + + + \NewDocumentCommand\sankeyoutin{O{}mm}{% options, sn, en + \begingroup + \sankeyset{##1} + \edef\sn{##2} + \edef\en{##3} + \edef\sqty{\sankeygetnodeqty{\sn}} + \edef\eqty{\sankeygetnodeqty{\en}} + \ifdefstrequal{\sqty}{\eqty}{}{ + \PackageError{sankey}% + {^^J*** \string\sankeyoutin: quantities differ between \sn (\sqty) and \en (\eqty)^^J}% + {The quantities of the two Sankey nodes must be equal.} + } + %\typeout{*** sankeyoutin: \sn\space to \en\space (\sankeystepoutin)} + \pgfmathsetmacro\qty{\sankeygetnodeqty{\sn}} + \pgfmathsetmacro\mylength{\sankeyqtytolen{\qty}/2} + \pgfmathsetmacro\mystep{1/\sankeystepoutin} + %\typeout{*** a} + \pgfmathsetmacro\mybound{1-.5*\mystep} + %\typeout{*** b} + \pgfmathsetmacro\laststep{int(\sankeystepoutin-1)} + %\typeout{sankeystepoutin(\sankeystepoutin) mystep(\mystep) mybound(\mybound) laststep(\laststep)} + %\typeout{*** sankeyoutin: \startangle to \endangle} + \path[decorate,overlay,decoration={ + markings, + mark=between positions \mystep and \mybound step \mystep with { + \def\sankeyoutinmidptname{sankeyoutinmidpt \pgfkeysvalueof{/pgf/decoration/mark info/sequence number}} + \path + (0,0) coordinate(\sankeyoutinmidptname) + (0,-\mylength pt) coordinate (\sankeyoutinmidptname\space r) + (0,\mylength pt) coordinate (\sankeyoutinmidptname\space l) + ; + } + }](\sn.center) to[out=\sankeygetnodeorient{\sn},in=\sankeygetnodeorient{\en}+180] (\en.center); + \foreach \myptnum in {1,...,\laststep}{ + \def\sankeyoutinmidptname{sankeyoutinmidpt \myptnum} + \anglebetween\sankeyoutinmidptangle{\sankeyoutinmidptname\space r}{\sankeyoutinmidptname\space l} + \sankeynodenodebug{quantity=\qty,angle=\sankeyoutinmidptangle-90,name=\sankeyoutinmidptname,at=\sankeyoutinmidptname} + } + \sankeynodenodebug{quantity=\sankeygetnodeqty{\sn},angle=\sankeygetnodeorient{\sn},name={sankeyoutinmidpt 0},at={\sn}} + \sankeynodenodebug{quantity={\sankeygetnodeqty{\en}},angle={\sankeygetnodeorient{\en}},name={sankeyoutinmidpt \sankeystepoutin},at={\en}} + + %\typeout{*********************} + \path[/sankey/fill,looseness=1] + (sankeyoutinmidpt 0.left) + \foreach \curpt[remember=\curpt as \prevpt (initially 0)] in {1,...,\sankeystepoutin}{ + to[out=\sankeygetnodeorient{sankeyoutinmidpt \prevpt},in=\sankeygetnodeorient{sankeyoutinmidpt \curpt}+180] + (sankeyoutinmidpt \curpt.left) + } + -- + (sankeyoutinmidpt \sankeystepoutin.right) + \foreach \curpt[remember=\curpt as \prevpt (initially \sankeystepoutin)] in {\laststep,...,0}{ + to[out=\sankeygetnodeorient{sankeyoutinmidpt \prevpt}+180,in=\sankeygetnodeorient{sankeyoutinmidpt \curpt}] + (sankeyoutinmidpt \curpt.right) + } + -- cycle; + + \path[/sankey/draw,looseness=1] + (sankeyoutinmidpt 0.left) + \foreach \curpt[remember=\curpt as \prevpt (initially 0)] in {1,...,\sankeystepoutin}{ + to[out=\sankeygetnodeorient{sankeyoutinmidpt \prevpt},in=\sankeygetnodeorient{sankeyoutinmidpt \curpt}+180] + (sankeyoutinmidpt \curpt.left) + } + % + (sankeyoutinmidpt \sankeystepoutin.right) + \foreach \curpt[remember=\curpt as \prevpt (initially \sankeystepoutin)] in {\laststep,...,0}{ + to[out=\sankeygetnodeorient{sankeyoutinmidpt \prevpt}+180,in=\sankeygetnodeorient{sankeyoutinmidpt \curpt}] + (sankeyoutinmidpt \curpt.right) + } + ; + + \endgroup + } + + \NewDocumentCommand\sankeynodealias{mm}{%name, alias + \path [late options={name=##1,alias=##2}]; + \sankeysetnodeqty{##2}{\sankeygetnodeqty{##1}} + \sankeysetnodeorient{##2}{\sankeygetnodeorient{##1}} + } + + \newcommand\sankeynode[2][]{ + \begingroup + \sankeyset{##1} + \sankeyset{node parameters,##2} + %\typeout{sankeynode: '\name' at(\pos) angle(\orient) qty(\qty)} + \sankeymakenode{} + \endgroup + } + + \def\sankeyfilldrawstart{ + \begin{scope}[shift={(\name)},rotate=\orient] + \path[/sankey/fill] \sankeygetstartfill{\sankeystartstyle}; + \path[/sankey/draw] \sankeygetstartdraw{\sankeystartstyle}; + \end{scope} + } + + \NewDocumentCommand\sankeystart{O{}m}{ + \begingroup + \sankeyset{##1} + \edef\name{##2} + \edef\orient{\sankeygetnodeorient{##2}} + \edef\qty{\sankeygetnodeqty{##2}} + \sankeyfilldrawstart + \endgroup + } + + \NewDocumentCommand\sankeynodestart{O{}m}{ + \begingroup + %\typeout{sankeynodestart: ##2} + \sankeyset{##1} + \sankeyset{node parameters,##2} + %\typeout{sankeynodestart: '\name' at(\pos) angle(\orient) qty(\qty)} + \sankeymakenode{} + %\typeout{sankeynodestart: sankeynode ok} + %\typeout{sankeynodestart: '\name' at(\pos) angle(\orient) qty(\qty)} + \sankeyfilldrawstart + \endgroup + } + + \def\sankeyfilldrawend{ + \begin{scope}[shift={(\name)},rotate=\sankeygetnodeorient{\name}] + \path[/sankey/fill] \sankeygetendfill{\sankeyendstyle}; + \path[/sankey/draw] \sankeygetenddraw{\sankeyendstyle}; + \end{scope} + } + + \NewDocumentCommand\sankeyend{O{}m}{%name + \begingroup + \sankeyset{##1} + \edef\name{##2} + \edef\orient{\sankeygetnodeorient{##2}} + \edef\qty{\sankeygetnodeqty{##2}} + \sankeyfilldrawend + \endgroup + } + + \NewDocumentCommand\sankeynodeend{O{}m}{ + \begingroup + %\typeout{sankeynodestart: ##2} + \sankeyset{##1} + \sankeyset{node parameters,##2} + %\typeout{sankeynodestart: '\name' at(\pos) angle(\orient) qty(\qty)} + \sankeymakenode{} + %\typeout{sankeynodestart: sankeynode ok} + %\typeout{sankeynodestart: '\name' at(\pos) angle(\orient) qty(\qty)} + \sankeyfilldrawend + \endgroup + } + + + + \NewDocumentCommand\sankeyadvance{sO{}mm}{% *(reverse), options, name, distance + \begingroup + \sankeyset{##2} + \def\newname{##3} + \def\oldname{##3-old} + \sankeynodealias{\newname}{\oldname} + % \typeout{*** sankeyadvance: ok (\oldname => \newname)} + \IfBooleanTF{##1}{ + % reverse advance + \sankeynode{ + at={$(\oldname.center)!##4!90:(\oldname.left)$}, + angle=\sankeygetnodeorient{\oldname}, + quantity=\sankeygetnodeqty{\oldname}, + name=\newname, + } + \path[/sankey/fill] + (\newname.left) -- (\oldname.left) + -- + (\oldname.right) -- (\newname.right) + -- cycle; + \path[/sankey/draw] + (\newname.left) -- (\oldname.left) + (\oldname.right) -- (\newname.right); + \endgroup + }{ + % advance + \sankeynode{ + at={$(\oldname.center)!##4!-90:(\oldname.left)$}, + angle=\sankeygetnodeorient{\oldname}, + quantity=\sankeygetnodeqty{\oldname}, + name=\newname, + } + \path[/sankey/fill] + (\oldname.left) -- (\newname.left) + -- + (\newname.right) -- (\oldname.right) + -- cycle; + \path[/sankey/draw] + (\oldname.left) -- (\newname.left) + (\newname.right) -- (\oldname.right); + \endgroup + } + } + + \NewDocumentCommand\sankeyturn{sO{}mm}{% *(reverse), options, name, angle + \begingroup + \sankeyset{##2} + \def\name{##3} + \def\oldname{##3-old} + \sankeynodealias{\name}{\oldname} + \pgfmathsetmacro\qty{\sankeygetnodeqty{\oldname}} + \pgfmathsetmacro\oldangle{\sankeygetnodeorient{\oldname}} + \IfBooleanTF{##1}{ + % turn in reverse + \pgfmathsetmacro\orient{\oldangle-##4} + % \typeout{TURN: \oldangle=>\orient} + \ifnumgreater{##4}{0}{% anti-clockwise turn + \path let + \p1=(##3.left), \p2=(##3.right), + % center of rotation + \p{c}=($(\p1)!-\sankeyminradius!(\p2)$), + % starting position + \p{s}=(##3.center), + % ending position + \p{e}=($(\p{c})!1!-##4:(\p{s})$) + in \pgfextra{ + \begin{pgfinterruptpath} + % \typeout{acw turn:\qty:\orient} + \def\pos{\p{e}} + \sankeymakenode{} + \sankeyarc{\name}{\oldname} + \end{pgfinterruptpath} + }; + }{ % clockwise turn + \path let + \p1=(##3.right), \p2=(##3.left), + % center of rotation + \p{c}=($(\p1)!-\sankeyminradius!(\p2)$), + % starting position + \p{s}=(##3.center), + % ending position + \p{e}=($(\p{c})!1!-##4:(\p{s})$) + in \pgfextra{ + % \typeout{cw turn:\qty:\orient} + \begin{pgfinterruptpath} + \def\pos{\p{e}} + \sankeymakenode{} + \sankeyarc{\name}{\oldname} + \end{pgfinterruptpath} + }; + } + }{ + % turn + \pgfmathsetmacro\orient{\oldangle+##4} + % \typeout{TURN: \oldangle=>\orient} + \ifnumgreater{##4}{0}{% anti-clockwise turn + \path let + \p1=(##3.left), \p2=(##3.right), + % center of rotation + \p{c}=($(\p1)!-\sankeyminradius!(\p2)$), + % starting position + \p{s}=(##3.center), + % ending position + \p{e}=($(\p{c})!1!##4:(\p{s})$) + in \pgfextra{ + \begin{pgfinterruptpath} + % \typeout{acw turn:\qty:\orient} + \def\pos{\p{e}} + \sankeymakenode{} + \sankeyarc{\oldname}{\name} + \end{pgfinterruptpath} + }; + }{ % clockwise turn + \path let + \p1=(##3.right), \p2=(##3.left), + % center of rotation + \p{c}=($(\p1)!-\sankeyminradius!(\p2)$), + % starting position + \p{s}=(##3.center), + % ending position + \p{e}=($(\p{c})!1!##4:(\p{s})$) + in \pgfextra{ + % \typeout{cw turn:\qty:\orient} + \begin{pgfinterruptpath} + \def\pos{\p{e}} + \sankeymakenode{} + \sankeyarc{\oldname}{\name} + \end{pgfinterruptpath} + }; + } + } + \endgroup + } + + \NewDocumentCommand\sankeyfork{mm}{%name,list of forks + \def\listofforks{##2} + \xdef\sankeytot{0} + \xdef\sankeycalculus{0} + \pgfmathsetmacro\iqty{\sankeygetnodeqty{##1}} + \pgfmathsetmacro\iorient{\sankeygetnodeorient{##1}} + \pgfmathsetmacro\orient{\iorient} + %\typeout{iname:##1, iqty:\iqty,iorient:\iorient} + \foreach \qty/\name[count=\c] in \listofforks { + %\typeout{sankeyfork foreach: qty(\qty) name(\name) count(\c)} + \path + let + \p1=(##1.left),\p2=(##1.right), + \p{start}=($(\p1)!\fpeval{\sankeytot/\iqty}!(\p2)$), + \n{nexttot}={\fpeval{\sankeytot+\qty}}, + \p{end}=($(\p1)!\fpeval{\n{nexttot}/\iqty}!(\p2)$), + \p{mid}=($(\p{start})!.5!(\p{end})$) + in \pgfextra{ + %\typeout{new fork:\name} + %\typeout{qty:\qty,name:\name,count:\c,pos=\p{mid}} + \xdef\sankeytot{\n{nexttot}} + \xdef\sankeycalculus{\fpeval{\sankeycalculus+\qty}} + \begin{pgfinterruptpath} + \def\pos{\p{mid}} + \sankeymakenode{} + \end{pgfinterruptpath} + }; + } + \edef\diff{\fpeval{abs(\iqty-\sankeytot)}} + %\typeout{sankeyfork diff: \diff} + %\pgfmathsetmacro{\diff}{abs(\iqty-\sankeytot)} + %\pgfmathtruncatemacro{\finish}{\diff<0.01?1:0} + \ifnumequal{\diff}{0}{}{ + \PackageWarning{sankey}{^^J*** Warning: bad sankey fork: \iqty\space!=\space\sankeycalculus(=\sankeytot)^^J##2} + } + } + + \def\sankeyqtytolen##1{\fpeval{(##1)/\sankeytotalqty*\sankeytotallen}} + + \sankeyset{ + % % default values, + % /tikz/declare function={ + % % sankeyqtytolen(\qty)=\qty/\sankeytotalqty*\sankeytotallen; + % % sankeylentoqty(\len)=\len/\sankeytotallen*\sankeytotalqty; + % },% + default parameters,% + % user values + #1} + + %\typeout{sankeytotallen: \sankeytotallen} + + #2 % body of sankeydiagram environment +} + + +\sankeyset{ + % none style + new start style={none}{}{}, + new end style={none}{}{}, + % simple style + new start style={simple}{ + (\name.left) -- ([xshift=-.5\pgflinewidth]\name.left) -- ([xshift=-.5\pgflinewidth]\name.right) -- (\name.right) -- cycle + }{ + (\name.left) -- ([xshift=-.5\pgflinewidth]\name.left) -- ([xshift=-.5\pgflinewidth]\name.right) -- (\name.right) + }, + new end style={simple}{ + (\name.left) -- ([xshift=2mm]\name.center) -- (\name.right) -- cycle + }{ + (\name.left) -- ([xshift=2mm]\name.center) -- (\name.right) + }, + % arrow style + new start style={arrow}{ + (\name.left) -- ++(-10pt,0) + -- ([xshift=-10pt/6]\name.center) + -- ([xshift=-10pt]\name.right) + -- (\name.right) -- cycle + }{ + (\name.left) -- ++(-10pt,0) + -- ([xshift=-10pt/6]\name.center) + -- ([xshift=-10pt]\name.right) + -- (\name.right) + }, + new end style={arrow}{ + (\name.left) -- ([yshift=1mm]\name.left) + -- ([xshift=10pt]\name.center) + -- ([yshift=-1mm]\name.right) -- (\name.right) -- cycle + }{ + (\name.left) -- ([yshift=1mm]\name.left) + -- ([xshift=10pt]\name.center) + -- ([yshift=-1mm]\name.right) -- (\name.right) + }, +} \ No newline at end of file -- cgit v1.2.3