% ddphonism
% 
% (c) Celia Rubio Madrigal
%
%% This program can be redistributed and/or modified under the terms
%% of the LaTeX Project Public License Distributed from CTAN archives
%% in directory macros/latex/base/lppl.txt.

\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{ddphonism}
[2019/09/01 v0.2 Dodecaphonic diagrams: twelve-tone matrices, clock diagrams, etc.]

\RequirePackage{etoolbox}
\RequirePackage{xparse}
\RequirePackage{tikz}
\RequirePackage{xstring}
\RequirePackage{pgfkeys}


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Matrices

\usetikzlibrary{matrix}

\ExplSyntaxOn
\DeclareExpandableDocumentCommand{\Evaluation}{m}{\int_eval:n {#1}}
\ExplSyntaxOff

\newcounter{Dsize}
\newcommand{\DsizeMake}[1]{%
	\setcounter{Dsize}{0}%
	\foreach \n in {#1}{%
		\stepcounter{Dsize}%
	}%
}

% Only with numbers.
\newcounter{Dfirst}
\newcommand{\DheadMake}[1]{%
	\setcounter{Dfirst}{-1}%
	\foreach \n in {#1}{%
		\ifnum\theDfirst=-1%
		\setcounter{Dfirst}{\n}%
		\fi%
	}%
}

% Only when DsizeMake is already done.
\newcounter{Dmod}
\newcommand{\Modulo}[1]{%
	\setcounter{Dmod}{#1}%
	\loop%
	\ifnum\theDmod>\Evaluation{\theDsize-1}%		
	\setcounter{Dmod}{\Evaluation{\theDmod-\theDsize}}%
	\repeat%
	\ifnum\theDmod<0%
	\setcounter{Dmod}{\Evaluation{\theDmod+\theDsize}}%
	\repeat%
	\theDmod%
}

\newif\ifdmatrixLines
\newif\ifdmatrixOutside
\newif\ifdmatrixInside
\newif\ifdmatrixV
\newif\ifdmatrixH
\newif\ifdmatrixTikz
\pgfkeys{
	/dmatrix/.is family
	, /dmatrix
	, default/.style = 
	{ lines = false
		, outside lines = false
		, inside lines = false
		, sep = 1
		, vsep = 1
		, hsep = 1
		, no tikz = false
	}
	, no tikz/.is if=dmatrixTikz
	, lines/.is if=dmatrixLines
	, outside lines/.is if=dmatrixOutside
	, inside lines/.is if=dmatrixInside
	, vlines/.is if=dmatrixV
	, hlines/.is if=dmatrixH
	, sep/.estore in=\dmatrixSep
	, vsep/.estore in=\dmatrixVsep
	, hsep/.estore in=\dmatrixHsep
}

\newcommand{\DLOH}{%
	\draw (0.05*\dmatrixSep*\dmatrixHsep,0) --%
	(\theDsize*\dmatrixSep*\dmatrixHsep+0.05*\dmatrixSep*\dmatrixHsep,0);%
	\draw (0.05*\dmatrixSep*\dmatrixHsep,-\theDsize*0.5*\dmatrixSep*\dmatrixVsep) -- %
	(\theDsize*\dmatrixSep*\dmatrixHsep+0.05*\dmatrixSep*\dmatrixHsep,-\theDsize*0.5*\dmatrixSep*\dmatrixVsep);%
}

\newcommand{\DLOV}{%
	\draw (0.05*\dmatrixSep*\dmatrixHsep,0) -- %
	(0.05*\dmatrixSep*\dmatrixHsep,-\theDsize*0.5*\dmatrixSep*\dmatrixVsep);%
	\draw (\theDsize*\dmatrixSep*\dmatrixHsep+0.05*\dmatrixSep*\dmatrixHsep,0) -- %
	(\theDsize*\dmatrixSep*\dmatrixHsep+0.05*\dmatrixSep*\dmatrixHsep,-\theDsize*0.5*\dmatrixSep*\dmatrixVsep);
}

\newcommand{\DLIH}{%
	\draw (0.05*\dmatrixSep*\dmatrixHsep,-\xD*0.5*\dmatrixSep*\dmatrixVsep) -- %
	(\theDsize*\dmatrixSep*\dmatrixHsep+0.05*\dmatrixSep*\dmatrixHsep,-\xD*0.5*\dmatrixSep*\dmatrixVsep);%
}

\newcommand{\DLIV}{%
	\draw (\xD*\dmatrixSep*\dmatrixHsep+0.05*\dmatrixSep*\dmatrixHsep,0) -- %
	(\xD*\dmatrixSep*\dmatrixHsep+0.05*\dmatrixSep*\dmatrixHsep,-\theDsize*0.5*\dmatrixSep*\dmatrixVsep);%
}

\newcommand{\dmatrix}[2][]{%
	\DsizeMake{#2}%
	\DheadMake{#2}%
	%
	\pgfkeys{/dmatrix, default, #1}%
	%
	\ifdmatrixTikz\else%
	\begin{tikzpicture}%
	\fi%
	\foreach [count=\nj] \j in {#2} {%
		\foreach [count=\ni] \i in {#2} {%
			\draw node at 
			( \ni*\dmatrixSep*\dmatrixHsep-0.5*\dmatrixSep*\dmatrixHsep
			, -\nj*\dmatrixSep*\dmatrixVsep/2+0.25*\dmatrixSep*\dmatrixVsep) {%
				\Modulo{\Evaluation{\i-\j+\theDfirst}}%
			};%
		}%
	}%
	\foreach \xD in {1,...,\Evaluation{\theDsize-1}} {%
		\ifdmatrixLines
		\DLOH\DLOV\DLIH\DLIV
		\fi
		\ifdmatrixOutside
		\DLOH\DLOV
		\fi
		\ifdmatrixInside
		\DLIH\DLIV
		\fi
		\ifdmatrixH
		\DLOH\DLIH
		\fi
		\ifdmatrixV
		\DLOV\DLIV
		\fi
	}%
	%
	\ifdmatrixTikz\else%
	\end{tikzpicture}%
	\fi%
}


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Diagrams

\usetikzlibrary{shapes,arrows,decorations.markings,shapes.misc}

\tikzstyle{ddiagram}=[minimum height=0pt,inner sep=0pt,outer sep=0pt,scale=0.65]

\newif\ifddiagramTikz
\newif\ifddiagramNoNum
\newif\ifddiagramNoArr
\pgfkeys{
	/ddiagram/.is family
	, /ddiagram
	, default/.style = 
	{ name =\empty%
		, up =\empty%
		, no tikz = false
		, no numbers = false
		, no arrow = false
		, xshift = 0
		, yshift = 0
		, arrow shift = 2.5
	}
	, no tikz/.is if=ddiagramTikz
	, no numbers/.is if=ddiagramNoNum
	, no arrow/.is if=ddiagramNoArr
	, name/.estore in=\ddiagramName
	, up/.estore in=\ddiagramUp
	, xshift/.estore in=\ddiagramX
	, yshift/.estore in=\ddiagramY
	, arrow shift/.estore in=\ddiagramArrS
}

\newcounter{Dprev}
\newcommand{\Dvar}{}
\newcommand{\ddiagram}[2][]{%
	\DsizeMake{#2}%
	\DheadMake{#2}%
	%
	\pgfkeys{/ddiagram, default, #1}%
	%
	\ifdefequal{\ddiagramUp}{\empty}%
	{\renewcommand{\Dvar}{\theDfirst}}% if empty
	{\renewcommand{\Dvar}{\ddiagramUp}}% if not empty
	%
	\ifddiagramTikz\else%
	\begin{tikzpicture}[ddiagram,rotate=360*\Dvar/\theDsize]%
	\fi%
	\foreach \x in {0,...,\Evaluation{\theDsize-1}} {%
		\ifddiagramNoNum\else
		\node [xshift=\ddiagramX,yshift=\ddiagramY] at (90-360*\x/\theDsize:2) {\x};%
		\fi
		\node [xshift=\ddiagramX,yshift=\ddiagramY] (\x) at (90-360*\x/\theDsize:1.6) {};%
	};%
	%
	\setcounter{Dprev}{-1}%
	\foreach \x in {#2}{%
		\ifnum \theDprev=\theDfirst%
		\ifddiagramNoArr
		\draw [xshift=\ddiagramX,yshift=\ddiagramY] (\theDprev) -- (\x);%
		\else
		\draw [xshift=\ddiagramX,yshift=\ddiagramY,
		decoration=
		{markings,mark=at position 0.099*\ddiagramArrS with
			{\arrow[scale=1.25,>=triangle 45]{>}}},
		postaction={decorate}
		] (\theDprev) -- (\x);%
		\fi
		\else \ifnum \theDprev=-1 \else%
		\draw [xshift=\ddiagramX,yshift=\ddiagramY] (\theDprev) -- (\x);%
		\fi\fi%
		\setcounter{Dprev}{\x}%
	};%
	\draw [xshift=\ddiagramX,yshift=\ddiagramY] (\theDprev) -- (\theDfirst);%
	%
	\ifdefequal{\ddiagramName}{\empty}%
	{}% if empty
	{\node [xshift=\ddiagramX,yshift=\ddiagramY] at (0,0) [circle,fill=white] {\ddiagramName};}% if not empty
	\ifddiagramTikz\else%
	\end{tikzpicture}%
	\fi%
}


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Dihedral diagrams

\tikzstyle ddihedralArrow=[decoration=
{markings,mark=at position 1 with {\arrow[scale=1.5,>=angle 60]{>}}},
postaction={decorate}]

\tikzstyle{ddihedral}=[inner sep=0,minimum height=18pt]

\newif\ifddihedralTikz
\newif\ifddihedralItalics
\pgfkeys{
	/ddihedral/.is family, /ddihedral,
	default/.style = 
		{ t = 0, c = 0, s = 0, v = 0
		, no tikz=false
		, new t = T, new c = C, new s = S, new v = V
		, no italics = false
		},
	no tikz/.is if=ddihedralTikz,
	t/.estore in = \ddihedralT,
	c/.estore in = \ddihedralC,
	s/.estore in = \ddihedralS,
	v/.estore in = \ddihedralV,
	no italics/.is if=ddihedralItalics,
	new t/.estore in = \ddihedralNewT,
	new c/.estore in = \ddihedralNewC,
	new s/.estore in = \ddihedralNewS,
	new v/.estore in = \ddihedralNewV,
}

\newif\ifdarrowsTikz
\pgfkeys{
	/darrows/.is family, /darrows,
	default/.style = {no tikz=false},
	no tikz/.is if=darrowsTikz,
}
\newcommand{\darrows}[2][]{%
	\DsizeMake{#2}%
	%
	\pgfkeys{/darrows, default, #1}%
	%
	\ifdarrowsTikz\else%
	\begin{tikzpicture}%
	\fi%
	\draw foreach \x in {0,...,\Evaluation{\theDsize-1}} {%
		(90-360*\x/\theDsize:2.5) node[circle] (\x) {}%
	};%
	\foreach \x [count=\y] in {#2} {%
		\draw [style=ddihedralArrow] (90-360*\Evaluation{\y-1}/\theDsize:1.25) -- (\x);%
	};%
	\ifdarrowsTikz\else%
	\end{tikzpicture}%
	\fi%
}

\newcommand\ddihedral[2][]{%
	\DsizeMake{#2}%
	%
	\pgfkeys{/ddihedral, default, #1}%
	%
	\ifddihedralTikz\else%
	\begin{tikzpicture}[ddihedral]%
	\fi%
	\draw foreach \x in {0,...,\Evaluation{\theDsize-1}} {%
		(\Evaluation{(90+\ddihedralT*360/\theDsize)+(2*\ddihedralS-1)*\x*360/\theDsize}:2.5)%
		node[very thin,circle,draw] (\x) {\x}%
	};%
	%
	\draw foreach \x in {0,...,\Evaluation{\theDsize-1}} {%
		(\Evaluation{(90-\ddihedralC*360/\theDsize)+(2*\ddihedralV-1)*\x*360/\theDsize}:1.25)%
		node[very thin,circle,draw] {\x}%
	};%
	%
	\darrows[no tikz]{#2}%
	%
	\node at (0,0) [very thin,draw,circle, fill=white] {%
	{\ifddihedralItalics\else\it\fi%
		\ifodd\ddihedralV%
		\ddihedralNewV\else%
		\ifnum\ddihedralC=0%
		\ifodd\ddihedralS\else%
		\ifnum\ddihedralT=0%
		\ddihedralNewT$^0$%
		\fi\fi\fi\fi%
		\ifnum\ddihedralC=0%
		\else \ddihedralNewC$^{\ddihedralC}$\fi%
		\ifodd\ddihedralS%
		\ddihedralNewS\fi%
		\ifnum\ddihedralT=0%
		\else \ddihedralNewT$^{\ddihedralT}$\fi}%
	};%
	\ifddihedralTikz\else%
	\end{tikzpicture}%
	\fi%
}


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Rows

\pgfkeys{
	/drow/.is family, /drow,
	default/.style = {sep=\arraycolsep},
	sep/.estore in = \drowSep,
}

\long\def\addto#1#2{\expandafter\def\expandafter#1\expandafter{#1#2}}
\newcounter{myDDcntr}
\newlength{\Dvarr}

\newcommand{\drow}[2][]{%
	\DsizeMake{#2}%
	%
	\pgfkeys{/drow, default, #1}%
	\setlength{\Dvarr}{\arraycolsep}
	\setlength{\arraycolsep}{\drowSep}
	%
	\ifnum\theDsize=0%
	\ensuremath{\left(\right)}%
	\else\ifnum\theDsize=1%
	\ensuremath{%
		\left(\begin{array}{*{\theDsize}c}%
			0\\%
			#2\\%
		\end{array}\right)%
	}%
	\else%
	\def\TableDDdata{}%
	\setcounter{myDDcntr}{0}%
	\loop%
	\addto\TableDDdata{\themyDDcntr\stepcounter{myDDcntr} &}%
	\stepcounter{myDDcntr}%
	\ifnum\themyDDcntr<\Evaluation{\theDsize-1}%
	\repeat%
	\addto\TableDDdata{\themyDDcntr \\}%
	\setcounter{myDDcntr}{0}%
	%
	\ensuremath{%
		\left(\begin{array}{*{\theDsize}c}%
			\TableDDdata%
			\StrSubstitute{#2}{,}{&}\\%
		\end{array}\right)%
	}%
	\fi\fi%
	\setlength{\arraycolsep}{\Dvarr}
}

\endinput


%% End of file `ddphonism.sty'.