格林(Green)公式告訴我們,在平面閉區域D上的二重積分可以通過沿閉區域D的邊界曲線L上的曲線積分來表達。即,設閉區域$D$由分段光滑的曲線$L$圍成,函數$P(x,y)$及$Q(x,y)$在$D$上具有一階連續偏導數,則有$$\iint_{D}(\frac{\partial Q}{\partial x}-\frac{\partial P}{\partial y})dxdy=\oint_{L}Pdx+Qdy$$其中$L$是$D$的取正向的邊界曲線。根據公式推導過程(參考高等數學同濟版下),有如下關系存在:$$-\iint_{D}\frac{\partial P}{\partial y}dxdy=\oint_{L}Pdx\\\iint_{D}\frac{\partial Q}{\partial x}dxdy=\oint_{L}Qdy $$
對於上面兩個式子,分別取$P=-y$,$Q=x$可得到:$$\begin{align}&\iint_{D}dxdy=A=-\oint_{L}ydx\\&\iint_{D}dxdy=A=\oint_{L}xdy\end{align}$$
其中,$A$為閉區域$D$的面積。根據式(1)和(2)也可得出:$$A=\frac{1}{2}\oint_{L}xdy-ydx$$
根據公式(2)來計算多邊形面積,參考下面的示意圖,曲線$L$由多段光滑的直線段$L_i$連接而成。由於$L$是分段光滑的,則有向曲線$L$上對坐標的曲線積分等於在光滑的各段上對坐標的曲線積分之和。即$A=\oint_Lxdy=\int_{L_0}xdy+...+\int_{L_6}xdy$

直線段$L_i$從點$(x_i,y_i)$到點$(x_{i+1},y_{i+1})$的參數方程為$L_i(t)=\left ((x_{i+1}-x_i)t+x_i,(y_{i+1}-y_i)t+y_i\right),\quad 0\leq t\leq 1$,於是對線段$L_i$的坐標積分為:$$\begin{aligned}\int_{Li}xdy&=\int_0^1((x_{i+1}-x_i)t+x_i)(y_{i+1}-y_i)dt\\&=(y_{i+1}-y_i)(x_it+\frac{1}{2}t^2(x_{i+1}-x_i))\big|_{0}^{1}\\&=\frac{1}{2}(x_{i+1}+x_i)(y_{i+1}-y_i)\end{aligned}$$
將每段結果相加得到多邊形的面積為:$$\boxed{A=\sum_{i=0}^{n}\frac{(x_{i+1}+x_i)(y_{i+1}-y_i)}{2}}$$
其中,$n$為多邊形頂點數-1,$(x_{n+1},y_{n+1})=(x_0,y_0)$。類似的,根據公式(1)可計算出多邊形的面積為:$$\boxed{A=\sum_{i=0}^{n}\frac{(x_i-x_{i+1})(y_i+y_{i+1})}{2}}$$
多邊形面積和頂點數組的順-逆時針方向可以根據上面的公式計算,即如果是逆時針($L$為正向),則計算出的面積為正值;如果是順時針,計算出的面積為負值。可參考下面的代碼:
class Point: def __init__(self, x, y): self.x = x self.y = y def isCounterclockwise(points): s = 0 points_count = len(points) for i in range(points_count): point = points[i] point2 = points[(i + 1) % points_count] s += (point.x - point2.x) * (point.y + point2.y) return s > 0 def poly_area(points): s = 0 points_count = len(points) for i in range(points_count): point = points[i] point2 = points[(i + 1) % points_count] s += (point.x - point2.x) * (point.y + point2.y) return abs(s/2) if __name__ == "__main__": polygon1 = [Point(0,0), Point(0.5,0), Point(0.5,0.5), Point(0,1)] # counter-clockwise polygon2 = polygon1[::-1] # clockwise print(isCounterclockwise(polygon1), isCounterclockwise(polygon2)) # True, False print(poly_area(polygon1)) # 0.375
參考:
