/// <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; Positions rightmostSide = GetRightmostSide(minDe, minIndex); if (rightmostSide == Positions.Left) orientedDe = minDe.Sym; }
/// <summary> /// /// </summary> /// <param name="de"></param> public void ComputeDepths(DirectedEdge de) { int edgeIndex = FindIndex(de); int startDepth = de.GetDepth(Positions.Left); int targetLastDepth = de.GetDepth(Positions.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> /// 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 void CollectBoundaryTouchEdge(DirectedEdge de, SpatialFunction opCode, IList<Edge> 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> /// /// </summary> /// <param name="de"></param> /// <param name="er"></param> public override void SetEdgeRing(DirectedEdge de, EdgeRing er) { de.MinEdgeRing = er; }
/// <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.Length - 1; } }
/// <summary> /// /// </summary> /// <param name="start"></param> /// <param name="geometryFactory"></param> public MinimalEdgeRing(DirectedEdge start, IGeometryFactory geometryFactory) : base(start, geometryFactory) { }
/// <summary> /// /// </summary> /// <param name="de"></param> /// <returns></returns> public override DirectedEdge GetNext(DirectedEdge de) { return de.NextMin; }
/// <summary> /// /// </summary> /// <param name="start"></param> /// <param name="geometryFactory"></param> protected EdgeRing(DirectedEdge start, GeometryFactory geometryFactory) { _geometryFactory = geometryFactory; ComputePoints(start); ComputeRing(); }
/// <summary> /// /// </summary> /// <param name="de"></param> private void CheckForRightmostCoordinate(DirectedEdge de) { Coordinate[] coord = de.Edge.Coordinates; for (int i = 0; i < coord.Length - 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="er"></param> public abstract void SetEdgeRing(DirectedEdge de, EdgeRing er);
private EdgeRing _shell; // if non-null, the ring is a hole and this EdgeRing is its containing shell #endregion Fields #region Constructors /// <summary> /// /// </summary> /// <param name="start"></param> /// <param name="geometryFactory"></param> protected EdgeRing(DirectedEdge start, IGeometryFactory geometryFactory) { _geometryFactory = geometryFactory; ComputePoints(start); ComputeRing(); }
/// <summary> /// /// </summary> /// <param name="de"></param> private static void CopySymDepths(DirectedEdge de) { DirectedEdge sym = de.Sym; sym.SetDepth(Positions.Left, de.GetDepth(Positions.Right)); sym.SetDepth(Positions.Right, de.GetDepth(Positions.Left)); }
/// <summary> /// /// </summary> /// <param name="de"></param> /// <param name="opCode"></param> /// <param name="edges"></param> public void CollectLineEdge(DirectedEdge de, SpatialFunction opCode, IList<Edge> 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> /// 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) { Set<Node> nodesVisited = new Set<Node>(); Queue<Node> nodeQueue = new Queue<Node>(); Node startNode = startEdge.Node; nodeQueue.Enqueue(startNode); nodesVisited.Add(startNode); startEdge.Visited = true; while (nodeQueue.Count != 0) { Node n = 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 foreach (DirectedEdge de in (DirectedEdgeStar)n.Edges) { DirectedEdge sym = de.Sym; if (sym.IsVisited) continue; Node adjNode = sym.Node; if (!(nodesVisited.Contains(adjNode))) { nodeQueue.Enqueue(adjNode); nodesVisited.Add(adjNode); } } } }
/// <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 void AddEdges(IList<Edge> edgesToAdd) { // create all the nodes for the edges foreach (Edge e in edgesToAdd) { _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> /// 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 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 = ScanningForIncoming; // link edges in CCW order for (int i = 0; i < _resultAreaEdgeList.Count; i++) { var nextOut = _resultAreaEdgeList[i]; var 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 ScanningForIncoming: if (!nextIn.IsInResult) { continue; } incoming = nextIn; state = LinkingToOutgoing; break; case LinkingToOutgoing: if (!nextOut.IsInResult) { continue; } incoming.Next = nextOut; state = ScanningForIncoming; break; default: break; } } if (state == LinkingToOutgoing) { if (firstOut == null) { throw new TopologyException("no outgoing dirEdge found", Coordinate); } Assert.IsTrue(firstOut.IsInResult, "unable to link last incoming dirEdge"); incoming.Next = firstOut; } }
/* * /// <summary> * /// * /// </summary> * public DirectedEdgeStar() { } */ /// <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> /// 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 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 */ Location startLoc = Location.Null; foreach (DirectedEdge nextOut in Edges) { DirectedEdge nextIn = nextOut.Sym; if (!nextOut.IsLineEdge) { if (nextOut.IsInResult) { startLoc = Location.Interior; break; } if (nextIn.IsInResult) { startLoc = Location.Exterior; break; } } } // no A edges found, so can't determine if Curve edges are covered or not if (startLoc == Location.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 */ Location currLoc = startLoc; foreach (DirectedEdge nextOut in Edges) { DirectedEdge nextIn = nextOut.Sym; if (nextOut.IsLineEdge) { nextOut.Edge.Covered = (currLoc == Location.Interior); } else { // edge is an Area edge if (nextOut.IsInResult) { currLoc = Location.Exterior; } if (nextIn.IsInResult) { currLoc = Location.Interior; } } } }
/// <summary> /// /// </summary> /// <param name="de"></param> /// <returns></returns> public abstract DirectedEdge GetNext(DirectedEdge de);
/// <summary> /// /// </summary> /// <param name="de"></param> /// <param name="index"></param> /// <returns></returns> private Positions GetRightmostSide(DirectedEdge de, int index) { Positions 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 { if (de == null) throw new TopologyException("found null Directed Edge"); 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="de"></param> /// <param name="i"></param> /// <returns></returns> private Positions GetRightmostSideOfSegment(DirectedEdge de, int i) { Edge e = de.Edge; Coordinate[] coord = e.Coordinates; if (i < 0 || i + 1 >= coord.Length) return Positions.Parallel; if (coord[i].Y == coord[i + 1].Y) return Positions.Parallel; Positions pos = Positions.Left; if (coord[i].Y < coord[i + 1].Y) pos = Positions.Right; return pos; }
/// <summary> /// /// </summary> /// <param name="start"></param> protected void VisitLinkedDirectedEdges(DirectedEdge start) { DirectedEdge startDe = start; DirectedEdge de = start; do { Assert.IsTrue(de != null, "found null Directed Edge"); de.Visited = true; de = de.Next; } while (de != startDe); }
/// <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<DepthSegment> stabbedSegments) { Coordinate[] pts = dirEdge.Edge.Coordinates; for (int i = 0; i < pts.Length - 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 var 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(Positions.Left); // if segment direction was flipped, use RHS depth instead if (!_seg.P0.Equals(pts[i])) depth = dirEdge.GetDepth(Positions.Right); var ds = new DepthSegment(_seg, depth); stabbedSegments.Add(ds); } }