Пример #1
0
        /// <summary>
        /// Converts the specified <see cref="PointD"/> to a WPF <see cref="Point"/>.</summary>
        /// <param name="point">
        /// The <see cref="PointD"/> instance to convert.</param>
        /// <returns>
        /// A new WPF <see cref="Point"/> instance whose coordinates equal those of the specified
        /// <paramref name="point"/>.</returns>

        public static Point ToWpfPoint(this PointD point)
        {
            return(new Point(point.X, point.Y));
        }
Пример #2
0
        /// <summary>
        /// Finds the half-edge bounding the <see cref="SubdivisionFace"/> that is nearest to and
        /// facing the specified coordinates.</summary>
        /// <param name="q">
        /// The <see cref="PointD"/> coordinates to locate.</param>
        /// <param name="distance">
        /// Returns the distance between <paramref name="q"/> and the returned <see
        /// cref="SubdivisionEdge"/>, if any; otherwise, <see cref="Double.MaxValue"/>.</param>
        /// <returns><para>
        /// The <see cref="SubdivisionEdge"/> on any outer or inner boundaries of the <see
        /// cref="SubdivisionFace"/> with the smallest distance to and facing <paramref name="q"/>.
        /// </para><para>-or-</para><para>
        /// A null reference if the <see cref="SubdivisionFace"/> is completely unbounded.
        /// </para></returns>
        /// <remarks><para>
        /// <b>FindNearestEdge</b> traverses the <see cref="OuterEdge"/> boundary and all <see
        /// cref="InnerEdges"/> boundaries, computing the distance from <paramref name="q"/> to each
        /// <see cref="SubdivisionEdge"/>. This is an O(n) operation where n is the number of
        /// half-edges incident on the <see cref="SubdivisionFace"/>.
        /// </para><para>
        /// If <paramref name="q"/> is nearest to an edge that belongs to a zero-area protrusion
        /// into the <see cref="SubdivisionFace"/>, <b>FindNearestEdge</b> returns the twin
        /// half-edge that faces <paramref name="q"/>, according to its <see
        /// cref="SubdivisionEdge.Face"/> orientation.</para></remarks>

        public SubdivisionEdge FindNearestEdge(PointD q, out double distance)
        {
            distance = Double.MaxValue;
            SubdivisionEdge nearestEdge = null;

            // find smallest distance to any outer cycle edge
            if (_outerEdge != null)
            {
                SubdivisionEdge edge = _outerEdge;
                do
                {
                    double d = edge.ToLine().DistanceSquared(q);
                    if (distance > d)
                    {
                        distance = d;
                        if (d == 0)
                        {
                            return(edge);
                        }
                        nearestEdge = edge;
                    }
                    edge = edge._next;
                } while (edge != _outerEdge);
            }

            // find smallest distance to any inner cycle edge
            if (_innerEdges != null)
            {
                for (int i = 0; i < _innerEdges.Count; i++)
                {
                    SubdivisionEdge innerEdge = _innerEdges[i];
                    SubdivisionEdge edge      = innerEdge;
                    do
                    {
                        double d = edge.ToLine().DistanceSquared(q);
                        if (distance > d)
                        {
                            distance = d;
                            if (d == 0)
                            {
                                return(edge);
                            }
                            nearestEdge = edge;
                        }
                        edge = edge._next;
                    } while (edge != innerEdge);
                }
            }

            if (nearestEdge == null)
            {
                return(null);
            }

            // check twin in case of zero-area protrusion
            if (nearestEdge._twin._face == this)
            {
                LineLocation location = nearestEdge.ToLine().Locate(q);
                if (location == LineLocation.Right)
                {
                    nearestEdge = nearestEdge._twin;
                }
            }

            distance = Math.Sqrt(distance);
            return(nearestEdge);
        }
Пример #3
0
        /// <summary>
        /// Converts the specified <see cref="PointD"/> to a WPF <see cref="Vector"/>.</summary>
        /// <param name="vector">
        /// The <see cref="PointD"/> instance to convert.</param>
        /// <returns>
        /// A new WPF <see cref="Vector"/> instance whose coordinates equal those of the specified
        /// <paramref name="vector"/>.</returns>

        public static Vector ToWpfVector(this PointD vector)
        {
            return(new Vector(vector.X, vector.Y));
        }
