OpenWalnut  1.5.0dev
WLine.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 #include <algorithm>
26 #include <complex>
27 #include <iostream>
28 #include <string>
29 #include <utility>
30 #include <vector>
31 
32 #include <boost/array.hpp>
33 
34 #include "../exceptions/WOutOfBounds.h"
35 #include "../WAssert.h"
36 #include "../WLimits.h"
37 #include "../WStringUtils.h"
38 #include "WLine.h"
39 #include "WPolynomialEquationSolvers.h"
40 #include "linearAlgebra/WPosition.h"
41 #include "linearAlgebra/WVectorFixed.h"
42 
43 WLine::WLine( const std::vector< WPosition > &points )
44  : WMixinVector< WPosition >( points )
45 {
46 }
47 
49  : WMixinVector< WPosition >()
50 {
51 }
52 
53 const WPosition& midPoint( const WLine& line )
54 {
55  if( line.empty() )
56  {
57  throw WOutOfBounds( std::string( "There is no midpoint for an empty line." ) );
58  }
59  return line[( line.size() - 1 ) / 2];
60 }
61 
63 {
64  std::reverse( begin(), end() );
65 }
66 
67 double pathLength( const WLine& line )
68 {
69  double len = 0;
70  // incase of size() <= 1 the for loop will not run!
71  for( size_t i = 1; i < line.size(); ++i )
72  {
73  len += length( line[i - 1] - line[i] );
74  }
75  return len;
76 }
77 
78 void WLine::resampleByNumberOfPoints( size_t numPoints )
79 {
80  WLine newLine;
81  newLine.reserve( numPoints );
82  if( size() != numPoints && size() > 1 && numPoints > 0 )
83  {
84  const double pathL = pathLength( *this );
85  double newSegmentLength = pathL / ( numPoints - 1 );
86  const double delta = newSegmentLength * 1.0e-10; // 1.0e-10 which represents the precision is choosen by intuition
87  double remainingLength = 0.0;
88  newLine.push_back( front() );
89  for( size_t i = 0; i < ( size() - 1 ); ++i )
90  {
91  remainingLength += length( at( i ) - at( i + 1 ) );
92  while( ( remainingLength > newSegmentLength ) || std::abs( remainingLength - newSegmentLength ) < delta )
93  {
94  remainingLength -= newSegmentLength;
95  // TODO(math): fix numerical issuses: newSegmentLength may be wrong => great offset by many intraSegment sample points
96  // remainingLength may be wrong => ...
97  // Take a look at the unit test testNumericalStabilityOfResampling
98  WPosition newPoint = at( i + 1 ) + remainingLength * normalize( at( i ) - at( i + 1 ) );
99  newLine.push_back( newPoint );
100  // std::cout << "line size so far" << newLine.size() << " lenght so far: " << newLine.pathLength() << std::endl;
101  // std::cout << numPoints - newLine.size() << std::endl;
102  }
103  }
104  // using string_utils::operator<<;
105  // std::cout << "this: " << *this << std::endl << "new: " << newLine << std::endl;
106  // std::cout << "|remL - newSegL|: " << std::abs( remainingLength - newSegmentLength ) << std::endl;
107  // std::cout << std::setprecision( 35 ) << "remainingLength: " << remainingLength << " newSegmentLength: " << newSegmentLength << std::endl;
108  // std::cout << "this size: " << size() << " new size: " << newLine.size() << std::endl;
109  }
110  else if( size() == 1 && size() < numPoints )
111  {
112  for( size_t i = 0; i < numPoints; ++i )
113  {
114  newLine.push_back( front() );
115  }
116  }
117  if( size() != numPoints )
118  {
119  this->WMixinVector< WPosition >::operator=( newLine );
120  }
121  // Note if the size() == 0, then the resampled tract is also of length 0
122 }
123 
125 {
126  if( empty() )
127  {
128  return;
129  }
130 
131  // Note: We cannot use std::remove for that since it allows only unary predicates to identify
132  // elements which are to be removed
133  WLine newLine;
134  newLine.reserve( size() );
135  newLine.push_back( front() );
136 
137  for( size_t i = 1; i < size(); ++i )
138  {
139  if( length( (*this)[i] - newLine.back() ) > wlimits::DBL_EPS )
140  {
141  newLine.push_back( (*this)[i] );
142  }
143  }
144  this->WMixinVector< WPosition >::operator=( newLine );
145 }
146 
147 void WLine::resampleBySegmentLength( double newSegmentLength )
148 {
149  // eliminate duplicate points following next to another
151 
152  if( empty() || size() == 1 )
153  {
154  return;
155  }
156 
157  WLine newLine;
158  newLine.push_back( front() );
159 
160  std::size_t i = 1;
161  while( i < size() )
162  {
163  // find first point outside of a circle of radius newSegmentLength around newLine.back()
164  // its index will be k
165  std::size_t k = i;
166  while( k < size() && length( newLine.back() - at( k ) ) < newSegmentLength )
167  ++k;
168 
169  if( k == size() )
170  {
171  break;
172  }
173 
174  if( k == i )
175  {
176  newLine.push_back( newLine.back() + normalize( at( k ) - newLine.back() ) * newSegmentLength );
177  }
178  else
179  {
180  WPosition const& current = at( k );
181  WPosition const& pred = at( k - 1 );
182 
183  WVector3d lineDirection = current - pred;
184  WAssert( lineDirection != WVector3d( 0.0, 0.0, 0.0 ), "current should be diffrent from pred" );
185  WVector3d o_c = pred - newLine.back(); // origin - center
186  double alpha = dot( lineDirection, lineDirection );
187  double beta = 2.0 * dot( lineDirection, o_c );
188  double gamma = dot( o_c, o_c ) - newSegmentLength * newSegmentLength;
189 
190  std::pair< std::complex< double >, std::complex< double > > solution = solveRealQuadraticEquation( alpha, beta, gamma );
191  // NOTE: if this assert fires, then this algo is wrong and produces wrong results, and I've to search to bug!
192  WAssert( std::imag( solution.first ) == 0.0 && std::imag( solution.second ) == 0.0, "Imaginary solution detected." );
193  WPosition pointOfIntersection;
194  if( std::real( solution.first ) > 0.0 )
195  {
196  pointOfIntersection = pred + std::real( solution.first ) * ( current - pred );
197  }
198  else
199  {
200  pointOfIntersection = pred + std::real( solution.second ) * ( current - pred );
201  }
202  newLine.push_back( pointOfIntersection );
203  }
204  i = k;
205  }
206  this->WMixinVector< WPosition >::operator=( newLine );
207 }
208 
209 void WLine::resampleBySegmentLengthKeepShortFibers( double newSegmentLength )
210 {
211  // eliminate duplicate points following next to another
213 
214  if( empty() || size() == 1 )
215  {
216  return;
217  }
218 
219  WLine newLine;
220  newLine.push_back( front() );
221 
222  std::size_t i = 1;
223  while( i < size() )
224  {
225  // find first point outside of a circle of radius newSegmentLength around newLine.back()
226  // its index will be k
227  std::size_t k = i;
228  while( k < size() && length( newLine.back() - at( k ) ) < newSegmentLength )
229  ++k;
230 
231  if( k == size() )
232  {
233  if( length( newLine.back() - at( k - 1 ) ) > 0.001 )
234  newLine.push_back( at( k - 1 ) );
235  break;
236  }
237 
238  if( k == i )
239  {
240  newLine.push_back( newLine.back() + normalize( at( k ) - newLine.back() ) * newSegmentLength );
241  }
242  else
243  {
244  WPosition const& current = at( k );
245  WPosition const& pred = at( k - 1 );
246 
247  WVector3d lineDirection = current - pred;
248  WAssert( lineDirection != WVector3d( 0.0, 0.0, 0.0 ), "current should be diffrent from pred" );
249  WVector3d o_c = pred - newLine.back(); // origin - center
250  double alpha = dot( lineDirection, lineDirection );
251  double beta = 2.0 * dot( lineDirection, o_c );
252  double gamma = dot( o_c, o_c ) - newSegmentLength * newSegmentLength;
253 
254  std::pair< std::complex< double >, std::complex< double > > solution = solveRealQuadraticEquation( alpha, beta, gamma );
255  // NOTE: if this assert fires, then this algo is wrong and produces wrong results, and I've to search to bug!
256  WAssert( std::imag( solution.first ) == 0.0 && std::imag( solution.second ) == 0.0, "Imaginary solution detected." );
257  WPosition pointOfIntersection;
258  if( std::real( solution.first ) > 0.0 )
259  {
260  pointOfIntersection = pred + std::real( solution.first ) * ( current - pred );
261  }
262  else
263  {
264  pointOfIntersection = pred + std::real( solution.second ) * ( current - pred );
265  }
266  newLine.push_back( pointOfIntersection );
267  }
268  i = k;
269  }
270  this->WMixinVector< WPosition >::operator=( newLine );
271 }
272 
273 int equalsDelta( const WLine& line, const WLine& other, double delta )
274 {
275  size_t pts = ( std::min )( other.size(), line.size() ); // This ( std::min ) thing compiles also under Win32/Win64
276  size_t diffPos = 0;
277  bool sameLines = true;
278  for( diffPos = 0; ( diffPos < pts ) && sameLines; ++diffPos )
279  {
280  for( int x = 0; x < 3; ++x ) // since WLine uses WPosition as elements there are 3 components per position
281  {
282  sameLines = sameLines && ( std::abs( line[diffPos][x] - other[diffPos][x] ) <= delta );
283  }
284  }
285  if( sameLines && ( line.size() == other.size() ) )
286  {
287  return -1;
288  }
289  if( !sameLines )
290  {
291  return diffPos - 1;
292  }
293  return diffPos;
294 }
295 
296 double maxSegmentLength( const WLine& line )
297 {
298  double result = 0.0;
299  if( line.empty() || line.size() == 1 )
300  {
301  return result;
302  }
303  for( size_t i = 0; i < line.size() - 1; ++i )
304  {
305  result = std::max( result, static_cast< double >( length( line[i] - line[i+1] ) ) );
306  }
307  return result;
308 }
309 
310 void WLine::unifyDirectionBy( const WLine& other )
311 {
312  const size_t numBasePoints = 4;
313  boost::array< WPosition, numBasePoints > m;
314  boost::array< WPosition, numBasePoints > n;
315 
316  double distance = 0.0;
317  double inverseDistance = 0.0;
318  for( size_t i = 0; i < numBasePoints; ++i )
319  {
320  m[i] = other.at( ( other.size() - 1 ) * static_cast< double >( i ) / ( numBasePoints - 1 ) );
321  n[i] = at( ( size() - 1 ) * static_cast< double >( i ) / ( numBasePoints - 1 ) );
322  distance += length2( m[i] - n[i] );
323  inverseDistance += length2( m[i] - at( ( size() - 1 ) * static_cast< double >( numBasePoints - 1 - i ) / ( numBasePoints - 1 ) ) );
324  }
325  distance /= static_cast< double >( numBasePoints );
326  inverseDistance /= static_cast< double >( numBasePoints );
327 
328  if( inverseDistance < distance )
329  {
330  this->reverseOrder();
331  }
332 }
333 
334 WBoundingBox computeBoundingBox( const WLine& line )
335 {
336  WBoundingBox result;
337  for( WLine::const_iterator cit = line.begin(); cit != line.end(); ++cit )
338  {
339  result.expandBy( *cit );
340  }
341  return result;
342 }
void expandBy(const WBoundingBoxImpl< VT > &bb)
Expands this bounding box to include the given bounding box.
Definition: WBoundingBox.h:240
A line is an ordered sequence of WPositions.
Definition: WLine.h:42
void resampleByNumberOfPoints(size_t numPoints)
Resample this line so it has a number of given points afterwards.
Definition: WLine.cpp:78
void unifyDirectionBy(const WLine &other)
Put the line into reverse ordering if the reverse ordering would have a similar direction to the give...
Definition: WLine.cpp:310
void resampleBySegmentLengthKeepShortFibers(double newSegmentLength)
Resample this line so there are only segements of the given length.
Definition: WLine.cpp:209
void reverseOrder()
Reverses the order of the points.
Definition: WLine.cpp:62
void removeAdjacentDuplicates()
Collapse samplepoints which are equal and neighboured.
Definition: WLine.cpp:124
void resampleBySegmentLength(double newSegementLength)
Resample this line so there are only segements of the given length.
Definition: WLine.cpp:147
WLine()
Creates an empty line.
Definition: WLine.cpp:48
This is taken from OpenSceneGraph <osg/MixinVector> but copy and pasted in order to reduce dependency...
Definition: WMixinVector.h:48
void reserve(size_type new_capacity)
Wrapper around std::vector member function.
Definition: WMixinVector.h:227
WMixinVector & operator=(const vector_type &other)
Assignment operator for the appropriate vector type.
Definition: WMixinVector.h:177
size_type size() const
Wrapper around std::vector member function.
Definition: WMixinVector.h:267
void push_back(const value_type &value)
Wrapper around std::vector member function.
Definition: WMixinVector.h:457
const_reference back() const
Wrapper around std::vector member function.
Definition: WMixinVector.h:537
bool empty() const
Wrapper around std::vector member function.
Definition: WMixinVector.h:257
const_reference front() const
Wrapper around std::vector member function.
Definition: WMixinVector.h:557
const_iterator end() const
Wrapper around std::vector member function.
Definition: WMixinVector.h:327
const_iterator begin() const
Wrapper around std::vector member function.
Definition: WMixinVector.h:307
const_reference at(size_type index) const
Wrapper around std::vector member function.
Definition: WMixinVector.h:413
vector_type::const_iterator const_iterator
Compares to std::vector type.
Definition: WMixinVector.h:87
Indicates invalid element access of a container.
Definition: WOutOfBounds.h:37
This only is a 3d double vector.
const double DBL_EPS
Smallest double such: 1.0 + DBL_EPS == 1.0 is still true.
Definition: WLimits.cpp:46