ofxMSAInterpolatorを使いやすく改造する.
以前紹介したことのあるスプライン補間addonのofxMSAInterpolatorを勝手に使いやすく改造します。
以前の記事はこちら。cvl-robot.hateblo.jp
ofxMSAInterpolatorは若干バグが残っている気がするので、心配な時にはこちらを参照すると良いかもしれません。(未テスト)qiita.com
改造点は、
- ofxMSACoreなど他のaddonに依存しないようにした。
- セグメンテーションエラーで落ちるバグフィックス(sampleAtの描画用メモリ、と、距離オプションのときの参照外メモリエラー)
- http://paulbourke.net/miscellaneous/interpolation/ で紹介されている他の補間関数の追加 Cosine, CatmullRom, Hermite. ただしCosine変かも。
- Hermite追加に伴い、tensionとbiasパラメータの追加。
MSAInterpolationTypes.h
#pragma once namespace msa { typedef enum { kInterpolationLinear = 0, kInterpolationCosine = 1, kInterpolationCubic = 2, kInterpolationCatmullRom = 3, kInterpolationHermite = 4, } InterpolationType; }
MSAInterpolator.h (変更なし)
/**************************** InterpolatorT Classes **************************** Usage: msa::InterpolatorT<float> myInterpolator1; // create spline of floats msa::InterpolatorT<myDataType> myInterpolator2; // create spline of custom data types (more info below) // OR use preset classes: msa::Interpolator1D myInterpolator1D; // create spline of floats (1D) msa::Interpolator2D myInterpolator2D; // create spline of Vec2f (2D) msa::Interpolator3D myInterpolator3D; // create spline of Vec3f (3D) // splines wrap basic functionality of stl::vector: myInterpolator.size(); // return number of data elements myInterpolator.reserve(int count); // if you know how many elements up front it will improved performance when adding (you can still add more than this number of elements) myInterpolator.at(int i); // return data at i'th index myInterpolator.clear(); // remove all elements myInterpolator.push_back(data1); // add some data to the spline myInterpolator.push_back(data2); myInterpolator.sampleAt(float t); // (e.g. t:0.34 =>) samples along 34% of the whole spline using the current interpolation method and options setInterpolation(i); // set interpolation type, see MSAInterpolationTypes.h (currently cubic catmull rom and linear) int getInterpolation(); // get interpolation type setUseLength(bool b); // whether to use Length or not. using Length is slightly slower than not using (depending on number of data points) bool getUseLength(); // if useLength is true, sampleAt(0.57) means sample at 57% along the physical length of the spline (using the interpolated spline for Length calculation) // if useLength is false, the %t refers to % along the data points. If data points are evenly spaced its no problem, but if they are randomly spaced, the interpolation will not be uniform myInterpolator.drawRaw(int dotSize, int lineWidth); // draws raw data with dotSize and lineWidth (make either zero to not draw dots or lines) myInterpolator.drawSmooth(int numSteps, int dotSize, int lineWidth); // draws smoothed data in (make either zero to not draw dots or lines) Using custom data type: msa::InterpolatorT<myDataType> myInterpolator2; // create spline of custom data types (more info below) myDataType has to be a scalar or class with the overloaded operators: + (myDataType&) - (myDataType&) == (myDataType&) = (myDataType&) * (float) and also define the function lengthOf(myDataType&) to return a scalar float value depicting the 'magnitude' of the data type (used in calculating Length) *************************************************************************/ /*************** DEPENDENCIES: - MSACore ***************/ #pragma once #include "MSAInterpolationTypes.h" #include "MSAInterpolatorT.h" #include "MSAInterpolator1D.h" #include "MSAInterpolator2D.h" #include "MSAInterpolator3D.h"
MSAInterpolator1D.h (変更なし)
/**************************** 1D InterpolatorT (of floats) ****************************/ #pragma once #include "MSAInterpolatorT.h" namespace msa { //-------------------------------------------------------------- inline float lengthOf(float f) { return f; } //-------------------------------------------------------------- typedef InterpolatorT<float> Interpolator1D; }
MSAInterpolator2D.h
/**************************** 2D InterpolatorT (of Vec2) ****************************/ #pragma once #include "MSAInterpolatorT.h" namespace msa { typedef InterpolatorT<ofVec2f> Interpolator2D; //-------------------------------------------------------------- inline float lengthOf(const ofVec2f& v) { return v.length(); } //-------------------------------------------------------------- // OpenGL ES compatibility added by Rob Seward // http://www.openframeworks.cc/forum/viewtopic.php?f=25&t=3767&p=19865 inline void drawInterpolatorRaw(Interpolator2D &spline, int dotSize = 20, int lineWidth = 4){ int numItems = spline.size(); if(lineWidth) { glLineWidth(lineWidth); vector<GLfloat> vertex; vertex.resize(numItems * 2); for(int i=0; i<numItems; i++) { vertex[i*2] = spline.at(i).x; vertex[(i*2)+1] = spline.at(i).y; } glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(2, GL_FLOAT, 0, vertex.data()); glDrawArrays(GL_LINE_STRIP, 0, numItems); } if(dotSize) { glPointSize(dotSize); vector<GLfloat> vertex; vertex.resize(numItems * 2); for(int i=0; i<numItems; i++) { vertex[i*2] = spline.at(i).x; vertex[(i*2)+1] = spline.at(i).y; } glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(2, GL_FLOAT, 0, vertex.data()); glDrawArrays(GL_POINTS, 0, numItems); } } //-------------------------------------------------------------- inline void drawInterpolatorSmooth(Interpolator2D &spline, int numSteps, int dotSize = 8, int lineWidth = 2) { float spacing = 1.0/numSteps; if(lineWidth) { glLineWidth(lineWidth); vector<GLfloat> vertex; vertex.clear(); for(float f=0; f<1; f+= spacing) { ofVec2f v = spline.sampleAt(f); vertex.push_back(v.x); vertex.push_back(v.y); } glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(2, GL_FLOAT, 0, vertex.data()); glDrawArrays(GL_LINE_STRIP, 0, numSteps); } if(dotSize) { glPointSize(dotSize); vector<GLfloat> vertex; vertex.clear(); for(float f=0; f<1; f+= spacing) { ofVec2f v = spline.sampleAt(f); vertex.push_back(v.x); vertex.push_back(v.y); } glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(2, GL_FLOAT, 0, vertex.data()); glDrawArrays(GL_POINTS, 0, numSteps); } } }
MSAInterpolator3D.h
/**************************** 3D InterpolatorT (of Vec3) ****************************/ #pragma once #include "MSAInterpolatorT.h" namespace msa { typedef InterpolatorT<ofVec3f> Interpolator3D; //-------------------------------------------------------------- inline float lengthOf(const ofVec3f& v) { return v.length(); } //-------------------------------------------------------------- // OpenGL ES compatibility added by Rob Seward // http://www.openframeworks.cc/forum/viewtopic.php?f=25&t=3767&p=19865 inline void drawInterpolatorRaw(Interpolator3D spline, int dotSize = 20, int lineWidth = 4){ int numItems = spline.size(); if(numItems == 0) return; if(lineWidth) { glLineWidth(lineWidth); vector<GLfloat> vertex; vertex.resize(numItems * 3); for(int i=0; i<numItems; i++) { vertex[i*3] = spline.at(i).x; vertex[(i*3)+1] = spline.at(i).y; vertex[(i*3)+2] = spline.at(i).z; } glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(3, GL_FLOAT, 0, vertex.data()); glDrawArrays(GL_LINE_STRIP, 0, numItems); } if(dotSize) { glPointSize(dotSize); vector<GLfloat> vertex; vertex.resize(numItems * 3); for(int i=0; i<numItems; i++) { vertex[i*3] = spline.at(i).x; vertex[(i*3)+1] = spline.at(i).y; vertex[(i*3)+2] = spline.at(i).z; } glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(3, GL_FLOAT, 0, vertex.data()); glDrawArrays(GL_POINTS, 0, numItems); } } //-------------------------------------------------------------- inline void drawInterpolatorSmooth(Interpolator3D spline, int numSteps, int dotSize = 8, int lineWidth = 2) { float spacing = 1.0/numSteps; if(spline.size() == 0) return; if(lineWidth) { glLineWidth(lineWidth); vector<GLfloat> vertex; vertex.clear(); for(float f=0; f<1; f+= spacing) { ofVec3f v = spline.sampleAt(f); vertex.push_back(v.x); vertex.push_back(v.y); vertex.push_back(v.z); } glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(3, GL_FLOAT, 0, vertex.data()); glDrawArrays(GL_LINE_STRIP, 0, numSteps); } if(dotSize) { glPointSize(dotSize); vector<GLfloat> vertex; vertex.clear(); for(float f=0; f<1; f+= spacing) { ofVec3f v = spline.sampleAt(f); vertex.push_back(v.x); vertex.push_back(v.y); vertex.push_back(v.z); } glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(3, GL_FLOAT, 0, vertex.data()); glDrawArrays(GL_POINTS, 0, numSteps); } } }
MSAInterpolatorT.h
/**************************** InterpolatorT Template Class ****************************/ #pragma once //#include "MSACore.h" #include "MSAInterpolationTypes.h" namespace msa { template< typename T > inline float lengthOf(const T &v) { return 1; } template <typename T> class InterpolatorT { public: bool verbose; InterpolatorT(); // interpolate and re-sample at t position along the spline // where t: 0....1 based on length of spline T sampleAt(float t) const; void setInterpolation(InterpolationType i = kInterpolationCubic); int getInterpolation() const; void setUseLength(bool b); bool getUseLength() const; // return length upto data point i // leave blank (-1) to return length of entire data set // only valid if setUseLength is true // uses current interpolation settings for lenth calculation // returns cached value, no calculations done in this function const float getLength(int i=-1) const; // set number of subdivisions used to calculation length of segment void setLengthSubdivisions(int i = 100); int getLengthSubdivisions() const; /******************* stl::container wrapper functions *******************/ void push_back(const T& newData); int size() const; void reserve(int i); void clear(); T& at(int i); // added const T& at(int i) const; vector<T>& getData(); const vector<T>& getData() const; // for Hermite void setTension(float t) { _tension = t; } void setBias(float b) { _bias = b; } float getTension() { return _tension; } float getBias() { return _bias; } protected: InterpolationType _interpolationMethod; bool _useLength; int _lengthSubdivisions; // number of subdivisions used for length calculation vector<T> _data; // vector of all data vector<float> _dist; // vector of cumulative Lengths from i'th data point to beginning of spline // for Hermite float _tension; float _bias; // calculates length of segment prior to (leading up to) i'th point float calcSegmentLength(int i); // update all Lengths in _dist array void updateAllLengths(); // given t(0...1) find the node index directly to the left of the point void findPosition(float t, int &leftIndex, float &mu) const; T linearInterpolate(const T& y1, const T& y2, float mu) const; T CosineInterpolate(const T&y1, const T&y2, float mu) const; // this function is from Paul Bourke's site // http://local.wasp.uwa.edu.au/~pbourke/miscellaneous/interpolation/ T cubicInterpolate(const T& y0, const T& y1, const T& y2, const T& y3, float mu) const; T CatmullRomInterpolate(const T& y0, const T& y1, const T& y2, const T& y3, float mu) const; /* Tension: 1 is high, 0 normal, -1 is low Bias: 0 is even, positive is towards first segment, negative towards the other */ T HermiteInterpolate(const T& y0, const T& y1, const T& y2, const T& y3, float mu, float tension, float bias) const; }; //---------------------------------------------------------------------------- //-------------------------------------------------------------- //-------------------------------------------------------------- template <typename T> InterpolatorT<T>::InterpolatorT() : _tension(0.0f), _bias(0.0f) { setInterpolation(); setUseLength(false); setLengthSubdivisions(); verbose = false; } //-------------------------------------------------------------- // use catmull rom interpolation to re-sample At normT position along the spline // where normT: 0....1 based on length of spline template <typename T> T InterpolatorT<T>::sampleAt(float t) const { int numItems = size(); if(numItems == 0) { // if(verbose) printf("InterpolatorT: not enough samples", t); return T(); } if(t>1) t = 1; else if(t<0) t=0; int i0, i1, i2, i3; float mu; findPosition(t, i1, mu); // if less than 4 data points, force linear interpolation InterpolationType it = _interpolationMethod; if (numItems < 4) it = kInterpolationLinear; switch(it) { case kInterpolationHermite: i0 = i1 - 1; i2 = i1 + 1; i3 = i2 + 1; if (i0 < 0) i0 = 0; if (i3 >= numItems) i3 = numItems - 1; return HermiteInterpolate(at(i0), at(i1), at(i2), at(i3), mu, _tension, _bias); break; case kInterpolationCatmullRom: i0 = i1 - 1; i2 = i1 + 1; i3 = i2 + 1; if (i0 < 0) i0 = 0; if (i3 >= numItems) i3 = numItems - 1; return CatmullRomInterpolate(at(i0), at(i1), at(i2), at(i3), mu); break; case kInterpolationCubic: i0 = i1 - 1; i2 = i1 + 1; i3 = i2 + 1; if(i0 < 0) i0 = 0; if(i3 >= numItems) i3 = numItems-1; return cubicInterpolate(at(i0), at(i1), at(i2), at(i3), mu); break; case kInterpolationLinear: i2 = i1 + 1; if(i2 >= numItems) i2 = numItems-1; return linearInterpolate(at(i1), at(i2), mu); break; case kInterpolationCosine: i2 = i1 + 1; if (i2 >= numItems) i2 = numItems - 1; return CosineInterpolate(at(i1), at(i2), mu); break; } } //-------------------------------------------------------------- template <typename T> void InterpolatorT<T>::setInterpolation(InterpolationType i) { _interpolationMethod = i; updateAllLengths(); } //-------------------------------------------------------------- template <typename T> int InterpolatorT<T>::getInterpolation() const { return _interpolationMethod; } //-------------------------------------------------------------- template <typename T> void InterpolatorT<T>::setUseLength(bool b) { _useLength = b; if(_useLength) updateAllLengths(); else _dist.clear(); } //-------------------------------------------------------------- template <typename T> bool InterpolatorT<T>::getUseLength() const { return _useLength; } //-------------------------------------------------------------- template <typename T> const float InterpolatorT<T>::getLength(int i) const { if (_useLength&&!_dist.empty()) { return i < 0 ? _dist[_dist.size() - 1] : _dist.at(i); } else { return 0; } } //-------------------------------------------------------------- template <typename T> void InterpolatorT<T>::setLengthSubdivisions(int i) { _lengthSubdivisions = i; } //-------------------------------------------------------------- template <typename T> int InterpolatorT<T>::getLengthSubdivisions() const { return _lengthSubdivisions; } //-------------------------------------------------------------- template <typename T> void InterpolatorT<T>::push_back(const T& newData) { _data.push_back(newData); // add data if(getUseLength()) { float segmentLength; float totalLength; if(size() > 1) { // T distT = newData - _data.at(prevIndex); // get offset to previous node // float dist = lengthOf(distT); // actual Length to node segmentLength = calcSegmentLength(size()-1); totalLength = segmentLength + _dist.at(size()-2); } else { segmentLength = 0; totalLength = 0; } _dist.push_back(totalLength); // if(verbose) printf("segment length = %f | total length = %f\n", segmentLength, totalLength); } } //-------------------------------------------------------------- template <typename T> int InterpolatorT<T>::size() const { return _data.size(); } //-------------------------------------------------------------- template <typename T> void InterpolatorT<T>::reserve(int i) { _data.reserve(i); _dist.reserve(i); } //-------------------------------------------------------------- template <typename T> void InterpolatorT<T>::clear() { _data.clear(); _dist.clear(); } //-------------------------------------------------------------- added template <typename T> T& InterpolatorT<T>::at(int i) { return _data.at(ofClamp(i, 0, size()-1)); // modified } //-------------------------------------------------------------- template <typename T> const T& InterpolatorT<T>::at(int i) const { return _data.at(ofClamp(i, 0, size()-1)); // modified } //-------------------------------------------------------------- template <typename T> vector<T>& InterpolatorT<T>::getData() { return _data; } //-------------------------------------------------------------- template <typename T> const vector<T>& InterpolatorT<T>::getData() const { return _data; } //-------------------------------------------------------------- template <typename T> float InterpolatorT<T>::calcSegmentLength(int i) { ofLogVerbose("msa::InterpolatorT<T>::calcSegmentLength(int i) isn't working anymore"); int numItems = size(); if(numItems < 2 || i < 1 || i >= numItems) return 0; bool saveUseLength = _useLength; _useLength = false; float startPerc = (i-1) * 1.0f/(numItems-1); float endPerc = (i) * 1.0f/(numItems-1); float incPerc = (endPerc - startPerc)/_lengthSubdivisions; T prev = sampleAt(startPerc); T cur; float segmentLength = 0; for(float f = startPerc; f <= endPerc; f+= incPerc) { cur = sampleAt(f); segmentLength += lengthOf(cur - prev); // TODO: this isn't compiling anymore! prev = cur; } _useLength = saveUseLength; if(verbose) printf("segment length for %i is %f\n", i, segmentLength); return segmentLength; } //-------------------------------------------------------------- template <typename T> void InterpolatorT<T>::updateAllLengths() { _dist.clear(); float curTotal = 0; for(int i=0; i<size(); i++) { curTotal += calcSegmentLength(i); _dist.push_back(curTotal); } } //-------------------------------------------------------------- template <typename T> void InterpolatorT<T>::findPosition(float t, int &leftIndex, float &mu) const { int numItems = size(); switch(numItems) { case 0: leftIndex = 0; mu = 0; break; case 1: leftIndex = 0; mu = 0; break; case 2: leftIndex = 0; mu = t; break; default: if(_useLength) { // need to use float totalLengthOfInterpolator = _dist.at(numItems-1); float tDist = totalLengthOfInterpolator * t; // the Length we want to be from the start int startIndex = floor(t * (numItems - 1)); // start approximation here int i1 = startIndex; int limitLeft = 0; int limitRight = numItems-1; float distAt1, distAt2; // do { for(int iterations = 0; iterations < 100; iterations ++) { // limit iterations distAt1 = _dist.at(i1); if(distAt1 <= tDist) { // if Length at i1 is less than desired Length (this is good) distAt2 = _dist.at(ofClamp(i1+1, 0, (int)_dist.size()-1)); // modified if(distAt2 > tDist) { leftIndex = i1; mu = (tDist - distAt1) / (distAt2-distAt1); return; } else { limitLeft = i1; } } else { limitRight = i1; } i1 = (limitLeft + limitRight)>>1; } // } while(true); } else { float actT = t * (numItems - 1); leftIndex = floor(actT); mu = actT - leftIndex; } } } //-------------------------------------------------------------- template <typename T> T InterpolatorT<T>::linearInterpolate(const T& y1, const T& y2, float mu) const { return (y2-y1) * mu + y1; } //-------------------------------------------------------------- template <typename T> T InterpolatorT<T>::CosineInterpolate(const T&y1, const T&y2, float mu) const { float mu2; mu2 = (1.0f - cosf(mu*(float)PI)) / 2.0f; return(y1*(1.0f - mu2) + y2*mu2); } //-------------------------------------------------------------- // this function is from Paul Bourke's site // http://local.wasp.uwa.edu.au/~pbourke/miscellaneous/interpolation/ // is move to http://paulbourke.net/miscellaneous/interpolation/ template <typename T> T InterpolatorT<T>::cubicInterpolate(const T& y0, const T& y1, const T& y2, const T& y3, float mu) const { float mu2 = mu * mu; T a0 = y3 - y2 - y0 + y1; T a1 = y0 - y1 - a0; T a2 = y2 - y0; T a3 = y1; return(a0 * mu * mu2 + a1 * mu2 + a2 * mu + a3); } template <typename T> T InterpolatorT<T>::CatmullRomInterpolate(const T& y0, const T& y1, const T& y2, const T& y3, float mu) const { float mu2 = mu * mu; T a0 = -0.5*y0 + 1.5*y1 - 1.5*y2 + 0.5*y3; T a1 = y0 - 2.5*y1 + 2 * y2 - 0.5*y3; T a2 = -0.5*y0 + 0.5*y2; T a3 = y1; return(a0 * mu * mu2 + a1 * mu2 + a2 * mu + a3); } template <typename T> T InterpolatorT<T>::HermiteInterpolate(const T& y0, const T& y1, const T& y2, const T& y3, float mu, float tension, float bias) const { T m0, m1; float mu2, mu3; float a0, a1, a2, a3; mu2 = mu * mu; mu3 = mu2 * mu; m0 = (y1 - y0)*(1 + bias)*(1 - tension) / 2; m0 += (y2 - y1)*(1 - bias)*(1 - tension) / 2; m1 = (y2 - y1)*(1 + bias)*(1 - tension) / 2; m1 += (y3 - y2)*(1 - bias)*(1 - tension) / 2; a0 = 2 * mu3 - 3 * mu2 + 1; a1 = mu3 - 2 * mu2 + mu; a2 = mu3 - mu2; a3 = -2 * mu3 + 3 * mu2; return(a0*y1 + a1*m0 + a2*m1 + a3*y2); } }
ここからは、テスト用のexample。
main.cpp
#include "ofMain.h" #include "testApp.h" //======================================================================== int main( ){ // can be OF_WINDOW or OF_FULLSCREEN // pass in width and height too: ofSetupOpenGL(1024, 768, OF_WINDOW); // <-------- setup the GL context // this kicks off the running of my app ofRunApp(new testApp); }
testApp.h
#include "testApp.h" #include "MSAInterpolator.h" msa::Interpolator2D spline2D; msa::Interpolator3D spline3D; msa::InterpolationType interpolationType = msa::kInterpolationCubic; bool useLength = false; float currentRot; bool rotateView; float spherePosPerc = 0; // 0....1 percentage of how far along the 3D path the sphere is float sphereSpeed = 0.005f; //-------------------------------------------------------------- void testApp::setup(){ ofSetVerticalSync(true); rotateView = true; currentRot = 0; // create a 2D spline with ofVec2f's int numItems = 10; int padding = 30; float len = (ofGetWidth() - padding*2.0f) / numItems; spline2D.reserve(numItems); // not essential, but good habit if you know how big its gonna be for(int i=0; i<numItems; i++) { ofVec2f v = ofVec2f(i * len + padding - ofGetWidth()/2, ofGetHeight()/2 + ofGetHeight()*0.2f * cos(i*0.9)); spline2D.push_back(v); } spline3D.verbose = true; glEnable(GL_DEPTH_TEST); } //-------------------------------------------------------------- void testApp::draw() { glPushMatrix(); glTranslatef(ofGetWidth()/2, 0, 0); // move to center of screen horizontally (so we can rotate) if(rotateView) { currentRot += 0.2; glRotatef(currentRot, 0, 1, 0); // rotate view } else { currentRot = 0; } int numSteps = floor(mouseX / (float)ofGetWidth() * 1000); if(numSteps<10) numSteps = 10; float spacing = 1.0/numSteps; // draw spline2D glColor3f(1, 1, 1); drawInterpolatorRaw(spline2D); // draw interpolated spline2D glColor3f(0, 0, 1); drawInterpolatorSmooth(spline2D, numSteps); // draw raw spline3D glColor3f(1, 1, 1); drawInterpolatorRaw(spline3D); // draw interpolated spline3D glColor3f(0.2f, 0.2f, 0.2f); drawInterpolatorSmooth(spline3D, numSteps); // draw sphere moving along 3D path ofVec3f spherePos = spline3D.sampleAt(spherePosPerc); glPushMatrix(); glColor3f(1, 1, 0); glTranslatef(spherePos.x, spherePos.y, spherePos.z); ofSphere(5); glPopMatrix(); // move sphere // if it reaches the edges, bounce back spherePosPerc += sphereSpeed; if(spherePosPerc > 1) { spherePosPerc = 1; sphereSpeed *= -1; } else if(spherePosPerc < 0) { spherePosPerc = 0; sphereSpeed *= -1; } glPopMatrix(); ofSetColor(0); string uiLin = interpolationType == msa::kInterpolationLinear ? "* " : " "; string uiCub = interpolationType == msa::kInterpolationCubic ? "* " : " "; string uiCos = interpolationType == msa::kInterpolationCosine ? "* " : " "; string uiCat = interpolationType == msa::kInterpolationCatmullRom ? "* " : " "; string uiHer = interpolationType == msa::kInterpolationHermite ? "* " : " "; string uiDist = spline3D.getUseLength() ? "* " : " "; ofDrawBitmapString( ofToString(ofGetFrameRate(), 2) + "\n" + "numSteps (resampling resolution - mouseX to change): " + ofToString(numSteps) + "\n" + "mouse click around the area to draw a 3D spline (length = " + ofToString(spline3D.getLength()) + "\n" + "\n" + uiLin + "'1' to use linear interpolation\n" + uiCub + "'2' to use cubic (catmull rom) interpolation\n" + uiCos + "'3' to use cosine interpolation\n" + uiCat + "'4' to use catmull rom interpolation\n" + uiHer + "'5' to use hermite interpolation with random tension and bias\n" + "\n" + uiDist + "'d' to toggle 'using Length in interpolation'\n" + "\n" + "'c' to clear 3D spline\n" , 20, 20); } void testApp::keyPressed(int key) { switch(key) { case '1': interpolationType = msa::kInterpolationLinear; spline3D.setInterpolation(interpolationType); spline2D.setInterpolation(interpolationType); break; case '2': interpolationType = msa::kInterpolationCubic; spline3D.setInterpolation(interpolationType); spline2D.setInterpolation(interpolationType); break; case '3': interpolationType = msa::kInterpolationCosine; spline3D.setInterpolation(interpolationType); spline2D.setInterpolation(interpolationType); break; case '4': interpolationType = msa::kInterpolationCatmullRom; spline3D.setInterpolation(interpolationType); spline2D.setInterpolation(interpolationType); break; case '5': interpolationType = msa::kInterpolationHermite; spline3D.setInterpolation(interpolationType); spline2D.setInterpolation(interpolationType); spline3D.setTension(ofRandom(-1, 1)); spline3D.setBias(ofRandom(-1, 1)); spline2D.setTension(spline3D.getTension()); spline2D.setTension(spline3D.getBias()); cout << "tension = " << spline3D.getTension() << ", bias = " << spline3D.getBias() << endl; break; case 'd': useLength ^=true; spline3D.setUseLength(useLength); spline2D.setUseLength(useLength); break; case 'c': case 'C': spline3D.clear(); break; case 'r': case 'R': rotateView ^= true; break; } } //-------------------------------------------------------------- void testApp::mousePressed(int x, int y, int button) { // when you add a new point, the length of the spline increases // so the sphere will jump to a new position because it's position is based on percentage // so a little bit of maths to calculate what the new position percentage should be to stay at the same physical location // (not precise, but close enough) int numPoints = spline3D.size(); spherePosPerc = spherePosPerc * numPoints / (numPoints + 1); ofVec3f pt(x-ofGetWidth()/2, y, 0); pt.rotate(-currentRot, ofVec3f(0, 1, 0)); spline3D.push_back(pt); }
testApp.h
#ifndef _TEST_APP #define _TEST_APP #include "ofMain.h" class testApp : public ofBaseApp { public: void setup(); void draw(); void mousePressed(int x, int y, int button); void keyPressed(int key); }; #endif
オリジナルはこちら。www.memo.tv
ちょっとしたデジタルデバイスの実験に便利な、大容量小型軽量バッテリーがAnkerから発売されました.ラズパイの電源などに最適だと思います.26800mAhなら2日ぐらい持ちそう。