9. Dezember 2010
Als eine Weiterentwicklung habe ich bei der Permutation, die schon auf den Mauszeiger interagiert, die Zeichen mit Beziérkurven neu erstellt, sodass ich dynamisch die Ecken der Quadrate abrunden kann. Ebenfalls habe ich weitere Parameter eingeführt und diese über Tastenkombinationen editierbar gemacht. Somit ist es mir möglich die Zeichen "on the fly" zu verändern und interessante Einstellungen mittels Screenshot zu speichern. Als letzte Anpassung habe ich eine Stack-Klasse erstellt, welche die Wertesettings dieser interessanten Permutationen speichern kann. Diese Wertesets werden nun eins nach dem anderen durchlaufen und die Werte dazwischen berechnet. Abhängig von millis(), den Millisekunden seit Programmstart, hat jede Transformation einer Permutation zu der nächsten eine definierbare Zeitspanne zur Verfügung. Somit verformen sich die verschiedenen Zeichen nahtlos in die definierten Zustände, wobei die Interaktion mit der Maus stets vorhanden bleibt. Als weiteres Vorgehen möchte ich noch mehr ansprechende Wertesets sammeln und den Gesamtablauf gestalten. Hauptprogrammcode//import megamu.shapetween.*; import ddf.minim.*; import fullscreen.*; SoftFullScreen _fullScreen; int anzahl = 7; int rand = 100; float xStep; float yStep; int[] permutationsIndexList = { 0, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 5, 2, 3, 4, 5, 6, 5, 4, 3, 4, 5, 6, 5, 4, 3, 4, 5, 6, 5, 4, 3, 2, 5, 6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 0 }; char mode = '1'; float scaleRangeTop = 1.0; float scaleRangeBottom = 1.0; float strokeRangeTop = 2.0; float strokeRangeBottom = 2.0; float heightRangeTop = 20; float heightRangeBottom = 10; float borderRadiusBlack = 0; float borderRadiusWhite = 0; float mirrorToggle = 1; // whether negative values are set to positive ones (ring) int keyMode = 1; int numSigns = permutationsIndexList.length; Sign[] signs = new Sign[numSigns]; // holds all objects of signs Stacks[] animationStacks = new Stacks[11]; // sets of values for animation float prevBorderRadiusBlack = borderRadiusBlack; float prevBorderRadiusWhite = borderRadiusWhite; float prevScaleRangeTop = scaleRangeTop; float prevScaleRangeBottom = scaleRangeBottom; float prevStrokeRangeTop = strokeRangeTop; float prevStrokeRangeBottom = strokeRangeBottom; float prevHeightRangeTop = heightRangeTop; float prevHeightRangeBottom = heightRangeBottom; //int animationNextTimeLimit=0; int animationPrevTimeLimit=0; int timerStack=0; int idleCompleted=0; //WaveformRenderer waveform; //AudioInput in; void setup() { size(880, 880); smooth(); PFont font; font = loadFont("Archer-Book-48.vlw"); textFont(font, 14); xStep = (width - 2 * rand) / (float)(anzahl-1); yStep = (height - 2 * rand) / (float)(anzahl-1); frameRate(24); int permutationsIndex = 0; for(int y=0; y<anzahl;y++) { for(int x=0; x<anzahl;x++) { signs[permutationsIndex] = new Sign(permutationsIndexList[permutationsIndex]); ++permutationsIndex; } } int keyAnim = 0; animationStacks[keyAnim] = new Stacks(); animationStacks[keyAnim].setDuration(0); animationStacks[keyAnim]._completed = true; keyAnim++; animationStacks[keyAnim] = new Stacks(); animationStacks[keyAnim].setDuration(6000); animationStacks[keyAnim].setBorderRadiusBlack(74); animationStacks[keyAnim].setBorderRadiusWhite(68); animationStacks[keyAnim].setHeightRange(0, 10); animationStacks[keyAnim].setScaleRange(2.1, 0.9); animationStacks[keyAnim].setStrokeRange(0.5, 6.5); keyAnim++; animationStacks[keyAnim] = new Stacks(); animationStacks[keyAnim].setDuration(6000); animationStacks[keyAnim].setBorderRadiusBlack(2.0); animationStacks[keyAnim].setBorderRadiusWhite(2.0); animationStacks[keyAnim].setHeightRange(0, 10); animationStacks[keyAnim].setScaleRange(1.0, 2.0); animationStacks[keyAnim].setStrokeRange(0.5, 6.5); keyAnim++; animationStacks[keyAnim] = new Stacks(); animationStacks[keyAnim].setDuration(6000); animationStacks[keyAnim].setBorderRadiusBlack(27.0); animationStacks[keyAnim].setBorderRadiusWhite(32.0); animationStacks[keyAnim].setHeightRange(10, 20); animationStacks[keyAnim].setScaleRange(2.9, -0.3); animationStacks[keyAnim].setStrokeRange(2.0, 2.0); keyAnim++; animationStacks[keyAnim] = new Stacks(); animationStacks[keyAnim].setDuration(6000); animationStacks[keyAnim].setBorderRadiusBlack(2.0); animationStacks[keyAnim].setBorderRadiusWhite(2.0); animationStacks[keyAnim].setHeightRange(9, 14); animationStacks[keyAnim].setScaleRange(2.2, -1.3); animationStacks[keyAnim].setStrokeRange(0.5, 0.5); keyAnim++; animationStacks[keyAnim] = new Stacks(); animationStacks[keyAnim].setDuration(6000); animationStacks[keyAnim].setBorderRadiusBlack(27.0); animationStacks[keyAnim].setBorderRadiusWhite(24.0); animationStacks[keyAnim].setHeightRange(10, 20); animationStacks[keyAnim].setScaleRange(2.6, -1.8); animationStacks[keyAnim].setStrokeRange(2.0, 2.0); keyAnim++; animationStacks[keyAnim] = new Stacks(); animationStacks[keyAnim].setDuration(6000); animationStacks[keyAnim].setBorderRadiusBlack(-26.0); animationStacks[keyAnim].setBorderRadiusWhite(-20.0); animationStacks[keyAnim].setHeightRange(0, 27); animationStacks[keyAnim].setScaleRange(1.9, 0.2); animationStacks[keyAnim].setStrokeRange(0.5, 14.5); keyAnim++; animationStacks[keyAnim] = new Stacks(); animationStacks[keyAnim].setDuration(6000); animationStacks[keyAnim].setBorderRadiusBlack(46.0); animationStacks[keyAnim].setBorderRadiusWhite(25.0); animationStacks[keyAnim].setHeightRange(10, 12.5); animationStacks[keyAnim].setScaleRange(2.4, 0); animationStacks[keyAnim].setStrokeRange(8, 12); keyAnim++; animationStacks[keyAnim] = new Stacks(); animationStacks[keyAnim].setDuration(6000); animationStacks[keyAnim].setBorderRadiusBlack(-7.0); animationStacks[keyAnim].setBorderRadiusWhite(28.0); animationStacks[keyAnim].setHeightRange(27, 31); animationStacks[keyAnim].setScaleRange(2.3, 2.4); animationStacks[keyAnim].setStrokeRange(0.5, 26); keyAnim++; animationStacks[keyAnim] = new Stacks(); animationStacks[keyAnim].setDuration(6000); animationStacks[keyAnim].setBorderRadiusBlack(-40.0); animationStacks[keyAnim].setBorderRadiusWhite(98.0); animationStacks[keyAnim].setHeightRange(10, 25); animationStacks[keyAnim].setScaleRange(2.1, 2.4); animationStacks[keyAnim].setStrokeRange(2, 2); keyAnim++; animationStacks[keyAnim] = new Stacks(); animationStacks[keyAnim].setDuration(6000); animationStacks[keyAnim].setBorderRadiusBlack(27.0); animationStacks[keyAnim].setBorderRadiusWhite(24.0); animationStacks[keyAnim].setHeightRange(10, 20); animationStacks[keyAnim].setScaleRange(2.6, -1.8); animationStacks[keyAnim].setStrokeRange(2.0, 2.0); //waveform = new WaveformRenderer(); //Minim minim = new Minim(this); //AudioPlayer groove = minim.loadFile("gas.mp3", 2048); //groove.addListener(waveform); //groove.loop(); //groove.play(); //in = minim.getLineIn(Minim.STEREO, 512); // FULLSCREEN _fullScreen = new SoftFullScreen(this); _fullScreen.enter(); } void draw() { background(255); // ani.position()*255 //noStroke(); int permutationsIndex = 0; /**println(in.bufferSize()); // draw the waveforms for(int i = 0; i < in.bufferSize() - 1; i++) { line(i, 50 + in.left.get(i)*50, i+1, 50 + in.left.get(i+1)*50); //line(i, 150 + in.right.get(i)*50, i+1, 150 + in.right.get(i+1)*50); }*/ fill(0); String info = "Scale: (1) "+nf(scaleRangeBottom,0,2)+" (2) "+nf(scaleRangeTop,0,2)+" "; info+= "Stroke: (3) "+nf(strokeRangeBottom,0,2)+" (4) "+nf(strokeRangeTop,0,2)+" "; info+= "Height: (5) "+nf(heightRangeBottom,0,2)+" (6) "+nf(heightRangeTop,0,2)+" "; info+= "Rad Black: (7) "+nf(borderRadiusBlack,0,2)+" "; info+= "Rad White: (8) "+nf(borderRadiusWhite,0,2)+" "; info+= "Mirror: (9) "+mirrorToggle; text(info,20,20); // animation int timerStack = millis() - animationPrevTimeLimit; for(int i=0; i<animationStacks.length; i++) { if (i>0 && idleCompleted==0) { if (((animationStacks[i-1]._completed==true && animationStacks[i]._completed==false)) && timerStack < animationStacks[i]._duration) { // single tranformation borderRadiusBlack = ((animationStacks[i]._borderRadiusBlack - prevBorderRadiusBlack) * timerStack / animationStacks[i]._duration) + prevBorderRadiusBlack; borderRadiusWhite = ((animationStacks[i]._borderRadiusWhite - prevBorderRadiusWhite) * timerStack / animationStacks[i]._duration) + prevBorderRadiusWhite; scaleRangeBottom = ((animationStacks[i]._scaleRangeBottom - prevScaleRangeBottom) * timerStack / animationStacks[i]._duration) + prevScaleRangeBottom; scaleRangeTop = ((animationStacks[i]._scaleRangeTop - prevScaleRangeTop) * timerStack / animationStacks[i]._duration) + prevScaleRangeTop; strokeRangeBottom = ((animationStacks[i]._strokeRangeBottom - prevStrokeRangeBottom) * timerStack / animationStacks[i]._duration) + prevStrokeRangeBottom; strokeRangeTop = ((animationStacks[i]._strokeRangeTop - prevStrokeRangeTop) * timerStack / animationStacks[i]._duration) + prevStrokeRangeTop; heightRangeBottom = ((animationStacks[i]._heightRangeBottom - prevHeightRangeBottom) * timerStack / animationStacks[i]._duration) + prevHeightRangeBottom; heightRangeTop = ((animationStacks[i]._heightRangeTop - prevHeightRangeTop) * timerStack / animationStacks[i]._duration) + prevHeightRangeTop; } else if (animationStacks[i]._completed==false && timerStack > animationStacks[i]._duration) { // single transformation completed animationStacks[i]._completed = true; prevBorderRadiusBlack = animationStacks[i]._borderRadiusBlack; prevBorderRadiusWhite = animationStacks[i]._borderRadiusWhite; prevScaleRangeBottom = animationStacks[i]._scaleRangeBottom; prevScaleRangeTop = animationStacks[i]._scaleRangeTop; prevStrokeRangeBottom = animationStacks[i]._strokeRangeBottom; prevStrokeRangeTop = animationStacks[i]._strokeRangeTop; prevHeightRangeBottom = animationStacks[i]._heightRangeBottom; prevHeightRangeTop = animationStacks[i]._heightRangeTop; animationPrevTimeLimit = millis()+animationStacks[i]._idleCompleted; timerStack = 0; idleCompleted = animationStacks[i]._idleCompleted; println("stop animation "+idleCompleted); } } else { // idle when at end of each transformation if (idleCompleted>0 && animationPrevTimeLimit < millis()) { idleCompleted=0; println("idle "+animationPrevTimeLimit+" > "+(millis())); } } } // soundtrack //float[] wLeft = subset(waveform.left,0,1); //float[] wRight = subset(waveform.right,0,1); // audio input //float soundLeftS = map(in.left.get(0),-0.1,0.1,0.2,1); for(int y=0; y<anzahl;y++) { for(int x=0; x<anzahl;x++) { signs[permutationsIndex].setSize(50); signs[permutationsIndex].setBorderRadiusBlack(borderRadiusBlack); signs[permutationsIndex].setBorderRadiusWhite(borderRadiusWhite); signs[permutationsIndex].setHeightRange(heightRangeBottom,heightRangeTop); signs[permutationsIndex].setScaleRange(scaleRangeBottom,scaleRangeTop); signs[permutationsIndex].setStrokeRange(strokeRangeBottom,strokeRangeTop); signs[permutationsIndex].setPositionMouse(mouseX,mouseY); signs[permutationsIndex].setPosition((int)xStep*x+rand,(int)yStep*y+rand); signs[permutationsIndex].draw(); ++permutationsIndex; } } //println(subset(waveform.left,0,1)); //waveform.draw(); } void mousePressed() { for(int i=0; i<animationStacks.length; i++) { if (i>0) { animationStacks[i]._completed=false; animationPrevTimeLimit = millis(); timerStack = 0; } } /** mouseStartPos.set(mouseX,mouseY,0); mouseEndPos = mouseStartPos.get(); startPosSet=true; endPosSet=false; */ } void keyPressed() { if (key == CODED) { if (keyCode == UP) { keyMode = 1; //println("up"); } else if (keyCode == DOWN) { keyMode = -1; //println("down"); } switch(mode) { case '2': scaleRangeTop+=0.1*keyMode; break; case '1': scaleRangeBottom+=0.1*keyMode; break; case '4': strokeRangeTop+=0.5*keyMode; break; case '3': strokeRangeBottom+=0.5*keyMode; break; case '6': heightRangeTop+=0.5*keyMode; break; case '5': heightRangeBottom+=1*keyMode; break; case '7': borderRadiusBlack+=1*keyMode; break; case '8': borderRadiusWhite+=1*keyMode; break; case '9': mirrorToggle = 1*keyMode; break; } } else { switch(key) { case ' ': save("permutation_"+hour()+"-"+minute()+"-"+second()+".jpg"); break; default: mode = key; break; } } }Klasse, welche ein einzelnes Zeichen schreibt
class Sign { int _type; // type of sign (0-6) int _rotation; // rotation sign int _rotInc; // incrementation of rotation float _scaling; // scaling 1 = original size int _posX; // position x on grid int _posY; // position y on grid PVector _position = new PVector(); int _size = 75; // initial size of sign int _mouseX; // position mouse x int _mouseY; // position mouse y PVector _mousePos = new PVector(); float[] _scaleRange = {3.0,1.0}; // flip range float[] _strokeRange = {0.2, 10.0}; float[] _heightRange = {1.0, 2.0}; float _borderRadiusBlack = 0; // radius for black rectangles float _borderRadiusWhite = 0; // radous for white ractangles // constructor Sign(int type) { _rotation = type*30; _type = type; _rotInc = 1; //_type+1; } void setSize(int sizeIni) { _size = sizeIni; // overwrite default rotate incrementation } void setRotationInc(int rotInc) { _rotInc = rotInc; // overwrite default rotate incrementation } void setPosition(int posX, int posY) // position of sign { _posX = posX; _posY = posY; _position.set(posX,posY,0); } void setPositionMouse(int mX, int mY) // mouse position { _mouseX = mX; _mouseY = mY; _mousePos.set(mX,mY,0); } void setScaleRange(float start, float end) { _scaleRange[0] = start; _scaleRange[1] = end; } void setStrokeRange(float start, float end) { _strokeRange[0] = start; _strokeRange[1] = end; } void setHeightRange(float start, float end) { _heightRange[0] = start; _heightRange[1] = end; } void setBorderRadiusBlack(float radius) { _borderRadiusBlack = radius; } void setBorderRadiusWhite(float radius) { _borderRadiusWhite = radius; } void draw() { // how far is this sign from mouse start point PVector dir = PVector.sub(_position,_mousePos); float distance = dir.mag(); // distance in px PVector center = new PVector(); center.set(_posX,_posY,0); float angleMouse = atan2(_mousePos.y - center.y, _mousePos.x - center.x); // scaling depends on distance to mouse pointer float _scaling = map(distance,0.0,width,_scaleRange[0],_scaleRange[1]); // 2,1: inverts range -> signs nearest to mous appear the biggest if (mirrorToggle<0 && _scaling<0.01) { _scaling = 0.01; // only positive numbers allowed } // rotation speed depends on sign type _rotation+=_rotInc; // stroke width depends on distance to mouse pointer float sw = map(distance,0.0,width,_strokeRange[0],_strokeRange[1]); if (sw<0) { sw*=-1; // only positive numbers allowed } // height range float hr = map(distance,0.0,30,_heightRange[0],_heightRange[1]); //int b1 = _size; pushMatrix(); translate(_posX,_posY); scale(_scaling); //rotate(radians(_rotation)); //radians(_rotation) pushStyle(); noFill(); noStroke(); int levels = 6; for(int i=0; i<levels; i++) { pushMatrix(); translate(1*(dir.x/hr)*i,1*(dir.y/hr)*i); float w = _size-(i*_size/(levels)); fill(0); // border radius is scaled for inner rectangles bezierRect(-1*w/2,-1*w/2,w,w, _borderRadiusBlack*(w/_size), _borderRadiusBlack*(w/_size)); fill(255); bezierRect(-1*(float)(w/2-sw/2),-1*(float)(w/2-sw/2),w-sw,w-sw, _borderRadiusWhite*(w/_size), _borderRadiusWhite*(w/_size)); popMatrix(); } popStyle(); popMatrix(); } /** @param x x-coordinate of upper-left @param y y-coordinate of upper-left @param w width of the rectangle @param h height of the rectangle @param xr radius to inset x-coordinate corners for bezier controls (may be negative to "outset") @param yr radius to inset y-coordinate corners for bezier controls (may be negative to "outset") */ void bezierRect(float x, float y, float w, float h, float xr, float yr) { float w2=w/2f, h2=h/2f, cx=x+w2, cy=y+h2; /**noFill(); stroke(0); ellipse(cx,cy-h2,10,10); ellipse(cx+w2-xr, cy-h2,5,5); ellipse(cx+w2, cy-h2+yr,5,5); ellipse( cx+w2, cy,10,10);*/ beginShape(); vertex(cx,cy-h2); bezierVertex(cx+w2-xr, cy-h2, cx+w2, cy-h2+yr, cx+w2, cy); bezierVertex(cx+w2, cy+h2-yr, cx+w2-xr, cy+h2, cx, cy+h2); bezierVertex(cx-w2+xr, cy+h2, cx-w2, cy+h2-yr, cx-w2, cy); bezierVertex(cx-w2, cy-h2+yr, cx-w2+xr, cy-h2, cx, cy-h2); endShape(); } }Kleine Klasse, welche das Werteset einer Permutation speichert
/** * stores set of values for a specific permutation */ class Stacks { int _duration = 1000; // milliseconds int _idleCompleted = 2000; // stop for a moment when transformation ended till next begins float[] _scaleRange = {3.0,1.0}; // flip range float[] _strokeRange = {0.2, 10.0}; float[] _heightRange = {1.0, 2.0}; float _borderRadiusBlack = 0; // radius for black rectangles float _borderRadiusWhite = 0; // radous for white ractangles boolean _completed = true; // is set on mouse click to false float _scaleRangeTop; float _scaleRangeBottom; float _strokeRangeTop; float _strokeRangeBottom; float _heightRangeTop; float _heightRangeBottom; // constructor Stacks() { } void setDuration(int duration) { _duration = duration; } void setScaleRange(float start, float end) { _scaleRange[0] = start; _scaleRange[1] = end; _scaleRangeBottom = start; _scaleRangeTop = end; } void setStrokeRange(float start, float end) { _strokeRange[0] = start; _strokeRange[1] = end; _strokeRangeBottom = start; _strokeRangeTop = end; } void setHeightRange(float start, float end) { _heightRange[0] = start; _heightRange[1] = end; _heightRangeBottom = start; _heightRangeTop = end; } void setBorderRadiusBlack(float radius) { _borderRadiusBlack = radius; } void setBorderRadiusWhite(float radius) { _borderRadiusWhite = radius; } }