diff options
author | Karl Berry <karl@freefriends.org> | 2021-03-14 22:49:02 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2021-03-14 22:49:02 +0000 |
commit | 11cb80cef7eaad10df3ed09d6d5a50ad2c055881 (patch) | |
tree | d61dcff1e45fad3df683797a9fa17e91e2fb7bdd /Master/texmf-dist/tex/latex/sankey/sankey.sty | |
parent | fd1e514f7290c40fb1e5c37994e7756c4d73348d (diff) |
sankey (14mar21)
git-svn-id: svn://tug.org/texlive/trunk@58349 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/tex/latex/sankey/sankey.sty')
-rw-r--r-- | Master/texmf-dist/tex/latex/sankey/sankey.sty | 1201 |
1 files changed, 645 insertions, 556 deletions
diff --git a/Master/texmf-dist/tex/latex/sankey/sankey.sty b/Master/texmf-dist/tex/latex/sankey/sankey.sty index 4c28a1fe0d4..b6734004f77 100644 --- a/Master/texmf-dist/tex/latex/sankey/sankey.sty +++ b/Master/texmf-dist/tex/latex/sankey/sankey.sty @@ -1,722 +1,811 @@ -% 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] - +%% +%% This is file `sankey.sty', +%% generated with the docstrip utility. +%% +%% The original source files were: +%% +%% sankey.dtx (with options: `sankey') +%% +%% This is a generated file. +%% +%% Copyright (C) 2016, 2021 by Paul Gaborit +%% +%% This work may be distributed and/or modified under the conditions of the +%% LaTeX Project Public License, either version 1.3 of this license or (at +%% your option) any later version. The latest version of this license is +%% in +%% +%% <http://www.latex-project.org/lppl.txt> +%% +%% and version 1.3 or later is part of all distributions of LaTeX version +%% 2005/12/01 or later. +%% +%% This work has the LPPL maintenance status “maintained”. +%% +%% The Current Maintainer of this work is Paul Gaborit. +%% +%% This work consists of the files sankey.dtx, sankey.ins and the derived +%% files sankey.sty, tikzlibrarydubins.code.tex, sankey-doc-preamble.sty, +%% sankey-example1.tex, sankey-example2.tex, sankey-example3.tex, +%% sankey-example3-variation.tex, sankey-example4.tex, +%% sankey-example-energy.tex +%% +\NeedsTeXFormat{LaTeX2e}[2015/10/01] +\ProvidesPackage{sankey}[2021/03/14 v3.0 to draw Sankey diagrams] \RequirePackage{xparse} +\RequirePackage{etoolbox} \RequirePackage{xfp} \RequirePackage{tikz} -\usetikzlibrary{calc,decorations.markings,dubins} -\usepackage{etoolbox} - -% add a new layer to debug sankey diagrams +\usetikzlibrary{ + calc, + decorations.markings, + dubins +} +%% 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\snk@newfield#1#2#3#4#5{ + % setter + \def#2##1##2{\expandafter#1\csname #4\endcsname{##2}} + % getter + \def#3##1{% + \ifcsdef{#4}{% + \csname#4\endcsname% + }{% + \PackageError{sankey}{#5}{unknown key with \string#3}% + }% + } } - -\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'}% - }% +\snk@newfield\xdef\snk@setnodeqty\sankeygetnodeqty% +{@snk@node@qty@#1}{Unknown sankey node '#1'} +\snk@newfield\xdef\snk@setnodeorient\sankeygetnodeorient% +{@snk@node@orient@#1}{Unknown sankey node '#1'} +\snk@newfield\def\snk@setstartfill\snk@getstartfill% +{@snk@start@fill@#1}{Unknown sankey start fill path #1} + +\snk@newfield\def\snk@setstartdraw\snk@getstartdraw% +{@snk@start@draw@#1}{Unknown sankey start draw path #1} +\snk@newfield\def\snk@setendfill\snk@getendfill% +{@snk@end@fill@#1}{Unknown sankey end fill path #1} + +\snk@newfield\def\snk@setenddraw\snk@getenddraw% +{@snk@end@draw@#1}{Unknown sankey end draw path #1} + +\newcommand\snk@ifnodedefined[3]{% + \ifcsdef{@snk@node@qty@#1}{#2}{#3}% +} +\newcommand\snk@errorifnotdefined[1]{% + \snk@ifnodedefined{#1}{}% + {\PackageError{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] + \inheritanchorborder[from=rectangle] \anchor{left}{\pgf@process{\northeast}} \anchor{right}{\pgf@process{\southwest}} + % compatibility with 'fit' library + \inheritanchor[from=rectangle]{west} + \inheritanchor[from=rectangle]{east} + \inheritanchor[from=rectangle]{north} + \inheritanchor[from=rectangle]{south} + \inheritanchor[from=rectangle]{north west} + \inheritanchor[from=rectangle]{south east} + \inheritanchor[from=rectangle]{north east} + \inheritanchor[from=rectangle]{south west} } - -% pgfkeys family 'sankey' \pgfkeys{/sankey/.is family} - \NewDocumentCommand\sankeyset{m}{\pgfkeys{sankey,#1}} - \sankeyset{ + ratio quantity/.code={\edef\snk@totalqty{\fpeval{#1}}}, + ratio quantity/.value required, ratio length/.code={ - \pgfmathsetmacro\sankeytotallen{#1} - \edef\sankeytotallen{\sankeytotallen pt} + \pgfmathsetmacro\snk@totallen{#1} + \edef\snk@totallen{\snk@totallen 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, +} +\sankeyset{ + rotate/.code={\edef\snk@rotate{\fpeval{#1}}}, + rotate/.value required, +} +\sankeyset{ + minimum radius/.code={ + \pgfmathsetmacro\snk@minradius{#1} + \edef\snk@minradius{\snk@minradius pt} + }, minimum radius/.value required, - outin steps/.estore in=\sankeystepoutin, +} +\sankeyset{ + outin steps/.estore in=\snk@stepoutin, outon steps/.value required, - % +} +\newtoggle{sankey debug} +\sankeyset{ debug/.is choice, debug/true/.code={\toggletrue{sankey debug}}, debug/false/.code={\togglefalse{sankey debug}}, debug/.default=true, - % +} +\sankeyset{ start style/.is choice, - % end style/.is choice, - % default values - default parameters/.style={ - % default values + % to define new start and end styles + new start style/.code n args={3}{% name, fill path, draw path + \snk@setstartfill{#1}{#2} + \snk@setstartdraw{#1}{#3} + \sankeyset{start style/#1/.code={\def\snk@startstyle{#1}}} + }, + new end style/.code n args={3}{% name, fill path, draw path + \snk@setendfill{#1}{#2} + \snk@setenddraw{#1}{#3} + \sankeyset{end style/#1/.code={\def\snk@endstyle{#1}}} + }, +} +\sankeyset{ + debug color/.style={/utils/exec={\colorlet{debug color}{#1}}}, + @initial options/.style={ 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}% + rotate=0, + % default fill/draw styles, + fill/.style={line width=0pt,fill=white}, + draw/.style={draw=black,line width=.4pt}, + % debug color used by all debug macros + debug color=red!75!black, + % debug line between left and right anchors + debug line/.style={overlay,draw=debug color,|-|}, + % debug line between center and label + debug normal/.style={overlay,draw=debug color}, + % debug node label + debug label/.style={ + overlay, + draw, + font=\ttfamily\tiny, + text=debug color,text opacity=1, + inner sep=.1em, + fill=white,fill opacity=1, + rounded corners=.1em, + node contents={\name}, }, - 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, + every node/.style={}, }, + every diagram/.style={}, } -% 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={}, +\sankeyset{node parameters/.is family} +\sankeyset{node parameters, + name/.estore in=\name, + name/.value required, + quantity/.code={\edef\qty{\fpeval{#1}}}, + quantity/.value required, + angle/.code={\edef\orient{\snk@normalize@angle{#1}}}, + angle/.value required, + at/.code={\snk@getpos\pos{#1}}, + at/.value required, +} +\sankeyset{node parameters, + as/.style={ + name=#1, + quantity=\sankeygetnodeqty{#1}, + angle=\sankeygetnodeorient{#1}, + at={#1.center}, }, - 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, + as/.value required, +} +\sankeyset{node parameters, + anchor/.is choice, + anchor/left/.code={\def\snk@anchor{left}}, + anchor/right/.code={\def\snk@anchor{right}}, + anchor/center/.code={\def\snk@anchor{center}}, + anchor/.value required, +} +\newtoggle{sankey node start} +\newtoggle{sankey node end} +\sankeyset{node parameters, + start/.is choice, + start/true/.code={\toggletrue{sankey node start}}, + start/false/.code={\togglefalse{sankey node start}}, + start/.default=true, + % + end/.is choice, + end/true/.code={\toggletrue{sankey node end}}, + end/false/.code={\togglefalse{sankey node end}}, + end/.default=true, +} +\sankeyset{node parameters, + forked/.estore in=\snk@listofforks, + forked/.value required, + fork anchor/.estore in=\snk@forkanchor, + fork anchor/.value required, +} +\sankeyset{node parameters, + @initial parameters/.style={ + start=false, + end=false, + forked=, + fork anchor=, + anchor=center, + at={0,0}, + angle=0, }, } - \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}}} + % sankey node TikZ style + @sankey node/.style n args={3}{% name, pos, anchor + shape=sankey node, + inner sep=0, + minimum height={\sankeyqtytolen{\sankeygetnodeqty{#1}}}, + minimum width=0, + draw=none, + line width=0pt, + fill=none, + node contents={}, + rotate=\sankeygetnodeorient{#1}+\snk@rotate, + at={(#2)}, + name=#1, + anchor=#3, }, } - -% 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 +\NewDocumentEnvironment{sankeydiagram}{O{}}{ + \def\snk@getpos##1##2{% + \tikz@scan@one@point\pgfutil@firstofone(##2)\relax% + \edef##1{\the\pgf@x,\the\pgf@y}% } - - \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 + \def\snk@modulo##1##2{\fpeval{##1-(floor((##1)/(##2),0)*##2)}} + \def\snk@normalize@angle##1{% + \fpeval{\snk@modulo{(##1)+180}{360}-180}% } - - \newcommand\sankeymakenode{ - \begingroup - \sankeymakenodenodebug{} + \def\snk@show@debug##1{% node name \iftoggle{sankey debug}{ + \begingroup + \edef\name{##1} + \edef\qty{\sankeygetnodeqty{\name}} + \edef\orient{\sankeygetnodeorient{\name}} \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} + \pgfmathsetmacro{\snk@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}}; + -- ($(\name.center)!\snk@len pt!90:(\name.right)$) + node[/sankey/debug label,rotate=\orient+90+\snk@rotate,anchor=north]; \end{pgfonlayer} + \endgroup }{} + } + \def\snk@makeforkednode{ + \begingroup + \ifdefempty{\snk@listofforks}{ + \ifdefempty{\snk@forkanchor}{}{ + \PackageWarning{sankey}% + {Can't use 'fork anchor' key without 'forked' key} + } + }{ + \def\snk@tot{0} + \def\snk@added@values{} + \sankeyset{ + @add forked node/.code args={####1/####2}{ + \coordinate (####2) at ($(\name.left)% + !\fpeval{(\snk@tot+.5*(####1))/\qty}% + !(\name.right)$); + \edef\snk@orient{\orient} + \sankeynode[debug=false] + {name=####2,quantity=####1,at=####2,angle=\snk@orient} + \edef\snk@tot{\fpeval{\snk@tot+####1}} + \edef\snk@added@values{\snk@added@values+####1} + }, + @add forked node/.list/.expand once=\snk@listofforks, + } + \edef\snk@diff{\fpeval{abs(\qty-\snk@tot)}} + \ifnumequal{\snk@diff}{0}{}{ + \PackageWarning{sankey}% + {^^J*** Warning: bad sankey fork: % + \qty\space!=\space\snk@added@values(=\snk@tot)% + ^^J\snk@listofforks} + } + \ifdefempty{\snk@forkanchor}{ + \edef\snk@forkanchor{\name.\snk@anchor} + }{} + \snk@getpos\snk@c{$(\snk@forkanchor) - (\pos)$} + \sankeynode{as=\name,at={$(\name) - (\snk@c)$}} + \foreach \snk@subnodeqty/\snk@subnodename in \snk@listofforks { + \sankeynode{as=\snk@subnodename,at={$(\snk@subnodename) - (\snk@c)$}} + } + } \endgroup } - - \newcommand\sankeynodenodebug[1]{ + \def\snk@makenode{ \begingroup - \sankeyset{node parameters,##1} - %\typeout{sankeynodenodebug: '\name' at(\pos) angle(\orient) qty(\qty)} - \sankeymakenodenodebug{} + \snk@setnodeqty{\name}{\qty} + \edef\orient{\snk@normalize@angle{\orient}} + \snk@setnodeorient{\name}{\orient} + \ifundef{\snk@anchor}{\def\snk@anchor{center}}{} + \node[/sankey/@sankey node={\name}{\pos}{\snk@anchor}]; \endgroup } - - % ============================================================ - % user commands - % ============================================================ - - \NewDocumentCommand\sankeydubins{O{}mm}{% options, sn, en + \def\snk@filldrawstart{ + \begin{scope}[shift={(\name)},rotate=\orient] + \path[/sankey/fill] \snk@getstartfill{\snk@startstyle}; + \path[/sankey/draw] \snk@getstartdraw{\snk@startstyle}; + \end{scope} + } + \def\snk@filldrawend{ + \begin{scope}[shift={(\name)},rotate=\sankeygetnodeorient{\name}] + \path[/sankey/fill] \snk@getendfill{\snk@endstyle}; + \path[/sankey/draw] \snk@getenddraw{\snk@endstyle}; + \end{scope} + } + \def\snk@checkquantities##1##2##3{ \begingroup - \sankeyset{##1} - \pgfmathsetmacro\startangle{\sankeygetnodeorient{##2}} - \pgfmathsetmacro\endangle{\sankeygetnodeorient{##3}} - \edef\sqty{\sankeygetnodeqty{##2}} - \edef\eqty{\sankeygetnodeqty{##3}} - \ifdefstrequal{\sqty}{\eqty}{}{ + \edef\snk@qtyi{\sankeygetnodeqty{##1}} + \edef\snk@qtyii{\sankeygetnodeqty{##2}} + \ifdefstrequal{\snk@qtyi}{\snk@qtyii}{}{ \PackageError{sankey}% - {^^J*** \string\sankeyoutin: quantities differ between ##2 (\sqty) and ##3 (\eqty)^^J}% + {^^J*** \string##3: quantities differ between % + ##1 (\snk@qtyi) and ##2 (\snk@qtyii)% + ^^J}% {The quantities of the two Sankey nodes must be equal.} } + \endgroup + } + \NewDocumentCommand\sankeydubins{O{}mm}{% options, sn, en + \snk@errorifnotdefined{##2} + \snk@errorifnotdefined{##3} + \snk@checkquantities{##2}{##3}{\sankeydubins} + \begingroup + \sankeyset{##1} \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, + start point=##2.center,start angle=\sankeygetnodeorient{##2}, + end point=##3.center,end angle=\sankeygetnodeorient{##3}, + minimum radius=\snk@minradius + .5 * \sankeyqtytolen{\qty} pt, }, } \dubinspathcalc{sankey,store=sankey} - \dubinspathset{ left border/.style={ sankey, use store=sankey, - left and right minimum radii= - {\sankeyminradius} and {\sankeyminradius + \width pt}, + left and right minimum radii={\snk@minradius} + and {\snk@minradius + \sankeyqtytolen{\qty} pt}, }, right border/.style={ sankey, use store=sankey, left and right minimum radii= - {\sankeyminradius + \width pt} and {\sankeyminradius}, + {\snk@minradius + \sankeyqtytolen{\qty} pt} + and {\snk@minradius}, }, } - - \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} - }; + % fill the region + \path[/sankey/fill] (##2.left) \dubinspath{left border} + -- (##3.left) -- (##3.right) \dubinspath{right border,reverse} + -- (##2.right) -- cycle; + % draw left and right borders + \path[/sankey/draw] (##2.left) \dubinspath{left border} + (##2.right) \dubinspath{right border}; \endgroup } - - \NewDocumentCommand\sankeyoutin{O{}mm}{% options, sn, en + \snk@errorifnotdefined{##2} + \snk@errorifnotdefined{##3} + \snk@checkquantities{##2}{##3}{\sankeyoutin} \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={ + \edef\qty{\sankeygetnodeqty{##2}} + \pgfmathsetmacro\snk@len{\sankeyqtytolen{\qty}/2} + \edef\snk@step{\fpeval{1/\snk@stepoutin}} + \edef\snk@laststep{\inteval{\snk@stepoutin-1}} + \path[overlay,decorate,decoration={ markings, - mark=between positions \mystep and \mybound step \mystep with { - \def\sankeyoutinmidptname{sankeyoutinmidpt \pgfkeysvalueof{/pgf/decoration/mark info/sequence number}} + mark=between positions \snk@step and {\fpeval{1-.5*\snk@step}} + step \snk@step with { + \edef\snk@outinmidptname{% + snk@outinmidpt-% + \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) + (0,0) coordinate(\snk@outinmidptname) + (0,-\snk@len pt) coordinate (\snk@outinmidptname-r) + (0,\snk@len pt) coordinate (\snk@outinmidptname-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} + }] + (##2.center) + to[out=\sankeygetnodeorient{##2},in=\sankeygetnodeorient{##3}+180] + (##3.center); + \foreach \snk@ptnum in {1,...,\snk@laststep}{ + \edef\snk@outinmidptname{snk@outinmidpt-\snk@ptnum} + \dbp@anglebetween\snk@outinmidptangle% + {\snk@outinmidptname-r}{\snk@outinmidptname-l} + \sankeynode[debug=false]{% + name=\snk@outinmidptname, + quantity=\qty, + angle=\snk@outinmidptangle-90,% + at=\snk@outinmidptname% + } + } + \sankeynode[debug=false]{% + name={snk@outinmidpt-0}, + quantity=\sankeygetnodeqty{##2}, + angle=\sankeygetnodeorient{##2},% + at={##2}% + } + \sankeynode[debug=false]{% + name={snk@outinmidpt-\snk@stepoutin}, + quantity={\sankeygetnodeqty{##3}}, + angle={\sankeygetnodeorient{##3}},% + at={##3}% } - \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) + (snk@outinmidpt-0.left) + \foreach \snk@curpt + [remember=\snk@curpt as \snk@prevpt (initially 0)] + in {1,...,\snk@stepoutin}{ + to[out=\sankeygetnodeorient{snk@outinmidpt-\snk@prevpt}, + in=\sankeygetnodeorient{snk@outinmidpt-\snk@curpt}+180] + (snk@outinmidpt-\snk@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) + (snk@outinmidpt-\snk@stepoutin.right) + \foreach \snk@curpt + [remember=\snk@curpt as \snk@prevpt (initially \snk@stepoutin)] + in {\snk@laststep,...,0}{ + to[out=\sankeygetnodeorient{snk@outinmidpt-\snk@prevpt}+180, + in=\sankeygetnodeorient{snk@outinmidpt-\snk@curpt}] + (snk@outinmidpt-\snk@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) + (snk@outinmidpt-0.left) + \foreach \snk@curpt + [remember=\snk@curpt as \snk@prevpt (initially 0)] + in {1,...,\snk@stepoutin}{ + to[out=\sankeygetnodeorient{snk@outinmidpt-\snk@prevpt}, + in=\sankeygetnodeorient{snk@outinmidpt-\snk@curpt}+180] + (snk@outinmidpt-\snk@curpt.left) } - ; - + (snk@outinmidpt-\snk@stepoutin.right) + \foreach \snk@curpt + [remember=\snk@curpt as \snk@prevpt (initially \snk@stepoutin)] + in {\snk@laststep,...,0}{ + to[out=\sankeygetnodeorient{snk@outinmidpt-\snk@prevpt}+180, + in=\sankeygetnodeorient{snk@outinmidpt-\snk@curpt}] + (snk@outinmidpt-\snk@curpt.right) + }; \endgroup } - \NewDocumentCommand\sankeynodealias{mm}{%name, alias - \path [late options={name=##1,alias=##2}]; - \sankeysetnodeqty{##2}{\sankeygetnodeqty{##1}} - \sankeysetnodeorient{##2}{\sankeygetnodeorient{##1}} + \snk@errorifnotdefined{##1} + \path[late options={name=##1,alias=##2}]; + \snk@setnodeqty{##2}{\sankeygetnodeqty{##1}} + \snk@setnodeorient{##2}{\sankeygetnodeorient{##1}} } - - \newcommand\sankeynode[2][]{ + \NewDocumentCommand\sankeynode{O{}m}{% options, node parameters \begingroup \sankeyset{##1} - \sankeyset{node parameters,##2} - %\typeout{sankeynode: '\name' at(\pos) angle(\orient) qty(\qty)} - \sankeymakenode{} + \sankeyset{node parameters,@initial parameters,/sankey/every node,##2} + \snk@makenode{} + \snk@makeforkednode{} + \iftoggle{sankey node start}{\snk@filldrawstart}{} + \iftoggle{sankey node end}{\snk@filldrawend}{} + \snk@show@debug{\name} \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}{ + \NewDocumentCommand\sankeystart{O{}m}{% options, name + \snk@errorifnotdefined{##2} \begingroup \sankeyset{##1} \edef\name{##2} \edef\orient{\sankeygetnodeorient{##2}} \edef\qty{\sankeygetnodeqty{##2}} - \sankeyfilldrawstart + \snk@filldrawstart \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 + \NewDocumentCommand\sankeynodestart{O{}m}{% option, node parameters + \sankeynode[##1]{start,##2} } - - \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 + \NewDocumentCommand\sankeyend{O{}m}{%options, name + \snk@errorifnotdefined{##2} \begingroup \sankeyset{##1} \edef\name{##2} \edef\orient{\sankeygetnodeorient{##2}} \edef\qty{\sankeygetnodeqty{##2}} - \sankeyfilldrawend + \snk@filldrawend \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\sankeynodeend{O{}m}{% options, node parameters + \sankeynode[##1]{end,##2} } - - - - \NewDocumentCommand\sankeyadvance{sO{}mm}{% *(reverse), options, name, distance + \NewDocumentCommand\sankeyadvance{sO{}mm}{% + %params: *(reverse), options, name, distance + \snk@errorifnotdefined{##3} \begingroup \sankeyset{##2} - \def\newname{##3} - \def\oldname{##3-old} - \sankeynodealias{\newname}{\oldname} - % \typeout{*** sankeyadvance: ok (\oldname => \newname)} + \edef\name{##3} + \edef\snk@oldname{##3-old} + \sankeynodealias{\name}{\snk@oldname} \IfBooleanTF{##1}{ - % reverse advance + % move backward \sankeynode{ - at={$(\oldname.center)!##4!90:(\oldname.left)$}, - angle=\sankeygetnodeorient{\oldname}, - quantity=\sankeygetnodeqty{\oldname}, - name=\newname, + at={$(\snk@oldname.center)!##4!90:(\snk@oldname.left)$}, + angle=\sankeygetnodeorient{\snk@oldname}, + quantity=\sankeygetnodeqty{\snk@oldname}, + name=\name, } \path[/sankey/fill] - (\newname.left) -- (\oldname.left) - -- - (\oldname.right) -- (\newname.right) - -- cycle; + (\name.left) -- (\snk@oldname.left) + -- (\snk@oldname.right) -- (\name.right) -- cycle; \path[/sankey/draw] - (\newname.left) -- (\oldname.left) - (\oldname.right) -- (\newname.right); - \endgroup + (\name.left) -- (\snk@oldname.left) + (\snk@oldname.right) -- (\name.right); }{ - % advance + % move forward \sankeynode{ - at={$(\oldname.center)!##4!-90:(\oldname.left)$}, - angle=\sankeygetnodeorient{\oldname}, - quantity=\sankeygetnodeqty{\oldname}, - name=\newname, + at={$(\snk@oldname.center)!##4!-90:(\snk@oldname.left)$}, + angle=\sankeygetnodeorient{\snk@oldname}, + quantity=\sankeygetnodeqty{\snk@oldname}, + name=\name, } \path[/sankey/fill] - (\oldname.left) -- (\newname.left) - -- - (\newname.right) -- (\oldname.right) - -- cycle; + (\snk@oldname.left) -- (\name.left) + -- (\name.right) -- (\snk@oldname.right) -- cycle; \path[/sankey/draw] - (\oldname.left) -- (\newname.left) - (\newname.right) -- (\oldname.right); - \endgroup + (\snk@oldname.left) -- (\name.left) + (\name.right) -- (\snk@oldname.right); } + \snk@show@debug{\name} + \endgroup } - - \NewDocumentCommand\sankeyturn{sO{}mm}{% *(reverse), options, name, angle + \NewDocumentCommand\sankeyturnright{sO{}mm}{ + % *(reverse), options, name, angle + \snk@errorifnotdefined{##3} \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} - }; - } + \IfBooleanTF{##1} + {\edef\snk@angle{\fpeval{-1*##4}}} + {\edef\snk@angle{\fpeval{1*##4}}} + \edef\snk@anglesign{\fpeval{sign(\snk@angle)}} + \ifnumgreater{\snk@anglesign}{-1}{ + \sankeyset{##2} + \edef\name{##3} + \edef\snk@oldname{##3-old} + \sankeynodealias{\name}{\snk@oldname} + \edef\qty{\sankeygetnodeqty{\name}} + \edef\orient{\snk@normalize@angle{\sankeygetnodeorient{\name}-\snk@angle}} + \snk@getpos\snk@c{$(\name.right)!-\snk@minradius!(\name.left)$} + \snk@getpos\pos{$(\snk@c)!1!-\snk@angle:(\name.center)$} + \snk@makenode{} + % fill the region + \path[/sankey/fill] let + \p1=(\snk@oldname.left),\p2=(\snk@oldname.right), + \p3=(\name.left),\p4=(\name.right), + \n1={\sankeyqtytolen{\qty}}, + \n{maxr}={\snk@minradius+\n1}, + \n{minr}={\snk@minradius} + in + (\p1) arc(\orient+\snk@angle+90:\orient+90:\n{maxr}) -- (\p3) -- + (\p4) arc(\orient+90:\orient+\snk@angle+90:\n{minr}) -- (\p2) -- cycle; + % draw left and right borders + \path[/sankey/draw] let + \p1=(\snk@oldname.left),\p2=(\snk@oldname.right), + \p3=(\name.left),\p4=(\name.right), + \n1={\sankeyqtytolen{\qty}}, + \n{maxr}={\snk@minradius+\n1}, + \n{minr}={\snk@minradius} + in + (\p1) arc(\orient+\snk@angle+90:\orient+90:\n{maxr}) + (\p4) arc(\orient+90:\orient+\snk@angle+90:\n{minr}); + \snk@show@debug{\name} }{ - % 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} - }; - } + \sankeyturnrightbackward[##2]{##3}{-1*\snk@angle} } \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} - }; + \NewDocumentCommand\sankeyturnrightbackward{sO{}mm}{ + % *(reverse), options, name, angle + \snk@errorifnotdefined{##3} + \begingroup + \IfBooleanTF{##1} + {\edef\snk@angle{\fpeval{-1*##4}}} + {\edef\snk@angle{\fpeval{1*##4}}} + \edef\snk@anglesign{\fpeval{sign(\snk@angle)}} + \ifnumgreater{\snk@anglesign}{-1}{ + \sankeyset{##2} + \edef\name{##3} + \edef\snk@oldname{##3-old} + \sankeynodealias{\name}{\snk@oldname} + \edef\qty{\sankeygetnodeqty{\name}} + \edef\orient{\snk@normalize@angle{\sankeygetnodeorient{\name}+\snk@angle}} + \snk@getpos\snk@c{$(\name.right)!-\snk@minradius!(\name.left)$} + \snk@getpos\pos{$(\snk@c)!1!\snk@angle:(\name.center)$} + \snk@makenode{} + % fill the region + \path[/sankey/fill] let + \p1=(\name.left),\p2=(\name.right), + \p3=(\snk@oldname.left),\p4=(\snk@oldname.right), + \n1={\sankeyqtytolen{\qty}}, + \n{maxr}={\snk@minradius+\n1}, \n{minr}={\snk@minradius} + in + (\p1) arc(\orient+90:\orient-\snk@angle+90:\n{maxr}) -- (\p3) -- + (\p4) arc(\orient-\snk@angle+90:\orient+90:\n{minr}) -- (\p2) -- cycle; + % draw left and right borders + \path[/sankey/draw] let + \p1=(\name.left),\p2=(\name.right), + \p3=(\snk@oldname.left),\p4=(\snk@oldname.right), + \n1={\sankeyqtytolen{\qty}}, + \n{maxr}={\snk@minradius+\n1}, \n{minr}={\snk@minradius} + in + (\p1) arc(\orient+90:\orient-\snk@angle+90:\n{maxr}) + (\p4) arc(\orient-\snk@angle+90:\orient+90:\n{minr}); + \snk@show@debug{\name} + }{ + \sankeyturnright[##2]{##3}{-1*\snk@angle} } - \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} + \endgroup + } + \NewDocumentCommand\sankeyturnleft{sO{}mm}{ + % *(reverse), options, name, angle + \snk@errorifnotdefined{##3} + \begingroup + \IfBooleanTF{##1} + {\edef\snk@angle{\fpeval{-1*##4}}} + {\edef\snk@angle{\fpeval{1*##4}}} + \edef\snk@anglesign{\fpeval{sign(\snk@angle)}} + \ifnumgreater{\snk@anglesign}{-1}{ + \sankeyset{##2} + \edef\name{##3} + \edef\snk@oldname{##3-old} + \sankeynodealias{\name}{\snk@oldname} + \edef\qty{\sankeygetnodeqty{\name}} + \edef\orient{\snk@normalize@angle{\sankeygetnodeorient{\name}+\snk@angle}} + \snk@getpos\snk@c{$(\name.left)!-\snk@minradius!(\name.right)$} + \snk@getpos\pos{$(\snk@c)!1!\snk@angle:(\name.center)$} + \snk@makenode{} + % fill the region + \path[/sankey/fill] let + \p1=(\snk@oldname.left),\p2=(\snk@oldname.right), + \p3=(\name.left),\p4=(\name.right), + \n1={\sankeyqtytolen{\qty}}, + \n{maxr}={\snk@minradius+\n1}, \n{minr}={\snk@minradius} + in + (\p1) arc(\orient-\snk@angle-90:\orient-90:\n{minr}) -- (\p3) -- + (\p4) arc(\orient-90:\orient-\snk@angle-90:\n{maxr}) -- (\p2) -- cycle; + % draw left and right borders + \path[/sankey/draw] let + \p1=(\snk@oldname.left),\p2=(\snk@oldname.right), + \p3=(\name.left),\p4=(\name.right), + \n1={\sankeyqtytolen{\qty}}, + \n{maxr}={\snk@minradius+\n1}, \n{minr}={\snk@minradius} + in + (\p1) arc(\orient-\snk@angle-90:\orient-90:\n{minr}) + (\p4) arc(\orient-90:\orient-\snk@angle-90:\n{maxr}); + \snk@show@debug{\name} + }{ + \sankeyturnleftbackward[##2]{##3}{-1*\snk@angle} } + \endgroup } - - \def\sankeyqtytolen##1{\fpeval{(##1)/\sankeytotalqty*\sankeytotallen}} - + \NewDocumentCommand\sankeyturnleftbackward{sO{}mm}{ + % *(reverse), options, name, angle + \snk@errorifnotdefined{##3} + \begingroup + \IfBooleanTF{##1} + {\edef\snk@angle{\fpeval{-1*##4}}} + {\edef\snk@angle{\fpeval{1*##4}}} + \edef\snk@anglesign{\fpeval{sign(\snk@angle)}} + \ifnumgreater{\snk@anglesign}{-1}{ + \sankeyset{##2} + \edef\name{##3} + \edef\snk@oldname{##3-old} + \sankeynodealias{\name}{\snk@oldname} + \edef\qty{\sankeygetnodeqty{\name}} + \edef\orient{\snk@normalize@angle{\sankeygetnodeorient{\name}-\snk@angle}} + \snk@getpos\snk@c{$(\name.left)!-\snk@minradius!(\name.right)$} + \snk@getpos\pos{$(\snk@c)!1!-\snk@angle:(\name.center)$} + \snk@makenode{} + % fill the region + \path[/sankey/fill] let + \p1=(\name.left),\p2=(\name.right), + \p3=(\snk@oldname.left),\p4=(\snk@oldname.right), + \n1={\sankeyqtytolen{\qty}}, + \n{maxr}={\snk@minradius+\n1}, \n{minr}={\snk@minradius} + in + (\p1) arc(\orient-90:\orient+\snk@angle-90:\n{minr}) -- (\p3) -- + (\p4) arc(\orient+\snk@angle-90:\orient-90:\n{maxr}) -- (\p2) -- cycle; + % draw left and right borders + \path[/sankey/draw] let + \p1=(\name.left),\p2=(\name.right), + \p3=(\snk@oldname.left),\p4=(\snk@oldname.right), + \n1={\sankeyqtytolen{\qty}}, + \n{maxr}={\snk@minradius+\n1}, \n{minr}={\snk@minradius} + in + (\p1) arc(\orient-90:\orient+\snk@angle-90:\n{minr}) + (\p4) arc(\orient+\snk@angle-90:\orient-90:\n{maxr}); + \snk@show@debug{\name} + }{ + \sankeyturnleft[##2]{##3}{-1*\snk@angle} + } + \endgroup + } + \NewDocumentCommand\sankeyturn{sO{}mm}{% + % *(reverse), options, name, angle + \snk@errorifnotdefined{##3} + \begingroup + \edef\snk@anglesign{\fpeval{sign(##4)}} + \IfBooleanTF{##1}{ + \ifnumgreater{\snk@anglesign}{-1} + {\sankeyturnleftbackward[##2]{##3}{##4}} + {\sankeyturnrightbackward[##2]{##3}{-1*##4}} + }{ + \ifnumgreater{\snk@anglesign}{-1} + {\sankeyturnleft[##2]{##3}{##4}} + {\sankeyturnright[##2]{##3}{-1*##4}} + } + \endgroup + } + \NewDocumentCommand\sankeyfork{O{}mm}{%options, name, list of forks + \snk@errorifnotdefined{##2} + \begingroup + \sankeyset{##1} + \sankeynode[debug=false]{as={##2},forked={##3}} + \foreach \qty/\snk@subnodename in {##3}{\sankeynode{as={\snk@subnodename}}} + \endgroup + } + \def\sankeyqtytolen##1{\fpeval{(##1)/\snk@totalqty*\snk@totallen}} \sankeyset{ - % % default values, - % /tikz/declare function={ - % % sankeyqtytolen(\qty)=\qty/\sankeytotalqty*\sankeytotallen; - % % sankeylentoqty(\len)=\len/\sankeytotallen*\sankeytotalqty; - % },% - default parameters,% + @initial options, + every diagram, % user values #1} - - %\typeout{sankeytotallen: \sankeytotallen} - - #2 % body of sankeydiagram environment } - - +{} % empty but mandatory ! :-) \sankeyset{ - % none style new start style={none}{}{}, new end style={none}{}{}, - % simple style +} +\sankeyset{ 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) -- cycle }{ - (\name.left) -- ([xshift=-.5\pgflinewidth]\name.left) -- ([xshift=-.5\pgflinewidth]\name.right) -- (\name.right) + (\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) -- cycle }{ - (\name.left) -- ([xshift=2mm]\name.center) -- (\name.right) + (\name.left) -- ([xshift=2mm]\name.center) -- (\name.right) }, - % arrow style +} +\sankeyset{ 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) -- cycle }{ - (\name.left) -- ++(-10pt,0) - -- ([xshift=-10pt/6]\name.center) - -- ([xshift=-10pt]\name.right) - -- (\name.right) + (\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 + -- ([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) + -- ([xshift=10pt]\name.center) -- ([yshift=-1mm]\name.right) + -- (\name.right) }, -}
\ No newline at end of file +} +\endinput +%% +%% End of file `sankey.sty'. |