Ejemplo n.º 1
0
        /// <summary>
        /// Executes the polygon clipping operation. Difference operation can result in a region split in two parts - the new region will be added to the regions list if passed.
        /// </summary>
        public bool Compute(PolygonOp operation, List <Region> regions)
        {
            int startingContourCount = subject.contours.Count + clipping.contours.Count;

            Polygon polygon = ComputeInternal(operation);

            if (polygon == null || polygon.contours.Count == 0 || (operation == PolygonOp.UNION && polygon.contours.Count == startingContourCount))
            {
                if (regions != null && regions.Contains(regionSubject) && (operation == PolygonOp.DIFFERENCE || operation == PolygonOp.XOR))
                {
                    regions.Remove(regionSubject);
                }
                return(false);                  // if number of contours equals to starting contours, the operation has not produced anything new - end now.
            }

            for (int k = 0; k < polygon.contours.Count; k++)
            {
                Contour cont            = polygon.contours[k];
                int     contPointsCount = cont.points.Count;
                for (int j = 0; j < contPointsCount; j++)
                {
                    cont.points[j] /= PRECISION;
                }
            }
            // Returns the contour
            List <Vector2> points = polygon.contours[0].points;

            if (points.Count < 5 && (operation == PolygonOp.DIFFERENCE || operation == PolygonOp.XOR))
            {
                if (regions != null)
                {
                    regions.Remove(regionSubject);
                }
            }
            else
            {
                regionSubject.UpdatePointsAndRect(points);
            }

            if (regions != null)
            {
                // New valid extra regions?
                for (int k = 1; k < polygon.contours.Count; k++)
                {
                    Region newRegion = new Region(regionSubject.entity, regions.Count);
                    points = polygon.contours[k].points;
                    if (points.Count >= 5)
                    {
                        newRegion.UpdatePointsAndRect(points);
                        regions.Add(newRegion);
                    }
                }
            }

            return(true);
        }
Ejemplo n.º 2
0
        public void Compute(PolygonOp operation)
        {
            Polygon polygon = ComputeInternal(operation);

            if (polygon == null)
            {
                return;
            }

            for (int k = 0; k < polygon.contours.Count; k++)
            {
                Contour cont = polygon.contours[k];
                for (int j = 0; j < cont.points.Count; j++)
                {
                    cont.points[j] /= PRECISION;
                }
            }
            // Returns the contour
            regionSubject.points = polygon.contours[0].points.ToArray();
        }
Ejemplo n.º 3
0
        Polygon ComputeInternal(PolygonOp operation)
        {
            Polygon result = null;

            // 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.
            foreach (Contour sCont in subject.contours)
            {
                for (int pParse1 = 0; pParse1 < sCont.points.Count; pParse1++)
                {
                    ProcessSegment(sCont.GetSegment(pParse1), PolygonType.SUBJECT);
                }
            }

            foreach (Contour cCont in clipping.contours)
            {
                for (int pParse2 = 0; pParse2 < cCont.points.Count; 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();

            float MINMAX_X = Mathf.Min(subjectBB.right, clippingBB.right);

            SweepEvent prev, next;

            int panicCounter = 0;

            while (!eventQueue.isEmpty)
            {
                if (panicCounter++ > 100000)
                {
                    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))
                {
                    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());
        }
Ejemplo n.º 4
0
 public void Compute(PolygonOp operation)
 {
     subject = ComputeInternal(operation);
 }
 public void Compute(PolygonOp operation)
 {
     subject = ComputeInternal(operation);
 }
        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];
                for (int pParse1=0;pParse1<sCont.points.Count;pParse1++)
                    ProcessSegment(sCont.GetSegment(pParse1), PolygonType.SUBJECT);
            }

            for(int k=0;k<clipping.contours.Count;k++) {
                Contour cCont = clipping.contours[k];
                for (int pParse2=0;pParse2<cCont.points.Count;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();
        }