From 6a27a2ae7662f8c5cc28398a30101e32c133b439 Mon Sep 17 00:00:00 2001 From: Hermann Kraus Date: Sun, 5 Aug 2012 11:28:01 +0200 Subject: [PATCH] Implement auto-upright. --- include/mapnik/text/placement_finder_ng.hpp | 3 +- include/mapnik/vertex_cache.hpp | 59 ++++++++++++++++++--- src/text/placement_finder_ng.cpp | 55 +++++++++++-------- src/text_properties.cpp | 1 - src/xml_tree.cpp | 1 + 5 files changed, 87 insertions(+), 32 deletions(-) diff --git a/include/mapnik/text/placement_finder_ng.hpp b/include/mapnik/text/placement_finder_ng.hpp index 5ab9f69ed..c250cc252 100644 --- a/include/mapnik/text/placement_finder_ng.hpp +++ b/include/mapnik/text/placement_finder_ng.hpp @@ -71,7 +71,8 @@ private: void init_alignment(); pixel_position alignment_offset() const; - bool single_line_placement(vertex_cache &pp, signed orientation); + bool single_line_placement(vertex_cache &pp, text_upright_e orientation); + static double normalize_angle(double angle); Feature const& feature_; DetectorType &detector_; box2d const& extent_; diff --git a/include/mapnik/vertex_cache.hpp b/include/mapnik/vertex_cache.hpp index 75f14b0db..cc886a418 100644 --- a/include/mapnik/vertex_cache.hpp +++ b/include/mapnik/vertex_cache.hpp @@ -71,10 +71,9 @@ public: pixel_position const& current_position() const { return current_position_; } - double angle(double width); + double angle(double width=0.); bool next_subpath(); - bool next_segment(); /** Moves all positions to a parallel line in the specified distance. */ void set_offset(double offset); @@ -86,12 +85,16 @@ public: * on a point on the path. */ bool forward(double length); + bool backward(double length); + bool move(double length); //Move works in both directions state save_state() const; void restore_state(state s); private: + bool next_segment(); + bool previous_segment(); pixel_position current_position_; pixel_position segment_starting_point_; std::vector subpaths_; @@ -159,21 +162,24 @@ vertex_cache::vertex_cache(T &path) double vertex_cache::angle(double width) { - if (width + position_in_segment_ < current_segment_->length) + /* IMPORTANT NOTE: See note about coordinate systems in placement_finder::find_point_placement() + * for imformation about why the y axis is inverted! */ + double tmp = width + position_in_segment_; + if ((tmp <= current_segment_->length) && (tmp >= 0)) { //Only calculate angle on request as it is expensive if (!angle_valid_) { - angle_ = atan2(current_segment_->pos.y - segment_starting_point_.y, + angle_ = atan2(-(current_segment_->pos.y - segment_starting_point_.y), current_segment_->pos.x - segment_starting_point_.x); } - return angle_; + return width >= 0 ? angle_ : angle_ + M_PI; } else { state s = save_state(); pixel_position const& old_pos = s.position(); - forward(width); - double angle = atan2(current_position_.y - old_pos.y, + move(width); + double angle = atan2(-(current_position_.y - old_pos.y), current_position_.x - old_pos.x); restore_state(s); return angle; @@ -205,8 +211,23 @@ bool vertex_cache::next_segment() segment_starting_point_ = current_segment_->pos; //Next segments starts at the end of the current one if (current_segment_ == current_subpath_->vector.end()) return false; current_segment_++; - if (current_segment_ == current_subpath_->vector.end()) return false; angle_valid_ = false; + if (current_segment_ == current_subpath_->vector.end()) return false; + return true; +} + +bool vertex_cache::previous_segment() +{ + if (current_segment_ == current_subpath_->vector.begin()) return false; + current_segment_--; + angle_valid_ = false; + if (current_segment_ == current_subpath_->vector.begin()) + { + //First segment is special + segment_starting_point_ = current_segment_->pos; + return true; + } + segment_starting_point_ = (current_segment_-1)->pos; return true; } @@ -220,19 +241,41 @@ bool vertex_cache::forward(double length) if (length < 0) { MAPNIK_LOG_ERROR(vertex_cache) << "vertex_cache::forward() called with negative argument!\n"; + return false; } + return move(length); +} + +bool vertex_cache::backward(double length) +{ + if (length < 0) + { + MAPNIK_LOG_ERROR(vertex_cache) << "vertex_cache::backward() called with negative argument!\n"; + return false; + } + return move(-length); +} + +bool vertex_cache::move(double length) +{ length += position_in_segment_; while (length >= current_segment_->length) { length -= current_segment_->length; if (!next_segment()) return false; //Skip all complete segments } + while (length < 0) + { + if (!previous_segment()) return false; + length += current_segment_->length; + } double factor = length / current_segment_->length; position_in_segment_ = length; current_position_ = segment_starting_point_ + (current_segment_->pos - segment_starting_point_) * factor; return true; } + vertex_cache::state vertex_cache::save_state() const { state s; diff --git a/src/text/placement_finder_ng.cpp b/src/text/placement_finder_ng.cpp index 57b755c56..25c7b9f45 100644 --- a/src/text/placement_finder_ng.cpp +++ b/src/text/placement_finder_ng.cpp @@ -301,7 +301,7 @@ bool placement_finder_ng::find_line_placements(T & path) do { vertex_cache::state s = pp.save_state(); - success = single_line_placement(pp, 0) || success; + success = single_line_placement(pp, info_->properties.upright) || success; pp.restore_state(s); } while (pp.forward(spacing)); } @@ -309,52 +309,54 @@ bool placement_finder_ng::find_line_placements(T & path) } -bool placement_finder_ng::single_line_placement(vertex_cache &pp, signed orientation) +bool placement_finder_ng::single_line_placement(vertex_cache &pp, text_upright_e orientation) { - std::cout << "single_line" << pp.current_position().x << ", " << pp.current_position().y << "\n"; + /* IMPORTANT NOTE: See note about coordinate systems in find_point_placement()! */ + text_upright_e real_orientation = orientation; + if (orientation == UPRIGHT_AUTO) + { + double angle = normalize_angle(pp.angle()); + real_orientation = (angle > 0.5*M_PI && angle < 1.5*M_PI) ? UPRIGHT_LEFT : UPRIGHT_RIGHT; + } + double sign = 1; + if (real_orientation == UPRIGHT_LEFT) + { + sign = -1; + pp.forward(layout_.width()); + } + double base_offset = alignment_offset().y + info_->properties.displacement.y; glyph_positions_ptr glyphs = boost::make_shared(); - /* IMPORTANT NOTE: - x and y are relative to the center of the text - coordinate system: - x: grows from left to right - y: grows from bottom to top (all values are negative!) - */ - double offset = base_offset + layout_.height(); + unsigned upside_down_glyph_count = 0; text_layout::const_iterator line_itr = layout_.begin(), line_end = layout_.end(); for (; line_itr != line_end; line_itr++) { double char_height = (*line_itr)->max_char_height(); offset -= (*line_itr)->height(); - // reset to begining of line position + pp.set_offset(offset); text_line::const_iterator glyph_itr = (*line_itr)->begin(), glyph_end = (*line_itr)->end(); for (; glyph_itr != glyph_end; glyph_itr++) { - double angle = pp.angle(glyph_itr->width); + double angle = normalize_angle(pp.angle(sign * glyph_itr->width)); double sina = sin(angle); double cosa = cos(angle); pixel_position pos = pp.current_position(); //Center the text on the line pos.y = -pos.y - char_height/2.0*cosa; - pos.x = pos.x - char_height/2.0*sina; + pos.x = pos.x + char_height/2.0*sina; -// if (orientation < 0) -// { -// // rotate in place -// render_x += cwidth*cosa - char_height*sina; -// render_y -= cwidth*sina + char_height*cosa; -// render_angle += M_PI; -// } + if (angle > M_PI/2. && angle < 1.5*M_PI) + upside_down_glyph_count++; - glyphs->push_back(*glyph_itr, pos, -angle); //TODO: Store cosa, sina instead + glyphs->push_back(*glyph_itr, pos, angle); //TODO: Store cosa, sina instead if (glyph_itr->width) { //Only advance if glyph is not part of a multiple glyph sequence - pp.forward(glyph_itr->width + glyph_itr->format->character_spacing); + pp.move(sign * (glyph_itr->width + glyph_itr->format->character_spacing)); } } } @@ -362,6 +364,15 @@ bool placement_finder_ng::single_line_placement(vertex_cache &pp, signed orienta return true; } +double placement_finder_ng::normalize_angle(double angle) +{ + while (angle >= 2*M_PI) + angle -= 2*M_PI; + while (angle < 0) + angle += 2*M_PI; + return angle; +} + /*********************************************************************************************/ diff --git a/src/text_properties.cpp b/src/text_properties.cpp index 21a506128..631d8ad12 100644 --- a/src/text_properties.cpp +++ b/src/text_properties.cpp @@ -86,7 +86,6 @@ static const char * text_upright_strings[] = { "auto", "left", "right", - "capitalize", "" }; IMPLEMENT_ENUM(text_upright_e, text_upright_strings) diff --git a/src/xml_tree.cpp b/src/xml_tree.cpp index e6b92567c..4e9d6569d 100644 --- a/src/xml_tree.cpp +++ b/src/xml_tree.cpp @@ -480,6 +480,7 @@ compile_get_opt_attr(label_placement_e); compile_get_opt_attr(vertical_alignment_e); compile_get_opt_attr(horizontal_alignment_e); compile_get_opt_attr(justify_alignment_e); +compile_get_opt_attr(text_upright_e); compile_get_opt_attr(expression_ptr); compile_get_attr(std::string); compile_get_attr(filter_mode_e);