用所有多边形的边和X轴围成的梯形的面积累加起来就可以了。例如某条边 ( P1 - P2 ) 和在它在 x 轴的投影 ( p1’ - P2’ ) 所围成的梯形面积是:
S [ p1->p2 ] = 1/2 * ( y1 + y2 ) * ( x2 - x1 ) ; ----(I)
这个式子拆开后就是:
S [ p1->p2 ] = 1/2 * ( -x1 * y1 + x2 * y2 - x1 * y2 + x2 * y1 ) ; ---- ( 注:蓝色部分的 xi * yi 累加后被消掉 )
由于每个边都被累加一遍,每个点参与了组成边两次(前一条边的终点和后一条边的起始点),所以每个顶点自身坐标的乘积 ( xi * yi ) 累加的时候都被消掉了!最后就是累加所有的式(I):
Area = ∑ 1/2 * ( -x( i ) * y( i + 1 ) + x( i + 1 ) * y( i ) ) ;
----( i = 1,2,…,n,且P(n+1) = P(0) ); ----(II)
上面的式子,括号内是两个矢量的叉积 P[ i + 1 ] × P[ i ]。因此式II实际上是依次累加相邻点的叉积,最后在除以2。考虑叉积的含义,是以两个矢量为相邻边的平行四边形(由 O(0,0), P1, P2, (P1 + P2) 四个点组成)的面积(在平面坐标中用三角形面积相减可以很容易推导出该含义),因此这个方法的物理意义就非常明显了,我们可以直观的假定原点位于多边形内部,显然依次把相邻点和原点围成的三角形的面积累加起来就是这些点组成的多边形的面积。因此 1/2 ( P[ i + 1 ] X P[ i ] ) 就是三角形 O - P[ i + 1 ] - P[ i ] 的面积(平行四边形面积的一半,O 表示原点)。当然,由于叉积有符号,所以原点位于多边形外部也成立,只是在内部更直观,更容易理解。
备注:我们可以把存储顶点的数组扩充一个元素,然后把 P( n+1 ) 赋值为 P( 0 ),这样我们对上式的循环中就不必对顶点索引使用取模运算(MOD)。我自己写的代码采用的(I)这种计算方法,我参考的代码采用(II)中的计算方法,两者计算量相差不大,但后者不太直观,所以我就稍微解释了下两者本质是相同的。
注意式(I)中的梯形面积是有符号的,和 P1,P2 的点顺序相关( S[ p1->p2 ] = -S[ p2->p1 ] ) ,因此 Area 的符号和顶点顺序有关,所以最后别忘了对计算结果求绝对值。
判断两线段相交
(ab × ad)*(bc × bd) < 0
(ca × cb)*(da × db) < 0
判断a,b,c是否有重合的点
ab × ac = 0 且 ca cb =0
#include <stdio.h>
using namespace std;
double det(double x1, double y1, double x2, double y2, double x3, double y3){
return x1*y2 + x2*y3 + x3*y1 - x2*y1 - x3*y2 - x1*y3;
}
int main(){
int n, i, j, temp;
int count =0;
double x[1000], y[1000], area;
while(1){
scanf("%d", &n);
if(n == 0) return 0;
count++;
if(count > 1) printf("\n");
printf("Figuer %d: ", count);
for(i = 0; i < n; i++) scanf("%lf%lf", &x[i], &y[i]);
if(n < 3){
printf("Impossible\n");
continue;
}
for(i=0;i<n;i++){
for(j=(i+1)/n;j<i-1;j++){
temp=(i+1)%n;
if (det(x[j],y[j],x[i],y[i],x[j+1],y[j+1])*det(x[j],y[j],x[temp],y[temp],x[j+1],y[j+1])<=0&&
det(x[i],y[i],x[j],y[j],x[temp],y[temp])*det(x[i],y[i],x[j+1],y[j+1],x[temp],y[temp])<=0) {
printf("Impossible\n");
goto quit;
}
}
}
area=0;
for(i=0;i<n;i++){
area+=x[i]*y[(i+1)%n];
area-=y[i]*x[(i+1)%n];
}
if (area<0) area*=-1;
printf("%.2lf\n",area/2);
quit:;
}
}