diff --git a/src/placement_finder.cpp b/src/placement_finder.cpp index 853fb04fc..9f637cf35 100644 --- a/src/placement_finder.cpp +++ b/src/placement_finder.cpp @@ -45,641 +45,638 @@ namespace mapnik { - //For shields - placement::placement(string_info *info_, - CoordTransform *ctrans_, - const proj_transform *proj_trans_, - geometry_ptr geom_, - std::pair dimensions_) - : info(info_), - ctrans(ctrans_), - proj_trans(proj_trans_), - geom(geom_), - displacement_(0,0), - label_placement(point_placement), - dimensions(dimensions_), - has_dimensions(true), - shape_path(*ctrans_, *geom_, *proj_trans_), - total_distance_(-1.0), - wrap_width(0), - text_ratio(0), - label_spacing(0), - label_position_tolerance(0), - force_odd_labels(false), - max_char_angle_delta(0.0), avoid_edges(false) - { - } + //For shields + placement::placement(string_info *info_, + CoordTransform *ctrans_, + const proj_transform *proj_trans_, + geometry_ptr geom_, + std::pair dimensions_) + : info(info_), + ctrans(ctrans_), + proj_trans(proj_trans_), + geom(geom_), + displacement_(0,0), + label_placement(point_placement), + dimensions(dimensions_), + has_dimensions(true), + shape_path(*ctrans_, *geom_, *proj_trans_), + total_distance_(-1.0), + wrap_width(0), + text_ratio(0), + label_spacing(0), + label_position_tolerance(0), + force_odd_labels(false), + max_char_angle_delta(0.0), avoid_edges(false) + { + } - //For text - placement::placement(string_info *info_, - CoordTransform *ctrans_, - const proj_transform *proj_trans_, - geometry_ptr geom_, - position const& displacement, - label_placement_e placement_) - : info(info_), - ctrans(ctrans_), - proj_trans(proj_trans_), - geom(geom_), - displacement_(displacement), - label_placement(placement_), - has_dimensions(false), - shape_path(*ctrans_, *geom_, *proj_trans_), - total_distance_(-1.0), - wrap_width(0), - text_ratio(0), - label_spacing(0), - label_position_tolerance(0), - force_odd_labels(false), - max_char_angle_delta(0.0), - avoid_edges(false) - { - } + //For text + placement::placement(string_info *info_, + CoordTransform *ctrans_, + const proj_transform *proj_trans_, + geometry_ptr geom_, + position const& displacement, + label_placement_e placement_) + : info(info_), + ctrans(ctrans_), + proj_trans(proj_trans_), + geom(geom_), + displacement_(displacement), + label_placement(placement_), + has_dimensions(false), + shape_path(*ctrans_, *geom_, *proj_trans_), + total_distance_(-1.0), + wrap_width(0), + text_ratio(0), + label_spacing(0), + label_position_tolerance(0), + force_odd_labels(false), + max_char_angle_delta(0.0), + avoid_edges(false) + { + } - placement::~placement() - { - } + placement::~placement() + { + } - unsigned placement::path_size() const - { - return geom->num_points(); - } + unsigned placement::path_size() const + { + return geom->num_points(); + } - std::pair placement::get_position_at_distance(double target_distance) - { - double old_x, old_y, new_x, new_y; - double x, y; - x = y = 0.0; + std::pair placement::get_position_at_distance(double target_distance) + { + double old_x, old_y, new_x, new_y; + double x, y; + x = y = 0.0; - double distance = 0.0; + double distance = 0.0; - shape_path.rewind(0); - shape_path.vertex(&new_x,&new_y); - unsigned num_points = geom->num_points(); - for (unsigned i = 1; i < num_points; ++i) - { - double dx, dy; + shape_path.rewind(0); + shape_path.vertex(&new_x,&new_y); + unsigned num_points = geom->num_points(); + for (unsigned i = 1; i < num_points; ++i) + { + double dx, dy; - old_x = new_x; - old_y = new_y; + old_x = new_x; + old_y = new_y; - shape_path.vertex(&new_x,&new_y); + shape_path.vertex(&new_x,&new_y); - dx = new_x - old_x; - dy = new_y - old_y; + dx = new_x - old_x; + dy = new_y - old_y; - double segment_length = sqrt(dx*dx + dy*dy); + double segment_length = sqrt(dx*dx + dy*dy); - distance += segment_length; - if (distance > target_distance) - { - x = new_x - dx*(distance - target_distance)/segment_length; - y = new_y - dy*(distance - target_distance)/segment_length; + distance += segment_length; + if (distance > target_distance) + { + x = new_x - dx*(distance - target_distance)/segment_length; + y = new_y - dy*(distance - target_distance)/segment_length; - break; - } - } + break; + } + } - return std::pair(x, y); - } + return std::pair(x, y); + } - double placement::get_total_distance() - { - if (total_distance_ < 0.0) - { - double old_x, old_y, new_x, new_y; + double placement::get_total_distance() + { + if (total_distance_ < 0.0) + { + double old_x, old_y, new_x, new_y; - shape_path.rewind(0); + shape_path.rewind(0); - shape_path.vertex(&old_x,&old_y); + shape_path.vertex(&old_x,&old_y); - total_distance_ = 0.0; + total_distance_ = 0.0; - unsigned num_points = geom->num_points(); - for (unsigned i = 1; i < num_points ; ++i) - { - double dx, dy; - shape_path.vertex(&new_x,&new_y); - - dx = new_x - old_x; - dy = new_y - old_y; - - total_distance_ += sqrt(dx*dx + dy*dy); - - old_x = new_x; - old_y = new_y; - } - } - - return total_distance_; - } - - void placement::clear_envelopes() - { - while (!envelopes.empty()) - envelopes.pop(); - } - - - - placement_finder::placement_finder(Envelope e, unsigned buffer) - : dimensions_(e), - detector_(Envelope(e.minx() - buffer, e.miny() - buffer, e.maxx() + buffer, e.maxy() + buffer)) - { - } - - - bool placement_finder::find_placements(placement *p) - { - if (p->label_placement == point_placement) - { - return find_placement_horizontal(p); - } - else if (p->label_placement == line_placement) - { - return find_placement_follow(p); - } - - return false; - } - - bool placement_finder::find_placement_follow(placement *p) - { - std::pair string_dimensions = p->info->get_dimensions(); - double string_width = string_dimensions.first; - // double string_height = string_dimensions.second; - - // for (unsigned int ii = 0; ii < p->info->num_characters(); ++ii) - // std::clog << static_cast (p->info->at(ii).character); - // std::clog << std::endl; - - double distance = p->get_total_distance(); - - if (string_width > distance) - { - //std::clog << "String longer than segment, bailing" << std::endl; - return false; - } - - - int num_labels = 0; - if (p->label_spacing) - num_labels = static_cast (floor(distance / (p->label_spacing + string_width))); - if (p->force_odd_labels && num_labels%2 == 0) - num_labels--; - if (num_labels <= 0) - num_labels = 1; - - double ideal_spacing = distance/num_labels; - std::vector ideal_label_distances; - - double middle = (distance / 2.0) - (string_width/2.0); //try draw text centered - - if (num_labels % 2) //odd amount of labels - { - for (int a = 0; a < (num_labels+1)/2; a++) - { - ideal_label_distances.push_back(middle - (a*ideal_spacing)); - - if (a != 0) - ideal_label_distances.push_back(middle + (a*ideal_spacing)); - } - } - else //even amount of labels - { - for (int a = 0; a < num_labels/2; a++) - { - ideal_label_distances.push_back(middle - (ideal_spacing/2.0) - (a*ideal_spacing)); - ideal_label_distances.push_back(middle + (ideal_spacing/2.0) + (a*ideal_spacing)); - } - } - - double delta; - double tolerance; - - if (p->label_position_tolerance > 0) - { - tolerance = p->label_position_tolerance; - delta = std::max ( 1.0, p->label_position_tolerance/100.0); - } - else - { - tolerance = ideal_spacing/2.0; - delta = ideal_spacing/100.0; - } - - - bool FoundPlacement = false; - for (std::vector::const_iterator itr = ideal_label_distances.begin(); itr < ideal_label_distances.end(); ++itr) - { - //std::clog << "Trying to find txt placement at distance: " << *itr << std::endl; - for (double i = 0; i < tolerance; i += delta) - { - p->clear_envelopes(); - - // check position +- delta for valid placement - if ( build_path_follow(p, *itr + i) ) { - update_detector(p); - FoundPlacement = true; - break; - } - - p->clear_envelopes(); - if ( build_path_follow(p, *itr - i) ) { - update_detector(p); - FoundPlacement = true; - break; - } - } - } - - // if (FoundPlacement) - // std::clog << "Found Placement" << string_width << " " << distance << std::endl; - - return FoundPlacement; - } - - bool placement_finder::find_placement_horizontal(placement *p) - { - if (p->path_size() == 1) // point geometry - { - if ( build_path_horizontal(p, 0) ) - { - update_detector(p); - return true; - } - return false; - } - - double distance = p->get_total_distance(); - //~ double delta = string_width/distance; - double delta = distance/100.0; - - for (double i = 0; i < distance/2.0; i += delta) - { - p->clear_envelopes(); - - if ( build_path_horizontal(p, distance/2.0 + i) ) { - update_detector(p); - return true; - } - - p->clear_envelopes(); - - if ( build_path_horizontal(p, distance/2.0 - i) ) { - update_detector(p); - return true; - } - } - return false; - } - - void placement_finder::update_detector(placement *p) - { - while (!p->envelopes.empty()) - { - Envelope e = p->envelopes.front(); - - detector_.insert(e); - - p->envelopes.pop(); - } - } - - bool placement_finder::build_path_follow(placement *p, double target_distance) - { - double new_x, new_y, old_x, old_y; - unsigned cur_node = 0; - double next_char_x = 0; - double next_char_y = 0; - - double angle = 0.0; - int orientation = 0; - - p->current_placement.path.clear(); - - double x, y; - x = y = 0.0; - - double distance = 0.0; - - std::pair string_dimensions = p->info->get_dimensions(); - // double string_width = string_dimensions.first; - double string_height = string_dimensions.second; - - // find the segment that our text should start on - p->shape_path.rewind(0); - p->shape_path.vertex(&new_x,&new_y); - old_x = new_x; - old_y = new_y; - unsigned num_points = p->geom->num_points(); - for (unsigned i = 1; i < num_points; ++i) - { + unsigned num_points = geom->num_points(); + for (unsigned i = 1; i < num_points ; ++i) + { double dx, dy; - - cur_node++; - - old_x = new_x; - old_y = new_y; - - p->shape_path.vertex(&new_x,&new_y); - + shape_path.vertex(&new_x,&new_y); + dx = new_x - old_x; dy = new_y - old_y; - - double segment_length = sqrt(dx*dx + dy*dy); - - distance += segment_length; - if (distance > target_distance) - { - // this segment is greater that the target starting distance so start here - p->current_placement.starting_x = new_x - dx*(distance - target_distance)/segment_length; - p->current_placement.starting_y = new_y - dy*(distance - target_distance)/segment_length; + + total_distance_ += sqrt(dx*dx + dy*dy); + + old_x = new_x; + old_y = new_y; + } + } - // angle text starts at and orientation - angle = atan2(-dy, dx); - orientation = fabs(angle) > M_PI/2 ? -1 : 1; + return total_distance_; + } + + void placement::clear_envelopes() + { + while (!envelopes.empty()) + envelopes.pop(); + } + + + + placement_finder::placement_finder(Envelope e, unsigned buffer) + : dimensions_(e), + detector_(Envelope(e.minx() - buffer, e.miny() - buffer, e.maxx() + buffer, e.maxy() + buffer)) + { + } - distance -= target_distance; - - break; - } - } - - // now find the placement of each character starting from our initial segment - // determined above - double last_angle = angle; - for (unsigned i = 0; i < p->info->num_characters(); i++) - { - character_info ci; - unsigned c; - // grab the next character according to the orientation - ci = orientation > 0 ? p->info->at(i) : p->info->at(p->info->num_characters() - i - 1); - c = ci.character; + bool placement_finder::find_placements(placement *p) + { + if (p->label_placement == point_placement) + { + return find_placement_horizontal(p); + } + else if (p->label_placement == line_placement) + { + return find_placement_follow(p); + } - double angle_delta = 0; + return false; + } - // if the distance remaining in this segment is less than the character width - // move to the next segment - if (distance <= ci.width) - { - last_angle = angle; - while (distance <= ci.width) - { - double dx, dy; - - cur_node++; - - if (cur_node >= p->geom->num_points()) { - return false; - } - - old_x = new_x; - old_y = new_y; - - p->shape_path.vertex(&new_x,&new_y); - - dx = new_x - old_x; - dy = new_y - old_y; - - angle = atan2(-dy, dx ); - distance += sqrt(dx*dx+dy*dy); - } - // since our rendering angle has changed then check against our - // max allowable angle change. - angle_delta = last_angle - angle; - // normalise between -180 and 180 - while (angle_delta > M_PI) - angle_delta -= 2*M_PI; - while (angle_delta < -M_PI) - angle_delta += 2*M_PI; - if (p->max_char_angle_delta > 0 && fabs(angle_delta) > p->max_char_angle_delta*(M_PI/180)) - { - return false; - } + bool placement_finder::find_placement_follow(placement *p) + { + std::pair string_dimensions = p->info->get_dimensions(); + double string_width = string_dimensions.first; + + double distance = p->get_total_distance(); + + if (string_width > distance) + { + return false; + } + + int num_labels = 0; + if (p->label_spacing) + num_labels = static_cast (floor(distance / (p->label_spacing + string_width))); + if (p->force_odd_labels && num_labels%2 == 0) + num_labels--; + if (num_labels <= 0) + num_labels = 1; + + double ideal_spacing = distance/num_labels; + std::vector ideal_label_distances; + + double middle = (distance / 2.0) - (string_width/2.0); //try draw text centered + + if (num_labels % 2) //odd amount of labels + { + for (int a = 0; a < (num_labels+1)/2; a++) + { + ideal_label_distances.push_back(middle - (a*ideal_spacing)); + + if (a != 0) + ideal_label_distances.push_back(middle + (a*ideal_spacing)); + } + } + else //even amount of labels + { + for (int a = 0; a < num_labels/2; a++) + { + ideal_label_distances.push_back(middle - (ideal_spacing/2.0) - (a*ideal_spacing)); + ideal_label_distances.push_back(middle + (ideal_spacing/2.0) + (a*ideal_spacing)); + } + } + + double delta; + double tolerance; + + if (p->label_position_tolerance > 0) + { + tolerance = p->label_position_tolerance; + delta = std::max ( 1.0, p->label_position_tolerance/100.0); + } + else + { + tolerance = ideal_spacing/2.0; + delta = ideal_spacing/100.0; + } + + + bool FoundPlacement = false; + std::vector::const_iterator itr = ideal_label_distances.begin(); + std::vector::const_iterator end = ideal_label_distances.end(); + for (; itr != end; ++itr) + { + //std::clog << "Trying to find txt placement at distance: " << *itr << std::endl; + for (double i = 0; i < tolerance; i += delta) + { + p->clear_envelopes(); + + // check position +- delta for valid placement + if ( build_path_follow(p, *itr + i) ) { + update_detector(p); + FoundPlacement = true; + break; } + + p->clear_envelopes(); + if ( build_path_follow(p, *itr - i) ) { + update_detector(p); + FoundPlacement = true; + break; + } + } + } + + // if (FoundPlacement) + // std::clog << "Found Placement" << string_width << " " << distance << std::endl; + + return FoundPlacement; + } + + bool placement_finder::find_placement_horizontal(placement *p) + { + if (p->path_size() == 1) // point geometry + { + if ( build_path_horizontal(p, 0) ) + { + update_detector(p); + return true; + } + return false; + } + + double distance = p->get_total_distance(); + //~ double delta = string_width/distance; + double delta = distance/100.0; + + for (double i = 0; i < distance/2.0; i += delta) + { + p->clear_envelopes(); + + if ( build_path_horizontal(p, distance/2.0 + i) ) { + update_detector(p); + return true; + } + + p->clear_envelopes(); + + if ( build_path_horizontal(p, distance/2.0 - i) ) { + update_detector(p); + return true; + } + } + return false; + } + + void placement_finder::update_detector(placement *p) + { + while (!p->envelopes.empty()) + { + Envelope e = p->envelopes.front(); + + detector_.insert(e); + + p->envelopes.pop(); + } + } + + bool placement_finder::build_path_follow(placement *p, double target_distance) + { + double new_x, new_y, old_x, old_y; + unsigned cur_node = 0; + double next_char_x = 0; + double next_char_y = 0; + + double angle = 0.0; + int orientation = 0; + + p->current_placement.path.clear(); + + double x, y; + x = y = 0.0; + + double distance = 0.0; + + std::pair string_dimensions = p->info->get_dimensions(); + // double string_width = string_dimensions.first; + double string_height = string_dimensions.second; + + // find the segment that our text should start on + p->shape_path.rewind(0); + p->shape_path.vertex(&new_x,&new_y); + old_x = new_x; + old_y = new_y; + unsigned num_points = p->geom->num_points(); + for (unsigned i = 1; i < num_points; ++i) + { + double dx, dy; + + cur_node++; + + old_x = new_x; + old_y = new_y; + + p->shape_path.vertex(&new_x,&new_y); + + dx = new_x - old_x; + dy = new_y - old_y; + + double segment_length = sqrt(dx*dx + dy*dy); + + distance += segment_length; + if (distance > target_distance) + { + // this segment is greater that the target starting distance so start here + p->current_placement.starting_x = new_x - dx*(distance - target_distance)/segment_length; + p->current_placement.starting_y = new_y - dy*(distance - target_distance)/segment_length; + + // angle text starts at and orientation + angle = atan2(-dy, dx); + orientation = fabs(angle) > M_PI/2 ? -1 : 1; + + distance -= target_distance; + + break; + } + } + + // now find the placement of each character starting from our initial segment + // determined above + double last_angle = angle; + for (unsigned i = 0; i < p->info->num_characters(); i++) + { + character_info ci; + unsigned c; + + // grab the next character according to the orientation + ci = orientation > 0 ? p->info->at(i) : p->info->at(p->info->num_characters() - i - 1); + c = ci.character; + + double angle_delta = 0; + + // if the distance remaining in this segment is less than the character width + // move to the next segment + if (distance <= ci.width) + { + last_angle = angle; + while (distance <= ci.width) + { + double dx, dy; + + cur_node++; + + if (cur_node >= p->geom->num_points()) { + return false; + } + + old_x = new_x; + old_y = new_y; + + p->shape_path.vertex(&new_x,&new_y); + + dx = new_x - old_x; + dy = new_y - old_y; + + angle = atan2(-dy, dx ); + distance += sqrt(dx*dx+dy*dy); + } + // since our rendering angle has changed then check against our + // max allowable angle change. + angle_delta = last_angle - angle; + // normalise between -180 and 180 + while (angle_delta > M_PI) + angle_delta -= 2*M_PI; + while (angle_delta < -M_PI) + angle_delta += 2*M_PI; + if (p->max_char_angle_delta > 0 && fabs(angle_delta) > p->max_char_angle_delta*(M_PI/180)) + { + return false; + } + } + Envelope e; + if (p->has_dimensions) + { + e.init(x, y, x + p->dimensions.first, y + p->dimensions.second); + } + + + double render_angle = angle; + + x = new_x - (distance)*cos(angle); + y = new_y + (distance)*sin(angle); + //Center the text on the line. + x -= (((double)string_height/2.0) - 1.0)*cos(render_angle+M_PI/2); + y += (((double)string_height/2.0) - 1.0)*sin(render_angle+M_PI/2); + distance -= ci.width; + next_char_x = ci.width*cos(render_angle); + next_char_y = ci.width*sin(render_angle); + + double render_x = x; + double render_y = y; + + if (!p->has_dimensions) + { + // put four corners of the letter into envelope + e.init(render_x, render_y, render_x + ci.width*cos(render_angle), render_y - ci.width*sin(render_angle)); + e.expand_to_include(render_x - ci.height*sin(render_angle), render_y - ci.height*cos(render_angle)); + e.expand_to_include(render_x + (ci.width*cos(render_angle) - ci.height*sin(render_angle)), render_y - (ci.width*sin(render_angle) + ci.height*cos(render_angle))); + } + + if (!detector_.has_placement(e)) + { + return false; + } + + if (p->avoid_edges && !dimensions_.contains(e)) + { + return false; + } + + p->envelopes.push(e); + + if (orientation < 0) + { + // rotate in place + render_x += ci.width*cos(render_angle) - (string_height-2)*sin(render_angle); + render_y -= ci.width*sin(render_angle) + (string_height-2)*cos(render_angle); + render_angle += M_PI; + } + + + p->current_placement.path.add_node(c,render_x - p->current_placement.starting_x, + -render_y + p->current_placement.starting_y, + render_angle); + x += next_char_x; + y -= next_char_y; + } + p->placements.push_back(p->current_placement); + + return true; + } + + + bool placement_finder::build_path_horizontal(placement *p, double target_distance) + { + double x, y; + + p->current_placement.path.clear(); + + std::pair string_dimensions = p->info->get_dimensions(); + double string_width = string_dimensions.first; + double string_height = string_dimensions.second; + + // check if we need to wrap the string + double wrap_at = string_width + 1; + if (p->wrap_width && string_width > p->wrap_width) + { + if (p->text_ratio) + for (int i = 1; ((wrap_at = string_width/i)/(string_height*i)) > p->text_ratio && (string_width/i) > p->wrap_width; ++i); + else + wrap_at = p->wrap_width; + } + + // work out where our line breaks need to be + std::vector line_breaks; + std::vector line_widths; + if (wrap_at < string_width && p->info->num_characters() > 0) + { + int line_count=0; + int last_space = 0; + string_width = 0; + string_height = 0; + double line_width = 0; + double line_height = 0; + double word_width = 0; + double word_height = 0; + for (unsigned int ii = 0; ii < p->info->num_characters(); ii++) + { + character_info ci;; + ci = p->info->at(ii); + + unsigned c = ci.character; + word_width += ci.width; + word_height = word_height > ci.height ? word_height : ci.height; + ++line_count; + + if (c == ' ') + { + last_space = ii; + line_width += word_width; + line_height = line_height > word_height ? line_height : word_height; + word_width = 0; + word_height = 0; + } + if (line_width > 0 && line_width > wrap_at) + { + string_width = string_width > line_width ? string_width : line_width; + string_height += line_height; + line_breaks.push_back(last_space); + line_widths.push_back(line_width); + ii = last_space; + line_count = 0; + line_width = 0; + line_height = 0; + word_width = 0; + word_height = 0; + } + } + line_width += word_width; + string_width = string_width > line_width ? string_width : line_width; + line_breaks.push_back(p->info->num_characters() + 1); + line_widths.push_back(line_width); + } + if (line_breaks.size() == 0) + { + line_breaks.push_back(p->info->num_characters() + 1); + line_widths.push_back(string_width); + } + + p->info->set_dimensions(string_width, string_height); + + if (p->geom->type() == LineString) + { + std::pair starting_pos = p->get_position_at_distance(target_distance); + + p->current_placement.starting_x = starting_pos.first; + p->current_placement.starting_y = starting_pos.second; + } + else + { + p->geom->label_position(&p->current_placement.starting_x, &p->current_placement.starting_y); + // TODO: + // We would only want label position in final 'paper' coords. + // Move view and proj transforms to e.g. label_position(x,y,proj_trans,ctrans)? + double z=0; + p->proj_trans->backward(p->current_placement.starting_x, p->current_placement.starting_y, z); + p->ctrans->forward(&p->current_placement.starting_x, &p->current_placement.starting_y); + // apply displacement ( in pixels ) + p->current_placement.starting_x += boost::tuples::get<0>(p->displacement_); + p->current_placement.starting_y += boost::tuples::get<1>(p->displacement_); + } + + double line_height = 0; + unsigned int line_number = 0; + unsigned int index_to_wrap_at = line_breaks[line_number]; + double line_width = line_widths[line_number]; + + x = -line_width/2.0; + y = -string_height/2.0 + 1.0; + + for (unsigned i = 0; i < p->info->num_characters(); i++) + { + character_info ci;; + ci = p->info->at(i); + + unsigned c = ci.character; + if (i == index_to_wrap_at) + { + index_to_wrap_at = line_breaks[++line_number]; + line_width = line_widths[line_number]; + y -= line_height; + x = -line_width/2.0; + line_height = 0; + continue; + } + else + { + p->current_placement.path.add_node(c, x, y, 0.0); + Envelope e; if (p->has_dimensions) { - e.init(x, y, x + p->dimensions.first, y + p->dimensions.second); + e.init(p->current_placement.starting_x - (p->dimensions.first/2.0), + p->current_placement.starting_y - (p->dimensions.second/2.0), + p->current_placement.starting_x + (p->dimensions.first/2.0), + p->current_placement.starting_y + (p->dimensions.second/2.0)); } - - - double render_angle = angle; - - x = new_x - (distance)*cos(angle); - y = new_y + (distance)*sin(angle); - //Center the text on the line. - x -= (((double)string_height/2.0) - 1.0)*cos(render_angle+M_PI/2); - y += (((double)string_height/2.0) - 1.0)*sin(render_angle+M_PI/2); - distance -= ci.width; - next_char_x = ci.width*cos(render_angle); - next_char_y = ci.width*sin(render_angle); - - double render_x = x; - double render_y = y; - - if (!p->has_dimensions) + else { - // put four corners of the letter into envelope - e.init(render_x, render_y, render_x + ci.width*cos(render_angle), render_y - ci.width*sin(render_angle)); - e.expand_to_include(render_x - ci.height*sin(render_angle), render_y - ci.height*cos(render_angle)); - e.expand_to_include(render_x + (ci.width*cos(render_angle) - ci.height*sin(render_angle)), render_y - (ci.width*sin(render_angle) + ci.height*cos(render_angle))); + e.init(p->current_placement.starting_x + x, + p->current_placement.starting_y - y, + p->current_placement.starting_x + x + ci.width, + p->current_placement.starting_y - y - ci.height); } - + if (!detector_.has_placement(e)) { - return false; + return false; } - + if (p->avoid_edges && !dimensions_.contains(e)) { - return false; + return false; } - + p->envelopes.push(e); - - if (orientation < 0) - { - // rotate in place - render_x += ci.width*cos(render_angle) - (string_height-2)*sin(render_angle); - render_y -= ci.width*sin(render_angle) + (string_height-2)*cos(render_angle); - render_angle += M_PI; - } - - - p->current_placement.path.add_node(c, render_x - p->current_placement.starting_x, -render_y + p->current_placement.starting_y, render_angle); - x += next_char_x; - y -= next_char_y; - } - p->placements.push_back(p->current_placement); - - return true; - } - - - bool placement_finder::build_path_horizontal(placement *p, double target_distance) - { - double x, y; + } + x += ci.width; + line_height = line_height > ci.height ? line_height : ci.height; + } + p->placements.push_back(p->current_placement); - p->current_placement.path.clear(); - - std::pair string_dimensions = p->info->get_dimensions(); - double string_width = string_dimensions.first; - double string_height = string_dimensions.second; - - // check if we need to wrap the string - double wrap_at = string_width + 1; - if (p->wrap_width && string_width > p->wrap_width) - { - if (p->text_ratio) - for (int i = 1; ((wrap_at = string_width/i)/(string_height*i)) > p->text_ratio && (string_width/i) > p->wrap_width; ++i); - else - wrap_at = p->wrap_width; - } - - // work out where our line breaks need to be - std::vector line_breaks; - std::vector line_widths; - if (wrap_at < string_width && p->info->num_characters() > 0) - { - int line_count=0; - int last_space = 0; - string_width = 0; - string_height = 0; - double line_width = 0; - double line_height = 0; - double word_width = 0; - double word_height = 0; - for (unsigned int ii = 0; ii < p->info->num_characters(); ii++) - { - character_info ci;; - ci = p->info->at(ii); - - unsigned c = ci.character; - word_width += ci.width; - word_height = word_height > ci.height ? word_height : ci.height; - ++line_count; - - if (c == ' ') - { - last_space = ii; - line_width += word_width; - line_height = line_height > word_height ? line_height : word_height; - word_width = 0; - word_height = 0; - } - if (line_width > 0 && line_width > wrap_at) - { - string_width = string_width > line_width ? string_width : line_width; - string_height += line_height; - line_breaks.push_back(last_space); - line_widths.push_back(line_width); - ii = last_space; - line_count = 0; - line_width = 0; - line_height = 0; - word_width = 0; - word_height = 0; - } - } - line_width += word_width; - string_width = string_width > line_width ? string_width : line_width; - line_breaks.push_back(p->info->num_characters() + 1); - line_widths.push_back(line_width); - } - if (line_breaks.size() == 0) - { - line_breaks.push_back(p->info->num_characters() + 1); - line_widths.push_back(string_width); - } - - p->info->set_dimensions(string_width, string_height); - - if (p->geom->type() == LineString) - { - std::pair starting_pos = p->get_position_at_distance(target_distance); - - p->current_placement.starting_x = starting_pos.first; - p->current_placement.starting_y = starting_pos.second; - } - else - { - p->geom->label_position(&p->current_placement.starting_x, &p->current_placement.starting_y); - // TODO: - // We would only want label position in final 'paper' coords. - // Move view and proj transforms to e.g. label_position(x,y,proj_trans,ctrans)? - double z=0; - p->proj_trans->backward(p->current_placement.starting_x, p->current_placement.starting_y, z); - p->ctrans->forward(&p->current_placement.starting_x, &p->current_placement.starting_y); - // apply displacement ( in pixels ) - p->current_placement.starting_x += boost::tuples::get<0>(p->displacement_); - p->current_placement.starting_y += boost::tuples::get<1>(p->displacement_); - } - - double line_height = 0; - unsigned int line_number = 0; - unsigned int index_to_wrap_at = line_breaks[line_number]; - double line_width = line_widths[line_number]; - - x = -line_width/2.0; - y = -string_height/2.0 + 1.0; - - for (unsigned i = 0; i < p->info->num_characters(); i++) - { - character_info ci;; - ci = p->info->at(i); - - unsigned c = ci.character; - if (i == index_to_wrap_at) - { - index_to_wrap_at = line_breaks[++line_number]; - line_width = line_widths[line_number]; - y -= line_height; - x = -line_width/2.0; - line_height = 0; - continue; - } - else - { - p->current_placement.path.add_node(c, x, y, 0.0); - - Envelope e; - if (p->has_dimensions) - { - e.init(p->current_placement.starting_x - (p->dimensions.first/2.0), - p->current_placement.starting_y - (p->dimensions.second/2.0), - p->current_placement.starting_x + (p->dimensions.first/2.0), - p->current_placement.starting_y + (p->dimensions.second/2.0)); - } - else - { - e.init(p->current_placement.starting_x + x, - p->current_placement.starting_y - y, - p->current_placement.starting_x + x + ci.width, - p->current_placement.starting_y - y - ci.height); - } - - if (!detector_.has_placement(e)) - { - return false; - } - - if (p->avoid_edges && !dimensions_.contains(e)) - { - return false; - } - - p->envelopes.push(e); - } - x += ci.width; - line_height = line_height > ci.height ? line_height : ci.height; - } - p->placements.push_back(p->current_placement); - - return true; - } + return true; + } } // namespace