cvl-robot's diary

研究ノート メモメモ https://github.com/dotchang/

ofxMSAInterpolatorを使いやすく改造する.

以前紹介したことのあるスプライン補間addonのofxMSAInterpolatorを勝手に使いやすく改造します。
以前の記事はこちら。cvl-robot.hateblo.jp

ofxMSAInterpolatorは若干バグが残っている気がするので、心配な時にはこちらを参照すると良いかもしれません。(未テスト)qiita.com

改造点は、

  1. ofxMSACoreなど他のaddonに依存しないようにした。
  2. セグメンテーションエラーで落ちるバグフィックス(sampleAtの描画用メモリ、と、距離オプションのときの参照外メモリエラー)
  3. http://paulbourke.net/miscellaneous/interpolation/ で紹介されている他の補間関数の追加 Cosine, CatmullRom, Hermite. ただしCosine変かも。
  4. 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日ぐらい持ちそう。