/// <summary> /// If "swapped" is true, the loops A and B have been swapped. This affects /// how arguments are passed to the given loop relation, since for example /// A.Contains(B) is not the same as B.Contains(A). /// </summary> public IndexCrosser(S2ShapeIndex a_index, S2ShapeIndex b_index, CrossingType type, EdgePairVisitor visitor, bool swapped) { a_index_ = a_index; b_index_ = b_index; visitor_ = visitor; min_crossing_sign_ = type == CrossingType.INTERIOR ? 1 : 0; swapped_ = swapped; b_query_ = new S2CrossingEdgeQuery(b_index_); }
/// <summary> /// Given a vector of edges within an S2ShapeIndexCell, visit all pairs of /// crossing edges (of the given CrossingType). /// </summary> private static bool VisitCrossings(ShapeEdgeVector shape_edges, CrossingType type, bool need_adjacent, EdgePairVisitor visitor) { var min_crossing_sign = type == CrossingType.INTERIOR ? 1 : 0; var num_edges = shape_edges.Count; for (int i = 0; i + 1 < num_edges; i++) { var a = shape_edges[i]; var j = i + 1; // A common situation is that an edge AB is followed by an edge BC. We // only need to visit such crossings if "need_adjacent" is true (even if // AB and BC belong to different edge chains). if (!need_adjacent && a.V1 == shape_edges[j].V0) { j++; if (j >= num_edges) { break; } } var crosser = new S2EdgeCrosser(a.V0, a.V1); for (; j < num_edges; j++) { var b = shape_edges[j]; if (crosser.C == S2Point.Empty || crosser.C != b.V0) { crosser.RestartAt(b.V0); } var sign = crosser.CrossingSign(b.V1); if (sign >= min_crossing_sign) { if (!visitor(a, b, sign == 1)) { return(false); } } } } return(true); }
/// <summary> /// Visits all pairs of crossing edges in the given S2ShapeIndex, terminating /// early if the given EdgePairVisitor function returns false (in which case /// VisitCrossings returns false as well). "type" indicates whether all /// crossings should be visited, or only interior crossings. /// /// CAVEAT: Crossings may be visited more than once. /// </summary> public static bool VisitCrossingEdgePairs(S2ShapeIndex index, CrossingType type, EdgePairVisitor visitor) { var needAdjacent = type == CrossingType.ALL; return(VisitCrossings(index, type, needAdjacent, visitor)); }
/// <summary> /// Like the above, but visits all pairs of crossing edges where one edge comes /// from each S2ShapeIndex. /// /// CAVEAT: Crossings may be visited more than once. /// </summary> public static bool VisitCrossingEdgePairs(S2ShapeIndex a_index, S2ShapeIndex b_index, CrossingType type, EdgePairVisitor visitor) { // We look for S2CellId ranges where the indexes of A and B overlap, and // then test those edges for crossings. // TODO(ericv): Use brute force if the total number of edges is small enough // (using a larger threshold if the S2ShapeIndex is not constructed yet). var ai = new RangeEnumerator(a_index); var bi = new RangeEnumerator(b_index); var ab = new IndexCrosser(a_index, b_index, type, visitor, false); // Tests A against B var ba = new IndexCrosser(b_index, a_index, type, visitor, true); // Tests B against A while (!ai.Done() || !bi.Done()) { if (ai.RangeMax < bi.RangeMin) { // The A and B cells don't overlap, and A precedes B. ai.SeekTo(bi); } else if (bi.RangeMax < ai.RangeMin) { // The A and B cells don't overlap, and B precedes A. bi.SeekTo(ai); } else { // One cell contains the other. Determine which cell is larger. var ab_relation = ai.Id.LowestOnBit() - bi.Id.LowestOnBit(); if (ab_relation > 0) { // A's index cell is larger. if (!ab.VisitCrossings(ai, bi)) { return(false); } } else if (ab_relation < 0) { // B's index cell is larger. if (!ba.VisitCrossings(bi, ai)) { return(false); } } else { // The A and B cells are the same. if (ai.Cell.NumEdges() > 0 && bi.Cell.NumEdges() > 0) { if (!ab.VisitCellCellCrossings(ai.Cell, bi.Cell)) { return(false); } } ai.MoveNext(); bi.MoveNext(); } } } return(true); }
/// <summary> /// Visits all pairs of crossing edges in the given S2ShapeIndex, terminating /// early if the given EdgePairVisitor function returns false (in which case /// VisitCrossings returns false as well). "type" indicates whether all /// crossings should be visited, or only interior crossings. /// /// If "need_adjacent" is false, then edge pairs of the form (AB, BC) may /// optionally be ignored (even if the two edges belong to different edge /// chains). This option exists for the benefit of FindSelfIntersection(), /// which does not need such edge pairs (see below). /// </summary> private static bool VisitCrossings(S2ShapeIndex index, CrossingType type, bool need_adjacent, EdgePairVisitor visitor) { // TODO(ericv): Use brute force if the total number of edges is small enough // (using a larger threshold if the S2ShapeIndex is not constructed yet). var count = index.GetEnumerableCount(); for (var pos = 0; pos < count; pos++) { var shape_edges = new ShapeEdgeVector(); var icell = index.GetIndexCell(pos); var indexCell = icell.Value.Item2; GetShapeEdges(index, indexCell, shape_edges); if (!VisitCrossings(shape_edges, type, need_adjacent, visitor)) { return(false); } } return(true); }