Пример #1
0
        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());
        }