00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025 #ifndef SOFA_COMPONENT_MASS_DIAGONALMASS_INL
00026 #define SOFA_COMPONENT_MASS_DIAGONALMASS_INL
00027
00028 #include <sofa/component/mass/DiagonalMass.h>
00029 #include <sofa/helper/io/MassSpringLoader.h>
00030 #include <sofa/helper/gl/template.h>
00031 #include <sofa/defaulttype/RigidTypes.h>
00032 #include <sofa/defaulttype/DataTypeInfo.h>
00033 #include <sofa/component/topology/TopologyChangedEvent.h>
00034 #include <sofa/component/topology/PointData.inl>
00035 #include <sofa/component/topology/RegularGridTopology.h>
00036 #include <sofa/component/mass/AddMToMatrixFunctor.h>
00037
00038 namespace sofa
00039 {
00040
00041 namespace component
00042 {
00043
00044 namespace mass
00045 {
00046
00047
00048 using namespace sofa::component::topology;
00049 using namespace core::componentmodel::topology;
00050
00051 template<class MassType>
00052 void MassPointCreationFunction(int ,
00053 void* , MassType & t,
00054 const sofa::helper::vector< unsigned int > &,
00055 const sofa::helper::vector< double >&)
00056 {
00057 t=0;
00058 }
00059
00060 template< class DataTypes, class MassType>
00061 inline void MassEdgeCreationFunction(const sofa::helper::vector<unsigned int> &edgeAdded,
00062 void* param, vector<MassType> &masses)
00063 {
00064 DiagonalMass<DataTypes, MassType> *dm= (DiagonalMass<DataTypes, MassType> *)param;
00065 if (dm->getMassTopologyType()==DiagonalMass<DataTypes, MassType>::TOPOLOGY_EDGESET) {
00066
00067 typename DataTypes::Real md=dm->getMassDensity();
00068 typename DataTypes::Real mass=typename DataTypes::Real();
00069 unsigned int i;
00070
00071 for (i=0;i<edgeAdded.size();++i) {
00073 const Edge &e=dm->_topology->getEdge(edgeAdded[i]);
00074
00075 if(dm->edgeGeo){
00076 mass=(md*dm->edgeGeo->computeRestEdgeLength(edgeAdded[i]))/(typename DataTypes::Real)2.0;
00077 }
00078
00079 masses[e[0]]+=mass;
00080 masses[e[1]]+=mass;
00081 }
00082
00083 }
00084 }
00085
00086 template< class DataTypes, class MassType>
00087 inline void MassEdgeDestroyFunction(const sofa::helper::vector<unsigned int> &edgeRemoved,
00088 void* param, vector<MassType> &masses)
00089 {
00090 DiagonalMass<DataTypes, MassType> *dm= (DiagonalMass<DataTypes, MassType> *)param;
00091 if (dm->getMassTopologyType()==DiagonalMass<DataTypes, MassType>::TOPOLOGY_EDGESET) {
00092
00093 typename DataTypes::Real md=dm->getMassDensity();
00094 typename DataTypes::Real mass=typename DataTypes::Real();
00095 unsigned int i;
00096
00097 for (i=0;i<edgeRemoved.size();++i) {
00099 const Edge &e=dm->_topology->getEdge(edgeRemoved[i]);
00100
00101 if(dm->edgeGeo){
00102 mass=(md*dm->edgeGeo->computeRestEdgeLength(edgeRemoved[i]))/(typename DataTypes::Real)2.0;
00103 }
00104
00105 masses[e[0]]-=mass;
00106 masses[e[1]]-=mass;
00107 }
00108
00109 }
00110 }
00111
00112 template< class DataTypes, class MassType>
00113 inline void MassTriangleCreationFunction(const sofa::helper::vector<unsigned int> &triangleAdded,
00114 void* param, vector<MassType> &masses)
00115 {
00116 DiagonalMass<DataTypes, MassType> *dm= (DiagonalMass<DataTypes, MassType> *)param;
00117 if (dm->getMassTopologyType()==DiagonalMass<DataTypes, MassType>::TOPOLOGY_TRIANGLESET) {
00118
00119 typename DataTypes::Real md=dm->getMassDensity();
00120 typename DataTypes::Real mass=typename DataTypes::Real();
00121 unsigned int i;
00122
00123 for (i=0;i<triangleAdded.size();++i) {
00125 const Triangle &t=dm->_topology->getTriangle(triangleAdded[i]);
00126
00127 if(dm->triangleGeo){
00128 mass=(md*dm->triangleGeo->computeRestTriangleArea(triangleAdded[i]))/(typename DataTypes::Real)3.0;
00129 }
00130
00131 masses[t[0]]+=mass;
00132 masses[t[1]]+=mass;
00133 masses[t[2]]+=mass;
00134 }
00135
00136 }
00137 }
00138
00139 template< class DataTypes, class MassType>
00140 inline void MassTriangleDestroyFunction(const sofa::helper::vector<unsigned int> &triangleRemoved,
00141 void* param, vector<MassType> &masses)
00142 {
00143 DiagonalMass<DataTypes, MassType> *dm= (DiagonalMass<DataTypes, MassType> *)param;
00144 if (dm->getMassTopologyType()==DiagonalMass<DataTypes, MassType>::TOPOLOGY_TRIANGLESET) {
00145
00146 typename DataTypes::Real md=dm->getMassDensity();
00147 typename DataTypes::Real mass=typename DataTypes::Real();
00148 unsigned int i;
00149
00150 for (i=0;i<triangleRemoved.size();++i) {
00152 const Triangle &t=dm->_topology->getTriangle(triangleRemoved[i]);
00153
00154 if(dm->triangleGeo){
00155 mass=(md*dm->triangleGeo->computeRestTriangleArea(triangleRemoved[i]))/(typename DataTypes::Real)3.0;
00156 }
00157
00158 masses[t[0]]-=mass;
00159 masses[t[1]]-=mass;
00160 masses[t[2]]-=mass;
00161
00162
00163
00164
00165 }
00166
00167 }
00168 }
00169
00170 template< class DataTypes, class MassType>
00171 inline void MassTetrahedronCreationFunction(const sofa::helper::vector<unsigned int> &tetrahedronAdded,
00172 void* param, vector<MassType> &masses)
00173 {
00174 DiagonalMass<DataTypes, MassType> *dm= (DiagonalMass<DataTypes, MassType> *)param;
00175 if (dm->getMassTopologyType()==DiagonalMass<DataTypes, MassType>::TOPOLOGY_TETRAHEDRONSET) {
00176
00177 typename DataTypes::Real md=dm->getMassDensity();
00178 typename DataTypes::Real mass=typename DataTypes::Real();
00179 unsigned int i;
00180
00181 for (i=0;i<tetrahedronAdded.size();++i) {
00183 const Tetrahedron &t=dm->_topology->getTetra(tetrahedronAdded[i]);
00184
00185 if(dm->tetraGeo){
00186 mass=(md*dm->tetraGeo->computeRestTetrahedronVolume(tetrahedronAdded[i]))/(typename DataTypes::Real)4.0;
00187 }
00188
00189 masses[t[0]]+=mass;
00190 masses[t[1]]+=mass;
00191 masses[t[2]]+=mass;
00192 masses[t[3]]+=mass;
00193
00194 }
00195
00196 }
00197 }
00198
00199 template< class DataTypes, class MassType>
00200 inline void MassTetrahedronDestroyFunction(const sofa::helper::vector<unsigned int> &tetrahedronRemoved,
00201 void* param, vector<MassType> &masses)
00202 {
00203 DiagonalMass<DataTypes, MassType> *dm= (DiagonalMass<DataTypes, MassType> *)param;
00204 if (dm->getMassTopologyType()==DiagonalMass<DataTypes, MassType>::TOPOLOGY_TETRAHEDRONSET) {
00205
00206 typename DataTypes::Real md=dm->getMassDensity();
00207 typename DataTypes::Real mass=typename DataTypes::Real();
00208 unsigned int i;
00209
00210 for (i=0;i<tetrahedronRemoved.size();++i) {
00212 const Tetrahedron &t=dm->_topology->getTetra(tetrahedronRemoved[i]);
00213 if(dm->tetraGeo){
00214
00215 mass=(md*dm->tetraGeo->computeRestTetrahedronVolume(tetrahedronRemoved[i]))/(typename DataTypes::Real)4.0;
00216 }
00217
00218 masses[t[0]]-=mass;
00219 masses[t[1]]-=mass;
00220 masses[t[2]]-=mass;
00221 masses[t[3]]-=mass;
00222 }
00223
00224 }
00225 }
00226
00227
00228 using namespace sofa::defaulttype;
00229 using namespace sofa::core::componentmodel::behavior;
00230
00231
00232 template <class DataTypes, class MassType>
00233 DiagonalMass<DataTypes, MassType>::DiagonalMass()
00234 : f_mass( initData(&f_mass, "mass", "values of the particles masses") )
00235 , m_massDensity( initData(&m_massDensity, (Real)1.0,"massDensity", "mass density that allows to compute the particles masses from a mesh topology and geometry.\nOnly used if > 0") )
00236 , showCenterOfGravity( initData(&showCenterOfGravity, false, "showGravityCenter", "display the center of gravity of the system" ) )
00237 , showAxisSize( initData(&showAxisSize, 1.0f, "showAxisSizeFactor", "factor length of the axis displayed (only used for rigids)" ) )
00238 , topologyType(TOPOLOGY_UNKNOWN)
00239 {
00240
00241 }
00242
00243
00244
00245
00246 template <class DataTypes, class MassType>
00247 DiagonalMass<DataTypes, MassType>::~DiagonalMass()
00248 {
00249 }
00250
00251 template <class DataTypes, class MassType>
00252 void DiagonalMass<DataTypes, MassType>::clear()
00253 {
00254 VecMass& masses = *f_mass.beginEdit();
00255 masses.clear();
00256 f_mass.endEdit();
00257 }
00258
00259 template <class DataTypes, class MassType>
00260 void DiagonalMass<DataTypes, MassType>::addMass(const MassType& m)
00261 {
00262 VecMass& masses = *f_mass.beginEdit();
00263 masses.push_back(m);
00264 f_mass.endEdit();
00265 }
00266
00267 template <class DataTypes, class MassType>
00268 void DiagonalMass<DataTypes, MassType>::resize(int vsize)
00269 {
00270 VecMass& masses = *f_mass.beginEdit();
00271 masses.resize(vsize);
00272 f_mass.endEdit();
00273 }
00274
00275
00276 template <class DataTypes, class MassType>
00277 void DiagonalMass<DataTypes, MassType>::addMDx(VecDeriv& res, const VecDeriv& dx, double factor)
00278 {
00279
00280 const MassVector &masses= f_mass.getValue();
00281 if (factor == 1.0)
00282 {
00283 for (unsigned int i=0;i<dx.size();i++)
00284 {
00285 res[i] += dx[i] * masses[i];
00286 }
00287 }
00288 else
00289 {
00290 for (unsigned int i=0;i<dx.size();i++)
00291 {
00292 res[i] += (dx[i] * masses[i]) * (Real)factor;
00293 }
00294 }
00295 }
00296
00297 template <class DataTypes, class MassType>
00298 void DiagonalMass<DataTypes, MassType>::accFromF(VecDeriv& a, const VecDeriv& f)
00299 {
00300
00301 const MassVector &masses= f_mass.getValue();
00302 for (unsigned int i=0;i<f.size();i++)
00303 {
00304 a[i] = f[i] / masses[i];
00305 }
00306 }
00307
00308 template <class DataTypes, class MassType>
00309 double DiagonalMass<DataTypes, MassType>::getKineticEnergy( const VecDeriv& v )
00310 {
00311
00312 const MassVector &masses= f_mass.getValue();
00313 double e = 0;
00314 for (unsigned int i=0;i<masses.size();i++)
00315 {
00316 e += v[i]*masses[i]*v[i];
00317 }
00318 return e/2;
00319 }
00320
00321 template <class DataTypes, class MassType>
00322 double DiagonalMass<DataTypes, MassType>::getPotentialEnergy( const VecCoord& x )
00323 {
00324
00325 const MassVector &masses= f_mass.getValue();
00326 SReal e = 0;
00327
00328 Vec3d g ( this->getContext()->getLocalGravity() );
00329 Deriv theGravity;
00330 DataTypes::set ( theGravity, g[0], g[1], g[2]);
00331 for (unsigned int i=0;i<masses.size();i++)
00332 {
00333 e -= theGravity*masses[i]*x[i];
00334 }
00335 return e;
00336 }
00337
00338 template <class DataTypes, class MassType>
00339 void DiagonalMass<DataTypes, MassType>::addMToMatrix(defaulttype::BaseMatrix * mat, double mFact, unsigned int &offset)
00340 {
00341 const MassVector &masses= f_mass.getValue();
00342 const int N = defaulttype::DataTypeInfo<Deriv>::size();
00343 AddMToMatrixFunctor<Deriv,MassType> calc;
00344 for (unsigned int i=0;i<masses.size();i++)
00345 calc(mat, masses[i], offset + N*i, mFact);
00346 }
00347
00348
00349 template <class DataTypes, class MassType>
00350 double DiagonalMass<DataTypes, MassType>::getElementMass(unsigned int index)
00351 {
00352 return (SReal)(f_mass.getValue()[index]);
00353 }
00354
00355
00356 template <class DataTypes, class MassType>
00357 void DiagonalMass<DataTypes, MassType>::handleTopologyChange()
00358 {
00359 std::list<const TopologyChange *>::const_iterator itBegin=_topology->firstChange();
00360 std::list<const TopologyChange *>::const_iterator itEnd=_topology->lastChange();
00361 std::list<const TopologyChange *>::const_iterator it;
00362
00363 VecMass& masses = *f_mass.beginEdit();
00364 masses.handleTopologyEvents(itBegin,itEnd);
00365 f_mass.endEdit();
00366 }
00367
00368
00369 template <class DataTypes, class MassType>
00370 void DiagonalMass<DataTypes, MassType>::reinit()
00371 {
00372 if (_topology && (m_massDensity.getValue() > 0 || f_mass.getValue().size() == 0))
00373 {
00374 if (_topology->getNbTetras()>0) {
00375
00376 VecMass& masses = *f_mass.beginEdit();
00377 topologyType=TOPOLOGY_TETRAHEDRONSET;
00378
00379
00380 clear();
00381 masses.resize(this->mstate->getSize());
00382
00383 for(unsigned int i=0;i<masses.size();++i)
00384 masses[i]=(Real)0;
00385
00386 Real md=m_massDensity.getValue();
00387 Real mass=Real();
00388
00389 for (int i=0;i<_topology->getNbTetras();++i) {
00390
00391 const Tetrahedron &t=_topology->getTetra(i);
00392 if(tetraGeo){
00393 mass=(md*tetraGeo->computeRestTetrahedronVolume(i))/(Real)4.0;
00394 }
00395 masses[t[0]]+=mass;
00396 masses[t[1]]+=mass;
00397 masses[t[2]]+=mass;
00398 masses[t[3]]+=mass;
00399 }
00400 f_mass.endEdit();
00401 }
00402 else if (_topology->getNbTriangles()>0) {
00403 VecMass& masses = *f_mass.beginEdit();
00404 topologyType=TOPOLOGY_TRIANGLESET;
00405
00406
00407 clear();
00408 masses.resize(this->mstate->getSize());
00409
00410 for(unsigned int i=0;i<masses.size();++i)
00411 masses[i]=(Real)0;
00412
00413 Real md=m_massDensity.getValue();
00414 Real mass=Real();
00415
00416 for (int i=0;i<_topology->getNbTriangles();++i) {
00417 const Triangle &t=_topology->getTriangle(i);
00418 if(triangleGeo){
00419 mass=(md*triangleGeo->computeRestTriangleArea(i))/(Real)3.0;
00420 }
00421 masses[t[0]]+=mass;
00422 masses[t[1]]+=mass;
00423 masses[t[2]]+=mass;
00424 }
00425 f_mass.endEdit();
00426 }
00427
00428
00429
00430
00431
00432
00433
00434
00435
00436
00437
00438
00439 else if (_topology->getNbEdges()>0) {
00440
00441 VecMass& masses = *f_mass.beginEdit();
00442 topologyType=TOPOLOGY_EDGESET;
00443
00444
00445 clear();
00446 masses.resize(this->mstate->getSize());
00447
00448 for(unsigned int i=0;i<masses.size();++i)
00449 masses[i]=(Real)0;
00450
00451 Real md=m_massDensity.getValue();
00452 Real mass=Real();
00453
00454 for (int i=0;i<_topology->getNbEdges();++i) {
00455 const Edge &e=_topology->getEdge(i);
00456 if(edgeGeo){
00457 mass=(md*edgeGeo->computeEdgeLength(i))/(Real)2.0;
00458 }
00459 masses[e[0]]+=mass;
00460 masses[e[1]]+=mass;
00461 }
00462 f_mass.endEdit();
00463 }
00464 }
00465 }
00466
00467 template <class DataTypes, class MassType>
00468 void DiagonalMass<DataTypes, MassType>::init()
00469 {
00470
00471
00472
00473
00474
00475
00476
00477
00478
00479
00480
00481
00482
00483
00484
00485
00486
00487
00488
00489
00490
00491
00492
00493
00494
00495
00496 _topology = this->getContext()->getMeshTopology();
00497
00498 this->getContext()->get(edgeGeo);
00499 this->getContext()->get(triangleGeo);
00500 this->getContext()->get(quadGeo);
00501 this->getContext()->get(tetraGeo);
00502 this->getContext()->get(hexaGeo);
00503
00504 Inherited::init();
00505
00506
00507
00508 VecMass& masses = *f_mass.beginEdit();
00509 masses.setCreateFunction(MassPointCreationFunction<MassType>);
00510 masses.setCreateEdgeFunction(MassEdgeCreationFunction<DataTypes,MassType>);
00511 masses.setDestroyEdgeFunction(MassEdgeDestroyFunction<DataTypes,MassType>);
00512 masses.setCreateTriangleFunction(MassTriangleCreationFunction<DataTypes,MassType>);
00513 masses.setDestroyTriangleFunction(MassTriangleDestroyFunction<DataTypes,MassType>);
00514 masses.setCreateTetrahedronFunction(MassTetrahedronCreationFunction<DataTypes,MassType>);
00515 masses.setDestroyTetrahedronFunction(MassTetrahedronDestroyFunction<DataTypes,MassType>);
00516
00517 masses.setCreateParameter( (void *) this );
00518 masses.setDestroyParameter( (void *) this );
00519 f_mass.endEdit();
00520
00521 if ((f_mass.getValue().size()==0) && (_topology!=0)) {
00522 reinit();
00523 }
00524 }
00525
00526 template <class DataTypes, class MassType>
00527 void DiagonalMass<DataTypes, MassType>::addGravityToV(double dt)
00528 {
00529 if(this->mstate){
00530 VecDeriv& v = *this->mstate->getV();
00531
00532
00533 Vec3d g ( this->getContext()->getLocalGravity() );
00534 Deriv theGravity;
00535 DataTypes::set ( theGravity, g[0], g[1], g[2]);
00536 Deriv hg = theGravity * (typename DataTypes::Real)dt;
00537
00538 for (unsigned int i=0;i<v.size();i++) {
00539 v[i] += hg;
00540 }
00541 }
00542 }
00543
00544 template <class DataTypes, class MassType>
00545 void DiagonalMass<DataTypes, MassType>::addForce(VecDeriv& f, const VecCoord& x, const VecDeriv& v)
00546 {
00547
00548 if(this->m_separateGravity.getValue())
00549 return;
00550
00551 const MassVector &masses= f_mass.getValue();
00552
00553
00554 Vec3d g ( this->getContext()->getLocalGravity() );
00555 Deriv theGravity;
00556 DataTypes::set ( theGravity, g[0], g[1], g[2]);
00557
00558
00559 core::objectmodel::BaseContext::SpatialVector vframe = getContext()->getVelocityInWorld();
00560 core::objectmodel::BaseContext::Vec3 aframe = getContext()->getVelocityBasedLinearAccelerationInWorld() ;
00561
00562
00563 vframe = getContext()->getPositionInWorld() / vframe;
00564 aframe = getContext()->getPositionInWorld().backProjectVector( aframe );
00565
00566
00567 for (unsigned int i=0;i<masses.size();i++) {
00568 f[i] += theGravity*masses[i] + core::componentmodel::behavior::inertiaForce(vframe,aframe,masses[i],x[i],v[i]);
00569 }
00570 }
00571
00572 template <class DataTypes, class MassType>
00573 void DiagonalMass<DataTypes, MassType>::draw()
00574 {
00575 if (!getContext()->getShowBehaviorModels()) return;
00576 const MassVector &masses= f_mass.getValue();
00577 const VecCoord& x = *this->mstate->getX();
00578 Coord gravityCenter;
00579 Real totalMass=0.0;
00580 glDisable (GL_LIGHTING);
00581 glPointSize(2);
00582 glColor4f (1,1,1,1);
00583 glBegin (GL_POINTS);
00584 for (unsigned int i=0; i<x.size(); i++)
00585 {
00586 helper::gl::glVertexT(x[i]);
00587 gravityCenter += x[i]*masses[i];
00588 totalMass += masses[i];
00589 }
00590 glEnd();
00591 if(showCenterOfGravity.getValue()){
00592 glBegin (GL_LINES);
00593 glColor4f (1,1,0,1);
00594 glPointSize(5);
00595 gravityCenter /= totalMass;
00596 for(unsigned int i=0 ; i<Coord::static_size ; i++){
00597 Coord v;
00598 v[i] = showAxisSize.getValue();
00599 helper::gl::glVertexT(gravityCenter-v);
00600 helper::gl::glVertexT(gravityCenter+v);
00601 }
00602 glEnd();
00603 }
00604 }
00605
00606 template <class DataTypes, class MassType>
00607 bool DiagonalMass<DataTypes, MassType>::addBBox(double* minBBox, double* maxBBox)
00608 {
00609 const VecCoord& x = *this->mstate->getX();
00610 for (unsigned int i=0; i<x.size(); i++)
00611 {
00612
00613 Real p[3] = {0.0, 0.0, 0.0};
00614 DataTypes::get(p[0],p[1],p[2],x[i]);
00615 for (int c=0;c<3;c++)
00616 {
00617 if (p[c] > maxBBox[c]) maxBBox[c] = p[c];
00618 if (p[c] < minBBox[c]) minBBox[c] = p[c];
00619 }
00620 }
00621 return true;
00622 }
00623
00624 template <class DataTypes, class MassType>
00625 class DiagonalMass<DataTypes, MassType>::Loader : public helper::io::MassSpringLoader
00626 {
00627 public:
00628 DiagonalMass<DataTypes, MassType>* dest;
00629 Loader(DiagonalMass<DataTypes, MassType>* dest) : dest(dest) {}
00630 virtual void addMass(SReal , SReal , SReal , SReal , SReal , SReal , SReal mass, SReal , bool , bool )
00631 {
00632 dest->addMass(MassType((Real)mass));
00633 }
00634 };
00635
00636 template <class DataTypes, class MassType>
00637 bool DiagonalMass<DataTypes, MassType>::load(const char *filename)
00638 {
00639 clear();
00640 if (filename!=NULL && filename[0]!='\0')
00641 {
00642 Loader loader(this);
00643 return loader.load(filename);
00644 }
00645 else return false;
00646 }
00647
00648
00649 #ifndef SOFA_FLOAT
00650
00651
00652
00653
00654
00655
00656
00657
00658 template <>
00659 double DiagonalMass<Rigid3dTypes, Rigid3dMass>::getPotentialEnergy( const VecCoord& x );
00660 template <>
00661 double DiagonalMass<Rigid2dTypes, Rigid2dMass>::getPotentialEnergy( const VecCoord& x );
00662 template <>
00663 void DiagonalMass<Rigid3dTypes, Rigid3dMass>::draw();
00664 template <>
00665 void DiagonalMass<Rigid2dTypes, Rigid2dMass>::draw();
00666 #endif
00667 #ifndef SOFA_DOUBLE
00668 template <>
00669 double DiagonalMass<Rigid3fTypes, Rigid3fMass>::getPotentialEnergy( const VecCoord& x );
00670 template <>
00671 double DiagonalMass<Rigid2fTypes, Rigid2fMass>::getPotentialEnergy( const VecCoord& x );
00672
00673 template <>
00674 void DiagonalMass<Rigid3fTypes, Rigid3fMass>::draw();
00675 template <>
00676 void DiagonalMass<Rigid2fTypes, Rigid2fMass>::draw();
00677 #endif
00678
00679
00680 template<class DataTypes, class MassType>
00681 void DiagonalMass<DataTypes, MassType>::parse(core::objectmodel::BaseObjectDescription* arg)
00682 {
00683
00684 if (arg->getAttribute("filename"))
00685 {
00686 this->load(arg->getAttribute("filename"));
00687 arg->removeAttribute("filename");
00688 }
00689 this->Inherited::parse(arg);
00690 }
00691
00692
00693
00694 }
00695
00696 }
00697
00698 }
00699
00700 #endif