Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ S2PointRegion | ❌
S2PointVector | ✅
S2Polygon | 🟡
S2Polyline | ✅
S2R2Rect |
S2R2Rect |
S2Region | ✅
S2RegionCoverer | ✅
S2RegionIntersection | ❌
Expand Down
10 changes: 10 additions & 0 deletions r1/interval.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,16 @@ func (i Interval) ClampPoint(p float64) float64 {
return math.Max(i.Lo, math.Min(i.Hi, p))
}

func (i Interval) Project(p float64) float64 {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Document all public methods. You can take inspiration from the cpp implementation at https://github.com/google/s2geometry/blob/ac448e2e939863dfefdeb6500ea74fda92d8187c/src/s2/r1interval.h#L172, but make sure you follow https://go.dev/doc/comment#func.

if p < i.Lo {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's clampInt, add clamp there if #256 doesn't get merged first.

geo/s2/util.go

Line 29 in fd65259

func clampInt(x, lo, hi int) int {

return i.Lo
}
if p > i.Hi {
return i.Hi
}
return p
}

// Expanded returns an interval that has been expanded on each side by margin.
// If margin is negative, then the function shrinks the interval on
// each side by margin instead. The resulting interval may be empty. Any
Expand Down
11 changes: 11 additions & 0 deletions r2/rect.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,13 @@ func (r Rect) Vertices() [4]Point {
}
}

func (r Rect) Vertex(k int) Point {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All public functions should be directly tested.

// Twiddle bits to return the points in CCW order (lower left, lower right,
// upper right, upper left).
j := (k >> 1) & 1
return r.VertexIJ(j^(k&1), j)
}

// VertexIJ returns the vertex in direction i along the X-axis (0=left, 1=right) and
// direction j along the Y-axis (0=down, 1=up).
func (r Rect) VertexIJ(i, j int) Point {
Expand Down Expand Up @@ -210,6 +217,10 @@ func (r Rect) ClampPoint(p Point) Point {
return Point{r.X.ClampPoint(p.X), r.Y.ClampPoint(p.Y)}
}

func (r Rect) Project(p Point) Point {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function doesn't come from C++ as far as I can see.

return Point{r.X.Project(p.X), r.Y.Project(p.Y)}
}

// Expanded returns a rectangle that has been expanded in the x-direction
// by margin.X, and in y-direction by margin.Y. If either margin is empty,
// then shrink the interval on the corresponding sides instead. The resulting
Expand Down
165 changes: 165 additions & 0 deletions s2/r2rect.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
package s2

import "github.com/golang/geo/r2"

// R2Rect represents a closed axis-aligned rectangle in the (x,y) plane.
type R2Rect struct {
r2.Rect
}

// ContainsR2Point reports whether the rectangle contains the given point.
// Rectangles are closed regions, i.e. they contain their boundary.
func (r R2Rect) ContainsR2Point(p r2.Point) bool {
return r.Rect.ContainsPoint(p)
}

// Contains reports whether the rectangle contains the given rectangle.
func (r R2Rect) Contains(other R2Rect) bool {
return r.Rect.Contains(other.Rect)
}

// InteriorContainsPoint returns true iff the given point is contained in the interior
// of the region (i.e. the region excluding its boundary).
func (r R2Rect) InteriorContainsPoint(p r2.Point) bool {
return r.Rect.InteriorContainsPoint(p)
}

// InteriorContains reports whether the interior of this rectangle contains all of the
// points of the given other rectangle (including its boundary).
func (r R2Rect) InteriorContains(other R2Rect) bool {
return r.Rect.InteriorContains(other.Rect)
}

// Intersects reports whether this rectangle and the other rectangle have any points in common.
func (r R2Rect) Intersects(other R2Rect) bool {
return r.Rect.Intersects(other.Rect)
}

// InteriorIntersects reports whether the interior of this rectangle intersects
// any point (including the boundary) of the given other rectangle.
func (r R2Rect) InteriorIntersects(other R2Rect) bool {
return r.Rect.InteriorIntersects(other.Rect)
}

// Expanded returns a rectangle that has been expanded in the x-direction
// by margin.X, and in y-direction by margin.Y. If either margin is empty,
// then shrink the interval on the corresponding sides instead. The resulting
// rectangle may be empty. Any expansion of an empty rectangle remains empty.
func (r R2Rect) Expanded(margin r2.Point) R2Rect {
return R2Rect{r.Rect.Expanded(margin)}
}

// ExpandedByMargin returns a Rect that has been expanded by the amount on all sides.
func (r R2Rect) ExpandedByMargin(margin float64) R2Rect {
return R2Rect{r.Rect.ExpandedByMargin(margin)}
}

// Union returns the smallest rectangle containing the union of this rectangle and
// the given rectangle.
func (r R2Rect) Union(other R2Rect) R2Rect {
return R2Rect{r.Rect.Union(other.Rect)}
}

// Intersection returns the smallest rectangle containing the intersection of this
// rectangle and the given rectangle.
func (r R2Rect) Intersection(other R2Rect) R2Rect {
return R2Rect{r.Rect.Intersection(other.Rect)}
}

// ApproxEqual returns true if the x- and y-intervals of the two rectangles are
// the same up to the given tolerance.
func (r R2Rect) ApproxEqual(other R2Rect) bool {
return r.Rect.ApproxEqual(other.Rect)
}

// AddPoint expands the rectangle to include the given point. The rectangle is
// expanded by the minimum amount possible.
func (r R2Rect) AddPoint(p r2.Point) R2Rect {
return R2Rect{r.Rect.AddPoint(p)}
}

// AddRect expands the rectangle to include the given rectangle. This is the
// same as replacing the rectangle by the union of the two rectangles, but
// is more efficient.
func (r R2Rect) AddRect(other R2Rect) R2Rect {
return R2Rect{r.Rect.AddRect(other.Rect)}
}

// R2RectFromCell constructs a rectangle that corresponds to the boundary of the given cell
// in (s,t)-space. Such rectangles are always a subset of [0,1]x[0,1].
func R2RectFromCell(cell Cell) R2Rect {
size := cell.SizeST()
return R2Rect{r2.RectFromCenterSize(cell.id.centerST(), r2.Point{X: size, Y: size})}
}

// R2RectFromCellID constructs a rectangle that corresponds to the boundary of the given cell ID
// in (s,t)-space. Such rectangles are always a subset of [0,1]x[0,1].
func R2RectFromCellID(id CellID) R2Rect {
size := id.sizeST(id.Level())
return R2Rect{r2.RectFromCenterSize(id.centerST(), r2.Point{X: size, Y: size})}
}

func toPoint(p r2.Point) Point {
return Point{faceUVToXYZ(0, stToUV(p.X), stToUV(p.Y)).Normalize()}
}

// CapBound returns a Cap that bounds this rectangle.
func (r R2Rect) CapBound() Cap {
if r.IsEmpty() {
return EmptyCap()
}

// The rectangle is a convex polygon on the sphere, since it is a subset of
// one cube face. Its bounding cap is also a convex region on the sphere,
// and therefore we can bound the rectangle by just bounding its vertices.
// We use the rectangle's center in (s,t)-space as the cap axis. This
// doesn't yield the minimal cap but it's pretty close.
cap := CapFromPoint(toPoint(r.Center()))
for k := 0; k < 4; k++ {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

range?

cap = cap.AddPoint(toPoint(r.Vertex(k)))
}

return cap
}

// RectBound returns a bounding latitude-longitude rectangle.
// The bounds are not guaranteed to be tight.
func (r R2Rect) RectBound() Rect {
return r.CapBound().RectBound()
}

// CellUnionBound computes a covering of the rectangle. In general the covering
// consists of at most 4 cells except for very large rectangles, which may need
// up to 6 cells. The output is not sorted.
func (r R2Rect) CellUnionBound() []CellID {
return r.CapBound().CellUnionBound()
}

// ContainsPoint reports whether the rectangle contains the given point.
// Rectangles are closed regions, i.e. they contain their boundary.
func (r R2Rect) ContainsPoint(p Point) bool {
if face(p.Vector) != 0 {
return false
}

u, v := validFaceXYZToUV(0, p.Vector)
return r.Rect.ContainsPoint(r2.Point{X: u, Y: v})
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where's the UVtoST call and why is no test failing?

}

// ContainsCell reports whether the rectangle contains the given cell.
func (r R2Rect) ContainsCell(cell Cell) bool {
if cell.Face() != 0 {
return false
}

return r.Rect.Contains(R2RectFromCell(cell).Rect)
}

// MayIntersect reports whether the rectangle may intersect the given cell.
func (r R2Rect) MayIntersect(cell Cell) bool {
if cell.Face() != 0 {
return false
}

return r.Rect.Intersects(R2RectFromCell(cell).Rect)
}
Loading