MinAreaRectangle.cpp
Go to the documentation of this file.
00001 // ========================================================================================
00002 //  ApproxMVBB
00003 //  Copyright (C) 2014 by Gabriel Nützi <nuetzig (at) imes (d0t) mavt (d0t) ethz (døt) ch>
00004 //
00005 //  This Source Code Form is subject to the terms of the Mozilla Public
00006 //  License, v. 2.0. If a copy of the MPL was not distributed with this
00007 //  file, You can obtain one at http://mozilla.org/MPL/2.0/.
00008 // ========================================================================================
00009 
00010 #include "ApproxMVBB/MinAreaRectangle.hpp"
00011 
00012 namespace ApproxMVBB{
00013 void MinAreaRectangle::compute() {
00014     // Compute minimum area rectangle
00015 
00016     // Clear minBox;
00017     m_minBox.reset();
00018 
00019     // If no points return early!
00020     if(m_p.cols() == 0) {
00021         return;
00022     }
00023 
00024     // Generate Convex Hull
00025     m_convh.compute();
00026     ApproxMVBB_ASSERTMSG(m_convh.verifyHull(), "Convex hull not ok!")
00027 
00028     // Compute Rectangle
00029     // computes p,u,v of box (not normalized)
00030     computeRectangle();
00031 
00032     // Final adjustments (u and v are getting normalized)
00033     adjustRectangle();
00034 }
00035 
00036 void MinAreaRectangle::computeRectangle() {
00037 
00038     // Copy Points;
00039     m_hullIdx = m_convh.getIndices();
00040     unsigned int nPoints = m_hullIdx.size();
00041 
00042     //std::cout << "Convex hull points:" << nPoints << std::endl;
00043 
00044     // The below code works for points >= 2
00045     // Anyway catch the cases n=1 and 2 and return early!
00046     if(nPoints == 0) {
00047         return;
00048     } else if( nPoints == 1) {
00049         m_minBox.m_p = m_p.col(m_hullIdx[0]);
00050         m_minBox.m_u.setZero();
00051         m_minBox.m_v.setZero();
00052         return;
00053     } else if(nPoints == 2) {
00054         m_minBox.m_p = m_p.col(m_hullIdx[0]);
00055         m_minBox.m_u = m_p.col(m_hullIdx[1]) - m_p.col(m_hullIdx[0]);
00056         m_minBox.m_v.setZero();
00057         return;
00058     }
00059 
00060 
00061     // Performing Rotating Calipers method
00062     // 1. find the vertices with the minimum and maximum x an y coordinates.
00063     //    These vertices (points) will be denoted by {PI, PJ, PK, PL}
00064     // 2. construct CALL and CALJ as the first set of calipers parallel to the x-axis,
00065     //    and CALI and CALK as the second set of calipers parallel to the y-axis
00066     // 3. create for every CALX a next caliper,
00067     //    NEXT_CALX, based on the next point in the convex hull and calculate the tangent of CALX and it's NEXT_CALX
00068     // 4. get the smallest positive tangent between all CALX and NEXT_CALX and rotate every caliper by that smallest gradient
00069     // 5. repeat step 3 and 4 untill the calipers have turned more then 90 degrees
00070 
00071     // We implement this algorithm a bit differently:
00072     // 1. Compute all angles of all edges angle[i] is angle of edge p[i],p[i+1].
00073     // 2. Rotatate the first caliper A from edge to edge.
00074     // 3. Determine all new angles for B,C,D such that it forms a box (rotation of pi from angle of caliper A)
00075     // 4. Determine for each caliper I  the location (the corresponding vertex index) where the
00076     //    caliper I forms a tangent (starting from the currend index).
00077     // 5. Determine the box by intersecting the calipers.
00078     // 6. Compare to area to minimum and return to 2.
00079 
00080     // Calcualte all edge angles
00081 
00082     m_angles.resize(nPoints);
00083     for(unsigned int i=0; i<nPoints; ++i) {
00084         m_angles[i] = PointFunctions::getAngle(m_p.col(m_hullIdx[i]),
00085                                                m_p.col(m_hullIdx[(i+1) % nPoints])
00086                                               );
00087         //std::cout << "angle: " << m_angles[i] << std::endl;
00088     }
00089 
00090     // 2. Construct 4 Calipers
00091     Caliper calipers[4];
00092     // Set A
00093     calipers[0].m_idx = 0;
00094     calipers[0].m_ptIdx = m_hullIdx[0];
00095     // Init A,B,C,D
00096     updateCalipers(m_angles[0],calipers);
00097 
00098     Box2d box;
00099     // Init minBox
00100     getBox(calipers,m_minBox);
00101 
00102     // Rotate calipers over all edges and take minimum box
00103     for(unsigned int i=1; i<nPoints; ++i) {
00104 
00105         updateCalipers(m_angles[i],calipers);
00106         // Get Box from Calipers
00107         getBox(calipers,box);
00108 
00109         if(box.m_area < m_minBox.m_area) {
00110 
00111             m_minBox = box;
00112         }
00113     }
00114 }
00115 
00116 // determine the vertex v for which the edge angle is greater than c.m_currAngle
00117 // the find algroithm starts at c.m_idx
00118 void MinAreaRectangle::findVertex(Caliper & c) {
00119 
00120     PREC matchAngle = c.m_currAngle;    // between 0-2pi
00121     //std::cout << "Match Angle: " << matchAngle << std::endl;
00122 
00123     unsigned int currIdx = c.m_idx;
00124 
00125     PREC currAngle = m_angles[currIdx]; // all between 0-2pi
00126     PREC nextAngle;
00127 
00128     unsigned int nPoints = m_hullIdx.size();
00129     bool found = false;
00130     unsigned int i = 0;
00131     while(!found && i < nPoints) {
00132 
00133         currIdx = (currIdx + 1) % nPoints;
00134         // std::cout << "c: " << currIdx << std::endl;
00135         nextAngle = m_angles[currIdx];
00136         //std::cout << "diff: " << std::abs(currAngle-nextAngle) << std::endl;
00137         //                                         curr       next
00138         // if we are not at the boundary [ ..., 45 degree, 50 degree , ....]
00139         // and in
00140         if( nextAngle > currAngle ) {
00141             //std::cout << " greater" <<std::endl;
00142             found = (currAngle < matchAngle && matchAngle <= nextAngle);
00143         } else {
00144             //std::cout << " boundary" <<std::endl;
00145             //                                     curr       next
00146             // if we are at the boundary [ ..., 359 degree] [ 5 degree , .... ]
00147             // skip this if we have an angle difference |curr -next| < eps
00148             // this might happen due to floating point nonsense
00149             found = (std::abs(currAngle-nextAngle)>1e-10) && (currAngle < matchAngle || matchAngle <= nextAngle);
00150         }
00151 
00152         currAngle = nextAngle;
00153         ++i;
00154     }
00155 
00156     if(found) {
00157         c.m_idx = currIdx;
00158         c.m_ptIdx = m_hullIdx[currIdx];
00159         //std::cout << "caliper idx:" << currIdx << std::endl;
00160     } else {
00161         std::stringstream ss;
00162         for(auto & a : m_angles){ ss << a <<",";}
00163         ApproxMVBB_ERRORMSG("Could not find vertex with angle greater than: "
00164                  << matchAngle << "in angles: " << ss.str());
00165     }
00166 }
00167 
00168 void MinAreaRectangle::getBox(Caliper (&c)[4], Box2d & box) {
00169         using namespace PointFunctions;
00170         // Intersect Caliper A with B,
00171         box.m_u = intersectLines(   m_p.col(c[0].m_ptIdx), c[0].m_currAngle,
00172                                     m_p.col(c[1].m_ptIdx), c[1].m_currAngle);
00173         // Intersect Caliper D with C,
00174         box.m_v = intersectLines(   m_p.col(c[3].m_ptIdx), c[3].m_currAngle,
00175                                     m_p.col(c[2].m_ptIdx), c[2].m_currAngle);
00176 
00177         // Intersect Caliper A with D,
00178         box.m_p = intersectLines(  m_p.col(c[0].m_ptIdx), c[0].m_currAngle,
00179                                    m_p.col(c[3].m_ptIdx), c[3].m_currAngle);
00180 
00181         box.m_u -= box.m_p; //(p1-p0)
00182         box.m_v -= box.m_p; //(p2-p0)
00183         box.m_area = box.m_u.norm()*box.m_v.norm();
00184 
00185     }
00186 
00187 }


asr_approx_mvbb
Author(s): Gassner Nikolai
autogenerated on Sat Jun 8 2019 20:21:49