七巧板复原之剩余区域的计算-多边形的加减法计算

按:这个区域就是之前小文提到的空腔,后来笔者经过抽象,发现其实这是个多边形加减法的问题(不知道计算机图形学里有没有专门的叫法)

多边形的减法

如图示
在这里插入图片描述

图中G1是已经放好的部分,Part是刚刚摆放好的部件,我们要把G2用多边形的点的集合表达出来。也就是要把ABCDEFHG各个点的坐标求出来。(说明,G2可能并不是唯一的,也就是说空腔不止一个)。 下面给出了计算Part的非重合边 与 box 多边形的边围成的多边形的算法,这是我们要使用的剩余的主要区域,其他区域也快吃参照这种算法进行计算,但是我们暂时忽略。(其实其他空腔可以通过算法排除掉,或者认为出现这种空腔是一种不合理的方法,本算法就是采用了这种方式。)

根据上文的匹配方法,Part 的第一条边和G1的某一边是重合的,并且有一个公共的起始点。我们从这个起点开始计算,利用边线关系,确定 A点,也就是G2的初始点,然后烟逆时针方向遍历G2 的其余点,再利用边线关系确定F点, 再在Part上沿顺时针方向遍历Part的顶点一直到 A点, 这样就找到了G2的所有顶点。

判断A点和F点比较复杂,有各种情况,笔者简单总结了一下,如图示:
在这里插入图片描述
AB 是PArt上的线段, CD是G2 上的线段,上图基本覆盖了线段AB和CD的各种位置关系, 其中18 是比较特殊的关系,本算法中用的部件不会出现这种情况,因此没有考虑。图中 12 和7 , 11 和16 是一样的, 顾只采用其中的一种即可。由于在本算法中,采用了有向线段,因此可以根据AB和CD的方向,确定G2的A点和F点。
下面是多边形减法主程序代码:

       /// <summary>
        ///  get all of the new plygons enclosed by the 2 input polygpons which just have the common side and the 
        ///  parttial polygong is inside the box polygon. 
        ///  In this version the new polygon will be "trimed" that the segments on have the same slope and share the same vertex will be merged to one segment.
        /// </summary>
        /// <param name="partPlg"> the parts polygon which is a subset of the box polygon</param>
        /// <param name="boxPlg"> The bigger polygon which enclose partial polygon</param>
        /// <returns></returns>
        public static List<TangramPolygon> GetNewPolygonV1(TangramPolygon partPlg, TangramPolygon boxPlg,bool border=true)
        {
            List<TangramPolygon> newPlgLst = new List<TangramPolygon>();// the result of the new polygon , the number of it is not fixed theoritically 
            List<KeyValuePair<int, int>> UnCoincidentSegLst = new List<KeyValuePair<int, int>>();// point index on part polygon , side index on box polygon
            // get those vertexes of part polygon on the side of the box polygon
            List<PointF> newPolygonPntLst = new List<PointF>();
            int partIdx = partPlg.firstPntIdx;
            //var bFindStPartPnt = false;// the first point in the part polygon of the new polygon has been found
            //var bFindEndPartPnt = false;// the second point in the part polygon of the new polygon has been found
            //var bFindEndBoxPnt = false;
            int boxIdx;
            boxIdx = boxPlg.firstPntIdx;
            //1# find the segment in part polygon 
            //newPolygonPntLst = GetPntsBetween2PlgOld(partPlg, ref partIdx, boxPlg, ref boxIdx);
            newPolygonPntLst = GetPntsBetween2Plg(partPlg, ref partIdx, boxPlg, ref boxIdx,border);

            PointF[] pntArr = newPolygonPntLst.ToArray();
            if (pntArr.Length != 0)
            {
                TangramPolygon tplg = new TangramPolygon(pntArr, false);
                // merge the segments which have the same slop and point.
                
                tplg = MergeSegment(tplg);// here is the difference compared with the old version. 

                newPlgLst.Add(tplg);
            }
            return newPlgLst;

        }

