public SweepEvent(Point p, bool isLeft, PolygonType polygonType, SweepEvent otherSweepEvent, EdgeType edgeType)
 {
     this.p = p;
     this.isLeft = isLeft;
     this.polygonType = polygonType;
     this.otherSE = otherSweepEvent;
     this.edgeType = edgeType;
 }
        public bool Equals(SweepEvent e2)
        {
            bool equal = isLeft == e2.isLeft && polygonType == e2.polygonType && inOut == e2.inOut && edgeType == e2.edgeType && inside == e2.inside && Point.EqualsBoth(p, e2.p);
            if (!equal) return false;

            return otherSE.isLeft == e2.otherSE.isLeft && otherSE.polygonType == e2.otherSE.polygonType &&
                otherSE.inOut == e2.otherSE.inOut && otherSE.edgeType == e2.otherSE.edgeType &&	otherSE.inside == e2.otherSE.inside && Point.EqualsBoth(otherSE.p, e2.otherSE.p);
        }
        public void Remove(SweepEvent key)
        {
            int keyIndex = eventSet.IndexOf (key);
            if (keyIndex == -1)
                return;

            eventSet.RemoveAt (keyIndex);
        }
        // Should only be called by segmentCompare
        private bool CompareSweepEvent(SweepEvent e1, SweepEvent e2)
        {
            if (e1.p.x > e2.p.x) // Different x coordinate
                return true;

            if (e2.p.x > e1.p.x) // Different x coordinate
                return false;

            if (e1.p != e2.p) // Different points, but same x coordinate. The event with lower y coordinate is processed first
                return e1.p.y > e2.p.y;

            if (e1.isLeft != e2.isLeft) // Same point, but one is a left endpoint and the other a right endpoint. The right endpoint is processed first
                return e1.isLeft;

            // Same point, both events are left endpoints or both are right endpoints. The event associate to the bottom segment is processed first
            return e1.isAbove (e2.otherSE.p);
        }
        public int Insert(SweepEvent item)
        {
            int count = eventSet.Count;
            if (count == 0) {
                eventSet.Add (item);
                return 0;
            }

            eventSet.Add (null); // Expand the Vector by one.

            int i = count - 1;
            while (i >= 0 && SegmentCompare(item, eventSet[i])) {
                eventSet [i + 1] = eventSet [i];
                i--;
            }
            eventSet [i + 1] = item;
            return i + 1;
        }
        private bool SegmentCompare(SweepEvent e1, SweepEvent e2)
        {
            if (e1.Equals(e2))
                return false;

            if (signedArea (e1.p, e1.otherSE.p, e2.p) != 0 || signedArea (e1.p, e1.otherSE.p, e2.otherSE.p) != 0) {
                // Segments are not collinear
                // If they share their left endpoint use the right endpoint to sort
                if (Point.EqualsBoth(e1.p, e2.p))
                    return e1.isBelow (e2.otherSE.p);

                if (CompareSweepEvent (e1, e2))
                    return e2.isAbove (e1.p);

                return e1.isBelow (e2.p);
            }

            if (Point.EqualsBoth(e1.p, e2.p)) // Segments colinear
                return false;

            return CompareSweepEvent (e1, e2);
        }
        // The ordering is reversed because push and pop are faster.
        int CompareSweepEvent(SweepEvent e1, SweepEvent e2)
        {
            if (e1.Equals(e2)) return 0;

            if (e1.p.x - e2.p.x > Point.PRECISION) // Different x coordinate
                return -1;

            if (e1.p.x - e2.p.x <  -Point.PRECISION) // Different x coordinate
                return 1;

            if ( e1.p.y - e2.p.y > Point.PRECISION) // Different points, but same x coordinate. The event with lower y coordinate is processed first
                return -1;

            if ( e1.p.y - e2.p.y < -Point.PRECISION ) // Different points, but same x coordinate. The event with lower y coordinate is processed first
                return 1;

            if (e1.isLeft != e2.isLeft) // Same point, but one is a left endpoint and the other a right endpoint. The right endpoint is processed first
                return (e1.isLeft) ? -1 : 1;

            // Same point, both events are left endpoints or both are right endpoints. The event associate to the bottom segment is processed first
            bool isAbove = e1.isAbove (e2.otherSE.p);
            return isAbove ? -1 : 1;
        }
 public void Enqueue(SweepEvent obj)
 {
     if (!sorted) {
         elements.Add (obj);
         pointer = elements.Count-1;
         return;
     }
     // If already sorted use insertionSort on the inserted item.
     //			int count = pointer = elements.Count;
     if (pointer < 0) {
         elements.Add (obj);
         pointer = 0;
         return;
     }
     elements.Add (null); // Expand the Vector by one.
     int i = pointer;
     pointer++;
     while (i >= 0 && CompareSweepEvent(obj, elements[i]) == -1) {
         elements [i + 1] = elements [i];
         i--;
     }
     elements [i + 1] = obj;
 }
