public void findIntersections(Rectangle query, Consumer<T> consumer)
    {
        IntArrayList todoNodes = new IntArrayList(levelOffsets.length * degree);
        IntArrayList todoLevels = new IntArrayList(levelOffsets.length * degree);

        int rootLevel = levelOffsets.length - 1;
        int rootIndex = levelOffsets[rootLevel];
        if (doesIntersect(query, rootIndex)) {
            todoNodes.push(rootIndex);
            todoLevels.push(rootLevel);
        }

        while (!todoNodes.isEmpty()) {
            int nodeIndex = todoNodes.popInt();
            int level = todoLevels.popInt();

            if (level == 0) {
                // This is a leaf node
                consumer.accept(items[nodeIndex / ENVELOPE_SIZE]);
            }
            else {
                int childrenOffset = getChildrenOffset(nodeIndex, level);
                for (int i = 0; i < degree; i++) {
                    int childIndex = childrenOffset + ENVELOPE_SIZE * i;
                    if (doesIntersect(query, childIndex)) {
                        todoNodes.push(childIndex);
                        todoLevels.push(level - 1);
                    }
                }
            }
        }
    }
@Test(dataProvider = "rectangle-counts")
    public void testRectangleCollection(int numBuildRectangles, int numProbeRectangles, int seed)
    {
        Random random = new Random(seed);
        List<Rectangle> buildRectangles = makeRectangles(random, numBuildRectangles);
        List<Rectangle> probeRectangles = makeRectangles(random, numProbeRectangles);

        Flatbush<Rectangle> rtree = new Flatbush<>(buildRectangles.toArray(new Rectangle[] {}));
        for (Rectangle query : probeRectangles) {
            List<Rectangle> actual = findIntersections(rtree, query);
            List<Rectangle> expected = buildRectangles.stream()
                    .filter(rect -> rect.intersects(query))
                    .collect(toList());
            assertEqualsSorted(actual, expected, RECTANGLE_COMPARATOR);
        }
    }