Search code examples
c#quadtree

How to insert node in all intersecting quads in QuadTree?


I have a QuadTree but the insert method is not working like I want it to. Right now it only inserts in the first intersecting quad it sees. The aim is that all nodes are inserted in all the quads its intersecting with. For example: when a node is on the border of two quads, it is inserted in both quads. Can somebody help me to get the insert method to where it is inserted in all intersecting quads?

This is my current implementation:

This is the call to insert the nodes:

QuadTree = new QuadTreeNode < Artist > (new Rect(0, 0, Width, Height), 1);
foreach(Artist artist in Artists) {
  QuadTree.Insert(artist);
}

This is my QuadTree class:

public interface WithRect {
  Rect Rect {
    get;
  }
}

public class QuadTreeNode < T > where T: WithRect {
  Rect bounds;
  List < T > contents;
  int numberOfNodesInserted = 0;
  int max = 4;
  int depth = 0;
  int maxDepth = 6;
  bool divided;

  QuadTreeNode < T > TopLeft;
  QuadTreeNode < T > TopRight;
  QuadTreeNode < T > BottomLeft;
  QuadTreeNode < T > BottomRight;

  public QuadTreeNode(Rect _bounds, int _depth) {
    depth = _depth;
    bounds = _bounds;
    contents = new List < T > ();
    divided = false;
  }

  public void DivideQuad() {
    int newDepth = depth + 1;
    double x = bounds.X;
    double y = bounds.Y;
    double width = bounds.Width;
    double height = bounds.Height;

    TopLeft = new QuadTreeNode < T > (new Rect(x, y, width / 2, height / 2), newDepth);
    TopRight = new QuadTreeNode < T > (new Rect(x + width / 2, y, width / 2, height / 2), newDepth);
    BottomLeft = new QuadTreeNode < T > (new Rect(x, y + height / 2, width / 2, height / 2), newDepth);
    BottomRight = new QuadTreeNode < T > (new Rect(x + width / 2, y + height / 2, width / 2, height / 2), newDepth);
    divided = true;

    foreach(T item in contents) {
      Insert(item);
    }
    contents.Clear();
  }

  public bool Insert(T item) {
    if (!bounds.IntersectsWith(item.Rect)) return false;
    
    if (numberOfNodesInserted < max || depth == maxDepth) {
      contents.Add(item);
      numberOfNodesInserted++;
      return true;
    }
    else {
      if (!divided) {
        DivideQuad();
      }

      if (TopLeft.Insert(item)) return true;
      else if (TopRight.Insert(item)) return true;
      else if (BottomLeft.Insert(item)) return true;
      else if (BottomRight.Insert(item)) return true;
      else return false;
    }
  }

  public void GetBounds(ref List < Rect > results) {
    if (bounds != null) results.Add(bounds);

    if (TopLeft != null) TopLeft.GetBounds(ref results);
    if (TopRight != null) TopRight.GetBounds(ref results);
    if (BottomLeft != null) BottomLeft.GetBounds(ref results);
    if (BottomRight != null) BottomRight.GetBounds(ref results);
  }

  public void Query(T item, ref List < T > collisions, List < Square > squares, int squareWidth, int squareHeight) {
    if (item.Rect.IntersectsWith(bounds)) {
      if (divided) {
        TopLeft.Query(item, ref collisions, squares, squareWidth, squareHeight);
        TopRight.Query(item, ref collisions, squares, squareWidth, squareHeight);
        BottomLeft.Query(item, ref collisions, squares, squareWidth, squareHeight);
        BottomRight.Query(item, ref collisions, squares, squareWidth, squareHeight);
      }

      for (int i = 0; i < contents.Count; i++) {
        if (item.Rect != contents[i].Rect) {
          if (item.Rect.IntersectsWith(contents[i].Rect)) {
            if (!collisions.Contains(contents[i])) {
              collisions.Add(contents[i]);
            }
          }
        }
        foreach(Square square in squares) {
          if (square.IsVisited) {
            if (item.Rect.IntersectsWith(new Rect(square.X * squareWidth, square.Y * squareHeight, squareWidth, squareHeight))) {
              if (!collisions.Contains(contents[i])) {
                collisions.Add(contents[i]);
              }
            }
          }
        }
      }
    }
  }
}

Solution

  • Remove the else from else if to ensure a item is inserted to each of its subnodes.

    if (TopLeft.Insert(item)) return true;
    else if (TopRight.Insert(item)) return true;
    else if (BottomLeft.Insert(item)) return true;
    else if (BottomRight.Insert(item)) return true;
    else return false;
    

    should be

    var result = TopLeft.Insert(item);
    result  |= TopRight.Insert(item);
    result  |= BottomLeft.Insert(item);
    result  |= BottomRight.Insert(item);
    return result;
    

    Another problem is

    if (numberOfNodesInserted < max || depth == maxDepth)
    

    you should never insert items to the node if it is divided, so it should be:

    if (!divided && (numberOfNodesInserted < max || depth == maxDepth))
    

    That is, assuming you are only storing items in the leaf-nodes.