[feature] support processing of (many) more media types (#3090)

* initial work replacing our media decoding / encoding pipeline with ffprobe + ffmpeg

* specify the video codec to use when generating static image from emoji

* update go-storage library (fixes incompatibility after updating go-iotools)

* maintain image aspect ratio when generating a thumbnail for it

* update readme to show go-ffmpreg

* fix a bunch of media tests, move filesize checking to callers of media manager for more flexibility

* remove extra debug from error message

* fix up incorrect function signatures

* update PutFile to just use regular file copy, as changes are file is on separate partition

* fix remaining tests, remove some unneeded tests now we're working with ffmpeg/ffprobe

* update more tests, add more code comments

* add utilities to generate processed emoji / media outputs

* fix remaining tests

* add test for opus media file, add license header to utility cmds

* limit the number of concurrently available ffmpeg / ffprobe instances

* reduce number of instances

* further reduce number of instances

* fix envparsing test with configuration variables

* update docs and configuration with new media-{local,remote}-max-size variables
This commit is contained in:
kim
2024-07-12 09:39:47 +00:00
committed by GitHub
parent 5bc567196b
commit cde2fb6244
376 changed files with 8026 additions and 54091 deletions

View File

@ -1,53 +0,0 @@
// Copyright 2018 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.
// +build !go1.9
package s2
// This file is for the bit manipulation code pre-Go 1.9.
// findMSBSetNonZero64 returns the index (between 0 and 63) of the most
// significant set bit. Passing zero to this function returns zero.
func findMSBSetNonZero64(x uint64) int {
val := []uint64{0x2, 0xC, 0xF0, 0xFF00, 0xFFFF0000, 0xFFFFFFFF00000000}
shift := []uint64{1, 2, 4, 8, 16, 32}
var msbPos uint64
for i := 5; i >= 0; i-- {
if x&val[i] != 0 {
x >>= shift[i]
msbPos |= shift[i]
}
}
return int(msbPos)
}
const deBruijn64 = 0x03f79d71b4ca8b09
const digitMask = uint64(1<<64 - 1)
var deBruijn64Lookup = []byte{
0, 1, 56, 2, 57, 49, 28, 3, 61, 58, 42, 50, 38, 29, 17, 4,
62, 47, 59, 36, 45, 43, 51, 22, 53, 39, 33, 30, 24, 18, 12, 5,
63, 55, 48, 27, 60, 41, 37, 16, 46, 35, 44, 21, 52, 32, 23, 11,
54, 26, 40, 15, 34, 20, 31, 10, 25, 14, 19, 9, 13, 8, 7, 6,
}
// findLSBSetNonZero64 returns the index (between 0 and 63) of the least
// significant set bit. Passing zero to this function returns zero.
//
// This code comes from trailingZeroBits in https://golang.org/src/math/big/nat.go
// which references (Knuth, volume 4, section 7.3.1).
func findLSBSetNonZero64(x uint64) int {
return int(deBruijn64Lookup[((x&-x)*(deBruijn64&digitMask))>>58])
}

View File

@ -1,39 +0,0 @@
// Copyright 2018 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.
// +build go1.9
package s2
// This file is for the bit manipulation code post-Go 1.9.
import "math/bits"
// findMSBSetNonZero64 returns the index (between 0 and 63) of the most
// significant set bit. Passing zero to this function return zero.
func findMSBSetNonZero64(x uint64) int {
if x == 0 {
return 0
}
return 63 - bits.LeadingZeros64(x)
}
// findLSBSetNonZero64 returns the index (between 0 and 63) of the least
// significant set bit. Passing zero to this function return zero.
func findLSBSetNonZero64(x uint64) int {
if x == 0 {
return 0
}
return bits.TrailingZeros64(x)
}

View File

@ -1,519 +0,0 @@
// Copyright 2014 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 (
"fmt"
"io"
"math"
"github.com/golang/geo/r1"
"github.com/golang/geo/s1"
)
var (
// centerPoint is the default center for Caps
centerPoint = PointFromCoords(1.0, 0, 0)
)
// Cap represents a disc-shaped region defined by a center and radius.
// Technically this shape is called a "spherical cap" (rather than disc)
// because it is not planar; the cap represents a portion of the sphere that
// has been cut off by a plane. The boundary of the cap is the circle defined
// by the intersection of the sphere and the plane. For containment purposes,
// the cap is a closed set, i.e. it contains its boundary.
//
// For the most part, you can use a spherical cap wherever you would use a
// disc in planar geometry. The radius of the cap is measured along the
// surface of the sphere (rather than the straight-line distance through the
// interior). Thus a cap of radius π/2 is a hemisphere, and a cap of radius
// π covers the entire sphere.
//
// The center is a point on the surface of the unit sphere. (Hence the need for
// it to be of unit length.)
//
// A cap can also be defined by its center point and height. The height is the
// distance from the center point to the cutoff plane. There is also support for
// "empty" and "full" caps, which contain no points and all points respectively.
//
// Here are some useful relationships between the cap height (h), the cap
// radius (r), the maximum chord length from the cap's center (d), and the
// radius of cap's base (a).
//
// h = 1 - cos(r)
// = 2 * sin^2(r/2)
// d^2 = 2 * h
// = a^2 + h^2
//
// The zero value of Cap is an invalid cap. Use EmptyCap to get a valid empty cap.
type Cap struct {
center Point
radius s1.ChordAngle
}
// CapFromPoint constructs a cap containing a single point.
func CapFromPoint(p Point) Cap {
return CapFromCenterChordAngle(p, 0)
}
// CapFromCenterAngle constructs a cap with the given center and angle.
func CapFromCenterAngle(center Point, angle s1.Angle) Cap {
return CapFromCenterChordAngle(center, s1.ChordAngleFromAngle(angle))
}
// CapFromCenterChordAngle constructs a cap where the angle is expressed as an
// s1.ChordAngle. This constructor is more efficient than using an s1.Angle.
func CapFromCenterChordAngle(center Point, radius s1.ChordAngle) Cap {
return Cap{
center: center,
radius: radius,
}
}
// CapFromCenterHeight constructs a cap with the given center and height. A
// negative height yields an empty cap; a height of 2 or more yields a full cap.
// The center should be unit length.
func CapFromCenterHeight(center Point, height float64) Cap {
return CapFromCenterChordAngle(center, s1.ChordAngleFromSquaredLength(2*height))
}
// CapFromCenterArea constructs a cap with the given center and surface area.
// Note that the area can also be interpreted as the solid angle subtended by the
// cap (because the sphere has unit radius). A negative area yields an empty cap;
// an area of 4*π or more yields a full cap.
func CapFromCenterArea(center Point, area float64) Cap {
return CapFromCenterChordAngle(center, s1.ChordAngleFromSquaredLength(area/math.Pi))
}
// EmptyCap returns a cap that contains no points.
func EmptyCap() Cap {
return CapFromCenterChordAngle(centerPoint, s1.NegativeChordAngle)
}
// FullCap returns a cap that contains all points.
func FullCap() Cap {
return CapFromCenterChordAngle(centerPoint, s1.StraightChordAngle)
}
// IsValid reports whether the Cap is considered valid.
func (c Cap) IsValid() bool {
return c.center.Vector.IsUnit() && c.radius <= s1.StraightChordAngle
}
// IsEmpty reports whether the cap is empty, i.e. it contains no points.
func (c Cap) IsEmpty() bool {
return c.radius < 0
}
// IsFull reports whether the cap is full, i.e. it contains all points.
func (c Cap) IsFull() bool {
return c.radius == s1.StraightChordAngle
}
// Center returns the cap's center point.
func (c Cap) Center() Point {
return c.center
}
// Height returns the height of the cap. This is the distance from the center
// point to the cutoff plane.
func (c Cap) Height() float64 {
return float64(0.5 * c.radius)
}
// Radius returns the cap radius as an s1.Angle. (Note that the cap angle
// is stored internally as a ChordAngle, so this method requires a trigonometric
// operation and may yield a slightly different result than the value passed
// to CapFromCenterAngle).
func (c Cap) Radius() s1.Angle {
return c.radius.Angle()
}
// Area returns the surface area of the Cap on the unit sphere.
func (c Cap) Area() float64 {
return 2.0 * math.Pi * math.Max(0, c.Height())
}
// Contains reports whether this cap contains the other.
func (c Cap) Contains(other Cap) bool {
// In a set containment sense, every cap contains the empty cap.
if c.IsFull() || other.IsEmpty() {
return true
}
return c.radius >= ChordAngleBetweenPoints(c.center, other.center).Add(other.radius)
}
// Intersects reports whether this cap intersects the other cap.
// i.e. whether they have any points in common.
func (c Cap) Intersects(other Cap) bool {
if c.IsEmpty() || other.IsEmpty() {
return false
}
return c.radius.Add(other.radius) >= ChordAngleBetweenPoints(c.center, other.center)
}
// InteriorIntersects reports whether this caps interior intersects the other cap.
func (c Cap) InteriorIntersects(other Cap) bool {
// Make sure this cap has an interior and the other cap is non-empty.
if c.radius <= 0 || other.IsEmpty() {
return false
}
return c.radius.Add(other.radius) > ChordAngleBetweenPoints(c.center, other.center)
}
// ContainsPoint reports whether this cap contains the point.
func (c Cap) ContainsPoint(p Point) bool {
return ChordAngleBetweenPoints(c.center, p) <= c.radius
}
// InteriorContainsPoint reports whether the point is within the interior of this cap.
func (c Cap) InteriorContainsPoint(p Point) bool {
return c.IsFull() || ChordAngleBetweenPoints(c.center, p) < c.radius
}
// Complement returns the complement of the interior of the cap. A cap and its
// complement have the same boundary but do not share any interior points.
// The complement operator is not a bijection because the complement of a
// singleton cap (containing a single point) is the same as the complement
// of an empty cap.
func (c Cap) Complement() Cap {
if c.IsFull() {
return EmptyCap()
}
if c.IsEmpty() {
return FullCap()
}
return CapFromCenterChordAngle(Point{c.center.Mul(-1)}, s1.StraightChordAngle.Sub(c.radius))
}
// CapBound returns a bounding spherical cap. This is not guaranteed to be exact.
func (c Cap) CapBound() Cap {
return c
}
// RectBound returns a bounding latitude-longitude rectangle.
// The bounds are not guaranteed to be tight.
func (c Cap) RectBound() Rect {
if c.IsEmpty() {
return EmptyRect()
}
capAngle := c.Radius().Radians()
allLongitudes := false
lat := r1.Interval{
Lo: latitude(c.center).Radians() - capAngle,
Hi: latitude(c.center).Radians() + capAngle,
}
lng := s1.FullInterval()
// Check whether cap includes the south pole.
if lat.Lo <= -math.Pi/2 {
lat.Lo = -math.Pi / 2
allLongitudes = true
}
// Check whether cap includes the north pole.
if lat.Hi >= math.Pi/2 {
lat.Hi = math.Pi / 2
allLongitudes = true
}
if !allLongitudes {
// Compute the range of longitudes covered by the cap. We use the law
// of sines for spherical triangles. Consider the triangle ABC where
// A is the north pole, B is the center of the cap, and C is the point
// of tangency between the cap boundary and a line of longitude. Then
// C is a right angle, and letting a,b,c denote the sides opposite A,B,C,
// we have sin(a)/sin(A) = sin(c)/sin(C), or sin(A) = sin(a)/sin(c).
// Here "a" is the cap angle, and "c" is the colatitude (90 degrees
// minus the latitude). This formula also works for negative latitudes.
//
// The formula for sin(a) follows from the relationship h = 1 - cos(a).
sinA := c.radius.Sin()
sinC := math.Cos(latitude(c.center).Radians())
if sinA <= sinC {
angleA := math.Asin(sinA / sinC)
lng.Lo = math.Remainder(longitude(c.center).Radians()-angleA, math.Pi*2)
lng.Hi = math.Remainder(longitude(c.center).Radians()+angleA, math.Pi*2)
}
}
return Rect{lat, lng}
}
// Equal reports whether this cap is equal to the other cap.
func (c Cap) Equal(other Cap) bool {
return (c.radius == other.radius && c.center == other.center) ||
(c.IsEmpty() && other.IsEmpty()) ||
(c.IsFull() && other.IsFull())
}
// ApproxEqual reports whether this cap is equal to the other cap within the given tolerance.
func (c Cap) ApproxEqual(other Cap) bool {
const epsilon = 1e-14
r2 := float64(c.radius)
otherR2 := float64(other.radius)
return c.center.ApproxEqual(other.center) &&
math.Abs(r2-otherR2) <= epsilon ||
c.IsEmpty() && otherR2 <= epsilon ||
other.IsEmpty() && r2 <= epsilon ||
c.IsFull() && otherR2 >= 2-epsilon ||
other.IsFull() && r2 >= 2-epsilon
}
// AddPoint increases the cap if necessary to include the given point. If this cap is empty,
// then the center is set to the point with a zero height. p must be unit-length.
func (c Cap) AddPoint(p Point) Cap {
if c.IsEmpty() {
c.center = p
c.radius = 0
return c
}
// After calling cap.AddPoint(p), cap.Contains(p) must be true. However
// we don't need to do anything special to achieve this because Contains()
// does exactly the same distance calculation that we do here.
if newRad := ChordAngleBetweenPoints(c.center, p); newRad > c.radius {
c.radius = newRad
}
return c
}
// AddCap increases the cap height if necessary to include the other cap. If this cap is empty,
// it is set to the other cap.
func (c Cap) AddCap(other Cap) Cap {
if c.IsEmpty() {
return other
}
if other.IsEmpty() {
return c
}
// We round up the distance to ensure that the cap is actually contained.
// TODO(roberts): Do some error analysis in order to guarantee this.
dist := ChordAngleBetweenPoints(c.center, other.center).Add(other.radius)
if newRad := dist.Expanded(dblEpsilon * float64(dist)); newRad > c.radius {
c.radius = newRad
}
return c
}
// Expanded returns a new cap expanded by the given angle. If the cap is empty,
// it returns an empty cap.
func (c Cap) Expanded(distance s1.Angle) Cap {
if c.IsEmpty() {
return EmptyCap()
}
return CapFromCenterChordAngle(c.center, c.radius.Add(s1.ChordAngleFromAngle(distance)))
}
func (c Cap) String() string {
return fmt.Sprintf("[Center=%v, Radius=%f]", c.center.Vector, c.Radius().Degrees())
}
// radiusToHeight converts an s1.Angle into the height of the cap.
func radiusToHeight(r s1.Angle) float64 {
if r.Radians() < 0 {
return float64(s1.NegativeChordAngle)
}
if r.Radians() >= math.Pi {
return float64(s1.RightChordAngle)
}
return float64(0.5 * s1.ChordAngleFromAngle(r))
}
// ContainsCell reports whether the cap contains the given cell.
func (c Cap) ContainsCell(cell Cell) bool {
// If the cap does not contain all cell vertices, return false.
var vertices [4]Point
for k := 0; k < 4; k++ {
vertices[k] = cell.Vertex(k)
if !c.ContainsPoint(vertices[k]) {
return false
}
}
// Otherwise, return true if the complement of the cap does not intersect the cell.
return !c.Complement().intersects(cell, vertices)
}
// IntersectsCell reports whether the cap intersects the cell.
func (c Cap) IntersectsCell(cell Cell) bool {
// If the cap contains any cell vertex, return true.
var vertices [4]Point
for k := 0; k < 4; k++ {
vertices[k] = cell.Vertex(k)
if c.ContainsPoint(vertices[k]) {
return true
}
}
return c.intersects(cell, vertices)
}
// intersects reports whether the cap intersects any point of the cell excluding
// its vertices (which are assumed to already have been checked).
func (c Cap) intersects(cell Cell, vertices [4]Point) bool {
// If the cap is a hemisphere or larger, the cell and the complement of the cap
// are both convex. Therefore since no vertex of the cell is contained, no other
// interior point of the cell is contained either.
if c.radius >= s1.RightChordAngle {
return false
}
// We need to check for empty caps due to the center check just below.
if c.IsEmpty() {
return false
}
// Optimization: return true if the cell contains the cap center. This allows half
// of the edge checks below to be skipped.
if cell.ContainsPoint(c.center) {
return true
}
// At this point we know that the cell does not contain the cap center, and the cap
// does not contain any cell vertex. The only way that they can intersect is if the
// cap intersects the interior of some edge.
sin2Angle := c.radius.Sin2()
for k := 0; k < 4; k++ {
edge := cell.Edge(k).Vector
dot := c.center.Vector.Dot(edge)
if dot > 0 {
// The center is in the interior half-space defined by the edge. We do not need
// to consider these edges, since if the cap intersects this edge then it also
// intersects the edge on the opposite side of the cell, because the center is
// not contained with the cell.
continue
}
// The Norm2() factor is necessary because "edge" is not normalized.
if dot*dot > sin2Angle*edge.Norm2() {
return false
}
// Otherwise, the great circle containing this edge intersects the interior of the cap. We just
// need to check whether the point of closest approach occurs between the two edge endpoints.
dir := edge.Cross(c.center.Vector)
if dir.Dot(vertices[k].Vector) < 0 && dir.Dot(vertices[(k+1)&3].Vector) > 0 {
return true
}
}
return false
}
// CellUnionBound computes a covering of the Cap. In general the covering
// consists of at most 4 cells except for very large caps, which may need
// up to 6 cells. The output is not sorted.
func (c Cap) CellUnionBound() []CellID {
// TODO(roberts): The covering could be made quite a bit tighter by mapping
// the cap to a rectangle in (i,j)-space and finding a covering for that.
// Find the maximum level such that the cap contains at most one cell vertex
// and such that CellID.AppendVertexNeighbors() can be called.
level := MinWidthMetric.MaxLevel(c.Radius().Radians()) - 1
// If level < 0, more than three face cells are required.
if level < 0 {
cellIDs := make([]CellID, 6)
for face := 0; face < 6; face++ {
cellIDs[face] = CellIDFromFace(face)
}
return cellIDs
}
// The covering consists of the 4 cells at the given level that share the
// cell vertex that is closest to the cap center.
return cellIDFromPoint(c.center).VertexNeighbors(level)
}
// Centroid returns the true centroid of the cap multiplied by its surface area
// The result lies on the ray from the origin through the cap's center, but it
// is not unit length. Note that if you just want the "surface centroid", i.e.
// the normalized result, then it is simpler to call Center.
//
// The reason for multiplying the result by the cap area is to make it
// easier to compute the centroid of more complicated shapes. The centroid
// of a union of disjoint regions can be computed simply by adding their
// Centroid() results. Caveat: for caps that contain a single point
// (i.e., zero radius), this method always returns the origin (0, 0, 0).
// This is because shapes with no area don't affect the centroid of a
// union whose total area is positive.
func (c Cap) Centroid() Point {
// From symmetry, the centroid of the cap must be somewhere on the line
// from the origin to the center of the cap on the surface of the sphere.
// When a sphere is divided into slices of constant thickness by a set of
// parallel planes, all slices have the same surface area. This implies
// that the radial component of the centroid is simply the midpoint of the
// range of radial distances spanned by the cap. That is easily computed
// from the cap height.
if c.IsEmpty() {
return Point{}
}
r := 1 - 0.5*c.Height()
return Point{c.center.Mul(r * c.Area())}
}
// Union returns the smallest cap which encloses this cap and other.
func (c Cap) Union(other Cap) Cap {
// If the other cap is larger, swap c and other for the rest of the computations.
if c.radius < other.radius {
c, other = other, c
}
if c.IsFull() || other.IsEmpty() {
return c
}
// TODO: This calculation would be more efficient using s1.ChordAngles.
cRadius := c.Radius()
otherRadius := other.Radius()
distance := c.center.Distance(other.center)
if cRadius >= distance+otherRadius {
return c
}
resRadius := 0.5 * (distance + cRadius + otherRadius)
resCenter := InterpolateAtDistance(0.5*(distance-cRadius+otherRadius), c.center, other.center)
return CapFromCenterAngle(resCenter, resRadius)
}
// Encode encodes the Cap.
func (c Cap) Encode(w io.Writer) error {
e := &encoder{w: w}
c.encode(e)
return e.err
}
func (c Cap) encode(e *encoder) {
e.writeFloat64(c.center.X)
e.writeFloat64(c.center.Y)
e.writeFloat64(c.center.Z)
e.writeFloat64(float64(c.radius))
}
// Decode decodes the Cap.
func (c *Cap) Decode(r io.Reader) error {
d := &decoder{r: asByteReader(r)}
c.decode(d)
return d.err
}
func (c *Cap) decode(d *decoder) {
c.center.X = d.readFloat64()
c.center.Y = d.readFloat64()
c.center.Z = d.readFloat64()
c.radius = s1.ChordAngle(d.readFloat64())
}

View File

@ -1,698 +0,0 @@
// Copyright 2014 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 (
"io"
"math"
"github.com/golang/geo/r1"
"github.com/golang/geo/r2"
"github.com/golang/geo/r3"
"github.com/golang/geo/s1"
)
// Cell is an S2 region object that represents a cell. Unlike CellIDs,
// it supports efficient containment and intersection tests. However, it is
// also a more expensive representation.
type Cell struct {
face int8
level int8
orientation int8
id CellID
uv r2.Rect
}
// CellFromCellID constructs a Cell corresponding to the given CellID.
func CellFromCellID(id CellID) Cell {
c := Cell{}
c.id = id
f, i, j, o := c.id.faceIJOrientation()
c.face = int8(f)
c.level = int8(c.id.Level())
c.orientation = int8(o)
c.uv = ijLevelToBoundUV(i, j, int(c.level))
return c
}
// CellFromPoint constructs a cell for the given Point.
func CellFromPoint(p Point) Cell {
return CellFromCellID(cellIDFromPoint(p))
}
// CellFromLatLng constructs a cell for the given LatLng.
func CellFromLatLng(ll LatLng) Cell {
return CellFromCellID(CellIDFromLatLng(ll))
}
// Face returns the face this cell is on.
func (c Cell) Face() int {
return int(c.face)
}
// oppositeFace returns the face opposite the given face.
func oppositeFace(face int) int {
return (face + 3) % 6
}
// Level returns the level of this cell.
func (c Cell) Level() int {
return int(c.level)
}
// ID returns the CellID this cell represents.
func (c Cell) ID() CellID {
return c.id
}
// IsLeaf returns whether this Cell is a leaf or not.
func (c Cell) IsLeaf() bool {
return c.level == maxLevel
}
// SizeIJ returns the edge length of this cell in (i,j)-space.
func (c Cell) SizeIJ() int {
return sizeIJ(int(c.level))
}
// SizeST returns the edge length of this cell in (s,t)-space.
func (c Cell) SizeST() float64 {
return c.id.sizeST(int(c.level))
}
// Vertex returns the k-th vertex of the cell (k = 0,1,2,3) in CCW order
// (lower left, lower right, upper right, upper left in the UV plane).
func (c Cell) Vertex(k int) Point {
return Point{faceUVToXYZ(int(c.face), c.uv.Vertices()[k].X, c.uv.Vertices()[k].Y).Normalize()}
}
// Edge returns the inward-facing normal of the great circle passing through
// the CCW ordered edge from vertex k to vertex k+1 (mod 4) (for k = 0,1,2,3).
func (c Cell) Edge(k int) Point {
switch k {
case 0:
return Point{vNorm(int(c.face), c.uv.Y.Lo).Normalize()} // Bottom
case 1:
return Point{uNorm(int(c.face), c.uv.X.Hi).Normalize()} // Right
case 2:
return Point{vNorm(int(c.face), c.uv.Y.Hi).Mul(-1.0).Normalize()} // Top
default:
return Point{uNorm(int(c.face), c.uv.X.Lo).Mul(-1.0).Normalize()} // Left
}
}
// BoundUV returns the bounds of this cell in (u,v)-space.
func (c Cell) BoundUV() r2.Rect {
return c.uv
}
// Center returns the direction vector corresponding to the center in
// (s,t)-space of the given cell. This is the point at which the cell is
// divided into four subcells; it is not necessarily the centroid of the
// cell in (u,v)-space or (x,y,z)-space
func (c Cell) Center() Point {
return Point{c.id.rawPoint().Normalize()}
}
// Children returns the four direct children of this cell in traversal order
// and returns true. If this is a leaf cell, or the children could not be created,
// false is returned.
// The C++ method is called Subdivide.
func (c Cell) Children() ([4]Cell, bool) {
var children [4]Cell
if c.id.IsLeaf() {
return children, false
}
// Compute the cell midpoint in uv-space.
uvMid := c.id.centerUV()
// Create four children with the appropriate bounds.
cid := c.id.ChildBegin()
for pos := 0; pos < 4; pos++ {
children[pos] = Cell{
face: c.face,
level: c.level + 1,
orientation: c.orientation ^ int8(posToOrientation[pos]),
id: cid,
}
// We want to split the cell in half in u and v. To decide which
// side to set equal to the midpoint value, we look at cell's (i,j)
// position within its parent. The index for i is in bit 1 of ij.
ij := posToIJ[c.orientation][pos]
i := ij >> 1
j := ij & 1
if i == 1 {
children[pos].uv.X.Hi = c.uv.X.Hi
children[pos].uv.X.Lo = uvMid.X
} else {
children[pos].uv.X.Lo = c.uv.X.Lo
children[pos].uv.X.Hi = uvMid.X
}
if j == 1 {
children[pos].uv.Y.Hi = c.uv.Y.Hi
children[pos].uv.Y.Lo = uvMid.Y
} else {
children[pos].uv.Y.Lo = c.uv.Y.Lo
children[pos].uv.Y.Hi = uvMid.Y
}
cid = cid.Next()
}
return children, true
}
// ExactArea returns the area of this cell as accurately as possible.
func (c Cell) ExactArea() float64 {
v0, v1, v2, v3 := c.Vertex(0), c.Vertex(1), c.Vertex(2), c.Vertex(3)
return PointArea(v0, v1, v2) + PointArea(v0, v2, v3)
}
// ApproxArea returns the approximate area of this cell. This method is accurate
// to within 3% percent for all cell sizes and accurate to within 0.1% for cells
// at level 5 or higher (i.e. squares 350km to a side or smaller on the Earth's
// surface). It is moderately cheap to compute.
func (c Cell) ApproxArea() float64 {
// All cells at the first two levels have the same area.
if c.level < 2 {
return c.AverageArea()
}
// First, compute the approximate area of the cell when projected
// perpendicular to its normal. The cross product of its diagonals gives
// the normal, and the length of the normal is twice the projected area.
flatArea := 0.5 * (c.Vertex(2).Sub(c.Vertex(0).Vector).
Cross(c.Vertex(3).Sub(c.Vertex(1).Vector)).Norm())
// Now, compensate for the curvature of the cell surface by pretending
// that the cell is shaped like a spherical cap. The ratio of the
// area of a spherical cap to the area of its projected disc turns out
// to be 2 / (1 + sqrt(1 - r*r)) where r is the radius of the disc.
// For example, when r=0 the ratio is 1, and when r=1 the ratio is 2.
// Here we set Pi*r*r == flatArea to find the equivalent disc.
return flatArea * 2 / (1 + math.Sqrt(1-math.Min(1/math.Pi*flatArea, 1)))
}
// AverageArea returns the average area of cells at the level of this cell.
// This is accurate to within a factor of 1.7.
func (c Cell) AverageArea() float64 {
return AvgAreaMetric.Value(int(c.level))
}
// IntersectsCell reports whether the intersection of this cell and the other cell is not nil.
func (c Cell) IntersectsCell(oc Cell) bool {
return c.id.Intersects(oc.id)
}
// ContainsCell reports whether this cell contains the other cell.
func (c Cell) ContainsCell(oc Cell) bool {
return c.id.Contains(oc.id)
}
// CellUnionBound computes a covering of the Cell.
func (c Cell) CellUnionBound() []CellID {
return c.CapBound().CellUnionBound()
}
// latitude returns the latitude of the cell vertex in radians given by (i,j),
// where i and j indicate the Hi (1) or Lo (0) corner.
func (c Cell) latitude(i, j int) float64 {
var u, v float64
switch {
case i == 0 && j == 0:
u = c.uv.X.Lo
v = c.uv.Y.Lo
case i == 0 && j == 1:
u = c.uv.X.Lo
v = c.uv.Y.Hi
case i == 1 && j == 0:
u = c.uv.X.Hi
v = c.uv.Y.Lo
case i == 1 && j == 1:
u = c.uv.X.Hi
v = c.uv.Y.Hi
default:
panic("i and/or j is out of bounds")
}
return latitude(Point{faceUVToXYZ(int(c.face), u, v)}).Radians()
}
// longitude returns the longitude of the cell vertex in radians given by (i,j),
// where i and j indicate the Hi (1) or Lo (0) corner.
func (c Cell) longitude(i, j int) float64 {
var u, v float64
switch {
case i == 0 && j == 0:
u = c.uv.X.Lo
v = c.uv.Y.Lo
case i == 0 && j == 1:
u = c.uv.X.Lo
v = c.uv.Y.Hi
case i == 1 && j == 0:
u = c.uv.X.Hi
v = c.uv.Y.Lo
case i == 1 && j == 1:
u = c.uv.X.Hi
v = c.uv.Y.Hi
default:
panic("i and/or j is out of bounds")
}
return longitude(Point{faceUVToXYZ(int(c.face), u, v)}).Radians()
}
var (
poleMinLat = math.Asin(math.Sqrt(1.0/3)) - 0.5*dblEpsilon
)
// RectBound returns the bounding rectangle of this cell.
func (c Cell) RectBound() Rect {
if c.level > 0 {
// Except for cells at level 0, the latitude and longitude extremes are
// attained at the vertices. Furthermore, the latitude range is
// determined by one pair of diagonally opposite vertices and the
// longitude range is determined by the other pair.
//
// We first determine which corner (i,j) of the cell has the largest
// absolute latitude. To maximize latitude, we want to find the point in
// the cell that has the largest absolute z-coordinate and the smallest
// absolute x- and y-coordinates. To do this we look at each coordinate
// (u and v), and determine whether we want to minimize or maximize that
// coordinate based on the axis direction and the cell's (u,v) quadrant.
u := c.uv.X.Lo + c.uv.X.Hi
v := c.uv.Y.Lo + c.uv.Y.Hi
var i, j int
if uAxis(int(c.face)).Z == 0 {
if u < 0 {
i = 1
}
} else if u > 0 {
i = 1
}
if vAxis(int(c.face)).Z == 0 {
if v < 0 {
j = 1
}
} else if v > 0 {
j = 1
}
lat := r1.IntervalFromPoint(c.latitude(i, j)).AddPoint(c.latitude(1-i, 1-j))
lng := s1.EmptyInterval().AddPoint(c.longitude(i, 1-j)).AddPoint(c.longitude(1-i, j))
// We grow the bounds slightly to make sure that the bounding rectangle
// contains LatLngFromPoint(P) for any point P inside the loop L defined by the
// four *normalized* vertices. Note that normalization of a vector can
// change its direction by up to 0.5 * dblEpsilon radians, and it is not
// enough just to add Normalize calls to the code above because the
// latitude/longitude ranges are not necessarily determined by diagonally
// opposite vertex pairs after normalization.
//
// We would like to bound the amount by which the latitude/longitude of a
// contained point P can exceed the bounds computed above. In the case of
// longitude, the normalization error can change the direction of rounding
// leading to a maximum difference in longitude of 2 * dblEpsilon. In
// the case of latitude, the normalization error can shift the latitude by
// up to 0.5 * dblEpsilon and the other sources of error can cause the
// two latitudes to differ by up to another 1.5 * dblEpsilon, which also
// leads to a maximum difference of 2 * dblEpsilon.
return Rect{lat, lng}.expanded(LatLng{s1.Angle(2 * dblEpsilon), s1.Angle(2 * dblEpsilon)}).PolarClosure()
}
// The 4 cells around the equator extend to +/-45 degrees latitude at the
// midpoints of their top and bottom edges. The two cells covering the
// poles extend down to +/-35.26 degrees at their vertices. The maximum
// error in this calculation is 0.5 * dblEpsilon.
var bound Rect
switch c.face {
case 0:
bound = Rect{r1.Interval{-math.Pi / 4, math.Pi / 4}, s1.Interval{-math.Pi / 4, math.Pi / 4}}
case 1:
bound = Rect{r1.Interval{-math.Pi / 4, math.Pi / 4}, s1.Interval{math.Pi / 4, 3 * math.Pi / 4}}
case 2:
bound = Rect{r1.Interval{poleMinLat, math.Pi / 2}, s1.FullInterval()}
case 3:
bound = Rect{r1.Interval{-math.Pi / 4, math.Pi / 4}, s1.Interval{3 * math.Pi / 4, -3 * math.Pi / 4}}
case 4:
bound = Rect{r1.Interval{-math.Pi / 4, math.Pi / 4}, s1.Interval{-3 * math.Pi / 4, -math.Pi / 4}}
default:
bound = Rect{r1.Interval{-math.Pi / 2, -poleMinLat}, s1.FullInterval()}
}
// Finally, we expand the bound to account for the error when a point P is
// converted to an LatLng to test for containment. (The bound should be
// large enough so that it contains the computed LatLng of any contained
// point, not just the infinite-precision version.) We don't need to expand
// longitude because longitude is calculated via a single call to math.Atan2,
// which is guaranteed to be semi-monotonic.
return bound.expanded(LatLng{s1.Angle(dblEpsilon), s1.Angle(0)})
}
// CapBound returns the bounding cap of this cell.
func (c Cell) CapBound() Cap {
// We use the cell center in (u,v)-space as the cap axis. This vector is very close
// to GetCenter() and faster to compute. Neither one of these vectors yields the
// bounding cap with minimal surface area, but they are both pretty close.
cap := CapFromPoint(Point{faceUVToXYZ(int(c.face), c.uv.Center().X, c.uv.Center().Y).Normalize()})
for k := 0; k < 4; k++ {
cap = cap.AddPoint(c.Vertex(k))
}
return cap
}
// ContainsPoint reports whether this cell contains the given point. Note that
// unlike Loop/Polygon, a Cell is considered to be a closed set. This means
// that a point on a Cell's edge or vertex belong to the Cell and the relevant
// adjacent Cells too.
//
// If you want every point to be contained by exactly one Cell,
// you will need to convert the Cell to a Loop.
func (c Cell) ContainsPoint(p Point) bool {
var uv r2.Point
var ok bool
if uv.X, uv.Y, ok = faceXYZToUV(int(c.face), p); !ok {
return false
}
// Expand the (u,v) bound to ensure that
//
// CellFromPoint(p).ContainsPoint(p)
//
// is always true. To do this, we need to account for the error when
// converting from (u,v) coordinates to (s,t) coordinates. In the
// normal case the total error is at most dblEpsilon.
return c.uv.ExpandedByMargin(dblEpsilon).ContainsPoint(uv)
}
// Encode encodes the Cell.
func (c Cell) Encode(w io.Writer) error {
e := &encoder{w: w}
c.encode(e)
return e.err
}
func (c Cell) encode(e *encoder) {
c.id.encode(e)
}
// Decode decodes the Cell.
func (c *Cell) Decode(r io.Reader) error {
d := &decoder{r: asByteReader(r)}
c.decode(d)
return d.err
}
func (c *Cell) decode(d *decoder) {
c.id.decode(d)
*c = CellFromCellID(c.id)
}
// vertexChordDist2 returns the squared chord distance from point P to the
// given corner vertex specified by the Hi or Lo values of each.
func (c Cell) vertexChordDist2(p Point, xHi, yHi bool) s1.ChordAngle {
x := c.uv.X.Lo
y := c.uv.Y.Lo
if xHi {
x = c.uv.X.Hi
}
if yHi {
y = c.uv.Y.Hi
}
return ChordAngleBetweenPoints(p, PointFromCoords(x, y, 1))
}
// uEdgeIsClosest reports whether a point P is closer to the interior of the specified
// Cell edge (either the lower or upper edge of the Cell) or to the endpoints.
func (c Cell) uEdgeIsClosest(p Point, vHi bool) bool {
u0 := c.uv.X.Lo
u1 := c.uv.X.Hi
v := c.uv.Y.Lo
if vHi {
v = c.uv.Y.Hi
}
// These are the normals to the planes that are perpendicular to the edge
// and pass through one of its two endpoints.
dir0 := r3.Vector{v*v + 1, -u0 * v, -u0}
dir1 := r3.Vector{v*v + 1, -u1 * v, -u1}
return p.Dot(dir0) > 0 && p.Dot(dir1) < 0
}
// vEdgeIsClosest reports whether a point P is closer to the interior of the specified
// Cell edge (either the right or left edge of the Cell) or to the endpoints.
func (c Cell) vEdgeIsClosest(p Point, uHi bool) bool {
v0 := c.uv.Y.Lo
v1 := c.uv.Y.Hi
u := c.uv.X.Lo
if uHi {
u = c.uv.X.Hi
}
dir0 := r3.Vector{-u * v0, u*u + 1, -v0}
dir1 := r3.Vector{-u * v1, u*u + 1, -v1}
return p.Dot(dir0) > 0 && p.Dot(dir1) < 0
}
// edgeDistance reports the distance from a Point P to a given Cell edge. The point
// P is given by its dot product, and the uv edge by its normal in the
// given coordinate value.
func edgeDistance(ij, uv float64) s1.ChordAngle {
// Let P by the target point and let R be the closest point on the given
// edge AB. The desired distance PR can be expressed as PR^2 = PQ^2 + QR^2
// where Q is the point P projected onto the plane through the great circle
// through AB. We can compute the distance PQ^2 perpendicular to the plane
// from "dirIJ" (the dot product of the target point P with the edge
// normal) and the squared length the edge normal (1 + uv**2).
pq2 := (ij * ij) / (1 + uv*uv)
// We can compute the distance QR as (1 - OQ) where O is the sphere origin,
// and we can compute OQ^2 = 1 - PQ^2 using the Pythagorean theorem.
// (This calculation loses accuracy as angle POQ approaches Pi/2.)
qr := 1 - math.Sqrt(1-pq2)
return s1.ChordAngleFromSquaredLength(pq2 + qr*qr)
}
// distanceInternal reports the distance from the given point to the interior of
// the cell if toInterior is true or to the boundary of the cell otherwise.
func (c Cell) distanceInternal(targetXYZ Point, toInterior bool) s1.ChordAngle {
// All calculations are done in the (u,v,w) coordinates of this cell's face.
target := faceXYZtoUVW(int(c.face), targetXYZ)
// Compute dot products with all four upward or rightward-facing edge
// normals. dirIJ is the dot product for the edge corresponding to axis
// I, endpoint J. For example, dir01 is the right edge of the Cell
// (corresponding to the upper endpoint of the u-axis).
dir00 := target.X - target.Z*c.uv.X.Lo
dir01 := target.X - target.Z*c.uv.X.Hi
dir10 := target.Y - target.Z*c.uv.Y.Lo
dir11 := target.Y - target.Z*c.uv.Y.Hi
inside := true
if dir00 < 0 {
inside = false // Target is to the left of the cell
if c.vEdgeIsClosest(target, false) {
return edgeDistance(-dir00, c.uv.X.Lo)
}
}
if dir01 > 0 {
inside = false // Target is to the right of the cell
if c.vEdgeIsClosest(target, true) {
return edgeDistance(dir01, c.uv.X.Hi)
}
}
if dir10 < 0 {
inside = false // Target is below the cell
if c.uEdgeIsClosest(target, false) {
return edgeDistance(-dir10, c.uv.Y.Lo)
}
}
if dir11 > 0 {
inside = false // Target is above the cell
if c.uEdgeIsClosest(target, true) {
return edgeDistance(dir11, c.uv.Y.Hi)
}
}
if inside {
if toInterior {
return s1.ChordAngle(0)
}
// Although you might think of Cells as rectangles, they are actually
// arbitrary quadrilaterals after they are projected onto the sphere.
// Therefore the simplest approach is just to find the minimum distance to
// any of the four edges.
return minChordAngle(edgeDistance(-dir00, c.uv.X.Lo),
edgeDistance(dir01, c.uv.X.Hi),
edgeDistance(-dir10, c.uv.Y.Lo),
edgeDistance(dir11, c.uv.Y.Hi))
}
// Otherwise, the closest point is one of the four cell vertices. Note that
// it is *not* trivial to narrow down the candidates based on the edge sign
// tests above, because (1) the edges don't meet at right angles and (2)
// there are points on the far side of the sphere that are both above *and*
// below the cell, etc.
return minChordAngle(c.vertexChordDist2(target, false, false),
c.vertexChordDist2(target, true, false),
c.vertexChordDist2(target, false, true),
c.vertexChordDist2(target, true, true))
}
// Distance reports the distance from the cell to the given point. Returns zero if
// the point is inside the cell.
func (c Cell) Distance(target Point) s1.ChordAngle {
return c.distanceInternal(target, true)
}
// MaxDistance reports the maximum distance from the cell (including its interior) to the
// given point.
func (c Cell) MaxDistance(target Point) s1.ChordAngle {
// First check the 4 cell vertices. If all are within the hemisphere
// centered around target, the max distance will be to one of these vertices.
targetUVW := faceXYZtoUVW(int(c.face), target)
maxDist := maxChordAngle(c.vertexChordDist2(targetUVW, false, false),
c.vertexChordDist2(targetUVW, true, false),
c.vertexChordDist2(targetUVW, false, true),
c.vertexChordDist2(targetUVW, true, true))
if maxDist <= s1.RightChordAngle {
return maxDist
}
// Otherwise, find the minimum distance dMin to the antipodal point and the
// maximum distance will be pi - dMin.
return s1.StraightChordAngle - c.BoundaryDistance(Point{target.Mul(-1)})
}
// BoundaryDistance reports the distance from the cell boundary to the given point.
func (c Cell) BoundaryDistance(target Point) s1.ChordAngle {
return c.distanceInternal(target, false)
}
// DistanceToEdge returns the minimum distance from the cell to the given edge AB. Returns
// zero if the edge intersects the cell interior.
func (c Cell) DistanceToEdge(a, b Point) s1.ChordAngle {
// Possible optimizations:
// - Currently the (cell vertex, edge endpoint) distances are computed
// twice each, and the length of AB is computed 4 times.
// - To fix this, refactor GetDistance(target) so that it skips calculating
// the distance to each cell vertex. Instead, compute the cell vertices
// and distances in this function, and add a low-level UpdateMinDistance
// that allows the XA, XB, and AB distances to be passed in.
// - It might also be more efficient to do all calculations in UVW-space,
// since this would involve transforming 2 points rather than 4.
// First, check the minimum distance to the edge endpoints A and B.
// (This also detects whether either endpoint is inside the cell.)
minDist := minChordAngle(c.Distance(a), c.Distance(b))
if minDist == 0 {
return minDist
}
// Otherwise, check whether the edge crosses the cell boundary.
crosser := NewChainEdgeCrosser(a, b, c.Vertex(3))
for i := 0; i < 4; i++ {
if crosser.ChainCrossingSign(c.Vertex(i)) != DoNotCross {
return 0
}
}
// Finally, check whether the minimum distance occurs between a cell vertex
// and the interior of the edge AB. (Some of this work is redundant, since
// it also checks the distance to the endpoints A and B again.)
//
// Note that we don't need to check the distance from the interior of AB to
// the interior of a cell edge, because the only way that this distance can
// be minimal is if the two edges cross (already checked above).
for i := 0; i < 4; i++ {
minDist, _ = UpdateMinDistance(c.Vertex(i), a, b, minDist)
}
return minDist
}
// MaxDistanceToEdge returns the maximum distance from the cell (including its interior)
// to the given edge AB.
func (c Cell) MaxDistanceToEdge(a, b Point) s1.ChordAngle {
// If the maximum distance from both endpoints to the cell is less than π/2
// then the maximum distance from the edge to the cell is the maximum of the
// two endpoint distances.
maxDist := maxChordAngle(c.MaxDistance(a), c.MaxDistance(b))
if maxDist <= s1.RightChordAngle {
return maxDist
}
return s1.StraightChordAngle - c.DistanceToEdge(Point{a.Mul(-1)}, Point{b.Mul(-1)})
}
// DistanceToCell returns the minimum distance from this cell to the given cell.
// It returns zero if one cell contains the other.
func (c Cell) DistanceToCell(target Cell) s1.ChordAngle {
// If the cells intersect, the distance is zero. We use the (u,v) ranges
// rather than CellID intersects so that cells that share a partial edge or
// corner are considered to intersect.
if c.face == target.face && c.uv.Intersects(target.uv) {
return 0
}
// Otherwise, the minimum distance always occurs between a vertex of one
// cell and an edge of the other cell (including the edge endpoints). This
// represents a total of 32 possible (vertex, edge) pairs.
//
// TODO(roberts): This could be optimized to be at least 5x faster by pruning
// the set of possible closest vertex/edge pairs using the faces and (u,v)
// ranges of both cells.
var va, vb [4]Point
for i := 0; i < 4; i++ {
va[i] = c.Vertex(i)
vb[i] = target.Vertex(i)
}
minDist := s1.InfChordAngle()
for i := 0; i < 4; i++ {
for j := 0; j < 4; j++ {
minDist, _ = UpdateMinDistance(va[i], vb[j], vb[(j+1)&3], minDist)
minDist, _ = UpdateMinDistance(vb[i], va[j], va[(j+1)&3], minDist)
}
}
return minDist
}
// MaxDistanceToCell returns the maximum distance from the cell (including its
// interior) to the given target cell.
func (c Cell) MaxDistanceToCell(target Cell) s1.ChordAngle {
// Need to check the antipodal target for intersection with the cell. If it
// intersects, the distance is the straight ChordAngle.
// antipodalUV is the transpose of the original UV, interpreted within the opposite face.
antipodalUV := r2.Rect{target.uv.Y, target.uv.X}
if int(c.face) == oppositeFace(int(target.face)) && c.uv.Intersects(antipodalUV) {
return s1.StraightChordAngle
}
// Otherwise, the maximum distance always occurs between a vertex of one
// cell and an edge of the other cell (including the edge endpoints). This
// represents a total of 32 possible (vertex, edge) pairs.
//
// TODO(roberts): When the maximum distance is at most π/2, the maximum is
// always attained between a pair of vertices, and this could be made much
// faster by testing each vertex pair once rather than the current 4 times.
var va, vb [4]Point
for i := 0; i < 4; i++ {
va[i] = c.Vertex(i)
vb[i] = target.Vertex(i)
}
maxDist := s1.NegativeChordAngle
for i := 0; i < 4; i++ {
for j := 0; j < 4; j++ {
maxDist, _ = UpdateMaxDistance(va[i], vb[j], vb[(j+1)&3], maxDist)
maxDist, _ = UpdateMaxDistance(vb[i], va[j], va[(j+1)&3], maxDist)
}
}
return maxDist
}

View File

@ -1,498 +0,0 @@
// Copyright 2020 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 (
"sort"
)
const (
// A special label indicating that the ContentsIterator done is true.
cellIndexDoneContents = -1
)
// cellIndexNode represents a node in the CellIndex. Cells are organized in a
// tree such that the ancestors of a given node contain that node.
type cellIndexNode struct {
cellID CellID
label int32
parent int32
}
// newCellIndexNode returns a node with the appropriate default values.
func newCellIndexNode() cellIndexNode {
return cellIndexNode{
cellID: 0,
label: cellIndexDoneContents,
parent: -1,
}
}
// A rangeNode represents a range of leaf CellIDs. The range starts at
// startID (a leaf cell) and ends at the startID field of the next
// rangeNode. contents points to the node of the CellIndex cellTree
// representing the cells that overlap this range.
type rangeNode struct {
startID CellID // First leaf cell contained by this range.
contents int32 // Contents of this node (an index within the cell tree).
}
// CellIndexIterator is an iterator that visits the entire set of indexed
// (CellID, label) pairs in an unspecified order.
type CellIndexIterator struct {
// TODO(roberts): Implement
}
// NewCellIndexIterator creates an iterator for the given CellIndex.
func NewCellIndexIterator(index *CellIndex) *CellIndexIterator {
return &CellIndexIterator{}
}
// CellIndexRangeIterator is an iterator that seeks and iterates over a set of
// non-overlapping leaf cell ranges that cover the entire sphere. The indexed
// (CellID, label) pairs that intersect the current leaf cell range can be
// visited using CellIndexContentsIterator (see below).
type CellIndexRangeIterator struct {
rangeNodes []rangeNode
pos int
nonEmpty bool
}
// NewCellIndexRangeIterator creates an iterator for the given CellIndex.
// The iterator is initially *unpositioned*; you must call a positioning method
// such as Begin() or Seek() before accessing its contents.
func NewCellIndexRangeIterator(index *CellIndex) *CellIndexRangeIterator {
return &CellIndexRangeIterator{
rangeNodes: index.rangeNodes,
}
}
// NewCellIndexNonEmptyRangeIterator creates an iterator for the given CellIndex.
// The iterator is initially *unpositioned*; you must call a positioning method such as
// Begin() or Seek() before accessing its contents.
func NewCellIndexNonEmptyRangeIterator(index *CellIndex) *CellIndexRangeIterator {
return &CellIndexRangeIterator{
rangeNodes: index.rangeNodes,
nonEmpty: true,
}
}
// StartID reports the CellID of the start of the current range of leaf CellIDs.
//
// If done is true, this returns the last possible CellID. This property means
// that most loops do not need to test done explicitly.
func (c *CellIndexRangeIterator) StartID() CellID {
return c.rangeNodes[c.pos].startID
}
// LimitID reports the non-inclusive end of the current range of leaf CellIDs.
//
// This assumes the iterator is not done.
func (c *CellIndexRangeIterator) LimitID() CellID {
return c.rangeNodes[c.pos+1].startID
}
// IsEmpty reports if no (CellID, label) pairs intersect this range.
// Also returns true if done() is true.
func (c *CellIndexRangeIterator) IsEmpty() bool {
return c.rangeNodes[c.pos].contents == cellIndexDoneContents
}
// Begin positions the iterator at the first range of leaf cells (if any).
func (c *CellIndexRangeIterator) Begin() {
c.pos = 0
for c.nonEmpty && c.IsEmpty() && !c.Done() {
c.pos++
}
}
// Prev positions the iterator at the previous entry and reports whether it was not
// already positioned at the beginning.
func (c *CellIndexRangeIterator) Prev() bool {
if c.nonEmpty {
return c.nonEmptyPrev()
}
return c.prev()
}
// prev is used to position the iterator at the previous entry without checking
// if nonEmpty is true to prevent unwanted recursion.
func (c *CellIndexRangeIterator) prev() bool {
if c.pos == 0 {
return false
}
c.pos--
return true
}
// Prev positions the iterator at the previous entry, and reports whether it was
// already positioned at the beginning.
func (c *CellIndexRangeIterator) nonEmptyPrev() bool {
for c.prev() {
if !c.IsEmpty() {
return true
}
}
// Return the iterator to its original position.
if c.IsEmpty() && !c.Done() {
c.Next()
}
return false
}
// Next advances the iterator to the next range of leaf cells.
//
// This assumes the iterator is not done.
func (c *CellIndexRangeIterator) Next() {
c.pos++
for c.nonEmpty && c.IsEmpty() && !c.Done() {
c.pos++
}
}
// Advance reports if advancing would leave it positioned on a valid range. If
// the value would not be valid, the positioning is not changed.
func (c *CellIndexRangeIterator) Advance(n int) bool {
// Note that the last element of rangeNodes is a sentinel value.
if n >= len(c.rangeNodes)-1-c.pos {
return false
}
c.pos += n
return true
}
// Finish positions the iterator so that done is true.
func (c *CellIndexRangeIterator) Finish() {
// Note that the last element of rangeNodes is a sentinel value.
c.pos = len(c.rangeNodes) - 1
}
// Done reports if the iterator is positioned beyond the last valid range.
func (c *CellIndexRangeIterator) Done() bool {
return c.pos >= len(c.rangeNodes)-1
}
// Seek positions the iterator at the first range with startID >= target.
// Such an entry always exists as long as "target" is a valid leaf cell.
//
// Note that it is valid to access startID even when done is true.
func (c *CellIndexRangeIterator) Seek(target CellID) {
c.pos = sort.Search(len(c.rangeNodes), func(i int) bool {
return c.rangeNodes[i].startID > target
}) - 1
// Ensure we don't go beyond the beginning.
if c.pos < 0 {
c.pos = 0
}
// Nonempty needs to find the next non-empty entry.
for c.nonEmpty && c.IsEmpty() && !c.Done() {
// c.Next()
c.pos++
}
}
// CellIndexContentsIterator is an iterator that visits the (CellID, label) pairs
// that cover a set of leaf cell ranges (see CellIndexRangeIterator). Note that
// when multiple leaf cell ranges are visited, this iterator only guarantees that
// each result will be reported at least once, i.e. duplicate values may be
// suppressed. If you want duplicate values to be reported again, be sure to call
// Clear first.
//
// In particular, the implementation guarantees that when multiple leaf
// cell ranges are visited in monotonically increasing order, then each
// (CellID, label) pair is reported exactly once.
type CellIndexContentsIterator struct {
// The maximum index within the cellTree slice visited during the
// previous call to StartUnion. This is used to eliminate duplicate
// values when StartUnion is called multiple times.
nodeCutoff int32
// The maximum index within the cellTree visited during the
// current call to StartUnion. This is used to update nodeCutoff.
nextNodeCutoff int32
// The value of startID from the previous call to StartUnion.
// This is used to check whether these values are monotonically
// increasing.
prevStartID CellID
// The cell tree from CellIndex
cellTree []cellIndexNode
// A copy of the current node in the cell tree.
node cellIndexNode
}
// NewCellIndexContentsIterator returns a new contents iterator.
//
// Note that the iterator needs to be positioned using StartUnion before
// it can be safely used.
func NewCellIndexContentsIterator(index *CellIndex) *CellIndexContentsIterator {
it := &CellIndexContentsIterator{
cellTree: index.cellTree,
prevStartID: 0,
nodeCutoff: -1,
nextNodeCutoff: -1,
node: cellIndexNode{label: cellIndexDoneContents},
}
return it
}
// Clear clears all state with respect to which range(s) have been visited.
func (c *CellIndexContentsIterator) Clear() {
c.prevStartID = 0
c.nodeCutoff = -1
c.nextNodeCutoff = -1
c.node.label = cellIndexDoneContents
}
// CellID returns the current CellID.
func (c *CellIndexContentsIterator) CellID() CellID {
return c.node.cellID
}
// Label returns the current Label.
func (c *CellIndexContentsIterator) Label() int32 {
return c.node.label
}
// Next advances the iterator to the next (CellID, label) pair covered by the
// current leaf cell range.
//
// This requires the iterator to not be done.
func (c *CellIndexContentsIterator) Next() {
if c.node.parent <= c.nodeCutoff {
// We have already processed this node and its ancestors.
c.nodeCutoff = c.nextNodeCutoff
c.node.label = cellIndexDoneContents
} else {
c.node = c.cellTree[c.node.parent]
}
}
// Done reports if all (CellID, label) pairs have been visited.
func (c *CellIndexContentsIterator) Done() bool {
return c.node.label == cellIndexDoneContents
}
// StartUnion positions the ContentsIterator at the first (cell_id, label) pair
// that covers the given leaf cell range. Note that when multiple leaf cell
// ranges are visited using the same ContentsIterator, duplicate values
// may be suppressed. If you don't want this behavior, call Reset() first.
func (c *CellIndexContentsIterator) StartUnion(r *CellIndexRangeIterator) {
if r.StartID() < c.prevStartID {
c.nodeCutoff = -1 // Can't automatically eliminate duplicates.
}
c.prevStartID = r.StartID()
contents := r.rangeNodes[r.pos].contents
if contents <= c.nodeCutoff {
c.node.label = cellIndexDoneContents
} else {
c.node = c.cellTree[contents]
}
// When visiting ancestors, we can stop as soon as the node index is smaller
// than any previously visited node index. Because indexes are assigned
// using a preorder traversal, such nodes are guaranteed to have already
// been reported.
c.nextNodeCutoff = contents
}
// CellIndex stores a collection of (CellID, label) pairs.
//
// The CellIDs may be overlapping or contain duplicate values. For example, a
// CellIndex could store a collection of CellUnions, where each CellUnion
// gets its own non-negative int32 label.
//
// Similar to ShapeIndex and PointIndex which map each stored element to an
// identifier, CellIndex stores a label that is typically used to map the
// results of queries back to client's specific data.
//
// The zero value for a CellIndex is sufficient when constructing a CellIndex.
//
// To build a CellIndex where each Cell has a distinct label, call Add for each
// (CellID, label) pair, and then Build the index. For example:
//
// // contents is a mapping of an identifier in my system (restaurantID,
// // vehicleID, etc) to a CellID
// var contents = map[int32]CellID{...}
//
// for key, val := range contents {
// index.Add(val, key)
// }
//
// index.Build()
//
// There is also a helper method that adds all elements of CellUnion with the
// same label:
//
// index.AddCellUnion(cellUnion, label)
//
// Note that the index is not dynamic; the contents of the index cannot be
// changed once it has been built. Adding more after calling Build results in
// undefined behavior of the index.
//
// There are several options for retrieving data from the index. The simplest
// is to use a built-in method such as IntersectingLabels (which returns
// the labels of all cells that intersect a given target CellUnion):
//
// labels := index.IntersectingLabels(targetUnion);
//
// Alternatively, you can use a ClosestCellQuery which computes the cell(s)
// that are closest to a given target geometry.
//
// For example, here is how to find all cells that are closer than
// distanceLimit to a given target point:
//
// query := NewClosestCellQuery(cellIndex, opts)
// target := NewMinDistanceToPointTarget(targetPoint);
// for result := range query.FindCells(target) {
// // result.Distance() is the distance to the target.
// // result.CellID() is the indexed CellID.
// // result.Label() is the label associated with the CellID.
// DoSomething(targetPoint, result);
// }
//
// Internally, the index consists of a set of non-overlapping leaf cell ranges
// that subdivide the sphere and such that each range intersects a particular
// set of (cellID, label) pairs.
//
// Most clients should use either the methods such as VisitIntersectingCells
// and IntersectingLabels, or a helper such as ClosestCellQuery.
type CellIndex struct {
// A tree of (cellID, label) pairs such that if X is an ancestor of Y, then
// X.cellID contains Y.cellID. The contents of a given range of leaf
// cells can be represented by pointing to a node of this tree.
cellTree []cellIndexNode
// The last element of rangeNodes is a sentinel value, which is necessary
// in order to represent the range covered by the previous element.
rangeNodes []rangeNode
}
// Add adds the given CellID and Label to the index.
func (c *CellIndex) Add(id CellID, label int32) {
if label < 0 {
panic("labels must be non-negative")
}
c.cellTree = append(c.cellTree, cellIndexNode{cellID: id, label: label, parent: -1})
}
// AddCellUnion adds all of the elements of the given CellUnion to the index with the same label.
func (c *CellIndex) AddCellUnion(cu CellUnion, label int32) {
if label < 0 {
panic("labels must be non-negative")
}
for _, cell := range cu {
c.Add(cell, label)
}
}
// Build builds the index for use. This method should only be called once.
func (c *CellIndex) Build() {
// To build the cell tree and leaf cell ranges, we maintain a stack of
// (CellID, label) pairs that contain the current leaf cell. This struct
// represents an instruction to push or pop a (cellID, label) pair.
//
// If label >= 0, the (cellID, label) pair is pushed on the stack.
// If CellID == SentinelCellID, a pair is popped from the stack.
// Otherwise the stack is unchanged but a rangeNode is still emitted.
// delta represents an entry in a stack of (CellID, label) pairs used in the
// construction of the CellIndex structure.
type delta struct {
startID CellID
cellID CellID
label int32
}
deltas := make([]delta, 0, 2*len(c.cellTree)+2)
// Create two deltas for each (cellID, label) pair: one to add the pair to
// the stack (at the start of its leaf cell range), and one to remove it from
// the stack (at the end of its leaf cell range).
for _, node := range c.cellTree {
deltas = append(deltas, delta{
startID: node.cellID.RangeMin(),
cellID: node.cellID,
label: node.label,
})
deltas = append(deltas, delta{
startID: node.cellID.RangeMax().Next(),
cellID: SentinelCellID,
label: -1,
})
}
// We also create two special deltas to ensure that a RangeNode is emitted at
// the beginning and end of the CellID range.
deltas = append(deltas, delta{
startID: CellIDFromFace(0).ChildBeginAtLevel(maxLevel),
cellID: CellID(0),
label: -1,
})
deltas = append(deltas, delta{
startID: CellIDFromFace(5).ChildEndAtLevel(maxLevel),
cellID: CellID(0),
label: -1,
})
sort.Slice(deltas, func(i, j int) bool {
// deltas are sorted first by startID, then in reverse order by cellID,
// and then by label. This is necessary to ensure that (1) larger cells
// are pushed on the stack before smaller cells, and (2) cells are popped
// off the stack before any new cells are added.
if si, sj := deltas[i].startID, deltas[j].startID; si != sj {
return si < sj
}
if si, sj := deltas[i].cellID, deltas[j].cellID; si != sj {
return si > sj
}
return deltas[i].label < deltas[j].label
})
// Now walk through the deltas to build the leaf cell ranges and cell tree
// (which is essentially a permanent form of the "stack" described above).
c.cellTree = nil
c.rangeNodes = nil
contents := int32(-1)
for i := 0; i < len(deltas); {
startID := deltas[i].startID
// Process all the deltas associated with the current startID.
for ; i < len(deltas) && deltas[i].startID == startID; i++ {
if deltas[i].label >= 0 {
c.cellTree = append(c.cellTree, cellIndexNode{
cellID: deltas[i].cellID,
label: deltas[i].label,
parent: contents})
contents = int32(len(c.cellTree) - 1)
} else if deltas[i].cellID == SentinelCellID {
contents = c.cellTree[contents].parent
}
}
c.rangeNodes = append(c.rangeNodes, rangeNode{startID, contents})
}
}
// TODO(roberts): Differences from C++
// IntersectingLabels
// VisitIntersectingCells
// CellIndexIterator

View File

@ -1,944 +0,0 @@
// Copyright 2014 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 (
"bytes"
"fmt"
"io"
"math"
"sort"
"strconv"
"strings"
"github.com/golang/geo/r1"
"github.com/golang/geo/r2"
"github.com/golang/geo/r3"
"github.com/golang/geo/s1"
)
// CellID uniquely identifies a cell in the S2 cell decomposition.
// The most significant 3 bits encode the face number (0-5). The
// remaining 61 bits encode the position of the center of this cell
// along the Hilbert curve on that face. The zero value and the value
// (1<<64)-1 are invalid cell IDs. The first compares less than any
// valid cell ID, the second as greater than any valid cell ID.
//
// Sequentially increasing cell IDs follow a continuous space-filling curve
// over the entire sphere. They have the following properties:
//
// - The ID of a cell at level k consists of a 3-bit face number followed
// by k bit pairs that recursively select one of the four children of
// each cell. The next bit is always 1, and all other bits are 0.
// Therefore, the level of a cell is determined by the position of its
// lowest-numbered bit that is turned on (for a cell at level k, this
// position is 2 * (maxLevel - k)).
//
// - The ID of a parent cell is at the midpoint of the range of IDs spanned
// by its children (or by its descendants at any level).
//
// Leaf cells are often used to represent points on the unit sphere, and
// this type provides methods for converting directly between these two
// representations. For cells that represent 2D regions rather than
// discrete point, it is better to use Cells.
type CellID uint64
// SentinelCellID is an invalid cell ID guaranteed to be larger than any
// valid cell ID. It is used primarily by ShapeIndex. The value is also used
// by some S2 types when encoding data.
// Note that the sentinel's RangeMin == RangeMax == itself.
const SentinelCellID = CellID(^uint64(0))
// sortCellIDs sorts the slice of CellIDs in place.
func sortCellIDs(ci []CellID) {
sort.Sort(cellIDs(ci))
}
// cellIDs implements the Sort interface for slices of CellIDs.
type cellIDs []CellID
func (c cellIDs) Len() int { return len(c) }
func (c cellIDs) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
func (c cellIDs) Less(i, j int) bool { return c[i] < c[j] }
// TODO(dsymonds): Some of these constants should probably be exported.
const (
faceBits = 3
numFaces = 6
// This is the number of levels needed to specify a leaf cell.
maxLevel = 30
// The extra position bit (61 rather than 60) lets us encode each cell as its
// Hilbert curve position at the cell center (which is halfway along the
// portion of the Hilbert curve that fills that cell).
posBits = 2*maxLevel + 1
// The maximum index of a valid leaf cell plus one. The range of valid leaf
// cell indices is [0..maxSize-1].
maxSize = 1 << maxLevel
wrapOffset = uint64(numFaces) << posBits
)
// CellIDFromFacePosLevel returns a cell given its face in the range
// [0,5], the 61-bit Hilbert curve position pos within that face, and
// the level in the range [0,maxLevel]. The position in the cell ID
// will be truncated to correspond to the Hilbert curve position at
// the center of the returned cell.
func CellIDFromFacePosLevel(face int, pos uint64, level int) CellID {
return CellID(uint64(face)<<posBits + pos | 1).Parent(level)
}
// CellIDFromFace returns the cell corresponding to a given S2 cube face.
func CellIDFromFace(face int) CellID {
return CellID((uint64(face) << posBits) + lsbForLevel(0))
}
// CellIDFromLatLng returns the leaf cell containing ll.
func CellIDFromLatLng(ll LatLng) CellID {
return cellIDFromPoint(PointFromLatLng(ll))
}
// CellIDFromToken returns a cell given a hex-encoded string of its uint64 ID.
func CellIDFromToken(s string) CellID {
if len(s) > 16 {
return CellID(0)
}
n, err := strconv.ParseUint(s, 16, 64)
if err != nil {
return CellID(0)
}
// Equivalent to right-padding string with zeros to 16 characters.
if len(s) < 16 {
n = n << (4 * uint(16-len(s)))
}
return CellID(n)
}
// ToToken returns a hex-encoded string of the uint64 cell id, with leading
// zeros included but trailing zeros stripped.
func (ci CellID) ToToken() string {
s := strings.TrimRight(fmt.Sprintf("%016x", uint64(ci)), "0")
if len(s) == 0 {
return "X"
}
return s
}
// IsValid reports whether ci represents a valid cell.
func (ci CellID) IsValid() bool {
return ci.Face() < numFaces && (ci.lsb()&0x1555555555555555 != 0)
}
// Face returns the cube face for this cell ID, in the range [0,5].
func (ci CellID) Face() int { return int(uint64(ci) >> posBits) }
// Pos returns the position along the Hilbert curve of this cell ID, in the range [0,2^posBits-1].
func (ci CellID) Pos() uint64 { return uint64(ci) & (^uint64(0) >> faceBits) }
// Level returns the subdivision level of this cell ID, in the range [0, maxLevel].
func (ci CellID) Level() int {
return maxLevel - findLSBSetNonZero64(uint64(ci))>>1
}
// IsLeaf returns whether this cell ID is at the deepest level;
// that is, the level at which the cells are smallest.
func (ci CellID) IsLeaf() bool { return uint64(ci)&1 != 0 }
// ChildPosition returns the child position (0..3) of this cell's
// ancestor at the given level, relative to its parent. The argument
// should be in the range 1..kMaxLevel. For example,
// ChildPosition(1) returns the position of this cell's level-1
// ancestor within its top-level face cell.
func (ci CellID) ChildPosition(level int) int {
return int(uint64(ci)>>uint64(2*(maxLevel-level)+1)) & 3
}
// lsbForLevel returns the lowest-numbered bit that is on for cells at the given level.
func lsbForLevel(level int) uint64 { return 1 << uint64(2*(maxLevel-level)) }
// Parent returns the cell at the given level, which must be no greater than the current level.
func (ci CellID) Parent(level int) CellID {
lsb := lsbForLevel(level)
return CellID((uint64(ci) & -lsb) | lsb)
}
// immediateParent is cheaper than Parent, but assumes !ci.isFace().
func (ci CellID) immediateParent() CellID {
nlsb := CellID(ci.lsb() << 2)
return (ci & -nlsb) | nlsb
}
// isFace returns whether this is a top-level (face) cell.
func (ci CellID) isFace() bool { return uint64(ci)&(lsbForLevel(0)-1) == 0 }
// lsb returns the least significant bit that is set.
func (ci CellID) lsb() uint64 { return uint64(ci) & -uint64(ci) }
// Children returns the four immediate children of this cell.
// If ci is a leaf cell, it returns four identical cells that are not the children.
func (ci CellID) Children() [4]CellID {
var ch [4]CellID
lsb := CellID(ci.lsb())
ch[0] = ci - lsb + lsb>>2
lsb >>= 1
ch[1] = ch[0] + lsb
ch[2] = ch[1] + lsb
ch[3] = ch[2] + lsb
return ch
}
func sizeIJ(level int) int {
return 1 << uint(maxLevel-level)
}
// EdgeNeighbors returns the four cells that are adjacent across the cell's four edges.
// Edges 0, 1, 2, 3 are in the down, right, up, left directions in the face space.
// All neighbors are guaranteed to be distinct.
func (ci CellID) EdgeNeighbors() [4]CellID {
level := ci.Level()
size := sizeIJ(level)
f, i, j, _ := ci.faceIJOrientation()
return [4]CellID{
cellIDFromFaceIJWrap(f, i, j-size).Parent(level),
cellIDFromFaceIJWrap(f, i+size, j).Parent(level),
cellIDFromFaceIJWrap(f, i, j+size).Parent(level),
cellIDFromFaceIJWrap(f, i-size, j).Parent(level),
}
}
// VertexNeighbors returns the neighboring cellIDs with vertex closest to this cell at the given level.
// (Normally there are four neighbors, but the closest vertex may only have three neighbors if it is one of
// the 8 cube vertices.)
func (ci CellID) VertexNeighbors(level int) []CellID {
halfSize := sizeIJ(level + 1)
size := halfSize << 1
f, i, j, _ := ci.faceIJOrientation()
var isame, jsame bool
var ioffset, joffset int
if i&halfSize != 0 {
ioffset = size
isame = (i + size) < maxSize
} else {
ioffset = -size
isame = (i - size) >= 0
}
if j&halfSize != 0 {
joffset = size
jsame = (j + size) < maxSize
} else {
joffset = -size
jsame = (j - size) >= 0
}
results := []CellID{
ci.Parent(level),
cellIDFromFaceIJSame(f, i+ioffset, j, isame).Parent(level),
cellIDFromFaceIJSame(f, i, j+joffset, jsame).Parent(level),
}
if isame || jsame {
results = append(results, cellIDFromFaceIJSame(f, i+ioffset, j+joffset, isame && jsame).Parent(level))
}
return results
}
// AllNeighbors returns all neighbors of this cell at the given level. Two
// cells X and Y are neighbors if their boundaries intersect but their
// interiors do not. In particular, two cells that intersect at a single
// point are neighbors. Note that for cells adjacent to a face vertex, the
// same neighbor may be returned more than once. There could be up to eight
// neighbors including the diagonal ones that share the vertex.
//
// This requires level >= ci.Level().
func (ci CellID) AllNeighbors(level int) []CellID {
var neighbors []CellID
face, i, j, _ := ci.faceIJOrientation()
// Find the coordinates of the lower left-hand leaf cell. We need to
// normalize (i,j) to a known position within the cell because level
// may be larger than this cell's level.
size := sizeIJ(ci.Level())
i &= -size
j &= -size
nbrSize := sizeIJ(level)
// We compute the top-bottom, left-right, and diagonal neighbors in one
// pass. The loop test is at the end of the loop to avoid 32-bit overflow.
for k := -nbrSize; ; k += nbrSize {
var sameFace bool
if k < 0 {
sameFace = (j+k >= 0)
} else if k >= size {
sameFace = (j+k < maxSize)
} else {
sameFace = true
// Top and bottom neighbors.
neighbors = append(neighbors, cellIDFromFaceIJSame(face, i+k, j-nbrSize,
j-size >= 0).Parent(level))
neighbors = append(neighbors, cellIDFromFaceIJSame(face, i+k, j+size,
j+size < maxSize).Parent(level))
}
// Left, right, and diagonal neighbors.
neighbors = append(neighbors, cellIDFromFaceIJSame(face, i-nbrSize, j+k,
sameFace && i-size >= 0).Parent(level))
neighbors = append(neighbors, cellIDFromFaceIJSame(face, i+size, j+k,
sameFace && i+size < maxSize).Parent(level))
if k >= size {
break
}
}
return neighbors
}
// RangeMin returns the minimum CellID that is contained within this cell.
func (ci CellID) RangeMin() CellID { return CellID(uint64(ci) - (ci.lsb() - 1)) }
// RangeMax returns the maximum CellID that is contained within this cell.
func (ci CellID) RangeMax() CellID { return CellID(uint64(ci) + (ci.lsb() - 1)) }
// Contains returns true iff the CellID contains oci.
func (ci CellID) Contains(oci CellID) bool {
return uint64(ci.RangeMin()) <= uint64(oci) && uint64(oci) <= uint64(ci.RangeMax())
}
// Intersects returns true iff the CellID intersects oci.
func (ci CellID) Intersects(oci CellID) bool {
return uint64(oci.RangeMin()) <= uint64(ci.RangeMax()) && uint64(oci.RangeMax()) >= uint64(ci.RangeMin())
}
// String returns the string representation of the cell ID in the form "1/3210".
func (ci CellID) String() string {
if !ci.IsValid() {
return "Invalid: " + strconv.FormatInt(int64(ci), 16)
}
var b bytes.Buffer
b.WriteByte("012345"[ci.Face()]) // values > 5 will have been picked off by !IsValid above
b.WriteByte('/')
for level := 1; level <= ci.Level(); level++ {
b.WriteByte("0123"[ci.ChildPosition(level)])
}
return b.String()
}
// cellIDFromString returns a CellID from a string in the form "1/3210".
func cellIDFromString(s string) CellID {
level := len(s) - 2
if level < 0 || level > maxLevel {
return CellID(0)
}
face := int(s[0] - '0')
if face < 0 || face > 5 || s[1] != '/' {
return CellID(0)
}
id := CellIDFromFace(face)
for i := 2; i < len(s); i++ {
childPos := s[i] - '0'
if childPos < 0 || childPos > 3 {
return CellID(0)
}
id = id.Children()[childPos]
}
return id
}
// Point returns the center of the s2 cell on the sphere as a Point.
// The maximum directional error in Point (compared to the exact
// mathematical result) is 1.5 * dblEpsilon radians, and the maximum length
// error is 2 * dblEpsilon (the same as Normalize).
func (ci CellID) Point() Point { return Point{ci.rawPoint().Normalize()} }
// LatLng returns the center of the s2 cell on the sphere as a LatLng.
func (ci CellID) LatLng() LatLng { return LatLngFromPoint(Point{ci.rawPoint()}) }
// ChildBegin returns the first child in a traversal of the children of this cell, in Hilbert curve order.
//
// for ci := c.ChildBegin(); ci != c.ChildEnd(); ci = ci.Next() {
// ...
// }
func (ci CellID) ChildBegin() CellID {
ol := ci.lsb()
return CellID(uint64(ci) - ol + ol>>2)
}
// ChildBeginAtLevel returns the first cell in a traversal of children a given level deeper than this cell, in
// Hilbert curve order. The given level must be no smaller than the cell's level.
// See ChildBegin for example use.
func (ci CellID) ChildBeginAtLevel(level int) CellID {
return CellID(uint64(ci) - ci.lsb() + lsbForLevel(level))
}
// ChildEnd returns the first cell after a traversal of the children of this cell in Hilbert curve order.
// The returned cell may be invalid.
func (ci CellID) ChildEnd() CellID {
ol := ci.lsb()
return CellID(uint64(ci) + ol + ol>>2)
}
// ChildEndAtLevel returns the first cell after the last child in a traversal of children a given level deeper
// than this cell, in Hilbert curve order.
// The given level must be no smaller than the cell's level.
// The returned cell may be invalid.
func (ci CellID) ChildEndAtLevel(level int) CellID {
return CellID(uint64(ci) + ci.lsb() + lsbForLevel(level))
}
// Next returns the next cell along the Hilbert curve.
// This is expected to be used with ChildBegin and ChildEnd,
// or ChildBeginAtLevel and ChildEndAtLevel.
func (ci CellID) Next() CellID {
return CellID(uint64(ci) + ci.lsb()<<1)
}
// Prev returns the previous cell along the Hilbert curve.
func (ci CellID) Prev() CellID {
return CellID(uint64(ci) - ci.lsb()<<1)
}
// NextWrap returns the next cell along the Hilbert curve, wrapping from last to
// first as necessary. This should not be used with ChildBegin and ChildEnd.
func (ci CellID) NextWrap() CellID {
n := ci.Next()
if uint64(n) < wrapOffset {
return n
}
return CellID(uint64(n) - wrapOffset)
}
// PrevWrap returns the previous cell along the Hilbert curve, wrapping around from
// first to last as necessary. This should not be used with ChildBegin and ChildEnd.
func (ci CellID) PrevWrap() CellID {
p := ci.Prev()
if uint64(p) < wrapOffset {
return p
}
return CellID(uint64(p) + wrapOffset)
}
// AdvanceWrap advances or retreats the indicated number of steps along the
// Hilbert curve at the current level and returns the new position. The
// position wraps between the first and last faces as necessary.
func (ci CellID) AdvanceWrap(steps int64) CellID {
if steps == 0 {
return ci
}
// We clamp the number of steps if necessary to ensure that we do not
// advance past the End() or before the Begin() of this level.
shift := uint(2*(maxLevel-ci.Level()) + 1)
if steps < 0 {
if min := -int64(uint64(ci) >> shift); steps < min {
wrap := int64(wrapOffset >> shift)
steps %= wrap
if steps < min {
steps += wrap
}
}
} else {
// Unlike Advance(), we don't want to return End(level).
if max := int64((wrapOffset - uint64(ci)) >> shift); steps > max {
wrap := int64(wrapOffset >> shift)
steps %= wrap
if steps > max {
steps -= wrap
}
}
}
// If steps is negative, then shifting it left has undefined behavior.
// Cast to uint64 for a 2's complement answer.
return CellID(uint64(ci) + (uint64(steps) << shift))
}
// Encode encodes the CellID.
func (ci CellID) Encode(w io.Writer) error {
e := &encoder{w: w}
ci.encode(e)
return e.err
}
func (ci CellID) encode(e *encoder) {
e.writeUint64(uint64(ci))
}
// Decode decodes the CellID.
func (ci *CellID) Decode(r io.Reader) error {
d := &decoder{r: asByteReader(r)}
ci.decode(d)
return d.err
}
func (ci *CellID) decode(d *decoder) {
*ci = CellID(d.readUint64())
}
// TODO: the methods below are not exported yet. Settle on the entire API design
// before doing this. Do we want to mirror the C++ one as closely as possible?
// distanceFromBegin returns the number of steps along the Hilbert curve that
// this cell is from the first node in the S2 hierarchy at our level. (i.e.,
// FromFace(0).ChildBeginAtLevel(ci.Level())). This is analogous to Pos(), but
// for this cell's level.
// The return value is always non-negative.
func (ci CellID) distanceFromBegin() int64 {
return int64(ci >> uint64(2*(maxLevel-ci.Level())+1))
}
// rawPoint returns an unnormalized r3 vector from the origin through the center
// of the s2 cell on the sphere.
func (ci CellID) rawPoint() r3.Vector {
face, si, ti := ci.faceSiTi()
return faceUVToXYZ(face, stToUV((0.5/maxSize)*float64(si)), stToUV((0.5/maxSize)*float64(ti)))
}
// faceSiTi returns the Face/Si/Ti coordinates of the center of the cell.
func (ci CellID) faceSiTi() (face int, si, ti uint32) {
face, i, j, _ := ci.faceIJOrientation()
delta := 0
if ci.IsLeaf() {
delta = 1
} else {
if (i^(int(ci)>>2))&1 != 0 {
delta = 2
}
}
return face, uint32(2*i + delta), uint32(2*j + delta)
}
// faceIJOrientation uses the global lookupIJ table to unfiddle the bits of ci.
func (ci CellID) faceIJOrientation() (f, i, j, orientation int) {
f = ci.Face()
orientation = f & swapMask
nbits := maxLevel - 7*lookupBits // first iteration
// Each iteration maps 8 bits of the Hilbert curve position into
// 4 bits of "i" and "j". The lookup table transforms a key of the
// form "ppppppppoo" to a value of the form "iiiijjjjoo", where the
// letters [ijpo] represents bits of "i", "j", the Hilbert curve
// position, and the Hilbert curve orientation respectively.
//
// On the first iteration we need to be careful to clear out the bits
// representing the cube face.
for k := 7; k >= 0; k-- {
orientation += (int(uint64(ci)>>uint64(k*2*lookupBits+1)) & ((1 << uint(2*nbits)) - 1)) << 2
orientation = lookupIJ[orientation]
i += (orientation >> (lookupBits + 2)) << uint(k*lookupBits)
j += ((orientation >> 2) & ((1 << lookupBits) - 1)) << uint(k*lookupBits)
orientation &= (swapMask | invertMask)
nbits = lookupBits // following iterations
}
// The position of a non-leaf cell at level "n" consists of a prefix of
// 2*n bits that identifies the cell, followed by a suffix of
// 2*(maxLevel-n)+1 bits of the form 10*. If n==maxLevel, the suffix is
// just "1" and has no effect. Otherwise, it consists of "10", followed
// by (maxLevel-n-1) repetitions of "00", followed by "0". The "10" has
// no effect, while each occurrence of "00" has the effect of reversing
// the swapMask bit.
if ci.lsb()&0x1111111111111110 != 0 {
orientation ^= swapMask
}
return
}
// cellIDFromFaceIJ returns a leaf cell given its cube face (range 0..5) and IJ coordinates.
func cellIDFromFaceIJ(f, i, j int) CellID {
// Note that this value gets shifted one bit to the left at the end
// of the function.
n := uint64(f) << (posBits - 1)
// Alternating faces have opposite Hilbert curve orientations; this
// is necessary in order for all faces to have a right-handed
// coordinate system.
bits := f & swapMask
// Each iteration maps 4 bits of "i" and "j" into 8 bits of the Hilbert
// curve position. The lookup table transforms a 10-bit key of the form
// "iiiijjjjoo" to a 10-bit value of the form "ppppppppoo", where the
// letters [ijpo] denote bits of "i", "j", Hilbert curve position, and
// Hilbert curve orientation respectively.
for k := 7; k >= 0; k-- {
mask := (1 << lookupBits) - 1
bits += ((i >> uint(k*lookupBits)) & mask) << (lookupBits + 2)
bits += ((j >> uint(k*lookupBits)) & mask) << 2
bits = lookupPos[bits]
n |= uint64(bits>>2) << (uint(k) * 2 * lookupBits)
bits &= (swapMask | invertMask)
}
return CellID(n*2 + 1)
}
func cellIDFromFaceIJWrap(f, i, j int) CellID {
// Convert i and j to the coordinates of a leaf cell just beyond the
// boundary of this face. This prevents 32-bit overflow in the case
// of finding the neighbors of a face cell.
i = clampInt(i, -1, maxSize)
j = clampInt(j, -1, maxSize)
// We want to wrap these coordinates onto the appropriate adjacent face.
// The easiest way to do this is to convert the (i,j) coordinates to (x,y,z)
// (which yields a point outside the normal face boundary), and then call
// xyzToFaceUV to project back onto the correct face.
//
// The code below converts (i,j) to (si,ti), and then (si,ti) to (u,v) using
// the linear projection (u=2*s-1 and v=2*t-1). (The code further below
// converts back using the inverse projection, s=0.5*(u+1) and t=0.5*(v+1).
// Any projection would work here, so we use the simplest.) We also clamp
// the (u,v) coordinates so that the point is barely outside the
// [-1,1]x[-1,1] face rectangle, since otherwise the reprojection step
// (which divides by the new z coordinate) might change the other
// coordinates enough so that we end up in the wrong leaf cell.
const scale = 1.0 / maxSize
limit := math.Nextafter(1, 2)
u := math.Max(-limit, math.Min(limit, scale*float64((i<<1)+1-maxSize)))
v := math.Max(-limit, math.Min(limit, scale*float64((j<<1)+1-maxSize)))
// Find the leaf cell coordinates on the adjacent face, and convert
// them to a cell id at the appropriate level.
f, u, v = xyzToFaceUV(faceUVToXYZ(f, u, v))
return cellIDFromFaceIJ(f, stToIJ(0.5*(u+1)), stToIJ(0.5*(v+1)))
}
func cellIDFromFaceIJSame(f, i, j int, sameFace bool) CellID {
if sameFace {
return cellIDFromFaceIJ(f, i, j)
}
return cellIDFromFaceIJWrap(f, i, j)
}
// ijToSTMin converts the i- or j-index of a leaf cell to the minimum corresponding
// s- or t-value contained by that cell. The argument must be in the range
// [0..2**30], i.e. up to one position beyond the normal range of valid leaf
// cell indices.
func ijToSTMin(i int) float64 {
return float64(i) / float64(maxSize)
}
// stToIJ converts value in ST coordinates to a value in IJ coordinates.
func stToIJ(s float64) int {
return clampInt(int(math.Floor(maxSize*s)), 0, maxSize-1)
}
// cellIDFromPoint returns a leaf cell containing point p. Usually there is
// exactly one such cell, but for points along the edge of a cell, any
// adjacent cell may be (deterministically) chosen. This is because
// s2.CellIDs are considered to be closed sets. The returned cell will
// always contain the given point, i.e.
//
// CellFromPoint(p).ContainsPoint(p)
//
// is always true.
func cellIDFromPoint(p Point) CellID {
f, u, v := xyzToFaceUV(r3.Vector{p.X, p.Y, p.Z})
i := stToIJ(uvToST(u))
j := stToIJ(uvToST(v))
return cellIDFromFaceIJ(f, i, j)
}
// ijLevelToBoundUV returns the bounds in (u,v)-space for the cell at the given
// level containing the leaf cell with the given (i,j)-coordinates.
func ijLevelToBoundUV(i, j, level int) r2.Rect {
cellSize := sizeIJ(level)
xLo := i & -cellSize
yLo := j & -cellSize
return r2.Rect{
X: r1.Interval{
Lo: stToUV(ijToSTMin(xLo)),
Hi: stToUV(ijToSTMin(xLo + cellSize)),
},
Y: r1.Interval{
Lo: stToUV(ijToSTMin(yLo)),
Hi: stToUV(ijToSTMin(yLo + cellSize)),
},
}
}
// Constants related to the bit mangling in the Cell ID.
const (
lookupBits = 4
swapMask = 0x01
invertMask = 0x02
)
// The following lookup tables are used to convert efficiently between an
// (i,j) cell index and the corresponding position along the Hilbert curve.
//
// lookupPos maps 4 bits of "i", 4 bits of "j", and 2 bits representing the
// orientation of the current cell into 8 bits representing the order in which
// that subcell is visited by the Hilbert curve, plus 2 bits indicating the
// new orientation of the Hilbert curve within that subcell. (Cell
// orientations are represented as combination of swapMask and invertMask.)
//
// lookupIJ is an inverted table used for mapping in the opposite
// direction.
//
// We also experimented with looking up 16 bits at a time (14 bits of position
// plus 2 of orientation) but found that smaller lookup tables gave better
// performance. (2KB fits easily in the primary cache.)
var (
ijToPos = [4][4]int{
{0, 1, 3, 2}, // canonical order
{0, 3, 1, 2}, // axes swapped
{2, 3, 1, 0}, // bits inverted
{2, 1, 3, 0}, // swapped & inverted
}
posToIJ = [4][4]int{
{0, 1, 3, 2}, // canonical order: (0,0), (0,1), (1,1), (1,0)
{0, 2, 3, 1}, // axes swapped: (0,0), (1,0), (1,1), (0,1)
{3, 2, 0, 1}, // bits inverted: (1,1), (1,0), (0,0), (0,1)
{3, 1, 0, 2}, // swapped & inverted: (1,1), (0,1), (0,0), (1,0)
}
posToOrientation = [4]int{swapMask, 0, 0, invertMask | swapMask}
lookupIJ [1 << (2*lookupBits + 2)]int
lookupPos [1 << (2*lookupBits + 2)]int
)
func init() {
initLookupCell(0, 0, 0, 0, 0, 0)
initLookupCell(0, 0, 0, swapMask, 0, swapMask)
initLookupCell(0, 0, 0, invertMask, 0, invertMask)
initLookupCell(0, 0, 0, swapMask|invertMask, 0, swapMask|invertMask)
}
// initLookupCell initializes the lookupIJ table at init time.
func initLookupCell(level, i, j, origOrientation, pos, orientation int) {
if level == lookupBits {
ij := (i << lookupBits) + j
lookupPos[(ij<<2)+origOrientation] = (pos << 2) + orientation
lookupIJ[(pos<<2)+origOrientation] = (ij << 2) + orientation
return
}
level++
i <<= 1
j <<= 1
pos <<= 2
r := posToIJ[orientation]
initLookupCell(level, i+(r[0]>>1), j+(r[0]&1), origOrientation, pos, orientation^posToOrientation[0])
initLookupCell(level, i+(r[1]>>1), j+(r[1]&1), origOrientation, pos+1, orientation^posToOrientation[1])
initLookupCell(level, i+(r[2]>>1), j+(r[2]&1), origOrientation, pos+2, orientation^posToOrientation[2])
initLookupCell(level, i+(r[3]>>1), j+(r[3]&1), origOrientation, pos+3, orientation^posToOrientation[3])
}
// CommonAncestorLevel returns the level of the common ancestor of the two S2 CellIDs.
func (ci CellID) CommonAncestorLevel(other CellID) (level int, ok bool) {
bits := uint64(ci ^ other)
if bits < ci.lsb() {
bits = ci.lsb()
}
if bits < other.lsb() {
bits = other.lsb()
}
msbPos := findMSBSetNonZero64(bits)
if msbPos > 60 {
return 0, false
}
return (60 - msbPos) >> 1, true
}
// Advance advances or retreats the indicated number of steps along the
// Hilbert curve at the current level, and returns the new position. The
// position is never advanced past End() or before Begin().
func (ci CellID) Advance(steps int64) CellID {
if steps == 0 {
return ci
}
// We clamp the number of steps if necessary to ensure that we do not
// advance past the End() or before the Begin() of this level. Note that
// minSteps and maxSteps always fit in a signed 64-bit integer.
stepShift := uint(2*(maxLevel-ci.Level()) + 1)
if steps < 0 {
minSteps := -int64(uint64(ci) >> stepShift)
if steps < minSteps {
steps = minSteps
}
} else {
maxSteps := int64((wrapOffset + ci.lsb() - uint64(ci)) >> stepShift)
if steps > maxSteps {
steps = maxSteps
}
}
return ci + CellID(steps)<<stepShift
}
// centerST return the center of the CellID in (s,t)-space.
func (ci CellID) centerST() r2.Point {
_, si, ti := ci.faceSiTi()
return r2.Point{siTiToST(si), siTiToST(ti)}
}
// sizeST returns the edge length of this CellID in (s,t)-space at the given level.
func (ci CellID) sizeST(level int) float64 {
return ijToSTMin(sizeIJ(level))
}
// boundST returns the bound of this CellID in (s,t)-space.
func (ci CellID) boundST() r2.Rect {
s := ci.sizeST(ci.Level())
return r2.RectFromCenterSize(ci.centerST(), r2.Point{s, s})
}
// centerUV returns the center of this CellID in (u,v)-space. Note that
// the center of the cell is defined as the point at which it is recursively
// subdivided into four children; in general, it is not at the midpoint of
// the (u,v) rectangle covered by the cell.
func (ci CellID) centerUV() r2.Point {
_, si, ti := ci.faceSiTi()
return r2.Point{stToUV(siTiToST(si)), stToUV(siTiToST(ti))}
}
// boundUV returns the bound of this CellID in (u,v)-space.
func (ci CellID) boundUV() r2.Rect {
_, i, j, _ := ci.faceIJOrientation()
return ijLevelToBoundUV(i, j, ci.Level())
}
// expandEndpoint returns a new u-coordinate u' such that the distance from the
// line u=u' to the given edge (u,v0)-(u,v1) is exactly the given distance
// (which is specified as the sine of the angle corresponding to the distance).
func expandEndpoint(u, maxV, sinDist float64) float64 {
// This is based on solving a spherical right triangle, similar to the
// calculation in Cap.RectBound.
// Given an edge of the form (u,v0)-(u,v1), let maxV = max(abs(v0), abs(v1)).
sinUShift := sinDist * math.Sqrt((1+u*u+maxV*maxV)/(1+u*u))
cosUShift := math.Sqrt(1 - sinUShift*sinUShift)
// The following is an expansion of tan(atan(u) + asin(sinUShift)).
return (cosUShift*u + sinUShift) / (cosUShift - sinUShift*u)
}
// expandedByDistanceUV returns a rectangle expanded in (u,v)-space so that it
// contains all points within the given distance of the boundary, and return the
// smallest such rectangle. If the distance is negative, then instead shrink this
// rectangle so that it excludes all points within the given absolute distance
// of the boundary.
//
// Distances are measured *on the sphere*, not in (u,v)-space. For example,
// you can use this method to expand the (u,v)-bound of an CellID so that
// it contains all points within 5km of the original cell. You can then
// test whether a point lies within the expanded bounds like this:
//
// if u, v, ok := faceXYZtoUV(face, point); ok && bound.ContainsPoint(r2.Point{u,v}) { ... }
//
// Limitations:
//
// - Because the rectangle is drawn on one of the six cube-face planes
// (i.e., {x,y,z} = +/-1), it can cover at most one hemisphere. This
// limits the maximum amount that a rectangle can be expanded. For
// example, CellID bounds can be expanded safely by at most 45 degrees
// (about 5000 km on the Earth's surface).
//
// - The implementation is not exact for negative distances. The resulting
// rectangle will exclude all points within the given distance of the
// boundary but may be slightly smaller than necessary.
func expandedByDistanceUV(uv r2.Rect, distance s1.Angle) r2.Rect {
// Expand each of the four sides of the rectangle just enough to include all
// points within the given distance of that side. (The rectangle may be
// expanded by a different amount in (u,v)-space on each side.)
maxU := math.Max(math.Abs(uv.X.Lo), math.Abs(uv.X.Hi))
maxV := math.Max(math.Abs(uv.Y.Lo), math.Abs(uv.Y.Hi))
sinDist := math.Sin(float64(distance))
return r2.Rect{
X: r1.Interval{expandEndpoint(uv.X.Lo, maxV, -sinDist),
expandEndpoint(uv.X.Hi, maxV, sinDist)},
Y: r1.Interval{expandEndpoint(uv.Y.Lo, maxU, -sinDist),
expandEndpoint(uv.Y.Hi, maxU, sinDist)}}
}
// MaxTile returns the largest cell with the same RangeMin such that
// RangeMax < limit.RangeMin. It returns limit if no such cell exists.
// This method can be used to generate a small set of CellIDs that covers
// a given range (a tiling). This example shows how to generate a tiling
// for a semi-open range of leaf cells [start, limit):
//
// for id := start.MaxTile(limit); id != limit; id = id.Next().MaxTile(limit)) { ... }
//
// Note that in general the cells in the tiling will be of different sizes;
// they gradually get larger (near the middle of the range) and then
// gradually get smaller as limit is approached.
func (ci CellID) MaxTile(limit CellID) CellID {
start := ci.RangeMin()
if start >= limit.RangeMin() {
return limit
}
if ci.RangeMax() >= limit {
// The cell is too large, shrink it. Note that when generating coverings
// of CellID ranges, this loop usually executes only once. Also because
// ci.RangeMin() < limit.RangeMin(), we will always exit the loop by the
// time we reach a leaf cell.
for {
ci = ci.Children()[0]
if ci.RangeMax() < limit {
break
}
}
return ci
}
// The cell may be too small. Grow it if necessary. Note that generally
// this loop only iterates once.
for !ci.isFace() {
parent := ci.immediateParent()
if parent.RangeMin() != start || parent.RangeMax() >= limit {
break
}
ci = parent
}
return ci
}
// centerFaceSiTi returns the (face, si, ti) coordinates of the center of the cell.
// Note that although (si,ti) coordinates span the range [0,2**31] in general,
// the cell center coordinates are always in the range [1,2**31-1] and
// therefore can be represented using a signed 32-bit integer.
func (ci CellID) centerFaceSiTi() (face, si, ti int) {
// First we compute the discrete (i,j) coordinates of a leaf cell contained
// within the given cell. Given that cells are represented by the Hilbert
// curve position corresponding at their center, it turns out that the cell
// returned by faceIJOrientation is always one of two leaf cells closest
// to the center of the cell (unless the given cell is a leaf cell itself,
// in which case there is only one possibility).
//
// Given a cell of size s >= 2 (i.e. not a leaf cell), and letting (imin,
// jmin) be the coordinates of its lower left-hand corner, the leaf cell
// returned by faceIJOrientation is either (imin + s/2, jmin + s/2)
// (imin + s/2 - 1, jmin + s/2 - 1). The first case is the one we want.
// We can distinguish these two cases by looking at the low bit of i or
// j. In the second case the low bit is one, unless s == 2 (i.e. the
// level just above leaf cells) in which case the low bit is zero.
//
// In the code below, the expression ((i ^ (int(id) >> 2)) & 1) is true
// if we are in the second case described above.
face, i, j, _ := ci.faceIJOrientation()
delta := 0
if ci.IsLeaf() {
delta = 1
} else if (int64(i)^(int64(ci)>>2))&1 == 1 {
delta = 2
}
// Note that (2 * {i,j} + delta) will never overflow a 32-bit integer.
return face, 2*i + delta, 2*j + delta
}

View File

@ -1,590 +0,0 @@
// Copyright 2014 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 (
"fmt"
"io"
"sort"
"github.com/golang/geo/s1"
)
// A CellUnion is a collection of CellIDs.
//
// It is normalized if it is sorted, and does not contain redundancy.
// Specifically, it may not contain the same CellID twice, nor a CellID that
// is contained by another, nor the four sibling CellIDs that are children of
// a single higher level CellID.
//
// CellUnions are not required to be normalized, but certain operations will
// return different results if they are not (e.g. Contains).
type CellUnion []CellID
// CellUnionFromRange creates a CellUnion that covers the half-open range
// of leaf cells [begin, end). If begin == end the resulting union is empty.
// This requires that begin and end are both leaves, and begin <= end.
// To create a closed-ended range, pass in end.Next().
func CellUnionFromRange(begin, end CellID) CellUnion {
// We repeatedly add the largest cell we can.
var cu CellUnion
for id := begin.MaxTile(end); id != end; id = id.Next().MaxTile(end) {
cu = append(cu, id)
}
// The output is normalized because the cells are added in order by the iteration.
return cu
}
// CellUnionFromUnion creates a CellUnion from the union of the given CellUnions.
func CellUnionFromUnion(cellUnions ...CellUnion) CellUnion {
var cu CellUnion
for _, cellUnion := range cellUnions {
cu = append(cu, cellUnion...)
}
cu.Normalize()
return cu
}
// CellUnionFromIntersection creates a CellUnion from the intersection of the given CellUnions.
func CellUnionFromIntersection(x, y CellUnion) CellUnion {
var cu CellUnion
// This is a fairly efficient calculation that uses binary search to skip
// over sections of both input vectors. It takes constant time if all the
// cells of x come before or after all the cells of y in CellID order.
var i, j int
for i < len(x) && j < len(y) {
iMin := x[i].RangeMin()
jMin := y[j].RangeMin()
if iMin > jMin {
// Either j.Contains(i) or the two cells are disjoint.
if x[i] <= y[j].RangeMax() {
cu = append(cu, x[i])
i++
} else {
// Advance j to the first cell possibly contained by x[i].
j = y.lowerBound(j+1, len(y), iMin)
// The previous cell y[j-1] may now contain x[i].
if x[i] <= y[j-1].RangeMax() {
j--
}
}
} else if jMin > iMin {
// Identical to the code above with i and j reversed.
if y[j] <= x[i].RangeMax() {
cu = append(cu, y[j])
j++
} else {
i = x.lowerBound(i+1, len(x), jMin)
if y[j] <= x[i-1].RangeMax() {
i--
}
}
} else {
// i and j have the same RangeMin(), so one contains the other.
if x[i] < y[j] {
cu = append(cu, x[i])
i++
} else {
cu = append(cu, y[j])
j++
}
}
}
// The output is generated in sorted order.
cu.Normalize()
return cu
}
// CellUnionFromIntersectionWithCellID creates a CellUnion from the intersection
// of a CellUnion with the given CellID. This can be useful for splitting a
// CellUnion into chunks.
func CellUnionFromIntersectionWithCellID(x CellUnion, id CellID) CellUnion {
var cu CellUnion
if x.ContainsCellID(id) {
cu = append(cu, id)
cu.Normalize()
return cu
}
idmax := id.RangeMax()
for i := x.lowerBound(0, len(x), id.RangeMin()); i < len(x) && x[i] <= idmax; i++ {
cu = append(cu, x[i])
}
cu.Normalize()
return cu
}
// CellUnionFromDifference creates a CellUnion from the difference (x - y)
// of the given CellUnions.
func CellUnionFromDifference(x, y CellUnion) CellUnion {
// TODO(roberts): This is approximately O(N*log(N)), but could probably
// use similar techniques as CellUnionFromIntersectionWithCellID to be more efficient.
var cu CellUnion
for _, xid := range x {
cu.cellUnionDifferenceInternal(xid, &y)
}
// The output is generated in sorted order, and there should not be any
// cells that can be merged (provided that both inputs were normalized).
return cu
}
// The C++ constructor methods FromNormalized and FromVerbatim are not necessary
// since they don't call Normalize, and just set the CellIDs directly on the object,
// so straight casting is sufficient in Go to replicate this behavior.
// IsValid reports whether the cell union is valid, meaning that the CellIDs are
// valid, non-overlapping, and sorted in increasing order.
func (cu *CellUnion) IsValid() bool {
for i, cid := range *cu {
if !cid.IsValid() {
return false
}
if i == 0 {
continue
}
if (*cu)[i-1].RangeMax() >= cid.RangeMin() {
return false
}
}
return true
}
// IsNormalized reports whether the cell union is normalized, meaning that it is
// satisfies IsValid and that no four cells have a common parent.
// Certain operations such as Contains will return a different
// result if the cell union is not normalized.
func (cu *CellUnion) IsNormalized() bool {
for i, cid := range *cu {
if !cid.IsValid() {
return false
}
if i == 0 {
continue
}
if (*cu)[i-1].RangeMax() >= cid.RangeMin() {
return false
}
if i < 3 {
continue
}
if areSiblings((*cu)[i-3], (*cu)[i-2], (*cu)[i-1], cid) {
return false
}
}
return true
}
// Normalize normalizes the CellUnion.
func (cu *CellUnion) Normalize() {
sortCellIDs(*cu)
output := make([]CellID, 0, len(*cu)) // the list of accepted cells
// Loop invariant: output is a sorted list of cells with no redundancy.
for _, ci := range *cu {
// The first two passes here either ignore this new candidate,
// or remove previously accepted cells that are covered by this candidate.
// Ignore this cell if it is contained by the previous one.
// We only need to check the last accepted cell. The ordering of the
// cells implies containment (but not the converse), and output has no redundancy,
// so if this candidate is not contained by the last accepted cell
// then it cannot be contained by any previously accepted cell.
if len(output) > 0 && output[len(output)-1].Contains(ci) {
continue
}
// Discard any previously accepted cells contained by this one.
// This could be any contiguous trailing subsequence, but it can't be
// a discontiguous subsequence because of the containment property of
// sorted S2 cells mentioned above.
j := len(output) - 1 // last index to keep
for j >= 0 {
if !ci.Contains(output[j]) {
break
}
j--
}
output = output[:j+1]
// See if the last three cells plus this one can be collapsed.
// We loop because collapsing three accepted cells and adding a higher level cell
// could cascade into previously accepted cells.
for len(output) >= 3 && areSiblings(output[len(output)-3], output[len(output)-2], output[len(output)-1], ci) {
// Replace four children by their parent cell.
output = output[:len(output)-3]
ci = ci.immediateParent() // checked !ci.isFace above
}
output = append(output, ci)
}
*cu = output
}
// IntersectsCellID reports whether this CellUnion intersects the given cell ID.
func (cu *CellUnion) IntersectsCellID(id CellID) bool {
// Find index of array item that occurs directly after our probe cell:
i := sort.Search(len(*cu), func(i int) bool { return id < (*cu)[i] })
if i != len(*cu) && (*cu)[i].RangeMin() <= id.RangeMax() {
return true
}
return i != 0 && (*cu)[i-1].RangeMax() >= id.RangeMin()
}
// ContainsCellID reports whether the CellUnion contains the given cell ID.
// Containment is defined with respect to regions, e.g. a cell contains its 4 children.
//
// CAVEAT: If you have constructed a non-normalized CellUnion, note that groups
// of 4 child cells are *not* considered to contain their parent cell. To get
// this behavior you must use one of the call Normalize() explicitly.
func (cu *CellUnion) ContainsCellID(id CellID) bool {
// Find index of array item that occurs directly after our probe cell:
i := sort.Search(len(*cu), func(i int) bool { return id < (*cu)[i] })
if i != len(*cu) && (*cu)[i].RangeMin() <= id {
return true
}
return i != 0 && (*cu)[i-1].RangeMax() >= id
}
// Denormalize replaces this CellUnion with an expanded version of the
// CellUnion where any cell whose level is less than minLevel or where
// (level - minLevel) is not a multiple of levelMod is replaced by its
// children, until either both of these conditions are satisfied or the
// maximum level is reached.
func (cu *CellUnion) Denormalize(minLevel, levelMod int) {
var denorm CellUnion
for _, id := range *cu {
level := id.Level()
newLevel := level
if newLevel < minLevel {
newLevel = minLevel
}
if levelMod > 1 {
newLevel += (maxLevel - (newLevel - minLevel)) % levelMod
if newLevel > maxLevel {
newLevel = maxLevel
}
}
if newLevel == level {
denorm = append(denorm, id)
} else {
end := id.ChildEndAtLevel(newLevel)
for ci := id.ChildBeginAtLevel(newLevel); ci != end; ci = ci.Next() {
denorm = append(denorm, ci)
}
}
}
*cu = denorm
}
// RectBound returns a Rect that bounds this entity.
func (cu *CellUnion) RectBound() Rect {
bound := EmptyRect()
for _, c := range *cu {
bound = bound.Union(CellFromCellID(c).RectBound())
}
return bound
}
// CapBound returns a Cap that bounds this entity.
func (cu *CellUnion) CapBound() Cap {
if len(*cu) == 0 {
return EmptyCap()
}
// Compute the approximate centroid of the region. This won't produce the
// bounding cap of minimal area, but it should be close enough.
var centroid Point
for _, ci := range *cu {
area := AvgAreaMetric.Value(ci.Level())
centroid = Point{centroid.Add(ci.Point().Mul(area))}
}
if zero := (Point{}); centroid == zero {
centroid = PointFromCoords(1, 0, 0)
} else {
centroid = Point{centroid.Normalize()}
}
// Use the centroid as the cap axis, and expand the cap angle so that it
// contains the bounding caps of all the individual cells. Note that it is
// *not* sufficient to just bound all the cell vertices because the bounding
// cap may be concave (i.e. cover more than one hemisphere).
c := CapFromPoint(centroid)
for _, ci := range *cu {
c = c.AddCap(CellFromCellID(ci).CapBound())
}
return c
}
// ContainsCell reports whether this cell union contains the given cell.
func (cu *CellUnion) ContainsCell(c Cell) bool {
return cu.ContainsCellID(c.id)
}
// IntersectsCell reports whether this cell union intersects the given cell.
func (cu *CellUnion) IntersectsCell(c Cell) bool {
return cu.IntersectsCellID(c.id)
}
// ContainsPoint reports whether this cell union contains the given point.
func (cu *CellUnion) ContainsPoint(p Point) bool {
return cu.ContainsCell(CellFromPoint(p))
}
// CellUnionBound computes a covering of the CellUnion.
func (cu *CellUnion) CellUnionBound() []CellID {
return cu.CapBound().CellUnionBound()
}
// LeafCellsCovered reports the number of leaf cells covered by this cell union.
// This will be no more than 6*2^60 for the whole sphere.
func (cu *CellUnion) LeafCellsCovered() int64 {
var numLeaves int64
for _, c := range *cu {
numLeaves += 1 << uint64((maxLevel-int64(c.Level()))<<1)
}
return numLeaves
}
// Returns true if the given four cells have a common parent.
// This requires that the four CellIDs are distinct.
func areSiblings(a, b, c, d CellID) bool {
// A necessary (but not sufficient) condition is that the XOR of the
// four cell IDs must be zero. This is also very fast to test.
if (a ^ b ^ c) != d {
return false
}
// Now we do a slightly more expensive but exact test. First, compute a
// mask that blocks out the two bits that encode the child position of
// "id" with respect to its parent, then check that the other three
// children all agree with "mask".
mask := d.lsb() << 1
mask = ^(mask + (mask << 1))
idMasked := (uint64(d) & mask)
return ((uint64(a)&mask) == idMasked &&
(uint64(b)&mask) == idMasked &&
(uint64(c)&mask) == idMasked &&
!d.isFace())
}
// Contains reports whether this CellUnion contains all of the CellIDs of the given CellUnion.
func (cu *CellUnion) Contains(o CellUnion) bool {
// TODO(roberts): Investigate alternatives such as divide-and-conquer
// or alternating-skip-search that may be significantly faster in both
// the average and worst case. This applies to Intersects as well.
for _, id := range o {
if !cu.ContainsCellID(id) {
return false
}
}
return true
}
// Intersects reports whether this CellUnion intersects any of the CellIDs of the given CellUnion.
func (cu *CellUnion) Intersects(o CellUnion) bool {
for _, c := range *cu {
if o.IntersectsCellID(c) {
return true
}
}
return false
}
// lowerBound returns the index in this CellUnion to the first element whose value
// is not considered to go before the given cell id. (i.e., either it is equivalent
// or comes after the given id.) If there is no match, then end is returned.
func (cu *CellUnion) lowerBound(begin, end int, id CellID) int {
for i := begin; i < end; i++ {
if (*cu)[i] >= id {
return i
}
}
return end
}
// cellUnionDifferenceInternal adds the difference between the CellID and the union to
// the result CellUnion. If they intersect but the difference is non-empty, it divides
// and conquers.
func (cu *CellUnion) cellUnionDifferenceInternal(id CellID, other *CellUnion) {
if !other.IntersectsCellID(id) {
(*cu) = append((*cu), id)
return
}
if !other.ContainsCellID(id) {
for _, child := range id.Children() {
cu.cellUnionDifferenceInternal(child, other)
}
}
}
// ExpandAtLevel expands this CellUnion by adding a rim of cells at expandLevel
// around the unions boundary.
//
// For each cell c in the union, we add all cells at level
// expandLevel that abut c. There are typically eight of those
// (four edge-abutting and four sharing a vertex). However, if c is
// finer than expandLevel, we add all cells abutting
// c.Parent(expandLevel) as well as c.Parent(expandLevel) itself,
// as an expandLevel cell rarely abuts a smaller cell.
//
// Note that the size of the output is exponential in
// expandLevel. For example, if expandLevel == 20 and the input
// has a cell at level 10, there will be on the order of 4000
// adjacent cells in the output. For most applications the
// ExpandByRadius method below is easier to use.
func (cu *CellUnion) ExpandAtLevel(level int) {
var output CellUnion
levelLsb := lsbForLevel(level)
for i := len(*cu) - 1; i >= 0; i-- {
id := (*cu)[i]
if id.lsb() < levelLsb {
id = id.Parent(level)
// Optimization: skip over any cells contained by this one. This is
// especially important when very small regions are being expanded.
for i > 0 && id.Contains((*cu)[i-1]) {
i--
}
}
output = append(output, id)
output = append(output, id.AllNeighbors(level)...)
}
sortCellIDs(output)
*cu = output
cu.Normalize()
}
// ExpandByRadius expands this CellUnion such that it contains all points whose
// distance to the CellUnion is at most minRadius, but do not use cells that
// are more than maxLevelDiff levels higher than the largest cell in the input.
// The second parameter controls the tradeoff between accuracy and output size
// when a large region is being expanded by a small amount (e.g. expanding Canada
// by 1km). For example, if maxLevelDiff == 4 the region will always be expanded
// by approximately 1/16 the width of its largest cell. Note that in the worst case,
// the number of cells in the output can be up to 4 * (1 + 2 ** maxLevelDiff) times
// larger than the number of cells in the input.
func (cu *CellUnion) ExpandByRadius(minRadius s1.Angle, maxLevelDiff int) {
minLevel := maxLevel
for _, cid := range *cu {
minLevel = minInt(minLevel, cid.Level())
}
// Find the maximum level such that all cells are at least "minRadius" wide.
radiusLevel := MinWidthMetric.MaxLevel(minRadius.Radians())
if radiusLevel == 0 && minRadius.Radians() > MinWidthMetric.Value(0) {
// The requested expansion is greater than the width of a face cell.
// The easiest way to handle this is to expand twice.
cu.ExpandAtLevel(0)
}
cu.ExpandAtLevel(minInt(minLevel+maxLevelDiff, radiusLevel))
}
// Equal reports whether the two CellUnions are equal.
func (cu CellUnion) Equal(o CellUnion) bool {
if len(cu) != len(o) {
return false
}
for i := 0; i < len(cu); i++ {
if cu[i] != o[i] {
return false
}
}
return true
}
// AverageArea returns the average area of this CellUnion.
// This is accurate to within a factor of 1.7.
func (cu *CellUnion) AverageArea() float64 {
return AvgAreaMetric.Value(maxLevel) * float64(cu.LeafCellsCovered())
}
// ApproxArea returns the approximate area of this CellUnion. This method is accurate
// to within 3% percent for all cell sizes and accurate to within 0.1% for cells
// at level 5 or higher within the union.
func (cu *CellUnion) ApproxArea() float64 {
var area float64
for _, id := range *cu {
area += CellFromCellID(id).ApproxArea()
}
return area
}
// ExactArea returns the area of this CellUnion as accurately as possible.
func (cu *CellUnion) ExactArea() float64 {
var area float64
for _, id := range *cu {
area += CellFromCellID(id).ExactArea()
}
return area
}
// Encode encodes the CellUnion.
func (cu *CellUnion) Encode(w io.Writer) error {
e := &encoder{w: w}
cu.encode(e)
return e.err
}
func (cu *CellUnion) encode(e *encoder) {
e.writeInt8(encodingVersion)
e.writeInt64(int64(len(*cu)))
for _, ci := range *cu {
ci.encode(e)
}
}
// Decode decodes the CellUnion.
func (cu *CellUnion) Decode(r io.Reader) error {
d := &decoder{r: asByteReader(r)}
cu.decode(d)
return d.err
}
func (cu *CellUnion) decode(d *decoder) {
version := d.readInt8()
if d.err != nil {
return
}
if version != encodingVersion {
d.err = fmt.Errorf("only version %d is supported", encodingVersion)
return
}
n := d.readInt64()
if d.err != nil {
return
}
const maxCells = 1000000
if n > maxCells {
d.err = fmt.Errorf("too many cells (%d; max is %d)", n, maxCells)
return
}
*cu = make([]CellID, n)
for i := range *cu {
(*cu)[i].decode(d)
}
}

View File

@ -1,133 +0,0 @@
// Copyright 2018 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"
"github.com/golang/geo/r3"
)
// There are several notions of the "centroid" of a triangle. First, there
// is the planar centroid, which is simply the centroid of the ordinary
// (non-spherical) triangle defined by the three vertices. Second, there is
// the surface centroid, which is defined as the intersection of the three
// medians of the spherical triangle. It is possible to show that this
// point is simply the planar centroid projected to the surface of the
// sphere. Finally, there is the true centroid (mass centroid), which is
// defined as the surface integral over the spherical triangle of (x,y,z)
// divided by the triangle area. This is the point that the triangle would
// rotate around if it was spinning in empty space.
//
// The best centroid for most purposes is the true centroid. Unlike the
// planar and surface centroids, the true centroid behaves linearly as
// regions are added or subtracted. That is, if you split a triangle into
// pieces and compute the average of their centroids (weighted by triangle
// area), the result equals the centroid of the original triangle. This is
// not true of the other centroids.
//
// Also note that the surface centroid may be nowhere near the intuitive
// "center" of a spherical triangle. For example, consider the triangle
// with vertices A=(1,eps,0), B=(0,0,1), C=(-1,eps,0) (a quarter-sphere).
// The surface centroid of this triangle is at S=(0, 2*eps, 1), which is
// within a distance of 2*eps of the vertex B. Note that the median from A
// (the segment connecting A to the midpoint of BC) passes through S, since
// this is the shortest path connecting the two endpoints. On the other
// hand, the true centroid is at M=(0, 0.5, 0.5), which when projected onto
// the surface is a much more reasonable interpretation of the "center" of
// this triangle.
//
// TrueCentroid returns the true centroid of the spherical triangle ABC
// multiplied by the signed area of spherical triangle ABC. The reasons for
// multiplying by the signed area are (1) this is the quantity that needs to be
// summed to compute the centroid of a union or difference of triangles, and
// (2) it's actually easier to calculate this way. All points must have unit length.
//
// Note that the result of this function is defined to be Point(0, 0, 0) if
// the triangle is degenerate.
func TrueCentroid(a, b, c Point) Point {
// Use Distance to get accurate results for small triangles.
ra := float64(1)
if sa := float64(b.Distance(c)); sa != 0 {
ra = sa / math.Sin(sa)
}
rb := float64(1)
if sb := float64(c.Distance(a)); sb != 0 {
rb = sb / math.Sin(sb)
}
rc := float64(1)
if sc := float64(a.Distance(b)); sc != 0 {
rc = sc / math.Sin(sc)
}
// Now compute a point M such that:
//
// [Ax Ay Az] [Mx] [ra]
// [Bx By Bz] [My] = 0.5 * det(A,B,C) * [rb]
// [Cx Cy Cz] [Mz] [rc]
//
// To improve the numerical stability we subtract the first row (A) from the
// other two rows; this reduces the cancellation error when A, B, and C are
// very close together. Then we solve it using Cramer's rule.
//
// The result is the true centroid of the triangle multiplied by the
// triangle's area.
//
// This code still isn't as numerically stable as it could be.
// The biggest potential improvement is to compute B-A and C-A more
// accurately so that (B-A)x(C-A) is always inside triangle ABC.
x := r3.Vector{a.X, b.X - a.X, c.X - a.X}
y := r3.Vector{a.Y, b.Y - a.Y, c.Y - a.Y}
z := r3.Vector{a.Z, b.Z - a.Z, c.Z - a.Z}
r := r3.Vector{ra, rb - ra, rc - ra}
return Point{r3.Vector{y.Cross(z).Dot(r), z.Cross(x).Dot(r), x.Cross(y).Dot(r)}.Mul(0.5)}
}
// EdgeTrueCentroid returns the true centroid of the spherical geodesic edge AB
// multiplied by the length of the edge AB. As with triangles, the true centroid
// of a collection of line segments may be computed simply by summing the result
// of this method for each segment.
//
// Note that the planar centroid of a line segment is simply 0.5 * (a + b),
// while the surface centroid is (a + b).Normalize(). However neither of
// these values is appropriate for computing the centroid of a collection of
// edges (such as a polyline).
//
// Also note that the result of this function is defined to be Point(0, 0, 0)
// if the edge is degenerate.
func EdgeTrueCentroid(a, b Point) Point {
// The centroid (multiplied by length) is a vector toward the midpoint
// of the edge, whose length is twice the sine of half the angle between
// the two vertices. Defining theta to be this angle, we have:
vDiff := a.Sub(b.Vector) // Length == 2*sin(theta)
vSum := a.Add(b.Vector) // Length == 2*cos(theta)
sin2 := vDiff.Norm2()
cos2 := vSum.Norm2()
if cos2 == 0 {
return Point{} // Ignore antipodal edges.
}
return Point{vSum.Mul(math.Sqrt(sin2 / cos2))} // Length == 2*sin(theta)
}
// PlanarCentroid returns the centroid of the planar triangle ABC. This can be
// normalized to unit length to obtain the "surface centroid" of the corresponding
// spherical triangle, i.e. the intersection of the three medians. However, note
// that for large spherical triangles the surface centroid may be nowhere near
// the intuitive "center".
func PlanarCentroid(a, b, c Point) Point {
return Point{a.Add(b.Vector).Add(c.Vector).Mul(1. / 3)}
}

View File

@ -1,190 +0,0 @@
// Copyright 2018 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
// VertexModel defines whether shapes are considered to contain their vertices.
// Note that these definitions differ from the ones used by BooleanOperation.
//
// Note that points other than vertices are never contained by polylines.
// If you want need this behavior, use ClosestEdgeQuery's IsDistanceLess
// with a suitable distance threshold instead.
type VertexModel int
const (
// VertexModelOpen means no shapes contain their vertices (not even
// points). Therefore Contains(Point) returns true if and only if the
// point is in the interior of some polygon.
VertexModelOpen VertexModel = iota
// VertexModelSemiOpen means that polygon point containment is defined
// such that if several polygons tile the region around a vertex, then
// exactly one of those polygons contains that vertex. Points and
// polylines still do not contain any vertices.
VertexModelSemiOpen
// VertexModelClosed means all shapes contain their vertices (including
// points and polylines).
VertexModelClosed
)
// ContainsPointQuery determines whether one or more shapes in a ShapeIndex
// contain a given Point. The ShapeIndex may contain any number of points,
// polylines, and/or polygons (possibly overlapping). Shape boundaries may be
// modeled as Open, SemiOpen, or Closed (this affects whether or not shapes are
// considered to contain their vertices).
//
// This type is not safe for concurrent use.
//
// However, note that if you need to do a large number of point containment
// tests, it is more efficient to re-use the query rather than creating a new
// one each time.
type ContainsPointQuery struct {
model VertexModel
index *ShapeIndex
iter *ShapeIndexIterator
}
// NewContainsPointQuery creates a new instance of the ContainsPointQuery for the index
// and given vertex model choice.
func NewContainsPointQuery(index *ShapeIndex, model VertexModel) *ContainsPointQuery {
return &ContainsPointQuery{
index: index,
model: model,
iter: index.Iterator(),
}
}
// Contains reports whether any shape in the queries index contains the point p
// under the queries vertex model (Open, SemiOpen, or Closed).
func (q *ContainsPointQuery) Contains(p Point) bool {
if !q.iter.LocatePoint(p) {
return false
}
cell := q.iter.IndexCell()
for _, clipped := range cell.shapes {
if q.shapeContains(clipped, q.iter.Center(), p) {
return true
}
}
return false
}
// shapeContains reports whether the clippedShape from the iterator's center position contains
// the given point.
func (q *ContainsPointQuery) shapeContains(clipped *clippedShape, center, p Point) bool {
inside := clipped.containsCenter
numEdges := clipped.numEdges()
if numEdges <= 0 {
return inside
}
shape := q.index.Shape(clipped.shapeID)
if shape.Dimension() != 2 {
// Points and polylines can be ignored unless the vertex model is Closed.
if q.model != VertexModelClosed {
return false
}
// Otherwise, the point is contained if and only if it matches a vertex.
for _, edgeID := range clipped.edges {
edge := shape.Edge(edgeID)
if edge.V0 == p || edge.V1 == p {
return true
}
}
return false
}
// Test containment by drawing a line segment from the cell center to the
// given point and counting edge crossings.
crosser := NewEdgeCrosser(center, p)
for _, edgeID := range clipped.edges {
edge := shape.Edge(edgeID)
sign := crosser.CrossingSign(edge.V0, edge.V1)
if sign == DoNotCross {
continue
}
if sign == MaybeCross {
// For the Open and Closed models, check whether p is a vertex.
if q.model != VertexModelSemiOpen && (edge.V0 == p || edge.V1 == p) {
return (q.model == VertexModelClosed)
}
// C++ plays fast and loose with the int <-> bool conversions here.
if VertexCrossing(crosser.a, crosser.b, edge.V0, edge.V1) {
sign = Cross
} else {
sign = DoNotCross
}
}
inside = inside != (sign == Cross)
}
return inside
}
// ShapeContains reports whether the given shape contains the point under this
// queries vertex model (Open, SemiOpen, or Closed).
//
// This requires the shape belongs to this queries index.
func (q *ContainsPointQuery) ShapeContains(shape Shape, p Point) bool {
if !q.iter.LocatePoint(p) {
return false
}
clipped := q.iter.IndexCell().findByShapeID(q.index.idForShape(shape))
if clipped == nil {
return false
}
return q.shapeContains(clipped, q.iter.Center(), p)
}
// shapeVisitorFunc is a type of function that can be called against shaped in an index.
type shapeVisitorFunc func(shape Shape) bool
// visitContainingShapes visits all shapes in the given index that contain the
// given point p, terminating early if the given visitor function returns false,
// in which case visitContainingShapes returns false. Each shape is
// visited at most once.
func (q *ContainsPointQuery) visitContainingShapes(p Point, f shapeVisitorFunc) bool {
// This function returns false only if the algorithm terminates early
// because the visitor function returned false.
if !q.iter.LocatePoint(p) {
return true
}
cell := q.iter.IndexCell()
for _, clipped := range cell.shapes {
if q.shapeContains(clipped, q.iter.Center(), p) &&
!f(q.index.Shape(clipped.shapeID)) {
return false
}
}
return true
}
// ContainingShapes returns a slice of all shapes that contain the given point.
func (q *ContainsPointQuery) ContainingShapes(p Point) []Shape {
var shapes []Shape
q.visitContainingShapes(p, func(shape Shape) bool {
shapes = append(shapes, shape)
return true
})
return shapes
}
// TODO(roberts): Remaining methods from C++
// type edgeVisitorFunc func(shape ShapeEdge) bool
// func (q *ContainsPointQuery) visitIncidentEdges(p Point, v edgeVisitorFunc) bool

View File

@ -1,63 +0,0 @@
// 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
// ContainsVertexQuery is used to track the edges entering and leaving the
// given vertex of a Polygon in order to be able to determine if the point is
// contained by the Polygon.
//
// Point containment is defined according to the semi-open boundary model
// which means that if several polygons tile the region around a vertex,
// then exactly one of those polygons contains that vertex.
type ContainsVertexQuery struct {
target Point
edgeMap map[Point]int
}
// NewContainsVertexQuery returns a new query for the given vertex whose
// containment will be determined.
func NewContainsVertexQuery(target Point) *ContainsVertexQuery {
return &ContainsVertexQuery{
target: target,
edgeMap: make(map[Point]int),
}
}
// AddEdge adds the edge between target and v with the given direction.
// (+1 = outgoing, -1 = incoming, 0 = degenerate).
func (q *ContainsVertexQuery) AddEdge(v Point, direction int) {
q.edgeMap[v] += direction
}
// ContainsVertex reports a +1 if the target vertex is contained, -1 if it is
// not contained, and 0 if the incident edges consisted of matched sibling pairs.
func (q *ContainsVertexQuery) ContainsVertex() int {
// Find the unmatched edge that is immediately clockwise from Ortho(P).
referenceDir := Point{q.target.Ortho()}
bestPoint := referenceDir
bestDir := 0
for k, v := range q.edgeMap {
if v == 0 {
continue // This is a "matched" edge.
}
if OrderedCCW(referenceDir, bestPoint, k, q.target) {
bestPoint = k
bestDir = v
}
}
return bestDir
}

View File

@ -1,258 +0,0 @@
// Copyright 2018 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 (
"sort"
"github.com/golang/geo/r3"
)
// ConvexHullQuery builds the convex hull of any collection of points,
// polylines, loops, and polygons. It returns a single convex loop.
//
// The convex hull is defined as the smallest convex region on the sphere that
// contains all of your input geometry. Recall that a region is "convex" if
// for every pair of points inside the region, the straight edge between them
// is also inside the region. In our case, a "straight" edge is a geodesic,
// i.e. the shortest path on the sphere between two points.
//
// Containment of input geometry is defined as follows:
//
// - Each input loop and polygon is contained by the convex hull exactly
// (i.e., according to Polygon's Contains(Polygon)).
//
// - Each input point is either contained by the convex hull or is a vertex
// of the convex hull. (Recall that S2Loops do not necessarily contain their
// vertices.)
//
// - For each input polyline, the convex hull contains all of its vertices
// according to the rule for points above. (The definition of convexity
// then ensures that the convex hull also contains the polyline edges.)
//
// To use this type, call the various Add... methods to add your input geometry, and
// then call ConvexHull. Note that ConvexHull does *not* reset the
// state; you can continue adding geometry if desired and compute the convex
// hull again. If you want to start from scratch, simply create a new
// ConvexHullQuery value.
//
// This implement Andrew's monotone chain algorithm, which is a variant of the
// Graham scan (see https://en.wikipedia.org/wiki/Graham_scan). The time
// complexity is O(n log n), and the space required is O(n). In fact only the
// call to "sort" takes O(n log n) time; the rest of the algorithm is linear.
//
// Demonstration of the algorithm and code:
// en.wikibooks.org/wiki/Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain
//
// This type is not safe for concurrent use.
type ConvexHullQuery struct {
bound Rect
points []Point
}
// NewConvexHullQuery creates a new ConvexHullQuery.
func NewConvexHullQuery() *ConvexHullQuery {
return &ConvexHullQuery{
bound: EmptyRect(),
}
}
// AddPoint adds the given point to the input geometry.
func (q *ConvexHullQuery) AddPoint(p Point) {
q.bound = q.bound.AddPoint(LatLngFromPoint(p))
q.points = append(q.points, p)
}
// AddPolyline adds the given polyline to the input geometry.
func (q *ConvexHullQuery) AddPolyline(p *Polyline) {
q.bound = q.bound.Union(p.RectBound())
q.points = append(q.points, (*p)...)
}
// AddLoop adds the given loop to the input geometry.
func (q *ConvexHullQuery) AddLoop(l *Loop) {
q.bound = q.bound.Union(l.RectBound())
if l.isEmptyOrFull() {
return
}
q.points = append(q.points, l.vertices...)
}
// AddPolygon adds the given polygon to the input geometry.
func (q *ConvexHullQuery) AddPolygon(p *Polygon) {
q.bound = q.bound.Union(p.RectBound())
for _, l := range p.loops {
// Only loops at depth 0 can contribute to the convex hull.
if l.depth == 0 {
q.AddLoop(l)
}
}
}
// CapBound returns a bounding cap for the input geometry provided.
//
// Note that this method does not clear the geometry; you can continue
// adding to it and call this method again if desired.
func (q *ConvexHullQuery) CapBound() Cap {
// We keep track of a rectangular bound rather than a spherical cap because
// it is easy to compute a tight bound for a union of rectangles, whereas it
// is quite difficult to compute a tight bound around a union of caps.
// Also, polygons and polylines implement CapBound() in terms of
// RectBound() for this same reason, so it is much better to keep track
// of a rectangular bound as we go along and convert it at the end.
//
// TODO(roberts): We could compute an optimal bound by implementing Welzl's
// algorithm. However we would still need to have special handling of loops
// and polygons, since if a loop spans more than 180 degrees in any
// direction (i.e., if it contains two antipodal points), then it is not
// enough just to bound its vertices. In this case the only convex bounding
// cap is FullCap(), and the only convex bounding loop is the full loop.
return q.bound.CapBound()
}
// ConvexHull returns a Loop representing the convex hull of the input geometry provided.
//
// If there is no geometry, this method returns an empty loop containing no
// points.
//
// If the geometry spans more than half of the sphere, this method returns a
// full loop containing the entire sphere.
//
// If the geometry contains 1 or 2 points, or a single edge, this method
// returns a very small loop consisting of three vertices (which are a
// superset of the input vertices).
//
// Note that this method does not clear the geometry; you can continue
// adding to the query and call this method again.
func (q *ConvexHullQuery) ConvexHull() *Loop {
c := q.CapBound()
if c.Height() >= 1 {
// The bounding cap is not convex. The current bounding cap
// implementation is not optimal, but nevertheless it is likely that the
// input geometry itself is not contained by any convex polygon. In any
// case, we need a convex bounding cap to proceed with the algorithm below
// (in order to construct a point "origin" that is definitely outside the
// convex hull).
return FullLoop()
}
// Remove duplicates. We need to do this before checking whether there are
// fewer than 3 points.
x := make(map[Point]bool)
r, w := 0, 0 // read/write indexes
for ; r < len(q.points); r++ {
if x[q.points[r]] {
continue
}
q.points[w] = q.points[r]
x[q.points[r]] = true
w++
}
q.points = q.points[:w]
// This code implements Andrew's monotone chain algorithm, which is a simple
// variant of the Graham scan. Rather than sorting by x-coordinate, instead
// we sort the points in CCW order around an origin O such that all points
// are guaranteed to be on one side of some geodesic through O. This
// ensures that as we scan through the points, each new point can only
// belong at the end of the chain (i.e., the chain is monotone in terms of
// the angle around O from the starting point).
origin := Point{c.Center().Ortho()}
sort.Slice(q.points, func(i, j int) bool {
return RobustSign(origin, q.points[i], q.points[j]) == CounterClockwise
})
// Special cases for fewer than 3 points.
switch len(q.points) {
case 0:
return EmptyLoop()
case 1:
return singlePointLoop(q.points[0])
case 2:
return singleEdgeLoop(q.points[0], q.points[1])
}
// Generate the lower and upper halves of the convex hull. Each half
// consists of the maximal subset of vertices such that the edge chain
// makes only left (CCW) turns.
lower := q.monotoneChain()
// reverse the points
for left, right := 0, len(q.points)-1; left < right; left, right = left+1, right-1 {
q.points[left], q.points[right] = q.points[right], q.points[left]
}
upper := q.monotoneChain()
// Remove the duplicate vertices and combine the chains.
lower = lower[:len(lower)-1]
upper = upper[:len(upper)-1]
lower = append(lower, upper...)
return LoopFromPoints(lower)
}
// monotoneChain iterates through the points, selecting the maximal subset of points
// such that the edge chain makes only left (CCW) turns.
func (q *ConvexHullQuery) monotoneChain() []Point {
var output []Point
for _, p := range q.points {
// Remove any points that would cause the chain to make a clockwise turn.
for len(output) >= 2 && RobustSign(output[len(output)-2], output[len(output)-1], p) != CounterClockwise {
output = output[:len(output)-1]
}
output = append(output, p)
}
return output
}
// singlePointLoop constructs a 3-vertex polygon consisting of "p" and two nearby
// vertices. Note that ContainsPoint(p) may be false for the resulting loop.
func singlePointLoop(p Point) *Loop {
const offset = 1e-15
d0 := p.Ortho()
d1 := p.Cross(d0)
vertices := []Point{
p,
{p.Add(d0.Mul(offset)).Normalize()},
{p.Add(d1.Mul(offset)).Normalize()},
}
return LoopFromPoints(vertices)
}
// singleEdgeLoop constructs a loop consisting of the two vertices and their midpoint.
func singleEdgeLoop(a, b Point) *Loop {
// If the points are exactly antipodal we return the full loop.
//
// Note that we could use the code below even in this case (which would
// return a zero-area loop that follows the edge AB), except that (1) the
// direction of AB is defined using symbolic perturbations and therefore is
// not predictable by ordinary users, and (2) Loop disallows anitpodal
// adjacent vertices and so we would need to use 4 vertices to define the
// degenerate loop. (Note that the Loop antipodal vertex restriction is
// historical and now could easily be removed, however it would still have
// the problem that the edge direction is not easily predictable.)
if a.Add(b.Vector) == (r3.Vector{}) {
return FullLoop()
}
// Construct a loop consisting of the two vertices and their midpoint. We
// use Interpolate() to ensure that the midpoint is very close to
// the edge even when its endpoints nearly antipodal.
vertices := []Point{a, b, Interpolate(0.5, a, b)}
loop := LoopFromPoints(vertices)
// The resulting loop may be clockwise, so invert it if necessary.
loop.Normalize()
return loop
}

View File

@ -1,409 +0,0 @@
// 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
import (
"sort"
"github.com/golang/geo/r2"
)
// CrossingEdgeQuery is used to find the Edge IDs of Shapes that are crossed by
// a given edge(s).
//
// Note that if you need to query many edges, it is more efficient to declare
// a single CrossingEdgeQuery instance and reuse it.
//
// If you want to find *all* the pairs of crossing edges, it is more efficient to
// use the not yet implemented VisitCrossings in shapeutil.
type CrossingEdgeQuery struct {
index *ShapeIndex
// temporary values used while processing a query.
a, b r2.Point
iter *ShapeIndexIterator
// candidate cells generated when finding crossings.
cells []*ShapeIndexCell
}
// NewCrossingEdgeQuery creates a CrossingEdgeQuery for the given index.
func NewCrossingEdgeQuery(index *ShapeIndex) *CrossingEdgeQuery {
c := &CrossingEdgeQuery{
index: index,
iter: index.Iterator(),
}
return c
}
// Crossings returns the set of edge of the shape S that intersect the given edge AB.
// If the CrossingType is Interior, then only intersections at a point interior to both
// edges are reported, while if it is CrossingTypeAll then edges that share a vertex
// are also reported.
func (c *CrossingEdgeQuery) Crossings(a, b Point, shape Shape, crossType CrossingType) []int {
edges := c.candidates(a, b, shape)
if len(edges) == 0 {
return nil
}
crosser := NewEdgeCrosser(a, b)
out := 0
n := len(edges)
for in := 0; in < n; in++ {
b := shape.Edge(edges[in])
sign := crosser.CrossingSign(b.V0, b.V1)
if crossType == CrossingTypeAll && (sign == MaybeCross || sign == Cross) || crossType != CrossingTypeAll && sign == Cross {
edges[out] = edges[in]
out++
}
}
if out < n {
edges = edges[0:out]
}
return edges
}
// EdgeMap stores a sorted set of edge ids for each shape.
type EdgeMap map[Shape][]int
// CrossingsEdgeMap returns the set of all edges in the index that intersect the given
// edge AB. If crossType is CrossingTypeInterior, then only intersections at a
// point interior to both edges are reported, while if it is CrossingTypeAll
// then edges that share a vertex are also reported.
//
// The edges are returned as a mapping from shape to the edges of that shape
// that intersect AB. Every returned shape has at least one crossing edge.
func (c *CrossingEdgeQuery) CrossingsEdgeMap(a, b Point, crossType CrossingType) EdgeMap {
edgeMap := c.candidatesEdgeMap(a, b)
if len(edgeMap) == 0 {
return nil
}
crosser := NewEdgeCrosser(a, b)
for shape, edges := range edgeMap {
out := 0
n := len(edges)
for in := 0; in < n; in++ {
edge := shape.Edge(edges[in])
sign := crosser.CrossingSign(edge.V0, edge.V1)
if (crossType == CrossingTypeAll && (sign == MaybeCross || sign == Cross)) || (crossType != CrossingTypeAll && sign == Cross) {
edgeMap[shape][out] = edges[in]
out++
}
}
if out == 0 {
delete(edgeMap, shape)
} else {
if out < n {
edgeMap[shape] = edgeMap[shape][0:out]
}
}
}
return edgeMap
}
// candidates returns a superset of the edges of the given shape that intersect
// the edge AB.
func (c *CrossingEdgeQuery) candidates(a, b Point, shape Shape) []int {
var edges []int
// For small loops it is faster to use brute force. The threshold below was
// determined using benchmarks.
const maxBruteForceEdges = 27
maxEdges := shape.NumEdges()
if maxEdges <= maxBruteForceEdges {
edges = make([]int, maxEdges)
for i := 0; i < maxEdges; i++ {
edges[i] = i
}
return edges
}
// Compute the set of index cells intersected by the query edge.
c.getCellsForEdge(a, b)
if len(c.cells) == 0 {
return nil
}
// Gather all the edges that intersect those cells and sort them.
// TODO(roberts): Shapes don't track their ID, so we need to range over
// the index to find the ID manually.
var shapeID int32
for k, v := range c.index.shapes {
if v == shape {
shapeID = k
}
}
for _, cell := range c.cells {
if cell == nil {
continue
}
clipped := cell.findByShapeID(shapeID)
if clipped == nil {
continue
}
edges = append(edges, clipped.edges...)
}
if len(c.cells) > 1 {
edges = uniqueInts(edges)
}
return edges
}
// uniqueInts returns the sorted uniqued values from the given input.
func uniqueInts(in []int) []int {
var edges []int
m := make(map[int]bool)
for _, i := range in {
if m[i] {
continue
}
m[i] = true
edges = append(edges, i)
}
sort.Ints(edges)
return edges
}
// candidatesEdgeMap returns a map from shapes to the superse of edges for that
// shape that intersect the edge AB.
//
// CAVEAT: This method may return shapes that have an empty set of candidate edges.
// However the return value is non-empty only if at least one shape has a candidate edge.
func (c *CrossingEdgeQuery) candidatesEdgeMap(a, b Point) EdgeMap {
edgeMap := make(EdgeMap)
// If there are only a few edges then it's faster to use brute force. We
// only bother with this optimization when there is a single shape.
if len(c.index.shapes) == 1 {
// Typically this method is called many times, so it is worth checking
// whether the edge map is empty or already consists of a single entry for
// this shape, and skip clearing edge map in that case.
shape := c.index.Shape(0)
// Note that we leave the edge map non-empty even if there are no candidates
// (i.e., there is a single entry with an empty set of edges).
edgeMap[shape] = c.candidates(a, b, shape)
return edgeMap
}
// Compute the set of index cells intersected by the query edge.
c.getCellsForEdge(a, b)
if len(c.cells) == 0 {
return edgeMap
}
// Gather all the edges that intersect those cells and sort them.
for _, cell := range c.cells {
for _, clipped := range cell.shapes {
s := c.index.Shape(clipped.shapeID)
for j := 0; j < clipped.numEdges(); j++ {
edgeMap[s] = append(edgeMap[s], clipped.edges[j])
}
}
}
if len(c.cells) > 1 {
for s, edges := range edgeMap {
edgeMap[s] = uniqueInts(edges)
}
}
return edgeMap
}
// getCells returns the set of ShapeIndexCells that might contain edges intersecting
// the edge AB in the given cell root. This method is used primarily by loop and shapeutil.
func (c *CrossingEdgeQuery) getCells(a, b Point, root *PaddedCell) []*ShapeIndexCell {
aUV, bUV, ok := ClipToFace(a, b, root.id.Face())
if ok {
c.a = aUV
c.b = bUV
edgeBound := r2.RectFromPoints(c.a, c.b)
if root.Bound().Intersects(edgeBound) {
c.computeCellsIntersected(root, edgeBound)
}
}
if len(c.cells) == 0 {
return nil
}
return c.cells
}
// getCellsForEdge populates the cells field to the set of index cells intersected by an edge AB.
func (c *CrossingEdgeQuery) getCellsForEdge(a, b Point) {
c.cells = nil
segments := FaceSegments(a, b)
for _, segment := range segments {
c.a = segment.a
c.b = segment.b
// Optimization: rather than always starting the recursive subdivision at
// the top level face cell, instead we start at the smallest S2CellId that
// contains the edge (the edge root cell). This typically lets us skip
// quite a few levels of recursion since most edges are short.
edgeBound := r2.RectFromPoints(c.a, c.b)
pcell := PaddedCellFromCellID(CellIDFromFace(segment.face), 0)
edgeRoot := pcell.ShrinkToFit(edgeBound)
// Now we need to determine how the edge root cell is related to the cells
// in the spatial index (cellMap). There are three cases:
//
// 1. edgeRoot is an index cell or is contained within an index cell.
// In this case we only need to look at the contents of that cell.
// 2. edgeRoot is subdivided into one or more index cells. In this case
// we recursively subdivide to find the cells intersected by AB.
// 3. edgeRoot does not intersect any index cells. In this case there
// is nothing to do.
relation := c.iter.LocateCellID(edgeRoot)
if relation == Indexed {
// edgeRoot is an index cell or is contained by an index cell (case 1).
c.cells = append(c.cells, c.iter.IndexCell())
} else if relation == Subdivided {
// edgeRoot is subdivided into one or more index cells (case 2). We
// find the cells intersected by AB using recursive subdivision.
if !edgeRoot.isFace() {
pcell = PaddedCellFromCellID(edgeRoot, 0)
}
c.computeCellsIntersected(pcell, edgeBound)
}
}
}
// computeCellsIntersected computes the index cells intersected by the current
// edge that are descendants of pcell and adds them to this queries set of cells.
func (c *CrossingEdgeQuery) computeCellsIntersected(pcell *PaddedCell, edgeBound r2.Rect) {
c.iter.seek(pcell.id.RangeMin())
if c.iter.Done() || c.iter.CellID() > pcell.id.RangeMax() {
// The index does not contain pcell or any of its descendants.
return
}
if c.iter.CellID() == pcell.id {
// The index contains this cell exactly.
c.cells = append(c.cells, c.iter.IndexCell())
return
}
// Otherwise, split the edge among the four children of pcell.
center := pcell.Middle().Lo()
if edgeBound.X.Hi < center.X {
// Edge is entirely contained in the two left children.
c.clipVAxis(edgeBound, center.Y, 0, pcell)
return
} else if edgeBound.X.Lo >= center.X {
// Edge is entirely contained in the two right children.
c.clipVAxis(edgeBound, center.Y, 1, pcell)
return
}
childBounds := c.splitUBound(edgeBound, center.X)
if edgeBound.Y.Hi < center.Y {
// Edge is entirely contained in the two lower children.
c.computeCellsIntersected(PaddedCellFromParentIJ(pcell, 0, 0), childBounds[0])
c.computeCellsIntersected(PaddedCellFromParentIJ(pcell, 1, 0), childBounds[1])
} else if edgeBound.Y.Lo >= center.Y {
// Edge is entirely contained in the two upper children.
c.computeCellsIntersected(PaddedCellFromParentIJ(pcell, 0, 1), childBounds[0])
c.computeCellsIntersected(PaddedCellFromParentIJ(pcell, 1, 1), childBounds[1])
} else {
// The edge bound spans all four children. The edge itself intersects
// at most three children (since no padding is being used).
c.clipVAxis(childBounds[0], center.Y, 0, pcell)
c.clipVAxis(childBounds[1], center.Y, 1, pcell)
}
}
// clipVAxis computes the intersected cells recursively for a given padded cell.
// Given either the left (i=0) or right (i=1) side of a padded cell pcell,
// determine whether the current edge intersects the lower child, upper child,
// or both children, and call c.computeCellsIntersected recursively on those children.
// The center is the v-coordinate at the center of pcell.
func (c *CrossingEdgeQuery) clipVAxis(edgeBound r2.Rect, center float64, i int, pcell *PaddedCell) {
if edgeBound.Y.Hi < center {
// Edge is entirely contained in the lower child.
c.computeCellsIntersected(PaddedCellFromParentIJ(pcell, i, 0), edgeBound)
} else if edgeBound.Y.Lo >= center {
// Edge is entirely contained in the upper child.
c.computeCellsIntersected(PaddedCellFromParentIJ(pcell, i, 1), edgeBound)
} else {
// The edge intersects both children.
childBounds := c.splitVBound(edgeBound, center)
c.computeCellsIntersected(PaddedCellFromParentIJ(pcell, i, 0), childBounds[0])
c.computeCellsIntersected(PaddedCellFromParentIJ(pcell, i, 1), childBounds[1])
}
}
// splitUBound returns the bound for two children as a result of spliting the
// current edge at the given value U.
func (c *CrossingEdgeQuery) splitUBound(edgeBound r2.Rect, u float64) [2]r2.Rect {
v := edgeBound.Y.ClampPoint(interpolateFloat64(u, c.a.X, c.b.X, c.a.Y, c.b.Y))
// diag indicates which diagonal of the bounding box is spanned by AB:
// it is 0 if AB has positive slope, and 1 if AB has negative slope.
var diag int
if (c.a.X > c.b.X) != (c.a.Y > c.b.Y) {
diag = 1
}
return splitBound(edgeBound, 0, diag, u, v)
}
// splitVBound returns the bound for two children as a result of spliting the
// current edge into two child edges at the given value V.
func (c *CrossingEdgeQuery) splitVBound(edgeBound r2.Rect, v float64) [2]r2.Rect {
u := edgeBound.X.ClampPoint(interpolateFloat64(v, c.a.Y, c.b.Y, c.a.X, c.b.X))
var diag int
if (c.a.X > c.b.X) != (c.a.Y > c.b.Y) {
diag = 1
}
return splitBound(edgeBound, diag, 0, u, v)
}
// splitBound returns the bounds for the two childrenn as a result of spliting
// the current edge into two child edges at the given point (u,v). uEnd and vEnd
// indicate which bound endpoints of the first child will be updated.
func splitBound(edgeBound r2.Rect, uEnd, vEnd int, u, v float64) [2]r2.Rect {
var childBounds = [2]r2.Rect{
edgeBound,
edgeBound,
}
if uEnd == 1 {
childBounds[0].X.Lo = u
childBounds[1].X.Hi = u
} else {
childBounds[0].X.Hi = u
childBounds[1].X.Lo = u
}
if vEnd == 1 {
childBounds[0].Y.Lo = v
childBounds[1].Y.Hi = v
} else {
childBounds[0].Y.Hi = v
childBounds[1].Y.Lo = v
}
return childBounds
}

View File

@ -1,149 +0,0 @@
// Copyright 2019 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 (
"github.com/golang/geo/s1"
)
// The distance interface represents a set of common methods used by algorithms
// that compute distances between various S2 types.
type distance interface {
// chordAngle returns this type as a ChordAngle.
chordAngle() s1.ChordAngle
// fromChordAngle is used to type convert a ChordAngle to this type.
// This is to work around needing to be clever in parts of the code
// where a distanceTarget interface method expects distances, but the
// user only supplies a ChordAngle, and we need to dynamically cast it
// to an appropriate distance interface types.
fromChordAngle(o s1.ChordAngle) distance
// zero returns a zero distance.
zero() distance
// negative returns a value smaller than any valid value.
negative() distance
// infinity returns a value larger than any valid value.
infinity() distance
// less is similar to the Less method in Sort. To get minimum values,
// this would be a less than type operation. For maximum, this would
// be a greater than type operation.
less(other distance) bool
// sub subtracts the other value from this one and returns the new value.
// This is done as a method and not simple mathematical operation to
// allow closest and furthest to implement this in opposite ways.
sub(other distance) distance
// chordAngleBound reports the upper bound on a ChordAngle corresponding
// to this distance. For example, if distance measures WGS84 ellipsoid
// distance then the corresponding angle needs to be 0.56% larger.
chordAngleBound() s1.ChordAngle
// updateDistance may update the value this distance represents
// based on the given input. The updated value and a boolean reporting
// if the value was changed are returned.
updateDistance(other distance) (distance, bool)
}
// distanceTarget is an interface that represents a geometric type to which distances
// are measured.
//
// For example, there are implementations that measure distances to a Point,
// an Edge, a Cell, a CellUnion, and even to an arbitrary collection of geometry
// stored in ShapeIndex.
//
// The distanceTarget types are provided for the benefit of types that measure
// distances and/or find nearby geometry, such as ClosestEdgeQuery, FurthestEdgeQuery,
// ClosestPointQuery, and ClosestCellQuery, etc.
type distanceTarget interface {
// capBound returns a Cap that bounds the set of points whose distance to the
// target is distance.zero().
capBound() Cap
// updateDistanceToPoint updates the distance if the distance to
// the point P is within than the given dist.
// The boolean reports if the value was updated.
updateDistanceToPoint(p Point, dist distance) (distance, bool)
// updateDistanceToEdge updates the distance if the distance to
// the edge E is within than the given dist.
// The boolean reports if the value was updated.
updateDistanceToEdge(e Edge, dist distance) (distance, bool)
// updateDistanceToCell updates the distance if the distance to the cell C
// (including its interior) is within than the given dist.
// The boolean reports if the value was updated.
updateDistanceToCell(c Cell, dist distance) (distance, bool)
// setMaxError potentially updates the value of MaxError, and reports if
// the specific type supports altering it. Whenever one of the
// updateDistanceTo... methods above returns true, the returned distance
// is allowed to be up to maxError larger than the true minimum distance.
// In other words, it gives this target object permission to terminate its
// distance calculation as soon as it has determined that (1) the minimum
// distance is less than minDist and (2) the best possible further
// improvement is less than maxError.
//
// If the target takes advantage of maxError to optimize its distance
// calculation, this method must return true. (Most target types will
// default to return false.)
setMaxError(maxErr s1.ChordAngle) bool
// maxBruteForceIndexSize reports the maximum number of indexed objects for
// which it is faster to compute the distance by brute force (e.g., by testing
// every edge) rather than by using an index.
//
// The following method is provided as a convenience for types that compute
// distances to a collection of indexed geometry, such as ClosestEdgeQuery
// and ClosestPointQuery.
//
// Types that do not support this should return a -1.
maxBruteForceIndexSize() int
// distance returns an instance of the underlying distance type this
// target uses. This is to work around the use of Templates in the C++.
distance() distance
// visitContainingShapes finds all polygons in the given index that
// completely contain a connected component of the target geometry. (For
// example, if the target consists of 10 points, this method finds
// polygons that contain any of those 10 points.) For each such polygon,
// the visit function is called with the Shape of the polygon along with
// a point of the target geometry that is contained by that polygon.
//
// Optionally, any polygon that intersects the target geometry may also be
// returned. In other words, this method returns all polygons that
// contain any connected component of the target, along with an arbitrary
// subset of the polygons that intersect the target.
//
// For example, suppose that the index contains two abutting polygons
// A and B. If the target consists of two points "a" contained by A and
// "b" contained by B, then both A and B are returned. But if the target
// consists of the edge "ab", then any subset of {A, B} could be returned
// (because both polygons intersect the target but neither one contains
// the edge "ab").
//
// If the visit function returns false, this method terminates early and
// returns false as well. Otherwise returns true.
//
// NOTE(roberts): This method exists only for the purpose of implementing
// edgeQuery IncludeInteriors efficiently.
visitContainingShapes(index *ShapeIndex, v shapePointVisitorFunc) bool
}
// shapePointVisitorFunc defines a type of function the visitContainingShapes can call.
type shapePointVisitorFunc func(containingShape Shape, targetPoint Point) bool

View File

@ -1,29 +0,0 @@
// Copyright 2014 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 is a library for working with geometry in S² (spherical geometry).
Its related packages, parallel to this one, are s1 (operates on S¹), r1 (operates on ℝ¹),
r2 (operates on ℝ²) and r3 (operates on ℝ³).
This package provides types and functions for the S2 cell hierarchy and coordinate systems.
The S2 cell hierarchy is a hierarchical decomposition of the surface of a unit sphere (S²)
into ``cells''; it is highly efficient, scales from continental size to under 1 cm²
and preserves spatial locality (nearby cells have close IDs).
More information including an in-depth introduction to S2 can be found on the
S2 website https://s2geometry.io/
*/
package s2

View File

@ -1,672 +0,0 @@
// 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
// This file contains a collection of methods for:
//
// (1) Robustly clipping geodesic edges to the faces of the S2 biunit cube
// (see s2stuv), and
//
// (2) Robustly clipping 2D edges against 2D rectangles.
//
// These functions can be used to efficiently find the set of CellIDs that
// are intersected by a geodesic edge (e.g., see CrossingEdgeQuery).
import (
"math"
"github.com/golang/geo/r1"
"github.com/golang/geo/r2"
"github.com/golang/geo/r3"
)
const (
// edgeClipErrorUVCoord is the maximum error in a u- or v-coordinate
// compared to the exact result, assuming that the points A and B are in
// the rectangle [-1,1]x[1,1] or slightly outside it (by 1e-10 or less).
edgeClipErrorUVCoord = 2.25 * dblEpsilon
// edgeClipErrorUVDist is the maximum distance from a clipped point to
// the corresponding exact result. It is equal to the error in a single
// coordinate because at most one coordinate is subject to error.
edgeClipErrorUVDist = 2.25 * dblEpsilon
// faceClipErrorRadians is the maximum angle between a returned vertex
// and the nearest point on the exact edge AB. It is equal to the
// maximum directional error in PointCross, plus the error when
// projecting points onto a cube face.
faceClipErrorRadians = 3 * dblEpsilon
// faceClipErrorDist is the same angle expressed as a maximum distance
// in (u,v)-space. In other words, a returned vertex is at most this far
// from the exact edge AB projected into (u,v)-space.
faceClipErrorUVDist = 9 * dblEpsilon
// faceClipErrorUVCoord is the maximum angle between a returned vertex
// and the nearest point on the exact edge AB expressed as the maximum error
// in an individual u- or v-coordinate. In other words, for each
// returned vertex there is a point on the exact edge AB whose u- and
// v-coordinates differ from the vertex by at most this amount.
faceClipErrorUVCoord = 9.0 * (1.0 / math.Sqrt2) * dblEpsilon
// intersectsRectErrorUVDist is the maximum error when computing if a point
// intersects with a given Rect. If some point of AB is inside the
// rectangle by at least this distance, the result is guaranteed to be true;
// if all points of AB are outside the rectangle by at least this distance,
// the result is guaranteed to be false. This bound assumes that rect is
// a subset of the rectangle [-1,1]x[-1,1] or extends slightly outside it
// (e.g., by 1e-10 or less).
intersectsRectErrorUVDist = 3 * math.Sqrt2 * dblEpsilon
)
// ClipToFace returns the (u,v) coordinates for the portion of the edge AB that
// intersects the given face, or false if the edge AB does not intersect.
// This method guarantees that the clipped vertices lie within the [-1,1]x[-1,1]
// cube face rectangle and are within faceClipErrorUVDist of the line AB, but
// the results may differ from those produced by FaceSegments.
func ClipToFace(a, b Point, face int) (aUV, bUV r2.Point, intersects bool) {
return ClipToPaddedFace(a, b, face, 0.0)
}
// ClipToPaddedFace returns the (u,v) coordinates for the portion of the edge AB that
// intersects the given face, but rather than clipping to the square [-1,1]x[-1,1]
// in (u,v) space, this method clips to [-R,R]x[-R,R] where R=(1+padding).
// Padding must be non-negative.
func ClipToPaddedFace(a, b Point, f int, padding float64) (aUV, bUV r2.Point, intersects bool) {
// Fast path: both endpoints are on the given face.
if face(a.Vector) == f && face(b.Vector) == f {
au, av := validFaceXYZToUV(f, a.Vector)
bu, bv := validFaceXYZToUV(f, b.Vector)
return r2.Point{au, av}, r2.Point{bu, bv}, true
}
// Convert everything into the (u,v,w) coordinates of the given face. Note
// that the cross product *must* be computed in the original (x,y,z)
// coordinate system because PointCross (unlike the mathematical cross
// product) can produce different results in different coordinate systems
// when one argument is a linear multiple of the other, due to the use of
// symbolic perturbations.
normUVW := pointUVW(faceXYZtoUVW(f, a.PointCross(b)))
aUVW := pointUVW(faceXYZtoUVW(f, a))
bUVW := pointUVW(faceXYZtoUVW(f, b))
// Padding is handled by scaling the u- and v-components of the normal.
// Letting R=1+padding, this means that when we compute the dot product of
// the normal with a cube face vertex (such as (-1,-1,1)), we will actually
// compute the dot product with the scaled vertex (-R,-R,1). This allows
// methods such as intersectsFace, exitAxis, etc, to handle padding
// with no further modifications.
scaleUV := 1 + padding
scaledN := pointUVW{r3.Vector{X: scaleUV * normUVW.X, Y: scaleUV * normUVW.Y, Z: normUVW.Z}}
if !scaledN.intersectsFace() {
return aUV, bUV, false
}
// TODO(roberts): This is a workaround for extremely small vectors where some
// loss of precision can occur in Normalize causing underflow. When PointCross
// is updated to work around this, this can be removed.
if math.Max(math.Abs(normUVW.X), math.Max(math.Abs(normUVW.Y), math.Abs(normUVW.Z))) < math.Ldexp(1, -511) {
normUVW = pointUVW{normUVW.Mul(math.Ldexp(1, 563))}
}
normUVW = pointUVW{normUVW.Normalize()}
aTan := pointUVW{normUVW.Cross(aUVW.Vector)}
bTan := pointUVW{bUVW.Cross(normUVW.Vector)}
// As described in clipDestination, if the sum of the scores from clipping the two
// endpoints is 3 or more, then the segment does not intersect this face.
aUV, aScore := clipDestination(bUVW, aUVW, pointUVW{scaledN.Mul(-1)}, bTan, aTan, scaleUV)
bUV, bScore := clipDestination(aUVW, bUVW, scaledN, aTan, bTan, scaleUV)
return aUV, bUV, aScore+bScore < 3
}
// ClipEdge returns the portion of the edge defined by AB that is contained by the
// given rectangle. If there is no intersection, false is returned and aClip and bClip
// are undefined.
func ClipEdge(a, b r2.Point, clip r2.Rect) (aClip, bClip r2.Point, intersects bool) {
// Compute the bounding rectangle of AB, clip it, and then extract the new
// endpoints from the clipped bound.
bound := r2.RectFromPoints(a, b)
if bound, intersects = clipEdgeBound(a, b, clip, bound); !intersects {
return aClip, bClip, false
}
ai := 0
if a.X > b.X {
ai = 1
}
aj := 0
if a.Y > b.Y {
aj = 1
}
return bound.VertexIJ(ai, aj), bound.VertexIJ(1-ai, 1-aj), true
}
// The three functions below (sumEqual, intersectsFace, intersectsOppositeEdges)
// all compare a sum (u + v) to a third value w. They are implemented in such a
// way that they produce an exact result even though all calculations are done
// with ordinary floating-point operations. Here are the principles on which these
// functions are based:
//
// A. If u + v < w in floating-point, then u + v < w in exact arithmetic.
//
// B. If u + v < w in exact arithmetic, then at least one of the following
// expressions is true in floating-point:
// u + v < w
// u < w - v
// v < w - u
//
// Proof: By rearranging terms and substituting ">" for "<", we can assume
// that all values are non-negative. Now clearly "w" is not the smallest
// value, so assume WLOG that "u" is the smallest. We want to show that
// u < w - v in floating-point. If v >= w/2, the calculation of w - v is
// exact since the result is smaller in magnitude than either input value,
// so the result holds. Otherwise we have u <= v < w/2 and w - v >= w/2
// (even in floating point), so the result also holds.
// sumEqual reports whether u + v == w exactly.
func sumEqual(u, v, w float64) bool {
return (u+v == w) && (u == w-v) && (v == w-u)
}
// pointUVW represents a Point in (u,v,w) coordinate space of a cube face.
type pointUVW Point
// intersectsFace reports whether a given directed line L intersects the cube face F.
// The line L is defined by its normal N in the (u,v,w) coordinates of F.
func (p pointUVW) intersectsFace() bool {
// L intersects the [-1,1]x[-1,1] square in (u,v) if and only if the dot
// products of N with the four corner vertices (-1,-1,1), (1,-1,1), (1,1,1),
// and (-1,1,1) do not all have the same sign. This is true exactly when
// |Nu| + |Nv| >= |Nw|. The code below evaluates this expression exactly.
u := math.Abs(p.X)
v := math.Abs(p.Y)
w := math.Abs(p.Z)
// We only need to consider the cases where u or v is the smallest value,
// since if w is the smallest then both expressions below will have a
// positive LHS and a negative RHS.
return (v >= w-u) && (u >= w-v)
}
// intersectsOppositeEdges reports whether a directed line L intersects two
// opposite edges of a cube face F. This includs the case where L passes
// exactly through a corner vertex of F. The directed line L is defined
// by its normal N in the (u,v,w) coordinates of F.
func (p pointUVW) intersectsOppositeEdges() bool {
// The line L intersects opposite edges of the [-1,1]x[-1,1] (u,v) square if
// and only exactly two of the corner vertices lie on each side of L. This
// is true exactly when ||Nu| - |Nv|| >= |Nw|. The code below evaluates this
// expression exactly.
u := math.Abs(p.X)
v := math.Abs(p.Y)
w := math.Abs(p.Z)
// If w is the smallest, the following line returns an exact result.
if math.Abs(u-v) != w {
return math.Abs(u-v) >= w
}
// Otherwise u - v = w exactly, or w is not the smallest value. In either
// case the following returns the correct result.
if u >= v {
return u-w >= v
}
return v-w >= u
}
// axis represents the possible results of exitAxis.
type axis int
const (
axisU axis = iota
axisV
)
// exitAxis reports which axis the directed line L exits the cube face F on.
// The directed line L is represented by its CCW normal N in the (u,v,w) coordinates
// of F. It returns axisU if L exits through the u=-1 or u=+1 edge, and axisV if L exits
// through the v=-1 or v=+1 edge. Either result is acceptable if L exits exactly
// through a corner vertex of the cube face.
func (p pointUVW) exitAxis() axis {
if p.intersectsOppositeEdges() {
// The line passes through through opposite edges of the face.
// It exits through the v=+1 or v=-1 edge if the u-component of N has a
// larger absolute magnitude than the v-component.
if math.Abs(p.X) >= math.Abs(p.Y) {
return axisV
}
return axisU
}
// The line passes through through two adjacent edges of the face.
// It exits the v=+1 or v=-1 edge if an even number of the components of N
// are negative. We test this using signbit() rather than multiplication
// to avoid the possibility of underflow.
var x, y, z int
if math.Signbit(p.X) {
x = 1
}
if math.Signbit(p.Y) {
y = 1
}
if math.Signbit(p.Z) {
z = 1
}
if x^y^z == 0 {
return axisV
}
return axisU
}
// exitPoint returns the UV coordinates of the point where a directed line L (represented
// by the CCW normal of this point), exits the cube face this point is derived from along
// the given axis.
func (p pointUVW) exitPoint(a axis) r2.Point {
if a == axisU {
u := -1.0
if p.Y > 0 {
u = 1.0
}
return r2.Point{u, (-u*p.X - p.Z) / p.Y}
}
v := -1.0
if p.X < 0 {
v = 1.0
}
return r2.Point{(-v*p.Y - p.Z) / p.X, v}
}
// clipDestination returns a score which is used to indicate if the clipped edge AB
// on the given face intersects the face at all. This function returns the score for
// the given endpoint, which is an integer ranging from 0 to 3. If the sum of the scores
// from both of the endpoints is 3 or more, then edge AB does not intersect this face.
//
// First, it clips the line segment AB to find the clipped destination B' on a given
// face. (The face is specified implicitly by expressing *all arguments* in the (u,v,w)
// coordinates of that face.) Second, it partially computes whether the segment AB
// intersects this face at all. The actual condition is fairly complicated, but it
// turns out that it can be expressed as a "score" that can be computed independently
// when clipping the two endpoints A and B.
func clipDestination(a, b, scaledN, aTan, bTan pointUVW, scaleUV float64) (r2.Point, int) {
var uv r2.Point
// Optimization: if B is within the safe region of the face, use it.
maxSafeUVCoord := 1 - faceClipErrorUVCoord
if b.Z > 0 {
uv = r2.Point{b.X / b.Z, b.Y / b.Z}
if math.Max(math.Abs(uv.X), math.Abs(uv.Y)) <= maxSafeUVCoord {
return uv, 0
}
}
// Otherwise find the point B' where the line AB exits the face.
uv = scaledN.exitPoint(scaledN.exitAxis()).Mul(scaleUV)
p := pointUVW(Point{r3.Vector{uv.X, uv.Y, 1.0}})
// Determine if the exit point B' is contained within the segment. We do this
// by computing the dot products with two inward-facing tangent vectors at A
// and B. If either dot product is negative, we say that B' is on the "wrong
// side" of that point. As the point B' moves around the great circle AB past
// the segment endpoint B, it is initially on the wrong side of B only; as it
// moves further it is on the wrong side of both endpoints; and then it is on
// the wrong side of A only. If the exit point B' is on the wrong side of
// either endpoint, we can't use it; instead the segment is clipped at the
// original endpoint B.
//
// We reject the segment if the sum of the scores of the two endpoints is 3
// or more. Here is what that rule encodes:
// - If B' is on the wrong side of A, then the other clipped endpoint A'
// must be in the interior of AB (otherwise AB' would go the wrong way
// around the circle). There is a similar rule for A'.
// - If B' is on the wrong side of either endpoint (and therefore we must
// use the original endpoint B instead), then it must be possible to
// project B onto this face (i.e., its w-coordinate must be positive).
// This rule is only necessary to handle certain zero-length edges (A=B).
score := 0
if p.Sub(a.Vector).Dot(aTan.Vector) < 0 {
score = 2 // B' is on wrong side of A.
} else if p.Sub(b.Vector).Dot(bTan.Vector) < 0 {
score = 1 // B' is on wrong side of B.
}
if score > 0 { // B' is not in the interior of AB.
if b.Z <= 0 {
score = 3 // B cannot be projected onto this face.
} else {
uv = r2.Point{b.X / b.Z, b.Y / b.Z}
}
}
return uv, score
}
// updateEndpoint returns the interval with the specified endpoint updated to
// the given value. If the value lies beyond the opposite endpoint, nothing is
// changed and false is returned.
func updateEndpoint(bound r1.Interval, highEndpoint bool, value float64) (r1.Interval, bool) {
if !highEndpoint {
if bound.Hi < value {
return bound, false
}
if bound.Lo < value {
bound.Lo = value
}
return bound, true
}
if bound.Lo > value {
return bound, false
}
if bound.Hi > value {
bound.Hi = value
}
return bound, true
}
// clipBoundAxis returns the clipped versions of the bounding intervals for the given
// axes for the line segment from (a0,a1) to (b0,b1) so that neither extends beyond the
// given clip interval. negSlope is a precomputed helper variable that indicates which
// diagonal of the bounding box is spanned by AB; it is false if AB has positive slope,
// and true if AB has negative slope. If the clipping interval doesn't overlap the bounds,
// false is returned.
func clipBoundAxis(a0, b0 float64, bound0 r1.Interval, a1, b1 float64, bound1 r1.Interval,
negSlope bool, clip r1.Interval) (bound0c, bound1c r1.Interval, updated bool) {
if bound0.Lo < clip.Lo {
// If the upper bound is below the clips lower bound, there is nothing to do.
if bound0.Hi < clip.Lo {
return bound0, bound1, false
}
// narrow the intervals lower bound to the clip bound.
bound0.Lo = clip.Lo
if bound1, updated = updateEndpoint(bound1, negSlope, interpolateFloat64(clip.Lo, a0, b0, a1, b1)); !updated {
return bound0, bound1, false
}
}
if bound0.Hi > clip.Hi {
// If the lower bound is above the clips upper bound, there is nothing to do.
if bound0.Lo > clip.Hi {
return bound0, bound1, false
}
// narrow the intervals upper bound to the clip bound.
bound0.Hi = clip.Hi
if bound1, updated = updateEndpoint(bound1, !negSlope, interpolateFloat64(clip.Hi, a0, b0, a1, b1)); !updated {
return bound0, bound1, false
}
}
return bound0, bound1, true
}
// edgeIntersectsRect reports whether the edge defined by AB intersects the
// given closed rectangle to within the error bound.
func edgeIntersectsRect(a, b r2.Point, r r2.Rect) bool {
// First check whether the bounds of a Rect around AB intersects the given rect.
if !r.Intersects(r2.RectFromPoints(a, b)) {
return false
}
// Otherwise AB intersects the rect if and only if all four vertices of rect
// do not lie on the same side of the extended line AB. We test this by finding
// the two vertices of rect with minimum and maximum projections onto the normal
// of AB, and computing their dot products with the edge normal.
n := b.Sub(a).Ortho()
i := 0
if n.X >= 0 {
i = 1
}
j := 0
if n.Y >= 0 {
j = 1
}
max := n.Dot(r.VertexIJ(i, j).Sub(a))
min := n.Dot(r.VertexIJ(1-i, 1-j).Sub(a))
return (max >= 0) && (min <= 0)
}
// clippedEdgeBound returns the bounding rectangle of the portion of the edge defined
// by AB intersected by clip. The resulting bound may be empty. This is a convenience
// function built on top of clipEdgeBound.
func clippedEdgeBound(a, b r2.Point, clip r2.Rect) r2.Rect {
bound := r2.RectFromPoints(a, b)
if b1, intersects := clipEdgeBound(a, b, clip, bound); intersects {
return b1
}
return r2.EmptyRect()
}
// clipEdgeBound clips an edge AB to sequence of rectangles efficiently.
// It represents the clipped edges by their bounding boxes rather than as a pair of
// endpoints. Specifically, let A'B' be some portion of an edge AB, and let bound be
// a tight bound of A'B'. This function returns the bound that is a tight bound
// of A'B' intersected with a given rectangle. If A'B' does not intersect clip,
// it returns false and the original bound.
func clipEdgeBound(a, b r2.Point, clip, bound r2.Rect) (r2.Rect, bool) {
// negSlope indicates which diagonal of the bounding box is spanned by AB: it
// is false if AB has positive slope, and true if AB has negative slope. This is
// used to determine which interval endpoints need to be updated each time
// the edge is clipped.
negSlope := (a.X > b.X) != (a.Y > b.Y)
b0x, b0y, up1 := clipBoundAxis(a.X, b.X, bound.X, a.Y, b.Y, bound.Y, negSlope, clip.X)
if !up1 {
return bound, false
}
b1y, b1x, up2 := clipBoundAxis(a.Y, b.Y, b0y, a.X, b.X, b0x, negSlope, clip.Y)
if !up2 {
return r2.Rect{b0x, b0y}, false
}
return r2.Rect{X: b1x, Y: b1y}, true
}
// interpolateFloat64 returns a value with the same combination of a1 and b1 as the
// given value x is of a and b. This function makes the following guarantees:
// - If x == a, then x1 = a1 (exactly).
// - If x == b, then x1 = b1 (exactly).
// - If a <= x <= b, then a1 <= x1 <= b1 (even if a1 == b1).
// This requires a != b.
func interpolateFloat64(x, a, b, a1, b1 float64) float64 {
// To get results that are accurate near both A and B, we interpolate
// starting from the closer of the two points.
if math.Abs(a-x) <= math.Abs(b-x) {
return a1 + (b1-a1)*(x-a)/(b-a)
}
return b1 + (a1-b1)*(x-b)/(a-b)
}
// FaceSegment represents an edge AB clipped to an S2 cube face. It is
// represented by a face index and a pair of (u,v) coordinates.
type FaceSegment struct {
face int
a, b r2.Point
}
// FaceSegments subdivides the given edge AB at every point where it crosses the
// boundary between two S2 cube faces and returns the corresponding FaceSegments.
// The segments are returned in order from A toward B. The input points must be
// unit length.
//
// This function guarantees that the returned segments form a continuous path
// from A to B, and that all vertices are within faceClipErrorUVDist of the
// line AB. All vertices lie within the [-1,1]x[-1,1] cube face rectangles.
// The results are consistent with Sign, i.e. the edge is well-defined even its
// endpoints are antipodal.
// TODO(roberts): Extend the implementation of PointCross so that this is true.
func FaceSegments(a, b Point) []FaceSegment {
var segment FaceSegment
// Fast path: both endpoints are on the same face.
var aFace, bFace int
aFace, segment.a.X, segment.a.Y = xyzToFaceUV(a.Vector)
bFace, segment.b.X, segment.b.Y = xyzToFaceUV(b.Vector)
if aFace == bFace {
segment.face = aFace
return []FaceSegment{segment}
}
// Starting at A, we follow AB from face to face until we reach the face
// containing B. The following code is designed to ensure that we always
// reach B, even in the presence of numerical errors.
//
// First we compute the normal to the plane containing A and B. This normal
// becomes the ultimate definition of the line AB; it is used to resolve all
// questions regarding where exactly the line goes. Unfortunately due to
// numerical errors, the line may not quite intersect the faces containing
// the original endpoints. We handle this by moving A and/or B slightly if
// necessary so that they are on faces intersected by the line AB.
ab := a.PointCross(b)
aFace, segment.a = moveOriginToValidFace(aFace, a, ab, segment.a)
bFace, segment.b = moveOriginToValidFace(bFace, b, Point{ab.Mul(-1)}, segment.b)
// Now we simply follow AB from face to face until we reach B.
var segments []FaceSegment
segment.face = aFace
bSaved := segment.b
for face := aFace; face != bFace; {
// Complete the current segment by finding the point where AB
// exits the current face.
z := faceXYZtoUVW(face, ab)
n := pointUVW{z.Vector}
exitAxis := n.exitAxis()
segment.b = n.exitPoint(exitAxis)
segments = append(segments, segment)
// Compute the next face intersected by AB, and translate the exit
// point of the current segment into the (u,v) coordinates of the
// next face. This becomes the first point of the next segment.
exitXyz := faceUVToXYZ(face, segment.b.X, segment.b.Y)
face = nextFace(face, segment.b, exitAxis, n, bFace)
exitUvw := faceXYZtoUVW(face, Point{exitXyz})
segment.face = face
segment.a = r2.Point{exitUvw.X, exitUvw.Y}
}
// Finish the last segment.
segment.b = bSaved
return append(segments, segment)
}
// moveOriginToValidFace updates the origin point to a valid face if necessary.
// Given a line segment AB whose origin A has been projected onto a given cube
// face, determine whether it is necessary to project A onto a different face
// instead. This can happen because the normal of the line AB is not computed
// exactly, so that the line AB (defined as the set of points perpendicular to
// the normal) may not intersect the cube face containing A. Even if it does
// intersect the face, the exit point of the line from that face may be on
// the wrong side of A (i.e., in the direction away from B). If this happens,
// we reproject A onto the adjacent face where the line AB approaches A most
// closely. This moves the origin by a small amount, but never more than the
// error tolerances.
func moveOriginToValidFace(face int, a, ab Point, aUV r2.Point) (int, r2.Point) {
// Fast path: if the origin is sufficiently far inside the face, it is
// always safe to use it.
const maxSafeUVCoord = 1 - faceClipErrorUVCoord
if math.Max(math.Abs((aUV).X), math.Abs((aUV).Y)) <= maxSafeUVCoord {
return face, aUV
}
// Otherwise check whether the normal AB even intersects this face.
z := faceXYZtoUVW(face, ab)
n := pointUVW{z.Vector}
if n.intersectsFace() {
// Check whether the point where the line AB exits this face is on the
// wrong side of A (by more than the acceptable error tolerance).
uv := n.exitPoint(n.exitAxis())
exit := faceUVToXYZ(face, uv.X, uv.Y)
aTangent := ab.Normalize().Cross(a.Vector)
// We can use the given face.
if exit.Sub(a.Vector).Dot(aTangent) >= -faceClipErrorRadians {
return face, aUV
}
}
// Otherwise we reproject A to the nearest adjacent face. (If line AB does
// not pass through a given face, it must pass through all adjacent faces.)
var dir int
if math.Abs((aUV).X) >= math.Abs((aUV).Y) {
// U-axis
if aUV.X > 0 {
dir = 1
}
face = uvwFace(face, 0, dir)
} else {
// V-axis
if aUV.Y > 0 {
dir = 1
}
face = uvwFace(face, 1, dir)
}
aUV.X, aUV.Y = validFaceXYZToUV(face, a.Vector)
aUV.X = math.Max(-1.0, math.Min(1.0, aUV.X))
aUV.Y = math.Max(-1.0, math.Min(1.0, aUV.Y))
return face, aUV
}
// nextFace returns the next face that should be visited by FaceSegments, given that
// we have just visited face and we are following the line AB (represented
// by its normal N in the (u,v,w) coordinates of that face). The other
// arguments include the point where AB exits face, the corresponding
// exit axis, and the target face containing the destination point B.
func nextFace(face int, exit r2.Point, axis axis, n pointUVW, targetFace int) int {
// this bit is to work around C++ cleverly casting bools to ints for you.
exitA := exit.X
exit1MinusA := exit.Y
if axis == axisV {
exitA = exit.Y
exit1MinusA = exit.X
}
exitAPos := 0
if exitA > 0 {
exitAPos = 1
}
exit1MinusAPos := 0
if exit1MinusA > 0 {
exit1MinusAPos = 1
}
// We return the face that is adjacent to the exit point along the given
// axis. If line AB exits *exactly* through a corner of the face, there are
// two possible next faces. If one is the target face containing B, then
// we guarantee that we advance to that face directly.
//
// The three conditions below check that (1) AB exits approximately through
// a corner, (2) the adjacent face along the non-exit axis is the target
// face, and (3) AB exits *exactly* through the corner. (The sumEqual
// code checks whether the dot product of (u,v,1) and n is exactly zero.)
if math.Abs(exit1MinusA) == 1 &&
uvwFace(face, int(1-axis), exit1MinusAPos) == targetFace &&
sumEqual(exit.X*n.X, exit.Y*n.Y, -n.Z) {
return targetFace
}
// Otherwise return the face that is adjacent to the exit point in the
// direction of the exit axis.
return uvwFace(face, int(axis), exitAPos)
}

View File

@ -1,227 +0,0 @@
// 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
import (
"math"
)
// EdgeCrosser allows edges to be efficiently tested for intersection with a
// given fixed edge AB. It is especially efficient when testing for
// intersection with an edge chain connecting vertices v0, v1, v2, ...
//
// Example usage:
//
// func CountIntersections(a, b Point, edges []Edge) int {
// count := 0
// crosser := NewEdgeCrosser(a, b)
// for _, edge := range edges {
// if crosser.CrossingSign(&edge.First, &edge.Second) != DoNotCross {
// count++
// }
// }
// return count
// }
//
type EdgeCrosser struct {
a Point
b Point
aXb Point
// To reduce the number of calls to expensiveSign, we compute an
// outward-facing tangent at A and B if necessary. If the plane
// perpendicular to one of these tangents separates AB from CD (i.e., one
// edge on each side) then there is no intersection.
aTangent Point // Outward-facing tangent at A.
bTangent Point // Outward-facing tangent at B.
// The fields below are updated for each vertex in the chain.
c Point // Previous vertex in the vertex chain.
acb Direction // The orientation of triangle ACB.
}
// NewEdgeCrosser returns an EdgeCrosser with the fixed edge AB.
func NewEdgeCrosser(a, b Point) *EdgeCrosser {
norm := a.PointCross(b)
return &EdgeCrosser{
a: a,
b: b,
aXb: Point{a.Cross(b.Vector)},
aTangent: Point{a.Cross(norm.Vector)},
bTangent: Point{norm.Cross(b.Vector)},
}
}
// CrossingSign reports whether the edge AB intersects the edge CD. If any two
// vertices from different edges are the same, returns MaybeCross. If either edge
// is degenerate (A == B or C == D), returns either DoNotCross or MaybeCross.
//
// Properties of CrossingSign:
//
// (1) CrossingSign(b,a,c,d) == CrossingSign(a,b,c,d)
// (2) CrossingSign(c,d,a,b) == CrossingSign(a,b,c,d)
// (3) CrossingSign(a,b,c,d) == MaybeCross if a==c, a==d, b==c, b==d
// (3) CrossingSign(a,b,c,d) == DoNotCross or MaybeCross if a==b or c==d
//
// Note that if you want to check an edge against a chain of other edges,
// it is slightly more efficient to use the single-argument version
// ChainCrossingSign below.
func (e *EdgeCrosser) CrossingSign(c, d Point) Crossing {
if c != e.c {
e.RestartAt(c)
}
return e.ChainCrossingSign(d)
}
// EdgeOrVertexCrossing reports whether if CrossingSign(c, d) > 0, or AB and
// CD share a vertex and VertexCrossing(a, b, c, d) is true.
//
// This method extends the concept of a "crossing" to the case where AB
// and CD have a vertex in common. The two edges may or may not cross,
// according to the rules defined in VertexCrossing above. The rules
// are designed so that point containment tests can be implemented simply
// by counting edge crossings. Similarly, determining whether one edge
// chain crosses another edge chain can be implemented by counting.
func (e *EdgeCrosser) EdgeOrVertexCrossing(c, d Point) bool {
if c != e.c {
e.RestartAt(c)
}
return e.EdgeOrVertexChainCrossing(d)
}
// NewChainEdgeCrosser is a convenience constructor that uses AB as the fixed edge,
// and C as the first vertex of the vertex chain (equivalent to calling RestartAt(c)).
//
// You don't need to use this or any of the chain functions unless you're trying to
// squeeze out every last drop of performance. Essentially all you are saving is a test
// whether the first vertex of the current edge is the same as the second vertex of the
// previous edge.
func NewChainEdgeCrosser(a, b, c Point) *EdgeCrosser {
e := NewEdgeCrosser(a, b)
e.RestartAt(c)
return e
}
// RestartAt sets the current point of the edge crosser to be c.
// Call this method when your chain 'jumps' to a new place.
// The argument must point to a value that persists until the next call.
func (e *EdgeCrosser) RestartAt(c Point) {
e.c = c
e.acb = -triageSign(e.a, e.b, e.c)
}
// ChainCrossingSign is like CrossingSign, but uses the last vertex passed to one of
// the crossing methods (or RestartAt) as the first vertex of the current edge.
func (e *EdgeCrosser) ChainCrossingSign(d Point) Crossing {
// For there to be an edge crossing, the triangles ACB, CBD, BDA, DAC must
// all be oriented the same way (CW or CCW). We keep the orientation of ACB
// as part of our state. When each new point D arrives, we compute the
// orientation of BDA and check whether it matches ACB. This checks whether
// the points C and D are on opposite sides of the great circle through AB.
// Recall that triageSign is invariant with respect to rotating its
// arguments, i.e. ABD has the same orientation as BDA.
bda := triageSign(e.a, e.b, d)
if e.acb == -bda && bda != Indeterminate {
// The most common case -- triangles have opposite orientations. Save the
// current vertex D as the next vertex C, and also save the orientation of
// the new triangle ACB (which is opposite to the current triangle BDA).
e.c = d
e.acb = -bda
return DoNotCross
}
return e.crossingSign(d, bda)
}
// EdgeOrVertexChainCrossing is like EdgeOrVertexCrossing, but uses the last vertex
// passed to one of the crossing methods (or RestartAt) as the first vertex of the current edge.
func (e *EdgeCrosser) EdgeOrVertexChainCrossing(d Point) bool {
// We need to copy e.c since it is clobbered by ChainCrossingSign.
c := e.c
switch e.ChainCrossingSign(d) {
case DoNotCross:
return false
case Cross:
return true
}
return VertexCrossing(e.a, e.b, c, d)
}
// crossingSign handle the slow path of CrossingSign.
func (e *EdgeCrosser) crossingSign(d Point, bda Direction) Crossing {
// Compute the actual result, and then save the current vertex D as the next
// vertex C, and save the orientation of the next triangle ACB (which is
// opposite to the current triangle BDA).
defer func() {
e.c = d
e.acb = -bda
}()
// At this point, a very common situation is that A,B,C,D are four points on
// a line such that AB does not overlap CD. (For example, this happens when
// a line or curve is sampled finely, or when geometry is constructed by
// computing the union of S2CellIds.) Most of the time, we can determine
// that AB and CD do not intersect using the two outward-facing
// tangents at A and B (parallel to AB) and testing whether AB and CD are on
// opposite sides of the plane perpendicular to one of these tangents. This
// is moderately expensive but still much cheaper than expensiveSign.
// The error in RobustCrossProd is insignificant. The maximum error in
// the call to CrossProd (i.e., the maximum norm of the error vector) is
// (0.5 + 1/sqrt(3)) * dblEpsilon. The maximum error in each call to
// DotProd below is dblEpsilon. (There is also a small relative error
// term that is insignificant because we are comparing the result against a
// constant that is very close to zero.)
maxError := (1.5 + 1/math.Sqrt(3)) * dblEpsilon
if (e.c.Dot(e.aTangent.Vector) > maxError && d.Dot(e.aTangent.Vector) > maxError) || (e.c.Dot(e.bTangent.Vector) > maxError && d.Dot(e.bTangent.Vector) > maxError) {
return DoNotCross
}
// Otherwise, eliminate the cases where two vertices from different edges are
// equal. (These cases could be handled in the code below, but we would rather
// avoid calling ExpensiveSign if possible.)
if e.a == e.c || e.a == d || e.b == e.c || e.b == d {
return MaybeCross
}
// Eliminate the cases where an input edge is degenerate. (Note that in
// most cases, if CD is degenerate then this method is not even called
// because acb and bda have different signs.)
if e.a == e.b || e.c == d {
return DoNotCross
}
// Otherwise it's time to break out the big guns.
if e.acb == Indeterminate {
e.acb = -expensiveSign(e.a, e.b, e.c)
}
if bda == Indeterminate {
bda = expensiveSign(e.a, e.b, d)
}
if bda != e.acb {
return DoNotCross
}
cbd := -RobustSign(e.c, d, e.b)
if cbd != e.acb {
return DoNotCross
}
dac := RobustSign(e.c, d, e.a)
if dac != e.acb {
return DoNotCross
}
return Cross
}

View File

@ -1,396 +0,0 @@
// 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
import (
"fmt"
"math"
"github.com/golang/geo/r3"
"github.com/golang/geo/s1"
)
const (
// intersectionError can be set somewhat arbitrarily, because the algorithm
// uses more precision if necessary in order to achieve the specified error.
// The only strict requirement is that intersectionError >= dblEpsilon
// radians. However, using a larger error tolerance makes the algorithm more
// efficient because it reduces the number of cases where exact arithmetic is
// needed.
intersectionError = s1.Angle(8 * dblError)
// intersectionMergeRadius is used to ensure that intersection points that
// are supposed to be coincident are merged back together into a single
// vertex. This is required in order for various polygon operations (union,
// intersection, etc) to work correctly. It is twice the intersection error
// because two coincident intersection points might have errors in
// opposite directions.
intersectionMergeRadius = 2 * intersectionError
)
// A Crossing indicates how edges cross.
type Crossing int
const (
// Cross means the edges cross.
Cross Crossing = iota
// MaybeCross means two vertices from different edges are the same.
MaybeCross
// DoNotCross means the edges do not cross.
DoNotCross
)
func (c Crossing) String() string {
switch c {
case Cross:
return "Cross"
case MaybeCross:
return "MaybeCross"
case DoNotCross:
return "DoNotCross"
default:
return fmt.Sprintf("(BAD CROSSING %d)", c)
}
}
// CrossingSign reports whether the edge AB intersects the edge CD.
// If AB crosses CD at a point that is interior to both edges, Cross is returned.
// If any two vertices from different edges are the same it returns MaybeCross.
// Otherwise it returns DoNotCross.
// If either edge is degenerate (A == B or C == D), the return value is MaybeCross
// if two vertices from different edges are the same and DoNotCross otherwise.
//
// Properties of CrossingSign:
//
// (1) CrossingSign(b,a,c,d) == CrossingSign(a,b,c,d)
// (2) CrossingSign(c,d,a,b) == CrossingSign(a,b,c,d)
// (3) CrossingSign(a,b,c,d) == MaybeCross if a==c, a==d, b==c, b==d
// (3) CrossingSign(a,b,c,d) == DoNotCross or MaybeCross if a==b or c==d
//
// This method implements an exact, consistent perturbation model such
// that no three points are ever considered to be collinear. This means
// that even if you have 4 points A, B, C, D that lie exactly in a line
// (say, around the equator), C and D will be treated as being slightly to
// one side or the other of AB. This is done in a way such that the
// results are always consistent (see RobustSign).
func CrossingSign(a, b, c, d Point) Crossing {
crosser := NewChainEdgeCrosser(a, b, c)
return crosser.ChainCrossingSign(d)
}
// VertexCrossing reports whether two edges "cross" in such a way that point-in-polygon
// containment tests can be implemented by counting the number of edge crossings.
//
// Given two edges AB and CD where at least two vertices are identical
// (i.e. CrossingSign(a,b,c,d) == 0), the basic rule is that a "crossing"
// occurs if AB is encountered after CD during a CCW sweep around the shared
// vertex starting from a fixed reference point.
//
// Note that according to this rule, if AB crosses CD then in general CD
// does not cross AB. However, this leads to the correct result when
// counting polygon edge crossings. For example, suppose that A,B,C are
// three consecutive vertices of a CCW polygon. If we now consider the edge
// crossings of a segment BP as P sweeps around B, the crossing number
// changes parity exactly when BP crosses BA or BC.
//
// Useful properties of VertexCrossing (VC):
//
// (1) VC(a,a,c,d) == VC(a,b,c,c) == false
// (2) VC(a,b,a,b) == VC(a,b,b,a) == true
// (3) VC(a,b,c,d) == VC(a,b,d,c) == VC(b,a,c,d) == VC(b,a,d,c)
// (3) If exactly one of a,b equals one of c,d, then exactly one of
// VC(a,b,c,d) and VC(c,d,a,b) is true
//
// It is an error to call this method with 4 distinct vertices.
func VertexCrossing(a, b, c, d Point) bool {
// If A == B or C == D there is no intersection. We need to check this
// case first in case 3 or more input points are identical.
if a == b || c == d {
return false
}
// If any other pair of vertices is equal, there is a crossing if and only
// if OrderedCCW indicates that the edge AB is further CCW around the
// shared vertex O (either A or B) than the edge CD, starting from an
// arbitrary fixed reference point.
// Optimization: if AB=CD or AB=DC, we can avoid most of the calculations.
switch {
case a == c:
return (b == d) || OrderedCCW(Point{a.Ortho()}, d, b, a)
case b == d:
return OrderedCCW(Point{b.Ortho()}, c, a, b)
case a == d:
return (b == c) || OrderedCCW(Point{a.Ortho()}, c, b, a)
case b == c:
return OrderedCCW(Point{b.Ortho()}, d, a, b)
}
return false
}
// EdgeOrVertexCrossing is a convenience function that calls CrossingSign to
// handle cases where all four vertices are distinct, and VertexCrossing to
// handle cases where two or more vertices are the same. This defines a crossing
// function such that point-in-polygon containment tests can be implemented
// by simply counting edge crossings.
func EdgeOrVertexCrossing(a, b, c, d Point) bool {
switch CrossingSign(a, b, c, d) {
case DoNotCross:
return false
case Cross:
return true
default:
return VertexCrossing(a, b, c, d)
}
}
// Intersection returns the intersection point of two edges AB and CD that cross
// (CrossingSign(a,b,c,d) == Crossing).
//
// Useful properties of Intersection:
//
// (1) Intersection(b,a,c,d) == Intersection(a,b,d,c) == Intersection(a,b,c,d)
// (2) Intersection(c,d,a,b) == Intersection(a,b,c,d)
//
// The returned intersection point X is guaranteed to be very close to the
// true intersection point of AB and CD, even if the edges intersect at a
// very small angle.
func Intersection(a0, a1, b0, b1 Point) Point {
// It is difficult to compute the intersection point of two edges accurately
// when the angle between the edges is very small. Previously we handled
// this by only guaranteeing that the returned intersection point is within
// intersectionError of each edge. However, this means that when the edges
// cross at a very small angle, the computed result may be very far from the
// true intersection point.
//
// Instead this function now guarantees that the result is always within
// intersectionError of the true intersection. This requires using more
// sophisticated techniques and in some cases extended precision.
//
// - intersectionStable computes the intersection point using
// projection and interpolation, taking care to minimize cancellation
// error.
//
// - intersectionExact computes the intersection point using precision
// arithmetic and converts the final result back to an Point.
pt, ok := intersectionStable(a0, a1, b0, b1)
if !ok {
pt = intersectionExact(a0, a1, b0, b1)
}
// Make sure the intersection point is on the correct side of the sphere.
// Since all vertices are unit length, and edges are less than 180 degrees,
// (a0 + a1) and (b0 + b1) both have positive dot product with the
// intersection point. We use the sum of all vertices to make sure that the
// result is unchanged when the edges are swapped or reversed.
if pt.Dot((a0.Add(a1.Vector)).Add(b0.Add(b1.Vector))) < 0 {
pt = Point{pt.Mul(-1)}
}
return pt
}
// Computes the cross product of two vectors, normalized to be unit length.
// Also returns the length of the cross
// product before normalization, which is useful for estimating the amount of
// error in the result. For numerical stability, the vectors should both be
// approximately unit length.
func robustNormalWithLength(x, y r3.Vector) (r3.Vector, float64) {
var pt r3.Vector
// This computes 2 * (x.Cross(y)), but has much better numerical
// stability when x and y are unit length.
tmp := x.Sub(y).Cross(x.Add(y))
length := tmp.Norm()
if length != 0 {
pt = tmp.Mul(1 / length)
}
return pt, 0.5 * length // Since tmp == 2 * (x.Cross(y))
}
/*
// intersectionSimple is not used by the C++ so it is skipped here.
*/
// projection returns the projection of aNorm onto X (x.Dot(aNorm)), and a bound
// on the error in the result. aNorm is not necessarily unit length.
//
// The remaining parameters (the length of aNorm (aNormLen) and the edge endpoints
// a0 and a1) allow this dot product to be computed more accurately and efficiently.
func projection(x, aNorm r3.Vector, aNormLen float64, a0, a1 Point) (proj, bound float64) {
// The error in the dot product is proportional to the lengths of the input
// vectors, so rather than using x itself (a unit-length vector) we use
// the vectors from x to the closer of the two edge endpoints. This
// typically reduces the error by a huge factor.
x0 := x.Sub(a0.Vector)
x1 := x.Sub(a1.Vector)
x0Dist2 := x0.Norm2()
x1Dist2 := x1.Norm2()
// If both distances are the same, we need to be careful to choose one
// endpoint deterministically so that the result does not change if the
// order of the endpoints is reversed.
var dist float64
if x0Dist2 < x1Dist2 || (x0Dist2 == x1Dist2 && x0.Cmp(x1) == -1) {
dist = math.Sqrt(x0Dist2)
proj = x0.Dot(aNorm)
} else {
dist = math.Sqrt(x1Dist2)
proj = x1.Dot(aNorm)
}
// This calculation bounds the error from all sources: the computation of
// the normal, the subtraction of one endpoint, and the dot product itself.
// dblError appears because the input points are assumed to be
// normalized in double precision.
//
// For reference, the bounds that went into this calculation are:
// ||N'-N|| <= ((1 + 2 * sqrt(3))||N|| + 32 * sqrt(3) * dblError) * epsilon
// |(A.B)'-(A.B)| <= (1.5 * (A.B) + 1.5 * ||A|| * ||B||) * epsilon
// ||(X-Y)'-(X-Y)|| <= ||X-Y|| * epsilon
bound = (((3.5+2*math.Sqrt(3))*aNormLen+32*math.Sqrt(3)*dblError)*dist + 1.5*math.Abs(proj)) * epsilon
return proj, bound
}
// compareEdges reports whether (a0,a1) is less than (b0,b1) with respect to a total
// ordering on edges that is invariant under edge reversals.
func compareEdges(a0, a1, b0, b1 Point) bool {
if a0.Cmp(a1.Vector) != -1 {
a0, a1 = a1, a0
}
if b0.Cmp(b1.Vector) != -1 {
b0, b1 = b1, b0
}
return a0.Cmp(b0.Vector) == -1 || (a0 == b0 && b0.Cmp(b1.Vector) == -1)
}
// intersectionStable returns the intersection point of the edges (a0,a1) and
// (b0,b1) if it can be computed to within an error of at most intersectionError
// by this function.
//
// The intersection point is not guaranteed to have the correct sign because we
// choose to use the longest of the two edges first. The sign is corrected by
// Intersection.
func intersectionStable(a0, a1, b0, b1 Point) (Point, bool) {
// Sort the two edges so that (a0,a1) is longer, breaking ties in a
// deterministic way that does not depend on the ordering of the endpoints.
// This is desirable for two reasons:
// - So that the result doesn't change when edges are swapped or reversed.
// - It reduces error, since the first edge is used to compute the edge
// normal (where a longer edge means less error), and the second edge
// is used for interpolation (where a shorter edge means less error).
aLen2 := a1.Sub(a0.Vector).Norm2()
bLen2 := b1.Sub(b0.Vector).Norm2()
if aLen2 < bLen2 || (aLen2 == bLen2 && compareEdges(a0, a1, b0, b1)) {
return intersectionStableSorted(b0, b1, a0, a1)
}
return intersectionStableSorted(a0, a1, b0, b1)
}
// intersectionStableSorted is a helper function for intersectionStable.
// It expects that the edges (a0,a1) and (b0,b1) have been sorted so that
// the first edge passed in is longer.
func intersectionStableSorted(a0, a1, b0, b1 Point) (Point, bool) {
var pt Point
// Compute the normal of the plane through (a0, a1) in a stable way.
aNorm := a0.Sub(a1.Vector).Cross(a0.Add(a1.Vector))
aNormLen := aNorm.Norm()
bLen := b1.Sub(b0.Vector).Norm()
// Compute the projection (i.e., signed distance) of b0 and b1 onto the
// plane through (a0, a1). Distances are scaled by the length of aNorm.
b0Dist, b0Error := projection(b0.Vector, aNorm, aNormLen, a0, a1)
b1Dist, b1Error := projection(b1.Vector, aNorm, aNormLen, a0, a1)
// The total distance from b0 to b1 measured perpendicularly to (a0,a1) is
// |b0Dist - b1Dist|. Note that b0Dist and b1Dist generally have
// opposite signs because b0 and b1 are on opposite sides of (a0, a1). The
// code below finds the intersection point by interpolating along the edge
// (b0, b1) to a fractional distance of b0Dist / (b0Dist - b1Dist).
//
// It can be shown that the maximum error in the interpolation fraction is
//
// (b0Dist * b1Error - b1Dist * b0Error) / (distSum * (distSum - errorSum))
//
// We save ourselves some work by scaling the result and the error bound by
// "distSum", since the result is normalized to be unit length anyway.
distSum := math.Abs(b0Dist - b1Dist)
errorSum := b0Error + b1Error
if distSum <= errorSum {
return pt, false // Error is unbounded in this case.
}
x := b1.Mul(b0Dist).Sub(b0.Mul(b1Dist))
err := bLen*math.Abs(b0Dist*b1Error-b1Dist*b0Error)/
(distSum-errorSum) + 2*distSum*epsilon
// Finally we normalize the result, compute the corresponding error, and
// check whether the total error is acceptable.
xLen := x.Norm()
maxError := intersectionError
if err > (float64(maxError)-epsilon)*xLen {
return pt, false
}
return Point{x.Mul(1 / xLen)}, true
}
// intersectionExact returns the intersection point of (a0, a1) and (b0, b1)
// using precise arithmetic. Note that the result is not exact because it is
// rounded down to double precision at the end. Also, the intersection point
// is not guaranteed to have the correct sign (i.e., the return value may need
// to be negated).
func intersectionExact(a0, a1, b0, b1 Point) Point {
// Since we are using presice arithmetic, we don't need to worry about
// numerical stability.
a0P := r3.PreciseVectorFromVector(a0.Vector)
a1P := r3.PreciseVectorFromVector(a1.Vector)
b0P := r3.PreciseVectorFromVector(b0.Vector)
b1P := r3.PreciseVectorFromVector(b1.Vector)
aNormP := a0P.Cross(a1P)
bNormP := b0P.Cross(b1P)
xP := aNormP.Cross(bNormP)
// The final Normalize() call is done in double precision, which creates a
// directional error of up to 2*dblError. (Precise conversion and Normalize()
// each contribute up to dblError of directional error.)
x := xP.Vector()
if x == (r3.Vector{}) {
// The two edges are exactly collinear, but we still consider them to be
// "crossing" because of simulation of simplicity. Out of the four
// endpoints, exactly two lie in the interior of the other edge. Of
// those two we return the one that is lexicographically smallest.
x = r3.Vector{10, 10, 10} // Greater than any valid S2Point
aNorm := Point{aNormP.Vector()}
bNorm := Point{bNormP.Vector()}
if OrderedCCW(b0, a0, b1, bNorm) && a0.Cmp(x) == -1 {
return a0
}
if OrderedCCW(b0, a1, b1, bNorm) && a1.Cmp(x) == -1 {
return a1
}
if OrderedCCW(a0, b0, a1, aNorm) && b0.Cmp(x) == -1 {
return b0
}
if OrderedCCW(a0, b1, a1, aNorm) && b1.Cmp(x) == -1 {
return b1
}
}
return Point{x}
}

View File

@ -1,408 +0,0 @@
// 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
// This file defines a collection of methods for computing the distance to an edge,
// interpolating along an edge, projecting points onto edges, etc.
import (
"math"
"github.com/golang/geo/s1"
)
// DistanceFromSegment returns the distance of point X from line segment AB.
// The points are expected to be normalized. The result is very accurate for small
// distances but may have some numerical error if the distance is large
// (approximately pi/2 or greater). The case A == B is handled correctly.
func DistanceFromSegment(x, a, b Point) s1.Angle {
var minDist s1.ChordAngle
minDist, _ = updateMinDistance(x, a, b, minDist, true)
return minDist.Angle()
}
// IsDistanceLess reports whether the distance from X to the edge AB is less
// than limit. (For less than or equal to, specify limit.Successor()).
// This method is faster than DistanceFromSegment(). If you want to
// compare against a fixed s1.Angle, you should convert it to an s1.ChordAngle
// once and save the value, since this conversion is relatively expensive.
func IsDistanceLess(x, a, b Point, limit s1.ChordAngle) bool {
_, less := UpdateMinDistance(x, a, b, limit)
return less
}
// UpdateMinDistance checks if the distance from X to the edge AB is less
// than minDist, and if so, returns the updated value and true.
// The case A == B is handled correctly.
//
// Use this method when you want to compute many distances and keep track of
// the minimum. It is significantly faster than using DistanceFromSegment
// because (1) using s1.ChordAngle is much faster than s1.Angle, and (2) it
// can save a lot of work by not actually computing the distance when it is
// obviously larger than the current minimum.
func UpdateMinDistance(x, a, b Point, minDist s1.ChordAngle) (s1.ChordAngle, bool) {
return updateMinDistance(x, a, b, minDist, false)
}
// UpdateMaxDistance checks if the distance from X to the edge AB is greater
// than maxDist, and if so, returns the updated value and true.
// Otherwise it returns false. The case A == B is handled correctly.
func UpdateMaxDistance(x, a, b Point, maxDist s1.ChordAngle) (s1.ChordAngle, bool) {
dist := maxChordAngle(ChordAngleBetweenPoints(x, a), ChordAngleBetweenPoints(x, b))
if dist > s1.RightChordAngle {
dist, _ = updateMinDistance(Point{x.Mul(-1)}, a, b, dist, true)
dist = s1.StraightChordAngle - dist
}
if maxDist < dist {
return dist, true
}
return maxDist, false
}
// IsInteriorDistanceLess reports whether the minimum distance from X to the edge
// AB is attained at an interior point of AB (i.e., not an endpoint), and that
// distance is less than limit. (Specify limit.Successor() for less than or equal to).
func IsInteriorDistanceLess(x, a, b Point, limit s1.ChordAngle) bool {
_, less := UpdateMinInteriorDistance(x, a, b, limit)
return less
}
// UpdateMinInteriorDistance reports whether the minimum distance from X to AB
// is attained at an interior point of AB (i.e., not an endpoint), and that distance
// is less than minDist. If so, the value of minDist is updated and true is returned.
// Otherwise it is unchanged and returns false.
func UpdateMinInteriorDistance(x, a, b Point, minDist s1.ChordAngle) (s1.ChordAngle, bool) {
return interiorDist(x, a, b, minDist, false)
}
// Project returns the point along the edge AB that is closest to the point X.
// The fractional distance of this point along the edge AB can be obtained
// using DistanceFraction.
//
// This requires that all points are unit length.
func Project(x, a, b Point) Point {
aXb := a.PointCross(b)
// Find the closest point to X along the great circle through AB.
p := x.Sub(aXb.Mul(x.Dot(aXb.Vector) / aXb.Vector.Norm2()))
// If this point is on the edge AB, then it's the closest point.
if Sign(aXb, a, Point{p}) && Sign(Point{p}, b, aXb) {
return Point{p.Normalize()}
}
// Otherwise, the closest point is either A or B.
if x.Sub(a.Vector).Norm2() <= x.Sub(b.Vector).Norm2() {
return a
}
return b
}
// DistanceFraction returns the distance ratio of the point X along an edge AB.
// If X is on the line segment AB, this is the fraction T such
// that X == Interpolate(T, A, B).
//
// This requires that A and B are distinct.
func DistanceFraction(x, a, b Point) float64 {
d0 := x.Angle(a.Vector)
d1 := x.Angle(b.Vector)
return float64(d0 / (d0 + d1))
}
// Interpolate returns the point X along the line segment AB whose distance from A
// is the given fraction "t" of the distance AB. Does NOT require that "t" be
// between 0 and 1. Note that all distances are measured on the surface of
// the sphere, so this is more complicated than just computing (1-t)*a + t*b
// and normalizing the result.
func Interpolate(t float64, a, b Point) Point {
if t == 0 {
return a
}
if t == 1 {
return b
}
ab := a.Angle(b.Vector)
return InterpolateAtDistance(s1.Angle(t)*ab, a, b)
}
// InterpolateAtDistance returns the point X along the line segment AB whose
// distance from A is the angle ax.
func InterpolateAtDistance(ax s1.Angle, a, b Point) Point {
aRad := ax.Radians()
// Use PointCross to compute the tangent vector at A towards B. The
// result is always perpendicular to A, even if A=B or A=-B, but it is not
// necessarily unit length. (We effectively normalize it below.)
normal := a.PointCross(b)
tangent := normal.Vector.Cross(a.Vector)
// Now compute the appropriate linear combination of A and "tangent". With
// infinite precision the result would always be unit length, but we
// normalize it anyway to ensure that the error is within acceptable bounds.
// (Otherwise errors can build up when the result of one interpolation is
// fed into another interpolation.)
return Point{(a.Mul(math.Cos(aRad)).Add(tangent.Mul(math.Sin(aRad) / tangent.Norm()))).Normalize()}
}
// minUpdateDistanceMaxError returns the maximum error in the result of
// UpdateMinDistance (and the associated functions such as
// UpdateMinInteriorDistance, IsDistanceLess, etc), assuming that all
// input points are normalized to within the bounds guaranteed by r3.Vector's
// Normalize. The error can be added or subtracted from an s1.ChordAngle
// using its Expanded method.
func minUpdateDistanceMaxError(dist s1.ChordAngle) float64 {
// There are two cases for the maximum error in UpdateMinDistance(),
// depending on whether the closest point is interior to the edge.
return math.Max(minUpdateInteriorDistanceMaxError(dist), dist.MaxPointError())
}
// minUpdateInteriorDistanceMaxError returns the maximum error in the result of
// UpdateMinInteriorDistance, assuming that all input points are normalized
// to within the bounds guaranteed by Point's Normalize. The error can be added
// or subtracted from an s1.ChordAngle using its Expanded method.
//
// Note that accuracy goes down as the distance approaches 0 degrees or 180
// degrees (for different reasons). Near 0 degrees the error is acceptable
// for all practical purposes (about 1.2e-15 radians ~= 8 nanometers). For
// exactly antipodal points the maximum error is quite high (0.5 meters),
// but this error drops rapidly as the points move away from antipodality
// (approximately 1 millimeter for points that are 50 meters from antipodal,
// and 1 micrometer for points that are 50km from antipodal).
//
// TODO(roberts): Currently the error bound does not hold for edges whose endpoints
// are antipodal to within about 1e-15 radians (less than 1 micron). This could
// be fixed by extending PointCross to use higher precision when necessary.
func minUpdateInteriorDistanceMaxError(dist s1.ChordAngle) float64 {
// If a point is more than 90 degrees from an edge, then the minimum
// distance is always to one of the endpoints, not to the edge interior.
if dist >= s1.RightChordAngle {
return 0.0
}
// This bound includes all source of error, assuming that the input points
// are normalized. a and b are components of chord length that are
// perpendicular and parallel to a plane containing the edge respectively.
b := math.Min(1.0, 0.5*float64(dist))
a := math.Sqrt(b * (2 - b))
return ((2.5+2*math.Sqrt(3)+8.5*a)*a +
(2+2*math.Sqrt(3)/3+6.5*(1-b))*b +
(23+16/math.Sqrt(3))*dblEpsilon) * dblEpsilon
}
// updateMinDistance computes the distance from a point X to a line segment AB,
// and if either the distance was less than the given minDist, or alwaysUpdate is
// true, the value and whether it was updated are returned.
func updateMinDistance(x, a, b Point, minDist s1.ChordAngle, alwaysUpdate bool) (s1.ChordAngle, bool) {
if d, ok := interiorDist(x, a, b, minDist, alwaysUpdate); ok {
// Minimum distance is attained along the edge interior.
return d, true
}
// Otherwise the minimum distance is to one of the endpoints.
xa2, xb2 := (x.Sub(a.Vector)).Norm2(), x.Sub(b.Vector).Norm2()
dist := s1.ChordAngle(math.Min(xa2, xb2))
if !alwaysUpdate && dist >= minDist {
return minDist, false
}
return dist, true
}
// interiorDist returns the shortest distance from point x to edge ab, assuming
// that the closest point to X is interior to AB. If the closest point is not
// interior to AB, interiorDist returns (minDist, false). If alwaysUpdate is set to
// false, the distance is only updated when the value exceeds certain the given minDist.
func interiorDist(x, a, b Point, minDist s1.ChordAngle, alwaysUpdate bool) (s1.ChordAngle, bool) {
// Chord distance of x to both end points a and b.
xa2, xb2 := (x.Sub(a.Vector)).Norm2(), x.Sub(b.Vector).Norm2()
// The closest point on AB could either be one of the two vertices (the
// vertex case) or in the interior (the interior case). Let C = A x B.
// If X is in the spherical wedge extending from A to B around the axis
// through C, then we are in the interior case. Otherwise we are in the
// vertex case.
//
// Check whether we might be in the interior case. For this to be true, XAB
// and XBA must both be acute angles. Checking this condition exactly is
// expensive, so instead we consider the planar triangle ABX (which passes
// through the sphere's interior). The planar angles XAB and XBA are always
// less than the corresponding spherical angles, so if we are in the
// interior case then both of these angles must be acute.
//
// We check this by computing the squared edge lengths of the planar
// triangle ABX, and testing whether angles XAB and XBA are both acute using
// the law of cosines:
//
// | XA^2 - XB^2 | < AB^2 (*)
//
// This test must be done conservatively (taking numerical errors into
// account) since otherwise we might miss a situation where the true minimum
// distance is achieved by a point on the edge interior.
//
// There are two sources of error in the expression above (*). The first is
// that points are not normalized exactly; they are only guaranteed to be
// within 2 * dblEpsilon of unit length. Under the assumption that the two
// sides of (*) are nearly equal, the total error due to normalization errors
// can be shown to be at most
//
// 2 * dblEpsilon * (XA^2 + XB^2 + AB^2) + 8 * dblEpsilon ^ 2 .
//
// The other source of error is rounding of results in the calculation of (*).
// Each of XA^2, XB^2, AB^2 has a maximum relative error of 2.5 * dblEpsilon,
// plus an additional relative error of 0.5 * dblEpsilon in the final
// subtraction which we further bound as 0.25 * dblEpsilon * (XA^2 + XB^2 +
// AB^2) for convenience. This yields a final error bound of
//
// 4.75 * dblEpsilon * (XA^2 + XB^2 + AB^2) + 8 * dblEpsilon ^ 2 .
ab2 := a.Sub(b.Vector).Norm2()
maxError := (4.75*dblEpsilon*(xa2+xb2+ab2) + 8*dblEpsilon*dblEpsilon)
if math.Abs(xa2-xb2) >= ab2+maxError {
return minDist, false
}
// The minimum distance might be to a point on the edge interior. Let R
// be closest point to X that lies on the great circle through AB. Rather
// than computing the geodesic distance along the surface of the sphere,
// instead we compute the "chord length" through the sphere's interior.
//
// The squared chord length XR^2 can be expressed as XQ^2 + QR^2, where Q
// is the point X projected onto the plane through the great circle AB.
// The distance XQ^2 can be written as (X.C)^2 / |C|^2 where C = A x B.
// We ignore the QR^2 term and instead use XQ^2 as a lower bound, since it
// is faster and the corresponding distance on the Earth's surface is
// accurate to within 1% for distances up to about 1800km.
c := a.PointCross(b)
c2 := c.Norm2()
xDotC := x.Dot(c.Vector)
xDotC2 := xDotC * xDotC
if !alwaysUpdate && xDotC2 > c2*float64(minDist) {
// The closest point on the great circle AB is too far away. We need to
// test this using ">" rather than ">=" because the actual minimum bound
// on the distance is (xDotC2 / c2), which can be rounded differently
// than the (more efficient) multiplicative test above.
return minDist, false
}
// Otherwise we do the exact, more expensive test for the interior case.
// This test is very likely to succeed because of the conservative planar
// test we did initially.
//
// TODO(roberts): Ensure that the errors in test are accurately reflected in the
// minUpdateInteriorDistanceMaxError.
cx := c.Cross(x.Vector)
if a.Sub(x.Vector).Dot(cx) >= 0 || b.Sub(x.Vector).Dot(cx) <= 0 {
return minDist, false
}
// Compute the squared chord length XR^2 = XQ^2 + QR^2 (see above).
// This calculation has good accuracy for all chord lengths since it
// is based on both the dot product and cross product (rather than
// deriving one from the other). However, note that the chord length
// representation itself loses accuracy as the angle approaches π.
qr := 1 - math.Sqrt(cx.Norm2()/c2)
dist := s1.ChordAngle((xDotC2 / c2) + (qr * qr))
if !alwaysUpdate && dist >= minDist {
return minDist, false
}
return dist, true
}
// updateEdgePairMinDistance computes the minimum distance between the given
// pair of edges. If the two edges cross, the distance is zero. The cases
// a0 == a1 and b0 == b1 are handled correctly.
func updateEdgePairMinDistance(a0, a1, b0, b1 Point, minDist s1.ChordAngle) (s1.ChordAngle, bool) {
if minDist == 0 {
return 0, false
}
if CrossingSign(a0, a1, b0, b1) == Cross {
minDist = 0
return 0, true
}
// Otherwise, the minimum distance is achieved at an endpoint of at least
// one of the two edges. We ensure that all four possibilities are always checked.
//
// The calculation below computes each of the six vertex-vertex distances
// twice (this could be optimized).
var ok1, ok2, ok3, ok4 bool
minDist, ok1 = UpdateMinDistance(a0, b0, b1, minDist)
minDist, ok2 = UpdateMinDistance(a1, b0, b1, minDist)
minDist, ok3 = UpdateMinDistance(b0, a0, a1, minDist)
minDist, ok4 = UpdateMinDistance(b1, a0, a1, minDist)
return minDist, ok1 || ok2 || ok3 || ok4
}
// updateEdgePairMaxDistance reports the minimum distance between the given pair of edges.
// If one edge crosses the antipodal reflection of the other, the distance is pi.
func updateEdgePairMaxDistance(a0, a1, b0, b1 Point, maxDist s1.ChordAngle) (s1.ChordAngle, bool) {
if maxDist == s1.StraightChordAngle {
return s1.StraightChordAngle, false
}
if CrossingSign(a0, a1, Point{b0.Mul(-1)}, Point{b1.Mul(-1)}) == Cross {
return s1.StraightChordAngle, true
}
// Otherwise, the maximum distance is achieved at an endpoint of at least
// one of the two edges. We ensure that all four possibilities are always checked.
//
// The calculation below computes each of the six vertex-vertex distances
// twice (this could be optimized).
var ok1, ok2, ok3, ok4 bool
maxDist, ok1 = UpdateMaxDistance(a0, b0, b1, maxDist)
maxDist, ok2 = UpdateMaxDistance(a1, b0, b1, maxDist)
maxDist, ok3 = UpdateMaxDistance(b0, a0, a1, maxDist)
maxDist, ok4 = UpdateMaxDistance(b1, a0, a1, maxDist)
return maxDist, ok1 || ok2 || ok3 || ok4
}
// EdgePairClosestPoints returns the pair of points (a, b) that achieves the
// minimum distance between edges a0a1 and b0b1, where a is a point on a0a1 and
// b is a point on b0b1. If the two edges intersect, a and b are both equal to
// the intersection point. Handles a0 == a1 and b0 == b1 correctly.
func EdgePairClosestPoints(a0, a1, b0, b1 Point) (Point, Point) {
if CrossingSign(a0, a1, b0, b1) == Cross {
x := Intersection(a0, a1, b0, b1)
return x, x
}
// We save some work by first determining which vertex/edge pair achieves
// the minimum distance, and then computing the closest point on that edge.
var minDist s1.ChordAngle
var ok bool
minDist, ok = updateMinDistance(a0, b0, b1, minDist, true)
closestVertex := 0
if minDist, ok = UpdateMinDistance(a1, b0, b1, minDist); ok {
closestVertex = 1
}
if minDist, ok = UpdateMinDistance(b0, a0, a1, minDist); ok {
closestVertex = 2
}
if minDist, ok = UpdateMinDistance(b1, a0, a1, minDist); ok {
closestVertex = 3
}
switch closestVertex {
case 0:
return a0, Project(a0, b0, b1)
case 1:
return a1, Project(a1, b0, b1)
case 2:
return Project(b0, a0, a1), b0
case 3:
return Project(b1, a0, a1), b1
default:
panic("illegal case reached")
}
}

View File

@ -1,803 +0,0 @@
// Copyright 2019 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 (
"sort"
"github.com/golang/geo/s1"
)
// EdgeQueryOptions holds the options for controlling how EdgeQuery operates.
//
// Options can be chained together builder-style:
//
// opts = NewClosestEdgeQueryOptions().
// MaxResults(1).
// DistanceLimit(s1.ChordAngleFromAngle(3 * s1.Degree)).
// MaxError(s1.ChordAngleFromAngle(0.001 * s1.Degree))
// query = NewClosestEdgeQuery(index, opts)
//
// or set individually:
//
// opts = NewClosestEdgeQueryOptions()
// opts.IncludeInteriors(true)
//
// or just inline:
//
// query = NewClosestEdgeQuery(index, NewClosestEdgeQueryOptions().MaxResults(3))
//
// If you pass a nil as the options you get the default values for the options.
type EdgeQueryOptions struct {
common *queryOptions
}
// DistanceLimit specifies that only edges whose distance to the target is
// within, this distance should be returned. Edges whose distance is equal
// are not returned. To include values that are equal, specify the limit with
// the next largest representable distance. i.e. limit.Successor().
func (e *EdgeQueryOptions) DistanceLimit(limit s1.ChordAngle) *EdgeQueryOptions {
e.common = e.common.DistanceLimit(limit)
return e
}
// IncludeInteriors specifies whether polygon interiors should be
// included when measuring distances.
func (e *EdgeQueryOptions) IncludeInteriors(x bool) *EdgeQueryOptions {
e.common = e.common.IncludeInteriors(x)
return e
}
// UseBruteForce sets or disables the use of brute force in a query.
func (e *EdgeQueryOptions) UseBruteForce(x bool) *EdgeQueryOptions {
e.common = e.common.UseBruteForce(x)
return e
}
// MaxError specifies that edges up to dist away than the true
// matching edges may be substituted in the result set, as long as such
// edges satisfy all the remaining search criteria (such as DistanceLimit).
// This option only has an effect if MaxResults is also specified;
// otherwise all edges closer than MaxDistance will always be returned.
func (e *EdgeQueryOptions) MaxError(dist s1.ChordAngle) *EdgeQueryOptions {
e.common = e.common.MaxError(dist)
return e
}
// MaxResults specifies that at most MaxResults edges should be returned.
// This must be at least 1.
func (e *EdgeQueryOptions) MaxResults(n int) *EdgeQueryOptions {
e.common = e.common.MaxResults(n)
return e
}
// NewClosestEdgeQueryOptions returns a set of edge query options suitable
// for performing closest edge queries.
func NewClosestEdgeQueryOptions() *EdgeQueryOptions {
return &EdgeQueryOptions{
common: newQueryOptions(minDistance(0)),
}
}
// NewFurthestEdgeQueryOptions returns a set of edge query options suitable
// for performing furthest edge queries.
func NewFurthestEdgeQueryOptions() *EdgeQueryOptions {
return &EdgeQueryOptions{
common: newQueryOptions(maxDistance(0)),
}
}
// EdgeQueryResult represents an edge that meets the target criteria for the
// query. Note the following special cases:
//
// - ShapeID >= 0 && EdgeID < 0 represents the interior of a shape.
// Such results may be returned when the option IncludeInteriors is true.
//
// - ShapeID < 0 && EdgeID < 0 is returned to indicate that no edge
// satisfies the requested query options.
type EdgeQueryResult struct {
distance distance
shapeID int32
edgeID int32
}
// Distance reports the distance between the edge in this shape that satisfied
// the query's parameters.
func (e EdgeQueryResult) Distance() s1.ChordAngle { return e.distance.chordAngle() }
// ShapeID reports the ID of the Shape this result is for.
func (e EdgeQueryResult) ShapeID() int32 { return e.shapeID }
// EdgeID reports the ID of the edge in the results Shape.
func (e EdgeQueryResult) EdgeID() int32 { return e.edgeID }
// newEdgeQueryResult returns a result instance with default values.
func newEdgeQueryResult(target distanceTarget) EdgeQueryResult {
return EdgeQueryResult{
distance: target.distance().infinity(),
shapeID: -1,
edgeID: -1,
}
}
// IsInterior reports if this result represents the interior of a Shape.
func (e EdgeQueryResult) IsInterior() bool {
return e.shapeID >= 0 && e.edgeID < 0
}
// IsEmpty reports if this has no edge that satisfies the given edge query options.
// This result is only returned in one special case, namely when FindEdge() does
// not find any suitable edges.
func (e EdgeQueryResult) IsEmpty() bool {
return e.shapeID < 0
}
// Less reports if this results is less that the other first by distance,
// then by (shapeID, edgeID). This is used for sorting.
func (e EdgeQueryResult) Less(other EdgeQueryResult) bool {
if e.distance.chordAngle() != other.distance.chordAngle() {
return e.distance.less(other.distance)
}
if e.shapeID != other.shapeID {
return e.shapeID < other.shapeID
}
return e.edgeID < other.edgeID
}
// EdgeQuery is used to find the edge(s) between two geometries that match a
// given set of options. It is flexible enough so that it can be adapted to
// compute maximum distances and even potentially Hausdorff distances.
//
// By using the appropriate options, this type can answer questions such as:
//
// - Find the minimum distance between two geometries A and B.
// - Find all edges of geometry A that are within a distance D of geometry B.
// - Find the k edges of geometry A that are closest to a given point P.
//
// You can also specify whether polygons should include their interiors (i.e.,
// if a point is contained by a polygon, should the distance be zero or should
// it be measured to the polygon boundary?)
//
// The input geometries may consist of any number of points, polylines, and
// polygons (collectively referred to as "shapes"). Shapes do not need to be
// disjoint; they may overlap or intersect arbitrarily. The implementation is
// designed to be fast for both simple and complex geometries.
type EdgeQuery struct {
index *ShapeIndex
opts *queryOptions
target distanceTarget
// True if opts.maxError must be subtracted from ShapeIndex cell distances
// in order to ensure that such distances are measured conservatively. This
// is true only if the target takes advantage of maxError in order to
// return faster results, and 0 < maxError < distanceLimit.
useConservativeCellDistance bool
// The decision about whether to use the brute force algorithm is based on
// counting the total number of edges in the index. However if the index
// contains a large number of shapes, this in itself might take too long.
// So instead we only count edges up to (maxBruteForceIndexSize() + 1)
// for the current target type (stored as indexNumEdgesLimit).
indexNumEdges int
indexNumEdgesLimit int
// The distance beyond which we can safely ignore further candidate edges.
// (Candidates that are exactly at the limit are ignored; this is more
// efficient for UpdateMinDistance and should not affect clients since
// distance measurements have a small amount of error anyway.)
//
// Initially this is the same as the maximum distance specified by the user,
// but it can also be updated by the algorithm (see maybeAddResult).
distanceLimit distance
// The current set of results of the query.
results []EdgeQueryResult
// This field is true when duplicates must be avoided explicitly. This
// is achieved by maintaining a separate set keyed by (shapeID, edgeID)
// only, and checking whether each edge is in that set before computing the
// distance to it.
avoidDuplicates bool
// testedEdges tracks the set of shape and edges that have already been tested.
testedEdges map[ShapeEdgeID]uint32
// For the optimized algorihm we precompute the top-level CellIDs that
// will be added to the priority queue. There can be at most 6 of these
// cells. Essentially this is just a covering of the indexed edges, except
// that we also store pointers to the corresponding ShapeIndexCells to
// reduce the number of index seeks required.
indexCovering []CellID
indexCells []*ShapeIndexCell
// The algorithm maintains a priority queue of unprocessed CellIDs, sorted
// in increasing order of distance from the target.
queue *queryQueue
iter *ShapeIndexIterator
maxDistanceCovering []CellID
initialCells []CellID
}
// NewClosestEdgeQuery returns an EdgeQuery that is used for finding the
// closest edge(s) to a given Point, Edge, Cell, or geometry collection.
//
// You can find either the k closest edges, or all edges within a given
// radius, or both (i.e., the k closest edges up to a given maximum radius).
// E.g. to find all the edges within 5 kilometers, set the DistanceLimit in
// the options.
//
// By default *all* edges are returned, so you should always specify either
// MaxResults or DistanceLimit options or both.
//
// Note that by default, distances are measured to the boundary and interior
// of polygons. For example, if a point is inside a polygon then its distance
// is zero. To change this behavior, set the IncludeInteriors option to false.
//
// If you only need to test whether the distance is above or below a given
// threshold (e.g., 10 km), you can use the IsDistanceLess() method. This is
// much faster than actually calculating the distance with FindEdge,
// since the implementation can stop as soon as it can prove that the minimum
// distance is either above or below the threshold.
func NewClosestEdgeQuery(index *ShapeIndex, opts *EdgeQueryOptions) *EdgeQuery {
if opts == nil {
opts = NewClosestEdgeQueryOptions()
}
e := &EdgeQuery{
testedEdges: make(map[ShapeEdgeID]uint32),
index: index,
opts: opts.common,
queue: newQueryQueue(),
}
return e
}
// NewFurthestEdgeQuery returns an EdgeQuery that is used for finding the
// furthest edge(s) to a given Point, Edge, Cell, or geometry collection.
//
// The furthest edge is defined as the one which maximizes the
// distance from any point on that edge to any point on the target geometry.
//
// Similar to the example in NewClosestEdgeQuery, to find the 5 furthest edges
// from a given Point:
func NewFurthestEdgeQuery(index *ShapeIndex, opts *EdgeQueryOptions) *EdgeQuery {
if opts == nil {
opts = NewFurthestEdgeQueryOptions()
}
e := &EdgeQuery{
testedEdges: make(map[ShapeEdgeID]uint32),
index: index,
opts: opts.common,
queue: newQueryQueue(),
}
return e
}
// Reset resets the state of this EdgeQuery.
func (e *EdgeQuery) Reset() {
e.indexNumEdges = 0
e.indexNumEdgesLimit = 0
e.indexCovering = nil
e.indexCells = nil
}
// FindEdges returns the edges for the given target that satisfy the current options.
//
// Note that if opts.IncludeInteriors is true, the results may include some
// entries with edge_id == -1. This indicates that the target intersects
// the indexed polygon with the given ShapeID.
func (e *EdgeQuery) FindEdges(target distanceTarget) []EdgeQueryResult {
return e.findEdges(target, e.opts)
}
// Distance reports the distance to the target. If the index or target is empty,
// returns the EdgeQuery's maximal sentinel.
//
// Use IsDistanceLess()/IsDistanceGreater() if you only want to compare the
// distance against a threshold value, since it is often much faster.
func (e *EdgeQuery) Distance(target distanceTarget) s1.ChordAngle {
return e.findEdge(target, e.opts).Distance()
}
// IsDistanceLess reports if the distance to target is less than the given limit.
//
// This method is usually much faster than Distance(), since it is much
// less work to determine whether the minimum distance is above or below a
// threshold than it is to calculate the actual minimum distance.
//
// If you wish to check if the distance is less than or equal to the limit, use:
//
// query.IsDistanceLess(target, limit.Successor())
//
func (e *EdgeQuery) IsDistanceLess(target distanceTarget, limit s1.ChordAngle) bool {
opts := e.opts
opts = opts.MaxResults(1).
DistanceLimit(limit).
MaxError(s1.StraightChordAngle)
return !e.findEdge(target, opts).IsEmpty()
}
// IsDistanceGreater reports if the distance to target is greater than limit.
//
// This method is usually much faster than Distance, since it is much
// less work to determine whether the maximum distance is above or below a
// threshold than it is to calculate the actual maximum distance.
// If you wish to check if the distance is less than or equal to the limit, use:
//
// query.IsDistanceGreater(target, limit.Predecessor())
//
func (e *EdgeQuery) IsDistanceGreater(target distanceTarget, limit s1.ChordAngle) bool {
return e.IsDistanceLess(target, limit)
}
// IsConservativeDistanceLessOrEqual reports if the distance to target is less
// or equal to the limit, where the limit has been expanded by the maximum error
// for the distance calculation.
//
// For example, suppose that we want to test whether two geometries might
// intersect each other after they are snapped together using Builder
// (using the IdentitySnapFunction with a given "snap radius"). Since
// Builder uses exact distance predicates (s2predicates), we need to
// measure the distance between the two geometries conservatively. If the
// distance is definitely greater than "snap radius", then the geometries
// are guaranteed to not intersect after snapping.
func (e *EdgeQuery) IsConservativeDistanceLessOrEqual(target distanceTarget, limit s1.ChordAngle) bool {
return e.IsDistanceLess(target, limit.Expanded(minUpdateDistanceMaxError(limit)))
}
// IsConservativeDistanceGreaterOrEqual reports if the distance to the target is greater
// than or equal to the given limit with some small tolerance.
func (e *EdgeQuery) IsConservativeDistanceGreaterOrEqual(target distanceTarget, limit s1.ChordAngle) bool {
return e.IsDistanceGreater(target, limit.Expanded(-minUpdateDistanceMaxError(limit)))
}
// findEdges returns the closest edges to the given target that satisfy the given options.
//
// Note that if opts.includeInteriors is true, the results may include some
// entries with edgeID == -1. This indicates that the target intersects the
// indexed polygon with the given shapeID.
func (e *EdgeQuery) findEdges(target distanceTarget, opts *queryOptions) []EdgeQueryResult {
e.findEdgesInternal(target, opts)
// TODO(roberts): Revisit this if there is a heap or other sorted and
// uniquing datastructure we can use instead of just a slice.
e.results = sortAndUniqueResults(e.results)
if len(e.results) > e.opts.maxResults {
e.results = e.results[:e.opts.maxResults]
}
return e.results
}
func sortAndUniqueResults(results []EdgeQueryResult) []EdgeQueryResult {
if len(results) <= 1 {
return results
}
sort.Slice(results, func(i, j int) bool { return results[i].Less(results[j]) })
j := 0
for i := 1; i < len(results); i++ {
if results[j] == results[i] {
continue
}
j++
results[j] = results[i]
}
return results[:j+1]
}
// findEdge is a convenience method that returns exactly one edge, and if no
// edges satisfy the given search criteria, then a default Result is returned.
//
// This is primarily to ease the usage of a number of the methods in the DistanceTargets
// and in EdgeQuery.
func (e *EdgeQuery) findEdge(target distanceTarget, opts *queryOptions) EdgeQueryResult {
opts.MaxResults(1)
e.findEdges(target, opts)
if len(e.results) > 0 {
return e.results[0]
}
return newEdgeQueryResult(target)
}
// findEdgesInternal does the actual work for find edges that match the given options.
func (e *EdgeQuery) findEdgesInternal(target distanceTarget, opts *queryOptions) {
e.target = target
e.opts = opts
e.testedEdges = make(map[ShapeEdgeID]uint32)
e.distanceLimit = target.distance().fromChordAngle(opts.distanceLimit)
e.results = make([]EdgeQueryResult, 0)
if e.distanceLimit == target.distance().zero() {
return
}
if opts.includeInteriors {
shapeIDs := map[int32]struct{}{}
e.target.visitContainingShapes(e.index, func(containingShape Shape, targetPoint Point) bool {
shapeIDs[e.index.idForShape(containingShape)] = struct{}{}
return len(shapeIDs) < opts.maxResults
})
for shapeID := range shapeIDs {
e.addResult(EdgeQueryResult{target.distance().zero(), shapeID, -1})
}
if e.distanceLimit == target.distance().zero() {
return
}
}
// If maxError > 0 and the target takes advantage of this, then we may
// need to adjust the distance estimates to ShapeIndex cells to ensure
// that they are always a lower bound on the true distance. For example,
// suppose max_distance == 100, maxError == 30, and we compute the distance
// to the target from some cell C0 as d(C0) == 80. Then because the target
// takes advantage of maxError, the true distance could be as low as 50.
// In order not to miss edges contained by such cells, we need to subtract
// maxError from the distance estimates. This behavior is controlled by
// the useConservativeCellDistance flag.
//
// However there is one important case where this adjustment is not
// necessary, namely when distanceLimit < maxError, This is because
// maxError only affects the algorithm once at least maxEdges edges
// have been found that satisfy the given distance limit. At that point,
// maxError is subtracted from distanceLimit in order to ensure that
// any further matches are closer by at least that amount. But when
// distanceLimit < maxError, this reduces the distance limit to 0,
// i.e. all remaining candidate cells and edges can safely be discarded.
// (This is how IsDistanceLess() and friends are implemented.)
targetUsesMaxError := opts.maxError != target.distance().zero().chordAngle() &&
e.target.setMaxError(opts.maxError)
// Note that we can't compare maxError and distanceLimit directly
// because one is a Delta and one is a Distance. Instead we subtract them.
e.useConservativeCellDistance = targetUsesMaxError &&
(e.distanceLimit == target.distance().infinity() ||
target.distance().zero().less(e.distanceLimit.sub(target.distance().fromChordAngle(opts.maxError))))
// Use the brute force algorithm if the index is small enough. To avoid
// spending too much time counting edges when there are many shapes, we stop
// counting once there are too many edges. We may need to recount the edges
// if we later see a target with a larger brute force edge threshold.
minOptimizedEdges := e.target.maxBruteForceIndexSize() + 1
if minOptimizedEdges > e.indexNumEdgesLimit && e.indexNumEdges >= e.indexNumEdgesLimit {
e.indexNumEdges = e.index.NumEdgesUpTo(minOptimizedEdges)
e.indexNumEdgesLimit = minOptimizedEdges
}
if opts.useBruteForce || e.indexNumEdges < minOptimizedEdges {
// The brute force algorithm already considers each edge exactly once.
e.avoidDuplicates = false
e.findEdgesBruteForce()
} else {
// If the target takes advantage of maxError then we need to avoid
// duplicate edges explicitly. (Otherwise it happens automatically.)
e.avoidDuplicates = targetUsesMaxError && opts.maxResults > 1
e.findEdgesOptimized()
}
}
func (e *EdgeQuery) addResult(r EdgeQueryResult) {
e.results = append(e.results, r)
if e.opts.maxResults == 1 {
// Optimization for the common case where only the closest edge is wanted.
e.distanceLimit = r.distance.sub(e.target.distance().fromChordAngle(e.opts.maxError))
}
// TODO(roberts): Add the other if/else cases when a different data structure
// is used for the results.
}
func (e *EdgeQuery) maybeAddResult(shape Shape, edgeID int32) {
if _, ok := e.testedEdges[ShapeEdgeID{e.index.idForShape(shape), edgeID}]; e.avoidDuplicates && !ok {
return
}
edge := shape.Edge(int(edgeID))
dist := e.distanceLimit
if dist, ok := e.target.updateDistanceToEdge(edge, dist); ok {
e.addResult(EdgeQueryResult{dist, e.index.idForShape(shape), edgeID})
}
}
func (e *EdgeQuery) findEdgesBruteForce() {
// Range over all shapes in the index. Does order matter here? if so
// switch to for i = 0 .. n?
for _, shape := range e.index.shapes {
// TODO(roberts): can this happen if we are only ranging over current entries?
if shape == nil {
continue
}
for edgeID := int32(0); edgeID < int32(shape.NumEdges()); edgeID++ {
e.maybeAddResult(shape, edgeID)
}
}
}
func (e *EdgeQuery) findEdgesOptimized() {
e.initQueue()
// Repeatedly find the closest Cell to "target" and either split it into
// its four children or process all of its edges.
for e.queue.size() > 0 {
// We need to copy the top entry before removing it, and we need to
// remove it before adding any new entries to the queue.
entry := e.queue.pop()
if !entry.distance.less(e.distanceLimit) {
e.queue.reset() // Clear any remaining entries.
break
}
// If this is already known to be an index cell, just process it.
if entry.indexCell != nil {
e.processEdges(entry)
continue
}
// Otherwise split the cell into its four children. Before adding a
// child back to the queue, we first check whether it is empty. We do
// this in two seek operations rather than four by seeking to the key
// between children 0 and 1 and to the key between children 2 and 3.
id := entry.id
ch := id.Children()
e.iter.seek(ch[1].RangeMin())
if !e.iter.Done() && e.iter.CellID() <= ch[1].RangeMax() {
e.processOrEnqueueCell(ch[1])
}
if e.iter.Prev() && e.iter.CellID() >= id.RangeMin() {
e.processOrEnqueueCell(ch[0])
}
e.iter.seek(ch[3].RangeMin())
if !e.iter.Done() && e.iter.CellID() <= id.RangeMax() {
e.processOrEnqueueCell(ch[3])
}
if e.iter.Prev() && e.iter.CellID() >= ch[2].RangeMin() {
e.processOrEnqueueCell(ch[2])
}
}
}
func (e *EdgeQuery) processOrEnqueueCell(id CellID) {
if e.iter.CellID() == id {
e.processOrEnqueue(id, e.iter.IndexCell())
} else {
e.processOrEnqueue(id, nil)
}
}
func (e *EdgeQuery) initQueue() {
if len(e.indexCovering) == 0 {
// We delay iterator initialization until now to make queries on very
// small indexes a bit faster (i.e., where brute force is used).
e.iter = NewShapeIndexIterator(e.index)
}
// Optimization: if the user is searching for just the closest edge, and the
// center of the target's bounding cap happens to intersect an index cell,
// then we try to limit the search region to a small disc by first
// processing the edges in that cell. This sets distance_limit_ based on
// the closest edge in that cell, which we can then use to limit the search
// area. This means that the cell containing "target" will be processed
// twice, but in general this is still faster.
//
// TODO(roberts): Even if the cap center is not contained, we could still
// process one or both of the adjacent index cells in CellID order,
// provided that those cells are closer than distanceLimit.
cb := e.target.capBound()
if cb.IsEmpty() {
return // Empty target.
}
if e.opts.maxResults == 1 && e.iter.LocatePoint(cb.Center()) {
e.processEdges(&queryQueueEntry{
distance: e.target.distance().zero(),
id: e.iter.CellID(),
indexCell: e.iter.IndexCell(),
})
// Skip the rest of the algorithm if we found an intersecting edge.
if e.distanceLimit == e.target.distance().zero() {
return
}
}
if len(e.indexCovering) == 0 {
e.initCovering()
}
if e.distanceLimit == e.target.distance().infinity() {
// Start with the precomputed index covering.
for i := range e.indexCovering {
e.processOrEnqueue(e.indexCovering[i], e.indexCells[i])
}
} else {
// Compute a covering of the search disc and intersect it with the
// precomputed index covering.
coverer := &RegionCoverer{MaxCells: 4, LevelMod: 1, MaxLevel: maxLevel}
radius := cb.Radius() + e.distanceLimit.chordAngleBound().Angle()
searchCB := CapFromCenterAngle(cb.Center(), radius)
maxDistCover := coverer.FastCovering(searchCB)
e.initialCells = CellUnionFromIntersection(e.indexCovering, maxDistCover)
// Now we need to clean up the initial cells to ensure that they all
// contain at least one cell of the ShapeIndex. (Some may not intersect
// the index at all, while other may be descendants of an index cell.)
i, j := 0, 0
for i < len(e.initialCells) {
idI := e.initialCells[i]
// Find the top-level cell that contains this initial cell.
for e.indexCovering[j].RangeMax() < idI {
j++
}
idJ := e.indexCovering[j]
if idI == idJ {
// This initial cell is one of the top-level cells. Use the
// precomputed ShapeIndexCell pointer to avoid an index seek.
e.processOrEnqueue(idJ, e.indexCells[j])
i++
j++
} else {
// This initial cell is a proper descendant of a top-level cell.
// Check how it is related to the cells of the ShapeIndex.
r := e.iter.LocateCellID(idI)
if r == Indexed {
// This cell is a descendant of an index cell.
// Enqueue it and skip any other initial cells
// that are also descendants of this cell.
e.processOrEnqueue(e.iter.CellID(), e.iter.IndexCell())
lastID := e.iter.CellID().RangeMax()
for i < len(e.initialCells) && e.initialCells[i] <= lastID {
i++
}
} else {
// Enqueue the cell only if it contains at least one index cell.
if r == Subdivided {
e.processOrEnqueue(idI, nil)
}
i++
}
}
}
}
}
func (e *EdgeQuery) initCovering() {
// Find the range of Cells spanned by the index and choose a level such
// that the entire index can be covered with just a few cells. These are
// the "top-level" cells. There are two cases:
//
// - If the index spans more than one face, then there is one top-level cell
// per spanned face, just big enough to cover the index cells on that face.
//
// - If the index spans only one face, then we find the smallest cell "C"
// that covers the index cells on that face (just like the case above).
// Then for each of the 4 children of "C", if the child contains any index
// cells then we create a top-level cell that is big enough to just fit
// those index cells (i.e., shrinking the child as much as possible to fit
// its contents). This essentially replicates what would happen if we
// started with "C" as the top-level cell, since "C" would immediately be
// split, except that we take the time to prune the children further since
// this will save work on every subsequent query.
e.indexCovering = make([]CellID, 0, 6)
// TODO(roberts): Use a single iterator below and save position
// information using pair {CellID, ShapeIndexCell}.
next := NewShapeIndexIterator(e.index, IteratorBegin)
last := NewShapeIndexIterator(e.index, IteratorEnd)
last.Prev()
if next.CellID() != last.CellID() {
// The index has at least two cells. Choose a level such that the entire
// index can be spanned with at most 6 cells (if the index spans multiple
// faces) or 4 cells (it the index spans a single face).
level, ok := next.CellID().CommonAncestorLevel(last.CellID())
if !ok {
level = 0
} else {
level++
}
// Visit each potential top-level cell except the last (handled below).
lastID := last.CellID().Parent(level)
for id := next.CellID().Parent(level); id != lastID; id = id.Next() {
// Skip any top-level cells that don't contain any index cells.
if id.RangeMax() < next.CellID() {
continue
}
// Find the range of index cells contained by this top-level cell and
// then shrink the cell if necessary so that it just covers them.
cellFirst := next.clone()
next.seek(id.RangeMax().Next())
cellLast := next.clone()
cellLast.Prev()
e.addInitialRange(cellFirst, cellLast)
break
}
}
e.addInitialRange(next, last)
}
// addInitialRange adds an entry to the indexCovering and indexCells that covers the given
// inclusive range of cells.
//
// This requires that first and last cells have a common ancestor.
func (e *EdgeQuery) addInitialRange(first, last *ShapeIndexIterator) {
if first.CellID() == last.CellID() {
// The range consists of a single index cell.
e.indexCovering = append(e.indexCovering, first.CellID())
e.indexCells = append(e.indexCells, first.IndexCell())
} else {
// Add the lowest common ancestor of the given range.
level, _ := first.CellID().CommonAncestorLevel(last.CellID())
e.indexCovering = append(e.indexCovering, first.CellID().Parent(level))
e.indexCells = append(e.indexCells, nil)
}
}
// processEdges processes all the edges of the given index cell.
func (e *EdgeQuery) processEdges(entry *queryQueueEntry) {
for _, clipped := range entry.indexCell.shapes {
shape := e.index.Shape(clipped.shapeID)
for j := 0; j < clipped.numEdges(); j++ {
e.maybeAddResult(shape, int32(clipped.edges[j]))
}
}
}
// processOrEnqueue the given cell id and indexCell.
func (e *EdgeQuery) processOrEnqueue(id CellID, indexCell *ShapeIndexCell) {
if indexCell != nil {
// If this index cell has only a few edges, then it is faster to check
// them directly rather than computing the minimum distance to the Cell
// and inserting it into the queue.
const minEdgesToEnqueue = 10
numEdges := indexCell.numEdges()
if numEdges == 0 {
return
}
if numEdges < minEdgesToEnqueue {
// Set "distance" to zero to avoid the expense of computing it.
e.processEdges(&queryQueueEntry{
distance: e.target.distance().zero(),
id: id,
indexCell: indexCell,
})
return
}
}
// Otherwise compute the minimum distance to any point in the cell and add
// it to the priority queue.
cell := CellFromCellID(id)
dist := e.distanceLimit
var ok bool
if dist, ok = e.target.updateDistanceToCell(cell, dist); !ok {
return
}
if e.useConservativeCellDistance {
// Ensure that "distance" is a lower bound on the true distance to the cell.
dist = dist.sub(e.target.distance().fromChordAngle(e.opts.maxError))
}
e.queue.push(&queryQueueEntry{
distance: dist,
id: id,
indexCell: indexCell,
})
}
// TODO(roberts): Remaining pieces
// GetEdge
// Project

View File

@ -1,291 +0,0 @@
// Copyright 2018 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 (
"github.com/golang/geo/r2"
"github.com/golang/geo/s1"
)
// Tessellation is implemented by subdividing the edge until the estimated
// maximum error is below the given tolerance. Estimating error is a hard
// problem, especially when the only methods available are point evaluation of
// the projection and its inverse. (These are the only methods that
// Projection provides, which makes it easier and less error-prone to
// implement new projections.)
//
// One technique that significantly increases robustness is to treat the
// geodesic and projected edges as parametric curves rather than geometric ones.
// Given a spherical edge AB and a projection p:S2->R2, let f(t) be the
// normalized arc length parametrization of AB and let g(t) be the normalized
// arc length parameterization of the projected edge p(A)p(B). (In other words,
// f(0)=A, f(1)=B, g(0)=p(A), g(1)=p(B).) We now define the geometric error as
// the maximum distance from the point p^-1(g(t)) to the geodesic edge AB for
// any t in [0,1], where p^-1 denotes the inverse projection. In other words,
// the geometric error is the maximum distance from any point on the projected
// edge (mapped back onto the sphere) to the geodesic edge AB. On the other
// hand we define the parametric error as the maximum distance between the
// points f(t) and p^-1(g(t)) for any t in [0,1], i.e. the maximum distance
// (measured on the sphere) between the geodesic and projected points at the
// same interpolation fraction t.
//
// The easiest way to estimate the parametric error is to simply evaluate both
// edges at their midpoints and measure the distance between them (the "midpoint
// method"). This is very fast and works quite well for most edges, however it
// has one major drawback: it doesn't handle points of inflection (i.e., points
// where the curvature changes sign). For example, edges in the Mercator and
// Plate Carree projections always curve towards the equator relative to the
// corresponding geodesic edge, so in these projections there is a point of
// inflection whenever the projected edge crosses the equator. The worst case
// occurs when the edge endpoints have different longitudes but the same
// absolute latitude, since in that case the error is non-zero but the edges
// have exactly the same midpoint (on the equator).
//
// One solution to this problem is to split the input edges at all inflection
// points (i.e., along the equator in the case of the Mercator and Plate Carree
// projections). However for general projections these inflection points can
// occur anywhere on the sphere (e.g., consider the Transverse Mercator
// projection). This could be addressed by adding methods to the S2Projection
// interface to split edges at inflection points but this would make it harder
// and more error-prone to implement new projections.
//
// Another problem with this approach is that the midpoint method sometimes
// underestimates the true error even when edges do not cross the equator.
// For the Plate Carree and Mercator projections, the midpoint method can
// underestimate the error by up to 3%.
//
// Both of these problems can be solved as follows. We assume that the error
// can be modeled as a convex combination of two worst-case functions, one
// where the error is maximized at the edge midpoint and another where the
// error is *minimized* (i.e., zero) at the edge midpoint. For example, we
// could choose these functions as:
//
// E1(x) = 1 - x^2
// E2(x) = x * (1 - x^2)
//
// where for convenience we use an interpolation parameter "x" in the range
// [-1, 1] rather than the original "t" in the range [0, 1]. Note that both
// error functions must have roots at x = {-1, 1} since the error must be zero
// at the edge endpoints. E1 is simply a parabola whose maximum value is 1
// attained at x = 0, while E2 is a cubic with an additional root at x = 0,
// and whose maximum value is 2 * sqrt(3) / 9 attained at x = 1 / sqrt(3).
//
// Next, it is convenient to scale these functions so that the both have a
// maximum value of 1. E1 already satisfies this requirement, and we simply
// redefine E2 as
//
// E2(x) = x * (1 - x^2) / (2 * sqrt(3) / 9)
//
// Now define x0 to be the point where these two functions intersect, i.e. the
// point in the range (-1, 1) where E1(x0) = E2(x0). This value has the very
// convenient property that if we evaluate the actual error E(x0), then the
// maximum error on the entire interval [-1, 1] is bounded by
//
// E(x) <= E(x0) / E1(x0)
//
// since whether the error is modeled using E1 or E2, the resulting function
// has the same maximum value (namely E(x0) / E1(x0)). If it is modeled as
// some other convex combination of E1 and E2, the maximum value can only
// decrease.
//
// Finally, since E2 is not symmetric about the y-axis, we must also allow for
// the possibility that the error is a convex combination of E1 and -E2. This
// can be handled by evaluating the error at E(-x0) as well, and then
// computing the final error bound as
//
// E(x) <= max(E(x0), E(-x0)) / E1(x0) .
//
// Effectively, this method is simply evaluating the error at two points about
// 1/3 and 2/3 of the way along the edges, and then scaling the maximum of
// these two errors by a constant factor. Intuitively, the reason this works
// is that if the two edges cross somewhere in the interior, then at least one
// of these points will be far from the crossing.
//
// The actual algorithm implemented below has some additional refinements.
// First, edges longer than 90 degrees are always subdivided; this avoids
// various unusual situations that can happen with very long edges, and there
// is really no reason to avoid adding vertices to edges that are so long.
//
// Second, the error function E1 above needs to be modified to take into
// account spherical distortions. (It turns out that spherical distortions are
// beneficial in the case of E2, i.e. they only make its error estimates
// slightly more conservative.) To do this, we model E1 as the maximum error
// in a Plate Carree edge of length 90 degrees or less. This turns out to be
// an edge from 45:-90 to 45:90 (in lat:lng format). The corresponding error
// as a function of "x" in the range [-1, 1] can be computed as the distance
// between the Plate Caree edge point (45, 90 * x) and the geodesic
// edge point (90 - 45 * abs(x), 90 * sgn(x)). Using the Haversine formula,
// the corresponding function E1 (normalized to have a maximum value of 1) is:
//
// E1(x) =
// asin(sqrt(sin(Pi / 8 * (1 - x)) ^ 2 +
// sin(Pi / 4 * (1 - x)) ^ 2 * cos(Pi / 4) * sin(Pi / 4 * x))) /
// asin(sqrt((1 - 1 / sqrt(2)) / 2))
//
// Note that this function does not need to be evaluated at runtime, it
// simply affects the calculation of the value x0 where E1(x0) = E2(x0)
// and the corresponding scaling factor C = 1 / E1(x0).
//
// ------------------------------------------------------------------
//
// In the case of the Mercator and Plate Carree projections this strategy
// produces a conservative upper bound (verified using 10 million random
// edges). Furthermore the bound is nearly tight; the scaling constant is
// C = 1.19289, whereas the maximum observed value was 1.19254.
//
// Compared to the simpler midpoint evaluation method, this strategy requires
// more function evaluations (currently twice as many, but with a smarter
// tessellation algorithm it will only be 50% more). It also results in a
// small amount of additional tessellation (about 1.5%) compared to the
// midpoint method, but this is due almost entirely to the fact that the
// midpoint method does not yield conservative error estimates.
//
// For random edges with a tolerance of 1 meter, the expected amount of
// overtessellation is as follows:
//
// Midpoint Method Cubic Method
// Plate Carree 1.8% 3.0%
// Mercator 15.8% 17.4%
const (
// tessellationInterpolationFraction is the fraction at which the two edges
// are evaluated in order to measure the error between them. (Edges are
// evaluated at two points measured this fraction from either end.)
tessellationInterpolationFraction = 0.31215691082248312
tessellationScaleFactor = 0.83829992569888509
// minTessellationTolerance is the minimum supported tolerance (which
// corresponds to a distance less than 1 micrometer on the Earth's
// surface, but is still much larger than the expected projection and
// interpolation errors).
minTessellationTolerance s1.Angle = 1e-13
)
// EdgeTessellator converts an edge in a given projection (e.g., Mercator) into
// a chain of spherical geodesic edges such that the maximum distance between
// the original edge and the geodesic edge chain is at most the requested
// tolerance. Similarly, it can convert a spherical geodesic edge into a chain
// of edges in a given 2D projection such that the maximum distance between the
// geodesic edge and the chain of projected edges is at most the requested tolerance.
//
// Method | Input | Output
// ------------|------------------------|-----------------------
// Projected | S2 geodesics | Planar projected edges
// Unprojected | Planar projected edges | S2 geodesics
type EdgeTessellator struct {
projection Projection
// The given tolerance scaled by a constant fraction so that it can be
// compared against the result returned by estimateMaxError.
scaledTolerance s1.ChordAngle
}
// NewEdgeTessellator creates a new edge tessellator for the given projection and tolerance.
func NewEdgeTessellator(p Projection, tolerance s1.Angle) *EdgeTessellator {
return &EdgeTessellator{
projection: p,
scaledTolerance: s1.ChordAngleFromAngle(maxAngle(tolerance, minTessellationTolerance)),
}
}
// AppendProjected converts the spherical geodesic edge AB to a chain of planar edges
// in the given projection and returns the corresponding vertices.
//
// If the given projection has one or more coordinate axes that wrap, then
// every vertex's coordinates will be as close as possible to the previous
// vertex's coordinates. Note that this may yield vertices whose
// coordinates are outside the usual range. For example, tessellating the
// edge (0:170, 0:-170) (in lat:lng notation) yields (0:170, 0:190).
func (e *EdgeTessellator) AppendProjected(a, b Point, vertices []r2.Point) []r2.Point {
pa := e.projection.Project(a)
if len(vertices) == 0 {
vertices = []r2.Point{pa}
} else {
pa = e.projection.WrapDestination(vertices[len(vertices)-1], pa)
}
pb := e.projection.Project(b)
return e.appendProjected(pa, a, pb, b, vertices)
}
// appendProjected splits a geodesic edge AB as necessary and returns the
// projected vertices appended to the given vertices.
//
// The maximum recursion depth is (math.Pi / minTessellationTolerance) < 45
func (e *EdgeTessellator) appendProjected(pa r2.Point, a Point, pbIn r2.Point, b Point, vertices []r2.Point) []r2.Point {
pb := e.projection.WrapDestination(pa, pbIn)
if e.estimateMaxError(pa, a, pb, b) <= e.scaledTolerance {
return append(vertices, pb)
}
mid := Point{a.Add(b.Vector).Normalize()}
pmid := e.projection.WrapDestination(pa, e.projection.Project(mid))
vertices = e.appendProjected(pa, a, pmid, mid, vertices)
return e.appendProjected(pmid, mid, pb, b, vertices)
}
// AppendUnprojected converts the planar edge AB in the given projection to a chain of
// spherical geodesic edges and returns the vertices.
//
// Note that to construct a Loop, you must eliminate the duplicate first and last
// vertex. Note also that if the given projection involves coordinate wrapping
// (e.g. across the 180 degree meridian) then the first and last vertices may not
// be exactly the same.
func (e *EdgeTessellator) AppendUnprojected(pa, pb r2.Point, vertices []Point) []Point {
a := e.projection.Unproject(pa)
b := e.projection.Unproject(pb)
if len(vertices) == 0 {
vertices = []Point{a}
}
// Note that coordinate wrapping can create a small amount of error. For
// example in the edge chain "0:-175, 0:179, 0:-177", the first edge is
// transformed into "0:-175, 0:-181" while the second is transformed into
// "0:179, 0:183". The two coordinate pairs for the middle vertex
// ("0:-181" and "0:179") may not yield exactly the same S2Point.
return e.appendUnprojected(pa, a, pb, b, vertices)
}
// appendUnprojected interpolates a projected edge and appends the corresponding
// points on the sphere.
func (e *EdgeTessellator) appendUnprojected(pa r2.Point, a Point, pbIn r2.Point, b Point, vertices []Point) []Point {
pb := e.projection.WrapDestination(pa, pbIn)
if e.estimateMaxError(pa, a, pb, b) <= e.scaledTolerance {
return append(vertices, b)
}
pmid := e.projection.Interpolate(0.5, pa, pb)
mid := e.projection.Unproject(pmid)
vertices = e.appendUnprojected(pa, a, pmid, mid, vertices)
return e.appendUnprojected(pmid, mid, pb, b, vertices)
}
func (e *EdgeTessellator) estimateMaxError(pa r2.Point, a Point, pb r2.Point, b Point) s1.ChordAngle {
// See the algorithm description at the top of this file.
// We always tessellate edges longer than 90 degrees on the sphere, since the
// approximation below is not robust enough to handle such edges.
if a.Dot(b.Vector) < -1e-14 {
return s1.InfChordAngle()
}
t1 := tessellationInterpolationFraction
t2 := 1 - tessellationInterpolationFraction
mid1 := Interpolate(t1, a, b)
mid2 := Interpolate(t2, a, b)
pmid1 := e.projection.Unproject(e.projection.Interpolate(t1, pa, pb))
pmid2 := e.projection.Unproject(e.projection.Interpolate(t2, pa, pb))
return maxChordAngle(ChordAngleBetweenPoints(mid1, pmid1), ChordAngleBetweenPoints(mid2, pmid2))
}

View File

@ -1,224 +0,0 @@
// 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
import (
"encoding/binary"
"io"
"math"
)
const (
// encodingVersion is the current version of the encoding
// format that is compatible with C++ and other S2 libraries.
encodingVersion = int8(1)
// encodingCompressedVersion is the current version of the
// compressed format.
encodingCompressedVersion = int8(4)
)
// encoder handles the specifics of encoding for S2 types.
type encoder struct {
w io.Writer // the real writer passed to Encode
err error
}
func (e *encoder) writeUvarint(x uint64) {
if e.err != nil {
return
}
var buf [binary.MaxVarintLen64]byte
n := binary.PutUvarint(buf[:], x)
_, e.err = e.w.Write(buf[:n])
}
func (e *encoder) writeBool(x bool) {
if e.err != nil {
return
}
var val int8
if x {
val = 1
}
e.err = binary.Write(e.w, binary.LittleEndian, val)
}
func (e *encoder) writeInt8(x int8) {
if e.err != nil {
return
}
e.err = binary.Write(e.w, binary.LittleEndian, x)
}
func (e *encoder) writeInt16(x int16) {
if e.err != nil {
return
}
e.err = binary.Write(e.w, binary.LittleEndian, x)
}
func (e *encoder) writeInt32(x int32) {
if e.err != nil {
return
}
e.err = binary.Write(e.w, binary.LittleEndian, x)
}
func (e *encoder) writeInt64(x int64) {
if e.err != nil {
return
}
e.err = binary.Write(e.w, binary.LittleEndian, x)
}
func (e *encoder) writeUint8(x uint8) {
if e.err != nil {
return
}
_, e.err = e.w.Write([]byte{x})
}
func (e *encoder) writeUint32(x uint32) {
if e.err != nil {
return
}
e.err = binary.Write(e.w, binary.LittleEndian, x)
}
func (e *encoder) writeUint64(x uint64) {
if e.err != nil {
return
}
e.err = binary.Write(e.w, binary.LittleEndian, x)
}
func (e *encoder) writeFloat32(x float32) {
if e.err != nil {
return
}
e.err = binary.Write(e.w, binary.LittleEndian, x)
}
func (e *encoder) writeFloat64(x float64) {
if e.err != nil {
return
}
e.err = binary.Write(e.w, binary.LittleEndian, x)
}
type byteReader interface {
io.Reader
io.ByteReader
}
// byteReaderAdapter embellishes an io.Reader with a ReadByte method,
// so that it implements the io.ByteReader interface.
type byteReaderAdapter struct {
io.Reader
}
func (b byteReaderAdapter) ReadByte() (byte, error) {
buf := []byte{0}
_, err := io.ReadFull(b, buf)
return buf[0], err
}
func asByteReader(r io.Reader) byteReader {
if br, ok := r.(byteReader); ok {
return br
}
return byteReaderAdapter{r}
}
type decoder struct {
r byteReader // the real reader passed to Decode
err error
buf []byte
}
// Get a buffer of size 8, to avoid allocating over and over.
func (d *decoder) buffer() []byte {
if d.buf == nil {
d.buf = make([]byte, 8)
}
return d.buf
}
func (d *decoder) readBool() (x bool) {
if d.err != nil {
return
}
var val int8
d.err = binary.Read(d.r, binary.LittleEndian, &val)
return val == 1
}
func (d *decoder) readInt8() (x int8) {
if d.err != nil {
return
}
d.err = binary.Read(d.r, binary.LittleEndian, &x)
return
}
func (d *decoder) readInt64() (x int64) {
if d.err != nil {
return
}
d.err = binary.Read(d.r, binary.LittleEndian, &x)
return
}
func (d *decoder) readUint8() (x uint8) {
if d.err != nil {
return
}
x, d.err = d.r.ReadByte()
return
}
func (d *decoder) readUint32() (x uint32) {
if d.err != nil {
return
}
d.err = binary.Read(d.r, binary.LittleEndian, &x)
return
}
func (d *decoder) readUint64() (x uint64) {
if d.err != nil {
return
}
d.err = binary.Read(d.r, binary.LittleEndian, &x)
return
}
func (d *decoder) readFloat64() float64 {
if d.err != nil {
return 0
}
buf := d.buffer()
_, d.err = io.ReadFull(d.r, buf)
return math.Float64frombits(binary.LittleEndian.Uint64(buf))
}
func (d *decoder) readUvarint() (x uint64) {
if d.err != nil {
return
}
x, d.err = binary.ReadUvarint(d.r)
return
}

View File

@ -1,143 +0,0 @@
// 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
/*
The lookup table below can convert a sequence of interleaved 8 bits into
non-interleaved 4 bits. The table can convert both odd and even bits at the
same time, and lut[x & 0x55] converts the even bits (bits 0, 2, 4 and 6),
while lut[x & 0xaa] converts the odd bits (bits 1, 3, 5 and 7).
The lookup table below was generated using the following python code:
def deinterleave(bits):
if bits == 0: return 0
if bits < 4: return 1
return deinterleave(bits / 4) * 2 + deinterleave(bits & 3)
for i in range(256): print "0x%x," % deinterleave(i),
*/
var deinterleaveLookup = [256]uint32{
0x0, 0x1, 0x1, 0x1, 0x2, 0x3, 0x3, 0x3,
0x2, 0x3, 0x3, 0x3, 0x2, 0x3, 0x3, 0x3,
0x4, 0x5, 0x5, 0x5, 0x6, 0x7, 0x7, 0x7,
0x6, 0x7, 0x7, 0x7, 0x6, 0x7, 0x7, 0x7,
0x4, 0x5, 0x5, 0x5, 0x6, 0x7, 0x7, 0x7,
0x6, 0x7, 0x7, 0x7, 0x6, 0x7, 0x7, 0x7,
0x4, 0x5, 0x5, 0x5, 0x6, 0x7, 0x7, 0x7,
0x6, 0x7, 0x7, 0x7, 0x6, 0x7, 0x7, 0x7,
0x8, 0x9, 0x9, 0x9, 0xa, 0xb, 0xb, 0xb,
0xa, 0xb, 0xb, 0xb, 0xa, 0xb, 0xb, 0xb,
0xc, 0xd, 0xd, 0xd, 0xe, 0xf, 0xf, 0xf,
0xe, 0xf, 0xf, 0xf, 0xe, 0xf, 0xf, 0xf,
0xc, 0xd, 0xd, 0xd, 0xe, 0xf, 0xf, 0xf,
0xe, 0xf, 0xf, 0xf, 0xe, 0xf, 0xf, 0xf,
0xc, 0xd, 0xd, 0xd, 0xe, 0xf, 0xf, 0xf,
0xe, 0xf, 0xf, 0xf, 0xe, 0xf, 0xf, 0xf,
0x8, 0x9, 0x9, 0x9, 0xa, 0xb, 0xb, 0xb,
0xa, 0xb, 0xb, 0xb, 0xa, 0xb, 0xb, 0xb,
0xc, 0xd, 0xd, 0xd, 0xe, 0xf, 0xf, 0xf,
0xe, 0xf, 0xf, 0xf, 0xe, 0xf, 0xf, 0xf,
0xc, 0xd, 0xd, 0xd, 0xe, 0xf, 0xf, 0xf,
0xe, 0xf, 0xf, 0xf, 0xe, 0xf, 0xf, 0xf,
0xc, 0xd, 0xd, 0xd, 0xe, 0xf, 0xf, 0xf,
0xe, 0xf, 0xf, 0xf, 0xe, 0xf, 0xf, 0xf,
0x8, 0x9, 0x9, 0x9, 0xa, 0xb, 0xb, 0xb,
0xa, 0xb, 0xb, 0xb, 0xa, 0xb, 0xb, 0xb,
0xc, 0xd, 0xd, 0xd, 0xe, 0xf, 0xf, 0xf,
0xe, 0xf, 0xf, 0xf, 0xe, 0xf, 0xf, 0xf,
0xc, 0xd, 0xd, 0xd, 0xe, 0xf, 0xf, 0xf,
0xe, 0xf, 0xf, 0xf, 0xe, 0xf, 0xf, 0xf,
0xc, 0xd, 0xd, 0xd, 0xe, 0xf, 0xf, 0xf,
0xe, 0xf, 0xf, 0xf, 0xe, 0xf, 0xf, 0xf,
}
// deinterleaveUint32 decodes the interleaved values.
func deinterleaveUint32(code uint64) (uint32, uint32) {
x := (deinterleaveLookup[code&0x55]) |
(deinterleaveLookup[(code>>8)&0x55] << 4) |
(deinterleaveLookup[(code>>16)&0x55] << 8) |
(deinterleaveLookup[(code>>24)&0x55] << 12) |
(deinterleaveLookup[(code>>32)&0x55] << 16) |
(deinterleaveLookup[(code>>40)&0x55] << 20) |
(deinterleaveLookup[(code>>48)&0x55] << 24) |
(deinterleaveLookup[(code>>56)&0x55] << 28)
y := (deinterleaveLookup[code&0xaa]) |
(deinterleaveLookup[(code>>8)&0xaa] << 4) |
(deinterleaveLookup[(code>>16)&0xaa] << 8) |
(deinterleaveLookup[(code>>24)&0xaa] << 12) |
(deinterleaveLookup[(code>>32)&0xaa] << 16) |
(deinterleaveLookup[(code>>40)&0xaa] << 20) |
(deinterleaveLookup[(code>>48)&0xaa] << 24) |
(deinterleaveLookup[(code>>56)&0xaa] << 28)
return x, y
}
var interleaveLookup = [256]uint64{
0x0000, 0x0001, 0x0004, 0x0005, 0x0010, 0x0011, 0x0014, 0x0015,
0x0040, 0x0041, 0x0044, 0x0045, 0x0050, 0x0051, 0x0054, 0x0055,
0x0100, 0x0101, 0x0104, 0x0105, 0x0110, 0x0111, 0x0114, 0x0115,
0x0140, 0x0141, 0x0144, 0x0145, 0x0150, 0x0151, 0x0154, 0x0155,
0x0400, 0x0401, 0x0404, 0x0405, 0x0410, 0x0411, 0x0414, 0x0415,
0x0440, 0x0441, 0x0444, 0x0445, 0x0450, 0x0451, 0x0454, 0x0455,
0x0500, 0x0501, 0x0504, 0x0505, 0x0510, 0x0511, 0x0514, 0x0515,
0x0540, 0x0541, 0x0544, 0x0545, 0x0550, 0x0551, 0x0554, 0x0555,
0x1000, 0x1001, 0x1004, 0x1005, 0x1010, 0x1011, 0x1014, 0x1015,
0x1040, 0x1041, 0x1044, 0x1045, 0x1050, 0x1051, 0x1054, 0x1055,
0x1100, 0x1101, 0x1104, 0x1105, 0x1110, 0x1111, 0x1114, 0x1115,
0x1140, 0x1141, 0x1144, 0x1145, 0x1150, 0x1151, 0x1154, 0x1155,
0x1400, 0x1401, 0x1404, 0x1405, 0x1410, 0x1411, 0x1414, 0x1415,
0x1440, 0x1441, 0x1444, 0x1445, 0x1450, 0x1451, 0x1454, 0x1455,
0x1500, 0x1501, 0x1504, 0x1505, 0x1510, 0x1511, 0x1514, 0x1515,
0x1540, 0x1541, 0x1544, 0x1545, 0x1550, 0x1551, 0x1554, 0x1555,
0x4000, 0x4001, 0x4004, 0x4005, 0x4010, 0x4011, 0x4014, 0x4015,
0x4040, 0x4041, 0x4044, 0x4045, 0x4050, 0x4051, 0x4054, 0x4055,
0x4100, 0x4101, 0x4104, 0x4105, 0x4110, 0x4111, 0x4114, 0x4115,
0x4140, 0x4141, 0x4144, 0x4145, 0x4150, 0x4151, 0x4154, 0x4155,
0x4400, 0x4401, 0x4404, 0x4405, 0x4410, 0x4411, 0x4414, 0x4415,
0x4440, 0x4441, 0x4444, 0x4445, 0x4450, 0x4451, 0x4454, 0x4455,
0x4500, 0x4501, 0x4504, 0x4505, 0x4510, 0x4511, 0x4514, 0x4515,
0x4540, 0x4541, 0x4544, 0x4545, 0x4550, 0x4551, 0x4554, 0x4555,
0x5000, 0x5001, 0x5004, 0x5005, 0x5010, 0x5011, 0x5014, 0x5015,
0x5040, 0x5041, 0x5044, 0x5045, 0x5050, 0x5051, 0x5054, 0x5055,
0x5100, 0x5101, 0x5104, 0x5105, 0x5110, 0x5111, 0x5114, 0x5115,
0x5140, 0x5141, 0x5144, 0x5145, 0x5150, 0x5151, 0x5154, 0x5155,
0x5400, 0x5401, 0x5404, 0x5405, 0x5410, 0x5411, 0x5414, 0x5415,
0x5440, 0x5441, 0x5444, 0x5445, 0x5450, 0x5451, 0x5454, 0x5455,
0x5500, 0x5501, 0x5504, 0x5505, 0x5510, 0x5511, 0x5514, 0x5515,
0x5540, 0x5541, 0x5544, 0x5545, 0x5550, 0x5551, 0x5554, 0x5555,
}
// interleaveUint32 interleaves the given arguments into the return value.
//
// The 0-bit in val0 will be the 0-bit in the return value.
// The 0-bit in val1 will be the 1-bit in the return value.
// The 1-bit of val0 will be the 2-bit in the return value, and so on.
func interleaveUint32(x, y uint32) uint64 {
return (interleaveLookup[x&0xff]) |
(interleaveLookup[(x>>8)&0xff] << 16) |
(interleaveLookup[(x>>16)&0xff] << 32) |
(interleaveLookup[x>>24] << 48) |
(interleaveLookup[y&0xff] << 1) |
(interleaveLookup[(y>>8)&0xff] << 17) |
(interleaveLookup[(y>>16)&0xff] << 33) |
(interleaveLookup[y>>24] << 49)
}

View File

@ -1,101 +0,0 @@
// Copyright 2014 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 (
"fmt"
"math"
"github.com/golang/geo/r3"
"github.com/golang/geo/s1"
)
const (
northPoleLat = s1.Angle(math.Pi/2) * s1.Radian
southPoleLat = -northPoleLat
)
// LatLng represents a point on the unit sphere as a pair of angles.
type LatLng struct {
Lat, Lng s1.Angle
}
// LatLngFromDegrees returns a LatLng for the coordinates given in degrees.
func LatLngFromDegrees(lat, lng float64) LatLng {
return LatLng{s1.Angle(lat) * s1.Degree, s1.Angle(lng) * s1.Degree}
}
// IsValid returns true iff the LatLng is normalized, with Lat ∈ [-π/2,π/2] and Lng ∈ [-π,π].
func (ll LatLng) IsValid() bool {
return math.Abs(ll.Lat.Radians()) <= math.Pi/2 && math.Abs(ll.Lng.Radians()) <= math.Pi
}
// Normalized returns the normalized version of the LatLng,
// with Lat clamped to [-π/2,π/2] and Lng wrapped in [-π,π].
func (ll LatLng) Normalized() LatLng {
lat := ll.Lat
if lat > northPoleLat {
lat = northPoleLat
} else if lat < southPoleLat {
lat = southPoleLat
}
lng := s1.Angle(math.Remainder(ll.Lng.Radians(), 2*math.Pi)) * s1.Radian
return LatLng{lat, lng}
}
func (ll LatLng) String() string { return fmt.Sprintf("[%v, %v]", ll.Lat, ll.Lng) }
// Distance returns the angle between two LatLngs.
func (ll LatLng) Distance(ll2 LatLng) s1.Angle {
// Haversine formula, as used in C++ S2LatLng::GetDistance.
lat1, lat2 := ll.Lat.Radians(), ll2.Lat.Radians()
lng1, lng2 := ll.Lng.Radians(), ll2.Lng.Radians()
dlat := math.Sin(0.5 * (lat2 - lat1))
dlng := math.Sin(0.5 * (lng2 - lng1))
x := dlat*dlat + dlng*dlng*math.Cos(lat1)*math.Cos(lat2)
return s1.Angle(2*math.Atan2(math.Sqrt(x), math.Sqrt(math.Max(0, 1-x)))) * s1.Radian
}
// NOTE(mikeperrow): The C++ implementation publicly exposes latitude/longitude
// functions. Let's see if that's really necessary before exposing the same functionality.
func latitude(p Point) s1.Angle {
return s1.Angle(math.Atan2(p.Z, math.Sqrt(p.X*p.X+p.Y*p.Y))) * s1.Radian
}
func longitude(p Point) s1.Angle {
return s1.Angle(math.Atan2(p.Y, p.X)) * s1.Radian
}
// PointFromLatLng returns an Point for the given LatLng.
// The maximum error in the result is 1.5 * dblEpsilon. (This does not
// include the error of converting degrees, E5, E6, or E7 into radians.)
func PointFromLatLng(ll LatLng) Point {
phi := ll.Lat.Radians()
theta := ll.Lng.Radians()
cosphi := math.Cos(phi)
return Point{r3.Vector{math.Cos(theta) * cosphi, math.Sin(theta) * cosphi, math.Sin(phi)}}
}
// LatLngFromPoint returns an LatLng for a given Point.
func LatLngFromPoint(p Point) LatLng {
return LatLng{latitude(p), longitude(p)}
}
// ApproxEqual reports whether the latitude and longitude of the two LatLngs
// are the same up to a small tolerance.
func (ll LatLng) ApproxEqual(other LatLng) bool {
return ll.Lat.ApproxEqual(other.Lat) && ll.Lng.ApproxEqual(other.Lng)
}

View File

@ -1,175 +0,0 @@
// Copyright 2020 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 (
"encoding/binary"
"hash/adler32"
"math"
"sort"
)
// TODO(roberts): If any of these are worth making public, change the
// method signatures and type names.
// emptySetID represents the last ID that will ever be generated.
// (Non-negative IDs are reserved for singleton sets.)
var emptySetID = int32(math.MinInt32)
// idSetLexicon compactly represents a set of non-negative
// integers such as array indices ("ID sets"). It is especially suitable when
// either (1) there are many duplicate sets, or (2) there are many singleton
// or empty sets. See also sequenceLexicon.
//
// Each distinct ID set is mapped to a 32-bit integer. Empty and singleton
// sets take up no additional space; the set itself is represented
// by the unique ID assigned to the set. Duplicate sets are automatically
// eliminated. Note also that ID sets are referred to using 32-bit integers
// rather than pointers.
type idSetLexicon struct {
idSets *sequenceLexicon
}
func newIDSetLexicon() *idSetLexicon {
return &idSetLexicon{
idSets: newSequenceLexicon(),
}
}
// add adds the given set of integers to the lexicon if it is not already
// present, and return the unique ID for this set. The values are automatically
// sorted and duplicates are removed.
//
// The primary difference between this and sequenceLexicon are:
// 1. Empty and singleton sets are represented implicitly; they use no space.
// 2. Sets are represented rather than sequences; the ordering of values is
// not important and duplicates are removed.
// 3. The values must be 32-bit non-negative integers only.
func (l *idSetLexicon) add(ids ...int32) int32 {
// Empty sets have a special ID chosen not to conflict with other IDs.
if len(ids) == 0 {
return emptySetID
}
// Singleton sets are represented by their element.
if len(ids) == 1 {
return ids[0]
}
// Canonicalize the set by sorting and removing duplicates.
//
// Creates a new slice in order to not alter the supplied values.
set := uniqueInt32s(ids)
// Non-singleton sets are represented by the bitwise complement of the ID
// returned by the sequenceLexicon
return ^l.idSets.add(set)
}
// idSet returns the set of integers corresponding to an ID returned by add.
func (l *idSetLexicon) idSet(setID int32) []int32 {
if setID >= 0 {
return []int32{setID}
}
if setID == emptySetID {
return []int32{}
}
return l.idSets.sequence(^setID)
}
func (l *idSetLexicon) clear() {
l.idSets.clear()
}
// sequenceLexicon compactly represents a sequence of values (e.g., tuples).
// It automatically eliminates duplicates slices, and maps the remaining
// sequences to sequentially increasing integer IDs. See also idSetLexicon.
//
// Each distinct sequence is mapped to a 32-bit integer.
type sequenceLexicon struct {
values []int32
begins []uint32
// idSet is a mapping of a sequence hash to sequence index in the lexicon.
idSet map[uint32]int32
}
func newSequenceLexicon() *sequenceLexicon {
return &sequenceLexicon{
begins: []uint32{0},
idSet: make(map[uint32]int32),
}
}
// clears all data from the lexicon.
func (l *sequenceLexicon) clear() {
l.values = nil
l.begins = []uint32{0}
l.idSet = make(map[uint32]int32)
}
// add adds the given value to the lexicon if it is not already present, and
// returns its ID. IDs are assigned sequentially starting from zero.
func (l *sequenceLexicon) add(ids []int32) int32 {
if id, ok := l.idSet[hashSet(ids)]; ok {
return id
}
for _, v := range ids {
l.values = append(l.values, v)
}
l.begins = append(l.begins, uint32(len(l.values)))
id := int32(len(l.begins)) - 2
l.idSet[hashSet(ids)] = id
return id
}
// sequence returns the original sequence of values for the given ID.
func (l *sequenceLexicon) sequence(id int32) []int32 {
return l.values[l.begins[id]:l.begins[id+1]]
}
// size reports the number of value sequences in the lexicon.
func (l *sequenceLexicon) size() int {
// Subtract one because the list of begins starts out with the first element set to 0.
return len(l.begins) - 1
}
// hash returns a hash of this sequence of int32s.
func hashSet(s []int32) uint32 {
// TODO(roberts): We just need a way to nicely hash all the values down to
// a 32-bit value. To ensure no unnecessary dependencies we use the core
// library types available to do this. Is there a better option?
a := adler32.New()
binary.Write(a, binary.LittleEndian, s)
return a.Sum32()
}
// uniqueInt32s returns the sorted and uniqued set of int32s from the input.
func uniqueInt32s(in []int32) []int32 {
var vals []int32
m := make(map[int32]bool)
for _, i := range in {
if m[i] {
continue
}
m[i] = true
vals = append(vals, i)
}
sort.Slice(vals, func(i, j int) bool { return vals[i] < vals[j] })
return vals
}

1833
vendor/github.com/golang/geo/s2/loop.go generated vendored

File diff suppressed because it is too large Load Diff

View File

@ -1,127 +0,0 @@
// Copyright 2015 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 (
"fmt"
"github.com/golang/geo/r3"
)
// matrix3x3 represents a traditional 3x3 matrix of floating point values.
// This is not a full fledged matrix. It only contains the pieces needed
// to satisfy the computations done within the s2 package.
type matrix3x3 [3][3]float64
// col returns the given column as a Point.
func (m *matrix3x3) col(col int) Point {
return Point{r3.Vector{m[0][col], m[1][col], m[2][col]}}
}
// row returns the given row as a Point.
func (m *matrix3x3) row(row int) Point {
return Point{r3.Vector{m[row][0], m[row][1], m[row][2]}}
}
// setCol sets the specified column to the value in the given Point.
func (m *matrix3x3) setCol(col int, p Point) *matrix3x3 {
m[0][col] = p.X
m[1][col] = p.Y
m[2][col] = p.Z
return m
}
// setRow sets the specified row to the value in the given Point.
func (m *matrix3x3) setRow(row int, p Point) *matrix3x3 {
m[row][0] = p.X
m[row][1] = p.Y
m[row][2] = p.Z
return m
}
// scale multiplies the matrix by the given value.
func (m *matrix3x3) scale(f float64) *matrix3x3 {
return &matrix3x3{
[3]float64{f * m[0][0], f * m[0][1], f * m[0][2]},
[3]float64{f * m[1][0], f * m[1][1], f * m[1][2]},
[3]float64{f * m[2][0], f * m[2][1], f * m[2][2]},
}
}
// mul returns the multiplication of m by the Point p and converts the
// resulting 1x3 matrix into a Point.
func (m *matrix3x3) mul(p Point) Point {
return Point{r3.Vector{
m[0][0]*p.X + m[0][1]*p.Y + m[0][2]*p.Z,
m[1][0]*p.X + m[1][1]*p.Y + m[1][2]*p.Z,
m[2][0]*p.X + m[2][1]*p.Y + m[2][2]*p.Z,
}}
}
// det returns the determinant of this matrix.
func (m *matrix3x3) det() float64 {
// | a b c |
// det | d e f | = aei + bfg + cdh - ceg - bdi - afh
// | g h i |
return m[0][0]*m[1][1]*m[2][2] + m[0][1]*m[1][2]*m[2][0] + m[0][2]*m[1][0]*m[2][1] -
m[0][2]*m[1][1]*m[2][0] - m[0][1]*m[1][0]*m[2][2] - m[0][0]*m[1][2]*m[2][1]
}
// transpose reflects the matrix along its diagonal and returns the result.
func (m *matrix3x3) transpose() *matrix3x3 {
m[0][1], m[1][0] = m[1][0], m[0][1]
m[0][2], m[2][0] = m[2][0], m[0][2]
m[1][2], m[2][1] = m[2][1], m[1][2]
return m
}
// String formats the matrix into an easier to read layout.
func (m *matrix3x3) String() string {
return fmt.Sprintf("[ %0.4f %0.4f %0.4f ] [ %0.4f %0.4f %0.4f ] [ %0.4f %0.4f %0.4f ]",
m[0][0], m[0][1], m[0][2],
m[1][0], m[1][1], m[1][2],
m[2][0], m[2][1], m[2][2],
)
}
// getFrame returns the orthonormal frame for the given point on the unit sphere.
func getFrame(p Point) matrix3x3 {
// Given the point p on the unit sphere, extend this into a right-handed
// coordinate frame of unit-length column vectors m = (x,y,z). Note that
// the vectors (x,y) are an orthonormal frame for the tangent space at point p,
// while p itself is an orthonormal frame for the normal space at p.
m := matrix3x3{}
m.setCol(2, p)
m.setCol(1, Point{p.Ortho()})
m.setCol(0, Point{m.col(1).Cross(p.Vector)})
return m
}
// toFrame returns the coordinates of the given point with respect to its orthonormal basis m.
// The resulting point q satisfies the identity (m * q == p).
func toFrame(m matrix3x3, p Point) Point {
// The inverse of an orthonormal matrix is its transpose.
return m.transpose().mul(p)
}
// fromFrame returns the coordinates of the given point in standard axis-aligned basis
// from its orthonormal basis m.
// The resulting point p satisfies the identity (p == m * q).
func fromFrame(m matrix3x3, q Point) Point {
return m.mul(q)
}

View File

@ -1,306 +0,0 @@
// Copyright 2019 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"
"github.com/golang/geo/s1"
)
// maxDistance implements distance as the supplementary distance (Pi - x) to find
// results that are the furthest using the distance related algorithms.
type maxDistance s1.ChordAngle
func (m maxDistance) chordAngle() s1.ChordAngle { return s1.ChordAngle(m) }
func (m maxDistance) zero() distance { return maxDistance(s1.StraightChordAngle) }
func (m maxDistance) negative() distance { return maxDistance(s1.InfChordAngle()) }
func (m maxDistance) infinity() distance { return maxDistance(s1.NegativeChordAngle) }
func (m maxDistance) less(other distance) bool { return m.chordAngle() > other.chordAngle() }
func (m maxDistance) sub(other distance) distance {
return maxDistance(m.chordAngle() + other.chordAngle())
}
func (m maxDistance) chordAngleBound() s1.ChordAngle {
return s1.StraightChordAngle - m.chordAngle()
}
func (m maxDistance) updateDistance(dist distance) (distance, bool) {
if dist.less(m) {
m = maxDistance(dist.chordAngle())
return m, true
}
return m, false
}
func (m maxDistance) fromChordAngle(o s1.ChordAngle) distance {
return maxDistance(o)
}
// MaxDistanceToPointTarget is used for computing the maximum distance to a Point.
type MaxDistanceToPointTarget struct {
point Point
dist distance
}
// NewMaxDistanceToPointTarget returns a new target for the given Point.
func NewMaxDistanceToPointTarget(point Point) *MaxDistanceToPointTarget {
m := maxDistance(0)
return &MaxDistanceToPointTarget{point: point, dist: &m}
}
func (m *MaxDistanceToPointTarget) capBound() Cap {
return CapFromCenterChordAngle(Point{m.point.Mul(-1)}, (s1.ChordAngle(0)))
}
func (m *MaxDistanceToPointTarget) updateDistanceToPoint(p Point, dist distance) (distance, bool) {
return dist.updateDistance(maxDistance(ChordAngleBetweenPoints(p, m.point)))
}
func (m *MaxDistanceToPointTarget) updateDistanceToEdge(edge Edge, dist distance) (distance, bool) {
if d, ok := UpdateMaxDistance(m.point, edge.V0, edge.V1, dist.chordAngle()); ok {
dist, _ = dist.updateDistance(maxDistance(d))
return dist, true
}
return dist, false
}
func (m *MaxDistanceToPointTarget) updateDistanceToCell(cell Cell, dist distance) (distance, bool) {
return dist.updateDistance(maxDistance(cell.MaxDistance(m.point)))
}
func (m *MaxDistanceToPointTarget) visitContainingShapes(index *ShapeIndex, v shapePointVisitorFunc) bool {
// For furthest points, we visit the polygons whose interior contains
// the antipode of the target point. These are the polygons whose
// distance to the target is maxDistance.zero()
q := NewContainsPointQuery(index, VertexModelSemiOpen)
return q.visitContainingShapes(Point{m.point.Mul(-1)}, func(shape Shape) bool {
return v(shape, m.point)
})
}
func (m *MaxDistanceToPointTarget) setMaxError(maxErr s1.ChordAngle) bool { return false }
func (m *MaxDistanceToPointTarget) maxBruteForceIndexSize() int { return 30 }
func (m *MaxDistanceToPointTarget) distance() distance { return m.dist }
// MaxDistanceToEdgeTarget is used for computing the maximum distance to an Edge.
type MaxDistanceToEdgeTarget struct {
e Edge
dist distance
}
// NewMaxDistanceToEdgeTarget returns a new target for the given Edge.
func NewMaxDistanceToEdgeTarget(e Edge) *MaxDistanceToEdgeTarget {
m := maxDistance(0)
return &MaxDistanceToEdgeTarget{e: e, dist: m}
}
// capBound returns a Cap that bounds the antipode of the target. (This
// is the set of points whose maxDistance to the target is maxDistance.zero)
func (m *MaxDistanceToEdgeTarget) capBound() Cap {
// The following computes a radius equal to half the edge length in an
// efficient and numerically stable way.
d2 := float64(ChordAngleBetweenPoints(m.e.V0, m.e.V1))
r2 := (0.5 * d2) / (1 + math.Sqrt(1-0.25*d2))
return CapFromCenterChordAngle(Point{m.e.V0.Add(m.e.V1.Vector).Mul(-1).Normalize()}, s1.ChordAngleFromSquaredLength(r2))
}
func (m *MaxDistanceToEdgeTarget) updateDistanceToPoint(p Point, dist distance) (distance, bool) {
if d, ok := UpdateMaxDistance(p, m.e.V0, m.e.V1, dist.chordAngle()); ok {
dist, _ = dist.updateDistance(maxDistance(d))
return dist, true
}
return dist, false
}
func (m *MaxDistanceToEdgeTarget) updateDistanceToEdge(edge Edge, dist distance) (distance, bool) {
if d, ok := updateEdgePairMaxDistance(m.e.V0, m.e.V1, edge.V0, edge.V1, dist.chordAngle()); ok {
dist, _ = dist.updateDistance(maxDistance(d))
return dist, true
}
return dist, false
}
func (m *MaxDistanceToEdgeTarget) updateDistanceToCell(cell Cell, dist distance) (distance, bool) {
return dist.updateDistance(maxDistance(cell.MaxDistanceToEdge(m.e.V0, m.e.V1)))
}
func (m *MaxDistanceToEdgeTarget) visitContainingShapes(index *ShapeIndex, v shapePointVisitorFunc) bool {
// We only need to test one edge point. That is because the method *must*
// visit a polygon if it fully contains the target, and *is allowed* to
// visit a polygon if it intersects the target. If the tested vertex is not
// contained, we know the full edge is not contained; if the tested vertex is
// contained, then the edge either is fully contained (must be visited) or it
// intersects (is allowed to be visited). We visit the center of the edge so
// that edge AB gives identical results to BA.
target := NewMaxDistanceToPointTarget(Point{m.e.V0.Add(m.e.V1.Vector).Normalize()})
return target.visitContainingShapes(index, v)
}
func (m *MaxDistanceToEdgeTarget) setMaxError(maxErr s1.ChordAngle) bool { return false }
func (m *MaxDistanceToEdgeTarget) maxBruteForceIndexSize() int { return 30 }
func (m *MaxDistanceToEdgeTarget) distance() distance { return m.dist }
// MaxDistanceToCellTarget is used for computing the maximum distance to a Cell.
type MaxDistanceToCellTarget struct {
cell Cell
dist distance
}
// NewMaxDistanceToCellTarget returns a new target for the given Cell.
func NewMaxDistanceToCellTarget(cell Cell) *MaxDistanceToCellTarget {
m := maxDistance(0)
return &MaxDistanceToCellTarget{cell: cell, dist: m}
}
func (m *MaxDistanceToCellTarget) capBound() Cap {
c := m.cell.CapBound()
return CapFromCenterAngle(Point{c.Center().Mul(-1)}, c.Radius())
}
func (m *MaxDistanceToCellTarget) updateDistanceToPoint(p Point, dist distance) (distance, bool) {
return dist.updateDistance(maxDistance(m.cell.MaxDistance(p)))
}
func (m *MaxDistanceToCellTarget) updateDistanceToEdge(edge Edge, dist distance) (distance, bool) {
return dist.updateDistance(maxDistance(m.cell.MaxDistanceToEdge(edge.V0, edge.V1)))
}
func (m *MaxDistanceToCellTarget) updateDistanceToCell(cell Cell, dist distance) (distance, bool) {
return dist.updateDistance(maxDistance(m.cell.MaxDistanceToCell(cell)))
}
func (m *MaxDistanceToCellTarget) visitContainingShapes(index *ShapeIndex, v shapePointVisitorFunc) bool {
// We only need to check one point here - cell center is simplest.
// See comment at MaxDistanceToEdgeTarget's visitContainingShapes.
target := NewMaxDistanceToPointTarget(m.cell.Center())
return target.visitContainingShapes(index, v)
}
func (m *MaxDistanceToCellTarget) setMaxError(maxErr s1.ChordAngle) bool { return false }
func (m *MaxDistanceToCellTarget) maxBruteForceIndexSize() int { return 30 }
func (m *MaxDistanceToCellTarget) distance() distance { return m.dist }
// MaxDistanceToShapeIndexTarget is used for computing the maximum distance to a ShapeIndex.
type MaxDistanceToShapeIndexTarget struct {
index *ShapeIndex
query *EdgeQuery
dist distance
}
// NewMaxDistanceToShapeIndexTarget returns a new target for the given ShapeIndex.
func NewMaxDistanceToShapeIndexTarget(index *ShapeIndex) *MaxDistanceToShapeIndexTarget {
m := maxDistance(0)
return &MaxDistanceToShapeIndexTarget{
index: index,
dist: m,
query: NewFurthestEdgeQuery(index, NewFurthestEdgeQueryOptions()),
}
}
// capBound returns a Cap that bounds the antipode of the target. This
// is the set of points whose maxDistance to the target is maxDistance.zero()
func (m *MaxDistanceToShapeIndexTarget) capBound() Cap {
// TODO(roberts): Depends on ShapeIndexRegion
// c := makeShapeIndexRegion(m.index).CapBound()
// return CapFromCenterRadius(Point{c.Center.Mul(-1)}, c.Radius())
panic("not implemented yet")
}
func (m *MaxDistanceToShapeIndexTarget) updateDistanceToPoint(p Point, dist distance) (distance, bool) {
m.query.opts.distanceLimit = dist.chordAngle()
target := NewMaxDistanceToPointTarget(p)
r := m.query.findEdge(target, m.query.opts)
if r.shapeID < 0 {
return dist, false
}
return r.distance, true
}
func (m *MaxDistanceToShapeIndexTarget) updateDistanceToEdge(edge Edge, dist distance) (distance, bool) {
m.query.opts.distanceLimit = dist.chordAngle()
target := NewMaxDistanceToEdgeTarget(edge)
r := m.query.findEdge(target, m.query.opts)
if r.shapeID < 0 {
return dist, false
}
return r.distance, true
}
func (m *MaxDistanceToShapeIndexTarget) updateDistanceToCell(cell Cell, dist distance) (distance, bool) {
m.query.opts.distanceLimit = dist.chordAngle()
target := NewMaxDistanceToCellTarget(cell)
r := m.query.findEdge(target, m.query.opts)
if r.shapeID < 0 {
return dist, false
}
return r.distance, true
}
// visitContainingShapes returns the polygons containing the antipodal
// reflection of *any* connected component for target types consisting of
// multiple connected components. It is sufficient to test containment of
// one vertex per connected component, since this allows us to also return
// any polygon whose boundary has distance.zero() to the target.
func (m *MaxDistanceToShapeIndexTarget) visitContainingShapes(index *ShapeIndex, v shapePointVisitorFunc) bool {
// It is sufficient to find the set of chain starts in the target index
// (i.e., one vertex per connected component of edges) that are contained by
// the query index, except for one special case to handle full polygons.
//
// TODO(roberts): Do this by merge-joining the two ShapeIndexes and share
// the code with BooleanOperation.
for _, shape := range m.index.shapes {
numChains := shape.NumChains()
// Shapes that don't have any edges require a special case (below).
testedPoint := false
for c := 0; c < numChains; c++ {
chain := shape.Chain(c)
if chain.Length == 0 {
continue
}
testedPoint = true
target := NewMaxDistanceToPointTarget(shape.ChainEdge(c, 0).V0)
if !target.visitContainingShapes(index, v) {
return false
}
}
if !testedPoint {
// Special case to handle full polygons.
ref := shape.ReferencePoint()
if !ref.Contained {
continue
}
target := NewMaxDistanceToPointTarget(ref.Point)
if !target.visitContainingShapes(index, v) {
return false
}
}
}
return true
}
func (m *MaxDistanceToShapeIndexTarget) setMaxError(maxErr s1.ChordAngle) bool {
m.query.opts.maxError = maxErr
return true
}
func (m *MaxDistanceToShapeIndexTarget) maxBruteForceIndexSize() int { return 30 }
func (m *MaxDistanceToShapeIndexTarget) distance() distance { return m.dist }
func (m *MaxDistanceToShapeIndexTarget) setIncludeInteriors(b bool) {
m.query.opts.includeInteriors = b
}
func (m *MaxDistanceToShapeIndexTarget) setUseBruteForce(b bool) { m.query.opts.useBruteForce = b }
// TODO(roberts): Remaining methods
//
// func (m *MaxDistanceToShapeIndexTarget) capBound() Cap {
// CellUnionTarget

View File

@ -1,164 +0,0 @@
// Copyright 2015 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
// This file implements functions for various S2 measurements.
import "math"
// A Metric is a measure for cells. It is used to describe the shape and size
// of cells. They are useful for deciding which cell level to use in order to
// satisfy a given condition (e.g. that cell vertices must be no further than
// "x" apart). You can use the Value(level) method to compute the corresponding
// length or area on the unit sphere for cells at a given level. The minimum
// and maximum bounds are valid for cells at all levels, but they may be
// somewhat conservative for very large cells (e.g. face cells).
type Metric struct {
// Dim is either 1 or 2, for a 1D or 2D metric respectively.
Dim int
// Deriv is the scaling factor for the metric.
Deriv float64
}
// Defined metrics.
// Of the projection methods defined in C++, Go only supports the quadratic projection.
// Each cell is bounded by four planes passing through its four edges and
// the center of the sphere. These metrics relate to the angle between each
// pair of opposite bounding planes, or equivalently, between the planes
// corresponding to two different s-values or two different t-values.
var (
MinAngleSpanMetric = Metric{1, 4.0 / 3}
AvgAngleSpanMetric = Metric{1, math.Pi / 2}
MaxAngleSpanMetric = Metric{1, 1.704897179199218452}
)
// The width of geometric figure is defined as the distance between two
// parallel bounding lines in a given direction. For cells, the minimum
// width is always attained between two opposite edges, and the maximum
// width is attained between two opposite vertices. However, for our
// purposes we redefine the width of a cell as the perpendicular distance
// between a pair of opposite edges. A cell therefore has two widths, one
// in each direction. The minimum width according to this definition agrees
// with the classic geometric one, but the maximum width is different. (The
// maximum geometric width corresponds to MaxDiag defined below.)
//
// The average width in both directions for all cells at level k is approximately
// AvgWidthMetric.Value(k).
//
// The width is useful for bounding the minimum or maximum distance from a
// point on one edge of a cell to the closest point on the opposite edge.
// For example, this is useful when growing regions by a fixed distance.
var (
MinWidthMetric = Metric{1, 2 * math.Sqrt2 / 3}
AvgWidthMetric = Metric{1, 1.434523672886099389}
MaxWidthMetric = Metric{1, MaxAngleSpanMetric.Deriv}
)
// The edge length metrics can be used to bound the minimum, maximum,
// or average distance from the center of one cell to the center of one of
// its edge neighbors. In particular, it can be used to bound the distance
// between adjacent cell centers along the space-filling Hilbert curve for
// cells at any given level.
var (
MinEdgeMetric = Metric{1, 2 * math.Sqrt2 / 3}
AvgEdgeMetric = Metric{1, 1.459213746386106062}
MaxEdgeMetric = Metric{1, MaxAngleSpanMetric.Deriv}
// MaxEdgeAspect is the maximum edge aspect ratio over all cells at any level,
// where the edge aspect ratio of a cell is defined as the ratio of its longest
// edge length to its shortest edge length.
MaxEdgeAspect = 1.442615274452682920
MinAreaMetric = Metric{2, 8 * math.Sqrt2 / 9}
AvgAreaMetric = Metric{2, 4 * math.Pi / 6}
MaxAreaMetric = Metric{2, 2.635799256963161491}
)
// The maximum diagonal is also the maximum diameter of any cell,
// and also the maximum geometric width (see the comment for widths). For
// example, the distance from an arbitrary point to the closest cell center
// at a given level is at most half the maximum diagonal length.
var (
MinDiagMetric = Metric{1, 8 * math.Sqrt2 / 9}
AvgDiagMetric = Metric{1, 2.060422738998471683}
MaxDiagMetric = Metric{1, 2.438654594434021032}
// MaxDiagAspect is the maximum diagonal aspect ratio over all cells at any
// level, where the diagonal aspect ratio of a cell is defined as the ratio
// of its longest diagonal length to its shortest diagonal length.
MaxDiagAspect = math.Sqrt(3)
)
// Value returns the value of the metric at the given level.
func (m Metric) Value(level int) float64 {
return math.Ldexp(m.Deriv, -m.Dim*level)
}
// MinLevel returns the minimum level such that the metric is at most
// the given value, or maxLevel (30) if there is no such level.
//
// For example, MinLevel(0.1) returns the minimum level such that all cell diagonal
// lengths are 0.1 or smaller. The returned value is always a valid level.
//
// In C++, this is called GetLevelForMaxValue.
func (m Metric) MinLevel(val float64) int {
if val < 0 {
return maxLevel
}
level := -(math.Ilogb(val/m.Deriv) >> uint(m.Dim-1))
if level > maxLevel {
level = maxLevel
}
if level < 0 {
level = 0
}
return level
}
// MaxLevel returns the maximum level such that the metric is at least
// the given value, or zero if there is no such level.
//
// For example, MaxLevel(0.1) returns the maximum level such that all cells have a
// minimum width of 0.1 or larger. The returned value is always a valid level.
//
// In C++, this is called GetLevelForMinValue.
func (m Metric) MaxLevel(val float64) int {
if val <= 0 {
return maxLevel
}
level := math.Ilogb(m.Deriv/val) >> uint(m.Dim-1)
if level > maxLevel {
level = maxLevel
}
if level < 0 {
level = 0
}
return level
}
// ClosestLevel returns the level at which the metric has approximately the given
// value. The return value is always a valid level. For example,
// AvgEdgeMetric.ClosestLevel(0.1) returns the level at which the average cell edge
// length is approximately 0.1.
func (m Metric) ClosestLevel(val float64) int {
x := math.Sqrt2
if m.Dim == 2 {
x = 2
}
return m.MinLevel(x * val)
}

View File

@ -1,362 +0,0 @@
// Copyright 2019 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"
"github.com/golang/geo/s1"
)
// minDistance implements distance interface to find closest distance types.
type minDistance s1.ChordAngle
func (m minDistance) chordAngle() s1.ChordAngle { return s1.ChordAngle(m) }
func (m minDistance) zero() distance { return minDistance(0) }
func (m minDistance) negative() distance { return minDistance(s1.NegativeChordAngle) }
func (m minDistance) infinity() distance { return minDistance(s1.InfChordAngle()) }
func (m minDistance) less(other distance) bool { return m.chordAngle() < other.chordAngle() }
func (m minDistance) sub(other distance) distance {
return minDistance(m.chordAngle() - other.chordAngle())
}
func (m minDistance) chordAngleBound() s1.ChordAngle {
return m.chordAngle().Expanded(m.chordAngle().MaxAngleError())
}
// updateDistance updates its own value if the other value is less() than it is,
// and reports if it updated.
func (m minDistance) updateDistance(dist distance) (distance, bool) {
if dist.less(m) {
m = minDistance(dist.chordAngle())
return m, true
}
return m, false
}
func (m minDistance) fromChordAngle(o s1.ChordAngle) distance {
return minDistance(o)
}
// MinDistanceToPointTarget is a type for computing the minimum distance to a Point.
type MinDistanceToPointTarget struct {
point Point
dist distance
}
// NewMinDistanceToPointTarget returns a new target for the given Point.
func NewMinDistanceToPointTarget(point Point) *MinDistanceToPointTarget {
m := minDistance(0)
return &MinDistanceToPointTarget{point: point, dist: &m}
}
func (m *MinDistanceToPointTarget) capBound() Cap {
return CapFromCenterChordAngle(m.point, s1.ChordAngle(0))
}
func (m *MinDistanceToPointTarget) updateDistanceToPoint(p Point, dist distance) (distance, bool) {
var ok bool
dist, ok = dist.updateDistance(minDistance(ChordAngleBetweenPoints(p, m.point)))
return dist, ok
}
func (m *MinDistanceToPointTarget) updateDistanceToEdge(edge Edge, dist distance) (distance, bool) {
if d, ok := UpdateMinDistance(m.point, edge.V0, edge.V1, dist.chordAngle()); ok {
dist, _ = dist.updateDistance(minDistance(d))
return dist, true
}
return dist, false
}
func (m *MinDistanceToPointTarget) updateDistanceToCell(cell Cell, dist distance) (distance, bool) {
var ok bool
dist, ok = dist.updateDistance(minDistance(cell.Distance(m.point)))
return dist, ok
}
func (m *MinDistanceToPointTarget) visitContainingShapes(index *ShapeIndex, v shapePointVisitorFunc) bool {
// For furthest points, we visit the polygons whose interior contains
// the antipode of the target point. These are the polygons whose
// distance to the target is maxDistance.zero()
q := NewContainsPointQuery(index, VertexModelSemiOpen)
return q.visitContainingShapes(m.point, func(shape Shape) bool {
return v(shape, m.point)
})
}
func (m *MinDistanceToPointTarget) setMaxError(maxErr s1.ChordAngle) bool { return false }
func (m *MinDistanceToPointTarget) maxBruteForceIndexSize() int { return 30 }
func (m *MinDistanceToPointTarget) distance() distance { return m.dist }
// ----------------------------------------------------------
// MinDistanceToEdgeTarget is a type for computing the minimum distance to an Edge.
type MinDistanceToEdgeTarget struct {
e Edge
dist distance
}
// NewMinDistanceToEdgeTarget returns a new target for the given Edge.
func NewMinDistanceToEdgeTarget(e Edge) *MinDistanceToEdgeTarget {
m := minDistance(0)
return &MinDistanceToEdgeTarget{e: e, dist: m}
}
// capBound returns a Cap that bounds the antipode of the target. (This
// is the set of points whose maxDistance to the target is maxDistance.zero)
func (m *MinDistanceToEdgeTarget) capBound() Cap {
// The following computes a radius equal to half the edge length in an
// efficient and numerically stable way.
d2 := float64(ChordAngleBetweenPoints(m.e.V0, m.e.V1))
r2 := (0.5 * d2) / (1 + math.Sqrt(1-0.25*d2))
return CapFromCenterChordAngle(Point{m.e.V0.Add(m.e.V1.Vector).Normalize()}, s1.ChordAngleFromSquaredLength(r2))
}
func (m *MinDistanceToEdgeTarget) updateDistanceToPoint(p Point, dist distance) (distance, bool) {
if d, ok := UpdateMinDistance(p, m.e.V0, m.e.V1, dist.chordAngle()); ok {
dist, _ = dist.updateDistance(minDistance(d))
return dist, true
}
return dist, false
}
func (m *MinDistanceToEdgeTarget) updateDistanceToEdge(edge Edge, dist distance) (distance, bool) {
if d, ok := updateEdgePairMinDistance(m.e.V0, m.e.V1, edge.V0, edge.V1, dist.chordAngle()); ok {
dist, _ = dist.updateDistance(minDistance(d))
return dist, true
}
return dist, false
}
func (m *MinDistanceToEdgeTarget) updateDistanceToCell(cell Cell, dist distance) (distance, bool) {
return dist.updateDistance(minDistance(cell.DistanceToEdge(m.e.V0, m.e.V1)))
}
func (m *MinDistanceToEdgeTarget) visitContainingShapes(index *ShapeIndex, v shapePointVisitorFunc) bool {
// We test the center of the edge in order to ensure that edge targets AB
// and BA yield identical results (which is not guaranteed by the API but
// users might expect). Other options would be to test both endpoints, or
// return different results for AB and BA in some cases.
target := NewMinDistanceToPointTarget(Point{m.e.V0.Add(m.e.V1.Vector).Normalize()})
return target.visitContainingShapes(index, v)
}
func (m *MinDistanceToEdgeTarget) setMaxError(maxErr s1.ChordAngle) bool { return false }
func (m *MinDistanceToEdgeTarget) maxBruteForceIndexSize() int { return 30 }
func (m *MinDistanceToEdgeTarget) distance() distance { return m.dist }
// ----------------------------------------------------------
// MinDistanceToCellTarget is a type for computing the minimum distance to a Cell.
type MinDistanceToCellTarget struct {
cell Cell
dist distance
}
// NewMinDistanceToCellTarget returns a new target for the given Cell.
func NewMinDistanceToCellTarget(cell Cell) *MinDistanceToCellTarget {
m := minDistance(0)
return &MinDistanceToCellTarget{cell: cell, dist: m}
}
func (m *MinDistanceToCellTarget) capBound() Cap {
return m.cell.CapBound()
}
func (m *MinDistanceToCellTarget) updateDistanceToPoint(p Point, dist distance) (distance, bool) {
return dist.updateDistance(minDistance(m.cell.Distance(p)))
}
func (m *MinDistanceToCellTarget) updateDistanceToEdge(edge Edge, dist distance) (distance, bool) {
return dist.updateDistance(minDistance(m.cell.DistanceToEdge(edge.V0, edge.V1)))
}
func (m *MinDistanceToCellTarget) updateDistanceToCell(cell Cell, dist distance) (distance, bool) {
return dist.updateDistance(minDistance(m.cell.DistanceToCell(cell)))
}
func (m *MinDistanceToCellTarget) visitContainingShapes(index *ShapeIndex, v shapePointVisitorFunc) bool {
// The simplest approach is simply to return the polygons that contain the
// cell center. Alternatively, if the index cell is smaller than the target
// cell then we could return all polygons that are present in the
// shapeIndexCell, but since the index is built conservatively this may
// include some polygons that don't quite intersect the cell. So we would
// either need to recheck for intersection more accurately, or weaken the
// VisitContainingShapes contract so that it only guarantees approximate
// intersection, neither of which seems like a good tradeoff.
target := NewMinDistanceToPointTarget(m.cell.Center())
return target.visitContainingShapes(index, v)
}
func (m *MinDistanceToCellTarget) setMaxError(maxErr s1.ChordAngle) bool { return false }
func (m *MinDistanceToCellTarget) maxBruteForceIndexSize() int { return 30 }
func (m *MinDistanceToCellTarget) distance() distance { return m.dist }
// ----------------------------------------------------------
/*
// MinDistanceToCellUnionTarget is a type for computing the minimum distance to a CellUnion.
type MinDistanceToCellUnionTarget struct {
cu CellUnion
query *ClosestCellQuery
dist distance
}
// NewMinDistanceToCellUnionTarget returns a new target for the given CellUnion.
func NewMinDistanceToCellUnionTarget(cu CellUnion) *MinDistanceToCellUnionTarget {
m := minDistance(0)
return &MinDistanceToCellUnionTarget{cu: cu, dist: m}
}
func (m *MinDistanceToCellUnionTarget) capBound() Cap {
return m.cu.CapBound()
}
func (m *MinDistanceToCellUnionTarget) updateDistanceToCell(cell Cell, dist distance) (distance, bool) {
m.query.opts.DistanceLimit = dist.chordAngle()
target := NewMinDistanceToPointTarget(p)
r := m.query.findEdge(target)
if r.ShapeID < 0 {
return dist, false
}
return minDistance(r.Distance), true
}
func (m *MinDistanceToCellUnionTarget) visitContainingShapes(index *ShapeIndex, v shapePointVisitorFunc) bool {
// We test the center of the edge in order to ensure that edge targets AB
// and BA yield identical results (which is not guaranteed by the API but
// users might expect). Other options would be to test both endpoints, or
// return different results for AB and BA in some cases.
target := NewMinDistanceToPointTarget(Point{m.e.V0.Add(m.e.V1.Vector).Normalize()})
return target.visitContainingShapes(index, v)
}
func (m *MinDistanceToCellUnionTarget) setMaxError(maxErr s1.ChordAngle) bool {
m.query.opts.MaxError = maxErr
return true
}
func (m *MinDistanceToCellUnionTarget) maxBruteForceIndexSize() int { return 30 }
func (m *MinDistanceToCellUnionTarget) distance() distance { return m.dist }
*/
// ----------------------------------------------------------
// MinDistanceToShapeIndexTarget is a type for computing the minimum distance to a ShapeIndex.
type MinDistanceToShapeIndexTarget struct {
index *ShapeIndex
query *EdgeQuery
dist distance
}
// NewMinDistanceToShapeIndexTarget returns a new target for the given ShapeIndex.
func NewMinDistanceToShapeIndexTarget(index *ShapeIndex) *MinDistanceToShapeIndexTarget {
m := minDistance(0)
return &MinDistanceToShapeIndexTarget{
index: index,
dist: m,
query: NewClosestEdgeQuery(index, NewClosestEdgeQueryOptions()),
}
}
func (m *MinDistanceToShapeIndexTarget) capBound() Cap {
// TODO(roberts): Depends on ShapeIndexRegion existing.
// c := makeS2ShapeIndexRegion(m.index).CapBound()
// return CapFromCenterRadius(Point{c.Center.Mul(-1)}, c.Radius())
panic("not implemented yet")
}
func (m *MinDistanceToShapeIndexTarget) updateDistanceToPoint(p Point, dist distance) (distance, bool) {
m.query.opts.distanceLimit = dist.chordAngle()
target := NewMinDistanceToPointTarget(p)
r := m.query.findEdge(target, m.query.opts)
if r.shapeID < 0 {
return dist, false
}
return r.distance, true
}
func (m *MinDistanceToShapeIndexTarget) updateDistanceToEdge(edge Edge, dist distance) (distance, bool) {
m.query.opts.distanceLimit = dist.chordAngle()
target := NewMinDistanceToEdgeTarget(edge)
r := m.query.findEdge(target, m.query.opts)
if r.shapeID < 0 {
return dist, false
}
return r.distance, true
}
func (m *MinDistanceToShapeIndexTarget) updateDistanceToCell(cell Cell, dist distance) (distance, bool) {
m.query.opts.distanceLimit = dist.chordAngle()
target := NewMinDistanceToCellTarget(cell)
r := m.query.findEdge(target, m.query.opts)
if r.shapeID < 0 {
return dist, false
}
return r.distance, true
}
// For target types consisting of multiple connected components (such as this one),
// this method should return the polygons containing the antipodal reflection of
// *any* connected component. (It is sufficient to test containment of one vertex per
// connected component, since this allows us to also return any polygon whose
// boundary has distance.zero() to the target.)
func (m *MinDistanceToShapeIndexTarget) visitContainingShapes(index *ShapeIndex, v shapePointVisitorFunc) bool {
// It is sufficient to find the set of chain starts in the target index
// (i.e., one vertex per connected component of edges) that are contained by
// the query index, except for one special case to handle full polygons.
//
// TODO(roberts): Do this by merge-joining the two ShapeIndexes.
for _, shape := range m.index.shapes {
numChains := shape.NumChains()
// Shapes that don't have any edges require a special case (below).
testedPoint := false
for c := 0; c < numChains; c++ {
chain := shape.Chain(c)
if chain.Length == 0 {
continue
}
testedPoint = true
target := NewMinDistanceToPointTarget(shape.ChainEdge(c, 0).V0)
if !target.visitContainingShapes(index, v) {
return false
}
}
if !testedPoint {
// Special case to handle full polygons.
ref := shape.ReferencePoint()
if !ref.Contained {
continue
}
target := NewMinDistanceToPointTarget(ref.Point)
if !target.visitContainingShapes(index, v) {
return false
}
}
}
return true
}
func (m *MinDistanceToShapeIndexTarget) setMaxError(maxErr s1.ChordAngle) bool {
m.query.opts.maxError = maxErr
return true
}
func (m *MinDistanceToShapeIndexTarget) maxBruteForceIndexSize() int { return 25 }
func (m *MinDistanceToShapeIndexTarget) distance() distance { return m.dist }
func (m *MinDistanceToShapeIndexTarget) setIncludeInteriors(b bool) {
m.query.opts.includeInteriors = b
}
func (m *MinDistanceToShapeIndexTarget) setUseBruteForce(b bool) { m.query.opts.useBruteForce = b }
// TODO(roberts): Remaining methods
//
// func (m *MinDistanceToShapeIndexTarget) capBound() Cap {
// CellUnionTarget

View File

@ -1,88 +0,0 @@
// 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
// nthDerivativeCoder provides Nth Derivative Coding.
// (In signal processing disciplines, this is known as N-th Delta Coding.)
//
// Good for varint coding integer sequences with polynomial trends.
//
// Instead of coding a sequence of values directly, code its nth-order discrete
// derivative. Overflow in integer addition and subtraction makes this a
// lossless transform.
//
// constant linear quadratic
// trend trend trend
// / \ / \ / \_
// input |0 0 0 0 1 2 3 4 9 16 25 36
// 0th derivative(identity) |0 0 0 0 1 2 3 4 9 16 25 36
// 1st derivative(delta coding) | 0 0 0 1 1 1 1 5 7 9 11
// 2nd derivative(linear prediction) | 0 0 1 0 0 0 4 2 2 2
// -------------------------------------
// 0 1 2 3 4 5 6 7 8 9 10 11
// n in sequence
//
// Higher-order codings can break even or be detrimental on other sequences.
//
// random oscillating
// / \ / \_
// input |5 9 6 1 8 8 2 -2 4 -4 6 -6
// 0th derivative(identity) |5 9 6 1 8 8 2 -2 4 -4 6 -6
// 1st derivative(delta coding) | 4 -3 -5 7 0 -6 -4 6 -8 10 -12
// 2nd derivative(linear prediction) | -7 -2 12 -7 -6 2 10 -14 18 -22
// ---------------------------------------
// 0 1 2 3 4 5 6 7 8 9 10 11
// n in sequence
//
// Note that the nth derivative isn't available until sequence item n. Earlier
// values are coded at lower order. For the above table, read 5 4 -7 -2 12 ...
type nthDerivativeCoder struct {
n, m int
memory [10]int32
}
// newNthDerivativeCoder returns a new coder, where n is the derivative order of the encoder (the N in NthDerivative).
// n must be within [0,10].
func newNthDerivativeCoder(n int) *nthDerivativeCoder {
c := &nthDerivativeCoder{n: n}
if n < 0 || n > len(c.memory) {
panic("unsupported n. Must be within [0,10].")
}
return c
}
func (c *nthDerivativeCoder) encode(k int32) int32 {
for i := 0; i < c.m; i++ {
delta := k - c.memory[i]
c.memory[i] = k
k = delta
}
if c.m < c.n {
c.memory[c.m] = k
c.m++
}
return k
}
func (c *nthDerivativeCoder) decode(k int32) int32 {
if c.m < c.n {
c.m++
}
for i := c.m - 1; i >= 0; i-- {
c.memory[i] += k
k = c.memory[i]
}
return k
}

View File

@ -1,252 +0,0 @@
// 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 (
"github.com/golang/geo/r1"
"github.com/golang/geo/r2"
)
// PaddedCell represents a Cell whose (u,v)-range has been expanded on
// all sides by a given amount of "padding". Unlike Cell, its methods and
// representation are optimized for clipping edges against Cell boundaries
// to determine which cells are intersected by a given set of edges.
type PaddedCell struct {
id CellID
padding float64
bound r2.Rect
middle r2.Rect // A rect in (u, v)-space that belongs to all four children.
iLo, jLo int // Minimum (i,j)-coordinates of this cell before padding
orientation int // Hilbert curve orientation of this cell.
level int
}
// PaddedCellFromCellID constructs a padded cell with the given padding.
func PaddedCellFromCellID(id CellID, padding float64) *PaddedCell {
p := &PaddedCell{
id: id,
padding: padding,
middle: r2.EmptyRect(),
}
// Fast path for constructing a top-level face (the most common case).
if id.isFace() {
limit := padding + 1
p.bound = r2.Rect{r1.Interval{-limit, limit}, r1.Interval{-limit, limit}}
p.middle = r2.Rect{r1.Interval{-padding, padding}, r1.Interval{-padding, padding}}
p.orientation = id.Face() & 1
return p
}
_, p.iLo, p.jLo, p.orientation = id.faceIJOrientation()
p.level = id.Level()
p.bound = ijLevelToBoundUV(p.iLo, p.jLo, p.level).ExpandedByMargin(padding)
ijSize := sizeIJ(p.level)
p.iLo &= -ijSize
p.jLo &= -ijSize
return p
}
// PaddedCellFromParentIJ constructs the child of parent with the given (i,j) index.
// The four child cells have indices of (0,0), (0,1), (1,0), (1,1), where the i and j
// indices correspond to increasing u- and v-values respectively.
func PaddedCellFromParentIJ(parent *PaddedCell, i, j int) *PaddedCell {
// Compute the position and orientation of the child incrementally from the
// orientation of the parent.
pos := ijToPos[parent.orientation][2*i+j]
p := &PaddedCell{
id: parent.id.Children()[pos],
padding: parent.padding,
bound: parent.bound,
orientation: parent.orientation ^ posToOrientation[pos],
level: parent.level + 1,
middle: r2.EmptyRect(),
}
ijSize := sizeIJ(p.level)
p.iLo = parent.iLo + i*ijSize
p.jLo = parent.jLo + j*ijSize
// For each child, one corner of the bound is taken directly from the parent
// while the diagonally opposite corner is taken from middle().
middle := parent.Middle()
if i == 1 {
p.bound.X.Lo = middle.X.Lo
} else {
p.bound.X.Hi = middle.X.Hi
}
if j == 1 {
p.bound.Y.Lo = middle.Y.Lo
} else {
p.bound.Y.Hi = middle.Y.Hi
}
return p
}
// CellID returns the CellID this padded cell represents.
func (p PaddedCell) CellID() CellID {
return p.id
}
// Padding returns the amount of padding on this cell.
func (p PaddedCell) Padding() float64 {
return p.padding
}
// Level returns the level this cell is at.
func (p PaddedCell) Level() int {
return p.level
}
// Center returns the center of this cell.
func (p PaddedCell) Center() Point {
ijSize := sizeIJ(p.level)
si := uint32(2*p.iLo + ijSize)
ti := uint32(2*p.jLo + ijSize)
return Point{faceSiTiToXYZ(p.id.Face(), si, ti).Normalize()}
}
// Middle returns the rectangle in the middle of this cell that belongs to
// all four of its children in (u,v)-space.
func (p *PaddedCell) Middle() r2.Rect {
// We compute this field lazily because it is not needed the majority of the
// time (i.e., for cells where the recursion terminates).
if p.middle.IsEmpty() {
ijSize := sizeIJ(p.level)
u := stToUV(siTiToST(uint32(2*p.iLo + ijSize)))
v := stToUV(siTiToST(uint32(2*p.jLo + ijSize)))
p.middle = r2.Rect{
r1.Interval{u - p.padding, u + p.padding},
r1.Interval{v - p.padding, v + p.padding},
}
}
return p.middle
}
// Bound returns the bounds for this cell in (u,v)-space including padding.
func (p PaddedCell) Bound() r2.Rect {
return p.bound
}
// ChildIJ returns the (i,j) coordinates for the child cell at the given traversal
// position. The traversal position corresponds to the order in which child
// cells are visited by the Hilbert curve.
func (p PaddedCell) ChildIJ(pos int) (i, j int) {
ij := posToIJ[p.orientation][pos]
return ij >> 1, ij & 1
}
// EntryVertex return the vertex where the space-filling curve enters this cell.
func (p PaddedCell) EntryVertex() Point {
// The curve enters at the (0,0) vertex unless the axis directions are
// reversed, in which case it enters at the (1,1) vertex.
i := p.iLo
j := p.jLo
if p.orientation&invertMask != 0 {
ijSize := sizeIJ(p.level)
i += ijSize
j += ijSize
}
return Point{faceSiTiToXYZ(p.id.Face(), uint32(2*i), uint32(2*j)).Normalize()}
}
// ExitVertex returns the vertex where the space-filling curve exits this cell.
func (p PaddedCell) ExitVertex() Point {
// The curve exits at the (1,0) vertex unless the axes are swapped or
// inverted but not both, in which case it exits at the (0,1) vertex.
i := p.iLo
j := p.jLo
ijSize := sizeIJ(p.level)
if p.orientation == 0 || p.orientation == swapMask+invertMask {
i += ijSize
} else {
j += ijSize
}
return Point{faceSiTiToXYZ(p.id.Face(), uint32(2*i), uint32(2*j)).Normalize()}
}
// ShrinkToFit returns the smallest CellID that contains all descendants of this
// padded cell whose bounds intersect the given rect. For algorithms that use
// recursive subdivision to find the cells that intersect a particular object, this
// method can be used to skip all of the initial subdivision steps where only
// one child needs to be expanded.
//
// Note that this method is not the same as returning the smallest cell that contains
// the intersection of this cell with rect. Because of the padding, even if one child
// completely contains rect it is still possible that a neighboring child may also
// intersect the given rect.
//
// The provided Rect must intersect the bounds of this cell.
func (p *PaddedCell) ShrinkToFit(rect r2.Rect) CellID {
// Quick rejection test: if rect contains the center of this cell along
// either axis, then no further shrinking is possible.
if p.level == 0 {
// Fast path (most calls to this function start with a face cell).
if rect.X.Contains(0) || rect.Y.Contains(0) {
return p.id
}
}
ijSize := sizeIJ(p.level)
if rect.X.Contains(stToUV(siTiToST(uint32(2*p.iLo+ijSize)))) ||
rect.Y.Contains(stToUV(siTiToST(uint32(2*p.jLo+ijSize)))) {
return p.id
}
// Otherwise we expand rect by the given padding on all sides and find
// the range of coordinates that it spans along the i- and j-axes. We then
// compute the highest bit position at which the min and max coordinates
// differ. This corresponds to the first cell level at which at least two
// children intersect rect.
// Increase the padding to compensate for the error in uvToST.
// (The constant below is a provable upper bound on the additional error.)
padded := rect.ExpandedByMargin(p.padding + 1.5*dblEpsilon)
iMin, jMin := p.iLo, p.jLo // Min i- or j- coordinate spanned by padded
var iXor, jXor int // XOR of the min and max i- or j-coordinates
if iMin < stToIJ(uvToST(padded.X.Lo)) {
iMin = stToIJ(uvToST(padded.X.Lo))
}
if a, b := p.iLo+ijSize-1, stToIJ(uvToST(padded.X.Hi)); a <= b {
iXor = iMin ^ a
} else {
iXor = iMin ^ b
}
if jMin < stToIJ(uvToST(padded.Y.Lo)) {
jMin = stToIJ(uvToST(padded.Y.Lo))
}
if a, b := p.jLo+ijSize-1, stToIJ(uvToST(padded.Y.Hi)); a <= b {
jXor = jMin ^ a
} else {
jXor = jMin ^ b
}
// Compute the highest bit position where the two i- or j-endpoints differ,
// and then choose the cell level that includes both of these endpoints. So
// if both pairs of endpoints are equal we choose maxLevel; if they differ
// only at bit 0, we choose (maxLevel - 1), and so on.
levelMSB := uint64(((iXor | jXor) << 1) + 1)
level := maxLevel - findMSBSetNonZero64(levelMSB)
if level <= p.level {
return p.id
}
return cellIDFromFaceIJ(p.id.Face(), iMin, jMin).Parent(level)
}

View File

@ -1,258 +0,0 @@
// Copyright 2014 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 (
"fmt"
"io"
"math"
"sort"
"github.com/golang/geo/r3"
"github.com/golang/geo/s1"
)
// Point represents a point on the unit sphere as a normalized 3D vector.
// Fields should be treated as read-only. Use one of the factory methods for creation.
type Point struct {
r3.Vector
}
// sortPoints sorts the slice of Points in place.
func sortPoints(e []Point) {
sort.Sort(points(e))
}
// points implements the Sort interface for slices of Point.
type points []Point
func (p points) Len() int { return len(p) }
func (p points) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func (p points) Less(i, j int) bool { return p[i].Cmp(p[j].Vector) == -1 }
// PointFromCoords creates a new normalized point from coordinates.
//
// This always returns a valid point. If the given coordinates can not be normalized
// the origin point will be returned.
//
// This behavior is different from the C++ construction of a S2Point from coordinates
// (i.e. S2Point(x, y, z)) in that in C++ they do not Normalize.
func PointFromCoords(x, y, z float64) Point {
if x == 0 && y == 0 && z == 0 {
return OriginPoint()
}
return Point{r3.Vector{x, y, z}.Normalize()}
}
// OriginPoint returns a unique "origin" on the sphere for operations that need a fixed
// reference point. In particular, this is the "point at infinity" used for
// point-in-polygon testing (by counting the number of edge crossings).
//
// It should *not* be a point that is commonly used in edge tests in order
// to avoid triggering code to handle degenerate cases (this rules out the
// north and south poles). It should also not be on the boundary of any
// low-level S2Cell for the same reason.
func OriginPoint() Point {
return Point{r3.Vector{-0.0099994664350250197, 0.0025924542609324121, 0.99994664350250195}}
}
// PointCross returns a Point that is orthogonal to both p and op. This is similar to
// p.Cross(op) (the true cross product) except that it does a better job of
// ensuring orthogonality when the Point is nearly parallel to op, it returns
// a non-zero result even when p == op or p == -op and the result is a Point.
//
// It satisfies the following properties (f == PointCross):
//
// (1) f(p, op) != 0 for all p, op
// (2) f(op,p) == -f(p,op) unless p == op or p == -op
// (3) f(-p,op) == -f(p,op) unless p == op or p == -op
// (4) f(p,-op) == -f(p,op) unless p == op or p == -op
func (p Point) PointCross(op Point) Point {
// NOTE(dnadasi): In the C++ API the equivalent method here was known as "RobustCrossProd",
// but PointCross more accurately describes how this method is used.
x := p.Add(op.Vector).Cross(op.Sub(p.Vector))
// Compare exactly to the 0 vector.
if x == (r3.Vector{}) {
// The only result that makes sense mathematically is to return zero, but
// we find it more convenient to return an arbitrary orthogonal vector.
return Point{p.Ortho()}
}
return Point{x}
}
// OrderedCCW returns true if the edges OA, OB, and OC are encountered in that
// order while sweeping CCW around the point O.
//
// You can think of this as testing whether A <= B <= C with respect to the
// CCW ordering around O that starts at A, or equivalently, whether B is
// contained in the range of angles (inclusive) that starts at A and extends
// CCW to C. Properties:
//
// (1) If OrderedCCW(a,b,c,o) && OrderedCCW(b,a,c,o), then a == b
// (2) If OrderedCCW(a,b,c,o) && OrderedCCW(a,c,b,o), then b == c
// (3) If OrderedCCW(a,b,c,o) && OrderedCCW(c,b,a,o), then a == b == c
// (4) If a == b or b == c, then OrderedCCW(a,b,c,o) is true
// (5) Otherwise if a == c, then OrderedCCW(a,b,c,o) is false
func OrderedCCW(a, b, c, o Point) bool {
sum := 0
if RobustSign(b, o, a) != Clockwise {
sum++
}
if RobustSign(c, o, b) != Clockwise {
sum++
}
if RobustSign(a, o, c) == CounterClockwise {
sum++
}
return sum >= 2
}
// Distance returns the angle between two points.
func (p Point) Distance(b Point) s1.Angle {
return p.Vector.Angle(b.Vector)
}
// ApproxEqual reports whether the two points are similar enough to be equal.
func (p Point) ApproxEqual(other Point) bool {
return p.approxEqual(other, s1.Angle(epsilon))
}
// approxEqual reports whether the two points are within the given epsilon.
func (p Point) approxEqual(other Point, eps s1.Angle) bool {
return p.Vector.Angle(other.Vector) <= eps
}
// ChordAngleBetweenPoints constructs a ChordAngle corresponding to the distance
// between the two given points. The points must be unit length.
func ChordAngleBetweenPoints(x, y Point) s1.ChordAngle {
return s1.ChordAngle(math.Min(4.0, x.Sub(y.Vector).Norm2()))
}
// regularPoints generates a slice of points shaped as a regular polygon with
// the numVertices vertices, all located on a circle of the specified angular radius
// around the center. The radius is the actual distance from center to each vertex.
func regularPoints(center Point, radius s1.Angle, numVertices int) []Point {
return regularPointsForFrame(getFrame(center), radius, numVertices)
}
// regularPointsForFrame generates a slice of points shaped as a regular polygon
// with numVertices vertices, all on a circle of the specified angular radius around
// the center. The radius is the actual distance from the center to each vertex.
func regularPointsForFrame(frame matrix3x3, radius s1.Angle, numVertices int) []Point {
// We construct the loop in the given frame coordinates, with the center at
// (0, 0, 1). For a loop of radius r, the loop vertices have the form
// (x, y, z) where x^2 + y^2 = sin(r) and z = cos(r). The distance on the
// sphere (arc length) from each vertex to the center is acos(cos(r)) = r.
z := math.Cos(radius.Radians())
r := math.Sin(radius.Radians())
radianStep := 2 * math.Pi / float64(numVertices)
var vertices []Point
for i := 0; i < numVertices; i++ {
angle := float64(i) * radianStep
p := Point{r3.Vector{r * math.Cos(angle), r * math.Sin(angle), z}}
vertices = append(vertices, Point{fromFrame(frame, p).Normalize()})
}
return vertices
}
// CapBound returns a bounding cap for this point.
func (p Point) CapBound() Cap {
return CapFromPoint(p)
}
// RectBound returns a bounding latitude-longitude rectangle from this point.
func (p Point) RectBound() Rect {
return RectFromLatLng(LatLngFromPoint(p))
}
// ContainsCell returns false as Points do not contain any other S2 types.
func (p Point) ContainsCell(c Cell) bool { return false }
// IntersectsCell reports whether this Point intersects the given cell.
func (p Point) IntersectsCell(c Cell) bool {
return c.ContainsPoint(p)
}
// ContainsPoint reports if this Point contains the other Point.
// (This method is named to satisfy the Region interface.)
func (p Point) ContainsPoint(other Point) bool {
return p.Contains(other)
}
// CellUnionBound computes a covering of the Point.
func (p Point) CellUnionBound() []CellID {
return p.CapBound().CellUnionBound()
}
// Contains reports if this Point contains the other Point.
// (This method matches all other s2 types where the reflexive Contains
// method does not contain the type's name.)
func (p Point) Contains(other Point) bool { return p == other }
// Encode encodes the Point.
func (p Point) Encode(w io.Writer) error {
e := &encoder{w: w}
p.encode(e)
return e.err
}
func (p Point) encode(e *encoder) {
e.writeInt8(encodingVersion)
e.writeFloat64(p.X)
e.writeFloat64(p.Y)
e.writeFloat64(p.Z)
}
// Decode decodes the Point.
func (p *Point) Decode(r io.Reader) error {
d := &decoder{r: asByteReader(r)}
p.decode(d)
return d.err
}
func (p *Point) decode(d *decoder) {
version := d.readInt8()
if d.err != nil {
return
}
if version != encodingVersion {
d.err = fmt.Errorf("only version %d is supported", encodingVersion)
return
}
p.X = d.readFloat64()
p.Y = d.readFloat64()
p.Z = d.readFloat64()
}
// Rotate the given point about the given axis by the given angle. p and
// axis must be unit length; angle has no restrictions (e.g., it can be
// positive, negative, greater than 360 degrees, etc).
func Rotate(p, axis Point, angle s1.Angle) Point {
// Let M be the plane through P that is perpendicular to axis, and let
// center be the point where M intersects axis. We construct a
// right-handed orthogonal frame (dx, dy, center) such that dx is the
// vector from center to P, and dy has the same length as dx. The
// result can then be expressed as (cos(angle)*dx + sin(angle)*dy + center).
center := axis.Mul(p.Dot(axis.Vector))
dx := p.Sub(center)
dy := axis.Cross(p.Vector)
// Mathematically the result is unit length, but normalization is necessary
// to ensure that numerical errors don't accumulate.
return Point{dx.Mul(math.Cos(angle.Radians())).Add(dy.Mul(math.Sin(angle.Radians()))).Add(center).Normalize()}
}

View File

@ -1,149 +0,0 @@
// Copyright 2018 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"
"github.com/golang/geo/s1"
)
// PointArea returns the area of triangle ABC. This method combines two different
// algorithms to get accurate results for both large and small triangles.
// The maximum error is about 5e-15 (about 0.25 square meters on the Earth's
// surface), the same as GirardArea below, but unlike that method it is
// also accurate for small triangles. Example: when the true area is 100
// square meters, PointArea yields an error about 1 trillion times smaller than
// GirardArea.
//
// All points should be unit length, and no two points should be antipodal.
// The area is always positive.
func PointArea(a, b, c Point) float64 {
// This method is based on l'Huilier's theorem,
//
// tan(E/4) = sqrt(tan(s/2) tan((s-a)/2) tan((s-b)/2) tan((s-c)/2))
//
// where E is the spherical excess of the triangle (i.e. its area),
// a, b, c are the side lengths, and
// s is the semiperimeter (a + b + c) / 2.
//
// The only significant source of error using l'Huilier's method is the
// cancellation error of the terms (s-a), (s-b), (s-c). This leads to a
// *relative* error of about 1e-16 * s / min(s-a, s-b, s-c). This compares
// to a relative error of about 1e-15 / E using Girard's formula, where E is
// the true area of the triangle. Girard's formula can be even worse than
// this for very small triangles, e.g. a triangle with a true area of 1e-30
// might evaluate to 1e-5.
//
// So, we prefer l'Huilier's formula unless dmin < s * (0.1 * E), where
// dmin = min(s-a, s-b, s-c). This basically includes all triangles
// except for extremely long and skinny ones.
//
// Since we don't know E, we would like a conservative upper bound on
// the triangle area in terms of s and dmin. It's possible to show that
// E <= k1 * s * sqrt(s * dmin), where k1 = 2*sqrt(3)/Pi (about 1).
// Using this, it's easy to show that we should always use l'Huilier's
// method if dmin >= k2 * s^5, where k2 is about 1e-2. Furthermore,
// if dmin < k2 * s^5, the triangle area is at most k3 * s^4, where
// k3 is about 0.1. Since the best case error using Girard's formula
// is about 1e-15, this means that we shouldn't even consider it unless
// s >= 3e-4 or so.
sa := float64(b.Angle(c.Vector))
sb := float64(c.Angle(a.Vector))
sc := float64(a.Angle(b.Vector))
s := 0.5 * (sa + sb + sc)
if s >= 3e-4 {
// Consider whether Girard's formula might be more accurate.
dmin := s - math.Max(sa, math.Max(sb, sc))
if dmin < 1e-2*s*s*s*s*s {
// This triangle is skinny enough to use Girard's formula.
area := GirardArea(a, b, c)
if dmin < s*0.1*area {
return area
}
}
}
// Use l'Huilier's formula.
return 4 * math.Atan(math.Sqrt(math.Max(0.0, math.Tan(0.5*s)*math.Tan(0.5*(s-sa))*
math.Tan(0.5*(s-sb))*math.Tan(0.5*(s-sc)))))
}
// GirardArea returns the area of the triangle computed using Girard's formula.
// All points should be unit length, and no two points should be antipodal.
//
// This method is about twice as fast as PointArea() but has poor relative
// accuracy for small triangles. The maximum error is about 5e-15 (about
// 0.25 square meters on the Earth's surface) and the average error is about
// 1e-15. These bounds apply to triangles of any size, even as the maximum
// edge length of the triangle approaches 180 degrees. But note that for
// such triangles, tiny perturbations of the input points can change the
// true mathematical area dramatically.
func GirardArea(a, b, c Point) float64 {
// This is equivalent to the usual Girard's formula but is slightly more
// accurate, faster to compute, and handles a == b == c without a special
// case. PointCross is necessary to get good accuracy when two of
// the input points are very close together.
ab := a.PointCross(b)
bc := b.PointCross(c)
ac := a.PointCross(c)
area := float64(ab.Angle(ac.Vector) - ab.Angle(bc.Vector) + bc.Angle(ac.Vector))
if area < 0 {
area = 0
}
return area
}
// SignedArea returns a positive value for counterclockwise triangles and a negative
// value otherwise (similar to PointArea).
func SignedArea(a, b, c Point) float64 {
return float64(RobustSign(a, b, c)) * PointArea(a, b, c)
}
// Angle returns the interior angle at the vertex B in the triangle ABC. The
// return value is always in the range [0, pi]. All points should be
// normalized. Ensures that Angle(a,b,c) == Angle(c,b,a) for all a,b,c.
//
// The angle is undefined if A or C is diametrically opposite from B, and
// becomes numerically unstable as the length of edge AB or BC approaches
// 180 degrees.
func Angle(a, b, c Point) s1.Angle {
// PointCross is necessary to get good accuracy when two of the input
// points are very close together.
return a.PointCross(b).Angle(c.PointCross(b).Vector)
}
// TurnAngle returns the exterior angle at vertex B in the triangle ABC. The
// return value is positive if ABC is counterclockwise and negative otherwise.
// If you imagine an ant walking from A to B to C, this is the angle that the
// ant turns at vertex B (positive = left = CCW, negative = right = CW).
// This quantity is also known as the "geodesic curvature" at B.
//
// Ensures that TurnAngle(a,b,c) == -TurnAngle(c,b,a) for all distinct
// a,b,c. The result is undefined if (a == b || b == c), but is either
// -Pi or Pi if (a == c). All points should be normalized.
func TurnAngle(a, b, c Point) s1.Angle {
// We use PointCross to get good accuracy when two points are very
// close together, and RobustSign to ensure that the sign is correct for
// turns that are close to 180 degrees.
angle := a.PointCross(b).Angle(b.PointCross(c).Vector)
// Don't return RobustSign * angle because it is legal to have (a == c).
if RobustSign(a, b, c) == CounterClockwise {
return angle
}
return -angle
}

View File

@ -1,42 +0,0 @@
// 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
// Shape interface enforcement
var (
_ Shape = (*PointVector)(nil)
)
// PointVector is a Shape representing a set of Points. Each point
// is represented as a degenerate edge with the same starting and ending
// vertices.
//
// This type is useful for adding a collection of points to an ShapeIndex.
//
// Its methods are on *PointVector due to implementation details of ShapeIndex.
type PointVector []Point
func (p *PointVector) NumEdges() int { return len(*p) }
func (p *PointVector) Edge(i int) Edge { return Edge{(*p)[i], (*p)[i]} }
func (p *PointVector) ReferencePoint() ReferencePoint { return OriginReferencePoint(false) }
func (p *PointVector) NumChains() int { return len(*p) }
func (p *PointVector) Chain(i int) Chain { return Chain{i, 1} }
func (p *PointVector) ChainEdge(i, j int) Edge { return Edge{(*p)[i], (*p)[j]} }
func (p *PointVector) ChainPosition(e int) ChainPosition { return ChainPosition{e, 0} }
func (p *PointVector) Dimension() int { return 0 }
func (p *PointVector) IsEmpty() bool { return defaultShapeIsEmpty(p) }
func (p *PointVector) IsFull() bool { return defaultShapeIsFull(p) }
func (p *PointVector) typeTag() typeTag { return typeTagPointVector }
func (p *PointVector) privateInterface() {}

View File

@ -1,319 +0,0 @@
// 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
import (
"errors"
"fmt"
"github.com/golang/geo/r3"
)
// maxEncodedVertices is the maximum number of vertices, in a row, to be encoded or decoded.
// On decode, this defends against malicious encodings that try and have us exceed RAM.
const maxEncodedVertices = 50000000
// xyzFaceSiTi represents the The XYZ and face,si,ti coordinates of a Point
// and, if this point is equal to the center of a Cell, the level of this cell
// (-1 otherwise). This is used for Loops and Polygons to store data in a more
// compressed format.
type xyzFaceSiTi struct {
xyz Point
face int
si, ti uint32
level int
}
const derivativeEncodingOrder = 2
func appendFace(faces []faceRun, face int) []faceRun {
if len(faces) == 0 || faces[len(faces)-1].face != face {
return append(faces, faceRun{face, 1})
}
faces[len(faces)-1].count++
return faces
}
// encodePointsCompressed uses an optimized compressed format to encode the given values.
func encodePointsCompressed(e *encoder, vertices []xyzFaceSiTi, level int) {
var faces []faceRun
for _, v := range vertices {
faces = appendFace(faces, v.face)
}
encodeFaces(e, faces)
type piQi struct {
pi, qi uint32
}
verticesPiQi := make([]piQi, len(vertices))
for i, v := range vertices {
verticesPiQi[i] = piQi{siTitoPiQi(v.si, level), siTitoPiQi(v.ti, level)}
}
piCoder, qiCoder := newNthDerivativeCoder(derivativeEncodingOrder), newNthDerivativeCoder(derivativeEncodingOrder)
for i, v := range verticesPiQi {
f := encodePointCompressed
if i == 0 {
// The first point will be just the (pi, qi) coordinates
// of the Point. NthDerivativeCoder will not save anything
// in that case, so we encode in fixed format rather than varint
// to avoid the varint overhead.
f = encodeFirstPointFixedLength
}
f(e, v.pi, v.qi, level, piCoder, qiCoder)
}
var offCenter []int
for i, v := range vertices {
if v.level != level {
offCenter = append(offCenter, i)
}
}
e.writeUvarint(uint64(len(offCenter)))
for _, idx := range offCenter {
e.writeUvarint(uint64(idx))
e.writeFloat64(vertices[idx].xyz.X)
e.writeFloat64(vertices[idx].xyz.Y)
e.writeFloat64(vertices[idx].xyz.Z)
}
}
func encodeFirstPointFixedLength(e *encoder, pi, qi uint32, level int, piCoder, qiCoder *nthDerivativeCoder) {
// Do not ZigZagEncode the first point, since it cannot be negative.
codedPi, codedQi := piCoder.encode(int32(pi)), qiCoder.encode(int32(qi))
// Interleave to reduce overhead from two partial bytes to one.
interleaved := interleaveUint32(uint32(codedPi), uint32(codedQi))
// Write as little endian.
bytesRequired := (level + 7) / 8 * 2
for i := 0; i < bytesRequired; i++ {
e.writeUint8(uint8(interleaved))
interleaved >>= 8
}
}
// encodePointCompressed encodes points into e.
// Given a sequence of Points assumed to be the center of level-k cells,
// compresses it into a stream using the following method:
// - decompose the points into (face, si, ti) tuples.
// - run-length encode the faces, combining face number and count into a
// varint32. See the faceRun struct.
// - right shift the (si, ti) to remove the part that's constant for all cells
// of level-k. The result is called the (pi, qi) space.
// - 2nd derivative encode the pi and qi sequences (linear prediction)
// - zig-zag encode all derivative values but the first, which cannot be
// negative
// - interleave the zig-zag encoded values
// - encode the first interleaved value in a fixed length encoding
// (varint would make this value larger)
// - encode the remaining interleaved values as varint64s, as the
// derivative encoding should make the values small.
// In addition, provides a lossless method to compress a sequence of points even
// if some points are not the center of level-k cells. These points are stored
// exactly, using 3 double precision values, after the above encoded string,
// together with their index in the sequence (this leads to some redundancy - it
// is expected that only a small fraction of the points are not cell centers).
//
// To encode leaf cells, this requires 8 bytes for the first vertex plus
// an average of 3.8 bytes for each additional vertex, when computed on
// Google's geographic repository.
func encodePointCompressed(e *encoder, pi, qi uint32, level int, piCoder, qiCoder *nthDerivativeCoder) {
// ZigZagEncode, as varint requires the maximum number of bytes for
// negative numbers.
zzPi := zigzagEncode(piCoder.encode(int32(pi)))
zzQi := zigzagEncode(qiCoder.encode(int32(qi)))
// Interleave to reduce overhead from two partial bytes to one.
interleaved := interleaveUint32(zzPi, zzQi)
e.writeUvarint(interleaved)
}
type faceRun struct {
face, count int
}
func decodeFaceRun(d *decoder) faceRun {
faceAndCount := d.readUvarint()
ret := faceRun{
face: int(faceAndCount % numFaces),
count: int(faceAndCount / numFaces),
}
if ret.count <= 0 && d.err == nil {
d.err = errors.New("non-positive count for face run")
}
return ret
}
func decodeFaces(numVertices int, d *decoder) []faceRun {
var frs []faceRun
for nparsed := 0; nparsed < numVertices; {
fr := decodeFaceRun(d)
if d.err != nil {
return nil
}
frs = append(frs, fr)
nparsed += fr.count
}
return frs
}
// encodeFaceRun encodes each faceRun as a varint64 with value numFaces * count + face.
func encodeFaceRun(e *encoder, fr faceRun) {
// It isn't necessary to encode the number of faces left for the last run,
// but since this would only help if there were more than 21 faces, it will
// be a small overall savings, much smaller than the bound encoding.
coded := numFaces*uint64(fr.count) + uint64(fr.face)
e.writeUvarint(coded)
}
func encodeFaces(e *encoder, frs []faceRun) {
for _, fr := range frs {
encodeFaceRun(e, fr)
}
}
type facesIterator struct {
faces []faceRun
// How often have we yet shown the current face?
numCurrentFaceShown int
curFace int
}
func (fi *facesIterator) next() (ok bool) {
if len(fi.faces) == 0 {
return false
}
fi.curFace = fi.faces[0].face
fi.numCurrentFaceShown++
// Advance fs if needed.
if fi.faces[0].count <= fi.numCurrentFaceShown {
fi.faces = fi.faces[1:]
fi.numCurrentFaceShown = 0
}
return true
}
func decodePointsCompressed(d *decoder, level int, target []Point) {
faces := decodeFaces(len(target), d)
piCoder := newNthDerivativeCoder(derivativeEncodingOrder)
qiCoder := newNthDerivativeCoder(derivativeEncodingOrder)
iter := facesIterator{faces: faces}
for i := range target {
decodeFn := decodePointCompressed
if i == 0 {
decodeFn = decodeFirstPointFixedLength
}
pi, qi := decodeFn(d, level, piCoder, qiCoder)
if ok := iter.next(); !ok && d.err == nil {
d.err = fmt.Errorf("ran out of faces at target %d", i)
return
}
target[i] = Point{facePiQitoXYZ(iter.curFace, pi, qi, level)}
}
numOffCenter := int(d.readUvarint())
if d.err != nil {
return
}
if numOffCenter > len(target) {
d.err = fmt.Errorf("numOffCenter = %d, should be at most len(target) = %d", numOffCenter, len(target))
return
}
for i := 0; i < numOffCenter; i++ {
idx := int(d.readUvarint())
if d.err != nil {
return
}
if idx >= len(target) {
d.err = fmt.Errorf("off center index = %d, should be < len(target) = %d", idx, len(target))
return
}
target[idx].X = d.readFloat64()
target[idx].Y = d.readFloat64()
target[idx].Z = d.readFloat64()
}
}
func decodeFirstPointFixedLength(d *decoder, level int, piCoder, qiCoder *nthDerivativeCoder) (pi, qi uint32) {
bytesToRead := (level + 7) / 8 * 2
var interleaved uint64
for i := 0; i < bytesToRead; i++ {
rr := d.readUint8()
interleaved |= (uint64(rr) << uint(i*8))
}
piCoded, qiCoded := deinterleaveUint32(interleaved)
return uint32(piCoder.decode(int32(piCoded))), uint32(qiCoder.decode(int32(qiCoded)))
}
func zigzagEncode(x int32) uint32 {
return (uint32(x) << 1) ^ uint32(x>>31)
}
func zigzagDecode(x uint32) int32 {
return int32((x >> 1) ^ uint32((int32(x&1)<<31)>>31))
}
func decodePointCompressed(d *decoder, level int, piCoder, qiCoder *nthDerivativeCoder) (pi, qi uint32) {
interleavedZigZagEncodedDerivPiQi := d.readUvarint()
piZigzag, qiZigzag := deinterleaveUint32(interleavedZigZagEncodedDerivPiQi)
return uint32(piCoder.decode(zigzagDecode(piZigzag))), uint32(qiCoder.decode(zigzagDecode(qiZigzag)))
}
// We introduce a new coordinate system (pi, qi), which is (si, ti)
// with the bits that are constant for cells of that level shifted
// off to the right.
// si = round(s * 2^31)
// pi = si >> (31 - level)
// = floor(s * 2^level)
// If the point has been snapped to the level, the bits that are
// shifted off will be a 1 in the msb, then 0s after that, so the
// fractional part discarded by the cast is (close to) 0.5.
// stToPiQi returns the value transformed to the PiQi coordinate space.
func stToPiQi(s float64, level uint) uint32 {
return uint32(s * float64(int(1)<<level))
}
// siTiToPiQi returns the value transformed into the PiQi coordinate spade.
// encodeFirstPointFixedLength encodes the return value using level bits,
// so we clamp si to the range [0, 2**level - 1] before trying to encode
// it. This is okay because if si == maxSiTi, then it is not a cell center
// anyway and will be encoded separately as an off-center point.
func siTitoPiQi(siTi uint32, level int) uint32 {
s := uint(siTi)
const max = maxSiTi - 1
if s > max {
s = max
}
return uint32(s >> (maxLevel + 1 - uint(level)))
}
// piQiToST returns the value transformed to ST space.
func piQiToST(pi uint32, level int) float64 {
// We want to recover the position at the center of the cell. If the point
// was snapped to the center of the cell, then math.Modf(s * 2^level) == 0.5.
// Inverting STtoPiQi gives:
// s = (pi + 0.5) / 2^level.
return (float64(pi) + 0.5) / float64(int(1)<<uint(level))
}
func facePiQitoXYZ(face int, pi, qi uint32, level int) r3.Vector {
return faceUVToXYZ(face, stToUV(piQiToST(pi, level)), stToUV(piQiToST(qi, level))).Normalize()
}

File diff suppressed because it is too large Load Diff

View File

@ -1,589 +0,0 @@
// 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 (
"fmt"
"io"
"math"
"github.com/golang/geo/s1"
)
// Polyline represents a sequence of zero or more vertices connected by
// straight edges (geodesics). Edges of length 0 and 180 degrees are not
// allowed, i.e. adjacent vertices should not be identical or antipodal.
type Polyline []Point
// PolylineFromLatLngs creates a new Polyline from the given LatLngs.
func PolylineFromLatLngs(points []LatLng) *Polyline {
p := make(Polyline, len(points))
for k, v := range points {
p[k] = PointFromLatLng(v)
}
return &p
}
// Reverse reverses the order of the Polyline vertices.
func (p *Polyline) Reverse() {
for i := 0; i < len(*p)/2; i++ {
(*p)[i], (*p)[len(*p)-i-1] = (*p)[len(*p)-i-1], (*p)[i]
}
}
// Length returns the length of this Polyline.
func (p *Polyline) Length() s1.Angle {
var length s1.Angle
for i := 1; i < len(*p); i++ {
length += (*p)[i-1].Distance((*p)[i])
}
return length
}
// Centroid returns the true centroid of the polyline multiplied by the length of the
// polyline. The result is not unit length, so you may wish to normalize it.
//
// Scaling by the Polyline length makes it easy to compute the centroid
// of several Polylines (by simply adding up their centroids).
func (p *Polyline) Centroid() Point {
var centroid Point
for i := 1; i < len(*p); i++ {
// The centroid (multiplied by length) is a vector toward the midpoint
// of the edge, whose length is twice the sin of half the angle between
// the two vertices. Defining theta to be this angle, we have:
vSum := (*p)[i-1].Add((*p)[i].Vector) // Length == 2*cos(theta)
vDiff := (*p)[i-1].Sub((*p)[i].Vector) // Length == 2*sin(theta)
// Length == 2*sin(theta)
centroid = Point{centroid.Add(vSum.Mul(math.Sqrt(vDiff.Norm2() / vSum.Norm2())))}
}
return centroid
}
// Equal reports whether the given Polyline is exactly the same as this one.
func (p *Polyline) Equal(b *Polyline) bool {
if len(*p) != len(*b) {
return false
}
for i, v := range *p {
if v != (*b)[i] {
return false
}
}
return true
}
// ApproxEqual reports whether two polylines have the same number of vertices,
// and corresponding vertex pairs are separated by no more the standard margin.
func (p *Polyline) ApproxEqual(o *Polyline) bool {
return p.approxEqual(o, s1.Angle(epsilon))
}
// approxEqual reports whether two polylines are equal within the given margin.
func (p *Polyline) approxEqual(o *Polyline, maxError s1.Angle) bool {
if len(*p) != len(*o) {
return false
}
for offset, val := range *p {
if !val.approxEqual((*o)[offset], maxError) {
return false
}
}
return true
}
// CapBound returns the bounding Cap for this Polyline.
func (p *Polyline) CapBound() Cap {
return p.RectBound().CapBound()
}
// RectBound returns the bounding Rect for this Polyline.
func (p *Polyline) RectBound() Rect {
rb := NewRectBounder()
for _, v := range *p {
rb.AddPoint(v)
}
return rb.RectBound()
}
// ContainsCell reports whether this Polyline contains the given Cell. Always returns false
// because "containment" is not numerically well-defined except at the Polyline vertices.
func (p *Polyline) ContainsCell(cell Cell) bool {
return false
}
// IntersectsCell reports whether this Polyline intersects the given Cell.
func (p *Polyline) IntersectsCell(cell Cell) bool {
if len(*p) == 0 {
return false
}
// We only need to check whether the cell contains vertex 0 for correctness,
// but these tests are cheap compared to edge crossings so we might as well
// check all the vertices.
for _, v := range *p {
if cell.ContainsPoint(v) {
return true
}
}
cellVertices := []Point{
cell.Vertex(0),
cell.Vertex(1),
cell.Vertex(2),
cell.Vertex(3),
}
for j := 0; j < 4; j++ {
crosser := NewChainEdgeCrosser(cellVertices[j], cellVertices[(j+1)&3], (*p)[0])
for i := 1; i < len(*p); i++ {
if crosser.ChainCrossingSign((*p)[i]) != DoNotCross {
// There is a proper crossing, or two vertices were the same.
return true
}
}
}
return false
}
// ContainsPoint returns false since Polylines are not closed.
func (p *Polyline) ContainsPoint(point Point) bool {
return false
}
// CellUnionBound computes a covering of the Polyline.
func (p *Polyline) CellUnionBound() []CellID {
return p.CapBound().CellUnionBound()
}
// NumEdges returns the number of edges in this shape.
func (p *Polyline) NumEdges() int {
if len(*p) == 0 {
return 0
}
return len(*p) - 1
}
// Edge returns endpoints for the given edge index.
func (p *Polyline) Edge(i int) Edge {
return Edge{(*p)[i], (*p)[i+1]}
}
// ReferencePoint returns the default reference point with negative containment because Polylines are not closed.
func (p *Polyline) ReferencePoint() ReferencePoint {
return OriginReferencePoint(false)
}
// NumChains reports the number of contiguous edge chains in this Polyline.
func (p *Polyline) NumChains() int {
return minInt(1, p.NumEdges())
}
// Chain returns the i-th edge Chain in the Shape.
func (p *Polyline) Chain(chainID int) Chain {
return Chain{0, p.NumEdges()}
}
// ChainEdge returns the j-th edge of the i-th edge Chain.
func (p *Polyline) ChainEdge(chainID, offset int) Edge {
return Edge{(*p)[offset], (*p)[offset+1]}
}
// ChainPosition returns a pair (i, j) such that edgeID is the j-th edge
func (p *Polyline) ChainPosition(edgeID int) ChainPosition {
return ChainPosition{0, edgeID}
}
// Dimension returns the dimension of the geometry represented by this Polyline.
func (p *Polyline) Dimension() int { return 1 }
// IsEmpty reports whether this shape contains no points.
func (p *Polyline) IsEmpty() bool { return defaultShapeIsEmpty(p) }
// IsFull reports whether this shape contains all points on the sphere.
func (p *Polyline) IsFull() bool { return defaultShapeIsFull(p) }
func (p *Polyline) typeTag() typeTag { return typeTagPolyline }
func (p *Polyline) privateInterface() {}
// findEndVertex reports the maximal end index such that the line segment between
// the start index and this one such that the line segment between these two
// vertices passes within the given tolerance of all interior vertices, in order.
func findEndVertex(p Polyline, tolerance s1.Angle, index int) int {
// The basic idea is to keep track of the "pie wedge" of angles
// from the starting vertex such that a ray from the starting
// vertex at that angle will pass through the discs of radius
// tolerance centered around all vertices processed so far.
//
// First we define a coordinate frame for the tangent and normal
// spaces at the starting vertex. Essentially this means picking
// three orthonormal vectors X,Y,Z such that X and Y span the
// tangent plane at the starting vertex, and Z is up. We use
// the coordinate frame to define a mapping from 3D direction
// vectors to a one-dimensional ray angle in the range (-π,
// π]. The angle of a direction vector is computed by
// transforming it into the X,Y,Z basis, and then calculating
// atan2(y,x). This mapping allows us to represent a wedge of
// angles as a 1D interval. Since the interval wraps around, we
// represent it as an Interval, i.e. an interval on the unit
// circle.
origin := p[index]
frame := getFrame(origin)
// As we go along, we keep track of the current wedge of angles
// and the distance to the last vertex (which must be
// non-decreasing).
currentWedge := s1.FullInterval()
var lastDistance s1.Angle
for index++; index < len(p); index++ {
candidate := p[index]
distance := origin.Distance(candidate)
// We don't allow simplification to create edges longer than
// 90 degrees, to avoid numeric instability as lengths
// approach 180 degrees. We do need to allow for original
// edges longer than 90 degrees, though.
if distance > math.Pi/2 && lastDistance > 0 {
break
}
// Vertices must be in increasing order along the ray, except
// for the initial disc around the origin.
if distance < lastDistance && lastDistance > tolerance {
break
}
lastDistance = distance
// Points that are within the tolerance distance of the origin
// do not constrain the ray direction, so we can ignore them.
if distance <= tolerance {
continue
}
// If the current wedge of angles does not contain the angle
// to this vertex, then stop right now. Note that the wedge
// of possible ray angles is not necessarily empty yet, but we
// can't continue unless we are willing to backtrack to the
// last vertex that was contained within the wedge (since we
// don't create new vertices). This would be more complicated
// and also make the worst-case running time more than linear.
direction := toFrame(frame, candidate)
center := math.Atan2(direction.Y, direction.X)
if !currentWedge.Contains(center) {
break
}
// To determine how this vertex constrains the possible ray
// angles, consider the triangle ABC where A is the origin, B
// is the candidate vertex, and C is one of the two tangent
// points between A and the spherical cap of radius
// tolerance centered at B. Then from the spherical law of
// sines, sin(a)/sin(A) = sin(c)/sin(C), where a and c are
// the lengths of the edges opposite A and C. In our case C
// is a 90 degree angle, therefore A = asin(sin(a) / sin(c)).
// Angle A is the half-angle of the allowable wedge.
halfAngle := math.Asin(math.Sin(tolerance.Radians()) / math.Sin(distance.Radians()))
target := s1.IntervalFromPointPair(center, center).Expanded(halfAngle)
currentWedge = currentWedge.Intersection(target)
}
// We break out of the loop when we reach a vertex index that
// can't be included in the line segment, so back up by one
// vertex.
return index - 1
}
// SubsampleVertices returns a subsequence of vertex indices such that the
// polyline connecting these vertices is never further than the given tolerance from
// the original polyline. Provided the first and last vertices are distinct,
// they are always preserved; if they are not, the subsequence may contain
// only a single index.
//
// Some useful properties of the algorithm:
//
// - It runs in linear time.
//
// - The output always represents a valid polyline. In particular, adjacent
// output vertices are never identical or antipodal.
//
// - The method is not optimal, but it tends to produce 2-3% fewer
// vertices than the Douglas-Peucker algorithm with the same tolerance.
//
// - The output is parametrically equivalent to the original polyline to
// within the given tolerance. For example, if a polyline backtracks on
// itself and then proceeds onwards, the backtracking will be preserved
// (to within the given tolerance). This is different than the
// Douglas-Peucker algorithm which only guarantees geometric equivalence.
func (p *Polyline) SubsampleVertices(tolerance s1.Angle) []int {
var result []int
if len(*p) < 1 {
return result
}
result = append(result, 0)
clampedTolerance := s1.Angle(math.Max(tolerance.Radians(), 0))
for index := 0; index+1 < len(*p); {
nextIndex := findEndVertex(*p, clampedTolerance, index)
// Don't create duplicate adjacent vertices.
if (*p)[nextIndex] != (*p)[index] {
result = append(result, nextIndex)
}
index = nextIndex
}
return result
}
// Encode encodes the Polyline.
func (p Polyline) Encode(w io.Writer) error {
e := &encoder{w: w}
p.encode(e)
return e.err
}
func (p Polyline) encode(e *encoder) {
e.writeInt8(encodingVersion)
e.writeUint32(uint32(len(p)))
for _, v := range p {
e.writeFloat64(v.X)
e.writeFloat64(v.Y)
e.writeFloat64(v.Z)
}
}
// Decode decodes the polyline.
func (p *Polyline) Decode(r io.Reader) error {
d := decoder{r: asByteReader(r)}
p.decode(d)
return d.err
}
func (p *Polyline) decode(d decoder) {
version := d.readInt8()
if d.err != nil {
return
}
if int(version) != int(encodingVersion) {
d.err = fmt.Errorf("can't decode version %d; my version: %d", version, encodingVersion)
return
}
nvertices := d.readUint32()
if d.err != nil {
return
}
if nvertices > maxEncodedVertices {
d.err = fmt.Errorf("too many vertices (%d; max is %d)", nvertices, maxEncodedVertices)
return
}
*p = make([]Point, nvertices)
for i := range *p {
(*p)[i].X = d.readFloat64()
(*p)[i].Y = d.readFloat64()
(*p)[i].Z = d.readFloat64()
}
}
// Project returns a point on the polyline that is closest to the given point,
// and the index of the next vertex after the projected point. The
// value of that index is always in the range [1, len(polyline)].
// The polyline must not be empty.
func (p *Polyline) Project(point Point) (Point, int) {
if len(*p) == 1 {
// If there is only one vertex, it is always closest to any given point.
return (*p)[0], 1
}
// Initial value larger than any possible distance on the unit sphere.
minDist := 10 * s1.Radian
minIndex := -1
// Find the line segment in the polyline that is closest to the point given.
for i := 1; i < len(*p); i++ {
if dist := DistanceFromSegment(point, (*p)[i-1], (*p)[i]); dist < minDist {
minDist = dist
minIndex = i
}
}
// Compute the point on the segment found that is closest to the point given.
closest := Project(point, (*p)[minIndex-1], (*p)[minIndex])
if closest == (*p)[minIndex] {
minIndex++
}
return closest, minIndex
}
// IsOnRight reports whether the point given is on the right hand side of the
// polyline, using a naive definition of "right-hand-sideness" where the point
// is on the RHS of the polyline iff the point is on the RHS of the line segment
// in the polyline which it is closest to.
// The polyline must have at least 2 vertices.
func (p *Polyline) IsOnRight(point Point) bool {
// If the closest point C is an interior vertex of the polyline, let B and D
// be the previous and next vertices. The given point P is on the right of
// the polyline (locally) if B, P, D are ordered CCW around vertex C.
closest, next := p.Project(point)
if closest == (*p)[next-1] && next > 1 && next < len(*p) {
if point == (*p)[next-1] {
// Polyline vertices are not on the RHS.
return false
}
return OrderedCCW((*p)[next-2], point, (*p)[next], (*p)[next-1])
}
// Otherwise, the closest point C is incident to exactly one polyline edge.
// We test the point P against that edge.
if next == len(*p) {
next--
}
return Sign(point, (*p)[next], (*p)[next-1])
}
// Validate checks whether this is a valid polyline or not.
func (p *Polyline) Validate() error {
// All vertices must be unit length.
for i, pt := range *p {
if !pt.IsUnit() {
return fmt.Errorf("vertex %d is not unit length", i)
}
}
// Adjacent vertices must not be identical or antipodal.
for i := 1; i < len(*p); i++ {
prev, cur := (*p)[i-1], (*p)[i]
if prev == cur {
return fmt.Errorf("vertices %d and %d are identical", i-1, i)
}
if prev == (Point{cur.Mul(-1)}) {
return fmt.Errorf("vertices %d and %d are antipodal", i-1, i)
}
}
return nil
}
// Intersects reports whether this polyline intersects the given polyline. If
// the polylines share a vertex they are considered to be intersecting. When a
// polyline endpoint is the only intersection with the other polyline, the
// function may return true or false arbitrarily.
//
// The running time is quadratic in the number of vertices.
func (p *Polyline) Intersects(o *Polyline) bool {
if len(*p) == 0 || len(*o) == 0 {
return false
}
if !p.RectBound().Intersects(o.RectBound()) {
return false
}
// TODO(roberts): Use ShapeIndex here.
for i := 1; i < len(*p); i++ {
crosser := NewChainEdgeCrosser((*p)[i-1], (*p)[i], (*o)[0])
for j := 1; j < len(*o); j++ {
if crosser.ChainCrossingSign((*o)[j]) != DoNotCross {
return true
}
}
}
return false
}
// Interpolate returns the point whose distance from vertex 0 along the polyline is
// the given fraction of the polyline's total length, and the index of
// the next vertex after the interpolated point P. Fractions less than zero
// or greater than one are clamped. The return value is unit length. The cost of
// this function is currently linear in the number of vertices.
//
// This method allows the caller to easily construct a given suffix of the
// polyline by concatenating P with the polyline vertices starting at that next
// vertex. Note that P is guaranteed to be different than the point at the next
// vertex, so this will never result in a duplicate vertex.
//
// The polyline must not be empty. Note that if fraction >= 1.0, then the next
// vertex will be set to len(p) (indicating that no vertices from the polyline
// need to be appended). The value of the next vertex is always between 1 and
// len(p).
//
// This method can also be used to construct a prefix of the polyline, by
// taking the polyline vertices up to next vertex-1 and appending the
// returned point P if it is different from the last vertex (since in this
// case there is no guarantee of distinctness).
func (p *Polyline) Interpolate(fraction float64) (Point, int) {
// We intentionally let the (fraction >= 1) case fall through, since
// we need to handle it in the loop below in any case because of
// possible roundoff errors.
if fraction <= 0 {
return (*p)[0], 1
}
target := s1.Angle(fraction) * p.Length()
for i := 1; i < len(*p); i++ {
length := (*p)[i-1].Distance((*p)[i])
if target < length {
// This interpolates with respect to arc length rather than
// straight-line distance, and produces a unit-length result.
result := InterpolateAtDistance(target, (*p)[i-1], (*p)[i])
// It is possible that (result == vertex(i)) due to rounding errors.
if result == (*p)[i] {
return result, i + 1
}
return result, i
}
target -= length
}
return (*p)[len(*p)-1], len(*p)
}
// Uninterpolate is the inverse operation of Interpolate. Given a point on the
// polyline, it returns the ratio of the distance to the point from the
// beginning of the polyline over the length of the polyline. The return
// value is always betwen 0 and 1 inclusive.
//
// The polyline should not be empty. If it has fewer than 2 vertices, the
// return value is zero.
func (p *Polyline) Uninterpolate(point Point, nextVertex int) float64 {
if len(*p) < 2 {
return 0
}
var sum s1.Angle
for i := 1; i < nextVertex; i++ {
sum += (*p)[i-1].Distance((*p)[i])
}
lengthToPoint := sum + (*p)[nextVertex-1].Distance(point)
for i := nextVertex; i < len(*p); i++ {
sum += (*p)[i-1].Distance((*p)[i])
}
// The ratio can be greater than 1.0 due to rounding errors or because the
// point is not exactly on the polyline.
return minFloat64(1.0, float64(lengthToPoint/sum))
}
// TODO(roberts): Differences from C++.
// NearlyCoversPolyline
// InitToSnapped
// InitToSimplified
// SnapLevel
// encode/decode compressed

View File

@ -1,53 +0,0 @@
// Copyright 2018 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
// This file defines various measures for polylines on the sphere. These are
// low-level methods that work directly with arrays of Points. They are used to
// implement the methods in various other measures files.
import (
"github.com/golang/geo/r3"
"github.com/golang/geo/s1"
)
// polylineLength returns the length of the given Polyline.
// It returns 0 for polylines with fewer than two vertices.
func polylineLength(p []Point) s1.Angle {
var length s1.Angle
for i := 1; i < len(p); i++ {
length += p[i-1].Distance(p[i])
}
return length
}
// polylineCentroid returns the true centroid of the polyline multiplied by the
// length of the polyline. The result is not unit length, so you may wish to
// normalize it.
//
// Scaling by the Polyline length makes it easy to compute the centroid
// of several Polylines (by simply adding up their centroids).
//
// Note that for degenerate Polylines (e.g., AA) this returns Point(0, 0, 0).
// (This answer is correct; the result of this function is a line integral over
// the polyline, whose value is always zero if the polyline is degenerate.)
func polylineCentroid(p []Point) Point {
var centroid r3.Vector
for i := 1; i < len(p); i++ {
centroid = centroid.Add(EdgeTrueCentroid(p[i-1], p[i]).Vector)
}
return Point{centroid}
}

View File

@ -1,701 +0,0 @@
// 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
// This file contains various predicates that are guaranteed to produce
// correct, consistent results. They are also relatively efficient. This is
// achieved by computing conservative error bounds and falling back to high
// precision or even exact arithmetic when the result is uncertain. Such
// predicates are useful in implementing robust algorithms.
//
// See also EdgeCrosser, which implements various exact
// edge-crossing predicates more efficiently than can be done here.
import (
"math"
"math/big"
"github.com/golang/geo/r3"
"github.com/golang/geo/s1"
)
const (
// If any other machine architectures need to be suppported, these next three
// values will need to be updated.
// epsilon is a small number that represents a reasonable level of noise between two
// values that can be considered to be equal.
epsilon = 1e-15
// dblEpsilon is a smaller number for values that require more precision.
// This is the C++ DBL_EPSILON equivalent.
dblEpsilon = 2.220446049250313e-16
// dblError is the C++ value for S2 rounding_epsilon().
dblError = 1.110223024625156e-16
// maxDeterminantError is the maximum error in computing (AxB).C where all vectors
// are unit length. Using standard inequalities, it can be shown that
//
// fl(AxB) = AxB + D where |D| <= (|AxB| + (2/sqrt(3))*|A|*|B|) * e
//
// where "fl()" denotes a calculation done in floating-point arithmetic,
// |x| denotes either absolute value or the L2-norm as appropriate, and
// e is a reasonably small value near the noise level of floating point
// number accuracy. Similarly,
//
// fl(B.C) = B.C + d where |d| <= (|B.C| + 2*|B|*|C|) * e .
//
// Applying these bounds to the unit-length vectors A,B,C and neglecting
// relative error (which does not affect the sign of the result), we get
//
// fl((AxB).C) = (AxB).C + d where |d| <= (3 + 2/sqrt(3)) * e
maxDeterminantError = 1.8274 * dblEpsilon
// detErrorMultiplier is the factor to scale the magnitudes by when checking
// for the sign of set of points with certainty. Using a similar technique to
// the one used for maxDeterminantError, the error is at most:
//
// |d| <= (3 + 6/sqrt(3)) * |A-C| * |B-C| * e
//
// If the determinant magnitude is larger than this value then we know
// its sign with certainty.
detErrorMultiplier = 3.2321 * dblEpsilon
)
// Direction is an indication of the ordering of a set of points.
type Direction int
// These are the three options for the direction of a set of points.
const (
Clockwise Direction = -1
Indeterminate Direction = 0
CounterClockwise Direction = 1
)
// newBigFloat constructs a new big.Float with maximum precision.
func newBigFloat() *big.Float { return new(big.Float).SetPrec(big.MaxPrec) }
// Sign returns true if the points A, B, C are strictly counterclockwise,
// and returns false if the points are clockwise or collinear (i.e. if they are all
// contained on some great circle).
//
// Due to numerical errors, situations may arise that are mathematically
// impossible, e.g. ABC may be considered strictly CCW while BCA is not.
// However, the implementation guarantees the following:
//
// If Sign(a,b,c), then !Sign(c,b,a) for all a,b,c.
func Sign(a, b, c Point) bool {
// NOTE(dnadasi): In the C++ API the equivalent method here was known as "SimpleSign".
// We compute the signed volume of the parallelepiped ABC. The usual
// formula for this is (A B) · C, but we compute it here using (C A) · B
// in order to ensure that ABC and CBA are not both CCW. This follows
// from the following identities (which are true numerically, not just
// mathematically):
//
// (1) x y == -(y x)
// (2) -x · y == -(x · y)
return c.Cross(a.Vector).Dot(b.Vector) > 0
}
// RobustSign returns a Direction representing the ordering of the points.
// CounterClockwise is returned if the points are in counter-clockwise order,
// Clockwise for clockwise, and Indeterminate if any two points are the same (collinear),
// or the sign could not completely be determined.
//
// This function has additional logic to make sure that the above properties hold even
// when the three points are coplanar, and to deal with the limitations of
// floating-point arithmetic.
//
// RobustSign satisfies the following conditions:
//
// (1) RobustSign(a,b,c) == Indeterminate if and only if a == b, b == c, or c == a
// (2) RobustSign(b,c,a) == RobustSign(a,b,c) for all a,b,c
// (3) RobustSign(c,b,a) == -RobustSign(a,b,c) for all a,b,c
//
// In other words:
//
// (1) The result is Indeterminate if and only if two points are the same.
// (2) Rotating the order of the arguments does not affect the result.
// (3) Exchanging any two arguments inverts the result.
//
// On the other hand, note that it is not true in general that
// RobustSign(-a,b,c) == -RobustSign(a,b,c), or any similar identities
// involving antipodal points.
func RobustSign(a, b, c Point) Direction {
sign := triageSign(a, b, c)
if sign == Indeterminate {
sign = expensiveSign(a, b, c)
}
return sign
}
// stableSign reports the direction sign of the points in a numerically stable way.
// Unlike triageSign, this method can usually compute the correct determinant sign
// even when all three points are as collinear as possible. For example if three
// points are spaced 1km apart along a random line on the Earth's surface using
// the nearest representable points, there is only a 0.4% chance that this method
// will not be able to find the determinant sign. The probability of failure
// decreases as the points get closer together; if the collinear points are 1 meter
// apart, the failure rate drops to 0.0004%.
//
// This method could be extended to also handle nearly-antipodal points, but antipodal
// points are rare in practice so it seems better to simply fall back to
// exact arithmetic in that case.
func stableSign(a, b, c Point) Direction {
ab := b.Sub(a.Vector)
ab2 := ab.Norm2()
bc := c.Sub(b.Vector)
bc2 := bc.Norm2()
ca := a.Sub(c.Vector)
ca2 := ca.Norm2()
// Now compute the determinant ((A-C)x(B-C)).C, where the vertices have been
// cyclically permuted if necessary so that AB is the longest edge. (This
// minimizes the magnitude of cross product.) At the same time we also
// compute the maximum error in the determinant.
// The two shortest edges, pointing away from their common point.
var e1, e2, op r3.Vector
if ab2 >= bc2 && ab2 >= ca2 {
// AB is the longest edge.
e1, e2, op = ca, bc, c.Vector
} else if bc2 >= ca2 {
// BC is the longest edge.
e1, e2, op = ab, ca, a.Vector
} else {
// CA is the longest edge.
e1, e2, op = bc, ab, b.Vector
}
det := -e1.Cross(e2).Dot(op)
maxErr := detErrorMultiplier * math.Sqrt(e1.Norm2()*e2.Norm2())
// If the determinant isn't zero, within maxErr, we know definitively the point ordering.
if det > maxErr {
return CounterClockwise
}
if det < -maxErr {
return Clockwise
}
return Indeterminate
}
// triageSign returns the direction sign of the points. It returns Indeterminate if two
// points are identical or the result is uncertain. Uncertain cases can be resolved, if
// desired, by calling expensiveSign.
//
// The purpose of this method is to allow additional cheap tests to be done without
// calling expensiveSign.
func triageSign(a, b, c Point) Direction {
det := a.Cross(b.Vector).Dot(c.Vector)
if det > maxDeterminantError {
return CounterClockwise
}
if det < -maxDeterminantError {
return Clockwise
}
return Indeterminate
}
// expensiveSign reports the direction sign of the points. It returns Indeterminate
// if two of the input points are the same. It uses multiple-precision arithmetic
// to ensure that its results are always self-consistent.
func expensiveSign(a, b, c Point) Direction {
// Return Indeterminate if and only if two points are the same.
// This ensures RobustSign(a,b,c) == Indeterminate if and only if a == b, b == c, or c == a.
// ie. Property 1 of RobustSign.
if a == b || b == c || c == a {
return Indeterminate
}
// Next we try recomputing the determinant still using floating-point
// arithmetic but in a more precise way. This is more expensive than the
// simple calculation done by triageSign, but it is still *much* cheaper
// than using arbitrary-precision arithmetic. This optimization is able to
// compute the correct determinant sign in virtually all cases except when
// the three points are truly collinear (e.g., three points on the equator).
detSign := stableSign(a, b, c)
if detSign != Indeterminate {
return detSign
}
// Otherwise fall back to exact arithmetic and symbolic permutations.
return exactSign(a, b, c, true)
}
// exactSign reports the direction sign of the points computed using high-precision
// arithmetic and/or symbolic perturbations.
func exactSign(a, b, c Point, perturb bool) Direction {
// Sort the three points in lexicographic order, keeping track of the sign
// of the permutation. (Each exchange inverts the sign of the determinant.)
permSign := CounterClockwise
pa := &a
pb := &b
pc := &c
if pa.Cmp(pb.Vector) > 0 {
pa, pb = pb, pa
permSign = -permSign
}
if pb.Cmp(pc.Vector) > 0 {
pb, pc = pc, pb
permSign = -permSign
}
if pa.Cmp(pb.Vector) > 0 {
pa, pb = pb, pa
permSign = -permSign
}
// Construct multiple-precision versions of the sorted points and compute
// their precise 3x3 determinant.
xa := r3.PreciseVectorFromVector(pa.Vector)
xb := r3.PreciseVectorFromVector(pb.Vector)
xc := r3.PreciseVectorFromVector(pc.Vector)
xbCrossXc := xb.Cross(xc)
det := xa.Dot(xbCrossXc)
// The precision of big.Float is high enough that the result should always
// be exact enough (no rounding was performed).
// If the exact determinant is non-zero, we're done.
detSign := Direction(det.Sign())
if detSign == Indeterminate && perturb {
// Otherwise, we need to resort to symbolic perturbations to resolve the
// sign of the determinant.
detSign = symbolicallyPerturbedSign(xa, xb, xc, xbCrossXc)
}
return permSign * detSign
}
// symbolicallyPerturbedSign reports the sign of the determinant of three points
// A, B, C under a model where every possible Point is slightly perturbed by
// a unique infinitesmal amount such that no three perturbed points are
// collinear and no four points are coplanar. The perturbations are so small
// that they do not change the sign of any determinant that was non-zero
// before the perturbations, and therefore can be safely ignored unless the
// determinant of three points is exactly zero (using multiple-precision
// arithmetic). This returns CounterClockwise or Clockwise according to the
// sign of the determinant after the symbolic perturbations are taken into account.
//
// Since the symbolic perturbation of a given point is fixed (i.e., the
// perturbation is the same for all calls to this method and does not depend
// on the other two arguments), the results of this method are always
// self-consistent. It will never return results that would correspond to an
// impossible configuration of non-degenerate points.
//
// This requires that the 3x3 determinant of A, B, C must be exactly zero.
// And the points must be distinct, with A < B < C in lexicographic order.
//
// Reference:
// "Simulation of Simplicity" (Edelsbrunner and Muecke, ACM Transactions on
// Graphics, 1990).
//
func symbolicallyPerturbedSign(a, b, c, bCrossC r3.PreciseVector) Direction {
// This method requires that the points are sorted in lexicographically
// increasing order. This is because every possible Point has its own
// symbolic perturbation such that if A < B then the symbolic perturbation
// for A is much larger than the perturbation for B.
//
// Alternatively, we could sort the points in this method and keep track of
// the sign of the permutation, but it is more efficient to do this before
// converting the inputs to the multi-precision representation, and this
// also lets us re-use the result of the cross product B x C.
//
// Every input coordinate x[i] is assigned a symbolic perturbation dx[i].
// We then compute the sign of the determinant of the perturbed points,
// i.e.
// | a.X+da.X a.Y+da.Y a.Z+da.Z |
// | b.X+db.X b.Y+db.Y b.Z+db.Z |
// | c.X+dc.X c.Y+dc.Y c.Z+dc.Z |
//
// The perturbations are chosen such that
//
// da.Z > da.Y > da.X > db.Z > db.Y > db.X > dc.Z > dc.Y > dc.X
//
// where each perturbation is so much smaller than the previous one that we
// don't even need to consider it unless the coefficients of all previous
// perturbations are zero. In fact, it is so small that we don't need to
// consider it unless the coefficient of all products of the previous
// perturbations are zero. For example, we don't need to consider the
// coefficient of db.Y unless the coefficient of db.Z *da.X is zero.
//
// The follow code simply enumerates the coefficients of the perturbations
// (and products of perturbations) that appear in the determinant above, in
// order of decreasing perturbation magnitude. The first non-zero
// coefficient determines the sign of the result. The easiest way to
// enumerate the coefficients in the correct order is to pretend that each
// perturbation is some tiny value "eps" raised to a power of two:
//
// eps** 1 2 4 8 16 32 64 128 256
// da.Z da.Y da.X db.Z db.Y db.X dc.Z dc.Y dc.X
//
// Essentially we can then just count in binary and test the corresponding
// subset of perturbations at each step. So for example, we must test the
// coefficient of db.Z*da.X before db.Y because eps**12 > eps**16.
//
// Of course, not all products of these perturbations appear in the
// determinant above, since the determinant only contains the products of
// elements in distinct rows and columns. Thus we don't need to consider
// da.Z*da.Y, db.Y *da.Y, etc. Furthermore, sometimes different pairs of
// perturbations have the same coefficient in the determinant; for example,
// da.Y*db.X and db.Y*da.X have the same coefficient (c.Z). Therefore
// we only need to test this coefficient the first time we encounter it in
// the binary order above (which will be db.Y*da.X).
//
// The sequence of tests below also appears in Table 4-ii of the paper
// referenced above, if you just want to look it up, with the following
// translations: [a,b,c] -> [i,j,k] and [0,1,2] -> [1,2,3]. Also note that
// some of the signs are different because the opposite cross product is
// used (e.g., B x C rather than C x B).
detSign := bCrossC.Z.Sign() // da.Z
if detSign != 0 {
return Direction(detSign)
}
detSign = bCrossC.Y.Sign() // da.Y
if detSign != 0 {
return Direction(detSign)
}
detSign = bCrossC.X.Sign() // da.X
if detSign != 0 {
return Direction(detSign)
}
detSign = newBigFloat().Sub(newBigFloat().Mul(c.X, a.Y), newBigFloat().Mul(c.Y, a.X)).Sign() // db.Z
if detSign != 0 {
return Direction(detSign)
}
detSign = c.X.Sign() // db.Z * da.Y
if detSign != 0 {
return Direction(detSign)
}
detSign = -(c.Y.Sign()) // db.Z * da.X
if detSign != 0 {
return Direction(detSign)
}
detSign = newBigFloat().Sub(newBigFloat().Mul(c.Z, a.X), newBigFloat().Mul(c.X, a.Z)).Sign() // db.Y
if detSign != 0 {
return Direction(detSign)
}
detSign = c.Z.Sign() // db.Y * da.X
if detSign != 0 {
return Direction(detSign)
}
// The following test is listed in the paper, but it is redundant because
// the previous tests guarantee that C == (0, 0, 0).
// (c.Y*a.Z - c.Z*a.Y).Sign() // db.X
detSign = newBigFloat().Sub(newBigFloat().Mul(a.X, b.Y), newBigFloat().Mul(a.Y, b.X)).Sign() // dc.Z
if detSign != 0 {
return Direction(detSign)
}
detSign = -(b.X.Sign()) // dc.Z * da.Y
if detSign != 0 {
return Direction(detSign)
}
detSign = b.Y.Sign() // dc.Z * da.X
if detSign != 0 {
return Direction(detSign)
}
detSign = a.X.Sign() // dc.Z * db.Y
if detSign != 0 {
return Direction(detSign)
}
return CounterClockwise // dc.Z * db.Y * da.X
}
// CompareDistances returns -1, 0, or +1 according to whether AX < BX, A == B,
// or AX > BX respectively. Distances are measured with respect to the positions
// of X, A, and B as though they were reprojected to lie exactly on the surface of
// the unit sphere. Furthermore, this method uses symbolic perturbations to
// ensure that the result is non-zero whenever A != B, even when AX == BX
// exactly, or even when A and B project to the same point on the sphere.
// Such results are guaranteed to be self-consistent, i.e. if AB < BC and
// BC < AC, then AB < AC.
func CompareDistances(x, a, b Point) int {
// We start by comparing distances using dot products (i.e., cosine of the
// angle), because (1) this is the cheapest technique, and (2) it is valid
// over the entire range of possible angles. (We can only use the sin^2
// technique if both angles are less than 90 degrees or both angles are
// greater than 90 degrees.)
sign := triageCompareCosDistances(x, a, b)
if sign != 0 {
return sign
}
// Optimization for (a == b) to avoid falling back to exact arithmetic.
if a == b {
return 0
}
// It is much better numerically to compare distances using cos(angle) if
// the distances are near 90 degrees and sin^2(angle) if the distances are
// near 0 or 180 degrees. We only need to check one of the two angles when
// making this decision because the fact that the test above failed means
// that angles "a" and "b" are very close together.
cosAX := a.Dot(x.Vector)
if cosAX > 1/math.Sqrt2 {
// Angles < 45 degrees.
sign = triageCompareSin2Distances(x, a, b)
} else if cosAX < -1/math.Sqrt2 {
// Angles > 135 degrees. sin^2(angle) is decreasing in this range.
sign = -triageCompareSin2Distances(x, a, b)
}
// C++ adds an additional check here using 80-bit floats.
// This is skipped in Go because we only have 32 and 64 bit floats.
if sign != 0 {
return sign
}
sign = exactCompareDistances(r3.PreciseVectorFromVector(x.Vector), r3.PreciseVectorFromVector(a.Vector), r3.PreciseVectorFromVector(b.Vector))
if sign != 0 {
return sign
}
return symbolicCompareDistances(x, a, b)
}
// cosDistance returns cos(XY) where XY is the angle between X and Y, and the
// maximum error amount in the result. This requires X and Y be normalized.
func cosDistance(x, y Point) (cos, err float64) {
cos = x.Dot(y.Vector)
return cos, 9.5*dblError*math.Abs(cos) + 1.5*dblError
}
// sin2Distance returns sin**2(XY), where XY is the angle between X and Y,
// and the maximum error amount in the result. This requires X and Y be normalized.
func sin2Distance(x, y Point) (sin2, err float64) {
// The (x-y).Cross(x+y) trick eliminates almost all of error due to x
// and y being not quite unit length. This method is extremely accurate
// for small distances; the *relative* error in the result is O(dblError) for
// distances as small as dblError.
n := x.Sub(y.Vector).Cross(x.Add(y.Vector))
sin2 = 0.25 * n.Norm2()
err = ((21+4*math.Sqrt(3))*dblError*sin2 +
32*math.Sqrt(3)*dblError*dblError*math.Sqrt(sin2) +
768*dblError*dblError*dblError*dblError)
return sin2, err
}
// triageCompareCosDistances returns -1, 0, or +1 according to whether AX < BX,
// A == B, or AX > BX by comparing the distances between them using cosDistance.
func triageCompareCosDistances(x, a, b Point) int {
cosAX, cosAXerror := cosDistance(a, x)
cosBX, cosBXerror := cosDistance(b, x)
diff := cosAX - cosBX
err := cosAXerror + cosBXerror
if diff > err {
return -1
}
if diff < -err {
return 1
}
return 0
}
// triageCompareSin2Distances returns -1, 0, or +1 according to whether AX < BX,
// A == B, or AX > BX by comparing the distances between them using sin2Distance.
func triageCompareSin2Distances(x, a, b Point) int {
sin2AX, sin2AXerror := sin2Distance(a, x)
sin2BX, sin2BXerror := sin2Distance(b, x)
diff := sin2AX - sin2BX
err := sin2AXerror + sin2BXerror
if diff > err {
return 1
}
if diff < -err {
return -1
}
return 0
}
// exactCompareDistances returns -1, 0, or 1 after comparing using the values as
// PreciseVectors.
func exactCompareDistances(x, a, b r3.PreciseVector) int {
// This code produces the same result as though all points were reprojected
// to lie exactly on the surface of the unit sphere. It is based on testing
// whether x.Dot(a.Normalize()) < x.Dot(b.Normalize()), reformulated
// so that it can be evaluated using exact arithmetic.
cosAX := x.Dot(a)
cosBX := x.Dot(b)
// If the two values have different signs, we need to handle that case now
// before squaring them below.
aSign := cosAX.Sign()
bSign := cosBX.Sign()
if aSign != bSign {
// If cos(AX) > cos(BX), then AX < BX.
if aSign > bSign {
return -1
}
return 1
}
cosAX2 := newBigFloat().Mul(cosAX, cosAX)
cosBX2 := newBigFloat().Mul(cosBX, cosBX)
cmp := newBigFloat().Sub(cosBX2.Mul(cosBX2, a.Norm2()), cosAX2.Mul(cosAX2, b.Norm2()))
return aSign * cmp.Sign()
}
// symbolicCompareDistances returns -1, 0, or +1 given three points such that AX == BX
// (exactly) according to whether AX < BX, AX == BX, or AX > BX after symbolic
// perturbations are taken into account.
func symbolicCompareDistances(x, a, b Point) int {
// Our symbolic perturbation strategy is based on the following model.
// Similar to "simulation of simplicity", we assign a perturbation to every
// point such that if A < B, then the symbolic perturbation for A is much,
// much larger than the symbolic perturbation for B. We imagine that
// rather than projecting every point to lie exactly on the unit sphere,
// instead each point is positioned on its own tiny pedestal that raises it
// just off the surface of the unit sphere. This means that the distance AX
// is actually the true distance AX plus the (symbolic) heights of the
// pedestals for A and X. The pedestals are infinitesmally thin, so they do
// not affect distance measurements except at the two endpoints. If several
// points project to exactly the same point on the unit sphere, we imagine
// that they are placed on separate pedestals placed close together, where
// the distance between pedestals is much, much less than the height of any
// pedestal. (There are a finite number of Points, and therefore a finite
// number of pedestals, so this is possible.)
//
// If A < B, then A is on a higher pedestal than B, and therefore AX > BX.
switch a.Cmp(b.Vector) {
case -1:
return 1
case 1:
return -1
default:
return 0
}
}
var (
// ca45Degrees is a predefined ChordAngle representing (approximately) 45 degrees.
ca45Degrees = s1.ChordAngleFromSquaredLength(2 - math.Sqrt2)
)
// CompareDistance returns -1, 0, or +1 according to whether the distance XY is
// respectively less than, equal to, or greater than the provided chord angle. Distances are measured
// with respect to the positions of all points as though they are projected to lie
// exactly on the surface of the unit sphere.
func CompareDistance(x, y Point, r s1.ChordAngle) int {
// As with CompareDistances, we start by comparing dot products because
// the sin^2 method is only valid when the distance XY and the limit "r" are
// both less than 90 degrees.
sign := triageCompareCosDistance(x, y, float64(r))
if sign != 0 {
return sign
}
// Unlike with CompareDistances, it's not worth using the sin^2 method
// when the distance limit is near 180 degrees because the ChordAngle
// representation itself has has a rounding error of up to 2e-8 radians for
// distances near 180 degrees.
if r < ca45Degrees {
sign = triageCompareSin2Distance(x, y, float64(r))
if sign != 0 {
return sign
}
}
return exactCompareDistance(r3.PreciseVectorFromVector(x.Vector), r3.PreciseVectorFromVector(y.Vector), big.NewFloat(float64(r)).SetPrec(big.MaxPrec))
}
// triageCompareCosDistance returns -1, 0, or +1 according to whether the distance XY is
// less than, equal to, or greater than r2 respectively using cos distance.
func triageCompareCosDistance(x, y Point, r2 float64) int {
cosXY, cosXYError := cosDistance(x, y)
cosR := 1.0 - 0.5*r2
cosRError := 2.0 * dblError * cosR
diff := cosXY - cosR
err := cosXYError + cosRError
if diff > err {
return -1
}
if diff < -err {
return 1
}
return 0
}
// triageCompareSin2Distance returns -1, 0, or +1 according to whether the distance XY is
// less than, equal to, or greater than r2 respectively using sin^2 distance.
func triageCompareSin2Distance(x, y Point, r2 float64) int {
// Only valid for distance limits < 90 degrees.
sin2XY, sin2XYError := sin2Distance(x, y)
sin2R := r2 * (1.0 - 0.25*r2)
sin2RError := 3.0 * dblError * sin2R
diff := sin2XY - sin2R
err := sin2XYError + sin2RError
if diff > err {
return 1
}
if diff < -err {
return -1
}
return 0
}
var (
bigOne = big.NewFloat(1.0).SetPrec(big.MaxPrec)
bigHalf = big.NewFloat(0.5).SetPrec(big.MaxPrec)
)
// exactCompareDistance returns -1, 0, or +1 after comparing using PreciseVectors.
func exactCompareDistance(x, y r3.PreciseVector, r2 *big.Float) int {
// This code produces the same result as though all points were reprojected
// to lie exactly on the surface of the unit sphere. It is based on
// comparing the cosine of the angle XY (when both points are projected to
// lie exactly on the sphere) to the given threshold.
cosXY := x.Dot(y)
cosR := newBigFloat().Sub(bigOne, newBigFloat().Mul(bigHalf, r2))
// If the two values have different signs, we need to handle that case now
// before squaring them below.
xySign := cosXY.Sign()
rSign := cosR.Sign()
if xySign != rSign {
if xySign > rSign {
return -1
}
return 1 // If cos(XY) > cos(r), then XY < r.
}
cmp := newBigFloat().Sub(
newBigFloat().Mul(
newBigFloat().Mul(cosR, cosR), newBigFloat().Mul(x.Norm2(), y.Norm2())),
newBigFloat().Mul(cosXY, cosXY))
return xySign * cmp.Sign()
}
// TODO(roberts): Differences from C++
// CompareEdgeDistance
// CompareEdgeDirections
// EdgeCircumcenterSign
// GetVoronoiSiteExclusion
// GetClosestVertex
// TriageCompareLineSin2Distance
// TriageCompareLineCos2Distance
// TriageCompareLineDistance
// TriageCompareEdgeDistance
// ExactCompareLineDistance
// ExactCompareEdgeDistance
// TriageCompareEdgeDirections
// ExactCompareEdgeDirections
// ArePointsAntipodal
// ArePointsLinearlyDependent
// GetCircumcenter
// TriageEdgeCircumcenterSign
// ExactEdgeCircumcenterSign
// UnperturbedSign
// SymbolicEdgeCircumcenterSign
// ExactVoronoiSiteExclusion

View File

@ -1,241 +0,0 @@
// Copyright 2018 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"
"github.com/golang/geo/r2"
"github.com/golang/geo/s1"
)
// Projection defines an interface for different ways of mapping between s2 and r2 Points.
// It can also define the coordinate wrapping behavior along each axis.
type Projection interface {
// Project converts a point on the sphere to a projected 2D point.
Project(p Point) r2.Point
// Unproject converts a projected 2D point to a point on the sphere.
//
// If wrapping is defined for a given axis (see below), then this method
// should accept any real number for the corresponding coordinate.
Unproject(p r2.Point) Point
// FromLatLng is a convenience function equivalent to Project(LatLngToPoint(ll)),
// but the implementation is more efficient.
FromLatLng(ll LatLng) r2.Point
// ToLatLng is a convenience function equivalent to LatLngFromPoint(Unproject(p)),
// but the implementation is more efficient.
ToLatLng(p r2.Point) LatLng
// Interpolate returns the point obtained by interpolating the given
// fraction of the distance along the line from A to B.
// Fractions < 0 or > 1 result in extrapolation instead.
Interpolate(f float64, a, b r2.Point) r2.Point
// WrapDistance reports the coordinate wrapping distance along each axis.
// If this value is non-zero for a given axis, the coordinates are assumed
// to "wrap" with the given period. For example, if WrapDistance.Y == 360
// then (x, y) and (x, y + 360) should map to the same Point.
//
// This information is used to ensure that edges takes the shortest path
// between two given points. For example, if coordinates represent
// (latitude, longitude) pairs in degrees and WrapDistance().Y == 360,
// then the edge (5:179, 5:-179) would be interpreted as spanning 2 degrees
// of longitude rather than 358 degrees.
//
// If a given axis does not wrap, its WrapDistance should be set to zero.
WrapDistance() r2.Point
// WrapDestination that wraps the coordinates of B if necessary in order to
// obtain the shortest edge AB. For example, suppose that A = [170, 20],
// B = [-170, 20], and the projection wraps so that [x, y] == [x + 360, y].
// Then this function would return [190, 20] for point B (reducing the edge
// length in the "x" direction from 340 to 20).
WrapDestination(a, b r2.Point) r2.Point
// We do not support implementations of this interface outside this package.
privateInterface()
}
// PlateCarreeProjection defines the "plate carree" (square plate) projection,
// which converts points on the sphere to (longitude, latitude) pairs.
// Coordinates can be scaled so that they represent radians, degrees, etc, but
// the projection is always centered around (latitude=0, longitude=0).
//
// Note that (x, y) coordinates are backwards compared to the usual (latitude,
// longitude) ordering, in order to match the usual convention for graphs in
// which "x" is horizontal and "y" is vertical.
type PlateCarreeProjection struct {
xWrap float64
toRadians float64 // Multiplier to convert coordinates to radians.
fromRadians float64 // Multiplier to convert coordinates from radians.
}
// NewPlateCarreeProjection constructs a plate carree projection where the
// x-coordinates (lng) span [-xScale, xScale] and the y coordinates (lat)
// span [-xScale/2, xScale/2]. For example if xScale==180 then the x
// range is [-180, 180] and the y range is [-90, 90].
//
// By default coordinates are expressed in radians, i.e. the x range is
// [-Pi, Pi] and the y range is [-Pi/2, Pi/2].
func NewPlateCarreeProjection(xScale float64) Projection {
return &PlateCarreeProjection{
xWrap: 2 * xScale,
toRadians: math.Pi / xScale,
fromRadians: xScale / math.Pi,
}
}
// Project converts a point on the sphere to a projected 2D point.
func (p *PlateCarreeProjection) Project(pt Point) r2.Point {
return p.FromLatLng(LatLngFromPoint(pt))
}
// Unproject converts a projected 2D point to a point on the sphere.
func (p *PlateCarreeProjection) Unproject(pt r2.Point) Point {
return PointFromLatLng(p.ToLatLng(pt))
}
// FromLatLng returns the LatLng projected into an R2 Point.
func (p *PlateCarreeProjection) FromLatLng(ll LatLng) r2.Point {
return r2.Point{
X: p.fromRadians * ll.Lng.Radians(),
Y: p.fromRadians * ll.Lat.Radians(),
}
}
// ToLatLng returns the LatLng projected from the given R2 Point.
func (p *PlateCarreeProjection) ToLatLng(pt r2.Point) LatLng {
return LatLng{
Lat: s1.Angle(p.toRadians * pt.Y),
Lng: s1.Angle(p.toRadians * math.Remainder(pt.X, p.xWrap)),
}
}
// Interpolate returns the point obtained by interpolating the given
// fraction of the distance along the line from A to B.
func (p *PlateCarreeProjection) Interpolate(f float64, a, b r2.Point) r2.Point {
return a.Mul(1 - f).Add(b.Mul(f))
}
// WrapDistance reports the coordinate wrapping distance along each axis.
func (p *PlateCarreeProjection) WrapDistance() r2.Point {
return r2.Point{p.xWrap, 0}
}
// WrapDestination wraps the points if needed to get the shortest edge.
func (p *PlateCarreeProjection) WrapDestination(a, b r2.Point) r2.Point {
return wrapDestination(a, b, p.WrapDistance)
}
func (p *PlateCarreeProjection) privateInterface() {}
// MercatorProjection defines the spherical Mercator projection. Google Maps
// uses this projection together with WGS84 coordinates, in which case it is
// known as the "Web Mercator" projection (see Wikipedia). This class makes
// no assumptions regarding the coordinate system of its input points, but
// simply applies the spherical Mercator projection to them.
//
// The Mercator projection is finite in width (x) but infinite in height (y).
// "x" corresponds to longitude, and spans a finite range such as [-180, 180]
// (with coordinate wrapping), while "y" is a function of latitude and spans
// an infinite range. (As "y" coordinates get larger, points get closer to
// the north pole but never quite reach it.) The north and south poles have
// infinite "y" values. (Note that this will cause problems if you tessellate
// a Mercator edge where one endpoint is a pole. If you need to do this, clip
// the edge first so that the "y" coordinate is no more than about 5 * maxX.)
type MercatorProjection struct {
xWrap float64
toRadians float64 // Multiplier to convert coordinates to radians.
fromRadians float64 // Multiplier to convert coordinates from radians.
}
// NewMercatorProjection constructs a Mercator projection with the given maximum
// longitude axis value corresponding to a range of [-maxLng, maxLng].
// The horizontal and vertical axes are scaled equally.
func NewMercatorProjection(maxLng float64) Projection {
return &MercatorProjection{
xWrap: 2 * maxLng,
toRadians: math.Pi / maxLng,
fromRadians: maxLng / math.Pi,
}
}
// Project converts a point on the sphere to a projected 2D point.
func (p *MercatorProjection) Project(pt Point) r2.Point {
return p.FromLatLng(LatLngFromPoint(pt))
}
// Unproject converts a projected 2D point to a point on the sphere.
func (p *MercatorProjection) Unproject(pt r2.Point) Point {
return PointFromLatLng(p.ToLatLng(pt))
}
// FromLatLng returns the LatLng projected into an R2 Point.
func (p *MercatorProjection) FromLatLng(ll LatLng) r2.Point {
// This formula is more accurate near zero than the log(tan()) version.
// Note that latitudes of +/- 90 degrees yield "y" values of +/- infinity.
sinPhi := math.Sin(float64(ll.Lat))
y := 0.5 * math.Log((1+sinPhi)/(1-sinPhi))
return r2.Point{p.fromRadians * float64(ll.Lng), p.fromRadians * y}
}
// ToLatLng returns the LatLng projected from the given R2 Point.
func (p *MercatorProjection) ToLatLng(pt r2.Point) LatLng {
// This formula is more accurate near zero than the atan(exp()) version.
x := p.toRadians * math.Remainder(pt.X, p.xWrap)
k := math.Exp(2 * p.toRadians * pt.Y)
var y float64
if math.IsInf(k, 0) {
y = math.Pi / 2
} else {
y = math.Asin((k - 1) / (k + 1))
}
return LatLng{s1.Angle(y), s1.Angle(x)}
}
// Interpolate returns the point obtained by interpolating the given
// fraction of the distance along the line from A to B.
func (p *MercatorProjection) Interpolate(f float64, a, b r2.Point) r2.Point {
return a.Mul(1 - f).Add(b.Mul(f))
}
// WrapDistance reports the coordinate wrapping distance along each axis.
func (p *MercatorProjection) WrapDistance() r2.Point {
return r2.Point{p.xWrap, 0}
}
// WrapDestination wraps the points if needed to get the shortest edge.
func (p *MercatorProjection) WrapDestination(a, b r2.Point) r2.Point {
return wrapDestination(a, b, p.WrapDistance)
}
func (p *MercatorProjection) privateInterface() {}
func wrapDestination(a, b r2.Point, wrapDistance func() r2.Point) r2.Point {
wrap := wrapDistance()
x := b.X
y := b.Y
// The code below ensures that "b" is unmodified unless wrapping is required.
if wrap.X > 0 && math.Abs(x-a.X) > 0.5*wrap.X {
x = a.X + math.Remainder(x-a.X, wrap.X)
}
if wrap.Y > 0 && math.Abs(y-a.Y) > 0.5*wrap.Y {
y = a.Y + math.Remainder(y-a.Y, wrap.Y)
}
return r2.Point{x, y}
}

View File

@ -1,93 +0,0 @@
// Copyright 2020 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 "container/heap"
// A queryQueueEntry stores CellIDs and distance from a target. It is used by the
// different S2 Query types to efficiently build their internal priority queue
// in the optimized algorithm implementations.
type queryQueueEntry struct {
// A lower bound on the distance from the target to ID. This is the key
// of the priority queue.
distance distance
// The cell being queued.
id CellID
// If the CellID belongs to a ShapeIndex, this field stores the
// corresponding ShapeIndexCell. Otherwise ID is a proper ancestor of
// one or more ShapeIndexCells and this field stores is nil.
indexCell *ShapeIndexCell
}
// queryQueue is used by the optimized algorithm to maintain a priority queue of
// unprocessed CellIDs, sorted in increasing order of distance from the target.
type queryQueue struct {
queue queryPQ
}
// newQueryQueue returns a new initialized queryQueue.
func newQueryQueue() *queryQueue {
q := &queryQueue{
queue: make(queryPQ, 0),
}
heap.Init(&q.queue)
return q
}
// push adds the given entry to the top of this queue.
func (q *queryQueue) push(e *queryQueueEntry) {
heap.Push(&q.queue, e)
}
// pop returns the top element of this queue.
func (q *queryQueue) pop() *queryQueueEntry {
return heap.Pop(&q.queue).(*queryQueueEntry)
}
func (q *queryQueue) size() int {
return q.queue.Len()
}
func (q *queryQueue) reset() {
q.queue = q.queue[:0]
}
// queryPQ is a priority queue that implements the heap interface.
type queryPQ []*queryQueueEntry
func (q queryPQ) Len() int { return len(q) }
func (q queryPQ) Less(i, j int) bool {
return q[i].distance.less(q[j].distance)
}
// Swap swaps the two entries.
func (q queryPQ) Swap(i, j int) {
q[i], q[j] = q[j], q[i]
}
// Push adds the given entry to the queue.
func (q *queryPQ) Push(x interface{}) {
item := x.(*queryQueueEntry)
*q = append(*q, item)
}
// Pop returns the top element of the queue.
func (q *queryPQ) Pop() interface{} {
item := (*q)[len(*q)-1]
*q = (*q)[:len(*q)-1]
return item
}

View File

@ -1,196 +0,0 @@
// Copyright 2019 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"
"github.com/golang/geo/s1"
)
const maxQueryResults = math.MaxInt32
// queryOptions represents the set of all configurable parameters used by all of
// the Query types. Most of these fields have non-zero defaults, so initialization
// is handled within each Query type. All of the exported methods accept user
// supplied sets of options to set or adjust as necessary.
//
// Several of the defaults depend on the distance interface type being used
// (e.g. minDistance, maxDistance, etc.)
//
// If a user sets an option value that a given query type doesn't use, it is ignored.
type queryOptions struct {
// maxResults specifies that at most MaxResults edges should be returned.
// This must be at least 1.
//
// The default value is to return all results.
maxResults int
// distanceLimit specifies that only edges whose distance to the target is
// within this distance should be returned.
//
// Note that edges whose distance is exactly equal to this are
// not returned. In most cases this doesn't matter (since distances are
// not computed exactly in the first place), but if such edges are needed
// then you can retrieve them by specifying the distance as the next
// largest representable distance. i.e. distanceLimit.Successor().
//
// The default value is the infinity value, such that all results will be
// returned.
distanceLimit s1.ChordAngle
// maxError specifies that edges up to MaxError further away than the true
// closest edges may be substituted in the result set, as long as such
// edges satisfy all the remaining search criteria (such as DistanceLimit).
// This option only has an effect if MaxResults is also specified;
// otherwise all edges closer than MaxDistance will always be returned.
//
// Note that this does not affect how the distance between edges is
// computed; it simply gives the algorithm permission to stop the search
// early as soon as the best possible improvement drops below MaxError.
//
// This can be used to implement distance predicates efficiently. For
// example, to determine whether the minimum distance is less than D, set
// MaxResults == 1 and MaxDistance == MaxError == D. This causes
// the algorithm to terminate as soon as it finds any edge whose distance
// is less than D, rather than continuing to search for an edge that is
// even closer.
//
// The default value is zero.
maxError s1.ChordAngle
// includeInteriors specifies that polygon interiors should be included
// when measuring distances. In other words, polygons that contain the target
// should have a distance of zero. (For targets consisting of multiple connected
// components, the distance is zero if any component is contained.) This
// is indicated in the results by returning a (ShapeID, EdgeID) pair
// with EdgeID == -1, i.e. this value denotes the polygons's interior.
//
// Note that for efficiency, any polygon that intersects the target may or
// may not have an EdgeID == -1 result. Such results are optional
// because in that case the distance to the polygon is already zero.
//
// The default value is true.
includeInteriors bool
// specifies that distances should be computed by examining every edge
// rather than using the ShapeIndex.
//
// TODO(roberts): When optimized is implemented, update the default to false.
// The default value is true.
useBruteForce bool
// region specifies that results must intersect the given Region.
//
// Note that if you want to set the region to a disc around a target
// point, it is faster to use a PointTarget with distanceLimit set
// instead. You can also set a distance limit and also require that results
// lie within a given rectangle.
//
// The default is nil (no region limits).
region Region
}
// UseBruteForce sets or disables the use of brute force in a query.
func (q *queryOptions) UseBruteForce(x bool) *queryOptions {
q.useBruteForce = x
return q
}
// IncludeInteriors specifies whether polygon interiors should be
// included when measuring distances.
func (q *queryOptions) IncludeInteriors(x bool) *queryOptions {
q.includeInteriors = x
return q
}
// MaxError specifies that edges up to dist away than the true
// matching edges may be substituted in the result set, as long as such
// edges satisfy all the remaining search criteria (such as DistanceLimit).
// This option only has an effect if MaxResults is also specified;
// otherwise all edges closer than MaxDistance will always be returned.
func (q *queryOptions) MaxError(x s1.ChordAngle) *queryOptions {
q.maxError = x
return q
}
// MaxResults specifies that at most MaxResults edges should be returned.
// This must be at least 1.
func (q *queryOptions) MaxResults(x int) *queryOptions {
// TODO(roberts): What should be done if the value is <= 0?
q.maxResults = int(x)
return q
}
// DistanceLimit specifies that only edges whose distance to the target is
// within, this distance should be returned. Edges whose distance is equal
// are not returned.
//
// To include values that are equal, specify the limit with the next largest
// representable distance such as limit.Successor(), or set the option with
// Furthest/ClosestInclusiveDistanceLimit.
func (q *queryOptions) DistanceLimit(x s1.ChordAngle) *queryOptions {
q.distanceLimit = x
return q
}
// ClosestInclusiveDistanceLimit sets the distance limit such that results whose
// distance is exactly equal to the limit are also returned.
func (q *queryOptions) ClosestInclusiveDistanceLimit(limit s1.ChordAngle) *queryOptions {
q.distanceLimit = limit.Successor()
return q
}
// FurthestInclusiveDistanceLimit sets the distance limit such that results whose
// distance is exactly equal to the limit are also returned.
func (q *queryOptions) FurthestInclusiveDistanceLimit(limit s1.ChordAngle) *queryOptions {
q.distanceLimit = limit.Predecessor()
return q
}
// ClosestConservativeDistanceLimit sets the distance limit such that results
// also incorporates the error in distance calculations. This ensures that all
// edges whose true distance is less than or equal to limit will be returned
// (along with some edges whose true distance is slightly greater).
//
// Algorithms that need to do exact distance comparisons can use this
// option to find a set of candidate edges that can then be filtered
// further (e.g., using CompareDistance).
func (q *queryOptions) ClosestConservativeDistanceLimit(limit s1.ChordAngle) *queryOptions {
q.distanceLimit = limit.Expanded(minUpdateDistanceMaxError(limit))
return q
}
// FurthestConservativeDistanceLimit sets the distance limit such that results
// also incorporates the error in distance calculations. This ensures that all
// edges whose true distance is greater than or equal to limit will be returned
// (along with some edges whose true distance is slightly less).
func (q *queryOptions) FurthestConservativeDistanceLimit(limit s1.ChordAngle) *queryOptions {
q.distanceLimit = limit.Expanded(-minUpdateDistanceMaxError(limit))
return q
}
// newQueryOptions returns a set of options using the given distance type
// with the proper default values.
func newQueryOptions(d distance) *queryOptions {
return &queryOptions{
maxResults: maxQueryResults,
distanceLimit: d.infinity().chordAngle(),
maxError: 0,
includeInteriors: true,
useBruteForce: false,
region: nil,
}
}

View File

@ -1,710 +0,0 @@
// Copyright 2014 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 (
"fmt"
"io"
"math"
"github.com/golang/geo/r1"
"github.com/golang/geo/r3"
"github.com/golang/geo/s1"
)
// Rect represents a closed latitude-longitude rectangle.
type Rect struct {
Lat r1.Interval
Lng s1.Interval
}
var (
validRectLatRange = r1.Interval{-math.Pi / 2, math.Pi / 2}
validRectLngRange = s1.FullInterval()
)
// EmptyRect returns the empty rectangle.
func EmptyRect() Rect { return Rect{r1.EmptyInterval(), s1.EmptyInterval()} }
// FullRect returns the full rectangle.
func FullRect() Rect { return Rect{validRectLatRange, validRectLngRange} }
// RectFromLatLng constructs a rectangle containing a single point p.
func RectFromLatLng(p LatLng) Rect {
return Rect{
Lat: r1.Interval{p.Lat.Radians(), p.Lat.Radians()},
Lng: s1.Interval{p.Lng.Radians(), p.Lng.Radians()},
}
}
// RectFromCenterSize constructs a rectangle with the given size and center.
// center needs to be normalized, but size does not. The latitude
// interval of the result is clamped to [-90,90] degrees, and the longitude
// interval of the result is FullRect() if and only if the longitude size is
// 360 degrees or more.
//
// Examples of clamping (in degrees):
// center=(80,170), size=(40,60) -> lat=[60,90], lng=[140,-160]
// center=(10,40), size=(210,400) -> lat=[-90,90], lng=[-180,180]
// center=(-90,180), size=(20,50) -> lat=[-90,-80], lng=[155,-155]
func RectFromCenterSize(center, size LatLng) Rect {
half := LatLng{size.Lat / 2, size.Lng / 2}
return RectFromLatLng(center).expanded(half)
}
// IsValid returns true iff the rectangle is valid.
// This requires Lat ⊆ [-π/2,π/2] and Lng ⊆ [-π,π], and Lat = ∅ iff Lng = ∅
func (r Rect) IsValid() bool {
return math.Abs(r.Lat.Lo) <= math.Pi/2 &&
math.Abs(r.Lat.Hi) <= math.Pi/2 &&
r.Lng.IsValid() &&
r.Lat.IsEmpty() == r.Lng.IsEmpty()
}
// IsEmpty reports whether the rectangle is empty.
func (r Rect) IsEmpty() bool { return r.Lat.IsEmpty() }
// IsFull reports whether the rectangle is full.
func (r Rect) IsFull() bool { return r.Lat.Equal(validRectLatRange) && r.Lng.IsFull() }
// IsPoint reports whether the rectangle is a single point.
func (r Rect) IsPoint() bool { return r.Lat.Lo == r.Lat.Hi && r.Lng.Lo == r.Lng.Hi }
// Vertex returns the i-th vertex of the rectangle (i = 0,1,2,3) in CCW order
// (lower left, lower right, upper right, upper left).
func (r Rect) Vertex(i int) LatLng {
var lat, lng float64
switch i {
case 0:
lat = r.Lat.Lo
lng = r.Lng.Lo
case 1:
lat = r.Lat.Lo
lng = r.Lng.Hi
case 2:
lat = r.Lat.Hi
lng = r.Lng.Hi
case 3:
lat = r.Lat.Hi
lng = r.Lng.Lo
}
return LatLng{s1.Angle(lat) * s1.Radian, s1.Angle(lng) * s1.Radian}
}
// Lo returns one corner of the rectangle.
func (r Rect) Lo() LatLng {
return LatLng{s1.Angle(r.Lat.Lo) * s1.Radian, s1.Angle(r.Lng.Lo) * s1.Radian}
}
// Hi returns the other corner of the rectangle.
func (r Rect) Hi() LatLng {
return LatLng{s1.Angle(r.Lat.Hi) * s1.Radian, s1.Angle(r.Lng.Hi) * s1.Radian}
}
// Center returns the center of the rectangle.
func (r Rect) Center() LatLng {
return LatLng{s1.Angle(r.Lat.Center()) * s1.Radian, s1.Angle(r.Lng.Center()) * s1.Radian}
}
// Size returns the size of the Rect.
func (r Rect) Size() LatLng {
return LatLng{s1.Angle(r.Lat.Length()) * s1.Radian, s1.Angle(r.Lng.Length()) * s1.Radian}
}
// Area returns the surface area of the Rect.
func (r Rect) Area() float64 {
if r.IsEmpty() {
return 0
}
capDiff := math.Abs(math.Sin(r.Lat.Hi) - math.Sin(r.Lat.Lo))
return r.Lng.Length() * capDiff
}
// AddPoint increases the size of the rectangle to include the given point.
func (r Rect) AddPoint(ll LatLng) Rect {
if !ll.IsValid() {
return r
}
return Rect{
Lat: r.Lat.AddPoint(ll.Lat.Radians()),
Lng: r.Lng.AddPoint(ll.Lng.Radians()),
}
}
// expanded returns a rectangle that has been expanded by margin.Lat on each side
// in the latitude direction, and by margin.Lng on each side in the longitude
// direction. If either margin is negative, then it shrinks the rectangle on
// the corresponding sides instead. The resulting rectangle may be empty.
//
// The latitude-longitude space has the topology of a cylinder. Longitudes
// "wrap around" at +/-180 degrees, while latitudes are clamped to range [-90, 90].
// This means that any expansion (positive or negative) of the full longitude range
// remains full (since the "rectangle" is actually a continuous band around the
// cylinder), while expansion of the full latitude range remains full only if the
// margin is positive.
//
// If either the latitude or longitude interval becomes empty after
// expansion by a negative margin, the result is empty.
//
// Note that if an expanded rectangle contains a pole, it may not contain
// all possible lat/lng representations of that pole, e.g., both points [π/2,0]
// and [π/2,1] represent the same pole, but they might not be contained by the
// same Rect.
//
// If you are trying to grow a rectangle by a certain distance on the
// sphere (e.g. 5km), refer to the ExpandedByDistance() C++ method implementation
// instead.
func (r Rect) expanded(margin LatLng) Rect {
lat := r.Lat.Expanded(margin.Lat.Radians())
lng := r.Lng.Expanded(margin.Lng.Radians())
if lat.IsEmpty() || lng.IsEmpty() {
return EmptyRect()
}
return Rect{
Lat: lat.Intersection(validRectLatRange),
Lng: lng,
}
}
func (r Rect) String() string { return fmt.Sprintf("[Lo%v, Hi%v]", r.Lo(), r.Hi()) }
// PolarClosure returns the rectangle unmodified if it does not include either pole.
// If it includes either pole, PolarClosure returns an expansion of the rectangle along
// the longitudinal range to include all possible representations of the contained poles.
func (r Rect) PolarClosure() Rect {
if r.Lat.Lo == -math.Pi/2 || r.Lat.Hi == math.Pi/2 {
return Rect{r.Lat, s1.FullInterval()}
}
return r
}
// Union returns the smallest Rect containing the union of this rectangle and the given rectangle.
func (r Rect) Union(other Rect) Rect {
return Rect{
Lat: r.Lat.Union(other.Lat),
Lng: r.Lng.Union(other.Lng),
}
}
// Intersection returns the smallest rectangle containing the intersection of
// this rectangle and the given rectangle. Note that the region of intersection
// may consist of two disjoint rectangles, in which case a single rectangle
// spanning both of them is returned.
func (r Rect) Intersection(other Rect) Rect {
lat := r.Lat.Intersection(other.Lat)
lng := r.Lng.Intersection(other.Lng)
if lat.IsEmpty() || lng.IsEmpty() {
return EmptyRect()
}
return Rect{lat, lng}
}
// Intersects reports whether this rectangle and the other have any points in common.
func (r Rect) Intersects(other Rect) bool {
return r.Lat.Intersects(other.Lat) && r.Lng.Intersects(other.Lng)
}
// CapBound returns a cap that contains Rect.
func (r Rect) CapBound() Cap {
// We consider two possible bounding caps, one whose axis passes
// through the center of the lat-long rectangle and one whose axis
// is the north or south pole. We return the smaller of the two caps.
if r.IsEmpty() {
return EmptyCap()
}
var poleZ, poleAngle float64
if r.Lat.Hi+r.Lat.Lo < 0 {
// South pole axis yields smaller cap.
poleZ = -1
poleAngle = math.Pi/2 + r.Lat.Hi
} else {
poleZ = 1
poleAngle = math.Pi/2 - r.Lat.Lo
}
poleCap := CapFromCenterAngle(Point{r3.Vector{0, 0, poleZ}}, s1.Angle(poleAngle)*s1.Radian)
// For bounding rectangles that span 180 degrees or less in longitude, the
// maximum cap size is achieved at one of the rectangle vertices. For
// rectangles that are larger than 180 degrees, we punt and always return a
// bounding cap centered at one of the two poles.
if math.Remainder(r.Lng.Hi-r.Lng.Lo, 2*math.Pi) >= 0 && r.Lng.Hi-r.Lng.Lo < 2*math.Pi {
midCap := CapFromPoint(PointFromLatLng(r.Center())).AddPoint(PointFromLatLng(r.Lo())).AddPoint(PointFromLatLng(r.Hi()))
if midCap.Height() < poleCap.Height() {
return midCap
}
}
return poleCap
}
// RectBound returns itself.
func (r Rect) RectBound() Rect {
return r
}
// Contains reports whether this Rect contains the other Rect.
func (r Rect) Contains(other Rect) bool {
return r.Lat.ContainsInterval(other.Lat) && r.Lng.ContainsInterval(other.Lng)
}
// ContainsCell reports whether the given Cell is contained by this Rect.
func (r Rect) ContainsCell(c Cell) bool {
// A latitude-longitude rectangle contains a cell if and only if it contains
// the cell's bounding rectangle. This test is exact from a mathematical
// point of view, assuming that the bounds returned by Cell.RectBound()
// are tight. However, note that there can be a loss of precision when
// converting between representations -- for example, if an s2.Cell is
// converted to a polygon, the polygon's bounding rectangle may not contain
// the cell's bounding rectangle. This has some slightly unexpected side
// effects; for instance, if one creates an s2.Polygon from an s2.Cell, the
// polygon will contain the cell, but the polygon's bounding box will not.
return r.Contains(c.RectBound())
}
// ContainsLatLng reports whether the given LatLng is within the Rect.
func (r Rect) ContainsLatLng(ll LatLng) bool {
if !ll.IsValid() {
return false
}
return r.Lat.Contains(ll.Lat.Radians()) && r.Lng.Contains(ll.Lng.Radians())
}
// ContainsPoint reports whether the given Point is within the Rect.
func (r Rect) ContainsPoint(p Point) bool {
return r.ContainsLatLng(LatLngFromPoint(p))
}
// CellUnionBound computes a covering of the Rect.
func (r Rect) CellUnionBound() []CellID {
return r.CapBound().CellUnionBound()
}
// intersectsLatEdge reports whether the edge AB intersects the given edge of constant
// latitude. Requires the points to have unit length.
func intersectsLatEdge(a, b Point, lat s1.Angle, lng s1.Interval) bool {
// Unfortunately, lines of constant latitude are curves on
// the sphere. They can intersect a straight edge in 0, 1, or 2 points.
// First, compute the normal to the plane AB that points vaguely north.
z := Point{a.PointCross(b).Normalize()}
if z.Z < 0 {
z = Point{z.Mul(-1)}
}
// Extend this to an orthonormal frame (x,y,z) where x is the direction
// where the great circle through AB achieves its maximium latitude.
y := Point{z.PointCross(PointFromCoords(0, 0, 1)).Normalize()}
x := y.Cross(z.Vector)
// Compute the angle "theta" from the x-axis (in the x-y plane defined
// above) where the great circle intersects the given line of latitude.
sinLat := math.Sin(float64(lat))
if math.Abs(sinLat) >= x.Z {
// The great circle does not reach the given latitude.
return false
}
cosTheta := sinLat / x.Z
sinTheta := math.Sqrt(1 - cosTheta*cosTheta)
theta := math.Atan2(sinTheta, cosTheta)
// The candidate intersection points are located +/- theta in the x-y
// plane. For an intersection to be valid, we need to check that the
// intersection point is contained in the interior of the edge AB and
// also that it is contained within the given longitude interval "lng".
// Compute the range of theta values spanned by the edge AB.
abTheta := s1.IntervalFromPointPair(
math.Atan2(a.Dot(y.Vector), a.Dot(x)),
math.Atan2(b.Dot(y.Vector), b.Dot(x)))
if abTheta.Contains(theta) {
// Check if the intersection point is also in the given lng interval.
isect := x.Mul(cosTheta).Add(y.Mul(sinTheta))
if lng.Contains(math.Atan2(isect.Y, isect.X)) {
return true
}
}
if abTheta.Contains(-theta) {
// Check if the other intersection point is also in the given lng interval.
isect := x.Mul(cosTheta).Sub(y.Mul(sinTheta))
if lng.Contains(math.Atan2(isect.Y, isect.X)) {
return true
}
}
return false
}
// intersectsLngEdge reports whether the edge AB intersects the given edge of constant
// longitude. Requires the points to have unit length.
func intersectsLngEdge(a, b Point, lat r1.Interval, lng s1.Angle) bool {
// The nice thing about edges of constant longitude is that
// they are straight lines on the sphere (geodesics).
return CrossingSign(a, b, PointFromLatLng(LatLng{s1.Angle(lat.Lo), lng}),
PointFromLatLng(LatLng{s1.Angle(lat.Hi), lng})) == Cross
}
// IntersectsCell reports whether this rectangle intersects the given cell. This is an
// exact test and may be fairly expensive.
func (r Rect) IntersectsCell(c Cell) bool {
// First we eliminate the cases where one region completely contains the
// other. Once these are disposed of, then the regions will intersect
// if and only if their boundaries intersect.
if r.IsEmpty() {
return false
}
if r.ContainsPoint(Point{c.id.rawPoint()}) {
return true
}
if c.ContainsPoint(PointFromLatLng(r.Center())) {
return true
}
// Quick rejection test (not required for correctness).
if !r.Intersects(c.RectBound()) {
return false
}
// Precompute the cell vertices as points and latitude-longitudes. We also
// check whether the Cell contains any corner of the rectangle, or
// vice-versa, since the edge-crossing tests only check the edge interiors.
vertices := [4]Point{}
latlngs := [4]LatLng{}
for i := range vertices {
vertices[i] = c.Vertex(i)
latlngs[i] = LatLngFromPoint(vertices[i])
if r.ContainsLatLng(latlngs[i]) {
return true
}
if c.ContainsPoint(PointFromLatLng(r.Vertex(i))) {
return true
}
}
// Now check whether the boundaries intersect. Unfortunately, a
// latitude-longitude rectangle does not have straight edges: two edges
// are curved, and at least one of them is concave.
for i := range vertices {
edgeLng := s1.IntervalFromEndpoints(latlngs[i].Lng.Radians(), latlngs[(i+1)&3].Lng.Radians())
if !r.Lng.Intersects(edgeLng) {
continue
}
a := vertices[i]
b := vertices[(i+1)&3]
if edgeLng.Contains(r.Lng.Lo) && intersectsLngEdge(a, b, r.Lat, s1.Angle(r.Lng.Lo)) {
return true
}
if edgeLng.Contains(r.Lng.Hi) && intersectsLngEdge(a, b, r.Lat, s1.Angle(r.Lng.Hi)) {
return true
}
if intersectsLatEdge(a, b, s1.Angle(r.Lat.Lo), r.Lng) {
return true
}
if intersectsLatEdge(a, b, s1.Angle(r.Lat.Hi), r.Lng) {
return true
}
}
return false
}
// Encode encodes the Rect.
func (r Rect) Encode(w io.Writer) error {
e := &encoder{w: w}
r.encode(e)
return e.err
}
func (r Rect) encode(e *encoder) {
e.writeInt8(encodingVersion)
e.writeFloat64(r.Lat.Lo)
e.writeFloat64(r.Lat.Hi)
e.writeFloat64(r.Lng.Lo)
e.writeFloat64(r.Lng.Hi)
}
// Decode decodes a rectangle.
func (r *Rect) Decode(rd io.Reader) error {
d := &decoder{r: asByteReader(rd)}
r.decode(d)
return d.err
}
func (r *Rect) decode(d *decoder) {
if version := d.readUint8(); int8(version) != encodingVersion && d.err == nil {
d.err = fmt.Errorf("can't decode version %d; my version: %d", version, encodingVersion)
return
}
r.Lat.Lo = d.readFloat64()
r.Lat.Hi = d.readFloat64()
r.Lng.Lo = d.readFloat64()
r.Lng.Hi = d.readFloat64()
return
}
// DistanceToLatLng returns the minimum distance (measured along the surface of the sphere)
// from a given point to the rectangle (both its boundary and its interior).
// If r is empty, the result is meaningless.
// The latlng must be valid.
func (r Rect) DistanceToLatLng(ll LatLng) s1.Angle {
if r.Lng.Contains(float64(ll.Lng)) {
return maxAngle(0, ll.Lat-s1.Angle(r.Lat.Hi), s1.Angle(r.Lat.Lo)-ll.Lat)
}
i := s1.IntervalFromEndpoints(r.Lng.Hi, r.Lng.ComplementCenter())
rectLng := r.Lng.Lo
if i.Contains(float64(ll.Lng)) {
rectLng = r.Lng.Hi
}
lo := LatLng{s1.Angle(r.Lat.Lo) * s1.Radian, s1.Angle(rectLng) * s1.Radian}
hi := LatLng{s1.Angle(r.Lat.Hi) * s1.Radian, s1.Angle(rectLng) * s1.Radian}
return DistanceFromSegment(PointFromLatLng(ll), PointFromLatLng(lo), PointFromLatLng(hi))
}
// DirectedHausdorffDistance returns the directed Hausdorff distance (measured along the
// surface of the sphere) to the given Rect. The directed Hausdorff
// distance from rectangle A to rectangle B is given by
// h(A, B) = max_{p in A} min_{q in B} d(p, q).
func (r Rect) DirectedHausdorffDistance(other Rect) s1.Angle {
if r.IsEmpty() {
return 0 * s1.Radian
}
if other.IsEmpty() {
return math.Pi * s1.Radian
}
lng := r.Lng.DirectedHausdorffDistance(other.Lng)
return directedHausdorffDistance(lng, r.Lat, other.Lat)
}
// HausdorffDistance returns the undirected Hausdorff distance (measured along the
// surface of the sphere) to the given Rect.
// The Hausdorff distance between rectangle A and rectangle B is given by
// H(A, B) = max{h(A, B), h(B, A)}.
func (r Rect) HausdorffDistance(other Rect) s1.Angle {
return maxAngle(r.DirectedHausdorffDistance(other),
other.DirectedHausdorffDistance(r))
}
// ApproxEqual reports whether the latitude and longitude intervals of the two rectangles
// are the same up to a small tolerance.
func (r Rect) ApproxEqual(other Rect) bool {
return r.Lat.ApproxEqual(other.Lat) && r.Lng.ApproxEqual(other.Lng)
}
// directedHausdorffDistance returns the directed Hausdorff distance
// from one longitudinal edge spanning latitude range 'a' to the other
// longitudinal edge spanning latitude range 'b', with their longitudinal
// difference given by 'lngDiff'.
func directedHausdorffDistance(lngDiff s1.Angle, a, b r1.Interval) s1.Angle {
// By symmetry, we can assume a's longitude is 0 and b's longitude is
// lngDiff. Call b's two endpoints bLo and bHi. Let H be the hemisphere
// containing a and delimited by the longitude line of b. The Voronoi diagram
// of b on H has three edges (portions of great circles) all orthogonal to b
// and meeting at bLo cross bHi.
// E1: (bLo, bLo cross bHi)
// E2: (bHi, bLo cross bHi)
// E3: (-bMid, bLo cross bHi), where bMid is the midpoint of b
//
// They subdivide H into three Voronoi regions. Depending on how longitude 0
// (which contains edge a) intersects these regions, we distinguish two cases:
// Case 1: it intersects three regions. This occurs when lngDiff <= π/2.
// Case 2: it intersects only two regions. This occurs when lngDiff > π/2.
//
// In the first case, the directed Hausdorff distance to edge b can only be
// realized by the following points on a:
// A1: two endpoints of a.
// A2: intersection of a with the equator, if b also intersects the equator.
//
// In the second case, the directed Hausdorff distance to edge b can only be
// realized by the following points on a:
// B1: two endpoints of a.
// B2: intersection of a with E3
// B3: farthest point from bLo to the interior of D, and farthest point from
// bHi to the interior of U, if any, where D (resp. U) is the portion
// of edge a below (resp. above) the intersection point from B2.
if lngDiff < 0 {
panic("impossible: negative lngDiff")
}
if lngDiff > math.Pi {
panic("impossible: lngDiff > Pi")
}
if lngDiff == 0 {
return s1.Angle(a.DirectedHausdorffDistance(b))
}
// Assumed longitude of b.
bLng := lngDiff
// Two endpoints of b.
bLo := PointFromLatLng(LatLng{s1.Angle(b.Lo), bLng})
bHi := PointFromLatLng(LatLng{s1.Angle(b.Hi), bLng})
// Cases A1 and B1.
aLo := PointFromLatLng(LatLng{s1.Angle(a.Lo), 0})
aHi := PointFromLatLng(LatLng{s1.Angle(a.Hi), 0})
maxDistance := maxAngle(
DistanceFromSegment(aLo, bLo, bHi),
DistanceFromSegment(aHi, bLo, bHi))
if lngDiff <= math.Pi/2 {
// Case A2.
if a.Contains(0) && b.Contains(0) {
maxDistance = maxAngle(maxDistance, lngDiff)
}
return maxDistance
}
// Case B2.
p := bisectorIntersection(b, bLng)
pLat := LatLngFromPoint(p).Lat
if a.Contains(float64(pLat)) {
maxDistance = maxAngle(maxDistance, p.Angle(bLo.Vector))
}
// Case B3.
if pLat > s1.Angle(a.Lo) {
intDist, ok := interiorMaxDistance(r1.Interval{a.Lo, math.Min(float64(pLat), a.Hi)}, bLo)
if ok {
maxDistance = maxAngle(maxDistance, intDist)
}
}
if pLat < s1.Angle(a.Hi) {
intDist, ok := interiorMaxDistance(r1.Interval{math.Max(float64(pLat), a.Lo), a.Hi}, bHi)
if ok {
maxDistance = maxAngle(maxDistance, intDist)
}
}
return maxDistance
}
// interiorMaxDistance returns the max distance from a point b to the segment spanning latitude range
// aLat on longitude 0 if the max occurs in the interior of aLat. Otherwise, returns (0, false).
func interiorMaxDistance(aLat r1.Interval, b Point) (a s1.Angle, ok bool) {
// Longitude 0 is in the y=0 plane. b.X >= 0 implies that the maximum
// does not occur in the interior of aLat.
if aLat.IsEmpty() || b.X >= 0 {
return 0, false
}
// Project b to the y=0 plane. The antipodal of the normalized projection is
// the point at which the maxium distance from b occurs, if it is contained
// in aLat.
intersectionPoint := PointFromCoords(-b.X, 0, -b.Z)
if !aLat.InteriorContains(float64(LatLngFromPoint(intersectionPoint).Lat)) {
return 0, false
}
return b.Angle(intersectionPoint.Vector), true
}
// bisectorIntersection return the intersection of longitude 0 with the bisector of an edge
// on longitude 'lng' and spanning latitude range 'lat'.
func bisectorIntersection(lat r1.Interval, lng s1.Angle) Point {
lng = s1.Angle(math.Abs(float64(lng)))
latCenter := s1.Angle(lat.Center())
// A vector orthogonal to the bisector of the given longitudinal edge.
orthoBisector := LatLng{latCenter - math.Pi/2, lng}
if latCenter < 0 {
orthoBisector = LatLng{-latCenter - math.Pi/2, lng - math.Pi}
}
// A vector orthogonal to longitude 0.
orthoLng := Point{r3.Vector{0, -1, 0}}
return orthoLng.PointCross(PointFromLatLng(orthoBisector))
}
// Centroid returns the true centroid of the given Rect multiplied by its
// surface area. The result is not unit length, so you may want to normalize it.
// Note that in general the centroid is *not* at the center of the rectangle, and
// in fact it may not even be contained by the rectangle. (It is the "center of
// mass" of the rectangle viewed as subset of the unit sphere, i.e. it is the
// point in space about which this curved shape would rotate.)
//
// The reason for multiplying the result by the rectangle area is to make it
// easier to compute the centroid of more complicated shapes. The centroid
// of a union of disjoint regions can be computed simply by adding their
// Centroid results.
func (r Rect) Centroid() Point {
// When a sphere is divided into slices of constant thickness by a set
// of parallel planes, all slices have the same surface area. This
// implies that the z-component of the centroid is simply the midpoint
// of the z-interval spanned by the Rect.
//
// Similarly, it is easy to see that the (x,y) of the centroid lies in
// the plane through the midpoint of the rectangle's longitude interval.
// We only need to determine the distance "d" of this point from the
// z-axis.
//
// Let's restrict our attention to a particular z-value. In this
// z-plane, the Rect is a circular arc. The centroid of this arc
// lies on a radial line through the midpoint of the arc, and at a
// distance from the z-axis of
//
// r * (sin(alpha) / alpha)
//
// where r = sqrt(1-z^2) is the radius of the arc, and "alpha" is half
// of the arc length (i.e., the arc covers longitudes [-alpha, alpha]).
//
// To find the centroid distance from the z-axis for the entire
// rectangle, we just need to integrate over the z-interval. This gives
//
// d = Integrate[sqrt(1-z^2)*sin(alpha)/alpha, z1..z2] / (z2 - z1)
//
// where [z1, z2] is the range of z-values covered by the rectangle.
// This simplifies to
//
// d = sin(alpha)/(2*alpha*(z2-z1))*(z2*r2 - z1*r1 + theta2 - theta1)
//
// where [theta1, theta2] is the latitude interval, z1=sin(theta1),
// z2=sin(theta2), r1=cos(theta1), and r2=cos(theta2).
//
// Finally, we want to return not the centroid itself, but the centroid
// scaled by the area of the rectangle. The area of the rectangle is
//
// A = 2 * alpha * (z2 - z1)
//
// which fortunately appears in the denominator of "d".
if r.IsEmpty() {
return Point{}
}
z1 := math.Sin(r.Lat.Lo)
z2 := math.Sin(r.Lat.Hi)
r1 := math.Cos(r.Lat.Lo)
r2 := math.Cos(r.Lat.Hi)
alpha := 0.5 * r.Lng.Length()
r0 := math.Sin(alpha) * (r2*z2 - r1*z1 + r.Lat.Length())
lng := r.Lng.Center()
z := alpha * (z2 + z1) * (z2 - z1) // scaled by the area
return Point{r3.Vector{r0 * math.Cos(lng), r0 * math.Sin(lng), z}}
}
// BUG: The major differences from the C++ version are:
// - Get*Distance, Vertex, InteriorContains(LatLng|Rect|Point)

View File

@ -1,352 +0,0 @@
// 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
import (
"math"
"github.com/golang/geo/r1"
"github.com/golang/geo/r3"
"github.com/golang/geo/s1"
)
// RectBounder is used to compute a bounding rectangle that contains all edges
// defined by a vertex chain (v0, v1, v2, ...). All vertices must be unit length.
// Note that the bounding rectangle of an edge can be larger than the bounding
// rectangle of its endpoints, e.g. consider an edge that passes through the North Pole.
//
// The bounds are calculated conservatively to account for numerical errors
// when points are converted to LatLngs. More precisely, this function
// guarantees the following:
// Let L be a closed edge chain (Loop) such that the interior of the loop does
// not contain either pole. Now if P is any point such that L.ContainsPoint(P),
// then RectBound(L).ContainsPoint(LatLngFromPoint(P)).
type RectBounder struct {
// The previous vertex in the chain.
a Point
// The previous vertex latitude longitude.
aLL LatLng
bound Rect
}
// NewRectBounder returns a new instance of a RectBounder.
func NewRectBounder() *RectBounder {
return &RectBounder{
bound: EmptyRect(),
}
}
// maxErrorForTests returns the maximum error in RectBound provided that the
// result does not include either pole. It is only used for testing purposes
func (r *RectBounder) maxErrorForTests() LatLng {
// The maximum error in the latitude calculation is
// 3.84 * dblEpsilon for the PointCross calculation
// 0.96 * dblEpsilon for the Latitude calculation
// 5 * dblEpsilon added by AddPoint/RectBound to compensate for error
// -----------------
// 9.80 * dblEpsilon maximum error in result
//
// The maximum error in the longitude calculation is dblEpsilon. RectBound
// does not do any expansion because this isn't necessary in order to
// bound the *rounded* longitudes of contained points.
return LatLng{10 * dblEpsilon * s1.Radian, 1 * dblEpsilon * s1.Radian}
}
// AddPoint adds the given point to the chain. The Point must be unit length.
func (r *RectBounder) AddPoint(b Point) {
bLL := LatLngFromPoint(b)
if r.bound.IsEmpty() {
r.a = b
r.aLL = bLL
r.bound = r.bound.AddPoint(bLL)
return
}
// First compute the cross product N = A x B robustly. This is the normal
// to the great circle through A and B. We don't use RobustSign
// since that method returns an arbitrary vector orthogonal to A if the two
// vectors are proportional, and we want the zero vector in that case.
n := r.a.Sub(b.Vector).Cross(r.a.Add(b.Vector)) // N = 2 * (A x B)
// The relative error in N gets large as its norm gets very small (i.e.,
// when the two points are nearly identical or antipodal). We handle this
// by choosing a maximum allowable error, and if the error is greater than
// this we fall back to a different technique. Since it turns out that
// the other sources of error in converting the normal to a maximum
// latitude add up to at most 1.16 * dblEpsilon, and it is desirable to
// have the total error be a multiple of dblEpsilon, we have chosen to
// limit the maximum error in the normal to be 3.84 * dblEpsilon.
// It is possible to show that the error is less than this when
//
// n.Norm() >= 8 * sqrt(3) / (3.84 - 0.5 - sqrt(3)) * dblEpsilon
// = 1.91346e-15 (about 8.618 * dblEpsilon)
nNorm := n.Norm()
if nNorm < 1.91346e-15 {
// A and B are either nearly identical or nearly antipodal (to within
// 4.309 * dblEpsilon, or about 6 nanometers on the earth's surface).
if r.a.Dot(b.Vector) < 0 {
// The two points are nearly antipodal. The easiest solution is to
// assume that the edge between A and B could go in any direction
// around the sphere.
r.bound = FullRect()
} else {
// The two points are nearly identical (to within 4.309 * dblEpsilon).
// In this case we can just use the bounding rectangle of the points,
// since after the expansion done by GetBound this Rect is
// guaranteed to include the (lat,lng) values of all points along AB.
r.bound = r.bound.Union(RectFromLatLng(r.aLL).AddPoint(bLL))
}
r.a = b
r.aLL = bLL
return
}
// Compute the longitude range spanned by AB.
lngAB := s1.EmptyInterval().AddPoint(r.aLL.Lng.Radians()).AddPoint(bLL.Lng.Radians())
if lngAB.Length() >= math.Pi-2*dblEpsilon {
// The points lie on nearly opposite lines of longitude to within the
// maximum error of the calculation. The easiest solution is to assume
// that AB could go on either side of the pole.
lngAB = s1.FullInterval()
}
// Next we compute the latitude range spanned by the edge AB. We start
// with the range spanning the two endpoints of the edge:
latAB := r1.IntervalFromPoint(r.aLL.Lat.Radians()).AddPoint(bLL.Lat.Radians())
// This is the desired range unless the edge AB crosses the plane
// through N and the Z-axis (which is where the great circle through A
// and B attains its minimum and maximum latitudes). To test whether AB
// crosses this plane, we compute a vector M perpendicular to this
// plane and then project A and B onto it.
m := n.Cross(r3.Vector{0, 0, 1})
mA := m.Dot(r.a.Vector)
mB := m.Dot(b.Vector)
// We want to test the signs of "mA" and "mB", so we need to bound
// the error in these calculations. It is possible to show that the
// total error is bounded by
//
// (1 + sqrt(3)) * dblEpsilon * nNorm + 8 * sqrt(3) * (dblEpsilon**2)
// = 6.06638e-16 * nNorm + 6.83174e-31
mError := 6.06638e-16*nNorm + 6.83174e-31
if mA*mB < 0 || math.Abs(mA) <= mError || math.Abs(mB) <= mError {
// Minimum/maximum latitude *may* occur in the edge interior.
//
// The maximum latitude is 90 degrees minus the latitude of N. We
// compute this directly using atan2 in order to get maximum accuracy
// near the poles.
//
// Our goal is compute a bound that contains the computed latitudes of
// all S2Points P that pass the point-in-polygon containment test.
// There are three sources of error we need to consider:
// - the directional error in N (at most 3.84 * dblEpsilon)
// - converting N to a maximum latitude
// - computing the latitude of the test point P
// The latter two sources of error are at most 0.955 * dblEpsilon
// individually, but it is possible to show by a more complex analysis
// that together they can add up to at most 1.16 * dblEpsilon, for a
// total error of 5 * dblEpsilon.
//
// We add 3 * dblEpsilon to the bound here, and GetBound() will pad
// the bound by another 2 * dblEpsilon.
maxLat := math.Min(
math.Atan2(math.Sqrt(n.X*n.X+n.Y*n.Y), math.Abs(n.Z))+3*dblEpsilon,
math.Pi/2)
// In order to get tight bounds when the two points are close together,
// we also bound the min/max latitude relative to the latitudes of the
// endpoints A and B. First we compute the distance between A and B,
// and then we compute the maximum change in latitude between any two
// points along the great circle that are separated by this distance.
// This gives us a latitude change "budget". Some of this budget must
// be spent getting from A to B; the remainder bounds the round-trip
// distance (in latitude) from A or B to the min or max latitude
// attained along the edge AB.
latBudget := 2 * math.Asin(0.5*(r.a.Sub(b.Vector)).Norm()*math.Sin(maxLat))
maxDelta := 0.5*(latBudget-latAB.Length()) + dblEpsilon
// Test whether AB passes through the point of maximum latitude or
// minimum latitude. If the dot product(s) are small enough then the
// result may be ambiguous.
if mA <= mError && mB >= -mError {
latAB.Hi = math.Min(maxLat, latAB.Hi+maxDelta)
}
if mB <= mError && mA >= -mError {
latAB.Lo = math.Max(-maxLat, latAB.Lo-maxDelta)
}
}
r.a = b
r.aLL = bLL
r.bound = r.bound.Union(Rect{latAB, lngAB})
}
// RectBound returns the bounding rectangle of the edge chain that connects the
// vertices defined so far. This bound satisfies the guarantee made
// above, i.e. if the edge chain defines a Loop, then the bound contains
// the LatLng coordinates of all Points contained by the loop.
func (r *RectBounder) RectBound() Rect {
return r.bound.expanded(LatLng{s1.Angle(2 * dblEpsilon), 0}).PolarClosure()
}
// ExpandForSubregions expands a bounding Rect so that it is guaranteed to
// contain the bounds of any subregion whose bounds are computed using
// ComputeRectBound. For example, consider a loop L that defines a square.
// GetBound ensures that if a point P is contained by this square, then
// LatLngFromPoint(P) is contained by the bound. But now consider a diamond
// shaped loop S contained by L. It is possible that GetBound returns a
// *larger* bound for S than it does for L, due to rounding errors. This
// method expands the bound for L so that it is guaranteed to contain the
// bounds of any subregion S.
//
// More precisely, if L is a loop that does not contain either pole, and S
// is a loop such that L.Contains(S), then
//
// ExpandForSubregions(L.RectBound).Contains(S.RectBound).
//
func ExpandForSubregions(bound Rect) Rect {
// Empty bounds don't need expansion.
if bound.IsEmpty() {
return bound
}
// First we need to check whether the bound B contains any nearly-antipodal
// points (to within 4.309 * dblEpsilon). If so then we need to return
// FullRect, since the subregion might have an edge between two
// such points, and AddPoint returns Full for such edges. Note that
// this can happen even if B is not Full for example, consider a loop
// that defines a 10km strip straddling the equator extending from
// longitudes -100 to +100 degrees.
//
// It is easy to check whether B contains any antipodal points, but checking
// for nearly-antipodal points is trickier. Essentially we consider the
// original bound B and its reflection through the origin B', and then test
// whether the minimum distance between B and B' is less than 4.309 * dblEpsilon.
// lngGap is a lower bound on the longitudinal distance between B and its
// reflection B'. (2.5 * dblEpsilon is the maximum combined error of the
// endpoint longitude calculations and the Length call.)
lngGap := math.Max(0, math.Pi-bound.Lng.Length()-2.5*dblEpsilon)
// minAbsLat is the minimum distance from B to the equator (if zero or
// negative, then B straddles the equator).
minAbsLat := math.Max(bound.Lat.Lo, -bound.Lat.Hi)
// latGapSouth and latGapNorth measure the minimum distance from B to the
// south and north poles respectively.
latGapSouth := math.Pi/2 + bound.Lat.Lo
latGapNorth := math.Pi/2 - bound.Lat.Hi
if minAbsLat >= 0 {
// The bound B does not straddle the equator. In this case the minimum
// distance is between one endpoint of the latitude edge in B closest to
// the equator and the other endpoint of that edge in B'. The latitude
// distance between these two points is 2*minAbsLat, and the longitude
// distance is lngGap. We could compute the distance exactly using the
// Haversine formula, but then we would need to bound the errors in that
// calculation. Since we only need accuracy when the distance is very
// small (close to 4.309 * dblEpsilon), we substitute the Euclidean
// distance instead. This gives us a right triangle XYZ with two edges of
// length x = 2*minAbsLat and y ~= lngGap. The desired distance is the
// length of the third edge z, and we have
//
// z ~= sqrt(x^2 + y^2) >= (x + y) / sqrt(2)
//
// Therefore the region may contain nearly antipodal points only if
//
// 2*minAbsLat + lngGap < sqrt(2) * 4.309 * dblEpsilon
// ~= 1.354e-15
//
// Note that because the given bound B is conservative, minAbsLat and
// lngGap are both lower bounds on their true values so we do not need
// to make any adjustments for their errors.
if 2*minAbsLat+lngGap < 1.354e-15 {
return FullRect()
}
} else if lngGap >= math.Pi/2 {
// B spans at most Pi/2 in longitude. The minimum distance is always
// between one corner of B and the diagonally opposite corner of B'. We
// use the same distance approximation that we used above; in this case
// we have an obtuse triangle XYZ with two edges of length x = latGapSouth
// and y = latGapNorth, and angle Z >= Pi/2 between them. We then have
//
// z >= sqrt(x^2 + y^2) >= (x + y) / sqrt(2)
//
// Unlike the case above, latGapSouth and latGapNorth are not lower bounds
// (because of the extra addition operation, and because math.Pi/2 is not
// exactly equal to Pi/2); they can exceed their true values by up to
// 0.75 * dblEpsilon. Putting this all together, the region may contain
// nearly antipodal points only if
//
// latGapSouth + latGapNorth < (sqrt(2) * 4.309 + 1.5) * dblEpsilon
// ~= 1.687e-15
if latGapSouth+latGapNorth < 1.687e-15 {
return FullRect()
}
} else {
// Otherwise we know that (1) the bound straddles the equator and (2) its
// width in longitude is at least Pi/2. In this case the minimum
// distance can occur either between a corner of B and the diagonally
// opposite corner of B' (as in the case above), or between a corner of B
// and the opposite longitudinal edge reflected in B'. It is sufficient
// to only consider the corner-edge case, since this distance is also a
// lower bound on the corner-corner distance when that case applies.
// Consider the spherical triangle XYZ where X is a corner of B with
// minimum absolute latitude, Y is the closest pole to X, and Z is the
// point closest to X on the opposite longitudinal edge of B'. This is a
// right triangle (Z = Pi/2), and from the spherical law of sines we have
//
// sin(z) / sin(Z) = sin(y) / sin(Y)
// sin(maxLatGap) / 1 = sin(dMin) / sin(lngGap)
// sin(dMin) = sin(maxLatGap) * sin(lngGap)
//
// where "maxLatGap" = max(latGapSouth, latGapNorth) and "dMin" is the
// desired minimum distance. Now using the facts that sin(t) >= (2/Pi)*t
// for 0 <= t <= Pi/2, that we only need an accurate approximation when
// at least one of "maxLatGap" or lngGap is extremely small (in which
// case sin(t) ~= t), and recalling that "maxLatGap" has an error of up
// to 0.75 * dblEpsilon, we want to test whether
//
// maxLatGap * lngGap < (4.309 + 0.75) * (Pi/2) * dblEpsilon
// ~= 1.765e-15
if math.Max(latGapSouth, latGapNorth)*lngGap < 1.765e-15 {
return FullRect()
}
}
// Next we need to check whether the subregion might contain any edges that
// span (math.Pi - 2 * dblEpsilon) radians or more in longitude, since AddPoint
// sets the longitude bound to Full in that case. This corresponds to
// testing whether (lngGap <= 0) in lngExpansion below.
// Otherwise, the maximum latitude error in AddPoint is 4.8 * dblEpsilon.
// In the worst case, the errors when computing the latitude bound for a
// subregion could go in the opposite direction as the errors when computing
// the bound for the original region, so we need to double this value.
// (More analysis shows that it's okay to round down to a multiple of
// dblEpsilon.)
//
// For longitude, we rely on the fact that atan2 is correctly rounded and
// therefore no additional bounds expansion is necessary.
latExpansion := 9 * dblEpsilon
lngExpansion := 0.0
if lngGap <= 0 {
lngExpansion = math.Pi
}
return bound.expanded(LatLng{s1.Angle(latExpansion), s1.Angle(lngExpansion)}).PolarClosure()
}

View File

@ -1,71 +0,0 @@
// Copyright 2014 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
// A Region represents a two-dimensional region on the unit sphere.
//
// The purpose of this interface is to allow complex regions to be
// approximated as simpler regions. The interface is restricted to methods
// that are useful for computing approximations.
type Region interface {
// CapBound returns a bounding spherical cap. This is not guaranteed to be exact.
CapBound() Cap
// RectBound returns a bounding latitude-longitude rectangle that contains
// the region. The bounds are not guaranteed to be tight.
RectBound() Rect
// ContainsCell reports whether the region completely contains the given region.
// It returns false if containment could not be determined.
ContainsCell(c Cell) bool
// IntersectsCell reports whether the region intersects the given cell or
// if intersection could not be determined. It returns false if the region
// does not intersect.
IntersectsCell(c Cell) bool
// ContainsPoint reports whether the region contains the given point or not.
// The point should be unit length, although some implementations may relax
// this restriction.
ContainsPoint(p Point) bool
// CellUnionBound returns a small collection of CellIDs whose union covers
// the region. The cells are not sorted, may have redundancies (such as cells
// that contain other cells), and may cover much more area than necessary.
//
// This method is not intended for direct use by client code. Clients
// should typically use Covering, which has options to control the size and
// accuracy of the covering. Alternatively, if you want a fast covering and
// don't care about accuracy, consider calling FastCovering (which returns a
// cleaned-up version of the covering computed by this method).
//
// CellUnionBound implementations should attempt to return a small
// covering (ideally 4 cells or fewer) that covers the region and can be
// computed quickly. The result is used by RegionCoverer as a starting
// point for further refinement.
CellUnionBound() []CellID
}
// Enforce Region interface satisfaction.
var (
_ Region = Cap{}
_ Region = Cell{}
_ Region = (*CellUnion)(nil)
_ Region = (*Loop)(nil)
_ Region = Point{}
_ Region = (*Polygon)(nil)
_ Region = (*Polyline)(nil)
_ Region = Rect{}
)

View File

@ -1,615 +0,0 @@
// Copyright 2015 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 (
"container/heap"
"sort"
)
// RegionCoverer allows arbitrary regions to be approximated as unions of cells (CellUnion).
// This is useful for implementing various sorts of search and precomputation operations.
//
// Typical usage:
//
// rc := &s2.RegionCoverer{MaxLevel: 30, MaxCells: 5}
// r := s2.Region(CapFromCenterArea(center, area))
// covering := rc.Covering(r)
//
// This yields a CellUnion of at most 5 cells that is guaranteed to cover the
// given region (a disc-shaped region on the sphere).
//
// For covering, only cells where (level - MinLevel) is a multiple of LevelMod will be used.
// This effectively allows the branching factor of the S2 CellID hierarchy to be increased.
// Currently the only parameter values allowed are 1, 2, or 3, corresponding to
// branching factors of 4, 16, and 64 respectively.
//
// Note the following:
//
// - MinLevel takes priority over MaxCells, i.e. cells below the given level will
// never be used even if this causes a large number of cells to be returned.
//
// - For any setting of MaxCells, up to 6 cells may be returned if that
// is the minimum number of cells required (e.g. if the region intersects
// all six face cells). Up to 3 cells may be returned even for very tiny
// convex regions if they happen to be located at the intersection of
// three cube faces.
//
// - For any setting of MaxCells, an arbitrary number of cells may be
// returned if MinLevel is too high for the region being approximated.
//
// - If MaxCells is less than 4, the area of the covering may be
// arbitrarily large compared to the area of the original region even if
// the region is convex (e.g. a Cap or Rect).
//
// The approximation algorithm is not optimal but does a pretty good job in
// practice. The output does not always use the maximum number of cells
// allowed, both because this would not always yield a better approximation,
// and because MaxCells is a limit on how much work is done exploring the
// possible covering as well as a limit on the final output size.
//
// Because it is an approximation algorithm, one should not rely on the
// stability of the output. In particular, the output of the covering algorithm
// may change across different versions of the library.
//
// One can also generate interior coverings, which are sets of cells which
// are entirely contained within a region. Interior coverings can be
// empty, even for non-empty regions, if there are no cells that satisfy
// the provided constraints and are contained by the region. Note that for
// performance reasons, it is wise to specify a MaxLevel when computing
// interior coverings - otherwise for regions with small or zero area, the
// algorithm may spend a lot of time subdividing cells all the way to leaf
// level to try to find contained cells.
type RegionCoverer struct {
MinLevel int // the minimum cell level to be used.
MaxLevel int // the maximum cell level to be used.
LevelMod int // the LevelMod to be used.
MaxCells int // the maximum desired number of cells in the approximation.
}
// NewRegionCoverer returns a region coverer with the appropriate defaults.
func NewRegionCoverer() *RegionCoverer {
return &RegionCoverer{
MinLevel: 0,
MaxLevel: maxLevel,
LevelMod: 1,
MaxCells: 8,
}
}
type coverer struct {
minLevel int // the minimum cell level to be used.
maxLevel int // the maximum cell level to be used.
levelMod int // the LevelMod to be used.
maxCells int // the maximum desired number of cells in the approximation.
region Region
result CellUnion
pq priorityQueue
interiorCovering bool
}
type candidate struct {
cell Cell
terminal bool // Cell should not be expanded further.
numChildren int // Number of children that intersect the region.
children []*candidate // Actual size may be 0, 4, 16, or 64 elements.
priority int // Priority of the candidate.
}
type priorityQueue []*candidate
func (pq priorityQueue) Len() int {
return len(pq)
}
func (pq priorityQueue) Less(i, j int) bool {
// We want Pop to give us the highest, not lowest, priority so we use greater than here.
return pq[i].priority > pq[j].priority
}
func (pq priorityQueue) Swap(i, j int) {
pq[i], pq[j] = pq[j], pq[i]
}
func (pq *priorityQueue) Push(x interface{}) {
item := x.(*candidate)
*pq = append(*pq, item)
}
func (pq *priorityQueue) Pop() interface{} {
item := (*pq)[len(*pq)-1]
*pq = (*pq)[:len(*pq)-1]
return item
}
func (pq *priorityQueue) Reset() {
*pq = (*pq)[:0]
}
// newCandidate returns a new candidate with no children if the cell intersects the given region.
// The candidate is marked as terminal if it should not be expanded further.
func (c *coverer) newCandidate(cell Cell) *candidate {
if !c.region.IntersectsCell(cell) {
return nil
}
cand := &candidate{cell: cell}
level := int(cell.level)
if level >= c.minLevel {
if c.interiorCovering {
if c.region.ContainsCell(cell) {
cand.terminal = true
} else if level+c.levelMod > c.maxLevel {
return nil
}
} else if level+c.levelMod > c.maxLevel || c.region.ContainsCell(cell) {
cand.terminal = true
}
}
return cand
}
// expandChildren populates the children of the candidate by expanding the given number of
// levels from the given cell. Returns the number of children that were marked "terminal".
func (c *coverer) expandChildren(cand *candidate, cell Cell, numLevels int) int {
numLevels--
var numTerminals int
last := cell.id.ChildEnd()
for ci := cell.id.ChildBegin(); ci != last; ci = ci.Next() {
childCell := CellFromCellID(ci)
if numLevels > 0 {
if c.region.IntersectsCell(childCell) {
numTerminals += c.expandChildren(cand, childCell, numLevels)
}
continue
}
if child := c.newCandidate(childCell); child != nil {
cand.children = append(cand.children, child)
cand.numChildren++
if child.terminal {
numTerminals++
}
}
}
return numTerminals
}
// addCandidate adds the given candidate to the result if it is marked as "terminal",
// otherwise expands its children and inserts it into the priority queue.
// Passing an argument of nil does nothing.
func (c *coverer) addCandidate(cand *candidate) {
if cand == nil {
return
}
if cand.terminal {
c.result = append(c.result, cand.cell.id)
return
}
// Expand one level at a time until we hit minLevel to ensure that we don't skip over it.
numLevels := c.levelMod
level := int(cand.cell.level)
if level < c.minLevel {
numLevels = 1
}
numTerminals := c.expandChildren(cand, cand.cell, numLevels)
maxChildrenShift := uint(2 * c.levelMod)
if cand.numChildren == 0 {
return
} else if !c.interiorCovering && numTerminals == 1<<maxChildrenShift && level >= c.minLevel {
// Optimization: add the parent cell rather than all of its children.
// We can't do this for interior coverings, since the children just
// intersect the region, but may not be contained by it - we need to
// subdivide them further.
cand.terminal = true
c.addCandidate(cand)
} else {
// We negate the priority so that smaller absolute priorities are returned
// first. The heuristic is designed to refine the largest cells first,
// since those are where we have the largest potential gain. Among cells
// of the same size, we prefer the cells with the fewest children.
// Finally, among cells with equal numbers of children we prefer those
// with the smallest number of children that cannot be refined further.
cand.priority = -(((level<<maxChildrenShift)+cand.numChildren)<<maxChildrenShift + numTerminals)
heap.Push(&c.pq, cand)
}
}
// adjustLevel returns the reduced "level" so that it satisfies levelMod. Levels smaller than minLevel
// are not affected (since cells at these levels are eventually expanded).
func (c *coverer) adjustLevel(level int) int {
if c.levelMod > 1 && level > c.minLevel {
level -= (level - c.minLevel) % c.levelMod
}
return level
}
// adjustCellLevels ensures that all cells with level > minLevel also satisfy levelMod,
// by replacing them with an ancestor if necessary. Cell levels smaller
// than minLevel are not modified (see AdjustLevel). The output is
// then normalized to ensure that no redundant cells are present.
func (c *coverer) adjustCellLevels(cells *CellUnion) {
if c.levelMod == 1 {
return
}
var out int
for _, ci := range *cells {
level := ci.Level()
newLevel := c.adjustLevel(level)
if newLevel != level {
ci = ci.Parent(newLevel)
}
if out > 0 && (*cells)[out-1].Contains(ci) {
continue
}
for out > 0 && ci.Contains((*cells)[out-1]) {
out--
}
(*cells)[out] = ci
out++
}
*cells = (*cells)[:out]
}
// initialCandidates computes a set of initial candidates that cover the given region.
func (c *coverer) initialCandidates() {
// Optimization: start with a small (usually 4 cell) covering of the region's bounding cap.
temp := &RegionCoverer{MaxLevel: c.maxLevel, LevelMod: 1, MaxCells: minInt(4, c.maxCells)}
cells := temp.FastCovering(c.region)
c.adjustCellLevels(&cells)
for _, ci := range cells {
c.addCandidate(c.newCandidate(CellFromCellID(ci)))
}
}
// coveringInternal generates a covering and stores it in result.
// Strategy: Start with the 6 faces of the cube. Discard any
// that do not intersect the shape. Then repeatedly choose the
// largest cell that intersects the shape and subdivide it.
//
// result contains the cells that will be part of the output, while pq
// contains cells that we may still subdivide further. Cells that are
// entirely contained within the region are immediately added to the output,
// while cells that do not intersect the region are immediately discarded.
// Therefore pq only contains cells that partially intersect the region.
// Candidates are prioritized first according to cell size (larger cells
// first), then by the number of intersecting children they have (fewest
// children first), and then by the number of fully contained children
// (fewest children first).
func (c *coverer) coveringInternal(region Region) {
c.region = region
c.initialCandidates()
for c.pq.Len() > 0 && (!c.interiorCovering || len(c.result) < c.maxCells) {
cand := heap.Pop(&c.pq).(*candidate)
// For interior covering we keep subdividing no matter how many children
// candidate has. If we reach MaxCells before expanding all children,
// we will just use some of them.
// For exterior covering we cannot do this, because result has to cover the
// whole region, so all children have to be used.
// candidate.numChildren == 1 case takes care of the situation when we
// already have more than MaxCells in result (minLevel is too high).
// Subdividing of the candidate with one child does no harm in this case.
if c.interiorCovering || int(cand.cell.level) < c.minLevel || cand.numChildren == 1 || len(c.result)+c.pq.Len()+cand.numChildren <= c.maxCells {
for _, child := range cand.children {
if !c.interiorCovering || len(c.result) < c.maxCells {
c.addCandidate(child)
}
}
} else {
cand.terminal = true
c.addCandidate(cand)
}
}
c.pq.Reset()
c.region = nil
// Rather than just returning the raw list of cell ids, we construct a cell
// union and then denormalize it. This has the effect of replacing four
// child cells with their parent whenever this does not violate the covering
// parameters specified (min_level, level_mod, etc). This significantly
// reduces the number of cells returned in many cases, and it is cheap
// compared to computing the covering in the first place.
c.result.Normalize()
if c.minLevel > 0 || c.levelMod > 1 {
c.result.Denormalize(c.minLevel, c.levelMod)
}
}
// newCoverer returns an instance of coverer.
func (rc *RegionCoverer) newCoverer() *coverer {
return &coverer{
minLevel: maxInt(0, minInt(maxLevel, rc.MinLevel)),
maxLevel: maxInt(0, minInt(maxLevel, rc.MaxLevel)),
levelMod: maxInt(1, minInt(3, rc.LevelMod)),
maxCells: rc.MaxCells,
}
}
// Covering returns a CellUnion that covers the given region and satisfies the various restrictions.
func (rc *RegionCoverer) Covering(region Region) CellUnion {
covering := rc.CellUnion(region)
covering.Denormalize(maxInt(0, minInt(maxLevel, rc.MinLevel)), maxInt(1, minInt(3, rc.LevelMod)))
return covering
}
// InteriorCovering returns a CellUnion that is contained within the given region and satisfies the various restrictions.
func (rc *RegionCoverer) InteriorCovering(region Region) CellUnion {
intCovering := rc.InteriorCellUnion(region)
intCovering.Denormalize(maxInt(0, minInt(maxLevel, rc.MinLevel)), maxInt(1, minInt(3, rc.LevelMod)))
return intCovering
}
// CellUnion returns a normalized CellUnion that covers the given region and
// satisfies the restrictions except for minLevel and levelMod. These criteria
// cannot be satisfied using a cell union because cell unions are
// automatically normalized by replacing four child cells with their parent
// whenever possible. (Note that the list of cell ids passed to the CellUnion
// constructor does in fact satisfy all the given restrictions.)
func (rc *RegionCoverer) CellUnion(region Region) CellUnion {
c := rc.newCoverer()
c.coveringInternal(region)
cu := c.result
cu.Normalize()
return cu
}
// InteriorCellUnion returns a normalized CellUnion that is contained within the given region and
// satisfies the restrictions except for minLevel and levelMod. These criteria
// cannot be satisfied using a cell union because cell unions are
// automatically normalized by replacing four child cells with their parent
// whenever possible. (Note that the list of cell ids passed to the CellUnion
// constructor does in fact satisfy all the given restrictions.)
func (rc *RegionCoverer) InteriorCellUnion(region Region) CellUnion {
c := rc.newCoverer()
c.interiorCovering = true
c.coveringInternal(region)
cu := c.result
cu.Normalize()
return cu
}
// FastCovering returns a CellUnion that covers the given region similar to Covering,
// except that this method is much faster and the coverings are not as tight.
// All of the usual parameters are respected (MaxCells, MinLevel, MaxLevel, and LevelMod),
// except that the implementation makes no attempt to take advantage of large values of
// MaxCells. (A small number of cells will always be returned.)
//
// This function is useful as a starting point for algorithms that
// recursively subdivide cells.
func (rc *RegionCoverer) FastCovering(region Region) CellUnion {
c := rc.newCoverer()
cu := CellUnion(region.CellUnionBound())
c.normalizeCovering(&cu)
return cu
}
// IsCanonical reports whether the given CellUnion represents a valid covering
// that conforms to the current covering parameters. In particular:
//
// - All CellIDs must be valid.
//
// - CellIDs must be sorted and non-overlapping.
//
// - CellID levels must satisfy MinLevel, MaxLevel, and LevelMod.
//
// - If the covering has more than MaxCells, there must be no two cells with
// a common ancestor at MinLevel or higher.
//
// - There must be no sequence of cells that could be replaced by an
// ancestor (i.e. with LevelMod == 1, the 4 child cells of a parent).
func (rc *RegionCoverer) IsCanonical(covering CellUnion) bool {
return rc.newCoverer().isCanonical(covering)
}
// normalizeCovering normalizes the "covering" so that it conforms to the
// current covering parameters (maxCells, minLevel, maxLevel, and levelMod).
// This method makes no attempt to be optimal. In particular, if
// minLevel > 0 or levelMod > 1 then it may return more than the
// desired number of cells even when this isn't necessary.
//
// Note that when the covering parameters have their default values, almost
// all of the code in this function is skipped.
func (c *coverer) normalizeCovering(covering *CellUnion) {
// If any cells are too small, or don't satisfy levelMod, then replace them with ancestors.
if c.maxLevel < maxLevel || c.levelMod > 1 {
for i, ci := range *covering {
level := ci.Level()
newLevel := c.adjustLevel(minInt(level, c.maxLevel))
if newLevel != level {
(*covering)[i] = ci.Parent(newLevel)
}
}
}
// Sort the cells and simplify them.
covering.Normalize()
// Make sure that the covering satisfies minLevel and levelMod,
// possibly at the expense of satisfying MaxCells.
if c.minLevel > 0 || c.levelMod > 1 {
covering.Denormalize(c.minLevel, c.levelMod)
}
// If there are too many cells and the covering is very large, use the
// RegionCoverer to compute a new covering. (This avoids possible O(n^2)
// behavior of the simpler algorithm below.)
excess := len(*covering) - c.maxCells
if excess <= 0 || c.isCanonical(*covering) {
return
}
if excess*len(*covering) > 10000 {
rc := NewRegionCoverer()
(*covering) = rc.Covering(covering)
return
}
// If there are still too many cells, then repeatedly replace two adjacent
// cells in CellID order by their lowest common ancestor.
for len(*covering) > c.maxCells {
bestIndex := -1
bestLevel := -1
for i := 0; i+1 < len(*covering); i++ {
level, ok := (*covering)[i].CommonAncestorLevel((*covering)[i+1])
if !ok {
continue
}
level = c.adjustLevel(level)
if level > bestLevel {
bestLevel = level
bestIndex = i
}
}
if bestLevel < c.minLevel {
break
}
// Replace all cells contained by the new ancestor cell.
id := (*covering)[bestIndex].Parent(bestLevel)
(*covering) = c.replaceCellsWithAncestor(*covering, id)
// Now repeatedly check whether all children of the parent cell are
// present, in which case we can replace those cells with their parent.
for bestLevel > c.minLevel {
bestLevel -= c.levelMod
id = id.Parent(bestLevel)
if !c.containsAllChildren(*covering, id) {
break
}
(*covering) = c.replaceCellsWithAncestor(*covering, id)
}
}
}
// isCanonical reports whether the covering is canonical.
func (c *coverer) isCanonical(covering CellUnion) bool {
trueMax := c.maxLevel
if c.levelMod != 1 {
trueMax = c.maxLevel - (c.maxLevel-c.minLevel)%c.levelMod
}
tooManyCells := len(covering) > c.maxCells
sameParentCount := 1
prevID := CellID(0)
for _, id := range covering {
if !id.IsValid() {
return false
}
// Check that the CellID level is acceptable.
level := id.Level()
if level < c.minLevel || level > trueMax {
return false
}
if c.levelMod > 1 && (level-c.minLevel)%c.levelMod != 0 {
return false
}
if prevID != 0 {
// Check that cells are sorted and non-overlapping.
if prevID.RangeMax() >= id.RangeMin() {
return false
}
lev, ok := id.CommonAncestorLevel(prevID)
// If there are too many cells, check that no pair of adjacent cells
// could be replaced by an ancestor.
if tooManyCells && (ok && lev >= c.minLevel) {
return false
}
// Check that there are no sequences of (4 ** level_mod) cells that all
// have the same parent (considering only multiples of "level_mod").
pLevel := level - c.levelMod
if pLevel < c.minLevel || level != prevID.Level() ||
id.Parent(pLevel) != prevID.Parent(pLevel) {
sameParentCount = 1
} else {
sameParentCount++
if sameParentCount == 1<<uint(2*c.levelMod) {
return false
}
}
}
prevID = id
}
return true
}
func (c *coverer) containsAllChildren(covering []CellID, id CellID) bool {
pos := sort.Search(len(covering), func(i int) bool { return (covering)[i] >= id.RangeMin() })
level := id.Level() + c.levelMod
for child := id.ChildBeginAtLevel(level); child != id.ChildEndAtLevel(level); child = child.Next() {
if pos == len(covering) || covering[pos] != child {
return false
}
pos++
}
return true
}
// replaceCellsWithAncestor replaces all descendants of the given id in covering
// with id. This requires the covering contains at least one descendant of id.
func (c *coverer) replaceCellsWithAncestor(covering []CellID, id CellID) []CellID {
begin := sort.Search(len(covering), func(i int) bool { return covering[i] > id.RangeMin() })
end := sort.Search(len(covering), func(i int) bool { return covering[i] > id.RangeMax() })
return append(append(covering[:begin], id), covering[end:]...)
}
// SimpleRegionCovering returns a set of cells at the given level that cover
// the connected region and a starting point on the boundary or inside the
// region. The cells are returned in arbitrary order.
//
// Note that this method is not faster than the regular Covering
// method for most region types, such as Cap or Polygon, and in fact it
// can be much slower when the output consists of a large number of cells.
// Currently it can be faster at generating coverings of long narrow regions
// such as polylines, but this may change in the future.
func SimpleRegionCovering(region Region, start Point, level int) []CellID {
return FloodFillRegionCovering(region, cellIDFromPoint(start).Parent(level))
}
// FloodFillRegionCovering returns all edge-connected cells at the same level as
// the given CellID that intersect the given region, in arbitrary order.
func FloodFillRegionCovering(region Region, start CellID) []CellID {
var output []CellID
all := map[CellID]bool{
start: true,
}
frontier := []CellID{start}
for len(frontier) > 0 {
id := frontier[len(frontier)-1]
frontier = frontier[:len(frontier)-1]
if !region.IntersectsCell(CellFromCellID(id)) {
continue
}
output = append(output, id)
for _, nbr := range id.EdgeNeighbors() {
if !all[nbr] {
all[nbr] = true
frontier = append(frontier, nbr)
}
}
}
return output
}

View File

@ -1,66 +0,0 @@
// Copyright 2020 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
// A RegionUnion represents a union of possibly overlapping regions.
// It is convenient for computing a covering of a set of regions.
type RegionUnion []Region
// CapBound returns a bounding cap for this RegionUnion.
func (ru RegionUnion) CapBound() Cap { return ru.RectBound().CapBound() }
// RectBound returns a bounding latitude-longitude rectangle for this RegionUnion.
func (ru RegionUnion) RectBound() Rect {
ret := EmptyRect()
for _, reg := range ru {
ret = ret.Union(reg.RectBound())
}
return ret
}
// ContainsCell reports whether the given Cell is contained by this RegionUnion.
func (ru RegionUnion) ContainsCell(c Cell) bool {
for _, reg := range ru {
if reg.ContainsCell(c) {
return true
}
}
return false
}
// IntersectsCell reports whether this RegionUnion intersects the given cell.
func (ru RegionUnion) IntersectsCell(c Cell) bool {
for _, reg := range ru {
if reg.IntersectsCell(c) {
return true
}
}
return false
}
// ContainsPoint reports whether this RegionUnion contains the Point.
func (ru RegionUnion) ContainsPoint(p Point) bool {
for _, reg := range ru {
if reg.ContainsPoint(p) {
return true
}
}
return false
}
// CellUnionBound computes a covering of the RegionUnion.
func (ru RegionUnion) CellUnionBound() []CellID {
return ru.CapBound().CellUnionBound()
}

View File

@ -1,263 +0,0 @@
// 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
import (
"sort"
)
// Edge represents a geodesic edge consisting of two vertices. Zero-length edges are
// allowed, and can be used to represent points.
type Edge struct {
V0, V1 Point
}
// Cmp compares the two edges using the underlying Points Cmp method and returns
//
// -1 if e < other
// 0 if e == other
// +1 if e > other
//
// The two edges are compared by first vertex, and then by the second vertex.
func (e Edge) Cmp(other Edge) int {
if v0cmp := e.V0.Cmp(other.V0.Vector); v0cmp != 0 {
return v0cmp
}
return e.V1.Cmp(other.V1.Vector)
}
// sortEdges sorts the slice of Edges in place.
func sortEdges(e []Edge) {
sort.Sort(edges(e))
}
// edges implements the Sort interface for slices of Edge.
type edges []Edge
func (e edges) Len() int { return len(e) }
func (e edges) Swap(i, j int) { e[i], e[j] = e[j], e[i] }
func (e edges) Less(i, j int) bool { return e[i].Cmp(e[j]) == -1 }
// ShapeEdgeID is a unique identifier for an Edge within an ShapeIndex,
// consisting of a (shapeID, edgeID) pair.
type ShapeEdgeID struct {
ShapeID int32
EdgeID int32
}
// Cmp compares the two ShapeEdgeIDs and returns
//
// -1 if s < other
// 0 if s == other
// +1 if s > other
//
// The two are compared first by shape id and then by edge id.
func (s ShapeEdgeID) Cmp(other ShapeEdgeID) int {
switch {
case s.ShapeID < other.ShapeID:
return -1
case s.ShapeID > other.ShapeID:
return 1
}
switch {
case s.EdgeID < other.EdgeID:
return -1
case s.EdgeID > other.EdgeID:
return 1
}
return 0
}
// ShapeEdge represents a ShapeEdgeID with the two endpoints of that Edge.
type ShapeEdge struct {
ID ShapeEdgeID
Edge Edge
}
// Chain represents a range of edge IDs corresponding to a chain of connected
// edges, specified as a (start, length) pair. The chain is defined to consist of
// edge IDs {start, start + 1, ..., start + length - 1}.
type Chain struct {
Start, Length int
}
// ChainPosition represents the position of an edge within a given edge chain,
// specified as a (chainID, offset) pair. Chains are numbered sequentially
// starting from zero, and offsets are measured from the start of each chain.
type ChainPosition struct {
ChainID, Offset int
}
// A ReferencePoint consists of a point and a boolean indicating whether the point
// is contained by a particular shape.
type ReferencePoint struct {
Point Point
Contained bool
}
// OriginReferencePoint returns a ReferencePoint with the given value for
// contained and the origin point. It should be used when all points or no
// points are contained.
func OriginReferencePoint(contained bool) ReferencePoint {
return ReferencePoint{Point: OriginPoint(), Contained: contained}
}
// typeTag is a 32-bit tag that can be used to identify the type of an encoded
// Shape. All encodable types have a non-zero type tag. The tag associated with
type typeTag uint32
const (
// Indicates that a given Shape type cannot be encoded.
typeTagNone typeTag = 0
typeTagPolygon typeTag = 1
typeTagPolyline typeTag = 2
typeTagPointVector typeTag = 3
typeTagLaxPolyline typeTag = 4
typeTagLaxPolygon typeTag = 5
// The minimum allowable tag for future user-defined Shape types.
typeTagMinUser typeTag = 8192
)
// Shape represents polygonal geometry in a flexible way. It is organized as a
// collection of edges that optionally defines an interior. All geometry
// represented by a given Shape must have the same dimension, which means that
// an Shape can represent either a set of points, a set of polylines, or a set
// of polygons.
//
// Shape is defined as an interface in order to give clients control over the
// underlying data representation. Sometimes an Shape does not have any data of
// its own, but instead wraps some other type.
//
// Shape operations are typically defined on a ShapeIndex rather than
// individual shapes. An ShapeIndex is simply a collection of Shapes,
// possibly of different dimensions (e.g. 10 points and 3 polygons), organized
// into a data structure for efficient edge access.
//
// The edges of a Shape are indexed by a contiguous range of edge IDs
// starting at 0. The edges are further subdivided into chains, where each
// chain consists of a sequence of edges connected end-to-end (a polyline).
// For example, a Shape representing two polylines AB and CDE would have
// three edges (AB, CD, DE) grouped into two chains: (AB) and (CD, DE).
// Similarly, an Shape representing 5 points would have 5 chains consisting
// of one edge each.
//
// Shape has methods that allow edges to be accessed either using the global
// numbering (edge ID) or within a particular chain. The global numbering is
// sufficient for most purposes, but the chain representation is useful for
// certain algorithms such as intersection (see BooleanOperation).
type Shape interface {
// NumEdges returns the number of edges in this shape.
NumEdges() int
// Edge returns the edge for the given edge index.
Edge(i int) Edge
// ReferencePoint returns an arbitrary reference point for the shape. (The
// containment boolean value must be false for shapes that do not have an interior.)
//
// This reference point may then be used to compute the containment of other
// points by counting edge crossings.
ReferencePoint() ReferencePoint
// NumChains reports the number of contiguous edge chains in the shape.
// For example, a shape whose edges are [AB, BC, CD, AE, EF] would consist
// of two chains (AB,BC,CD and AE,EF). Every chain is assigned a chain Id
// numbered sequentially starting from zero.
//
// Note that it is always acceptable to implement this method by returning
// NumEdges, i.e. every chain consists of a single edge, but this may
// reduce the efficiency of some algorithms.
NumChains() int
// Chain returns the range of edge IDs corresponding to the given edge chain.
// Edge chains must form contiguous, non-overlapping ranges that cover
// the entire range of edge IDs. This is spelled out more formally below:
//
// 0 <= i < NumChains()
// Chain(i).length > 0, for all i
// Chain(0).start == 0
// Chain(i).start + Chain(i).length == Chain(i+1).start, for i < NumChains()-1
// Chain(i).start + Chain(i).length == NumEdges(), for i == NumChains()-1
Chain(chainID int) Chain
// ChainEdgeReturns the edge at offset "offset" within edge chain "chainID".
// Equivalent to "shape.Edge(shape.Chain(chainID).start + offset)"
// but more efficient.
ChainEdge(chainID, offset int) Edge
// ChainPosition finds the chain containing the given edge, and returns the
// position of that edge as a ChainPosition(chainID, offset) pair.
//
// shape.Chain(pos.chainID).start + pos.offset == edgeID
// shape.Chain(pos.chainID+1).start > edgeID
//
// where pos == shape.ChainPosition(edgeID).
ChainPosition(edgeID int) ChainPosition
// Dimension returns the dimension of the geometry represented by this shape,
// either 0, 1 or 2 for point, polyline and polygon geometry respectively.
//
// 0 - Point geometry. Each point is represented as a degenerate edge.
//
// 1 - Polyline geometry. Polyline edges may be degenerate. A shape may
// represent any number of polylines. Polylines edges may intersect.
//
// 2 - Polygon geometry. Edges should be oriented such that the polygon
// interior is always on the left. In theory the edges may be returned
// in any order, but typically the edges are organized as a collection
// of edge chains where each chain represents one polygon loop.
// Polygons may have degeneracies (e.g., degenerate edges or sibling
// pairs consisting of an edge and its corresponding reversed edge).
// A polygon loop may also be full (containing all points on the
// sphere); by convention this is represented as a chain with no edges.
// (See laxPolygon for details.)
//
// This method allows degenerate geometry of different dimensions
// to be distinguished, e.g. it allows a point to be distinguished from a
// polyline or polygon that has been simplified to a single point.
Dimension() int
// IsEmpty reports whether the Shape contains no points. (Note that the full
// polygon is represented as a chain with zero edges.)
IsEmpty() bool
// IsFull reports whether the Shape contains all points on the sphere.
IsFull() bool
// typeTag returns a value that can be used to identify the type of an
// encoded Shape.
typeTag() typeTag
// We do not support implementations of this interface outside this package.
privateInterface()
}
// defaultShapeIsEmpty reports whether this shape contains no points.
func defaultShapeIsEmpty(s Shape) bool {
return s.NumEdges() == 0 && (s.Dimension() != 2 || s.NumChains() == 0)
}
// defaultShapeIsFull reports whether this shape contains all points on the sphere.
func defaultShapeIsFull(s Shape) bool {
return s.NumEdges() == 0 && s.Dimension() == 2 && s.NumChains() > 0
}
// A minimal check for types that should satisfy the Shape interface.
var (
_ Shape = &Loop{}
_ Shape = &Polygon{}
_ Shape = &Polyline{}
)

File diff suppressed because it is too large Load Diff

View File

@ -1,228 +0,0 @@
// 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
}

View File

@ -1,72 +0,0 @@
// Copyright 2020 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
// EdgeIterator is an iterator that advances through all edges in an ShapeIndex.
// This is different to the ShapeIndexIterator, which advances through the cells in the
// ShapeIndex.
type EdgeIterator struct {
index *ShapeIndex
shapeID int32
numEdges int32
edgeID int32
}
// NewEdgeIterator creates a new edge iterator for the given index.
func NewEdgeIterator(index *ShapeIndex) *EdgeIterator {
e := &EdgeIterator{
index: index,
shapeID: -1,
edgeID: -1,
}
e.Next()
return e
}
// ShapeID returns the current shape ID.
func (e *EdgeIterator) ShapeID() int32 { return e.shapeID }
// EdgeID returns the current edge ID.
func (e *EdgeIterator) EdgeID() int32 { return e.edgeID }
// ShapeEdgeID returns the current (shapeID, edgeID).
func (e *EdgeIterator) ShapeEdgeID() ShapeEdgeID { return ShapeEdgeID{e.shapeID, e.edgeID} }
// Edge returns the current edge.
func (e *EdgeIterator) Edge() Edge {
return e.index.Shape(e.shapeID).Edge(int(e.edgeID))
}
// Done reports if the iterator is positioned at or after the last index edge.
func (e *EdgeIterator) Done() bool { return e.shapeID >= int32(len(e.index.shapes)) }
// Next positions the iterator at the next index edge.
func (e *EdgeIterator) Next() {
e.edgeID++
for ; e.edgeID >= e.numEdges; e.edgeID++ {
e.shapeID++
if e.shapeID >= int32(len(e.index.shapes)) {
break
}
shape := e.index.Shape(e.shapeID)
if shape == nil {
e.numEdges = 0
} else {
e.numEdges = int32(shape.NumEdges())
}
e.edgeID = -1
}
}

View File

@ -1,427 +0,0 @@
// Copyright 2014 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"
"github.com/golang/geo/r3"
)
//
// This file contains documentation of the various coordinate systems used
// throughout the library. Most importantly, S2 defines a framework for
// decomposing the unit sphere into a hierarchy of "cells". Each cell is a
// quadrilateral bounded by four geodesics. The top level of the hierarchy is
// obtained by projecting the six faces of a cube onto the unit sphere, and
// lower levels are obtained by subdividing each cell into four children
// recursively. Cells are numbered such that sequentially increasing cells
// follow a continuous space-filling curve over the entire sphere. The
// transformation is designed to make the cells at each level fairly uniform
// in size.
//
////////////////////////// S2 Cell Decomposition /////////////////////////
//
// The following methods define the cube-to-sphere projection used by
// the Cell decomposition.
//
// In the process of converting a latitude-longitude pair to a 64-bit cell
// id, the following coordinate systems are used:
//
// (id)
// An CellID is a 64-bit encoding of a face and a Hilbert curve position
// on that face. The Hilbert curve position implicitly encodes both the
// position of a cell and its subdivision level (see s2cellid.go).
//
// (face, i, j)
// Leaf-cell coordinates. "i" and "j" are integers in the range
// [0,(2**30)-1] that identify a particular leaf cell on the given face.
// The (i, j) coordinate system is right-handed on each face, and the
// faces are oriented such that Hilbert curves connect continuously from
// one face to the next.
//
// (face, s, t)
// Cell-space coordinates. "s" and "t" are real numbers in the range
// [0,1] that identify a point on the given face. For example, the point
// (s, t) = (0.5, 0.5) corresponds to the center of the top-level face
// cell. This point is also a vertex of exactly four cells at each
// subdivision level greater than zero.
//
// (face, si, ti)
// Discrete cell-space coordinates. These are obtained by multiplying
// "s" and "t" by 2**31 and rounding to the nearest unsigned integer.
// Discrete coordinates lie in the range [0,2**31]. This coordinate
// system can represent the edge and center positions of all cells with
// no loss of precision (including non-leaf cells). In binary, each
// coordinate of a level-k cell center ends with a 1 followed by
// (30 - k) 0s. The coordinates of its edges end with (at least)
// (31 - k) 0s.
//
// (face, u, v)
// Cube-space coordinates in the range [-1,1]. To make the cells at each
// level more uniform in size after they are projected onto the sphere,
// we apply a nonlinear transformation of the form u=f(s), v=f(t).
// The (u, v) coordinates after this transformation give the actual
// coordinates on the cube face (modulo some 90 degree rotations) before
// it is projected onto the unit sphere.
//
// (face, u, v, w)
// Per-face coordinate frame. This is an extension of the (face, u, v)
// cube-space coordinates that adds a third axis "w" in the direction of
// the face normal. It is always a right-handed 3D coordinate system.
// Cube-space coordinates can be converted to this frame by setting w=1,
// while (u,v,w) coordinates can be projected onto the cube face by
// dividing by w, i.e. (face, u/w, v/w).
//
// (x, y, z)
// Direction vector (Point). Direction vectors are not necessarily unit
// length, and are often chosen to be points on the biunit cube
// [-1,+1]x[-1,+1]x[-1,+1]. They can be be normalized to obtain the
// corresponding point on the unit sphere.
//
// (lat, lng)
// Latitude and longitude (LatLng). Latitudes must be between -90 and
// 90 degrees inclusive, and longitudes must be between -180 and 180
// degrees inclusive.
//
// Note that the (i, j), (s, t), (si, ti), and (u, v) coordinate systems are
// right-handed on all six faces.
//
//
// There are a number of different projections from cell-space (s,t) to
// cube-space (u,v): linear, quadratic, and tangent. They have the following
// tradeoffs:
//
// Linear - This is the fastest transformation, but also produces the least
// uniform cell sizes. Cell areas vary by a factor of about 5.2, with the
// largest cells at the center of each face and the smallest cells in
// the corners.
//
// Tangent - Transforming the coordinates via Atan makes the cell sizes
// more uniform. The areas vary by a maximum ratio of 1.4 as opposed to a
// maximum ratio of 5.2. However, each call to Atan is about as expensive
// as all of the other calculations combined when converting from points to
// cell ids, i.e. it reduces performance by a factor of 3.
//
// Quadratic - This is an approximation of the tangent projection that
// is much faster and produces cells that are almost as uniform in size.
// It is about 3 times faster than the tangent projection for converting
// cell ids to points or vice versa. Cell areas vary by a maximum ratio of
// about 2.1.
//
// Here is a table comparing the cell uniformity using each projection. Area
// Ratio is the maximum ratio over all subdivision levels of the largest cell
// area to the smallest cell area at that level, Edge Ratio is the maximum
// ratio of the longest edge of any cell to the shortest edge of any cell at
// the same level, and Diag Ratio is the ratio of the longest diagonal of
// any cell to the shortest diagonal of any cell at the same level.
//
// Area Edge Diag
// Ratio Ratio Ratio
// -----------------------------------
// Linear: 5.200 2.117 2.959
// Tangent: 1.414 1.414 1.704
// Quadratic: 2.082 1.802 1.932
//
// The worst-case cell aspect ratios are about the same with all three
// projections. The maximum ratio of the longest edge to the shortest edge
// within the same cell is about 1.4 and the maximum ratio of the diagonals
// within the same cell is about 1.7.
//
// For Go we have chosen to use only the Quadratic approach. Other language
// implementations may offer other choices.
const (
// maxSiTi is the maximum value of an si- or ti-coordinate.
// It is one shift more than maxSize. The range of valid (si,ti)
// values is [0..maxSiTi].
maxSiTi = maxSize << 1
)
// siTiToST converts an si- or ti-value to the corresponding s- or t-value.
// Value is capped at 1.0 because there is no DCHECK in Go.
func siTiToST(si uint32) float64 {
if si > maxSiTi {
return 1.0
}
return float64(si) / float64(maxSiTi)
}
// stToSiTi converts the s- or t-value to the nearest si- or ti-coordinate.
// The result may be outside the range of valid (si,ti)-values. Value of
// 0.49999999999999994 (math.NextAfter(0.5, -1)), will be incorrectly rounded up.
func stToSiTi(s float64) uint32 {
if s < 0 {
return uint32(s*maxSiTi - 0.5)
}
return uint32(s*maxSiTi + 0.5)
}
// stToUV converts an s or t value to the corresponding u or v value.
// This is a non-linear transformation from [-1,1] to [-1,1] that
// attempts to make the cell sizes more uniform.
// This uses what the C++ version calls 'the quadratic transform'.
func stToUV(s float64) float64 {
if s >= 0.5 {
return (1 / 3.) * (4*s*s - 1)
}
return (1 / 3.) * (1 - 4*(1-s)*(1-s))
}
// uvToST is the inverse of the stToUV transformation. Note that it
// is not always true that uvToST(stToUV(x)) == x due to numerical
// errors.
func uvToST(u float64) float64 {
if u >= 0 {
return 0.5 * math.Sqrt(1+3*u)
}
return 1 - 0.5*math.Sqrt(1-3*u)
}
// face returns face ID from 0 to 5 containing the r. For points on the
// boundary between faces, the result is arbitrary but deterministic.
func face(r r3.Vector) int {
f := r.LargestComponent()
switch {
case f == r3.XAxis && r.X < 0:
f += 3
case f == r3.YAxis && r.Y < 0:
f += 3
case f == r3.ZAxis && r.Z < 0:
f += 3
}
return int(f)
}
// validFaceXYZToUV given a valid face for the given point r (meaning that
// dot product of r with the face normal is positive), returns
// the corresponding u and v values, which may lie outside the range [-1,1].
func validFaceXYZToUV(face int, r r3.Vector) (float64, float64) {
switch face {
case 0:
return r.Y / r.X, r.Z / r.X
case 1:
return -r.X / r.Y, r.Z / r.Y
case 2:
return -r.X / r.Z, -r.Y / r.Z
case 3:
return r.Z / r.X, r.Y / r.X
case 4:
return r.Z / r.Y, -r.X / r.Y
}
return -r.Y / r.Z, -r.X / r.Z
}
// xyzToFaceUV converts a direction vector (not necessarily unit length) to
// (face, u, v) coordinates.
func xyzToFaceUV(r r3.Vector) (f int, u, v float64) {
f = face(r)
u, v = validFaceXYZToUV(f, r)
return f, u, v
}
// faceUVToXYZ turns face and UV coordinates into an unnormalized 3 vector.
func faceUVToXYZ(face int, u, v float64) r3.Vector {
switch face {
case 0:
return r3.Vector{1, u, v}
case 1:
return r3.Vector{-u, 1, v}
case 2:
return r3.Vector{-u, -v, 1}
case 3:
return r3.Vector{-1, -v, -u}
case 4:
return r3.Vector{v, -1, -u}
default:
return r3.Vector{v, u, -1}
}
}
// faceXYZToUV returns the u and v values (which may lie outside the range
// [-1, 1]) if the dot product of the point p with the given face normal is positive.
func faceXYZToUV(face int, p Point) (u, v float64, ok bool) {
switch face {
case 0:
if p.X <= 0 {
return 0, 0, false
}
case 1:
if p.Y <= 0 {
return 0, 0, false
}
case 2:
if p.Z <= 0 {
return 0, 0, false
}
case 3:
if p.X >= 0 {
return 0, 0, false
}
case 4:
if p.Y >= 0 {
return 0, 0, false
}
default:
if p.Z >= 0 {
return 0, 0, false
}
}
u, v = validFaceXYZToUV(face, p.Vector)
return u, v, true
}
// faceXYZtoUVW transforms the given point P to the (u,v,w) coordinate frame of the given
// face where the w-axis represents the face normal.
func faceXYZtoUVW(face int, p Point) Point {
// The result coordinates are simply the dot products of P with the (u,v,w)
// axes for the given face (see faceUVWAxes).
switch face {
case 0:
return Point{r3.Vector{p.Y, p.Z, p.X}}
case 1:
return Point{r3.Vector{-p.X, p.Z, p.Y}}
case 2:
return Point{r3.Vector{-p.X, -p.Y, p.Z}}
case 3:
return Point{r3.Vector{-p.Z, -p.Y, -p.X}}
case 4:
return Point{r3.Vector{-p.Z, p.X, -p.Y}}
default:
return Point{r3.Vector{p.Y, p.X, -p.Z}}
}
}
// faceSiTiToXYZ transforms the (si, ti) coordinates to a (not necessarily
// unit length) Point on the given face.
func faceSiTiToXYZ(face int, si, ti uint32) Point {
return Point{faceUVToXYZ(face, stToUV(siTiToST(si)), stToUV(siTiToST(ti)))}
}
// xyzToFaceSiTi transforms the (not necessarily unit length) Point to
// (face, si, ti) coordinates and the level the Point is at.
func xyzToFaceSiTi(p Point) (face int, si, ti uint32, level int) {
face, u, v := xyzToFaceUV(p.Vector)
si = stToSiTi(uvToST(u))
ti = stToSiTi(uvToST(v))
// If the levels corresponding to si,ti are not equal, then p is not a cell
// center. The si,ti values of 0 and maxSiTi need to be handled specially
// because they do not correspond to cell centers at any valid level; they
// are mapped to level -1 by the code at the end.
level = maxLevel - findLSBSetNonZero64(uint64(si|maxSiTi))
if level < 0 || level != maxLevel-findLSBSetNonZero64(uint64(ti|maxSiTi)) {
return face, si, ti, -1
}
// In infinite precision, this test could be changed to ST == SiTi. However,
// due to rounding errors, uvToST(xyzToFaceUV(faceUVToXYZ(stToUV(...)))) is
// not idempotent. On the other hand, the center is computed exactly the same
// way p was originally computed (if it is indeed the center of a Cell);
// the comparison can be exact.
if p.Vector == faceSiTiToXYZ(face, si, ti).Normalize() {
return face, si, ti, level
}
return face, si, ti, -1
}
// uNorm returns the right-handed normal (not necessarily unit length) for an
// edge in the direction of the positive v-axis at the given u-value on
// the given face. (This vector is perpendicular to the plane through
// the sphere origin that contains the given edge.)
func uNorm(face int, u float64) r3.Vector {
switch face {
case 0:
return r3.Vector{u, -1, 0}
case 1:
return r3.Vector{1, u, 0}
case 2:
return r3.Vector{1, 0, u}
case 3:
return r3.Vector{-u, 0, 1}
case 4:
return r3.Vector{0, -u, 1}
default:
return r3.Vector{0, -1, -u}
}
}
// vNorm returns the right-handed normal (not necessarily unit length) for an
// edge in the direction of the positive u-axis at the given v-value on
// the given face.
func vNorm(face int, v float64) r3.Vector {
switch face {
case 0:
return r3.Vector{-v, 0, 1}
case 1:
return r3.Vector{0, -v, 1}
case 2:
return r3.Vector{0, -1, -v}
case 3:
return r3.Vector{v, -1, 0}
case 4:
return r3.Vector{1, v, 0}
default:
return r3.Vector{1, 0, v}
}
}
// faceUVWAxes are the U, V, and W axes for each face.
var faceUVWAxes = [6][3]Point{
{Point{r3.Vector{0, 1, 0}}, Point{r3.Vector{0, 0, 1}}, Point{r3.Vector{1, 0, 0}}},
{Point{r3.Vector{-1, 0, 0}}, Point{r3.Vector{0, 0, 1}}, Point{r3.Vector{0, 1, 0}}},
{Point{r3.Vector{-1, 0, 0}}, Point{r3.Vector{0, -1, 0}}, Point{r3.Vector{0, 0, 1}}},
{Point{r3.Vector{0, 0, -1}}, Point{r3.Vector{0, -1, 0}}, Point{r3.Vector{-1, 0, 0}}},
{Point{r3.Vector{0, 0, -1}}, Point{r3.Vector{1, 0, 0}}, Point{r3.Vector{0, -1, 0}}},
{Point{r3.Vector{0, 1, 0}}, Point{r3.Vector{1, 0, 0}}, Point{r3.Vector{0, 0, -1}}},
}
// faceUVWFaces are the precomputed neighbors of each face.
var faceUVWFaces = [6][3][2]int{
{{4, 1}, {5, 2}, {3, 0}},
{{0, 3}, {5, 2}, {4, 1}},
{{0, 3}, {1, 4}, {5, 2}},
{{2, 5}, {1, 4}, {0, 3}},
{{2, 5}, {3, 0}, {1, 4}},
{{4, 1}, {3, 0}, {2, 5}},
}
// uvwAxis returns the given axis of the given face.
func uvwAxis(face, axis int) Point {
return faceUVWAxes[face][axis]
}
// uvwFaces returns the face in the (u,v,w) coordinate system on the given axis
// in the given direction.
func uvwFace(face, axis, direction int) int {
return faceUVWFaces[face][axis][direction]
}
// uAxis returns the u-axis for the given face.
func uAxis(face int) Point {
return uvwAxis(face, 0)
}
// vAxis returns the v-axis for the given face.
func vAxis(face int) Point {
return uvwAxis(face, 1)
}
// Return the unit-length normal for the given face.
func unitNorm(face int) Point {
return uvwAxis(face, 2)
}

View File

@ -1,125 +0,0 @@
// 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
import "github.com/golang/geo/s1"
// roundAngle returns the value rounded to nearest as an int32.
// This does not match C++ exactly for the case of x.5.
func roundAngle(val s1.Angle) int32 {
if val < 0 {
return int32(val - 0.5)
}
return int32(val + 0.5)
}
// minAngle returns the smallest of the given values.
func minAngle(x s1.Angle, others ...s1.Angle) s1.Angle {
min := x
for _, y := range others {
if y < min {
min = y
}
}
return min
}
// maxAngle returns the largest of the given values.
func maxAngle(x s1.Angle, others ...s1.Angle) s1.Angle {
max := x
for _, y := range others {
if y > max {
max = y
}
}
return max
}
// minChordAngle returns the smallest of the given values.
func minChordAngle(x s1.ChordAngle, others ...s1.ChordAngle) s1.ChordAngle {
min := x
for _, y := range others {
if y < min {
min = y
}
}
return min
}
// maxChordAngle returns the largest of the given values.
func maxChordAngle(x s1.ChordAngle, others ...s1.ChordAngle) s1.ChordAngle {
max := x
for _, y := range others {
if y > max {
max = y
}
}
return max
}
// minFloat64 returns the smallest of the given values.
func minFloat64(x float64, others ...float64) float64 {
min := x
for _, y := range others {
if y < min {
min = y
}
}
return min
}
// maxFloat64 returns the largest of the given values.
func maxFloat64(x float64, others ...float64) float64 {
max := x
for _, y := range others {
if y > max {
max = y
}
}
return max
}
// minInt returns the smallest of the given values.
func minInt(x int, others ...int) int {
min := x
for _, y := range others {
if y < min {
min = y
}
}
return min
}
// maxInt returns the largest of the given values.
func maxInt(x int, others ...int) int {
max := x
for _, y := range others {
if y > max {
max = y
}
}
return max
}
// clampInt returns the number closest to x within the range min..max.
func clampInt(x, min, max int) int {
if x < min {
return min
}
if x > max {
return max
}
return x
}

View File

@ -1,97 +0,0 @@
// 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
// WedgeRel enumerates the possible relation between two wedges A and B.
type WedgeRel int
// Define the different possible relationships between two wedges.
//
// Given an edge chain (x0, x1, x2), the wedge at x1 is the region to the
// left of the edges. More precisely, it is the set of all rays from x1x0
// (inclusive) to x1x2 (exclusive) in the *clockwise* direction.
const (
WedgeEquals WedgeRel = iota // A and B are equal.
WedgeProperlyContains // A is a strict superset of B.
WedgeIsProperlyContained // A is a strict subset of B.
WedgeProperlyOverlaps // A-B, B-A, and A intersect B are non-empty.
WedgeIsDisjoint // A and B are disjoint.
)
// WedgeRelation reports the relation between two non-empty wedges
// A=(a0, ab1, a2) and B=(b0, ab1, b2).
func WedgeRelation(a0, ab1, a2, b0, b2 Point) WedgeRel {
// There are 6 possible edge orderings at a shared vertex (all
// of these orderings are circular, i.e. abcd == bcda):
//
// (1) a2 b2 b0 a0: A contains B
// (2) a2 a0 b0 b2: B contains A
// (3) a2 a0 b2 b0: A and B are disjoint
// (4) a2 b0 a0 b2: A and B intersect in one wedge
// (5) a2 b2 a0 b0: A and B intersect in one wedge
// (6) a2 b0 b2 a0: A and B intersect in two wedges
//
// We do not distinguish between 4, 5, and 6.
// We pay extra attention when some of the edges overlap. When edges
// overlap, several of these orderings can be satisfied, and we take
// the most specific.
if a0 == b0 && a2 == b2 {
return WedgeEquals
}
// Cases 1, 2, 5, and 6
if OrderedCCW(a0, a2, b2, ab1) {
// The cases with this vertex ordering are 1, 5, and 6,
if OrderedCCW(b2, b0, a0, ab1) {
return WedgeProperlyContains
}
// We are in case 5 or 6, or case 2 if a2 == b2.
if a2 == b2 {
return WedgeIsProperlyContained
}
return WedgeProperlyOverlaps
}
// We are in case 2, 3, or 4.
if OrderedCCW(a0, b0, b2, ab1) {
return WedgeIsProperlyContained
}
if OrderedCCW(a0, b0, a2, ab1) {
return WedgeIsDisjoint
}
return WedgeProperlyOverlaps
}
// WedgeContains reports whether non-empty wedge A=(a0, ab1, a2) contains B=(b0, ab1, b2).
// Equivalent to WedgeRelation == WedgeProperlyContains || WedgeEquals.
func WedgeContains(a0, ab1, a2, b0, b2 Point) bool {
// For A to contain B (where each loop interior is defined to be its left
// side), the CCW edge order around ab1 must be a2 b2 b0 a0. We split
// this test into two parts that test three vertices each.
return OrderedCCW(a2, b2, b0, ab1) && OrderedCCW(b0, a0, a2, ab1)
}
// WedgeIntersects reports whether non-empty wedge A=(a0, ab1, a2) intersects B=(b0, ab1, b2).
// Equivalent but faster than WedgeRelation != WedgeIsDisjoint
func WedgeIntersects(a0, ab1, a2, b0, b2 Point) bool {
// For A not to intersect B (where each loop interior is defined to be
// its left side), the CCW edge order around ab1 must be a0 b2 b0 a2.
// Note that it's important to write these conditions as negatives
// (!OrderedCCW(a,b,c,o) rather than Ordered(c,b,a,o)) to get correct
// results when two vertices are the same.
return !(OrderedCCW(a0, b2, b0, ab1) && OrderedCCW(b0, a2, a0, ab1))
}