如何找到两个矩形之间最接近的距离的两个点?

时间:2020-06-01 18:01:46

标签: boost geometry boost-geometry

我正在尝试找到一种算法,该算法将找到代表两个矩形之间最接近距离的两个点。像 C J 这样的点在下面的图像中形成最小的距离:

enter image description here

我试图在这里不重新发明方向盘,而使用经过boost::geometry::distance等经过考验的东西,但它只会返回距离,而不会返回积分。

1 个答案:

答案 0 :(得分:2)

不会使事物过于通用(通过假设浮点坐标和笛卡尔坐标系),这是点到线段距离的一种实现,该距离返回投影点以及距离:

struct DistancePoint {
    double distance;
    P projected_point;
};

template <typename Strategy = bg::strategy::distance::pythagoras<> >
DistancePoint point_to_segment(P const& p, P const& p1, P const& p2) {
    P v = p2, w = p;
    bg::subtract_point(v, p1);
    bg::subtract_point(w, p1);

    auto const c1 = bg::dot_product(w, v);
    if (c1 <= 0)  return { Strategy::apply(p, p1), p1 };

    auto const c2 = bg::dot_product(v, v);
    if (c2 <= c1) return { Strategy::apply(p, p2), p2 };

    P prj = p1;
    bg::multiply_value(v, c1/c2);
    bg::add_point(prj, v);

    return { Strategy::apply(p, prj), prj };
}

现在您可以将其与几何一起使用。我不想满足距离策略概念的所有条件,因此您不能将以上内容与boost::geometry::distance配合使用。

但是,由于您输入的几何图形具有较低的点数,因此您可以避免使用“强行强制”(不需要库内部进行closest_feature选择):

R a = gen_rect(),
  b = gen_rect();

// make sure a and b don't overlap (distance > 0)
while (!bg::disjoint(a,b)) { b = gen_rect(); }

std::cout
    << wkt(a) << "\n"
    << wkt(b) << "\n"
    << bg::distance(a, b) << " apart\n";

DistancePoint nearest;
P const* which = nullptr;

for (auto& [a,b] : { std::tie(a,b), std::tie(b,a) } ) {
    auto segments = boost::make_iterator_range(bg::segments_begin(a), bg::segments_end(a));
    auto points   = boost::make_iterator_range(bg::points_begin(b), bg::points_end(b));

    for (auto&& pq : segments) {
        for (auto&& r : points) {
            auto d = point_to_segment(r, *pq.first, *pq.second);

            if (!which || d.distance < nearest.distance) {
                which = &r;
                nearest = d;
            }
        }
    }
}

std::cout << wkt(which) << " at " << nearest.distance << " from " << wkt(nearest.projected_point) << "\n";

演示

Live On Coliru

#include <boost/geometry.hpp>
#include <boost/geometry/util/range.hpp>
#include <boost/geometry/io/io.hpp>
#include <boost/geometry/algorithms/assign.hpp>
#include <boost/geometry/algorithms/convex_hull.hpp>
#include <boost/geometry/algorithms/point_on_surface.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/polygon.hpp>
#include <boost/geometry/geometries/box.hpp>
#include <boost/range/adaptors.hpp>
#include <iostream>
#include <fstream>

namespace bg = boost::geometry;
namespace bgm = boost::geometry::model;
using namespace boost::adaptors;
using bg::wkt;

using P = bgm::d2::point_xy<double>;
using B = bgm::box<P>;
using S = bgm::segment<P>;
using R = bgm::polygon<P>;

R gen_rect(); // generates a random rectangle

namespace {
    struct DistancePoint {
        double distance;
        P projected_point;
    };

    // after strategy::distance::projected_point<>
    template <typename Strategy = bg::strategy::distance::pythagoras<> >
    DistancePoint point_to_segment(P const& p, P const& p1, P const& p2) {
        P v = p2, w = p;
        bg::subtract_point(v, p1);
        bg::subtract_point(w, p1);

        auto const c1 = bg::dot_product(w, v);
        if (c1 <= 0)  return { Strategy::apply(p, p1), p1 };

        auto const c2 = bg::dot_product(v, v);
        if (c2 <= c1) return { Strategy::apply(p, p2), p2 };

        P prj = p1;
        bg::multiply_value(v, c1/c2);
        bg::add_point(prj, v);

        return { Strategy::apply(p, prj), prj };
    }
}

int main() {
    std::cout << std::setprecision(2);

    for (auto i = 0; i<10; ++i) {
        R a = gen_rect(),
          b = gen_rect();

        // make sure a and b don't overlap (distance > 0)
        while (!bg::disjoint(a,b)) { b = gen_rect(); }

        std::cout
            << wkt(a) << "\n"
            << wkt(b) << "\n"
            << bg::distance(a, b) << " apart\n";

        DistancePoint nearest;
        P const* which = nullptr;

        for (auto& [a,b] : { std::tie(a,b), std::tie(b,a) } ) {
            auto segments = boost::make_iterator_range(bg::segments_begin(a), bg::segments_end(a));
            auto points   = boost::make_iterator_range(bg::points_begin(b), bg::points_end(b));

            for (auto&& pq : segments) {
                for (auto&& r : points) {
                    auto d = point_to_segment(r, *pq.first, *pq.second);

                    if (!which || d.distance < nearest.distance) {
                        which = &r;
                        nearest = d;
                    }
                }
            }
        }

        std::cout << wkt(which) << " at " << nearest.distance << " from " << wkt(nearest.projected_point) << "\n";

        {
            std::ofstream svg("output" + std::to_string(i) + ".svg");
            boost::geometry::svg_mapper<P> mapper(svg, 400, 400, "style='fill-opacity:1;fill:rgb(255,255,255)'");
            mapper.add(a);
            mapper.add(b);
            S dline {*which, nearest.projected_point};
            mapper.add(dline);

            mapper.map(a, "fill-opacity:0.5;fill:rgb(153,204,0);stroke:rgb(153,204,0);stroke-width:2");
            mapper.map(b, "fill-opacity:0.5;fill:rgb(204,153,0);stroke:rgb(202,153,0);stroke-width:2");
            mapper.map(dline, "stroke-dasharray:1,1;stroke:rgb(255,0,0);stroke-width:1");
        }
    }
}

// details for generating the rectangles
#include <boost/geometry/strategies/transform/matrix_transformers.hpp>
#include <random>
std::mt19937 prng { std::random_device{}() };

static auto rand(double b, double e) { 
    return std::uniform_real_distribution<double>(b, e)(prng);
}

R gen_rect() {
    B initial {{0, 0}, { rand(0.1, 1), rand(0.1, 1) } };
    R raw, rect; // todo rotate and stuff
    bg::assign(raw, initial);

    using namespace bg::strategy::transform;
    auto rot   = rand(-M_PI, +M_PI);
    auto scale = rand(1, 3);
    auto x     = rand(-5, 5),
         y     = rand(-5, 5);

    matrix_transformer<double, 2, 2> xfrm(
         scale* cos(rot), scale*sin(rot), x,
         scale*-sin(rot), scale*cos(rot), y,
                       0,              0, 1);

    bg::transform(raw, rect, xfrm);
    bg::correct(rect);
    return rect;
}

这会生成一些随机场景,例如:

enter image description here

enter image description here

enter image description here