Beispiel #1
0
// This is named GetDistance() in the C++ API.
func (x Point) DistanceToEdgeWithNormal(a, b, a_cross_b Point) s1.Angle {
	// There are three cases. If X is located in the spherical wedge
	// defined by A, B, and the axis A x B, then the closest point is on
	// the segment AB. Otherwise the closest point is either A or B; the
	// dividing line between these two cases is the great circle passing
	// through (A x B) and the midpoint of AB.
	if CCW(a_cross_b, a, x) && CCW(x, b, a_cross_b) {
		// The closest point to X lies on the segment AB. We compute
		// the distance to the corresponding great circle. The result
		// is accurate for small distances but not necessarily for
		// large distances (approaching Pi/2).
		//
		// TODO: sanity check a != b
		sin_dist := math.Abs(x.Dot(a_cross_b.Vector)) / a_cross_b.Norm()
		return s1.Angle(math.Asin(math.Min(1.0, sin_dist)))
	}
	// Otherwise, the closest point is either A or B. The cheapest method is
	// just to compute the minimum of the two linear (as opposed to spherical)
	// distances and convert the result to an angle. Again, this method is
	// accurate for small but not large distances (approaching Pi).
	xa := x.Sub(a.Vector).Norm2()
	xb := x.Sub(b.Vector).Norm2()
	linear_dist2 := math.Min(xa, xb)
	return s1.Angle(2 * math.Asin(math.Min(1.0, 0.5*math.Sqrt(linear_dist2))))
}
Beispiel #2
0
// Radius returns the cap's radius.
func (c Cap) Radius() s1.Angle {
	if c.IsEmpty() {
		return s1.Angle(emptyHeight)
	}

	// This could also be computed as acos(1 - height_), but the following
	// formula is much more accurate when the cap height is small. It
	// follows from the relationship h = 1 - cos(r) = 2 sin^2(r/2).
	return s1.Angle(2 * math.Asin(math.Sqrt(0.5*c.height)))
}
Beispiel #3
0
func randomEdgeCrossingCap(maxLengthMeters float64, s2cap Cap) Edge {
	center := samplePoint(s2cap)
	edgeCap := CapFromCenterAngle(center, s1.Angle(maxLengthMeters/kEarthRadiusMeters/2))
	p0 := samplePoint(edgeCap)
	p1 := samplePoint(edgeCap)
	return Edge{p0, p1}
}
Beispiel #4
0
func Perturb(x Point, maxPerturb float64) Point {
	// Perturb "x" randomly within the radius of maxPerturb.
	if maxPerturb == 0 {
		return x
	}
	s2cap := CapFromCenterAngle(Point{x.Normalize()}, s1.Angle(maxPerturb))
	return samplePoint(s2cap)
}
Beispiel #5
0
func EdgeInterpolate(t float64, a, b Point) Point {
	if t == 0 {
		return a
	}
	if t == 1 {
		return b
	}
	ab := a.Angle(b.Vector)
	return EdgeInterpolateAtDistance(s1.Angle(t)*ab, a, b, ab)
}
Beispiel #6
0
func (r Rect) CapBound() Cap {
	// We consider two possible bounding caps, one whose axis passes
	// through the center of the lat-lng 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
	//	var poleAngle s1.Angle
	if r.Lat.Lo+r.Lat.Hi < 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(PointFromCoords(0, 0, poleZ), s1.Angle(poleAngle))

	// 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.
	lngSpan := r.Lng.Hi - r.Lng.Lo
	if math.Remainder(lngSpan, 2*math.Pi) >= 0 {
		if lngSpan < 2*math.Pi {
			midCap := CapFromCenterAngle(PointFromLatLng(r.Center()), s1.Angle(0))
			for k := 0; k < 4; k++ {
				midCap.AddPoint(PointFromLatLng(r.Vertex(k)))
			}
			if midCap.height < poleCap.height {
				return midCap
			}
		}
	}
	return poleCap
}
Beispiel #7
0
// Angle returns the angle between v and ov.
func (v Vector) Angle(ov Vector) s1.Angle {
	return s1.Angle(math.Atan2(v.Cross(ov).Norm(), v.Dot(ov))) * s1.Radian
}
Beispiel #8
0
			lastParent = parent
		}
		count := 0
		for j := 0; j < child.NumVertices(); j++ {
			if _, ok := vertices[*child.vertex(j)]; ok {
				count++
			}
		}
		if count > 1 {
			return false
		}
	}
	return true
}

var intersectionTolerance = s1.Angle(1.5e-15)

func (p *Polygon) InitToIntersection(a, b *Polygon) {
	p.InitToIntersectionSloppy(a, b, intersectionTolerance)
}

