> show canvas only <


/* built with Studio Sketchpad: 
 *   https://sketchpad.cc
 * 
 * observe the evolution of this sketch: 
 *   https://studio.sketchpad.cc/sp/pad/view/ro.lgYAWdDaE9R/rev.661
 * 
 * authors: 
 *   Rui Gil

 * license (unless otherwise specified): 
 *   creative commons attribution-share alike 3.0 license.
 *   https://creativecommons.org/licenses/by-sa/3.0/ 
 */ 



// new script

Network network;

void setup() { 
  size(512,512); 
  colorMode(HSB, 255); 
  smooth();
  frameRate(30);
  network = new Network();
} 

void draw() {
  background(0);
  network.draw();
}

void mousePressed() {
  network.initialize();
}

class Network { 
  ArrayList edges = new ArrayList(); 
  ArrayList nodes = new ArrayList(); 

  void initialize() { 
    edges.clear(); 
    nodes.clear(); 
    generate();
  } 

  void generate() { 
    // create nodes
    for(int i=0; i<20; i++) { 
      Node n = (Node)randomNode();
      nodes.add(n);
    }
//    edges.add(new Edge(nodes.get(0),nodes.get(1)));
    // connect to neightbours
    for(int i=0; i<nodes.size(); i++) { 
      Node n = (Node)nodes.get(i);
      ArrayList ns = getRandomNodes(n,1);
      for(int j=0; j<ns.size(); j++) {
        Edge e = new Edge(n,ns.get(j));
        edges.add(e);
      }
    }
  } 
  
  ArrayList getRandomNodes(Node original, int n) {
    ArrayList candidates = new ArrayList();
    ArrayList connectedNodes = new ArrayList();
    candidates.addAll(nodes);
    candidates.remove(original);
    for (int i=0; i<n; i++) {
      Node node = (Node)candidates.get(int(random(candidates.size())));
      connectedNodes.add(node);
      candidates.remove(node);
    }
    return connectedNodes;
  }

  Node randomNode() {
    float c = random(255);
    float r = 15;//+random(40);
    Node node = new Node(random(width),random(height),r,c);
    return node;
  }

  Edge randomEdge() {
    // create an edge between two randoms nodes
    int n1 = int(random(nodes.size()));
    // the second node must different that the first.
    int n2 = (1+n1+int(random(nodes.size()-1)))%(nodes.size());
    Edge edge = new Edge((Node)nodes.get(n1),(Node)nodes.get(n2));
    return edge;
  }

  void draw() { 
    // a "static" edge so nodes dont step into each other.
    Edge edge = new Edge(); 

    /*
    unidrected graph
    for (int i=0; i<nodes.size(); i++) {
      for (int j=i+1; j<nodes.size(); j++) {
        Node n1 = (Node)nodes.get(i);
        Node n2 = (Node)nodes.get(j);
        edge.repulse(n1,n2);
      }
    }*/

    // in a directed graph
    for (int i=0; i<nodes.size(); i++) {
      for (int j=0; j<nodes.size(); j++) {
        if (i!=j) {
          Node n1 = (Node)nodes.get(i);
          Node n2 = (Node)nodes.get(j);
          edge.repulse(n1,n2);
        }
      }
    }
    
    for (int i=0; i<nodes.size(); i++) { 
      Node node = (Node)nodes.get(i); 
      node.draw(); 
    } 
    for (int i=0; i<edges.size(); i++) {
      Edge e = (Edge)edges.get(i);
      e.update();
      e.draw();
    }
  } 
} 

class Edge {
  Node n1, n2;
  float damping = 0.75;
  
  Edge() {
  }

  Edge(Node node1, Node node2) {
    n1 = node1;
    n2 = node2;
  }
  
  void update() {
    updatePos(n1,n2);
    updateSync(n1,n2);
  }
  
  void repulse(Node node1, Node node2) {
    float dista = PVector.dist(node1.position, node2.position);
    if (dista < node1.radius+node2.radius) {
      updatePos(node1,node2);
    }
  }


  void updatePos(Node node1, Node node2) {
    float dista = PVector.dist(node1.position, node2.position);
    PVector force = PVector.sub(node1.position, node2.position); 
    force.normalize(); 
    float factor = (dista - (node1.radius+node2.radius+30));//+4*(abs(n1.phase.x-n2.phase.x))));
    force.mult(factor);
    //node2.updatePos(force);
    force.mult(-1);
    node1.updatePos(force);
    node1.velocity.mult(damping);
    //node2.velocity.mult(damping);
  }

  void updateSync(Node node1, Node node2) {
    float dista = PVector.dist(node1.position, node2.position);
    PVector force = PVector.sub(node1.phase, node2.phase);
    //force.mult(1/dista);
    force.normalize(); 
    float factor = (dista);//+4*(abs(n1.phase.x-n2.phase.x))));
    force.mult(1/factor);
    force.mult(-1);
    node1.updatePhase(force);
    //node2.updatePhase(force);
    //node2.phasevelocity.mult(damping);
    node1.phasevelocity.mult(damping);
  }
  
  void draw() {
    stroke(255);
    line(n1.position.x,n1.position.y,n2.position.x,n2.position.y);
  }
}

class Node { 
  PVector position, velocity, aceleration, phase, phasevelocity; 
  float rotation, direction, mass, radius, innerRadius; 
  float clr;
  float ss = 0;

  Node(float gx, float gy, float r,float c) {
    velocity = new PVector(0,0,0);
    position = new PVector(gx,gy,0); 
    phase = new PVector(c*(TWO_PI/255),0,0);
    phasevelocity = new PVector(0,0,0);
    mass = r; 
    radius = r;
  } 

  void updatePhase(PVector force) {
    aceleration = PVector.div(force, mass); 
    phasevelocity.add(aceleration);
    phase.add(phasevelocity);
  }

  void updatePos(PVector force) { 
    aceleration = PVector.div(force, mass); 
    velocity.add(aceleration);
    position.add(velocity);
  }

  void draw() { 
    clr = 128+(cos(phase.x + ss)*128);
    ss += PI/10;
    noStroke();
    fill(color(0,0,clr,255));
    ellipse(position.x,position.y,radius*2,radius*2); 
  } 
}