
上篇我们把一块带圆弧的多边形面积算了出来做法是先找到一条上边界和一条下边界然后对上减下做定积分。当时就有读者留言要是图形中间有个洞怎么办这问题问得特别实在因为工程图里的截面、零件轮廓十个里有八个带孔、带槽、带缺口。一旦有了洞竖着切一刀同一根x上会同时出现四个边界点——外下、外上、孔下、孔上。这个时候原来的“上下两条函数”直接失效y和x的关系变成了多值。这篇文章就专门处理这种“多值函数的情况”主角是一块圆角矩形外框加圆角矩形内孔的截面我会把直线段和圆弧段逐段用格林公式算一遍最后再给出一段能直接写进脚本的伪代码。1. 为什么会出现多值从“两条曲线”到“一堆曲线”1.1 上篇的单值套路还能用吗上篇文章里的图形比如普通的圆角矩形、半圆键槽都能在x方向上找到一段连续的投影。竖线从下往上穿过区域只会碰到两次边界一次进一次出。因此面积可以写成[ S\int_a^b [y_{\text{上}}(x)-y_{\text{下}}(x)],dx ]这个公式用起来很顺手只要把边界函数拆成几段显式函数就行。但是给这个圆角矩形加上一个内孔之后事情就变了。在孔的x坐标范围内竖线会碰到四次边界外框下边界、孔下边界、孔上边界、外框上边界。也就是说同一个x对应了四个y值区域在竖直方向上被分成了两个互不相连的区间。面积如果还按“上减下”来写就要在孔的范围里额外减去一个内孔的积分等于要手动判断哪些x区间有孔、哪些没有。图形简单时还好一旦孔的位置偏心、孔的轮廓也是圆弧多边形写出来的分段表达式会非常痛苦。1.2 多值不只是“有孔”这一种情况除了带内孔还有几种常见情况也会造成多值。比如一块C形截面的板边界在x方向有重叠竖线穿过去会碰到四个甚至六个边界点再比如带有悬臂凸台的复杂轮廓同一x下可能出现多个独立区间。这些图形的共同特点是不能用“两个显式函数”完整描述全部截面范围。遇到这类问题我一般不再纠结怎么分段写积分而是直接换工具——用格林公式把面积计算转化成边界积分。这个思路在工程软件里非常普遍因为无论轮廓多复杂只要你能把边界拆成直线段和圆弧段逐段算积分就行完全不用管内部怎么分叉。2. 核心方法格林公式与两类边界的参数化2.1 面积等于边界积分格林公式把平面区域的二重积分和边界曲线积分联系起来。对于面积我们关心的是这个经典形式[ S\frac{1}{2}\oint_C (x,dy-y,dx) ]这里的边界方向有要求外边界逆时针内边界顺时针。这样规定之后整个积分值除以2就正好等于“外边界围成的面积减去内孔面积”。也就是说多连通的复杂截面只需要把外边界和内边界各自的积分加起来就能直接得到净面积不需要显式分段。我最早学这个公式时觉得它就是个理论定理直到做CAD二次开发算截面参数才发现它才是面积计算的“万能钥匙”。代码里只需要一个函数算直线段、一个函数算圆弧段然后按顺序把轮廓逐段遍历一遍结果自然出来。2.2 直线段的积分公式设有向线段从点P0(x0,y0)到点P1(x1,y1)参数化写成[ xx_0t\Delta x,\quad yy_0t\Delta y,\quad \Delta xx_1-x_0,\ \Delta yy_1-y_0 ]其中t从0到1。代入被积表达式[ x,dy-y,dx(x_0t\Delta x)\Delta y,dt-(y_0t\Delta y)\Delta x,dt ]展开之后含有t的交叉项刚好抵消只剩下常数项[ I_{\text{line}}(x_0\Delta y-y_0\Delta x)\int_0^1 dtx_0\Delta y-y_0\Delta x ]这个结果特别适合编程只要传入起点和终点一行代码就算完。2.3 圆弧段的积分公式与圆心偏置项圆弧段的参数化同样简单。设圆心为(a,b)半径为r参数角为θ则[ xar\cos\theta,\quad ybr\sin\theta ]注意这里的θ是从圆心正右方开始、逆时针增大的角度。代入被积表达式[ x,dy-y,dx\big[r^2r(a\cos\thetab\sin\theta)\big]d\theta ]所以圆弧段的积分是[ I_{\text{arc}}\int_{\theta_1}^{\theta_2}\big[r^2r(a\cos\thetab\sin\theta)\big]d\theta r^2(\theta_2-\theta_1)r\left[a(\sin\theta_2-\sin\theta_1)-b(\cos\theta_2-\cos\theta_1)\right] ]这里特别容易踩坑的是“圆心偏置项” r(a cosθb sinθ)。很多教材只在原点圆上推导得到x dy - y dx r² dθ于是拿到非原点圆弧上直接套用结果偏得离谱。我用一个类比来解释r²dθ是圆弧绕自身圆心扫过的扇面贡献而偏置项描述的是整个圆心绕坐标系原点旋转时圆弧作为一个整体扫过的额外面积。圆心不在原点时这两部分必须同时算缺一不可。3. 实操计算圆角矩形外框加圆角矩形内孔3.1 图形参数与解析面积为了让计算可以逐项核对我选一个完全能解析验证的图形。外框中心(5,3)宽度10高度6四角圆角半径1。内孔中心(5,3)宽度4高度2四角圆角半径0.5。外框面积可以直接用几何关系算中间矩形面积10×660四个角各被圆角削掉一块。每个角从方角变成半径1的圆角后净减量为[ \rho^2-\frac{\pi\rho^2}{4}1-\frac{\pi}{4} ]四个角合计减少4-π。所以外框面积是[ S_{\text{out}}60-(4-\pi)56\pi\approx 59.1416 ]同理内孔面积[ S_{\text{in}}4\times2-\left(4-\pi\right)\times0.257\frac{\pi}{4}\approx 7.7854 ]这里的0.25来自圆角半径0.5的平方。最终净面积[ S_{\text{net}}56\pi-\left(7\frac{\pi}{4}\right)49\frac{3\pi}{4}\approx 51.3562 ]后面所有积分结果都会跟这个值对照。3.2 外边界逐段积分外边界按逆时针方向走从底部直线左端点(1,0)开始。我把每一段列成表格积分值指的就是∫(x dy - y dx)在该段上的贡献。先看两段代表性的计算过程。底部直线段从(1,0)到(9,0)Δx8Δy0。套直线公式[ I1\times0-0\times80 ]这个结果跟直觉一致因为整条线段在x轴上dy0且y0被积表达式恒为0。右下圆角段是四分之一圆弧圆心(9,1)半径1参数角θ从-90°到0°。代入圆弧公式[ Ir^2(\theta_2-\theta_1)r\left[a(\sin\theta_2-\sin\theta_1)-b(\cos\theta_2-\cos\theta_1)\right] ]先算第一项1×(0-(-π/2))π/2。再算偏置项1×[9×(0-(-1))-1×(1-0)]8。合计为π/28≈9.5708。其余各段按相同方法计算结果如下表段号内容起止点/起止角积分值1底部直线(1,0)→(9,0)02右下圆角θ:-π/2→0π/283右侧竖直线(10,1)→(10,5)404右上圆角θ:0→π/2π/2145顶部直线(9,6)→(1,6)486左上圆角θ:π/2→ππ/247左侧竖直线(0,5)→(0,1)08左下圆角θ:π→3π/2π/2-2外边界合计[ I_{\text{out}}1122\pi ]除以2得到56π≈59.1416跟解析面积完全一致。3.3 内边界逐段积分内孔圆角矩形的四个顶点范围是x从3到7y从2到4圆角半径0.5。它的边界仍然由四条直线和四段四分之一圆弧组成。计算时我先按逆时针方向算出每段积分最后整体取反作为顺时针贡献。这里以右下圆角为例演示。右下圆角在孔的内侧圆心(6.5,2.5)半径0.5θ从-90°到0°。代入公式第一项0.25×(0-(-π/2))π/8。偏置项0.5×[6.5×(0-(-1))-2.5×(1-0)]2。合计π/82≈2.3927。所有内边界段按逆时针计算结果如下段号内容起点→终点/起止角积分值1底边直线(3.5,2)→(6.5,2)-62右下圆角θ:-π/2→0π/823右侧竖直线(7,2.5)→(7,3.5)74右上圆角θ:0→π/2π/855顶边直线(6.5,4)→(3.5,4)126左上圆角θ:π/2→ππ/87左侧竖直线(3,3.5)→(3,2.5)-38左下圆角θ:π→3π/2π/8-3内边界逆时针总积分[ I_{\text{in,ccw}}14\frac{\pi}{2} ]它除以2应该等于内孔面积7π/4验证无误。因此在外逆内顺的约定下内边界对总积分的贡献是负值[ I_{\text{in,hole}}-\left(14\frac{\pi}{2}\right) ]3.4 汇总与验证总积分等于外边界加内边界的贡献[ I_{\text{total}}(1122\pi)-\left(14\frac{\pi}{2}\right)98\frac{3\pi}{2} ]面积[ S\frac{I_{\text{total}}}{2}49\frac{3\pi}{4}\approx 51.3562 ]与解析结果完全一致。这说明只要边界方向正确、每一段的参数起止角正确格林公式就能自动完成“外减内”的运算全程不用管多值分段。3.5 可以直接落地的伪代码实际的工程图形可能有几十条边手算不现实。我一般会把计算逻辑写成下面这样的通用流程import math def line_integral(p0, p1): x0, y0 p0 x1, y1 p1 return x0 * (y1 - y0) - y0 * (x1 - x0) def arc_integral(cx, cy, r, theta_start, theta_end): # 角度全部使用弧度制 dt theta_end - theta_start return (r * r * dt r * (cx * (math.sin(theta_end) - math.sin(theta_start)) - cy * (math.cos(theta_end) - math.cos(theta_start)))) def polygon_area(outer_edges, inner_edges_list): # outer_edges: 外边界逆时针每条边为 (line, p0, p1) 或 (arc, cx, cy, r, th0, th1) total 0.0 for edge in outer_edges: if edge[0] line: total line_integral(edge[1], edge[2]) else: _, cx, cy, r, th0, th1 edge total arc_integral(cx, cy, r, th0, th1) for inner_edges in inner_edges_list: for edge in inner_edges: if edge[0] line: total - line_integral(edge[1], edge[2]) else: _, cx, cy, r, th0, th1 edge total - arc_integral(cx, cy, r, th0, th1) return total / 2.0这段代码的核心思想是所有内边界在传入时仍然是逆时针顺序计算时统一取负号代表顺时针贡献。这样不管截面里有几个孔只需要传入一个内边界的列表最后面积自然是对的。4. 常见问题与排查心得4.1 方向错是第一位的问题我见过最多的错误是内孔方向弄反。如果内边界也按逆时针积分总积分就变成外框面积加上内孔面积最终算出来的面积比真实值多一个内孔。这个错误特别隐蔽因为数值看起来仍然很“合理”如果没有解析值对照很难一眼发现。排查方法很简单检查每一圈的净面积是否为正。外边界逆时针积分除以2应该是正的而且约等于外圈面积内孔逆时针积分除以2也是正的但代码里处理时要加负号。调试时可以先把总积分打印出来看看期望符号。4.2 圆弧参数角度的起点和方向圆弧公式里的θ是从圆心正右方开始、逆时针增大的。很多人把圆角切点的坐标当成θ本身或者把θ的范围写反导致积分值不对。我自己的习惯是每条圆弧段记录圆心、半径、起始角度、结束角度然后单独画一张草图核对角度区间。比如右下角圆角从底部切点(6.5,2)走到右侧切点(7,2.5)对应θ从-90°到0°方向是逆时针增大。如果写成了从0到-90°积分结果就会变号或者变成另一段大弧。4.3 不能忽略圆心偏置项圆心不在原点时圆弧积分里除了r²Δθ还有偏置项r(a(sinθ2-sinθ1)-b(cosθ2-cosθ1))。这句我在实操中反复强调因为很多人套公式时只写r²Δθ然后发现结果怎么对不上。尤其圆心离原点远、圆弧半径小的时候偏置项占比很大漏掉它误差是数量级的。4.4 关于“多边形”的通用化理解标题里说的是带圆弧多边形实际上这个套路能覆盖的范围更大直线段可以看成圆弧半径为无穷大的特例圆弧段也可以推广到任意参数曲线。工程上很多复杂截面其实都是直线和圆弧的拼接所以这两类积分的组合已经够用。如果遇到样条曲线通常的做法是先离散成微小直线段再调用直线段积分精度由离散密度控制。5. 延伸思路多孔、凹槽和任意圆弧多边形5.1 多孔情况的统一处理方式如果截面里有两个孔、三个孔处理方式完全一样。把所有外边界按逆时针逐段积分把所有孔的边界按顺时针逐段积分最后统一相加除以2。内孔之间互相不重叠时这个公式直接成立即使孔的位置偏心、形状不规则只要每条边都是直线或圆弧计算流程不需要改。我在某个模拟项目里处理过一块包含两个圆角矩形孔和一个圆孔的安装板截面总共30多条边界段。逐段手算完全不可能但写成代码后只需要维护一个边的列表。这让面积计算从“每换一张图就要重新推导积分式”变成“数据准备活了结果自动出来”。5.2 C形凹槽也能这样算有一种很常见的图形矩形板中间挖掉一个C形凹槽导致截面在x方向上出现悬垂。它同样会形成多值而且比内孔更麻烦因为凹槽边界可能和外边界相交出尖点。但只要把整个外轮廓沿边界走一圈遇到凹槽就绕进去再绕出来整体仍然是一个闭合的有向轮廓格林公式依然成立。这就是边界积分相对“上下函数法”最核心的优势不关心区域在某个方向上是否多值只关心边界在哪、方向对不对。5.3 学习和工作的位置互换这个系列标题叫“让微积分穿梭于工作与学习之间”我自己的感受是学校里学格林公式时总觉得它是抽象的第二类曲线积分工作后处理截面参数才发现它就是工程几何计算的通用底盘。反过来工作中积累的边界离散化、方向约定、避坑经验也会让人回头理解格林公式时多了很多直观感受。如果以后再看到复杂的带圆弧多边形我建议先别急着找上下函数。把图形拆成直线段和圆弧段确认好方向和参数角剩下的交给公式和代码。这就是多值函数情况下的标准解法。最后分享一个实用经验每次算完面积我都会顺手用解析几何的粗略面积估算一次比如用外接矩形减掉明显可见的缺口和孔洞。这种量级检查只需要几秒钟但能拦住大部分方向错误和参数错误。毕竟公式算出来的数再漂亮也得经得起常识的检验。