Search code examples
c++geometrycomputational-geometryboost-geometryboost-polygon

Polygon transformation in Boost.Geometry in C++: Translation, Rotation, Reflection around a line


I have three types of queries for a polygon to implement using boost geometry:

  1. translating a polygon to a given point
  2. rotating a polygon about a reference point(not necessarily about origin)
  3. reflecting a polygon around a line

As I am new to Boost Geomtry I am facing problems understanding the syntax. I searched for examples that satisfies my queries but not quite found one or found something that hard to understand for me. Could you please give any examples code that implement the queries with little explanation?


Solution

  • The most generic transform I know of in the library is the matrix transform, which is a strategy you can use with transform.

    Let's generate some random rectangles, rotated by random angles, translated over (-500..500, -500..500) and scaled between 1..3:

    polygon gen_rectangle() {
        using box = bgm::box<point_xy>;
        polygon raw, result;
        box initial { {0, 0}, { rand(1, 1'000), rand(1, 1'000) } };
        bg::correct(raw);
        bg::assign(raw, initial);
    
        using namespace bg::strategy::transform;
        auto rot   = rand(-M_PI, +M_PI);
        auto scale = rand(1.0, 3.0);
        auto x     = rand(-500.0, 500.0),
             y     = rand(-500.0, 500.0);
    
        matrix_transformer<double, 2, 2> xfrm(
                scale* cos(rot), scale*sin(rot), x,
                scale*-sin(rot), scale*cos(rot), y,
                0,              0, 1);
    
        boost::geometry::transform(raw, result, xfrm);
        return result;
    }
    

    Writing some frames:

    int main(int argc, char** argv) {
        auto const seed = argc>1? std::stoul(argv[1]) : std::random_device{}();
        prng.seed(seed);
    
        // generate shapes
        for (int frame = 0; frame < 30; ++frame) {
            multi_polygon shapes, merged;
            std::generate_n(back_inserter(shapes), 10, gen_rectangle);
            save_frame(shapes, frame);
        }
    }
    

    Results in:

    enter image description here

    Mirroring In A Line

    I don't think that's covered with that transform (although you might be able to express any of these with a suitable translation, followed by a rotation and another translation?).

    I'd attack that by a point-wise transform, where you do the projection of a point on a line (see e.g. How to find two points that form closest distance between two rectangles?, but less complicated because you can assume infinite lines, not segments), and extending the same distance on the other side.

    Full Demo Code

    Live On Coliru

    #include <boost/geometry.hpp>
    #include <boost/geometry/strategies/transform/matrix_transformers.hpp>
    #include <boost/geometry/geometries/point_xy.hpp>
    #include <boost/geometry/geometries/polygon.hpp>
    #include <boost/geometry/geometries/multi_polygon.hpp>
    #include <iostream>
    #include <fstream>
    #include <iomanip>
    #include <random>
    
    namespace bg  = boost::geometry;
    namespace bgm = bg::model;
    
    using point_xy      = bgm::d2::point_xy<double>;
    using polygon       = bgm::polygon<point_xy>;
    using multi_polygon = bgm::multi_polygon<polygon>;
    
    static std::mt19937 prng;
    void save_frame(multi_polygon const& p, int frameno);
    polygon gen_rectangle();
    
    int main(int argc, char** argv) {
        auto const seed = argc>1? std::stoul(argv[1]) : std::random_device{}();
        prng.seed(seed);
    
        // generate shapes
        for (int frame = 0; frame < 30; ++frame) {
            multi_polygon shapes, merged;
            std::generate_n(back_inserter(shapes), 10, gen_rectangle);
            save_frame(shapes, frame);
        }
    }
    
    static inline double rand(double b, double e) { return std::uniform_real_distribution<double>(b, e)(prng); }
    
    // generate rectangle shape with varying sizes, positions and rotations
    polygon gen_rectangle() {
        using box = bgm::box<point_xy>;
        polygon raw, result;
        box initial { {0, 0}, { rand(1, 1'000), rand(1, 1'000) } };
        bg::assign(raw, initial);
    
        using namespace bg::strategy::transform;
        double rot   = rand(-M_PI, +M_PI);
        double scale = rand(1.0, 3.0);
        double x     = rand(-500.0, 500.0),
               y     = rand(-500.0, 500.0);
    
        matrix_transformer<double, 2, 2> xfrm(
                scale* cos(rot), scale*sin(rot), x,
                scale*-sin(rot), scale*cos(rot), y,
                0,              0, 1);
    
        boost::geometry::transform(raw, result, xfrm);
        return result;
    }
    
    void save_frame(multi_polygon const& p, int frameno) {
        std::ostringstream name;
        name << "frame" << std::setw(4) << std::setfill('0') << frameno << ".svg";
        std::ofstream ofs(name.str());
        bg::svg_mapper<point_xy> mapper(ofs, 400, 400);
    
        mapper.add(p);
        mapper.map(p, "fill-opacity:0.5;fill:rgb(204,153,0);stroke:rgb(204,153,0);stroke-width:1", 5);
    }