SVG transform - implement in terms of boost::spirit::x3

This commit is contained in:
artemp 2016-12-23 16:32:05 +01:00
parent 3b52ae97c0
commit ed648ced46
8 changed files with 236 additions and 243 deletions

View file

@ -33,6 +33,7 @@ namespace mapnik { namespace svg { namespace grammar {
class relative_tag;
class svg_path_tag;
class svg_transform_tag;
namespace x3 = boost::spirit::x3;
using space_type = x3::standard::space_type;
@ -42,6 +43,9 @@ using phrase_parse_context_type = x3::phrase_parse_context<space_type>::type;
using svg_parse_context_type = x3::with_context<relative_tag, std::reference_wrapper<bool> const,
x3::with_context<svg_path_tag,std::reference_wrapper<svg_converter_type> const,
phrase_parse_context_type>::type>::type;
inline double deg2rad(double deg) {return (M_PI * deg) / 180.0;}
}}}
#endif // MAPNIK_SVG_GRAMMAR_CONFIG_X3_HPP

View file

@ -102,10 +102,6 @@ auto const curve3_smooth = [] (auto const& ctx)
x3::get<relative_tag>(ctx));
};
inline double deg2rad(double deg)
{
return (M_PI * deg) / 180.0;
}
auto const arc_to = [] (auto & ctx)
{
@ -136,10 +132,11 @@ auto const absolute = [] (auto const& ctx)
x3::get<relative_tag>(ctx).get() = false;
};
// exported rules
svg_path_grammar_type const svg_path = "SVG Path";
svg_points_grammar_type const svg_points = "SVG_Points";
// rules
auto const coord = x3::rule<class coord_tag, coord_type>{} = double_ > -lit(',') > double_;
auto const svg_points_def = coord[move_to] // move_to

View file

@ -39,12 +39,12 @@ template <typename PathType>
bool parse_points(const char* wkt, PathType& p);
template <typename TransformType>
bool MAPNIK_DECL parse_svg_transform(const char* wkt, TransformType& tr);
bool parse_svg_transform(const char* wkt, TransformType& tr);
//
extern template bool MAPNIK_DECL parse_path<svg_converter_type>(const char*, svg_converter_type&);
extern template bool MAPNIK_DECL parse_points<svg_converter_type>(const char*, svg_converter_type&);
extern template bool MAPNIK_DECL parse_svg_transform<svg_converter_type>(const char*, svg_converter_type&);
extern template bool MAPNIK_DECL parse_svg_transform<agg::trans_affine>(const char*, agg::trans_affine&);
}
}

View file

@ -1,56 +0,0 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2016 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_SVG_TRANSFORM_GRAMMAR_HPP
#define MAPNIK_SVG_TRANSFORM_GRAMMAR_HPP
// mapnik
#include <mapnik/global.hpp>
#pragma GCC diagnostic push
#include <mapnik/warning_ignore.hpp>
#include <boost/spirit/include/qi.hpp>
#pragma GCC diagnostic pop
namespace mapnik { namespace svg {
using namespace boost::spirit;
template <typename Iterator, typename TransformType, typename SkipType>
struct svg_transform_grammar : qi::grammar<Iterator, void(TransformType&), SkipType>
{
// ctor
svg_transform_grammar();
// rules
qi::rule<Iterator, void(TransformType&), SkipType> start;
qi::rule<Iterator, void(TransformType&), SkipType> transform_;
qi::rule<Iterator, void(TransformType&), SkipType> matrix;
qi::rule<Iterator, void(TransformType&), SkipType> translate;
qi::rule<Iterator, void(TransformType&), SkipType> scale;
qi::rule<Iterator, qi::locals<double, double, double>, void(TransformType&), SkipType> rotate;
qi::rule<Iterator, void(TransformType&), SkipType> skewX;
qi::rule<Iterator, void(TransformType&), SkipType> skewY;
};
}}
#endif // MAPNIK_SVG_TRANSFORM_GRAMMAR_HPP

View file

@ -1,171 +0,0 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2016 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
*
*****************************************************************************/
// NOTE: This is an implementation header file and is only meant to be included
// from implementation files. It therefore doesn't have an include guard.
// mapnik
#include <mapnik/svg/svg_transform_grammar.hpp>
#pragma GCC diagnostic push
#include <mapnik/warning_ignore.hpp>
#include <boost/spirit/include/phoenix_function.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_object.hpp>
#pragma GCC diagnostic pop
#pragma GCC diagnostic push
#include <mapnik/warning_ignore_agg.hpp>
#include <agg_trans_affine.h>
#pragma GCC diagnostic pop
namespace mapnik { namespace svg {
using namespace boost::spirit;
using namespace boost::fusion;
using namespace boost::phoenix;
inline double deg2rad(double d)
{
return M_PI * d / 180.0;
}
struct process_matrix
{
using result_type = void;
template <typename TransformType>
void operator () (TransformType & tr, double a, double b, double c, double d, double e, double f) const
{
tr = agg::trans_affine(a,b,c,d,e,f) * tr;
}
};
struct process_rotate
{
using result_type = void;
template <typename TransformType, typename T0,typename T1,typename T2>
void operator () (TransformType & tr, T0 a, T1 cx, T2 cy) const
{
if (cx == 0.0 && cy == 0.0)
{
tr = agg::trans_affine_rotation(deg2rad(a)) * tr;
}
else
{
agg::trans_affine t = agg::trans_affine_translation(-cx,-cy);
t *= agg::trans_affine_rotation(deg2rad(a));
t *= agg::trans_affine_translation(cx, cy);
tr = t * tr;
}
}
};
struct process_translate
{
using result_type = void;
template <typename TransformType, typename T0,typename T1>
void operator () (TransformType & tr, T0 tx, T1 ty) const
{
if (ty) tr = agg::trans_affine_translation(tx,*ty) * tr;
else tr = agg::trans_affine_translation(tx,0.0) * tr;
}
};
struct process_scale
{
using result_type = void;
template <typename TransformType, typename T0,typename T1>
void operator () (TransformType & tr, T0 sx, T1 sy) const
{
if (sy) tr = agg::trans_affine_scaling(sx,*sy) * tr;
else tr = agg::trans_affine_scaling(sx,sx) * tr;
}
};
struct process_skew
{
using result_type = void;
template <typename TransformType, typename T0,typename T1>
void operator () (TransformType & tr, T0 skew_x, T1 skew_y) const
{
tr = agg::trans_affine_skewing(deg2rad(skew_x),deg2rad(skew_y)) * tr;
}
};
template <typename Iterator, typename TransformType, typename SkipType>
svg_transform_grammar<Iterator, TransformType, SkipType>::svg_transform_grammar()
: svg_transform_grammar::base_type(start)
{
qi::_1_type _1;
qi::_2_type _2;
qi::_3_type _3;
qi::_4_type _4;
qi::_5_type _5;
qi::_6_type _6;
qi::_a_type _a;
qi::_b_type _b;
qi::_c_type _c;
qi::_r1_type _r1;
qi::lit_type lit;
qi::double_type double_;
qi::no_case_type no_case;
// actions
function<process_matrix> matrix_action;
function<process_rotate> rotate_action;
function<process_translate> translate_action;
function<process_scale> scale_action;
function<process_skew> skew_action;
start = +transform_(_r1) ;
transform_ = matrix(_r1) | rotate(_r1) | translate(_r1) | scale(_r1) | rotate(_r1) | skewX(_r1) | skewY (_r1) ;
matrix = no_case[lit("matrix")] >> lit('(')
>> (double_ >> -lit(',')
>> double_ >> -lit(',')
>> double_ >> -lit(',')
>> double_ >> -lit(',')
>> double_ >> -lit(',')
>> double_)[matrix_action(_r1, _1, _2, _3, _4, _5, _6)] >> lit(')');
translate = no_case[lit("translate")]
>> lit('(') >> (double_ >> -lit(',') >> -double_)[translate_action(_r1, _1, _2)] >> lit(')');
scale = no_case[lit("scale")]
>> lit('(') >> (double_ >> -lit(',') >> -double_)[scale_action(_r1, _1, _2)] >> lit(')');
rotate = no_case[lit("rotate")]
>> lit('(')
>> double_[_a = _1] >> -lit(',')
>> -(double_ [_b = _1] >> -lit(',') >> double_[_c = _1])
>> lit(')') [ rotate_action(_r1, _a,_b,_c)];
skewX = no_case[lit("skewX")] >> lit('(') >> double_ [ skew_action(_r1, _1, 0.0)] >> lit(')');
skewY = no_case[lit("skewY")] >> lit('(') >> double_ [ skew_action(_r1, 0.0, _1)] >> lit(')');
}
}}

View file

@ -0,0 +1,43 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2016 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_SVG_TRANSFORM_GRAMMAR_X3_HPP
#define MAPNIK_SVG_TRANSFORM_GRAMMAR_X3_HPP
// mapnik
#include <mapnik/svg/svg_grammar_config_x3.hpp>
namespace mapnik { namespace svg { namespace grammar {
using namespace boost::spirit::x3;
using svg_transform_grammar_type = x3::rule<class svg_transform_rule_tag>;
BOOST_SPIRIT_DECLARE(svg_transform_grammar_type);
}
grammar::svg_transform_grammar_type const& svg_transform_grammar();
}}
#endif // MAPNIK_SVG_TRANSFORM_GRAMMAR_X3_HPP

View file

@ -0,0 +1,163 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2016 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_SVG_TRANSFORM_GRAMMAR_X3_DEF_HPP
#define MAPNIK_SVG_TRANSFORM_GRAMMAR_X3_DEF_HPP
// mapnik
#include <mapnik/svg/svg_transform_grammar_x3.hpp>
// boost::fusion
#pragma GCC diagnostic push
#include <mapnik/warning_ignore.hpp>
#include <boost/fusion/sequence.hpp>
#pragma GCC diagnostic pop
// agg
#pragma GCC diagnostic push
#include <mapnik/warning_ignore_agg.hpp>
#include <agg_trans_affine.h>
#pragma GCC diagnostic pop
namespace mapnik { namespace svg { namespace grammar {
namespace x3 = boost::spirit::x3;
using x3::lit;
using x3::double_;
using x3::no_case;
auto const matrix_action = [] (auto const& ctx)
{
auto & tr = x3::get<svg_transform_tag>(ctx).get();
auto const& attr = _attr(ctx);
auto a = boost::fusion::at_c<0>(attr);
auto b = boost::fusion::at_c<1>(attr);
auto c = boost::fusion::at_c<2>(attr);
auto d = boost::fusion::at_c<3>(attr);
auto e = boost::fusion::at_c<4>(attr);
auto f = boost::fusion::at_c<5>(attr);
tr = agg::trans_affine(a, b, c, d, e, f) * tr;
};
auto const rotate_action = [] (auto const& ctx)
{
auto & tr = x3::get<svg_transform_tag>(ctx).get();
auto const& attr = _attr(ctx);
auto a = boost::fusion::at_c<0>(attr);
auto cx = boost::fusion::at_c<1>(attr) ? *boost::fusion::at_c<1>(attr) : 0.0;
auto cy = boost::fusion::at_c<2>(attr) ? *boost::fusion::at_c<2>(attr) : 0.0;
if (cx == 0.0 && cy == 0.0)
{
tr = agg::trans_affine_rotation(deg2rad(a)) * tr;
}
else
{
agg::trans_affine t = agg::trans_affine_translation(-cx,-cy);
t *= agg::trans_affine_rotation(deg2rad(a));
t *= agg::trans_affine_translation(cx, cy);
tr = t * tr;
}
};
auto const translate_action = [] (auto const& ctx)
{
auto & tr = x3::get<svg_transform_tag>(ctx).get();
auto const& attr = _attr(ctx);
auto tx = boost::fusion::at_c<0>(attr);
auto ty = boost::fusion::at_c<1>(attr);
if (ty) tr = agg::trans_affine_translation(tx, *ty) * tr;
else tr = agg::trans_affine_translation(tx,0.0) * tr;
};
auto const scale_action = [] (auto const& ctx)
{
auto & tr = x3::get<svg_transform_tag>(ctx).get();
auto const& attr = _attr(ctx);
auto sx = boost::fusion::at_c<0>(attr);
auto sy = boost::fusion::at_c<1>(attr);
if (sy) tr = agg::trans_affine_scaling(sx, *sy) * tr;
else tr = agg::trans_affine_scaling(sx, sx) * tr;
};
auto const skewX_action = [] (auto const& ctx)
{
auto & tr = x3::get<svg_transform_tag>(ctx).get();
auto skew_x = _attr(ctx);
tr = agg::trans_affine_skewing(deg2rad(skew_x), 0.0) * tr;
};
auto const skewY_action = [] (auto const& ctx)
{
auto & tr = x3::get<svg_transform_tag>(ctx).get();
auto skew_y= _attr(ctx);
tr = agg::trans_affine_skewing(0.0, deg2rad(skew_y)) * tr;
};
//exported rule
svg_transform_grammar_type const svg_transform = "SVG Transform";
// rules
auto const matrix = x3::rule<class matrix_tag> {} = no_case[lit("matrix")]
> lit('(') > (double_ > -lit(',')
> double_ > -lit(',')
> double_ > -lit(',')
> double_ > -lit(',')
> double_ > -lit(',')
> double_)[matrix_action]
> lit(')');
auto const translate = x3::rule<class translate_tag> {} = no_case[lit("translate")]
> lit('(') > (double_ > -lit(',') >> -double_)[translate_action] > lit(')');
auto const scale = x3::rule<class scale_tag> {} = no_case[lit("scale")]
> lit('(') > (double_ > -lit(',') > -double_)[scale_action] > lit(')');
auto const rotate = x3::rule<class rotate_tag> {} = no_case[lit("rotate")]
> lit('(')
> (double_ > -(lit(',') > double_) > -(lit(',') > double_))[rotate_action]
> lit(')') ;
auto const skewX = x3::rule<class skewX_tag> {} = no_case[lit("skewX")]
> lit('(') > double_ [ skewX_action] > lit(')');
auto const skewY = x3::rule<class skewY_tag> {} = no_case[lit("skewY")]
> lit('(') > double_ [ skewY_action] > lit(')');
auto const transform = x3::rule<class transform_tag> {} = matrix | rotate | translate | scale /*| rotate*/ | skewX | skewY ;
auto const svg_transform_def = +transform ;
#pragma GCC diagnostic push
#include <mapnik/warning_ignore.hpp>
BOOST_SPIRIT_DEFINE(
svg_transform
);
#pragma GCC diagnostic pop
}
grammar::svg_transform_grammar_type const& svg_transform_grammar()
{
return grammar::svg_transform;
}
}}
#endif // MAPNIK_SVG_TRANSFORM_GRAMMAR_X3_HPP

View file

@ -22,24 +22,37 @@
// mapnik
#include <mapnik/config.hpp>
#include <mapnik/svg/svg_transform_grammar_impl.hpp>
#include <mapnik/svg/svg_transform_grammar_x3_def.hpp>
// stl
#include <string>
#include <cstring>
namespace mapnik {
namespace svg {
namespace mapnik { namespace svg {
template <typename TransformType>
bool parse_svg_transform(const char* wkt, TransformType& tr)
template <typename Transform>
bool parse_svg_transform(const char* wkt, Transform& tr)
{
using namespace boost::spirit;
using iterator_type = const char*;
using skip_type = ascii::space_type;
static const svg_transform_grammar<iterator_type, TransformType, skip_type> g;
using iterator_type = char const*;
iterator_type first = wkt;
iterator_type last = wkt + std::strlen(wkt);
return qi::phrase_parse(first, last, (g)(boost::phoenix::ref(tr)), skip_type());
using space_type = mapnik::svg::grammar::space_type;
auto const grammar = x3::with<mapnik::svg::grammar::svg_transform_tag>(std::ref(tr))
[mapnik::svg::svg_transform_grammar()];
try
{
if (!x3::phrase_parse(first, last, grammar, space_type())
|| first != last)
{
throw std::runtime_error("Failed to parse svg-transform");
}
}
catch (...)
{
return false;
}
return true;
}
template bool MAPNIK_DECL parse_svg_transform<agg::trans_affine>(const char*, agg::trans_affine&);