> show canvas only <


/* built with Studio Sketchpad: 
 *   https://sketchpad.cc
 * 
 * observe the evolution of this sketch: 
 *   https://p5js.sketchpad.cc/sp/pad/view/ro.tO37L9PB$pH/rev.198
 * 
 * authors: 
 *   David Griswold

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



var myGraph=new Graph();
var moving;
var connecting;
var xdistance;
var ydistance;
var movingCircle;
var startNode;
var promptedNoKey;

function buildGraph() {
 

//you build a graph by adding edges (nodes are built automatically as needed, one per name
  //For now, all edges are undirected, but the ability to add directions is available, though they can't be visualized.
  myGraph.addEdge("A","B",10); //adds an edge between nodes A to B of weight 10
  myGraph.addEdge("B","C",11); //adds an edge from B to C of weight 11. 
  myGraph.addEdge("E", "D", 3); //adds an edge from E to D of weight 3.
  myGraph.addEdge("A","E",6);
  myGraph.addEdge("A","D",1);
  myGraph.addEdge("C","D",4);
 
  //other commands you can use:
  //g.getEdges() returns a list of Edges
  //g.getNodes() returns a list of Nodes
  //g.getNodeString() returns a string you can print to visualize the graph
  
  //n.getEdgesOut() returns a list of edges going OUT of a Node.
  //n.getEdgesIn() returns a list of edges coming IN to a Node This list is the same as above for undirected.
  //n.getName()
  
  //e.getToNode() returns the node the edge is pointing toward ()
  //e.getFromNode() returns the node the edge is coming from
  //e.getWeight() returns the weight of an edge
  
}

function setup() {
  createCanvas(500,500);
  textSize(16);
  textAlign(CENTER,CENTER);
  
  //buildGraph();
}

function draw() {
  clear();
  fill(0);
  noStroke();
  text("Ctrl-click for new node. Shift-drag for new edge. Drag to move.",250,10);
  if (!mouseIsPressed) {
    moving=false; 
    }
  var nodes = myGraph.getNodes();
  var edges = myGraph.getEdges();
  for (i=0;i<edges.length;i++) {
    x1=edges[i].getFromNode().getX();
    y1=edges[i].getFromNode().getY();
    x2=edges[i].getToNode().getX();
    y2=edges[i].getToNode().getY();
    stroke(0)
    line(x1,y1,x2,y2);
    fill(0);
    var xpos=(x1+x2)/2;
    var ypos=(y1+y2)/2;
    var v1=createVector(x2-x1,y2-y1);
    v1.normalize();
    v1.rotate(PI/2);
    v1.mult(10);
    noStroke();
    text(edges[i].getWeight(),xpos+v1.x,ypos+v1.y);
  }
  for (i=0;i<nodes.length;i++) {
    if(moving && movingCircle==i) {
        nodes[i].move(mouseX-xdistance,mouseY-ydistance);
    }
    if(distance(mouseX,mouseY,nodes[i].getX(),nodes[i].getY())<=25) {
      fill(128,0,0);
      if(mouseIsPressed && !moving && !connecting && (!keyIsDown(SHIFT) || promptedNoKey)) {
        moving=true;
        xdistance=mouseX-nodes[i].getX();
        ydistance=mouseY-nodes[i].getY();
        movingCircle=i;
      }
      if(mouseIsPressed && !moving && !connecting && keyIsDown(SHIFT) &&!promptedNoKey) {
        connecting=true;
        startNode=i;
      }
      if (!mouseIsPressed && connecting && startNode!=i) {
        connecting=false;
        var w=prompt("Weight for this edge?");
        if(w!=null) {myGraph.addEdge(nodes[startNode],nodes[i], w);}
        promptedNoKey=true;
      }
    }else{
      fill(0,0,128);
    }
    ellipse(nodes[i].getX(), nodes[i].getY(),50,50);
    fill(255,255,255);
    noStroke();
    text(nodes[i].getName(),nodes[i].getX(),nodes[i].getY());
  }
  if(!mouseIsPressed) connecting=false;
  
  if(connecting) {
    stroke(255,0,0);
    line(nodes[startNode].getX(),nodes[startNode].getY(),mouseX,mouseY);
    stroke(0);
  }
}

function mouseClicked() {
  if (!moving && !connecting && keyIsDown(CONTROL) &&!promptedNoKey) {
    var n=prompt("What should I name this new node?")
    promptedNoKey=true;
    if (n != null) {
      var newNode=myGraph.addNode(n);
      newNode.move(mouseX,mouseY);
    }
  }
  return false;
}
function keyPressed(){
    promptedNoKey=false;
  // Do something  
  return false; // prevent any default behaviour
  }
function distance(x1,y1,x2,y2) {
  return sqrt(sq(x2-x1)+sq(y2-y1));
}

//----------------------------------//
function Edge(from, to, w, g,twoway) {
  this.fromNode = from;
  this.toNode = to;
  this.weight = w;
  this.graph = g;
  this.twoway=twoway;
  this.getGraph = function() {return this.getGraph;}
  this.getFromNode = function() {return this.fromNode;}
  this.getToNode = function() {return this.toNode;}
  this.getWeight = function() {return this.weight;}
  this.getOtherNode = function(n) {
    if (n instanceof Node) {
      if (n===this.toNode) {
        return this.fromNode;
      }else{
        return this.toNode;
      }
    }else{
      if(n===this.toNode.getName()) {
        return this.fromNode;
      }else{
        return this.toNode;
      }
    }
  }
}

function Node(n, g) {
  this.xpos = random(25,475);
  this.ypos = random(25,475);
  this.name = n;
  this.edgesOut = [];
  this.edgesIn = [];
  this.graph = g;
  this.getEdgesOut = function() {return this.edgesOut;}
  this.getEdgesIn = function() {return this.edgesIn;}
  this.getName = function() {return this.name;}
  this.getGraph = function() {return this.graph;}
  this.move = function(x,y) {
    this.xpos = x;
    this.ypos = y;
  }
  this.getX = function() {return this.xpos};
  this.getY = function() {return this.ypos};
  this.addEdgeOut = function(e) {
    this.edgesOut.push(e);
  }
  this.addEdgeIn = function(e) {
    this.edgesIn.push(e);
  }
}

function Graph() {
  this.nodes = [];
  this.edges = [];
  
  this.addNode = function(name) {
    n = new Node(name, this);
    
    this.nodes.push(n);
    return n;
  }
  
  this.addEdge= function(fromNodeName, toNodeName, weight) {
    var n1;
    var n2;
    if (fromNodeName instanceof Node) n1=fromNodeName;
    if (toNodeName instanceof Node) n2=toNodeName;
    if(n1===undefined || n2===undefined) {
      for (i=0;i<this.nodes.length;i++) {
        if (n1===undefined) if(this.nodes[i].getName()===fromNodeName) n1=this.nodes[i];
        if (n2===undefined) if(this.nodes[i].getName()===toNodeName) n2=this.nodes[i];
      }
    }
    if (n1 === undefined) {
      n1 = this.addNode(fromNodeName);
    }
    if (n2=== undefined) {
      n2 = this.addNode(toNodeName);
    }
    var e = new Edge(n1, n2, weight, this,true);
    n2.addEdgeIn(e);
    n1.addEdgeIn(e);
    n1.addEdgeOut(e);
    n2.addEdgeOut(e);
    this.edges.push(e);
    return e;
  }
  
  this.getNodes = function() {return this.nodes;}
  this.getEdges = function() {return this.edges;}
  this.getNodeString = function() {
    var nodestring="";
    console.log(this.nodes.length);
    for (i=0;i<this.nodes.length;i++) {
      nodestring += this.nodes[i].getName()+" --> {";
      var e = this.nodes[i].getEdgesOut();
      for (j=0;j<e.length;j++) {
        var n=e[j].getOtherNode(this.nodes[i]);
        nodestring = nodestring + n.getName() + "," +e[j].getWeight()+" ";
      }
    nodestring=nodestring.trim();
    nodestring = nodestring + '}   ';
    }
  nodestring = nodestring.trim();
  return nodestring;
  }
}