100 如何使用l3coffin找到合适的锚点拼接以实现嵌套积分号排版

发布于 2024-09-19 21:39:43

林间花老师指出如下的嵌套积分是简单的😊

image.png

在尝试学习了l3coffin后实现已经实现了从对于coffin中的积分号,先后在其右上和右下位置处添加新的coffin连接的"轮子",如下面的代码:

\documentclass{article}
\usepackage[margin=2cm,a2paper]{geometry}
\usepackage{amsmath}
\usepackage{xcolor}
\usepackage{xcoffins}
\usepackage{graphicx}
\newcommand{\diffx}{$\mathrm{d}x$}
\newcommand{\myint}[1][8]{\scalebox{#1}{$\int$}}
\newcommand{\numzero}[1][8]{\scalebox{#1}{$0$}}
\newcommand{\numone}[1][8]{\scalebox{#1}{$1$}}
\begin{document}
\ExplSyntaxOn
%改进\coffin_reset_poles:N的c功能并将其封装为\my_coffin_reset:n
\cs_generate_variant:Nn \coffin_reset_poles:N {c}
\cs_new:Nn \my_coffin_reset:n % <coffin>
{
    \coffin_reset_poles:c {l_tmp#1_coffin}
}
\cs_set_nopar:Nn \newSetCof:nn {
    %重新封装coffins命令,若不存在,则新建名为l_tmp#1_coffin的新coffin
    %之后设置hcoffin的matrial内容为#2
    \coffin_if_exist:cF {l_tmp#1_coffin}
    {\coffin_new:c {l_tmp#1_coffin}}
    \hcoffin_set:cn {l_tmp#1_coffin}{#2}
}
\cs_set_nopar:Nn \useCof:n {
    %控制输出coffin,默认按照手柄位置handles{l,b}进行对齐
    %\coffin_typeset:cnnnn {l_tmp#1_coffin} {l} {b} {0pt} {0pt}
    \coffin_display_handles:cn {l_tmp#1_coffin} {magenta}
}
\cs_set_nopar:Nn \setIntPolesUpper:n {%设置上半对齐点位
    \my_coffin_reset:n {#1}
    \coffin_set_horizontal_pole:cnn {l_tmp#1_coffin}{upperh}{0.95 \coffin_ht:c {l_tmp#1_coffin}}% 0.8 height pole set
    \coffin_set_vertical_pole:cnn {l_tmp#1_coffin}{upperv}{0.88 \coffin_wd:c {l_tmp#1_coffin}}
    % then we can use the handle (upperv,upperh) for the upper one
}
\cs_set_nopar:Nn \setIntPolesLower:n {
    \my_coffin_reset:n {#1}%设置下半对齐点位
    \coffin_set_horizontal_pole:cnn {l_tmp#1_coffin}{lowerh}{-0.3 \coffin_ht:c {l_tmp#1_coffin}}% -0.25 height pole set
    % \coffin_set_vertical_pole:Nnn {\l_tmpI_coffin}{baselow}{0pt}
    \coffin_set_vertical_pole:cnn {l_tmp#1_coffin}{lowerv}{0.85 \coffin_wd:c {l_tmp#1_coffin}}
    % then we can use the handle (lowerv,lowerh) for the upper one
}
%关键的核心封装命令\intgral_coffin_join:nnn <intgral-within-coffin> <content1> <content2> 
\cs_new:Nn \intgral_coffin_join:nnn {
    % #1=base #2 = \myint[4] #3 = \numone[4]
    %处理流程:
    %(1) reset coffin poles with base
    \my_coffin_reset:n {#1}
    %(2) new_coffin_with_tmpA with matrial #2
    \newSetCof:nn {A} {#2}
    %(3) join base with upper_part tmpA with handles (upperv,upperh) (l,vc)
    \setIntPolesUpper:n {#1}

    \coffin_join:cnncnnnn {l_tmp#1_coffin} {upperv} {upperh} {l_tmpA_coffin} {l} {vc} {0pt} {0pt}
    %(4) reset coffin poles with base
    \my_coffin_reset:n {#1}
    % %(5) new_coffin_with_tmpB with matrial #3
    \newSetCof:nn {B} {#3}
    % %(6) join base with lower_part tmpB with handles (lowerv,lowerh) (l,vc)
    \setIntPolesLower:n {#1}
    \coffin_join:cnncnnnn {l_tmp#1_coffin} {lowerv} {lowerh} {l_tmpB_coffin} {l} {vc} {0pt} {0pt}
    %(7) reset coffin poles with base
    \my_coffin_reset:n {#1}
    % return => joined and poles-reset coffin "base"
}

\newSetCof:nn {base}{\myint}
\my_coffin_reset:n {base}
\useCof:n {base}

% test for command \intgral_coffin_join
% \intgral_coffin_join:nnn {base}{\myint[4]}{\numone[4]}
% \useCof:n {base}

\int_step_inline:nn {6}{
    \intgral_coffin_join:nnn {base}{\myint[5]}{\myint[5]}
    \useCof:n {base} \par
}
\ExplSyntaxOff
\end{document}

其中关键代码已做注释,\intgral_coffin_join:nnn用于实现一次coffin的拼接,实现的效果如下:
image.png

现阶段存在的问题是:

  • 在默认的积分号的倾斜角度下,是否容易找到合适的定位「superscript」和「subscript pattern」(例如使用的水平或者垂直pole进行定位的距离符合某一等比数列),或者改用垂直积分号
  • 使用递归等方式找到原图的重复规律(这种规律不一定是真实存在的,原图的转发者透露该图是使用类似 photoshop 等软件制作的)只要能实现这种类似"分形"的效果就可以...

有关可汇总和参考的代码如下压缩包文件nested-integral.zip

(Un)Happy TeXing!


Edited: 2025-12-04

林间花老师又补充了一个图

来自:

image.png

其递推结构为...

image.png

现在问题的关键主要是找到原问题的递归结构....

查看更多

关注者
0
被浏览
1.2k
2 个回答
Eureka
Eureka 2024-09-20
这家伙很懒,什么也没写!

目前给一个比较粗糙的答案, 使用的是递归生成的, 生成的效果大概如下:

  • 支持分支,不可以指定上下限

image.png

这种情况下可以用上一些 PS 的手段,然后就可以得到像下面这样的结果:
image.png

  • 不支持分支,可以指定上下限

image.png

完整的可编译代码如下:

\documentclass{article}
\usepackage{amsmath}
\usepackage[a3paper, margin=1in]{geometry}



\ExplSyntaxOn
\int_new:N \g_recursion_depth_int
\int_gset:Nn \g_recursion_depth_int {0}
\seq_new:N \g_data_collect_I_seq
\seq_new:N \g_data_collect_II_seq

% ==> fractuation integral
% ARGUMENTS SPECIFICATION
% #1 -> subscript
% #2 -> superscript
% #3 -> total depth
\NewDocumentCommand\NEWINT{mmm}{
  \int_compare:nTF {\g_recursion_depth_int <= #3}{
    \int_gadd:Nn \g_recursion_depth_int {1}
    \_split_int_fractal_tree:ennn 
      {\_split_list}{#1}{#2}{#3}
  }{
    \int
      \c_math_subscript_token{#1}
      \c_math_superscript_token{#2}
  }
}
\NewDocumentCommand\_split_list{}{}
\DeclareDocumentCommand\SplitList{m}{
  \renewcommand\_split_list{#1}
}
% ==> split tree using a int array
% ARGUMENTS SPECIFICATION
% #1 -> places to split the tree
% #2 -> subscript
% #3 -> superscript
% #4 -> total depth
\cs_new_protected:Npn \_split_int_fractal_tree:nnnn #1#2#3#4 {
  \clist_if_in:nVTF {#1}\g_recursion_depth_int {
    \NEWINT
        {\int\c_math_subscript_token {
            #2 \c_math_superscript_token{#3}
          }
        }
        {\int\c_math_superscript_token {#3 \c_math_subscript_token {#2}}}
        {#4}
  }{
    \int_compare:nTF {\g_recursion_depth_int = 1}{
      \NEWINT
        {\int\c_math_subscript_token {#2}\c_math_superscript_token{1}\!x\mathrm{d}x}
        {\int\c_math_superscript_token {#3}\c_math_subscript_token{0}\!x\mathrm{d}x}
        {#4}
    }{
      \NEWINT
        {\int\c_math_subscript_token   {#2}\c_math_superscript_token{\scriptscriptstyle 1}}
        {\int\c_math_superscript_token {#3}\c_math_subscript_token{\scriptscriptstyle 0}}
        {#4}
    }
  }
}
\cs_generate_variant:Nn \_split_int_fractuation_tree:nnnn {ennn}
\makeatletter
\newcommand{\raisemath}[1]{\mathpalette{\raisem@th{#1}}}
\newcommand{\raisem@th}[3]{\raisebox{#1}{$#2#3$}}
\makeatother
\ExplSyntaxOff


\begin{document}
% \SplitList{5, 10, 25, 50}
\[
  \NEWINT{\beta}{\alpha}{20}
\]


\begin{align}
  & \int_a^b x \\
  & \int_a^{\raisemath{-10pt}{b}} x
\end{align}
\end{document}
  • l3coffin 的思路我还没有去尝试,后续我也许会换到这个方向上来试一试.
  • 关于积分上下限的位置,目前可以使用 \raisemath{}{} 这个函数
Sagittarius Rover
这家伙很懒,什么也没写!

时隔一年,一个简化版coffin递归实现可见:

\documentclass[border=5pt]{standalone}
\usepackage{fourier}
\usepackage{cmupint}
\usepackage{fixdif}
\DeclareRobustCommand{\myint}{$\displaystyle\int\nolimits$}

\begin{document}

\ExplSyntaxOn

% 基础内容盒子
\coffin_new:N \l_base_int_coffin
\hcoffin_set:Nn \l_base_int_coffin { \myint }
\coffin_new:N \l_base_dx_coffin
\hcoffin_set:Nn \l_base_dx_coffin { $x \d x$ }

% 递归用的临时变量
\coffin_new:N \l_rec_root_coffin
\coffin_new:N \l_rec_child_coffin
\coffin_new:N \l_rec_dx_coffin

% 全局变量
\coffin_new:N \g_export_tmp_coffin
\coffin_new:N \l_final_result_coffin

\cs_new_protected:Npn \typeset_integral_recursive:Nnn #1 #2 #3
  {
    \group_begin:
      % 当前层的 Root
      \coffin_set_eq:NN \l_rec_root_coffin \l_base_int_coffin
      \coffin_scale:Nnn \l_rec_root_coffin { #3 } { #3 }
      
      % 当前层的 dx 
      \coffin_set_eq:NN \l_rec_dx_coffin \l_base_dx_coffin
      \coffin_scale:Nnn \l_rec_dx_coffin { #3 } { #3 }

      \int_compare:nNnTF { #2 } = { 0 }
        {
          \coffin_join:NnnNnnnn 
              \l_rec_root_coffin { r } { vc } 
              \l_rec_dx_coffin { l } { vc } { 0pt } { 0pt }
        }
        {
          % 递归
          % 计算子层缩放比例
          \fp_set:Nn \l_tmpa_fp { #3 * 0.5 }
          % 递归生成子节点
          \typeset_integral_recursive:Nnn \l_rec_child_coffin
            { \int_eval:n { #2 - 1 } } 
            { \l_tmpa_fp }

          % 拼接子节点到 Root
          \coffin_join:NnnNnnnn 
            \l_rec_root_coffin { r } { t } 
            \l_rec_child_coffin { l } { vc } { 0pt } { 0pt }
          \coffin_join:NnnNnnnn 
            \l_rec_root_coffin { r } { b } 
            \l_rec_child_coffin { r } { vc } { 0pt } { 0pt }
          \coffin_join:NnnNnnnn 
            \l_rec_root_coffin { r } { vc } 
            \l_rec_dx_coffin { l } { vc } { 0pt } { 0pt }
        }
      \coffin_gset_eq:NN \g_export_tmp_coffin \l_rec_root_coffin
      
    \group_end:
    \coffin_set_eq:NN #1 \g_export_tmp_coffin
  }

\typeset_integral_recursive:Nnn \l_final_result_coffin { 5 } { 2.0 }

\coffin_typeset:Nnnnn \l_final_result_coffin { l } { vc } { 0pt } { 0pt }

\ExplSyntaxOff

\end{document}

image.png

其实可以不用coffin的,但是注意到整体积分上下限的位置并不好控制,进一步的不成功实践如下:

\documentclass[border=5pt,multi=tmpenv]{standalone}
\usepackage{amsmath}
\usepackage{fouriernc}
\usepackage{cmupint}
\usepackage{fixdif}
\parindent=0pt
% \DeclareMathOperator{\myint}{\int\nolimits}
\DeclareRobustCommand{\myint}{\int\nolimits}
\newcommand{\basicint}{\myint_0^1xdx}
\newcommand{\basicintt}{\myint_{\basicint}^{\basicint}xdx}
% https://tex.stackexchange.com/a/5192/322482
\makeatletter
\newcommand{\raisemath}[1]{\mathpalette{\raisem@th{#1}}}
\newcommand{\raisem@th}[3]{\raisebox{#1}{$#2#3$}}
\makeatother
\newcommand{\originintunit}{%
    \displaystyle\myint_{
    \textstyle\myint^{0}_{\basicintt}
    }^{
        \textstyle\myint_{1}^{\basicintt}
    }
}%
\newcommand{\intunit}[2]{%
    \myint_{
        \myint^{0}_{\myint_{#1}^{\basicint}xdx}
    }^{
        \myint_{1}^{\myint_{\basicint}^{#2}xdx}
    }
}
\ExplSyntaxOn
% \cs_generate_variant:Nn \tl_use:N {V}
\tl_new:N \l_explorer_base_tl
\tl_new:N \l_explorer_tmp_tl
\tl_new:N \l_explorer_script_tl
\tl_new:N \g_export_tmp_tl 

\cs_new_protected:Npn \typeset_integral_recursive:Nn #1#2
{
    % #1:tl to store
    % #2:depth
    \group_begin:
      \tl_set_eq:NN \l_explorer_tmp_tl #1
        \int_compare:nNnTF { #2 } = { 0 }
            {
               \tl_put_right:Ne \l_explorer_tmp_tl { \intunit{\basicint}{\basicint} }
            }
            {
                \tl_clear:N \l_explorer_script_tl
                \typeset_integral_recursive:Nn \l_explorer_script_tl { \int_eval:n { #2 - 1 } } 
                \tl_put_right:Ne \l_explorer_tmp_tl { \intunit{\l_explorer_script_tl}{\l_explorer_script_tl} } 
            }
        \tl_put_right:Nn \l_explorer_tmp_tl { xdx } 
        \tl_gset_eq:NN \g_export_tmp_tl \l_explorer_tmp_tl
    \group_end:
    \tl_set_eq:NN #1 \g_export_tmp_tl
}

% \typeset_integral_recursive:Nn \l_explorer_base_tl { 4 }

\NewDocumentCommand{\exploreint}{ m }{
    \begingroup
    \typeset_integral_recursive:Nn \l_explorer_base_tl { #1 }
    \begin{tmpenv}
    $\l_explorer_base_tl$
    \end{tmpenv}
    \endgroup
}
\ExplSyntaxOff

\newenvironment{tmpenv}{}{}
\begin{document}

\exploreint{0}

\exploreint{1}

\exploreint{2}

\exploreint{3}

\end{document}

image.png

image.png

撰写答案

请登录后再发布答案,点击登录

发布
问题

分享
好友

手机
浏览

扫码手机浏览