// used by tests
 protected void forceTreeSplit(int depth) {
   if (depth <= treeRoot.maxNodeDepth && this.depth < depth) {
     split();
     for (AbstractQuadTreeNode<E> child : children) {
       child.forceTreeSplit(depth);
     }
   }
 }
 /**
  * Ajoute tous les éléments contenus dans cette node et ses enfants dans <code>collection</code>
  *
  * @param collection
  */
 protected void getAllElements(Collection<E> collection) {
   collection.addAll(elements);
   if (children != null) {
     for (AbstractQuadTreeNode<E> child : children) {
       child.getAllElements(collection);
     }
   }
 }
  /**
   * Ajoute cette node et toutes les sous-nodes dans <code>collection</code>
   *
   * @param collection
   */
  protected void getAllNodes(Collection<QuadTreeNodeInterface<E>> collection) {
    collection.add(this);

    if (children != null) {
      for (AbstractQuadTreeNode<E> child : children) {
        child.getAllNodes(collection);
      }
    }
  }
 /**
  * Renvoie dans <code>collection</code> toutes les nodes à la profondeur indiquée
  *
  * @param collection la collection dans laquelleajouter les nodes
  * @param depth
  */
 protected void getAllNodesAtDepth(Collection<QuadTreeNodeInterface<E>> collection, int depth) {
   if (this.depth == depth) {
     collection.add(this);
   } else if (this.depth < depth) {
     if (children != null) {
       for (AbstractQuadTreeNode<E> child : children) {
         child.getAllNodesAtDepth(collection, depth);
       }
     }
   }
 }
  // retourne le noeud voisin d'un noeud (algorithme générique)
  private QuadTreeNodeInterface<E> sibling(
      AbstractQuadTreeNode<E> node, int[] ancestortype, int[] reverter) {
    int[] path = new int[treeRoot.maxNodeDepth + 1];
    int pathlength = 0;

    // recherche du plus proche ancetre commun
    AbstractQuadTreeNode<E> ancestor = node;

    while (true) {
      if (ancestor.getPositionInParentNode() == ROOT) {
        return null; // no common ancestor -> exit
      }
      path[pathlength] = ancestor.getPositionInParentNode();
      pathlength++;
      if (ancestor.getPositionInParentNode() == ancestortype[0]) {
        ancestor = ancestor.getParent();
        break;
      }
      if (ancestor.getPositionInParentNode() == ancestortype[1]) {
        ancestor = ancestor.getParent();
        break;
      }
      ancestor = ancestor.getParent();
    }

    // parcours de l'arbre en utilisant le chemin symetrique
    AbstractQuadTreeNode<E> cursor = ancestor, next = null;
    for (int i = pathlength - 1; i >= 0; i--) {
      if (cursor.children == null) break;
      next = cursor.children.get(reverter[path[i]]);
      if (next == null) break;
      cursor = next;
    }
    return cursor;
  }
 /** @return le chemin pour accéder à cette node sous une forme du type (ROOT)123012 etc. */
 public String getCompactPath() {
   if (parent == this) {
     return "";
   } else {
     return parent.getCompactPath() + positionInParentNode;
   }
 }
 @Override
 public int hashCode() {
   final int prime = 31;
   int result = 1;
   result = prime * result + ((parent == null) ? 0 : parent.hashCode());
   result = prime * result + ((splitPoint == null) ? 0 : splitPoint.hashCode());
   return result;
 }
  /**
   * Supprime une instance de <code>e</code> de cette node. Utilise le QuadTree pour localiser
   * l'objet à supprimer. Effectue un clear() sur le parent si il s'agissait du dernier élément
   * contenu dans la node parent
   *
   * @param e L'objet à supprimer
   * @return <code>true</code> si l'élément a été supprimé
   */
  public boolean remove(E e) {
    if (e == null) {
      return false;
    }
    AbstractQuadTreeNode<E> node = (AbstractQuadTreeNode<E>) getElementNode(e);
    if (node == null) {
      return false;
    } else {
      boolean ret = node.elements.remove(e);

      // vérification : est ce que cet élément était le dernier élément contenu dans la node parent.
      // Si oui : effacer les enfants dans le parent.
      if (parent.isEmpty()) {
        parent.clear();
      }

      return ret;
    }
  }
 @Override
 public boolean equals(Object obj) {
   if (this == obj) return true;
   if (obj == null) return false;
   if (getClass() != obj.getClass()) return false;
   @SuppressWarnings("unchecked")
   AbstractQuadTreeNode<E> other = (AbstractQuadTreeNode<E>) obj;
   if (parent == null) {
     if (other.parent != null) return false;
   } else if (!parent.equals(other.parent)) return false;
   if (splitPoint == null) {
     if (other.splitPoint != null) return false;
   } else if (!splitPoint.equals(other.splitPoint)) return false;
   return true;
 }
  public AbstractQuadTreeNode(AbstractQuadTreeNode<E> parent, int positionInParentNode) {
    this.parent = parent;
    this.depth = parent.getDepth() + 1;
    this.treeRoot = parent.getTreeRoot();
    this.positionInParentNode = positionInParentNode;

    this.elements = new ArrayList<E>(treeRoot.maxElementsPerNode);

    switch (positionInParentNode) {
      case NW:
        maxX = parent.getSplitPoint().x;
        maxY = parent.getMaxY();
        minX = parent.getMinX();
        minY = parent.getSplitPoint().y;
        break;

      case NE:
        maxX = parent.getMaxX();
        maxY = parent.getMaxY();
        minX = parent.getSplitPoint().x;
        minY = parent.getSplitPoint().y;
        break;

      case SW:
        maxX = parent.getSplitPoint().x;
        maxY = parent.getSplitPoint().y;
        minX = parent.getMinX();
        minY = parent.getMinY();
        break;

      case SE:
        maxX = parent.getMaxX();
        maxY = parent.getSplitPoint().y;
        minX = parent.getSplitPoint().x;
        minY = parent.getMinY();
        break;
    }

    this.splitPoint = new Point2D.Double((maxX + minX) / 2, (maxY + minY) / 2);
  }