//import processing.opengl.*; //USER INTERFACE boolean dragging; boolean showSliders; // Charge Position Handles Point chargeAHandle; Point chargeBHandle; // Charge Value Sliders CircleSlider chargeASlider; CircleSlider chargeBSlider; float SCALE; // Field Lines int numFieldLines; //How many field lines around each point? int SRADIUS; //At what distance? float sX, sY; //Computed start coordinates //CHARGES float[] chargeA = new float[3]; //Charge: {x, y, charge} float[] chargeB = new float[3]; //FIELD LINE VARS float[] vector = new float[2]; float[] testParticle = new float[2]; float[] lastDrawn = new float[2]; int STEP; //CORE void setup(){ size(500,200,P3D); defaultKnobs(); //HOUSEKEEPING background(255); ellipseMode(CENTER_RADIUS); } void draw(){ background(0); //FIELD LINES: Draw one at a time around each point at distance SRADIUS for(int i = 0; i < numFieldLines; i++){ sX = chargeA[0] + SRADIUS * cos(i * TWO_PI/numFieldLines); sY = chargeA[1] + SRADIUS * sin(i * TWO_PI/numFieldLines); stroke(255); drawFieldLine(sX, sY); sX = chargeB[0] + SRADIUS * cos(i * TWO_PI/numFieldLines); sY = chargeB[1] + SRADIUS * sin(i * TWO_PI/numFieldLines); stroke(255); drawFieldLine(sX, sY); } //USER INTERFACE noFill(); stroke(200); //ellipse(chargeAHandle.x, chargeAHandle.y, SRADIUS, SRADIUS); //ellipse(chargeBHandle.x, chargeBHandle.y, SRADIUS, SRADIUS); //Charge Handles chargeAHandle.update(); chargeBHandle.update(); //Charge Sliders if(showSliders){ chargeASlider.update(); chargeBSlider.update(); } chargeASlider.x = chargeAHandle.x; chargeASlider.y = chargeAHandle.y; chargeBSlider.x = chargeBHandle.x; chargeBSlider.y = chargeBHandle.y; // Update charges chargeA[0] = chargeAHandle.x; //X chargeA[1] = chargeAHandle.y; //Y chargeA[2] = chargeASlider.value * SCALE; //println(chargeB[2]); chargeB[0] = chargeBHandle.x; //X chargeB[1] = chargeBHandle.y; //Y chargeB[2] = chargeBSlider.value * SCALE; if(keyPressed){ if(key == 's'){ showSliders = true; } if(key == 'r'){ defaultKnobs(); } }else{ showSliders = false; } } //FUNCTIONS //defaultKnobs(): Sets all knobs to default. void defaultKnobs(){ //KNOBS // USER INTERFACE // Charge Position Handles chargeAHandle = new Point( width/5, height*2/5, color(10,50,255) ); chargeBHandle = new Point( width*2/5, height*3/5, color(10,50,255) ); // Charge Value Sliders chargeASlider = new CircleSlider(chargeAHandle.x,chargeAHandle.y,color(255,0,0)); chargeBSlider = new CircleSlider(chargeBHandle.x,chargeBHandle.y,color(255,0,0)); SCALE = 1000 / 20; // Field Lines Display numFieldLines = 8; SRADIUS = 15; STEP = 3; // CHARGES chargeA[0] = chargeAHandle.x; //X chargeA[1] = chargeAHandle.y; //Y chargeA[2] = 1000; //Charge chargeB[0] = chargeBHandle.x; //X chargeB[1] = chargeBHandle.y; //Y chargeB[2] = 1000; //Charge //END KNOBS// } //drawFieldLine(): Simultaneously computes and draws the path of a positive test particle starting at (startX, startY) until it leaves // the screen void drawFieldLine(float startX, float startY){ testParticle[0] = startX; testParticle[1] = startY; lastDrawn[0] = testParticle[0]; lastDrawn[1] = testParticle[1]; int i = 0; do{ vector = field(testParticle[0], testParticle[1], new float[][] { chargeA, chargeB }, 2); testParticle[0] += vector[0]*cos(vector[1]); testParticle[1] += vector[0]*sin(vector[1]); if( dist(testParticle[0], testParticle[1], lastDrawn[0], lastDrawn[1]) > STEP && dist(testParticle[0], testParticle[1], chargeA[0], chargeA[1]) > SRADIUS && dist(testParticle[0], testParticle[1], chargeB[0], chargeB[1]) > SRADIUS ){ //line(testParticle[0], testParticle[1], lastDrawn[0], lastDrawn[1]); //noStroke(); //ellipse(testParticle[0], testParticle[1], 1, 1); point(testParticle[0], testParticle[1]); lastDrawn[0] = testParticle[0]; lastDrawn[1] = testParticle[1]; } } while(testParticle[0] < width+STEP && testParticle[0] > 0-STEP && testParticle[1] < height+STEP && testParticle[1] > 0-STEP && vector[0] > .01 && dist(testParticle[0], testParticle[1], chargeA[0], chargeA[1]) > SRADIUS && dist(testParticle[0], testParticle[1], chargeB[0], chargeB[1]) > SRADIUS ); } //field(): Given a set of charges, returns the force vector at point (x,y). float[] field(float x, float y, float[][] charge, int numCharges){ float[][] vector = new float[numCharges][2]; //The force vectors each charge produces float[] output = new float[] { 0, 0 }; //force, direction for(int i = 0; i < numCharges; i++){ vector[i][0] = charge[i][2] / sq( dist(charge[i][0],charge[i][1], x,y) ); vector[i][1] = atan2(y-charge[i][1], x-charge[i][0]); } for(int i = 0; i < numCharges; i++){ output = vectorSum(output, vector[i]); //Add up all the force vectors... } return output; //...and return the net force. } //vectorSum: Given two vectors of the form (r, theta), returns their sum in the same form vector. float[] vectorSum(float[] v1, float[] v2){ float x,y; x = v1[0] * cos(v1[1]) + v2[0] * cos(v2[1]); y = v1[0] * sin(v1[1]) + v2[0] * sin(v2[1]); return new float[] { sqrt( sq(x) + sq(y) ), atan2(y, x) }; } //CLASSES class Point { float x,y; float nx, ny; float sx, sy; int R = 5; int GLIDE=3; boolean drag; boolean once; color Color; Point(int x, int y, color Color) { this.x=x; this.y=y; this.Color = Color; } void update() { if(!drag) { // IF NOT DRAGGING: if(mousePressed && dist(mouseX,mouseY,x,y)