Exemple #1
0
def triNoneSplit(skel, anchor, itchy, scratchy, tri):
    #
    # scratchy /|                |
    #         / |                |
    #        /  |              A |
    #   ____/tri|   ===>     ----|
    # anchor\   |                |
    #        \  |              B |
    #         \ |                |
    #   itchy  \|                |
    #

    # Exit conditions
    if anchor.pinned(): return []
    if itchy.pinned() and scratchy.pinned(): return []

    change = skeleton.ProvisionalChanges(skel)
    change.moveNode(anchor,
                    position=0.5 * (itchy.position() + scratchy.position()),
                    mobility=(itchy.movable_x() or scratchy.movable_x(),
                              itchy.movable_y() or scratchy.movable_y()))
    change.removeElements(tri)
    change.substituteSegment(
        skel.getSegment(itchy, scratchy),
        [skel.getSegment(anchor, itchy),
         skel.getSegment(anchor, scratchy)])
    return [change]
Exemple #2
0
    def mergeTriangles(self, element, skel, processed):
        saved = [None]*3    # saved energy through the merge
        newels = [None]*3   # new provisional elements from the merge
        sisters = [None]*3  # sisters

        changes = []
        for i in range(3):
            node0 = element.nodes[i]
            node1 = element.nodes[(i+1)%3]
            sister = element.getSister(skel, node0, node1)
            # These are not welcome here.
            if (sister is None or sister.nnodes()!=3 or
                sister in processed or
                element.dominantPixel(skel.MS)!=sister.dominantPixel(skel.MS)):
                continue
            j = sister.nodes.index(node0)
            nodeA = sister.nodes[(j+1)%3]
            nodeB = element.nodes[(i+2)%3]
            nlist = [node0, nodeA, node1, nodeB]
            parents = element.getParents() + sister.getParents()
            change = skeleton.ProvisionalChanges(skel)
            change.removeElements(element, sister)
            change.insertElements(ProvisionalQuad(nlist, parents=parents))
            changes.append(change)
        return changes
Exemple #3
0
    def tritriSwap(skel, n0, n1, tri0, tri1):
        #           n1
        #          /|\               / \
        #         / | \             /   \
        #        /  |  \           /  B  \
        #     n2/ t0| t1\n3 ===>  /_______\
        #       \   |   /         \       /
        #        \  |  /           \  A  /
        #         \ | /             \   /
        #          \|/               \ /
        #           n0
        #

        # Exit condition
        if (n0.pinned() and n1.pinned()):
            return []
        n2 = tri0.nodes[(tri0.nodes.index(n1) + 1) % 3]
        n3 = tri1.nodes[(tri1.nodes.index(n0) + 1) % 3]
        parents = tri0.getParents() + tri1.getParents()
        A = ProvisionalTriangle([n0, n3, n2], parents=parents)
        B = ProvisionalTriangle([n1, n2, n3], parents=parents)

        change = skeleton.ProvisionalChanges(skel)
        change.removeElements(tri0, tri1)
        change.insertElements(A, B)

        # If source elements are 100 % homogeneous with the same dominant pixel,
        # resulting elements will be so.
        if tri0.homogeneity(skel.MS)==1. and tri1.homogeneity(skel.MS)==1. and \
           tri0.dominantPixel(skel.MS)==tri1.dominantPixel(skel.MS):
            A.copyHomogeneity(tri0)
            B.copyHomogeneity(tri0)

        return [change]
Exemple #4
0
    def twoToThreeSwap(skel, tet1, tet2, sn):

        if (sn[0].pinned() and sn[1].pinned() and sn[2].pinned()):
            return []

        faceToNodeMap = tet1.faceToNodeMap()
        unsharedNode1 = [n for n in tet1.nodes if n not in sn][0]
        unsharedNode2 = [n for n in tet2.nodes if n not in sn][0]
        change = skeleton.ProvisionalChanges(skel)
        change.removeElements(tet1,tet2)
        uniform = (tet1.homogeneity(skel.MS, False)==1. and tet2.homogeneity(skel.MS, False)==1. and \
                       tet1.dominantPixel(skel.MS)==tet2.dominantPixel(skel.MS))
        for face in faceToNodeMap:
            nodes = [tet2.nodes[i] for i in face]
            if unsharedNode2 in nodes:
                nodes.reverse()
                nodes.append(unsharedNode1)
                tet = ProvisionalTetra(nodes,parents=[tet1,tet2])
                if not tet.onBoundary(skel):
                    change.insertElements(tet)
                    if uniform:
                        tet.copyHomogeneity(tet1)

        
        return [change]
