Homepage Demos Overview Downloads Tutorials Reference
Credits

LineData.cc

Go to the documentation of this file.
00001 //-*-c++-*-
00002 #include <iostream>
00003 #include <math.h>
00004 #include <vector>
00005 #include <list>
00006 
00007 #include "Macrodefs.h"
00008 
00009 #include "SketchSpace.h"
00010 #include "Sketch.h"
00011 #include "Region.h"
00012 #include "visops.h"
00013 
00014 #include "ShapeSpace.h"
00015 #include "ShapeRoot.h"
00016 
00017 #include "PointData.h"
00018 #include "LineData.h"
00019 #include "ShapeLine.h"
00020 
00021 namespace DualCoding {
00022 
00023 DATASTUFF_CC(LineData);
00024 
00025 const Point LineData::origin_pt = Point(0,0);
00026 
00027 LineData::LineData(ShapeSpace& _space, const Point &p1, orientation_t orient)
00028   : BaseData(_space,getStaticType()), end1_pt(p1), end2_pt(), 
00029     rho_norm(0), theta_norm(0), orientation(0), length(0) {
00030   int const width = space->getDualSpace().getWidth();
00031   int const height = space->getDualSpace().getHeight();
00032   // Use a large offset from p1 to p2 because SketchGUI must calculate
00033   // the line slope from p1/p2 coords transmitted as strings; we don't
00034   // transmit the orientation.  But don't use an offset so large that the line
00035   // goes off-screen.
00036   float p2x=0, p2y=0;
00037   if ( fabs(orient-M_PI/2) < 0.001 ) {
00038     p2x = p1.coordX();
00039     p2y = p1.coordY() > height/2 ? 0 : height-1;
00040   } else {
00041     float slope = tan(orient);
00042     float intcpt = p1.coordY() - p1.coordX()*slope;
00043     p2x = p1.coordX() >= width/2 ? 0.0 : width-1;
00044     p2y = p2x * slope + intcpt;
00045     if ( p2y > height-1 ) {
00046       p2x = (height - intcpt) / slope;
00047       p2y = height;
00048     } else if ( p2y < 0 ) {
00049       p2x = -intcpt / slope;
00050       p2y = 0;
00051     }
00052   }
00053   end2_pt = Point(p2x,p2y);
00054   end1_pt.setValid(false);
00055   end1_pt.setActive(false);
00056   end2_pt.setValid(false);
00057   end2_pt.setActive(false);
00058   update_derived_properties();
00059 }
00060 
00061 Point LineData::getCentroid() const { return (end1Pt()+end2Pt())*0.5; }
00062 
00063 void LineData::setInfinite(bool value) {
00064   end1_pt.setActive(!value);
00065   end2_pt.setActive(!value);
00066 }
00067 
00068 #define NORMPOINT_MATCH_DISTSQ 3500
00069 #define LINE_MATCH_OVERLAP -10
00070 #define LINE_ORIENTATION_MATCH M_PI/6
00071 
00072 bool LineData::isMatchFor(const ShapeRoot& other) const {
00073   if (!(isSameTypeAs(other) && isSameColorAs(other)))
00074     return false;
00075   else {
00076     const Shape<LineData>& other_ln = ShapeRootTypeConst(other,LineData);
00077     return isMatchFor(other_ln.getData());
00078   }
00079 }
00080 
00081 bool LineData::isMatchFor(const LineData& other_line) const {
00082   float const px = rho_norm*cos(theta_norm), 
00083     py = rho_norm*sin(theta_norm);
00084   float const qx = other_line.rho_norm*cos(other_line.theta_norm),
00085     qy = other_line.rho_norm*sin(other_line.theta_norm);
00086   AngPi theta_diff = float(theta_norm) - float(other_line.theta_norm);
00087   if ( theta_diff > M_PI/2 )
00088     theta_diff = M_PI - theta_diff;
00089   float normpointdistsq = (px-qx)*(px-qx) + (py-qy)*(py-qy);
00090   // cout << "px=" << px << "  py=" << py << "  qx=" << qx << "  qy=" << qy << "  normpointdistsq=" << normpointdistsq 
00091   //  << "  theta_diff=" << theta_diff
00092   //   << "  perpdist=" << perpendicularDistanceFrom(other_line.getCentroid())
00093   //   << "  isoverlapped=" << isOverlappedWith(other_line,LINE_MATCH_OVERLAP) << endl;
00094   return normpointdistsq < NORMPOINT_MATCH_DISTSQ  // *** DST hack
00095     && theta_diff < LINE_ORIENTATION_MATCH
00096     && perpendicularDistanceFrom(other_line.getCentroid()) < 100
00097     && isOverlappedWith(other_line,LINE_MATCH_OVERLAP);
00098 }
00099 
00100   bool LineData::isOverlappedWith(const LineData& otherline, int amount) const {
00101   if ( firstPtCoord() <= otherline.firstPtCoord() )
00102     return secondPtCoord()-amount >= otherline.firstPtCoord();
00103   else
00104     return firstPtCoord()+amount <= otherline.secondPtCoord();
00105 }
00106 
00107 
00108 void LineData::mergeWith(const ShapeRoot& other) {
00109   const Shape<LineData>& other_line = ShapeRootTypeConst(other,LineData);
00110   if (other_line->confidence <= 0)
00111     return;
00112   const int other_conf = other_line->confidence;
00113   confidence += other_conf;
00114   end1_pt = (end1_pt*confidence + other_line->end1Pt()*other_conf) / (confidence+other_conf);
00115   end2_pt = (end2_pt*confidence + other_line->end2Pt()*other_conf) / (confidence+other_conf);
00116   update_derived_properties();
00117 }
00118 
00119 
00120 bool LineData::isValidUpdate(coordinate_t c1_cur, coordinate_t c2_cur, coordinate_t c1_new, coordinate_t c2_new) {
00121   const float c1_noise = 10.0 + fabs(c1_cur+c1_new) / 20.0; // allow larger error for shapes far from the robot
00122   const float c2_noise = 10.0 + fabs(c2_cur+c2_new) / 20.0;
00123   return (c1_new-c1_noise < c1_cur && c2_cur < c2_new+c2_noise);
00124 }
00125 
00126 
00127 //! Update a line in the local map with info from a matching line in the ground space.
00128 bool LineData::updateParams(const ShapeRoot& ground_root, bool force) {
00129   const Shape<LineData>& ground_line = ShapeRootTypeConst(ground_root,LineData);
00130   //  cout << "updating local Line " << getId() << " with data from ground line " << ground_line->getId() << ":" << endl;
00131   //  ground_line->printEnds();
00132   //  cout << "Update from " << endl;
00133   //  printEnds();
00134   
00135   const coordinate_t c1_cur = firstPtCoord();
00136   const coordinate_t c2_cur = secondPtCoord();
00137 
00138   Point _end1_pt = firstPt();
00139   Point _end2_pt = secondPt();
00140   
00141   updateLinePt(firstPt(), firstPtCoord(), firstPt(ground_line), firstPtCoord(ground_line), -1);
00142   updateLinePt(secondPt(), secondPtCoord(), secondPt(ground_line), secondPtCoord(ground_line), +1);
00143   //  cout << "to" << endl;
00144   //  printEnds();
00145   
00146   const coordinate_t c1_new = firstPtCoord();
00147   const coordinate_t c2_new = secondPtCoord();
00148   
00149   if (isValidUpdate(c1_cur, c2_cur, c1_new, c2_new) || force){
00150     //    cout << "was accepted, line updated" << endl;
00151     //    ++nsamples;
00152     update_derived_properties();
00153     return true;
00154   }
00155   
00156   //  cout << "was denied, line not updated" << endl;
00157   setEndPts(_end1_pt, _end2_pt);
00158   return false;
00159 }
00160 
00161 void LineData::updateLinePt(EndPoint& localPt, coordinate_t local_coord,
00162           const EndPoint& groundPt, coordinate_t ground_coord,
00163           int sign) {
00164   if ( groundPt.isValid() ) {
00165     if ( localPt.isValid() )
00166       localPt.updateParams(groundPt);
00167     else
00168       localPt = groundPt;
00169   }
00170   else if ( (ground_coord - local_coord)*sign > 0 )
00171     localPt = groundPt;
00172 }
00173 
00174 bool LineData::isAdmissible() const {
00175   if (end1Pt().isValid() && end2Pt().isValid())
00176     return length >= 70.0;  // *** DST hack: lines should be at least 70 mm long
00177   else
00178     return length >= 40.0;
00179 }
00180 
00181 //! Print information about this shape. (Virtual in BaseData.)
00182 void LineData::printParams() const {
00183   cout << "Type = " << getTypeName() << "  ID=" << getId() << "  ParentID=" << getParentId() << endl;
00184   cout << "end1{" << end1Pt().coordX() << ", " << end1Pt().coordY()  << "}";
00185   cout << " active=" << end1Pt().isActive();
00186   cout << " valid=" << end1Pt().isValid() << endl;
00187   
00188   cout << "end2{" << end2Pt().coordX() << ", " << end2Pt().coordY() <<  "}";
00189   cout << " active=" << end2Pt().isActive();
00190   cout << " valid=" << end2Pt().isValid() << std::endl;
00191   
00192   cout << "rho_norm=" << rho_norm  << "  theta_norm=" << theta_norm;
00193   cout << "  orientation=" << getOrientation() 
00194        << "  length=" << getLength() << endl;
00195   
00196   printf("color = %d %d %d\n",getColor().red,getColor().green,getColor().blue);
00197   
00198   cout << "  mobile=" << isMobile() << endl;
00199   cout << "  viewable=" << isViewable() << endl;
00200   
00201   vector<float> abc = lineEquation_abc();
00202   printf("equ = %f %f %f\n",abc[0],abc[1],abc[2]);
00203 }
00204 
00205 void LineData::printEnds() const {
00206   cout << "  end1{" << end1Pt().coordX() << ", " << end1Pt().coordY()  << "}";
00207   cout << "  active=" << end1Pt().isActive() << ", valid=" << end1Pt().isValid() << endl;
00208   cout << "  end2{" << end2Pt().coordX() << ", " << end2Pt().coordY() <<  "}";
00209   cout << "  active=" << end2Pt().isActive() << ", valid=" << end2Pt().isValid() << endl;
00210   // cout << endl;
00211 }
00212 
00213 
00214 
00215 //! Transformations.
00216 //@{
00217 //! Apply a transformation to this shape.
00218 void LineData::applyTransform(const NEWMAT::Matrix& Tmat) {
00219   end1Pt().applyTransform(Tmat);
00220   end2Pt().applyTransform(Tmat);
00221   update_derived_properties();
00222 }
00223 
00224 void LineData::projectToGround(const NEWMAT::Matrix& camToBase,
00225              const NEWMAT::ColumnVector& groundplane) {
00226   end1Pt().projectToGround(camToBase,groundplane);
00227   end2Pt().projectToGround(camToBase,groundplane);
00228   update_derived_properties();
00229 }
00230 
00231 //! Logical endpoints
00232 //@{
00233 
00234 EndPoint& LineData::leftPt() { return end1Pt().isLeftOf(end2Pt()) ? end1_pt : end2_pt; }
00235 EndPoint& LineData::rightPt() { return end1Pt().isLeftOf(end2Pt()) ? end2_pt : end1_pt; }
00236 EndPoint& LineData::topPt() { return end1Pt().isAbove(end2Pt()) ? end1_pt : end2_pt; }
00237 EndPoint& LineData::bottomPt() { return end1Pt().isAbove(end2Pt()) ? end2_pt : end1_pt; }
00238 
00239 Shape<PointData> LineData::leftPtShape() {
00240   Shape<PointData> result(new PointData(*space, leftPt()));
00241   result->setName("leftPt");
00242   result->inheritFrom(*this);
00243   result->setViewable(false);
00244   return result;
00245 }
00246 
00247 Shape<PointData> LineData::rightPtShape() {
00248   Shape<PointData> result(new PointData(*space, leftPt()));
00249   result->setName("rightPt");
00250   result->inheritFrom(*this);
00251   result->setViewable(false);
00252   return result;
00253 }
00254 
00255 Shape<PointData> LineData::topPtShape() {
00256   Shape<PointData> result(new PointData(*space, leftPt()));
00257   result->setName("topPt");
00258   result->inheritFrom(*this);
00259   result->setViewable(false);
00260   return result;
00261 }
00262 
00263 Shape<PointData> LineData::bottomPtShape() {
00264   Shape<PointData> result(new PointData(*space, leftPt()));
00265   result->setName("bottomPt");
00266   result->inheritFrom(*this);
00267   result->setViewable(false);
00268   return result;
00269 }
00270 
00271 EndPoint& LineData::firstPt() {
00272   if ( isNotVertical() )
00273     if ( end1Pt().coordX() < end2Pt().coordX() )
00274       return end1Pt();
00275     else return end2Pt();
00276   else
00277     if ( end1Pt().coordY() < end2Pt().coordY() )
00278       return end1Pt();
00279     else return end2Pt();
00280 }
00281     
00282 EndPoint& LineData::firstPt(const Shape<LineData> &otherline) const {
00283   if ( isNotVertical() )
00284     if ( otherline->end1Pt().coordX() < otherline->end2Pt().coordX() )
00285       return otherline->end1Pt();
00286     else return otherline->end2Pt();
00287   else
00288     if ( otherline->end1Pt().coordY() < otherline->end2Pt().coordY() )
00289       return otherline->end1Pt();
00290     else return otherline->end2Pt();
00291 }
00292     
00293 EndPoint& LineData::secondPt() {
00294   if ( isNotVertical() )
00295     if ( end1Pt().coordX() > end2Pt().coordX() )
00296       return end1Pt();
00297     else return end2Pt();
00298   else
00299     if ( end1Pt().coordY() > end2Pt().coordY() )
00300       return end1Pt();
00301     else return end2Pt();
00302 }
00303     
00304 EndPoint& LineData::secondPt(const Shape<LineData> &otherline) const {
00305   if ( isNotVertical() )
00306     if ( otherline->end1Pt().coordX() > otherline->end2Pt().coordX() )
00307       return otherline->end1Pt();
00308     else return otherline->end2Pt();
00309   else
00310     if ( otherline->end1Pt().coordY() > otherline->end2Pt().coordY() )
00311       return otherline->end1Pt();
00312     else return otherline->end2Pt();
00313 }
00314     
00315 coordinate_t LineData::firstPtCoord() const {
00316   return  isNotVertical() ?
00317     const_cast<LineData*>(this)->firstPt().coordX() :
00318     const_cast<LineData*>(this)->firstPt().coordY();
00319 }
00320 
00321 coordinate_t LineData::firstPtCoord(const Shape<LineData> &otherline) const {
00322   return  isNotVertical() ?
00323     firstPt(otherline).coordX() :
00324     firstPt(otherline).coordY();
00325 }
00326 
00327 coordinate_t LineData::secondPtCoord() const {
00328   return  isNotVertical() ?
00329     const_cast<LineData*>(this)->secondPt().coordX() :
00330     const_cast<LineData*>(this)->secondPt().coordY();
00331 }
00332 
00333 coordinate_t LineData::secondPtCoord(const Shape<LineData> &otherline) const {
00334   return  isNotVertical() ?
00335     secondPt(otherline).coordX() :
00336     secondPt(otherline).coordY();
00337 }
00338 
00339 //@}
00340 
00341 //! Functions to set endpoints.
00342 
00343 void LineData::setEndPts(const EndPoint& _end1_pt, const EndPoint& _end2_pt) {
00344   end1_pt.setCoords(_end1_pt);
00345   end1_pt.setActive(_end1_pt.isActive());
00346   end1_pt.setValid(_end1_pt.isValid());
00347   end1_pt.setNumUpdates(_end1_pt.numUpdates());
00348 
00349   end2_pt.setCoords(_end2_pt);
00350   end2_pt.setActive(_end2_pt.isActive());
00351   end2_pt.setValid(_end2_pt.isValid());
00352   end2_pt.setNumUpdates(_end2_pt.numUpdates());
00353 
00354   update_derived_properties();
00355 }
00356 
00357 
00358 //! Properties functions.
00359 //@{
00360 
00361 std::pair<float,float> LineData::lineEquation_mb() const {
00362   float m;
00363   if ((fabs(end2Pt().coordX() - end1Pt().coordX()) * BIG_SLOPE)
00364       <= fabs(end2Pt().coordY() - end2Pt().coordY()))
00365     m = BIG_SLOPE;
00366   else 
00367     m = (end2Pt().coordY() - end1Pt().coordY())/(end2Pt().coordX() - end1Pt().coordX());
00368   float b = end1Pt().coordY() - m * end1Pt().coordX();
00369   return pair<float,float>(m,b);
00370 }
00371 
00372 
00373 //! Determine parameters a, b, c, d satisfying the equation ax + bz = c.
00374 std::vector<float> LineData::lineEquation_abc_xz() const {
00375   float dx = end2Pt().coordX() - end1Pt().coordX();
00376   float dz = end2Pt().coordZ() - end1Pt().coordZ();
00377 
00378   std::vector<float> abc;
00379   abc.resize(3, 1.0);
00380   float& a = abc[0];
00381   float& b = abc[1];
00382   float& c = abc[2];
00383 
00384   // If vertical...b = 0
00385   if((dx == 0.0)
00386      || (dz/dx > BIG_SLOPE)) {
00387     a = 1.0;
00388     b = 0.0;
00389     c = end1Pt().coordX();
00390   }
00391   
00392   // If horizontal...a = 0
00393   else if((dz == 0.0)
00394     || (dx/dz > BIG_SLOPE)) {
00395     a = 0.0;
00396     b = 1.0;
00397     c = end1Pt().coordZ();
00398   }
00399   
00400   // If not horizontal or vertical...a = 1.0
00401   else {
00402     a = 1.0;
00403     b = (end1Pt().coordX() - end2Pt().coordX()) 
00404       / (end2Pt().coordZ() - end1Pt().coordZ());
00405     c = end1Pt().coordX() + b*end1Pt().coordZ();
00406   }
00407   
00408   return(abc);
00409 
00410 }
00411 //@}
00412 
00413   //! Determine parameters a, b, c satisfying the equation ax + by = c.
00414 std::vector<float> LineData::lineEquation_abc() const {
00415   float dx = end2Pt().coordX() - end1Pt().coordX();
00416   float dy = end2Pt().coordY() - end1Pt().coordY();
00417   
00418   std::vector<float> abc;
00419   abc.resize(3, 1.0);
00420   float& a = abc[0];
00421   float& b = abc[1];
00422   float& c = abc[2];
00423 
00424   // If vertical...b = 0
00425   if( fabs(dx) < 1.0e-6 || dy/dx > BIG_SLOPE) {
00426     a = 1.0;
00427     b = 0.0;
00428     c = end1Pt().coordX();
00429   }
00430   
00431   // If horizontal...a = 0
00432  else if ( fabs(dy) < 1.0e-6 || dx/dy > BIG_SLOPE ) {
00433     a = 0.0;
00434     b = 1.0;
00435     c = end1Pt().coordY();
00436   }
00437   
00438   // If not horizontal or vertical...a = 1.0
00439   else {
00440     a = 1.0;
00441     //    b = (end1Pt().coordX() - end2Pt().coordX()) 
00442     // / (end2Pt().coordY() - end1Pt().coordY());
00443     b = -dx / dy;
00444     c = end1Pt().coordX() + b*end1Pt().coordY();
00445   }
00446   
00447   return(abc);
00448 }
00449 //@}
00450 
00451 
00452 //! Functions to set values dealing with orientation.
00453 //@{
00454 void LineData::update_derived_properties() {
00455   rho_norm = perpendicularDistanceFrom(origin_pt);
00456   const vector<float> abc = lineEquation_abc();
00457   const float& a1 = abc[0];
00458   const float& b1 = abc[1];
00459   const float& c1 = abc[2];
00460   const float c1sign = (c1 >= 0) ? 1.0 : -1.0;
00461   theta_norm = atan2(b1*c1sign, a1*c1sign);
00462   orientation = theta_norm + AngPi(M_PI/2);
00463   length = end1Pt().distanceFrom(end2Pt());
00464   const ReferenceFrameType_t ref = getRefFrameType();
00465   end1_pt.setRefFrameType(ref);
00466   end2_pt.setRefFrameType(ref);
00467   deleteRendering();
00468 }
00469 
00470 bool LineData::isNotVertical() const {
00471   const AngPi threshold = M_PI / 3;
00472   const AngPi orient = getOrientation();
00473   return (orient <= threshold) || (orient >= M_PI - threshold);
00474 }
00475 
00476 /*
00477 bool LineData::isRoughlyPerpendicularTo(Shape<LineData>& other) {
00478   AngPi threshold = M_PI_4;
00479   AngPi orientation_diff = getOrientation() - other->getOrientation();
00480   if((orientation_diff >= threshold) && (orientation_diff < (M_PI-threshold)))
00481     return true;
00482   else
00483     return false;
00484 }
00485 
00486 bool LineData::isExactlyPerpendicularTo(Shape<LineData>& other) {
00487   AngPi orientation_diff = getOrientation() - other->getOrientation();
00488   return (orientation_diff == M_PI_2);
00489 }
00490 
00491 */
00492 
00493 
00494 //! These functions are true based on line length.
00495 //@{
00496 bool LineData::isLongerThan(const Shape<LineData>& other) const {
00497   return length > other->length; }
00498 
00499 bool LineData::isLongerThan(float ref_length) const {
00500   return length > ref_length; }
00501 
00502 bool LineData::isShorterThan(const Shape<LineData>& other) const {
00503   return length < other->length; }
00504 
00505 bool LineData::isShorterThan(float ref_length) const {
00506   return length < ref_length; }
00507 //@}
00508 
00509 bool LineData::isBetween(const Point &p, const LineData &other) const {
00510   if (getOrientation() == other.getOrientation()) { // parallel lines
00511     float dl = perpendicularDistanceFrom(other.end1Pt()); // distance between the lines
00512     return (perpendicularDistanceFrom(p) <= dl && other.perpendicularDistanceFrom(p) <= dl);
00513   }
00514   else {
00515     bool b;
00516     const LineData p_line (*space,  p,  // line from intersection of this and other to p
00517          intersectionWithLine(other, b, b));
00518     const AngPi theta_pline = p_line.getOrientation();
00519     const AngPi theta_greater = 
00520       (getOrientation() > other.getOrientation()) ? getOrientation() : other.getOrientation();
00521     const AngPi theta_smaller = 
00522       (getOrientation() < other.getOrientation()) ? getOrientation() : other.getOrientation();
00523     if (theta_greater - theta_smaller > M_PI/2)
00524       return (theta_pline >= theta_greater || theta_pline <= theta_smaller);
00525     else
00526       return (theta_pline <= theta_greater && theta_pline >= theta_smaller);
00527   }
00528 }
00529 
00530 //! Check intersection.
00531 //{
00532 bool
00533 LineData::intersectsLine(const Shape<LineData>& other) const {
00534   return intersectsLine(other.getData());
00535 }
00536 
00537 bool
00538 LineData::intersectsLine(const LineData& other) const {
00539   // Calculate F(x,y) = 0 for this line (x1,y1) to (x2,y2).
00540   pair<float,float> F = lineEquation_mb();
00541   
00542   // Calculate G(x,y) = 0 for L (x3,y3) to (x4,y4).
00543   pair<float,float> G = other.lineEquation_mb();
00544   
00545   // NOTE: These tests are assumed to be taking place in the space of
00546   // "this" line.  Therefore, the limits of line extent (for lines
00547   // with inactive endpoints) are calculated in the space of "this"
00548   // line.
00549   
00550   // JJW *** YOU NEED TO TAKE ACCOUNT OF END POINTS BEING TURNED OFF.
00551   
00552   //  float end1x, end1y, end2x, end2y, other_end1x, other_end1y, other_end2x, other_end2y;
00553   //  if(end1Pt().isActive()) {
00554   //    end1x = end1Pt().coordX();
00555   //    end1y = end1Pt().coordY();
00556   //  } else {
00557   //    end1x = 
00558   
00559   
00560   // TEST 1
00561   
00562   // Calculate r3 = F(x3,y3)
00563   float r3 = F.first * other.end1Pt().coordX() + F.second - other.end1Pt().coordY();
00564   
00565   // Calculate r4 = F(x4,y4)
00566   float r4 = F.first * other.end2Pt().coordX() + F.second - other.end2Pt().coordY();
00567   
00568   // If r3 != 0...
00569   // ...AND r4 != 0...
00570   // ...AND SGN(r3) == SGN(r4)...
00571   // ...THEN the lines do not intersect.
00572   
00573   if((r3 != 0)
00574      && (r4 != 0)
00575      && (SGN(r3) == SGN(r4))
00576      )
00577     return false;
00578   
00579   
00580   // TEST 2
00581   
00582   // Calculate r1 = G(x1,y1)
00583   float r1 = G.first * end1Pt().coordX() + G.second - end1Pt().coordY();
00584   
00585   // Calculate r2 = G(x2,y2)
00586   float r2 = G.first * end2Pt().coordX() + G.second - end2Pt().coordY(); 
00587   
00588   // If r1 != 0...
00589   // ...AND r2 != 0...
00590   // ...AND SGN(r1) == SGN(r2)...
00591   // ...THEN the lines do not intersect.
00592   
00593   if((r1 != 0)
00594      && (r2 != 0)
00595      && (SGN(r1) == SGN(r2))
00596      )
00597     return false;
00598   
00599   // Otherwise, the lines DO intersect.
00600   return true;
00601 }
00602 
00603 
00604 Point LineData::intersectionWithLine(const Shape<LineData>& other,
00605              bool& intersection_on_this,
00606              bool& intersection_on_other) const { 
00607   return intersectionWithLine(other.getData(), intersection_on_this,intersection_on_other); 
00608 }
00609 
00610 Point
00611 LineData::intersectionWithLine(const LineData& other, 
00612              bool& intersection_on_this, bool& intersection_on_other) const {
00613 
00614   // Based upon algorithm written by Paul Bourke, April 1989.
00615   // http://astronomy.swin.edu/~pbourke/geometry/lineline2d/  
00616   // Accessed July 20th 2004
00617   
00618   // Points 1 and 2 are on "this" line. Points 3 and 4 define the "other" line.
00619   //  float x1, x2, x3, x4, y1, y2, y3, y4;
00620   float x1, x2, x3, x4, y1, y2, y3, y4;
00621   x1 = end1Pt().coordX();
00622   x2 = end2Pt().coordX();
00623   x3 = other.end1Pt().coordX();
00624   x4 = other.end2Pt().coordX();
00625   // x3 = other->end1Pt().coordX();
00626   // x4 = other->end2Pt().coordX();
00627   y1 = end1Pt().coordY();
00628   y2 = end2Pt().coordY();
00629   y3 = other.end1Pt().coordY();
00630   y4 = other.end2Pt().coordY();
00631   // y3 = other->end1Pt().coordY();
00632   // y4 = other->end2Pt().coordY();
00633   
00634   // x1 + u_a(x2-x1) = x3 + u_b(x4-x3)
00635   // y1 + u_a(y2-y1) = y3 + u_b(y4-y3)
00636   
00637   // u_a = (x4-x3)(y1-y3) - (y4-y3)(x1-x3)
00638   //       -------------------------------
00639   //       (y4-y3)(x2-x1) - (x4-x3)(y2-y1)
00640   
00641   // u_b = (x2-x1)(y1-y3) - (y2-y1)(x1-x3)
00642   //       -------------------------------
00643   //       (y4-y3)(x2-x1) - (x4-x3)(y2-y1)
00644   
00645   float denom = ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1));
00646   float u_a_numerator = ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3));
00647   float u_b_numerator = ((x2-x1)*(y1-y3) - (y2-y1)*(x1-x3));
00648   
00649   // If denominators of u_a and u_b are zero, then lines are parallel.
00650   if(denom == 0.0) {
00651     if (u_a_numerator == 0.0 && u_b_numerator == 0.0) {
00652       PRINTF("intersectionWithLine: lines are coincident!\n");
00653       return(end1Pt());
00654     }
00655     else {
00656       cout << x1 << " " << x2 << " " << x3 << " " << x4 << " "
00657      << y1 << " " << y2 << " " << y3 << " " << y4 << endl;
00658       cout << "this theta; " << getOrientation() << ", other theta: " << other.getOrientation() << endl;
00659       PRINTF("ERROR in intersectionWithLine: lines are parallel!\n");
00660       return(Point(-9999.0,-99999.0));
00661     }
00662   }
00663   
00664   else {
00665     float u_a = u_a_numerator / denom;
00666     float u_b = u_b_numerator / denom;
00667     
00668     // If 0 <= u_a <=1  then intersection point is on segment a.
00669     if(0.0 <= u_a <=1.0)
00670       intersection_on_this = true;
00671     else
00672       intersection_on_this = false;
00673     
00674     // If 0 <= u_b <=1  then intersection point is on segment b.
00675     if(0.0 <= u_b <=1.0)
00676       intersection_on_other = true;
00677     else
00678       intersection_on_other = false;
00679     
00680     return(Point((x1+u_a*(x2-x1)),
00681      (y1+u_a*(y2-y1))));
00682   }
00683 }
00684 
00685 //! Distance.
00686 //{
00687 
00688 float LineData::perpendicularDistanceFrom(const Point& otherPt) const {
00689   // NOTE that this formula is rather slow, as it involves the
00690   // calculation of the line equation in addition to a square root here.
00691   
00692   // Using the formula from http://www.tpub.com/math2/8.htm
00693   vector<float> abc = lineEquation_abc();
00694   float& a = abc[0];
00695   float& b = abc[1];
00696   float& c = abc[2];
00697   
00698   // Distance...
00699   //    (x*A + y*B - C  )
00700   // abs(-------------  )
00701   //    (sqrt(a^2 + b^2)) 
00702   float d = fabs((a * otherPt.coordX() + b * otherPt.coordY() - c)/sqrt(a*a + b*b));
00703   //  cout << "abcd: " << a << ", " << b << ", " << c << ", " << d << endl;
00704   return(d);
00705 }
00706 
00707 //}
00708 
00709 
00710 // ==================================================
00711 // BEGIN LINE EXTRACTION CODE
00712 // ==================================================
00713 
00714 //!@name Line Extraction.
00715 //@{
00716 
00717 
00718 #define BEG_DIST_THRESH 2
00719 #define END_DIST_THRESH 2
00720 
00721 Shape<LineData> LineData::extractLine(Sketch<bool>& sketch) {
00722   NEW_SKETCH_N(fatmask,bool,visops::fillin(sketch,1,2,8));
00723   NEW_SKETCH_N(skel,bool,visops::skel(fatmask));
00724   return extractLine(skel, sketch);
00725 }
00726 
00727 Shape<LineData> LineData::extractLine(Sketch<bool>& skeleton, 
00728               const Sketch<bool>& occlusions) {
00729   const int width = skeleton->getWidth(), height = skeleton->getHeight();
00730   SketchSpace &SkS = skeleton->getSpace();
00731   ShapeSpace &ShS = SkS.getDualSpace();
00732 
00733   // approximate largest skel region with line
00734   NEW_SKETCH_N(labels,usint,visops::oldlabelcc(skeleton,visops::EightWayConnect));
00735   list<Region> regions = Region::extractRegions(labels,EXTRACT_LINE_MIN_AREA);
00736   if ( regions.empty() ) {
00737     ShapeRoot invalid; // need to define a named variable to avoid warning on next line
00738     return ShapeRootType(invalid,LineData);
00739   }
00740   Region skelchunk = regions.front();  // regions not empty, so use the largest....
00741   AngPi orientation(skelchunk.findPrincipalAxisOrientation());
00742   Point centroid(skelchunk.findCentroid());
00743   // cout << " orientation=" << orientation << "  centroid=" << centroid.getCoords() << endl;
00744   if (! skelchunk.isContained(centroid, 3) ) {   //  region does not look like a straight line
00745     Shape<LineData> result(splitLine(ShS, skelchunk, skeleton, occlusions));
00746     if ( result.isValid() )
00747       return result;
00748   }
00749   
00750   // Create a symbolic line object.
00751   Shape<LineData> extracted_line(ShS, centroid, orientation);
00752   extracted_line->setColor(skeleton->getColor());
00753   extracted_line->setParentId(skeleton->getViewableId());
00754 
00755   // rho and theta describe the normal line.
00756   // normpoint is where the normal line intersects with this line.
00757   // normpoint may actually be off-image.
00758   float x_normpoint = extracted_line->rho_norm*cos(extracted_line->theta_norm);
00759   float y_normpoint = extracted_line->rho_norm*sin(extracted_line->theta_norm);
00760   
00761   // m is slope of this line (not the normal line)
00762   // float m = (y_normpoint != 0) ? -(x_normpoint/y_normpoint) : BIG_SLOPE;
00763   float m = max(min(tan(extracted_line->theta_norm+AngPi(M_PI/2)), (float)BIG_SLOPE), (float)(-BIG_SLOPE));
00764   float b = y_normpoint - m*x_normpoint;
00765   // cout << "  x_normpoint=" << x_normpoint << "  y_normpoint=" << y_normpoint
00766   // << "  m=" << m << "  b=" << b <<std::endl;
00767   
00768   NEW_SKETCH_N(skelDist,usint,visops::edist(skeleton));
00769   if ( abs(m) <= 1 )  // when |slope| <= 1, scan along x, else scan along y
00770     extracted_line->scanHorizForEndPts(skelDist,occlusions,m,b);
00771   else
00772     extracted_line->scanVertForEndPts(skelDist,occlusions,m,b);
00773 
00774   // Check to see if any endpoints are near any edge of the screen.
00775   // If they are, invalidate them, assuming that line continues beyond screen.
00776   extracted_line->end1_pt.checkValidity(width,height,BEG_DIST_THRESH);
00777   extracted_line->end2_pt.checkValidity(width,height,BEG_DIST_THRESH);
00778 
00779   // Transform from SketchSpace coordinates to ShapeSpace coordinates
00780   SkS.applyTmatinv(extracted_line->end1_pt.getCoords());
00781   SkS.applyTmatinv(extracted_line->end2_pt.getCoords());
00782   extracted_line->update_derived_properties();
00783   return extracted_line;
00784 }
00785 
00786 
00787   // hack to split a non-straight region by kei
00788 Shape<LineData> LineData::splitLine(ShapeSpace &ShS, Region &skelchunk, 
00789         Sketch<bool> &skeleton, const Sketch<bool> &occlusions) {
00790   //  cout << "this region is not straight, needs be split into two regions" << endl;
00791   Point bounds[4] = {skelchunk.findTopBoundPoint(), skelchunk.findLeftBoundPoint(),
00792          skelchunk.findRightBoundPoint(), skelchunk.findBotBoundPoint()};
00793   //  cout << "top(0): " << bounds[0] << endl;
00794   //  cout << "left(1): " << bounds[1] << endl;
00795   //  cout << "right(2): " << bounds[2] << endl;
00796   //  cout << "bottom(3): " << bounds[3] << endl;
00797   for (int i = 0; i < 4; i++) {
00798     for (int j = i+1; j < 4; j++) {
00799       if (bounds[i].distanceFrom(bounds[j]) > 20 && ! skelchunk.isContained((bounds[i]+bounds[j])/2.0, 3)) {
00800   //  cout << "[" << i << "," << j << "] form a line from which most distant point is where the region should split " << endl;
00801   LineData ln(ShS,bounds[i],bounds[j]);
00802   PointData most_distant_pt(ShS, skelchunk.mostDistantPtFrom(ln));
00803   //  cout << "Point of split: " << most_distant_pt.getCentroid() << endl;
00804   const Sketch<bool>& point_rendering = most_distant_pt.getRendering();
00805   usint const clear_dist = 10;
00806   NEW_SKETCH_N(not_too_close, bool, visops::edist(point_rendering) >= clear_dist);
00807   skeleton &= not_too_close;
00808   return extractLine(skeleton, occlusions);
00809       }
00810     }
00811   }
00812   ShapeRoot invalid; // need to define a named variable to avoid warning on next line
00813   return ShapeRootType(invalid,LineData);
00814 }
00815 
00816 void LineData::scanHorizForEndPts(const Sketch<usint>& skelDist, 
00817           const Sketch<bool>& occlusions, 
00818           float m, float b) {
00819   // Scan along the infinite line, looking for segments in the image.
00820   bool on_line = false;        // true if tracing a line skeleton
00821   int beg_dist_thresh = BEG_DIST_THRESH;   // skel has to be <= this close to start segment
00822   int end_dist_thresh = END_DIST_THRESH;   // how far line should extend from skel
00823   int curxstart = -1;    // start of current segment we're examining
00824   int possxstop = -1;    // end of current segment unless we bridge a gap
00825   int bestxstart = -1;   // start of best (longest) segment seen so far
00826   int bestxstop = -1;    // end of best segment seen so far
00827   int bestlength = -1;   // length of best segment seen so far
00828   for (int x = 0, y, dist=0; x < skelDist.width; x++) {
00829     y = int(m*x+b);
00830     
00831     // If y is on-screen...
00832     if (y >= 0 && y < skelDist.height) {
00833       dist = skelDist(x,y);
00834 
00835       if (on_line == false) {   // not currently on a line segment
00836   if (dist <= beg_dist_thresh) {
00837     // start a new segment
00838     on_line = true;
00839     curxstart = x - dist;
00840     // if new segment begins at an occluder, back up over the occluder
00841     int curystart;
00842     bool backupflag = false;
00843     while ( curxstart >= 0 && (curystart=int(m*curxstart+b)) >= 0 && curystart < skelDist.height )
00844       if ( occlusions(curxstart,curystart) || skelDist(curxstart,curystart) == 0 ) {
00845         --curxstart;
00846         backupflag = true;
00847       } else { // if we backed up past the occluder, go one step forward
00848         curxstart += backupflag;
00849         break;
00850       }
00851     if ( curxstart < 0) // occluder extended to left edge of image
00852       curxstart = 0;
00853   }
00854       }
00855       else {   // on_line == true:  currently on a line segment
00856   const bool occ = occlusions(x,y);
00857   // cout << "x=" << x << "  dist=" << dist <<"  occ=" << occ;
00858   if ( dist <= end_dist_thresh || occ ) {
00859     if ( occ )
00860       possxstop = x;
00861     else
00862       possxstop = x - dist;
00863     // cout << "  possxstop=" << possxstop;
00864     if ( possxstop-curxstart > bestlength ) {
00865       bestxstart = curxstart;
00866       bestxstop = possxstop;
00867       bestlength = bestxstop-bestxstart;
00868       // cout << "  bestxstop=" << bestxstop << "  bestlength=" << bestlength;
00869     }
00870   }
00871   else if ( dist > extractorGapTolerance ) {
00872     // we're traversing a gap, and it just got too long
00873     on_line = false;
00874     // cout << "  on_line=" << on_line << "  dist=" << dist;
00875   };  
00876   // cout << endl;
00877       }
00878     }
00879   }
00880 
00881   // cout << "xstart:" << bestxstart << "xstop:" << bestxstop << endl;
00882   float y1 = m*bestxstart + b;
00883   float y2 = m*bestxstop + b;
00884   setEndPts(Point(bestxstart,y1), Point(bestxstop,y2));
00885   // cout << "m = " << m << "   b = " << b << endl;
00886   balanceEndPointHoriz(end1_pt,occlusions,m,b);
00887   balanceEndPointHoriz(end2_pt,occlusions,m,b);
00888 }
00889 
00890 void LineData::scanVertForEndPts(const Sketch<usint>& skelDist, 
00891           const Sketch<bool>& occlusions, 
00892           float m, float b) {
00893   // Scan along the infinite line, looking for segments in the image.
00894   bool on_line = false;        // true if tracing a line skeleton
00895   int beg_dist_thresh = BEG_DIST_THRESH;   // skel has to be <= this close to start segment
00896   int end_dist_thresh = END_DIST_THRESH;   // how far line should extend from skel
00897   int curystart = -1;    // start of current segment we're examining
00898   int possystop = -1;    // end of current segment unless we bridge a gap
00899   int bestystart = -1;   // start of best (longest) segment seen so far
00900   int bestystop = -1;    // end of best segment seen so far
00901   int bestlength = -1;   // length of best segment seen so far
00902   for (int x, y = 0, dist=0; y < skelDist.height; y++) {
00903     x = int((y-b)/m);
00904     
00905     // If x is on-screen...
00906     if (x >= 0 && x < skelDist.width) {
00907       dist = int(skelDist(x,y));
00908 
00909       if (on_line == false) {   // not currently on a line segment
00910   if (dist <= beg_dist_thresh) {
00911     // start a new segment
00912     on_line = true;
00913     curystart = y - dist;
00914     // if new segment begins at an occluder, back up over the occluder
00915     int curxstart;
00916     bool backupflag = false;
00917     while ( curystart >= 0 && (curxstart=int((curystart-b)/m)) >= 0 && curxstart < skelDist.width )
00918       if ( occlusions(curxstart,curystart) || skelDist(curxstart,curystart) == 0 ){
00919         --curystart;
00920         backupflag = true;
00921       } else { // if we backed up past the occluder, go one step forward
00922         curystart += backupflag;
00923         break;
00924       }
00925     if ( curystart < 0) // occluder extended to top edge of image
00926       curystart = 0;
00927   }
00928       }
00929       else {   // on_line == true:  currently on a line segment
00930   const bool occ = occlusions(x,y);
00931   // cout << "y=" << y << "  dist=" << dist <<"  occ=" << occ;
00932   if ( dist <= end_dist_thresh || occ ) {
00933     if ( occ )
00934       possystop = y;
00935     else
00936       possystop = y - dist;
00937     // cout << "  possystop=" << possystop;
00938     if ( possystop-curystart > bestlength ) {
00939       bestystart = curystart;
00940       bestystop = possystop;
00941       bestlength = bestystop-bestystart;
00942       // cout << "  bestystop=" << bestystop << "  bestlength=" << bestlength;
00943     }
00944   }
00945   else if ( dist > extractorGapTolerance ) {
00946     // we're traversing a gap, and it just got too long
00947     on_line = false;
00948     // cout << "  on_line=" << on_line;
00949   };  
00950   // cout << endl;
00951       }
00952     }
00953   }
00954 
00955   float x1 = (bestystart-b)/m;
00956   float x2 = (bestystop-b)/m;
00957   // cout << "x1=" << x1 << ", ystart=" << bestystart << "   x2=" << x2 << ", ystop=" << bestystop << endl;
00958   setEndPts(Point(x1,bestystart), Point(x2,bestystop));
00959   // cout << "m = " << m << "   b = " << b << endl;
00960   balanceEndPointVert(end1_pt,occlusions,m,b);
00961   balanceEndPointVert(end2_pt,occlusions,m,b);
00962 }
00963 
00964 void LineData::balanceEndPointHoriz(EndPoint &, Sketch<bool> const &, float, float) {
00965   /*
00966   int xstep = ( pt.coordX() < max(end1_pt.coordX(),end2_pt.coordX()) ? +1 : -1 );
00967   int toppix = 0, bottompix = 0;
00968   int const balance_rows = 5;    // check this many rows from the line endpoint inward
00969   int const row_samples = 10; // for each row, check this many pixels each side of the line
00970   */
00971   return;
00972 }
00973 
00974 void LineData::balanceEndPointVert(EndPoint &pt, Sketch<bool> const &occluders, float m, float b) {
00975   int ystep = ( pt.coordY() < max(end1_pt.coordY(),end2_pt.coordY()) ? +1 : -1 );
00976   int leftpix = 0, rightpix = 0;
00977   int const balance_rows = 8;    // check this many rows from the line endpoint inward
00978   int const row_samples = 10; // for each row, check this many pixels each side of the line
00979   // cout << endl << " ystep=" << ystep << " coords= ( " << pt.coordX() << " , " << pt.coordY() << ")"  << endl;
00980   for ( int y = (int)pt.coordY(), ycnt=balance_rows ;
00981   y>= 0 && y<occluders.height && ycnt-- > 0 ; y+=ystep ) {
00982     int const xstart = (int) ((y-b)/m);
00983     for ( int x = xstart-row_samples; x < xstart; x++ )
00984       if ( x >= 0 )
00985   leftpix += occluders(x,y);
00986     for ( int x = xstart+row_samples; x > xstart; x-- )
00987       if ( x < occluders.width )
00988   rightpix += occluders(x,y);
00989     // cout << "   " << xstart << "/" << y << "  =  " << leftpix << " " << rightpix << endl;;
00990   }
00991    float const new_x = pt.coordX() + (rightpix-leftpix)/(2*balance_rows);
00992    // cout << "    leftpix=" << leftpix <<"  rightpix=" << rightpix << endl;
00993    pt.setCoords(new_x, pt.coordY());
00994 }
00995 
00996 vector<Shape<LineData> > LineData::extractLines(Sketch<bool> const& sketch,
00997             const int num_lines) {
00998   NEW_SKETCH_N(fatmask,bool,visops::fillin(sketch,1,2,8));
00999   NEW_SKETCH_N(skel,bool,visops::skel(fatmask));
01000   return extractLines(skel, sketch, num_lines);
01001 }
01002 
01003 vector<Shape<LineData> > LineData::extractLines(Sketch<bool> const& skel,
01004             Sketch<bool> const& occluders,
01005             const int num_lines) {
01006   vector<Shape<LineData> > lines_vec;
01007   NEW_SKETCH_N(temp,bool,visops::copy(skel))
01008   for (int gloop = 0; gloop<num_lines; gloop++) {  // <---- HACK TO LIMIT NUMBER OF LINES RETURNED
01009     Shape<LineData> newline(extractLine(temp,occluders));
01010     if ( !newline.isValid() ) break;
01011     newline->clearLine(temp);
01012     if (newline->isLongerThan(DEFAULT_MIN_LENGTH)) {
01013       lines_vec.push_back(newline);
01014     }
01015   }
01016   // std::cout << "extractLines returning " << lines_vec.size() << " lines." << std::endl;
01017   
01018   return lines_vec;
01019 }
01020 
01021 //! Clear out pixels that are on or close to this line.
01022 void LineData::clearLine(Sketch<bool>& sketch) {
01023   const Sketch<bool>& line_rendering = getRendering();
01024   usint const clear_dist = 5;
01025   Sketch<bool> not_too_close = (visops::edist(line_rendering) >= clear_dist);
01026   sketch &= not_too_close;
01027 } 
01028 
01029 //@}
01030   
01031 
01032 // ==================================================
01033 // BEGIN LINE RENDERING CODE
01034 // ==================================================
01035 
01036 Sketch<bool>& LineData::getRendering() {
01037   if ( ! end1Pt().rendering_valid || ! end2Pt().rendering_valid )
01038     deleteRendering();
01039   if ( rendering_sketch == NULL )
01040     rendering_sketch = render();
01041   return *rendering_sketch;
01042 }
01043 
01044 //! Render line to SketchSpace and return reference.
01045 //! This function does not link the Sketch<bool>* in the shape to the sketch returned.
01046 Sketch<bool>* LineData::render() const {
01047   SketchSpace &renderspace = space->getDualSpace();
01048   int const width = renderspace.getWidth();
01049   int const height = renderspace.getHeight();
01050   float x1,y1,x2,y2;
01051   setDrawCoords(x1, y1, x2, y2, width, height);
01052   Sketch<bool>* draw_result = 
01053     new Sketch<bool>(renderspace, "render("+getName()+")");
01054   (*draw_result)->setParentId(getViewableId());
01055   (*draw_result)->setColor(getColor());
01056   *draw_result = 0;
01057   drawline2d(*draw_result, (int)x1, (int)y1, (int)x2, (int)y2);
01058   const_cast<LineData*>(this)->end1Pt().rendering_valid = true;
01059   const_cast<LineData*>(this)->end2Pt().rendering_valid = true;
01060   return draw_result;
01061 }  
01062  
01063   
01064 // This function will be called by both LineData and PolygonData renderers
01065 void LineData::setDrawCoords(float& x1,float& y1, float& x2, float& y2,
01066            const int width, const int height) const {
01067   EndPoint e1(end1Pt());
01068   EndPoint e2(end2Pt());
01069   space->getDualSpace().applyTmat(e1.getCoords());
01070   space->getDualSpace().applyTmat(e2.getCoords());
01071   const EndPoint &left_point = e1.coordX() <= e2.coordX() ? e1 : e2;
01072   const EndPoint &right_point = e1.coordX() <= e2.coordX() ? e2 : e1;
01073 
01074   // Check if horizontal
01075   if((int)left_point.coordY() == (int)right_point.coordY()) { 
01076     if (!left_point.isActive())
01077       x1 = 0;
01078     else x1 = max(0,(int)left_point.coordX());
01079     
01080     if (!right_point.isActive())
01081       x2 = width-1;
01082     else x2 = min(width-1, (int)right_point.coordX());
01083     
01084     y1 = (int)left_point.coordY();
01085     y2 = y1;
01086   } 
01087   else // Check if vertical...
01088     if ((int)left_point.coordX() == (int)right_point.coordX()) { 
01089     
01090     x1 = (int)left_point.coordX();
01091     x2 = x1;
01092     
01093     const EndPoint &top_point = end1Pt().coordY() <= end2Pt().coordY() ? end1Pt() : end2Pt();
01094     const EndPoint &bottom_point = end1Pt().coordY() <= end2Pt().coordY() ? end2Pt() : end1Pt();
01095 
01096     if(!top_point.isActive())
01097       y1 = 0;
01098     else y1 = max(0,(int)top_point.coordY());
01099     
01100     if (!bottom_point.isActive())
01101       y2 = height-1;
01102     else y2 = min(height-1,(int)bottom_point.coordY());
01103   } 
01104 
01105   else {   // Neither horizontal nor vertical...
01106     float m = (right_point.coordY()-left_point.coordY())/(right_point.coordX()-left_point.coordX());
01107     float b = left_point.coordY() - m*left_point.coordX();
01108     
01109     // find image edge intersections
01110     int i0x = (int)((0-b)/m);
01111     int ihx = (int)(((height-1)-b)/m);
01112     int i0y = (int)(m*0+b);
01113     int iwy = (int)(m*(width-1)+b);
01114     
01115     // If left point is active, set starting x and y accordingly.
01116     if(left_point.isActive()) {
01117       x1 = (int)left_point.coordX();
01118       y1 = (int)left_point.coordY();
01119     } 
01120     
01121     // If endpoint 1 extends past image edge...
01122     else { 
01123       
01124       // intersects left edge
01125       if(i0y >= 0 && i0y < height) { 
01126   x1 = 0;
01127   y1 = i0y;
01128       } 
01129       
01130       // intersects top or bottom edge
01131       else { 
01132   
01133   // intersects top first
01134   if (i0x < ihx) { 
01135     x1 = i0x;
01136     y1 = 0;
01137   } 
01138   
01139   // intersects bottom first
01140   else { 
01141     x1 = ihx;
01142     y1 = height-1;
01143   }
01144       }
01145     }
01146     
01147     // If right point is active, set starting x and y accordingly.
01148     if(right_point.isActive()) {
01149       x2 = (int)right_point.coordX();
01150       y2 = (int)right_point.coordY();
01151     } 
01152     else { // endpoint extends to image edge
01153       if(iwy >= 0 && iwy < height) { // intersects right edge
01154   x2 = width-1;
01155   y2 = iwy;
01156       } 
01157       else { // intersects top or bottom edge
01158   if (i0x > ihx) { // intersects top last 
01159     x2 = i0x;
01160     y2 = 0;
01161   } 
01162   else { // intersects bottom last
01163     x2 = ihx;
01164     y2 = height-1;
01165   }
01166       }
01167     }
01168   }
01169 }
01170 
01171 void LineData::drawline2d(Sketch<bool>& canvas, int x0, int y0, int x1, int y1) {
01172   int width = canvas->getWidth();
01173   int height = canvas->getHeight();
01174   
01175   // code below from free Graphics Gems repository, graphicsgems.org
01176   int d, x, y, ax, ay, sx, sy, dx, dy;
01177   dx = x1-x0;  ax = abs(dx)<<1;  sx = SGN(dx);
01178   dy = y1-y0;  ay = abs(dy)<<1;  sy = SGN(dy);
01179   
01180   x = x0;
01181   y = y0;
01182   if ( ax > ay ) {    /* x dominant */
01183     d = ay-(ax>>1);
01184     for (;;) {
01185       if (x >= 0 && y >= 0 && x < width && y < height)
01186   canvas(x,y) = true;
01187       
01188       if (x==x1)
01189   return;
01190       
01191       if ( d >= 0 ) {
01192   y += sy;
01193   d -= ax;
01194       }
01195       
01196       x += sx;
01197       d += ay;
01198     }
01199   }
01200   else {      /* y dominant */
01201     d = ax-(ay>>1);
01202     for (;;) {
01203       if (x >= 0 && y >= 0 && x < width && y < height)
01204   canvas(x,y) = true;
01205       
01206       if ( y == y1 )
01207   return;
01208       
01209       if ( d >= 0 ) {
01210   x += sx;
01211   d -= ay;
01212       }
01213       
01214       y += sy;
01215       d += ax;
01216     }
01217   }
01218 }
01219 //}
01220 
01221 
01222 std::vector<Shape<LineData> > LineData::houghTransform(const Sketch<bool>& fat, 
01223             const Sketch<bool>& skinny,
01224             const Sketch<bool>& occlusions,
01225             const size_t num_lines, int minLength)
01226 {
01227   std::vector<Shape<LineData> > result;
01228   ShapeSpace& ShS = fat->getSpace().getDualSpace();
01229 
01230   const int width = fat->getWidth(), height = fat->getHeight();
01231   const int numR = 120, numTheta = 120;
01232   const int numRf = 40, numThetaf = 40;
01233   int hsize = numR*numTheta, hsizef = numRf * numThetaf;
01234   int htab[hsize], hfine[hsizef]; // Hough accumulator table
01235   float minTheta = 0.0, maxTheta = 2*M_PI; //0.9999*M_PI;
01236   float minR = 0.000, maxR = sqrt((float)width*width+(float)height*height); //240.0;
01237   float thetaSpread = maxTheta - minTheta;
01238   float rSpread = maxR - minR;
01239   //float bestR = 0, bestTheta = 0;
01240 
01241   for (int i = 0; i < hsize; i++)
01242     htab[i] = 0; // zero accumulator table
01243 
01244   // build accumulator table
01245   float theta, r;
01246   int ridx;
01247   for (int x = 0; x < width; x++) {
01248     for (int y = 0; y < height; y++) {
01249       if (fat(x,y) == true) {
01250   for (int tidx = 0; tidx < numTheta ; tidx++) {
01251     theta = minTheta + tidx*thetaSpread/numTheta;
01252     r = (float)x*cos(theta)+(float)y*sin(theta);
01253     ridx = (int)((r-minR)*(float)numR/rSpread); // Is this right calc?
01254     if (ridx >= 0 && ridx < numR) // check ridx bounds
01255       htab[ridx+tidx*numR]++; 
01256   }
01257       }
01258     }
01259   }
01260 
01261 
01262   /*float lineLen = 500;
01263   for (int i = 0; i < numR*numTheta; i++) {
01264     if (htab[i] >= minCount)
01265       {
01266   const float curR = (i%numR)*rSpread/numR + minR;
01267   const float curTheta = (i/numR)*thetaSpread/numTheta + minTheta;
01268   const float x1 = curR*cos(curTheta), y1 = curR*sin(curTheta);
01269   const float x2 = x1+lineLen*cos(curTheta+M_PI/2), y2 = y1+lineLen*sin(curTheta+M_PI/2);
01270   const float x3 = x1-lineLen*cos(curTheta+M_PI/2), y3 = y1-lineLen*sin(curTheta+M_PI/2);
01271 
01272   Point e1(x3,y3), e2(x2,y2);
01273   Shape<LineData> line(ShS,e1,e2);
01274   line->end1Pt().setValid(false);
01275   line->end2Pt().setValid(false);
01276   result.push_back(line);
01277       }
01278       }*/
01279 
01280   // Finely explore the first n best lines in the rough table
01281   int max_val = -1, max_i = 0;
01282   while(result.size() < num_lines) {
01283 
01284     // find maximum value in accumulator table
01285     max_val = -1;
01286     max_i = 0;
01287     for (int i = 0; i < numR*numTheta; i++) {
01288       if (max_val < htab[i]) {
01289   max_val = htab[i];
01290   max_i = i;
01291       }
01292     }
01293 
01294     // Give up if the lines start getting too small
01295     if (max_val < minLength)
01296       break;
01297     
01298 
01299     float bestR = (max_i%numR)*rSpread/numR + minR;
01300     float bestTheta = (max_i/numR)*thetaSpread/numTheta + minTheta;
01301 
01302     // determine parameters for the fine hough iteration
01303     const float fthetaSpread = M_PI/40.0; 
01304     const float frSpread = maxR/40.0;
01305     float fminTheta = bestTheta-fthetaSpread/2.0;
01306     float fminR = bestR-frSpread/2.0;
01307 
01308     for (int i = 0; i < hsizef; i++)
01309       hfine[i] = 0; // zero fine table
01310     
01311     // build fine table
01312     float thetaf, rf;
01313     int ridxf;
01314     for (int x = 0; x < width; x++) {
01315       for (int y = 0; y < height; y++) {
01316   if (skinny(x,y) == true) {
01317     for (int tidx = 0; tidx < numThetaf ; tidx++) {
01318       thetaf = fminTheta + tidx*fthetaSpread/numThetaf;
01319       rf = (float)x*cos(thetaf)+(float)y*sin(thetaf);
01320       ridxf = (int)((rf-fminR)*(float)numRf/frSpread); // Is this right calc?
01321       if (ridxf >= 0 && ridxf < numRf) // check ridx bounds
01322         hfine[ridxf+tidx*numRf]++;  
01323     }
01324   }
01325       }
01326     }
01327 
01328     
01329     // Pull the best line out of the fine table
01330     int max_val_f = -1, max_i_f = 0;
01331     for (int i = 0; i < numRf*numThetaf; i++) {
01332       if (max_val_f < hfine[i]) {
01333   max_val_f = hfine[i];
01334   max_i_f = i;
01335       }
01336     }
01337     float bestRf = (max_i_f%numRf)*frSpread/numRf + fminR;
01338     float bestThetaf = (max_i_f/numRf)*fthetaSpread/numThetaf + fminTheta;
01339 
01340     // Add viable segments to the vector
01341     // Step along the line, looking for segments. 
01342     float lineLen = 500;
01343     // Can be simpler
01344     const float x1 = bestRf*cos(bestThetaf), y1 = bestRf*sin(bestThetaf);
01345     const float x2 = x1+lineLen*cos(bestThetaf+M_PI/2), y2 = y1+lineLen*sin(bestThetaf+M_PI/2);
01346     const float x3 = x1-lineLen*cos(bestThetaf+M_PI/2), y3 = y1-lineLen*sin(bestThetaf+M_PI/2);
01347     
01348     Point e1(x3,y3), e2(x2,y2);
01349     Shape<LineData> line(ShS,e1,e2);
01350 
01351 
01352     line->setColor(skinny->getColor());
01353     line->setParentId(skinny->getViewableId());
01354 
01355     // m is slope of this line (not the normal line)
01356     float m = (y1 != 0) ? -(x1/y1) : BIG_SLOPE;
01357     float b = y1 - m*x1;
01358     
01359     NEW_SKETCH_N(skelDist,usint,visops::edist(skinny));
01360     if ( abs(m) <= 1 )  // when |slope| <= 1, scan along x, else scan along y
01361       line->scanHorizForEndPts(skelDist,occlusions,m,b);
01362     else
01363       line->scanVertForEndPts(skelDist,occlusions,m,b);
01364     
01365     //! Check to see if any endpoints are near any edge of the screen.
01366     //! If they are, invalidate them, assuming that line continues beyond screen.
01367     line->end1_pt.checkValidity(width,height,BEG_DIST_THRESH);
01368     line->end2_pt.checkValidity(width,height,BEG_DIST_THRESH);
01369 
01370 
01371     // Experimental: check how much of the line actually lies along edge pixels
01372     // only include lines that are at least 50% above edge pixels. 
01373     
01374     float len = line->getLength();
01375     EndPoint start = line->end1_pt;
01376     EndPoint dx = (line->end2_pt - start)/len;
01377     float onCount = 0;
01378     for (float i=0; i<len; i++) {
01379       EndPoint cur = start+dx*i;
01380       if (skinny((int)(cur.coordX()), (int)(cur.coordY()))) {
01381   onCount++;
01382       }
01383     }
01384 
01385 
01386     std::cout<<"Line with "<<onCount<<" / "<<len<<" pixels";
01387     if (line->isLongerThan(minLength) && (onCount / len) > .5 )
01388       {
01389   std::cout<<" accepted";
01390   result.push_back(line);
01391       }
01392     std::cout<<std::endl;
01393 
01394 
01395     // Empty the corresponding bins in the rough hough table
01396     //std::cout<<max_i%numR<<","<<max_i/numR<<std::endl;
01397     for(int row = -5; row <= 5; row++)
01398       {
01399   for (int col = -(5-abs(row)); col <=5-abs(row); col++)
01400     {
01401       //int index = (max_i - (max_i%numR) + ((max_i+col)%numR)+row*numR + numR*numTheta)%(numR*numTheta);
01402       //std::cout<<index%numR<<","<<index/numR<<" ";
01403       htab[(max_i - (max_i%numR) + ((max_i+col)%numR)+row*numR + numR*numTheta)%(numR*numTheta)] = 0;
01404     }
01405   //std::cout<<std::endl;
01406       }
01407     //std::cout<<std::endl;
01408   }
01409   
01410   return result;
01411 }
01412 
01413 bool LineData::linesParallel(Shape<LineData> l1, Shape<LineData>l2)
01414 {
01415   const float maxDTheta = .15, minDThetaR = 10.0;
01416   float dTheta = l1->getOrientation() - l2->getOrientation(); 
01417   if (dTheta > M_PI_2)
01418     dTheta = dTheta - M_PI;
01419   if (abs(dTheta) < maxDTheta)
01420     {
01421       if (abs (100*dTheta + (l1->rho_norm - l2->rho_norm)) > minDThetaR)
01422   return true;
01423     }
01424   return false;
01425 }
01426 
01427 LineData& LineData::operator=(const LineData& other) {
01428   if (&other == this)
01429     return *this;
01430   BaseData::operator=(other);
01431   end1_pt = other.end1_pt;
01432   end2_pt = other.end2_pt;
01433   rho_norm = other.rho_norm;
01434   theta_norm = other.theta_norm;
01435   orientation = other.orientation;
01436   length = other.length;
01437   return *this;
01438 }
01439 
01440 
01441 // Compute if two points are on the same side of the line
01442 bool LineData::pointsOnSameSide(const Point& p1, const Point& p2)
01443 {
01444   float dx = end2_pt.coordX() - end1_pt.coordX();
01445   float dy = end2_pt.coordY() - end1_pt.coordY();
01446   
01447   float p1val = (p1.coordY() - end1_pt.coordY())*dx - (p1.coordX() - end1_pt.coordX())*dy;
01448   float p2val = (p2.coordY() - end1_pt.coordY())*dx - (p2.coordX() - end1_pt.coordX())*dy;
01449 
01450   return (p1val>0) == (p2val>0);
01451 }
01452 
01453 // Checks if the distance from the line to the point is less than 1 pixel,
01454 // and checks that the point is within the end points of the line.
01455 bool LineData::pointOnLine(const Point& p)
01456 {
01457   const float BOUNDS_EXTEND = 1.0;
01458   float dx = end2_pt.coordX() - end1_pt.coordX();
01459   float dy = end2_pt.coordY() - end1_pt.coordY();
01460   float val = (p.coordY() - end1_pt.coordY())*dx - (p.coordX() - end1_pt.coordX())*dy;
01461   val /= length;
01462   bool inBounds = (p.coordX() >= (leftPt().coordX() - BOUNDS_EXTEND)) &&
01463     (p.coordX() <= (rightPt().coordX() + BOUNDS_EXTEND)) &&
01464     (p.coordY() >= (topPt().coordY() - BOUNDS_EXTEND)) &&
01465     (p.coordY() <= (bottomPt().coordY() + BOUNDS_EXTEND));
01466   return (val > -1) && (val < 1) && inBounds;
01467 }
01468 
01469 
01470 // Comparison predicates
01471 
01472 bool LineData::LengthLessThan::operator() (const Shape<LineData> &line1, const Shape<LineData> &line2) const {
01473       return line1->getLength() < line2->getLength();
01474 }
01475 
01476 bool LineData::ParallelTest::operator() (const Shape<LineData> &line1, const Shape<LineData> &line2) const {
01477   return angdist(line1->getOrientation(),line2->getOrientation()) <= tolerance;
01478 }
01479 
01480 bool LineData::PerpendicularTest::operator() (const Shape<LineData> &line1, const Shape<LineData> &line2) const {
01481   return angdist(angdist(line1->getOrientation(),line2->getOrientation()), M_PI/2) <= tolerance;
01482 }
01483 
01484 bool LineData::ColinearTest::operator() (const Shape<LineData> &line1, const Shape<LineData> &line2) const {
01485   return ParallelTest(ang_tol)(line1,line2) && 
01486     abs( line1->getRhoNorm() - line2->getRhoNorm() ) <= dist_tol;
01487 }
01488 
01489 bool LineData::IsHorizontal::operator() (const Shape<LineData> &line) {
01490       const AngPi orient = line->getOrientation();
01491       return  (orient <= threshold) || (orient >= M_PI - threshold);
01492     }
01493 
01494 } // namespace

DualCoding 3.0beta
Generated Wed Oct 4 00:01:53 2006 by Doxygen 1.4.7