summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/latex/sankey
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2021-01-27 22:04:47 +0000
committerKarl Berry <karl@freefriends.org>2021-01-27 22:04:47 +0000
commit568c49b6f47c72b1be977a58baf6b922f998a349 (patch)
tree80ad558b6ddab19f1605c2d2212feba776fb78a4 /Master/texmf-dist/tex/latex/sankey
parent3ca3289f1ee269d3f6403bf479ab7c80adfc6cbf (diff)
sankey (27jan21)
git-svn-id: svn://tug.org/texlive/trunk@57535 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/tex/latex/sankey')
-rw-r--r--Master/texmf-dist/tex/latex/sankey/sankey.sty722
-rw-r--r--Master/texmf-dist/tex/latex/sankey/tikzlibrarydubins.code.tex361
2 files changed, 1083 insertions, 0 deletions
diff --git a/Master/texmf-dist/tex/latex/sankey/sankey.sty b/Master/texmf-dist/tex/latex/sankey/sankey.sty
new file mode 100644
index 00000000000..4c28a1fe0d4
--- /dev/null
+++ b/Master/texmf-dist/tex/latex/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
diff --git a/Master/texmf-dist/tex/latex/sankey/tikzlibrarydubins.code.tex b/Master/texmf-dist/tex/latex/sankey/tikzlibrarydubins.code.tex
new file mode 100644
index 00000000000..da1dc120dca
--- /dev/null
+++ b/Master/texmf-dist/tex/latex/sankey/tikzlibrarydubins.code.tex
@@ -0,0 +1,361 @@
+% 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.
+
+\usetikzlibrary{calc}
+
+\RequirePackage{etoolbox}
+\RequirePackage{etextools}
+\RequirePackage{xfp}
+
+\newbool{dubinspathreverse}
+
+\def\ifpgfmathcond#1{%
+ \pgfmathparse{(#1)?1:0}%
+ \ifnumequal{\pgfmathresult}{1}%
+}
+
+\def\anglebetween#1#2#3{%macro, s, t
+ \path let
+ \p{s}=(#2), \p{t}=(#3),
+ \n{angle}={atan2(\y{t}-\y{s},\x{t}-\x{s})}
+ in \pgfextra {
+ \pgfmathsetmacro#1{\n{angle}}
+ \aftergroup@def#1
+ };
+}
+
+\def\distancebetween#1#2#3{%macro, s, t
+ \path let
+ \p{s}=(#2), \p{t}=(#3),
+ %\n{dist}={veclen(\x{t}-\x{s},\y{t}-\y{s}))}
+ \n{dist}={\fpeval{sqrt((\x{t}-\x{s})*(\x{t}-\x{s})+(\y{t}-\y{s})*(\y{t}-\y{s}))}}
+ in \pgfextra {
+ %\typeout{n{dist}:\n{dist}}
+ \pgfmathsetmacro#1{\n{dist}}
+ \aftergroup@def#1
+ };
+}
+
+\newcommand\dubinspath@rsr{% s, sa, t, ta, as, len, at, r
+ let
+ \p{tr}=([shift={(\dbp@angb-90:\dbp@radius pt)}]\dbp@b),
+ \n1={\dbp@anga+90},
+ \n2={\dbp@angb+90},
+ \n3={\n2+\dbp@lastangle}
+ in
+ arc(\n1:\n1-\dbp@firstangle:\dbp@rradius pt)
+ -- ([shift={(\p{tr})}]\n3:\dbp@rradius pt)
+ arc(\n3:\n2:\dbp@rradius pt)
+}
+
+\newcommand\dubinspath@lsl{% s, sa, t, ta, as, len, at, r
+ let
+ \p{tl}=([shift={(\dbp@angb+90:\dbp@radius pt)}]\dbp@b),
+ \n1={\dbp@anga-90},\n2={\n1+\dbp@firstangle},
+ \n3={\dbp@angb-90},\n4={\n3-\dbp@lastangle}
+ in
+ arc(\n1:\n2:\dbp@lradius pt)
+ -- ([shift={(\p{tl})}]\n4:\dbp@lradius pt)
+ arc(\n4:\n3:\dbp@lradius pt)
+}
+
+\newcommand\dubinspath@rsl{% s, sa, t, ta, as, len, at, r
+ let
+ \p{tl}=([shift={(\dbp@angb+90:\dbp@radius pt)}]\dbp@b),
+ \n1={\dbp@anga+90},\n2={\n1-\dbp@firstangle},
+ \n3={\dbp@angb-90},\n4={\n3-\dbp@lastangle}
+ in
+ arc(\n1:\n2:\dbp@rradius pt)
+ -- ([shift={(\p{tl})}]\n4:\dbp@lradius pt)
+ arc(\n4:\n3:\dbp@lradius pt)
+}
+
+\newcommand\dubinspath@lsr{% s, sa, t, ta, as, len, at, r
+ let
+ \p{tr}=([shift={(\dbp@angb-90:\dbp@radius pt)}]\dbp@b),
+ \n1={\dbp@anga-90},\n2={\n1+\dbp@firstangle},
+ \n3={\dbp@angb+90},\n4={\n3+\dbp@lastangle}
+ in
+ arc(\n1:\n2:\dbp@lradius pt)
+ -- ([shift={(\p{tr})}]\n4:\dbp@rradius pt)
+ arc(\n4:\n3:\dbp@rradius pt)
+}
+
+\newcommand\dubinspath@lrl{% s, sa, t, ta, as, ai, at, r
+ let
+ \n1={\dbp@anga-90},\n2={\n1+\dbp@firstangle},
+ \n3={\dbp@angb-90},\n4={\n3-\dbp@lastangle}
+ in
+ arc(\n1:\n2:\dbp@lradius pt)
+ arc(\n2+180:\n2+180-\dbp@midparam:\dbp@rradius pt)
+ arc(\n4:\n3:\dbp@lradius pt)
+}
+
+\newcommand\dubinspath@rlr{% s, sa, t, ta, as, ai, at, r
+ let
+ \n1={\dbp@anga+90},\n2={\n1-\dbp@firstangle},
+ \n3={\dbp@angb+90},\n4={\n3+\dbp@lastangle}
+ in
+ arc(\n1:\n2:\dbp@rradius pt)
+ arc(\n2+180:\n2+180+\dbp@midparam:\dbp@lradius pt)
+ arc(\n4:\n3:\dbp@rradius pt)
+}
+\newcommand\dubinspath@rev@lsl{\dubinspath@rsr}
+\newcommand\dubinspath@rev@rsr{\dubinspath@lsl}
+\newcommand\dubinspath@rev@lsr{\dubinspath@lsr}
+\newcommand\dubinspath@rev@rsl{\dubinspath@rsl}
+\newcommand\dubinspath@rev@lrl{\dubinspath@rlr}
+\newcommand\dubinspath@rev@rlr{\dubinspath@lrl}
+
+
+\newcommand\dubinspath[1]{%
+ \pgfextra{
+ \dubinspathset{#1}
+ \ifbool{dubinspathreverse}{
+ \edef\newa{\dbp@b}
+ \edef\newb{\dbp@a}
+ \pgfmathsetmacro\newanga{180+\dbp@angb}
+ \pgfmathsetmacro\newangb{180+\dbp@anga}
+ \edef\newfirstangle{\dbp@lastangle}
+ \edef\newlastangle{\dbp@firstangle}
+ \edef\newmethod{rev@\dbp@method}
+ \edef\newlradius{\dbp@rradius}
+ \edef\newrradius{\dbp@lradius}
+ \dubinspathset{
+ start point=\newa,
+ end point=\newb,
+ start angle=\newanga,
+ end angle=\newangb,
+ first angle=\newfirstangle,
+ last angle=\newlastangle,
+ left and right minimum radii=\newlradius pt and \newrradius pt,
+ method=\newmethod,
+ }
+ }{}
+ %\typeout{dbp@lradius:\dbp@lradius}
+ %\typeout{dbp@rradius:\dbp@rradius}
+ }
+ \csname dubinspath@\dbp@method\endcsname%
+}
+
+
+% solution
+% \xdef\dubinspathmethod{} % type of dubins path
+% \xdef\dubinspathlength{} % lenght (pt)
+% \xdef\dubinspathfirstangle{} % first angle
+% \xdef\dubinspathmiddleparam{} % intermediate angle or straight distance
+% \xdef\dubinspathlastangle{} % last angle
+
+\def\dubinspathstore#1#2{%
+ \expandafter\xdef\csname dubinspath@store@#1@#2\endcsname%
+ {\csname dubinspath#2\endcsname}%
+}
+\def\dubinspathget#1#2{%
+ \csname dubinspath@store@#1@#2\endcsname%
+}
+
+%
+\def\dubinspathsetparams#1#2#3#4#5{% method, length, fisrt angle, middle param, last angle
+ \edef\dubinspathmethod{#1}
+ \edef\dubinspathlength{#2}
+ \edef\dubinspathfirstangle{#3}
+ \edef\dubinspathmiddleparam{#4}
+ \edef\dubinspathlastangle{#5}
+ \ifdef{\dbp@storename}{
+ \foreach \p in {method,length,firstangle,middleparam,lastangle}{
+ \dubinspathstore{\dbp@storename}{\p}
+ }
+ }{}
+}
+
+\def\dubinspathupdateparams#1#2#3#4#5{
+ \ifpgfmathcond{#2<\dubinspathlength}{
+ \dubinspathsetparams{#1}{#2}{#3}{#4}{#5}
+ }{}
+}
+
+\tikzset{
+ dubins path/.is family,
+ dubins path,
+ start point/.store in=\dbp@a,
+ start angle/.store in=\dbp@anga,
+ end point/.store in=\dbp@b,
+ end angle/.store in=\dbp@angb,
+ store/.store in=\dbp@storename,
+ use store/.style={
+ method=\dubinspathget{#1}{method},
+ first angle=\dubinspathget{#1}{firstangle},
+ last angle=\dubinspathget{#1}{lastangle},
+ middle param=\dubinspathget{#1}{middleparam},
+ },
+ minimum radius/.code={
+ \pgfmathsetmacro\dbp@radius{#1}
+ \pgfmathsetmacro\dbp@rradius{#1}
+ \pgfmathsetmacro\dbp@lradius{#1}
+ },
+ left and right minimum radii/.code args={#1 and #2}{
+ \pgfmathsetmacro\dbp@lradius{#1}
+ \pgfmathsetmacro\dbp@rradius{#2}
+ \pgfmathsetmacro\dbp@radius{(\dbp@lradius + \dbp@rradius)/2}
+ },
+ method/.store in=\dbp@method,
+ first angle/.store in=\dbp@firstangle,
+ last angle/.store in=\dbp@lastangle,
+ middle param/.store in=\dbp@midparam,
+ reverse/.is if=dubinspathreverse,
+}
+
+\newcommand\dubinspathset[1]{\tikzset{dubins path,#1}}
+
+\newcommand\dubinspathcalc[1]{%
+ \begingroup
+ \dubinspathset{#1}
+ \tikzset{
+ declare function={
+ angtodist(\a,\r)={abs(\a)*.01745329*\r};
+ modangr(\a,\b)={
+ (Mod(\a,360)<Mod(\b,360)?Mod(\a,360):Mod(\a,360)-360)+\b-Mod(\b,360)
+ };
+ modangl(\a,\b)={
+ (Mod(\a,360)<Mod(\b,360)?Mod(\a,360)+360:Mod(\a,360))+(\b)-Mod(\b,360)
+ };
+ },
+ }
+
+ \pgfmathsetmacro\radius{\dbp@rradius}
+ \pgfmathsetmacro\anga{mod((\dbp@anga)+180,360)-180}
+ \pgfmathsetmacro\angb{mod((\dbp@angb)+180,360)-180}
+ %\typeout{radius:\radius,anga:\anga,angb:\angb}
+ \path
+ let
+ \p{a}=(\dbp@a),
+ \p{b}=(\dbp@b),
+ \p{ar}=($(\p{a}) + (\anga-90:\radius pt)$),
+ \p{al}=($(\p{a}) + (\anga+90:\radius pt)$),
+ \p{br}=($(\p{b}) + (\angb-90:\radius pt)$),
+ \p{bl}=($(\p{b}) + (\angb+90:\radius pt)$)
+ in \pgfextra{
+ \pgfinterruptpath
+
+ % RSR (ar et br)
+ %\typeout{RSR begin}
+ \anglebetween\rsrarbr{\p{ar}}{\p{br}}
+ \distancebetween\rsrdarbr{\p{ar}}{\p{br}}
+ \pgfmathsetmacro\rsrangone{Mod(\anga-\rsrarbr,360)}
+ \pgfmathsetmacro\rsrangtwo{Mod(\rsrarbr-\angb,360)}
+ \pgfmathsetmacro\rsrlen{\rsrdarbr}
+ \pgfmathsetmacro\rsrdist{
+ angtodist(\rsrangone,\radius)
+ +\rsrlen
+ +angtodist(\rsrangtwo,\radius)
+ }
+ \dubinspathsetparams{rsr}{\rsrdist}{\rsrangone}{\rsrlen}{\rsrangtwo}
+
+ % LSL (al et bl)
+ %\typeout{LSL begin}
+ \anglebetween\lslalbl{\p{al}}{\p{bl}}
+ \distancebetween\lsldalbl{\p{al}}{\p{bl}}
+ \pgfmathsetmacro{\lslangone}{mod(\lslalbl-\anga+720,360)}
+ \pgfmathsetmacro{\lslangtwo}{mod(\angb-\lslalbl+720,360)}
+ \pgfmathsetmacro\lsllen{\lsldalbl}
+ \pgfmathsetmacro\lsldist{
+ angtodist(\lslangone,\radius)
+ +\lsllen
+ +angtodist(\lslangtwo,\radius)
+ }
+ \dubinspathupdateparams{lsl}{\lsldist}{\lslangone}{\lsllen}{\lslangtwo}
+
+ % RSL (ar et bl)
+ %\typeout{RSL begin}
+ \distancebetween\rsldarbl{\p{ar}}{\p{bl}}
+ \pgfmathtruncatemacro\rslok{(\rsldarbl>=2*\radius)?1:0}
+ \ifnumequal{\rslok}{1}{
+ \anglebetween\rslarbl{\p{ar}}{\p{bl}}
+ \pgfmathsetmacro\rslanglesup{
+ asin(\radius/\rsldarbl*2)}
+ \pgfmathsetmacro\rslangone{Mod(\anga-\rslarbl+\rslanglesup,360)}
+ \pgfmathsetmacro\rslangtwo{Mod(\angb-\rslarbl+\rslanglesup,360)}
+ \pgfmathsetmacro\rsllen{veclen(\rsldarbl,\radius)}
+ \pgfmathsetmacro\rsldist{
+ angtodist(\rslangone,\radius)
+ +\rsllen
+ +angtodist(\rslangtwo,\radius)
+ }
+
+ \dubinspathupdateparams{rsl}{\rsldist}{\rslangone}{\rsllen}{\rslangtwo}
+ }{}
+
+ % LSR (al et br)
+ %\typeout{LSR begin}
+ \distancebetween\lsrdalbr{\p{al}}{\p{br}}
+ \pgfmathtruncatemacro\lsrok{(\lsrdalbr>=2*\radius)?1:0}
+ \ifnumequal{\lsrok}{1}{
+ \anglebetween\lsralbr{\p{al}}{\p{br}}
+ \pgfmathsetmacro\lsranglesup{
+ asin(\radius/\lsrdalbr*2)}
+ \pgfmathsetmacro\lsrangone{Mod(\lsralbr+\lsranglesup-\anga,360)}
+ \pgfmathsetmacro\lsrangtwo{Mod(\lsralbr+\lsranglesup-\angb,360)}
+ \pgfmathsetmacro\lsrlen{veclen(\lsrdalbr,\radius)}
+ \pgfmathsetmacro\lsrdist{
+ angtodist(\lsrangone,\radius)
+ +\lsrlen
+ +angtodist(\lsrangtwo,\radius)
+ }
+ \dubinspathupdateparams{lsr}{\lsrdist}{\lsrangone}{\lsrlen}{\lsrangtwo}
+ }{}
+
+ % LRL (al et bl)
+ %\typeout{LRL begin}
+ \distancebetween\lrldalbl{\p{al}}{\p{bl}}
+ \pgfmathtruncatemacro\lrlok{(\lrldalbl<=4*\radius)?1:0}
+ \ifnumequal{\lrlok}{1}{
+ % \debug{LRL}
+ \anglebetween\lrlalbl{\p{al}}{\p{bl}}
+ \pgfmathsetmacro\lrlangsup{acos(\lrldalbl/\radius/4)}
+ \pgfmathsetmacro\lrlangone{
+ modangl(\lrlalbl+\lrlangsup,\anga-90)-(\anga-90)}
+ \pgfmathsetmacro\lrlangtwo{%
+ (\angb-90)-modangr(\lrlalbl+180-\lrlangsup,\angb-90)}
+ \pgfmathsetmacro\lrlangthree{360-2*(90-\lrlangsup)}
+ \pgfmathsetmacro\lrldist{
+ angtodist(\lrlangone,\radius)
+ +angtodist(\lrlangthree,\radius)
+ +angtodist(\lrlangtwo,\radius)
+ }
+ \dubinspathupdateparams{lrl}{\lrldist}{\lrlangone}{\lrlangthree}{\lrlangtwo}
+ }{}
+
+ % RLR (ar et br)
+ %\typeout{RLR begin}
+ \distancebetween\rlrdarbr{\p{ar}}{\p{br}}
+ \pgfmathtruncatemacro\rlrok{(\rlrdarbr<=4*\radius)?1:0}
+ \ifnumequal{\rlrok}{1}{
+ \anglebetween\rlrarbr{\p{ar}}{\p{br}}
+ \pgfmathsetmacro\rlrangsup{acos(\rlrdarbr/\radius/4)}
+ \pgfmathsetmacro\rlrangone{
+ (\anga+90)-modangr(\rlrarbr-\rlrangsup,\anga+90)}
+ \pgfmathsetmacro\rlrangtwo{%
+ modangl(\rlrarbr+180+\rlrangsup,\angb+90)-(\angb+90)}
+ \pgfmathsetmacro\rlrangthree{360-2*(90-\rlrangsup)}
+ \pgfmathsetmacro\rlrdist{
+ angtodist(\rlrangone,\radius)
+ +angtodist(\rlrangthree,\radius)
+ +angtodist(\rlrangtwo,\radius)
+ }
+ \dubinspathupdateparams{rlr}{\rlrdist}{\rlrangone}{\rlrangthree}{\rlrangtwo}
+ }{}
+
+ \endpgfinterruptpath
+ };
+ \endgroup
+}
+
+%%% Local Variables:
+%%% mode: latex
+%%% TeX-master: t
+%%% End: