12. Dezember 2011
Kinect Interaktion
Beschreibung
Setzen Sie einige der Mausinteraktionen aus Aufgabe 5 in Kinect um.
Vorgehensweise
Zuerst habe ich nochmals von vorne angefangen und die ganze Applikation auf Klassen aufgebaut: Eine Klasse für die einzelnen Zeichen, ein Container der Zeichen und die Hands Klasse von Max. Als Zeichen wollte ich zuerst wieder Kreise und Quadrate verwenden, dann bin ich durch Zufall auf ein Video von morphenden Zeichen gestossen, das mit Bezierkurver arbeitet und aus Kreisen zum Beispiel Quadraten in einer sauberen Animation Kreise machte. So habe ich dann eine Animationssequenz zwischen den verschiedenen Zeichen (bestehend aus je vier Bezierkurven) gemacht, die entweder automatisch abgespielt werden konnte, oder durch Userinput. Schlussendlich war es dem Benutzer möglich mit der Kinect alleine die Zeichen und die Interaktion/Permutation (je nach Definition) zu ändern.
Code herunterladen (.zip) Praesi.pde/*** Libraries ***/ import fullscreen.*; import processing.opengl.*; import peasy.*; /*** Global objects and variables ***/ PeasyCam cam; FullScreen fs; // Handtracking HandCapture handCapture; boolean drawFlag=false; ObjectContainer objectCont; int userKey = 0; PVector hand1Pos = new PVector(); PVector hand2Pos = new PVector(); int globalType = 0; float globalState = 0; int globalDirection = 1; boolean increment = false; float waitCounter = -1; /*** Config * This is the main section to make changes to. Go to town! ***/ boolean kinect = false; /*** Config: grid ***/ PVector workspace = new PVector(1024, 768); int gridAxisSize = 25; int gridSize = (int)sq(gridAxisSize); //defaultOptions values color defaultFilling = color(0); color defaultStroke = color(255); color defaultStrokeWeight = 1; int defaultInteractionMode = 6; //defaultOptions values end int mouseMode = 1; //Mouse mode 1: Use mouse position for interaction; Mouse mode 2: Use mouse as a value regulator float stateTransition = 0.05; boolean cursor = true; /*** Setup ***/ ArrayList defaultOptions = new ArrayList(); boolean block = false; void setup() { size((int)workspace.x, (int)workspace.y, OPENGL); if(kinect == true) { fs = new FullScreen(this); fs.enter(); handCapture = new HandCapture(this); } defaultOptions.add(defaultFilling); defaultOptions.add(defaultStroke); defaultOptions.add(defaultStrokeWeight); defaultOptions.add(defaultInteractionMode); /*** Setup objects ***/ objectCont = new ObjectContainer(gridAxisSize, gridSize, 0, 0, defaultOptions); /*** Setup style ***/ noStroke(); noFill(); smooth(); background(255); /*** Setup cam ***/ cam = new PeasyCam(this, width / 2, workspace.x / 2, 0, 1000); cam.setMinimumDistance(50); cam.setMaximumDistance(5000); } void draw() { if(kinect == false) { hand1Pos.x = mouseX; hand1Pos.y = mouseY; hand1Pos.z = 0; } else { handCapture.update(); if(handCapture.isHandTracked()) { pushStyle(); stroke(255,0,0); strokeWeight(8); PVector handPos = handCapture.handPos(); //point(handPos.x,handPos.y); hand1Pos.x = map(handPos.x, 0, handCapture.width(), 0, workspace.x); hand1Pos.y = map(handPos.y, 0, handCapture.height(), 0, workspace.y / 4 * 5); hand1Pos.z = handPos.z; if(hand1Pos.z < 1200) mouseMode = 2; else mouseMode = 1; popStyle(); } } //fill(200, 150); background(0); //rect(-2000, -2000, 4000, 4000); if(userKey != 0) objectCont.setInteractionMode(userKey - 49); objectCont.morphAll(globalType, globalState); objectCont.draw(); if(increment == true) { if(globalDirection == 1) { globalState += stateTransition; if(globalState >= 1) { globalType++; globalState = 0; } } else if(globalDirection == -1) { globalState = (globalState - stateTransition); if(globalState <= stateTransition) { globalType--; globalState = 1; } } if(globalType >= 10 && globalDirection == 1) globalDirection = -1; else if (globalType <= -1 && globalDirection == -1) globalDirection = 1; } else if (increment == false && mouseMode == 2) { /*X Axis*/ float stateAndType = map(hand1Pos.x, 0, workspace.x, 0, 10); /*globalType = floor(stateAndType / 10); globalState = stateAndType % 10;*/ float globalDistance = stateAndType - (globalType + globalState); if(abs(globalDistance) > 0) { globalState += globalDistance * stateTransition; if(globalState >= 1) { globalType = (globalType + 1) % 10; globalState = 0; } else if(globalState <= 0) { globalType = (globalType - 1) % 10; globalState = 1; } } } if(mouseMode == 2) { /*Y Axis*/ /*Switch between interactions by gesture at the top of the screen */ if(waitCounter == -1) waitCounter = millis(); if(hand1Pos.y < workspace.y / 4 && waitCounter + 2000 <= millis()) { int interactionMode = (objectCont.getInteractionMode() + 1) % 6; objectCont.setInteractionMode(interactionMode); waitCounter = millis(); } else if(hand1Pos.y < workspace.y / 4 && waitCounter + 2000 > millis()) { stroke(150, 27, 60, abs(waitCounter - millis()) / 4); strokeWeight(10); //ellipse(150, 300, 100, 100); } else if(hand1Pos.y <= workspace.y && hand1Pos.y >= workspace.y / 4) { noStroke(); waitCounter = millis(); } else if(hand1Pos.y >= workspace.y / 4) { noStroke(); } fill(255, 100); rect(0, 0, workspace.x, workspace.y / 4); /*Toggle on auto-increment by gesture at the bottom of the screen */ if(hand1Pos.y > workspace.y && hand1Pos.y < workspace.y / 4 * 5 && waitCounter + 2000 <= millis()) { increment = ! increment; waitCounter = millis(); } else if(hand1Pos.y > workspace.y && hand1Pos.y < workspace.y / 4 * 5 && waitCounter + 2000 > millis()) { stroke(150, 27, 60, abs(waitCounter - millis()) / 4); strokeWeight(10); //ellipse(150, workspace.y - 300, 100, 100); } else if(hand1Pos.y >= workspace.y / 4 && hand1Pos.y <= workspace.y) { noStroke(); waitCounter = millis(); } else if(hand1Pos.y <= workspace.y) { noStroke(); } fill(255, 100); rect(0, workspace.y, workspace.x, workspace.y / 4); } if(mouseMode == 2 || increment == true) { drawHud(); } pushMatrix(); fill(200, 27, 60, 250); translate(hand1Pos.x, hand1Pos.y); if(cursor == true) ellipse(0, 0, 10, 10); popMatrix(); } void mouseClicked() { if(mouseButton == LEFT) { increment = ! increment; } } void mousePressed() { if(kinect == false) mouseMode = 2; } void mouseReleased() { if(kinect == false) mouseMode = 1; } /*void mouseMoved() { }*/ void drawHud() { float stateOfTheHud = map(globalType + globalState, 0, 10, 50, workspace.x - 100); if(stateOfTheHud >= 0) { pushMatrix(); cam.beginHUD(); stroke(50, 150); strokeWeight(1); noFill(); //rect(50, workspace.y - 100, workspace.x - 100, 50); //for(int i = 1; i < 50; i++) { fill(150, 27, 60, 150); //line(50, workspace.y - 100 + i, stateOfTheHud, workspace.y - 100 + i); rect(50, workspace.y - 50, stateOfTheHud, 50); //} cam.endHUD(); popMatrix(); } } void keyPressed() { if(keyCode >= 49 && keyCode <= 59) userKey = keyCode; if(keyCode == 82) { cursor = ! cursor; } // redraw(); } // ----------------------------------------------------------------- // hand events void onCreateHands(int handId,PVector pos,float time) { handCapture.onCreateHands(handId,pos,time); } void onUpdateHands(int handId,PVector pos,float time) { handCapture.onUpdateHands(handId,pos,time); } void onDestroyHands(int handId,float time) { handCapture.onDestroyHands(handId,time); } void onRecognizeGesture(String strGesture, PVector idPosition, PVector endPosition) { handCapture.onRecognizeGesture(strGesture,idPosition,endPosition); } void onProgressGesture(String strGesture, PVector position,float progress) { handCapture.onProgressGesture(strGesture,position,progress); }HandCapture.pde
import SimpleOpenNI.*; class HandCapture { SimpleOpenNI _context; boolean _drawFlag=false; PVector _handVec = new PVector(); String _lastGesture = ""; PVector _screenPos = new PVector(); PApplet _parent; boolean _handsTrackFlag = false; HandCapture(PApplet parent) { _parent = parent; _context = new SimpleOpenNI(_parent); // mirror is by default enabled _context.setMirror(true); // enable depthMap _context.enableDepth(); // enable camera _context.enableRGB(); _context.enableHands(); _context.enableGesture(); _context.addGesture("RaiseHand"); // align depth data to image data _context.alternativeViewPointDepthToImage(); } boolean isHandTracked() { return _handsTrackFlag; } PVector handPos() { return _screenPos; } PVector handPos3d() { return _handVec; } PImage depthImage() { return _context.depthImage(); } PImage rgbImage() { return _context.rgbImage(); } int width() { return _context.depthWidth(); } int height() { return _context.depthHeight(); } void update() { _context.update(); _context.convertRealWorldToProjective(_handVec,_screenPos); } void onCreateHands(int handId, PVector pos, float time) { _handsTrackFlag = true; _handVec = pos; } void onUpdateHands(int handId, PVector pos, float time) { _handVec = pos; } void onDestroyHands(int handId, float time) { _handsTrackFlag = false; _context.addGesture(_lastGesture); } void onRecognizeGesture(String strGesture, PVector idPosition, PVector endPosition) { _lastGesture = strGesture; _context.removeGesture(strGesture); _context.startTrackingHands(endPosition); } void onProgressGesture(String strGesture, PVector position, float progress) { } }Object Container
class ObjectContainer { ArrayList _container; int _gridAxisSize; int _gridSize; ArrayList _defaultOptions; int _interactionMode; int _interactionModeOld; int _pulseDistance; float _pulseState; ObjectContainer(int gridAxisSize, int gridSize, ArrayList options) { this._gridAxisSize = gridAxisSize; this._gridSize = gridSize; this._defaultOptions = options; this._container = new ArrayList(); this._interactionMode = (Integer)options.get(3); this._pulseDistance = 0; this._pulseState = 0; } ObjectContainer(int gridAxisSize, int gridSize, int type, float state, ArrayList options) { this._gridAxisSize = gridAxisSize; this._gridSize = gridSize; this._defaultOptions = options; this._container = new ArrayList(); for(int cycle = 1; cycle <= _gridSize; cycle++) { this.add(type, state, this._defaultOptions); } this._interactionMode = (Integer)options.get(3); this._pulseDistance = 0; this._pulseState = 0; } void add(int type, float state, ArrayList options) { _container.add(_container.size(), new Objects(type, state, options)); } boolean draw() { if(_container.size() >= this._gridSize) { for(int cycle = 0; cycle < this._gridSize; cycle++) { Objects currentObject = (Objects)_container.get(cycle); float distance = dist(workspace.x / _gridAxisSize * (cycle % _gridAxisSize), workspace.x / _gridAxisSize * ceil(cycle / _gridAxisSize), hand1Pos.x, hand1Pos.y); if(distance <= workspace.y * sqrt(2)) distance = map(distance, 0, workspace.y * sqrt(2), 0, 10); else distance = 10; PVector objectPos; PVector mousePos; float distanceX; float distanceY; pushMatrix(); switch(this._interactionMode) { case 0: translate(workspace.x / _gridAxisSize * (cycle % _gridAxisSize), workspace.x / _gridAxisSize * ceil(cycle / _gridAxisSize), 0); break; case 1: objectPos = new PVector(workspace.x / _gridAxisSize * (cycle % _gridAxisSize), workspace.x / _gridAxisSize * ceil(cycle / _gridAxisSize), 0); mousePos = new PVector(hand1Pos.x, hand1Pos.y, 0); distanceX = map(mousePos.x - objectPos.x, -300 * 2, 300 * 2, PI / 2, -PI / 2); distanceY = map(mousePos.y - objectPos.y, -300 * 2, 300 * 2, -PI / 2, PI / 2); if(distance <= _gridAxisSize / 8) { translate(workspace.x / _gridAxisSize * (cycle % _gridAxisSize), workspace.x / _gridAxisSize * ceil(cycle / _gridAxisSize), (PI / 2) * 280 - sqrt(sq(distanceX) + sq(distanceY)) * 500); //float distAngle = PVector.angleBetween(objectPos, mousePos); float distAngle = atan2(mousePos.y - objectPos.y , mousePos.x - objectPos.x); rotateY(distanceX); rotateX(distanceY); } else { translate(workspace.x / _gridAxisSize * (cycle % _gridAxisSize), workspace.x / _gridAxisSize * ceil(cycle / _gridAxisSize)); } break; case 2: objectPos = new PVector(workspace.x / _gridAxisSize * (cycle % _gridAxisSize), workspace.x / _gridAxisSize * ceil(cycle / _gridAxisSize), 0); mousePos = new PVector(hand1Pos.x, hand1Pos.y, 0); distanceX = map(mousePos.x - objectPos.x, -300 * 2, 300 * 2, -PI / 2, PI / 2); distanceY = map(mousePos.y - objectPos.y, -300 * 2, 300 * 2, PI / 2, -PI / 2); if(distance <= _gridAxisSize / 8) { translate(workspace.x / _gridAxisSize * (cycle % _gridAxisSize), workspace.x / _gridAxisSize * ceil(cycle / _gridAxisSize), -((PI / 2) * 280 - sqrt(sq(distanceX) + sq(distanceY)) * 500)); //float distAngle = PVector.angleBetween(objectPos, mousePos); float distAngle = atan2(mousePos.y - objectPos.y , mousePos.x - objectPos.x); rotateY(distanceX); rotateX(distanceY); } else { translate(workspace.x / _gridAxisSize * (cycle % _gridAxisSize), workspace.x / _gridAxisSize * ceil(cycle / _gridAxisSize)); } break; case 3: objectPos = new PVector(workspace.x / _gridAxisSize * (cycle % _gridAxisSize), workspace.x / _gridAxisSize * ceil(cycle / _gridAxisSize), 0); mousePos = new PVector(hand1Pos.x, hand1Pos.y, 0); translate(workspace.x / _gridAxisSize * (cycle % _gridAxisSize), workspace.x / _gridAxisSize * ceil(cycle / _gridAxisSize)); float distAngle = atan2(mousePos.y - objectPos.y , mousePos.x - objectPos.x); scale(distance / 10); rotate(distAngle); break; case 4: //println(this._pulseDistance + "---" + round(distance)); if(round(distance * 2.5) == this._pulseDistance) { translate(workspace.x / _gridAxisSize * (cycle % _gridAxisSize), workspace.x / _gridAxisSize * ceil(cycle / _gridAxisSize), (0.5 + 0.5 * this._pulseState) * 100); scale(0.5 + 0.5 * this._pulseState); } else if(round(distance * 2.5) == this._pulseDistance + 1) { translate(workspace.x / _gridAxisSize * (cycle % _gridAxisSize), workspace.x / _gridAxisSize * ceil(cycle / _gridAxisSize), (1 - 0.5 * this._pulseState) * 100); scale(1 - 0.5 * this._pulseState); } else { translate(workspace.x / _gridAxisSize * (cycle % _gridAxisSize), workspace.x / _gridAxisSize * ceil(cycle / _gridAxisSize)); scale(0.5); } break; case 5: translate(workspace.x / _gridAxisSize * (cycle % _gridAxisSize), workspace.x / _gridAxisSize * ceil(cycle / _gridAxisSize), -distance * 10); rotate(radians(distance * 36)); break; case 6: translate(workspace.x / _gridAxisSize * (cycle % _gridAxisSize), workspace.x / _gridAxisSize * ceil(cycle / _gridAxisSize), -distance * 5); //rotate(radians(distance * 36)); scale((distance % 2) * 2); break; case 7: break; case 8: break; case 9: break; } currentObject.draw(); popMatrix(); } this._pulseState += 0.2; if(this._pulseState >= 1) { this._pulseState = 0; this._pulseDistance = (this._pulseDistance) % (_gridAxisSize) + 1; } return true; } else { return false; } } int getInteractionMode() { return this._interactionMode; } void setInteractionMode(int interactionMode) { this._interactionMode = interactionMode; } void morph(int objectId, int type, float state) { Objects currentObject = (Objects)_container.get(objectId); currentObject.setType(type); currentObject.setState(state); _container.set(objectId, currentObject); } void morphAll(int type, float state) { for(int cycle = 0; cycle < this._gridSize; cycle++) { Objects currentObject = (Objects)_container.get(cycle); currentObject.setType(type); currentObject.setState(state); _container.set(cycle, currentObject); } } }Objects
class Objects { int _type; float _state; color _filling; color _stroke; int _strokeWeight; boolean _extremeMode; Objects(int type, float state, ArrayList options) { this._type = type; this._state = state; this._filling = (color)(Integer)options.get(0); this._stroke = (color)(Integer)options.get(1); this._strokeWeight = (Integer)options.get(2); this._extremeMode = false; } void draw() { /* switch(this._type % 5) { case 0: case 1: case 2: case 3: case 4:*/ square(); /* break; case 5: break; case 6: break; case 7: break; case 8: break; case 9: break; }*/ } color getfilling() { return this._filling; } float getState() { return this._state; } int getType() { return this._type; } void setfilling(color filling) { this._filling = filling; } void setState(float state) { this._state = state; } void setType(int type) { this._type = type; } /*void square() { fill(this._filling); stroke(this._filling); shapeMode(CENTER); float objectWidth = workspace.x / (gridAxisSize * 1.5); beginShape(); vertex(0, 0); bezierVertex(0, 0, 0, objectWidth, 0, objectWidth); bezierVertex(0, objectWidth, objectWidth, objectWidth, objectWidth, objectWidth); bezierVertex(objectWidth, objectWidth, objectWidth, 0, objectWidth, 0); bezierVertex(objectWidth, 0, 0, 0, 0, 0); endShape(); stroke(255); endShape(); }*/ void square() { fill(this._filling); stroke(this._stroke); strokeWeight(this._strokeWeight); shapeMode(CENTER); float objectWidth = workspace.x / (gridAxisSize * 1.5); if(this._extremeMode == true) { for(int j = 0; j < 6; j++) { pushMatrix(); scale(1 - ((this._state + this._type) / 9) * 0.5); switch(j) { case 0: break; case 1: translate(0, 0, -objectWidth); rotateX(radians(90)); break; case 2: translate(0, objectWidth, -objectWidth); rotateX(radians(90)); break; case 3: translate(0, 0, 0); rotateY(radians(90)); break; case 4: translate(objectWidth, 0, 0); rotateY(radians(90)); break; case 5: translate(0, 0, -objectWidth); break; } beginShape(); vertex(0, 0); bezierVertex(-objectWidth * 0.25 * (this._state + this._type), objectWidth * 0.25 * (this._state + this._type), -objectWidth * 0.25 * (this._state + this._type), objectWidth - objectWidth * 0.25 * (this._state + this._type), 0, objectWidth); bezierVertex(objectWidth * 0.25 * (this._state + this._type), objectWidth + objectWidth * 0.25 * (this._state + this._type), objectWidth - objectWidth * 0.25 * (this._state + this._type), objectWidth + objectWidth * 0.25 * (this._state + this._type), objectWidth, objectWidth); bezierVertex(objectWidth + objectWidth * 0.25 * (this._state + this._type), objectWidth - objectWidth * 0.25 * (this._state + this._type), objectWidth + objectWidth * 0.25 * (this._state + this._type), objectWidth * 0.25 * (this._state + this._type), objectWidth, 0); bezierVertex(objectWidth - objectWidth * 0.25 * (this._state + this._type), -objectWidth * 0.25 * (this._state + this._type), objectWidth * 0.25 * (this._state + this._type), -objectWidth * 0.25 * (this._state + this._type), 0, 0); endShape(); popMatrix(); } } else { beginShape(); vertex(0, 0); bezierVertex(-objectWidth * 0.25 * (this._state + this._type), objectWidth * 0.25 * (this._state + this._type), -objectWidth * 0.25 * (this._state + this._type), objectWidth - objectWidth * 0.25 * (this._state + this._type), 0, objectWidth); bezierVertex(objectWidth * 0.25 * (this._state + this._type), objectWidth + objectWidth * 0.25 * (this._state + this._type), objectWidth - objectWidth * 0.25 * (this._state + this._type), objectWidth + objectWidth * 0.25 * (this._state + this._type), objectWidth, objectWidth); bezierVertex(objectWidth + objectWidth * 0.25 * (this._state + this._type), objectWidth - objectWidth * 0.25 * (this._state + this._type), objectWidth + objectWidth * 0.25 * (this._state + this._type), objectWidth * 0.25 * (this._state + this._type), objectWidth, 0); bezierVertex(objectWidth - objectWidth * 0.25 * (this._state + this._type), -objectWidth * 0.25 * (this._state + this._type), objectWidth * 0.25 * (this._state + this._type), -objectWidth * 0.25 * (this._state + this._type), 0, 0); endShape(); } } }