public WeilerAtherton(Polygon2D a_subject, Polygon2D a_clip) { // initialize variables Subject = a_subject; Clip = a_clip; SubjectList = new LinkedList <WAPoint>(); ClipList = new LinkedList <WAPoint>(); SubjectNode = new Dictionary <WAPoint, LinkedListNode <WAPoint> >(); ClipNode = new Dictionary <WAPoint, LinkedListNode <WAPoint> >(); EntryIntersections = new HashSet <WAPoint>(); // store intersections for clipping line segment // saves recomputation of intersections and entry-exit categorization var intersectSegmentsClip = new Dictionary <LineSegment, List <WAPoint> >(); foreach (var seg in a_clip.Segments) { intersectSegmentsClip.Add(seg, new List <WAPoint>()); } // find all intersections and create subject list foreach (var seg1 in a_subject.Segments) { // retrieve intersection list var vertices = new List <WAPoint>(); var point = new WAPoint(seg1.Point1, WAList.Vertex); var node = SubjectList.AddLast(point); SubjectNode.Add(point, node); foreach (var seg2 in a_clip.Segments) { var intersect = seg1.Intersect(seg2); if (intersect.HasValue) { // store intersection point point = new WAPoint(intersect.Value, WAList.Ignore); vertices.Add(point); intersectSegmentsClip[seg2].Add(point); } } // sort intersections on distance to start vertex vertices.Sort(new ClosestToPointComparer(seg1.Point1)); foreach (var vertex in vertices) { node = SubjectList.AddLast(vertex); SubjectNode.Add(vertex, node); } } // remove duplicates for (var node = SubjectList.First; node != null; node = node.Next) { var next = node.Next ?? SubjectList.First; while (node != next && MathUtil.EqualsEps(node.Value.Pos, next.Value.Pos)) { if (node.Value.Type == WAList.Vertex) { node.Value.Type = WAList.Ignore; SubjectList.Remove(node); EntryIntersections.Remove(node.Value); node = next; } else { next.Value.Type = WAList.Ignore; SubjectList.Remove(next); EntryIntersections.Remove(next.Value); } next = node.Next ?? SubjectList.First; } } // set entry/exit types correctly for (var node = SubjectList.First; node != null; node = node.Next) { var prev = node.Previous ?? SubjectList.Last; var vertex = node.Value; var inside1 = Clip.ContainsInside(prev.Value.Pos); var inside2 = Clip.ContainsInside(vertex.Pos); if (!inside1 && inside2) { vertex.Type = WAList.Entry; } else if (inside1 && !inside2) { prev.Value.Type = WAList.Exit; EntryIntersections.Remove(vertex); } if (vertex.Type == WAList.Entry) { EntryIntersections.Add(vertex); } } // create clip list and intersections foreach (var seg in a_clip.Segments) { var vertices = intersectSegmentsClip[seg]; // sort intersections on distance to start vertex intersectSegmentsClip[seg].Sort(new ClosestToPointComparer(seg.Point1)); vertices.Insert(0, new WAPoint(seg.Point1, WAList.Vertex)); // loop over intersections foreach (var vertex in vertices) { if (vertex.Type == WAList.Ignore) { continue; } // add intersection to clipping list var node = ClipList.AddLast(vertex); ClipNode.Add(vertex, node); } } }
/// <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); }
public WeilerAtherton(Polygon2D a_subject, Polygon2D a_clip) { // initialize variables Subject = a_subject; Clip = a_clip; SubjectList = new LinkedList <WAPoint>(Subject.Vertices.Select(v => new WAPoint(v, WAList.Vertex))); ClipList = new LinkedList <WAPoint>(Clip.Vertices.Select(v => new WAPoint(v, WAList.Vertex))); SubjectNode = new Dictionary <WAPoint, LinkedListNode <WAPoint> >(); ClipNode = new Dictionary <WAPoint, LinkedListNode <WAPoint> >(); Entry = new HashSet <WAPoint>(); Exit = new HashSet <WAPoint>(); // find all intersections and create subject and clip list for (var node1 = SubjectList.First; node1 != null; node1 = node1.Next) { // calculate subject segment var next1 = node1.Next ?? SubjectList.First; var seg1 = new LineSegment(node1.Value.Pos, next1.Value.Pos); // obtain intersection lists var intersections = new List <Vector2>(); var intersectNodes = new Dictionary <Vector2, LinkedListNode <WAPoint> >(); for (var node2 = ClipList.Last; node2 != null; node2 = node2.Previous) { var prev2 = node2.Previous ?? ClipList.Last; var seg2 = new LineSegment(node2.Value.Pos, prev2.Value.Pos); var intersect = seg1.Intersect(seg2); if (intersect.HasValue && !intersectNodes.ContainsKey(intersect.Value)) { // store intersection point intersectNodes.Add(intersect.Value, node2); intersections.Add(intersect.Value); } } // sort intersections on distance to start vertex intersections.Sort(new ClosestToPointComparer(seg1.Point1)); // insert intersections into subject/clip lists foreach (var vertex in intersections) { var point = new WAPoint(vertex, WAList.Ignore); var newNode1 = SubjectList.AddAfter(node1, point); SubjectNode.Add(point, newNode1); var newNode2 = ClipList.AddBefore(intersectNodes[vertex], point); ClipNode.Add(point, newNode2); // increment node1 since we added a vertex node1 = newNode1; } } // remove duplicates of subject for (var node = SubjectList.First; node != null;) { var next = node.Next ?? SubjectList.First; while (MathUtil.EqualsEps(node.Value.Pos, next.Value.Pos, MathUtil.EPS * 10)) { if (next.Value.Type != WAList.Vertex) { if (ClipNode.ContainsKey(node.Value)) { ClipList.Remove(ClipNode[node.Value]); } SubjectList.Remove(node); node = next; } else { SubjectList.Remove(next); } if (node == null) { break; } next = node.Next ?? SubjectList.First; } if (node != null) { node = node.Next; } } // remove duplicates of clip for (var node = ClipList.Last; node != null;) { var prev = node.Previous ?? ClipList.Last; while (MathUtil.EqualsEps(node.Value.Pos, prev.Value.Pos, MathUtil.EPS * 10)) { if (prev.Value.Type != WAList.Vertex) { if (SubjectNode.ContainsKey(node.Value)) { SubjectList.Remove(SubjectNode[node.Value]); } ClipList.Remove(node); node = prev; } else { ClipList.Remove(prev); } if (node == null) { break; } prev = node.Previous ?? ClipList.Last; } if (node != null) { node = node.Previous; } } // set entry/exit types correctly for (var node = SubjectList.First; node != null; node = node.Next) { if (node.Value.Type == WAList.Vertex) { continue; } var prev = node.Previous ?? SubjectList.Last; var next = node.Next ?? SubjectList.First; var inside1 = Clip.ContainsInside(prev.Value.Pos) || Clip.OnBoundary(prev.Value.Pos); var inside2 = Clip.ContainsInside(next.Value.Pos) || Clip.OnBoundary(next.Value.Pos); if (!inside1 && inside2) { node.Value.Type = WAList.Entry; Entry.Add(node.Value); } else if (inside1 && !inside2) { node.Value.Type = WAList.Exit; Exit.Add(node.Value); } } }