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, bool straightLine)
64 {
66  if (straightLine)
67  {
68  StraightLineFromSphericals(startLat, startLong, endLat, endLong, *m_visualizationState).draw(true, width);
69  }
70  else
71  {
72  GreatCircleArc(startLat, startLong, endLat, endLong, *m_visualizationState).draw(true, width);
73  };
74 }
75 
76 GreatCircleArc::GreatCircleArc() : m_xscale(DBL_MAX), m_visualizationState(NULL)
77 {
78 }
79 
80 GreatCircleArc::GreatCircleArc(double startLat, double startLong,
81  double endLat, double endLong,
82  VisualizationState & visualizationState)
83 
84 {
85  m_visualizationState = &visualizationState;
86  // get the output projection
87  const HuginBase::PanoramaOptions & options = *(visualizationState.GetOptions());
88  // make an image to transform spherical coordinates into the output projection
89  static const HuginBase::SrcPanoImage equirectangularImage(HuginBase::SrcPanoImage::EQUIRECTANGULAR, 360.0, vigra::Size2D(360.0, 180.0));
90  // make a transformation from spherical coordinates to the output projection
92  transform.createInvTransform(equirectangularImage, options);
93 
94  m_xscale = visualizationState.GetScale();
95 
101  if (startLat == 360.0 - endLat && startLong == 180.0 - endLong)
102  {
103  // we should probably check to see if we already go through (180, 90).
104  if (startLat == 180.0 && startLong == 90.0)
105  {
106  // foiled again: pick one going through (180, 0) instead.
107  *this = GreatCircleArc(startLat, startLong, 180.0, 0.0, visualizationState);
108  GreatCircleArc other(180.0, 0.0, endLat, endLong, visualizationState);
109  m_lines.insert(m_lines.end(), other.m_lines.begin(), other.m_lines.end());
110  return;
111  }
112  *this = GreatCircleArc(startLat, startLong, 180.0, 90.0, visualizationState);
113  GreatCircleArc other(180.0, 90.0, endLat, endLong, visualizationState);
114  m_lines.insert(m_lines.end(), other.m_lines.begin(), other.m_lines.end());
115  return;
116  }
117 
118  // convert start and end positions so that they don't go across the +/-180
119  // degree seam
120  if (startLat < 90.0 && endLat > 270.0)
121  {
122  endLat -= 360.0;
123  }
124  // convert to radians
125  startLat *= (M_PI / 180.0);
126  startLong *= (M_PI / 180.0);
127  endLat *= (M_PI / 180.0);
128  endLong *= (M_PI / 180.0);
129 
130  // find sines and cosines, they are used multiple times.
131  const double sineStartLat = std::sin(startLat);
132  const double sineStartLong = std::sin(startLong);
133  const double sineEndLat = std::sin(endLat);
134  const double sineEndLong = std::sin(endLong);
135  const double cosineStartLat = std::cos(startLat);
136  const double cosineStartLong = std::cos(startLong);
137  const double cosineEndLat = std::cos(endLat);
138  const double cosineEndLong = std::cos(endLong);
139 
140  /* to get points on the great circle, we linearly interpolate between the
141  * two 3D coordinates for the given spherical coordinates, then normalise
142  * the vector to get back on the sphere. This works everywhere except exact
143  * opposite points, where we'll get the original points repeated several
144  * times (and if we are even more unlucky we will hit the origin where the
145  * normal isn't defined.)
146  */
147  // convert locations to 3d coordinates.
148  double p1[3] = {cosineStartLat * sineStartLong, sineStartLat * sineStartLong, cosineStartLong};
149  double p2[3] = {cosineEndLat * sineEndLong, sineEndLat * sineEndLong, cosineEndLong};
150 
152  bool hasSeam = true;
153  // draw a line strip and transform the coordinates as we go.
154  double b = 0.0;
155 
156  const double line_v[3] = {p2[0] - p1[0], p2[1] - p1[1], p2[2] - p1[2]};
157  const double line_length = sqrt(line_v[0] * line_v[0] + line_v[1] * line_v[1] + line_v[2] * line_v[2]);
158  unsigned int segments = hugin_utils::roundi(line_length / min_segment_angle);
159  segments = std::max(2u, std::min(max_segments, segments));
160  const double bDifference = 1.0 / (segments - 1.0);
161  // for discontinuity detection.
162  int lastSegment = 1;
163  // The last processed vertex's position.
164  hugin_utils::FDiff2D last_vertex;
165  /* true if we shouldn't use last_vertex to make a line segment
166  * i.e. We just crossed a discontinuity. */
167  bool skip = true;
168  for (unsigned int segment_index = 0; segment_index < segments;
169  segment_index++, b += bDifference)
170  {
171  // linearly interpolate positions
172  double v[3] = {p1[0] * b + p2[0] * (1.0 - b), p1[1] * b + p2[1] * (1.0 - b), p1[2] * b + p2[2] * (1.0 - b)};
173  // normalise
174  double length = sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
175  v[0] /= length;
176  v[1] /= length;
177  v[2] /= length;
178  /*double longitude = atan2(numerator,
179  cosineStartLong * (c1 * std::sin(latitude) -
180  c2 * std::cos(latitude)));*/
181  double longitude = std::acos(v[2]);
182  // acos returns values between 0 and M_PI. The other
183  // latitudes are on the back of the sphere, so check y coordinate (v1)
184  double latitude = std::acos(v[0] / std::sin(longitude));
185  if (v[1] < 0.0)
186  {
187  // on the back.
188  latitude = -latitude + 2 * M_PI;
189  }
190 
191  double vx, vy;
192  bool infront = transform.transformImgCoord(vx,
193  vy,
194  latitude * 180.0 / M_PI,
195  longitude * 180.0 / M_PI);
196  // don't draw across +/- 180 degree seems.
197  if (hasSeam)
198  {
199  // we divide the width of the panorama into 3 segments. If we jump
200  // across the middle section, we split the line into two.
201  int newSegment = vx / (options.getWidth() / 3);
202  if ((newSegment < 1 && lastSegment > 1) ||
203  (newSegment > 1 && lastSegment < 1))
204  {
205  skip = true;
206  }
207  lastSegment = newSegment;
208  }
209  if (infront)
210  {
211  if (!skip)
212  {
214  line.vertices[0] = last_vertex;
215  line.vertices[1] = hugin_utils::FDiff2D(vx, vy);
216  m_lines.push_back(line);
217  }
218  // The next line segment should be a valid one.
219  last_vertex = hugin_utils::FDiff2D(vx, vy);
220  skip = false;
221  } else {
222  skip = true;
223  }
224  }
225 }
226 
227 void GreatCircleArc::draw(bool withCross, double width) const
228 {
229  // Just draw all the previously worked out line segments
230  LineSegment * pre;
231  LineSegment * pro;
232  for (unsigned int i = 0 ; i < m_lines.size() ; i++) {
233  if (i != 0) {
234  pre = (LineSegment*) &(m_lines[i-1]);
235  } else {
236  pre = NULL;
237  }
238  if (i != m_lines.size() - 1) {
239  pro = (LineSegment*)&(m_lines[i+1]);
240  } else {
241  pro = NULL;
242  }
243  m_lines[i].doGL(width, m_visualizationState,pre,pro);
244  }
245  // The arc might contain no line segments in some circumstances,
246  // so check for this before drawing crosses at the ends.
247  if(withCross && !m_lines.empty())
248  {
249  double scale = 4 / getxscale();
250  // The scale to draw them: this is 5 pixels outside in every direction.
251  {
252  m_lines.begin()->doGLcross(0,scale, m_visualizationState);
253  m_lines.rbegin()->doGLcross(1,scale, m_visualizationState);
254  }
255  };
256 }
257 
259 {
260  double m = (vertices[1].y - vertices[0].y) / (vertices[1].x - vertices[0].x);
261  double b = vertices[1].y - m * vertices[1].x;
262  hugin_utils::FDiff2D rect[4];
263  double d = width / state->GetScale() / 2.0;
264 //DEBUG_DEBUG("drawing lines " << d);
265  double yd = d * sqrt(m*m + 1);
266  double xd = d * sin(atan(m));
267 
268  if (vertices[1].x < vertices[0].x) {
269  yd *= -1;
270  } else {
271  xd *= -1;
272  }
273 
274  //first find out if it is a special case
275 
276  bool vertical = false;
277  if (std::abs(vertices[1].x - vertices[0].x) < 0.00001) {
278  xd = d;
279  if (vertices[1].y > vertices[0].y) {
280  xd *= -1;
281  }
282  yd = 0;
283  vertical = true;
284  }
285 
286  bool horizontal = false;
287  if(std::abs(vertices[1].y - vertices[0].y) < 0.00001) {
288  xd = 0;
289  yd = d;
290  if (vertices[1].x > vertices[0].x) {
291  yd *= -1;
292  }
293  m = 0;
294  b = vertices[0].y;
295  horizontal = true;
296  }
297 
298  //TODO: line segment ends of special cases correspondend to preceding and proceeding segments
299  if (vertical || horizontal) {
300  rect[0].x = vertices[0].x - xd;
301  rect[0].y = vertices[0].y + yd;
302  rect[1].x = vertices[0].x + xd;
303  rect[1].y = vertices[0].y - yd;
304  rect[2].x = vertices[1].x + xd;
305  rect[2].y = vertices[1].y - yd;
306  rect[3].x = vertices[1].x - xd;
307  rect[3].y = vertices[1].y + yd;
308  } else {
309 
310  //The mathematics behind this is to get the equation for the line of the segment in the form of y = m * x + b
311  //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
312  //or just make the ends perpendicular
313  bool def = false;
314  if (preceding != NULL) {
315 
316  double m_pre = (preceding->vertices[1].y - preceding->vertices[0].y) / (preceding->vertices[1].x - preceding->vertices[0].x);
317  double b_pre = preceding->vertices[1].y - m_pre * preceding->vertices[1].x;
318 
319  if (m_pre == m) {
320  def = true;
321  } else {
322 
323  double yd_pre = d * sqrt(m_pre*m_pre + 1);
324  if (preceding->vertices[1].x < preceding->vertices[0].x) {
325  yd_pre *= -1;
326  }
327 
328 
329  rect[1].x = ((b_pre + yd_pre) - (b + yd)) / (m - m_pre);
330  rect[1].y = m_pre * rect[1].x + b_pre + yd_pre;
331 
332  rect[0].x = ((b_pre - yd_pre) - (b - yd)) / (m - m_pre);
333  rect[0].y = m_pre * rect[0].x + b_pre - yd_pre;
334  }
335 
336  } else {
337  def = true;
338  }
339 
340  if (def) {
341 
342  //in this case return just a proper rectangle
343  rect[1].x = vertices[0].x + xd;
344  rect[1].y = m * rect[1].x + b + yd;
345 
346  rect[0].x = vertices[0].x - xd;
347  rect[0].y = m * rect[0].x + b - yd;
348 
349  }
350 
351 // DEBUG_DEBUG("");
352 // DEBUG_DEBUG("drawing line " << vertices[0].x << " " << vertices[0].y << " ; " << vertices[1].x << " " << vertices[1].y);
353 // DEBUG_DEBUG("drawing line r " << rect[0].x << " " << rect[0].y << " ; " << rect[1].x << " " << rect[1].y);
354 // DEBUG_DEBUG("drawing line a " << m << " " << b << " yd: " << yd << " xd: " << xd << " ; " << vertices[1].x - vertices[0].x);
355 
356  def = false;
357  if (proceeding != NULL) {
358 
359 
360  double m_pro = (proceeding->vertices[1].y - proceeding->vertices[0].y) / (proceeding->vertices[1].x - proceeding->vertices[0].x);
361  double b_pro = proceeding->vertices[1].y - m_pro * proceeding->vertices[1].x;
362 
363  if (m_pro == m) {
364  def = true;
365  } else {
366 
367  double yd_pro = d * sqrt(m_pro*m_pro + 1);
368  if (proceeding->vertices[1].x < proceeding->vertices[0].x) {
369  yd_pro *= -1;
370  }
371 
372 
373  rect[3].x = ((b_pro - yd_pro) - (b - yd)) / (m - m_pro);
374  rect[3].y = m_pro * rect[3].x + b_pro - yd_pro;
375 
376  rect[2].x = ((b_pro + yd_pro) - (b + yd)) / (m - m_pro);
377  rect[2].y = m_pro * rect[2].x + b_pro + yd_pro;
378 
379  }
380 
381  } else {
382  def = true;
383  }
384 
385  if (def) {
386 
387  rect[3].x = vertices[1].x - xd;
388  rect[3].y = m * rect[3].x + b - yd;
389 
390  rect[2].x = vertices[1].x + xd;
391  rect[2].y = m * rect[2].x + b + yd;
392  }
393 
394 // DEBUG_DEBUG("");
395 // DEBUG_DEBUG("drawing line " << vertices[0].x << " " << vertices[0].y << " ; " << vertices[1].x << " " << vertices[1].y);
396 // DEBUG_DEBUG("drawing line r " << rect[0].x << " " << rect[0].y << " ; " << rect[1].x << " " << rect[1].y);
397 // DEBUG_DEBUG("drawing line r " << rect[2].x << " " << rect[2].y << " ; " << rect[3].x << " " << rect[3].y);
398 
399  }
400 
401 
402 
407  glBegin(GL_QUADS);
408  glVertex3d(res1.x,res1.y,res1.z);
409  glVertex3d(res2.x,res2.y,res2.z);
410  glVertex3d(res3.x,res3.y,res3.z);
411  glVertex3d(res4.x,res4.y,res4.z);
412  glEnd();
413 
414 // glBegin(GL_QUADS);
415 // glVertex3d(rect[0].x,rect[0].y,0);
416 // glVertex3d(rect[1].x,rect[1].y,0);
417 // glVertex3d(rect[2].x,rect[2].y,0);
418 // glVertex3d(rect[3].x,rect[3].y,0);
419 // glEnd();
420 
421 // MeshManager::MeshInfo::Coord3D res1 = state->GetMeshManager()->GetCoord3D((hugin_utils::FDiff2D&)vertices[0]);
422 // MeshManager::MeshInfo::Coord3D res2 = state->GetMeshManager()->GetCoord3D((hugin_utils::FDiff2D&)vertices[1]);
423 // glVertex3d(res1.x,res1.y,res1.z);
424 // glVertex3d(res2.x,res2.y,res2.z);
425 }
426 
427 double GreatCircleArc::getxscale(void) const
428 {
429  return m_xscale;
430 }
431 
432 
433 void GreatCircleArc::LineSegment::doGLcross(int point, double xscale, VisualizationState * state) const
434 {
435  //TODO: cross with polygons instead of lines
436  double vx, vy;
437 
438  vx = vertices[point].x;
439  vy = vertices[point].y;
440 
441  hugin_utils::FDiff2D p1(vx - xscale, vy - xscale);
442  hugin_utils::FDiff2D p2(vx + xscale, vy + xscale);
443 
444  hugin_utils::FDiff2D p3(vx - xscale, vy + xscale);
445  hugin_utils::FDiff2D p4(vx + xscale, vy - xscale);
446 
449 
452 
453  glBegin(GL_LINES);
454 
455  // main diagonal
456  glVertex3f(res1.x,res1.y,res1.z);
457  glVertex3f(res2.x,res2.y,res2.z);
458  // second diagonal
459  glVertex3f(res3.x,res3.y,res3.z);
460  glVertex3f(res4.x,res4.y,res4.z);
461 
462  glEnd();
463 }
464 
465 
467 {
468  // find the minimal distance.
469  float distance = std::numeric_limits<float>::max();
470  for (std::vector<GreatCircleArc::LineSegment>::const_iterator it = m_lines.begin();
471  it != m_lines.end();
472  ++it)
473  {
474  float this_distance = it->squareDistance(point);
475  if (this_distance < distance)
476  {
477  distance = this_distance;
478  }
479  }
480  return distance;
481 }
482 
484 {
485  // minimal distance between a point and a line segment
486 
487  // Does the line segment start and end in the same place?
488  if (vertices[0] == vertices[1])
489 
490  {
491  // yes, so return the distance to the point where the 'line' segment is.
492  return point.squareDistance(vertices[0]);
493  }
494  // the vector along the line segment.
495  hugin_utils::FDiff2D line_vector = vertices[1] - vertices[0];
496  // the parameter indicating the point's nearest position along the line.
497  float t = line_vector.x == 0 ?
498  (point.y - vertices[0].y) / line_vector.y
499  : (point.x - vertices[0].x) / line_vector.x;
500  // check if the point lies along the line segment.
501  if (t <= 0.0)
502  {
503  // no, nearest vertices[0].
504  return vertices[0].squareDistance(point);
505  }
506  if (t >= 1.0)
507  {
508  // no, nearest vertices[1].
509  return vertices[1].squareDistance(point);
510  }
511 
512  // Find the intersection with the line segment and the tangent line.
513  hugin_utils::FDiff2D intersection(vertices[0] + line_vector * t);
514  // Finally, find the distance between the intersection and the point.
515  return intersection.squareDistance(point);
516 }
517 
518 
520  double endLat, double endLong,
521  VisualizationState& visualizationState)
522 
523 {
524  m_visualizationState = &visualizationState;
525  // get the output projection
526  const HuginBase::PanoramaOptions& options = *(visualizationState.GetOptions());
527  // make an image to transform spherical coordinates into the output projection
528  static const HuginBase::SrcPanoImage equirectangularImage(HuginBase::SrcPanoImage::EQUIRECTANGULAR, 360.0, vigra::Size2D(360.0, 180.0));
529  // make a transformation from spherical coordinates to the output projection
531  transform.createInvTransform(equirectangularImage, options);
532 
533  double x1, y1, x2, y2;
534  if (transform.transformImgCoord(x1, y1, startLat, startLong) && transform.transformImgCoord(x2, y2, endLat, endLong))
535  {
536  m_xscale = visualizationState.GetScale();
538  line.vertices[0] = hugin_utils::FDiff2D(x1, y1);
539  line.vertices[1] = hugin_utils::FDiff2D(x2, y2);
540  m_lines.push_back(line);
541  };
542 }
const double min_segment_angle
implementation of huginApp Class
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:65
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:113
void setVisualizationState(VisualizationState *visualizationState)
Set the ViewState to use for information on output projection and preview display.
StraightLineFromSphericals(double startLat, double startLong, double endLat, double endLong, VisualizationState &m_visualizationState)
Create a straight line from begin to end in spherical coordinates.
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:162
hugin_utils::FDiff2D vertices[2]
Definition: GreatCircles.h:96
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:112
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.
void drawLineFromSpherical(double startLat, double startLong, double endLat, double endLong, double width=1.0, bool straightLine=false)
Draw the shortest segment of the great circle crossing two spherical coordinates. ...
float squareDistance(hugin_utils::FDiff2D point) const
Return the square of the minimal distance between the great circle arc and a coordinate on the panora...
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:147