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

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

The newest version!
/*
 * (C) Copyright 2018-2023, 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.*;
import org.jgrapht.alg.drawing.FRQuadTree.*;
import org.jgrapht.alg.drawing.model.*;
import org.jgrapht.alg.util.*;

import java.util.*;

/**
 * Fruchterman and Reingold Force-Directed Placement Algorithm using the
 * Barnes-Hut indexing
 * technique with a QuadTree.
 * 
 * The Barnes-Hut indexing technique is described in the following paper:
 * 
    *
  • J. Barnes and P. Hut. A hierarchical O(N log N) force-calculation algorithm. Nature. * 324(4):446--449, 1986.
  • *
* * @author Dimitrios Michail * * @param the graph vertex type * @param the graph edge type */ public class IndexedFRLayoutAlgorithm2D extends FRLayoutAlgorithm2D { /** * Default $\theta$ value for approximation using the Barnes-Hut technique */ public static final double DEFAULT_THETA_FACTOR = 0.5; protected double theta; protected long savedComparisons; /** * Create a new layout algorithm */ public IndexedFRLayoutAlgorithm2D() { this(DEFAULT_ITERATIONS, DEFAULT_THETA_FACTOR, DEFAULT_NORMALIZATION_FACTOR); } /** * Create a new layout algorithm * * @param iterations number of iterations * @param theta parameter for approximation using the Barnes-Hut technique */ public IndexedFRLayoutAlgorithm2D(int iterations, double theta) { this(iterations, theta, DEFAULT_NORMALIZATION_FACTOR); } /** * Create a new layout algorithm * * @param iterations number of iterations * @param theta parameter for approximation using the Barnes-Hut technique * @param normalizationFactor normalization factor for the optimal distance */ public IndexedFRLayoutAlgorithm2D(int iterations, double theta, double normalizationFactor) { this(iterations, theta, normalizationFactor, new Random()); } /** * Create a new layout algorithm * * @param iterations number of iterations * @param theta theta parameter for the Barnes-Hut approximation * @param normalizationFactor normalization factor for the optimal distance * @param rng the random number generator */ public IndexedFRLayoutAlgorithm2D( int iterations, double theta, double normalizationFactor, Random rng) { this( iterations, theta, normalizationFactor, rng, ToleranceDoubleComparator.DEFAULT_EPSILON); } /** * Create a new layout algorithm * * @param iterations number of iterations * @param theta theta parameter for the Barnes-Hut approximation * @param normalizationFactor normalization factor for the optimal distance * @param rng the random number generator * @param tolerance tolerance used when comparing floating point values */ public IndexedFRLayoutAlgorithm2D( int iterations, double theta, double normalizationFactor, Random rng, double tolerance) { super(iterations, normalizationFactor, rng, tolerance); this.theta = theta; if (theta < 0d || theta > 1d) { throw new IllegalArgumentException("Illegal theta value"); } this.savedComparisons = 0; } @Override public void layout(Graph graph, LayoutModel2D model) { this.savedComparisons = 0; super.layout(graph, model); } @Override protected Map calculateRepulsiveForces(Graph graph, LayoutModel2D model) { // index all points FRQuadTree quadTree = new FRQuadTree(model.getDrawableArea()); for (V v : graph.vertexSet()) { quadTree.insert(model.get(v)); } Point2D origin = Point2D.of(model.getDrawableArea().getMinX(), model.getDrawableArea().getMinY()); // compute displacement with index Map disp = new HashMap<>(); for (V v : graph.vertexSet()) { Point2D vPos = Points.subtract(model.get(v), origin); Point2D vDisp = Point2D.of(0d, 0d); Deque queue = new ArrayDeque<>(); queue.add(quadTree.getRoot()); while (!queue.isEmpty()) { Node node = queue.removeFirst(); Box2D box = node.getBox(); double boxWidth = box.getWidth(); Point2D uPos = null; if (node.isLeaf()) { if (!node.hasPoints()) { continue; } uPos = Points.subtract(node.getPoints().iterator().next(), origin); } else { double distanceToCentroid = Points.length(Points.subtract(vPos, node.getCentroid())); if (comparator.compare(distanceToCentroid, 0d) == 0) { savedComparisons += node.getNumberOfPoints() - 1; continue; } else if (comparator.compare(boxWidth / distanceToCentroid, theta) < 0) { uPos = Points.subtract(node.getCentroid(), origin); savedComparisons += node.getNumberOfPoints() - 1; } else { for (Node child : node.getChildren()) { queue.add(child); } continue; } } if (comparator.compare(vPos.getX(), uPos.getX()) != 0 || comparator.compare(vPos.getY(), uPos.getY()) != 0) { Point2D delta = Points.subtract(vPos, uPos); double deltaLen = Points.length(delta); Point2D dispContribution = Points.scalarMultiply(delta, repulsiveForce(deltaLen) / deltaLen); vDisp = Points.add(vDisp, dispContribution); } } disp.put(v, vDisp); } return disp; } /** * Get the total number of saved comparisons due to the Barnes-Hut technique. * * @return the total number of saved comparisons */ public long getSavedComparisons() { return savedComparisons; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy