Ejemplo n.º 1
0
            /// <summary>
            /// Adds an intersection <see cref="EventPoint"/> to the <see cref="Schedule"/> if the
            /// two specified <see cref="SweepLine"/> nodes indicate a line crossing.</summary>
            /// <param name="a">
            /// The first <see cref="SweepLine"/> node to examine.</param>
            /// <param name="b">
            /// The second <see cref="SweepLine"/> node to examine.</param>
            /// <param name="e">
            /// The current <see cref="EventPoint"/> which receives a detected crossing that occurs
            /// exactly at the <see cref="Cursor"/>.</param>
            /// <remarks>
            /// If the <see cref="Schedule"/> already contains an <see cref="EventPoint"/> for the
            /// computed intersection, <b>AddCrossing</b> adds the indicated lines to the existing
            /// <see cref="EventPoint"/> if they are not already present.</remarks>

            private void AddCrossing(BraidedTreeNode <Int32, Int32> a,
                                     BraidedTreeNode <Int32, Int32> b, EventPoint e)
            {
                int aIndex = a.Key, bIndex = b.Key;
                LineIntersection c = Lines[aIndex].Intersect(Lines[bIndex]);

                // ignore crossings that involve only start or end points,
                // as those line events have been scheduled during initialization
                if ((c.First == LineLocation.Between && LineIntersection.Contains(c.Second)) ||
                    (LineIntersection.Contains(c.First) && c.Second == LineLocation.Between))
                {
                    // quit if crossing occurs before cursor
                    PointD p      = c.Shared.Value;
                    int    result = PointDComparerY.CompareExact(Cursor, p);
                    if (result > 0)
                    {
                        return;
                    }

                    // update schedule if crossing occurs after cursor
                    if (result < 0)
                    {
                        BraidedTreeNode <PointD, EventPoint> node;
                        Schedule.TryAddNode(p, new EventPoint(p), out node);
                        e = node._value;
                    }

                    // add crossing to current or scheduled event point
                    e.TryAddLines(aIndex, c.First, bIndex, c.Second);
                }
            }
Ejemplo n.º 2
0
        /// <summary>
        /// Finds all intersections between the specified line segments, using a brute force
        /// algorithm and given the specified epsilon for coordinate comparisons.</summary>
        /// <param name="lines">
        /// An <see cref="Array"/> containing the <see cref="LineD"/> instances to intersect.
        /// </param>
        /// <param name="epsilon">
        /// The maximum absolute difference at which two coordinates should be considered equal.
        /// </param>
        /// <returns>
        /// A lexicographically sorted <see cref="Array"/> containing a <see cref="MultiLinePoint"/>
        /// for every point of intersection between the <paramref name="lines"/>.</returns>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="lines"/> is a null reference.</exception>
        /// <exception cref="ArgumentOutOfRangeException">
        /// <paramref name="epsilon"/> is equal to or less than zero.</exception>
        /// <remarks>
        /// <b>FindSimple</b> is identical with the basic <see cref="FindSimple(LineD[])"/> overload
        /// but uses the specified <paramref name="epsilon"/> to determine intersections between the
        /// specified <paramref name="lines"/> and to combine nearby intersections.</remarks>

        public static MultiLinePoint[] FindSimple(LineD[] lines, double epsilon)
        {
            if (lines == null)
            {
                ThrowHelper.ThrowArgumentNullException("lines");
            }
            if (epsilon <= 0.0)
            {
                ThrowHelper.ThrowArgumentOutOfRangeException(
                    "epsilon", epsilon, Strings.ArgumentNotPositive);
            }

            var crossings = new BraidedTree <PointD, EventPoint>(
                (a, b) => PointDComparerY.CompareEpsilon(a, b, epsilon));

            for (int i = 0; i < lines.Length - 1; i++)
            {
                for (int j = i + 1; j < lines.Length; j++)
                {
                    var crossing = lines[i].Intersect(lines[j], epsilon);

                    if (crossing.Exists)
                    {
                        PointD p = crossing.Shared.Value;
                        BraidedTreeNode <PointD, EventPoint> node;
                        crossings.TryAddNode(p, new EventPoint(p), out node);
                        node._value.TryAddLines(i, crossing.First, j, crossing.Second);
                    }
                }
            }

            return(EventPoint.Convert(crossings.Values));
        }