Exemple #5
0
def slashRight(skel, element):
    change = skeleton.ProvisionalChanges(skel)
    nodes = element.nodes
    parent = element.getParents()[:1]
    change.removeElements(element)
    change.insertElements(
        ProvisionalTriangle([nodes[0], nodes[1], nodes[2]], parents=parent),
        ProvisionalTriangle([nodes[0], nodes[2], nodes[3]], parents=parent))
    return change
Exemple #6
0
 def fix(self, skel, element, which):
     nodes = element.nodes
     parents = element.getParents()
     if which%2 == 0:
         triangles = (ProvisionalTriangle([nodes[0], nodes[1], nodes[2]],
                                          parents=parents),
                      ProvisionalTriangle([nodes[0], nodes[2], nodes[3]],
                                          parents=parents))
     else:
         triangles = (ProvisionalTriangle([nodes[1], nodes[2], nodes[3]],
                                          parents=parents),
                      ProvisionalTriangle([nodes[0], nodes[1], nodes[3]],
                                          parents=parents))
     change = skeleton.ProvisionalChanges(skel)
     change.removeElements(element)
     change.insertElements(*triangles)
     return [change]
Exemple #7
0
    def quadquadSwap(skel, n0, n1, quad0, quad1):
        #           n1
        #          /|\
        #         / | \
        #        /  |  \          [n0, n4, n2, n3] & [n4, n5, n1, n2]
        #     n2|   |   |n5       [n0, n4, n5, n3] & [n5, n1, n2, n3]
        #       |q0 |q1 |     ==>
        #     n3|   |   |n4
        #        \  |  /
        #         \ | /
        #          \|/
        #           n0

        #           n1
        #          /|\
        #         / | \
        #        /  |  \
        #     n2|  /n6  |n5
        #       | /   \ |     ==> [n0, n4, n6, n3] & [n3, n6, n1, n2] &
        #     n3|/     \|n4       [n6, n4, n5, n1]
        #        \     /           n6 = (n3+n4+n1)/3.
        #         \   /
        #          \ /
        #           n0

        #           n1
        #          / \
        #         /   \
        #        /     \
        #     n2|\     /|n5
        #       | \   / |     ==> [n7, n5, n1, n2] & [n3, n0, n7, n2] &
        #     n3|  \n7  |n4       [n0, n4, n5, n7]
        #        \  |  /          n7 = (n2+n0+n5)/3.
        #         \ | /
        #          \|/
        #           n0

        if (n0.pinned() and n1.pinned()) and \
           (quad0.dominantPixel(skel.MS) != quad1.dominantPixel(skel.MS)):
            return []

        i = quad0.nodes.index(n1)
        n2 = quad0.nodes[(i + 1) % 4]
        n3 = quad0.nodes[(i + 2) % 4]
        i = quad1.nodes.index(n0)
        n4 = quad1.nodes[(i + 1) % 4]
        n5 = quad1.nodes[(i + 2) % 4]

        parents = quad0.getParents() + quad1.getParents()

        A = ProvisionalQuad([n0, n4, n2, n3], parents=parents)
        B = ProvisionalQuad([n4, n5, n1, n2], parents=parents)
        change0 = skeleton.ProvisionalChanges(skel)
        change0.insertElements(A, B)
        change0.removeElements(quad0, quad1)

        C = ProvisionalQuad([n0, n4, n5, n3], parents=parents)
        D = ProvisionalQuad([n5, n1, n2, n3], parents=parents)
        change1 = skeleton.ProvisionalChanges(skel)
        change1.insertElements(C, D)
        change1.removeElements(quad0, quad1)

        pos = (n3.position() + n4.position() + n1.position()) / 3.0
        n6 = skel.newNode(pos.x, pos.y)
        E = ProvisionalQuad([n0, n4, n6, n3], parents=parents)
        F = ProvisionalQuad([n3, n6, n1, n2], parents=quad0.getParents())
        G = ProvisionalQuad([n6, n4, n5, n1], parents=quad1.getParents())
        change2 = skeleton.ProvisionalInsertion(skel)
        change2.addNode(n6)
        change2.insertElements(E, F, G)
        change2.removeElements(quad0, quad1)

        pos = (n2.position() + n0.position() + n5.position()) / 3.0
        n7 = skel.newNode(pos.x, pos.y)
        H = ProvisionalQuad([n7, n5, n1, n2], parents=parents)
        I = ProvisionalQuad([n3, n0, n7, n2], parents=quad0.getParents())
        J = ProvisionalQuad([n0, n4, n5, n7], parents=quad1.getParents())
        change3 = skeleton.ProvisionalInsertion(skel)
        change3.addNode(n7)
        change3.insertElements(H, I, J)
        change3.removeElements(quad0, quad1)

        # If source elements are 100 % homogeneous with the same dominant pixel,
        # resulting elements will be so.
        if quad0.homogeneity(skel.MS)==1. and quad1.homogeneity(skel.MS)==1. and \
           quad0.dominantPixel(skel.MS)==quad1.dominantPixel(skel.MS):
            A.copyHomogeneity(quad0)
            B.copyHomogeneity(quad0)
            C.copyHomogeneity(quad0)
            D.copyHomogeneity(quad0)
            E.copyHomogeneity(quad0)
            F.copyHomogeneity(quad0)
            G.copyHomogeneity(quad0)
            H.copyHomogeneity(quad0)
            I.copyHomogeneity(quad0)
            J.copyHomogeneity(quad0)

        return [change0, change1, change2, change3]
Exemple #8
0
    def triquadSwap(skel, n0, n1, tri, quad):
        #
        #      n3__ n0
        #       |   |\
        #       |   | \             [n4, n0, n3] & [n4, n1, n2, n0]
        #       |   |  \            [n4, n1, n2] & [n4, n2, n0, n3]
        #       |   |   \           [n4, n1, n3] & [n1, n2, n0, n3]
        #       | q |    \     ==>  [n0, n3, n2] & [n3, n4, n1, n2]
        #       |   | t  /n2
        #       |   |   /
        #       |   |  /
        #       |   | /
        #       |__ |/
        #      n4   n1
        #
        if (n0.pinned() and n1.pinned()) and \
           (tri.dominantPixel(skel.MS) != quad.dominantPixel(skel.MS)):
            return []

        n2 = tri.nodes[(tri.nodes.index(n1) + 1) % 3]
        i = quad.nodes.index(n0)
        n3 = quad.nodes[(i + 1) % 4]
        n4 = quad.nodes[(i + 2) % 4]

        parents = quad.getParents() + tri.getParents()

        A = ProvisionalQuad([n4, n1, n2, n0], parents=parents)
        B = ProvisionalTriangle([n4, n0, n3], parents=parents)
        change0 = skeleton.ProvisionalChanges(skel)
        change0.insertElements(A, B)
        change0.removeElements(quad, tri)

        C = ProvisionalQuad([n4, n2, n0, n3], parents=parents)
        D = ProvisionalTriangle([n4, n1, n2], parents=parents)
        change1 = skeleton.ProvisionalChanges(skel)
        change1.insertElements(C, D)
        change1.removeElements(quad, tri)

        E = ProvisionalQuad([n1, n2, n0, n3], parents=parents)
        F = ProvisionalTriangle([n4, n1, n3], parents=parents)
        change2 = skeleton.ProvisionalChanges(skel)
        change2.insertElements(E, F)
        change2.removeElements(quad, tri)

        G = ProvisionalQuad([n3, n4, n1, n2], parents=parents)
        H = ProvisionalTriangle([n0, n3, n2], parents=parents)
        change3 = skeleton.ProvisionalChanges(skel)
        change3.insertElements(G, H)
        change3.removeElements(quad, tri)

        # If source elements are 100 % homogeneous with the same dominant pixel,
        # resulting elements will be so.
        if tri.homogeneity(skel.MS)==1. and quad.homogeneity(skel.MS)==1. and \
           tri.dominantPixel(skel.MS)==quad.dominantPixel(skel.MS):
            A.copyHomogeneity(tri)
            B.copyHomogeneity(tri)
            C.copyHomogeneity(tri)
            D.copyHomogeneity(tri)
            E.copyHomogeneity(tri)
            F.copyHomogeneity(tri)
            G.copyHomogeneity(tri)
            H.copyHomogeneity(tri)

        return [change0, change1, change2, change3]
