// 传入的多边形的点需要封闭,即首尾点要相同 public static List<List<VertexBase>> Cut(List<VertexBase> listS, List<VertexBase> listC) { //如果是浮点数,使用这个处理保证相等比较正确 PrepareVertex(listS); PrepareVertex(listC); int cutStartIdx = 0;//实际切割由这个idx起始即可 // 阶段1: 这个循环中的切割用于判断方向 // 如果这步中没有发现交点,则进入不相交多边形的处理 for (; cutStartIdx < listS.Count; cutStartIdx++) { //用实体多边形的一条边去切切割多边形 var s1 = listS[cutStartIdx % listS.Count]; var s2 = listS[(cutStartIdx + 1) % listS.Count]; Tuple<CrossInOut, int, bool> ret; if (s1.X == s2.X) ret = CutByLineVerticalForCrossDi(s1, s2, listC, false); else ret = CutByLineForCrossDi(s1, s2, listC, false); //如果没有交点,继续下一条边。 if (!ret.Item3) continue; var interDirect1 = ret.Item1;//交点对于切割多边形的进出性 var cutLineIdx = ret.Item2;//交点所在的切割多边形的边的起点的索引,用于在使用切割多边形边去切实体多边形时,确定那条边 WindowLog.Default.Log("得到S多边形边{0}{1}切割C多边形{2}{3}产生的交点,对于S进出性为{4}", s1.Name, s2.Name, listC[cutLineIdx % listC.Count].Name, listC[(cutLineIdx + 1) % listC.Count].Name, interDirect1 == CrossInOut.In ? "进" : "出"); //用切割多边形的一条边(cutLineIdx->cutLineIdx+1)去切实体多边形 var ret2 = CutByLineForCrossDi(listC[cutLineIdx % listC.Count], listC[(cutLineIdx + 1) % listC.Count], listS, true, cutStartIdx); var interDirect2 = ret2.Item1; WindowLog.Default.Log("得到C多边形边{0}{1}切割S多边形{2}{3}产生的交点,对于C进出性为{4}", listC[cutLineIdx % listC.Count].Name, listC[(cutLineIdx + 1) % listC.Count].Name, s1.Name, s2.Name, interDirect2 == CrossInOut.In ? "进" : "出"); if (interDirect1 == interDirect2) //进出性相同表示多边形不同向,反转其中一个多边形 { WindowLog.Default.Log("交点进出性相同,把C多边形反向"); //文档中是把S进行了反向,但这里不行,如果反向S,则记录的第一个交点(主要是记录这个交点的进出性)就不再是第一个交点了 //所以实际实现中需要将C反向,而且反向后第一条边,仍然需要是确定第一点那条线段 var listCReverse = new List<VertexBase>(); var reverseStartIdx = cutLineIdx + 1; for (int i = 0; i < listC.Count; i++) { listCReverse.Add(listC[(reverseStartIdx - i + listC.Count) % listC.Count]); } listC = listCReverse; WindowLog.Default.ReversePolygon(); WindowLog.Default.Log("反向后多边形点序列为:{0}", string.Join("->", listC.Select(r => r.Name))); } WindowLog.Default.Log("使用S多边形边{0}{1}与C多边形{2}{3}交点为第一个点,对于S进出性为{4}", s1.Name, s2.Name, listC[cutLineIdx % listC.Count].Name, listC[(cutLineIdx + 1) % listC.Count].Name, interDirect1 == CrossInOut.In ? "进" : "出"); _firstInterDi = ret.Item1; break; } if (cutStartIdx == listS.Count)//没有交点 { WindowLog.Default.Log("没有交点,进入无交点情况处理"); var ret = ProcessNoCross(listS, listC); return ret == null ? new List<List<VertexBase>>() : new List<List<VertexBase>> { ret }; } // 阶段2: 链接多边形,即设置Next LinkNode(listS.Cast<Vertex>().ToList()); LinkNode(listC.Cast<Vertex>().ToList()); var listI = new List<Intersection>(); var linkC = new LinkedList<VertexBase>(listC); //循环中用S中每条边切割C(准确说是C的链表,每次切割后交点插入C的列表再进行下次切割),把交点插入S和C形成多边形链表 for (; cutStartIdx < listS.Count; cutStartIdx++) { var s1 = listS[cutStartIdx % listS.Count] as Vertex; var s2 = listS[(cutStartIdx + 1) % listS.Count] as Vertex; WindowLog.Default.Log("---------------使用S多边形边{0}{1}切割C多边形---------------", s1.Name, s2.Name); var inters = CutByLine(s1, s2, linkC); //var inters = ret; if (inters.Count == 0) continue; listI.AddRange(inters); //把交点排序,准备插入S的边中 if (s1.X < s2.X) inters.Sort((p1, p2) => p1.X.CompareTo(p2.X)); else inters.Sort((p1, p2) => -(p1.X.CompareTo(p2.X))); //将交点插入S的边中 s1.Next = inters[0]; for (int j = 0; j < inters.Count - 1; j++) { inters[j].NextS = inters[j + 1]; } inters[inters.Count - 1].NextS = s2; #region log var sLinkSb = new StringBuilder(); var v = listS[0]; while (v != null) { sLinkSb.Append(v.Name); VertexBase next = null; if (v is Intersection) { var curr = v as Intersection; next = curr.NextS; } else if (v is Vertex) { var curr = v as Vertex; next = curr.Next; } if (next.Equals(listS[0])) { break; } v = next; sLinkSb.Append("->"); } WindowLog.Default.Log("------S链表:" + sLinkSb); #endregion } // 设置交点的进出性 // 不能简单的把listI中的交点按偶奇的顺序标记进出性,因为listI中的顺序可能和S链表中交点出现的顺序不一样 // 需要安装S链表中交点的顺序标记 var secondDi = _firstInterDi == CrossInOut.In ? CrossInOut.Out : CrossInOut.In; var nextS = (listS[0] as Vertex).Next; int order = 1; while (!nextS.Equals(listS[0])) { if (nextS is Intersection) { (nextS as Intersection).CrossDi = order %2 ==1 ? _firstInterDi:secondDi; ++order; nextS = (nextS as Intersection).NextS; } else { nextS = (nextS as Vertex).Next; } } #region log foreach (var inters in listI) { WindowLog.Default.Log("交点{0},NextS:{1},NextC:{2},S对于C进出性:{3}", inters.Name, inters.NextS.Name, inters.NextC.Name, inters.CrossDi == CrossInOut.In ? "进" : "出"); WindowLog.Default.AddLabel(inters.Name, inters.ToPoint()); } #endregion //阶段3:按规则连接交点得到结果 var result = Compose(listI); return result; }
// 传入的多边形的点需要封闭,即首尾点要相同 public static List <List <VertexBase> > Cut(List <VertexBase> listS, List <VertexBase> listC) { //如果是浮点数,使用这个处理保证相等比较正确 PrepareVertex(listS); PrepareVertex(listC); int cutStartIdx = 0;//实际切割由这个idx起始即可 // 阶段1: 这个循环中的切割用于判断方向 // 如果这步中没有发现交点,则进入不相交多边形的处理 for (; cutStartIdx < listS.Count; cutStartIdx++) { //用实体多边形的一条边去切切割多边形 var s1 = listS[cutStartIdx % listS.Count]; var s2 = listS[(cutStartIdx + 1) % listS.Count]; Tuple <CrossInOut, int, bool> ret; if (s1.X == s2.X) { ret = CutByLineVerticalForCrossDi(s1, s2, listC, false); } else { ret = CutByLineForCrossDi(s1, s2, listC, false); } //如果没有交点,继续下一条边。 if (!ret.Item3) { continue; } var interDirect1 = ret.Item1; //交点对于切割多边形的进出性 var cutLineIdx = ret.Item2; //交点所在的切割多边形的边的起点的索引,用于在使用切割多边形边去切实体多边形时,确定那条边 WindowLog.Default.Log("得到S多边形边{0}{1}切割C多边形{2}{3}产生的交点,对于S进出性为{4}", s1.Name, s2.Name, listC[cutLineIdx % listC.Count].Name, listC[(cutLineIdx + 1) % listC.Count].Name, interDirect1 == CrossInOut.In ? "进" : "出"); //用切割多边形的一条边(cutLineIdx->cutLineIdx+1)去切实体多边形 var ret2 = CutByLineForCrossDi(listC[cutLineIdx % listC.Count], listC[(cutLineIdx + 1) % listC.Count], listS, true, cutStartIdx); var interDirect2 = ret2.Item1; WindowLog.Default.Log("得到C多边形边{0}{1}切割S多边形{2}{3}产生的交点,对于C进出性为{4}", listC[cutLineIdx % listC.Count].Name, listC[(cutLineIdx + 1) % listC.Count].Name, s1.Name, s2.Name, interDirect2 == CrossInOut.In ? "进" : "出"); if (interDirect1 == interDirect2) //进出性相同表示多边形不同向,反转其中一个多边形 { WindowLog.Default.Log("交点进出性相同,把C多边形反向"); //文档中是把S进行了反向,但这里不行,如果反向S,则记录的第一个交点(主要是记录这个交点的进出性)就不再是第一个交点了 //所以实际实现中需要将C反向,而且反向后第一条边,仍然需要是确定第一点那条线段 var listCReverse = new List <VertexBase>(); var reverseStartIdx = cutLineIdx + 1; for (int i = 0; i < listC.Count; i++) { listCReverse.Add(listC[(reverseStartIdx - i + listC.Count) % listC.Count]); } listC = listCReverse; WindowLog.Default.ReversePolygon(); WindowLog.Default.Log("反向后多边形点序列为:{0}", string.Join("->", listC.Select(r => r.Name))); } WindowLog.Default.Log("使用S多边形边{0}{1}与C多边形{2}{3}交点为第一个点,对于S进出性为{4}", s1.Name, s2.Name, listC[cutLineIdx % listC.Count].Name, listC[(cutLineIdx + 1) % listC.Count].Name, interDirect1 == CrossInOut.In ? "进" : "出"); _firstInterDi = ret.Item1; break; } if (cutStartIdx == listS.Count)//没有交点 { WindowLog.Default.Log("没有交点,进入无交点情况处理"); var ret = ProcessNoCross(listS, listC); return(ret == null ? new List <List <VertexBase> >() : new List <List <VertexBase> > { ret }); } // 阶段2: 链接多边形,即设置Next LinkNode(listS.Cast <Vertex>().ToList()); LinkNode(listC.Cast <Vertex>().ToList()); var listI = new List <Intersection>(); var linkC = new LinkedList <VertexBase>(listC); //循环中用S中每条边切割C(准确说是C的链表,每次切割后交点插入C的列表再进行下次切割),把交点插入S和C形成多边形链表 for (; cutStartIdx < listS.Count; cutStartIdx++) { var s1 = listS[cutStartIdx % listS.Count] as Vertex; var s2 = listS[(cutStartIdx + 1) % listS.Count] as Vertex; WindowLog.Default.Log("---------------使用S多边形边{0}{1}切割C多边形---------------", s1.Name, s2.Name); var inters = CutByLine(s1, s2, linkC); //var inters = ret; if (inters.Count == 0) { continue; } listI.AddRange(inters); //把交点排序,准备插入S的边中 if (s1.X < s2.X) { inters.Sort((p1, p2) => p1.X.CompareTo(p2.X)); } else { inters.Sort((p1, p2) => - (p1.X.CompareTo(p2.X))); } //将交点插入S的边中 s1.Next = inters[0]; for (int j = 0; j < inters.Count - 1; j++) { inters[j].NextS = inters[j + 1]; } inters[inters.Count - 1].NextS = s2; #region log var sLinkSb = new StringBuilder(); var v = listS[0]; while (v != null) { sLinkSb.Append(v.Name); VertexBase next = null; if (v is Intersection) { var curr = v as Intersection; next = curr.NextS; } else if (v is Vertex) { var curr = v as Vertex; next = curr.Next; } if (next.Equals(listS[0])) { break; } v = next; sLinkSb.Append("->"); } WindowLog.Default.Log("------S链表:" + sLinkSb); #endregion } // 设置交点的进出性 // 不能简单的把listI中的交点按偶奇的顺序标记进出性,因为listI中的顺序可能和S链表中交点出现的顺序不一样 // 需要安装S链表中交点的顺序标记 var secondDi = _firstInterDi == CrossInOut.In ? CrossInOut.Out : CrossInOut.In; var nextS = (listS[0] as Vertex).Next; int order = 1; while (!nextS.Equals(listS[0])) { if (nextS is Intersection) { (nextS as Intersection).CrossDi = order % 2 == 1 ? _firstInterDi:secondDi; ++order; nextS = (nextS as Intersection).NextS; } else { nextS = (nextS as Vertex).Next; } } #region log foreach (var inters in listI) { WindowLog.Default.Log("交点{0},NextS:{1},NextC:{2},S对于C进出性:{3}", inters.Name, inters.NextS.Name, inters.NextC.Name, inters.CrossDi == CrossInOut.In ? "进" : "出"); WindowLog.Default.AddLabel(inters.Name, inters.ToPoint()); } #endregion //阶段3:按规则连接交点得到结果 var result = Compose(listI); return(result); }