/// <summary> /// /// </summary> /// <param name="de"></param> private static void CopySymDepths(DirectedEdge de) { DirectedEdge sym = de.Sym; sym.SetDepth(PositionType.Left, de.GetDepth(PositionType.Right)); sym.SetDepth(PositionType.Right, de.GetDepth(PositionType.Left)); }
/// <summary> /// /// </summary> /// <param name="start"></param> /// <param name="geometryFactory"></param> public MaximalEdgeRing(DirectedEdge start, IGeometryFactory geometryFactory) : base(start, geometryFactory) { }
/// <summary> /// /// </summary> /// <param name="de"></param> public virtual void ComputeDepths(DirectedEdge de) { int edgeIndex = FindIndex(de); int startDepth = de.GetDepth(PositionType.Left); int targetLastDepth = de.GetDepth(PositionType.Right); // compute the depths from this edge up to the end of the edge array int nextDepth = ComputeDepths(edgeIndex + 1, EdgeList.Count, startDepth); // compute the depths for the initial part of the array int lastDepth = ComputeDepths(0, edgeIndex, nextDepth); if (lastDepth != targetLastDepth) throw new TopologyException("depth mismatch at " + de.Coordinate); }
/// <summary> /// Traverse the star of edges, maintaing the current location in the result /// area at this node (if any). /// If any L edges are found in the interior of the result, mark them as covered. /// </summary> public virtual void FindCoveredLineEdges() { // Since edges are stored in CCW order around the node, // as we move around the ring we move from the right to the left side of the edge /* * Find first DirectedEdge of result area (if any). * The interior of the result is on the RHS of the edge, * so the start location will be: * - Interior if the edge is outgoing * - Exterior if the edge is incoming */ LocationType startLoc = LocationType.Null; for (IEnumerator it = GetEnumerator(); it.MoveNext();) { DirectedEdge nextOut = (DirectedEdge)it.Current; DirectedEdge nextIn = nextOut.Sym; if (!nextOut.IsLineEdge) { if (nextOut.IsInResult) { startLoc = LocationType.Interior; break; } if (nextIn.IsInResult) { startLoc = LocationType.Exterior; break; } } } // no A edges found, so can't determine if Curve edges are covered or not if (startLoc == LocationType.Null) { return; } /* * move around ring, keeping track of the current location * (Interior or Exterior) for the result area. * If Curve edges are found, mark them as covered if they are in the interior */ LocationType currLoc = startLoc; for (IEnumerator it = GetEnumerator(); it.MoveNext();) { DirectedEdge nextOut = (DirectedEdge)it.Current; DirectedEdge nextIn = nextOut.Sym; if (nextOut.IsLineEdge) { nextOut.Edge.IsCovered = (currLoc == LocationType.Interior); } else { // edge is an Area edge if (nextOut.IsInResult) { currLoc = LocationType.Exterior; } if (nextIn.IsInResult) { currLoc = LocationType.Interior; } } } }
/// <summary> /// /// </summary> /// <param name="de"></param> /// <param name="er"></param> public abstract void SetEdgeRing(DirectedEdge de, EdgeRing er);
/// <summary> /// /// </summary> /// <param name="start"></param> /// <param name="geometryFactory"></param> protected EdgeRing(DirectedEdge start, IGeometryFactory geometryFactory) { _innerGeometryFactory = geometryFactory; ComputePoints(start); ComputeRing(); }
/// <summary> /// Collect edges from Area inputs which should be in the result but /// which have not been included in a result area. /// This happens ONLY: /// during an intersection when the boundaries of two /// areas touch in a line segment /// OR as a result of a dimensional collapse. /// </summary> /// <param name="de"></param> /// <param name="opCode"></param> /// <param name="edges"></param> public virtual void CollectBoundaryTouchEdge(DirectedEdge de, SpatialFunction opCode, IList edges) { Label label = de.Label; if (de.IsLineEdge) return; // only interested in area edges if (de.IsVisited) return; // already processed if (de.IsInteriorAreaEdge) return; // added to handle dimensional collapses if (de.Edge.IsInResult) return; // if the edge linework is already included, don't include it again // sanity check for labelling of result edgerings Assert.IsTrue(!(de.IsInResult || de.Sym.IsInResult) || !de.Edge.IsInResult); // include the linework if it's in the result of the operation if (OverlayOp.IsResultOfOp(label, opCode) && opCode == SpatialFunction.Intersection) { edges.Add(de.Edge); de.VisitedEdge = true; } }
/// <summary> /// Add a set of edges to the graph. For each edge two DirectedEdges /// will be created. DirectedEdges are NOT linked by this method. /// </summary> /// <param name="edgesToAdd"></param> public virtual void AddEdges(IList edgesToAdd) { // create all the nodes for the edges for (IEnumerator it = edgesToAdd.GetEnumerator(); it.MoveNext(); ) { Edge e = (Edge)it.Current; _edges.Add(e); DirectedEdge de1 = new DirectedEdge(e, true); DirectedEdge de2 = new DirectedEdge(e, false); de1.Sym = de2; de2.Sym = de1; Add(de1); Add(de2); } }
/// <summary> /// /// </summary> /// <param name="de"></param> /// <param name="opCode"></param> /// <param name="edges"></param> public void CollectLineEdge(DirectedEdge de, SpatialFunction opCode, IList edges) { Label label = de.Label; Edge e = de.Edge; // include Curve edges which are in the result if (de.IsLineEdge) { if (!de.IsVisited && OverlayOp.IsResultOfOp(label, opCode) && !e.IsCovered) { edges.Add(e); de.VisitedEdge = true; } } }
/// <summary> /// /// </summary> /// <param name="de"></param> /// <returns></returns> public abstract DirectedEdge GetNext(DirectedEdge de);
/// <summary> /// Insert a directed edge in the list. /// </summary> /// <param name="ee"></param> public override void Insert(EdgeEnd ee) { DirectedEdge de = (DirectedEdge)ee; InsertEdgeEnd(de, de); }
/// <summary> /// /// </summary> /// <param name="de"></param> /// <returns></returns> public override DirectedEdge GetNext(DirectedEdge de) { return de.Next; }
/// <summary> /// /// </summary> private void FindRightmostEdgeAtNode() { Node node = _minDe.Node; DirectedEdgeStar star = (DirectedEdgeStar)node.Edges; _minDe = star.GetRightmostEdge(); // the DirectedEdge returned by the previous call is not // necessarily in the forward direction. Use the sym edge if it isn't. if (!_minDe.IsForward) { _minDe = _minDe.Sym; _minIndex = _minDe.Edge.Coordinates.Count - 1; } }
/// <summary> /// /// </summary> /// <param name="de"></param> /// <param name="er"></param> public override void SetEdgeRing(DirectedEdge de, EdgeRing er) { de.EdgeRing = er; }
/// <summary> /// /// </summary> /// <param name="de"></param> private void CheckForRightmostCoordinate(DirectedEdge de) { IList<Coordinate> coord = de.Edge.Coordinates; for (int i = 0; i < coord.Count - 1; i++) { // only check vertices which are the start or end point of a non-horizontal segment // <FIX> MD 19 Sep 03 - NO! we can test all vertices, since the rightmost must have a non-horiz segment adjacent to it if (_minCoord == null || coord[i].X > _minCoord.X) { _minDe = de; _minIndex = i; _minCoord = coord[i]; } } }
/// <summary> /// /// </summary> /// <param name="de"></param> /// <param name="index"></param> /// <returns></returns> private PositionType GetRightmostSide(DirectedEdge de, int index) { PositionType side = GetRightmostSideOfSegment(de, index); if (side < 0) side = GetRightmostSideOfSegment(de, index - 1); if (side < 0) { // reaching here can indicate that segment is horizontal _minCoord = null; CheckForRightmostCoordinate(de); } return side; }
/// <summary> /// Collect all the points from the DirectedEdges of this ring into a contiguous list. /// </summary> /// <param name="start"></param> protected void ComputePoints(DirectedEdge start) { StartDe = start; DirectedEdge de = start; bool isFirstEdge = true; do { Assert.IsTrue(de != null, "found null Directed Edge"); if (de == null) continue; if (de.EdgeRing == this) throw new TopologyException("Directed Edge visited twice during ring-building at " + de.Coordinate); _edges.Add(de); Label label = de.Label; Assert.IsTrue(label.IsArea()); MergeLabel(label); AddPoints(de.Edge, de.IsForward, isFirstEdge); isFirstEdge = false; SetEdgeRing(de, this); de = GetNext(de); } while (de != StartDe); }
/// <summary> /// /// </summary> /// <param name="dirEdgeList"></param> public void FindEdge(IList dirEdgeList) { /* * Check all forward DirectedEdges only. This is still general, * because each edge has a forward DirectedEdge. */ for (IEnumerator i = dirEdgeList.GetEnumerator(); i.MoveNext(); ) { DirectedEdge de = (DirectedEdge)i.Current; if (!de.IsForward) continue; CheckForRightmostCoordinate(de); } /* * If the rightmost point is a node, we need to identify which of * the incident edges is rightmost. */ Assert.IsTrue(_minIndex != 0 || _minCoord.Equals(_minDe.Coordinate), "inconsistency in rightmost processing"); if (_minIndex == 0) FindRightmostEdgeAtNode(); else FindRightmostEdgeAtVertex(); /* * now check that the extreme side is the R side. * If not, use the sym instead. */ _orientedDe = _minDe; PositionType rightmostSide = GetRightmostSide(_minDe, _minIndex); if (rightmostSide == PositionType.Left) _orientedDe = _minDe.Sym; }
/// <summary> /// Finds all non-horizontal segments intersecting the stabbing line /// in the input dirEdge. /// The stabbing line is the ray to the right of stabbingRayLeftPt. /// </summary> /// <param name="stabbingRayLeftPt">The left-hand origin of the stabbing line.</param> /// <param name="dirEdge"></param> /// <param name="stabbedSegments">The current list of DepthSegments intersecting the stabbing line.</param> private void FindStabbedSegments(Coordinate stabbingRayLeftPt, DirectedEdge dirEdge, IList stabbedSegments) { IList<Coordinate> pts = dirEdge.Edge.Coordinates; for (int i = 0; i < pts.Count - 1; i++) { _seg.P0 = pts[i]; _seg.P1 = pts[i + 1]; // ensure segment always points upwards if (_seg.P0.Y > _seg.P1.Y) _seg.Reverse(); // skip segment if it is left of the stabbing line double maxx = Math.Max(_seg.P0.X, _seg.P1.X); if (maxx < stabbingRayLeftPt.X) continue; // skip horizontal segments (there will be a non-horizontal one carrying the same depth info if (_seg.IsHorizontal) continue; // skip if segment is above or below stabbing line if (stabbingRayLeftPt.Y < _seg.P0.Y || stabbingRayLeftPt.Y > _seg.P1.Y) continue; // skip if stabbing ray is right of the segment if (CgAlgorithms.ComputeOrientation(_seg.P0, _seg.P1, stabbingRayLeftPt) == CgAlgorithms.RIGHT) continue; // stabbing line cuts this segment, so record it int depth = dirEdge.GetDepth(PositionType.Left); // if segment direction was flipped, use RHS depth instead if (!_seg.P0.Equals(pts[i])) depth = dirEdge.GetDepth(PositionType.Right); DepthSegment ds = new DepthSegment(_seg, depth); stabbedSegments.Add(ds); } }
/// <summary> /// Compute depths for all dirEdges via breadth-first traversal of nodes in graph. /// </summary> /// <param name="startEdge">Edge to start processing with.</param> // <FIX> MD - use iteration & queue rather than recursion, for speed and robustness private static void ComputeDepths(DirectedEdge startEdge) { ISet nodesVisited = new HashedSet(); Queue nodeQueue = new Queue(); Node startNode = startEdge.Node; nodeQueue.Enqueue(startNode); nodesVisited.Add(startNode); startEdge.IsVisited = true; while (nodeQueue.Count != 0) { Node n = (Node)nodeQueue.Dequeue(); nodesVisited.Add(n); // compute depths around node, starting at this edge since it has depths assigned ComputeNodeDepth(n); // add all adjacent nodes to process queue, unless the node has been visited already IEnumerator i = n.Edges.GetEnumerator(); while (i.MoveNext()) { DirectedEdge de = (DirectedEdge)i.Current; DirectedEdge sym = de.Sym; if (sym.IsVisited) continue; Node adjNode = sym.Node; if (!(nodesVisited.Contains(adjNode))) { nodeQueue.Enqueue(adjNode); nodesVisited.Add(adjNode); } } } }
/// <summary> /// /// </summary> /// <param name="start"></param> private static void VisitLinkedDirectedEdges(DirectedEdge start) { DirectedEdge startDe = start; DirectedEdge de = start; do { if (de == null) throw new NullEdgeException(); de.IsVisited = true; de = de.Next; } while (de != startDe); }
/// <summary> /// Traverse the star of DirectedEdges, linking the included edges together. /// To link two dirEdges, the next pointer for an incoming dirEdge /// is set to the next outgoing edge. /// DirEdges are only linked if: /// they belong to an area (i.e. they have sides) /// they are marked as being in the result /// Edges are linked in CCW order (the order they are stored). /// This means that rings have their face on the Right /// (in other words, the topological location of the face is given by the RHS label of the DirectedEdge). /// PRECONDITION: No pair of dirEdges are both marked as being in the result. /// </summary> public virtual void LinkResultDirectedEdges() { // make sure edges are copied to resultAreaEdges list GetResultAreaEdges(); // find first area edge (if any) to start linking at DirectedEdge firstOut = null; DirectedEdge incoming = null; int state = SCANNING_FOR_INCOMING; // link edges in CCW order for (int i = 0; i < _resultAreaEdgeList.Count; i++) { DirectedEdge nextOut = (DirectedEdge)_resultAreaEdgeList[i]; DirectedEdge nextIn = nextOut.Sym; // skip de's that we're not interested in if (!nextOut.Label.IsArea()) { continue; } // record first outgoing edge, in order to link the last incoming edge if (firstOut == null && nextOut.IsInResult) { firstOut = nextOut; } switch (state) { case SCANNING_FOR_INCOMING: if (!nextIn.IsInResult) { continue; } incoming = nextIn; state = LINKING_TO_OUTGOING; break; case LINKING_TO_OUTGOING: if (!nextOut.IsInResult) { continue; } if (incoming != null) { incoming.Next = nextOut; } state = SCANNING_FOR_INCOMING; break; default: break; } } if (state == LINKING_TO_OUTGOING) { if (firstOut == null) { throw new TopologyException("no outgoing dirEdge found", Coordinate); } Assert.IsTrue(firstOut.IsInResult, "unable to link last incoming dirEdge"); if (incoming != null) { incoming.Next = firstOut; } } }