Hugintrunk  0.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
GreatCircles.cpp
Go to the documentation of this file.
1 // -*- c-basic-offset: 4 -*-
25 #ifdef __WXMAC__
26 #include "panoinc_WX.h"
27 #include "panoinc.h"
28 #endif
29 
30 #include "GreatCircles.h"
31 
32 // for some reason #include <GL/gl.h> isn't portable enough.
33 #include <wx/platform.h>
34 #ifdef __WXMAC__
35 #include <OpenGL/gl.h>
36 #else
37 #ifdef __WXMSW__
38 #include <vigra/windows.h>
39 #endif
40 #include <GL/gl.h>
41 #endif
42 
43 // we use the trigonometric functions.
44 #include <cmath>
45 #include <limits>
46 
47 // The number of segments to use in subdivision of the line.
48 // Higher numbers increase the accuracy of the line, but it is slower.
49 // Must be at least two. More is much better.
50 const unsigned int max_segments = 48;
51 const double min_segment_angle = 1.0 / 180.0 * M_PI;
52 
53 GreatCircles::GreatCircles() : m_visualizationState(NULL)
54 {
55 }
56 
58 {
59  m_visualizationState = visualizationStateIn;
60 }
61 
62 void GreatCircles::drawLineFromSpherical(double startLat, double startLong,
63  double endLat, double endLong, double width)
64 {
66  GreatCircleArc(startLat, startLong, endLat, endLong, *m_visualizationState).draw(true, width);
67 }
68 
69 GreatCircleArc::GreatCircleArc() : m_xscale(DBL_MAX), m_visualizationState(NULL)
70 {
71 }
72 
73 GreatCircleArc::GreatCircleArc(double startLat, double startLong,
74  double endLat, double endLong,
75  VisualizationState & visualizationState)
76 
77 {
78  m_visualizationState = &visualizationState;
79  // get the output projection
80  const HuginBase::PanoramaOptions & options = *(visualizationState.GetOptions());
81  // make an image to transform spherical coordinates into the output projection
82  static const HuginBase::SrcPanoImage equirectangularImage(HuginBase::SrcPanoImage::EQUIRECTANGULAR, 360.0, vigra::Size2D(360.0, 180.0));
83  // make a transformation from spherical coordinates to the output projection
85  transform.createInvTransform(equirectangularImage, options);
86 
87  m_xscale = visualizationState.GetScale();
88 
94  if (startLat == 360.0 - endLat && startLong == 180.0 - endLong)
95  {
96  // we should probably check to see if we already go through (180, 90).
97  if (startLat == 180.0 && startLong == 90.0)
98  {
99  // foiled again: pick one going through (180, 0) instead.
100  *this = GreatCircleArc(startLat, startLong, 180.0, 0.0, visualizationState);
101  GreatCircleArc other(180.0, 0.0, endLat, endLong, visualizationState);
102  m_lines.insert(m_lines.end(), other.m_lines.begin(), other.m_lines.end());
103  return;
104  }
105  *this = GreatCircleArc(startLat, startLong, 180.0, 90.0, visualizationState);
106  GreatCircleArc other(180.0, 90.0, endLat, endLong, visualizationState);
107  m_lines.insert(m_lines.end(), other.m_lines.begin(), other.m_lines.end());
108  return;
109  }
110 
111  // convert start and end positions so that they don't go across the +/-180
112  // degree seam
113  if (startLat < 90.0 && endLat > 270.0)
114  {
115  endLat -= 360.0;
116  }
117  // convert to radians
118  startLat *= (M_PI / 180.0);
119  startLong *= (M_PI / 180.0);
120  endLat *= (M_PI / 180.0);
121  endLong *= (M_PI / 180.0);
122 
123  // find sines and cosines, they are used multiple times.
124  double sineStartLat = std::sin(startLat);
125  double sineStartLong = std::sin(startLong);
126  double sineEndLat = std::sin(endLat);
127  double sineEndLong = std::sin(endLong);
128  double cosineStartLat = std::cos(startLat);
129  double cosineStartLong = std::cos(startLong);
130  double cosineEndLat = std::cos(endLat);
131  double cosineEndLong = std::cos(endLong);
132 
133  /* to get points on the great circle, we linearly interpolate between the
134  * two 3D coordinates for the given spherical coordinates, then normalise
135  * the vector to get back on the sphere. This works everywhere except exact
136  * opposite points, where we'll get the original points repeated several
137  * times (and if we are even more unlucky we will hit the origin where the
138  * normal isn't defined.)
139  */
140  // convert locations to 3d coordinates.
141  double p1[3] = {cosineStartLat * sineStartLong, sineStartLat * sineStartLong, cosineStartLong};
142  double p2[3] = {cosineEndLat * sineEndLong, sineEndLat * sineEndLong, cosineEndLong};
143 
145  bool hasSeam = true;
146  // draw a line strip and transform the coordinates as we go.
147  double b1 = 0.0;
148  double b2 = 1.0;
149 
150  const double line_v[3] = {p2[0] - p1[0], p2[1] - p1[1], p2[2] - p1[2]};
151  const double line_length = sqrt(line_v[0] * line_v[0] + line_v[1] * line_v[1] + line_v[2] * line_v[2]);
152  unsigned int segments = hugin_utils::roundi(line_length / min_segment_angle);
153  segments = std::max(2u, std::min(max_segments, segments));
154  const double bDifference = 1.0 / double(segments);
155  // for discontinuity detection.
156  int lastSegment = 1;
157  // The last processed vertex's position.
158  hugin_utils::FDiff2D last_vertex;
159  /* true if we shouldn't use last_vertex to make a line segment
160  * i.e. We just crossed a discontinuity. */
161  bool skip = true;
162  for (unsigned int segment_index = 0; segment_index < segments;
163  segment_index++, b1 += bDifference, b2 -= bDifference)
164  {
165  // linearly interpolate positions
166  double v[3] = {p1[0] * b1 + p2[0] * b2, p1[1] * b1 + p2[1] * b2, p1[2] * b1 + p2[2] * b2};
167  // normalise
168  double length = sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
169  v[0] /= length;
170  v[1] /= length;
171  v[2] /= length;
172  /*double longitude = atan2(numerator,
173  cosineStartLong * (c1 * std::sin(latitude) -
174  c2 * std::cos(latitude)));*/
175  double longitude = std::acos(v[2]);
176  // acos returns values between 0 and M_PI. The other
177  // latitudes are on the back of the sphere, so check y coordinate (v1)
178  double latitude = std::acos(v[0] / std::sin(longitude));
179  if (v[1] < 0.0)
180  {
181  // on the back.
182  latitude = -latitude + 2 * M_PI;
183  }
184 
185  double vx, vy;
186  bool infront = transform.transformImgCoord(vx,
187  vy,
188  latitude * 180.0 / M_PI,
189  longitude * 180.0 / M_PI);
190  // don't draw across +/- 180 degree seems.
191  if (hasSeam)
192  {
193  // we divide the width of the panorama into 3 segments. If we jump
194  // across the middle section, we split the line into two.
195  int newSegment = vx / (options.getWidth() / 3);
196  if ((newSegment < 1 && lastSegment > 1) ||
197  (newSegment > 1 && lastSegment < 1))
198  {
199  skip = true;
200  }
201  lastSegment = newSegment;
202  }
203  if (infront)
204  {
205  if (!skip)
206  {
208  line.vertices[0] = last_vertex;
209  line.vertices[1] = hugin_utils::FDiff2D(vx, vy);
210  m_lines.push_back(line);
211  }
212  // The next line segment should be a valid one.
213  last_vertex = hugin_utils::FDiff2D(vx, vy);
214  skip = false;
215  } else {
216  skip = true;
217  }
218  }
219 }
220 
221 void GreatCircleArc::draw(bool withCross, double width) const
222 {
223  // Just draw all the previously worked out line segments
224  LineSegment * pre;
225  LineSegment * pro;
226  for (unsigned int i = 0 ; i < m_lines.size() ; i++) {
227  if (i != 0) {
228  pre = (LineSegment*) &(m_lines[i-1]);
229  } else {
230  pre = NULL;
231  }
232  if (i != m_lines.size() - 1) {
233  pro = (LineSegment*)&(m_lines[i+1]);
234  } else {
235  pro = NULL;
236  }
237  m_lines[i].doGL(width, m_visualizationState,pre,pro);
238  }
239  // The arc might contain no line segments in some circumstances,
240  // so check for this before drawing crosses at the ends.
241  if(withCross && !m_lines.empty())
242  {
243  double scale = 4 / getxscale();
244  // The scale to draw them: this is 5 pixels outside in every direction.
245  {
246  std::vector<GreatCircleArc::LineSegment>::const_iterator it;
247  it = m_lines.begin();
248  it->doGLcross(0,scale, m_visualizationState);
249 
250  it = m_lines.end();
251  --it; //.end points beyond last point.
252  it->doGLcross(1,scale, m_visualizationState);
253  }
254  };
255 }
256 
258 {
259  double m = (vertices[1].y - vertices[0].y) / (vertices[1].x - vertices[0].x);
260  double b = vertices[1].y - m * vertices[1].x;
261  hugin_utils::FDiff2D rect[4];
262  double d = width / state->GetScale() / 2.0;
263 //DEBUG_DEBUG("drawing lines " << d);
264  double yd = d * sqrt(m*m + 1);
265  double xd = d * sin(atan(m));
266 
267  if (vertices[1].x < vertices[0].x) {
268  yd *= -1;
269  } else {
270  xd *= -1;
271  }
272 
273  //first find out if it is a special case
274 
275  bool vertical = false;
276  if (std::abs(vertices[1].x - vertices[0].x) < 0.00001) {
277  xd = d;
278  if (vertices[1].y > vertices[0].y) {
279  xd *= -1;
280  }
281  yd = 0;
282  vertical = true;
283  }
284 
285  bool horizontal = false;
286  if(std::abs(vertices[1].y - vertices[0].y) < 0.00001) {
287  xd = 0;
288  yd = d;
289  if (vertices[1].x > vertices[0].x) {
290  yd *= -1;
291  }
292  m = 0;
293  b = vertices[0].y;
294  horizontal = true;
295  }
296 
297  //TODO: line segment ends of special cases correspondend to preceding and proceeding segments
298  if (vertical || horizontal) {
299  rect[0].x = vertices[0].x - xd;
300  rect[0].y = vertices[0].y + yd;
301  rect[1].x = vertices[0].x + xd;
302  rect[1].y = vertices[0].y - yd;
303  rect[2].x = vertices[1].x + xd;
304  rect[2].y = vertices[1].y - yd;
305  rect[3].x = vertices[1].x - xd;
306  rect[3].y = vertices[1].y + yd;
307  } else {
308 
309  //The mathematics behind this is to get the equation for the line of the segment in the form of y = m * x + b
310  //then shift the line + - in the y direction to get two lines, and finally do this for the preceding and proceeding line segments and find the intersection
311  //or just make the ends perpendicular
312  bool def = false;
313  if (preceding != NULL) {
314 
315  double m_pre = (preceding->vertices[1].y - preceding->vertices[0].y) / (preceding->vertices[1].x - preceding->vertices[0].x);
316  double b_pre = preceding->vertices[1].y - m_pre * preceding->vertices[1].x;
317 
318  if (m_pre == m) {
319  def = true;
320  } else {
321 
322  double yd_pre = d * sqrt(m_pre*m_pre + 1);
323  if (preceding->vertices[1].x < preceding->vertices[0].x) {
324  yd_pre *= -1;
325  }
326 
327 
328  rect[1].x = ((b_pre + yd_pre) - (b + yd)) / (m - m_pre);
329  rect[1].y = m_pre * rect[1].x + b_pre + yd_pre;
330 
331  rect[0].x = ((b_pre - yd_pre) - (b - yd)) / (m - m_pre);
332  rect[0].y = m_pre * rect[0].x + b_pre - yd_pre;
333  }
334 
335  } else {
336  def = true;
337  }
338 
339  if (def) {
340 
341  //in this case return just a proper rectangle
342  rect[1].x = vertices[0].x + xd;
343  rect[1].y = m * rect[1].x + b + yd;
344 
345  rect[0].x = vertices[0].x - xd;
346  rect[0].y = m * rect[0].x + b - yd;
347 
348  }
349 
350 // DEBUG_DEBUG("");
351 // DEBUG_DEBUG("drawing line " << vertices[0].x << " " << vertices[0].y << " ; " << vertices[1].x << " " << vertices[1].y);
352 // DEBUG_DEBUG("drawing line r " << rect[0].x << " " << rect[0].y << " ; " << rect[1].x << " " << rect[1].y);
353 // DEBUG_DEBUG("drawing line a " << m << " " << b << " yd: " << yd << " xd: " << xd << " ; " << vertices[1].x - vertices[0].x);
354 
355  def = false;
356  if (proceeding != NULL) {
357 
358 
359  double m_pro = (proceeding->vertices[1].y - proceeding->vertices[0].y) / (proceeding->vertices[1].x - proceeding->vertices[0].x);
360  double b_pro = proceeding->vertices[1].y - m_pro * proceeding->vertices[1].x;
361 
362  if (m_pro == m) {
363  def = true;
364  } else {
365 
366  double yd_pro = d * sqrt(m_pro*m_pro + 1);
367  if (proceeding->vertices[1].x < proceeding->vertices[0].x) {
368  yd_pro *= -1;
369  }
370 
371 
372  rect[3].x = ((b_pro - yd_pro) - (b - yd)) / (m - m_pro);
373  rect[3].y = m_pro * rect[3].x + b_pro - yd_pro;
374 
375  rect[2].x = ((b_pro + yd_pro) - (b + yd)) / (m - m_pro);
376  rect[2].y = m_pro * rect[2].x + b_pro + yd_pro;
377 
378  }
379 
380  } else {
381  def = true;
382  }
383 
384  if (def) {
385 
386  rect[3].x = vertices[1].x - xd;
387  rect[3].y = m * rect[3].x + b - yd;
388 
389  rect[2].x = vertices[1].x + xd;
390  rect[2].y = m * rect[2].x + b + yd;
391  }
392 
393 // DEBUG_DEBUG("");
394 // DEBUG_DEBUG("drawing line " << vertices[0].x << " " << vertices[0].y << " ; " << vertices[1].x << " " << vertices[1].y);
395 // DEBUG_DEBUG("drawing line r " << rect[0].x << " " << rect[0].y << " ; " << rect[1].x << " " << rect[1].y);
396 // DEBUG_DEBUG("drawing line r " << rect[2].x << " " << rect[2].y << " ; " << rect[3].x << " " << rect[3].y);
397 
398  }
399 
400 
401 
406  glBegin(GL_QUADS);
407  glVertex3d(res1.x,res1.y,res1.z);
408  glVertex3d(res2.x,res2.y,res2.z);
409  glVertex3d(res3.x,res3.y,res3.z);
410  glVertex3d(res4.x,res4.y,res4.z);
411  glEnd();
412 
413 // glBegin(GL_QUADS);
414 // glVertex3d(rect[0].x,rect[0].y,0);
415 // glVertex3d(rect[1].x,rect[1].y,0);
416 // glVertex3d(rect[2].x,rect[2].y,0);
417 // glVertex3d(rect[3].x,rect[3].y,0);
418 // glEnd();
419 
420 // MeshManager::MeshInfo::Coord3D res1 = state->GetMeshManager()->GetCoord3D((hugin_utils::FDiff2D&)vertices[0]);
421 // MeshManager::MeshInfo::Coord3D res2 = state->GetMeshManager()->GetCoord3D((hugin_utils::FDiff2D&)vertices[1]);
422 // glVertex3d(res1.x,res1.y,res1.z);
423 // glVertex3d(res2.x,res2.y,res2.z);
424 }
425 
426 double GreatCircleArc::getxscale(void) const
427 {
428  return m_xscale;
429 }
430 
431 
432 void GreatCircleArc::LineSegment::doGLcross(int point, double xscale, VisualizationState * state) const
433 {
434  //TODO: cross with polygons instead of lines
435  double vx, vy;
436 
437  vx = vertices[point].x;
438  vy = vertices[point].y;
439 
440  hugin_utils::FDiff2D p1(vx - xscale, vy - xscale);
441  hugin_utils::FDiff2D p2(vx + xscale, vy + xscale);
442 
443  hugin_utils::FDiff2D p3(vx - xscale, vy + xscale);
444  hugin_utils::FDiff2D p4(vx + xscale, vy - xscale);
445 
448 
451 
452  glBegin(GL_LINES);
453 
454  // main diagonal
455  glVertex3f(res1.x,res1.y,res1.z);
456  glVertex3f(res2.x,res2.y,res2.z);
457  // second diagonal
458  glVertex3f(res3.x,res3.y,res3.z);
459  glVertex3f(res4.x,res4.y,res4.z);
460 
461  glEnd();
462 }
463 
464 
466 {
467  // find the minimal distance.
468  float distance = std::numeric_limits<float>::max();
469  for (std::vector<GreatCircleArc::LineSegment>::const_iterator it = m_lines.begin();
470  it != m_lines.end();
471  ++it)
472  {
473  float this_distance = it->squareDistance(point);
474  if (this_distance < distance)
475  {
476  distance = this_distance;
477  }
478  }
479  return distance;
480 }
481 
483 {
484  // minimal distance between a point and a line segment
485 
486  // Does the line segment start and end in the same place?
487  if (vertices[0] == vertices[1])
488 
489  {
490  // yes, so return the distance to the point where the 'line' segment is.
491  return point.squareDistance(vertices[0]);
492  }
493  // the vector along the line segment.
494  hugin_utils::FDiff2D line_vector = vertices[1] - vertices[0];
495  // the parameter indicating the point's nearest position along the line.
496  float t = line_vector.x == 0 ?
497  (point.y - vertices[0].y) / line_vector.y
498  : (point.x - vertices[0].x) / line_vector.x;
499  // check if the point lies along the line segment.
500  if (t <= 0.0)
501  {
502  // no, nearest vertices[0].
503  return vertices[0].squareDistance(point);
504  }
505  if (t >= 1.0)
506  {
507  // no, nearest vertices[1].
508  return vertices[1].squareDistance(point);
509  }
510 
511  // Find the intersection with the line segment and the tangent line.
512  hugin_utils::FDiff2D intersection(vertices[0] + line_vector * t);
513  // Finally, find the distance between the intersection and the point.
514  return intersection.squareDistance(point);
515 }
516 
517 
const double min_segment_angle
implementation of huginApp Class
void drawLineFromSpherical(double startLat, double startLong, double endLat, double endLong, double width=1.0)
Draw the shortest segment of the great circle crossing two spherical coordinates. ...
GreatCircles()
constructor
int roundi(T x)
Definition: hugin_math.h:73
double getxscale() const
void doGL(double width, VisualizationState *state, LineSegment *preceding=NULL, LineSegment *proceeding=NULL) const
Draw a meshed line.
VisualizationState * m_visualizationState
Definition: GreatCircles.h:64
void doGLcross(int point, double cscale, VisualizationState *state) const
Specify the line to OpenGL. Must be within a glBegin/glEnd pair.
#define DEBUG_ASSERT(cond)
Definition: utils.h:80
include file for the hugin project
static char * line
Definition: svm.cpp:2784
VisualizationState * m_visualizationState
Definition: GreatCircles.h:112
void setVisualizationState(VisualizationState *visualizationState)
Set the ViewState to use for information on output projection and preview display.
MeshManager * GetMeshManager()
Definition: ViewState.h:207
void createInvTransform(const vigra::Diff2D &srcSize, VariableMap srcVars, Lens::LensProjectionFormat srcProj, const vigra::Diff2D &destSize, PanoramaOptions::ProjectionFormat destProj, const std::vector< double > &destProjParam, double destHFOV, const vigra::Diff2D &origSrcSize)
create image-&gt;pano transformation
const unsigned int max_segments
Declare GreatCircles class.
virtual MeshInfo::Coord3D GetCoord3D(hugin_utils::FDiff2D &)=0
GreatCircleArc()
Create a bad great circle arc.
TDiff2D< double > FDiff2D
Definition: hugin_math.h:150
hugin_utils::FDiff2D vertices[2]
Definition: GreatCircles.h:95
unsigned int getWidth() const
include file for the hugin project
bool transformImgCoord(double &x_dest, double &y_dest, double x_src, double y_src) const
like transform, but return image coordinates, not cartesian coordinates
std::vector< LineSegment > m_lines
Definition: GreatCircles.h:111
virtual HuginBase::PanoramaOptions * GetOptions()
Definition: ViewState.cpp:468
static T max(T x, T y)
Definition: svm.cpp:65
Holds transformations for Image -&gt; Pano and the other way.
float squareDistance(hugin_utils::FDiff2D point) const
Return the square of the minimal distance between the great circle arc and a coorinate on the panoram...
All variables of a source image.
Definition: SrcPanoImage.h:194
Panorama image options.
#define M_PI
Definition: GaborFilter.cpp:34
static T min(T x, T y)
Definition: svm.cpp:62
a class to handle a 3D point
Definition: MeshManager.h:93
void draw(bool withCross=true, double width=1.0) const
Draw the great circle arc on the fast preview.
float squareDistance(hugin_utils::FDiff2D point) const
Get the square of the minimal distance to a point.
T squareDistance(TDiff2D< T > other) const
Return square of the distance to another point.
Definition: hugin_math.h:135