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