Exemplo n.º 9
0
        void PossibleIntersection(SweepEvent e1, SweepEvent e2)
        {
            IntersectResult intData          = FindIntersection(e1.segment, e2.segment);
            int             numIntersections = intData.max;
            Point           ip1 = intData.point1;

            if (numIntersections == 0)
            {
                return;
            }

            if (numIntersections == 1 && (Point.EqualsBoth(e1.p, e2.p) || Point.EqualsBoth(e1.otherSE.p, e2.otherSE.p)))
            {
                return;                 // the line segments intersect at an endpoint of both line segments
            }
            if (numIntersections == 2 && e1.polygonType == e2.polygonType)
            {
                return;                  // the line segments overlap, but they belong to the same polygon
            }
            // The line segments associated to e1 and e2 intersect
            if (numIntersections == 1)
            {
                if (!Point.EqualsBoth(e1.p, ip1) && !Point.EqualsBoth(e1.otherSE.p, ip1))
                {
                    DivideSegment(e1, ip1);                      // if ip1 is not an endpoint of the line segment associated to e1 then divide "e1"
                }
                if (!Point.EqualsBoth(e2.p, ip1) && !Point.EqualsBoth(e2.otherSE.p, ip1))
                {
                    DivideSegment(e2, ip1);                      // if ip1 is not an endpoint of the line segment associated to e2 then divide "e2"
                }
                return;
            }

            // The line segments overlap
            sortedEvents.Clear();
            if (Point.EqualsBoth(e1.p, e2.p))
            {
                sortedEvents.Add(null);
            }
            else if (Sec(e1, e2))
            {
                sortedEvents.Add(e2);
                sortedEvents.Add(e1);
            }
            else
            {
                sortedEvents.Add(e1);
                sortedEvents.Add(e2);
            }

            if (Point.EqualsBoth(e1.otherSE.p, e2.otherSE.p))
            {
                sortedEvents.Add(null);
            }
            else if (Sec(e1.otherSE, e2.otherSE))
            {
                sortedEvents.Add(e2.otherSE);
                sortedEvents.Add(e1.otherSE);
            }
            else
            {
                sortedEvents.Add(e1.otherSE);
                sortedEvents.Add(e2.otherSE);
            }

            if (sortedEvents.Count == 2)               // are both line segments equal?
            {
                e1.edgeType = e1.otherSE.edgeType = EdgeType.NON_CONTRIBUTING;
                e2.edgeType = e2.otherSE.edgeType = ((e1.inOut == e2.inOut) ? EdgeType.SAME_TRANSITION : EdgeType.DIFFERENT_TRANSITION);
                return;
            }

            if (sortedEvents.Count == 3)                // the line segments share an endpoint
            {
                sortedEvents[1].edgeType = sortedEvents[1].otherSE.edgeType = EdgeType.NON_CONTRIBUTING;
                if (sortedEvents[0] != null)                         // is the right endpoint the shared point?
                {
                    sortedEvents[0].otherSE.edgeType = (e1.inOut == e2.inOut) ? EdgeType.SAME_TRANSITION : EdgeType.DIFFERENT_TRANSITION;
                }
                else                                                                            // the shared point is the left endpoint
                {
                    sortedEvents[2].otherSE.edgeType = (e1.inOut == e2.inOut) ? EdgeType.SAME_TRANSITION : EdgeType.DIFFERENT_TRANSITION;
                }
                DivideSegment(sortedEvents[0] != null ? sortedEvents[0] : sortedEvents[2].otherSE, sortedEvents[1].p);
                return;
            }

            if (!sortedEvents[0].Equals(sortedEvents[3].otherSE))
            {             // no segment includes totally the otherSE one
                sortedEvents[1].edgeType = EdgeType.NON_CONTRIBUTING;
                sortedEvents[2].edgeType = (e1.inOut == e2.inOut) ? EdgeType.SAME_TRANSITION : EdgeType.DIFFERENT_TRANSITION;
                DivideSegment(sortedEvents[0], sortedEvents[1].p);
                DivideSegment(sortedEvents[1], sortedEvents[2].p);
                return;
            }

            // one line segment includes the other one
            sortedEvents[1].edgeType = sortedEvents[1].otherSE.edgeType = EdgeType.NON_CONTRIBUTING;
            DivideSegment(sortedEvents[0], sortedEvents[1].p);
            sortedEvents[3].otherSE.edgeType = (e1.inOut == e2.inOut) ? EdgeType.SAME_TRANSITION : EdgeType.DIFFERENT_TRANSITION;
            DivideSegment(sortedEvents[3].otherSE, sortedEvents[2].p);
        }
Exemplo n.º 10
0
        Polygon ComputeInternal(PolygonOp operation)
        {
            Polygon result = null;

            sortedEvents = new List <SweepEvent>();

            // Init event queue
            eventQueue = new EventQueue();

            // Test 1 for trivial result case
            if (subject.contours.Count * clipping.contours.Count == 0)
            {
                if (operation == PolygonOp.DIFFERENCE)
                {
                    result = subject;
                }
                else if (operation == PolygonOp.UNION || operation == PolygonOp.XOR)
                {
                    result = (subject.contours.Count == 0) ? clipping : subject;
                }
                return(result);
            }

            // Test 2 for trivial result case
            Rectangle subjectBB  = subject.boundingBox;
            Rectangle clippingBB = clipping.boundingBox;

            if (!subjectBB.Intersects(clippingBB))
            {
                if (operation == PolygonOp.DIFFERENCE)
                {
                    result = subject;
                }
                if (operation == PolygonOp.UNION || operation == PolygonOp.XOR)
                {
                    result = subject;
                    foreach (Contour c in clipping.contours)
                    {
                        result.AddContour(c);
                    }
                }

                return(result);
            }

            // Add each segment to the eventQueue, sorted from left to right.
            for (int k = 0; k < subject.contours.Count; k++)
            {
                Contour sCont            = subject.contours[k];
                int     sContPointsCount = sCont.points.Count;
                for (int pParse1 = 0; pParse1 < sContPointsCount; pParse1++)
                {
                    ProcessSegment(sCont.GetSegment(pParse1), PolygonType.SUBJECT);
                }
            }

            for (int k = 0; k < clipping.contours.Count; k++)
            {
                Contour cCont            = clipping.contours[k];
                int     cContPointsCount = cCont.points.Count;
                for (int pParse2 = 0; pParse2 < cContPointsCount; pParse2++)
                {
                    ProcessSegment(cCont.GetSegment(pParse2), PolygonType.CLIPPING);
                }
            }

            Connector connector = new Connector();

            // This is the SweepLine. That is, we go through all the polygon edges
            // by sweeping from left to right.
            SweepEventSet S = new SweepEventSet();

            double MINMAX_X = Math.Min(subjectBB.right, clippingBB.right) + Point.PRECISION;

            SweepEvent prev, next;

            int panicCounter = 0;             // This is a safety check to prevent infinite loops (very rare but could happen due to floating-point issues with a high number of points)

            while (!eventQueue.isEmpty)
            {
                if (panicCounter++ > 10000)
                {
                    Debug.Log("PANIC!");
                    break;
                }
                prev = null;
                next = null;

                SweepEvent e = eventQueue.Dequeue();

                if ((operation == PolygonOp.INTERSECTION && e.p.x > MINMAX_X) || (operation == PolygonOp.DIFFERENCE && e.p.x > subjectBB.right + Point.PRECISION))
                {
                    return(connector.ToPolygonFromLargestLineStrip());
                }

                if (operation == PolygonOp.UNION && e.p.x > MINMAX_X)
                {
                    // add all the non-processed line segments to the result
                    if (!e.isLeft)
                    {
                        connector.Add(e.segment);
                    }

                    while (!eventQueue.isEmpty)
                    {
                        e = eventQueue.Dequeue();
                        if (!e.isLeft)
                        {
                            connector.Add(e.segment);
                        }
                    }
                    return(connector.ToPolygonFromLargestLineStrip());
                }

                if (e.isLeft)                    // the line segment must be inserted into S
                {
                    int pos = S.Insert(e);

                    prev = (pos > 0) ? S.eventSet[pos - 1] : null;
                    next = (pos < S.eventSet.Count - 1) ? S.eventSet[pos + 1] : null;

                    if (prev == null)
                    {
                        e.inside = e.inOut = false;
                    }
                    else if (prev.edgeType != EdgeType.NORMAL)
                    {
                        if (pos - 2 < 0)                           // e overlaps with prev
                        // Not sure how to handle the case when pos - 2 < 0, but judging
                        // from the C++ implementation this looks like how it should be handled.
                        {
                            e.inside = e.inOut = false;
                            if (prev.polygonType != e.polygonType)
                            {
                                e.inside = true;
                            }
                            else
                            {
                                e.inOut = true;
                            }
                        }
                        else
                        {
                            SweepEvent prevTwo = S.eventSet[pos - 2];
                            if (prev.polygonType == e.polygonType)
                            {
                                e.inOut  = !prev.inOut;
                                e.inside = !prevTwo.inOut;
                            }
                            else
                            {
                                e.inOut  = !prevTwo.inOut;
                                e.inside = !prev.inOut;
                            }
                        }
                    }
                    else if (e.polygonType == prev.polygonType)
                    {
                        e.inside = prev.inside;
                        e.inOut  = !prev.inOut;
                    }
                    else
                    {
                        e.inside = !prev.inOut;
                        e.inOut  = prev.inside;
                    }

                    // Process a possible intersection between "e" and its next neighbor in S
                    if (next != null)
                    {
                        PossibleIntersection(e, next);
                    }

                    // Process a possible intersection between "e" and its previous neighbor in S
                    if (prev != null)
                    {
                        PossibleIntersection(prev, e);
                    }
                }
                else                     // the line segment must be removed from S

                // Get the next and previous line segments to "e" in S
                {
                    int otherPos = -1;
                    for (int evt = 0; evt < S.eventSet.Count; evt++)
                    {
                        if (e.otherSE.Equals(S.eventSet[evt]))
                        {
                            otherPos = evt;
                            break;
                        }
                    }
                    if (otherPos != -1)
                    {
                        prev = (otherPos > 0) ? S.eventSet[otherPos - 1] : null;
                        next = (otherPos < S.eventSet.Count - 1) ? S.eventSet[otherPos + 1] : null;
                    }

                    switch (e.edgeType)
                    {
                    case EdgeType.NORMAL:
                        switch (operation)
                        {
                        case PolygonOp.INTERSECTION:
                            if (e.otherSE.inside)
                            {
                                connector.Add(e.segment);
                            }
                            break;

                        case PolygonOp.UNION:
                            if (!e.otherSE.inside)
                            {
                                connector.Add(e.segment);
                            }
                            break;

                        case PolygonOp.DIFFERENCE:
                            if ((e.polygonType == PolygonType.SUBJECT && !e.otherSE.inside) || (e.polygonType == PolygonType.CLIPPING && e.otherSE.inside))
                            {
                                connector.Add(e.segment);
                            }
                            break;

                        case PolygonOp.XOR:
                            connector.Add(e.segment);
                            break;
                        }
                        break;

                    case EdgeType.SAME_TRANSITION:
                        if (operation == PolygonOp.INTERSECTION || operation == PolygonOp.UNION)
                        {
                            connector.Add(e.segment);
                        }
                        break;

                    case EdgeType.DIFFERENT_TRANSITION:
                        if (operation == PolygonOp.DIFFERENCE)
                        {
                            connector.Add(e.segment);
                        }
                        break;
                    }

                    if (otherPos != -1)
                    {
                        S.Remove(S.eventSet[otherPos]);
                    }

                    if (next != null && prev != null)
                    {
                        PossibleIntersection(prev, next);
                    }
                }
            }

            return(connector.ToPolygonFromLargestLineStrip());
        }
