source: src/Fragmentation/Exporters/SphericalPointDistribution.cpp@ 2199c2

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 2199c2 was 2199c2, checked in by Frederik Heber <heber@…>, 8 years ago

Extended SphericalPointDistribution::Polygon_t to WeightedPolygon_t.

  • contains additionally the weights from the already present points.
  • in order to deal sensibly with present bonds of higher degree (>1) that shift neighboring occupied orbitals even further away, we additionally pass on the bond degree. This indicates how many points of the N points have to be accumulated for this on present bond.
  • TESTS: Regression test FragmentMolecule-cylces failing for the moment.
  • Property mode set to 100644
File size: 23.1 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::WeightedPolygon_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 for (WeightedPolygon_t::const_iterator iter = _polygon.begin();
265 iter != _polygon.end(); ++iter)
266 MCS.oldpoints.push_back(iter->first);
267 MCS.newpoints.insert(MCS.newpoints.begin(), _newpolygon.begin(),_newpolygon.end() );
268
269 // search for bestmatching combinatorially
270 {
271 // translate polygon into vector to enable index addressing
272 IndexList_t indices(_newpolygon.size());
273 std::generate(indices.begin(), indices.end(), UniqueNumber);
274 IndexList_t matching;
275
276 // walk through all matchings
277 const unsigned int matchingsize = _polygon.size();
278 ASSERT( matchingsize <= indices.size(),
279 "SphericalPointDistribution::matchSphericalPointDistributions() - not enough new points to choose for matching to old ones.");
280 recurseMatchings(MCS, matching, indices, matchingsize);
281 }
282 return MCS.bestmatching;
283}
284
285inline
286Vector calculateCenter(
287 const SphericalPointDistribution::VectorArray_t &_positions,
288 const SphericalPointDistribution::IndexList_t &_indices)
289{
290 Vector Center;
291 Center.Zero();
292 for (SphericalPointDistribution::IndexList_t::const_iterator iter = _indices.begin();
293 iter != _indices.end(); ++iter)
294 Center += _positions[*iter];
295 if (!_indices.empty())
296 Center *= 1./(double)_indices.size();
297
298 return Center;
299}
300
301inline
302void calculateOldAndNewCenters(
303 Vector &_oldCenter,
304 Vector &_newCenter,
305 const SphericalPointDistribution::VectorArray_t &_referencepositions,
306 const SphericalPointDistribution::VectorArray_t &_currentpositions,
307 const SphericalPointDistribution::IndexList_t &_bestmatching)
308{
309 const size_t NumberIds = std::min(_bestmatching.size(), (size_t)3);
310 SphericalPointDistribution::IndexList_t continuousIds(NumberIds, -1);
311 std::generate(continuousIds.begin(), continuousIds.end(), UniqueNumber);
312 _oldCenter = calculateCenter(_referencepositions, continuousIds);
313 // C++11 defines a copy_n function ...
314 SphericalPointDistribution::IndexList_t::const_iterator enditer = _bestmatching.begin();
315 std::advance(enditer, NumberIds);
316 SphericalPointDistribution::IndexList_t firstbestmatchingIds(NumberIds, -1);
317 std::copy(_bestmatching.begin(), enditer, firstbestmatchingIds.begin());
318 _newCenter = calculateCenter( _currentpositions, firstbestmatchingIds);
319}
320
321SphericalPointDistribution::Rotation_t SphericalPointDistribution::findPlaneAligningRotation(
322 const VectorArray_t &_referencepositions,
323 const VectorArray_t &_currentpositions,
324 const IndexList_t &_bestmatching
325 )
326{
327#ifndef NDEBUG
328 bool dontcheck = false;
329#endif
330 // initialize to no rotation
331 Rotation_t Rotation;
332 Rotation.first.Zero();
333 Rotation.first[0] = 1.;
334 Rotation.second = 0.;
335
336 // calculate center of triangle/line/point consisting of first points of matching
337 Vector oldCenter;
338 Vector newCenter;
339 calculateOldAndNewCenters(
340 oldCenter, newCenter,
341 _referencepositions, _currentpositions, _bestmatching);
342
343 if ((!oldCenter.IsZero()) && (!newCenter.IsZero())) {
344 LOG(4, "DEBUG: oldCenter is " << oldCenter << ", newCenter is " << newCenter);
345 oldCenter.Normalize();
346 newCenter.Normalize();
347 if (!oldCenter.IsEqualTo(newCenter)) {
348 // calculate rotation axis and angle
349 Rotation.first = oldCenter;
350 Rotation.first.VectorProduct(newCenter);
351 Rotation.second = oldCenter.Angle(newCenter); // /(M_PI/2.);
352 } else {
353 // no rotation required anymore
354 }
355 } else {
356 LOG(4, "DEBUG: oldCenter is " << oldCenter << ", newCenter is " << newCenter);
357 if ((oldCenter.IsZero()) && (newCenter.IsZero())) {
358 // either oldCenter or newCenter (or both) is directly at origin
359 if (_bestmatching.size() == 2) {
360 // line case
361 Vector oldPosition = _currentpositions[*_bestmatching.begin()];
362 Vector newPosition = _referencepositions[0];
363 // check whether we need to rotate at all
364 if (!oldPosition.IsEqualTo(newPosition)) {
365 Rotation.first = oldPosition;
366 Rotation.first.VectorProduct(newPosition);
367 // orientation will fix the sign here eventually
368 Rotation.second = oldPosition.Angle(newPosition);
369 } else {
370 // no rotation required anymore
371 }
372 } else {
373 // triangle case
374 // both triangles/planes have same center, hence get axis by
375 // VectorProduct of Normals
376 Plane newplane(_referencepositions[0], _referencepositions[1], _referencepositions[2]);
377 VectorArray_t vectors;
378 for (IndexList_t::const_iterator iter = _bestmatching.begin();
379 iter != _bestmatching.end(); ++iter)
380 vectors.push_back(_currentpositions[*iter]);
381 Plane oldplane(vectors[0], vectors[1], vectors[2]);
382 Vector oldPosition = oldplane.getNormal();
383 Vector newPosition = newplane.getNormal();
384 // check whether we need to rotate at all
385 if (!oldPosition.IsEqualTo(newPosition)) {
386 Rotation.first = oldPosition;
387 Rotation.first.VectorProduct(newPosition);
388 Rotation.first.Normalize();
389
390 // construct reference vector to determine direction of rotation
391 const double sign = determineSignOfRotation(oldPosition, newPosition, Rotation.first);
392 Rotation.second = sign * oldPosition.Angle(newPosition);
393 LOG(5, "DEBUG: Rotating plane normals by " << Rotation.second
394 << " around axis " << Rotation.first);
395 } else {
396 // else do nothing
397 }
398 }
399 } else {
400 // TODO: we can't do anything here, but this case needs to be dealt with when
401 // we have no ideal geometries anymore
402 if ((oldCenter-newCenter).Norm() > warn_amplitude)
403 ELOG(2, "oldCenter is " << oldCenter << ", yet newCenter is " << newCenter);
404#ifndef NDEBUG
405 // else they are considered close enough
406 dontcheck = true;
407#endif
408 }
409 }
410
411#ifndef NDEBUG
412 // check: rotation brings newCenter onto oldCenter position
413 if (!dontcheck) {
414 Line Axis(zeroVec, Rotation.first);
415 Vector test = Axis.rotateVector(newCenter, Rotation.second);
416 LOG(4, "CHECK: rotated newCenter is " << test
417 << ", oldCenter is " << oldCenter);
418 ASSERT( (test - oldCenter).NormSquared() < std::numeric_limits<double>::epsilon()*1e4,
419 "matchSphericalPointDistributions() - rotation does not work as expected by "
420 +toString((test - oldCenter).NormSquared())+".");
421 }
422#endif
423
424 return Rotation;
425}
426
427SphericalPointDistribution::Rotation_t SphericalPointDistribution::findPointAligningRotation(
428 const VectorArray_t &remainingold,
429 const VectorArray_t &remainingnew,
430 const IndexList_t &_bestmatching)
431{
432 // initialize rotation to zero
433 Rotation_t Rotation;
434 Rotation.first.Zero();
435 Rotation.first[0] = 1.;
436 Rotation.second = 0.;
437
438 // recalculate center
439 Vector oldCenter;
440 Vector newCenter;
441 calculateOldAndNewCenters(
442 oldCenter, newCenter,
443 remainingold, remainingnew, _bestmatching);
444
445 Vector oldPosition = remainingnew[*_bestmatching.begin()];
446 Vector newPosition = remainingold[0];
447 LOG(6, "DEBUG: oldPosition is " << oldPosition << " and newPosition is " << newPosition);
448 if (!oldPosition.IsEqualTo(newPosition)) {
449 if ((!oldCenter.IsZero()) && (!newCenter.IsZero())) {
450 oldCenter.Normalize(); // note weighted sum of normalized weight is not normalized
451 Rotation.first = oldCenter;
452 LOG(6, "DEBUG: Picking normalized oldCenter as Rotation.first " << oldCenter);
453 oldPosition.ProjectOntoPlane(Rotation.first);
454 newPosition.ProjectOntoPlane(Rotation.first);
455 LOG(6, "DEBUG: Positions after projection are " << oldPosition << " and " << newPosition);
456 } else {
457 if (_bestmatching.size() == 2) {
458 // line situation
459 try {
460 Plane oldplane(oldPosition, oldCenter, newPosition);
461 Rotation.first = oldplane.getNormal();
462 LOG(6, "DEBUG: Plane is " << oldplane << " and normal is " << Rotation.first);
463 } catch (LinearDependenceException &e) {
464 LOG(6, "DEBUG: Vectors defining plane are linearly dependent.");
465 // oldPosition and newPosition are on a line, just flip when not equal
466 if (!oldPosition.IsEqualTo(newPosition)) {
467 Rotation.first.Zero();
468 Rotation.first.GetOneNormalVector(oldPosition);
469 LOG(6, "DEBUG: For flipping we use Rotation.first " << Rotation.first);
470 assert( Rotation.first.ScalarProduct(oldPosition) < std::numeric_limits<double>::epsilon()*1e4);
471 // Rotation.second = M_PI;
472 } else {
473 LOG(6, "DEBUG: oldPosition and newPosition are equivalent.");
474 }
475 }
476 } else {
477 // triangle situation
478 Plane oldplane(remainingold[0], remainingold[1], remainingold[2]);
479 Rotation.first = oldplane.getNormal();
480 LOG(6, "DEBUG: oldPlane is " << oldplane << " and normal is " << Rotation.first);
481 oldPosition.ProjectOntoPlane(Rotation.first);
482 LOG(6, "DEBUG: Positions after projection are " << oldPosition << " and " << newPosition);
483 }
484 }
485 // construct reference vector to determine direction of rotation
486 const double sign = determineSignOfRotation(oldPosition, newPosition, Rotation.first);
487 Rotation.second = sign * oldPosition.Angle(newPosition);
488 } else {
489 LOG(6, "DEBUG: oldPosition and newPosition are equivalent, hence no orientating rotation.");
490 }
491
492 return Rotation;
493}
494
495
496SphericalPointDistribution::Polygon_t
497SphericalPointDistribution::matchSphericalPointDistributions(
498 const SphericalPointDistribution::WeightedPolygon_t &_polygon,
499 const SphericalPointDistribution::Polygon_t &_newpolygon
500 )
501{
502 SphericalPointDistribution::Polygon_t remainingpoints;
503 VectorArray_t remainingold;
504 for (WeightedPolygon_t::const_iterator iter = _polygon.begin();
505 iter != _polygon.end(); ++iter)
506 remainingold.push_back(iter->first);
507 VectorArray_t remainingnew(_newpolygon.begin(), _newpolygon.end());
508 LOG(2, "INFO: Matching old polygon " << _polygon
509 << " with new polygon " << _newpolygon);
510
511 if (_polygon.size() == _newpolygon.size()) {
512 // same number of points desired as are present? Do nothing
513 LOG(2, "INFO: There are no vacant points to return.");
514 return remainingpoints;
515 }
516
517 if (_polygon.size() > 0) {
518 IndexList_t bestmatching = findBestMatching(_polygon, _newpolygon);
519 LOG(2, "INFO: Best matching is " << bestmatching);
520
521 // determine rotation angles to align the two point distributions with
522 // respect to bestmatching:
523 // we use the center between the three first matching points
524 /// the first rotation brings these two centers to coincide
525 VectorArray_t rotated_newpolygon = remainingnew;
526 {
527 Rotation_t Rotation = findPlaneAligningRotation(
528 remainingold,
529 remainingnew,
530 bestmatching);
531 LOG(5, "DEBUG: Rotating coordinate system by " << Rotation.second
532 << " around axis " << Rotation.first);
533 Line Axis(zeroVec, Rotation.first);
534
535 // apply rotation angle to bring newCenter to oldCenter
536 for (VectorArray_t::iterator iter = rotated_newpolygon.begin();
537 iter != rotated_newpolygon.end(); ++iter) {
538 Vector &current = *iter;
539 LOG(6, "DEBUG: Original point is " << current);
540 current = Axis.rotateVector(current, Rotation.second);
541 LOG(6, "DEBUG: Rotated point is " << current);
542 }
543
544#ifndef NDEBUG
545 // check: rotated "newCenter" should now equal oldCenter
546 {
547 Vector oldCenter;
548 Vector rotatednewCenter;
549 calculateOldAndNewCenters(
550 oldCenter, rotatednewCenter,
551 remainingold, rotated_newpolygon, bestmatching);
552 // NOTE: Center must not necessarily lie on the sphere with norm 1, hence, we
553 // have to normalize it just as before, as oldCenter and newCenter lengths may differ.
554 if ((!oldCenter.IsZero()) && (!rotatednewCenter.IsZero())) {
555 oldCenter.Normalize();
556 rotatednewCenter.Normalize();
557 LOG(4, "CHECK: rotatednewCenter is " << rotatednewCenter
558 << ", oldCenter is " << oldCenter);
559 ASSERT( (rotatednewCenter - oldCenter).NormSquared() < std::numeric_limits<double>::epsilon()*1e4,
560 "matchSphericalPointDistributions() - rotation does not work as expected by "
561 +toString((rotatednewCenter - oldCenter).NormSquared())+".");
562 }
563 }
564#endif
565 }
566 /// the second (orientation) rotation aligns the planes such that the
567 /// points themselves coincide
568 if (bestmatching.size() > 1) {
569 Rotation_t Rotation = findPointAligningRotation(
570 remainingold,
571 rotated_newpolygon,
572 bestmatching);
573
574 // construct RotationAxis and two points on its plane, defining the angle
575 Rotation.first.Normalize();
576 const Line RotationAxis(zeroVec, Rotation.first);
577
578 LOG(5, "DEBUG: Rotating around self is " << Rotation.second
579 << " around axis " << RotationAxis);
580
581#ifndef NDEBUG
582 // check: first bestmatching in rotated_newpolygon and remainingnew
583 // should now equal
584 {
585 const IndexList_t::const_iterator iter = bestmatching.begin();
586 Vector rotatednew = RotationAxis.rotateVector(
587 rotated_newpolygon[*iter],
588 Rotation.second);
589 LOG(4, "CHECK: rotated first new bestmatching is " << rotatednew
590 << " while old was " << remainingold[0]);
591 ASSERT( (rotatednew - remainingold[0]).Norm() < warn_amplitude,
592 "matchSphericalPointDistributions() - orientation rotation ends up off by more than "
593 +toString(warn_amplitude)+".");
594 }
595#endif
596
597 for (VectorArray_t::iterator iter = rotated_newpolygon.begin();
598 iter != rotated_newpolygon.end(); ++iter) {
599 Vector &current = *iter;
600 LOG(6, "DEBUG: Original point is " << current);
601 current = RotationAxis.rotateVector(current, Rotation.second);
602 LOG(6, "DEBUG: Rotated point is " << current);
603 }
604 }
605
606 // remove all points in matching and return remaining ones
607 SphericalPointDistribution::Polygon_t remainingpoints =
608 removeMatchingPoints(rotated_newpolygon, bestmatching);
609 LOG(2, "INFO: Remaining points are " << remainingpoints);
610 return remainingpoints;
611 } else
612 return _newpolygon;
613}
Note: See TracBrowser for help on using the repository browser.