Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

PlannerObstacles.cc

Go to the documentation of this file.
00001 #include "PlannerObstacles.h"
00002 #include "GJK.h"
00003 #include "Shared/FamilyFactory.h"
00004 #include "Shared/debuget.h"
00005 #include "Shared/plistSpecialty.h"
00006 #include "Shared/mathutils.h" // for isnan fix
00007 
00008 #include <iostream>
00009 #include <typeinfo>
00010 
00011 using namespace std;
00012 using namespace fmat;
00013 
00014 const float INF = 10e10f;
00015 
00016 const std::string RectangularObstacle::autoRegisterName = PlannerObstacle2D::getRegistry().registerType<RectangularObstacle>("Rectangle");
00017 const std::string CircularObstacle::autoRegisterName = PlannerObstacle2D::getRegistry().registerType<CircularObstacle>("Circle");
00018 const std::string EllipticalObstacle::autoRegisterName = PlannerObstacle2D::getRegistry().registerType<EllipticalObstacle>("Ellipse");
00019 const std::string ConvexPolyObstacle::autoRegisterName = PlannerObstacle2D::getRegistry().registerType<ConvexPolyObstacle>("ConvexPoly");
00020 const std::string HierarchicalObstacle::autoRegisterName = PlannerObstacle2D::getRegistry().registerType<HierarchicalObstacle>("Hierarchy");
00021 const std::string BoxObstacle::autoRegisterName = PlannerObstacle3D::getRegistry().registerType<BoxObstacle>("Box");
00022 const std::string CylindricalObstacle::autoRegisterName = PlannerObstacle3D::getRegistry().registerType<CylindricalObstacle>("Cylinder");
00023 const std::string SphericalObstacle::autoRegisterName = PlannerObstacle3D::getRegistry().registerType<SphericalObstacle>("Sphere");
00024 const std::string EllipsoidObstacle::autoRegisterName = PlannerObstacle3D::getRegistry().registerType<EllipsoidObstacle>("Ellipsoid");
00025 
00026 PLIST_CLONE_IMP(RectangularObstacle,new RectangularObstacle(*this));
00027 PLIST_CLONE_IMP(CircularObstacle,new CircularObstacle(*this));
00028 PLIST_CLONE_IMP(EllipticalObstacle,new EllipticalObstacle(*this));
00029 PLIST_CLONE_IMP(ConvexPolyObstacle,new ConvexPolyObstacle(*this));
00030 PLIST_CLONE_IMP(HierarchicalObstacle,new HierarchicalObstacle(*this));
00031 PLIST_CLONE_IMP(BoxObstacle,new BoxObstacle(*this));
00032 PLIST_CLONE_IMP(CylindricalObstacle,new CylindricalObstacle(*this));
00033 PLIST_CLONE_IMP(SphericalObstacle,new SphericalObstacle(*this));
00034 PLIST_CLONE_IMP(EllipsoidObstacle,new EllipsoidObstacle(*this));
00035 
00036 namespace plist {
00037   template<> PlannerObstacle2D* loadXML(xmlNode* node) {
00038     plist::Primitive<std::string> type;
00039     Dictionary td(false);
00040     td.setUnusedWarning(false);
00041     td.addEntry(".type",type);
00042     td.loadXML(node);
00043     PlannerObstacle2D* po = PlannerObstacle2D::getRegistry().create(type);
00044     if(po==NULL)
00045       throw XMLLoadSave::bad_format(node,"Unknown PlannerObstacle type "+type);
00046     try {
00047       po->loadXML(node);
00048     } catch(...) {
00049       delete po;
00050       throw;
00051     }
00052     return po;
00053   }
00054   template<> PlannerObstacle3D* loadXML(xmlNode* node) {
00055     plist::Primitive<std::string> type;
00056     Dictionary td(false);
00057     td.setUnusedWarning(false);
00058     td.addEntry(".type",type);
00059     td.loadXML(node);
00060     PlannerObstacle3D* po = PlannerObstacle3D::getRegistry().create(type);
00061     if(po==NULL)
00062       throw XMLLoadSave::bad_format(node,"Unknown PlannerObstacle type "+type);
00063     try {
00064       po->loadXML(node);
00065     } catch(...) {
00066       delete po;
00067       throw;
00068     }
00069     return po;
00070   }
00071 }
00072 
00073 // Collision dispatching
00074 template <>
00075 bool PlannerObstacle2D::collides(const PlannerObstacle2D& other) const {
00076   /* Although we have the generalized GJK algorithm for collision detection,
00077    we use analytic solutions that are fully tested and are faster than GJK. */
00078   switch(geometry * geometry * other.geometry) {
00079     // handled by rect: rect-rect, rect-circ
00080     case RECTANGULAR_OBS * RECTANGULAR_OBS * RECTANGULAR_OBS:
00081       return static_cast<const RectangularObstacle*>(this)->collides(static_cast<const RectangularObstacle&>(other));
00082     case RECTANGULAR_OBS * RECTANGULAR_OBS * CIRCULAR_OBS:
00083       return static_cast<const RectangularObstacle*>(this)->collides(static_cast<const CircularObstacle&>(other));
00084     case RECTANGULAR_OBS * CIRCULAR_OBS * CIRCULAR_OBS:
00085       return static_cast<const RectangularObstacle*>(&other)->collides(static_cast<const CircularObstacle&>(*this));
00086       
00087     // handled by circ: circ-circ
00088     case CIRCULAR_OBS * CIRCULAR_OBS * CIRCULAR_OBS:
00089       return static_cast<const CircularObstacle*>(this)->collides(static_cast<const CircularObstacle&>(other));
00090       
00091     // handled by convex: convex-rect, convex-circ, convex-convex
00092     case CONVEX_POLY_OBS * CONVEX_POLY_OBS * RECTANGULAR_OBS:
00093       return static_cast<const ConvexPolyObstacle*>(this)->collides(static_cast<const RectangularObstacle&>(other));
00094     case CONVEX_POLY_OBS * RECTANGULAR_OBS * RECTANGULAR_OBS:
00095       return static_cast<const ConvexPolyObstacle*>(&other)->collides(static_cast<const RectangularObstacle&>(*this));
00096     case CONVEX_POLY_OBS * CONVEX_POLY_OBS * CIRCULAR_OBS:
00097       return static_cast<const ConvexPolyObstacle*>(this)->collides(static_cast<const CircularObstacle&>(other));
00098     case CONVEX_POLY_OBS * CIRCULAR_OBS * CIRCULAR_OBS:
00099       return static_cast<const ConvexPolyObstacle*>(&other)->collides(static_cast<const CircularObstacle&>(*this));
00100     case CONVEX_POLY_OBS * CONVEX_POLY_OBS * CONVEX_POLY_OBS:
00101       return static_cast<const ConvexPolyObstacle*>(this)->collides(static_cast<const ConvexPolyObstacle&>(other));
00102   }
00103   
00104   /* For the shapes that don't have efficient or accurate analytic solutions
00105    we default to GJK.*/
00106   return GJK::collides(this, &other);
00107 }
00108 
00109 template <>
00110 bool PlannerObstacle3D::collides(const PlannerObstacle3D& other) const {
00111   return GJK::collides(this, &other);
00112 }
00113 
00114 void RectangularObstacle::reset(fmat::Column<2> centerPoint,
00115                 const fmat::SubVector<2,const fmat::fmatReal>& extents,
00116                 const fmat::SubMatrix<2,2,const fmat::fmatReal>& rot) {
00117   center = centerPoint;
00118   
00119   fmat::Column<2> off = rot * extents;
00120   points.row(TOP_RIGHT) = &(center + off)[0]; // 0 top right
00121   points.row(BOTTOM_LEFT) = &(center - off)[0]; // 2 bottom left
00122   off = rot * fmat::pack(-extents[0],extents[1]);
00123   points.row(TOP_LEFT) = &(center + off)[0]; // 1 top left
00124   points.row(BOTTOM_RIGHT) = &(center - off)[0]; // 3 bottom right
00125   
00126   bBox.min = &points.minR()[0];
00127   bBox.max = &points.maxR()[0];
00128   
00129   unrot = rot.transpose(); // now switch to inverse
00130   centerPoint = unrot * centerPoint;
00131   maxEx = centerPoint + extents;
00132   minEx = centerPoint - extents;
00133 }
00134 
00135 void RectangularObstacle::updatePosition(const fmat::SubVector<2,const fmat::fmatReal>& newPos) {
00136   fmat::Column<2> diff = newPos - getCenter();
00137   center = newPos;
00138   bBox.min += diff;
00139   bBox.max += diff;
00140   points.column(0)+=diff[0];
00141   points.column(1)+=diff[1];
00142   
00143   diff = unrot * diff;
00144   minEx += diff;
00145   maxEx += diff;
00146 }
00147 
00148 bool RectangularObstacle::collides(const RectangularObstacle& other) const {
00149   if( !bBox.collides(other.bBox) ) // test cheap aabb first
00150     return false;
00151   if(unrot(0,0)==1 && other.unrot(0,0)==1)
00152     return true; // rectangle is axis aligned, bb hit is all we needed
00153   
00154   // first test of other against current axes
00155   fmat::Matrix<4,2> p2 = other.points * unrot.transpose();
00156   float minX,maxX,minY,maxY;
00157   minX=p2.column(0).min();
00158   maxX=p2.column(0).max();
00159   minY=p2.column(1).min();
00160   maxY=p2.column(1).max();
00161   if(maxX <= minEx[0] || maxEx[0] <= minX
00162      || maxY <= minEx[1] || maxEx[1] <= minY)
00163     return false;
00164   
00165   if(unrot(0,0)!=other.unrot(0,0)) {
00166     // test current points against other's axes
00167     p2 = points * other.unrot.transpose();
00168     minX=p2.column(0).min();
00169     maxX=p2.column(0).max();
00170     minY=p2.column(1).min();
00171     maxY=p2.column(1).max();
00172     if(maxX <= other.minEx[0] || other.maxEx[0] <= minX
00173        || maxY <= other.minEx[1] || other.maxEx[1] <= minY)
00174       return false;
00175   }
00176   
00177   return true;
00178 }
00179 
00180 bool RectangularObstacle::collides(const CircularObstacle& other) const { 
00181   const fmat::Column<2> p = unrot * other.getCenter();
00182   const fmat::Column<2> pmin = p-other.getRadius(), pmax = p+other.getRadius();
00183   
00184   if(pmax[0]<=minEx[0] || maxEx[0]<=pmin[0] || pmax[1]<=minEx[1] || maxEx[1]<=pmin[1]) {
00185     return false;
00186   } else if(p[0]<minEx[0]) { // left margin
00187     if(p[1]<minEx[1]) { // bottom left corner
00188       return ((minEx - p).norm() < other.getRadius());
00189     } else if(maxEx[1]<p[1]) { // top left corner
00190       return ((fmat::pack(minEx[0],maxEx[1]) - p).norm() < other.getRadius());
00191     }
00192     return true; // center left
00193   } else if(maxEx[0] < p[0]) { // right margin
00194     if(p[1]<minEx[1]) { // bottom right corner
00195       return ((fmat::pack(maxEx[0],minEx[1]) - p).norm() < other.getRadius());
00196     } else if(maxEx[1]<p[1]) { // top right corner
00197       return ((maxEx - p).norm() < other.getRadius());
00198     }
00199     return true; // center right
00200   } else {
00201     return true; // center top or bottom margin
00202   }
00203 }
00204 
00205 void RectangularObstacle::rotate(const fmat::SubVector<2,const fmat::fmatReal>& origin,
00206                  const fmat::SubMatrix<2,2,const fmat::fmatReal>& rot) {
00207   fmat::Column<2> newCenter = rot * (getCenter() - origin) + origin;
00208   reset(newCenter, getExtents(), unrot.transpose() * rot);
00209 }
00210 
00211 fmat::Column<2> RectangularObstacle::gradient(const fmat::SubVector<2,const fmat::fmatReal>& pt) const {
00212   const fmat::Column<2> p = unrot * pt;
00213   
00214   if(p[0]<minEx[0]) { // left margin
00215     if(p[1]<minEx[1]) { // bottom left corner
00216       return fmat::Column<2>(points.row(BOTTOM_LEFT).transpose()) - pt;
00217     } else if(maxEx[1]<p[1]) { // top left corner
00218       return fmat::Column<2>(points.row(TOP_LEFT).transpose()) - pt;
00219     }
00220     return unrot.row(0).transpose() * (minEx[0]-p[0]); // center left
00221   } else if(maxEx[0] < p[0]) { // right margin
00222     if(p[1]<minEx[1]) { // bottom right corner
00223       return fmat::Column<2>(points.row(BOTTOM_RIGHT).transpose()) - pt;
00224     } else if(maxEx[1]<p[1]) { // top right corner
00225       return fmat::Column<2>(points.row(TOP_RIGHT).transpose()) - pt;
00226     }
00227     return unrot.row(0).transpose() * (maxEx[0]-p[0]); // center right
00228   } else if(p[1]<minEx[1]) {
00229     return unrot.row(1).transpose() * (minEx[1]-p[1]); // bottom margin
00230   } else if(maxEx[1]<p[1]) {
00231     return unrot.row(1).transpose() * (maxEx[1]-p[1]); // top margin
00232   } else {
00233     // inside
00234     fmatReal dist[4];
00235     dist[0] = p[0]-minEx[0]; // left
00236     dist[1] = p[1]-minEx[1]; // bottom
00237     dist[2] = maxEx[0]-p[0]; // right
00238     dist[3] = maxEx[1]-p[1]; // top
00239     if(dist[0] < dist[1]) {
00240       if(dist[0] < dist[2]) {
00241         if(dist[0] < dist[3]) {
00242           return unrot.row(0).transpose() * (minEx[0]-p[0]); // left
00243         }
00244       } else {
00245         if(dist[2] < dist[3]) {
00246           return unrot.row(0).transpose() * (maxEx[0]-p[0]); // right
00247         }
00248       }
00249     } else {
00250       if(dist[1] < dist[2]) {
00251         if(dist[1] < dist[3]) {
00252           return unrot.row(1).transpose() * (minEx[1]-p[1]); // bottom
00253         }
00254       } else {
00255         if(dist[2] < dist[3]) {
00256           return unrot.row(0).transpose() * (maxEx[0]-p[0]); // right
00257         }
00258       }
00259     }
00260     return unrot.row(1).transpose() * (maxEx[1]-p[1]); // top
00261   }
00262 }
00263 
00264 std::string RectangularObstacle::toString() const {
00265   ostringstream os;
00266   os << "RectangularObstacle[" << name << ",points=" << points.fmt() << ",unrot=" << unrot.fmt() << "]";
00267   return os.str();
00268 }
00269 
00270 bool RectangularObstacle::collides(const fmat::SubVector<2,const fmat::fmatReal>& point) const {
00271   const fmat::Column<2> p2 = unrot * point;
00272   return (minEx[0]<p2[0] && p2[0]<maxEx[0] &&
00273       minEx[1]<p2[1] && p2[1]<maxEx[1]);
00274 }
00275 
00276 fmat::Column<2> RectangularObstacle::getSupport(const fmat::SubVector<2,const fmat::fmatReal>& direction) const {
00277   const fmat::Column<2> dir = unrot * direction;
00278   // choose the closest corner
00279   if (dir[0]>0) {
00280     if (dir[1]>0)
00281       return points.row(TOP_RIGHT).transpose();
00282     else
00283       return points.row(BOTTOM_RIGHT).transpose();
00284   }
00285   else {
00286     if (dir[1]>0)
00287       return points.row(TOP_LEFT).transpose();
00288     else
00289       return points.row(BOTTOM_LEFT).transpose();
00290   }
00291 }
00292 
00293 void RectangularObstacle::bloat(float amount) {
00294   reset(getCenter(),maxEx+amount,unrot.transpose());
00295 }
00296 
00297 //! Decreases the size of the obstacle in all directions by at least @a amount
00298 void RectangularObstacle::contract(float amount) {
00299   bloat(-amount);
00300 }
00301 
00302 void RectangularObstacle::loadXML(xmlNode* node) {
00303   // temporarily add plist entries to get 'normalized' coordinates, then convert
00304   plist::ArrayOf< plist::Primitive<fmat::fmatReal> > c(2,0,false);
00305   plist::ArrayOf< plist::Primitive<fmat::fmatReal> > d(2,0,false);
00306   plist::Angle ang;
00307   addEntry("Center",c);
00308   addEntry("Dimensions",d);
00309   addEntry("Orientation",ang);
00310   PlannerObstacle2D::loadXML(node);
00311   removeEntry("Center");
00312   removeEntry("Dimensions");
00313   removeEntry("Orientation");
00314   reset(fmat::pack(c[0],c[1]), fmat::pack(d[0]/2,d[1]/2), ang);
00315 }
00316 void RectangularObstacle::saveXML(xmlNode * node) const {
00317   // temporarily add plist entries to get 'normalized' coordinates, then convert
00318   plist::ArrayOf< plist::Primitive<fmat::fmatReal> > c(2,0,false);
00319   getCenter().exportTo(c);
00320   c.setSaveInlineStyle(true);
00321   plist::ArrayOf< plist::Primitive<fmat::fmatReal> > d(2,0,false);
00322   d[0] = getWidth();
00323   d[1] = getHeight();
00324   d.setSaveInlineStyle(true);
00325   plist::Angle ang = getOrientation();
00326   plist::Dictionary tmp(*this);
00327   tmp.addEntry("Center",c,"Center point");
00328   tmp.addEntry("Dimensions",d,"Width and height");
00329   tmp.addEntry("Orientation",ang,"Rotation (radians unless you specify ° suffix)");
00330   tmp.saveXML(node);
00331 }
00332 
00333 float RectangularObstacle::getOrientation() const {
00334   return std::atan2(unrot(0,1),unrot(0,0));
00335 }
00336 
00337 fmat::Column<2> CircularObstacle::gradient(const fmat::SubVector<2,const fmat::fmatReal>& pt) const {
00338   fmat::Column<2> v = pt - center;
00339   fmat::fmatReal n = v.norm();
00340   if(n == 0)
00341     return fmat::pack(radius,0);
00342   return v * ((radius - n)/n);
00343 }
00344 
00345 std::string CircularObstacle::toString() const {
00346   ostringstream os;
00347   os << "CircularObstacle[" << name << ",center=" << center << ",radius=" << radius << "]";
00348   return os.str();
00349 }
00350 
00351 bool CircularObstacle::collides(const CircularObstacle& other) const {
00352   const fmat::Column<2> diff = center - other.center;
00353   const float r = radius + other.radius;
00354   return diff.sumSq() < r*r;
00355 }
00356 
00357 fmat::Column<2> CircularObstacle::getSupport(const fmat::SubVector<2,const fmat::fmatReal>& direction) const {
00358   return center + (direction / direction.norm() * radius);
00359 }
00360 
00361 void CircularObstacle::loadXML(xmlNode* node) {
00362   // temporarily add plist entries for serialization
00363   plist::ArrayOf< plist::Primitive<fmat::fmatReal> > c(2,0,false);
00364   plist::Primitive<fmat::fmatReal> r;
00365   addEntry("Center",c);
00366   addEntry("Radius",r);
00367   PlannerObstacle2D::loadXML(node);
00368   removeEntry("Center");
00369   removeEntry("Radius");
00370   center.importFrom(c);
00371   radius = r;
00372 }
00373 void CircularObstacle::saveXML(xmlNode * node) const {
00374   // temporarily add plist entries to get 'normalized' coordinates, then convert
00375   plist::ArrayOf< plist::Primitive<fmat::fmatReal> > c(2,0,false);
00376   c.setSaveInlineStyle(true);
00377   center.exportTo(c);
00378   plist::Primitive<fmat::fmatReal> r = radius;
00379   plist::Dictionary tmp(*this);
00380   tmp.addEntry("Center",c);
00381   tmp.addEntry("Radius",r);
00382   tmp.saveXML(node);
00383 }
00384 
00385 void EllipticalObstacle::reset(const fmat::Column<2>& f1, const fmat::Column<2>& f2, fmat::fmatReal s) {
00386   center = ( (focus1 = f1) + (focus2 = f2) ) / 2;
00387   semimajor = std::abs(s);
00388   fmat::fmatReal d = s*s - (f1-center).sumSq();
00389   if(d<0)
00390     throw std::invalid_argument("EllipticalObstacle::reset with two foci and too-short semimajor");
00391   semiminor = std::sqrt(d);
00392 }
00393 
00394 void EllipticalObstacle::reset(const fmat::Column<2>& c, fmat::fmatReal _semimajor, fmat::fmatReal _semiminor, fmat::fmatReal orientation) {
00395   center = c;
00396   semimajor = _semimajor;
00397   semiminor = _semiminor;
00398   if(semimajor < semiminor) {
00399     std::swap(semimajor, semiminor);
00400     orientation+=static_cast<fmat::fmatReal>(M_PI_2);
00401   }
00402   fmat::fmatReal focusDist = std::sqrt(semimajor*semimajor - semiminor*semiminor);
00403   fmat::Column<2> ax = fmat::pack(focusDist*std::cos(orientation),focusDist*std::sin(orientation));
00404   focus1 = center + ax;
00405   focus2 = center - ax;
00406 }
00407 
00408 fmat::Column<2> EllipticalObstacle::getSupport(const fmat::SubVector<2,const fmat::fmatReal>& direction) const {
00409   // handle circle case (semimajor == semiminor)
00410   if (semimajor == semiminor)
00411     return center + (direction / direction.norm() * semimajor); // or semiminor...
00412   
00413   // otherwise, we use a Lagrange Multiplier.
00414   fmat::Column<2> dir = getRotation().transpose() * direction;
00415   
00416   // if we're zero on any axis, it's easy.
00417   if (dir[0] == 0) {
00418     return getRotation() * fmat::pack(0, sgn(dir[1]) * semiminor) + center;
00419   }
00420   else if (dir[1] == 0) {
00421     return getRotation() * fmat::pack(sgn(dir[0]) * semimajor, 0) + center;
00422   }
00423   
00424   // for efficiency
00425   fmat::fmatReal a2 = semimajor*semimajor, b2 = semiminor*semiminor;
00426   
00427   // solve for x
00428   fmat::fmatReal k = (dir[1]*b2)/(dir[0]*a2);
00429   fmat::fmatReal x = sgn(dir[0]) * std::sqrt(1/((1/(a2)) + (k*k/(b2))));
00430   
00431   return getRotation() * fmat::pack(x, k*x) + center;
00432 }
00433 
00434 void EllipticalObstacle::updatePosition(const fmat::SubVector<2,const fmat::fmatReal>& newPos) {
00435   fmat::Column<2> diff = newPos - center;
00436   center = newPos;
00437   focus1 += diff;
00438   focus2 += diff;
00439 }
00440 
00441 void EllipticalObstacle::rotate(const fmat::SubVector<2,const fmat::fmatReal>& origin,
00442                 const fmat::SubMatrix<2,2,const fmat::fmatReal>& rot) {
00443   focus1 = rot * (focus1 - origin) + origin;
00444   focus2 = rot * (focus2 - origin) + origin;
00445   center = (focus1+focus2)/2;
00446 }
00447 
00448 fmat::Matrix<2,2> EllipticalObstacle::getRotation() const {
00449   if(semimajor==semiminor)
00450     return fmat::Matrix<2,2>::identity();
00451   fmat::fmatReal x[4] = { focus1[0]-center[0], focus1[1]-center[1] };
00452   fmat::fmatReal n = fmat::SubVector<2>(x).norm();
00453   x[0]/=n; x[1]/=n; x[2]=-x[1]; x[3]=x[0];
00454   return fmat::Matrix<2,2>(x);
00455 }
00456 
00457 fmat::Column<2> EllipticalObstacle::getPointOnEdge(const fmat::Column<2>& direction) const {
00458   fmat::Matrix<2,2> rot = getRotation();
00459   fmat::Column<2> x = rot.transpose() * direction;
00460   fmat::fmatReal ratio = semimajor/semiminor;
00461   x[1]*=ratio;
00462   x *= semimajor/x.norm();
00463   x[1]/=ratio;
00464   return rot * x + center;
00465 }
00466 
00467 BoundingBox2D EllipticalObstacle::getBoundingBox() const {
00468   float orientation = getOrientation();
00469   float t_x = std::atan(-semiminor * tan(orientation) / semimajor);
00470   float t_y = std::atan( semiminor / tan(orientation) / semimajor);
00471   BoundingBox2D b;
00472   // derived from parametric equation of the ellipse
00473   float o_sin = std::sin(orientation), t_x_sin = std::sin(t_x), t_y_sin = std::sin(t_y);
00474   float o_cos = std::cos(orientation), t_x_cos = std::cos(t_x), t_y_cos = std::cos(t_y);
00475   b.expand(fmat::pack(center[0] + semimajor*t_x_cos*o_cos - semiminor*t_x_sin*o_sin,
00476             center[1] + semiminor*t_y_sin*o_cos + semimajor*t_y_cos*o_sin));
00477   // shift t by PI for both x and y, which just flips the signs of the sin/cos
00478   b.expand(fmat::pack(center[0] - semimajor*t_x_cos*o_cos + semiminor*t_x_sin*o_sin,
00479             center[1] - semiminor*t_y_sin*o_cos - semimajor*t_y_cos*o_sin));
00480   return b;
00481 }
00482 
00483 fmat::Column<2> EllipticalObstacle::gradient(const fmat::SubVector<2,const fmat::fmatReal>& pt) const {
00484   // TODO: completely re-do this. the circle approximation might be fine.
00485   if (semimajor == semiminor)
00486     return getPointOnEdge(pt - center) - pt;
00487   
00488   fmat::Column<2> newPt = getRotation().transpose() * (pt - center);
00489   
00490   float a = (semimajor*semimajor - semiminor*semiminor);
00491   float b = semimajor * newPt[0];
00492   float c = semiminor * newPt[1];
00493   
00494   // http://planetmath.org/encyclopedia/QuarticFormula.html -- explanation
00495   
00496   float aa = a*a, bb = b*b;
00497   
00498   float a3 = -2*b/a;
00499   float a2 = (bb-aa+c*c)/aa;
00500   float a1 = -a3;
00501   float a0 = -bb/aa;
00502   
00503   float a2a2 = a2*a2, a3a3 = a3*a3;
00504   
00505   float t1 = -a3/4;
00506   float t2 = a2a2 - 3*a3*a1 + 12*a0;
00507   float t3 = (2*a2a2*a2 - 9*a3*a2*a1 + 27*a1*a1 + 27*a3a3*a0 - 72*a2*a0)/2;
00508   float t4 = (-a3a3*a3 + 4*a3*a2 - 8*a1)/32;
00509   float t5 = (3*a3a3 - 8*a2)/48;
00510   
00511   float s1 = std::sqrt(t3*t3 - std::pow(t2,3));
00512   float s2 = std::pow(t3 + s1, 1.0f/3.0f);
00513   float s3 = (1.0f/12.0f)*(t2/s2 + s2);
00514   float s4 = std::sqrt(t5 + s3);
00515   float s5 = 2*t5 - s3;
00516   float s6 = t4/s4;
00517   
00518   float sq1 = std::sqrt(s5 - s6);
00519   float sq2 = std::sqrt(s5 + s6);
00520   
00521   float sols[8];
00522   
00523   sols[0] = std::acos(t1 - s4 - sq1);
00524   sols[1] = std::acos(t1 - s4 + sq1);
00525   sols[2] = std::acos(t1 + s4 - sq2);
00526   sols[3] = std::acos(t1 + s4 + sq2);
00527   
00528   if (std::isnan(sols[0]) && std::isnan(sols[1]) && std::isnan(sols[2]) && std::isnan(sols[3])) {
00529     //std::cout << a << ' ' << b << ' ' << c << std::endl;
00530     return fmat::pack(0,0);
00531   }
00532   
00533   // if we're below the x-axis, negative angles
00534   if (newPt[1] < 0) {
00535     for (int i = 0; i < 4; i++)
00536       sols[i] = -sols[i];
00537   }
00538   
00539   float ori = getOrientation(), so = std::sin(ori), co = std::cos(ori);
00540   
00541   fmat::Column<2> grads[4];
00542   float minMag = std::numeric_limits<float>::infinity();
00543   int minMagIndex = 0;
00544   for (int i = 0; i < 4; i++) {
00545     if (std::isnan(sols[i])) continue;
00546     float st = std::sin(sols[i]), ct = std::cos(sols[i]);
00547     grads[i] = fmat::pack(semimajor*ct*co - semiminor*st*so,
00548                 semimajor*ct*so + semiminor*st*co) + center - pt;
00549     float sumSq = grads[i].sumSq();
00550     if (sumSq < minMag) {
00551       minMag = sumSq;
00552       minMagIndex = i;
00553     }
00554   }
00555   
00556   return grads[minMagIndex];
00557 }
00558 
00559 std::string EllipticalObstacle::toString() const {
00560   ostringstream os;
00561   os << "EllipticalObstacle[" << name << ",focus1=" << focus1 << ",focus2=" << focus2 << "," << std::endl
00562   << "                   center=" << center << ",semimajor=" << semimajor << ",semiminor=" << semiminor << std::endl
00563   << "                   orientation=" << getOrientation() << "]";
00564   return os.str();
00565 }
00566 
00567 void EllipticalObstacle::loadXML(xmlNode* node) {
00568   // temporarily add plist entries for serialization
00569   plist::ArrayOf< plist::Primitive<fmat::fmatReal> > c(2,0,false);
00570   plist::Primitive<fmat::fmatReal> smajor;
00571   plist::Primitive<fmat::fmatReal> sminor;
00572   plist::Angle ori;
00573   addEntry("Center",c);
00574   addEntry("Orientation",ori);
00575   addEntry("Semimajor",smajor);
00576   addEntry("Semiminor",sminor);
00577   PlannerObstacle2D::loadXML(node);
00578   removeEntry("Center");
00579   removeEntry("Orientation");
00580   removeEntry("Semimajor");
00581   removeEntry("Semiminor");
00582   center.importFrom(c);
00583   reset(center,smajor,sminor,ori);
00584 }
00585 
00586 void EllipticalObstacle::saveXML(xmlNode * node) const {
00587   // temporarily add plist entries to get 'normalized' coordinates, then convert
00588   plist::ArrayOf< plist::Primitive<fmat::fmatReal> > c(2,0,false);
00589   c.setSaveInlineStyle(true);
00590   center.exportTo(c);
00591   plist::Primitive<fmat::fmatReal> smajor = semimajor;
00592   plist::Primitive<fmat::fmatReal> sminor = semiminor;
00593   plist::Angle ori = getOrientation();
00594   plist::Dictionary tmp(*this);
00595   tmp.addEntry("Center",c);
00596   tmp.addEntry("Orientation",ori);
00597   tmp.addEntry("Semimajor",smajor);
00598   tmp.addEntry("Semiminor",sminor);
00599   tmp.saveXML(node);
00600 }
00601 
00602 void ConvexPolyObstacle::hull(const std::set<fmat::Column<2> >& p) {
00603   size_t k = 0;
00604   points.resize(p.size()+1);
00605   
00606   // Build lower hull
00607   for(std::set<fmat::Column<2> >::const_iterator it=p.begin(); it!=p.end(); ++it) {
00608     while(k>=2 && fmat::crossProduct(points[k-1]-points[k-2], *it-points[k-2])[2] <= 0)
00609       --k;
00610     points[k++] = *it;
00611   }
00612   
00613   // Build upper hull
00614   const size_t t = k+1;
00615   std::set<fmat::Column<2> >::const_reverse_iterator it=p.rbegin();
00616   for(++it; it!=p.rend(); ++it) {
00617     while(k >= t && fmat::crossProduct(points[k-1]-points[k-2], *it-points[k-2])[2] <= 0)
00618       --k;
00619     points[k++] = *it;
00620   }
00621   
00622   // update normals
00623   normals.resize(k-1);
00624   for(size_t i=0; i<k-1; ++i) {
00625     fmat::Column<2> d = points[i+1] - points[i];
00626     // this is why we specify counter-clockwise... right normal is outside
00627     normals[i] = (fmat::pack(d[1],-d[0]) / d.norm());
00628   }
00629   points.resize(k-1);
00630 }
00631 
00632 void ConvexPolyObstacle::addPoint(const fmat::Column<2>& p) {
00633   if(points.size()>0) {
00634     fmat::Column<2> d = p - points.back();
00635     // this is why we specify counter-clockwise... right normal is outside
00636     d = fmat::pack(d[1],-d[0]) / d.norm();
00637     if(normals.size()==points.size())
00638       normals.back() = d;
00639     else
00640       normals.push_back(d);
00641   }
00642   points.push_back(p);
00643 }
00644 
00645 void ConvexPolyObstacle::close() {
00646   ASSERTRET(points.size()==normals.size()+1,"ConvexPolyObstacle already closed: " << points.size() << " points " << normals.size() << " normals");
00647   fmat::Column<2> d = points.front() - points.back();
00648   // this is why we specify counter-clockwise... right normal is outside
00649   d = fmat::pack(d[1],-d[0]) / d.norm();
00650   normals.push_back(d);
00651 }
00652 
00653 bool ConvexPolyObstacle::collides(const fmat::SubVector<2,const fmat::fmatReal>& point) const {
00654   ASSERTRETVAL(points.size()>=3,"ConvexPolyObstacle: not enough points (" << points.size() << ", need at least 3)",false);
00655   ASSERTRETVAL(points.size()==normals.size(),"ConvexPolyObstacle not closed",false);
00656   for(size_t i=0; i<points.size(); ++i) {
00657     if(fmat::dotProduct(point-points[i],normals[i]) >= 0)
00658       return false;
00659   }
00660   return true;
00661 }
00662 
00663 bool ConvexPolyObstacle::collides(const RectangularObstacle& other) const {
00664   ASSERTRETVAL(points.size()>=3,"ConvexPolyObstacle: not enough points (" << points.size() << ", need at least 3)",false);
00665   ASSERTRETVAL(points.size()==normals.size(),"ConvexPolyObstacle not closed",false);
00666   for(size_t i=0; i<points.size(); ++i) {
00667     fmat::Matrix<4,2> c(other.points);
00668     c.column(0)-=points[i][0];
00669     c.column(1)-=points[i][1];
00670     fmat::Column<4> d = c * normals[i];
00671     if(d.min() >= 0)
00672       return false;
00673   }
00674   fmat::Column<2> p = other.unrot * points[0];
00675   fmat::fmatReal minX=p[0],maxX=p[0],minY=p[1],maxY=p[1];
00676   for(size_t i=1; i<points.size(); ++i) {
00677     p = other.unrot * points[i];
00678     if(p[0]<minX)
00679       minX=p[0];
00680     else if(maxX<p[0])
00681       maxX=p[0];
00682     if(p[1]<minY)
00683       minY=p[1];
00684     else if(maxY<p[1])
00685       maxY=p[1];
00686   }
00687   return (other.minEx[0]<maxX && minX<other.maxEx[0] && other.minEx[1]<maxY && minY<other.maxEx[1]);
00688 }
00689 
00690 bool ConvexPolyObstacle::collides(const CircularObstacle& other) const {
00691   ASSERTRETVAL(points.size()>=3,"ConvexPolyObstacle: not enough points (" << points.size() << ", need at least 3)",false);
00692   ASSERTRETVAL(points.size()==normals.size(),"ConvexPolyObstacle not closed",false);
00693   const fmat::fmatReal r2 = other.getRadius() * other.getRadius();
00694   bool outside = false; // once we see we are outside, must 'hit' corner or edge (see final return)
00695   bool testedLast = false; // prevents duplicate corner tests on consecutive edges (not a big issue though)
00696   for(size_t i=0; i<points.size(); ++i) {
00697     fmat::Column<2> v = other.getCenter()-points[i];
00698     fmat::fmatReal d = fmat::dotProduct(v,normals[i]);
00699     if(d >= other.getRadius())
00700       return false;
00701     if(d >= 0) {
00702       outside = true;
00703       // close... now project onto the edge
00704       d = v[0]*-normals[i][1] + v[1]*normals[i][0]; // reuse the normal, just flip it
00705       // see if it hits either endpoint or the edge itself
00706       if(d >= 0) {
00707         const fmat::Column<2>& next = (i+1==points.size() ? points[0] : points[i+1]);
00708         fmat::fmatReal len = (points[i] - next).norm();
00709         if(d <= len) // within edge itself
00710           return true;
00711         if(testedLast) {
00712           testedLast=false;
00713         } else if(d < len+other.getRadius()) {
00714           // may hit next corner point
00715           if((other.getCenter() - next).sumSq() < r2)
00716             return true;
00717           testedLast=true;
00718         }
00719       } else {
00720         if(d > -other.getRadius()) {
00721           // may hit this corner point
00722           if(v.sumSq() < r2)
00723             return true;
00724         }
00725         testedLast=false;
00726       }
00727     }
00728   }
00729   return !outside;
00730 }
00731 
00732 bool ConvexPolyObstacle::collides(const ConvexPolyObstacle& other) const {
00733   ASSERTRETVAL(points.size()>=3,"ConvexPolyObstacle: not enough points (" << points.size() << ", need at least 3)",false);
00734   ASSERTRETVAL(points.size()==normals.size(),"ConvexPolyObstacle not closed",false);
00735   ASSERTRETVAL(other.points.size()==other.normals.size(),"ConvexPolyObstacle not closed",false);
00736   for(size_t i=0; i<points.size(); ++i) {
00737     bool inter=false;
00738     for(size_t j=0; j<other.points.size(); ++j) {
00739       if(fmat::dotProduct(other.points[j]-points[i],normals[i]) < 0) {
00740         inter=true;
00741         break;
00742       }
00743     }
00744     if(!inter)
00745       return false;
00746   }
00747   for(size_t i=0; i<other.points.size(); ++i) {
00748     bool inter=false;
00749     for(size_t j=0; j<points.size(); ++j) {
00750       if(fmat::dotProduct(points[j]-other.points[i],other.normals[i]) < 0) {
00751         inter=true;
00752         break;
00753       }
00754     }
00755     if(!inter)
00756       return false;
00757   }
00758   return true;
00759 }
00760 
00761 fmat::Column<2> ConvexPolyObstacle::getSupport(const fmat::SubVector<2,const fmat::fmatReal>& direction) const {
00762   ASSERTRETVAL(points.size()>0,"ConvexPolyObstacle: no points",fmat::pack(0,0));
00763   fmat::fmatReal max = fmat::dotProduct(direction, points[0]);
00764   int maxIndex = 0;
00765   for (unsigned int i = 1; i < points.size(); i++) {
00766     fmat::fmatReal newMax = fmat::dotProduct(direction, points[i]);
00767     if (newMax > max) {
00768       max = newMax;
00769       maxIndex = i;
00770     }
00771   }
00772   return points[maxIndex];
00773 }
00774 
00775 fmat::Column<2> ConvexPolyObstacle::getCenter() const {
00776   ASSERTRETVAL(points.size()>0,"ConvexPolyObstacle empty (" << points.size() << ", need at least 1 for getCenter())",fmat::Column<2>());
00777   fmat::Column<2> ans=points.front();
00778   for(size_t i=1; i<points.size(); ++i) {
00779     ans+=points[i];
00780   }
00781   return ans/points.size();
00782 }
00783 
00784 void ConvexPolyObstacle::rotate(const fmat::SubVector<2,const fmat::fmatReal>& origin,
00785                 const fmat::SubMatrix<2,2,const fmat::fmatReal>& rot) {
00786   std::vector<fmat::Column<2> > tmpPoints = points;
00787   clear();
00788   for(size_t i=0; i<tmpPoints.size(); ++i) {
00789     addPoint(rot*(tmpPoints[i] - origin) + origin);
00790   }
00791   close();
00792 }
00793 
00794 BoundingBox2D ConvexPolyObstacle::getBoundingBox() const {
00795   BoundingBox2D bb;
00796   for(size_t i=0; i<points.size(); ++i)
00797     bb.expand(points[i]);
00798   return bb;
00799 }
00800 
00801 void ConvexPolyObstacle::offset(const fmat::Column<2>& off) {
00802   for(size_t i=0; i<points.size(); ++i) {
00803     points[i]+=off;
00804   }
00805 }
00806 
00807 fmat::Column<2> ConvexPolyObstacle::gradient(const fmat::SubVector<2,const fmat::fmatReal>& pt) const {
00808   ASSERTRETVAL(points.size()>=3,"ConvexPolyObstacle: not enough points (" << points.size() << ", need at least 3)",fmat::Column<2>());
00809   ASSERTRETVAL(points.size()==normals.size(),"ConvexPolyObstacle not closed",fmat::Column<2>());
00810   fmat::fmatReal closest2=std::numeric_limits<fmat::fmatReal>::infinity();
00811   fmat::Column<2> ans;
00812   bool testedLast = false; // prevents duplicate corner tests on consecutive edges (not a big issue though)
00813   for(size_t i=0; i<points.size(); ++i) {
00814     fmat::Column<2> v = pt-points[i];
00815     // project onto the edge
00816     const fmat::fmatReal d = v[0]*-normals[i][1] + v[1]*normals[i][0]; // reuse the normal, just flip it
00817     // is it closer to an endpoint or the edge itself
00818     if(d >= 0) {
00819       const fmat::Column<2>& next = (i+1==points.size() ? points[0] : points[i+1]);
00820       fmat::fmatReal len = (points[i] - next).norm();
00821       if(d <= len) {
00822         fmat::fmatReal n = fmat::dotProduct(v,normals[i]);
00823         fmat::fmatReal n2 = n*n;
00824         if(n2<closest2) {
00825           closest2 = n2;
00826           ans = normals[i]*-n;
00827         }
00828       }
00829       if(testedLast) {
00830         testedLast=false;
00831       } else if(d < len) {
00832         // test corner point
00833         fmat::fmatReal n2 = (next - pt).sumSq();
00834         if(n2 < closest2) {
00835           closest2 = n2;
00836           ans = next - pt;
00837         }
00838         testedLast=true;
00839       }
00840     } else {
00841       // may hit this corner point
00842       fmat::fmatReal n2 = v.sumSq();
00843       if(n2 < closest2) {
00844         closest2 = n2;
00845         ans = -v;
00846       }
00847       testedLast=false;
00848     }
00849   }
00850   return ans;
00851 }
00852 
00853 std::string ConvexPolyObstacle::toString() const {
00854   ostringstream os;
00855   os << "ConvexPolyObstacle[" << name << ",#points=" << points.size() << ",#normals=" << normals.size() << "]";
00856   return os.str();
00857 }
00858 
00859 
00860 void ConvexPolyObstacle::loadXML(xmlNode* node) {
00861   // temporarily add plist entries for serialization
00862   plist::ArrayOf< plist::Point > ps;
00863   addEntry("Points",ps);
00864   PlannerObstacle2D::loadXML(node);
00865   removeEntry("Points");
00866   points.resize(ps.size());
00867   for(size_t i=0; i<ps.size(); ++i)
00868     points[i].importFrom(ps[i]);
00869 }
00870 
00871 void ConvexPolyObstacle::saveXML(xmlNode * node) const {
00872   // temporarily add plist entries to get 'normalized' coordinates, then convert
00873   plist::ArrayOf< plist::Point > ps;
00874   for(size_t i=0; i<ps.size(); ++i) {
00875     points[i].exportTo(ps[i]);
00876     ps.removeEntry(2); // only two dimensions
00877   }
00878   plist::Dictionary tmp(*this);
00879   tmp.addEntry("Points",ps);
00880   tmp.saveXML(node);
00881 }
00882 
00883 void HierarchicalObstacle::recalculateBoundingBox() {
00884   aabb = BoundingBox2D();
00885   
00886   if (obstacles.empty())
00887     return;
00888   
00889   for (std::vector<PlannerObstacle2D*>::iterator it = obstacles.begin(); it != obstacles.end(); ++it)
00890     expandBoundingBox(**it);
00891 }
00892 
00893 void HierarchicalObstacle::expandBoundingBox(PlannerObstacle2D& other) {
00894   other.rotate(fmat::pack(0,0), rotation);
00895   fmat::Column<2> oldCenter = other.getCenter();
00896   other.updatePosition(oldCenter+getCenter());
00897   
00898   aabb.expand(other.getBoundingBox());
00899   
00900   other.updatePosition(oldCenter);
00901   other.rotate(fmat::pack(0,0), rotation.transpose());
00902 }
00903 
00904 bool HierarchicalObstacle::collides(const fmat::SubVector<2,const fmat::fmatReal>& point) const {
00905   // if we're not inside the box, no collision
00906   if( !aabb.collides(point) )
00907     return false;
00908   
00909   // rotate point into Hierarchical Obstacle frame
00910   const fmat::Column<2> newPt = rotation.transpose() * (point - center);
00911   
00912   // if we are, test further
00913   for (unsigned int i = 0; i < obstacles.size(); i++) {
00914     if (obstacles[i]->collides(newPt))
00915       return true;
00916   }
00917   return false;
00918 }
00919 
00920 fmat::Column<2> HierarchicalObstacle::getSupport(const fmat::SubVector<2,const fmat::fmatReal>& direction) const {
00921   fmat::Column<2> dir = rotation.transpose() * direction;
00922   fmat::Column<2> maxSupport = obstacles[0]->getSupport(dir);
00923   fmat::fmatReal max = fmat::dotProduct(maxSupport, dir);
00924   for (unsigned int i = 1; i < obstacles.size(); i++) {
00925     fmat::Column<2> newSupport = obstacles[i]->getSupport(dir);
00926     fmat::fmatReal newMax = fmat::dotProduct(newSupport, dir);
00927     if (newMax > max) {
00928       maxSupport = newSupport;
00929       max = newMax;
00930     }
00931   }
00932   return rotation * maxSupport + center;
00933 }
00934 
00935 void HierarchicalObstacle::updatePosition(const fmat::SubVector<2,const fmat::fmatReal>& newPos) {
00936   fmat::Column<2> diff = newPos - getCenter();
00937   aabb.min += diff;
00938   aabb.max += diff;
00939   center = newPos;
00940 }
00941 
00942 void HierarchicalObstacle::rotate(const fmat::SubVector<2,const fmat::fmatReal>& origin,
00943                   const fmat::SubMatrix<2,2,const fmat::fmatReal>& rot) {
00944   rotation = rotation * rot;
00945   center = rot * (getCenter() - origin) + origin;
00946   recalculateBoundingBox();
00947 }
00948 
00949 fmat::Column<2> HierarchicalObstacle::gradient(const fmat::SubVector<2,const fmat::fmatReal>& pt) const {
00950   fmat::Column<2> transformedPt = rotation.transpose() * (pt - getCenter());
00951   ASSERTRETVAL(obstacles.size()>0,"HierarchicalObstacle: no obstacles, can't determine gradient",fmat::Column<2>());
00952   fmat::Column<2> minGradient;
00953   bool minSet = false;
00954   for (unsigned int i = 0; i < obstacles.size(); ++i) {
00955     fmat::Column<2> newGradient = obstacles[i]->gradient(transformedPt);
00956     bool collision = false;
00957     for (unsigned int j = 0; j < obstacles.size(); ++j) {
00958       if (i == j) continue;
00959       if (obstacles[j]->collides(transformedPt+newGradient)) {
00960         collision = true;
00961         break;
00962       }
00963     }
00964     
00965     if (collision)
00966       continue;
00967     
00968     if (!minSet) {
00969       minGradient = newGradient;
00970       minSet = true;
00971     }
00972     else if (newGradient.sumSq() < minGradient.sumSq())
00973       minGradient = newGradient;
00974   }
00975   
00976   return rotation * minGradient;
00977 }
00978 
00979 std::string HierarchicalObstacle::toString() const {
00980   ostringstream os;
00981   os << "HierarchicalObstacle[" << name << ",#components=" << obstacles.size() << "]";
00982   return os.str();
00983 }
00984 
00985 void BoxObstacle::reset(fmat::Column<3> centerPoint,
00986             const fmat::SubVector<3,const fmat::fmatReal>& extents,
00987             const fmat::SubMatrix<3,3,const fmat::fmatReal>& rot) {
00988   center = centerPoint;
00989   fmat::Column<3> off = rot * extents;
00990   points.row(TOP_UPPER_RIGHT) = &(center + off)[0]; // 0
00991   points.row(BOTTOM_LOWER_LEFT) = &(center - off)[0]; // 6
00992   off = rot * fmat::pack(extents[0],-extents[1],extents[2]);
00993   points.row(TOP_LOWER_RIGHT) = &(center + off)[0]; // 1
00994   points.row(BOTTOM_UPPER_LEFT) = &(center - off)[0]; // 7
00995   off = rot * fmat::pack(-extents[0],-extents[1],extents[2]);
00996   points.row(TOP_LOWER_LEFT) = &(center + off)[0]; // 2
00997   points.row(BOTTOM_UPPER_RIGHT) = &(center - off)[0]; // 4
00998   off = rot * fmat::pack(-extents[0],extents[1],extents[2]);
00999   points.row(TOP_UPPER_LEFT) = &(center + off)[0]; // 3
01000   points.row(BOTTOM_LOWER_RIGHT) = &(center - off)[0]; // 5
01001   
01002   bBox.min = &points.minR()[0];
01003   bBox.max = &points.maxR()[0];
01004   
01005   unrot = rot.transpose(); // now switch to inverse
01006   centerPoint = unrot * centerPoint;
01007   maxEx = centerPoint + extents;
01008   minEx = centerPoint - extents;
01009 }
01010 
01011 void BoxObstacle::updatePosition(const fmat::SubVector<3, const fmat::fmatReal>& newPos) {
01012   fmat::Column<3> diff = newPos - getCenter();
01013   center = newPos;
01014   bBox.min += diff;
01015   bBox.max += diff;
01016   points.column(0)+=diff[0];
01017   points.column(1)+=diff[1];
01018   points.column(2)+=diff[2];
01019   
01020   diff = unrot * diff;
01021   minEx += diff;
01022   maxEx += diff;
01023 }
01024 
01025 void BoxObstacle::rotate(const fmat::SubVector<3,const fmat::fmatReal>& origin,
01026              const fmat::SubMatrix<3,3,const fmat::fmatReal>& rot) {
01027   fmat::Column<3> newCenter = rot * (getCenter() - origin) + origin;
01028   reset(newCenter, getExtents(), unrot.transpose() * rot);
01029 }
01030 
01031 std::string BoxObstacle::toString() const {
01032   ostringstream os;
01033   os << "BoxObstacle[" << name << ",points=" << points.fmt() << ",unrot=" << unrot.fmt() << "]";
01034   return os.str();
01035 }
01036 
01037 bool BoxObstacle::collides(const fmat::SubVector<3,const fmat::fmatReal>& point) const {
01038   const fmat::Column<3> p2 = unrot * point;
01039   return (minEx[0]<p2[0] && p2[0]<maxEx[0] &&
01040       minEx[1]<p2[1] && p2[1]<maxEx[1] &&
01041       minEx[2]<p2[2] && p2[2]<maxEx[2]);
01042 }
01043 
01044 fmat::Column<3> BoxObstacle::getSupport(const fmat::SubVector<3,const fmat::fmatReal>& direction) const {
01045   const fmat::Column<3> dir = unrot * direction;
01046   // choose the closest corner
01047   if (dir[0]>0) {
01048     if (dir[1]>0) {
01049       if (dir[2]>0) {
01050         return points.row(TOP_UPPER_RIGHT).transpose();
01051       } else {
01052         return points.row(BOTTOM_UPPER_RIGHT).transpose();
01053       }
01054     } else {
01055       if (dir[2]>0) {
01056         return points.row(TOP_LOWER_RIGHT).transpose();
01057       } else {
01058         return points.row(BOTTOM_LOWER_RIGHT).transpose();
01059       }
01060     }
01061   } else {
01062     if (dir[1]>0) {
01063       if (dir[2]>0) {
01064         return points.row(TOP_UPPER_LEFT).transpose();
01065       } else {
01066         return points.row(BOTTOM_UPPER_LEFT).transpose();
01067       }
01068     } else {
01069       if (dir[2]>0) {
01070         return points.row(TOP_LOWER_LEFT).transpose();
01071       } else {
01072         return points.row(BOTTOM_LOWER_LEFT).transpose();
01073       }
01074     }
01075   }
01076 }
01077 
01078 fmat::Column<3> BoxObstacle::gradient(const fmat::SubVector<3,const fmat::fmatReal>& pt) const {
01079   const fmat::Column<3> p = unrot * pt;
01080   
01081   if (p[0]<minEx[0]) { // left side - 9 cases
01082     if (p[1]<minEx[1]) {
01083       if (p[2]<minEx[2]) {
01084         return fmat::Column<3>(points.row(BOTTOM_LOWER_LEFT).transpose()) - pt;
01085       } else if (maxEx[2]<p[2]) {
01086         return fmat::Column<3>(points.row(TOP_LOWER_LEFT).transpose()) - pt;
01087       } else {
01088         return unrot.transpose() * fmat::pack(minEx[0]-p[0], minEx[1]-p[1], 0);
01089       }
01090     } else if (maxEx[1]<p[1]) {
01091       if (p[2]<minEx[2]) {
01092         return fmat::Column<3>(points.row(BOTTOM_UPPER_LEFT).transpose()) - pt;
01093       } else if (maxEx[2]<p[2]) {
01094         return fmat::Column<3>(points.row(TOP_UPPER_LEFT).transpose()) - pt;
01095       } else {
01096         return unrot.transpose() * fmat::pack(minEx[0]-p[0], maxEx[1]-p[1], 0);
01097       }
01098     } else {
01099       if (p[2]<minEx[2]) {
01100         return unrot.transpose() * fmat::pack(minEx[0]-p[0], 0, minEx[2]-p[2]);
01101       } else if (maxEx[2]<p[2]) {
01102         return unrot.transpose() * fmat::pack(minEx[0]-p[0], 0, maxEx[2]-p[2]);
01103       } else {
01104         return unrot.row(0).transpose() * (minEx[0]-p[0]);
01105       }
01106     }
01107   } else if (maxEx[0]<p[0]) { // right side - 9 cases
01108     if (p[1]<minEx[1]) {
01109       if (p[2]<minEx[2]) {
01110         return fmat::Column<3>(points.row(BOTTOM_LOWER_RIGHT).transpose()) - pt;
01111       } else if (maxEx[2]<p[2]) {
01112         return fmat::Column<3>(points.row(TOP_LOWER_RIGHT).transpose()) - pt;
01113       } else {
01114         return unrot.transpose() * fmat::pack(maxEx[0]-p[0], minEx[1]-p[1], 0);
01115       }
01116     } else if (maxEx[1]<p[1]) {
01117       if (p[2]<minEx[2]) {
01118         return fmat::Column<3>(points.row(BOTTOM_UPPER_RIGHT).transpose()) - pt;
01119       } else if (maxEx[2]<p[2]) {
01120         return fmat::Column<3>(points.row(TOP_UPPER_RIGHT).transpose()) - pt;
01121       } else {
01122         return unrot.transpose() * fmat::pack(maxEx[0]-p[0], maxEx[1]-p[1], 0);
01123       }
01124     } else {
01125       if (p[2]<minEx[2]) {
01126         return unrot.transpose() * fmat::pack(maxEx[0]-p[0], 0, minEx[2]-p[2]);
01127       } else if (maxEx[2]<p[2]) {
01128         return unrot.transpose() * fmat::pack(maxEx[0]-p[0], 0, maxEx[1]-p[1]);
01129       } else {
01130         return unrot.row(0).transpose() * (maxEx[0]-p[0]);
01131       }
01132     }
01133   } else if (p[1]<minEx[1]) { // lower - 3 cases
01134     if (p[2]<minEx[2]) {
01135       return unrot.transpose() * fmat::pack(0, minEx[1]-p[1], minEx[2]-p[2]);
01136     } else if (maxEx[2]<p[2]) {
01137       return unrot.transpose() * fmat::pack(0, minEx[1]-p[1], maxEx[2]-p[2]);
01138     } else {
01139       return unrot.row(1).transpose() * (minEx[1]-p[1]);
01140     }
01141   } else if (maxEx[1]<p[1]) { // upper - 3 cases
01142     if (p[2]<minEx[2]) {
01143       return unrot.transpose() * fmat::pack(0, maxEx[1]-p[1], minEx[2]-p[2]);
01144     } else if (maxEx[2]<p[2]) {
01145       return unrot.transpose() * fmat::pack(0, maxEx[1]-p[1], maxEx[2]-p[2]);
01146     } else {
01147       return unrot.row(1).transpose() * (maxEx[1]-p[1]);
01148     }
01149   } else if (p[2]<minEx[2]) { // below - 1 case
01150     return unrot.row(2).transpose() * (minEx[2]-p[2]);
01151   } else if (maxEx[2]<p[2]) { // above - 1 case
01152     return unrot.row(2).transpose() * (maxEx[2]-p[2]);
01153   } else { // inside
01154     fmatReal dist[6];
01155     dist[0] = p[0]-minEx[0]; // left
01156     dist[1] = p[1]-minEx[1]; // lower
01157     dist[2] = p[2]-minEx[2]; // below
01158     dist[3] = maxEx[0]-p[0]; // right
01159     dist[4] = maxEx[1]-p[1]; // upper
01160     dist[5] = maxEx[2]-p[2]; // above
01161     if (dist[0] < dist[1]) {
01162       if (dist[0] < dist[2]) {
01163         if (dist[0] < dist[3]) {
01164           if (dist[0] < dist[4]) {
01165             if (dist[0] < dist[5]) {
01166               return unrot.row(0).transpose() * (minEx[0]-p[0]); // left
01167             }
01168           } else {
01169             if (dist[4] < dist[5]) {
01170               return unrot.row(1).transpose() * (maxEx[1]-p[1]); // upper
01171             }
01172           }
01173         } else {
01174           if (dist[3] < dist[4]) {
01175             if (dist[3] < dist[5]) {
01176               return unrot.row(0).transpose() * (maxEx[0]-p[0]); // right
01177             }
01178           } else {
01179             if (dist[4] < dist[5]) {
01180               return unrot.row(1).transpose() * (maxEx[1]-p[1]); // upper
01181             }
01182           }
01183         }
01184       } else {
01185         if (dist[2] < dist[3]) {
01186           if (dist[2] < dist[4]) {
01187             if (dist[2] < dist[5]) {
01188               return unrot.row(2).transpose() * (minEx[2]-p[2]); // below
01189             }
01190           } else {
01191             if (dist[4] < dist[5]) {
01192               return unrot.row(1).transpose() * (maxEx[1]-p[1]); // upper
01193             }
01194           }
01195         } else {
01196           if (dist[3] < dist[4]) {
01197             if (dist[3] < dist[5]) {
01198               return unrot.row(0).transpose() * (maxEx[0]-p[0]); // right
01199             }
01200           } else {
01201             if (dist[4] < dist[5]) {
01202               return unrot.row(1).transpose() * (maxEx[1]-p[1]); // upper
01203             }
01204           }
01205         }
01206       }
01207     } else {
01208       if (dist[1] < dist[2]) {
01209         if (dist[1] < dist[3]) {
01210           if (dist[1] < dist[4]) {
01211             if (dist[1] < dist[5]) {
01212               return unrot.row(1).transpose() * (minEx[1]-p[1]); // lower
01213             }
01214           } else {
01215             if (dist[4] < dist[5]) {
01216               return unrot.row(1).transpose() * (maxEx[1]-p[1]); // upper
01217             }
01218           }
01219         } else {
01220           if (dist[3] < dist[4]) {
01221             if (dist[3] < dist[5]) {
01222               return unrot.row(0).transpose() * (maxEx[0]-p[0]); // right
01223             }
01224           } else {
01225             if (dist[4] < dist[5]) {
01226               return unrot.row(1).transpose() * (maxEx[1]-p[1]); // upper
01227             }
01228           }
01229         }
01230       } else {
01231         if (dist[2] < dist[3]) {
01232           if (dist[2] < dist[4]) {
01233             if (dist[2] < dist[4]) {
01234               return unrot.row(2).transpose() * (minEx[2]-p[2]); // below
01235             }
01236           } else {
01237             if (dist[4] < dist[5]) {
01238               return unrot.row(1).transpose() * (maxEx[1]-p[1]); // upper
01239             }
01240           }
01241         } else {
01242           if (dist[3] < dist[4]) {
01243             if (dist[3] < dist[5]) {
01244               return unrot.row(0).transpose() * (maxEx[0]-p[0]); // right
01245             }
01246           } else {
01247             if (dist[4] < dist[5]) {
01248               return unrot.row(1).transpose() * (maxEx[1]-p[1]); // upper
01249             }
01250           }
01251         }
01252       }
01253     }
01254     return unrot.row(2).transpose() * (maxEx[2]-p[2]); // above
01255   }
01256 }
01257 
01258 void BoxObstacle::bloat(float amount) {
01259   reset(getCenter(),maxEx+amount,unrot.transpose());
01260 }
01261 
01262 void BoxObstacle::contract(float amount) {
01263   bloat(-amount);
01264 }
01265 
01266 void BoxObstacle::loadXML(xmlNode* node) {
01267   // temporarily add plist entries to get 'normalized' coordinates, then convert
01268   plist::ArrayOf< plist::Primitive<fmat::fmatReal> > c(3,0,false);
01269   plist::ArrayOf< plist::Primitive<fmat::fmatReal> > d(3,0,false);
01270   plist::ArrayOf< plist::Primitive<fmat::fmatReal> > r(9,0,false);
01271   addEntry("Center",c);
01272   addEntry("Dimensions",d);
01273   addEntry("Orientation",r);
01274   PlannerObstacle3D::loadXML(node);
01275   removeEntry("Center");
01276   removeEntry("Dimensions");
01277   removeEntry("Orientation");
01278   fmat::Matrix<3,3> rot;
01279   reset(fmat::pack(c[0],c[1],c[2]), fmat::pack(d[0]/2,d[1]/2,d[2]/2), rot.importFromCMajor(r));
01280 }
01281 
01282 void BoxObstacle::saveXML(xmlNode * node) const {
01283   // temporarily add plist entries to get 'normalized' coordinates, then convert
01284   plist::ArrayOf< plist::Primitive<fmat::fmatReal> > c(3,0,false);
01285   getCenter().exportTo(c);
01286   c.setSaveInlineStyle(true);
01287   plist::ArrayOf< plist::Primitive<fmat::fmatReal> > d(3,0,false);
01288   d[0] = getLength();
01289   d[1] = getWidth();
01290   d[2] = getHeight();
01291   d.setSaveInlineStyle(true);
01292   plist::ArrayOf< plist::Primitive<fmat::fmatReal> > r(9,0,false);
01293   unrot.transpose().exportToCMajor(r);
01294   plist::Dictionary tmp(*this);
01295   tmp.addEntry("Center",c,"Center point");
01296   tmp.addEntry("Dimensions",d,"Width and height");
01297   tmp.addEntry("Orientation",r,"Rotation (3x3 Matrix condensed to float[9])");
01298   tmp.saveXML(node);
01299 }
01300 
01301 BoundingBox3D CylindricalObstacle::getBoundingBox() const { return BoundingBox3D(); }
01302 
01303 bool CylindricalObstacle::collides(const fmat::SubVector<3,const fmat::fmatReal>& point) const {
01304   fmat::Column<3> newPt = orientation.transpose() * point;
01305   if (std::abs(newPt[2]) < halfHeight && std::sqrt(newPt[0]*newPt[0] + newPt[1]*newPt[1]) < radius)
01306     return true;
01307   else
01308     return false;
01309 }
01310 
01311 fmat::Column<3> CylindricalObstacle::getSupport(const fmat::SubVector<3,const fmat::fmatReal>& direction) const {
01312   fmat::Column<3> dir = orientation.transpose() * direction;
01313   fmat::fmatReal sigma = std::sqrt(dir[0]*dir[0] + dir[1]*dir[1]);
01314   if (sigma > 0) {
01315     return orientation.transpose() *
01316     fmat::pack(radius / sigma * dir[0],
01317            radius / sigma * dir[1],
01318            sgn(dir[2]) * halfHeight);
01319   }
01320   else {
01321     return orientation.transpose() *
01322     fmat::pack(0, 0, sgn(dir[2]) * halfHeight);
01323   }
01324 }
01325 
01326 fmat::Column<3> CylindricalObstacle::gradient(const fmat::SubVector<3,const fmat::fmatReal>& pt) const { return fmat::Column<3>(); }
01327 
01328 void CylindricalObstacle::loadXML(xmlNode* node) {
01329   // temporarily add plist entries for serialization
01330   plist::ArrayOf< plist::Primitive<fmat::fmatReal> > c(3,0,false);
01331   plist::ArrayOf< plist::Primitive<fmat::fmatReal> > o(9,0,false);
01332   plist::Primitive<fmat::fmatReal> r;
01333   plist::Primitive<fmat::fmatReal> hh;
01334   addEntry("Center",c);
01335   addEntry("Orientation",o);
01336   addEntry("Radius",r);
01337   addEntry("HalfHeight",hh);
01338   PlannerObstacle3D::loadXML(node);
01339   removeEntry("Center");
01340   removeEntry("Orientation");
01341   removeEntry("Radius");
01342   removeEntry("HalfHeight");
01343   center.importFrom(c);
01344   radius = r;
01345   orientation.importFromCMajor(o);
01346   halfHeight = hh;
01347 }
01348 
01349 void CylindricalObstacle::saveXML(xmlNode * node) const {
01350   // temporarily add plist entries to get 'normalized' coordinates, then convert
01351   plist::ArrayOf< plist::Primitive<fmat::fmatReal> > c(3,0,false);
01352   c.setSaveInlineStyle(true);
01353   center.exportTo(c);
01354   plist::ArrayOf< plist::Primitive<fmat::fmatReal> > o(9,0,false);
01355   o.setSaveInlineStyle(true);
01356   orientation.exportToCMajor(o);
01357   plist::Primitive<fmat::fmatReal> r = radius;
01358   plist::Primitive<fmat::fmatReal> hh = halfHeight;
01359   plist::Dictionary tmp(*this);
01360   tmp.addEntry("Center",c);
01361   tmp.addEntry("Radius",r);
01362   tmp.addEntry("Orientation",o);
01363   tmp.addEntry("HalfHeight",hh);
01364   tmp.saveXML(node);
01365 }
01366 
01367 bool SphericalObstacle::collides(const fmat::SubVector<3,const fmat::fmatReal>& point) const {
01368   // point intersects if its distance from the center is less than the radius
01369   return (point - center).sumSq() <= radius * radius;
01370 }
01371 
01372 fmat::Column<3> SphericalObstacle::getSupport(const fmat::SubVector<3,const fmat::fmatReal>& direction) const {
01373   return center + (direction / direction.norm() * radius);
01374 }
01375 
01376 fmat::Column<3> SphericalObstacle::gradient(const fmat::SubVector<3,const fmat::fmatReal>& pt) const {
01377   fmat::Column<3> v = pt - center;
01378   fmat::fmatReal n = v.norm();
01379   if(n == 0)
01380     return fmat::pack(radius,0,0);
01381   return v * ((radius - n)/n);
01382 }
01383 
01384 void SphericalObstacle::loadXML(xmlNode* node) {
01385   // temporarily add plist entries for serialization
01386   plist::ArrayOf< plist::Primitive<fmat::fmatReal> > c(3,0,false);
01387   plist::Primitive<fmat::fmatReal> r;
01388   addEntry("Center",c);
01389   addEntry("Radius",r);
01390   PlannerObstacle3D::loadXML(node);
01391   removeEntry("Center");
01392   removeEntry("Radius");
01393   center.importFrom(c);
01394   radius = r;
01395 }
01396 
01397 void SphericalObstacle::saveXML(xmlNode * node) const {
01398   // temporarily add plist entries to get 'normalized' coordinates, then convert
01399   plist::ArrayOf< plist::Primitive<fmat::fmatReal> > c(3,0,false);
01400   c.setSaveInlineStyle(true);
01401   center.exportTo(c);
01402   plist::Primitive<fmat::fmatReal> r = radius;
01403   plist::Dictionary tmp(*this);
01404   tmp.addEntry("Center",c);
01405   tmp.addEntry("Radius",r);
01406   tmp.saveXML(node);
01407 }
01408 
01409 BoundingBox3D EllipsoidObstacle::getBoundingBox() const { return BoundingBox3D(); }
01410 
01411 bool EllipsoidObstacle::collides(const fmat::SubVector<3,const fmat::fmatReal>& point) const {
01412   fmat::Column<3> newPt = orientation.transpose() * point;
01413   
01414   // shrink ourselves to circle world, then test
01415   newPt[0] /= extents[0];
01416   newPt[1] /= extents[1];
01417   newPt[2] /= extents[2];
01418   if (newPt.sumSq() < 1)
01419     return true;
01420   else
01421     return false;
01422 }
01423 
01424 fmat::Column<3> EllipsoidObstacle::getSupport(const fmat::SubVector<3,const fmat::fmatReal>& direction) const {
01425   fmat::Column<3> dir = orientation.transpose() * direction;
01426   
01427   if (dir[0] == 0) {
01428     fmat::Column<2> s = get2Support(fmat::pack(dir[1],dir[2]));
01429     return orientation * fmat::pack(0, s[0], s[1]) + center;
01430   }
01431   else if (dir[1] == 0) {
01432     fmat::Column<2> s = get2Support(fmat::pack(dir[0],dir[2]));
01433     return orientation * fmat::pack(s[0], 0, s[1]) + center;
01434   }
01435   else if (dir[2] == 0) {
01436     fmat::Column<2> s = get2Support(fmat::pack(dir[0],dir[1]));
01437     return orientation * fmat::pack(s[0], s[1], 0) + center;
01438   }
01439   
01440   // convenience
01441   fmat::fmatReal a2 = extents[0]*extents[0], b2 = extents[1]*extents[1], c2 = extents[2]*extents[2];
01442   
01443   // solve for x (lagrange multiplier)
01444   fmat::fmatReal k1 = (dir[1]*b2)/(dir[0]*a2);
01445   fmat::fmatReal k2 = (dir[2]*c2)/(dir[0]*a2);
01446   fmat::fmatReal x = sgn(dir[0]) * std::sqrt(1/((1/(a2)) + (k1*k1/(b2)) + (k2*k2/(c2))));
01447   
01448   return orientation * fmat::pack(x, k1*x, k2*x) + center;
01449 }
01450 
01451 fmat::Column<2> EllipsoidObstacle::get2Support(const fmat::SubVector<2,const fmat::fmatReal>& direction) const {
01452   // if we're zero on either axis, we just use the extents of the other.
01453   if (direction[0] == 0) {
01454     return fmat::pack(0, sgn(direction[1]) * extents[1]);
01455   }
01456   else if (direction[1] == 0) {
01457     return fmat::pack(sgn(direction[0]) * extents[0], 0);
01458   }
01459   
01460   // convenience
01461   fmat::fmatReal a2 = extents[0]*extents[0], b2 = extents[1]*extents[1];
01462   
01463   // solve for x (lagrange multiplier)
01464   fmat::fmatReal k = (direction[1]*b2)/(direction[0]*a2);
01465   fmat::fmatReal x = sgn(direction[0]) * std::sqrt(1/((1/(a2)) + (k*k/(b2))));
01466   
01467   return fmat::pack(x, k*x);
01468 }
01469 
01470 fmat::Column<3> EllipsoidObstacle::gradient(const fmat::SubVector<3,const fmat::fmatReal>& pt) const { return fmat::Column<3>(); }
01471 
01472 void EllipsoidObstacle::loadXML(xmlNode* node) {
01473   // temporarily add plist entries for serialization
01474   plist::ArrayOf< plist::Primitive<fmat::fmatReal> > c(3,0,false);
01475   plist::ArrayOf< plist::Primitive<fmat::fmatReal> > o(9,0,false);
01476   plist::ArrayOf< plist::Primitive<fmat::fmatReal> > e(3,0,false);
01477   addEntry("Center",c);
01478   addEntry("Orientation",o);
01479   addEntry("Extents",e);
01480   PlannerObstacle3D::loadXML(node);
01481   removeEntry("Center");
01482   removeEntry("Orientation");
01483   removeEntry("Extents");
01484   center.importFrom(c);
01485   orientation.importFromCMajor(o);
01486   extents.importFrom(e);
01487 }
01488 
01489 void EllipsoidObstacle::saveXML(xmlNode * node) const {
01490   // temporarily add plist entries to get 'normalized' coordinates, then convert
01491   plist::ArrayOf< plist::Primitive<fmat::fmatReal> > c(3,0,false);
01492   c.setSaveInlineStyle(true);
01493   center.exportTo(c);
01494   plist::ArrayOf< plist::Primitive<fmat::fmatReal> > o(9,0,false);
01495   o.setSaveInlineStyle(true);
01496   orientation.exportToCMajor(o);
01497   plist::ArrayOf< plist::Primitive<fmat::fmatReal> > e(3,0,false);
01498   e.setSaveInlineStyle(true);
01499   extents.exportTo(e);
01500   plist::Dictionary tmp(*this);
01501   tmp.addEntry("Center",c);
01502   tmp.addEntry("Orietation",o);
01503   tmp.addEntry("Extents",e);
01504   tmp.saveXML(node);
01505 }

Tekkotsu v5.1CVS
Generated Fri Mar 16 05:26:46 2012 by Doxygen 1.6.3