All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jgrapht.alg.drawing.FRQuadTree Maven / Gradle / Ivy

/*
 * (C) Copyright 2018-2021, by Dimitrios Michail and Contributors.
 *
 * JGraphT : a free Java graph-theory library
 *
 * See the CONTRIBUTORS.md file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0, or the
 * GNU Lesser General Public License v2.1 or later
 * which is available at
 * http://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR LGPL-2.1-or-later
 */
package org.jgrapht.alg.drawing;

import org.jgrapht.alg.drawing.model.*;
import org.jgrapht.alg.util.*;

import java.util.*;

/**
 * A simple QuadTree for indexing during force
 * calculations in the Fruchterman and Reingold Force-Directed Placement Algorithm.
 *
 * 

* See the following paper for the definition of a QuadTree. *

    *
  • Raphael Finkel and J.L. Bentley. Quad Trees: A Data Structure for Retrieval on Composite * Keys. Acta Informatica, 4(1):1--9, 1974.
  • *
* *

* The tree supports adding points one by one and maintains the centroid and total number of points * on each tree node. * * @author Dimitrios Michail */ class FRQuadTree { private static final int NW = 0; private static final int NE = 1; private static final int SW = 2; private static final int SE = 3; private Node root; /** * Create a new tree for a certain area. * * @param box the area */ public FRQuadTree(Box2D box) { this.root = new Node(box); } /** * Insert a new point. * * @param p the new point */ public void insert(Point2D p) { Node cur = root; while (true) { if (cur.isLeaf()) { if (cur.points.size() == 0) { cur.points.add(p); return; } // split Box2D rect = cur.getBox(); Pair xsplit = Boxes.splitAlongXAxis(rect); Pair west = Boxes.splitAlongYAxis(xsplit.getFirst()); Pair east = Boxes.splitAlongYAxis(xsplit.getSecond()); // create 4 children cur.children = new Node[4]; cur.children[NW] = new Node(west.getSecond()); cur.children[NE] = new Node(east.getSecond()); cur.children[SW] = new Node(west.getFirst()); cur.children[SE] = new Node(east.getFirst()); // distribute old points and compute centroid double centroidX = 0, centroidY = 0; for (Point2D point : cur.points) { if (Boxes.containsPoint(cur.children[NW].getBox(), point)) { cur.children[NW].points.add(point); } else if (Boxes.containsPoint(cur.children[NE].getBox(), point)) { cur.children[NE].points.add(point); } else if (Boxes.containsPoint(cur.children[SW].getBox(), point)) { cur.children[SW].points.add(point); } else if (Boxes.containsPoint(cur.children[SE].getBox(), point)) { cur.children[SE].points.add(point); } centroidX += point.getX(); centroidY += point.getY(); } cur.totalPoints = cur.points.size(); cur.centroid = Point2D.of(centroidX / cur.totalPoints, centroidY / cur.totalPoints); // change from leaf to internal node cur.points = null; } // here we are not a leaf // count new point and update centroid cur.totalPoints++; cur.centroid = Point2D .of( (cur.centroid.getX() * (cur.totalPoints - 1) + p.getX()) / cur.totalPoints, (cur.centroid.getY() * (cur.totalPoints - 1) + p.getY()) / cur.totalPoints); // non-leaf if (Boxes.containsPoint(cur.children[NW].getBox(), p)) { cur = cur.children[NW]; } else if (Boxes.containsPoint(cur.children[NE].getBox(), p)) { cur = cur.children[NE]; } else if (Boxes.containsPoint(cur.children[SW].getBox(), p)) { cur = cur.children[SW]; } else if (Boxes.containsPoint(cur.children[SE].getBox(), p)) { cur = cur.children[SE]; } else { throw new IllegalArgumentException(); } } } /** * Get the root node of the tree. * * @return the root */ public Node getRoot() { return root; } /** * The Quad-Tree node. * * @author Dimitrios Michail */ public class Node { // node region Box2D box; // internal node int totalPoints; Point2D centroid; Node[] children; // leaf node List points; /** * Create a new node for a given area * * @param box the area */ public Node(Box2D box) { this.box = Objects.requireNonNull(box); this.points = new ArrayList<>(); } /** * Check if a node is a leaf. * * @return true if leaf, false otherwise */ public boolean isLeaf() { return points != null; } /** * Get a list of all points contained in this node. * * @return a list of points */ public List getPoints() { if (points != null) { return points; } else { List result = new ArrayList<>(); getChildren().forEach(node -> { result.addAll(node.getPoints()); }); return result; } } /** * Check if the node contains any points. * * @return true if the node contains points, false otherwise */ public boolean hasPoints() { if (points != null) { return points.size() != 0; } else { return totalPoints != 0; } } /** * Get the area represented by this node. * * @return the area of the node */ public Box2D getBox() { return box; } /** * Get the total number of points under this node. * * @return the total number of points */ public int getNumberOfPoints() { if (points != null) { return points.size(); } else { return totalPoints; } } /** * Get the centroid of all points contained in this node. * * @return the centroid of all points contained in this node */ public Point2D getCentroid() { if (points != null) { int numPoints = points.size(); if (numPoints == 0) { throw new IllegalArgumentException("No points"); } double x = 0, y = 0; for (Point2D p : points) { x += p.getX(); y += p.getY(); } return Point2D.of(x / numPoints, y / numPoints); } else { return centroid; } } /** * Get the children of this node as a list. * * @return a list containing the children of this node */ public List getChildren() { if (children == null) { return Collections.emptyList(); } return Arrays.asList(children); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy