private int eventCompare(bool p1_isStart, ref Vector2 p1_1, ref Vector2 p1_2, bool p2_isStart, ref Vector2 p2_1, ref Vector2 p2_2) { // compare the selected points first var comp = Epsilon.pointsCompare(p1_1, p2_1); if (comp != 0) { return(comp); } // the selected points are the same if (Epsilon.pointsSame(p1_2, p2_2)) // if the non-selected points are the same too... { return(0); // then the segments are equal } if (p1_isStart != p2_isStart) // if one is a start and the other isn't... { return(p1_isStart ? 1 : -1); // favor the one that isn't the start } // otherwise, we'll have to calculate which one is below the other manually return(Epsilon.pointAboveOrOnLine( p1_2, p2_isStart ? p2_1 : p2_2, // order matters p2_isStart ? p2_2 : p2_1 ) ? 1 : -1); }
private int statusCompare(EventNode ev1, EventNode ev2) { var a1 = ev1.seg.start; var a2 = ev1.seg.end; var b1 = ev2.seg.start; var b2 = ev2.seg.end; if (Epsilon.pointsCollinear(a1, b1, b2)) { if (Epsilon.pointsCollinear(a2, b1, b2)) { return(1); //eventCompare(true, a1, a2, true, b1, b2); } return(Epsilon.pointAboveOrOnLine(a2, b1, b2) ? 1 : -1); } return(Epsilon.pointAboveOrOnLine(a1, b1, b2) ? 1 : -1); }
private void appendChain(int index1, int index2) { // index1 gets index2 appended to it, and index2 is removed var chain1 = chains[index1]; var chain2 = chains[index2]; var tail = chain1[chain1.Count - 1]; var tail2 = chain1[chain1.Count - 2]; var head = chain2[0]; var head2 = chain2[1]; if (Epsilon.pointsCollinear(tail2, tail, head)) { // tail isn't needed because it's directly between tail2 and head // tail2 ---tail---> head if (buildLog != null) { buildLog.chainRemoveTail(index1, tail); } chain1.RemoveAt(chain1.Count - 1); tail = tail2; // old tail is gone... new tail is what tail2 was } if (Epsilon.pointsCollinear(tail, head, head2)) { // head isn't needed because it's directly between tail and head2 // tail ---head---> head2 if (buildLog != null) { buildLog.chainRemoveHead(index2, head); } chain2.RemoveAt(0); } if (buildLog != null) { buildLog.chainJoin(index1, index2); } chain1.AddRange(chain2); chains.RemoveAt(index2); }
public void addRegion(PointList region) { if (!selfIntersection) { throw new Exception("The addRegion() function is only intended for use when selfIntersection = false"); } // Ensure that the polygon is fully closed (the start point and end point are exactly the same) if (!Epsilon.pointsSame(region[region.Count - 1], region[0])) { region.Add(region[0]); } // regions are a list of points: // [ [0, 0], [100, 0], [50, 100] ] // you can add multiple regions before running calculate var pt1 = new Point(); var pt2 = region[region.Count - 1]; for (var i = 0; i < region.Count; i++) { pt1 = pt2; pt2 = region[i]; var forward = Epsilon.pointsCompare(pt1, pt2); if (forward == 0) // points are equal, so we have a zero-length segment { continue; // just skip it } eventAddSegment( segmentNew( forward < 0 ? pt1 : pt2, forward < 0 ? pt2 : pt1 ), true ); } }
public List <List <Vector2> > chain(SegmentList segments, BuildLog buildLog = null) { this.buildLog = buildLog; this.chains = new List <List <Vector2> >(); this.regions = new List <List <Vector2> >(); foreach (var seg in segments) { var pt1 = seg.start; var pt2 = seg.end; if (Epsilon.pointsSame(pt1, pt2)) { Console.WriteLine("PolyBool: Warning: Zero-length segment detected; your epsilon is probably too small or too large"); continue; } if (buildLog != null) { buildLog.chainStart(seg); } first_match = new Match() { index = 0, matches_head = false, matches_pt1 = false }; second_match = new Match() { index = 0, matches_head = false, matches_pt1 = false }; next_match = first_match; for (var i = 0; i < chains.Count; i++) { var chain = chains[i]; var head = chain[0]; var head2 = chain[1]; var tail = chain[chain.Count - 1]; var tail2 = chain[chain.Count - 2]; if (Epsilon.pointsSame(head, pt1)) { if (setMatch(i, true, true)) { break; } } else if (Epsilon.pointsSame(head, pt2)) { if (setMatch(i, true, false)) { break; } } else if (Epsilon.pointsSame(tail, pt1)) { if (setMatch(i, false, true)) { break; } } else if (Epsilon.pointsSame(tail, pt2)) { if (setMatch(i, false, false)) { break; } } } if (next_match == first_match) { // we didn't match anything, so create a new chain chains.Add(new List <Vector2>() { pt1, pt2 }); if (buildLog != null) { buildLog.chainNew(pt1, pt2); } continue; } if (next_match == second_match) { // we matched a single chain if (buildLog != null) { buildLog.chainMatch(first_match.index); } // add the other point to the apporpriate end, and check to see if we've closed the // chain into a loop var index = first_match.index; var pt = first_match.matches_pt1 ? pt2 : pt1; // if we matched pt1, then we add pt2, etc var addToHead = first_match.matches_head; // if we matched at head, then add to the head var chain = chains[index]; var grow = addToHead ? chain[0] : chain[chain.Count - 1]; var grow2 = addToHead ? chain[1] : chain[chain.Count - 2]; var oppo = addToHead ? chain[chain.Count - 1] : chain[0]; var oppo2 = addToHead ? chain[chain.Count - 2] : chain[1]; if (Epsilon.pointsCollinear(grow2, grow, pt)) { // grow isn't needed because it's directly between grow2 and pt: // grow2 ---grow---> pt if (addToHead) { if (buildLog != null) { buildLog.chainRemoveHead(first_match.index, pt); } chain.RemoveAt(0); } else { if (buildLog != null) { buildLog.chainRemoveTail(first_match.index, pt); } chain.RemoveAt(chain.Count - 1); } grow = grow2; // old grow is gone... new grow is what grow2 was } if (Epsilon.pointsSame(oppo, pt)) { // we're closing the loop, so remove chain from chains chains.RemoveAt(index); if (Epsilon.pointsCollinear(oppo2, oppo, grow)) { // oppo isn't needed because it's directly between oppo2 and grow: // oppo2 ---oppo--->grow if (addToHead) { if (buildLog != null) { buildLog.chainRemoveTail(first_match.index, grow); } chain.RemoveAt(chain.Count - 1); } else { if (buildLog != null) { buildLog.chainRemoveHead(first_match.index, grow); } chain.RemoveAt(0); } } if (buildLog != null) { buildLog.chainClose(first_match.index); } // we have a closed chain! regions.Add(chain); continue; } // not closing a loop, so just add it to the apporpriate side if (addToHead) { if (buildLog != null) { buildLog.chainAddHead(first_match.index, pt); } chain.Insert(0, pt); } else { if (buildLog != null) { buildLog.chainAddTail(first_match.index, pt); } chain.Add(pt); } continue; } // otherwise, we matched two chains, so we need to combine those chains together var F = first_match.index; var S = second_match.index; if (buildLog != null) { buildLog.chainConnect(F, S); } var reverseF = chains[F].Count < chains[S].Count; // reverse the shorter chain, if needed if (first_match.matches_head) { if (second_match.matches_head) { 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 (second_match.matches_head) { // >>>> 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); }
private EventNode checkIntersection(EventNode ev1, EventNode ev2) { // returns the segment equal to ev1, or false if nothing equal var seg1 = ev1.seg; var seg2 = ev2.seg; var a1 = seg1.start; var a2 = seg1.end; var b1 = seg2.start; var b2 = seg2.end; if (buildLog != null) { buildLog.checkIntersection(seg1, seg2); } Intersection intersect; if (!Epsilon.linesIntersect(a1, a2, b1, b2, out intersect)) { // segments are parallel or coincident // if points aren't collinear, then the segments are parallel, so no intersections if (!Epsilon.pointsCollinear(a1, a2, b1)) { return(null); } // otherwise, segments are on top of each other somehow (aka coincident) if (Epsilon.pointsSame(a1, b2) || Epsilon.pointsSame(a2, b1)) { return(null); // segments touch at endpoints... no intersection } var a1_equ_b1 = Epsilon.pointsSame(a1, b1); var a2_equ_b2 = Epsilon.pointsSame(a2, b2); if (a1_equ_b1 && a2_equ_b2) { return(ev2); // segments are exactly equal } var a1_between = !a1_equ_b1 && Epsilon.pointBetween(a1, b1, b2); var a2_between = !a2_equ_b2 && Epsilon.pointBetween(a2, b1, b2); // handy for debugging: // buildLog.log({ // a1_equ_b1: a1_equ_b1, // a2_equ_b2: a2_equ_b2, // a1_between: a1_between, // a2_between: a2_between // }); if (a1_equ_b1) { if (a2_between) { // (a1)---(a2) // (b1)----------(b2) eventDivide(ev2, a2); } else { // (a1)----------(a2) // (b1)---(b2) eventDivide(ev1, b2); } return(ev2); } else if (a1_between) { if (!a2_equ_b2) { // make a2 equal to b2 if (a2_between) { // (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 (intersect.alongA == 0) { if (intersect.alongB == -1) // yes, at exactly b1 { eventDivide(ev1, b1); } else if (intersect.alongB == 0) // yes, somewhere between B's endpoints { eventDivide(ev1, intersect.pt); } else if (intersect.alongB == 1) // yes, at exactly b2 { eventDivide(ev1, b2); } } // is B divided between its endpoints? (exclusive) if (intersect.alongB == 0) { if (intersect.alongA == -1) // yes, at exactly a1 { eventDivide(ev2, a1); } else if (intersect.alongA == 0) // yes, somewhere between A's endpoints (exclusive) { eventDivide(ev2, intersect.pt); } else if (intersect.alongA == 1) // yes, at exactly a2 { eventDivide(ev2, a2); } } } return(null); }