diff options
author | Karl Berry <karl@freefriends.org> | 2021-10-22 21:35:38 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2021-10-22 21:35:38 +0000 |
commit | 2faed11ad6c1013ed23ab4413056d97cc80b0f40 (patch) | |
tree | 15c28795950687e5b536a1d1f36bcbf3530d8c54 /Master/texmf-dist/tex/latex/zx-calculus | |
parent | ec57691e2b199ae86269d9ff54acdb2729d29397 (diff) |
zx-calculus (22oct21)
git-svn-id: svn://tug.org/texlive/trunk@60838 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/tex/latex/zx-calculus')
-rw-r--r-- | Master/texmf-dist/tex/latex/zx-calculus/tikzlibraryzx-calculus.code.tex | 1417 | ||||
-rw-r--r-- | Master/texmf-dist/tex/latex/zx-calculus/zx-calculus.sty | 2 |
2 files changed, 1263 insertions, 156 deletions
diff --git a/Master/texmf-dist/tex/latex/zx-calculus/tikzlibraryzx-calculus.code.tex b/Master/texmf-dist/tex/latex/zx-calculus/tikzlibraryzx-calculus.code.tex index d73c1ad0bd7..af29676b46b 100644 --- a/Master/texmf-dist/tex/latex/zx-calculus/tikzlibraryzx-calculus.code.tex +++ b/Master/texmf-dist/tex/latex/zx-calculus/tikzlibraryzx-calculus.code.tex @@ -1,5 +1,5 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%% Version: 2021/10/15 +%%% Version: 2021/10/22 %%% License: MIT %%% Author: Léo Colisson %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -15,7 +15,7 @@ \RequirePackage{xparse} % For NewDocumentComments \RequirePackage{bm} % For bold math fonts -\usetikzlibrary{cd,backgrounds,positioning,shapes,calc} +\usetikzlibrary{cd,backgrounds,positioning,shapes,calc,intersections} % Declare layers. \pgfdeclarelayer{background} % Fox boxes using "fit" to group parts of the graph. \pgfdeclarelayer{edgelayer} % For edges that are explicitely defined "wc" @@ -28,6 +28,7 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% User modifiable variables %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % Define colors, can be redefine by user \definecolor{colorZxZ}{RGB}{204,255,204} \definecolor{colorZxX}{RGB}{255,136,136} @@ -54,12 +55,31 @@ \newcommand{\zxHSColFlat}{1mm} \newcommand{\zxHSRowFlat}{1mm} %% Wires only: when adding only wires with empty nodes, the space between columns can be too small. Useful not to shrink swap gates... -\newcommand{\zxWCol}{.55em} -\newcommand{\zxWRow}{.55em} +\newcommand{\zxWCol}{2mm} +\newcommand{\zxWRow}{2mm} +%% Same as zxWCol, but when a single empty node is connected to a non-empty node. +\newcommand{\zxwCol}{1mm} +\newcommand{\zxwRow}{1mm} %% When vdots/dots are used in lines \newcommand{\zxDotsCol}{3mm} \newcommand{\zxDotsRow}{3mm} +%% Default separation between rows and columns +\newcommand{\zxDefaultColumnSep}{1.5mm} +\newcommand{\zxDefaultRowSep}{1.5mm} +% To cancel the default tow separation +\newcommand{\zxZeroCol}{-\zxDefaultColumnSep} +\newcommand{\zxZeroRow}{-\zxDefaultRowSep} +%% When adding two lines with only empty nodes, it can create quite a large space. Use this to reduce it +%% Yes, we can add multiple arguments, space will add up (see documentation pgfmatrixnextcell) +\newcommand{\zxNCol}{-\zxDefaultColumnSep,.5mm} +\newcommand{\zxNRow}{-\zxDefaultRowSep,.5mm} + +% Angles by default for s and o related arrows +\def\zxDefaultSoftAngleS{30} +\def\zxDefaultSoftAngleN{60} +\def\zxDefaultSoftAngleO{40} +\def\zxDefaultSoftAngleChevron{60} % Angles by default for s and o related arrows \def\zxDefaultSoftAngleS{30} @@ -67,6 +87,42 @@ \def\zxDefaultSoftAngleO{40} \def\zxDefaultSoftAngleChevron{60} +% Angles by default for s and o related arrows (in/out version) +\def\zxDefaultSoftAngleN{60} +\def\zxDefaultSoftAngleO{40} +\def\zxDefaultSoftAngleChevron{60} + +% Styles for O,N,... (new bezier version) +\tikzset{ + /zx/args/-andL/.cd, + defaultO/.style={-=.2,L=.4}, + defaultN/.style={-=.2,L=.8}, + defaultN-/.style={1-=.4,1L=0}, + defaultNN/.style={}, + defaultNIN/.style={1-=0,1L=.6}, + defaultChevron/.style={}, % < should be stronger than N- also in ui mode. + defaultChevronDown/.style={}, % The ^ v versions (default NN and default NIN will be loaded before). + defaultS/.style={-=.8,L=0}, + defaultS'/.style={-=.8,L=.2}, + default-S/.style={1-=.8,1L=0}, + defaultSIS/.style={1-=0,1L=.8}, + % Loads the default styles for the "use intersection" method. + /zx/args/-andL/ui/.style={ + /zx/args/-andL/defaultO/.style={-=.2,L=.17}, + /zx/args/-andL/defaultN/.style={-=.2,L=.7}, + /zx/args/-andL/defaultN-/.style={1-=.5,1L=.2,2-=.2,2L=.5}, %% Todo: write a different style for < + /zx/args/-andL/defaultNN/.style={}, + /zx/args/-andL/defaultChevron/.style={1-=.2,1L=0,2-=.2,2L=.8}, + /zx/args/-andL/defaultChevronDown/.style={1L=.2,1-=0,2L=.2,2-=.8}, + /zx/args/-andL/defaultNIN/.style={1-=0,1L=.66,2-=.55,2L=.16}, + /zx/args/-andL/defaultS/.style={-=.8,L=0}, + /zx/args/-andL/defaultS'/.style={-=.8,L=.3}, + /zx/args/-andL/default-S/.style={1-=.8,1L=0}, + /zx/args/-andL/defaultSIS/.style={1-=0,1L=.8,2-=.15,2L=.6}, + }, +} + + % Scale to use when scaling 3 dots \def\zxScaleDots{.7} @@ -86,11 +142,410 @@ \ifthenelse{\equal{#1}{-}}{\zxMinus}{#1}#4/#5% } +% Minus sign to use in \zxZ-{\alpha} +%\NewExpandableDocumentCommand{\zxMinusInShort}{}{-} +\NewExpandableDocumentCommand{\zxMinusInShort}{}{-} + +% The library tries to find intersection between fake center east/... and the border of the node +% to draw the nodes directly on the border. +% To enable this function (can also be set locally), enable this macro: +% \def\zxEnableIntersectionsNodes{} %% To add the "name shape" on each node +% \def\zxEnableIntersectionsWires{} %% For wires only (can't work without \zxEnableIntersectionsNodes) +\newcommand{\zxEnableIntersections}{% + \def\zxEnableIntersectionsWires{}% + \def\zxEnableIntersectionsNodes{}% +} +% to disable intersections, undefined it: +\let\zxEnableIntersectionsNodes\undefined +\let\zxEnableIntersectionsWires\undefined +\newcommand{\zxDisableIntersections}{% + \let\zxEnableIntersectionsNodes\undefined% + \let\zxEnableIntersectionsWires\undefined% +} +% Now, if it cannot find a good intersection (either because "name path" is disabled, or +% because no intersection is found, or because intersections are disabled anyway) we need to +% have a fallback mechanism. Either we choose instead the "fake center" (or center if fake +% center does not exists), but then the wire will be hidden behind the node (it is a problem +% mostly if you use H nodes a lot, otherwise we recommend this solution): +\def\zxWireInsideIfNoIntersectionName{} +% If you prefer to start the wire on the surface of the node, do that instead... +% but be aware that the shape of the wire will be changed a lot, since we can't use anymore the +% fake anchors... You could also define a style that does that. +% \let\zxWireInsideIfNoIntersectionName\undefined + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%% Adding anchors +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% We add anchors "fake center {north, south, east, west}" to the nodes. The wires will leave from these anchors (except in IO mode) depending on the direction. If the anchor does not exist, center is picked. +% https://tex.stackexchange.com/questions/14769/add-more-anchors-to-standard-tikz-nodes +\def\zx@pgfaddtoshape#1#2{% + \begingroup + \def\pgf@sm@shape@name{#1}% + \let\anchor\pgf@sh@anchor + #2% + \endgroup +} +\def\zx@useanchor#1#2{\csname pgf@anchor@#1@#2\endcsname} +\zx@pgfaddtoshape{rounded rectangle}{% + \anchor{fake center east}{% + \zx@useanchor{rounded rectangle}{north east}% + \pgf@yc=.5\pgf@y% final y = 0.5*this y + 0.5*other y. + \zx@useanchor{rounded rectangle}{south east}% + \pgf@y=.5\pgf@y% + \advance\pgf@y by \pgf@yc% + }% + \anchor{fake center west}{% + \zx@useanchor{rounded rectangle}{north west}% + \pgf@yc=.5\pgf@y% final y = 0.5*this y + 0.5*other y. + \zx@useanchor{rounded rectangle}{south west}% + \pgf@y=.5\pgf@y% + \advance\pgf@y by \pgf@yc% + }% + \anchor{fake center north}{% + \zx@useanchor{rounded rectangle}{center}% + }% + \anchor{fake center south}{% + \zx@useanchor{rounded rectangle}{center}% + }% +} + +\zx@pgfaddtoshape{coordinate}{% + \anchor{fake center east}{% + \zx@useanchor{coordinate}{center}% + }% + \anchor{fake center west}{% + \zx@useanchor{coordinate}{center}% + }% + \anchor{fake center north}{% + \zx@useanchor{coordinate}{center}% + }% + \anchor{fake center south}{% + \zx@useanchor{coordinate}{center}% + }% +} + +\zx@pgfaddtoshape{circle}{% + \anchor{fake center east}{% + \zx@useanchor{circle}{center}% + }% + \anchor{fake center west}{% + \zx@useanchor{circle}{center}% + }% + \anchor{fake center north}{% + \zx@useanchor{circle}{center}% + }% + \anchor{fake center south}{% + \zx@useanchor{circle}{center}% + }% +} + +\zx@pgfaddtoshape{rectangle}{ + \anchor{fake center east}{% + \zx@useanchor{rectangle}{center}% + }% + \anchor{fake center west}{% + \zx@useanchor{rectangle}{center}% + }% + \anchor{fake center north}{% + \zx@useanchor{rectangle}{center}% + }% + \anchor{fake center south}{% + \zx@useanchor{rectangle}{center}% + }% +} + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Tikz styles %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% For arguments later: +\tikzset{ + /zx/args/-andL/.cd, + % - is "towards the point", or "x value", + % L is "perpendicular to the line going towards the point" (L is for the perpendicular shape), or "y value". + 1-/.initial=.5, % random value, defaultO... will change that. + 2-/.initial=.5, + -/.style={ + 1-=#1, + 2-=#1, + }, + 1L/.initial=.5, + 2L/.initial=.5, + L/.style={ + 1L=#1, + 2L=#1, + }, + symmetry-L/.code={% 1- <-> 1L, 2- <-> 2L + \edef\zx@tmpone{\pgfkeysvalueof{/zx/args/-andL/1-}}% + \edef\zx@tmptwo{\pgfkeysvalueof{/zx/args/-andL/2-}}% + \pgfkeysalso{% + 1-/.evaluated=\pgfkeysvalueof{/zx/args/-andL/1L}, + 2-/.evaluated=\pgfkeysvalueof{/zx/args/-andL/2L}, + 1L/.evaluated=\zx@tmpone, + 2L/.evaluated=\zx@tmptwo, + }% + }, + symmetry/.code={% 1- <-> 2-, 1L <-> 2L + \edef\zx@tmpone{\pgfkeysvalueof{/zx/args/-andL/1-}}% + \edef\zx@tmptwo{\pgfkeysvalueof{/zx/args/-andL/1L}}% + \pgfkeysalso{ + 1-/.evaluated=\pgfkeysvalueof{/zx/args/-andL/2-}, + 1L/.evaluated=\pgfkeysvalueof{/zx/args/-andL/2L}, + 2-/.evaluated=\zx@tmpone, + 2L/.evaluated=\zx@tmptwo, + }% + }, + negate1L/.style={ + /zx/args/-andL/1L/.evaluated=-\pgfkeysvalueof{/zx/args/-andL/1L}, + }, + negate2L/.style={ + /zx/args/-andL/2L/.evaluated=-\pgfkeysvalueof{/zx/args/-andL/2L}, + }, + negateL/.style={ + negate1L, + negate2L, + }, + negate1-/.style={ + /zx/args/-andL/1-/.evaluated=-\pgfkeysvalueof{/zx/args/-andL/1-}, + }, + negate2-/.style={ + /zx/args/-andL/2-/.evaluated=-\pgfkeysvalueof{/zx/args/-andL/2-}, + }, + negate-/.style={ + negate1-, + negate2-, + }, + negateL/.style={ + negate1L, + negate2L, + }, + oneMinus1-/.style={ + /zx/args/-andL/2-/.evaluated=1-\pgfkeysvalueof{/zx/args/-andL/2-}, + }, + oneMinus2-/.style={ + /zx/args/-andL/2-/.evaluated=1-\pgfkeysvalueof{/zx/args/-andL/2-}, + }, + oneMinus1L/.style={ + /zx/args/-andL/1L/.evaluated=1-\pgfkeysvalueof{/zx/args/-andL/1L}, + }, + oneMinus2L/.style={ + /zx/args/-andL/2L/.evaluated=1-\pgfkeysvalueof{/zx/args/-andL/2L}, + }, + % Angle/length: + 1 angle and length/.code 2 args={% + \pgfmathparse{#2*cos(#1)}% + \pgfkeysalso{1-/.expand once=\pgfmathresult}% + \pgfmathparse{#2*sin(#1)}% + \pgfkeysalso{1L/.expand once=\pgfmathresult}% + }, + 2 angle and length/.code 2 args={% + \pgfmathparse{#2*cos(#1)}% + \pgfkeysalso{2-/.expand once=\pgfmathresult}% + \pgfmathparse{#2*sin(#1)}% + \pgfkeysalso{2L/.expand once=\pgfmathresult}% + }, + 1al/.style 2 args={ + 1 angle and length={#1}{#2} + }, + 2al/.style 2 args={ + 2 angle and length={#1}{#2} + }, + al/.style 2 args={ + 1 angle and length={#1}{#2}, + 2 angle and length={#1}{#2}, + }, + 1 angle/.style={ + 1 angle and length={#1}{.6} + }, + 2 angle/.style={ + 2 angle and length={#1}{.6} + }, + 1a/.style={ + 1 angle={#1}, + }, + 2a/.style={ + 2 angle={#1}, + }, + angle/.style={ + 1 angle={#1}, + 2 angle={#1}, + }, + a/.style={ + 1 angle and length={#1}{.6}, + 2 angle and length={#1}{.6}, + }, +} + +%%% Useful debug functions +%% This function will be called when debugging the library. +\def\zx@message#1{} +% The ^^J adds a new line: +\def\zx@verbose@message#1{\message{#1^^J}} % For more important messages. Like to show the code is not looping forever +% To enable debugging: +% WARNING: if you use in code, do not forget the makeatletter! +%\def\zx@message#1{\message{#1^^J}} +% To display stroke of while trying to compute the intersections: +% WARNING: if you use in code, do not forget the makeatletter! +%\def\zx@draw@stroke@inter@debug{} + + +%% This function checks if the node has a "name path", if so it computes the intersection between +%% if not if \zxWireInsideIfNoIntersectionName is defined it defines the \tikzto* to the +%% new@center (argument #2), otherwise it does nothing. +% #1: Node to consider: \tikztostart or \tikztotarget +% #2: name of the new@center node corresponding to #1 +% #3: name of the other new@center node +% #4: result will be stored in this macro... if there is something to change. +% #5: 1 if start, 2 if end +% Result will be moved back into #1 +\def\zx@find@intersection@fakecenter#1#2#3#4#5{% + % First, we check if the user wants to compute the intersection: + \ifdefined\zxEnableIntersectionsWires% + %% We compute the intersection + \edef\tikz@intersect@path@a{zx@name@path@#1}% + \ifcsname tikz@intersect@path@name@\tikz@intersect@path@a \endcsname% + \zx@verbose@message{[ZX] I will start computing intersections}% + \pgfintersectionofpaths{% + \begin{pgfinterruptboundingbox}% + %%%% /!\ This lines is the one I'm not sure how to write: + \expandafter\pgfsetpath\csname tikz@intersect@path@name@\tikz@intersect@path@a\endcsname% + \pgfgetpath\temppath% + \ifdefined\zx@draw@stroke@inter@debug% + \pgfusepath{stroke} % We draw it, useful to debug, and realize the shape is moved. + \fi% + \pgfsetpath\temppath% + \end{pgfinterruptboundingbox}% + }{% The first path is moved to the center... So we need to shift it also here. + %%% First intersection: we use the line between the two nodes... but it's really ugly (e.g. in o it does not make a lot of sense) + \begin{pgfinterruptboundingbox}% + \ifdefined\zxIntersectionLineBetweenStartEnd% + %% Not idea why, but x axis is inverted??? + \pgfpointdiff{\tikz@scan@one@point\pgf@process(#2)}{\pgfpointanchor{#1}{center}}% + \pgfpathmoveto{\pgfpoint{-\pgf@x}{\pgf@y}}% + % And now both axis are??? + \pgfpointdiff{\tikz@scan@one@point\pgf@process(#3)}{\pgfpointanchor{#1}{center}}% + \pgfpathlineto{\pgfpoint{-\pgf@x}{-\pgf@y}}% + \else% + %% We use now the method used for bezier curve (at least it's what I see "experimentally") + %% More precisely, we use the direction of the bezier curve to know where to start the wire. + %% Parse once the control points for efficiency reasons: + %% First parse the fake centers. + \tikz@scan@one@point\pgf@process(#2)% + \edef\zx@tmp@current@fake@x{\the\pgf@x}% + \edef\zx@tmp@current@fake@y{\the\pgf@y}% + \zx@message{The input fakes are #2 and #3.}% + \tikz@scan@one@point\pgf@process(#3)% + \edef\zx@tmp@other@fake@x{\the\pgf@x}% + \edef\zx@tmp@other@fake@y{\the\pgf@y}% + \zx@message{The parsed input fakes are (\zx@tmp@current@fake@x , \zx@tmp@current@fake@y) and (\zx@tmp@other@fake@x , \zx@tmp@other@fake@y).}% + %% Reorder the controls to compute. #5 = 1 if "start fake center" is "current fake center". + \ifnum#5=1 % <-- important space!!! + \edef\zx@tmp@start@fake@x{\zx@tmp@current@fake@x}% + \edef\zx@tmp@start@fake@y{\zx@tmp@current@fake@y}% + \edef\zx@tmp@end@fake@x{\zx@tmp@other@fake@x}% + \edef\zx@tmp@end@fake@y{\zx@tmp@other@fake@y}% + \else% + \edef\zx@tmp@end@fake@x{\zx@tmp@current@fake@x}% + \edef\zx@tmp@end@fake@y{\zx@tmp@current@fake@y}% + \edef\zx@tmp@start@fake@x{\zx@tmp@other@fake@x}% + \edef\zx@tmp@start@fake@y{\zx@tmp@other@fake@y}% + \fi% + \zx@message{We computed: start fake (\zx@tmp@start@fake@x , \zx@tmp@start@fake@y) end fake (\zx@tmp@end@fake@x , \zx@tmp@end@fake@y )}% + \pgfkeysalso{/zx/tmp/zx@getControlPoints={\zx@tmp@start@fake@x}{\zx@tmp@start@fake@y}{\zx@tmp@end@fake@x}{\zx@tmp@end@fake@y}}% + % Now results are in controlOne, and controlTwo + \ifnum#5=1 \relax% + \def\zx@control{\controlOne}% + \else% + \def\zx@control{\controlTwo}% + \fi% + %%% Parse new control + \tikz@scan@one@point\pgf@process(\zx@control)% + \edef\zx@tmp@current@ctrl@x{\the\pgf@x}% + \edef\zx@tmp@current@ctrl@y{\the\pgf@y}% + \zx@message{[ZX] Current control is number #5, i.e. \zx@control (both are \controlOne and \controlTwo)}% + % We store the center coordinates since it's used several times + \pgfpointanchor{#1}{center}% + \edef\zx@tmp@real@center@x{\the\pgf@x}% + \edef\zx@tmp@real@center@y{\the\pgf@y}% + %%% Go to current fake center, except that real center is the new origin. + \pgfpointdiff{\pgfpoint{\zx@tmp@real@center@x}{\zx@tmp@real@center@y}}{\pgfpoint{\zx@tmp@current@fake@x}{\zx@tmp@current@fake@y}}% + \pgfpathmoveto{\pgfpoint{\pgf@x}{\pgf@y}}% + %% What, now both axis are inverted??? If someone can explain me this... + %% We scale the point to force it to cross the boundary. + \pgfpathlineto{% + \pgfpointadd{% + \pgf@process{% + \pgfpointscale{10}{% + \pgf@process{% + \pgfpointdiff{% + \pgfpoint{\zx@tmp@current@fake@x}{\zx@tmp@current@fake@y}% + }{% + \pgfpoint{\zx@tmp@current@ctrl@x}{\zx@tmp@current@ctrl@y}% + }% + }% + }% + }% + }{% + \pgfpointdiff{% + \pgfpoint{\zx@tmp@real@center@x}{\zx@tmp@real@center@y}% + }{% + \pgfpoint{\zx@tmp@current@fake@x}{\zx@tmp@current@fake@y}% + }% + }% + }% + \fi% + \pgfgetpath\temppath% + \ifdefined\zx@draw@stroke@inter@debug% + \pgfusepath{stroke}% We draw it, useful to debug, and realize the shape is moved. + \fi% + \pgfsetpath\temppath% + \end{pgfinterruptboundingbox}% + }% + \pgfintersectionsolutions% + \pgfpointintersectionsolution{1}% + \ifnum\pgfintersectionsolutions=1%% The good number of solution has been found! + \zx@message{[ZX] Cool, just found one solution!} + %% Store the intersection (warning: the center of the shape is moved to the center!) + \edef\zx@relinter@x{\the\pgf@x}% + \edef\zx@relinter@y{\the\pgf@y}% + %% Because the shape was moved to center, we shift it back by adding the coord of the shape: + \pgfextractx\pgf@xa{\pgfpointanchor{#1}{center}}% + \pgfextractx\pgf@xb{\pgfpointanchor{#1}{center}}% + % WARNING! pgfmath removes the dimension (converted in pt). Make sure to put them back after + \pgfmathsetmacro{\zx@inter@x}{\pgf@x+\zx@relinter@x}% + \pgfmathsetmacro{\zx@inter@y}{\pgf@y+\zx@relinter@y}% + \edef#4{\zx@inter@x pt,\zx@inter@y pt}% + \else% The bad number of solution has been found + \zx@message{[ZX] WARNING: expecting one solution, found \pgfintersectionsolutions}% + \ifdefined\zxWireInsideIfNoIntersectionName% + % No intersection was found, but still want to use fake center (or center). + % ==> The wire will be inside the shape + % Basically \tikztostart := tikztostart@new@center + \edef#4{#2}% + \fi% Otherwise, do nothing + \fi% + \else% + \zx@message{ZX: no path named \tikz@intersect@path@a was found.}% + \ifdefined\zxWireInsideIfNoIntersectionName% + % No intersection was found, but still want to use fake center (or center). + % ==> The wire will be inside the shape + % Basically \tikztostart := tikztostart@new@center + \edef#4{#2}% + \fi% Otherwise, do nothing + \fi% + \else% + \zx@message{[ZX]: intersection mechanism disabled}% + \ifdefined\zxWireInsideIfNoIntersectionName% + % No intersection was found, but still want to use fake center (or center). + % ==> The wire will be inside the shape + % Basically \tikztostart := tikztostart@new@center + \edef#4{#2}% + \fi% Otherwise, do nothing + \fi% +}% + % Styles. User should not modify "wires definition", but is free to change: % - "/zx/default style nodes/" to change completely the node style % - "/zx/user overlay nodes" to add stuff on current node style @@ -110,7 +565,7 @@ looseness=1.2, }, lw/.style={looseness wires only}, - % Use this when you are drawing lines between none nodes only (like swap gates...) + % Use this when you are drawing lines between none nodes only (like swap gates...) in IO mode between none/.style={ looseness wires only, wire centered @@ -118,6 +573,50 @@ bn/.style={ between none }, + % To debug the bezier curve. + edge above/.code={% + \def\zxEdgesAbove{}% + },% + edge not above/.code={% + \let\zxEdgesAbove\undefined% + }, + control points visible/.code={% + \def\zxControlPointsVisible{}% + },% + control points not visible/.code={% + \let\zxControlPointsVisible\undefined% + }, + % See explaination at the beginning where we define \def\zxEnableIntersectionsWires{} + % Rouhgly, tries to start the wires on the border of the shape on the line between the + % fake anchors. + use intersections/.code={% + \def\zxEnableIntersectionsWires{}% + }, + dont use intersections/.code={% + \let\zxEnableIntersectionsWires\undefined% + }, + ui/.style={ + use intersections, + /zx/args/-andL/ui + }, + intersections mode between start end/.code={% + \def\zxIntersectionLineBetweenStartEnd{}% + }, + intersections mode bezier controls/.code={% + \let\zxIntersectionLineBetweenStartEnd\undefined% + }, + wires behind nodes/.code={% + \def\zxWireInsideIfNoIntersectionName{}% + }, + no wires behind nodes/.code={% + \let\zxWireInsideIfNoIntersectionName\undefined% + }, + intersection between start end/.code={ + \def\zxIntersectionLineBetweenStartEnd{}% + }, + intersection bezier control/.code={ + \let\zxIntersectionLineBetweenStartEnd\undefined% + }, % ------------------------------ % Practical stuff to draw lines easily: % Prefer to use these are they can be easily customized for each style and shorter to type. @@ -133,7 +632,7 @@ % If only tex was a functional language... https://tex.stackexchange.com/questions/618955 C@generic/.style n args={8}{ % min/max, angle1, angle2, anchor, \x or \y, \y or \x, where to move,radius code (for circle should be "radius=\n3") to path={ - \pgfextra{ %% <- we will use def... so need to "exit" a few seconds pgf + \pgfextra{%% <- we will use def... so need to "exit" a few seconds pgf % Test if tikztostart is a point or a node, and define StartPoint accordingly. \ifPgfpointOrNode{\tikztostart}{% \def\StartPoint{\tikztostart}% @@ -163,24 +662,32 @@ }% \ifpgfmathcomparison\else -- ##7\fi %%%% Version 1: - \pgfextra{ - \pgfmathparse{ + \pgfextra{% + \pgfmathparse{% ifthenelse(##61<##62, % if end angle < angle, draw clockwis "arc[start angle=##3,end angle=##2,##8]",% "arc[start angle=##2,end angle=##3,##8]"% )% - } - } - \pgfmathresult + }% + }% + \pgfmathresult% \pgfextra{% %% We check if we are moving or not (required to preserve arrow tip direction) \pgfmathapproxequalto{##52}{\n1}% }% \ifpgfmathcomparison\else -- (\TargetPoint)\fi - \tikztonodes % All to path finishes with that to deal with futur nodes I think + \tikztonodes% All to path finishes with that to deal with futur nodes I think } }, - %% + % At the first version, styles were defined using in=... out=... looseness=... However + % it gives sometimes bad results (like the curve goes forward at some points) when nodes are + % too close or too far appart. However, it may still be useful, so now we define the old + % styles, that you can use them using \ar[r,IO,C]. + % NB: for the newest styles, we add new anchors to the nodes depending on the direction. + % I also wanted to add more complicated ways to configqure the starting point (i.e. start at the boundary + % and not behind the node), but seems like it's hard (impossible?) to do with matrices... + % The reason is that the intersection library seems to fail with matrices. + % If you know how to solve that, you can help here: https://tex.stackexchange.com/questions/619274 C/.style={C@generic={min}{90}{180+90}{west}{\x}{\y}{(\n1,\y1)}{y radius=\n3, x radius=##1*\n3}}, C/.default=1, % Like C, but rotated @@ -190,97 +697,568 @@ C'/.default=1, C./.style={C@generic={min}{0}{-180}{south}{\y}{\x}{(\x1,\n1)}{x radius=\n3, y radius=##1*\n3}}, C./.default=1, - % Like a C shape (with without a perfect half circle). This was the old definition of C... - % Hopefully not useful anymore. - c/.style={/tikz/in=180,/tikz/out=180,looseness=2}, - % Like C, but symetric - c-/.style={/tikz/in=0,/tikz/out=0,looseness=2}, - c'/.style={/tikz/in=90,/tikz/out=90,looseness=2}, - c./.style={/tikz/in=-90,/tikz/out=-90,looseness=2}, + %%% bezier{px1}{py1}{px2}{py2} creates a bezier curve where px1/py1 are the + %%% coordinates of the first control (in "percentage" 0<px1<1, between start and end point) and px2/py2 + %%% are the coordinates in % of the second control point + %%% bezier@general is used to construct also x/y versions, 5 and 6'th argument are \x or \y. + bezier@general/.style n args={6}{ + possibly@on@edge@layer/.code={%% see "edges above" style. for debug mode only. + \ifdefined\zxEdgesAbove\else% + \pgfkeysalso{% + on layer=edgelayer% + }% + \fi% + }, + possibly@on@edge@layer, + to path={% + %%% First, check that inputs/outputs are points. + \pgfextra{% <- Type latex code inside path + %% Setup the startup node. This code can be changed by styles + %% /zx/tmp/zx@setup@start@node will contain the anchor of the first fake center + %% /zx/tmp/zx@setup@end@node will contain the anchor of the second fake center + %% If none of them are defined, nothing happens and the start point will be a node. + % Retrieve the content of the keys + % But first check if they are defined + \pgfkeysifdefined{/zx/tmp/zx@setup@start@node}{}{% Put an empty value + \pgfkeysalso{/zx/tmp/zx@setup@start@node/.initial=,}% + }% + \pgfkeysifdefined{/zx/tmp/zx@setup@end@node}{}{% Put an empty value + \pgfkeysalso{/zx/tmp/zx@setup@end@node/.initial=,}% + }% + \pgfkeysalso{ + /zx/tmp/zx@setup@start@node/.get=\zx@anchor@start@node, + /zx/tmp/zx@setup@end@node/.get=\zx@anchor@end@node, + /zx/tmp/zx@getControlPoints/.code n args={4}{% + \zx@message{I will evaluate my control points:}% + % Args: start point, x,y then end point x,y + % Use ########1 ... (yes, LaTeX is a funny exponential language, and I love nested functions) + % Because to path is considered as a function already. + \def\x################1{% + \ifnum################1=1 % <-- super important space... + ########1% + \else% + ########3% + \fi% + }% + \zx@message{[ZX] x is defined.}% + \def\y################1{% + \ifnum ################1 = 1 % <-- super important space... + ########2% + \else% + ########4% + \fi% + }% + \tikz@scan@one@point\pgf@process(\x1-##51*##1+##52*##1,\y1-##61*##2+##62*##2)% + \edef\controlOne{\the\pgf@x,\the\pgf@y}% + \tikz@scan@one@point\pgf@process(\x1-##51*##3+##52*##3,\y1-##61*##4+##62*##4)% + \edef\controlTwo{\the\pgf@x,\the\pgf@y}% + \zx@message{[ZX] First control point is: \controlOne, second is \controlTwo.}% + },% + }% + %%% This will be the new \tikztostart and \tikztotarget... if someone defines them + \let\new@tikztostart\undefined% + \let\new@tikztotarget\undefined% + % Check if a start anchor was defined... + \ifthenelse{\equal{\zx@anchor@start@node}{}}{% + % No start anchor was chosen... :c + \ifthenelse{\equal{\zx@anchor@end@node}{}}{% + % Nothing was defined... So I won't do anything! :C + }{% + % An end node was defined :D Let's configure its end anchor. + \pgfkeysalso{% + /zx/utils/change@tikz@fake@center@intersect={\tikztotarget}{\zx@anchor@end@node}{\tikztostart}{center}{\new@tikztotarget}{2}, + }% + }% + }{% A start node was chosen :D + \ifthenelse{\equal{\zx@anchor@end@node}{}}{% + % No end node was chosen... I'll take center to configure the start node, + % and not configure the end node. + \pgfkeysalso{ + /zx/utils/change@tikz@fake@center@intersect={\tikztostart}{\zx@anchor@start@node}{\tikztotarget}{center}{\new@tikztostart}{1}, + }% + }{% + % And an end node was chosen :D + \zx@message{[ZX] Both anchors are set. Lets configure intersection.}% + \pgfkeysalso{ + /zx/utils/change@tikz@fake@center@intersect={\tikztostart}{\zx@anchor@start@node}{\tikztotarget}{\zx@anchor@end@node}{\new@tikztostart}{1}, + /zx/utils/change@tikz@fake@center@intersect={\tikztotarget}{\zx@anchor@end@node}{\tikztostart}{\zx@anchor@start@node}{\new@tikztotarget}{2}, + }% + }% + }% + \ifdefined\new@tikztostart% + \edef\tikztostart{\new@tikztostart}% + \fi% + \ifdefined\new@tikztotarget% + \edef\tikztotarget{\new@tikztotarget}% + \fi% + %%% Test if tikztostart is a point or a node, and define StartPoint accordingly. + % \ifPgfpointOrNode{\tikztostart}{% + % \def\StartPoint{\tikztostart}% + % }{% + % \def\StartPoint{\tikztostart.center}% + % }% + % % Test if tikztostart is a point or a node, and define StartPoint accordingly. + % \ifPgfpointOrNode{\tikztotarget}{% + % \def\TargetPoint{\tikztotarget}% + % }{% + % \def\TargetPoint{\tikztotarget.center}% + % }% + %%% Well, let's just use \tikztostart and \tikztotarget in any case, setting it to + %%% center will put the wire below the node which is not really desired for H nodes + %%% (position will be wrong) or when using arrows. And if center is really desired + %%% it's easy to force it using start anchor and end anchor. + \def\StartPoint{\tikztostart}% + \def\TargetPoint{\tikztotarget}% + %%% TODO: check that it does not change the shape of the wire since I'm not sure what \x1 + %%% is in that case% + }% + let + \p1=(\StartPoint), + \p2=(\TargetPoint),%% ##5 = \x or \y, so ##51 = \x1 or \y1. + \p{control1}=(\x1-##51*##1+##52*##1, \y1-##61*##2+##62*##2), + \p{control2}=(\x1-##51*##3+##52*##3, \y1-##61*##4+##62*##4) in + (\StartPoint) .. controls (\p{control1}) and (\p{control2}) .. (\TargetPoint) + \ifdefined\zxControlPointsVisible + (\StartPoint) node[anchor=center,circle,fill=yellow,inner sep=.5pt,on layer=foreground]{} + -- (\p{control1}) node[anchor=center,circle,fill=orange,inner sep=.5pt,on layer=foreground]{} + (\p{control2}) node[anchor=center,circle,fill=purple,inner sep=.5pt,on layer=foreground]{} + -- (\TargetPoint) node[anchor=center,circle,fill=brown,inner sep=.5pt,on layer=foreground]{} + \fi + \tikztonodes + }, + }, + %% Not sure it will be very useful, compared to bezier, bezier x and bezier y directly. + bezier 4/.style n args={4}{ + bezier@general={##1}{##2}{##3}{##4}{\x}{\y}, + }, + % Like bezier, all proportions are relative to x distance + bezier 4 x/.style n args={4}{ + bezier@general={##1}{##2}{##3}{##4}{\x}{\x} + }, + % Like bezier, all proportions are relative to y distance + bezier 4 y/.style n args={4}{ + bezier@general={##1}{##2}{##3}{##4}{\y}{\y} + }, + bezier/.code={% + \pgfkeys{ + /zx/args/-andL/.cd, + ##1 + }% + \pgfkeysalso{ + bezier 4={\pgfkeysvalueof{/zx/args/-andL/1-}}{\pgfkeysvalueof{/zx/args/-andL/1L}}{\pgfkeysvalueof{/zx/args/-andL/2-}}{\pgfkeysvalueof{/zx/args/-andL/2L}} + }% + }, + bezier x/.code={% + \pgfkeys{ + /zx/args/-andL/.cd, + ##1 + }% + \pgfkeysalso{ + bezier 4 x={\pgfkeysvalueof{/zx/args/-andL/1-}}{\pgfkeysvalueof{/zx/args/-andL/1L}}{\pgfkeysvalueof{/zx/args/-andL/2-}}{\pgfkeysvalueof{/zx/args/-andL/2L}} + }% + }, + bezier y/.code={% + \pgfkeys{ + /zx/args/-andL/.cd, + ##1 + }% + \pgfkeysalso{ + bezier 4 y={\pgfkeysvalueof{/zx/args/-andL/1-}}{\pgfkeysvalueof{/zx/args/-andL/1L}}{\pgfkeysvalueof{/zx/args/-andL/2-}}{\pgfkeysvalueof{/zx/args/-andL/2L}} + }% + }, + % Directions: configure start/end points to "fake center *" if it exists. + % If the input is not a node, it's copied to ##3 + /zx/utils/change@tikz@fake@center/.code n args={3}{% + % ##1: \tikztostart or \tikztotarget, + % ##2: fake center north... + % ##3 name of result. e.g. \tikztostart + \ifPgfpointOrNode{##1}{% + %%% Sometimes, ##1 is not 1pt,2pt but "tikz-1-2.north east". Since later we use + %%% a parsing mechanism which assumes that we have a coordinate... this is a problem. + \edef##3{##1}% + % This is a great explaination of how parsing is done in tikz: + % https://tex.stackexchange.com/a/516897/116348 + }{% This is a node, not a point: + \ifAnchorExists{##1}{##2}{% + \pgfpointanchor{##1}{##2}% + }{% + \pgfpointanchor{##1}{center}% + }% + %%% Whoo, pgfpoint only redefine pgf@x/pgf@y, but in no way it stores it!!! + %%% So can't do \pgfpoint{\pgf@x}{\pgf@y}... + %%% I needed to create my own system... see \zx@parse@coordx. + \edef##3{\the\pgf@x,\the\pgf@y}% + }% + }, + % Directions: configure start/end points to the intersection between + % the "fake center *" if it exists and the "fake center *" of the destination. + % If the input is not a node, nothing is done. + /zx/utils/change@tikz@fake@center@intersect/.code n args={6}{% + % ##1: \tikztostart or \tikztotarget + % ##2: fake center north... for ##1 + % ##3: \tikztostart or \tikztotarget, the other one (needed to compute the intersection) + % ##4 fake center north... for ##3 + % ##5: result will be moved to that macro + % ##6: 1 if start, 2 if end. + \ifPgfpointOrNode{##1}{}{% This is a node, not a point: + %% Configure the zx@new@center@* which should point to "fake center" if it exists. + \pgfkeysalso{% + /zx/utils/change@tikz@fake@center={##1}{##2}{\zx@new@center},% + }% + \pgfkeysalso{% + /zx/utils/change@tikz@fake@center={##3}{##4}{\zx@new@center@other},% + }% + \zx@message{ZX: coordinate of new center is \zx@new@center}% + \zx@message{ZX: coordinate of new other center is \zx@new@center@other}% + \zx@find@intersection@fakecenter{##1}{\zx@new@center}{\zx@new@center@other}{##5}{##6}% + }% + }, + start fake center east/.style={% + /zx/tmp/zx@setup@start@node/.initial=fake center east, + }, + start fake center west/.style={% + /zx/tmp/zx@setup@start@node/.initial=fake center west, + }, + start fake center north/.style={% + /zx/tmp/zx@setup@start@node/.initial=fake center north, + }, + start fake center south/.style={% + /zx/tmp/zx@setup@start@node/.initial=fake center south, + }, + start real center/.style={ + /zx/tmp/zx@setup@start@node/.initial=center, + }, + start no fake center/.style={ + /zx/tmp/zx@setup@start@node/.initial=, + }, + end fake center east/.style={% + /zx/tmp/zx@setup@end@node/.initial=fake center east, + }, + end fake center west/.style={% + /zx/tmp/zx@setup@end@node/.initial=fake center west, + }, + end fake center north/.style={% + /zx/tmp/zx@setup@end@node/.initial=fake center north, + }, + end fake center south/.style={% + /zx/tmp/zx@setup@end@node/.initial=fake center south, + }, + end real center/.style={ + /zx/tmp/zx@setup@end@node/.initial=center, + }, + end no fake center/.style={ + /zx/tmp/zx@setup@end@node/.initial=, + }, + left to right/.style={ + start fake center east, + end fake center west + }, + right to left/.style={ + start fake center west, + end fake center east + }, + up to down/.style={ + start fake center south, + end fake center north + }, + down to up/.style={ + start fake center north, + end fake center south + }, + no fake center/.style={ + start no fake center, + end no fake center, + start anchor=, + end anchor=, + }, + %% Forced versions are versions which do not nicely fallback to center if shape does not + %% have "fake center XXX". This is used mostly for wires based on in/out (our function b. + %% Not sure how to have a code which always fallback to center if shape does not. + %% All shapes used in the default style should work with force left to right. + force left to right/.style={ + on layer=edgelayer, + start anchor=fake center east, + end anchor=fake center west + }, + force right to left/.style={ + on layer=edgelayer, + start anchor=fake center west, + end fake center east + }, + force up to down/.style={ + on layer=edgelayer, + start anchor=fake center south, + end fake center north + }, + force down to up/.style={ + on layer=edgelayer, + start anchor=fake center north, + end fake center south + }, % Similar to C, but with a softer angle. The '.- marker represents the portion of % the circle (hence the o) to keep (top, bottom,left/right). % Angle is customizable, for instance o'=50. - o'/.style={/tikz/out=##1,/tikz/in=180-##1}, - o'/.default=\zxDefaultSoftAngleO, - o./.style={/tikz/out=-##1,/tikz/in=180+##1}, - o./.default=\zxDefaultSoftAngleO, - -o/.style={/tikz/out=-90-##1,/tikz/in=90+##1}, - -o/.default=\zxDefaultSoftAngleO, - o-/.style={/tikz/out=-90+##1,/tikz/in=90-##1}, - o-/.default=\zxDefaultSoftAngleO, - % Similar to o, but can be used also for diagonal items. - % Why ()? Visualize fixing the top part and moving the bottom part. - (/.style={bend right=##1}, + %%% Actually, it may not be super useful to define it via bezier since it's used only + %%% horizontally and vertically... Anyway. + o'/.style={% + left to right, + bezier x={ + defaultO, + ##1, + %% 2- --> 1 - (2-) + oneMinus2-, + }, + }, + o./.style={% + left to right, + bezier x={% + /zx/args/-andL/.cd, + defaultO, + ##1, + %% 2- --> 1 - (2-) + oneMinus2-, + negate1L, + negate2L, + },% + }, + -o/.style={% + up to down, + bezier y={% + /zx/args/-andL/.cd, + defaultO, + ##1, + /zx/args/-andL/2-/.evaluated=1-\pgfkeysvalueof{/zx/args/-andL/2-}, + symmetry-L, + } + }, + o-/.style={% + up to down, + bezier y={% + /zx/args/-andL/.cd, + defaultO, + ##1, + /zx/args/-andL/2-/.evaluated=1-\pgfkeysvalueof{/zx/args/-andL/2-}, + /zx/args/-andL/1L/.evaluated=-\pgfkeysvalueof{/zx/args/-andL/1L}, + /zx/args/-andL/2L/.evaluated=-\pgfkeysvalueof{/zx/args/-andL/2L}, + symmetry-L, + }, + }, + % Similar to o, but can be used also for diagonal items. Combine it with + % The dot versions are mode logic in "left to right", and the others for up to down (possible to change) + % Can combine with "force left to right"... + (/.style={ + bend right=##1, + }, (/.default=30, - )/.style={bend left=##1}, + )/.style={ + bend left=##1, + }, )/.default=30, - ('/.style={bend left=##1}, + ('/.style={ + bend left=##1 + }, ('/.default=30, - (./.style={bend right=##1}, + (./.style={ + bend right=##1 + }, (./.default=30, - <'/.style={out=##1,in=180,looseness=0.65}, - <'/.default=\zxDefaultSoftAngleChevron, - <./.style={out=-##1,in=180,looseness=0.65}, - <./.default=\zxDefaultSoftAngleChevron, - '>/.style={out=0,in=180-##1,looseness=0.65}, - '>/.default=\zxDefaultSoftAngleChevron, - .>/.style={out=0,in=180+##1,looseness=0.65}, - .>/.default=\zxDefaultSoftAngleChevron, - ^./.style={out=-90+##1,in=90,looseness=0.65}, - ^./.default=\zxDefaultSoftAngleChevron, - .^/.style={out=-90-##1,in=90,looseness=0.65}, - .^/.default=\zxDefaultSoftAngleChevron, - 'v/.style={out=90+##1,in=-90,looseness=0.65}, - 'v/.default=\zxDefaultSoftAngleChevron, - v'/.style={out=90-##1,in=-90,looseness=0.65}, - v'/.default=\zxDefaultSoftAngleChevron, - % Links with a s-like shape. - s/.style={/tikz/out=0,/tikz/in=180,looseness=0.6}, + %%%% Links with a N-shape, i.e. like s shape, but symetric against the diagonal. Equivalently, it's a soft 's' shape with a much wider angle (>45). + %% Nbase will be reused later by < > shapes as well. It's useful to make a distinction between N and + %% Nbase when user will want to overwrite N without overwritting < >. + Nbase/.style={ + left to right, + bezier={ + /zx/args/-andL/.cd, + defaultN, + ##1, + oneMinus2-, + oneMinus2L, + }, + }, + N/.style={Nbase={##1}}, + % With this model, we don't need differences between N' and N. so we can also call it N. + % But keep other notations to allow easy style change (to use IO again for instance). + N'/.style={N={##1}}, + N./.style={N={##1}}, + -N/.style={N={defaultN-,##1}}, + -N'/.style={-N={##1}}, + -N./.style={-N={##1}}, + N-/.style={-N'={symmetry,##1}}, + N'-/.style={N-={##1}}, + N.-/.style={N-={##1}}, + %% Up to down version + NN/.style={N'={symmetry-L,defaultNN,##1},up to down}, + NN./.style={NN={##1}}, + .NN/.style={NN={##1}}, + NIN/.style={NN={defaultNIN,##1}}, + INN/.style={NIN={symmetry,##1}}, + NNI/.style={INN={##1}}, + %%% <' is basically like -N' (historically < was first), we just put an anchor on the east/.... + <'/.style={Nbase={defaultN,defaultN-,defaultChevron,symmetry,##1},end anchor=west}, + <./.style={<'={##1}}, + % Don't use <>^ alone char as style name, it could be useful later for other shortcuts + % (replacement directions) + '>/.style={Nbase={defaultN,defaultN-,defaultChevron,##1},start anchor=east}, + .>/.style={'>={##1}}, + ^./.style={ + Nbase={defaultN,defaultN-,defaultChevron,symmetry-L,defaultNN,defaultNIN,defaultChevronDown,symmetry,##1}, + end anchor=north + }, + .^/.style={^.={##1}}, + 'v/.style={ + Nbase={defaultN,defaultN-,defaultChevron,symmetry-L,defaultNN,defaultNIN,defaultChevronDown,##1}, + start anchor=south + }, + v'/.style={'v={##1}}, + %%%% Links with a s-like shape. + s/.style={ + left to right, + bezier={% + /zx/args/-andL/.cd, + defaultS, + ##1, + oneMinus2-, + oneMinus2L, + } + }, + % like S, but with anchor east and west + S/.style={ + s={##1}, start anchor=east, end anchor=west + }, % -s'.- shapes are like s, but with a soften (customizable like o) angle. % The '. say if you are going up or down, and - forces a sharp angle (- is flat) on the side of the - - s'/.style={/tikz/out=##1,/tikz/in=180+##1}, - s'/.default=\zxDefaultSoftAngleS, - s./.style={/tikz/out=-##1,/tikz/in=180-##1}, - s./.default=\zxDefaultSoftAngleS, - -s'/.style={/tikz/out=0,/tikz/in=180+##1}, - -s'/.default=\zxDefaultSoftAngleS, - -s./.style={/tikz/out=0,/tikz/in=180-##1}, - -s./.default=\zxDefaultSoftAngleS, - s'-/.style={/tikz/out=##1,/tikz/in=180}, - s'-/.default=\zxDefaultSoftAngleS, - s.-/.style={/tikz/out=-##1,/tikz/in=180}, - s.-/.default=\zxDefaultSoftAngleS, + s'/.style={s={defaultS',##1}}, + s./.style={s'={##1}}, + -s/.style={s'={default-S,##1}}, + -s'/.style={-s={##1}}, % To fit with s' notation + -s./.style={-s={##1}}, + s-/.style={-s'={symmetry,##1}}, + s'-/.style={s-={##1}}, + s.-/.style={s-={##1}}, + % Anchor more marked + S/.style={s={##1}, start anchor=east, end anchor=west,}, + -S/.style={-s'={##1},start anchor=east}, + -S'/.style={-S={##1}}, + -S./.style={-S={##1}}, + S-/.style={s'-={##1},end anchor=west}, + S'-/.style={S-={##1}}, + S.-/.style={S-={##1}}, + %%%% Doubling = up to down. % Links with a s-like shape... but read from top to bottom - ss/.style={/tikz/out=0-90,/tikz/in=180-90,looseness=0.6}, + ss/.style={s={symmetry-L,##1},up to down}, + SS/.style={ss={##1}, start anchor=south, end anchor=north}, % -s'.- shapes are like s, but with a soften (customizable like o) angle. - % The '. say if you are going up or down, and - forces a sharp angle (- is flat) on the side of the - - ss./.style={/tikz/out=##1-90,/tikz/in=180-90+##1}, - ss./.default=\zxDefaultSoftAngleS, - .ss/.style={/tikz/out=-##1-90,/tikz/in=180-90-##1}, - .ss/.default=\zxDefaultSoftAngleS, - sIs./.style={/tikz/out=0-90,/tikz/in=180-90+##1}, - sIs./.default=\zxDefaultSoftAngleS, - .sIs/.style={/tikz/out=0-90,/tikz/in=180-90-##1}, - .sIs/.default=\zxDefaultSoftAngleS, - ss.I/.style={/tikz/out=##1-90,/tikz/in=180-90}, - ss.I/.default=\zxDefaultSoftAngleS, - I.ss/.style={/tikz/out=-##1-90,/tikz/in=180-90}, - I.ss/.default=\zxDefaultSoftAngleS, - %%%% Links with a N-shape, i.e. like s shape, but symetric against the diagonal. Equivalently, it's a soft 's' shape with a much wider angle (>45). - N'/.style={/tikz/out=##1,/tikz/in=180+##1}, - N'/.default=\zxDefaultSoftAngleN, - N./.style={/tikz/out=-##1,/tikz/in=180-##1}, - N./.default=\zxDefaultSoftAngleN, - -N'/.style={/tikz/out=0,/tikz/in=180+##1}, - -N'/.default=\zxDefaultSoftAngleN, - -N./.style={/tikz/out=0,/tikz/in=180-##1}, - -N./.default=\zxDefaultSoftAngleN, - N'-/.style={/tikz/out=##1,/tikz/in=180}, - N'-/.default=\zxDefaultSoftAngleN, - N.-/.style={/tikz/out=-##1,/tikz/in=180}, - N.-/.default=\zxDefaultSoftAngleN, + % The '. say if you are going up or down, and 'I' forces a sharp angle (I is flat) on the side of the I + ss./.style={s.={symmetry-L,##1},up to down}, + .ss/.style={ss.={##1}}, + sIs/.style={ss={defaultSIS,##1}}, + sIs./.style={ss.={defaultSIS,##1}}, + .sIs/.style={sIs.={##1}}, + Iss/.style={sIs={symmetry,##1}}, + ssI/.style={Iss={##1}}, + ss.I/.style={sIs.={symmetry,##1}}, + I.ss/.style={ss.I={##1}}, + SIS/.style={sIs={##1},start anchor=south}, + .SIS/.style={.sIs={##1},start anchor=south}, + ISS/.style={ssI={##1},end anchor=north}, + SS.I/.style={ss.I={##1},end anchor=north}, + I.SS/.style={SS.I={##1}}, + SSI/.style={ISS={##1}}, + % At the first version, styles were defined using in=... out=... looseness=... However + % it gives sometimes bad results (like the curve goes forward at some points) when nodes are + % too close or too far appart. However, it may still be useful, so now we define the old + % styles, that you can use them using \ar[r,IO,C]. + IO/.style={ + C/.style={/tikz/in=180,/tikz/out=180,looseness=2}, + % Like C, but symetric + C-/.style={/tikz/in=0,/tikz/out=0,looseness=2}, + C'/.style={/tikz/in=90,/tikz/out=90,looseness=2}, + C./.style={/tikz/in=-90,/tikz/out=-90,looseness=2}, + % Similar to C, but with a softer angle. The '.- marker represents the portion of + % the circle (hence the o) to keep (top, bottom,left/right). + % Angle is customizable, for instance o'=50. + o'/.style={/tikz/out=####1,/tikz/in=180-####1}, + o'/.default=\zxDefaultSoftAngleO, + o./.style={/tikz/out=-####1,/tikz/in=180+####1}, + o./.default=\zxDefaultSoftAngleO, + -o/.style={/tikz/out=-90-####1,/tikz/in=90+####1}, + -o/.default=\zxDefaultSoftAngleO, + o-/.style={/tikz/out=-90+####1,/tikz/in=90-####1}, + o-/.default=\zxDefaultSoftAngleO, + % Similar to o, but can be used also for diagonal items. + % Why ()? Visualize fixing the top part and moving the bottom part. + (/.style={bend right=####1}, + (/.default=30, + )/.style={bend left=####1}, + )/.default=30, + ('/.style={bend left=####1}, + ('/.default=30, + (./.style={bend right=####1}, + (./.default=30, + <'/.style={out=####1,in=180,looseness=0.65}, + <'/.default=\zxDefaultSoftAngleChevron, + <./.style={out=-####1,in=180,looseness=0.65}, + <./.default=\zxDefaultSoftAngleChevron, + '>/.style={out=0,in=180-####1,looseness=0.65}, + '>/.default=\zxDefaultSoftAngleChevron, + .>/.style={out=0,in=180+####1,looseness=0.65}, + .>/.default=\zxDefaultSoftAngleChevron, + ^./.style={out=-90+####1,in=90,looseness=0.65}, + ^./.default=\zxDefaultSoftAngleChevron, + .^/.style={out=-90-####1,in=90,looseness=0.65}, + .^/.default=\zxDefaultSoftAngleChevron, + 'v/.style={out=90+####1,in=-90,looseness=0.65}, + 'v/.default=\zxDefaultSoftAngleChevron, + v'/.style={out=90-####1,in=-90,looseness=0.65}, + v'/.default=\zxDefaultSoftAngleChevron, + % Links with a s-like shape. + s/.style={/tikz/out=0,/tikz/in=180,looseness=0.6}, + % -s'.- shapes are like s, but with a soften (customizable like o) angle. + % The '. say if you are going up or down, and - forces a sharp angle (- is flat) on the side of the - + s'/.style={/tikz/out=####1,/tikz/in=180+####1}, + s'/.default=\zxDefaultSoftAngleS, + s./.style={/tikz/out=-####1,/tikz/in=180-####1}, + s./.default=\zxDefaultSoftAngleS, + -s'/.style={/tikz/out=0,/tikz/in=180+####1}, + -s'/.default=\zxDefaultSoftAngleS, + -s./.style={/tikz/out=0,/tikz/in=180-####1}, + -s./.default=\zxDefaultSoftAngleS, + s'-/.style={/tikz/out=####1,/tikz/in=180}, + s'-/.default=\zxDefaultSoftAngleS, + s.-/.style={/tikz/out=-####1,/tikz/in=180}, + s.-/.default=\zxDefaultSoftAngleS, + % Links with a s-like shape... but read from top to bottom + ss/.style={/tikz/out=0-90,/tikz/in=180-90,looseness=0.6}, + % -s'.- shapes are like s, but with a soften (customizable like o) angle. + % The '. say if you are going up or down, and - forces a sharp angle (- is flat) on the side of the - + ss./.style={/tikz/out=####1-90,/tikz/in=180-90+####1}, + ss./.default=\zxDefaultSoftAngleS, + .ss/.style={/tikz/out=-####1-90,/tikz/in=180-90-####1}, + .ss/.default=\zxDefaultSoftAngleS, + sIs./.style={/tikz/out=0-90,/tikz/in=180-90+####1}, + sIs./.default=\zxDefaultSoftAngleS, + .sIs/.style={/tikz/out=0-90,/tikz/in=180-90-####1}, + .sIs/.default=\zxDefaultSoftAngleS, + ss.I/.style={/tikz/out=####1-90,/tikz/in=180-90}, + ss.I/.default=\zxDefaultSoftAngleS, + I.ss/.style={/tikz/out=-####1-90,/tikz/in=180-90}, + I.ss/.default=\zxDefaultSoftAngleS, + %%%% Links with a N-shape, i.e. like s shape, but symetric against the diagonal. Equivalently, it's a soft 's' shape with a much wider angle (>45). + N'/.style={/tikz/out=####1,/tikz/in=180+####1}, + N'/.default=\zxDefaultSoftAngleN, + N./.style={/tikz/out=-####1,/tikz/in=180-####1}, + N./.default=\zxDefaultSoftAngleN, + -N'/.style={/tikz/out=0,/tikz/in=180+####1}, + -N'/.default=\zxDefaultSoftAngleN, + -N./.style={/tikz/out=0,/tikz/in=180-####1}, + -N./.default=\zxDefaultSoftAngleN, + N'-/.style={/tikz/out=####1,/tikz/in=180}, + N'-/.default=\zxDefaultSoftAngleN, + N.-/.style={/tikz/out=-####1,/tikz/in=180}, + N.-/.default=\zxDefaultSoftAngleN, + }, % No line but vdots/dots in between. - 3 vdots/.style={draw=none, "\makebox[0pt][r]{##1}\scalebox{\zxScaleDots}{$\cvdots$}" anchor=center}, + 3 vdots/.style={draw=none, "\makebox[0pt][r]{##1}\scalebox{\zxScaleDots}{$\cvdotsCenterMathline$}" anchor=center}, 3 vdots/.default={}, 3 dots/.style={draw=none, "\makebox[0pt][r]{##1}\scalebox{\zxScaleDots}{$\chdots$}" anchor=center}, 3 dots/.default={}, @@ -288,9 +1266,12 @@ % a new column/row. However, make sure the corresponding row/column is larger, using &[\zxHCol] % for columns and \\[\zxHRow] for rows (for Z/X style, use zxSCol and sxSRow), if you have both spiders % and Hadamard, use \zxHSCol and \zxHSRow. - H/.style={"" {zxHSmall,anchor=center}}, - Z/.style={"" {zxNoPhaseSmallZ,anchor=center}}, - X/.style={"" {zxNoPhaseSmallX,anchor=center}}, + H/.style={"" {zxHSmall,anchor=center,##1}}, + H/.default={}, + Z/.style={"" {zxNoPhaseSmallZ,anchor=center,##1}}, + Z/.default={}, + X/.style={"" {zxNoPhaseSmallX,anchor=center,##1}}, + X/.default={}, % Arrow will go out from the center of the shape instead of from the border. Useful % when connecting nodes with different shapes, it will give back a symetric connection. wire centered/.style={ @@ -319,7 +1300,7 @@ % Style for empty nodes zxAllNodes/.style={ shape=rectangle, % Otherwise nodes are asymetrical rectangle, which is not practical in our case. Gives notably anchor "center" which is really centered compared to asymatrical rectangles - anchor=center, % Center cells + anchor=center, % Center cells line width=\zxDefaultLineWidth, execute at begin node={\thinmuskip=0mu\medmuskip=0mu\thickmuskip=0mu}, % Reduce space around +/-... }, @@ -372,9 +1353,20 @@ zxNoneDouble+, inner ysep=0mm, }, + % Used to compute the intersection with the node's boundary + allow boundary intersection/.code={% + \ifdefined\zxEnableIntersectionsNodes% + \edef\zx@name@path{zx@name@path@\tikz@fig@name}% + \zx@message{[ZX] The name of the path will be \zx@name@path.}% + \pgfkeysalso{ + name path=\zx@name@path, % Used by intersection library + }% + \fi% + }, % Will be specific to all spiders zxSpiders/.style={ draw=black, + allow boundary intersection,% Used later to do math on it. }, % Will use this style when drawing a X/Z node without phase (not for end user directly) zxNoPhase/.style={ @@ -388,7 +1380,7 @@ zxNoPhaseSmall/.style={ zxNoPhase }, - % Style for nodes that are small enough to fit in a circle, like $\zxMinus \frac{\pi}{4}$ + % Style for nodes that are small enough to fit in a circle, like $\zxMinus \frac{\pi}{4}$ or $- \alpha$ zxShort/.style={ zxAllNodes, zxSpiders, @@ -397,7 +1389,7 @@ rounded rectangle, inner sep=0.0mm, scale=0.8, - }, % negative outer sep would draw lines from inside... + },% negative outer sep would draw lines from inside... % Style for nodes that are bigger, like $\alpha+\beta$ or $(a\oplus b)\pi$ zxLong/.style={zxShort, inner xsep=1.2mm}, %%% Styles of the label when |phase in label| is used @@ -429,14 +1421,16 @@ %%% Instead of adding directly the style as the node's content (which would make %%% impossible styles that adds the phase in a label outside of the node) %%% add@Phase@Spider{,Z,X}={phase of the node} will be in charge of adding it. - add@Phase@Spider/.style n args={4}{ % add@Phase@Spider{emptyStyle}{NotEmptyStyle}{node content} + % add@Phase@Spider{emptyStyle}{ShortStyle}{LongStyle}{label style}{node content} + add@Phase@Spider/.style n args={5}{% zx@emptyStyle/.style={##1}, - zx@notEmptyStyle/.style={##2}, - zx@labelStyle/.style={##3}, - /zx/zx@content/.initial={##4}, + zx@shortStyle/.style={##2}, + zx@notEmptyStyle/.style={##3}, + zx@labelStyle/.style={##4}, + /zx/zx@content/.initial={##5}, phase in content, }, - add@Phase@Spider@Frac/.style n args={8}{ % add@Phase@Spider{emptyStyle}{NotEmptyStyle}{labelstyle}{sign}{above fraction (no parens)}{below fraction (no parens)}{above fraction (parens)}{below fraction (parens)} + add@Phase@Spider@Frac/.style n args={8}{% add@Phase@Spider{emptyStyle}{NotEmptyStyle}{labelstyle}{sign}{above fraction (no parens)}{below fraction (no parens)}{above fraction (parens)}{below fraction (parens)} zx@emptyStyle/.style={##1}, zx@notEmptyStyle/.style={##2}, zx@labelStyle/.style={##3}, @@ -446,8 +1440,8 @@ }, % #1 is the node content. Seems that storing it in /zx/zx@content is not enough because keys % seems to be local to nodes and are not transfered to label. - zx@Execute@Very@End/.style n args={4}{ - zx@commandToExecuteVeryEnd/.try={##1}{##2}{##3}{##4}, + zx@Execute@Very@End/.style n args={5}{ + zx@commandToExecuteVeryEnd/.try={##1}{##2}{##3}{##4}{##5}, }, zx@Execute@Very@End/.default={}{}{}{}, %% zx@Execute@Very@End@Frac={emptystyle}{contentstyle}{labelstyle}{sign}{above frac (no parens)}{below frac (no parens)}{above frac (parens)}{below frac (parens)} @@ -478,8 +1472,8 @@ %% Modifies zx@commandToExecuteVeryEnd (which is executed at the very end by zx@Execute@Very@End) %% in order to add the good style \pgfkeysalso{ - zx@commandToExecuteVeryEnd/.style n args={4}{% - execute at begin node={####4},% ####4 = content + zx@commandToExecuteVeryEnd/.style n args={5}{% + execute at begin node={####5},% ####4 = content }% }% % Checks if the content (stored by add@Phase@Spider in /zx/zx@content) is empty or not @@ -487,9 +1481,15 @@ \pgfkeysalso{% zx@emptyStyle,% }% - }{% - \pgfkeysalso{% - zx@notEmptyStyle,% + }{% We check if we need to force "short mode" (zxShort instead of zxLong) + \ifthenelse{\equal{\pgfkeysvalueof{/zx/zx@shortModeForced}}{true}}{% + \pgfkeysalso{% + zx@shortStyle,% + }% + }{% Otherwise, just use the default style. + \pgfkeysalso{% + zx@notEmptyStyle,% + }% }% }% }% @@ -510,12 +1510,12 @@ }{% \pgfkeysalso{% % ##1 is the argument of "phase in label", i.e. the style of the label - zx@commandToExecuteVeryEnd/.code n args={4}{% ####3: label style, ####4: content + zx@commandToExecuteVeryEnd/.code n args={5}{% ####4: label style, ####5: content % Checks if the content (stored by add@Phase@Spider in /zx/zx@content) is empty or not - \ifthenelse{\equal{####4}{}}{% Content is empty + \ifthenelse{\equal{####5}{}}{% Content is empty }{% Content is not empty \pgfkeysalso{ - label={[####3,##1] ####4},% + label={[####4,##1] ####5},% }% }% },% @@ -541,26 +1541,70 @@ }, pill/.style={phase in label left=##1}, %%% Was supposed to automatically find the good style depending on content... Can't find how to do. - % Styles zxLong{X/Z} zxNoPhase{X/Z} are automatically selected by \zxZ2{...} and \zxX2{...} commands + % Styles zxLong{X/Z} zxNoPhase{X/Z} are automatically selected by \zxZ4{...} and \zxX4{...} commands % and zxShort is selected for fractions only like in \zxFracZ-{\pi}{4} % zxZ/.style={zxChoose={##1},fill=colorZxZ}, % zxX/.style={zxChoose={##1},fill=colorZxX}, - %%% First argument is additional style. Second argument is content of node. - zxZ2/.style 2 args={ - add@Phase@Spider={zxNoPhaseZ}{zxLongZ}{stylePhaseInLabelZ}{##2}, - /zx/post preparation labels, - /zx/user post preparation labels, - %/zx/user overlay nodes, - ##1, - zx@Execute@Very@End={zxNoPhaseZ}{zxLongZ}{stylePhaseInLabelZ}{##2}, + %%% First argument is additional style. + %%% Second argument is the minus mode: "-" for minus sign, which forces short mode. + %%% Third argument is "*" for forced short mode. + %%% 4th argument is content of node. + zx@spider/.code n args={8}{ + %% ##1: zxnophase style + %% ##2: zxshort style + %% ##3: zxlong style + %% ##4: label style + %% user provided: + %% ##5: additional tikz options + %% ##6: minus "-" or empty, like "-alpha" + %% ##7: star "*" or empty, to force short mode + %% ##8: content + %%% The argument is a minus. Like \zxZ-{\alpha}, goal is to typeset "-\alpha" in short mode. + \ifthenelse{\equal{##6}{-}}{% It's a minus! + \pgfkeysalso{% We update the content to "-content" + /zx/zx@shortModeForced/.initial={true},% + }% + }{}% + \ifthenelse{\equal{##7}{*}}{% We force short mode! + \pgfkeysalso{% + /zx/zx@shortModeForced/.initial={true},% + }% + }{}% + % It's a minus, and LaTeX is... grrrr. How to get a cleaner code? Tried def/let, not working. + \ifthenelse{\equal{##6}{-}}{% + \pgfkeysalso{% + add@Phase@Spider={##1}{##2}{##3}{##4}{\zxMinusInShort##8},% + }% + }{% + \pgfkeysalso{% + add@Phase@Spider={##1}{##2}{##3}{##4}{##8},% + }% + }% + \pgfkeysalso{% + /zx/post preparation labels, + /zx/user post preparation labels, + % /zx/user overlay nodes, + ##5, + }% + \ifthenelse{\equal{##6}{-}}{% It's a minus, and LaTeX is... grrrr + \pgfkeysalso{% + zx@Execute@Very@End={##1}{##2}{##3}{##4}{\zxMinusInShort##8},% + }% + }{% + \pgfkeysalso{% + zx@Execute@Very@End={##1}{##2}{##3}{##4}{##8},% + }% + }% }, - %% ##1: other styles, ##2: content - zxX2/.style 2 args={ - add@Phase@Spider={zxNoPhaseX}{zxLongX}{stylePhaseInLabelX}{##2}, - /zx/post preparation labels, - /zx/user post preparation labels, - ##1, - zx@Execute@Very@End={zxNoPhaseX}{zxLongX}{stylePhaseInLabelX}{##2}, + %% ##1: additional tikz options + %% ##2: minus "-" or empty, like "-alpha" + %% ##3: star "*" or empty, to force short mode + %% ##4: content + zxZ4/.style n args={4}{ + zx@spider={zxNoPhaseZ}{zxShortZ}{zxLongZ}{stylePhaseInLabelZ}{##1}{##2}{##3}{##4} + }, + zxX4/.style n args={4}{ + zx@spider={zxNoPhaseX}{zxShortX}{zxLongX}{stylePhaseInLabelX}{##1}{##2}{##3}{##4} }, %% These take 6 arguments: additional style, sign (string "-" for minus, nothing for "+", %% otherwise inserted directly), above fraction (no parens), below fraction (no parens), above fraction (parens), below fraction (parens). @@ -610,13 +1654,35 @@ /zx/user overlay wires/.style={ }, /zx/defaultEnv/.style={ - column sep=tiny, - row sep=tiny, + %tiny depends on the size of the font... which we want to keep consistent look + column sep=\zxDefaultColumnSep, + row sep=\zxDefaultRowSep, + zx column sep/.code={% + \def\zxDefaultColumnSep{##1}% + \pgfkeysalso{ + column sep=##1, + }% + }, + zx row sep/.code={% + \def\zxDefaultRowSep{##1}% + \pgfkeysalso{% + row sep=##1, + }% + }, + %column sep=tiny, + %row sep=tiny, % center on the math axis baseline={([yshift=-axis_height]current bounding box.center)}, % Fix 1-row diagram baseline - 1-row diagram/.style={% - /tikz/baseline={([yshift=-axis_height]current bounding box.center)}% + % By default, 1-row diagrams have a different baseline... This package does not want a special case for 1-row diagrams. + 1-row diagram/.style={}, + %% usage: math baseline=wantedBaseline, where you have somewhere \zxX[a=wantedBaseline]{\beta} + math baseline/.style={baseline={([yshift=-axis_height]##1)}}, + %%% Include a diagram in another diagram. + use diagram/.style 2 args={ + execute at end picture={ + \node[fit=##2,inner sep=0pt,yshift=-axis_height]{\zxUseDiagram{##1}}; + } }, % Load (thanks ".search also") our own style /tikz/every node/.style={% @@ -625,6 +1691,15 @@ \pgfkeysalso{% alias=####1,% }% + %% name path won't work with alias... So let's create two name path ^^ + %% https://tex.stackexchange.com/questions/619622 + \ifdefined\zxEnableIntersectionsNodes% + \edef\zx@name@path{zx@name@path@####1}% + \zx@message{[ZX] Name given using an alias: \zx@name@path.}% + \pgfkeysalso{ + name path=\zx@name@path, % Used by intersection library + }% + \fi% \ifdefined\zxDebugMode% \pgfkeysalso{% label={[inner sep=0pt,overlay,red,font={\fontsize{5}{6}}]-45:\scalebox{.5}{####1}} @@ -707,7 +1782,7 @@ \def\node@on@layer{\aftergroup\node@@on@layer} %%% Declare a symbol for a short minus (useful in fractions) -\DeclareMathSymbol{\zxMinus}{\mathbin}{AMSa}{"39} % Requires amssymb +\DeclareMathSymbol{\zxMinus}{\mathbin}{AMSa}{"39}% Requires amssymb %%% Checks if a function is a point or a node. %%% Not sure if best solution (needed to dig into source of TeX), but can't find anything better in manual @@ -720,6 +1795,23 @@ }% } +% shape anchor name if exists if not. Works, while doing \pgfutil@ifundefined{pgf@anchor@\shapenode{}@#2} fails. +% I guess it has to do with the way macro are expanded... +\def\ifAnchorExistsFromShape#1#2#3#4{ + \pgfutil@ifundefined{pgf@anchor@#1@#2}{% + #4% + }{% + #3% + }% +} + +% node name, anchor name, if exists, if not. +\def\ifAnchorExists#1#2#3#4{% + %%% First we extract the shape of the node: + \edef\pgf@node@name{#1}% + \edef\shapenode{\csname pgf@sh@ns@\pgf@node@name\endcsname}% + \ifAnchorExistsFromShape{\shapenode}{#2}{#3}{#4} +} %%% Create different kinds of dots... %% https://tex.stackexchange.com/questions/617959 @@ -750,6 +1842,19 @@ \DeclareRobustCommand{\cvdots}{\cvdotsCenterMathline} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% Practical functions to nest diagrams +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\NewDocumentCommand{\zxSaveDiagram}{mO{}m}{ + \newsavebox{#1}% + \savebox{#1}{\begin{ZX}[ampersand replacement=\&,#2]#3\end{ZX}}% +} + +\NewDocumentCommand{\zxUseDiagram}{m}{ + \usebox{#1}% +} + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Practical macros to automatically choose appropriate style and arrows @@ -762,7 +1867,7 @@ % Useful to put on the left of a node like "n \vdots", linked to the next node. Example: \leftManyDots{n}. % First optional argument is scale of text, second is scale of =. \NewExpandableDocumentCommand{\leftManyDots}{O{1}O{\zxScaleDots}m}{% - |[zxNone+,inner xsep=0pt]| \scalebox{#1}{$#3$\,}\makebox[0pt][l]{\scalebox{#2}{$\cvdots$}} \ar[r,-s.,start anchor=north east] \ar[r,-s',start anchor=south east] \pgfmatrixnextcell% + |[zxNone+,inner xsep=0pt]| \scalebox{#1}{$#3$\,}\makebox[0pt][l]{\scalebox{#2}{$\cvdotsCenterMathline$}} \ar[r,-N.,start anchor=north east] \ar[r,-N',start anchor=south east] \pgfmatrixnextcell[\zxwCol]% } % Useful to link two nodes and put a vdots in between. @@ -772,7 +1877,7 @@ % Like \leftManyDots but on the right. Do *not* create a new node, like in |[zxShortZ]| \alpha \rightManyDots{m} \NewExpandableDocumentCommand{\rightManyDots}{O{1}O{\zxScaleDots}m}{% - \ar[r,s'-,end anchor=north west] \ar[r,s.-,end anchor=south west] \pgfmatrixnextcell |[zxNone+,inner xsep=0pt]| \makebox[0pt][r]{\scalebox{#2}{$\cvdots$}}\scalebox{#1}{\,$#3$} + \ar[r,N'-,end anchor=north west] \ar[r,N.-,end anchor=south west] \pgfmatrixnextcell[\zxwCol] |[zxNone+,inner xsep=0pt]| \makebox[0pt][r]{\scalebox{#2}{$\cvdotsCenterMathline$}}\scalebox{#1}{\,$#3$} } % A swap on one line... Practical mostly to gain space. Must be used with large nodes tough... @@ -786,19 +1891,20 @@ } \NewExpandableDocumentCommand{\zxLoopAboveDots}{O{20}O{}m}{% - \ar[loop,in=90-#1,out=90+#1,looseness=8,min distance=3mm,"\cvdots" {scale=.6,anchor=north,yshift=-0.4mm},#2] + \ar[loop,in=90-#1,out=90+#1,looseness=8,min distance=3mm,"\cvdotsCenterMathline" {scale=.6,anchor=north,yshift=-0.25mm},#2] } + % Usage: node without any style, but may have space. Default is no space, \zxNone+{} is both horizontal % and vertical, \zxNone-{} is only horizontal space, \zxNone|{} is only vertical space. \NewExpandableDocumentCommand{\zxNone}{t+t-t|O{}m}{ - \IfBooleanTF{#1}{ % \zxNone+ + \IfBooleanTF{#1}{% \zxNone+ |[zxNone+,#4]| #5% }{ - \IfBooleanTF{#2}{ % \zxNone- + \IfBooleanTF{#2}{% \zxNone- |[zxNone-,#4]| #5% }{ - \IfBooleanTF{#3}{ % \zxNone + \IfBooleanTF{#3}{% \zxNone |[zxNoneI,#4]| #5% }{% \zxNone |[zxNone,#4]| #5% @@ -809,13 +1915,13 @@ % Usage: alias of \zxNone... To bad token can't be easily forwarded to another function. \NewExpandableDocumentCommand{\zxN}{t+t-t|O{}m}{ - \IfBooleanTF{#1}{ % \zxNone+ + \IfBooleanTF{#1}{% \zxNone+ |[zxNone+,#4]| #5% }{ - \IfBooleanTF{#2}{ % \zxNone- + \IfBooleanTF{#2}{% \zxNone- |[zxNone-,#4]| #5% }{ - \IfBooleanTF{#3}{ % \zxNone + \IfBooleanTF{#3}{% \zxNone |[zxNoneI,#4]| #5% }{% \zxNone |[zxNone,#4]| #5% @@ -826,13 +1932,13 @@ % Cf \zxNone, but with larger space. \NewExpandableDocumentCommand{\zxNoneDouble}{t+t-t|O{}m}{ - \IfBooleanTF{#1}{ % \zxNoneDouble+ + \IfBooleanTF{#1}{% \zxNoneDouble+ |[zxNoneDouble+,#4]| #5% }{ - \IfBooleanTF{#2}{ % \zxNoneDouble- + \IfBooleanTF{#2}{% \zxNoneDouble- |[zxNoneDouble-,#4]| #5% }{ - \IfBooleanTF{#3}{ % \zxNoneDouble + \IfBooleanTF{#3}{% \zxNoneDouble |[zxNoneDoubleI,#4]| #5% }{% \zxNoneDouble |[zxNoneDouble,#4]| #5% @@ -843,14 +1949,14 @@ %% For maximum styling liberty, the content is given directly to the style. % It allows the style to put the phase in a label. -\NewExpandableDocumentCommand{\zxZ}{O{}m}{ - |[zxZ2={#1}{#2}]| % +\NewExpandableDocumentCommand{\zxZ}{O{}t*t-m}{ + |[zxZ4={#1}{\IfBooleanTF{#3}{-}{}}{\IfBooleanTF{#2}{*}{}}{#4}]| % } %% For maximum styling liberty, the content is given directly to the style. %% It allows the style to put the phase in a label. -\NewExpandableDocumentCommand{\zxX}{O{}m}{ - |[zxX2={#1}{#2}]| % +\NewExpandableDocumentCommand{\zxX}{O{}t*t-m}{ + |[zxX4={#1}{\IfBooleanTF{#3}{-}{}}{\IfBooleanTF{#2}{*}{}}{#4}]| % } \NewExpandableDocumentCommand{\zxH}{O{}m}{ @@ -889,13 +1995,14 @@ %%%%% Main environment \begin{ZX}...\end{ZX} \NewDocumentEnvironment{ZX}{O{}}{% \bgroup% + \fontsize{10}{12}\selectfont% Normalize the font size to have consistent drawings everywhere. % Add a switch in case someone really wants the current tikzcd version: \ifdefined\doNotPatchQuantikz% Do not patch tikzcd. \else% Restore locally original tikzcd. \let\tikzcd@\tikzcd@@originalCopyZx% \let\endtikzcd\endtikzcd@originalCopyZx% \fi% - \pgfsetlayers{background,edgelayer,nodelayer,main,box,labellayer,foreground} % Layers are defined locally to avoid to disturb other drawings + \pgfsetlayers{background,edgelayer,nodelayer,main,box,labellayer,foreground}% Layers are defined locally to avoid to disturb other drawings \begin{tikzcd}[% /zx/defaultEnv,% #1]% @@ -935,11 +2042,11 @@ % Check if width < ratio*(height+depth) to see if short style applies \ifdimcomp{\wd\zx@box}{<}{\dimexpr \zxMaxRatio\ht\zx@box + \zxMaxRatio\dp\zx@box\relax}{% #3% Short style is used - }{ % Else + }{% Else #4% Long style is used - } % - }{ + }% + }{% #4% Long style is used - } + }% }% } diff --git a/Master/texmf-dist/tex/latex/zx-calculus/zx-calculus.sty b/Master/texmf-dist/tex/latex/zx-calculus/zx-calculus.sty index d3ce3beea87..cb6a983c1f3 100644 --- a/Master/texmf-dist/tex/latex/zx-calculus/zx-calculus.sty +++ b/Master/texmf-dist/tex/latex/zx-calculus/zx-calculus.sty @@ -2,7 +2,7 @@ % Written by Léo Colisson. Report bugs on https://github.com/leo-colisson/zx-calculus/issues % Published under the MIT license. \NeedsTeXFormat{LaTeX2e} -\ProvidesPackage{zx-calculus}[2021/10/15 A package to typeset ZX calculus in directly in LaTeX, based on tikz/tikzcd.] +\ProvidesPackage{zx-calculus}[2021/10/22 A package to typeset ZX calculus in directly in LaTeX, based on tikz/tikzcd.] \RequirePackage{tikz} |