mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-11-04 04:02:25 -06:00 
			
		
		
		
	* start fixing up tests * fix up tests + automate with drone * fiddle with linting * messing about with drone.yml * some more fiddling * hmmm * add cache * add vendor directory * verbose * ci updates * update some little things * update sig
		
			
				
	
	
		
			1526 lines
		
	
	
	
		
			54 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			1526 lines
		
	
	
	
		
			54 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Copyright 2016 Google Inc. All rights reserved.
 | 
						|
//
 | 
						|
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
// you may not use this file except in compliance with the License.
 | 
						|
// You may obtain a copy of the License at
 | 
						|
//
 | 
						|
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
//
 | 
						|
// Unless required by applicable law or agreed to in writing, software
 | 
						|
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
// See the License for the specific language governing permissions and
 | 
						|
// limitations under the License.
 | 
						|
 | 
						|
package s2
 | 
						|
 | 
						|
import (
 | 
						|
	"math"
 | 
						|
	"sort"
 | 
						|
	"sync"
 | 
						|
	"sync/atomic"
 | 
						|
 | 
						|
	"github.com/golang/geo/r1"
 | 
						|
	"github.com/golang/geo/r2"
 | 
						|
)
 | 
						|
 | 
						|
// CellRelation describes the possible relationships between a target cell
 | 
						|
// and the cells of the ShapeIndex. If the target is an index cell or is
 | 
						|
// contained by an index cell, it is Indexed. If the target is subdivided
 | 
						|
// into one or more index cells, it is Subdivided. Otherwise it is Disjoint.
 | 
						|
type CellRelation int
 | 
						|
 | 
						|
// The possible CellRelations for a ShapeIndex.
 | 
						|
const (
 | 
						|
	Indexed CellRelation = iota
 | 
						|
	Subdivided
 | 
						|
	Disjoint
 | 
						|
)
 | 
						|
 | 
						|
const (
 | 
						|
	// cellPadding defines the total error when clipping an edge which comes
 | 
						|
	// from two sources:
 | 
						|
	// (1) Clipping the original spherical edge to a cube face (the face edge).
 | 
						|
	//     The maximum error in this step is faceClipErrorUVCoord.
 | 
						|
	// (2) Clipping the face edge to the u- or v-coordinate of a cell boundary.
 | 
						|
	//     The maximum error in this step is edgeClipErrorUVCoord.
 | 
						|
	// Finally, since we encounter the same errors when clipping query edges, we
 | 
						|
	// double the total error so that we only need to pad edges during indexing
 | 
						|
	// and not at query time.
 | 
						|
	cellPadding = 2.0 * (faceClipErrorUVCoord + edgeClipErrorUVCoord)
 | 
						|
 | 
						|
	// cellSizeToLongEdgeRatio defines the cell size relative to the length of an
 | 
						|
	// edge at which it is first considered to be long. Long edges do not
 | 
						|
	// contribute toward the decision to subdivide a cell further. For example,
 | 
						|
	// a value of 2.0 means that the cell must be at least twice the size of the
 | 
						|
	// edge in order for that edge to be counted. There are two reasons for not
 | 
						|
	// counting long edges: (1) such edges typically need to be propagated to
 | 
						|
	// several children, which increases time and memory costs without much benefit,
 | 
						|
	// and (2) in pathological cases, many long edges close together could force
 | 
						|
	// subdivision to continue all the way to the leaf cell level.
 | 
						|
	cellSizeToLongEdgeRatio = 1.0
 | 
						|
)
 | 
						|
 | 
						|
// clippedShape represents the part of a shape that intersects a Cell.
 | 
						|
// It consists of the set of edge IDs that intersect that cell and a boolean
 | 
						|
// indicating whether the center of the cell is inside the shape (for shapes
 | 
						|
// that have an interior).
 | 
						|
//
 | 
						|
// Note that the edges themselves are not clipped; we always use the original
 | 
						|
// edges for intersection tests so that the results will be the same as the
 | 
						|
// original shape.
 | 
						|
type clippedShape struct {
 | 
						|
	// shapeID is the index of the shape this clipped shape is a part of.
 | 
						|
	shapeID int32
 | 
						|
 | 
						|
	// containsCenter indicates if the center of the CellID this shape has been
 | 
						|
	// clipped to falls inside this shape. This is false for shapes that do not
 | 
						|
	// have an interior.
 | 
						|
	containsCenter bool
 | 
						|
 | 
						|
	// edges is the ordered set of ShapeIndex original edge IDs. Edges
 | 
						|
	// are stored in increasing order of edge ID.
 | 
						|
	edges []int
 | 
						|
}
 | 
						|
 | 
						|
// newClippedShape returns a new clipped shape for the given shapeID and number of expected edges.
 | 
						|
func newClippedShape(id int32, numEdges int) *clippedShape {
 | 
						|
	return &clippedShape{
 | 
						|
		shapeID: id,
 | 
						|
		edges:   make([]int, numEdges),
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// numEdges returns the number of edges that intersect the CellID of the Cell this was clipped to.
 | 
						|
func (c *clippedShape) numEdges() int {
 | 
						|
	return len(c.edges)
 | 
						|
}
 | 
						|
 | 
						|
// containsEdge reports if this clipped shape contains the given edge ID.
 | 
						|
func (c *clippedShape) containsEdge(id int) bool {
 | 
						|
	// Linear search is fast because the number of edges per shape is typically
 | 
						|
	// very small (less than 10).
 | 
						|
	for _, e := range c.edges {
 | 
						|
		if e == id {
 | 
						|
			return true
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return false
 | 
						|
}
 | 
						|
 | 
						|
// ShapeIndexCell stores the index contents for a particular CellID.
 | 
						|
type ShapeIndexCell struct {
 | 
						|
	shapes []*clippedShape
 | 
						|
}
 | 
						|
 | 
						|
// NewShapeIndexCell creates a new cell that is sized to hold the given number of shapes.
 | 
						|
func NewShapeIndexCell(numShapes int) *ShapeIndexCell {
 | 
						|
	return &ShapeIndexCell{
 | 
						|
		shapes: make([]*clippedShape, numShapes),
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// numEdges reports the total number of edges in all clipped shapes in this cell.
 | 
						|
func (s *ShapeIndexCell) numEdges() int {
 | 
						|
	var e int
 | 
						|
	for _, cs := range s.shapes {
 | 
						|
		e += cs.numEdges()
 | 
						|
	}
 | 
						|
	return e
 | 
						|
}
 | 
						|
 | 
						|
// add adds the given clipped shape to this index cell.
 | 
						|
func (s *ShapeIndexCell) add(c *clippedShape) {
 | 
						|
	// C++ uses a set, so it's ordered and unique. We don't currently catch
 | 
						|
	// the case when a duplicate value is added.
 | 
						|
	s.shapes = append(s.shapes, c)
 | 
						|
}
 | 
						|
 | 
						|
// findByShapeID returns the clipped shape that contains the given shapeID,
 | 
						|
// or nil if none of the clipped shapes contain it.
 | 
						|
func (s *ShapeIndexCell) findByShapeID(shapeID int32) *clippedShape {
 | 
						|
	// Linear search is fine because the number of shapes per cell is typically
 | 
						|
	// very small (most often 1), and is large only for pathological inputs
 | 
						|
	// (e.g. very deeply nested loops).
 | 
						|
	for _, clipped := range s.shapes {
 | 
						|
		if clipped.shapeID == shapeID {
 | 
						|
			return clipped
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// faceEdge and clippedEdge store temporary edge data while the index is being
 | 
						|
// updated.
 | 
						|
//
 | 
						|
// While it would be possible to combine all the edge information into one
 | 
						|
// structure, there are two good reasons for separating it:
 | 
						|
//
 | 
						|
//  - Memory usage. Separating the two means that we only need to
 | 
						|
//    store one copy of the per-face data no matter how many times an edge is
 | 
						|
//    subdivided, and it also lets us delay computing bounding boxes until
 | 
						|
//    they are needed for processing each face (when the dataset spans
 | 
						|
//    multiple faces).
 | 
						|
//
 | 
						|
//  - Performance. UpdateEdges is significantly faster on large polygons when
 | 
						|
//    the data is separated, because it often only needs to access the data in
 | 
						|
//    clippedEdge and this data is cached more successfully.
 | 
						|
 | 
						|
// faceEdge represents an edge that has been projected onto a given face,
 | 
						|
type faceEdge struct {
 | 
						|
	shapeID     int32    // The ID of shape that this edge belongs to
 | 
						|
	edgeID      int      // Edge ID within that shape
 | 
						|
	maxLevel    int      // Not desirable to subdivide this edge beyond this level
 | 
						|
	hasInterior bool     // Belongs to a shape that has a dimension of 2
 | 
						|
	a, b        r2.Point // The edge endpoints, clipped to a given face
 | 
						|
	edge        Edge     // The original edge.
 | 
						|
}
 | 
						|
 | 
						|
// clippedEdge represents the portion of that edge that has been clipped to a given Cell.
 | 
						|
type clippedEdge struct {
 | 
						|
	faceEdge *faceEdge // The original unclipped edge
 | 
						|
	bound    r2.Rect   // Bounding box for the clipped portion
 | 
						|
}
 | 
						|
 | 
						|
// ShapeIndexIteratorPos defines the set of possible iterator starting positions. By
 | 
						|
// default iterators are unpositioned, since this avoids an extra seek in this
 | 
						|
// situation where one of the seek methods (such as Locate) is immediately called.
 | 
						|
type ShapeIndexIteratorPos int
 | 
						|
 | 
						|
const (
 | 
						|
	// IteratorBegin specifies the iterator should be positioned at the beginning of the index.
 | 
						|
	IteratorBegin ShapeIndexIteratorPos = iota
 | 
						|
	// IteratorEnd specifies the iterator should be positioned at the end of the index.
 | 
						|
	IteratorEnd
 | 
						|
)
 | 
						|
 | 
						|
// ShapeIndexIterator is an iterator that provides low-level access to
 | 
						|
// the cells of the index. Cells are returned in increasing order of CellID.
 | 
						|
//
 | 
						|
//   for it := index.Iterator(); !it.Done(); it.Next() {
 | 
						|
//     fmt.Print(it.CellID())
 | 
						|
//   }
 | 
						|
//
 | 
						|
type ShapeIndexIterator struct {
 | 
						|
	index    *ShapeIndex
 | 
						|
	position int
 | 
						|
	id       CellID
 | 
						|
	cell     *ShapeIndexCell
 | 
						|
}
 | 
						|
 | 
						|
// NewShapeIndexIterator creates a new iterator for the given index. If a starting
 | 
						|
// position is specified, the iterator is positioned at the given spot.
 | 
						|
func NewShapeIndexIterator(index *ShapeIndex, pos ...ShapeIndexIteratorPos) *ShapeIndexIterator {
 | 
						|
	s := &ShapeIndexIterator{
 | 
						|
		index: index,
 | 
						|
	}
 | 
						|
 | 
						|
	if len(pos) > 0 {
 | 
						|
		if len(pos) > 1 {
 | 
						|
			panic("too many ShapeIndexIteratorPos arguments")
 | 
						|
		}
 | 
						|
		switch pos[0] {
 | 
						|
		case IteratorBegin:
 | 
						|
			s.Begin()
 | 
						|
		case IteratorEnd:
 | 
						|
			s.End()
 | 
						|
		default:
 | 
						|
			panic("unknown ShapeIndexIteratorPos value")
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return s
 | 
						|
}
 | 
						|
 | 
						|
func (s *ShapeIndexIterator) clone() *ShapeIndexIterator {
 | 
						|
	return &ShapeIndexIterator{
 | 
						|
		index:    s.index,
 | 
						|
		position: s.position,
 | 
						|
		id:       s.id,
 | 
						|
		cell:     s.cell,
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// CellID returns the CellID of the current index cell.
 | 
						|
// If s.Done() is true, a value larger than any valid CellID is returned.
 | 
						|
func (s *ShapeIndexIterator) CellID() CellID {
 | 
						|
	return s.id
 | 
						|
}
 | 
						|
 | 
						|
// IndexCell returns the current index cell.
 | 
						|
func (s *ShapeIndexIterator) IndexCell() *ShapeIndexCell {
 | 
						|
	// TODO(roberts): C++ has this call a virtual method to allow subclasses
 | 
						|
	// of ShapeIndexIterator to do other work before returning the cell. Do
 | 
						|
	// we need such a thing?
 | 
						|
	return s.cell
 | 
						|
}
 | 
						|
 | 
						|
// Center returns the Point at the center of the current position of the iterator.
 | 
						|
func (s *ShapeIndexIterator) Center() Point {
 | 
						|
	return s.CellID().Point()
 | 
						|
}
 | 
						|
 | 
						|
// Begin positions the iterator at the beginning of the index.
 | 
						|
func (s *ShapeIndexIterator) Begin() {
 | 
						|
	if !s.index.IsFresh() {
 | 
						|
		s.index.maybeApplyUpdates()
 | 
						|
	}
 | 
						|
	s.position = 0
 | 
						|
	s.refresh()
 | 
						|
}
 | 
						|
 | 
						|
// Next positions the iterator at the next index cell.
 | 
						|
func (s *ShapeIndexIterator) Next() {
 | 
						|
	s.position++
 | 
						|
	s.refresh()
 | 
						|
}
 | 
						|
 | 
						|
// Prev advances the iterator to the previous cell in the index and returns true to
 | 
						|
// indicate it was not yet at the beginning of the index. If the iterator is at the
 | 
						|
// first cell the call does nothing and returns false.
 | 
						|
func (s *ShapeIndexIterator) Prev() bool {
 | 
						|
	if s.position <= 0 {
 | 
						|
		return false
 | 
						|
	}
 | 
						|
 | 
						|
	s.position--
 | 
						|
	s.refresh()
 | 
						|
	return true
 | 
						|
}
 | 
						|
 | 
						|
// End positions the iterator at the end of the index.
 | 
						|
func (s *ShapeIndexIterator) End() {
 | 
						|
	s.position = len(s.index.cells)
 | 
						|
	s.refresh()
 | 
						|
}
 | 
						|
 | 
						|
// Done reports if the iterator is positioned at or after the last index cell.
 | 
						|
func (s *ShapeIndexIterator) Done() bool {
 | 
						|
	return s.id == SentinelCellID
 | 
						|
}
 | 
						|
 | 
						|
// refresh updates the stored internal iterator values.
 | 
						|
func (s *ShapeIndexIterator) refresh() {
 | 
						|
	if s.position < len(s.index.cells) {
 | 
						|
		s.id = s.index.cells[s.position]
 | 
						|
		s.cell = s.index.cellMap[s.CellID()]
 | 
						|
	} else {
 | 
						|
		s.id = SentinelCellID
 | 
						|
		s.cell = nil
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// seek positions the iterator at the first cell whose ID >= target, or at the
 | 
						|
// end of the index if no such cell exists.
 | 
						|
func (s *ShapeIndexIterator) seek(target CellID) {
 | 
						|
	s.position = sort.Search(len(s.index.cells), func(i int) bool {
 | 
						|
		return s.index.cells[i] >= target
 | 
						|
	})
 | 
						|
	s.refresh()
 | 
						|
}
 | 
						|
 | 
						|
// LocatePoint positions the iterator at the cell that contains the given Point.
 | 
						|
// If no such cell exists, the iterator position is unspecified, and false is returned.
 | 
						|
// The cell at the matched position is guaranteed to contain all edges that might
 | 
						|
// intersect the line segment between target and the cell's center.
 | 
						|
func (s *ShapeIndexIterator) LocatePoint(p Point) bool {
 | 
						|
	// Let I = cellMap.LowerBound(T), where T is the leaf cell containing
 | 
						|
	// point P. Then if T is contained by an index cell, then the
 | 
						|
	// containing cell is either I or I'. We test for containment by comparing
 | 
						|
	// the ranges of leaf cells spanned by T, I, and I'.
 | 
						|
	target := cellIDFromPoint(p)
 | 
						|
	s.seek(target)
 | 
						|
	if !s.Done() && s.CellID().RangeMin() <= target {
 | 
						|
		return true
 | 
						|
	}
 | 
						|
 | 
						|
	if s.Prev() && s.CellID().RangeMax() >= target {
 | 
						|
		return true
 | 
						|
	}
 | 
						|
	return false
 | 
						|
}
 | 
						|
 | 
						|
// LocateCellID attempts to position the iterator at the first matching index cell
 | 
						|
// in the index that has some relation to the given CellID. Let T be the target CellID.
 | 
						|
// If T is contained by (or equal to) some index cell I, then the iterator is positioned
 | 
						|
// at I and returns Indexed. Otherwise if T contains one or more (smaller) index cells,
 | 
						|
// then the iterator is positioned at the first such cell I and return Subdivided.
 | 
						|
// Otherwise Disjoint is returned and the iterator position is undefined.
 | 
						|
func (s *ShapeIndexIterator) LocateCellID(target CellID) CellRelation {
 | 
						|
	// Let T be the target, let I = cellMap.LowerBound(T.RangeMin()), and
 | 
						|
	// let I' be the predecessor of I. If T contains any index cells, then T
 | 
						|
	// contains I. Similarly, if T is contained by an index cell, then the
 | 
						|
	// containing cell is either I or I'. We test for containment by comparing
 | 
						|
	// the ranges of leaf cells spanned by T, I, and I'.
 | 
						|
	s.seek(target.RangeMin())
 | 
						|
	if !s.Done() {
 | 
						|
		if s.CellID() >= target && s.CellID().RangeMin() <= target {
 | 
						|
			return Indexed
 | 
						|
		}
 | 
						|
		if s.CellID() <= target.RangeMax() {
 | 
						|
			return Subdivided
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if s.Prev() && s.CellID().RangeMax() >= target {
 | 
						|
		return Indexed
 | 
						|
	}
 | 
						|
	return Disjoint
 | 
						|
}
 | 
						|
 | 
						|
// tracker keeps track of which shapes in a given set contain a particular point
 | 
						|
// (the focus). It provides an efficient way to move the focus from one point
 | 
						|
// to another and incrementally update the set of shapes which contain it. We use
 | 
						|
// this to compute which shapes contain the center of every CellID in the index,
 | 
						|
// by advancing the focus from one cell center to the next.
 | 
						|
//
 | 
						|
// Initially the focus is at the start of the CellID space-filling curve. We then
 | 
						|
// visit all the cells that are being added to the ShapeIndex in increasing order
 | 
						|
// of CellID. For each cell, we draw two edges: one from the entry vertex to the
 | 
						|
// center, and another from the center to the exit vertex (where entry and exit
 | 
						|
// refer to the points where the space-filling curve enters and exits the cell).
 | 
						|
// By counting edge crossings we can incrementally compute which shapes contain
 | 
						|
// the cell center. Note that the same set of shapes will always contain the exit
 | 
						|
// point of one cell and the entry point of the next cell in the index, because
 | 
						|
// either (a) these two points are actually the same, or (b) the intervening
 | 
						|
// cells in CellID order are all empty, and therefore there are no edge crossings
 | 
						|
// if we follow this path from one cell to the other.
 | 
						|
//
 | 
						|
// In C++, this is S2ShapeIndex::InteriorTracker.
 | 
						|
type tracker struct {
 | 
						|
	isActive   bool
 | 
						|
	a          Point
 | 
						|
	b          Point
 | 
						|
	nextCellID CellID
 | 
						|
	crosser    *EdgeCrosser
 | 
						|
	shapeIDs   []int32
 | 
						|
 | 
						|
	// Shape ids saved by saveAndClearStateBefore. The state is never saved
 | 
						|
	// recursively so we don't need to worry about maintaining a stack.
 | 
						|
	savedIDs []int32
 | 
						|
}
 | 
						|
 | 
						|
// newTracker returns a new tracker with the appropriate defaults.
 | 
						|
func newTracker() *tracker {
 | 
						|
	// As shapes are added, we compute which ones contain the start of the
 | 
						|
	// CellID space-filling curve by drawing an edge from OriginPoint to this
 | 
						|
	// point and counting how many shape edges cross this edge.
 | 
						|
	t := &tracker{
 | 
						|
		isActive:   false,
 | 
						|
		b:          trackerOrigin(),
 | 
						|
		nextCellID: CellIDFromFace(0).ChildBeginAtLevel(maxLevel),
 | 
						|
	}
 | 
						|
	t.drawTo(Point{faceUVToXYZ(0, -1, -1).Normalize()}) // CellID curve start
 | 
						|
 | 
						|
	return t
 | 
						|
}
 | 
						|
 | 
						|
// trackerOrigin returns the initial focus point when the tracker is created
 | 
						|
// (corresponding to the start of the CellID space-filling curve).
 | 
						|
func trackerOrigin() Point {
 | 
						|
	// The start of the S2CellId space-filling curve.
 | 
						|
	return Point{faceUVToXYZ(0, -1, -1).Normalize()}
 | 
						|
}
 | 
						|
 | 
						|
// focus returns the current focus point of the tracker.
 | 
						|
func (t *tracker) focus() Point { return t.b }
 | 
						|
 | 
						|
// addShape adds a shape whose interior should be tracked. containsOrigin indicates
 | 
						|
// whether the current focus point is inside the shape. Alternatively, if
 | 
						|
// the focus point is in the process of being moved (via moveTo/drawTo), you
 | 
						|
// can also specify containsOrigin at the old focus point and call testEdge
 | 
						|
// for every edge of the shape that might cross the current drawTo line.
 | 
						|
// This updates the state to correspond to the new focus point.
 | 
						|
//
 | 
						|
// This requires shape.HasInterior
 | 
						|
func (t *tracker) addShape(shapeID int32, containsFocus bool) {
 | 
						|
	t.isActive = true
 | 
						|
	if containsFocus {
 | 
						|
		t.toggleShape(shapeID)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// moveTo moves the focus of the tracker to the given point. This method should
 | 
						|
// only be used when it is known that there are no edge crossings between the old
 | 
						|
// and new focus locations; otherwise use drawTo.
 | 
						|
func (t *tracker) moveTo(b Point) { t.b = b }
 | 
						|
 | 
						|
// drawTo moves the focus of the tracker to the given point. After this method is
 | 
						|
// called, testEdge should be called with all edges that may cross the line
 | 
						|
// segment between the old and new focus locations.
 | 
						|
func (t *tracker) drawTo(b Point) {
 | 
						|
	t.a = t.b
 | 
						|
	t.b = b
 | 
						|
	// TODO: the edge crosser may need an in-place Init method if this gets expensive
 | 
						|
	t.crosser = NewEdgeCrosser(t.a, t.b)
 | 
						|
}
 | 
						|
 | 
						|
// testEdge checks if the given edge crosses the current edge, and if so, then
 | 
						|
// toggle the state of the given shapeID.
 | 
						|
// This requires shape to have an interior.
 | 
						|
func (t *tracker) testEdge(shapeID int32, edge Edge) {
 | 
						|
	if t.crosser.EdgeOrVertexCrossing(edge.V0, edge.V1) {
 | 
						|
		t.toggleShape(shapeID)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// setNextCellID is used to indicate that the last argument to moveTo or drawTo
 | 
						|
// was the entry vertex of the given CellID, i.e. the tracker is positioned at the
 | 
						|
// start of this cell. By using this method together with atCellID, the caller
 | 
						|
// can avoid calling moveTo in cases where the exit vertex of the previous cell
 | 
						|
// is the same as the entry vertex of the current cell.
 | 
						|
func (t *tracker) setNextCellID(nextCellID CellID) {
 | 
						|
	t.nextCellID = nextCellID.RangeMin()
 | 
						|
}
 | 
						|
 | 
						|
// atCellID reports if the focus is already at the entry vertex of the given
 | 
						|
// CellID (provided that the caller calls setNextCellID as each cell is processed).
 | 
						|
func (t *tracker) atCellID(cellid CellID) bool {
 | 
						|
	return cellid.RangeMin() == t.nextCellID
 | 
						|
}
 | 
						|
 | 
						|
// toggleShape adds or removes the given shapeID from the set of IDs it is tracking.
 | 
						|
func (t *tracker) toggleShape(shapeID int32) {
 | 
						|
	// Most shapeIDs slices are small, so special case the common steps.
 | 
						|
 | 
						|
	// If there is nothing here, add it.
 | 
						|
	if len(t.shapeIDs) == 0 {
 | 
						|
		t.shapeIDs = append(t.shapeIDs, shapeID)
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	// If it's the first element, drop it from the slice.
 | 
						|
	if t.shapeIDs[0] == shapeID {
 | 
						|
		t.shapeIDs = t.shapeIDs[1:]
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	for i, s := range t.shapeIDs {
 | 
						|
		if s < shapeID {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
 | 
						|
		// If it's in the set, cut it out.
 | 
						|
		if s == shapeID {
 | 
						|
			copy(t.shapeIDs[i:], t.shapeIDs[i+1:]) // overwrite the ith element
 | 
						|
			t.shapeIDs = t.shapeIDs[:len(t.shapeIDs)-1]
 | 
						|
			return
 | 
						|
		}
 | 
						|
 | 
						|
		// We've got to a point in the slice where we should be inserted.
 | 
						|
		// (the given shapeID is now less than the current positions id.)
 | 
						|
		t.shapeIDs = append(t.shapeIDs[0:i],
 | 
						|
			append([]int32{shapeID}, t.shapeIDs[i:len(t.shapeIDs)]...)...)
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	// We got to the end and didn't find it, so add it to the list.
 | 
						|
	t.shapeIDs = append(t.shapeIDs, shapeID)
 | 
						|
}
 | 
						|
 | 
						|
// saveAndClearStateBefore makes an internal copy of the state for shape ids below
 | 
						|
// the given limit, and then clear the state for those shapes. This is used during
 | 
						|
// incremental updates to track the state of added and removed shapes separately.
 | 
						|
func (t *tracker) saveAndClearStateBefore(limitShapeID int32) {
 | 
						|
	limit := t.lowerBound(limitShapeID)
 | 
						|
	t.savedIDs = append([]int32(nil), t.shapeIDs[:limit]...)
 | 
						|
	t.shapeIDs = t.shapeIDs[limit:]
 | 
						|
}
 | 
						|
 | 
						|
// restoreStateBefore restores the state previously saved by saveAndClearStateBefore.
 | 
						|
// This only affects the state for shapeIDs below "limitShapeID".
 | 
						|
func (t *tracker) restoreStateBefore(limitShapeID int32) {
 | 
						|
	limit := t.lowerBound(limitShapeID)
 | 
						|
	t.shapeIDs = append(append([]int32(nil), t.savedIDs...), t.shapeIDs[limit:]...)
 | 
						|
	t.savedIDs = nil
 | 
						|
}
 | 
						|
 | 
						|
// lowerBound returns the shapeID of the first entry x where x >= shapeID.
 | 
						|
func (t *tracker) lowerBound(shapeID int32) int32 {
 | 
						|
	panic("not implemented")
 | 
						|
}
 | 
						|
 | 
						|
// removedShape represents a set of edges from the given shape that is queued for removal.
 | 
						|
type removedShape struct {
 | 
						|
	shapeID               int32
 | 
						|
	hasInterior           bool
 | 
						|
	containsTrackerOrigin bool
 | 
						|
	edges                 []Edge
 | 
						|
}
 | 
						|
 | 
						|
// There are three basic states the index can be in.
 | 
						|
const (
 | 
						|
	stale    int32 = iota // There are pending updates.
 | 
						|
	updating              // Updates are currently being applied.
 | 
						|
	fresh                 // There are no pending updates.
 | 
						|
)
 | 
						|
 | 
						|
// ShapeIndex indexes a set of Shapes, where a Shape is some collection of edges
 | 
						|
// that optionally defines an interior. It can be used to represent a set of
 | 
						|
// points, a set of polylines, or a set of polygons. For Shapes that have
 | 
						|
// interiors, the index makes it very fast to determine which Shape(s) contain
 | 
						|
// a given point or region.
 | 
						|
//
 | 
						|
// The index can be updated incrementally by adding or removing shapes. It is
 | 
						|
// designed to handle up to hundreds of millions of edges. All data structures
 | 
						|
// are designed to be small, so the index is compact; generally it is smaller
 | 
						|
// than the underlying data being indexed. The index is also fast to construct.
 | 
						|
//
 | 
						|
// Polygon, Loop, and Polyline implement Shape which allows these objects to
 | 
						|
// be indexed easily. You can find useful query methods in CrossingEdgeQuery
 | 
						|
// and ClosestEdgeQuery (Not yet implemented in Go).
 | 
						|
//
 | 
						|
// Example showing how to build an index of Polylines:
 | 
						|
//
 | 
						|
//   index := NewShapeIndex()
 | 
						|
//   for _, polyline := range polylines {
 | 
						|
//       index.Add(polyline);
 | 
						|
//   }
 | 
						|
//   // Now you can use a CrossingEdgeQuery or ClosestEdgeQuery here.
 | 
						|
//
 | 
						|
type ShapeIndex struct {
 | 
						|
	// shapes is a map of shape ID to shape.
 | 
						|
	shapes map[int32]Shape
 | 
						|
 | 
						|
	// The maximum number of edges per cell.
 | 
						|
	// TODO(roberts): Update the comments when the usage of this is implemented.
 | 
						|
	maxEdgesPerCell int
 | 
						|
 | 
						|
	// nextID tracks the next ID to hand out. IDs are not reused when shapes
 | 
						|
	// are removed from the index.
 | 
						|
	nextID int32
 | 
						|
 | 
						|
	// cellMap is a map from CellID to the set of clipped shapes that intersect that
 | 
						|
	// cell. The cell IDs cover a set of non-overlapping regions on the sphere.
 | 
						|
	// In C++, this is a BTree, so the cells are ordered naturally by the data structure.
 | 
						|
	cellMap map[CellID]*ShapeIndexCell
 | 
						|
	// Track the ordered list of cell IDs.
 | 
						|
	cells []CellID
 | 
						|
 | 
						|
	// The current status of the index; accessed atomically.
 | 
						|
	status int32
 | 
						|
 | 
						|
	// Additions and removals are queued and processed on the first subsequent
 | 
						|
	// query. There are several reasons to do this:
 | 
						|
	//
 | 
						|
	//  - It is significantly more efficient to process updates in batches if
 | 
						|
	//    the amount of entities added grows.
 | 
						|
	//  - Often the index will never be queried, in which case we can save both
 | 
						|
	//    the time and memory required to build it. Examples:
 | 
						|
	//     + Loops that are created simply to pass to an Polygon. (We don't
 | 
						|
	//       need the Loop index, because Polygon builds its own index.)
 | 
						|
	//     + Applications that load a database of geometry and then query only
 | 
						|
	//       a small fraction of it.
 | 
						|
	//
 | 
						|
	// The main drawback is that we need to go to some extra work to ensure that
 | 
						|
	// some methods are still thread-safe. Note that the goal is *not* to
 | 
						|
	// make this thread-safe in general, but simply to hide the fact that
 | 
						|
	// we defer some of the indexing work until query time.
 | 
						|
	//
 | 
						|
	// This mutex protects all of following fields in the index.
 | 
						|
	mu sync.RWMutex
 | 
						|
 | 
						|
	// pendingAdditionsPos is the index of the first entry that has not been processed
 | 
						|
	// via applyUpdatesInternal.
 | 
						|
	pendingAdditionsPos int32
 | 
						|
 | 
						|
	// The set of shapes that have been queued for removal but not processed yet by
 | 
						|
	// applyUpdatesInternal.
 | 
						|
	pendingRemovals []*removedShape
 | 
						|
}
 | 
						|
 | 
						|
// NewShapeIndex creates a new ShapeIndex.
 | 
						|
func NewShapeIndex() *ShapeIndex {
 | 
						|
	return &ShapeIndex{
 | 
						|
		maxEdgesPerCell: 10,
 | 
						|
		shapes:          make(map[int32]Shape),
 | 
						|
		cellMap:         make(map[CellID]*ShapeIndexCell),
 | 
						|
		cells:           nil,
 | 
						|
		status:          fresh,
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Iterator returns an iterator for this index.
 | 
						|
func (s *ShapeIndex) Iterator() *ShapeIndexIterator {
 | 
						|
	s.maybeApplyUpdates()
 | 
						|
	return NewShapeIndexIterator(s, IteratorBegin)
 | 
						|
}
 | 
						|
 | 
						|
// Begin positions the iterator at the first cell in the index.
 | 
						|
func (s *ShapeIndex) Begin() *ShapeIndexIterator {
 | 
						|
	s.maybeApplyUpdates()
 | 
						|
	return NewShapeIndexIterator(s, IteratorBegin)
 | 
						|
}
 | 
						|
 | 
						|
// End positions the iterator at the last cell in the index.
 | 
						|
func (s *ShapeIndex) End() *ShapeIndexIterator {
 | 
						|
	// TODO(roberts): It's possible that updates could happen to the index between
 | 
						|
	// the time this is called and the time the iterators position is used and this
 | 
						|
	// will be invalid or not the end. For now, things will be undefined if this
 | 
						|
	// happens. See about referencing the IsFresh to guard for this in the future.
 | 
						|
	s.maybeApplyUpdates()
 | 
						|
	return NewShapeIndexIterator(s, IteratorEnd)
 | 
						|
}
 | 
						|
 | 
						|
// Len reports the number of Shapes in this index.
 | 
						|
func (s *ShapeIndex) Len() int {
 | 
						|
	return len(s.shapes)
 | 
						|
}
 | 
						|
 | 
						|
// Reset resets the index to its original state.
 | 
						|
func (s *ShapeIndex) Reset() {
 | 
						|
	s.shapes = make(map[int32]Shape)
 | 
						|
	s.nextID = 0
 | 
						|
	s.cellMap = make(map[CellID]*ShapeIndexCell)
 | 
						|
	s.cells = nil
 | 
						|
	atomic.StoreInt32(&s.status, fresh)
 | 
						|
}
 | 
						|
 | 
						|
// NumEdges returns the number of edges in this index.
 | 
						|
func (s *ShapeIndex) NumEdges() int {
 | 
						|
	numEdges := 0
 | 
						|
	for _, shape := range s.shapes {
 | 
						|
		numEdges += shape.NumEdges()
 | 
						|
	}
 | 
						|
	return numEdges
 | 
						|
}
 | 
						|
 | 
						|
// NumEdgesUpTo returns the number of edges in the given index, up to the given
 | 
						|
// limit. If the limit is encountered, the current running total is returned,
 | 
						|
// which may be more than the limit.
 | 
						|
func (s *ShapeIndex) NumEdgesUpTo(limit int) int {
 | 
						|
	var numEdges int
 | 
						|
	// We choose to iterate over the shapes in order to match the counting
 | 
						|
	// up behavior in C++ and for test compatibility instead of using a
 | 
						|
	// more idiomatic range over the shape map.
 | 
						|
	for i := int32(0); i <= s.nextID; i++ {
 | 
						|
		s := s.Shape(i)
 | 
						|
		if s == nil {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		numEdges += s.NumEdges()
 | 
						|
		if numEdges >= limit {
 | 
						|
			break
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return numEdges
 | 
						|
}
 | 
						|
 | 
						|
// Shape returns the shape with the given ID, or nil if the shape has been removed from the index.
 | 
						|
func (s *ShapeIndex) Shape(id int32) Shape { return s.shapes[id] }
 | 
						|
 | 
						|
// idForShape returns the id of the given shape in this index, or -1 if it is
 | 
						|
// not in the index.
 | 
						|
//
 | 
						|
// TODO(roberts): Need to figure out an appropriate way to expose this on a Shape.
 | 
						|
// C++ allows a given S2 type (Loop, Polygon, etc) to be part of multiple indexes.
 | 
						|
// By having each type extend S2Shape which has an id element, they all inherit their
 | 
						|
// own id field rather than having to track it themselves.
 | 
						|
func (s *ShapeIndex) idForShape(shape Shape) int32 {
 | 
						|
	for k, v := range s.shapes {
 | 
						|
		if v == shape {
 | 
						|
			return k
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return -1
 | 
						|
}
 | 
						|
 | 
						|
// Add adds the given shape to the index and returns the assigned ID..
 | 
						|
func (s *ShapeIndex) Add(shape Shape) int32 {
 | 
						|
	s.shapes[s.nextID] = shape
 | 
						|
	s.nextID++
 | 
						|
	atomic.StoreInt32(&s.status, stale)
 | 
						|
	return s.nextID - 1
 | 
						|
}
 | 
						|
 | 
						|
// Remove removes the given shape from the index.
 | 
						|
func (s *ShapeIndex) Remove(shape Shape) {
 | 
						|
	// The index updates itself lazily because it is much more efficient to
 | 
						|
	// process additions and removals in batches.
 | 
						|
	id := s.idForShape(shape)
 | 
						|
 | 
						|
	// If the shape wasn't found, it's already been removed or was not in the index.
 | 
						|
	if s.shapes[id] == nil {
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	// Remove the shape from the shapes map.
 | 
						|
	delete(s.shapes, id)
 | 
						|
 | 
						|
	// We are removing a shape that has not yet been added to the index,
 | 
						|
	// so there is nothing else to do.
 | 
						|
	if id >= s.pendingAdditionsPos {
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	numEdges := shape.NumEdges()
 | 
						|
	removed := &removedShape{
 | 
						|
		shapeID:               id,
 | 
						|
		hasInterior:           shape.Dimension() == 2,
 | 
						|
		containsTrackerOrigin: shape.ReferencePoint().Contained,
 | 
						|
		edges:                 make([]Edge, numEdges),
 | 
						|
	}
 | 
						|
 | 
						|
	for e := 0; e < numEdges; e++ {
 | 
						|
		removed.edges[e] = shape.Edge(e)
 | 
						|
	}
 | 
						|
 | 
						|
	s.pendingRemovals = append(s.pendingRemovals, removed)
 | 
						|
	atomic.StoreInt32(&s.status, stale)
 | 
						|
}
 | 
						|
 | 
						|
// Build triggers the update of the index. Calls to Add and Release are normally
 | 
						|
// queued and processed on the first subsequent query. This has many advantages,
 | 
						|
// the most important of which is that sometimes there *is* no subsequent
 | 
						|
// query, which lets us avoid building the index completely.
 | 
						|
//
 | 
						|
// This method forces any pending updates to be applied immediately.
 | 
						|
func (s *ShapeIndex) Build() {
 | 
						|
	s.maybeApplyUpdates()
 | 
						|
}
 | 
						|
 | 
						|
// IsFresh reports if there are no pending updates that need to be applied.
 | 
						|
// This can be useful to avoid building the index unnecessarily, or for
 | 
						|
// choosing between two different algorithms depending on whether the index
 | 
						|
// is available.
 | 
						|
//
 | 
						|
// The returned index status may be slightly out of date if the index was
 | 
						|
// built in a different thread. This is fine for the intended use (as an
 | 
						|
// efficiency hint), but it should not be used by internal methods.
 | 
						|
func (s *ShapeIndex) IsFresh() bool {
 | 
						|
	return atomic.LoadInt32(&s.status) == fresh
 | 
						|
}
 | 
						|
 | 
						|
// isFirstUpdate reports if this is the first update to the index.
 | 
						|
func (s *ShapeIndex) isFirstUpdate() bool {
 | 
						|
	// Note that it is not sufficient to check whether cellMap is empty, since
 | 
						|
	// entries are added to it during the update process.
 | 
						|
	return s.pendingAdditionsPos == 0
 | 
						|
}
 | 
						|
 | 
						|
// isShapeBeingRemoved reports if the shape with the given ID is currently slated for removal.
 | 
						|
func (s *ShapeIndex) isShapeBeingRemoved(shapeID int32) bool {
 | 
						|
	// All shape ids being removed fall below the index position of shapes being added.
 | 
						|
	return shapeID < s.pendingAdditionsPos
 | 
						|
}
 | 
						|
 | 
						|
// maybeApplyUpdates checks if the index pieces have changed, and if so, applies pending updates.
 | 
						|
func (s *ShapeIndex) maybeApplyUpdates() {
 | 
						|
	// TODO(roberts): To avoid acquiring and releasing the mutex on every
 | 
						|
	// query, we should use atomic operations when testing whether the status
 | 
						|
	// is fresh and when updating the status to be fresh. This guarantees
 | 
						|
	// that any thread that sees a status of fresh will also see the
 | 
						|
	// corresponding index updates.
 | 
						|
	if atomic.LoadInt32(&s.status) != fresh {
 | 
						|
		s.mu.Lock()
 | 
						|
		s.applyUpdatesInternal()
 | 
						|
		atomic.StoreInt32(&s.status, fresh)
 | 
						|
		s.mu.Unlock()
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// applyUpdatesInternal does the actual work of updating the index by applying all
 | 
						|
// pending additions and removals. It does *not* update the indexes status.
 | 
						|
func (s *ShapeIndex) applyUpdatesInternal() {
 | 
						|
	// TODO(roberts): Building the index can use up to 20x as much memory per
 | 
						|
	// edge as the final index memory size. If this causes issues, add in
 | 
						|
	// batched updating to limit the amount of items per batch to a
 | 
						|
	// configurable memory footprint overhead.
 | 
						|
	t := newTracker()
 | 
						|
 | 
						|
	// allEdges maps a Face to a collection of faceEdges.
 | 
						|
	allEdges := make([][]faceEdge, 6)
 | 
						|
 | 
						|
	for _, p := range s.pendingRemovals {
 | 
						|
		s.removeShapeInternal(p, allEdges, t)
 | 
						|
	}
 | 
						|
 | 
						|
	for id := s.pendingAdditionsPos; id < int32(len(s.shapes)); id++ {
 | 
						|
		s.addShapeInternal(id, allEdges, t)
 | 
						|
	}
 | 
						|
 | 
						|
	for face := 0; face < 6; face++ {
 | 
						|
		s.updateFaceEdges(face, allEdges[face], t)
 | 
						|
	}
 | 
						|
 | 
						|
	s.pendingRemovals = s.pendingRemovals[:0]
 | 
						|
	s.pendingAdditionsPos = int32(len(s.shapes))
 | 
						|
	// It is the caller's responsibility to update the index status.
 | 
						|
}
 | 
						|
 | 
						|
// addShapeInternal clips all edges of the given shape to the six cube faces,
 | 
						|
// adds the clipped edges to the set of allEdges, and starts tracking its
 | 
						|
// interior if necessary.
 | 
						|
func (s *ShapeIndex) addShapeInternal(shapeID int32, allEdges [][]faceEdge, t *tracker) {
 | 
						|
	shape, ok := s.shapes[shapeID]
 | 
						|
	if !ok {
 | 
						|
		// This shape has already been removed.
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	faceEdge := faceEdge{
 | 
						|
		shapeID:     shapeID,
 | 
						|
		hasInterior: shape.Dimension() == 2,
 | 
						|
	}
 | 
						|
 | 
						|
	if faceEdge.hasInterior {
 | 
						|
		t.addShape(shapeID, containsBruteForce(shape, t.focus()))
 | 
						|
	}
 | 
						|
 | 
						|
	numEdges := shape.NumEdges()
 | 
						|
	for e := 0; e < numEdges; e++ {
 | 
						|
		edge := shape.Edge(e)
 | 
						|
 | 
						|
		faceEdge.edgeID = e
 | 
						|
		faceEdge.edge = edge
 | 
						|
		faceEdge.maxLevel = maxLevelForEdge(edge)
 | 
						|
		s.addFaceEdge(faceEdge, allEdges)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// addFaceEdge adds the given faceEdge into the collection of all edges.
 | 
						|
func (s *ShapeIndex) addFaceEdge(fe faceEdge, allEdges [][]faceEdge) {
 | 
						|
	aFace := face(fe.edge.V0.Vector)
 | 
						|
	// See if both endpoints are on the same face, and are far enough from
 | 
						|
	// the edge of the face that they don't intersect any (padded) adjacent face.
 | 
						|
	if aFace == face(fe.edge.V1.Vector) {
 | 
						|
		x, y := validFaceXYZToUV(aFace, fe.edge.V0.Vector)
 | 
						|
		fe.a = r2.Point{x, y}
 | 
						|
		x, y = validFaceXYZToUV(aFace, fe.edge.V1.Vector)
 | 
						|
		fe.b = r2.Point{x, y}
 | 
						|
 | 
						|
		maxUV := 1 - cellPadding
 | 
						|
		if math.Abs(fe.a.X) <= maxUV && math.Abs(fe.a.Y) <= maxUV &&
 | 
						|
			math.Abs(fe.b.X) <= maxUV && math.Abs(fe.b.Y) <= maxUV {
 | 
						|
			allEdges[aFace] = append(allEdges[aFace], fe)
 | 
						|
			return
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// Otherwise, we simply clip the edge to all six faces.
 | 
						|
	for face := 0; face < 6; face++ {
 | 
						|
		if aClip, bClip, intersects := ClipToPaddedFace(fe.edge.V0, fe.edge.V1, face, cellPadding); intersects {
 | 
						|
			fe.a = aClip
 | 
						|
			fe.b = bClip
 | 
						|
			allEdges[face] = append(allEdges[face], fe)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// updateFaceEdges adds or removes the various edges from the index.
 | 
						|
// An edge is added if shapes[id] is not nil, and removed otherwise.
 | 
						|
func (s *ShapeIndex) updateFaceEdges(face int, faceEdges []faceEdge, t *tracker) {
 | 
						|
	numEdges := len(faceEdges)
 | 
						|
	if numEdges == 0 && len(t.shapeIDs) == 0 {
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	// Create the initial clippedEdge for each faceEdge. Additional clipped
 | 
						|
	// edges are created when edges are split between child cells. We create
 | 
						|
	// two arrays, one containing the edge data and another containing pointers
 | 
						|
	// to those edges, so that during the recursion we only need to copy
 | 
						|
	// pointers in order to propagate an edge to the correct child.
 | 
						|
	clippedEdges := make([]*clippedEdge, numEdges)
 | 
						|
	bound := r2.EmptyRect()
 | 
						|
	for e := 0; e < numEdges; e++ {
 | 
						|
		clipped := &clippedEdge{
 | 
						|
			faceEdge: &faceEdges[e],
 | 
						|
		}
 | 
						|
		clipped.bound = r2.RectFromPoints(faceEdges[e].a, faceEdges[e].b)
 | 
						|
		clippedEdges[e] = clipped
 | 
						|
		bound = bound.AddRect(clipped.bound)
 | 
						|
	}
 | 
						|
 | 
						|
	// Construct the initial face cell containing all the edges, and then update
 | 
						|
	// all the edges in the index recursively.
 | 
						|
	faceID := CellIDFromFace(face)
 | 
						|
	pcell := PaddedCellFromCellID(faceID, cellPadding)
 | 
						|
 | 
						|
	disjointFromIndex := s.isFirstUpdate()
 | 
						|
	if numEdges > 0 {
 | 
						|
		shrunkID := s.shrinkToFit(pcell, bound)
 | 
						|
		if shrunkID != pcell.id {
 | 
						|
			// All the edges are contained by some descendant of the face cell. We
 | 
						|
			// can save a lot of work by starting directly with that cell, but if we
 | 
						|
			// are in the interior of at least one shape then we need to create
 | 
						|
			// index entries for the cells we are skipping over.
 | 
						|
			s.skipCellRange(faceID.RangeMin(), shrunkID.RangeMin(), t, disjointFromIndex)
 | 
						|
			pcell = PaddedCellFromCellID(shrunkID, cellPadding)
 | 
						|
			s.updateEdges(pcell, clippedEdges, t, disjointFromIndex)
 | 
						|
			s.skipCellRange(shrunkID.RangeMax().Next(), faceID.RangeMax().Next(), t, disjointFromIndex)
 | 
						|
			return
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// Otherwise (no edges, or no shrinking is possible), subdivide normally.
 | 
						|
	s.updateEdges(pcell, clippedEdges, t, disjointFromIndex)
 | 
						|
}
 | 
						|
 | 
						|
// shrinkToFit shrinks the PaddedCell to fit within the given bounds.
 | 
						|
func (s *ShapeIndex) shrinkToFit(pcell *PaddedCell, bound r2.Rect) CellID {
 | 
						|
	shrunkID := pcell.ShrinkToFit(bound)
 | 
						|
 | 
						|
	if !s.isFirstUpdate() && shrunkID != pcell.CellID() {
 | 
						|
		// Don't shrink any smaller than the existing index cells, since we need
 | 
						|
		// to combine the new edges with those cells.
 | 
						|
		iter := s.Iterator()
 | 
						|
		if iter.LocateCellID(shrunkID) == Indexed {
 | 
						|
			shrunkID = iter.CellID()
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return shrunkID
 | 
						|
}
 | 
						|
 | 
						|
// skipCellRange skips over the cells in the given range, creating index cells if we are
 | 
						|
// currently in the interior of at least one shape.
 | 
						|
func (s *ShapeIndex) skipCellRange(begin, end CellID, t *tracker, disjointFromIndex bool) {
 | 
						|
	// If we aren't in the interior of a shape, then skipping over cells is easy.
 | 
						|
	if len(t.shapeIDs) == 0 {
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	// Otherwise generate the list of cell ids that we need to visit, and create
 | 
						|
	// an index entry for each one.
 | 
						|
	skipped := CellUnionFromRange(begin, end)
 | 
						|
	for _, cell := range skipped {
 | 
						|
		var clippedEdges []*clippedEdge
 | 
						|
		s.updateEdges(PaddedCellFromCellID(cell, cellPadding), clippedEdges, t, disjointFromIndex)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// updateEdges adds or removes the given edges whose bounding boxes intersect a
 | 
						|
// given cell. disjointFromIndex is an optimization hint indicating that cellMap
 | 
						|
// does not contain any entries that overlap the given cell.
 | 
						|
func (s *ShapeIndex) updateEdges(pcell *PaddedCell, edges []*clippedEdge, t *tracker, disjointFromIndex bool) {
 | 
						|
	// This function is recursive with a maximum recursion depth of 30 (maxLevel).
 | 
						|
 | 
						|
	// Incremental updates are handled as follows. All edges being added or
 | 
						|
	// removed are combined together in edges, and all shapes with interiors
 | 
						|
	// are tracked using tracker. We subdivide recursively as usual until we
 | 
						|
	// encounter an existing index cell. At this point we absorb the index
 | 
						|
	// cell as follows:
 | 
						|
	//
 | 
						|
	//   - Edges and shapes that are being removed are deleted from edges and
 | 
						|
	//     tracker.
 | 
						|
	//   - All remaining edges and shapes from the index cell are added to
 | 
						|
	//     edges and tracker.
 | 
						|
	//   - Continue subdividing recursively, creating new index cells as needed.
 | 
						|
	//   - When the recursion gets back to the cell that was absorbed, we
 | 
						|
	//     restore edges and tracker to their previous state.
 | 
						|
	//
 | 
						|
	// Note that the only reason that we include removed shapes in the recursive
 | 
						|
	// subdivision process is so that we can find all of the index cells that
 | 
						|
	// contain those shapes efficiently, without maintaining an explicit list of
 | 
						|
	// index cells for each shape (which would be expensive in terms of memory).
 | 
						|
	indexCellAbsorbed := false
 | 
						|
	if !disjointFromIndex {
 | 
						|
		// There may be existing index cells contained inside pcell. If we
 | 
						|
		// encounter such a cell, we need to combine the edges being updated with
 | 
						|
		// the existing cell contents by absorbing the cell.
 | 
						|
		iter := s.Iterator()
 | 
						|
		r := iter.LocateCellID(pcell.id)
 | 
						|
		if r == Disjoint {
 | 
						|
			disjointFromIndex = true
 | 
						|
		} else if r == Indexed {
 | 
						|
			// Absorb the index cell by transferring its contents to edges and
 | 
						|
			// deleting it. We also start tracking the interior of any new shapes.
 | 
						|
			s.absorbIndexCell(pcell, iter, edges, t)
 | 
						|
			indexCellAbsorbed = true
 | 
						|
			disjointFromIndex = true
 | 
						|
		} else {
 | 
						|
			// DCHECK_EQ(SUBDIVIDED, r)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// If there are existing index cells below us, then we need to keep
 | 
						|
	// subdividing so that we can merge with those cells. Otherwise,
 | 
						|
	// makeIndexCell checks if the number of edges is small enough, and creates
 | 
						|
	// an index cell if possible (returning true when it does so).
 | 
						|
	if !disjointFromIndex || !s.makeIndexCell(pcell, edges, t) {
 | 
						|
		// TODO(roberts): If it turns out to have memory problems when there
 | 
						|
		// are 10M+ edges in the index, look into pre-allocating space so we
 | 
						|
		// are not always appending.
 | 
						|
		childEdges := [2][2][]*clippedEdge{} // [i][j]
 | 
						|
 | 
						|
		// Compute the middle of the padded cell, defined as the rectangle in
 | 
						|
		// (u,v)-space that belongs to all four (padded) children. By comparing
 | 
						|
		// against the four boundaries of middle we can determine which children
 | 
						|
		// each edge needs to be propagated to.
 | 
						|
		middle := pcell.Middle()
 | 
						|
 | 
						|
		// Build up a vector edges to be passed to each child cell. The (i,j)
 | 
						|
		// directions are left (i=0), right (i=1), lower (j=0), and upper (j=1).
 | 
						|
		// Note that the vast majority of edges are propagated to a single child.
 | 
						|
		for _, edge := range edges {
 | 
						|
			if edge.bound.X.Hi <= middle.X.Lo {
 | 
						|
				// Edge is entirely contained in the two left children.
 | 
						|
				a, b := s.clipVAxis(edge, middle.Y)
 | 
						|
				if a != nil {
 | 
						|
					childEdges[0][0] = append(childEdges[0][0], a)
 | 
						|
				}
 | 
						|
				if b != nil {
 | 
						|
					childEdges[0][1] = append(childEdges[0][1], b)
 | 
						|
				}
 | 
						|
			} else if edge.bound.X.Lo >= middle.X.Hi {
 | 
						|
				// Edge is entirely contained in the two right children.
 | 
						|
				a, b := s.clipVAxis(edge, middle.Y)
 | 
						|
				if a != nil {
 | 
						|
					childEdges[1][0] = append(childEdges[1][0], a)
 | 
						|
				}
 | 
						|
				if b != nil {
 | 
						|
					childEdges[1][1] = append(childEdges[1][1], b)
 | 
						|
				}
 | 
						|
			} else if edge.bound.Y.Hi <= middle.Y.Lo {
 | 
						|
				// Edge is entirely contained in the two lower children.
 | 
						|
				if a := s.clipUBound(edge, 1, middle.X.Hi); a != nil {
 | 
						|
					childEdges[0][0] = append(childEdges[0][0], a)
 | 
						|
				}
 | 
						|
				if b := s.clipUBound(edge, 0, middle.X.Lo); b != nil {
 | 
						|
					childEdges[1][0] = append(childEdges[1][0], b)
 | 
						|
				}
 | 
						|
			} else if edge.bound.Y.Lo >= middle.Y.Hi {
 | 
						|
				// Edge is entirely contained in the two upper children.
 | 
						|
				if a := s.clipUBound(edge, 1, middle.X.Hi); a != nil {
 | 
						|
					childEdges[0][1] = append(childEdges[0][1], a)
 | 
						|
				}
 | 
						|
				if b := s.clipUBound(edge, 0, middle.X.Lo); b != nil {
 | 
						|
					childEdges[1][1] = append(childEdges[1][1], b)
 | 
						|
				}
 | 
						|
			} else {
 | 
						|
				// The edge bound spans all four children. The edge
 | 
						|
				// itself intersects either three or four padded children.
 | 
						|
				left := s.clipUBound(edge, 1, middle.X.Hi)
 | 
						|
				a, b := s.clipVAxis(left, middle.Y)
 | 
						|
				if a != nil {
 | 
						|
					childEdges[0][0] = append(childEdges[0][0], a)
 | 
						|
				}
 | 
						|
				if b != nil {
 | 
						|
					childEdges[0][1] = append(childEdges[0][1], b)
 | 
						|
				}
 | 
						|
				right := s.clipUBound(edge, 0, middle.X.Lo)
 | 
						|
				a, b = s.clipVAxis(right, middle.Y)
 | 
						|
				if a != nil {
 | 
						|
					childEdges[1][0] = append(childEdges[1][0], a)
 | 
						|
				}
 | 
						|
				if b != nil {
 | 
						|
					childEdges[1][1] = append(childEdges[1][1], b)
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		// Now recursively update the edges in each child. We call the children in
 | 
						|
		// increasing order of CellID so that when the index is first constructed,
 | 
						|
		// all insertions into cellMap are at the end (which is much faster).
 | 
						|
		for pos := 0; pos < 4; pos++ {
 | 
						|
			i, j := pcell.ChildIJ(pos)
 | 
						|
			if len(childEdges[i][j]) > 0 || len(t.shapeIDs) > 0 {
 | 
						|
				s.updateEdges(PaddedCellFromParentIJ(pcell, i, j), childEdges[i][j],
 | 
						|
					t, disjointFromIndex)
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if indexCellAbsorbed {
 | 
						|
		// Restore the state for any edges being removed that we are tracking.
 | 
						|
		t.restoreStateBefore(s.pendingAdditionsPos)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// makeIndexCell builds an indexCell from the given padded cell and set of edges and adds
 | 
						|
// it to the index. If the cell or edges are empty, no cell is added.
 | 
						|
func (s *ShapeIndex) makeIndexCell(p *PaddedCell, edges []*clippedEdge, t *tracker) bool {
 | 
						|
	// If the cell is empty, no index cell is needed. (In most cases this
 | 
						|
	// situation is detected before we get to this point, but this can happen
 | 
						|
	// when all shapes in a cell are removed.)
 | 
						|
	if len(edges) == 0 && len(t.shapeIDs) == 0 {
 | 
						|
		return true
 | 
						|
	}
 | 
						|
 | 
						|
	// Count the number of edges that have not reached their maximum level yet.
 | 
						|
	// Return false if there are too many such edges.
 | 
						|
	count := 0
 | 
						|
	for _, ce := range edges {
 | 
						|
		if p.Level() < ce.faceEdge.maxLevel {
 | 
						|
			count++
 | 
						|
		}
 | 
						|
 | 
						|
		if count > s.maxEdgesPerCell {
 | 
						|
			return false
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// Possible optimization: Continue subdividing as long as exactly one child
 | 
						|
	// of the padded cell intersects the given edges. This can be done by finding
 | 
						|
	// the bounding box of all the edges and calling ShrinkToFit:
 | 
						|
	//
 | 
						|
	// cellID = p.ShrinkToFit(RectBound(edges));
 | 
						|
	//
 | 
						|
	// Currently this is not beneficial; it slows down construction by 4-25%
 | 
						|
	// (mainly computing the union of the bounding rectangles) and also slows
 | 
						|
	// down queries (since more recursive clipping is required to get down to
 | 
						|
	// the level of a spatial index cell). But it may be worth trying again
 | 
						|
	// once containsCenter is computed and all algorithms are modified to
 | 
						|
	// take advantage of it.
 | 
						|
 | 
						|
	// We update the InteriorTracker as follows. For every Cell in the index
 | 
						|
	// we construct two edges: one edge from entry vertex of the cell to its
 | 
						|
	// center, and one from the cell center to its exit vertex. Here entry
 | 
						|
	// and exit refer the CellID ordering, i.e. the order in which points
 | 
						|
	// are encountered along the 2 space-filling curve. The exit vertex then
 | 
						|
	// becomes the entry vertex for the next cell in the index, unless there are
 | 
						|
	// one or more empty intervening cells, in which case the InteriorTracker
 | 
						|
	// state is unchanged because the intervening cells have no edges.
 | 
						|
 | 
						|
	// Shift the InteriorTracker focus point to the center of the current cell.
 | 
						|
	if t.isActive && len(edges) != 0 {
 | 
						|
		if !t.atCellID(p.id) {
 | 
						|
			t.moveTo(p.EntryVertex())
 | 
						|
		}
 | 
						|
		t.drawTo(p.Center())
 | 
						|
		s.testAllEdges(edges, t)
 | 
						|
	}
 | 
						|
 | 
						|
	// Allocate and fill a new index cell. To get the total number of shapes we
 | 
						|
	// need to merge the shapes associated with the intersecting edges together
 | 
						|
	// with the shapes that happen to contain the cell center.
 | 
						|
	cshapeIDs := t.shapeIDs
 | 
						|
	numShapes := s.countShapes(edges, cshapeIDs)
 | 
						|
	cell := NewShapeIndexCell(numShapes)
 | 
						|
 | 
						|
	// To fill the index cell we merge the two sources of shapes: edge shapes
 | 
						|
	// (those that have at least one edge that intersects this cell), and
 | 
						|
	// containing shapes (those that contain the cell center). We keep track
 | 
						|
	// of the index of the next intersecting edge and the next containing shape
 | 
						|
	// as we go along. Both sets of shape ids are already sorted.
 | 
						|
	eNext := 0
 | 
						|
	cNextIdx := 0
 | 
						|
	for i := 0; i < numShapes; i++ {
 | 
						|
		var clipped *clippedShape
 | 
						|
		// advance to next value base + i
 | 
						|
		eshapeID := int32(s.Len())
 | 
						|
		cshapeID := eshapeID // Sentinels
 | 
						|
 | 
						|
		if eNext != len(edges) {
 | 
						|
			eshapeID = edges[eNext].faceEdge.shapeID
 | 
						|
		}
 | 
						|
		if cNextIdx < len(cshapeIDs) {
 | 
						|
			cshapeID = cshapeIDs[cNextIdx]
 | 
						|
		}
 | 
						|
		eBegin := eNext
 | 
						|
		if cshapeID < eshapeID {
 | 
						|
			// The entire cell is in the shape interior.
 | 
						|
			clipped = newClippedShape(cshapeID, 0)
 | 
						|
			clipped.containsCenter = true
 | 
						|
			cNextIdx++
 | 
						|
		} else {
 | 
						|
			// Count the number of edges for this shape and allocate space for them.
 | 
						|
			for eNext < len(edges) && edges[eNext].faceEdge.shapeID == eshapeID {
 | 
						|
				eNext++
 | 
						|
			}
 | 
						|
			clipped = newClippedShape(eshapeID, eNext-eBegin)
 | 
						|
			for e := eBegin; e < eNext; e++ {
 | 
						|
				clipped.edges[e-eBegin] = edges[e].faceEdge.edgeID
 | 
						|
			}
 | 
						|
			if cshapeID == eshapeID {
 | 
						|
				clipped.containsCenter = true
 | 
						|
				cNextIdx++
 | 
						|
			}
 | 
						|
		}
 | 
						|
		cell.shapes[i] = clipped
 | 
						|
	}
 | 
						|
 | 
						|
	// Add this cell to the map.
 | 
						|
	s.cellMap[p.id] = cell
 | 
						|
	s.cells = append(s.cells, p.id)
 | 
						|
 | 
						|
	// Shift the tracker focus point to the exit vertex of this cell.
 | 
						|
	if t.isActive && len(edges) != 0 {
 | 
						|
		t.drawTo(p.ExitVertex())
 | 
						|
		s.testAllEdges(edges, t)
 | 
						|
		t.setNextCellID(p.id.Next())
 | 
						|
	}
 | 
						|
	return true
 | 
						|
}
 | 
						|
 | 
						|
// updateBound updates the specified endpoint of the given clipped edge and returns the
 | 
						|
// resulting clipped edge.
 | 
						|
func (s *ShapeIndex) updateBound(edge *clippedEdge, uEnd int, u float64, vEnd int, v float64) *clippedEdge {
 | 
						|
	c := &clippedEdge{faceEdge: edge.faceEdge}
 | 
						|
	if uEnd == 0 {
 | 
						|
		c.bound.X.Lo = u
 | 
						|
		c.bound.X.Hi = edge.bound.X.Hi
 | 
						|
	} else {
 | 
						|
		c.bound.X.Lo = edge.bound.X.Lo
 | 
						|
		c.bound.X.Hi = u
 | 
						|
	}
 | 
						|
 | 
						|
	if vEnd == 0 {
 | 
						|
		c.bound.Y.Lo = v
 | 
						|
		c.bound.Y.Hi = edge.bound.Y.Hi
 | 
						|
	} else {
 | 
						|
		c.bound.Y.Lo = edge.bound.Y.Lo
 | 
						|
		c.bound.Y.Hi = v
 | 
						|
	}
 | 
						|
 | 
						|
	return c
 | 
						|
}
 | 
						|
 | 
						|
// clipUBound clips the given endpoint (lo=0, hi=1) of the u-axis so that
 | 
						|
// it does not extend past the given value of the given edge.
 | 
						|
func (s *ShapeIndex) clipUBound(edge *clippedEdge, uEnd int, u float64) *clippedEdge {
 | 
						|
	// First check whether the edge actually requires any clipping. (Sometimes
 | 
						|
	// this method is called when clipping is not necessary, e.g. when one edge
 | 
						|
	// endpoint is in the overlap area between two padded child cells.)
 | 
						|
	if uEnd == 0 {
 | 
						|
		if edge.bound.X.Lo >= u {
 | 
						|
			return edge
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		if edge.bound.X.Hi <= u {
 | 
						|
			return edge
 | 
						|
		}
 | 
						|
	}
 | 
						|
	// We interpolate the new v-value from the endpoints of the original edge.
 | 
						|
	// This has two advantages: (1) we don't need to store the clipped endpoints
 | 
						|
	// at all, just their bounding box; and (2) it avoids the accumulation of
 | 
						|
	// roundoff errors due to repeated interpolations. The result needs to be
 | 
						|
	// clamped to ensure that it is in the appropriate range.
 | 
						|
	e := edge.faceEdge
 | 
						|
	v := edge.bound.Y.ClampPoint(interpolateFloat64(u, e.a.X, e.b.X, e.a.Y, e.b.Y))
 | 
						|
 | 
						|
	// Determine which endpoint of the v-axis bound to update. If the edge
 | 
						|
	// slope is positive we update the same endpoint, otherwise we update the
 | 
						|
	// opposite endpoint.
 | 
						|
	var vEnd int
 | 
						|
	positiveSlope := (e.a.X > e.b.X) == (e.a.Y > e.b.Y)
 | 
						|
	if (uEnd == 1) == positiveSlope {
 | 
						|
		vEnd = 1
 | 
						|
	}
 | 
						|
	return s.updateBound(edge, uEnd, u, vEnd, v)
 | 
						|
}
 | 
						|
 | 
						|
// clipVBound clips the given endpoint (lo=0, hi=1) of the v-axis so that
 | 
						|
// it does not extend past the given value of the given edge.
 | 
						|
func (s *ShapeIndex) clipVBound(edge *clippedEdge, vEnd int, v float64) *clippedEdge {
 | 
						|
	if vEnd == 0 {
 | 
						|
		if edge.bound.Y.Lo >= v {
 | 
						|
			return edge
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		if edge.bound.Y.Hi <= v {
 | 
						|
			return edge
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// We interpolate the new v-value from the endpoints of the original edge.
 | 
						|
	// This has two advantages: (1) we don't need to store the clipped endpoints
 | 
						|
	// at all, just their bounding box; and (2) it avoids the accumulation of
 | 
						|
	// roundoff errors due to repeated interpolations. The result needs to be
 | 
						|
	// clamped to ensure that it is in the appropriate range.
 | 
						|
	e := edge.faceEdge
 | 
						|
	u := edge.bound.X.ClampPoint(interpolateFloat64(v, e.a.Y, e.b.Y, e.a.X, e.b.X))
 | 
						|
 | 
						|
	// Determine which endpoint of the v-axis bound to update. If the edge
 | 
						|
	// slope is positive we update the same endpoint, otherwise we update the
 | 
						|
	// opposite endpoint.
 | 
						|
	var uEnd int
 | 
						|
	positiveSlope := (e.a.X > e.b.X) == (e.a.Y > e.b.Y)
 | 
						|
	if (vEnd == 1) == positiveSlope {
 | 
						|
		uEnd = 1
 | 
						|
	}
 | 
						|
	return s.updateBound(edge, uEnd, u, vEnd, v)
 | 
						|
}
 | 
						|
 | 
						|
// cliupVAxis returns the given edge clipped to within the boundaries of the middle
 | 
						|
// interval along the v-axis, and adds the result to its children.
 | 
						|
func (s *ShapeIndex) clipVAxis(edge *clippedEdge, middle r1.Interval) (a, b *clippedEdge) {
 | 
						|
	if edge.bound.Y.Hi <= middle.Lo {
 | 
						|
		// Edge is entirely contained in the lower child.
 | 
						|
		return edge, nil
 | 
						|
	} else if edge.bound.Y.Lo >= middle.Hi {
 | 
						|
		// Edge is entirely contained in the upper child.
 | 
						|
		return nil, edge
 | 
						|
	}
 | 
						|
	// The edge bound spans both children.
 | 
						|
	return s.clipVBound(edge, 1, middle.Hi), s.clipVBound(edge, 0, middle.Lo)
 | 
						|
}
 | 
						|
 | 
						|
// absorbIndexCell absorbs an index cell by transferring its contents to edges
 | 
						|
// and/or "tracker", and then delete this cell from the index. If edges includes
 | 
						|
// any edges that are being removed, this method also updates their
 | 
						|
// InteriorTracker state to correspond to the exit vertex of this cell.
 | 
						|
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
 | 
						|
}
 | 
						|
 | 
						|
// testAllEdges calls the trackers testEdge on all edges from shapes that have interiors.
 | 
						|
func (s *ShapeIndex) testAllEdges(edges []*clippedEdge, t *tracker) {
 | 
						|
	for _, edge := range edges {
 | 
						|
		if edge.faceEdge.hasInterior {
 | 
						|
			t.testEdge(edge.faceEdge.shapeID, edge.faceEdge.edge)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// countShapes reports the number of distinct shapes that are either associated with the
 | 
						|
// given edges, or that are currently stored in the InteriorTracker.
 | 
						|
func (s *ShapeIndex) countShapes(edges []*clippedEdge, shapeIDs []int32) int {
 | 
						|
	count := 0
 | 
						|
	lastShapeID := int32(-1)
 | 
						|
 | 
						|
	// next clipped shape id in the shapeIDs list.
 | 
						|
	clippedNext := int32(0)
 | 
						|
	// index of the current element in the shapeIDs list.
 | 
						|
	shapeIDidx := 0
 | 
						|
	for _, edge := range edges {
 | 
						|
		if edge.faceEdge.shapeID == lastShapeID {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
 | 
						|
		count++
 | 
						|
		lastShapeID = edge.faceEdge.shapeID
 | 
						|
 | 
						|
		// Skip over any containing shapes up to and including this one,
 | 
						|
		// updating count as appropriate.
 | 
						|
		for ; shapeIDidx < len(shapeIDs); shapeIDidx++ {
 | 
						|
			clippedNext = shapeIDs[shapeIDidx]
 | 
						|
			if clippedNext > lastShapeID {
 | 
						|
				break
 | 
						|
			}
 | 
						|
			if clippedNext < lastShapeID {
 | 
						|
				count++
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// Count any remaining containing shapes.
 | 
						|
	count += len(shapeIDs) - shapeIDidx
 | 
						|
	return count
 | 
						|
}
 | 
						|
 | 
						|
// maxLevelForEdge reports the maximum level for a given edge.
 | 
						|
func maxLevelForEdge(edge Edge) int {
 | 
						|
	// Compute the maximum cell size for which this edge is considered long.
 | 
						|
	// The calculation does not need to be perfectly accurate, so we use Norm
 | 
						|
	// rather than Angle for speed.
 | 
						|
	cellSize := edge.V0.Sub(edge.V1.Vector).Norm() * cellSizeToLongEdgeRatio
 | 
						|
	// Now return the first level encountered during subdivision where the
 | 
						|
	// average cell size is at most cellSize.
 | 
						|
	return AvgEdgeMetric.MinLevel(cellSize)
 | 
						|
}
 | 
						|
 | 
						|
// removeShapeInternal does the actual work for removing a given shape from the index.
 | 
						|
func (s *ShapeIndex) removeShapeInternal(removed *removedShape, allEdges [][]faceEdge, t *tracker) {
 | 
						|
	// TODO(roberts): finish the implementation of this.
 | 
						|
}
 |