GetPntsBetween2Plg 函数就是上面算法的具体实现,代码如下:

        /// <summary>
        ///  Get the the points between 2 polygons, starts from an known coinsected  segment of the 2 plygons. 
        /// </summary>
        /// <param name="part"></param>
        /// <param name="partIdx"> the coinsected segment index in polygon 1 </param>
        /// <param name="box"></param>
        /// <param name="boxIdx"></param>
        /// <param name="bSub"> true, plygon substract, false: add  </param>
        /// <returns> The points of the new plolygon in a list</returns>
        private static List<PointF> GetPntsBetween2Plg(TangramPolygon part, ref int partIdx, TangramPolygon box, ref int boxIdx,bool bSub = true)
        {
            List<PointF> newPntLst = new List<PointF>();
            bool bFindStartPnt = false;
            bool bFindEndPnt = false;
            int partStIdx, partEndIdx, boxStIdx, boxEndIdx;
            boxStIdx = boxIdx;
            boxEndIdx = boxIdx;
            bool bOrder = true;
            bool bSearchAll = false;// false: only return the end point, true: all the point 
            int searchIdx = box.firstPntIdx;
            int lastCaseNo = -1;
            var caseNo0 = -1;
            var caseNo1 = -1;
            var caseNo2 = -1;
            var caseNo3 = -1;
            for (int i = 0; i < part.Count(); i++)
            {
                caseNo0 = -1;
                caseNo1 = -1;
                caseNo2 = -1;
                caseNo3 = -1;
                PointF A = part.GetVertexPoint(partIdx);// postive order 
                PointF B = part.GetNextVertexPoint(partIdx);
                var segCaseLst = PointsCoincidePlgSeg(new Segment(A, B), box, searchIdx, bSub, false);
                
                if (segCaseLst.Count > 0)
                {
                    searchIdx = segCaseLst[0].idx;
                    caseNo0 = segCaseLst[0].no; 
                }
               
                

                if (segCaseLst.Count > 1)
                {
                    caseNo1 = segCaseLst[1].no;

                }
                if (segCaseLst.Count > 2)
                {
                    caseNo2 = segCaseLst[2].no;

                }
                if (segCaseLst.Count > 3)
                {
                    caseNo3 = segCaseLst[3].no;

                }
                if (!(lastCaseNo == 2 || lastCaseNo == 5 || lastCaseNo == 7 || lastCaseNo == 8 || lastCaseNo == 9) && lastCaseNo != -1)
                {
                    searchIdx = box.GetVertex(searchIdx).next_idx;
                }
                var caseHeadNo = string.Format("{0}-{1}", caseNo0, caseNo1);
                var caseTailNo = string.Format("{0}-{1}", caseNo2, caseNo3);
                lastCaseNo = caseNo0;
                if (!bFindStartPnt)
                {
                    bOrder = true;
                    bSearchAll = false;
                    // check the first element by far
                    if (caseHeadNo == "2-5" || caseHeadNo == "5-2")// find startPoint  and end point , 5 (A)is the start point , but the idx is different 
                    {

                        bFindStartPnt = true;
                        newPntLst.Add(A);
                        if (caseNo1 == 5)
                        {
                            boxStIdx = segCaseLst[1].idx;
                            boxEndIdx = segCaseLst[0].idx;
                        }
                        else
                        {
                            boxStIdx = segCaseLst[0].idx;
                            boxEndIdx = segCaseLst[1].idx;
                        }
                        bFindEndPnt = true;
                        newPntLst.Add(B);
                       

                    }
                    else if (caseHeadNo == "3-1")
                    {
                        if (caseNo2 == 5)// find startPoint  and end point , 5 is the start point 
                        {
                            bFindStartPnt = true;
                            newPntLst.Add(A);
                            boxStIdx = segCaseLst[2].idx;
                            bFindEndPnt = true;
                            newPntLst.Add(B);
                            boxEndIdx = segCaseLst[0].idx;

                        }
                    }
                    
                    else  if (caseHeadNo == "6-4")// find startPoint 
                    {
                        bFindStartPnt = true;
                        newPntLst.Add(A);
                        boxStIdx = segCaseLst[0].idx;
                    }
                    else
                    {
                        if (caseNo0 == 4 || caseNo0 == 5 || caseNo0 == 6 || caseNo0 == 11 || caseNo0 == 14 || caseNo0 == 15)
                        {

                            if (caseNo0 == 4 || caseNo0 == 6)
                            {
                                if (caseNo1 == 8 || caseNo1 == 17)
                                {
                                    partIdx = part.GetVertex(partIdx).next_idx;
                                    continue;
                                }
                                if(caseNo1 == 15)// D 
                                {
                                    var D = segCaseLst[1].casePntLst[2].Value;
                                    newPntLst.Add(D);
                                    bFindStartPnt = true;
                                    boxStIdx = segCaseLst[1].idx;
                                    partIdx = part.GetVertex(partIdx).next_idx;
                                    continue;

                                }
                            }


                            bFindStartPnt = true;
                            boxStIdx = segCaseLst[0].idx;
                            if (caseNo0 == 4 || caseNo0 == 5 || caseNo0 == 6)
                            {
                                newPntLst.Add(A);
                            }
                            else //if (caseNo == 11 || caseNo == 14 ||caseNo == 15))
                            {
                                var D = segCaseLst[0].casePntLst[2].Value;
                                newPntLst.Add(D);
                            }

                        }
                    }
                    if (caseTailNo == "3-1")// find endPoint 
                    {
                        bFindEndPnt = true;
                        newPntLst.Add(B);
                        boxEndIdx = segCaseLst[2].idx;
                    }
                    else if (caseTailNo == "13-1")// find endPoint
                    {
                        bFindEndPnt = true;
                        var C = segCaseLst[2].casePntLst[1].Value;
                        newPntLst.Add(C);
                        boxEndIdx = segCaseLst[2].idx;
                    }else if (caseTailNo == "2--1")// find endPoint -1 means not found 
                    {
                        bFindEndPnt = true;
                        B = segCaseLst[2].casePntLst[1].Value;

                        newPntLst.Add(B);
                        boxEndIdx = segCaseLst[2].idx;
                    }
                    else if(bFindStartPnt && ! bFindEndPnt)
                    {
                        // check the second element for end poing check 
                        if (segCaseLst.Count > 1)
                        {
                            caseNo1 = segCaseLst[1].no;
                            if (caseNo1 == 1 || caseNo1 == 2 || caseNo1 == 3 || caseNo1 == 7 || caseNo1 == 13 || caseNo1 == 14)
                            {
                                if (caseNo1 == 1 || caseNo1 == 3)
                                {
                                    if (caseNo0 == 10 || caseNo0 == 13 || caseNo0 == 17)
                                    {
                                        partIdx = part.GetVertex(partIdx).next_idx;
                                        continue;
                                    }
                                }
                                bFindEndPnt = true;
                                boxEndIdx = segCaseLst[1].idx;
                                if (caseNo1 == 1 || caseNo1 == 2 || caseNo1 == 3)
                                {
                                    newPntLst.Add(B);
                                }
                                else
                                {
                                    var C = segCaseLst[1].casePntLst[1].Value;
                                    newPntLst.Add(C);
                                }
                            }
                        }
                    }

                }
                else if (!bFindEndPnt)
                {
                    newPntLst.Add(A);

                    if (caseNo0 == 0)
                    {
                        partIdx = part.GetVertex(partIdx).next_idx;
                        continue;
                    }
                    bOrder = true;

                    if (caseNo0 == 1 || caseNo0 == 2 || caseNo0 == 3 || caseNo0 == 7 || caseNo0 == 13 || caseNo0 == 14)
                    {
                        if (caseNo0 == 1 || caseNo0 == 3)
                        {
                            //if (caseNo1 == 10 || caseNo1 == 13 || caseNo1 == 17)
                            if (caseNo1 == 10 || caseNo1 == 17)
                            {
                                partIdx = part.GetVertex(partIdx).next_idx;
                                continue;
                            }
                        }
                        bFindEndPnt = true;
                        if (caseNo1 == 13)
                        {
                            boxEndIdx = segCaseLst[1].idx;
                        }
                        else
                        {
                            boxEndIdx = segCaseLst[0].idx;
                        }
                        
                        //if the intersection is on the vertex of the boxPlg, the outBoxIdx should move to its previous one 
                        var boxPnt = box.GetVertexPoint(boxEndIdx);

                        if (B == boxPnt)
                        {
                            if (bSub)
                            {
                                boxEndIdx = box.GetVertex(boxEndIdx).last_idx;
                            }
                            //else
                            //{
                            //    boxEndIdx = box.GetVertex(boxEndIdx).next_idx;

                            //}
                        }
                        if (caseNo0 == 1 || caseNo0 == 2 || caseNo0 == 3)
                        {
                            if(caseNo1 == 7 || caseNo1 == 13 || caseNo1 == 14)
                            {
                                var C = segCaseLst[1].casePntLst[1].Value;
                                newPntLst.Add(C);
                               
                            }
                            else
                            {
                                newPntLst.Add(B);
                            }
                           
                        }
                        else
                        {
                            var C = segCaseLst[0].casePntLst[1].Value;
                            newPntLst.Add(C);
                        }
                    }
                }
                if (bFindEndPnt) break;

                partIdx = part.GetVertex(partIdx).next_idx;

            }
            // build the new point list based one start and end index...
            boxIdx = boxEndIdx;
            for (int i = 0; i < box.Count(); i++)
            {
                var boxPnt = box.GetVertexPoint(boxIdx);
                if (!bSub)// plus 
                {
                    boxIdx = box.GetVertex(boxIdx).next_idx;
                    boxPnt = box.GetVertexPoint(boxIdx);
                }
                if (boxIdx != boxStIdx)
                {
                    //newPntLst.Add(boxPnt);
                    int findIdx = newPntLst.FindIndex(p => p.X == boxPnt.X && p.Y == boxPnt.Y);
                    if (findIdx == -1)
                    {
                        newPntLst.Add(boxPnt);
                    }
                }
                else
                {
                    //var stPnt = newPntLst[0];
                    //if(!(stPnt.X == boxPnt.X && stPnt.Y == boxPnt.Y))// if the end point of the last segment is not equal the first point , add it to list.
                    //{
                    //    newPntLst.Add(boxPnt);
                    //    break;// finished 
                    //}
                    if (!bSub)// plus 
                    {
              
                        int findIdx = newPntLst.FindIndex(p => p.X == boxPnt.X && p.Y == boxPnt.Y);
                        if (findIdx == -1)
                        {
                            newPntLst.Add(boxPnt);
                        }

                    }
                    break;
                }

                if (bSub)//substract 
                {
                    boxIdx = box.GetVertex(boxIdx).last_idx;
                }
               
            }
            //if (newPntLst.Count < 3)
            //{
            //    throw new Exception("Calculation error!");
            //}
            return newPntLst;
        }

