+ add offset_converter

This commit is contained in:
Artem Pavlenko 2012-05-02 15:13:46 +01:00
parent 642899e52c
commit a475b6e0bc
6 changed files with 288 additions and 229 deletions

View file

@ -30,15 +30,9 @@
#include <mapnik/coord_array.hpp>
#include <mapnik/proj_transform.hpp>
// boost
#include <boost/math/constants/constants.hpp>
// stl
#include <algorithm>
const double pi = boost::math::constants::pi<double>();
const double pi_by_2 = pi/2.0;
namespace mapnik
{
@ -173,221 +167,6 @@ private:
};
// TODO - expose this and make chainable
template <typename Transform,typename Geometry>
struct MAPNIK_DECL coord_transform_parallel
{
typedef std::size_t size_type;
typedef typename Geometry::value_type value_type;
coord_transform_parallel(Transform const& t,
Geometry const& geom,
proj_transform const& prj_trans )
: t_(t),
geom_(geom),
prj_trans_(prj_trans),
offset_(0.0),
threshold_(10),
m_status(initial) {}
enum status
{
initial,
start,
first,
process,
last_vertex,
angle_joint,
end
};
double get_offset() const
{
return offset_;
}
void set_offset(double offset)
{
offset_ = offset;
}
unsigned int get_threshold() const
{
return threshold_;
}
void set_threshold(unsigned int t)
{
threshold_ = t;
}
unsigned vertex(double * x , double * y)
{
double z=0;
if (offset_==0.0)
{
unsigned command = geom_.vertex(x,y);
prj_trans_.backward(*x,*y,z);
t_.forward(x,y);
return command;
}
else
{
while(true){
switch(m_status)
{
case end:
return SEG_END;
break;
case initial:
m_pre_cmd = geom_.vertex(x,y);
prj_trans_.backward(*x,*y,z);
t_.forward(x,y);
m_pre_x = *x;
m_pre_y = *y;
//m_status = (m_pre_cmd!=SEG_END)?start:end; //
case start:
m_cur_cmd = geom_.vertex(&m_cur_x, &m_cur_y);
prj_trans_.backward(m_cur_x,m_cur_y,z);
t_.forward(&m_cur_x,&m_cur_y);
case first:
angle_a = atan2((m_pre_y-m_cur_y),(m_pre_x-m_cur_x));
dx_pre = cos(angle_a + pi_by_2);
dy_pre = sin(angle_a + pi_by_2);
MAPNIK_LOG_DEBUG(ctrans) << "coord_transform_parallel: Offsetting line by=" << offset_;
MAPNIK_LOG_DEBUG(ctrans) << "coord_transform_parallel: Initial dx=" << (dx_pre * offset_) << ",dy=" << (dy_pre * offset_);
*x = m_pre_x + (dx_pre * offset_);
*y = m_pre_y + (dy_pre * offset_);
m_status = process;
return SEG_MOVETO;
case process:
switch(m_cur_cmd)
{
case SEG_LINETO:
m_next_cmd = geom_.vertex(&m_next_x, &m_next_y);
prj_trans_.backward(m_next_x,m_next_y,z);
t_.forward(&m_next_x,&m_next_y);
switch(m_next_cmd)
{
case SEG_LINETO:
m_status = angle_joint;
break;
default:
m_status = last_vertex;
break;
}
break;
case SEG_END:
m_status = end;
return SEG_END;
}
break;
case last_vertex:
dx_curr = cos(angle_a + pi_by_2);
dy_curr = sin(angle_a + pi_by_2);
*x = m_cur_x + (dx_curr * offset_);
*y = m_cur_y + (dy_curr * offset_);
m_status = end;
return m_cur_cmd;
case angle_joint:
angle_b = atan2((m_cur_y-m_next_y),(m_cur_x-m_next_x));
h = tan((angle_b - angle_a)/2.0);
if (fabs(h) < threshold_)
{
dx_curr = cos(angle_a + pi_by_2);
dy_curr = sin(angle_a + pi_by_2);
*x = m_cur_x + (dx_curr * offset_) - h * (dy_curr * offset_);
*y = m_cur_y + (dy_curr * offset_) + h * (dx_curr * offset_);
}
else // skip sharp spikes
{
#ifdef MAPNIK_LOG
dx_curr = cos(angle_a + pi_by_2);
dy_curr = sin(angle_a + pi_by_2);
sin_curve = dx_curr*dy_pre-dy_curr*dx_pre;
MAPNIK_LOG_DEBUG(ctrans) << "coord_transform_parallel: angle a=" << angle_a;
MAPNIK_LOG_DEBUG(ctrans) << "coord_transform_parallel: angle b=" << angle_b;
MAPNIK_LOG_DEBUG(ctrans) << "coord_transform_parallel: h=" << h;
MAPNIK_LOG_DEBUG(ctrans) << "coord_transform_parallel: sin_curve=" << sin_curve;
#endif
m_status = process;
break;
}
// alternate sharp spike fix, but suboptimal...
/*
sin_curve = dx_curr*dy_pre-dy_curr*dx_pre;
cos_curve = -dx_pre*dx_curr-dy_pre*dy_curr;
MAPNIK_LOG_DEBUG(ctrans) << "coord_transform_parallel: sin_curve value=" << sin_curve;
if(sin_curve > -0.3 && sin_curve < 0.3) {
angle_b = atan2((m_cur_y-m_next_y),(m_cur_x-m_next_x));
h = tan((angle_b - angle_a)/2.0);
*x = m_cur_x + (dx_curr * offset_) - h * (dy_curr * offset_);
*y = m_cur_y + (dy_curr * offset_) + h * (dx_curr * offset_);
} else {
if (angle_b - angle_a > 0)
h = -1.0*(1.0+cos_curve)/sin_curve;
else
h = (1.0+cos_curve)/sin_curve;
*x = m_cur_x + (dx_curr + base_shift*dy_curr)*offset_;
*y = m_cur_y + (dy_curr - base_shift*dx_curr)*offset_;
}
*/
m_pre_x = *x;
m_pre_x = *y;
m_cur_x = m_next_x;
m_cur_y = m_next_y;
angle_a = angle_b;
m_pre_cmd = m_cur_cmd;
m_cur_cmd = m_next_cmd;
m_status = process;
return m_pre_cmd;
}
}
}
}
void rewind (unsigned pos)
{
geom_.rewind(pos);
m_status = initial;
}
private:
Transform const& t_;
Geometry const& geom_;
proj_transform const& prj_trans_;
int offset_;
unsigned int threshold_;
status m_status;
double dx_pre;
double dy_pre;
double dx_curr;
double dy_curr;
double sin_curve;
double cos_curve;
double angle_a;
double angle_b;
double h;
unsigned m_pre_cmd;
double m_pre_x;
double m_pre_y;
unsigned m_cur_cmd;
double m_cur_x;
double m_cur_y;
unsigned m_next_cmd;
double m_next_x;
double m_next_y;
};
class CoordTransform
{
private:

View file

@ -45,19 +45,25 @@ struct MAPNIK_DECL line_symbolizer : public symbolizer_base
: symbolizer_base(),
stroke_(),
rasterizer_p_(RASTERIZER_FULL),
smooth_(0.0) {}
smooth_(0.0),
offset_(0.0)
{}
line_symbolizer(stroke const& stroke)
: symbolizer_base(),
stroke_(stroke),
rasterizer_p_(RASTERIZER_FULL),
smooth_(0.0) {}
smooth_(0.0),
offset_(0.0)
{}
line_symbolizer(color const& pen,float width=1.0)
: symbolizer_base(),
stroke_(pen,width),
rasterizer_p_(RASTERIZER_FULL),
smooth_(0.0) {}
smooth_(0.0),
offset_(0.0)
{}
stroke const& get_stroke() const
{
@ -89,10 +95,21 @@ struct MAPNIK_DECL line_symbolizer : public symbolizer_base
return smooth_;
}
void set_offset(double val)
{
offset_ = val;
}
double offset() const
{
return offset_;
}
private:
stroke stroke_;
line_rasterizer_e rasterizer_p_;
double smooth_;
double offset_;
};
}

