Transform¶
Transformation parsing is controlled by Transform Policy and Transform Events Policy. Transform Policy configures the adapter that simplifies application handling of coordinate system transformations (e.g. adapter may substitute simple transformation steps translate/scale/rotate/skew with corresponding transformation matrices, more convenient in some cases). Transform Events Policy defines how parsed data is passed to the user code.
Transform Events Policy Concept¶
Transform Events Policy depends on compile-time settings in Transform Policy.
If policy::transform::raw
Transform Policy is used that preserves input data at much as possible
then Transform Events Policy becomes:
struct transform_events_policy
{
typedef /*...*/ context_type;
static void transform_matrix(context_type & context, const boost::array<number_type, 6> & matrix);
static void transform_translate(context_type & context, number_type tx, number_type ty);
static void transform_translate(context_type & context, number_type tx);
static void transform_scale(context_type & context, number_type sx, number_type sy);
static void transform_scale(context_type & context, number_type scale);
static void transform_rotate(context_type & context, number_type angle);
static void transform_rotate(context_type & context, number_type angle, number_type cx, number_type cy);
static void transform_skew_x(context_type & context, number_type angle);
static void transform_skew_y(context_type & context, number_type angle);
};
Depending on Transform Policy, some of the methods aren’t called by SVG++ and therefore shouldn’t be implemented.
Note
Transformation matrix is passed as array of size 6 [a b c d e f]
, corresponding to this matrix:
Named class template parameter for Transform Events Policy is transform_events_policy
.
Default Transform Events Policy (policy::transform_events::forward_to_method
) forwards calls to its
static methods to methods of context
object:
struct forward_to_method
{
typedef Context context_type;
template<class Number>
static void transform_matrix(context_type & context, const boost::array<Number, 6> & matrix)
{
context.transform_matrix(matrix);
}
/*...*/
};
Example of handling transforms with default settings (src/samples/sample_transform01.cpp
):
#include <svgpp/svgpp.hpp>
#include <algorithm>
#include <iterator>
using namespace svgpp;
struct Context
{
void transform_matrix(const boost::array<double, 6> & matrix)
{
std::copy(matrix.begin(), matrix.end(),
std::ostream_iterator<double>(std::cout, " "));
std::cout << "\n";
}
};
int main()
{
Context context;
value_parser<tag::type::transform_list>::parse(tag::attribute::transform(), context,
std::string("translate(-10,-20) scale(2) rotate(45) translate(5,10)"), tag::source::attribute());
return 0;
}
In this example sequential transforms are joined in user code (src/samples/sample_transform02.cpp
):
#include <svgpp/svgpp.hpp>
#include <boost/version.hpp>
#if BOOST_VERSION >= 106400
#include <boost/serialization/array_wrapper.hpp>
#endif
#include <boost/math/constants/constants.hpp>
#include <boost/numeric/ublas/assignment.hpp>
#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/io.hpp>
using namespace svgpp;
namespace ublas = boost::numeric::ublas;
typedef ublas::matrix<double> matrix_t;
struct TransformEventsPolicy
{
typedef matrix_t context_type;
static void transform_matrix(matrix_t & transform, const boost::array<double, 6> & matrix)
{
matrix_t m(3, 3);
m <<=
matrix[0], matrix[2], matrix[4],
matrix[1], matrix[3], matrix[5],
0, 0, 1;
transform = ublas::prod(transform, m);
}
static void transform_translate(matrix_t & transform, double tx, double ty)
{
matrix_t m = ublas::identity_matrix<double>(3, 3);
m(0, 2) = tx; m(1, 2) = ty;
transform = ublas::prod(transform, m);
}
static void transform_scale(matrix_t & transform, double sx, double sy)
{
matrix_t m = ublas::identity_matrix<double>(3, 3);
m(0, 0) = sx; m(1, 1) = sy;
transform = ublas::prod(transform, m);
}
static void transform_rotate(matrix_t & transform, double angle)
{
angle *= boost::math::constants::degree<double>();
matrix_t m(3, 3);
m <<=
std::cos(angle), -std::sin(angle), 0,
std::sin(angle), std::cos(angle), 0,
0, 0, 1;
transform = ublas::prod(transform, m);
}
static void transform_skew_x(matrix_t & transform, double angle)
{
angle *= boost::math::constants::degree<double>();
matrix_t m = ublas::identity_matrix<double>(3, 3);
m(0, 1) = std::tan(angle);
transform = ublas::prod(transform, m);
}
static void transform_skew_y(matrix_t & transform, double angle)
{
angle *= boost::math::constants::degree<double>();
matrix_t m = ublas::identity_matrix<double>(3, 3);
m(1, 0) = std::tan(angle);
transform = ublas::prod(transform, m);
}
};
int main()
{
matrix_t transform(ublas::identity_matrix<double>(3, 3));
value_parser<
tag::type::transform_list,
transform_policy<policy::transform::minimal>,
transform_events_policy<TransformEventsPolicy>
>::parse(tag::attribute::transform(), transform,
std::string("translate(-10,-20) scale(2) rotate(45) translate(5,10)"), tag::source::attribute());
std::cout << transform << "\n";
return 0;
}
Transform Policy Concept¶
struct transform_policy_concept
{
static const bool join_transforms = /* true or false */;
static const bool no_rotate_about_point = /* true or false */;
static const bool no_shorthands = /* true or false */;
static const bool only_matrix_transform = /* true or false */;
};
Transform Policy is a class with bool
static member constants. If they all are false
(as in policy::transform::raw
), then adapter isn’t used and the parser passes parsed values as is.
Setting some members to true
programmer may simplify the application:
join_transforms = true
All transformations in SVG attribute are joined in single transformation matrix. Values of other Transform Policy members are ignored. Transform Events Policy in this case contains only one method:
struct transform_events_policy { typedef /*...*/ context_type; static void transform_matrix(context_type & context, const boost::array<number_type, 6> & matrix); }no_rotate_about_point = true
- rotate(<rotate-angle> <cx> <cy>) substituted with translate(<cx>, <cy>) rotate(<rotate-angle>) translate(-<cx>, -<cy>).
transform_rotate
method of Transform Events Policy with parameterscx
andcy
is not used.no_shorthands = true
- Calls to
transform_translate
andtransform_scale
with one number substituted with corresponding calls with two numbers.only_matrix_transform = true
- Each transformation step is substituted with corresponding call to
transform_matrix
. Therefore only this method is used in Transform Events Policy.
File svgpp/policy/transform.hpp
contains some predefined Transform Policies.
policy::transform::matrix
, used by default, sets join_transforms = true
.
Named class template parameter for Transform Policy is transform_policy
.