You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
cours-m1-eea/414-Energie_Renouvelable/Cours/sankey.tex

253 lines
6.8 KiB
TeX

\usetikzlibrary{calc}
\usepackage{etoolbox}
\pgfdeclarelayer{background}
\pgfdeclarelayer{foreground}
\pgfdeclarelayer{sankeydebug}
\pgfsetlayers{background,main,foreground,sankeydebug}
\newif\ifsankeydebug
\newenvironment{sankeydiagram}[1][]{
\def\sankeyflow##1##2{% sn, en
\path[sankey fill]
let
\p1=(##1.north east),\p2=(##1.south east),
\n1={atan2(\y1-\y2,\x1-\x2)-90},
\p3=(##2.north west),\p4=(##2.south west),
\n2={atan2(\y3-\y4,\x3-\x4)+90}
in
(\p1) to[out=\n1,in=\n2] (\p3) --
(\p4) to[in=\n1,out=\n2] (\p2) -- cycle;
\draw[sankey draw]
let
\p1=(##1.north east),\p2=(##1.south east),
\n1={atan2(\y1-\y2,\x1-\x2)-90},
\p3=(##2.north west),\p4=(##2.south west),
\n2={atan2(\y3-\y4,\x3-\x4)+90}
in
(\p1) to[out=\n1,in=\n2] (\p3)
(\p4) to[in=\n1,out=\n2] (\p2);
}
\tikzset{
sankey tot length/.store in=\sankeytotallen,
sankey tot quantity/.store in=\sankeytotalqty,
sankey min radius/.store in=\sankeyminradius,
sankey arrow length/.store in=\sankeyarrowlen,
sankey debug/.is if=sankeydebug,
sankey debug=false,
sankey flow/.style={
to path={
\pgfextra{
\pgfinterruptpath
\edef\sankeystart{\tikztostart}
\edef\sankeytarget{\tikztotarget}
\sankeyflow{\sankeystart}{\sankeytarget}
\endpgfinterruptpath
}
},
},
sankey node/.style={
inner sep=0,minimum height={sankeyqtytolen(##1)},
minimum width=0,draw=none,line width=0pt,
},
% sankey angle
sankey angle/.store in=\sankeyangle,
% sankey default styles
sankey fill/.style={line width=0pt,fill,white},
sankey draw/.style={draw=black,line width=.4pt},
}
\newcommand\sankeynode[4]{%prop,orientation,name,pos
\node[sankey node=##1,rotate=##2] (##3) at (##4) {};
\ifsankeydebug
\begin{pgfonlayer}{sankeydebug}
\draw[red,|-|] (##3.north west) -- (##3.south west);
\pgfmathsetmacro{\len}{sankeyqtytolen(##1)/3}
\draw[red] (##3.west)
-- ($(##3.west)!\len pt!90:(##3.south west)$)
node[font=\tiny,text=black] {##3};
\end{pgfonlayer}
\fi
}
\newcommand\sankeynodestart[4]{%prop,orientation,name,pos
\sankeynode{##1}{##2}{##3}{##4}
\begin{scope}[shift={(##3)},rotate=##2]
\path[sankey fill]
(##3.north west) -- ++(-\sankeyarrowlen,0)
-- ([xshift=-\sankeyarrowlen/6]##3.west)
-- ([xshift=-\sankeyarrowlen]##3.south west)
-- (##3.south west) -- cycle;
\path[sankey draw]
(##3.north west) -- ++(-\sankeyarrowlen,0)
-- ([xshift=-\sankeyarrowlen/6]##3.west)
-- ([xshift=-\sankeyarrowlen]##3.south west)
-- (##3.south west);
\end{scope}
}
\newcommand\sankeynodeend[4]{%prop,orientation,name,pos
\sankeynode{##1}{##2}{##3}{##4}
\begin{scope}[shift={(##3)},rotate=##2]
\path[sankey fill]
(##3.north east)
-- ([xshift=\sankeyarrowlen]##3.east)
-- (##3.south west) -- cycle;
\path[sankey draw]
(##3.north east)
-- ([xshift=\sankeyarrowlen]##3.east)
-- (##3.south west);
\end{scope}
}
\newcommand\sankeyadvance[3][]{%newname,name,distance
\edef\name{##2}
\ifstrempty{##1}{
\def\newname{##2}
\edef\name{##2-old}
\path [late options={name=##2,alias=\name}];
}{
\def\newname{##1}
}
\path
let
% sankey node angle
\p1=(##2.north east),
\p2=(##2.south east),
\n1={atan2(\y1-\y2,\x1-\x2)-90},
% sankey prop
\p3=($(\p1)-(\p2)$),
\n2={sankeylentoqty(veclen(\x3,\y3))},
% next position
\p4=($(##2.east)!##3!-90:(##2.north east)$)
in
\pgfextra{
\pgfmathsetmacro{\prop}{\n2}
\pgfinterruptpath
\sankeynode{\prop}{\n1}{\newname}{\p4}
\path (\name) to[sankey flow] (\newname);
\endpgfinterruptpath
};
}
\newcommand\sankeyturn[3][]{%newname,name,angle
\edef\name{##2}
\ifstrempty{##1}{
\def\newname{##2}
\edef\name{##2-old}
\path [late options={name=##2,alias=\name}];
}{
\def\newname{##1}
}
\ifnumgreater{##3}{0}{
\typeout{turn acw: ##3}
\path
let
% sankey node angle
\p1=(##2.north east),
\p2=(##2.south east),
\p3=($(\p1)!-\sankeyminradius!(\p2)$),
\n1={atan2(\y1-\y2,\x1-\x2)-90},
% sankey prop
\p4=($(\p1)-(\p2)$),
\n2={sankeylentoqty(veclen(\x4,\y4))},
\p5=(##2.east),
\p6=($(\p3)!1!##3:(\p5)$)
in
\pgfextra{
\pgfmathsetmacro{\prop}{\n2}
\pgfinterruptpath
% \fill[red] (\p3) circle (2pt);
% \fill[blue](\p6) circle (2pt);
\sankeynode{\prop}{\n1+##3}{\newname}{\p6}
\path (\name) to[sankey flow] (\newname);
\endpgfinterruptpath
};
}{
\typeout{turn acw: ##3}
\path
let
% sankey node angle
\p1=(##2.south east),
\p2=(##2.north east),
\p3=($(\p1)!-\sankeyminradius!(\p2)$),
\n1={atan2(\y1-\y2,\x1-\x2)+90},
% sankey prop
\p4=($(\p1)-(\p2)$),
\n2={sankeylentoqty(veclen(\x4,\y4))},
\p5=(##2.east),
\p6=($(\p3)!1!##3:(\p5)$)
in
\pgfextra{
\pgfmathsetmacro{\prop}{\n2}
\pgfinterruptpath
% \fill[red] (\p3) circle (2pt);
% \fill[blue](\p6) circle (2pt);
\sankeynode{\prop}{\n1+##3}{\newname}{\p6}
\path (\name) to[sankey flow] (\newname);
\endpgfinterruptpath
};
}
}
\newcommand\sankeyfork[2]{%name,list of forks
\def\name{##1}
\def\listofforks{##2}
\xdef\sankeytot{0}
\path
let
% sankey node angle
\p1=(\name.north east),
\p2=(\name.south east),
\n1={atan2(\y1-\y2,\x1-\x2)-90},
% sankey prop
\p4=($(\p1)-(\p2)$),
\n2={sankeylentoqty(veclen(\x4,\y4))}
in
\pgfextra{
\pgfmathsetmacro{\iprop}{\n2}
}
\foreach \prop/\name[count=\c] in \listofforks {
let
\p{start \name}=($(\p1)!\sankeytot/\iprop!(\p2)$),
\n{nexttot}={\sankeytot+\prop},
\p{end \name}=($(\p1)!\n{nexttot}/\iprop!(\p2)$),
\p{mid \name}=($(\p{start \name})!.5!(\p{end \name})$)
in
\pgfextra{
\xdef\sankeytot{\n{nexttot}}
\pgfinterruptpath
\sankeynode{\prop}{\n1}{\name}{\p{mid \name}}
\endpgfinterruptpath
}
}
\pgfextra{
\pgfmathsetmacro{\diff}{abs(\iprop-\sankeytot)}
\pgfmathtruncatemacro{\finish}{\diff<0.01?1:0}
\ifnumequal{\finish}{1}{}{
\message{*** Warning: bad sankey fork (maybe)...}
\message{\iprop-\sankeytot}
}
};
}
\tikzset{
% default values,
declare function={
sankeyqtytolen(\qty)=\qty/\sankeytotalqty*\sankeytotallen;
sankeylentoqty(\len)=\len/\sankeytotallen*\sankeytotalqty;
},
sankey tot length=100pt,
sankey tot quantity=100,
sankey min radius=30pt,%
sankey arrow length=10pt,%
% user values
#1}
}{
}