Main Page | Modules | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

BoundingBox.C

Go to the documentation of this file.
00001 /*---------------------------------------------------------------------+
00002  | Library QgarLib, graphics analysis and recognition                  |
00003  | Copyright (C) 2002  Qgar Project, LORIA                             |
00004  |                                                                     |
00005  | This library is free software; you can redistribute it and/or       |
00006  | modify it under the terms of the GNU Lesser General Public          |
00007  | License version 2.1, as published by the Free Software Foundation.  |
00008  |                                                                     |
00009  | This library is distributed in the hope that it will be useful,     |
00010  | but WITHOUT ANY WARRANTY; without even the implied warranty of      |
00011  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                |
00012  | See the GNU Lesser General Public License for more details.         |
00013  |                                                                     |
00014  | The GNU Lesser General Public License is included in the file       |
00015  | LICENSE.LGPL, in the root directory of the Qgar packaging. See      |
00016  | http://www.gnu.org/licenses/lgpl.html for the terms of the licence. |
00017  | To receive a paper copy, write to the Free Software Foundation,     |
00018  | Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.       |
00019  |                                                                     |
00020  | Contact Project Qgar for any information:                           |
00021  |   LORIA - équipe Qgar                                               |
00022  |   B.P. 239, 54506 Vandoeuvre-lès-Nancy Cedex, France                |
00023  |   email: qgar-contact@loria.fr                                      |
00024  |   http://www.qgar.org/                                              |
00025  *---------------------------------------------------------------------*/
00026 
00027 
00028 /**
00029  * @file   BoundingBox.C
00030  * @brief  Implementation of class qgar::BoundingBox.
00031  *
00032  *         See file BoundingBox.H for the interface.
00033  * 
00034  * @author <a href="mailto:qgar-develop@loria.fr?subject=Qgar fwd Gérald Masini">Gérald Masini</a>
00035  * @date   June 17, 2004  18:46
00036  * @since  Qgar 2.1.1
00037  */
00038 
00039 
00040 // STL
00041 #include <cmath>
00042 #include <iostream>
00043 #include <string>
00044 // QGAR
00045 #include <qgarlib/BoundingBox.H>
00046 #include <qgarlib/primitives.H>
00047 #include <qgarlib/QgarErrorDomain.H>
00048 
00049 
00050 
00051 namespace qgar
00052 {
00053 
00054 // -------------------------------------------------------------------
00055 // C O N S T R U C T O R S 
00056 // -------------------------------------------------------------------
00057 
00058   
00059 // DEFAULT CONSTRUCTOR
00060 
00061 BoundingBox::BoundingBox()
00062 
00063   : _topLeft    (0, 0),
00064     _bottomRight(0, 0)
00065 
00066 {
00067   // VOID
00068 }
00069 
00070 
00071 // COPY CONSTRUCTOR
00072 
00073 BoundingBox::BoundingBox(const BoundingBox& aBox)
00074 
00075   : _topLeft    (aBox._topLeft),
00076     _bottomRight(aBox._bottomRight)
00077 
00078 {
00079   // VOID
00080 }
00081 
00082 
00083 // INITIALIZE FROM TWO POINTS
00084 
00085 BoundingBox::BoundingBox(const IPoint& aPt1,
00086                          const IPoint& aPt2)
00087 {
00088    setCorners(aPt1, aPt2);
00089 }
00090 
00091 
00092 // INITIALIZE FROM COORDINATES OF TWO POINTS
00093 
00094 BoundingBox::BoundingBox(int aX1, int aY1, int aX2, int aY2)
00095 {
00096   setCorners(aX1, aY1, aX2, aY2);
00097 }
00098 
00099 
00100 // INITIALIZE FROM TOP LEFT CORNER, LENGTH AND HEIGHT
00101 
00102 BoundingBox::BoundingBox(const IPoint& aTopLeft,
00103                          int aLength,
00104                          int aHeight)
00105  throw(QgarErrorDomain)
00106 
00107   : _topLeft(aTopLeft),
00108     _bottomRight(aTopLeft.x() + aLength - 1, 
00109                  aTopLeft.y() + aHeight - 1)
00110 
00111 {
00112   // Ensure that width and height are greater than 1.
00113   // Throw exception otherwise.
00114 
00115   if (aLength <= 0) 
00116     {
00117       std::ostringstream os;
00118       os << "Bounding box length ("
00119          << aLength
00120          << ") must be greater than 0";
00121       throw QgarErrorDomain(__FILE__, __LINE__,
00122                             "qgar::BoundingBox::BoundingBox(const qgar::IPoint&, int, int)",
00123                             os.str());
00124     }
00125 
00126   if (aHeight <= 0) 
00127     {
00128       std::ostringstream os;
00129       os << "Bounding box height ("
00130          << aHeight
00131          << ") must be greater than 0";
00132       throw QgarErrorDomain(__FILE__, __LINE__,
00133                             "qgar::BoundingBox::BoundingBox(const qgar::IPoint&, int, int)",
00134                             os.str());
00135     }
00136 }
00137 
00138 
00139 // INITIALIZE FROM BOTTOM RIGHT CORNER, LENGTH AND HEIGHT
00140 
00141 BoundingBox::BoundingBox(int aLength,
00142                          int aHeight,
00143                          const IPoint& aBottomRight) 
00144   throw(QgarErrorDomain)
00145 
00146   : _topLeft(aBottomRight.x() - aLength + 1, 
00147              aBottomRight.y() - aHeight + 1),
00148     _bottomRight(aBottomRight)
00149 
00150 {
00151   // Ensure that width and height are greater than 1.
00152   // Throw exception otherwise.
00153 
00154   if (aLength <= 0) 
00155     {
00156       std::ostringstream os;
00157       os << "Bounding box length ("
00158          << aLength
00159          << ") must be greater than 0";
00160       throw QgarErrorDomain(__FILE__, __LINE__,
00161                             "qgar::BoundingBox::BoundingBox(int, int, const qgar::IPoint&)",
00162                             os.str());
00163     }
00164 
00165   if (aHeight <= 0) 
00166     {
00167       std::ostringstream os;
00168       os << "Bounding box height ("
00169          << aHeight
00170          << ") must be greater than 0";
00171       throw QgarErrorDomain(__FILE__, __LINE__,
00172                             "qgar::BoundingBox::BoundingBox(int, int, const qgar::IPoint&)",
00173                             os.str());
00174     }
00175 }
00176 
00177 
00178 // -------------------------------------------------------------------
00179 // D E S T R U C T O R
00180 // -------------------------------------------------------------------
00181 
00182 
00183 BoundingBox::~BoundingBox()
00184 {
00185   // VOID
00186 }
00187 
00188 
00189 // -------------------------------------------------------------------
00190 // S E T   C O R N E R S
00191 // -------------------------------------------------------------------
00192 
00193 
00194 // SET TOP LEFT CORNER
00195 
00196 void 
00197 BoundingBox::setTopLeft(const IPoint& aPt)
00198 
00199   throw(QgarErrorDomain)
00200 
00201 {
00202   // Is the new point a valid top left corner?
00203   if ((aPt.x() > _topLeft.x()) || (aPt.y() > _bottomRight.y()))
00204     {
00205       std::ostringstream os;
00206       os << "New top left corner (" << aPt.x() << ',' << aPt.y()
00207          << ") does not fit current box: Top left("
00208          << _topLeft.x() << ',' << _topLeft.y()
00209          << "), bottom right("
00210          << _bottomRight.x() << ',' << _bottomRight.y()
00211          << ')';
00212       throw QgarErrorDomain(__FILE__, __LINE__,
00213                             "void qgar::BoundingBox::setTopLeft(const qgar::IPoint&)",
00214                             os.str());
00215     }
00216   _topLeft = aPt;
00217 }
00218 
00219 
00220 // SET (X AND Y OF) TOP LEFT CORNER
00221 
00222 void
00223 BoundingBox::setTopLeft(int aX, int aY)
00224 
00225   throw(QgarErrorDomain)
00226 
00227 {
00228   // Is the new point a valid top left corner?
00229   if ((aX > _topLeft.x()) || (aY > _bottomRight.y()))
00230     {
00231       std::ostringstream os;
00232       os << "New top left corner (" << aX << ',' << aY
00233          << ") does not fit current box: Top left("
00234          << _topLeft.x() << ',' << _topLeft.y()
00235          << "), bottom right("
00236          << _bottomRight.x() << ',' << _bottomRight.y()
00237          << ')';
00238       throw QgarErrorDomain(__FILE__, __LINE__,
00239                             "void qgar::BoundingBox::setTopLeft(int, int)",
00240                             os.str());
00241     }
00242 
00243   _topLeft.setXY(aX, aY);
00244 }
00245 
00246 
00247 // SET X COORDINATE OF TOP LEFT CORNER
00248 
00249 void
00250 BoundingBox::setXTopLeft(int aX)
00251 
00252   throw(QgarErrorDomain)
00253 
00254 {
00255   // Is the new point a valid top left corner?
00256   if (aX > _bottomRight.x())
00257     {
00258       std::ostringstream os;
00259       os << "New top left corner (" << aX << ',' << _topLeft.y()
00260          << ") does not fit current box: Top left("
00261          << _topLeft.x() << ',' << _topLeft.y()
00262          << "), bottom right("
00263          << _bottomRight.x() << ',' << _bottomRight.y()
00264          << ')';
00265       throw QgarErrorDomain(__FILE__, __LINE__,
00266                             "void qgar::BoundingBox::setXTopLeft(int)",
00267                             os.str());
00268     }
00269 
00270   _topLeft.setX(aX);
00271 }
00272 
00273 
00274 // SET Y COORDINATE OF TOP LEFT CORNER
00275 
00276 void
00277 BoundingBox::setYTopLeft(int aY)
00278 
00279   throw(QgarErrorDomain)
00280 
00281 {
00282   // Is the new point a valid top left corner?
00283   if (aY > _bottomRight.y())
00284     {
00285       std::ostringstream os;
00286       os << "New top left corner (" << _topLeft.x() << ',' << aY
00287          << ") does not fit current box: Top left("
00288          << _topLeft.x() << ',' << _topLeft.y()
00289          << "), bottom right("
00290          << _bottomRight.x() << ',' << _bottomRight.y()
00291          << ')';
00292       throw QgarErrorDomain(__FILE__, __LINE__,
00293                             "void qgar::BoundingBox::setYTopLeft(int)",
00294                             os.str());
00295     }
00296 
00297   _topLeft.setY(aY);
00298 }
00299 
00300 
00301 // SET BOTTOM RIGHT CORNER
00302 
00303 void 
00304 BoundingBox::setBottomRight(const IPoint& aPt)
00305 
00306   throw(QgarErrorDomain)
00307 
00308 {
00309   // Is the new point a valid bottom right corner?
00310   if ((aPt.x() < _topLeft.x()) || (aPt.y() < _bottomRight.y()))
00311     {
00312       std::ostringstream os;
00313       os << "New bottom right corner (" << aPt.x() << ',' << aPt.y()
00314          << ") does not fit current box: Top left("
00315          << _topLeft.x() << ',' << _topLeft.y()
00316          << "), bottom right("
00317          << _bottomRight.x() << ',' << _bottomRight.y()
00318          << ')';
00319       throw QgarErrorDomain(__FILE__, __LINE__,
00320                             "void qgar::BoundingBox::BottomRight(const qgar::IPoint&)",
00321                             os.str());
00322     }
00323   _bottomRight = aPt;
00324 }
00325 
00326 
00327 // SET (X AND Y OF) BOTTOM RIGHT CORNER
00328 
00329 void
00330 BoundingBox::setBottomRight(int aX, int aY)
00331 
00332   throw(QgarErrorDomain)
00333 
00334 {
00335   // Is the new point a valid bottom right corner?
00336   if ((aX < _topLeft.x()) || (aY < _bottomRight.y()))
00337     {
00338       std::ostringstream os;
00339       os << "New bottom right corner (" << aX << ',' << aY
00340          << ") does not fit current box: Top left("
00341          << _topLeft.x() << ',' << _topLeft.y()
00342          << "), bottom right("
00343          << _bottomRight.x() << ',' << _bottomRight.y()
00344          << ')';
00345       throw QgarErrorDomain(__FILE__, __LINE__,
00346                             "void qgar::BoundingBox::setBottomRight(int, int)",
00347                             os.str());
00348     }
00349   _bottomRight.setXY(aX, aY);
00350 }
00351 
00352 
00353 // SET X COORDINATE OF BOTTOM RIGHT CORNER
00354 
00355 void
00356 BoundingBox::setXBottomRight(int aX)
00357 
00358   throw(QgarErrorDomain)
00359 
00360 {
00361   // Is the new point a valid bottom right corner?
00362   if (aX < _topLeft.x())
00363     {
00364       std::ostringstream os;
00365       os << "New bottom right corner (" << aX << ',' << _bottomRight.y()
00366          << ") does not fit current box: Top left("
00367          << _topLeft.x() << ',' << _topLeft.y()
00368          << "), bottom right("
00369          << _bottomRight.x() << ',' << _bottomRight.y()
00370          << ')';
00371       throw QgarErrorDomain(__FILE__, __LINE__,
00372                             "void qgar::BoundingBox::setXBottomRight(int)",
00373                             os.str());
00374     }
00375 
00376   _bottomRight.setX(aX);
00377 }
00378 
00379 
00380 // SET Y COORDINATE OF BOTTOM RIGHT CORNER
00381 
00382 void
00383 BoundingBox::setYBottomRight(int aY)
00384 
00385   throw(QgarErrorDomain)
00386 
00387 {
00388   // Is the new point a valid bottom right corner?
00389   if (aY < _topLeft.y())
00390     {
00391       std::ostringstream os;
00392       os << "New bottom right corner (" << _bottomRight.x() << ',' << aY
00393          << ") does not fit current box: Top left("
00394          << _topLeft.x() << ',' << _topLeft.y()
00395          << "), bottom right("
00396          << _bottomRight.x() << ',' << _bottomRight.y()
00397          << ')';
00398       throw QgarErrorDomain(__FILE__, __LINE__,
00399                             "void qgar::BoundingBox::setYBottomRight(int)",
00400                             os.str());
00401     }
00402 
00403   _bottomRight.setY(aY);
00404 }
00405 
00406 
00407 // SET BOTH TOP LEFT AND BOTTOM RIGHT CORNERS, USING POINTS
00408 
00409 void
00410 BoundingBox::setCorners(const IPoint& aPt1, const IPoint& aPt2)
00411 {
00412   setCorners(aPt1.x(), aPt1.y(), aPt2.x(), aPt2.y());
00413 }
00414 
00415 
00416 // SET BOTH TOP LEFT AND BOTTOM RIGHT CORNERS, USING COORDINATES
00417 
00418 void
00419 BoundingBox::setCorners(int aX1, int aY1, int aX2, int aY2)
00420 {
00421   // WARNING: THE ORIGIN OF THE COORDINATE SYSTEM
00422   //          IS AT TOP LEFT OF THE (IMAGE) PLANE
00423   //   0
00424   //    +--------------------------------------> X
00425   //    |
00426   //    |  top left o--------------+
00427   //    |           |              |
00428   //    |           | Bounding Box |
00429   //    |           |              |
00430   //    |           +--------------o
00431   //    |                           bottom right
00432   //    V
00433   //
00434   //    Y
00435 
00436   _topLeft.setXY    ((aX1 > aX2) ? aX2 : aX1, (aY1 > aY2) ? aY2 : aY1);
00437   _bottomRight.setXY((aX1 > aX2) ? aX1 : aX2, (aY1 > aY2) ? aY1 : aY2);
00438 }
00439 
00440 
00441 // -------------------------------------------------------------------
00442 // G E O M E T R I C A L   P R E D I C A T E S
00443 // -------------------------------------------------------------------
00444 
00445 
00446 // DOES THE POINT DEFINED BY THE GIVEN COORDINATES BELONG
00447 // TO THE CURRENT BOUNDING BOX?
00448 
00449 bool
00450 BoundingBox::contains(int aX, int aY) const
00451 {
00452   // WARNING: THE ORIGIN OF THE COORDINATE SYSTEM
00453   //          IS AT TOP LEFT OF THE (IMAGE) PLANE
00454   //   0
00455   //    +--------------------------------------> X
00456   //    |
00457   //    |  top left o--------------+
00458   //    |           |              |
00459   //    |           | Bounding Box |
00460   //    |           |              |
00461   //    |           +--------------o
00462   //    |                           bottom right
00463   //    V
00464   //
00465   //    Y
00466 
00467   return
00468        (aX >= _topLeft.x())
00469     && (aX <= _bottomRight.x())
00470     && (aY >= _topLeft.y())
00471     && (aY <= _bottomRight.y());
00472 }
00473 
00474 
00475 // DOES THE CURRENT BOUNDING BOX INTERSECT THE GIVEN BOUNDING BOX?
00476 
00477 bool
00478 BoundingBox::intersects(const BoundingBox& aBox) const
00479 {
00480   //  0
00481   //   +-------------------------------> X
00482   //   |                    |                t: current box top left
00483   //   |             -------R      T---      r: current box bottom right
00484   //   |        |             |    |
00485   //   |      --t-------------+--  |         T: given box top left
00486   //   |        |/////////////|    |         R: given box bottom right
00487   //   |   |    |/current box/|
00488   //   |   |    |/////////////|
00489   //   |   |  --+-------------r--
00490   //   | --R    |             |
00491   //   |
00492   //   |                     T-------
00493   //   V                     |
00494   //   Y
00495   //
00496   //  The given box does not intersect the current box iff:
00497   //
00498   //  X(R) < X(t)  or  Y(R) < Y(t)  or  X(T) > X(r)  or  Y(T) > Y(r)
00499   //
00500   //  => The given box intersects the current box iff:
00501 
00502   return
00503        (aBox.xBottomRight() >= _topLeft.x())       // X(R) >= X(t)
00504     && (aBox.yBottomRight() >= _topLeft.y())       // Y(R) >= Y(t)
00505     && (aBox.xTopLeft()     <= _bottomRight.x())   // X(T) <= X(r)
00506     && (aBox.yTopLeft()     <= _bottomRight.y());  // Y(T) <= Y(r)
00507 }
00508 
00509 
00510 // -------------------------------------------------------------------
00511 // G E O M E T R I C A L   O P E R A T I O N S
00512 // -------------------------------------------------------------------
00513 
00514 
00515 // INTERSECTION BETWEEN THE CURRENT AND GIVEN BOUNDING BOXES
00516 
00517 BoundingBox*
00518 BoundingBox::intersection(const BoundingBox& aBox) const
00519 {
00520   if (this->intersects(aBox))
00521     {
00522       // WARNING: THE ORIGIN OF THE COORDINATE SYSTEM
00523       //          IS AT TOP LEFT OF THE (IMAGE) PLANE
00524       //   0
00525       //    +--------------------------------------> X
00526       //    |
00527       //    |  top left o--------------+
00528       //    |           |              |
00529       //    |           | Bounding Box |
00530       //    |           |              |
00531       //    |           +--------------o
00532       //    |                           bottom right
00533       //    V
00534       //
00535       //    Y
00536 
00537       return
00538         new BoundingBox(std::max(_topLeft.x(), aBox.xTopLeft()),
00539                         std::max(_topLeft.y(), aBox.yTopLeft()),
00540                         std::min(_bottomRight.x(), aBox.xBottomRight()),
00541                         std::min(_bottomRight.y(), aBox.yBottomRight()));
00542     }
00543   else
00544     {
00545       return (BoundingBox*)0;
00546     }
00547 }
00548 
00549 
00550 // -------------------------------------------------------------------
00551 // O P E R A T O R S
00552 // -------------------------------------------------------------------
00553 
00554 
00555 // ASSIGNMENT
00556 
00557 BoundingBox&
00558 BoundingBox::operator=(const BoundingBox& aBox)
00559 {
00560   // Are left hand side and right hand side different objects?
00561   if (this != &aBox)
00562     {
00563       _topLeft     = aBox._topLeft;
00564       _bottomRight = aBox._bottomRight;
00565     }
00566   return *this;
00567 }
00568 
00569 
00570 // -------------------------------------------------------------------
00571 // F U N C T I O N A L   O P E R A T O R S
00572 // -------------------------------------------------------------------
00573 
00574 
00575 // EQUALITY
00576 
00577 bool
00578 BoundingBox::eq(const BoundingBox& aBox) const
00579 {
00580   return (_topLeft == aBox.topLeft())
00581     && (_bottomRight == aBox.bottomRight());
00582 }
00583 
00584 
00585 // INEQUALITY
00586 
00587 bool
00588 BoundingBox::notEq(const BoundingBox& aBox) const
00589 {
00590   return (_topLeft != aBox.topLeft())
00591     || (_bottomRight != aBox.bottomRight());
00592 }
00593 
00594 
00595 // -------------------------------------------------------------------
00596 // S E R I A L I Z A T I O N / D E S E R I A L I Z A T I O N
00597 // -------------------------------------------------------------------
00598 
00599 
00600 std::istream& 
00601 BoundingBox::read(std::istream& anInStream)
00602 {
00603   IPoint topL;
00604   IPoint bottomR;
00605   qgReadObjName(anInStream, "BoundingBox");
00606   qgReadObjData(anInStream, topL);
00607   qgReadObjData(anInStream, bottomR);
00608 
00609   setCorners(topL, bottomR);
00610 
00611   return anInStream;
00612 }
00613 
00614 
00615 std::ostream& 
00616 BoundingBox::write(std::ostream& anOutStream) const
00617 {
00618   anOutStream << "BoundingBox("
00619               << _topLeft
00620               << ")("
00621               << _bottomRight
00622               << ')';
00623 
00624   return anOutStream;
00625 }
00626 
00627 
00628 // -------------------------------------------------------------------
00629 
00630 
00631 } // namespace qgar