Exemplo n.º 1
0
        /// <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);
        }
Exemplo n.º 2
0
            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);
            }