Exemple #1
0
    private static void EncodeEdges(S2ClippedShape clipped, Encoder encoder)
    {
        // Each entry is an (edge_id, count) pair representing a contiguous range of
        // edges.  The edge ids are delta-encoded such that 0 represents the minimum
        // valid next edge id.
        //
        // Encoding: if bits 0-2 < 7: encodes (count - 1)
        //            - bits 3+: edge delta
        //           if bits 0-2 == 7:
        //            - bits 3+ encode (count - 8)
        //            - Next value is edge delta
        //
        // No count is encoded for the last edge (saving 3 bits).
        int edge_id_base = 0;
        int num_edges    = clipped.NumEdges;

        for (int i = 0; i < num_edges; ++i)
        {
            int edge_id = clipped.Edge(i);
            System.Diagnostics.Debug.Assert(edge_id >= edge_id_base);
            int delta = edge_id - edge_id_base;
            if (i + 1 == num_edges)
            {
                // This is the last edge; no need to encode an edge count.
                encoder.PutVarInt32(delta);
            }
            else
            {
                // Count the edges in this contiguous range.
                int count = 1;
                for (; i + 1 < num_edges && clipped.Edge(i + 1) == edge_id + count; ++i)
                {
                    ++count;
                }
                if (count < 8)
                {
                    // Count is encoded in low 3 bits of delta.
                    encoder.PutVarInt32(delta << 3 | (count - 1));
                }
                else
                {
                    // Count and delta are encoded separately.
                    encoder.PutVarInt32((count - 8) << 3 | 7);
                    encoder.PutVarInt32(delta);
                }
                edge_id_base = edge_id + count;
            }
        }
    }
Exemple #2
0
    // Returns true if any edge of the indexed shape "clipped" intersects the
    // cell "target".  It may also return true if an edge is very close to
    // "target"; the maximum error is less than 10 * S2Constants.DoubleEpsilon radians (about
    // 15 nanometers).
    private bool AnyEdgeIntersects(S2ClippedShape clipped, S2Cell target)
    {
        var bound     = target.BoundUV.Expanded(kMaxError);
        var face      = target.Face;
        var shape     = Index().Shape(clipped.ShapeId);
        int num_edges = clipped.NumEdges;

        for (int i = 0; i < num_edges; ++i)
        {
            var edge = shape.GetEdge(clipped.Edge(i));
            if (S2EdgeClipping.ClipToPaddedFace(edge.V0, edge.V1, face, kMaxError, out var p0, out var p1) &&
                S2EdgeClipping.IntersectsRect(p0, p1, bound))
            {
                return(true);
            }
        }
        return(false);
    }
Exemple #3
0
    private static bool DecodeEdges(int num_edges, S2ClippedShape clipped, Decoder decoder)
    {
        // This function inverts the encodings documented above.
        Int32 edge_id = 0;

        for (int i = 0; i < num_edges;)
        {
            if (!decoder.TryGetVarUInt32(out var delta))
            {
                return(false);
            }
            if (i + 1 == num_edges)
            {
                // The last edge is encoded without an edge count.
                clipped.SetEdge(i++, edge_id + (int)delta);
            }
            else
            {
                // Otherwise decode the count and edge delta.
                UInt32 count = (delta & 7) + 1;
                delta >>= 3;
                if (count == 8)
                {
                    count = delta + 8;
                    if (!decoder.TryGetVarUInt32(out delta))
                    {
                        return(false);
                    }
                }
                edge_id += (int)delta;
                for (; count > 0; --count, ++i, ++edge_id)
                {
                    clipped.SetEdge(i, edge_id);
                }
            }
        }
        return(true);
    }
Exemple #4
0
 /*// Returns true if the indexed shape "clipped" in the indexed cell "id"
  * // contains the point "p".
  * //
  * // REQUIRES: id.contains(S2CellId(p))
  * bool Contains(S2CellId id, const S2ClippedShape& clipped,
  *          const S2Point& p) const;*/
 // REQUIRES: iter_.id() contains "p".
 private bool Contains(S2CellId id, S2ClippedShape clipped, S2Point p)
 {
     return(contains_query_.ShapeContains(id, clipped, p));
 }
Exemple #5
0
 public void AddShape(S2ClippedShape s)
 {
     shapes_.Add(s);
 }
