<commit_before>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2012 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_VERTEX_CONVERTERS_HPP
#define MAPNIK_VERTEX_CONVERTERS_HPP

// mapnik
#include <mapnik/config.hpp>
#include <mapnik/attribute.hpp>
#include <mapnik/view_transform.hpp>
#include <mapnik/transform_path_adapter.hpp>
#include <mapnik/offset_converter.hpp>
#include <mapnik/simplify.hpp>
#include <mapnik/simplify_converter.hpp>
#include <mapnik/noncopyable.hpp>
#include <mapnik/value_types.hpp>
#include <mapnik/symbolizer_enumerations.hpp>
#include <mapnik/symbolizer_keys.hpp>
#include <mapnik/symbolizer.hpp>

// agg
#include "agg_math_stroke.h"
#include "agg_trans_affine.h"
#include "agg_conv_clip_polygon.h"
#include "agg_conv_clip_polyline.h"
#include "agg_conv_close_polygon.h"
#include "agg_conv_smooth_poly1.h"
#include "agg_conv_stroke.h"
#include "agg_conv_dash.h"
#include "agg_conv_transform.h"

// stl
#include <type_traits>
#include <stdexcept>
#include <array>

namespace mapnik {

struct transform_tag {};
struct clip_line_tag {};
struct clip_poly_tag {};
struct close_poly_tag {};
struct smooth_tag {};
struct simplify_tag {};
struct stroke_tag {};
struct dash_tag {};
struct affine_transform_tag {};
struct offset_transform_tag {};

namespace  detail {

template <typename T0, typename T1>
struct converter_traits
{
    using geometry_type = T0;
    using conv_type = geometry_type;
};

template <typename T>
struct converter_traits<T,mapnik::smooth_tag>
{
    using geometry_type = T;
    using conv_type = typename agg::conv_smooth_poly1_curve<geometry_type>;

    template <typename Args>
    static void setup(geometry_type & geom, Args const& args)
    {
        geom.smooth_value(get<value_double>(args.sym, keys::smooth, args.feature, args.vars));
    }
};

template <typename T>
struct converter_traits<T,mapnik::simplify_tag>
{
    using geometry_type = T;
    using conv_type = simplify_converter<geometry_type>;

    template <typename Args>
    static void setup(geometry_type & geom, Args const& args)
    {
        geom.set_simplify_algorithm(static_cast<simplify_algorithm_e>(get<value_integer>(args.sym, keys::simplify_algorithm, args.feature, args.vars)));
        geom.set_simplify_tolerance(get<value_double>(args.sym, keys::simplify_tolerance,args.feature, args.vars));
    }
};

template <typename T>
struct converter_traits<T, mapnik::clip_line_tag>
{
    using geometry_type = T;
    using conv_type = typename agg::conv_clip_polyline<geometry_type>;

    template <typename Args>
    static void setup(geometry_type & geom, Args const& args)
    {
        auto const& box = args.bbox;
        geom.clip_box(box.minx(),box.miny(),box.maxx(),box.maxy());
    }
};

template <typename T>
struct converter_traits<T, mapnik::dash_tag>
{
    using geometry_type = T;
    using conv_type = typename agg::conv_dash<geometry_type>;

    template <typename Args>
    static void setup(geometry_type & geom, Args const& args)
    {
        auto const& sym = args.sym;
        auto const& feat = args.feature;
        auto const& vars = args.vars;
        double scale_factor = args.scale_factor;
        auto dash = get_optional<dash_array>(sym, keys::stroke_dasharray, feat, vars);
        if (dash)
        {
            for (auto const& d : *dash)
            {
                geom.add_dash(d.first * scale_factor,
                              d.second * scale_factor);
            }
        }
    }
};

template <typename Symbolizer, typename PathType, typename Feature>
void set_join_caps(Symbolizer const& sym, PathType & stroke, Feature const& feature, attributes const& vars)
{
    line_join_enum join = get<line_join_enum>(sym, keys::stroke_linejoin, feature, vars, MITER_JOIN);
    switch (join)
    {
    case MITER_JOIN:
        stroke.generator().line_join(agg::miter_join);
        break;
    case MITER_REVERT_JOIN:
        stroke.generator().line_join(agg::miter_join);
        break;
    case ROUND_JOIN:
        stroke.generator().line_join(agg::round_join);
        break;
    default:
        stroke.generator().line_join(agg::bevel_join);
    }

    line_cap_enum cap = get<line_cap_enum>(sym, keys::stroke_linecap, feature, vars, BUTT_CAP);

    switch (cap)
    {
    case BUTT_CAP:
        stroke.generator().line_cap(agg::butt_cap);
        break;
    case SQUARE_CAP:
        stroke.generator().line_cap(agg::square_cap);
        break;
    default:
        stroke.generator().line_cap(agg::round_cap);
    }
}

template <typename T>
struct converter_traits<T, mapnik::stroke_tag>
{
    using geometry_type = T;
    using conv_type = typename agg::conv_stroke<geometry_type>;

    template <typename Args>
    static void setup(geometry_type & geom, Args const& args)
    {
        auto const& sym = args.sym;
        auto const& feat = args.feature;
        auto const& vars = args.vars;
        set_join_caps(sym, geom, feat, vars);
        double miterlimit = get<value_double>(sym, keys::stroke_miterlimit, feat, vars, 4.0);
        geom.generator().miter_limit(miterlimit);
        double scale_factor = args.scale_factor;
        double width = get<value_double>(sym, keys::stroke_width, feat, vars, 1.0);
        geom.generator().width(width * scale_factor);
    }
};

template <typename T>
struct converter_traits<T,mapnik::clip_poly_tag>
{
    using geometry_type = T;
    using conv_type = typename agg::conv_clip_polygon<geometry_type>;
    template <typename Args>
    static void setup(geometry_type & geom, Args const& args)
    {
        auto const& box = args.bbox;
        geom.clip_box(box.minx(),box.miny(),box.maxx(),box.maxy());
    }
};

template <typename T>
struct converter_traits<T,mapnik::close_poly_tag>
{
    using geometry_type = T;
    using conv_type = typename agg::conv_close_polygon<geometry_type>;
    template <typename Args>
    static void setup(geometry_type & , Args const&) {}
};

template <typename T>
struct converter_traits<T,mapnik::transform_tag>
{
    using geometry_type = T;
    using conv_type = transform_path_adapter<view_transform, geometry_type>;

    template <typename Args>
    static void setup(geometry_type & geom, Args const& args)
    {
        geom.set_proj_trans(args.prj_trans);
        geom.set_trans(args.tr);
    }
};

template <typename T>
struct converter_traits<T,mapnik::affine_transform_tag>
{
    using geometry_type = T;
    using conv_base_type =  agg::conv_transform<geometry_type, agg::trans_affine const>;
    struct conv_type : public conv_base_type
    {
        conv_type(geometry_type& geom)
            : conv_base_type(geom, agg::trans_affine::identity) {}
    };

    template <typename Args>
    static void setup(geometry_type & geom, Args & args)
    {
        geom.transformer(args.affine_trans);
    }
};

template <typename T>
struct converter_traits<T,mapnik::offset_transform_tag>
{
    using geometry_type = T;
    using conv_type = offset_converter<geometry_type>;

    template <typename Args>
    static void setup(geometry_type & geom, Args const& args)
    {
        auto const& sym = args.sym;
        auto const& feat = args.feature;
        auto const& vars = args.vars;
        double offset = get<value_double>(sym, keys::offset, feat, vars);
        geom.set_offset(offset * args.scale_factor);
    }
};

template <typename Dispatcher, typename... ConverterTypes>
struct converters_helper;

template <typename Dispatcher, typename Current, typename... ConverterTypes>
struct converters_helper<Dispatcher,Current,ConverterTypes...>
{
    template <typename Converter>
    static void set(Dispatcher & disp, int state)
    {
        if (std::is_same<Converter,Current>::value)
        {
            std::size_t index = sizeof...(ConverterTypes) ;
            disp.vec_[index] = state;
        }
        else
        {
            converters_helper<Dispatcher,ConverterTypes...>:: template set<Converter>(disp, state);
        }
    }

    template <typename Geometry>
    static void forward(Dispatcher & disp, Geometry & geom)
    {
        std::size_t index = sizeof...(ConverterTypes);
        if (disp.vec_[index] == 1)
        {
            using conv_type = typename detail::converter_traits<Geometry,Current>::conv_type;
            conv_type conv(geom);
            detail::converter_traits<conv_type,Current>::setup(conv,disp.args_);
            converters_helper<Dispatcher, ConverterTypes...>::forward(disp, conv);
        }
        else
        {
            converters_helper<Dispatcher,ConverterTypes...>::forward(disp, geom);
        }
    }
};

template <typename Dispatcher>
struct converters_helper<Dispatcher>
{
    template <typename Converter>
    static void set(Dispatcher & disp, int state) {}
    template <typename Geometry>
    static void forward(Dispatcher & disp, Geometry & geom)
    {
        disp.args_.proc.add_path(geom);
    }
};

template <typename Args, typename... ConverterTypes>
struct dispatcher : mapnik::noncopyable
{
    using this_type = dispatcher;
    using args_type = Args;

    dispatcher(typename Args::processor_type & proc, box2d<double> const& bbox, symbolizer_base const& sym, view_transform const& tr,
               proj_transform const& prj_trans, agg::trans_affine const& affine_trans, feature_impl const& feature,
               attributes const& vars, double scale_factor)
        : args_(proc,bbox,sym,tr,prj_trans,affine_trans,feature,vars,scale_factor)
    {
        std::fill(vec_.begin(), vec_.end(), 0);
    }

    std::array<unsigned, sizeof...(ConverterTypes)> vec_;
    args_type args_;
};

template <typename Processor>
struct arguments : mapnik::noncopyable
{
    using processor_type = Processor;
    arguments(Processor & proc, box2d<double> const& bbox, symbolizer_base const& sym, view_transform const& tr,
              proj_transform const& prj_trans, agg::trans_affine const& affine_trans, feature_impl const& feature,
              attributes const& vars, double scale_factor)
        : proc(proc),
          bbox(bbox),
          sym(sym),
          tr(tr),
          prj_trans(prj_trans),
          affine_trans(affine_trans),
          feature(feature),
          vars(vars),
          scale_factor(scale_factor) {}

    Processor & proc;
    box2d<double> const& bbox;
    symbolizer_base const& sym;
    view_transform const& tr;
    proj_transform const& prj_trans;
    agg::trans_affine const& affine_trans;
    feature_impl const& feature;
    attributes const& vars;
    double scale_factor;
};

}

template <typename Processor, typename... ConverterTypes >
struct vertex_converter : private mapnik::noncopyable
{
    using bbox_type = box2d<double>;
    using processor_type = Processor;
    using symbolizer_type = symbolizer_base;
    using trans_type = view_transform;
    using proj_trans_type = proj_transform;
    using affine_trans_type = agg::trans_affine;
    using feature_type = feature_impl;
    using args_type = detail::arguments<Processor>;
    using dispatcher_type = detail::dispatcher<args_type,ConverterTypes...>;

    vertex_converter(bbox_type const& bbox,
                     processor_type & proc,
                     symbolizer_type const& sym,
                     trans_type const& tr,
                     proj_trans_type const& prj_trans,
                     affine_trans_type const& affine_trans,
                     feature_type const& feature,
                     attributes const& vars,
                     double scale_factor)
        : disp_(proc,bbox,sym,tr,prj_trans,affine_trans,feature,vars,scale_factor) {}

    template <typename Geometry>
    void apply(Geometry & geom)
    {
        detail::converters_helper<dispatcher_type, ConverterTypes...>:: template forward<Geometry>(disp_, geom);
    }

    template <typename Converter>
    void set()
    {
        detail::converters_helper<dispatcher_type, ConverterTypes...>:: template set<Converter>(disp_, 1);
    }

    template <typename Converter>
    void unset()
    {
        detail::converters_helper<dispatcher_type, ConverterTypes...>:: template set<Converter>(disp_, 0);
    }

    dispatcher_type disp_;
};

}

#endif // MAPNIK_VERTEX_CONVERTERS_HPP
<commit_msg>make index constexpr<commit_after>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2012 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_VERTEX_CONVERTERS_HPP
#define MAPNIK_VERTEX_CONVERTERS_HPP

// mapnik
#include <mapnik/config.hpp>
#include <mapnik/attribute.hpp>
#include <mapnik/view_transform.hpp>
#include <mapnik/transform_path_adapter.hpp>
#include <mapnik/offset_converter.hpp>
#include <mapnik/simplify.hpp>
#include <mapnik/simplify_converter.hpp>
#include <mapnik/noncopyable.hpp>
#include <mapnik/value_types.hpp>
#include <mapnik/symbolizer_enumerations.hpp>
#include <mapnik/symbolizer_keys.hpp>
#include <mapnik/symbolizer.hpp>

// agg
#include "agg_math_stroke.h"
#include "agg_trans_affine.h"
#include "agg_conv_clip_polygon.h"
#include "agg_conv_clip_polyline.h"
#include "agg_conv_close_polygon.h"
#include "agg_conv_smooth_poly1.h"
#include "agg_conv_stroke.h"
#include "agg_conv_dash.h"
#include "agg_conv_transform.h"

// stl
#include <type_traits>
#include <stdexcept>
#include <array>

namespace mapnik {

struct transform_tag {};
struct clip_line_tag {};
struct clip_poly_tag {};
struct close_poly_tag {};
struct smooth_tag {};
struct simplify_tag {};
struct stroke_tag {};
struct dash_tag {};
struct affine_transform_tag {};
struct offset_transform_tag {};

namespace  detail {

template <typename T0, typename T1>
struct converter_traits
{
    using geometry_type = T0;
    using conv_type = geometry_type;
};

template <typename T>
struct converter_traits<T,mapnik::smooth_tag>
{
    using geometry_type = T;
    using conv_type = typename agg::conv_smooth_poly1_curve<geometry_type>;

    template <typename Args>
    static void setup(geometry_type & geom, Args const& args)
    {
        geom.smooth_value(get<value_double>(args.sym, keys::smooth, args.feature, args.vars));
    }
};

template <typename T>
struct converter_traits<T,mapnik::simplify_tag>
{
    using geometry_type = T;
    using conv_type = simplify_converter<geometry_type>;

    template <typename Args>
    static void setup(geometry_type & geom, Args const& args)
    {
        geom.set_simplify_algorithm(static_cast<simplify_algorithm_e>(get<value_integer>(args.sym, keys::simplify_algorithm, args.feature, args.vars)));
        geom.set_simplify_tolerance(get<value_double>(args.sym, keys::simplify_tolerance,args.feature, args.vars));
    }
};

template <typename T>
struct converter_traits<T, mapnik::clip_line_tag>
{
    using geometry_type = T;
    using conv_type = typename agg::conv_clip_polyline<geometry_type>;

    template <typename Args>
    static void setup(geometry_type & geom, Args const& args)
    {
        auto const& box = args.bbox;
        geom.clip_box(box.minx(),box.miny(),box.maxx(),box.maxy());
    }
};

template <typename T>
struct converter_traits<T, mapnik::dash_tag>
{
    using geometry_type = T;
    using conv_type = typename agg::conv_dash<geometry_type>;

    template <typename Args>
    static void setup(geometry_type & geom, Args const& args)
    {
        auto const& sym = args.sym;
        auto const& feat = args.feature;
        auto const& vars = args.vars;
        double scale_factor = args.scale_factor;
        auto dash = get_optional<dash_array>(sym, keys::stroke_dasharray, feat, vars);
        if (dash)
        {
            for (auto const& d : *dash)
            {
                geom.add_dash(d.first * scale_factor,
                              d.second * scale_factor);
            }
        }
    }
};

template <typename Symbolizer, typename PathType, typename Feature>
void set_join_caps(Symbolizer const& sym, PathType & stroke, Feature const& feature, attributes const& vars)
{
    line_join_enum join = get<line_join_enum>(sym, keys::stroke_linejoin, feature, vars, MITER_JOIN);
    switch (join)
    {
    case MITER_JOIN:
        stroke.generator().line_join(agg::miter_join);
        break;
    case MITER_REVERT_JOIN:
        stroke.generator().line_join(agg::miter_join);
        break;
    case ROUND_JOIN:
        stroke.generator().line_join(agg::round_join);
        break;
    default:
        stroke.generator().line_join(agg::bevel_join);
    }

    line_cap_enum cap = get<line_cap_enum>(sym, keys::stroke_linecap, feature, vars, BUTT_CAP);

    switch (cap)
    {
    case BUTT_CAP:
        stroke.generator().line_cap(agg::butt_cap);
        break;
    case SQUARE_CAP:
        stroke.generator().line_cap(agg::square_cap);
        break;
    default:
        stroke.generator().line_cap(agg::round_cap);
    }
}

template <typename T>
struct converter_traits<T, mapnik::stroke_tag>
{
    using geometry_type = T;
    using conv_type = typename agg::conv_stroke<geometry_type>;

    template <typename Args>
    static void setup(geometry_type & geom, Args const& args)
    {
        auto const& sym = args.sym;
        auto const& feat = args.feature;
        auto const& vars = args.vars;
        set_join_caps(sym, geom, feat, vars);
        double miterlimit = get<value_double>(sym, keys::stroke_miterlimit, feat, vars, 4.0);
        geom.generator().miter_limit(miterlimit);
        double scale_factor = args.scale_factor;
        double width = get<value_double>(sym, keys::stroke_width, feat, vars, 1.0);
        geom.generator().width(width * scale_factor);
    }
};

template <typename T>
struct converter_traits<T,mapnik::clip_poly_tag>
{
    using geometry_type = T;
    using conv_type = typename agg::conv_clip_polygon<geometry_type>;
    template <typename Args>
    static void setup(geometry_type & geom, Args const& args)
    {
        auto const& box = args.bbox;
        geom.clip_box(box.minx(),box.miny(),box.maxx(),box.maxy());
    }
};

template <typename T>
struct converter_traits<T,mapnik::close_poly_tag>
{
    using geometry_type = T;
    using conv_type = typename agg::conv_close_polygon<geometry_type>;
    template <typename Args>
    static void setup(geometry_type & , Args const&) {}
};

template <typename T>
struct converter_traits<T,mapnik::transform_tag>
{
    using geometry_type = T;
    using conv_type = transform_path_adapter<view_transform, geometry_type>;

    template <typename Args>
    static void setup(geometry_type & geom, Args const& args)
    {
        geom.set_proj_trans(args.prj_trans);
        geom.set_trans(args.tr);
    }
};

template <typename T>
struct converter_traits<T,mapnik::affine_transform_tag>
{
    using geometry_type = T;
    using conv_base_type =  agg::conv_transform<geometry_type, agg::trans_affine const>;
    struct conv_type : public conv_base_type
    {
        conv_type(geometry_type& geom)
            : conv_base_type(geom, agg::trans_affine::identity) {}
    };

    template <typename Args>
    static void setup(geometry_type & geom, Args & args)
    {
        geom.transformer(args.affine_trans);
    }
};

template <typename T>
struct converter_traits<T,mapnik::offset_transform_tag>
{
    using geometry_type = T;
    using conv_type = offset_converter<geometry_type>;

    template <typename Args>
    static void setup(geometry_type & geom, Args const& args)
    {
        auto const& sym = args.sym;
        auto const& feat = args.feature;
        auto const& vars = args.vars;
        double offset = get<value_double>(sym, keys::offset, feat, vars);
        geom.set_offset(offset * args.scale_factor);
    }
};

template <typename Dispatcher, typename... ConverterTypes>
struct converters_helper;

template <typename Dispatcher, typename Current, typename... ConverterTypes>
struct converters_helper<Dispatcher,Current,ConverterTypes...>
{
    template <typename Converter>
    static void set(Dispatcher & disp, int state)
    {
        if (std::is_same<Converter,Current>::value)
        {
            constexpr std::size_t index = sizeof...(ConverterTypes) ;
            disp.vec_[index] = state;
        }
        else
        {
            converters_helper<Dispatcher,ConverterTypes...>:: template set<Converter>(disp, state);
        }
    }

    template <typename Geometry>
    static void forward(Dispatcher & disp, Geometry & geom)
    {
        constexpr std::size_t index = sizeof...(ConverterTypes);
        if (disp.vec_[index] == 1)
        {
            using conv_type = typename detail::converter_traits<Geometry,Current>::conv_type;
            conv_type conv(geom);
            detail::converter_traits<conv_type,Current>::setup(conv,disp.args_);
            converters_helper<Dispatcher, ConverterTypes...>::forward(disp, conv);
        }
        else
        {
            converters_helper<Dispatcher,ConverterTypes...>::forward(disp, geom);
        }
    }
};

template <typename Dispatcher>
struct converters_helper<Dispatcher>
{
    template <typename Converter>
    static void set(Dispatcher & disp, int state) {}
    template <typename Geometry>
    static void forward(Dispatcher & disp, Geometry & geom)
    {
        disp.args_.proc.add_path(geom);
    }
};

template <typename Args, typename... ConverterTypes>
struct dispatcher : mapnik::noncopyable
{
    using this_type = dispatcher;
    using args_type = Args;

    dispatcher(typename Args::processor_type & proc, box2d<double> const& bbox, symbolizer_base const& sym, view_transform const& tr,
               proj_transform const& prj_trans, agg::trans_affine const& affine_trans, feature_impl const& feature,
               attributes const& vars, double scale_factor)
        : args_(proc,bbox,sym,tr,prj_trans,affine_trans,feature,vars,scale_factor)
    {
        std::fill(vec_.begin(), vec_.end(), 0);
    }

    std::array<unsigned, sizeof...(ConverterTypes)> vec_;
    args_type args_;
};

template <typename Processor>
struct arguments : mapnik::noncopyable
{
    using processor_type = Processor;
    arguments(Processor & proc, box2d<double> const& bbox, symbolizer_base const& sym, view_transform const& tr,
              proj_transform const& prj_trans, agg::trans_affine const& affine_trans, feature_impl const& feature,
              attributes const& vars, double scale_factor)
        : proc(proc),
          bbox(bbox),
          sym(sym),
          tr(tr),
          prj_trans(prj_trans),
          affine_trans(affine_trans),
          feature(feature),
          vars(vars),
          scale_factor(scale_factor) {}

