Skip to content

Instantly share code, notes, and snippets.

@tprynn
Created February 14, 2014 00:16
Show Gist options
  • Save tprynn/8986754 to your computer and use it in GitHub Desktop.
Save tprynn/8986754 to your computer and use it in GitHub Desktop.
/* 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