6

Short Description

I am trying to create a template for a multiple choice question that has an image associated with it. I want the question to look like this:

I've TeXed this picture up manually

In this picture, I've shown the square that I want my Tikz image to appear in, and I've added some lines to show that the square should be lined up with the 5 (and always 5) answer choices.

I'm wondering if there's maybe some package like textpos that automatically takes the input, centers it in the box, and then resizes it to fill the whole box.

Long Description w/ MWE

(Bear with me here, I'm finding it hard to describe my problems)

I've set this up by using the textpos package to place the tikzpicture in its desired location. The issue here is that the tikzpicture is rendered, cropped (appropriately) to the dimensions of the tikzpicture, and then pushed to the top left (where the textpos "anchor" is located).

I want to be able to draw tikzpictures that are lots of different sizes and have different coordinates, but still have them be centered within the box shown in the above picture. For example, its easy to draw a circle by centering it at (0,0) and using all 4 quadrants, but for something like a right triangle, its easier to the 90deg angle at (0,0) and only use the first quadrant.

My attempts at this haven't been great, as I'm sort of fudging everything manually. I manually set the textpos anchor by moving it 0.6 of the paper width to the right and 3.415cm up from the 5th answer choice. After that, I've just drawn a handful of tikz pictures and made sure that their size exactly fits in the box (which happens to be 3.5cm x 3.5cm).

I've got some code below that might help explain my issues. I've included some dimension things (margins, parskip, etc.) as they are an important part of the project I'm working on. I've included some boundary lines to show that the image shouldn't be larger than the height of the 5 answer choices.

I think what I'm looking for is to just have the 3.5cm x 3.5cm box be its own thing that I can just place something in, and it will automatically center it and size it appropriately.

\documentclass[11pt, twoside]{article}

\usepackage[left=1in, top=1in, right=1in, bottom=1in, head=30pt, marginparwidth=2cm, marginparsep=0.35cm]{geometry}
    \geometry{letterpaper}

\usepackage{parskip}

\usepackage{textpos}
    \setlength{\TPHorizModule}{\the\paperwidth}
    \setlength{\TPVertModule}{1cm}

\usepackage{tikz}

\newlength{\mylen}
\settoheight{\mylen}{C}



\begin{document}

\begin{enumerate}

    \item Showing the box I want the image to appear in for the examples below.
    \begin{enumerate}
        \item Choice 1 \rule[\mylen]{0.65\linewidth}{0.1pt}
        \item Choice 2
        \item Choice 3 \rule[0.5\mylen]{0.8\linewidth}{0.1pt}
        \item Choice 4
        \item Choice 5 \rule{0.65\linewidth}{0.1pt}
    \end{enumerate}


    \begin{textblock}{1}(0.6,-3.415)
        \begin{tikzpicture}[scale = 3.5/200]
            \draw[blue, fill = red!20,] (-100,-100) rectangle (100,100);
        \end{tikzpicture}
    \end{textblock}


    \item Image is cropped to just include drawings, then pushed to top left of the red box.
    \begin{enumerate}
        \item Choice 1 \rule[\mylen]{0.65\linewidth}{0.1pt}
        \item Choice 2
        \item Choice 3 \rule[0.5\mylen]{0.65\linewidth}{0.1pt}
        \item Choice 4
        \item Choice 5 \rule{0.65\linewidth}{0.1pt}
    \end{enumerate}


    \begin{textblock}{1}(0.6,-3.415)
        \framebox{%
            \begin{tikzpicture}[scale = 3.5/200]
                \draw (0,0) node {Image};           
            \end{tikzpicture}
        }
    \end{textblock}


    \item When the size or location of the image changes in ``tikz-land'', the tikzpicture is still pushed to the top left of the red box (as expected).
    \begin{enumerate}
        \item Choice 1 \rule[\mylen]{0.65\linewidth}{0.1pt}
        \item Choice 2
        \item Choice 3 \rule[0.5\mylen]{0.65\linewidth}{0.1pt}
        \item Choice 4
        \item Choice 5 \rule{0.65\linewidth}{0.1pt}
    \end{enumerate}


    \begin{textblock}{1}(0.6,-3.415)
        \framebox{
            \begin{tikzpicture}[scale = 3.5/200]
                \draw (10,10) node {\Huge{Image}};         
            \end{tikzpicture}
        }
    \end{textblock}


    \item How I want the image to look (without resizing it)
    \begin{enumerate}
        \item Choice 1 \rule[\mylen]{0.65\linewidth}{0.1pt}
        \item Choice 2
        \item Choice 3 \rule[0.5\mylen]{0.65\linewidth}{0.1pt}
        \item Choice 4
        \item Choice 5 \rule{0.65\linewidth}{0.1pt}
    \end{enumerate}


    \begin{textblock}{1}(0.6,-3.415)
        \begin{tikzpicture}[scale = 3.5/200]
            \draw[blue] (-100,-100) rectangle (100,100);
            \draw[] (0,0) node {Image};         
        \end{tikzpicture}
    \end{textblock}


    \item How I want the image to look (with resizing it)
    \begin{enumerate}
        \item Choice 1 \rule[\mylen]{0.65\linewidth}{0.1pt}
        \item Choice 2
        \item Choice 3 \rule[0.5\mylen]{0.65\linewidth}{0.1pt}
        \item Choice 4
        \item Choice 5 \rule{0.65\linewidth}{0.1pt}
    \end{enumerate}


    \begin{textblock}{1}(0.6,-3.415)
        \begin{tikzpicture}[scale = 3.5/200]
            \draw[blue] (-100,-100) rectangle (100,100);
            \draw[] (0,0) node {\Huge{Image}};         
        \end{tikzpicture}
    \end{textblock}


\end{enumerate}

\end{document}

(Note: I've just manually created the box/image for numbers 4 and 5)

As can be seen here, changing the location of the node in "Tikz-land" doesn't affect its position (it still attaches to the top-left anchor point). If I change the size of the tikzpicture, it will still attach to the top-left, but just look bigger. I want it to attach to the center of the box and resize itself to fill the box (but no larger).

Sorry if I'm a little verbose, but I figured it would be better to try and fully explain myself now rather than have to come back and discuss everything again. Thanks in advance!


EDIT & ADDITIONAL QUESTION

After implementing the solution provided by @frougon, I've noticed that when the Tikz-coordinates exceed \myheight and/or \mywidth (in this case, 3.5), the picture effectively "zoom's-out".

It would be great to have Tikz-pictures where the scaling/proportions don't matter. I would think this could be fixed by always scaling back the Tikz-picture so that the coordinates don't exceed 3.5 (see Image 4, where I scale the picture by 3.5/10, since 10 is the maximum coordinate value).

Maybe there is a definition such as \MaxCoordinate for all Tikz-pictures that I'm not aware of, but I'm not sure.

Here is my code and output:

\documentclass{article}
\usepackage{graphicx}
\usepackage{adjustbox}
\usepackage{xcolor}
\usepackage{tikz}

% Frame dimensions
\newlength{\mywidth}
\newlength{\myheight}
\setlength{\mywidth}{3.5cm}
\setlength{\myheight}{3.5cm}

\makeatletter

\newenvironment{choices}[1]{%
  \def\braced@stuff@to@include{{#1}}%
  \par\noindent
  \minipage{\dimexpr\linewidth-\mywidth-2\fboxrule-2\fboxsep}
  \enumerate
  \ignorespaces
}{%
  \unskip
  \endenumerate
  \endminipage
  \setlength{\fboxsep}{0pt}%
  \fcolorbox{blue!20}{green!20}{%
    \begin{minipage}[c][\myheight][c]{\mywidth}
      \centering
      \def\@tmp{%
        \adjustbox{max totalsize={\mywidth}{\myheight}}}%
      \expandafter\@tmp\braced@stuff@to@include
    \end{minipage}%
  }%
  \ignorespacesafterend
}

\makeatother




\begin{document}

\begin{enumerate}


\item Image 1
  \begin{choices}{%
      \begin{tikzpicture}
        \draw (0,0) rectangle (1,1);
        \draw (0.5,0.5) node {\textbullet};  % node placed in center of square
      \end{tikzpicture}}
  \item Choice 1
  \item Choice 2
  \item Choice 3
  \item Choice 4
  \item Choice 5
  \end{choices}

\item Image 2: same proportions as Image 1, but it's larger
  \begin{choices}{
      \begin{tikzpicture}
        \draw (0,0) rectangle (3.5,3.5);
        \draw (1.75,1.75) node {\textbullet}; % node placed in center of square
      \end{tikzpicture}
  }
  \item Choice 1
  \item Choice 2
  \item Choice 3
  \item Choice 4
  \item Choice 5
  \end{choices}

  \item Image 3: when the tikz-coordinates exceed 3.5, the image effectively ``zooms-out''
  \begin{choices}{
      \begin{tikzpicture}
        \draw (0,0) rectangle (10,10);
        \draw (5,5) node {\textbullet}; % node placed in center of square
      \end{tikzpicture}
  }
  \item Choice 1
  \item Choice 2
  \item Choice 3
  \item Choice 4
  \item Choice 5
  \end{choices}


  \item Image 4: scaling the tikzpicture down fixes the issue, but this requires knowledge that ``10'' is the largest coordinate in the tikzpicture environment
  \begin{choices}{
      \begin{tikzpicture}[scale = 3.5/10]
        \draw (0,0) rectangle (10,10);
        \draw (5,5) node {\textbullet}; % node placed in center of square
      \end{tikzpicture}
  }
  \item Choice 1
  \item Choice 2
  \item Choice 3
  \item Choice 4
  \item Choice 5
  \end{choices}


\clearpage


  \item Image 5: ideally, images 5, 6, \& 7 would look identical as they have the same coordinates, just scaled up and down.
  \begin{choices}{
      \begin{tikzpicture}
        \draw[thick] (0,0)--(1,.5);
        \draw[thick] (0,0)--(.5,-1);
        \draw[thick] (0,0)--(-1,.5);
        \draw[thick] (0,0)--(-.5,-1);
        \draw[thick] (0,0)--(1,-.5);
        \draw[thick] (0,0)--(-.5,1);
        \draw (.05,.2) node {$1$};
        \draw (-.25,.25) node {$2$};
        \draw (-.2,-.05) node {$3$};
        \draw (0,-.25) node {$4$};
        \draw (.25,-.25) node {$5$};
        \draw (.25,0) node {$6$};
      \end{tikzpicture}
  }
  \item Choice 1
  \item Choice 2
  \item Choice 3
  \item Choice 4
  \item Choice 5
  \end{choices}


  \item Image 6: all coordinates are doubled from Image 5.
  \begin{choices}{
      \begin{tikzpicture}
        \draw[thick] (0,0)--(2,1);
        \draw[thick] (0,0)--(1,-2);
        \draw[thick] (0,0)--(-2,1);
        \draw[thick] (0,0)--(-1,-2);
        \draw[thick] (0,0)--(2,-1);
        \draw[thick] (0,0)--(-1,2);
        \draw (.1,.4) node {$1$};
        \draw (-.5,.5) node {$2$};
        \draw (-.4,-.1) node {$3$};
        \draw (0,-.5) node {$4$};
        \draw (.5,-.5) node {$5$};
        \draw (.5,0) node {$6$};
      \end{tikzpicture}
  }
  \item Choice 1
  \item Choice 2
  \item Choice 3
  \item Choice 4
  \item Choice 5
  \end{choices}


  \item Image 7: all coordinates are multiplied by a factor of 5 from Image 6. (Note that the nodes are extemely tiny here).
  \begin{choices}{
      \begin{tikzpicture}
        \draw[thick] (0,0)--(10,5);
        \draw[thick] (0,0)--(5,-10);
        \draw[thick] (0,0)--(-10,5);
        \draw[thick] (0,0)--(-5,-10);
        \draw[thick] (0,0)--(10,-5);
        \draw[thick] (0,0)--(-5,10);
        \draw (.5,2) node {$1$};
        \draw (-2.5,2.5) node {$2$};
        \draw (-2,-.5) node {$3$};
        \draw (0,-2.5) node {$4$};
        \draw (2.5,-2.5) node {$5$};
        \draw (2.5,0) node {$6$};
      \end{tikzpicture}
  }
  \item Choice 1
  \item Choice 2
  \item Choice 3
  \item Choice 4
  \item Choice 5
  \end{choices}


\end{enumerate}

\end{document}

enter image description here

ryanj1823
  • 301
  • I don't have a specific answer, I'm afraid, but I'll just add that textpos 1.9.1 has recently been released, which allows one to adjust the default box reference position (which sounds like it's relevant to this question), and incidentally includes an experimental fix to address a TikZ compatibility issue (which sounds like it might be tangentially relevant to this question). – Norman Gray Jun 27 '19 at 09:40
  • @NormanGray, thanks for the reference, I'll have to take a look at version 1.9.1. – ryanj1823 Jun 27 '19 at 22:07
  • If you want center a tikzpicture inside a textblock(the textpos environment) you can put the tikzpicture inside a figure environment (\begin{figure}\begin{tikzpicture}...\end{tikzpicture}\end{figure}). – vi pa Jul 11 '19 at 09:07

3 Answers3

8

If you want to have images automatically resized to fit in the frames while preserving their aspect ratio, then the following should do what you want. Note that the body of the questionpicture environmment is boxed as with lrbox; it can contain anything that can be boxed this way: for instance, one or more character tokens, an image included with \includegraphics, a tikzpicture, etc.).

\documentclass{article}
\usepackage{graphicx}
\usepackage{calc} % for \settototalheight (used for convenience)
\usepackage{xcolor}
\usepackage{xurl}
\usepackage{etoolbox}
\usepackage{xparse}
\usepackage{tikz}
\usetikzlibrary{calc}

% Frame dimensions
\newlength{\mywidth}
\newlength{\myheight}
\setlength{\mywidth}{3.5cm}
\setlength{\myheight}{3.5cm}

\makeatletter

% Use l3keys to support a key/value-style interface for the optional argument
% of the 'choices' environment (l3keys is great!).
\ExplSyntaxOn

\msg_new:nnn { ryanjform } { duplicate-figure-id }
  { duplicate~figure~identifier:~'\exp_not:n {#1}'. }

% Whether to perform the \scalebox-based autoscaling for a given figure
\bool_new:N \l__ryanjform_do_autoscale_pic_in_choices_bool
% Sequence recording all figure identifiers (for the 'scale to max size' TikZ
% style) found so far
\seq_new:N \g__ryanjform_scale_to_max_style_figure_ids_seq

% Define the options supported in the optional argument of the 'choices'
% environment
\keys_define:nn { ryanjform }
  {
    autoscale .bool_set:N = \l__ryanjform_do_autoscale_pic_in_choices_bool,
    % Value used when the 'autoscale' key is passed with no value
    autoscale .default:n = { true },
    autoscale .initial:n = { true }
  }

\cs_new_protected:Npn \__ryanjform_set_keys:n #1
  { \keys_set:nn { ryanjform } {#1} }

\cs_new_protected:Npn \__ryanjform_check_unique_id:n #1
  {
    \seq_if_in:NnTF \g__ryanjform_scale_to_max_style_figure_ids_seq {#1}
      { \msg_error:nnn { ryanjform } { duplicate-figure-id } {#1} }
      { \seq_gput_right:Nn \g__ryanjform_scale_to_max_style_figure_ids_seq {#1} }
  }

% Set up aliases using LaTeX2e naming style
\cs_set_eq:NN \ryanjformsetup \__ryanjform_set_keys:n
\cs_set_eq:NN \ryanjform@check@unique@id \__ryanjform_check_unique_id:n

% If-then-else command using the boolean
% \l__ryanjform_do_autoscale_pic_in_choices_bool to choose the branch
\NewDocumentCommand \ryanjform@ifautoscale@enabled { }
  {
    \bool_if:NTF \l__ryanjform_do_autoscale_pic_in_choices_bool
  }

\ExplSyntaxOff

\newsavebox{\ryanjform@box}     % will be set with \global
\newlength{\ryanjform@total@height}
\newcounter{choice}[enumi]
% In case you want to prepend the question number to the choice number in
% \thechoice, you can use \renewcommand{\thechoice}{\theenumi.\arabic{choice}}
\renewcommand{\thechoice}{\arabic{choice}} % only the choice number

\newenvironment{questionpicture}{%
  \begin{lrbox}{0}}{%
  \end{lrbox}%
  \global\setbox\ryanjform@box=\box0
}

\AfterEndEnvironment{questionpicture}{%
  \setcounter{choice}{0}%
  \par\noindent
  \setlength{\fboxsep}{0pt}%
  \begingroup
  \minipage{\dimexpr\linewidth-\mywidth-2\fboxrule-2\fboxsep}
  \begingroup
  \enumerate
  \let\ryanjform@item@cmd@orig\item
  \let\item\ryanjform@item@cmd
  \ignorespaces
}

\let\ryanjform@start@question@picture\questionpicture
\newcommand*{\ryanjform@invalid@place@for@calling@questionpicture}{%
  \errmessage{The 'questionpicture' environment must be used at the start of a
    'choices' environment}%
}
% Generate an error message unless \questionpicture is used where expected
\let\questionpicture\ryanjform@invalid@place@for@calling@questionpicture

\newenvironment{choices}[1][]{%
  \ryanjformsetup{#1}%
  \let\questionpicture\ryanjform@start@question@picture
  \ignorespaces
}{%
  % Restore \item as it was before the 'choices' environment (re)defined it
  \let\item\ryanjform@item@cmd@orig
  \unskip\ryanjform@hrule       % rule at the end of the last choice
  \endenumerate
  \endgroup
  \endminipage
  \endgroup
  %
  \settototalheight{\ryanjform@total@height}{\usebox{\ryanjform@box}}%
  \pgfmathsetmacro{\ryanjform@x@ratio}{\the\mywidth / \wd\ryanjform@box}%
  \pgfmathsetmacro{\ryanjform@y@ratio}{\the\myheight / \ryanjform@total@height}%
  \pgfmathsetmacro{\ryanjform@scale}{min(\ryanjform@x@ratio,
                                         \ryanjform@y@ratio)}%
  %
  \fcolorbox{red!20}{blue!20}{%
    \begin{minipage}[c][\myheight][c]{\mywidth}
      \centering
      \ryanjform@ifautoscale@enabled{%
        % We know that \ryanjform@scale will be (fully) expanded:
        % <https://tex.stackexchange.com/q/497769/73317>
        \scalebox{\ryanjform@scale}{\usebox{\ryanjform@box}}%
      }{%
        \usebox{\ryanjform@box}%
      }%
    \end{minipage}%
  }%
  \ignorespacesafterend
}

\newcommand*{\ryanjform@item@cmd}{%
  % Automatically insert the horizontal rule unless this is the first \item
  % in the current 'choices' environment
  \ifnum\value{choice}=0\else\unskip\ryanjform@hrule\fi
  \refstepcounter{choice}%
  \ryanjform@item@cmd@orig
}

% Autoscaling technique that doesn't affect font sizes in TikZ pictures.
% (based on code from marmot: <https://tex.stackexchange.com/a/497749/73317>)
%
% #1: unique per-picture id allowing several pictures to use this mechanism
%     in a given document (it should contain no control sequence token nor
%     active character)
% #2: width of the reference rectangle
% #3: height of the reference rectangle
\newcommand*\ryanjform@ExportBB[3]{%
 \path let
   \p1=($(current bounding box.north east)-(current bounding box.south west)$),
   \n1={#2/\x1},\n2={#3/\y1}
 in \pgfextra{\pgfmathsetmacro{\ryanjform@figscale}{min(\n1,\n2)}%
              \expandafter\xdef\csname ryanjform@auto@figscale@#1\endcsname{%
                \ryanjform@figscale}};
 \immediate\write\@mainaux{%
   \string\expandafter
   \gdef\string\csname\space ryanjform@auto@figscale@#1\string\endcsname{%
     \csname ryanjform@auto@figscale@#1\endcsname}}}

\tikzset{scale to max size/.style args={id #1 width #2height #3}{%
    execute at end picture={\ryanjform@ExportBB{#1}{#2}{#3}},
    /utils/exec={\ryanjform@check@unique@id{#1}%
                 \ifcsname ryanjform@auto@figscale@#1\endcsname
                   \wlog{Found autoscale value for picture '#1'}%
                 \else
                   \typeout{Automatically-scaled pictures: please recompile
                            for picture '#1'.}
                   \expandafter\gdef
                     \csname ryanjform@auto@figscale@#1\endcsname{1}
                 \fi},
   scale=\csname ryanjform@auto@figscale@#1\endcsname},
         form autoscale/.style={%
           scale to max size=id #1 width \mywidth height \myheight}}
% End of the code based on <https://tex.stackexchange.com/a/497749/73317>

\newcommand*{\ryanjform@hrule}{%
  \leavevmode
  \unskip\kern 0.5em
  \leaders\hrule height 3pt depth -2.6pt \hfill
  \kern 0.5em
}

\makeatother

% Sample pictures only used to show how to use the 'choices' environment
\newcommand*{\sometikzpictureBase}[1]{%
  \begin{tikzpicture}#1
    \coordinate (A) at (1,5);
    \coordinate (B) at (3,15);
    \node[below left] at (A) {$A$};
    \node[above right] at (B) {$B$};
    % Some rules may disappear if one doesn't substract some “epsilon”, see
    % <https://tex.stackexchange.com/q/13834/73317>
    \draw ($(A)-(0.001, 0.001)$) grid (B);
  \end{tikzpicture}%
}

\newcommand*{\sometikzpicture}{\sometikzpictureBase{}}
\newcommand*{\sometikzpictureWithMarmotScaling}[1]{%
  \sometikzpictureBase{[form autoscale={#1}]}%
}

\begin{document}

\begin{enumerate}
\item Showing the box I want the image to appear in for the examples below.
  \begin{choices}
    \begin{questionpicture}
      \includegraphics{example-image}
    \end{questionpicture}
     \item Choice \thechoice
     \item Choice \thechoice
     \item Choice \thechoice
     \item Choice \thechoice
     \item Choice \thechoice
  \end{choices}

\item Second question:
  \begin{choices}
    \begin{questionpicture}
      \begin{tikzpicture}
        \draw (0,0) grid (2,3);
      \end{tikzpicture}
    \end{questionpicture}
    \item Choice \thechoice
    \item Choice \thechoice
    \item Choice \thechoice
    \item Choice \thechoice
    \item Choice \thechoice
  \end{choices}

\item Third question:
  \begin{choices}
    \begin{questionpicture}
      \tikz \draw (0,0) grid (3,2);
    \end{questionpicture}
    \item Choice \thechoice
    \item Choice \thechoice
    \item Choice \thechoice
  \end{choices}

\item Fourth question using \verb|autoscale=false| in the optional argument of
      the \verb|choices| environment:
  \begin{choices}[autoscale=false]
    \begin{questionpicture}
      \tikz \draw (0,0) grid (3,2);
    \end{questionpicture}
    \item Choice \thechoice
    \item Choice \thechoice
    \item Choice \thechoice
  \end{choices}

\newpage
\item Using \verb|\scalebox|-based autoscaling (default behavior of
  the \verb|choices| environment):
  \begin{choices}
    \begin{questionpicture}
      \sometikzpicture
    \end{questionpicture}
    \item Choice \thechoice
    \item Choice \thechoice
  \end{choices}

\item Same picture using Ti\emph{k}Z scaling (scale factor found using
  marmot's technique at \url{https://tex.stackexchange.com/a/497749/73317},
  via the \verb|form autoscale| style implemented in my answer; note that
  several compilation runs may be necessary to let the computed scale factor
  converge):
  \begin{choices}[autoscale=false]
    \begin{questionpicture}
      \sometikzpictureWithMarmotScaling{grid-2}
    \end{questionpicture}
    \item Choice \thechoice
    \item Choice \thechoice
  \end{choices}

\item A triangle also scaled using the \verb|form autoscale| style:
  \begin{choices}[autoscale=false]
    \begin{questionpicture}
      \begin{tikzpicture}[form autoscale={my triangle}]
        \draw (0,0) node[below left] {$A$}-- (0.5,0.5) node[above] {$B$} --
              (1,0) node[below right] {$C$} -- cycle;
      \end{tikzpicture}
    \end{questionpicture}
    \item Choice \thechoice
    \item Choice \thechoice
  \end{choices}

\item Colored and verbatim material inside the framed box:
  \begin{choices}
    \begin{questionpicture}
      \color{green!20}\verb|@^_&~#%'${}|
    \end{questionpicture}
    \item Choice \thechoice
    \item Choice \thechoice
    \item Choice \thechoice
    \item Choice \thechoice
  \end{choices}
\end{enumerate}

\end{document}

On page 1:

Page 1

On page 2:

Page 2

General remarks on this code

The minipage environment is very useful, as you can see. :-) I use it twice here:

  • with a fixed width and a height that adapts to the contents: this is used around the inner enumerate environment;

  • with a fixed width and a fixed height: this is used inside the frame, and allows for easy centering of the framed material in both horizontal and vertical directions.

The \ryanjform@hrule command allows you to tune the rule parameters in a central place. It has a fixed kern on both sides for æsthetic reasons (IMHO). The rule itself is made with \leaders in a similar way as \hrulefill, but is raised for æsthetic reasons too (see the height and depth parameters; the rule produced by \hrulefill would lie on the baseline). The rule length is therefore not hardcoded in my version: it adapts to the text on the same line before the rule, and to the enclosing minipage.

About the optional scaling done by the choices environment

By default, the choices environment scales the box created from the contents of the questionpicture environment using \scalebox, so that the scaled box fits in the frame and either its width or its height is equal to that of the frame. This scaling process preserves the aspect ratio; it can be turned off on a picture by picture basis using autoscale=false in the optional argument of the choices environment (see below). There is also the \ryanjformsetup command for more durable changes; it will be described below.

This scaling process only considers the box dimensions, it doesn't care about their contents. This implies that fonts used inside a tikzpicture will be scaled along with the rest of the picture. In case you want fonts not to be scaled, you should use autoscale=false or write your tikzpictures so that they need no scaling. You can do the latter either manually or using the TikZ style form autoscale defined in the above document (style based on marmot's code that writes the picture bounding box to the .aux file in order to scale it appropriately on the next LaTeX run). As marmot wrote, you'll probably need to disable the \scalebox-based scaling for tikzpictures that use the remember picture option. The easiest way to do this is to pass autoscale=false to the choices environment, like this:

\begin{choices}[autoscale=false]
  \begin{questionpicture}
    〈boxed material〉
  \end{questionpicture}

where 〈boxed material〉 represents your picture here, but can be pretty much anything.

Each tikzpicture for which you use form autoscale must be assigned a unique identifier containing no control sequence token nor active character. For instance:

\begin{choices}[autoscale=false]
  \begin{questionpicture}
    \begin{tikzpicture}[form autoscale=my identifier]
       (...)
    \end{tikzpicture}
  \end{questionpicture}
  \item (...)
\end{choices}

As said, pictures using the form autoscale style write their scale factor to the .aux file, therefore after the first compilation of a document containing a picture with the identifier used above, you would see this message on the terminal (LaTeX output):

Automatically-scaled pictures: please recompile for picture 'my identifier'.

The form autoscale autoid style defined in an example from a further section allows you to automatically generate such identifiers using a counter, in case you don't want to bother with them.

Possibly desirable changes

  • If you want an inner border inside the frames, just specify the desired width in the line that reads \setlength{\fboxsep}{0pt}%.

  • If you want autoscale=false to be the default (i.e., perform no \scalebox-based scaling by default), simply replace autoscale .initial:n = { true } with autoscale .initial:n = { false }. Then to turn on automatic \scalebox-based scaling for a given framed picture, pass autoscale or autoscale=true in the optional argument of the choices environment:

    \begin{choices}[autoscale] ...
    

    Another way to change the default behavior is to call, for instance, \ryanjformsetup{autoscale=false} somewhere before the choices environments you want to affect. The \ryanjformsetup command respects TeX's scoping rules as defined by groups. It can be used in the preamble or elsewhere in the document.

  • If you prefer this style of alignment between the choices and the framed pictures: Top alignment please do the following.

    1. Replace
      \minipage{\dimexpr\linewidth-\mywidth-2\fboxrule-2\fboxsep}
      

      with:

      \minipage[t]{\dimexpr\linewidth-\mywidth-2\fboxrule-2\fboxsep}
      
    2. Replace
      \begin{minipage}[c][\myheight][c]{\mywidth}
      

      with:

      \begin{minipage}[t][\myheight][c]{\mywidth}
      
    3. Wrap the \fcolorbox inside \raisebox{\baselineskip}{...} like this:
      \raisebox{\baselineskip}{%
        \fcolorbox{red!20}{blue!20}{%
          \begin{minipage}[t][\myheight][c]{\mywidth}
            ...
          \end{minipage}%
      }}%
      

Positioning of tikzpictures in general

Concerning the fact that shifting the coordinates used inside your tikzpictures doesn't affect where they are placed in the LaTeX document, this is indeed correct. A tikzpicture behaves as a box with a width, a height, a depth, a reference point, and gets placed like other boxes by TeX. Without particular options, the width and height are generally just enough to hold the picture contents, and the reference point is located at the lower left corner; but options such as overlay and baseline can change this. You may want to read this answer for more details about this.

Code with different behavior in reply to comments

Here is code implementing a different behavior as described in this comment and those following (mainly: no horizontal rules, no \choice counter anymore, uppercase choice labels, \scalebox-based automatic scaling off by default and “marmot autoscaling” using the .aux file on for all tikzpictures occurring inside the questionpicture environment using an automatically generated picture identifier):

\documentclass{article}
\usepackage{graphicx}
\usepackage{calc} % for \settototalheight (used for convenience)
\usepackage{xcolor}
\usepackage{etoolbox}
\usepackage{xparse}
\usepackage{tikz}
\usepackage{enumitem}
\usetikzlibrary{calc}

% Frame dimensions
\newlength{\mywidth}
\newlength{\myheight}
\setlength{\mywidth}{3.5cm}
\setlength{\myheight}{3.5cm}

\makeatletter

% Use l3keys to support a key/value-style interface for the optional argument
% of the 'choices' environment (l3keys is great!).
\ExplSyntaxOn

\msg_new:nnn { ryanjform } { duplicate-figure-id }
  { duplicate~figure~identifier:~'\exp_not:n {#1}'. }

% Whether to perform the \scalebox-based autoscaling for a given figure
\bool_new:N \l__ryanjform_do_autoscale_pic_in_choices_bool
% Sequence recording all figure identifiers (for the 'scale to max size' TikZ
% style) found so far
\seq_new:N \g__ryanjform_scale_to_max_style_figure_ids_seq
% Counter used when generating automatic figure identifiers for 'form autoscale'
\int_new:N \g_ryanjform_last_autogenerated_figure_nb_int

% Define the options supported in the optional argument of the 'choices'
% environment
\keys_define:nn { ryanjform }
  {
    autoscale .bool_set:N = \l__ryanjform_do_autoscale_pic_in_choices_bool,
    % Value used when the 'autoscale' key is passed with no value
    autoscale .default:n = { true },
    autoscale .initial:n = { false }
  }

\cs_new_protected:Npn \__ryanjform_set_keys:n #1
  { \keys_set:nn { ryanjform } {#1} }

\cs_new_protected:Npn \__ryanjform_check_unique_id:n #1
  {
    \seq_if_in:NnTF \g__ryanjform_scale_to_max_style_figure_ids_seq {#1}
      { \msg_error:nnn { ryanjform } { duplicate-figure-id } {#1} }
      { \seq_gput_right:Nn \g__ryanjform_scale_to_max_style_figure_ids_seq {#1} }
  }

\cs_new_protected:Npn \__ryanjform_form_autoscale:n #1
  {
    \pgfkeys { /tikz/.cd, form~autoscale={#1} }
  }

\cs_generate_variant:Nn \__ryanjform_form_autoscale:n { V }

% Automatic construction of generated ids (the pattern is defined here)
\cs_new:Npn \__ryanjform_autogenerated_id:n #1
  { ryanjform~autogenerated~id~#1 }

\cs_generate_variant:Nn \__ryanjform_autogenerated_id:n { V }

\cs_new_protected:Npn \__ryanjform_form_autoscale_autoid:
  {
    % Increment the counter
    \int_gincr:N \g_ryanjform_last_autogenerated_figure_nb_int
    \tl_set:Nx \l_tmpa_tl       % generate an id based on the counter value
      {
        \__ryanjform_autogenerated_id:V
          \g_ryanjform_last_autogenerated_figure_nb_int
      }
    % Call the 'form autoscale' style with the new id
    \__ryanjform_form_autoscale:V \l_tmpa_tl
  }

% Set up aliases using LaTeX2e naming style
\cs_set_eq:NN \ryanjformsetup \__ryanjform_set_keys:n
\cs_set_eq:NN \ryanjform@check@unique@id \__ryanjform_check_unique_id:n
\cs_set_eq:NN \ryanjform@form@autscale@autoid \__ryanjform_form_autoscale_autoid:

% If-then-else command using the boolean
% \l__ryanjform_do_autoscale_pic_in_choices_bool to choose the branch
\NewDocumentCommand \ryanjform@ifautoscale@enabled { }
  {
    \bool_if:NTF \l__ryanjform_do_autoscale_pic_in_choices_bool
  }

\ExplSyntaxOff

\newsavebox{\ryanjform@box}     % will be set with \global
\newlength{\ryanjform@total@height}

\newenvironment{questionpicture}{%
  % The OP wants 'form autoscale autoid' turned on for every picture framed by
  % the 'choices' environment
  \tikzset{every picture/.style=form autoscale autoid}%
  \begin{lrbox}{0}
}{%
  \end{lrbox}%
  \global\setbox\ryanjform@box=\box0
}

\AfterEndEnvironment{questionpicture}{%
  \par\noindent
  \setlength{\fboxsep}{0pt}%
  \begingroup
  \minipage{\dimexpr\linewidth-\mywidth-2\fboxrule-2\fboxsep}
  \begingroup
  \enumerate[label=(\Alph*)]
}

\let\ryanjform@start@question@picture\questionpicture
\newcommand*{\ryanjform@invalid@place@for@calling@questionpicture}{%
  \errmessage{The 'questionpicture' environment must be used at the start of a
    'choices' environment}%
}
% Generate an error message unless \questionpicture is used where expected
\let\questionpicture\ryanjform@invalid@place@for@calling@questionpicture

\newenvironment{choices}[1][]{%
  \ryanjformsetup{#1}%
  \let\questionpicture\ryanjform@start@question@picture
  \ignorespaces
}{%
  \endenumerate
  \endgroup
  \endminipage
  \endgroup
  %
  \settototalheight{\ryanjform@total@height}{\usebox{\ryanjform@box}}%
  \pgfmathsetmacro{\ryanjform@x@ratio}{\the\mywidth / \wd\ryanjform@box}%
  \pgfmathsetmacro{\ryanjform@y@ratio}{\the\myheight / \ryanjform@total@height}%
  \pgfmathsetmacro{\ryanjform@scale}{min(\ryanjform@x@ratio,
                                         \ryanjform@y@ratio)}%
  %
  \fcolorbox{red!20}{blue!20}{%
    \begin{minipage}[c][\myheight][c]{\mywidth}
      \centering
      \ryanjform@ifautoscale@enabled{%
        % We know that \ryanjform@scale will be (fully) expanded:
        % <https://tex.stackexchange.com/q/497769/73317>
        \scalebox{\ryanjform@scale}{\usebox{\ryanjform@box}}%
      }{%
        \usebox{\ryanjform@box}%
      }%
    \end{minipage}%
  }%
  \ignorespacesafterend
}

% Autoscaling technique that doesn't affect font sizes in TikZ pictures.
% (based on code from marmot: <https://tex.stackexchange.com/a/497749/73317>)
%
% #1: unique per-picture id allowing several pictures to use this mechanism
%     in a given document (it should contain no control sequence token nor
%     active character)
% #2: width of the reference rectangle
% #3: height of the reference rectangle
\newcommand*\ryanjform@ExportBB[3]{%
 \path let
   \p1=($(current bounding box.north east)-(current bounding box.south west)$),
   \n1={#2/\x1},\n2={#3/\y1}
 in \pgfextra{\pgfmathsetmacro{\ryanjform@figscale}{min(\n1,\n2)}%
              \expandafter\xdef\csname ryanjform@auto@figscale@#1\endcsname{%
                \ryanjform@figscale}};
 \immediate\write\@mainaux{%
   \string\expandafter
   \gdef\string\csname\space ryanjform@auto@figscale@#1\string\endcsname{%
     \csname ryanjform@auto@figscale@#1\endcsname}}}

\tikzset{scale to max size/.style args={id #1 width #2height #3}{%
    execute at end picture={\ryanjform@ExportBB{#1}{#2}{#3}},
    /utils/exec={\ryanjform@check@unique@id{#1}%
                 \ifcsname ryanjform@auto@figscale@#1\endcsname
                   \wlog{Found autoscale value for picture '#1'}%
                 \else
                   \typeout{Automatically-scaled pictures: please recompile
                            for picture '#1'.}
                   \expandafter\gdef
                     \csname ryanjform@auto@figscale@#1\endcsname{1}
                 \fi},
   scale=\csname ryanjform@auto@figscale@#1\endcsname},
         form autoscale/.style={%
           scale to max size=id #1 width \mywidth height \myheight},
         % Same style except the id is automatically generated using a counter
         form autoscale autoid/.style={%
           /utils/exec={\ryanjform@form@autscale@autoid}}}
% End of the code based on <https://tex.stackexchange.com/a/497749/73317>

\makeatother

\begin{document}

\begin{enumerate}

\item Original picture
  \begin{choices}
    \begin{questionpicture}
      \begin{tikzpicture}
        \draw[thick] (0,0)--(100,50);
        \draw[thick] (0,0)--(50,-100);
        \draw[thick] (0,0)--(-100,50);
        \draw[thick] (0,0)--(-50,-100);
        \draw[thick] (0,0)--(100,-50);
        \draw[thick] (0,0)--(-50,100);
        \draw (5,20) node {$1$};
        \draw (-25,25) node {$2$};
        \draw (-20,-5) node {$3$};
        \draw (0,-25) node {$4$};
        \draw (25,-25) node {$5$};
        \draw (25,0) node {$6$};
      \end{tikzpicture}
    \end{questionpicture}
    \item Choice
    \item Choice
    \item Choice
    \item Choice
    \item Choice
  \end{choices}

\item Original picture scaled manually by 0.1
  \begin{choices}
    \begin{questionpicture}
      \begin{tikzpicture}
        \draw[thick] (0,0)--(10,5);
        \draw[thick] (0,0)--(5,-10);
        \draw[thick] (0,0)--(-10,5);
        \draw[thick] (0,0)--(-5,-10);
        \draw[thick] (0,0)--(10,-5);
        \draw[thick] (0,0)--(-5,10);
        \draw (.5,2) node {$1$};
        \draw (-2.5,2.5) node {$2$};
        \draw (-2,-.5) node {$3$};
        \draw (0,-2.5) node {$4$};
        \draw (2.5,-2.5) node {$5$};
        \draw (2.5,0) node {$6$};
      \end{tikzpicture}
    \end{questionpicture}
    \item Choice
    \item Choice
    \item Choice
    \item Choice
    \item Choice
  \end{choices}

\item Original picture scaled manually by 0.01
  \begin{choices}
    \begin{questionpicture}
      \begin{tikzpicture}
        \draw[thick] (0,0)--(1,.5);
        \draw[thick] (0,0)--(.5,-1);
        \draw[thick] (0,0)--(-1,.5);
        \draw[thick] (0,0)--(-.5,-1);
        \draw[thick] (0,0)--(1,-.5);
        \draw[thick] (0,0)--(-.5,1);
        \draw (.05,.2) node {$1$};
        \draw (-.25,.25) node {$2$};
        \draw (-.2,-.05) node {$3$};
        \draw (0,-.25) node {$4$};
        \draw (.25,-.25) node {$5$};
        \draw (.25,0) node {$6$};
      \end{tikzpicture}
    \end{questionpicture}
    \item Choice
    \item Choice
    \item Choice
    \item Choice
    \item Choice
  \end{choices}

\clearpage

\item Original picture shifted up/right by 100.
  \begin{choices}
    \begin{questionpicture}
      \begin{tikzpicture}
        \draw[thick] (100,100)--(200,150);
        \draw[thick] (100,100)--(150,0);
        \draw[thick] (100,100)--(0,150);
        \draw[thick] (100,100)--(50,0);
        \draw[thick] (100,100)--(200,50);
        \draw[thick] (100,100)--(50,200);
        \draw (105,120) node {$1$};
        \draw (75,125) node {$2$};
        \draw (80,95) node {$3$};
        \draw (100,75) node {$4$};
        \draw (125,75) node {$5$};
        \draw (125,100) node {$6$};
      \end{tikzpicture}
    \end{questionpicture}
    \item Choice
    \item Choice
    \item Choice
    \item Choice
    \item Choice
  \end{choices}

  \item Picture from Problem 4 scaled by 0.1 (which happens to be the picture from Problem 2 shifted up/right by 10 because \emph{math})
  \begin{choices}
    \begin{questionpicture}
      \begin{tikzpicture}
        \draw[thick] (10,10)--(20,15);
        \draw[thick] (10,10)--(15,0);
        \draw[thick] (10,10)--(0,15);
        \draw[thick] (10,10)--(5,0);
        \draw[thick] (10,10)--(20,5);
        \draw[thick] (10,10)--(5,20);
        \draw (10.5,12) node {$1$};
        \draw (7.5,12.5) node {$2$};
        \draw (8,9.5) node {$3$};
        \draw (10,7.5) node {$4$};
        \draw (12.5,7.5) node {$5$};
        \draw (12.5,10) node {$6$};
      \end{tikzpicture}
    \end{questionpicture}
    \item Choice
    \item Choice
    \item Choice
    \item Choice
    \item Choice
  \end{choices}

\end{enumerate}

\end{document}

Page 1:

Page 1


Page 2:

Page 2

Conditional enumitem setup

As pointed out in comments, loading parskip modifies the vertical spacing between items of the enumerate environment from enumitem. Here is one way to deal with this problem:

\documentclass{article}

(...)

\makeatletter

\newtoggle{parskiploaded}
\@ifpackageloaded{parskip}
  {\toggletrue{parskiploaded}}
  {\togglefalse{parskiploaded}}

(...)

\AfterEndEnvironment{questionpicture}{%

  (...)

  \begingroup
  \iftoggle{parskiploaded}%
    {\enumerate[label=(\Alph*),itemsep=7pt]}%
    {\enumerate[label=(\Alph*)]}%
}

(...)

\end{document}

We need two steps because \@ifpackageloaded only works in the preamble. For alternatives to the \iftoggle I used from etoolbox, you may want to read this answer (\iftoggle is quite fine, though).

frougon
  • 24,283
  • 1
  • 32
  • 55
  • @NormanGray Hi, thanks for your comment! My answer doesn't use textpos at all, so I'm not really sure about what you meant in your final parenthesis. If you meant that some comment on the OP's code usingtextpos could be added to the answer, I'd be grateful that you add it yourself (via an edit of my answer or your own answer if you prefer) or write it here more explicitly, because my experience with textpos is short and slightly ancient. :-) (it didn't appear to me that absolute positioning on the page was useful here, so I dismissed this approach right away when answering) – frougon Jun 27 '19 at 08:02
  • Apologies: I skimmed your answer too quickly and didn't realise it was taking a different approach. I've moved my comment to the question. – Norman Gray Jun 27 '19 at 09:41
  • @NormanGray No problem, thanks. – frougon Jun 27 '19 at 09:48
  • @frougon, thanks for the explanation and code. Just to make sure that I understand the minipage-stuff, you've created a first minipage that creates an "image box" based on how many answer choices I have (2 answer choices = small box height, while 8 answer choices = large box height). After this, you've created a second minipage (that in my case, is probably smaller in height than the first minipage) that contains the image itself. Does that all sound right? Also, am I correct in assuming that a \draw (0,0) grid (-2,-3) or \draw (1,5) grid (3,8) would look exactly like the image in #2? – ryanj1823 Jun 27 '19 at 22:06
  • The first minipage contains the inner enumerate: (a) Choice 1 + rule, (b) Choice 2 + rule ... (e) Choice 5 + rule. Its width is determined by the \linewidth and the fixed 3.5cm x 3.5cm (plus 2\fboxsep and 2\fboxrule) square frame; the height of this first minipage is just enough to hold the choices inside—but we can make it fixed if you prefer. The second minipage is all the framed contents (i.e., the frame is as tight as \fboxsep says around this minipage, and I locally set \fboxsep to 0pt in the environment). Concerning the TikZ grid: yes, it works as you said. – frougon Jun 27 '19 at 22:18
  • @frougon, thanks for the clarification. I've noticed something interesting happening as I change the coordinates I use in my tikzpicture. I've added an addendum to my original question post that includes some code to explain what's going on. (Sorry to all if this isn't protocol, I'm new to SE and am not quite sure about where to post extensive comments) – ryanj1823 Jun 28 '19 at 05:27
  • I don't see the question as having been edited. (?) Concerning the “protocol”, I think it relies a bit on politeness and “common sense.” I'd say that small changes in the first day are okay, and clarifications are always welcome. But the question isn't supposed to change day after day. If a change is making the question different, it should be avoided and a new question should be asked instead. Besides, when you have an answer that answers the question, you should do as indicated in “How do you accept an answer?”. – frougon Jun 28 '19 at 05:37
  • I just edited/updated it. Thanks for the "protocol" lesson! – ryanj1823 Jun 28 '19 at 05:40
  • Okay, you've noticed that big pictures are scaled down to fit (sorry, the beginning text of my answer was misleading, will fix it). This is the behavior you want, right? So, what do you want to change? Is it that you want any picture to completely fill the box in width or height, whichever is larger? For now, the \adjustbox I've used only does “shrink if too big”; but I think calculating the scale manually should allow doing what I just said, if this is what you would like. – frougon Jun 28 '19 at 05:50
  • Correct, I would like any picture to completely fill the box. Moreover, I would hope that two pictures with the same aspect ratio (not sure if I'm using that term correctly) would look the same. That is, if I created a triangle using the points (0,0)--(1,0)--(0,1) and you created a triangle using the points (0,0)--(10,0)--(0,10), they would look the same because the proportions are the same (this is sounding ludicrous as I'm typing it). Basically, I'd like to create each Tikz-picture using dimensions that make sense for that picture, and the picture is placed into the 3.5cm x 3.5cm box. – ryanj1823 Jun 28 '19 at 05:57
  • Nothing is ludicrous in your description, and aspect ratio is precisely the appropriate term here. Unless I'm mistaken, my last edit should do exactly what you want. I didn't find a way to do this type of scaling with \adjustbox, so I computed the scale with pgfmath and used \scalebox with the result and the box. I added another example that shows that you can use the shorter \tikz syntax (instead of the tikzpicture environment) for simple drawings like the grids we have here. – frougon Jun 28 '19 at 07:17
  • I extended marmot's scaling method to 1) support several pictures scaled with this technique per document 2) allow for a rectangular frame, not necessarily a square one 3) provide a form autoscale TikZ style that “knows” the common dimensions for all frames in your particular use case (form autoscale automatically supplies \mywidth and \myheight to my extended scale to max size style based on marmot's style of the same name). This is all incorporated in my code, please see the last revision of the answer. – frougon Jun 29 '19 at 08:15
  • You don't need to write \myrule at the end of choice items anymore: the horizontal rules will be automatically drawn. You also have a choice counter now. Its default representation can be obtained and modified via \thechoice (see the commented out definition in my code). I also added a \ryanjformsetup command to change the default behavior of choices environments present inside a given TeX group; this command may be called as \ryanjformsetup{autoscale=false}, for instance. The current code supports catcode-sensitive stuff inside the box, such as verbatim material. – frougon Jun 29 '19 at 20:30
  • Hi all. Sorry, I have been offline for a while, and I appreciate the extensive help on this question and my other question. I'm going to take an extensive look into all of this a little later, check it all out, and get back to everyone! – ryanj1823 Jun 30 '19 at 06:45
  • Alright, I'm back! Again, thanks for all of the help. Using both autoscale=false and form autoscale={identifier} was what I was looking for, as shown here (note that I removed the \hrule and \thechoice features as I won't need them). Three questions and two requests follow. First, what does the code between \ExplSyntaxOn and \ExplSyntaxOff do? Second, I do not have to compile twice to get form autoscale to work (it compiles and produces the desired PDF in one compilation). Because of this, I'm wondering if there's a way to get rid ... – ryanj1823 Jul 02 '19 at 20:47
  • ... of the identifier feature and just write \begin{tikzpicture}[form autoscale]? Third (and related to #2), can the code be altered to always have form autoscale turned on so I can just write \begin{tikzpicture} and be on my way with Tikz-code? As for the requests, I was hoping that questions #2 and #3 could be addressed in the code I currently have. Second, I would like my answer labels to be capital letters rather than lowercase, and wasn't quite sure where to insert \item labeling into the code. Thanks again everyone! – ryanj1823 Jul 02 '19 at 20:47
  • The code between \ExplSyntaxOn and \ExplSyntaxOff does two things: 1) Process the argument containing key=value pairs (the argument where you can use autoscale[=true|false]). It uses the powerful l3keys and is ready to extend if you need other options. 2) It has a little function to print a friendly error message if two pics use the same id (as passed to form autoscale). It's incorrect that one compilation is enough. Remove the .aux file and you should see that there are huge overfull \hbox messages reported on the first run, because the figures haven't been scaled... – frougon Jul 02 '19 at 22:24
  • ... appropriately yet (at least, I do see them). Even the second compilation has little overfulls, but the third one is perfect (that's why I mentioned convergence). The ids are absolutely necessary, because we must be able to tell which scale belongs to which picture in the .aux file. But they could be automatically generated on the fly (no time tonight, sorry). For the labels, you can use \usepackage{enumitem} and then make the end of \ryanjform@startchoices@aftergroup@hook look like this: \minipage{\dimexpr\linewidth-\mywidth-2\fboxrule-2\fboxsep} \enumerate[label=\Alph*.]}... – frougon Jul 02 '19 at 22:41
  • ... (the \ignorespaces isn't necessary anymore, then, because \enumerate will do it). Beware of introducing blank lines (your changes introduced one, apparently ignored, but a bit risky—put a % when unsure). – frougon Jul 02 '19 at 22:42
  • Unless I'm being dumb, I'm not needing to compile multiple times. I deleted the .aux file (and all the others except for .tex just to be safe), recompiled the .tex file, and it worked just fine. I even copy and pasted the code into a blank file, saved it as .tex, then compiled, and it worked. But I digress (it's not really important to me how many times I need to compile. And thanks for the blank space note, I didn't catch that! Are there any simple examples of "other options" to use in key=value pairs? – ryanj1823 Jul 03 '19 at 05:55
  • Seriously? I just redownloaded your own document and compiled it as is, the output is like this. marmot's method requires several passes, there is no way around that. First compilation uses 1:1 scale for all pics using his method. Besides, I told you to look at the LaTeX terminal output. The overfull boxes are duly reported. As for other examples of l3keys, you should find some by looking for \keys_define:nn. One of mine is here. A more complex one... – frougon Jul 03 '19 at 06:17
  • ... here (not mine but reviewed & fixed by me). But better read the manual first, it's relatively short and well explained, with a few examples: look for l3keys in interface3.pdf. If you don't understand, it means you need to read expl3.pdf and possibly l3styleguide.pdf first (expl3.pdf is really a must-read LaTeX3 intro). – frougon Jul 03 '19 at 06:24
  • 100%. I redownloaded and compiled just as you did, and got no errors, warnings, or overfull boxes. I could make a screen-recording of what I'm doing as proof, but that'll have to come in the morning. – ryanj1823 Jul 03 '19 at 06:25
  • What are you compiling with? It's hard to believe... – frougon Jul 03 '19 at 06:26
  • A while back, I installed LaTeX/Sublime for MacOS via this link. I followed the installation directions precisely, so I'm using MacTeX. Not sure if that's what you're looking for, or if there's an easy way to show you what it is you're looking for. – ryanj1823 Jul 03 '19 at 06:31
  • I just wanted to know if it's something that automatically compiles several times without you realizing. No experience with macs, so... – frougon Jul 03 '19 at 06:38
  • That's my guess. Here's a screen recording – ryanj1823 Jul 03 '19 at 06:46
  • This video doesn't show the LaTeX textual output, so we really can't say how many times the code is compiled, which TeX engine is used, nor can we see potential badbox messages (I did see the “0 badboxes” or similar message, but that is not LaTeX's message). And nothing proves in the recording that the PDF file wasn't created from another document (by mistake, not by malice). Anyway. See Code with different behavior in reply to comments in my last edit: it implements your requests (automatically generated ids, uppercase labels, etc.). – frougon Jul 03 '19 at 07:11
  • 1
    Please see the last edit. There was still a problem with color stack handling inside the boxed material. I tried to keep the previous syntax while still dealing correctly with catcode-sensitive material (such as verbatim), but the nasty \color that uses \aftergroup and might occur inside the boxed material makes this too difficult (I could do it, but that would be hackish and space handling before the first \item would be unsatisfactory). Therefore, in order to solve the problem cleanly without losing on features, I had to change the input syntax: the boxed material must now be given... – frougon Jul 04 '19 at 09:51
  • ... inside a questionpicture environment. This environment is essentially a wrapper around lrbox, therefore it should be quite robust. I believe the bug is fixed for good. – frougon Jul 04 '19 at 09:51
  • @frougon My sincere compliments. Excellent. – Sebastiano Jul 04 '19 at 11:47
  • 1
    Thanks @Sebastiano, always appreciated. :-) – frougon Jul 04 '19 at 22:21
  • I've noticed that inserting \usepackage{parskip} (which I'm using in my document) decreases the item sep. I can manually edit the itemsep by including itemsep=7pt in the options for \enumerate, producing a good result. I'm wondering if there is a way to properly handle the item separation while using the parskip package rather than just manually editing the itemsep? – ryanj1823 Jul 10 '19 at 18:27
  • You could set the value conditionally using \@ifpackageloaded, but getting this kind of thing “perfect” is an endless game. Probably someone is going to use setspace and will need another special treatment, etc. But maybe that will be enough for your use, after all. I have no magic solution for these interactions. Similarly, [french]{babel} has this kind of interaction with list spacing (that's on purpose, but when using enumitem, you need a “complex” setup to get the same list spacing you get with \usepackage[french]{babel} and enumitem not loaded...). – frougon Jul 10 '19 at 19:35
  • I added code to my answer doing this (see at the end). I had to remove a few words from the explanation because the answer had reached the maximum limit of 30000 characters... – frougon Jul 10 '19 at 22:42
  • Thanks for that. And hopefully the last question I have: I tried to make sure each problem was on the same page using the samepage environment, but it didn't seem to do anything. I believe it has something to do with using minipage inside of samepage. I then tried to put a \begin{minipage}{\textwidth} and a corresponding \end{minipage}, but it really messed up all of the text. I'm not sure if I'm just not implementing samepage or minipage correctly, or if there's just an inherent fault in trying to minipage within a minipage. – ryanj1823 Jul 11 '19 at 05:10
  • Using minipage works (I don't know samepage), but this is going to make very high blocks that can't be broken. A problem can't span over two pages, then. Do you want each of the outer items to be in its own minipage? Well, I've implemented it. I've posted it as a separate answer because of the 30000 characters limitation... – frougon Jul 11 '19 at 06:44
4

I am putting forward this approach with a good deal of diffidence. It is, frankly, a brute-force approach. Still it is relatively simple and maintains much of the OP's original code. This should be effective for any reasonably well-behaved and self-contained TikZ graphic. As examples, I have used graphics both from this post and from http://www.texample.net.

The choices environment now has an optional argument for a correction (generally small) to the scaling used for the graphic (see items 12 and 14 below). The scaling is used to change the font size in TikZ nodes only. Rules and lines (the consequence of \draw for example) are left untouched.

There is a lot of back-and-forth about this in comments, and I may have missed something.

\documentclass{article}

\usepackage{graphicx}
\usepackage{xcolor}
\usepackage{tikz}
\usepackage{anyfontsize} %% used to be able to scale CM fonts. Unnecessary with fontspec and OTF fonts.
\usepackage[margin=0.5in]{geometry}%% For the examples. Remove.
\usepackage{multicol} %% Only for compact framework for examples

\usetikzlibrary{calc,matrix} %% matrix for example 14

% Frame dimensions
\newlength{\mywidth}
\newlength{\myheight}
\setlength{\mywidth}{3.5cm}
\setlength{\myheight}{3.5cm}
\newsavebox{\testbox}
\usepackage{calc}
\newlength{\testht}

%% Alter the size of text in TikZ nodes.
\def\putit{\tikzset{every node/.append style={font=\fontsize{\testing}{\testing}\selectfont}}}

\makeatletter

\newenvironment{choices}[2][0]{% #1 Optional scaling correction; #2 the graphic
  \def\testing{10}
  \def\braced@stuff@to@include{{#2}}%
  \par\noindent
  %% Set the graphic as-is
  \sbox{\testbox}{\braced@stuff@to@include}
  %% Get the necessary scales (c)hange the 0.3cm to suit -- gives a frame to the graphic)
  \pgfmathsetmacro{\scalewd}{(\mywidth-0.3cm)/\wd\testbox}
  \pgfmathsetmacro{\scaleht}{(\myheight-0.3cm)/\ht\testbox}
  %% Choose the largest scale amount plus any correction...
  \pgfmathsetmacro{\myscale}{ifthenelse(greater(\scaleht,\scalewd),\scalewd+#1,\scaleht+#1)}
  %% ...and use it to scale type in TikZ nodes -- '8' seems best factor, but change to suit:
  \pgfmathsetmacro{\testing}{8/(\myscale)}%\typeout{!!!!!!!\theenumi: \myscale, \testing}%
  %% re-set the graphic (alas!) with altered nodes
  \sbox{\testbox}{%
    \putit %% Resize text in nodes
    \braced@stuff@to@include
}
  \minipage{\dimexpr\linewidth-\mywidth-2\fboxrule-2\fboxsep-6pt\relax}
  \enumerate
  \ignorespaces
}{%
  \unskip
  \endenumerate
  \endminipage
  \setlength{\fboxsep}{0pt}%
  \fcolorbox{blue!20}{green!20}{%
    \begin{minipage}[c][\myheight][c]{\mywidth}
      \centering
      \scalebox{\myscale}{\usebox{\testbox}}
    \end{minipage}%
  }%
  \ignorespacesafterend
}

\makeatother

\def\mychoice{Choice \arabic{enumii}}
\raggedcolumns

\begin{document}

\begin{multicols*}{2}

\begin{enumerate}

\item Image 1, scaled with a \verb+\textbullet+ in a node, but the node is at the correct size.
  \begin{choices}{%
      \begin{tikzpicture}
        \draw (0,0) rectangle (1,1);
        \draw (0.5,0.5) node {\textbullet};  % node placed in center of square
      \end{tikzpicture}}
  \item Choice 1
  \item Choice 2
  \item Choice 3
  \item Choice 4
  \item Choice 5
  \end{choices}

\item Image 2: same proportions as Image 1, but it's larger but the bullet (in a node) is the same size
  \begin{choices}{
      \begin{tikzpicture}
        \draw (0,0) rectangle (3.5,3.5);
        \draw (1.75,1.75) node {\textbullet}; % node placed in center of square
      \end{tikzpicture}
  }
  \item Choice 1
  \item Choice 2
  \item Choice 3
  \item Choice 4
  \item Choice 5
  \end{choices}

\item Showing the box I want the image to appear in for the examples below.
  \begin{choices}{\includegraphics{example-image}}
     \item \mychoice
     \item \mychoice
     \item \mychoice
     \item \mychoice
     \item \mychoice
  \end{choices}

\item Second question:
  \begin{choices}{%
      \begin{tikzpicture}
        \draw (0,0) grid (2,3);
      \end{tikzpicture}}
    \item \mychoice
    \item \mychoice
    \item \mychoice
    \item \mychoice
    \item \mychoice
  \end{choices}

\item Third question.
  \begin{choices}{\tikz \draw (0,0) grid (15,20);}
    \item \mychoice
    \item \mychoice
    \item \mychoice
  \end{choices}

\columnbreak

\item Fourth question.
  \begin{choices}{%
    \begin{tikzpicture}
    \coordinate (A) at (1,5);
    \coordinate (B) at (3,15);
    \node[below left] at (A) {$A$};
    \node[above right] at (B) {$B$};
    % Some rules may disappear if one doesn't substract some “epsilon”, see
    % <https://tex.stackexchange.com/q/13834/73317>
    \draw ($(A)-(0.001, 0.001)$) grid (B);
  \end{tikzpicture}%
}
    \item \mychoice
    \item \mychoice
  \end{choices}


\item Fifth question, a triangle.
  \begin{choices}{
    \begin{tikzpicture}
        \draw (0,0) node[below left] {$A$}-- (0.5,0.5) node[above] {$B$} --
              (1,0) node[below right] {$C$} -- cycle;
    \end{tikzpicture}}
    \item \mychoice
    \item \mychoice
  \end{choices}

  \item Image \theenumi: ideally, the next three images would look identical as they have the same coordinates, just scaled up and down. Note that the lines are scaled, but the nodes (apparently) are not.
  \begin{choices}{
      \begin{tikzpicture}
        \draw[thick] (0,0)--(1,.5);
        \draw[thick] (0,0)--(.5,-1);
        \draw[thick] (0,0)--(-1,.5);
        \draw[thick] (0,0)--(-.5,-1);
        \draw[thick] (0,0)--(1,-.5);
        \draw[thick] (0,0)--(-.5,1);
        \draw (.05,.2) node {$1$};
        \draw (-.25,.25) node {$2$};
        \draw (-.2,-.05) node {$3$};
        \draw (0,-.25) node {$4$};
        \draw (.25,-.25) node {$5$};
        \draw (.25,0) node {$6$};
      \end{tikzpicture}
  }
  \item \mychoice
  \item \mychoice
  \item \mychoice
  \item \mychoice
  \item \mychoice
  \end{choices}

  \item Image \theenumi: all coordinates are doubled from Image \number\numexpr\theenumi-1\relax.
  \begin{choices}{
      \begin{tikzpicture}
        \draw[thick] (0,0)--(2,1);
        \draw[thick] (0,0)--(1,-2);
        \draw[thick] (0,0)--(-2,1);
        \draw[thick] (0,0)--(-1,-2);
        \draw[thick] (0,0)--(2,-1);
        \draw[thick] (0,0)--(-1,2);
        \draw (.1,.4) node {$1$};
        \draw (-.5,.5) node {$2$};
        \draw (-.4,-.1) node {$3$};
        \draw (0,-.5) node {$4$};
        \draw (.5,-.5) node {$5$};
        \draw (.5,0) node {$6$};
      \end{tikzpicture}
  }
  \item \mychoice
  \item \mychoice 
  \item \mychoice
  \item \mychoice
  \item \mychoice
  \end{choices}

  \item Image \theenumi: all coordinates are multiplied by a factor of 5 from Image \number\numexpr\theenumi-1\relax. (Note that the nodes are \emph{no longer} extemely tiny here).
  \begin{choices}{
      \begin{tikzpicture}
        \draw[thick] (0,0)--(10,5);
        \draw[thick] (0,0)--(5,-10);
        \draw[thick] (0,0)--(-10,5);
        \draw[thick] (0,0)--(-5,-10);
        \draw[thick] (0,0)--(10,-5);
        \draw[thick] (0,0)--(-5,10);
        \draw (.5,2) node {$1$};
        \draw (-2.5,2.5) node {$2$};
        \draw (-2,-.5) node {$3$};
        \draw (0,-2.5) node {$4$};
        \draw (2.5,-2.5) node {$5$};
        \draw (2.5,0) node {$6$};
      \end{tikzpicture}
  }
  \item \mychoice
  \item \mychoice
  \item \mychoice
  \item \mychoice
  \item \mychoice
  \end{choices}

\columnbreak

\item A random example from TeXample.net.
\begin{choices}{
\begin{tikzpicture} 
%% http://www.texample.net/tikz/examples/parallelepiped/
% Figure parameters (tta and k needs to have the same sign)
% They can be modified at will
\def \tta{ -10.00000000000000 } % Defines the first angle of perspective
\def \k{    -3.00000000000000 } % Factor for second angle of perspective
\def \l{     6.00000000000000 } % Defines the width  of the parallelepiped
\def \d{     5.00000000000000 } % Defines the depth  of the parallelepiped
\def \h{     7.00000000000000 } % Defines the heigth of the parallelepiped

% The vertices A,B,C,D define the reference plan (vertical)
\coordinate (A) at (0,0); 
\coordinate (B) at ({-\h*sin(\tta)},{\h*cos(\tta)}); 
\coordinate (C) at ({-\h*sin(\tta)-\d*sin(\k*\tta)},
                    {\h*cos(\tta)+\d*cos(\k*\tta)}); 
\coordinate (D) at ({-\d*sin(\k*\tta)},{\d*cos(\k*\tta)}); 

% The vertices Ap,Bp,Cp,Dp define a plane translated from the 
% reference plane by the width of the parallelepiped
\coordinate (Ap) at (\l,0); 
\coordinate (Bp) at ({\l-\h*sin(\tta)},{\h*cos(\tta)}); 
\coordinate (Cp) at ({\l-\h*sin(\tta)-\d*sin(\k*\tta)},
                     {\h*cos(\tta)+\d*cos(\k*\tta)}); 
\coordinate (Dp) at ({\l-\d*sin(\k*\tta)},{\d*cos(\k*\tta)}); 

% Marking the vertices of the tetrahedron (red)
% and of the parallelepiped (black)
\fill[black]  (A) circle [radius=2pt]; 
\fill[red]    (B) circle [radius=2pt]; 
\fill[black]  (C) circle [radius=2pt]; 
\fill[red]    (D) circle [radius=2pt]; 
\fill[red]   (Ap) circle [radius=2pt]; 
\fill[black] (Bp) circle [radius=2pt]; 
\fill[red]   (Cp) circle [radius=2pt]; 
\fill[black] (Dp) circle [radius=2pt]; 

% painting first the three visible faces of the tetrahedron
\filldraw[draw=red,bottom color=red!50!black, top color=cyan!50]
  (B) -- (Cp) -- (D);
\filldraw[draw=red,bottom color=red!50!black, top color=cyan!50]
  (B) -- (D)  -- (Ap);
\filldraw[draw=red,bottom color=red!50!black, top color=cyan!50]
  (B) -- (Cp) -- (Ap);

% Draw the edges of the tetrahedron
\draw[red,-,very thick] (Ap) --  (D)
                        (Ap) --  (B)
                        (Ap) -- (Cp)
                        (B)  --  (D)
                        (Cp) --  (D)
                        (B)  -- (Cp);

% Draw the visible edges of the parallelepiped
\draw [-,thin] (B)  --  (A)
               (Ap) -- (Bp)
               (B)  --  (C)
               (D)  --  (C)
               (A)  --  (D)
               (Ap) --  (A)
               (Cp) --  (C)
               (Bp) --  (B)
               (Bp) -- (Cp);

% Draw the hidden edges of the parallelepiped
\draw [gray,-,thin] (Dp) -- (Cp);
                    (Dp) --  (D);
                    (Ap) -- (Dp);

% Name the vertices (the names are not consistent
%  with the node name, but it makes the programming easier)
\draw (Ap) node [right]           {$A$}
      (Bp) node [right, gray]     {$F$}
      (Cp) node [right]           {$D$}
      (C)  node [left,gray]       {$E$}
      (D)  node [left]            {$B$}
      (A)  node [left,gray]       {$G$}
      (B)  node [above left=+5pt] {$C$}
      (Dp) node [right,gray]      {$H$};

% Drawing again vertex $C$, node (B) because it disappeared behind the edges.
% Drawing again vertex $H$, node (Dp) because it disappeared behind the edges.
\fill[red]   (B) circle [radius=2pt]; 
\fill[gray] (Dp) circle [radius=2pt]; 

% From the reference and this example one can easily draw 
% the twin tetrahedron jointly to this one.
% Drawing the edges of the twin tetrahedron
% switching the p_s: A <-> Ap, etc...
\draw[red,-,dashed, thin] (A)  -- (Dp)
                          (A)  -- (Bp)
                          (A)  --  (C)
                          (Bp) -- (Dp)
                          (C)  -- (Dp)
                          (Bp) --  (C);
\end{tikzpicture}
}
\item \mychoice
\item \mychoice
\end{choices}

\item Another sample from TeXample.net. A small scaling correction was necessary $=-0.05$.
\begin{choices}[-.05]{% Note scaling correction
% A hexagon for memorizing trigonometric identities
% Author: Josef Nilsen
% http://www.texample.net/tikz/examples/trigonometric-hexagon/
\begin{tikzpicture}[scale=4,cap=round,>=latex]
% Radius of regular polygons
  \newdimen\R
  \R=0.8cm
  \coordinate (center) at (0,0);
 \draw (0:\R)
     \foreach \x in {60,120,...,360} {  -- (\x:\R) }
              -- cycle (300:\R) node[below] {$\csc \theta$}
              -- cycle (240:\R) node[below] {$\sec \theta$}
              -- cycle (180:\R) node[left] {$\tan \theta$}
              -- cycle (120:\R) node[above] {$\sin \theta$}
              -- cycle (60:\R) node[above] {$\cos \theta$}
              -- cycle (0:\R) node[right] {$\cot \theta$};
  \draw { (60:\R) -- (120:\R) -- (center) -- (60:\R) } [fill=gray];
  \draw { (180:\R) -- (240:\R) -- (center) -- (180:\R) } [fill=gray];
  \draw { (0:\R) -- (300:\R) -- (center) -- (0:\R) }  [fill=gray];
   \R=0.1cm
  \draw (0:\R) \foreach \x in {60,120,...,360} { -- (\x:\R) }
    [fill=white] -- cycle (center) node {1};
\end{tikzpicture}
}
\item\mychoice
\item\mychoice
\item\mychoice
\item\mychoice
\item\mychoice
\end{choices}

\item Yet another from TeXample.net
\begin{choices}{%
% CIELAB color space
% Vilson Vieira <vilson@void.cc> - http://automata.cc - 2013 - copyleft
% http://www.texample.net/tikz/examples/cielab/
\begin{tikzpicture}[scale=1.5]
  % b* shade
  \path[draw, shade, left color=blue, right color=yellow, opacity=.6] 
    (0,0,0) node[below] {$-b*$} -- (5,2.0,0) node[below] {$+b*$}
    -- (5, 2.5, 0) -- (0, 0.5, 0) -- cycle;

  % a* shade
  \path[draw, shade, left color=green, right color=red, opacity=.6] 
  (0, 2.0, 0) node[below] {$-a*$} -- (5, 0, 0) node[below] {$+a*$} 
  -- (5, .5, 0) -- (0, 2.5, 0) -- cycle;

  % L* shade
  \path[draw, shade, top color=white, bottom color=black, opacity=.6] 
    (2.65, -1.85, 0) node[right] {$L* = 0$} -- (2.65, 4.45, 0)
    node[right] {$L*=100$} -- (2.35, 4.3, 0)  -- (2.35, -2., 0) -- cycle;

  % b*-axis
  \draw[<->] (0,0.25,0) -- (5, 2.25, 0);
  % a*-axis
  \draw[<->] (0,2.25,0) -- (5, 0.25, 0);
  % L*-axis
  \draw[<->] (2.5,-1.90,0) -- (2.5,4.35,0);
\end{tikzpicture}
}
\item\mychoice
\item\mychoice
\item\mychoice
\item\mychoice
\item\mychoice
\end{choices}

\item And yet another from TeXampe.net. This required the addition of \verb+ampersand replacement=\&+  and changing \verb+&+ to \verb+\&+ in the matrix. Note small scaling correction ($=0.1$) to enlarge the  CD slightly.
% https://tex.stackexchange.com/questions/1111/problem-with-defining-shortcuts-for-tikz-matrices
\begin{choices}[0.1]{%
\begin{tikzpicture}
% A simple commutative diagram
% Stefan Kottwitz
% http://www.texample.net/tikz/examples/commutative-diagram-tikz/
  \matrix (m) [ampersand replacement=\&,matrix of math nodes,row sep=3em,column sep=4em,minimum width=2em]
  {
     F_t(x) \& F(x) \\
     A_t \& A \\};
  \path[-stealth]
    (m-1-1) edge node [left] {$\mathcal{B}_X$} (m-2-1)
            edge [double] node [below] {$\mathcal{B}_t$} (m-1-2)
    (m-2-1.east|-m-2-2) edge node [below] {$\mathcal{B}_T$}
            node [above] {$\exists$} (m-2-2)
    (m-1-2) edge node [right] {$\mathcal{B}_T$} (m-2-2)
            edge [dashed,-] (m-2-1);
\end{tikzpicture}
}
\item\mychoice
\item\mychoice
\item\mychoice
\item\mychoice
\item\mychoice
\end{choices}

\end{enumerate}

\end{multicols*}

\end{document}

First page

Second page

sgmoye
  • 8,586
  • Hi, only had a very quick look, but grabbing the braced contents as a macro argument has the consequence that catcode-sensitive stuff (like verbatim material) can't work... – frougon Jul 04 '19 at 14:27
  • True, though that sort of possibility did not seem a part of the OP's concern. Yes, there are limitations... – sgmoye Jul 04 '19 at 14:31
  • Okay, so, basically, your idea is to apply \scalebox-based scaling to the pictures but before that, correct the font sizes used in all TikZ nodes using a factor proportional to the inverse of the scale factor that is going to be applied to the whole box. Thanks for your participation. :-) – frougon Jul 04 '19 at 15:08
  • Correct -- and no one could be more surprised that it worked than I am. :-) I was aware of the dangers of a braced argument, but I saw no way around it without the necessity of having to TeX the file twice -- ugh. The lesser of two evils, in my view... – sgmoye Jul 04 '19 at 15:57
  • Right, you need TikZ to process the picture twice, so choices are limited. TeXing the file twice isn't so bad, we do it all the time for the table of contents, references, etc. The big advantage of marmot's scaling method is its accuracy and the fact that rule widths don't get scaled. BTW, seeing the 0.3cm in your code: if one wants a border with my answer inside the frames, the correct way is to change the line that reads \setlength{\fboxsep}{0pt}% (it was moved several revisons ago above the \minipage{\dimexpr\linewidth-\mywidth-2\fboxrule-2\fboxsep} so that... – frougon Jul 04 '19 at 19:32
  • ... this calculation uses the value of \fboxsep that is actually used by the \fcolorbox—seems you didn't pick this change...). – frougon Jul 04 '19 at 19:32
0

This new answer is posted in reply to this request. It complements my answer and is only posted here because my first answer can't grow more due to the 30000 characters limitation (!).

This defines a problems environment for the outer list that wraps each (outer) item in a minipage, because the OP apparently wants to completely prevent page breaking within a given problem.

\documentclass{article}
\usepackage{graphicx}
\usepackage{calc} % for \settototalheight (used for convenience)
\usepackage{xcolor}
\usepackage{etoolbox}
\usepackage{xparse}
\usepackage{tikz}
\usepackage{parskip}
\usepackage{enumitem}
\usetikzlibrary{calc}

% Frame dimensions
\newlength{\mywidth}
\newlength{\myheight}
\setlength{\mywidth}{3.5cm}
\setlength{\myheight}{3.5cm}

\makeatletter

\newtoggle{parskiploaded}
\@ifpackageloaded{parskip}
  {\toggletrue{parskiploaded}}
  {\togglefalse{parskiploaded}}

% Use l3keys to support a key/value-style interface for the optional argument
% of the 'choices' environment (l3keys is great!).
\ExplSyntaxOn

\msg_new:nnn { ryanjform } { duplicate-figure-id }
  { duplicate~figure~identifier:~'\exp_not:n {#1}'. }

% Whether to perform the \scalebox-based autoscaling for a given figure
\bool_new:N \l__ryanjform_do_autoscale_pic_in_choices_bool
% Sequence recording all figure identifiers (for the 'scale to max size' TikZ
% style) found so far
\seq_new:N \g__ryanjform_scale_to_max_style_figure_ids_seq
% Counter used when generating automatic figure identifiers for 'form autoscale'
\int_new:N \g_ryanjform_last_autogenerated_figure_nb_int

% Define the options supported in the optional argument of the 'choices'
% environment
\keys_define:nn { ryanjform }
  {
    autoscale .bool_set:N = \l__ryanjform_do_autoscale_pic_in_choices_bool,
    % Value used when the 'autoscale' key is passed with no value
    autoscale .default:n = { true },
    autoscale .initial:n = { false }
  }

\cs_new_protected:Npn \__ryanjform_set_keys:n #1
  { \keys_set:nn { ryanjform } {#1} }

\cs_new_protected:Npn \__ryanjform_check_unique_id:n #1
  {
    \seq_if_in:NnTF \g__ryanjform_scale_to_max_style_figure_ids_seq {#1}
      { \msg_error:nnn { ryanjform } { duplicate-figure-id } {#1} }
      { \seq_gput_right:Nn \g__ryanjform_scale_to_max_style_figure_ids_seq {#1} }
  }

\cs_new_protected:Npn \__ryanjform_form_autoscale:n #1
  {
    \pgfkeys { /tikz/.cd, form~autoscale={#1} }
  }

\cs_generate_variant:Nn \__ryanjform_form_autoscale:n { V }

% Automatic construction of generated ids (the pattern is defined here)
\cs_new:Npn \__ryanjform_autogenerated_id:n #1
  { ryanjform~autogenerated~id~#1 }

\cs_generate_variant:Nn \__ryanjform_autogenerated_id:n { V }

\cs_new_protected:Npn \__ryanjform_form_autoscale_autoid:
  {
    % Increment the counter
    \int_gincr:N \g_ryanjform_last_autogenerated_figure_nb_int
    \tl_set:Nx \l_tmpa_tl       % generate an id based on the counter value
      {
        \__ryanjform_autogenerated_id:V
          \g_ryanjform_last_autogenerated_figure_nb_int
      }
    % Call the 'form autoscale' style with the new id
    \__ryanjform_form_autoscale:V \l_tmpa_tl
  }

% Set up aliases using LaTeX2e naming style
\cs_set_eq:NN \ryanjformsetup \__ryanjform_set_keys:n
\cs_set_eq:NN \ryanjform@check@unique@id \__ryanjform_check_unique_id:n
\cs_set_eq:NN \ryanjform@form@autscale@autoid \__ryanjform_form_autoscale_autoid:

% If-then-else command using the boolean
% \l__ryanjform_do_autoscale_pic_in_choices_bool to choose the branch
\NewDocumentCommand \ryanjform@ifautoscale@enabled { }
  {
    \bool_if:NTF \l__ryanjform_do_autoscale_pic_in_choices_bool
  }

\bool_new:N \l__ryanjform_before_first_problem_bool

\NewDocumentCommand \ryanjform@outeritem { o }
  {
    \bool_if:NTF \l__ryanjform_before_first_problem_bool
      { \bool_set_false:N \l__ryanjform_before_first_problem_bool }
      {
        \endminipage
        \group_end:
      }

    \IfValueTF {#1}
      { \ryanjform@outeritem@ORI [#1] } % call the original
      { \ryanjform@outeritem@ORI }      % \item command

    \group_begin:
    \minipage[t]{\linewidth}
  }

\NewDocumentEnvironment { problems } { }
  {
    \group_begin:
    \enumerate
    \cs_set_eq:NN \ryanjform@outeritem@ORI \item % save the original \item
    \cs_set_eq:NN \item \ryanjform@outeritem     % locally override it
    \bool_set_true:N \l__ryanjform_before_first_problem_bool
  }
  {
    \endminipage                % for the
    \group_end:                 % last item
    \endenumerate
    \group_end:
  }

\ExplSyntaxOff

\newsavebox{\ryanjform@box}     % will be set with \global
\newlength{\ryanjform@total@height}

\newenvironment{questionpicture}{%
  % The OP wants 'form autoscale autoid' turned on for every picture framed by
  % the 'choices' environment
  \tikzset{every picture/.style=form autoscale autoid}%
  \begin{lrbox}{0}
}{%
  \end{lrbox}%
  \global\setbox\ryanjform@box=\box0
}

\AfterEndEnvironment{questionpicture}{%
  \par\noindent
  \setlength{\fboxsep}{0pt}%
  \begingroup
  \minipage{\dimexpr\linewidth-\mywidth-2\fboxrule-2\fboxsep}
  \begingroup
  \iftoggle{parskiploaded}%
    {\enumerate[label=(\Alph*),itemsep=7pt]}%
    {\enumerate[label=(\Alph*)]}%
}

\let\ryanjform@start@question@picture\questionpicture
\newcommand*{\ryanjform@invalid@place@for@calling@questionpicture}{%
  \errmessage{The 'questionpicture' environment must be used at the start of a
    'choices' environment}%
}
% Generate an error message unless \questionpicture is used where expected
\let\questionpicture\ryanjform@invalid@place@for@calling@questionpicture

\newenvironment{choices}[1][]{%
  \ryanjformsetup{#1}%
  \let\questionpicture\ryanjform@start@question@picture
  \let\item\ryanjform@outeritem@ORI
  \ignorespaces
}{%
  \endenumerate
  \endgroup
  \endminipage
  \endgroup
  %
  \settototalheight{\ryanjform@total@height}{\usebox{\ryanjform@box}}%
  \pgfmathsetmacro{\ryanjform@x@ratio}{\the\mywidth / \wd\ryanjform@box}%
  \pgfmathsetmacro{\ryanjform@y@ratio}{\the\myheight / \ryanjform@total@height}%
  \pgfmathsetmacro{\ryanjform@scale}{min(\ryanjform@x@ratio,
                                         \ryanjform@y@ratio)}%
  %
  \fcolorbox{red!20}{blue!20}{%
    \begin{minipage}[c][\myheight][c]{\mywidth}
      \centering
      \ryanjform@ifautoscale@enabled{%
        % We know that \ryanjform@scale will be (fully) expanded:
        % <https://tex.stackexchange.com/q/497769/73317>
        \scalebox{\ryanjform@scale}{\usebox{\ryanjform@box}}%
      }{%
        \usebox{\ryanjform@box}%
      }%
    \end{minipage}%
  }%
  \ignorespacesafterend
}

% Autoscaling technique that doesn't affect font sizes in TikZ pictures.
% (based on code from marmot: <https://tex.stackexchange.com/a/497749/73317>)
%
% #1: unique per-picture id allowing several pictures to use this mechanism
%     in a given document (it should contain no control sequence token nor
%     active character)
% #2: width of the reference rectangle
% #3: height of the reference rectangle
\newcommand*\ryanjform@ExportBB[3]{%
 \path let
   \p1=($(current bounding box.north east)-(current bounding box.south west)$),
   \n1={#2/\x1},\n2={#3/\y1}
 in \pgfextra{\pgfmathsetmacro{\ryanjform@figscale}{min(\n1,\n2)}%
              \expandafter\xdef\csname ryanjform@auto@figscale@#1\endcsname{%
                \ryanjform@figscale}};
 \immediate\write\@mainaux{%
   \string\expandafter
   \gdef\string\csname\space ryanjform@auto@figscale@#1\string\endcsname{%
     \csname ryanjform@auto@figscale@#1\endcsname}}}

\tikzset{scale to max size/.style args={id #1 width #2height #3}{%
    execute at end picture={\ryanjform@ExportBB{#1}{#2}{#3}},
    /utils/exec={\ryanjform@check@unique@id{#1}%
                 \ifcsname ryanjform@auto@figscale@#1\endcsname
                   \wlog{Found autoscale value for picture '#1'}%
                 \else
                   \typeout{Automatically-scaled pictures: please recompile
                            for picture '#1'.}
                   \expandafter\gdef
                     \csname ryanjform@auto@figscale@#1\endcsname{1}
                 \fi},
   scale=\csname ryanjform@auto@figscale@#1\endcsname},
         form autoscale/.style={%
           scale to max size=id #1 width \mywidth height \myheight},
         % Same style except the id is automatically generated using a counter
         form autoscale autoid/.style={%
           /utils/exec={\ryanjform@form@autscale@autoid}}}
% End of the code based on <https://tex.stackexchange.com/a/497749/73317>

\makeatother

\begin{document}

\begin{problems}

\item Original picture
  \begin{choices}
    \begin{questionpicture}
      \begin{tikzpicture}
        \draw[thick] (0,0)--(100,50);
        \draw[thick] (0,0)--(50,-100);
        \draw[thick] (0,0)--(-100,50);
        \draw[thick] (0,0)--(-50,-100);
        \draw[thick] (0,0)--(100,-50);
        \draw[thick] (0,0)--(-50,100);
        \draw (5,20) node {$1$};
        \draw (-25,25) node {$2$};
        \draw (-20,-5) node {$3$};
        \draw (0,-25) node {$4$};
        \draw (25,-25) node {$5$};
        \draw (25,0) node {$6$};
      \end{tikzpicture}
    \end{questionpicture}
    \item Choice
    \item Choice
    \item Choice
    \item Choice
    \item Choice
  \end{choices}

\item Original picture scaled manually by 0.1
  \begin{choices}
    \begin{questionpicture}
      \begin{tikzpicture}
        \draw[thick] (0,0)--(10,5);
        \draw[thick] (0,0)--(5,-10);
        \draw[thick] (0,0)--(-10,5);
        \draw[thick] (0,0)--(-5,-10);
        \draw[thick] (0,0)--(10,-5);
        \draw[thick] (0,0)--(-5,10);
        \draw (.5,2) node {$1$};
        \draw (-2.5,2.5) node {$2$};
        \draw (-2,-.5) node {$3$};
        \draw (0,-2.5) node {$4$};
        \draw (2.5,-2.5) node {$5$};
        \draw (2.5,0) node {$6$};
      \end{tikzpicture}
    \end{questionpicture}
    \item Choice
    \item Choice
    \item Choice
    \item Choice
    \item Choice
  \end{choices}

\item Original picture scaled manually by 0.01
  \begin{choices}
    \begin{questionpicture}
      \begin{tikzpicture}
        \draw[thick] (0,0)--(1,.5);
        \draw[thick] (0,0)--(.5,-1);
        \draw[thick] (0,0)--(-1,.5);
        \draw[thick] (0,0)--(-.5,-1);
        \draw[thick] (0,0)--(1,-.5);
        \draw[thick] (0,0)--(-.5,1);
        \draw (.05,.2) node {$1$};
        \draw (-.25,.25) node {$2$};
        \draw (-.2,-.05) node {$3$};
        \draw (0,-.25) node {$4$};
        \draw (.25,-.25) node {$5$};
        \draw (.25,0) node {$6$};
      \end{tikzpicture}
    \end{questionpicture}
    \item Choice
    \item Choice
    \item Choice
    \item Choice
    \item Choice
  \end{choices}

\item Original picture shifted up/right by 100.
  \begin{choices}
    \begin{questionpicture}
      \begin{tikzpicture}
        \draw[thick] (100,100)--(200,150);
        \draw[thick] (100,100)--(150,0);
        \draw[thick] (100,100)--(0,150);
        \draw[thick] (100,100)--(50,0);
        \draw[thick] (100,100)--(200,50);
        \draw[thick] (100,100)--(50,200);
        \draw (105,120) node {$1$};
        \draw (75,125) node {$2$};
        \draw (80,95) node {$3$};
        \draw (100,75) node {$4$};
        \draw (125,75) node {$5$};
        \draw (125,100) node {$6$};
      \end{tikzpicture}
    \end{questionpicture}
    \item Choice
    \item Choice
    \item Choice
    \item Choice
    \item Choice
  \end{choices}

\item Picture from Problem 4 scaled by 0.1 (which happens to be the picture from Problem 2 shifted up/right by 10 because \emph{math})
  \begin{choices}
    \begin{questionpicture}
      \begin{tikzpicture}
        \draw[thick] (10,10)--(20,15);
        \draw[thick] (10,10)--(15,0);
        \draw[thick] (10,10)--(0,15);
        \draw[thick] (10,10)--(5,0);
        \draw[thick] (10,10)--(20,5);
        \draw[thick] (10,10)--(5,20);
        \draw (10.5,12) node {$1$};
        \draw (7.5,12.5) node {$2$};
        \draw (8,9.5) node {$3$};
        \draw (10,7.5) node {$4$};
        \draw (12.5,7.5) node {$5$};
        \draw (12.5,10) node {$6$};
      \end{tikzpicture}
    \end{questionpicture}
    \item Choice
    \item Choice
    \item Choice
    \item Choice
    \item Choice
  \end{choices}

\end{problems}

\end{document}
frougon
  • 24,283
  • 1
  • 32
  • 55