Exemple #9
0
    def tetTetSplit(skel, obtusenode, oppnode, tet1, tet2):
        # obtusenode is the node in tet1 with a wide angle, oppnode is
        # the node in tet2 that is NOT shared between tet1 and tet2.

        # print "in tetTetSplit"
        # print obtusenode.position()

        changes = []
        faces = tet2.faceToNodeMap()

        unSharedFaces = []
        for face in faces:
            if oppnode in [tet2.nodes[i] for i in face]:
                unSharedFaces.append(face)
        sharedNodes = []
        for node in tet2.nodes:
            if node is not oppnode:
                sharedNodes.append(node)
        midpoints = _centroidAndMidpoints(skel, sharedNodes)
        periodic = midpoints[-1][1] is not None

        # first just split the two into three, without moving the obtusenode
        if not periodic:
            change = skeleton.ProvisionalChanges(skel)
            change.removeElements(tet1, tet2)
            parents = tet1.getParents() + tet2.getParents()
            tetra = []
            for face in unSharedFaces:
                nodes = [tet2.nodes[i] for i in face]
                nodes.reverse()
                nodes.append(obtusenode)
                change.insertElements(ProvisionalTetra(nodes, parents=parents))
            print change, change.illegal(skel)
            #print unSharedFaces, [el.illegal() for el in change.elAfter()]
            changes.append(change)

            # for the first three midpoints, we split into two tetra
            #for i in range(3):
            #    pair = (sharedNodes[i],sharedNodes[(i+1)%3])
            #    midpoint = midpoints[i][0]
            #    if obtusenode.canMoveTo(midpoint):
            #        change = skeleton.ProvisionalChanges(skel)
            #        change.moveNode(obtusenode, midpoint)
            #        change.removeElements(tet1, tet2)
            #        for face in unSharedFaces:
            #            nodes = [tet2.nodes[j] for j in face]
            #            nodes.reverse()
            #            nodes.append(obtusenode)
            #            if not (pair[0] in nodes and pair[1] in nodes):
            #                change.insertElements(ProvisionalTetra(nodes,parents=tet2.getParents()))
            #            else:
            #                otherneighbor = tet2.getSisterPeriodic(skel, nodes)
            #        print midpoint, pair, otherneighbor
            #        print "num elements", len(change.elAfter())
            #        for el in change.elAfter():
            #if el.illegal():
            #                print "illegal element:"
            #                nodes = el.nodes
            #                for node in el.nodes:
            #                    print node.position()
            #                testel = ProvisionalTetra([nodes[0],nodes[2],nodes[1],nodes[3]], parents=parents)
            #                print "reordering tetra: ", testel.illegal()
            #        if otherneighbor == None:
            #            changes.append(change)

            centroid = midpoints[3][0]
            if obtusenode.canMoveTo(centroid):
                change = skeleton.ProvisionalChanges(skel)
                change.moveNode(obtusenode, centroid)
                change.removeElements(tet1, tet2)
                tetra = []
                for face in unSharedFaces:
                    nodes = [tet2.nodes[i] for i in face]
                    nodes.reverse()
                    nodes.append(obtusenode)
                    change.insertElements(
                        ProvisionalTetra(nodes, parents=tet2.getParents()))
                changes.append(change)

        return changes
