From b4972d6ba659cec3759fd87d6f0489a040e0fbf6 Mon Sep 17 00:00:00 2001 From: Tim Redfern Date: Wed, 13 Sep 2017 13:36:26 +0100 Subject: chainImageSet and threadedChainImageLoader --- gui/src/chainImageSet.cpp | 450 +++++++++++++++++++++++++++++++++++ gui/src/chainImageSet.h | 61 +++++ gui/src/threadedChainImageLoader.cpp | 99 ++++++++ gui/src/threadedChainImageLoader.h | 53 +++++ 4 files changed, 663 insertions(+) create mode 100644 gui/src/chainImageSet.cpp create mode 100644 gui/src/chainImageSet.h create mode 100644 gui/src/threadedChainImageLoader.cpp create mode 100644 gui/src/threadedChainImageLoader.h (limited to 'gui') diff --git a/gui/src/chainImageSet.cpp b/gui/src/chainImageSet.cpp new file mode 100644 index 0000000..5a538cb --- /dev/null +++ b/gui/src/chainImageSet.cpp @@ -0,0 +1,450 @@ +#include "chainImageSet.h" + +void chainImageSet::drawOutput(){ + + //movment and rotation are working on their own but not together. + + float camera_throw= (float)outputSize.y/(float)outputSize.x; //the ratio of z distance to x width + + camera_throw*=fitFactor; //fudge factor to allow tweening + + if (images.size()){ + +/* + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(-ofGetWidth()/2, ofGetWidth()/2, ofGetHeight()/2, -ofGetHeight()/2, 0.0f, 5000.0f); +*/ + glMatrixMode ( GL_MODELVIEW ); + glLoadIdentity ( ); + gluLookAt( currentImage->getTransform().x, + currentImage->getTransform().y, // i1.linkPos.y+(xform.y*intervalpoint), + currentImage->getWidth()*camera_throw*currentImage->getScale(), + currentImage->getTransform().x, + currentImage->getTransform().y, // i1.linkPos.y+(xform.y*intervalpoint), + 0, + sin(-currentImage->getRotation()*(PI/180)), + cos(-currentImage->getRotation()*(PI/180)), + 0); + + + currentImage->drawChain(DEFAULT_FADEIN,additive,intensity); + + } + +} + +void chainImageSet::drawGui(int x,int y,bool is_selected){ + + float t_xoffs=0.0; + float borderwidth=THUMB_SIZE*0.1; //ofGetWindowHeight()*(1.0-THUMB_BORDER_RATIO)*0.5; + + + + /* + float relscale=((float)ofGetWindowHeight())/THUMB_SIZE; + glScalef(relscale,relscale,relscale); + //scale view by distance betweeen thumnail size and window size: doesn't work + */ + + //draw each image, outlined + + glPushMatrix(); + + glTranslatef(x,y,0); + + for(std::list::iterator ii=images.begin(); ii != images.end(); ii++){ + + ofSetColor(255,255,255); + + float thumbx=ii->thumbnail.getWidth()/2; + float thumby=ii->thumbnail.getHeight()/2; + float thumbscale=ii->thumbnail.getWidth()/ii->getWidth(); + + + //why do I have to set this every time?? + ii->thumbnail.setAnchorPercent(0.5,0.5); + + ii->thumbnail.draw( + t_xoffs+borderwidth+thumbx, + borderwidth+thumby, + ii->thumbnail.getWidth(), + ii->thumbnail.getHeight() + ); + + ofSetColor(255,255,255); + if (ii==selected) ofSetColor(255,0,0); + if (&(*ii)==currentImage) ofSetColor(0,255,0); + + ofDrawLine(t_xoffs+borderwidth,borderwidth, + t_xoffs+borderwidth+ii->thumbnail.getWidth(),borderwidth); + ofDrawLine(t_xoffs+borderwidth+ii->thumbnail.getWidth(),borderwidth, + t_xoffs+borderwidth+ii->thumbnail.getWidth(),borderwidth+ii->thumbnail.getHeight()); + ofDrawLine(t_xoffs+borderwidth+ii->thumbnail.getWidth(),borderwidth+ii->thumbnail.getHeight(), + t_xoffs+borderwidth,borderwidth+ii->thumbnail.getHeight()); + ofDrawLine(t_xoffs+borderwidth,borderwidth+ii->thumbnail.getHeight(), + t_xoffs+borderwidth,borderwidth); + + ofSetColor(255,255,255); + + if (ii->link){ + ofPoint lp=ii->linkPos; + if (ii==selected){ + lp+=dragPoint; + } + + float subpictx=t_xoffs+borderwidth+thumbx+(lp.x*thumbscale); + float subpicty=borderwidth+thumby+(lp.y*thumbscale); + + ofDrawLine( + subpictx, + subpicty, + t_xoffs+(borderwidth*3)+ii->thumbnail.getWidth(), + borderwidth+thumby + + ); + + glPushMatrix(); + + glTranslatef(subpictx,subpicty,0); + + float r=ii->linkRot; + if (ii==selected) r+=dragRotate; + glRotatef(r,0,0,1); + + //printf("Sub image: centre at %f,%f \n",ii->link->thumbnail.getAnchorPoint().x,ii->link->thumbnail.getAnchorPoint().y); + + ii->link->thumbnail.setAnchorPercent(0.5,0.5); + + float thescale=ii->linkScale; + if (ii==selected){ + thescale*=(1.0f+dragScale); + } + + ii->link->thumbnail.draw( + 0, + 0, + ii->link->thumbnail.getWidth()*thescale, + ii->link->thumbnail.getHeight()*thescale + ); + + ofPoint p1=ofPoint( + -(ii->link->thumbnail.getWidth()*thescale*0.5), + -(ii->link->thumbnail.getHeight()*thescale*0.5)); + ofPoint p2=ofPoint( + (ii->link->thumbnail.getWidth()*thescale*0.5), + -(ii->link->thumbnail.getHeight()*thescale*0.5)); + ofPoint p3=ofPoint( + (ii->link->thumbnail.getWidth()*thescale*0.5), + (ii->link->thumbnail.getHeight()*thescale*0.5)); + ofPoint p4=ofPoint( + -(ii->link->thumbnail.getWidth()*thescale*0.5), + (ii->link->thumbnail.getHeight()*thescale*0.5)); + + ofDrawLine(p1,p2); + ofDrawLine(p2,p3); + ofDrawLine(p3,p4); + ofDrawLine(p4,p1); + + + + glPopMatrix(); + + } + + + t_xoffs+=ii->thumbnail.getWidth()+(borderwidth*2); + + + } + + if (is_selected) { + ofSetColor(0,0,255); + ofPolyline box; + box.addVertex(ofPoint(5,5)); + box.addVertex(ofPoint(t_xoffs-5,5)); + box.addVertex(ofPoint(t_xoffs-5,(THUMB_SIZE/THUMB_BORDER_RATIO)-10)); + box.addVertex(ofPoint(5,(THUMB_SIZE/THUMB_BORDER_RATIO)-10)); + box.addVertex(ofPoint(5,5)); + box.draw(); + } + + + glPopMatrix(); + +} + +bool chainImageSet::add(std::string filename,glm::vec2 pos){ + printf("Dropped file: %s at %f,%f \n",filename.c_str(),pos.x,pos.y); +/* +attempt to add file to chain. +find if file exists in data folder +if not make a symbolic link +*/ + chainImage *image=new chainImage(); // + + loadingImages.push_back(image); + + loader.loadFromDisk(*image,"bogwaterreeds/IMG_9819.JPG"); //filename); + + //if (image.load(filename)){ + + image->init(ofPoint(0,0), + currentDefaultImageRatio, + 0 //default rotation + ); + + printf("Loading file: %s at %f,%f \n",filename.c_str(),pos.x,pos.y); + + + //loadingImages.push_back(image); + + return true; + //} + //printf("Could not load file: %s \n",filename.c_str()); + + //return false; +} + + +void chainImageSet::keyPressed(ofKeyEventArgs &keyargs){ + //printf("Got key %i, modifiers:%i\n",keyargs.key,keyargs.modifiers); + switch(keyargs.key){ + case '[': + if (selected==images.begin()){ + selected=--images.end(); + } + else --selected; + break; + case ']': + ++selected; + if (selected==images.end()){ + selected=images.begin(); + } + break; + case OF_KEY_UP: + selected->linkPos.y--; + break; + case OF_KEY_DOWN: + selected->linkPos.y++; + break; + case OF_KEY_LEFT: + selected->linkPos.x--; + break; + case OF_KEY_RIGHT: + selected->linkPos.x++; + break; + case OF_KEY_BACKSPACE:{ + images.clear(); + filename.clear(); + /* + auto previous=selected; + previous--; + if (previous==images.end()){ + previous=images.end()--; + } + previous->link=selected->link; + printf("DELETED: %s \n",selected->filename.c_str()); + images.erase(selected); + selected=previous++; + if (selected==images.end()){ + selected=images.begin(); + } + break; + */ + } + case 's': + case 83: { + //printf("Saving... keyargs modifiers:%i\n",keyargs.modifiers); + //saves over last filename or use control to choose a new one + if (!filename.length()||keyargs.hasModifier(OF_KEY_CONTROL)){ + ofFileDialogResult saveFileResult = ofSystemSaveDialog(ofGetTimestampString() + ".json" , "Save your file"); + if (saveFileResult.bSuccess){ + filename=saveFileResult.filePath; + //printf("Got filename: %s\n",filename.c_str()); + } + //else printf("ofSystemSaveDialog failed.\n"); + } + if (filename.length()){ + //printf("Saving %s\n",filename.c_str()); + saveJson(filename); + } + + break; + } + case 'l':{ + //load_chain(); + ofFileDialogResult openFileResult= ofSystemLoadDialog("Select a json preset"); + + //Check if the user opened a file + if (openFileResult.bSuccess){ + + + bool success=loadJson(openFileResult.fileName); + + ofLogVerbose("Load %s %s\n", + openFileResult.filePath.c_str(), + success?"succeeded":"failed"); + + + }else { + ofLogVerbose("User hit cancel"); + } + break; + + } + } +} + +//-------------------------------------------------------------- +void chainImageSet::mouseDragged(int x, int y, int button){ + switch (button){ + case OF_MOUSE_BUTTON_1: + dragPoint=ofPoint(x-clickPoint.x,y-clickPoint.y)*(selected->getHeight()/ofGetWindowHeight()); + break; + case OF_MOUSE_BUTTON_2: + //alt-click + dragRotate=((clickPoint.x-x)/(selected->thumbnail.getWidth()))*180.0f; + break; + case OF_MOUSE_BUTTON_3: + //control-click + dragScale=(y-clickPoint.y)/(selected->thumbnail.getHeight()); + //if (dragScale*selected->linkScale<0.15){ + // dragScale=0.15/selected->linkScale; + //} + break; + } + } + +//-------------------------------------------------------------- +void chainImageSet::mousePressed(int x, int y, int button){ + //todo: select + clickPoint=ofPoint(x,y); +} + +//-------------------------------------------------------------- +void chainImageSet::mouseReleased(int x, int y, int button){ + if (images.size()){ + selected->linkPos+=dragPoint; + dragPoint=ofPoint(0,0); + selected->linkScale*=(1.0f+dragScale); + dragScale=0.0f; + selected->linkRot+=dragRotate; + dragRotate=0.0f; + } +} + +bool chainImageSet::saveJson(std::string filename){ + ofxJSON json; + + json["images"] = Json::Value(Json::arrayValue); + + for(std::list::iterator ii=images.begin(); ii != images.end(); ii++){ + json["images"].append(ii->toJson()); + } + + + json.save(filename, true); + +} +bool chainImageSet::loadJson(std::string _filename){ + ofxJSON json; + bool parsingSuccessful = json.open(_filename); + + if (parsingSuccessful) + { + images.clear(); + + for (int i=0;i::iterator ii=images.begin(); ii != images.end(); ii++){ + auto li=ii; + li++; + if (li==images.end()) { + li=images.begin(); + } + ii->link=&(*li); + + } + selected=images.begin(); + currentImage=&(*images.begin()); + currentImage->start(); + filename=_filename; + return true; + + + } + ofLogVerbose("JSON load: parsing unsuccesful\n"); + return false; +} + +void chainImageSet::updateOutput(){ + + for (auto i=loadingImages.begin();i!=loadingImages.end();){ + + printf("Checking loadingImage: %s\n",(*i)->filename.c_str()); + + + if ((*i)->isAllocated()){ + printf("Finished loadingImage: %s\n",(*i)->filename.c_str()); + + images.push_back(*(*i)); + (*images.rbegin()).link=&(*images.begin()); + + printf("Linked: %s to %s\n", + images.rbegin()->filename.c_str(), + images.begin()->filename.c_str()); + + + if (images.size()>1){ + (++images.rbegin())->link=&(*(images.rbegin())); + printf("Linked: %s to %s\n", + (++images.rbegin())->filename.c_str(), + images.rbegin()->filename.c_str()); + } + else { + selected=images.begin(); + currentImage=&(*images.begin()); + currentImage->start(); + } + + loadingImages.erase(i); + } + else i++; + + } + + + if (images.size()){ + int switchResponse=currentImage->updateOutput(decayFactor); + switch(switchResponse){ + case SWITCH_FORWARD:{ + currentImage=currentImage->link; + currentImage->start(); + ofLogNotice() << "Switched images forward"; + currentImage->updateOutput(decayFactor); + break; + } + case SWITCH_NONE: + break; + case SWITCH_REVERSE:{ + for (auto ii=images.begin();ii!=images.end();++ii){ + if (ii->link->filename==currentImage->filename){ + currentImage=&(*ii); + currentImage->start(true); + ofLogNotice() << "Switched images reverse"; + currentImage->updateOutput(decayFactor); + break; + } + } + break; + } + } + } +} + + diff --git a/gui/src/chainImageSet.h b/gui/src/chainImageSet.h new file mode 100644 index 0000000..87b1d11 --- /dev/null +++ b/gui/src/chainImageSet.h @@ -0,0 +1,61 @@ +#include "ofMain.h" +#include "ofxJSON.h" +#include "chainImage.h" +#include "threadedChainImageLoader.h" + + +class chainImageSet{ + public: + chainImageSet(){ + currentDefaultImageRatio=0.3; + filename=""; + outputSize=ofPoint(1024,576); + decayFactor=.999; + additive=false; + intensity=1.0f; + fitFactor=0.9f; + dragScale=0.0f; + dragRotate=0.0f; + dragPoint=ofPoint(0,0); + } + chainImageSet(const chainImageSet& mom){ + chainImageSet(); + } + void drawGui(int x,int y,bool is_selected); + void drawOutput(); + void updateOutput(); + bool add(std::string filename,glm::vec2 pos); + void keyPressed(ofKeyEventArgs &keyargs); + void mouseDragged(int x, int y, int button); + void mousePressed(int x, int y, int button); + void mouseReleased(int x, int y, int button); + + bool saveJson(std::string filename); + bool loadJson(std::string filename); + + ofPoint outputSize; + + std::list images; + + float currentDefaultImageRatio; + + std::list::iterator selected; + ofPoint clickPoint; + ofPoint dragPoint; + float dragScale; + float dragRotate; + float fitFactor; + + std::string filename; + + chainImage *currentImage; + + float decayFactor; + + bool additive; + float intensity; + + std::vector < std::unique_ptr > loadingImages; + + threadedChainImageLoader loader; +}; \ No newline at end of file diff --git a/gui/src/threadedChainImageLoader.cpp b/gui/src/threadedChainImageLoader.cpp new file mode 100644 index 0000000..ba3dd26 --- /dev/null +++ b/gui/src/threadedChainImageLoader.cpp @@ -0,0 +1,99 @@ +#include "threadedChainImageLoader.h" +#include + +threadedChainImageLoader::threadedChainImageLoader(){ + nextID = 0; + ofAddListener(ofEvents().update, this, &threadedChainImageLoader::update); + ofAddListener(ofURLResponseEvent(),this,&threadedChainImageLoader::urlResponse); + + startThread(); + lastUpdate = 0; +} + +threadedChainImageLoader::~threadedChainImageLoader(){ + images_to_load_from_disk.close(); + images_to_update.close(); + waitForThread(true); + ofRemoveListener(ofEvents().update, this, &threadedChainImageLoader::update); + ofRemoveListener(ofURLResponseEvent(),this,&threadedChainImageLoader::urlResponse); +} + +// Load an image from disk. +//-------------------------------------------------------------- +void threadedChainImageLoader::loadFromDisk(chainImage& image, string filename) { + nextID++; + chainImageLoaderEntry entry(image); + entry.filename = filename; + entry.image->setUseTexture(false); + entry.name = filename; + + images_to_load_from_disk.send(entry); +} + + +// Load an url asynchronously from an url. +//-------------------------------------------------------------- +void threadedChainImageLoader::loadFromURL(chainImage& image, string url) { + nextID++; + chainImageLoaderEntry entry(image); + entry.url = url; + entry.image->setUseTexture(false); + entry.name = "image" + ofToString(nextID); + images_async_loading[entry.name] = entry; + ofLoadURLAsync(entry.url, entry.name); +} + + +// Reads from the queue and loads new images. +//-------------------------------------------------------------- +void threadedChainImageLoader::threadedFunction() { + setThreadName("threadedChainImageLoader " + ofToString(thread.get_id())); + chainImageLoaderEntry entry; + while( images_to_load_from_disk.receive(entry) ) { + if(entry.image->load(entry.filename) ) { + images_to_update.send(entry); + }else{ + ofLogError("threadedChainImageLoader") << "couldn't load file: \"" << entry.filename << "\""; + } + } + ofLogVerbose("threadedChainImageLoader") << "finishing thread on closed queue"; +} + + +// When we receive an url response this method is called; +// The loaded image is removed from the async_queue and added to the +// update queue. The update queue is used to update the texture. +//-------------------------------------------------------------- +void threadedChainImageLoader::urlResponse(ofHttpResponse & response) { + // this happens in the update thread so no need to lock to access + // images_async_loading + entry_iterator it = images_async_loading.find(response.request.name); + if(response.status == 200) { + if(it != images_async_loading.end()) { + it->second.image->load(response.data); + images_to_update.send(it->second); + } + }else{ + // log error. + ofLogError("threadedChainImageLoader") << "couldn't load url, response status: " << response.status; + ofRemoveURLRequest(response.request.getID()); + } + + // remove the entry from the queue + if(it != images_async_loading.end()) { + images_async_loading.erase(it); + } +} + + +// Check the update queue and update the texture +//-------------------------------------------------------------- +void threadedChainImageLoader::update(ofEventArgs & a){ + // Load 1 image per update so we don't block the gl thread for too long + chainImageLoaderEntry entry; + if (images_to_update.tryReceive(entry)) { + entry.image->setUseTexture(true); + entry.image->update(); + } +} + diff --git a/gui/src/threadedChainImageLoader.h b/gui/src/threadedChainImageLoader.h new file mode 100644 index 0000000..01f0d03 --- /dev/null +++ b/gui/src/threadedChainImageLoader.h @@ -0,0 +1,53 @@ +#pragma once + +#include "ofThread.h" +#include "chainImage.h" +#include "ofURLFileLoader.h" +#include "ofTypes.h" +#include "ofThreadChannel.h" + + +using namespace std; + +class threadedChainImageLoader : public ofThread { +public: + threadedChainImageLoader(); + ~threadedChainImageLoader(); + + void loadFromDisk(chainImage& image, string file); + void loadFromURL(chainImage& image, string url); + + + +private: + void update(ofEventArgs & a); + virtual void threadedFunction(); + void urlResponse(ofHttpResponse & response); + + // Entry to load. + struct chainImageLoaderEntry { + chainImageLoaderEntry() { + image = NULL; + } + + chainImageLoaderEntry(chainImage & pImage) { + image = &pImage; + } + chainImage* image; + string filename; + string url; + string name; + }; + + + typedef map::iterator entry_iterator; + + int nextID; + int lastUpdate; + + map images_async_loading; // keeps track of images which are loading async + ofThreadChannel images_to_load_from_disk; + ofThreadChannel images_to_update; +}; + + -- cgit v1.2.3