diff --git a/include/mapnik/label_collision_detector.hpp b/include/mapnik/label_collision_detector.hpp index 6dce9ff2c..9d0f6b172 100644 --- a/include/mapnik/label_collision_detector.hpp +++ b/include/mapnik/label_collision_detector.hpp @@ -27,7 +27,7 @@ #include #include #include - +#include // icu #include @@ -131,11 +131,18 @@ class label_collision_detector4 : util::noncopyable public: struct label { - label(box2d const& b) : box(b), text() {} - label(box2d const& b, mapnik::value_unicode_string const& t) : box(b), text(t) {} + label(box2d const& b, double a) + : box(b), + text(), + angle(a) + {} + + label(box2d const& b, mapnik::value_unicode_string const& t, double a) + : box(b), text(t), angle(a) {} box2d box; mapnik::value_unicode_string text; + double angle = 0.0; }; private: @@ -181,10 +188,30 @@ public: return true; } + bool has_placement(box2d 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 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 const& box) + void insert(box2d const& box, double angle = 0.0) { - tree_.insert(label(box), box); + tree_.insert(label(box, angle), box); } - void insert(box2d const& box, mapnik::value_unicode_string const& text) + void insert(box2d 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() diff --git a/include/mapnik/quad_tree.hpp b/include/mapnik/quad_tree.hpp index d46faa28c..e3a7244f8 100644 --- a/include/mapnik/quad_tree.hpp +++ b/include/mapnik/quad_tree.hpp @@ -184,7 +184,7 @@ private: nodes_.push_back(std::make_unique(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; } } diff --git a/include/mapnik/rotated_rectangle_collision.hpp b/include/mapnik/rotated_rectangle_collision.hpp new file mode 100644 index 000000000..b42a6b0f6 --- /dev/null +++ b/include/mapnik/rotated_rectangle_collision.hpp @@ -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 +#include +#include +#include +#include + +namespace mapnik { + +template +using rotated_rectangle = std::array ,4>; + +template +rotated_rectangle rotate(mapnik::box2d const& box, double angle) +{ + rotated_rectangle 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 +box2d bounding_box(rotated_rectangle const& r) +{ + box2d 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 +bool intersects(rotated_rectangle const& r0, rotated_rectangle const& r1) +{ + for (rotated_rectangle 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::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::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 diff --git a/src/agg/process_debug_symbolizer.cpp b/src/agg/process_debug_symbolizer.cpp index c56add951..bcff95cb8 100644 --- a/src/agg/process_debug_symbolizer.cpp +++ b/src/agg/process_debug_symbolizer.cpp @@ -32,7 +32,8 @@ #include #include #include - +#include +#include // agg #include "agg_basics.h" #include "agg_rendering_buffer.h" @@ -52,6 +53,7 @@ void draw_rect(T &pixmap, box2d const& box) int x1 = static_cast(box.maxx()); int y0 = static_cast(box.miny()); int y1 = static_cast(box.maxy()); + unsigned color1 = 0xff0000ff; for (int x=x0; x const& box) } } +template +void draw_rotated_rect(T & pixmap, R & ras, box2d const& box, double angle) +{ + using color_type = agg::rgba8; + using order_type = agg::order_rgba; + using blender_type = agg::comp_op_adaptor_rgba_pre; // comp blender + using pixfmt_comp_type = agg::pixfmt_custom_blend_rgba; + using renderer_base = agg::renderer_base; + using renderer_type = agg::renderer_scanline_aa_solid; + + 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 struct apply_vertex_mode { @@ -208,8 +241,8 @@ struct render_ring_visitor { template void agg_renderer::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(sym, keys::mode, feature, common_.vars_, DEBUG_SYM_MODE_COLLISION); @@ -232,7 +265,8 @@ void agg_renderer::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) diff --git a/src/text/placement_finder.cpp b/src/text/placement_finder.cpp index c4e0866a5..ec5b42e42 100644 --- a/src/text/placement_finder.cpp +++ b/src/text/placement_finder.cpp @@ -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(); - std::vector > bboxes; + std::vector,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 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 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 &box, const value_unicode_string &repeat_key, bool line_placement) const +bool placement_finder::collision(box2d 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 &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 box, bool marker_unlocked, pixel_position const& marker_displacement) { marker_ = m; diff --git a/src/text/text_layout.cpp b/src/text/text_layout.cpp index ff37d28e2..3cfb225cf 100644 --- a/src/text/text_layout.cpp +++ b/src/text/text_layout.cpp @@ -40,7 +40,7 @@ namespace mapnik static void rotated_box2d(box2d & 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.;