• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdeedu
  • Sitemap
  • Contact Us
 

step/stepcore

collisionsolver.cc

Go to the documentation of this file.
00001 /* This file is part of StepCore library.
00002    Copyright (C) 2007 Vladimir Kuznetsov <ks.vladimir@gmail.com>
00003 
00004    StepCore library is free software; you can redistribute it and/or modify
00005    it under the terms of the GNU General Public License as published by
00006    the Free Software Foundation; either version 2 of the License, or
00007    (at your option) any later version.
00008 
00009    StepCore library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012    GNU General Public License for more details.
00013 
00014    You should have received a copy of the GNU General Public License
00015    along with StepCore; if not, write to the Free Software
00016    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00017 */
00018 
00019 #include "collisionsolver.h"
00020 #include "rigidbody.h"
00021 #include "particle.h"
00022 
00023 #include <algorithm>
00024 
00025 namespace StepCore {
00026 
00027 // XXX: units for toleranceAbs and localError
00028 STEPCORE_META_OBJECT(CollisionSolver, "CollisionSolver", MetaObject::ABSTRACT, STEPCORE_SUPER_CLASS(Object),
00029     STEPCORE_PROPERTY_RW(double, toleranceAbs, STEPCORE_UNITS_1, "Allowed absolute tolerance", toleranceAbs, setToleranceAbs)
00030     STEPCORE_PROPERTY_R_D(double, localError, STEPCORE_UNITS_1, "Maximal local error during last step", localError))
00031 
00032 STEPCORE_META_OBJECT(GJKCollisionSolver, "GJKCollisionSolver", 0,
00033                         STEPCORE_SUPER_CLASS(CollisionSolver),)
00034 
00035 int GJKCollisionSolver::checkPolygonPolygon(Contact* contact)
00036 {
00037     BasePolygon* polygon0 = static_cast<BasePolygon*>(contact->body0);
00038     BasePolygon* polygon1 = static_cast<BasePolygon*>(contact->body1);
00039 
00040     if(polygon0->vertexes().empty() || polygon1->vertexes().empty()) {
00041         return contact->state = Contact::Unknown;
00042     }
00043 
00044     // Algorithm description can be found in 
00045     // "A Fast and Robust GJK Implementation for
00046     //    Collision Detection of Convex Objects"
00047     //    by Gino van den Bergen
00048 
00049     Vector2dList vertexes[2];
00050     vertexes[0].reserve(polygon0->vertexes().size());
00051     vertexes[1].reserve(polygon1->vertexes().size());
00052 
00053     const Vector2dList::const_iterator p0_it_end = polygon0->vertexes().end();
00054     for(Vector2dList::const_iterator it0 = polygon0->vertexes().begin();
00055                                         it0 != p0_it_end; ++it0) {
00056         vertexes[0].push_back(polygon0->pointLocalToWorld(*it0));
00057     }
00058 
00059     const Vector2dList::const_iterator p1_it_end = polygon1->vertexes().end();
00060     for(Vector2dList::const_iterator it1 = polygon1->vertexes().begin();
00061                                         it1 != p1_it_end; ++it1) {
00062         vertexes[1].push_back(polygon1->pointLocalToWorld(*it1));
00063     }
00064 
00065     int wsize;
00066     Vector2d w[3];  // Vertexes of current simplex
00067     Vector2d v;     // Closest point of current simplex
00068     Vector2d s;     // New support vertex in direction v
00069 
00070     Vector2d vv[2]; // Closest points on the first and second objects
00071     int wi[2][3];   // Indexes of vertexes corresponding to w
00072     int si[2];      // Indexes of vertexes corresponding to s
00073 
00074     wsize = 1;
00075     // Start with arbitrary vertex (TODO: cache the whole w simplex)
00076     if(contact->state >= Contact::Separating && contact->state < Contact::Intersected) {
00077         wi[0][0] = contact->_w1[0];
00078         wi[1][0] = contact->_w1[1];
00079     } else {
00080         wi[0][0] = 0;
00081         wi[1][0] = 0;
00082     }
00083     vv[0] = vertexes[0][wi[0][0]];
00084     vv[1] = vertexes[1][wi[1][0]];
00085     w[0] = v = vv[1] - vv[0];
00086 
00087     bool intersects = false;
00088     unsigned int iteration = 0;
00089     for(;; ++iteration) {
00090         //STEPCORE_ASSERT_NOABORT( iteration < vertexes[0].size()*vertexes[1].size() );
00091 
00092         double smin = v.norm2();
00093 
00094         // Check for penetration (part 1)
00095         // If we are closer to the origin then given tolerance
00096         // we should stop just now to avoid computational errors later
00097         if(smin < _toleranceAbs*_toleranceAbs*1e-4) { // XXX: separate tolerance for penetration ?
00098             intersects = true;
00099             break;
00100         }
00101 
00102         // Find support vertex in direction v
00103         // TODO: coherence optimization
00104         bool sfound = false;
00105         unsigned int vertex0_size = vertexes[0].size();
00106         unsigned int vertex1_size = vertexes[1].size();
00107 
00108         for(unsigned int i0=0; i0<vertex0_size; ++i0) {
00109             for(unsigned int i1=0; i1<vertex1_size; ++i1) {
00110                 Vector2d sn = vertexes[1][i1] - vertexes[0][i0];
00111                 double scurr = v.innerProduct(sn);
00112                 if(smin - scurr > _toleranceAbs*_toleranceAbs*1e-4) { // XXX: separate tolerance ?
00113                     smin = scurr;
00114                     s = sn;
00115                     si[0] = i0;
00116                     si[1] = i1;
00117                     sfound = true;
00118                 }
00119             }
00120         }
00121 
00122         // If no support vertex have been found than we are at minimum
00123         if(!sfound) {
00124             if(wsize == 0) { // we have guessed right point
00125                 w[0] = v;
00126                 wi[0][0] = 0;
00127                 wi[1][0] = 0;
00128                 wsize = 1;
00129             }
00130             break;
00131         }
00132 
00133         // Check for penetration (part 2)
00134         if(wsize == 2) {
00135             // objects are penetrating if origin lies inside the simplex
00136             // XXX: are there faster method to test it ?
00137             Vector2d w02 = w[0] - s;
00138             Vector2d w12 = w[1] - s;
00139             double det  =  w02[0]*w12[1] - w02[1]*w12[0];
00140             double det0 =   -s[0]*w12[1] +   s[1]*w12[0];
00141             double det1 = -w02[0]*  s[1] + w02[1]*  s[0];
00142             if(det0/det > 0 && det1/det > 0) { // XXX: tolerance
00143                 w[wsize] = s;
00144                 wi[0][wsize] = si[0];
00145                 wi[1][wsize] = si[1];
00146                 ++wsize;
00147                 v.setZero();
00148                 intersects = true;
00149                 break;
00150             }
00151         }
00152 
00153         // Find v = dist(conv(w+s))
00154         double lambda = 0;
00155         int ii = -1;
00156         for(int i=0; i<wsize; ++i) {
00157             double lambda0 = - s.innerProduct(w[i]-s) / (w[i]-s).norm2();
00158             if(lambda0 > 0) {
00159                 Vector2d vn = s*(1-lambda0) + w[i]*lambda0;
00160                 if(vn.norm2() < v.norm2()) {
00161                     v = vn; ii = i;
00162                     lambda = lambda0;
00163                 }
00164             }
00165         }
00166 
00167         if(ii >= 0) { // Closest simplex is line
00168             vv[0] = vertexes[0][si[0]]*(1-lambda) + vertexes[0][wi[0][ii]]*lambda;
00169             vv[1] = vertexes[1][si[1]]*(1-lambda) + vertexes[1][wi[1][ii]]*lambda;
00170             if(wsize == 2) {
00171                 w[1-ii] = s;
00172                 wi[0][1-ii] = si[0];
00173                 wi[1][1-ii] = si[1];
00174             } else {
00175                 w[wsize] = s;
00176                 wi[0][wsize] = si[0];
00177                 wi[1][wsize] = si[1];
00178                 ++wsize;
00179             }
00180         } else { // Closest simplex is vertex
00181             STEPCORE_ASSERT_NOABORT(iteration == 0 || s.norm2() < v.norm2());
00182 
00183             v = w[0] = s;
00184             vv[0] = vertexes[0][si[0]];
00185             vv[1] = vertexes[1][si[1]];
00186             wi[0][0] = si[0];
00187             wi[1][0] = si[1];
00188             wsize = 1;
00189         }
00190     }
00191 
00192     if(intersects) {
00193         /*
00194         qDebug("penetration detected");
00195         qDebug("iteration = %d", iteration);
00196         qDebug("simplexes:");
00197         qDebug("    1:   2:");
00198         for(int i=0; i<wsize; ++i) {
00199             qDebug("    %d    %d", wi[0][i], wi[1][i]);
00200         }
00201         */
00202         contact->distance = 0;
00203         contact->normal.setZero();
00204         contact->pointsCount = 0;
00205         return contact->state = Contact::Intersected;
00206     }
00207 
00208     /*
00209     qDebug("distance = %f", v.norm());
00210     Vector2d v1 = v / v.norm();
00211     qDebug("normal = (%f,%f)", v1[0], v1[1]);
00212     qDebug("iteration = %d", iteration);
00213     qDebug("simplexes:");
00214     qDebug("    1:   2:");
00215     for(int i=0; i<wsize; ++i) {
00216         qDebug("    %d    %d", wi[0][i], wi[1][i]);
00217     }
00218     qDebug("contact points:");
00219     qDebug("    (%f,%f)    (%f,%f)", vv[0][0], vv[0][1], vv[1][0], vv[1][1]);
00220     */
00221 
00222     double vnorm = v.norm();
00223     contact->distance = vnorm;
00224     contact->normal = v/vnorm;
00225     contact->pointsCount = 0;
00226     contact->state = Contact::Separated;
00227 
00228     contact->_w1[0] = wi[0][0];
00229     contact->_w1[1] = wi[1][0];
00230 
00231     if(vnorm > _toleranceAbs) return contact->state;
00232 
00233     // If the objects are close enough we need to find contact manifold
00234     // We are going to find simplexes (lines) that are 'most parallel'
00235     // to contact plane and look for contact manifold among them. It
00236     // works for almost all cases when adjacent polygon edges are
00237     // not parallel
00238     Vector2d vunit = v / vnorm;
00239     Vector2d wm[2][2];
00240 
00241     for(int i=0; i<2; ++i) {
00242         wm[i][0] = vertexes[i][ wi[i][0] ];
00243 
00244         if(wsize < 2 || wi[i][0] == wi[i][1]) { // vertex contact
00245             // Check two adjacent edges
00246             int ai1 = wi[i][0] - 1; if(ai1 < 0) ai1 = vertexes[i].size()-1;
00247             Vector2d av1 = vertexes[i][ai1];
00248             Vector2d dv1 = wm[i][0] - av1;
00249             double dist1 = vunit.innerProduct( dv1 ) * (i==0 ? 1 : -1);
00250             double angle1 = dist1 / dv1.norm();
00251 
00252             int ai2 = wi[i][0] + 1; if(ai2 >= (int) vertexes[i].size()) ai2 = 0;
00253             Vector2d av2 = vertexes[i][ai2];
00254             Vector2d dv2 = wm[i][0] - av2;
00255             double dist2 = vunit.innerProduct( dv2 ) * (i==0 ? 1 : -1);
00256             double angle2 = dist2 / dv2.norm();
00257 
00258             if(angle1 <= angle2 && dist1 < (_toleranceAbs-vnorm)/2) {
00259                 wm[i][1] = av1;
00260             } else if(angle2 <= angle1 && dist2 < (_toleranceAbs-vnorm)/2) {
00261                 wm[i][1] = av2;
00262             } else {
00263                 wm[i][1] = wm[i][0]; contact->pointsCount = 1;
00264                 break;
00265             }
00266         } else { // edge contact
00267             wm[i][1] = vertexes[i][ wi[i][1] ];
00268         }
00269     }
00270 
00271     // Find intersection of two lines
00272     if(contact->pointsCount != 1) {
00273         Vector2d vunit_o(-vunit[1], vunit[0]);
00274         double wm_o[2][2];
00275 
00276         for(int i=0; i<2; ++i) {
00277             wm_o[i][0] = vunit_o.innerProduct(wm[i][0]);
00278             wm_o[i][1] = vunit_o.innerProduct(wm[i][1]);
00279 
00280             if(wm_o[i][0] > wm_o[i][1]) {
00281                 std::swap(wm_o[i][0], wm_o[i][1]);
00282                 std::swap(wm[i][0], wm[i][1]);
00283             }
00284         }
00285 
00286         if(wm_o[0][0] > wm_o[1][0]) contact->points[0] = wm[0][0];
00287         else contact->points[0] = wm[1][0];
00288 
00289         if(wm_o[0][1] < wm_o[1][1]) contact->points[1] = wm[0][1];
00290         else contact->points[1] = wm[1][1];
00291 
00292         // TODO: interpolate to midpoint
00293         if((contact->points[1] - contact->points[0]).norm() > _toleranceAbs) {
00294             /*
00295             for(int i=0; i<2; ++i) {
00296                 qDebug("contact%d: (%f,%f)", i, contact->points[i][0], contact->points[i][1]);
00297             }
00298             */
00299             contact->pointsCount = 2;
00300         }
00301         /*
00302             contact->vrel[0] = contact->normal.innerProduct(
00303                                 polygon1->velocityWorld(contact->points[0]) -
00304                                 polygon0->velocityWorld(contact->points[0]));
00305             contact->vrel[1] = contact->normal.innerProduct(
00306                                 polygon1->velocityWorld(contact->points[1]) -
00307                                 polygon0->velocityWorld(contact->points[1]));
00308             if(contact->vrel[0] < 0 || contact->vrel[1] < 0)
00309                 return contact->state = Contact::Colliding;
00310             else if(contact->vrel[0] < _toleranceAbs || contact->vrel[1] < _toleranceAbs) // XXX: tolerance
00311                 return contact->state = Colliding::Contacted;
00312             return contact->state = Contact::Separating;
00313         }
00314         */
00315     }
00316 
00317     if(contact->pointsCount != 2) {
00318         contact->pointsCount = 1;
00319         contact->points[0] = vv[0]; // TODO: interpolate vv[0] and vv[1]
00320         //qDebug("contact is one point: (%f %f) (%f %f)", vv[0][0], vv[0][1], vv[1][0], vv[1][1]);
00321     }
00322 
00323     int pCount = contact->pointsCount;
00324     for(int i=0; i<pCount; ++i) {
00325         contact->vrel[i] = contact->normal.innerProduct(
00326                         polygon1->velocityWorld(contact->points[i]) -
00327                         polygon0->velocityWorld(contact->points[i]));
00328 
00329         if(contact->vrel[i] < 0)
00330             contact->pointsState[i] = Contact::Colliding;
00331         else if(contact->vrel[i] < _toleranceAbs) // XXX: tolerance
00332             contact->pointsState[i] = Contact::Contacted;
00333         else contact->pointsState[i] = Contact::Separating;
00334 
00335         if(contact->pointsState[i] > contact->state)
00336             contact->state = contact->pointsState[i];
00337     }
00338 
00339     contact->normalDerivative.setZero(); //XXX
00340     return contact->state;
00341 }
00342 
00343 int GJKCollisionSolver::checkPolygonDisk(Contact* contact)
00344 {
00345     BasePolygon* polygon0 = static_cast<BasePolygon*>(contact->body0);
00346     Disk* disk1 = static_cast<Disk*>(contact->body1);
00347 
00348     if(polygon0->vertexes().empty()) {
00349         return contact->state = Contact::Unknown;
00350     }
00351 
00352     // Simplier version of checkPolygonPolygon algorithm
00353 
00354     Vector2dList vertexes;
00355     vertexes.reserve(polygon0->vertexes().size());
00356 
00357     const Vector2dList::const_iterator p0_it_end = polygon0->vertexes().end();
00358     for(Vector2dList::const_iterator it0 = polygon0->vertexes().begin();
00359                                         it0 != p0_it_end; ++it0) {
00360         vertexes.push_back(polygon0->pointLocalToWorld(*it0));
00361     }
00362 
00363     int wsize;
00364     Vector2d w[3];  // Vertexes of current simplex
00365     Vector2d v;     // Closest point of current simplex
00366     Vector2d s;     // New support vertex in direction v
00367 
00368     Vector2d vv; // Closest points on the polygon
00369     int wi[3];   // Indexes of vertexes corresponding to w
00370     int si;      // Indexes of vertexes corresponding to s
00371 
00372     // Start with arbitrary vertex (TODO: cache the whole w simplex)
00373     wsize = 1;
00374     if(contact->state >= Contact::Separating && contact->state < Contact::Intersected) {
00375         wi[0] = contact->_w1[0];
00376     } else {
00377         wi[0] = 0;
00378     }
00379     vv = vertexes[wi[0]];
00380     w[0] = v = disk1->position() - vv;
00381 
00382     bool intersects = false;
00383     unsigned int iteration = 0;
00384     for(;; ++iteration) {
00385         //STEPCORE_ASSERT_NOABORT( iteration < vertexes.size()*vertexes.size() );
00386 
00387         double smin = v.norm2();
00388 
00389         // Check for penetration (part 1)
00390         // If we are closer to the origin then given tolerance
00391         // we should stop just now to avoid computational errors later
00392         if(std::sqrt(smin) - disk1->radius() < _toleranceAbs*1e-2) { // XXX: separate tolerance for penetration ?
00393             intersects = true;
00394             break;
00395         }
00396 
00397         // Find support vertex in direction v
00398         // TODO: coherence optimization
00399         bool sfound = false;
00400         unsigned int vertex_size = vertexes.size();
00401 
00402         for(unsigned int i0=0; i0<vertex_size; ++i0) {
00403             Vector2d sn = disk1->position() - vertexes[i0];
00404             double scurr = v.innerProduct(sn);
00405             if(smin - scurr > _toleranceAbs*_toleranceAbs*1e-4) { // XXX: separate tolerance ?
00406                 smin = scurr;
00407                 s = sn;
00408                 si = i0;
00409                 sfound = true;
00410             }
00411         }
00412 
00413         // If no support vertex have been found than we are at minimum
00414         if(!sfound) {
00415             if(wsize == 0) { // we have guessed right point
00416                 w[0] = v;
00417                 wi[0] = 0;
00418                 wsize = 1;
00419             }
00420             break;
00421         }
00422 
00423         // Check for penetration (part 2)
00424         if(wsize == 2) {
00425             // objects are penetrating if origin lies inside the simplex
00426             // XXX: are there faster method to test it ?
00427             Vector2d w02 = w[0] - s;
00428             Vector2d w12 = w[1] - s;
00429             Vector2d s0 = s - s.unit() * disk1->radius();
00430             double det  =  w02[0]*w12[1] - w02[1]*w12[0];
00431             double det0 =  -s0[0]*w12[1] +  s0[1]*w12[0];
00432             double det1 = -w02[0]* s0[1] + w02[1]* s0[0];
00433             if(det0/det > 0 && det1/det > 0) { // XXX: tolerance
00434                 w[wsize] = s;
00435                 wi[wsize] = si;
00436                 ++wsize;
00437                 v.setZero();
00438                 intersects = true;
00439                 break;
00440             }
00441         }
00442 
00443         // Find v = dist(conv(w+s))
00444         double lambda = 0;
00445         int ii = -1;
00446         for(int i=0; i<wsize; ++i) {
00447             double lambda0 = - s.innerProduct(w[i]-s) / (w[i]-s).norm2();
00448             if(lambda0 > 0) {
00449                 Vector2d vn = s*(1-lambda0) + w[i]*lambda0;
00450                 if(vn.norm2() < v.norm2()) {
00451                     v = vn; ii = i;
00452                     lambda = lambda0;
00453                 }
00454             }
00455         }
00456 
00457         if(ii >= 0) { // Closest simplex is line
00458             vv = vertexes[si]*(1-lambda) + vertexes[wi[ii]]*lambda;
00459             if(wsize == 2) {
00460                 w[1-ii] = s;
00461                 wi[1-ii] = si;
00462             } else {
00463                 w[wsize] = s;
00464                 wi[wsize] = si;
00465                 ++wsize;
00466             }
00467         } else { // Closest simplex is vertex
00468             STEPCORE_ASSERT_NOABORT(iteration == 0 || s.norm2() < v.norm2());
00469 
00470             v = w[0] = s;
00471             vv = vertexes[si];
00472             wi[0] = si;
00473             wsize = 1;
00474         }
00475     }
00476 
00477     if(intersects) {
00478         /*qDebug("penetration detected");
00479         qDebug("iteration = %d", iteration);
00480         qDebug("simplexes:");
00481         qDebug("    1:   2:");
00482         for(int i=0; i<wsize; ++i) {
00483             qDebug("    %d    %d", wi[i], wi[i]);
00484         }*/
00485         contact->distance = 0;
00486         contact->normal.setZero();
00487         contact->pointsCount = 0;
00488         return contact->state = Contact::Intersected;
00489     }
00490 
00491     /*
00492     qDebug("distance = %f", v.norm());
00493     Vector2d v1 = v / v.norm();
00494     qDebug("normal = (%f,%f)", v1[0], v1[1]);
00495     qDebug("iteration = %d", iteration);
00496     qDebug("simplexes:");
00497     qDebug("    1:   2:");
00498     for(int i=0; i<wsize; ++i) {
00499         qDebug("    %d    %d", wi[i], wi[i]);
00500     }
00501     qDebug("contact points:");
00502     qDebug("    (%f,%f)    (%f,%f)", vv[0], vv[1], vv[0], vv[1]);
00503     */
00504 
00505     double vnorm = v.norm();
00506     contact->distance = vnorm - disk1->radius();
00507     contact->normal = v/vnorm;
00508 
00509     contact->_w1[0] = wi[0];
00510 
00511     if(contact->distance > _toleranceAbs) {
00512         contact->pointsCount = 0;
00513         contact->state = Contact::Separated;
00514         return contact->state;
00515     }/* else if(contact->distance < _toleranceAbs*1e-2) {
00516         contact->state = Contact::Intersected;
00517         return contact->state;
00518     }*/
00519 
00520     contact->pointsCount = 1;
00521     contact->points[0] = disk1->position() - contact->normal * disk1->radius();
00522     contact->vrel[0] = contact->normal.innerProduct(
00523                         disk1->velocity() - 
00524                         polygon0->velocityWorld(contact->points[0]));
00525 
00526     if(contact->vrel[0] < 0)
00527         contact->pointsState[0] = Contact::Colliding;
00528     else if(contact->vrel[0] < _toleranceAbs) // XXX: tolerance
00529         contact->pointsState[0] = Contact::Contacted;
00530     else contact->pointsState[0] = Contact::Separating;
00531 
00532     contact->normalDerivative.setZero(); //XXX
00533     contact->state = contact->pointsState[0];
00534     return contact->state;
00535 }
00536 
00537 int GJKCollisionSolver::checkPolygonParticle(Contact* contact)
00538 {
00539     BasePolygon* polygon0 = static_cast<BasePolygon*>(contact->body0);
00540     Particle* particle1 = static_cast<Particle*>(contact->body1);
00541 
00542     if(polygon0->vertexes().empty()) {
00543         return contact->state = Contact::Unknown;
00544     }
00545 
00546     // Simplier version of checkPolygonPolygon algorithm
00547 
00548     Vector2dList vertexes;
00549     vertexes.reserve(polygon0->vertexes().size());
00550 
00551     const Vector2dList::const_iterator p0_it_end = polygon0->vertexes().end();
00552     for(Vector2dList::const_iterator it0 = polygon0->vertexes().begin();
00553                                         it0 != p0_it_end; ++it0) {
00554         vertexes.push_back(polygon0->pointLocalToWorld(*it0));
00555     }
00556 
00557     int wsize;
00558     Vector2d w[3];  // Vertexes of current simplex
00559     Vector2d v;     // Closest point of current simplex
00560     Vector2d s;     // New support vertex in direction v
00561 
00562     Vector2d vv; // Closest points on the polygon
00563     int wi[3];   // Indexes of vertexes corresponding to w
00564     int si;      // Indexes of vertexes corresponding to s
00565 
00566     // Start with arbitrary vertex (TODO: cache the whole w simplex)
00567     wsize = 1;
00568     if(contact->state >= Contact::Separating && contact->state < Contact::Intersected) {
00569         wi[0] = contact->_w1[0];
00570     } else {
00571         wi[0] = 0;
00572     }
00573     vv = vertexes[wi[0]];
00574     w[0] = v = particle1->position() - vv;
00575 
00576     bool intersects = false;
00577     unsigned int iteration = 0;
00578     for(;; ++iteration) {
00579         //STEPCORE_ASSERT_NOABORT( iteration < vertexes[0].size()*vertexes[1].size() );
00580 
00581         double smin = v.norm2();
00582 
00583         // Check for penetration (part 1)
00584         // If we are closer to the origin then given tolerance
00585         // we should stop just now to avoid computational errors later
00586         if(smin < _toleranceAbs*_toleranceAbs*1e-4) { // XXX: separate tolerance for penetration ?
00587             intersects = true;
00588             break;
00589         }
00590 
00591         // Find support vertex in direction v
00592         // TODO: coherence optimization
00593         bool sfound = false;
00594         unsigned int vertex_size = vertexes.size();
00595 
00596         for(unsigned int i0=0; i0<vertex_size; ++i0) {
00597             Vector2d sn = particle1->position() - vertexes[i0];
00598             double scurr = v.innerProduct(sn);
00599             if(smin - scurr > _toleranceAbs*_toleranceAbs*1e-4) { // XXX: separate tolerance ?
00600                 smin = scurr;
00601                 s = sn;
00602                 si = i0;
00603                 sfound = true;
00604             }
00605         }
00606 
00607         // If no support vertex have been found than we are at minimum
00608         if(!sfound) {
00609             if(wsize == 0) { // we have guessed right point
00610                 w[0] = v;
00611                 wi[0] = 0;
00612                 wsize = 1;
00613             }
00614             break;
00615         }
00616 
00617         // Check for penetration (part 2)
00618         if(wsize == 2) {
00619             // objects are penetrating if origin lies inside the simplex
00620             // XXX: are there faster method to test it ?
00621             Vector2d w02 = w[0] - s;
00622             Vector2d w12 = w[1] - s;
00623             double det  =  w02[0]*w12[1] - w02[1]*w12[0];
00624             double det0 =   -s[0]*w12[1] +   s[1]*w12[0];
00625             double det1 = -w02[0]*  s[1] + w02[1]*  s[0];
00626             if(det0/det > 0 && det1/det > 0) { // XXX: tolerance
00627                 w[wsize] = s;
00628                 wi[wsize] = si;
00629                 ++wsize;
00630                 v.setZero();
00631                 intersects = true;
00632                 break;
00633             }
00634         }
00635 
00636         // Find v = dist(conv(w+s))
00637         double lambda = 0;
00638         int ii = -1;
00639         for(int i=0; i<wsize; ++i) {
00640             double lambda0 = - s.innerProduct(w[i]-s) / (w[i]-s).norm2();
00641             if(lambda0 > 0) {
00642                 Vector2d vn = s*(1-lambda0) + w[i]*lambda0;
00643                 if(vn.norm2() < v.norm2()) {
00644                     v = vn; ii = i;
00645                     lambda = lambda0;
00646                 }
00647             }
00648         }
00649 
00650         if(ii >= 0) { // Closest simplex is line
00651             vv = vertexes[si]*(1-lambda) + vertexes[wi[ii]]*lambda;
00652             if(wsize == 2) {
00653                 w[1-ii] = s;
00654                 wi[1-ii] = si;
00655             } else {
00656                 w[wsize] = s;
00657                 wi[wsize] = si;
00658                 ++wsize;
00659             }
00660         } else { // Closest simplex is vertex
00661             STEPCORE_ASSERT_NOABORT(iteration == 0 || s.norm2() < v.norm2());
00662 
00663             v = w[0] = s;
00664             vv = vertexes[si];
00665             wi[0] = si;
00666             wsize = 1;
00667         }
00668     }
00669 
00670     if(intersects) {
00671         /*
00672         qDebug("penetration detected");
00673         qDebug("iteration = %d", iteration);
00674         qDebug("simplexes:");
00675         qDebug("    1:   2:");
00676         for(int i=0; i<wsize; ++i) {
00677             qDebug("    %d    %d", wi[0][i], wi[1][i]);
00678         }
00679         */
00680         contact->distance = 0;
00681         contact->normal.setZero();
00682         contact->pointsCount = 0;
00683         return contact->state = Contact::Intersected;
00684     }
00685 
00686     /*
00687     qDebug("distance = %f", v.norm());
00688     Vector2d v1 = v / v.norm();
00689     qDebug("normal = (%f,%f)", v1[0], v1[1]);
00690     qDebug("iteration = %d", iteration);
00691     qDebug("simplexes:");
00692     qDebug("    1:   2:");
00693     for(int i=0; i<wsize; ++i) {
00694         qDebug("    %d    %d", wi[0][i], wi[1][i]);
00695     }
00696     qDebug("contact points:");
00697     qDebug("    (%f,%f)    (%f,%f)", vv[0][0], vv[0][1], vv[1][0], vv[1][1]);
00698     */
00699 
00700     double vnorm = v.norm();
00701     contact->distance = vnorm;
00702     contact->normal = v/vnorm;
00703 
00704     contact->_w1[0] = wi[0];
00705 
00706     if(vnorm > _toleranceAbs) {
00707         contact->pointsCount = 0;
00708         contact->state = Contact::Separated;
00709         return contact->state;
00710     }
00711 
00712     contact->pointsCount = 1;
00713     contact->points[0] = particle1->position();
00714     contact->vrel[0] = contact->normal.innerProduct(
00715                         particle1->velocity() - 
00716                         polygon0->velocityWorld(contact->points[0]));
00717 
00718     if(contact->vrel[0] < 0)
00719         contact->pointsState[0] = Contact::Colliding;
00720     else if(contact->vrel[0] < _toleranceAbs) // XXX: tolerance
00721         contact->pointsState[0] = Contact::Contacted;
00722     else contact->pointsState[0] = Contact::Separating;
00723 
00724     contact->state = contact->pointsState[0];
00725     return contact->state;
00726 }
00727 
00728 int GJKCollisionSolver::checkDiskDisk(Contact* contact)
00729 {
00730     Disk* disk0 = static_cast<Disk*>(contact->body0);
00731     Disk* disk1 = static_cast<Disk*>(contact->body1);
00732 
00733     Vector2d r = disk1->position() - disk0->position();
00734     double rd = disk1->radius() + disk0->radius();
00735     double rn = r.norm();
00736     contact->normal = r/rn;
00737     contact->distance = rn - rd;
00738 
00739     if(contact->distance > _toleranceAbs) {
00740         contact->state = Contact::Separated;
00741         return contact->state;
00742     } else if(contact->distance < 0) {
00743         contact->state = Contact::Intersected;
00744         return contact->state;
00745     }
00746 
00747     contact->pointsCount = 1;
00748     contact->points[0] = disk0->position() + contact->normal * disk0->radius();
00749 
00750     Vector2d v = disk1->velocity() - disk0->velocity();
00751     contact->vrel[0] = contact->normal.innerProduct(v);
00752     contact->normalDerivative = v / rn - (r.innerProduct(v)/rn/rn/rn) * r;
00753 
00754     if(contact->vrel[0] < 0)
00755         contact->pointsState[0] = Contact::Colliding;
00756     else if(contact->vrel[0] < _toleranceAbs) // XXX: tolerance
00757         contact->pointsState[0] = Contact::Contacted;
00758     else contact->pointsState[0] = Contact::Separating;
00759 
00760     contact->normalDerivative.setZero(); //XXX
00761     contact->state = contact->pointsState[0];
00762     return contact->state;
00763 }
00764 
00765 int GJKCollisionSolver::checkDiskParticle(Contact* contact)
00766 {
00767     Disk* disk0 = static_cast<Disk*>(contact->body0);
00768     Particle* particle1 = static_cast<Particle*>(contact->body1);
00769 
00770     Vector2d r = particle1->position() - disk0->position();
00771     double rd = disk0->radius();
00772     double rn = r.norm();
00773     contact->normal = r/rn;
00774     contact->distance = rn - rd;
00775 
00776     if(contact->distance > _toleranceAbs) {
00777         contact->state = Contact::Separated;
00778         return contact->state;
00779     } else if(contact->distance < _toleranceAbs*1e-2) {
00780         contact->state = Contact::Intersected;
00781         return contact->state;
00782     }
00783 
00784     contact->pointsCount = 1;
00785     contact->points[0] = disk0->position() + contact->normal * disk0->radius();
00786 
00787     Vector2d v = particle1->velocity() - disk0->velocity();
00788     contact->vrel[0] = contact->normal.innerProduct(v);
00789     contact->normalDerivative = v / rn - (r.innerProduct(v)/rn/rn/rn) * r;
00790 
00791     if(contact->vrel[0] < 0)
00792         contact->pointsState[0] = Contact::Colliding;
00793     else if(contact->vrel[0] < _toleranceAbs) // XXX: tolerance
00794         contact->pointsState[0] = Contact::Contacted;
00795     else contact->pointsState[0] = Contact::Separating;
00796 
00797     contact->state = contact->pointsState[0];
00798     return contact->state;
00799 }
00800 
00801 
00802 /*
00803 int GJKCollisionSolver::checkContact(Contact* contact)
00804 {
00805 
00806     if(contact->type == Contact::UnknownType) {
00807         if(contact->body0->metaObject()->inherits<BasePolygon>()) {
00808             if(contact->body1->metaObject()->inherits<BasePolygon>()) contact->type = Contact::PolygonPolygonType;
00809             else if(contact->body1->metaObject()->inherits<Particle>()) contact->type = Contact::PolygonParticleType;
00810         } else if(contact->body0->metaObject()->inherits<Particle>()) {
00811             if(contact->body1->metaObject()->inherits<BasePolygon>()) {
00812                 std::swap(contact->body0, contact->body1);
00813                 contact->type = Contact::PolygonParticleType;
00814             }
00815         }
00816         contact->state = Contact::Unknown;
00817     }
00818 
00819     if(contact->type == Contact::PolygonPolygonType) return checkPolygonPolygon(contact);
00820     else if(contact->type == Contact::PolygonParticleType) return checkPolygonParticle(contact);
00821     return contact->state = Contact::Unknown;
00822 }*/
00823 
00824 inline int GJKCollisionSolver::checkContact(Contact* contact)
00825 {
00826     if(contact->type == Contact::PolygonPolygonType) checkPolygonPolygon(contact);
00827     else if(contact->type == Contact::PolygonDiskType) checkPolygonDisk(contact);
00828     else if(contact->type == Contact::PolygonParticleType) checkPolygonParticle(contact);
00829     else if(contact->type == Contact::DiskDiskType) checkDiskDisk(contact);
00830     else if(contact->type == Contact::DiskParticleType) checkDiskParticle(contact);
00831     else contact->state = Contact::Unknown;
00832     return contact->state;
00833 }
00834 
00835 int GJKCollisionSolver::checkContacts(BodyList& bodies, ConstraintsInfo* info, bool collisions)
00836 {
00837     int state = Contact::Unknown;
00838 
00839     checkCache(bodies);
00840 
00841     // Detect and classify contacts
00842     const ContactValueList::iterator end = _contacts.end();
00843     for(ContactValueList::iterator it = _contacts.begin(); it != end; ++it) {
00844         Contact& contact = *it;
00845 
00846         checkContact(&contact);
00847 
00848         if(contact.state > state) state = contact.state;
00849         if(contact.state == Contact::Intersected) return state;
00850     }
00851 
00852     if(info) {
00853         // Add contact joints
00854         for(ContactValueList::iterator it = _contacts.begin(); it != end; ++it) {
00855             Contact& contact = *it;
00856             if(contact.state == Contact::Contacted) {
00857                 //qDebug("** resting contact, points: %d", contact.pointsCount);
00858                 for(int p=0; p<contact.pointsCount; ++p) {
00859                     int i = info->addContact();
00860                     // XXX: check signs !
00861                     // XXX: rotation and friction !
00862                     /*info->value[i0] = contact.normal[0] * contact.distance;
00863                     info->value[i1] = contact.normal[1] * contact.distance;
00864                     info->derivative[i0] = contact.normal[0] * contact.vrel[0];
00865                     info->derivative[i1] = contact.normal[1] * contact.vrel[0];*/
00866                     /*info->value[i] = contact.distance;
00867                     info->derivative[i] = contact.vrel[p];*/
00868                     
00869                     info->jacobian.row(i).w(contact.body0->variablesOffset() +
00870                                             RigidBody::PositionOffset, -contact.normal[0]);
00871                     info->jacobian.row(i).w(contact.body0->variablesOffset() +
00872                                             RigidBody::PositionOffset+1, -contact.normal[1]);
00873                     info->jacobian.row(i).w(contact.body1->variablesOffset() +
00874                                             RigidBody::PositionOffset, contact.normal[0]);
00875                     info->jacobian.row(i).w(contact.body1->variablesOffset() +
00876                                             RigidBody::PositionOffset+1, contact.normal[1]);
00877 
00878                     if(!collisions) {
00879                         info->jacobianDerivative.row(i).w(contact.body0->variablesOffset() +
00880                                                 RigidBody::PositionOffset, -contact.normalDerivative[0]);
00881                         info->jacobianDerivative.row(i).w(contact.body0->variablesOffset() +
00882