Пример #4
0
        /// <summary>
        /// Finds the intersection of the specified line segments, given the specified epsilon for
        /// coordinate comparisons.</summary>
        /// <param name="a">
        /// The <see cref="LineD.Start"/> point of the first line segment.</param>
        /// <param name="b">
        /// The <see cref="LineD.End"/> point of the first line segment.</param>
        /// <param name="c">
        /// The <see cref="LineD.Start"/> point of the second line segment.</param>
        /// <param name="d">
        /// The <see cref="LineD.End"/> point of the second line segment.</param>
        /// <param name="epsilon">
        /// The maximum absolute difference at which coordinates and intermediate results should be
        /// considered equal. This value is always raised to a minium of 1e-10.</param>
        /// <returns>
        /// A <see cref="LineIntersection"/> instance that describes if and how the line segments
        /// from <paramref name="a"/> to <paramref name="b"/> and from <paramref name="c"/> to
        /// <paramref name="d"/> intersect.</returns>
        /// <remarks><para>
        /// <b>Find</b> is identical with the basic <see cref="Find(PointD, PointD, PointD,
        /// PointD)"/> overload but uses the specified <paramref name="epsilon"/> to compare
        /// coordinates and intermediate results.
        /// </para><para>
        /// <b>Find</b> always raises the specified <paramref name="epsilon"/> to a minimum of 1e-10
        /// because the algorithm is otherwise too unstable, and would initiate multiple recursions
        /// with a greater epsilon anyway.</para></remarks>

        public static LineIntersection Find(PointD a, PointD b, PointD c, PointD d, double epsilon)
        {
            if (epsilon < 1e-10)
            {
                epsilon = 1e-10;
            }
            LineLocation first, second;

            double bax = b.X - a.X, bay = b.Y - a.Y;
            double dcx = d.X - c.X, dcy = d.Y - c.Y;

            // compute cross-products for all end points
            double d1 = (a.X - c.X) * dcy - (a.Y - c.Y) * dcx;
            double d2 = (b.X - c.X) * dcy - (b.Y - c.Y) * dcx;
            double d3 = (c.X - a.X) * bay - (c.Y - a.Y) * bax;
            double d4 = (d.X - a.X) * bay - (d.Y - a.Y) * bax;

            //Debug.Assert(d1 == c.CrossProductLength(a, d));
            //Debug.Assert(d2 == c.CrossProductLength(b, d));
            //Debug.Assert(d3 == a.CrossProductLength(c, b));
            //Debug.Assert(d4 == a.CrossProductLength(d, b));

            // check for collinear (but not parallel) lines
            if (Math.Abs(d1) <= epsilon && Math.Abs(d2) <= epsilon &&
                Math.Abs(d3) <= epsilon && Math.Abs(d4) <= epsilon)
            {
                // find lexicographically first point where segments overlap
                if (PointDComparerY.CompareExact(c, d) < 0)
                {
                    first = LocateCollinear(a, b, c, epsilon);
                    if (Contains(first))
                    {
                        return(new LineIntersection(c, first, LineLocation.Start, LineRelation.Collinear));
                    }

                    first = LocateCollinear(a, b, d, epsilon);
                    if (Contains(first))
                    {
                        return(new LineIntersection(d, first, LineLocation.End, LineRelation.Collinear));
                    }
                }
                else
                {
                    first = LocateCollinear(a, b, d, epsilon);
                    if (Contains(first))
                    {
                        return(new LineIntersection(d, first, LineLocation.End, LineRelation.Collinear));
                    }

                    first = LocateCollinear(a, b, c, epsilon);
                    if (Contains(first))
                    {
                        return(new LineIntersection(c, first, LineLocation.Start, LineRelation.Collinear));
                    }
                }

                // collinear line segments without overlapping points
                return(new LineIntersection(LineRelation.Collinear));
            }

            // check for divergent lines with end point intersection
            if (Math.Abs(d1) <= epsilon)
            {
                second = LocateCollinear(c, d, a, epsilon);
                return(new LineIntersection(a, LineLocation.Start, second, LineRelation.Divergent));
            }
            if (Math.Abs(d2) <= epsilon)
            {
                second = LocateCollinear(c, d, b, epsilon);
                return(new LineIntersection(b, LineLocation.End, second, LineRelation.Divergent));
            }
            if (Math.Abs(d3) <= epsilon)
            {
                first = LocateCollinear(a, b, c, epsilon);
                return(new LineIntersection(c, first, LineLocation.Start, LineRelation.Divergent));
            }
            if (Math.Abs(d4) <= epsilon)
            {
                first = LocateCollinear(a, b, d, epsilon);
                return(new LineIntersection(d, first, LineLocation.End, LineRelation.Divergent));
            }

            // compute parameters of line equations
            double denom = dcx * bay - bax * dcy;

            if (Math.Abs(denom) <= epsilon)
            {
                return(new LineIntersection(LineRelation.Parallel));
            }

            double snum = a.X * dcy - a.Y * dcx - c.X * d.Y + c.Y * d.X;
            double s    = snum / denom;

            if ((d1 < 0 && d2 < 0) || (d1 > 0 && d2 > 0))
            {
                if (s < 0)
                {
                    first = LineLocation.Before;
                }
                else if (s > 1)
                {
                    first = LineLocation.After;
                }
                else
                {
                    return(Find(a, b, c, d, 2 * epsilon));
                }
            }
            else
            {
                if (s > 0 && s < 1)
                {
                    first = LineLocation.Between;
                }
                else
                {
                    return(Find(a, b, c, d, 2 * epsilon));
                }
            }

            double tnum = c.Y * bax - c.X * bay + a.X * b.Y - a.Y * b.X;
            double t    = tnum / denom;

            if ((d3 < 0 && d4 < 0) || (d3 > 0 && d4 > 0))
            {
                if (t < 0)
                {
                    second = LineLocation.Before;
                }
                else if (t > 1)
                {
                    second = LineLocation.After;
                }
                else
                {
                    return(Find(a, b, c, d, 2 * epsilon));
                }
            }
            else
            {
                if (t > 0 && t < 1)
                {
                    second = LineLocation.Between;
                }
                else
                {
                    return(Find(a, b, c, d, 2 * epsilon));
                }
            }

            PointD shared = new PointD(a.X + s * bax, a.Y + s * bay);

            /*
             * Epsilon comparisons of cross products (or line equation parameters) might miss
             * epsilon-close end point intersections of very long line segments. We compensate by
             * directly comparing the computed intersection point against the four end points.
             */

            if (PointD.Equals(a, shared, epsilon))
            {
                first = LineLocation.Start;
            }
            else if (PointD.Equals(b, shared, epsilon))
            {
                first = LineLocation.End;
            }

            if (PointD.Equals(c, shared, epsilon))
            {
                second = LineLocation.Start;
            }
            else if (PointD.Equals(d, shared, epsilon))
            {
                second = LineLocation.End;
            }

            return(new LineIntersection(shared, first, second, LineRelation.Divergent));
        }
Пример #5
0
        /// <overloads>
        /// Finds the intersection of the specified line segments.</overloads>
        /// <summary>
        /// Finds the intersection of the specified line segments, using exact coordinate
        /// comparisons.</summary>
        /// <param name="a">
        /// The <see cref="LineD.Start"/> point of the first line segment.</param>
        /// <param name="b">
        /// The <see cref="LineD.End"/> point of the first line segment.</param>
        /// <param name="c">
        /// The <see cref="LineD.Start"/> point of the second line segment.</param>
        /// <param name="d">
        /// The <see cref="LineD.End"/> point of the second line segment.</param>
        /// <returns>
        /// A <see cref="LineIntersection"/> instance that describes if and how the line segments
        /// from <paramref name="a"/> to <paramref name="b"/> and from <paramref name="c"/> to
        /// <paramref name="d"/> intersect.</returns>
        /// <remarks><para>
        /// <b>Find</b> was adapted from the <c>Segments-Intersect</c> algorithm by Thomas H. Cormen
        /// et al., <em>Introduction to Algorithms</em> (3rd ed.), The MIT Press 2009, p.1018, for
        /// intersection testing; and from the <c>SegSegInt</c> and <c>ParallelInt</c> algorithms by
        /// Joseph O’Rourke, <em>Computational Geometry in C</em> (2nd ed.), Cambridge University
        /// Press 1998, p.224f, for line relationships and shared coordinates.
        /// </para><para>
        /// Cormen’s intersection testing first examines the <see cref="PointD.CrossProductLength"/>
        /// for each triplet of specified points. If that is insufficient, O’Rourke’s algorithm then
        /// examines the parameters of both line equations. This is mathematically redundant since
        /// O’Rourke’s algorithm alone should produce all desired information, but the combination
        /// of both algorithms proved much more resilient against misjudging line relationships due
        /// to floating-point inaccuracies.
        /// </para><para>
        /// Although most comparisons in this overload are exact, cross-product testing is always
        /// performed with a minimum epsilon of 1e-10. Moreover, <b>Find</b> will return the result
        /// of the other <see cref="Find(PointD, PointD, PointD, PointD, Double)"/> overload with an
        /// epsilon of 2e-10 if cross-product testing contradicts line equation testing. Subsequent
        /// contradictions result in further recursive calls, each time with a doubled epsilon,
        /// until an intersection can be determined without contradictions.</para></remarks>

        public static LineIntersection Find(PointD a, PointD b, PointD c, PointD d)
        {
            const double epsilon = 1e-10;
            LineLocation first, second;

            double bax = b.X - a.X, bay = b.Y - a.Y;
            double dcx = d.X - c.X, dcy = d.Y - c.Y;

            // compute cross-products for all end points
            double d1 = (a.X - c.X) * dcy - (a.Y - c.Y) * dcx;
            double d2 = (b.X - c.X) * dcy - (b.Y - c.Y) * dcx;
            double d3 = (c.X - a.X) * bay - (c.Y - a.Y) * bax;
            double d4 = (d.X - a.X) * bay - (d.Y - a.Y) * bax;

            //Debug.Assert(d1 == c.CrossProductLength(a, d));
            //Debug.Assert(d2 == c.CrossProductLength(b, d));
            //Debug.Assert(d3 == a.CrossProductLength(c, b));
            //Debug.Assert(d4 == a.CrossProductLength(d, b));

            /*
             * Some cross-products are zero: corresponding end point triplets are collinear.
             *
             * The infinite lines intersect on the corresponding end points. The lines are collinear
             * exactly if all cross-products are zero; otherwise, the lines are divergent and we
             * need to check whether the finite line segments also intersect on the end points.
             *
             * We always perform epsilon comparisons on cross-products, even in the exact overload,
             * because almost-zero cases are very frequent, especially for collinear lines.
             */
            if (Math.Abs(d1) <= epsilon && Math.Abs(d2) <= epsilon &&
                Math.Abs(d3) <= epsilon && Math.Abs(d4) <= epsilon)
            {
                // find lexicographically first point where segments overlap
                if (PointDComparerY.CompareExact(c, d) < 0)
                {
                    first = LocateCollinear(a, b, c);
                    if (Contains(first))
                    {
                        return(new LineIntersection(c, first, LineLocation.Start, LineRelation.Collinear));
                    }

                    first = LocateCollinear(a, b, d);
                    if (Contains(first))
                    {
                        return(new LineIntersection(d, first, LineLocation.End, LineRelation.Collinear));
                    }
                }
                else
                {
                    first = LocateCollinear(a, b, d);
                    if (Contains(first))
                    {
                        return(new LineIntersection(d, first, LineLocation.End, LineRelation.Collinear));
                    }

                    first = LocateCollinear(a, b, c);
                    if (Contains(first))
                    {
                        return(new LineIntersection(c, first, LineLocation.Start, LineRelation.Collinear));
                    }
                }

                // collinear line segments without overlapping points
                return(new LineIntersection(LineRelation.Collinear));
            }

            // check for divergent lines with end point intersection
            if (Math.Abs(d1) <= epsilon)
            {
                second = LocateCollinear(c, d, a);
                return(new LineIntersection(a, LineLocation.Start, second, LineRelation.Divergent));
            }
            if (Math.Abs(d2) <= epsilon)
            {
                second = LocateCollinear(c, d, b);
                return(new LineIntersection(b, LineLocation.End, second, LineRelation.Divergent));
            }
            if (Math.Abs(d3) <= epsilon)
            {
                first = LocateCollinear(a, b, c);
                return(new LineIntersection(c, first, LineLocation.Start, LineRelation.Divergent));
            }
            if (Math.Abs(d4) <= epsilon)
            {
                first = LocateCollinear(a, b, d);
                return(new LineIntersection(d, first, LineLocation.End, LineRelation.Divergent));
            }

            /*
             * All cross-products are non-zero: divergent or parallel lines.
             *
             * The lines and segments might intersect, but not on any end point.
             * Compute parameters of both line equations to determine intersections.
             * Zero denominator indicates parallel lines (but not collinear, see above).
             */
            double denom = dcx * bay - bax * dcy;

            if (Math.Abs(denom) <= epsilon)
            {
                return(new LineIntersection(LineRelation.Parallel));
            }

            /*
             * Compute position of intersection point relative to line segments, and also perform
             * sanity checks for floating-point inaccuracies. If a check fails, we cannot give a
             * reliable result at the current precision and must recurse with a greater epsilon.
             *
             * Cross-products have pairwise opposite signs exactly if the corresponding line segment
             * straddles the infinite extension of the other line segment, implying a line equation
             * parameter between zero and one. Pairwise identical signs imply a parameter less than
             * zero or greater than one. Parameters cannot be exactly zero or one, as that indicates
             * end point intersections which were already ruled out by cross-product testing.
             */
            double snum = a.X * dcy - a.Y * dcx - c.X * d.Y + c.Y * d.X;
            double s    = snum / denom;

            if ((d1 < 0 && d2 < 0) || (d1 > 0 && d2 > 0))
            {
                if (s < 0)
                {
                    first = LineLocation.Before;
                }
                else if (s > 1)
                {
                    first = LineLocation.After;
                }
                else
                {
                    return(Find(a, b, c, d, 2 * epsilon));
                }
            }
            else
            {
                if (s > 0 && s < 1)
                {
                    first = LineLocation.Between;
                }
                else
                {
                    return(Find(a, b, c, d, 2 * epsilon));
                }
            }

            double tnum = c.Y * bax - c.X * bay + a.X * b.Y - a.Y * b.X;
            double t    = tnum / denom;

            if ((d3 < 0 && d4 < 0) || (d3 > 0 && d4 > 0))
            {
                if (t < 0)
                {
                    second = LineLocation.Before;
                }
                else if (t > 1)
                {
                    second = LineLocation.After;
                }
                else
                {
                    return(Find(a, b, c, d, 2 * epsilon));
                }
            }
            else
            {
                if (t > 0 && t < 1)
                {
                    second = LineLocation.Between;
                }
                else
                {
                    return(Find(a, b, c, d, 2 * epsilon));
                }
            }

            PointD shared = new PointD(a.X + s * bax, a.Y + s * bay);

            return(new LineIntersection(shared, first, second, LineRelation.Divergent));
        }
