Hugintrunk  0.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
MeshRemapper.cpp
Go to the documentation of this file.
1 // -*- c-basic-offset: 4 -*-
2 
23 #include "panoinc_WX.h"
24 #include "panoinc.h"
25 
26 #include <math.h>
27 #include "MeshRemapper.h"
28 #include "ViewState.h"
29 
31  HuginBase::SrcPanoImage * image_in,
32  VisualizationState *visualization_state_in)
33 {
34  m_pano = m_pano_in;
35  image = image_in;
36  visualization_state = visualization_state_in;
37 }
38 
40 {
41 }
42 
44 {
45  // we want to make a remapped mesh, get some generic information:
46 // const HuginBase::SrcPanoImage *src = visualization_state->GetSrcImage(image_number);
47  // get the size of the image.
48  width = (double) image->getSize().width();
49  height = (double) image->getSize().height();
50 
51  // use the scale to determine edge lengths in pixels for any
52  // resolution selection.
54  // It is up to the child classes to determine what to do here. They should
55  // probably use set up transform and use it to fill some data structure that
56  // stores coordinates.
57 }
58 
60 {
61 // const HuginBase::SrcPanoImage *src = visualization_state->GetSrcImage(image_number);
62  crop_mode = image->getCropMode();
63  crop_x1 = (double) image->getCropRect().left() / width;
64  crop_x2 = (double) image->getCropRect().right() / width;
65  crop_y1 = (double) image->getCropRect().top() / height;
66  crop_y2 = (double) image->getCropRect().bottom() / height;
67  // set variables for circular crop.
69  if (circle_crop)
70  {
73  double crop_width_px = (double) image->getCropRect().width(),
74  crop_hieght_px = (double) image->getCropRect().height(),
75  crop_radius = (crop_width_px < crop_hieght_px ?
76  crop_width_px : crop_hieght_px) / 2.0;
77  circle_crop_radius_x = crop_radius / width;
78  circle_crop_radius_y = crop_radius / height;
79  }
80  // hugin allows negative cropping regions, but we are expected to only
81  // output the regions that exist in the original image, so clamp the values:
82  if (crop_x1 < 0.0) crop_x1 = 0.0; if (crop_x1 > 1.0) crop_x1 = 1.0;
83  if (crop_x2 < 0.0) crop_x2 = 0.0; if (crop_x2 > 1.0) crop_x2 = 1.0;
84  if (crop_y1 < 0.0) crop_y1 = 0.0; if (crop_y1 > 1.0) crop_y1 = 1.0;
85  if (crop_y2 < 0.0) crop_y2 = 0.0; if (crop_y2 > 1.0) crop_y2 = 1.0;
86 }
87 
88 /******************************************************************************
89  * Clipping stuff *
90  ******************************************************************************/
91 
92 // a vertex has vertex coordinates and a texture coordinates.
93 class Vertex
94 {
95 public:
96  Vertex(double vx, double vy, double tx, double ty);
97  Vertex() {}
98  double vertex_c[2];
99  double tex_c[2];
100 };
101 
102 Vertex::Vertex(double vx, double vy, double tx, double ty)
103 {
104  vertex_c[0] = vx;
105  vertex_c[1] = vy;
106  tex_c[0] = tx;
107  tex_c[1] = ty;
108 }
109 
110 // The A_Polygon class stores an arbitrary polygon, and can give you another
111 // polygon that is the result of clipping it with some line.
112 // hmmm... Using the name "Polygon" causes errors on windows, hence A_Polgon.
114 {
115 public:
116  // Create the polygon from a quadrilateral
117  explicit A_Polygon(MeshRemapper::Coords *face);
119  // clip lines are the line defined by x * p[0] + y * p[1] + p[2] = 0.
120  // inside is the side where x * p[0] + y *p[1] + p[2] > 0.
121  A_Polygon Clip(const double clip_line[3]);
122  // add a vertex on the end of the list. Used to build the polygon when not
123  // created from a quad.
124  inline void AddVertex(Vertex v);
125  // convert to a list of quads. A bit approximate.
126  std::vector<MeshRemapper::ArrayCoords> ConvertToQuads();
127 private:
128  // our list of vertices
129  std::vector<Vertex> verts;
130  // Test which side of the clip plane we are on
131  inline bool Inside(const unsigned int vertex,
132  const double clip_line[3]) const;
133  // given that the edge between the specified vertices crosses the clip line,
134  // find where they cross and set the vertex and texture coordinates there.
135  Vertex Intersect(const unsigned int v1, const unsigned int v2,
136  const double clip_line[3]) const;
137 };
138 
140 {
141  verts.resize(4);
142  for (unsigned short int v = 0; v < 4; v++)
143  {
144  // give verticies counter-clockwise
145  unsigned short int x = (v == 2 || v==3) ? 1 : 0,
146  y = (v == 1 || v==2) ? 1 : 0;
147  for (unsigned short int a = 0; a < 2; a++)
148  {
149  verts[v].vertex_c[a] = face->vertex_c[x][y][a];
150  verts[v].tex_c[a] = face->tex_c[x][y][a];
151  }
152  }
153 }
154 
156 {
157  verts.push_back(v);
158 }
159 
160 inline bool A_Polygon::Inside(const unsigned int v, const double l[3]) const
161 {
162  return verts[v].tex_c[0] * l[0] + verts[v].tex_c[1] * l[1] + l[2] > 0;
163 }
164 
165 Vertex A_Polygon::Intersect(const unsigned int v1_index,
166  const unsigned int v2_index,
167  const double cl[3]) const
168 {
169  // find the point of intersection from the given edge with a clip plane.
170  // the edge is the vertex with the same number and the one before it.
171  // Get pointers to the vertecies we will use
172  const Vertex *v1 = &verts[v1_index],
173  // the vertex before is the last one if we at the beginning.
174  *v2 = &verts[v2_index];
175  // find a vector along the path of the edge we are clipping
176  double dx = v2->tex_c[0] - v1->tex_c[0],
177  dy = v2->tex_c[1] - v1->tex_c[1];
178  // The line along the edge we clip is defined by (x,y) = v1 + t(dx, dy)
179  // The line of the clipping plane is cl[0] * x + cl[1] * y + cl[2] = 0
180  // now find what value of t that is on both lines.
181  // substitute x and y from the edge line into the equation of the clip line:
182  // cl[0] * (v1_x + t * dx) + cl[1] * (v1_y + t * dy) + cl[2] = 0
183  // then rearrange to get t:
184  // cl[0] * v1_x + t * cl[0] * dx + cl[1] * v1_y + t * cl[1] * dy + cl[2] = 0
185  // t * (cl[0] * dx + cl[1] * dy) + cl[0] * v1_x + cl[1] * v1_y + cl[2] = 0
186  // t * (cl[0] * dx + cl[1] * dy) = -cl[0] * v1_x - cl[1] * v1_y - cl[2]
187  // so we get:
188  /* FIXME this assertion sometimes fails. t should always be between 0 and 1,
189  * but sometimes isn't even when this one passes:
190  */
191  // DEBUG_ASSERT(cl[0] * dx + cl[1] * dy);
192  double t = (-cl[0] * v1->tex_c[0] - cl[1] * v1->tex_c[1] - cl[2]) /
193  (cl[0] * dx + cl[1] * dy),
194  // now substitute t into the edge we are testing's line:
195  x = dx * t + v1->tex_c[0],
196  y = dy * t + v1->tex_c[1],
197  // move the vertex coordinates to match the texture ones.
198  // t = 0 would give v1, t = 1 would give v2; so we linearly interpolate by t:
199  td1 = 1.0 - t,
200  xc = v1->vertex_c[0] * td1 + v2->vertex_c[0] * t,
201  yc = v1->vertex_c[1] * td1 + v2->vertex_c[1] * t;
202  // DEBUG_ASSERT(-0.1 <= t && t <= 1.1);
203  return Vertex(xc, yc, x, y);
204 }
205 
206 A_Polygon A_Polygon::Clip(const double clip_line[3])
207 {
208  // We'll use the Sutherland-Hodgman clipping algorithm.
209  // see http://en.wikipedia.org/wiki/Sutherland-Hodgeman
210  A_Polygon result;
211  unsigned int vertices_count = verts.size(),
212  v_previous = vertices_count - 1;
213  // we want to examine all edges in turn, and find the vertices to keep:
214  for (unsigned int v_index = 0; v_index < vertices_count; v_index++)
215  {
216  bool v_index_inside = Inside(v_index, clip_line),
217  v_previous_inside = Inside(v_previous, clip_line);
218  if (v_index_inside != v_previous_inside)
219  {
220  // one in, one out, therefore the edge instersts the clip line.
221  result.AddVertex(Intersect(v_previous, v_index, clip_line));
222  }
223  if (v_index_inside)
224  {
225  // keep any inner vertices.
226  result.AddVertex(verts[v_index]);
227  }
228  v_previous = v_index;
229  }
230  return result;
231 }
232 
233 std::vector<MeshRemapper::ArrayCoords> A_Polygon::ConvertToQuads()
234 {
235  // we want to convert any old polygon to quadrilaterals.
236  /* If the input was from a TexCoordRemapper, we'll have a rectangle with
237  * some edges chopped off, this could be empty, or a convex polygon.
238  * If we got our input from a VertexCoordRemapper it is likely to be (but
239  * not necessarily) convex.
240  * This is only guaranteed to work on convex polygons, though it will work
241  * some concave ones.
242  * FIXME Either use something more stable for concave polygons, or allow
243  * the Coords structure to take arbitrary polygons.
244  * Warning: The output of the clipping algorithm can create coincident lines
245  * where it has clipped through something concave. See this:
246  * http://en.wikipedia.org/wiki/Sutherland-Hodgman_clipping_algorithm
247  * It would be fine to leave those in if we don't mess up converting it to
248  * quadrilaterals (for example if sending the polygon directly to OpenGL)
249  */
250  std::vector<MeshRemapper::ArrayCoords> result;
251  unsigned int vertices_count = verts.size();
252  unsigned int i = 2;
253  // use vertex 0 for each face, and share another vertex with the last drawn,
254  // to (at least in convex cases) get the shape we intended, like this:
255  /* 0+------,7
256  * /|\ c \
257  * 1 / | \ \ 6
258  * |a | \ |
259  * | | \|
260  * 2 \ | b / 5
261  * \| /
262  * 3+------'4
263  */
264  while (i < vertices_count)
265  {
266  if (i < vertices_count - 1)
267  {
268  // make a new quadrilateral
270  for (unsigned short int c = 0; c < 2; c++)
271  {
272  quad.vertex_c[0][0][c] = verts[ 0 ].vertex_c[c];
273  quad.tex_c [0][0][c] = verts[ 0 ].tex_c [c];
274  quad.vertex_c[0][1][c] = verts[i - 1].vertex_c[c];
275  quad.tex_c [0][1][c] = verts[i - 1].tex_c [c];
276  quad.vertex_c[1][1][c] = verts[ i ].vertex_c[c];
277  quad.tex_c [1][1][c] = verts[ i ].tex_c [c];
278  quad.vertex_c[1][0][c] = verts[i + 1].vertex_c[c];
279  quad.tex_c [1][0][c] = verts[i + 1].tex_c [c];
280  }
281  result.push_back(quad);
282  i += 2;
283  }
284  else
285  {
286  // make a new triangle, but repeat the last vertex so it is a quad
288  for (unsigned short int c = 0; c < 2; c++)
289  {
290  quad.vertex_c[0][0][c] = verts[ 0 ].vertex_c[c];
291  quad.tex_c [0][0][c] = verts[ 0 ].tex_c [c];
292  quad.vertex_c[0][1][c] = verts[i - 1].vertex_c[c];
293  quad.tex_c [0][1][c] = verts[i - 1].tex_c [c];
294  quad.vertex_c[1][1][c] = verts[ i ].vertex_c[c];
295  quad.tex_c [1][1][c] = verts[ i ].tex_c [c];
296  quad.vertex_c[1][0][c] = verts[ i ].vertex_c[c];
297  quad.tex_c [1][0][c] = verts[ i ].tex_c [c];
298  }
299  result.push_back(quad);
300  i ++;
301  }
302  }
303  return result;
304 }
305 
307 {
308  // Clip the given face so that it only shows the cropped region of the
309  // source image.
310  // We use the texture coordinates to work out where to crop, and adjust the
311  // vertices locations accordingly.
312 
313  // convert the face to a polygon:
314  A_Polygon poly(face);
315  // work out where the clipping lines are, we use the cropping rectangle:
316  double clip_lines[4][3] = {{ 1.0, 0.0, -crop_x1},
317  {-1.0, 0.0, crop_x2},
318  { 0.0, 1.0, -crop_y1},
319  { 0.0, -1.0, crop_y2}};
320  // cut off each side
321  poly = poly.Clip(clip_lines[0]);
322  poly = poly.Clip(clip_lines[1]);
323  poly = poly.Clip(clip_lines[2]);
324  poly = poly.Clip(clip_lines[3]);
325  // Feign circular clipping by trimming to some tangent lines.
326  /* Work out what angles to clip to based on the locations of the vertices.
327  * This is generally good, although it will break down where {a huge part /
328  * all / even more} of the image contained within the input face. However in
329  * this case the interpolation would make it well out of sync anyway, so
330  * it's no big deal.
331  */
332  /* Since the coordinates are scaled to between 0 and 1, we actually need to
333  * crop to a elipse to get the aspect ratio right.
334  */
335  if (circle_crop)
336  {
337  for (unsigned int edge = 0; edge < 4; edge++)
338  {
339  double angle = M_PI + atan2(face->tex_c[edge % 2][edge / 2][1]
341  face->tex_c[edge % 2][edge / 2][0]
343  ac_x = cos(angle) / circle_crop_radius_x,
344  ac_y = sin(angle) / circle_crop_radius_y,
345  clip_line[3] = {ac_x, ac_y, 1.0 - ac_x * circle_crop_centre_x
346  - ac_y * circle_crop_centre_y};
347  poly = poly.Clip(clip_line);
348  }
349  }
350  // now convert to quadrilaterals.
351  face_list = poly.ConvertToQuads();
352 }
353 
355 {
356  if (face_list.empty())
357  {
358  // no more faces
359  return false;
360  } else {
361  // return a face
362  for (unsigned short int x = 0; x < 2; x++)
363  {
364  for (unsigned short int y = 0; y < 2; y++)
365  {
366  for (unsigned short int c = 0; c < 2; c++)
367  {
368  result->tex_c[x][y][c] = face_list.back().tex_c[x][y][c];
369  result->vertex_c[x][y][c] = face_list.back().vertex_c[x][y][c];
370  }
371  }
372  }
373  face_list.pop_back();
374  return true;
375  }
376 }
377 
bool GiveClipFaceResult(Coords *result)
Get a face that was produced by ClipFace.
double crop_x1
Definition: MeshRemapper.h:100
double circle_crop_centre_y
Definition: MeshRemapper.h:100
double crop_x2
Definition: MeshRemapper.h:100
std::vector< MeshRemapper::ArrayCoords > face_list
List for results when clipping faces.
Definition: MeshRemapper.h:118
double tex_c[2]
std::vector< MeshRemapper::ArrayCoords > ConvertToQuads()
A class for exchanging pointers to coordinates.
Definition: MeshRemapper.h:60
HuginBase::SrcPanoImage * image
Definition: MeshRemapper.h:85
HuginBase::SrcPanoImage::CropMode crop_mode
Crop mode of the source image.
Definition: MeshRemapper.h:99
virtual void UpdateAndResetIndex()
A_Polygon Clip(const double clip_line[3])
include file for the hugin project
double(* tex_c)[2][2]
The coordinate in the source image ranging from 0 to 1.
Definition: MeshRemapper.h:64
VisualizationState * visualization_state
Definition: MeshRemapper.h:83
Model for a panorama.
Definition: Panorama.h:152
Vertex Intersect(const unsigned int v1, const unsigned int v2, const double clip_line[3]) const
void ClipFace(Coords *face)
Crop a face to the source image, return true if there is anything left.
void SetCrop()
Fill the crop values of the MeshRemapper from the source image.
double height
The sizes of the input images in pixels.
Definition: MeshRemapper.h:94
double vertex_c[2]
double crop_y1
Definition: MeshRemapper.h:100
float scale
The number number of units between vertex coorinates that gives a pixel in the display.
Definition: MeshRemapper.h:89
double circle_crop_radius_x
Definition: MeshRemapper.h:100
double circle_crop_radius_y
Definition: MeshRemapper.h:100
MeshRemapper(HuginBase::Panorama *m_pano, HuginBase::SrcPanoImage *image, VisualizationState *visualization_state)
include file for the hugin project
double vertex_c[2][2][2]
Definition: MeshRemapper.h:75
void AddVertex(Vertex v)
double width
Definition: MeshRemapper.h:94
double tex_c[2][2][2]
Definition: MeshRemapper.h:74
double circle_crop_centre_x
Definition: MeshRemapper.h:100
double crop_y2
Definition: MeshRemapper.h:100
bool Inside(const unsigned int vertex, const double clip_line[3]) const
std::vector< Vertex > verts
double(* vertex_c)[2][2]
The coordinate in the panorama, in its pixel space.
Definition: MeshRemapper.h:66
All variables of a source image.
Definition: SrcPanoImage.h:194
#define M_PI
Definition: GaborFilter.cpp:34
HuginBase::Panorama * m_pano
Definition: MeshRemapper.h:84
virtual ~MeshRemapper()
A class for exchanging coordinates by value.
Definition: MeshRemapper.h:70