说明:函数支持多边形加法和减法,通过参数控制。
MergeSegment(tplg): 该函数的作用是将在同一条线段上的点合并,只保留最长线段的的端点,代码如下:

      /// <summary>
        ///  Merge the segments to one segments if they are adjancent and have the same slop
        /// </summary>
        /// <param name="newPlg"></param>
        /// <returns></returns>
        private static TangramPolygon MergeSegment(TangramPolygon plg)
        {
            List<PointF> pntLst = new List<PointF>();
            var idx = plg.GetVertex(0).idx;
            for (int i = 0; i < plg.Count(); i++)
            {
                bool slopAB_EQ_BC = false; 

                var A = plg.GetLastVertexPoint(idx);
                var B = plg.GetVertexPoint(idx); 
                var C = plg.GetNextVertexPoint(idx);
                if(A.X == B.X && B.X == C.X)
                {
                    slopAB_EQ_BC = true;
                }
                else if(A.X != B.X && B.X != C.X)
                {
                    slopAB_EQ_BC = (B.Y - A.Y) / (B.X - A.X) == (C.Y - B.Y) / (C.X - B.X);
                }
                if (!slopAB_EQ_BC)
                {
                    pntLst.Add(B);
                }
                idx = plg.GetVertex(idx).next_idx;
  
            }
            if(pntLst.Count == plg.Count())
            {
                return plg;
            }
            else
            {
                return new TangramPolygon(pntLst.ToArray());
            }
        }

至此,核心计算程序已经叙述完毕,下面将讨论如何回溯的方法查找七巧板的大部分解决方法。

maraSun 于BJFJDQ。
北京仍然在尝试不使用上海用过的方法处理北京的特殊问题,但是事实上是殊途同归。
海淀小区核酸连续不中断检查到20多天了,不知道何时算个完。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值