convexhull.h

Back to Convex hull of a point-set

pastel/geometry/

// Description: Convex hull of a point-set

#ifndef PASTELGEOMETRY_CONVEXHULL_H
#define PASTELGEOMETRY_CONVEXHULL_H

#include "pastel/sys/vector.h"
#include "pastel/sys/math_functions.h"

#include "pastel/geometry/predicates.h"

#include <vector>
#include <algorithm>

namespace Pastel
{

    namespace ConvexHull_
    {

        namespace GrahamsScan
        {

            template <typename Real>
            class ConvexHullPointCompare
            {
            public:
                bool operator()(const Vector<Real, 2>& left,
                    const Vector<Real, 2>& right) const
                {
                    return lexicographicLess(left, right);
                }
            };

        }

    }

    //! Computes the boundary of the convex hull of a point set.
    /*!
   Time complexity: O(n lg n)
   Exception safety: strong

   The convex hull of a point set is the set of all
   convex combinations of the point set.

   This function uses the Graham's scan algorithm for
   computing the boundary of the convex hull.

   An optimal convex hull algorithm is output-sensitive
   with complexity O(n log h), where h is the number
   of vertices on the boundary of the convex hull.
   */
    template <typename Real>
    void convexHullGrahamsScan(
        const std::vector<Vector<Real, 2> >& pointSet,
        std::vector<Vector<Real, 2> >& hullResult)
    {
        // This is the "Graham's scan" algorithm.

        std::vector<Vector<Real, 2> > hull;

        if (pointSet.empty())
        {
            return;
        }

        std::vector<Vector<Real, 2> > orderedSet(pointSet);
        std::sort(orderedSet.begin(), orderedSet.end(),
            ConvexHull_::GrahamsScan::ConvexHullPointCompare<Real>());

        using Container = std::vector<Vector<Real, 2> >;
        typedef typename Container::iterator
            Iterator;
        typedef typename Container::const_iterator
            ConstIterator;
        typedef typename Container::reverse_iterator
            ReverseIterator;
        typedef typename Container::const_reverse_iterator
            ConstReverseIterator;

        // Scan the upper hull.

        ConstIterator from(orderedSet.begin());
        ConstIterator to(orderedSet.end());
        ConstIterator current(from);

        integer points = 0;

        hull.push_back(*current);
        ++points;
        ++current;
        if (current == to)
        {
            // No more points left,
            // we are done.
            hull.swap(hullResult);

            return;
        }

        // Find a second point
        // from the hull.

        while(points < 2)
        {
            if (*current != hull.back())
            {
                hull.push_back(*current);
                ++points;
            }
            ++current;
            if (current == to)
            {
                // No more points left,
                // we are done.
                hull.swap(hullResult);

                return;
            }
        }

        // Scan the upper hull.

        while (current != to)
        {
            const Vector<Real, 2>& toPoint = *current;

            if (toPoint != hull.back())
            {
                while (points > 1)
                {
                    const Vector<Real, 2>& fromPoint = hull[points - 2];
                    const Vector<Real, 2>& middlePoint = hull[points - 1];

                    if (signedArea(fromPoint, middlePoint, toPoint) > 0)
                    {
                        hull.pop_back();
                        --points;
                    }
                    else
                    {
                        break;
                    }
                }

                hull.push_back(toPoint);
                ++points;
            }

            ++current;
        }

        // Scan the lower hull.

        ConstReverseIterator rFrom(
            orderedSet.rbegin());
        ConstReverseIterator rTo(
            orderedSet.rend());

        ++rFrom;

        ConstReverseIterator rCurrent(rFrom);

        while (rCurrent != rTo)
        {
            const Vector<Real, 2>& toPoint = *rCurrent;

            if (toPoint != hull.back())
            {
                while (true)
                {
                    const Vector<Real, 2>& fromPoint = hull[points - 2];
                    const Vector<Real, 2>& middlePoint = hull[points - 1];

                    if (signedArea(fromPoint, middlePoint, toPoint) > 0)
                    {
                        hull.pop_back();
                        --points;
                    }
                    else
                    {
                        break;
                    }
                }

                hull.push_back(toPoint);
                ++points;
            }

            ++rCurrent;
        }

        hull.pop_back();

        hull.swap(hullResult);
    }

}

#endif