diff options
author | 2021-08-12 21:03:24 +0200 | |
---|---|---|
committer | 2021-08-12 21:03:24 +0200 | |
commit | 98263a7de64269898a2f81207e38943b5c8e8653 (patch) | |
tree | 743c90f109a6c5d27832d1dcef2388d939f0f77a /vendor/github.com/golang/geo/s2/shapeutil.go | |
parent | Text duplication fix (#137) (diff) | |
download | gotosocial-98263a7de64269898a2f81207e38943b5c8e8653.tar.xz |
Grand test fixup (#138)
* 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
Diffstat (limited to 'vendor/github.com/golang/geo/s2/shapeutil.go')
-rw-r--r-- | vendor/github.com/golang/geo/s2/shapeutil.go | 228 |
1 files changed, 228 insertions, 0 deletions
diff --git a/vendor/github.com/golang/geo/s2/shapeutil.go b/vendor/github.com/golang/geo/s2/shapeutil.go new file mode 100644 index 000000000..64245dfa1 --- /dev/null +++ b/vendor/github.com/golang/geo/s2/shapeutil.go @@ -0,0 +1,228 @@ +// Copyright 2017 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 + +// CrossingType defines different ways of reporting edge intersections. +type CrossingType int + +const ( + // CrossingTypeInterior reports intersections that occur at a point + // interior to both edges (i.e., not at a vertex). + CrossingTypeInterior CrossingType = iota + + // CrossingTypeAll reports all intersections, even those where two edges + // intersect only because they share a common vertex. + CrossingTypeAll + + // CrossingTypeNonAdjacent reports all intersections except for pairs of + // the form (AB, BC) where both edges are from the same ShapeIndex. + CrossingTypeNonAdjacent +) + +// rangeIterator is a wrapper over ShapeIndexIterator with extra methods +// that are useful for merging the contents of two or more ShapeIndexes. +type rangeIterator struct { + it *ShapeIndexIterator + // The min and max leaf cell ids covered by the current cell. If done() is + // true, these methods return a value larger than any valid cell id. + rangeMin CellID + rangeMax CellID +} + +// newRangeIterator creates a new rangeIterator positioned at the first cell of the given index. +func newRangeIterator(index *ShapeIndex) *rangeIterator { + r := &rangeIterator{ + it: index.Iterator(), + } + r.refresh() + return r +} + +func (r *rangeIterator) cellID() CellID { return r.it.CellID() } +func (r *rangeIterator) indexCell() *ShapeIndexCell { return r.it.IndexCell() } +func (r *rangeIterator) next() { r.it.Next(); r.refresh() } +func (r *rangeIterator) done() bool { return r.it.Done() } + +// seekTo positions the iterator at the first cell that overlaps or follows +// the current range minimum of the target iterator, i.e. such that its +// rangeMax >= target.rangeMin. +func (r *rangeIterator) seekTo(target *rangeIterator) { + r.it.seek(target.rangeMin) + // If the current cell does not overlap target, it is possible that the + // previous cell is the one we are looking for. This can only happen when + // the previous cell contains target but has a smaller CellID. + if r.it.Done() || r.it.CellID().RangeMin() > target.rangeMax { + if r.it.Prev() && r.it.CellID().RangeMax() < target.cellID() { + r.it.Next() + } + } + r.refresh() +} + +// seekBeyond positions the iterator at the first cell that follows the current +// range minimum of the target iterator. i.e. the first cell such that its +// rangeMin > target.rangeMax. +func (r *rangeIterator) seekBeyond(target *rangeIterator) { + r.it.seek(target.rangeMax.Next()) + if !r.it.Done() && r.it.CellID().RangeMin() <= target.rangeMax { + r.it.Next() + } + r.refresh() +} + +// refresh updates the iterators min and max values. +func (r *rangeIterator) refresh() { + r.rangeMin = r.cellID().RangeMin() + r.rangeMax = r.cellID().RangeMax() +} + +// referencePointForShape is a helper function for implementing various Shapes +// ReferencePoint functions. +// +// Given a shape consisting of closed polygonal loops, the interior of the +// shape is defined as the region to the left of all edges (which must be +// oriented consistently). This function then chooses an arbitrary point and +// returns true if that point is contained by the shape. +// +// Unlike Loop and Polygon, this method allows duplicate vertices and +// edges, which requires some extra care with definitions. The rule that we +// apply is that an edge and its reverse edge cancel each other: the result +// is the same as if that edge pair were not present. Therefore shapes that +// consist only of degenerate loop(s) are either empty or full; by convention, +// the shape is considered full if and only if it contains an empty loop (see +// laxPolygon for details). +// +// Determining whether a loop on the sphere contains a point is harder than +// the corresponding problem in 2D plane geometry. It cannot be implemented +// just by counting edge crossings because there is no such thing as a point +// at infinity that is guaranteed to be outside the loop. +// +// This function requires that the given Shape have an interior. +func referencePointForShape(shape Shape) ReferencePoint { + if shape.NumEdges() == 0 { + // A shape with no edges is defined to be full if and only if it + // contains at least one chain. + return OriginReferencePoint(shape.NumChains() > 0) + } + // Define a "matched" edge as one that can be paired with a corresponding + // reversed edge. Define a vertex as "balanced" if all of its edges are + // matched. In order to determine containment, we must find an unbalanced + // vertex. Often every vertex is unbalanced, so we start by trying an + // arbitrary vertex. + edge := shape.Edge(0) + + if ref, ok := referencePointAtVertex(shape, edge.V0); ok { + return ref + } + + // That didn't work, so now we do some extra work to find an unbalanced + // vertex (if any). Essentially we gather a list of edges and a list of + // reversed edges, and then sort them. The first edge that appears in one + // list but not the other is guaranteed to be unmatched. + n := shape.NumEdges() + var edges = make([]Edge, n) + var revEdges = make([]Edge, n) + for i := 0; i < n; i++ { + edge := shape.Edge(i) + edges[i] = edge + revEdges[i] = Edge{V0: edge.V1, V1: edge.V0} + } + + sortEdges(edges) + sortEdges(revEdges) + + for i := 0; i < n; i++ { + if edges[i].Cmp(revEdges[i]) == -1 { // edges[i] is unmatched + if ref, ok := referencePointAtVertex(shape, edges[i].V0); ok { + return ref + } + } + if revEdges[i].Cmp(edges[i]) == -1 { // revEdges[i] is unmatched + if ref, ok := referencePointAtVertex(shape, revEdges[i].V0); ok { + return ref + } + } + } + + // All vertices are balanced, so this polygon is either empty or full except + // for degeneracies. By convention it is defined to be full if it contains + // any chain with no edges. + for i := 0; i < shape.NumChains(); i++ { + if shape.Chain(i).Length == 0 { + return OriginReferencePoint(true) + } + } + + return OriginReferencePoint(false) +} + +// referencePointAtVertex reports whether the given vertex is unbalanced, and +// returns a ReferencePoint indicating if the point is contained. +// Otherwise returns false. +func referencePointAtVertex(shape Shape, vTest Point) (ReferencePoint, bool) { + var ref ReferencePoint + + // Let P be an unbalanced vertex. Vertex P is defined to be inside the + // region if the region contains a particular direction vector starting from + // P, namely the direction p.Ortho(). This can be calculated using + // ContainsVertexQuery. + + containsQuery := NewContainsVertexQuery(vTest) + n := shape.NumEdges() + for e := 0; e < n; e++ { + edge := shape.Edge(e) + if edge.V0 == vTest { + containsQuery.AddEdge(edge.V1, 1) + } + if edge.V1 == vTest { + containsQuery.AddEdge(edge.V0, -1) + } + } + containsSign := containsQuery.ContainsVertex() + if containsSign == 0 { + return ref, false // There are no unmatched edges incident to this vertex. + } + ref.Point = vTest + ref.Contained = containsSign > 0 + + return ref, true +} + +// containsBruteForce reports whether the given shape contains the given point. +// Most clients should not use this method, since its running time is linear in +// the number of shape edges. Instead clients should create a ShapeIndex and use +// ContainsPointQuery, since this strategy is much more efficient when many +// points need to be tested. +// +// Polygon boundaries are treated as being semi-open (see ContainsPointQuery +// and VertexModel for other options). +func containsBruteForce(shape Shape, point Point) bool { + if shape.Dimension() != 2 { + return false + } + + refPoint := shape.ReferencePoint() + if refPoint.Point == point { + return refPoint.Contained + } + + crosser := NewEdgeCrosser(refPoint.Point, point) + inside := refPoint.Contained + for e := 0; e < shape.NumEdges(); e++ { + edge := shape.Edge(e) + inside = inside != crosser.EdgeOrVertexCrossing(edge.V0, edge.V1) + } + return inside +} |