Hugin trunk 0.1
Loading...
Searching...
No Matches
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.
50const unsigned int max_segments = 48;
51const double min_segment_angle = 1.0 / 180.0 * M_PI;
52
53GreatCircles::GreatCircles() : m_visualizationState(NULL)
54{
55}
56
61
75
76GreatCircleArc::GreatCircleArc() : m_xscale(DBL_MAX), m_visualizationState(NULL)
77{
78}
79
81 double endLat, double endLong,
83
84{
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.
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);
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
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.
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]);
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.
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;
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);
203 (newSegment > 1 && lastSegment < 1))
204 {
205 skip = true;
206 }
208 }
209 if (infront)
210 {
211 if (!skip)
212 {
213 LineSegment line;
214 line.vertices[0] = last_vertex;
216 m_lines.push_back(line);
217 }
218 // The next line segment should be a valid one.
220 skip = false;
221 } else {
222 skip = true;
223 }
224 }
225}
226
227void GreatCircleArc::draw(bool withCross, double width) const
228{
229 // Just draw all the previously worked out line segments
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 }
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;
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
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
428{
429 return m_xscale;
430}
431
432
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
443
446
449
452
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,
522
523{
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();
537 LineSegment line;
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}
#define M_PI
const unsigned int max_segments
const double min_segment_angle
Declare GreatCircles class.
void draw(bool withCross=true, double width=1.0) const
Draw the great circle arc on the fast preview.
double getxscale() const
GreatCircleArc()
Create a bad great circle arc.
VisualizationState * m_visualizationState
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...
std::vector< LineSegment > m_lines
GreatCircles()
constructor
void setVisualizationState(VisualizationState *visualizationState)
Set the ViewState to use for information on output projection and preview display.
VisualizationState * m_visualizationState
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.
Holds transformations for Image -> Pano and the other way.
bool transformImgCoord(double &x_dest, double &y_dest, double x_src, double y_src) const
like transform, but return image coordinates, not cartesian coordinates
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->pano transformation
Panorama image options.
unsigned int getWidth() const
All variables of a source image.
a class to handle a 3D point
Definition MeshManager.h:94
virtual MeshInfo::Coord3D GetCoord3D(hugin_utils::FDiff2D &)=0
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
implementation of huginApp Class
#define DEBUG_ASSERT(cond)
Definition utils.h:80
TDiff2D< double > FDiff2D
Definition hugin_math.h:162
int roundi(T x)
Definition hugin_math.h:73
include file for the hugin project
include file for the hugin project
void doGLcross(int point, double cscale, VisualizationState *state) const
Specify the line to OpenGL. Must be within a glBegin/glEnd pair.
hugin_utils::FDiff2D vertices[2]
float squareDistance(hugin_utils::FDiff2D point) const
Get the square of the minimal distance to a point.
void doGL(double width, VisualizationState *state, LineSegment *preceding=NULL, LineSegment *proceeding=NULL) const
Draw a meshed line.
T squareDistance(TDiff2D< T > other) const
Return square of the distance to another point.
Definition hugin_math.h:147
std::vector< deghosting::BImagePtr > threshold(const std::vector< deghosting::FImagePtr > &inputImages, const double threshold, const uint16_t flags)
Threshold function used for creating alpha masks for images.
Definition threshold.h:41