剧情提要:
阿伟看到了一本比较有趣的书,是关于《计算几何》的,2008年由北清派出版。很好奇
它里面讲了些什么,就来看看啦。
正剧开始:
星历2016年10月21日 14:37:23, 银河系厄尔斯星球中华帝国江南行省。
Execute()->_ExecuteInternal()/_BuildResult()
_ExecuteInternal()->_Reset()/_PopScanbeam()/_InsertLocalMinimaIntoAEL()/
_ProcessIntersections()/_ProcessEdgesAtTopOfScanbeam()/
_FixupOutPolygon()/_ReversePolyPtLinks()/
_JoinCommonEdges()/_DoSimplePolygons()
_Reset()->[]
_PopScanbeam()->[]
_InsertLocalMinimaIntoAEL()->_InsertEdgeIntoAEL()/_InsertScanbeam()/
_AddEdgeToSEL()/_AddLocalMinPoly()/_GetOverlapSegment()/_AddJoin()/
_IntersectEdges()/_PopLocalMinima()
_InsertScanbeam()->[]
_ProcessIntersections()->_BuildIntersectList()/_ProcessIntersectList()
_BuildIntersectList()->_InsertIntersectNode()/_SwapPositionsInSEL()
_BuildResult()->[]
_ProcessEdgesAtTopOfScanbeam()->_GetMaximaPair()/_DoMaxima()/
_GetOverlapSegment()/_AddJoin()/_AddHorzJoin()/_UpdateEdgeIntoAEL()/
_AddEdgeToSEL()/_AddOutPt()/_ProcessHorizontals()
_JoinPoints()->_FindSegment()
_FindSegment()->_GetOverlapSegment();
$vert_subj = [[199, 133], [577, 464], [468, 386], [130, 128], [336, 140], [324, 418], [91, 223], [67, 455], [168, 458], [639, 227]]
$vert_clip = [[233, 423], [193, 76], [369, 373], [538, 416], [155, 301], [215, 323], [82, 473], [136, 405], [466, 145], [250, 336], [480, 218], [135, 320], [492, 135], [40, 361], [467, 323], [347, 26]]
$subj_edge = [[[577, 464], [199, 133]],[[577, 464], [468, 386]],[[468, 386], [130, 128]],[[336, 140], [130, 128]],[[324, 418], [336, 140]],[[324, 418], [91, 223]],[[67, 455], [91, 223]],[[168, 458], [67, 455]],[[168, 458], [639, 227]],[[639, 227], [199, 133]],];
$clip_edge = [[[233, 423], [193, 76]],[[369, 373], [193, 76]],[[538, 416], [369, 373]],[[538, 416], [155, 301]],[[215, 323], [155, 301]],[[82, 473], [215, 323]],[[82, 473], [136, 405]],[[136, 405], [466, 145]],[[250, 336], [466, 145]],[[250, 336], [480, 218]],[[135, 320], [480, 218]],[[135, 320], [492, 135]],[[40, 361], [492, 135]],[[40, 361], [467, 323]],[[467, 323], [347, 26]],[[233, 423], [347, 26]],];
$solution_i = [[[223, 333], [225, 335], [223, 336], [224, 345], [211, 346], [157, 389], [97, 456], [96, 456], [136, 405], [157, 389], [193, 347], [211, 346], [223, 336]], [[235, 344], [252, 358], [233, 423], [224, 345]], [[490, 401], [514, 409], [516, 410], [494, 405]], [[341, 289], [394, 329], [346, 334], [363, 362], [362, 363], [327, 353], [328, 335], [346, 334], [329, 305], [329, 295]], [[165, 305], [135, 320], [175, 308], [165, 305], [166, 304], [158, 302], [175, 293], [179, 297], [166, 304], [177, 308], [175, 308], [207, 320], [213, 325], [193, 347], [77, 358], [79, 342], [158, 302]], [[480, 218], [434, 242], [463, 313], [438, 326], [421, 327], [364, 278], [434, 242], [430, 233]], [[188, 304], [201, 315], [177, 308]], [[331, 262], [331, 265], [321, 274], [313, 267]], [[309, 230], [331, 249], [331, 251], [312, 267], [301, 259], [290, 240]], [[421, 180], [425, 181], [414, 191], [430, 233], [342, 259], [340, 256], [414, 191], [412, 187]], [[409, 178], [408, 178], [412, 187], [333, 250], [331, 249], [333, 218]], [[293, 215], [307, 228], [288, 237], [290, 240], [285, 242], [284, 246], [282, 244], [285, 242], [286, 238], [288, 237], [287, 235]], [[200, 134], [257, 184], [287, 235], [286, 238], [279, 242], [206, 186]], [[315, 139], [336, 140], [335, 162], [407, 177], [333, 215], [335, 162], [309, 157]], [[199, 132], [227, 134], [231, 140], [200, 133]]];
$solution_u = [[[347, 26], [408, 177], [407, 177], [409, 178], [408, 177], [456, 153], [466, 145], [458, 152], [456, 153], [453, 155], [409, 178], [408, 178], [421, 180], [453, 155], [457, 153], [458, 152], [492, 135], [457, 153], [425, 181], [639, 227], [463, 313], [467, 323], [438, 326], [426, 332], [514, 409], [538, 416], [516, 410], [577, 464], [494, 405], [369, 373], [363, 364], [362, 363], [326, 381], [324, 418], [296, 395], [326, 381], [327, 353], [283, 339], [328, 335], [329, 305], [324, 298], [329, 295], [330, 281], [321, 274], [314, 280], [324, 298], [259, 331], [259, 332], [283, 339], [256, 342], [252, 358], [296, 395], [168, 458], [97, 456], [82, 473], [96, 456], [67, 455], [77, 358], [40, 361], [79, 342], [91, 223], [175, 293], [216, 273], [206, 186], [130, 128], [199, 132], [193, 76], [227, 134], [315, 139]], [[421, 327], [394, 329], [408, 340], [363, 362], [363, 364], [490, 401], [468, 386], [408, 340], [426, 332]], [[284, 246], [274, 279], [218, 295], [221, 321], [237, 326], [225, 335], [235, 344], [256, 342], [259, 332], [258, 332], [259, 331], [261, 327], [314, 280], [308, 270], [309, 268], [313, 267], [312, 267], [309, 268], [307, 269], [308, 270], [268, 301], [261, 327], [255, 331], [258, 332], [250, 336], [255, 331], [237, 326], [268, 301], [274, 279], [307, 269], [301, 259]], [[201, 315], [207, 320], [215, 323], [213, 325], [223, 333], [221, 321]], [[279, 242], [216, 273], [216, 278], [179, 297], [188, 304], [218, 295], [216, 278], [282, 244]], [[342, 259], [335, 261], [331, 265], [330, 281], [341, 289], [364, 278]], [[333, 250], [331, 251], [331, 262], [335, 261], [340, 256]], [[333, 215], [307, 228], [309, 230], [333, 218]], [[231, 140], [257, 184], [293, 215], [309, 157]], [[199, 133], [200, 134], [200, 133]]]
$solution_d = [[[494, 405], [516, 410], [577, 464]], [[225, 335], [235, 344], [224, 345], [233, 423], [252, 358], [296, 395], [168, 458], [97, 456], [157, 389], [211, 346], [224, 345], [223, 336]], [[213, 325], [223, 333], [223, 336], [211, 346], [193, 347], [157, 389], [136, 405], [96, 456], [67, 455], [77, 358], [193, 347]], [[327, 353], [362, 363], [326, 381], [324, 418], [296, 395], [326, 381]], [[438, 326], [426, 332], [514, 409], [490, 401], [468, 386], [408, 340], [426, 332], [421, 327]], [[394, 329], [408, 340], [363, 362], [346, 334]], [[79, 342], [91, 223], [175, 293], [158, 302], [166, 304], [165, 305], [175, 308], [135, 320], [165, 305], [158, 302]], [[329, 305], [346, 334], [328, 335]], [[179, 297], [188, 304], [177, 308], [201, 315], [207, 320], [175, 308], [177, 308], [166, 304]], [[425, 181], [639, 227], [463, 313], [434, 242], [480, 218], [430, 233], [434, 242], [364, 278], [342, 259], [430, 233], [414, 191]], [[331, 265], [330, 281], [341, 289], [329, 295], [330, 281], [321, 274]], [[331, 251], [331, 262], [313, 267], [312, 267]], [[307, 228], [309, 230], [290, 240], [301, 259], [284, 246], [285, 242], [290, 240], [288, 237]], [[408, 178], [421, 180], [412, 187], [414, 191], [340, 256], [333, 250], [412, 187]], [[257, 184], [293, 215], [287, 235], [288, 237], [286, 238], [285, 242], [282, 244], [279, 242], [286, 238], [287, 235]], [[407, 177], [409, 178], [333, 218], [333, 215]], [[130, 128], [199, 132], [200, 133], [199, 133], [200, 134], [206, 186]], [[227, 134], [315, 139], [309, 157], [231, 140]]]
$solution = [[[347, 26], [408, 177], [407, 177], [409, 178], [333, 218], [331, 249], [333, 250], [331, 251], [331, 262], [313, 267], [321, 274], [314, 280], [324, 298], [259, 331], [259, 332], [283, 339], [256, 342], [252, 358], [296, 395], [168, 458], [97, 456], [157, 389], [211, 346], [224, 345], [223, 336], [225, 335], [223, 333], [221, 321], [201, 315], [188, 304], [218, 295], [216, 278], [282, 244], [279, 242], [286, 238], [287, 235], [257, 184], [200, 134], [200, 133], [199, 133], [200, 134], [206, 186], [279, 242], [216, 273], [216, 278], [179, 297], [188, 304], [177, 308], [201, 315], [207, 320], [215, 323], [213, 325], [223, 333], [223, 336], [211, 346], [193, 347], [157, 389], [136, 405], [96, 456], [97, 456], [82, 473], [96, 456], [67, 455], [77, 358], [193, 347], [213, 325], [207, 320], [175, 308], [177, 308], [166, 304], [179, 297], [175, 293], [216, 273], [206, 186], [130, 128], [199, 132], [200, 133], [231, 140], [257, 184], [293, 215], [287, 235], [288, 237], [286, 238], [285, 242], [282, 244], [284, 246], [274, 279], [218, 295], [221, 321], [237, 326], [225, 335], [235, 344], [224, 345], [233, 423], [252, 358], [235, 344], [256, 342], [259, 332], [258, 332], [259, 331], [261, 327], [314, 280], [308, 270], [309, 268], [313, 267], [312, 267], [331, 251], [331, 249], [309, 230], [333, 218], [333, 215], [407, 177], [335, 162], [336, 140], [315, 139]], [[492, 135], [457, 153], [425, 181], [639, 227], [463, 313], [467, 323], [438, 326], [426, 332], [514, 409], [538, 416], [516, 410], [514, 409], [490, 401], [468, 386], [408, 340], [426, 332], [421, 327], [438, 326], [463, 313], [434, 242], [480, 218], [430, 233], [434, 242], [364, 278], [421, 327], [394, 329], [408, 340], [363, 362], [363, 364], [490, 401], [494, 405], [516, 410], [577, 464], [494, 405], [369, 373], [363, 364], [362, 363], [363, 362], [346, 334], [394, 329], [341, 289], [364, 278], [342, 259], [430, 233], [414, 191], [425, 181], [421, 180], [453, 155], [457, 153], [458, 152]], [[466, 145], [458, 152], [456, 153], [453, 155], [409, 178], [408, 178], [421, 180], [412, 187], [414, 191], [340, 256], [342, 259], [335, 261], [331, 265], [330, 281], [341, 289], [329, 295], [329, 305], [346, 334], [328, 335], [327, 353], [362, 363], [326, 381], [324, 418], [296, 395], [326, 381], [327, 353], [283, 339], [328, 335], [329, 305], [324, 298], [329, 295], [330, 281], [321, 274], [331, 265], [331, 262], [335, 261], [340, 256], [333, 250], [412, 187], [408, 178], [409, 178], [408, 177], [456, 153]], [[79, 342], [77, 358], [40, 361], [79, 342], [91, 223], [175, 293], [158, 302], [166, 304], [165, 305], [175, 308], [135, 320], [165, 305], [158, 302]], [[193, 76], [227, 134], [315, 139], [309, 157], [335, 162], [333, 215], [307, 228], [309, 230], [290, 240], [301, 259], [312, 267], [309, 268], [307, 269], [308, 270], [268, 301], [261, 327], [255, 331], [258, 332], [250, 336], [255, 331], [237, 326], [268, 301], [274, 279], [307, 269], [301, 259], [284, 246], [285, 242], [290, 240], [288, 237], [307, 228], [293, 215], [309, 157], [231, 140], [227, 134], [199, 132]]]

