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

org.jungrapht.visualization.layout.algorithms.repulsion.BarnesHutSpringRepulsion Maven / Gradle / Ivy

The newest version!
package org.jungrapht.visualization.layout.algorithms.repulsion;

import static org.jungrapht.visualization.layout.algorithms.SpringLayoutAlgorithm.*;

import java.util.ConcurrentModificationException;
import java.util.Map;
import java.util.Random;
import org.jungrapht.visualization.layout.model.LayoutModel;
import org.jungrapht.visualization.layout.model.Point;
import org.jungrapht.visualization.layout.quadtree.BarnesHutQuadTree;
import org.jungrapht.visualization.layout.quadtree.ForceObject;
import org.jungrapht.visualization.layout.quadtree.Node;

/**
 * @author Tom Nelson
 * @param  the vertex type
 */
public class BarnesHutSpringRepulsion
    extends StandardSpringRepulsion<
        V, BarnesHutSpringRepulsion, BarnesHutSpringRepulsion.Builder>
    implements BarnesHutRepulsion<
        V, BarnesHutSpringRepulsion, BarnesHutSpringRepulsion.Builder> {

  public static class Builder
      extends StandardSpringRepulsion.Builder<
          V, BarnesHutSpringRepulsion, BarnesHutSpringRepulsion.Builder>
      implements BarnesHutRepulsion.Builder<
          V, BarnesHutSpringRepulsion, BarnesHutSpringRepulsion.Builder> {

    private double theta = Node.DEFAULT_THETA;
    private BarnesHutQuadTree tree = new BarnesHutQuadTree<>();

    public Builder layoutModel(LayoutModel layoutModel) {
      this.layoutModel = layoutModel;
      this.tree =
          BarnesHutQuadTree.builder()
              .bounds(layoutModel.getWidth(), layoutModel.getHeight())
              .theta(theta)
              .build();
      return this;
    }

    public Builder theta(double theta) {
      this.theta = theta;
      return this;
    }

    public Builder nodeData(Map springVertexData) {
      this.springVertexData = springVertexData;
      return this;
    }

    public Builder repulsionRangeSquared(int repulsionRangeSquared) {
      this.repulsionRangeSquared = repulsionRangeSquared;
      return this;
    }

    @Override
    public Builder random(Random random) {
      this.random = random;
      return this;
    }

    public BarnesHutSpringRepulsion build() {
      return new BarnesHutSpringRepulsion(this);
    }
  }

  protected BarnesHutQuadTree tree;

  public static Builder builder() {
    return new Builder();
  }

  @Deprecated
  public static Builder barnesHutBuilder() {
    return builder();
  }

  protected BarnesHutSpringRepulsion(Builder builder) {
    super(builder);
    this.tree = builder.tree;
  }

  public void step() {
    tree.rebuild(layoutModel.getGraph().vertexSet(), layoutModel);
  }

  public void calculateRepulsion() {
    try {
      for (V vertex : vertexSet) {

        if (layoutModel.isLocked(vertex)) {
          continue;
        }

        SpringVertexData svd = springVertexData.getOrDefault(vertex, new SpringVertexData());
        Point forcePoint = layoutModel.apply(vertex);
        ForceObject nodeForceObject =
            new ForceObject(vertex, forcePoint.x, forcePoint.y) {
              @Override
              protected void addForceFrom(ForceObject other) {

                if (other == null || vertex == other.getElement()) {
                  return;
                }
                Point p = this.p;
                Point p2 = other.p;
                if (p == null || p2 == null) {
                  return;
                }
                double vx = p.x - p2.x;
                double vy = p.y - p2.y;
                double distanceSq = p.distanceSquared(p2);
                if (distanceSq == 0) {
                  f = f.add(random.nextDouble(), random.nextDouble());
                } else if (distanceSq < repulsionRangeSquared) {
                  double factor = 1;
                  f = f.add(factor * vx / distanceSq, factor * vy / distanceSq);
                }
              }
            };
        tree.applyForcesTo(nodeForceObject);
        Point f = Point.of(nodeForceObject.f.x, nodeForceObject.f.y);
        double dlen = f.x * f.x + f.y * f.y;
        if (dlen > 0) {
          dlen = Math.sqrt(dlen) / 2;
          svd.repulsiondx += f.x / dlen;
          svd.repulsiondy += f.y / dlen;
        }
      }
    } catch (ConcurrentModificationException cme) {
      calculateRepulsion();
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy