[从头学数学] 第273节 [计算几何] 多边形求交集

本文探讨了在计算几何中如何求解两个多边形的交集问题。通过分析避免了通过顶点判断的方法,转而直接记录相交边和内部边。提到了路径连通算法的潜在缺陷,并指出在某些情况下,共享边是否被视为交集取决于具体应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

剧情提要:
阿伟看到了一本比较有趣的书,是关于《计算几何》的,2008年由北清派出版。很好奇
它里面讲了些什么,就来看看啦。


正剧开始:
星历2016年09月23日 11:58:19, 银河系厄尔斯星球中华帝国江南行省。

[工程师阿伟]正在和[机器小伟]一起研究[计算几何]]。




两个多边形求交集,比如下图:



最初很容易想到通过顶点在另一个多边形里的位置来判断。


<span style="font-size:18px;">#
class Polygon():
    #格式:path = [[-6, 9], [8, -7], [8, 3], [-6, 9]]
    def __init__(self, path):
        if (path[-1] != path[0]):
            path.append(path[0]);

        self.path = path;
        #顶点数量
        self.vertNum = len(self.path)-1;
        self.vertex = [];
        self.edge = [];

        self.vertexCalc();
        self.edgeCalc();

    def vertexCalc(self):
        for i in range(self.vertNum):
            self.vertex.append(Point(self.path[i]));

    def getVertex(self):
        return self.vertex;

    def edgeCalc(self):
        for i in range(self.vertNum):
            self.edge.append(SegLine(self.path[i], self.path[i+1]));

    def getEdge(self):
        return self.edge;

    #传入的是[x, y]格式的对象
    def pointInPolygon(self, point):
        #测试点左右两边截多边形的各边,得到的交点数都为奇数,说明测试点在多边形内
        #反之在外面
        oddNodes = False;

        #点在多边形的边上
        if (Point(point) in self.vertex):
            return 0;

        x, y = point[0], point[1];

        j = self.vertNum - 1;

        for i in range(self.vertNum):
            p1 = self.vertex[i].value();
            p2 = self.vertex[j].value();
            px1, py1 = p1[0], p1[1];
            px2, py2 = p2[0], p2[1];
            
            if (((py1 < y and py2 >= y) or (py2 < y and py1 >= y)) and \
               (px1 <= x or px2 <= x)):
                if ((px1+(y-py1)/(py2-py1)*(px2-px1)) < x):
                    oddNodes = not oddNodes;

            j = i;
        if (oddNodes == False):
            #点不在多边形内
            return -1;
        else:
            #点在多边形内
            return 1;

    #两个多边形的交集区域
    def intersection(self, other):
        vlist_1 = self.vertex;
        vlist_2 = other.vertex;        

        commonVert = set();

        for i in range(len(vlist_1)):
            #点在多边形外部是-1, 在边上是0, 在内部是1
            if other.pointInPolygon(vlist_1[i].value()) != -1:
                commonVert.add(vlist_1[i]);

        for i in range(len(vlist_2)):
            if self.pointInPolygon(vlist_2[i].value()) != -1:
                commonVert.add(vlist_2[i]);

        commonVert = list(commonVert);

        for i in range(len(commonVert)):
            commonVert[i] = commonVert[i].value();

        #返回顶点序列
        return commonVert;
        

    #把给定的坐标点阵列数组[x, y],...按照距离它们的中心点的角度进行排列
    #是为了把无序排列的闭合曲线上的点进行有序排列,后续可再经过连线形成
    #可填充的闭合曲线
    def angularSort(self, array):
        len0 = len(array);
        len1 = len(array[0]);

        if (len0 <= 0 or len1 != 2):
            return array;

        xTotal = yTotal = xCenter = yCenter = 0;

        for i in range(len0):
            xTotal += array[i][0];
            yTotal += array[i][1];

        xCenter = xTotal/len0;
        yCenter = yTotal/len0;

        x = y = xdiff = ydiff = 0;
        arrayB = [];
        
        for i in range(len0):
            x = array[i][0];
            y = array[i][1];
            xdiff = x - xCenter;
            ydiff = y - yCenter;

            if (abs(xdiff) < 1e-4):
                if (ydiff > 0):
                    arrayB.append([x, y, math.pi/2]);
                else:
                    arrayB.append([x, y, math.pi/2*3]);

            elif (xdiff >= 0 and ydiff > 0):
                #第一象限
                arrayB.append([x, y, math.atan(abs(ydiff/xdiff))]);
            elif (xdiff < 0 and ydiff >= 0):
                #第二象限
                arrayB.append([x, y, math.pi-math.atan(abs(ydiff/xdiff))]);
            elif (xdiff <= 0 and ydiff < 0):
                #第三象限
                arrayB.append([x, y, math.pi+math.atan(abs(ydiff/xdiff))]);
            else:
                #第四象限
                arrayB.append([x, y, math.pi*2-math.atan(abs(ydiff/xdiff))]);

        arrayB = sorted(arrayB, key = lambda a:(a[2], (a[0]-xCenter)**2+(a[1]-yCenter)**2));

        retArray = [];

        for i in range(len(arrayB)):
            retArray.append(arrayB[i][:-1]);

        return retArray;

>>> 
20
[]
[[4, -1], [3.56, -0.67], [6, -3]]
[[4, -1], [3.6, -0.6], [3.56, -0.67]]
[]
[]
[]
[[4, -1], [3.6, -0.6]]
[[4, -1], [3.6, -0.6]]
[[4, -1], [3.6, -0.6]]
[[4, -1], [1.5, 1.5], [3.6, -0.6]]
[[4, -1], [1.5, 1.5], [3.6, -0.6]]
[[4, -1], [1.5, 1.5], [3.6, -0.6]]
[[4, -1]]
[[4, -1], [3.08, -1.38], [3.33, -1.67], [3.56, -0.67]]
[[4, -1], [3.56, -0.67], [1.33, 1]]
[[3.56, -0.67], [1.5, 1.5], [3.6, -0.6]]
[[4, -1], [1.33, 1], [3.56, -0.67]]
[[1.33, 1], [3.56, -0.67]]
[]
[[3.56, -0.67]]
20
[[], [[4, -1], [3.56, -0.67], [6, -3]], [[4, -1], [3.6, -0.6], [3.56, -0.67]], [], [], [], [[4, -1], [3.6, -0.6]], [[4, -1], [3.6, -0.6]], [[4, -1], [3.6, -0.6]], [[4, -1], [1.5, 1.5], [3.6, -0.6]], [[4, -1], [1.5, 1.5], [3.6, -0.6]], [[4, -1], [1.5, 1.5], [3.6, -0.6]], [[4, -1]], [[4, -1], [3.08, -1.38], [3.33, -1.67], [3.56, -0.67]], [[4, -1], [3.56, -0.67], [1.33, 1]], [[3.56, -0.67], [1.5, 1.5], [3.6, -0.6]], [[4, -1], [1.33, 1], [3.56, -0.67]], [[1.33, 1], [3.56, -0.67]], [], [[3.56, -0.67]]]
---
[[], [[4, -1], [3.56, -0.67], [6, -3]], [[3.6, -0.6], [3.56, -0.67], [4, -1]], [], [], [], [[4, -1], [3.6, -0.6]], [[4, -1], [3.6, -0.6]], [[4, -1], [3.6, -0.6]], [[1.5, 1.5], [3.6, -0.6], [4, -1]], [[1.5, 1.5], [3.6, -0.6], [4, -1]], [[1.5, 1.5], [3.6, -0.6], [4, -1]], [[4, -1]], [[4, -1], [3.56, -0.67], [3.08, -1.38], [3.33, -1.67]], [[1.33, 1], [4, -1], [3.56, -0.67]], [[1.5, 1.5], [3.56, -0.67], [3.6, -0.6]], [[1.33, 1], [4, -1], [3.56, -0.67]], [[1.33, 1], [3.56, -0.67]], [], [[3.56, -0.67]]]
>>> 

def tmp15():
    #两组路径
    path1 = a6=[[[2, -3], [3.33, -1.67], [5.09, -3.68], [2, -6], [1.2, -6.6], [-0.86, -5.57], [-0.4, -4.2], [1, 0], [2, -3]], [[2, -3], [3.33, -1.67], [5.09, -3.68], [5.56, -3.33], [6, -3], [4, -1], [3.56, -0.67], [3.08, -1.38], [2, -3]], [[2, -3], [3.33, -1.67], [5.09, -3.68], [5.56, -3.33], [4, -1], [3.6, -0.6], [3.56, -0.67], [3.08, -1.38], [2, -3]], [[2, -3], [3.33, -1.67], [3.08, -1.38], [1.24, 0.72], [1.33, 1], [0.44, 1.67], [0.46, 1.62], [1, 0], [2, -3]], [[2, -3], [3.33, -1.67], [3.08, -1.38], [3.56, -0.67], [1.33, 1], [1.24, 0.72], [0.46, 1.62], [1, 0], [2, -3]], [[2, -3], [3.33, -1.67], [3.08, -1.38], [3.56, -0.67], [1.33, 1], [0.44, 1.67], [0.46, 1.62], [1, 0], [2, -3]], [[2, -3], [3.33, -1.67], [4, -1], [3.6, -0.6], [3.56, -0.67], [3.08, -1.38], [1.24, 0.72], [1, 0], [2, -3]], [[2, -3], [3.33, -1.67], [4, -1], [3.6, -0.6], [3.56, -0.67], [1.33, 1], [1.24, 0.72], [1, 0], [2, -3]], [[2, -3], [3.33, -1.67], [4, -1], [3.6, -0.6], [3.56, -0.67], [1.33, 1], [1.24, 0.72], [3.08, -1.38], [2, -3]], [[2, -3], [3.33, -1.67], [4, -1], [3.6, -0.6], [1.5, 1.5], [1.33, 1], [1.24, 0.72], [1, 0], [2, -3]], [[2, -3], [3.33, -1.6
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值