Exemple #10
0
def triTriSplit(skel, anchor, itchy, scratchy, tri1, tri2):
    #
    #
    # scratchy /|\               |\
    #         / | \              | \
    #        /  |  \           C | A\   If tri1.dominantPixel is different
    #       /tri|tri\T0    ===>  |___\  from tri2.dominantPixel ...
    # anchor\ 1 | 2 /            |   /
    #        \  |  /           D | B/
    #         \ | /              | /
    #   itchy  \|/               |/
    #
    #
    #          /|\              / \
    #         / | \            /   \
    #        /  |  \          /     \   If tri1.dominantPixel is the same as
    #       /tri|tri\   ===> /_______\  from tri2.dominantPixel ...
    #       \ 1 | 2 /        \       /
    #        \  |  /          \     /
    #         \ | /            \   /
    #          \|/              \ /
    #

    # If itchy and scratchy are pinned, the process should be aborted
    if itchy.pinned() and scratchy.pinned():
        return []

    # Find the midpoint of the segment, and its periodic partner, if
    # it exists.
    midpoint, midpoint2 = _midpoints(skel, itchy, scratchy)
    periodic = midpoint2 is not None

    # Find the nodes of tri2 that correspond to itchy and scratchy.
    # If the border between tri and quad is a periodic boundary, these
    # nodes aren't the same as itchy and scratchy!
    if periodic:
        partners = itchy.getPartnerPair(scratchy)
        itchy2, scratchy2 = partners
    else:
        itchy2, scratchy2 = itchy, scratchy

    tri2nodes = tri2.nodes
    t0 = tri2nodes[(tri2nodes.index(itchy2) + 1) % 3]
    parents = tri2.getParents()
    changes = []

    if anchor.movable_x() and anchor.movable_y() and not anchor.getPartners():
        if not periodic:
            change0 = skeleton.ProvisionalChanges(skel)
            change0.moveNode(anchor, midpoint)
            change0.removeElements(tri1, tri2)
            change0.insertElements(
                ProvisionalTriangle([anchor, t0, scratchy], parents=parents),
                ProvisionalTriangle([anchor, itchy, t0], parents=parents))
        else:  # periodic
            change0 = skeleton.ProvisionalInsertion(skel)
            newanchor = skel.newNode(midpoint.x, midpoint.y)
            anchor2 = skel.newNode(midpoint2.x, midpoint2.y)
            newanchor.addPartner(anchor2)
            elsubs = [(el, el.provisionalReplacement(anchor, newanchor))
                      for el in anchor.aperiodicNeighborElements(skel)
                      if el is not tri1]
            change0.removeElements(tri1, tri2)
            for oldel, newel in elsubs:
                change0.substituteElement(oldel, newel)
            change0.insertElements(
                ProvisionalTriangle([anchor2, t0, scratchy2], parents=parents),
                ProvisionalTriangle([anchor2, itchy2, t0], parents=parents))
            change0.addNode(newanchor)
            change0.addNode(anchor2)
        changes.append(change0)

    if not periodic:
        change1 = skeleton.ProvisionalChanges(skel)
        change1.removeElements(tri1, tri2)
        parents = tri1.getParents() + tri2.getParents()
        change1.insertElements(
            ProvisionalTriangle([anchor, t0, scratchy], parents=parents),
            ProvisionalTriangle([anchor, itchy, t0], parents=parents))
        changes.append(change1)

    return changes
