add extend_converter
This commit is contained in:
parent
237022719a
commit
8733622885
2 changed files with 360 additions and 0 deletions
232
include/mapnik/extend_converter.hpp
Normal file
232
include/mapnik/extend_converter.hpp
Normal file
|
@ -0,0 +1,232 @@
|
|||
/*****************************************************************************
|
||||
*
|
||||
* This file is part of Mapnik (c++ mapping toolkit)
|
||||
*
|
||||
* Copyright (C) 2015 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_EXTEND_CONVERTER_HPP
|
||||
#define MAPNIK_EXTEND_CONVERTER_HPP
|
||||
|
||||
#include <mapnik/vertex.hpp>
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#include <mapnik/warning_ignore.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/msm/back/state_machine.hpp>
|
||||
#include <boost/msm/front/state_machine_def.hpp>
|
||||
#include <boost/msm/front/functor_row.hpp>
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
// stl
|
||||
#include <cmath>
|
||||
|
||||
namespace mapnik
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
namespace msm = boost::msm;
|
||||
namespace mpl = boost::mpl;
|
||||
using namespace msm::front;
|
||||
|
||||
template <typename T>
|
||||
T extend(T const & v1, T const & v2, double length)
|
||||
{
|
||||
double dx = v2.x - v1.x;
|
||||
double dy = v2.y - v1.y;
|
||||
double l12 = std::sqrt(dx * dx + dy * dy);
|
||||
double coef = 1.0 + length / l12;
|
||||
return vertex2d(v1.x + dx * coef,
|
||||
v1.y + dy * coef, v2.cmd);
|
||||
}
|
||||
|
||||
namespace events
|
||||
{
|
||||
struct vertex_event
|
||||
{
|
||||
vertex_event(vertex2d const & vertex) : vertex(vertex) { }
|
||||
vertex2d const & vertex;
|
||||
};
|
||||
|
||||
struct move_to : vertex_event { using vertex_event::vertex_event; };
|
||||
struct line_to : vertex_event { using vertex_event::vertex_event; };
|
||||
struct close : vertex_event { using vertex_event::vertex_event; };
|
||||
struct end : vertex_event { using vertex_event::vertex_event; };
|
||||
}
|
||||
|
||||
namespace actions
|
||||
{
|
||||
struct store
|
||||
{
|
||||
template <class FSM, class EVT, class SourceState, class TargetState>
|
||||
void operator()(EVT const & e, FSM & m, SourceState&, TargetState&)
|
||||
{
|
||||
m.v2 = m.v1;
|
||||
m.v1 = e.vertex;
|
||||
m.output = boost::none;
|
||||
}
|
||||
};
|
||||
|
||||
struct output
|
||||
{
|
||||
template <class FSM, class EVT, class SourceState, class TargetState>
|
||||
void operator()(EVT const & e, FSM & m, SourceState&, TargetState&)
|
||||
{
|
||||
m.output = e.vertex;
|
||||
}
|
||||
};
|
||||
|
||||
struct store_and_output
|
||||
{
|
||||
template <class FSM, class EVT, class SourceState, class TargetState>
|
||||
void operator()(EVT const & e, FSM & m, SourceState&, TargetState&)
|
||||
{
|
||||
m.v2 = m.v1;
|
||||
m.v1 = e.vertex;
|
||||
m.output = m.v2;
|
||||
}
|
||||
};
|
||||
|
||||
struct output_begin
|
||||
{
|
||||
template <class FSM, class EVT, class SourceState, class TargetState>
|
||||
void operator()(EVT const & e, FSM & m, SourceState&, TargetState&)
|
||||
{
|
||||
m.v2 = m.v1;
|
||||
m.v1 = e.vertex;
|
||||
m.output = extend(m.v1, m.v2, m.extend_length);
|
||||
}
|
||||
};
|
||||
|
||||
struct output_end
|
||||
{
|
||||
template <class FSM, class EVT, class SourceState, class TargetState>
|
||||
void operator()(EVT const & e, FSM & m, SourceState&, TargetState&)
|
||||
{
|
||||
m.output = extend(m.v2, m.v1, m.extend_length);
|
||||
m.v1 = e.vertex;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
struct extender_def : public msm::front::state_machine_def<extender_def>
|
||||
{
|
||||
using no_exception_thrown = int;
|
||||
using no_message_queue = int;
|
||||
|
||||
struct initial : public msm::front::state<> { };
|
||||
struct vertex_one : public msm::front::state<> { };
|
||||
struct vertex_two : public msm::front::state<> { };
|
||||
struct end : public msm::front::state<> { };
|
||||
|
||||
using initial_state = initial;
|
||||
|
||||
struct transition_table : mpl::vector<
|
||||
// Start Event Next Action Guard
|
||||
// +------------+-----------------+------------+--------------------+------+
|
||||
Row < initial , events::move_to , vertex_one , actions::store >,
|
||||
Row < initial , events::line_to , vertex_one , actions::store >,
|
||||
Row < initial , events::close , initial >,
|
||||
Row < initial , events::end , end , actions::output >,
|
||||
Row < vertex_one , events::move_to , vertex_one , actions::store_and_output >,
|
||||
Row < vertex_one , events::line_to , vertex_two , actions::output_begin >,
|
||||
Row < vertex_one , events::close , initial , actions::store_and_output >,
|
||||
Row < vertex_one , events::end , end , actions::store_and_output >,
|
||||
Row < vertex_two , events::move_to , vertex_one , actions::output_end >,
|
||||
Row < vertex_two , events::line_to , vertex_two , actions::store_and_output >,
|
||||
Row < vertex_two , events::close , initial , actions::output_end >,
|
||||
Row < vertex_two , events::end , end , actions::output_end >,
|
||||
Row < end , events::end , end , actions::output >
|
||||
> {};
|
||||
|
||||
extender_def(double extend_length)
|
||||
: extend_length(extend_length)
|
||||
{
|
||||
}
|
||||
|
||||
boost::optional<vertex2d> output;
|
||||
vertex2d v1, v2;
|
||||
double extend_length;
|
||||
};
|
||||
|
||||
using extender = msm::back::state_machine<extender_def>;
|
||||
|
||||
}
|
||||
|
||||
template <typename Geometry>
|
||||
struct extend_converter
|
||||
{
|
||||
extend_converter(Geometry & geom)
|
||||
: extend_converter(geom, 0)
|
||||
{}
|
||||
|
||||
extend_converter(Geometry & geom, double extend)
|
||||
: geom_(geom), extender_(extend)
|
||||
{}
|
||||
|
||||
void set_extend(double extend)
|
||||
{
|
||||
extender_.extend_length = extend;
|
||||
}
|
||||
|
||||
unsigned vertex(double * x, double * y)
|
||||
{
|
||||
using namespace detail;
|
||||
vertex2d v;
|
||||
do
|
||||
{
|
||||
v.cmd = geom_.vertex(&v.x, &v.y);
|
||||
switch (v.cmd)
|
||||
{
|
||||
case SEG_MOVETO:
|
||||
extender_.process_event(events::move_to(v));
|
||||
break;
|
||||
case SEG_LINETO:
|
||||
extender_.process_event(events::line_to(v));
|
||||
break;
|
||||
case SEG_CLOSE:
|
||||
extender_.process_event(events::close(v));
|
||||
break;
|
||||
case SEG_END:
|
||||
extender_.process_event(events::end(v));
|
||||
break;
|
||||
}
|
||||
} while(!extender_.output);
|
||||
|
||||
vertex2d const & output = *extender_.output;
|
||||
*x = output.x;
|
||||
*y = output.y;
|
||||
return output.cmd;
|
||||
}
|
||||
|
||||
void rewind(unsigned)
|
||||
{
|
||||
geom_.rewind(0);
|
||||
extender_.start();
|
||||
}
|
||||
|
||||
private:
|
||||
Geometry & geom_;
|
||||
detail::extender extender_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // MAPNIK_EXTEND_CONVERTER_HPP
|
128
test/unit/vertex_adapter/extend_converter.cpp
Normal file
128
test/unit/vertex_adapter/extend_converter.cpp
Normal file
|
@ -0,0 +1,128 @@
|
|||
#include "catch.hpp"
|
||||
#include "fake_path.hpp"
|
||||
|
||||
// mapnik
|
||||
#include <mapnik/extend_converter.hpp>
|
||||
|
||||
// stl
|
||||
#include <iostream>
|
||||
|
||||
namespace offset_test {
|
||||
|
||||
TEST_CASE("extend converter") {
|
||||
|
||||
SECTION("empty") {
|
||||
try
|
||||
{
|
||||
fake_path path = {};
|
||||
mapnik::extend_converter<fake_path> c(path, 1000);
|
||||
double x, y;
|
||||
REQUIRE(c.vertex(&x, &y) == mapnik::SEG_END);
|
||||
}
|
||||
catch (std::exception const& ex)
|
||||
{
|
||||
std::cerr << ex.what() << "\n";
|
||||
REQUIRE(false);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("one point") {
|
||||
try
|
||||
{
|
||||
fake_path path = { 0, 0 };
|
||||
mapnik::extend_converter<fake_path> c(path, 1000);
|
||||
double x, y;
|
||||
REQUIRE(c.vertex(&x, &y) == mapnik::SEG_MOVETO);
|
||||
REQUIRE(x == 0);
|
||||
REQUIRE(y == 0);
|
||||
REQUIRE(c.vertex(&x, &y) == mapnik::SEG_END);
|
||||
REQUIRE(c.vertex(&x, &y) == mapnik::SEG_END);
|
||||
}
|
||||
catch (std::exception const& ex)
|
||||
{
|
||||
std::cerr << ex.what() << "\n";
|
||||
REQUIRE(false);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("two points") {
|
||||
try
|
||||
{
|
||||
fake_path path = { 0, 0 , 1, 0};
|
||||
mapnik::extend_converter<fake_path> c(path, 1000);
|
||||
double x, y;
|
||||
REQUIRE(c.vertex(&x, &y) == mapnik::SEG_MOVETO);
|
||||
REQUIRE(x == -1000);
|
||||
REQUIRE(y == 0);
|
||||
REQUIRE(c.vertex(&x, &y) == mapnik::SEG_LINETO);
|
||||
REQUIRE(x == 1001);
|
||||
REQUIRE(y == 0);
|
||||
REQUIRE(c.vertex(&x, &y) == mapnik::SEG_END);
|
||||
}
|
||||
catch (std::exception const& ex)
|
||||
{
|
||||
std::cerr << ex.what() << "\n";
|
||||
REQUIRE(false);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("three points") {
|
||||
try
|
||||
{
|
||||
fake_path path = { 0, 0, 1, 0, 2, 0 };
|
||||
mapnik::extend_converter<fake_path> c(path, 1000);
|
||||
double x, y;
|
||||
REQUIRE(c.vertex(&x, &y) == mapnik::SEG_MOVETO);
|
||||
REQUIRE(x == -1000);
|
||||
REQUIRE(y == 0);
|
||||
REQUIRE(c.vertex(&x, &y) == mapnik::SEG_LINETO);
|
||||
REQUIRE(x == 1);
|
||||
REQUIRE(y == 0);
|
||||
REQUIRE(c.vertex(&x, &y) == mapnik::SEG_LINETO);
|
||||
REQUIRE(x == 1002);
|
||||
REQUIRE(y == 0);
|
||||
REQUIRE(c.vertex(&x, &y) == mapnik::SEG_END);
|
||||
}
|
||||
catch (std::exception const& ex)
|
||||
{
|
||||
std::cerr << ex.what() << "\n";
|
||||
REQUIRE(false);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("more points") {
|
||||
try
|
||||
{
|
||||
fake_path path = { 0, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0 };
|
||||
mapnik::extend_converter<fake_path> c(path, 1000);
|
||||
double x, y;
|
||||
REQUIRE(c.vertex(&x, &y) == mapnik::SEG_MOVETO);
|
||||
REQUIRE(x == -1000);
|
||||
REQUIRE(y == 0);
|
||||
REQUIRE(c.vertex(&x, &y) == mapnik::SEG_LINETO);
|
||||
REQUIRE(x == 1);
|
||||
REQUIRE(y == 0);
|
||||
REQUIRE(c.vertex(&x, &y) == mapnik::SEG_LINETO);
|
||||
REQUIRE(x == 2);
|
||||
REQUIRE(y == 0);
|
||||
REQUIRE(c.vertex(&x, &y) == mapnik::SEG_LINETO);
|
||||
REQUIRE(x == 3);
|
||||
REQUIRE(y == 0);
|
||||
REQUIRE(c.vertex(&x, &y) == mapnik::SEG_LINETO);
|
||||
REQUIRE(x == 4);
|
||||
REQUIRE(y == 0);
|
||||
REQUIRE(c.vertex(&x, &y) == mapnik::SEG_LINETO);
|
||||
REQUIRE(x == 1005);
|
||||
REQUIRE(y == 0);
|
||||
REQUIRE(c.vertex(&x, &y) == mapnik::SEG_END);
|
||||
}
|
||||
catch (std::exception const& ex)
|
||||
{
|
||||
std::cerr << ex.what() << "\n";
|
||||
REQUIRE(false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue