[从头学数学] 第283节 [计算几何] 多边形的单调块划分

来源:互联网 发布:java字符串换行连接 编辑:程序博客网 时间:2024/05/18 01:00
剧情提要:
阿伟看到了一本比较有趣的书,是关于《计算几何》的,2008年由北清派出版。很好奇
它里面讲了些什么,就来看看啦。


正剧开始:
星历2016年09月28日 15:47:30, 银河系厄尔斯星球中华帝国江南行省。

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







<span style="font-size:18px;">#>>> [[6, 9], [2.4, 5.4], [0.8, 3.8], [1.33, 3], [2, 3], [3.33, 5], [6, 3.86], [8, 3], [6, 9]][1.33, 3] End[2, 3] Regular[8, 3] End[0.8, 3.8] Regular[6, 3.86] Regular[3.33, 5] SplitSup[Point([2.4, 5.4]), Point([0.8, 3.8]), Point([1.33, 3]), Point([2, 3]), Point([3.33, 5])][1.33, 3] End[2, 3] Regular[0.8, 3.8] Regular[3.33, 5] Regular[2.4, 5.4] Start[Point([3.33, 5]), Point([6, 3.86]), Point([8, 3]), Point([6, 9]), Point([2.4, 5.4])][8, 3] End[6, 3.86] Regular[3.33, 5] Regular[2.4, 5.4] Regular[6, 9] Start---2[[[2.4, 5.4], [0.8, 3.8], [1.33, 3], [2, 3], [3.33, 5], [2.4, 5.4]], [[3.33, 5], [6, 3.86], [8, 3], [6, 9], [2.4, 5.4], [3.33, 5]]]def tmp6():    path = [[[6, 9], [2.4, 5.4], [0.8, 3.8], [1.33, 3], [2, 3], [6, 3], [6, 3.86], [3.33, 5], [6, 9]], [[6, 9], [2.4, 5.4], [0.8, 3.8], [1.33, 3], [2, 3], [3.33, 5], [6, 3.86], [8, 3], [6, 9]], [[6, 9], [2.4, 5.4], [0.8, 3.8], [0, 3], [1.5, 1.5], [1.78, 2.33], [2, 3], [3.33, 5], [6, 9]], [[6, 9], [2.4, 5.4], [0.8, 3.8], [0, 3], [1.33, 3], [1.78, 2.33], [2, 3], [3.33, 5], [6, 9]], [[6, 9], [2.4, 5.4], [0.8, 3.8], [0, 3], [1.33, 3], [2, 3], [6, 3], [6, 3.86], [6, 9]], [[6, 9], [2.4, 5.4], [0.8, 3.8], [0, 3], [1.33, 3], [2, 3], [3.33, 5], [6, 3.86], [6, 9]], [[6, 9], [2.4, 5.4], [3.33, 5], [2, 3], [6, 3], [6, -3], [8, -7], [8, 3], [6, 9]], [[6, 9], [2.4, 5.4], [3.33, 5], [6, 3.86], [6, 3], [6, -3], [8, -7], [8, 3], [6, 9]], [[6, 9], [2.4, 5.4], [-6, 9], [-0.37, 2.56], [0, 3], [1.33, 3], [2, 3], [3.33, 5], [6, 9]], [[6, 9], [3.33, 5], [2, 3], [1.78, 2.33], [3.67, -0.5], [4.29, 0.43], [6, 3], [6, 3.86], [6, 9]], [[6, 9], [3.33, 5], [2, 3], [6, 3], [6, -3], [5.67, -3.5], [8, -7], [8, 3], [6, 9]], [[6, 9], [3.33, 5], [2, 3], [6, 3], [6, -3], [8, -7], [8, 3], [6, 3.86], [6, 9]], [[6, 9], [3.33, 5], [2, 3], [6, 3], [4.5, 0], [6, -3], [8, -7], [8, 3], [6, 9]], [[6, 9], [3.33, 5], [6, 3.86], [6, 3], [6, -3], [5.67, -3.5], [8, -7], [8, 3], [6, 9]], [[6, 9], [3.33, 5], [6, 3.86], [6, 3], [4.5, 0], [6, -3], [8, -7], [8, 3], [6, 9]], [[6, 9], [6, 3.86], [6, 3], [4.29, 0.43], [4.5, 0], [6, -3], [8, -7], [8, 3], [6, 9]], [[6, 9], [6, 3.86], [6, 3], [6, -3], [5.56, -3.33], [5.67, -3.5], [8, -7], [8, 3], [6, 9]], [[6, 9], [6, 3.86], [6, 3], [6, -3], [5.67, -3.5], [5.35, -3.97], [8, -7], [8, 3], [6, 9]], [[6, 9], [6, 3.86], [6, 3], [4.5, 0], [6, -3], [5.67, -3.5], [8, -7], [8, 3], [6, 9]], [[6, 9], [6, 3.86], [6, 3], [4.5, 0], [4, -1], [6, -3], [8, -7], [8, 3], [6, 9]]]    len_path = len(path);    #第几条线段作为测试        n = 1;    pathArray = [];    dealed = [];    pathArray.append(path[n]);    while (len(pathArray) > 0):        path_ = pathArray.pop(0);        print(path_);                #把路径注册给Polygon类        poly = Polygon(path_);        poly.genVertexTree();        #计算凹凸性,这步不能省,否则结果会出错。        poly.setConcave();        #把路径注册给Path类        a = Path(path_);        #标记路径中是否有需要处理的点        changed = False;        for item in poly.vertexTree:            #print(item);            stype = item.pointType();            print(item.point, stype);                        pitem = item.point;             #如果顶点类型是汇合顶点            if (stype == 'Merge'):                print('M');                downBrother = a.findDownBrother(pitem);                if (downBrother != None): #这个其实可以保证,这是由Merge顶点的特性决定的。                    print('down');                    #找到两个顶点在路径中的序号                    idx1 = a.path.index(pitem);                    idx2 = a.path.index(downBrother);                    pathArray.append(a.majorSubpath(idx1, idx2));                    pathArray.append(a.minorSubpath(idx1, idx2));                    changed = True;                    break;            elif (stype == 'Split'):                print('S');                upBrother = a.findUpBrother(pitem);                if (upBrother != None): #这个其实可以保证,这是由Merge顶点的特性决定的。                    print('up');                    #找到两个顶点在路径中的序号                    idx1 = a.path.index(pitem);                    idx2 = a.path.index(upBrother);                    pathArray.append(a.majorSubpath(idx1, idx2));                    pathArray.append(a.minorSubpath(idx1, idx2));                    changed = True;                    break;        if (changed == False):            dealed.append(path_);    print('---');    print(len(dealed));    print(dealed);#</span>


