/* * The Cyclotron Lives! Copyright (c) 2007 Dan Trueman. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 U.S.A. ----------------------------------------------------------------------------- * * notes: holding 'a' down will keep the angle constant while dragging * holding 's' down will keep the length constant while dragging * holding 'd' down will allow dragging to adjust the cap size, while keeping everything else constant * pressing 'q' will quantize both times and lengths * shift-clicking will add a new spoke * pressing 'shift-k' will delete current spoke * [ => stop/start this * { => stop/start this with reset * ] => stop/start all * } => stop/start all with reset * - => reset this * _ => reset all * * to add: * -button to rotate cycler by one tic (clockwise) * -multiply all for spoke length, so all spokes can be multiplied by a certain amount * * dang, shoulda made a Spoke class.... stoooopid. i had no idea how big this thing would become.... * * this version (cyc_sa) is meant to run independently, for web demo purposes, so a bunch of * unneeded functionality, mostly having to do with ChucK/OSC communications, has been commented out * -----------------------------------------------------------------------------*/ int maxspokes = 100; int numspokes = 4; float bx[] = new float[maxspokes]; float by[] = new float[maxspokes]; float btheta[] = new float[maxspokes]; float blen[] = new float[maxspokes]; float bs[] = new float[maxspokes]; //init to 5 float binc[] = new float[maxspokes]; float bwarp[] = new float[maxspokes]; boolean bover[] = new boolean[maxspokes]; //init to false boolean locked[] = new boolean[maxspokes]; //init to false float bdifx[] = new float[maxspokes]; //init to 0. float bdify[] = new float[maxspokes]; //init to 0. int dimx = 600; int dimy = 600; int currentSpoke = 0; //PApplet embed = new PApplet(); float qtheta[] = new float[maxspokes*2]; //for quantizing int qspokes = 9; //number of spokes to quantize to; qspokes >= numspokes float qx[] = new float[maxspokes*2]; float qy[] = new float[maxspokes*2]; int numCircles = 10; float numCirclesInv = 1./numCircles; float qlen[] = new float[50]; float spokelenNorm; float spokelenNormInv; float capsizeMin = 8; float capsizeMax = 60; float tempo = 2.; float diameterSave = 0.9; float warp = 1.; boolean Qonly = true; boolean holdTheta = false; boolean holdLen = false; boolean adjustCap = false; boolean shiftDown = false; boolean controlDown = false; boolean playing = false; int flash = 0; //OSC stuff import oscP5.*; import netP5.*; OscP5 oscP5; OscP5 oscP5All; NetAddress toChuck; NetAddress toAll; int thisInPort = 12000; int thisOutPort = 12001; int allOutPort = 1999; //int allInPort = 1998; float intime; //current time, from ChucK float xtime, ytime; float phaseWarp = 1.; float dphase = 1.; //phase for display; for offsetting if needed int framerate = 25; //File stuff File infile, outfile; import java.applet.*; import java.awt.*; import java.awt.event.*; //time stuff float phase = 0.; float ctime = 0.; int phaseCtr = 0; //sound synth stuff import krister.Ess.*; AudioChannel myChannel; SineWave myWave; int nextspoke = 0; /* static public void main(String args[]) { if(args.length == 0) PApplet.main(new String[] { "cyc1" } ); if(args.length == 1) PApplet.main(new String[] { "cyc1", args[0] } ); if(args.length == 2) PApplet.main(new String[] { "cyc1", args[0], args[1] } ); if(args.length == 3) PApplet.main(new String[] { "cyc1", args[0], args[1], args[2] } ); //PApplet.main(new String[] { "cyc1", "portnum", "filename"}); //println("arg1 inside main: " + args); } */ MenuBar menubar = new MenuBar(); Menu file = new Menu("File"); MenuItem savemenu, openmenu, saveasmenu; void setup() { //basic size(dimx, dimy + 150); frameRate(framerate); /* //println("args len = " + args.length); if(args.length >= 1) { thisInPort = int(args[0]); thisOutPort = thisInPort + 1; //allOutPort = thisInPort + 2; } //menubar stuff frame.setMenuBar(menubar); menubar.add(file); openmenu = new MenuItem("Open", new MenuShortcut(KeyEvent.VK_O)); file.add(openmenu); openmenu.addActionListener(new OpenFileListener()); savemenu = new MenuItem("Save", new MenuShortcut(KeyEvent.VK_S)); file.add(savemenu); savemenu.addActionListener(new SaveFileListener()); saveasmenu = new MenuItem("Save As"); file.add(saveasmenu); saveasmenu.addActionListener(new SaveAsFileListener()); */ //OSC stuff /* oscP5 = new OscP5(this, thisInPort); OscProperties allOut_properties = new OscProperties(); allOut_properties.setRemoteAddress("239.0.0.1", allOutPort); allOut_properties.setNetworkProtocol(OscProperties.MULTICAST); oscP5All = new OscP5(this, allOut_properties); //oscP5All = new OscP5(this, "239.0.0.1", allOutPort); toChuck = new NetAddress("localhost", thisOutPort); //toAll = new NetAddress("localhost", allOutPort); */ //var init spokelenNorm = dimx*0.5*diameterSave; spokelenNormInv = 1./spokelenNorm; for(int i=0;i= 2) { //println("open file " + args[1]); openCycler(args[1]); } if(args.length >= 3) { //println("framerate " + int(args[2])); framerate = int(args[2]); frameRate(framerate); framerate_text.setValue(str(framerate)); } */ //sound stuff Ess.start(this); // create a new AudioChannel myChannel=new AudioChannel(); myChannel.initChannel(myChannel.frames(2)); // generate 8 ms of a soft sine wave myWave=new SineWave(440,.1); myWave.generate(myChannel,0,myChannel.frames(2)); } //unlike original Cyclotron, have T=0 be vertical void placeSpoke(int which, float angle, float len) { //angle = angle*warp; angle = pow(angle, warp); float xt = dphase*len*sin(TWO_PI*angle); float yt = dphase*len*cos(TWO_PI*angle); bx[which] = xt + dimx*0.5; by[which] = dimy*0.5 - yt; btheta[which] = angle; blen[which] = len; sendSpoke(angle, len*spokelenNormInv, bs[which], binc[which], bwarp[which], which); //redraw(); //println("spoke num " + which + " " + bx[which] + " " + by[which] + " " + TWO_PI*angle); } //unlike original Cyclotron, have T=0 be vertical void placeSpokeLocal(int which, float angle, float len) { //angle = angle*warp; angle = pow(angle, warp); float xt = dphase*len*sin(TWO_PI*angle); float yt = dphase*len*cos(TWO_PI*angle); bx[which] = xt + dimx*0.5; by[which] = dimy*0.5 - yt; btheta[which] = angle; blen[which] = len; //sendSpoke(angle, len*spokelenNormInv, bs[which], binc[which], bwarp[which], which); //redraw(); //println("spoke num " + which + " " + btheta[which]); } //sets location for racing time ball (around circumferance) void placeTime() { //if (phaseWarp != 1.) (Math.pow(phaseWarp, phase) - 1.) / (phaseWarp - 1.) => phase; float warpFactor; //if (phaseWarp != 1.) warpFactor = (pow(phaseWarp, intime) - 1.) / (phaseWarp - 1.); //else warpFactor = intime; warpFactor = intime; float xt = dphase*spokelenNorm*sin(TWO_PI*warpFactor) ; float yt = dphase*spokelenNorm*cos(TWO_PI*warpFactor); //float xt = spokelenNorm*sin(TWO_PI*(pow(intime, phaseWarp))) ; //float yt = spokelenNorm*cos(TWO_PI*(pow(intime, phaseWarp))); xtime = xt + dimx*0.5; ytime = dimy*0.5 - yt; //redraw(); } void placeQtick(int which, float angle) { float xt = spokelenNorm*sin(TWO_PI*angle); float yt = spokelenNorm*cos(TWO_PI*angle); qx[which] = xt + dimx*0.5; qy[which] = dimy*0.5 - yt; } void quantizeTimes() { if(Qonly) { int j=qspokes - 1; while(j>=0) { //println(j + ":" + qtheta[j]); if(btheta[currentSpoke] >= qtheta[j]) { btheta[currentSpoke] = qtheta[j]; j=0; //println("quantizing spoke " + i + " to tic " + j + " qspokes = " + qspokes); } j--; } placeSpoke(currentSpoke, btheta[currentSpoke], blen[currentSpoke]); } else { for (int i=0; i=0) { //println(j + ":" + qtheta[j]); if(btheta[i] >= qtheta[j]) { btheta[i] = qtheta[j]; j=0; //println("quantizing spoke " + i + " to tic " + j + " qspokes = " + qspokes); } j--; } placeSpoke(i, btheta[i], blen[i]); } } } void quantizeLengths() { if(Qonly) { int j=numCircles; //println("spoke " + i + " = "+ blen[i] + " "); if(blen[currentSpoke] > spokelenNorm) blen[currentSpoke] = spokelenNorm; while(j>0) { if(blen[currentSpoke] < qlen[j] && blen[currentSpoke] > qlen[j-1]) { if(qlen[j] - blen[currentSpoke] > blen[currentSpoke] - qlen[j-1]) { blen[currentSpoke] = qlen[j-1]; } else { blen[currentSpoke] = qlen[j]; } } //println(j + ":" + qlen[j]); j--; } placeSpoke(currentSpoke, btheta[currentSpoke], blen[currentSpoke]); } else { for (int i=0; i spokelenNorm) blen[i] = spokelenNorm; //println("spoke " + i + " = "+ blen[i] + " "); while(j>0) { if(blen[i] < qlen[j] && blen[i] > qlen[j-1]) { if(qlen[j] - blen[i] > blen[i] - qlen[j-1]) { blen[i] = qlen[j-1]; } else { blen[i] = qlen[j]; } } //println(j + ":" + qlen[j]); j--; } placeSpoke(i, btheta[i], blen[i]); } } } void initSpokes() { float angleInc = 1./numspokes; //for evenly spaced spokes for(int i=0;i= 1.) { nextspoke = 0; phase = ctime = ctime % 1.; //phase = 0.; } if(phase > btheta[nextspoke]) { myWave.frequency = blen[nextspoke]/spokelenNorm * 880 + 440; myWave.volume = bs[nextspoke]/capsizeMax; myWave.generate(myChannel,0,myChannel.frames(2)); myChannel.play(); nextspoke++; } intime = phase; placeTime(); //end time stuff if(flash==1) background(255, 0 , 0); //turn background red; useful for downbeating flash else background(0); smooth(); fill(0); frameRate(framerate); //main circle stroke(50); fill(0); ellipse(dimx*0.5, dimy*0.5, (spokelenNorm)*2, (spokelenNorm)*2); //length quantize circles stroke(20); for(int i=numCircles-1;i>0;i--) { fill(0); qlen[i] = (spokelenNorm)*i*numCirclesInv; ellipse(dimx*0.5, dimy*0.5, (spokelenNorm)*i*2.*numCirclesInv, (spokelenNorm)*i*2.*numCirclesInv); } qlen[numCircles] = (spokelenNorm); qlen[0] = 0.; //time quantization tics for(int i=0;i bx[i]-bs[i]*0.5 && mouseX < bx[i]+bs[i]*0.5 && mouseY > by[i]-bs[i]*0.5 && mouseY < by[i]+bs[i]*0.5) { if(!bover[i]) { //println("over spoke # " + i); } bover[i] = true; if(!locked[i]) { //stroke(255); //fill(153); stroke(0, 0, 150); fill(0, 0, 150); } } else { if (currentSpoke == i) { //stroke(255); stroke(150, 0, 0); fill(150, 0, 0); } else { stroke(153); fill(153); } bover[i] = false; } //Draw the cap //rect(bx[i], by[i], bs[i], bs[i]); ellipse(bx[i], by[i], bs[i], bs[i]); //draw the spoke line(dimx*0.5,dimy*0.5,bx[i],by[i]); } //draw the running ball, for current time stroke(0, 150, 50); ellipse(xtime, ytime, 5, 5); line(xtime, ytime, dimx*0.5,dimy*0.5); //line across the bottom stroke(255, 255, 255); fill(0, 0, 0); line(0, dimy, dimx, dimy); } void addSpoke() { println("adding spoke"); float xt = mouseX - dimx/2.; float yt = -mouseY + dimy/2.; currentSpoke = numspokes++; btheta[currentSpoke] = atan2(xt, yt)/TWO_PI; if (btheta[currentSpoke] < 0) btheta[currentSpoke] = 1. + btheta[currentSpoke]; blen[currentSpoke] = dist(xt, yt, 0, 0);//*spokelenNormInv; bs[currentSpoke] = 8; bover[currentSpoke] = locked[currentSpoke] = false; bdifx[currentSpoke] = bdify[currentSpoke] = 0.; binc[currentSpoke] = .01; bwarp[currentSpoke] = bwarp[0]; sortSpokes(); sendSpokeNum(numspokes); sendAllSpokes(); //placeSpoke(currentSpoke, btheta[currentSpoke], blen[currentSpoke]); } void deleteSpoke() { btheta[currentSpoke] = 1.; currentSpoke--; if(currentSpoke<0) currentSpoke = 0; sortSpokes(); numspokes--; sendSpokeNum(numspokes); sendAllSpokes(); } void sortSpokes() { //oh, should have made a damn Spoke struct.... //println("sorting...."); int minm; float ftemp; boolean btemp; float currentTheta = btheta[currentSpoke]; //brutal for(int i=0;i capsizeMax) bs[i] = capsizeMax; setTextValues(currentSpoke); sendSpoke(btheta[i], blen[i]*spokelenNormInv, bs[i], binc[i], bwarp[i], i); return; } //otherwise move spoke, and check to see if theta or len should be held constant float newx = mouseX-bdifx[i]; float newy = mouseY-bdify[i]; float xt = newx - dimx*0.5; float yt = -newy + dimy*0.5; if(!holdTheta) btheta[i] = atan2(xt, yt)/TWO_PI; if(!holdLen) blen[i] = dist(xt, yt, 0, 0); if (btheta[i] < 0) btheta[i] = 1. + btheta[i]; placeSpoke(i, btheta[i], blen[i]); setTextValues(i); sendSpoke(btheta[i], blen[i]*spokelenNormInv, bs[i], binc[i], bwarp[i], i); //shouldn't need to do this, since placeSpoke sendsSpokes as well if(btheta[i] > btheta[i+1]) { sortSpokes(); sendAllSpokes(); } if(i>0) { if(btheta[i] < btheta[i-1]) { sortSpokes(); sendAllSpokes(); } } } } //redraw(); } void mouseReleased() { for(int i=0;i capsizeMax) bs[currentSpoke] = capsizeMax; setTextValues(currentSpoke); } else if(e.getSource() == cdiameter_text) { /* this was for changing the diameter; not using float newdiameter = float(cdiameter_text.getValue()); if(newdiameter <= 1.) { //protect against NaNs newdiameter *= 0.9; if (newdiameter > 1.) newdiameter = 1.; if (newdiameter < 0.125) newdiameter = 0.125; spokelenNorm = dimx*newdiameter*0.5; spokelenNormInv = 1./spokelenNorm; for(int i=0;i maxspokes) numspokes = maxspokes; sendSpokeNum(numspokes); initSpokes(); sortSpokes(); sendAllSpokes(); } else if(e.getSource() == numQspokes_text) { qspokes = int(numQspokes_text.getValue()); if(qspokes > maxspokes*2) qspokes = maxspokes*2; initQspokes(); } else if(e.getSource() == numQcircles_text) { numCircles = int(numQcircles_text.getValue()); if(numCircles > 50) numCircles = 50; if(numCircles < 1) numCircles = 1; numCirclesInv = 1./numCircles; setTextValues(currentSpoke); //initQspokes(); } else if(e.getSource() == ctempo_text) { tempo = float(ctempo_text.getValue()); sendTempo(tempo); //initQspokes(); } else if(e.getSource() == framerate_text) { framerate = int(framerate_text.getValue()); //println("new framerate = " + framerate); frameRate(framerate); } } if(e.getSource() == qb) { //println("pressed Quantize!"); quantizeTimes(); setTextValues(currentSpoke); //sendFloat(0.24, "/blen"); //works! } else if(e.getSource() == qb2) { //println("pressed spoke length Quantize!"); quantizeLengths(); setTextValues(currentSpoke); } else if(e.getSource() == whichQ) { Qonly = whichQ.isSelected(); //println("quantize selected only?: " + Qonly); } else if(e.getSource() == playAll) { if(!playAll.isSelected()) sendPlayAll(); else sendStopAll(); } else if(e.getSource() == playThis) { if(!playThis.isSelected()) sendPlay(); else sendStop(); } else if(e.getSource() == restartAll) { //println("sending restart to all"); sendRestartAll(); } else if(e.getSource() == restartThis) { //println("sending restart to this"); sendRestartThis(); } else if(e.getSource() == reverse) { //println("reversing"); reverseSpokes(); } } void controlEvent(ControlEvent theEvent) { //println("got a control event from controller with id "+theEvent.controller().id()); switch(theEvent.controller().id()) { case(1): warp = (float)(theEvent.controller().value()); for(int i=0;i>>>>>>>>>>>>>>>> reversing spokes <<<<<<<<<<<<<<<<<<<"); } else if(inmessage.checkAddrPattern("/load")==true) { inload = inmessage.get(0).stringValue(); openCycler(inload); //println("got message to open cycler " + inload); } else if(inmessage.checkAddrPattern("/setwarp")==true) { float newWarp = inmessage.get(0).floatValue(); //for(int i=0;i0) { String first = newline.substring(0, endword); //println(first); if(first.equals("qspokes")) { temp = newline.substring(endword+1, newline.length()); qspokes = int(temp); initQspokes(); } else if(first.equals("numcircles")) { temp = newline.substring(endword+1, newline.length()); numCircles = int(temp); numCirclesInv = 1./numCircles; } else if(first.equals("cyclelen")) { temp = newline.substring(endword+1, newline.length()); tempo = float(temp); ctempo_text.setValue(str(tempo)); sendTempo(tempo); } else if(first.equals("warp")) { temp = newline.substring(endword+1, newline.length()); for(int i=0;i