2

Code:

\[
\begin{tikzcd}[cells={nodes={draw=gray}}]
    \text{Math is excellent!} \ar[r, rightarrow] \ar[d, leftarrow]
        & \text{w} \ar[d, rightarrow]\\
    \text{w} \ar[u, rightarrow] \ar[r, leftarrow]
        & \text{Pythagoreans killed someone!}
\end{tikzcd}
\]

Output:

enter image description here

Requirement: I am trying to achieve something like this:

enter image description here

  1. I know that Case 1 is not possible since the columns (and rows) are formed by centering the cells.

  2. So I try to implement Case 2 which does seem achievable. In particular, I am trying to do this:

    • Make the width of the cells in a column equal.
    • Then somehow write on that cell at a specific place. (Left aligned on the top right, and right aligned on the bottom left.)

Note: I deliberately write "at a specific place", and not just the special cases of left and right alignments.

Can this be done? (I thought of using \phantom. But that obviously gives crude results in this case.)

I know that how to modify the arrow anchor points so it is not a problem.

Atom
  • 665
  • Is tikz-cd mandatory for your future use? If not, I think that a simple TikZ graph would be a better solution. – SebGlav Apr 02 '22 at 22:46
  • I am trying to avoid TikZ since it seems overwhelmingly complicated to me than the friendly and simple tikz-cd. – Atom Apr 02 '22 at 22:50
  • I can hear that, but if you're not to draw more complicated graph than this one, it's pretty straightforward in plain TikZ and would be a pain to achieve in tikz-cd. Hope someone will find a solution for you. – SebGlav Apr 02 '22 at 22:52
  • Can you extend what you mean with at a specific place? Both your cases can be done the way you've drawn them. – Qrrbrbirlbel Feb 20 '23 at 18:10
  • 1
    Could be great if you accepted an answer below, stating that your question has been solved. – SebGlav Feb 21 '23 at 05:13
  • @SebGlav I haven't yet dabbled in the complexities of the codes given in the answers, but have upvoted them. So I don't want to pick one above others yet. But please know that I believe that the answers are great! – Atom Feb 21 '23 at 06:19
  • Your choice, but actually, letting a question open like this means that you're still looking for a fitting answer. It lets other members to check and eventually try once again to answer. But here, your problem seems to be solved (even for the tikz-cd original demand), so that letting the question open is misleading, in my opinion. – SebGlav Feb 21 '23 at 16:30

4 Answers4

7

Just to let you know how to do this in plain TikZ, even if this is not what you're asking for:

four nodes and arrows

\documentclass[12pt]{article}
\usepackage{tikz}
\usetikzlibrary{positioning, arrows.meta}

\begin{document}

\begin{tikzpicture}[
        node distance = 2cm and 5cm,
        every node/.style = {
            rectangle,
            draw,
            inner sep = 5pt,
            minimum size = 1cm},
        > = {Straight Barb}
        ]
    \node[left] (A) {Math is excellent!};
    \node[right, right= of A] (B) {w};
    \node[below = of A.east, anchor = east] (C) {w};
    \node[below = of B.west, anchor = west] (D) {Pythagoreans kill someone!};

    \path[->]
        (A) edge (B)
        (B.south) edge (D.north-|B)
        (D) edge (C)
        (C.north) edge (A.south-|C);
\end{tikzpicture}

\end{document}

There are better ways to do this (but more complex), so I just wrote a simple code.

SebGlav
  • 19,186
6

Here is a tikz-cd solution. The idea is to use \fcolorbox to draw the boxes around the text, and \llap and \rlap to control the arrow placement. Box color is controlled globally with \cdboxcolor, set to gray.

Define new commands \leftbox, \rightbox and \cdbox:

\leftbox{<dim>}{<text>} places <text> in a box that is shifted left so the tikzcd arrow is aimed as if the box had width <dim> instead of its actual width. \rightbox is defined analogously. \cdbox{<text>} does not shift.

enter image description here

\documentclass{article}

\usepackage{tikz-cd,amsmath}

\newcommand{\cdboxcolor}{gray} \newcommand{\leftbox}[2]{\hspace{#1}\llap{\fcolorbox{\cdboxcolor}{white}{#2}}} \newcommand{\rightbox}[2]{\rlap{\fcolorbox{\cdboxcolor}{white}{#2}}\hspace{#1}} \newcommand{\cdbox}[1]{\fcolorbox{\cdboxcolor}{white}{#1}}

\begin{document}

[ \begin{tikzcd}[cells={nodes={inner sep=0pt}}] \leftbox{1cm}{Math is excellent!} \ar[r, rightarrow] \ar[d, leftarrow] & \cdbox{w} \ar[d, rightarrow]\ \cdbox{w} \ar[u, rightarrow] \ar[r, leftarrow] & \rightbox{1cm}{Pythagoreans killed someone!} \end{tikzcd} ]

\end{document}

Sandy G
  • 42,558
  • Interesting idea, but is there any way to adjust the boxes anchors in order to align them to the left or to the right like we do with TikZ? – SebGlav Apr 03 '22 at 09:53
  • 1
    @SebGlav: I couldn't think of an elementary way of doing this. Trying to adjust the anchors in tikz-cd seems much more complicated than learning how to do this with TikZ, as you suggested. :-) – Sandy G Apr 03 '22 at 15:20
5

I'm not quite sure what you mean by

Note: I deliberately write "at a specific place", and not just the special cases of left and right alignments.

What other kind of alignments are there?

Case 1

Case 1 is actually the easiest.

  1. I know that Case 1 is not possible since the columns (and rows) are formed by centering the cells.

No, they aren't. They are aligned by their coordinate systems. The rows align by y = 0 and the columns align by x = 0. (Though, they are some pitfalls.)

Since every node is placed by default at (0, 0) you can just change its anchor. By default, all nodes in a matrix of math nodes are placed at their base anchor.

This means the columns are horizontally centered and the rows are aligned by their baseline. By changing this to base west or base east we can enforce left or right alignment.

In the second diagram I've added some xshifting that changes the position “at a specifical place”.

I'm using my ext.paths.ortho library which provides handy styles following question and answer to “Tikz: purely vertical arrow from nodeA.south to nodeB.north”. The shortcut *| stands for only vertical second and |* stands for only vertical first.

Code

\documentclass[varwidth]{standalone}
\usepackage{tikz-cd}
\tikzset{strut without depth/.style={text depth=+0pt, text height=+\ht\strutbox}}
\tikzcdset{
  column align/.is choice,
  @column align/.style n args={2}{column align/#1/.style={anchor={#2}}},
  @column align/.list={{left}{base west},{right}{base east},{center}{base}},
  colspec/.style={
    /utils/exec=\def\pgfmathresult{0},
    /utils/temp/.code={%
      \edef\pgfmathresult{\pgfinteval{\pgfmathresult+1}}%
      \pgfkeysalso{/tikz/column \pgfmathresult/.append style={
                     /tikz/commutative diagrams/column align/##1}}},
    /utils/temp/.list={#1}}}

\usetikzlibrary{ext.paths.ortho} \begin{document} [ \begin{tikzcd}[ cells={strut without depth, nodes={draw=gray}}, math mode=false, /tikz/ortho/install shortcuts, colspec={right, left}, ] Math is excellent! \ar[r, rightarrow] \ar[d, *|, leftarrow] & w \ar[d, |*, rightarrow] \ w \ar[r, leftarrow] & Pythagoreans killed someone! \end{tikzcd} ] [ \begin{tikzcd}[ cells={strut without depth, nodes={draw=gray}}, math mode=false, /tikz/ortho/install shortcuts, colspec={right, left}, ] Math is excellent! \ar[r, rightarrow] \ar[d, *|, leftarrow] & |[xshift=1cm]| w \ar[d, |*, rightarrow] \ |[xshift=-.5cm]| w \ar[r, leftarrow] & Pythagoreans killed someone! \end{tikzcd} ] \end{document}

Output

enter image description here

Case 2

Again, with purely left, right and center alignment, this is possible.

This is basically the same as case 1 just with a fitted node around the actual contant which uses my ext.node-families library.

I've added a third and centered column to highlight why from' and/or to' is necessary to switch between the smaller nodes and the bigger drawn ones.

We could even invert that toggle (by naming the inner nodes with a ' and the drawn nodes without one).

This uses a custom fit key (that also only scans one node) because TikZ-CD uses a special asymmetrical rectangle shape. For other alignments than left/right/center, i.e. as above with xshift, an adjustment to the fit key is necessary.

We could also do something like I did in my answer for “Make two nodes occupy the space of one node” where you can just go haywire with your placement and place a subnode inside it and have it act almost like a proper node.

Code

\documentclass[varwidth]{standalone}
\usepackage{tikz-cd}
\tikzcdset{
  @column align/.style n args={4}{
    column align/#1/.style={
      anchor={#2}, /tikz/commutative diagrams/@make box of column/.style={
        anchor={#3}, at={(\tikzlastnode.#4)}}}},
  column align/.is choice,
  @column align/.list={
    {left}  {base west}{real west}  {real west},
    {right} {base east}{real east}  {real east},
    {center}{base}     {real center}{real center}},
  colspec/.style={
    /utils/exec=\def\pgfmathresult{0},
    /utils/temp/.code={%
      \edef\pgfmathresult{\pgfinteval{\pgfmathresult+1}}%
      \pgfkeysalso{/tikz/column \pgfmathresult/.append style={
                     /tikz/commutative diagrams/column align/##1}}},
    /utils/temp/.list={#1}},
  make box of column/.style={
    inner sep=+0pt, outer sep=+0pt, minimum size=+0pt,
    commutative diagrams/fit=\tikzlastnode,name=\tikzlastnode',
    node family/width=\tikzmatrixname-\the\pgfmatrixcurrentcolumn,
    commutative diagrams/@make box of column,
    append after command={(\tikzlastnode.south west) edge[
      commutative diagrams/column box/.try,to path={
        ([xshift=+.5\pgflinewidth,yshift=+.5\pgflinewidth]\tikztostart)
        rectangle([xshift=+-.5\pgflinewidth,yshift=+-.5\pgflinewidth]%
          \tikztotarget)}](\tikzlastnode.north east)}},
  make column box/.style={
    append after command={
      [every node/.code=]node[commutative diagrams/make box of column]{}}}}
\makeatletter
\tikzcdset{
  fit/.code={% tikz-cd uses an asymmetrical rectangle …
    \pgf@process{\pgfpointdiff{\pgfpointanchor{\tikz@pp@name{#1}}{west}}
                              {\pgfpointanchor{\tikz@pp@name{#1}}{east}}}%
    \pgfkeysalso{/tikz/text width/.expanded=+\the\pgf@x}%
    \pgf@process{\pgfpointdiff{\pgfpointanchor{\tikz@pp@name{#1}}{base}}
                              {\pgfpointanchor{\tikz@pp@name{#1}}{north}}}%
    \pgfkeysalso{/tikz/text height/.expanded=+\the\pgf@y}%
    \pgf@process{\pgfpointdiff{\pgfpointanchor{\tikz@pp@name{#1}}{south}}
                              {\pgfpointanchor{\tikz@pp@name{#1}}{base}}}%
    \pgfkeysalso{/tikz/text depth/.expanded=+\the\pgf@y}},
  from'/.code=\edef\tikzcd@ar@start{\tikzcd@ar@start'},
  to'/.code=\edef\tikzcd@ar@target{\tikzcd@ar@target'}}
\makeatother
\usetikzlibrary{ext.paths.ortho,ext.node-families,fit}
\begin{document}
\[
\begin{tikzcd}[
  cells={
    text depth=+0pt, text height=+.7em,% \strut without depth
    nodes={commutative diagrams/make column box}},
  math mode=false,
  /tikz/ortho/install shortcuts,
  colspec={right, left, center}
]
  Math is excellent!
    \ar[r, rightarrow]
    \ar[d, *|, leftarrow]
& w \ar[d, |*, rightarrow] \rar[from', to']
& Foo \\
  w \ar[r, leftarrow]
& Pythagoreans killed someone!
& Baaaaaaaaaar
\end{tikzcd}
\]
\end{document}

Output

enter image description here

Qrrbrbirlbel
  • 119,821
2

Who told you Case 1 is not possible?
Of course, it is, with tikz-cd, without using hacking!
You may 1) use \tikztostart (starting point) and \tikztotarget (ending point); or 2) name a node; and then use them in to path or from.

Here are some examples:

\documentclass{article}
\usepackage{amsmath}
\usepackage{tikz-cd}

\begin{document} Solution with node names and \verb|from|/\verb|to|: [ \begin{tikzcd}[ cells={nodes={draw=gray}}, /tikz/column 1/.append style={nodes={anchor=base east}}, /tikz/column 2/.append style={nodes={anchor=base west}} ] |[alias=mathis]|\text{Math is excellent!}\ar[r]\ar[d, leftarrow, from={mathis.south -| secondw}] & |[alias=firstw]|\text{w}\ar[d, to={Pytha.north -| firstw}]\ |[alias=secondw]|\text{w}\ar[r, leftarrow] &
|[alias=Pytha]|\text{Pythagoreans killed someone!} \end{tikzcd} ]

Solution with node names and \verb|to path|: [ \begin{tikzcd}[ cells={nodes={draw=gray}}, /tikz/column 1/.append style={nodes={anchor=base east}}, /tikz/column 2/.append style={nodes={anchor=base west}} ] |[alias=mathis]|\text{Math is excellent!}\ar[r]\ar[d, leftarrow, to path={(mathis.south -| secondw) -- (secondw)}] & |[alias=firstw]|\text{w}\ar[d, to path={ -- (Pytha.north -| firstw)}]\ |[alias=secondw]|\text{w}\ar[r, leftarrow] & |[alias=Pytha]|\text{Pythagoreans killed someone!} \end{tikzcd} ]

Solution with \verb|\tikztostart|, \verb|\tikztotarget| and \verb|to path|: [ \begin{tikzcd}[ cells={nodes={draw=gray}}, /tikz/column 1/.append style={nodes={anchor=base east}}, /tikz/column 2/.append style={nodes={anchor=base west}} ] \text{Math is excellent!}\ar[r]\ar[d, leftarrow, to path={(\tikztostart.south -| \tikztotarget) -- (\tikztotarget)}] & \text{w}\ar[d, to path={-- (\tikztotarget.north -| \tikztostart)}]\ \text{w}\ar[r, leftarrow] & \text{Pythagoreans killed someone!} \end{tikzcd} ]

Mixed: [ \begin{tikzcd}[ cells={nodes={draw=gray}}, /tikz/column 1/.append style={nodes={anchor=base east}}, /tikz/column 2/.append style={nodes={anchor=base west}} ] |[alias=first]|\text{Math is excellent!}\ar[r]\ar[d, leftarrow, from={first.south -| second}] & \text{w}\ar[d, to path={-- (\tikztotarget.north -| \tikztostart)}]\ |[alias=second]|\text{w}\ar[r, leftarrow] & \text{Pythagoreans killed someone!} \end{tikzcd} ] \end{document}

enter image description here

Look at Section 2.4 here, for more info: https://tug.org/TUGboat/tb43-1/tb133duck-tikz-cd.pdf.

CarLaTeX
  • 62,716