阿伟看到了一本比较有趣的书,是关于《计算几何》的,2008年由北清派出版。很好奇
它里面讲了些什么,就来看看啦。
正剧开始:
星历2016年10月21日 14:37:23, 银河系厄尔斯星球中华帝国江南行省。
[工程师阿伟]正在和[机器小伟]一起研究[计算几何]]。
<span style="font-size:18px;">#
#===============================================================================
# Clipper class (+ data structs & ancilliary functions)
#===============================================================================
def _IntersectPoint(edge1, edge2):
if _SlopesEqual2(edge1, edge2):
if (edge2.ybot > edge1.ybot): y = edge2.ybot
else: y = edge1.ybot
return Point(0, y), False
if edge1.dx == 0:
x = edge1.xBot
if edge2.dx == horizontal:
y = edge2.yBot
else:
b2 = edge2.yBot - Decimal(edge2.xBot)/edge2.dx
y = round(Decimal(x)/edge2.dx + b2)
elif edge2.dx == 0:
x = edge2.xBot
if edge1.dx == horizontal:
y = edge1.yBot
else:
b1 = edge1.yBot - Decimal(edge1.xBot)/edge1.dx
y = round(Decimal(x)/edge1.dx + b1)
else:
b1 = edge1.xBot - edge1.yBot * edge1.dx
b2 = edge2.xBot - edge2.yBot * edge2.dx
m = Decimal(b2-b1)/(edge1.dx - edge2.dx)
y = round(m)
if math.fabs(edge1.dx) < math.fabs(edge2.dx):
x = round(edge1.dx * m + b1)
else:
x = round(edge2.dx * m + b2)
if (y < edge1.yTop) or (y < edge2.yTop):
if (edge1.yTop > edge2.yTop):
return Point(edge1.xTop,edge1.yTop), _TopX(edge2, edge1.yTop) < edge1.xTop
else:
return Point(edge2.xTop,edge2.yTop), _TopX(edge1, edge2.yTop) > edge2.xTop
else:
return Point(x,y), True
def _TopX(e, currentY):
if currentY == e.yTop: return e.xTop
elif e.xTop == e.xBot: return e.xBot
else: return e.xBot + round(e.dx * Decimal(currentY - e.yBot))
def _E2InsertsBeforeE1(e1,e2):
if (e2.xCurr == e1.xCurr):
if (e2.yTop > e1.yTop):
return e2.xTop < _TopX(e1, e2.yTop)
return e1.xTop > _TopX(e2, e1.yTop)
else:
return e2.xCurr < e1.xCurr
def _IsMinima(e):
return e is not None and e.prevE.nextInLML != e and e.nextE.nextInLML != e
def _IsMaxima(e, y):
return e is not None and e.yTop == y and e.nextInLML is None
def _IsIntermediate(e, y):
return e.yTop == y and e.nextInLML is not None
def _GetMaximaPair(e):
if not _IsMaxima(e.nextE, e.yTop) or e.nextE.xTop != e.xTop:
return e.prevE
else:
return e.nextE
def _GetnextInAEL(e, direction):
if direction == Direction.LeftToRight: return e.nextInAEL
else: return e.prevInAEL
def _ProtectLeft(val):
if val: return Protects.Both
else: return Protects.Right
def _ProtectRight(val):
if val: return Protects.Both
else: return Protects.Left
def _GetDx(pt1, pt2):
if (pt1.y == pt2.y): return horizontal
else: return Decimal(pt2.x - pt1.x)/(pt2.y - pt1.y)
def _Param1RightOfParam2(outRec1, outRec2):
while outRec1 is not None:
outRec1 = outRec1.FirstLeft
if outRec1 == outRec2: return True
return False
def _FirstParamIsbottomPt(btmPt1, btmPt2):
p = btmPt1.prevOp
while _PointsEqual(p.pt, btmPt1.pt) and (p != btmPt1): p = p.prevOp
dx1p = abs(_GetDx(btmPt1.pt, p.pt))
p = btmPt1.nextOp
while _PointsEqual(p.pt, btmPt1.pt) and (p != btmPt1): p = p.nextOp
dx1n = abs(_GetDx(btmPt1.pt, p.pt))
p = btmPt2.prevOp
while _PointsEqual(p.pt, btmPt2.pt) and (p != btmPt2): p = p.prevOp
dx2p = abs(_GetDx(btmPt2.pt, p.pt))
p = btmPt2.nextOp
while _PointsEqual(p.pt, btmPt2.pt) and (p != btmPt2): p = p.nextOp
dx2n = abs(_GetDx(btmPt2.pt, p.pt))
return (dx1p >= dx2p and dx1p >= dx2n) or (dx1n >= dx2p and dx1n >= dx2n)
def _GetBottomPt(pp):
dups = None
p = pp.nextOp
while p != pp:
if p.pt.y > pp.pt.y:
pp = p
dups = None
elif p.pt.y == pp.pt.y and p.pt.x <= pp.pt.x:
if p.pt.x < pp.pt.x:
dups = None
pp = p
else:
if p.nextOp != pp and p.prevOp != pp: dups = p
p = p.nextOp
if dups is not None:
while dups != p:
if not _FirstParamIsbottomPt(p, dups): pp = dups
dups = dups.nextOp
while not _PointsEqual(dups.pt, pp.pt): dups = dups.nextOp
return pp
def _GetLowermostRec(outRec1, outRec2):
if (outRec1.bottomPt is None):
outPt1 = _GetBottomPt(outRec1.pts)
else: outPt1 = outRec1.bottomPt
if (outRec2.bottomPt is None):
outPt2 = _GetBottomPt(outRec2.pts)
else: outPt2 = outRec2.bottomPt
if (outPt1.pt.y > outPt2.pt.y): return outRec1
elif (outPt1.pt.y < outPt2.pt.y): return outRec2
elif (outPt1.pt.x < outPt2.pt.x): return outRec1
elif (outPt1.pt.x > outPt2.pt.x): return outRec2
elif (outPt1.nextOp == outPt1): return outRec2
elif (outPt2.nextOp == outPt2): return outRec1
elif _FirstParamIsbottomPt(outPt1, outPt2): return outRec1
else: return outRec2
def _SetHoleState(e, outRec, polyOutList):
isHole = False
e2 = e.prevInAEL
while e2 is not None:
if e2.outIdx >= 0:
isHole = not isHole
if outRec.FirstLeft is None:
outRec.FirstLeft = polyOutList[e2.outIdx]
e2 = e2.prevInAEL
outRec.isHole = isHole
def _PointCount(pts):
if pts is None: return 0
p = pts
result = 0
while True:
result += 1
p = p.nextOp
if p == pts: break
return result
def _PointIsVertex(pt, outPts):
op = outPts
while True:
if _PointsEqual(op.pt, pt): return True
op = op.nextOp
if op == outPts: break
return False
def _ReversePolyPtLinks(pp):
if pp is None: return
pp1 = pp
while True:
pp2 = pp1.nextOp
pp1.nextOp = pp1.prevOp
pp1.prevOp = pp2;
pp1 = pp2
if pp1 == pp: break
def _FixupOutPolygon(outRec):
lastOK = None
outRec.bottomPt = None
pp = outRec.pts
while True:
if pp.prevOp == pp or pp.nextOp == pp.prevOp:
outRec.pts = None
return
if _PointsEqual(pp.pt, pp.nextOp.pt) or \
_SlopesEqual(pp.prevOp.pt, pp.pt, pp.nextOp.pt):
lastOK = None
pp.prevOp.nextOp = pp.nextOp
pp.nextOp.prevOp = pp.prevOp
pp = pp.prevOp
elif pp == lastOK: break
else:
if lastOK is None: lastOK = pp
pp = pp.nextOp
outRec.pts = pp
def _FixHoleLinkage(outRec):
if outRec.FirstLeft is None or \
(outRec.isHole != outRec.FirstLeft.isHole and \
outRec.FirstLeft.pts is not None): return
orfl = outRec.FirstLeft
while orfl is not None and \
(orfl.isHole == outRec.isHole or orfl.pts is None):
orfl = orfl.FirstLeft
outRec.FirstLeft = orfl
def _GetOverlapSegment(pt1a, pt1b, pt2a, pt2b):
# precondition: segments are co-linear
if abs(pt1a.x - pt1b.x) > abs(pt1a.y - pt1b.y):
if pt1a.x > pt1b.x: tmp = pt1a; pt1a = pt1b; pt1b = tmp
if pt2a.x > pt2b.x: tmp = pt2a; pt2a = pt2b; pt2b = tmp
if (pt1a.x > pt2a.x): pt1 = pt1a
else: pt1 = pt2a
if (pt1b.x < pt2b.x): pt2 = pt1b
else: pt2 = pt2b
return pt1, pt2, pt1.x < pt2.x
else:
if pt1a.y < pt1b.y: tmp = pt1a; pt1a = pt1b; pt1b = tmp
if pt2a.y < pt2b.y: tmp = pt2a; pt2a = pt2b; pt2b = tmp
if (pt1a.y < pt2a.y): pt1 = pt1a
else: pt1 = pt2a
if (pt1b.y > pt2b.y): pt2 = pt1b
else: pt2 = pt2b
return pt1, pt2, pt1.y > pt2.y
def _FindSegment(outPt, pt1, pt2):
if outPt is None: return outPt, pt1, pt2, False
pt1a = pt1; pt2a = pt2
outPt2 = outPt
while True:
if _SlopesEqual(pt1a, pt2a, outPt.pt, outPt.prevOp.pt) and _SlopesEqual(pt1a, pt2a, outPt.pt):
pt1, pt2, overlap = _GetOverlapSegment(pt1a, pt2a, outPt.pt, outPt.prevOp.pt)
if overlap: return outPt, pt1, pt2, True
outPt = outPt.nextOp
if outPt == outPt2: return outPt, pt1, pt2, False
def _Pt3IsBetweenPt1AndPt2(pt1, pt2, pt3):
if _PointsEqual(pt1, pt3) or _PointsEqual(pt2, pt3): return True
elif pt1.x != pt2.x: return (pt1.x < pt3.x) == (pt3.x < pt2.x)
else: return (pt1.y < pt3.y) == (pt3.y < pt2.y)
def _InsertPolyPtBetween(outPt1, outPt2, pt):
if outPt1 == outPt2: raise Exception("JoinError")
result = OutPt(outPt1.idx, pt)
if outPt2 == outPt1.nextOp:
outPt1.nextOp = result
outPt2.prevOp = result
result.nextOp = outPt2
result.prevOp = outPt1
else:
outPt2.nextOp = result
outPt1.prevOp = result
result.nextOp = outPt1
result.prevOp = outPt2
return result
def _PointOnLineSegment(pt, linePt1, linePt2):
return ((pt.x == linePt1.x) and (pt.y == linePt1.y)) or \
((pt.x == linePt2.x) and (pt.y == linePt2.y)) or \
(((pt.x > linePt1.x) == (pt.x < linePt2.x)) and \
((pt.y > linePt1.y) == (pt.y < linePt2.y)) and \
((pt.x - linePt1.x) * (linePt2.y - linePt1.y) == \
(linePt2.x - linePt1.x) * (pt.y - linePt1.y)))
def _PointOnPolygon(pt, pp):
pp2 = pp;
while True:
if (_PointOnLineSegment(pt, pp2.pt, pp2.nextOp.pt)):
return True
pp2 = pp2.nextOp
if (pp2 == pp): return False
def _PointInPolygon(pt, outPt):
result = False
outPt2 = outPt
while True:
if ((((outPt2.pt.y <= pt.y) and (pt.y < outPt2.prevOp.pt.y)) or \
((outPt2.prevOp.pt.y <= pt.y) and (pt.y < outPt2.pt.y))) and \
(pt.x < (outPt2.prevOp.pt.x - outPt2.pt.x) * (pt.y - outPt2.pt.y) / \
(outPt2.prevOp.pt.y - outPt2.pt.y) + outPt2.pt.x)): result = not result
outPt2 = outPt2.nextOp
if outPt2 == outPt: break
def _Poly2ContainsPoly1(outPt1, outPt2):
pt = outPt1
if (_PointOnPolygon(pt.pt, outPt2)):
pt = pt.nextOp
while (pt != outPt1 and _PointOnPolygon(pt.pt, outPt2)):
pt = pt.nextOp
if (pt == outPt1): return True
return _PointInPolygon(pt.pt, outPt2)
def _EdgesAdjacent(inode):
return (inode.e1.nextInSEL == inode.e2) or \
(inode.e1.prevInSEL == inode.e2)
def _UpdateOutPtIdxs(outrec):
op = outrec.pts
while True:
op.idx = outrec.idx
op = op.prevOp
if (op == outrec.pts): break
class Clipper(ClipperBase):
#算上__init__,共有49个方法
#__init__
#_Reset
#Clear
#_InsertScanbeam
#_PopScanbeam
#_SetWindingCount
#_IsEvenOddFillType
#_IsEvenOddAltFillType
#_IsContributing
#_AddEdgeToSEL
#_CopyAELToSEL
#_InsertEdgeIntoAEL
#_InsertLocalMinimaIntoAEL
#_SwapPositionsInAEL
#_SwapPositionsInSEL
#_IsTopHorz
#_ProcessHorizontal
#_ProcessHorizontals
#_AddJoin
#_FixupJoinRecs
#_AddHorzJoin
#_InsertIntersectNode
#_ProcessIntersections
#_BuildIntersectList
#_ProcessIntersectList
#_DeleteFromAEL
#_DeleteFromSEL
#_IntersectEdges
#_DoMaxima
#_UpdateEdgeIntoAEL
#_AddLocalMinPoly
#_AddLocalMaxPoly
#_CreateOutRec
#_AddOutPt
#_AppendPolygon
#_FixupIntersectionOrder
#_ProcessEdgesAtTopOfScanbeam
#_Area
#_JoinPoints
#_FixupFirstLefts1
#_FixupFirstLefts2
#_GetOutRec
#_JoinCommonEdges
#_DoSimplePolygons
#_ExecuteInternal
#Execute
#Execute2
#_BuildResult
#_BuildResult2
def __init__(self):
ClipperBase.__init__(self)
self.ReverseOutput = False
self.ForceSimple = False
self._PolyOutList = []
self._ClipType = ClipType.Intersection
self._Scanbeam = None
self._ActiveEdges = None
self._SortedEdges = None
self._IntersectNodes = None
self._ClipFillType = PolyFillType.EvenOdd
self._SubjFillType = PolyFillType.EvenOdd
self._ExecuteLocked = False
self._UsingPolyTree = False
self._JoinList = None
self._HorzJoins = None
#以下4个方法
def _Reset(self):
ClipperBase._Reset(self)
self._Scanbeam = None
self._PolyOutList = []
lm = self._LocalMinList
while lm is not None:
self._InsertScanbeam(lm.y)
lm = lm.nextLm
def Clear(self):
self._PolyOutList = []
ClipperBase.Clear(self)
def _InsertScanbeam(self, y):
if self._Scanbeam is None:
self._Scanbeam = Scanbeam(y)
elif y > self._Scanbeam.y:
self._Scanbeam = Scanbeam(y, self._Scanbeam)
else:
sb = self._Scanbeam
while sb.nextSb is not None and y <= sb.nextSb.y:
sb = sb.nextSb
if y == sb.y: return
newSb = Scanbeam(y, sb.nextSb)
sb.nextSb = newSb
def _PopScanbeam(self):
result = self._Scanbeam.y
self._Scanbeam = self._Scanbeam.nextSb
return result
#以下3个方法, 有关属性
def _SetWindingCount(self, edge):
e = edge.prevInAEL
while e is not None and e.PolyType != edge.PolyType:
e = e.prevInAEL
if e is None:
edge.windCnt = edge.windDelta
edge.windCnt2 = 0
e = self._ActiveEdges
elif self._IsEvenOddFillType(edge):
edge.windCnt = 1
edge.windCnt2 = e.windCnt2
e = e.nextInAEL
else:
if e.windCnt * e.windDelta < 0:
if (abs(e.windCnt) > 1):
if (e.windDelta * edge.windDelta < 0): edge.windCnt = e.windCnt
else: edge.windCnt = e.windCnt + edge.windDelta
else:
edge.windCnt = e.windCnt + e.windDelta + edge.windDelta
elif (abs(e.windCnt) > 1) and (e.windDelta * edge.windDelta < 0):
edge.windCnt = e.windCnt
elif e.windCnt + edge.windDelta == 0:
edge.windCnt = e.windCnt
else:
edge.windCnt = e.windCnt + edge.windDelta
edge.windCnt2 = e.windCnt2
e = e.nextInAEL
# update windCnt2 ...
if self._IsEvenOddAltFillType(edge):
while (e != edge):
if edge.windCnt2 == 0: edge.windCnt2 = 1
else: edge.windCnt2 = 0
e = e.nextInAEL
else:
while (e != edge):
edge.windCnt2 += e.windDelta
e = e.nextInAEL
def _IsEvenOddFillType(self, edge):
if edge.PolyType == PolyType.Subject:
return self._SubjFillType == PolyFillType.EvenOdd
else:
return self._ClipFillType == PolyFillType.EvenOdd
def _IsEvenOddAltFillType(self, edge):
if edge.PolyType == PolyType.Subject:
return self._ClipFillType == PolyFillType.EvenOdd
else:
return self._SubjFillType == PolyFillType.EvenOdd
#交、并、差、异或的每种运算所保留下来的点,在这里得到区别对待
def _IsContributing(self, edge):
if edge.PolyType == PolyType.Subject:
pft = self._SubjFillType
pft2 = self._ClipFillType
else:
pft = self._ClipFillType
pft2 = self._SubjFillType
if pft == PolyFillType.EvenOdd or pft == PolyFillType.NonZero:
if abs(edge.windCnt) != 1: return False
elif pft == PolyFillType.Positive:
if edge.windCnt != 1: return False
elif pft == PolyFillType.Negative:
if edge.windCnt != -1: return False
if self._ClipType == ClipType.Intersection: ###########
if pft2 == PolyFillType.EvenOdd or pft2 == PolyFillType.NonZero:
return edge.windCnt2 != 0
elif pft2 == PolyFillType.Positive:
return edge.windCnt2 > 0
else:
return edge.windCnt2 < 0 # Negative
elif self._ClipType == ClipType.Union: ###########
if pft2 == PolyFillType.EvenOdd or pft2 == PolyFillType.NonZero:
return edge.windCnt2 == 0
elif pft2 == PolyFillType.Positive:
return edge.windCnt2 <= 0
else: return edge.windCnt2 >= 0 # Negative
elif self._ClipType == ClipType.Difference: ###########
if edge.PolyType == PolyType.Subject:
if pft2 == PolyFillType.EvenOdd or pft2 == PolyFillType.NonZero:
return edge.windCnt2 == 0
elif edge.PolyType == PolyFillType.Positive:
return edge.windCnt2 <= 0
else:
return edge.windCnt2 >= 0
else:
if pft2 == PolyFillType.EvenOdd or pft2 == PolyFillType.NonZero:
return edge.windCnt2 != 0
elif pft2 == PolyFillType.Positive:
return edge.windCnt2 > 0
else:
return edge.windCnt2 < 0
else: # self._ClipType == ClipType.XOR: ###########
return True
#边的属性记录相关,6个方法,
def _AddEdgeToSEL(self, edge):
if self._SortedEdges is None:
self._SortedEdges = edge
edge.prevInSEL = None
edge.nextInSEL = None
else:
# add edge to front of list ...
edge.nextInSEL = self._SortedEdges
edge.prevInSEL = None
self._SortedEdges.prevInSEL = edge
self._SortedEdges = edge
def _CopyAELToSEL(self):
e = self._ActiveEdges
self._SortedEdges = e
while e is not None:
e.prevInSEL = e.prevInAEL
e.nextInSEL = e.nextInAEL
e = e.nextInAEL
def _InsertEdgeIntoAEL(self, edge):
edge.prevInAEL = None
edge.nextInAEL = None
if self._ActiveEdges is None:
self._ActiveEdges = edge
elif _E2InsertsBeforeE1(self._ActiveEdges, edge):
edge.nextInAEL = self._ActiveEdges
self._ActiveEdges.prevInAEL = edge
self._ActiveEdges = edge
else:
e = self._ActiveEdges
while e.nextInAEL is not None and \
not _E2InsertsBeforeE1(e.nextInAEL, edge):
e = e.nextInAEL
edge.nextInAEL = e.nextInAEL
if e.nextInAEL is not None: e.nextInAEL.prevInAEL = edge
edge.prevInAEL = e
e.nextInAEL = edge
def _InsertLocalMinimaIntoAEL(self, botY):
while self._CurrentLocMin is not None and \
self._CurrentLocMin.y == botY:
lb = self._CurrentLocMin.leftBound
rb = self._CurrentLocMin.rightBound
self._InsertEdgeIntoAEL(lb)
self._InsertScanbeam(lb.yTop)
self._InsertEdgeIntoAEL(rb)
if self._IsEvenOddFillType(lb):
lb.windDelta = 1
rb.windDelta = 1
else:
rb.windDelta = -lb.windDelta
self._SetWindingCount(lb)
rb.windCnt = lb.windCnt
rb.windCnt2 = lb.windCnt2
if rb.dx == horizontal:
self._AddEdgeToSEL(rb)
self._InsertScanbeam(rb.nextInLML.yTop)
else:
self._InsertScanbeam(rb.yTop)
if self._IsContributing(lb):
self._AddLocalMinPoly(lb, rb, Point(lb.xCurr, self._CurrentLocMin.y))
if rb.outIdx >= 0 and rb.dx == horizontal and self._HorzJoins is not None:
hj = self._HorzJoins
while True:
dummy1, dummy2, overlap = _GetOverlapSegment(Point(hj.edge.xBot, hj.edge.yBot),
Point(hj.edge.xTop, hj.edge.yTop),
Point(rb.xBot, rb.yBot),
Point(rb.xTop, rb.yTop))
if overlap:
self._AddJoin(hj.edge, rb, hj.savedIdx)
hj = hj.nextHj
if hj == self._HorzJoins: break
if (lb.nextInAEL != rb):
if rb.outIdx >= 0 and rb.prevInAEL.outIdx >= 0 and _SlopesEqual2(rb.prevInAEL, rb):
self._AddJoin(rb, rb.prevInAEL)
e = lb.nextInAEL
pt = Point(lb.xCurr, lb.yCurr)
while e != rb:
self._IntersectEdges(rb, e, pt)
e = e.nextInAEL
self._PopLocalMinima()
def _SwapPositionsInAEL(self, e1, e2):
if e1.nextInAEL == e2:
nextE = e2.nextInAEL
if nextE is not None: nextE.prevInAEL = e1
prevE = e1.prevInAEL
if prevE is not None: prevE.nextInAEL = e2
e2.prevInAEL = prevE
e2.nextInAEL = e1
e1.prevInAEL = e2
e1.nextInAEL = nextE
elif e2.nextInAEL == e1:
nextE = e1.nextInAEL
if nextE is not None: nextE.prevInAEL = e2
prevE = e2.prevInAEL
if prevE is not None: prevE.nextInAEL = e1
e1.prevInAEL = prevE
e1.nextInAEL = e2
e2.prevInAEL = e1
e2.nextInAEL = nextE
else:
nextE = e1.nextInAEL
prevE = e1.prevInAEL
e1.nextInAEL = e2.nextInAEL
if e1.nextInAEL is not None: e1.nextInAEL.prevInAEL = e1
e1.prevInAEL = e2.prevInAEL
if e1.prevInAEL is not None: e1.prevInAEL.nextInAEL = e1
e2.nextInAEL = nextE
if e2.nextInAEL is not None: e2.nextInAEL.prevInAEL = e2
e2.prevInAEL = prevE
if e2.prevInAEL is not None: e2.prevInAEL.nextInAEL = e2
if e1.prevInAEL is None: self._ActiveEdges = e1
elif e2.prevInAEL is None: self._ActiveEdges = e2
def _SwapPositionsInSEL(self, e1, e2):
if e1.nextInSEL == e2:
nextE = e2.nextInSEL
if nextE is not None: nextE.prevInSEL = e1
prevE = e1.prevInSEL
if prevE is not None: prevE.nextInSEL = e2
e2.prevInSEL = prevE
e2.nextInSEL = e1
e1.prevInSEL = e2
e1.nextInSEL = nextE
elif e2.nextInSEL == e1:
nextE = e1.nextInSEL
if nextE is not None: nextE.prevInSEL = e2
prevE = e2.prevInSEL
if prevE is not None: prevE.nextInSEL = e1
e1.prevInSEL = prevE
e1.nextInSEL = e2
e2.prevInSEL = e1
e2.nextInSEL = nextE
else:
nextE = e1.nextInSEL
prevE = e1.prevInSEL
e1.nextInSEL = e2.nextInSEL
e1.nextInSEL = e2.nextInSEL
if e1.nextInSEL is not None: e1.nextInSEL.prevInSEL = e1
e1.prevInSEL = e2.prevInSEL
if e1.prevInSEL is not None: e1.prevInSEL.nextInSEL = e1
e2.nextInSEL = nextE
if e2.nextInSEL is not None: e2.nextInSEL.prevInSEL = e2
e2.prevInSEL = prevE
if e2.prevInSEL is not None: e2.prevInSEL.nextInSEL = e2
if e1.prevInSEL is None: self._SortedEdges = e1
elif e2.prevInSEL is None: self._SortedEdges = e2
#有关水平特例的处理, 3个方法
def _IsTopHorz(self, xPos):
e = self._SortedEdges
while e is not None:
if (xPos >= min(e.xCurr,e.xTop)) and (xPos <= max(e.xCurr,e.xTop)):
return False
e = e.nextInSEL
return True
def _ProcessHorizontal(self, horzEdge):
if horzEdge.xCurr < horzEdge.xTop:
horzLeft = horzEdge.xCurr
horzRight = horzEdge.xTop
direction = Direction.LeftToRight
else:
horzLeft = horzEdge.xTop
horzRight = horzEdge.xCurr
direction = Direction.RightToLeft
eMaxPair = None
if horzEdge.nextInLML is None:
eMaxPair = _GetMaximaPair(horzEdge)
e = _GetnextInAEL(horzEdge, direction)
while e is not None:
if (e.xCurr == horzEdge.xTop) and eMaxPair is None:
if _SlopesEqual2(e, horzEdge.nextInLML):
if horzEdge.outIdx >= 0 and e.outIdx >= 0:
self._AddJoin(horzEdge.nextInLML, e, horzEdge.outIdx)
break
elif e.dx < horzEdge.nextInLML.dx: break
eNext = _GetnextInAEL(e, direction)
if eMaxPair is not None or \
((direction == Direction.LeftToRight) and (e.xCurr < horzRight)) or \
((direction == Direction.RightToLeft) and (e.xCurr > horzLeft)):
if e == eMaxPair:
if direction == Direction.LeftToRight:
self._IntersectEdges(horzEdge, e, Point(e.xCurr, horzEdge.yCurr))
else:
self._IntersectEdges(e, horzEdge, Point(e.xCurr, horzEdge.yCurr))
return
elif e.dx == horizontal and not _IsMinima(e) and e.xCurr <= e.xTop:
if direction == Direction.LeftToRight:
self._IntersectEdges(horzEdge, e, Point(e.xCurr, horzEdge.yCurr),
_ProtectRight(not self._IsTopHorz(e.xCurr)))
else:
self._IntersectEdges(e, horzEdge, Point(e.xCurr, horzEdge.yCurr),
_ProtectLeft(not self._IsTopHorz(e.xCurr)))
elif (direction == Direction.LeftToRight):
self._IntersectEdges(horzEdge, e, Point(e.xCurr, horzEdge.yCurr),
_ProtectRight(not self._IsTopHorz(e.xCurr)))
else:
self._IntersectEdges(e, horzEdge, Point(e.xCurr, horzEdge.yCurr),
_ProtectLeft(not self._IsTopHorz(e.xCurr)))
self._SwapPositionsInAEL(horzEdge, e)
elif ((direction == Direction.LeftToRight and e.xCurr >= horzRight) or \
(direction == Direction.RightToLeft and e.xCurr <= horzLeft)): break
e = eNext
if horzEdge.nextInLML is not None:
if horzEdge.outIdx >= 0:
self._AddOutPt(horzEdge, Point(horzEdge.xTop, horzEdge.yTop))
self._UpdateEdgeIntoAEL(horzEdge)
else:
if horzEdge.outIdx >= 0:
self._IntersectEdges(horzEdge, eMaxPair, \
Point(horzEdge.xTop, horzEdge.yCurr), Protects.Both)
if eMaxPair.outIdx >= 0: raise Exception("Clipper: Horizontal Error")
self._DeleteFromAEL(eMaxPair)
self._DeleteFromAEL(horzEdge)
def _ProcessHorizontals(self):
while self._SortedEdges is not None:
e = self._SortedEdges
self._DeleteFromSEL(e)
self._ProcessHorizontal(e)
#连接的处理,3个方法
def _AddJoin(self, e1, e2, e1OutIdx = -1, e2OutIdx = -1):
jr = JoinRec()
if e1OutIdx >= 0: jr.poly1Idx = e1OutIdx
else: jr.poly1Idx = e1.outIdx
jr.pt1a = Point(e1.xCurr, e1.yCurr)
jr.pt1b = Point(e1.xTop, e1.yTop)
if e2OutIdx >= 0: jr.poly2Idx = e2OutIdx
else: jr.poly2Idx = e2.outIdx
jr.pt2a = Point(e2.xCurr, e2.yCurr)
jr.pt2b = Point(e2.xTop, e2.yTop)
if self._JoinList is None:
self._JoinList = []
self._JoinList.append(jr)
def _FixupJoinRecs(self, jr, outPt, startIdx):
for i in range(startIdx, len(self._JoinList)):
jr2 = self._JoinList[i]
if jr2.poly1Idx == jr.poly1Idx and _PointIsVertex(jr2.pt1a, outPt):
jr2.poly1Idx = jr.poly2Idx
if jr2.poly2Idx == jr.poly1Idx and _PointIsVertex(jr2.pt2a, outPt):
jr2.poly2Idx = jr.poly2Idx
def _AddHorzJoin(self, e, idx):
hj = HorzJoin(e, idx)
if self._HorzJoins == None:
self._HorzJoins = hj
hj.nextHj = hj
hj.prevHj = hj
else:
hj.nextHj = self._HorzJoins
hj.prevHj = self._HorzJoins.prevHj
self._HorzJoins.prevHj.nextHj = hj
self._HorzJoins.prevHj = hj
#交点的处理, 4个方法
def _InsertIntersectNode(self, e1, e2, pt):
newNode = IntersectNode(e1, e2, pt)
if self._IntersectNodes is None:
self._IntersectNodes = newNode
elif newNode.pt.y > self._IntersectNodes.pt.y:
newNode.nextIn = self._IntersectNodes
self._IntersectNodes = newNode
else:
node = self._IntersectNodes
while node.nextIn is not None and \
newNode.pt.y < node.nextIn.pt.y:
node = node.nextIn
newNode.nextIn = node.nextIn
node.nextIn = newNode
def _ProcessIntersections(self, botY, topY):
try:
self._BuildIntersectList(botY, topY)
if self._IntersectNodes is None: return True
if self._IntersectNodes.nextIn is not None and \
not self._FixupIntersectionOrder(): return False
self._ProcessIntersectList()
return True
finally:
self._IntersectNodes = None
self._SortedEdges = None
def _BuildIntersectList(self, botY, topY):
e = self._ActiveEdges
if e is None: return
self._SortedEdges = e
while e is not None:
e.prevInSEL = e.prevInAEL
e.nextInSEL = e.nextInAEL
e.xCurr = _TopX(e, topY)
e = e.nextInAEL
while True:
isModified = False
e = self._SortedEdges
while e.nextInSEL is not None:
eNext = e.nextInSEL
if e.xCurr <= eNext.xCurr:
e = eNext
continue
pt, intersected = _IntersectPoint(e, eNext)
if not intersected and e.xCurr > eNext.xCurr +1:
raise Exception("Intersect Error")
if pt.y > botY:
pt = Point(_TopX(e, botY), botY)
self._InsertIntersectNode(e, eNext, pt)
self._SwapPositionsInSEL(e, eNext)
isModified = True
if e.prevInSEL is not None:
e.prevInSEL.nextInSEL = None
else:
break
if not isModified: break
self._SortedEdges = None
return
def _ProcessIntersectList(self):
while self._IntersectNodes is not None:
node = self._IntersectNodes
self._IntersectEdges(node.e1, node.e2, node.pt, Protects.Both)
self._SwapPositionsInAEL(node.e1, node.e2)
self._IntersectNodes = node.nextIn
#删除相关, 2个方法
def _DeleteFromAEL(self, e):
aelPrev = e.prevInAEL
aelNext = e.nextInAEL
if aelPrev is None and aelNext is None and e != self._ActiveEdges:
return
if aelPrev is not None:
aelPrev.nextInAEL = aelNext
else:
self._ActiveEdges = aelNext
if aelNext is not None:
aelNext.prevInAEL = aelPrev
e.nextInAEL = None
e.prevInAEL = None
def _DeleteFromSEL(self, e):
SELPrev = e.prevInSEL
SELNext = e.nextInSEL
if SELPrev is None and SELNext is None and e != self._SortedEdges:
return
if SELPrev is not None:
SELPrev.nextInSEL = SELNext
else:
self._SortedEdges = SELNext
if SELNext is not None:
SELNext.prevInSEL = SELPrev
e.nextInSEL = None
e.prevInSEL = None
#交点导致的线段边
def _IntersectEdges(self, e1, e2, pt, protects = Protects.Neither):
e1stops = protects & Protects.Left == 0 and \
e1.nextInLML is None and \
e1.xTop == pt.x and e1.yTop == pt.y
e2stops = protects & Protects.Right == 0 and \
e2.nextInLML is None and \
e2.xTop == pt.x and e2.yTop == pt.y
e1Contributing = e1.outIdx >= 0
e2contributing = e2.outIdx >= 0
if e1.PolyType == e2.PolyType:
if self._IsEvenOddFillType(e1):
e1Wc = e1.windCnt
e1.windCnt = e2.windCnt
e2.windCnt = e1Wc
else:
if e1.windCnt + e2.windDelta == 0: e1.windCnt = -e1.windCnt
else: e1.windCnt += e2.windDelta
if e2.windCnt - e1.windDelta == 0: e2.windCnt = -e2.windCnt
else: e2.windCnt -= e1.windDelta
else:
if not self._IsEvenOddFillType(e2): e1.windCnt2 += e2.windDelta
elif e1.windCnt2 == 0: e1.windCnt2 = 1
else: e1.windCnt2 = 0
if not self._IsEvenOddFillType(e1): e2.windCnt2 -= e1.windDelta
elif e2.windCnt2 == 0: e2.windCnt2 = 1
else: e2.windCnt2 = 0
if e1.PolyType == PolyType.Subject:
e1FillType = self._SubjFillType
e1FillType2 = self._ClipFillType
else:
e1FillType = self._ClipFillType
e1FillType2 = self._SubjFillType
if e2.PolyType == PolyType.Subject:
e2FillType = self._SubjFillType
e2FillType2 = self._ClipFillType
else:
e2FillType = self._ClipFillType
e2FillType2 = self._SubjFillType
if e1FillType == PolyFillType.Positive: e1Wc = e1.windCnt
elif e1FillType == PolyFillType.Negative: e1Wc = -e1.windCnt
else: e1Wc = abs(e1.windCnt)
if e2FillType == PolyFillType.Positive: e2Wc = e2.windCnt
elif e2FillType == PolyFillType.Negative: e2Wc = -e2.windCnt
else: e2Wc = abs(e2.windCnt)
if e1Contributing and e2contributing:
if e1stops or e2stops or \
(e1Wc != 0 and e1Wc != 1) or (e2Wc != 0 and e2Wc != 1) or \
(e1.PolyType != e2.PolyType and self._ClipType != ClipType.Xor):
self._AddLocalMaxPoly(e1, e2, pt)
else:
self._AddOutPt(e1, pt)
self._AddOutPt(e2, pt)
_SwapSides(e1, e2)
_SwapPolyIndexes(e1, e2)
elif e1Contributing:
if (e2Wc == 0 or e2Wc == 1):
self._AddOutPt(e1, pt)
_SwapSides(e1, e2)
_SwapPolyIndexes(e1, e2)
elif e2contributing:
if (e1Wc == 0 or e1Wc == 1):
self._AddOutPt(e2, pt)
_SwapSides(e1, e2)
_SwapPolyIndexes(e1, e2)
elif (e1Wc == 0 or e1Wc == 1) and (e2Wc == 0 or e2Wc == 1) and \
not e1stops and not e2stops:
e1FillType2 = e2FillType2 = PolyFillType.EvenOdd
if e1FillType2 == PolyFillType.Positive: e1Wc2 = e1.windCnt2
elif e1FillType2 == PolyFillType.Negative: e1Wc2 = -e1.windCnt2
else: e1Wc2 = abs(e1.windCnt2)
if e2FillType2 == PolyFillType.Positive: e2Wc2 = e2.windCnt2
elif e2FillType2 == PolyFillType.Negative: e2Wc2 = -e2.windCnt2
else: e2Wc2 = abs(e2.windCnt2)
if e1.PolyType != e2.PolyType:
self._AddLocalMinPoly(e1, e2, pt)
elif e1Wc == 1 and e2Wc == 1:
if self._ClipType == ClipType.Intersection:
if e1Wc2 > 0 and e2Wc2 > 0:
self._AddLocalMinPoly(e1, e2, pt)
elif self._ClipType == ClipType.Union:
if e1Wc2 <= 0 and e2Wc2 <= 0:
self._AddLocalMinPoly(e1, e2, pt)
elif self._ClipType == ClipType.Difference:
if (e1.PolyType == PolyType.Clip and e1Wc2 > 0 and e2Wc2 > 0) or \
(e1.PolyType == PolyType.Subject and e1Wc2 <= 0 and e2Wc2 <= 0):
self._AddLocalMinPoly(e1, e2, pt)
else:
self._AddLocalMinPoly(e1, e2, pt)
else:
_SwapSides(e1, e2, self._PolyOutList)
if e1stops != e2stops and \
((e1stops and e1.outIdx >= 0) or (e2stops and e2.outIdx >= 0)):
_SwapSides(e1, e2, self._PolyOutList)
_SwapPolyIndexes(e1, e2)
if e1stops: self._DeleteFromAEL(e1)
if e2stops: self._DeleteFromAEL(e2)
#以下10个方法
def _DoMaxima(self, e, topY):
eMaxPair = _GetMaximaPair(e)
x = e.xTop
eNext = e.nextInAEL
while eNext != eMaxPair:
if eNext is None: raise Exception("DoMaxima error")
self._IntersectEdges(e, eNext, Point(x, topY), Protects.Both)
self._SwapPositionsInAEL(e, eNext)
eNext = e.nextInAEL
if e.outIdx < 0 and eMaxPair.outIdx < 0:
self._DeleteFromAEL(e)
self._DeleteFromAEL(eMaxPair)
elif e.outIdx >= 0 and eMaxPair.outIdx >= 0:
self._IntersectEdges(e, eMaxPair, Point(x, topY))
else:
raise Exception("DoMaxima error")
def _UpdateEdgeIntoAEL(self, e):
if e.nextInLML is None:
raise Exception("UpdateEdgeIntoAEL error")
aelPrev = e.prevInAEL
aelNext = e.nextInAEL
e.nextInLML.outIdx = e.outIdx
if aelPrev is not None:
aelPrev.nextInAEL = e.nextInLML
else:
self._ActiveEdges = e.nextInLML
if aelNext is not None:
aelNext.prevInAEL = e.nextInLML
e.nextInLML.side = e.side
e.nextInLML.windDelta = e.windDelta
e.nextInLML.windCnt = e.windCnt
e.nextInLML.windCnt2 = e.windCnt2
e = e.nextInLML
e.prevInAEL = aelPrev
e.nextInAEL = aelNext
if e.dx != horizontal:
self._InsertScanbeam(e.yTop)
return e
def _AddLocalMinPoly(self, e1, e2, pt):
if e2.dx == horizontal or e1.dx > e2.dx:
self._AddOutPt(e1, pt)
e2.outIdx = e1.outIdx
e1.side = EdgeSide.Left
e2.side = EdgeSide.Right
e = e1
if e.prevInAEL == e2: prevE = e2.prevInAEL
else: prevE = e1.prevInAEL
else:
self._AddOutPt(e2, pt)
e1.outIdx = e2.outIdx
e1.side = EdgeSide.Right
e2.side = EdgeSide.Left
e = e2
if e.prevInAEL == e1: prevE = e1.prevInAEL
else: prevE = e.prevInAEL
if prevE is not None and prevE.outIdx >= 0 and \
_TopX(prevE, pt.y) == _TopX(e, pt.y) and \
_SlopesEqual2(e, prevE):
self._AddJoin(e, prevE)
return
def _AddLocalMaxPoly(self, e1, e2, pt):
self._AddOutPt(e1, pt)
if e1.outIdx == e2.outIdx:
e1.outIdx = -1
e2.outIdx = -1
elif e1.outIdx < e2.outIdx:
self._AppendPolygon(e1, e2)
else:
self._AppendPolygon(e2, e1)
def _CreateOutRec(self):
outRec = OutRec(len(self._PolyOutList))
self._PolyOutList.append(outRec)
return outRec
def _AddOutPt(self, e, pt):
toFront = e.side == EdgeSide.Left
if e.outIdx < 0:
outRec = self._CreateOutRec();
e.outIdx = outRec.idx
op = OutPt(outRec.idx, pt)
op.nextOp = op
op.prevOp = op
outRec.pts = op
_SetHoleState(e, outRec, self._PolyOutList)
else:
outRec = self._PolyOutList[e.outIdx]
op = outRec.pts
if (toFront and _PointsEqual(pt, op.pt)) or \
(not toFront and _PointsEqual(pt, op.prevOp.pt)): return
op2 = OutPt(outRec.idx, pt)
op2.nextOp = op
op2.prevOp = op.prevOp
op.prevOp.nextOp = op2
op.prevOp = op2
if toFront: outRec.pts = op2
def _AppendPolygon(self, e1, e2):
outRec1 = self._PolyOutList[e1.outIdx]
outRec2 = self._PolyOutList[e2.outIdx]
holeStateRec = None
if _Param1RightOfParam2(outRec1, outRec2): holeStateRec = outRec2
elif _Param1RightOfParam2(outRec2, outRec1): holeStateRec = outRec1
else: holeStateRec = _GetLowermostRec(outRec1, outRec2)
p1_lft = outRec1.pts
p2_lft = outRec2.pts
p1_rt = p1_lft.prevOp
p2_rt = p2_lft.prevOp
newSide = EdgeSide.Left
if e1.side == EdgeSide.Left:
if e2.side == EdgeSide.Left:
# z y x a b c
_ReversePolyPtLinks(p2_lft)
p2_lft.nextOp = p1_lft
p1_lft.prevOp = p2_lft
p1_rt.nextOp = p2_rt
p2_rt.prevOp = p1_rt
outRec1.pts = p2_rt
else:
# x y z a b c
p2_rt.nextOp = p1_lft
p1_lft.prevOp = p2_rt
p2_lft.prevOp = p1_rt
p1_rt.nextOp = p2_lft
outRec1.pts = p2_lft
else:
newSide = EdgeSide.Right
if e2.side == EdgeSide.Right:
# a b c z y x
_ReversePolyPtLinks(p2_lft)
p1_rt.nextOp = p2_rt
p2_rt.prevOp = p1_rt
p2_lft.nextOp = p1_lft
p1_lft.prevOp = p2_lft
else:
# a b c x y z
p1_rt.nextOp = p2_lft
p2_lft.prevOp = p1_rt
p1_lft.prevOp = p2_rt
p2_rt.nextOp = p1_lft
outRec1.bottomPt = None
if holeStateRec == outRec2:
if outRec2.FirstLeft != outRec1:
outRec1.FirstLeft = outRec2.FirstLeft
outRec1.isHole = outRec2.isHole
outRec2.pts = None
outRec2.bottomPt = None
outRec2.FirstLeft = outRec1
OKIdx = outRec1.idx
ObsoleteIdx = outRec2.idx
e1.outIdx = -1
e2.outIdx = -1
e = self._ActiveEdges
while e is not None:
if e.outIdx == ObsoleteIdx:
e.outIdx = OKIdx
e.side = newSide
break
e = e.nextInAEL
outRec2.idx = outRec1.idx
#交点排序
def _FixupIntersectionOrder(self):
self._CopyAELToSEL()
inode = self._IntersectNodes
while inode is not None:
if (not _EdgesAdjacent(inode)):
nextNode = inode.nextIn
while (nextNode and not _EdgesAdjacent(nextNode)):
nextNode = nextNode.nextIn
if (nextNode is None): return False
e1 = inode.e1
e2 = inode.e2
p = inode.pt
inode.e1 = nextNode.e1
inode.e2 = nextNode.e2
inode.pt = nextNode.pt
nextNode.e1 = e1
nextNode.e2 = e2
nextNode.pt = p
self._SwapPositionsInSEL(inode.e1, inode.e2);
inode = inode.nextIn
return True
#扫描线
def _ProcessEdgesAtTopOfScanbeam(self, topY):
e = self._ActiveEdges
while e is not None:
if _IsMaxima(e, topY) and _GetMaximaPair(e).dx != horizontal:
ePrev = e.prevInAEL
self._DoMaxima(e, topY)
if ePrev is None: e = self._ActiveEdges
else: e = ePrev.nextInAEL
else:
intermediateVert = _IsIntermediate(e, topY)
if intermediateVert and e.nextInLML.dx == horizontal:
if e.outIdx >= 0:
self._AddOutPt(e, Point(e.xTop, e.yTop))
hj = self._HorzJoins
if hj is not None:
while True:
_1, _2, overlap = _GetOverlapSegment(
Point(hj.edge.xBot, hj.edge.yBot),
Point(hj.edge.xTop, hj.edge.yTop),
Point(e.nextInLML.XBot, e.nextInLML.yBot),
Point(e.nextInLML.xTop, e.nextInLML.yTop))
if overlap: self._AddJoin(hj.edge, e.nextInLML, hj.savedIdx, e.outIdx)
hj = hj.nextHj
if hj == self._HorzJoins: break
self._AddHorzJoin(e.nextInLML, e.outIdx)
e = self._UpdateEdgeIntoAEL(e)
self._AddEdgeToSEL(e)
else:
e.xCurr = _TopX(e, topY)
e.yCurr = topY
if (self.ForceSimple and e.prevInAEL is not None and
e.prevInAEL.xCurr == e.xCurr and
e.outIdx >= 0 and e.prevInAEL.outIdx >= 0):
if (intermediateVert):
self._AddOutPt(e.prevInAEL, Point(e.xCurr, topY));
else:
self._AddOutPt(e, Point(e.xCurr, topY))
e = e.nextInAEL
self._ProcessHorizontals()
e = self._ActiveEdges
while e is not None:
if _IsIntermediate(e, topY):
if (e.outIdx >= 0) :
self._AddOutPt(e, Point(e.xTop, e.yTop))
e = self._UpdateEdgeIntoAEL(e)
ePrev = e.prevInAEL
eNext = e.nextInAEL
if ePrev is not None and ePrev.xCurr == e.xBot and \
(ePrev.yCurr == e.yBot) and (e.outIdx >= 0) and \
(ePrev.outIdx >= 0) and (ePrev.yCurr > ePrev.yTop) and \
_SlopesEqual2(e, ePrev):
self._AddOutPt(ePrev, Point(e.xBot, e.yBot))
self._AddJoin(e, ePrev)
elif eNext is not None and (eNext.xCurr == e.xBot) and \
(eNext.yCurr == e.yBot) and (e.outIdx >= 0) and \
(eNext.outIdx >= 0) and (eNext.yCurr > eNext.yTop) and \
_SlopesEqual2(e, eNext):
self._AddOutPt(eNext, Point(e.xBot, e.yBot))
self._AddJoin(e, eNext)
e = e.nextInAEL
def _Area(self, pts):
# see http://www.mathopenref.com/coordpolygonarea2.html
result = 0.0
p = pts
while True:
result += (p.pt.x + p.prevOp.pt.x) * (p.prevOp.pt.y - p.pt.y)
p = p.nextOp
if p == pts: break
return result / 2
#倒数第三步处理:点的处理, 4个方法
def _JoinPoints(self, jr):
p1, p2 = None, None
outRec1 = self._PolyOutList[jr.poly1Idx]
outRec2 = self._PolyOutList[jr.poly2Idx]
if outRec1 is None or outRec2 is None: return p1, p2, False
pp1a = outRec1.pts; pp2a = outRec2.pts
pt1 = jr.pt2a; pt2 = jr.pt2b
pt3 = jr.pt1a; pt4 = jr.pt1b
pp1a, pt1, pt2, result = _FindSegment(pp1a, pt1, pt2)
if not result: return p1, p2, False
if (outRec1 == outRec2):
pp2a = pp1a.nextOp
pp2a, pt3, pt4, result = _FindSegment(pp2a, pt3, pt4)
if not result or pp2a == pp1a: return p1, p2, False
else:
pp2a, pt3, pt4, result = _FindSegment(pp2a, pt3, pt4)
if not result: return p1, p2, False
pt1, pt2, result = _GetOverlapSegment(pt1, pt2, pt3, pt4)
if not result: return p1, p2, False
prevOp = pp1a.prevOp
if _PointsEqual(pp1a.pt, pt1): p1 = pp1a
elif _PointsEqual(prevOp.pt, pt1): p1 = prevOp
else: p1 = _InsertPolyPtBetween(pp1a, prevOp, pt1)
if _PointsEqual(pp1a.pt, pt2): p2 = pp1a
elif _PointsEqual(prevOp.pt, pt2): p2 = prevOp
elif (p1 == pp1a) or (p1 == prevOp):
p2 = _InsertPolyPtBetween(pp1a, prevOp, pt2)
elif _Pt3IsBetweenPt1AndPt2(pp1a.pt, p1.pt, pt2):
p2 = _InsertPolyPtBetween(pp1a, p1, pt2)
else: p2 = _InsertPolyPtBetween(p1, prevOp, pt2)
prevOp = pp2a.prevOp
if _PointsEqual(pp2a.pt, pt1): p3 = pp2a
elif _PointsEqual(prevOp.pt, pt1): p3 = prevOp
else: p3 = _InsertPolyPtBetween(pp2a, prevOp, pt1)
if _PointsEqual(pp2a.pt, pt2): p4 = pp2a
elif _PointsEqual(prevOp.pt, pt2): p4 = prevOp
elif (p3 == pp2a) or (p3 == prevOp):
p4 = _InsertPolyPtBetween(pp2a, prevOp, pt2)
elif _Pt3IsBetweenPt1AndPt2(pp2a.pt, p3.pt, pt2):
p4 = _InsertPolyPtBetween(pp2a, p3, pt2)
else: p4 = _InsertPolyPtBetween(p3, prevOp, pt2)
if p1.nextOp == p2 and p3.prevOp == p4:
p1.nextOp = p3
p3.prevOp = p1
p2.prevOp = p4
p4.nextOp = p2
return p1, p2, True
elif p1.prevOp == p2 and p3.nextOp == p4:
p1.prevOp = p3
p3.nextOp = p1
p2.nextOp = p4
p4.prevOp = p2
return p1, p2, True
return p1, p2, False
def _FixupFirstLefts1(self, oldOutRec, newOutRec):
for outRec in self._PolyOutList:
if outRec.pts is not None and outRec.FirstLeft == oldOutRec:
if _Poly2ContainsPoly1(outRec.pts, newOutRec.pts):
outRec.FirstLeft = newOutRec
def _FixupFirstLefts2(self, oldOutRec, newOutRec):
for outRec in self._PolyOutList:
if outRec.FirstLeft == oldOutRec: outRec.FirstLeft = newOutRec
def _GetOutRec(self, idx):
outrec = self._PolyOutList[idx]
while (outrec != self._PolyOutList[outrec.idx]):
outrec = self._PolyOutList[outrec.idx]
return outrec
#倒数第二步处理:边的处理, 2个方法
def _JoinCommonEdges(self):
for i in range(len(self._JoinList)):
jr = self._JoinList[i]
outRec1 = self._GetOutRec(jr.poly1Idx)
outRec2 = self._GetOutRec(jr.poly2Idx)
if outRec1.pts is None or outRec2.pts is None: continue
if outRec1 == outRec2: holeStateRec = outRec1
elif _Param1RightOfParam2(outRec1, outRec2): holeStateRec = outRec2
elif _Param1RightOfParam2(outRec2, outRec1): holeStateRec = outRec1
else: holeStateRec = _GetLowermostRec(outRec1, outRec2)
p1, p2, result = self._JoinPoints(jr)
if not result: continue
if outRec1 == outRec2:
outRec1.pts = p1
outRec1.bottomPt = None
outRec2 = self._CreateOutRec()
outRec2.pts = p2
jr.poly2Idx = outRec2.idx
if _Poly2ContainsPoly1(outRec2.pts, outRec1.pts):
outRec2.isHole = not outRec1.isHole
outRec2.FirstLeft = outRec1
self._FixupJoinRecs(jr, p2, i + 1)
if self._UsingPolyTree: self._FixupFirstLefts2(outRec2, outRec1)
_FixupOutPolygon(outRec1)
_FixupOutPolygon(outRec2)
if outRec2.isHole == self._Area(outRec2) > 0.0:
_ReversePolyPtLinks(outRec2.pts)
elif _Poly2ContainsPoly1(outRec1.pts, outRec2.pts):
outRec2.isHole = outRec1.isHole
outRec1.isHole = not outRec2.isHole
outRec2.FirstLeft = outRec1.FirstLeft
outRec1.FirstLeft = outRec2
self._FixupJoinRecs(jr, p2, i + 1)
if self._UsingPolyTree: self._FixupFirstLefts2(outRec1, outRec2)
_FixupOutPolygon(outRec1)
_FixupOutPolygon(outRec2)
if outRec1.isHole == self._Area(outRec1) > 0.0:
_ReversePolyPtLinks(outRec1.pts)
else:
outRec2.isHole = outRec1.isHole
outRec2.FirstLeft = outRec1.FirstLeft
self._FixupJoinRecs(jr, p2, i + 1)
if self._UsingPolyTree: self._FixupFirstLefts1(outRec1, outRec2)
_FixupOutPolygon(outRec1)
_FixupOutPolygon(outRec2)
else:
_FixupOutPolygon(outRec1)
outRec2.pts = None
outRec2.bottomPt = None
outRec2.idx = outRec1.idx
outRec1.isHole = holeStateRec.isHole
if holeStateRec == outRec2:
outRec1.FirstLeft = outRec2.FirstLeft
outRec2.FirstLeft = outRec1
if self._UsingPolyTree: self._FixupFirstLefts2(outRec2, outRec1)
return
def _DoSimplePolygons(self):
i = 0;
while i < len(self._PolyOutList):
outrec = self._PolyOutList[i]
i +=1
op = outrec.pts
if (op is None): continue
while True:
op2 = op.nextOp
while (op2 != outrec.pts):
if (_PointsEqual(op.pt, op2.pt) and op2.nextOp != op and op2.prevOp != op):
#split the polygon into two ...
op3 = op.prevOp
op4 = op2.prevOp
op.prevOp = op4
op4.nextOp = op
op2.prevOp = op3
op3.nextOp = op2
outrec.pts = op
outrec2 = self._CreateOutRec();
outrec2.pts = op2;
_UpdateOutPtIdxs(outrec2)
if (_Poly2ContainsPoly1(outrec2.pts, outrec.pts)):
#OutRec2 is contained by OutRec1 ...
outrec2.isHole = not outrec.isHole
outrec2.FirstLeft = outrec
elif (_Poly2ContainsPoly1(outrec.pts, outrec2.pts)):
#OutRec1 is contained by OutRec2 ...
outrec2.isHole = outrec.isHole
outrec.isHole = not outrec2.isHole
outrec2.FirstLeft = outrec.FirstLeft
outrec.FirstLeft = outrec2
else:
#the 2 polygons are separate ...
outrec2.isHole = outrec.isHole;
outrec2.FirstLeft = outrec.FirstLeft;
op2 = op; # ie get ready for the next iteration
op2 = op2.nextOp
op = op.nextOp
if op == outrec.pts: break
return
#倒数第一步处理:整体的处理, 5个方法
def _ExecuteInternal(self):
try:
try:
self._Reset()
if self._Scanbeam is None: return True
botY = self._PopScanbeam()
while True:
#把当前边的边界范围加入到活动边表中
self._InsertLocalMinimaIntoAEL(botY)
self._HorzJoins = None
#处理水平状况
self._ProcessHorizontals()
#移动扫描线
topY = self._PopScanbeam()
#处理交点
if not self._ProcessIntersections(botY, topY): return False
#处理当前扫描线上的边
self._ProcessEdgesAtTopOfScanbeam(topY)
botY = topY
#跳出条件
if self._Scanbeam is None and self._CurrentLocMin is None: break
for outRec in self._PolyOutList:
if outRec.pts is None: continue
_FixupOutPolygon(outRec)
if outRec.pts is None: continue
if outRec.isHole == (self._Area(outRec.pts) > 0.0):
_ReversePolyPtLinks(outRec.pts)
#连接边表
if self._JoinList is not None: self._JoinCommonEdges()
if self.ForceSimple: self._DoSimplePolygons()
return True
finally:
self._JoinList = None
self._HorzJoins = None
except:
return False
#运算入口
def Execute(
self,
clipType,
solution,
subjFillType = PolyFillType.EvenOdd,
clipFillType = PolyFillType.EvenOdd):
if self._ExecuteLocked: return False
try:
self._ExecuteLocked = True
self._UsingPolyTree = True
del solution[:]
self._SubjFillType = subjFillType
self._ClipFillType = clipFillType
self._ClipType = clipType
result = self._ExecuteInternal()
if result: self._BuildResult(solution)
finally:
self._ExecuteLocked = False
self._UsingPolyTree = False
return result
def Execute2(
self,
clipType,
solutionTree,
subjFillType = PolyFillType.EvenOdd,
clipFillType = PolyFillType.EvenOdd):
if self._ExecuteLocked: return False
try:
self._ExecuteLocked = True
self._UsingPolyTree = True
solutionTree.Clear()
self._SubjFillType = subjFillType
self._ClipFillType = clipFillType
self._ClipType = clipType
result = self._ExecuteInternal()
if result: self._BuildResult2(solutionTree)
finally:
self._ExecuteLocked = False
self._UsingPolyTree = False
return result
#获得多边形的顶点集
def _BuildResult(self, polygons):
for outRec in self._PolyOutList:
if outRec is None: continue
cnt = _PointCount(outRec.pts)
if (cnt < 3): continue
poly = []
op = outRec.pts
for _ in range(cnt):
poly.append(Point(op.pt.x, op.pt.y))
op = op.prevOp
polygons.append(poly)
return
def _BuildResult2(self, polyTree):
for outRec in self._PolyOutList:
if outRec is None: continue
cnt = _PointCount(outRec.pts)
if (cnt < 3): continue
_FixHoleLinkage(outRec)
# add nodes to _AllNodes list ...
polyNode = PolyNode()
polyTree._AllNodes.append(polyNode)
outRec.PolyNode = polyNode
op = outRec.pts
while True:
polyNode.Contour.append(op.pt)
op = op.prevOp
if op == outRec.pts: break
# build the tree ...
for outRec in self._PolyOutList:
if outRec.PolyNode is None: continue
if outRec.FirstLeft is None:
polyTree._AddChild(outRec.PolyNode)
else:
outRec.FirstLeft.PolyNode._AddChild(outRec.PolyNode)
return
#多边形布尔运算的求解
def tmp5():
# Load the polygons into Clipper and execute the boolean clip op ...
c = Clipper()
solution = []
pft = PolyFillType.EvenOdd
vert_subj = [[199, 133], [577, 464], [468, 386], [130, 128], [336, 140], [324, 418], [91, 223], [67, 455], [168, 458], [639, 227]]
vert_clip = [[233, 423], [193, 76], [369, 373], [538, 416], [155, 301], [215, 323], [82, 473], [136, 405], [466, 145], [250, 336], [480, 218], [135, 320], [492, 135], [40, 361], [467, 323], [347, 26]]
subj, clip = [], [];
for i in range(len(vert_subj)):
subj.append(Point(vert_subj[i][0], vert_subj[i][1]));
for i in range(len(vert_clip)):
clip.append(Point(vert_clip[i][0], vert_clip[i][1]));
c.AddPolygons([subj], PolyType.Subject)
c.AddPolygons([clip], PolyType.Clip)
result = c.Execute(ClipType.Intersection, solution, pft, pft)
scale = 1;
SaveToFile('./solution2.txt', solution, scale)
print('solved');
#多边形布尔运算的求解
def tmp_5():
# Load the polygons into Clipper and execute the boolean clip op ...
c = Clipper()
solution = []
pft = PolyFillType.EvenOdd
vert_subj = [[199, 133], [577, 464], [468, 386], [130, 128], [336, 140], [324, 418], [91, 223], [67, 455], [168, 458], [639, 227]]
vert_clip = [[233, 423], [193, 76], [369, 373], [538, 416], [155, 301], [215, 323], [82, 473], [136, 405], [466, 145], [250, 336], [480, 218], [135, 320], [492, 135], [40, 361], [467, 323], [347, 26]]
subj, clip = [], [];
for i in range(len(vert_subj)):
subj.append(Point(vert_subj[i][0], vert_subj[i][1]));
for i in range(len(vert_clip)):
clip.append(Point(vert_clip[i][0], vert_clip[i][1]));
c.AddPolygons([subj], PolyType.Subject)
c.AddPolygons([clip], PolyType.Clip)
result = c.Execute(ClipType.Intersection, solution, pft, pft)
scale = 1;
solution_ = [];
for i in range(len(solution)):
a = [];
for j in range(len(solution[i])):
a.append([solution[i][j].x, solution[i][j].y]);
solution_.append(a);
print(len(solution_));
print(solution_);
>>>
15
[[[223, 333], [225, 335], [223, 336], [224, 345], [211, 346], [157, 389], [97, 456], [96, 456], [136, 405], [157, 389], [193, 347], [211, 346], [223, 336]], [[235, 344], [252, 358], [233, 423], [224, 345]], [[490, 401], [514, 409], [516, 410], [494, 405]], [[341, 289], [394, 329], [346, 334], [363, 362], [362, 363], [327, 353], [328, 335], [346, 334], [329, 305], [329, 295]], [[165, 305], [135, 320], [175, 308], [165, 305], [166, 304], [158, 302], [175, 293], [179, 297], [166, 304], [177, 308], [175, 308], [207, 320], [213, 325], [193, 347], [77, 358], [79, 342], [158, 302]], [[480, 218], [434, 242], [463, 313], [438, 326], [421, 327], [364, 278], [434, 242], [430, 233]], [[188, 304], [201, 315], [177, 308]], [[331, 262], [331, 265], [321, 274], [313, 267]], [[309, 230], [331, 249], [331, 251], [312, 267], [301, 259], [290, 240]], [[421, 180], [425, 181], [414, 191], [430, 233], [342, 259], [340, 256], [414, 191], [412, 187]], [[409, 178], [408, 178], [412, 187], [333, 250], [331, 249], [333, 218]], [[293, 215], [307, 228], [288, 237], [290, 240], [285, 242], [284, 246], [282, 244], [285, 242], [286, 238], [288, 237], [287, 235]], [[200, 134], [257, 184], [287, 235], [286, 238], [279, 242], [206, 186]], [[315, 139], [336, 140], [335, 162], [407, 177], [333, 215], [335, 162], [309, 157]], [[199, 132], [227, 134], [231, 140], [200, 133]]]
>>>
#</span>
<span style="font-size:18px;">//
if (1) {
var r = 20;
config.setSector(8,15,7,2);
config.graphPaper2D(0, 0, r);
config.axis2D(0, 0, 450, 1.2);
//坐标轴设定
var scaleX = 2*r, scaleY = 2*r;
var spaceX = 50, spaceY = 50;
var xS = 0, xE = 640;
var yS = 0, yE = 480;
config.axisSpacing(xS, xE, spaceX, scaleX, 'X');
config.axisSpacing(yS, yE, spaceY, scaleY, 'Y');
var transform = new Transform();
var lable = [];
for (var i = 0; i < 100; i++) {
lable.push(i.toFixed(0));
}
var colorArray = ['red', 'orange', 'yellow', 'green', 'cyan', 'blue', 'purple'];
var color = 0;
var seg = [];
var subjEdges = $subj_edge.length;
var clipEdges = $clip_edge.length;
var solus = $solution.length;
for (var i = 0; i < solus; i++) {
seg = transform.scale($solution[i], scaleX/spaceX, scaleY/spaceY);
shape.fillDraw([].concat(seg), colorArray[i%7]);
}
for (var i = 0; i < subjEdges; i++) {
seg = transform.scale($subj_edge[i], scaleX/spaceX, scaleY/spaceY);
shape.multiLineDraw([].concat(seg), 'red');
}
for (var i = 0; i < clipEdges; i++) {
seg = transform.scale($clip_edge[i], scaleX/spaceX, scaleY/spaceY);
shape.multiLineDraw([].concat(seg), 'blue');
}
//主多边形
seg = transform.scale($vert_subj, scaleX/spaceX, scaleY/spaceY);
shape.pointDraw([].concat(seg), 'red', 1, 1, lable);
//从多边形
seg = transform.scale($vert_clip, scaleX/spaceX, scaleY/spaceY);
shape.pointDraw([].concat(seg), 'blue', 1, 1, lable);
plot.fillText('图:主多边形和从多边形的交集', 10, 60, 300);
}
//</span>
Execute()->_ExecuteInternal()/_BuildResult()
_ExecuteInternal()->_Reset()/_PopScanbeam()/_InsertLocalMinimaIntoAEL()/
_ProcessIntersections()/_ProcessEdgesAtTopOfScanbeam()/
_FixupOutPolygon()/_ReversePolyPtLinks()/
_JoinCommonEdges()/_DoSimplePolygons()
_Reset()->[]
_PopScanbeam()->[]
_InsertLocalMinimaIntoAEL()->_InsertEdgeIntoAEL()/_InsertScanbeam()/
_AddEdgeToSEL()/_AddLocalMinPoly()/_GetOverlapSegment()/_AddJoin()/
_IntersectEdges()/_PopLocalMinima()
_InsertScanbeam()->[]
_ProcessIntersections()->_BuildIntersectList()/_ProcessIntersectList()
_BuildIntersectList()->_InsertIntersectNode()/_SwapPositionsInSEL()
_BuildResult()->[]
_ProcessEdgesAtTopOfScanbeam()->_GetMaximaPair()/_DoMaxima()/
_GetOverlapSegment()/_AddJoin()/_AddHorzJoin()/_UpdateEdgeIntoAEL()/
_AddEdgeToSEL()/_AddOutPt()/_ProcessHorizontals()
_JoinPoints()->_FindSegment()
_FindSegment()->_GetOverlapSegment();
$vert_subj = [[199, 133], [577, 464], [468, 386], [130, 128], [336, 140], [324, 418], [91, 223], [67, 455], [168, 458], [639, 227]]
$vert_clip = [[233, 423], [193, 76], [369, 373], [538, 416], [155, 301], [215, 323], [82, 473], [136, 405], [466, 145], [250, 336], [480, 218], [135, 320], [492, 135], [40, 361], [467, 323], [347, 26]]
$subj_edge = [[[577, 464], [199, 133]],[[577, 464], [468, 386]],[[468, 386], [130, 128]],[[336, 140], [130, 128]],[[324, 418], [336, 140]],[[324, 418], [91, 223]],[[67, 455], [91, 223]],[[168, 458], [67, 455]],[[168, 458], [639, 227]],[[639, 227], [199, 133]],];
$clip_edge = [[[233, 423], [193, 76]],[[369, 373], [193, 76]],[[538, 416], [369, 373]],[[538, 416], [155, 301]],[[215, 323], [155, 301]],[[82, 473], [215, 323]],[[82, 473], [136, 405]],[[136, 405], [466, 145]],[[250, 336], [466, 145]],[[250, 336], [480, 218]],[[135, 320], [480, 218]],[[135, 320], [492, 135]],[[40, 361], [492, 135]],[[40, 361], [467, 323]],[[467, 323], [347, 26]],[[233, 423], [347, 26]],];
$solution_i = [[[223, 333], [225, 335], [223, 336], [224, 345], [211, 346], [157, 389], [97, 456], [96, 456], [136, 405], [157, 389], [193, 347], [211, 346], [223, 336]], [[235, 344], [252, 358], [233, 423], [224, 345]], [[490, 401], [514, 409], [516, 410], [494, 405]], [[341, 289], [394, 329], [346, 334], [363, 362], [362, 363], [327, 353], [328, 335], [346, 334], [329, 305], [329, 295]], [[165, 305], [135, 320], [175, 308], [165, 305], [166, 304], [158, 302], [175, 293], [179, 297], [166, 304], [177, 308], [175, 308], [207, 320], [213, 325], [193, 347], [77, 358], [79, 342], [158, 302]], [[480, 218], [434, 242], [463, 313], [438, 326], [421, 327], [364, 278], [434, 242], [430, 233]], [[188, 304], [201, 315], [177, 308]], [[331, 262], [331, 265], [321, 274], [313, 267]], [[309, 230], [331, 249], [331, 251], [312, 267], [301, 259], [290, 240]], [[421, 180], [425, 181], [414, 191], [430, 233], [342, 259], [340, 256], [414, 191], [412, 187]], [[409, 178], [408, 178], [412, 187], [333, 250], [331, 249], [333, 218]], [[293, 215], [307, 228], [288, 237], [290, 240], [285, 242], [284, 246], [282, 244], [285, 242], [286, 238], [288, 237], [287, 235]], [[200, 134], [257, 184], [287, 235], [286, 238], [279, 242], [206, 186]], [[315, 139], [336, 140], [335, 162], [407, 177], [333, 215], [335, 162], [309, 157]], [[199, 132], [227, 134], [231, 140], [200, 133]]];
$solution_u = [[[347, 26], [408, 177], [407, 177], [409, 178], [408, 177], [456, 153], [466, 145], [458, 152], [456, 153], [453, 155], [409, 178], [408, 178], [421, 180], [453, 155], [457, 153], [458, 152], [492, 135], [457, 153], [425, 181], [639, 227], [463, 313], [467, 323], [438, 326], [426, 332], [514, 409], [538, 416], [516, 410], [577, 464], [494, 405], [369, 373], [363, 364], [362, 363], [326, 381], [324, 418], [296, 395], [326, 381], [327, 353], [283, 339], [328, 335], [329, 305], [324, 298], [329, 295], [330, 281], [321, 274], [314, 280], [324, 298], [259, 331], [259, 332], [283, 339], [256, 342], [252, 358], [296, 395], [168, 458], [97, 456], [82, 473], [96, 456], [67, 455], [77, 358], [40, 361], [79, 342], [91, 223], [175, 293], [216, 273], [206, 186], [130, 128], [199, 132], [193, 76], [227, 134], [315, 139]], [[421, 327], [394, 329], [408, 340], [363, 362], [363, 364], [490, 401], [468, 386], [408, 340], [426, 332]], [[284, 246], [274, 279], [218, 295], [221, 321], [237, 326], [225, 335], [235, 344], [256, 342], [259, 332], [258, 332], [259, 331], [261, 327], [314, 280], [308, 270], [309, 268], [313, 267], [312, 267], [309, 268], [307, 269], [308, 270], [268, 301], [261, 327], [255, 331], [258, 332], [250, 336], [255, 331], [237, 326], [268, 301], [274, 279], [307, 269], [301, 259]], [[201, 315], [207, 320], [215, 323], [213, 325], [223, 333], [221, 321]], [[279, 242], [216, 273], [216, 278], [179, 297], [188, 304], [218, 295], [216, 278], [282, 244]], [[342, 259], [335, 261], [331, 265], [330, 281], [341, 289], [364, 278]], [[333, 250], [331, 251], [331, 262], [335, 261], [340, 256]], [[333, 215], [307, 228], [309, 230], [333, 218]], [[231, 140], [257, 184], [293, 215], [309, 157]], [[199, 133], [200, 134], [200, 133]]]
$solution_d = [[[494, 405], [516, 410], [577, 464]], [[225, 335], [235, 344], [224, 345], [233, 423], [252, 358], [296, 395], [168, 458], [97, 456], [157, 389], [211, 346], [224, 345], [223, 336]], [[213, 325], [223, 333], [223, 336], [211, 346], [193, 347], [157, 389], [136, 405], [96, 456], [67, 455], [77, 358], [193, 347]], [[327, 353], [362, 363], [326, 381], [324, 418], [296, 395], [326, 381]], [[438, 326], [426, 332], [514, 409], [490, 401], [468, 386], [408, 340], [426, 332], [421, 327]], [[394, 329], [408, 340], [363, 362], [346, 334]], [[79, 342], [91, 223], [175, 293], [158, 302], [166, 304], [165, 305], [175, 308], [135, 320], [165, 305], [158, 302]], [[329, 305], [346, 334], [328, 335]], [[179, 297], [188, 304], [177, 308], [201, 315], [207, 320], [175, 308], [177, 308], [166, 304]], [[425, 181], [639, 227], [463, 313], [434, 242], [480, 218], [430, 233], [434, 242], [364, 278], [342, 259], [430, 233], [414, 191]], [[331, 265], [330, 281], [341, 289], [329, 295], [330, 281], [321, 274]], [[331, 251], [331, 262], [313, 267], [312, 267]], [[307, 228], [309, 230], [290, 240], [301, 259], [284, 246], [285, 242], [290, 240], [288, 237]], [[408, 178], [421, 180], [412, 187], [414, 191], [340, 256], [333, 250], [412, 187]], [[257, 184], [293, 215], [287, 235], [288, 237], [286, 238], [285, 242], [282, 244], [279, 242], [286, 238], [287, 235]], [[407, 177], [409, 178], [333, 218], [333, 215]], [[130, 128], [199, 132], [200, 133], [199, 133], [200, 134], [206, 186]], [[227, 134], [315, 139], [309, 157], [231, 140]]]
$solution = [[[347, 26], [408, 177], [407, 177], [409, 178], [333, 218], [331, 249], [333, 250], [331, 251], [331, 262], [313, 267], [321, 274], [314, 280], [324, 298], [259, 331], [259, 332], [283, 339], [256, 342], [252, 358], [296, 395], [168, 458], [97, 456], [157, 389], [211, 346], [224, 345], [223, 336], [225, 335], [223, 333], [221, 321], [201, 315], [188, 304], [218, 295], [216, 278], [282, 244], [279, 242], [286, 238], [287, 235], [257, 184], [200, 134], [200, 133], [199, 133], [200, 134], [206, 186], [279, 242], [216, 273], [216, 278], [179, 297], [188, 304], [177, 308], [201, 315], [207, 320], [215, 323], [213, 325], [223, 333], [223, 336], [211, 346], [193, 347], [157, 389], [136, 405], [96, 456], [97, 456], [82, 473], [96, 456], [67, 455], [77, 358], [193, 347], [213, 325], [207, 320], [175, 308], [177, 308], [166, 304], [179, 297], [175, 293], [216, 273], [206, 186], [130, 128], [199, 132], [200, 133], [231, 140], [257, 184], [293, 215], [287, 235], [288, 237], [286, 238], [285, 242], [282, 244], [284, 246], [274, 279], [218, 295], [221, 321], [237, 326], [225, 335], [235, 344], [224, 345], [233, 423], [252, 358], [235, 344], [256, 342], [259, 332], [258, 332], [259, 331], [261, 327], [314, 280], [308, 270], [309, 268], [313, 267], [312, 267], [331, 251], [331, 249], [309, 230], [333, 218], [333, 215], [407, 177], [335, 162], [336, 140], [315, 139]], [[492, 135], [457, 153], [425, 181], [639, 227], [463, 313], [467, 323], [438, 326], [426, 332], [514, 409], [538, 416], [516, 410], [514, 409], [490, 401], [468, 386], [408, 340], [426, 332], [421, 327], [438, 326], [463, 313], [434, 242], [480, 218], [430, 233], [434, 242], [364, 278], [421, 327], [394, 329], [408, 340], [363, 362], [363, 364], [490, 401], [494, 405], [516, 410], [577, 464], [494, 405], [369, 373], [363, 364], [362, 363], [363, 362], [346, 334], [394, 329], [341, 289], [364, 278], [342, 259], [430, 233], [414, 191], [425, 181], [421, 180], [453, 155], [457, 153], [458, 152]], [[466, 145], [458, 152], [456, 153], [453, 155], [409, 178], [408, 178], [421, 180], [412, 187], [414, 191], [340, 256], [342, 259], [335, 261], [331, 265], [330, 281], [341, 289], [329, 295], [329, 305], [346, 334], [328, 335], [327, 353], [362, 363], [326, 381], [324, 418], [296, 395], [326, 381], [327, 353], [283, 339], [328, 335], [329, 305], [324, 298], [329, 295], [330, 281], [321, 274], [331, 265], [331, 262], [335, 261], [340, 256], [333, 250], [412, 187], [408, 178], [409, 178], [408, 177], [456, 153]], [[79, 342], [77, 358], [40, 361], [79, 342], [91, 223], [175, 293], [158, 302], [166, 304], [165, 305], [175, 308], [135, 320], [165, 305], [158, 302]], [[193, 76], [227, 134], [315, 139], [309, 157], [335, 162], [333, 215], [307, 228], [309, 230], [290, 240], [301, 259], [312, 267], [309, 268], [307, 269], [308, 270], [268, 301], [261, 327], [255, 331], [258, 332], [250, 336], [255, 331], [237, 326], [268, 301], [274, 279], [307, 269], [301, 259], [284, 246], [285, 242], [290, 240], [288, 237], [307, 228], [293, 215], [309, 157], [231, 140], [227, 134], [199, 132]]]
<span style="font-size:18px;">//
if (1) {
var r = 20;
config.setSector(8,15,7,2);
config.graphPaper2D(0, 0, r);
config.axis2D(0, 0, 450, 1.2);
//坐标轴设定
var scaleX = 2*r, scaleY = 2*r;
var spaceX = 50, spaceY = 50;
var xS = 0, xE = 640;
var yS = 0, yE = 480;
config.axisSpacing(xS, xE, spaceX, scaleX, 'X');
config.axisSpacing(yS, yE, spaceY, scaleY, 'Y');
var transform = new Transform();
var lable = [];
for (var i = 0; i < 100; i++) {
lable.push(i.toFixed(0));
}
var colorArray = ['#FF8888', 'orange', 'yellow', 'green', 'cyan', '#8888FF', 'purple'];
var color = 0;
var seg = [];
var subjEdges = $subj_edge.length;
var clipEdges = $clip_edge.length;
var solus = $solution.length;
for (var i = 0; i < solus; i++) {
seg = transform.scale($solution[i], scaleX/spaceX, scaleY/spaceY);
shape.fillDraw([].concat(seg), colorArray[i%7]);
}
for (var i = 0; i < subjEdges; i++) {
seg = transform.scale($subj_edge[i], scaleX/spaceX, scaleY/spaceY);
shape.multiLineDraw([].concat(seg), 'red');
}
for (var i = 0; i < clipEdges; i++) {
seg = transform.scale($clip_edge[i], scaleX/spaceX, scaleY/spaceY);
shape.multiLineDraw([].concat(seg), 'blue');
}
//主多边形
seg = transform.scale($vert_subj, scaleX/spaceX, scaleY/spaceY);
shape.pointDraw([].concat(seg), 'red', 1, 1, lable);
//从多边形
seg = transform.scale($vert_clip, scaleX/spaceX, scaleY/spaceY);
shape.pointDraw([].concat(seg), 'blue', 1, 1, lable);
plot.fillText('图:主多边形和从多边形的异或集', 10, 60, 300);
}
//</span>
<span style="font-size:18px;">#
from datastruct.geo import Transform
#多边形布尔运算的求解
def tmp_5():
# Load the polygons into Clipper and execute the boolean clip op ...
c = Clipper()
solution = []
pft = PolyFillType.NonZero
vert_subj = [[199, 133], [577, 464], [468, 386], [130, 128], [336, 140], [324, 418], [91, 223], [67, 455], [168, 458], [639, 227]]
vert_clip = [[233, 423], [193, 76], [369, 373], [538, 416], [155, 301], [215, 323], [82, 473], [136, 405], [466, 145], [250, 336], [480, 218], [135, 320], [492, 135], [40, 361], [467, 323], [347, 26]]
trans = Transform();
vert_subj = trans.angularSort(vert_subj);
vert_clip = trans.angularSort(vert_clip);
'''
print(vert_subj);
print(vert_clip);
'''
subj, clip = [], [];
for i in range(len(vert_subj)):
subj.append(Point(vert_subj[i][0], vert_subj[i][1]));
for i in range(len(vert_clip)):
clip.append(Point(vert_clip[i][0], vert_clip[i][1]));
c.AddPolygons([subj], PolyType.Subject)
c.AddPolygons([clip], PolyType.Clip)
result = c.Execute(ClipType.Union, solution, pft, pft)
scale = 1;
solution_ = [];
for i in range(len(solution)):
a = [];
for j in range(len(solution[i])):
a.append([solution[i][j].x, solution[i][j].y]);
solution_.append(a);
print(len(solution_));
print(solution_);
if (1) {
var r = 20;
config.setSector(8,15,7,2);
config.graphPaper2D(0, 0, r);
config.axis2D(0, 0, 450, 1.2);
//坐标轴设定
var scaleX = 2*r, scaleY = 2*r;
var spaceX = 50, spaceY = 50;
var xS = 0, xE = 640;
var yS = 0, yE = 480;
config.axisSpacing(xS, xE, spaceX, scaleX, 'X');
config.axisSpacing(yS, yE, spaceY, scaleY, 'Y');
var transform = new Transform();
var lable = [];
for (var i = 0; i < 100; i++) {
lable.push(i.toFixed(0));
}
var colorArray = ['#FF8888', 'orange', 'yellow', 'green', 'cyan', '#8888FF', 'purple'];
var color = 0;
var seg = [];
var solus = $solution.length;
for (var i = 0; i < solus; i++) {
seg = transform.scale($solution[i], scaleX/spaceX, scaleY/spaceY);
shape.fillDraw([].concat(seg), colorArray[i%7]);
}
//主多边形
var seg_subj = transform.scale($vert_subj, scaleX/spaceX, scaleY/spaceY);
//从多边形
var seg_clip = transform.scale($vert_clip, scaleX/spaceX, scaleY/spaceY);
shape.strokeDraw([].concat(seg_subj), 'red');
shape.strokeDraw([].concat(seg_clip), 'blue');
shape.pointDraw([].concat(seg_subj), 'red', 1, 1, lable);
shape.pointDraw([].concat(seg_clip), 'blue', 1, 1, lable);
plot.fillText('图:主多边形和从多边形的并集, EvenOdd', 10, 60, 300);
}
$vert_subj = [[67, 455], [91, 223], [130, 128], [168, 458], [199, 133], [324, 418], [336, 140], [468, 386], [577, 464], [639, 227]]
$vert_clip = [[40, 361], [82, 473], [135, 320], [136, 405], [155, 301], [193, 76], [215, 323], [233, 423], [250, 336], [347, 26], [369, 373], [466, 145], [467, 323], [480, 218], [492, 135], [538, 416]]
$solution = [[[76, 365], [118, 370], [92, 445], [74, 452], [69, 438]], [[515, 276], [538, 416], [505, 412], [468, 386], [428, 311], [467, 296], [467, 323], [471, 294]], [[135, 320], [136, 372], [142, 372], [136, 405], [136, 372], [118, 370]], [[336, 140], [357, 179], [367, 336], [327, 351], [325, 392], [312, 391], [300, 362], [327, 351]], [[199, 138], [215, 323], [226, 381], [241, 383], [240, 386], [227, 391], [226, 381], [176, 376]], [[266, 285], [300, 362], [246, 384], [241, 383], [250, 336]], [[152, 318], [158, 374], [142, 372]], [[410, 277], [428, 311], [389, 327]]]
#</span>
本节到此结束,欲知后事如何,请看下回分解。