MeshRemapper.cpp

Go to the documentation of this file.
00001 // -*- c-basic-offset: 4 -*-
00002 
00023 #ifdef __WXMAC__
00024 #include "panoinc_WX.h"
00025 #include "panoinc.h"
00026 #endif
00027 
00028 #include <math.h>
00029 #include "MeshRemapper.h"
00030 #include "ViewState.h"
00031 
00032 MeshRemapper::MeshRemapper(HuginBase::Panorama *m_pano_in,
00033                            HuginBase::SrcPanoImage * image_in,
00034                            VisualizationState *visualization_state_in)
00035 {
00036     m_pano = m_pano_in;
00037     image = image_in;
00038     visualization_state = visualization_state_in;
00039 }
00040 
00041 MeshRemapper::~MeshRemapper()
00042 {
00043 }
00044 
00045 void MeshRemapper::UpdateAndResetIndex()
00046 {
00047     // we want to make a remapped mesh, get some generic information:
00048 //    const HuginBase::SrcPanoImage *src = visualization_state->GetSrcImage(image_number);
00049     // get the size of the image.
00050     width = (double) image->getSize().width();
00051     height = (double) image->getSize().height();
00052   
00053     // use the scale to determine edge lengths in pixels for any
00054     // resolution selection.
00055     scale = visualization_state->GetScale();
00056     // It is up to the child classes to determine what to do here. They should
00057     // probably use set up transform and use it to fill some data structure that
00058     // stores coordinates.
00059 }
00060 
00061 void MeshRemapper::SetCrop()
00062 {
00063 //    const HuginBase::SrcPanoImage *src = visualization_state->GetSrcImage(image_number);
00064     crop_mode = image->getCropMode();
00065     crop_x1 = (double) image->getCropRect().left() / width;
00066     crop_x2 = (double) image->getCropRect().right() / width;
00067     crop_y1 = (double) image->getCropRect().top() / height;
00068     crop_y2 = (double) image->getCropRect().bottom() / height;
00069     // set variables for circular crop.
00070     circle_crop = image->getCropMode() == HuginBase::SrcPanoImage::CROP_CIRCLE;
00071     if (circle_crop)
00072     {
00073         circle_crop_centre_x = (crop_x1 + crop_x2) / 2.0;
00074         circle_crop_centre_y = (crop_y1 + crop_y2) / 2.0;
00075         double crop_width_px = (double) image->getCropRect().width(),
00076                crop_hieght_px = (double) image->getCropRect().height(),
00077                crop_radius = (crop_width_px < crop_hieght_px ?
00078                                 crop_width_px : crop_hieght_px) / 2.0;
00079         circle_crop_radius_x = crop_radius / width;
00080         circle_crop_radius_y = crop_radius / height;
00081     }
00082     // hugin allows negative cropping regions, but we are expected to only
00083     // output the regions that exist in the original image, so clamp the values:
00084     if (crop_x1 < 0.0) crop_x1 = 0.0; if (crop_x1 > 1.0) crop_x1 = 1.0;
00085     if (crop_x2 < 0.0) crop_x2 = 0.0; if (crop_x2 > 1.0) crop_x2 = 1.0;
00086     if (crop_y1 < 0.0) crop_y1 = 0.0; if (crop_y1 > 1.0) crop_y1 = 1.0;
00087     if (crop_y2 < 0.0) crop_y2 = 0.0; if (crop_y2 > 1.0) crop_y2 = 1.0;
00088 }
00089 
00090 /******************************************************************************
00091  * Clipping stuff                                                             *
00092  ******************************************************************************/
00093  
00094 // a vertex has vertex coordinates and a texture coordinates.
00095 class Vertex
00096 {
00097 public:
00098     Vertex(double vx, double vy, double tx, double ty);
00099     Vertex() {}
00100     double vertex_c[2];
00101     double tex_c[2];
00102 };
00103 
00104 Vertex::Vertex(double vx, double vy, double tx, double ty)
00105 {
00106     vertex_c[0] = vx;
00107     vertex_c[1] = vy;
00108     tex_c[0] = tx;
00109     tex_c[1] = ty;
00110 }
00111 
00112 // The A_Polygon class stores an arbitrary polygon, and can give you another
00113 // polygon that is the result of clipping it with some line.
00114 // hmmm... Using the name "Polygon" causes errors on windows, hence A_Polgon.
00115 class A_Polygon
00116 {
00117 public:
00118     // Create the polygon from a quadrilateral
00119     A_Polygon(MeshRemapper::Coords *face);
00120     A_Polygon() {}
00121     // clip lines are the line defined by x * p[0] + y * p[1] + p[2] = 0.
00122     // inside is the side where x * p[0] + y *p[1] + p[2] > 0.
00123     A_Polygon Clip(const double clip_line[3]);
00124     // add a vertex on the end of the list. Used to build the polygon when not
00125     // created from a quad.
00126     inline void AddVertex(Vertex v);
00127     // convert to a list of quads. A bit approximate.
00128     std::vector<MeshRemapper::ArrayCoords> ConvertToQuads();
00129 private:
00130     // our list of vertices
00131     std::vector<Vertex> verts;
00132     // Test which side of the clip plane we are on
00133     inline bool Inside(const unsigned int vertex,
00134                        const double clip_line[3]) const;
00135     // given that the edge between the specified vertices crosses the clip line,
00136     // find where they cross and set the vertex and texture coordinates there.
00137     Vertex Intersect(const unsigned int v1, const unsigned int v2,
00138                      const double clip_line[3]) const;
00139 };
00140 
00141 A_Polygon::A_Polygon(MeshRemapper::Coords *face)
00142 {
00143     verts.resize(4);
00144     for (unsigned short int v = 0; v < 4; v++)
00145     {
00146         // give verticies counter-clockwise
00147         unsigned short int x = (v == 2 || v==3) ? 1 : 0,
00148                            y = (v == 1 || v==2) ? 1 : 0;
00149         for (unsigned short int a = 0; a < 2; a++)
00150         {
00151             verts[v].vertex_c[a] = face->vertex_c[x][y][a];
00152             verts[v].tex_c[a]    = face->tex_c[x][y][a];
00153         }
00154     }
00155 }
00156 
00157 inline void A_Polygon::AddVertex(Vertex v)
00158 {
00159     verts.push_back(v);
00160 }
00161 
00162 inline bool A_Polygon::Inside(const unsigned int v, const double l[3]) const
00163 {
00164     return verts[v].tex_c[0] * l[0] + verts[v].tex_c[1] * l[1] + l[2] > 0;
00165 }
00166 
00167 Vertex A_Polygon::Intersect(const unsigned int v1_index,
00168                           const unsigned int v2_index,
00169                           const double cl[3]) const
00170 {
00171     // find the point of intersection from the given edge with a clip plane.
00172     // the edge is the vertex with the same number and the one before it.
00173     // Get pointers to the vertecies we will use
00174     const Vertex *v1 = &verts[v1_index],
00175                  // the vertex before is the last one if we at the beginning.
00176                  *v2 = &verts[v2_index];
00177     // find a vector along the path of the edge we are clipping
00178     double dx = v2->tex_c[0] - v1->tex_c[0],
00179            dy = v2->tex_c[1] - v1->tex_c[1];
00180     // The line along the edge we clip is defined by (x,y) = v1 + t(dx, dy)
00181     // The line of the clipping plane is cl[0] * x + cl[1] * y + cl[2] = 0
00182     // now find what value of t that is on both lines.
00183     // substitute x and y from the edge line into the equation of the clip line:
00184     // cl[0] * (v1_x + t * dx) + cl[1] * (v1_y + t * dy) + cl[2] = 0
00185     // then rearrange to get t:
00186     // cl[0] * v1_x + t * cl[0] * dx + cl[1] * v1_y + t * cl[1] * dy + cl[2] = 0
00187     // t * (cl[0] * dx + cl[1] * dy) + cl[0] * v1_x + cl[1] * v1_y + cl[2] = 0
00188     // t * (cl[0] * dx + cl[1] * dy) = -cl[0] * v1_x - cl[1] * v1_y - cl[2]
00189     // so we get:
00190     /* FIXME this assertion sometimes fails. t should always be between 0 and 1,
00191      * but sometimes isn't even when this one passes:
00192      */
00193     // DEBUG_ASSERT(cl[0] * dx + cl[1] * dy);
00194     double t = (-cl[0] * v1->tex_c[0] - cl[1] * v1->tex_c[1] - cl[2]) /
00195                (cl[0] * dx + cl[1] * dy),
00196     // now substitute t into the edge we are testing's line:
00197            x = dx * t + v1->tex_c[0],
00198            y = dy * t + v1->tex_c[1],
00199     // move the vertex coordinates to match the texture ones.
00200   // t = 0 would give v1, t = 1 would give v2; so we linearly interpolate by t:
00201            td1 = 1.0 - t,
00202            xc = v1->vertex_c[0] * td1 + v2->vertex_c[0] * t,
00203            yc = v1->vertex_c[1] * td1 + v2->vertex_c[1] * t;
00204     // DEBUG_ASSERT(-0.1 <= t && t <= 1.1);
00205     return Vertex(xc, yc, x, y);
00206 }
00207 
00208 A_Polygon A_Polygon::Clip(const double clip_line[3])
00209 {
00210     // We'll use the Sutherland-Hodgman clipping algorithm.
00211     // see http://en.wikipedia.org/wiki/Sutherland-Hodgeman
00212     A_Polygon result;
00213     unsigned int vertices_count = verts.size(),
00214                  v_previous = vertices_count - 1;
00215     // we want to examine all edges in turn, and find the vertices to keep:
00216     for (unsigned int v_index = 0; v_index < vertices_count; v_index++)
00217     {
00218         bool v_index_inside = Inside(v_index, clip_line),
00219              v_previous_inside = Inside(v_previous, clip_line);
00220         if (v_index_inside != v_previous_inside)
00221         {
00222             // one in, one out, therefore the edge instersts the clip line.
00223             result.AddVertex(Intersect(v_previous, v_index, clip_line));
00224         }
00225         if (v_index_inside)
00226         {
00227             // keep any inner vertices.
00228             result.AddVertex(verts[v_index]);
00229         }
00230         v_previous = v_index;
00231     }
00232     return result;
00233 }
00234 
00235 std::vector<MeshRemapper::ArrayCoords> A_Polygon::ConvertToQuads()
00236 {
00237     // we want to convert any old polygon to quadrilaterals.
00238     /* If the input was from a TexCoordRemapper, we'll have a rectangle with
00239      * some edges chopped off, this could be empty, or a convex polygon.
00240      * If we got our input from a VertexCoordRemapper it is likely to be (but
00241      * not necessarily) convex.
00242      * This is only guaranteed to work on convex polygons, though it will work 
00243      * some concave ones.
00244      * FIXME Either use something more stable for concave polygons, or allow
00245      * the Coords structure to take arbitrary polygons.
00246      * Warning: The output of the clipping algorithm can create coincident lines
00247      * where it has clipped through something concave. See this:
00248      * http://en.wikipedia.org/wiki/Sutherland-Hodgman_clipping_algorithm
00249      * It would be fine to leave those in if we don't mess up converting it to
00250      * quadrilaterals (for example if sending the polygon directly to OpenGL)
00251      */
00252     std::vector<MeshRemapper::ArrayCoords> result;
00253     unsigned int vertices_count = verts.size();
00254     unsigned int i = 2;
00255     // use vertex 0 for each face, and share another vertex with the last drawn,
00256     // to (at least in convex cases) get the shape we intended, like this:
00257     /*      0+------,7
00258      *      /|\    c \
00259      *   1 / |   \    \ 6
00260      *    |a |     \   |
00261      *    |  |        \|
00262      *   2 \ |   b    / 5
00263      *      \|       /
00264      *      3+------'4
00265      */
00266     while (i < vertices_count)
00267     {
00268         if (i < vertices_count - 1)
00269         {
00270             // make a new quadrilateral
00271             MeshRemapper::ArrayCoords quad;
00272             for (unsigned short int c = 0; c < 2; c++)
00273             {
00274                 quad.vertex_c[0][0][c] = verts[  0  ].vertex_c[c];
00275                 quad.tex_c   [0][0][c] = verts[  0  ].tex_c   [c];
00276                 quad.vertex_c[0][1][c] = verts[i - 1].vertex_c[c];
00277                 quad.tex_c   [0][1][c] = verts[i - 1].tex_c   [c];
00278                 quad.vertex_c[1][1][c] = verts[  i  ].vertex_c[c];
00279                 quad.tex_c   [1][1][c] = verts[  i  ].tex_c   [c];
00280                 quad.vertex_c[1][0][c] = verts[i + 1].vertex_c[c];
00281                 quad.tex_c   [1][0][c] = verts[i + 1].tex_c   [c];
00282             }
00283             result.push_back(quad);
00284             i += 2;
00285         }
00286         else
00287         {
00288             // make a new triangle, but repeat the last vertex so it is a quad
00289             MeshRemapper::ArrayCoords quad;
00290             for (unsigned short int c = 0; c < 2; c++)
00291             {
00292                 quad.vertex_c[0][0][c] = verts[  0  ].vertex_c[c];
00293                 quad.tex_c   [0][0][c] = verts[  0  ].tex_c   [c];
00294                 quad.vertex_c[0][1][c] = verts[i - 1].vertex_c[c];
00295                 quad.tex_c   [0][1][c] = verts[i - 1].tex_c   [c];
00296                 quad.vertex_c[1][1][c] = verts[  i  ].vertex_c[c];
00297                 quad.tex_c   [1][1][c] = verts[  i  ].tex_c   [c];
00298                 quad.vertex_c[1][0][c] = verts[  i  ].vertex_c[c];
00299                 quad.tex_c   [1][0][c] = verts[  i  ].tex_c   [c];
00300             }
00301             result.push_back(quad);
00302             i ++;
00303         }
00304     }
00305     return result;
00306 }
00307 
00308 void MeshRemapper::ClipFace(MeshRemapper::Coords *face)
00309 {
00310     // Clip the given face so that it only shows the cropped region of the
00311     // source image.
00312     // We use the texture coordinates to work out where to crop, and adjust the
00313     // vertices locations accordingly.
00314     
00315     // convert the face to a polygon:
00316     A_Polygon poly(face);
00317     // work out where the clipping lines are, we use the cropping rectangle:
00318     double clip_lines[4][3] = {{ 1.0,  0.0, -crop_x1},
00319                                {-1.0,  0.0,  crop_x2},
00320                                { 0.0,  1.0, -crop_y1},
00321                                { 0.0, -1.0,  crop_y2}};
00322     // cut off each side
00323     poly = poly.Clip(clip_lines[0]);
00324     poly = poly.Clip(clip_lines[1]);
00325     poly = poly.Clip(clip_lines[2]);
00326     poly = poly.Clip(clip_lines[3]);
00327     // Feign circular clipping by trimming to some tangent lines.
00328     /* Work out what angles to clip to based on the locations of the vertices.
00329      * This is generally good, although it will break down where {a huge part /
00330      * all / even more} of the image contained within the input face. However in
00331      * this case the interpolation would make it well out of sync anyway, so
00332      * it's no big deal.
00333      */
00334     /* Since the coordinates are scaled to between 0 and 1, we actually need to
00335      * crop to a elipse to get the aspect ratio right.
00336      */
00337     if (circle_crop)
00338     {
00339         for (unsigned int edge = 0; edge < 4; edge++)
00340         {
00341             double angle = M_PI + atan2(face->tex_c[edge % 2][edge / 2][1]
00342                                                         - circle_crop_centre_y,
00343                                         face->tex_c[edge % 2][edge / 2][0]
00344                                                         - circle_crop_centre_x),
00345                     ac_x = cos(angle) / circle_crop_radius_x,
00346                     ac_y = sin(angle) / circle_crop_radius_y,
00347             clip_line[3] = {ac_x, ac_y, 1.0 - ac_x * circle_crop_centre_x
00348                                             - ac_y * circle_crop_centre_y};
00349             poly = poly.Clip(clip_line);
00350         }
00351     }
00352     // now convert to quadrilaterals.
00353     face_list = poly.ConvertToQuads();
00354 }
00355 
00356 bool MeshRemapper::GiveClipFaceResult(Coords * result)
00357 {
00358     if (face_list.empty())
00359     {
00360         // no more faces
00361         return false;
00362     } else {
00363         // return a face
00364         for (unsigned short int x = 0; x < 2; x++)
00365         {
00366             for (unsigned short int y = 0; y < 2; y++)
00367             {
00368                 for (unsigned short int c = 0; c < 2; c++)
00369                 {
00370                     result->tex_c[x][y][c] = face_list.back().tex_c[x][y][c];
00371                     result->vertex_c[x][y][c] = face_list.back().vertex_c[x][y][c];
00372                 }
00373             }
00374         }
00375         face_list.pop_back();
00376         return true;
00377     }
00378 }
00379 

Generated on 30 Oct 2014 for Hugintrunk by  doxygen 1.4.7