public void AddEdges(S2Cap index_cap, int num_edges, MutableS2ShapeIndex index) { var fractal = new S2Testing.Fractal(); fractal.SetLevelForApproxMaxEdges(num_edges); index.Add(new S2Loop.Shape( fractal.MakeLoop(S2Testing.GetRandomFrameAt(index_cap.Center), index_cap.RadiusAngle()))); }
public void AddEdges(S2Cap index_cap, int num_edges, MutableS2ShapeIndex index) { var points = new List <S2Point>(); for (int i = 0; i < num_edges; ++i) { points.Add(S2Testing.SamplePoint(index_cap)); } index.Add(new S2PointVectorShape(points.ToArray())); }
// As above, but does not Debug.Assert-fail on invalid input. Returns true if // conversion is successful. public static bool MakeIndex(string str, out MutableS2ShapeIndex index) { index = null; var result = new MutableS2ShapeIndex(); var strs = str.Split('#'); System.Diagnostics.Debug.Assert(3 == strs.Length); var points = new List <S2Point>(); foreach (var point_str in SplitString(strs[0], '|')) { if (!MakePoint(point_str, out var point)) { return(false); } points.Add(point); } if (points.Any()) { result.Add(new S2PointVectorShape(points.ToArray())); } foreach (var line_str in SplitString(strs[1], '|')) { if (!MakeLaxPolyline(line_str, out var lax_polyline)) { return(false); } result.Add(lax_polyline); } foreach (var polygon_str in SplitString(strs[2], '|')) { if (!MakeLaxPolygon(polygon_str, out var lax_polygon)) { return(false); } result.Add(lax_polygon); } index = result; return(true); }
public VisitIntersectingShapesTest(S2ShapeIndex index) { index_ = index; iter_ = new(index); region_ = new(index); // Create an S2ShapeIndex for each shape in the original index, so that we // can use MayIntersect() and Contains() to determine the status of // individual shapes. for (int s = 0; s < index_.NumShapeIds(); ++s) { var shape_index = new MutableS2ShapeIndex(); shape_index.Add(new S2WrappedShape(index_.Shape(s))); shape_indexes_.Add(shape_index); } }
// The purpose of this function is to construct polygons consisting of // multiple loops. It takes as input a collection of loops whose boundaries // do not cross, and groups them into polygons whose interiors do not // intersect (where the boundary of each polygon may consist of multiple // loops). // // some of those islands have lakes, then the input to this function would // islands, and their lakes. Each loop would actually be present twice, once // in each direction (see below). The output would consist of one polygon // representing each lake, one polygon representing each island not including // islands or their lakes, and one polygon representing the rest of the world // // This method is intended for internal use; external clients should use // S2Builder, which has more convenient interface. // // The input consists of a set of connected components, where each component // consists of one or more loops. The components must satisfy the following // properties: // // - The loops in each component must form a subdivision of the sphere (i.e., // they must cover the entire sphere without overlap), except that a // component may consist of a single loop if and only if that loop is // degenerate (i.e., its interior is empty). // // - The boundaries of different components must be disjoint (i.e. no // crossing edges or shared vertices). // // - No component should be empty, and no loop should have zero edges. // // The output consists of a set of polygons, where each polygon is defined by // the collection of loops that form its boundary. This function does not // actually construct any S2Shapes; it simply identifies the loops that belong // to each polygon. public static void BuildPolygonBoundaries(List <List <S2Shape> > components, List <List <S2Shape> > polygons) { polygons.Clear(); if (!components.Any()) { return; } // Since the loop boundaries do not cross, a loop nesting hierarchy can be // defined by choosing any point on the sphere as the "point at infinity". // Loop A then contains loop B if (1) A contains the boundary of B and (2) // loop A does not contain the point at infinity. // // We choose S2.Origin for this purpose. The loop nesting hierarchy then // determines the face structure. Here are the details: // // 1. Build an S2ShapeIndex of all loops that do not contain S2.Origin. // This leaves at most one unindexed loop per connected component // (the "outer loop"). // // 2. For each component, choose a representative vertex and determine // which indexed loops contain it. The "depth" of this component is // defined as the number of such loops. // // 3. Assign the outer loop of each component to the containing loop whose // depth is one less. This generates a set of multi-loop polygons. // // 4. The outer loops of all components at depth 0 become a single face. var index = new MutableS2ShapeIndex(); // A map from shape.id() to the corresponding component number. var component_ids = new List <int>(); var outer_loops = new List <S2Shape>(); for (int i = 0; i < components.Count; ++i) { var component = components[i]; foreach (var loop in component) { if (component.Count > 1 && !loop.ContainsBruteForce(S2.Origin)) { // Ownership is transferred back at the end of this function. index.Add(loop); component_ids.Add(i); } else { outer_loops.Add(loop); } } // Check that there is exactly one outer loop in each component. System.Diagnostics.Debug.Assert(i + 1 == outer_loops.Count); // Component is not a subdivision } // Find the loops containing each component. var ancestors = new List <List <S2Shape> >(components.Count); var contains_query = index.MakeS2ContainsPointQuery(); for (int i = 0; i < outer_loops.Count; ++i) { var loop = outer_loops[i]; System.Diagnostics.Debug.Assert(loop.NumEdges() > 0); ancestors[i] = contains_query.GetContainingShapes(loop.GetEdge(0).V0); } // Assign each outer loop to the component whose depth is one less. // Components at depth 0 become a single face. Dictionary <S2Shape, List <S2Shape> > children = new(); // btree_map for (int i = 0; i < outer_loops.Count; ++i) { S2Shape?ancestor = null; int depth = ancestors[i].Count; if (depth > 0) { foreach (var candidate in ancestors[i]) { if (ancestors[component_ids[candidate.Id]].Count == depth - 1) { System.Diagnostics.Debug.Assert(ancestor is null); ancestor = candidate; } } System.Diagnostics.Debug.Assert(ancestor is not null); } S2Shape notNullAncestor = ancestor !; if (!children.ContainsKey(notNullAncestor)) { children.Add(notNullAncestor, new List <S2Shape>()); } children[notNullAncestor].Add(outer_loops[i]); } // There is one face per loop that is not an outer loop, plus one for the // outer loops of components at depth 0. polygons.Resize(index.NumShapeIds() + 1, () => new List <S2Shape>()); for (int i = 0; i < index.NumShapeIds(); ++i) { var polygon = polygons[i]; var loop = index.Shape(i); var itr = children.ContainsKey(loop) ? children[loop] : null; if (itr != null) { polygon = itr; } polygon.Add(loop); } polygons[^ 1] = children[null];
public void AddEdges(S2Cap index_cap, int num_edges, MutableS2ShapeIndex index) { index.Add(new S2Loop.Shape(S2Loop.MakeRegularLoop( index_cap.Center, index_cap.RadiusAngle(), num_edges))); }