Пример #6
0
        /// <summary>
        /// Creates the regions of the Voronoi diagram.</summary>
        /// <remarks>
        /// <b>CreateRegions</b> stores its output in the <see cref="VoronoiRegions"/> property.
        /// Please see there for details.</remarks>

        private void CreateRegions()
        {
            /*
             * 1. Create list of unsorted edges for each region
             * ================================================
             * First we accumulate the raw material for each Voronoi region.
             * All edges are stored as indices into VoronoiVertices here.
             */

            var listRegions = new LinkedList <PointI> [GeneratorSites.Length];

            for (int i = 0; i < listRegions.Length; i++)
            {
                listRegions[i] = new LinkedList <PointI>();
            }

            foreach (VoronoiEdge edge in VoronoiEdges)
            {
                PointI vertex = new PointI(edge.Vertex1, edge.Vertex2);
                listRegions[edge.Site1].AddLast(vertex);
                listRegions[edge.Site2].AddLast(vertex);
            }

            /*
             * 2. Sort and complete list of edges for each region
             * ==================================================
             * Sort each edge list so that an edge’s second vertex index equals
             * the first vertex index of the subsequent edge in the region list.
             * We may need to swap an edge’s vertex indices to allow this connection.
             *
             * Because all Voronoi edges are terminated with a pseudo-vertex where they
             * intersect the clipping rectangle, a Voronoi region may appear as several
             * internally connected, but mutually disconnected series of edges. To get
             * a single connected list, we must then insert pseudo-edges that span either
             * a border or even a corner of the clipping rectangle.
             */

            for (int i = 0; i < listRegions.Length; i++)
            {
                var candidates = listRegions[i];
                var listRegion = new LinkedList <PointI>();

                // start with first unsorted edge
                listRegion.AddFirst(candidates.First.Value);
                candidates.RemoveFirst();

                var  candidate    = candidates.First;
                bool wasEdgeAdded = false;

                while (candidate != null)
                {
                    // save next candidate before removing current one
                    var nextCandidate = candidate.Next;

                    PointI vertex = candidate.Value;
                    for (var node = listRegion.First; node != null; node = node.Next)
                    {
                        Debug.Assert(vertex != node.Value); // all vertices are distinct

                        // invert edges with out-of-order indices
                        if (vertex.X == node.Value.X || vertex.Y == node.Value.Y)
                        {
                            vertex = new PointI(vertex.Y, vertex.X);
                        }

                        // move preceding edge to sorted list
                        if (vertex.Y == node.Value.X)
                        {
                            candidates.Remove(candidate);
                            listRegion.AddBefore(node, vertex);
                            wasEdgeAdded = true;
                            break;
                        }

                        // move succeeding edge to sorted list
                        if (node.Value.Y == vertex.X)
                        {
                            candidates.Remove(candidate);
                            listRegion.AddAfter(node, vertex);
                            wasEdgeAdded = true;
                            break;
                        }
                    }

                    // try next unsorted edge
                    candidate = nextCandidate;
                    if (candidate == null && candidates.Count > 0)
                    {
                        // connection across border or corner required
                        if (!wasEdgeAdded)
                        {
                            ConnectCandidates(candidates, listRegion);
                        }

                        // start over with first candidate node
                        candidate    = candidates.First;
                        wasEdgeAdded = false;
                    }
                }

                // replace unsorted with sorted list
                listRegions[i] = listRegion;
            }

            /*
             * 3. Transform index list into polygon for each region
             * ====================================================
             * We now have sorted lists containing all edges of each Voronoi region.
             * For closed (interior) regions, the last edge connects to the first,
             * and we store exactly one vertex per edge (we always choose the first).
             *
             * For open (exterior) regions, the first edge begins and the last edge ends
             * with two different pseudo-vertices. We must now close them in the same way
             * in which we connected separate sublists in step 2.
             *
             * That is, we add one or more pseudo-edges that connect the outer vertices
             * of the list across a border or corner of the clipping rectangle. Unlike
             * step 2, we may need to extend the connection across two corners.
             */

            _voronoiRegions = new PointD[GeneratorSites.Length][];

            for (int i = 0; i < listRegions.Length; i++)
            {
                LinkedList <PointI> listRegion = listRegions[i];

                int firstIndex = listRegion.First.Value.X;
                int lastIndex  = listRegion.Last.Value.Y;

                if (firstIndex != lastIndex)
                {
                    // extend region to last pseudo-vertex
                    listRegion.AddLast(new PointI(lastIndex, Int32.MinValue));

                    PointD firstVertex = GetVertex(firstIndex);
                    PointD lastVertex  = GetVertex(lastIndex);

                    // check if pseudo-vertices span one or two corners of clipping region
                    if (firstVertex.X != lastVertex.X && firstVertex.Y != lastVertex.Y)
                    {
                        CloseCornerRegion(listRegion, firstVertex, lastVertex);
                    }
                }

                PointD[] region = new PointD[listRegion.Count];
                _voronoiRegions[i] = region;

                // store coordinates for first vertex of each edge
                int j = 0;
                for (var edge = listRegion.First; edge != null; edge = edge.Next, j++)
                {
                    region[j] = GetVertex(edge.Value.X);
                }
            }
        }
