I am using the boost geometry library to store polygons and I want to print different polygons on the console to debug my geometries like rectangle, n point polygon. Is there any library to do so in Linux? Thanks!
- 
                    Look here for inspiration https://stackoverflow.com/questions/47753700/draw-line-using-c-without-graphics/47758148#47758148 – sehe Dec 13 '17 at 08:33
1 Answers
For sheer fun, I adapted that Canvas example from yesterday (Draw Line using c++ without graphics) with some Boost Geometry support.
You can use it like:
int main() {
    using manip::as_geo;
    Polygon poly;
    bg::read_wkt("POLYGON((0 0,0 7,4 2,2 0,0 0))", poly);
    std::cout << as_geo(poly);
    // a polygon with a hole
    std::cout << as_geo<Polygon>("POLYGON((0 0,0 7,4 2,2 0,0 0) (1.5 2.5, 1.5 3.0, 2.5 3.0, 2.5 2.5, 1.5 2.5))");
    // custom canvas size and glyphs
    std::cout << as_geo<60, 30>(poly, '@', '%');
}
Which renders the first polygon as 
The second polygon has a rectangular inner ring and shows as 
The third option uses a custom canvas size and drawing glyphs: 
Implementation
Utility Vec2
There's a generic x/y pair class with many conversions and basic arithmetic operations (for scaling, offsetting, logical-physical conversions):
namespace Utility {
template <typename T> struct Vec2 {
    T x, y;
    Vec2(T x = {}, T y = {}) : x(x), y(y) {}
    template <typename U, typename V> Vec2(U const& x, V const& y) : x(x), y(y) {}
    template <typename U> Vec2(Vec2<U> const& rhs) : Vec2(rhs.x, rhs.y) {}
    template <typename U> Vec2& operator=(Vec2<U> const& rhs) {
        return *this = {rhs.x, rhs.y};
    }
#define VEC_OPS VEC_DECLARE_OP(*) VEC_DECLARE_OP(/) VEC_DECLARE_OP(+) VEC_DECLARE_OP(-)
#define VEC_DECLARE_OP(op) template <typename U, typename R = typename std::common_type<T, U>::type> \
    Vec2<R> operator op(Vec2<U> const& rhs) const { return {x op rhs.x, y op rhs.y}; }
    VEC_OPS
#undef VEC_DECLARE_OP
#define VEC_DECLARE_OP(op) template <typename U, typename R = typename std::common_type<T, U>::type> \
    Vec2<R> operator op(U const& rhs) const { return {x op rhs, y op rhs}; }
    VEC_OPS
#undef VEC_DECLARE_OP
#define VEC_DECLARE_OP(op) template <typename U, typename R = typename std::common_type<T, U>::type> \
    Vec2& operator op##=(U const& rhs) { return operator=((*this) op rhs); }
    VEC_OPS
#undef VEC_DECLARE_OP
  private:
    friend std::ostream& operator<<(std::ostream& os, Vec2 const& xy) {
        return os << "{" << xy.x << "," << xy.y << "}";
    }
};
}
Canvas Type
Let's start with defining some useful building blocks:
using Physical = Utility::Vec2<int>;
using Logical  = Utility::Vec2<double>;
using Scale    = Utility::Vec2<double>;
struct Extents { 
    Logical TopLeft, BottomRight; 
    void normalize() {
        if (TopLeft.y < BottomRight.y) std::swap(TopLeft.y, BottomRight.y);
        if (TopLeft.x > BottomRight.x) std::swap(TopLeft.x, BottomRight.x);
    }
    auto height() const { return std::abs(TopLeft.y - BottomRight.y); }
    auto width() const { return std::abs(BottomRight.x - TopLeft.x); }
    friend std::ostream& operator<<(std::ostream& os, Extents const& e) {
        return os << "{" << e.TopLeft << " - " << e.BottomRight << "; " << e.width() << "×" << e.height() << "}";
    }
};
Now we can go ahead with the canvas, largely the same as in the previous answer:
template <int Columns = 100, int Rows = 50>
struct BasicCanvas {
    using Line   = std::array<char, Columns>;
    using Screen = std::array<Line, Rows>;
    static constexpr size_t rows()    { return Rows;    }
    static constexpr size_t columns() { return Columns; }
    BasicCanvas(Logical origin = {Columns/2, Rows/2}, Scale scale = {1.0, 1.0}) : origin(origin), scale(scale) {
        clear();
    }
    BasicCanvas(Extents extents) {
        extents.normalize();
        using Utility::Vec2;
        scale = Vec2{extents.width(), extents.height()} / Vec2{Columns, Rows};
        origin = { -extents.TopLeft.x, -extents.BottomRight.y };
        clear();
    }
    Screen screen;
    Logical origin;
    Scale scale; // physical * scale = logical
    Logical to_logical(Physical const& phys) const  { return (phys * scale) - origin; }
    Physical to_physical(Logical const& log)  const { return (log + origin) / scale; }
    Extents extents() const { return { to_logical({ 0, Rows }), to_logical({ Columns, 0}) }; }
    friend std::ostream& operator<<(std::ostream& os, BasicCanvas const& c) {
        for (auto& line : c.screen) {
            os.write(line.data(), line.size()) << "\n";
        }
        return os;
    }
    Line&       operator[](size_t y)             { return screen.at(screen.size()-(y+1)); }
    Line const& operator[](size_t y) const       { return screen.at(screen.size()-(y+1)); }
    char&       operator[](Physical coord)       { return operator[](coord.y).at(coord.x); }
    char const& operator[](Physical coord) const { return operator[](coord.y).at(coord.x); }
    void clear(char filler = '.') {
        Line empty;
        std::fill(empty.begin(), empty.end(), filler);
        std::fill(screen.begin(), screen.end(), empty);
    }
    void axes() {
        Physical phys_org = to_physical({0,0});
        auto const y_shown = (phys_org.x >= 0 && phys_org.x < Columns);
        auto const x_shown = (phys_org.y >= 0 && phys_org.y < Rows);
        if (y_shown)
            for (auto& line : screen)
                line.at(phys_org.x) = '|';
        if (x_shown) {
            auto& y_axis = operator[](phys_org.y);
            for (auto& cell : y_axis)
                cell = '-';
            if (y_shown)
                y_axis.at(phys_org.x) = '+';
        }
    }
    template <typename F>
    void plot(F f) {
        for (size_t x_tick = 0; x_tick < Columns; ++x_tick) {
            auto x = to_logical({ x_tick, 0 }).x;
            auto y = f(x);
            auto y_ = derivative(f, x, scale.x/2);
            size_t y_tick = to_physical({x, y}).y;
            if (y_tick < Rows)
                operator[]({x_tick, y_tick}) = line_glyph(y_);
        }
    }
  private:
    template <typename F>
    auto derivative(F const& f, double x, double dx = 0.01) {
        return (f(x+dx)-f(x-dx))/(2*dx);
    }
    char line_glyph(double tangent) {
        auto angle = atan(tangent);
        while (angle < 0) 
            angle += 2*M_PI;
        int angle_index = 2.0 * angle / atan(1);
        return R"(--/||\--)"[angle_index % 8];
    }
};
Note This now contains some operations (
axes()andplot()that aren't strictly required for the purpose of this answer.
Supporting Boost Geometry
In the interest of de-coupling, let's define the drawing operations out-of-class.
For the purpose of this demonstration I have only implemented an operation
fill(geo, filler_char), which implies we can handle planar surfaces for now. Line-segments and linestrings could be added similarly to theplotoperation above, but I suggest to look at the Bresenham Algorithm to avoid sub-optimal results.
Let's introduce Boost Geometry:
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/polygon.hpp>
#include <boost/geometry/geometries/box.hpp>
#include <boost/geometry/io/io.hpp>
namespace bg = boost::geometry;
using Point   = bg::model::d2::point_xy<double, bg::cs::cartesian>;
using Polygon = bg::model::polygon<Point>;
using Box     = bg::model::box<Point>;
We are going to use Box to detect a bounding rectangle for the geometry we're about to draw. This way we don't have to worry about scaling the geometry to fit the canvas.
Assuming we have the canvas set-up, we can implement fill quite simply as:
template <typename Canvas, typename G>
void fill(Canvas& canvas, G const& geo, char filler = '*') {
    for (size_t x_tick = 0; x_tick < canvas.columns(); ++x_tick) {
        for (size_t y_tick = 0; y_tick < canvas.rows(); ++y_tick) {
            Physical phys { x_tick, y_tick };
            Logical log = canvas.to_logical(phys);
            if (bg::within(Point(log.x, log.y), geo))
                canvas[{x_tick, y_tick}] = filler;
        }
    }
}
Note: no performance considerations have been taken into account for now
A Stream IO Manipulator
To glue it all together like in the sample we started with, we create an IO manipulator that takes care of
- calculating the bounding box
- instantiating a canvas with sufficient extent to fit the bounding box
- draw the background
- fill the geometry
- optionally parses the geometry right from WKT, in which case the caller must specify the type to deserialize
template <typename Geo, int Columns = 100, int Rows = 50>
struct geo_manip {
    Geo _geo_or_ref;
    char _filler, _bg;
    friend std::ostream& operator<<(std::ostream& os, geo_manip const& gm) {
        Box bounding;
        bg::envelope(gm._geo_or_ref, bounding);
        BasicCanvas<Columns, Rows> canvas(Extents {
                {bounding.max_corner().x(), bounding.max_corner().y()},
                {bounding.min_corner().x(), bounding.min_corner().y()}});
        canvas.clear(gm._bg);
        fill(canvas, gm._geo_or_ref, gm._filler);
        os << "Canvas extents: " << canvas.extents() << "\n";
        os << "Canvas origin:" << canvas.origin << " scale:" << canvas.scale << "\n";
        return os << canvas;
    }
};
The convenience function as_geo makes it easy to create the manipulator, either for existing Geometries (by reference) or parsing a WKT fragment:
template <int Columns = 100, int Rows = 50, typename Geo>
geo_manip<Geo const&, Columns, Rows> as_geo(Geo const& geo, char filler = '*', char background = ' ') {
    return {geo, filler, background};
}
template <typename Geo = Polygon, int Columns = 100, int Rows = 50>
geo_manip<Geo, Columns, Rows> as_geo(std::string const& wkt, char filler = '*', char background = ' ') {
    Geo geo;
    bg::read_wkt(wkt, geo);
    return {geo, filler, background};
}
Full Live Demo
#include <iostream>
#include <array>
#include <limits>
#include <cmath>
namespace Utility {
    template <typename T> struct Vec2 {
        T x, y;
        Vec2(T x = {}, T y = {}) : x(x), y(y) {}
        template <typename U, typename V> Vec2(U const& x, V const& y) : x(x), y(y) {}
        template <typename U> Vec2(Vec2<U> const& rhs) : Vec2(rhs.x, rhs.y) {}
        template <typename U> Vec2& operator=(Vec2<U> const& rhs) {
            return *this = {rhs.x, rhs.y};
        }
    #define VEC_OPS VEC_DECLARE_OP(*) VEC_DECLARE_OP(/) VEC_DECLARE_OP(+) VEC_DECLARE_OP(-)
    #define VEC_DECLARE_OP(op) template <typename U, typename R = typename std::common_type<T, U>::type> \
        Vec2<R> operator op(Vec2<U> const& rhs) const { return {x op rhs.x, y op rhs.y}; }
        VEC_OPS
    #undef VEC_DECLARE_OP
    #define VEC_DECLARE_OP(op) template <typename U, typename R = typename std::common_type<T, U>::type> \
        Vec2<R> operator op(U const& rhs) const { return {x op rhs, y op rhs}; }
        VEC_OPS
    #undef VEC_DECLARE_OP
    #define VEC_DECLARE_OP(op) template <typename U, typename R = typename std::common_type<T, U>::type> \
        Vec2& operator op##=(U const& rhs) { return operator=((*this) op rhs); }
        VEC_OPS
    #undef VEC_DECLARE_OP
      private:
        friend std::ostream& operator<<(std::ostream& os, Vec2 const& xy) {
            return os << "{" << xy.x << "," << xy.y << "}";
        }
    };
}
using Physical = Utility::Vec2<int>;
using Logical  = Utility::Vec2<double>;
using Scale    = Utility::Vec2<double>;
struct Extents { 
    Logical TopLeft, BottomRight; 
    void normalize() {
        if (TopLeft.y < BottomRight.y) std::swap(TopLeft.y, BottomRight.y);
        if (TopLeft.x > BottomRight.x) std::swap(TopLeft.x, BottomRight.x);
    }
    auto height() const { return std::abs(TopLeft.y - BottomRight.y); }
    auto width() const { return std::abs(BottomRight.x - TopLeft.x); }
    friend std::ostream& operator<<(std::ostream& os, Extents const& e) {
        return os << "{" << e.TopLeft << " - " << e.BottomRight << "; " << e.width() << "×" << e.height() << "}";
    }
};
template <int Columns = 100, int Rows = 50>
struct BasicCanvas {
    using Line   = std::array<char, Columns>;
    using Screen = std::array<Line, Rows>;
    static constexpr size_t rows()    { return Rows;    }
    static constexpr size_t columns() { return Columns; }
    BasicCanvas(Logical origin = {Columns/2, Rows/2}, Scale scale = {1.0, 1.0}) : origin(origin), scale(scale) {
        clear();
    }
    BasicCanvas(Extents extents) {
        extents.normalize();
        using Utility::Vec2;
        scale = Vec2{extents.width(), extents.height()} / Vec2{Columns, Rows};
        origin = { -extents.TopLeft.x, -extents.BottomRight.y };
        clear();
    }
    Screen screen;
    Logical origin;
    Scale scale; // physical * scale = logical
    Logical to_logical(Physical const& phys) const  { return (phys * scale) - origin; }
    Physical to_physical(Logical const& log)  const { return (log + origin) / scale; }
    Extents extents() const { return { to_logical({ 0, Rows }), to_logical({ Columns, 0}) }; }
    friend std::ostream& operator<<(std::ostream& os, BasicCanvas const& c) {
        for (auto& line : c.screen) {
            os.write(line.data(), line.size()) << "\n";
        }
        return os;
    }
    Line&       operator[](size_t y)             { return screen.at(screen.size()-(y+1)); }
    Line const& operator[](size_t y) const       { return screen.at(screen.size()-(y+1)); }
    char&       operator[](Physical coord)       { return operator[](coord.y).at(coord.x); }
    char const& operator[](Physical coord) const { return operator[](coord.y).at(coord.x); }
    void clear(char filler = '.') {
        Line empty;
        std::fill(empty.begin(), empty.end(), filler);
        std::fill(screen.begin(), screen.end(), empty);
    }
    void axes() {
        Physical phys_org = to_physical({0,0});
        auto const y_shown = (phys_org.x >= 0 && phys_org.x < Columns);
        auto const x_shown = (phys_org.y >= 0 && phys_org.y < Rows);
        if (y_shown)
            for (auto& line : screen)
                line.at(phys_org.x) = '|';
        if (x_shown) {
            auto& y_axis = operator[](phys_org.y);
            for (auto& cell : y_axis)
                cell = '-';
            if (y_shown)
                y_axis.at(phys_org.x) = '+';
        }
    }
    template <typename F>
    void plot(F f) {
        for (size_t x_tick = 0; x_tick < Columns; ++x_tick) {
            auto x = to_logical({ x_tick, 0 }).x;
            auto y = f(x);
            auto y_ = derivative(f, x, scale.x/2);
            size_t y_tick = to_physical({x, y}).y;
            if (y_tick < Rows)
                operator[]({x_tick, y_tick}) = line_glyph(y_);
        }
    }
  private:
    template <typename F>
    auto derivative(F const& f, double x, double dx = 0.01) {
        return (f(x+dx)-f(x-dx))/(2*dx);
    }
    char line_glyph(double tangent) {
        auto angle = atan(tangent);
        while (angle < 0) 
            angle += 2*M_PI;
        int angle_index = 2.0 * angle / atan(1);
        return R"(--/||\--)"[angle_index % 8];
    }
};
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/polygon.hpp>
#include <boost/geometry/geometries/box.hpp>
#include <boost/geometry/io/io.hpp>
namespace bg = boost::geometry;
using Point   = bg::model::d2::point_xy<double, bg::cs::cartesian>;
using Polygon = bg::model::polygon<Point>;
using Box     = bg::model::box<Point>;
template <typename Canvas, typename G>
void fill(Canvas& canvas, G const& geo, char filler = '*') {
    for (size_t x_tick = 0; x_tick < canvas.columns(); ++x_tick) {
        for (size_t y_tick = 0; y_tick < canvas.rows(); ++y_tick) {
            Physical phys { x_tick, y_tick };
            Logical log = canvas.to_logical(phys);
            if (bg::within(Point(log.x, log.y), geo))
                canvas[phys] = filler;
        }
    }
}
namespace manip {
    template <typename Geo, int Columns = 100, int Rows = 50>
    struct geo_manip {
        Geo _geo_or_ref;
        char _filler, _bg;
        friend std::ostream& operator<<(std::ostream& os, geo_manip const& gm) {
            Box bounding;
            bg::envelope(gm._geo_or_ref, bounding);
            BasicCanvas<Columns, Rows> canvas(Extents {
                    {bounding.max_corner().x(), bounding.max_corner().y()},
                    {bounding.min_corner().x(), bounding.min_corner().y()}});
            canvas.clear(gm._bg);
            fill(canvas, gm._geo_or_ref, gm._filler);
            os << "Canvas extents: " << canvas.extents() << "\n";
            os << "Canvas origin:" << canvas.origin << " scale:" << canvas.scale << "\n";
            return os << canvas;
        }
    };
    template <int Columns = 100, int Rows = 50, typename Geo>
    geo_manip<Geo const&, Columns, Rows> as_geo(Geo const& geo, char filler = '*', char background = ' ') {
        return {geo, filler, background};
    }
    template <typename Geo = Polygon, int Columns = 100, int Rows = 50>
    geo_manip<Geo, Columns, Rows> as_geo(std::string const& wkt, char filler = '*', char background = ' ') {
        Geo geo;
        bg::read_wkt(wkt, geo);
        return {geo, filler, background};
    }
}
int main() {
    using manip::as_geo;
    Polygon poly;
    bg::read_wkt("POLYGON((0 0,0 7,4 2,2 0,0 0))", poly);
    std::cout << as_geo(poly);
    // a polygon with a hole
    std::cout << as_geo<Polygon>("POLYGON((0 0,0 7,4 2,2 0,0 0) (1.5 2.5, 1.5 3.0, 2.5 3.0, 2.5 2.5, 1.5 2.5))");
    // custom canvas size and glyphs
    std::cout << as_geo<60, 30>(poly, '@', '%');
}
The output is also live on coliru.
 
    
    - 374,641
- 47
- 450
- 633
- 
                    Out of the box, have you looked at https://github.com/awulkiew/graphical-debugging? – sehe Dec 13 '17 at 22:53
 
    