Exemple #11
0
def triQuadSplit(skel, anchor, itchy, scratchy, tri, quad):

    # If tri.dominantPixel is different from quad.dominantPixel ...
    # Pick the best of three configurations.
    #            ___________        ______      ______      ______
    # scratchy /|          Q1       |   /|      |   /|      |    |
    #         / |          |        |C / |      |B / |      |    |
    #        /  |          |        | /  |      | /  |      |  B |
    #       /tri|  quad    |  ===>  |/   |      |/   |      |    |
    # anchor\   |          |        |\ B |      |    |      |\   |
    #        \  |          |        | \  |      | A  |      | \  |
    #         \ |          |        |A \ |      |    |      |A \ |
    #   itchy  \|__________Q0       |___\|      |____|      |___\|
    #

    # If tri & quad have same dominantPixel and aren't separated by a
    # periodic boundary, consider this geometry as well:
    #            ___________            _________
    #          /|          |           /      . |
    #         / |          |          / C  .    |
    #        /  |          |         /  .       |
    #       /tri|  quad    |  ===>  /.     B    | Three different cases
    #       \   |          |        \   .       | as in the above
    #        \  |          |         \     .    |
    #         \ |          |          \  A    . |
    #          \|__________|           \________|
    #

    # If itchy and scratchy are pinned, the process should be aborted
    if itchy.pinned() and scratchy.pinned():
        return []

    # Find the midpoint of the segment, and its periodic partner, if
    # it exists.
    midpoint, midpointQ = _midpoints(skel, itchy, scratchy)
    periodic = midpointQ is not None

    # Find the nodes of the quad that correspond to itchy and
    # scratchy.  If the border between tri and quad is a periodic
    # boundary, these nodes aren't the same as itchy and scratchy!
    if periodic:
        partners = itchy.getPartnerPair(scratchy)
        itchyQ, scratchyQ = partners
    else:
        itchyQ, scratchyQ = itchy, scratchy

    # Find nodes Q0 and Q1
    quadnodes = quad.nodes
    itchyindex = quadnodes.index(itchyQ)
    q0 = quadnodes[(itchyindex + 1) % 4]
    q1 = quadnodes[(itchyindex + 2) % 4]

    parents = quad.getParents()
    changes = []

    # Cases that move the anchor point.  Don't do these at all if the
    # anchor is a periodic node or not otherwise movable:
    if anchor.movable_x() and anchor.movable_y() and not anchor.getPartners():
        # Divide the quad into three triangles.  The periodic case is
        # complicated because it has to replace the aperiodic anchor
        # Node with a periodic one, and that requires replacing all of
        # the elements connected to the anchor point.  We do the
        # periodic and aperiodic cases completely separately here.
        if not periodic:
            change0 = skeleton.ProvisionalChanges(skel)
            change0.moveNode(anchor, midpoint)
            change0.removeElements(tri, quad)
            change0.insertElements(
                ProvisionalTriangle([anchor, itchy, q0], parents=parents),
                ProvisionalTriangle([anchor, q0, q1], parents=parents),
                ProvisionalTriangle([anchor, q1, scratchy], parents=parents))

            change1 = skeleton.ProvisionalChanges(skel)
            change1.moveNode(anchor, midpoint)
            change1.removeElements(tri, quad)
            parents = quad.getParents()
            change1.insertElements(
                ProvisionalQuad([anchor, itchy, q0, q1], parents=parents),
                ProvisionalTriangle([anchor, q1, scratchy], parents=parents))

            change2 = skeleton.ProvisionalChanges(skel)
            change2.moveNode(anchor, midpoint)
            change2.removeElements(tri, quad)
            parents = quad.getParents()
            change2.insertElements(
                ProvisionalTriangle([anchor, itchy, q0], parents=parents),
                ProvisionalQuad([anchor, q0, q1, scratchy], parents=parents))
        else:
            # Three ways of moving the anchor point to the *periodic*
            # boundary and dividing the quad.
            newanchor, anchorQ, elsubs = triquadhelper(skel, tri, anchor,
                                                       midpoint, midpointQ)
            change0 = skeleton.ProvisionalInsertion(skel)
            change0.removeElements(quad, tri)
            change0.addNode(newanchor)
            change0.addNode(anchorQ)
            for oldel, newel in elsubs:
                change0.substituteElement(oldel, newel)
            change0.insertElements(
                ProvisionalTriangle([anchorQ, itchyQ, q0], parents=parents),
                ProvisionalTriangle([anchorQ, q0, q1], parents=parents),
                ProvisionalTriangle([anchorQ, q1, scratchyQ], parents=parents))

            newanchor, anchorQ, elsubs = triquadhelper(skel, tri, anchor,
                                                       midpoint, midpointQ)
            change1 = skeleton.ProvisionalInsertion(skel)
            change1.removeElements(quad, tri)
            change1.addNode(newanchor)
            change1.addNode(anchorQ)
            for oldel, newel in elsubs:
                change1.substituteElement(oldel, newel)
            change1.insertElements(
                ProvisionalQuad([anchorQ, itchyQ, q0, q1], parents=parents),
                ProvisionalTriangle([anchorQ, q1, scratchyQ], parents=parents))

            newanchor, anchorQ, elsubs = triquadhelper(skel, tri, anchor,
                                                       midpoint, midpointQ)
            change2 = skeleton.ProvisionalInsertion(skel)
            change2.removeElements(quad, tri)
            change2.addNode(newanchor)
            change2.addNode(anchorQ)
            for oldel, newel in elsubs:
                change2.substituteElement(oldel, newel)
            change2.insertElements(
                ProvisionalTriangle([anchorQ, itchyQ, q0], parents=parents),
                ProvisionalQuad([anchorQ, q0, q1, scratchyQ], parents=parents))

        changes.extend([change0, change1, change2])

    # Cases for lazy (immobile) anchor.  These can only be done if tri
    # and quad don't span a periodic boundary.
    if not periodic:
        change3 = skeleton.ProvisionalChanges(skel)
        change3.removeElements(tri, quad)
        parents = quad.getParents() + tri.getParents()
        change3.insertElements(
            ProvisionalTriangle([anchor, itchy, q0], parents=parents),
            ProvisionalTriangle([anchor, q0, q1], parents=parents),
            ProvisionalTriangle([anchor, q1, scratchy], parents=parents))

        change4 = skeleton.ProvisionalChanges(skel)
        change4.removeElements(tri, quad)
        parents = quad.getParents()
        change4.insertElements(
            ProvisionalQuad([anchor, itchy, q0, q1], parents=parents),
            ProvisionalTriangle([anchor, q1, scratchy], parents=parents))

        change5 = skeleton.ProvisionalChanges(skel)
        change5.removeElements(tri, quad)
        parents = quad.getParents()
        change5.insertElements(
            ProvisionalTriangle([anchor, itchy, q0], parents=parents),
            ProvisionalQuad([anchor, q0, q1, scratchy], parents=parents))

        changes.extend([change3, change4, change5])

    return changes