View file

@ -0,0 +1,241 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2012 Artem Pavlenko
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*****************************************************************************/
#ifndef MAPNIK_OFFSET_CONVERTER_HPP
#define MAPNIK_OFFSET_CONVERTER_HPP
#include <mapnik/debug.hpp>
#include <mapnik/box2d.hpp>
#include <mapnik/vertex.hpp>
#include <mapnik/coord_array.hpp>
#include <mapnik/proj_transform.hpp>
// boost
#include <boost/math/constants/constants.hpp>
namespace mapnik
{
const double pi = boost::math::constants::pi<double>();
const double half_pi = pi/2.0;
template <typename Geometry>
struct MAPNIK_DECL offset_converter
{
typedef std::size_t size_type;
//typedef typename Geometry::value_type value_type;
offset_converter(Geometry & geom)
: geom_(geom),
offset_(0.0),
threshold_(10),
status_(initial) {}
enum status
{
initial,
start,
first,
process,
last_vertex,
angle_joint,
end
};
double get_offset() const
{
return offset_;
}
void set_offset(double offset)
{
offset_ = offset;
}
unsigned int get_threshold() const
{
return threshold_;
}
void set_threshold(unsigned int t)
{
threshold_ = t;
}
unsigned vertex(double * x , double * y)
{
if (offset_==0.0)
{
unsigned command = geom_.vertex(x,y);
return command;
}
else
{
while(true)
{
switch(status_)
{
case end:
return SEG_END;
break;
case initial:
pre_cmd_ = geom_.vertex(x,y);
pre_x_ = *x;
pre_y_ = *y;
//status_ = (pre_cmd_!=SEG_END)?start:end; //
case start:
cur_cmd_ = geom_.vertex(&cur_x_, &cur_y_);
case first:
angle_a = atan2((pre_y_-cur_y_),(pre_x_-cur_x_));
dx_pre = cos(angle_a + half_pi);
dy_pre = sin(angle_a + half_pi);
MAPNIK_LOG_DEBUG(ctrans) << "coord_transform_parallel: Offsetting line by=" << offset_;
MAPNIK_LOG_DEBUG(ctrans) << "coord_transform_parallel: Initial dx=" << (dx_pre * offset_) << ",dy=" << (dy_pre * offset_);
*x = pre_x_ + (dx_pre * offset_);
*y = pre_y_ + (dy_pre * offset_);
status_ = process;
return SEG_MOVETO;
case process:
switch(cur_cmd_)
{
case SEG_LINETO:
next_cmd_ = geom_.vertex(&next_x_, &next_y_);
switch(next_cmd_)
{
case SEG_LINETO:
status_ = angle_joint;
break;
default:
status_ = last_vertex;
break;
}
break;
case SEG_END:
status_ = end;
return SEG_END;
}
break;
case last_vertex:
dx_curr = cos(angle_a + half_pi);
dy_curr = sin(angle_a + half_pi);
*x = cur_x_ + (dx_curr * offset_);
*y = cur_y_ + (dy_curr * offset_);
status_ = end;
return cur_cmd_;
case angle_joint:
angle_b = atan2((cur_y_-next_y_),(cur_x_-next_x_));
h = tan((angle_b - angle_a)/2.0);
if (fabs(h) < threshold_)
{
dx_curr = cos(angle_a + half_pi);
dy_curr = sin(angle_a + half_pi);
*x = cur_x_ + (dx_curr * offset_) - h * (dy_curr * offset_);
*y = cur_y_ + (dy_curr * offset_) + h * (dx_curr * offset_);
}
else // skip sharp spikes
{
#ifdef MAPNIK_LOG
dx_curr = cos(angle_a + half_pi);
dy_curr = sin(angle_a + half_pi);
sin_curve = dx_curr*dy_pre-dy_curr*dx_pre;
MAPNIK_LOG_DEBUG(ctrans) << "coord_transform_parallel: angle a=" << angle_a;
MAPNIK_LOG_DEBUG(ctrans) << "coord_transform_parallel: angle b=" << angle_b;
MAPNIK_LOG_DEBUG(ctrans) << "coord_transform_parallel: h=" << h;
MAPNIK_LOG_DEBUG(ctrans) << "coord_transform_parallel: sin_curve=" << sin_curve;
#endif
status_ = process;
break;
}
// alternate sharp spike fix, but suboptimal...
/*
sin_curve = dx_curr*dy_pre-dy_curr*dx_pre;
cos_curve = -dx_pre*dx_curr-dy_pre*dy_curr;
MAPNIK_LOG_DEBUG(ctrans) << "coord_transform_parallel: sin_curve value=" << sin_curve;
if(sin_curve > -0.3 && sin_curve < 0.3) {
angle_b = atan2((cur_y_-next_y_),(cur_x_-next_x_));
h = tan((angle_b - angle_a)/2.0);
*x = cur_x_ + (dx_curr * offset_) - h * (dy_curr * offset_);
*y = cur_y_ + (dy_curr * offset_) + h * (dx_curr * offset_);
} else {
if (angle_b - angle_a > 0)
h = -1.0*(1.0+cos_curve)/sin_curve;
else
h = (1.0+cos_curve)/sin_curve;
*x = cur_x_ + (dx_curr + base_shift*dy_curr)*offset_;
*y = cur_y_ + (dy_curr - base_shift*dx_curr)*offset_;
}
*/
pre_x_ = *x;
pre_x_ = *y;
cur_x_ = next_x_;
cur_y_ = next_y_;
angle_a = angle_b;
pre_cmd_ = cur_cmd_;
cur_cmd_ = next_cmd_;
status_ = process;
return pre_cmd_;
}
}
}
}
void rewind (unsigned pos)
{
geom_.rewind(pos);
status_ = initial;
}
private:
Geometry & geom_;
int offset_;
unsigned int threshold_;
status status_;
double dx_pre;
double dy_pre;
double dx_curr;
double dy_curr;
double sin_curve;
double cos_curve;
double angle_a;
double angle_b;
double h;
unsigned pre_cmd_;
double pre_x_;
double pre_y_;
unsigned cur_cmd_;
double cur_x_;
double cur_y_;
unsigned next_cmd_;
double next_x_;
double next_y_;
};
}
#endif // MAPNIK_OFFSET_CONVERTER_HPP

