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); }
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); }