2 * This program is free software: you can redistribute it and/or modify
3 * it under the terms of the GNU Lesser General Public License as
4 * published by the Free Software Foundation, either version 3 of the
5 * License, or (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 * @file Hull2DHelpers.ih
19 * @author Tristan Roussillon (\c tristan.roussillon@liris.cnrs.fr )
20 * Laboratoire d'InfoRmatique en Image et Systèmes d'information - LIRIS (CNRS, UMR 5205), CNRS, France
24 * Implementation of inline methods defined in Hull2DHelpers.h
26 * This file is part of the DGtal library.
30//////////////////////////////////////////////////////////////////////////////
34#include "DGtal/kernel/PointVector.h"
35//////////////////////////////////////////////////////////////////////////////
37///////////////////////////////////////////////////////////////////////////////
38// Implementation of inline functions //
47 //----------------------------------------------------------------------------
48 template <typename Stack, typename Point, typename Predicate>
50 void updateHullWithStack(Stack& aStack, const Point& aNewPoint, const Predicate& aPredicate)
52 BOOST_CONCEPT_ASSERT(( concepts::CStack<Stack> ));
54 Point Q = aStack.top();
56 if (aStack.size() != 0)
58 Point P = aStack.top();
59 while ( ( !aPredicate(P,Q,aNewPoint) )&&(aStack.size() != 0) )
64 if (aStack.size() != 0)
72 //----------------------------------------------------------------------------
73 template <typename Stack, typename Point, typename Predicate>
75 void updateHullWithAdaptedStack(Stack aStack, const Point& aNewPoint, const Predicate& aPredicate)
77 updateHullWithStack( aStack, aNewPoint, aPredicate );
80 //----------------------------------------------------------------------------
81 template <typename Stack, typename ForwardIterator, typename Predicate>
83 void buildHullWithStack(Stack& aStack,
84 const ForwardIterator& itb, const ForwardIterator& ite,
85 const Predicate& aPredicate)
87 BOOST_CONCEPT_ASSERT(( concepts::CStack<Stack> ));
88 BOOST_CONCEPT_ASSERT(( boost_concepts::ForwardTraversalConcept<ForwardIterator> ));
89 BOOST_CONCEPT_ASSERT(( boost_concepts::ReadableIteratorConcept<ForwardIterator> ));
92 for(ForwardIterator it = itb; it != ite; ++it)
100 //we update the hull so that the predicate returns 'true'
101 //for each sets of three consecutive points
102 updateHullWithStack(aStack, *it, aPredicate);
106 }//end for all points
109 //----------------------------------------------------------------------------
110 template <typename Stack, typename ForwardIterator, typename Predicate>
112 void buildHullWithAdaptedStack(Stack aStack,
113 const ForwardIterator& itb, const ForwardIterator& ite,
114 const Predicate& aPredicate)
116 buildHullWithStack( aStack, itb, ite, aPredicate );
119 //----------------------------------------------------------------------------
120 template <typename ForwardIterator, typename OutputIterator, typename Predicate>
122 void openGrahamScan(const ForwardIterator& itb, const ForwardIterator& ite,
123 OutputIterator res, const Predicate& aPredicate)
125 BOOST_CONCEPT_ASSERT(( boost_concepts::ForwardTraversalConcept<ForwardIterator> ));
126 BOOST_CONCEPT_ASSERT(( boost_concepts::ReadableIteratorConcept<ForwardIterator> ));
127 typedef typename IteratorCirculatorTraits<ForwardIterator>::Value Point;
128 BOOST_CONCEPT_ASSERT(( boost_concepts::IncrementableIteratorConcept<OutputIterator> ));
129 BOOST_CONCEPT_ASSERT(( boost_concepts::WritableIteratorConcept<OutputIterator,Point> ));
132 std::deque<Point> container;
135 buildHullWithAdaptedStack( backStack(container), itb, ite, aPredicate );
138 std::copy(container.begin(), container.end(), res);
141 //----------------------------------------------------------------------------
142 template <typename ForwardIterator, typename OutputIterator, typename Predicate>
144 void closedGrahamScanFromVertex(const ForwardIterator& itb, const ForwardIterator& ite,
145 OutputIterator res, const Predicate& aPredicate)
147 BOOST_CONCEPT_ASSERT(( boost_concepts::ForwardTraversalConcept<ForwardIterator> ));
148 BOOST_CONCEPT_ASSERT(( boost_concepts::ReadableIteratorConcept<ForwardIterator> ));
149 typedef typename IteratorCirculatorTraits<ForwardIterator>::Value Point;
150 BOOST_CONCEPT_ASSERT(( boost_concepts::IncrementableIteratorConcept<OutputIterator> ));
151 BOOST_CONCEPT_ASSERT(( boost_concepts::WritableIteratorConcept<OutputIterator,Point> ));
154 std::deque<Point> container;
157 buildHullWithAdaptedStack( backStack(container), itb, ite, aPredicate);
159 //we update the hull to take into account the starting point
160 if ( container.size() > 3 )
161 updateHullWithAdaptedStack( backStack(container), container.front(), aPredicate );
163 std::copy(container.begin(), container.end(), res);
166 //----------------------------------------------------------------------------
167 template <typename ForwardIterator, typename OutputIterator, typename Predicate>
169 void closedGrahamScanFromAnyPoint(const ForwardIterator& itb, const ForwardIterator& ite,
170 OutputIterator res, const Predicate& aPredicate)
172 BOOST_CONCEPT_ASSERT(( boost_concepts::ForwardTraversalConcept<ForwardIterator> ));
173 BOOST_CONCEPT_ASSERT(( boost_concepts::ReadableIteratorConcept<ForwardIterator> ));
174 typedef typename IteratorCirculatorTraits<ForwardIterator>::Value Point;
175 BOOST_CONCEPT_ASSERT(( boost_concepts::IncrementableIteratorConcept<OutputIterator> ));
176 BOOST_CONCEPT_ASSERT(( boost_concepts::WritableIteratorConcept<OutputIterator,Point> ));
179 std::deque<Point> container;
182 buildHullWithAdaptedStack( backStack(container), itb, ite, aPredicate);
184 //we update the hull to take into account the starting point
185 if ( container.size() > 3 )
187 updateHullWithAdaptedStack( backStack(container), container.front(), aPredicate );
188 //we move forward the starting point
189 container.push_back( container.front() );
190 container.pop_front();
193 //while the last two points and the first one are not correctly oriented, we update the hull
194 while ( (container.size() > 3)&&
195 !aPredicate( *boost::next(container.rbegin()), container.back(), container.front() ) )
197 updateHullWithAdaptedStack( backStack(container), container.front(), aPredicate );
198 //we move forward the starting point
199 container.push_back( container.front() );
200 container.pop_front();
203 std::copy(container.begin(), container.end(), res);
207 //----------------------------------------------------------------------------
208 template <typename ForwardIterator,
209 typename OutputIterator,
211 typename PolarComparator >
213 void grahamConvexHullAlgorithm(const ForwardIterator& itb, const ForwardIterator& ite,
215 const Predicate& aPredicate,
216 PolarComparator& aPolarComparator)
218 BOOST_CONCEPT_ASSERT(( boost_concepts::ForwardTraversalConcept<ForwardIterator> ));
219 BOOST_CONCEPT_ASSERT(( boost_concepts::ReadableIteratorConcept<ForwardIterator> ));
220 typedef typename IteratorCirculatorTraits<ForwardIterator>::Value Point;
221 BOOST_CONCEPT_ASSERT(( boost_concepts::IncrementableIteratorConcept<OutputIterator> ));
222 BOOST_CONCEPT_ASSERT(( boost_concepts::WritableIteratorConcept<OutputIterator,Point> ));
223 BOOST_CONCEPT_ASSERT(( concepts::CPolarPointComparator2D<PolarComparator> ));
228 std::vector<Point> container;
229 std::copy( itb, ite, std::back_inserter( container ) );
231 //find an extremal point
232 //NB: we choose the point of greatest x-coordinate
233 //so that the sort step (by a polar comparator)
234 //returns a weakly externally visible polygon
235 typename std::vector<Point>::iterator itMax
236 = std::max_element( container.begin(), container.end() );
238 //sort around this point
239 aPolarComparator.setPole( *itMax );
240 std::sort( container.begin(), container.end(), aPolarComparator );
242 //scan from an extremal point
243 closedGrahamScanFromVertex( container.begin(), container.end(), res, aPredicate );
247 //----------------------------------------------------------------------------
248 template <typename ForwardIterator,
249 typename OutputIterator,
252 void grahamConvexHullAlgorithm(const ForwardIterator& itb, const ForwardIterator& ite,
254 const Predicate& aPredicate)
256 BOOST_CONCEPT_ASSERT(( boost_concepts::ForwardTraversalConcept<ForwardIterator> ));
257 BOOST_CONCEPT_ASSERT(( boost_concepts::ReadableIteratorConcept<ForwardIterator> ));
258 typedef typename IteratorCirculatorTraits<ForwardIterator>::Value Point;
260 //define a default comparator
261 typedef typename Point::Coordinate Integer;
262 typedef AvnaimEtAl2x2DetSignComputer<Integer> DetComputer;
263 typedef functors::PolarPointComparatorBy2x2DetComputer<Point, DetComputer> Comparator;
264 Comparator comparator;
266 //call overloaded version
267 grahamConvexHullAlgorithm( itb, ite, res, aPredicate, comparator );
270 //----------------------------------------------------------------------------
271 template <typename ForwardIterator,
272 typename OutputIterator,
275 void andrewConvexHullAlgorithm(const ForwardIterator& itb, const ForwardIterator& ite,
277 const Predicate& aPredicate )
279 BOOST_CONCEPT_ASSERT(( boost_concepts::ForwardTraversalConcept<ForwardIterator> ));
280 BOOST_CONCEPT_ASSERT(( boost_concepts::ReadableIteratorConcept<ForwardIterator> ));
281 typedef typename IteratorCirculatorTraits<ForwardIterator>::Value Point;
282 BOOST_CONCEPT_ASSERT(( boost_concepts::IncrementableIteratorConcept<OutputIterator> ));
283 BOOST_CONCEPT_ASSERT(( boost_concepts::WritableIteratorConcept<OutputIterator,Point> ));
288 std::vector<Point> container;
289 std::copy( itb, ite, std::back_inserter( container ) );
290 std::vector<Point> upperHull, lowerHull;
292 //sort according to the x-coordinate
293 std::sort( container.begin(), container.end() );
295 //lower hull computation
296 openGrahamScan( container.begin(), container.end(), std::back_inserter(lowerHull), aPredicate );
298 //upper hull computation
299 openGrahamScan( container.rbegin(), container.rend(), std::back_inserter(upperHull), aPredicate );
302 typename std::vector<Point>::iterator lowerHullStart = lowerHull.begin();
303 if ( lowerHull.front() == upperHull.back() )
305 std::copy( lowerHullStart, lowerHull.end(), res );
308 typename std::vector<Point>::iterator upperHullStart = upperHull.begin();
309 if ( lowerHull.back() == upperHull.front() )
311 std::copy( upperHullStart, upperHull.end(), res );
317 template <typename ForwardIterator>
319 double computeHullThickness(const ForwardIterator &itb,
320 const ForwardIterator &ite,
321 const ThicknessDefinition &def)
323 typedef typename std::iterator_traits<ForwardIterator>::value_type TInputPoint;
325 return computeHullThickness(itb, ite, def, p, q, r);
329 template <typename ForwardIterator,
330 typename TInputPoint>
332 double computeHullThickness(const ForwardIterator &itb,
333 const ForwardIterator &ite,
334 const ThicknessDefinition &def,
335 TInputPoint& antipodalEdgeP,
336 TInputPoint& antipodalEdgeQ,
337 TInputPoint& antipodalVertexR)
340 WrapIt(const ForwardIterator &b, const ForwardIterator &e): myB(b), myE(e),
341 mySize(std::distance(b, e)){}
342 TInputPoint operator[](unsigned int i){
343 unsigned int j = i%(mySize);
350 WrapIt aConvexHull(itb, ite);
351 double resThickness = std::numeric_limits<double>::max();
354 unsigned int size = aConvexHull.mySize;
359 antipodalEdgeP = aConvexHull[0];
360 antipodalEdgeQ = aConvexHull[size==1?0:1];
361 antipodalVertexR = aConvexHull[size==1?0:1];
365 while(getAngle(aConvexHull[i], aConvexHull[i+1],
366 aConvexHull[j], aConvexHull[j+1]) < M_PI - angleTolerance ){
369 double th = getThicknessAntipodalPair(aConvexHull[i], aConvexHull[i+1], aConvexHull[j], def);
372 antipodalVertexR = aConvexHull[j];
373 antipodalEdgeP = aConvexHull[i];
374 antipodalEdgeQ = aConvexHull[i+1];
378 if(getAngle(aConvexHull[i], aConvexHull[i+1],
379 aConvexHull[j], aConvexHull[j+1])< M_PI - angleTolerance){
382 th = getThicknessAntipodalPair(aConvexHull[i], aConvexHull[i+1],
383 aConvexHull[j], def);
386 antipodalVertexR = aConvexHull[j];
387 antipodalEdgeP = aConvexHull[i];
388 antipodalEdgeQ = aConvexHull[i+1];
390 if(isCoLinearOpp(aConvexHull[i], aConvexHull[i+1],
391 aConvexHull[j], aConvexHull[j+1]) ){
393 th = getThicknessAntipodalPair(aConvexHull[i], aConvexHull[i+1],
394 aConvexHull[j+1], def);
397 antipodalVertexR = aConvexHull[j+1];
398 antipodalEdgeP = aConvexHull[i];
399 antipodalEdgeQ = aConvexHull[i+1];
409 template<typename TInputPoint>
411 double getAngle(const TInputPoint &a, const TInputPoint &b, const TInputPoint &c, const TInputPoint &d)
413 TInputPoint v1 (b-a);
414 TInputPoint v2 (d-c);
415 double det = (v1[0]*v2[1] - v1[1]*v2[0])/ (v1.norm()*v2.norm());
416 double dot = (v1[0]*v2[0] + v1[1]*v2[1])/ (v1.norm()*v2.norm());
417 double ang = atan2(det, dot);
418 return ( ang < -angleTolerance) ? 2*M_PI+ang : ang;
422 template<typename TInputPoint>
424 bool isCoLinearOpp(const TInputPoint& a, const TInputPoint& b,
425 const TInputPoint& c,const TInputPoint& d)
427 double ang = getAngle(a, b, c, d);
428 return ang < M_PI + angleTolerance &&
429 ang > M_PI - angleTolerance ;
434 template<typename TInputPoint>
436 double getThicknessAntipodalPair(const TInputPoint &p, const TInputPoint &q,
437 const TInputPoint &r, const ThicknessDefinition &def){
439 if(def == HorizontalVerticalThickness){
440 double dH = computeHProjDistance(p, q, r, isInside);
441 double dV = computeVProjDistance(p, q, r, isInside);
442 return dH > dV ? dV : dH;
444 return computeEuclideanDistance(p, q, r, isInside);
448 template< typename TInputPoint>
451 computeHProjDistance(const TInputPoint &a, const TInputPoint &b, const TInputPoint &c,
456 return std::numeric_limits<double>::max();
460 auto k = -(a[0]-b[0])*c[1]-(b[0]*a[1]-a[0]*b[1]);
461 isInside = ( a[1] <= c[1] && c[1] <= b[1] )
462 || ( b[1] <= c[1] && c[1] <= a[1] );
463 return std::abs((k/static_cast<double>(b[1]-a[1])) - c[0]);
467 template< typename TInputPoint>
470 computeVProjDistance(const TInputPoint &a, const TInputPoint &b,
471 const TInputPoint &c, bool &isInside )
475 return std::numeric_limits<double>::max();
479 auto k = -(b[1]-a[1])*c[0]-(b[0]*a[1]-a[0]*b[1]);
480 isInside = ( a[0] <= c[0] && c[0] <= b[0] )
481 || ( b[0] <= c[0] && c[0] <= a[0] );
482 return std::abs((k/static_cast<double>(a[0]-b[0])) - c[1]);
487 template< typename TInputPoint>
490 computeEuclideanDistance(const TInputPoint &a, const TInputPoint &b, const TInputPoint &c,
495 auto dc = b[0]*a[1]-a[0]*b[1];
496 auto const pos = (b-a).dot(c-a);
497 isInside = (0 <= pos) && (pos <= (b-a).dot(b-a));
498 return std::abs(dy*c[0]+dx*c[1]+dc)/(std::sqrt(dy*dy+dx*dx));
503 } // namespace convexHull2D
504 } // namespace functions
508///////////////////////////////////////////////////////////////////////////////