rotated rectangle collision impl

store rotation angle along bounding box
add new `has_placement` - checks for rotated boxes collisions
This commit is contained in:
artemp 2015-06-29 13:03:57 +02:00
parent 7158c19f80
commit 3e74f68b26
6 changed files with 191 additions and 23 deletions

View file

@ -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()

View file

@ -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;
}
}

View 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

View file

@ -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)

View file

@ -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;

View file

@ -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.;