<span style="font-size:18px;">#class Path():    def info(self):        print(self.path);        def __init__(self, path):        if (path[-1] == path[0]):            path = path[:-1];                    if (type(path[0]) == Point):            self.path = path;        else:            self.path = [];            for i in range(len(path)):                self.path.append(Point(path[i]));    #判断路径相等    def __eq__(self, other):        if other == None:            return False;        len_1 = len(self.path);        len_2 = len(other.path);        if (len_1 != len_2):            return False;        if (other[0] in self):            idx = self.path.index(other[0]);            for i in range(len_1):                if other[i] != self.path[(idx+i)%len_1]:                    return False;            return True;        else:            return False;    def __len__(self):        return len(self.path);    def __iter__(self):        for i in range(len(self)):            yield self.path[i];    def __contains__(self, item):        return item in self.path;    def __getitem__(self, index):        return self.path[index];       #路径上的顶点集    def pointSet(self):        pSet = set();        for i in range(len(self.path)):            pSet.add(self.path[i]);        return pSet;    #两个点序号之间的劣弧路径,是以通过点的数量定优劣弧的,不是根据路径长度。    def minorSubpath(self, idx1, idx2):        idx1, idx2 = idx1%len(self), idx2%len(self);        idx1, idx2  = min(idx1, idx2), max(idx1, idx2);        len_path = len(self.path);        if (idx2 - idx1) < len_path//2:            subPath = self.path[idx1:idx2+1];        else:            subPath = self.path[idx2:]+self.path[:idx1+1];        return subPath;    #两个点序号之间的优弧路径,是以通过点的数量定优劣弧的,不是根据路径长度。    def majorSubpath(self, idx1, idx2):        idx1, idx2 = idx1%len(self), idx2%len(self);        idx1, idx2  = min(idx1, idx2), max(idx1, idx2);        len_path = len(self.path);        if (idx2 - idx1) >= len_path//2:            subPath = self.path[idx1:idx2+1];        else:            subPath = self.path[idx2:]+self.path[:idx1+1];        return subPath;    #连接两段子路径,两段路径必须有相同的起点和终点,这样形成一个环路    def linkPath(self, path1, path2):        result = [];        if path1[0] == path2[0] and path1[-1] == path2[-1]:            path2 = list(reversed(path2));            result = path1[:-1]+path2[:-1];        elif path1[0] == path2[-1] and path1[-1] == path2[0]:            result = path1[:-1]+path2[:-1];        return result;    #在路径中两个不同点增加对角线,把一条路径分割成两条路径    def addDiagonal(self, index1, index2):        if (abs(index1 - index2) > 1):            return [self.minorSubpath(index1, index2), self.majorSubpath(index1, index2)];    #找某点的左邻居边,也就是路径中处于给定点的左边,并且在y扫描线上最接近的那条边    def findLeftNearestEdge(self, point):        edge = None;        scanPoint = None;        len_ = len(self.path);        for i in range(len_):            seg = SegLine(self.path[i].value(), self.path[(i+1)%len_].value());            #点在边的右侧            if (judgePointAgainstEdgePosition(seg, point) == 'Right'):                if (edge == None):                    edge = seg;                    scanPoint = calcScanLineCrosspoint(seg, point);                else:                    a = calcScanLineCrosspoint(seg, point);                    #取扫描线的交点的x值                    if a[0] > scanPoint[0]:                        edge = seg;                        scanPoint = a;        return [edge, scanPoint];    #找某点的右邻居边,也就是路径中处于给定点的右边,并且在y扫描线上最接近的那条边    def findRightNearestEdge(self, point):        edge = None;        scanPoint = None;        len_ = len(self.path);        for i in range(len_):            seg = SegLine(self.path[i].value(), self.path[(i+1)%len_].value());            #点在边的左侧            if (judgePointAgainstEdgePosition(seg, point) == 'Left'):                if (edge == None):                    edge = seg;                    scanPoint = calcScanLineCrosspoint(seg, point);                else:                    a = calcScanLineCrosspoint(seg, point);                    #取扫描线的交点的x值                    if a[0] < scanPoint[0]:                        edge = seg;                        scanPoint = a;        return [edge, scanPoint];    #找某点的上兄弟点,这个点的y坐标大于给定点,    #并且处于该点的左、右邻居边之间。    #如果没有这种点,返回左、右邻居边的上顶点中y值比较小的那一个点。        def findUpBrother(self, point): #point是Point类型        rEdge = self.findRightNearestEdge(point);        lEdge = self.findLeftNearestEdge(point);        rSeg, lSeg = rEdge[0], lEdge[0];        upBrother = None;                if (rSeg != None and lSeg != None):            len_ = len(self.path);            for i in range(len_):                a = self.path[i].value();                b = point.value();                if (a[1] > b[1]):                    if (judgePointAgainstEdgePosition(rSeg, self.path[i]) == 'Left' and\                        judgePointAgainstEdgePosition(lSeg, self.path[i]) == 'Right'):                        if upBrother == None:                            upBrother = self.path[i];                        else:                            if (upBrother < self.path[i]):                                upBrother = self.path[i];            if upBrother == None:                upBrother = min(Point(lSeg.value()[1]), Point(rSeg.value()[1]));        return upBrother;    #找某点的下兄弟点,这个点的y坐标小于给定点,    #并且处于该点的左、右邻居边之间。    #如果没有这种点,返回左、右邻居边的下顶点中y值比较大的那一个点。        def findDownBrother(self, point): #point是Point类型        rEdge = self.findRightNearestEdge(point);        lEdge = self.findLeftNearestEdge(point);        rSeg, lSeg = rEdge[0], lEdge[0];        downBrother = None;                if (rSeg != None and lSeg != None):            len_ = len(self.path);            for i in range(len_):                a = self.path[i].value();                b = point.value();                if (a[1] < b[1]):                    if (judgePointAgainstEdgePosition(rSeg, self.path[i]) == 'Left' and\                        judgePointAgainstEdgePosition(lSeg, self.path[i]) == 'Right'):                        if downBrother == None:                            downBrother = self.path[i];                        else:                            if (downBrother < self.path[i]):                                downBrother = self.path[i];            if downBrother == None:                downBrother = max(Point(lSeg.value()[0]), Point(rSeg.value()[0]));        return downBrother;    #三个点的叉积def crossProduct(P1, P2, P3):    x1, y1, x2, y2, x3, y3 = P1[0], P1[1], P2[0], P2[1], P3[0], P3[1];    #    # 1   1   1    # x_1 x_2 x_3    # y_1 y_2 y_3    #    #逆时针结果为正,顺时针为负    return round((x1*y2-x2*y1)-(x1*y3-x3*y1)+(x2*y3-x3*y2), 3);#判断点在边的左边还是右边def judgePointAgainstEdgePosition(seg, point):    #传入SegLine类型和Point类型,这样方便比较    #SegLine是以y优先x其次由小大大排序的,终端点按排序规则大于起始端点    pValue = point.value();    sValue = seg.value();    x0, y0, x1, y1, x2, y2 = pValue[0], pValue[1], sValue[0][0], sValue[0][1],\                             sValue[1][0], sValue[1][1];    #点的y值要在线段两个端点的y值中间,可以等于    if (y0 >= y1 and y0 <= y2):        cross = crossProduct(pValue, sValue[0], sValue[1]);        if (cross > 0):            #点在线段的左边            return 'Left';        elif (cross < 0):            #点在线段的右边            return 'Right';    return 'Nevermind';#获取扫描线与线段的交点#y方向的扫描线为平行于x轴且y值为某一定值的直线def calcScanLineCrosspoint(seg, point):    #传入SegLine类型和Point类型,这样方便比较    #SegLine是以y优先x其次由小大大排序的,终端点按排序规则大于起始端点    pValue = point.value();    sValue = seg.value();    x0, y0, x1, y1, x2, y2 = pValue[0], pValue[1], sValue[0][0], sValue[0][1],\                             sValue[1][0], sValue[1][1];    #点的y值要在线段两个端点的y值中间,可以等于    if (y0 >= y1 and y0 <= y2):        if y0 == y1:            return sValue[0];        elif y0 == y2:            return sValue[1];        else:            x = x1 + (y0 - y1)/(y2 - y1)*(x2-x1);            return [round(x, 2), y0];    return [];#</span>



