2

One of my publishing products involves creating a fairly large number (21,000) of diagrams, consisting of about 14,000 unique nodes. This is all machine-generated, drawing dependency graphs of game objects.

I want to favor nodes with a text width of 21mm, but allow nodes a few millimetres wider if it means reducing the height of nodes that are more than two rows of text. If a node would have two lines of text, I don't feel the need to tweak it to be made one row of text.

Improved Unarmed Strike nodes, first has three lines of text and the second has two lines of text, the third is a little wider than it needs to be

I prefer the second node shown here. The third is wider than it needs to be.

Until now, I've been manually adjusting the node widths to do this. As might be understandable, I want to minimize manual intervention. I was looking for a way to automate these tweaks.

@Qrrbrbirlbel gave me the hint I needed to build the full solution I was looking for, varwidth. My MWE below is a minimally-working solution (not ready for packaging, but enough to show how to do what I needed).

\documentclass{article}
\usepackage{calc}
\usepackage{tikz}
\usepackage{varwidth}
\usetikzlibrary{calc,graphs,fit,matrix}


\newcommand{\tn}[2][]{\node[,align=left,anchor=west,#1] {#2};}

\tikzset{
  n/.style={draw,align=flush center,inner sep=1mm},
  n21/.style={n,text width=21mm},
  nvw/.style={n,minimum width=23mm},
}

\newlength{\nntw}

\newcommand{\nnvw}[3][]{\node[nvw,#1] (#2) {\begin{varwidth}{25mm}\centering #3\end{varwidth}};}
\newcommand{\nnif}[3][]{%
  \setlength{\nntw}{\widthof{#3}}%
  \ifdim\nntw < 25mm
    \node[n21,#1](#2){#3};
  \else
    \nnvw[#1]{#2}{#3}
  \fi
}

\begin{document}
\begin{tikzpicture}
\matrix [row sep=4mm,column sep=4mm,every node/.style={font=\sffamily},] {
  \node{1}; & \node[n,text width=21mm]{Improved Unarmed Strike}; & \tn{text width = 21mm} \\
  \node{2}; & \node[n,text width=23mm]{Improved Unarmed Strike}; & \tn{text width = 23mm} \\
  \node{3}; & \node[n,text width=25mm]{Improved Unarmed Strike}; & \tn{text width = 25mm} \\
  \node{4}; & \nnvw{}{Improved Unarmed Strike} & \tn{varwidth 25mm} \\
  \node{5}; & \nnvw{}{Unarmed Strike} & \tn{varwidth 25mm} \\
  \node{6}; & \nnif{}{Unarmed Strike} & \tn{varwidth if 25mm} \\
};

\end{tikzpicture}
\end{document}

The \nnwe command creates a node with a minimum width of 23mm (21mm+2*inner sep), and populates the node with a varwidth environment that can be up to 23mm wide (but will be narrower if the 'natural width' of the content can be). This functionally gives me a node that can have a text width of 21mm-23mm. Node 4 in my output was generated with the \nnvw command.

Improved Unarmed Strike nodes, first has three lines of text and the second has two lines of text, the third is a little wider than it needs to be, fourth is the right size and did not need manual intervention

The final piece above is to handle where the node could be reduced to a single line, but I don't need it to be. I have lots of two-line nodes, so single-line nodes actually tend to stand out. If I use \nnvw, a node of 'Unarmed Strike' is reduced to a single line... but I don't need that. I added another command, \nnif, that first checks the text width it see if it would even get close to being three lines, before deciding to use \nnvw. I found that I needed to create a length to compare using \ifdim, I couldn't directly use \widthof{#3} with \ifdim.

Unarmed Strike nodes, first has been unnecessarily tweaked to be one line of text, and the second is still two lines of text

This isn't my final solution that'll make it into my class file. I certainly want better names for the commands and whatnot, and I'd like to see if I can build the varwidth decision into a style definition... but for simple "how to get a node that can grow a bit when needed", I think this'll work.

16
  • it's not clear what you want. how is the code to know that the second one should be a single line, the first and third two lines and the remainder maybe-one-maybe-two? Commented Apr 10 at 5:54
  • My initial thought was something like "how to minimize node height by increasing node width a small amount". I can usually afford a few millimetres width here and there more easily than I can another row of text. Commented Apr 10 at 6:02
  • 1
    thanks. but it really isn't clear how you want it to work. how should TeX know the difference between nodes 1 and 2, say? one you want broken and one not, but there doesn't seem any programmatic way of telling. (except the difference in width, but that's what you don't want to specify ....) Commented Apr 10 at 6:24
  • 2
    but in your example, the nodes all have identical content. how is it determined that the second should be 1 line and the first 2 etc.? right now, that looks like some judgement you make on the basis of some unspecified factors. that's fine, but can't really be automated without figuring out a way to read you mind. while that is probably part of the latex project's long-term development plans for world domination, they are currently tied up with accessibility and presentations, so l3esp is probably still some way off ... Commented Apr 10 at 7:12
  • 1
    It sounds like you want to replace \node with a wrapper something like "if width("[Text]") [is between 21 and 23] then \node[n, width=width("[Text]")] {[Text]} else \node[n, width=21mm] {[Text]}" ? (width is provided by calc) Commented Apr 10 at 10:32

1 Answer 1

2

You can combine a minimum width that is the minimum text width (here 21mm) plus twice the inner xsep and a varwidth environment with a maximum width of your maximum text width (here 27mm).

Code

\documentclass[tikz]{standalone}
%\documentclass{article}
\usepackage{tikz}
\usepackage{varwidth}
\begin{document}
\begin{tikzpicture}
\node[draw, align=center, minimum width=21mm+2*\pgfkeysvalueof{/pgf/inner xsep}](n){%
  \begin{varwidth}{27mm}
    \centering
    Improved Unarmed Strike
  \end{varwidth}%
};
%\pgfpointdiff{\pgfpointanchor{n}{text}}{\pgfpointanchor{n}{base}}
%\pgfgetlastxy\xxx\yyy
%\node[above] at (n.north) {\tiny\the\dimexpr21mm\ $\leq$ \the\dimexpr\xxx*2\relax\ $\leq$ \the\dimexpr27mm ?};
\end{tikzpicture}
\end{document}
1
  • This isn't exactly the solution I was looking for, but varwidth gives me exactly the hint I needed. Commented Apr 12 at 5:32

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.