TuttleOFX
1
|
00001 #ifndef _TUTTLE_HOST_CORE_ATTRIBUTE_ANIMATED_PARAM_HPP_ 00002 #define _TUTTLE_HOST_CORE_ATTRIBUTE_ANIMATED_PARAM_HPP_ 00003 00004 /* Template class for an animated parameter. 00005 00006 Used for ParamDouble and ParamInteger */ 00007 00008 #include "Param.hpp" 00009 #include "expression.hpp" 00010 00011 #include <tuttle/host/INode.hpp> 00012 #include <tuttle/host/ofx/attribute/OfxhParamDouble.hpp> 00013 #include <tuttle/host/ofx/attribute/OfxhParamInteger.hpp> 00014 #include <tuttle/host/attribute/ValueInterpolator.hpp> 00015 00016 #include <boost/scoped_ptr.hpp> 00017 #include <boost/functional/hash.hpp> 00018 00019 #include <vector> 00020 #include <algorithm> 00021 00022 namespace tuttle { 00023 namespace host { 00024 namespace attribute { 00025 00026 template<typename T, typename OFX_PARAM> 00027 class AnimatedParam : public Param, public OFX_PARAM 00028 { 00029 protected: 00030 T _value; 00031 00032 /* An ordered list of key frames. Used when this param is 00033 animated. This must stay sorted */ 00034 std::vector< TimeValue<T> > _key_frames; 00035 00036 /* The class that interpolates key frames when animating this param 00037 All transitions yare animated with the same interpolator. A more 00038 advanced version of this class might allow any of the interpolators 00039 in ValueInterpolator.hpp to be used between any adjacent key frames. */ 00040 boost::scoped_ptr< Interpolator<T> > _interpolator; 00041 00042 public: 00043 typedef AnimatedParam<T, OFX_PARAM> This; 00044 typedef typename std::vector< TimeValue<T> >::iterator TimeValueTIterator; 00045 typedef typename std::vector< TimeValue<T> >::const_iterator TimeValueTConstIterator; 00046 00047 AnimatedParam( 00048 INode& effect, 00049 const std::string& name, 00050 const ofx::attribute::OfxhParamDescriptor& descriptor, 00051 const std::size_t index, 00052 const T value ) 00053 : Param( effect ) 00054 , OFX_PARAM( descriptor, name, effect.getParamSet( ), index ) 00055 , _value( value ) 00056 , _interpolator( new LinearInterpolator<T>() ) 00057 { 00058 } 00059 00060 AnimatedParam( const AnimatedParam<T, OFX_PARAM>& other ) 00061 : Param( other ) 00062 , OFX_PARAM( other ) 00063 , _value( other._value ) 00064 , _interpolator( other._interpolator->clone() ) 00065 { 00066 } 00067 00068 ~AnimatedParam( ) 00069 { 00070 } 00071 00072 void setInterpolator( const ofx::attribute::EInterpolatorType interpolatorType ) OFX_EXCEPTION_SPEC 00073 { 00074 switch( interpolatorType ) 00075 { 00076 case ofx::attribute::eSmoothInterpolator: 00077 _interpolator.reset( new SmoothInterpolator<T>() ); 00078 break; 00079 case ofx::attribute::eFastInterpolator: 00080 _interpolator.reset( new FastInterpolator<T>() ); 00081 break; 00082 case ofx::attribute::eSlowInterpolator: 00083 _interpolator.reset( new SlowInterpolator<T>() ); 00084 break; 00085 case ofx::attribute::eLinearInterpolator: 00086 _interpolator.reset( new LinearInterpolator<T>() ); 00087 break; 00088 } 00089 } 00090 00091 void getValue( T& v ) const OFX_EXCEPTION_SPEC 00092 { 00093 v = _value; 00094 } 00095 00096 void getValueAtTime( const OfxTime time, T& v ) const OFX_EXCEPTION_SPEC 00097 { 00098 if( _key_frames.size( ) == 0 ) 00099 v = _value; 00100 else 00101 { 00102 00103 /* Find the key frames surrounding this time 00104 00105 Set prev to the first key frame before "time" or the first one 00106 after "time" if there's not one before. 00107 00108 Set next to the first key frame after "time" or the first one 00109 before "time" if there's not one after */ 00110 00111 TimeValue<T> temp, prev, next; 00112 TimeValueTConstIterator it; 00113 00114 // Find the first item at or before time 00115 temp.time = time; 00116 it = std::lower_bound( _key_frames.begin( ), _key_frames.end( ), temp ); 00117 00118 if( it->time == time || it == _key_frames.begin( ) ) 00119 { 00120 // There's a key frame at exactly this time 00121 // or this is the first key frame. Return this key frame value 00122 v = it->value; 00123 return; 00124 } 00125 else if( it == _key_frames.end( ) ) 00126 { 00127 // There's no key frame at or after this time 00128 // Return the last key frame value 00129 it--; 00130 v = it->value; 00131 return; 00132 } 00133 else 00134 { 00135 // The time is between two values. 00136 // Get the previous key frame and interpolate 00137 next = *it; 00138 it--; 00139 prev = *it; 00140 _interpolator->getValue( prev, next, ( const OfxTime ) time, v ); 00141 } 00142 } 00143 } 00144 00145 void setValue( const T& v, const ofx::attribute::EChange change ) OFX_EXCEPTION_SPEC 00146 { 00147 /* Setting a single value for this param clears the animation */ 00148 _key_frames.clear( ); 00149 _value = v; 00150 this->paramChanged( change ); 00151 } 00152 00153 void setValueAtTime( const OfxTime time, const T& v, const ofx::attribute::EChange change ) OFX_EXCEPTION_SPEC 00154 { 00155 TimeValue<T> new_tv; 00156 TimeValueTIterator it; 00157 00158 new_tv.time = time; 00159 new_tv.value = v; 00160 it = find( _key_frames.begin( ), _key_frames.end( ), new_tv ); 00161 if( it == _key_frames.end( ) ) 00162 { 00163 _key_frames.push_back( new_tv ); 00164 std::sort( _key_frames.begin( ), _key_frames.end( ) ); 00165 } 00166 else 00167 { 00168 ( *it ).value = v; 00169 } 00170 this->paramChanged( change ); 00171 } 00172 00173 void setValueFromExpression( const std::string& value, const ofx::attribute::EChange change ) OFX_EXCEPTION_SPEC 00174 { 00175 _value = extractValueFromExpression<T>( value ); 00176 this->paramChanged( change ); 00177 } 00178 00179 void derive( const OfxTime time, T& ) const OFX_EXCEPTION_SPEC 00180 { 00181 BOOST_THROW_EXCEPTION( ofx::OfxhException( kOfxStatErrMissingHostFeature ) ); 00182 } 00183 00184 void integrate( const OfxTime time1, const OfxTime time2, T& ) const OFX_EXCEPTION_SPEC 00185 { 00186 BOOST_THROW_EXCEPTION( ofx::OfxhException( kOfxStatErrMissingHostFeature ) ); 00187 } 00188 00189 void copy( const AnimatedParam<T, OFX_PARAM>& p ) OFX_EXCEPTION_SPEC 00190 { 00191 _value = p._value; 00192 _key_frames = p._key_frames; 00193 // paramChanged( ofx::attribute::eChangeUserEdited ); 00194 } 00195 00196 void copy( const ofx::attribute::OfxhParam& p ) OFX_EXCEPTION_SPEC 00197 { 00198 const AnimatedParam<T, OFX_PARAM>& param = dynamic_cast < const AnimatedParam<T, OFX_PARAM>& > ( p ); 00199 00200 copy( param ); 00201 } 00202 00203 This* clone() const 00204 { 00205 return new This( *this ); 00206 } 00207 00208 /* ======= BEGIN OfxhKeyframeParam functions ======= 00209 00210 Since this implementation is backed by a set, indexes may change and 00211 they are no longer valid after a key frame is set or deleted. */ 00212 void getNumKeys( unsigned int& outNumKeys ) const OFX_EXCEPTION_SPEC 00213 { 00214 outNumKeys = _key_frames.size( ); 00215 } 00216 00217 void getKeyTime( const int nth, OfxTime& outTime ) const OFX_EXCEPTION_SPEC 00218 { 00219 if( nth >= 0 && ( size_t ) nth < _key_frames.size( ) ) 00220 outTime = _key_frames[nth].time; 00221 else 00222 BOOST_THROW_EXCEPTION( ofx::OfxhException( kOfxStatErrBadIndex ) ); 00223 } 00224 00225 void getKeyIndex( const OfxTime time, const int direction, int& outIndex ) const OFX_EXCEPTION_SPEC 00226 { 00227 TimeValue<T> temp; 00228 TimeValueTConstIterator it; 00229 00230 // Find the first item at or before time 00231 temp.time = time; 00232 it = std::lower_bound( _key_frames.begin( ), _key_frames.end( ), temp ); 00233 00234 if( it == _key_frames.end( ) ) 00235 BOOST_THROW_EXCEPTION( ofx::OfxhException( kOfxStatFailed ) ); 00236 00237 if( direction == 0 ) 00238 { 00239 // Find an exact match or fail 00240 if( it->time == time ) 00241 { 00242 outIndex = it - _key_frames.begin( ); 00243 } 00244 else 00245 { 00246 BOOST_THROW_EXCEPTION( ofx::OfxhException( kOfxStatFailed ) ); 00247 } 00248 00249 } 00250 else if( direction < 0 ) 00251 { 00252 // Find the key frame directly before the match. If the match is the first element, fail. 00253 if( it == _key_frames.begin( ) ) 00254 BOOST_THROW_EXCEPTION( ofx::OfxhException( kOfxStatFailed ) ); 00255 else 00256 { 00257 outIndex = it - _key_frames.begin( ) - 1; 00258 00259 } 00260 00261 } 00262 else 00263 { // direction > 0 00264 if( it->time > time ) 00265 { 00266 // The match is greater than time. Success. 00267 outIndex = it - _key_frames.begin( ); 00268 } 00269 else 00270 { // (it->time == time) 00271 // The match is exact. If there's a next element, use that. 00272 it++; 00273 if( it != _key_frames.end( ) ) 00274 outIndex = it - _key_frames.begin( ); 00275 else 00276 BOOST_THROW_EXCEPTION( ofx::OfxhException( kOfxStatFailed ) ); 00277 } 00278 } 00279 } 00280 00281 void deleteKey( const OfxTime time ) OFX_EXCEPTION_SPEC 00282 { 00283 TimeValue<T> temp; 00284 TimeValueTIterator it; 00285 00286 temp.time = time; 00287 it = find( _key_frames.begin( ), _key_frames.end( ), temp ); 00288 if( it != _key_frames.end( ) ) 00289 _key_frames.erase( it ); 00290 else 00291 BOOST_THROW_EXCEPTION( ofx::OfxhException( kOfxStatErrBadIndex ) ); 00292 } 00293 00294 void deleteAllKeys( ) OFX_EXCEPTION_SPEC 00295 { 00296 _key_frames.clear( ); 00297 } 00298 00299 /* ======= END OfxhKeyframeParam functions ======= */ 00300 00301 bool paramTypeHasData() const { return true; } 00302 00303 std::size_t getHash() const 00304 { 00305 std::size_t seed = 0; 00306 BOOST_FOREACH( const TimeValue<T>& tv, _key_frames ) 00307 { 00308 boost::hash_combine( seed, tv.time ); 00309 boost::hash_combine( seed, tv.value ); 00310 } 00311 return seed; 00312 } 00313 00314 std::size_t getHashAtTime_valueChangeToEnd( const OfxTime time ) const 00315 { 00316 T valueAtTime = 0; 00317 getValueAtTime( time, valueAtTime ); 00318 std::size_t seed = 0; 00319 BOOST_FOREACH( const TimeValue<T>& tv, _key_frames ) 00320 { 00321 if( tv.time >= time ) 00322 { 00323 boost::hash_combine( seed, tv.time ); 00324 boost::hash_combine( seed, tv.value ); 00325 } 00326 } 00327 return seed; 00328 } 00329 00330 std::size_t getHashAtTime( const OfxTime time ) const 00331 { 00332 const std::string cacheInvalidation = getCacheInvalidation(); 00333 if( cacheInvalidation == kOfxParamInvalidateAll ) 00334 { 00335 std::size_t seed = getHash(); 00336 boost::hash_combine( seed, time ); 00337 return seed; 00338 } 00339 else if( cacheInvalidation == kOfxParamInvalidateValueChangeToEnd ) 00340 { 00341 return getHashAtTime_valueChangeToEnd( time ); 00342 } 00343 else 00344 { 00345 BOOST_ASSERT( cacheInvalidation == kOfxParamInvalidateValueChange ); 00346 00347 // Standard case 00348 T valueAtTime = 0; 00349 getValueAtTime( time, valueAtTime ); 00350 return boost::hash_value( valueAtTime ); 00351 } 00352 } 00353 }; 00354 00355 } 00356 } 00357 } 00358 00359 #endif