TuttleOFX
1
|
00001 #ifndef _SAM_NODE_HPP_ 00002 #define _SAM_NODE_HPP_ 00003 00004 #include <tuttle/host/Core.hpp> 00005 00006 #include <boost/algorithm/string/case_conv.hpp> 00007 #include <boost/foreach.hpp> 00008 #include <boost/regex.hpp> 00009 00010 #include <map> 00011 00012 namespace sam { 00013 00014 inline std::string retrieveNodeFullname( const std::string& userId ) 00015 { 00016 std::string userIdLower = userId; 00017 boost::to_lower( userIdLower ); 00018 00019 std::vector<std::string> results; 00020 std::vector<std::string> ambiguousResults; 00021 00022 typedef std::map<std::string, tuttle::host::ofx::imageEffect::OfxhImageEffectPlugin*> PluginMap; 00023 const PluginMap& pluginsById = tuttle::host::core().getImageEffectPluginCache().getPluginsByID(); 00024 00025 if( pluginsById.find(userIdLower) != pluginsById.end() ) 00026 { 00027 return userIdLower; 00028 } 00029 00030 BOOST_FOREACH( const PluginMap::value_type& plugin, pluginsById ) 00031 { 00032 const std::string& plugId = plugin.first; 00033 boost::iterator_range<std::string::const_iterator> it = boost::algorithm::find_last( plugId, userIdLower ); 00034 if( it ) 00035 { 00036 if( ( it.end() == plugId.end() ) && 00037 ( *(it.begin()-1) == '.' ) 00038 ) 00039 { 00040 results.push_back( plugId ); 00041 } 00042 else 00043 { 00044 ambiguousResults.push_back( plugId ); 00045 } 00046 } 00047 } 00048 if( results.size() == 1 ) 00049 return results.front(); 00050 if( results.size() > 1 ) 00051 { 00052 ambiguousResults.insert( ambiguousResults.begin(), results.begin(), results.end() ); 00053 } 00054 if( ambiguousResults.size() == 1 ) 00055 return ambiguousResults.front(); 00056 00057 if( ambiguousResults.size() > 1 ) 00058 { 00059 tuttle::exception::user userMsg; 00060 userMsg + "Ambiguous node name \"" + userId + "\".\n"; 00061 userMsg + "Possible nodes:\n"; 00062 BOOST_FOREACH( const std::string& p, ambiguousResults ) 00063 { 00064 userMsg + " - \"" + p + "\"\n"; 00065 } 00066 BOOST_THROW_EXCEPTION( tuttle::exception::Value() 00067 << userMsg ); 00068 } 00069 00070 BOOST_THROW_EXCEPTION( tuttle::exception::Value() 00071 << tuttle::exception::user() + "Unrecognized node name \"" + userId + "\"." ); 00072 } 00073 00074 typedef std::pair<ttl::ofx::attribute::OfxhClipImage*, std::string> ClipAndConnection; 00075 00076 void setParametersForNode( const std::vector<std::string> parameters, ttl::Graph::Node& currentNode, std::vector<ClipAndConnection>& clipsToConnect, const bool isDummyNode = false, bool orderedParams = true ) 00077 { 00078 static const boost::regex re_param( "(?:([a-zA-Z_][a-zA-Z0-9_]*)=)?(.*)" ); 00079 std::size_t paramIdx = 0; 00080 00081 BOOST_FOREACH( const std::string& paramStr, parameters ) 00082 { 00083 boost::cmatch matches; 00084 if( !boost::regex_match( paramStr.c_str(), matches, re_param ) ) 00085 { 00086 BOOST_THROW_EXCEPTION( 00087 tuttle::exception::Value() << 00088 tuttle::exception::user() + "Parameter can't be parsed \"" + paramStr + "\"." ); 00089 } 00090 if( matches.size() != 3 ) 00091 { 00092 // should never happen 00093 BOOST_THROW_EXCEPTION( 00094 tuttle::exception::Value() << 00095 tuttle::exception::user() + "Parameter can't be parsed \"" + paramStr + "\". " + matches.size() + " matches." ); 00096 } 00097 const std::string attributeName = matches[1]; 00098 const std::string attributeValue = matches[2]; 00099 if( attributeName.size() ) 00100 { 00101 // if we start using non-ordered param (== named param) 00102 // we can't use ordered parameters anymore 00103 orderedParams = false; 00104 } 00105 else if( orderedParams == false ) 00106 { 00107 BOOST_THROW_EXCEPTION( 00108 tuttle::exception::Value() << 00109 tuttle::exception::user() + "Non-keyword parameter after keyword parameter. \"" + paramStr + "\"." ); 00110 } 00111 //TUTTLE_LOG_INFO( "* " << paramStr ); 00112 //TUTTLE_LOG_INFO( "3: " << paramName << " => " << paramValue ); 00113 00114 if( isDummyNode ) 00115 { 00116 ++paramIdx; 00117 continue; 00118 } 00119 00120 // setup the node with parameter value in tuttle. 00121 if( attributeName.size() ) 00122 { 00123 // set a value to a named parameter or clip 00124 using namespace ttl::ofx::attribute; 00125 OfxhParam* param = NULL; 00126 param = currentNode.getParamSet().getParamPtrByScriptName( attributeName ); 00127 00128 OfxhClipImage* clip = NULL; 00129 00130 if( param == NULL ) 00131 { 00132 // search in clips 00133 clip = currentNode.getClipImageSet().getClipPtr( attributeName ); 00134 } 00135 00136 if( param == NULL && clip == NULL ) 00137 { 00138 std::vector<std::string> allAttr; 00139 std::vector<std::string> paramMatches; 00140 std::vector<std::string> clipMatches; 00141 //if( acceptPartialName ) // if sam-do accept partial names 00142 { 00143 00144 BOOST_FOREACH( OfxhParamSet::ParamMap::value_type& p, currentNode.getParamSet().getParamsByScriptName() ) 00145 { 00146 allAttr.push_back( p.first ); 00147 if( boost::algorithm::starts_with( p.first, attributeName ) ) 00148 { 00149 paramMatches.push_back( p.first ); 00150 param = p.second; 00151 } 00152 } 00153 00154 BOOST_FOREACH( OfxhClipImageSet::ClipImageMap::value_type& c, currentNode.getClipImageSet().getClipsByName() ) 00155 { 00156 allAttr.push_back( c.first ); 00157 if( boost::algorithm::starts_with( c.first, attributeName ) ) 00158 { 00159 clipMatches.push_back( c.first ); 00160 clip = c.second; 00161 } 00162 } 00163 if( paramMatches.size() + clipMatches.size() > 1 ) 00164 { 00165 std::vector<std::string> matches; 00166 matches.insert( matches.begin(), paramMatches.begin(), paramMatches.end() ); 00167 matches.insert( matches.end(), clipMatches.begin(), clipMatches.end() ); 00168 BOOST_THROW_EXCEPTION( 00169 ttl::exception::Value() << 00170 ttl::exception::user() + "Ambiguous partial attribute name \"" + attributeName + "\". Possible values are: " + boost::algorithm::join( matches, ", " ) + "." ); 00171 } 00172 } 00173 00174 if( paramMatches.size() + clipMatches.size() == 0 ) 00175 { 00176 BOOST_THROW_EXCEPTION( 00177 ttl::exception::Value() << 00178 ttl::exception::user() + "Attribute name \"" + attributeName + "\" not found. Possible values are: " + boost::algorithm::join( allAttr, ", " ) + "." ); 00179 } 00180 } 00181 00182 if( param != NULL ) 00183 { 00184 param->setValueFromExpression( attributeValue ); 00185 } 00186 else if( clip != NULL ) 00187 { 00188 clipsToConnect.push_back( ClipAndConnection( clip, attributeValue ) ); 00189 } 00190 else 00191 { 00192 BOOST_THROW_EXCEPTION( 00193 ttl::exception::Value() << 00194 ttl::exception::user() + "Parameter or clip name " + tuttle::quotes( attributeName ) + " not found." ); 00195 } 00196 } 00197 else 00198 { 00199 currentNode.getParam( paramIdx ).setValueFromExpression( attributeValue ); 00200 } 00201 ++paramIdx; 00202 } 00203 } 00204 00205 void connectClips( const std::vector<ttl::Graph::Node*> nodes, const std::vector<ClipAndConnection> clipsToConnect, ttl::Graph::Node& currentNode, ttl::Graph& graph, const std::vector<std::string> idNames ) 00206 { 00207 // connect current node to previous node(s) 00208 if( nodes.size() > 0 ) // if not the first node 00209 { 00210 if( clipsToConnect.size() == 0 ) 00211 { 00212 // No clip connection specified, so by default 00213 // we connect the new node to the last previous node 00214 00215 /// @todo We only check if we have more than one clip (assuming that it's the default "Output" clip...) 00216 /// instead of checking the number of input clips... 00217 // if we have an input clip 00218 if( currentNode.getClipImageSet().getClipsByName().size() > 1 ) 00219 { 00220 graph.connect( *nodes.back(), currentNode ); 00221 } 00222 } 00223 else 00224 { 00225 // The user has specified some clips to connect 00226 00227 BOOST_FOREACH( const ClipAndConnection& clip, clipsToConnect ) 00228 { 00229 //TUTTLE_TCOUT_VAR3( clip.second, currentNode.getName(), clip.first->getName() ); 00230 00231 if( clip.second.size() <= 1 && ( clip.second == " " || clip.second == "!" || clip.second == "/" || clip.second == "-" ) ) 00232 { 00233 // these keywords allows to keep this clip unconnected 00234 //TUTTLE_TCOUT( "Don't connect the clip " << clip.first->getName() ); 00235 continue; 00236 } 00237 try 00238 { 00239 //TUTTLE_TCOUT( "Connect the clip " << clip.first->getName() ); 00240 // test if it's an index 00241 const int relativeIndex = std::abs( boost::lexical_cast<int>( clip.second ) ); 00242 const int absIndex = nodes.size() - relativeIndex; 00243 if( absIndex < 0 || absIndex >= (int) nodes.size() ) 00244 { 00245 using namespace ttl; 00246 using tuttle::quotes; 00247 BOOST_THROW_EXCEPTION( 00248 exception::Value() << 00249 exception::user() + "The relative index \"" + -relativeIndex + "\" for the connection of the clip " + quotes( clip.first->getName() ) + " on node " + quotes( currentNode.getName() ) + " is not valid." ); 00250 } 00251 graph.connect( *nodes[absIndex], currentNode.getAttribute( clip.first->getName() ) ); 00252 } 00253 catch( ... ) 00254 { 00255 // It's not an index so we assume, it's the name/id of the clip. 00256 // If the node doesn't exist it will throw an exception. 00257 try 00258 { 00259 graph.connect( graph.getNode( clip.second ), currentNode.getAttribute( clip.first->getName() ) ); 00260 } 00261 catch( ... ) 00262 { 00263 using namespace ttl; 00264 using tuttle::quotes; 00265 00266 BOOST_THROW_EXCEPTION( 00267 exception::Failed() 00268 << exception::user() + "unable to connect clip " + quotes( clip.first->getName() ) + ", with the id " + quotes( clip.second ) + ". Possible id's are: " + boost::algorithm::join( idNames, ", " ) ); 00269 } 00270 } 00271 } 00272 } 00273 } 00274 } 00275 00276 } 00277 #endif