[从头学数学] 第287节 [计算几何] 多边形的布尔运算(中)

[工程师阿伟]与[机器小伟]探究[计算几何]的奥秘,聚焦多边形布尔运算的详细解析。

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

剧情提要:
阿伟看到了一本比较有趣的书,是关于《计算几何》的,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>










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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值