/// <summary> /// Determination of the co-ordinates of point B /// /// Point B can be characterized as having the same perpendicular distance to the straight line carrying /// the "opposite" line segment to the vertex V and from both straight lines containing the line segments /// starting at the vertex V. We have to find such an "opposite" line segment. /// /// We traverse all line segments e of the original polygon [polyline] and test them whether they can be /// "opposite" line segments. Unfortunately, a simple test of the intersection between a bisector starting /// at V and the currently tested line segment cannot be used. We have to test the intersection with the /// line holding the edge ei and by the bisectors bi and bi+1 leading from vertices at both ends of this /// line segment. /// </summary> /// <param name="lav"></param> /// <param name="vertex"></param> /// <returns></returns> private static Point2D?OppositeIntersection(CircularLinkedList <Vertex> lav, Vertex vertex) { double minDistance2 = Double.MaxValue; Point2D?minIncenter = null; // Project the supporting the edges starting at V (node) as rays Ray2D inRay = new Ray2D(vertex.inEdge.target.Position, new Direction2D(vertex.inEdge.Vector)); Ray2D outRay = new Ray2D(vertex.outEdge.source.Position, new Direction2D(-vertex.outEdge.Vector)); // Ignore cases where the in and out edges are collinear. if (!inRay.SupportingLine.IsParallelTo(outRay.SupportingLine)) { lav.ForEachPair(delegate(Vertex oppA, Vertex oppB) { // Ignore testing againt the in and out edges of V if (oppA == vertex || oppB == vertex) { return; } // Simple intersection test between the bisector starting at V and the (whole) line containing the // currently tested line segment ei rejects the line segments laying "behind" the vertex V. We then compute // the co-ordinates of the candidate point Bi as the intersection between the bisector at V and the axis [bisector] // of the angle between of the edges starting at V and the tested line segment ei. Simple check should be performed // to exclude the case where one of the line segments starting at V us parallel to ei. The resulting point B // is selected from all the candidates Bi as the nearest point to the vertex V // TODO: Performance: How much code can be factored out of this loop? // TODO: Do we need to prevent testing against the in and out edges of V? // and the candiate "opposite" line Line2D oppositeLine = new Line2D(oppA.outEdge.source.Position, oppA.outEdge.target.Position); // TODO: Need to use a ray-line intersection here, where inLine and outLine are // replaced by rays which start at V and project in alignment with the two incident edges Point2D?inOppIntersection = Intersector.Intersect(inRay, oppositeLine); Point2D?outOppIntersection = Intersector.Intersect(outRay, oppositeLine); if (inOppIntersection.HasValue && outOppIntersection.HasValue) { Triangle2D triangle = new Triangle2D(vertex.Position, inOppIntersection.Value, outOppIntersection.Value); if (!triangle.IsDegenerate) { Point2D incenter = triangle.Incenter; // Is the incenter in the zone bounded by the oppositeLine and bisectors at the beginning // and end of the segment embedded within it? // TODO: Check which way round these lines are - so the the positive side defines our zone of interest Line2D oppositeBoundary1 = oppA.Bisector.SupportingLine.Opposite; Line2D oppositeBoundary2 = oppB.Bisector.SupportingLine; OrientedSide sideA = oppositeBoundary1.Side(incenter); OrientedSide sideB = oppositeLine.Side(incenter); OrientedSide sideC = oppositeBoundary2.Side(incenter); if (sideA == OrientedSide.Negative && sideB == OrientedSide.Negative && sideC == OrientedSide.Negative) { double distance2 = (incenter - vertex.Position).Magnitude2; if (distance2 > 0.0 && distance2 < minDistance2) { minDistance2 = distance2; minIncenter = incenter; } } } } }); } return(minIncenter); }
public override void ModifyLav(AngularBisectorNetwork network) { Debug.Assert(nodeV.List != null); CircularLinkedList <Vertex> lav1 = nodeV.List; // * Create two new nodes V1 and V2 with the same co-ordinates as the intersection point I CircularLinkedListNode <Vertex> nodeV1 = new CircularLinkedListNode <Vertex>(new Vertex(this.Position)); CircularLinkedListNode <Vertex> nodeV2 = new CircularLinkedListNode <Vertex>(new Vertex(this.Position)); // * Search the opposite edge in SLAV sequentially CircularLinkedListNode <Vertex> oppositeNode = lav1.FindPair(delegate(Vertex va, Vertex vb) { // Ignore testing againt the in and out edges of V if (va == V || vb == V) { return(false); } // and the candiate "opposite" line Line2D oppositeLine = new Line2D(va.outEdge.source.Position, va.outEdge.target.Position); // TODO: Check which way round these lines are - so the the positive side defines our zone of interest Line2D oppositeBoundary1 = va.Bisector.SupportingLine.Opposite; Line2D oppositeBoundary2 = vb.Bisector.SupportingLine; OrientedSide sideA = oppositeBoundary1.Side(this.Position); OrientedSide sideB = oppositeLine.Side(this.Position); OrientedSide sideC = oppositeBoundary2.Side(this.Position); //return sideA == OrientedSide.Negative && sideB == OrientedSide.Negative && sideC == OrientedSide.Negative; return(sideA == OrientedSide.Negative && sideB == OrientedSide.Negative && sideC == OrientedSide.Negative); }); // TODO: Is this legitimate? if (oppositeNode == null) { Debug.Assert(false); return; } // oppositeNode is Y in the paper Debug.Assert(oppositeNode != null); // * insert both new nodes into the SLAV (break one LAV into two parts). Vertex V1 will be // interconnected between the predecessor of V and the vertex/node which is an end point of // the opposite line segment. V2 will be connected between the successor of V and the vertex/ // node which is a starting point of the opposite line segment. This step actually splits the // polygon shape into two parts. // Set up nodes according to the naming conventions in the Felkel et al paper. CircularLinkedListNode <Vertex> nodeY = oppositeNode; CircularLinkedListNode <Vertex> nodeX = oppositeNode.Next; CircularLinkedListNode <Vertex> nodeM = nodeV.Previous; CircularLinkedListNode <Vertex> nodeN = nodeV.Next; // The LinkedList is split into two parts. The first part remains in the original list, // the second part is placed into a new list. CircularLinkedList <Vertex> lav2 = network.CreateLav(); // We search the list for either M or Y, whichever comes first. This tells us which // algorithm to use to split the lav in two. CircularLinkedListNode <Vertex> found = lav1.FindNode(node => node == nodeM || node == nodeY); lav1.Remove(nodeV); // Splice nodes from lav1 into lav2 Debug.Assert(found != null); if (found == nodeY) { // Splice two sections of lav1 into lav2 with V1 and V2 // TODO: We have four nodes in a linked list defining two ranges First--->Y and N--->Last // These ranges may be non-overlapping, or may be overlapping in some way. e.g. Y may come after N. // 1) Find none overlapping range or ranges // 2) Copy the range(s) to lav2 // 3) Insert nodeV2 into the correct place in lav2 after Y // Another alternative 2 // 1. Is the break in the list between N and Y? bool continuousNY = null != lav1.FindNodeFrom(nodeN, node => node == nodeY); if (continuousNY) { lav2.AddLast(nodeV2); lav2.SpliceLast(nodeN, nodeY); lav2.IsCircular = true; } else // !continuousNY { lav2.SpliceLast(lav1.First, nodeY); lav2.AddLast(nodeV2); lav2.SpliceLast(nodeN, lav1.Last); lav2.IsCircular = lav1.IsCircular; } // Alternative approach //// 1. Remember whether lav1 is circular //bool lav1Circularity = lav1.IsCircular; //// 1a. Determine whether lav2 should be circular- by whether it is continuous between nodeN and nodeY //bool continuousNY = null != lav1.FindNodeFrom(nodeN, node => node == nodeY); //bool lav2Circularity = lav1Circularity || continuousNY; //// 2. Make lav1 circular, so we can safely iterate from N to Y irrespective of the location of the list head //lav1.IsCircular = true; //// 3. Add nodeV2 into lav2 //lav2.AddLast(nodeV2); //// 4. Splice from N to Y into Lav2 //lav2.SpliceLast(nodeN, nodeY); //// 5. Restore the circularity of lav1 and lav2 //lav1.IsCircular = lav1Circularity; //lav2.IsCircular = lav2Circularity; // X--->M->V1 if (lav1.Count > 0) // <--- Is this needed? { Debug.Assert(lav1.First == nodeX); Debug.Assert(lav1.Last == nodeM); lav1.AddLast(nodeV1); lav1.IsCircular = true; } } else { Debug.Assert(found == nodeM); // Splice one section of lav1 into lav2 // N--->Y->V2 lav2.SpliceFirst(nodeN, nodeY); lav2.AddLast(nodeV2); // First--->M->V2->X--->Last if (lav1.Count > 0) { Debug.Assert(nodeM.Next == nodeX); lav1.AddAfter(nodeM, nodeV1); } } // * link the new nodes V1 and V2 with the appropriate edges nodeV1.Value.inEdge = V.inEdge; nodeV1.Value.outEdge = nodeY.Value.outEdge; nodeV2.Value.inEdge = nodeY.Value.outEdge; nodeV2.Value.outEdge = V.outEdge; // f. for both nodes V1 and V2: // * compute the new angle bisectors betwenne the line segment linked to them is step 2e nodeV1.Value.Bisector = new Ray2D(nodeV1.Value.Bisector.Source, AngularBisector(nodeV1.Value.inEdge, nodeV1.Value.outEdge)); nodeV2.Value.Bisector = new Ray2D(nodeV2.Value.Bisector.Source, AngularBisector(nodeV2.Value.inEdge, nodeV2.Value.outEdge)); // * compute the intersections of these bisectors with the bisectors starting at their neighbour // vertices according to the LAVs (e.g. at points N and Y and M and X in fig 6a.), the same // way as in step 1c. New intersection points of both types may occur // * store the nearest intersection into the priority queue // TODO: [ Does this mean the nearest for V1 and the nearest for V2, or only the nearest of them both? ] network.EnqueueNearestBisectorIntersection(lav1, nodeV1); network.EnqueueNearestBisectorIntersection(lav2, nodeV2); }