Exemple #6
0
    // Decodes an S2ShapeIndexCell, returning true on success.
    // "num_shape_ids" should be set to index.num_shape_ids().
    public bool Decode(int num_shape_ids, Decoder decoder)
    {
        // This function inverts the encodings documented above.
        if (num_shape_ids == 1)
        {
            // Entire S2ShapeIndex contains only one shape.
            var clippedTmp = new S2ClippedShape();
            shapes_.Add(clippedTmp);
            if (!decoder.TryGetVarUInt64(out var header))
            {
                return(false);
            }
            if ((header & 1) == 0)
            {
                // The cell contains a contiguous range of edges.
                int num_edges = (int)(((header >> 2) & 15) + 2);
                clippedTmp.Init(0 /*shape_id*/, num_edges);
                clippedTmp.ContainsCenter = ((header & 2) != 0);
                for (int i = 0, edge_id = (int)(header >> 6); i < num_edges; ++i)
                {
                    clippedTmp.SetEdge(i, edge_id + i);
                }
                return(true);
            }
            if ((header & 2) == 0)
            {
                // The cell contains a single edge.
                clippedTmp.Init(0 /*shape_id*/, 1 /*num_edges*/);
                clippedTmp.ContainsCenter = ((header & 4) != 0);
                clippedTmp.SetEdge(0, (int)(header >> 3));
                return(true);
            }
            else
            {
                // The cell contains some other combination of edges.
                int num_edges = (int)(header >> 3);
                clippedTmp.Init(0 /*shape_id*/, num_edges);
                clippedTmp.ContainsCenter = ((header & 4) != 0);
                return(DecodeEdges(num_edges, clippedTmp, decoder));
            }
        }
        // S2ShapeIndex contains more than one shape.
        if (!decoder.TryGetVarUInt32(out var header32))
        {
            return(false);
        }
        int num_clipped = 1;

        if ((header32 & 7) == 3)
        {
            // This cell contains more than one shape.
            num_clipped = (int)(header32 >> 3);
            if (!decoder.TryGetVarUInt32(out header32))
            {
                return(false);
            }
        }
        int shape_id = 0;

        for (int j = 0; j < num_clipped; ++j, ++shape_id)
        {
            var clipped = new S2ClippedShape();
            shapes_.Add(clipped);
            if (j > 0 && !decoder.TryGetVarUInt32(out header32))
            {
                return(false);
            }
            if ((header32 & 1) == 0)
            {
                // The clipped shape contains a contiguous range of edges.
                if (!decoder.TryGetVarUInt32(out var shape_id_count))
                {
                    return(false);
                }
                shape_id += (int)(shape_id_count >> 4);
                int num_edges = (int)((shape_id_count & 15) + 1);
                clipped.Init(shape_id, num_edges);
                clipped.ContainsCenter = ((header32 & 2) != 0);
                for (int i = 0, edge_id = (int)(header32 >> 2); i < num_edges; ++i)
                {
                    clipped.SetEdge(i, edge_id + i);
                }
            }
            else if ((header32 & 7) == 7)
            {
                // The clipped shape has no edges.
                shape_id += (int)(header32 >> 4);
                clipped.Init(shape_id, 0);
                clipped.ContainsCenter = ((header32 & 8) != 0);
            }
            else
            {
                // The clipped shape contains some other combination of edges.
                System.Diagnostics.Debug.Assert((header32 & 3U) == 1U);
                if (!decoder.TryGetVarUInt32(out var shape_delta))
                {
                    return(false);
                }
                shape_id += (int)shape_delta;
                int num_edges = (int)((header32 >> 3) + 1);
                clipped.Init(shape_id, num_edges);
                clipped.ContainsCenter = ((header32 & 4) != 0);
                if (!DecodeEdges(num_edges, clipped, decoder))
                {
                    return(false);
                }
            }
        }
        return(true);
    }
Exemple #7
0
    // Appends an encoded representation of the S2ShapeIndexCell to "encoder".
    // "num_shape_ids" should be set to index.num_shape_ids(); this information
    // allows the encoding to be more compact in some cases.
    //
    // REQUIRES: "encoder" uses the default constructor, so that its buffer
    //           can be enlarged as necessary by calling Ensure(int).
    public void Encode(int num_shape_ids, Encoder encoder)
    {
        // The encoding is designed to be especially compact in certain common
        // situations:
        //
        // 1. The S2ShapeIndex contains exactly one shape.
        //
        // 2. The S2ShapeIndex contains more than one shape, but a particular index
        //    cell contains only one shape (num_clipped == 1).
        //
        // 3. The edge ids for a given shape in a cell form a contiguous range.
        //
        // The details were optimized by constructing an S2ShapeIndex for each
        // feature in Google's geographic repository and measuring their total
        // encoded size.  The MutableS2ShapeIndex encoding (of which this function
        // is just one part) uses an average of 1.88 bytes per vertex for features
        // consisting of polygons or polylines.
        //
        // Note that this code does not bother handling num_shapes >= 2**28 or
        // num_edges >= 2**29.  This could be fixed using varint64 in a few more
        // places, but if a single cell contains this many shapes or edges then we
        // have bigger problems than just the encoding format :)
        if (num_shape_ids == 1)
        {
            // If the entire S2ShapeIndex contains just one shape, then we don't need
            // to encode any shape ids.  This is a very important and common case.
            System.Diagnostics.Debug.Assert(NumClipped() == 1);  // Index invariant: no empty cells.
            S2ClippedShape clipped = Clipped(0);
            System.Diagnostics.Debug.Assert(clipped.ShapeId == 0);
            int n = clipped.NumEdges;
            encoder.Ensure(Encoder.kVarintMax64 + n * Encoder.kVarintMax32);
            var ccc = clipped.ContainsCenter ? 2 : 0;
            if (n >= 2 && n <= 17 && clipped.Edge(n - 1) - clipped.Edge(0) == n - 1)
            {
                // The cell contains a contiguous range of edges (*most common case*).
                // If the starting edge id is small then we can encode the cell in one
                // byte.  (The n == 0 and n == 1 cases are encoded compactly below.)
                // This encoding uses a 1-bit tag because it is by far the most common.
                //
                // Encoding: bit 0: 0
                //           bit 1: contains_center
                //           bits 2-5: (num_edges - 2)
                //           bits 6+: edge_id
                encoder.PutVarInt64(clipped.Edge(0) << 6 | (n - 2) << 2 | ccc | 0);
            }
            else if (n == 1)
            {
                // The cell contains only one edge.  For edge ids up to 15, we can
                // encode the cell in a single byte.
                //
                // Encoding: bits 0-1: 1
                //           bit 2: contains_center
                //           bits 3+: edge_id
                encoder.PutVarInt64(clipped.Edge(0) << 3 | ccc | 1);
            }
            else
            {
                // General case (including n == 0, which is encoded compactly here).
                //
                // Encoding: bits 0-1: 3
                //           bit 2: contains_center
                //           bits 3+: num_edges
                encoder.PutVarInt64(n << 3 | ccc | 3);
                EncodeEdges(clipped, encoder);
            }
        }
        else
        {
            if (NumClipped() > 1)
            {
                // The cell contains more than one shape.  The tag for this encoding
                // must be distinguishable from the cases encoded below.  We can afford
                // to use a 3-bit tag because num_clipped is generally small.
                encoder.Ensure(Encoder.kVarintMax32);
                encoder.PutVarInt32((NumClipped() << 3) | 3);
            }
            // The shape ids are delta-encoded.
            int shape_id_base = 0;
            for (int j = 0; j < NumClipped(); ++j)
            {
                var clipped = Clipped(j);
                System.Diagnostics.Debug.Assert(clipped.ShapeId >= shape_id_base);
                int shape_delta = clipped.ShapeId - shape_id_base;
                shape_id_base = clipped.ShapeId + 1;

                // Like the code above except that we also need to encode shape_id(s).
                // Because of this some choices are slightly different.
                int n = clipped.NumEdges;
                encoder.Ensure((n + 2) * Encoder.kVarintMax32);
                var ccc = clipped.ContainsCenter ? 2 : 0;
                if (n >= 1 && n <= 16 && clipped.Edge(n - 1) - clipped.Edge(0) == n - 1)
                {
                    // The clipped shape has a contiguous range of up to 16 edges.  This
                    // encoding uses a 1-bit tag because it is by far the most common.
                    //
                    // Encoding: bit 0: 0
                    //           bit 1: contains_center
                    //           bits 2+: edge_id
                    // Next value: bits 0-3: (num_edges - 1)
                    //             bits 4+: shape_delta
                    encoder.PutVarInt32(clipped.Edge(0) << 2 | ccc | 0);
                    encoder.PutVarInt32(shape_delta << 4 | (n - 1));
                }
                else if (n == 0)
                {
                    // Special encoding for clipped shapes with no edges.  Such shapes are
                    // common in polygon interiors.  This encoding uses a 3-bit tag in
                    // order to leave more bits available for the other encodings.
                    //
                    // NOTE(ericv): When num_clipped > 1, this tag could be 2 bits
                    // (because the tag used to indicate num_clipped > 1 can't appear).
                    // Alternatively, that tag can be considered reserved for future use.
                    //
                    // Encoding: bits 0-2: 7
                    //           bit 3: contains_center
                    //           bits 4+: shape_delta
                    encoder.PutVarInt32(shape_delta << 4 | ccc << 2 | 7);
                }
                else
                {
                    // General case.  This encoding uses a 2-bit tag, and the first value
                    // typically is encoded into one byte.
                    //
                    // Encoding: bits 0-1: 1
                    //           bit 2: contains_center
                    //           bits 3+: (num_edges - 1)
                    // Next value: shape_delta
                    encoder.PutVarInt32((n - 1) << 3 | ccc << 1 | 1);
                    encoder.PutVarInt32(shape_delta);
                    EncodeEdges(clipped, encoder);
                }
            }
        }
    }