    Processor & proc;
    box2d<double> const& bbox;
    symbolizer_base const& sym;
    view_transform const& tr;
    proj_transform const& prj_trans;
    agg::trans_affine const& affine_trans;
    feature_impl const& feature;
    attributes const& vars;
    double scale_factor;
};

}

template <typename Processor, typename... ConverterTypes >
struct vertex_converter : private mapnik::noncopyable
{
    using bbox_type = box2d<double>;
    using processor_type = Processor;
    using symbolizer_type = symbolizer_base;
    using trans_type = view_transform;
    using proj_trans_type = proj_transform;
    using affine_trans_type = agg::trans_affine;
    using feature_type = feature_impl;
    using args_type = detail::arguments<Processor>;
    using dispatcher_type = detail::dispatcher<args_type,ConverterTypes...>;

    vertex_converter(bbox_type const& bbox,
                     processor_type & proc,
                     symbolizer_type const& sym,
                     trans_type const& tr,
                     proj_trans_type const& prj_trans,
                     affine_trans_type const& affine_trans,
                     feature_type const& feature,
                     attributes const& vars,
                     double scale_factor)
        : disp_(proc,bbox,sym,tr,prj_trans,affine_trans,feature,vars,scale_factor) {}

    template <typename Geometry>
    void apply(Geometry & geom)
    {
        detail::converters_helper<dispatcher_type, ConverterTypes...>:: template forward<Geometry>(disp_, geom);
    }

    template <typename Converter>
    void set()
    {
        detail::converters_helper<dispatcher_type, ConverterTypes...>:: template set<Converter>(disp_, 1);
    }

    template <typename Converter>
    void unset()
    {
        detail::converters_helper<dispatcher_type, ConverterTypes...>:: template set<Converter>(disp_, 0);
    }

    dispatcher_type disp_;
};

}

#endif // MAPNIK_VERTEX_CONVERTERS_HPP
<|endoftext|>


<commit_before>#pragma once
#ifndef NIFTY_GRAPH_SIMPLE_GRAPH_HXX
#define NIFTY_GRAPH_SIMPLE_GRAPH_HXX

#include <vector>
#include <boost/version.hpp>

#if BOOST_VERSION  < 104700
#include <boost/interprocess/container/flat_set.hpp>
#include <boost/interprocess/containers/flat_set.hpp>
#else
#include <boost/container/flat_set.hpp>
#endif

#include <boost/iterator/counting_iterator.hpp>

#include "nifty/tools/runtime_check.hxx"
#include "nifty/graph/undirected_graph_base.hxx"
#include "nifty/graph/detail/adjacency.hxx"
#include "nifty/graph/graph_tags.hxx"

namespace nifty{
namespace graph{

namespace detail_graph{
    template<class EDGE_INTERANL_TYPE, class NODE_INTERNAL_TYPE >
    struct UndirectedGraphTypeHelper{
        typedef EDGE_INTERANL_TYPE EdgeInternalType;
        typedef NODE_INTERNAL_TYPE NodeInteralType;
        typedef detail_graph::UndirectedAdjacency<int64_t,int64_t,NodeInteralType,EdgeInternalType> NodeAdjacency;
        //typedef std::set<NodeAdjacency > NodeStorage;
        typedef boost::container::flat_set<NodeAdjacency> NodeStorage;

        typedef std::pair<NodeInteralType,NodeInteralType> EdgeStorage;
        typedef boost::counting_iterator<int64_t> NodeIter;
        typedef boost::counting_iterator<int64_t> EdgeIter;
        typedef typename NodeStorage::const_iterator AdjacencyIter;
    };

    class SimpleGraphNodeIter : public boost::counting_iterator<int64_t>{
        using boost::counting_iterator<int64_t>::counting_iterator;
        using boost::counting_iterator<int64_t>::operator=;
    };

    class SimpleGraphEdgeIter : public boost::counting_iterator<int64_t>{
        using boost::counting_iterator<int64_t>::counting_iterator;
        using boost::counting_iterator<int64_t>::operator=;
    };
};

template<class EDGE_INTERANL_TYPE = int64_t,
         class NODE_INTERNAL_TYPE = int64_t>
class UndirectedGraph : public
    UndirectedGraphBase<
        UndirectedGraph<EDGE_INTERANL_TYPE,NODE_INTERNAL_TYPE>,
        detail_graph::SimpleGraphNodeIter,
        detail_graph::SimpleGraphEdgeIter,
        typename detail_graph::UndirectedGraphTypeHelper<EDGE_INTERANL_TYPE,NODE_INTERNAL_TYPE>::AdjacencyIter
    >
{
private:
    typedef EDGE_INTERANL_TYPE EdgeInternalType;
    typedef NODE_INTERNAL_TYPE NodeInteralType;
    typedef detail_graph::UndirectedAdjacency<int64_t,int64_t,NodeInteralType,EdgeInternalType> NodeAdjacency;
    typedef boost::container::flat_set<NodeAdjacency> NodeStorage;
    typedef std::pair<EdgeInternalType,EdgeInternalType> EdgeStorage;
public:
    typedef detail_graph::SimpleGraphNodeIter NodeIter;
    typedef boost::counting_iterator<int64_t> EdgeIter;
    typedef typename NodeStorage::const_iterator AdjacencyIter;

    typedef ContiguousTag EdgeIdTag;
    typedef ContiguousTag NodeIdTag;

    // constructors
    UndirectedGraph(const uint64_t numberOfNodes = 0, const uint64_t reserveNumberOfEdges = 0)
    :   nodes_(numberOfNodes),
        edges_()
    {
        edges_.reserve(reserveNumberOfEdges);
    }

    void assign(const uint64_t numberOfNodes = 0, const uint64_t reserveNumberOfEdges = 0){
        nodes_.clear();
        edges_.clear();
        nodes_.resize(numberOfNodes);
        edges_.reserve(reserveNumberOfEdges);
    }

    int64_t insertEdge(const int64_t u, const int64_t v){
        auto & adjU = nodes_[u];
        auto & adjV = nodes_[v];
        const auto fres =  nodes_[u].find(NodeAdjacency(v));
        if(fres != nodes_[u].end())
            return fres->edge();
        else{
            const auto uu = std::min(u,v);
            const auto vv = std::max(u,v);
            auto e = EdgeStorage(uu, vv);
            auto ei = edges_.size();
            edges_.push_back(e);
            nodes_[u].insert(NodeAdjacency(v,ei));
            nodes_[v].insert(NodeAdjacency(u,ei));
            return ei;
        }
    }

    // MUST IMPL INTERFACE
    int64_t u(const int64_t e)const{
        NIFTY_ASSERT_OP(e,<,numberOfEdges());
        return edges_[e].first;
    }
    int64_t v(const int64_t e)const{
        NIFTY_ASSERT_OP(e,<,numberOfEdges());
        return edges_[e].second;
    }

    int64_t findEdge(const int64_t u, const int64_t v)const{
        NIFTY_ASSERT_OP(u,<,numberOfNodes());
        NIFTY_ASSERT_OP(v,<,numberOfNodes());
        const auto fres =  nodes_[u].find(NodeAdjacency(v));
        if(fres != nodes_[u].end())
            return fres->edge();
        else
            return -1;
    }
    int64_t maxNodeId() const{return numberOfNodes() == 0 ? -1 : numberOfNodes()-1;}
    int64_t maxEdgeId() const{return numberOfEdges() == 0 ? -1 : numberOfEdges()-1;}
    int64_t numberOfEdges() const {return edges_.size();}
    int64_t numberOfNodes() const{return nodes_.size();}

    NodeIter nodesBegin()const{return NodeIter(0);}
    NodeIter nodesEnd()const{return NodeIter(this->numberOfNodes());}
    EdgeIter edgesBegin()const{return EdgeIter(0);}
    EdgeIter edgesEnd()const{return EdgeIter(this->numberOfEdges());}

    AdjacencyIter adjacencyBegin(const int64_t node)const{
        NIFTY_ASSERT_OP(node,<,numberOfNodes());
        return nodes_[node].begin();
    }
    AdjacencyIter adjacencyEnd(const int64_t node)const{
        NIFTY_ASSERT_OP(node,<,numberOfNodes());
        return nodes_[node].end();
    }

    AdjacencyIter adjacencyOutBegin(const int64_t node)const{
        return adjacencyBegin(node);
    }

    // optional (with default impl in base)
    std::pair<int64_t,int64_t> uv(const int64_t e)const{
        const auto _uv = edges_[e];
        return std::pair<int64_t,int64_t>(_uv.first, _uv.second);
    }
    template<class F>
    void forEachEdge(F && f)const{
        for(uint64_t edge=0; edge< numberOfEdges(); ++edge){
            f(edge);
        }
    }

private:

    std::vector<NodeStorage> nodes_;
    std::vector<EdgeStorage> edges_;
};

} // namespace nifty::graph
} // namespace nifty

#endif  // NIFTY_GRAPH_SIMPLE_GRAPH_HXX
<commit_msg>new try<commit_after>#pragma once
#ifndef NIFTY_GRAPH_SIMPLE_GRAPH_HXX
#define NIFTY_GRAPH_SIMPLE_GRAPH_HXX

#include <vector>
#include <boost/version.hpp>

#if BOOST_VERSION  <= 104600
#include <boost/interprocess/containers/flat_set.hpp>
#else
#include <boost/container/flat_set.hpp>
#endif

#include <boost/iterator/counting_iterator.hpp>

#include "nifty/tools/runtime_check.hxx"
#include "nifty/graph/undirected_graph_base.hxx"
#include "nifty/graph/detail/adjacency.hxx"
#include "nifty/graph/graph_tags.hxx"

namespace nifty{
namespace graph{

namespace detail_graph{
    template<class EDGE_INTERANL_TYPE, class NODE_INTERNAL_TYPE >
    struct UndirectedGraphTypeHelper{
        typedef EDGE_INTERANL_TYPE EdgeInternalType;
        typedef NODE_INTERNAL_TYPE NodeInteralType;
        typedef detail_graph::UndirectedAdjacency<int64_t,int64_t,NodeInteralType,EdgeInternalType> NodeAdjacency;
        //typedef std::set<NodeAdjacency > NodeStorage;
        typedef boost::container::flat_set<NodeAdjacency> NodeStorage;

        typedef std::pair<NodeInteralType,NodeInteralType> EdgeStorage;
        typedef boost::counting_iterator<int64_t> NodeIter;
        typedef boost::counting_iterator<int64_t> EdgeIter;
        typedef typename NodeStorage::const_iterator AdjacencyIter;
    };

    class SimpleGraphNodeIter : public boost::counting_iterator<int64_t>{
        using boost::counting_iterator<int64_t>::counting_iterator;
        using boost::counting_iterator<int64_t>::operator=;
    };

