source: src/Fragmentation/Exporters/SphericalPointDistribution.cpp@ 450adf

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 450adf was 450adf, checked in by Frederik Heber <heber@…>, 9 years ago

Extracted joinPoints() function to make it accessible to tests.

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