5

I tried looking here for an answer but didn't find anything.

I'm trying to do this (taken from objectmentor.com): enter image description here

So far, I've got this:

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{positioning}
\usetikzlibrary{calc}

\begin{document}

\begin{tikzpicture}[node distance=0mm and 0mm]

\def \bigside {15}
\def \smallside {5}
\def \unit {mm}

\tikzset{holder/.style={draw, fill=white, font=\bfseries, minimum size=\bigside \unit, rectangle}}
\tikzset{derecha/.style={draw, fill=white, minimum size=\smallside \unit, rectangle}}

\node[holder] at (0,0) {5};
\node[holder] at ({\bigside*1 \unit},0) {14};
\node[holder] at ({\bigside*2 \unit},0) {29};
\node[holder] at ({\bigside*3 \unit},0) {49};
\node[holder] at ({\bigside*4 \unit},0) {60};
\node[holder] at ({\bigside*5 \unit},0) {61};
\node[holder] at ({\bigside*6 \unit},0) {77};
\node[holder] at ({\bigside*7 \unit},0) {97};
\node[holder] at ({\bigside*8 \unit},0) {117};
\node[holder] at ({\bigside*9 \unit},0) {133};

\end{tikzpicture}

\end{document}

That draws the big rectangles. Now I'm trying to draw the small rectangles (I'll start with those that have numbers) on the top right corner.

I was trying to do something like this:

\node[derecha] at ({\smallside + \bigside*0 \unit }, {\smallside \unit }) {4};
\node[derecha] at ({\smallside + \bigside*1 \unit }, {\smallside \unit }) {5};
% etc

If I put the result of (\smallside + \bigside* i) it works (i.e. 5 mm, 20mm, 35mm, etc.), but this doesn't.

If I use the default units (cm) and don't put the \unit it also works:

\node[derecha] at ({\smallside + \bigside*0 }, {\smallside }) {4};
\node[derecha] at ({\smallside + \bigside*1}, {\smallside  }) {5};

This also works, but it's a pain in the ... stomach

\pgfmathsetmacro\posa{\smallside + \bigside*0}
\pgfmathsetmacro\posb{\smallside + \bigside*1}
% etc
\node[derecha] at ({\posa \unit }, {\smallside \unit }) {4};
\node[derecha] at ({\posb \unit }, {\smallside \unit }) {5};
% etc

So, how can I make this work?

2
  • 1
    5+15mm will evaluated to 5pt+15mm by TikZ. But: instead of using \unit simply set the x and y vector mm: \begin{tikzpicture}[x=1mm,y=1mm,node distance=0mm and 0mm] Commented May 13, 2013 at 19:07
  • Excelent, now I don't know which answer is better. If you put it as an answer, I'll see later which one was the best for me.
    – Nico
    Commented May 13, 2013 at 19:54

3 Answers 3

6

As said in the comment, something like 5+15mm will be interpreted as 5pt+15mm(source: somewhere in the PGF manual). But you can change the coordinate system to use the unit mm with x=1mm, y=1mm, then you are able to use 5 + 15 which translate to 20mm.

Toscho’s solution (with a path picture instead of a separate node) would then look like Code A.


Though you didn’t asked for, I created a slightly more powerful holder key that accepts a parameter in the form of <val1>:<val2> or <val1>:<val2>:<val3> (for the last node). You can also give no argument at all, then : will be used (i.e. no contents but a box in the right-upper corner). The <vali> can also be one of x, X or / which will draw a black box or a black triangle (this may not be the best approach).

I haven’t realized that it is also possible to only have a black box, this will need to be input as :X. The holder stuff can be improved so that you can use holder=X if needed.

The chains library helps us to easily place the nodes.

Code A

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{positioning}

\tikzset{
 use unit/.code={
   \def\myUnit{#1}%
   \pgfkeysalso{x=1#1,y=1#1}%
 },
 set sizes/.code args={#1and#2}{\def\bigside{#1}\def\smallside{#2}},
 ?/.style={
   path picture={
     \node[dereche,anchor=north east] at (path picture bounding box.north east) {#1};
   }
 }
}
\begin{document}

\begin{tikzpicture}[
  node distance=0mm and 0mm,
  use unit=mm,
  set sizes=15 and 5,
  box/.style={
    shape=rectangle, % the default
    draw,
    fill=white
  },
  holder/.style={
    box,
    minimum size=\bigside\myUnit
  },
  dereche/.style={
    box,
    minimum size=\smallside\myUnit
  }
  ]

    \node[holder, ?=4] (n1) at (0,0) {5};
    \node[holder, ?=5] (n2) at (\bigside*1,0) {14};
    \node[holder] (n3)  at (\bigside*2,0) {29};
    \node[holder] (n4)  at (\bigside*3,0) {49};
    \node[holder] (n5)  at (\bigside*4,0) {60};
    \node[holder] (n6)  at (\bigside*5,0) {61};
    \node[holder] (n7)  at (\bigside*6,0) {77};
    \node[holder] (n8)  at (\bigside*7,0) {97};
    \node[holder] (n9)  at (\bigside*8,0) {117};
    \node[holder] (n10) at (\bigside*9,0) {133};
\end{tikzpicture}
\end{document}

Code B

\documentclass[tikz]{standalone}
\usetikzlibrary{positioning,
                chains}
\makeatletter
\def\qrr@bowl@X{x}
\def\qrr@bowl@slash{/}
\tikzset{
 use unit/.code={
   \def\myUnit{#1}%
   \pgfkeysalso{x=1#1,y=1#1}% we don't really need this anymore
 },
 set sizes/.code={\def\bigside{#1}\def\smallside{(#1/3)}},
 box/.style={
   shape=rectangle, % the default
   draw,
   outer sep=+0pt,
 },
 holder/.style={
   box,
   minimum size=\bigside\myUnit,
   font=\bfseries\Large,
   text height=+2em,
   qrr@holder=#1,
 },
 holder/.default=:,
 mini/.style={
   box,
   minimum width=\smallside*1\myUnit,
   minimum height=\smallside*1\myUnit,
   outer sep=+0pt,
%   text height=,
%   font=,
   qrr@mini=#1
 },
 mini/.default=,
 qrr@mini/.code=%
  \lowercase{\def\pgf@tempa{#1}}%
  \ifx\pgf@tempa\qrr@bowl@X
    \pgfkeysalso{qrr@X}%
  \else\ifx\pgf@tempa\qrr@bowl@slash
      \pgfkeysalso{qrr@slash}%
    \else
      \pgfkeysalso{label={center:{#1}}}% the labels also needs [font=,text height=] if used with a path picture
    \fi
  \fi,
 qrr@holder/.code args={#1:#2}{%
   \pgfutil@in@:{#2}%
   \ifpgfutil@in@
     \pgfkeysalso{qrr@@holder={#1:#2}}%
   \else
     \pgfkeysalso{qrr@@holder={:#1:#2}}%
   \fi
 },
 qrr@@holder/.style args={#1:#2:#3}{% we could have used simple rectangles instead of nodes ...
%   path picture={
   append after command={%
     \pgfextra
      \begingroup
%     \node[mini=#3, anchor=north east] (@qrr@) at (path picture bounding box.north east)            {};
        \node[mini=#3, anchor=east] (@qrr@) at ([yshift=-\smallside*.5\myUnit]\tikzlastnode.north east)                {};
        \tikzset{mini/.append style={left=+0pt of @qrr@}}
        \if\relax\detokenize{#1}\relax
          \node[mini=#2, shape=rectangle, draw=none, minimum width={(\bigside-\smallside)*1\myUnit}]                    {};
        \else
          \node[mini=#2, minimum width={(\bigside-\smallside-\smallside)*1\myUnit}]            (@qrr@) {};
          \node[mini=#1, shape=rectangle, draw=none, minimum width={(\bigside-\smallside-\smallside)*1\myUnit}]         {};
        \fi
      \endgroup
     \endpgfextra
   }
 },
 qrr@X/.style={fill=black},
 qrr@slash/.style={path picture={\fill[black](path picture bounding box.south west)-|(path picture bounding box.north east)--cycle;}}
}
\makeatother
\begin{document}
\begin{tikzpicture}[
  use unit=mm,
  set sizes=15,
  % just for fun a little bubblier:
  mini/.append style={shape=circle},
  holder/.append style={rounded corners=\smallside*.5\myUnit}
  ]
  \begin{scope}[start chain=going right, holder/.append style={on chain}, node distance=+0pt]
    \node[holder=1:4]     {5};
    \node[holder=4:5]    {14};
    \node[holder=6:/]    {29};
    \node[holder=5:/]    {49};
    \node[holder= :x]    {60};
    \node[holder=0:1]    {61};
    \node[holder=7:/]    {77};
    \node[holder=6:/]    {97};
    \node[holder= :X]   {117};
    \node[holder=2:/:6] {133};
  \end{scope}
\end{tikzpicture}
\end{document}

Output

enter image description here

Output (bubbly)

enter image description here

4
  • Thanks a lot, it will take a while to fully understand but the result is awesome. One last question, I'm having each tikz-picture in a separate tex file and then I \input{} them. It's ok to put the makeatletter ... makeatother part inside the document so I can directly input the file and I don't have to touch the master?
    – Nico
    Commented May 14, 2013 at 14:47
  • @Nico You’re welcome. I have updated the code a little bit. Instead of a path picture I use now a append after command which has the advantage that nodes used inside there do not inherit any styles from the holder node but the disadvantage that the stuff will be drawn behind the holder node (so a fill shouldn’t be used). Another solution would be to use simple paths (for rectangles, triangles and whatnot) instead of the mini nodes. Commented May 14, 2013 at 15:18
  • I have some odd behaviour... The bold numbers look crappy. If I comment the qrr@holder=#1, in the holder style, it looks good. I'm using the last edit, commented out the circle and rounded corners, I also used \large instead of \Large. Check out what I mean here: dl.dropboxusercontent.com/u/63456043/normal.png, dl.dropboxusercontent.com/u/63456043/odd.png
    – Nico
    Commented May 14, 2013 at 16:39
  • @Nico I can’t really see an issue. Maybe there is a difference in aliasing, but hardly visible even at full zoom. Commented May 16, 2013 at 13:51
4

Use node names and anchors:

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{positioning}
\usetikzlibrary{calc}

\begin{document}

\begin{tikzpicture}[node distance=0mm and 0mm]

\def \bigside {15}
\def \smallside {5}
\def \unit {mm}

\tikzset{holder/.style={draw, fill=white, font=\bfseries, minimum size=\bigside \unit, rectangle}}
\tikzset{derecha/.style={draw, fill=white, minimum size=\smallside \unit, rectangle}}

\node[holder](n1) at (0,0) {5};
\node[holder](n2) at ({\bigside*1 \unit},0) {14};
\node[holder](n3) at ({\bigside*2 \unit},0) {29};
\node[holder](n4) at ({\bigside*3 \unit},0) {49};
\node[holder](n5) at ({\bigside*4 \unit},0) {60};
\node[holder](n6) at ({\bigside*5 \unit},0) {61};
\node[holder](n7) at ({\bigside*6 \unit},0) {77};
\node[holder](n8) at ({\bigside*7 \unit},0) {97};
\node[holder](n9) at ({\bigside*8 \unit},0) {117};
\node[holder](n10) at ({\bigside*9 \unit},0) {133};

\node[derecha,anchor=north east] at (n1.north east) {4};
\node[derecha,anchor=north east] at (n2.north east) {5};

\end{tikzpicture}

\end{document}
1
  • That seems to work, the first time I tried to put the "holders" with right=of but had some problems so I abandoned the idea.
    – Nico
    Commented May 13, 2013 at 19:51
2

To place the nodes automatically, I would use a chain. In the example below, to be considered as a starting point, I also defined some shapes: bowling and bowlings the first one is basically a rectangle with a little square in the upper right corner, and the second one adds to bowling a filling of half the square (to be used for spares).

Here's an example showing the two defined shapes:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{positioning,calc,chains,shapes}

\def\smallside{10pt}

\makeatletter
\pgfdeclareshape{bowling}{
\inheritsavedanchors[from=rectangle] % this is nearly a rectangle
\inheritanchorborder[from=rectangle]
\inheritanchor[from=rectangle]{center}
\inheritanchor[from=rectangle]{north}
\inheritanchor[from=rectangle]{north east}
\inheritanchor[from=rectangle]{north west}
\inheritanchor[from=rectangle]{south}
\inheritanchor[from=rectangle]{south east}
\inheritanchor[from=rectangle]{south west}
\inheritanchor[from=rectangle]{west}
\inheritanchor[from=rectangle]{east}
\backgroundpath{%
% store lower right in xa/ya and upper right in xb/yb
\southwest \pgf@xa=\pgf@x \pgf@ya=\pgf@y
\northeast \pgf@xb=\pgf@x \pgf@yb=\pgf@y
% compute corner of little square
\pgf@xc=\pgf@xb \advance\pgf@xc by-\smallside
\pgf@yc=\pgf@yb \advance\pgf@yc by-\smallside
% construct main path
\pgfpathmoveto{\pgfpoint{\pgf@xa}{\pgf@ya}}
\pgfpathlineto{\pgfpoint{\pgf@xa}{\pgf@yb}}
\pgfpathlineto{\pgfpoint{\pgf@xb}{\pgf@yb}}
\pgfpathlineto{\pgfpoint{\pgf@xb}{\pgf@ya}}
\pgfpathclose
% add little dquare
\pgfpathmoveto{\pgfpoint{\pgf@xc}{\pgf@yb}}
\pgfpathlineto{\pgfpoint{\pgf@xc}{\pgf@yc}}
\pgfpathlineto{\pgfpoint{\pgf@xb}{\pgf@yc}}
}
}

\pgfdeclareshape{bowlings}{
\inheritsavedanchors[from=rectangle] % this is nearly a rectangle
\inheritanchorborder[from=rectangle]
\inheritanchor[from=rectangle]{center}
\inheritanchor[from=rectangle]{north}
\inheritanchor[from=rectangle]{north east}
\inheritanchor[from=rectangle]{north west}
\inheritanchor[from=rectangle]{south}
\inheritanchor[from=rectangle]{south east}
\inheritanchor[from=rectangle]{south west}
\inheritanchor[from=rectangle]{west}
\inheritanchor[from=rectangle]{east}
\backgroundpath{%
% store lower right in xa/ya and upper right in xb/yb
\southwest \pgf@xa=\pgf@x \pgf@ya=\pgf@y
\northeast \pgf@xb=\pgf@x \pgf@yb=\pgf@y
% compute corner of little square
\pgf@xc=\pgf@xb \advance\pgf@xc by-\smallside
\pgf@yc=\pgf@yb \advance\pgf@yc by-\smallside
% construct main path
\pgfpathmoveto{\pgfpoint{\pgf@xa}{\pgf@ya}}
\pgfpathlineto{\pgfpoint{\pgf@xa}{\pgf@yb}}
\pgfpathlineto{\pgfpoint{\pgf@xb}{\pgf@yb}}
\pgfpathlineto{\pgfpoint{\pgf@xb}{\pgf@ya}}
\pgfpathclose
% add little square
\pgfpathmoveto{\pgfpoint{\pgf@xc}{\pgf@yb}}
\pgfpathlineto{\pgfpoint{\pgf@xc}{\pgf@yc}}
\pgfpathlineto{\pgfpoint{\pgf@xb}{\pgf@yc}}
\pgfusepath{stroke}
% fill half the square
\begin{pgfscope}
\pgfpathmoveto{\pgfpoint{\pgf@xb}{\pgf@yb}}
\pgfpathlineto{\pgfpoint{\pgf@xc}{\pgf@yc}}
\pgfpathlineto{\pgfpoint{\pgf@xb}{\pgf@yc}}
\pgfpathclose
\pgfsetfillcolor{black}
\pgfusepath{fill,stroke}
\end{pgfscope}
}
}
\makeatother

\begin{document}

\begin{tikzpicture}
\node[draw,bowling,minimum size=30pt]  {};
\node[draw,bowlings,minimum size=30pt,xshift=40pt]  {};
\end{tikzpicture}

\end{document}

enter image description here

The long part is to define the shapes, but once thay have been defined, the code for the actual figure is really simple:

\begin{tikzpicture}
\begin{scope}[
start chain,
every node/.style={on chain,outer sep=0pt},
node distance=0pt
]
\node[holder]  (a) {5};
\node[holder] (b) {14};
\node[holder] (c) {29};
\node[holder] (d) {49};
\node[spare] (e) {60};
\node[spare] (f) {61};
\node[holder] (g) {77};
\node[holder] (h) {97};
\node[holder] (i){117};
\node[holder] (j) {133};
\end{scope}
\foreach \name/\numero in {a/1,b/5,c/6,d/5,g/8,h/1,i/6,j/4}
  \node[derecha,anchor=north east] at (\name.north east) {\numero};
\foreach \name/\numero in {e/3,f/6}
  \node[derecha,anchor=north east,xshift=-\smallside] at (\name.north east) {\numero};
\end{tikzpicture}

Here's the complete code:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{positioning,calc,chains,shapes}

\makeatletter
\pgfdeclareshape{bowling}{
\inheritsavedanchors[from=rectangle] % this is nearly a rectangle
\inheritanchorborder[from=rectangle]
\inheritanchor[from=rectangle]{center}
\inheritanchor[from=rectangle]{north}
\inheritanchor[from=rectangle]{north east}
\inheritanchor[from=rectangle]{north west}
\inheritanchor[from=rectangle]{south}
\inheritanchor[from=rectangle]{south east}
\inheritanchor[from=rectangle]{south west}
\inheritanchor[from=rectangle]{west}
\inheritanchor[from=rectangle]{east}
\backgroundpath{%
% store lower right in xa/ya and upper right in xb/yb
\southwest \pgf@xa=\pgf@x \pgf@ya=\pgf@y
\northeast \pgf@xb=\pgf@x \pgf@yb=\pgf@y
% compute corner of little square
\pgf@xc=\pgf@xb \advance\pgf@xc by-\smallside
\pgf@yc=\pgf@yb \advance\pgf@yc by-\smallside
% construct main path
\pgfpathmoveto{\pgfpoint{\pgf@xa}{\pgf@ya}}
\pgfpathlineto{\pgfpoint{\pgf@xa}{\pgf@yb}}
\pgfpathlineto{\pgfpoint{\pgf@xb}{\pgf@yb}}
\pgfpathlineto{\pgfpoint{\pgf@xb}{\pgf@ya}}
\pgfpathclose
% add little dquare
\pgfpathmoveto{\pgfpoint{\pgf@xc}{\pgf@yb}}
\pgfpathlineto{\pgfpoint{\pgf@xc}{\pgf@yc}}
\pgfpathlineto{\pgfpoint{\pgf@xb}{\pgf@yc}}
}
}

\pgfdeclareshape{bowlings}{
\inheritsavedanchors[from=rectangle] % this is nearly a rectangle
\inheritanchorborder[from=rectangle]
\inheritanchor[from=rectangle]{center}
\inheritanchor[from=rectangle]{north}
\inheritanchor[from=rectangle]{north east}
\inheritanchor[from=rectangle]{north west}
\inheritanchor[from=rectangle]{south}
\inheritanchor[from=rectangle]{south east}
\inheritanchor[from=rectangle]{south west}
\inheritanchor[from=rectangle]{west}
\inheritanchor[from=rectangle]{east}
\backgroundpath{%
% store lower right in xa/ya and upper right in xb/yb
\southwest \pgf@xa=\pgf@x \pgf@ya=\pgf@y
\northeast \pgf@xb=\pgf@x \pgf@yb=\pgf@y
% compute corner of little square
\pgf@xc=\pgf@xb \advance\pgf@xc by-\smallside
\pgf@yc=\pgf@yb \advance\pgf@yc by-\smallside
% construct main path
\pgfpathmoveto{\pgfpoint{\pgf@xa}{\pgf@ya}}
\pgfpathlineto{\pgfpoint{\pgf@xa}{\pgf@yb}}
\pgfpathlineto{\pgfpoint{\pgf@xb}{\pgf@yb}}
\pgfpathlineto{\pgfpoint{\pgf@xb}{\pgf@ya}}
\pgfpathclose
% add little square
\pgfpathmoveto{\pgfpoint{\pgf@xc}{\pgf@yb}}
\pgfpathlineto{\pgfpoint{\pgf@xc}{\pgf@yc}}
\pgfpathlineto{\pgfpoint{\pgf@xb}{\pgf@yc}}
\pgfusepath{stroke}
% fill half the square
\begin{pgfscope}
\pgfpathmoveto{\pgfpoint{\pgf@xb}{\pgf@yb}}
\pgfpathlineto{\pgfpoint{\pgf@xc}{\pgf@yc}}
\pgfpathlineto{\pgfpoint{\pgf@xb}{\pgf@yc}}
\pgfpathclose
\pgfsetfillcolor{black}
\pgfusepath{fill,stroke}
\end{pgfscope}
}
}
\makeatother

\def\bigside{15mm}
\def\smallside{5mm}

\tikzset{holder/.style={bowling,draw, fill=white, font=\bfseries, minimum size=\bigside}}
\tikzset{spare/.style={bowlings,draw, fill=white, font=\bfseries, minimum size=\bigside}}
\tikzset{derecha/.style={minimum size=\smallside}}

\begin{document}

\begin{tikzpicture}
\begin{scope}[
start chain,
every node/.style={on chain,outer sep=0pt},
node distance=0pt
]
\node[holder]  (a) {5};
\node[holder] (b) {14};
\node[holder] (c) {29};
\node[holder] (d) {49};
\node[spare] (e) {60};
\node[spare] (f) {61};
\node[holder] (g) {77};
\node[holder] (h) {97};
\node[holder] (i){117};
\node[holder] (j) {133};
\end{scope}
\foreach \name/\numero in {a/1,b/5,c/6,d/5,g/8,h/1,i/6,j/4}
  \node[derecha,anchor=north east] at (\name.north east) {\numero};
\foreach \name/\numero in {e/3,f/6}
  \node[derecha,anchor=north east,xshift=-\smallside] at (\name.north east) {\numero};
\end{tikzpicture}

\end{document}

enter image description here

You must log in to answer this question.

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