3

I would like to position "grids" on the page. I've placed them in a scope, named the local bounding box, and attempted to use that for relative positioning, e.g. shift={($(myscope.center)+(1cm,1cm)$)}. I've also attempted to position on the page with, e.g. ($(page.south west)$), but am not very pleased with the result.

To be specific, in the screenshot below, I'd like to be able to align the left side of the three grids relative to the left margin: currently the second grid is well aligned, but the first and third are a little too far from the left margin. I could go on attempting to tweak the shifting, but I have a feeling there's a better approach than this.

To provide context for my question, the grids are part of a "multiple-choice" sheet. I adapted some expl3 code found in the accepted answer here. The \vgrid and \hgrid commands produce vertical/horizontal grids: the integers are the question number; the letters are the answer choices; the starting number for the question can be changed; the correct answer is shown in blue (0 leaves the question unanswered). See details immediately below:

% ARGUMENTS of \vgrid and \hgrid
% [starting question number] [total number of questions] [total number of answer choices] [scaling factor] {list of zeros or list of the correct answer numbers, separated by semi-colons}
  % #1 : question sequence starting number, default 1
  % #2 : total number of questions in sequence, default 20
  % #3 : total number of choices for each question, default 5
  % #4 : scaling factor, default 1.0
  % #5 : semi-colon separated list of numbers corresponding to correct answers, e.g. 1 for A, 2 for B, and 0 for not-answered

\documentclass[12pt]{article} \usepackage[papersize={8.5in,11in},left=0.5in,right=0.5in,nohead,top=0.5in,nofoot,bottom=0.5in,marginparsep=0pt,showframe]{geometry} \setlength{\parindent}{0pt} \usepackage{tikz} \usetikzlibrary{calc,positioning}

