/* This is circular. */ class Graph{ class Node{ class Connection{ Node nodeFrom; Node nodeTo; float strength; Connection(Node nodeTo, Node nodeFrom){ this(nodeTo, nodeFrom, 1); } Connection(Node nodeTo, Node nodeFrom, float strength){ this.nodeTo = nodeTo; this.nodeFrom = nodeFrom; this.strength = strength; } } int index; float mass; float force; int forceCount; float velocity; float position; Object o; LinkedList nodeConnections; Node(Object o){ this(o, 1); } Node(Object o, float mass){ this.index = nodes.size(); this.o = o; this.mass = mass; this.force = 0; this.forceCount = 0; this.velocity = 0; this.position = 0; nodeConnections = new LinkedList(); } void put(Object o){ this.o = o; } Object get(){ return o; } float distanceTo(Node n){ return distanceBetween(this, n); } void addForce(float force){ this.force += constrain(force,-0.03,0.03); this.forceCount++; } void applyForce(){ if(forceCount>0){ force = force / forceCount; velocity = constrain(velocity*friction + force/mass, -1, 1); position = normalize(position+velocity); force = 0; forceCount = 0; } } void connectTo(Node n, float strength){ if(this.index == n.index) return; Integer hashKey = new Integer(min(this.index,n.index)*hashKeyBase + max(this.index,n.index)); if(connections.containsKey(hashKey)){ Connection c = (Connection) connections.get(hashKey); c.strength = strength; } else{ Connection c = new Connection(this, n, strength); nodeConnections.add(c); n.nodeConnections.add(c); connections.put(hashKey, c); } } } boolean inEquilibrium; float equilibriumThreshold; float averageVelocity; float friction; LinkedList nodes; HashMap connections; int hashKeyBase; Graph(){ inEquilibrium = false; equilibriumThreshold = 0.0001; hashKeyBase = 30000; averageVelocity = 0; friction = 0.75; nodes = new LinkedList(); connections = new HashMap(); } ListIterator listIterator(){ return nodes.listIterator(); } HashMap getConnections(){ return connections; } Node addNode(Object o){ return addNode(o,1); } Node addNode(Object o, float mass){ inEquilibrium = false; Node n = new Node(o, mass); n.position = random(1); nodes.add(n); return n; } Node getNode(int index){ return (Node) nodes.get(index); } boolean contains(Object o){ ListIterator nodeIterator = nodes.listIterator(); while(nodeIterator.hasNext()){ Node n = (Node) nodeIterator.next(); if(n.o == o) return true; } return false; } Node getNode(Object o){ ListIterator nodeIterator = nodes.listIterator(); while(nodeIterator.hasNext()){ Node n = (Node) nodeIterator.next(); if(n.o == o) return n; } return null; } int size(){ return nodes.size(); } void connectNodes(Node n1, Node n2, float w){ n1.connectTo(n2, w); } void update(){ if(!inEquilibrium){ // get all nodes ListIterator nodeIterator = nodes.listIterator(); while(nodeIterator.hasNext()){ Node n = (Node) nodeIterator.next(); // should we even use this? Artist a = (Artist) n.get(); if(a.getPopularity() >= longtail.getRangeMin() && a.getPopularity() <= longtail.getRangeMax() && a.getMaxPopularity() >= maxslider.getPosition()){ ListIterator reactors = nodes.listIterator(n.index+1); while(reactors.hasNext()){ Node r = (Node) reactors.next(); // should we even use this? Artist ar = (Artist) r.get(); if(ar.getPopularity() >= overallPop && ar.getMaxPopularity() >= maxPop){ // distances float d1 = normalize(n.position - r.position); float d2 = normalize(r.position - n.position); if(d1==0 || d2==0){ n.addForce(-0.1); r.addForce( 0.1); } else{ // rejection force float f1 = spacingSlider.getPosition()*0.001*(pow((n.mass + r.mass),2)/(d1*d1)); float f2 = spacingSlider.getPosition()*0.001*(pow((n.mass + r.mass),2)/(d2*d2)); n.addForce(f1-f2); r.addForce(f2-f1); } } } } } // get connections Iterator connectionIterator = connections.values().iterator(); while(connectionIterator.hasNext()){ // get nodes Node.Connection c = (Node.Connection) connectionIterator.next(); Node n1 = c.nodeTo; Node n2 = c.nodeFrom; Artist a1 = (Artist) n1.get(); Artist a2 = (Artist) n2.get(); if(a1.getPopularity() >= longtail.getRangeMin() && a1.getPopularity() <= longtail.getRangeMax() && a1.getMaxPopularity() >= maxslider.getPosition() && a2.getPopularity() >= longtail.getRangeMin() && a2.getPopularity() <= longtail.getRangeMax() && a2.getMaxPopularity() >= maxslider.getPosition()){ // distances float d1 = normalize(n1.position - n2.position); float d2 = normalize(n2.position - n1.position); // attraction force if(d1 1) value--; while(value < 0) value++; return value; } float distanceBetween(Node n1, Node n2){ float p1 = n1.position; float p2 = n2.position; return min(normalize(p1-p2),normalize(p2-p1)); } void sortByPosition(){ Collections.sort(nodes, new SortByPosition()); println("should be sorted now!"); } class SortByPosition implements Comparator{ public int compare(Object o1, Object o2){ return int((((Node) o1).position - ((Node) o2).position)*Integer.MAX_VALUE); } } }