New placement finder.
This commit is contained in:
parent
a95524ae35
commit
9d2a6088b1
2 changed files with 330 additions and 361 deletions
|
@ -23,86 +23,34 @@
|
||||||
#ifndef MAPNIK_PLACEMENT_FINDER_HPP
|
#ifndef MAPNIK_PLACEMENT_FINDER_HPP
|
||||||
#define MAPNIK_PLACEMENT_FINDER_HPP
|
#define MAPNIK_PLACEMENT_FINDER_HPP
|
||||||
|
|
||||||
// mapnik
|
|
||||||
#include <mapnik/ctrans.hpp>
|
|
||||||
#include <mapnik/label_collision_detector.hpp>
|
|
||||||
#include <mapnik/text_symbolizer.hpp>
|
|
||||||
#include <mapnik/shield_symbolizer.hpp>
|
|
||||||
#include <mapnik/geometry.hpp>
|
#include <mapnik/geometry.hpp>
|
||||||
#include <mapnik/text_path.hpp>
|
|
||||||
#include <mapnik/text_placements.hpp>
|
#include <mapnik/text_placements.hpp>
|
||||||
|
|
||||||
// stl
|
|
||||||
#include <queue>
|
|
||||||
|
|
||||||
namespace mapnik
|
namespace mapnik
|
||||||
{
|
{
|
||||||
typedef text_path placement_element;
|
|
||||||
|
|
||||||
struct placement : boost::noncopyable
|
|
||||||
{
|
|
||||||
placement(string_info & info_, shield_symbolizer const& sym, double scale_factor, unsigned w, unsigned h, bool has_dimensions_= false);
|
|
||||||
|
|
||||||
placement(string_info & info_, text_symbolizer const& sym, double scale_factor);
|
|
||||||
|
|
||||||
~placement();
|
|
||||||
|
|
||||||
string_info & info; // should only be used for finding placement. doesn't necessarily match placements.vertex() values
|
|
||||||
|
|
||||||
double scale_factor_;
|
|
||||||
label_placement_e label_placement;
|
|
||||||
|
|
||||||
std::queue< box2d<double> > envelopes;
|
|
||||||
|
|
||||||
//output
|
|
||||||
boost::ptr_vector<placement_element> placements;
|
|
||||||
|
|
||||||
int wrap_width;
|
|
||||||
bool wrap_before; // wraps text at wrap_char immediately before current word
|
|
||||||
unsigned char wrap_char;
|
|
||||||
int text_ratio;
|
|
||||||
|
|
||||||
int label_spacing; // distance between repeated labels on a single geometry
|
|
||||||
unsigned label_position_tolerance; //distance the label can be moved on the line to fit, if 0 the default is used
|
|
||||||
bool force_odd_labels; //Always try render an odd amount of labels
|
|
||||||
|
|
||||||
double max_char_angle_delta;
|
|
||||||
double minimum_distance;
|
|
||||||
double minimum_padding;
|
|
||||||
double minimum_path_length;
|
|
||||||
bool avoid_edges;
|
|
||||||
bool has_dimensions;
|
|
||||||
bool allow_overlap;
|
|
||||||
std::pair<double, double> dimensions;
|
|
||||||
bool collect_extents;
|
|
||||||
box2d<double> extents;
|
|
||||||
|
|
||||||
// additional boxes attached to the text labels which must also be
|
|
||||||
// placed in order for the text placement to succeed. e.g: shields.
|
|
||||||
std::vector<box2d<double> > additional_boxes;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
template <typename DetectorT>
|
template <typename DetectorT>
|
||||||
class placement_finder : boost::noncopyable
|
class placement_finder : boost::noncopyable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
placement_finder(DetectorT & detector);
|
placement_finder(text_placement_info &p, string_info &info, DetectorT & detector);
|
||||||
placement_finder(DetectorT & detector, box2d<double> const& extent);
|
placement_finder(text_placement_info &p, string_info &info, DetectorT & detector, box2d<double> const& extent);
|
||||||
|
|
||||||
//Try place a single label at the given point
|
//Try place a single label at the given point
|
||||||
void find_point_placement(placement & p, text_placement_info_ptr po, double pos_x, double pos_y, double angle=0.0, unsigned line_spacing=0, unsigned character_spacing=0);
|
void find_point_placement(double pos_x, double pos_y, double angle=0.0);
|
||||||
|
|
||||||
//Iterate over the given path, placing point labels with respect to label_spacing
|
//Iterate over the given path, placing point labels with respect to label_spacing
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void find_point_placements(placement & p, text_placement_info_ptr po, T & path);
|
void find_point_placements(T & path);
|
||||||
|
|
||||||
//Iterate over the given path, placing line-following labels with respect to label_spacing
|
//Iterate over the given path, placing line-following labels with respect to label_spacing
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void find_line_placements(placement & p, text_placement_info_ptr po, T & path);
|
void find_line_placements(T & path);
|
||||||
|
|
||||||
void update_detector(placement & p);
|
//Find placement, automatically select point or line placement
|
||||||
|
void find_placement(double angle, geometry_type const& geom, CoordTransform const& t, proj_transform const& prj_trans);
|
||||||
|
|
||||||
|
void update_detector();
|
||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
|
@ -117,15 +65,14 @@ private:
|
||||||
// otherwise it will autodetect the orientation.
|
// otherwise it will autodetect the orientation.
|
||||||
// If >= 50% of the characters end up upside down, it will be retried the other way.
|
// If >= 50% of the characters end up upside down, it will be retried the other way.
|
||||||
// RETURN: 1/-1 depending which way up the string ends up being.
|
// RETURN: 1/-1 depending which way up the string ends up being.
|
||||||
std::auto_ptr<placement_element> get_placement_offset(placement & p,
|
std::auto_ptr<placement_element> get_placement_offset(const std::vector<vertex2d> & path_positions,
|
||||||
const std::vector<vertex2d> & path_positions,
|
|
||||||
const std::vector<double> & path_distances,
|
const std::vector<double> & path_distances,
|
||||||
int & orientation, unsigned index, double distance);
|
int & orientation, unsigned index, double distance);
|
||||||
|
|
||||||
///Tests wether the given placement_element be placed without a collision
|
///Tests wether the given placement_element be placed without a collision
|
||||||
// Returns true if it can
|
// Returns true if it can
|
||||||
// NOTE: This edits p.envelopes so it can be used afterwards (you must clear it otherwise)
|
// NOTE: This edits p.envelopes so it can be used afterwards (you must clear it otherwise)
|
||||||
bool test_placement(placement & p, const std::auto_ptr<placement_element> & current_placement, const int & orientation);
|
bool test_placement(const std::auto_ptr<placement_element> & current_placement, const int & orientation);
|
||||||
|
|
||||||
///Does a line-circle intersect calculation
|
///Does a line-circle intersect calculation
|
||||||
// NOTE: Follow the strict pre conditions
|
// NOTE: Follow the strict pre conditions
|
||||||
|
@ -137,14 +84,26 @@ private:
|
||||||
const double &x1, const double &y1, const double &x2, const double &y2,
|
const double &x1, const double &y1, const double &x2, const double &y2,
|
||||||
double &ix, double &iy);
|
double &ix, double &iy);
|
||||||
|
|
||||||
|
void find_line_breaks();
|
||||||
|
void init_string_size();
|
||||||
|
void init_alignment();
|
||||||
|
void adjust_position(placement_element *current_placement, double label_x, double label_y);
|
||||||
|
|
||||||
///General Internals
|
///General Internals
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
DetectorT & detector_;
|
DetectorT & detector_;
|
||||||
box2d<double> const& dimensions_;
|
box2d<double> const& dimensions_;
|
||||||
|
string_info &info_;
|
||||||
|
text_symbolizer_properties &p;
|
||||||
|
text_placement_info π
|
||||||
|
double string_width_;
|
||||||
|
double string_height_;
|
||||||
|
double first_line_space_;
|
||||||
|
vertical_alignment_e valign_;
|
||||||
|
horizontal_alignment_e halign_;
|
||||||
|
std::vector<unsigned> line_breaks_;
|
||||||
|
std::vector<std::pair<double, double> > line_sizes_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#endif // MAPNIK_PLACEMENT_FINDER_HPP
|
#endif // MAPNIK_PLACEMENT_FINDER_HPP
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include <mapnik/placement_finder.hpp>
|
#include <mapnik/placement_finder.hpp>
|
||||||
#include <mapnik/geometry.hpp>
|
#include <mapnik/geometry.hpp>
|
||||||
#include <mapnik/text_path.hpp>
|
#include <mapnik/text_path.hpp>
|
||||||
|
#include <mapnik/label_collision_detector.hpp>
|
||||||
#include <mapnik/fastmath.hpp>
|
#include <mapnik/fastmath.hpp>
|
||||||
|
|
||||||
// agg
|
// agg
|
||||||
|
@ -49,60 +50,6 @@
|
||||||
|
|
||||||
namespace mapnik
|
namespace mapnik
|
||||||
{
|
{
|
||||||
placement::placement(string_info & info_,
|
|
||||||
shield_symbolizer const& sym,
|
|
||||||
double scale_factor,
|
|
||||||
unsigned w, unsigned h,
|
|
||||||
bool has_dimensions_)
|
|
||||||
: info(info_),
|
|
||||||
scale_factor_(scale_factor),
|
|
||||||
label_placement(sym.get_label_placement()),
|
|
||||||
wrap_width(sym.get_wrap_width()),
|
|
||||||
wrap_before(sym.get_wrap_before()),
|
|
||||||
wrap_char(sym.get_wrap_char()),
|
|
||||||
text_ratio(sym.get_text_ratio()),
|
|
||||||
label_spacing(scale_factor_ * sym.get_label_spacing()),
|
|
||||||
label_position_tolerance(sym.get_label_position_tolerance()),
|
|
||||||
force_odd_labels(sym.get_force_odd_labels()),
|
|
||||||
max_char_angle_delta(sym.get_max_char_angle_delta()),
|
|
||||||
minimum_distance(scale_factor_ * sym.get_minimum_distance()),
|
|
||||||
minimum_padding(scale_factor_ * sym.get_minimum_padding()),
|
|
||||||
minimum_path_length(0),
|
|
||||||
avoid_edges(sym.get_avoid_edges()),
|
|
||||||
has_dimensions(has_dimensions_),
|
|
||||||
allow_overlap(false),
|
|
||||||
dimensions(std::make_pair(w,h)),
|
|
||||||
collect_extents(false),
|
|
||||||
extents()
|
|
||||||
{}
|
|
||||||
|
|
||||||
placement::placement(string_info & info_,
|
|
||||||
text_symbolizer const& sym,
|
|
||||||
double scale_factor)
|
|
||||||
: info(info_),
|
|
||||||
scale_factor_(scale_factor),
|
|
||||||
label_placement(sym.get_label_placement()),
|
|
||||||
wrap_width(sym.get_wrap_width()),
|
|
||||||
wrap_before(sym.get_wrap_before()),
|
|
||||||
wrap_char(sym.get_wrap_char()),
|
|
||||||
text_ratio(sym.get_text_ratio()),
|
|
||||||
label_spacing(scale_factor_ * sym.get_label_spacing()),
|
|
||||||
label_position_tolerance(sym.get_label_position_tolerance()),
|
|
||||||
force_odd_labels(sym.get_force_odd_labels()),
|
|
||||||
max_char_angle_delta(sym.get_max_char_angle_delta()),
|
|
||||||
minimum_distance(scale_factor_ * sym.get_minimum_distance()),
|
|
||||||
minimum_padding(scale_factor_ * sym.get_minimum_padding()),
|
|
||||||
minimum_path_length(scale_factor_ * sym.get_minimum_path_length()),
|
|
||||||
avoid_edges(sym.get_avoid_edges()),
|
|
||||||
has_dimensions(false),
|
|
||||||
allow_overlap(sym.get_allow_overlap()),
|
|
||||||
dimensions(),
|
|
||||||
collect_extents(false),
|
|
||||||
extents()
|
|
||||||
{}
|
|
||||||
|
|
||||||
|
|
||||||
placement::~placement() {}
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
std::pair<double, double> get_position_at_distance(double target_distance, T & shape_path)
|
std::pair<double, double> get_position_at_distance(double target_distance, T & shape_path)
|
||||||
|
@ -151,22 +98,24 @@ double get_total_distance(T & shape_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename DetectorT>
|
template <typename DetectorT>
|
||||||
placement_finder<DetectorT>::placement_finder(DetectorT & detector)
|
placement_finder<DetectorT>::placement_finder(text_placement_info &placement_info, string_info &info, DetectorT & detector)
|
||||||
: detector_(detector),
|
: detector_(detector),
|
||||||
dimensions_(detector_.extent())
|
dimensions_(detector_.extent()),
|
||||||
|
info_(info), p(placement_info.properties), pi(placement_info), string_width_(0), string_height_(0), first_line_space_(0), valign_(V_AUTO), halign_(H_AUTO), line_breaks_(), line_sizes_()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename DetectorT>
|
template <typename DetectorT>
|
||||||
placement_finder<DetectorT>::placement_finder(DetectorT & detector, box2d<double> const& extent)
|
placement_finder<DetectorT>::placement_finder(text_placement_info &placement_info, string_info &info, DetectorT & detector, box2d<double> const& extent)
|
||||||
: detector_(detector),
|
: detector_(detector),
|
||||||
dimensions_(extent)
|
dimensions_(extent),
|
||||||
|
info_(info), p(placement_info.properties), pi(placement_info), string_width_(0), string_height_(0), first_line_space_(0), valign_(V_AUTO), halign_(H_AUTO), line_breaks_(), line_sizes_()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename DetectorT>
|
template <typename DetectorT>
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void placement_finder<DetectorT>::find_point_placements(placement & p, text_placement_info_ptr po, T & shape_path)
|
void placement_finder<DetectorT>::find_point_placements(T & shape_path)
|
||||||
{
|
{
|
||||||
unsigned cmd;
|
unsigned cmd;
|
||||||
double new_x = 0.0;
|
double new_x = 0.0;
|
||||||
|
@ -182,15 +131,15 @@ void placement_finder<DetectorT>::find_point_placements(placement & p, text_plac
|
||||||
{
|
{
|
||||||
double x, y;
|
double x, y;
|
||||||
shape_path.vertex(&x,&y);
|
shape_path.vertex(&x,&y);
|
||||||
find_point_placement(p, po, x, y);
|
find_point_placement(x, y);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int num_labels = 1;
|
int num_labels = 1;
|
||||||
if (p.label_spacing > 0)
|
if (p.label_spacing > 0)
|
||||||
num_labels = static_cast<int> (floor(total_distance / p.label_spacing));
|
num_labels = static_cast<int> (floor(total_distance / pi.get_actual_label_spacing()));
|
||||||
|
|
||||||
if (p.force_odd_labels && num_labels%2 == 0)
|
if (p.force_odd_labels && num_labels % 2 == 0)
|
||||||
num_labels--;
|
num_labels--;
|
||||||
if (num_labels <= 0)
|
if (num_labels <= 0)
|
||||||
num_labels = 1;
|
num_labels = 1;
|
||||||
|
@ -217,7 +166,7 @@ void placement_finder<DetectorT>::find_point_placements(placement & p, text_plac
|
||||||
{
|
{
|
||||||
//Try place at the specified place
|
//Try place at the specified place
|
||||||
double new_weight = (segment_length - (distance - target_distance))/segment_length;
|
double new_weight = (segment_length - (distance - target_distance))/segment_length;
|
||||||
find_point_placement(p, po, old_x + (new_x-old_x)*new_weight, old_y + (new_y-old_y)*new_weight);
|
find_point_placement(old_x + (new_x-old_x)*new_weight, old_y + (new_y-old_y)*new_weight);
|
||||||
|
|
||||||
distance -= target_distance; //Consume the spacing gap we have used up
|
distance -= target_distance; //Consume the spacing gap we have used up
|
||||||
target_distance = spacing; //Need to reset the target_distance as it is spacing/2 for the first label.
|
target_distance = spacing; //Need to reset the target_distance as it is spacing/2 for the first label.
|
||||||
|
@ -231,206 +180,227 @@ void placement_finder<DetectorT>::find_point_placements(placement & p, text_plac
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename DetectorT>
|
template <typename DetectorT>
|
||||||
void placement_finder<DetectorT>::find_point_placement(placement & p,
|
void placement_finder<DetectorT>::init_string_size()
|
||||||
text_placement_info_ptr po,
|
|
||||||
double label_x,
|
|
||||||
double label_y,
|
|
||||||
double angle,
|
|
||||||
unsigned line_spacing,
|
|
||||||
unsigned character_spacing)
|
|
||||||
{
|
{
|
||||||
double x, y;
|
// Get total string size
|
||||||
std::auto_ptr<placement_element> current_placement(new placement_element);
|
string_width_ = 0;
|
||||||
|
string_height_ = 0;
|
||||||
|
first_line_space_ = 0;
|
||||||
|
if (!info_.num_characters()) return; //At least one character is required
|
||||||
|
for (unsigned i = 0; i < info_.num_characters(); i++)
|
||||||
|
{
|
||||||
|
char_info const& ci = info_.at(i);
|
||||||
|
if (!ci.width || !ci.line_height) continue; //Skip empty chars (add no character_spacing for them)
|
||||||
|
string_width_ += ci.width + ci.format->character_spacing;
|
||||||
|
string_height_ = std::max(string_height_, ci.line_height+ci.format->line_spacing);
|
||||||
|
first_line_space_ = std::max(first_line_space_, ci.line_height-ci.avg_height);
|
||||||
|
}
|
||||||
|
string_width_ -= info_.at(info_.num_characters()-1).format->character_spacing; //Remove last space
|
||||||
|
}
|
||||||
|
|
||||||
std::pair<double, double> string_dimensions = p.info.get_dimensions();
|
|
||||||
double string_width = string_dimensions.first + (character_spacing *(p.info.num_characters()-1));
|
|
||||||
double string_height = string_dimensions.second;
|
|
||||||
|
|
||||||
// use height of tallest character in the string for the 'line' spacing to obtain consistent line spacing
|
|
||||||
double max_character_height = string_height; // height of the tallest character in the string
|
|
||||||
|
|
||||||
|
|
||||||
|
template <typename DetectorT>
|
||||||
|
void placement_finder<DetectorT>::find_line_breaks()
|
||||||
|
{
|
||||||
|
bool first_line = true;
|
||||||
|
line_breaks_.clear();
|
||||||
|
line_sizes_.clear();
|
||||||
// check if we need to wrap the string
|
// check if we need to wrap the string
|
||||||
double wrap_at = string_width + 1.0;
|
double wrap_at = string_width_ + 1.0;
|
||||||
if (p.wrap_width && string_width > p.wrap_width)
|
if (p.wrap_width && string_width_ > p.wrap_width)
|
||||||
{
|
{
|
||||||
if (p.text_ratio)
|
if (p.text_ratio)
|
||||||
for (double i = 1.0; ((wrap_at = string_width/i)/(string_height*i)) > p.text_ratio && (string_width/i) > p.wrap_width; i += 1.0) ;
|
for (double i = 1.0; ((wrap_at = string_width_/i)/(string_height_*i)) > p.text_ratio && (string_width_/i) > p.wrap_width; i += 1.0) ;
|
||||||
else
|
else
|
||||||
wrap_at = p.wrap_width;
|
wrap_at = p.wrap_width;
|
||||||
}
|
}
|
||||||
|
|
||||||
// work out where our line breaks need to be and the resultant width to the 'wrapped' string
|
// work out where our line breaks need to be and the resultant width to the 'wrapped' string
|
||||||
std::vector<int> line_breaks;
|
if ((wrap_at < string_width_) || info_.has_line_breaks())
|
||||||
std::vector<double> line_widths;
|
|
||||||
|
|
||||||
if ((p.info.num_characters() > 0) && ((wrap_at < string_width) || p.info.has_line_breaks()))
|
|
||||||
{
|
{
|
||||||
int last_wrap_char = 0;
|
first_line_space_ = 0.0;
|
||||||
int last_wrap_char_width = 0;
|
int last_wrap_char_pos = 0; //Position of last char where wrapping is possible
|
||||||
string_width = 0.0;
|
double last_char_spacing = 0.0;
|
||||||
string_height = 0.0;
|
double last_wrap_char_width = 0.0; //Include char_spacing before and after
|
||||||
|
string_width_ = 0.0;
|
||||||
|
string_height_ = 0.0;
|
||||||
double line_width = 0.0;
|
double line_width = 0.0;
|
||||||
double word_width = 0.0;
|
double line_height = 0.0; //Height of tallest char in line
|
||||||
|
double word_width = 0.0; //Current unfinished word width
|
||||||
|
double word_height = 0.0;
|
||||||
|
//line_width, word_width does include char width + spacing, but not the spacing after the last char
|
||||||
|
|
||||||
for (unsigned int ii = 0; ii < p.info.num_characters(); ii++)
|
for (unsigned int ii = 0; ii < info_.num_characters(); ii++)
|
||||||
{
|
{
|
||||||
char_info ci;
|
char_info const& ci = info_.at(ii);
|
||||||
ci = p.info.at(ii);
|
|
||||||
|
|
||||||
double cwidth = ci.width + character_spacing;
|
|
||||||
|
|
||||||
unsigned c = ci.c;
|
unsigned c = ci.c;
|
||||||
word_width += cwidth;
|
|
||||||
|
|
||||||
if ((c == p.wrap_char) || (c == '\n'))
|
if ((c == ci.format->wrap_char) || (c == '\n'))
|
||||||
{
|
{
|
||||||
last_wrap_char = ii;
|
last_wrap_char_pos = ii;
|
||||||
last_wrap_char_width = cwidth;
|
//No wrap at previous position
|
||||||
line_width += word_width;
|
line_width += word_width + last_wrap_char_width;
|
||||||
|
line_height = std::max(line_height, word_height);
|
||||||
|
last_wrap_char_width = last_char_spacing + ci.width + ci.format->character_spacing;
|
||||||
|
last_char_spacing = 0.0;
|
||||||
word_width = 0.0;
|
word_width = 0.0;
|
||||||
|
word_height = 0.0;
|
||||||
|
} else {
|
||||||
|
//No wrap char
|
||||||
|
word_width += last_char_spacing + ci.width;
|
||||||
|
last_char_spacing = ci.format->character_spacing;
|
||||||
|
word_height = std::max(word_height, ci.line_height + ci.format->line_spacing);
|
||||||
|
if (first_line) first_line_space_ = std::max(first_line_space_, ci.line_height-ci.avg_height);
|
||||||
}
|
}
|
||||||
|
|
||||||
// wrap text at first wrap_char after (default) the wrap width or immediately before the current word
|
// wrap text at first wrap_char after (default) the wrap width or immediately before the current word
|
||||||
if ((c == '\n') ||
|
if ((c == '\n') ||
|
||||||
(line_width > 0 && (((line_width - character_spacing) > wrap_at && !p.wrap_before) ||
|
(line_width > 0 && ((line_width > wrap_at && !ci.format->wrap_before) ||
|
||||||
((line_width + word_width - character_spacing) > wrap_at && p.wrap_before)) ))
|
((line_width + last_wrap_char_width + word_width) > wrap_at && ci.format->wrap_before)) ))
|
||||||
{
|
{
|
||||||
// Remove width of breaking space character since it is not rendered and the character_spacing for the last character on the line
|
string_width_ = std::max(string_width_, line_width); //Total width is the longest line
|
||||||
line_width -= (last_wrap_char_width + character_spacing);
|
string_height_ += line_height;
|
||||||
string_width = string_width > line_width ? string_width : line_width;
|
line_breaks_.push_back(last_wrap_char_pos);
|
||||||
string_height += max_character_height;
|
line_sizes_.push_back(std::make_pair(line_width, line_height));
|
||||||
line_breaks.push_back(last_wrap_char);
|
|
||||||
line_widths.push_back(line_width);
|
|
||||||
ii = last_wrap_char;
|
|
||||||
line_width = 0.0;
|
line_width = 0.0;
|
||||||
word_width = 0.0;
|
line_height = 0.0;
|
||||||
|
last_wrap_char_width = 0; //Wrap char supressed
|
||||||
|
first_line = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
line_width += (word_width - character_spacing); // remove character_spacing from last character on the line
|
line_width += last_wrap_char_width + word_width;
|
||||||
string_width = string_width > line_width ? string_width : line_width;
|
line_height = std::max(line_height, word_height);
|
||||||
string_height += max_character_height;
|
string_width_ = std::max(string_width_, line_width);
|
||||||
line_breaks.push_back(p.info.num_characters());
|
string_height_ += line_height;
|
||||||
line_widths.push_back(line_width);
|
line_sizes_.push_back(std::make_pair(line_width, line_height));
|
||||||
|
} else {
|
||||||
|
//No linebreaks
|
||||||
|
line_sizes_.push_back(std::make_pair(string_width_, string_height_));
|
||||||
}
|
}
|
||||||
if (line_breaks.size() == 0)
|
line_breaks_.push_back(info_.num_characters());
|
||||||
{
|
}
|
||||||
line_breaks.push_back(p.info.num_characters());
|
|
||||||
line_widths.push_back(string_width);
|
|
||||||
|
|
||||||
|
template <typename DetectorT>
|
||||||
|
void placement_finder<DetectorT>::init_alignment()
|
||||||
|
{
|
||||||
|
valign_ = p.valign;
|
||||||
|
if (valign_ == V_AUTO) {
|
||||||
|
if (p.displacement.get<1>() > 0.0)
|
||||||
|
valign_ = V_BOTTOM;
|
||||||
|
else if (p.displacement.get<1>() < 0.0)
|
||||||
|
valign_ = V_TOP;
|
||||||
|
else
|
||||||
|
valign_ = V_MIDDLE;
|
||||||
}
|
}
|
||||||
int total_lines = line_breaks.size();
|
|
||||||
|
|
||||||
p.info.set_dimensions( string_width, (string_height + (line_spacing * (total_lines-1))) );
|
halign_ = p.halign;
|
||||||
|
if (halign_ == H_AUTO) {
|
||||||
|
if (p.displacement.get<0>() > 0.0)
|
||||||
|
halign_ = H_RIGHT;
|
||||||
|
else if (p.displacement.get<0>() < 0.0)
|
||||||
|
halign_ = H_LEFT;
|
||||||
|
else
|
||||||
|
halign_ = H_MIDDLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename DetectorT>
|
||||||
|
void placement_finder<DetectorT>::adjust_position(placement_element *current_placement, double label_x, double label_y)
|
||||||
|
{
|
||||||
// if needed, adjust for desired vertical alignment
|
// if needed, adjust for desired vertical alignment
|
||||||
current_placement->starting_y = label_y; // no adjustment, default is MIDDLE
|
current_placement->starting_y = label_y; // no adjustment, default is MIDDLE
|
||||||
|
if (valign_ == V_TOP)
|
||||||
vertical_alignment_e real_valign = po->valign;
|
current_placement->starting_y -= 0.5 * string_height_; // move center up by 1/2 the total height
|
||||||
if (real_valign == V_AUTO) {
|
else if (valign_ == V_BOTTOM) {
|
||||||
if (po->displacement.get<1>() > 0.0)
|
current_placement->starting_y += 0.5 * string_height_; // move center down by the 1/2 the total height
|
||||||
real_valign = V_BOTTOM;
|
current_placement->starting_y -= first_line_space_;
|
||||||
else if (po->displacement.get<1>() < 0.0)
|
} else if (valign_ == V_MIDDLE) {
|
||||||
real_valign = V_TOP;
|
current_placement->starting_y -= first_line_space_/2.0;
|
||||||
else
|
|
||||||
real_valign = V_MIDDLE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
horizontal_alignment_e real_halign = po->halign;
|
|
||||||
if (real_halign == H_AUTO) {
|
|
||||||
if (po->displacement.get<0>() > 0.0)
|
|
||||||
real_halign = H_RIGHT;
|
|
||||||
else if (po->displacement.get<0>() < 0.0)
|
|
||||||
real_halign = H_LEFT;
|
|
||||||
else
|
|
||||||
real_halign = H_MIDDLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (real_valign == V_TOP)
|
|
||||||
current_placement->starting_y -= 0.5 * (string_height + (line_spacing * (total_lines-1))); // move center up by 1/2 the total height
|
|
||||||
|
|
||||||
else if (real_valign == V_BOTTOM)
|
|
||||||
current_placement->starting_y += 0.5 * (string_height + (line_spacing * (total_lines-1))); // move center down by the 1/2 the total height
|
|
||||||
|
|
||||||
// correct placement for error, but BOTTOM does not need to be adjusted
|
|
||||||
// (text rendering is at text_size, but line placement is by line_height (max_character_height),
|
|
||||||
// and the rendering adds the extra space below the characters)
|
|
||||||
if (real_valign == V_TOP )
|
|
||||||
current_placement->starting_y -= (po->text_size - max_character_height); // move up by the error
|
|
||||||
|
|
||||||
else if (real_valign == V_MIDDLE)
|
|
||||||
current_placement->starting_y -= ((po->text_size - max_character_height) / 2.0); // move up by 1/2 the error
|
|
||||||
|
|
||||||
// set horizontal position to middle of text
|
// set horizontal position to middle of text
|
||||||
current_placement->starting_x = label_x; // no adjustment, default is MIDDLE
|
current_placement->starting_x = label_x; // no adjustment, default is MIDDLE
|
||||||
|
if (halign_ == H_LEFT)
|
||||||
if (real_halign == H_LEFT)
|
current_placement->starting_x -= 0.5 * string_width_; // move center left by 1/2 the string width
|
||||||
current_placement->starting_x -= 0.5 * string_width; // move center left by 1/2 the string width
|
else if (halign_ == H_RIGHT)
|
||||||
|
current_placement->starting_x += 0.5 * string_width_; // move center right by 1/2 the string width
|
||||||
else if (real_halign == H_RIGHT)
|
|
||||||
current_placement->starting_x += 0.5 * string_width; // move center right by 1/2 the string width
|
|
||||||
|
|
||||||
// adjust text envelope position by user's x-y displacement (dx, dy)
|
// adjust text envelope position by user's x-y displacement (dx, dy)
|
||||||
current_placement->starting_x += p.scale_factor_ * boost::tuples::get<0>(po->displacement);
|
current_placement->starting_x += pi.get_scale_factor() * boost::tuples::get<0>(p.displacement);
|
||||||
current_placement->starting_y += p.scale_factor_ * boost::tuples::get<1>(po->displacement);
|
current_placement->starting_y += pi.get_scale_factor() * boost::tuples::get<1>(p.displacement);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename DetectorT>
|
||||||
|
void placement_finder<DetectorT>::find_point_placement(double label_x, double label_y, double angle)
|
||||||
|
{
|
||||||
|
init_string_size();
|
||||||
|
find_line_breaks();
|
||||||
|
init_alignment();
|
||||||
|
|
||||||
|
double rad = M_PI * angle/180.0;
|
||||||
|
double cosa = std::cos(rad);
|
||||||
|
double sina = std::sin(rad);
|
||||||
|
|
||||||
|
double x, y;
|
||||||
|
std::auto_ptr<placement_element> current_placement(new placement_element);
|
||||||
|
|
||||||
|
adjust_position(current_placement.get(), label_x, label_y);
|
||||||
|
|
||||||
// presets for first line
|
// presets for first line
|
||||||
unsigned int line_number = 0;
|
unsigned int line_number = 0;
|
||||||
unsigned int index_to_wrap_at = line_breaks[0];
|
unsigned int index_to_wrap_at = line_breaks_[0];
|
||||||
double line_width = line_widths[0];
|
double line_width = line_sizes_[0].first;
|
||||||
|
double line_height = line_sizes_[0].second;
|
||||||
|
|
||||||
|
//TODO: Understand and document this
|
||||||
// set for upper left corner of text envelope for the first line, bottom left of first character
|
// set for upper left corner of text envelope for the first line, bottom left of first character
|
||||||
x = -(line_width / 2.0);
|
y = (string_height_ / 2.0) - line_height;
|
||||||
if (p.info.get_rtl()==false)
|
|
||||||
{
|
// adjust for desired justification
|
||||||
y = (0.5 * (string_height + (line_spacing * (total_lines-1)))) - max_character_height;
|
//TODO: Understand and document this
|
||||||
}
|
if (p.jalign == J_LEFT)
|
||||||
|
x = -(string_width_ / 2.0);
|
||||||
|
else if (p.jalign == J_RIGHT)
|
||||||
|
x = (string_width_ / 2.0) - line_width;
|
||||||
else
|
else
|
||||||
{
|
x = -(line_width / 2.0);
|
||||||
y = -(0.5 * (string_height + (line_spacing * (total_lines-1)))) + max_character_height;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if needed, adjust for desired justification (J_MIDDLE is the default)
|
|
||||||
if( po->jalign == J_LEFT )
|
|
||||||
x = -(string_width / 2.0);
|
|
||||||
|
|
||||||
else if (po->jalign == J_RIGHT)
|
|
||||||
x = (string_width / 2.0) - line_width;
|
|
||||||
|
|
||||||
// save each character rendering position and build envelope as go thru loop
|
// save each character rendering position and build envelope as go thru loop
|
||||||
std::queue< box2d<double> > c_envelopes;
|
std::queue< box2d<double> > c_envelopes;
|
||||||
|
|
||||||
for (unsigned i = 0; i < p.info.num_characters(); i++)
|
for (unsigned i = 0; i < info_.num_characters(); i++)
|
||||||
{
|
{
|
||||||
char_info ci;
|
char_info const& ci = info_.at(i);
|
||||||
ci = p.info.at(i);
|
|
||||||
|
|
||||||
double cwidth = ci.width + character_spacing;
|
double cwidth = ci.width + ci.format->character_spacing;
|
||||||
|
|
||||||
unsigned c = ci.c;
|
unsigned c = ci.c;
|
||||||
if (i == index_to_wrap_at)
|
if (i == index_to_wrap_at)
|
||||||
{
|
{
|
||||||
index_to_wrap_at = line_breaks[++line_number];
|
index_to_wrap_at = line_breaks_[++line_number];
|
||||||
line_width = line_widths[line_number];
|
line_width = line_sizes_[line_number].first;
|
||||||
|
line_height= line_sizes_[line_number].second;
|
||||||
|
|
||||||
if (p.info.get_rtl()==false)
|
y -= line_height; // move position down to line start
|
||||||
{
|
|
||||||
y -= (max_character_height + line_spacing); // move position down to line start
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
y += (max_character_height + line_spacing); // move position up to line start
|
|
||||||
}
|
|
||||||
|
|
||||||
// reset to begining of line position
|
// reset to begining of line position
|
||||||
x = ((po->jalign == J_LEFT)? -(string_width / 2.0): ((po->jalign == J_RIGHT)? ((string_width /2.0) - line_width): -(line_width / 2.0)));
|
if (p.jalign == J_LEFT)
|
||||||
|
x = -(string_width_ / 2.0);
|
||||||
|
else if (p.jalign == J_RIGHT)
|
||||||
|
x = (string_width_ / 2.0) - line_width;
|
||||||
|
else
|
||||||
|
x = -(line_width / 2.0);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// place the character relative to the center of the string envelope
|
// place the character relative to the center of the string envelope
|
||||||
double rad = M_PI * angle/180.0;
|
|
||||||
double cosa = fast_cos(rad);
|
|
||||||
double sina = fast_sin(rad);
|
|
||||||
|
|
||||||
double dx = x * cosa - y*sina;
|
double dx = x * cosa - y*sina;
|
||||||
double dy = x * sina + y*cosa;
|
double dy = x * sina + y*cosa;
|
||||||
|
|
||||||
|
@ -439,37 +409,41 @@ void placement_finder<DetectorT>::find_point_placement(placement & p,
|
||||||
// compute the Bounding Box for each character and test for:
|
// compute the Bounding Box for each character and test for:
|
||||||
// overlap, minimum distance or edge avoidance - exit if condition occurs
|
// overlap, minimum distance or edge avoidance - exit if condition occurs
|
||||||
box2d<double> e;
|
box2d<double> e;
|
||||||
if (p.has_dimensions)
|
/*x axis: left to right, y axis: top to bottom (negative values higher)*/
|
||||||
|
if (pi.has_dimensions)
|
||||||
{
|
{
|
||||||
e.init(current_placement->starting_x - (p.dimensions.first/2.0), // Top Left
|
e.init(current_placement->starting_x - (pi.dimensions.first/2.0), // Top Left
|
||||||
current_placement->starting_y - (p.dimensions.second/2.0),
|
current_placement->starting_y - (pi.dimensions.second/2.0),
|
||||||
|
|
||||||
current_placement->starting_x + (p.dimensions.first/2.0), // Bottom Right
|
current_placement->starting_x + (pi.dimensions.first/2.0), // Bottom Right
|
||||||
current_placement->starting_y + (p.dimensions.second/2.0));
|
current_placement->starting_y + (pi.dimensions.second/2.0));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
e.init(current_placement->starting_x + dx, // Bottom Left
|
e.init(current_placement->starting_x + dx, // Bottom Left
|
||||||
current_placement->starting_y - dy,
|
current_placement->starting_y - dy - ci.ymin, /*ymin usually <0 */
|
||||||
|
|
||||||
current_placement->starting_x + dx + ci.width, // Top Right
|
current_placement->starting_x + dx + ci.width, // Top Right
|
||||||
current_placement->starting_y - dy - max_character_height);
|
current_placement->starting_y - dy - ci.ymax);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if there is an overlap with existing envelopes, then exit - no placement
|
// if there is an overlap with existing envelopes, then exit - no placement
|
||||||
if (!detector_.extent().intersects(e) || (!p.allow_overlap && !detector_.has_point_placement(e,p.minimum_distance)))
|
if (!detector_.extent().intersects(e) || (!p.allow_overlap && !detector_.has_point_placement(e, pi.get_actual_minimum_distance()))) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// if avoid_edges test dimensions contains e
|
// if avoid_edges test dimensions contains e
|
||||||
if (p.avoid_edges && !dimensions_.contains(e))
|
if (p.avoid_edges && !dimensions_.contains(e)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (p.minimum_padding > 0)
|
if (p.minimum_padding > 0)
|
||||||
{
|
{
|
||||||
box2d<double> epad(e.minx()-p.minimum_padding,
|
double min_pad = pi.get_actual_minimum_padding();
|
||||||
e.miny()-p.minimum_padding,
|
box2d<double> epad(e.minx()-min_pad,
|
||||||
e.maxx()+p.minimum_padding,
|
e.miny()-min_pad,
|
||||||
e.maxy()+p.minimum_padding);
|
e.maxx()+min_pad,
|
||||||
|
e.maxy()+min_pad);
|
||||||
if (!dimensions_.contains(epad))
|
if (!dimensions_.contains(epad))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
@ -482,6 +456,8 @@ void placement_finder<DetectorT>::find_point_placement(placement & p,
|
||||||
x += cwidth; // move position to next character
|
x += cwidth; // move position to next character
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
//TODO
|
||||||
// check the placement of any additional envelopes
|
// check the placement of any additional envelopes
|
||||||
if (!p.allow_overlap && !p.additional_boxes.empty())
|
if (!p.allow_overlap && !p.additional_boxes.empty())
|
||||||
{
|
{
|
||||||
|
@ -498,22 +474,24 @@ void placement_finder<DetectorT>::find_point_placement(placement & p,
|
||||||
c_envelopes.push(pt);
|
c_envelopes.push(pt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// since there was no early exit, add the character envelopes to the placements' envelopes
|
// since there was no early exit, add the character envelopes to the placements' envelopes
|
||||||
while( !c_envelopes.empty() )
|
while( !c_envelopes.empty() )
|
||||||
{
|
{
|
||||||
p.envelopes.push( c_envelopes.front() );
|
pi.envelopes.push( c_envelopes.front() );
|
||||||
c_envelopes.pop();
|
c_envelopes.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
p.placements.push_back(current_placement.release());
|
pi.placements.push_back(current_placement.release());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template <typename DetectorT>
|
template <typename DetectorT>
|
||||||
template <typename PathT>
|
template <typename PathT>
|
||||||
void placement_finder<DetectorT>::find_line_placements(placement & p, text_placement_info_ptr po, PathT & shape_path)
|
void placement_finder<DetectorT>::find_line_placements(PathT & shape_path)
|
||||||
{
|
{
|
||||||
|
init_string_size();
|
||||||
unsigned cmd;
|
unsigned cmd;
|
||||||
double new_x = 0.0;
|
double new_x = 0.0;
|
||||||
double new_y = 0.0;
|
double new_y = 0.0;
|
||||||
|
@ -556,29 +534,26 @@ void placement_finder<DetectorT>::find_line_placements(placement & p, text_place
|
||||||
return;
|
return;
|
||||||
|
|
||||||
double distance = 0.0;
|
double distance = 0.0;
|
||||||
std::pair<double, double> string_dimensions = p.info.get_dimensions();
|
|
||||||
|
|
||||||
double string_width = string_dimensions.first;
|
double displacement = boost::tuples::get<1>(p.displacement); // displace by dy
|
||||||
|
|
||||||
double displacement = boost::tuples::get<1>(po->displacement); // displace by dy
|
|
||||||
|
|
||||||
//Calculate a target_distance that will place the labels centered evenly rather than offset from the start of the linestring
|
//Calculate a target_distance that will place the labels centered evenly rather than offset from the start of the linestring
|
||||||
if (total_distance < string_width) //Can't place any strings
|
if (total_distance < string_width_) //Can't place any strings
|
||||||
return;
|
return;
|
||||||
|
|
||||||
//If there is no spacing then just do one label, otherwise calculate how many there should be
|
//If there is no spacing then just do one label, otherwise calculate how many there should be
|
||||||
int num_labels = 1;
|
int num_labels = 1;
|
||||||
if (p.label_spacing > 0)
|
if (p.label_spacing > 0)
|
||||||
num_labels = static_cast<int> (floor(total_distance / (p.label_spacing + string_width)));
|
num_labels = static_cast<int> (floor(total_distance / (pi.get_actual_label_spacing() + string_width_)));
|
||||||
|
|
||||||
if (p.force_odd_labels && num_labels%2 == 0)
|
if (p.force_odd_labels && (num_labels % 2 == 0))
|
||||||
num_labels--;
|
num_labels--;
|
||||||
if (num_labels <= 0)
|
if (num_labels <= 0)
|
||||||
num_labels = 1;
|
num_labels = 1;
|
||||||
|
|
||||||
//Now we know how many labels we are going to place, calculate the spacing so that they will get placed evenly
|
//Now we know how many labels we are going to place, calculate the spacing so that they will get placed evenly
|
||||||
double spacing = total_distance / num_labels;
|
double spacing = total_distance / num_labels;
|
||||||
double target_distance = (spacing - string_width) / 2; // first label should be placed at half the spacing
|
double target_distance = (spacing - string_width_) / 2; // first label should be placed at half the spacing
|
||||||
|
|
||||||
//Calculate or read out the tolerance
|
//Calculate or read out the tolerance
|
||||||
double tolerance_delta, tolerance;
|
double tolerance_delta, tolerance;
|
||||||
|
@ -620,7 +595,7 @@ void placement_finder<DetectorT>::find_line_placements(placement & p, text_place
|
||||||
{
|
{
|
||||||
//Record details for the start of the string placement
|
//Record details for the start of the string placement
|
||||||
int orientation = 0;
|
int orientation = 0;
|
||||||
std::auto_ptr<placement_element> current_placement = get_placement_offset(p, path_positions, path_distances, orientation, index, segment_length - (distance - target_distance) + (diff*dir));
|
std::auto_ptr<placement_element> current_placement = get_placement_offset(path_positions, path_distances, orientation, index, segment_length - (distance - target_distance) + (diff*dir));
|
||||||
|
|
||||||
//We were unable to place here
|
//We were unable to place here
|
||||||
if (current_placement.get() == NULL)
|
if (current_placement.get() == NULL)
|
||||||
|
@ -639,22 +614,20 @@ void placement_finder<DetectorT>::find_line_placements(placement & p, text_place
|
||||||
}
|
}
|
||||||
anglesum /= current_placement->nodes_.size(); //Now it is angle average
|
anglesum /= current_placement->nodes_.size(); //Now it is angle average
|
||||||
|
|
||||||
double disp_x = p.scale_factor_ * displacement*fast_cos(anglesum+M_PI/2);
|
|
||||||
double disp_y = p.scale_factor_ * displacement*fast_sin(anglesum+M_PI/2);
|
|
||||||
//Offset all the characters by this angle
|
//Offset all the characters by this angle
|
||||||
for (unsigned i = 0; i < current_placement->nodes_.size(); i++)
|
for (unsigned i = 0; i < current_placement->nodes_.size(); i++)
|
||||||
{
|
{
|
||||||
current_placement->nodes_[i].x += disp_x;
|
current_placement->nodes_[i].x += pi.get_scale_factor() * displacement*cos(anglesum+M_PI/2);
|
||||||
current_placement->nodes_[i].y += disp_y;
|
current_placement->nodes_[i].y += pi.get_scale_factor() * displacement*sin(anglesum+M_PI/2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool status = test_placement(p, current_placement, orientation);
|
bool status = test_placement(current_placement, orientation);
|
||||||
|
|
||||||
if (status) //We have successfully placed one
|
if (status) //We have successfully placed one
|
||||||
{
|
{
|
||||||
p.placements.push_back(current_placement.release());
|
pi.placements.push_back(current_placement.release());
|
||||||
update_detector(p);
|
update_detector();
|
||||||
|
|
||||||
//Totally break out of the loops
|
//Totally break out of the loops
|
||||||
diff = tolerance;
|
diff = tolerance;
|
||||||
|
@ -663,8 +636,8 @@ void placement_finder<DetectorT>::find_line_placements(placement & p, text_place
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//If we've failed to place, remove all the envelopes we've added up
|
//If we've failed to place, remove all the envelopes we've added up
|
||||||
while (!p.envelopes.empty())
|
while (!pi.envelopes.empty())
|
||||||
p.envelopes.pop();
|
pi.envelopes.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
//Don't need to loop twice when diff = 0
|
//Don't need to loop twice when diff = 0
|
||||||
|
@ -684,7 +657,7 @@ void placement_finder<DetectorT>::find_line_placements(placement & p, text_place
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename DetectorT>
|
template <typename DetectorT>
|
||||||
std::auto_ptr<placement_element> placement_finder<DetectorT>::get_placement_offset(placement & p, const std::vector<vertex2d> &path_positions, const std::vector<double> &path_distances, int &orientation, unsigned index, double distance)
|
std::auto_ptr<placement_element> placement_finder<DetectorT>::get_placement_offset(const std::vector<vertex2d> &path_positions, const std::vector<double> &path_distances, int &orientation, unsigned index, double distance)
|
||||||
{
|
{
|
||||||
//Check that the given distance is on the given index and find the correct index and distance if not
|
//Check that the given distance is on the given index and find the correct index and distance if not
|
||||||
while (distance < 0 && index > 1)
|
while (distance < 0 && index > 1)
|
||||||
|
@ -710,7 +683,6 @@ std::auto_ptr<placement_element> placement_finder<DetectorT>::get_placement_offs
|
||||||
|
|
||||||
std::auto_ptr<placement_element> current_placement(new placement_element);
|
std::auto_ptr<placement_element> current_placement(new placement_element);
|
||||||
|
|
||||||
double string_height = p.info.get_dimensions().second;
|
|
||||||
double old_x = path_positions[index-1].x;
|
double old_x = path_positions[index-1].x;
|
||||||
double old_y = path_positions[index-1].y;
|
double old_y = path_positions[index-1].y;
|
||||||
|
|
||||||
|
@ -728,7 +700,7 @@ std::auto_ptr<placement_element> placement_finder<DetectorT>::get_placement_offs
|
||||||
|
|
||||||
current_placement->starting_x = old_x + dx*distance/segment_length;
|
current_placement->starting_x = old_x + dx*distance/segment_length;
|
||||||
current_placement->starting_y = old_y + dy*distance/segment_length;
|
current_placement->starting_y = old_y + dy*distance/segment_length;
|
||||||
double angle = fast_atan2(-dy, dx);
|
double angle = atan2(-dy, dx);
|
||||||
|
|
||||||
bool orientation_forced = (orientation != 0); //Wether the orientation was set by the caller
|
bool orientation_forced = (orientation != 0); //Wether the orientation was set by the caller
|
||||||
if (!orientation_forced)
|
if (!orientation_forced)
|
||||||
|
@ -736,17 +708,14 @@ std::auto_ptr<placement_element> placement_finder<DetectorT>::get_placement_offs
|
||||||
|
|
||||||
unsigned upside_down_char_count = 0; //Count of characters that are placed upside down.
|
unsigned upside_down_char_count = 0; //Count of characters that are placed upside down.
|
||||||
|
|
||||||
for (unsigned i = 0; i < p.info.num_characters(); ++i)
|
for (unsigned i = 0; i < info_.num_characters(); ++i)
|
||||||
{
|
{
|
||||||
char_info ci;
|
// grab the next character according to the orientation
|
||||||
unsigned c;
|
char_info const &ci = orientation > 0 ? info_.at(i) : info_.at(info_.num_characters() - i - 1);
|
||||||
|
unsigned c = ci.c;
|
||||||
|
|
||||||
double last_character_angle = angle;
|
double last_character_angle = angle;
|
||||||
|
|
||||||
// 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.c;
|
|
||||||
|
|
||||||
//Coordinates this character will start at
|
//Coordinates this character will start at
|
||||||
if (segment_length == 0) {
|
if (segment_length == 0) {
|
||||||
// Not allowed to place across on 0 length segments or discontinuities
|
// Not allowed to place across on 0 length segments or discontinuities
|
||||||
|
@ -826,14 +795,15 @@ std::auto_ptr<placement_element> placement_finder<DetectorT>::get_placement_offs
|
||||||
double render_y = start_y;
|
double render_y = start_y;
|
||||||
|
|
||||||
//Center the text on the line
|
//Center the text on the line
|
||||||
render_x += (((double)string_height/2.0) - 1.0)*sina;
|
double char_height = ci.avg_height;
|
||||||
render_y += (((double)string_height/2.0) - 1.0)*cosa;
|
render_x -= char_height/2.0*cos(render_angle+M_PI/2);
|
||||||
|
render_y += char_height/2.0*sin(render_angle+M_PI/2);
|
||||||
|
|
||||||
if (orientation < 0)
|
if (orientation < 0)
|
||||||
{
|
{
|
||||||
// rotate in place
|
// rotate in place
|
||||||
render_x += ci.width*cosa - (string_height-2)*sina;
|
render_x += ci.width*cos(render_angle) - (char_height-2)*sin(render_angle);
|
||||||
render_y -= ci.width*sina + (string_height-2)*cosa;
|
render_y -= ci.width*sin(render_angle) + (char_height-2)*cos(render_angle);
|
||||||
render_angle += M_PI;
|
render_angle += M_PI;
|
||||||
}
|
}
|
||||||
current_placement->add_node(c,render_x - current_placement->starting_x,
|
current_placement->add_node(c,render_x - current_placement->starting_x,
|
||||||
|
@ -851,13 +821,13 @@ std::auto_ptr<placement_element> placement_finder<DetectorT>::get_placement_offs
|
||||||
}
|
}
|
||||||
|
|
||||||
//If we placed too many characters upside down
|
//If we placed too many characters upside down
|
||||||
if (upside_down_char_count >= p.info.num_characters()/2.0)
|
if (upside_down_char_count >= info_.num_characters()/2.0)
|
||||||
{
|
{
|
||||||
//if we auto-detected the orientation then retry with the opposite orientation
|
//if we auto-detected the orientation then retry with the opposite orientation
|
||||||
if (!orientation_forced)
|
if (!orientation_forced)
|
||||||
{
|
{
|
||||||
orientation = -orientation;
|
orientation = -orientation;
|
||||||
current_placement = get_placement_offset(p, path_positions, path_distances, orientation, initial_index, initial_distance);
|
current_placement = get_placement_offset(path_positions, path_distances, orientation, initial_index, initial_distance);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -871,58 +841,50 @@ std::auto_ptr<placement_element> placement_finder<DetectorT>::get_placement_offs
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename DetectorT>
|
template <typename DetectorT>
|
||||||
bool placement_finder<DetectorT>::test_placement(placement & p, const std::auto_ptr<placement_element> & current_placement, const int & orientation)
|
bool placement_finder<DetectorT>::test_placement(const std::auto_ptr<placement_element> & current_placement, const int & orientation)
|
||||||
{
|
{
|
||||||
std::pair<double, double> string_dimensions = p.info.get_dimensions();
|
|
||||||
|
|
||||||
double string_height = string_dimensions.second;
|
|
||||||
|
|
||||||
|
|
||||||
//Create and test envelopes
|
//Create and test envelopes
|
||||||
bool status = true;
|
bool status = true;
|
||||||
for (unsigned i = 0; i < p.info.num_characters(); ++i)
|
for (unsigned i = 0; i < info_.num_characters(); ++i)
|
||||||
{
|
{
|
||||||
// grab the next character according to the orientation
|
// grab the next character according to the orientation
|
||||||
char_info ci = orientation > 0 ? p.info.at(i) : p.info.at(p.info.num_characters() - i - 1);
|
char_info const& ci = orientation > 0 ? info_.at(i) : info_.at(info_.num_characters() - i - 1);
|
||||||
int c;
|
int c;
|
||||||
double x, y, angle;
|
double x, y, angle;
|
||||||
char_properties *format;
|
char_properties *properties;
|
||||||
current_placement->vertex(&c, &x, &y, &angle, &format);
|
current_placement->vertex(&c, &x, &y, &angle, &properties);
|
||||||
x = current_placement->starting_x + x;
|
x = current_placement->starting_x + x;
|
||||||
y = current_placement->starting_y - y;
|
y = current_placement->starting_y - y;
|
||||||
if (orientation < 0)
|
if (orientation < 0)
|
||||||
{
|
{
|
||||||
double sina = fast_sin(angle);
|
|
||||||
double cosa = fast_cos(angle);
|
|
||||||
// rotate in place
|
// rotate in place
|
||||||
x += ci.width*cosa - (string_height-2)*sina;
|
/* TODO: What's the meaning of -2? */
|
||||||
y -= ci.width*sina + (string_height-2)*cosa;
|
x += ci.width*cos(angle) - (string_height_-2)*sin(angle);
|
||||||
|
y -= ci.width*sin(angle) + (string_height_-2)*cos(angle);
|
||||||
angle += M_PI;
|
angle += M_PI;
|
||||||
}
|
}
|
||||||
|
|
||||||
box2d<double> e;
|
box2d<double> e;
|
||||||
if (p.has_dimensions)
|
if (pi.has_dimensions)
|
||||||
{
|
{
|
||||||
e.init(x, y, x + p.dimensions.first, y + p.dimensions.second);
|
e.init(x, y, x + pi.dimensions.first, y + pi.dimensions.second);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
double sina = fast_sin(angle);
|
|
||||||
double cosa = fast_cos(angle);
|
|
||||||
// put four corners of the letter into envelope
|
// put four corners of the letter into envelope
|
||||||
e.init(x, y, x + ci.width*cosa,
|
e.init(x, y, x + ci.width*cos(angle),
|
||||||
y - ci.width*sina);
|
y - ci.width*sin(angle));
|
||||||
e.expand_to_include(x - ci.height()*sina,
|
e.expand_to_include(x - ci.height()*sin(angle),
|
||||||
y - ci.height()*cosa);
|
y - ci.height()*cos(angle));
|
||||||
e.expand_to_include(x + (ci.width*cosa - ci.height()*sina),
|
e.expand_to_include(x + (ci.width*cos(angle) - ci.height()*sin(angle)),
|
||||||
y - (ci.width*sina + ci.height()*cosa));
|
y - (ci.width*sin(angle) + ci.height()*cos(angle)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!detector_.extent().intersects(e) ||
|
if (!detector_.extent().intersects(e) ||
|
||||||
!detector_.has_placement(e, p.info.get_string(), p.minimum_distance))
|
!detector_.has_placement(e, info_.get_string(), pi.get_actual_minimum_distance()))
|
||||||
{
|
{
|
||||||
//std::clog << "No Intersects:" << !dimensions_.intersects(e) << ": " << e << " @ " << dimensions_ << std::endl;
|
//std::clog << "No Intersects:" << !dimensions_.intersects(e) << ": " << e << " @ " << dimensions_ << std::endl;
|
||||||
//std::clog << "No Placements:" << !detector_.has_placement(e, p.info.get_string(), p.minimum_distance) << std::endl;
|
//std::clog << "No Placements:" << !detector_.has_placement(e, info.get_string(), p.minimum_distance) << std::endl;
|
||||||
status = false;
|
status = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -935,17 +897,18 @@ bool placement_finder<DetectorT>::test_placement(placement & p, const std::auto_
|
||||||
}
|
}
|
||||||
if (p.minimum_padding > 0)
|
if (p.minimum_padding > 0)
|
||||||
{
|
{
|
||||||
box2d<double> epad(e.minx()-p.minimum_padding,
|
double min_pad = pi.get_actual_minimum_padding();
|
||||||
e.miny()-p.minimum_padding,
|
box2d<double> epad(e.minx()-min_pad,
|
||||||
e.maxx()+p.minimum_padding,
|
e.miny()-min_pad,
|
||||||
e.maxy()+p.minimum_padding);
|
e.maxx()+min_pad,
|
||||||
|
e.maxy()+min_pad);
|
||||||
if (!dimensions_.contains(epad))
|
if (!dimensions_.contains(epad))
|
||||||
{
|
{
|
||||||
status = false;
|
status = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.envelopes.push(e);
|
pi.envelopes.push(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
current_placement->rewind();
|
current_placement->rewind();
|
||||||
|
@ -1001,27 +964,27 @@ void placement_finder<DetectorT>::find_line_circle_intersection(
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename DetectorT>
|
template <typename DetectorT>
|
||||||
void placement_finder<DetectorT>::update_detector(placement & p)
|
void placement_finder<DetectorT>::update_detector()
|
||||||
{
|
{
|
||||||
bool first = true;
|
bool first = true;
|
||||||
|
|
||||||
// add the bboxes to the detector and remove from the placement
|
// add the bboxes to the detector and remove from the placement
|
||||||
while (!p.envelopes.empty())
|
while (!pi.envelopes.empty())
|
||||||
{
|
{
|
||||||
box2d<double> e = p.envelopes.front();
|
box2d<double> e = pi.envelopes.front();
|
||||||
detector_.insert(e, p.info.get_string());
|
detector_.insert(e, info_.get_string());
|
||||||
p.envelopes.pop();
|
pi.envelopes.pop();
|
||||||
|
|
||||||
if (p.collect_extents)
|
if (pi.collect_extents)
|
||||||
{
|
{
|
||||||
if(first)
|
if(first)
|
||||||
{
|
{
|
||||||
first = false;
|
first = false;
|
||||||
p.extents = e;
|
pi.extents = e;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
p.extents.expand_to_include(e);
|
pi.extents.expand_to_include(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1033,11 +996,58 @@ void placement_finder<DetectorT>::clear()
|
||||||
detector_.clear();
|
detector_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename DetectorT>
|
||||||
|
void placement_finder<DetectorT>::find_placement(double angle, geometry_type const& geom, CoordTransform const& t, proj_transform const& prj_trans)
|
||||||
|
{
|
||||||
|
double label_x=0.0;
|
||||||
|
double label_y=0.0;
|
||||||
|
double z=0.0;
|
||||||
|
if (p.label_placement == POINT_PLACEMENT ||
|
||||||
|
p.label_placement == VERTEX_PLACEMENT ||
|
||||||
|
p.label_placement == INTERIOR_PLACEMENT)
|
||||||
|
{
|
||||||
|
unsigned iterations = 1;
|
||||||
|
if (p.label_placement == VERTEX_PLACEMENT)
|
||||||
|
{
|
||||||
|
iterations = geom.num_points();
|
||||||
|
geom.rewind(0);
|
||||||
|
}
|
||||||
|
for(unsigned jj = 0; jj < iterations; jj++) {
|
||||||
|
switch (p.label_placement)
|
||||||
|
{
|
||||||
|
case POINT_PLACEMENT:
|
||||||
|
geom.label_position(&label_x, &label_y);
|
||||||
|
break;
|
||||||
|
case INTERIOR_PLACEMENT:
|
||||||
|
geom.label_interior_position(&label_x, &label_y);
|
||||||
|
break;
|
||||||
|
case VERTEX_PLACEMENT:
|
||||||
|
geom.vertex(&label_x, &label_y);
|
||||||
|
break;
|
||||||
|
case LINE_PLACEMENT:
|
||||||
|
case label_placement_enum_MAX:
|
||||||
|
/*not handled here*/
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
prj_trans.backward(label_x, label_y, z);
|
||||||
|
t.forward(&label_x, &label_y);
|
||||||
|
|
||||||
|
find_point_placement(label_x, label_y, angle);
|
||||||
|
}
|
||||||
|
update_detector();
|
||||||
|
} else if (p.label_placement == LINE_PLACEMENT && geom.num_points() > 1)
|
||||||
|
{
|
||||||
|
typedef coord_transform2<CoordTransform,geometry_type> path_type;
|
||||||
|
path_type path(t, geom, prj_trans);
|
||||||
|
find_line_placements<path_type>(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
typedef coord_transform2<CoordTransform,geometry_type> PathType;
|
typedef coord_transform2<CoordTransform,geometry_type> PathType;
|
||||||
typedef label_collision_detector4 DetectorType;
|
typedef label_collision_detector4 DetectorType;
|
||||||
|
|
||||||
template class placement_finder<DetectorType>;
|
template class placement_finder<DetectorType>;
|
||||||
template void placement_finder<DetectorType>::find_point_placements<PathType> (placement&, text_placement_info_ptr po, PathType & );
|
template void placement_finder<DetectorType>::find_point_placements<PathType>(PathType &);
|
||||||
template void placement_finder<DetectorType>::find_line_placements<PathType> (placement&, text_placement_info_ptr po, PathType & );
|
template void placement_finder<DetectorType>::find_line_placements<PathType>(PathType &);
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
Loading…
Reference in a new issue