gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

cell.go (24880B)


      1 // Copyright 2014 Google Inc. All rights reserved.
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //     http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 package s2
     16 
     17 import (
     18 	"io"
     19 	"math"
     20 
     21 	"github.com/golang/geo/r1"
     22 	"github.com/golang/geo/r2"
     23 	"github.com/golang/geo/r3"
     24 	"github.com/golang/geo/s1"
     25 )
     26 
     27 // Cell is an S2 region object that represents a cell. Unlike CellIDs,
     28 // it supports efficient containment and intersection tests. However, it is
     29 // also a more expensive representation.
     30 type Cell struct {
     31 	face        int8
     32 	level       int8
     33 	orientation int8
     34 	id          CellID
     35 	uv          r2.Rect
     36 }
     37 
     38 // CellFromCellID constructs a Cell corresponding to the given CellID.
     39 func CellFromCellID(id CellID) Cell {
     40 	c := Cell{}
     41 	c.id = id
     42 	f, i, j, o := c.id.faceIJOrientation()
     43 	c.face = int8(f)
     44 	c.level = int8(c.id.Level())
     45 	c.orientation = int8(o)
     46 	c.uv = ijLevelToBoundUV(i, j, int(c.level))
     47 	return c
     48 }
     49 
     50 // CellFromPoint constructs a cell for the given Point.
     51 func CellFromPoint(p Point) Cell {
     52 	return CellFromCellID(cellIDFromPoint(p))
     53 }
     54 
     55 // CellFromLatLng constructs a cell for the given LatLng.
     56 func CellFromLatLng(ll LatLng) Cell {
     57 	return CellFromCellID(CellIDFromLatLng(ll))
     58 }
     59 
     60 // Face returns the face this cell is on.
     61 func (c Cell) Face() int {
     62 	return int(c.face)
     63 }
     64 
     65 // oppositeFace returns the face opposite the given face.
     66 func oppositeFace(face int) int {
     67 	return (face + 3) % 6
     68 }
     69 
     70 // Level returns the level of this cell.
     71 func (c Cell) Level() int {
     72 	return int(c.level)
     73 }
     74 
     75 // ID returns the CellID this cell represents.
     76 func (c Cell) ID() CellID {
     77 	return c.id
     78 }
     79 
     80 // IsLeaf returns whether this Cell is a leaf or not.
     81 func (c Cell) IsLeaf() bool {
     82 	return c.level == maxLevel
     83 }
     84 
     85 // SizeIJ returns the edge length of this cell in (i,j)-space.
     86 func (c Cell) SizeIJ() int {
     87 	return sizeIJ(int(c.level))
     88 }
     89 
     90 // SizeST returns the edge length of this cell in (s,t)-space.
     91 func (c Cell) SizeST() float64 {
     92 	return c.id.sizeST(int(c.level))
     93 }
     94 
     95 // Vertex returns the k-th vertex of the cell (k = 0,1,2,3) in CCW order
     96 // (lower left, lower right, upper right, upper left in the UV plane).
     97 func (c Cell) Vertex(k int) Point {
     98 	return Point{faceUVToXYZ(int(c.face), c.uv.Vertices()[k].X, c.uv.Vertices()[k].Y).Normalize()}
     99 }
    100 
    101 // Edge returns the inward-facing normal of the great circle passing through
    102 // the CCW ordered edge from vertex k to vertex k+1 (mod 4) (for k = 0,1,2,3).
    103 func (c Cell) Edge(k int) Point {
    104 	switch k {
    105 	case 0:
    106 		return Point{vNorm(int(c.face), c.uv.Y.Lo).Normalize()} // Bottom
    107 	case 1:
    108 		return Point{uNorm(int(c.face), c.uv.X.Hi).Normalize()} // Right
    109 	case 2:
    110 		return Point{vNorm(int(c.face), c.uv.Y.Hi).Mul(-1.0).Normalize()} // Top
    111 	default:
    112 		return Point{uNorm(int(c.face), c.uv.X.Lo).Mul(-1.0).Normalize()} // Left
    113 	}
    114 }
    115 
    116 // BoundUV returns the bounds of this cell in (u,v)-space.
    117 func (c Cell) BoundUV() r2.Rect {
    118 	return c.uv
    119 }
    120 
    121 // Center returns the direction vector corresponding to the center in
    122 // (s,t)-space of the given cell. This is the point at which the cell is
    123 // divided into four subcells; it is not necessarily the centroid of the
    124 // cell in (u,v)-space or (x,y,z)-space
    125 func (c Cell) Center() Point {
    126 	return Point{c.id.rawPoint().Normalize()}
    127 }
    128 
    129 // Children returns the four direct children of this cell in traversal order
    130 // and returns true. If this is a leaf cell, or the children could not be created,
    131 // false is returned.
    132 // The C++ method is called Subdivide.
    133 func (c Cell) Children() ([4]Cell, bool) {
    134 	var children [4]Cell
    135 
    136 	if c.id.IsLeaf() {
    137 		return children, false
    138 	}
    139 
    140 	// Compute the cell midpoint in uv-space.
    141 	uvMid := c.id.centerUV()
    142 
    143 	// Create four children with the appropriate bounds.
    144 	cid := c.id.ChildBegin()
    145 	for pos := 0; pos < 4; pos++ {
    146 		children[pos] = Cell{
    147 			face:        c.face,
    148 			level:       c.level + 1,
    149 			orientation: c.orientation ^ int8(posToOrientation[pos]),
    150 			id:          cid,
    151 		}
    152 
    153 		// We want to split the cell in half in u and v. To decide which
    154 		// side to set equal to the midpoint value, we look at cell's (i,j)
    155 		// position within its parent. The index for i is in bit 1 of ij.
    156 		ij := posToIJ[c.orientation][pos]
    157 		i := ij >> 1
    158 		j := ij & 1
    159 		if i == 1 {
    160 			children[pos].uv.X.Hi = c.uv.X.Hi
    161 			children[pos].uv.X.Lo = uvMid.X
    162 		} else {
    163 			children[pos].uv.X.Lo = c.uv.X.Lo
    164 			children[pos].uv.X.Hi = uvMid.X
    165 		}
    166 		if j == 1 {
    167 			children[pos].uv.Y.Hi = c.uv.Y.Hi
    168 			children[pos].uv.Y.Lo = uvMid.Y
    169 		} else {
    170 			children[pos].uv.Y.Lo = c.uv.Y.Lo
    171 			children[pos].uv.Y.Hi = uvMid.Y
    172 		}
    173 		cid = cid.Next()
    174 	}
    175 	return children, true
    176 }
    177 
    178 // ExactArea returns the area of this cell as accurately as possible.
    179 func (c Cell) ExactArea() float64 {
    180 	v0, v1, v2, v3 := c.Vertex(0), c.Vertex(1), c.Vertex(2), c.Vertex(3)
    181 	return PointArea(v0, v1, v2) + PointArea(v0, v2, v3)
    182 }
    183 
    184 // ApproxArea returns the approximate area of this cell. This method is accurate
    185 // to within 3% percent for all cell sizes and accurate to within 0.1% for cells
    186 // at level 5 or higher (i.e. squares 350km to a side or smaller on the Earth's
    187 // surface). It is moderately cheap to compute.
    188 func (c Cell) ApproxArea() float64 {
    189 	// All cells at the first two levels have the same area.
    190 	if c.level < 2 {
    191 		return c.AverageArea()
    192 	}
    193 
    194 	// First, compute the approximate area of the cell when projected
    195 	// perpendicular to its normal. The cross product of its diagonals gives
    196 	// the normal, and the length of the normal is twice the projected area.
    197 	flatArea := 0.5 * (c.Vertex(2).Sub(c.Vertex(0).Vector).
    198 		Cross(c.Vertex(3).Sub(c.Vertex(1).Vector)).Norm())
    199 
    200 	// Now, compensate for the curvature of the cell surface by pretending
    201 	// that the cell is shaped like a spherical cap. The ratio of the
    202 	// area of a spherical cap to the area of its projected disc turns out
    203 	// to be 2 / (1 + sqrt(1 - r*r)) where r is the radius of the disc.
    204 	// For example, when r=0 the ratio is 1, and when r=1 the ratio is 2.
    205 	// Here we set Pi*r*r == flatArea to find the equivalent disc.
    206 	return flatArea * 2 / (1 + math.Sqrt(1-math.Min(1/math.Pi*flatArea, 1)))
    207 }
    208 
    209 // AverageArea returns the average area of cells at the level of this cell.
    210 // This is accurate to within a factor of 1.7.
    211 func (c Cell) AverageArea() float64 {
    212 	return AvgAreaMetric.Value(int(c.level))
    213 }
    214 
    215 // IntersectsCell reports whether the intersection of this cell and the other cell is not nil.
    216 func (c Cell) IntersectsCell(oc Cell) bool {
    217 	return c.id.Intersects(oc.id)
    218 }
    219 
    220 // ContainsCell reports whether this cell contains the other cell.
    221 func (c Cell) ContainsCell(oc Cell) bool {
    222 	return c.id.Contains(oc.id)
    223 }
    224 
    225 // CellUnionBound computes a covering of the Cell.
    226 func (c Cell) CellUnionBound() []CellID {
    227 	return c.CapBound().CellUnionBound()
    228 }
    229 
    230 // latitude returns the latitude of the cell vertex in radians given by (i,j),
    231 // where i and j indicate the Hi (1) or Lo (0) corner.
    232 func (c Cell) latitude(i, j int) float64 {
    233 	var u, v float64
    234 	switch {
    235 	case i == 0 && j == 0:
    236 		u = c.uv.X.Lo
    237 		v = c.uv.Y.Lo
    238 	case i == 0 && j == 1:
    239 		u = c.uv.X.Lo
    240 		v = c.uv.Y.Hi
    241 	case i == 1 && j == 0:
    242 		u = c.uv.X.Hi
    243 		v = c.uv.Y.Lo
    244 	case i == 1 && j == 1:
    245 		u = c.uv.X.Hi
    246 		v = c.uv.Y.Hi
    247 	default:
    248 		panic("i and/or j is out of bounds")
    249 	}
    250 	return latitude(Point{faceUVToXYZ(int(c.face), u, v)}).Radians()
    251 }
    252 
    253 // longitude returns the longitude of the cell vertex in radians given by (i,j),
    254 // where i and j indicate the Hi (1) or Lo (0) corner.
    255 func (c Cell) longitude(i, j int) float64 {
    256 	var u, v float64
    257 	switch {
    258 	case i == 0 && j == 0:
    259 		u = c.uv.X.Lo
    260 		v = c.uv.Y.Lo
    261 	case i == 0 && j == 1:
    262 		u = c.uv.X.Lo
    263 		v = c.uv.Y.Hi
    264 	case i == 1 && j == 0:
    265 		u = c.uv.X.Hi
    266 		v = c.uv.Y.Lo
    267 	case i == 1 && j == 1:
    268 		u = c.uv.X.Hi
    269 		v = c.uv.Y.Hi
    270 	default:
    271 		panic("i and/or j is out of bounds")
    272 	}
    273 	return longitude(Point{faceUVToXYZ(int(c.face), u, v)}).Radians()
    274 }
    275 
    276 var (
    277 	poleMinLat = math.Asin(math.Sqrt(1.0/3)) - 0.5*dblEpsilon
    278 )
    279 
    280 // RectBound returns the bounding rectangle of this cell.
    281 func (c Cell) RectBound() Rect {
    282 	if c.level > 0 {
    283 		// Except for cells at level 0, the latitude and longitude extremes are
    284 		// attained at the vertices.  Furthermore, the latitude range is
    285 		// determined by one pair of diagonally opposite vertices and the
    286 		// longitude range is determined by the other pair.
    287 		//
    288 		// We first determine which corner (i,j) of the cell has the largest
    289 		// absolute latitude.  To maximize latitude, we want to find the point in
    290 		// the cell that has the largest absolute z-coordinate and the smallest
    291 		// absolute x- and y-coordinates.  To do this we look at each coordinate
    292 		// (u and v), and determine whether we want to minimize or maximize that
    293 		// coordinate based on the axis direction and the cell's (u,v) quadrant.
    294 		u := c.uv.X.Lo + c.uv.X.Hi
    295 		v := c.uv.Y.Lo + c.uv.Y.Hi
    296 		var i, j int
    297 		if uAxis(int(c.face)).Z == 0 {
    298 			if u < 0 {
    299 				i = 1
    300 			}
    301 		} else if u > 0 {
    302 			i = 1
    303 		}
    304 		if vAxis(int(c.face)).Z == 0 {
    305 			if v < 0 {
    306 				j = 1
    307 			}
    308 		} else if v > 0 {
    309 			j = 1
    310 		}
    311 		lat := r1.IntervalFromPoint(c.latitude(i, j)).AddPoint(c.latitude(1-i, 1-j))
    312 		lng := s1.EmptyInterval().AddPoint(c.longitude(i, 1-j)).AddPoint(c.longitude(1-i, j))
    313 
    314 		// We grow the bounds slightly to make sure that the bounding rectangle
    315 		// contains LatLngFromPoint(P) for any point P inside the loop L defined by the
    316 		// four *normalized* vertices.  Note that normalization of a vector can
    317 		// change its direction by up to 0.5 * dblEpsilon radians, and it is not
    318 		// enough just to add Normalize calls to the code above because the
    319 		// latitude/longitude ranges are not necessarily determined by diagonally
    320 		// opposite vertex pairs after normalization.
    321 		//
    322 		// We would like to bound the amount by which the latitude/longitude of a
    323 		// contained point P can exceed the bounds computed above.  In the case of
    324 		// longitude, the normalization error can change the direction of rounding
    325 		// leading to a maximum difference in longitude of 2 * dblEpsilon.  In
    326 		// the case of latitude, the normalization error can shift the latitude by
    327 		// up to 0.5 * dblEpsilon and the other sources of error can cause the
    328 		// two latitudes to differ by up to another 1.5 * dblEpsilon, which also
    329 		// leads to a maximum difference of 2 * dblEpsilon.
    330 		return Rect{lat, lng}.expanded(LatLng{s1.Angle(2 * dblEpsilon), s1.Angle(2 * dblEpsilon)}).PolarClosure()
    331 	}
    332 
    333 	// The 4 cells around the equator extend to +/-45 degrees latitude at the
    334 	// midpoints of their top and bottom edges.  The two cells covering the
    335 	// poles extend down to +/-35.26 degrees at their vertices.  The maximum
    336 	// error in this calculation is 0.5 * dblEpsilon.
    337 	var bound Rect
    338 	switch c.face {
    339 	case 0:
    340 		bound = Rect{r1.Interval{-math.Pi / 4, math.Pi / 4}, s1.Interval{-math.Pi / 4, math.Pi / 4}}
    341 	case 1:
    342 		bound = Rect{r1.Interval{-math.Pi / 4, math.Pi / 4}, s1.Interval{math.Pi / 4, 3 * math.Pi / 4}}
    343 	case 2:
    344 		bound = Rect{r1.Interval{poleMinLat, math.Pi / 2}, s1.FullInterval()}
    345 	case 3:
    346 		bound = Rect{r1.Interval{-math.Pi / 4, math.Pi / 4}, s1.Interval{3 * math.Pi / 4, -3 * math.Pi / 4}}
    347 	case 4:
    348 		bound = Rect{r1.Interval{-math.Pi / 4, math.Pi / 4}, s1.Interval{-3 * math.Pi / 4, -math.Pi / 4}}
    349 	default:
    350 		bound = Rect{r1.Interval{-math.Pi / 2, -poleMinLat}, s1.FullInterval()}
    351 	}
    352 
    353 	// Finally, we expand the bound to account for the error when a point P is
    354 	// converted to an LatLng to test for containment. (The bound should be
    355 	// large enough so that it contains the computed LatLng of any contained
    356 	// point, not just the infinite-precision version.) We don't need to expand
    357 	// longitude because longitude is calculated via a single call to math.Atan2,
    358 	// which is guaranteed to be semi-monotonic.
    359 	return bound.expanded(LatLng{s1.Angle(dblEpsilon), s1.Angle(0)})
    360 }
    361 
    362 // CapBound returns the bounding cap of this cell.
    363 func (c Cell) CapBound() Cap {
    364 	// We use the cell center in (u,v)-space as the cap axis.  This vector is very close
    365 	// to GetCenter() and faster to compute.  Neither one of these vectors yields the
    366 	// bounding cap with minimal surface area, but they are both pretty close.
    367 	cap := CapFromPoint(Point{faceUVToXYZ(int(c.face), c.uv.Center().X, c.uv.Center().Y).Normalize()})
    368 	for k := 0; k < 4; k++ {
    369 		cap = cap.AddPoint(c.Vertex(k))
    370 	}
    371 	return cap
    372 }
    373 
    374 // ContainsPoint reports whether this cell contains the given point. Note that
    375 // unlike Loop/Polygon, a Cell is considered to be a closed set. This means
    376 // that a point on a Cell's edge or vertex belong to the Cell and the relevant
    377 // adjacent Cells too.
    378 //
    379 // If you want every point to be contained by exactly one Cell,
    380 // you will need to convert the Cell to a Loop.
    381 func (c Cell) ContainsPoint(p Point) bool {
    382 	var uv r2.Point
    383 	var ok bool
    384 	if uv.X, uv.Y, ok = faceXYZToUV(int(c.face), p); !ok {
    385 		return false
    386 	}
    387 
    388 	// Expand the (u,v) bound to ensure that
    389 	//
    390 	//   CellFromPoint(p).ContainsPoint(p)
    391 	//
    392 	// is always true. To do this, we need to account for the error when
    393 	// converting from (u,v) coordinates to (s,t) coordinates. In the
    394 	// normal case the total error is at most dblEpsilon.
    395 	return c.uv.ExpandedByMargin(dblEpsilon).ContainsPoint(uv)
    396 }
    397 
    398 // Encode encodes the Cell.
    399 func (c Cell) Encode(w io.Writer) error {
    400 	e := &encoder{w: w}
    401 	c.encode(e)
    402 	return e.err
    403 }
    404 
    405 func (c Cell) encode(e *encoder) {
    406 	c.id.encode(e)
    407 }
    408 
    409 // Decode decodes the Cell.
    410 func (c *Cell) Decode(r io.Reader) error {
    411 	d := &decoder{r: asByteReader(r)}
    412 	c.decode(d)
    413 	return d.err
    414 }
    415 
    416 func (c *Cell) decode(d *decoder) {
    417 	c.id.decode(d)
    418 	*c = CellFromCellID(c.id)
    419 }
    420 
    421 // vertexChordDist2 returns the squared chord distance from point P to the
    422 // given corner vertex specified by the Hi or Lo values of each.
    423 func (c Cell) vertexChordDist2(p Point, xHi, yHi bool) s1.ChordAngle {
    424 	x := c.uv.X.Lo
    425 	y := c.uv.Y.Lo
    426 	if xHi {
    427 		x = c.uv.X.Hi
    428 	}
    429 	if yHi {
    430 		y = c.uv.Y.Hi
    431 	}
    432 
    433 	return ChordAngleBetweenPoints(p, PointFromCoords(x, y, 1))
    434 }
    435 
    436 // uEdgeIsClosest reports whether a point P is closer to the interior of the specified
    437 // Cell edge (either the lower or upper edge of the Cell) or to the endpoints.
    438 func (c Cell) uEdgeIsClosest(p Point, vHi bool) bool {
    439 	u0 := c.uv.X.Lo
    440 	u1 := c.uv.X.Hi
    441 	v := c.uv.Y.Lo
    442 	if vHi {
    443 		v = c.uv.Y.Hi
    444 	}
    445 	// These are the normals to the planes that are perpendicular to the edge
    446 	// and pass through one of its two endpoints.
    447 	dir0 := r3.Vector{v*v + 1, -u0 * v, -u0}
    448 	dir1 := r3.Vector{v*v + 1, -u1 * v, -u1}
    449 	return p.Dot(dir0) > 0 && p.Dot(dir1) < 0
    450 }
    451 
    452 // vEdgeIsClosest reports whether a point P is closer to the interior of the specified
    453 // Cell edge (either the right or left edge of the Cell) or to the endpoints.
    454 func (c Cell) vEdgeIsClosest(p Point, uHi bool) bool {
    455 	v0 := c.uv.Y.Lo
    456 	v1 := c.uv.Y.Hi
    457 	u := c.uv.X.Lo
    458 	if uHi {
    459 		u = c.uv.X.Hi
    460 	}
    461 	dir0 := r3.Vector{-u * v0, u*u + 1, -v0}
    462 	dir1 := r3.Vector{-u * v1, u*u + 1, -v1}
    463 	return p.Dot(dir0) > 0 && p.Dot(dir1) < 0
    464 }
    465 
    466 // edgeDistance reports the distance from a Point P to a given Cell edge. The point
    467 // P is given by its dot product, and the uv edge by its normal in the
    468 // given coordinate value.
    469 func edgeDistance(ij, uv float64) s1.ChordAngle {
    470 	// Let P by the target point and let R be the closest point on the given
    471 	// edge AB.  The desired distance PR can be expressed as PR^2 = PQ^2 + QR^2
    472 	// where Q is the point P projected onto the plane through the great circle
    473 	// through AB.  We can compute the distance PQ^2 perpendicular to the plane
    474 	// from "dirIJ" (the dot product of the target point P with the edge
    475 	// normal) and the squared length the edge normal (1 + uv**2).
    476 	pq2 := (ij * ij) / (1 + uv*uv)
    477 
    478 	// We can compute the distance QR as (1 - OQ) where O is the sphere origin,
    479 	// and we can compute OQ^2 = 1 - PQ^2 using the Pythagorean theorem.
    480 	// (This calculation loses accuracy as angle POQ approaches Pi/2.)
    481 	qr := 1 - math.Sqrt(1-pq2)
    482 	return s1.ChordAngleFromSquaredLength(pq2 + qr*qr)
    483 }
    484 
    485 // distanceInternal reports the distance from the given point to the interior of
    486 // the cell if toInterior is true or to the boundary of the cell otherwise.
    487 func (c Cell) distanceInternal(targetXYZ Point, toInterior bool) s1.ChordAngle {
    488 	// All calculations are done in the (u,v,w) coordinates of this cell's face.
    489 	target := faceXYZtoUVW(int(c.face), targetXYZ)
    490 
    491 	// Compute dot products with all four upward or rightward-facing edge
    492 	// normals. dirIJ is the dot product for the edge corresponding to axis
    493 	// I, endpoint J. For example, dir01 is the right edge of the Cell
    494 	// (corresponding to the upper endpoint of the u-axis).
    495 	dir00 := target.X - target.Z*c.uv.X.Lo
    496 	dir01 := target.X - target.Z*c.uv.X.Hi
    497 	dir10 := target.Y - target.Z*c.uv.Y.Lo
    498 	dir11 := target.Y - target.Z*c.uv.Y.Hi
    499 	inside := true
    500 	if dir00 < 0 {
    501 		inside = false // Target is to the left of the cell
    502 		if c.vEdgeIsClosest(target, false) {
    503 			return edgeDistance(-dir00, c.uv.X.Lo)
    504 		}
    505 	}
    506 	if dir01 > 0 {
    507 		inside = false // Target is to the right of the cell
    508 		if c.vEdgeIsClosest(target, true) {
    509 			return edgeDistance(dir01, c.uv.X.Hi)
    510 		}
    511 	}
    512 	if dir10 < 0 {
    513 		inside = false // Target is below the cell
    514 		if c.uEdgeIsClosest(target, false) {
    515 			return edgeDistance(-dir10, c.uv.Y.Lo)
    516 		}
    517 	}
    518 	if dir11 > 0 {
    519 		inside = false // Target is above the cell
    520 		if c.uEdgeIsClosest(target, true) {
    521 			return edgeDistance(dir11, c.uv.Y.Hi)
    522 		}
    523 	}
    524 	if inside {
    525 		if toInterior {
    526 			return s1.ChordAngle(0)
    527 		}
    528 		// Although you might think of Cells as rectangles, they are actually
    529 		// arbitrary quadrilaterals after they are projected onto the sphere.
    530 		// Therefore the simplest approach is just to find the minimum distance to
    531 		// any of the four edges.
    532 		return minChordAngle(edgeDistance(-dir00, c.uv.X.Lo),
    533 			edgeDistance(dir01, c.uv.X.Hi),
    534 			edgeDistance(-dir10, c.uv.Y.Lo),
    535 			edgeDistance(dir11, c.uv.Y.Hi))
    536 	}
    537 
    538 	// Otherwise, the closest point is one of the four cell vertices. Note that
    539 	// it is *not* trivial to narrow down the candidates based on the edge sign
    540 	// tests above, because (1) the edges don't meet at right angles and (2)
    541 	// there are points on the far side of the sphere that are both above *and*
    542 	// below the cell, etc.
    543 	return minChordAngle(c.vertexChordDist2(target, false, false),
    544 		c.vertexChordDist2(target, true, false),
    545 		c.vertexChordDist2(target, false, true),
    546 		c.vertexChordDist2(target, true, true))
    547 }
    548 
    549 // Distance reports the distance from the cell to the given point. Returns zero if
    550 // the point is inside the cell.
    551 func (c Cell) Distance(target Point) s1.ChordAngle {
    552 	return c.distanceInternal(target, true)
    553 }
    554 
    555 // MaxDistance reports the maximum distance from the cell (including its interior) to the
    556 // given point.
    557 func (c Cell) MaxDistance(target Point) s1.ChordAngle {
    558 	// First check the 4 cell vertices.  If all are within the hemisphere
    559 	// centered around target, the max distance will be to one of these vertices.
    560 	targetUVW := faceXYZtoUVW(int(c.face), target)
    561 	maxDist := maxChordAngle(c.vertexChordDist2(targetUVW, false, false),
    562 		c.vertexChordDist2(targetUVW, true, false),
    563 		c.vertexChordDist2(targetUVW, false, true),
    564 		c.vertexChordDist2(targetUVW, true, true))
    565 
    566 	if maxDist <= s1.RightChordAngle {
    567 		return maxDist
    568 	}
    569 
    570 	// Otherwise, find the minimum distance dMin to the antipodal point and the
    571 	// maximum distance will be pi - dMin.
    572 	return s1.StraightChordAngle - c.BoundaryDistance(Point{target.Mul(-1)})
    573 }
    574 
    575 // BoundaryDistance reports the distance from the cell boundary to the given point.
    576 func (c Cell) BoundaryDistance(target Point) s1.ChordAngle {
    577 	return c.distanceInternal(target, false)
    578 }
    579 
    580 // DistanceToEdge returns the minimum distance from the cell to the given edge AB. Returns
    581 // zero if the edge intersects the cell interior.
    582 func (c Cell) DistanceToEdge(a, b Point) s1.ChordAngle {
    583 	// Possible optimizations:
    584 	//  - Currently the (cell vertex, edge endpoint) distances are computed
    585 	//    twice each, and the length of AB is computed 4 times.
    586 	//  - To fix this, refactor GetDistance(target) so that it skips calculating
    587 	//    the distance to each cell vertex. Instead, compute the cell vertices
    588 	//    and distances in this function, and add a low-level UpdateMinDistance
    589 	//    that allows the XA, XB, and AB distances to be passed in.
    590 	//  - It might also be more efficient to do all calculations in UVW-space,
    591 	//    since this would involve transforming 2 points rather than 4.
    592 
    593 	// First, check the minimum distance to the edge endpoints A and B.
    594 	// (This also detects whether either endpoint is inside the cell.)
    595 	minDist := minChordAngle(c.Distance(a), c.Distance(b))
    596 	if minDist == 0 {
    597 		return minDist
    598 	}
    599 
    600 	// Otherwise, check whether the edge crosses the cell boundary.
    601 	crosser := NewChainEdgeCrosser(a, b, c.Vertex(3))
    602 	for i := 0; i < 4; i++ {
    603 		if crosser.ChainCrossingSign(c.Vertex(i)) != DoNotCross {
    604 			return 0
    605 		}
    606 	}
    607 
    608 	// Finally, check whether the minimum distance occurs between a cell vertex
    609 	// and the interior of the edge AB. (Some of this work is redundant, since
    610 	// it also checks the distance to the endpoints A and B again.)
    611 	//
    612 	// Note that we don't need to check the distance from the interior of AB to
    613 	// the interior of a cell edge, because the only way that this distance can
    614 	// be minimal is if the two edges cross (already checked above).
    615 	for i := 0; i < 4; i++ {
    616 		minDist, _ = UpdateMinDistance(c.Vertex(i), a, b, minDist)
    617 	}
    618 	return minDist
    619 }
    620 
    621 // MaxDistanceToEdge returns the maximum distance from the cell (including its interior)
    622 // to the given edge AB.
    623 func (c Cell) MaxDistanceToEdge(a, b Point) s1.ChordAngle {
    624 	// If the maximum distance from both endpoints to the cell is less than π/2
    625 	// then the maximum distance from the edge to the cell is the maximum of the
    626 	// two endpoint distances.
    627 	maxDist := maxChordAngle(c.MaxDistance(a), c.MaxDistance(b))
    628 	if maxDist <= s1.RightChordAngle {
    629 		return maxDist
    630 	}
    631 
    632 	return s1.StraightChordAngle - c.DistanceToEdge(Point{a.Mul(-1)}, Point{b.Mul(-1)})
    633 }
    634 
    635 // DistanceToCell returns the minimum distance from this cell to the given cell.
    636 // It returns zero if one cell contains the other.
    637 func (c Cell) DistanceToCell(target Cell) s1.ChordAngle {
    638 	// If the cells intersect, the distance is zero.  We use the (u,v) ranges
    639 	// rather than CellID intersects so that cells that share a partial edge or
    640 	// corner are considered to intersect.
    641 	if c.face == target.face && c.uv.Intersects(target.uv) {
    642 		return 0
    643 	}
    644 
    645 	// Otherwise, the minimum distance always occurs between a vertex of one
    646 	// cell and an edge of the other cell (including the edge endpoints).  This
    647 	// represents a total of 32 possible (vertex, edge) pairs.
    648 	//
    649 	// TODO(roberts): This could be optimized to be at least 5x faster by pruning
    650 	// the set of possible closest vertex/edge pairs using the faces and (u,v)
    651 	// ranges of both cells.
    652 	var va, vb [4]Point
    653 	for i := 0; i < 4; i++ {
    654 		va[i] = c.Vertex(i)
    655 		vb[i] = target.Vertex(i)
    656 	}
    657 	minDist := s1.InfChordAngle()
    658 	for i := 0; i < 4; i++ {
    659 		for j := 0; j < 4; j++ {
    660 			minDist, _ = UpdateMinDistance(va[i], vb[j], vb[(j+1)&3], minDist)
    661 			minDist, _ = UpdateMinDistance(vb[i], va[j], va[(j+1)&3], minDist)
    662 		}
    663 	}
    664 	return minDist
    665 }
    666 
    667 // MaxDistanceToCell returns the maximum distance from the cell (including its
    668 // interior) to the given target cell.
    669 func (c Cell) MaxDistanceToCell(target Cell) s1.ChordAngle {
    670 	// Need to check the antipodal target for intersection with the cell. If it
    671 	// intersects, the distance is the straight ChordAngle.
    672 	// antipodalUV is the transpose of the original UV, interpreted within the opposite face.
    673 	antipodalUV := r2.Rect{target.uv.Y, target.uv.X}
    674 	if int(c.face) == oppositeFace(int(target.face)) && c.uv.Intersects(antipodalUV) {
    675 		return s1.StraightChordAngle
    676 	}
    677 
    678 	// Otherwise, the maximum distance always occurs between a vertex of one
    679 	// cell and an edge of the other cell (including the edge endpoints).  This
    680 	// represents a total of 32 possible (vertex, edge) pairs.
    681 	//
    682 	// TODO(roberts): When the maximum distance is at most π/2, the maximum is
    683 	// always attained between a pair of vertices, and this could be made much
    684 	// faster by testing each vertex pair once rather than the current 4 times.
    685 	var va, vb [4]Point
    686 	for i := 0; i < 4; i++ {
    687 		va[i] = c.Vertex(i)
    688 		vb[i] = target.Vertex(i)
    689 	}
    690 	maxDist := s1.NegativeChordAngle
    691 	for i := 0; i < 4; i++ {
    692 		for j := 0; j < 4; j++ {
    693 			maxDist, _ = UpdateMaxDistance(va[i], vb[j], vb[(j+1)&3], maxDist)
    694 			maxDist, _ = UpdateMaxDistance(vb[i], va[j], va[(j+1)&3], maxDist)
    695 		}
    696 	}
    697 	return maxDist
    698 }