public SectorConstrainedOSMAreaGraph Clone() { // TODO: make vector2d a struct? SectorConstrainedOSMAreaGraph map = new SectorConstrainedOSMAreaGraph(); Dictionary <AreaNode, AreaNode> mapper = new Dictionary <AreaNode, AreaNode>(); // fully clone, even the bad parts HashSet <AreaNode> explored = new HashSet <AreaNode>(); Queue <AreaNode> bfs = new Queue <AreaNode>(); foreach (var nodeList in nodes.Values) { foreach (var node in nodeList) { bfs.Enqueue(node); } } while (bfs.Count > 0) { var head = bfs.Dequeue(); if (explored.Contains(head)) { continue; } explored.Add(head); if (head.next != null) { bfs.Enqueue(head.next); } if (head.prev != null) { bfs.Enqueue(head.prev); } } foreach (var x in explored) { mapper[x] = new AreaNode() { id = x.id }; } foreach (var x in explored) { mapper[x].prev = x.prev == null ? null : mapper[x.prev]; mapper[x].next = x.next == null ? null : mapper[x.next]; } foreach (var pair in nodes) { map.nodes[pair.Key] = pair.Value.Select(x => mapper[x]).ToList(); } return(map); }
// TODO: apparently we've been adding loops twice this entire time, basically // I see, I never did loops at the end before because map was modified at that point private List <AreaNode> DoLoops(SectorConstrainedOSMAreaGraph map, BlobCollection blobs) { // first, find those loops // actually, non-loops should be found as well! List <AreaNode> loopNodes = new List <AreaNode>(); HashSet <AreaNode> explored = new HashSet <AreaNode>(); foreach (var nodeList in map.nodes.Values) { foreach (var node in nodeList) { if (explored.Contains(node)) { continue; } // just loops can be found at this point List <AreaNode> newLoop = new List <AreaNode>(); bool loopHasConnections = false; AreaNode curr = node; while (true) { if (nodes.ContainsKey(curr.id)) { loopHasConnections = true; } newLoop.Add(curr); explored.Add(curr); if (curr.next == node) { break; } curr = curr.next; } if (!loopHasConnections) { // TODO: use winding rule foreach (var n in newLoop) { loopNodes.Add(n); } } } } return(loopNodes); }
public SectorConstrainedOSMAreaGraph Intersect(SectorConstrainedOSMAreaGraph map, BlobCollection blobs) { return(BoolOp(map, blobs, false, (x, y) => x && y)); }
public SectorConstrainedOSMAreaGraph BoolOp(SectorConstrainedOSMAreaGraph map, BlobCollection blobs, bool reverse, Func <bool, bool, bool> boolOp) { map = map.Clone(); // now we're allowed to junk it if (reverse) { map.Reverse(); } var loopNodes = DoLoops(map, blobs); // remember, counterclockwise makes an island List <AreaNode> doAdd = new List <AreaNode>(); List <AreaNode> doDelete = new List <AreaNode>(); List <long> singularDelete = new List <long>(); HashSet <long> criticalPoints = new HashSet <long>(); foreach (var pair in map.nodes) { if (nodes.ContainsKey(pair.Key)) { criticalPoints.Add(pair.Key); List <AreaNode> srcInitialLines = new List <AreaNode>(); List <AreaNode> mapInitialLines = new List <AreaNode>(); List <AreaNode> initialLines = new List <AreaNode>(); foreach (var n in nodes[pair.Key]) { srcInitialLines.Add(n.prev); srcInitialLines.Add(n.next); } foreach (var n in map.nodes[pair.Key]) { mapInitialLines.Add(n.prev); mapInitialLines.Add(n.next); } initialLines.AddRange(srcInitialLines); initialLines.AddRange(mapInitialLines); List <AreaNode> finalLines = initialLines.ToList(); // sort clockwise finalLines = finalLines.OrderBy(x => ComputeInnerAngle(new Vector2d(1, 0), GetPos(x, blobs) - blobs.nodes[pair.Key])).ToList(); bool[] sectorsSrc = new bool[finalLines.Count]; bool[] sectorsMap = new bool[finalLines.Count]; bool[] sectors = new bool[finalLines.Count]; // first sector means the area just ccw of the first finalLine // NOTE: this logic only works assuming multiple polygons are disjoint at intersections foreach (var n in nodes[pair.Key]) { int from = finalLines.IndexOf(n.prev); int to = finalLines.IndexOf(n.next); if (to <= from) { to += finalLines.Count; } for (int i = from + 1; i <= to; i++) { sectorsSrc[i % sectorsSrc.Length] = true; } } foreach (var n in map.nodes[pair.Key]) { int from, to; if (reverse) { from = finalLines.IndexOf(n.next); to = finalLines.IndexOf(n.prev); } else { from = finalLines.IndexOf(n.prev); to = finalLines.IndexOf(n.next); } if (to <= from) { to += finalLines.Count; } for (int i = from + 1; i <= to; i++) { sectorsMap[i % sectorsMap.Length] = true; } } for (int i = 0; i < sectors.Length; i++) { sectors[i] = boolOp(sectorsSrc[i], sectorsMap[i]); } // make sure zero-width areas match one of their neighbors so they get cleaned up // correction: make sure zero-width somehow prioritize getting rid of new map lines like our original code // turns out, even in our first test case, being inconsistent with this is just wrong (to see, treat this test case as one of two non-degenerate cases) int[] makeMatch = new int[sectors.Length]; // -1 means, make match left, etc. TODO: I'm really winging it here, no clue if this makes any sense at all (basically, just test it!) for (int i = 0; i < sectors.Length; i++) { AreaNode p = finalLines[(i - 1 + finalLines.Count) % finalLines.Count]; AreaNode n = finalLines[i]; if (p.id == n.id) { makeMatch[i] = mapInitialLines.Contains(p) ? -1 : 1; // make sector before this match this one to eliminate p } } if (!makeMatch.Contains(0)) { throw new NotImplementedException(); } for (int i = 0; i < makeMatch.Length; i++) { HashSet <int> explored = new HashSet <int>(); int curr = i; while (true) { if (makeMatch[curr] == 0) { sectors[i] = sectors[curr]; break; } if (explored.Contains(curr)) { throw new NotImplementedException(); // some kind of rare loop } explored.Add(curr); curr = (curr + makeMatch[curr] + makeMatch.Length) % makeMatch.Length; } } List <int> remove = new List <int>(); for (int i = 0; i < finalLines.Count; i++) { // i and i + 1 surround this line // get rid of any line that doesn't act as a border if (sectors[i] == sectors[(i + 1) % sectors.Length]) { remove.Add(i); } } remove.Reverse(); foreach (var i in remove) { finalLines.RemoveAt(i); } // we should only have an even number of things in finalLine now if (finalLines.Count % 2 != 0) { throw new NotImplementedException(); } // let's make sure the first one points in if (finalLines.Count > 0) { if (finalLines.First().next == null || finalLines.First().next.id != pair.Key) { finalLines.Add(finalLines.First()); finalLines.RemoveAt(0); } } foreach (var line in srcInitialLines) { if (!finalLines.Contains(line)) { doDelete.Add(line); } } foreach (var line in mapInitialLines) { if (finalLines.Contains(line)) { doAdd.Add(line); } } List <AreaNode> newSrcNodes = new List <AreaNode>(); for (int i = 0; i < finalLines.Count / 2; i++) { AreaNode into = finalLines[i * 2]; AreaNode outof = finalLines[i * 2 + 1]; AreaNode newSrcNode; if (i == 0) // mimic our original code for debugging? { newSrcNode = nodes[pair.Key].First(); } else { newSrcNode = new AreaNode() { id = pair.Key }; } into.next = newSrcNode; outof.prev = newSrcNode; newSrcNode.prev = into; newSrcNode.next = outof; newSrcNodes.Add(newSrcNode); } if (newSrcNodes.Count > 0) { nodes[pair.Key] = newSrcNodes; } else { singularDelete.Add(pair.Key); } } } // delete until you reach the next critical point foreach (var d in doDelete) { bool forwards = d.next != null && (!criticalPoints.Contains(d.next.id)); var temp = d; while (temp != null && !criticalPoints.Contains(temp.id)) { if (!ContainsNode(temp)) { break; } nodes[temp.id].Remove(temp); if (nodes[temp.id].Count == 0) { nodes.Remove(temp.id); } temp = forwards ? temp.next : temp.prev; } } // add until you reach the next critical point foreach (var d in doAdd) { bool forwards = d.next != null && (!criticalPoints.Contains(d.next.id)); var temp = d; while (temp != null && !criticalPoints.Contains(temp.id)) { if (ContainsNode(temp)) { break; } if (!nodes.ContainsKey(temp.id)) { nodes[temp.id] = new List <AreaNode>(); } nodes[temp.id].Add(temp); temp = forwards ? temp.next : temp.prev; } } foreach (var d in singularDelete) { nodes.Remove(d); } foreach (var n in loopNodes) { if (!nodes.ContainsKey(n.id)) { nodes[n.id] = new List <AreaNode>(); } nodes[n.id].Add(n); } return(this); }
public SectorConstrainedOSMAreaGraph Subtract(SectorConstrainedOSMAreaGraph map, BlobCollection blobs) { return(BoolOp(map, blobs, true, (x, y) => x && !y)); }