OpenWalnut  1.5.0dev
WAngleHelper.cpp
1 //---------------------------------------------------------------------------
2 //
3 // Project: OpenWalnut ( http://www.openwalnut.org )
4 //
5 // Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
6 // For more information see http://www.openwalnut.org/copying
7 //
8 // This file is part of OpenWalnut.
9 //
10 // OpenWalnut is free software: you can redistribute it and/or modify
11 // it under the terms of the GNU Lesser General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // OpenWalnut is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU Lesser General Public License for more details.
19 //
20 // You should have received a copy of the GNU Lesser General Public License
21 // along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
22 //
23 //---------------------------------------------------------------------------
24 
25 #define _USE_MATH_DEFINES
26 
27 #include <algorithm>
28 #include <cmath>
29 #include <functional>
30 #include <limits>
31 #include <unordered_map>
32 #include <queue>
33 #include <thread>
34 #include <vector>
35 
36 #include <boost/asio/thread_pool.hpp>
37 #include <boost/asio/post.hpp>
38 #include <boost/lockfree/queue.hpp>
39 
40 #include "WAngleHelper.h"
41 
42 
43 /**
44  * Data for the thread handling
45  */
46 struct SaptData
47 {
48  /**
49  * Creates the sapt data
50  * \param prevs The unordered_map of previous points.
51  * \param endPoint The endpoint.
52  * \param distance The distance to the endpoint.
53  */
56  {
57  }
58 
59  WAngleHelper::PositionMap prevs; //!< The unordered_map of previous points.
60  WPosition endPoint; //!< The endpoint.
61  double distance; //!< The distance to the endpoint.
62 };
63 
64 /**
65  * A queue with data that is threadsafe
66  */
67 typedef boost::lockfree::queue< SaptData*, boost::lockfree::fixed_sized< false > > lfr_queue;
68 
69 
70 static int compareWPosition( WPosition a, WPosition b )
71 {
72  return a.z() < b.z();
73 }
74 
76 {
77  double num = a.x() * b.x() + a.y() * b.y() + a.z() * b.z();
78  double denom = ( a.x() * a.x() + a.y() * a.y() + a.z() * a.z() ) *
79  ( b.x() * b.x() + b.y() * b.y() + b.z() * b.z() );
80  denom = sqrt( denom );
81  double angle = acos( num / denom ) * 180.0 / M_PI;
82 
83  return angle;
84 }
85 
86 static WAngleHelper::DJLinePair createLines( std::vector< WPosition > positions )
87 {
88  WAngleHelper::PositionLineMap prevMap; // A map containing all the backwards connections of a point
89  WAngleHelper::PositionLineMap postMap; // A map containing all the forward connections of a point
90 
91  if( positions.empty() )
92  {
93  return std::make_pair( prevMap, postMap );
94  }
95 
96  std::sort( positions.begin(), positions.end(), compareWPosition );
97  std::vector< WPosition > oldPoints; // holds the points that are on the layer one before the current one
98  std::vector< WPosition > currentPoints; // holds all the points on the current layer (becomes oldPoints afterwards)
99  double currentZ = positions.at( 0 ).z();
100 
101  // WPosition vert( 0.0, 0.0, 1.0 ); // A vertical position used for angle calculation
102 
103  for( size_t idx = 0; idx < positions.size(); idx++ )
104  {
105  WPosition point = positions.at( idx );
106  if( point.z() - currentZ > 4.0 ) // A new layer is found
107  {
108  currentZ = point.z();
109  oldPoints.clear();
110  oldPoints.insert( oldPoints.end(), currentPoints.begin(), currentPoints.end() );
111  currentPoints.clear();
112  }
113 
114  WAngleHelper::PositionMap prevM; // All the backwards connections
115  for( size_t j = 0; j < oldPoints.size(); j++ )
116  {
117  WPosition p = oldPoints.at( j );
118  // double angle = WAngleHelper::calculateAngle( p - vert, point - p );
119 
120  double angleX = -atan2( point.x() - p.x(), point.y() - p.y() ) / M_PI * 180.0 + 180.0;
121  double angleY = asin( ( point.z() - p.z() ) / distance( p, point ) ) / M_PI * 180.0;
122  WPosition angle( angleX, angleY, 0.0 );
123 
124  prevM[p] = angle; // create a backwards connection
125  postMap[p][point] = angle; // create a forward connection
126  }
127  prevMap[point] = prevM;
128  postMap[point] = WAngleHelper::PositionMap();
129  currentPoints.push_back( point );
130  }
131 
132  return std::make_pair( prevMap, postMap );
133 }
134 
135 static WAngleHelper::DJOut dijkstra( WAngleHelper::DJLinePair lines, std::vector< WPosition > points, WPosition start )
136 {
137  std::vector< WPosition > Q; // All positions
138  WAngleHelper::PositionDoubleMap dist; // A map from position to the lowest distance it would need to get there
139  WAngleHelper::PositionMap prev; // A map from position to it's previous position
140 
141  for( size_t idx = 0; idx < points.size(); idx++ )
142  {
143  WPosition p = points.at( idx );
144  dist[p] = std::numeric_limits< double >::infinity();
145  Q.push_back( p );
146  }
147  dist[start] = 0;
148 
149  while( Q.size() > 0 )
150  {
151  // find position with minimum distance
152  auto min = std::min_element( Q.begin(), Q.end(),
153  [dist]( WPosition a, WPosition b )
154  {
155  return dist.at( a ) < dist.at( b );
156  }
157  );
158  WPosition u = *min;
159  Q.erase( min );
160 
161  if( dist[u] == std::numeric_limits< double >::infinity() )
162  {
163  break;
164  }
165 
166  WAngleHelper::PositionMap prevM = lines.first[u]; // forward connections of that position
167  WAngleHelper::PositionMap postM = lines.second[u]; // backward connections of that positions
168 
169  for( auto it = postM.begin(); it != postM.end(); it++ ) // loop all forward connections
170  {
171  WPosition v = it->first;
172  auto pr = prev.find( u );
173  auto old = pr == prev.end() ? prevM.end() : prevM.find( pr->second ); // find the backward connections associated with the
174  // previous position that has the lowest total distance
175 
176  double alt = 0; // The 'alternative' total angle (could become the new one)
177  if( old != prevM.end() )
178  {
179  WPosition angles = it->second - old->second;
180  alt = dist[u] + abs( angles.x() ) + abs( angles.y() ); // add difference from old to current to the total distance
181  }
182  if( alt < dist[v] ) // If alternative angle is lower than current total => override
183  {
184  dist[v] = alt;
185  prev[v] = u;
186  }
187  }
188  }
189 
190  return std::make_pair( prev, dist );
191 }
192 
193 std::vector< WPosition > WAngleHelper::findSmoothestPath( std::vector< WPosition > positions )
194 {
195  // create the lines
196  WAngleHelper::DJLinePair lines = createLines( positions );
197 
198  // a queue for all the data that the threads pump out
199  std::shared_ptr< lfr_queue > data = std::shared_ptr< lfr_queue >( new lfr_queue( 0 ) );
200 
201  double firstZ = positions.at( 0 ).z();
202  double lastZ = positions.at( positions.size() - 1 ).z();
203  auto points = positions.begin();
204 
205  // Since we use the dijkstra from each point on the first layer we can parallelize it
206  boost::asio::thread_pool pool( std::thread::hardware_concurrency() );
207  while( points->z() == firstZ )
208  {
209  WPosition start = *points;
210  boost::asio::post( pool, [lines, positions, start, lastZ, data]()
211  {
212  DJOut res = dijkstra( lines, positions, start );
213  double endDis = std::numeric_limits< double >::infinity();
214  WPosition endPoint;
215 
216  auto end = positions.end();
217  end--;
218  while( end->z() == lastZ ) // Check the paths from the current first layer point to all last layer points
219  { // And find the one with the lowest total angle
220  if( res.second[ *end ] < endDis )
221  {
222  endDis = res.second[ *end ];
223  endPoint = *end;
224  }
225  end--;
226  }
227 
228  data->push( new SaptData( res.first, endPoint, endDis ) );
229  } );
230  points++;
231  }
232 
233  // wait for all threads
234  pool.join();
235 
237  WPosition ePoint;
238  double eDis = std::numeric_limits< double >::infinity();
239 
240  // Analyze the data of all threads and find the path with the lowest angle deviation among them
241  SaptData* d;
242  while( data->pop( d ) )
243  {
244  if( d->distance < eDis )
245  {
246  prevs = d->prevs;
247  ePoint = d->endPoint;
248  eDis = d->distance;
249  }
250  delete d;
251  }
252 
253  std::vector< WPosition > S;
254  WPosition u = ePoint;
255  while( true )
256  {
257  S.insert( S.begin(), u );
258  auto it = prevs.find( u );
259  if( it == prevs.end() )
260  {
261  break;
262  }
263  u = it->second;
264  }
265 
266  return S;
267 }
268 
269 std::vector< WPosition > WAngleHelper::findSmoothestPath( std::vector< WPosition > positions, WFiberHandler::PCFiber fiber )
270 {
271  if( positions.empty() )
272  {
273  return std::vector< WPosition >();
274  }
275  std::sort( positions.begin(), positions.end(), compareWPosition );
276 
277  if( fiber.empty() )
278  {
279  return findSmoothestPath( positions );
280  }
281 
282  std::vector< WPosition > pos;
283  auto posIter = positions.begin();
284  auto fibIter = fiber.begin();
285 
286  // intertwine positions and fiber.
287  while( posIter < positions.end() )
288  {
289  if( fibIter >= fiber.end() || posIter->z() < fibIter->z() )
290  {
291  pos.push_back( *posIter );
292  posIter++;
293  }
294  else if( posIter->z() == fibIter->z() )
295  {
296  posIter++;
297  }
298  else
299  {
300  pos.push_back( *fibIter );
301  fibIter++;
302  }
303  }
304 
305  while( fibIter < fiber.end() )
306  {
307  pos.push_back( *fibIter );
308  fibIter++;
309  }
310 
311  if( pos.empty() )
312  {
313  return std::vector< WPosition >();
314  }
315 
316  return findSmoothestPath( pos );
317 }
std::vector< osg::Vec3 > PCFiber
Vector of 3D vectors, representing points.
Definition: WFiberHandler.h:63
This only is a 3d double vector.
std::unordered_map< WPosition, double, HashFn > PositionDoubleMap
An unordered map from WPosition to double.
Definition: WAngleHelper.h:65
std::vector< WPosition > findSmoothestPath(std::vector< WPosition > positions)
Determines the path with the smallest angle change.
std::unordered_map< WPosition, PositionMap, HashFn > PositionLineMap
An unordered map from WPosition to a PositionDoubleMap.
Definition: WAngleHelper.h:70
std::pair< WAngleHelper::PositionLineMap, WAngleHelper::PositionLineMap > DJLinePair
The output of the createLine function.
Definition: WAngleHelper.h:75
std::pair< PositionMap, PositionDoubleMap > DJOut
The output of the dijkstra algorithm.
Definition: WAngleHelper.h:80
double calculateAngle(WPosition a, WPosition b)
Calculates the angle between two positions.
std::unordered_map< WPosition, WPosition, HashFn > PositionMap
An unordered map from WPosition to WPosition.
Definition: WAngleHelper.h:60
Data for the thread handling.
WPosition endPoint
The endpoint.
SaptData(WAngleHelper::PositionMap prevs, WPosition endPoint, double distance)
Creates the sapt data.
double distance
The distance to the endpoint.
WAngleHelper::PositionMap prevs
The unordered_map of previous points.