%
% CoDi: Commutative Diagrams for TeX
% Copyright (c) 2015-2024 Paolo Brasolin <
[email protected]>
% SPDX-License-Identifier: MIT
%
% This file is part of CoDi 1.1.2, released on 2024/04/22 under MIT license.
%
% μανδύας • (mandýas)
% 1. cloak
% 2. mantle
% 3. dolman
% Mandias defines the aesthetics aspect of the user level functionality,
% together with some baseline configuration.
\usetikzlibrary[calc]
% \usetikzlibrary[decorations.pathreplacing]
% \usetikzlibrary{decorations.pathmorphing}
% \usetikzlibrary[decorations.markings]
% \usetikzlibrary[arrows.meta]
\usetikzlibrary[positioning]
\pgfqkeys{/codi}{
%==[ universal styles ]=========================================================
every diagram/.append style={},
every layout/.append style={
/codi/every object/.append style={shape=codi baseline centered rectangle},
square,
},
every object/.append style={},
every arrow/.append style={
>=stealth,
},
every label/.append style={
auto,
inner sep=0.5ex,
font=\everymath\expandafter{\the\everymath\scriptstyle}
},
%==[ arrow styles ]=============================================================
/codi/arrows/.cd,
crossing over/clearance/.initial=0.5ex,
crossing over/color/.initial=white,
crossing over/.style={
/tikz/preaction={
-,
draw=\pgfkeysvalueof{/codi/arrows/crossing over/color},
line width=\pgfkeysvalueof{/codi/arrows/crossing over/clearance},
},
},
shove/.style={
/tikz/transform canvas={
/tikz/shift={($(\tikztostart)!#1!-90:(\tikztotarget)-(\tikztostart)$)}
}
},
slide/.style={
/tikz/transform canvas={
/tikz/shift={($(\tikztostart)!#1!0:(\tikztotarget)-(\tikztostart)$)}
}
},
%==[ label styles ]=============================================================
/codi/labels/.cd,
mid/.style={
/tikz/fill=white,
/tikz/shape=circle,
/tikz/anchor=center,
/tikz/inner sep=.25ex
},
%=[ objects styles ]============================================================
/codi/objects/.cd,
% TODO: reflect on these styles
% tetragonal/.style 2 args={
% /tikz/node distance=#2 and #1
% },
% square/.style={
% /codi/objects/rectangular={#1}{#1}
% },
% golden/.style={
% /codi/objects/rectangular={#1}{0.618*#1}
% },
% % comb/.style={
% % /codi/objects/rectangular={#1}{sqrt(3/4)*#1},
% % },
% % comb/.default=4em,
% square/.default=4em,
% golden/.default=4em,
%=[ lattice styles ]============================================================
/codi/layouts/.cd,
hexagonal/.code args={#1side #2 angle #3}{
\pgfkeys{/codi/layouts/#1hexagonal=side {#2} angle {#3}},
},
horizontal hexagonal/.style args={side #1 angle #2}{
/codi/layouts/tetragonal=base {#1} height {tan(#2)*#1*0.5},
/tikz/every odd row/.append style={/tikz/xshift=(#1)*0.5},
},
vertical hexagonal/.style args={side #1 angle #2}{
/codi/layouts/tetragonal=base {tan(#2)*#1*0.5} height {#1},
/tikz/every odd column/.append style={/tikz/yshift=-1*(#1)*0.5},
},
hexagonal/.default=horizontal side 4.5em angle 60,
%
tetragonal/.style args={base #1 height #2}{
/tikz/column sep={{#1},between origins},
/tikz/row sep={{#2},between origins},
},
tetragonal/.default=base 4.5em height 2.8em,
%
square/.style={
/codi/layouts/tetragonal=base {#1} height {#1},
},
golden/.style={
/codi/layouts/tetragonal=base {#1} height {0.618*#1},
},
square/.default=4.5em,
golden/.default=4.5em,
%=[ diagram styles ]============================================================
/codi/diagrams/.cd,
grid/.style 2 args={
/tikz/x={#1},
/tikz/y={#2},
/tikz/on grid,
},
metric/.style 2 args={
% NOTE: {1 and 1}{1} is infty-norm, unit circle is unit square
% NOTE: {2}{0.5} is 1-norm, circle of radius 2 is rhombus circumscribing base hexagon
/tikz/node distance={#1},
% TODO: implement control to allow for explicit units on single factor specs
/tikz/above left/.code={\tikz@lib@place@handle@{##1}{south east}{-1}{1}{north west}{#2}},
/tikz/above right/.code={\tikz@lib@place@handle@{##1}{south west}{1}{1}{north east}{#2}},
/tikz/below left/.code={\tikz@lib@place@handle@{##1}{north east}{-1}{-1}{south west}{#2}},
/tikz/below right/.code={\tikz@lib@place@handle@{##1}{north west}{1}{-1}{south east}{#2}},
},
%
hexagonal/.code args={#1side #2 angle #3}{
\pgfkeys{/codi/diagrams/#1hexagonal=side #2 angle #3}
},
horizontal hexagonal/.style args={side #1 angle #2}{
/codi/every layout/.append style={/codi/layouts/hexagonal=horizontal side {#1} angle {#2}},
/codi/diagrams/grid={#1*0.5}{#1*tan(#2)*0.5},
/codi/diagrams/metric={2}{0.5},
},
vertical hexagonal/.style args={side #1 angle #2}{
/codi/every layout/.append style={/codi/layouts/hexagonal=vertical side {#1} angle {#2}},
/codi/diagrams/grid={#1*tan(#2)*0.5}{#1*0.5},
/codi/diagrams/metric={2}{0.5},
},
hexagonal/.default=horizontal side 4.5em angle 60,
%
tetragonal/.style args={base #1 height #2}{
/codi/every layout/.append style={/codi/layouts/tetragonal=base {#1} height {#2}},
/codi/diagrams/grid={#1}{#2},
/codi/diagrams/metric={1 and 1}{1},
},
tetragonal/.default=base 4.5em height 2.8em,
%
square/.style={
/codi/every layout/.append style={/codi/layouts/square=#1},
/codi/diagrams/tetragonal=base {#1} height {#1},
},
golden/.style={
/codi/every layout/.append style={/codi/layouts/golden=#1},
/codi/diagrams/tetragonal=base {#1} height {0.618*#1},
},
square/.default=4.5em,
golden/.default=4.5em,
}
%==[ baseline centered rectangle shape ]========================================
% The math formula axis height is recovered and stored as a pgf function.
% NOTE: the LuaTeX version is needed just by ConTeXt
\pgfutil@ifluatex
\directlua{tex.enableprimitives('kD', {'Umathaxis'})}
\pgfmathdeclarefunction{kD_math_formula_axis_height}{0}{%
\begingroup%
$\relax$% update fontdimens
% See TeX by Topic §23.5 for details.
\pgfmathreturn\the\kDUmathaxis\textstyle%
\endgroup}
\else% if using (pdf)tex
\pgfmathdeclarefunction{kD_math_formula_axis_height}{0}{%
\begingroup%
$\relax$% update fontdimens
% See TeX by Topic §23.5 for details.
\pgfmathreturn\the\fontdimen22\textfont2%
\endgroup}
\fi
\pgfqkeys{/codi/baseline centered rectangle}{
center raise/.initial=kD_math_formula_axis_height
}
% Then the shape is defined by inheritance.
\pgfdeclareshape{codi baseline centered rectangle} {
% Inherit the rectangle shape.
\inheritsavedanchors[from={rectangle}]
\inheritanchor[from={rectangle}]{base}
\inheritanchor[from={rectangle}]{north}
\inheritanchor[from={rectangle}]{south}
\inheritanchor[from={rectangle}]{base west}
\inheritanchor[from={rectangle}]{north west}
\inheritanchor[from={rectangle}]{south west}
\inheritanchor[from={rectangle}]{base east}
\inheritanchor[from={rectangle}]{north east}
\inheritanchor[from={rectangle}]{south east}
\inheritanchor[from={rectangle}]{mid}
\inheritanchor[from={rectangle}]{mid west}
\inheritanchor[from={rectangle}]{mid east}
\inheritbackgroundpath[from={rectangle}]
% Redefine west, center and east anchors
% setting their y coordinates to center raise.
\anchor{center}{\pgf@anchor@rectangle@center\pgfmathsetlength\pgf@y%
{\pgfkeysvalueof{/codi/baseline centered rectangle/center raise}}}
\anchor{west}{\pgf@anchor@rectangle@west\pgfmathsetlength\pgf@y%
{\pgfkeysvalueof{/codi/baseline centered rectangle/center raise}}}
\anchor{east}{\pgf@anchor@rectangle@east\pgfmathsetlength\pgf@y%
{\pgfkeysvalueof{/codi/baseline centered rectangle/center raise}}}
% Save the original anchors as alternate "real" versions.
\anchor{real center}{\pgf@anchor@rectangle@center}
\anchor{real west}{\pgf@anchor@rectangle@west}
\anchor{real east}{\pgf@anchor@rectangle@east}
% Redefine the border anchor calculation.
\anchorborder{%
% (x,y) = target
% let tempdima = center raise
\pgfmathsetlength\pgfutil@tempdima%
{\pgfkeysvalueof{/codi/baseline centered rectangle/center raise}}%
% let b = (x,y) = target
\pgf@xb=\pgf@x%
\pgf@yb=\pgf@y%
% let (x,y) = south west
\southwest%
% let a = (x,y) = south west
\pgf@xa=\pgf@x%
\pgf@ya=\pgf@y%
% let (x,y) = north east
\northeast%
% let (x,y) = (x,y) - a = north east - south west = (width, height)
\advance\pgf@x by-\pgf@xa%
\advance\pgf@y by-\pgf@ya%
% let c = (x,y)/2 = (width, height)/2 = (width/2, height/2)
\pgf@xc=.5\pgf@x%
\pgf@yc=.5\pgf@y%
% let a = a + c = south west + (width/2, height/2) = center
\advance\pgf@xa by\pgf@xc%
\advance\pgf@ya by\pgf@yc%
% if by = target y > 0
\ifdim\pgf@yb>0pt%
% let (x,y) = north east
\northeast%
% let cy = y = north east y
\pgf@yc=\pgf@y%
% let cy = cy - center raise = north east y - center raise
\advance\pgf@yc by-\pgfutil@tempdima%
\else%
% let (x,y) = south west
\southwest%
% let cy = y = - south west y
\pgf@yc=-\pgf@y%
% let cy = cy + center raise = - south west y + center raise
\advance\pgf@yc by\pgfutil@tempdima%
\fi
\edef\pgf@marshal{%
% calculate the intersection of the half line from the origin
\noexpand\pgfpointborderrectangle
% passing through target
{\noexpand\pgfqpoint{\the\pgf@xb}{\the\pgf@yb}}
% and the rectangle centered on the origin
% whose upper right corner is
% (width/2, +north east y - center raise) if target y > 0
% (width/2, -south west y + center raise) if target y < 0
{\noexpand\pgfqpoint{\the\pgf@xc}{\the\pgf@yc}}%
}%
% let (x,y) = the intersection
\pgf@process{\pgf@marshal}%
% let x = x + ax = width/2 + center x
\advance\pgf@x by\pgf@xa%
% let y = y + tempdima = ±(ne/sw y - center raise) + center raise
\advance\pgf@y by\pgfutil@tempdima%
% that is, y = + north east y if target y > 0
% y = - south west y + 2 * center raise if target y < 0
%
% NOTE: in essence, we're just compensating for the redefinition
% of ne/sw anchors that shifted them by cr below the real center.
% ┏━━━━━━┯━━━━━━┓ ╮╮
% ┃ │ ┃ ││ + ney - cr
% ╭╭ ┠──────┼──────┨ ╮│╯ y > 0
% - swy + cr │╰╭ ┣━━━━━━┿━━━━━━┫ ╯╯ ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
% ╰ ╰ ┗━━━━━━┷━━━━━━┛ y < 0
}%
}%
%==[ prompter ]=================================================================
% This key is meant for visual assistance with the node labeling automation.
\pgfqkeys{/codi}{
prompter label/.style={
/tikz/.cd,
inner sep=0sp,
font=\ttfamily\bfseries\tiny,
line width=1pt,
draw=violet,
fill=violet,
text=white,
overlay,
label anchor/.style={tikz@label@post/.append style={anchor=##1}},
label anchor=north east,
label position=south east
},
prompter pinner/.style={
/tikz/draw=violet,
/tikz/line width=1sp,
/tikz/label={[/codi/prompter label]:#1},
},
prompter/.style={/bapto/output/.forward to=/codi/prompter pinner}
}