1.前言
计算轮廓面积是常见的几何算法话题,获取轮廓面积、计算轮廓法线等场景会涉及到。计算轮廓面积的方法有很多,一种常用的是微积分思路的分段求和办法,即组成轮廓的每条线段与X轴或Y轴进行有向投影,轮廓边线与X轴或Y轴的投影之和即为轮廓的有向面积。
上图中轮廓共有7条边,每条边与Y轴投影,投影面积有向,每个投影均为梯形,计算公式为:
double areaThis = (abs(d0X) + abs(d1X)) * (d1Y - d0Y) * 0.5;
其中第0、1、6的投影为正,第2、3、4、5的投影为负,累积即为轮廓面积(绿色填充部分)。
如上所示,如果关心面积的正负时,一般将轮廓移到第一或第四象限再求面积。满足上述条件时,逆时针轮廓的面积为正,顺时针轮廓的面积为负。
2.问题
在调试问题时发现上图中的轮廓面积计算为负,摸不着头脑 !=-=,好吧,那就调试,发现此轮廓有如下特点:
- X坐标很大;
- 同时边线之间并不是“紧密连接”的,而是在一定误差内连接;
- 边线都比较短;
2.1. 问题分析
正常来说该轮廓面积为正,且值比较小,为什么会计算错误呢?
轮廓的边没有紧密连接,之间是有缝隙的,也就是如果紧密连接的话需要再加很短的线,轮廓的“准确”面积应该要包括要“紧密”连接而添加的短线与坐标轴的有向投影面积。
加之该轮廓X坐标值很大,上述系列短线与Y轴投影面积值(梯形)加起来就比较大了,影响到了最终的面积和的值,甚至影响了面积和的符号!
3. 解决办法
3.1. 方案一
将轮廓偏置到贴近Y轴位置,再进行面积计算,这样导致该问题的的“短线”围成的面积会很小,不足以影响轮廓边线“不紧密连接”导致的误差。
这种办法是一种中和兼容的处理办法,兼容轮廓边线可能不紧密连接的情况,同时也需要计算轮廓的包围盒最小点,以将轮廓偏置到贴近Y轴的第一或第四象限,也增加了计算的耗时。
3.2. 方案二
处理导致此问题的轮廓,在生成轮廓或计算面积前将轮廓边线紧密连接。
4. 写在后面
double GeometryUtils::GetArea(const list<Line>& polygon, const Transform& trsW2L)
{
// 目前只支持边为线段的轮廓
double dArea = 0;
for(auto curve : polygon)
{
Vector3f pt0, pt1;
Transform::MultPoint(trsW2L, curve.pt0, pt0);
Transform::MultPoint(trsW2L, curve.pt1, pt1);
// 保证轮廓在第一/四象限,这样逆时针轮廓的面积为正
double d0X = pt0.X + 100000.0;
double d0Y = pt0.Y + 100000.0;
double d1X = pt1.X + 100000.0;
double d1Y = pt1.Y + 100000.0;
double areaThis = (abs(d0X) + abs(d1X)) * (d1Y - d0Y) * 0.5;
dArea += areaThis;
}
return dArea;
}
根据需要进行方案的选取,博主采用了方案二,具体是在生成轮廓的函数中进行了处理保证轮廓边线“紧密连接”,这有利于规避由“不紧密连接”带来的很多问题,降低问题处理的复杂度,同时减少了兼容处理过程,也可以节省了时间。
欢迎交流:公众号:geometrylib