/* ISTA 303 Assignment 1 | |
* Tanner Prynn | |
* communities.pde | |
* Implements a signal-passing automata based on a random graph. | |
* Some notes: | |
* - Nodes are connected according to their relative distances. | |
* - Communication between nodes in the same community is shown in green, | |
* communication between different communities is shown in red. | |
* - Nodes are more likely to communicate within their own communities. | |
* - Messages spread outward from their origination point. | |
* - Larger Nodes are more likely to communicate (based on Perlin noise) | |
* - Inactive nodes are transparent, active nodes are opaque | |
*/ | |
import java.util.List; | |
import java.util.ArrayList; | |
import java.util.Set; | |
import java.util.HashSet; | |
import java.util.HashMap; | |
// Variables controlling the creation of the graph | |
static final int CLUSTERS = 12; | |
static final int NODES_PER_CLUSTER = 70; | |
static final int COMMUNITY_RADIUS = 100; | |
// Variables controlling the behavior of the message passing | |
static final float NEW_MESSAGE_RATE = 0.1; | |
static final float DISTANCE_RATIO_INNER = 0.25; | |
static final float DISTANCE_RATIO_OUTER = 0.15; | |
static final int FRAME_RATE = 5; | |
// Map from community centers to a list of their child nodes | |
HashMap<Node, ArrayList<Node>> centers; | |
// Display variables | |
static final int WIDTH = 500; | |
static final int HEIGHT = 500; | |
static final float OPACITY = 200; | |
// Debug variables | |
static final boolean DEBUG_DRAW_CONNECTIONS = false; | |
void setup() { | |
frameRate(FRAME_RATE); | |
smooth(); | |
size(WIDTH, HEIGHT); | |
background(0); | |
centers = new HashMap<Node, ArrayList<Node>>(); | |
for (int i = 0; i < CLUSTERS; i++) { | |
float x = random(width); | |
float y = random(height); | |
color col = color(random(255),random(255),random(255)); | |
Node c = new Node(x, y, col); | |
centers.put(c, new ArrayList<Node>()); | |
for (int j = 0; j < NODES_PER_CLUSTER; j++) { | |
float theta = random((float)(2*Math.PI)); | |
float r = random(COMMUNITY_RADIUS); | |
float x1 = x + r*cos(theta); | |
float y1 = y + r*sin(theta); | |
centers.get(c).add(new Node(x1, y1, noise(x1, y1), c)); | |
} | |
} | |
// Connect the nodes of the graph based on distance | |
for (Node center : centers.keySet()) { | |
for (Node c1 : centers.get(center)) { | |
// Connect nodes within the community | |
for (Node c2 : centers.get(center)) { | |
if(c1.equals(c2)) continue; | |
if(dist(c1.x, c1.y, c2.x, c2.y) < (COMMUNITY_RADIUS*DISTANCE_RATIO_INNER)) { | |
c1.addNeighbor(c2); | |
} | |
} | |
// Connect to nodes outside the community | |
for (Node otherCenter : centers.keySet()) { | |
if(center.equals(otherCenter)) continue; | |
for(Node c2 : centers.get(otherCenter)) { | |
if(dist(c1.x, c1.y, c2.x, c2.y) < (COMMUNITY_RADIUS*DISTANCE_RATIO_OUTER)) { | |
c1.addNeighbor(c2); | |
} | |
} | |
} | |
} | |
} | |
} | |
void draw() { | |
if(DEBUG_DRAW_CONNECTIONS) { | |
debug_drawConnections(); | |
noLoop(); | |
return; | |
} | |
background(255); | |
// Draw the lines connecting nodes in the graph | |
stroke(0); | |
for (Node center : centers.keySet()) { | |
for (Node c1 : centers.get(center)) { | |
for (Node c2 : c1.neighbors) { | |
line(c1.x, c1.y, c2.x, c2.y); | |
} | |
} | |
} | |
// Draw the nodes and pass messages between them | |
noStroke(); | |
for(Node center : centers.keySet()) { | |
for(Node c : centers.get(center)) { | |
if(c.active()) { | |
c.message_out(); | |
fill(center.c); | |
c.draw(); | |
} | |
else { | |
fill(center.c, OPACITY); | |
c.draw(); | |
} | |
} | |
if(random(1) < NEW_MESSAGE_RATE) { | |
ArrayList<Node> Nodes = centers.get(center); | |
Nodes.get(int(random(Nodes.size()))).message_in(); | |
} | |
} | |
} | |
class Node { | |
private static final int RETRANSMISSION_DELAY = 10; | |
private static final int ACTIVE_TRANSMISSION_PERIOD = 30; | |
private static final int REFRACTORY_PERIOD = 90; | |
private static final float TRANSMISSION_PROB_INSIDER = 0.5; | |
private static final float TRANSMISSION_PROB_OUTSIDER = 0.3; | |
private static final int SCALE = 30; | |
public Set<Node> neighbors; | |
private Node parent; | |
public float x; | |
public float y; | |
public float n; | |
public color c; | |
private int lastFrameActivated = | |
-(RETRANSMISSION_DELAY+ACTIVE_TRANSMISSION_PERIOD+REFRACTORY_PERIOD); | |
/* Default Node | |
* Has an x,y location, Perlin noise value, and parent Node | |
*/ | |
public Node(float x, float y, float n, Node parent) { | |
this.x = x; | |
this.y = y; | |
this.n = n; | |
this.parent = parent; | |
neighbors = new HashSet<Node>(); | |
} | |
/* Invisible Parent Node | |
* Center of each community has an x,y location and a color | |
*/ | |
public Node(float x, float y, color c) { | |
this.x = x; | |
this.y = y; | |
this.c = c; | |
} | |
public void addNeighbor(Node other) { | |
neighbors.add(other); | |
other.neighbors.add(this); | |
} | |
/* Communicate a message to this cell */ | |
public void message_in() { | |
if(lastFrameActivated + | |
RETRANSMISSION_DELAY + | |
ACTIVE_TRANSMISSION_PERIOD + | |
REFRACTORY_PERIOD | |
< frameCount) { | |
lastFrameActivated = frameCount; | |
} | |
} | |
/* Communicate a message from this cell to its neighbors */ | |
public void message_out() { | |
for(Node neighbor : neighbors) { | |
if(parent.equals(neighbor.parent)) { | |
if(n*random(1) < TRANSMISSION_PROB_INSIDER) { | |
neighbor.message_in(); | |
stroke(0,255,0); | |
line(x, y, neighbor.x, neighbor.y); | |
noStroke(); | |
} | |
} | |
else if(n*random(1) < TRANSMISSION_PROB_OUTSIDER) { | |
neighbor.message_in(); | |
stroke(255,0,0); | |
line(x, y, neighbor.x, neighbor.y); | |
noStroke(); | |
} | |
} | |
} | |
/* Return true if this cell is actively communicating */ | |
public boolean active() { | |
int state = frameCount - lastFrameActivated; | |
return state < (ACTIVE_TRANSMISSION_PERIOD + RETRANSMISSION_DELAY) | |
&& state > RETRANSMISSION_DELAY; | |
} | |
public void draw() { | |
ellipse(x, y, n*SCALE, n*SCALE); | |
} | |
} | |
/* Debugging method that only shows the graph with no activity | |
* Set DEBUG_DRAW_CONNECTIONS to true to activate | |
*/ | |
void debug_drawConnections() { | |
background(255); | |
noStroke(); | |
for (Node center : centers.keySet()) { | |
fill(center.c, 200); | |
for (Node c : centers.get(center)) { | |
c.draw(); | |
} | |
} | |
stroke(0); | |
for (Node center : centers.keySet()) { | |
for (Node c1 : centers.get(center)) { | |
for (Node c2 : c1.neighbors) { | |
line(c1.x, c1.y, c2.x, c2.y); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment