source: src/Fragmentation/Exporters/SphericalPointDistribution.cpp@ c56380

Last change on this file since c56380 was c56380, checked in by Frederik Heber <heber@…>, 11 years ago

tempcommit: Typo. Merge with c13adf75

  • Property mode set to 100644
File size: 49.1 KB
RevLine 
[0d4daf]1/*
2 * Project: MoleCuilder
3 * Description: creates and alters molecular systems
4 * Copyright (C) 2014 Frederik Heber. All rights reserved.
5 *
6 *
7 * This file is part of MoleCuilder.
8 *
9 * MoleCuilder is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * MoleCuilder is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with MoleCuilder. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23/*
24 * SphericalPointDistribution.cpp
25 *
26 * Created on: May 30, 2014
27 * Author: heber
28 */
29
30// include config.h
31#ifdef HAVE_CONFIG_H
32#include <config.h>
33#endif
34
35#include "CodePatterns/MemDebug.hpp"
36
37#include "SphericalPointDistribution.hpp"
38
39#include "CodePatterns/Assert.hpp"
40#include "CodePatterns/IteratorAdaptors.hpp"
[90426a]41#include "CodePatterns/Log.hpp"
[0d4daf]42#include "CodePatterns/toString.hpp"
43
44#include <algorithm>
[a2f8a9]45#include <boost/assign.hpp>
[0d4daf]46#include <cmath>
[0d5ca7]47#include <functional>
48#include <iterator>
[0d4daf]49#include <limits>
50#include <list>
[23c605]51#include <numeric>
[0d4daf]52#include <vector>
53#include <map>
54
55#include "LinearAlgebra/Line.hpp"
[3da643]56#include "LinearAlgebra/Plane.hpp"
[0d4daf]57#include "LinearAlgebra/RealSpaceMatrix.hpp"
58#include "LinearAlgebra/Vector.hpp"
59
[a2f8a9]60using namespace boost::assign;
61
[3da643]62// static entities
63const double SphericalPointDistribution::warn_amplitude = 1e-2;
[23c605]64const double SphericalPointDistribution::L1THRESHOLD = 1e-2;
65const double SphericalPointDistribution::L2THRESHOLD = 2e-1;
[3da643]66
[0d4daf]67typedef std::vector<double> DistanceArray_t;
68
[1ae9aa]69// class generator: taken from www.cplusplus.com example std::generate
70struct c_unique {
[23c605]71 unsigned int current;
[1ae9aa]72 c_unique() {current=0;}
[23c605]73 unsigned int operator()() {return current++;}
[1ae9aa]74} UniqueNumber;
75
[23c605]76struct c_unique_list {
77 unsigned int current;
78 c_unique_list() {current=0;}
79 std::list<unsigned int> operator()() {return std::list<unsigned int>(1, current++);}
80} UniqueNumberList;
[0d4daf]81
[1ae9aa]82/** Calculate the center of a given set of points in \a _positions but only
83 * for those indicated by \a _indices.
84 *
85 */
86inline
[a2f8a9]87Vector calculateGeographicMidpoint(
[1ae9aa]88 const SphericalPointDistribution::VectorArray_t &_positions,
89 const SphericalPointDistribution::IndexList_t &_indices)
90{
91 Vector Center;
92 Center.Zero();
93 for (SphericalPointDistribution::IndexList_t::const_iterator iter = _indices.begin();
94 iter != _indices.end(); ++iter)
95 Center += _positions[*iter];
96 if (!_indices.empty())
97 Center *= 1./(double)_indices.size();
98
99 return Center;
100}
[0d4daf]101
[a2f8a9]102inline
103double calculateMinimumDistance(
104 const Vector &_center,
105 const SphericalPointDistribution::VectorArray_t &_points,
106 const SphericalPointDistribution::IndexList_t & _indices)
107{
108 double MinimumDistance = 0.;
109 for (SphericalPointDistribution::IndexList_t::const_iterator iter = _indices.begin();
110 iter != _indices.end(); ++iter) {
111 const double angle = _center.Angle(_points[*iter]);
112 MinimumDistance += angle*angle;
113 }
114 return sqrt(MinimumDistance);
115}
116
117/** Calculates the center of minimum distance for a given set of points \a _points.
118 *
119 * According to http://www.geomidpoint.com/calculation.html this goes a follows:
120 * -# Let CurrentPoint be the geographic midpoint found in Method A. this is used as the starting point for the search.
121 * -# Let MinimumDistance be the sum total of all distances from the current point to all locations in 'Your Places'.
122 * -# Find the total distance between each location in 'Your Places' and all other locations in 'Your Places'. If any one of these locations has a new smallest distance then that location becomes the new CurrentPoint and MinimumDistance.
123 * -# Let TestDistance be PI/2 radians (6225 miles or 10018 km).
124 * -# Find the total distance between each of 8 test points and all locations in 'Your Places'. The test points are arranged in a circular pattern around the CurrentPoint at a distance of TestDistance to the north, northeast, east, southeast, south, southwest, west and northwest.
125 * -# If any of these 8 points has a new smallest distance then that point becomes the new CurrentPoint and MinimumDistance and go back to step 5 using that point.
126 * -# If none of the 8 test points has a new smallest distance then divide TestDistance by 2 and go back to step 5 using the same point.
127 * -# Repeat steps 5 to 7 until no new smallest distance can be found or until TestDistance is less than 0.00000002 radians.
128 *
129 * \param _points set of points
130 * \return Center of minimum distance for all these points, is always of length 1
131 */
132Vector SphericalPointDistribution::calculateCenterOfMinimumDistance(
133 const SphericalPointDistribution::VectorArray_t &_positions,
134 const SphericalPointDistribution::IndexList_t &_indices)
135{
136 ASSERT( _positions.size() >= _indices.size(),
137 "calculateCenterOfMinimumDistance() - less positions than indices given.");
138 Vector center(1.,0.,0.);
139
140 /// first treat some special cases
141 // no positions given: return x axis vector (NOT zero!)
142 {
143 if (_indices.empty())
144 return center;
145 // one position given: return it directly
[4a44ed]146 if (_indices.size() == (size_t)1)
147 return _positions[*_indices.begin()];
[a2f8a9]148 // two positions on a line given: return closest point to (1.,0.,0.)
[4a44ed]149// IndexList_t::const_iterator indexiter = _indices.begin();
150// const unsigned int firstindex = *indexiter++;
151// const unsigned int secondindex = *indexiter;
152// if ( fabs(_positions[firstindex].ScalarProduct(_positions[secondindex]) + 1.)
153// < std::numeric_limits<double>::epsilon()*1e4) {
154// Vector candidate;
155// if (_positions[firstindex].ScalarProduct(center) > _positions[secondindex].ScalarProduct(center))
156// candidate = _positions[firstindex];
157// else
158// candidate = _positions[secondindex];
159// // non-uniqueness: all positions on great circle, normal to given line are valid
160// // so, we just pick one because returning a unique point is topmost priority
161// Vector normal;
162// normal.GetOneNormalVector(candidate);
163// Vector othernormal = candidate;
164// othernormal.VectorProduct(normal);
165// // now both normal and othernormal describe the plane containing the great circle
166// Plane greatcircle(normal, zeroVec, othernormal);
167// // check which axis is contained and pick the one not
168// if (greatcircle.isContained(center)) {
169// center = Vector(0.,1.,0.);
170// if (greatcircle.isContained(center))
171// center = Vector(0.,0.,1.);
172// // now we are done cause a plane cannot contain all three axis vectors
173// }
174// center = greatcircle.getClosestPoint(candidate);
175// // assure length of 1
176// center.Normalize();
177//
178// return center;
179// }
180 // given points lie on a great circle and go completely round it
181 // two or more positions on a great circle given: return closest point to (1.,0.,0.)
182 {
183 bool AllNormal = true;
184 Vector Normal;
185 {
186 IndexList_t::const_iterator indexiter = _indices.begin();
187 Normal = _positions[*indexiter++];
188 Normal.VectorProduct(_positions[*indexiter++]);
189 Normal.Normalize();
190 for (;(AllNormal) && (indexiter != _indices.end()); ++indexiter)
191 AllNormal &= _positions[*indexiter].IsNormalTo(Normal, 1e-8);
192 }
193 double AngleSum = 0.;
194 if (AllNormal) {
195 // check by angle sum whether points go round are cover just one half
196 IndexList_t::const_iterator indexiter = _indices.begin();
197 Vector CurrentVector = _positions[*indexiter++];
198 for(; indexiter != _indices.end(); ++indexiter) {
199 AngleSum += CurrentVector.Angle(_positions[*indexiter]);
200 CurrentVector = _positions[*indexiter];
201 }
202 }
203 if (AngleSum - M_PI > std::numeric_limits<double>::epsilon()*1e4) {
204// Vector candidate;
205// double oldSKP = -1.;
206// for (IndexList_t::const_iterator iter = _indices.begin();
207// iter != _indices.end(); ++iter) {
208// const double newSKP = _positions[*iter].ScalarProduct(center);
209// if (newSKP > oldSKP) {
210// candidate = _positions[*iter];
211// oldSKP = newSKP;
212// }
213// }
214 // non-uniqueness: all positions on great circle, normal to given line are valid
215 // so, we just pick one because returning a unique point is topmost priority
216// Vector normal;
217// normal.GetOneNormalVector(candidate);
218// Vector othernormal = candidate;
219// othernormal.VectorProduct(normal);
220// // now both normal and othernormal describe the plane containing the great circle
221// Plane greatcircle(normal, zeroVec, othernormal);
222 // check which axis is contained and pick the one not
223// if (greatcircle.isContained(center)) {
224// center = Vector(0.,1.,0.);
225// if (greatcircle.isContained(center))
226// center = Vector(0.,0.,1.);
227// // now we are done cause a plane cannot contain all three axis vectors
228// }
229// center = greatcircle.getClosestPoint(candidate);
230// center = greatcircle.getNormal();
231 center = Normal;
232 // assure length of 1
233 center.Normalize();
234
235 return center;
[a2f8a9]236 }
237 }
238 }
239
240 // start with geographic midpoint
241 center = calculateGeographicMidpoint(_positions, _indices);
242 if (!center.IsZero()) {
243 center.Normalize();
[4a44ed]244 LOG(5, "DEBUG: Starting with geographical midpoint of " << _positions << " under indices "
[a2f8a9]245 << _indices << " is " << center);
246 } else {
247 // any point is good actually
248 center = _positions[0];
[4a44ed]249 LOG(5, "DEBUG: Starting with first position " << center);
[a2f8a9]250 }
251
252 // calculate initial MinimumDistance
253 double MinimumDistance = calculateMinimumDistance(center, _positions, _indices);
[4a44ed]254 LOG(6, "DEBUG: MinimumDistance to this center is " << MinimumDistance);
[a2f8a9]255
256 // check all present points whether they may serve as a better center
257 for (SphericalPointDistribution::IndexList_t::const_iterator iter = _indices.begin();
258 iter != _indices.end(); ++iter) {
259 const Vector &centerCandidate = _positions[*iter];
260 const double candidateDistance = calculateMinimumDistance(centerCandidate, _positions, _indices);
261 if (candidateDistance < MinimumDistance) {
262 MinimumDistance = candidateDistance;
263 center = centerCandidate;
[4a44ed]264 LOG(6, "DEBUG: new MinimumDistance to current test point " << MinimumDistance
[a2f8a9]265 << " is " << center);
266 }
267 }
[4a44ed]268 LOG(6, "DEBUG: new MinimumDistance to center " << center << " is " << MinimumDistance);
[a2f8a9]269
270 // now iterate over TestDistance
271 double TestDistance = Vector(1.,0.,0.).Angle(Vector(0.,1.,0.));
272// LOG(6, "DEBUG: initial TestDistance is " << TestDistance);
273
274 const double threshold = sqrt(std::numeric_limits<double>::epsilon());
275 // check each of eight test points at N, NE, E, SE, S, SW, W, NW
276 typedef std::vector<double> angles_t;
277 angles_t testangles;
278 testangles += 0./180.*M_PI, 45./180.*M_PI, 90./180.*M_PI, 135./180.*M_PI,
279 180./180.*M_PI, 225./180.*M_PI, 270./180.*M_PI, 315./180.*M_PI;
280 while (TestDistance > threshold) {
281 Vector OneNormal;
282 OneNormal.GetOneNormalVector(center);
283 Line RotationAxis(zeroVec, OneNormal);
284 Vector North = RotationAxis.rotateVector(center,TestDistance);
285 Line CompassRose(zeroVec, center);
286 bool updatedflag = false;
287 for (angles_t::const_iterator angleiter = testangles.begin();
288 angleiter != testangles.end(); ++angleiter) {
289 Vector centerCandidate = CompassRose.rotateVector(North, *angleiter);
290// centerCandidate.Normalize();
291 const double candidateDistance = calculateMinimumDistance(centerCandidate, _positions, _indices);
292 if (candidateDistance < MinimumDistance) {
293 MinimumDistance = candidateDistance;
294 center = centerCandidate;
295 updatedflag = true;
[4a44ed]296 LOG(7, "DEBUG: new MinimumDistance to test point at " << *angleiter/M_PI*180.
[a2f8a9]297 << "° is " << MinimumDistance);
298 }
299 }
300
301 // if no new point, decrease TestDistance
302 if (!updatedflag) {
303 TestDistance *= 0.5;
304// LOG(6, "DEBUG: TestDistance is now " << TestDistance);
305 }
306 }
307 LOG(4, "DEBUG: Final MinimumDistance to center " << center << " is " << MinimumDistance);
308
309 return center;
310}
311
312Vector calculateCenterOfMinimumDistance(
313 const SphericalPointDistribution::PolygonWithIndices &_points)
314{
315 return SphericalPointDistribution::calculateCenterOfMinimumDistance(_points.polygon, _points.indices);
316}
317
318/** Calculate the center of a given set of points in \a _positions but only
319 * for those indicated by \a _indices.
320 *
321 */
322inline
323Vector calculateCenter(
324 const SphericalPointDistribution::VectorArray_t &_positions,
325 const SphericalPointDistribution::IndexList_t &_indices)
326{
327// Vector Center;
328// Center.Zero();
329// for (SphericalPointDistribution::IndexList_t::const_iterator iter = _indices.begin();
330// iter != _indices.end(); ++iter)
331// Center += _positions[*iter];
332// if (!_indices.empty())
333// Center *= 1./(double)_indices.size();
334//
335// return Center;
336 return SphericalPointDistribution::calculateCenterOfMinimumDistance(_positions, _indices);
337}
338
[1cde4e8]339/** Calculate the center of a given set of points in \a _positions but only
340 * for those indicated by \a _indices.
341 *
342 */
343inline
344Vector calculateCenter(
345 const SphericalPointDistribution::PolygonWithIndices &_polygon)
346{
347 return calculateCenter(_polygon.polygon, _polygon.indices);
348}
349
[23c605]350inline
351DistanceArray_t calculatePairwiseDistances(
352 const SphericalPointDistribution::VectorArray_t &_points,
353 const SphericalPointDistribution::IndexTupleList_t &_indices
354 )
355{
356 DistanceArray_t result;
357 for (SphericalPointDistribution::IndexTupleList_t::const_iterator firstiter = _indices.begin();
358 firstiter != _indices.end(); ++firstiter) {
359
360 // calculate first center from possible tuple of indices
361 Vector FirstCenter;
362 ASSERT(!firstiter->empty(), "calculatePairwiseDistances() - there is an empty tuple.");
363 if (firstiter->size() == 1) {
364 FirstCenter = _points[*firstiter->begin()];
365 } else {
366 FirstCenter = calculateCenter( _points, *firstiter);
367 if (!FirstCenter.IsZero())
368 FirstCenter.Normalize();
369 }
370
371 for (SphericalPointDistribution::IndexTupleList_t::const_iterator seconditer = firstiter;
372 seconditer != _indices.end(); ++seconditer) {
373 if (firstiter == seconditer)
374 continue;
375
376 // calculate second center from possible tuple of indices
377 Vector SecondCenter;
378 ASSERT(!seconditer->empty(), "calculatePairwiseDistances() - there is an empty tuple.");
379 if (seconditer->size() == 1) {
380 SecondCenter = _points[*seconditer->begin()];
381 } else {
382 SecondCenter = calculateCenter( _points, *seconditer);
383 if (!SecondCenter.IsZero())
384 SecondCenter.Normalize();
385 }
386
387 // calculate distance between both centers
388 double distance = 2.; // greatest distance on surface of sphere with radius 1.
389 if ((!FirstCenter.IsZero()) && (!SecondCenter.IsZero()))
390 distance = (FirstCenter - SecondCenter).NormSquared();
391 result.push_back(distance);
392 }
393 }
394 return result;
395}
396
[1ae9aa]397/** Decides by an orthonormal third vector whether the sign of the rotation
398 * angle should be negative or positive.
399 *
400 * \return -1 or 1
401 */
402inline
403double determineSignOfRotation(
404 const Vector &_oldPosition,
405 const Vector &_newPosition,
406 const Vector &_RotationAxis
407 )
408{
409 Vector dreiBein(_oldPosition);
410 dreiBein.VectorProduct(_RotationAxis);
[23c605]411 ASSERT( !dreiBein.IsZero(), "determineSignOfRotation() - dreiBein is zero.");
[1ae9aa]412 dreiBein.Normalize();
413 const double sign =
414 (dreiBein.ScalarProduct(_newPosition) < 0.) ? -1. : +1.;
415 LOG(6, "DEBUG: oldCenter on plane is " << _oldPosition
[23c605]416 << ", newCenter on plane is " << _newPosition
[1ae9aa]417 << ", and dreiBein is " << dreiBein);
418 return sign;
419}
420
421/** Convenience function to recalculate old and new center for determining plane
422 * rotation.
423 */
424inline
425void calculateOldAndNewCenters(
426 Vector &_oldCenter,
427 Vector &_newCenter,
[1cde4e8]428 const SphericalPointDistribution::PolygonWithIndices &_referencepositions,
429 const SphericalPointDistribution::PolygonWithIndices &_currentpositions)
[1ae9aa]430{
[1cde4e8]431 _oldCenter = calculateCenter(_referencepositions.polygon, _referencepositions.indices);
[1ae9aa]432 // C++11 defines a copy_n function ...
[1cde4e8]433 _newCenter = calculateCenter( _currentpositions.polygon, _currentpositions.indices);
[1ae9aa]434}
[0d4daf]435/** Returns squared L2 error of the given \a _Matching.
436 *
437 * We compare the pair-wise distances of each associated matching
438 * and check whether these distances each match between \a _old and
439 * \a _new.
440 *
441 * \param _old first set of points (fewer or equal to \a _new)
442 * \param _new second set of points
443 * \param _Matching matching between the two sets
444 * \return pair with L1 and squared L2 error
445 */
[3da643]446std::pair<double, double> SphericalPointDistribution::calculateErrorOfMatching(
[23c605]447 const VectorArray_t &_old,
448 const VectorArray_t &_new,
449 const IndexTupleList_t &_Matching)
[0d4daf]450{
451 std::pair<double, double> errors( std::make_pair( 0., 0. ) );
452
453 if (_Matching.size() > 1) {
[23c605]454 LOG(5, "INFO: Matching is " << _Matching);
[0d4daf]455
456 // calculate all pair-wise distances
[23c605]457 IndexTupleList_t keys(_old.size(), IndexList_t() );
458 std::generate (keys.begin(), keys.end(), UniqueNumberList);
459
[0d4daf]460 const DistanceArray_t firstdistances = calculatePairwiseDistances(_old, keys);
461 const DistanceArray_t seconddistances = calculatePairwiseDistances(_new, _Matching);
462
463 ASSERT( firstdistances.size() == seconddistances.size(),
464 "calculateL2ErrorOfMatching() - mismatch in pair-wise distance array sizes.");
465 DistanceArray_t::const_iterator firstiter = firstdistances.begin();
466 DistanceArray_t::const_iterator seconditer = seconddistances.begin();
467 for (;(firstiter != firstdistances.end()) && (seconditer != seconddistances.end());
468 ++firstiter, ++seconditer) {
[23c605]469 const double gap = fabs(*firstiter - *seconditer);
[0d4daf]470 // L1 error
471 if (errors.first < gap)
472 errors.first = gap;
473 // L2 error
474 errors.second += gap*gap;
475 }
[4a44ed]476 // there is at least one distance, we've checked that before
477 errors.second *= 1./(double)firstdistances.size();
[23c605]478 } else {
479 // check whether we have any zero centers: Combining points on new sphere may result
480 // in zero centers
481 for (SphericalPointDistribution::IndexTupleList_t::const_iterator iter = _Matching.begin();
482 iter != _Matching.end(); ++iter) {
483 if ((iter->size() != 1) && (calculateCenter( _new, *iter).IsZero())) {
484 errors.first = 2.;
485 errors.second = 2.;
486 }
487 }
488 }
489 LOG(4, "INFO: Resulting errors for matching (L1, L2): "
[90426a]490 << errors.first << "," << errors.second << ".");
[0d4daf]491
492 return errors;
493}
494
[3da643]495SphericalPointDistribution::Polygon_t SphericalPointDistribution::removeMatchingPoints(
[1cde4e8]496 const PolygonWithIndices &_points
[0d4daf]497 )
498{
499 SphericalPointDistribution::Polygon_t remainingpoints;
[1cde4e8]500 IndexArray_t indices(_points.indices.begin(), _points.indices.end());
[0d4daf]501 std::sort(indices.begin(), indices.end());
[90426a]502 LOG(4, "DEBUG: sorted matching is " << indices);
[1cde4e8]503 IndexArray_t remainingindices(_points.polygon.size(), -1);
[bb011f]504 std::generate(remainingindices.begin(), remainingindices.end(), UniqueNumber);
505 IndexArray_t::iterator remainiter = std::set_difference(
506 remainingindices.begin(), remainingindices.end(),
507 indices.begin(), indices.end(),
508 remainingindices.begin());
509 remainingindices.erase(remainiter, remainingindices.end());
510 LOG(4, "DEBUG: remaining indices are " << remainingindices);
511 for (IndexArray_t::const_iterator iter = remainingindices.begin();
512 iter != remainingindices.end(); ++iter) {
[1cde4e8]513 remainingpoints.push_back(_points.polygon[*iter]);
[0d4daf]514 }
515
516 return remainingpoints;
517}
518
519/** Recursive function to go through all possible matchings.
520 *
521 * \param _MCS structure holding global information to the recursion
522 * \param _matching current matching being build up
523 * \param _indices contains still available indices
[23c605]524 * \param _remainingweights current weights to fill (each weight a place)
525 * \param _remainiter iterator over the weights, indicating the current position we match
[0d4daf]526 * \param _matchingsize
527 */
[3da643]528void SphericalPointDistribution::recurseMatchings(
[0d4daf]529 MatchingControlStructure &_MCS,
[23c605]530 IndexTupleList_t &_matching,
[0d4daf]531 IndexList_t _indices,
[23c605]532 WeightsArray_t &_remainingweights,
533 WeightsArray_t::iterator _remainiter,
534 const unsigned int _matchingsize
535 )
[0d4daf]536{
[23c605]537 LOG(5, "DEBUG: Recursing with current matching " << _matching
[90426a]538 << ", remaining indices " << _indices
[23c605]539 << ", and remaining weights " << _matchingsize);
[0d4daf]540 if (!_MCS.foundflag) {
[23c605]541 LOG(5, "DEBUG: Current matching has size " << _matching.size() << ", places left " << _matchingsize);
[0d5ca7]542 if (_matchingsize > 0) {
[0d4daf]543 // go through all indices
544 for (IndexList_t::iterator iter = _indices.begin();
[0d5ca7]545 (iter != _indices.end()) && (!_MCS.foundflag);) {
[e6ca85]546
[23c605]547 // check whether we can stay in the current bin or have to move on to next one
548 if (*_remainiter == 0) {
549 // we need to move on
550 if (_remainiter != _remainingweights.end()) {
551 ++_remainiter;
552 } else {
553 // as we check _matchingsize > 0 this should be impossible
554 ASSERT( 0, "recurseMatchings() - we must not come to this position.");
555 }
556 }
[e6ca85]557
558 // advance in matching to current bin to fill in
[23c605]559 const size_t OldIndex = std::distance(_remainingweights.begin(), _remainiter);
[c56380]560 while (_matching.size() <= OldIndex) { // add empty lists if new bin is opened
[23c605]561 LOG(6, "DEBUG: Extending _matching.");
562 _matching.push_back( IndexList_t() );
563 }
564 IndexTupleList_t::iterator filliniter = _matching.begin();
565 std::advance(filliniter, OldIndex);
[e6ca85]566
567 // check whether connection between bins' indices and candidate is satisfied
568 {
569 adjacency_t::const_iterator finder = _MCS.adjacency.find(*iter);
570 ASSERT( finder != _MCS.adjacency.end(),
571 "recurseMatchings() - "+toString(*iter)+" is not in adjacency list.");
572 if ((!filliniter->empty())
573 && (finder->second.find(*filliniter->begin()) == finder->second.end())) {
574 LOG(5, "DEBUG; Skipping index " << *iter
575 << " as is not connected to current set." << *filliniter << ".");
576 ++iter; // note that for loop does not contain incrementor
577 continue;
578 }
579 }
580
[0d4daf]581 // add index to matching
[23c605]582 filliniter->push_back(*iter);
583 --(*_remainiter);
584 LOG(6, "DEBUG: Adding " << *iter << " to matching at " << OldIndex << ".");
[0d4daf]585 // remove index but keep iterator to position (is the next to erase element)
586 IndexList_t::iterator backupiter = _indices.erase(iter);
587 // recurse with decreased _matchingsize
[23c605]588 recurseMatchings(_MCS, _matching, _indices, _remainingweights, _remainiter, _matchingsize-1);
[0d4daf]589 // re-add chosen index and reset index to new position
[23c605]590 _indices.insert(backupiter, filliniter->back());
[0d4daf]591 iter = backupiter;
592 // remove index from _matching to make space for the next one
[23c605]593 filliniter->pop_back();
594 ++(*_remainiter);
[0d4daf]595 }
596 // gone through all indices then exit recursion
[0d5ca7]597 if (_matching.empty())
598 _MCS.foundflag = true;
[0d4daf]599 } else {
[23c605]600 LOG(4, "INFO: Found matching " << _matching);
[0d4daf]601 // calculate errors
602 std::pair<double, double> errors = calculateErrorOfMatching(
603 _MCS.oldpoints, _MCS.newpoints, _matching);
604 if (errors.first < L1THRESHOLD) {
605 _MCS.bestmatching = _matching;
606 _MCS.foundflag = true;
[0d5ca7]607 } else if (_MCS.bestL2 > errors.second) {
[0d4daf]608 _MCS.bestmatching = _matching;
609 _MCS.bestL2 = errors.second;
610 }
611 }
612 }
613}
614
[e6ca85]615SphericalPointDistribution::MatchingControlStructure::MatchingControlStructure(
616 const adjacency_t &_adjacency,
617 const VectorArray_t &_oldpoints,
618 const VectorArray_t &_newpoints,
619 const WeightsArray_t &_weights
620 ) :
621 foundflag(false),
622 bestL2(std::numeric_limits<double>::max()),
623 adjacency(_adjacency),
624 oldpoints(_oldpoints),
625 newpoints(_newpoints),
626 weights(_weights)
627{}
628
[3da643]629/** Finds combinatorially the best matching between points in \a _polygon
630 * and \a _newpolygon.
631 *
632 * We find the matching with the smallest L2 error, where we break when we stumble
633 * upon a matching with zero error.
634 *
[1ae9aa]635 * As points in \a _polygon may be have a weight greater 1 we have to match it to
636 * multiple points in \a _newpolygon. Eventually, these multiple points are combined
637 * for their center of weight, which is the only thing follow-up function see of
638 * these multiple points. Hence, we actually modify \a _newpolygon in the process
639 * such that the returned IndexList_t indicates a bijective mapping in the end.
640 *
[3da643]641 * \sa recurseMatchings() for going through all matchings
642 *
643 * \param _polygon here, we have indices 0,1,2,...
644 * \param _newpolygon and here we need to find the correct indices
[c8d2e7]645 * \return control structure containing the matching and more
[3da643]646 */
[c8d2e7]647SphericalPointDistribution::MatchingControlStructure
648SphericalPointDistribution::findBestMatching(
[e6ca85]649 const WeightedPolygon_t &_polygon
[3da643]650 )
[0d5ca7]651{
[23c605]652 // transform lists into arrays
[e6ca85]653 VectorArray_t oldpoints;
654 VectorArray_t newpoints;
655 WeightsArray_t weights;
[260540]656 for (WeightedPolygon_t::const_iterator iter = _polygon.begin();
[23c605]657 iter != _polygon.end(); ++iter) {
[e6ca85]658 oldpoints.push_back(iter->first);
659 weights.push_back(iter->second);
[23c605]660 }
[e6ca85]661 newpoints.insert(newpoints.begin(), points.begin(), points.end() );
662 MatchingControlStructure MCS(adjacency, oldpoints, newpoints, weights);
[3da643]663
664 // search for bestmatching combinatorially
665 {
666 // translate polygon into vector to enable index addressing
[e6ca85]667 IndexList_t indices(points.size());
[3da643]668 std::generate(indices.begin(), indices.end(), UniqueNumber);
[23c605]669 IndexTupleList_t matching;
[3da643]670
671 // walk through all matchings
[23c605]672 WeightsArray_t remainingweights = MCS.weights;
673 unsigned int placesleft = std::accumulate(remainingweights.begin(), remainingweights.end(), 0);
674 recurseMatchings(MCS, matching, indices, remainingweights, remainingweights.begin(), placesleft);
675 }
676 if (MCS.foundflag)
677 LOG(3, "Found a best matching beneath L1 threshold of " << L1THRESHOLD);
678 else {
679 if (MCS.bestL2 < warn_amplitude)
680 LOG(3, "Picking matching is " << MCS.bestmatching << " with best L2 error of "
681 << MCS.bestL2);
682 else if (MCS.bestL2 < L2THRESHOLD)
683 ELOG(2, "Picking matching is " << MCS.bestmatching
684 << " with rather large L2 error of " << MCS.bestL2);
685 else
686 ASSERT(0, "findBestMatching() - matching "+toString(MCS.bestmatching)
687 +" has L2 error of "+toString(MCS.bestL2)+" that is too large.");
[0d5ca7]688 }
689
[c8d2e7]690 return MCS;
[3da643]691}
692
[1ae9aa]693SphericalPointDistribution::IndexList_t SphericalPointDistribution::joinPoints(
694 Polygon_t &_newpolygon,
695 const VectorArray_t &_newpoints,
696 const IndexTupleList_t &_bestmatching
697 )
[3da643]698{
[1ae9aa]699 // combine all multiple points
700 IndexList_t IndexList;
701 IndexArray_t removalpoints;
702 unsigned int UniqueIndex = _newpolygon.size(); // all indices up to size are used right now
703 VectorArray_t newCenters;
704 newCenters.reserve(_bestmatching.size());
705 for (IndexTupleList_t::const_iterator tupleiter = _bestmatching.begin();
706 tupleiter != _bestmatching.end(); ++tupleiter) {
707 ASSERT (tupleiter->size() > 0,
708 "findBestMatching() - encountered tuple in bestmatching with size 0.");
709 if (tupleiter->size() == 1) {
710 // add point and index
711 IndexList.push_back(*tupleiter->begin());
712 } else {
713 // combine into weighted and normalized center
714 Vector Center = calculateCenter(_newpoints, *tupleiter);
715 Center.Normalize();
716 _newpolygon.push_back(Center);
[23c605]717 LOG(5, "DEBUG: Combining " << tupleiter->size() << " points to weighted center "
[1ae9aa]718 << Center << " with new index " << UniqueIndex);
719 // mark for removal
720 removalpoints.insert(removalpoints.end(), tupleiter->begin(), tupleiter->end());
721 // add new index
722 IndexList.push_back(UniqueIndex++);
723 }
724 }
725 // IndexList is now our new bestmatching (that is bijective)
726 LOG(4, "DEBUG: Our new bijective IndexList reads as " << IndexList);
727
728 // modifying _newpolygon: remove all points in removalpoints, add those in newCenters
729 Polygon_t allnewpoints = _newpolygon;
730 {
731 _newpolygon.clear();
732 std::sort(removalpoints.begin(), removalpoints.end());
733 size_t i = 0;
734 IndexArray_t::const_iterator removeiter = removalpoints.begin();
735 for (Polygon_t::iterator iter = allnewpoints.begin();
736 iter != allnewpoints.end(); ++iter, ++i) {
737 if ((removeiter != removalpoints.end()) && (i == *removeiter)) {
738 // don't add, go to next remove index
739 ++removeiter;
740 } else {
741 // otherwise add points
742 _newpolygon.push_back(*iter);
743 }
744 }
745 }
746 LOG(4, "DEBUG: The polygon with recentered points removed is " << _newpolygon);
747
748 // map IndexList to new shrinked _newpolygon
749 typedef std::set<unsigned int> IndexSet_t;
750 IndexSet_t SortedIndexList(IndexList.begin(), IndexList.end());
751 IndexList.clear();
752 {
753 size_t offset = 0;
754 IndexSet_t::const_iterator listiter = SortedIndexList.begin();
755 IndexArray_t::const_iterator removeiter = removalpoints.begin();
756 for (size_t i = 0; i < allnewpoints.size(); ++i) {
757 if ((removeiter != removalpoints.end()) && (i == *removeiter)) {
758 ++offset;
759 ++removeiter;
760 } else if ((listiter != SortedIndexList.end()) && (i == *listiter)) {
761 IndexList.push_back(*listiter - offset);
762 ++listiter;
763 }
764 }
765 }
766 LOG(4, "DEBUG: Our new bijective IndexList corrected for removed points reads as "
767 << IndexList);
768
769 return IndexList;
[3da643]770}
771
772SphericalPointDistribution::Rotation_t SphericalPointDistribution::findPlaneAligningRotation(
[1cde4e8]773 const PolygonWithIndices &_referencepositions,
774 const PolygonWithIndices &_currentpositions
[3da643]775 )
776{
777 bool dontcheck = false;
778 // initialize to no rotation
779 Rotation_t Rotation;
780 Rotation.first.Zero();
781 Rotation.first[0] = 1.;
782 Rotation.second = 0.;
783
784 // calculate center of triangle/line/point consisting of first points of matching
785 Vector oldCenter;
786 Vector newCenter;
787 calculateOldAndNewCenters(
788 oldCenter, newCenter,
[1cde4e8]789 _referencepositions, _currentpositions);
[3da643]790
[0b517b]791 ASSERT( !oldCenter.IsZero() && !newCenter.IsZero(),
792 "findPlaneAligningRotation() - either old "+toString(oldCenter)
793 +" or new center "+toString(newCenter)+" are zero.");
794 LOG(4, "DEBUG: oldCenter is " << oldCenter << ", newCenter is " << newCenter);
795 if (!oldCenter.IsEqualTo(newCenter)) {
796 // calculate rotation axis and angle
797 Rotation.first = oldCenter;
798 Rotation.first.VectorProduct(newCenter);
799 Rotation.first.Normalize();
800 // construct reference vector to determine direction of rotation
801 const double sign = determineSignOfRotation(newCenter, oldCenter, Rotation.first);
802 Rotation.second = sign * oldCenter.Angle(newCenter);
[3da643]803 } else {
[0b517b]804 // no rotation required anymore
[3da643]805 }
806
807#ifndef NDEBUG
808 // check: rotation brings newCenter onto oldCenter position
809 if (!dontcheck) {
810 Line Axis(zeroVec, Rotation.first);
811 Vector test = Axis.rotateVector(newCenter, Rotation.second);
812 LOG(4, "CHECK: rotated newCenter is " << test
813 << ", oldCenter is " << oldCenter);
814 ASSERT( (test - oldCenter).NormSquared() < std::numeric_limits<double>::epsilon()*1e4,
815 "matchSphericalPointDistributions() - rotation does not work as expected by "
816 +toString((test - oldCenter).NormSquared())+".");
817 }
818#endif
819
820 return Rotation;
821}
822
823SphericalPointDistribution::Rotation_t SphericalPointDistribution::findPointAligningRotation(
[1cde4e8]824 const PolygonWithIndices &remainingold,
825 const PolygonWithIndices &remainingnew)
[3da643]826{
827 // initialize rotation to zero
828 Rotation_t Rotation;
829 Rotation.first.Zero();
830 Rotation.first[0] = 1.;
831 Rotation.second = 0.;
832
833 // recalculate center
834 Vector oldCenter;
835 Vector newCenter;
836 calculateOldAndNewCenters(
837 oldCenter, newCenter,
[1cde4e8]838 remainingold, remainingnew);
[3da643]839
[1cde4e8]840 Vector oldPosition = remainingnew.polygon[*remainingnew.indices.begin()];
841 Vector newPosition = remainingold.polygon[0];
842 LOG(6, "DEBUG: oldPosition is " << oldPosition << " (length: "
843 << oldPosition.Norm() << ") and newPosition is " << newPosition << " length(: "
844 << newPosition.Norm() << ")");
[0b517b]845
[3da643]846 if (!oldPosition.IsEqualTo(newPosition)) {
[0b517b]847 // we rotate at oldCenter and around the radial direction, which is again given
848 // by oldCenter.
849 Rotation.first = oldCenter;
850 Rotation.first.Normalize(); // note weighted sum of normalized weight is not normalized
851 LOG(6, "DEBUG: Using oldCenter " << oldCenter << " as rotation center and "
852 << Rotation.first << " as axis.");
853 oldPosition -= oldCenter;
854 newPosition -= oldCenter;
855 oldPosition = (oldPosition - oldPosition.Projection(Rotation.first));
856 newPosition = (newPosition - newPosition.Projection(Rotation.first));
857 LOG(6, "DEBUG: Positions after projection are " << oldPosition << " and " << newPosition);
858
[3da643]859 // construct reference vector to determine direction of rotation
860 const double sign = determineSignOfRotation(oldPosition, newPosition, Rotation.first);
861 Rotation.second = sign * oldPosition.Angle(newPosition);
862 } else {
863 LOG(6, "DEBUG: oldPosition and newPosition are equivalent, hence no orientating rotation.");
864 }
865
866 return Rotation;
[0d5ca7]867}
868
[e6ca85]869void SphericalPointDistribution::initSelf(const int _NumberOfPoints)
870{
871 switch (_NumberOfPoints)
872 {
873 case 0:
874 points = get<0>();
875 adjacency = getConnections<0>();
876 break;
877 case 1:
878 points = get<1>();
879 adjacency = getConnections<1>();
880 break;
881 case 2:
882 points = get<2>();
883 adjacency = getConnections<2>();
884 break;
885 case 3:
886 points = get<3>();
887 adjacency = getConnections<3>();
888 break;
889 case 4:
890 points = get<4>();
891 adjacency = getConnections<4>();
892 break;
893 case 5:
894 points = get<5>();
895 adjacency = getConnections<5>();
896 break;
897 case 6:
898 points = get<6>();
899 adjacency = getConnections<6>();
900 break;
901 case 7:
902 points = get<7>();
903 adjacency = getConnections<7>();
904 break;
905 case 8:
906 points = get<8>();
907 adjacency = getConnections<8>();
908 break;
909 case 9:
910 points = get<9>();
911 adjacency = getConnections<9>();
912 break;
913 case 10:
914 points = get<10>();
915 adjacency = getConnections<10>();
916 break;
917 case 11:
918 points = get<11>();
919 adjacency = getConnections<11>();
920 break;
921 case 12:
922 points = get<12>();
923 adjacency = getConnections<12>();
924 break;
925 case 14:
926 points = get<14>();
927 adjacency = getConnections<14>();
928 break;
929 default:
930 ASSERT(0, "SphericalPointDistribution::initSelf() - cannot deal with the case "
931 +toString(_NumberOfPoints)+".");
932 }
933 LOG(3, "DEBUG: Ideal polygon is " << points);
934}
[0d5ca7]935
[0d4daf]936SphericalPointDistribution::Polygon_t
[e6ca85]937SphericalPointDistribution::getRemainingPoints(
938 const WeightedPolygon_t &_polygon,
939 const int _N)
[0d4daf]940{
941 SphericalPointDistribution::Polygon_t remainingpoints;
[1cde4e8]942
[e6ca85]943 // initialze to given number of points
944 initSelf(_N);
[0d5ca7]945 LOG(2, "INFO: Matching old polygon " << _polygon
[e6ca85]946 << " with new polygon " << points);
[0d4daf]947
[e6ca85]948 // check whether any points will remain vacant
949 int RemainingPoints = _N;
950 for (WeightedPolygon_t::const_iterator iter = _polygon.begin();
951 iter != _polygon.end(); ++iter)
952 RemainingPoints -= iter->second;
953 if (RemainingPoints == 0)
[3da643]954 return remainingpoints;
[0d4daf]955
[e6ca85]956 if (_N > 0) {
[c8d2e7]957 // combine multiple points and create simple IndexList from IndexTupleList
958 MatchingControlStructure MCS = findBestMatching(_polygon);
959 IndexList_t bestmatching = joinPoints(points, MCS.newpoints, MCS.bestmatching);
[3da643]960 LOG(2, "INFO: Best matching is " << bestmatching);
[0d4daf]961
[1cde4e8]962 const size_t NumberIds = std::min(bestmatching.size(), (size_t)3);
963 // create old set
964 PolygonWithIndices oldSet;
965 oldSet.indices.resize(NumberIds, -1);
966 std::generate(oldSet.indices.begin(), oldSet.indices.end(), UniqueNumber);
967 for (WeightedPolygon_t::const_iterator iter = _polygon.begin();
968 iter != _polygon.end(); ++iter)
969 oldSet.polygon.push_back(iter->first);
970
971 // _newpolygon has changed, so now convert to array with matched indices
972 PolygonWithIndices newSet;
973 SphericalPointDistribution::IndexList_t::const_iterator beginiter = bestmatching.begin();
974 SphericalPointDistribution::IndexList_t::const_iterator enditer = bestmatching.begin();
975 std::advance(enditer, NumberIds);
976 newSet.indices.resize(NumberIds, -1);
977 std::copy(beginiter, enditer, newSet.indices.begin());
[e6ca85]978 std::copy(points.begin(),points.end(), std::back_inserter(newSet.polygon));
[23c605]979
[0d4daf]980 // determine rotation angles to align the two point distributions with
[3da643]981 // respect to bestmatching:
982 // we use the center between the three first matching points
983 /// the first rotation brings these two centers to coincide
[1cde4e8]984 PolygonWithIndices rotatednewSet = newSet;
[0d4daf]985 {
[1cde4e8]986 Rotation_t Rotation = findPlaneAligningRotation(oldSet, newSet);
[3da643]987 LOG(5, "DEBUG: Rotating coordinate system by " << Rotation.second
988 << " around axis " << Rotation.first);
989 Line Axis(zeroVec, Rotation.first);
990
991 // apply rotation angle to bring newCenter to oldCenter
[1cde4e8]992 for (VectorArray_t::iterator iter = rotatednewSet.polygon.begin();
993 iter != rotatednewSet.polygon.end(); ++iter) {
[3da643]994 Vector &current = *iter;
995 LOG(6, "DEBUG: Original point is " << current);
996 current = Axis.rotateVector(current, Rotation.second);
997 LOG(6, "DEBUG: Rotated point is " << current);
[0d4daf]998 }
[3da643]999
1000#ifndef NDEBUG
1001 // check: rotated "newCenter" should now equal oldCenter
[0b517b]1002 // we don't check in case of two points as these lie on a great circle
1003 // and the center cannot stably be recalculated. We may reactivate this
1004 // when we calculate centers only once
1005 if (oldSet.indices.size() > 2) {
[3da643]1006 Vector oldCenter;
1007 Vector rotatednewCenter;
1008 calculateOldAndNewCenters(
1009 oldCenter, rotatednewCenter,
[1cde4e8]1010 oldSet, rotatednewSet);
[a2f8a9]1011 oldCenter.Normalize();
1012 rotatednewCenter.Normalize();
1013 // check whether centers are anti-parallel (factor -1)
1014 // then we have the "non-unique poles" situation: points lie on great circle
1015 // and both poles are valid solution
1016 if (fabs(oldCenter.ScalarProduct(rotatednewCenter) + 1.)
1017 < std::numeric_limits<double>::epsilon()*1e4)
1018 rotatednewCenter *= -1.;
1019 LOG(4, "CHECK: rotatednewCenter is " << rotatednewCenter
1020 << ", oldCenter is " << oldCenter);
1021 const double difference = (rotatednewCenter - oldCenter).NormSquared();
1022 ASSERT( difference < std::numeric_limits<double>::epsilon()*1e4,
1023 "matchSphericalPointDistributions() - rotation does not work as expected by "
1024 +toString(difference)+".");
[bb011f]1025 }
[3da643]1026#endif
[bb011f]1027 }
[3da643]1028 /// the second (orientation) rotation aligns the planes such that the
1029 /// points themselves coincide
1030 if (bestmatching.size() > 1) {
[1cde4e8]1031 Rotation_t Rotation = findPointAligningRotation(oldSet, rotatednewSet);
[3da643]1032
1033 // construct RotationAxis and two points on its plane, defining the angle
1034 Rotation.first.Normalize();
1035 const Line RotationAxis(zeroVec, Rotation.first);
1036
1037 LOG(5, "DEBUG: Rotating around self is " << Rotation.second
1038 << " around axis " << RotationAxis);
[bb011f]1039
[2d50a2]1040#ifndef NDEBUG
[3da643]1041 // check: first bestmatching in rotated_newpolygon and remainingnew
1042 // should now equal
1043 {
1044 const IndexList_t::const_iterator iter = bestmatching.begin();
[1cde4e8]1045
1046 // check whether both old and newPosition are at same distance to oldCenter
1047 Vector oldCenter = calculateCenter(oldSet);
1048 const double distance = fabs(
1049 (oldSet.polygon[0] - oldCenter).NormSquared()
1050 - (rotatednewSet.polygon[*iter] - oldCenter).NormSquared()
1051 );
1052 LOG(4, "CHECK: Squared distance between oldPosition and newPosition "
1053 << " with respect to oldCenter " << oldCenter << " is " << distance);
1054// ASSERT( distance < warn_amplitude,
1055// "matchSphericalPointDistributions() - old and newPosition's squared distance to oldCenter differs by "
1056// +toString(distance));
1057
[3da643]1058 Vector rotatednew = RotationAxis.rotateVector(
[1cde4e8]1059 rotatednewSet.polygon[*iter],
[3da643]1060 Rotation.second);
1061 LOG(4, "CHECK: rotated first new bestmatching is " << rotatednew
[1cde4e8]1062 << " while old was " << oldSet.polygon[0]);
1063 const double difference = (rotatednew - oldSet.polygon[0]).NormSquared();
1064 ASSERT( difference < distance+1e-8,
1065 "matchSphericalPointDistributions() - orientation rotation ends up off by "
1066 +toString(difference)+", more than "
1067 +toString(distance+1e-8)+".");
[3da643]1068 }
[2d50a2]1069#endif
[0d5ca7]1070
[1cde4e8]1071 for (VectorArray_t::iterator iter = rotatednewSet.polygon.begin();
1072 iter != rotatednewSet.polygon.end(); ++iter) {
[3da643]1073 Vector &current = *iter;
1074 LOG(6, "DEBUG: Original point is " << current);
1075 current = RotationAxis.rotateVector(current, Rotation.second);
1076 LOG(6, "DEBUG: Rotated point is " << current);
[2d50a2]1077 }
1078 }
[0d4daf]1079
1080 // remove all points in matching and return remaining ones
[0d5ca7]1081 SphericalPointDistribution::Polygon_t remainingpoints =
[1cde4e8]1082 removeMatchingPoints(rotatednewSet);
[0d5ca7]1083 LOG(2, "INFO: Remaining points are " << remainingpoints);
1084 return remainingpoints;
[0d4daf]1085 } else
[e6ca85]1086 return points;
[0d4daf]1087}
1088
[ce0ca4]1089SphericalPointDistribution::PolygonWithIndexTuples
1090SphericalPointDistribution::getAssociatedPoints(
1091 const WeightedPolygon_t &_polygon,
1092 const int _N)
1093{
1094 SphericalPointDistribution::PolygonWithIndexTuples associatedpoints;
1095
1096 // initialze to given number of points
1097 initSelf(_N);
1098 LOG(2, "INFO: Matching old polygon " << _polygon
1099 << " with new polygon " << points);
1100
1101 // check whether there are any points to associate
1102 if (_polygon.empty()) {
1103 associatedpoints.polygon.insert(
1104 associatedpoints.polygon.end(),
1105 points.begin(), points.end());
1106 return associatedpoints;
1107 }
1108
1109 if (_N > 0) {
1110 // combine multiple points and create simple IndexList from IndexTupleList
1111 MatchingControlStructure MCS = findBestMatching(_polygon);
1112 IndexList_t bestmatching = joinPoints(points, MCS.newpoints, MCS.bestmatching);
1113 LOG(2, "INFO: Best matching is " << bestmatching);
1114
1115 // gather the associated points (not the joined ones)
1116 associatedpoints.polygon = MCS.newpoints;
1117 // gather indices
1118 associatedpoints.indices = MCS.bestmatching;
1119
1120 /// now we only need to rotate the associated points to match the given ones
1121 /// with respect to the joined points in points
1122
1123 const size_t NumberIds = std::min(bestmatching.size(), (size_t)3);
1124 // create old set
1125 PolygonWithIndices oldSet;
1126 oldSet.indices.resize(NumberIds, -1);
1127 std::generate(oldSet.indices.begin(), oldSet.indices.end(), UniqueNumber);
1128 for (WeightedPolygon_t::const_iterator iter = _polygon.begin();
1129 iter != _polygon.end(); ++iter)
1130 oldSet.polygon.push_back(iter->first);
1131
1132 // _newpolygon has changed, so now convert to array with matched indices
1133 PolygonWithIndices newSet;
1134 SphericalPointDistribution::IndexList_t::const_iterator beginiter = bestmatching.begin();
1135 SphericalPointDistribution::IndexList_t::const_iterator enditer = bestmatching.begin();
1136 std::advance(enditer, NumberIds);
1137 newSet.indices.resize(NumberIds, -1);
1138 std::copy(beginiter, enditer, newSet.indices.begin());
1139 std::copy(points.begin(),points.end(), std::back_inserter(newSet.polygon));
1140
1141 // determine rotation angles to align the two point distributions with
1142 // respect to bestmatching:
1143 // we use the center between the three first matching points
1144 /// the first rotation brings these two centers to coincide
1145 PolygonWithIndices rotatednewSet = newSet;
1146 {
1147 Rotation_t Rotation = findPlaneAligningRotation(oldSet, newSet);
1148 LOG(5, "DEBUG: Rotating coordinate system by " << Rotation.second
1149 << " around axis " << Rotation.first);
1150 Line Axis(zeroVec, Rotation.first);
1151
1152 // apply rotation angle to bring newCenter to oldCenter in joined points
1153 for (VectorArray_t::iterator iter = rotatednewSet.polygon.begin();
1154 iter != rotatednewSet.polygon.end(); ++iter) {
1155 Vector &current = *iter;
1156 LOG(6, "DEBUG: Original joined point is " << current);
1157 current = Axis.rotateVector(current, Rotation.second);
1158 LOG(6, "DEBUG: Rotated joined point is " << current);
1159 }
1160
1161 // apply rotation angle to the whole set of associated points
1162 for (VectorArray_t::iterator iter = associatedpoints.polygon.begin();
1163 iter != associatedpoints.polygon.end(); ++iter) {
1164 Vector &current = *iter;
1165 LOG(6, "DEBUG: Original associated point is " << current);
1166 current = Axis.rotateVector(current, Rotation.second);
1167 LOG(6, "DEBUG: Rotated associated point is " << current);
1168 }
1169
1170#ifndef NDEBUG
1171 // check: rotated "newCenter" should now equal oldCenter
1172 // we don't check in case of two points as these lie on a great circle
1173 // and the center cannot stably be recalculated. We may reactivate this
1174 // when we calculate centers only once
1175 if (oldSet.indices.size() > 2) {
1176 Vector oldCenter;
1177 Vector rotatednewCenter;
1178 calculateOldAndNewCenters(
1179 oldCenter, rotatednewCenter,
1180 oldSet, rotatednewSet);
1181 oldCenter.Normalize();
1182 rotatednewCenter.Normalize();
1183 // check whether centers are anti-parallel (factor -1)
1184 // then we have the "non-unique poles" situation: points lie on great circle
1185 // and both poles are valid solution
1186 if (fabs(oldCenter.ScalarProduct(rotatednewCenter) + 1.)
1187 < std::numeric_limits<double>::epsilon()*1e4)
1188 rotatednewCenter *= -1.;
1189 LOG(4, "CHECK: rotatednewCenter is " << rotatednewCenter
1190 << ", oldCenter is " << oldCenter);
1191 const double difference = (rotatednewCenter - oldCenter).NormSquared();
1192 ASSERT( difference < std::numeric_limits<double>::epsilon()*1e4,
1193 "matchSphericalPointDistributions() - rotation does not work as expected by "
1194 +toString(difference)+".");
1195 }
1196#endif
1197 }
1198 /// the second (orientation) rotation aligns the planes such that the
1199 /// points themselves coincide
1200 if (bestmatching.size() > 1) {
1201 Rotation_t Rotation = findPointAligningRotation(oldSet, rotatednewSet);
1202
1203 // construct RotationAxis and two points on its plane, defining the angle
1204 Rotation.first.Normalize();
1205 const Line RotationAxis(zeroVec, Rotation.first);
1206
1207 LOG(5, "DEBUG: Rotating around self is " << Rotation.second
1208 << " around axis " << RotationAxis);
1209
1210#ifndef NDEBUG
1211 // check: first bestmatching in rotated_newpolygon and remainingnew
1212 // should now equal
1213 {
1214 const IndexList_t::const_iterator iter = bestmatching.begin();
1215
1216 // check whether both old and newPosition are at same distance to oldCenter
1217 Vector oldCenter = calculateCenter(oldSet);
1218 const double distance = fabs(
1219 (oldSet.polygon[0] - oldCenter).NormSquared()
1220 - (rotatednewSet.polygon[*iter] - oldCenter).NormSquared()
1221 );
1222 LOG(4, "CHECK: Squared distance between oldPosition and newPosition "
1223 << " with respect to oldCenter " << oldCenter << " is " << distance);
1224// ASSERT( distance < warn_amplitude,
1225// "matchSphericalPointDistributions() - old and newPosition's squared distance to oldCenter differs by "
1226// +toString(distance));
[0d4daf]1227
[ce0ca4]1228 Vector rotatednew = RotationAxis.rotateVector(
1229 rotatednewSet.polygon[*iter],
1230 Rotation.second);
1231 LOG(4, "CHECK: rotated first new bestmatching is " << rotatednew
1232 << " while old was " << oldSet.polygon[0]);
1233 const double difference = (rotatednew - oldSet.polygon[0]).NormSquared();
1234 ASSERT( difference < distance+1e-8,
1235 "matchSphericalPointDistributions() - orientation rotation ends up off by "
1236 +toString(difference)+", more than "
1237 +toString(distance+1e-8)+".");
1238 }
1239#endif
1240
1241 // align the set of associated points only here
1242 for (VectorArray_t::iterator iter = associatedpoints.polygon.begin();
1243 iter != associatedpoints.polygon.end(); ++iter) {
1244 Vector &current = *iter;
1245 LOG(6, "DEBUG: Original associated point is " << current);
1246 current = RotationAxis.rotateVector(current, Rotation.second);
1247 LOG(6, "DEBUG: Rotated associated point is " << current);
1248 }
1249 }
1250 }
1251
1252 return associatedpoints;
1253}
1254
1255SphericalPointDistribution::PolygonWithIndexTuples
1256SphericalPointDistribution::getIdentityAssociation(
1257 const WeightedPolygon_t &_polygon)
1258{
1259 unsigned int index = 0;
1260 SphericalPointDistribution::PolygonWithIndexTuples returnpolygon;
1261 for (WeightedPolygon_t::const_iterator iter = _polygon.begin();
1262 iter != _polygon.end(); ++iter, ++index) {
1263 returnpolygon.polygon.push_back( iter->first );
1264 ASSERT( iter->second == 1,
1265 "getIdentityAssociation() - bond with direction "
1266 +toString(iter->second)
1267 +" has degree higher than 1, getIdentityAssociation makes no sense.");
1268 returnpolygon.indices.push_back( IndexList_t(1, index) );
1269 }
1270 return returnpolygon;
1271}
Note: See TracBrowser for help on using the repository browser.