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 : Param( effect ) 00053 , OFX_PARAM( descriptor, name, effect.getParamSet( ), index ) 00054 , _interpolator( new LinearInterpolator<T>() ) 00055 { 00056 } 00057 00058 AnimatedParam( const AnimatedParam<T, OFX_PARAM>& other ) 00059 : Param( other ) 00060 , OFX_PARAM( other ) 00061 , _interpolator( other._interpolator->clone() ) 00062 { 00063 } 00064 00065 ~AnimatedParam( ) 00066 { 00067 } 00068 00069 void setInterpolator( const ofx::attribute::EInterpolatorType interpolatorType ) OFX_EXCEPTION_SPEC 00070 { 00071 switch( interpolatorType ) 00072 { 00073 case ofx::attribute::eSmoothInterpolator: 00074 _interpolator.reset( new SmoothInterpolator<T>() ); 00075 break; 00076 case ofx::attribute::eFastInterpolator: 00077 _interpolator.reset( new FastInterpolator<T>() ); 00078 break; 00079 case ofx::attribute::eSlowInterpolator: 00080 _interpolator.reset( new SlowInterpolator<T>() ); 00081 break; 00082 case ofx::attribute::eLinearInterpolator: 00083 _interpolator.reset( new LinearInterpolator<T>() ); 00084 break; 00085 } 00086 } 00087 00088 void getValue( T& v ) const OFX_EXCEPTION_SPEC 00089 { 00090 v = _value; 00091 } 00092 00093 void getValueAtTime( const OfxTime time, T& v ) const OFX_EXCEPTION_SPEC 00094 { 00095 if( _key_frames.size( ) == 0 ) 00096 v = _value; 00097 else 00098 { 00099 00100 /* Find the key frames surrounding this time 00101 00102 Set prev to the first key frame before "time" or the first one 00103 after "time" if there's not one before. 00104 00105 Set next to the first key frame after "time" or the first one 00106 before "time" if there's not one after */ 00107 00108 TimeValue<T> temp, prev, next; 00109 TimeValueTConstIterator it; 00110 00111 // Find the first item at or before time 00112 temp.time = time; 00113 it = std::lower_bound( _key_frames.begin( ), _key_frames.end( ), temp ); 00114 00115 if( it->time == time || it == _key_frames.begin( ) ) 00116 { 00117 // There's a key frame at exactly this time 00118 // or this is the first key frame. Return this key frame value 00119 v = it->value; 00120 return; 00121 } 00122 else if( it == _key_frames.end( ) ) 00123 { 00124 // There's no key frame at or after this time 00125 // Return the last key frame value 00126 it--; 00127 v = it->value; 00128 return; 00129 } 00130 else 00131 { 00132 // The time is between two values. 00133 // Get the previous key frame and interpolate 00134 next = *it; 00135 it--; 00136 prev = *it; 00137 _interpolator->getValue( prev, next, ( const OfxTime ) time, v ); 00138 } 00139 } 00140 } 00141 00142 void setValue( const T& v, const ofx::attribute::EChange change ) OFX_EXCEPTION_SPEC 00143 { 00144 /* Setting a single value for this param clears the animation */ 00145 _key_frames.clear( ); 00146 _value = v; 00147 this->paramChanged( change ); 00148 } 00149 00150 void setValueAtTime( const OfxTime time, const T& v, const ofx::attribute::EChange change ) OFX_EXCEPTION_SPEC 00151 { 00152 TimeValue<T> new_tv; 00153 TimeValueTIterator it; 00154 00155 new_tv.time = time; 00156 new_tv.value = v; 00157 it = find( _key_frames.begin( ), _key_frames.end( ), new_tv ); 00158 if( it == _key_frames.end( ) ) 00159 { 00160 _key_frames.push_back( new_tv ); 00161 std::sort( _key_frames.begin( ), _key_frames.end( ) ); 00162 } 00163 else 00164 { 00165 ( *it ).value = v; 00166 } 00167 this->paramChanged( change ); 00168 } 00169 00170 void setValueFromExpression( const std::string& value, const ofx::attribute::EChange change ) OFX_EXCEPTION_SPEC 00171 { 00172 _value = extractValueFromExpression<T>( value ); 00173 this->paramChanged( change ); 00174 } 00175 00176 void derive( const OfxTime time, T& ) const OFX_EXCEPTION_SPEC 00177 { 00178 BOOST_THROW_EXCEPTION( ofx::OfxhException( kOfxStatErrMissingHostFeature ) ); 00179 } 00180 00181 void integrate( const OfxTime time1, const OfxTime time2, T& ) const OFX_EXCEPTION_SPEC 00182 { 00183 BOOST_THROW_EXCEPTION( ofx::OfxhException( kOfxStatErrMissingHostFeature ) ); 00184 } 00185 00186 void copy( const AnimatedParam<T, OFX_PARAM>& p ) OFX_EXCEPTION_SPEC 00187 { 00188 _value = p._value; 00189 _key_frames = p._key_frames; 00190 // paramChanged( ofx::attribute::eChangeUserEdited ); 00191 } 00192 00193 void copy( const ofx::attribute::OfxhParam& p ) OFX_EXCEPTION_SPEC 00194 { 00195 const AnimatedParam<T, OFX_PARAM>& param = dynamic_cast < const AnimatedParam<T, OFX_PARAM>& > ( p ); 00196 00197 copy( param ); 00198 } 00199 00200 This* clone() const 00201 { 00202 return new This( *this ); 00203 } 00204 00205 /* ======= BEGIN OfxhKeyframeParam functions ======= 00206 00207 Since this implementation is backed by a set, indexes may change and 00208 they are no longer valid after a key frame is set or deleted. */ 00209 void getNumKeys( unsigned int& outNumKeys ) const OFX_EXCEPTION_SPEC 00210 { 00211 outNumKeys = _key_frames.size( ); 00212 } 00213 00214 void getKeyTime( const int nth, OfxTime& outTime ) const OFX_EXCEPTION_SPEC 00215 { 00216 if( nth >= 0 && ( size_t ) nth < _key_frames.size( ) ) 00217 outTime = _key_frames[nth].time; 00218 else 00219 BOOST_THROW_EXCEPTION( ofx::OfxhException( kOfxStatErrBadIndex ) ); 00220 } 00221 00222 void getKeyIndex( const OfxTime time, const int direction, int& outIndex ) const OFX_EXCEPTION_SPEC 00223 { 00224 TimeValue<T> temp; 00225 TimeValueTConstIterator it; 00226 00227 // Find the first item at or before time 00228 temp.time = time; 00229 it = std::lower_bound( _key_frames.begin( ), _key_frames.end( ), temp ); 00230 00231 if( it == _key_frames.end( ) ) 00232 BOOST_THROW_EXCEPTION( ofx::OfxhException( kOfxStatFailed ) ); 00233 00234 if( direction == 0 ) 00235 { 00236 // Find an exact match or fail 00237 if( it->time == time ) 00238 { 00239 outIndex = it - _key_frames.begin( ); 00240 } 00241 else 00242 { 00243 BOOST_THROW_EXCEPTION( ofx::OfxhException( kOfxStatFailed ) ); 00244 } 00245 00246 } 00247 else if( direction < 0 ) 00248 { 00249 // Find the key frame directly before the match. If the match is the first element, fail. 00250 if( it == _key_frames.begin( ) ) 00251 BOOST_THROW_EXCEPTION( ofx::OfxhException( kOfxStatFailed ) ); 00252 else 00253 { 00254 outIndex = it - _key_frames.begin( ) - 1; 00255 00256 } 00257 00258 } 00259 else 00260 { // direction > 0 00261 if( it->time > time ) 00262 { 00263 // The match is greater than time. Success. 00264 outIndex = it - _key_frames.begin( ); 00265 } 00266 else 00267 { // (it->time == time) 00268 // The match is exact. If there's a next element, use that. 00269 it++; 00270 if( it != _key_frames.end( ) ) 00271 outIndex = it - _key_frames.begin( ); 00272 else 00273 BOOST_THROW_EXCEPTION( ofx::OfxhException( kOfxStatFailed ) ); 00274 } 00275 } 00276 } 00277 00278 void deleteKey( const OfxTime time ) OFX_EXCEPTION_SPEC 00279 { 00280 TimeValue<T> temp; 00281 TimeValueTIterator it; 00282 00283 temp.time = time; 00284 it = find( _key_frames.begin( ), _key_frames.end( ), temp ); 00285 if( it != _key_frames.end( ) ) 00286 _key_frames.erase( it ); 00287 else 00288 BOOST_THROW_EXCEPTION( ofx::OfxhException( kOfxStatErrBadIndex ) ); 00289 } 00290 00291 void deleteAllKeys( ) OFX_EXCEPTION_SPEC 00292 { 00293 _key_frames.clear( ); 00294 } 00295 00296 /* ======= END OfxhKeyframeParam functions ======= */ 00297 00298 bool paramTypeHasData() const { return true; } 00299 00300 std::size_t getHash() const 00301 { 00302 std::size_t seed = 0; 00303 BOOST_FOREACH( const TimeValue<T>& tv, _key_frames ) 00304 { 00305 boost::hash_combine( seed, tv.time ); 00306 boost::hash_combine( seed, tv.value ); 00307 } 00308 return seed; 00309 } 00310 00311 std::size_t getHashAtTime_valueChangeToEnd( const OfxTime time ) const 00312 { 00313 T valueAtTime = 0; 00314 getValueAtTime( time, valueAtTime ); 00315 std::size_t seed = 0; 00316 BOOST_FOREACH( const TimeValue<T>& tv, _key_frames ) 00317 { 00318 if( tv.time >= time ) 00319 { 00320 boost::hash_combine( seed, tv.time ); 00321 boost::hash_combine( seed, tv.value ); 00322 } 00323 } 00324 return seed; 00325 } 00326 00327 std::size_t getHashAtTime( const OfxTime time ) const 00328 { 00329 const std::string cacheInvalidation = getCacheInvalidation(); 00330 if( cacheInvalidation == kOfxParamInvalidateAll ) 00331 { 00332 std::size_t seed = getHash(); 00333 boost::hash_combine( seed, time ); 00334 return seed; 00335 } 00336 else if( cacheInvalidation == kOfxParamInvalidateValueChangeToEnd ) 00337 { 00338 return getHashAtTime_valueChangeToEnd( time ); 00339 } 00340 else 00341 { 00342 BOOST_ASSERT( cacheInvalidation == kOfxParamInvalidateValueChange ); 00343 00344 // Standard case 00345 T valueAtTime = 0; 00346 getValueAtTime( time, valueAtTime ); 00347 return boost::hash_value( valueAtTime ); 00348 } 00349 } 00350 }; 00351 00352 /** 00353 * @brief This class is just a re-implementation dedicated to Double Param. 00354 * It allows implicit conversion from Int to Double. 00355 */ 00356 class AnimatedParamDouble : public AnimatedParam<double, ofx::attribute::OfxhParamDouble> 00357 { 00358 typedef AnimatedParam<double, ofx::attribute::OfxhParamDouble> Parent; 00359 00360 public: 00361 00362 AnimatedParamDouble( 00363 INode& effect, 00364 const std::string& name, 00365 const ofx::attribute::OfxhParamDescriptor& descriptor, 00366 const std::size_t index ) 00367 : Parent( effect, name, descriptor, index ) 00368 {} 00369 00370 inline void setValue( const double& v, const ofx::attribute::EChange change ) OFX_EXCEPTION_SPEC 00371 { 00372 Parent::setValue( v, change ); 00373 } 00374 00375 inline void setValueAtTime( const OfxTime time, const double& v, const ofx::attribute::EChange change ) OFX_EXCEPTION_SPEC 00376 { 00377 Parent::setValueAtTime( time, v, change ); 00378 } 00379 00380 inline void setValue( const int& v, const ofx::attribute::EChange change ) OFX_EXCEPTION_SPEC 00381 { 00382 Parent::setValue( (double)v, change ); 00383 } 00384 00385 inline void setValueAtTime( const OfxTime time, const int& v, const ofx::attribute::EChange change ) OFX_EXCEPTION_SPEC 00386 { 00387 Parent::setValueAtTime( time, (double)v, change ); 00388 } 00389 00390 }; 00391 00392 typedef AnimatedParam<int, ofx::attribute::OfxhParamInteger> AnimatedParamInteger; 00393 } 00394 } 00395 } 00396 00397 #endif