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.

7
  • Is tikz-cd mandatory for your future use? If not, I think that a simple TikZ graph would be a better solution.
    – SebGlav
    Apr 2, 2022 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 2, 2022 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 2, 2022 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. Feb 20 at 18:10
  • 1
    Could be great if you accepted an answer below, stating that your question has been solved.
    – SebGlav
    Feb 21 at 5:13

4 Answers 4

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.

1
  • I guess it's my time to try my hands on TikZ.
    – Atom
    Apr 2, 2022 at 23:10
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}
2
  • 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 3, 2022 at 9: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 3, 2022 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

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.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .