Skip to content

Sync to latest #101

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 20, 2023
Merged
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
221 changes: 131 additions & 90 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ S2 is a library for spherical geometry that aims to have the same robustness,
flexibility, and performance as the best planar geometry libraries.

This is a library for manipulating geometric shapes. Unlike many geometry
libraries, S2 is primarily designed to work with _spherical geometry_, i.e.,
libraries, S2 is primarily designed to work with *spherical geometry*, i.e.,
shapes drawn on a sphere rather than on a planar 2D map. (In fact, the name S2
is derived from the mathematical notation for the unit sphere *S²*.) This makes
it especially suitable for working with geographic data.
Expand Down Expand Up @@ -92,109 +92,150 @@ This library is principally a port of the
where it makes sense. We detail the progress of this port below relative to that
C++ library.

Legend:

* ✅ - Feature Complete
* 🟡 - Mostly Complete
* ❌ - Not available

## [ℝ¹](https://godoc.org/github.com/golang/geo/r1) - One-dimensional Cartesian coordinates

Full parity with C++.
C++ Type | Go
:--------- | ---
R1Interval | ✅

## [ℝ²](https://godoc.org/github.com/golang/geo/r2) - Two-dimensional Cartesian coordinates

Full parity with C++.
C++ Type | Go
:------- | ---
R2Point | ✅
R2Rect | ✅

## [ℝ³](https://godoc.org/github.com/golang/geo/r3) - Three-dimensional Cartesian coordinates

Full parity with C++.
C++ Type | Go
:------------ | ---
R3Vector | ✅
R3ExactVector | ✅
Matrix3x3 | ✅

## [S¹](https://godoc.org/github.com/golang/geo/s1) - Circular Geometry

Full parity with C++.
C++ Type | Go
:----------- | ---
S1Angle | ✅
S1ChordAngle | ✅
S1Interval | ✅

## [S²](https://godoc.org/github.com/golang/geo/s2) - Spherical Geometry

Approximately ~40% complete.

**Complete** These files have full parity with the C++ implementation.

* Cap
* Cell
* CellID
* CellUnion
* ContainsVertexQuery
* ConvexHullQuery
* CrossingEdgeQuery
* LatLng
* matrix3x3
* Metric
* PaddedCell
* Point
* PointCompression
* Region
* RegionCoverer
* RegionUnion
* s2edge_clipping
* s2edge_crosser
* s2edge_crossings
* s2edge_distances
* edgeVectorShape
* laxLoop
* laxPolyline
* s2projections - Helpers for projecting points between R2 and S2.
* s2rect_bounder
* s2stuv.go (s2coords.h in C++) - This file is a collection of helper and
conversion methods to and from ST-space, UV-space, and XYZ-space.
* s2wedge_relations
* ShapeIndex
* idSetLexicon,sequenceLexicon

**Mostly Complete** Files that have almost all of the features of the original
C++ code, and are reasonably complete enough to use in live code. Up to date
listing of the incomplete methods are documented at the end of each file.

* EdgeQuery/Closest/Furthest - missing Project, GetEdge
* ContainsPointQuery - missing visit edges
* laxPolygon
* Loop - Loop is mostly complete now. Missing Project, Distance, Union, etc.
* Polyline - Missing InitTo... methods, NearlyCoversPolyline
* Rect (AKA s2latlngrect in C++) - Missing Centroid, InteriorContains.
* s2_test.go (AKA s2testing and s2textformat in C++) - Missing Fractal test
shape generation. This file is a collection of testing helper methods.
* s2edge_distances - Missing Intersection

**In Progress** Files that have some work done, but are probably not complete
enough for general use in production code.

* CellIndex - A queryable index of CellIDs.
* Polygon - Polygons with multiple loops are supported. It fully implements
Shape and Region, but it's missing most other methods. (Area, Centroid,
Distance, Projection, Intersection, Union, Contains, Normalized, etc.)
* PolylineSimplifier - Initial work has begun on this.
* s2predicates.go - This file is a collection of helper methods used by other
parts of the library.
* s2shapeutil - Initial elements added. Missing VisitCrossings.

**Not Started Yet.** These files (and their associated unit tests) have
dependencies on most of the In Progress files before they can begin to be
started.

* BooleanOperation - used when assembling polygons and loops.
* Builder - This is a robust tool for creating the various Shape types from
collection of simpler S2 types.
* BuilderClosedSetNormalizer
* BuilderFindPolygonDegneracies
* BuilderGraph
* BuilderLayers
* BuilderSnapFunctions
* BuilderTesting
* Centroids
* ClosestPointQuery
* EdgeTesselator
* LoopMeasures
* PointIndex
* PointRegion
* PointUtil
* PolygonMeasures
* RegionIntersection
* RegionTermIndexer
* ShapeIndexRegion - Allows ShapeIndexes to be used as Regions for things like
### Basic Types

C++ Type | Go
:------------------- | ---
S2Cap | ✅
S2Cell | ✅
S2CellId | ✅
S2CellIdVector | ❌
S2CellIndex | 🟡
S2CellUnion | ✅
S2Coords | ✅
S2DensityTree | ❌
S2DistanceTarget | ✅
S2EdgeVector | ✅
S2LatLng | ✅
S2LatLngRect | ✅
S2LaxLoop | 🟡
S2LaxPolygon | 🟡
S2LaxPolyline | 🟡
S2Loop | ✅
S2PaddedCell | ✅
S2Point | ✅
S2PointIndex | ❌
S2PointSpan | ❌
S2PointRegion | ❌
S2PointVector | ✅
S2Polygon | 🟡
S2Polyline | ✅
S2R2Rect | ❌
S2Region | ✅
S2RegionCoverer | ✅
S2RegionIntersection | ❌
S2RegionUnion | ✅
S2Shape | ✅
S2ShapeIndex | ✅
S2ShapeIndexRegion | ❌
EncodedLaxPolygon | ❌
EncodedLaxPolyline | ❌
EncodedShapeIndex | ❌
EncodedStringVector | ❌
EncodedUintVector | ❌
IdSetLexicon | ❌
ValueSetLexicon | ❌
SequenceLexicon | ❌
LaxClosedPolyline | ❌
VertexIDLaxLoop | ❌

### Query Types

C++ Type | Go
:------------------- | ---
S2ChainInterpolation | ❌
S2ClosestCell | ❌
S2FurthestCell | ❌
S2ClosestEdge | ✅
S2FurthestEdge | ✅
S2ClosestPoint | ❌
S2FurthestPoint | ❌
S2ContainsPoint | ✅
S2ContainsVertex | ✅
S2ConvexHull | ✅
S2CrossingEdge | ✅
S2HausdorffDistance | ❌
S2ShapeNesting | ❌

### Supporting Types

C++ Type | Go
:------------------------------- | ---
S2BooleanOperation | ❌
S2BufferOperation | ❌
S2Builder | ❌
S2BuilderClosedSetNormalizer | ❌
S2BuilderFindPolygonDegeneracies | ❌
S2BuilderGraph | ❌
S2BuilderLayers | ❌
S2BuilderSnapFunctions | ❌
S2BuilderTesting | ❌
S2Builderutil\* | ❌
S2Coder | ❌
S2EdgeClipping | ✅
S2EdgeCrosser | ✅
S2EdgeCrossings | ✅
S2EdgeDistances | ✅
S2EdgeTessellator | ✅
S2LoopMeasures | ❌
S2Measures | ✅
S2MemoryTracker | ❌
S2Metrics | ❌
S2PointUtil | 🟡
S2PolygonBuilder | ❌
S2PolylineAlignment | ❌
S2PolylineMeasures | ✅
S2PolylineSimplifier | ❌
S2Predicates | ✅
S2Projections | ❌
S2rectBounder | ❌
S2RegionTermIndexer | ❌
S2ShapeIndexMeasures | ❌
S2ShapeIndexUtil\* | 🟡
S2ShapeMeasures | ❌
S2ShapeUtil\* | 🟡
S2Stats | ❌
S2Testing | ✅
S2TextFormat | ✅
S2WedgeRelations | ✅
S2WindingOperation | ❌

### Encode/Decode

Expand Down
3 changes: 2 additions & 1 deletion r1/interval.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,8 @@ func (i Interval) ApproxEqual(other Interval) bool {

// DirectedHausdorffDistance returns the Hausdorff distance to the given interval. For two
// intervals x and y, this distance is defined as
// h(x, y) = max_{p in x} min_{q in y} d(p, q).
//
// h(x, y) = max_{p in x} min_{q in y} d(p, q).
func (i Interval) DirectedHausdorffDistance(other Interval) float64 {
if i.IsEmpty() {
return 0
Expand Down
5 changes: 5 additions & 0 deletions r3/precisevector.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,3 +196,8 @@ func (v PreciseVector) SmallestComponent() Axis {
}
return ZAxis
}

// IsZero reports if this vector is exactly 0 efficiently.
func (v PreciseVector) IsZero() bool {
return v.X.Sign() == 0 && v.Y.Sign() == 0 && v.Z.Sign() == 0
}
46 changes: 46 additions & 0 deletions r3/precisevector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -473,3 +473,49 @@ func TestPreciseLargestSmallestComponents(t *testing.T) {
}
}
}

func TestPreciseVectorIsZero(t *testing.T) {

x := NewPreciseVector(1e20, 0, 0)
y := NewPreciseVector(1, 0, 0)
xy := x.Add(y)

tests := []struct {
have PreciseVector
want bool
}{
{
have: NewPreciseVector(0, 0, 0),
want: true,
},
{
// Test with one element being negatively signed 0.
have: NewPreciseVector(0, -0, 0),
want: true,
},
{
// Test a non-zero value.
have: NewPreciseVector(0, 0, 1),
want: false,
},
{
// 1e20+1-1e20 should equal 1. Testing the case where the
// numbers are outside the range a traditional double would
// be able to handle correctly.
have: x.Add(y).Add(x.Mul(precFloat(-1))),
want: false,
},
{
// (1e20+1) - (1e20+1) should be zero. Test the same case
// where a non-representable double minus itself is 0.
have: xy.Sub(xy),
want: true,
},
}

for _, test := range tests {
if got := test.have.IsZero(); got != test.want {
t.Errorf("%s.IsZero() = %v, want %v", test.have, got, test.want)
}
}
}
18 changes: 10 additions & 8 deletions r3/vector.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,16 @@ func (v Vector) Sub(ov Vector) Vector { return Vector{v.X - ov.X, v.Y - ov.Y, v.
func (v Vector) Mul(m float64) Vector { return Vector{m * v.X, m * v.Y, m * v.Z} }

// Dot returns the standard dot product of v and ov.
func (v Vector) Dot(ov Vector) float64 { return v.X*ov.X + v.Y*ov.Y + v.Z*ov.Z }
func (v Vector) Dot(ov Vector) float64 {
return float64(v.X*ov.X) + float64(v.Y*ov.Y) + float64(v.Z*ov.Z)
}

// Cross returns the standard cross product of v and ov.
func (v Vector) Cross(ov Vector) Vector {
return Vector{
v.Y*ov.Z - v.Z*ov.Y,
v.Z*ov.X - v.X*ov.Z,
v.X*ov.Y - v.Y*ov.X,
float64(v.Y*ov.Z) - float64(v.Z*ov.Y),
float64(v.Z*ov.X) - float64(v.X*ov.Z),
float64(v.X*ov.Y) - float64(v.Y*ov.X),
}
}

Expand All @@ -100,7 +102,7 @@ const (
// Ortho returns a unit vector that is orthogonal to v.
// Ortho(-v) = -Ortho(v) for all v.
func (v Vector) Ortho() Vector {
ov := Vector{0.012, 0.0053, 0.00457}
ov := Vector{}
switch v.LargestComponent() {
case XAxis:
ov.Z = 1
Expand Down Expand Up @@ -146,9 +148,9 @@ func (v Vector) SmallestComponent() Axis {

// Cmp compares v and ov lexicographically and returns:
//
// -1 if v < ov
// 0 if v == ov
// +1 if v > ov
// -1 if v < ov
// 0 if v == ov
// +1 if v > ov
//
// This method is based on C++'s std::lexicographical_compare. Two entities
// are compared element by element with the given operator. The first mismatch
Expand Down
17 changes: 17 additions & 0 deletions r3/vector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,23 @@ func TestVectorOrtho(t *testing.T) {
}
}

func TestVectorOrthoAlignment(t *testing.T) {
tests := []struct {
have Vector
want Vector
}{
{have: Vector{1, 0, 0}, want: Vector{0, -1, 0}},
{have: Vector{0, 1, 0}, want: Vector{0, 0, -1}},
{have: Vector{0, 0, 1}, want: Vector{-1, 0, 0}},
}

for _, test := range tests {
if got := test.have.Ortho(); got != test.want {
t.Errorf("%v.Ortho() = %v, want %v", test.have, got, test.want)
}
}
}

func TestVectorIdentities(t *testing.T) {
tests := []struct {
v1, v2 Vector
Expand Down
Loading