首先说明,这文章讲的多边形是简单多边形,也就是不会出现自交的多边形。其实对于简单多边形的面积计算,如果你略知一点计算几何知识,你就觉得这个会很简单。
如果给你上面的10个点的多边形,你会怎样去求它的面积?显然在初高中的学习中,我们只学习了三角形等形状的面积求法,并没有学到关于多边形面积的求法。但我们能否将求多边形的面积的问题划分成多个求三角形面积子问题的的集合呢?如果能够,我们就能够将负责的问题转化成简单的问题了。
答案是能的。但该怎么剖分?下面讲述几种方法。
第一种方法是在多边形内的一点,然后将此点与多边形顶点连线,这样就可以将多边形划分成多个三角形了。这是最容易想的剖分方法
有了这一步的剖分,我们就可以分别求出每个三角形的面积,然后加起来就是所求多边形的面积了。但是这么种做法有几个不足之处。首先第一个是求多边形内的一点不容易,上图中给的是凸包,求凸包内的一点很容易,直接计算非相邻顶点连线段的中点即可,但当遇到的是非凸多边形,而仅仅是简单多边形呢?其次算法的精度会受到取点的优异而收到影响。因为如果恰好取点在某个顶点附件的话,很可能有一些小三角形计算出来的面积被忽略掉,进而就影响了整个算法的角度。因此为了避免取点带来的不足,我们还有一种方法是避免取多边形内的点。
第二种方法是采用三角剖分的方法,取多边形的一个顶点作为剖分出的三角形的顶点,三角形的其它点为多边形上相邻的点。如下图:
这样我们就可以简单地实现计算多边形的面积了。知道三角形的3个点的坐标,我们知道三角形的面积可以通过这3个点构成的2条向量的差乘来求得,而且使用差乘求面积的有一个好处就是求出的面积值是有方向性(这点我就不解析了,你画画或者百度就可以知道为什么)。利用这点,我们可以顺利解决上图中右图的类似三角形(x0, y0), (x8, y8), (x9,y9)这3点构成的三角形的剩余问题。因为根据差乘,求出的这个三角形的面积是负数,而(x0, y0), (x7, y7), (x8, y8)这3点构成的三角形计算出的面积却又有的剩余部分,这么一正一负恰好可以抵消掉多出区域的面积。这样可以得到一个计算公式:
具体实现的代码如下:
struct
TPoint {
double
x, y;
TPoint (
double
x0 = 0,
double
y0 = 0) : x(x0), y(y0) {}
TPoint operator-(TPoint &b)
const
{
return
TPoint(x - b.x, y - b.y);
}
};
double
cross(TPoint a, TPoint b) {
return
a.x * b.y - a.y * b.x;
}
//简单多边形面积,p[]为顶点集,按顺序存储,n为多边形顶点数
double
area_poly(TPoint p[],
int
n) {
double
ret = 0.00;
for
(
int
i = 1;i < n-1;++ i)
ret += cross(p[i]-p[0], p[i+1]-p[0]);
return
fabs
(ret) / 2.00;
}
|
利用上面代码求出的多边形代码,如果多边形的顶点是按顺时针存储的,那么求得的ret就是负值,逆时钟的话就是正值,因此你还可以使用它来判断一个多边形的顶点是按逆时钟储存的还是按顺时针存储的。
其实还有一个更好的方法就是所取的点既不是在多边形内也不是多边形的顶点,而是原点。
这样的话就可以将上面的p[i]-p[0]省略,公式见化成:
具体实现的代码如下:
double
area_poly(TPoint p[],
int
n) {
p[n] = p[0];
double
ret = 0.00;
for
(
int
i = 0;i < n;++ i)
ret += cross(p[i], p[i+1]);
return
fabs
(ret) / 2.00;
}
|