/* Copyright 2008-2010 Gephi Authors : Helder Suzuki <heldersuzuki@gephi.org> Website : http://www.gephi.org This file is part of Gephi. Gephi is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Gephi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with Gephi. If not, see <http://www.gnu.org/licenses/>. */ package org.gephi.layout.plugin.force.quadtree; import java.util.ArrayList; import java.util.List; import org.gephi.graph.api.HierarchicalGraph; import org.gephi.graph.api.Node; import org.gephi.graph.api.Spatial; /** * @author Helder Suzuki <heldersuzuki@gephi.org> */ public class QuadTree implements Spatial { private float posX; private float posY; private float size; private float centerMassX; // X and Y position of the center of mass private float centerMassY; private int mass; // Mass of this tree (the number of nodes it contains) private int maxLevel; private AddBehaviour add; private List<QuadTree> children; private boolean isLeaf; public static final float eps = (float) 1e-6; public static QuadTree buildTree(HierarchicalGraph graph, int maxLevel) { float minX = Float.POSITIVE_INFINITY; float maxX = Float.NEGATIVE_INFINITY; float minY = Float.POSITIVE_INFINITY; float maxY = Float.NEGATIVE_INFINITY; for (Node node : graph.getTopNodes()) { minX = Math.min(minX, node.getNodeData().x()); maxX = Math.max(maxX, node.getNodeData().x()); minY = Math.min(minY, node.getNodeData().y()); maxY = Math.max(maxY, node.getNodeData().y()); } float size = Math.max(maxY - minY, maxX - minX); QuadTree tree = new QuadTree(minX, minY, size, maxLevel); for (Node node : graph.getTopNodes()) { tree.addNode(node.getNodeData()); } return tree; } public QuadTree(float posX, float posY, float size, int maxLevel) { this.posX = posX; this.posY = posY; this.size = size; this.maxLevel = maxLevel; this.isLeaf = true; mass = 0; add = new FirstAdd(); } public float size() { return size; } private void divideTree() { float childSize = size / 2; children = new ArrayList<QuadTree>(); children.add(new QuadTree(posX + childSize, posY + childSize, childSize, maxLevel - 1)); children.add(new QuadTree(posX, posY + childSize, childSize, maxLevel - 1)); children.add(new QuadTree(posX, posY, childSize, maxLevel - 1)); children.add(new QuadTree(posX + childSize, posY, childSize, maxLevel - 1)); isLeaf = false; } private boolean addToChildren(Spatial node) { for (QuadTree q : children) { if (q.addNode(node)) { return true; } } return false; } private void assimilateNode(Spatial node) { centerMassX = (mass * centerMassX + node.x()) / (mass + 1); centerMassY = (mass * centerMassY + node.y()) / (mass + 1); mass++; } public Iterable<QuadTree> getChildren() { return children; } public float x() { return centerMassX; } public float y() { return centerMassY; } public int mass() { return mass; } public float z() { throw new UnsupportedOperationException("Not supported yet."); } public boolean addNode(Spatial node) { if (posX <= node.x() && node.x() <= posX + size && posY <= node.y() && node.y() <= posY + size) { return add.addNode(node); } else { return false; } } /** * @return the isLeaf */ public boolean isIsLeaf() { return isLeaf; } class FirstAdd implements AddBehaviour { public boolean addNode(Spatial node) { mass = 1; centerMassX = node.x(); centerMassY = node.y(); if (maxLevel == 0) { add = new LeafAdd(); } else { add = new SecondAdd(); } return true; } } class SecondAdd implements AddBehaviour { public boolean addNode(Spatial node) { divideTree(); add = new RootAdd(); /* This QuadTree represents one node, add it to a child accordingly */ addToChildren(QuadTree.this); return add.addNode(node); } } class LeafAdd implements AddBehaviour { public boolean addNode(Spatial node) { assimilateNode(node); return true; } } class RootAdd implements AddBehaviour { public boolean addNode(Spatial node) { assimilateNode(node); return addToChildren(node); } } } interface AddBehaviour { public boolean addNode(Spatial node); }