1 /* 2 Copyright (c) 2008-2009 NetAllied Systems GmbH 3 4 This file is part of COLLADASaxFrameworkLoader. 5 6 Licensed under the MIT Open Source License, 7 for details please see LICENSE file or the website 8 http://www.opensource.org/licenses/mit-license.php 9 */ 10 11 #include "COLLADASaxFWLStableHeaders.h" 12 #include "COLLADASaxFWLFormulasLinker.h" 13 #include "COLLADASaxFWLSidAddress.h" 14 #include "COLLADASaxFWLSidTreeNode.h" 15 #include "COLLADASaxFWLCOLLADACsymbol.h" 16 #include "COLLADASaxFWLFileLoader.h" 17 18 #include "MathMLASTNode.h" 19 #include "MathMLASTArithmeticExpression.h" 20 #include "MathMLASTBinaryComparisionExpression.h" 21 #include "MathMLASTConstantExpression.h" 22 #include "MathMLASTFragmentExpression.h" 23 #include "MathMLASTFunctionExpression.h" 24 #include "MathMLASTLogicExpression.h" 25 #include "MathMLASTUnaryArithmeticExpression.h" 26 #include "MathMLASTVariableExpression.h" 27 28 namespace COLLADASaxFWL 29 { 30 31 //------------------------------ FormulasLinker(DocumentProcessor * documentProcessor,COLLADAFW::FormulaArray & formulas)32 FormulasLinker::FormulasLinker( DocumentProcessor* documentProcessor, COLLADAFW::FormulaArray& formulas ) 33 : mDocumentProcessor(documentProcessor) 34 , mFormulas(formulas) 35 { 36 } 37 38 //------------------------------ ~FormulasLinker()39 FormulasLinker::~FormulasLinker() 40 { 41 } 42 43 //------------------------------ link()44 bool FormulasLinker::link() 45 { 46 size_t formulasCount = mFormulas.getCount(); 47 for ( size_t i = 0; i < formulasCount; ++i) 48 { 49 COLLADAFW::Formula* formula = mFormulas[i]; 50 COLLADAFW::MathmlAstArray& asts = formula->getMathmlAsts(); 51 size_t astsCount = asts.getCount(); 52 for ( size_t j = 0; j < astsCount; ++j) 53 { 54 MathML::AST::INode*& astNode = asts[j]; 55 bool success = true; 56 astNode = link( formula, astNode, success); 57 if ( !success ) 58 return false; 59 } 60 } 61 return true; 62 } 63 64 //------------------------------ link(COLLADAFW::Formula * formula,MathML::AST::INode * astNode,bool & success)65 MathML::AST::INode* FormulasLinker::link( COLLADAFW::Formula* formula, MathML::AST::INode* astNode, bool& success) 66 { 67 switch ( astNode->getNodeType() ) 68 { 69 case MathML::AST::INode::ARITHMETIC: 70 { 71 MathML::AST::ArithmeticExpression* arithmetic = (MathML::AST::ArithmeticExpression*)astNode; 72 MathML::AST::NodeList& operands = arithmetic->getOperands(); 73 for ( size_t i = 0, count = operands.size(); i < count; ++i ) 74 { 75 operands[i] = link(formula, operands[i], success); 76 } 77 } 78 return astNode; 79 case MathML::AST::INode::COMPARISON: 80 { 81 MathML::AST::BinaryComparisonExpression* comparison = (MathML::AST::BinaryComparisonExpression*)astNode; 82 comparison->setLeftOperand(link(formula, comparison->getLeftOperand(), success)); 83 comparison->setRightOperand(link(formula, comparison->getRightOperand(), success)); 84 } 85 return astNode; 86 case MathML::AST::INode::CONSTANT: 87 case MathML::AST::INode::VARIABLE: 88 success = true; 89 return astNode; 90 case MathML::AST::INode::FRAGMENT: 91 { 92 MathML::AST::FragmentExpression* fragment = (MathML::AST::FragmentExpression*)astNode; 93 fragment->setFragment( link( formula, fragment->getFragment(), success) ); 94 } 95 return astNode; 96 case MathML::AST::INode::LOGICAL: 97 { 98 MathML::AST::LogicExpression* logical = (MathML::AST::LogicExpression*)astNode; 99 MathML::AST::NodeList& operands = logical->getOperands(); 100 for ( size_t i = 0, count = operands.size(); i < count; ++i ) 101 { 102 operands[i] = link(formula, operands[i], success); 103 } 104 } 105 return astNode; 106 case MathML::AST::INode::UNARY: 107 { 108 MathML::AST::UnaryExpression* unary = (MathML::AST::UnaryExpression*)astNode; 109 unary->setOperand(link(formula, unary->getOperand(), success)); 110 } 111 return astNode; 112 case MathML::AST::INode::FUNCTION: 113 { 114 MathML::AST::FunctionExpression* func = (MathML::AST::FunctionExpression*)astNode; 115 MathML::AST::NodeList& operands = func->getParameterList(); 116 for ( size_t i = 0, count = operands.size(); i < count; ++i ) 117 { 118 operands[i] = link(formula, operands[i], success); 119 } 120 } 121 return astNode; 122 case MathML::AST::INode::USERDEFINED: 123 { 124 COLLADACsymbol* csymbol = (COLLADACsymbol*) astNode; 125 MathML::AST::INode* linkedNode = link( formula, csymbol, success ); 126 delete csymbol; 127 return linkedNode; 128 } 129 default: 130 COLLADABU_ASSERT(false); 131 } 132 return 0; 133 } 134 135 //------------------------------ link(COLLADAFW::Formula * formula,COLLADACsymbol * csymbol,bool & success)136 MathML::AST::INode* FormulasLinker::link( COLLADAFW::Formula* formula, COLLADACsymbol* csymbol, bool& success) 137 { 138 const SidAddress& targetAddress = csymbol->getSidAddress(); 139 140 const String* csymbolName = 0; 141 // if the address consist of a single word, this is considered to be the csymbol name. 142 if ( !targetAddress.getId().empty() && 143 targetAddress.getSids().empty() && 144 targetAddress.getMemberSelection() == SidAddress::MEMBER_SELECTION_NONE ) 145 { 146 csymbolName = &targetAddress.getId(); 147 } 148 149 if ( csymbol->getCSymbolType() == COLLADACsymbol::FUNCTION ) 150 { 151 const COLLADAFW::UniqueId& formulaUniqueId = csymbol->getFormulaUniqueId(); 152 153 COLLADAFW::Formula* targetFormula = mDocumentProcessor->getFormulaByUniqueId(formulaUniqueId); 154 155 MathML::AST::FragmentExpression* fragmentExpression = 0; 156 157 if ( targetFormula ) 158 { 159 // find a suitable name for the fragment expression 160 // if possible use the string in the csymbol element, otherwise the name of the referenced fromula 161 const String* fragmentExpressionName = 0; 162 fragmentExpressionName = csymbolName ? csymbolName : &targetFormula->getName(); 163 164 fragmentExpression = new MathML::AST::FragmentExpression(*fragmentExpressionName, MathML::AST::INode::CLONEFLAG_DEEPCOPY_FRAGMENT_PARAMS); 165 COLLADAFW::MathmlAstArray& asts = targetFormula->getMathmlAsts(); 166 if ( asts.getCount() == 1) 167 { 168 // we have to ensure, that the fragment won't change anymore. Therefore we need to link immediately 169 bool fragmentSuccess = true; 170 asts[0] = link( targetFormula, asts[0], fragmentSuccess); 171 fragmentExpression->setFragment( asts[0] ); 172 if ( !fragmentSuccess ) 173 { 174 return 0; 175 } 176 } 177 const COLLADACsymbol::ParameterList& parameters = csymbol->getParameterList(); 178 const COLLADAFW::FormulaNewParamPointerArray& targetFormulaParameters = targetFormula->getNewParams(); 179 if ( parameters.size() != targetFormulaParameters.getCount() ) 180 { 181 String msg = "Number of newparams in formula \"" + *fragmentExpressionName; 182 const String& formulaName = formula->getName(); 183 if ( !formulaName.empty() ) 184 { 185 msg.append( "referenced in \"" + formulaName + "\" "); 186 } 187 msg.append( "does not match parameters count." ); 188 189 success = mDocumentProcessor->handleFWLError(SaxFWLError::ERROR_PARAMETER_COUNT_DOESNOT_MATCH, msg); 190 } 191 else 192 { 193 COLLADACsymbol::ParameterList::const_iterator it = parameters.begin(); 194 for ( size_t i = 0; it!=parameters.end(); ++it, ++i ) 195 { 196 const COLLADAFW::FormulaNewParam* parameter = targetFormulaParameters[i]; 197 fragmentExpression->addParameter(parameter->getName(), link( formula, *it, success)); 198 if ( !success ) 199 { 200 break; 201 } 202 } 203 } 204 } 205 else 206 { 207 // formula not found 208 fragmentExpression = new MathML::AST::FragmentExpression(targetAddress.getSidAddressString(), MathML::AST::INode::CLONEFLAG_DEEPCOPY_FRAGMENT_PARAMS); 209 210 String msg = "Formula with unique id\"" + formulaUniqueId.toAscii() + "\" "; 211 msg.append( "could not be found." ); 212 213 success = mDocumentProcessor->handleFWLError(SaxFWLError::ERROR_UNRESOLVED_FORMULA, msg); 214 } 215 return fragmentExpression; 216 } 217 else 218 { 219 MathML::AST::VariableExpression* variableExpression = 0; 220 221 // the csymbol is a parameter. 222 // if the address consist of a single word, it might be a parameter of the formula itself. 223 if ( csymbolName ) 224 { 225 //search for the parameter name in the formulas new params 226 bool found = false; 227 getNewParamIndex(formula, *csymbolName, found); 228 if ( found ) 229 { 230 // we are done. create new VariableExpression and return 231 variableExpression = FW_NEW MathML::AST::VariableExpression(*csymbolName); 232 success = true; 233 return variableExpression; 234 } 235 } 236 237 // Try to find the referenced parameter in the entire COLLADA file, using ordinary sid address 238 // syntax 239 const SidTreeNode* targetSidTreeNode = mDocumentProcessor->resolveSid( targetAddress ); 240 241 if ( !targetSidTreeNode ) 242 { 243 // parameter not found 244 String msg = "Parameter with sid address \"" + targetAddress.getSidAddressString() + "\" "; 245 const String& formulaName = formula->getName(); 246 if ( !formulaName.empty() ) 247 { 248 msg.append( "referenced in \"" + formulaName + "\" "); 249 } 250 msg.append( "could not be resolved." ); 251 252 success = mDocumentProcessor->handleFWLError(SaxFWLError::ERROR_UNRESOLVED_PARAMETER, msg); 253 } 254 else 255 { 256 // parameter found 257 if (targetSidTreeNode->getTargetType() == SidTreeNode::TARGETTYPECLASS_OBJECT) 258 { 259 // the parameter is an object, e.g. a joint 260 const COLLADAFW::Object* object = targetSidTreeNode->getObjectTarget(); 261 COLLADABU_ASSERT(object); 262 variableExpression = new MathML::AST::VariableExpression(object->getUniqueId().toAscii()); 263 } 264 else if (targetSidTreeNode->getTargetType() == SidTreeNode::TARGETTYPECLASS_INTERMEDIATETARGETABLE) 265 { 266 // the parameter is an object, e.g. a joint 267 const IntermediateTargetable* intermediateTargetable = targetSidTreeNode->getIntermediateTargetableTarget(); 268 COLLADABU_ASSERT(intermediateTargetable); 269 const KinematicInstance* kinematicInstance = intermediateTargetableSafeCast<KinematicInstance>(intermediateTargetable); 270 // for now we only support kinematic instances here 271 COLLADABU_ASSERT(kinematicInstance); 272 variableExpression = new MathML::AST::VariableExpression(kinematicInstance->getReplacingObjectUniqueId().toAscii()); 273 } 274 else 275 { 276 // TODO: animatabel not supported yet 277 success = false; 278 return 0; 279 } 280 281 } 282 success = true; 283 return variableExpression; 284 } 285 286 success = false; 287 return 0; 288 } 289 290 //------------------------------ getNewParamIndex(const COLLADAFW::Formula * formula,const String & parameterName,bool & found)291 size_t FormulasLinker::getNewParamIndex(const COLLADAFW::Formula* formula, const String& parameterName, bool &found) 292 { 293 const COLLADAFW::FormulaNewParamPointerArray& newParams = formula->getNewParams(); 294 for ( size_t i = 0, count = newParams.getCount(); i < count; ++i) 295 { 296 const COLLADAFW::FormulaNewParam* newParam = newParams[i]; 297 if ( newParam->getName() == parameterName ) 298 { 299 found = true; 300 return i; 301 } 302 } 303 found = false; 304 return 0; 305 } 306 307 } // namespace COLLADASaxFWL 308