/// <summary> /// A implementation of the Weiler-Atherthon algorithm that cuts away the provided clippling area from the subject polygon. /// Assumes the cutting polygon is not entirely inside subject polygon! /// </summary> /// <param name="a_subject"></param> /// <param name="a_clip"></param> /// <returns></returns> public static MultiPolygon2D CutOut(Polygon2D a_subject, Polygon2D a_clip) { // copy and maybe reverse if (a_subject.IsClockwise()) { a_subject = new Polygon2D(a_subject.Vertices); } else { a_subject = new Polygon2D(a_subject.Vertices.Reverse()); } if (a_clip.IsClockwise()) { a_clip = new Polygon2D(a_clip.Vertices); } else { a_clip = new Polygon2D(a_clip.Vertices.Reverse()); } var WA = new WeilerAtherton(a_subject, a_clip); //return new MultiPolygon2D(); return(WeilerAthertonCutOut(WA)); }
/// <summary> /// Creates a cutout given the relevant weiler atherton object containing subject and clipping lists /// Can result in multiple polygons. /// Assumes the cutting polygon is not entirely inside subject polygon! /// </summary> /// <param name="WA"></param> /// <returns></returns> private static MultiPolygon2D WeilerAthertonCutOut(WeilerAtherton WA) { var multiPoly = new MultiPolygon2D(); // check if polygons have intersections if (WA.EntryIntersections.Count == 0) { if (!WA.Subject.Vertices.ToList().Exists(p => WA.Clip.ContainsInside(p))) { multiPoly.AddPolygon(new Polygon2D(WA.Subject.Vertices)); } } while (WA.EntryIntersections.Count > 0) { var vertices = new List <Vector2>(); var visited = new HashSet <LinkedListNode <WAPoint> >(); // remove an entry intersection point var startPoint = WA.EntryIntersections.LastOrDefault(); WA.EntryIntersections.Remove(startPoint); LinkedListNode <WAPoint> node; try { node = WA.ClipNode[startPoint]; } catch (KeyNotFoundException e) { Debug.Log("entry point not found: " + e); continue; } WAPoint last = null; while (!visited.Contains(node)) { // remove entry intersection from starting list WA.EntryIntersections.Remove(node.Value); // traverse clip polygon in counter-clockwise order until exit vertex found while (node.Value.Type != WAList.Exit && !visited.Contains(node)) { // check for duplicates if (last == null || !MathUtil.EqualsEps(node.Value.Pos, last.Pos)) { vertices.Add(node.Value.Pos); visited.Add(node); } last = node.Value; node = node.Previous ?? WA.ClipList.Last; } // might contains entry but no exit vertex // should not occur, return no polygon if (visited.Contains(node)) { vertices.Clear(); break; } try { node = WA.SubjectNode[node.Value]; } catch (KeyNotFoundException e) { Debug.Log("exit point not found: " + e.StackTrace); break; } // traverse subject polygon in clockwise order until new entr vertex found while (node.Value.Type != WAList.Entry && !visited.Contains(node)) { if (last == null || !MathUtil.EqualsEps(node.Value.Pos, last.Pos)) { vertices.Add(node.Value.Pos); visited.Add(node); } last = node.Value; node = node.Next ?? WA.SubjectList.First; } if (visited.Contains(node)) { break; } try { node = WA.ClipNode[node.Value]; } catch (KeyNotFoundException e) { Debug.Log("entry point not found: " + e); break; } } // add new polygon from the vertices if (vertices.Count > 2) { multiPoly.AddPolygon(new Polygon2D(vertices)); } } return(multiPoly); }