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

Action_Thermostats Add_AtomRandomPerturbation Add_FitFragmentPartialChargesAction Add_RotateAroundBondAction Add_SelectAtomByNameAction Adding_Graph_to_ChangeBondActions Adding_MD_integration_tests Adding_StructOpt_integration_tests Automaking_mpqc_open AutomationFragmentation_failures Candidate_v1.5.4 Candidate_v1.6.0 Candidate_v1.6.1 ChangeBugEmailaddress ChangingTestPorts ChemicalSpaceEvaluator Combining_Subpackages Debian_Package_split Debian_package_split_molecuildergui_only Disabling_MemDebug Docu_Python_wait EmpiricalPotential_contain_HomologyGraph EmpiricalPotential_contain_HomologyGraph_documentation Enable_parallel_make_install Enhance_userguide Enhanced_StructuralOptimization Enhanced_StructuralOptimization_continued Example_ManyWaysToTranslateAtom Exclude_Hydrogens_annealWithBondGraph FitPartialCharges_GlobalError Fix_ChargeSampling_PBC Fix_ChronosMutex Fix_FitPartialCharges Fix_FitPotential_needs_atomicnumbers Fix_ForceAnnealing Fix_IndependentFragmentGrids Fix_ParseParticles Fix_ParseParticles_split_forward_backward_Actions Fix_StatusMsg Fix_StepWorldTime_single_argument Fix_Verbose_Codepatterns ForceAnnealing_goodresults ForceAnnealing_oldresults ForceAnnealing_tocheck ForceAnnealing_with_BondGraph ForceAnnealing_with_BondGraph_continued ForceAnnealing_with_BondGraph_continued_betteresults ForceAnnealing_with_BondGraph_contraction-expansion GeometryObjects Gui_displays_atomic_force_velocity IndependentFragmentGrids IndependentFragmentGrids_IndividualZeroInstances IndependentFragmentGrids_IntegrationTest IndependentFragmentGrids_Sole_NN_Calculation JobMarket_RobustOnKillsSegFaults JobMarket_StableWorkerPool JobMarket_unresolvable_hostname_fix ODR_violation_mpqc_open PartialCharges_OrthogonalSummation PythonUI_with_named_parameters QtGui_reactivate_TimeChanged_changes Recreated_GuiChecks RotateToPrincipalAxisSystem_UndoRedo SaturateAtoms_findBestMatching StoppableMakroAction Subpackage_CodePatterns Subpackage_JobMarket Subpackage_LinearAlgebra Subpackage_levmar Subpackage_mpqc_open Subpackage_vmg ThirdParty_MPQC_rebuilt_buildsystem TrajectoryDependenant_MaxOrder TremoloParser_IncreasedPrecision TremoloParser_MultipleTimesteps Ubuntu_1604_changes stable
Last change on this file since b67d89 was b67d89, checked in by Frederik Heber <heber@…>, 8 years ago

Using the idea of three points giving a triangle to find rotation axis.

  • we calculate the center of either triangle and rotate the center of the ideal point distribution to match the one from the given points.
  • next we have the triangles normals as axis, take the first matching point and rotate align it.
  • we have to deal with a lot of special cases: What if only zero, one, or two points are given ...
  • in general we assume that the triangle lies relatively flat on the sphere's surface but what if the origin is in the triangle plane or even the calculated center is at the origin ...
  • TESTS: SphericalPointDistributionUnitTest working again, regression tests FragmentMolecule-cylces and StoreSaturatedFragment working.
  • Property mode set to 100644
