class Attractor {
float mass; // Mass, tied to size
float G; // Gravitational Constant
private PVector loc; // Location
private PVector vel;
private PVector acc;
boolean dragging = false; // Is the object being dragged?
boolean rollover = false; // Is the mouse over the ellipse?
PVector drag; // holds the offset for when object is clicked on
private float bounce = 1.0f;
private int c, id;
//boolean left = false;
//boolean move = false;
Attractor(PVector l_,float m_, float g_, PVector a_, PVector v_, int c_) {
loc = l_.get();
acc = a_.get();
vel = v_.get();
mass = m_;
G = g_;
drag = new PVector(0.0,0.0);
c = c_; //fill color
}
Attractor(PVector l_,float m_, PVector a_, PVector v_, int c_, int id_) {
loc = l_.get();
acc = a_.get();
vel = v_.get();
mass = m_;
//G = g_;
drag = new PVector(0.0,0.0);
c = c_; //fill color
id = id_;
}
void go() {
update();
borders();
render();
drag();
}
void update() {
vel.add(acc);
vel.limit(20);
loc.add(vel);
acc.mult(0);
//change G based on loc.x
if (id == 1) {
G = map(loc.x, 640, 1280, 30.0, 1.0);
}
else if (id == 2) {
G = map(loc.x, 0, 640, 30.0, 1.0);
}
}
//external access to gravity intensity
void setGravity(float gIn) {
if (gIn > 0.0 & gIn < 10) {
G = gIn;
}
}
void borders() {
if (loc.x >= 1280) {
vel.x *= -bounce;
}
if (loc.x <= 0) {
vel.x *= -bounce;
}
}
PVector calcGravForce(Thing t) {
PVector dir = PVector.sub(loc, t.getLoc()); // Calculate direction of force
float d = dir.mag(); // Distance between objects
d = constrain(d,5.0,25.0); // Limiting the distance to eliminate "extreme" results for very close or very far objects
dir.normalize(); // Normalize vector (distance doesn't matter here, we just want this vector for direction)
float force = (G * mass * t.getMass()) / (d * d); // Calculate gravitional force magnitude
dir.mult(force); // Get force vector --> magnitude * direction
return dir;
}
PVector calcGravForce(Thing2 t) {
PVector dir = PVector.sub(loc, t.getLoc()); // Calculate direction of force
float d = dir.mag(); // Distance between objects
d = constrain(d,5.0,25.0); // Limiting the distance to eliminate "extreme" results for very close or very far objects
dir.normalize(); // Normalize vector (distance doesn't matter here, we just want this vector for direction)
float force = (G * mass * t.getMass()) / (d * d); // Calculate gravitional force magnitude
dir.mult(force); // Get force vector --> magnitude * direction
return dir;
}
// Method to display
void render() {
ellipseMode(CENTER);
noStroke();
if (dragging) fill (200,50,10);
else if (rollover) fill(255);
else fill(c,200);
ellipse(loc.x,loc.y,mass*2,mass*2);
}
// The methods below are for mouse interaction
void clicked(int mx, int my) {
float d = dist(mx,my,loc.x,loc.y);
if (d < mass) {
dragging = true;
drag.x = loc.x-mx;
drag.y = loc.y-my;
}
}
void rollover(int mx, int my) {
float d = dist(mx,my,loc.x,loc.y);
if (d < mass) {
rollover = true;
}
else {
rollover = false;
}
}
void stopDragging() {
dragging = false;
}
void drag() {
if (dragging) {
loc.x = mouseX + drag.x;
loc.y = mouseY + drag.y;
}
}
}
//Xuan Li
//Audio Manipulator
//Player toggles the 'r' key
//Realtime audio panning and pitch is controlled by
//manipulating the three gray attractors and one red
//repulsor.
//Force equation: F = (G*m1*m2)/(d*d)
//Thank to Shiffman for PVector tutorials.
import krister.Ess.*;
import processing.opengl.*;
import javax.media.opengl.*;
int MAX = 65;
Thing[] t = new Thing[MAX];
Thing2[] t2 = new Thing2[MAX];
boolean showVectors = true;
boolean flag_thingAttract = true;
//Force pvectors
PVector f, f2, f3;
PVector f_t2, f2_t2, f3_t2;
PVector f_ta, f2_ta;
Attractor a2, a3;
Repulsor r;
boolean defaultMode = false;
int time;
color redC;
float totalX = 0;
float totalY = 0;
float totalC = 0;
//for debugging purposes
float[] totalYae = new float[MAX];
/*************************************************/
//Based on Ess realtime audio generation example
AudioStream myStream;
SineWave myWave1;
TriangleWave myWave2;
FadeOut myFadeOut;
FadeIn myFadeIn;
Reverb myReverb;
int oldFrequency=0;
boolean doFadeIn=false;
boolean recording=false;
AudioFile myFile=new AudioFile();
int bytesWritten;
int freqMod = 0;
/*************************************************/
void setup() {
size(1280,720,P3D);
frameRate(24);
redC = color(160, 27, 0, 155);
reset();
/***********************************************/
// start up Ess
Ess.start(this);
// create a new AudioStream
myStream=new AudioStream();
myStream.smoothPan=true;
// our waves
myWave1=new SineWave(0,.33);
myWave2=new TriangleWave(0,.66);
// our effects
myFadeOut=new FadeOut();
myFadeIn=new FadeIn();
myReverb=new Reverb();
// start
myStream.start();
/***********************************************/
}
void draw() {
//background(20);
noStroke();
fill(0,120); //original 120, 50
rect(0,0,width,height);
r.rollover(mouseX,mouseY);
r.go();
a2.rollover(mouseX,mouseY);
a2.go();
a3.rollover(mouseX,mouseY);
a3.go();
repulsionThings();
attractRepulseForces();
thingAttract();
thing2AttractRepulse();
for (int i = 0; i < t2.length; i++) {
t2[i].go();
}
//saveFrame("line-####.jpg");
returnFreqMod();
returnLocX();
drawEssStuff();
}
//returns average of the location of
//all Thing2 objects
void returnLocX() {
for (int i = 0; i < t2.length; i++) {
totalX += t2[i].getLocX();
//totalY += t2[i].getLocY();
}
//return ((totalX + totalY)/t2.length);
totalX = totalX/t2.length;
}
void returnFreqMod() {
for (int i = 0; i < t2.length; i++) {
//totalYae[i] = t2[i].getLocY();
totalY += t2[i].getLocY();
}
totalY = totalY/t2.length;
//transform frequency
//totalY = totalYae[0] + totalYae[1] + totalYae[2] + totalYae[3];
}
int mx, my;
void drawEssStuff() {
// original adjust the pan
//int mx=mouseX;
//int my=height-mouseY;
//original
//int my = height - mouseY;
//for method comparison
//totalC = totalYae[0] + totalYae[1] + totalYae[2] + totalYae[3] + totalYae[4];
//totalC = totalC/t2.length;
freqMod = int(totalY);
//println(totalC + " " + totalY);
totalY = 0;
my = freqMod;
mx = constrain(int(totalX), 0, 1280);
myStream.pan((mx-width/2f)/(width/2f));
totalX = 0;
// draw the curve
stroke(255, 150);
// draw waveform
int interp=(int)(((millis()-myStream.bufferStartTime)/(float)myStream.duration)*myStream.size);
for (int i=0;i<width;i++) {
float left=my;
float right=my;
if (i+interp+1<myStream.buffer2.length) {
left-=myStream.buffer2[i+interp]*75.0;
right-=myStream.buffer2[i+1+interp]*75.0;
}
line(i,left,i+1,right);
}
}
public void stop() {
Ess.stop();
super.stop();
}
void audioStreamWrite(AudioStream theStream) {
// next wave
//original
//int frequency=mouseX+200;
int frequency=freqMod+200;
//totalY = 0;
myWave1.generate(myStream);
myWave2.generate(myStream,Ess.ADD);
// adjust our phases
myWave1.phase+=myStream.size;
myWave1.phase%=myStream.sampleRate;
myWave2.phase=myWave1.phase;
if (doFadeIn) {
myFadeIn.filter(myStream);
doFadeIn=false;
}
if (frequency!=oldFrequency) {
// we have a new frequency
myWave1.frequency=frequency;
// non integer frequencies can cause timing issues with our simple timing code
myWave2.frequency=(int)(frequency*4.33);
myWave1.phase=myWave2.phase=0;
// out with the old
// fade out the old sound to create a
myFadeOut.filter(myStream);
doFadeIn=true;
//println("Playing frequency: "+frequency);
oldFrequency=frequency;
}
// reverb
myReverb.filter(myStream,.5);
// record
if (recording) {
myFile.write(myStream);
bytesWritten+=myStream.size*2;
}
}
void repulsionThings() {
//repulsion among "things"
for (int i = 0; i < t.length; i++) { // For every Thing t[i]
for (int j = 0; j < t.length; j++) { // For every Thing t[j]
if (i != j) { // Make sure we are not calculating gravtional pull on oneself
PVector f = t[i].calcGravForce(t[j]); // Use the function we wrote above
t[i].applyForce(f); // Add the force to the object to affect its acceleration
}
}
t[i].go(); // Implement the rest of the object's functionality
}
}
void attractRepulseForces() {
//apply to (Thing)
for (int i = 0; i < t.length; i++) {
f = r.calcGravForce(t[i]);
f2 = a2.calcGravForce(t[i]);
f3 = a3.calcGravForce(t[i]);
// Apply that force to the thing
t[i].applyForce(f);
t[i].applyForce(f2);
t[i].applyForce(f3);
// Update and render
t[i].go();
}
}
void thing2AttractRepulse() {
//apply attractor and repulsor forces to (Thing2)
for (int i = 0; i < t2.length; i++) {
f_t2 = r.calcGravForce(t2[i]);
f2_t2 = a2.calcGravForce(t2[i]);
f3_t2 = a3.calcGravForce(t2[i]);
if (defaultMode) {
t2[i].applyForce(f_t2);
t2[i].applyForce(f2_t2);
t2[i].applyForce(f3_t2);
}
else {
t2[i].applyForce(f);
t2[i].applyForce(f2);
t2[i].applyForce(f3);
}
//t2[i].go();
}
}
void thingAttract() {
//allow turning on off attraction among things
if (flag_thingAttract) {
//attraction among "thing2" and (thing2 to thing)
for (int i = 0; i < t2.length; i++) { // For every Thing t2[i]
for (int j = 0; j < t2.length; j++) { // For every Thing t2[j]
if (i != j) { // Make sure we are not calculating gravtional pull on oneself
f_ta = t2[i].calcGravForce(t[j]); //thing2 to thing
f2_ta = t2[i].calcGravForce2(t2[j]); // thing2 to thing2
f2_ta = PVector.div(f2_ta, 5);
t2[i].applyForce(f_ta);
t2[i].applyForce(f2_ta);
}
}
//t2[i].go();
}
}
}
void reset() {
// Create an repulsor and attractive bodies
r = new Repulsor(new PVector(width/2,height/2),20, 5.0, new PVector(0.0,0.0), new PVector(0.0,0.0), redC);
a2 = new Attractor(new PVector(width/2+500,height/2),20, new PVector(-3.9,0.0), new PVector(0.0,0.0), 125, 1);
a3 = new Attractor(new PVector(width/2-500,height/2),20, new PVector(3.9,0.0), new PVector(0.0,0.0), 125, 2);
// Some random bodies
for (int i = 0; i < t.length; i++) {
PVector ac = new PVector(0.0,0.0);
PVector ve = new PVector(0.0,0.0);
PVector lo = new PVector(random(width),random(height));
t[i] = new Thing(ac,ve,lo,random(4, 100));
}
for (int i = 0; i < t2.length; i++) {
PVector ac = new PVector(0.0,0.0);
PVector ve = new PVector(0.0,0.0);
PVector lo = new PVector(random(width),random(height));
t2[i] = new Thing2(ac,ve,lo,random(4, 80));
}
}
void mousePressed() {
a2.clicked(mouseX,mouseY);
a3.clicked(mouseX,mouseY);
r.clicked(mouseX,mouseY);
//reset();
}
void mouseReleased() {
a2.stopDragging();
a3.stopDragging();
r.stopDragging();
}
void keyReleased() {
/*
if (key == 'v') {
showVectors = !showVectors;
}
*/
if (key == 'r') {
time = second();
defaultMode = false;
//println(defaultMode);
if (time % 2 == 0) {
defaultMode = true;
//println(time + " " + defaultMode);
}
flag_thingAttract = !flag_thingAttract;
}
}
// Renders a vector object 'v' as an arrow and a location 'loc'
void drawVector(PVector v, PVector loc, float scayl) {
if (v.mag() > 0.0) {
pushMatrix();
float arrowsize = 0;
// Translate to location to render vector
translate(loc.x,loc.y);
stroke(180,234,255,10.0);
// Call vector heading function to get direction (note that pointing up is a heading of 0) and rotate
rotate(v.heading2D());
// Calculate length of vector & scale it to be bigger or smaller if necessary
float len = v.mag()*scayl*3;
// Draw three lines to make an arrow (draw pointing up since we've rotate to the proper direction)
//fill(180,234,255);
line(0,0,len,0);
//line(len,0,len-arrowsize,+arrowsize/2);
//line(len,0,len-arrowsize,-arrowsize/2);
popMatrix();
}
}
void drawVector2(PVector v, PVector loc, float scayl) {
if (v.mag() > 0.0) {
pushMatrix();
float arrowsize = 0;
// Translate to location to render vector
translate(loc.x,loc.y);
stroke(230,134,105,7.0);
// Call vector heading function to get direction (note that pointing up is a heading of 0) and rotate
rotate(v.heading2D());
// Calculate length of vector & scale it to be bigger or smaller if necessary
float len = v.mag()*scayl*3;
// Draw three lines to make an arrow (draw pointing up since we've rotate to the proper direction)
//fill(180,234,255);
line(0,0,len,0);
//line(len,0,len-arrowsize,+arrowsize/2);
//line(len,0,len-arrowsize,-arrowsize/2);
popMatrix();
}
}
class Repulsor {
float mass; // Mass, tied to size
float G; // Gravitational Constant
private PVector loc; // Location
private PVector vel;
private PVector acc;
boolean dragging = false; // Is the object being dragged?
boolean rollover = false; // Is the mouse over the ellipse?
PVector drag; // holds the offset for when object is clicked on
private float bounce = 1.0f;
private int c, id;
//boolean left = false;
//boolean move = false;
Repulsor(PVector l_,float m_, float g_, PVector a_, PVector v_, int c_) {
loc = l_.get();
acc = a_.get();
vel = v_.get();
mass = m_;
G = g_;
drag = new PVector(0.0,0.0);
c = c_; //fill color
}
Repulsor(PVector l_,float m_, PVector a_, PVector v_, int c_, int id_) {
loc = l_.get();
acc = a_.get();
vel = v_.get();
mass = m_;
//G = g_;
drag = new PVector(0.0,0.0);
c = c_; //fill color
id = id_;
}
void go() {
update();
//borders();
render();
drag();
}
void update() {
vel.add(acc);
vel.limit(20);
loc.add(vel);
acc.mult(0);
//change G based on loc.x
if (id == 1) {
G = map(loc.x, 640, 1140, 65.0, 0.4);
}
else if (id == 2) {
G = map(loc.x, 140, 640, 55.0, 0.4);
}
}
//external access to gravity intensity
void setGravity(float gIn) {
if (gIn > 0.0 & gIn < 10) {
G = gIn;
}
}
void borders() {
if (loc.x >= 638.8 && loc.x <= 640) {
vel.x *= -bounce;
}
if (loc.x > 640 && loc.x <= 641.2) {
vel.x *= -bounce;
}
if ((loc.x > width-140) || (loc.x < 140)) {
vel.x *= -bounce;
}
}
PVector calcGravForce(Thing t) {
PVector dir = PVector.sub(t.getLoc(), loc); // Calculate direction of force
float d = dir.mag(); // Distance between objects
d = constrain(d,5.0,25.0); // Limiting the distance to eliminate "extreme" results for very close or very far objects
dir.normalize(); // Normalize vector (distance doesn't matter here, we just want this vector for direction)
float force = (G * mass * t.getMass()) / (d * d); // Calculate gravitional force magnitude
dir.mult(force); // Get force vector --> magnitude * direction
return dir;
}
PVector calcGravForce(Thing2 t) {
PVector dir = PVector.sub(t.getLoc(), loc); // Calculate direction of force
float d = dir.mag(); // Distance between objects
d = constrain(d,5.0,25.0); // Limiting the distance to eliminate "extreme" results for very close or very far objects
dir.normalize(); // Normalize vector (distance doesn't matter here, we just want this vector for direction)
float force = (G * mass * t.getMass()) / (d * d); // Calculate gravitional force magnitude
dir.mult(force); // Get force vector --> magnitude * direction
return dir;
}
// Method to display
void render() {
ellipseMode(CENTER);
noStroke();
if (dragging) fill (10, 150, 200, 150);
else if (rollover) fill(255);
else fill(c,200);
ellipse(loc.x,loc.y,mass*2,mass*2);
}
// The methods below are for mouse interaction
void clicked(int mx, int my) {
float d = dist(mx,my,loc.x,loc.y);
if (d < mass) {
dragging = true;
drag.x = loc.x-mx;
drag.y = loc.y-my;
}
}
void rollover(int mx, int my) {
float d = dist(mx,my,loc.x,loc.y);
if (d < mass) {
rollover = true;
}
else {
rollover = false;
}
}
void stopDragging() {
dragging = false;
}
void drag() {
if (dragging) {
loc.x = mouseX + drag.x;
loc.y = mouseY + drag.y;
}
}
}
class Thing {
PVector loc;
PVector vel;
PVector acc;
float mass;
float max_vel;
float bounce = 1.0f; // How "elastic" is the object
float G; // Universal gravitational constant
float deltaAlpha = 0; //acc
float deltaVel = 0;
float accMag = 0;
Thing(PVector a, PVector v, PVector l, float m_) {
acc = a.get();
vel = v.get();
loc = l.get();
mass = m_;
max_vel = 15.0;
G = 0.05;
}
PVector getLoc() {
return loc;
}
PVector getVel() {
return vel;
}
float getMass() {
return mass;
}
PVector calcGravForce(Thing t) {
PVector dir = PVector.sub(loc, t.getLoc()); // Calculate direction of force
float d = dir.mag(); // Distance between objects
d = constrain(d,20.0,50.0); // Limiting the distance to eliminate "extreme" results for very close or very far objects
dir.normalize(); // Normalize vector (distance doesn't matter here, we just want this vector for direction)
float force = (G * mass * t.getMass()) / (d * d); // Calculate gravitional force magnitude
dir.mult(force); // Get force vector --> magnitude * direction
return dir;
}
void applyForce(PVector force) {
force.div(mass);
acc.add(force);
if (showVectors) {
drawVector(force,loc,5000);
}
}
// Main method to operate object
void go() {
update();
borders();
render();
}
void borders() {
if (loc.y > height || loc.y <= 0) {
vel.y *= -bounce;
//loc.y = height;
}
if ((loc.x > width) || (loc.x < 0)) {
vel.x *= -bounce;
}
}
// Method to update location
void update() {
accMag = acc.mag();
deltaAlpha = map(accMag, 0, 5, 3, 20);
vel.add(acc);
vel.limit(max_vel);
loc.add(vel);
deltaVel = map(vel.mag(), 0, 20, 50, 200);
acc.mult(0);
}
// Method to display
void render() {
ellipseMode(CENTER);
noStroke();
//fill(190,228,229,20.0);
if (defaultMode) {
fill(229,228,deltaVel,deltaAlpha);
}
else {
fill(190,228,229,deltaAlpha);
}
ellipse(loc.x,loc.y,mass*2,mass*2);
if (showVectors) {
//drawVector(vel,loc,20);
}
}
}
class Thing2 {
PVector loc;
PVector vel;
PVector acc;
float mass;
float max_vel;
float bounce = 1.0f; // How "elastic" is the object
float G; // Universal gravitational constant
Thing2(PVector a, PVector v, PVector l, float m_) {
acc = a.get();
vel = v.get();
loc = l.get();
mass = m_;
max_vel = 15.0;
G = 0.6;
}
float getLocX() {
return loc.x;
}
float getLocY() {
return loc.y;
}
PVector getLoc() {
return loc;
}
PVector getVel() {
return vel;
}
float getSpeed() {
return vel.mag();
}
float getMass() {
return mass;
}
//thing2 parameter
PVector calcGravForce2(Thing2 t) {
PVector dir = PVector.sub(t.getLoc(), loc); // Calculate direction of force
float d = dir.mag(); // Distance between objects
d = constrain(d,20.0,50.0); // Limiting the distance to eliminate "extreme" results for very close or very far objects
dir.normalize(); // Normalize vector (distance doesn't matter here, we just want this vector for direction)
float force = (G * mass * t.getMass()) / (d * d); // Calculate gravitional force magnitude
dir.mult(force); // Get force vector --> magnitude * direction
return dir;
}
//thing parameter
PVector calcGravForce(Thing t) {
PVector dir = PVector.sub(t.getLoc(), loc); // Calculate direction of force
float d = dir.mag(); // Distance between objects
d = constrain(d,20.0,50.0); // Limiting the distance to eliminate "extreme" results for very close or very far objects
dir.normalize(); // Normalize vector (distance doesn't matter here, we just want this vector for direction)
float force = (G * mass * t.getMass()) / (d * d); // Calculate gravitional force magnitude
dir.mult(force); // Get force vector --> magnitude * direction
return dir;
}
void applyForce(PVector force) {
force.div(mass);
acc.add(force);
if (showVectors) {
drawVector2(force,loc,5000);
}
}
// Main method to operate object
void go() {
update();
borders();
render();
}
void borders() {
if (loc.y > height || loc.y <= 0) {
vel.y *= -bounce;
//loc.y = height;
}
if ((loc.x > width) || (loc.x < 0)) {
vel.x *= -bounce;
}
}
// Method to update location
void update() {
vel.add(acc);
vel.limit(max_vel);
loc.add(vel);
acc.mult(0);
}
// Method to display
void render() {
ellipseMode(CENTER);
noStroke();
//fill(130,228,259,3);
fill(130,248,255,8); //6 alpha
ellipse(loc.x,loc.y,mass*2,mass*2);
if (showVectors) {
drawVector2(vel,loc,20);
}
}
}
Generating and manipulating sound using attractors and repulsors.
Pitch and panning are affected.
Play with toggling the 'r' key at different times and moving around the gray attractors and the red repulsor.
The long term idea is to create an ecosystem of "sound creatures" as a musical instrument.