How to calculate the convex hull with boost from arrays instead of setting each point separately?

醉酒当歌 提交于 2019-12-04 16:50:21
sehe

In addition to the mechanics of the polygon<> model shown in the other answer, you might be able to replace polygon<> by ring<> model because there doesn't seem to be inner rings involved?

That way, you can directly initialize into your ring:

typedef bg::model::ring<point> ring;

ring poly {
    { 0.0, 0.0 },
    //{ 0.5, 0.5 }, // mid point, which should not be part of the hull
    { 1.0, 0.0 },
    { 1.0, 1.0 },
    { 2.0, 2.0 },
    { 1.0, 0.0 },
};

And the API call could read like:

point const* hull_array_ptr = &hull.front();

call_API(hull_array_ptr, hull.size());

DEMO

Live On Coliru

#include <boost/geometry.hpp>
#include <boost/geometry/geometries/ring.hpp>
#include <boost/geometry/io/io.hpp>
#include <boost/geometry/geometries/adapted/boost_tuple.hpp>
#include <boost/geometry/geometries/register/point.hpp>
#include <iostream>

using namespace std;
namespace bg = boost::geometry;

BOOST_GEOMETRY_REGISTER_BOOST_TUPLE_CS(cs::cartesian)
typedef boost::tuple<float, float> point;

void dump(point const& p) { cout << get<0>(p) << " " << get<1>(p) << ","; }

void call_API(point const* arr, size_t n) {
    cout << "hull (API):";
    for_each(arr, arr+n, dump);
    cout << "\n";
}

int main()
{
    typedef bg::model::ring<point> ring;

    ring poly {
        { 0.0, 0.0 },
        //{ 0.5, 0.5 }, // mid point, which should not be part of the hull
        { 1.0, 0.0 },
        { 1.0, 1.0 },
        { 2.0, 2.0 },
        { 1.0, 0.0 },
    };

    cout << "raw:       " << bg::wkt(poly) << "\n";
    bg::correct(poly);
    cout << "corrected: " << bg::wkt(poly) << "\n";

    ring hull;
    bg::convex_hull(poly, hull);

    cout << "hull:      " << bg::wkt(hull) << "\n";

    point const* hull_array_ptr = &hull.front();

    call_API(hull_array_ptr, hull.size());
}

Prints, again:

raw:       POLYGON((0 0,1 0,1 1,2 2,1 0))
corrected: POLYGON((0 0,1 0,1 1,2 2,1 0,0 0))
hull:      POLYGON((0 0,2 2,1 0,0 0))
hull (API):0 0,2 2,1 0,0 0,

Okay, third time is the charm, right

OP: Thanks for you effort, but this is not the wanted solution. I clarified the question. The objective is to have a flat c-style array of floats for input and output. A print out of the data should work like this:

for( int i = 0; i<size; i++ ) 
    cout << hull[i];

Me: What would the floats mean?

OP: Alternating x and y coordinates, [...]

If you do the necessary things

  • size/padding checks
  • alignment overrides

You can create a ring as a range of type-punned points over a raw float[]:

template<typename T> using compact_point = boost::tuple<T, T>;
template<typename T> using compact_ring  = boost::iterator_range<T*>;

static_assert(sizeof(compact_point<float>)  == 2*sizeof(float), "");
static_assert(alignof(compact_point<float>) >= alignof(float), "");

BOOST_GEOMETRY_REGISTER_BOOST_TUPLE_CS(cs::cartesian)
BOOST_GEOMETRY_REGISTER_RING_TEMPLATED(compact_ring)

using point = compact_point<float>;
using ring  = compact_ring<point>;

NOTE keep in mind this only works for readonly rings

Conversion routines:

template <typename T, size_t N>
compact_ring<compact_point<T> > as_compact_ring(T (&arr)[N]) {
    auto f = reinterpret_cast<point*>(+arr);
    return { f, f + N/2 };
}

template <typename T>
boost::iterator_range<T const*> as_compact_points(std::vector<compact_point<T> > const& r) {
    auto f = reinterpret_cast<T const*>(&r[0]);
    return { f, f + r.size()*2 };
}

There you go, you can now apply:

Demo

int main() {
    alignas(compact_point<float>) float ringdata[] {
        0.0, 0.0, // clockwise rect
        0.0, 2.0,
        //
        1.0, 1.0, // dent...
        //
        2.0, 2.0,
        2.0, 0.0,
        0.0, 0.0,
    };

    ring poly = as_compact_ring(ringdata);

    cout << "raw: " << bg::wkt(poly) << "\n";

    std::string reason;
    if (!bg::is_valid(poly, reason)) {
        std::cout << "NOT VALID: " << reason << "\n";
        return 255;
    }

    bg::model::ring<point> hull; // not a range proxy though
    bg::convex_hull(poly, hull);

    cout << "hull:" << bg::wkt(hull) << "\n";

    // force back:
    auto view = as_compact_points(hull);
    float const* rawhull = &*view.begin();

    call_API(rawhull, 2*hull.size());
}

See it Live On Coliru

Prints

raw: POLYGON((0 0,0 2,1 1,2 2,2 0,0 0))
hull:POLYGON((0 0,0 2,2 2,2 0,0 0))
hull (API):0 0 0 2 2 2 2 0 0 0 

Q. The points appended with bg::append, should be in an array, so that the ideal function would be: bg::append_points(arrayOf_X_Y_coordinates)

You can assign the outer ring of the polygon at once:

point raw[] = {
    { 0.0, 0.0 },
    //{ 0.5, 0.5 }, // mid point, which should not be part of the hull
    { 1.0, 0.0 },
    { 1.0, 1.0 },
 // { 2.0, 2.0 },
    { 1.0, 0.0 },
};

// define rectangle, this will become also our convex hull
poly.outer().assign(begin(raw), end(raw));

Q. I also wanna pass the result (hull) as an array pointer to OpenGL instead of iterating over and reading out the coordinates float by float ( with bg::get<0>(*it)).

You can pass the address of the initial vector element:

point const* hull_array_ptr = &hull.outer().front();

call_API(hull_array_ptr, hull.outer().size());

DEMO

Notes Make sure your polygon is valid, satisfying the precondition for the convex_hull algorithm! correct does the necessary fixes here:

Live On Coliru

#include <boost/geometry.hpp>
#include <boost/geometry/geometries/polygon.hpp>
#include <boost/geometry/io/io.hpp>
#include <boost/geometry/geometries/adapted/boost_tuple.hpp>
#include <boost/geometry/geometries/register/point.hpp>
#include <iostream>

using namespace std;
namespace bg = boost::geometry;

BOOST_GEOMETRY_REGISTER_BOOST_TUPLE_CS(cs::cartesian)
typedef boost::tuple<float, float> point;

void dump(point const& p) { cout << get<0>(p) << " " << get<1>(p) << ","; }

void call_API(point const* arr, size_t n) {
    cout << "hull (API):";
    for_each(arr, arr+n, dump);
    cout << "\n";
}

int main()
{
    typedef bg::model::polygon<point> polygon;

    polygon poly, hull;

    point raw[] = {
        { 0.0, 0.0 },
        //{ 0.5, 0.5 }, // mid point, which should not be part of the hull
        { 1.0, 0.0 },
        { 1.0, 1.0 },
        { 2.0, 2.0 },
        { 1.0, 0.0 },
    };

    poly.outer().assign(begin(raw), end(raw));
    cout << "raw:       " << bg::wkt(poly) << "\n";
    bg::correct(poly);
    cout << "corrected: " << bg::wkt(poly) << "\n";

    bg::convex_hull(poly, hull);

    cout << "hull:      " << bg::wkt(hull) << "\n";

    point const* hull_array_ptr = &hull.outer().front();

    call_API(hull_array_ptr, hull.outer().size());
}

Output:

raw:       POLYGON((0 0,1 0,1 1,2 2,1 0,0 0))
corrected: POLYGON((0 0,1 0,1 1,2 2,1 0,0 0))
hull:      POLYGON((0 0,2 2,1 0,0 0))
hull (API):0 0,2 2,1 0,0 0,
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!