% HORIZONTAL GRID \ExplSyntaxOn \NewDocumentCommand{\hgrid}{ O{1} O{20} O{5} O{0.9} m}{% \seq_set_split:Nnn \l_tmpa_seq{;}{#5} % print question key (A, B, C...) \int_step_inline:nnnn {1} {1} {#3} {% \node at (0#4, #3#4-##1#4) {\int_to_Alph:n{##1}};} % print question number (1, 2, 3...) \int_step_inline:nnnn {#1} {1} {#1+#2-1} {% \node at (##1#4-#1#4+1#4, #3#4) {##1}; \int_step_inline:nnnn {1} {1} {#3} {% % draw an empty box for each question number/key \node[draw,line~width=1#4pt,minimum~width=0.8#4cm,minimum~height=0.8#4cm] at (##1#4-#1#4+1#4, #3#4-####1#4) {}; % fill the correct box (0 to leave it empty) \int_compare:nNnTF {####1} = {\seq_item:Nn \l_tmpa_seq {#1+#2-##1}} {\node[fill=blue,minimum~width=0.8#4cm,minimum~height=0.8#4cm] at (#1#4+#2#4-##1#4, #3#4-####1#4) {};}{} } } } \ExplSyntaxOff

% VERTICAL GRID \ExplSyntaxOn \NewDocumentCommand{\vgrid}{ O{1} O{20} O{5} O{0.9} m}{% \seq_set_split:Nnn \l_tmpa_seq{;}{#5} % print question key (A, B, C...) \int_step_inline:nnnn {1} {1} {#3} {% \node at (##1#4, #2#4) {\int_to_Alph:n{##1}};} % print question number (1, 2, 3...) \int_step_inline:nnnn {#1} {1} {#1+#2-1} {% \node at (0#4, #1#4+#2#4-1#4-##1#4) {##1}; \int_step_inline:nnnn {1} {1} {#3} {% % draw an empty box for each question number/key \node[draw,line~width=1#4pt,minimum~width=0.8#4cm,minimum~height=0.8#4cm] at (####1#4, ##1#4-#1#4) {}; % fill the correct box (0 to leave it empty) \int_compare:nNnTF {####1} = {\seq_item:Nn \l_tmpa_seq {#1+#2-##1}} {\node[fill=blue,minimum~width=0.8#4cm,minimum~height=0.8#4cm] at (####1#4, ##1#4-#1#4) {};}{} } } } \ExplSyntaxOff

\begin{document}\pagestyle{empty}

\begin{tikzpicture}[x=1cm, y=1cm, font=\scriptsize\bfseries]

% short horizontal grid \begin{scope}[anchor=south east, local bounding box=bb1] \hgrid{0;2;3;4;5;4;3;2;1;2;3;4;5;4;3;2;1;2;3;4} \end{scope}

% long horizontal grid \begin{scope}[anchor=south east, shift={($(bb1.south west)+(0cm,-5cm)$)}, local bounding box=bb2] \hgrid[21][30][6][0.6]{0;2;3;4;5;6;5;4;3;2;1;2;3;4;5;6;5;4;3;2;1;2;3;4;5;6;5;4;3;2} \end{scope}

% short vertical grid \begin{scope}[anchor=south east, shift={($(bb2.south west)+(1cm,-12cm)$)}, local bounding box=bb3] \vgrid[51][10][4][1.1]{0;2;3;4;5;4;3;2;1;2} \end{scope}

\end{tikzpicture}

\end{document}

enter image description here

Note: I have made a major edit to my question and in the process fixed some errors. If any errors remain, they are not intended!

PatrickT
  • 2,923
  • Can you add something about what the four arguments are for? Your code only uses the third and fourth ones, what are the first two for? – Andrew Stacey Oct 21 '21 at 05:34
  • Thanks Andrew. I have completely rewritten my question. I have hopefully fixed the problems you noted. I have also provided an explanation of what the arguments of the commands are. If questions remain, please do not hesitate to ask. Thanks! – PatrickT Oct 21 '21 at 09:45
  • I would redesign the grid to be a pic and then use https://tex.stackexchange.com/q/185279/86 to position the pic. – Andrew Stacey Oct 21 '21 at 10:26
  • Sounds great. I purposefully avoided pics, because in a recent attempt to use them for a completely different problem, I ran into difficulties... If no-one answers the question, I'll give that a try some time soon-ish... but not in the next 24 hours! Thanks. – PatrickT Oct 21 '21 at 10:29

1 Answers1

2

I had a go at re-envisioning your grids as pics, which would then mean that the techniques at Anchoring TiKZ pics could be used. Obviously, I'd recommend the tikzmark solution from that.

\documentclass[12pt]{article}
%\url{https://tex.stackexchange.com/q/619620/86}
\usepackage[papersize={8.5in,11in},left=0.5in,right=0.5in,nohead,top=0.5in,nofoot,bottom=0.5in,marginparsep=0pt,showframe]{geometry}
\setlength{\parindent}{0pt}
\usepackage{tikz}
\usetikzlibrary{calc,positioning,fit}

% HORIZONTAL AND VERTICAL GRIDS \ExplSyntaxOn \cs_new_nopar:Npn \grid_val:n #1 { \pgfkeysvalueof{/tikz/grid/#1} } \tikzset{ grid/.is~ family, grid/start/.initial=1, grid/total/.initial=20, grid/choices/.initial=5, horizontal~ grid/.pic={ \seq_set_split:Nnn \l_tmpa_seq{;}{#1} % print question key (A, B, C...) \int_step_inline:nnnn {1} {1} {\grid_val:n {choices} } {% \node at (0, \grid_val:n {choices}-##1) {\int_to_Alph:n{##1}}; } % print question number (1, 2, 3...) \int_step_inline:nnnn {\grid_val:n {start} } {1} {\grid_val:n {start} + \grid_val:n {total} - 1} {% \node at (##1-\grid_val:n {start} +1, \grid_val:n {choices} ) {##1}; \int_step_inline:nnnn {1} {1} {\grid_val:n {choices}} {% % fill the correct box (0 to leave it empty) \int_compare:nNnT {####1} = {\seq_item:Nn \l_tmpa_seq {\grid_val:n {start} + \grid_val:n {total} - ##1} } { \fill[gray] (\grid_val:n {start} + \grid_val:n {total} -##1, \grid_val:n {choices} - ####1) +(-.4,-.4) rectangle +(.4,.4); } % draw an empty box for each question number/key \draw (##1-\grid_val:n {start}+1, \grid_val:n {choices} -####1) +(-.4,-.4) rectangle +(.4,.4); } } \node[ draw=red, line~width=2pt, dashed, overlay, fit={ (1, \grid_val:n {choices}-1) (\grid_val:n {total}, 0) } ] (-grid) {}; \node[ draw=blue, line~width=2pt, dashed, overlay, fit={ (0, \grid_val:n {choices}) (\grid_val:n {total}, 0) } ] (-picture) {}; }, vertical~ grid/.pic={ \seq_set_split:Nnn \l_tmpa_seq{;}{#1} % print question key (A, B, C...) \int_step_inline:nnnn {1} {1} {\grid_val:n {choices} } {% \node at (##1, \grid_val:n {total}) {\int_to_Alph:n{##1}}; } % print question number (1, 2, 3...) \int_step_inline:nnnn {\grid_val:n {start} } {1} {\grid_val:n {start} + \grid_val:n {total} - 1} {% \node at (0,\grid_val:n {start} + \grid_val:n {total} -1 - ##1 ) {##1}; \int_step_inline:nnnn {1} {1} {\grid_val:n {choices}} {% % fill the correct box (0 to leave it empty) \int_compare:nNnT {####1} = {\seq_item:Nn \l_tmpa_seq {\grid_val:n {start} + \grid_val:n {total} - ##1} } { \fill[gray] (####1, ##1 - \grid_val:n {start}) +(-.4,-.4) rectangle +(.4,.4); } % draw an empty box for each question number/key \draw (####1, ##1 - \grid_val:n {start}) +(-.4,-.4) rectangle +(.4,.4); } } \node[ draw=red, line~width=2pt, dashed, overlay, fit={ (\grid_val:n {choices}, 0) (1, \grid_val:n {total}-1) } ] (-grid) {}; \node[ draw=blue, line~width=2pt, dotted, overlay, fit={ (\grid_val:n {choices}, 0) (0, \grid_val:n {total}) } ] (-picture) {}; } } \ExplSyntaxOff

\begin{document}\pagestyle{empty}

\begin{tikzpicture}[font=\scriptsize\bfseries] \pic [scale=.9] [grid/start=44] [grid/choices=8] [grid/total=10] {vertical grid={0;2;3;4;5;4;3;2;1;2}}; \end{tikzpicture}

\begin{tikzpicture}[font=\scriptsize\bfseries] \pic [scale=.9] [grid/start=21] [grid/choices=4] [grid/total=10] {horizontal grid={0;2;3;4;5;4;3;2;1;2}}; \end{tikzpicture}

\begin{tikzpicture}[font=\scriptsize\bfseries] \pic [scale=.9] [grid/start=1] [grid/choices=5] [grid/total=20] {horizontal grid={0;2;3;4;5;4;3;2;1;2;3;4;5;4;3;2;1;2;3;4}}; \end{tikzpicture}

\end{document}

grids redrawn as pics


Update 2021-10-23 Here's an example using the tikzmark method for anchoring a pic. Note that this is currently only available in the development version of tikzmark from github and I'm not yet fully set on the syntax (so suggestions for improvements greatly welcomed).

\documentclass[12pt]{article}
%\url{https://tex.stackexchange.com/q/619620/86}
\usepackage[papersize={8.5in,11in},left=0.5in,right=0.5in,nohead,top=0.5in,nofoot,bottom=0.5in,marginparsep=0pt,showframe]{geometry}
\setlength{\parindent}{0pt}
\usepackage{tikz}
\usetikzlibrary{calc,positioning,fit,tikzmark}

% HORIZONTAL GRID \ExplSyntaxOn

\cs_new_nopar:Npn \grid_val:n #1 { \pgfkeysvalueof{/tikz/grid/#1} }

\seq_new:N \l__grid_answers_seq

% #1 - ;-separated list of correct answers % #2 - choices per question % #3 - start number of questions % #4 - total number of questions \cs_new_nopar:Npn \grid_horizontal:nnnn #1#2#3#4 { % Split the answers into a sequence \seq_set_split:Nnn \l__grid_answers_seq {;} {#1} % Print the choice labels \int_step_inline:nnnn {1} {1} {#2} { \node at (0, -##1) {\int_to_Alph:n{##1}}; } % Print question numbers \int_step_inline:nnnn {1} {1} {#4} { \node at (##1, 0) {\int_eval:n{##1 + #3 - 1}}; } % Set out the grid \int_step_inline:nnnn {1} {1} {#2} { \int_step_inline:nnnn {1} {1} {#4} { \int_compare:nNnTF {##1} = {\seq_item:Nn \l__grid_answers_seq {####1}} { \filldraw[fill=blue,draw=black] } { \draw } (####1,-##1) +(-.4,-.4) rectangle +(.4,+.4); } } \node[ draw=green, overlay, fit={ (.5,-.5) (#4+.5,-#2-.5) } ] (-grid) {}; \node[ draw=orange, overlay, fit={ (-.5,.5) (#4+.5,-#2-.5) } ] (-picture) {}; }

% #1 - ;-separated list of correct answers % #2 - choices per question % #3 - start number of questions % #4 - total number of questions \cs_new_nopar:Npn \grid_vertical:nnnn #1#2#3#4 { % Split the answers into a sequence \seq_set_split:Nnn \l__grid_answers_seq {;} {#1} % Print the choice labels \int_step_inline:nnnn {1} {1} {#2} { \node at (##1,0) {\int_to_Alph:n{##1}}; } % Print question numbers \int_step_inline:nnnn {1} {1} {#4} { \node at (0,-##1) {\int_eval:n{##1 + #3 - 1}}; } % Set out the grid \int_step_inline:nnnn {1} {1} {#2} { \int_step_inline:nnnn {1} {1} {#4} { \int_compare:nNnTF {##1} = {\seq_item:Nn \l__grid_answers_seq {####1}} { \filldraw[fill=blue,draw=black] } { \draw } (##1,-####1) +(-.4,-.4) rectangle +(.4,+.4); } } \node[ draw=green, overlay, fit={ (.5,-.5) (#2+.5,-#4-.5) } ] (-grid) {}; \node[ draw=orange, overlay, fit={ (-.5,.5) (#2+.5,-#4-.5) } ] (-picture) {}; }

\cs_generate_variant:Nn \grid_horizontal:nnnn {nvvv} \cs_generate_variant:Nn \grid_vertical:nnnn {nvvv}

\cs_new_nopar:Npn \grid_horizontal_from_keys:nnnn #1#2#3#4 { \grid_horizontal:nvvv {#1} {pgfk@/tikz/grid/#2} {pgfk@/tikz/grid/#3} {pgfk@/tikz/grid/#4} }

\cs_new_nopar:Npn \grid_vertical_from_keys:nnnn #1#2#3#4 { \grid_vertical:nvvv {#1} {pgfk@/tikz/grid/#2} {pgfk@/tikz/grid/#3} {pgfk@/tikz/grid/#4} }

\tikzset{ grid/.is~ family, grid/start/.initial=1, grid/total/.initial=20, grid/choices/.initial=5, horizontal~ grid/.pic={ \grid_horizontal_from_keys:nnnn {#1} {choices} {start} {total} }, vertical~ grid/.pic={ \grid_vertical_from_keys:nnnn {#1} {choices} {start} {total} } }

\ExplSyntaxOff

\begin{document}\pagestyle{empty}

\begin{tikzpicture}[x=1cm, y=1cm, font=\scriptsize\bfseries] \fill[red] (0,0) circle[radius=5pt]; \pic[ scale=.7, pic anchor=(-grid.east), grid/.cd, start=3, total=15 ] {horizontal grid={0;2;3;4;5;4;3;2;1;2;3;4;5;4;3;2;1;2;3;4}};

\pic[ scale=.7, pic anchor=(-picture.west), grid/.cd, start=3, total=15 ] {vertical grid={0;2;3;4;5;4;3;2;1;2;3;4;5;4;3;2;1;2;3;4}}; \end{tikzpicture} \end{document}

I've rejigged the pic code, factoring out the L3 code into separate commands that are then invoked from the pic code. This also allows insertion of a layer to sort out getting values from pgf keys and so not cluttering up the main code. Another tweak I made was to lay out the grid down from the top rather than up from the bottom.

I've left in the outlines on the grid and picture nodes so that the positioning is more evident. Obviously, in a final version those would be removed (just the outlining - not the nodes themselves!).

positioned grids

Andrew Stacey
  • 153,724
  • 43
  • 389
  • 751
  • 1
    Thank you so much Andrew! It will take me a while to process this code. I added examples that show how to use the start, choices, total arguments and added a screenshot. I noticed you had left some green and red lines, presumably the code you copied is still work in progress? I would appreciate if you added an example of the tikzmark placement options: I'm confused about the difference between pic, \pic and .pic! – PatrickT Oct 22 '21 at 07:24
  • 1
    Yes, still a work in progress. The green and red lines were the boundaries of the nodes around the grid and the whole pic, but your example shows that I don't have them right as yet. I'll fix that and add the positioning examples. – Andrew Stacey Oct 22 '21 at 10:21
  • 1
    I think I've managed to fix the part of the code that traces the lines around the objects. The red lines are "tight" around the boxes, while the blue lines are shifted by 1 unit to also wrap around the letters/numbers printed next to the boxes. – PatrickT Oct 22 '21 at 11:36
  • 1
    @PatrickT I've updated my answer with an example of positioning. – Andrew Stacey Oct 23 '21 at 10:46
  • This is fantastic! Great work! A few questions off the top of my head. If I understand correctly, pic anchor=(-grid.east) anchors on the green line, while pic anchor=(-picture.west) anchors on the orange line. The default is to pin to (0,0): how do you specify another coordinate to pin to? And what does grid/.cd do? From the manual, I gather it stands for "change directory", but why is it needed? And why is it not simply pic anchor=(picture.west)? Why the "minus sign" is needed before "picture"? – PatrickT Oct 23 '21 at 11:28
  • About the offset values you hardcode to get the green and orange lines to fit around the boxes, things like +.5, where do these values come from? Will the lines still fit around the grids if they are re-scaled? – PatrickT Oct 23 '21 at 11:29
  • 1
    The syntax is pic anchor=(internal coordinate). This evaluates (internal coordinate) in the coordinate system inside the pic and places that coordinate at the point specified in the pic invocation, so \pic[pic anchor={(3,1)}] at (4,2) ... places the point which the pic thinks is at (3,1) at the point that the tikz picture thinks is at (4,2). Since you don't specify an external location for the pic, it defaults to (0,0). – Andrew Stacey Oct 23 '21 at 13:45
  • 1
    The grid/.cd is to avoid having to write grid/start=, grid/choices=, grid/total= and just write grid once. That's covered in the pgfkeys section of the manual, it goes hand in hand with the grid/.is family in the preamble. – Andrew Stacey Oct 23 '21 at 13:46
  • 1
    The hyphen in the node names is because when you give a pic a name then every node inside that pic picks up that name as a prefix on its own name. So if you named the first grid a then the nodes inside would be a-picture and a-grid. I use a hyphen to separate the prefix from the name so that when referring to them later then it's clearer to me which part is the pic prefix and which part the internal name. There isn't a default separator so I have to build one in. – Andrew Stacey Oct 23 '21 at 13:48
  • 1
    Lastly, the +/- .5s are because the grid is laid out with each square centred at an integer coordinate from the internal point of view of the pic, so to get the box round the whole lot we need to widen it by .5 in appropriate directions. As all the coordinates are dimensionless, they will scale with any scale factor given on the pic. – Andrew Stacey Oct 23 '21 at 13:50
  • Excellent, thanks Andrew! What a great answer! – PatrickT Oct 23 '21 at 20:12