rotated rectangle collision impl
store rotation angle along bounding box add new `has_placement` - checks for rotated boxes collisions
This commit is contained in:
parent
7158c19f80
commit
3e74f68b26
6 changed files with 191 additions and 23 deletions
|
@ -27,7 +27,7 @@
|
|||
#include <mapnik/quad_tree.hpp>
|
||||
#include <mapnik/util/noncopyable.hpp>
|
||||
#include <mapnik/value_types.hpp>
|
||||
|
||||
#include <mapnik/rotated_rectangle_collision.hpp>
|
||||
// icu
|
||||
#include <unicode/unistr.h>
|
||||
|
||||
|
@ -131,11 +131,18 @@ class label_collision_detector4 : util::noncopyable
|
|||
public:
|
||||
struct label
|
||||
{
|
||||
label(box2d<double> const& b) : box(b), text() {}
|
||||
label(box2d<double> const& b, mapnik::value_unicode_string const& t) : box(b), text(t) {}
|
||||
label(box2d<double> const& b, double a)
|
||||
: box(b),
|
||||
text(),
|
||||
angle(a)
|
||||
{}
|
||||
|
||||
label(box2d<double> const& b, mapnik::value_unicode_string const& t, double a)
|
||||
: box(b), text(t), angle(a) {}
|
||||
|
||||
box2d<double> box;
|
||||
mapnik::value_unicode_string text;
|
||||
double angle = 0.0;
|
||||
};
|
||||
|
||||
private:
|
||||
|
@ -181,10 +188,30 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
bool has_placement(box2d<double> const& box, double margin, double angle)
|
||||
{
|
||||
tree_t::query_iterator itr = tree_.query_in_box(box);
|
||||
tree_t::query_iterator end = tree_.query_end();
|
||||
|
||||
auto rect_a = rotate(box, angle);
|
||||
|
||||
for (;itr != end; ++itr)
|
||||
{
|
||||
auto const& lab = itr->get();
|
||||
auto rect_b = rotate(lab.box, lab.angle);
|
||||
if (intersects(rect_a, rect_b))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool has_placement(box2d<double> const& box, double margin, mapnik::value_unicode_string const& text, double repeat_distance)
|
||||
{
|
||||
// Don't bother with any of the repeat checking unless the repeat distance is greater than the margin
|
||||
if (repeat_distance <= margin) {
|
||||
if (repeat_distance <= margin)
|
||||
{
|
||||
return has_placement(box, margin);
|
||||
}
|
||||
|
||||
|
@ -210,14 +237,14 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
void insert(box2d<double> const& box)
|
||||
void insert(box2d<double> const& box, double angle = 0.0)
|
||||
{
|
||||
tree_.insert(label(box), box);
|
||||
tree_.insert(label(box, angle), box);
|
||||
}
|
||||
|
||||
void insert(box2d<double> const& box, mapnik::value_unicode_string const& text)
|
||||
void insert(box2d<double> const& box, mapnik::value_unicode_string const& text, double angle = 0.0)
|
||||
{
|
||||
tree_.insert(label(box, text), box);
|
||||
tree_.insert(label(box, text, angle), box);
|
||||
}
|
||||
|
||||
void clear()
|
||||
|
|
|
@ -184,7 +184,7 @@ private:
|
|||
nodes_.push_back(std::make_unique<node>(ext[i]));
|
||||
n->children_[i]=nodes_.back().get();
|
||||
}
|
||||
do_insert_data(data,box,n->children_[i],depth);
|
||||
do_insert_data(data,box, n->children_[i],depth);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
105
include/mapnik/rotated_rectangle_collision.hpp
Normal file
105
include/mapnik/rotated_rectangle_collision.hpp
Normal file
|
@ -0,0 +1,105 @@
|
|||
/*****************************************************************************
|
||||
*
|
||||
* This file is part of Mapnik (c++ mapping toolkit)
|
||||
*
|
||||
* Copyright (C) 2015 Artem Pavlenko
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef MAPNIK_ROTATED_RECTANGLE_COLLISION_HPP
|
||||
#define MAPNIK_ROTATED_RECTANGLE_COLLISION_HPP
|
||||
|
||||
#include <mapnik/box2d.hpp>
|
||||
#include <mapnik/geometry.hpp>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
|
||||
namespace mapnik {
|
||||
|
||||
template <typename T>
|
||||
using rotated_rectangle = std::array<geometry::point<T> ,4>;
|
||||
|
||||
template <typename T>
|
||||
rotated_rectangle<T> rotate(mapnik::box2d<T> const& box, double angle)
|
||||
{
|
||||
rotated_rectangle<T> rect;
|
||||
auto c = box.center();
|
||||
auto lox = box.minx() - c.x;
|
||||
auto loy = box.miny() - c.y;
|
||||
auto hix = box.maxx() - c.x;
|
||||
auto hiy = box.maxy() - c.y;
|
||||
double cos_a = cos(angle);
|
||||
double sin_a = sin(angle);
|
||||
|
||||
rect[0].x = c.x + lox * cos_a - loy * sin_a;
|
||||
rect[0].y = c.y + lox * sin_a + loy * cos_a;
|
||||
rect[1].x = c.x + lox * cos_a - hiy * sin_a;
|
||||
rect[1].y = c.y + lox * sin_a + hiy * cos_a;
|
||||
rect[2].x = c.x + hix * cos_a - hiy * sin_a;
|
||||
rect[2].y = c.y + hix * sin_a + hiy * cos_a;
|
||||
rect[3].x = c.x + hix * cos_a - loy * sin_a;
|
||||
rect[3].y = c.y + hix * sin_a + loy * cos_a;
|
||||
|
||||
return rect;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
box2d<T> bounding_box(rotated_rectangle<T> const& r)
|
||||
{
|
||||
box2d<T> box(r[0].x, r[0].x, r[0].y, r[0].y);
|
||||
box.expand_to_include(r[1].x, r[1].y);
|
||||
box.expand_to_include(r[2].x, r[2].y);
|
||||
box.expand_to_include(r[3].x, r[3].y);
|
||||
return box;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool intersects(rotated_rectangle<T> const& r0, rotated_rectangle<T> const& r1)
|
||||
{
|
||||
for (rotated_rectangle<T> const& r : { std::cref(r0), std::cref(r1) })
|
||||
{
|
||||
for (std::size_t i = 0; i < 2; ++i)
|
||||
{
|
||||
auto nx = r[i + 1].y - r[i].y;
|
||||
auto ny = r[i].x - r[i + 1].x;
|
||||
auto min_a = std::numeric_limits<T>::max();
|
||||
auto max_a = -min_a;
|
||||
for(std::size_t j = 0; j < 4; ++j)
|
||||
{
|
||||
auto projected = nx * r0[j].x + ny * r0[j].y;
|
||||
if( projected < min_a ) min_a = projected;
|
||||
if( projected > max_a ) max_a = projected;
|
||||
}
|
||||
|
||||
auto min_b = std::numeric_limits<T>::max();
|
||||
auto max_b = -min_b;
|
||||
for(std::size_t j = 0; j < 4; ++j)
|
||||
{
|
||||
auto projected = nx * r1[j].x + ny * r1[j].y;
|
||||
if( projected < min_b ) min_b = projected;
|
||||
if( projected > max_b ) max_b = projected;
|
||||
}
|
||||
if ( max_a < min_b || max_b < min_a ) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif //MAPNIK_ROTATED_RECTANGLE_COLLISION_HPP
|
|
@ -32,7 +32,8 @@
|
|||
#include <mapnik/transform_path_adapter.hpp>
|
||||
#include <mapnik/agg_helpers.hpp>
|
||||
#include <mapnik/util/is_clockwise.hpp>
|
||||
|
||||
#include <mapnik/rotated_rectangle_collision.hpp>
|
||||
#include <mapnik/path.hpp>
|
||||
// agg
|
||||
#include "agg_basics.h"
|
||||
#include "agg_rendering_buffer.h"
|
||||
|
@ -52,6 +53,7 @@ void draw_rect(T &pixmap, box2d<double> const& box)
|
|||
int x1 = static_cast<int>(box.maxx());
|
||||
int y0 = static_cast<int>(box.miny());
|
||||
int y1 = static_cast<int>(box.maxy());
|
||||
|
||||
unsigned color1 = 0xff0000ff;
|
||||
for (int x=x0; x<x1; x++)
|
||||
{
|
||||
|
@ -65,6 +67,37 @@ void draw_rect(T &pixmap, box2d<double> const& box)
|
|||
}
|
||||
}
|
||||
|
||||
template <typename T, typename R>
|
||||
void draw_rotated_rect(T & pixmap, R & ras, box2d<double> const& box, double angle)
|
||||
{
|
||||
using color_type = agg::rgba8;
|
||||
using order_type = agg::order_rgba;
|
||||
using blender_type = agg::comp_op_adaptor_rgba_pre<color_type, order_type>; // comp blender
|
||||
using pixfmt_comp_type = agg::pixfmt_custom_blend_rgba<blender_type, agg::rendering_buffer>;
|
||||
using renderer_base = agg::renderer_base<pixfmt_comp_type>;
|
||||
using renderer_type = agg::renderer_scanline_aa_solid<renderer_base>;
|
||||
|
||||
agg::rendering_buffer buf(pixmap.bytes(),pixmap.width(),pixmap.height(), pixmap.row_size());
|
||||
pixfmt_comp_type pixf(buf);
|
||||
pixf.comp_op(agg::comp_op_src_over);
|
||||
renderer_base renb(pixf);
|
||||
renderer_type ren(renb);
|
||||
ren.color(agg::rgba8_pre(0, 255, 0, 64));
|
||||
agg::scanline_u8 sl;
|
||||
ras.filling_rule(agg::fill_non_zero);
|
||||
auto rotated_box = rotate(box, angle);
|
||||
path_type p(path_type::LineString);
|
||||
p.move_to(rotated_box[0].x, rotated_box[0].y);
|
||||
p.line_to(rotated_box[1].x, rotated_box[1].y);
|
||||
p.line_to(rotated_box[2].x, rotated_box[2].y);
|
||||
p.line_to(rotated_box[3].x, rotated_box[3].y);
|
||||
p.line_to(rotated_box[0].x, rotated_box[0].y);
|
||||
vertex_adapter va(p);
|
||||
ras.add_path(va);
|
||||
agg::render_scanlines(ras, sl, ren);
|
||||
ras.reset();
|
||||
|
||||
}
|
||||
template <typename Pixmap>
|
||||
struct apply_vertex_mode
|
||||
{
|
||||
|
@ -208,8 +241,8 @@ struct render_ring_visitor {
|
|||
|
||||
template <typename T0, typename T1>
|
||||
void agg_renderer<T0,T1>::process(debug_symbolizer const& sym,
|
||||
mapnik::feature_impl & feature,
|
||||
proj_transform const& prj_trans)
|
||||
mapnik::feature_impl & feature,
|
||||
proj_transform const& prj_trans)
|
||||
{
|
||||
|
||||
debug_symbolizer_mode_enum mode = get<debug_symbolizer_mode_enum>(sym, keys::mode, feature, common_.vars_, DEBUG_SYM_MODE_COLLISION);
|
||||
|
@ -232,7 +265,8 @@ void agg_renderer<T0,T1>::process(debug_symbolizer const& sym,
|
|||
{
|
||||
for (auto const& n : *common_.detector_)
|
||||
{
|
||||
draw_rect(pixmap_, n.get().box);
|
||||
auto const& label = n.get();
|
||||
draw_rotated_rect(pixmap_, *ras_ptr, label.box, label.angle);
|
||||
}
|
||||
}
|
||||
else if (mode == DEBUG_SYM_MODE_VERTEX)
|
||||
|
|
|
@ -119,17 +119,17 @@ text_upright_e placement_finder::simplify_upright(text_upright_e upright, double
|
|||
bool placement_finder::find_point_placement(pixel_position const& pos)
|
||||
{
|
||||
glyph_positions_ptr glyphs = std::make_shared<glyph_positions>();
|
||||
std::vector<box2d<double> > bboxes;
|
||||
std::vector<std::tuple<box2d<double>,double> > labels;
|
||||
|
||||
glyphs->reserve(layouts_.glyphs_count());
|
||||
bboxes.reserve(layouts_.size());
|
||||
labels.reserve(layouts_.size());
|
||||
|
||||
bool base_point_set = false;
|
||||
for (auto const& layout_ptr : layouts_)
|
||||
{
|
||||
text_layout const& layout = *layout_ptr;
|
||||
rotation const& orientation = layout.orientation();
|
||||
|
||||
double angle = -asin(orientation.sin);
|
||||
// Find text origin.
|
||||
pixel_position layout_center = pos + layout.displacement();
|
||||
|
||||
|
@ -142,10 +142,11 @@ bool placement_finder::find_point_placement(pixel_position const& pos)
|
|||
box2d<double> bbox = layout.bounds();
|
||||
bbox.re_center(layout_center.x, layout_center.y);
|
||||
|
||||
/* For point placements it is faster to just check the bounding box. */
|
||||
if (collision(bbox, layouts_.text(), false)) return false;
|
||||
// For point placements it is faster to just check the bounding box. */
|
||||
//if (collision(bbox, layouts_.text(), false)) return false;
|
||||
|
||||
if (layout.num_lines()) bboxes.push_back(std::move(bbox));
|
||||
if (!detector_.has_placement(bbox, 0, angle)) return false;
|
||||
if (layout.num_lines()) labels.push_back(std::make_tuple(bbox,angle));
|
||||
|
||||
pixel_position layout_offset = layout_center - glyphs->get_base_point();
|
||||
layout_offset.y = -layout_offset.y;
|
||||
|
@ -182,9 +183,9 @@ bool placement_finder::find_point_placement(pixel_position const& pos)
|
|||
// add_marker first checks for collision and then updates the detector.
|
||||
if (has_marker_ && !add_marker(glyphs, pos)) return false;
|
||||
|
||||
for (box2d<double> const& bbox : bboxes)
|
||||
for (auto const& label : labels)
|
||||
{
|
||||
detector_.insert(bbox, layouts_.text());
|
||||
detector_.insert(std::get<0>(label), layouts_.text(), std::get<1>(label));
|
||||
}
|
||||
placements_.push_back(glyphs);
|
||||
|
||||
|
@ -355,7 +356,7 @@ double placement_finder::get_spacing(double path_length, double layout_width) co
|
|||
return path_length / num_labels;
|
||||
}
|
||||
|
||||
bool placement_finder::collision(const box2d<double> &box, const value_unicode_string &repeat_key, bool line_placement) const
|
||||
bool placement_finder::collision(box2d<double> const& box, value_unicode_string const& repeat_key, bool line_placement) const
|
||||
{
|
||||
double margin, repeat_distance;
|
||||
if (line_placement)
|
||||
|
@ -381,6 +382,7 @@ bool placement_finder::collision(const box2d<double> &box, const value_unicode_s
|
|||
(repeat_key.length() > 0 && !detector_.has_placement(box, margin, repeat_key, repeat_distance))));
|
||||
}
|
||||
|
||||
|
||||
void placement_finder::set_marker(marker_info_ptr m, box2d<double> box, bool marker_unlocked, pixel_position const& marker_displacement)
|
||||
{
|
||||
marker_ = m;
|
||||
|
|
|
@ -40,7 +40,7 @@ namespace mapnik
|
|||
static void rotated_box2d(box2d<double> & box, rotation const& rot, pixel_position const& center, double width, double height)
|
||||
{
|
||||
double half_width, half_height;
|
||||
if (rot.sin == 0 && rot.cos == 1.)
|
||||
if (true)//rot.sin == 0 && rot.cos == 1.)
|
||||
{
|
||||
half_width = width / 2.;
|
||||
half_height = height / 2.;
|
||||
|
|
Loading…
Reference in a new issue