Package org.rascalmpl.library.vis.figure.graph.spring

Source Code of org.rascalmpl.library.vis.figure.graph.spring.SpringGraphNode

/*******************************************************************************
* Copyright (c) 2009-2013 CWI
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:

*   * Paul Klint - Paul.Klint@cwi.nl - CWI
*******************************************************************************/
package org.rascalmpl.library.vis.figure.graph.spring;

import java.util.LinkedList;

import org.rascalmpl.library.vis.figure.Figure;
import org.rascalmpl.library.vis.util.FigureMath;
import org.rascalmpl.library.vis.util.vector.Rectangle;
import org.rascalmpl.library.vis.util.vector.Vector2D;

/**
* A SpringGraphNode is created for each "node" constructor that occurs in the
* graph.
*
* @author paulk
*
*/
public class SpringGraphNode extends Figure {

  private final SpringGraph G;
  protected final String name;
  protected final Figure figure;
  double x;  // Coordinates of center of node
  double y;

  double temperature;
  double skew;
  Vector2D oldImpulse;

  protected LinkedList<SpringGraphNode> in;
  protected LinkedList<SpringGraphNode> out;
  private static boolean debug = false;

  SpringGraphNode(SpringGraph springGraph, String name, Figure fig) {
    super(springGraph.prop);
    this.G = springGraph;
    this.name = name;
    this.figure = fig;
    this.children = new Figure[1];
    this.children[0] = fig;
    in = new LinkedList<SpringGraphNode>();
    out = new LinkedList<SpringGraphNode>();
  }

  public void init() {
    x = FigureMath.random(width()/2, G.minSize.getX() - width()/2);
    y = FigureMath.random(height()/2, G.minSize.getY() -height()/2);
    temperature = G.MAX_LOCAL_TEMPERATURE;
    skew = 0;
    oldImpulse = new Vector2D(0, 0);
  }

  public void addIn(SpringGraphNode n) {
    if (!in.contains(n))
      in.add(n);
  }

  public void addOut(SpringGraphNode n) {
    if (!out.contains(n))
      out.add(n);
  }

  public int degree() {
    return in.size() + out.size();
  }

  double width() {
    return figure != null ? figure.minSize.getX() : 0;
  }

  double height() {
    return figure != null ? figure.minSize.getY() : 0;
  }

  protected double getCenterX() {
    return x;
  }
 
  protected double getCenterY() {
    return y;
  }
 
  public Vector2D getCenter() {
    return new Vector2D(x, y);
  }
 
  // Move node during simulation
 
  protected void moveBy(double dx, double dy) {
    x += dx;
    y += dy;
  }
 
  // Place node after one round, update all dependent positions
 
  protected void placeCenter(double newX, double newY) {
    double w2 = width()/2;
    double h2 = height()/2;
    if (newX <= w2){
      System.err.printf("ERROR: node %s, x outside boundary: %f\n", name,  newX);
        this.x = w2 + 20
        if(oldImpulse.getX() < 0)
          oldImpulse.setX(-oldImpulse.getX());
    } else
    if(newX >= G.minSize.getX() - w2){
      System.err.printf("ERROR: node %s, x outside boundary: %f\n", name,  newX);
      this.x = G.minSize.getX() - w2 - 20;
      if(oldImpulse.getX() > 0)
        oldImpulse.setX(-oldImpulse.getX());
    } else
      this.x = newX;
   
   
    if (newY <= h2){
      System.err.printf("ERROR: node %s, y outside boundary: %f\n", name,  newY);
        this.y = h2 + 20
        if(oldImpulse.getY() < 0)
          oldImpulse.setY(-oldImpulse.getY());
    } else
    if(newY >= G.minSize.getY() - h2){
      System.err.printf("ERROR: node %s, y outside boundary: %f\n", name,  newY);
      this.y = G.minSize.getY() - h2 - 20
      if(oldImpulse.getY() > 0)
        oldImpulse.setY(-oldImpulse.getY());
    } else
      this.y = newY;
   
    setElementPosition();
  }
 
  public double distance(SpringGraphNode other){
    return getCenter().distance(other.getCenter()); //  - (radius() + other.radius()));// - Math.max(width(), height())/2 - Math.max(other.width(), other.height())/2;
  }
 
  public double distance2(SpringGraphNode other){
    double d = distance(other);
    return d * d;
    //return getCenter().distance2(other.getCenter());
  }
 
  public double radius(){
    double w = width();
    double h = height();
    return Math.sqrt(w * w + h * h);
  }
 
  // Mass of this node: surface * number of connections.
 
  public double getMass(){
    return  0.005 * width() * height() (1 + degree() / 2);
  }

  @Override
  public void computeMinSize() {
    minSize.set(figure.minSize.getX(), figure.minSize.getY());
  }

  @Override
  public void resizeElement(Rectangle view) {
    localLocation.set(0, 0);
    setElementPosition();
  }
 
  void setElementPosition(){
    figure.localLocation.set(
        localLocation.getX() + x - figure.minSize.getX() / 2,
        localLocation.getY() + y - figure.minSize.getY() / 2);
   
    figure.globalLocation.set(globalLocation);
    figure.globalLocation.set(figure.localLocation);
    figure.updateGlobalLocation();
   
    figure.computeMinSize();
  }

  // ---------------------

  private void print(String msg) {
    double ix = oldImpulse == null ? 0 : oldImpulse.getX();
    double iy = oldImpulse == null ? 0 : oldImpulse.getY();
    System.err.printf("%s: %s: (%3.2f,%3.2f), imp (%3.2f,%3.2f), temp %4.1f, skew %2.1f\n",
              msg, name, x, y, ix, iy, temperature, skew);
  }

  // Perform one step of the spring simulation algorithm for this node

