import processing.serial.*;
import java.util.*;
import java.util.regex.*;
Serial myPort;
final int BAUD = 115200;
final float CAM_W = 320.0;
final float CAM_H = 240.0;
final int MAX_PARTICLES = 1200;
final int MAX_DETECTIONS = 50;
ArrayList<Detection> detections = new ArrayList<Detection>();
ArrayList<Track> tracks = new ArrayList<Track>();
Particle[] pool = new Particle[MAX_PARTICLES];
int nextTrackId = 0;
Pattern boxPattern = Pattern.compile(
"Box\\[(\\d+)\\].*x=(\\d+), y=(\\d+), w=(\\d+), h=(\\d+)"
);
void setup() {
size(1280, 720);
frameRate(60);
background(0);
colorMode(HSB, 360, 100, 100, 100);
for (int i = 0; i < pool.length; i++) {
pool[i] = new Particle();
}
println(Serial.list());
myPort = new Serial(this, Serial.list()[1], BAUD);
myPort.bufferUntil('\n');
}
void draw() {
fill(0, 0, 0, 25);
rect(0, 0, width, height);
drawHumans();
updateParticles();
drawConnections();
}
void serialEvent(Serial p) {
String line = p.readStringUntil('\n');
if (line == null) return;
line = trim(line);
if (line.length() > 200) return;
if (line.startsWith("Box")) {
Matcher m = boxPattern.matcher(line);
if (m.find()) {
if (detections.size() >= MAX_DETECTIONS) return;
Detection d = new Detection();
d.cx = float(m.group(2)) + float(m.group(4)) / 2.0;
d.cy = float(m.group(3)) + float(m.group(5)) / 2.0;
d.w = float(m.group(4));
d.h = float(m.group(5));
d.sx = map(d.cx, 0, CAM_W, 0, width);
d.sy = map(d.cy, 0, CAM_H, 0, height);
d.sw = map(d.w, 0, CAM_W, 0, width);
d.sh = map(d.h, 0, CAM_H, 0, height);
detections.add(d);
}
}
if (line.contains("invoke success")) {
while (tracks.size() < detections.size()) {
Track t = new Track();
t.id = nextTrackId++;
tracks.add(t);
}
for (int i = 0; i < detections.size(); i++) {
tracks.get(i).update(detections.get(i));
}
for (int i = detections.size(); i < tracks.size(); i++) {
tracks.get(i).active = false;
}
detections.clear();
}
}
void drawHumans() {
blendMode(ADD);
ArrayList<Track> activeTracks = new ArrayList<Track>();
ArrayList<Integer> spawnCounts = new ArrayList<Integer>();
for (Track t : tracks) {
if (!t.active) continue;
activeTracks.add(t);
drawHumanShape(t);
int count = int(t.w * t.h / 1500.0);
count = constrain(count, 10, 40);
spawnCounts.add(count);
}
int maxSpawn = 0;
for (int c : spawnCounts) maxSpawn = max(maxSpawn, c);
for (int s = 0; s < maxSpawn; s++) {
for (int i = 0; i < activeTracks.size(); i++) {
if (s < spawnCounts.get(i)) {
activateParticle(activeTracks.get(i));
}
}
}
blendMode(BLEND);
}
void drawHumanShape(Track t) {
float cx = t.x;
float cy = t.y;
float w = t.w * 1.2;
float h = t.h * 1.5;
stroke(t.c, 80);
noFill();
ellipse(cx, cy - h * 0.3, w * 0.2, w * 0.2);
line(cx, cy - h * 0.2, cx, cy + h * 0.3);
line(cx, cy, cx - w * 0.3, cy + h * 0.1);
line(cx, cy, cx + w * 0.3, cy + h * 0.1);
line(cx, cy + h * 0.3, cx - w * 0.2, cy + h * 0.6);
line(cx, cy + h * 0.3, cx + w * 0.2, cy + h * 0.6);
}
boolean activateParticle(Track t) {
for (Particle p : pool) {
if (!p.active) {
p.init(t);
return true;
}
}
return false;
}
void updateParticles() {
blendMode(ADD);
for (Particle p : pool) {
if (p.active) {
p.update();
p.draw();
}
}
blendMode(BLEND);
}
void drawConnections() {
if (tracks.size() < 2) return;
blendMode(ADD);
for (int i = 0; i < tracks.size(); i++) {
Track a = tracks.get(i);
if (!a.active) continue;
for (int j = i + 1; j < tracks.size(); j++) {
Track b = tracks.get(j);
if (!b.active) continue;
float d = dist(a.x, a.y, b.x, b.y);
if (d < 300) {
stroke(lerpColor(a.c, b.c, 0.5), 60);
beginShape();
for (int k = 0; k < 20; k++) {
float tt = k / 20.0;
float x = lerp(a.x, b.x, tt);
float y = lerp(a.y, b.y, tt);
float offset = (noise(tt * 5, frameCount * 0.02) - 0.5) * 40;
y += offset;
vertex(x, y);
}
endShape();
}
}
}
blendMode(BLEND);
}
class Detection {
float cx, cy, w, h;
float sx, sy, sw, sh;
}
class Track {
int id = -1;
float x, y, w, h;
color c;
boolean active = true;
void update(Detection d) {
x = d.sx;
y = d.sy;
w = d.sw;
h = d.sh;
active = true;
float seed = id * 37.0 + d.cx * 0.13 + d.cy * 0.17;
float hue = (noise(seed) * 360.0) % 360.0;
c = color(hue, 80, 100);
}
}
class Particle {
boolean active = false;
Track t;
float x, y;
float vx, vy;
float life;
color c;
void init(Track t_) {
t = t_;
active = true;
c = t.c;
float cx = t.x;
float cy = t.y;
float w = t.w * 1.2;
float h = t.h * 1.5;
int type = int(random(5));
if (type == 0) {
float angle = random(TWO_PI);
float r = w * 0.1;
x = cx + cos(angle) * r;
y = cy - h * 0.3 + sin(angle) * r;
vx = cos(angle) * random(1, 3);
vy = sin(angle) * random(1, 3);
} else if (type == 1) {
float tLine = random(1);
x = cx;
y = lerp(cy - h * 0.2, cy + h * 0.3, tLine);
vx = random(-1.5, 1.5);
vy = random(0.5, 2);
} else if (type == 2) {
float tLine = random(1);
x = lerp(cx, cx - w * 0.3, tLine);
y = lerp(cy, cy + h * 0.1, tLine);
vx = random(-2, -0.5);
vy = random(-0.5, 1.5);
} else if (type == 3) {
float tLine = random(1);
x = lerp(cx, cx + w * 0.3, tLine);
y = lerp(cy, cy + h * 0.1, tLine);
vx = random(0.5, 2);
vy = random(-0.5, 1.5);
} else {
float side = random(1) < 0.5 ? -1 : 1;
float tLine = random(1);
x = lerp(cx, cx + side * w * 0.2, tLine);
y = lerp(cy + h * 0.3, cy + h * 0.6, tLine);
vx = side * random(0.5, 2);
vy = random(1, 3);
}
life = random(60, 120);
}
void update() {
if (t == null || !t.active) {
active = false;
return;
}
float dx = t.x - x;
float dy = t.y - y;
vx += dx * 0.0005;
vy += dy * 0.0005;
x += vx;
y += vy;
life--;
if (life <= 0) active = false;
}
void draw() {
fill(c, 85);
noStroke();
ellipse(x, y, 3, 3);
}
}