View file

@ -48,7 +48,7 @@
// mapnik
#include <mapnik/agg_helpers.hpp>
#include <mapnik/offset_converter.hpp>
// agg
#include "agg_conv_clip_polygon.h"
#include "agg_conv_clip_polyline.h"
@ -66,6 +66,7 @@ struct smooth_tag {};
struct stroke_tag {};
struct dash_tag {};
struct affine_transform_tag {};
struct offset_transform_tag {};
namespace detail {
@ -202,6 +203,20 @@ struct converter_traits<T,mapnik::affine_transform_tag>
}
};
template <typename T>
struct converter_traits<T,mapnik::offset_transform_tag>
{
typedef T geometry_type;
typedef offset_converter<geometry_type> conv_type;
template <typename Args>
static void setup(geometry_type & geom, Args & args)
{
typename boost::mpl::at<Args,boost::mpl::int_<2> >::type sym = boost::fusion::at_c<2>(args);
geom.set_offset(sym.offset());
}
};
template <bool>
struct converter_fwd
{

View file

@ -102,13 +102,15 @@ void agg_renderer<T>::process(line_symbolizer const& sym,
ras_ptr->reset();
set_gamma_method(stroke_, ras_ptr);
//metawriter_with_properties writer = sym.get_metawriter();
typedef boost::mpl::vector<clip_line_tag,transform_tag, affine_transform_tag, smooth_tag, dash_tag, stroke_tag> conv_types;
typedef boost::mpl::vector<clip_line_tag,transform_tag, offset_transform_tag,smooth_tag, dash_tag, stroke_tag> conv_types;
vertex_converter<box2d<double>,rasterizer,line_symbolizer, proj_transform, CoordTransform,conv_types>
converter(ext,*ras_ptr,sym,t_,prj_trans);
if (sym.clip()) converter.set<clip_line_tag>(); //optional clip (default: true)
converter.set<transform_tag>(); //always transform
converter.set<affine_transform_tag>(); // optional affine transform
if (sym.clip()) converter.set<clip_line_tag>(); // optional clip (default: true)
converter.set<transform_tag>(); // always transform
if (sym.offset() > 0.0) converter.set<offset_transform_tag>(); // parallel offset
//converter.set<affine_transform_tag>(); // optional affine transform
if (sym.smooth() > 0.0) converter.set<smooth_tag>(); // optional smooth converter
if (stroke_.has_dash()) converter.set<dash_tag>();
converter.set<stroke_tag>(); //always stroke

View file

@ -1430,6 +1430,11 @@ void map_parser::parse_line_symbolizer(rule & rule, xml_node const & sym)
// smooth value
optional<double> smooth = sym.get_opt_attr<double>("smooth");
if (smooth) symbol.set_smooth(*smooth);
// offset value
optional<double> offset = sym.get_opt_attr<double>("offset");
if (offset) symbol.set_offset(*offset);
// meta-writer
parse_metawriter_in_symbolizer(symbol, sym);
rule.append(symbol);