private List <RegionQuadtree <T> > getAllWhoShareEdge(QuadDirection direction, QuadType type) { QuadDirection[] dir; var op = QDO.OpSide(direction); if ((int)direction % 2 == 0) { dir = new QuadDirection[] { QDO.Quad(op, QDO.CSide(op)), QDO.Quad(op, QDO.CCSide(op)) }; } else { dir = new QuadDirection[] { QDO.OpSide(op) }; } if (type == QuadType.Grey) { throw new ArgumentException("Grey isn't allowed!"); } var share = new List <RegionQuadtree <T> >(); getAllWhoShareEdgeInternal(type, this, share, dir); return(share); }
private IEnumerable <RegionQuadtree <T> > traverseInternal(RegionQuadtree <T>[] a) { var sides = QDO.Sides; var t = new RegionQuadtree <T> [8]; if (Type == QuadType.Grey) { for (int i = 0; i < 4; i++) { var d = sides[i]; t[(int)d] = soni(a[(int)d], QDO.Quad(QDO.OpSide(d), QDO.CSide(d))); t[(int)QDO.Quad(d, QDO.CSide(d))] = soni(a[(int)QDO.Quad(d, QDO.CSide(d))], QDO.Quad(QDO.OpSide(d), QDO.CCSide(d))); t[(int)QDO.CSide(d)] = soni(a[(int)QDO.CSide(d)], QDO.Quad(d, QDO.CCSide(d))); t[(int)QDO.Quad(QDO.OpSide(d), QDO.CSide(d))] = soni(a[(int)QDO.CSide(d)], QDO.Quad(QDO.OpSide(d), QDO.CCSide(d))); t[(int)QDO.OpSide(d)] = this[QDO.Quad(QDO.OpSide(d), QDO.CSide(d))]; t[(int)QDO.Quad(QDO.OpSide(d), QDO.CCSide(d))] = this[QDO.Quad(QDO.OpSide(d), QDO.CCSide(d))]; t[(int)QDO.CCSide(d)] = this[QDO.Quad(d, QDO.CCSide(d))]; t[(int)QDO.Quad(d, QDO.CCSide(d))] = soni(a[(int)d], QDO.Quad(QDO.OpSide(d), QDO.CCSide(d))); foreach (var item in this[QDO.Quad(d, QDO.CSide(d))].traverseInternal(t)) { yield return(item); } } } else if (Type == QuadType.Black) { for (int i = 0; i < 8; i++) { if (a[i] != null && a[i].Type == QuadType.White) { a[i] = null; } } yield return(this); } }
public RegionQuadtree <T> ExpandFromCenter() { if (Type != QuadType.Grey) { subdivide(); } Action <RegionQuadtree <T>, Action <RegionQuadtree <T> > > doForEach = null; doForEach = (qt, f) => { f(qt); if (qt.Type == QuadType.Grey) { foreach (var dir in QDO.Quadrants) { doForEach(qt[dir], f); } } }; doForEach(this, (qt) => { if (qt.Type == QuadType.Black) { qt.propagateEvent(EventType.Removing); } }); var newRoot = new RegionQuadtree <T>(resolution + 1); newRoot.AutoExpand = AutoExpand; newRoot.subdivide(); foreach (var child in newRoot.quads) { child.subdivide(); } var offset = new Point2i(newRoot.aabb.Width / 4, newRoot.aabb.Height / 4); var offsets = new Dictionary <QuadDirection, Point2i> { { QuadDirection.NorthWest, new Point2i(quads[0].aabb.Width / 2, quads[0].aabb.Height / 2) }, { QuadDirection.SouthEast, new Point2i(quads[0].aabb.Width / 2, quads[0].aabb.Height / 2) }, { QuadDirection.NorthEast, new Point2i(quads[0].aabb.Width / 2, quads[0].aabb.Height / 2) }, { QuadDirection.SouthWest, new Point2i(quads[0].aabb.Width / 2, quads[0].aabb.Height / 2) }, }; // Set children Action <RegionQuadtree <T>, RegionQuadtree <T> > expandProcessor = null; expandProcessor = (qt, parent) => { // Do this qt.parent = parent; qt.depth = qt.parent.depth + 1; qt.resolution = qt.parent.resolution; // Do children if (qt.Type == QuadType.Grey) { foreach (var dir in QDO.Quadrants) { switch (dir) { case QuadDirection.NorthWest: qt[dir].aabb.LowerBound = qt.aabb.LowerBound; qt[dir].aabb.UpperBound = qt.aabb.LowerBound + new Point2i(qt.aabb.Width / 2, qt.aabb.Height / 2); break; case QuadDirection.NorthEast: qt[dir].aabb.LowerBound = qt.aabb.LowerBound + new Point2i(qt.aabb.Width / 2, 0); qt[dir].aabb.UpperBound = qt.aabb.LowerBound + new Point2i(qt.aabb.Width, qt.aabb.Height / 2); break; case QuadDirection.SouthEast: qt[dir].aabb.LowerBound = qt.aabb.LowerBound + new Point2i(qt.aabb.Width / 2, qt.aabb.Height / 2); qt[dir].aabb.UpperBound = qt.aabb.LowerBound + new Point2i(qt.aabb.Width, qt.aabb.Height); break; case QuadDirection.SouthWest: qt[dir].aabb.LowerBound = qt.aabb.LowerBound + new Point2i(0, qt.aabb.Height / 2); qt[dir].aabb.UpperBound = qt.aabb.LowerBound + new Point2i(qt.aabb.Width / 2, qt.aabb.Height); break; default: throw new ArgumentException("Invalid direction"); } expandProcessor(qt[dir], qt); } } }; foreach (var quadDirection in QDO.Quadrants) { var oldQuad = newRoot[quadDirection][QDO.OpSide(quadDirection)]; newRoot[quadDirection][QDO.OpSide(quadDirection)] = this[quadDirection]; var newQuad = newRoot[quadDirection][QDO.OpSide(quadDirection)]; newQuad.aabb = oldQuad.aabb; expandProcessor(newQuad, oldQuad.parent); } // Call the event if (OnExpand != null) { OnExpand(this, new QuadExpandEventArgs <T>(newRoot, offset)); } // Move events // TODO: Test this works properly newRoot.OnQuadAdded = OnQuadAdded; OnQuadAdded = null; newRoot.OnQuadRemoving = OnQuadRemoving; OnQuadRemoving = null; newRoot.OnQuadChanged = OnQuadChanged; OnQuadChanged = null; doForEach(newRoot, (qt) => { if (qt.Type == QuadType.Black) { qt.propagateEvent(EventType.Added); } }); newRoot.OnExpand = OnExpand; return(newRoot); }
private void cclInternal(RegionQuadtree <T>[] a, List <DisjointSet <int> > linked, Dictionary <RegionQuadtree <T>, int> labels, QuadDirection[] sides, bool quadrants) { var t = new RegionQuadtree <T> [8]; if (Type == QuadType.Grey) { for (int i = 0; i < 4; i++) { var d = sides[i]; t[(int)d] = soni(a[(int)d], QDO.Quad(QDO.OpSide(d), QDO.CSide(d))); t[(int)QDO.CSide(d)] = soni(a[(int)QDO.CSide(d)], QDO.Quad(d, QDO.CCSide(d))); t[(int)QDO.OpSide(d)] = this[QDO.Quad(QDO.OpSide(d), QDO.CSide(d))]; t[(int)QDO.CCSide(d)] = this[QDO.Quad(d, QDO.CCSide(d))]; if (quadrants) { t[(int)QDO.Quad(d, QDO.CSide(d))] = soni(a[(int)QDO.Quad(d, QDO.CSide(d))], QDO.Quad(QDO.OpSide(d), QDO.CCSide(d))); t[(int)QDO.Quad(QDO.OpSide(d), QDO.CSide(d))] = soni(a[(int)QDO.CSide(d)], QDO.Quad(QDO.OpSide(d), QDO.CCSide(d))); t[(int)QDO.Quad(QDO.OpSide(d), QDO.CCSide(d))] = this[QDO.Quad(QDO.OpSide(d), QDO.CCSide(d))]; t[(int)QDO.Quad(d, QDO.CCSide(d))] = soni(a[(int)d], QDO.Quad(QDO.OpSide(d), QDO.CCSide(d))); } this[QDO.Quad(d, QDO.CSide(d))].cclInternal(t, linked, labels, sides, quadrants); } } else if (Type == QuadType.Black) { bool noNeighbors = true; var neighborLabels = new List <int>(); for (int i = 0; i < 8; i++) { if (a[i] != null && a[i].Type == QuadType.White) { a[i] = null; } if (a[i] != null) { if (a[i].Type == QuadType.Black && labels.ContainsKey(a[i])) { neighborLabels.Add(labels[a[i]]); noNeighbors = false; } else if (a[i].Type == QuadType.Grey) { var share = getAllWhoShareEdge((QuadDirection)i, QuadType.Black); foreach (var shareBlack in share) { if (labels.ContainsKey(shareBlack)) { neighborLabels.Add(labels[shareBlack]); noNeighbors = false; } } } } } if (noNeighbors) { var nextLabel = linked.Count; linked.Add(new DisjointSet <int>(nextLabel)); labels[this] = nextLabel; } else { labels[this] = neighborLabels.Min(); for (int i = 0; i < neighborLabels.Count; i++) { for (int j = 0; j < neighborLabels.Count; j++) { linked[neighborLabels[i]].Union(linked[neighborLabels[j]]); } } } } }