func()

in s2/convex_hull_query.go [139:204]


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)
}