Example #1
0
        private static SegmentList select(SegmentList segments, int[] selection, BuildLog buildLog)
        {
            var result = new SegmentList();

            foreach (var seg in segments)
            {
                var index =
                    (seg.myFill.above ? 8 : 0) +
                    (seg.myFill.below.Value ? 4 : 0) +
                    ((seg.otherFill != null && seg.otherFill.above) ? 2 : 0) +
                    ((seg.otherFill != null && seg.otherFill.below.Value) ? 1 : 0);

                if (selection[index] != 0)
                {
                    // copy the segment to the results, while also calculating the fill status
                    result.Add(new Segment()
                    {
                        id     = buildLog != null ? buildLog.segmentId() : -1,
                        start  = seg.start,
                        end    = seg.end,
                        myFill = new SegmentFill()
                        {
                            above = selection[index] == 1,                               // 1 if filled above
                            below = selection[index] == 2                                // 2 if filled below
                        },
                        otherFill = null
                    });
                }
            }
            ;

            if (buildLog != null)
            {
                buildLog.selected(result);
            }

            return(result);
        }
Example #2
0
        private SegmentList calculate_INTERNAL(bool primaryPolyInverted, bool secondaryPolyInverted)
        {
            //
            // main event loop
            //
            var segments = new SegmentList();

            status_root = new StatusLinkedList();

            while (!event_root.isEmpty)
            {
                var ev = (EventNode)event_root.head;

                if (buildLog != null)
                {
                    buildLog.vert(ev.pt.x);
                }

                if (ev.isStart)
                {
                    if (buildLog != null)
                    {
                        buildLog.segmentNew(ev.seg, ev.primary);
                    }

                    var surrounding = statusFindSurrounding(ev);
                    var above       = surrounding.before != null ? surrounding.before : null;
                    var below       = surrounding.after != null ? surrounding.after : null;

                    if (buildLog != null)
                    {
                        buildLog.tempStatus(
                            ev.seg,
                            above != null ? above.seg : (object)false,
                            below != null ? below.seg : (object)false
                            );
                    }

                    var eve = checkBothIntersections(ev, above, below);
                    if (eve != null)
                    {
                        // ev and eve are equal
                        // we'll keep eve and throw away ev

                        // merge ev.seg's fill information into eve.seg

                        if (selfIntersection)
                        {
                            var toggle = false;                             // are we a toggling edge?
                            if (ev.seg.myFill.below == null)
                            {
                                toggle = true;
                            }
                            else
                            {
                                toggle = ev.seg.myFill.above != ev.seg.myFill.below;
                            }

                            // merge two segments that belong to the same polygon
                            // think of this as sandwiching two segments together, where `eve.seg` is
                            // the bottom -- this will cause the above fill flag to toggle
                            if (toggle)
                            {
                                eve.seg.myFill.above = !eve.seg.myFill.above;
                            }
                        }
                        else
                        {
                            // merge two segments that belong to different polygons
                            // each segment has distinct knowledge, so no special logic is needed
                            // note that this can only happen once per segment in this phase, because we
                            // are guaranteed that all self-intersections are gone
                            eve.seg.otherFill = ev.seg.myFill;
                        }

                        if (buildLog != null)
                        {
                            buildLog.segmentUpdate(eve.seg);
                        }

                        ev.other.remove();
                        ev.remove();
                    }

                    if (event_root.head != ev)
                    {
                        // something was inserted before us in the event queue, so loop back around and
                        // process it before continuing
                        if (buildLog != null)
                        {
                            buildLog.rewind(ev.seg);
                        }

                        continue;
                    }

                    //
                    // calculate fill flags
                    //
                    if (selfIntersection)
                    {
                        var toggle = false;                         // are we a toggling edge?

                        // if we are a new segment...
                        if (ev.seg.myFill.below == null)
                        {
                            // then we toggle
                            toggle = true;
                        }
                        else
                        {
                            // we are a segment that has previous knowledge from a division
                            toggle = ev.seg.myFill.above != ev.seg.myFill.below;                             // calculate toggle
                        }
                        // next, calculate whether we are filled below us
                        if (below == null)
                        {
                            // if nothing is below us...
                            // we are filled below us if the polygon is inverted
                            ev.seg.myFill.below = primaryPolyInverted;
                        }
                        else
                        {
                            // otherwise, we know the answer -- it's the same if whatever is below
                            // us is filled above it
                            ev.seg.myFill.below = below.seg.myFill.above;
                        }

                        // since now we know if we're filled below us, we can calculate whether
                        // we're filled above us by applying toggle to whatever is below us
                        if (toggle)
                        {
                            ev.seg.myFill.above = !ev.seg.myFill.below.Value;
                        }
                        else
                        {
                            ev.seg.myFill.above = ev.seg.myFill.below.Value;
                        }
                    }
                    else
                    {
                        // now we fill in any missing transition information, since we are all-knowing
                        // at this point

                        if (ev.seg.otherFill == null)
                        {
                            // if we don't have other information, then we need to figure out if we're
                            // inside the other polygon
                            var inside = false;
                            if (below == null)
                            {
                                // if nothing is below us, then we're inside if the other polygon is
                                // inverted
                                inside = ev.primary ? secondaryPolyInverted : primaryPolyInverted;
                            }
                            else
                            {
                                // otherwise, something is below us
                                // so copy the below segment's other polygon's above
                                if (ev.primary == below.primary)
                                {
                                    inside = below.seg.otherFill.above;
                                }
                                else
                                {
                                    inside = below.seg.myFill.above;
                                }
                            }

                            ev.seg.otherFill = new SegmentFill()
                            {
                                above = inside,
                                below = inside
                            };
                        }
                    }

                    if (buildLog != null)
                    {
                        buildLog.status(
                            ev.seg,
                            above != null ? above.seg : (object)false,
                            below != null ? below.seg : (object)false
                            );
                    }

                    // insert the status and remember it for later removal
                    ev.other.status = status_root.insert(surrounding, ev);
                }
                else
                {
                    var st = ev.status;

                    if (st == null)
                    {
                        throw new Exception("PolyBool: Zero-length segment detected; your epsilon is probably too small or too large");
                    }

                    // removing the status will create two new adjacent edges, so we'll need to check
                    // for those
                    if (status_root.exists(st.prev) && status_root.exists(st.next))
                    {
                        checkIntersection(st.prev.ev, st.next.ev);
                    }

                    if (buildLog != null)
                    {
                        buildLog.statusRemove(st.ev.seg);
                    }

                    // remove the status
                    st.remove();

                    // if we've reached this point, we've calculated everything there is to know, so
                    // save the segment for reporting
                    if (!ev.primary)
                    {
                        // make sure `seg.myFill` actually points to the primary polygon though
                        var s = ev.seg.myFill;
                        ev.seg.myFill    = ev.seg.otherFill;
                        ev.seg.otherFill = s;
                    }

                    segments.Add(ev.seg);
                }

                // remove the event and continue
                event_root.head.remove();
            }

            if (buildLog != null)
            {
                buildLog.done();
            }

            return(segments);
        }