summaryrefslogtreecommitdiff
path: root/gui
diff options
context:
space:
mode:
authorTim Redfern <tim@getdrop.com>2017-09-13 13:36:26 +0100
committerTim Redfern <tim@getdrop.com>2017-09-13 13:36:26 +0100
commitb4972d6ba659cec3759fd87d6f0489a040e0fbf6 (patch)
tree8646589d0db594c69eb5c6d6d86f6d05d8e3043f /gui
parent304c8f22dfb4e068685b2a1e1023129a7cd12eb4 (diff)
chainImageSet and threadedChainImageLoader
Diffstat (limited to 'gui')
-rw-r--r--gui/src/chainImageSet.cpp450
-rw-r--r--gui/src/chainImageSet.h61
-rw-r--r--gui/src/threadedChainImageLoader.cpp99
-rw-r--r--gui/src/threadedChainImageLoader.h53
4 files changed, 663 insertions, 0 deletions
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<chainImage>::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<chainImage>::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<json["images"].size();i++){
+ chainImage image;
+ if (image.fromJson(json["images"][i])){
+ images.push_back(image);
+ }
+ }
+ for (std::list<chainImage>::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 <chainImage> images;
+
+ float currentDefaultImageRatio;
+
+ std::list<chainImage>::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<chainImage> > 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 <sstream>
+
+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<string, chainImageLoaderEntry>::iterator entry_iterator;
+
+ int nextID;
+ int lastUpdate;
+
+ map<string,chainImageLoaderEntry> images_async_loading; // keeps track of images which are loading async
+ ofThreadChannel<chainImageLoaderEntry> images_to_load_from_disk;
+ ofThreadChannel<chainImageLoaderEntry> images_to_update;
+};
+
+