Exemplo n.º 11
0
 public SweepEvent(Point p, bool isLeft, PolygonType polygonType, SweepEvent otherSweepEvent) : this(p, isLeft, polygonType, otherSweepEvent, EdgeType.NORMAL)
 {
 }
        bool Sec(SweepEvent e1, SweepEvent e2)
        {
            // Different x coordinate
            if ( e1.p.x - e2.p.x > Point.PRECISION ||  e1.p.x - e2.p.x < -Point.PRECISION ) {
                return e1.p.x > e2.p.x;
            }

            // Same x coordinate. The event with lower y coordinate is processed first
            if ( e1.p.y -  e2.p.y > Point.PRECISION || e1.p.y -  e2.p.y < -Point.PRECISION) {
                return e1.p.y > e2.p.y;
            }

            // Same point, but one is a left endpoint and the other a right endpoint. The right endpoint is processed first
            if (e1.isLeft != e2.isLeft) {
                return e1.isLeft;
            }

            // Same point, both events are left endpoints or both are right endpoints. The event associate to the bottom segment is processed first
            return e1.isAbove(e2.otherSE.p);
        }
        void ProcessSegment(Segment segment, PolygonType polygonType)
        {
            if (Point.EqualsBoth(segment.start, segment.end)) return;
            SweepEvent e1 = new SweepEvent(segment.start, true, polygonType);
            SweepEvent e2 = new SweepEvent(segment.end, true, polygonType, e1);
            e1.otherSE = e2;

            if (e1.p.x < e2.p.x - Point.PRECISION ) {
                e2.isLeft = false;
            } else if (e1.p.x > e2.p.x + Point.PRECISION) {
                e1.isLeft = false;
            } else if (e1.p.y < e2.p.y - Point.PRECISION) { // the segment is vertical. The bottom endpoint is processed before the top endpoint
                e2.isLeft = false;
            } else {
                e1.isLeft = false;
            }

            // Pushing it so the que is sorted from left to right, with object on the left
            // having the highest priority.
            eventQueue.Enqueue(e1);
            eventQueue.Enqueue(e2);
        }
        void PossibleIntersection(SweepEvent e1, SweepEvent e2)
        {
            IntersectResult intData = FindIntersection(e1.segment, e2.segment);
            int numIntersections = intData.max;
            Point ip1 = intData.point1;

            if (numIntersections == 0)
                return;

            if (numIntersections == 1 && (Point.EqualsBoth(e1.p, e2.p) || Point.EqualsBoth(e1.otherSE.p, e2.otherSE.p)))
                return; // the line segments intersect at an endpoint of both line segments

            if (numIntersections == 2 && e1.polygonType==e2.polygonType)
                return;  // the line segments overlap, but they belong to the same polygon

            // The line segments associated to e1 and e2 intersect
            if (numIntersections == 1) {
                if (!Point.EqualsBoth(e1.p,ip1) && !Point.EqualsBoth(e1.otherSE.p,ip1))
                    DivideSegment (e1, ip1); // if ip1 is not an endpoint of the line segment associated to e1 then divide "e1"
                if (!Point.EqualsBoth(e2.p, ip1) && !Point.EqualsBoth(e2.otherSE.p,ip1))
                    DivideSegment (e2, ip1); // if ip1 is not an endpoint of the line segment associated to e2 then divide "e2"
                return;
            }

            // The line segments overlap
            sortedEvents.Clear();
            if (Point.EqualsBoth(e1.p,e2.p)) {
                sortedEvents.Add(null);
            } else if (Sec(e1, e2)) {
                sortedEvents.Add(e2);
                sortedEvents.Add(e1);
            } else {
                sortedEvents.Add(e1);
                sortedEvents.Add(e2);
            }

            if (Point.EqualsBoth(e1.otherSE.p,e2.otherSE.p)) {
                sortedEvents.Add(null);
            } else if (Sec(e1.otherSE, e2.otherSE)) {
                sortedEvents.Add(e2.otherSE);
                sortedEvents.Add(e1.otherSE);
            } else {
                sortedEvents.Add(e1.otherSE);
                sortedEvents.Add(e2.otherSE);
            }

            if (sortedEvents.Count == 2) { // are both line segments equal?
                e1.edgeType = e1.otherSE.edgeType = EdgeType.NON_CONTRIBUTING;
                e2.edgeType = e2.otherSE.edgeType = ((e1.inOut == e2.inOut) ? EdgeType.SAME_TRANSITION : EdgeType.DIFFERENT_TRANSITION);
                return;
            }

            if (sortedEvents.Count == 3) {  // the line segments share an endpoint
                sortedEvents[1].edgeType = sortedEvents[1].otherSE.edgeType = EdgeType.NON_CONTRIBUTING;
                if (sortedEvents[0] != null)         // is the right endpoint the shared point?
                    sortedEvents[0].otherSE.edgeType = (e1.inOut == e2.inOut) ? EdgeType.SAME_TRANSITION : EdgeType.DIFFERENT_TRANSITION;
                else 								// the shared point is the left endpoint
                    sortedEvents[2].otherSE.edgeType = (e1.inOut == e2.inOut) ? EdgeType.SAME_TRANSITION : EdgeType.DIFFERENT_TRANSITION;
                DivideSegment (sortedEvents[0] != null ? sortedEvents[0] : sortedEvents[2].otherSE, sortedEvents[1].p);
                return;
            }

            if (!sortedEvents[0].Equals(sortedEvents[3].otherSE))
            { // no segment includes totally the otherSE one
                sortedEvents[1].edgeType = EdgeType.NON_CONTRIBUTING;
                sortedEvents[2].edgeType = (e1.inOut == e2.inOut) ? EdgeType.SAME_TRANSITION : EdgeType.DIFFERENT_TRANSITION;
                DivideSegment (sortedEvents[0], sortedEvents[1].p);
                DivideSegment (sortedEvents[1], sortedEvents[2].p);
                return;
            }

            // one line segment includes the other one
            sortedEvents[1].edgeType = sortedEvents[1].otherSE.edgeType = EdgeType.NON_CONTRIBUTING;
            DivideSegment (sortedEvents[0], sortedEvents[1].p);
            sortedEvents[3].otherSE.edgeType = (e1.inOut == e2.inOut) ? EdgeType.SAME_TRANSITION : EdgeType.DIFFERENT_TRANSITION;
            DivideSegment (sortedEvents[3].otherSE, sortedEvents[2].p);
        }
        void DivideSegment(SweepEvent e, Point p)
        {
            // "Right event" of the "left line segment" resulting from dividing e (the line segment associated to e)
            SweepEvent r = new SweepEvent(p, false, e.polygonType, e, e.edgeType);
            // "Left event" of the "right line segment" resulting from dividing e (the line segment associated to e)
            SweepEvent l =  new SweepEvent(p, true, e.polygonType, e.otherSE, e.otherSE.edgeType);

            if (Sec(l, e.otherSE)) { // avoid a rounding error. The left event would be processed after the right event
                e.otherSE.isLeft = true;
                e.isLeft = false;
            }

            e.otherSE.otherSE = l;
            e.otherSE = r;

            eventQueue.Enqueue(l);
            eventQueue.Enqueue(r);
        }
Exemplo n.º 16
0
 public SweepEvent(Point p, bool isLeft, PolygonType polygonType, SweepEvent otherSweepEvent)
     : this(p,isLeft,polygonType,otherSweepEvent, EdgeType.NORMAL)
 {
 }