// Converts "input_str" to an S2LaxPolygonShape, assigns labels to its edges, // then uses LaxPolygonLayer with the given arguments to build a new // S2LaxPolygonShape and verifies that all edges have the expected labels. // (This function does not test whether the output edges are correct.) private static void TestEdgeLabels(string input_str, EdgeType edge_type, DegenerateBoundaries degenerate_boundaries) { S2Builder builder = new(new Options()); S2LaxPolygonShape output = new(); LabelSetIds label_set_ids = new(); IdSetLexicon label_set_lexicon = new(); LaxPolygonLayer.Options options = new(); options.EdgeType = (edge_type); options.DegenerateBoundaries_ = (degenerate_boundaries); builder.StartLayer(new LaxPolygonLayer( output, label_set_ids, label_set_lexicon, options)); EdgeLabelMap edge_label_map = new(); AddShapeWithLabels(MakeLaxPolygonOrDie(input_str), edge_type, builder, edge_label_map); Assert.True(builder.Build(out _)); for (int i = 0; i < output.NumChains(); ++i) { for (int j = 0; j < output.GetChain(i).Length; ++j) { S2Shape.Edge edge = output.ChainEdge(i, j); var expected_labels = edge_label_map[GetKey(edge, edge_type)]; Assert.Equal(expected_labels.Count, label_set_lexicon.IdSet_(label_set_ids[i][j]).Count); Assert.True(expected_labels.SequenceEqual(label_set_lexicon.IdSet_(label_set_ids[i][j]))); } } }
private static S2Shape.Edge GetKey(S2Shape.Edge edge, EdgeType edge_type) { // For undirected edges, sort the vertices in lexicographic order. if (edge_type == EdgeType.UNDIRECTED && edge.V0 > edge.V1) { edge = new S2Shape.Edge(edge.V1, edge.V0); } return(edge); }
private static void AddShapeWithLabels(S2Shape shape, EdgeType edge_type, S2Builder builder, EdgeLabelMap edge_label_map) { const int kLabelBegin = 1234; // Arbitrary. for (int e = 0; e < shape.NumEdges(); ++e) { Int32 label = kLabelBegin + e; builder.SetLabel(label); // For undirected edges, reverse the direction of every other input edge. S2Shape.Edge edge = shape.GetEdge(e); if (edge_type == EdgeType.UNDIRECTED && ((e & 1) != 0)) { edge = new S2Shape.Edge(edge.V1, edge.V0); } builder.AddEdge(edge.V0, edge.V1); edge_label_map[GetKey(edge, edge_type)].Add(label); } }
// This is a helper function for implementing S2Shape.GetReferencePoint(). // // Given a shape consisting of closed polygonal loops, the interior of the // shape is defined as the region to the left of all edges (which must be // oriented consistently). This function then chooses an arbitrary point and // returns true if that point is contained by the shape. // // Unlike S2Loop and S2Polygon, this method allows duplicate vertices and // edges, which requires some extra care with definitions. The rule that we // apply is that an edge and its reverse edge "cancel" each other: the result // is the same as if that edge pair were not present. Therefore shapes that // consist only of degenerate loop(s) are either empty or full; by convention, // the shape is considered full if and only if it contains an empty loop (see // S2LaxPolygonShape for details). // // Determining whether a loop on the sphere contains a point is harder than // the corresponding problem in 2D plane geometry. It cannot be implemented // just by counting edge crossings because there is no such thing as a "point // at infinity" that is guaranteed to be outside the loop. public static S2Shape.ReferencePoint GetReferencePoint(this S2Shape shape) { System.Diagnostics.Debug.Assert(shape.Dimension() == 2); if (shape.NumEdges() == 0) { // A shape with no edges is defined to be full if and only if it // contains at least one chain. return(S2Shape.ReferencePoint.FromContained(shape.NumChains() > 0)); } // Define a "matched" edge as one that can be paired with a corresponding // reversed edge. Define a vertex as "balanced" if all of its edges are // matched. In order to determine containment, we must find an unbalanced // vertex. Often every vertex is unbalanced, so we start by trying an // arbitrary vertex. var edge = shape.GetEdge(0); if (GetReferencePointAtVertex(shape, edge.V0, out var result)) { return(result); } // That didn't work, so now we do some extra work to find an unbalanced // vertex (if any). Essentially we gather a list of edges and a list of // reversed edges, and then sort them. The first edge that appears in one // list but not the other is guaranteed to be unmatched. int n = shape.NumEdges(); var edges = new S2Shape.Edge[n]; var rev_edges = new S2Shape.Edge[n]; for (int i = 0; i < n; ++i) { var edge2 = shape.GetEdge(i); edges[i] = edge2; rev_edges[i] = new S2Shape.Edge(edge2.V1, edge2.V0); } Array.Sort(edges); Array.Sort(rev_edges); for (int i = 0; i < n; ++i) { if (edges[i] < rev_edges[i]) { // edges[i] is unmatched System.Diagnostics.Debug.Assert(GetReferencePointAtVertex(shape, edges[i].V0, out result)); return(result); } if (rev_edges[i] < edges[i]) { // rev_edges[i] is unmatched System.Diagnostics.Debug.Assert(GetReferencePointAtVertex(shape, rev_edges[i].V0, out result)); return(result); } } // All vertices are balanced, so this polygon is either empty or full except // for degeneracies. By convention it is defined to be full if it contains // any chain with no edges. for (int i = 0; i < shape.NumChains(); ++i) { if (shape.GetChain(i).Length == 0) { return(S2Shape.ReferencePoint.FromContained(true)); } } return(S2Shape.ReferencePoint.FromContained(false)); }
public ShapeEdge(Int32 shape_id, Int32 edge_id, S2Shape.Edge edge) : this(new(shape_id, edge_id), edge.V0, edge.V1)