1. 寫好程式的介面,只要能夠通過編譯即可
2. 增加測試
3. 執行測試,如果有失敗的測試的話就修改程式,然後回到步驟2
回到我們目前的IsQuadrilateral函數,目前的實作非常簡單,只是檢查傳入的多邊形是否有四個頂點,若是的話就傳回真。現在要來處理其他狀況:長的像四角形的五角形(或更多角)。解法如前篇所說,我們先取多邊形的第一個點做為p0,然後找出最遠的點p1,接著找離p1最遠的點p0。我們把這件工作交給FindFarthestPoint,此函數接受一個點p和一個集合S,傳回在集合S中離p最遠的點。這時我們依照TDD的精神,加上個測試先。testFindFarthestPoint利用我們之前定義好的square,測試離左下角最遠的點是否為右上角。
# 要放到 class TestSequenceFunctions 裡面
def testFindFarthestPoint(self):
self.assertTrue( numpy.array_equal(FindFarthestPoint(numpy.array([0,0]), self.square)[0], numpy.array([1,1])) )
self.assertTrue( numpy.array_equal(FindFarthestPoint(numpy.array([0,2]), self.square)[0], numpy.array([1,0])) )
def FindFarthestPoint(p, polygon):
x = polygon[0,]-p[0]
y = polygon[1,]-p[1]
idx = numpy.argmax(x**2 + y**2)
return polygon[:,idx], idx
好了,雖然繞了些路,終於萬事俱備,可以來寫判斷三角形的函數了。由於我們已經把多邊形切成兩半,各自是兩群連續的點的集合。我們可以想像從p1p2的連線,如果途中有一個凸出的點,p1到p2就會是一個三角形(或者更多角)。我使用CountMiddleVertex來計算一群連續的點之中有幾個凸出的點。這會是一個遞迴函數,先找到最突出的點p3,然後用p3切割線段,然後分別呼叫CountMiddleVertex繼續計算。
# 要放到 class TestSequenceFunctions 裡面
def testCountVertex(self):
self.assertEqual(CountMiddleVertex(self.square2), 1)
self.assertEqual(CountMiddleVertex(self.triangle), 1)
self.assertEqual(CountMiddleVertex(self.square), 2)
self.assertEqual(CountMiddleVertex(self.pentagon1), 3)
self.assertEqual(CountMiddleVertex(self.pentagon2), 3)
self.assertEqual(CountMiddleVertex(self.hexagon), 4)
def CountMiddleVertex(pointset, threshold=0.03):
pointnum = pointset.shape[1]
if pointnum <=2:
return 0
p1 = pointset[:, 0]
p2 = pointset[:, -1]
dist2 = (p1[0]-p2[0])*(p1[0]-p2[0]) + (p1[1]-p2[1])*(p1[1]-p2[1])
triarea = []
for p in range(pointset.shape[1]):
triarea.append(PolygonArea(numpy.column_stack((p1,p2,pointset[:,p]))))
# 如果最突出的點超過門檻值
if max(triarea)*1.0/dist2 > threshold:
midpoint = triarea.index(max(triarea))
vertexnum = 1 + CountMiddleVertex(pointset[:, 0:midpoint+1])
+ CountMiddleVertex(pointset[:, midpoint:], maxnum-vertexnum);
return 0
CountMiddleVertex測試完成後,我們就利用它來判定三角形吧。更新後的IsQuadrilateral以及完整程式如下。為了讓CountMiddleVertex執行快一些,我加了一個參數maxnum,讓它可以早點結束。
import numpy
import unittest
def PolygonArea(polygon):
x,y = numpy.column_stack((polygon, numpy.array([polygon[0,0],polygon[1,0]])))
return abs((numpy.dot(x[0:-1],y[1:]) - numpy.dot(x[1:],y[0:-1])))/2.0
def FindFarthestPoint(p, polygon):
x = polygon[0,]-p[0]
y = polygon[1,]-p[1]
idx = numpy.argmax(x**2 + y**2)
return polygon[:,idx], idx
def CountMiddleVertex(pointset, maxnum=10, threshold=0.03):
pointnum = pointset.shape[1]
if pointnum <=2:
return 0
p1 = pointset[:, 0]
p2 = pointset[:, -1]
dist2 = (p1[0]-p2[0])*(p1[0]-p2[0]) + (p1[1]-p2[1])*(p1[1]-p2[1])
triarea = []
for p in range(pointset.shape[1]):
triarea.append(PolygonArea(numpy.column_stack((p1,p2,pointset[:,p]))))
if max(triarea)*1.0/dist2 > threshold:
midpoint = triarea.index(max(triarea))
vertexnum = 1 + CountMiddleVertex(pointset[:, 0:midpoint+1]);
if vertexnum >= maxnum:
return maxnum
return vertexnum + CountMiddleVertex(pointset[:, midpoint:], maxnum-vertexnum);
return 0
def IsQuadrilateral(polygon):
x,y = polygon
if len(x) < 4 or len(x)!=len(y):
return False
p1, p1idx = FindFarthestPoint(polygon[:,0], polygon)
p2, p2idx = FindFarthestPoint(p1, polygon)
if p1idx > p2idx:
p1idx,p2idx = p2idx,p1idx
p1,p2 = p2,p1
if CountMiddleVertex(polygon[:, p1idx:p2idx+1], 2)==1 and \
CountMiddleVertex(numpy.column_stack((polygon[:, p2idx:], polygon[:, :p1idx+1])), 2)==1:
return True
else:
return False
class TestSequenceFunctions(unittest.TestCase):
def setUp(self):
self.triangle = numpy.array( [ (0,10,0),(0,0,10) ] )
self.square = numpy.array( [ (0,1,1,0),(0,0,1,1) ] )
self.square2 = numpy.array( [ (0,0.5,1,0),(0,-0.01,0,1) ] )
self.pentagon1 = numpy.array( [ (0,1,1,0,-1),(0,0,1,1,0.5) ] )
self.pentagon2 = numpy.array( [ (0,1,1,0,-0.01),(0,0,1,1,0.5) ] )
self.pentagon3 = numpy.array( [ (0,1,1,0,-0.21),(0,0,1,1,0.5) ] )
self.hexagon = numpy.array( [ (1,1,0,-1,-1,0),(1,-1,-2,-1,1,2) ] )
def testFindFarthestPoint(self):
self.assertTrue( numpy.array_equal(FindFarthestPoint(numpy.array([0,0]), self.square)[0], numpy.array([1,1])) )
self.assertTrue( numpy.array_equal(FindFarthestPoint(numpy.array([0,2]), self.square)[0], numpy.array([1,0])) )
def testCountVertex(self):
self.assertEqual(CountMiddleVertex(self.square2), 1)
self.assertEqual(CountMiddleVertex(self.triangle), 1)
self.assertEqual(CountMiddleVertex(self.square), 2)
self.assertEqual(CountMiddleVertex(self.pentagon1), 3)
self.assertEqual(CountMiddleVertex(self.pentagon2), 3)
self.assertEqual(CountMiddleVertex(self.hexagon), 4)
def testIsQuadrilateral(self):
self.assertEqual(IsQuadrilateral(self.triangle), False)
self.assertEqual(IsQuadrilateral(self.square), True)
self.assertEqual(IsQuadrilateral(self.pentagon1), False)
self.assertEqual(IsQuadrilateral(self.pentagon2), True)
self.assertEqual(IsQuadrilateral(self.pentagon3), False)
self.assertEqual(IsQuadrilateral(self.hexagon), False)
if __name__ == '__main__':
unittest.main()
No comments:
Post a Comment