diff options
Diffstat (limited to 'macros/xetex/latex/exam-zh/tex/exam-zh-math.sty')
-rw-r--r-- | macros/xetex/latex/exam-zh/tex/exam-zh-math.sty | 427 |
1 files changed, 427 insertions, 0 deletions
diff --git a/macros/xetex/latex/exam-zh/tex/exam-zh-math.sty b/macros/xetex/latex/exam-zh/tex/exam-zh-math.sty new file mode 100644 index 0000000000..e11281f077 --- /dev/null +++ b/macros/xetex/latex/exam-zh/tex/exam-zh-math.sty @@ -0,0 +1,427 @@ +% +% Copyright (c) 2022 Kangwei Xia +% Released under the LaTeX Project Public License v1.3c License. +% Repository: https://gitee.com/xkwxdyy/exam-zh +% + +\NeedsTeXFormat{LaTeX2e} + +\RequirePackage{expl3} + +\ProvidesExplPackage {exam-zh-math} {2024-02-11} {v0.2.1} + {exam-zh math module} + +\RequirePackage { tabularray } +\RequirePackage { varwidth } +\RequirePackage { graphicx } +\RequirePackage { filehook } +\file_if_exist:nT { wrapstuff.sty } + { + \RequirePackage { wrapstuff } + \AtEndOfPackageFile* { exam-zh-choices } + { + \AddToHook { env / choices / before } + { \wrapstuffclear } + } + } + + +\keys_define:nn { exam-zh } + { calculations .meta:nn = { exam-zh / calculations } {#1} } + +%% calculations 环境 %% + + +% 图片相对于 label 的方位 +\str_new:N \l__examzh_calculations_figure_position_str +% coffin type 的对齐方式 +\str_new:N \l__examzh_calculations_coffin_align_str + + +\keys_define:nn { exam-zh / calculations } + { + % calculations 环境的 label 从几开始(但是其实是 index + seq 函数的 ##1 才是最终的 index) + index .int_set:N = \l__examzh_calculations_label_index_shift_int, + % 每行多少个(等价于有多少列) + columns .int_set:N = \l__examzh_calculations_column_int, + % 图片相对于文字的位置(上下左右) + fig-pos .choices:nn = + { top, above, bottom, below, left, right, left-top } + { \str_set:Nx \l__examzh_calculations_figure_position_str { \l_keys_choice_tl } }, + % 环境上方的额外距离 + top-sep .skip_set:N = \l__examzh_calculations_top_sep_skip, + % 环境下方的额外距离 + bottom-sep .skip_set:N = \l__examzh_calculations_bottom_sep_skip, + % 两列之间的水平间距 + hsep .skip_set:N = \l__examzh_calculations_hsep_skip, + % 两行之间的垂直间距 + vsep .skip_set:N = \l__examzh_calculations_vsep_skip, + % 整体的偏移量 + xshift .dim_set:N = \l__examzh_calculations_xshift_dim, + hshift .dim_set:N = \l__examzh_calculations_xshift_dim, + yshift .dim_set:N = \l__examzh_calculations_yshift_dim, + vshift .dim_set:N = \l__examzh_calculations_yshift_dim, + % label 的偏移量 + label-xshift .dim_set:N = \l__examzh_calculations_label_xshift_dim, + label-hshift .dim_set:N = \l__examzh_calculations_label_xshift_dim, + label-yshift .dim_set:N = \l__examzh_calculations_label_yshift_dim, + label-vshift .dim_set:N = \l__examzh_calculations_label_yshift_dim, + % 对齐方式 + align .choices:nn = + { t, m, b } + { \str_set:Nx \l__examzh_calculations_coffin_align_str { \l_keys_choice_tl } } + } +\keys_set:nn { exam-zh / calculations } + { + index = 0, + columns = 2, + fig-pos = left-top, + xshift = 2em, + hsep = 2em, + vsep = 0pt, + label-xshift = 0pt, + align = t, + top-sep = 1ex plus .5ex minus .5ex, + bottom-sep = 0pt, + } + +% item 的 index 指标(即第几个 item) +\int_new:N \l__examzh_calculations_item_index_int +% 将拼接后的 coffin 存到 seq 里 +\seq_new:N \l__examzh_calculations_store_seq +% 上面的 seq 的 item 数 +\int_new:N \l__examzh_calculations_store_seq_item_int +% 最终放在 tblr 里的内容 +\tl_new:N \l__examzh_calculations_tblr_content_tl + + + +% #1: 设置 calculations +% #2: 设置里面的 tblr +\NewDocumentEnvironment { calculations } { O{ } +O{ } } + { + \group_begin: + \RenewDocumentCommand \item { O{ } } + { \__examzh_calculations_item:n {##1} } + \int_set:Nn \l__examzh_calculations_item_index_int {0} + \seq_clear:N \l__examzh_calculations_store_seq + \tl_clear:N \l__examzh_calculations_tblr_content_tl + \keys_set:nn { exam-zh / calculations } {#1} + } + { + % 结束收集 + \unskip + \end{varwidth} + % \end{minipage} + \hcoffin_set_end: + % 拼接 label 和 figure + \__examzh_calculations_coffin_join: + % 输出 + \__examzh_calculations_coffin_typeset:n {#2} + % \par \int_use:N \l__examzh_calculations_item_index_int + \group_end: + } +% 拼接 label 和 figure +\cs_new:Npn \__examzh_calculations_coffin_join: + { + \int_step_inline:nn { \l__examzh_calculations_item_index_int } + { + \__examzh_calculations_coffin_join_position_set:n {##1} + \seq_gput_right:Nn \l__examzh_calculations_store_seq + { + \__examzh_calculations_coffin_align_set:n {##1} + \kern\l__examzh_calculations_hsep_skip + } + } + } +\cs_new:Npn \__examzh_calculations_coffin_align_set:n #1 + { + \str_case:Vn \l__examzh_calculations_coffin_align_str + { + {t} { \__examzh_calculations_coffin_align_set_t:n {#1} } + {m} { \__examzh_calculations_coffin_align_set_m:n {#1} } + {b} { \__examzh_calculations_coffin_align_set_b:n {#1} } + } + } +\cs_new:Npn \__examzh_calculations_coffin_align_set_t:n #1 + { + \coffin_typeset:cnnnn + { l__examzh_calculations_figure_ \int_to_roman:n { #1 } _coffin } + { l } { t } % align = t + { \l__examzh_calculations_xshift_dim } { \l__examzh_calculations_yshift_dim + 1em } + } +\cs_new:Npn \__examzh_calculations_coffin_align_set_m:n #1 + { + \coffin_typeset:cnnnn + { l__examzh_calculations_figure_ \int_to_roman:n { #1 } _coffin } + { l } { vc } % align = m + { \l__examzh_calculations_xshift_dim } { \l__examzh_calculations_yshift_dim } + } +\cs_new:Npn \__examzh_calculations_coffin_align_set_b:n #1 + { + \coffin_typeset:cnnnn + { l__examzh_calculations_figure_ \int_to_roman:n { #1 } _coffin } + { l } { b } % align = b + { \l__examzh_calculations_xshift_dim } { \l__examzh_calculations_yshift_dim } + } +\cs_new:Npn \__examzh_calculations_coffin_join_position_set:n #1 + { + \use:c { __examzh_calculations_coffin_join_position_set_ \l__examzh_calculations_figure_position_str :n } {#1} + } +\cs_new:cpn { __examzh_calculations_coffin_join_position_set_left-top :n } #1 + { + \coffin_join:cnncnnnn + { l__examzh_calculations_figure_ \int_to_roman:n {#1} _ coffin } + { l } { t } + { l__examzh_calculations_label_ \int_to_roman:n {#1} _ coffin } + { r } { b } + { \l__examzh_calculations_label_xshift_dim } + { \l__examzh_calculations_label_yshift_dim -10.6pt} + } +\cs_new:Npn \__examzh_calculations_coffin_join_position_set_top:n #1 + { + \coffin_join:cnncnnnn + { l__examzh_calculations_figure_ \int_to_roman:n {#1} _ coffin } + { hc } { b } + { l__examzh_calculations_label_ \int_to_roman:n {#1} _ coffin } + { hc } { t } + { \l__examzh_calculations_label_xshift_dim } + { \l__examzh_calculations_label_yshift_dim - 6pt } + } +\cs_set_eq:NN + \__examzh_calculations_coffin_join_position_set_above:n + \__examzh_calculations_coffin_join_position_set_top:n +\cs_new:Npn \__examzh_calculations_coffin_join_position_set_bottom:n #1 + { + \coffin_join:cnncnnnn + { l__examzh_calculations_figure_ \int_to_roman:n {#1} _ coffin } + { hc } { t } + { l__examzh_calculations_label_ \int_to_roman:n {#1} _ coffin } + { hc } { b } + { \l__examzh_calculations_label_xshift_dim } + { \l__examzh_calculations_label_yshift_dim + 6pt } + } +\cs_set_eq:NN + \__examzh_calculations_coffin_join_position_set_below:n + \__examzh_calculations_coffin_join_position_set_bottom:n +\cs_new:Npn \__examzh_calculations_coffin_join_position_set_left:n #1 + { + \coffin_join:cnncnnnn + { l__examzh_calculations_figure_ \int_to_roman:n {#1} _ coffin } + { l } { vc } + { l__examzh_calculations_label_ \int_to_roman:n {#1} _ coffin } + { r } { vc } + { \l__examzh_calculations_label_xshift_dim - 6pt } + { \l__examzh_calculations_label_yshift_dim } + } +\cs_new:Npn \__examzh_calculations_coffin_join_position_set_right:n #1 + { + \coffin_join:cnncnnnn + { l__examzh_calculations_figure_ \int_to_roman:n {#1} _ coffin } + { r } { vc } + { l__examzh_calculations_label_ \int_to_roman:n {#1} _ coffin } + { l } { vc } + { \l__examzh_calculations_label_xshift_dim + 2pt } + { \l__examzh_calculations_label_yshift_dim } + } +\cs_new:Npn \__examzh_calculations_coffin_typeset:n #1 + { + \__examzh_calculations_coffin_typeset_count: + \seq_map_indexed_inline:Nn \l__examzh_calculations_store_seq + % ##1: index + % ##2: content + { + \int_compare:nNnTF { \int_mod:nn {##1} { \l__examzh_calculations_column_int } } = {0} + { + \tl_gput_right:Nn \l__examzh_calculations_tblr_content_tl + { ##2 \\[\l__examzh_calculations_vsep_skip] } + } + { + \tl_gput_right:Nn \l__examzh_calculations_tblr_content_tl + { ##2 & } + } + } + % 如果 seq 的 item 比 column 多且不整除 column 的话,要补 & + \int_compare:nNnT { \l__examzh_calculations_store_seq_item_int } > { \l__examzh_calculations_column_int } + { + \int_compare:nNnF { \l__examzh_calculations_item_num_mod_column_left_int } = { 0 } + { + \tl_gput_right:Nx \l__examzh_calculations_tblr_content_tl + { + \prg_replicate:nn { \l__examzh_calculations_item_num_mod_column_left_int -1 } {&} + } + % \int_use:N \l__examzh_calculations_item_num_mod_column_left_int + } + } + \par + \vspace*{ \l__examzh_calculations_top_sep_skip } + \noindent + % \centering + % \SetTblrInner + % { + % rowsep = 4pt, + % % colsep = 0pt + % } + \begin{tblr} + [ expand = \l__examzh_calculations_tblr_content_tl ] + { + width = \linewidth, + columns = + { + l, + leftsep = 0em, + rightsep = 0pt, + }, + rows = + { + m, + ht=\baselineskip, + abovesep = 0pt, + belowsep = 0pt, + }, + stretch=0, + #1 + } + \l__examzh_calculations_tblr_content_tl + \end{tblr} + \vspace*{ \l__examzh_calculations_bottom_sep_skip } + \par + } +\int_new:N \l__examzh_calculations_item_num_mod_column_left_int +\cs_new:Npn \__examzh_calculations_coffin_typeset_count: + { + % 计算 seq 有多少项 + \int_set:Nn \l__examzh_calculations_store_seq_item_int + { \seq_count:N \l__examzh_calculations_store_seq } + % seq 项数小于 column 的话,column 设置为 seq 项数 + \int_compare:nNnTF { \l__examzh_calculations_store_seq_item_int } < { \l__examzh_calculations_column_int } + { \int_set_eq:NN \l__examzh_calculations_column_int \l__examzh_calculations_store_seq_item_int } + { + % 计算 \l__examzh_calculations_store_seq_item_int mod \l__examzh_calculations_column_int 的余数,用于补 & + \int_set:Nn \l__examzh_calculations_item_num_mod_column_left_int { \int_mod:nn { \l__examzh_calculations_store_seq_item_int } { \l__examzh_calculations_column_int } } + } + } + +\cs_new:Npn \__examzh_calculations_item:n #1 + { + % 增加指标(g 是关键) + \int_gincr:N \l__examzh_calculations_item_index_int + % 新建 coffin + \__examzh_calculations_item_new_coffin: + % 储存 label(一直出不来的原因是因为没改成 gset) + \hcoffin_gset:cn + { l__examzh_calculations_label_ \int_to_roman:n { \l__examzh_calculations_item_index_int } _coffin } + { + % 加上 index 键值设置的偏移量才是最终的 index + % (\int_eval:n { \l__examzh_calculations_item_index_int + \l__examzh_calculations_label_index_shift_int - 1} ) + % 如果 \item 的[] 中没内容,则需要把 label 升高 + % \tl_if_blank:nTF {#1} + % { + % \box_move_up:nn { 2.7pt } { \hbox{\__examzh_calculations_the_label:} } + % \space + % #1 + % } + % { + % \__examzh_calculations_the_label: #1 + % } + % \IfBooleanTF{#1}{#1 yes}{#1 no} + % \IfNoValueTF{#1} + % { + % \dim_set:Nn \l__examzh_calculations_label_yshift_dim { 4pt } + % }{#1 yes} + } + % 储存 figure + \int_compare:nNnF { \l__examzh_calculations_item_index_int } = {1} + { + \unskip + \end{varwidth} + % \end{minipage} + % 结束上一个 item 的收集 + \hcoffin_set_end: + } + % 收集 + \hcoffin_set:cw { l__examzh_calculations_figure_ \int_to_roman:n { \l__examzh_calculations_item_index_int } _coffin } + % \begin{varwidth}{\hsize} + % 根据 columns 个数设置宽度 + \__examzh_calculations_item_varwidth_width_set: + \begin{varwidth}{\l__examzh_calculations_item_varwidth_width_dim} + % \begin{minipage}{8cm} + \ignorespaces + \makebox[0em]{\rule{0pt}{\baselineskip}} + \__examzh_calculations_the_label: + } +\dim_new:N \l__examzh_calculations_item_varwidth_width_dim +\cs_new:Npn \__examzh_calculations_item_varwidth_width_set: + { + \int_case:nnF { \l__examzh_calculations_column_int } + { + {1} { \dim_set:Nn \l__examzh_calculations_item_varwidth_width_dim {0.8 \textwidth} } + {2} { \dim_set:Nn \l__examzh_calculations_item_varwidth_width_dim {0.3 \textwidth} } + {3} { \dim_set:Nn \l__examzh_calculations_item_varwidth_width_dim {0.3 \textwidth} } + {4} { \dim_set:Nn \l__examzh_calculations_item_varwidth_width_dim {0.2 \textwidth} } + } + { \dim_set:Nn \l__examzh_calculations_item_varwidth_width_dim { \hsize } } + } + +% 新建 coffin +\cs_new:Npn \__examzh_calculations_item_new_coffin: + { + % 放图片的 coffin + \coffin_if_exist:cF { l__examzh_calculations_figure_ \int_to_roman:n { \l__examzh_calculations_item_index_int } _coffin } + { \coffin_new:c { l__examzh_calculations_figure_ \int_to_roman:n { \l__examzh_calculations_item_index_int } _coffin } } + % 放 label 的 coffin + \coffin_if_exist:cF { l__examzh_calculations_label_ \int_to_roman:n { \l__examzh_calculations_item_index_int } _coffin } + { \coffin_new:c { l__examzh_calculations_label_ \int_to_roman:n { \l__examzh_calculations_item_index_int } _coffin } } + } + +\tl_new:N \l__examzh_calculations_counters_commands_set_tl + +\keys_define:nn { exam-zh / calculations } + { + label .tl_set:N = \l__examzh_calculations_label_tl + } +\keys_set:nn { exam-zh / calculations } + { + label = \arabic* + } + +\cs_new:Npn \__examzh_calculations_the_label: + { + \group_begin: + % 定义计数器相关的命令函数 + \l__examzh_calculations_counters_commands_set_tl + % 输出处理后的 label + \l__examzh_calculations_label_tl + \group_end: + } + +\NewDocumentCommand \AddCalculationsCounter { m m } + { + % 生成用户层命令 + \tl_put_right:Nn \l__examzh_calculations_counters_commands_set_tl + { \__examzh_process_counter:NNn #1 #2 { calculations } } + % 把核心函数存起来 + \cs_set_eq:cN { __examzh_calculations_save_ \cs_to_str:N #1 : } #2 + \cs_set_eq:cN { __examzh_calculations_save_ \cs_to_str:N #2 : } #2 + } + +\AddCalculationsCounter \arabic \@arabic +\AddCalculationsCounter \alph \@alph +\AddCalculationsCounter \Alph \@Alph +\AddCalculationsCounter \roman \@roman +\AddCalculationsCounter \Roman \@Roman + +\cs_new:Npn \__examzh_calculations_process_counter_aux:Nn #1#2 + { + \tl_if_eq:nnTF {#2} { * } + { + % \Alph* + \use:c { __examzh_calculations_save_ \cs_to_str:N #1 : } + { \int_eval:n { \l__examzh_calculations_item_index_int + \l__examzh_calculations_label_index_shift_int } } + } + { + % \Alph{...} + \use:c { __examzh_calculations_save_ \cs_to_str:N #1 : } + {#2} + } + }
\ No newline at end of file |