File size: 22.9 KB
Line 
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"
41#include "CodePatterns/Log.hpp"
42#include "CodePatterns/toString.hpp"
43
44#include <algorithm>
45#include <boost/math/quaternion.hpp>
46#include <cmath>
47#include <functional>
48#include <iterator>
49#include <limits>
50#include <list>
51#include <vector>
52#include <map>
53
54#include "LinearAlgebra/Line.hpp"
55#include "LinearAlgebra/Plane.hpp"
56#include "LinearAlgebra/RealSpaceMatrix.hpp"
57#include "LinearAlgebra/Vector.hpp"
58
59// static entities
60const double SphericalPointDistribution::SQRT_3(sqrt(3.0));
61const double SphericalPointDistribution::warn_amplitude = 1e-2;
62
63typedef std::vector<double> DistanceArray_t;
64
65inline
66DistanceArray_t calculatePairwiseDistances(
67 const std::vector<Vector> &_points,
68 const SphericalPointDistribution::IndexList_t &_indices
69 )
70{
71 DistanceArray_t result;
72 for (SphericalPointDistribution::IndexList_t::const_iterator firstiter = _indices.begin();
73 firstiter != _indices.end(); ++firstiter) {
74 for (SphericalPointDistribution::IndexList_t::const_iterator seconditer = firstiter;
75 seconditer != _indices.end(); ++seconditer) {
76 if (firstiter == seconditer)
77 continue;
78 const double distance = (_points[*firstiter] - _points[*seconditer]).NormSquared();
79 result.push_back(distance);
80 }
81 }
82 return result;
83}
84
85// class generator: taken from www.cplusplus.com example std::generate
86struct c_unique {
87 int current;
88 c_unique() {current=0;}
89 int operator()() {return current++;}
90} UniqueNumber;
91
92/** Returns squared L2 error of the given \a _Matching.
93 *
94 * We compare the pair-wise distances of each associated matching
95 * and check whether these distances each match between \a _old and
96 * \a _new.
97 *
98 * \param _old first set of returnpolygon (fewer or equal to \a _new)
99 * \param _new second set of returnpolygon
100 * \param _Matching matching between the two sets
101 * \return pair with L1 and squared L2 error
102 */
103std::pair<double, double> SphericalPointDistribution::calculateErrorOfMatching(
104 const std::vector<Vector> &_old,
105 const std::vector<Vector> &_new,
106 const IndexList_t &_Matching)
107{
108 std::pair<double, double> errors( std::make_pair( 0., 0. ) );
109
110 if (_Matching.size() > 1) {
111 LOG(3, "INFO: Matching is " << _Matching);
112
113 // calculate all pair-wise distances
114 IndexList_t keys(_Matching.size());
115 std::generate (keys.begin(), keys.end(), UniqueNumber);
116 const DistanceArray_t firstdistances = calculatePairwiseDistances(_old, keys);
117 const DistanceArray_t seconddistances = calculatePairwiseDistances(_new, _Matching);
118
119 ASSERT( firstdistances.size() == seconddistances.size(),
120 "calculateL2ErrorOfMatching() - mismatch in pair-wise distance array sizes.");
121 DistanceArray_t::const_iterator firstiter = firstdistances.begin();
122 DistanceArray_t::const_iterator seconditer = seconddistances.begin();
123 for (;(firstiter != firstdistances.end()) && (seconditer != seconddistances.end());
124 ++firstiter, ++seconditer) {
125 const double gap = *firstiter - *seconditer;
126 // L1 error
127 if (errors.first < gap)
128 errors.first = gap;
129 // L2 error
130 errors.second += gap*gap;
131 }
132 } else
133 ELOG(3, "calculateErrorOfMatching() - Given matching's size is less than 2.");
134 LOG(3, "INFO: Resulting errors for matching (L1, L2): "
135 << errors.first << "," << errors.second << ".");
136
137 return errors;
138}
139
140SphericalPointDistribution::Polygon_t SphericalPointDistribution::removeMatchingPoints(
141 const VectorArray_t &_points,
142 const IndexList_t &_matchingindices
143 )
144{
145 SphericalPointDistribution::Polygon_t remainingreturnpolygon;
146 IndexArray_t indices(_matchingindices.begin(), _matchingindices.end());
147 std::sort(indices.begin(), indices.end());
148 LOG(4, "DEBUG: sorted matching is " << indices);
149 IndexArray_t remainingindices(_points.size(), -1);
150 std::generate(remainingindices.begin(), remainingindices.end(), UniqueNumber);
151 IndexArray_t::iterator remainiter = std::set_difference(
152 remainingindices.begin(), remainingindices.end(),
153 indices.begin(), indices.end(),
154 remainingindices.begin());
155 remainingindices.erase(remainiter, remainingindices.end());
156 LOG(4, "DEBUG: remaining indices are " << remainingindices);
157 for (IndexArray_t::const_iterator iter = remainingindices.begin();
158 iter != remainingindices.end(); ++iter) {
159 remainingreturnpolygon.push_back(_points[*iter]);
160 }
161
162 return remainingreturnpolygon;
163}
164
165/** Recursive function to go through all possible matchings.
166 *
167 * \param _MCS structure holding global information to the recursion
168 * \param _matching current matching being build up
169 * \param _indices contains still available indices
170 * \param _matchingsize
171 */
172void SphericalPointDistribution::recurseMatchings(
173 MatchingControlStructure &_MCS,
174 IndexList_t &_matching,
175 IndexList_t _indices,
176 unsigned int _matchingsize)
177{
178 LOG(4, "DEBUG: Recursing with current matching " << _matching
179 << ", remaining indices " << _indices
180 << ", and sought size " << _matching.size()+_matchingsize);
181 //!> threshold for L1 error below which matching is immediately acceptable
182 const double L1THRESHOLD = 1e-2;
183 if (!_MCS.foundflag) {
184 LOG(4, "DEBUG: Current matching has size " << _matching.size() << ", places left " << _matchingsize);
185 if (_matchingsize > 0) {
186 // go through all indices
187 for (IndexList_t::iterator iter = _indices.begin();
188 (iter != _indices.end()) && (!_MCS.foundflag);) {
189 // add index to matching
190 _matching.push_back(*iter);
191 LOG(5, "DEBUG: Adding " << *iter << " to matching.");
192 // remove index but keep iterator to position (is the next to erase element)
193 IndexList_t::iterator backupiter = _indices.erase(iter);
194 // recurse with decreased _matchingsize
195 recurseMatchings(_MCS, _matching, _indices, _matchingsize-1);
196 // re-add chosen index and reset index to new position
197 _indices.insert(backupiter, _matching.back());
198 iter = backupiter;
199 // remove index from _matching to make space for the next one
200 _matching.pop_back();
201 }
202 // gone through all indices then exit recursion
203 if (_matching.empty())
204 _MCS.foundflag = true;
205 } else {
206 LOG(3, "INFO: Found matching " << _matching);
207 // calculate errors
208 std::pair<double, double> errors = calculateErrorOfMatching(
209 _MCS.oldpoints, _MCS.newpoints, _matching);
210 if (errors.first < L1THRESHOLD) {
211 _MCS.bestmatching = _matching;
212 _MCS.foundflag = true;
213 } else if (_MCS.bestL2 > errors.second) {
214 _MCS.bestmatching = _matching;
215 _MCS.bestL2 = errors.second;
216 }
217 }
218 }
219}
220
221/** Decides by an orthonormal third vector whether the sign of the rotation
222 * angle should be negative or positive.
223 *
224 * \return -1 or 1
225 */
226inline
227double determineSignOfRotation(
228 const Vector &_oldPosition,
229 const Vector &_newPosition,
230 const Vector &_RotationAxis
231 )
232{
233 Vector dreiBein(_oldPosition);
234 dreiBein.VectorProduct(_RotationAxis);
235 dreiBein.Normalize();
236 const double sign =
237 (dreiBein.ScalarProduct(_newPosition) < 0.) ? -1. : +1.;
238 LOG(6, "DEBUG: oldCenter on plane is " << _oldPosition
239 << ", newCenter in plane is " << _newPosition
240 << ", and dreiBein is " << dreiBein);
241 return sign;
242}
243
244/** Finds combinatorially the best matching between points in \a _polygon
245 * and \a _newpolygon.
246 *
247 * We find the matching with the smallest L2 error, where we break when we stumble
248 * upon a matching with zero error.
249 *
250 * \sa recurseMatchings() for going through all matchings
251 *
252 * \param _polygon here, we have indices 0,1,2,...
253 * \param _newpolygon and here we need to find the correct indices
254 * \return list of indices: first in \a _polygon goes to first index for \a _newpolygon
255 */
256SphericalPointDistribution::IndexList_t SphericalPointDistribution::findBestMatching(
257 const SphericalPointDistribution::Polygon_t &_polygon,
258 const SphericalPointDistribution::Polygon_t &_newpolygon
259 )
260{
261 MatchingControlStructure MCS;
262 MCS.foundflag = false;
263 MCS.bestL2 = std::numeric_limits<double>::max();
264 MCS.oldpoints.insert(MCS.oldpoints.begin(), _polygon.begin(),_polygon.end() );
265 MCS.newpoints.insert(MCS.newpoints.begin(), _newpolygon.begin(),_newpolygon.end() );
266
267 // search for bestmatching combinatorially
268 {
269 // translate polygon into vector to enable index addressing
270 IndexList_t indices(_newpolygon.size());
271 std::generate(indices.begin(), indices.end(), UniqueNumber);
272 IndexList_t matching;
273
274 // walk through all matchings
275 const unsigned int matchingsize = _polygon.size();
276 ASSERT( matchingsize <= indices.size(),
277 "SphericalPointDistribution::matchSphericalPointDistributions() - not enough new points to choose for matching to old ones.");
278 recurseMatchings(MCS, matching, indices, matchingsize);
279 }
280 return MCS.bestmatching;
281}
282
283inline
284Vector calculateCenter(
285 const SphericalPointDistribution::VectorArray_t &_positions,
286 const SphericalPointDistribution::IndexList_t &_indices)
287{
288 Vector Center;
289 Center.Zero();
290 for (SphericalPointDistribution::IndexList_t::const_iterator iter = _indices.begin();
291 iter != _indices.end(); ++iter)
292 Center += _positions[*iter];
293 if (!_indices.empty())
294 Center *= 1./(double)_indices.size();
295
296 return Center;
297}
298
299inline
300void calculateOldAndNewCenters(
301 Vector &_oldCenter,
302 Vector &_newCenter,
303 const SphericalPointDistribution::VectorArray_t &_referencepositions,
304 const SphericalPointDistribution::VectorArray_t &_currentpositions,
305 const SphericalPointDistribution::IndexList_t &_bestmatching)
306{
307 const size_t NumberIds = std::min(_bestmatching.size(), (size_t)3);
308 SphericalPointDistribution::IndexList_t continuousIds(NumberIds, -1);
309 std::generate(continuousIds.begin(), continuousIds.end(), UniqueNumber);
310 _oldCenter = calculateCenter(_referencepositions, continuousIds);
311 // C++11 defines a copy_n function ...
312 SphericalPointDistribution::IndexList_t::const_iterator enditer = _bestmatching.begin();
313 std::advance(enditer, NumberIds);
314 SphericalPointDistribution::IndexList_t firstbestmatchingIds(NumberIds, -1);
315 std::copy(_bestmatching.begin(), enditer, firstbestmatchingIds.begin());
316 _newCenter = calculateCenter( _currentpositions, firstbestmatchingIds);
317}
318
319SphericalPointDistribution::Rotation_t SphericalPointDistribution::findPlaneAligningRotation(
320 const VectorArray_t &_referencepositions,
321 const VectorArray_t &_currentpositions,
322 const IndexList_t &_bestmatching
323 )
324{
325#ifndef NDEBUG
326 bool dontcheck = false;
327#endif
328 // initialize to no rotation
329 Rotation_t Rotation;
330 Rotation.first.Zero();
331 Rotation.first[0] = 1.;
332 Rotation.second = 0.;
333
334 // calculate center of triangle/line/point consisting of first points of matching
335 Vector oldCenter;
336 Vector newCenter;
337 calculateOldAndNewCenters(
338 oldCenter, newCenter,
339 _referencepositions, _currentpositions, _bestmatching);
340
341 if ((!oldCenter.IsZero()) && (!newCenter.IsZero())) {
342 LOG(4, "DEBUG: oldCenter is " << oldCenter << ", newCenter is " << newCenter);
343 oldCenter.Normalize();
344 newCenter.Normalize();
345 if (!oldCenter.IsEqualTo(newCenter)) {
346 // calculate rotation axis and angle
347 Rotation.first = oldCenter;
348 Rotation.first.VectorProduct(newCenter);
349 Rotation.second = oldCenter.Angle(newCenter); // /(M_PI/2.);
350 } else {
351 // no rotation required anymore
352 }
353 } else {
354 LOG(4, "DEBUG: oldCenter is " << oldCenter << ", newCenter is " << newCenter);
355 if ((oldCenter.IsZero()) && (newCenter.IsZero())) {
356 // either oldCenter or newCenter (or both) is directly at origin
357 if (_bestmatching.size() == 2) {
358 // line case
359 Vector oldPosition = _currentpositions[*_bestmatching.begin()];
360 Vector newPosition = _referencepositions[0];
361 // check whether we need to rotate at all
362 if (!oldPosition.IsEqualTo(newPosition)) {
363 Rotation.first = oldPosition;
364 Rotation.first.VectorProduct(newPosition);
365 // orientation will fix the sign here eventually
366 Rotation.second = oldPosition.Angle(newPosition);
367 } else {
368 // no rotation required anymore
369 }
370 } else {
371 // triangle case
372 // both triangles/planes have same center, hence get axis by
373 // VectorProduct of Normals
374 Plane newplane(_referencepositions[0], _referencepositions[1], _referencepositions[2]);
375 VectorArray_t vectors;
376 for (IndexList_t::const_iterator iter = _bestmatching.begin();
377 iter != _bestmatching.end(); ++iter)
378 vectors.push_back(_currentpositions[*iter]);
379 Plane oldplane(vectors[0], vectors[1], vectors[2]);
380 Vector oldPosition = oldplane.getNormal();
381 Vector newPosition = newplane.getNormal();
382 // check whether we need to rotate at all
383 if (!oldPosition.IsEqualTo(newPosition)) {
384 Rotation.first = oldPosition;
385 Rotation.first.VectorProduct(newPosition);
386 Rotation.first.Normalize();
387
388 // construct reference vector to determine direction of rotation
389 const double sign = determineSignOfRotation(oldPosition, newPosition, Rotation.first);
390 Rotation.second = sign * oldPosition.Angle(newPosition);
391 LOG(5, "DEBUG: Rotating plane normals by " << Rotation.second
392 << " around axis " << Rotation.first);
393 } else {
394 // else do nothing
395 }
396 }
397 } else {
398 // TODO: we can't do anything here, but this case needs to be dealt with when
399 // we have no ideal geometries anymore
400 if ((oldCenter-newCenter).Norm() > warn_amplitude)
401 ELOG(2, "oldCenter is " << oldCenter << ", yet newCenter is " << newCenter);
402#ifndef NDEBUG
403 // else they are considered close enough
404 dontcheck = true;
405#endif
406 }
407 }
408
409#ifndef NDEBUG
410 // check: rotation brings newCenter onto oldCenter position
411 if (!dontcheck) {
412 Line Axis(zeroVec, Rotation.first);
413 Vector test = Axis.rotateVector(newCenter, Rotation.second);
414 LOG(4, "CHECK: rotated newCenter is " << test
415 << ", oldCenter is " << oldCenter);
416 ASSERT( (test - oldCenter).NormSquared() < std::numeric_limits<double>::epsilon()*1e4,
417 "matchSphericalPointDistributions() - rotation does not work as expected by "
418 +toString((test - oldCenter).NormSquared())+".");
419 }
420#endif
421
422 return Rotation;
423}
424
425SphericalPointDistribution::Rotation_t SphericalPointDistribution::findPointAligningRotation(
426 const VectorArray_t &remainingold,
427 const VectorArray_t &remainingnew,
428 const IndexList_t &_bestmatching)
429{
430 // initialize rotation to zero
431 Rotation_t Rotation;
432 Rotation.first.Zero();
433 Rotation.first[0] = 1.;
434 Rotation.second = 0.;
435
436 // recalculate center
437 Vector oldCenter;
438 Vector newCenter;
439 calculateOldAndNewCenters(
440 oldCenter, newCenter,
441 remainingold, remainingnew, _bestmatching);
442
443 Vector oldPosition = remainingnew[*_bestmatching.begin()];
444 Vector newPosition = remainingold[0];
445 LOG(6, "DEBUG: oldPosition is " << oldPosition << " and newPosition is " << newPosition);
446 if (!oldPosition.IsEqualTo(newPosition)) {
447 if ((!oldCenter.IsZero()) && (!newCenter.IsZero())) {
448 oldCenter.Normalize(); // note weighted sum of normalized weight is not normalized
449 Rotation.first = oldCenter;
450 LOG(6, "DEBUG: Picking normalized oldCenter as Rotation.first " << oldCenter);
451 oldPosition.ProjectOntoPlane(Rotation.first);
452 newPosition.ProjectOntoPlane(Rotation.first);
453 LOG(6, "DEBUG: Positions after projection are " << oldPosition << " and " << newPosition);
454 } else {
455 if (_bestmatching.size() == 2) {
456 // line situation
457 try {
458 Plane oldplane(oldPosition, oldCenter, newPosition);
459 Rotation.first = oldplane.getNormal();
460 LOG(6, "DEBUG: Plane is " << oldplane << " and normal is " << Rotation.first);
461 } catch (LinearDependenceException &e) {
462 LOG(6, "DEBUG: Vectors defining plane are linearly dependent.");
463 // oldPosition and newPosition are on a line, just flip when not equal
464 if (!oldPosition.IsEqualTo(newPosition)) {
465 Rotation.first.Zero();
466 Rotation.first.GetOneNormalVector(oldPosition);
467 LOG(6, "DEBUG: For flipping we use Rotation.first " << Rotation.first);
468 assert( Rotation.first.ScalarProduct(oldPosition) < std::numeric_limits<double>::epsilon()*1e4);
469 // Rotation.second = M_PI;
470 } else {
471 LOG(6, "DEBUG: oldPosition and newPosition are equivalent.");
472 }
473 }
474 } else {
475 // triangle situation
476 Plane oldplane(remainingold[0], remainingold[1], remainingold[2]);
477 Rotation.first = oldplane.getNormal();
478 LOG(6, "DEBUG: oldPlane is " << oldplane << " and normal is " << Rotation.first);
479 oldPosition.ProjectOntoPlane(Rotation.first);
480 LOG(6, "DEBUG: Positions after projection are " << oldPosition << " and " << newPosition);
481 }
482 }
483 // construct reference vector to determine direction of rotation
484 const double sign = determineSignOfRotation(oldPosition, newPosition, Rotation.first);
485 Rotation.second = sign * oldPosition.Angle(newPosition);
486 } else {
487 LOG(6, "DEBUG: oldPosition and newPosition are equivalent, hence no orientating rotation.");
488 }
489
490 return Rotation;
491}
492
493
494SphericalPointDistribution::Polygon_t
495SphericalPointDistribution::matchSphericalPointDistributions(
496 const SphericalPointDistribution::Polygon_t &_polygon,
497 const SphericalPointDistribution::Polygon_t &_newpolygon
498 )
499{
500 SphericalPointDistribution::Polygon_t remainingreturnpolygon;
501 VectorArray_t remainingold(_polygon.begin(), _polygon.end());
502 VectorArray_t remainingnew(_newpolygon.begin(), _newpolygon.end());
503 LOG(2, "INFO: Matching old polygon " << _polygon
504 << " with new polygon " << _newpolygon);
505
506 if (_polygon.size() == _newpolygon.size()) {
507 // same number of points desired as are present? Do nothing
508 LOG(2, "INFO: There are no vacant points to return.");
509 return remainingreturnpolygon;
510 }
511
512 if (_polygon.size() > 0) {
513 IndexList_t bestmatching = findBestMatching(_polygon, _newpolygon);
514 LOG(2, "INFO: Best matching is " << bestmatching);
515
516 // determine rotation angles to align the two point distributions with
517 // respect to bestmatching:
518 // we use the center between the three first matching points
519 /// the first rotation brings these two centers to coincide
520 VectorArray_t rotated_newpolygon = remainingnew;
521 {
522 Rotation_t Rotation = findPlaneAligningRotation(
523 remainingold,
524 remainingnew,
525 bestmatching);
526 LOG(5, "DEBUG: Rotating coordinate system by " << Rotation.second
527 << " around axis " << Rotation.first);
528 Line Axis(zeroVec, Rotation.first);
529
530 // apply rotation angle to bring newCenter to oldCenter
531 for (VectorArray_t::iterator iter = rotated_newpolygon.begin();
532 iter != rotated_newpolygon.end(); ++iter) {
533 Vector &current = *iter;
534 LOG(6, "DEBUG: Original point is " << current);
535 current = Axis.rotateVector(current, Rotation.second);
536 LOG(6, "DEBUG: Rotated point is " << current);
537 }
538
539#ifndef NDEBUG
540 // check: rotated "newCenter" should now equal oldCenter
541 {
542 Vector oldCenter;
543 Vector rotatednewCenter;
544 calculateOldAndNewCenters(
545 oldCenter, rotatednewCenter,
546 remainingold, rotated_newpolygon, bestmatching);
547 // NOTE: Center must not necessarily lie on the sphere with norm 1, hence, we
548 // have to normalize it just as before, as oldCenter and newCenter lengths may differ.
549 if ((!oldCenter.IsZero()) && (!rotatednewCenter.IsZero())) {
550 oldCenter.Normalize();
551 rotatednewCenter.Normalize();
552 LOG(4, "CHECK: rotatednewCenter is " << rotatednewCenter
553 << ", oldCenter is " << oldCenter);
554 ASSERT( (rotatednewCenter - oldCenter).NormSquared() < std::numeric_limits<double>::epsilon()*1e4,
555 "matchSphericalPointDistributions() - rotation does not work as expected by "
556 +toString((rotatednewCenter - oldCenter).NormSquared())+".");
557 }
558 }
559#endif
560 }
561 /// the second (orientation) rotation aligns the planes such that the
562 /// points themselves coincide
563 if (bestmatching.size() > 1) {
564 Rotation_t Rotation = findPointAligningRotation(
565 remainingold,
566 rotated_newpolygon,
567 bestmatching);
568
569 // construct RotationAxis and two points on its plane, defining the angle
570 Rotation.first.Normalize();
571 const Line RotationAxis(zeroVec, Rotation.first);
572
573 LOG(5, "DEBUG: Rotating around self is " << Rotation.second
574 << " around axis " << RotationAxis);
575
576#ifndef NDEBUG
577 // check: first bestmatching in rotated_newpolygon and remainingnew
578 // should now equal
579 {
580 const IndexList_t::const_iterator iter = bestmatching.begin();
581 Vector rotatednew = RotationAxis.rotateVector(
582 rotated_newpolygon[*iter],
583 Rotation.second);
584 LOG(4, "CHECK: rotated first new bestmatching is " << rotatednew
585 << " while old was " << remainingold[0]);
586 ASSERT( (rotatednew - remainingold[0]).Norm() < warn_amplitude,
587 "matchSphericalPointDistributions() - orientation rotation ends up off by more than "
588 +toString(warn_amplitude)+".");
589 }
590#endif
591
592 for (VectorArray_t::iterator iter = rotated_newpolygon.begin();
593 iter != rotated_newpolygon.end(); ++iter) {
594 Vector &current = *iter;
595 LOG(6, "DEBUG: Original point is " << current);
596 current = RotationAxis.rotateVector(current, Rotation.second);
597 LOG(6, "DEBUG: Rotated point is " << current);
598 }
599 }
600
601 // remove all points in matching and return remaining ones
602 SphericalPointDistribution::Polygon_t remainingpoints =
603 removeMatchingPoints(rotated_newpolygon, bestmatching);
604 LOG(2, "INFO: Remaining points are " << remainingpoints);
605 return remainingpoints;
606 } else
607 return _newpolygon;
608}
Note: See TracBrowser for help on using the repository browser.