$split = [[[2.4, 5.4], [0.8, 3.8], [1.33, 3], [2, 3], [3.33, 5], [2.4, 5.4]], [[3.33, 5], [6, 3.86], [8, 3], [6, 9], [2.4, 5.4], [3.33, 5]]]


<span style="font-size:18px;">//if (1) {var r = 20;              config.setSector(1,1,1,1);                config.graphPaper2D(0, 0, r);              config.axis2D(0, 0, 250, 1.2);   //坐标轴设定                var scaleX = 2*r, scaleY = 2*r;                  var spaceX = 2, spaceY = 2;                   var xS = -10, xE = 10;                  var yS = -10, yE = 10;                  config.axisSpacing(xS, xE, spaceX, scaleX, 'X');                    config.axisSpacing(yS, yE, spaceY, scaleY, 'Y');                                        var transform = new Transform();    //顶点var a = [];for (var i = 0; i < $vertex.length; i++) {a.push($vertex[i][0]);}//显示变换                if (a.length > 0) {                    a = transform.scale(transform.translate(a, 0, 0), scaleX/spaceX, scaleY/spaceY);}var lable = [];for (var i = 0; i < 100; i++) {lable.push(i.toFixed(0));}/*//边集var b = [];for (var i = 0; i < $seg.length; i++) {b.push([a[$seg[i][0]], a[$seg[i][1]]]);}var edges = b.length;for (var i = 0; i < edges; i++) {shape.multiLineDraw([].concat(b[i]), 'red');}*/var colorArray = ['red', 'orange', 'yellow', 'green', 'cyan', 'blue', 'purple'];var seg = [];var idx = xGlobal;var len = $path2.length;plot.setLineWidth(3);seg = transform.scale(transform.translate($path2[idx%len], 0, 0), scaleX/spaceX, scaleY/spaceY);shape.multiLineDraw([].concat(seg), 'pink');plot.fillText('路径: '+(idx%len).toFixed(0), 200, -150, 200);for (var i = 0; i < $split.length; i++) {seg = transform.scale(transform.translate($split[i], 0, 0), scaleX/spaceX, scaleY/spaceY);shape.multiLineDraw([].concat(seg), colorArray[i%7]);}//主要顶点shape.pointDraw([].concat(a), 'blue', 1, 1, lable);cPoint = transform.scale(transform.translate($center, 0, 0), scaleX/spaceX, scaleY/spaceY);shape.pointDraw([].concat([cPoint[idx%len]]), 'black');/*concavePoint = transform.scale(transform.translate($concave[idx%len], 0, 0), scaleX/spaceX, scaleY/spaceY);shape.pointDraw([].concat(concavePoint), 'orange');*///次要顶点var b = [];for (var i = 0; i < 6; i++) {b = [].concat($pointType[idx%len][i]);//显示变换        if (b.length > 0) {        b = transform.scale(transform.translate(b, 0, 0), scaleX/spaceX, scaleY/spaceY);shape.pointDraw([].concat(b), colorArray[(i-1)%7]);}}}//</span>

更多图片:







本节到此结束,欲知后事如何,请看下回分解。




0 0
原创粉丝点击