Blender V2.61 - r43446
|
00001 /* 00002 Bullet Continuous Collision Detection and Physics Library 00003 Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ 00004 00005 This software is provided 'as-is', without any express or implied warranty. 00006 In no event will the authors be held liable for any damages arising from the use of this software. 00007 Permission is granted to anyone to use this software for any purpose, 00008 including commercial applications, and to alter it and redistribute it freely, 00009 subject to the following restrictions: 00010 00011 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 00012 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 00013 3. This notice may not be removed or altered from any source distribution. 00014 */ 00015 00019 //define BT_DISABLE_CAPSULE_CAPSULE_COLLIDER 1 00020 00021 #include "btConvexConvexAlgorithm.h" 00022 00023 //#include <stdio.h> 00024 #include "BulletCollision/NarrowPhaseCollision/btDiscreteCollisionDetectorInterface.h" 00025 #include "BulletCollision/BroadphaseCollision/btBroadphaseInterface.h" 00026 #include "BulletCollision/CollisionDispatch/btCollisionObject.h" 00027 #include "BulletCollision/CollisionShapes/btConvexShape.h" 00028 #include "BulletCollision/CollisionShapes/btCapsuleShape.h" 00029 00030 00031 #include "BulletCollision/NarrowPhaseCollision/btGjkPairDetector.h" 00032 #include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" 00033 #include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h" 00034 #include "BulletCollision/CollisionShapes/btBoxShape.h" 00035 #include "BulletCollision/CollisionDispatch/btManifoldResult.h" 00036 00037 #include "BulletCollision/NarrowPhaseCollision/btConvexPenetrationDepthSolver.h" 00038 #include "BulletCollision/NarrowPhaseCollision/btContinuousConvexCollision.h" 00039 #include "BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.h" 00040 #include "BulletCollision/NarrowPhaseCollision/btGjkConvexCast.h" 00041 00042 00043 00044 #include "BulletCollision/NarrowPhaseCollision/btVoronoiSimplexSolver.h" 00045 #include "BulletCollision/CollisionShapes/btSphereShape.h" 00046 00047 #include "BulletCollision/NarrowPhaseCollision/btMinkowskiPenetrationDepthSolver.h" 00048 00049 #include "BulletCollision/NarrowPhaseCollision/btGjkEpa2.h" 00050 #include "BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.h" 00051 00052 00053 00055 00056 00057 00058 static SIMD_FORCE_INLINE void segmentsClosestPoints( 00059 btVector3& ptsVector, 00060 btVector3& offsetA, 00061 btVector3& offsetB, 00062 btScalar& tA, btScalar& tB, 00063 const btVector3& translation, 00064 const btVector3& dirA, btScalar hlenA, 00065 const btVector3& dirB, btScalar hlenB ) 00066 { 00067 // compute the parameters of the closest points on each line segment 00068 00069 btScalar dirA_dot_dirB = btDot(dirA,dirB); 00070 btScalar dirA_dot_trans = btDot(dirA,translation); 00071 btScalar dirB_dot_trans = btDot(dirB,translation); 00072 00073 btScalar denom = 1.0f - dirA_dot_dirB * dirA_dot_dirB; 00074 00075 if ( denom == 0.0f ) { 00076 tA = 0.0f; 00077 } else { 00078 tA = ( dirA_dot_trans - dirB_dot_trans * dirA_dot_dirB ) / denom; 00079 if ( tA < -hlenA ) 00080 tA = -hlenA; 00081 else if ( tA > hlenA ) 00082 tA = hlenA; 00083 } 00084 00085 tB = tA * dirA_dot_dirB - dirB_dot_trans; 00086 00087 if ( tB < -hlenB ) { 00088 tB = -hlenB; 00089 tA = tB * dirA_dot_dirB + dirA_dot_trans; 00090 00091 if ( tA < -hlenA ) 00092 tA = -hlenA; 00093 else if ( tA > hlenA ) 00094 tA = hlenA; 00095 } else if ( tB > hlenB ) { 00096 tB = hlenB; 00097 tA = tB * dirA_dot_dirB + dirA_dot_trans; 00098 00099 if ( tA < -hlenA ) 00100 tA = -hlenA; 00101 else if ( tA > hlenA ) 00102 tA = hlenA; 00103 } 00104 00105 // compute the closest points relative to segment centers. 00106 00107 offsetA = dirA * tA; 00108 offsetB = dirB * tB; 00109 00110 ptsVector = translation - offsetA + offsetB; 00111 } 00112 00113 00114 static SIMD_FORCE_INLINE btScalar capsuleCapsuleDistance( 00115 btVector3& normalOnB, 00116 btVector3& pointOnB, 00117 btScalar capsuleLengthA, 00118 btScalar capsuleRadiusA, 00119 btScalar capsuleLengthB, 00120 btScalar capsuleRadiusB, 00121 int capsuleAxisA, 00122 int capsuleAxisB, 00123 const btTransform& transformA, 00124 const btTransform& transformB, 00125 btScalar distanceThreshold ) 00126 { 00127 btVector3 directionA = transformA.getBasis().getColumn(capsuleAxisA); 00128 btVector3 translationA = transformA.getOrigin(); 00129 btVector3 directionB = transformB.getBasis().getColumn(capsuleAxisB); 00130 btVector3 translationB = transformB.getOrigin(); 00131 00132 // translation between centers 00133 00134 btVector3 translation = translationB - translationA; 00135 00136 // compute the closest points of the capsule line segments 00137 00138 btVector3 ptsVector; // the vector between the closest points 00139 00140 btVector3 offsetA, offsetB; // offsets from segment centers to their closest points 00141 btScalar tA, tB; // parameters on line segment 00142 00143 segmentsClosestPoints( ptsVector, offsetA, offsetB, tA, tB, translation, 00144 directionA, capsuleLengthA, directionB, capsuleLengthB ); 00145 00146 btScalar distance = ptsVector.length() - capsuleRadiusA - capsuleRadiusB; 00147 00148 if ( distance > distanceThreshold ) 00149 return distance; 00150 00151 btScalar lenSqr = ptsVector.length2(); 00152 if (lenSqr<= (SIMD_EPSILON*SIMD_EPSILON)) 00153 { 00154 //degenerate case where 2 capsules are likely at the same location: take a vector tangential to 'directionA' 00155 btVector3 q; 00156 btPlaneSpace1(directionA,normalOnB,q); 00157 } else 00158 { 00159 // compute the contact normal 00160 normalOnB = ptsVector*-btRecipSqrt(lenSqr); 00161 } 00162 pointOnB = transformB.getOrigin()+offsetB + normalOnB * capsuleRadiusB; 00163 00164 return distance; 00165 } 00166 00167 00168 00169 00170 00171 00172 00174 00175 00176 00177 00178 00179 btConvexConvexAlgorithm::CreateFunc::CreateFunc(btSimplexSolverInterface* simplexSolver, btConvexPenetrationDepthSolver* pdSolver) 00180 { 00181 m_numPerturbationIterations = 0; 00182 m_minimumPointsPerturbationThreshold = 3; 00183 m_simplexSolver = simplexSolver; 00184 m_pdSolver = pdSolver; 00185 } 00186 00187 btConvexConvexAlgorithm::CreateFunc::~CreateFunc() 00188 { 00189 } 00190 00191 btConvexConvexAlgorithm::btConvexConvexAlgorithm(btPersistentManifold* mf,const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* body0,btCollisionObject* body1,btSimplexSolverInterface* simplexSolver, btConvexPenetrationDepthSolver* pdSolver,int numPerturbationIterations, int minimumPointsPerturbationThreshold) 00192 : btActivatingCollisionAlgorithm(ci,body0,body1), 00193 m_simplexSolver(simplexSolver), 00194 m_pdSolver(pdSolver), 00195 m_ownManifold (false), 00196 m_manifoldPtr(mf), 00197 m_lowLevelOfDetail(false), 00198 #ifdef USE_SEPDISTANCE_UTIL2 00199 m_sepDistance((static_cast<btConvexShape*>(body0->getCollisionShape()))->getAngularMotionDisc(), 00200 (static_cast<btConvexShape*>(body1->getCollisionShape()))->getAngularMotionDisc()), 00201 #endif 00202 m_numPerturbationIterations(numPerturbationIterations), 00203 m_minimumPointsPerturbationThreshold(minimumPointsPerturbationThreshold) 00204 { 00205 (void)body0; 00206 (void)body1; 00207 } 00208 00209 00210 00211 00212 btConvexConvexAlgorithm::~btConvexConvexAlgorithm() 00213 { 00214 if (m_ownManifold) 00215 { 00216 if (m_manifoldPtr) 00217 m_dispatcher->releaseManifold(m_manifoldPtr); 00218 } 00219 } 00220 00221 void btConvexConvexAlgorithm ::setLowLevelOfDetail(bool useLowLevel) 00222 { 00223 m_lowLevelOfDetail = useLowLevel; 00224 } 00225 00226 00227 struct btPerturbedContactResult : public btManifoldResult 00228 { 00229 btManifoldResult* m_originalManifoldResult; 00230 btTransform m_transformA; 00231 btTransform m_transformB; 00232 btTransform m_unPerturbedTransform; 00233 bool m_perturbA; 00234 btIDebugDraw* m_debugDrawer; 00235 00236 00237 btPerturbedContactResult(btManifoldResult* originalResult,const btTransform& transformA,const btTransform& transformB,const btTransform& unPerturbedTransform,bool perturbA,btIDebugDraw* debugDrawer) 00238 :m_originalManifoldResult(originalResult), 00239 m_transformA(transformA), 00240 m_transformB(transformB), 00241 m_unPerturbedTransform(unPerturbedTransform), 00242 m_perturbA(perturbA), 00243 m_debugDrawer(debugDrawer) 00244 { 00245 } 00246 virtual ~ btPerturbedContactResult() 00247 { 00248 } 00249 00250 virtual void addContactPoint(const btVector3& normalOnBInWorld,const btVector3& pointInWorld,btScalar orgDepth) 00251 { 00252 btVector3 endPt,startPt; 00253 btScalar newDepth; 00254 btVector3 newNormal; 00255 00256 if (m_perturbA) 00257 { 00258 btVector3 endPtOrg = pointInWorld + normalOnBInWorld*orgDepth; 00259 endPt = (m_unPerturbedTransform*m_transformA.inverse())(endPtOrg); 00260 newDepth = (endPt - pointInWorld).dot(normalOnBInWorld); 00261 startPt = endPt+normalOnBInWorld*newDepth; 00262 } else 00263 { 00264 endPt = pointInWorld + normalOnBInWorld*orgDepth; 00265 startPt = (m_unPerturbedTransform*m_transformB.inverse())(pointInWorld); 00266 newDepth = (endPt - startPt).dot(normalOnBInWorld); 00267 00268 } 00269 00270 //#define DEBUG_CONTACTS 1 00271 #ifdef DEBUG_CONTACTS 00272 m_debugDrawer->drawLine(startPt,endPt,btVector3(1,0,0)); 00273 m_debugDrawer->drawSphere(startPt,0.05,btVector3(0,1,0)); 00274 m_debugDrawer->drawSphere(endPt,0.05,btVector3(0,0,1)); 00275 #endif //DEBUG_CONTACTS 00276 00277 00278 m_originalManifoldResult->addContactPoint(normalOnBInWorld,startPt,newDepth); 00279 } 00280 00281 }; 00282 00283 extern btScalar gContactBreakingThreshold; 00284 00285 00286 // 00287 // Convex-Convex collision algorithm 00288 // 00289 void btConvexConvexAlgorithm ::processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) 00290 { 00291 00292 if (!m_manifoldPtr) 00293 { 00294 //swapped? 00295 m_manifoldPtr = m_dispatcher->getNewManifold(body0,body1); 00296 m_ownManifold = true; 00297 } 00298 resultOut->setPersistentManifold(m_manifoldPtr); 00299 00300 //comment-out next line to test multi-contact generation 00301 //resultOut->getPersistentManifold()->clearManifold(); 00302 00303 00304 btConvexShape* min0 = static_cast<btConvexShape*>(body0->getCollisionShape()); 00305 btConvexShape* min1 = static_cast<btConvexShape*>(body1->getCollisionShape()); 00306 00307 btVector3 normalOnB; 00308 btVector3 pointOnBWorld; 00309 #ifndef BT_DISABLE_CAPSULE_CAPSULE_COLLIDER 00310 if ((min0->getShapeType() == CAPSULE_SHAPE_PROXYTYPE) && (min1->getShapeType() == CAPSULE_SHAPE_PROXYTYPE)) 00311 { 00312 btCapsuleShape* capsuleA = (btCapsuleShape*) min0; 00313 btCapsuleShape* capsuleB = (btCapsuleShape*) min1; 00314 btVector3 localScalingA = capsuleA->getLocalScaling(); 00315 btVector3 localScalingB = capsuleB->getLocalScaling(); 00316 00317 btScalar threshold = m_manifoldPtr->getContactBreakingThreshold(); 00318 00319 btScalar dist = capsuleCapsuleDistance(normalOnB, pointOnBWorld,capsuleA->getHalfHeight(),capsuleA->getRadius(), 00320 capsuleB->getHalfHeight(),capsuleB->getRadius(),capsuleA->getUpAxis(),capsuleB->getUpAxis(), 00321 body0->getWorldTransform(),body1->getWorldTransform(),threshold); 00322 00323 if (dist<threshold) 00324 { 00325 btAssert(normalOnB.length2()>=(SIMD_EPSILON*SIMD_EPSILON)); 00326 resultOut->addContactPoint(normalOnB,pointOnBWorld,dist); 00327 } 00328 resultOut->refreshContactPoints(); 00329 return; 00330 } 00331 #endif //BT_DISABLE_CAPSULE_CAPSULE_COLLIDER 00332 00333 00334 #ifdef USE_SEPDISTANCE_UTIL2 00335 if (dispatchInfo.m_useConvexConservativeDistanceUtil) 00336 { 00337 m_sepDistance.updateSeparatingDistance(body0->getWorldTransform(),body1->getWorldTransform()); 00338 } 00339 00340 if (!dispatchInfo.m_useConvexConservativeDistanceUtil || m_sepDistance.getConservativeSeparatingDistance()<=0.f) 00341 #endif //USE_SEPDISTANCE_UTIL2 00342 00343 { 00344 00345 00346 btGjkPairDetector::ClosestPointInput input; 00347 00348 btGjkPairDetector gjkPairDetector(min0,min1,m_simplexSolver,m_pdSolver); 00349 //TODO: if (dispatchInfo.m_useContinuous) 00350 gjkPairDetector.setMinkowskiA(min0); 00351 gjkPairDetector.setMinkowskiB(min1); 00352 00353 #ifdef USE_SEPDISTANCE_UTIL2 00354 if (dispatchInfo.m_useConvexConservativeDistanceUtil) 00355 { 00356 input.m_maximumDistanceSquared = BT_LARGE_FLOAT; 00357 } else 00358 #endif //USE_SEPDISTANCE_UTIL2 00359 { 00360 if (dispatchInfo.m_convexMaxDistanceUseCPT) 00361 { 00362 input.m_maximumDistanceSquared = min0->getMargin() + min1->getMargin() + m_manifoldPtr->getContactProcessingThreshold(); 00363 } else 00364 { 00365 input.m_maximumDistanceSquared = min0->getMargin() + min1->getMargin() + m_manifoldPtr->getContactBreakingThreshold(); 00366 } 00367 input.m_maximumDistanceSquared*= input.m_maximumDistanceSquared; 00368 } 00369 00370 input.m_stackAlloc = dispatchInfo.m_stackAllocator; 00371 input.m_transformA = body0->getWorldTransform(); 00372 input.m_transformB = body1->getWorldTransform(); 00373 00374 gjkPairDetector.getClosestPoints(input,*resultOut,dispatchInfo.m_debugDraw); 00375 00376 00377 00378 #ifdef USE_SEPDISTANCE_UTIL2 00379 btScalar sepDist = 0.f; 00380 if (dispatchInfo.m_useConvexConservativeDistanceUtil) 00381 { 00382 sepDist = gjkPairDetector.getCachedSeparatingDistance(); 00383 if (sepDist>SIMD_EPSILON) 00384 { 00385 sepDist += dispatchInfo.m_convexConservativeDistanceThreshold; 00386 //now perturbe directions to get multiple contact points 00387 00388 } 00389 } 00390 #endif //USE_SEPDISTANCE_UTIL2 00391 00392 //now perform 'm_numPerturbationIterations' collision queries with the perturbated collision objects 00393 00394 //perform perturbation when more then 'm_minimumPointsPerturbationThreshold' points 00395 if (m_numPerturbationIterations && resultOut->getPersistentManifold()->getNumContacts() < m_minimumPointsPerturbationThreshold) 00396 { 00397 00398 int i; 00399 btVector3 v0,v1; 00400 btVector3 sepNormalWorldSpace; 00401 00402 sepNormalWorldSpace = gjkPairDetector.getCachedSeparatingAxis().normalized(); 00403 btPlaneSpace1(sepNormalWorldSpace,v0,v1); 00404 00405 00406 bool perturbeA = true; 00407 const btScalar angleLimit = 0.125f * SIMD_PI; 00408 btScalar perturbeAngle; 00409 btScalar radiusA = min0->getAngularMotionDisc(); 00410 btScalar radiusB = min1->getAngularMotionDisc(); 00411 if (radiusA < radiusB) 00412 { 00413 perturbeAngle = gContactBreakingThreshold /radiusA; 00414 perturbeA = true; 00415 } else 00416 { 00417 perturbeAngle = gContactBreakingThreshold / radiusB; 00418 perturbeA = false; 00419 } 00420 if ( perturbeAngle > angleLimit ) 00421 perturbeAngle = angleLimit; 00422 00423 btTransform unPerturbedTransform; 00424 if (perturbeA) 00425 { 00426 unPerturbedTransform = input.m_transformA; 00427 } else 00428 { 00429 unPerturbedTransform = input.m_transformB; 00430 } 00431 00432 for ( i=0;i<m_numPerturbationIterations;i++) 00433 { 00434 if (v0.length2()>SIMD_EPSILON) 00435 { 00436 btQuaternion perturbeRot(v0,perturbeAngle); 00437 btScalar iterationAngle = i*(SIMD_2_PI/btScalar(m_numPerturbationIterations)); 00438 btQuaternion rotq(sepNormalWorldSpace,iterationAngle); 00439 00440 00441 if (perturbeA) 00442 { 00443 input.m_transformA.setBasis( btMatrix3x3(rotq.inverse()*perturbeRot*rotq)*body0->getWorldTransform().getBasis()); 00444 input.m_transformB = body1->getWorldTransform(); 00445 #ifdef DEBUG_CONTACTS 00446 dispatchInfo.m_debugDraw->drawTransform(input.m_transformA,10.0); 00447 #endif //DEBUG_CONTACTS 00448 } else 00449 { 00450 input.m_transformA = body0->getWorldTransform(); 00451 input.m_transformB.setBasis( btMatrix3x3(rotq.inverse()*perturbeRot*rotq)*body1->getWorldTransform().getBasis()); 00452 #ifdef DEBUG_CONTACTS 00453 dispatchInfo.m_debugDraw->drawTransform(input.m_transformB,10.0); 00454 #endif 00455 } 00456 00457 btPerturbedContactResult perturbedResultOut(resultOut,input.m_transformA,input.m_transformB,unPerturbedTransform,perturbeA,dispatchInfo.m_debugDraw); 00458 gjkPairDetector.getClosestPoints(input,perturbedResultOut,dispatchInfo.m_debugDraw); 00459 } 00460 00461 } 00462 } 00463 00464 00465 00466 #ifdef USE_SEPDISTANCE_UTIL2 00467 if (dispatchInfo.m_useConvexConservativeDistanceUtil && (sepDist>SIMD_EPSILON)) 00468 { 00469 m_sepDistance.initSeparatingDistance(gjkPairDetector.getCachedSeparatingAxis(),sepDist,body0->getWorldTransform(),body1->getWorldTransform()); 00470 } 00471 #endif //USE_SEPDISTANCE_UTIL2 00472 00473 00474 } 00475 00476 if (m_ownManifold) 00477 { 00478 resultOut->refreshContactPoints(); 00479 } 00480 00481 } 00482 00483 00484 00485 bool disableCcd = false; 00486 btScalar btConvexConvexAlgorithm::calculateTimeOfImpact(btCollisionObject* col0,btCollisionObject* col1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) 00487 { 00488 (void)resultOut; 00489 (void)dispatchInfo; 00491 00494 btScalar resultFraction = btScalar(1.); 00495 00496 00497 btScalar squareMot0 = (col0->getInterpolationWorldTransform().getOrigin() - col0->getWorldTransform().getOrigin()).length2(); 00498 btScalar squareMot1 = (col1->getInterpolationWorldTransform().getOrigin() - col1->getWorldTransform().getOrigin()).length2(); 00499 00500 if (squareMot0 < col0->getCcdSquareMotionThreshold() && 00501 squareMot1 < col1->getCcdSquareMotionThreshold()) 00502 return resultFraction; 00503 00504 if (disableCcd) 00505 return btScalar(1.); 00506 00507 00508 //An adhoc way of testing the Continuous Collision Detection algorithms 00509 //One object is approximated as a sphere, to simplify things 00510 //Starting in penetration should report no time of impact 00511 //For proper CCD, better accuracy and handling of 'allowed' penetration should be added 00512 //also the mainloop of the physics should have a kind of toi queue (something like Brian Mirtich's application of Timewarp for Rigidbodies) 00513 00514 00516 { 00517 btConvexShape* convex0 = static_cast<btConvexShape*>(col0->getCollisionShape()); 00518 00519 btSphereShape sphere1(col1->getCcdSweptSphereRadius()); //todo: allow non-zero sphere sizes, for better approximation 00520 btConvexCast::CastResult result; 00521 btVoronoiSimplexSolver voronoiSimplex; 00522 //SubsimplexConvexCast ccd0(&sphere,min0,&voronoiSimplex); 00524 btGjkConvexCast ccd1( convex0 ,&sphere1,&voronoiSimplex); 00525 //ContinuousConvexCollision ccd(min0,min1,&voronoiSimplex,0); 00526 if (ccd1.calcTimeOfImpact(col0->getWorldTransform(),col0->getInterpolationWorldTransform(), 00527 col1->getWorldTransform(),col1->getInterpolationWorldTransform(),result)) 00528 { 00529 00530 //store result.m_fraction in both bodies 00531 00532 if (col0->getHitFraction()> result.m_fraction) 00533 col0->setHitFraction( result.m_fraction ); 00534 00535 if (col1->getHitFraction() > result.m_fraction) 00536 col1->setHitFraction( result.m_fraction); 00537 00538 if (resultFraction > result.m_fraction) 00539 resultFraction = result.m_fraction; 00540 00541 } 00542 00543 00544 00545 00546 } 00547 00549 { 00550 btConvexShape* convex1 = static_cast<btConvexShape*>(col1->getCollisionShape()); 00551 00552 btSphereShape sphere0(col0->getCcdSweptSphereRadius()); //todo: allow non-zero sphere sizes, for better approximation 00553 btConvexCast::CastResult result; 00554 btVoronoiSimplexSolver voronoiSimplex; 00555 //SubsimplexConvexCast ccd0(&sphere,min0,&voronoiSimplex); 00557 btGjkConvexCast ccd1(&sphere0,convex1,&voronoiSimplex); 00558 //ContinuousConvexCollision ccd(min0,min1,&voronoiSimplex,0); 00559 if (ccd1.calcTimeOfImpact(col0->getWorldTransform(),col0->getInterpolationWorldTransform(), 00560 col1->getWorldTransform(),col1->getInterpolationWorldTransform(),result)) 00561 { 00562 00563 //store result.m_fraction in both bodies 00564 00565 if (col0->getHitFraction() > result.m_fraction) 00566 col0->setHitFraction( result.m_fraction); 00567 00568 if (col1->getHitFraction() > result.m_fraction) 00569 col1->setHitFraction( result.m_fraction); 00570 00571 if (resultFraction > result.m_fraction) 00572 resultFraction = result.m_fraction; 00573 00574 } 00575 } 00576 00577 return resultFraction; 00578 00579 } 00580