噂が広がる概念図を作ってみました。
球状に。もう少し直したいところもありますが、途中段階の記録として。
Visual
時間とともにノードが増えていく。





Code
// SNS / Rumor Spread Model
// 3D Spherical, Black & White
// Multiple sources over time
// Processing 4.x
int NODE_NUM = 180;
ArrayList<Node> nodes = new ArrayList<Node>();
ArrayList<Edge> edges = new ArrayList<Edge>();
// 拡散パラメータ
float spreadDelay = 2.2; // 拡散までの待ち時間(秒)
float spreadProb = 0.55; // 拡散確率
// 新しい発信源
float newSourceInterval = 12.0; // 秒
float lastSourceTime = 0;
// 球体・回転
float sphereRadius = 300;
float rotation = 0;
void setup() {
size(900, 900, P3D);
smooth(8);
// ノード生成(球の内部)
for (int i = 0; i < NODE_NUM; i++) {
PVector p = randomPointInSphere(sphereRadius);
nodes.add(new Node(p));
}
// ネットワーク生成
for (int i = 0; i < NODE_NUM; i++) {
int connections = int(random(3, 7));
for (int j = 0; j < connections; j++) {
int t = int(random(NODE_NUM));
if (t != i) {
edges.add(new Edge(nodes.get(i), nodes.get(t)));
}
}
}
// 最初の発信源
nodes.get(int(random(NODE_NUM))).activate();
lastSourceTime = millis() / 1000.0;
}
void draw() {
background(0);
lights();
translate(width/2, height/2);
rotateY(rotation);
rotateX(rotation * 0.6);
rotation += 0.0015;
// 一定時間ごとに新しい発信源
float now = millis() / 1000.0;
if (now - lastSourceTime > newSourceInterval) {
spawnNewSource();
lastSourceTime = now;
}
// 描画
for (Edge e : edges) e.display();
for (Node n : nodes) {
n.update();
n.display();
}
}
// ------------------------
// 球内部ランダム配置
PVector randomPointInSphere(float r) {
PVector p;
do {
p = new PVector(
random(-r, r),
random(-r, r),
random(-r, r)
);
} while (p.mag() > r);
return p;
}
// ------------------------
// 新しい発信源を生成
void spawnNewSource() {
ArrayList<Node> candidates = new ArrayList<Node>();
for (Node n : nodes) {
if (!n.active) candidates.add(n);
}
if (candidates.size() > 0) {
Node n = candidates.get(int(random(candidates.size())));
n.activate();
}
}
// ------------------------
// Node
class Node {
PVector pos;
boolean active = false;
float activatedTime = -1;
Node(PVector p) {
pos = p;
}
void activate() {
active = true;
activatedTime = millis() / 1000.0;
}
void update() {
if (!active) return;
float now = millis() / 1000.0;
if (now - activatedTime > spreadDelay) {
for (Edge e : edges) {
Node other = null;
if (e.a == this) other = e.b;
if (e.b == this) other = e.a;
if (other != null && !other.active) {
if (random(1) < spreadProb) {
other.activate();
}
}
}
// 二重拡散防止
activatedTime = now + 999;
}
}
void display() {
if (!active) return;
float now = millis() / 1000.0;
float age = now - activatedTime;
// 時間とともに明るくなる
float brightness = constrain(map(age, 0, 8, 120, 240), 120, 240);
float depth = map(pos.z, -sphereRadius, sphereRadius, 0.5, 1.2);
pushMatrix();
translate(pos.x, pos.y, pos.z);
noStroke();
fill(brightness * depth);
sphereDetail(6);
sphere(4 * depth);
popMatrix();
}
}
// ------------------------
// Edge
class Edge {
Node a, b;
Edge(Node a, Node b) {
this.a = a;
this.b = b;
}
void display() {
if (!(a.active || b.active)) return;
float zAvg = (a.pos.z + b.pos.z) * 0.5;
float col = map(zAvg, -sphereRadius, sphereRadius, 80, 200);
stroke(col);
strokeWeight(1.2);
line(
a.pos.x, a.pos.y, a.pos.z,
b.pos.x, b.pos.y, b.pos.z
);
}
}
オススメのProcessing参考書
Processing クリエイティブ・コーディング入門
―コードが生み出す創造表現



コメント