public static Vector[] WeilerAthertonClipping(Vector[] _ClipReg, Func <Vector, bool> IsInClipReg, Vector[] _SubjPoly) { // ========================= // checking the input // ========================= if (_ClipReg.Length < 3) { throw new ArgumentException(); } if (_SubjPoly.Length < 3) { throw new ArgumentException(); } foreach (Vector v in _ClipReg) { if (v.Dim != 2) { throw new ArgumentException(); } if (!IsInClipReg(v)) { throw new ArgumentException(); } } foreach (Vector v in _SubjPoly) { if (v.Dim != 2) { throw new ArgumentException(); } } // Given polygon A as the clipping region and polygon B as the subject polygon to be clipped, // the algorithm consists of the following steps: // // 1. List the vertices of the clipping-region polygon A and those of the subject polygon B. // 2. Label the listed vertices of subject polygon B as either inside or outside of clipping region A. // 3. Find all the polygon intersections and insert them into both lists, linking the lists at the intersections. // 4. Generate a list of "inbound" intersections – the intersections where the vector from the intersection // to the subsequent vertex of subject polygon B begins inside the clipping region. // 5. Follow each intersection clockwise around the linked lists until the start position is found. // // If there are no intersections then one of three conditions must be true: // i. A is inside B – return A for clipping, B for merging. // ii. B is inside A – return B for clipping, A for merging. // iii. A and B do not overlap – return None for clipping or A & B for merging. // // (from https://en.wikipedia.org/wiki/Weiler%E2%80%93Atherton_clipping_algorithm) // ================= // step 1: // ================= //List<Vector> ClipReg = _ClipReg.ToList(); //List<Vector> SubjPoly = _SubjPoly.ToList(); var ClipReg = PolygonList.FromEnum(_ClipReg); var SubjPoly = PolygonList.FromEnum(_SubjPoly); _ClipReg = null; // avoid accidental use _SubjPoly = null; // avoid accidental use // ================= // step 2: // ================= //List<int> SubjInside = SubjPoly.Select(X => IsInClipReg(X) ? 1 : -1).ToList(); bool AllInside = true; bool AllOutSide = true; for (var node = SubjPoly; node != null; node = node.next) { bool isin = IsInClipReg(node.v); node.inside = isin ? 1 : -1; AllInside = AllInside && isin; AllOutSide = AllOutSide && !isin; } for (var node = ClipReg; node.next != null; node = node.next) { node.inside = 1; } //if (SubjInside.All(node => node.ins > 0)) if (AllInside) { // case (ii) return(SubjPoly.ToVecArray()); } if (AllOutSide) { // case (iii) // for the moment, we ignore case (i) return(null); } // ================= // step 3: // ================= //List<MyTuple> links = new List<MyTuple>(); //for(int iEdgeSub = 1; iEdgeSub <= SubjPoly.Count; iEdgeSub++) { for (var node_S = SubjPoly; node_S != null; node_S = node_S.next) { //Vector V1 = SubjPoly[iEdgeSub - 1]; //Vector V2 = SubjPoly[iEdgeSub % SubjPoly.Count]; var V1 = node_S; var V2 = node_S.next != null ? node_S.next : SubjPoly; List <Tuple <double, PolygonList> > Alpha1sAndNodes = new List <Tuple <double, PolygonList> >(); // test against all edges //for(int iEdgeClip = 1; iEdgeClip <= ClipReg.Count; iEdgeClip++) { for (var node_C = ClipReg; node_C.next != null; node_C = node_C.next) { //Vector S1 = ClipReg[iEdgeClip - 1]; //Vector S2 = ClipReg[iEdgeClip % ClipReg.Count]; var S1 = node_C; var S2 = node_C.next != null ? node_C.next : ClipReg; bool Intersect = ComputeIntersection(V1.v, V2.v, S1.v, S2.v, out double alpha1, out double alpha2, out Vector I); if (Intersect) { if (alpha1 >= 0.0 && alpha1 <= 1.0 && alpha2 >= 0.0 && alpha2 <= 1.0) { //int iEdgeSubInsert; PolygonList SubjInsert; if (alpha1 <= 0.0) { SubjInsert = V1; SubjInsert.inside = 0; } else if (alpha1 >= 1.0) { SubjInsert = V2; SubjInsert.inside = 0; } else { //iEdgeSubInsert = iEdgeSub; //SubjPoly.Insert(iEdgeSub, I); //iEdgeSub++; SubjInsert = V1.InsertAfter(I); SubjInsert.inside = 0; node_S = SubjInsert; Alpha1sAndNodes.Add(new Tuple <double, PolygonList>(alpha1, SubjInsert)); } //ClipReg.Insert(iEdgeClip, I); // //for(int i = 0; i < links.Count; i++) { // if (links[i].iClip_2 >= iEdgeClip) { // var a = links[i]; // a.iClip_2++; // links[i] = a; // } //} //Debug.Assert(links.Count <= 0 || iEdgeSubInsert > links.Max(tt => tt.iSubj_1)); //links.Add(new MyTuple(iEdgeSubInsert, iEdgeClip)); //SubjInside.Insert(iEdgeSub, 0); //iEdgeClip++; PolygonList ClipInsert; if (alpha2 <= 0.0) { ClipInsert = S1; } else if (alpha2 >= 1.0) { ClipInsert = S2; } else { //iEdgeSubInsert = iEdgeSub; //SubjPoly.Insert(iEdgeSub, I); //iEdgeSub++; ClipInsert = S1.InsertAfter(I); node_C = ClipInsert; } ClipInsert.crossJoin = SubjInsert; SubjInsert.crossJoin = ClipInsert; } else { // somewhere outside } } else { // parallel } } // multiple cuts per edge are probably not handeled correctly { if (Alpha1sAndNodes.Count > 1) { for (int i = 1; i < Alpha1sAndNodes.Count; i++) { double alpha1_prev = Alpha1sAndNodes[i - 1].Item1; double alpha1 = Alpha1sAndNodes[i].Item1; if (alpha1_prev >= alpha1) { throw new ArithmeticException("nodes not inserted in correct order - todo: implement resorting"); } } } } // ================= // step 4 & 5 // ================= if (SubjPoly.Where(node => node.inside == 0).Count() % 2 != 0) { throw new ArithmeticException("un-even number of intersections."); } PolygonList start = SubjPoly; while (start.inside <= 0) { start = start.next; } int Lmax = SubjPoly.Count() + ClipReg.Count(); SubjPoly.Tail.next = SubjPoly; ClipReg.Tail.next = ClipReg; List <Vector> R = new List <Vector>(); PolygonList current = start; int lcur = 0; do { R.Add(current.v); PolygonList next; if (current.crossJoin != null) { next = current.crossJoin.next; } else { next = current.next; } current = next; if (lcur > Lmax) { throw new ApplicationException("running into infinity loop."); } lcur++; } while (!object.ReferenceEquals(start, current)); //Debug.Assert(SubjPoly.Count == SubjInside.Count); //Debug.Assert(SubjInside.Where(b => b > 0).Count() >= 1); /* * int iStart = -1; * for(int i = 0; i < SubjInside.Count; i++) { * if(SubjInside[i] > 0) { * iStart = i; * break; * } * } * * List<Vector> R = new List<Vector>(); * * int iCurrent = iStart; * bool bOnSub = true; * //int iNext = -1; * int linksPointer = 0; * Debug.Assert(linksPointer >= links.Count || iCurrent < links[linksPointer].iSubj_1); * * do { * int L; * if (bOnSub) { * R.Add(SubjPoly[iCurrent]); * L = SubjPoly.Count; * } else { * R.Add(ClipReg[iCurrent]); * L = ClipReg.Count; * } * * int iNext = iCurrent; * iNext++; * if (iNext >= L) * iNext = 0; * * if(bOnSub) { * Debug.Assert(SubjInside[iNext] >= 0); * Debug.Assert(linksPointer >= links.Count || iCurrent < links[linksPointer].iSubj_1); * * if(linksPointer < links.Count && iNext == links[linksPointer].iSubj_1) { * // switch to clipping polygon * * Debug.Assert(SubjInside[iNext] == 0); * iNext = links[linksPointer].iClip_2; * bOnSub = false; * linksPointer++; * } * * * } else { * Debug.Assert(linksPointer < links.Count); // we are on clipping poly; ther must be still one link that brings us back on subject poly * //Debug.Assert(iCurrent < links[linksPointer].iClip_2); * * if(iNext == links[linksPointer].iClip_2) { * iNext = links[linksPointer].iSubj_1; * bOnSub = true; * linksPointer++; * } * } * * iCurrent = iNext; * * } while (!bOnSub || iCurrent != iStart); * */ // ================= // return // ================= return(R.ToArray()); }