Implement auto-upright.

This commit is contained in:
Hermann Kraus 2012-08-05 11:28:01 +02:00
parent 5115658ecc
commit 6a27a2ae76
5 changed files with 87 additions and 32 deletions

View file

@ -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<double> const& extent_;

View file

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

View file

@ -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<glyph_positions>();
/* 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;
}
/*********************************************************************************************/

View file

@ -86,7 +86,6 @@ static const char * text_upright_strings[] = {
"auto",
"left",
"right",
"capitalize",
""
};
IMPLEMENT_ENUM(text_upright_e, text_upright_strings)

View file

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