import processing.serial.*;

Serial myPort;

// touch
float t1 = 0;
float t2 = 0;

float s1 = 0;
float s2 = 0;

// particles
int NUM = 600;
PVector[] pos = new PVector[NUM];
PVector[] vel = new PVector[NUM];

void setup() {
  size(800, 600);
  pixelDensity(1);
  background(10);

  myPort = new Serial(this, "/dev/cu.usbmodem1201", 115200);
  myPort.bufferUntil('\n');

  for (int i = 0; i < NUM; i++) {
    pos[i] = new PVector(random(width), random(height));
    vel[i] = new PVector(0, 0);
  }
}

void draw() {
  // softer fade = longer trails (brighter feel)
  noStroke();
  fill(0, 25);
  rect(0, 0, width, height);

  s1 = lerp(s1, t1, 0.1);
  s2 = lerp(s2, t2, 0.1);

  float touch1 = 1.0 - norm(s1);
  float touch2 = 1.0 - norm(s2);

  // BIGGER forces = more visible motion
  float fx = (touch1 - touch2) * 6.0;
  float fy = (touch2 - touch1) * 6.0;

  for (int i = 0; i < NUM; i++) {

    // swirling field + touch force
    float angle = noise(pos[i].x * 0.003, pos[i].y * 0.003) * TWO_PI * 3;

    vel[i].x += cos(angle) * 0.6 + fx;
    vel[i].y += sin(angle) * 0.6 + fy;

    // damping (keeps it fluid, not exploding)
    vel[i].mult(0.92);

    pos[i].add(vel[i]);

    // wrap edges
    if (pos[i].x < 0) pos[i].x = width;
    if (pos[i].x > width) pos[i].x = 0;
    if (pos[i].y < 0) pos[i].y = height;
    if (pos[i].y > height) pos[i].y = 0;

    // 🔥 BRIGHT COLOR MAPPING
    float brightness = (touch1 + touch2) * 255;

    float r = 80 + touch1 * 200;
    float g = 100 + brightness;
    float b = 200 + touch2 * 200;

    stroke(r, g, b, 180);
    strokeWeight(2); // makes it MUCH more visible

    point(pos[i].x, pos[i].y);
  }
}

// normalize touch range
float norm(float v) {
  return constrain((v - 18000) / (65000 - 18000), 0, 1);
}

// serial
void serialEvent(Serial p) {
  String data = trim(p.readStringUntil('\n'));
  if (data == null) return;

  String[] v = split(data, ',');
  if (v.length != 2) return;

  try {
    t1 = float(v[0]);
    t2 = float(v[1]);
  } catch (Exception e) {}
}
