summaryrefslogtreecommitdiff
path: root/macros/xetex/latex/exam-zh/tex/exam-zh-math.sty
diff options
context:
space:
mode:
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.sty427
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