Это долго с большим количеством кода, поэтому я надеюсь, что Stack Overflow справится с этим.: P
Я пытаюсь написать SVG-парсер с Boost Spirit.У меня есть грамматика, которая заполняет вектор "Контурами", которые являются векторами "BezierPoints", которые могут представлять либо обычные точки, либо точки с элементами управления Безье.
Пока у меня есть это (не обрабатывая относительные команды рисованияпока):
#ifndef SVG_PARSER_HPP
#define SVG_PARSER_HPP
#include <vector>
#include "boost/spirit/include/qi.hpp"
#include "boost/spirit/include/phoenix.hpp"
#include "boost/fusion/include/adapt_struct.hpp"
#include "boost/fusion/include/std_pair.hpp"
namespace qi = boost::spirit::qi;
namespace phoenix = boost::phoenix;
namespace ascii = boost::spirit::ascii;
struct Point
{
Point(const double nx = 0.0, const double ny = 0.0) : x(nx), y(ny)
{}
double x;
double y;
};
BOOST_FUSION_ADAPT_STRUCT(
Point,
(double, x)
(double, y)
)
struct BezierPoint
{
BezierPoint(const double x = 0.0, const double y = 0.0) :
point(x, y), control1(0.0, 0.0), control2(0.0, 0.0) {}
BezierPoint(const Point &p) : point(p), control1(0.0, 0.0),
control2(0.0, 0.0) {}
Point point; // End point. Start point is in the BezierPoint that
// came before it.
// Todo: Set these to be coincident with point for non-curve points.
Point control1;
Point control2;
};
BOOST_FUSION_ADAPT_STRUCT(
BezierPoint,
(Point, control1)
(Point, control2)
(Point, point)
)
typedef std::vector<BezierPoint> BezierVec;
typedef std::vector<BezierVec> Contours;
template <typename Iterator>
struct PathGrammar : qi::grammar<Iterator, Contours()>
{
///////////////////////////
// SVG is a damn monster //
///////////////////////////
PathGrammar() : PathGrammar::base_type(path_data)
{
using qi::char_;
using qi::double_;
using qi::_val;
using qi::_1;
using phoenix::push_back;
using phoenix::insert;
using phoenix::begin;
using phoenix::end;
using phoenix::construct;
using phoenix::val;
using ascii::space;
path_data = *space >> -moveto_drawto_command_groups
>> *space;
moveto_drawto_command_groups = moveto_drawto_command_group
% *space;
moveto_drawto_command_group = moveto[
insert(_val, end(_val), begin(_1), end(_1))
] >> *space
>> -drawto_commands[
insert(_val, end(_val), begin(_1), end(_1))
];
// Draw commands are (optionally) followed by a closepath
// command.
drawto_commands = (drawto_command[
insert(_val, end(_val), begin(_1), end(_1))
] % *space) >> *space >> -closepath;
drawto_command = lineto | horizontal_lineto
| vertical_lineto | curveto | smooth_curveto;
moveto = ( char_('M') | char_('m') ) >> *space
>> lineto_argument_sequence;
closepath = (char_('Z') | char_('z'));
lineto = ( char_('L') | char_('l') ) >> *space
>> lineto_argument_sequence;
lineto_argument_sequence = coordinate_pair[
push_back(_val, construct<BezierPoint>(_1))
] % -comma_space;
horizontal_lineto = ( char_('H') | char_('h') ) >> *space
>> horizontal_lineto_argument_sequence;
horizontal_lineto_argument_sequence = coordinate[
push_back(_val, construct<BezierPoint>(_1, val(0.0)))
] % -comma_space;
vertical_lineto = ( char_('V') | char_('v') ) >> *space
>> vertical_lineto_argument_sequence;
vertical_lineto_argument_sequence = coordinate[
push_back(_val, construct<BezierPoint>(val(0.0), _1))
] % -comma_space;
curveto = ( char_('C') | char_('c') ) >> *space
>> curveto_argument_sequence;
curveto_argument_sequence = curveto_argument % -comma_space;
curveto_argument = coordinate_pair >> -comma_space
>> coordinate_pair >> -comma_space >> coordinate_pair;
smooth_curveto = ( char_('S') | char_('s') ) >> *space
>> smooth_curveto_argument_sequence;
smooth_curveto_argument_sequence = smooth_curveto_argument
% -comma_space;
smooth_curveto_argument = coordinate_pair >> -comma_space
>> coordinate_pair;
coordinate_pair = (double_ >> -comma_space >> double_);
coordinate = double_;
comma_space = (+space >> -char_(',') >> *space)
| (char_(',') >> *space);
}
// Quadratic curves are not supported
qi::rule<Iterator, Contours()> path_data;
qi::rule<Iterator, Contours()> moveto_drawto_command_groups;
qi::rule<Iterator, BezierVec()> moveto_drawto_command_group;
qi::rule<Iterator, BezierVec()> drawto_commands;
qi::rule<Iterator, BezierVec()> drawto_command;
qi::rule<Iterator, BezierVec()> moveto;
qi::rule<Iterator, BezierVec()> moveto_argument_sequence;
qi::rule<Iterator> closepath;
qi::rule<Iterator, BezierVec()> lineto;
qi::rule<Iterator, BezierVec()> lineto_argument_sequence;
qi::rule<Iterator, BezierVec()> horizontal_lineto;
qi::rule<Iterator, BezierVec()>
horizontal_lineto_argument_sequence;
qi::rule<Iterator, BezierVec()> vertical_lineto;
qi::rule<Iterator, BezierVec()> vertical_lineto_argument_sequence;
qi::rule<Iterator, BezierVec()> curveto;
qi::rule<Iterator, BezierVec()> curveto_argument_sequence;
qi::rule<Iterator, BezierPoint()> curveto_argument;
qi::rule<Iterator, BezierVec()> smooth_curveto;
qi::rule<Iterator, BezierVec()> smooth_curveto_argument_sequence;
qi::rule<Iterator, BezierPoint()> smooth_curveto_argument;
qi::rule<Iterator, Point()> coordinate_pair;
qi::rule<Iterator, double()> coordinate;
qi::rule<Iterator> comma_space;
};
#endif
Грамматика вызывается так:
typedef string::const_iterator StrItr;
PathGrammar<StrItr> grammar;
Contours paths;
StrItr startIt = pathData.begin();
StrItr endIt = pathData.end();
qi::parse(startIt, endIt, grammar, paths);
BOOST_FOREACH(BezierVec v, paths)
{
cout << "Path:" << endl;
BOOST_FOREACH(BezierPoint p, v)
{
cout << '\t' << p.point.x << ", " << p.point.y << endl;
}
}
И это моя текущая тестовая строка:
M26.591,0L0,22.348l25.46,23.479L12.306,100l36.067-23.619L85.008,28.43L26.591,0z M30.553,34.23
l-8.487-10.467l9.052-5.234l25.601,8.77l-3.109,12.729L30.553,34.23z
Строка переформатирована, чтобы сделатьэто легче читать:
M 26.591, 0
L 0 , 22.348
l 25.46 , 23.479
L 12.306, 100
l 36.067, -23.619
L 85.008, 28.43
L 26.591, 0
z
M 30.553, 34.23
l -8.487, -10.467
l 9.052, -5.234
l 25.601, 8.77
l -3.109, 12.729
L 30.553, 34.23
z
Вот вывод:
Path:
77, 0
26.591, 0
76, 0
0, 22.348
108, 0
25.46, 23.479
76, 0
12.306, 100
108, 0
36.067, -23.619
76, 0
85.008, 28.43
76, 0
26.591, 0
Path:
77, 0
30.553, 34.23
108, 0
-8.487, -10.467
108, 0
9.052, -5.234
108, 0
25.601, 8.77
108, 0
-3.109, 12.729
76, 0
30.553, 34.23
Грамматика видит точки, но продолжает вставлять все эти дополнительные точки, и я понятия не имею, гдеони приходят.
PS
Мне также интересно узнать пару правил.Сначала есть это правило:
qi::rule<Iterator, BezierVec()> drawto_commands;
qi::rule<Iterator, BezierVec()> drawto_command;
...
drawto_commands = (drawto_command[
insert(_val, end(_val), begin(_1), end(_1))
] % *space) >> *space >> -closepath;
Я хочу получить результаты (drawto_command % *space)
в виде одного вектора вместо вектора векторов.Насколько я могу сказать, я должен сделать это вручную с Фениксом.Это тот случай?
У меня похожая вещь с моими правилами moveto:
qi::rule<Iterator, BezierVec()> moveto_drawto_command_group;
qi::rule<Iterator, BezierVec()> moveto;
qi::rule<Iterator, BezierVec()> moveto_argument_sequence;
...
moveto_drawto_command_group = moveto[
insert(_val, end(_val), begin(_1), end(_1))
] >> *space
>> -drawto_commands[
insert(_val, end(_val), begin(_1), end(_1))
];
У меня есть два правила, которые дают BezierVec, который я хочу объединить в один BezierVec длятретье правило.Пока что единственный способ сделать это - ручная вставка с помощью Phoenix.Нет ли более простого способа?