func (p *Polygon) InitToIntersectionSloppy(a, b *Polygon, vertexMergeRadius s1.Angle) {
	if !a.bound.Intersects(b.bound) {
		return
	}

	// We want the boundary of A clipped to the interior of B,
	// plus the boundary of B clipped to the interior of A,
	// plus one copy of any directed edges that are in both boundaries.
	options := DIRECTED_XOR()
	options.vertex_merge_radius = vertexMergeRadius
Beispiel #9
0
func GenerateRandomEarthEdges(edgeLengthMetersMax, capSpanMeters float64, numEdges int, edges *[]Edge) {
	s2cap := CapFromCenterAngle(randomPoint(), s1.Angle(capSpanMeters/kEarthRadiusMeters))
	for i := 0; i < numEdges; i++ {
		*edges = append(*edges, randomEdgeCrossingCap(edgeLengthMetersMax, s2cap))
	}
}
Beispiel #10
0
func runTestCase(t *testing.T, test TestCase) bool {
	for iter := 0; iter < 250; iter++ {
		options := PolygonBuilderOptions{
			edge_splice_fraction: .866,
			validate:             false,
			vertex_merge_radius:  s1.Angle(0),
		}
		options.undirected_edges = evalTristate(test.undirectedEdges)
		options.xor_edges = evalTristate(test.xorEdges)
		minMerge := (s1.Angle(test.minMerge) * s1.Degree).Radians()
		maxMerge := (s1.Angle(test.maxMerge) * s1.Degree).Radians()
		minSin := math.Sin((s1.Angle(test.minVertexAngle) * s1.Degree).Radians())

		// Half of the time we allow edges to be split into smaller
		// pieces (up to 5 levels, i.e. up to 32 pieces).
		maxSplits := max(0, rand.Intn(10)-4)
		if !test.canSplit {
			maxSplits = 0
		}

		// We choose randomly among two different values for the edge
		// fraction, just to exercise that code.
		edgeFraction := options.edge_splice_fraction
		var vertexMerge, maxPerturb float64
		if minSin < edgeFraction && oneIn(2) {
			edgeFraction = minSin
		}
		if maxSplits == 0 && oneIn(2) {
			// Turn off edge splicing completely.
			edgeFraction = 0
			vertexMerge = minMerge + smallFraction()*(maxMerge-minMerge)
			maxPerturb = 0.5 * math.Min(vertexMerge-minMerge, maxMerge-vertexMerge)
		} else {
			// Splice edges. These bounds also assume that edges
			// may be split.
			//
			// If edges are actually split, need to bump up the
			// minimum merge radius to ensure that split edges
			// in opposite directions are unified. Otherwise
			// there will be tiny degenerate loops created.
			if maxSplits > 0 {
				minMerge += 1e-15
			}
			minMerge /= edgeFraction
			maxMerge *= minSin
			if maxMerge < minMerge {
				t.Errorf("%v < %v", maxMerge, minMerge)
			}
			vertexMerge = minMerge + smallFraction()*(maxMerge-minMerge)
			maxPerturb = 0.5 * math.Min(edgeFraction*(vertexMerge-minMerge), maxMerge-vertexMerge)
		}

		// We can perturb by any amount up to the maximum, but choosing
		// a lower maximum decreases the error bounds when checking the
		// output.
		maxPerturb *= smallFraction()

		// This is the minimum length of a split edge to prevent
		// unexpected merging and/or splicing.
		minEdge := minMerge + (vertexMerge+2*maxPerturb)/minSin
		options.vertex_merge_radius = s1.Angle(vertexMerge)
		options.edge_splice_fraction = edgeFraction
		options.validate = true
		builder := NewPolygonBuilder(options)

		// On each iteration we randomly rotate the test case around
		// the sphere. This causes the PolygonBuilder to choose
		// different first edges when trying to build loops.
		x, y, z := randomFrame()
		m := r3.MatrixFromCols(x.Vector, y.Vector, z.Vector)
		for _, chain := range test.chainsIn {
			addChain(chain, m, maxSplits, maxPerturb, minEdge, &builder)
		}

		var loops []*Loop
		var unusedEdges []Edge
		if test.xorEdges < 0 {
			builder.AssembleLoops(&loops, &unusedEdges)
		} else {
			var polygon Polygon
			builder.AssemblePolygon(&polygon, &unusedEdges)
			polygon.Release(&loops)
		}

		expected := []*Loop{}
		for _, str := range test.loopsOut {
			if str != "" {
				vertices := getVertices(str, m)
				expected = append(expected, NewLoopFromPath(vertices))
			}
		}

		// We assume that the vertex locations in the expected output
		// polygon are separated from the corresponding vertex
		// locations in the input edges by at most half of the
		// minimum merge radius. Essentially this means that the
		// expected output vertices should be near the centroid of the
		// various input vertices.
		//
		// If any edges were split, we need to allow a bit more error
		// due to inaccuracies in the interpolated positions.
		// Similarly, if any vertices were perturbed, we need to bump
		// up the error to allow for numerical errors in the actual
		// perturbation.
		maxError := 0.5*minMerge + maxPerturb
		if maxSplits > 0 || maxPerturb > 0 {
			maxError += 1e-15
		}

		ok0 := findMissingLoops(loops, expected, m, maxSplits, maxError, "Actual")
		ok1 := findMissingLoops(expected, loops, m, maxSplits, maxError, "Expected")
		ok2 := unexpectedUnusedEdgeCount(len(unusedEdges), test.numUnusedEdges, maxSplits)
		if ok0 || ok1 || ok2 {
			// We found a problem.
			dumpUnusedEdges(unusedEdges, m, test.numUnusedEdges)
			fmt.Printf(`During iteration %d:
  undirected: %v
  xor: %v
  maxSplits: %d
  maxPerturb: %.6g
  vertexMergeRadius: %.6g
  edgeSpliceFraction: %.6g
  minEdge: %.6g
  maxError: %.6g

`, iter, options.undirected_edges, options.xor_edges, maxSplits,
				s1.Angle(maxPerturb).Degrees(),
				options.vertex_merge_radius.Degrees(),
				options.edge_splice_fraction,
				s1.Angle(minEdge).Degrees(),
				s1.Angle(maxError).Degrees())
			return false
		}
	}
	return true
}
Beispiel #11
0
// 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}
}
Beispiel #12
0
// 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}
}
Beispiel #13
0
// 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}
}
Beispiel #14
0
// 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}
}