您的位置:首页 > 其它

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

2016-09-28 16:11 435 查看
剧情提要:
阿伟看到了一本比较有趣的书,是关于《计算几何》的,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] Split
S
up
[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
);

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>

更多图片:







本节到此结束,欲知后事如何,请看下回分解。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: