%%% ---------------------------------------------------------------------------- %%% suanpan: draw suanpan(abacus) with LaTeX3 %%% Author : Nan Geng %%% Repository: https://gitee.com/nwafu_nan/suan-span %%% License : The LaTeX Project Public License 1.3c %%% ---------------------------------------------------------------------------- \NeedsTeXFormat{LaTeX2e}[2020/10/01] \RequirePackage{expl3} % \ProvidesExplPackage{suanpan}{2024-08-20}{v1.0.0} % 初稿 % \ProvidesExplPackage{suanpan}{2024-08-28}{v1.1.0} % 优化(统一尺寸计算,添加空白占位) \ProvidesExplPackage{suanpan-l3}{2024-08-30}{v1.1.1} % 更名为suanpan-l3 {draw suanpan(abacus) with LaTeX3} \RequirePackage{l3keys2e} \RequirePackage{xparse} \RequirePackage{l3draw} \cs_new:Npn \__suanpan_error:n { \msg_error:nn { suanpan(abacus) } } % 盒子容器总高度计算函数 \cs_new_nopar:Npn \__suanpan_coffin_ht_plus_dp:N #1 { \coffin_ht:N #1 + \coffin_dp:N #1 } % =============颜色处理函数============= % 填充色辅助函数 \cs_new_nopar:Nn \__suanpan_aux_color_boxfill: { } % 颜色命名函数 % #1 颜色名称 % #2 颜色表达式 \cs_set_nopar:Npn \__suanpan_color_select:nn #1#2 { \color_set:nn {#1} {#2} } \cs_generate_variant:Nn \__suanpan_color_select:nn {nx} % 颜色命名函数 % #1 颜色名称 % #2 颜色空间 % #3 颜色分量值 \cs_set_nopar:Npn \__suanpan_color_select:nnn #1#2#3 { \color_set:nnn {#1} {#2} {#3} } \cs_generate_variant:Nn \__suanpan_color_select:nnn {nnx} % 定义变量 % 算盘基础尺寸 \dim_new:N \l__suanpan_bid_h_dim \dim_new:N \l__suanpan_bid_d_dim \dim_new:N \l__suanpan_bid_sep_dim \dim_new:N \l__suanpan_rod_sep_dim \dim_new:N \l__suanpan_rod_d_dim \dim_new:N \l__suanpan_frame_b_dim % 算盘基础尺寸初值(参考 QBT1747-1993 算盘标准) \dim_set:Nn \l__suanpan_bid_h_dim { 12mm } \dim_set:Nn \l__suanpan_bid_d_dim { 23mm } \dim_set:Nn \l__suanpan_bid_sep_dim { 1.8pt } \dim_set:Nn \l__suanpan_rod_sep_dim { 4pt } \dim_set:Nn \l__suanpan_rod_d_dim { 7mm } \dim_set:Nn \l__suanpan_frame_b_dim { 11mm } % 半长度 \dim_new:N \l__suanpan_bid_h_half_dim \dim_new:N \l__suanpan_bid_d_half_dim \dim_new:N \l__suanpan_rod_d_half_dim \dim_new:N \l__suanpan_frame_b_half_dim % 上珠偏移 \dim_new:N \l__suanpan_bid_upper_offset_dim % 下珠偏移 \dim_new:N \l__suanpan_bid_lower_offset_dim % 上档杆长度 \dim_new:N \l__suanpan_rod_upper_h_dim % 下档杆长度 \dim_new:N \l__suanpan_rod_lower_h_dim % 上档杆偏移 \dim_new:N \l__suanpan_rod_upper_offset_dim % 下档杆偏移 \dim_new:N \l__suanpan_rod_lower_offset_dim % 上边框内线偏移 \dim_new:N \l__suanpan_frame_upper_inner_offset_dim % 下边框内线偏移 \dim_new:N \l__suanpan_frame_lower_inner_offset_dim % 上边框外线偏移 \dim_new:N \l__suanpan_frame_upper_outer_offset_dim % 下边框外线偏移 \dim_new:N \l__suanpan_frame_lower_outer_offset_dim % 算珠上边缘 \dim_new:N \l__suanpan_bid_upper_dim % 算珠下边缘 \dim_new:N \l__suanpan_bid_lower_dim % 算珠圆角半径 \dim_new:N \l__suanpan_bid_arc_dim % 横梁计位点半径 \dim_new:N \l__suanpan_frame_unit_r_dim % 边框外线半宽度 \dim_new:N \l__suanpan_frame_outer_half_dim % 档杆总成宽度 \dim_new:N \l__suanpan_support_d_dim % 档杆总成半宽度 \dim_new:N \l__suanpan_support_d_half_dim % 上档杆总成高度 \dim_new:N \l__suanpan_support_upper_dim % 下档杆总成高度 \dim_new:N \l__suanpan_support_lower_dim % 档杆总成组装垂直偏移 \dim_new:N \l__suanpan_support_y_offset_dim % 上边框外线位置 \dim_new:N \l__suanpan_frame_outer_upper_dim % 下边框外线位置 \dim_new:N \l__suanpan_frame_outer_lower_dim % 上边框内线位置 \dim_new:N \l__suanpan_frame_inner_upper_dim % 下边框内线位置 \dim_new:N \l__suanpan_frame_inner_lower_dim % 左右边框外线偏移 \dim_new:N \l__suanpan_frame_outer_lr_offset_dim % 左右边框外线圆角半径 \dim_new:N \l__suanpan_frame_outer_arc_dim \dim_new:N \l__suanpan_frame_inner_linewidth_dim \dim_new:N \l__suanpan_frame_outer_linewidth_dim \dim_new:N \l__suanpan_rod_linewidth_dim \dim_new:N \l__suanpan_bid_linewidth_dim % 状态变量 \bool_new:N \l__suanpan_support_frame_bool \bool_set_true:N \l__suanpan_support_frame_bool \bool_new:N \l__suanpan_support_unit_bool \bool_set_false:N \l__suanpan_support_unit_bool \bool_new:N \l__suanpan_draft_bool % 容器(盒子) \coffin_new:N \l__suanpan_inner_bid_coffin \coffin_new:N \l__suanpan_outer_bid_coffin \coffin_new:N \l__suanpan_float_bid_coffin \coffin_new:N \l__suanpan_support_coffin \coffin_new:N \l__suanpan_empty_support_coffin % 凭据表(Token) \tl_new:N \l__suanpan_bid_draw_color_tl \tl_new:N \l__suanpan_bid_fill_color_tl \tl_new:N \l__suanpan_scale_tl % 档位最大值 \int_new:N \l__suanpan_rod_max_int \int_zero:N \l__suanpan_rod_max_int % 档位算珠位置列表 \clist_new:N \l__suanpan_bids_pos_clist % 档位数字与算珠位置对照属性表常量(内珠和外珠) % {x, y}==>x: 算珠位置(1-11, 1-7---下珠,8-10---上珠,11---悬珠) % y: 算珠类型(0---外珠,1---内珠) \prop_const_from_keyval:Nn \l__suanpan_rods_val_prop { 0 = {{1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {9, 0}, {10, 0}}, 1 = {{1, 0}, {2, 0}, {3, 0}, {4, 0}, {7, 1}, {9, 0}, {10, 0}}, 2 = {{1, 0}, {2, 0}, {3, 0}, {6, 1}, {7, 1}, {9, 0}, {10, 0}}, 3 = {{1, 0}, {2, 0}, {5, 1}, {6, 1}, {7, 1}, {9, 0}, {10, 0}}, 4 = {{1, 0}, {4, 1}, {5, 1}, {6, 1}, {7, 1}, {9, 0}, {10, 0}}, 5 = {{1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {8, 1}, {10, 0}}, 6 = {{1, 0}, {2, 0}, {3, 0}, {4, 0}, {7, 1}, {8, 1}, {10, 0}}, 7 = {{1, 0}, {2, 0}, {3, 0}, {6, 1}, {7, 1}, {8, 1}, {10, 0}}, 8 = {{1, 0}, {2, 0}, {5, 1}, {6, 1}, {7, 1}, {8, 1}, {10, 0}}, 9 = {{1, 0}, {4, 1}, {5, 1}, {6, 1}, {7, 1}, {8, 1}, {10, 0}}, 10 = {{3, 1}, {4, 1}, {5, 1}, {6, 1}, {7, 1}, {8, 1}, {10, 0}}, 11 = {{1, 0}, {2, 0}, {3, 0}, {4, 0}, {7, 1}, {8, 1}, {9 , 1}}, 12 = {{1, 0}, {2, 0}, {3, 0}, {6, 1}, {7, 1}, {8, 1}, {9 , 1}}, 13 = {{1, 0}, {2, 0}, {5, 1}, {6, 1}, {7, 1}, {8, 1}, {9 , 1}}, 14 = {{1, 0}, {4, 1}, {5, 1}, {6, 1}, {7, 1}, {8, 1}, {9 , 1}}, 15 = {{3, 1}, {4, 1}, {5, 1}, {6, 1}, {7, 1}, {8, 1}, {9 , 1}}, 16 = {{1, 0}, {2, 0}, {3, 0}, {4, 0}, {7, 1}, {8, 1}, {11, 1}}, 17 = {{1, 0}, {2, 0}, {3, 0}, {6, 1}, {7, 1}, {8, 1}, {11, 1}}, 18 = {{1, 0}, {2, 0}, {5, 1}, {6, 1}, {7, 1}, {8, 1}, {11, 1}}, 19 = {{1, 0}, {4, 1}, {5, 1}, {6, 1}, {7, 1}, {8, 1}, {11, 1}}, 20 = {{3, 1}, {4, 1}, {5, 1}, {6, 1}, {7, 1}, {8, 1}, {11, 1}} } % 尺寸计算函数 \cs_new:Nn \__suanpan_dim_calc: { % 基础尺寸半长度 \dim_set:Nn \l__suanpan_bid_h_half_dim { \l__suanpan_bid_h_dim / 2 } \dim_set:Nn \l__suanpan_bid_d_half_dim { \l__suanpan_bid_d_dim / 2 } \dim_set:Nn \l__suanpan_rod_d_half_dim { \l__suanpan_rod_d_dim / 2 } \dim_set:Nn \l__suanpan_frame_b_half_dim { \l__suanpan_frame_b_dim / 2 } % 上珠偏移(8号算珠) \dim_set:Nn \l__suanpan_bid_upper_offset_dim { \l__suanpan_frame_b_half_dim + \l__suanpan_bid_h_half_dim } % 下珠偏移(1号算珠) \dim_set:Nn \l__suanpan_bid_lower_offset_dim { -\l__suanpan_frame_b_half_dim - \l__suanpan_bid_h_dim * 7 + \l__suanpan_bid_h_half_dim } % 上档杆长度 \dim_set:Nn \l__suanpan_rod_upper_h_dim { \l__suanpan_bid_h_dim * 3 } % 下档杆长度 \dim_set:Nn \l__suanpan_rod_lower_h_dim { \l__suanpan_bid_h_dim * 7 } % 上档杆偏移 \dim_set:Nn \l__suanpan_rod_upper_offset_dim { \l__suanpan_frame_b_half_dim } % 下档杆偏移 \dim_set:Nn \l__suanpan_rod_lower_offset_dim { -\l__suanpan_frame_b_half_dim - \l__suanpan_rod_lower_h_dim } % 上边框内线偏移 \dim_set:Nn \l__suanpan_frame_upper_inner_offset_dim { \l__suanpan_frame_b_half_dim + \l__suanpan_rod_upper_h_dim } % 下边框内线偏移 \dim_set:Nn \l__suanpan_frame_lower_inner_offset_dim { -\l__suanpan_frame_b_half_dim - \l__suanpan_rod_lower_h_dim } % 上边框外线偏移 \dim_set:Nn \l__suanpan_frame_upper_outer_offset_dim { \l__suanpan_frame_b_dim + \l__suanpan_frame_b_half_dim + \l__suanpan_rod_upper_h_dim } % 下边框外线偏移 \dim_set:Nn \l__suanpan_frame_lower_outer_offset_dim { -\l__suanpan_frame_b_dim - \l__suanpan_frame_b_half_dim - \l__suanpan_rod_lower_h_dim } % 算珠上边缘 \dim_set:Nn \l__suanpan_bid_upper_dim { \l__suanpan_bid_h_half_dim - \l__suanpan_bid_sep_dim } % 算珠上边缘 \dim_set:Nn \l__suanpan_bid_lower_dim { -\l__suanpan_bid_h_half_dim + \l__suanpan_bid_sep_dim } % 算珠圆角半径 \dim_set:Nn \l__suanpan_bid_arc_dim { \l__suanpan_bid_upper_dim - 1pt} % 横梁计位点半径 \dim_set:Nn \l__suanpan_frame_unit_r_dim { \l__suanpan_frame_b_dim / 4 } % 边框外线半宽度 \dim_set:Nn \l__suanpan_frame_outer_half_dim { \l__suanpan_frame_outer_linewidth_dim / 2 } % 档杆总成宽度 \dim_set:Nn \l__suanpan_support_d_dim { \l__suanpan_bid_d_dim + \l__suanpan_rod_sep_dim } % 档杆总成半宽度 \dim_set:Nn \l__suanpan_support_d_half_dim { \l__suanpan_bid_d_half_dim + \l__suanpan_rod_sep_dim / 2 } % 上档杆总成高度 \dim_set:Nn \l__suanpan_support_upper_dim { \l__suanpan_frame_upper_outer_offset_dim - \l__suanpan_frame_outer_half_dim } % 下档杆总成高度 \dim_set:Nn \l__suanpan_support_lower_dim { \l__suanpan_frame_lower_outer_offset_dim + \l__suanpan_frame_outer_half_dim } % 档杆总成组装垂直偏移 \dim_set:Nn \l__suanpan_support_y_offset_dim { ( \l__suanpan_frame_upper_outer_offset_dim - \l__suanpan_frame_lower_outer_offset_dim + \l__suanpan_frame_outer_linewidth_dim ) / 2 - \l__suanpan_frame_upper_outer_offset_dim - \l__suanpan_frame_outer_half_dim } % 上边框外线位置 \dim_set:Nn \l__suanpan_frame_outer_upper_dim { \l__suanpan_frame_upper_inner_offset_dim + \l__suanpan_frame_b_dim - \l__suanpan_frame_outer_half_dim } % 下边框外线位置 \dim_set:Nn \l__suanpan_frame_outer_lower_dim { \l__suanpan_frame_lower_inner_offset_dim - \l__suanpan_frame_b_dim + \l__suanpan_frame_outer_half_dim } % 上边框内线位置 \dim_set:Nn \l__suanpan_frame_inner_upper_dim { \l__suanpan_frame_upper_inner_offset_dim + \l__suanpan_frame_inner_linewidth_dim / 2 } % 下边框内线位置 \dim_set:Nn \l__suanpan_frame_inner_lower_dim { \l__suanpan_frame_lower_inner_offset_dim - \l__suanpan_frame_inner_linewidth_dim / 2 } % 左右边框外线偏移 \dim_set:Nn \l__suanpan_frame_outer_lr_offset_dim { \l__suanpan_support_d_half_dim + \l__suanpan_frame_b_dim - \l__suanpan_frame_outer_half_dim } % 左右边框外线圆角半径 \dim_set:Nn \l__suanpan_frame_outer_arc_dim { \l__suanpan_frame_outer_linewidth_dim / 3 - 1pt } } % 选项处理 % ============宏包选项================= \keys_define:nn { suanpan } { draft .choice:, draft / true .code:n = { \bool_set_true:N \l__suanpan_draft_bool }, draft / false .code:n = { \bool_set_false:N \l__suanpan_draft_bool }, draft .default:n = true, draft .initial:n = false, unknown .code:n = { \msg_error:nn { suanpan } { unknown-option } } } % key_value选项设计 \keys_define:nn { suanpan } { % 绘图线宽 linewd .code:n = { % 算盘边框内线线宽 \dim_set:Nn \l__suanpan_frame_inner_linewidth_dim {#1} % 算盘边框外线线宽 \dim_set:Nn \l__suanpan_frame_outer_linewidth_dim { \fp_to_dim:n { \fp_eval:n {% \l__suanpan_frame_inner_linewidth_dim * 7.0 } } } % 档杆线宽 \dim_set:Nn \l__suanpan_rod_linewidth_dim { \fp_to_dim:n { \fp_eval:n {% \l__suanpan_frame_inner_linewidth_dim * 1.0 } } } % 算珠线宽 \dim_set:Nn \l__suanpan_bid_linewidth_dim { \fp_to_dim:n { \fp_eval:n {% \l__suanpan_frame_inner_linewidth_dim * 1.0 } } } % 计算其它尺寸 \__suanpan_dim_calc: }, linewd .initial:n = 2.0pt, % 档间间距 rodsep .code:n = {% \dim_set:Nn \l__suanpan_rod_sep_dim { #1 } % 计算其它尺寸 \__suanpan_dim_calc: }, rodsep .initial:n = 3.0pt, % 算珠间距 bidsep .code:n = {% \dim_set:Nn \l__suanpan_bid_sep_dim { #1 } % 计算其它尺寸 \__suanpan_dim_calc: }, bidsep .initial:n = 0.0pt, % 缩放参数 scale .tl_set:N = \l__suanpan_scale_tl, scale .initial:n = 1.0, % 边框绘图颜色 framedraw .code:n = { \__suanpan_color_select:nn { framecolor } {#1} }, framedraw .initial:n = black, framedraw* .code:n = { \__suanpan_color_select:nnn { framecolor } #1 }, % 档杆绘图颜色 roddraw .code:n = { \__suanpan_color_select:nn { roddrawcolor } {#1} }, roddraw .initial:n = black, roddraw* .code:n = { \__suanpan_color_select:nnn { roddrawcolor } #1 }, % 档杆填充颜色 rodfill .code:n = { \__suanpan_color_select:nn { rodfillcolor } {#1} }, rodfill .initial:n = white, rodfill* .code:n = { \__suanpan_color_select:nnn { rodfillcolor } #1 }, % 外珠绘图颜色 outerdraw .code:n = { \__suanpan_color_select:nn { outerdrawcolor } {#1} }, outerdraw .initial:n = black, outerdraw* .code:n = { \__suanpan_color_select:nnn { outerdrawcolor } #1 }, % 外珠填充颜色 outerfill .code:n = { \__suanpan_color_select:nn { outerfillcolor } {#1} }, outerfill .initial:n = white, outerfill* .code:n = { \__suanpan_color_select:nnn { outerfillcolor } #1 }, % 内珠绘图颜色 innerdraw .code:n = { \__suanpan_color_select:nn { innerdrawcolor } {#1} }, innerdraw .initial:n = black, innerdraw* .code:n = { \__suanpan_color_select:nnn { innerdrawcolor } #1 }, % 内珠填充颜色 innerfill .code:n = { \__suanpan_color_select:nn { innerfillcolor } {#1} }, innerfill .initial:n = black, innerfill* .code:n = { \__suanpan_color_select:nnn { innerfillcolor } #1 }, unknown .code:n = { \msg_error:nn { suanpan } { unknown-option } } } % 出错输出信息 \msg_new:nnn { suanpan } { unknown-option } { package~ option~ "\l_keys_key_tl"~ is~ unknown. } % 将文档类选项传给suanpan \ProcessKeysOptions { suanpan } % 选项默认值 \keys_set:nn { suanpan } { linewd = 2.0pt, rodsep = 3.0pt, bidsep = 1.8pt, scale = 1.0, } % 构建内珠(靠近横梁,参与计数)容器(盒子) \cs_new:Nn \__suanpan_inner_bid_construct: { \group_begin: \hcoffin_gset:Nn \l__suanpan_inner_bid_coffin { \draw_begin: \color_stroke:n { innerdrawcolor } \draw_linewidth:n { \l__suanpan_bid_linewidth_dim } % 圆角矩形绘制较为耗时,draft模式时将绘制直角矩形 \bool_if:NF \l__suanpan_draft_bool { \draw_path_corner_arc:nn{ \l__suanpan_bid_arc_dim } { \l__suanpan_bid_arc_dim } } % 绘制算珠 \draw_path_rectangle_corners:nn { -\l__suanpan_bid_d_half_dim, \l__suanpan_bid_lower_dim} { \l__suanpan_bid_d_half_dim, \l__suanpan_bid_upper_dim} \color_fill:n { innerfillcolor } \draw_path_use_clear:n { stroke, fill } \draw_end: } \group_end: } % 构建外珠(远离横梁,不参与计数)容器(盒子) \cs_new:Nn \__suanpan_outer_bid_construct: { \group_begin: \hcoffin_gset:Nn \l__suanpan_outer_bid_coffin { \draw_begin: \color_stroke:n { outerdrawcolor } \draw_linewidth:n { \l__suanpan_bid_linewidth_dim } % 圆角矩形绘制较为耗时,draft模式时将绘制直角矩形 \bool_if:NF \l__suanpan_draft_bool { \draw_path_corner_arc:nn{ \l__suanpan_bid_arc_dim } { \l__suanpan_bid_arc_dim } } % 绘制算珠 \draw_path_rectangle_corners:nn { -\l__suanpan_bid_d_half_dim, \l__suanpan_bid_lower_dim} { \l__suanpan_bid_d_half_dim, \l__suanpan_bid_upper_dim} \color_fill:n { outerfillcolor } \draw_path_use_clear:n { stroke, fill } \draw_end: } \group_end: } % 构建浮珠容器(盒子) % 浮珠指可以在算盘中任意布置的算珠 \cs_new:Npn \__suanpan_float_bid_construct:nn #1#2 { \group_begin: \hcoffin_gset:Nn \l__suanpan_float_bid_coffin { \draw_begin: \color_stroke:n { #1 } \draw_linewidth:n { \l__suanpan_bid_linewidth_dim } % 圆角矩形绘制较为耗时,draft模式时将绘制直角矩形 \bool_if:NF \l__suanpan_draft_bool { \draw_path_corner_arc:nn{ \l__suanpan_bid_arc_dim } { \l__suanpan_bid_arc_dim } } % 绘制算珠 \draw_path_rectangle_corners:nn { -\l__suanpan_bid_d_half_dim, \l__suanpan_bid_lower_dim} { \l__suanpan_bid_d_half_dim, \l__suanpan_bid_upper_dim} \color_fill:n { #2 } \draw_path_use_clear:n { stroke, fill } \draw_end: } \group_end: } % 构建档杆容器(盒子) \cs_new:Nn \__suanpan_support_construct: { \group_begin: \hcoffin_gset:Nn \l__suanpan_support_coffin { \draw_begin: \draw_scope_begin: \color_stroke:n { roddrawcolor } \draw_linewidth:n { \l__suanpan_rod_linewidth_dim } % 绘制下杆 \draw_path_rectangle:nn { -\l__suanpan_rod_d_half_dim, \l__suanpan_rod_lower_offset_dim } { \l__suanpan_rod_d_dim, \l__suanpan_rod_lower_h_dim } % 绘制上杆 \draw_path_rectangle:nn { -\l__suanpan_rod_d_half_dim, \l__suanpan_rod_upper_offset_dim } { \l__suanpan_rod_d_dim, \l__suanpan_rod_upper_h_dim } \color_fill:n { rodfillcolor } \draw_path_use_clear:n { stroke, fill } % 绘制横梁 \color_stroke:n { framecolor } \draw_path_moveto:n { -\l__suanpan_support_d_half_dim, \l__suanpan_frame_b_half_dim } \draw_path_lineto:n { \l__suanpan_support_d_half_dim, \l__suanpan_frame_b_half_dim } \draw_path_moveto:n { -\l__suanpan_support_d_half_dim, -\l__suanpan_frame_b_half_dim } \draw_path_lineto:n { \l__suanpan_support_d_half_dim, -\l__suanpan_frame_b_half_dim } \draw_path_use_clear:n { stroke} \draw_scope_end: % 绘制上下边框 \draw_scope_begin: \color_stroke:n { framecolor } \bool_if:NT \l__suanpan_support_frame_bool { \draw_linewidth:n { \l__suanpan_frame_inner_linewidth_dim } % 上边框内线 \draw_path_moveto:n { -\l__suanpan_support_d_half_dim, \l__suanpan_frame_upper_inner_offset_dim } \draw_path_lineto:n { \l__suanpan_support_d_half_dim, \l__suanpan_frame_upper_inner_offset_dim } % 下边框内线 \draw_path_moveto:n { -\l__suanpan_support_d_half_dim, \l__suanpan_frame_lower_inner_offset_dim } \draw_path_lineto:n { \l__suanpan_support_d_half_dim, \l__suanpan_frame_lower_inner_offset_dim } \draw_path_use_clear:n { stroke} \draw_linewidth:n { \l__suanpan_frame_outer_linewidth_dim } % 上边框外线 \draw_path_moveto:n { -\l__suanpan_support_d_half_dim, \l__suanpan_support_upper_dim } \draw_path_lineto:n { \l__suanpan_support_d_half_dim, \l__suanpan_support_upper_dim } % 下边框外线 \draw_path_moveto:n { -\l__suanpan_support_d_half_dim, \l__suanpan_support_lower_dim } \draw_path_lineto:n { \l__suanpan_support_d_half_dim, \l__suanpan_support_lower_dim } \draw_path_use_clear:n { stroke} } \draw_scope_end: % 绘制计位点(黑色圆点) \bool_if:NT \l__suanpan_support_unit_bool { \draw_scope_begin: \color_stroke:n { black } \draw_path_circle:nn { 0pt, 0pt} { \l__suanpan_frame_unit_r_dim } \bool_if:NTF \l__suanpan_draft_bool { \draw_path_use_clear:n { stroke } }{ \color_fill:n { black } \draw_path_use_clear:n { stroke, fill } } \draw_scope_end: } \draw_end: } \group_end: } % 构建档杆空白容器(盒子),用于占位以避免算盘总成尺寸错误 \cs_new:Nn \__suanpan_empty_support_construct: { % 测量档杆容器(盒子)总宽度和高度 \dim_set:Nn \l_tmpa_dim { \coffin_wd:N \l__suanpan_support_coffin } \dim_set:Nn \l_tmpb_dim { \__suanpan_coffin_ht_plus_dp:N \l__suanpan_support_coffin } % 创建一个空容器(盒子) \hcoffin_set:Nn \l__suanpan_empty_support_coffin { \phantom{a} } % 调整为档杆宽度和高度 \coffin_resize:Nnn \l__suanpan_empty_support_coffin { \l_tmpa_dim } { \l_tmpb_dim } } % 绘制算珠 % #1: 算珠位置编号 % o o o o o o o | o o o % 1 2 3 4 5 6 7 8 9 10 % | o % 11 % 其中,11是悬珠位置 % #2: 算珠类型 % 0——外珠,1——内珠,2——浮珠 \cs_new:Npn \__suanpan_dispose_bid:nn #1#2 { % 算珠垂直偏移 \int_compare:nNnTF { #1 } > { 7 } { \int_compare:nNnTF { #1 } < { 11 } { % 上珠 \dim_set:Nn \l_tmpb_dim{ \l__suanpan_bid_upper_offset_dim + \l__suanpan_bid_h_dim * (#1 - 8) } }{ % 悬珠 \dim_set:Nn \l_tmpb_dim{ \l__suanpan_frame_b_half_dim + \l__suanpan_bid_h_dim * 2 } } }{ % 下珠 \dim_set:Nn \l_tmpb_dim{ \l__suanpan_bid_lower_offset_dim + \l__suanpan_bid_h_dim * (#1 - 1) } } % 绘图算珠 \draw_scope_begin: \draw_transform_yshift:n { \l_tmpb_dim } \int_case:nnF { #2 } { { 0 } { \draw_coffin_use:Nnn \l__suanpan_outer_bid_coffin { hc } { vc } } { 1 } { \draw_coffin_use:Nnn \l__suanpan_inner_bid_coffin { hc } { vc } } { 2 } { \draw_coffin_use:Nnn \l__suanpan_float_bid_coffin { hc } { vc } } }{ Bid~Type~Error! } \draw_scope_end: } % 函数变体 \cs_generate_variant:Nn \__suanpan_dispose_bid:nn {xx} % 组装一个档位 % #1: 档位编号(基于1,从左向右计数) % #2: 档位数字 \cs_new:Npn \__suanpan_assemble_rod:nn #1#2 { % 取得档位数字对应算珠位置及内珠/外珠编号 \prop_get:NnN \l__suanpan_rods_val_prop { #2 } \l_tmpa_tl \clist_clear:N \l__suanpan_bids_pos_clist \clist_set:NV \l__suanpan_bids_pos_clist \l_tmpa_tl % 绘制 \draw_scope_begin: % 水平方向偏移 \draw_transform_xshift:n { \l__suanpan_support_d_dim * (#1 - 1) } % 档杆 \draw_coffin_use:Nnnn \l__suanpan_support_coffin { hc } { vc } { 0pt, -\l__suanpan_support_y_offset_dim } % 算珠 \clist_map_inline:Nn \l__suanpan_bids_pos_clist { \clist_set:Nn \l_tmpa_clist { ##1 } \__suanpan_dispose_bid:xx { \clist_item:Nn \l_tmpa_clist { 1 } } { \clist_item:Nn \l_tmpa_clist { 2 } } } \draw_scope_end: } % 指定一个档上某个算珠的颜色 % #1: 档位编号(从左向右) % #2: 算珠位置([1, 11]) \cs_new:Npn \__suanpan_float_bid:nn #1#2 { \draw_scope_begin: % 水平偏移 \draw_transform_xshift:n { \l__suanpan_support_d_dim * ( #1 - 1 ) } % 空白支杆(宽度尺寸占位) \draw_coffin_use:Nnnn \l__suanpan_empty_support_coffin { hc } { vc } { 0pt, -\l__suanpan_support_y_offset_dim } % 覆盖绘制 \__suanpan_dispose_bid:xx { #2 }{ 2 } \draw_scope_end: } % 绘制左右边框 % #1 第一档编号(左) % #2 最后一档编号(右) \cs_new:Npn \__suanpan_left_right_frame:nn #1#2 { % 绘制左边框 \draw_scope_begin: \color_stroke:n { framecolor } \draw_linewidth:n { \l__suanpan_frame_inner_linewidth_dim } \draw_transform_xshift:n { \l__suanpan_support_d_dim * (#1 - 1) } % 内线 \draw_path_moveto:n { -\l__suanpan_support_d_half_dim, \l__suanpan_frame_inner_upper_dim } \draw_path_lineto:n { -\l__suanpan_support_d_half_dim, \l__suanpan_frame_inner_lower_dim } \draw_path_use_clear:n { stroke} % 外线 \draw_linewidth:n { \l__suanpan_frame_outer_linewidth_dim } \draw_path_corner_arc:nn { \l__suanpan_frame_outer_arc_dim } { \l__suanpan_frame_outer_arc_dim } \draw_path_moveto:n { -\l__suanpan_support_d_half_dim, \l__suanpan_frame_outer_upper_dim } \draw_path_lineto:n { -\l__suanpan_frame_outer_lr_offset_dim, \l__suanpan_frame_outer_upper_dim } \draw_path_lineto:n { -\l__suanpan_frame_outer_lr_offset_dim, \l__suanpan_frame_outer_lower_dim } \draw_path_lineto:n { -\l__suanpan_support_d_half_dim, \l__suanpan_frame_outer_lower_dim } \draw_path_use_clear:n { stroke} \draw_scope_end: % 绘制右边框 \draw_scope_begin: \color_stroke:n { framecolor } \draw_linewidth:n { \l__suanpan_frame_inner_linewidth_dim } \draw_transform_xshift:n { \l__suanpan_support_d_dim * (#2 - 1) } % 内线 \draw_path_moveto:n { \l__suanpan_support_d_half_dim, \l__suanpan_frame_inner_upper_dim } \draw_path_lineto:n { \l__suanpan_support_d_half_dim, \l__suanpan_frame_inner_lower_dim } \draw_path_use_clear:n { stroke} % 外线 \draw_linewidth:n { \l__suanpan_frame_outer_linewidth_dim } \draw_path_corner_arc:nn { \l__suanpan_frame_outer_arc_dim } { \l__suanpan_frame_outer_arc_dim } \draw_path_moveto:n { \l__suanpan_support_d_half_dim, \l__suanpan_frame_outer_upper_dim } \draw_path_lineto:n { \l__suanpan_frame_outer_lr_offset_dim, \l__suanpan_frame_outer_upper_dim } \draw_path_lineto:n { \l__suanpan_frame_outer_lr_offset_dim, \l__suanpan_frame_outer_lower_dim } \draw_path_lineto:n { \l__suanpan_support_d_half_dim, \l__suanpan_frame_outer_lower_dim } \draw_path_use_clear:n { stroke} \draw_scope_end: } % 算盘排版用户接口 % 算盘排版环境 % #1 suanpan环境选项 % #2 suanpan环境内容(!b参数) \NewDocumentEnvironment{ suanpan }{ o !b } { \group_begin: % 设置选项 \IfNoValueF{ #1 } { \keys_set:nn { suanpan }{ #1 } } % 构造基础容器(盒子) \__suanpan_inner_bid_construct: \__suanpan_outer_bid_construct: \__suanpan_support_construct: \__suanpan_empty_support_construct: % 删除#2(!b)参数取得的环境内容中的空白 \str_set:Nn \l_tmpa_str { #2 } \str_remove_all:Nn \l_tmpa_str {~} % 将str还原为tl \tl_set_rescan:Nno \l_tmpa_tl {}{ \l_tmpa_str } % 在容器(盒子)中用#2(!b)参数取得的按环境内容实现绘制 \hcoffin_set:Nn \l_tmpa_coffin { \draw_begin: \l_tmpa_tl \draw_end: } % TODO: 需要增加组装档位数字标记功能 \hcoffin_set:Nn \l_tmpb_coffin { % 将原图缩小50% \coffin_scale:Nnn \l_tmpa_coffin { 0.50 } { 0.50 } \coffin_typeset:Nnnnn \l_tmpa_coffin{ l }{ b }{ 0pt }{ 0pt } } }{ % 按用户指定比例缩放 \coffin_scale:Nnn \l_tmpb_coffin { \l__suanpan_scale_tl } { \l__suanpan_scale_tl } % 设置基字符盒子(排版深度) \hbox_set:Nn \l_tmpa_box { x } % 盒子深度 \dim_set:Nn \l_tmpa_dim { \box_dp:N \l_tmpa_box} % 输出算盘容器(盒子) \coffin_typeset:Nnnnn \l_tmpb_coffin{ l }{ b }{ 0pt }{ -\l_tmpa_dim } \int_zero:N \l__suanpan_rod_max_int \group_end: } % 排版算盘一个档位,包括档杆和算珠 % #1 星号命令,绘制计位点(黑色圆点) % #2 档位编号(基于1,从左向右计数) % #3 本档数字(0-20,10-15需用到顶珠和底珠,16-20需要用到悬珠) % TODO: 会比加了浮珠后的水平尺寸大,未找到原因 \NewDocumentCommand{ \rod }{ s m m } { % 记录原状态变量 \bool_set_eq:NN \l_tmpa_bool \l__suanpan_support_unit_bool % 星号命令,用于在横梁绘制计位点 \IfBooleanTF{#1} { \bool_set_true:N \l__suanpan_support_unit_bool }{ \bool_set_false:N \l__suanpan_support_unit_bool } % 如状态发生改变,则重新绘制档杆单元 \bool_if:NTF \l_tmpa_bool { \bool_if:NF \l__suanpan_support_unit_bool { \__suanpan_support_construct: } }{ \bool_if:NT \l__suanpan_support_unit_bool { \__suanpan_support_construct: } } % 统计档位总数 \int_set:Nn \l__suanpan_rod_max_int { \int_max:nn { \l__suanpan_rod_max_int }{ #2 } } % 组装一个档位 \__suanpan_assemble_rod:nn { #2 }{ #3 } } % 排版算盘多个档位,包括档杆和算珠 % #1 本档数字(0-20,10-15需用到顶珠和底珠,16-20需要用到悬珠) % 各数字之间需要用逗号分隔。 \NewDocumentCommand{ \rods }{ m } { % 构造档位数字列表 \clist_set:Nn \l_tmpa_clist { #1 } % 初始化档位计数器 \int_set:Nn \l_tmpa_int { 0 } \clist_map_inline:Nn \l_tmpa_clist { % 档位计数器自增1 \int_incr:N \l_tmpa_int % 组装当前档位 \__suanpan_assemble_rod:nn {\l_tmpa_int }{ ##1 } } % 记录档位总数 \int_set:Nn \l__suanpan_rod_max_int { \int_max:nn { \l__suanpan_rod_max_int }{ \l_tmpa_int } } } % 指定某档上某个算珠的颜色 % #1 档位(基于1,从左向右计数) % #2 算珠位置(1--11,11为悬珠位置) % #3 算珠填充颜色(绘图颜色将添加40%的黑色) \NewDocumentCommand{ \bid }{ m m m } { % 构造浮珠 \__suanpan_float_bid_construct:nn { #3!60!black }{ #3 } % 绘制浮珠 \__suanpan_float_bid:nn { #1 }{ #2 } % 如不是最后一个档位,则添加一个额外空白档杆以实现水平尺寸占位 \int_compare:nNnT { #1 } < { \l__suanpan_rod_max_int } { \draw_scope_begin: % 水平偏移 \draw_transform_xshift:n { \l__suanpan_support_d_dim * ( \l__suanpan_rod_max_int - 1 ) } % 空白支杆(水平尺寸占位) \draw_coffin_use:Nnnn \l__suanpan_empty_support_coffin { hc } { vc } { 0pt, -\l__suanpan_support_y_offset_dim } \draw_scope_end: } } % 指定某档上所有内珠或外珠的颜色 % #1 星号命令,选择内珠或外珠 % #2 档位(基于1,从左向右计数) % #3 该档数字 % #4 算珠填充颜色(绘图颜色将添加40%的黑色) \NewDocumentCommand{ \bids }{ s m m m } { % 构造浮珠 \__suanpan_float_bid_construct:nn { #4!60!black }{ #4 } % 取得档位数字对应算珠位置及内珠/外珠编号 \prop_get:NnN \l__suanpan_rods_val_prop { #3 } \l_tmpa_tl \clist_clear:N \l__suanpan_bids_pos_clist \clist_set:NV \l__suanpan_bids_pos_clist \l_tmpa_tl % 星号命令,用于选择外珠着色 \IfBooleanTF{#1} { \clist_map_inline:Nn \l__suanpan_bids_pos_clist { \clist_set:Nn \l_tmpa_clist { ##1 } % 选择外珠 \int_compare:nNnT { \clist_item:Nn \l_tmpa_clist { 2 } } = { 0 } { \__suanpan_float_bid:nn { #2 }{ \clist_item:Nn \l_tmpa_clist { 1 } } } } }{ \clist_map_inline:Nn \l__suanpan_bids_pos_clist { \clist_set:Nn \l_tmpa_clist { ##1 } % 选择内珠 \int_compare:nNnT { \clist_item:Nn \l_tmpa_clist { 2 } } = { 1 } { \__suanpan_float_bid:nn { #2 }{ \clist_item:Nn \l_tmpa_clist { 1 } } } } } % 如不是最后一个档位,则添加一个额外空白档杆以实现水平尺寸占位 \int_compare:nNnT { #1 } < { \l__suanpan_rod_max_int } { \draw_scope_begin: % 水平偏移 \draw_transform_xshift:n { \l__suanpan_support_d_dim * ( \l__suanpan_rod_max_int - 1 ) } % 空白支杆(水平尺寸占位) \draw_coffin_use:Nnnn \l__suanpan_empty_support_coffin { hc } { vc } { 0pt, -\l__suanpan_support_y_offset_dim } \draw_scope_end: } } % 左右边框 % #1 左边第一个档位(基于1,从左向右计数) % #2 右边最后一个档位(基于1,从左向右计数) \NewDocumentCommand{ \lrframe }{ m m } { \__suanpan_left_right_frame:nn { #1 }{ #2 } } % 选项设置用户接口 \NewDocumentCommand \suanpanset { m } { \IfNoValueF { #1} { \keys_set:nn { suanpan } { #1 } } } \endinput