    class SimpleGraphEdgeIter : public boost::counting_iterator<int64_t>{
        using boost::counting_iterator<int64_t>::counting_iterator;
        using boost::counting_iterator<int64_t>::operator=;
    };
};

template<class EDGE_INTERANL_TYPE = int64_t,
         class NODE_INTERNAL_TYPE = int64_t>
class UndirectedGraph : public
    UndirectedGraphBase<
        UndirectedGraph<EDGE_INTERANL_TYPE,NODE_INTERNAL_TYPE>,
        detail_graph::SimpleGraphNodeIter,
        detail_graph::SimpleGraphEdgeIter,
        typename detail_graph::UndirectedGraphTypeHelper<EDGE_INTERANL_TYPE,NODE_INTERNAL_TYPE>::AdjacencyIter
    >
{
private:
    typedef EDGE_INTERANL_TYPE EdgeInternalType;
    typedef NODE_INTERNAL_TYPE NodeInteralType;
    typedef detail_graph::UndirectedAdjacency<int64_t,int64_t,NodeInteralType,EdgeInternalType> NodeAdjacency;
    typedef boost::container::flat_set<NodeAdjacency> NodeStorage;
    typedef std::pair<EdgeInternalType,EdgeInternalType> EdgeStorage;
public:
    typedef detail_graph::SimpleGraphNodeIter NodeIter;
    typedef boost::counting_iterator<int64_t> EdgeIter;
    typedef typename NodeStorage::const_iterator AdjacencyIter;

    typedef ContiguousTag EdgeIdTag;
    typedef ContiguousTag NodeIdTag;

    // constructors
    UndirectedGraph(const uint64_t numberOfNodes = 0, const uint64_t reserveNumberOfEdges = 0)
    :   nodes_(numberOfNodes),
        edges_()
    {
        edges_.reserve(reserveNumberOfEdges);
    }

    void assign(const uint64_t numberOfNodes = 0, const uint64_t reserveNumberOfEdges = 0){
        nodes_.clear();
        edges_.clear();
        nodes_.resize(numberOfNodes);
        edges_.reserve(reserveNumberOfEdges);
    }

    int64_t insertEdge(const int64_t u, const int64_t v){
        auto & adjU = nodes_[u];
        auto & adjV = nodes_[v];
        const auto fres =  nodes_[u].find(NodeAdjacency(v));
        if(fres != nodes_[u].end())
            return fres->edge();
        else{
            const auto uu = std::min(u,v);
            const auto vv = std::max(u,v);
            auto e = EdgeStorage(uu, vv);
            auto ei = edges_.size();
            edges_.push_back(e);
            nodes_[u].insert(NodeAdjacency(v,ei));
            nodes_[v].insert(NodeAdjacency(u,ei));
            return ei;
        }
    }

    // MUST IMPL INTERFACE
    int64_t u(const int64_t e)const{
        NIFTY_ASSERT_OP(e,<,numberOfEdges());
        return edges_[e].first;
    }
    int64_t v(const int64_t e)const{
        NIFTY_ASSERT_OP(e,<,numberOfEdges());
        return edges_[e].second;
    }

    int64_t findEdge(const int64_t u, const int64_t v)const{
        NIFTY_ASSERT_OP(u,<,numberOfNodes());
        NIFTY_ASSERT_OP(v,<,numberOfNodes());
        const auto fres =  nodes_[u].find(NodeAdjacency(v));
        if(fres != nodes_[u].end())
            return fres->edge();
        else
            return -1;
    }
    int64_t maxNodeId() const{return numberOfNodes() == 0 ? -1 : numberOfNodes()-1;}
    int64_t maxEdgeId() const{return numberOfEdges() == 0 ? -1 : numberOfEdges()-1;}
    int64_t numberOfEdges() const {return edges_.size();}
    int64_t numberOfNodes() const{return nodes_.size();}

    NodeIter nodesBegin()const{return NodeIter(0);}
    NodeIter nodesEnd()const{return NodeIter(this->numberOfNodes());}
    EdgeIter edgesBegin()const{return EdgeIter(0);}
    EdgeIter edgesEnd()const{return EdgeIter(this->numberOfEdges());}

    AdjacencyIter adjacencyBegin(const int64_t node)const{
        NIFTY_ASSERT_OP(node,<,numberOfNodes());
        return nodes_[node].begin();
    }
    AdjacencyIter adjacencyEnd(const int64_t node)const{
        NIFTY_ASSERT_OP(node,<,numberOfNodes());
        return nodes_[node].end();
    }

    AdjacencyIter adjacencyOutBegin(const int64_t node)const{
        return adjacencyBegin(node);
    }

    // optional (with default impl in base)
    std::pair<int64_t,int64_t> uv(const int64_t e)const{
        const auto _uv = edges_[e];
        return std::pair<int64_t,int64_t>(_uv.first, _uv.second);
    }
    template<class F>
    void forEachEdge(F && f)const{
        for(uint64_t edge=0; edge< numberOfEdges(); ++edge){
            f(edge);
        }
    }

private:

    std::vector<NodeStorage> nodes_;
    std::vector<EdgeStorage> edges_;
};

} // namespace nifty::graph
} // namespace nifty

#endif  // NIFTY_GRAPH_SIMPLE_GRAPH_HXX
<|endoftext|>


<commit_before>#include <QtGui>
#include "metadataWindow.h"

QString metadataWindow::base64_encode(QString string){
    QByteArray ba;
    ba.append(string);
    return ba.toBase64();
}

QString metadataWindow::base64_decode(QString string){
    QByteArray ba;
    ba.append(string);
    return QByteArray::fromBase64(ba);
}

static void SetTextToLabel(QLabel *label, QString text)
{
    QFontMetrics metrix(label->font());
    int width = label->width() - 2;
    QString clippedText = metrix.elidedText(text, Qt::ElideRight, width);
    label->setText(clippedText);
}

metadataWindow::metadataWindow(QWidget *parent) : QWidget(parent)
{
    const char *metadata_file = "/tmp/shairport-sync-metadata";

    // Initialize the Track object with empty strings and the like
    initialise_track_object();

    this->setupUI();

    // Connect to the metadata pipe
    FILE *fd = fopen(metadata_file, "r");
    if (fd==NULL)
    {
        cout << "No metadata file found, testing UI." << "\n";
    } else {
        // Use for actual reads
        pipe = new QTextStream(fd);

        // Use for events when the pipe is filled
        streamReader = new QSocketNotifier(fileno(fd), QSocketNotifier::Read, qApp);
        QObject::connect(streamReader, SIGNAL(activated(int)), this, SLOT(onData()));
        streamReader->setEnabled(true);

        // Now disable cursor
        QApplication::setOverrideCursor(Qt::BlankCursor);
    }
}

void metadataWindow::setupUI()
{
    QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));

    // Create Fonts
    standard_font = new QFont("Droid Sans");
    standard_font->setPixelSize(14);

    title_font = new QFont("Droid Sans");
    title_font->setPixelSize(22);

    title_em_font = new QFont("Droid Sans");
    title_em_font->setPixelSize(22);
    title_em_font->setItalic(true);

    em_font = new QFont("Droid Sans");
    em_font->setPixelSize(16);
    em_font->setItalic(true);

    // Add cover image
    size = new QSize(120,120);
    image = new QPixmap(":/images/default_cover");
    image_label  = new QLabel();
        image_label->setPixmap(*image);
        image_label->setFixedHeight(size->height());
        image_label->setFixedWidth(size->width());

    // Add labels
    QPixmap note(":/icons/note");
    QLabel *title_label_icon = new QLabel();
    title_label_icon->setPixmap(note);
    title_label_icon->setFixedWidth(20);

    title_label = new QLabel("N/A");
    title_label->setFont(*title_em_font);
    title_label->setFixedHeight(50);
    title_label->setStyleSheet("background-color: #123456; padding-right:6px;");

    QHBoxLayout *title_label_layout = new QHBoxLayout();
    title_label_layout->addWidget(title_label_icon);
    title_label_layout->addWidget(title_label);

    // Artist widget
    QPixmap artist(":/icons/artist");
    QLabel *artist_label_icon = new QLabel();
    artist_label_icon->setPixmap(artist);
    artist_label_icon->setFixedWidth(20);

    artist_label = new QLabel("N/A");
    artist_label->setFont(*em_font);
    artist_label->setWordWrap(true);
    artist_label->setStyleSheet("background-color: #35467A; padding-right:6px;");

    QHBoxLayout *artist_label_layout = new QHBoxLayout();
    artist_label_layout->addWidget(artist_label_icon);
    artist_label_layout->addWidget(artist_label);

    // Album widget
    QPixmap cd(":/icons/cd");
    QLabel *release_label_icon = new QLabel();
    release_label_icon->setPixmap(cd);
    release_label_icon->setFixedWidth(20);

    release_label = new QLabel("N/A");
    release_label->setFont(*em_font);
    release_label->setWordWrap(true);
    release_label->setStyleSheet("background-color: #C593E2; padding-right:6px;");

    QHBoxLayout *release_label_layout = new QHBoxLayout();
    release_label_layout->addWidget(release_label_icon);
    release_label_layout->addWidget(release_label);

    // Status bar
    QMovie *load = new QMovie(":/icons/load");
    status_label_icon = new QLabel();
    status_label_icon->setMovie(load);
    load->start();
    status_label_icon->setFixedWidth(20);

    status_label = new QLabel("En attente de données ...");
    status_label->setStyleSheet("background-color: #177AC3; padding-right:6px;");
    status_label->setFixedHeight(30);
    status_label->setFont(*standard_font);

    QHBoxLayout *status_label_layout = new QHBoxLayout();
    status_label_layout->addWidget(status_label_icon);
    status_label_layout->addWidget(status_label);

    // Make the whole layout
    QVBoxLayout *main_layout = new QVBoxLayout();
    main_layout->setContentsMargins(2,12,2,12);

    QHBoxLayout *hbl = new QHBoxLayout();

    QVBoxLayout *vbl = new QVBoxLayout();
    vbl->addLayout(artist_label_layout);
    vbl->addLayout(release_label_layout);

    hbl->addWidget(image_label);
    hbl->addLayout(vbl);

    main_layout->addLayout(title_label_layout);
    main_layout->addLayout(hbl);
    main_layout->addLayout(status_label_layout);

    this->setLayout(main_layout);
}

void metadataWindow::initialise_track_object()
{
    track.title.clear();
    track.artist.clear();
    track.release.clear();
    track.playing = false;
    client_ip.clear();
    client_name.clear();
}

void metadataWindow::updateUI()
{
    if (track.title.empty()) {
        title_label->setFont(*title_em_font);
        SetTextToLabel(title_label, "N/A");
    } else {
        title_label->setFont(*title_font);
        SetTextToLabel(title_label,QString::fromStdString(track.title));
    }

    artist_label->setText(QString::fromStdString(track.artist));
    artist_label->setFont(*standard_font);

    release_label->setText(QString::fromStdString(track.release));
    release_label->setFont(*standard_font);

    if (client_name.length() != 0 && track.playing) {
        status_label->setFont(*standard_font);
        SetTextToLabel(status_label, "Lecture depuis " + QString::fromStdString(client_name));
        QPixmap device(":/icons/yt");
        status_label_icon->setPixmap(device);
    } else if (client_name.length() != 0) {
        status_label->setFont(*standard_font);
        SetTextToLabel(status_label, "En pause depuis " + QString::fromStdString(client_name));
        QPixmap device(":/icons/yt");
        status_label_icon->setPixmap(device);
    } else if (client_ip.length() != 0 && track.playing) {
        status_label->setFont(*standard_font);
        SetTextToLabel(status_label, "Lecture depuis " + QString::fromStdString(client_ip));
        QPixmap device(":/icons/device");
        status_label_icon->setPixmap(device);
    } else if (client_ip.length() != 0) {
        status_label->setFont(*standard_font);
        SetTextToLabel(status_label, "En pause depuis " + QString::fromStdString(client_ip));
        QPixmap device(":/icons/device");
        status_label_icon->setPixmap(device);
    } else if (track.playing) {
        status_label->setFont(*standard_font);
        status_label->setText("Lecture en cours");
        QPixmap collection(":/icons/collection");
        status_label_icon->setPixmap(collection);
    } else {
        status_label->setFont(*em_font);
        status_label->setText("Pas de streaming en cours");
        QPixmap load_full(":/icons/load_full");
        status_label_icon->setPixmap(load_full);
    }

    if (!track.image.isNull()) {
        image->convertFromImage(track.image);
        *image = image->scaled(*size, Qt::KeepAspectRatioByExpanding);
        image_label->setPixmap(*image);
        image_label->update();
    }

}

void metadataWindow::onData()
{
    emit dataReceived();
}

void metadataWindow::dataReceived()
{
    //cout << "\nProcessing new metadata ...\n";

    if (pipe->atEnd()) {
        cout << " > No more data to read ...\n";
        return;
    }

    QString line = pipe->readLine();
    if (line.isEmpty() || line.isNull()) {
        cout << " > Skipping empty line.\n";
        return;
    }

    if (line == "<data encoding=\"base64\">") {
        cout << "How did we get here ???\n";
    }

    const char * message = line.toStdString().c_str();

    uint32_t type, code, length;
    QImage base64_image;
    bool ret_pic = false;

    //cout << "Message: [" << message << "]\n";
    int ret = sscanf(message,"<item><type>%8x</type><code>%8x</code><length>%u</length>",&type,&code,&length);

    // Get code and type strings
    char typestring[5];
    *(uint32_t*)typestring = htonl(type);
    typestring[4] = 0;
    char codestring[5];
    *(uint32_t*)codestring = htonl(code);
    codestring[4] = 0;

    cout << " > Found tag: " << typestring << " / " << codestring << " of length " << length << "\n";

    if (type > 0 && code > 0 && ret == 3) {
        // now, think about processing the tag.
        // basically, we need to get hold of the base-64 data, if any
        QString payload;

        if (length > 0) {
            //cout << " > Extracting data ..." << "\n";

            // get the next line, which should be a data tag
            QString line = pipe->readLine();

            if (line == "<data encoding=\"base64\">") {
                // now, read in that big (possibly) base64 buffer
                //cout << " > Data is base64 buffer" << "\n";

                QString line = pipe->readLine();
                if (line.size() > 14) {
                    line.chop(14); // remove "</data></item>"
                    if (code != 'PICT') {
                        payload = base64_decode(line);
                    } else {
                        QByteArray base64Data = line.toAscii();
                        ret_pic = base64_image.loadFromData(QByteArray::fromBase64(base64Data));
                    }
                } else {
                    //cout << " > Looks like a bad payload" << "\n";
                }
            }

            //cout << " > Got it decoded. Length of decoded string is " << payload.size() << " bytes.\n";
        }

        // this has more information about tags, which might be relevant:
        // https://code.google.com/p/ytrack/wiki/DMAP
        if (code == 'asal') {
            track.release = payload.toStdString();
            //cout << "Album Name: " << payload.toStdString() << "\n";
        } else if (code == 'asar' || code == 'asaa') {
            track.artist = payload.toStdString();
            //cout << "Artist: " << payload.toStdString() << "\n";
        } else if (code == 'minm') {
            track.title = payload.toStdString();
            //cout << "Title: " << payload.toStdString() << "\n";
        } else if (code == 'PICT' && ret_pic) {
            track.image = base64_image;
            //cout << "Picture received, length " << length << " bytes." << "\n";
        } else if (code == 'clip') {
            client_ip = payload.toStdString();
            //cout << "Client's IP: " << payload.toStdString() << "\n";
        } else if (code == 'snam') {
            client_name = payload.toStdString();
            //cout << "Device Name: " << payload.toStdString() << "\n";
        } else if (code == 'snua') {
            client_name = payload.toStdString();
            //cout << "User Agent: " << payload.toStdString() << "\n";
        } else if (code == 'prsm' || code =='pbeg') {
            track.playing = true;
            //cout << "Started playing" << "\n";
        } else if (code == 'pend') {
            track.playing = false;
            //cout << "Stopped playing" << "\n";
        } else if (type=='ssnc') {
            //cout << "SSNC Stuff : " << typestring << "/" << codestring << " : " << payload.toStdString() << "\n";
        } else {
            //cout << "Other Stuff : " << typestring << "/" << codestring << " : " << payload.toStdString() << "\n";
        }

    } else {
        //cout << "Could not decipher the message.\n";;
    }

    pipe->flush();
    updateUI();
}
<commit_msg>Fix asaa when empty<commit_after>#include <QtGui>
#include "metadataWindow.h"

QString metadataWindow::base64_encode(QString string){
    QByteArray ba;
    ba.append(string);
    return ba.toBase64();
}

QString metadataWindow::base64_decode(QString string){
    QByteArray ba;
    ba.append(string);
    return QByteArray::fromBase64(ba);
}

static void SetTextToLabel(QLabel *label, QString text)
{
    QFontMetrics metrix(label->font());
    int width = label->width() - 2;
    QString clippedText = metrix.elidedText(text, Qt::ElideRight, width);
    label->setText(clippedText);
}

metadataWindow::metadataWindow(QWidget *parent) : QWidget(parent)
{
    const char *metadata_file = "/tmp/shairport-sync-metadata";

    // Initialize the Track object with empty strings and the like
    initialise_track_object();

    this->setupUI();

    // Connect to the metadata pipe
    FILE *fd = fopen(metadata_file, "r");
    if (fd==NULL)
    {
        cout << "No metadata file found, testing UI." << "\n";
    } else {
        // Use for actual reads
        pipe = new QTextStream(fd);

        // Use for events when the pipe is filled
        streamReader = new QSocketNotifier(fileno(fd), QSocketNotifier::Read, qApp);
        QObject::connect(streamReader, SIGNAL(activated(int)), this, SLOT(onData()));
        streamReader->setEnabled(true);

        // Now disable cursor
        QApplication::setOverrideCursor(Qt::BlankCursor);
    }
}

void metadataWindow::setupUI()
{
    QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));

    // Create Fonts
    standard_font = new QFont("Droid Sans");
    standard_font->setPixelSize(14);

    title_font = new QFont("Droid Sans");
    title_font->setPixelSize(22);

    title_em_font = new QFont("Droid Sans");
    title_em_font->setPixelSize(22);
    title_em_font->setItalic(true);

    em_font = new QFont("Droid Sans");
    em_font->setPixelSize(16);
    em_font->setItalic(true);

    // Add cover image
    size = new QSize(120,120);
    image = new QPixmap(":/images/default_cover");
    image_label  = new QLabel();
        image_label->setPixmap(*image);
        image_label->setFixedHeight(size->height());
        image_label->setFixedWidth(size->width());

    // Add labels
    QPixmap note(":/icons/note");
    QLabel *title_label_icon = new QLabel();
    title_label_icon->setPixmap(note);
    title_label_icon->setFixedWidth(20);

    title_label = new QLabel("N/A");
    title_label->setFont(*title_em_font);
    title_label->setFixedHeight(50);
    title_label->setStyleSheet("background-color: #123456; padding-right:6px;");

    QHBoxLayout *title_label_layout = new QHBoxLayout();
    title_label_layout->addWidget(title_label_icon);
    title_label_layout->addWidget(title_label);

    // Artist widget
    QPixmap artist(":/icons/artist");
    QLabel *artist_label_icon = new QLabel();
    artist_label_icon->setPixmap(artist);
    artist_label_icon->setFixedWidth(20);

    artist_label = new QLabel("N/A");
    artist_label->setFont(*em_font);
    artist_label->setWordWrap(true);
    artist_label->setStyleSheet("background-color: #35467A; padding-right:6px;");

    QHBoxLayout *artist_label_layout = new QHBoxLayout();
    artist_label_layout->addWidget(artist_label_icon);
    artist_label_layout->addWidget(artist_label);

    // Album widget
    QPixmap cd(":/icons/cd");
    QLabel *release_label_icon = new QLabel();
    release_label_icon->setPixmap(cd);
    release_label_icon->setFixedWidth(20);

    release_label = new QLabel("N/A");
    release_label->setFont(*em_font);
    release_label->setWordWrap(true);
    release_label->setStyleSheet("background-color: #C593E2; padding-right:6px;");

    QHBoxLayout *release_label_layout = new QHBoxLayout();
    release_label_layout->addWidget(release_label_icon);
    release_label_layout->addWidget(release_label);

    // Status bar
    QMovie *load = new QMovie(":/icons/load");
    status_label_icon = new QLabel();
    status_label_icon->setMovie(load);
    load->start();
    status_label_icon->setFixedWidth(20);

    status_label = new QLabel("En attente de données ...");
    status_label->setStyleSheet("background-color: #177AC3; padding-right:6px;");
    status_label->setFixedHeight(30);
    status_label->setFont(*standard_font);

    QHBoxLayout *status_label_layout = new QHBoxLayout();
    status_label_layout->addWidget(status_label_icon);
    status_label_layout->addWidget(status_label);

    // Make the whole layout
    QVBoxLayout *main_layout = new QVBoxLayout();
    main_layout->setContentsMargins(2,12,2,12);

    QHBoxLayout *hbl = new QHBoxLayout();

    QVBoxLayout *vbl = new QVBoxLayout();
    vbl->addLayout(artist_label_layout);
    vbl->addLayout(release_label_layout);

    hbl->addWidget(image_label);
    hbl->addLayout(vbl);

    main_layout->addLayout(title_label_layout);
    main_layout->addLayout(hbl);
    main_layout->addLayout(status_label_layout);

    this->setLayout(main_layout);
}

void metadataWindow::initialise_track_object()
{
    track.title.clear();
    track.artist.clear();
    track.release.clear();
    track.playing = false;
    client_ip.clear();
    client_name.clear();
}

void metadataWindow::updateUI()
{
    if (track.title.empty()) {
        title_label->setFont(*title_em_font);
        SetTextToLabel(title_label, "N/A");
    } else {
        title_label->setFont(*title_font);
        SetTextToLabel(title_label,QString::fromStdString(track.title));
    }

    artist_label->setText(QString::fromStdString(track.artist));
    artist_label->setFont(*standard_font);

    release_label->setText(QString::fromStdString(track.release));
    release_label->setFont(*standard_font);

    if (client_name.length() != 0 && track.playing) {
        status_label->setFont(*standard_font);
        SetTextToLabel(status_label, "Lecture depuis " + QString::fromStdString(client_name));
        QPixmap device(":/icons/yt");
        status_label_icon->setPixmap(device);
    } else if (client_name.length() != 0) {
        status_label->setFont(*standard_font);
        SetTextToLabel(status_label, "En pause depuis " + QString::fromStdString(client_name));
        QPixmap device(":/icons/yt");
        status_label_icon->setPixmap(device);
    } else if (client_ip.length() != 0 && track.playing) {
        status_label->setFont(*standard_font);
        SetTextToLabel(status_label, "Lecture depuis " + QString::fromStdString(client_ip));
        QPixmap device(":/icons/device");
        status_label_icon->setPixmap(device);
    } else if (client_ip.length() != 0) {
        status_label->setFont(*standard_font);
        SetTextToLabel(status_label, "En pause depuis " + QString::fromStdString(client_ip));
        QPixmap device(":/icons/device");
        status_label_icon->setPixmap(device);
    } else if (track.playing) {
        status_label->setFont(*standard_font);
        status_label->setText("Lecture en cours");
        QPixmap collection(":/icons/collection");
        status_label_icon->setPixmap(collection);
    } else {
        status_label->setFont(*em_font);
        status_label->setText("Pas de streaming en cours");
        QPixmap load_full(":/icons/load_full");
        status_label_icon->setPixmap(load_full);
    }

    if (!track.image.isNull()) {
        image->convertFromImage(track.image);
        *image = image->scaled(*size, Qt::KeepAspectRatioByExpanding);
        image_label->setPixmap(*image);
        image_label->update();
    }

}

void metadataWindow::onData()
{
    emit dataReceived();
}

void metadataWindow::dataReceived()
{
    //cout << "\nProcessing new metadata ...\n";

    if (pipe->atEnd()) {
        cout << " > No more data to read ...\n";
        return;
    }

    QString line = pipe->readLine();
    if (line.isEmpty() || line.isNull()) {
        cout << " > Skipping empty line.\n";
        return;
    }

    if (line == "<data encoding=\"base64\">") {
        cout << "How did we get here ???\n";
    }

    const char * message = line.toStdString().c_str();

    uint32_t type, code, length;
    QImage base64_image;
    bool ret_pic = false;

    //cout << "Message: [" << message << "]\n";
    int ret = sscanf(message,"<item><type>%8x</type><code>%8x</code><length>%u</length>",&type,&code,&length);

    // Get code and type strings
    char typestring[5];
    *(uint32_t*)typestring = htonl(type);
    typestring[4] = 0;
    char codestring[5];
    *(uint32_t*)codestring = htonl(code);
    codestring[4] = 0;

    cout << " > Found tag: " << typestring << " / " << codestring << " of length " << length << "\n";

    if (type > 0 && code > 0 && ret == 3) {
        // now, think about processing the tag.
        // basically, we need to get hold of the base-64 data, if any
        QString payload;

        if (length > 0) {
            //cout << " > Extracting data ..." << "\n";

            // get the next line, which should be a data tag
            QString line = pipe->readLine();

            if (line == "<data encoding=\"base64\">") {
                // now, read in that big (possibly) base64 buffer
                //cout << " > Data is base64 buffer" << "\n";

                QString line = pipe->readLine();
                if (line.size() > 14) {
                    line.chop(14); // remove "</data></item>"
                    if (code != 'PICT') {
                        payload = base64_decode(line);
                    } else {
                        QByteArray base64Data = line.toAscii();
                        ret_pic = base64_image.loadFromData(QByteArray::fromBase64(base64Data));
                    }
                } else {
                    //cout << " > Looks like a bad payload" << "\n";
                }
            }

            //cout << " > Got it decoded. Length of decoded string is " << payload.size() << " bytes.\n";
        }

        // this has more information about tags, which might be relevant:
        // https://code.google.com/p/ytrack/wiki/DMAP
        if (code == 'asal') {
            track.release = payload.toStdString();
            //cout << "Album Name: " << payload.toStdString() << "\n";
        } else if (code == 'asar' || (code == 'asaa' && !payload.isEmpty())) {
            track.artist = payload.toStdString();
            //cout << "Artist: " << payload.toStdString() << "\n";
        } else if (code == 'minm') {
            track.title = payload.toStdString();
            //cout << "Title: " << payload.toStdString() << "\n";
        } else if (code == 'PICT' && ret_pic) {
            track.image = base64_image;
            //cout << "Picture received, length " << length << " bytes." << "\n";
        } else if (code == 'clip') {
            client_ip = payload.toStdString();
            //cout << "Client's IP: " << payload.toStdString() << "\n";
        } else if (code == 'snam') {
            client_name = payload.toStdString();
            //cout << "Device Name: " << payload.toStdString() << "\n";
        } else if (code == 'snua') {
            client_name = payload.toStdString();
            //cout << "User Agent: " << payload.toStdString() << "\n";
        } else if (code == 'prsm' || code =='pbeg') {
            track.playing = true;
            //cout << "Started playing" << "\n";
        } else if (code == 'pend') {
            track.playing = false;
            //cout << "Stopped playing" << "\n";
        } else if (type=='ssnc') {
            //cout << "SSNC Stuff : " << typestring << "/" << codestring << " : " << payload.toStdString() << "\n";
        } else {
            //cout << "Other Stuff : " << typestring << "/" << codestring << " : " << payload.toStdString() << "\n";
        }

    } else {
        //cout << "Could not decipher the message.\n";;
    }

    pipe->flush();
    updateUI();
}
<|endoftext|>


<commit_before>/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2003 Robert Osfield
 *
 * This library is open source and may be redistributed and/or modified under
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 *
 * 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
 * OpenSceneGraph Public License for more details.
*/
#include <osg/AnimationPath>
#include <osg/MatrixTransform>
#include <osg/PositionAttitudeTransform>

using namespace osg;

void AnimationPath::insert(double time,const ControlPoint& controlPoint)
{
    _timeControlPointMap[time] = controlPoint;
}

bool AnimationPath::getInterpolatedControlPoint(double time,ControlPoint& controlPoint) const
{
    if (_timeControlPointMap.empty()) return false;

    switch(_loopMode)
    {
        case(SWING):
        {
            double modulated_time = (time - getFirstTime())/(getPeriod()*2.0);
            double fraction_part = modulated_time - floor(modulated_time);
            if (fraction_part>0.5) fraction_part = 1.0-fraction_part;

            time = getFirstTime()+(fraction_part*2.0) * getPeriod();
            break;
        }
        case(LOOP):
        {
            double modulated_time = (time - getFirstTime())/getPeriod();
            double fraction_part = modulated_time - floor(modulated_time);
            time = getFirstTime()+fraction_part * getPeriod();
            break;
        }
        case(NO_LOOPING):
            // no need to modulate the time.
            break;
    }

    TimeControlPointMap::const_iterator second = _timeControlPointMap.lower_bound(time);
    if (second==_timeControlPointMap.begin())
    {
        controlPoint = second->second;
    }
    else if (second!=_timeControlPointMap.end())
    {
        TimeControlPointMap::const_iterator first = second;
        --first;

        // we have both a lower bound and the next item.

        // deta_time = second.time - first.time
        double delta_time = second->first - first->first;

        if (delta_time==0.0)
            controlPoint = first->second;
        else
        {
            controlPoint.interpolate((time - first->first)/delta_time,
                            first->second,
                            second->second);
        }
    }
    else // (second==_timeControlPointMap.end())
    {
        controlPoint = _timeControlPointMap.rbegin()->second;
    }
    return true;
}

void AnimationPath::read(std::istream& in)
{
    while (!in.eof())
    {
        double time;
        osg::Vec3 position;
        osg::Quat rotation;
        in >> time >> position.x() >> position.y() >> position.z() >> rotation.x() >> rotation.y() >> rotation.z() >> rotation.w();
        if(!in.eof())
            insert(time,osg::AnimationPath::ControlPoint(position,rotation));
    }
}

void AnimationPath::write(std::ostream& fout) const
{
    const TimeControlPointMap& tcpm = getTimeControlPointMap();
    for(TimeControlPointMap::const_iterator tcpmitr=tcpm.begin();
        tcpmitr!=tcpm.end();
        ++tcpmitr)
    {
        const ControlPoint& cp = tcpmitr->second;
        fout<<tcpmitr->first<<" "<<cp._position<<" "<<cp._rotation<<std::endl;
    }
}

class AnimationPathCallbackVisitor : public NodeVisitor
{
    public:

        AnimationPathCallbackVisitor(const AnimationPath::ControlPoint& cp, const osg::Vec3d& pivotPoint, bool useInverseMatrix):
            _cp(cp),
            _pivotPoint(pivotPoint),
            _useInverseMatrix(useInverseMatrix) {}

        virtual void apply(MatrixTransform& mt)
        {
            Matrix matrix;
            if (_useInverseMatrix)
                _cp.getInverse(matrix);
            else
                _cp.getMatrix(matrix);

            mt.setMatrix(osg::Matrix::translate(-_pivotPoint)*matrix);
        }

        virtual void apply(PositionAttitudeTransform& pat)
        {
            if (_useInverseMatrix)
            {
                Matrix matrix;
                _cp.getInverse(matrix);
                pat.setPosition(matrix.getTrans());
                pat.setAttitude(_cp._rotation.inverse());
                pat.setScale(osg::Vec3(1.0f/_cp._scale.x(),1.0f/_cp._scale.y(),1.0f/_cp._scale.z()));
                pat.setPivotPoint(_pivotPoint);

            }
            else
            {
                pat.setPosition(_cp._position);
                pat.setAttitude(_cp._rotation);
                pat.setScale(_cp._scale);
                pat.setPivotPoint(_pivotPoint);
            }
        }

        AnimationPath::ControlPoint _cp;
        osg::Vec3d _pivotPoint;
        bool _useInverseMatrix;
};

void AnimationPathCallback::operator()(Node* node, NodeVisitor* nv)
{
    if (_animationPath.valid() &&
        nv->getVisitorType()==NodeVisitor::UPDATE_VISITOR &&
        nv->getFrameStamp())
    {
        double time = nv->getFrameStamp()->getReferenceTime();
        _latestTime = time;

        if (!_pause)
        {
            // Only update _firstTime the first time, when its value is still DBL_MAX
            if (_firstTime==DBL_MAX) _firstTime = time;
            update(*node);
        }
    }

    // must call any nested node callbacks and continue subgraph traversal.
    NodeCallback::traverse(node,nv);
}

double AnimationPathCallback::getAnimationTime() const
{
    return ((_latestTime-_firstTime)-_timeOffset)*_timeMultiplier;
}

void AnimationPathCallback::update(osg::Node& node)
{
    AnimationPath::ControlPoint cp;
    if (_animationPath->getInterpolatedControlPoint(getAnimationTime(),cp))
    {
        AnimationPathCallbackVisitor apcv(cp,_pivotPoint,_useInverseMatrix);
        node.accept(apcv);
    }
}

void AnimationPathCallback::reset()
{
    _firstTime = _latestTime;
    _pauseTime = _latestTime;
}

void AnimationPathCallback::setPause(bool pause)
{
    if (_pause==pause)
    {
        return;
    }

    _pause = pause;
    if (_pause)
    {
        _pauseTime = _latestTime;
    }
    else
    {
        _firstTime += (_latestTime-_pauseTime);
    }
}
<commit_msg>Improved the precision of animation paths.<commit_after>/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2003 Robert Osfield
 *
 * This library is open source and may be redistributed and/or modified under
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 *
 * 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
 * OpenSceneGraph Public License for more details.
*/
#include <osg/AnimationPath>
#include <osg/MatrixTransform>
#include <osg/PositionAttitudeTransform>

using namespace osg;

void AnimationPath::insert(double time,const ControlPoint& controlPoint)
{
    _timeControlPointMap[time] = controlPoint;
}

bool AnimationPath::getInterpolatedControlPoint(double time,ControlPoint& controlPoint) const
{
    if (_timeControlPointMap.empty()) return false;

    switch(_loopMode)
    {
        case(SWING):
        {
            double modulated_time = (time - getFirstTime())/(getPeriod()*2.0);
            double fraction_part = modulated_time - floor(modulated_time);
            if (fraction_part>0.5) fraction_part = 1.0-fraction_part;

            time = getFirstTime()+(fraction_part*2.0) * getPeriod();
            break;
        }
        case(LOOP):
        {
            double modulated_time = (time - getFirstTime())/getPeriod();
            double fraction_part = modulated_time - floor(modulated_time);
            time = getFirstTime()+fraction_part * getPeriod();
            break;
        }
        case(NO_LOOPING):
            // no need to modulate the time.
            break;
    }

    TimeControlPointMap::const_iterator second = _timeControlPointMap.lower_bound(time);
    if (second==_timeControlPointMap.begin())
    {
        controlPoint = second->second;
    }
    else if (second!=_timeControlPointMap.end())
    {
        TimeControlPointMap::const_iterator first = second;
        --first;

        // we have both a lower bound and the next item.

        // deta_time = second.time - first.time
        double delta_time = second->first - first->first;

        if (delta_time==0.0)
            controlPoint = first->second;
        else
        {
            controlPoint.interpolate((time - first->first)/delta_time,
                            first->second,
                            second->second);
        }
    }
    else // (second==_timeControlPointMap.end())
    {
        controlPoint = _timeControlPointMap.rbegin()->second;
    }
    return true;
}

void AnimationPath::read(std::istream& in)
{
    while (!in.eof())
    {
        double time;
        osg::Vec3d position;
        osg::Quat rotation;
        in >> time >> position.x() >> position.y() >> position.z() >> rotation.x() >> rotation.y() >> rotation.z() >> rotation.w();
        if(!in.eof())
            insert(time,osg::AnimationPath::ControlPoint(position,rotation));
    }
}

void AnimationPath::write(std::ostream& fout) const
{
    int prec = fout.precision();
    fout.precision(15);

    const TimeControlPointMap& tcpm = getTimeControlPointMap();
    for(TimeControlPointMap::const_iterator tcpmitr=tcpm.begin();
        tcpmitr!=tcpm.end();
        ++tcpmitr)
    {
        const ControlPoint& cp = tcpmitr->second;
        fout<<tcpmitr->first<<" "<<cp._position<<" "<<cp._rotation<<std::endl;
    }

    fout.precision(prec);
}

class AnimationPathCallbackVisitor : public NodeVisitor
{
    public:

        AnimationPathCallbackVisitor(const AnimationPath::ControlPoint& cp, const osg::Vec3d& pivotPoint, bool useInverseMatrix):
            _cp(cp),
            _pivotPoint(pivotPoint),
            _useInverseMatrix(useInverseMatrix) {}

        virtual void apply(MatrixTransform& mt)
        {
            Matrix matrix;
            if (_useInverseMatrix)
                _cp.getInverse(matrix);
            else
                _cp.getMatrix(matrix);

            mt.setMatrix(osg::Matrix::translate(-_pivotPoint)*matrix);
        }

        virtual void apply(PositionAttitudeTransform& pat)
        {
            if (_useInverseMatrix)
            {
                Matrix matrix;
                _cp.getInverse(matrix);
                pat.setPosition(matrix.getTrans());
                pat.setAttitude(_cp._rotation.inverse());
                pat.setScale(osg::Vec3(1.0f/_cp._scale.x(),1.0f/_cp._scale.y(),1.0f/_cp._scale.z()));
                pat.setPivotPoint(_pivotPoint);

            }
            else
            {
                pat.setPosition(_cp._position);
                pat.setAttitude(_cp._rotation);
                pat.setScale(_cp._scale);
                pat.setPivotPoint(_pivotPoint);
            }
        }

        AnimationPath::ControlPoint _cp;
        osg::Vec3d _pivotPoint;
        bool _useInverseMatrix;
};

void AnimationPathCallback::operator()(Node* node, NodeVisitor* nv)
{
    if (_animationPath.valid() &&
        nv->getVisitorType()==NodeVisitor::UPDATE_VISITOR &&
        nv->getFrameStamp())
    {
        double time = nv->getFrameStamp()->getReferenceTime();
        _latestTime = time;

        if (!_pause)
        {
            // Only update _firstTime the first time, when its value is still DBL_MAX
            if (_firstTime==DBL_MAX) _firstTime = time;
            update(*node);
        }
    }

    // must call any nested node callbacks and continue subgraph traversal.
    NodeCallback::traverse(node,nv);
}

double AnimationPathCallback::getAnimationTime() const
{
    return ((_latestTime-_firstTime)-_timeOffset)*_timeMultiplier;
}

void AnimationPathCallback::update(osg::Node& node)
{
    AnimationPath::ControlPoint cp;
    if (_animationPath->getInterpolatedControlPoint(getAnimationTime(),cp))
    {
        AnimationPathCallbackVisitor apcv(cp,_pivotPoint,_useInverseMatrix);
        node.accept(apcv);
    }
}

void AnimationPathCallback::reset()
{
    _firstTime = _latestTime;
    _pauseTime = _latestTime;
}

void AnimationPathCallback::setPause(bool pause)
{
    if (_pause==pause)
    {
        return;
    }

    _pause = pause;
    if (_pause)
    {
        _pauseTime = _latestTime;
    }
    else
    {
        _firstTime += (_latestTime-_pauseTime);
    }
}
<|endoftext|>


<commit_before>/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2003 Robert Osfield
 *
 * This library is open source and may be redistributed and/or modified under
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 *
 * 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
 * OpenSceneGraph Public License for more details.
*/
#include <osg/AutoTransform>
#include <osg/CullStack>

using namespace osg;

AutoTransform::AutoTransform():
    _autoUpdateEyeMovementTolerance(0.0f),
    _autoRotateToScreen(false),
    _autoScaleToScreen(false),
    _scale(1.0f,1.0f,1.0f),
    _firstTimeToInitEyePoint(true),
    _matrixDirty(true)
{
//    setNumChildrenRequiringUpdateTraversal(1);
}

AutoTransform::AutoTransform(const AutoTransform& pat,const CopyOp& copyop):
    Transform(pat,copyop),
    _position(pat._position),
    _pivotPoint(pat._pivotPoint),
    _rotation(pat._rotation),
    _scale(pat._scale)
{
//    setNumChildrenRequiringUpdateTraversal(getNumChildrenRequiringUpdateTraversal()+1);
}

bool AutoTransform::computeLocalToWorldMatrix(Matrix& matrix,NodeVisitor*) const
{
    if (_matrixDirty) computeMatrix();

    if (_referenceFrame==RELATIVE_TO_PARENTS)
    {
        matrix.preMult(_cachedMatrix);
    }
    else // absolute
    {
        matrix = _cachedMatrix;
    }
    return true;
}

bool AutoTransform::computeWorldToLocalMatrix(Matrix& matrix,NodeVisitor*) const
{
    if (_referenceFrame==RELATIVE_TO_PARENTS)
    {
        matrix.postMult(osg::Matrix::translate(-_position)*
                        osg::Matrix::rotate(_rotation.inverse())*
                        osg::Matrix::scale(1.0f/_scale.x(),1.0f/_scale.y(),1.0f/_scale.z())*
                        osg::Matrix::translate(_pivotPoint));
    }
    else // absolute
    {
        matrix = osg::Matrix::translate(-_position)*
                 osg::Matrix::rotate(_rotation.inverse())*
                 osg::Matrix::scale(1.0f/_scale.x(),1.0f/_scale.y(),1.0f/_scale.z())*
                 osg::Matrix::translate(_pivotPoint);
    }
    return true;
}

void AutoTransform::computeMatrix() const
{
    if (!_matrixDirty) return;

    _cachedMatrix.set(osg::Matrix::translate(-_pivotPoint)*
                      osg::Matrix::scale(_scale)*
                      osg::Matrix::rotate(_rotation)*
                      osg::Matrix::translate(_position));

    _matrixDirty = false;
}

void AutoTransform::accept(NodeVisitor& nv)
{
    // if app traversal update the frame count.
    if (nv.getVisitorType()==NodeVisitor::UPDATE_VISITOR)
    {
    }
    else
    if (nv.getVisitorType()==NodeVisitor::CULL_VISITOR)
    {

        CullStack* cs = dynamic_cast<CullStack*>(&nv);
        if (cs)
        {

            int width = _previousWidth;
            int height = _previousHeight;

            osg::Viewport* viewport = cs->getViewport();
            if (viewport)
            {
                width = viewport->width();
                height = viewport->height();
            }

            const osg::Vec3& eyePoint = cs->getEyeLocal();

            const osg::Matrix& projection = cs->getProjectionMatrix();

            bool doUpdate = _firstTimeToInitEyePoint;
            if (!_firstTimeToInitEyePoint)
            {
                osg::Vec3 dv = _previousEyePoint-eyePoint;
                if (dv.length2()>getAutoUpdateEyeMovementTolerance()*(eyePoint-getPosition()).length2())
                {
                    doUpdate = true;
                }
                else if (width!=_previousWidth || height!=_previousHeight)
                {
                    doUpdate = true;
                }
                else if (projection != _previousProjection)
                {
                    doUpdate = true;
                }
            }
            _firstTimeToInitEyePoint = false;

            if (doUpdate)
            {

                if (getAutoScaleToScreen())
                {
                    float size = 1.0f/cs->pixelSize(getPosition(),1.0f);
                    setScale(size);
                }

                if (getAutoRotateToScreen())
                {
                    osg::Quat rotation;
                    cs->getModelViewMatrix().get(rotation);
                    setRotation(rotation.inverse());
                }

                _previousEyePoint = eyePoint;
                _previousWidth = width;
                _previousHeight = height;
                _previousProjection = projection;

                _matrixDirty = true;
            }

        }
    }

    // now do the proper accept
    Transform::accept(nv);
}
<commit_msg>From Loic Dachary, added missing copy contructor operations.<commit_after>/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2003 Robert Osfield
 *
 * This library is open source and may be redistributed and/or modified under
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 *
 * 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
 * OpenSceneGraph Public License for more details.
*/
#include <osg/AutoTransform>
#include <osg/CullStack>

using namespace osg;

AutoTransform::AutoTransform():
    _autoUpdateEyeMovementTolerance(0.0f),
    _autoRotateToScreen(false),
    _autoScaleToScreen(false),
    _scale(1.0f,1.0f,1.0f),
    _firstTimeToInitEyePoint(true),
    _matrixDirty(true)
{
//    setNumChildrenRequiringUpdateTraversal(1);
}

AutoTransform::AutoTransform(const AutoTransform& pat,const CopyOp& copyop):
    Transform(pat,copyop),
    _position(pat._position),
    _pivotPoint(pat._pivotPoint),
    _autoUpdateEyeMovementTolerance(pat._autoUpdateEyeMovementTolerance),
    _autoRotateToScreen(pat._autoRotateToScreen),
    _autoScaleToScreen(pat._autoScaleToScreen),
    _rotation(pat._rotation),
    _scale(pat._scale),
    _firstTimeToInitEyePoint(true),
    _matrixDirty(true)
{
//    setNumChildrenRequiringUpdateTraversal(getNumChildrenRequiringUpdateTraversal()+1);
}

bool AutoTransform::computeLocalToWorldMatrix(Matrix& matrix,NodeVisitor*) const
{
    if (_matrixDirty) computeMatrix();

    if (_referenceFrame==RELATIVE_TO_PARENTS)
    {
        matrix.preMult(_cachedMatrix);
    }
    else // absolute
    {
        matrix = _cachedMatrix;
    }
    return true;
}

bool AutoTransform::computeWorldToLocalMatrix(Matrix& matrix,NodeVisitor*) const
{
    if (_referenceFrame==RELATIVE_TO_PARENTS)
    {
        matrix.postMult(osg::Matrix::translate(-_position)*
                        osg::Matrix::rotate(_rotation.inverse())*
                        osg::Matrix::scale(1.0f/_scale.x(),1.0f/_scale.y(),1.0f/_scale.z())*
                        osg::Matrix::translate(_pivotPoint));
    }
    else // absolute
    {
        matrix = osg::Matrix::translate(-_position)*
                 osg::Matrix::rotate(_rotation.inverse())*
                 osg::Matrix::scale(1.0f/_scale.x(),1.0f/_scale.y(),1.0f/_scale.z())*
                 osg::Matrix::translate(_pivotPoint);
    }
    return true;
}

void AutoTransform::computeMatrix() const
{
    if (!_matrixDirty) return;

    _cachedMatrix.set(osg::Matrix::translate(-_pivotPoint)*
                      osg::Matrix::scale(_scale)*
                      osg::Matrix::rotate(_rotation)*
                      osg::Matrix::translate(_position));

    _matrixDirty = false;
}

void AutoTransform::accept(NodeVisitor& nv)
{
    // if app traversal update the frame count.
    if (nv.getVisitorType()==NodeVisitor::UPDATE_VISITOR)
    {
    }
    else
    if (nv.getVisitorType()==NodeVisitor::CULL_VISITOR)
    {

        CullStack* cs = dynamic_cast<CullStack*>(&nv);
        if (cs)
        {

            int width = _previousWidth;
            int height = _previousHeight;

            osg::Viewport* viewport = cs->getViewport();
            if (viewport)
            {
                width = viewport->width();
                height = viewport->height();
            }

            const osg::Vec3& eyePoint = cs->getEyeLocal();

            const osg::Matrix& projection = cs->getProjectionMatrix();

            bool doUpdate = _firstTimeToInitEyePoint;
            if (!_firstTimeToInitEyePoint)
            {
                osg::Vec3 dv = _previousEyePoint-eyePoint;
                if (dv.length2()>getAutoUpdateEyeMovementTolerance()*(eyePoint-getPosition()).length2())
                {
                    doUpdate = true;
                }
                else if (width!=_previousWidth || height!=_previousHeight)
                {
                    doUpdate = true;
                }
                else if (projection != _previousProjection)
                {
                    doUpdate = true;
                }
            }
            _firstTimeToInitEyePoint = false;

            if (doUpdate)
            {

                if (getAutoScaleToScreen())
                {
                    float size = 1.0f/cs->pixelSize(getPosition(),1.0f);
                    setScale(size);
                }

                if (getAutoRotateToScreen())
                {
                    osg::Quat rotation;
                    cs->getModelViewMatrix().get(rotation);
                    setRotation(rotation.inverse());
                }

                _previousEyePoint = eyePoint;
                _previousWidth = width;
                _previousHeight = height;
                _previousProjection = projection;

                _matrixDirty = true;
            }

        }
    }

    // now do the proper accept
    Transform::accept(nv);
}
<|endoftext|>


<commit_before>/* * This file is part of meego-im-framework *
 *
 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
 * All rights reserved.
 * Contact: Nokia Corporation (directui@nokia.com)
 *
 * If you have questions regarding the use of this file, please contact
 * Nokia at directui@nokia.com.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1 as published by the Free Software Foundation
 * and appearing in the file LICENSE.LGPL included in the packaging
 * of this file.
 */

#include "mimapplication.h"

#include <MWindow>
#include <MDebug>

#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h> // defines IconicState

MIMApplication::MIMApplication(int &argc, char **argv)
    : MApplication(argc, argv),
      remoteWinId(0),
      passThruWindow(0)
{}

MIMApplication::~MIMApplication()
{}

bool MIMApplication::x11EventFilter(XEvent *ev)
{
    handleMapNotifyEvents(ev);
    handleTransientEvents(ev);
    return MApplication::x11EventFilter(ev);
}

void MIMApplication::handleMapNotifyEvents(XEvent *ev)
{
    if (wasPassThruWindowMapped(ev)) {
        mDebug("MIMApplication") << "PassThru window was mapped.";
        emit passThruWindowMapped();
    } else if (wasPassThruWindowUnmapped(ev)) {
        mDebug("MIMApplication") << "PassThru window was unmapped.";
        emit passThruWindowUnmapped();
    }
}

void MIMApplication::handleTransientEvents(XEvent *ev)
{
    if (0 == remoteWinId || not passThruWindow) {
        return;
    }

    if (wasRemoteWindowIconified(ev) || wasRemoteWindowUnmapped(ev)) {
        mDebug("MIMApplication") << "Remote window was destroyed or iconified - hiding.";
        remoteWinId = 0;
        emit remoteWindowGone();
    }
}

void MIMApplication::setTransientHint(int newRemoteWinId)
{
    if (0 == newRemoteWinId || not activeWindow()) {
        return;
    }

    remoteWinId = newRemoteWinId;

    XSetTransientForHint(QX11Info::display(),
                         passThruWindow->effectiveWinId(),
                         remoteWinId);

    // Using PropertyChangeMask is a work-around for NB#172722 (a WONTFIX):
    XSelectInput(QX11Info::display(),
                 remoteWinId,
                 StructureNotifyMask | PropertyChangeMask);
}

void MIMApplication::setPassThruWindow(QWidget *newPassThruWindow)
{
    if (newPassThruWindow != passThruWindow) {
        passThruWindow = newPassThruWindow;
    }
}

MIMApplication *MIMApplication::instance()
{
    return static_cast<MIMApplication *>(QCoreApplication::instance());
}

bool MIMApplication::wasRemoteWindowIconified(XEvent *ev) const
{
    if (PropertyNotify != ev->type) {
        return false;
    }

    static const Atom wmState = XInternAtom(QX11Info::display(), "WM_STATE", false);

    if (ev->xproperty.atom == wmState) {
        Atom type;
        int format;
        unsigned long length;
        unsigned long after;
        unsigned long *state;
        uchar *data = 0;

        int queryResult = XGetWindowProperty(QX11Info::display(), remoteWinId, wmState, 0, 2,
                                             false, AnyPropertyType, &type, &format, &length,
                                             &after, &data);
        state = (unsigned long *) data;

        if (queryResult == Success && data && format == 32 && *state == IconicState) {
            XFree((char *)data);
            return true;
        }
    }

    return false;
}

bool MIMApplication::wasRemoteWindowUnmapped(XEvent *ev) const
{
    return (UnmapNotify == ev->type &&
            static_cast<int>(ev->xunmap.event) == remoteWinId);
}

bool MIMApplication::wasPassThruWindowMapped(XEvent *ev) const
{
    return (passThruWindow &&
            MapNotify == ev->type &&
            static_cast<WId>(ev->xmap.event) == passThruWindow->effectiveWinId());
}

bool MIMApplication::wasPassThruWindowUnmapped(XEvent *ev) const
{
    return (passThruWindow &&
            UnmapNotify == ev->type &&
            static_cast<WId>(ev->xunmap.event) == passThruWindow->effectiveWinId());
}
<commit_msg>Fixes: memleak after XGetWindowProperty call (NB#224686)<commit_after>/* * This file is part of meego-im-framework *
 *
 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
 * All rights reserved.
 * Contact: Nokia Corporation (directui@nokia.com)
 *
 * If you have questions regarding the use of this file, please contact
 * Nokia at directui@nokia.com.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1 as published by the Free Software Foundation
 * and appearing in the file LICENSE.LGPL included in the packaging
 * of this file.
 */

#include "mimapplication.h"

#include <MWindow>
#include <MDebug>

#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h> // defines IconicState

MIMApplication::MIMApplication(int &argc, char **argv)
    : MApplication(argc, argv),
      remoteWinId(0),
      passThruWindow(0)
{}

MIMApplication::~MIMApplication()
{}

bool MIMApplication::x11EventFilter(XEvent *ev)
{
    handleMapNotifyEvents(ev);
    handleTransientEvents(ev);
    return MApplication::x11EventFilter(ev);
}

void MIMApplication::handleMapNotifyEvents(XEvent *ev)
{
    if (wasPassThruWindowMapped(ev)) {
        mDebug("MIMApplication") << "PassThru window was mapped.";
        emit passThruWindowMapped();
    } else if (wasPassThruWindowUnmapped(ev)) {
        mDebug("MIMApplication") << "PassThru window was unmapped.";
        emit passThruWindowUnmapped();
    }
}

void MIMApplication::handleTransientEvents(XEvent *ev)
{
    if (0 == remoteWinId || not passThruWindow) {
        return;
    }

    if (wasRemoteWindowIconified(ev) || wasRemoteWindowUnmapped(ev)) {
        mDebug("MIMApplication") << "Remote window was destroyed or iconified - hiding.";
        remoteWinId = 0;
        emit remoteWindowGone();
    }
}

void MIMApplication::setTransientHint(int newRemoteWinId)
{
    if (0 == newRemoteWinId || not activeWindow()) {
        return;
    }

    remoteWinId = newRemoteWinId;

    XSetTransientForHint(QX11Info::display(),
                         passThruWindow->effectiveWinId(),
                         remoteWinId);

    // Using PropertyChangeMask is a work-around for NB#172722 (a WONTFIX):
    XSelectInput(QX11Info::display(),
                 remoteWinId,
                 StructureNotifyMask | PropertyChangeMask);
}

void MIMApplication::setPassThruWindow(QWidget *newPassThruWindow)
{
    if (newPassThruWindow != passThruWindow) {
        passThruWindow = newPassThruWindow;
    }
}

MIMApplication *MIMApplication::instance()
{
    return static_cast<MIMApplication *>(QCoreApplication::instance());
}

bool MIMApplication::wasRemoteWindowIconified(XEvent *ev) const
{
    if (PropertyNotify != ev->type) {
        return false;
    }

    static const Atom wmState = XInternAtom(QX11Info::display(), "WM_STATE", false);

    if (ev->xproperty.atom == wmState) {
        Atom type;
        int format;
        unsigned long length;
        unsigned long after;
        unsigned long *state;
        uchar *data = 0;

        int queryResult = XGetWindowProperty(QX11Info::display(), remoteWinId, wmState, 0, 2,
                                             false, AnyPropertyType, &type, &format, &length,
                                             &after, &data);
        state = (unsigned long *) data;

        bool result = (queryResult == Success && data && format == 32 && *state == IconicState);

        if (data) {
            XFree(data);
        }

        return result;
    }

    return false;
}

bool MIMApplication::wasRemoteWindowUnmapped(XEvent *ev) const
{
    return (UnmapNotify == ev->type &&
            static_cast<int>(ev->xunmap.event) == remoteWinId);
}

bool MIMApplication::wasPassThruWindowMapped(XEvent *ev) const
{
    return (passThruWindow &&
            MapNotify == ev->type &&
            static_cast<WId>(ev->xmap.event) == passThruWindow->effectiveWinId());
}

bool MIMApplication::wasPassThruWindowUnmapped(XEvent *ev) const
{
    return (passThruWindow &&
            UnmapNotify == ev->type &&
            static_cast<WId>(ev->xunmap.event) == passThruWindow->effectiveWinId());
}
<|endoftext|>