- Timestamp:
- Aug 20, 2014, 1:06:16 PM (11 years ago)
- Children:
- ef3885
- Parents:
- 1cde4e8
- git-author:
- Frederik Heber <heber@…> (07/09/14 22:08:37)
- git-committer:
- Frederik Heber <heber@…> (08/20/14 13:06:16)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
src/Fragmentation/Exporters/SphericalPointDistribution.cpp
r1cde4e8 ra2f8a9 43 43 44 44 #include <algorithm> 45 #include <boost/ math/quaternion.hpp>45 #include <boost/assign.hpp> 46 46 #include <cmath> 47 47 #include <functional> … … 58 58 #include "LinearAlgebra/Vector.hpp" 59 59 60 using namespace boost::assign; 61 60 62 // static entities 61 63 const double SphericalPointDistribution::warn_amplitude = 1e-2; … … 83 85 */ 84 86 inline 85 Vector calculate Center(87 Vector calculateGeographicMidpoint( 86 88 const SphericalPointDistribution::VectorArray_t &_positions, 87 89 const SphericalPointDistribution::IndexList_t &_indices) … … 96 98 97 99 return Center; 100 } 101 102 inline 103 double 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 */ 132 Vector 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 146 if (_positions.size() == (size_t)1) 147 return _positions[0]; 148 // two positions on a line given: return closest point to (1.,0.,0.) 149 if (fabs(_positions[0].ScalarProduct(_positions[1]) + 1.) 150 < std::numeric_limits<double>::epsilon()*1e4) { 151 Vector candidate; 152 if (_positions[0].ScalarProduct(center) > _positions[1].ScalarProduct(center)) 153 candidate = _positions[0]; 154 else 155 candidate = _positions[1]; 156 // non-uniqueness: all positions on great circle, normal to given line are valid 157 // so, we just pick one because returning a unique point is topmost priority 158 Vector normal; 159 normal.GetOneNormalVector(candidate); 160 Vector othernormal = candidate; 161 othernormal.VectorProduct(normal); 162 // now both normal and othernormal describe the plane containing the great circle 163 Plane greatcircle(normal, zeroVec, othernormal); 164 // check which axis is contained and pick the one not 165 if (greatcircle.isContained(center)) { 166 center = Vector(0.,1.,0.); 167 if (greatcircle.isContained(center)) 168 center = Vector(0.,0.,1.); 169 // now we are done cause a plane cannot contain all three axis vectors 170 } 171 center = greatcircle.getClosestPoint(candidate); 172 // assure length of 1 173 center.Normalize(); 174 } 175 } 176 177 // start with geographic midpoint 178 center = calculateGeographicMidpoint(_positions, _indices); 179 if (!center.IsZero()) { 180 center.Normalize(); 181 LOG(4, "DEBUG: Starting with geographical midpoint of " << _positions << " under indices " 182 << _indices << " is " << center); 183 } else { 184 // any point is good actually 185 center = _positions[0]; 186 LOG(4, "DEBUG: Starting with first position " << center); 187 } 188 189 // calculate initial MinimumDistance 190 double MinimumDistance = calculateMinimumDistance(center, _positions, _indices); 191 LOG(5, "DEBUG: MinimumDistance to this center is " << MinimumDistance); 192 193 // check all present points whether they may serve as a better center 194 for (SphericalPointDistribution::IndexList_t::const_iterator iter = _indices.begin(); 195 iter != _indices.end(); ++iter) { 196 const Vector ¢erCandidate = _positions[*iter]; 197 const double candidateDistance = calculateMinimumDistance(centerCandidate, _positions, _indices); 198 if (candidateDistance < MinimumDistance) { 199 MinimumDistance = candidateDistance; 200 center = centerCandidate; 201 LOG(5, "DEBUG: new MinimumDistance to current test point " << MinimumDistance 202 << " is " << center); 203 } 204 } 205 LOG(5, "DEBUG: new MinimumDistance to center " << center << " is " << MinimumDistance); 206 207 // now iterate over TestDistance 208 double TestDistance = Vector(1.,0.,0.).Angle(Vector(0.,1.,0.)); 209 // LOG(6, "DEBUG: initial TestDistance is " << TestDistance); 210 211 const double threshold = sqrt(std::numeric_limits<double>::epsilon()); 212 // check each of eight test points at N, NE, E, SE, S, SW, W, NW 213 typedef std::vector<double> angles_t; 214 angles_t testangles; 215 testangles += 0./180.*M_PI, 45./180.*M_PI, 90./180.*M_PI, 135./180.*M_PI, 216 180./180.*M_PI, 225./180.*M_PI, 270./180.*M_PI, 315./180.*M_PI; 217 while (TestDistance > threshold) { 218 Vector OneNormal; 219 OneNormal.GetOneNormalVector(center); 220 Line RotationAxis(zeroVec, OneNormal); 221 Vector North = RotationAxis.rotateVector(center,TestDistance); 222 Line CompassRose(zeroVec, center); 223 bool updatedflag = false; 224 for (angles_t::const_iterator angleiter = testangles.begin(); 225 angleiter != testangles.end(); ++angleiter) { 226 Vector centerCandidate = CompassRose.rotateVector(North, *angleiter); 227 // centerCandidate.Normalize(); 228 const double candidateDistance = calculateMinimumDistance(centerCandidate, _positions, _indices); 229 if (candidateDistance < MinimumDistance) { 230 MinimumDistance = candidateDistance; 231 center = centerCandidate; 232 updatedflag = true; 233 LOG(5, "DEBUG: new MinimumDistance to test point at " << *angleiter/M_PI*180. 234 << "° is " << MinimumDistance); 235 } 236 } 237 238 // if no new point, decrease TestDistance 239 if (!updatedflag) { 240 TestDistance *= 0.5; 241 // LOG(6, "DEBUG: TestDistance is now " << TestDistance); 242 } 243 } 244 LOG(4, "DEBUG: Final MinimumDistance to center " << center << " is " << MinimumDistance); 245 246 return center; 247 } 248 249 Vector calculateCenterOfMinimumDistance( 250 const SphericalPointDistribution::PolygonWithIndices &_points) 251 { 252 return SphericalPointDistribution::calculateCenterOfMinimumDistance(_points.polygon, _points.indices); 253 } 254 255 /** Calculate the center of a given set of points in \a _positions but only 256 * for those indicated by \a _indices. 257 * 258 */ 259 inline 260 Vector calculateCenter( 261 const SphericalPointDistribution::VectorArray_t &_positions, 262 const SphericalPointDistribution::IndexList_t &_indices) 263 { 264 // Vector Center; 265 // Center.Zero(); 266 // for (SphericalPointDistribution::IndexList_t::const_iterator iter = _indices.begin(); 267 // iter != _indices.end(); ++iter) 268 // Center += _positions[*iter]; 269 // if (!_indices.empty()) 270 // Center *= 1./(double)_indices.size(); 271 // 272 // return Center; 273 return SphericalPointDistribution::calculateCenterOfMinimumDistance(_positions, _indices); 98 274 } 99 275 … … 744 920 oldCenter, rotatednewCenter, 745 921 oldSet, rotatednewSet); 746 // NOTE: Center must not necessarily lie on the sphere with norm 1, hence, we 747 // have to normalize it just as before, as oldCenter and newCenter lengths may differ. 748 if ((!oldCenter.IsZero()) && (!rotatednewCenter.IsZero())) { 749 oldCenter.Normalize(); 750 rotatednewCenter.Normalize(); 751 LOG(4, "CHECK: rotatednewCenter is " << rotatednewCenter 752 << ", oldCenter is " << oldCenter); 753 const double difference = (rotatednewCenter - oldCenter).NormSquared(); 754 ASSERT( difference < std::numeric_limits<double>::epsilon()*1e4, 755 "matchSphericalPointDistributions() - rotation does not work as expected by " 756 +toString(difference)+"."); 757 } 922 oldCenter.Normalize(); 923 rotatednewCenter.Normalize(); 924 // check whether centers are anti-parallel (factor -1) 925 // then we have the "non-unique poles" situation: points lie on great circle 926 // and both poles are valid solution 927 if (fabs(oldCenter.ScalarProduct(rotatednewCenter) + 1.) 928 < std::numeric_limits<double>::epsilon()*1e4) 929 rotatednewCenter *= -1.; 930 LOG(4, "CHECK: rotatednewCenter is " << rotatednewCenter 931 << ", oldCenter is " << oldCenter); 932 const double difference = (rotatednewCenter - oldCenter).NormSquared(); 933 ASSERT( difference < std::numeric_limits<double>::epsilon()*1e4, 934 "matchSphericalPointDistributions() - rotation does not work as expected by " 935 +toString(difference)+"."); 758 936 } 759 937 #endif
Note:
See TracChangeset
for help on using the changeset viewer.