Пример #7
0
        /// <summary>
        /// Connects the specified candidate edges to the specified Voronoi region by inserting
        /// edges that span a border or corner of the <see cref="ClippingBounds"/>.</summary>
        /// <param name="candidates">
        /// A <see cref="LinkedList{PointI}"/> containing the candidate edges that must be connected
        /// to the specified <paramref name="region"/>.</param>
        /// <param name="region">
        /// A <see cref="LinkedList{PointI}"/> containing the sorted and connected edges of the
        /// Voronoi region. The first and last vertex must touch the <see cref="ClippingBounds"/>.
        /// </param>
        /// <remarks>
        /// <b>ConnectCandidates</b> adds one or two edges at the beginning of the specified
        /// <paramref name="candidates"/> list. If two edges are added, both will contain one
        /// pseudo-vertex represented by a <see cref="CornerIndex"/> value.</remarks>

        private void ConnectCandidates(LinkedList <PointI> candidates, LinkedList <PointI> region)
        {
            int firstIndex = region.First.Value.X;
            int lastIndex  = region.Last.Value.Y;

            PointD firstVertex = GetVertex(firstIndex);
            PointD lastVertex  = GetVertex(lastIndex);

            Debug.Assert(IsAtClippingBounds(firstVertex));
            Debug.Assert(IsAtClippingBounds(lastVertex));

            // outer vertex indices of connecting edge(s)
            int connect1 = -1, connect2 = -1;

            foreach (PointI candidate in candidates)
            {
                connect1 = candidate.X;
                PointD connectVertex = GetVertex(connect1);

                if (MeetAtClippingBounds(connectVertex, firstVertex))
                {
                    connect2 = firstIndex; break;
                }
                if (MeetAtClippingBounds(connectVertex, lastVertex))
                {
                    connect2 = lastIndex; break;
                }

                connect1      = candidate.Y;
                connectVertex = GetVertex(connect1);

                if (MeetAtClippingBounds(connectVertex, firstVertex))
                {
                    connect2 = firstIndex; break;
                }
                if (MeetAtClippingBounds(connectVertex, lastVertex))
                {
                    connect2 = lastIndex; break;
                }
            }

            if (connect2 >= 0)
            {
                // add connecting edge on same clipping border
                candidates.AddFirst(new PointI(connect1, connect2));
                return;
            }

            connect1 = -1; connect2 = -1;
            CornerIndex corner = CornerIndex.None;

            foreach (PointI candidate in candidates)
            {
                connect1 = candidate.X;
                PointD connectVertex = GetVertex(connect1);

                corner = GetCornerIndex(connectVertex, firstVertex);
                if (corner != CornerIndex.None)
                {
                    connect2 = firstIndex; break;
                }
                corner = GetCornerIndex(connectVertex, lastVertex);
                if (corner != CornerIndex.None)
                {
                    connect2 = lastIndex; break;
                }

                connect1      = candidate.Y;
                connectVertex = GetVertex(connect1);

                corner = GetCornerIndex(connectVertex, firstVertex);
                if (corner != CornerIndex.None)
                {
                    connect2 = firstIndex; break;
                }
                corner = GetCornerIndex(connectVertex, lastVertex);
                if (corner != CornerIndex.None)
                {
                    connect2 = lastIndex; break;
                }
            }

            // add connecting edges across clipping corner
            Debug.Assert(connect2 >= 0);
            candidates.AddFirst(new PointI((int)corner, connect2));
            candidates.AddFirst(new PointI(connect1, (int)corner));
        }
Пример #8
0
        /// <summary>
        /// Determines whether the specified two <see cref="PointD"/> coordinates lie on the same
        /// border of the <see cref="ClippingBounds"/>.</summary>
        /// <param name="p">
        /// The first <see cref="PointD"/> to examine.</param>
        /// <param name="q">
        /// The second <see cref="PointD"/> to examine.</param>
        /// <returns>
        /// <c>true</c> if the <see cref="PointD.X"/> components of <paramref name="p"/> and
        /// <paramref name="q"/> both equal the <see cref="RectD.Left"/> or <see
        /// cref="RectD.Right"/> border of the <see cref="ClippingBounds"/>, or if their <see
        /// cref="PointD.Y"/> components both equal the <see cref="RectD.Top"/> or <see
        /// cref="RectD.Bottom"/> border; otherwise, <c>false</c>.</returns>

        private bool MeetAtClippingBounds(PointD p, PointD q)
        {
            return((p.X == q.X && (p.X == ClippingBounds.Left || p.X == ClippingBounds.Right)) ||
                   (p.Y == q.Y && (p.Y == ClippingBounds.Top || p.Y == ClippingBounds.Bottom)));
        }