private int EventCompare(bool p1IsStart, Point p11, Point p12, bool p2IsStart, Point p21, Point p22) { // compare the selected points first int comp = PointUtils.PointsCompare(p11, p21); if (comp != 0) { return(comp); } // the selected points are the same if (PointUtils.PointsSame(p12, p22)) // if the non-selected points are the same too... { return(0); // then the segments are equal } if (p1IsStart != p2IsStart) // if one is a start and the other isn"t... { return(p1IsStart ? 1 : -1); // favor the one that isn"t the start } // otherwise, we"ll have to calculate which one is below the other manually return(PointUtils.PointAboveOrOnLine(p12, p2IsStart ? p21 : p22, // order matters p2IsStart ? p22 : p21 ) ? 1 : -1); }
private Node CheckIntersection(Node ev1, Node ev2) { // returns the segment equal to ev1, or false if nothing equal Segment seg1 = ev1.Seg; Segment seg2 = ev2.Seg; Point a1 = seg1.Start; Point a2 = seg1.End; Point b1 = seg2.Start; Point b2 = seg2.End; IntersectionPoint i = PointUtils.LinesIntersect(a1, a2, b1, b2); if (i == null) { // segments are parallel or coincident // if points aren"t collinear, then the segments are parallel, so no intersections if (!PointUtils.PointsCollinear(a1, a2, b1)) { return(null); } // otherwise, segments are on top of each other somehow (aka coincident) if (PointUtils.PointsSame(a1, b2) || PointUtils.PointsSame(a2, b1)) { return(null); // segments touch at endpoints... no intersection } bool a1EquB1 = PointUtils.PointsSame(a1, b1); bool a2EquB2 = PointUtils.PointsSame(a2, b2); if (a1EquB1 && a2EquB2) { return(ev2); // segments are exactly equal } bool a1Between = !a1EquB1 && PointUtils.PointBetween(a1, b1, b2); bool a2Between = !a2EquB2 && PointUtils.PointBetween(a2, b1, b2); if (a1EquB1) { if (a2Between) { // (a1)---(a2) // (b1)----------(b2) EventDivide(ev2, a2); } else { // (a1)----------(a2) // (b1)---(b2) EventDivide(ev1, b2); } return(ev2); } else if (a1Between) { if (!a2EquB2) { // make a2 equal to b2 if (a2Between) { // (a1)---(a2) // (b1)-----------------(b2) EventDivide(ev2, a2); } else { // (a1)----------(a2) // (b1)----------(b2) EventDivide(ev1, b2); } } // (a1)---(a2) // (b1)----------(b2) EventDivide(ev2, a1); } } else { // otherwise, lines intersect at i.pt, which may or may not be between the endpoints // is A divided between its endpoints? (exclusive) if (i.AlongA == 0) { if (i.AlongB == -1) // yes, at exactly b1 { EventDivide(ev1, b1); } else if (i.AlongB == 0) // yes, somewhere between B"s endpoints { EventDivide(ev1, i.Pt); } else if (i.AlongB == 1) // yes, at exactly b2 { EventDivide(ev1, b2); } } // is B divided between its endpoints? (exclusive) if (i.AlongB == 0) { if (i.AlongA == -1) // yes, at exactly a1 { EventDivide(ev2, a1); } else if (i.AlongA == 0) // yes, somewhere between A"s endpoints (exclusive) { EventDivide(ev2, i.Pt); } else if (i.AlongA == 1) // yes, at exactly a2 { EventDivide(ev2, a2); } } } return(null); }
private static List <Region> SegmentChainer(List <Segment> segments) { List <Region> regions = new List <Region>(); List <List <Point> > chains = new List <List <Point> >(); foreach (Segment seg in segments) { Point pt1 = seg.Start; Point pt2 = seg.End; if (PointUtils.PointsSame(pt1, pt2)) { Debug.WriteLine("PolyBool: Warning: Zero-length segment detected; your epsilon is " + "probably too small or too large"); continue; } // search for two chains that this segment matches Matcher firstMatch = new Matcher() { Index = 0, MatchesHead = false, MatchesPt1 = false }; Matcher secondMatch = new Matcher() { Index = 0, MatchesHead = false, MatchesPt1 = false }; Matcher nextMatch = firstMatch; Func <int, bool, bool, bool> setMatch = (index, matchesHead, matchesPt1) => { // return true if we've matched twice nextMatch.Index = index; nextMatch.MatchesHead = matchesHead; nextMatch.MatchesPt1 = matchesPt1; if (Equals(nextMatch, firstMatch)) { nextMatch = secondMatch; return(false); } nextMatch = null; return(true); // we've matched twice, we're done here }; for (int i = 0; i < chains.Count; i++) { List <Point> chain = chains[i]; Point head = chain[0]; Point tail = chain[chain.Count - 1]; if (PointUtils.PointsSame(head, pt1)) { if (setMatch(i, true, true)) { break; } } else if (PointUtils.PointsSame(head, pt2)) { if (setMatch(i, true, false)) { break; } } else if (PointUtils.PointsSame(tail, pt1)) { if (setMatch(i, false, true)) { break; } } else if (PointUtils.PointsSame(tail, pt2)) { if (setMatch(i, false, false)) { break; } } } if (Equals(nextMatch, firstMatch)) { // we didn't match anything, so create a new chain chains.Add(new List <Point> { pt1, pt2 }); continue; } if (Equals(nextMatch, secondMatch)) { // we matched a single chain // add the other point to the apporpriate end, and check to see if we've closed the // chain into a loop int index = firstMatch.Index; Point pt = firstMatch.MatchesPt1 ? pt2 : pt1; // if we matched pt1, then we add pt2, etc bool addToHead = firstMatch.MatchesHead; // if we matched at head, then add to the head List <Point> chain = chains[index]; Point grow = addToHead ? chain[0] : chain[chain.Count - 1]; Point grow2 = addToHead ? chain[1] : chain[chain.Count - 2]; Point oppo = addToHead ? chain[chain.Count - 1] : chain[0]; Point oppo2 = addToHead ? chain[chain.Count - 2] : chain[1]; if (PointUtils.PointsCollinear(grow2, grow, pt)) { // grow isn't needed because it's directly between grow2 and pt: // grow2 ---grow---> pt if (addToHead) { chain.Shift(); } else { chain.Pop(); } grow = grow2; // old grow is gone... new grow is what grow2 was } if (PointUtils.PointsSame(oppo, pt)) { // we're closing the loop, so remove chain from chains chains.Splice(index, 1); if (PointUtils.PointsCollinear(oppo2, oppo, grow)) { // oppo isn't needed because it's directly between oppo2 and grow: // oppo2 ---oppo--->grow if (addToHead) { chain.Pop(); } else { chain.Shift(); } } // we have a closed chain! if (chain == null) { throw new ArgumentNullException("chain"); } regions.Add(new Region() { Points = new List <Point>(chain) }); continue; } // not closing a loop, so just add it to the apporpriate side if (addToHead) { chain.Unshift(pt); } else { chain.Push(pt); } continue; } // otherwise, we matched two chains, so we need to combine those chains together Action <int> reverseChain = (index) => { chains[index].Reverse(); // gee, that's easy }; Action <int, int> appendChain = (index1, index2) => { // index1 gets index2 appended to it, and index2 is removed List <Point> chain1 = chains[index1]; List <Point> chain2 = chains[index2]; Point tail = chain1[chain1.Count - 1]; Point tail2 = chain1[chain1.Count - 2]; Point head = chain2[0]; Point head2 = chain2[1]; if (PointUtils.PointsCollinear(tail2, tail, head)) { // tail isn't needed because it's directly between tail2 and head // tail2 ---tail---> head chain1.Pop(); tail = tail2; // old tail is gone... new tail is what tail2 was } if (PointUtils.PointsCollinear(tail, head, head2)) { // head isn't needed because it's directly between tail and head2 // tail ---head---> head2 chain2.Shift(); } if (chain1 == null) { throw new ArgumentNullException(nameof(chain1)); } if (chain2 == null) { throw new ArgumentNullException(nameof(chain2)); } List <Point> ls = new List <Point>(chain1); ls.AddRange(chain2); chains[index1] = ls; chains.Splice(index2, 1); }; int f = firstMatch.Index; int s = secondMatch.Index; bool reverseF = chains[f].Count < chains[s].Count; // reverse the shorter chain, if needed if (firstMatch.MatchesHead) { if (secondMatch.MatchesHead) { if (reverseF) { // <<<< F <<<< --- >>>> S >>>> reverseChain(f); // >>>> F >>>> --- >>>> S >>>> appendChain(f, s); } else { // <<<< F <<<< --- >>>> S >>>> reverseChain(s); // <<<< F <<<< --- <<<< S <<<< logically same as: // >>>> S >>>> --- >>>> F >>>> appendChain(s, f); } } else { // <<<< F <<<< --- <<<< S <<<< logically same as: // >>>> S >>>> --- >>>> F >>>> appendChain(s, f); } } else { if (secondMatch.MatchesHead) { // >>>> F >>>> --- >>>> S >>>> appendChain(f, s); } else { if (reverseF) { // >>>> F >>>> --- <<<< S <<<< reverseChain(f); // <<<< F <<<< --- <<<< S <<<< logically same as: // >>>> S >>>> --- >>>> F >>>> appendChain(s, f); } else { // >>>> F >>>> --- <<<< S <<<< reverseChain(s); // >>>> F >>>> --- >>>> S >>>> appendChain(f, s); } } } } return(regions); }