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; } } }
// 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); }
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); }
/*// 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)); }
public void AddShape(S2ClippedShape s) { shapes_.Add(s); }
// 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); }
// 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); } } } }