#include "chainImage.h" #define min(a,b) ((a) < (b) ? (a) : (b)) #define max(a,b) ((a) > (b) ? (a) : (b)) float distance(ofPoint p1,ofPoint p2){ return pow(pow(p1.x-p2.x,2)+pow(p1.y-p2.y,2),0.5); } void chainImage::init(ofPoint _linkPos,float _linkScale,float _linkRot){ //#ifdef GPU_ALGORITHM shader.load("shader"); //#endif //GPU_ALGORITHM linkPos=_linkPos; linkScale=_linkScale; linkRot=_linkRot; setAnchorPercent(0.5,0.5); dragPos=ofPoint(0,0); dragRot=0; dragScale=0; } void chainImage::start(bool reverse){ transition=reverse?1.0f:0.0f; time=ofGetElapsedTimef(); setUseTexture(true); scale=reverse?linkScale*link->linkScale:linkScale; /* printf("Started: %s: link %f current scale %f transition %f\n", filename.c_str(), linkScale, scale, transition ); */ } void chainImage::gpu_drawImage(){ GLint internalTextureFormat=GL_TEXTURE_2D_ARRAY; GLsizei textureWidth=getWidth(); GLsizei textureHeight=getHeight(); GLsizei numberOfTextures=2; GLint border=0; GLenum textureFormat=GL_RGB; glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, internalTextureFormat, textureWidth, textureHeight, numberOfTextures, 0, textureFormat, GL_UNSIGNED_BYTE, NULL); //bind the texture (using GL_TEXTURE_2D_ARRAY as the texture type) and use glTexParameteri as usual chainImage *thisimage; //iterate through the images to put into your texture array for (int i=0;ilink; glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, i, thisimage->width, thisimage->height, 1, textureFormat, GL_UNSIGNED_BYTE, &thisimage->getPixels()); } /* //composite the frame and set the next getTransform ofSetColor(255); float basescale=ofGetScreenWidth()/ofGetWidth(); shader.begin(); shader.setUniform3f("transform",getTransform()); shader.setUniform1f("scale",1.0f); //shader.setUniform1f("rotation",getRotation()); //shader.setUniform1f("transition",transition); //shader.setUniform1f("fadeIn",fadeIn); //shader.setUniform1f("intensity",intensity); //shader.setUniform3f("linkPos",linkPos); shader.setUniformTexture("thisImage", getTexture(), 1 ); shader.setUniformTexture("nextImage", link->getTexture(), 2 ); //draw(0, 0,ofGetWidth(),ofGetHeight()); ofDrawRectangle(0, 0, ofGetScreenWidth(), ofGetScreenHeight()); shader.end(); */ } //rotation could be a lot smoother //atm we only do horizontal rotation splines //movement is a bit jerky //could it be possible to visualise the path? void chainImage::updateRotationTimeline(){ rotationTimeline.clear(); //need to compare 5 points to generate this segment //quick and dirty approach, bezier handles take the slope between the //previous and subsequent points, unless it's a horizontal turning point //rotation curve always starts at the origin as it's relative to where we have already rotated to rotationTimeline.addVertex(0,0); //float dR; //if (abs(link->linkRot-linkRot)>abs((link->linkRos+360.0f)-linkRot)){ // dR=link->linkRot; //} //else dR=link->linkRos+360.0f; //try to choose the shortest ofPoint cp1,cp2,p; //if(linked->getLinkRot()==0.0f){ //if this is where the rotation starts cp1=ofPoint(ROTATION_BEZIER_FRACTION, 0); //} //else { // float slope=link->getLinkRot()-linked->getLinkRot(); // cp1=ofPoint(ROTATION_BEZIER_FRACTION,slope/ROTATION_BEZIER_FRACTION); //} //if(link->link->getLinkRot()==0.0f){ //hmm, shouldn't use float equality cp2=ofPoint(1.0f-ROTATION_BEZIER_FRACTION, link->getLinkRot()); //} //else { // float slope=link->link->getLinkRot()-getLinkRot(); // cp2=ofPoint(1.0f-ROTATION_BEZIER_FRACTION, link->getLinkRot()); //} p=ofPoint(1.0f, link->getLinkRot()); rotationTimeline.bezierTo(cp1,cp2,p); } ofPoint chainImage::getPathPoint(){ //printf("get point at transition: %f \n",transition); return path.getPointAtLength(transition); } int chainImage::updateOutput(float decayRatio){ //where there is rotation of the link, the path needs to be rotated updateRotationTimeline(); path.clear(); path.addVertex(getLinkPos()); //path.addVertex(linkPos+(link->linkPos*linkScale)); ofPoint rotated_destination=ofPoint( (link->getLinkPos().x*cos(getLinkRot()*(PI/180)))-(link->getLinkPos().y*sin(getLinkRot()*(PI/180))), (link->getLinkPos().y*cos(getLinkRot()*(PI/180)))+(link->getLinkPos().x*sin(getLinkRot()*(PI/180))) ); ofPoint destination=getLinkPos()+(rotated_destination*getLinkScale()); ofPoint previous=getLinkPos()+(rotated_destination*getLinkScale()*(1.0f-BEZIER_IN)); path.bezierTo( getLinkPos().x*(1.0f+(BEZIER_OUT*linkScale)),getLinkPos().y*(1.0f+(BEZIER_OUT*linkScale)), previous.x,previous.y, destination.x,destination.y); //totalframes=log(1.0f/256)/log(decayRatio); scale*=decayRatio; //framecount++; //find n such that decayRatio^n=1/256 //logarithm of x base b = log(x)/log(b) //log(decayRatio,decayRatio^n) //n = log(decayRatio,1/256) transition=-(scale-getLinkScale())/(getLinkScale()-(getLinkScale()*link->getLinkScale())); //transition=min(1.0f,((float)framecount)/totalframes); /* printf("ChainImage %f: scale %f linkscale %f transition %f \n", decayRatio, scale, linkScale, transition); */ if (scale>getLinkScale()){ transition = 0.0f; return SWITCH_REVERSE; } if (scale>getLinkScale()*link->getLinkScale()){ return SWITCH_NONE; } transition = 1.0f; return SWITCH_FORWARD; } ofVec3f chainImage::getTransform(){ //ofVec3f _scaledTarget = link->linkPos * linkScale; //return linkPos + ( _scaledTarget * transition ); //ofPoint return path.getPointAtPercent(transition); } float chainImage::getScale(){ return scale; } float chainImage::getRotation(){ //this is the camera rotation //at the beginning of the transition //this should rotate to match linkRot //at the end, it should match link->linkRot //when we switch image, link->linkRot becomes linkRot //linkRot is no longer seen //return linkRot+(transition*link->linkRot); //linkRot+ return getLinkRot()+rotationTimeline.getPointAtPercent(transition).y; }; ofPoint chainImage::getLinkPos(){ return linkPos+dragPos; } float chainImage::getLinkRot(){ return linkRot+dragRot; } float chainImage::getLinkScale(){ return linkScale*(1.0f+dragScale); } void chainImage::makeThumbnail(){ thumbnail.setUseTexture(false); thumbnail=(const ofImage)*this; //copy the ofImage itself // thumbnail.setFromPixels(this->getPixels()); float thumbheight=THUMB_SIZE; //ofGetWindowHeight()*THUMB_BORDER_RATIO; float thumbwidth=(thumbnail.getWidth()/thumbnail.getHeight())*thumbheight; float borderwidth=ofGetWindowHeight()*(1.0-THUMB_BORDER_RATIO)*0.5; printf("Rescaling: %fx%f to %fx%f for screen %fx%f, border %f\n", thumbnail.getWidth(),thumbnail.getHeight(), thumbwidth,thumbheight, (float)ofGetWindowWidth(),(float)ofGetWindowHeight(), borderwidth); thumbnail.resize(thumbwidth,thumbheight); thumbnail.update(); printf("Rescaled %s: %fx%f\n", filename.c_str(), thumbnail.getWidth(), thumbnail.getHeight() ); thumbnail.setAnchorPoint(thumbnail.getWidth()/2,thumbnail.getHeight()/2); } void chainImage::drawChain(float fadeIn,bool additive,float intensity,float zoomMultiplier){ //printf("Drawing chain transition: %f\n",transition); //we are correctly geting to 1 //the transformw don't quite add up? //we are drawing each image twice? glPushMatrix(); if (additive){ glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE); ofSetColor(255,255,255,255*(1.0f-min(1.0,transition/fadeIn))*intensity); } else { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); ofSetColor(255,255,255,255*intensity); } //ofDisableAlphaBlending(); setAnchorPoint(getWidth()/2,getHeight()/2); //add a scale factor to the outgoing image that builds up over the transition glPushMatrix(); //if this is linear we see the jump from the unaccelerated phase //float zoomFactor=((zoomMultiplier-1.0f)*transition)+1.0f; //this is worse why? float zoomFactor=pow(zoomMultiplier,1.0f+transition); glScalef(zoomFactor,zoomFactor,zoomFactor); //ofSetColor(colour); draw(0,0,getWidth(),getHeight()); glPopMatrix(); glTranslatef(getLinkPos().x,getLinkPos().y,0); glRotatef(getLinkRot(),0,0,1); glScalef(getLinkScale(),getLinkScale(),getLinkScale()); //ofEnableAlphaBlending(); glEnable(GL_BLEND); ofSetColor(255,255,255,255*min(1.0,transition/fadeIn)*intensity); link->setAnchorPoint(link->getWidth()/2,link->getHeight()/2); //ofSetColor(link->colour); link->draw(0,0,link->getWidth(),link->getHeight()); glDisable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glPopMatrix(); } void chainImage::gpu_drawChain(float fadeIn,bool additive,float intensity,float zoomMultiplier){ ofSetColor(255); float basescale=ofGetScreenWidth()/ofGetWidth(); shader.begin(); shader.setUniform3f("transform",getTransform()); shader.setUniform1f("scale",1.0f); //shader.setUniform1f("rotation",getRotation()); //shader.setUniform1f("transition",transition); //shader.setUniform1f("fadeIn",fadeIn); //shader.setUniform1f("intensity",intensity); //shader.setUniform3f("linkPos",linkPos); shader.setUniformTexture("thisImage", getTexture(), 1 ); shader.setUniformTexture("nextImage", link->getTexture(), 2 ); //draw(0, 0,ofGetWidth(),ofGetHeight()); ofDrawRectangle(0, 0, ofGetScreenWidth(), ofGetScreenHeight()); shader.end(); //1. can we FILL THE SCREEN? } Json::Value chainImage::toJson(){ Json::Value json=Json::Value(Json::objectValue); json["linkPos"]=Json::Value(Json::arrayValue); json["linkPos"].append(linkPos.x); json["linkPos"].append(linkPos.y); json["linkScale"]=linkScale; json["linkRot"]=linkRot; json["filename"]=filename; return json; } bool chainImage::fromJson(Json::Value json){ if (load(json["filename"].asString())){ linkPos=ofPoint(json["linkPos"][0].asDouble(),json["linkPos"][1].asDouble()); linkScale=json["linkScale"].asDouble(); linkRot=json["linkRot"].asDouble(); return true; } return false; } void chainImage::setupTextures(){ setUseTexture(true); update(); thumbnail.setUseTexture(true); thumbnail.update(); }