public override void ModifyLav(AngularBisectorNetwork network) { Debug.Assert(nodeA.List == nodeB.List); CircularLinkedList <Vertex> lav = nodeA.List; Vertex vA = nodeA.Value; Vertex vB = nodeB.Value; // * Create a new Vertex V with the coordinates of the intersection I Vertex v = new Vertex(this.Position); // * insert the new vertex into the LAV. That means connect it with the predecessor // of Va and the successor of Vb in the LAV CircularLinkedListNode <Vertex> nodeV = new CircularLinkedListNode <Vertex>(v); lav.AddAfter(nodeA, nodeV); lav.Remove(nodeA); lav.Remove(nodeB); // * link the new node V with the appropriate edges ea and eb (pointed to by vertices // Va and Vb // TODO: This needs some work for start and end nodes. What should happen? v.inEdge = vA.inEdge; v.outEdge = vB.outEdge; // f. for the new node V, created from I, compute: // * the new angle bisector b between the line segments ea and eb, and // TODO: This bisector sometimes needs to be revered! But under what circumstances? Direction2D bisector = AngularBisector(nodeV.Value.inEdge, nodeV.Value.outEdge); // Determine whether the triangle A B V has an acute or obtuse angle at V // this is used to determine the direction of the bisector Triangle2D triangle = new Triangle2D(vA.Position, vB.Position, v.Position); if (triangle.AngleC > (Math.PI / 2.0)) { bisector = -bisector; } nodeV.Value.Bisector = new Ray2D(nodeV.Value.Bisector.Source, bisector); // * the intersections of this bisector with the bisectors starting from the neighbour vertices in // the LAV in the same way as in the step 1c // * store the nearer intersection (if it exists) in the priority queue network.EnqueueNearestBisectorIntersection(lav, nodeV); }
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); }