Beispiel #1
0
        /// <summary>
        /// Finds the vertical border of the <see cref="ClippingBounds"/> towards which the Voronoi
        /// region containing the specified horizontal border coordinates is open.</summary>
        /// <param name="p">
        /// The first <see cref="PointD"/> to examine.</param>
        /// <param name="q">
        /// The second <see cref="PointD"/> to examine.</param>
        /// <returns>
        /// The <see cref="RectD.Left"/> or <see cref="RectD.Right"/> border of the <see
        /// cref="ClippingBounds"/>, depending on the specified <paramref name="p"/> and <paramref
        /// name="q"/>.</returns>
        /// <remarks>
        /// The specified <paramref name="p"/> and <paramref name="q"/> must lie on opposite
        /// horizontal borders of the <see cref="ClippingBounds"/>, and their Voronoi region must
        /// open to one of the vertical borders, including both adjacent corners.</remarks>

        private double FindVerticalBorder(PointD p, PointD q)
        {
            // line from top to bottom of clipping bounds
            LineD line = (p.Y < q.Y) ? new LineD(p, q) : new LineD(q, p);

            Debug.Assert(line.Start.Y == ClippingBounds.Top);
            Debug.Assert(line.End.Y == ClippingBounds.Bottom);

            // check for vertex on either side of line
            // (LineLocation assumes y-coordinates increase upward!)
            foreach (PointD vertex in VoronoiVertices)
            {
                switch (line.Locate(vertex))
                {
                case LineLocation.Left: return(ClippingBounds.Right);

                case LineLocation.Right: return(ClippingBounds.Left);
                }
            }

            Debug.Fail("Cannot identify open side of Voronoi region.");
            return(ClippingBounds.Left);
        }
Beispiel #2
0
        /// <summary>
        /// Splits the specified line segments on the specified intersection points.</summary>
        /// <param name="lines">
        /// An <see cref="Array"/> containing the <see cref="LineD"/> instances to split.</param>
        /// <param name="crossings">
        /// An <see cref="Array"/> of <see cref="MultiLinePoint"/> instances containing all points
        /// of intersection between the <paramref name="lines"/>.</param>
        /// <returns>
        /// An <see cref="Array"/> containing the <see cref="LineD"/> instances resulting from
        /// splitting all <paramref name="lines"/> on the matching <paramref name="crossings"/>.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="lines"/> or <paramref name="crossings"/> is a null reference.
        /// </exception>
        /// <exception cref="IndexOutOfRangeException">
        /// <paramref name="crossings"/> contains one or more <see cref="MultiLinePoint.Lines"/>
        /// indices that are equal to or greater than the number of <paramref name="lines"/>.
        /// </exception>
        /// <remarks><para>
        /// <b>Split</b> returns a collection of line segments that are guaranteed not to intersect,
        /// except at their <see cref="LineD.Start"/> or <see cref="LineD.End"/> points. The
        /// specified <paramref name="crossings"/> are usually the result of <see cref="Find"/> or
        /// <see cref="FindSimple"/> for the specified <paramref name="lines"/>.
        /// </para><para>
        /// <b>Split</b> sets the <see cref="LineD.Start"/> or <see cref="LineD.End"/> point of any
        /// line segment that participates in a <see cref="LineLocation.Start"/> or <see
        /// cref="LineLocation.End"/> intersection to the <see cref="MultiLinePoint.Shared"/>
        /// coordinates of that intersection, so as to preserve coordinate identities that were
        /// established by a positive comparison epsilon.</para></remarks>

        public static LineD[] Split(LineD[] lines, MultiLinePoint[] crossings)
        {
            if (lines == null)
            {
                ThrowHelper.ThrowArgumentNullException("lines");
            }
            if (crossings == null)
            {
                ThrowHelper.ThrowArgumentNullException("crossings");
            }

            // list of split segments, or null to use original line
            int count = lines.Length;

            List <PointD>[] segmentPoints = new List <PointD> [count];

            // process all epsilon-matched intersection points
            foreach (MultiLinePoint crossing in crossings)
            {
                for (int k = 0; k < crossing.Lines.Length; k++)
                {
                    int           i      = crossing.Lines[k];
                    List <PointD> points = segmentPoints[i];

                    // initialize split segment list with start & end points
                    if (points == null)
                    {
                        points = new List <PointD>(4);
                        points.Add(lines[i].Start);
                        points.Add(lines[i].End);
                        segmentPoints[i] = points;
                    }

                    switch (crossing.Locations[k])
                    {
                    case LineLocation.Start:
                        // replace start point with epsilon-matched intersection
                        points[0] = crossing.Shared;
                        break;

                    case LineLocation.End:
                        // replace end point with epsilon-matched intersection
                        points[1] = crossing.Shared;
                        break;

                    case LineLocation.Between:
                        // add intersection point that defines new split segment
                        points.Add(crossing.Shared);
                        ++count;
                        break;
                    }
                }
            }

            /*
             * Comparison function to sort split segment point list.
             *
             * We cannot use lexicographic or other single-coordinate comparisons because
             * epsilon matching might cause coordinate aberrations in the wrong direction.
             * So we compare the squared distances of both points from the start point.
             */

            PointD start = PointD.Empty;
            Comparison <PointD> comparison = (a, b) => {
                double ax = a.X - start.X, ay = a.Y - start.Y;
                double bx = b.X - start.X, by = b.Y - start.Y;
                double d = (ax * ax + ay * ay) - (bx * bx + by * by);

                if (d < 0)
                {
                    return(-1);
                }
                if (d > 0)
                {
                    return(+1);
                }
                return(0);
            };

            LineD[] segments = new LineD[count];
            int     index    = 0;

            for (int i = 0; i < lines.Length; i++)
            {
                List <PointD> points = segmentPoints[i];
                if (points == null)
                {
                    // no intersections, store original line
                    segments[index++] = lines[i];
                }
                else
                {
                    Debug.Assert(points.Count > 2);

                    // sort points by distance from start point
                    start = points[0];
                    points.Sort(comparison);

                    // convert sorted points to split line segments
                    for (int j = 0; j < points.Count - 1; j++)
                    {
                        segments[index++] = new LineD(points[j], points[j + 1]);
                    }
                }
            }

            Debug.Assert(index == count);
            return(segments);
        }