  public void step() {
    if(debug) print("update, before");
    Vector2D impulse = computeNodeImpulse();
    double angle = oldImpulse.angle(impulse);
    adjustTemperatureAndSkew(angle);
    double dx = G.UPDATE_STEP * impulse.getX() * temperature;
    double dy = G.UPDATE_STEP * impulse.getY() * temperature;

    moveBy(dx, dy); // adjust local position

    if(debug){
      print("relax, after ");
      System.err.printf("             imp (%3.2f, %3.2f), angle %1.2f, dx %3.2f, dy %3.2f\n",
            impulse.getX(), impulse.getY(), Math.toDegrees(angle),
            dx, dy);
    }
    oldImpulse = impulse;
  }

  /**
   * Compute the attractive force A with another node:
   * A = (this - other) * distance^2 / (EDGE_LENGTH_2 * PHI) * ATTRACT
   *
   */
  public Vector2D attractiveForce(SpringGraphNode other) {
    double distance2 = distance2(other);
    Vector2D thisVector = new Vector2D(x, y);
    Vector2D otherVector = new Vector2D(other.x, other.y);
    return thisVector.sub(otherVector).mul(distance2).div(G.EDGE_LENGTH_2 * getMass()) .mul(G.ATTRACT);
  }

  /**
   * Compute repulsive force R with another node:
   * R = (this - other) * EDGE_LENGHT_2 / distance ^ 2
   *
   */
  public Vector2D repulsiveForce(SpringGraphNode other) {
    double distance2 = distance2(other);

    if (distance2 > 0) {
      Vector2D thisVector = new Vector2D(getCenter());
      Vector2D otherVector = new Vector2D(other.getCenter());
      return thisVector.sub(otherVector).mul(G.EDGE_LENGTH_2).div(distance2).mul(G.REPEL);
    }
    return (new Vector2D(0, 0));
  }
 
  public Vector2D repulsiveForceWall(Vector2D wallVector) {
    Vector2D thisVector = new Vector2D(getCenter());
    double distance2 = thisVector.distance2(wallVector);

    if (distance2 > 0) {
      return thisVector.sub(wallVector).mul(G.EDGE_LENGTH_2).div(distance2).mul(10 * G.REPEL);
    }
    return (new Vector2D(0, 0));
  }

  private final static double Deg45 = Math.toDegrees(Math.PI/4);
  private final static double DegMin45 = Math.toDegrees(-Math.PI/4);
  private final static double Deg145 = Math.toDegrees(3*Math.PI/4);
  private final static double Deg225 = Math.toDegrees(5*Math.PI/4);
 
  /**
   * Adjust the temperature of the node according to the old temperature and
   * the the old impulse
   */
  public void adjustTemperatureAndSkew(double angle) {
    // Angle is a positive value between 0 and 2 pi.
    if (angle < Deg45 || angle > DegMin45 || (angle > Deg145 && angle < Deg225)) {
      // between -45 and 45 degrees there is acceleration,
      // between 145 and 225 degree there is oscillation
      temperature *= (1 + G.OSCILLATION * Math.cos(angle));
    } else {
      // in the other ranges of the angle, there is rotation:
      skew += G.SKEW * Math.sin(angle);
      temperature -= G.ROTATION * Math.abs(skew);
    }
    temperature = FigureMath.constrain(temperature, 0, G.MAX_LOCAL_TEMPERATURE);
  }

  /**
   * This method computes the normalized impulse F of the node by the sum of
   * all force vectors. F = gravity + random + sum(n in nodes, repel(n)) -
   * sum(n in adjacent, attract(n)) Returns norm(F)
   */
  public Vector2D computeNodeImpulse() {

    // Add a random force and the gravitational force
    Vector2D resultForce = gravitionalForce().add(randomForce());

    // Repulsive forces
    for (SpringGraphNode otherNode : G.nodes) {
      if (otherNode != this) {
        resultForce = resultForce.add(repulsiveForce(otherNode));
      }
    }

    // Attractive forces
    for (SpringGraphNode otherNode : in) {
      resultForce = resultForce.sub(this.attractiveForce(otherNode));
    }

    for (SpringGraphNode otherNode : out) {
      resultForce = resultForce.sub(attractiveForce(otherNode));
    }
   
    // Repulsion of left and right wall
   
    resultForce = resultForce.add(repulsiveForceWall(new Vector2D(0, y)));
    resultForce = resultForce.add(repulsiveForceWall(new Vector2D(G.minSize.getX(), y)));
   
    // Repulsion of top and bottom wall
   
    resultForce = resultForce.add(repulsiveForceWall(new Vector2D(x, 0)));
    resultForce = resultForce.add(repulsiveForceWall(new Vector2D(x, G.minSize.getY())));

    // we only need the impulse
    return resultForce.normalize();
  }

  /**
   * Compute the random force.
   * We need a random impulse [-RAND_CONSTANT, ..., +RAND_CONSTANT]
   * Range should be approx. [-1/4 ... +1/4] of desired edge length.
   */
  public Vector2D randomForce() {
    return new Vector2D(FigureMath.random(-G.RAND_DISTURB, G.RAND_DISTURB),
                      FigureMath.random(-G.RAND_DISTURB, G.RAND_DISTURB));
  }

  /**
   * This method computes the gravitational force between a node and the
   * barycenter of the drawing.
   * G = (barycenter - thisVector) * Mass * Gravity
   */
  public Vector2D gravitionalForce() {
    Vector2D barycenter = new Vector2D(G.getBaryCenter());
    Vector2D thisVector = new Vector2D(getCenter());
    return barycenter.sub(thisVector).mul(getMass()).mul(G.GRAVITY);
  }
}
TOP

Related Classes of org.rascalmpl.library.vis.figure.graph.spring.SpringGraphNode

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.