# 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 parameters `cx` and `cy` is not used.

> `no_shorthands = true`

>   Calls to `transform_translate` and `transform_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`.