From 0ce892ae2e43a4f693c18e88fa994f72c38c3560 Mon Sep 17 00:00:00 2001 From: Felix Dubrownik Date: Tue, 2 Sep 2014 16:10:24 +0200 Subject: [PATCH 01/73] implemented perspective Warping openGL ES fixed funtction pipeline. --- example/src/ofApp.cpp | 18 ++++---- src/ofxQuadSurface.cpp | 97 +++++++++++++++++++++++++++++++++++++++++- src/ofxQuadSurface.h | 5 +++ 3 files changed, 110 insertions(+), 10 deletions(-) diff --git a/example/src/ofApp.cpp b/example/src/ofApp.cpp index 7012632..2cab8a6 100755 --- a/example/src/ofApp.cpp +++ b/example/src/ofApp.cpp @@ -124,16 +124,16 @@ void ofApp::addQuadSurface() vector vertices; int border = 50; - vertices.push_back( ofVec2f(border, border) ); - vertices.push_back( ofVec2f(border, ofGetHeight() - border) ); - vertices.push_back( ofVec2f(ofGetWidth() - border, ofGetHeight() - border) ); - vertices.push_back( ofVec2f(ofGetWidth() - border, border) ); - + vertices.push_back(ofVec2f(border, border)); + vertices.push_back(ofVec2f(ofGetWidth() - border, border)); + vertices.push_back(ofVec2f(ofGetWidth() - border, ofGetHeight() - border)); + vertices.push_back(ofVec2f(border, ofGetHeight() - border)); + vector texCoords; - texCoords.push_back( ofVec2f(ofVec2f(0.0f, 0.0f)) ); - texCoords.push_back( ofVec2f(ofVec2f(1.0f, 0.0f)) ); - texCoords.push_back( ofVec2f(ofVec2f(1.0f, 1.0f)) ); - texCoords.push_back( ofVec2f(ofVec2f(0.0f, 1.0f)) ); + texCoords.push_back(ofVec2f(ofVec2f(0.0f, 0.0f))); + texCoords.push_back(ofVec2f(ofVec2f(1.0f, 0.0f))); + texCoords.push_back(ofVec2f(ofVec2f(1.0f, 1.0f))); + texCoords.push_back(ofVec2f(ofVec2f(0.0f, 1.0f))); surfaceManager.addSurface(surfaceType, vertices, texCoords); diff --git a/src/ofxQuadSurface.cpp b/src/ofxQuadSurface.cpp index 68f8c7e..dd75931 100644 --- a/src/ofxQuadSurface.cpp +++ b/src/ofxQuadSurface.cpp @@ -52,12 +52,35 @@ void ofxQuadSurface::setup( ofVec2f p1, ofVec2f p2, ofVec2f p3, ofVec2f p4, mesh.addTexCoord(t2); mesh.addTexCoord(t3); mesh.addTexCoord(t4); + + // Pure GL setup + // indices + quadIndices[0] = 0; + quadIndices[1] = 1; + quadIndices[2] = 2; + quadIndices[3] = 0; + quadIndices[4] = 2; + quadIndices[5] = 3; + //tex coords (those are alway 0) + quadTexCoordinates[2] = 0; + quadTexCoordinates[6] = 0; + quadTexCoordinates[10] = 0; + quadTexCoordinates[14] = 0; + + calculate4dTextureCoords(); } void ofxQuadSurface::draw() { + if(mesh.haveVertsChanged() || mesh.haveTexCoordsChanged()){ + calculate4dTextureCoords(); + } + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(4, GL_FLOAT, 0, quadTexCoordinates); + glVertexPointer(3, GL_FLOAT, 0, quadVertices); + texture->bind(); - mesh.draw(); + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, quadIndices); texture->unbind(); } @@ -153,4 +176,76 @@ vector& ofxQuadSurface::getTexCoords() { return mesh.getTexCoords(); +} + +void ofxQuadSurface::calculate4dTextureCoords() +{ + // Perspective Warping with OpenGL Fixed Pipeline and q coordinates + // see: + // http://www.reedbeta.com/blog/2012/05/26/quadrilateral-interpolation-part-1/ + // for information on the technique + // Pue OpenGL is used because the ofMesh sadly doesn't support ofVec4f as + // texture coordinates. + // calculate intersection point + ofVec3f p0 = mesh.getVertex(0); + ofVec3f p1 = mesh.getVertex(1); + ofVec3f p2 = mesh.getVertex(2); + ofVec3f p3 = mesh.getVertex(3); + + ofVec3f t0 = mesh.getTexCoord(0); + ofVec3f t1 = mesh.getTexCoord(1); + ofVec3f t2 = mesh.getTexCoord(2); + ofVec3f t3 = mesh.getTexCoord(3); + + ofPoint interSect; + ofLineSegmentIntersection(ofPoint(p0.x, p0.y), ofPoint(p2.x, p2.y), + ofPoint(p1.x, p1.y), ofPoint(p3.x, p3.y), + interSect); + ofVec3f interSecVec = ofVec3f(interSect.x, interSect.y, 0); + + // calculate distances to intersection point + float d0 = interSecVec.distance(p0); + float d1 = interSecVec.distance(p1); + float d2 = interSecVec.distance(p2); + float d3 = interSecVec.distance(p3); + + // vertices + // top left corner + quadVertices[0] = p0.x; + quadVertices[1] = p0.y; + quadVertices[2] = 0; + // top right corner + quadVertices[3] = p1.x; + quadVertices[4] = p1.y; + quadVertices[5] = 0; + // bottom right corner + quadVertices[6] = p2.x; + quadVertices[7] = p2.y; + quadVertices[8] = 0; + // bottom left corner + quadVertices[9] = p3.x; + quadVertices[10] = p3.y; + quadVertices[11] = 0; + + float q0 = (d0 + d2) / (d2); + float q1 = (d1 + d3) / (d3); + float q2 = (d2 + d0) / (d0); + float q3 = (d3 + d1) / (d1); + + quadTexCoordinates[0] = t0.x; + quadTexCoordinates[1] = t0.y; + quadTexCoordinates[3] = q0; + + quadTexCoordinates[4] = t1.x * q1; + quadTexCoordinates[5] = t1.y; + quadTexCoordinates[7] = q1; + + quadTexCoordinates[8] = t2.x * q2; + quadTexCoordinates[9] = t2.y * q2; + quadTexCoordinates[11] = q2; + + quadTexCoordinates[12] = t3.x; + quadTexCoordinates[13] = t3.y * q3; + quadTexCoordinates[15] = q3; + } \ No newline at end of file diff --git a/src/ofxQuadSurface.h b/src/ofxQuadSurface.h index f2b8164..13bb421 100755 --- a/src/ofxQuadSurface.h +++ b/src/ofxQuadSurface.h @@ -29,6 +29,11 @@ public: ofPolyline getTextureHitArea(); vector& getVertices(); vector& getTexCoords(); +private: + void calculate4dTextureCoords(); + GLfloat quadVertices[12]; + GLubyte quadIndices[6]; + GLfloat quadTexCoordinates[16]; }; #endif \ No newline at end of file From a79cbc0d94ae3f14249ccc04a2d1342d976564f2 Mon Sep 17 00:00:00 2001 From: Felix Dubrownik Date: Tue, 2 Sep 2014 16:14:33 +0200 Subject: [PATCH 02/73] only recalculate on setVertex and SetTexCoord --- src/ofxQuadSurface.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ofxQuadSurface.cpp b/src/ofxQuadSurface.cpp index dd75931..1817478 100644 --- a/src/ofxQuadSurface.cpp +++ b/src/ofxQuadSurface.cpp @@ -72,9 +72,9 @@ void ofxQuadSurface::setup( ofVec2f p1, ofVec2f p2, ofVec2f p3, ofVec2f p4, void ofxQuadSurface::draw() { - if(mesh.haveVertsChanged() || mesh.haveTexCoordsChanged()){ + /*if(mesh.haveVertsChanged() || mesh.haveTexCoordsChanged()){ calculate4dTextureCoords(); - } + }*/ glEnableClientState(GL_TEXTURE_COORD_ARRAY); glTexCoordPointer(4, GL_FLOAT, 0, quadTexCoordinates); glVertexPointer(3, GL_FLOAT, 0, quadVertices); @@ -92,6 +92,7 @@ void ofxQuadSurface::setVertex(int index, ofVec2f p) } mesh.setVertex(index, p); + calculate4dTextureCoords(); } void ofxQuadSurface::setTexCoord(int index, ofVec2f t) @@ -102,6 +103,7 @@ void ofxQuadSurface::setTexCoord(int index, ofVec2f t) } mesh.setTexCoord(index, t); + calculate4dTextureCoords(); } int ofxQuadSurface::getType() From 5e2d481d9a6c9aeeb7128e0b4ce9098620b8cd88 Mon Sep 17 00:00:00 2001 From: Felix Dubrownik Date: Sat, 6 Sep 2014 10:09:53 +0200 Subject: [PATCH 03/73] bugfix: mesh doesn't recalculate when moved --- src/ofxBaseSurface.h | 1 + src/ofxProjectionEditor.cpp | 5 +++-- src/ofxQuadSurface.cpp | 9 +++++++++ src/ofxQuadSurface.h | 1 + src/ofxTriangleSurface.cpp | 8 ++++++++ src/ofxTriangleSurface.h | 1 + 6 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/ofxBaseSurface.h b/src/ofxBaseSurface.h index 300df97..9cd98b6 100644 --- a/src/ofxBaseSurface.h +++ b/src/ofxBaseSurface.h @@ -14,6 +14,7 @@ public: virtual void draw(){}; virtual void setVertex(int index, ofVec2f p){}; virtual void setTexCoord(int index, ofVec2f t){}; + virtual void moveBy(ofVec2f v){}; virtual int getType(){}; virtual bool hitTest(ofVec2f p){}; virtual ofPolyline getHitArea(){}; diff --git a/src/ofxProjectionEditor.cpp b/src/ofxProjectionEditor.cpp index 2762f82..7560ba8 100644 --- a/src/ofxProjectionEditor.cpp +++ b/src/ofxProjectionEditor.cpp @@ -209,10 +209,11 @@ void ofxProjectionEditor::moveSelectedSurface(ofVec2f by) { if ( surfaceManager == NULL ) return; if ( surfaceManager->getSelectedSurface() == NULL ) return; - vector& vertices = surfaceManager->getSelectedSurface()->getVertices(); + surfaceManager->getSelectedSurface()->moveBy(by); + /*vector& vertices = surfaceManager->getSelectedSurface()->getVertices(); for (int i=0; i& vertices = getVertices(); + for (int i=0; i& vertices = getVertices(); + for (int i=0; i Date: Sat, 6 Sep 2014 23:38:09 +0200 Subject: [PATCH 04/73] Add ofxRadioList base code --- src/ui/ofxRadioList.cpp | 95 +++++++++++++++++++++++++++++++++++++++++ src/ui/ofxRadioList.h | 23 ++++++++++ 2 files changed, 118 insertions(+) create mode 100644 src/ui/ofxRadioList.cpp create mode 100644 src/ui/ofxRadioList.h diff --git a/src/ui/ofxRadioList.cpp b/src/ui/ofxRadioList.cpp new file mode 100644 index 0000000..655fbe7 --- /dev/null +++ b/src/ui/ofxRadioList.cpp @@ -0,0 +1,95 @@ +#include "ofxRadioList.h" + +ofxRadioList::ofxRadioList(){} + +ofxRadioList::ofxRadioList(vector &labels) +{ + setup(labels); +} + +ofxRadioList::~ofxRadioList() +{ + removeListeners(); + + int i; + for (i = 0; i < toggles.size(); i++) { + delete toggles[i]; + } + toggles.clear(); +} + +void ofxRadioList::setup(vector &labels) +{ + // Create toggles with labels from the labels arg + int i; + for (i = 0; i < labels.size(); i++) { + ofxToggle* toggle = new ofxToggle(); + toggle->setup(false); + toggle->setName(labels[i]); + toggle->setPosition(0, toggle->getHeight() * i); + toggles.push_back(toggle); + } + + addListeners(); +} + +void ofxRadioList::draw() +{ + int i; + for (i = 0; i < toggles.size(); i++) { + toggles[i]->draw(); + } +} + +void ofxRadioList::addListeners() +{ + if (toggles.size() <= 0) return; + + int i; + for (i = 0; i < toggles.size(); i++) { + toggles[i]->addListener(this, &ofxRadioList::onToggleClicked); + } +} + +void ofxRadioList::removeListeners() +{ + if (toggles.size() <= 0) return; + + int i; + for (i = 0; i < toggles.size(); i++) { + toggles[i]->removeListener(this, &ofxRadioList::onToggleClicked); + } +} + +void ofxRadioList::unselectAll() +{ + int i; + + removeListeners(); + + for (i = 0; i < toggles.size(); i++) { + ofParameter* paramPtr = static_cast*>(&toggles[i]->getParameter()); + *toggles[i] = false; + } + + addListeners(); +} + +void ofxRadioList::onToggleClicked(bool &toggleValue) +{ + unselectAll(); + + // Search for the actual toggle triggering the event + int i; + for (i = 0; i < toggles.size(); i++) { + ofParameter* paramPtr = static_cast*>(&toggles[i]->getParameter()); + + if (&(paramPtr->get()) == &toggleValue) { + removeListeners(); + *toggles[i] = true; + cout << toggles[i]->getName() << endl; + addListeners(); + break; + } + } +} diff --git a/src/ui/ofxRadioList.h b/src/ui/ofxRadioList.h new file mode 100644 index 0000000..fa1a34c --- /dev/null +++ b/src/ui/ofxRadioList.h @@ -0,0 +1,23 @@ +#pragma once + +#include "ofGraphics.h" +#include "ofxToggle.h" + +class ofxRadioList +{ +public: + ofxRadioList(); + ofxRadioList(vector &labels); + ~ofxRadioList(); + + void setup(vector &labels); + void draw(); + +private: + vector toggles; + + void addListeners(); + void removeListeners(); + void unselectAll(); + void onToggleClicked(bool &toggleValue); +}; \ No newline at end of file From 022d31de5d8df5674a2fe237c64765194478a906 Mon Sep 17 00:00:00 2001 From: Krisjanis Rijnieks Date: Sun, 7 Sep 2014 12:53:14 +0200 Subject: [PATCH 05/73] Add ofxGuiGroup as the container for toggles Add event that notifies its listeners about selected radio button/toggle --- src/ui/ofxRadioList.cpp | 82 ++++++++++++++++++++--------------------- src/ui/ofxRadioList.h | 15 ++++++-- 2 files changed, 53 insertions(+), 44 deletions(-) diff --git a/src/ui/ofxRadioList.cpp b/src/ui/ofxRadioList.cpp index 655fbe7..9faddf2 100644 --- a/src/ui/ofxRadioList.cpp +++ b/src/ui/ofxRadioList.cpp @@ -9,13 +9,12 @@ ofxRadioList::ofxRadioList(vector &labels) ofxRadioList::~ofxRadioList() { - removeListeners(); - + int i; - for (i = 0; i < toggles.size(); i++) { - delete toggles[i]; + for (i = 0; i < guiGroup.getNumControls(); i++) { + ofxToggle* toggle = static_cast(guiGroup.getControl(i)); + toggle->removeListener(this, &ofxRadioList::onToggleClicked); } - toggles.clear(); } void ofxRadioList::setup(vector &labels) @@ -26,53 +25,51 @@ void ofxRadioList::setup(vector &labels) ofxToggle* toggle = new ofxToggle(); toggle->setup(false); toggle->setName(labels[i]); - toggle->setPosition(0, toggle->getHeight() * i); - toggles.push_back(toggle); + toggle->addListener(this, &ofxRadioList::onToggleClicked); + guiGroup.add(toggle); } - - addListeners(); } void ofxRadioList::draw() { - int i; - for (i = 0; i < toggles.size(); i++) { - toggles[i]->draw(); - } + guiGroup.draw(); } -void ofxRadioList::addListeners() +void ofxRadioList::setPosition(ofPoint p) { - if (toggles.size() <= 0) return; - - int i; - for (i = 0; i < toggles.size(); i++) { - toggles[i]->addListener(this, &ofxRadioList::onToggleClicked); - } + guiGroup.setPosition(p); } -void ofxRadioList::removeListeners() +void ofxRadioList::setPosition(float x, float y) { - if (toggles.size() <= 0) return; - - int i; - for (i = 0; i < toggles.size(); i++) { - toggles[i]->removeListener(this, &ofxRadioList::onToggleClicked); - } + guiGroup.setPosition(x, y); +} + +ofPoint ofxRadioList::getPosition() +{ + return guiGroup.getPosition(); +} + +float ofxRadioList::getWidth() +{ + return guiGroup.getWidth(); +} + +float ofxRadioList::getHeight() +{ + return guiGroup.getHeight(); } void ofxRadioList::unselectAll() { int i; - - removeListeners(); - - for (i = 0; i < toggles.size(); i++) { - ofParameter* paramPtr = static_cast*>(&toggles[i]->getParameter()); - *toggles[i] = false; + for (i = 0; i < guiGroup.getNumControls(); i++) { + ofxToggle* toggle = static_cast(guiGroup.getControl(i)); + ofParameter* paramPtr = static_cast*>(&toggle->getParameter()); + toggle->removeListener(this, &ofxRadioList::onToggleClicked); + *toggle = false; + toggle->addListener(this, &ofxRadioList::onToggleClicked); } - - addListeners(); } void ofxRadioList::onToggleClicked(bool &toggleValue) @@ -81,14 +78,17 @@ void ofxRadioList::onToggleClicked(bool &toggleValue) // Search for the actual toggle triggering the event int i; - for (i = 0; i < toggles.size(); i++) { - ofParameter* paramPtr = static_cast*>(&toggles[i]->getParameter()); + for (i = 0; i < guiGroup.getNumControls(); i++) { + ofxToggle* toggle = static_cast(guiGroup.getControl(i)); + ofParameter* paramPtr = static_cast*>(&toggle->getParameter()); if (&(paramPtr->get()) == &toggleValue) { - removeListeners(); - *toggles[i] = true; - cout << toggles[i]->getName() << endl; - addListeners(); + toggle->removeListener(this, &ofxRadioList::onToggleClicked); + *toggle = true; // Select the specific radio button + toggle->addListener(this, &ofxRadioList::onToggleClicked); + string name = toggle->getName(); + ofNotifyEvent(radioSelectedEvent, name, this); + //cout << toggle->getName() << endl; // debug break; } } diff --git a/src/ui/ofxRadioList.h b/src/ui/ofxRadioList.h index fa1a34c..c0aed7d 100644 --- a/src/ui/ofxRadioList.h +++ b/src/ui/ofxRadioList.h @@ -1,6 +1,7 @@ #pragma once #include "ofGraphics.h" +#include "ofxGuiGroup.h" #include "ofxToggle.h" class ofxRadioList @@ -12,12 +13,20 @@ public: void setup(vector &labels); void draw(); + void setPosition(ofPoint p); + void setPosition(float x, float y); + ofPoint getPosition(); + float getWidth(); + float getHeight(); + + // This event notifies about a toggle being selected and passes it's name to the listeners + // Use ofAddListener(ofxRadioListInstance.radioSelectedEvent, listenerClassPtr, &listenerClass::listenerMethod) + // to listen to this. Listner method void listenerMethod(string & radioName) + ofEvent radioSelectedEvent; private: - vector toggles; + ofxGuiGroup guiGroup; - void addListeners(); - void removeListeners(); void unselectAll(); void onToggleClicked(bool &toggleValue); }; \ No newline at end of file From 55cc4cfac6a43fe41c4c283a9712bf09dbeb3450 Mon Sep 17 00:00:00 2001 From: Krisjanis Rijnieks Date: Sun, 7 Sep 2014 13:29:13 +0200 Subject: [PATCH 06/73] Add getTitle() method to ofxRadioList --- src/ui/ofxRadioList.cpp | 63 +++++++++++++++++++++++++++++++++++++++-- src/ui/ofxRadioList.h | 9 +++++- 2 files changed, 69 insertions(+), 3 deletions(-) diff --git a/src/ui/ofxRadioList.cpp b/src/ui/ofxRadioList.cpp index 9faddf2..c32097f 100644 --- a/src/ui/ofxRadioList.cpp +++ b/src/ui/ofxRadioList.cpp @@ -1,17 +1,29 @@ #include "ofxRadioList.h" -ofxRadioList::ofxRadioList(){} +ofxRadioList::ofxRadioList() +{ + bHasTitle = false; +} ofxRadioList::ofxRadioList(vector &labels) { + bHasTitle = false; setup(labels); } -ofxRadioList::~ofxRadioList() +ofxRadioList::ofxRadioList(string title, vector &labels) { + bHasTitle = false; + setup(title, labels); +} +ofxRadioList::~ofxRadioList() +{ int i; for (i = 0; i < guiGroup.getNumControls(); i++) { + if (bHasTitle && i == 0) { + continue; + } ofxToggle* toggle = static_cast(guiGroup.getControl(i)); toggle->removeListener(this, &ofxRadioList::onToggleClicked); } @@ -30,11 +42,41 @@ void ofxRadioList::setup(vector &labels) } } +void ofxRadioList::setup(string title, vector &labels) +{ + ofxLabel* label = new ofxLabel(title); + guiGroup.add(label); + bHasTitle = true; + setup(labels); +} + void ofxRadioList::draw() { guiGroup.draw(); } +void ofxRadioList::setTitle(string title) +{ + if (bHasTitle) { + ofxLabel* label = static_cast(guiGroup.getControl(0)); + label->setup(title); + } else { + ofxLabel* label = new ofxLabel(title); + vector toggles; + int i; + for (i = 0; i < guiGroup.getNumControls(); i++) { + ofxToggle* toggle = static_cast(guiGroup.getControl(i)); + toggles.push_back(toggle); + } + guiGroup.clear(); + guiGroup.add(label); + bHasTitle = true; + for (i = 0; i < toggles.size(); i++) { + guiGroup.add(toggles[i]); + } + } +} + void ofxRadioList::setPosition(ofPoint p) { guiGroup.setPosition(p); @@ -60,10 +102,24 @@ float ofxRadioList::getHeight() return guiGroup.getHeight(); } +string ofxRadioList::getTitle() +{ + if (bHasTitle) { + ofxLabel* label = static_cast(guiGroup.getControl(0)); + ofParameter* parameter = static_cast*>(&label->getParameter()); + return parameter->get(); + } else { + return ""; + } +} + void ofxRadioList::unselectAll() { int i; for (i = 0; i < guiGroup.getNumControls(); i++) { + if (bHasTitle && i == 0) { + continue; + } ofxToggle* toggle = static_cast(guiGroup.getControl(i)); ofParameter* paramPtr = static_cast*>(&toggle->getParameter()); toggle->removeListener(this, &ofxRadioList::onToggleClicked); @@ -79,6 +135,9 @@ void ofxRadioList::onToggleClicked(bool &toggleValue) // Search for the actual toggle triggering the event int i; for (i = 0; i < guiGroup.getNumControls(); i++) { + if (bHasTitle && i == 0) { + continue; + } ofxToggle* toggle = static_cast(guiGroup.getControl(i)); ofParameter* paramPtr = static_cast*>(&toggle->getParameter()); diff --git a/src/ui/ofxRadioList.h b/src/ui/ofxRadioList.h index c0aed7d..9954513 100644 --- a/src/ui/ofxRadioList.h +++ b/src/ui/ofxRadioList.h @@ -3,29 +3,36 @@ #include "ofGraphics.h" #include "ofxGuiGroup.h" #include "ofxToggle.h" +#include "ofxLabel.h" class ofxRadioList { public: ofxRadioList(); ofxRadioList(vector &labels); + ofxRadioList(string title, vector &labels); ~ofxRadioList(); void setup(vector &labels); + void setup(string title, vector &labels); void draw(); + void setTitle(string title); void setPosition(ofPoint p); void setPosition(float x, float y); ofPoint getPosition(); float getWidth(); float getHeight(); + string getTitle(); + string getItem(int index); - // This event notifies about a toggle being selected and passes it's name to the listeners + // This event notifies about a toggle being selected and passes it's name to the listeners. // Use ofAddListener(ofxRadioListInstance.radioSelectedEvent, listenerClassPtr, &listenerClass::listenerMethod) // to listen to this. Listner method void listenerMethod(string & radioName) ofEvent radioSelectedEvent; private: ofxGuiGroup guiGroup; + bool bHasTitle; void unselectAll(); void onToggleClicked(bool &toggleValue); From e040ea7d36447793ffab7af3262e19d69fed052f Mon Sep 17 00:00:00 2001 From: Krisjanis Rijnieks Date: Sun, 7 Sep 2014 14:34:04 +0200 Subject: [PATCH 07/73] Fix ofxRadioList to work with ofxSourcesEditor Add getItemName method Add enable and disable methods Add storedLabels and storedTitle variables for recreating toggles on enable Add clear method --- src/ui/ofxRadioList.cpp | 141 ++++++++++++++++++++++++++++++++++------ src/ui/ofxRadioList.h | 12 +++- 2 files changed, 130 insertions(+), 23 deletions(-) diff --git a/src/ui/ofxRadioList.cpp b/src/ui/ofxRadioList.cpp index c32097f..f86e63f 100644 --- a/src/ui/ofxRadioList.cpp +++ b/src/ui/ofxRadioList.cpp @@ -3,34 +3,32 @@ ofxRadioList::ofxRadioList() { bHasTitle = false; + storedTitle = ""; + storedSelectedItem = 0; } ofxRadioList::ofxRadioList(vector &labels) { - bHasTitle = false; + ofxRadioList(); setup(labels); } ofxRadioList::ofxRadioList(string title, vector &labels) { - bHasTitle = false; + ofxRadioList(); setup(title, labels); } ofxRadioList::~ofxRadioList() { - int i; - for (i = 0; i < guiGroup.getNumControls(); i++) { - if (bHasTitle && i == 0) { - continue; - } - ofxToggle* toggle = static_cast(guiGroup.getControl(i)); - toggle->removeListener(this, &ofxRadioList::onToggleClicked); - } + clear(); } void ofxRadioList::setup(vector &labels) { + // Copy incomming labels for later use + storedLabels = labels; + // Create toggles with labels from the labels arg int i; for (i = 0; i < labels.size(); i++) { @@ -44,6 +42,9 @@ void ofxRadioList::setup(vector &labels) void ofxRadioList::setup(string title, vector &labels) { + // Store title for later use + storedTitle = title; + ofxLabel* label = new ofxLabel(title); guiGroup.add(label); bHasTitle = true; @@ -87,6 +88,88 @@ void ofxRadioList::setPosition(float x, float y) guiGroup.setPosition(x, y); } +void ofxRadioList::selectItem(int index) +{ + if (bHasTitle) { + // We don't count the ofxLabel as an item, thus if title is set + // items in guiGroup start from index 1 + index += 1; + } + + if (index >= guiGroup.getNumControls()) { + return; + } + + unselectAll(); + + ofxToggle* toggle = static_cast(guiGroup.getControl(index)); + toggle->removeListener(this, &ofxRadioList::onToggleClicked); + *toggle = true; // Select the specific radio button + toggle->addListener(this, &ofxRadioList::onToggleClicked); + string name = toggle->getName(); + ofNotifyEvent(radioSelectedEvent, name, this); + + storedSelectedItem = index; +} + +void ofxRadioList::enable() +{ + if (guiGroup.getNumControls() >= 0) { + clear(); + } + + // Rebuild everyting + if (bHasTitle) { + setup(storedTitle, storedLabels); + } else { + setup(storedLabels); + } + + if (bHasTitle && storedSelectedItem == 0) { + return; + } + + // Select the stored selected item without throwing an event + ofxToggle* toggle = static_cast(guiGroup.getControl(storedSelectedItem)); + toggle->removeListener(this, &ofxRadioList::onToggleClicked); + *toggle = true; + toggle->addListener(this, &ofxRadioList::onToggleClicked); +} + +void ofxRadioList::disable() +{ + // Just remove everything + clear(); +} + +void ofxRadioList::clear() +{ + int i; + for (i = 0; i < guiGroup.getNumControls(); i++) { + if (bHasTitle && i == 0) { + continue; + } + ofxToggle* toggle = static_cast(guiGroup.getControl(i)); + toggle->removeListener(this, &ofxRadioList::onToggleClicked); + } + guiGroup.clear(); +} + +void ofxRadioList::unselectAll() +{ + int i; + for (i = 0; i < guiGroup.getNumControls(); i++) { + if (bHasTitle && i == 0) { + continue; + } + ofxToggle* toggle = static_cast(guiGroup.getControl(i)); + ofParameter* paramPtr = static_cast*>(&toggle->getParameter()); + toggle->removeListener(this, &ofxRadioList::onToggleClicked); + *toggle = false; + toggle->addListener(this, &ofxRadioList::onToggleClicked); + } +} + ofPoint ofxRadioList::getPosition() { return guiGroup.getPosition(); @@ -113,18 +196,26 @@ string ofxRadioList::getTitle() } } -void ofxRadioList::unselectAll() +string ofxRadioList::getItemName(int index) { - int i; - for (i = 0; i < guiGroup.getNumControls(); i++) { - if (bHasTitle && i == 0) { - continue; - } - ofxToggle* toggle = static_cast(guiGroup.getControl(i)); - ofParameter* paramPtr = static_cast*>(&toggle->getParameter()); - toggle->removeListener(this, &ofxRadioList::onToggleClicked); - *toggle = false; - toggle->addListener(this, &ofxRadioList::onToggleClicked); + if (bHasTitle) { + index += 1; + } + + if (index >= guiGroup.getNumControls()) { + return ""; + } + + ofxToggle* toggle = static_cast(guiGroup.getControl(index)); + return toggle->getName(); +} + +int ofxRadioList::size() +{ + if (bHasTitle) { + return guiGroup.getNumControls() - 1; + } else { + return guiGroup.getNumControls(); } } @@ -142,12 +233,20 @@ void ofxRadioList::onToggleClicked(bool &toggleValue) ofParameter* paramPtr = static_cast*>(&toggle->getParameter()); if (&(paramPtr->get()) == &toggleValue) { + /* toggle->removeListener(this, &ofxRadioList::onToggleClicked); *toggle = true; // Select the specific radio button toggle->addListener(this, &ofxRadioList::onToggleClicked); string name = toggle->getName(); ofNotifyEvent(radioSelectedEvent, name, this); //cout << toggle->getName() << endl; // debug + */ + if (bHasTitle) { + selectItem(i - 1); + } else { + selectItem(i); + } + break; } } diff --git a/src/ui/ofxRadioList.h b/src/ui/ofxRadioList.h index 9954513..df960bc 100644 --- a/src/ui/ofxRadioList.h +++ b/src/ui/ofxRadioList.h @@ -19,11 +19,17 @@ public: void setTitle(string title); void setPosition(ofPoint p); void setPosition(float x, float y); + void selectItem(int index); + void enable(); + void disable(); + void clear(); + void unselectAll(); ofPoint getPosition(); float getWidth(); float getHeight(); string getTitle(); - string getItem(int index); + string getItemName(int index); + int size(); // This event notifies about a toggle being selected and passes it's name to the listeners. // Use ofAddListener(ofxRadioListInstance.radioSelectedEvent, listenerClassPtr, &listenerClass::listenerMethod) @@ -31,9 +37,11 @@ public: ofEvent radioSelectedEvent; private: + vector storedLabels; + string storedTitle; ofxGuiGroup guiGroup; bool bHasTitle; + int storedSelectedItem; - void unselectAll(); void onToggleClicked(bool &toggleValue); }; \ No newline at end of file From 88d3b51bdfa808d56755b6ae66859458128af8a0 Mon Sep 17 00:00:00 2001 From: Krisjanis Rijnieks Date: Sun, 7 Sep 2014 14:36:25 +0200 Subject: [PATCH 08/73] Fix ofxSourcesEditor to work with ofxRadioList --- src/ofxSourcesEditor.cpp | 55 ++++++++++++---------------------------- src/ofxSourcesEditor.h | 6 ++--- 2 files changed, 19 insertions(+), 42 deletions(-) diff --git a/src/ofxSourcesEditor.cpp b/src/ofxSourcesEditor.cpp index 372f13d..34c9e07 100644 --- a/src/ofxSourcesEditor.cpp +++ b/src/ofxSourcesEditor.cpp @@ -28,9 +28,7 @@ void ofxSourcesEditor::unregisterAppEvents() void ofxSourcesEditor::setup(ofEventArgs& args) { - gui = new ofxUICanvas(); - gui->disable(); - gui->disableAppDrawCallback(); + gui = new ofxRadioList(); // read directory contents ofDirectory imgDir; @@ -44,11 +42,15 @@ void ofxSourcesEditor::setup(ofEventArgs& args) vnames.push_back(imgDir.getName(i)); } - gui->addLabel(defImgDir, OFX_UI_FONT_SMALL); - ofxUIRadio *radio = gui->addRadio("images", vnames, OFX_UI_ORIENTATION_VERTICAL); - radio->activateToggle("image0.png"); + gui->setup("Images", vnames); + ofAddListener(gui->radioSelectedEvent, this, &ofxSourcesEditor::guiEvent); + - ofAddListener(gui->newGUIEvent,this,&ofxSourcesEditor::guiEvent); + //gui->addLabel(defImgDir, OFX_UI_FONT_SMALL); + //ofxUIRadio *radio = gui->addRadio("images", vnames, OFX_UI_ORIENTATION_VERTICAL); + //radio->activateToggle("image0.png"); + + //ofAddListener(gui->newGUIEvent,this,&ofxSourcesEditor::guiEvent); } void ofxSourcesEditor::draw() @@ -83,29 +85,14 @@ void ofxSourcesEditor::setSurfaceManager(ofxSurfaceManager *newSurfaceManager) void ofxSourcesEditor::selectImageSourceRadioButton(string name) { - vector widgets = gui->getWidgets(); - - // find radio list item - ofxUIRadio* radio; - for ( int i=0; igetKind(); - if ( widgetKind == OFX_UI_WIDGET_RADIO ){ - radio = (ofxUIRadio*)widgets[i]; - break; - } - } - if (name == "none") { - ofxUIToggle* toggle = (ofxUIToggle*)radio->getActive(); - if ( toggle != NULL ) { - toggle->setValue(false); - } + gui->unselectAll(); return; } else { - for ( int i=0; igetName(); - if ( name == widgetName ) { - radio->activateToggle(name); + int i; + for (i = 0; i < gui->size(); i++) { + if (gui->getItemName(i) == name) { + gui->selectItem(i); return; } } @@ -126,24 +113,14 @@ ofTexture* ofxSourcesEditor::getTexture(int index) return &images[index]->getTextureReference(); } -void ofxSourcesEditor::guiEvent(ofxUIEventArgs &e) +void ofxSourcesEditor::guiEvent(string &imageName) { - string name = e.widget->getName(); - int kind = e.widget->getKind(); - - if(kind == OFX_UI_WIDGET_TOGGLE){ - ofxUIToggle *toggle = (ofxUIToggle *) e.widget; - cout << name << "\t value: " << toggle->getValue() << endl; - } + string name = imageName; if ( surfaceManager->getSelectedSurface() == NULL ) { return; } - if (name == "images") { - return; - } - stringstream ss; ss << defImgDir << name; cout << "attempt to load image: " << ss.str() << endl; diff --git a/src/ofxSourcesEditor.h b/src/ofxSourcesEditor.h index 7344e33..cab567e 100644 --- a/src/ofxSourcesEditor.h +++ b/src/ofxSourcesEditor.h @@ -3,8 +3,8 @@ #include "ofGraphics.h" #include "ofEvents.h" -#include "ofxUI.h" #include "ofxSurfaceManager.h" +#include "ofxRadioList.h" #define DEFAULT_IMAGES_DIR "sources/images/"; @@ -30,9 +30,9 @@ public: private: ofxSurfaceManager* surfaceManager; + ofxRadioList* gui; string defImgDir; - ofxUICanvas *gui; - void guiEvent(ofxUIEventArgs &e); + void guiEvent(string &imageName); vector images; vector imageNames; //ofxPanel imgSrcPanel; From c12a1c110e5e0694918eeb8b55ade4eaf78008a7 Mon Sep 17 00:00:00 2001 From: Krisjanis Rijnieks Date: Sun, 7 Sep 2014 14:43:50 +0200 Subject: [PATCH 09/73] Not showing source list if there is no surface selected --- src/ofxSourcesEditor.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/ofxSourcesEditor.cpp b/src/ofxSourcesEditor.cpp index 34c9e07..0d5e2f5 100644 --- a/src/ofxSourcesEditor.cpp +++ b/src/ofxSourcesEditor.cpp @@ -44,17 +44,15 @@ void ofxSourcesEditor::setup(ofEventArgs& args) gui->setup("Images", vnames); ofAddListener(gui->radioSelectedEvent, this, &ofxSourcesEditor::guiEvent); - - - //gui->addLabel(defImgDir, OFX_UI_FONT_SMALL); - //ofxUIRadio *radio = gui->addRadio("images", vnames, OFX_UI_ORIENTATION_VERTICAL); - //radio->activateToggle("image0.png"); - - //ofAddListener(gui->newGUIEvent,this,&ofxSourcesEditor::guiEvent); } void ofxSourcesEditor::draw() { + // Don't draw if there is no source selected + if ( surfaceManager->getSelectedSurface() == NULL ) { + return; + } + gui->draw(); } @@ -75,6 +73,12 @@ void ofxSourcesEditor::disable() void ofxSourcesEditor::enable() { + // Don't enable if there is no surface selected + if ( surfaceManager->getSelectedSurface() == NULL ) { + cout << "No surface selected. Not enable()ing source list." << endl; + return; + } + gui->enable(); } From f32fd852d933ec02fb231af92bf6593448e15fc8 Mon Sep 17 00:00:00 2001 From: Krisjanis Rijnieks Date: Sun, 7 Sep 2014 14:47:19 +0200 Subject: [PATCH 10/73] Replace ofxUI dependency with ofxGui --- example/addons.make | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/addons.make b/example/addons.make index 802cd29..e98663f 100644 --- a/example/addons.make +++ b/example/addons.make @@ -1,3 +1,3 @@ ofxPiMapper -ofxUI +ofxGui ofxXmlSettings From 2d3d73082152391ebdc992a52c69ea4fb5dbe687 Mon Sep 17 00:00:00 2001 From: Krisjanis Rijnieks Date: Sun, 7 Sep 2014 14:49:17 +0200 Subject: [PATCH 11/73] Fix link to A successgul Git branching model article --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ffe8880..41ded08 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Development As the projects gets a bit more popular, I see that people want to add missing features. I have a whole bunch of features that I want to add in future releases, but right now I'm trying to understand how to keep it more or less organized. -Currently I have decided to use [A successful Git branching model](http://nvie.com/posts/a-successful-git) by [Vincent Driessen](https://twitter.com/nvie), so read this article and I do not doubt that it will help you with other Git related projects. +Currently I have decided to use [A successful Git branching model](http://nvie.com/posts/a-successful-git-branching-model/) by [Vincent Driessen](https://twitter.com/nvie), so read this article and I do not doubt that it will help you with other Git related projects. I'm still working on boosting my understanding about the issue tracking system on GitHub, I believe that it would be the best way how to keep new feature requests and bugfixes organized. From ef5ac3da5276e6441d07d48601b80fe9461141c8 Mon Sep 17 00:00:00 2001 From: Krisjanis Rijnieks Date: Sun, 7 Sep 2014 15:13:44 +0200 Subject: [PATCH 12/73] Fix there is no need for a guiGroup label item as it has a header already --- src/ui/ofxRadioList.cpp | 89 ++++++----------------------------------- src/ui/ofxRadioList.h | 1 - 2 files changed, 12 insertions(+), 78 deletions(-) diff --git a/src/ui/ofxRadioList.cpp b/src/ui/ofxRadioList.cpp index f86e63f..e974284 100644 --- a/src/ui/ofxRadioList.cpp +++ b/src/ui/ofxRadioList.cpp @@ -2,7 +2,6 @@ ofxRadioList::ofxRadioList() { - bHasTitle = false; storedTitle = ""; storedSelectedItem = 0; } @@ -38,16 +37,15 @@ void ofxRadioList::setup(vector &labels) toggle->addListener(this, &ofxRadioList::onToggleClicked); guiGroup.add(toggle); } + + cout << "num items: " << guiGroup.getNumControls() << endl; } void ofxRadioList::setup(string title, vector &labels) { // Store title for later use storedTitle = title; - - ofxLabel* label = new ofxLabel(title); - guiGroup.add(label); - bHasTitle = true; + guiGroup.setName(title); setup(labels); } @@ -58,24 +56,8 @@ void ofxRadioList::draw() void ofxRadioList::setTitle(string title) { - if (bHasTitle) { - ofxLabel* label = static_cast(guiGroup.getControl(0)); - label->setup(title); - } else { - ofxLabel* label = new ofxLabel(title); - vector toggles; - int i; - for (i = 0; i < guiGroup.getNumControls(); i++) { - ofxToggle* toggle = static_cast(guiGroup.getControl(i)); - toggles.push_back(toggle); - } - guiGroup.clear(); - guiGroup.add(label); - bHasTitle = true; - for (i = 0; i < toggles.size(); i++) { - guiGroup.add(toggles[i]); - } - } + storedTitle = title; + guiGroup.setName(title); } void ofxRadioList::setPosition(ofPoint p) @@ -90,12 +72,6 @@ void ofxRadioList::setPosition(float x, float y) void ofxRadioList::selectItem(int index) { - if (bHasTitle) { - // We don't count the ofxLabel as an item, thus if title is set - // items in guiGroup start from index 1 - index += 1; - } - if (index >= guiGroup.getNumControls()) { return; } @@ -119,21 +95,15 @@ void ofxRadioList::enable() } // Rebuild everyting - if (bHasTitle) { - setup(storedTitle, storedLabels); - } else { - setup(storedLabels); - } - - if (bHasTitle && storedSelectedItem == 0) { - return; - } + setup(storedTitle, storedLabels); // Select the stored selected item without throwing an event ofxToggle* toggle = static_cast(guiGroup.getControl(storedSelectedItem)); toggle->removeListener(this, &ofxRadioList::onToggleClicked); *toggle = true; toggle->addListener(this, &ofxRadioList::onToggleClicked); + + cout << "num items after enable: " << guiGroup.getNumControls() << endl; } void ofxRadioList::disable() @@ -146,11 +116,9 @@ void ofxRadioList::clear() { int i; for (i = 0; i < guiGroup.getNumControls(); i++) { - if (bHasTitle && i == 0) { - continue; - } ofxToggle* toggle = static_cast(guiGroup.getControl(i)); toggle->removeListener(this, &ofxRadioList::onToggleClicked); + delete toggle; } guiGroup.clear(); } @@ -159,9 +127,6 @@ void ofxRadioList::unselectAll() { int i; for (i = 0; i < guiGroup.getNumControls(); i++) { - if (bHasTitle && i == 0) { - continue; - } ofxToggle* toggle = static_cast(guiGroup.getControl(i)); ofParameter* paramPtr = static_cast*>(&toggle->getParameter()); toggle->removeListener(this, &ofxRadioList::onToggleClicked); @@ -187,21 +152,11 @@ float ofxRadioList::getHeight() string ofxRadioList::getTitle() { - if (bHasTitle) { - ofxLabel* label = static_cast(guiGroup.getControl(0)); - ofParameter* parameter = static_cast*>(&label->getParameter()); - return parameter->get(); - } else { - return ""; - } + return guiGroup.getName(); } string ofxRadioList::getItemName(int index) { - if (bHasTitle) { - index += 1; - } - if (index >= guiGroup.getNumControls()) { return ""; } @@ -212,11 +167,7 @@ string ofxRadioList::getItemName(int index) int ofxRadioList::size() { - if (bHasTitle) { - return guiGroup.getNumControls() - 1; - } else { - return guiGroup.getNumControls(); - } + return guiGroup.getNumControls(); } void ofxRadioList::onToggleClicked(bool &toggleValue) @@ -226,27 +177,11 @@ void ofxRadioList::onToggleClicked(bool &toggleValue) // Search for the actual toggle triggering the event int i; for (i = 0; i < guiGroup.getNumControls(); i++) { - if (bHasTitle && i == 0) { - continue; - } ofxToggle* toggle = static_cast(guiGroup.getControl(i)); ofParameter* paramPtr = static_cast*>(&toggle->getParameter()); if (&(paramPtr->get()) == &toggleValue) { - /* - toggle->removeListener(this, &ofxRadioList::onToggleClicked); - *toggle = true; // Select the specific radio button - toggle->addListener(this, &ofxRadioList::onToggleClicked); - string name = toggle->getName(); - ofNotifyEvent(radioSelectedEvent, name, this); - //cout << toggle->getName() << endl; // debug - */ - if (bHasTitle) { - selectItem(i - 1); - } else { - selectItem(i); - } - + selectItem(i); break; } } diff --git a/src/ui/ofxRadioList.h b/src/ui/ofxRadioList.h index df960bc..2736352 100644 --- a/src/ui/ofxRadioList.h +++ b/src/ui/ofxRadioList.h @@ -40,7 +40,6 @@ private: vector storedLabels; string storedTitle; ofxGuiGroup guiGroup; - bool bHasTitle; int storedSelectedItem; void onToggleClicked(bool &toggleValue); From 39c5be41e0526ef50b45d2742f701c4f48226c00 Mon Sep 17 00:00:00 2001 From: Krisjanis Rijnieks Date: Sun, 7 Sep 2014 15:14:12 +0200 Subject: [PATCH 13/73] Position image source selector --- src/ofxSourcesEditor.cpp | 1 + src/ofxSourcesEditor.h | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/ofxSourcesEditor.cpp b/src/ofxSourcesEditor.cpp index 0d5e2f5..5b5603c 100644 --- a/src/ofxSourcesEditor.cpp +++ b/src/ofxSourcesEditor.cpp @@ -43,6 +43,7 @@ void ofxSourcesEditor::setup(ofEventArgs& args) } gui->setup("Images", vnames); + gui->setPosition(20, 20); ofAddListener(gui->radioSelectedEvent, this, &ofxSourcesEditor::guiEvent); } diff --git a/src/ofxSourcesEditor.h b/src/ofxSourcesEditor.h index cab567e..33aad38 100644 --- a/src/ofxSourcesEditor.h +++ b/src/ofxSourcesEditor.h @@ -35,9 +35,6 @@ private: void guiEvent(string &imageName); vector images; vector imageNames; - //ofxPanel imgSrcPanel; - - //void onSourceSelect(bool& value); }; #endif \ No newline at end of file From 921b2e20da6382090fdec0c62551ea1fdb92a495 Mon Sep 17 00:00:00 2001 From: Felix Dubrownik Date: Sun, 7 Sep 2014 20:38:39 +0200 Subject: [PATCH 14/73] Renamed the files --- src/{ofxBaseJoint.cpp => BaseJoint.cpp} | 0 src/{ofxBaseJoint.h => BaseJoint.h} | 0 src/{ofxBaseSurface.cpp => BaseSurface.cpp} | 0 src/{ofxBaseSurface.h => BaseSurface.h} | 0 src/{ofxCircleJoint.cpp => CircleJoint.cpp} | 0 src/{ofxCircleJoint.h => CircleJoint.h} | 0 src/{ofxEditorType.h => EditorType.h} | 0 src/{ofxGuiMode.h => GuiMode.h} | 0 src/{ofxProjectionEditor.cpp => ProjectionEditor.cpp} | 0 src/{ofxProjectionEditor.h => ProjectionEditor.h} | 0 src/{ofxQuadSurface.cpp => QuadSurface.cpp} | 0 src/{ofxQuadSurface.h => QuadSurface.h} | 0 src/{ofxSourcesEditor.cpp => SourcesEditor.cpp} | 0 src/{ofxSourcesEditor.h => SourcesEditor.h} | 0 src/{ofxSurfaceManager.cpp => SurfaceManager.cpp} | 0 src/{ofxSurfaceManager.h => SurfaceManager.h} | 0 src/{ofxSurfaceManagerGui.cpp => SurfaceManagerGui.cpp} | 0 src/{ofxSurfaceManagerGui.h => SurfaceManagerGui.h} | 0 src/{ofxSurfaceType.h => SurfaceType.h} | 0 src/{ofxTextureEditor.cpp => TextureEditor.cpp} | 0 src/{ofxTextureEditor.h => TextureEditor.h} | 0 src/{ofxTriangleSurface.cpp => TriangleSurface.cpp} | 0 src/{ofxTriangleSurface.h => TriangleSurface.h} | 0 23 files changed, 0 insertions(+), 0 deletions(-) rename src/{ofxBaseJoint.cpp => BaseJoint.cpp} (100%) rename src/{ofxBaseJoint.h => BaseJoint.h} (100%) rename src/{ofxBaseSurface.cpp => BaseSurface.cpp} (100%) rename src/{ofxBaseSurface.h => BaseSurface.h} (100%) rename src/{ofxCircleJoint.cpp => CircleJoint.cpp} (100%) rename src/{ofxCircleJoint.h => CircleJoint.h} (100%) rename src/{ofxEditorType.h => EditorType.h} (100%) rename src/{ofxGuiMode.h => GuiMode.h} (100%) rename src/{ofxProjectionEditor.cpp => ProjectionEditor.cpp} (100%) rename src/{ofxProjectionEditor.h => ProjectionEditor.h} (100%) rename src/{ofxQuadSurface.cpp => QuadSurface.cpp} (100%) rename src/{ofxQuadSurface.h => QuadSurface.h} (100%) rename src/{ofxSourcesEditor.cpp => SourcesEditor.cpp} (100%) rename src/{ofxSourcesEditor.h => SourcesEditor.h} (100%) rename src/{ofxSurfaceManager.cpp => SurfaceManager.cpp} (100%) rename src/{ofxSurfaceManager.h => SurfaceManager.h} (100%) rename src/{ofxSurfaceManagerGui.cpp => SurfaceManagerGui.cpp} (100%) rename src/{ofxSurfaceManagerGui.h => SurfaceManagerGui.h} (100%) rename src/{ofxSurfaceType.h => SurfaceType.h} (100%) rename src/{ofxTextureEditor.cpp => TextureEditor.cpp} (100%) rename src/{ofxTextureEditor.h => TextureEditor.h} (100%) rename src/{ofxTriangleSurface.cpp => TriangleSurface.cpp} (100%) rename src/{ofxTriangleSurface.h => TriangleSurface.h} (100%) diff --git a/src/ofxBaseJoint.cpp b/src/BaseJoint.cpp similarity index 100% rename from src/ofxBaseJoint.cpp rename to src/BaseJoint.cpp diff --git a/src/ofxBaseJoint.h b/src/BaseJoint.h similarity index 100% rename from src/ofxBaseJoint.h rename to src/BaseJoint.h diff --git a/src/ofxBaseSurface.cpp b/src/BaseSurface.cpp similarity index 100% rename from src/ofxBaseSurface.cpp rename to src/BaseSurface.cpp diff --git a/src/ofxBaseSurface.h b/src/BaseSurface.h similarity index 100% rename from src/ofxBaseSurface.h rename to src/BaseSurface.h diff --git a/src/ofxCircleJoint.cpp b/src/CircleJoint.cpp similarity index 100% rename from src/ofxCircleJoint.cpp rename to src/CircleJoint.cpp diff --git a/src/ofxCircleJoint.h b/src/CircleJoint.h similarity index 100% rename from src/ofxCircleJoint.h rename to src/CircleJoint.h diff --git a/src/ofxEditorType.h b/src/EditorType.h similarity index 100% rename from src/ofxEditorType.h rename to src/EditorType.h diff --git a/src/ofxGuiMode.h b/src/GuiMode.h similarity index 100% rename from src/ofxGuiMode.h rename to src/GuiMode.h diff --git a/src/ofxProjectionEditor.cpp b/src/ProjectionEditor.cpp similarity index 100% rename from src/ofxProjectionEditor.cpp rename to src/ProjectionEditor.cpp diff --git a/src/ofxProjectionEditor.h b/src/ProjectionEditor.h similarity index 100% rename from src/ofxProjectionEditor.h rename to src/ProjectionEditor.h diff --git a/src/ofxQuadSurface.cpp b/src/QuadSurface.cpp similarity index 100% rename from src/ofxQuadSurface.cpp rename to src/QuadSurface.cpp diff --git a/src/ofxQuadSurface.h b/src/QuadSurface.h similarity index 100% rename from src/ofxQuadSurface.h rename to src/QuadSurface.h diff --git a/src/ofxSourcesEditor.cpp b/src/SourcesEditor.cpp similarity index 100% rename from src/ofxSourcesEditor.cpp rename to src/SourcesEditor.cpp diff --git a/src/ofxSourcesEditor.h b/src/SourcesEditor.h similarity index 100% rename from src/ofxSourcesEditor.h rename to src/SourcesEditor.h diff --git a/src/ofxSurfaceManager.cpp b/src/SurfaceManager.cpp similarity index 100% rename from src/ofxSurfaceManager.cpp rename to src/SurfaceManager.cpp diff --git a/src/ofxSurfaceManager.h b/src/SurfaceManager.h similarity index 100% rename from src/ofxSurfaceManager.h rename to src/SurfaceManager.h diff --git a/src/ofxSurfaceManagerGui.cpp b/src/SurfaceManagerGui.cpp similarity index 100% rename from src/ofxSurfaceManagerGui.cpp rename to src/SurfaceManagerGui.cpp diff --git a/src/ofxSurfaceManagerGui.h b/src/SurfaceManagerGui.h similarity index 100% rename from src/ofxSurfaceManagerGui.h rename to src/SurfaceManagerGui.h diff --git a/src/ofxSurfaceType.h b/src/SurfaceType.h similarity index 100% rename from src/ofxSurfaceType.h rename to src/SurfaceType.h diff --git a/src/ofxTextureEditor.cpp b/src/TextureEditor.cpp similarity index 100% rename from src/ofxTextureEditor.cpp rename to src/TextureEditor.cpp diff --git a/src/ofxTextureEditor.h b/src/TextureEditor.h similarity index 100% rename from src/ofxTextureEditor.h rename to src/TextureEditor.h diff --git a/src/ofxTriangleSurface.cpp b/src/TriangleSurface.cpp similarity index 100% rename from src/ofxTriangleSurface.cpp rename to src/TriangleSurface.cpp diff --git a/src/ofxTriangleSurface.h b/src/TriangleSurface.h similarity index 100% rename from src/ofxTriangleSurface.h rename to src/TriangleSurface.h From 4605d05756cf6518c52574eacbe905f19a8b38a5 Mon Sep 17 00:00:00 2001 From: Felix Dubrownik Date: Sun, 7 Sep 2014 20:39:21 +0200 Subject: [PATCH 15/73] Renamed RadioList --- src/ui/{ofxRadioList.cpp => RadioList.cpp} | 0 src/ui/{ofxRadioList.h => RadioList.h} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/ui/{ofxRadioList.cpp => RadioList.cpp} (100%) rename src/ui/{ofxRadioList.h => RadioList.h} (100%) diff --git a/src/ui/ofxRadioList.cpp b/src/ui/RadioList.cpp similarity index 100% rename from src/ui/ofxRadioList.cpp rename to src/ui/RadioList.cpp diff --git a/src/ui/ofxRadioList.h b/src/ui/RadioList.h similarity index 100% rename from src/ui/ofxRadioList.h rename to src/ui/RadioList.h From 66283175f14ff4661b0bb59300051955c9686929 Mon Sep 17 00:00:00 2001 From: Felix Dubrownik Date: Sun, 7 Sep 2014 21:48:09 +0200 Subject: [PATCH 16/73] Refactoring: Renaming Classes, introducing namespace --- example/src/ofApp.cpp | 14 +++--- example/src/ofApp.h | 11 ++--- src/BaseJoint.cpp | 49 +++++++++++--------- src/BaseJoint.h | 90 +++++++++++++++++++------------------ src/BaseSurface.cpp | 21 +++++---- src/BaseSurface.h | 11 ++--- src/CircleJoint.cpp | 19 +++++--- src/CircleJoint.h | 15 ++++--- src/EditorType.h | 9 ++-- src/GuiMode.h | 10 ++--- src/ProjectionEditor.cpp | 87 +++++++++++++++++++----------------- src/ProjectionEditor.h | 25 ++++++----- src/QuadSurface.cpp | 48 +++++++++++--------- src/QuadSurface.h | 17 +++---- src/SourcesEditor.cpp | 44 +++++++++--------- src/SourcesEditor.h | 23 +++++----- src/SurfaceManager.cpp | 94 ++++++++++++++++++++------------------- src/SurfaceManager.h | 35 ++++++++------- src/SurfaceManagerGui.cpp | 90 +++++++++++++++++++------------------ src/SurfaceManagerGui.h | 34 +++++++------- src/SurfaceType.h | 9 ++-- src/TextureEditor.cpp | 68 +++++++++++++++------------- src/TextureEditor.h | 27 ++++++----- src/TriangleSurface.cpp | 46 ++++++++++--------- src/TriangleSurface.h | 18 ++++---- src/ofxPiMapper.h | 9 ++-- src/ui/RadioList.cpp | 70 +++++++++++++++-------------- src/ui/RadioList.h | 18 +++++--- 28 files changed, 536 insertions(+), 475 deletions(-) diff --git a/example/src/ofApp.cpp b/example/src/ofApp.cpp index 2cab8a6..18e5777 100755 --- a/example/src/ofApp.cpp +++ b/example/src/ofApp.cpp @@ -85,10 +85,10 @@ void ofApp::keyPressed(int key) cout << "Key pressed: " << static_cast(key) << endl; switch (key) { - case '1': gui.setMode(ofxGuiMode::NONE); break; - case '2': gui.setMode(ofxGuiMode::TEXTURE_MAPPING); break; - case '3': gui.setMode(ofxGuiMode::PROJECTION_MAPPING); break; - case '4': gui.setMode(ofxGuiMode::SOURCE_SELECTION); break; + case '1': gui.setMode(ofx::piMapper::GuiMode::NONE); break; + case '2': gui.setMode(ofx::piMapper::GuiMode::TEXTURE_MAPPING); break; + case '3': gui.setMode(ofx::piMapper::GuiMode::PROJECTION_MAPPING); break; + case '4': gui.setMode(ofx::piMapper::GuiMode::SOURCE_SELECTION); break; case 'i': bShowInfo = !bShowInfo; break; case 'r': addRandomSurface(); break; case 'q': addQuadSurface(); break; @@ -103,7 +103,7 @@ void ofApp::keyPressed(int key) void ofApp::addRandomSurface() { - int surfaceType = ofxSurfaceType::TRIANGLE_SURFACE; + int surfaceType = ofx::piMapper::SurfaceType::TRIANGLE_SURFACE; vector vertices; vertices.push_back( ofVec2f( ofRandomWidth(), ofRandomHeight() ) ); vertices.push_back( ofVec2f( ofRandomWidth(), ofRandomHeight() ) ); @@ -120,7 +120,7 @@ void ofApp::addRandomSurface() void ofApp::addQuadSurface() { - int surfaceType = ofxSurfaceType::QUAD_SURFACE; + int surfaceType = ofx::piMapper::SurfaceType::QUAD_SURFACE; vector vertices; int border = 50; @@ -143,7 +143,7 @@ void ofApp::addQuadSurface() void ofApp::addSurface() { - int surfaceType = ofxSurfaceType::TRIANGLE_SURFACE; + int surfaceType = ofx::piMapper::SurfaceType::TRIANGLE_SURFACE; vector vertices; vertices.push_back( ofVec2f( (float)ofGetWidth()/2.0f, 0.0f ) ); vertices.push_back( ofVec2f( (float)ofGetWidth(), (float)ofGetHeight() ) ); diff --git a/example/src/ofApp.h b/example/src/ofApp.h index 05d7a8f..10c1c99 100755 --- a/example/src/ofApp.h +++ b/example/src/ofApp.h @@ -1,5 +1,4 @@ -#ifndef H_OF_APP -#define H_OF_APP +#pragma once #include "ofMain.h" #include "ofxPiMapper.h" @@ -20,12 +19,10 @@ public: void setFboAsTexture(); ofImage image; - ofxSurfaceManager surfaceManager; - ofxSurfaceManagerGui gui; + ofx::piMapper::SurfaceManager surfaceManager; + ofx::piMapper::SurfaceManagerGui gui; bool bShowInfo; ofFbo* fbo; vector rects; vector rectSpeeds; -}; - -#endif \ No newline at end of file +}; \ No newline at end of file diff --git a/src/BaseJoint.cpp b/src/BaseJoint.cpp index 22d147d..19c490a 100644 --- a/src/BaseJoint.cpp +++ b/src/BaseJoint.cpp @@ -1,30 +1,33 @@ -#include "ofxBaseJoint.h" +#include "BaseJoint.h" -ofxBaseJoint::ofxBaseJoint() +namespace ofx{ +namespace piMapper{ + +BaseJoint::BaseJoint() { setDefaultColors(); setDefaultProperties(); registerMouseEvents(); } -ofxBaseJoint::~ofxBaseJoint() +BaseJoint::~BaseJoint() { unregisterMouseEvents(); } -void ofxBaseJoint::registerMouseEvents() +void BaseJoint::registerMouseEvents() { - ofAddListener(ofEvents().mousePressed, this, &ofxBaseJoint::mousePressed); - ofAddListener(ofEvents().mouseDragged, this, &ofxBaseJoint::mouseDragged); + ofAddListener(ofEvents().mousePressed, this, &BaseJoint::mousePressed); + ofAddListener(ofEvents().mouseDragged, this, &BaseJoint::mouseDragged); } -void ofxBaseJoint::unregisterMouseEvents() +void BaseJoint::unregisterMouseEvents() { - ofRemoveListener(ofEvents().mousePressed, this, &ofxBaseJoint::mousePressed); - ofRemoveListener(ofEvents().mouseDragged, this, &ofxBaseJoint::mouseDragged); + ofRemoveListener(ofEvents().mousePressed, this, &BaseJoint::mousePressed); + ofRemoveListener(ofEvents().mouseDragged, this, &BaseJoint::mouseDragged); } -void ofxBaseJoint::mousePressed(ofMouseEventArgs& args) +void BaseJoint::mousePressed(ofMouseEventArgs& args) { if ( hitTest(ofVec2f(args.x, args.y)) ) { //selected = true; @@ -33,53 +36,53 @@ void ofxBaseJoint::mousePressed(ofMouseEventArgs& args) } } -void ofxBaseJoint::mouseReleased(int x, int y, int button) +void BaseJoint::mouseReleased(int x, int y, int button) { stopDrag(); } -void ofxBaseJoint::mouseDragged(ofMouseEventArgs& args) +void BaseJoint::mouseDragged(ofMouseEventArgs& args) { if ( !bDrag ) return; position = ofVec2f(args.x, args.y) + clickDistance; } -void ofxBaseJoint::startDrag() +void BaseJoint::startDrag() { bDrag = true; } -void ofxBaseJoint::stopDrag() +void BaseJoint::stopDrag() { bDrag = false; } -void ofxBaseJoint::select() +void BaseJoint::select() { selected = true; } -void ofxBaseJoint::unselect() +void BaseJoint::unselect() { selected = false; } -void ofxBaseJoint::setClickDistance(ofVec2f newClickDistance) +void BaseJoint::setClickDistance(ofVec2f newClickDistance) { clickDistance = newClickDistance; } -bool ofxBaseJoint::isDragged() +bool BaseJoint::isDragged() { return bDrag; } -bool ofxBaseJoint::isSelected() +bool BaseJoint::isSelected() { return selected; } -void ofxBaseJoint::setDefaultColors() +void BaseJoint::setDefaultColors() { fillColor = ofColor(0, 255, 255, 0); strokeColor = ofColor(255, 255, 255); @@ -87,7 +90,7 @@ void ofxBaseJoint::setDefaultColors() strokeColorSelected = ofColor(255, 0, 0); } -void ofxBaseJoint::setDefaultProperties() +void BaseJoint::setDefaultProperties() { enabled = true; visible = true; @@ -96,4 +99,6 @@ void ofxBaseJoint::setDefaultProperties() bDrag = false; selected = false; strokeWidth = 1.5f; -} \ No newline at end of file +} + +}} \ No newline at end of file diff --git a/src/BaseJoint.h b/src/BaseJoint.h index d3decb7..5e5824d 100644 --- a/src/BaseJoint.h +++ b/src/BaseJoint.h @@ -1,48 +1,50 @@ -#ifndef H_OFX_BASE_JOINT -#define H_OFX_BASE_JOINT +#pragma once #include "ofMain.h" -class ofxBaseJoint { -public: - ofxBaseJoint(); - ~ofxBaseJoint(); +namespace ofx{ +namespace piMapper{ - void registerMouseEvents(); - void unregisterMouseEvents(); - - ofVec2f position; - bool enabled; - bool visible; - bool selected; - - void mousePressed(ofMouseEventArgs& args); - void mouseReleased(int x, int y, int button); - void mouseDragged(ofMouseEventArgs& args); - void startDrag(); - void stopDrag(); - void select(); - void unselect(); - void setClickDistance(ofVec2f newClickDistance); - bool isDragged(); - bool isSelected(); - - virtual void update(){}; - virtual void draw(){}; - virtual bool hitTest(ofVec2f position){}; - -protected: - ofColor fillColor; - ofColor strokeColor; - ofColor fillColorSelected; - ofColor strokeColorSelected; - float strokeWidth; - ofVec2f clickDistance; // How far from the center of the joint the user has clicked? - bool bDrag; - -private: - void setDefaultColors(); - void setDefaultProperties(); -}; - -#endif + class BaseJoint { + public: + BaseJoint(); + ~BaseJoint(); + + void registerMouseEvents(); + void unregisterMouseEvents(); + + ofVec2f position; + bool enabled; + bool visible; + bool selected; + + void mousePressed(ofMouseEventArgs& args); + void mouseReleased(int x, int y, int button); + void mouseDragged(ofMouseEventArgs& args); + void startDrag(); + void stopDrag(); + void select(); + void unselect(); + void setClickDistance(ofVec2f newClickDistance); + bool isDragged(); + bool isSelected(); + + virtual void update(){}; + virtual void draw(){}; + virtual bool hitTest(ofVec2f position){}; + + protected: + ofColor fillColor; + ofColor strokeColor; + ofColor fillColorSelected; + ofColor strokeColorSelected; + float strokeWidth; + ofVec2f clickDistance; // How far from the center of the joint the user has clicked? + bool bDrag; + + private: + void setDefaultColors(); + void setDefaultProperties(); + }; +} +} diff --git a/src/BaseSurface.cpp b/src/BaseSurface.cpp index 67db7ef..7350b29 100644 --- a/src/BaseSurface.cpp +++ b/src/BaseSurface.cpp @@ -1,12 +1,15 @@ -#include "ofxBaseSurface.h" +#include "BaseSurface.h" -ofxBaseSurface::ofxBaseSurface() +namespace ofx{ +namespace piMapper{ + +BaseSurface::BaseSurface() { ofEnableNormalizedTexCoords(); createDefaultTexture(); } -void ofxBaseSurface::createDefaultTexture() +void BaseSurface::createDefaultTexture() { ofPixels pixels; pixels.allocate(500, 500, 1); @@ -38,7 +41,7 @@ void ofxBaseSurface::createDefaultTexture() texture = &defaultTexture; } -void ofxBaseSurface::drawTexture(ofVec2f position) +void BaseSurface::drawTexture(ofVec2f position) { ofMesh texMesh; texMesh.addVertex(position); @@ -56,17 +59,19 @@ void ofxBaseSurface::drawTexture(ofVec2f position) texture->unbind(); } -void ofxBaseSurface::setTexture(ofTexture *texturePtr) +void BaseSurface::setTexture(ofTexture *texturePtr) { texture = texturePtr; } -ofTexture* ofxBaseSurface::getTexture() +ofTexture* BaseSurface::getTexture() { return texture; } -ofTexture* ofxBaseSurface::getDefaultTexture() +ofTexture* BaseSurface::getDefaultTexture() { return &defaultTexture; -} \ No newline at end of file +} + +}} \ No newline at end of file diff --git a/src/BaseSurface.h b/src/BaseSurface.h index 9cd98b6..e8e5b8f 100644 --- a/src/BaseSurface.h +++ b/src/BaseSurface.h @@ -1,15 +1,16 @@ -#ifndef H_OFX_BASE_SURFACE -#define H_OFX_BASE_SURFACE +#pragma once #include "ofMain.h" #include using namespace std; -class ofxBaseSurface +namespace ofx{ + namespace piMapper{ +class BaseSurface { public: - ofxBaseSurface(); + BaseSurface(); virtual void setup(){}; virtual void draw(){}; virtual void setVertex(int index, ofVec2f p){}; @@ -37,4 +38,4 @@ protected: void createDefaultTexture(); }; -#endif \ No newline at end of file + }} \ No newline at end of file diff --git a/src/CircleJoint.cpp b/src/CircleJoint.cpp index 4abbc5c..938659f 100644 --- a/src/CircleJoint.cpp +++ b/src/CircleJoint.cpp @@ -1,16 +1,19 @@ -#include "ofxCircleJoint.h" +#include "CircleJoint.h" -ofxCircleJoint::ofxCircleJoint() +namespace ofx{ +namespace piMapper{ + +CircleJoint::CircleJoint() { setDefaultProperties(); } -void ofxCircleJoint::update() +void CircleJoint::update() { if (!enabled) return; } -void ofxCircleJoint::draw() +void CircleJoint::draw() { if (!visible) return; if (!enabled) return; @@ -38,14 +41,16 @@ void ofxCircleJoint::draw() ofPopStyle(); } -void ofxCircleJoint::setDefaultProperties() +void CircleJoint::setDefaultProperties() { radius = 10.0f; } -bool ofxCircleJoint::hitTest(ofVec2f pos) +bool CircleJoint::hitTest(ofVec2f pos) { float distance = position.distance(pos); if ( distance < radius ) return true; else return false; -} \ No newline at end of file +} + + }} \ No newline at end of file diff --git a/src/CircleJoint.h b/src/CircleJoint.h index f3016e9..812bc3c 100644 --- a/src/CircleJoint.h +++ b/src/CircleJoint.h @@ -1,13 +1,15 @@ -#ifndef H_OFX_CIRCLE_JOINT -#define H_OFX_CIRCLE_JOINT +#pragma once #include "ofMain.h" -#include "ofxBaseJoint.h" +#include "BaseJoint.h" -class ofxCircleJoint : public ofxBaseJoint + +namespace ofx{ +namespace piMapper{ +class CircleJoint : public BaseJoint { public: - ofxCircleJoint(); + CircleJoint(); void update(); void draw(); @@ -18,5 +20,4 @@ private: void setDefaultProperties(); }; - -#endif \ No newline at end of file +}} \ No newline at end of file diff --git a/src/EditorType.h b/src/EditorType.h index cc0c1c8..af5650b 100644 --- a/src/EditorType.h +++ b/src/EditorType.h @@ -1,7 +1,8 @@ -#ifndef H_OFX_EDITOR_TYPE -#define H_OFX_EDITOR_TYPE +#pragma once -struct ofxEditorType +namespace ofx{ + namespace piMapper{ +struct EditorType { enum { TEXTURE, @@ -9,4 +10,4 @@ struct ofxEditorType }; }; -#endif \ No newline at end of file +}} \ No newline at end of file diff --git a/src/GuiMode.h b/src/GuiMode.h index c60fe71..4c65a74 100644 --- a/src/GuiMode.h +++ b/src/GuiMode.h @@ -1,7 +1,8 @@ -#ifndef H_OFX_GUI_MODE -#define H_OFX_GUI_MODE +#pragma once -struct ofxGuiMode +namespace ofx{ + namespace piMapper{ +struct GuiMode { enum { NONE, @@ -10,5 +11,4 @@ struct ofxGuiMode SOURCE_SELECTION }; }; - -#endif \ No newline at end of file + }} \ No newline at end of file diff --git a/src/ProjectionEditor.cpp b/src/ProjectionEditor.cpp index 7560ba8..12adcca 100644 --- a/src/ProjectionEditor.cpp +++ b/src/ProjectionEditor.cpp @@ -1,6 +1,8 @@ -#include "ofxProjectionEditor.h" +#include "ProjectionEditor.h" -ofxProjectionEditor::ofxProjectionEditor() +namespace ofx{ + namespace piMapper{ +ProjectionEditor::ProjectionEditor() { surfaceManager = NULL; bShiftKeyDown = false; @@ -8,62 +10,62 @@ ofxProjectionEditor::ofxProjectionEditor() enable(); } -ofxProjectionEditor::~ofxProjectionEditor() +ProjectionEditor::~ProjectionEditor() { clearJoints(); surfaceManager = NULL; disable(); } -void ofxProjectionEditor::registerAppEvents() +void ProjectionEditor::registerAppEvents() { - ofAddListener(ofEvents().update, this, &ofxProjectionEditor::update); - ofAddListener(ofEvents().messageEvent, this, &ofxProjectionEditor::gotMessage); + ofAddListener(ofEvents().update, this, &ProjectionEditor::update); + ofAddListener(ofEvents().messageEvent, this, &ProjectionEditor::gotMessage); } -void ofxProjectionEditor::unregisterAppEvents() +void ProjectionEditor::unregisterAppEvents() { - ofRemoveListener(ofEvents().update, this, &ofxProjectionEditor::update); - ofRemoveListener(ofEvents().messageEvent, this, &ofxProjectionEditor::gotMessage); + ofRemoveListener(ofEvents().update, this, &ProjectionEditor::update); + ofRemoveListener(ofEvents().messageEvent, this, &ProjectionEditor::gotMessage); } -void ofxProjectionEditor::registerMouseEvents() +void ProjectionEditor::registerMouseEvents() { - ofAddListener(ofEvents().mouseDragged, this, &ofxProjectionEditor::mouseDragged); + ofAddListener(ofEvents().mouseDragged, this, &ProjectionEditor::mouseDragged); } -void ofxProjectionEditor::unregisterMouseEvents() +void ProjectionEditor::unregisterMouseEvents() { - ofRemoveListener(ofEvents().mouseDragged, this, &ofxProjectionEditor::mouseDragged); + ofRemoveListener(ofEvents().mouseDragged, this, &ProjectionEditor::mouseDragged); } -void ofxProjectionEditor::registerKeyEvents() +void ProjectionEditor::registerKeyEvents() { - ofAddListener(ofEvents().keyPressed, this, &ofxProjectionEditor::keyPressed); - ofAddListener(ofEvents().keyReleased, this, &ofxProjectionEditor::keyReleased); + ofAddListener(ofEvents().keyPressed, this, &ProjectionEditor::keyPressed); + ofAddListener(ofEvents().keyReleased, this, &ProjectionEditor::keyReleased); } -void ofxProjectionEditor::unregisterKeyEvents() +void ProjectionEditor::unregisterKeyEvents() { - ofRemoveListener(ofEvents().keyPressed, this, &ofxProjectionEditor::keyPressed); - ofRemoveListener(ofEvents().keyReleased, this, &ofxProjectionEditor::keyReleased); + ofRemoveListener(ofEvents().keyPressed, this, &ProjectionEditor::keyPressed); + ofRemoveListener(ofEvents().keyReleased, this, &ProjectionEditor::keyReleased); } -void ofxProjectionEditor::enable() +void ProjectionEditor::enable() { registerAppEvents(); registerMouseEvents(); registerKeyEvents(); } -void ofxProjectionEditor::disable() +void ProjectionEditor::disable() { unregisterAppEvents(); unregisterMouseEvents(); unregisterKeyEvents(); } -void ofxProjectionEditor::update(ofEventArgs &args) +void ProjectionEditor::update(ofEventArgs &args) { // update surface if one of the joints is being dragged for ( int i=0; igetSelectedSurface() == NULL ) return; @@ -91,14 +93,14 @@ void ofxProjectionEditor::draw() drawJoints(); } -void ofxProjectionEditor::mouseDragged(ofMouseEventArgs &args) +void ProjectionEditor::mouseDragged(ofMouseEventArgs &args) { ofVec2f mousePosition = ofVec2f(args.x, args.y); // Collect all vertices of the projection surfaces vector allVertices; for ( int i=0; isize(); i++ ) { - ofxBaseSurface* surface = surfaceManager->getSurface(i); + BaseSurface* surface = surfaceManager->getSurface(i); if ( surface == surfaceManager->getSelectedSurface() ) { continue; // Don't add vertices of selected surface } @@ -125,7 +127,7 @@ void ofxProjectionEditor::mouseDragged(ofMouseEventArgs &args) } } -void ofxProjectionEditor::keyPressed(ofKeyEventArgs &args) +void ProjectionEditor::keyPressed(ofKeyEventArgs &args) { int key = args.key; float moveStep; @@ -142,7 +144,7 @@ void ofxProjectionEditor::keyPressed(ofKeyEventArgs &args) } } -void ofxProjectionEditor::keyReleased(ofKeyEventArgs &args) +void ProjectionEditor::keyReleased(ofKeyEventArgs &args) { int key = args.key; switch (key) { @@ -150,7 +152,7 @@ void ofxProjectionEditor::keyReleased(ofKeyEventArgs &args) } } -void ofxProjectionEditor::gotMessage(ofMessage& msg) +void ProjectionEditor::gotMessage(ofMessage& msg) { if (msg.message == "surfaceSelected") { // refresh gui @@ -159,12 +161,12 @@ void ofxProjectionEditor::gotMessage(ofMessage& msg) } } -void ofxProjectionEditor::setSurfaceManager(ofxSurfaceManager *newSurfaceManager) +void ProjectionEditor::setSurfaceManager(SurfaceManager *newSurfaceManager) { surfaceManager = newSurfaceManager; } -void ofxProjectionEditor::clearJoints() +void ProjectionEditor::clearJoints() { while ( joints.size() ) { delete joints.back(); @@ -172,7 +174,7 @@ void ofxProjectionEditor::clearJoints() } } -void ofxProjectionEditor::createJoints() +void ProjectionEditor::createJoints() { if ( surfaceManager == NULL ) return; clearJoints(); @@ -185,12 +187,12 @@ void ofxProjectionEditor::createJoints() vector& vertices = surfaceManager->getSelectedSurface()->getVertices(); for ( int i=0; iposition = ofVec2f(vertices[i].x, vertices[i].y); } } -void ofxProjectionEditor::updateJoints() +void ProjectionEditor::updateJoints() { vector& vertices = surfaceManager->getSelectedSurface()->getVertices(); for ( int i=0; iunselect(); } } -void ofxProjectionEditor::moveSelectedSurface(ofVec2f by) +void ProjectionEditor::moveSelectedSurface(ofVec2f by) { if ( surfaceManager == NULL ) return; if ( surfaceManager->getSelectedSurface() == NULL ) return; @@ -217,18 +219,18 @@ void ofxProjectionEditor::moveSelectedSurface(ofVec2f by) updateJoints(); } -void ofxProjectionEditor::stopDragJoints() +void ProjectionEditor::stopDragJoints() { for (int i=0; istopDrag(); } } -void ofxProjectionEditor::moveSelection(ofVec2f by) +void ProjectionEditor::moveSelection(ofVec2f by) { // check if joints selected bool bJointSelected = false; - ofxBaseJoint* selectedJoint; + BaseJoint* selectedJoint; for ( int i=0; iisSelected()) { bJointSelected = true; @@ -244,12 +246,12 @@ void ofxProjectionEditor::moveSelection(ofVec2f by) } } -void ofxProjectionEditor::setSnapDistance(float newSnapDistance) +void ProjectionEditor::setSnapDistance(float newSnapDistance) { fSnapDistance = newSnapDistance; } -ofxCircleJoint* ofxProjectionEditor::hitTestJoints(ofVec2f pos) +CircleJoint* ProjectionEditor::hitTestJoints(ofVec2f pos) { for ( int i=0; ihitTest(pos) ){ @@ -259,9 +261,10 @@ ofxCircleJoint* ofxProjectionEditor::hitTestJoints(ofVec2f pos) return NULL; } -void ofxProjectionEditor::drawJoints() +void ProjectionEditor::drawJoints() { for ( int i=0; idraw(); } -} \ No newline at end of file +} + }} \ No newline at end of file diff --git a/src/ProjectionEditor.h b/src/ProjectionEditor.h index c4b95ec..6a7a877 100755 --- a/src/ProjectionEditor.h +++ b/src/ProjectionEditor.h @@ -1,14 +1,15 @@ -#ifndef H_OFX_PROJECTION_EDITOR -#define H_OFX_PROJECTION_EDITOR +#pragma once -#include "ofxSurfaceManager.h" -#include "ofxCircleJoint.h" +#include "SurfaceManager.h" +#include "CircleJoint.h" -class ofxProjectionEditor +namespace ofx{ + namespace piMapper{ +class ProjectionEditor { public: - ofxProjectionEditor(); - ~ofxProjectionEditor(); + ProjectionEditor(); + ~ProjectionEditor(); void registerAppEvents(); void unregisterAppEvents(); @@ -26,7 +27,7 @@ public: void keyPressed(ofKeyEventArgs& args); void keyReleased(ofKeyEventArgs& args); void gotMessage(ofMessage& msg); - void setSurfaceManager(ofxSurfaceManager* newSurfaceManager); + void setSurfaceManager(SurfaceManager* newSurfaceManager); void clearJoints(); void createJoints(); void updateJoints(); @@ -36,15 +37,15 @@ public: void updateVertices(); void moveSelection(ofVec2f by); void setSnapDistance(float newSnapDistance); - ofxCircleJoint* hitTestJoints(ofVec2f pos); + CircleJoint* hitTestJoints(ofVec2f pos); private: - ofxSurfaceManager* surfaceManager; - vector joints; + SurfaceManager* surfaceManager; + vector joints; bool bShiftKeyDown; float fSnapDistance; void drawJoints(); }; -#endif \ No newline at end of file + }} \ No newline at end of file diff --git a/src/QuadSurface.cpp b/src/QuadSurface.cpp index 91f296b..e71f45c 100644 --- a/src/QuadSurface.cpp +++ b/src/QuadSurface.cpp @@ -1,17 +1,19 @@ -#include "ofxQuadSurface.h" +#include "QuadSurface.h" -ofxQuadSurface::ofxQuadSurface() +namespace ofx{ + namespace piMapper{ +QuadSurface::QuadSurface() { - cout << "ofxQuadSurface constructor." << endl; + cout << "QuadSurface constructor." << endl; setup(); } -ofxQuadSurface::~ofxQuadSurface() +QuadSurface::~QuadSurface() { - cout << "ofxQuadSurface destructor." << endl; + cout << "QuadSurface destructor." << endl; } -void ofxQuadSurface::setup() +void QuadSurface::setup() { // Create 4 points for the 2 triangles ofVec2f p1 = ofVec2f(0, 0); @@ -28,7 +30,7 @@ void ofxQuadSurface::setup() setup( p1, p2, p3, p4, t1, t2, t3, t4, texture ); } -void ofxQuadSurface::setup( ofVec2f p1, ofVec2f p2, ofVec2f p3, ofVec2f p4, +void QuadSurface::setup( ofVec2f p1, ofVec2f p2, ofVec2f p3, ofVec2f p4, ofVec2f t1, ofVec2f t2, ofVec2f t3, ofVec2f t4, ofTexture* texturePtr ) { // Assign texture @@ -70,7 +72,7 @@ void ofxQuadSurface::setup( ofVec2f p1, ofVec2f p2, ofVec2f p3, ofVec2f p4, calculate4dTextureCoords(); } -void ofxQuadSurface::draw() +void QuadSurface::draw() { /*if(mesh.haveVertsChanged() || mesh.haveTexCoordsChanged()){ calculate4dTextureCoords(); @@ -84,7 +86,7 @@ void ofxQuadSurface::draw() texture->unbind(); } -void ofxQuadSurface::setVertex(int index, ofVec2f p) +void QuadSurface::setVertex(int index, ofVec2f p) { if ( index > 3 ) { ofLog() << "Vertex with this index does not exist: " << index << endl; @@ -95,7 +97,7 @@ void ofxQuadSurface::setVertex(int index, ofVec2f p) calculate4dTextureCoords(); } -void ofxQuadSurface::setTexCoord(int index, ofVec2f t) +void QuadSurface::setTexCoord(int index, ofVec2f t) { if ( index > 3 ) { ofLog() << "Texture coordinate with this index does not exist: " << index << endl; @@ -106,7 +108,7 @@ void ofxQuadSurface::setTexCoord(int index, ofVec2f t) calculate4dTextureCoords(); } -void ofxQuadSurface::moveBy(ofVec2f v) +void QuadSurface::moveBy(ofVec2f v) { vector& vertices = getVertices(); for (int i=0; i 3 ) { ofLog() << "Vertex with this index does not exist: " << index << endl; @@ -143,7 +145,7 @@ ofVec2f ofxQuadSurface::getVertex(int index) return ofVec2f(vert.x, vert.y); } -ofVec2f ofxQuadSurface::getTexCoord(int index) +ofVec2f QuadSurface::getTexCoord(int index) { if (index > 3) { throw std::runtime_error("Texture coordinate index out of bounds."); @@ -152,7 +154,7 @@ ofVec2f ofxQuadSurface::getTexCoord(int index) return mesh.getTexCoord(index); } -ofPolyline ofxQuadSurface::getHitArea() +ofPolyline QuadSurface::getHitArea() { ofPolyline line; line.addVertex( ofPoint( mesh.getVertex(0).x, mesh.getVertex(0).y ) ); @@ -164,7 +166,7 @@ ofPolyline ofxQuadSurface::getHitArea() return line; } -ofPolyline ofxQuadSurface::getTextureHitArea() +ofPolyline QuadSurface::getTextureHitArea() { ofPolyline line; vector& texCoords = mesh.getTexCoords(); @@ -177,19 +179,19 @@ ofPolyline ofxQuadSurface::getTextureHitArea() return line; } -vector& ofxQuadSurface::getVertices() +vector& QuadSurface::getVertices() { // return only joint vertices return mesh.getVertices(); } -vector& ofxQuadSurface::getTexCoords() +vector& QuadSurface::getTexCoords() { return mesh.getTexCoords(); } -void ofxQuadSurface::calculate4dTextureCoords() +void QuadSurface::calculate4dTextureCoords() { // Perspective Warping with OpenGL Fixed Pipeline and q coordinates // see: @@ -259,4 +261,6 @@ void ofxQuadSurface::calculate4dTextureCoords() quadTexCoordinates[13] = t3.y * q3; quadTexCoordinates[15] = q3; -} \ No newline at end of file +} + + }} \ No newline at end of file diff --git a/src/QuadSurface.h b/src/QuadSurface.h index 4c6cb9d..87469cb 100755 --- a/src/QuadSurface.h +++ b/src/QuadSurface.h @@ -1,15 +1,16 @@ -#ifndef H_OFX_QUAD_SURFACE -#define H_OFX_QUAD_SURFACE +#pragma once #include "ofMain.h" -#include "ofxBaseSurface.h" -#include "ofxSurfaceType.h" +#include "BaseSurface.h" +#include "SurfaceType.h" -class ofxQuadSurface : public ofxBaseSurface +namespace ofx{ + namespace piMapper{ +class QuadSurface : public BaseSurface { public: - ofxQuadSurface(); - ~ofxQuadSurface(); + QuadSurface(); + ~QuadSurface(); void setup(); @@ -37,4 +38,4 @@ private: GLfloat quadTexCoordinates[16]; }; -#endif \ No newline at end of file + }} \ No newline at end of file diff --git a/src/SourcesEditor.cpp b/src/SourcesEditor.cpp index 5b5603c..05c9a83 100644 --- a/src/SourcesEditor.cpp +++ b/src/SourcesEditor.cpp @@ -1,12 +1,14 @@ -#include "ofxSourcesEditor.h" +#include "SourcesEditor.h" -ofxSourcesEditor::ofxSourcesEditor() +namespace ofx{ + namespace piMapper{ +SourcesEditor::SourcesEditor() { defImgDir = DEFAULT_IMAGES_DIR; registerAppEvents(); } -ofxSourcesEditor::~ofxSourcesEditor() +SourcesEditor::~SourcesEditor() { unregisterAppEvents(); delete gui; @@ -16,19 +18,19 @@ ofxSourcesEditor::~ofxSourcesEditor() } } -void ofxSourcesEditor::registerAppEvents() +void SourcesEditor::registerAppEvents() { - ofAddListener(ofEvents().setup, this, &ofxSourcesEditor::setup); + ofAddListener(ofEvents().setup, this, &SourcesEditor::setup); } -void ofxSourcesEditor::unregisterAppEvents() +void SourcesEditor::unregisterAppEvents() { - ofRemoveListener(ofEvents().setup, this, &ofxSourcesEditor::setup); + ofRemoveListener(ofEvents().setup, this, &SourcesEditor::setup); } -void ofxSourcesEditor::setup(ofEventArgs& args) +void SourcesEditor::setup(ofEventArgs& args) { - gui = new ofxRadioList(); + gui = new RadioList(); // read directory contents ofDirectory imgDir; @@ -44,10 +46,10 @@ void ofxSourcesEditor::setup(ofEventArgs& args) gui->setup("Images", vnames); gui->setPosition(20, 20); - ofAddListener(gui->radioSelectedEvent, this, &ofxSourcesEditor::guiEvent); + ofAddListener(gui->radioSelectedEvent, this, &SourcesEditor::guiEvent); } -void ofxSourcesEditor::draw() +void SourcesEditor::draw() { // Don't draw if there is no source selected if ( surfaceManager->getSelectedSurface() == NULL ) { @@ -57,7 +59,7 @@ void ofxSourcesEditor::draw() gui->draw(); } -void ofxSourcesEditor::loadImage( string name, string path ) +void SourcesEditor::loadImage( string name, string path ) { images.push_back(new ofImage()); images.back()->loadImage(path); @@ -67,12 +69,12 @@ void ofxSourcesEditor::loadImage( string name, string path ) ofSendMessage("imageLoaded"); } -void ofxSourcesEditor::disable() +void SourcesEditor::disable() { gui->disable(); } -void ofxSourcesEditor::enable() +void SourcesEditor::enable() { // Don't enable if there is no surface selected if ( surfaceManager->getSelectedSurface() == NULL ) { @@ -83,12 +85,12 @@ void ofxSourcesEditor::enable() gui->enable(); } -void ofxSourcesEditor::setSurfaceManager(ofxSurfaceManager *newSurfaceManager) +void SourcesEditor::setSurfaceManager(SurfaceManager *newSurfaceManager) { surfaceManager = newSurfaceManager; } -void ofxSourcesEditor::selectImageSourceRadioButton(string name) +void SourcesEditor::selectImageSourceRadioButton(string name) { if (name == "none") { gui->unselectAll(); @@ -104,12 +106,12 @@ void ofxSourcesEditor::selectImageSourceRadioButton(string name) } } -int ofxSourcesEditor::getLoadedTexCount() +int SourcesEditor::getLoadedTexCount() { return images.size(); } -ofTexture* ofxSourcesEditor::getTexture(int index) +ofTexture* SourcesEditor::getTexture(int index) { if (index >= images.size()){ throw std::runtime_error("Texture index out of bounds."); @@ -118,7 +120,7 @@ ofTexture* ofxSourcesEditor::getTexture(int index) return &images[index]->getTextureReference(); } -void ofxSourcesEditor::guiEvent(string &imageName) +void SourcesEditor::guiEvent(string &imageName) { string name = imageName; @@ -132,4 +134,6 @@ void ofxSourcesEditor::guiEvent(string &imageName) ofTexture* texture = surfaceManager->loadImageSource(name, ss.str()); surfaceManager->getSelectedSurface()->setTexture(texture); surfaceManager->manageMemory(); -} \ No newline at end of file +} + + }} \ No newline at end of file diff --git a/src/SourcesEditor.h b/src/SourcesEditor.h index 33aad38..8311d1f 100644 --- a/src/SourcesEditor.h +++ b/src/SourcesEditor.h @@ -1,18 +1,19 @@ -#ifndef H_OFX_SOURCES_EDITOR -#define H_OFX_SOURCES_EDITOR +#pragma once #include "ofGraphics.h" #include "ofEvents.h" -#include "ofxSurfaceManager.h" -#include "ofxRadioList.h" +#include "SurfaceManager.h" +#include "RadioList.h" #define DEFAULT_IMAGES_DIR "sources/images/"; -class ofxSourcesEditor +namespace ofx{ + namespace piMapper{ +class SourcesEditor { public: - ofxSourcesEditor(); - ~ofxSourcesEditor(); + SourcesEditor(); + ~SourcesEditor(); void registerAppEvents(); void unregisterAppEvents(); @@ -22,19 +23,19 @@ public: void loadImage( string name, string path ); void disable(); void enable(); - void setSurfaceManager(ofxSurfaceManager* newSurfaceManager); + void setSurfaceManager(SurfaceManager* newSurfaceManager); void selectImageSourceRadioButton(string name); int getLoadedTexCount(); ofTexture* getTexture(int index); private: - ofxSurfaceManager* surfaceManager; - ofxRadioList* gui; + SurfaceManager* surfaceManager; + RadioList* gui; string defImgDir; void guiEvent(string &imageName); vector images; vector imageNames; }; -#endif \ No newline at end of file + }} \ No newline at end of file diff --git a/src/SurfaceManager.cpp b/src/SurfaceManager.cpp index e7df328..48ac560 100644 --- a/src/SurfaceManager.cpp +++ b/src/SurfaceManager.cpp @@ -1,43 +1,45 @@ -#include "ofxSurfaceManager.h" +#include "SurfaceManager.h" -ofxSurfaceManager::ofxSurfaceManager() +namespace ofx{ + namespace piMapper{ +SurfaceManager::SurfaceManager() { } -ofxSurfaceManager::~ofxSurfaceManager() +SurfaceManager::~SurfaceManager() { clear(); } -void ofxSurfaceManager::draw() +void SurfaceManager::draw() { for ( int i=0; idraw(); } } -void ofxSurfaceManager::addSurface(int surfaceType) +void SurfaceManager::addSurface(int surfaceType) { - if ( surfaceType == ofxSurfaceType::TRIANGLE_SURFACE ) { - surfaces.push_back( new ofxTriangleSurface() ); + if ( surfaceType == SurfaceType::TRIANGLE_SURFACE ) { + surfaces.push_back( new TriangleSurface() ); } - else if (surfaceType == ofxSurfaceType::QUAD_SURFACE ) { - surfaces.push_back( new ofxQuadSurface() ); + else if (surfaceType == SurfaceType::QUAD_SURFACE ) { + surfaces.push_back( new QuadSurface() ); } else { throw std::runtime_error("Attempt to add non-existing surface type."); } } -void ofxSurfaceManager::addSurface(int surfaceType, ofTexture* texturePtr) +void SurfaceManager::addSurface(int surfaceType, ofTexture* texturePtr) { - if ( surfaceType == ofxSurfaceType::TRIANGLE_SURFACE ) { - surfaces.push_back( new ofxTriangleSurface() ); + if ( surfaceType == SurfaceType::TRIANGLE_SURFACE ) { + surfaces.push_back( new TriangleSurface() ); surfaces.back()->setTexture(texturePtr); } - else if (surfaceType == ofxSurfaceType::QUAD_SURFACE ) { - surfaces.push_back( new ofxQuadSurface() ); + else if (surfaceType == SurfaceType::QUAD_SURFACE ) { + surfaces.push_back( new QuadSurface() ); surfaces.back()->setTexture(texturePtr); } else { @@ -45,9 +47,9 @@ void ofxSurfaceManager::addSurface(int surfaceType, ofTexture* texturePtr) } } -void ofxSurfaceManager::addSurface(int surfaceType, vector vertices, vector texCoords) +void SurfaceManager::addSurface(int surfaceType, vector vertices, vector texCoords) { - if ( surfaceType == ofxSurfaceType::TRIANGLE_SURFACE ) { + if ( surfaceType == SurfaceType::TRIANGLE_SURFACE ) { if ( vertices.size() < 3 ) { throw std::runtime_error("There must be 3 vertices for a triangle surface."); @@ -55,7 +57,7 @@ void ofxSurfaceManager::addSurface(int surfaceType, vector vertices, ve throw std::runtime_error("There must be 3 texture coordinates for a triangle surface."); } - surfaces.push_back( new ofxTriangleSurface() ); + surfaces.push_back( new TriangleSurface() ); for ( int i=0; i<3; i++ ) { surfaces.back()->setVertex(i, vertices[i]); @@ -63,14 +65,14 @@ void ofxSurfaceManager::addSurface(int surfaceType, vector vertices, ve } } - else if (surfaceType == ofxSurfaceType::QUAD_SURFACE ) { + else if (surfaceType == SurfaceType::QUAD_SURFACE ) { if ( vertices.size() < 4 ) { throw std::runtime_error("There must be 4 vertices for a quad surface."); } else if (texCoords.size() < 4) { throw std::runtime_error("There must be 4 texture coordinates for a quad surface."); } - surfaces.push_back( new ofxQuadSurface() ); + surfaces.push_back( new QuadSurface() ); for ( int i=0; i<4; i++ ) { surfaces.back()->setVertex(i, vertices[i]); @@ -83,9 +85,9 @@ void ofxSurfaceManager::addSurface(int surfaceType, vector vertices, ve } -void ofxSurfaceManager::addSurface(int surfaceType, ofTexture* texturePtr, vector vertices, vector texCoords) +void SurfaceManager::addSurface(int surfaceType, ofTexture* texturePtr, vector vertices, vector texCoords) { - if ( surfaceType == ofxSurfaceType::TRIANGLE_SURFACE ) { + if ( surfaceType == SurfaceType::TRIANGLE_SURFACE ) { if ( vertices.size() < 3 ) { throw std::runtime_error("There must be 3 vertices for a triangle surface."); @@ -93,7 +95,7 @@ void ofxSurfaceManager::addSurface(int surfaceType, ofTexture* texturePtr, vecto throw std::runtime_error("Thre must be 3 texture coordinates for a triangle surface."); } - surfaces.push_back( new ofxTriangleSurface() ); + surfaces.push_back( new TriangleSurface() ); surfaces.back()->setTexture(texturePtr); for ( int i=0; i<3; i++ ) { @@ -102,14 +104,14 @@ void ofxSurfaceManager::addSurface(int surfaceType, ofTexture* texturePtr, vecto } } - else if (surfaceType == ofxSurfaceType::QUAD_SURFACE ) { + else if (surfaceType == SurfaceType::QUAD_SURFACE ) { if ( vertices.size() < 4 ) { throw std::runtime_error("There must be 4 vertices for a quad surface."); } else if (texCoords.size() < 4) { throw std::runtime_error("Thre must be 4 texture coordinates for a quad surface."); } - surfaces.push_back( new ofxQuadSurface() ); + surfaces.push_back( new QuadSurface() ); surfaces.back()->setTexture(texturePtr); for ( int i=0; i<4; i++ ) { @@ -122,7 +124,7 @@ void ofxSurfaceManager::addSurface(int surfaceType, ofTexture* texturePtr, vecto } } -void ofxSurfaceManager::removeSelectedSurface() +void SurfaceManager::removeSelectedSurface() { if ( selectedSurface == NULL ) return; @@ -136,7 +138,7 @@ void ofxSurfaceManager::removeSelectedSurface() } } -void ofxSurfaceManager::manageMemory() +void SurfaceManager::manageMemory() { // check if each of the sources is assigned to a surface or not for ( int i=0; igetType(); + // // surfaceType == SurfaceType::TRIANGLE_SURFACE + // SurfaceType surfaceType = &surface->getType(); // xmlSettings.addValue("surface-type", surfaceType); // xmlSettings.popTag(); // type @@ -253,7 +255,7 @@ void ofxSurfaceManager::saveXmlSettings(string fileName) xmlSettings.save(fileName); } -void ofxSurfaceManager::loadXmlSettings(string fileName) +void SurfaceManager::loadXmlSettings(string fileName) { @@ -337,9 +339,9 @@ void ofxSurfaceManager::loadXmlSettings(string fileName) // now we have variables sourceName and sourceTexture // by checking those we can use one or another addSurface method if ( sourceName != "none" && sourceTexture != NULL ) { - addSurface(ofxSurfaceType::TRIANGLE_SURFACE, sourceTexture, vertices, texCoords); + addSurface(SurfaceType::TRIANGLE_SURFACE, sourceTexture, vertices, texCoords); } else { - addSurface(ofxSurfaceType::TRIANGLE_SURFACE, vertices, texCoords); + addSurface(SurfaceType::TRIANGLE_SURFACE, vertices, texCoords); } } // it's a quad ? @@ -390,9 +392,9 @@ void ofxSurfaceManager::loadXmlSettings(string fileName) // now we have variables sourceName and sourceTexture // by checking those we can use one or another addSurface method if ( sourceName != "none" && sourceTexture != NULL ) { - addSurface(ofxSurfaceType::QUAD_SURFACE, sourceTexture, vertices, texCoords); + addSurface(SurfaceType::QUAD_SURFACE, sourceTexture, vertices, texCoords); } else { - addSurface(ofxSurfaceType::QUAD_SURFACE, vertices, texCoords); + addSurface(SurfaceType::QUAD_SURFACE, vertices, texCoords); } @@ -404,7 +406,7 @@ void ofxSurfaceManager::loadXmlSettings(string fileName) xmlSettings.popTag(); // surfaces } -ofxBaseSurface* ofxSurfaceManager::selectSurface(int index) +BaseSurface* SurfaceManager::selectSurface(int index) { if ( index >= surfaces.size() ) { throw std::runtime_error("Surface index out of bounds."); @@ -416,17 +418,17 @@ ofxBaseSurface* ofxSurfaceManager::selectSurface(int index) ofSendMessage("surfaceSelected"); } -ofxBaseSurface* ofxSurfaceManager::getSelectedSurface() +BaseSurface* SurfaceManager::getSelectedSurface() { return selectedSurface; } -void ofxSurfaceManager::deselectSurface() +void SurfaceManager::deselectSurface() { selectedSurface = NULL; } -ofTexture* ofxSurfaceManager::loadImageSource(string name, string path) +ofTexture* SurfaceManager::loadImageSource(string name, string path) { // check if it is loaded for ( int i=0; igetTextureReference(); } -string ofxSurfaceManager::getSelectedSurfaceSourceName() +string SurfaceManager::getSelectedSurfaceSourceName() { if ( selectedSurface == NULL ) { return "none"; @@ -455,7 +457,7 @@ string ofxSurfaceManager::getSelectedSurfaceSourceName() return getSurfaceSourceName( selectedSurface ); } -string ofxSurfaceManager::getSurfaceSourceName(ofxBaseSurface *surface) +string SurfaceManager::getSurfaceSourceName(BaseSurface *surface) { ofTexture* tex = surface->getTexture(); for ( int i=0; i= surfaces.size() ) { throw std::runtime_error("Surface index out of bounds."); @@ -477,10 +479,10 @@ ofxBaseSurface* ofxSurfaceManager::getSurface(int index) return surfaces[index]; } -int ofxSurfaceManager::size() +int SurfaceManager::size() { return surfaces.size(); } - + }} diff --git a/src/SurfaceManager.h b/src/SurfaceManager.h index 90e8bb1..48df5c5 100755 --- a/src/SurfaceManager.h +++ b/src/SurfaceManager.h @@ -1,20 +1,23 @@ -#ifndef H_OFX_SURFACE_MANAGER -#define H_OFX_SURFACE_MANAGER +#pragma once + +#include "BaseSurface.h" +#include "TriangleSurface.h" +#include "QuadSurface.h" +#include "SurfaceType.h" -#include "ofxBaseSurface.h" -#include "ofxTriangleSurface.h" -#include "ofxQuadSurface.h" -#include "ofxSurfaceType.h" #include "ofEvents.h" #include "ofxXmlSettings.h" using namespace std; -class ofxSurfaceManager + +namespace ofx{ + namespace piMapper{ +class SurfaceManager { public: - ofxSurfaceManager(); - ~ofxSurfaceManager(); + SurfaceManager(); + ~SurfaceManager(); void draw(); void addSurface(int surfaceType); @@ -27,22 +30,22 @@ public: void saveXmlSettings(string fileName); void loadXmlSettings(string fileName); - ofxBaseSurface* getSurface(int index); + BaseSurface* getSurface(int index); int size(); - ofxBaseSurface* selectSurface(int index); - ofxBaseSurface* getSelectedSurface(); + BaseSurface* selectSurface(int index); + BaseSurface* getSelectedSurface(); void deselectSurface(); ofTexture* loadImageSource(string name, string path); string getSelectedSurfaceSourceName(); - string getSurfaceSourceName( ofxBaseSurface* surface ); + string getSurfaceSourceName( BaseSurface* surface ); private: - vector surfaces; - ofxBaseSurface* selectedSurface; + vector surfaces; + BaseSurface* selectedSurface; vector loadedImageSourceNames; vector loadedImageSources; ofxXmlSettings xmlSettings; }; -#endif \ No newline at end of file + }} \ No newline at end of file diff --git a/src/SurfaceManagerGui.cpp b/src/SurfaceManagerGui.cpp index 49d5a1e..e932810 100644 --- a/src/SurfaceManagerGui.cpp +++ b/src/SurfaceManagerGui.cpp @@ -1,41 +1,43 @@ -#include "ofxSurfaceManagerGui.h" +#include "SurfaceManagerGui.h" -ofxSurfaceManagerGui::ofxSurfaceManagerGui() +namespace ofx{ + namespace piMapper{ +SurfaceManagerGui::SurfaceManagerGui() { surfaceManager = NULL; - guiMode = ofxGuiMode::NONE; + guiMode = GuiMode::NONE; bDrag = false; registerMouseEvents(); ofHideCursor(); } -ofxSurfaceManagerGui::~ofxSurfaceManagerGui() +SurfaceManagerGui::~SurfaceManagerGui() { unregisterMouseEvents(); surfaceManager = NULL; } -void ofxSurfaceManagerGui::registerMouseEvents() +void SurfaceManagerGui::registerMouseEvents() { - ofAddListener(ofEvents().mousePressed, this, &ofxSurfaceManagerGui::mousePressed); - ofAddListener(ofEvents().mouseReleased, this, &ofxSurfaceManagerGui::mouseReleased); - ofAddListener(ofEvents().mouseDragged, this, &ofxSurfaceManagerGui::mouseDragged); + ofAddListener(ofEvents().mousePressed, this, &SurfaceManagerGui::mousePressed); + ofAddListener(ofEvents().mouseReleased, this, &SurfaceManagerGui::mouseReleased); + ofAddListener(ofEvents().mouseDragged, this, &SurfaceManagerGui::mouseDragged); } -void ofxSurfaceManagerGui::unregisterMouseEvents() +void SurfaceManagerGui::unregisterMouseEvents() { - ofRemoveListener(ofEvents().mousePressed, this, &ofxSurfaceManagerGui::mousePressed); - ofRemoveListener(ofEvents().mouseReleased, this, &ofxSurfaceManagerGui::mouseReleased); - ofRemoveListener(ofEvents().mouseDragged, this, &ofxSurfaceManagerGui::mouseDragged); + ofRemoveListener(ofEvents().mousePressed, this, &SurfaceManagerGui::mousePressed); + ofRemoveListener(ofEvents().mouseReleased, this, &SurfaceManagerGui::mouseReleased); + ofRemoveListener(ofEvents().mouseDragged, this, &SurfaceManagerGui::mouseDragged); } -void ofxSurfaceManagerGui::draw() +void SurfaceManagerGui::draw() { if ( surfaceManager == NULL ) return; - if ( guiMode == ofxGuiMode::NONE ) { + if ( guiMode == GuiMode::NONE ) { surfaceManager->draw(); - } else if ( guiMode == ofxGuiMode::TEXTURE_MAPPING ) { + } else if ( guiMode == GuiMode::TEXTURE_MAPPING ) { // draw the texture of the selected surface if ( surfaceManager->getSelectedSurface() != NULL ) { @@ -57,7 +59,7 @@ void ofxSurfaceManagerGui::draw() // draw texture editing GUI on top textureEditor.draw(); - } else if ( guiMode == ofxGuiMode::PROJECTION_MAPPING ) { + } else if ( guiMode == GuiMode::PROJECTION_MAPPING ) { // draw projection surfaces first surfaceManager->draw(); @@ -68,7 +70,7 @@ void ofxSurfaceManagerGui::draw() // draw projection mapping editing gui projectionEditor.draw(); - } else if ( guiMode == ofxGuiMode::SOURCE_SELECTION ) { + } else if ( guiMode == GuiMode::SOURCE_SELECTION ) { // draw projection surfaces first surfaceManager->draw(); @@ -79,15 +81,15 @@ void ofxSurfaceManagerGui::draw() } } -void ofxSurfaceManagerGui::mousePressed(ofMouseEventArgs &args) +void SurfaceManagerGui::mousePressed(ofMouseEventArgs &args) { - if ( guiMode == ofxGuiMode::NONE ) { + if ( guiMode == GuiMode::NONE ) { return; - } else if ( guiMode == ofxGuiMode::TEXTURE_MAPPING ) { + } else if ( guiMode == GuiMode::TEXTURE_MAPPING ) { bool bSurfaceSelected = false; - ofxCircleJoint* hitJoint = textureEditor.hitTestJoints(ofVec2f(args.x, args.y)); + CircleJoint* hitJoint = textureEditor.hitTestJoints(ofVec2f(args.x, args.y)); if ( hitJoint != NULL ) { textureEditor.unselectAllJoints(); hitJoint->select(); @@ -105,11 +107,11 @@ void ofxSurfaceManagerGui::mousePressed(ofMouseEventArgs &args) } } - } else if ( guiMode == ofxGuiMode::PROJECTION_MAPPING ) { + } else if ( guiMode == GuiMode::PROJECTION_MAPPING ) { bool bSurfaceSelected = false; - ofxCircleJoint* hitJoint = projectionEditor.hitTestJoints(ofVec2f(args.x, args.y)); + CircleJoint* hitJoint = projectionEditor.hitTestJoints(ofVec2f(args.x, args.y)); if ( hitJoint != NULL ) { projectionEditor.unselectAllJoints(); hitJoint->select(); @@ -141,51 +143,51 @@ void ofxSurfaceManagerGui::mousePressed(ofMouseEventArgs &args) projectionEditor.clearJoints(); surfaceManager->deselectSurface(); } - } else if ( guiMode == ofxGuiMode::SOURCE_SELECTION ) { + } else if ( guiMode == GuiMode::SOURCE_SELECTION ) { } } -void ofxSurfaceManagerGui::mouseReleased(ofMouseEventArgs &args) +void SurfaceManagerGui::mouseReleased(ofMouseEventArgs &args) { stopDrag(); projectionEditor.stopDragJoints(); textureEditor.stopDragJoints(); } -void ofxSurfaceManagerGui::mouseDragged(ofMouseEventArgs &args) +void SurfaceManagerGui::mouseDragged(ofMouseEventArgs &args) { if (bDrag) { ofVec2f mousePosition = ofVec2f(args.x, args.y); ofVec2f distance = mousePosition - clickPosition; - if ( guiMode == ofxGuiMode::PROJECTION_MAPPING ) { + if ( guiMode == GuiMode::PROJECTION_MAPPING ) { // add this distance to all vertices in surface projectionEditor.moveSelectedSurface(distance); - } else if ( guiMode == ofxGuiMode::TEXTURE_MAPPING ) { + } else if ( guiMode == GuiMode::TEXTURE_MAPPING ) { textureEditor.moveTexCoords(distance); } clickPosition = mousePosition; } } -void ofxSurfaceManagerGui::setSurfaceManager(ofxSurfaceManager* newSurfaceManager) +void SurfaceManagerGui::setSurfaceManager(SurfaceManager* newSurfaceManager) { surfaceManager = newSurfaceManager; projectionEditor.setSurfaceManager( surfaceManager ); sourcesEditor.setSurfaceManager( surfaceManager ); } -void ofxSurfaceManagerGui::setMode(int newGuiMode) +void SurfaceManagerGui::setMode(int newGuiMode) { - if (newGuiMode != ofxGuiMode::NONE && - newGuiMode != ofxGuiMode::TEXTURE_MAPPING && - newGuiMode != ofxGuiMode::PROJECTION_MAPPING && - newGuiMode != ofxGuiMode::SOURCE_SELECTION) { + if (newGuiMode != GuiMode::NONE && + newGuiMode != GuiMode::TEXTURE_MAPPING && + newGuiMode != GuiMode::PROJECTION_MAPPING && + newGuiMode != GuiMode::SOURCE_SELECTION) { throw std::runtime_error("Trying to set invalid mode."); } - if ( newGuiMode == ofxGuiMode::NONE ) { + if ( newGuiMode == GuiMode::NONE ) { ofHideCursor(); } else { ofShowCursor(); @@ -193,7 +195,7 @@ void ofxSurfaceManagerGui::setMode(int newGuiMode) guiMode = newGuiMode; - if ( guiMode == ofxGuiMode::SOURCE_SELECTION ) { + if ( guiMode == GuiMode::SOURCE_SELECTION ) { sourcesEditor.enable(); string sourceName = surfaceManager->getSelectedSurfaceSourceName(); sourcesEditor.selectImageSourceRadioButton(sourceName); @@ -201,7 +203,7 @@ void ofxSurfaceManagerGui::setMode(int newGuiMode) sourcesEditor.disable(); } - if ( guiMode == ofxGuiMode::TEXTURE_MAPPING ) { + if ( guiMode == GuiMode::TEXTURE_MAPPING ) { textureEditor.enable(); // refresh texture editor surface reference textureEditor.setSurface(surfaceManager->getSelectedSurface()); @@ -209,14 +211,14 @@ void ofxSurfaceManagerGui::setMode(int newGuiMode) textureEditor.disable(); } - if (guiMode == ofxGuiMode::PROJECTION_MAPPING) { + if (guiMode == GuiMode::PROJECTION_MAPPING) { projectionEditor.enable(); } else { projectionEditor.disable(); } } -void ofxSurfaceManagerGui::drawSelectedSurfaceHighlight() +void SurfaceManagerGui::drawSelectedSurfaceHighlight() { if ( surfaceManager->getSelectedSurface() == NULL ) return; @@ -229,7 +231,7 @@ void ofxSurfaceManagerGui::drawSelectedSurfaceHighlight() ofPopStyle(); } -void ofxSurfaceManagerGui::drawSelectedSurfaceTextureHighlight() +void SurfaceManagerGui::drawSelectedSurfaceTextureHighlight() { if ( surfaceManager->getSelectedSurface() == NULL ) return; @@ -243,12 +245,14 @@ void ofxSurfaceManagerGui::drawSelectedSurfaceTextureHighlight() } -void ofxSurfaceManagerGui::startDrag() +void SurfaceManagerGui::startDrag() { bDrag = true; } -void ofxSurfaceManagerGui::stopDrag() +void SurfaceManagerGui::stopDrag() { bDrag = false; -} \ No newline at end of file +} + + }} \ No newline at end of file diff --git a/src/SurfaceManagerGui.h b/src/SurfaceManagerGui.h index 605d8a1..0dc8743 100644 --- a/src/SurfaceManagerGui.h +++ b/src/SurfaceManagerGui.h @@ -1,22 +1,24 @@ -#ifndef H_OFX_SURFACE_MANAGER_GUI -#define H_OFX_SURFACE_MANAGER_GUI +#pragma once // I'm starting to think, maybe we should use ofxStateMachine here. // Would make sense. TODO later. #include "ofEvents.h" -#include "ofxSurfaceManager.h" -#include "ofxTextureEditor.h" -#include "ofxProjectionEditor.h" -#include "ofxSourcesEditor.h" -#include "ofxGuiMode.h" #include "ofGraphics.h" -class ofxSurfaceManagerGui +#include "SurfaceManager.h" +#include "TextureEditor.h" +#include "ProjectionEditor.h" +#include "SourcesEditor.h" +#include "GuiMode.h" + +namespace ofx{ + namespace piMapper{ +class SurfaceManagerGui { public: - ofxSurfaceManagerGui(); - ~ofxSurfaceManagerGui(); + SurfaceManagerGui(); + ~SurfaceManagerGui(); void registerMouseEvents(); void unregisterMouseEvents(); @@ -25,7 +27,7 @@ public: void mousePressed(ofMouseEventArgs& args); void mouseReleased(ofMouseEventArgs& args); void mouseDragged(ofMouseEventArgs& args); - void setSurfaceManager(ofxSurfaceManager* newSurfaceManager); + void setSurfaceManager(SurfaceManager* newSurfaceManager); void setMode(int newGuiMode); void drawSelectedSurfaceHighlight(); void drawSelectedSurfaceTextureHighlight(); @@ -33,14 +35,14 @@ public: void stopDrag(); private: - ofxSurfaceManager* surfaceManager; - ofxTextureEditor textureEditor; - ofxProjectionEditor projectionEditor; - ofxSourcesEditor sourcesEditor; + SurfaceManager* surfaceManager; + TextureEditor textureEditor; + ProjectionEditor projectionEditor; + SourcesEditor sourcesEditor; int guiMode; bool bDrag; ofVec2f clickPosition; }; -#endif \ No newline at end of file + }} \ No newline at end of file diff --git a/src/SurfaceType.h b/src/SurfaceType.h index cbda131..21deb9b 100755 --- a/src/SurfaceType.h +++ b/src/SurfaceType.h @@ -1,7 +1,8 @@ -#ifndef H_OFX_SURFACE_TYPE -#define H_OFX_SURFACE_TYPE +#pragma once -struct ofxSurfaceType +namespace ofx{ + namespace piMapper{ +struct SurfaceType { enum { TRIANGLE_SURFACE, @@ -9,4 +10,4 @@ struct ofxSurfaceType }; }; -#endif \ No newline at end of file + }} \ No newline at end of file diff --git a/src/TextureEditor.cpp b/src/TextureEditor.cpp index 0a1aa41..a0a9eba 100755 --- a/src/TextureEditor.cpp +++ b/src/TextureEditor.cpp @@ -1,53 +1,55 @@ -#include "ofxTextureEditor.h" +#include "TextureEditor.h" -ofxTextureEditor::ofxTextureEditor() +namespace ofx{ + namespace piMapper{ +TextureEditor::TextureEditor() { clear(); enable(); } -ofxTextureEditor::~ofxTextureEditor() +TextureEditor::~TextureEditor() { clear(); disable(); } -void ofxTextureEditor::registerAppEvents() +void TextureEditor::registerAppEvents() { - ofAddListener(ofEvents().update, this, &ofxTextureEditor::update); + ofAddListener(ofEvents().update, this, &TextureEditor::update); } -void ofxTextureEditor::unregisterAppEvents() +void TextureEditor::unregisterAppEvents() { - ofRemoveListener(ofEvents().update, this, &ofxTextureEditor::update); + ofRemoveListener(ofEvents().update, this, &TextureEditor::update); } -void ofxTextureEditor::registerKeyEvents() +void TextureEditor::registerKeyEvents() { - ofAddListener(ofEvents().keyPressed, this, &ofxTextureEditor::keyPressed); - ofAddListener(ofEvents().keyReleased, this, &ofxTextureEditor::keyReleased); + ofAddListener(ofEvents().keyPressed, this, &TextureEditor::keyPressed); + ofAddListener(ofEvents().keyReleased, this, &TextureEditor::keyReleased); } -void ofxTextureEditor::unregisterKeyEvents() +void TextureEditor::unregisterKeyEvents() { - ofRemoveListener(ofEvents().keyPressed, this, &ofxTextureEditor::keyPressed); - ofRemoveListener(ofEvents().keyReleased, this, &ofxTextureEditor::keyReleased); + ofRemoveListener(ofEvents().keyPressed, this, &TextureEditor::keyPressed); + ofRemoveListener(ofEvents().keyReleased, this, &TextureEditor::keyReleased); } -void ofxTextureEditor::enable() +void TextureEditor::enable() { registerAppEvents(); registerKeyEvents(); bShiftKeyDown = false; } -void ofxTextureEditor::disable() +void TextureEditor::disable() { unregisterAppEvents(); unregisterKeyEvents(); } -void ofxTextureEditor::update(ofEventArgs &args) +void TextureEditor::update(ofEventArgs &args) { if ( surface == NULL ) return; @@ -62,7 +64,7 @@ void ofxTextureEditor::update(ofEventArgs &args) } } -void ofxTextureEditor::keyPressed(ofKeyEventArgs &args) +void TextureEditor::keyPressed(ofKeyEventArgs &args) { int key = args.key; float moveStep; @@ -79,7 +81,7 @@ void ofxTextureEditor::keyPressed(ofKeyEventArgs &args) } } -void ofxTextureEditor::keyReleased(ofKeyEventArgs &args) +void TextureEditor::keyReleased(ofKeyEventArgs &args) { int key = args.key; switch (key) { @@ -88,33 +90,33 @@ void ofxTextureEditor::keyReleased(ofKeyEventArgs &args) } -void ofxTextureEditor::draw() +void TextureEditor::draw() { if (surface == NULL) return; drawJoints(); } -void ofxTextureEditor::drawJoints() +void TextureEditor::drawJoints() { for ( int i=0; idraw(); } } -void ofxTextureEditor::setSurface(ofxBaseSurface* newSurface) +void TextureEditor::setSurface(BaseSurface* newSurface) { surface = newSurface; createJoints(); } -void ofxTextureEditor::clear() +void TextureEditor::clear() { surface = NULL; clearJoints(); } -void ofxTextureEditor::createJoints() +void TextureEditor::createJoints() { if ( surface == NULL ) return; clearJoints(); @@ -122,12 +124,12 @@ void ofxTextureEditor::createJoints() ofVec2f textureSize = ofVec2f(surface->getTexture()->getWidth(), surface->getTexture()->getHeight()); for ( int i=0; iposition = texCoords[i] * textureSize; } } -void ofxTextureEditor::clearJoints() +void TextureEditor::clearJoints() { while ( joints.size() ) { delete joints.back(); @@ -135,14 +137,14 @@ void ofxTextureEditor::clearJoints() } } -void ofxTextureEditor::unselectAllJoints() +void TextureEditor::unselectAllJoints() { for ( int i=0; iunselect(); } } -void ofxTextureEditor::moveTexCoords(ofVec2f by) +void TextureEditor::moveTexCoords(ofVec2f by) { if ( surface == NULL ) return; vector& texCoords = surface->getTexCoords(); @@ -153,18 +155,18 @@ void ofxTextureEditor::moveTexCoords(ofVec2f by) } } -void ofxTextureEditor::stopDragJoints() +void TextureEditor::stopDragJoints() { for (int i=0; istopDrag(); } } -void ofxTextureEditor::moveSelection(ofVec2f by) +void TextureEditor::moveSelection(ofVec2f by) { // check if joints selected bool bJointSelected = false; - ofxBaseJoint* selectedJoint; + BaseJoint* selectedJoint; for ( int i=0; iisSelected()) { bJointSelected = true; @@ -180,7 +182,7 @@ void ofxTextureEditor::moveSelection(ofVec2f by) } } -ofxCircleJoint* ofxTextureEditor::hitTestJoints(ofVec2f pos) +CircleJoint* TextureEditor::hitTestJoints(ofVec2f pos) { for ( int i=0; ihitTest(pos) ){ @@ -188,4 +190,6 @@ ofxCircleJoint* ofxTextureEditor::hitTestJoints(ofVec2f pos) } } return NULL; -} \ No newline at end of file +} + + }} \ No newline at end of file diff --git a/src/TextureEditor.h b/src/TextureEditor.h index 18f30ef..1387984 100644 --- a/src/TextureEditor.h +++ b/src/TextureEditor.h @@ -1,15 +1,18 @@ -#ifndef H_OFX_TEXTURE_EDITOR -#define H_OFX_TEXTURE_EDITOR +#pragma once #include "ofEvents.h" -#include "ofxBaseSurface.h" -#include "ofxCircleJoint.h" -class ofxTextureEditor +#include "BaseSurface.h" +#include "CircleJoint.h" + + +namespace ofx{ + namespace piMapper{ +class TextureEditor { public: - ofxTextureEditor(); - ~ofxTextureEditor(); + TextureEditor(); + ~TextureEditor(); void registerAppEvents(); void unregisterAppEvents(); @@ -23,7 +26,7 @@ public: void keyReleased(ofKeyEventArgs& args); void draw(); void drawJoints(); - void setSurface(ofxBaseSurface* newSurface); + void setSurface(BaseSurface* newSurface); void clear(); void createJoints(); void clearJoints(); @@ -31,12 +34,12 @@ public: void moveTexCoords(ofVec2f by); void stopDragJoints(); void moveSelection(ofVec2f by); - ofxCircleJoint* hitTestJoints(ofVec2f pos); + CircleJoint* hitTestJoints(ofVec2f pos); private: - ofxBaseSurface* surface; - vector joints; + BaseSurface* surface; + vector joints; bool bShiftKeyDown; }; -#endif \ No newline at end of file + }} \ No newline at end of file diff --git a/src/TriangleSurface.cpp b/src/TriangleSurface.cpp index 56d0a44..950e360 100644 --- a/src/TriangleSurface.cpp +++ b/src/TriangleSurface.cpp @@ -1,17 +1,19 @@ -#include "ofxTriangleSurface.h" +#include "TriangleSurface.h" -ofxTriangleSurface::ofxTriangleSurface() +namespace ofx{ + namespace piMapper{ +TriangleSurface::TriangleSurface() { - cout << "ofxTriangleSurface constructor." << endl; + cout << "TriangleSurface constructor." << endl; setup(); } -ofxTriangleSurface::~ofxTriangleSurface() +TriangleSurface::~TriangleSurface() { - cout << "ofxTriangleSurface destructor." << endl; + cout << "TriangleSurface destructor." << endl; } -void ofxTriangleSurface::setup() +void TriangleSurface::setup() { // Create 3 points for the triangle ofVec2f p1 = ofVec2f(ofGetWidth()/2.0f, 0); @@ -26,7 +28,7 @@ void ofxTriangleSurface::setup() setup( p1, p2, p3, t1, t2, t3, texture ); } -void ofxTriangleSurface::setup( ofVec2f p1, ofVec2f p2, ofVec2f p3, ofVec2f t1, ofVec2f t2, ofVec2f t3, ofTexture* texturePtr ) +void TriangleSurface::setup( ofVec2f p1, ofVec2f p2, ofVec2f p3, ofVec2f t1, ofVec2f t2, ofVec2f t3, ofTexture* texturePtr ) { // Assign texture texture = texturePtr; @@ -45,14 +47,14 @@ void ofxTriangleSurface::setup( ofVec2f p1, ofVec2f p2, ofVec2f p3, ofVec2f t1, mesh.addTexCoord(t3); } -void ofxTriangleSurface::draw() +void TriangleSurface::draw() { texture->bind(); mesh.draw(); texture->unbind(); } -void ofxTriangleSurface::setVertex(int index, ofVec2f p) +void TriangleSurface::setVertex(int index, ofVec2f p) { if ( index > 2 ) { ofLog() << "Vertex with this index does not exist: " << index << endl; @@ -62,7 +64,7 @@ void ofxTriangleSurface::setVertex(int index, ofVec2f p) mesh.setVertex(index, p); } -void ofxTriangleSurface::setTexCoord(int index, ofVec2f t) +void TriangleSurface::setTexCoord(int index, ofVec2f t) { if ( index > 2 ) { ofLog() << "Texture coordinate with this index does not exist: " << index << endl; @@ -72,7 +74,7 @@ void ofxTriangleSurface::setTexCoord(int index, ofVec2f t) mesh.setTexCoord(index, t); } -void ofxTriangleSurface::moveBy(ofVec2f v) +void TriangleSurface::moveBy(ofVec2f v) { vector& vertices = getVertices(); for (int i=0; i 2 ) { ofLog() << "Vertex with this index does not exist: " << index << endl; @@ -108,7 +110,7 @@ ofVec2f ofxTriangleSurface::getVertex(int index) return ofVec2f(vert.x, vert.y); } -ofVec2f ofxTriangleSurface::getTexCoord(int index) +ofVec2f TriangleSurface::getTexCoord(int index) { if (index > 2) { throw std::runtime_error("Texture coordinate index out of bounds."); @@ -117,7 +119,7 @@ ofVec2f ofxTriangleSurface::getTexCoord(int index) return mesh.getTexCoord(index); } -ofPolyline ofxTriangleSurface::getHitArea() +ofPolyline TriangleSurface::getHitArea() { ofPolyline line; line.addVertex( ofPoint( mesh.getVertex(0).x, mesh.getVertex(0).y ) ); @@ -128,7 +130,7 @@ ofPolyline ofxTriangleSurface::getHitArea() return line; } -ofPolyline ofxTriangleSurface::getTextureHitArea() +ofPolyline TriangleSurface::getTextureHitArea() { ofPolyline line; vector& texCoords = mesh.getTexCoords(); @@ -141,14 +143,16 @@ ofPolyline ofxTriangleSurface::getTextureHitArea() return line; } -vector& ofxTriangleSurface::getVertices() +vector& TriangleSurface::getVertices() { // return only joint vertices return mesh.getVertices(); } -vector& ofxTriangleSurface::getTexCoords() +vector& TriangleSurface::getTexCoords() { return mesh.getTexCoords(); -} \ No newline at end of file +} + + }} \ No newline at end of file diff --git a/src/TriangleSurface.h b/src/TriangleSurface.h index 27d3865..f1b2e74 100644 --- a/src/TriangleSurface.h +++ b/src/TriangleSurface.h @@ -1,15 +1,17 @@ -#ifndef H_OFX_TRIANGLE_SURFACE -#define H_OFX_TRIANGLE_SURFACE +#pragma once #include "ofMain.h" -#include "ofxBaseSurface.h" -#include "ofxSurfaceType.h" +#include "BaseSurface.h" +#include "SurfaceType.h" -class ofxTriangleSurface : public ofxBaseSurface + +namespace ofx{ + namespace piMapper{ +class TriangleSurface : public BaseSurface { public: - ofxTriangleSurface(); - ~ofxTriangleSurface(); + TriangleSurface(); + ~TriangleSurface(); void setup(); void setup( ofVec2f p1, ofVec2f p2, ofVec2f p3, ofVec2f t1, ofVec2f t2, ofVec2f t3, ofTexture* texturePtr ); @@ -28,4 +30,4 @@ public: vector& getTexCoords(); }; -#endif \ No newline at end of file + }} \ No newline at end of file diff --git a/src/ofxPiMapper.h b/src/ofxPiMapper.h index 4f65bd2..b0b5fea 100644 --- a/src/ofxPiMapper.h +++ b/src/ofxPiMapper.h @@ -1,7 +1,4 @@ -#ifndef H_OFX_PI_MAPPER -#define H_OFX_PI_MAPPER +#pragma once -#include "ofxSurfaceManager.h" -#include "ofxSurfaceManagerGui.h" - -#endif \ No newline at end of file +#include "SurfaceManager.h" +#include "SurfaceManagerGui.h" \ No newline at end of file diff --git a/src/ui/RadioList.cpp b/src/ui/RadioList.cpp index e974284..a1d5cf5 100644 --- a/src/ui/RadioList.cpp +++ b/src/ui/RadioList.cpp @@ -1,29 +1,31 @@ -#include "ofxRadioList.h" +#include "RadioList.h" -ofxRadioList::ofxRadioList() +namespace ofx{ + namespace piMapper{ +RadioList::RadioList() { storedTitle = ""; storedSelectedItem = 0; } -ofxRadioList::ofxRadioList(vector &labels) +RadioList::RadioList(vector &labels) { - ofxRadioList(); + RadioList(); setup(labels); } -ofxRadioList::ofxRadioList(string title, vector &labels) +RadioList::RadioList(string title, vector &labels) { - ofxRadioList(); + RadioList(); setup(title, labels); } -ofxRadioList::~ofxRadioList() +RadioList::~RadioList() { clear(); } -void ofxRadioList::setup(vector &labels) +void RadioList::setup(vector &labels) { // Copy incomming labels for later use storedLabels = labels; @@ -34,14 +36,14 @@ void ofxRadioList::setup(vector &labels) ofxToggle* toggle = new ofxToggle(); toggle->setup(false); toggle->setName(labels[i]); - toggle->addListener(this, &ofxRadioList::onToggleClicked); + toggle->addListener(this, &RadioList::onToggleClicked); guiGroup.add(toggle); } cout << "num items: " << guiGroup.getNumControls() << endl; } -void ofxRadioList::setup(string title, vector &labels) +void RadioList::setup(string title, vector &labels) { // Store title for later use storedTitle = title; @@ -49,28 +51,28 @@ void ofxRadioList::setup(string title, vector &labels) setup(labels); } -void ofxRadioList::draw() +void RadioList::draw() { guiGroup.draw(); } -void ofxRadioList::setTitle(string title) +void RadioList::setTitle(string title) { storedTitle = title; guiGroup.setName(title); } -void ofxRadioList::setPosition(ofPoint p) +void RadioList::setPosition(ofPoint p) { guiGroup.setPosition(p); } -void ofxRadioList::setPosition(float x, float y) +void RadioList::setPosition(float x, float y) { guiGroup.setPosition(x, y); } -void ofxRadioList::selectItem(int index) +void RadioList::selectItem(int index) { if (index >= guiGroup.getNumControls()) { return; @@ -79,16 +81,16 @@ void ofxRadioList::selectItem(int index) unselectAll(); ofxToggle* toggle = static_cast(guiGroup.getControl(index)); - toggle->removeListener(this, &ofxRadioList::onToggleClicked); + toggle->removeListener(this, &RadioList::onToggleClicked); *toggle = true; // Select the specific radio button - toggle->addListener(this, &ofxRadioList::onToggleClicked); + toggle->addListener(this, &RadioList::onToggleClicked); string name = toggle->getName(); ofNotifyEvent(radioSelectedEvent, name, this); storedSelectedItem = index; } -void ofxRadioList::enable() +void RadioList::enable() { if (guiGroup.getNumControls() >= 0) { clear(); @@ -99,63 +101,63 @@ void ofxRadioList::enable() // Select the stored selected item without throwing an event ofxToggle* toggle = static_cast(guiGroup.getControl(storedSelectedItem)); - toggle->removeListener(this, &ofxRadioList::onToggleClicked); + toggle->removeListener(this, &RadioList::onToggleClicked); *toggle = true; - toggle->addListener(this, &ofxRadioList::onToggleClicked); + toggle->addListener(this, &RadioList::onToggleClicked); cout << "num items after enable: " << guiGroup.getNumControls() << endl; } -void ofxRadioList::disable() +void RadioList::disable() { // Just remove everything clear(); } -void ofxRadioList::clear() +void RadioList::clear() { int i; for (i = 0; i < guiGroup.getNumControls(); i++) { ofxToggle* toggle = static_cast(guiGroup.getControl(i)); - toggle->removeListener(this, &ofxRadioList::onToggleClicked); + toggle->removeListener(this, &RadioList::onToggleClicked); delete toggle; } guiGroup.clear(); } -void ofxRadioList::unselectAll() +void RadioList::unselectAll() { int i; for (i = 0; i < guiGroup.getNumControls(); i++) { ofxToggle* toggle = static_cast(guiGroup.getControl(i)); ofParameter* paramPtr = static_cast*>(&toggle->getParameter()); - toggle->removeListener(this, &ofxRadioList::onToggleClicked); + toggle->removeListener(this, &RadioList::onToggleClicked); *toggle = false; - toggle->addListener(this, &ofxRadioList::onToggleClicked); + toggle->addListener(this, &RadioList::onToggleClicked); } } -ofPoint ofxRadioList::getPosition() +ofPoint RadioList::getPosition() { return guiGroup.getPosition(); } -float ofxRadioList::getWidth() +float RadioList::getWidth() { return guiGroup.getWidth(); } -float ofxRadioList::getHeight() +float RadioList::getHeight() { return guiGroup.getHeight(); } -string ofxRadioList::getTitle() +string RadioList::getTitle() { return guiGroup.getName(); } -string ofxRadioList::getItemName(int index) +string RadioList::getItemName(int index) { if (index >= guiGroup.getNumControls()) { return ""; @@ -165,12 +167,12 @@ string ofxRadioList::getItemName(int index) return toggle->getName(); } -int ofxRadioList::size() +int RadioList::size() { return guiGroup.getNumControls(); } -void ofxRadioList::onToggleClicked(bool &toggleValue) +void RadioList::onToggleClicked(bool &toggleValue) { unselectAll(); @@ -186,3 +188,5 @@ void ofxRadioList::onToggleClicked(bool &toggleValue) } } } + + }} diff --git a/src/ui/RadioList.h b/src/ui/RadioList.h index 2736352..0d1daea 100644 --- a/src/ui/RadioList.h +++ b/src/ui/RadioList.h @@ -5,13 +5,15 @@ #include "ofxToggle.h" #include "ofxLabel.h" -class ofxRadioList +namespace ofx{ + namespace piMapper{ +class RadioList { public: - ofxRadioList(); - ofxRadioList(vector &labels); - ofxRadioList(string title, vector &labels); - ~ofxRadioList(); + RadioList(); + RadioList(vector &labels); + RadioList(string title, vector &labels); + ~RadioList(); void setup(vector &labels); void setup(string title, vector &labels); @@ -32,7 +34,7 @@ public: int size(); // This event notifies about a toggle being selected and passes it's name to the listeners. - // Use ofAddListener(ofxRadioListInstance.radioSelectedEvent, listenerClassPtr, &listenerClass::listenerMethod) + // Use ofAddListener(RadioListInstance.radioSelectedEvent, listenerClassPtr, &listenerClass::listenerMethod) // to listen to this. Listner method void listenerMethod(string & radioName) ofEvent radioSelectedEvent; @@ -43,4 +45,6 @@ private: int storedSelectedItem; void onToggleClicked(bool &toggleValue); -}; \ No newline at end of file +}; + + }} \ No newline at end of file From 6d44d1cae79a9e5cc1a1e358716794fac2cf5b26 Mon Sep 17 00:00:00 2001 From: Felix Dubrownik Date: Sun, 7 Sep 2014 22:08:39 +0200 Subject: [PATCH 17/73] ran clang Format with google code Style --- example/src/ofApp.cpp | 307 +++++++------- example/src/ofApp.h | 41 +- src/BaseJoint.cpp | 126 +++--- src/BaseJoint.h | 89 +++-- src/BaseSurface.cpp | 119 +++--- src/BaseSurface.h | 65 ++- src/CircleJoint.cpp | 85 ++-- src/CircleJoint.h | 31 +- src/EditorType.h | 16 +- src/GuiMode.h | 17 +- src/ProjectionEditor.cpp | 411 ++++++++++--------- src/ProjectionEditor.h | 89 ++--- src/QuadSurface.cpp | 473 +++++++++++----------- src/QuadSurface.h | 63 ++- src/SourcesEditor.cpp | 202 +++++----- src/SourcesEditor.h | 61 ++- src/SurfaceManager.cpp | 818 ++++++++++++++++++-------------------- src/SurfaceManager.h | 73 ++-- src/SurfaceManagerGui.cpp | 431 ++++++++++---------- src/SurfaceManagerGui.h | 64 ++- src/SurfaceType.h | 16 +- src/TextureEditor.cpp | 288 +++++++------- src/TextureEditor.h | 70 ++-- src/TriangleSurface.cpp | 233 +++++------ src/TriangleSurface.h | 49 ++- src/ui/RadioList.cpp | 267 ++++++------- src/ui/RadioList.h | 85 ++-- 27 files changed, 2194 insertions(+), 2395 deletions(-) diff --git a/example/src/ofApp.cpp b/example/src/ofApp.cpp index 18e5777..260ebfe 100755 --- a/example/src/ofApp.cpp +++ b/example/src/ofApp.cpp @@ -1,164 +1,183 @@ #include "ofApp.h" -void ofApp::setup() -{ - bShowInfo = false; - - // check if the surfaces.xml file is there - // if not - load defaultSurfaces.xml - if ( ofFile::doesFileExist("surfaces.xml") ) { - surfaceManager.loadXmlSettings("surfaces.xml"); - } else { - surfaceManager.loadXmlSettings("defaultSurfaces.xml"); - } - - // Pass the surface manager to the mapper graphical user interface - gui.setSurfaceManager( &surfaceManager ); - - // Create FBO - fbo = new ofFbo(); - fbo->allocate( 500, 500 ); - setFboAsTexture(); - - // Genereate rects - int numRects = 20; // change this to add more or less rects - for ( int i=0; igetHeight()), fbo->getWidth(), ofRandom(20)) ); - rectSpeeds.push_back( (1.0f + ofRandom(5)) ); - } +void ofApp::setup() { + bShowInfo = false; + + // check if the surfaces.xml file is there + // if not - load defaultSurfaces.xml + if (ofFile::doesFileExist("surfaces.xml")) { + surfaceManager.loadXmlSettings("surfaces.xml"); + } else { + surfaceManager.loadXmlSettings("defaultSurfaces.xml"); + } + + // Pass the surface manager to the mapper graphical user interface + gui.setSurfaceManager(&surfaceManager); + + // Create FBO + fbo = new ofFbo(); + fbo->allocate(500, 500); + setFboAsTexture(); + + // Genereate rects + int numRects = 20; // change this to add more or less rects + for (int i = 0; i < numRects; i++) { + rects.push_back(ofRectangle(0, ofRandom(fbo->getHeight()), fbo->getWidth(), + ofRandom(20))); + rectSpeeds.push_back((1.0f + ofRandom(5))); + } } -void ofApp::update() -{ - // Move rects - for ( int i=0; i fbo->getHeight() ) { - rects[i].y = -rects[i].getHeight(); - } - } - - // Fill FBO - fbo->begin(); - ofClear(0); - ofBackground(0); - ofSetColor(255); - for ( int i=0; i fbo->getHeight()) { + rects[i].y = -rects[i].getHeight(); } - fbo->end(); + } + + // Fill FBO + fbo->begin(); + ofClear(0); + ofBackground(0); + ofSetColor(255); + for (int i = 0; i < rects.size(); i++) { + ofRect(rects[i]); + } + fbo->end(); } -void ofApp::draw() -{ - // Draw the piMapper GUI - gui.draw(); - - if ( bShowInfo ) { - // Draw instructions - stringstream ss; - ss << "There are 4 modes:\n\n"; - ss << " 1. Presentation mode\n"; - ss << " 2. Texture mapping mode\n"; - ss << " 3. Projection mapping mode\n"; - ss << " 4. Source selection mode\n\n"; - ss << "You can switch between the modes by using <1>, <2>, <3> and <4> keys on the keyboard.\n\n"; - ss << "Press or to add random or normal surface.\n"; - ss << "Press to add a new quad surface.\n"; - ss << "Press to save the composition.\n"; - ss << "Press to toggle fullscreen.\n"; - ss << "Press to reassign the fbo texture to the first surface\n"; - ss << "Hit to hide this message."; - - ofDrawBitmapStringHighlight(ss.str(), 10, 20, ofColor(0,0,0,100), ofColor(255,255,255,200)); - } +void ofApp::draw() { + // Draw the piMapper GUI + gui.draw(); + + if (bShowInfo) { + // Draw instructions + stringstream ss; + ss << "There are 4 modes:\n\n"; + ss << " 1. Presentation mode\n"; + ss << " 2. Texture mapping mode\n"; + ss << " 3. Projection mapping mode\n"; + ss << " 4. Source selection mode\n\n"; + ss << "You can switch between the modes by using <1>, <2>, <3> and <4> " + "keys on the keyboard.\n\n"; + ss << "Press or to add random or normal surface.\n"; + ss << "Press to add a new quad surface.\n"; + ss << "Press to save the composition.\n"; + ss << "Press to toggle fullscreen.\n"; + ss << "Press to reassign the fbo texture to the first surface\n"; + ss << "Hit to hide this message."; + + ofDrawBitmapStringHighlight(ss.str(), 10, 20, ofColor(0, 0, 0, 100), + ofColor(255, 255, 255, 200)); + } } -void ofApp::exit() -{ - // Clear FBO from mem - delete fbo; +void ofApp::exit() { + // Clear FBO from mem + delete fbo; } -void ofApp::keyPressed(int key) -{ - cout << "Key pressed: " << static_cast(key) << endl; - - switch (key) { - case '1': gui.setMode(ofx::piMapper::GuiMode::NONE); break; - case '2': gui.setMode(ofx::piMapper::GuiMode::TEXTURE_MAPPING); break; - case '3': gui.setMode(ofx::piMapper::GuiMode::PROJECTION_MAPPING); break; - case '4': gui.setMode(ofx::piMapper::GuiMode::SOURCE_SELECTION); break; - case 'i': bShowInfo = !bShowInfo; break; - case 'r': addRandomSurface(); break; - case 'q': addQuadSurface(); break; - case 'n': addSurface(); break; - case 'f': ofToggleFullscreen(); break; - case 's': surfaceManager.saveXmlSettings("surfaces.xml"); break; - case 'a': setFboAsTexture(); break; - case OF_KEY_BACKSPACE: surfaceManager.removeSelectedSurface(); break; - default: break; - } +void ofApp::keyPressed(int key) { + cout << "Key pressed: " << static_cast(key) << endl; + + switch (key) { + case '1': + gui.setMode(ofx::piMapper::GuiMode::NONE); + break; + case '2': + gui.setMode(ofx::piMapper::GuiMode::TEXTURE_MAPPING); + break; + case '3': + gui.setMode(ofx::piMapper::GuiMode::PROJECTION_MAPPING); + break; + case '4': + gui.setMode(ofx::piMapper::GuiMode::SOURCE_SELECTION); + break; + case 'i': + bShowInfo = !bShowInfo; + break; + case 'r': + addRandomSurface(); + break; + case 'q': + addQuadSurface(); + break; + case 'n': + addSurface(); + break; + case 'f': + ofToggleFullscreen(); + break; + case 's': + surfaceManager.saveXmlSettings("surfaces.xml"); + break; + case 'a': + setFboAsTexture(); + break; + case OF_KEY_BACKSPACE: + surfaceManager.removeSelectedSurface(); + break; + default: + break; + } } -void ofApp::addRandomSurface() -{ - int surfaceType = ofx::piMapper::SurfaceType::TRIANGLE_SURFACE; - vector vertices; - vertices.push_back( ofVec2f( ofRandomWidth(), ofRandomHeight() ) ); - vertices.push_back( ofVec2f( ofRandomWidth(), ofRandomHeight() ) ); - vertices.push_back( ofVec2f( ofRandomWidth(), ofRandomHeight() ) ); - vector texCoords; - texCoords.push_back( ofVec2f( ofRandomuf(), ofRandomuf() ) ); - texCoords.push_back( ofVec2f( ofRandomuf(), ofRandomuf() ) ); - texCoords.push_back( ofVec2f( ofRandomuf(), ofRandomuf() ) ); - surfaceManager.addSurface(surfaceType, vertices, texCoords); - - // select this surface right away - surfaceManager.selectSurface(surfaceManager.size()-1); +void ofApp::addRandomSurface() { + int surfaceType = ofx::piMapper::SurfaceType::TRIANGLE_SURFACE; + vector vertices; + vertices.push_back(ofVec2f(ofRandomWidth(), ofRandomHeight())); + vertices.push_back(ofVec2f(ofRandomWidth(), ofRandomHeight())); + vertices.push_back(ofVec2f(ofRandomWidth(), ofRandomHeight())); + vector texCoords; + texCoords.push_back(ofVec2f(ofRandomuf(), ofRandomuf())); + texCoords.push_back(ofVec2f(ofRandomuf(), ofRandomuf())); + texCoords.push_back(ofVec2f(ofRandomuf(), ofRandomuf())); + surfaceManager.addSurface(surfaceType, vertices, texCoords); + + // select this surface right away + surfaceManager.selectSurface(surfaceManager.size() - 1); } -void ofApp::addQuadSurface() -{ - int surfaceType = ofx::piMapper::SurfaceType::QUAD_SURFACE; - vector vertices; - - int border = 50; - vertices.push_back(ofVec2f(border, border)); - vertices.push_back(ofVec2f(ofGetWidth() - border, border)); - vertices.push_back(ofVec2f(ofGetWidth() - border, ofGetHeight() - border)); - vertices.push_back(ofVec2f(border, ofGetHeight() - border)); - - vector texCoords; - texCoords.push_back(ofVec2f(ofVec2f(0.0f, 0.0f))); - texCoords.push_back(ofVec2f(ofVec2f(1.0f, 0.0f))); - texCoords.push_back(ofVec2f(ofVec2f(1.0f, 1.0f))); - texCoords.push_back(ofVec2f(ofVec2f(0.0f, 1.0f))); - - surfaceManager.addSurface(surfaceType, vertices, texCoords); - - // select this surface right away - surfaceManager.selectSurface(surfaceManager.size()-1); +void ofApp::addQuadSurface() { + int surfaceType = ofx::piMapper::SurfaceType::QUAD_SURFACE; + vector vertices; + + int border = 50; + vertices.push_back(ofVec2f(border, border)); + vertices.push_back(ofVec2f(ofGetWidth() - border, border)); + vertices.push_back(ofVec2f(ofGetWidth() - border, ofGetHeight() - border)); + vertices.push_back(ofVec2f(border, ofGetHeight() - border)); + + vector texCoords; + texCoords.push_back(ofVec2f(ofVec2f(0.0f, 0.0f))); + texCoords.push_back(ofVec2f(ofVec2f(1.0f, 0.0f))); + texCoords.push_back(ofVec2f(ofVec2f(1.0f, 1.0f))); + texCoords.push_back(ofVec2f(ofVec2f(0.0f, 1.0f))); + + surfaceManager.addSurface(surfaceType, vertices, texCoords); + + // select this surface right away + surfaceManager.selectSurface(surfaceManager.size() - 1); } -void ofApp::addSurface() -{ - int surfaceType = ofx::piMapper::SurfaceType::TRIANGLE_SURFACE; - vector vertices; - vertices.push_back( ofVec2f( (float)ofGetWidth()/2.0f, 0.0f ) ); - vertices.push_back( ofVec2f( (float)ofGetWidth(), (float)ofGetHeight() ) ); - vertices.push_back( ofVec2f( 0.0f, (float)ofGetHeight() ) ); - vector texCoords; - texCoords.push_back( ofVec2f( 0.5f, 0.0f ) ); - texCoords.push_back( ofVec2f( 1.0f, 1.0f ) ); - texCoords.push_back( ofVec2f( 0.0f, 1.0f ) ); - surfaceManager.addSurface(surfaceType, vertices, texCoords); - - // select this surface right away - surfaceManager.selectSurface(surfaceManager.size()-1); +void ofApp::addSurface() { + int surfaceType = ofx::piMapper::SurfaceType::TRIANGLE_SURFACE; + vector vertices; + vertices.push_back(ofVec2f((float)ofGetWidth() / 2.0f, 0.0f)); + vertices.push_back(ofVec2f((float)ofGetWidth(), (float)ofGetHeight())); + vertices.push_back(ofVec2f(0.0f, (float)ofGetHeight())); + vector texCoords; + texCoords.push_back(ofVec2f(0.5f, 0.0f)); + texCoords.push_back(ofVec2f(1.0f, 1.0f)); + texCoords.push_back(ofVec2f(0.0f, 1.0f)); + surfaceManager.addSurface(surfaceType, vertices, texCoords); + + // select this surface right away + surfaceManager.selectSurface(surfaceManager.size() - 1); } -void ofApp::setFboAsTexture() -{ - surfaceManager.getSurface(0)->setTexture( &fbo->getTextureReference() ); +void ofApp::setFboAsTexture() { + surfaceManager.getSurface(0)->setTexture(&fbo->getTextureReference()); } \ No newline at end of file diff --git a/example/src/ofApp.h b/example/src/ofApp.h index 10c1c99..1a81109 100755 --- a/example/src/ofApp.h +++ b/example/src/ofApp.h @@ -3,26 +3,25 @@ #include "ofMain.h" #include "ofxPiMapper.h" -class ofApp : public ofBaseApp -{ -public: - void setup(); - void update(); - void draw(); - void exit(); +class ofApp : public ofBaseApp { + public: + void setup(); + void update(); + void draw(); + void exit(); - void keyPressed(int key); - - void addRandomSurface(); - void addQuadSurface(); - void addSurface(); - void setFboAsTexture(); - - ofImage image; - ofx::piMapper::SurfaceManager surfaceManager; - ofx::piMapper::SurfaceManagerGui gui; - bool bShowInfo; - ofFbo* fbo; - vector rects; - vector rectSpeeds; + void keyPressed(int key); + + void addRandomSurface(); + void addQuadSurface(); + void addSurface(); + void setFboAsTexture(); + + ofImage image; + ofx::piMapper::SurfaceManager surfaceManager; + ofx::piMapper::SurfaceManagerGui gui; + bool bShowInfo; + ofFbo* fbo; + vector rects; + vector rectSpeeds; }; \ No newline at end of file diff --git a/src/BaseJoint.cpp b/src/BaseJoint.cpp index 19c490a..37b96b2 100644 --- a/src/BaseJoint.cpp +++ b/src/BaseJoint.cpp @@ -1,104 +1,72 @@ #include "BaseJoint.h" -namespace ofx{ -namespace piMapper{ - -BaseJoint::BaseJoint() -{ - setDefaultColors(); - setDefaultProperties(); - registerMouseEvents(); -} +namespace ofx { +namespace piMapper { -BaseJoint::~BaseJoint() -{ - unregisterMouseEvents(); +BaseJoint::BaseJoint() { + setDefaultColors(); + setDefaultProperties(); + registerMouseEvents(); } -void BaseJoint::registerMouseEvents() -{ - ofAddListener(ofEvents().mousePressed, this, &BaseJoint::mousePressed); - ofAddListener(ofEvents().mouseDragged, this, &BaseJoint::mouseDragged); -} +BaseJoint::~BaseJoint() { unregisterMouseEvents(); } -void BaseJoint::unregisterMouseEvents() -{ - ofRemoveListener(ofEvents().mousePressed, this, &BaseJoint::mousePressed); - ofRemoveListener(ofEvents().mouseDragged, this, &BaseJoint::mouseDragged); +void BaseJoint::registerMouseEvents() { + ofAddListener(ofEvents().mousePressed, this, &BaseJoint::mousePressed); + ofAddListener(ofEvents().mouseDragged, this, &BaseJoint::mouseDragged); } -void BaseJoint::mousePressed(ofMouseEventArgs& args) -{ - if ( hitTest(ofVec2f(args.x, args.y)) ) { - //selected = true; - clickDistance = position - ofVec2f(args.x, args.y); - //startDrag(); - } +void BaseJoint::unregisterMouseEvents() { + ofRemoveListener(ofEvents().mousePressed, this, &BaseJoint::mousePressed); + ofRemoveListener(ofEvents().mouseDragged, this, &BaseJoint::mouseDragged); } -void BaseJoint::mouseReleased(int x, int y, int button) -{ - stopDrag(); +void BaseJoint::mousePressed(ofMouseEventArgs& args) { + if (hitTest(ofVec2f(args.x, args.y))) { + // selected = true; + clickDistance = position - ofVec2f(args.x, args.y); + // startDrag(); + } } -void BaseJoint::mouseDragged(ofMouseEventArgs& args) -{ - if ( !bDrag ) return; - position = ofVec2f(args.x, args.y) + clickDistance; -} +void BaseJoint::mouseReleased(int x, int y, int button) { stopDrag(); } -void BaseJoint::startDrag() -{ - bDrag = true; +void BaseJoint::mouseDragged(ofMouseEventArgs& args) { + if (!bDrag) return; + position = ofVec2f(args.x, args.y) + clickDistance; } -void BaseJoint::stopDrag() -{ - bDrag = false; -} +void BaseJoint::startDrag() { bDrag = true; } -void BaseJoint::select() -{ - selected = true; -} +void BaseJoint::stopDrag() { bDrag = false; } -void BaseJoint::unselect() -{ - selected = false; -} +void BaseJoint::select() { selected = true; } -void BaseJoint::setClickDistance(ofVec2f newClickDistance) -{ - clickDistance = newClickDistance; -} +void BaseJoint::unselect() { selected = false; } -bool BaseJoint::isDragged() -{ - return bDrag; +void BaseJoint::setClickDistance(ofVec2f newClickDistance) { + clickDistance = newClickDistance; } -bool BaseJoint::isSelected() -{ - return selected; -} +bool BaseJoint::isDragged() { return bDrag; } -void BaseJoint::setDefaultColors() -{ - fillColor = ofColor(0, 255, 255, 0); - strokeColor = ofColor(255, 255, 255); - fillColorSelected = ofColor(255, 255, 0, 0); - strokeColorSelected = ofColor(255, 0, 0); +bool BaseJoint::isSelected() { return selected; } + +void BaseJoint::setDefaultColors() { + fillColor = ofColor(0, 255, 255, 0); + strokeColor = ofColor(255, 255, 255); + fillColorSelected = ofColor(255, 255, 0, 0); + strokeColorSelected = ofColor(255, 0, 0); } -void BaseJoint::setDefaultProperties() -{ - enabled = true; - visible = true; - position = ofVec2f(20.0f, 20.0f); - clickDistance = ofVec2f(0.0f, 0.0f); - bDrag = false; - selected = false; - strokeWidth = 1.5f; +void BaseJoint::setDefaultProperties() { + enabled = true; + visible = true; + position = ofVec2f(20.0f, 20.0f); + clickDistance = ofVec2f(0.0f, 0.0f); + bDrag = false; + selected = false; + strokeWidth = 1.5f; +} } - -}} \ No newline at end of file +} \ No newline at end of file diff --git a/src/BaseJoint.h b/src/BaseJoint.h index 5e5824d..d2fdcf0 100644 --- a/src/BaseJoint.h +++ b/src/BaseJoint.h @@ -2,49 +2,50 @@ #include "ofMain.h" -namespace ofx{ -namespace piMapper{ - - class BaseJoint { - public: - BaseJoint(); - ~BaseJoint(); - - void registerMouseEvents(); - void unregisterMouseEvents(); - - ofVec2f position; - bool enabled; - bool visible; - bool selected; - - void mousePressed(ofMouseEventArgs& args); - void mouseReleased(int x, int y, int button); - void mouseDragged(ofMouseEventArgs& args); - void startDrag(); - void stopDrag(); - void select(); - void unselect(); - void setClickDistance(ofVec2f newClickDistance); - bool isDragged(); - bool isSelected(); - - virtual void update(){}; - virtual void draw(){}; - virtual bool hitTest(ofVec2f position){}; - - protected: - ofColor fillColor; - ofColor strokeColor; - ofColor fillColorSelected; - ofColor strokeColorSelected; - float strokeWidth; - ofVec2f clickDistance; // How far from the center of the joint the user has clicked? - bool bDrag; - - private: - void setDefaultColors(); - void setDefaultProperties(); - }; +namespace ofx { +namespace piMapper { + +class BaseJoint { + public: + BaseJoint(); + ~BaseJoint(); + + void registerMouseEvents(); + void unregisterMouseEvents(); + + ofVec2f position; + bool enabled; + bool visible; + bool selected; + + void mousePressed(ofMouseEventArgs& args); + void mouseReleased(int x, int y, int button); + void mouseDragged(ofMouseEventArgs& args); + void startDrag(); + void stopDrag(); + void select(); + void unselect(); + void setClickDistance(ofVec2f newClickDistance); + bool isDragged(); + bool isSelected(); + + virtual void update() {}; + virtual void draw() {}; + virtual bool hitTest(ofVec2f position) {}; + + protected: + ofColor fillColor; + ofColor strokeColor; + ofColor fillColorSelected; + ofColor strokeColorSelected; + float strokeWidth; + ofVec2f clickDistance; // How far from the center of the joint the user has + // clicked? + bool bDrag; + + private: + void setDefaultColors(); + void setDefaultProperties(); +}; } } diff --git a/src/BaseSurface.cpp b/src/BaseSurface.cpp index 7350b29..b674cd8 100644 --- a/src/BaseSurface.cpp +++ b/src/BaseSurface.cpp @@ -1,77 +1,70 @@ #include "BaseSurface.h" -namespace ofx{ -namespace piMapper{ +namespace ofx { +namespace piMapper { -BaseSurface::BaseSurface() -{ - ofEnableNormalizedTexCoords(); - createDefaultTexture(); +BaseSurface::BaseSurface() { + ofEnableNormalizedTexCoords(); + createDefaultTexture(); } -void BaseSurface::createDefaultTexture() -{ - ofPixels pixels; - pixels.allocate(500, 500, 1); - for ( int i=0; igetWidth(), 0.0f)); - texMesh.addVertex(position + ofVec2f(texture->getWidth(), texture->getHeight())); - texMesh.addVertex(position + ofVec2f(0.0f, texture->getHeight())); - texMesh.addTriangle(0, 2, 3); - texMesh.addTriangle(0, 1, 2); - texMesh.addTexCoord(ofVec2f(0.0f, 0.0f)); - texMesh.addTexCoord(ofVec2f(1.0f, 0.0f)); - texMesh.addTexCoord(ofVec2f(1.0f, 1.0f)); - texMesh.addTexCoord(ofVec2f(0.0f, 1.0f)); - texture->bind(); - texMesh.draw(); - texture->unbind(); -} + // load pixels into texture + defaultTexture.loadData(pixels); -void BaseSurface::setTexture(ofTexture *texturePtr) -{ - texture = texturePtr; + // Assign default texture to texture pointer + texture = &defaultTexture; } -ofTexture* BaseSurface::getTexture() -{ - return texture; +void BaseSurface::drawTexture(ofVec2f position) { + ofMesh texMesh; + texMesh.addVertex(position); + texMesh.addVertex(position + ofVec2f(texture->getWidth(), 0.0f)); + texMesh.addVertex(position + + ofVec2f(texture->getWidth(), texture->getHeight())); + texMesh.addVertex(position + ofVec2f(0.0f, texture->getHeight())); + texMesh.addTriangle(0, 2, 3); + texMesh.addTriangle(0, 1, 2); + texMesh.addTexCoord(ofVec2f(0.0f, 0.0f)); + texMesh.addTexCoord(ofVec2f(1.0f, 0.0f)); + texMesh.addTexCoord(ofVec2f(1.0f, 1.0f)); + texMesh.addTexCoord(ofVec2f(0.0f, 1.0f)); + texture->bind(); + texMesh.draw(); + texture->unbind(); } -ofTexture* BaseSurface::getDefaultTexture() -{ - return &defaultTexture; -} +void BaseSurface::setTexture(ofTexture* texturePtr) { texture = texturePtr; } + +ofTexture* BaseSurface::getTexture() { return texture; } -}} \ No newline at end of file +ofTexture* BaseSurface::getDefaultTexture() { return &defaultTexture; } +} +} \ No newline at end of file diff --git a/src/BaseSurface.h b/src/BaseSurface.h index e8e5b8f..bf6703f 100644 --- a/src/BaseSurface.h +++ b/src/BaseSurface.h @@ -5,37 +5,36 @@ using namespace std; -namespace ofx{ - namespace piMapper{ -class BaseSurface -{ -public: - BaseSurface(); - virtual void setup(){}; - virtual void draw(){}; - virtual void setVertex(int index, ofVec2f p){}; - virtual void setTexCoord(int index, ofVec2f t){}; - virtual void moveBy(ofVec2f v){}; - virtual int getType(){}; - virtual bool hitTest(ofVec2f p){}; - virtual ofPolyline getHitArea(){}; - virtual ofPolyline getTextureHitArea(){}; - virtual vector& getVertices(){}; - virtual vector& getTexCoords(){}; - - // Draws a texture using ofMesh - void drawTexture(ofVec2f position); - void setTexture(ofTexture* texturePtr); - - ofTexture* getTexture(); - ofTexture* getDefaultTexture(); - -protected: - ofMesh mesh; - ofTexture* texture; - ofTexture defaultTexture; - - void createDefaultTexture(); -}; +namespace ofx { +namespace piMapper { +class BaseSurface { + public: + BaseSurface(); + virtual void setup() {}; + virtual void draw() {}; + virtual void setVertex(int index, ofVec2f p) {}; + virtual void setTexCoord(int index, ofVec2f t) {}; + virtual void moveBy(ofVec2f v) {}; + virtual int getType() {}; + virtual bool hitTest(ofVec2f p) {}; + virtual ofPolyline getHitArea() {}; + virtual ofPolyline getTextureHitArea() {}; + virtual vector& getVertices() {}; + virtual vector& getTexCoords() {}; + + // Draws a texture using ofMesh + void drawTexture(ofVec2f position); + void setTexture(ofTexture* texturePtr); + + ofTexture* getTexture(); + ofTexture* getDefaultTexture(); - }} \ No newline at end of file + protected: + ofMesh mesh; + ofTexture* texture; + ofTexture defaultTexture; + + void createDefaultTexture(); +}; +} +} \ No newline at end of file diff --git a/src/CircleJoint.cpp b/src/CircleJoint.cpp index 938659f..1e3bb3d 100644 --- a/src/CircleJoint.cpp +++ b/src/CircleJoint.cpp @@ -1,56 +1,49 @@ #include "CircleJoint.h" -namespace ofx{ -namespace piMapper{ +namespace ofx { +namespace piMapper { -CircleJoint::CircleJoint() -{ - setDefaultProperties(); -} +CircleJoint::CircleJoint() { setDefaultProperties(); } -void CircleJoint::update() -{ - if (!enabled) return; +void CircleJoint::update() { + if (!enabled) return; } -void CircleJoint::draw() -{ - if (!visible) return; - if (!enabled) return; - - ofPushStyle(); - ofFill(); - - if ( selected ) { - ofSetColor(fillColorSelected); - } else { - ofSetColor(fillColor); - } - - ofCircle(position.x, position.y, radius); - ofNoFill(); - - if ( selected ) { - ofSetColor(strokeColorSelected); - } else { - ofSetColor(strokeColor); - } - - ofSetLineWidth(strokeWidth); - ofCircle(position.x, position.y, radius); - ofPopStyle(); -} +void CircleJoint::draw() { + if (!visible) return; + if (!enabled) return; -void CircleJoint::setDefaultProperties() -{ - radius = 10.0f; -} + ofPushStyle(); + ofFill(); + + if (selected) { + ofSetColor(fillColorSelected); + } else { + ofSetColor(fillColor); + } + + ofCircle(position.x, position.y, radius); + ofNoFill(); -bool CircleJoint::hitTest(ofVec2f pos) -{ - float distance = position.distance(pos); - if ( distance < radius ) return true; - else return false; + if (selected) { + ofSetColor(strokeColorSelected); + } else { + ofSetColor(strokeColor); + } + + ofSetLineWidth(strokeWidth); + ofCircle(position.x, position.y, radius); + ofPopStyle(); } - }} \ No newline at end of file +void CircleJoint::setDefaultProperties() { radius = 10.0f; } + +bool CircleJoint::hitTest(ofVec2f pos) { + float distance = position.distance(pos); + if (distance < radius) + return true; + else + return false; +} +} +} \ No newline at end of file diff --git a/src/CircleJoint.h b/src/CircleJoint.h index 812bc3c..ade616f 100644 --- a/src/CircleJoint.h +++ b/src/CircleJoint.h @@ -3,21 +3,20 @@ #include "ofMain.h" #include "BaseJoint.h" +namespace ofx { +namespace piMapper { +class CircleJoint : public BaseJoint { + public: + CircleJoint(); -namespace ofx{ -namespace piMapper{ -class CircleJoint : public BaseJoint -{ -public: - CircleJoint(); - - void update(); - void draw(); - bool hitTest(ofVec2f position); - -private: - float radius; - - void setDefaultProperties(); + void update(); + void draw(); + bool hitTest(ofVec2f position); + + private: + float radius; + + void setDefaultProperties(); }; -}} \ No newline at end of file +} +} \ No newline at end of file diff --git a/src/EditorType.h b/src/EditorType.h index af5650b..b7fdc14 100644 --- a/src/EditorType.h +++ b/src/EditorType.h @@ -1,13 +1,9 @@ #pragma once -namespace ofx{ - namespace piMapper{ -struct EditorType -{ - enum { - TEXTURE, - PROJECTION - }; +namespace ofx { +namespace piMapper { +struct EditorType { + enum { TEXTURE, PROJECTION }; }; - -}} \ No newline at end of file +} +} \ No newline at end of file diff --git a/src/GuiMode.h b/src/GuiMode.h index 4c65a74..f6b1ac4 100644 --- a/src/GuiMode.h +++ b/src/GuiMode.h @@ -1,14 +1,9 @@ #pragma once -namespace ofx{ - namespace piMapper{ -struct GuiMode -{ - enum { - NONE, - TEXTURE_MAPPING, - PROJECTION_MAPPING, - SOURCE_SELECTION - }; +namespace ofx { +namespace piMapper { +struct GuiMode { + enum { NONE, TEXTURE_MAPPING, PROJECTION_MAPPING, SOURCE_SELECTION }; }; - }} \ No newline at end of file +} +} \ No newline at end of file diff --git a/src/ProjectionEditor.cpp b/src/ProjectionEditor.cpp index 12adcca..1851cd2 100644 --- a/src/ProjectionEditor.cpp +++ b/src/ProjectionEditor.cpp @@ -1,270 +1,263 @@ #include "ProjectionEditor.h" -namespace ofx{ - namespace piMapper{ -ProjectionEditor::ProjectionEditor() -{ - surfaceManager = NULL; - bShiftKeyDown = false; - fSnapDistance = 10.0f; - enable(); +namespace ofx { +namespace piMapper { +ProjectionEditor::ProjectionEditor() { + surfaceManager = NULL; + bShiftKeyDown = false; + fSnapDistance = 10.0f; + enable(); } -ProjectionEditor::~ProjectionEditor() -{ - clearJoints(); - surfaceManager = NULL; - disable(); +ProjectionEditor::~ProjectionEditor() { + clearJoints(); + surfaceManager = NULL; + disable(); } -void ProjectionEditor::registerAppEvents() -{ - ofAddListener(ofEvents().update, this, &ProjectionEditor::update); - ofAddListener(ofEvents().messageEvent, this, &ProjectionEditor::gotMessage); +void ProjectionEditor::registerAppEvents() { + ofAddListener(ofEvents().update, this, &ProjectionEditor::update); + ofAddListener(ofEvents().messageEvent, this, &ProjectionEditor::gotMessage); } -void ProjectionEditor::unregisterAppEvents() -{ - ofRemoveListener(ofEvents().update, this, &ProjectionEditor::update); - ofRemoveListener(ofEvents().messageEvent, this, &ProjectionEditor::gotMessage); +void ProjectionEditor::unregisterAppEvents() { + ofRemoveListener(ofEvents().update, this, &ProjectionEditor::update); + ofRemoveListener(ofEvents().messageEvent, this, + &ProjectionEditor::gotMessage); } -void ProjectionEditor::registerMouseEvents() -{ - ofAddListener(ofEvents().mouseDragged, this, &ProjectionEditor::mouseDragged); +void ProjectionEditor::registerMouseEvents() { + ofAddListener(ofEvents().mouseDragged, this, &ProjectionEditor::mouseDragged); } -void ProjectionEditor::unregisterMouseEvents() -{ - ofRemoveListener(ofEvents().mouseDragged, this, &ProjectionEditor::mouseDragged); +void ProjectionEditor::unregisterMouseEvents() { + ofRemoveListener(ofEvents().mouseDragged, this, + &ProjectionEditor::mouseDragged); } -void ProjectionEditor::registerKeyEvents() -{ - ofAddListener(ofEvents().keyPressed, this, &ProjectionEditor::keyPressed); - ofAddListener(ofEvents().keyReleased, this, &ProjectionEditor::keyReleased); +void ProjectionEditor::registerKeyEvents() { + ofAddListener(ofEvents().keyPressed, this, &ProjectionEditor::keyPressed); + ofAddListener(ofEvents().keyReleased, this, &ProjectionEditor::keyReleased); } -void ProjectionEditor::unregisterKeyEvents() -{ - ofRemoveListener(ofEvents().keyPressed, this, &ProjectionEditor::keyPressed); - ofRemoveListener(ofEvents().keyReleased, this, &ProjectionEditor::keyReleased); +void ProjectionEditor::unregisterKeyEvents() { + ofRemoveListener(ofEvents().keyPressed, this, &ProjectionEditor::keyPressed); + ofRemoveListener(ofEvents().keyReleased, this, + &ProjectionEditor::keyReleased); } -void ProjectionEditor::enable() -{ - registerAppEvents(); - registerMouseEvents(); - registerKeyEvents(); +void ProjectionEditor::enable() { + registerAppEvents(); + registerMouseEvents(); + registerKeyEvents(); } -void ProjectionEditor::disable() -{ - unregisterAppEvents(); - unregisterMouseEvents(); - unregisterKeyEvents(); +void ProjectionEditor::disable() { + unregisterAppEvents(); + unregisterMouseEvents(); + unregisterKeyEvents(); } -void ProjectionEditor::update(ofEventArgs &args) -{ - // update surface if one of the joints is being dragged - for ( int i=0; iisDragged() || joints[i]->isSelected() ) { - - if ( surfaceManager->getSelectedSurface() != NULL ) { - // update vertex to new location - surfaceManager->getSelectedSurface()->setVertex(i, joints[i]->position); - } else { - // clear joints if there is no surface selected - // as the remove selected surface in the surface manager - // is not supposed to access joints here - joints.clear(); - } - break; - } +void ProjectionEditor::update(ofEventArgs& args) { + // update surface if one of the joints is being dragged + for (int i = 0; i < joints.size(); i++) { + if (joints[i]->isDragged() || joints[i]->isSelected()) { + if (surfaceManager->getSelectedSurface() != NULL) { + // update vertex to new location + surfaceManager->getSelectedSurface()->setVertex(i, joints[i]->position); + } else { + // clear joints if there is no surface selected + // as the remove selected surface in the surface manager + // is not supposed to access joints here + joints.clear(); + } + break; } + } } -void ProjectionEditor::draw() -{ - if ( surfaceManager == NULL ) return; - if ( surfaceManager->getSelectedSurface() == NULL ) return; - if ( joints.size() <= 0 ) createJoints(); - drawJoints(); +void ProjectionEditor::draw() { + if (surfaceManager == NULL) return; + if (surfaceManager->getSelectedSurface() == NULL) return; + if (joints.size() <= 0) createJoints(); + drawJoints(); } -void ProjectionEditor::mouseDragged(ofMouseEventArgs &args) -{ - ofVec2f mousePosition = ofVec2f(args.x, args.y); - - // Collect all vertices of the projection surfaces - vector allVertices; - for ( int i=0; isize(); i++ ) { - BaseSurface* surface = surfaceManager->getSurface(i); - if ( surface == surfaceManager->getSelectedSurface() ) { - continue; // Don't add vertices of selected surface - } - for ( int j=0; jgetVertices().size(); j++ ) { - allVertices.push_back(&surface->getVertices()[j]); - } +void ProjectionEditor::mouseDragged(ofMouseEventArgs& args) { + ofVec2f mousePosition = ofVec2f(args.x, args.y); + + // Collect all vertices of the projection surfaces + vector allVertices; + for (int i = 0; i < surfaceManager->size(); i++) { + BaseSurface* surface = surfaceManager->getSurface(i); + if (surface == surfaceManager->getSelectedSurface()) { + continue; // Don't add vertices of selected surface + } + for (int j = 0; j < surface->getVertices().size(); j++) { + allVertices.push_back(&surface->getVertices()[j]); } - - // Snap currently dragged joint to nearest vertex - for ( int i=0; iisDragged() ) { - // Snap it! - for ( int j=0; jposition = *allVertices[j]; - ofVec2f clickDistance = joints[i]->position - ofVec2f(args.x, args.y); - joints[i]->setClickDistance(clickDistance); - break; - } - } + } + + // Snap currently dragged joint to nearest vertex + for (int i = 0; i < joints.size(); i++) { + if (joints[i]->isDragged()) { + // Snap it! + for (int j = 0; j < allVertices.size(); j++) { + float distance = mousePosition.distance(*allVertices[j]); + // cout << "distance: " << distance << endl; + if (distance < fSnapDistance) { + joints[i]->position = *allVertices[j]; + ofVec2f clickDistance = joints[i]->position - ofVec2f(args.x, args.y); + joints[i]->setClickDistance(clickDistance); + break; } + } } + } } -void ProjectionEditor::keyPressed(ofKeyEventArgs &args) -{ - int key = args.key; - float moveStep; - - if (bShiftKeyDown) moveStep = 10.0f; - else moveStep = 0.5f; - - switch (key) { - case OF_KEY_LEFT: moveSelection(ofVec2f(-moveStep,0.0f)); break; - case OF_KEY_RIGHT: moveSelection(ofVec2f(moveStep,0.0f)); break; - case OF_KEY_UP: moveSelection(ofVec2f(0.0f,-moveStep)); break; - case OF_KEY_DOWN: moveSelection(ofVec2f(0.0f,moveStep)); break; - case OF_KEY_SHIFT: bShiftKeyDown = true; break; - } +void ProjectionEditor::keyPressed(ofKeyEventArgs& args) { + int key = args.key; + float moveStep; + + if (bShiftKeyDown) + moveStep = 10.0f; + else + moveStep = 0.5f; + + switch (key) { + case OF_KEY_LEFT: + moveSelection(ofVec2f(-moveStep, 0.0f)); + break; + case OF_KEY_RIGHT: + moveSelection(ofVec2f(moveStep, 0.0f)); + break; + case OF_KEY_UP: + moveSelection(ofVec2f(0.0f, -moveStep)); + break; + case OF_KEY_DOWN: + moveSelection(ofVec2f(0.0f, moveStep)); + break; + case OF_KEY_SHIFT: + bShiftKeyDown = true; + break; + } } -void ProjectionEditor::keyReleased(ofKeyEventArgs &args) -{ - int key = args.key; - switch (key) { - case OF_KEY_SHIFT: bShiftKeyDown = false; break; - } +void ProjectionEditor::keyReleased(ofKeyEventArgs& args) { + int key = args.key; + switch (key) { + case OF_KEY_SHIFT: + bShiftKeyDown = false; + break; + } } -void ProjectionEditor::gotMessage(ofMessage& msg) -{ - if (msg.message == "surfaceSelected") { - // refresh gui - clearJoints(); - createJoints(); - } +void ProjectionEditor::gotMessage(ofMessage& msg) { + if (msg.message == "surfaceSelected") { + // refresh gui + clearJoints(); + createJoints(); + } } -void ProjectionEditor::setSurfaceManager(SurfaceManager *newSurfaceManager) -{ - surfaceManager = newSurfaceManager; +void ProjectionEditor::setSurfaceManager(SurfaceManager* newSurfaceManager) { + surfaceManager = newSurfaceManager; } -void ProjectionEditor::clearJoints() -{ - while ( joints.size() ) { - delete joints.back(); - joints.pop_back(); - } +void ProjectionEditor::clearJoints() { + while (joints.size()) { + delete joints.back(); + joints.pop_back(); + } } -void ProjectionEditor::createJoints() -{ - if ( surfaceManager == NULL ) return; - clearJoints(); - - if ( surfaceManager->getSelectedSurface() == NULL ) { - ofLog(OF_LOG_WARNING, "Trying to create joints while no surface selected."); - return; - } - - vector& vertices = surfaceManager->getSelectedSurface()->getVertices(); - - for ( int i=0; iposition = ofVec2f(vertices[i].x, vertices[i].y); - } +void ProjectionEditor::createJoints() { + if (surfaceManager == NULL) return; + clearJoints(); + + if (surfaceManager->getSelectedSurface() == NULL) { + ofLog(OF_LOG_WARNING, "Trying to create joints while no surface selected."); + return; + } + + vector& vertices = + surfaceManager->getSelectedSurface()->getVertices(); + + for (int i = 0; i < vertices.size(); i++) { + joints.push_back(new CircleJoint()); + joints.back()->position = ofVec2f(vertices[i].x, vertices[i].y); + } } -void ProjectionEditor::updateJoints() -{ - vector& vertices = surfaceManager->getSelectedSurface()->getVertices(); - for ( int i=0; iposition = ofVec2f(vertices[i].x, vertices[i].y); - } +void ProjectionEditor::updateJoints() { + vector& vertices = + surfaceManager->getSelectedSurface()->getVertices(); + for (int i = 0; i < vertices.size(); i++) { + joints[i]->position = ofVec2f(vertices[i].x, vertices[i].y); + } } -void ProjectionEditor::unselectAllJoints() -{ - for ( int i=0; iunselect(); - } +void ProjectionEditor::unselectAllJoints() { + for (int i = 0; i < joints.size(); i++) { + joints[i]->unselect(); + } } -void ProjectionEditor::moveSelectedSurface(ofVec2f by) -{ - if ( surfaceManager == NULL ) return; - if ( surfaceManager->getSelectedSurface() == NULL ) return; - surfaceManager->getSelectedSurface()->moveBy(by); - /*vector& vertices = surfaceManager->getSelectedSurface()->getVertices(); - for (int i=0; igetSelectedSurface() == NULL) return; + surfaceManager->getSelectedSurface()->moveBy(by); + /*vector& vertices = + surfaceManager->getSelectedSurface()->getVertices(); + for (int i=0; istopDrag(); - } +void ProjectionEditor::stopDragJoints() { + for (int i = 0; i < joints.size(); i++) { + joints[i]->stopDrag(); + } } -void ProjectionEditor::moveSelection(ofVec2f by) -{ - // check if joints selected - bool bJointSelected = false; - BaseJoint* selectedJoint; - for ( int i=0; iisSelected()) { - bJointSelected = true; - selectedJoint = joints[i]; - break; - } - } - - if ( bJointSelected ) { - selectedJoint->position += by; - } else { - moveSelectedSurface(by); +void ProjectionEditor::moveSelection(ofVec2f by) { + // check if joints selected + bool bJointSelected = false; + BaseJoint* selectedJoint; + for (int i = 0; i < joints.size(); i++) { + if (joints[i]->isSelected()) { + bJointSelected = true; + selectedJoint = joints[i]; + break; } + } + + if (bJointSelected) { + selectedJoint->position += by; + } else { + moveSelectedSurface(by); + } } -void ProjectionEditor::setSnapDistance(float newSnapDistance) -{ - fSnapDistance = newSnapDistance; +void ProjectionEditor::setSnapDistance(float newSnapDistance) { + fSnapDistance = newSnapDistance; } -CircleJoint* ProjectionEditor::hitTestJoints(ofVec2f pos) -{ - for ( int i=0; ihitTest(pos) ){ - return joints[i]; - } +CircleJoint* ProjectionEditor::hitTestJoints(ofVec2f pos) { + for (int i = 0; i < joints.size(); i++) { + if (joints[i]->hitTest(pos)) { + return joints[i]; } - return NULL; + } + return NULL; } -void ProjectionEditor::drawJoints() -{ - for ( int i=0; idraw(); - } +void ProjectionEditor::drawJoints() { + for (int i = 0; i < joints.size(); i++) { + joints[i]->draw(); + } +} } - }} \ No newline at end of file +} \ No newline at end of file diff --git a/src/ProjectionEditor.h b/src/ProjectionEditor.h index 6a7a877..50b8582 100755 --- a/src/ProjectionEditor.h +++ b/src/ProjectionEditor.h @@ -3,49 +3,48 @@ #include "SurfaceManager.h" #include "CircleJoint.h" -namespace ofx{ - namespace piMapper{ -class ProjectionEditor -{ -public: - ProjectionEditor(); - ~ProjectionEditor(); - - void registerAppEvents(); - void unregisterAppEvents(); - void registerMouseEvents(); - void unregisterMouseEvents(); - void registerKeyEvents(); - void unregisterKeyEvents(); - - void enable(); - void disable(); - - void update(ofEventArgs& args); - void draw(); - void mouseDragged(ofMouseEventArgs& args); - void keyPressed(ofKeyEventArgs& args); - void keyReleased(ofKeyEventArgs& args); - void gotMessage(ofMessage& msg); - void setSurfaceManager(SurfaceManager* newSurfaceManager); - void clearJoints(); - void createJoints(); - void updateJoints(); - void unselectAllJoints(); - void moveSelectedSurface(ofVec2f by); - void stopDragJoints(); - void updateVertices(); - void moveSelection(ofVec2f by); - void setSnapDistance(float newSnapDistance); - CircleJoint* hitTestJoints(ofVec2f pos); - -private: - SurfaceManager* surfaceManager; - vector joints; - bool bShiftKeyDown; - float fSnapDistance; - - void drawJoints(); -}; +namespace ofx { +namespace piMapper { +class ProjectionEditor { + public: + ProjectionEditor(); + ~ProjectionEditor(); + + void registerAppEvents(); + void unregisterAppEvents(); + void registerMouseEvents(); + void unregisterMouseEvents(); + void registerKeyEvents(); + void unregisterKeyEvents(); + + void enable(); + void disable(); - }} \ No newline at end of file + void update(ofEventArgs& args); + void draw(); + void mouseDragged(ofMouseEventArgs& args); + void keyPressed(ofKeyEventArgs& args); + void keyReleased(ofKeyEventArgs& args); + void gotMessage(ofMessage& msg); + void setSurfaceManager(SurfaceManager* newSurfaceManager); + void clearJoints(); + void createJoints(); + void updateJoints(); + void unselectAllJoints(); + void moveSelectedSurface(ofVec2f by); + void stopDragJoints(); + void updateVertices(); + void moveSelection(ofVec2f by); + void setSnapDistance(float newSnapDistance); + CircleJoint* hitTestJoints(ofVec2f pos); + + private: + SurfaceManager* surfaceManager; + vector joints; + bool bShiftKeyDown; + float fSnapDistance; + + void drawJoints(); +}; +} +} \ No newline at end of file diff --git a/src/QuadSurface.cpp b/src/QuadSurface.cpp index e71f45c..c72587c 100644 --- a/src/QuadSurface.cpp +++ b/src/QuadSurface.cpp @@ -1,266 +1,243 @@ #include "QuadSurface.h" -namespace ofx{ - namespace piMapper{ -QuadSurface::QuadSurface() -{ - cout << "QuadSurface constructor." << endl; - setup(); -} - -QuadSurface::~QuadSurface() -{ - cout << "QuadSurface destructor." << endl; -} - -void QuadSurface::setup() -{ - // Create 4 points for the 2 triangles - ofVec2f p1 = ofVec2f(0, 0); - ofVec2f p2 = ofVec2f(0, ofGetHeight()); - ofVec2f p3 = ofVec2f(ofGetWidth(), ofGetHeight()); - ofVec2f p4 = ofVec2f(ofGetWidth(), 0); - - // Create 4 point for the texture coordinates - ofVec2f t1 = ofVec2f(ofVec2f(0.0f, 0.0f)); - ofVec2f t2 = ofVec2f(ofVec2f(1.0f, 0.0f)); - ofVec2f t3 = ofVec2f(ofVec2f(1.0f, 1.0f)); - ofVec2f t4 = ofVec2f(ofVec2f(0.0f, 1.0f)); - - setup( p1, p2, p3, p4, t1, t2, t3, t4, texture ); -} - -void QuadSurface::setup( ofVec2f p1, ofVec2f p2, ofVec2f p3, ofVec2f p4, - ofVec2f t1, ofVec2f t2, ofVec2f t3, ofVec2f t4, ofTexture* texturePtr ) -{ - // Assign texture - texture = texturePtr; - - // Clear mesh - mesh.clear(); - - // Create a surface with the points - mesh.addVertex( p1 ); - mesh.addVertex( p2 ); - mesh.addVertex( p3 ); - mesh.addVertex( p4 ); - - // Add 2 triangles - mesh.addTriangle(0, 2, 3); - mesh.addTriangle(0, 1, 2); - - // Add texture coordinates - mesh.addTexCoord(t1); - mesh.addTexCoord(t2); - mesh.addTexCoord(t3); - mesh.addTexCoord(t4); - - // Pure GL setup - // indices - quadIndices[0] = 0; - quadIndices[1] = 1; - quadIndices[2] = 2; - quadIndices[3] = 0; - quadIndices[4] = 2; - quadIndices[5] = 3; - //tex coords (those are alway 0) - quadTexCoordinates[2] = 0; - quadTexCoordinates[6] = 0; - quadTexCoordinates[10] = 0; - quadTexCoordinates[14] = 0; - - calculate4dTextureCoords(); -} - -void QuadSurface::draw() -{ - /*if(mesh.haveVertsChanged() || mesh.haveTexCoordsChanged()){ - calculate4dTextureCoords(); - }*/ - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glTexCoordPointer(4, GL_FLOAT, 0, quadTexCoordinates); - glVertexPointer(3, GL_FLOAT, 0, quadVertices); - - texture->bind(); - glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, quadIndices); - texture->unbind(); -} +namespace ofx { +namespace piMapper { +QuadSurface::QuadSurface() { + cout << "QuadSurface constructor." << endl; + setup(); +} + +QuadSurface::~QuadSurface() { cout << "QuadSurface destructor." << endl; } + +void QuadSurface::setup() { + // Create 4 points for the 2 triangles + ofVec2f p1 = ofVec2f(0, 0); + ofVec2f p2 = ofVec2f(0, ofGetHeight()); + ofVec2f p3 = ofVec2f(ofGetWidth(), ofGetHeight()); + ofVec2f p4 = ofVec2f(ofGetWidth(), 0); + + // Create 4 point for the texture coordinates + ofVec2f t1 = ofVec2f(ofVec2f(0.0f, 0.0f)); + ofVec2f t2 = ofVec2f(ofVec2f(1.0f, 0.0f)); + ofVec2f t3 = ofVec2f(ofVec2f(1.0f, 1.0f)); + ofVec2f t4 = ofVec2f(ofVec2f(0.0f, 1.0f)); -void QuadSurface::setVertex(int index, ofVec2f p) -{ - if ( index > 3 ) { - ofLog() << "Vertex with this index does not exist: " << index << endl; - return; - } - - mesh.setVertex(index, p); - calculate4dTextureCoords(); -} + setup(p1, p2, p3, p4, t1, t2, t3, t4, texture); +} + +void QuadSurface::setup(ofVec2f p1, ofVec2f p2, ofVec2f p3, ofVec2f p4, + ofVec2f t1, ofVec2f t2, ofVec2f t3, ofVec2f t4, + ofTexture* texturePtr) { + // Assign texture + texture = texturePtr; -void QuadSurface::setTexCoord(int index, ofVec2f t) -{ - if ( index > 3 ) { - ofLog() << "Texture coordinate with this index does not exist: " << index << endl; - return; - } - - mesh.setTexCoord(index, t); - calculate4dTextureCoords(); -} + // Clear mesh + mesh.clear(); -void QuadSurface::moveBy(ofVec2f v) -{ - vector& vertices = getVertices(); - for (int i=0; i 3 ) { - ofLog() << "Vertex with this index does not exist: " << index << endl; - throw std::runtime_error("Vertex index out of bounds."); - } - - ofVec3f vert = mesh.getVertex(index); - return ofVec2f(vert.x, vert.y); -} + // Pure GL setup + // indices + quadIndices[0] = 0; + quadIndices[1] = 1; + quadIndices[2] = 2; + quadIndices[3] = 0; + quadIndices[4] = 2; + quadIndices[5] = 3; + // tex coords (those are alway 0) + quadTexCoordinates[2] = 0; + quadTexCoordinates[6] = 0; + quadTexCoordinates[10] = 0; + quadTexCoordinates[14] = 0; -ofVec2f QuadSurface::getTexCoord(int index) -{ - if (index > 3) { - throw std::runtime_error("Texture coordinate index out of bounds."); - } - - return mesh.getTexCoord(index); + calculate4dTextureCoords(); } -ofPolyline QuadSurface::getHitArea() -{ - ofPolyline line; - line.addVertex( ofPoint( mesh.getVertex(0).x, mesh.getVertex(0).y ) ); - line.addVertex( ofPoint( mesh.getVertex(1).x, mesh.getVertex(1).y ) ); - line.addVertex( ofPoint( mesh.getVertex(2).x, mesh.getVertex(2).y ) ); - line.addVertex( ofPoint( mesh.getVertex(3).x, mesh.getVertex(3).y ) ); - line.close(); - - return line; -} +void QuadSurface::draw() { + /*if(mesh.haveVertsChanged() || mesh.haveTexCoordsChanged()){ + calculate4dTextureCoords(); + }*/ + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(4, GL_FLOAT, 0, quadTexCoordinates); + glVertexPointer(3, GL_FLOAT, 0, quadVertices); -ofPolyline QuadSurface::getTextureHitArea() -{ - ofPolyline line; - vector& texCoords = mesh.getTexCoords(); - ofVec2f textureSize = ofVec2f(texture->getWidth(), texture->getHeight()); - for ( int i=0; ibind(); + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, quadIndices); + texture->unbind(); } -vector& QuadSurface::getVertices() -{ - // return only joint vertices - return mesh.getVertices(); -} +void QuadSurface::setVertex(int index, ofVec2f p) { + if (index > 3) { + ofLog() << "Vertex with this index does not exist: " << index << endl; + return; + } -vector& QuadSurface::getTexCoords() -{ - - return mesh.getTexCoords(); + mesh.setVertex(index, p); + calculate4dTextureCoords(); } -void QuadSurface::calculate4dTextureCoords() -{ - // Perspective Warping with OpenGL Fixed Pipeline and q coordinates - // see: - // http://www.reedbeta.com/blog/2012/05/26/quadrilateral-interpolation-part-1/ - // for information on the technique - // Pue OpenGL is used because the ofMesh sadly doesn't support ofVec4f as - // texture coordinates. - // calculate intersection point - ofVec3f p0 = mesh.getVertex(0); - ofVec3f p1 = mesh.getVertex(1); - ofVec3f p2 = mesh.getVertex(2); - ofVec3f p3 = mesh.getVertex(3); - - ofVec3f t0 = mesh.getTexCoord(0); - ofVec3f t1 = mesh.getTexCoord(1); - ofVec3f t2 = mesh.getTexCoord(2); - ofVec3f t3 = mesh.getTexCoord(3); - - ofPoint interSect; - ofLineSegmentIntersection(ofPoint(p0.x, p0.y), ofPoint(p2.x, p2.y), - ofPoint(p1.x, p1.y), ofPoint(p3.x, p3.y), - interSect); - ofVec3f interSecVec = ofVec3f(interSect.x, interSect.y, 0); - - // calculate distances to intersection point - float d0 = interSecVec.distance(p0); - float d1 = interSecVec.distance(p1); - float d2 = interSecVec.distance(p2); - float d3 = interSecVec.distance(p3); - - // vertices - // top left corner - quadVertices[0] = p0.x; - quadVertices[1] = p0.y; - quadVertices[2] = 0; - // top right corner - quadVertices[3] = p1.x; - quadVertices[4] = p1.y; - quadVertices[5] = 0; - // bottom right corner - quadVertices[6] = p2.x; - quadVertices[7] = p2.y; - quadVertices[8] = 0; - // bottom left corner - quadVertices[9] = p3.x; - quadVertices[10] = p3.y; - quadVertices[11] = 0; - - float q0 = (d0 + d2) / (d2); - float q1 = (d1 + d3) / (d3); - float q2 = (d2 + d0) / (d0); - float q3 = (d3 + d1) / (d1); - - quadTexCoordinates[0] = t0.x; - quadTexCoordinates[1] = t0.y; - quadTexCoordinates[3] = q0; - - quadTexCoordinates[4] = t1.x * q1; - quadTexCoordinates[5] = t1.y; - quadTexCoordinates[7] = q1; - - quadTexCoordinates[8] = t2.x * q2; - quadTexCoordinates[9] = t2.y * q2; - quadTexCoordinates[11] = q2; - - quadTexCoordinates[12] = t3.x; - quadTexCoordinates[13] = t3.y * q3; - quadTexCoordinates[15] = q3; - -} +void QuadSurface::setTexCoord(int index, ofVec2f t) { + if (index > 3) { + ofLog() << "Texture coordinate with this index does not exist: " << index + << endl; + return; + } + + mesh.setTexCoord(index, t); + calculate4dTextureCoords(); +} + +void QuadSurface::moveBy(ofVec2f v) { + vector& vertices = getVertices(); + for (int i = 0; i < vertices.size(); i++) { + vertices[i] += v; + } + calculate4dTextureCoords(); +} + +int QuadSurface::getType() { return SurfaceType::QUAD_SURFACE; } + +bool QuadSurface::hitTest(ofVec2f p) { + // Construct ofPolyline from vertices + ofPolyline line = getHitArea(); + + if (line.inside(p.x, p.y)) { + return true; + } else { + return false; + } +} + +ofVec2f QuadSurface::getVertex(int index) { + if (index > 3) { + ofLog() << "Vertex with this index does not exist: " << index << endl; + throw std::runtime_error("Vertex index out of bounds."); + } + + ofVec3f vert = mesh.getVertex(index); + return ofVec2f(vert.x, vert.y); +} + +ofVec2f QuadSurface::getTexCoord(int index) { + if (index > 3) { + throw std::runtime_error("Texture coordinate index out of bounds."); + } + + return mesh.getTexCoord(index); +} + +ofPolyline QuadSurface::getHitArea() { + ofPolyline line; + line.addVertex(ofPoint(mesh.getVertex(0).x, mesh.getVertex(0).y)); + line.addVertex(ofPoint(mesh.getVertex(1).x, mesh.getVertex(1).y)); + line.addVertex(ofPoint(mesh.getVertex(2).x, mesh.getVertex(2).y)); + line.addVertex(ofPoint(mesh.getVertex(3).x, mesh.getVertex(3).y)); + line.close(); + + return line; +} + +ofPolyline QuadSurface::getTextureHitArea() { + ofPolyline line; + vector& texCoords = mesh.getTexCoords(); + ofVec2f textureSize = ofVec2f(texture->getWidth(), texture->getHeight()); + for (int i = 0; i < texCoords.size(); i++) { + line.addVertex(ofPoint(texCoords[i] * textureSize)); + } + line.close(); + + return line; +} - }} \ No newline at end of file +vector& QuadSurface::getVertices() { + // return only joint vertices + return mesh.getVertices(); +} + +vector& QuadSurface::getTexCoords() { return mesh.getTexCoords(); } + +void QuadSurface::calculate4dTextureCoords() { + // Perspective Warping with OpenGL Fixed Pipeline and q coordinates + // see: + // http://www.reedbeta.com/blog/2012/05/26/quadrilateral-interpolation-part-1/ + // for information on the technique + // Pue OpenGL is used because the ofMesh sadly doesn't support ofVec4f as + // texture coordinates. + // calculate intersection point + ofVec3f p0 = mesh.getVertex(0); + ofVec3f p1 = mesh.getVertex(1); + ofVec3f p2 = mesh.getVertex(2); + ofVec3f p3 = mesh.getVertex(3); + + ofVec3f t0 = mesh.getTexCoord(0); + ofVec3f t1 = mesh.getTexCoord(1); + ofVec3f t2 = mesh.getTexCoord(2); + ofVec3f t3 = mesh.getTexCoord(3); + + ofPoint interSect; + ofLineSegmentIntersection(ofPoint(p0.x, p0.y), ofPoint(p2.x, p2.y), + ofPoint(p1.x, p1.y), ofPoint(p3.x, p3.y), + interSect); + ofVec3f interSecVec = ofVec3f(interSect.x, interSect.y, 0); + + // calculate distances to intersection point + float d0 = interSecVec.distance(p0); + float d1 = interSecVec.distance(p1); + float d2 = interSecVec.distance(p2); + float d3 = interSecVec.distance(p3); + + // vertices + // top left corner + quadVertices[0] = p0.x; + quadVertices[1] = p0.y; + quadVertices[2] = 0; + // top right corner + quadVertices[3] = p1.x; + quadVertices[4] = p1.y; + quadVertices[5] = 0; + // bottom right corner + quadVertices[6] = p2.x; + quadVertices[7] = p2.y; + quadVertices[8] = 0; + // bottom left corner + quadVertices[9] = p3.x; + quadVertices[10] = p3.y; + quadVertices[11] = 0; + + float q0 = (d0 + d2) / (d2); + float q1 = (d1 + d3) / (d3); + float q2 = (d2 + d0) / (d0); + float q3 = (d3 + d1) / (d1); + + quadTexCoordinates[0] = t0.x; + quadTexCoordinates[1] = t0.y; + quadTexCoordinates[3] = q0; + + quadTexCoordinates[4] = t1.x * q1; + quadTexCoordinates[5] = t1.y; + quadTexCoordinates[7] = q1; + + quadTexCoordinates[8] = t2.x * q2; + quadTexCoordinates[9] = t2.y * q2; + quadTexCoordinates[11] = q2; + + quadTexCoordinates[12] = t3.x; + quadTexCoordinates[13] = t3.y * q3; + quadTexCoordinates[15] = q3; +} +} +} \ No newline at end of file diff --git a/src/QuadSurface.h b/src/QuadSurface.h index 87469cb..223e367 100755 --- a/src/QuadSurface.h +++ b/src/QuadSurface.h @@ -4,38 +4,37 @@ #include "BaseSurface.h" #include "SurfaceType.h" -namespace ofx{ - namespace piMapper{ -class QuadSurface : public BaseSurface -{ -public: - QuadSurface(); - ~QuadSurface(); - - void setup(); +namespace ofx { +namespace piMapper { +class QuadSurface : public BaseSurface { + public: + QuadSurface(); + ~QuadSurface(); - void setup( ofVec2f p1, ofVec2f p2, ofVec2f p3, ofVec2f p4, - ofVec2f t1, ofVec2f t2, ofVec2f t3, ofVec2f t4, - ofTexture* texturePtr ); + void setup(); - void draw(); - void setVertex( int index, ofVec2f p ); - void setTexCoord( int index, ofVec2f t ); - void moveBy(ofVec2f v); - - int getType(); - bool hitTest(ofVec2f p); - ofVec2f getVertex(int index); - ofVec2f getTexCoord(int index); - ofPolyline getHitArea(); - ofPolyline getTextureHitArea(); - vector& getVertices(); - vector& getTexCoords(); -private: - void calculate4dTextureCoords(); - GLfloat quadVertices[12]; - GLubyte quadIndices[6]; - GLfloat quadTexCoordinates[16]; -}; + void setup(ofVec2f p1, ofVec2f p2, ofVec2f p3, ofVec2f p4, ofVec2f t1, + ofVec2f t2, ofVec2f t3, ofVec2f t4, ofTexture* texturePtr); + + void draw(); + void setVertex(int index, ofVec2f p); + void setTexCoord(int index, ofVec2f t); + void moveBy(ofVec2f v); - }} \ No newline at end of file + int getType(); + bool hitTest(ofVec2f p); + ofVec2f getVertex(int index); + ofVec2f getTexCoord(int index); + ofPolyline getHitArea(); + ofPolyline getTextureHitArea(); + vector& getVertices(); + vector& getTexCoords(); + + private: + void calculate4dTextureCoords(); + GLfloat quadVertices[12]; + GLubyte quadIndices[6]; + GLfloat quadTexCoordinates[16]; +}; +} +} \ No newline at end of file diff --git a/src/SourcesEditor.cpp b/src/SourcesEditor.cpp index 05c9a83..452ddb9 100644 --- a/src/SourcesEditor.cpp +++ b/src/SourcesEditor.cpp @@ -1,139 +1,121 @@ #include "SourcesEditor.h" -namespace ofx{ - namespace piMapper{ -SourcesEditor::SourcesEditor() -{ - defImgDir = DEFAULT_IMAGES_DIR; - registerAppEvents(); +namespace ofx { +namespace piMapper { +SourcesEditor::SourcesEditor() { + defImgDir = DEFAULT_IMAGES_DIR; + registerAppEvents(); } -SourcesEditor::~SourcesEditor() -{ - unregisterAppEvents(); - delete gui; - while ( images.size() ) { - delete images.back(); - images.pop_back(); - } +SourcesEditor::~SourcesEditor() { + unregisterAppEvents(); + delete gui; + while (images.size()) { + delete images.back(); + images.pop_back(); + } } -void SourcesEditor::registerAppEvents() -{ - ofAddListener(ofEvents().setup, this, &SourcesEditor::setup); +void SourcesEditor::registerAppEvents() { + ofAddListener(ofEvents().setup, this, &SourcesEditor::setup); } -void SourcesEditor::unregisterAppEvents() -{ - ofRemoveListener(ofEvents().setup, this, &SourcesEditor::setup); +void SourcesEditor::unregisterAppEvents() { + ofRemoveListener(ofEvents().setup, this, &SourcesEditor::setup); } -void SourcesEditor::setup(ofEventArgs& args) -{ - gui = new RadioList(); - - // read directory contents - ofDirectory imgDir; - imgDir.listDir(defImgDir); - imgDir.sort(); - - vector vnames; - - for(int i = 0; i < (int)imgDir.size(); i++){ - //images[i].loadImage(imgDir.getPath(i)); - vnames.push_back(imgDir.getName(i)); - } - - gui->setup("Images", vnames); - gui->setPosition(20, 20); - ofAddListener(gui->radioSelectedEvent, this, &SourcesEditor::guiEvent); -} +void SourcesEditor::setup(ofEventArgs& args) { + gui = new RadioList(); -void SourcesEditor::draw() -{ - // Don't draw if there is no source selected - if ( surfaceManager->getSelectedSurface() == NULL ) { - return; - } - - gui->draw(); + // read directory contents + ofDirectory imgDir; + imgDir.listDir(defImgDir); + imgDir.sort(); + + vector vnames; + + for (int i = 0; i < (int)imgDir.size(); i++) { + // images[i].loadImage(imgDir.getPath(i)); + vnames.push_back(imgDir.getName(i)); + } + + gui->setup("Images", vnames); + gui->setPosition(20, 20); + ofAddListener(gui->radioSelectedEvent, this, &SourcesEditor::guiEvent); } -void SourcesEditor::loadImage( string name, string path ) -{ - images.push_back(new ofImage()); - images.back()->loadImage(path); - - imageNames.push_back(name); - - ofSendMessage("imageLoaded"); +void SourcesEditor::draw() { + // Don't draw if there is no source selected + if (surfaceManager->getSelectedSurface() == NULL) { + return; + } + + gui->draw(); } -void SourcesEditor::disable() -{ - gui->disable(); +void SourcesEditor::loadImage(string name, string path) { + images.push_back(new ofImage()); + images.back()->loadImage(path); + + imageNames.push_back(name); + + ofSendMessage("imageLoaded"); } -void SourcesEditor::enable() -{ - // Don't enable if there is no surface selected - if ( surfaceManager->getSelectedSurface() == NULL ) { - cout << "No surface selected. Not enable()ing source list." << endl; - return; - } - - gui->enable(); +void SourcesEditor::disable() { gui->disable(); } + +void SourcesEditor::enable() { + // Don't enable if there is no surface selected + if (surfaceManager->getSelectedSurface() == NULL) { + cout << "No surface selected. Not enable()ing source list." << endl; + return; + } + + gui->enable(); } -void SourcesEditor::setSurfaceManager(SurfaceManager *newSurfaceManager) -{ - surfaceManager = newSurfaceManager; +void SourcesEditor::setSurfaceManager(SurfaceManager* newSurfaceManager) { + surfaceManager = newSurfaceManager; } -void SourcesEditor::selectImageSourceRadioButton(string name) -{ - if (name == "none") { - gui->unselectAll(); +void SourcesEditor::selectImageSourceRadioButton(string name) { + if (name == "none") { + gui->unselectAll(); + return; + } else { + int i; + for (i = 0; i < gui->size(); i++) { + if (gui->getItemName(i) == name) { + gui->selectItem(i); return; - } else { - int i; - for (i = 0; i < gui->size(); i++) { - if (gui->getItemName(i) == name) { - gui->selectItem(i); - return; - } - } + } } + } } -int SourcesEditor::getLoadedTexCount() -{ - return images.size(); -} +int SourcesEditor::getLoadedTexCount() { return images.size(); } -ofTexture* SourcesEditor::getTexture(int index) -{ - if (index >= images.size()){ - throw std::runtime_error("Texture index out of bounds."); - } - - return &images[index]->getTextureReference(); -} +ofTexture* SourcesEditor::getTexture(int index) { + if (index >= images.size()) { + throw std::runtime_error("Texture index out of bounds."); + } -void SourcesEditor::guiEvent(string &imageName) -{ - string name = imageName; - - if ( surfaceManager->getSelectedSurface() == NULL ) { - return; - } - - stringstream ss; - ss << defImgDir << name; - cout << "attempt to load image: " << ss.str() << endl; - ofTexture* texture = surfaceManager->loadImageSource(name, ss.str()); - surfaceManager->getSelectedSurface()->setTexture(texture); - surfaceManager->manageMemory(); + return &images[index]->getTextureReference(); } - }} \ No newline at end of file +void SourcesEditor::guiEvent(string& imageName) { + string name = imageName; + + if (surfaceManager->getSelectedSurface() == NULL) { + return; + } + + stringstream ss; + ss << defImgDir << name; + cout << "attempt to load image: " << ss.str() << endl; + ofTexture* texture = surfaceManager->loadImageSource(name, ss.str()); + surfaceManager->getSelectedSurface()->setTexture(texture); + surfaceManager->manageMemory(); +} +} +} \ No newline at end of file diff --git a/src/SourcesEditor.h b/src/SourcesEditor.h index 8311d1f..ee4462b 100644 --- a/src/SourcesEditor.h +++ b/src/SourcesEditor.h @@ -7,35 +7,34 @@ #define DEFAULT_IMAGES_DIR "sources/images/"; -namespace ofx{ - namespace piMapper{ -class SourcesEditor -{ -public: - SourcesEditor(); - ~SourcesEditor(); - - void registerAppEvents(); - void unregisterAppEvents(); - - void setup(ofEventArgs& args); - void draw(); - void loadImage( string name, string path ); - void disable(); - void enable(); - void setSurfaceManager(SurfaceManager* newSurfaceManager); - void selectImageSourceRadioButton(string name); - - int getLoadedTexCount(); - ofTexture* getTexture(int index); - -private: - SurfaceManager* surfaceManager; - RadioList* gui; - string defImgDir; - void guiEvent(string &imageName); - vector images; - vector imageNames; -}; +namespace ofx { +namespace piMapper { +class SourcesEditor { + public: + SourcesEditor(); + ~SourcesEditor(); + + void registerAppEvents(); + void unregisterAppEvents(); + + void setup(ofEventArgs& args); + void draw(); + void loadImage(string name, string path); + void disable(); + void enable(); + void setSurfaceManager(SurfaceManager* newSurfaceManager); + void selectImageSourceRadioButton(string name); - }} \ No newline at end of file + int getLoadedTexCount(); + ofTexture* getTexture(int index); + + private: + SurfaceManager* surfaceManager; + RadioList* gui; + string defImgDir; + void guiEvent(string& imageName); + vector images; + vector imageNames; +}; +} +} \ No newline at end of file diff --git a/src/SurfaceManager.cpp b/src/SurfaceManager.cpp index 48ac560..d29ca09 100644 --- a/src/SurfaceManager.cpp +++ b/src/SurfaceManager.cpp @@ -1,183 +1,168 @@ #include "SurfaceManager.h" -namespace ofx{ - namespace piMapper{ -SurfaceManager::SurfaceManager() -{ - +namespace ofx { +namespace piMapper { +SurfaceManager::SurfaceManager() {} + +SurfaceManager::~SurfaceManager() { clear(); } + +void SurfaceManager::draw() { + for (int i = 0; i < surfaces.size(); i++) { + surfaces[i]->draw(); + } } -SurfaceManager::~SurfaceManager() -{ - clear(); +void SurfaceManager::addSurface(int surfaceType) { + if (surfaceType == SurfaceType::TRIANGLE_SURFACE) { + surfaces.push_back(new TriangleSurface()); + } else if (surfaceType == SurfaceType::QUAD_SURFACE) { + surfaces.push_back(new QuadSurface()); + } else { + throw std::runtime_error("Attempt to add non-existing surface type."); + } } -void SurfaceManager::draw() -{ - for ( int i=0; idraw(); - } +void SurfaceManager::addSurface(int surfaceType, ofTexture* texturePtr) { + if (surfaceType == SurfaceType::TRIANGLE_SURFACE) { + surfaces.push_back(new TriangleSurface()); + surfaces.back()->setTexture(texturePtr); + } else if (surfaceType == SurfaceType::QUAD_SURFACE) { + surfaces.push_back(new QuadSurface()); + surfaces.back()->setTexture(texturePtr); + } else { + throw std::runtime_error("Attempt to add non-existing surface type."); + } } -void SurfaceManager::addSurface(int surfaceType) -{ - if ( surfaceType == SurfaceType::TRIANGLE_SURFACE ) { - surfaces.push_back( new TriangleSurface() ); - } - else if (surfaceType == SurfaceType::QUAD_SURFACE ) { - surfaces.push_back( new QuadSurface() ); +void SurfaceManager::addSurface(int surfaceType, vector vertices, + vector texCoords) { + if (surfaceType == SurfaceType::TRIANGLE_SURFACE) { + if (vertices.size() < 3) { + throw std::runtime_error( + "There must be 3 vertices for a triangle surface."); + } else if (texCoords.size() < 3) { + throw std::runtime_error( + "There must be 3 texture coordinates for a triangle surface."); } - else { - throw std::runtime_error("Attempt to add non-existing surface type."); + + surfaces.push_back(new TriangleSurface()); + + for (int i = 0; i < 3; i++) { + surfaces.back()->setVertex(i, vertices[i]); + surfaces.back()->setTexCoord(i, texCoords[i]); } -} -void SurfaceManager::addSurface(int surfaceType, ofTexture* texturePtr) -{ - if ( surfaceType == SurfaceType::TRIANGLE_SURFACE ) { - surfaces.push_back( new TriangleSurface() ); - surfaces.back()->setTexture(texturePtr); - } - else if (surfaceType == SurfaceType::QUAD_SURFACE ) { - surfaces.push_back( new QuadSurface() ); - surfaces.back()->setTexture(texturePtr); + } else if (surfaceType == SurfaceType::QUAD_SURFACE) { + if (vertices.size() < 4) { + throw std::runtime_error("There must be 4 vertices for a quad surface."); + } else if (texCoords.size() < 4) { + throw std::runtime_error( + "There must be 4 texture coordinates for a quad surface."); } - else { - throw std::runtime_error("Attempt to add non-existing surface type."); + + surfaces.push_back(new QuadSurface()); + + for (int i = 0; i < 4; i++) { + surfaces.back()->setVertex(i, vertices[i]); + surfaces.back()->setTexCoord(i, texCoords[i]); } + } else { + throw std::runtime_error("Attempt to add non-existing surface type."); + } } -void SurfaceManager::addSurface(int surfaceType, vector vertices, vector texCoords) -{ - if ( surfaceType == SurfaceType::TRIANGLE_SURFACE ) { - - if ( vertices.size() < 3 ) { - throw std::runtime_error("There must be 3 vertices for a triangle surface."); - } else if (texCoords.size() < 3) { - throw std::runtime_error("There must be 3 texture coordinates for a triangle surface."); - } - - surfaces.push_back( new TriangleSurface() ); - - for ( int i=0; i<3; i++ ) { - surfaces.back()->setVertex(i, vertices[i]); - surfaces.back()->setTexCoord(i, texCoords[i]); - } - - } - else if (surfaceType == SurfaceType::QUAD_SURFACE ) { - if ( vertices.size() < 4 ) { - throw std::runtime_error("There must be 4 vertices for a quad surface."); - } else if (texCoords.size() < 4) { - throw std::runtime_error("There must be 4 texture coordinates for a quad surface."); - } - - surfaces.push_back( new QuadSurface() ); - - for ( int i=0; i<4; i++ ) { - surfaces.back()->setVertex(i, vertices[i]); - surfaces.back()->setTexCoord(i, texCoords[i]); - } - } - else { - throw std::runtime_error("Attempt to add non-existing surface type."); +void SurfaceManager::addSurface(int surfaceType, ofTexture* texturePtr, + vector vertices, + vector texCoords) { + if (surfaceType == SurfaceType::TRIANGLE_SURFACE) { + if (vertices.size() < 3) { + throw std::runtime_error( + "There must be 3 vertices for a triangle surface."); + } else if (texCoords.size() < 3) { + throw std::runtime_error( + "Thre must be 3 texture coordinates for a triangle surface."); } -} + surfaces.push_back(new TriangleSurface()); + surfaces.back()->setTexture(texturePtr); -void SurfaceManager::addSurface(int surfaceType, ofTexture* texturePtr, vector vertices, vector texCoords) -{ - if ( surfaceType == SurfaceType::TRIANGLE_SURFACE ) { - - if ( vertices.size() < 3 ) { - throw std::runtime_error("There must be 3 vertices for a triangle surface."); - } else if (texCoords.size() < 3) { - throw std::runtime_error("Thre must be 3 texture coordinates for a triangle surface."); - } - - surfaces.push_back( new TriangleSurface() ); - surfaces.back()->setTexture(texturePtr); - - for ( int i=0; i<3; i++ ) { - surfaces.back()->setVertex(i, vertices[i]); - surfaces.back()->setTexCoord(i, texCoords[i]); - } - - } - else if (surfaceType == SurfaceType::QUAD_SURFACE ) { - if ( vertices.size() < 4 ) { - throw std::runtime_error("There must be 4 vertices for a quad surface."); - } else if (texCoords.size() < 4) { - throw std::runtime_error("Thre must be 4 texture coordinates for a quad surface."); - } - - surfaces.push_back( new QuadSurface() ); - surfaces.back()->setTexture(texturePtr); - - for ( int i=0; i<4; i++ ) { - surfaces.back()->setVertex(i, vertices[i]); - surfaces.back()->setTexCoord(i, texCoords[i]); - } + for (int i = 0; i < 3; i++) { + surfaces.back()->setVertex(i, vertices[i]); + surfaces.back()->setTexCoord(i, texCoords[i]); } - else { - throw std::runtime_error("Attempt to add non-existing surface type."); + + } else if (surfaceType == SurfaceType::QUAD_SURFACE) { + if (vertices.size() < 4) { + throw std::runtime_error("There must be 4 vertices for a quad surface."); + } else if (texCoords.size() < 4) { + throw std::runtime_error( + "Thre must be 4 texture coordinates for a quad surface."); } -} -void SurfaceManager::removeSelectedSurface() -{ - if ( selectedSurface == NULL ) return; - - for ( int i=0; isetTexture(texturePtr); + + for (int i = 0; i < 4; i++) { + surfaces.back()->setVertex(i, vertices[i]); + surfaces.back()->setTexCoord(i, texCoords[i]); } + } else { + throw std::runtime_error("Attempt to add non-existing surface type."); + } } -void SurfaceManager::manageMemory() -{ - // check if each of the sources is assigned to a surface or not - for ( int i=0; igetTexture() == &loadedImageSources[i]->getTextureReference() ) { - bAssigned = true; - break; - } - } - - if ( !bAssigned ) { - // purge the image source from memory - delete loadedImageSources[i]; - loadedImageSources.erase(loadedImageSources.begin()+i); - cout << "Deleting image source: " << loadedImageSourceNames[i] << endl; - loadedImageSourceNames.erase(loadedImageSourceNames.begin()+i); - i--; - } +void SurfaceManager::removeSelectedSurface() { + if (selectedSurface == NULL) return; + + for (int i = 0; i < surfaces.size(); i++) { + if (surfaces[i] == selectedSurface) { + delete surfaces[i]; + surfaces.erase(surfaces.begin() + i); + selectedSurface = NULL; + break; } + } } -void SurfaceManager::clear() -{ - // delete all extra allocations from the heap - while ( surfaces.size() ) { - delete surfaces.back(); - surfaces.pop_back(); - } - - while ( loadedImageSources.size() ) { - delete loadedImageSources.back(); - loadedImageSources.pop_back(); +void SurfaceManager::manageMemory() { + // check if each of the sources is assigned to a surface or not + for (int i = 0; i < loadedImageSources.size(); i++) { + bool bAssigned = false; + + for (int j = 0; j < surfaces.size(); j++) { + if (surfaces[j]->getTexture() == + &loadedImageSources[i]->getTextureReference()) { + bAssigned = true; + break; + } } - - while ( loadedImageSourceNames.size() ) { - loadedImageSourceNames.pop_back(); + + if (!bAssigned) { + // purge the image source from memory + delete loadedImageSources[i]; + loadedImageSources.erase(loadedImageSources.begin() + i); + cout << "Deleting image source: " << loadedImageSourceNames[i] << endl; + loadedImageSourceNames.erase(loadedImageSourceNames.begin() + i); + i--; } + } +} + +void SurfaceManager::clear() { + // delete all extra allocations from the heap + while (surfaces.size()) { + delete surfaces.back(); + surfaces.pop_back(); + } + + while (loadedImageSources.size()) { + delete loadedImageSources.back(); + loadedImageSources.pop_back(); + } + + while (loadedImageSourceNames.size()) { + loadedImageSourceNames.pop_back(); + } } // String getTypeString(SurfaceType e) @@ -190,299 +175,288 @@ void SurfaceManager::clear() // } // } -void SurfaceManager::saveXmlSettings(string fileName) -{ - xmlSettings.clear(); - - // save surfaces - xmlSettings.addTag("surfaces"); - xmlSettings.pushTag("surfaces"); - for ( int i=0; i* vertices = &surface->getVertices(); - for ( int j=0; jsize(); j++ ) { - xmlSettings.addTag("vertex"); - xmlSettings.pushTag("vertex", j); - ofVec3f* vertex = &(*vertices)[j]; - xmlSettings.addValue("x", vertex->x); - xmlSettings.addValue("y", vertex->y); - - // we don't need z as it will be 0 anyways - - xmlSettings.popTag(); // vertex - } - xmlSettings.popTag(); // vertices - - xmlSettings.addTag("texCoords"); - xmlSettings.pushTag("texCoords"); - vector* texCoords = &surface->getTexCoords(); - for ( int j=0; jsize(); j++ ) { - xmlSettings.addTag("texCoord"); - xmlSettings.pushTag("texCoord", j); - ofVec2f* texCoord = &(*texCoords)[j]; - xmlSettings.addValue("x", texCoord->x); - xmlSettings.addValue("y", texCoord->y); - xmlSettings.popTag(); // texCoord - } - xmlSettings.popTag(); // texCoords - - xmlSettings.addTag("source"); - xmlSettings.pushTag("source"); - - xmlSettings.addValue("source-type", "image"); - xmlSettings.addValue("source-name", getSurfaceSourceName(surface)); - //xmlSettings.addValue("source-path", "/root/etc/image.jpg"); - xmlSettings.popTag(); // source - - - // xmlSettings.addTag("type"); - // xmlSettings.pushTag("type"); - // // surfaceType == SurfaceType::TRIANGLE_SURFACE - // SurfaceType surfaceType = &surface->getType(); - // xmlSettings.addValue("surface-type", surfaceType); - // xmlSettings.popTag(); // type - - xmlSettings.popTag(); // surface +void SurfaceManager::saveXmlSettings(string fileName) { + xmlSettings.clear(); + + // save surfaces + xmlSettings.addTag("surfaces"); + xmlSettings.pushTag("surfaces"); + for (int i = 0; i < surfaces.size(); i++) { + xmlSettings.addTag("surface"); + xmlSettings.pushTag("surface", i); + BaseSurface* surface = surfaces[i]; + + xmlSettings.addTag("vertices"); + xmlSettings.pushTag("vertices"); + vector* vertices = &surface->getVertices(); + for (int j = 0; j < vertices->size(); j++) { + xmlSettings.addTag("vertex"); + xmlSettings.pushTag("vertex", j); + ofVec3f* vertex = &(*vertices)[j]; + xmlSettings.addValue("x", vertex->x); + xmlSettings.addValue("y", vertex->y); + + // we don't need z as it will be 0 anyways + + xmlSettings.popTag(); // vertex } - xmlSettings.popTag(); // surfaces - - xmlSettings.save(fileName); -} + xmlSettings.popTag(); // vertices + + xmlSettings.addTag("texCoords"); + xmlSettings.pushTag("texCoords"); + vector* texCoords = &surface->getTexCoords(); + for (int j = 0; j < texCoords->size(); j++) { + xmlSettings.addTag("texCoord"); + xmlSettings.pushTag("texCoord", j); + ofVec2f* texCoord = &(*texCoords)[j]; + xmlSettings.addValue("x", texCoord->x); + xmlSettings.addValue("y", texCoord->y); + xmlSettings.popTag(); // texCoord + } + xmlSettings.popTag(); // texCoords -void SurfaceManager::loadXmlSettings(string fileName) -{ + xmlSettings.addTag("source"); + xmlSettings.pushTag("source"); + xmlSettings.addValue("source-type", "image"); + xmlSettings.addValue("source-name", getSurfaceSourceName(surface)); + // xmlSettings.addValue("source-path", "/root/etc/image.jpg"); + xmlSettings.popTag(); // source - if (!xmlSettings.loadFile(fileName)){ - ofLog(OF_LOG_WARNING, "Could not load XML settings."); - return; + // xmlSettings.addTag("type"); + // xmlSettings.pushTag("type"); + // // surfaceType == SurfaceType::TRIANGLE_SURFACE + // SurfaceType surfaceType = &surface->getType(); + // xmlSettings.addValue("surface-type", surfaceType); + // xmlSettings.popTag(); // type + + xmlSettings.popTag(); // surface + } + xmlSettings.popTag(); // surfaces + + xmlSettings.save(fileName); +} + +void SurfaceManager::loadXmlSettings(string fileName) { + if (!xmlSettings.loadFile(fileName)) { + ofLog(OF_LOG_WARNING, "Could not load XML settings."); + return; + } + + if (!xmlSettings.tagExists("surfaces")) { + ofLog(OF_LOG_WARNING, "XML settings is empty or has wrong markup."); + return; + } + + xmlSettings.pushTag("surfaces"); + + int numSurfaces = xmlSettings.getNumTags("surface"); + for (int i = 0; i < numSurfaces; i++) { + xmlSettings.pushTag("surface", i); + // attempt to load surface source + xmlSettings.pushTag("source"); + string sourceType = xmlSettings.getValue("source-type", "image"); + string sourceName = xmlSettings.getValue("source-name", "none"); + ofTexture* sourceTexture = NULL; + if (sourceName != "none") { + stringstream ss; + ss << "sources/images/" << sourceName; // TODO: reuse constants here + sourceTexture = loadImageSource(sourceName, ss.str()); } - - if (!xmlSettings.tagExists("surfaces")){ - ofLog(OF_LOG_WARNING, "XML settings is empty or has wrong markup."); - return; + xmlSettings.popTag(); // source + + // // attempt to load surface type + // ofLog(OF_LOG_WARNING, "Attempt to load surface type."); + // xmlSettings.pushTag("type"); + // string surfaceType = xmlSettings.getValue("surface-type", + // "TRIANGLE_SURFACE"); + // xmlSettings.popTag(); // type + + xmlSettings.pushTag("vertices"); + vector vertices; + + int vertexCount = xmlSettings.getNumTags("vertex"); + + // it's a triangle ? + if (vertexCount == 3) { + ofLog(OF_LOG_NOTICE, "create Triangle"); + xmlSettings.pushTag("vertex", 0); + vertices.push_back(ofVec2f(xmlSettings.getValue("x", 0.0f), + xmlSettings.getValue("y", 0.0f))); + xmlSettings.popTag(); + + xmlSettings.pushTag("vertex", 1); + vertices.push_back(ofVec2f(xmlSettings.getValue("x", 100.0f), + xmlSettings.getValue("y", 0.0f))); + xmlSettings.popTag(); + + xmlSettings.pushTag("vertex", 2); + vertices.push_back(ofVec2f(xmlSettings.getValue("x", 0.0f), + xmlSettings.getValue("y", 100.0f))); + xmlSettings.popTag(); + + xmlSettings.popTag(); // vertices + + xmlSettings.pushTag("texCoords"); + + vector texCoords; + + xmlSettings.pushTag("texCoord", 0); + texCoords.push_back(ofVec2f(xmlSettings.getValue("x", 0.0f), + xmlSettings.getValue("y", 0.0f))); + xmlSettings.popTag(); + + xmlSettings.pushTag("texCoord", 1); + texCoords.push_back(ofVec2f(xmlSettings.getValue("x", 1.0f), + xmlSettings.getValue("y", 0.0f))); + xmlSettings.popTag(); + + xmlSettings.pushTag("texCoord", 2); + texCoords.push_back(ofVec2f(xmlSettings.getValue("x", 0.0f), + xmlSettings.getValue("y", 1.0f))); + xmlSettings.popTag(); + + xmlSettings.popTag(); // texCoords + + // now we have variables sourceName and sourceTexture + // by checking those we can use one or another addSurface method + if (sourceName != "none" && sourceTexture != NULL) { + addSurface(SurfaceType::TRIANGLE_SURFACE, sourceTexture, vertices, + texCoords); + } else { + addSurface(SurfaceType::TRIANGLE_SURFACE, vertices, texCoords); + } } - - xmlSettings.pushTag("surfaces"); - - int numSurfaces = xmlSettings.getNumTags("surface"); - for ( int i=0; i vertices; - - int vertexCount = xmlSettings.getNumTags("vertex"); - - - //it's a triangle ? - if (vertexCount == 3) - { - ofLog(OF_LOG_NOTICE, "create Triangle"); - xmlSettings.pushTag("vertex", 0); - vertices.push_back( ofVec2f( xmlSettings.getValue("x", 0.0f), xmlSettings.getValue("y", 0.0f) ) ); - xmlSettings.popTag(); - - xmlSettings.pushTag("vertex", 1); - vertices.push_back( ofVec2f( xmlSettings.getValue("x", 100.0f), xmlSettings.getValue("y", 0.0f) ) ); - xmlSettings.popTag(); - - xmlSettings.pushTag("vertex", 2); - vertices.push_back( ofVec2f( xmlSettings.getValue("x", 0.0f), xmlSettings.getValue("y", 100.0f) ) ); - xmlSettings.popTag(); - - xmlSettings.popTag(); // vertices - - xmlSettings.pushTag("texCoords"); - - vector texCoords; - - xmlSettings.pushTag("texCoord", 0); - texCoords.push_back( ofVec2f( xmlSettings.getValue("x", 0.0f), xmlSettings.getValue("y", 0.0f) ) ); - xmlSettings.popTag(); - - xmlSettings.pushTag("texCoord", 1); - texCoords.push_back( ofVec2f( xmlSettings.getValue("x", 1.0f), xmlSettings.getValue("y", 0.0f) ) ); - xmlSettings.popTag(); - - xmlSettings.pushTag("texCoord", 2); - texCoords.push_back( ofVec2f( xmlSettings.getValue("x", 0.0f), xmlSettings.getValue("y", 1.0f) ) ); - xmlSettings.popTag(); - - xmlSettings.popTag(); // texCoords - - - // now we have variables sourceName and sourceTexture - // by checking those we can use one or another addSurface method - if ( sourceName != "none" && sourceTexture != NULL ) { - addSurface(SurfaceType::TRIANGLE_SURFACE, sourceTexture, vertices, texCoords); - } else { - addSurface(SurfaceType::TRIANGLE_SURFACE, vertices, texCoords); - } - } - // it's a quad ? - else if (vertexCount == 4) - // if (surface-type == QUAD_SURFACE) - { - xmlSettings.pushTag("vertex", 0); - vertices.push_back( ofVec2f( xmlSettings.getValue("x", 0.0f), xmlSettings.getValue("y", 0.0f) ) ); - xmlSettings.popTag(); - - xmlSettings.pushTag("vertex", 1); - vertices.push_back( ofVec2f( xmlSettings.getValue("x", 100.0f), xmlSettings.getValue("y", 0.0f) ) ); - xmlSettings.popTag(); - - xmlSettings.pushTag("vertex", 2); - vertices.push_back( ofVec2f( xmlSettings.getValue("x", 100.0f), xmlSettings.getValue("y", 100.0f) ) ); - xmlSettings.popTag(); - - xmlSettings.pushTag("vertex", 3); - vertices.push_back( ofVec2f( xmlSettings.getValue("x", 0.0f), xmlSettings.getValue("y", 100.0f) ) ); - xmlSettings.popTag(); - - xmlSettings.popTag(); // vertices - - xmlSettings.pushTag("texCoords"); - - vector texCoords; - - xmlSettings.pushTag("texCoord", 0); - texCoords.push_back( ofVec2f( xmlSettings.getValue("x", 0.0f), xmlSettings.getValue("y", 0.0f) ) ); - xmlSettings.popTag(); - - xmlSettings.pushTag("texCoord", 1); - texCoords.push_back( ofVec2f( xmlSettings.getValue("x", 1.0f), xmlSettings.getValue("y", 0.0f) ) ); - xmlSettings.popTag(); - - xmlSettings.pushTag("texCoord", 2); - texCoords.push_back( ofVec2f( xmlSettings.getValue("x", 1.0f), xmlSettings.getValue("y", 1.0f) ) ); - xmlSettings.popTag(); - - xmlSettings.pushTag("texCoord", 3); - texCoords.push_back( ofVec2f( xmlSettings.getValue("x", 0.0f), xmlSettings.getValue("y", 1.0f) ) ); - xmlSettings.popTag(); - - xmlSettings.popTag(); // texCoords - - - // now we have variables sourceName and sourceTexture - // by checking those we can use one or another addSurface method - if ( sourceName != "none" && sourceTexture != NULL ) { - addSurface(SurfaceType::QUAD_SURFACE, sourceTexture, vertices, texCoords); - } else { - addSurface(SurfaceType::QUAD_SURFACE, vertices, texCoords); - } - - - } - - xmlSettings.popTag(); // surface + // it's a quad ? + else if (vertexCount == 4) + // if (surface-type == QUAD_SURFACE) + { + xmlSettings.pushTag("vertex", 0); + vertices.push_back(ofVec2f(xmlSettings.getValue("x", 0.0f), + xmlSettings.getValue("y", 0.0f))); + xmlSettings.popTag(); + + xmlSettings.pushTag("vertex", 1); + vertices.push_back(ofVec2f(xmlSettings.getValue("x", 100.0f), + xmlSettings.getValue("y", 0.0f))); + xmlSettings.popTag(); + + xmlSettings.pushTag("vertex", 2); + vertices.push_back(ofVec2f(xmlSettings.getValue("x", 100.0f), + xmlSettings.getValue("y", 100.0f))); + xmlSettings.popTag(); + + xmlSettings.pushTag("vertex", 3); + vertices.push_back(ofVec2f(xmlSettings.getValue("x", 0.0f), + xmlSettings.getValue("y", 100.0f))); + xmlSettings.popTag(); + + xmlSettings.popTag(); // vertices + + xmlSettings.pushTag("texCoords"); + + vector texCoords; + + xmlSettings.pushTag("texCoord", 0); + texCoords.push_back(ofVec2f(xmlSettings.getValue("x", 0.0f), + xmlSettings.getValue("y", 0.0f))); + xmlSettings.popTag(); + + xmlSettings.pushTag("texCoord", 1); + texCoords.push_back(ofVec2f(xmlSettings.getValue("x", 1.0f), + xmlSettings.getValue("y", 0.0f))); + xmlSettings.popTag(); + + xmlSettings.pushTag("texCoord", 2); + texCoords.push_back(ofVec2f(xmlSettings.getValue("x", 1.0f), + xmlSettings.getValue("y", 1.0f))); + xmlSettings.popTag(); + + xmlSettings.pushTag("texCoord", 3); + texCoords.push_back(ofVec2f(xmlSettings.getValue("x", 0.0f), + xmlSettings.getValue("y", 1.0f))); + xmlSettings.popTag(); + + xmlSettings.popTag(); // texCoords + + // now we have variables sourceName and sourceTexture + // by checking those we can use one or another addSurface method + if (sourceName != "none" && sourceTexture != NULL) { + addSurface(SurfaceType::QUAD_SURFACE, sourceTexture, vertices, + texCoords); + } else { + addSurface(SurfaceType::QUAD_SURFACE, vertices, texCoords); + } } - - xmlSettings.popTag(); // surfaces -} -BaseSurface* SurfaceManager::selectSurface(int index) -{ - if ( index >= surfaces.size() ) { - throw std::runtime_error("Surface index out of bounds."); - } - - selectedSurface = surfaces[index]; - - // notify that a new surface has been selected - ofSendMessage("surfaceSelected"); -} + xmlSettings.popTag(); // surface + } -BaseSurface* SurfaceManager::getSelectedSurface() -{ - return selectedSurface; + xmlSettings.popTag(); // surfaces } -void SurfaceManager::deselectSurface() -{ - selectedSurface = NULL; -} +BaseSurface* SurfaceManager::selectSurface(int index) { + if (index >= surfaces.size()) { + throw std::runtime_error("Surface index out of bounds."); + } -ofTexture* SurfaceManager::loadImageSource(string name, string path) -{ - // check if it is loaded - for ( int i=0; igetTextureReference(); - } - } - - // not loaded - load - ofImage* image = new ofImage(); - if ( !image->loadImage(path) ){ - return NULL; - } - loadedImageSources.push_back(image); - loadedImageSourceNames.push_back(name); - return &image->getTextureReference(); + selectedSurface = surfaces[index]; + + // notify that a new surface has been selected + ofSendMessage("surfaceSelected"); } -string SurfaceManager::getSelectedSurfaceSourceName() -{ - if ( selectedSurface == NULL ) { - return "none"; +BaseSurface* SurfaceManager::getSelectedSurface() { return selectedSurface; } + +void SurfaceManager::deselectSurface() { selectedSurface = NULL; } + +ofTexture* SurfaceManager::loadImageSource(string name, string path) { + // check if it is loaded + for (int i = 0; i < loadedImageSourceNames.size(); i++) { + if (loadedImageSourceNames[i] == name) { + // this image is already loaded + return &loadedImageSources[i]->getTextureReference(); } - - return getSurfaceSourceName( selectedSurface ); + } + + // not loaded - load + ofImage* image = new ofImage(); + if (!image->loadImage(path)) { + return NULL; + } + loadedImageSources.push_back(image); + loadedImageSourceNames.push_back(name); + return &image->getTextureReference(); } -string SurfaceManager::getSurfaceSourceName(BaseSurface *surface) -{ - ofTexture* tex = surface->getTexture(); - for ( int i=0; igetTextureReference()) { - return loadedImageSourceNames[i]; - } - } - +string SurfaceManager::getSelectedSurfaceSourceName() { + if (selectedSurface == NULL) { return "none"; + } + + return getSurfaceSourceName(selectedSurface); } -BaseSurface* SurfaceManager::getSurface(int index) -{ - if ( index >= surfaces.size() ) { - throw std::runtime_error("Surface index out of bounds."); - return NULL; +string SurfaceManager::getSurfaceSourceName(BaseSurface* surface) { + ofTexture* tex = surface->getTexture(); + for (int i = 0; i < loadedImageSources.size(); i++) { + if (tex == &loadedImageSources[i]->getTextureReference()) { + return loadedImageSourceNames[i]; } - - return surfaces[index]; -} + } -int SurfaceManager::size() -{ - return surfaces.size(); + return "none"; } - }} +BaseSurface* SurfaceManager::getSurface(int index) { + if (index >= surfaces.size()) { + throw std::runtime_error("Surface index out of bounds."); + return NULL; + } + + return surfaces[index]; +} +int SurfaceManager::size() { return surfaces.size(); } +} +} diff --git a/src/SurfaceManager.h b/src/SurfaceManager.h index 48df5c5..38af95f 100755 --- a/src/SurfaceManager.h +++ b/src/SurfaceManager.h @@ -10,42 +10,41 @@ using namespace std; +namespace ofx { +namespace piMapper { +class SurfaceManager { + public: + SurfaceManager(); + ~SurfaceManager(); -namespace ofx{ - namespace piMapper{ -class SurfaceManager -{ -public: - SurfaceManager(); - ~SurfaceManager(); - - void draw(); - void addSurface(int surfaceType); - void addSurface(int surfaceType, ofTexture* texturePtr); - void addSurface(int surfaceType, vector vertices, vector texCoords); - void addSurface(int surfaceType, ofTexture* texturePtr, vector vertices, vector texCoords); - void removeSelectedSurface(); - void manageMemory(); // deletes unasigned sources - void clear(); - void saveXmlSettings(string fileName); - void loadXmlSettings(string fileName); - - BaseSurface* getSurface(int index); - int size(); - BaseSurface* selectSurface(int index); - BaseSurface* getSelectedSurface(); - void deselectSurface(); - ofTexture* loadImageSource(string name, string path); - string getSelectedSurfaceSourceName(); - string getSurfaceSourceName( BaseSurface* surface ); - -private: - vector surfaces; - BaseSurface* selectedSurface; - vector loadedImageSourceNames; - vector loadedImageSources; - ofxXmlSettings xmlSettings; - -}; + void draw(); + void addSurface(int surfaceType); + void addSurface(int surfaceType, ofTexture* texturePtr); + void addSurface(int surfaceType, vector vertices, + vector texCoords); + void addSurface(int surfaceType, ofTexture* texturePtr, + vector vertices, vector texCoords); + void removeSelectedSurface(); + void manageMemory(); // deletes unasigned sources + void clear(); + void saveXmlSettings(string fileName); + void loadXmlSettings(string fileName); + + BaseSurface* getSurface(int index); + int size(); + BaseSurface* selectSurface(int index); + BaseSurface* getSelectedSurface(); + void deselectSurface(); + ofTexture* loadImageSource(string name, string path); + string getSelectedSurfaceSourceName(); + string getSurfaceSourceName(BaseSurface* surface); - }} \ No newline at end of file + private: + vector surfaces; + BaseSurface* selectedSurface; + vector loadedImageSourceNames; + vector loadedImageSources; + ofxXmlSettings xmlSettings; +}; +} +} \ No newline at end of file diff --git a/src/SurfaceManagerGui.cpp b/src/SurfaceManagerGui.cpp index e932810..ce73e99 100644 --- a/src/SurfaceManagerGui.cpp +++ b/src/SurfaceManagerGui.cpp @@ -1,258 +1,243 @@ #include "SurfaceManagerGui.h" -namespace ofx{ - namespace piMapper{ -SurfaceManagerGui::SurfaceManagerGui() -{ - surfaceManager = NULL; - guiMode = GuiMode::NONE; - bDrag = false; - registerMouseEvents(); - ofHideCursor(); +namespace ofx { +namespace piMapper { +SurfaceManagerGui::SurfaceManagerGui() { + surfaceManager = NULL; + guiMode = GuiMode::NONE; + bDrag = false; + registerMouseEvents(); + ofHideCursor(); } -SurfaceManagerGui::~SurfaceManagerGui() -{ - unregisterMouseEvents(); - surfaceManager = NULL; +SurfaceManagerGui::~SurfaceManagerGui() { + unregisterMouseEvents(); + surfaceManager = NULL; } -void SurfaceManagerGui::registerMouseEvents() -{ - ofAddListener(ofEvents().mousePressed, this, &SurfaceManagerGui::mousePressed); - ofAddListener(ofEvents().mouseReleased, this, &SurfaceManagerGui::mouseReleased); - ofAddListener(ofEvents().mouseDragged, this, &SurfaceManagerGui::mouseDragged); +void SurfaceManagerGui::registerMouseEvents() { + ofAddListener(ofEvents().mousePressed, this, + &SurfaceManagerGui::mousePressed); + ofAddListener(ofEvents().mouseReleased, this, + &SurfaceManagerGui::mouseReleased); + ofAddListener(ofEvents().mouseDragged, this, + &SurfaceManagerGui::mouseDragged); } -void SurfaceManagerGui::unregisterMouseEvents() -{ - ofRemoveListener(ofEvents().mousePressed, this, &SurfaceManagerGui::mousePressed); - ofRemoveListener(ofEvents().mouseReleased, this, &SurfaceManagerGui::mouseReleased); - ofRemoveListener(ofEvents().mouseDragged, this, &SurfaceManagerGui::mouseDragged); +void SurfaceManagerGui::unregisterMouseEvents() { + ofRemoveListener(ofEvents().mousePressed, this, + &SurfaceManagerGui::mousePressed); + ofRemoveListener(ofEvents().mouseReleased, this, + &SurfaceManagerGui::mouseReleased); + ofRemoveListener(ofEvents().mouseDragged, this, + &SurfaceManagerGui::mouseDragged); } -void SurfaceManagerGui::draw() -{ - if ( surfaceManager == NULL ) return; - - if ( guiMode == GuiMode::NONE ) { - surfaceManager->draw(); - } else if ( guiMode == GuiMode::TEXTURE_MAPPING ) { - - // draw the texture of the selected surface - if ( surfaceManager->getSelectedSurface() != NULL ) { - surfaceManager->getSelectedSurface()->drawTexture( ofVec2f(0,0) ); - } - - // draw surfaces with opacity - ofPushStyle(); - ofSetColor(255, 255, 255, 200); - surfaceManager->draw(); - ofPopStyle(); - - // highlight selected surface - drawSelectedSurfaceHighlight(); - - // hilight selected surface texture - drawSelectedSurfaceTextureHighlight(); - - // draw texture editing GUI on top - textureEditor.draw(); - - } else if ( guiMode == GuiMode::PROJECTION_MAPPING ) { - - // draw projection surfaces first - surfaceManager->draw(); - - // highlight selected surface - drawSelectedSurfaceHighlight(); - - // draw projection mapping editing gui - projectionEditor.draw(); - - } else if ( guiMode == GuiMode::SOURCE_SELECTION ) { - // draw projection surfaces first - surfaceManager->draw(); - - // highlight selected surface - drawSelectedSurfaceHighlight(); - - sourcesEditor.draw(); - } -} +void SurfaceManagerGui::draw() { + if (surfaceManager == NULL) return; -void SurfaceManagerGui::mousePressed(ofMouseEventArgs &args) -{ - if ( guiMode == GuiMode::NONE ) { - return; - } else if ( guiMode == GuiMode::TEXTURE_MAPPING ) { - - bool bSurfaceSelected = false; - - CircleJoint* hitJoint = textureEditor.hitTestJoints(ofVec2f(args.x, args.y)); - if ( hitJoint != NULL ) { - textureEditor.unselectAllJoints(); - hitJoint->select(); - hitJoint->startDrag(); - bSurfaceSelected = true; - } else { - textureEditor.unselectAllJoints(); - } - - if ( surfaceManager->getSelectedSurface() != NULL && !bSurfaceSelected ) { - // hittest texture area to see if we are hitting the texture surface - if ( surfaceManager->getSelectedSurface()->getTextureHitArea().inside(args.x, args.y) ) { - clickPosition = ofVec2f(args.x, args.y); - startDrag(); - } - } - - } else if ( guiMode == GuiMode::PROJECTION_MAPPING ) { - - bool bSurfaceSelected = false; - - CircleJoint* hitJoint = projectionEditor.hitTestJoints(ofVec2f(args.x, args.y)); - if ( hitJoint != NULL ) { - projectionEditor.unselectAllJoints(); - hitJoint->select(); - hitJoint->startDrag(); - bSurfaceSelected = true; - } - - // attempt to select surface, loop from end to beginning - if ( !bSurfaceSelected ){ - for ( int i=surfaceManager->size()-1; i>=0; i-- ) { - if ( surfaceManager->getSurface(i)->hitTest( ofVec2f(args.x, args.y) ) ) { - projectionEditor.clearJoints(); - surfaceManager->selectSurface(i); - projectionEditor.createJoints(); - bSurfaceSelected = true; - break; - } - } - } - - if ( bSurfaceSelected && hitJoint == NULL ) { - // if not hitting the joints, start drag only if we have a selected surface - clickPosition = ofVec2f(args.x, args.y); - startDrag(); - } - - if ( !bSurfaceSelected ) { - // unselect if no surface selected - projectionEditor.clearJoints(); - surfaceManager->deselectSurface(); - } - } else if ( guiMode == GuiMode::SOURCE_SELECTION ) { - + if (guiMode == GuiMode::NONE) { + surfaceManager->draw(); + } else if (guiMode == GuiMode::TEXTURE_MAPPING) { + // draw the texture of the selected surface + if (surfaceManager->getSelectedSurface() != NULL) { + surfaceManager->getSelectedSurface()->drawTexture(ofVec2f(0, 0)); } -} -void SurfaceManagerGui::mouseReleased(ofMouseEventArgs &args) -{ - stopDrag(); - projectionEditor.stopDragJoints(); - textureEditor.stopDragJoints(); + // draw surfaces with opacity + ofPushStyle(); + ofSetColor(255, 255, 255, 200); + surfaceManager->draw(); + ofPopStyle(); + + // highlight selected surface + drawSelectedSurfaceHighlight(); + + // hilight selected surface texture + drawSelectedSurfaceTextureHighlight(); + + // draw texture editing GUI on top + textureEditor.draw(); + + } else if (guiMode == GuiMode::PROJECTION_MAPPING) { + // draw projection surfaces first + surfaceManager->draw(); + + // highlight selected surface + drawSelectedSurfaceHighlight(); + + // draw projection mapping editing gui + projectionEditor.draw(); + + } else if (guiMode == GuiMode::SOURCE_SELECTION) { + // draw projection surfaces first + surfaceManager->draw(); + + // highlight selected surface + drawSelectedSurfaceHighlight(); + + sourcesEditor.draw(); + } } -void SurfaceManagerGui::mouseDragged(ofMouseEventArgs &args) -{ - if (bDrag) { - ofVec2f mousePosition = ofVec2f(args.x, args.y); - ofVec2f distance = mousePosition - clickPosition; - - if ( guiMode == GuiMode::PROJECTION_MAPPING ) { - // add this distance to all vertices in surface - projectionEditor.moveSelectedSurface(distance); - } else if ( guiMode == GuiMode::TEXTURE_MAPPING ) { - textureEditor.moveTexCoords(distance); - } - clickPosition = mousePosition; +void SurfaceManagerGui::mousePressed(ofMouseEventArgs& args) { + if (guiMode == GuiMode::NONE) { + return; + } else if (guiMode == GuiMode::TEXTURE_MAPPING) { + bool bSurfaceSelected = false; + + CircleJoint* hitJoint = + textureEditor.hitTestJoints(ofVec2f(args.x, args.y)); + if (hitJoint != NULL) { + textureEditor.unselectAllJoints(); + hitJoint->select(); + hitJoint->startDrag(); + bSurfaceSelected = true; + } else { + textureEditor.unselectAllJoints(); } -} -void SurfaceManagerGui::setSurfaceManager(SurfaceManager* newSurfaceManager) -{ - surfaceManager = newSurfaceManager; - projectionEditor.setSurfaceManager( surfaceManager ); - sourcesEditor.setSurfaceManager( surfaceManager ); -} + if (surfaceManager->getSelectedSurface() != NULL && !bSurfaceSelected) { + // hittest texture area to see if we are hitting the texture surface + if (surfaceManager->getSelectedSurface()->getTextureHitArea().inside( + args.x, args.y)) { + clickPosition = ofVec2f(args.x, args.y); + startDrag(); + } + } + + } else if (guiMode == GuiMode::PROJECTION_MAPPING) { + bool bSurfaceSelected = false; -void SurfaceManagerGui::setMode(int newGuiMode) -{ - if (newGuiMode != GuiMode::NONE && - newGuiMode != GuiMode::TEXTURE_MAPPING && - newGuiMode != GuiMode::PROJECTION_MAPPING && - newGuiMode != GuiMode::SOURCE_SELECTION) { - throw std::runtime_error("Trying to set invalid mode."); + CircleJoint* hitJoint = + projectionEditor.hitTestJoints(ofVec2f(args.x, args.y)); + if (hitJoint != NULL) { + projectionEditor.unselectAllJoints(); + hitJoint->select(); + hitJoint->startDrag(); + bSurfaceSelected = true; } - - if ( newGuiMode == GuiMode::NONE ) { - ofHideCursor(); - } else { - ofShowCursor(); + + // attempt to select surface, loop from end to beginning + if (!bSurfaceSelected) { + for (int i = surfaceManager->size() - 1; i >= 0; i--) { + if (surfaceManager->getSurface(i)->hitTest(ofVec2f(args.x, args.y))) { + projectionEditor.clearJoints(); + surfaceManager->selectSurface(i); + projectionEditor.createJoints(); + bSurfaceSelected = true; + break; + } + } } - - guiMode = newGuiMode; - - if ( guiMode == GuiMode::SOURCE_SELECTION ) { - sourcesEditor.enable(); - string sourceName = surfaceManager->getSelectedSurfaceSourceName(); - sourcesEditor.selectImageSourceRadioButton(sourceName); - } else { - sourcesEditor.disable(); + + if (bSurfaceSelected && hitJoint == NULL) { + // if not hitting the joints, start drag only if we have a selected + // surface + clickPosition = ofVec2f(args.x, args.y); + startDrag(); } - - if ( guiMode == GuiMode::TEXTURE_MAPPING ) { - textureEditor.enable(); - // refresh texture editor surface reference - textureEditor.setSurface(surfaceManager->getSelectedSurface()); - } else { - textureEditor.disable(); + + if (!bSurfaceSelected) { + // unselect if no surface selected + projectionEditor.clearJoints(); + surfaceManager->deselectSurface(); } - + } else if (guiMode == GuiMode::SOURCE_SELECTION) { + } +} + +void SurfaceManagerGui::mouseReleased(ofMouseEventArgs& args) { + stopDrag(); + projectionEditor.stopDragJoints(); + textureEditor.stopDragJoints(); +} + +void SurfaceManagerGui::mouseDragged(ofMouseEventArgs& args) { + if (bDrag) { + ofVec2f mousePosition = ofVec2f(args.x, args.y); + ofVec2f distance = mousePosition - clickPosition; + if (guiMode == GuiMode::PROJECTION_MAPPING) { - projectionEditor.enable(); - } else { - projectionEditor.disable(); + // add this distance to all vertices in surface + projectionEditor.moveSelectedSurface(distance); + } else if (guiMode == GuiMode::TEXTURE_MAPPING) { + textureEditor.moveTexCoords(distance); } + clickPosition = mousePosition; + } } -void SurfaceManagerGui::drawSelectedSurfaceHighlight() -{ - if ( surfaceManager->getSelectedSurface() == NULL ) return; - - ofPolyline line = surfaceManager->getSelectedSurface()->getHitArea(); - - ofPushStyle(); - ofSetLineWidth(1); - ofSetColor(255, 255, 255, 255); - line.draw(); - ofPopStyle(); +void SurfaceManagerGui::setSurfaceManager(SurfaceManager* newSurfaceManager) { + surfaceManager = newSurfaceManager; + projectionEditor.setSurfaceManager(surfaceManager); + sourcesEditor.setSurfaceManager(surfaceManager); } -void SurfaceManagerGui::drawSelectedSurfaceTextureHighlight() -{ - if ( surfaceManager->getSelectedSurface() == NULL ) return; - - ofPolyline line = surfaceManager->getSelectedSurface()->getTextureHitArea(); - - ofPushStyle(); - ofSetLineWidth(1); - ofSetColor(255, 255, 0, 255); - line.draw(); - ofPopStyle(); +void SurfaceManagerGui::setMode(int newGuiMode) { + if (newGuiMode != GuiMode::NONE && newGuiMode != GuiMode::TEXTURE_MAPPING && + newGuiMode != GuiMode::PROJECTION_MAPPING && + newGuiMode != GuiMode::SOURCE_SELECTION) { + throw std::runtime_error("Trying to set invalid mode."); + } + if (newGuiMode == GuiMode::NONE) { + ofHideCursor(); + } else { + ofShowCursor(); + } + + guiMode = newGuiMode; + + if (guiMode == GuiMode::SOURCE_SELECTION) { + sourcesEditor.enable(); + string sourceName = surfaceManager->getSelectedSurfaceSourceName(); + sourcesEditor.selectImageSourceRadioButton(sourceName); + } else { + sourcesEditor.disable(); + } + + if (guiMode == GuiMode::TEXTURE_MAPPING) { + textureEditor.enable(); + // refresh texture editor surface reference + textureEditor.setSurface(surfaceManager->getSelectedSurface()); + } else { + textureEditor.disable(); + } + + if (guiMode == GuiMode::PROJECTION_MAPPING) { + projectionEditor.enable(); + } else { + projectionEditor.disable(); + } } -void SurfaceManagerGui::startDrag() -{ - bDrag = true; +void SurfaceManagerGui::drawSelectedSurfaceHighlight() { + if (surfaceManager->getSelectedSurface() == NULL) return; + + ofPolyline line = surfaceManager->getSelectedSurface()->getHitArea(); + + ofPushStyle(); + ofSetLineWidth(1); + ofSetColor(255, 255, 255, 255); + line.draw(); + ofPopStyle(); } -void SurfaceManagerGui::stopDrag() -{ - bDrag = false; +void SurfaceManagerGui::drawSelectedSurfaceTextureHighlight() { + if (surfaceManager->getSelectedSurface() == NULL) return; + + ofPolyline line = surfaceManager->getSelectedSurface()->getTextureHitArea(); + + ofPushStyle(); + ofSetLineWidth(1); + ofSetColor(255, 255, 0, 255); + line.draw(); + ofPopStyle(); } - }} \ No newline at end of file +void SurfaceManagerGui::startDrag() { bDrag = true; } + +void SurfaceManagerGui::stopDrag() { bDrag = false; } +} +} \ No newline at end of file diff --git a/src/SurfaceManagerGui.h b/src/SurfaceManagerGui.h index 0dc8743..1c62cf6 100644 --- a/src/SurfaceManagerGui.h +++ b/src/SurfaceManagerGui.h @@ -12,37 +12,35 @@ #include "SourcesEditor.h" #include "GuiMode.h" -namespace ofx{ - namespace piMapper{ -class SurfaceManagerGui -{ -public: - SurfaceManagerGui(); - ~SurfaceManagerGui(); - - void registerMouseEvents(); - void unregisterMouseEvents(); - - void draw(); - void mousePressed(ofMouseEventArgs& args); - void mouseReleased(ofMouseEventArgs& args); - void mouseDragged(ofMouseEventArgs& args); - void setSurfaceManager(SurfaceManager* newSurfaceManager); - void setMode(int newGuiMode); - void drawSelectedSurfaceHighlight(); - void drawSelectedSurfaceTextureHighlight(); - void startDrag(); - void stopDrag(); - -private: - SurfaceManager* surfaceManager; - TextureEditor textureEditor; - ProjectionEditor projectionEditor; - SourcesEditor sourcesEditor; - int guiMode; - bool bDrag; - ofVec2f clickPosition; - -}; +namespace ofx { +namespace piMapper { +class SurfaceManagerGui { + public: + SurfaceManagerGui(); + ~SurfaceManagerGui(); + + void registerMouseEvents(); + void unregisterMouseEvents(); - }} \ No newline at end of file + void draw(); + void mousePressed(ofMouseEventArgs& args); + void mouseReleased(ofMouseEventArgs& args); + void mouseDragged(ofMouseEventArgs& args); + void setSurfaceManager(SurfaceManager* newSurfaceManager); + void setMode(int newGuiMode); + void drawSelectedSurfaceHighlight(); + void drawSelectedSurfaceTextureHighlight(); + void startDrag(); + void stopDrag(); + + private: + SurfaceManager* surfaceManager; + TextureEditor textureEditor; + ProjectionEditor projectionEditor; + SourcesEditor sourcesEditor; + int guiMode; + bool bDrag; + ofVec2f clickPosition; +}; +} +} \ No newline at end of file diff --git a/src/SurfaceType.h b/src/SurfaceType.h index 21deb9b..9db0cca 100755 --- a/src/SurfaceType.h +++ b/src/SurfaceType.h @@ -1,13 +1,9 @@ #pragma once -namespace ofx{ - namespace piMapper{ -struct SurfaceType -{ - enum { - TRIANGLE_SURFACE, - QUAD_SURFACE - }; +namespace ofx { +namespace piMapper { +struct SurfaceType { + enum { TRIANGLE_SURFACE, QUAD_SURFACE }; }; - - }} \ No newline at end of file +} +} \ No newline at end of file diff --git a/src/TextureEditor.cpp b/src/TextureEditor.cpp index a0a9eba..feb0728 100755 --- a/src/TextureEditor.cpp +++ b/src/TextureEditor.cpp @@ -1,195 +1,189 @@ #include "TextureEditor.h" -namespace ofx{ - namespace piMapper{ -TextureEditor::TextureEditor() -{ - clear(); - enable(); +namespace ofx { +namespace piMapper { +TextureEditor::TextureEditor() { + clear(); + enable(); } -TextureEditor::~TextureEditor() -{ - clear(); - disable(); +TextureEditor::~TextureEditor() { + clear(); + disable(); } -void TextureEditor::registerAppEvents() -{ - ofAddListener(ofEvents().update, this, &TextureEditor::update); +void TextureEditor::registerAppEvents() { + ofAddListener(ofEvents().update, this, &TextureEditor::update); } -void TextureEditor::unregisterAppEvents() -{ - ofRemoveListener(ofEvents().update, this, &TextureEditor::update); +void TextureEditor::unregisterAppEvents() { + ofRemoveListener(ofEvents().update, this, &TextureEditor::update); } -void TextureEditor::registerKeyEvents() -{ - ofAddListener(ofEvents().keyPressed, this, &TextureEditor::keyPressed); - ofAddListener(ofEvents().keyReleased, this, &TextureEditor::keyReleased); +void TextureEditor::registerKeyEvents() { + ofAddListener(ofEvents().keyPressed, this, &TextureEditor::keyPressed); + ofAddListener(ofEvents().keyReleased, this, &TextureEditor::keyReleased); } -void TextureEditor::unregisterKeyEvents() -{ - ofRemoveListener(ofEvents().keyPressed, this, &TextureEditor::keyPressed); - ofRemoveListener(ofEvents().keyReleased, this, &TextureEditor::keyReleased); +void TextureEditor::unregisterKeyEvents() { + ofRemoveListener(ofEvents().keyPressed, this, &TextureEditor::keyPressed); + ofRemoveListener(ofEvents().keyReleased, this, &TextureEditor::keyReleased); } -void TextureEditor::enable() -{ - registerAppEvents(); - registerKeyEvents(); - bShiftKeyDown = false; +void TextureEditor::enable() { + registerAppEvents(); + registerKeyEvents(); + bShiftKeyDown = false; } -void TextureEditor::disable() -{ - unregisterAppEvents(); - unregisterKeyEvents(); +void TextureEditor::disable() { + unregisterAppEvents(); + unregisterKeyEvents(); } -void TextureEditor::update(ofEventArgs &args) -{ - if ( surface == NULL ) return; - - // update surface if one of the joints is being dragged - ofVec2f textureSize = ofVec2f( surface->getTexture()->getWidth(), surface->getTexture()->getHeight() ); - for ( int i=0; iisDragged() || joints[i]->isSelected() ) { - // update vertex to new location - surface->setTexCoord(i, joints[i]->position / textureSize); - break; - } +void TextureEditor::update(ofEventArgs& args) { + if (surface == NULL) return; + + // update surface if one of the joints is being dragged + ofVec2f textureSize = ofVec2f(surface->getTexture()->getWidth(), + surface->getTexture()->getHeight()); + for (int i = 0; i < joints.size(); i++) { + if (joints[i]->isDragged() || joints[i]->isSelected()) { + // update vertex to new location + surface->setTexCoord(i, joints[i]->position / textureSize); + break; } + } } -void TextureEditor::keyPressed(ofKeyEventArgs &args) -{ - int key = args.key; - float moveStep; - - if (bShiftKeyDown) moveStep = 10.0f; - else moveStep = 0.5f; - - switch (key) { - case OF_KEY_LEFT: moveSelection(ofVec2f(-moveStep,0.0f)); break; - case OF_KEY_RIGHT: moveSelection(ofVec2f(moveStep,0.0f)); break; - case OF_KEY_UP: moveSelection(ofVec2f(0.0f,-moveStep)); break; - case OF_KEY_DOWN: moveSelection(ofVec2f(0.0f,moveStep)); break; - case OF_KEY_SHIFT: bShiftKeyDown = true; break; - } +void TextureEditor::keyPressed(ofKeyEventArgs& args) { + int key = args.key; + float moveStep; + + if (bShiftKeyDown) + moveStep = 10.0f; + else + moveStep = 0.5f; + + switch (key) { + case OF_KEY_LEFT: + moveSelection(ofVec2f(-moveStep, 0.0f)); + break; + case OF_KEY_RIGHT: + moveSelection(ofVec2f(moveStep, 0.0f)); + break; + case OF_KEY_UP: + moveSelection(ofVec2f(0.0f, -moveStep)); + break; + case OF_KEY_DOWN: + moveSelection(ofVec2f(0.0f, moveStep)); + break; + case OF_KEY_SHIFT: + bShiftKeyDown = true; + break; + } } -void TextureEditor::keyReleased(ofKeyEventArgs &args) -{ - int key = args.key; - switch (key) { - case OF_KEY_SHIFT: bShiftKeyDown = false; break; - } +void TextureEditor::keyReleased(ofKeyEventArgs& args) { + int key = args.key; + switch (key) { + case OF_KEY_SHIFT: + bShiftKeyDown = false; + break; + } } +void TextureEditor::draw() { + if (surface == NULL) return; -void TextureEditor::draw() -{ - if (surface == NULL) return; - - drawJoints(); + drawJoints(); } -void TextureEditor::drawJoints() -{ - for ( int i=0; idraw(); - } +void TextureEditor::drawJoints() { + for (int i = 0; i < joints.size(); i++) { + joints[i]->draw(); + } } -void TextureEditor::setSurface(BaseSurface* newSurface) -{ - surface = newSurface; - createJoints(); +void TextureEditor::setSurface(BaseSurface* newSurface) { + surface = newSurface; + createJoints(); } -void TextureEditor::clear() -{ - surface = NULL; - clearJoints(); +void TextureEditor::clear() { + surface = NULL; + clearJoints(); } -void TextureEditor::createJoints() -{ - if ( surface == NULL ) return; - clearJoints(); - vector& texCoords = surface->getTexCoords(); - ofVec2f textureSize = ofVec2f(surface->getTexture()->getWidth(), surface->getTexture()->getHeight()); - - for ( int i=0; iposition = texCoords[i] * textureSize; - } -} +void TextureEditor::createJoints() { + if (surface == NULL) return; + clearJoints(); + vector& texCoords = surface->getTexCoords(); + ofVec2f textureSize = ofVec2f(surface->getTexture()->getWidth(), + surface->getTexture()->getHeight()); -void TextureEditor::clearJoints() -{ - while ( joints.size() ) { - delete joints.back(); - joints.pop_back(); - } + for (int i = 0; i < texCoords.size(); i++) { + joints.push_back(new CircleJoint()); + joints.back()->position = texCoords[i] * textureSize; + } } -void TextureEditor::unselectAllJoints() -{ - for ( int i=0; iunselect(); - } +void TextureEditor::clearJoints() { + while (joints.size()) { + delete joints.back(); + joints.pop_back(); + } } -void TextureEditor::moveTexCoords(ofVec2f by) -{ - if ( surface == NULL ) return; - vector& texCoords = surface->getTexCoords(); - ofVec2f textureSize = ofVec2f( surface->getTexture()->getWidth(), surface->getTexture()->getHeight() ); - for (int i=0; iposition += by; - texCoords[i] = joints[i]->position / textureSize; - } +void TextureEditor::unselectAllJoints() { + for (int i = 0; i < joints.size(); i++) { + joints[i]->unselect(); + } } -void TextureEditor::stopDragJoints() -{ - for (int i=0; istopDrag(); - } +void TextureEditor::moveTexCoords(ofVec2f by) { + if (surface == NULL) return; + vector& texCoords = surface->getTexCoords(); + ofVec2f textureSize = ofVec2f(surface->getTexture()->getWidth(), + surface->getTexture()->getHeight()); + for (int i = 0; i < texCoords.size(); i++) { + joints[i]->position += by; + texCoords[i] = joints[i]->position / textureSize; + } } -void TextureEditor::moveSelection(ofVec2f by) -{ - // check if joints selected - bool bJointSelected = false; - BaseJoint* selectedJoint; - for ( int i=0; iisSelected()) { - bJointSelected = true; - selectedJoint = joints[i]; - break; - } - } - - if ( bJointSelected ) { - selectedJoint->position += by; - } else { - moveTexCoords(by); +void TextureEditor::stopDragJoints() { + for (int i = 0; i < joints.size(); i++) { + joints[i]->stopDrag(); + } +} + +void TextureEditor::moveSelection(ofVec2f by) { + // check if joints selected + bool bJointSelected = false; + BaseJoint* selectedJoint; + for (int i = 0; i < joints.size(); i++) { + if (joints[i]->isSelected()) { + bJointSelected = true; + selectedJoint = joints[i]; + break; } + } + + if (bJointSelected) { + selectedJoint->position += by; + } else { + moveTexCoords(by); + } } -CircleJoint* TextureEditor::hitTestJoints(ofVec2f pos) -{ - for ( int i=0; ihitTest(pos) ){ - return joints[i]; - } +CircleJoint* TextureEditor::hitTestJoints(ofVec2f pos) { + for (int i = 0; i < joints.size(); i++) { + if (joints[i]->hitTest(pos)) { + return joints[i]; } - return NULL; + } + return NULL; } - - }} \ No newline at end of file +} +} \ No newline at end of file diff --git a/src/TextureEditor.h b/src/TextureEditor.h index 1387984..cae4383 100644 --- a/src/TextureEditor.h +++ b/src/TextureEditor.h @@ -5,41 +5,39 @@ #include "BaseSurface.h" #include "CircleJoint.h" +namespace ofx { +namespace piMapper { +class TextureEditor { + public: + TextureEditor(); + ~TextureEditor(); -namespace ofx{ - namespace piMapper{ -class TextureEditor -{ -public: - TextureEditor(); - ~TextureEditor(); - - void registerAppEvents(); - void unregisterAppEvents(); - void registerKeyEvents(); - void unregisterKeyEvents(); - void enable(); - void disable(); - - void update(ofEventArgs& args); - void keyPressed(ofKeyEventArgs& args); - void keyReleased(ofKeyEventArgs& args); - void draw(); - void drawJoints(); - void setSurface(BaseSurface* newSurface); - void clear(); - void createJoints(); - void clearJoints(); - void unselectAllJoints(); - void moveTexCoords(ofVec2f by); - void stopDragJoints(); - void moveSelection(ofVec2f by); - CircleJoint* hitTestJoints(ofVec2f pos); - -private: - BaseSurface* surface; - vector joints; - bool bShiftKeyDown; -}; + void registerAppEvents(); + void unregisterAppEvents(); + void registerKeyEvents(); + void unregisterKeyEvents(); + void enable(); + void disable(); + + void update(ofEventArgs& args); + void keyPressed(ofKeyEventArgs& args); + void keyReleased(ofKeyEventArgs& args); + void draw(); + void drawJoints(); + void setSurface(BaseSurface* newSurface); + void clear(); + void createJoints(); + void clearJoints(); + void unselectAllJoints(); + void moveTexCoords(ofVec2f by); + void stopDragJoints(); + void moveSelection(ofVec2f by); + CircleJoint* hitTestJoints(ofVec2f pos); - }} \ No newline at end of file + private: + BaseSurface* surface; + vector joints; + bool bShiftKeyDown; +}; +} +} \ No newline at end of file diff --git a/src/TriangleSurface.cpp b/src/TriangleSurface.cpp index 950e360..b5d816e 100644 --- a/src/TriangleSurface.cpp +++ b/src/TriangleSurface.cpp @@ -1,158 +1,139 @@ #include "TriangleSurface.h" -namespace ofx{ - namespace piMapper{ -TriangleSurface::TriangleSurface() -{ - cout << "TriangleSurface constructor." << endl; - setup(); +namespace ofx { +namespace piMapper { +TriangleSurface::TriangleSurface() { + cout << "TriangleSurface constructor." << endl; + setup(); } -TriangleSurface::~TriangleSurface() -{ - cout << "TriangleSurface destructor." << endl; +TriangleSurface::~TriangleSurface() { + cout << "TriangleSurface destructor." << endl; } -void TriangleSurface::setup() -{ - // Create 3 points for the triangle - ofVec2f p1 = ofVec2f(ofGetWidth()/2.0f, 0); - ofVec2f p2 = ofVec2f(ofVec2f(0, ofGetHeight())); - ofVec2f p3 = ofVec2f(ofGetWidth(), ofGetHeight()); - - // Create 3 point for the texture coordinates - ofVec2f t1 = ofVec2f(0.5f, 0); - ofVec2f t2 = ofVec2f(0, 1.0f); - ofVec2f t3 = ofVec2f(1, 1.0f); - - setup( p1, p2, p3, t1, t2, t3, texture ); -} +void TriangleSurface::setup() { + // Create 3 points for the triangle + ofVec2f p1 = ofVec2f(ofGetWidth() / 2.0f, 0); + ofVec2f p2 = ofVec2f(ofVec2f(0, ofGetHeight())); + ofVec2f p3 = ofVec2f(ofGetWidth(), ofGetHeight()); -void TriangleSurface::setup( ofVec2f p1, ofVec2f p2, ofVec2f p3, ofVec2f t1, ofVec2f t2, ofVec2f t3, ofTexture* texturePtr ) -{ - // Assign texture - texture = texturePtr; - - // Clear mesh - mesh.clear(); - - // Create a surface with the points - mesh.addVertex( p1 ); - mesh.addVertex( p2 ); - mesh.addVertex( p3 ); - - // Add texture coordinates - mesh.addTexCoord(t1); - mesh.addTexCoord(t2); - mesh.addTexCoord(t3); -} + // Create 3 point for the texture coordinates + ofVec2f t1 = ofVec2f(0.5f, 0); + ofVec2f t2 = ofVec2f(0, 1.0f); + ofVec2f t3 = ofVec2f(1, 1.0f); -void TriangleSurface::draw() -{ - texture->bind(); - mesh.draw(); - texture->unbind(); + setup(p1, p2, p3, t1, t2, t3, texture); } -void TriangleSurface::setVertex(int index, ofVec2f p) -{ - if ( index > 2 ) { - ofLog() << "Vertex with this index does not exist: " << index << endl; - return; - } - - mesh.setVertex(index, p); +void TriangleSurface::setup(ofVec2f p1, ofVec2f p2, ofVec2f p3, ofVec2f t1, + ofVec2f t2, ofVec2f t3, ofTexture* texturePtr) { + // Assign texture + texture = texturePtr; + + // Clear mesh + mesh.clear(); + + // Create a surface with the points + mesh.addVertex(p1); + mesh.addVertex(p2); + mesh.addVertex(p3); + + // Add texture coordinates + mesh.addTexCoord(t1); + mesh.addTexCoord(t2); + mesh.addTexCoord(t3); } -void TriangleSurface::setTexCoord(int index, ofVec2f t) -{ - if ( index > 2 ) { - ofLog() << "Texture coordinate with this index does not exist: " << index << endl; - return; - } - - mesh.setTexCoord(index, t); +void TriangleSurface::draw() { + texture->bind(); + mesh.draw(); + texture->unbind(); } -void TriangleSurface::moveBy(ofVec2f v) -{ - vector& vertices = getVertices(); - for (int i=0; i 2) { + ofLog() << "Vertex with this index does not exist: " << index << endl; + return; + } + + mesh.setVertex(index, p); } -int TriangleSurface::getType() -{ - return SurfaceType::TRIANGLE_SURFACE; +void TriangleSurface::setTexCoord(int index, ofVec2f t) { + if (index > 2) { + ofLog() << "Texture coordinate with this index does not exist: " << index + << endl; + return; + } + + mesh.setTexCoord(index, t); } -bool TriangleSurface::hitTest(ofVec2f p) -{ - // Construct ofPolyline from vertices - ofPolyline line = getHitArea(); - - if ( line.inside(p.x, p.y) ) { - return true; - } else { - return false; - } +void TriangleSurface::moveBy(ofVec2f v) { + vector& vertices = getVertices(); + for (int i = 0; i < vertices.size(); i++) { + vertices[i] += v; + } } -ofVec2f TriangleSurface::getVertex(int index) -{ - if ( index > 2 ) { - ofLog() << "Vertex with this index does not exist: " << index << endl; - throw std::runtime_error("Vertex index out of bounds."); - } - - ofVec3f vert = mesh.getVertex(index); - return ofVec2f(vert.x, vert.y); +int TriangleSurface::getType() { return SurfaceType::TRIANGLE_SURFACE; } + +bool TriangleSurface::hitTest(ofVec2f p) { + // Construct ofPolyline from vertices + ofPolyline line = getHitArea(); + + if (line.inside(p.x, p.y)) { + return true; + } else { + return false; + } } -ofVec2f TriangleSurface::getTexCoord(int index) -{ - if (index > 2) { - throw std::runtime_error("Texture coordinate index out of bounds."); - } - - return mesh.getTexCoord(index); +ofVec2f TriangleSurface::getVertex(int index) { + if (index > 2) { + ofLog() << "Vertex with this index does not exist: " << index << endl; + throw std::runtime_error("Vertex index out of bounds."); + } + + ofVec3f vert = mesh.getVertex(index); + return ofVec2f(vert.x, vert.y); } -ofPolyline TriangleSurface::getHitArea() -{ - ofPolyline line; - line.addVertex( ofPoint( mesh.getVertex(0).x, mesh.getVertex(0).y ) ); - line.addVertex( ofPoint( mesh.getVertex(1).x, mesh.getVertex(1).y ) ); - line.addVertex( ofPoint( mesh.getVertex(2).x, mesh.getVertex(2).y ) ); - line.close(); - - return line; +ofVec2f TriangleSurface::getTexCoord(int index) { + if (index > 2) { + throw std::runtime_error("Texture coordinate index out of bounds."); + } + + return mesh.getTexCoord(index); } -ofPolyline TriangleSurface::getTextureHitArea() -{ - ofPolyline line; - vector& texCoords = mesh.getTexCoords(); - ofVec2f textureSize = ofVec2f(texture->getWidth(), texture->getHeight()); - for ( int i=0; i& TriangleSurface::getVertices() -{ - // return only joint vertices - return mesh.getVertices(); +ofPolyline TriangleSurface::getTextureHitArea() { + ofPolyline line; + vector& texCoords = mesh.getTexCoords(); + ofVec2f textureSize = ofVec2f(texture->getWidth(), texture->getHeight()); + for (int i = 0; i < texCoords.size(); i++) { + line.addVertex(ofPoint(texCoords[i] * textureSize)); + } + line.close(); + + return line; } -vector& TriangleSurface::getTexCoords() -{ - - return mesh.getTexCoords(); +vector& TriangleSurface::getVertices() { + // return only joint vertices + return mesh.getVertices(); } - }} \ No newline at end of file +vector& TriangleSurface::getTexCoords() { return mesh.getTexCoords(); } +} +} \ No newline at end of file diff --git a/src/TriangleSurface.h b/src/TriangleSurface.h index f1b2e74..c3e968c 100644 --- a/src/TriangleSurface.h +++ b/src/TriangleSurface.h @@ -4,30 +4,29 @@ #include "BaseSurface.h" #include "SurfaceType.h" +namespace ofx { +namespace piMapper { +class TriangleSurface : public BaseSurface { + public: + TriangleSurface(); + ~TriangleSurface(); -namespace ofx{ - namespace piMapper{ -class TriangleSurface : public BaseSurface -{ -public: - TriangleSurface(); - ~TriangleSurface(); - - void setup(); - void setup( ofVec2f p1, ofVec2f p2, ofVec2f p3, ofVec2f t1, ofVec2f t2, ofVec2f t3, ofTexture* texturePtr ); - void draw(); - void setVertex( int index, ofVec2f p ); - void setTexCoord( int index, ofVec2f t ); - void moveBy(ofVec2f v); - - int getType(); - bool hitTest(ofVec2f p); - ofVec2f getVertex(int index); - ofVec2f getTexCoord(int index); - ofPolyline getHitArea(); - ofPolyline getTextureHitArea(); - vector& getVertices(); - vector& getTexCoords(); -}; + void setup(); + void setup(ofVec2f p1, ofVec2f p2, ofVec2f p3, ofVec2f t1, ofVec2f t2, + ofVec2f t3, ofTexture* texturePtr); + void draw(); + void setVertex(int index, ofVec2f p); + void setTexCoord(int index, ofVec2f t); + void moveBy(ofVec2f v); - }} \ No newline at end of file + int getType(); + bool hitTest(ofVec2f p); + ofVec2f getVertex(int index); + ofVec2f getTexCoord(int index); + ofPolyline getHitArea(); + ofPolyline getTextureHitArea(); + vector& getVertices(); + vector& getTexCoords(); +}; +} +} \ No newline at end of file diff --git a/src/ui/RadioList.cpp b/src/ui/RadioList.cpp index a1d5cf5..c280edc 100644 --- a/src/ui/RadioList.cpp +++ b/src/ui/RadioList.cpp @@ -1,192 +1,155 @@ #include "RadioList.h" -namespace ofx{ - namespace piMapper{ -RadioList::RadioList() -{ - storedTitle = ""; - storedSelectedItem = 0; +namespace ofx { +namespace piMapper { +RadioList::RadioList() { + storedTitle = ""; + storedSelectedItem = 0; } -RadioList::RadioList(vector &labels) -{ - RadioList(); - setup(labels); +RadioList::RadioList(vector& labels) { + RadioList(); + setup(labels); } -RadioList::RadioList(string title, vector &labels) -{ - RadioList(); - setup(title, labels); +RadioList::RadioList(string title, vector& labels) { + RadioList(); + setup(title, labels); } -RadioList::~RadioList() -{ - clear(); -} +RadioList::~RadioList() { clear(); } -void RadioList::setup(vector &labels) -{ - // Copy incomming labels for later use - storedLabels = labels; - - // Create toggles with labels from the labels arg - int i; - for (i = 0; i < labels.size(); i++) { - ofxToggle* toggle = new ofxToggle(); - toggle->setup(false); - toggle->setName(labels[i]); - toggle->addListener(this, &RadioList::onToggleClicked); - guiGroup.add(toggle); - } - - cout << "num items: " << guiGroup.getNumControls() << endl; +void RadioList::setup(vector& labels) { + // Copy incomming labels for later use + storedLabels = labels; + + // Create toggles with labels from the labels arg + int i; + for (i = 0; i < labels.size(); i++) { + ofxToggle* toggle = new ofxToggle(); + toggle->setup(false); + toggle->setName(labels[i]); + toggle->addListener(this, &RadioList::onToggleClicked); + guiGroup.add(toggle); + } + + cout << "num items: " << guiGroup.getNumControls() << endl; } -void RadioList::setup(string title, vector &labels) -{ - // Store title for later use - storedTitle = title; - guiGroup.setName(title); - setup(labels); +void RadioList::setup(string title, vector& labels) { + // Store title for later use + storedTitle = title; + guiGroup.setName(title); + setup(labels); } -void RadioList::draw() -{ - guiGroup.draw(); +void RadioList::draw() { guiGroup.draw(); } + +void RadioList::setTitle(string title) { + storedTitle = title; + guiGroup.setName(title); } -void RadioList::setTitle(string title) -{ - storedTitle = title; - guiGroup.setName(title); +void RadioList::setPosition(ofPoint p) { guiGroup.setPosition(p); } + +void RadioList::setPosition(float x, float y) { guiGroup.setPosition(x, y); } + +void RadioList::selectItem(int index) { + if (index >= guiGroup.getNumControls()) { + return; + } + + unselectAll(); + + ofxToggle* toggle = static_cast(guiGroup.getControl(index)); + toggle->removeListener(this, &RadioList::onToggleClicked); + *toggle = true; // Select the specific radio button + toggle->addListener(this, &RadioList::onToggleClicked); + string name = toggle->getName(); + ofNotifyEvent(radioSelectedEvent, name, this); + + storedSelectedItem = index; } -void RadioList::setPosition(ofPoint p) -{ - guiGroup.setPosition(p); +void RadioList::enable() { + if (guiGroup.getNumControls() >= 0) { + clear(); + } + + // Rebuild everyting + setup(storedTitle, storedLabels); + + // Select the stored selected item without throwing an event + ofxToggle* toggle = + static_cast(guiGroup.getControl(storedSelectedItem)); + toggle->removeListener(this, &RadioList::onToggleClicked); + *toggle = true; + toggle->addListener(this, &RadioList::onToggleClicked); + + cout << "num items after enable: " << guiGroup.getNumControls() << endl; } -void RadioList::setPosition(float x, float y) -{ - guiGroup.setPosition(x, y); +void RadioList::disable() { + // Just remove everything + clear(); } -void RadioList::selectItem(int index) -{ - if (index >= guiGroup.getNumControls()) { - return; - } - - unselectAll(); - - ofxToggle* toggle = static_cast(guiGroup.getControl(index)); +void RadioList::clear() { + int i; + for (i = 0; i < guiGroup.getNumControls(); i++) { + ofxToggle* toggle = static_cast(guiGroup.getControl(i)); toggle->removeListener(this, &RadioList::onToggleClicked); - *toggle = true; // Select the specific radio button - toggle->addListener(this, &RadioList::onToggleClicked); - string name = toggle->getName(); - ofNotifyEvent(radioSelectedEvent, name, this); - - storedSelectedItem = index; + delete toggle; + } + guiGroup.clear(); } -void RadioList::enable() -{ - if (guiGroup.getNumControls() >= 0) { - clear(); - } - - // Rebuild everyting - setup(storedTitle, storedLabels); - - // Select the stored selected item without throwing an event - ofxToggle* toggle = static_cast(guiGroup.getControl(storedSelectedItem)); +void RadioList::unselectAll() { + int i; + for (i = 0; i < guiGroup.getNumControls(); i++) { + ofxToggle* toggle = static_cast(guiGroup.getControl(i)); + ofParameter* paramPtr = + static_cast*>(&toggle->getParameter()); toggle->removeListener(this, &RadioList::onToggleClicked); - *toggle = true; + *toggle = false; toggle->addListener(this, &RadioList::onToggleClicked); - - cout << "num items after enable: " << guiGroup.getNumControls() << endl; + } } -void RadioList::disable() -{ - // Just remove everything - clear(); -} +ofPoint RadioList::getPosition() { return guiGroup.getPosition(); } -void RadioList::clear() -{ - int i; - for (i = 0; i < guiGroup.getNumControls(); i++) { - ofxToggle* toggle = static_cast(guiGroup.getControl(i)); - toggle->removeListener(this, &RadioList::onToggleClicked); - delete toggle; - } - guiGroup.clear(); -} +float RadioList::getWidth() { return guiGroup.getWidth(); } -void RadioList::unselectAll() -{ - int i; - for (i = 0; i < guiGroup.getNumControls(); i++) { - ofxToggle* toggle = static_cast(guiGroup.getControl(i)); - ofParameter* paramPtr = static_cast*>(&toggle->getParameter()); - toggle->removeListener(this, &RadioList::onToggleClicked); - *toggle = false; - toggle->addListener(this, &RadioList::onToggleClicked); - } -} +float RadioList::getHeight() { return guiGroup.getHeight(); } -ofPoint RadioList::getPosition() -{ - return guiGroup.getPosition(); -} +string RadioList::getTitle() { return guiGroup.getName(); } -float RadioList::getWidth() -{ - return guiGroup.getWidth(); -} +string RadioList::getItemName(int index) { + if (index >= guiGroup.getNumControls()) { + return ""; + } -float RadioList::getHeight() -{ - return guiGroup.getHeight(); + ofxToggle* toggle = static_cast(guiGroup.getControl(index)); + return toggle->getName(); } -string RadioList::getTitle() -{ - return guiGroup.getName(); -} +int RadioList::size() { return guiGroup.getNumControls(); } -string RadioList::getItemName(int index) -{ - if (index >= guiGroup.getNumControls()) { - return ""; - } - - ofxToggle* toggle = static_cast(guiGroup.getControl(index)); - return toggle->getName(); -} +void RadioList::onToggleClicked(bool& toggleValue) { + unselectAll(); -int RadioList::size() -{ - return guiGroup.getNumControls(); -} + // Search for the actual toggle triggering the event + int i; + for (i = 0; i < guiGroup.getNumControls(); i++) { + ofxToggle* toggle = static_cast(guiGroup.getControl(i)); + ofParameter* paramPtr = + static_cast*>(&toggle->getParameter()); -void RadioList::onToggleClicked(bool &toggleValue) -{ - unselectAll(); - - // Search for the actual toggle triggering the event - int i; - for (i = 0; i < guiGroup.getNumControls(); i++) { - ofxToggle* toggle = static_cast(guiGroup.getControl(i)); - ofParameter* paramPtr = static_cast*>(&toggle->getParameter()); - - if (&(paramPtr->get()) == &toggleValue) { - selectItem(i); - break; - } + if (&(paramPtr->get()) == &toggleValue) { + selectItem(i); + break; } + } +} +} } - - }} diff --git a/src/ui/RadioList.h b/src/ui/RadioList.h index 0d1daea..45b7327 100644 --- a/src/ui/RadioList.h +++ b/src/ui/RadioList.h @@ -5,46 +5,47 @@ #include "ofxToggle.h" #include "ofxLabel.h" -namespace ofx{ - namespace piMapper{ -class RadioList -{ -public: - RadioList(); - RadioList(vector &labels); - RadioList(string title, vector &labels); - ~RadioList(); - - void setup(vector &labels); - void setup(string title, vector &labels); - void draw(); - void setTitle(string title); - void setPosition(ofPoint p); - void setPosition(float x, float y); - void selectItem(int index); - void enable(); - void disable(); - void clear(); - void unselectAll(); - ofPoint getPosition(); - float getWidth(); - float getHeight(); - string getTitle(); - string getItemName(int index); - int size(); - - // This event notifies about a toggle being selected and passes it's name to the listeners. - // Use ofAddListener(RadioListInstance.radioSelectedEvent, listenerClassPtr, &listenerClass::listenerMethod) - // to listen to this. Listner method void listenerMethod(string & radioName) - ofEvent radioSelectedEvent; - -private: - vector storedLabels; - string storedTitle; - ofxGuiGroup guiGroup; - int storedSelectedItem; - - void onToggleClicked(bool &toggleValue); -}; +namespace ofx { +namespace piMapper { +class RadioList { + public: + RadioList(); + RadioList(vector &labels); + RadioList(string title, vector &labels); + ~RadioList(); + + void setup(vector &labels); + void setup(string title, vector &labels); + void draw(); + void setTitle(string title); + void setPosition(ofPoint p); + void setPosition(float x, float y); + void selectItem(int index); + void enable(); + void disable(); + void clear(); + void unselectAll(); + ofPoint getPosition(); + float getWidth(); + float getHeight(); + string getTitle(); + string getItemName(int index); + int size(); + + // This event notifies about a toggle being selected and passes it's name to + // the listeners. + // Use ofAddListener(RadioListInstance.radioSelectedEvent, listenerClassPtr, + // &listenerClass::listenerMethod) + // to listen to this. Listner method void listenerMethod(string & radioName) + ofEvent radioSelectedEvent; - }} \ No newline at end of file + private: + vector storedLabels; + string storedTitle; + ofxGuiGroup guiGroup; + int storedSelectedItem; + + void onToggleClicked(bool &toggleValue); +}; +} +} \ No newline at end of file From 13d21cd213594995cf7c920d5d9ddbcffb4fc636 Mon Sep 17 00:00:00 2001 From: Krisjanis Rijnieks Date: Mon, 8 Sep 2014 11:33:18 +0200 Subject: [PATCH 18/73] Update readme - Add development notes - Add next release info - Replace and add dependencies --- README.md | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 41ded08..e4d993e 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,9 @@ As the projects gets a bit more popular, I see that people want to add missing f Currently I have decided to use [A successful Git branching model](http://nvie.com/posts/a-successful-git-branching-model/) by [Vincent Driessen](https://twitter.com/nvie), so read this article and I do not doubt that it will help you with other Git related projects. -I'm still working on boosting my understanding about the issue tracking system on GitHub, I believe that it would be the best way how to keep new feature requests and bugfixes organized. +I'm trying to organize the project by adding future release version milestones and assigning specific issues as TODO items to them. If you notice something strange or see that there is something that can be done in a better way, don't hesitate and add an issue. + +As of Release 0.1.5 some refractoring has been done (thanks [aspeteRakete](https://github.com/aspeteRakete)), namespaces have been added and we are thinking about introducing a code style for this project. Licence ------- @@ -98,8 +100,9 @@ BACKSPACE | Delete surface Dependencies ------------ -[ofxUI](https://github.com/rezaali/ofxUI) - will be replaced with custom solution as it consumes a lot of processing power. +ofxGui ofxXmlSettings +ofxOMXPlayer Compatibility ------------ @@ -112,6 +115,14 @@ When launching the example with `make run` keyboard and mouse input is being los Version history --------------- +### Version 0.1.4 (2014-08-xx): + - Added perspective warping for quad surfaces + - Added mesh warping to quad surfaces (TODO) + - Added namespaces + - Replaced ofxUI with ofxGui dependency + - Added video source (TODO) + - ... + ### Version 0.1.4 (2014-07-10): - Added fbo texture example - Replaced the main example with the fbo texture one From e23b0143974753c12cf3ed0e2cbaedc0b591c6a8 Mon Sep 17 00:00:00 2001 From: Krisjanis Rijnieks Date: Tue, 9 Sep 2014 18:55:44 +0200 Subject: [PATCH 19/73] Add logo to readme header --- README.md | 2 ++ piMapperLogo.jpg | Bin 0 -> 41854 bytes 2 files changed, 2 insertions(+) create mode 100644 piMapperLogo.jpg diff --git a/README.md b/README.md index e4d993e..d85534c 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +![Pi Mapper Logo](piMapperLogo.jpg) + ofxPiMapper =========== diff --git a/piMapperLogo.jpg b/piMapperLogo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d1a39b497bfa98d5772821ff67a70680fdc89cd9 GIT binary patch literal 41854 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 D^vWEj literal 0 HcmV?d00001 From 7d41a83927aef41a02952141d7496126f8f1fcac Mon Sep 17 00:00:00 2001 From: Krisjanis Rijnieks Date: Tue, 9 Sep 2014 18:59:03 +0200 Subject: [PATCH 20/73] Add logo credits --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d85534c..1355d7a 100644 --- a/README.md +++ b/README.md @@ -118,6 +118,7 @@ Version history --------------- ### Version 0.1.4 (2014-08-xx): + - Added logo (thanks [Irina Spicaka](http://irina.spicaka.info/)) - Added perspective warping for quad surfaces - Added mesh warping to quad surfaces (TODO) - Added namespaces From db425d2afd699c6b045ab096a4de0c29956efbcc Mon Sep 17 00:00:00 2001 From: Krisjanis Rijnieks Date: Tue, 9 Sep 2014 19:03:18 +0200 Subject: [PATCH 21/73] Fix logo --- piMapperLogo.jpg | Bin 41854 -> 41927 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/piMapperLogo.jpg b/piMapperLogo.jpg index d1a39b497bfa98d5772821ff67a70680fdc89cd9..824fa327f4548b9c1d0c74e3f86c04fc427a161c 100644 GIT binary patch literal 41927 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

xMN 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 D^vWEj From fbc248e776b8dedfa3ad407310ab9c37b944fd23 Mon Sep 17 00:00:00 2001 From: Krisjanis Rijnieks Date: Tue, 9 Sep 2014 19:11:06 +0200 Subject: [PATCH 22/73] Add ofx addons thumbnail --- ofxaddons_thumbnail.png | Bin 0 -> 5359 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 ofxaddons_thumbnail.png diff --git a/ofxaddons_thumbnail.png b/ofxaddons_thumbnail.png new file mode 100644 index 0000000000000000000000000000000000000000..d7b7bf17e2532248d45d87feadf9690e47ad4608 GIT binary patch literal 5359 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 Date: Tue, 9 Sep 2014 19:17:07 +0200 Subject: [PATCH 23/73] Change ofx addon thumbnail --- ofxaddons_thumbnail.png | Bin 5359 -> 5214 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/ofxaddons_thumbnail.png b/ofxaddons_thumbnail.png index d7b7bf17e2532248d45d87feadf9690e47ad4608..e12019e755461ded690992bbb992789c9726f071 100644 GIT binary patch delta 4431 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