in s2/shapeindex.go [1352:1465]
func (s *ShapeIndex) absorbIndexCell(p *PaddedCell, iter *ShapeIndexIterator, edges []*clippedEdge, t *tracker) {
// When we absorb a cell, we erase all the edges that are being removed.
// However when we are finished with this cell, we want to restore the state
// of those edges (since that is how we find all the index cells that need
// to be updated). The edges themselves are restored automatically when
// UpdateEdges returns from its recursive call, but the InteriorTracker
// state needs to be restored explicitly.
//
// Here we first update the InteriorTracker state for removed edges to
// correspond to the exit vertex of this cell, and then save the
// InteriorTracker state. This state will be restored by UpdateEdges when
// it is finished processing the contents of this cell.
if t.isActive && len(edges) != 0 && s.isShapeBeingRemoved(edges[0].faceEdge.shapeID) {
// We probably need to update the tracker. ("Probably" because
// it's possible that all shapes being removed do not have interiors.)
if !t.atCellID(p.id) {
t.moveTo(p.EntryVertex())
}
t.drawTo(p.ExitVertex())
t.setNextCellID(p.id.Next())
for _, edge := range edges {
fe := edge.faceEdge
if !s.isShapeBeingRemoved(fe.shapeID) {
break // All shapes being removed come first.
}
if fe.hasInterior {
t.testEdge(fe.shapeID, fe.edge)
}
}
}
// Save the state of the edges being removed, so that it can be restored
// when we are finished processing this cell and its children. We don't
// need to save the state of the edges being added because they aren't being
// removed from "edges" and will therefore be updated normally as we visit
// this cell and its children.
t.saveAndClearStateBefore(s.pendingAdditionsPos)
// Create a faceEdge for each edge in this cell that isn't being removed.
var faceEdges []*faceEdge
trackerMoved := false
cell := iter.IndexCell()
for _, clipped := range cell.shapes {
shapeID := clipped.shapeID
shape := s.Shape(shapeID)
if shape == nil {
continue // This shape is being removed.
}
numClipped := clipped.numEdges()
// If this shape has an interior, start tracking whether we are inside the
// shape. updateEdges wants to know whether the entry vertex of this
// cell is inside the shape, but we only know whether the center of the
// cell is inside the shape, so we need to test all the edges against the
// line segment from the cell center to the entry vertex.
edge := &faceEdge{
shapeID: shapeID,
hasInterior: shape.Dimension() == 2,
}
if edge.hasInterior {
t.addShape(shapeID, clipped.containsCenter)
// There might not be any edges in this entire cell (i.e., it might be
// in the interior of all shapes), so we delay updating the tracker
// until we see the first edge.
if !trackerMoved && numClipped > 0 {
t.moveTo(p.Center())
t.drawTo(p.EntryVertex())
t.setNextCellID(p.id)
trackerMoved = true
}
}
for i := 0; i < numClipped; i++ {
edgeID := clipped.edges[i]
edge.edgeID = edgeID
edge.edge = shape.Edge(edgeID)
edge.maxLevel = maxLevelForEdge(edge.edge)
if edge.hasInterior {
t.testEdge(shapeID, edge.edge)
}
var ok bool
edge.a, edge.b, ok = ClipToPaddedFace(edge.edge.V0, edge.edge.V1, p.id.Face(), cellPadding)
if !ok {
panic("invariant failure in ShapeIndex")
}
faceEdges = append(faceEdges, edge)
}
}
// Now create a clippedEdge for each faceEdge, and put them in "new_edges".
var newEdges []*clippedEdge
for _, faceEdge := range faceEdges {
clipped := &clippedEdge{
faceEdge: faceEdge,
bound: clippedEdgeBound(faceEdge.a, faceEdge.b, p.bound),
}
newEdges = append(newEdges, clipped)
}
// Discard any edges from "edges" that are being removed, and append the
// remainder to "newEdges" (This keeps the edges sorted by shape id.)
for i, clipped := range edges {
if !s.isShapeBeingRemoved(clipped.faceEdge.shapeID) {
newEdges = append(newEdges, edges[i:]...)
break
}
}
// Update the edge list and delete this cell from the index.
edges, newEdges = newEdges, edges
delete(s.cellMap, p.id)
// TODO(roberts): delete from s.Cells
}