%%%============================================================================== %% Copyright 2023-present by Alceu Frigeri %% %% This work may be distributed and/or modified under the conditions of %% %% * The [LaTeX Project Public License](http://www.latex-project.org/lppl.txt), %% version 1.3c (or later), and/or %% * The [GNU Affero General Public License](https://www.gnu.org/licenses/agpl-3.0.html), %% version 3 (or later) %% %% This work has the LPPL maintenance status *maintained*. %% %% The Current Maintainer of this work is Alceu Frigeri %% %% This is version {2.1} {2025/10/06} %% %% The list of files that compose this work can be found in the README.md file at %% https://ctan.org/pkg/tikzquests %% %%%============================================================================== \NeedsTeXFormat{LaTeX2e}[2022/06/01] \ProvidesExplPackage {tikzquests} {2025/10/06} {2.1} {A Simple Framework for (tikz/text) Parametric Questions} %%%%%%% %%% %%% Just an attempt of having my packages info in a regular way %%% Idea being: { / pkg info } for each and all. %%% %%%%%%% \keys_define:nn { tikzquests / pkg info} { name .code:n = {tikzquests} , prefix .code:n = {tikzquests} , date .code:n = {2025/10/06}, version .code:n = {2.1} , description .code:n = {A Simple Framework for (tikz/text) Parametric Questions} } \cs_if_exist:NF \PkgInfo { \NewDocumentCommand \PkgInfo {mm} { \keys_set:nn {#1 / pkg info}{#2} } \NewDocumentCommand \PkgDescription {m} { \noindent Package~ \textbf{\PkgInfo{#1}{name}}~Version:~\PkgInfo{#1}{version}~ -~ \PkgInfo{#1}{date}\par \emph{\PkgInfo{#1}{description}}~\par } } %%%%%%% %%% End of cut-n-paste %%%%%%% %\newif\iftikzquests@@nodefs %\tikzquests@@nodefsfalse \keys_define:nn {tikzquests } { no~ alias .usage:n = load , no~ alias .bool_set:N = \l__tikzquests_noalias_bool , no~ alias .value_forbidden:n = true , xtrakeys .usage:n = load , xtrakeys .tl_set:N = \l__tikzquests_xtrakeys_tl , xtrakeys .value_required:n = true , xtraidx .usage:n = load , xtraidx .tl_set:N = \l__tikzquests_xtraidx_tl , xtraidx .value_required:n = true , undef~ color .usage:n = load , undef~ color .tl_set:N = \l__tikzquests_undefcolor_tl , undef~ color .value_required:n = true , undef~ color .initial:n = {red} , in~ review .usage:n = load , in~ review .bool_set:N = \l__tikzquests_inreview_bool , no~ defs .usage:n = load , no~ defs .bool_set:N = \l__tikzquests_nodefs_bool , no~ defs .value_forbidden:n = true , old~ settings .usage:n = load , old~ settings .bool_set:N = \l__tikzquests_oldsettings_bool , old~ settings .value_forbidden:n = true , } \ProcessKeyOptions [ tikzquests ] %%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%% %% %% %% %%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%% \msg_new:nnnn {tikzquests} {repository already defined} { (ID:#1)~Repository~ '#2'~already~defined! } { You~tried~to~create~an~already~defined~Repository:~'#2'. ~Error~Code~ ID:<#1>. } \msg_new:nnnn {tikzquests} {invalid repository} { (ID:#1)~Invalid~Repository:~ '#2' } { You~tried~to~use~an~invalid~Repository:~'#2'. ~Error~Code~ ID:<#1>. } \msg_new:nnnn {tikzquests} {invalid question} { (ID:#1)~Invalid~Question:~ '#2' } { You~tried~to~use~an~invalid~Question:~'#2'. ~Error~Code~ ID:<#1>. } \msg_new:nnnn {tikzquests} {deprecated} { (ID:#1)~#2 ~ is ~ deprecated.~ use~ '#3' ~ instead } { You~tried~to~use~a~deprecated~command:~#2. Use~'#3'~ instead. ~Error~Code~ ID:<#1>. } \msg_new:nnnn {tikzquests} {info} { Processed ~ #1:~ #2 } { Set~ of ~processed ~ #1:~ #2. } \seq_new:N \l__tikzquests_repositories_seq \tl_new:N \l__tikzquests_active_repository_tl \seq_new:N \l__tikzquests_tmpa_seq \seq_new:N \l__tikzquests_tmpb_seq \tl_new:N \l__tikzquests_tmp_tl \cs_new_protected:Npn \__tikzquests_select_repository:n #1 { \prop_if_exist:cTF {l__tikzquests_ #1 _tikzrepo_prop} { \tl_set:Nn \l__tikzquests_active_repository_tl {#1} } { \msg_error:nnne {tikzquests}{invalid repository}{select~01}{#1} } } \cs_new_protected:Npn \__tikzquests_new_repository:nn #1#2 { \prop_if_exist:cTF {l__tikzquests_ #2 _tikzrepo_prop} { \msg_warning:nnne {tikzquests}{repository already defined}{new~01}{#2} } { \prop_new_linked:c {l__tikzquests_ #2 _tikzrepo_prop} \prop_new_linked:c {l__tikzquests_ #2 _tikzrepo_remarks_prop} \prop_new_linked:c {l__tikzquests_ #2 _textrepo_prop} \prop_new_linked:c {l__tikzquests_ #2 _textrepo_remarks_prop} \seq_put_right:Nn \l__tikzquests_repositories_seq {#2} } \bool_if:nT {#1} { \__tikzquests_select_repository:n {#2} } } \__tikzquests_new_repository:nn {\c_true_bool}{default} \NewDocumentCommand{\SelectRepository}{m} { \__tikzquests_select_repository:n {#1} } \NewDocumentCommand{\defRepository}{sm} { \__tikzquests_new_repository:nn {#1}{#2} } \NewDocumentCommand\defNewRepository{sm} { \msg_warning:nnnnn {tikzquests}{deprecated}{dep~03}{\defNewRepository}{\defRepository} \__tikzquests_new_repository:nn {#1}{#2} } \NewDocumentCommand{\defQuestion}{sO{\l__tikzquests_active_repository_tl}m+mO{}} { \prop_if_exist:cTF {l__tikzquests_ #2 _tikzrepo_prop} { \bool_if:nTF {#1} { \prop_put:cnn {l__tikzquests_ #2 _textrepo_prop}{#3}{#4} \prop_put:cnn {l__tikzquests_ #2 _textrepo_remarks_prop}{#3}{#5} } { \prop_put:cnn {l__tikzquests_ #2 _tikzrepo_prop}{#3}{#4} \prop_put:cnn {l__tikzquests_ #2 _tikzrepo_remarks_prop}{#3}{#5} } } { \msg_error:nnne {tikzquests}{invalid repository}{def~quest~01}{#2} } } \NewDocumentCommand{\defQuestionAlias}{sO{\l__tikzquests_active_repository_tl}mO{}m} { \tl_if_blank:nTF {#4} { \tl_set:Nn \l_tmpa_tl {#2} } { \tl_set:Nn \l_tmpa_tl {#4} } \bool_lazy_and:nnTF { \prop_if_exist_p:c {l__tikzquests_ #2 _tikzrepo_prop} } { \prop_if_exist_p:c {l__tikzquests_ \l_tmpa_tl _tikzrepo_prop} } { \bool_if:NF \l__tikzquests_noalias_bool { \bool_if:nTF {#1} { \prop_get:cnNTF {l__tikzquests_ \l_tmpa_tl _textrepo_prop}{#5} \l_tmpb_tl { \prop_put:cnV {l__tikzquests_ #2 _textrepo_prop}{#3} \l_tmpb_tl \prop_get:cnNTF {l__tikzquests_ \l_tmpa_tl _textrepo_remarks_prop}{#5} \l_tmpb_tl { \prop_put:cnV {l__tikzquests_ #2 _textrepo_remarks_prop}{#3} \l_tmpb_tl } { \prop_put:cnn {l__tikzquests_ #2 _textrepo_remarks_prop}{#3}{} } } { \msg_error:nnnn {tikzquests}{invalid question}{alias~01}{#5} } } { \prop_get:cnNTF {l__tikzquests_ \l_tmpa_tl _tikzrepo_prop}{#5} \l_tmpb_tl { \prop_put:cnV {l__tikzquests_ #2 _tikzrepo_prop}{#3} \l_tmpb_tl \prop_get:cnNTF {l__tikzquests_ \l_tmpa_tl _tikzrepo_remarks_prop}{#5} \l_tmpb_tl { \prop_put:cnV {l__tikzquests_ #2 _tikzrepo_remarks_prop}{#3} \l_tmpb_tl } { \prop_put:cnn {l__tikzquests_ #2 _tikzrepo_remarks_prop}{#3}{} } } { \msg_error:nnnn {tikzquests}{invalid question}{alias~02}{#5} } } } } { \tl_if_blank:nTF {#4} { \msg_error:nnne {tikzquests}{invalid repository}{alias~04}{<#2>} } { \msg_error:nnne {tikzquests}{invalid repository}{alias~04}{<#2> - <#4>} } } } \cs_new_protected:Npn \__tikzquests_remarks:nnn #1#2#3 { \prop_get:cnN {l__tikzquests_ #1 _remarks_prop} {#2} \l__tikzquests_tmp_tl \tl_if_blank:eF {\l__tikzquests_tmp_tl} { {\par\footnotesize\textbf{Remarks:~}\color{\l__tikzquests_undefcolor_tl}\l__tikzquests_tmp_tl} } } \cs_new_protected:Npn \__tikzquests_inreview_remarks:nnn #1#2#3 { \bool_if:nT {\l__tikzquests_inreview_bool} { \__tikzquests_remarks:nnn {#1}{#2}{#3} \tl_if_blank:nF {#3} { {\par\footnotesize\textbf{Annotation:~}\color{\l__tikzquests_undefcolor_tl}#3} } } } \NewDocumentCommand{\ftikzQuestion}{sD(){}O{\l__tikzquests_active_repository_tl}mO{}} { \bool_if:nTF {#1} { \begin{center} \tikzQuestion*(#2)[#3]{#4}[#5] \end{center} } { \begin{center} \tikzQuestion(#2)[#3]{#4}[#5] \end{center} } } \cs_new_protected:Npn \__tikzquests_pgfkeys:n #1 { \pgfkeys{tikz , #1} } \tl_new:N \l__tikzquests_repo_tl \NewDocumentCommand{\tikzQuestion}{sD(){}O{\l__tikzquests_active_repository_tl}mO{}D<>{}} { \bool_if:nTF {#1} { \tl_set:Nn \l__tikzquests_repo_tl {textrepo} } { \tl_set:Nn \l__tikzquests_repo_tl {tikzrepo} } \prop_if_exist:cTF {l__tikzquests_ #3 _ \l__tikzquests_repo_tl _prop} { \prop_get:cnNTF {l__tikzquests_ #3 _ \l__tikzquests_repo_tl _prop} {#4} \l__tikzquests_tmp_tl { \tl_if_blank:nTF {#2} { \begin{tikzpicture} \__tikzquests_keys_set:n{#5} \exp_args:NV \__tikzquests_pgfkeys:n \l__tikzquests_tikz_keys_tl \l__tikzquests_tmp_tl \end{tikzpicture} } { \resizebox{#2\textwidth}{!}{ \begin{tikzpicture} \__tikzquests_keys_set:n{#5} \exp_args:NV \__tikzquests_pgfkeys:n \l__tikzquests_tikz_keys_tl \l__tikzquests_tmp_tl \end{tikzpicture}} } \__tikzquests_inreview_remarks:nnn {#3 _ \l__tikzquests_repo_tl}{#4}{#6} } { Invalid~Question:~#4 \msg_error:nnnn {tikzquests}{invalid question}{quest~01}{#4} } } { Invalid~Repository:~#3 \msg_error:nnne {tikzquests}{invalid repository}{quest~01}{#3} } } \cs_new_protected:Npn \__tikzquests_rawquestion:nnnnn #1#2#3#4#5 { \prop_if_exist:cTF {l__tikzquests_ #1 _ #2 _prop} { \prop_get:cnNTF {l__tikzquests_ #1 _ #2 _prop} {#3} \l__tikzquests_tmp_tl { \begingroup \__tikzquests_keys_set:n{#4} \l__tikzquests_tmp_tl \endgroup \__tikzquests_inreview_remarks:nnn {#1 _ #2}{#3}{#5} } { Invalid~Question:~#3 \msg_error:nnnn {tikzquests}{invalid question}{quest~02}{#3} } } { Invalid~Repository:~#1 \msg_error:nnne {tikzquests}{invalid repository}{quest~02}{#1} } } \NewDocumentCommand{\rawtikzQuestion}{O{\l__tikzquests_active_repository_tl}mO{}D<>{}} { \msg_warning:nnnnn {tikzquests}{deprecated}{dep~01}{\rawtikzQuestion}{\Question} \__tikzquests_rawquestion:nnnnn {#1}{tikzrepo}{#2}{#3}{#4} } \NewDocumentCommand{\textQuestion}{O{\l__tikzquests_active_repository_tl}mO{}D<>{}} { \msg_warning:nnnnn {tikzquests}{deprecated}{dep~02}{\textQuestion}{\Question*} \__tikzquests_rawquestion:nnnnn {#1}{textrepo}{#2}{#3}{#4} } \NewDocumentCommand{\Question}{sO{\l__tikzquests_active_repository_tl}mO{}D<>{}} { \bool_if:nTF {#1} { \__tikzquests_rawquestion:nnnnn {#2}{textrepo}{#3}{#4}{#5} } { \__tikzquests_rawquestion:nnnnn {#2}{tikzrepo}{#3}{#4}{#5} } } %%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%% \coffin_new:N \l__tikzquests_tmpa_coffin \coffin_new:N \l__tikzquests_tmpb_coffin \cs_new_protected:Npn \__tikzquests_sorted_list:nnn #1#2#3 { \prop_if_empty:cF {l__tikzquests_ #1 _ #2 _prop} { \seq_gclear_new:N \l__tikzquests_questions_seq \prop_map_inline:cn {l__tikzquests_ #1 _ #2 _prop} { \seq_put_right:Nn \l__tikzquests_questions_seq {##1} } \seq_sort:Nn \l__tikzquests_questions_seq { \str_compare:eNeTF { ##1 } > { ##2 } { \sort_return_swapped: } { \sort_return_same: } } \seq_map_inline:Nn \l__tikzquests_questions_seq { \vcoffin_set:Nnn \l__tikzquests_tmpa_coffin {0.2\textwidth} { \noindent \textbf{##1} } \vcoffin_set:Nnn \l__tikzquests_tmpb_coffin {0.8\textwidth} { \noindent #3[#1]{##1} \bool_if:nF {\l__tikzquests_inreview_bool} { \__tikzquests_remarks:nnn {#1 _ #2}{##1}{} } } \coffin_join:NnnNnnnn \l__tikzquests_tmpa_coffin {r}{vc} \l__tikzquests_tmpb_coffin {l}{vc} {0pt}{0pt} \noindent \coffin_typeset:Nnnnn \l__tikzquests_tmpa_coffin {l}{t}{0pt}{0pt} \skip_vertical:n{0.35ex} { \color{white!20!gray}\hrule\relax } \skip_vertical:n{1ex} } } } \NewDocumentCommand{\QuestionsFmtList}{O{}mm} { \tl_if_blank:nTF {#1} { \seq_set_eq:NN \l__tikzquests_tmpb_seq \l__tikzquests_repositories_seq } { \seq_set_from_clist:Nn \l__tikzquests_tmpb_seq {#1} } \seq_map_inline:Nn \l__tikzquests_tmpb_seq { \regex_match:nnTF {/$}{##1} { \tl_set:Nn \l_tmpa_tl {##1} \regex_replace_once:nnN {/$}{} \l_tmpa_tl \seq_put_right:Nn \l__tikzquests_tmpa_seq {\l_tmpa_tl} \seq_map_inline:Nn \l__tikzquests_repositories_seq { \regex_match:nnTF {##1}{####1} { \seq_put_right:Nn \l__tikzquests_tmpa_seq {####1} } {} } } { \seq_put_right:Nn \l__tikzquests_tmpa_seq {##1} } } \seq_remove_duplicates:N \l__tikzquests_tmpa_seq \seq_sort:Nn \l__tikzquests_tmpa_seq { \str_compare:eNeTF { ##1 } > { ##2 } { \sort_return_swapped: } { \sort_return_same: } } \seq_map_inline:Nn \l__tikzquests_tmpa_seq { \prop_if_exist:cTF {l__tikzquests_ ##1 _tikzrepo_prop} { \skip_vertical:n{0.35ex} \hrule\relax\hrule\relax \skip_vertical:n{1ex} {Repository:~ \large\textbf{##1}} \skip_vertical:n{0.35ex} \hrule \skip_vertical:n{1ex} ~\hfill\emph{\underline{ non~ starred~ ones~ -~ TikZ~ graphics}} \skip_vertical:n{0.35ex} \__tikzquests_sorted_list:nnn {##1}{tikzrepo}{#3} \skip_vertical:n{1ex} ~\hfill\emph{\underline{starred~ ones~ -~ text/\TeX}} \skip_vertical:n{0.35ex} \__tikzquests_sorted_list:nnn {##1}{textrepo}{#2} } { \msg_error:nnne {tikzquests}{invalid repository}{Qlist~01}{##1} } } } \NewDocumentCommand{\QuestionsList}{O{}} { \QuestionsFmtList[#1]{\Question*}{\tikzQuestion(0.35)} } %%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%% \cs_new:Npn \__tikzquests_keys_set:n #1 { \keys_set:nn { tikzquests / vars } {#1} } \NewDocumentCommand {\QuestVal} {m} { \tl_if_exist:cTF {l__tikzquests_ #1 _key_tl} { \use:c {l__tikzquests_ #1 _key_tl} } { \textbf { \emph{ \color{ \l__tikzquests_undefcolor_tl } #1 } } } } \cs_new_protected:Npn \__tikzquests_keydef:nnnN #1#2#3#4 { \tl_set:cn {#2} {#3} \keys_define:nn { tikzquests / vars } { #1 .code:n = { \tl_set:cn {#2} {#4{##1}} } , #1 .value_required:n = true , #1* .code:n = { \tl_set:cn {#2} {\ensuremath{##1}} } , #1* .value_required:n = true , #1 ~ raw .code:n = { \tl_set:cn {#2} {##1} } , #1 ~ raw .value_required:n = true , } } \cs_new_protected:Npn \__tikzquests_keys:NNn #1#2#3 { \clist_map_inline:Nn #1 { \clist_map_inline:Nn #2 { \clist_map_inline:nn {a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z} { \exp_args:Ncc \tl_set:Nn {l__tikzquests_ ##1####1########1 _key_tl} {##1####1########1} \__tikzquests_keydef:nnnN { ##1####1########1 } { ##1####1########1 } {\ensuremath{##1 \c_math_subscript_token {#3{####1########1}}}} \ensuremath } } } } \cs_new_protected:Npn \__tikzquests_keys_nodefs:NNn #1#2#3 { \bool_if:NTF \l__tikzquests_oldsettings_bool { \cs_set_eq:NN \__tikzquests_auxi: \relax } { \cs_set_eq:NN \__tikzquests_auxi: \ensuremath } \clist_map_inline:Nn #1 { \clist_map_inline:Nn #2 { \clist_map_inline:nn {a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z} { \__tikzquests_keydef:nnnN { ##1####1########1 } { l__tikzquests_ ##1####1########1 _key_tl } {\ensuremath{##1 \c_math_subscript_token {#3{####1########1}}}} \__tikzquests_auxi: } } } } \cs_new_protected:Npn \__tikzquests_newkey:nn #1#2 { \tl_if_empty:nTF {#2} { \tl_set:cn {l__tikzquests_ #1 _key_tl} { \textbf { \emph{ \color{ \l__tikzquests_undefcolor_tl } #1 } } } } { \tl_set:cn {l__tikzquests_ #1 _key_tl} { #2 } } \keys_define:nn { tikzquests / vars } { #1 .tl_set:c = {l__tikzquests_ #1 _key_tl} , #1 .value_required:n = true } } \cs_new_protected:Npn \__tikzquests_keys_set: { \keys_define:nn { tikzquests / vars } { unknown .code:n = { \exp_args:NV \__tikzquests_newkey:nn \l_keys_key_str {##1} } , tikz ~ keys .tl_set:N = \l__tikzquests_tikz_keys_tl } \clist_set:Nn \l__tikzquests_keys_clist {R,L,C,Z,Y,G,H,X,K,T,Q,D,EQ,V,I} \tl_if_blank:VF \l__tikzquests_xtrakeys_tl { \clist_put_right:NV \l__tikzquests_keys_clist \l__tikzquests_xtrakeys_tl \clist_remove_duplicates:N \l__tikzquests_keys_clist } \msg_note:nnnV {tikzquests}{info} {keys} \l__tikzquests_keys_clist \clist_set:Nn \l__tikzquests_idx_clist {{},a,b} \tl_if_blank:VF \l__tikzquests_xtraidx_tl { \clist_put_right:NV \l__tikzquests_idx_clist \l__tikzquests_xtraidx_tl \clist_remove_duplicates:N \l__tikzquests_idx_clist } \msg_note:nnnV {tikzquests}{info} {indexes} \l__tikzquests_idx_clist \bool_if:NTF \l__tikzquests_nodefs_bool { \cs_gset_eq:NN \__tikzquests_map:NNn \__tikzquests_keys_nodefs:NNn } { \cs_gset_eq:NN \__tikzquests_map:NNn \__tikzquests_keys:NNn } \__tikzquests_map:NNn \l__tikzquests_keys_clist \l__tikzquests_idx_clist {} \clist_set:Nn \l__tikzquests_keys_clist {V,I} \__tikzquests_map:NNn \l__tikzquests_keys_clist \l__tikzquests_idx_clist { f \c_math_subscript_token } } \AtBeginDocument{\__tikzquests_keys_set:}