Ejemplo n.º 3
0
        /// <overloads>
        /// Finds all intersections between the specified line segments, using a brute force
        /// algorithm.</overloads>
        /// <summary>
        /// Finds all intersections between the specified line segments, using a brute force
        /// algorithm and exact coordinate comparisons.</summary>
        /// <param name="lines">
        /// An <see cref="Array"/> containing the <see cref="LineD"/> instances to intersect.
        /// </param>
        /// <returns>
        /// A lexicographically sorted <see cref="Array"/> containing a <see cref="MultiLinePoint"/>
        /// for every point of intersection between the <paramref name="lines"/>.</returns>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="lines"/> is a null reference.</exception>
        /// <remarks><para>
        /// <b>FindSimple</b> performs a pairwise intersection of every <paramref name="lines"/>
        /// element with every other element. The runtime is therefore always O(n^2), regardless of
        /// the number of intersections found.
        /// </para><para>
        /// However, the constant factor is low and O(n^2) intersections are found in optimal time
        /// because <b>FindSimple</b> performs no additional work to avoid testing for possible
        /// intersections. For a small number of <paramref name="lines"/> (n &lt; 50),
        /// <b>FindSimple</b> usually beats the sweep line algorithm implemented by <see
        /// cref="Find"/> regardless of the number of intersections.</para></remarks>

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

            var crossings = new BraidedTree <PointD, EventPoint>(PointDComparerY.CompareExact);

            for (int i = 0; i < lines.Length - 1; i++)
            {
                for (int j = i + 1; j < lines.Length; j++)
                {
                    var crossing = lines[i].Intersect(lines[j]);

                    if (crossing.Exists)
                    {
                        PointD p = crossing.Shared.Value;
                        BraidedTreeNode <PointD, EventPoint> node;
                        crossings.TryAddNode(p, new EventPoint(p), out node);
                        node._value.TryAddLines(i, crossing.First, j, crossing.Second);
                    }
                }
            }

            return(EventPoint.Convert(crossings.Values));
        }
Ejemplo n.º 4
0
            /// <summary>
            /// Handles the specified <see cref="EventPoint"/> that was just removed from the <see
            /// cref="Schedule"/>.</summary>
            /// <param name="e">
            /// The <see cref="EventPoint"/> to handle.</param>
            /// <remarks>
            /// <b>HandleEvent</b> always updates the <see cref="SweepLine"/>, and possibly the <see
            /// cref="Schedule"/> via <see cref="AddCrossing"/>.</remarks>

            private void HandleEvent(EventPoint e)
            {
                BraidedTreeNode <Int32, Int32> node, previous = null, next = null;

                Cursor = e.Shared;
                bool adding = false;

                // remove end point & crossing nodes
                for (int i = 0; i < e.Locations.Count; i++)
                {
                    switch (e.Locations[i])
                    {
                    case LineLocation.Start:
                        adding = true;
                        break;

                    case LineLocation.End:
                        node = SweepLine.FindNode(e.Lines[i]);
                        if (node == null)
                        {
                            ThrowHelper.ThrowInvalidOperationException(
                                Strings.SearchStructureCorrupted);
                        }

                        // remember surrounding lines
                        previous = node._previous;
                        next     = node._next;
                        SweepLine.RemoveNode(node);
                        break;

                    case LineLocation.Between:
                        if (!SweepLine.Remove(e.Lines[i]))
                        {
                            ThrowHelper.ThrowInvalidOperationException(
                                Strings.SearchStructureCorrupted);
                        }
                        adding = true;
                        break;
                    }
                }

                if (!adding)
                {
                    // intersect remaining neighbors of removed lines
                    if (previous._parent != null && next._parent != null)
                    {
                        AddCrossing(previous, next, e);
                    }

                    // record intersection event
                    var lines = e.Lines;
                    if (lines.Count < 2)
                    {
                        return;
                    }

                    /*
                     * The sweep line algorithm would normally record TWO intersections for
                     * overlapping lines that share the same lexicographic end point: one for the
                     * start point, and one for the end point. So when we encounter an event that
                     * contains only end points, we must check that its line segments arrive from at
                     * least two different directions, and only then record an intersection.
                     */

                    double slope = Slopes[lines[0]];
                    for (int i = 1; i < lines.Count; i++)
                    {
                        if (slope != Slopes[lines[i]])
                        {
                            e.Normalize(Lines);
                            Crossings.Add(e);
                            break;
                        }
                    }

                    return;
                }

                // update remaining sweep line to prepare for insertion
                var root = SweepLine.RootNode;

                for (node = root._next; node != root; node = node._next)
                {
                    int    index = node.Key;
                    double slope = Slopes[index];
                    if (slope != Double.MaxValue)
                    {
                        PointD start = Lines[index].Start;
                        Positions[index] = slope * (Cursor.Y - start.Y) + start.X;
                    }
                }

                // (re-)insert start point & crossing nodes
                previous = next = null;
                for (int i = 0; i < e.Locations.Count; i++)
                {
                    if (e.Locations[i] != LineLocation.End)
                    {
                        int index = e.Lines[i];
                        Positions[index] = Cursor.X;
                        SweepLine.TryAddNode(index, 0, out node);

                        // remember surrounding lines
                        if (previous == null)
                        {
                            previous = node._previous;
                            next     = node._next;
                        }
                    }
                }

                // intersect outermost added lines with existing neighbors
                if (previous._parent != null)
                {
                    AddCrossing(previous, previous._next, e);
                }
                if (next._parent != null)
                {
                    AddCrossing(next._previous, next, e);
                }

                // record intersection event
                if (e.Lines.Count > 1)
                {
                    e.Normalize(Lines);
                    Crossings.Add(e);
                }
            }