Beispiel #1
0
        public void Test_S2Cell_GetMaxDistanceToEdge()
        {
            // Test an edge for which its antipode crosses the cell. Validates both the
            // standard and brute force implementations for this case.
            S2Cell  cell = S2Cell.FromFacePosLevel(0, 0, 20);
            S2Point a    = -S2.Interpolate(2.0, cell.Center(), cell.Vertex(0));
            S2Point b    = -S2.Interpolate(2.0, cell.Center(), cell.Vertex(2));

            S1ChordAngle actual   = cell.MaxDistance(a, b);
            S1ChordAngle expected = GetMaxDistanceToEdgeBruteForce(cell, a, b);

            Assert2.Near(expected.Radians(), S1ChordAngle.Straight.Radians(), S2.DoubleError);
            Assert2.Near(actual.Radians(), S1ChordAngle.Straight.Radians(), S2.DoubleError);
        }
Beispiel #2
0
        private static void CompareS2CellToPadded(S2Cell cell, S2PaddedCell pcell, double padding)
        {
            Assert.Equal(cell.Id, pcell.Id);
            Assert.Equal(cell.Level, pcell.Level);
            Assert.Equal(padding, pcell.Padding);
            Assert.Equal(cell.BoundUV.Expanded(padding), pcell.Bound);
            var center_uv = cell.Id.CenterUV();

            Assert.Equal(R2Rect.FromPoint(center_uv).Expanded(padding), pcell.Middle);
            Assert.Equal(cell.Center(), pcell.GetCenter());
        }
Beispiel #3
0
    // Returns true if any shape intersects "target".
    //
    // The implementation is conservative but not exact; if a shape is just
    // barely disjoint from the given cell then it may return true.  The maximum
    // error is less than 10 * S2Constants.DoubleEpsilon radians (or about 15 nanometers).
    public bool MayIntersect(S2Cell target)
    {
        var(relation, pos) = Index().LocateCell(target.Id);

        // If "target" does not overlap any index cell, there is no intersection.
        if (relation == S2ShapeIndex.CellRelation.DISJOINT)
        {
            return(false);
        }

        // If "target" is subdivided into one or more index cells, then there is an
        // intersection to within the S2ShapeIndex error bound.
        if (relation == S2ShapeIndex.CellRelation.SUBDIVIDED)
        {
            return(true);
        }

        // Otherwise, the iterator points to an index cell containing "target".
        //
        // If "target" is an index cell itself, there is an intersection because index
        // cells are created only if they have at least one edge or they are
        // entirely contained by the loop.
        var icell = Index().GetIndexCell(pos);

        System.Diagnostics.Debug.Assert(icell.Value.Item1.Contains(target.Id));
        if (icell.Value.Item1 == target.Id)
        {
            return(true);
        }

        // Test whether any shape intersects the target cell or contains its center.
        var cell = icell.Value.Item2;

        for (int s = 0; s < cell.NumClipped(); ++s)
        {
            var clipped = cell.Clipped(s);
            if (AnyEdgeIntersects(clipped, target))
            {
                return(true);
            }
            if (Contains(icell.Value.Item1, clipped, target.Center()))
            {
                return(true);
            }
        }
        return(false);
    }
Beispiel #4
0
    // Returns true if "target" is contained by any single shape.  If the cell
    // is covered by a union of different shapes then it may return false.
    //
    // The implementation is conservative but not exact; if a shape just barely
    // contains the given cell then it may return false.  The maximum error is
    // less than 10 * S2Constants.DoubleEpsilon radians (or about 15 nanometers).
    public bool Contains(S2Cell target)
    {
        var(relation, pos) = Index().LocateCell(target.Id);

        // If the relation is DISJOINT, then "target" is not contained.  Similarly if
        // the relation is SUBDIVIDED then "target" is not contained, since index
        // cells are subdivided only if they (nearly) intersect too many edges.
        if (relation != S2ShapeIndex.CellRelation.INDEXED)
        {
            return(false);
        }

        // Otherwise, the iterator points to an index cell containing "target".
        // If any shape contains the target cell, we return true.
        var icell = Index().GetIndexCell(pos);

        System.Diagnostics.Debug.Assert(icell.Value.Item1.Contains(target.Id));
        var cell = icell.Value.Item2;

        for (int s = 0; s < cell.NumClipped(); ++s)
        {
            var clipped = cell.Clipped(s);
            // The shape contains the target cell iff the shape contains the cell
            // center and none of its edges intersects the (padded) cell interior.
            if (icell.Value.Item1 == target.Id)
            {
                if (clipped.NumEdges == 0 && clipped.ContainsCenter)
                {
                    return(true);
                }
            }
            else
            {
                // It is faster to call AnyEdgeIntersects() before Contains().
                if (Index().Shape(clipped.ShapeId).Dimension() == 2 &&
                    !AnyEdgeIntersects(clipped, target) &&
                    Contains(icell.Value.Item1, clipped, target.Center()))
                {
                    return(true);
                }
            }
        }
        return(false);
    }
Beispiel #5
0
    // The implementation is approximate but conservative; it always returns
    // "false" if the cell is not contained by the buffered region, but it may
    // also return false in some cases where "cell" is in fact contained.
    public bool Contains(S2Cell cell)
    {
        // Return true if the buffered region is guaranteed to cover whole globe.
        if (radius_successor_ > S1ChordAngle.Straight)
        {
            return(true);
        }

        // To implement this method perfectly would require computing the directed
        // Hausdorff distance, which is expensive (and not currently implemented).
        // However the following heuristic is almost as good in practice and much
        // cheaper to compute.

        // Return true if the unbuffered region contains this cell.
        if (Index().MakeS2ShapeIndexRegion().Contains(cell))
        {
            return(true);
        }

        // Otherwise approximate the cell by its bounding cap.
        //
        // NOTE(ericv): It would be slightly more accurate to first find the closest
        // point in the indexed geometry to the cell, and then measure the actual
        // maximum distance from that point to the cell (a poor man's Hausdorff
        // distance).  But based on actual tests this is not worthwhile.
        S2Cap cap = cell.GetCapBound();

        if (Radius < cap.Radius)
        {
            return(false);
        }

        // Return true if the distance to the cell center plus the radius of the
        // cell's bounding cap is less than or equal to "radius_".
        var target = new S2ClosestEdgeQuery.PointTarget(cell.Center());

        return(query_.IsDistanceLess(target, radius_successor_ - cap.Radius));
    }
Beispiel #6
0
    // Visits all shapes that intersect "target", terminating early if the
    // "visitor" return false (in which case VisitIntersectingShapes returns
    // false as well).  Each shape is visited at most once.
    //
    // This method can also be used to visit all shapes that fully contain
    // "target" (VisitContainingShapes) by simply having the ShapeVisitor
    // function immediately return true when "contains_target" is false.
    public bool VisitIntersectingShapes(S2Cell target, ShapeVisitor visitor)
    {
        var(cellRelation, pos) = Index().LocateCell(target.Id);
        S2ShapeIndex.Enumerator iter_ = new(Index());
        iter_.SetPosition(pos);
        switch (cellRelation)
        {
        case S2ShapeIndex.CellRelation.DISJOINT:
            return(true);

        case S2ShapeIndex.CellRelation.SUBDIVIDED:
        {
            // A shape contains the target cell iff it appears in at least one cell,
            // it contains the center of all cells, and it has no edges in any cell.
            // It is easier to keep track of whether a shape does *not* contain the
            // target cell because boolean values default to false.
            Dictionary <int, bool> shape_not_contains = new();
            for (var max = target.Id.RangeMax();
                 !iter_.Done() && iter_.Id <= max; iter_.MoveNext())
            {
                var cell = iter_.Cell;
                for (int s = 0; s < cell.NumClipped(); ++s)
                {
                    var clipped = cell.Clipped(s);
                    shape_not_contains[clipped.ShapeId] |=
                        clipped.NumEdges > 0 || !clipped.ContainsCenter;
                }
            }
            foreach (var(shape_id, not_contains) in shape_not_contains)
            {
                if (!visitor(Index().Shape(shape_id), !not_contains))
                {
                    return(false);
                }
            }
            return(true);
        }

        case S2ShapeIndex.CellRelation.INDEXED:
        {
            var cell = iter_.Cell;
            for (int s = 0; s < cell.NumClipped(); ++s)
            {
                // The shape contains the target cell iff the shape contains the cell
                // center and none of its edges intersects the (padded) cell interior.
                var  clipped  = cell.Clipped(s);
                bool contains = false;
                if (iter_.Id == target.Id)
                {
                    contains = clipped.NumEdges == 0 && clipped.ContainsCenter;
                }
                else
                {
                    if (!AnyEdgeIntersects(clipped, target))
                    {
                        if (!Contains(iter_.Id, clipped, target.Center()))
                        {
                            continue;          // Disjoint.
                        }
                        contains = true;
                    }
                }
                if (!visitor(Index().Shape(clipped.ShapeId), contains))
                {
                    return(false);
                }
            }
            return(true);
        }
        }

        throw new Exception("(FATAL) Unhandled S2ShapeIndex::CellRelation");
    }