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

// mapnik
#include <mapnik/line_symbolizer.hpp>
#include <mapnik/line_pattern_symbolizer.hpp>
#include <mapnik/polygon_symbolizer.hpp>
#include <mapnik/polygon_pattern_symbolizer.hpp>
#include <mapnik/point_symbolizer.hpp>
#include <mapnik/raster_symbolizer.hpp>
#include <mapnik/shield_symbolizer.hpp>
#include <mapnik/text_symbolizer.hpp>
#include <mapnik/markers_symbolizer.hpp>
#include <mapnik/glyph_symbolizer.hpp>
#include <mapnik/feature.hpp>
#include <mapnik/filter_factory.hpp>
#include <mapnik/expression_string.hpp>

// boost
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/variant.hpp>

// stl
#include <string>
#include <vector>

namespace mapnik
{
inline bool operator==(point_symbolizer const& lhs,
                       point_symbolizer const& rhs)
{
    return (&lhs == &rhs);
}
inline bool operator==(line_symbolizer const& lhs,
                       line_symbolizer const& rhs)
{
    return (&lhs == &rhs);
}
inline bool operator==(line_pattern_symbolizer const& lhs,
                       line_pattern_symbolizer const& rhs)
{
    return (&lhs == &rhs);
}

inline bool operator==(polygon_symbolizer const& lhs,
                       polygon_symbolizer const& rhs)
{
    return (&lhs == &rhs);
}

inline bool operator==(polygon_pattern_symbolizer const& lhs,
                       polygon_pattern_symbolizer const& rhs)
{
    return (&lhs == &rhs);
}

inline bool operator==(raster_symbolizer const& lhs,
                       raster_symbolizer const& rhs)
{
    return (&lhs == &rhs);
}

inline bool operator==(text_symbolizer const& lhs,
                       text_symbolizer const& rhs)
{
    return (&lhs == &rhs);
}

inline bool operator==(shield_symbolizer const& lhs,
                       shield_symbolizer const& rhs)
{
    return (&lhs == &rhs);
}

inline bool operator==(building_symbolizer const& lhs,
                       building_symbolizer const& rhs)
{
    return (&lhs == &rhs);
}

inline bool operator==(markers_symbolizer const& lhs,
                       markers_symbolizer const& rhs)
{
    return (&lhs == &rhs);
}

inline bool operator==(glyph_symbolizer const& lhs,
                       glyph_symbolizer const& rhs)
{
    return (&lhs == &rhs);
}
typedef boost::variant<point_symbolizer,
                       line_symbolizer,
                       line_pattern_symbolizer,
                       polygon_symbolizer,
                       polygon_pattern_symbolizer,
                       raster_symbolizer,
                       shield_symbolizer,
                       text_symbolizer,
                       building_symbolizer,
                       markers_symbolizer,
                       glyph_symbolizer> symbolizer;

class rule
{
public:
    typedef std::vector<symbolizer> symbolizers;
private:

    std::string name_;
    std::string title_;
    std::string abstract_;
    double min_scale_;
    double max_scale_;
    symbolizers syms_;
    expression_ptr filter_;
    bool else_filter_;
    bool also_filter_;
public:
    rule()
        : name_(),
          title_(),
          abstract_(),
          min_scale_(0),
          max_scale_(std::numeric_limits<double>::infinity()),
          syms_(),
          filter_(boost::make_shared<mapnik::expr_node>(true)),
          else_filter_(false),
          also_filter_(false) {}

    rule(const std::string& name,
         const std::string& title="",
         double min_scale_denominator=0,
         double max_scale_denominator=std::numeric_limits<double>::infinity())
        : name_(name),
          title_(title),
          min_scale_(min_scale_denominator),
          max_scale_(max_scale_denominator),
          syms_(),
          filter_(boost::make_shared<mapnik::expr_node>(true)),
          else_filter_(false),
          also_filter_(false)  {}

    rule(const rule& rhs, bool deep_copy = false)
        : name_(rhs.name_),
          title_(rhs.title_),
          abstract_(rhs.abstract_),
          min_scale_(rhs.min_scale_),
          max_scale_(rhs.max_scale_),
          syms_(rhs.syms_),
          filter_(rhs.filter_),
          else_filter_(rhs.else_filter_),
          also_filter_(rhs.also_filter_)
    {
        if (deep_copy) {
            //std::string expr = to_expression_string(rhs.filter_);
            //filter_ = parse_expression(expr,"utf8");

            symbolizers::iterator it  = syms_.begin(),
                                  end = syms_.end();

            // FIXME - metawriter_ptr?

            for(; it != end; ++it) {

                /*if (polygon_symbolizer *sym = boost::get<polygon_symbolizer>(&(*it))) {
                    // no shared pointers
                } else if (line_symbolizer *sym = boost::get<line_symbolizer>(&(*it))) {
                    // no shared pointers
                } else if (building_symbolizer *sym = boost::get<building_symbolizer>(&(*it))) {
                    // no shared pointers
                }*/

                if (markers_symbolizer *sym = boost::get<markers_symbolizer>(&(*it))) {
                    copy_path_ptr(sym);
                } else if (point_symbolizer *sym = boost::get<point_symbolizer>(&(*it))) {
                    copy_path_ptr(sym);
                } else if (polygon_pattern_symbolizer *sym = boost::get<polygon_pattern_symbolizer>(&(*it))) {
                    copy_path_ptr(sym);
                } else if (line_pattern_symbolizer *sym = boost::get<line_pattern_symbolizer>(&(*it))) {
                    copy_path_ptr(sym);
                } else if (raster_symbolizer *sym = boost::get<raster_symbolizer>(&(*it))) {
                    raster_colorizer_ptr old_colorizer = sym->get_colorizer(),
                                         new_colorizer = raster_colorizer_ptr();

                    new_colorizer->set_stops( old_colorizer->get_stops());
                    new_colorizer->set_default_mode( old_colorizer->get_default_mode() );
                    new_colorizer->set_default_color( old_colorizer->get_default_color() );
                    new_colorizer->set_epsilon( old_colorizer->get_epsilon() );

                    sym->set_colorizer(new_colorizer);
                } else if (shield_symbolizer *sym = boost::get<shield_symbolizer>(&(*it))) {
                    copy_path_ptr(sym);
                    copy_text_ptr(sym);
                } else if (text_symbolizer *sym = boost::get<text_symbolizer>(&(*it))) {
                    copy_text_ptr(sym);
                }
            }
        }
    }

    rule& operator=(rule const& rhs)
    {
        rule tmp(rhs);
        swap(tmp);
        return *this;
    }
    bool operator==(rule const& other)
    {
        return  (this == &other);
    }

    void set_max_scale(double scale)
    {
        max_scale_=scale;
    }

    double get_max_scale() const
    {
        return max_scale_;
    }

    void set_min_scale(double scale)
    {
        min_scale_=scale;
    }

    double get_min_scale() const
    {
        return min_scale_;
    }

    void set_name(std::string const& name)
    {
        name_=name;
    }

    std::string const& get_name() const
    {
        return name_;
    }

    std::string const& get_title() const
    {
        return  title_;
    }

    void set_title(std::string const& title)
    {
        title_=title;
    }

    void set_abstract(std::string const& abstract)
    {
        abstract_=abstract;
    }

    std::string const& get_abstract() const
    {
        return abstract_;
    }

    void append(const symbolizer& sym)
    {
        syms_.push_back(sym);
    }

    void remove_at(size_t index)
    {
        if (index < syms_.size())
        {
            syms_.erase(syms_.begin()+index);
        }
    }

    const symbolizers& get_symbolizers() const
    {
        return syms_;
    }

    symbolizers::const_iterator begin() const
    {
        return syms_.begin();
    }

    symbolizers::const_iterator end() const
    {
        return syms_.end();
    }

    symbolizers::iterator begin()
    {
        return syms_.begin();
    }

    symbolizers::iterator end()
    {
        return syms_.end();
    }

    void set_filter(const expression_ptr& filter)
    {
        filter_=filter;
    }

    expression_ptr const& get_filter() const
    {
        return filter_;
    }

    void set_else(bool else_filter)
    {
        else_filter_=else_filter;
    }

    bool has_else_filter() const
    {
        return else_filter_;
    }

    void set_also(bool also_filter)
    {
        also_filter_=also_filter;
    }

    bool has_also_filter() const
    {
        return also_filter_;
    }

    bool active(double scale) const
    {
        return ( scale >= min_scale_ - 1e-6 && scale < max_scale_ + 1e-6);
    }

private:

    void swap(rule& rhs) throw()
    {
        name_=rhs.name_;
        title_=rhs.title_;
        abstract_=rhs.abstract_;
        min_scale_=rhs.min_scale_;
        max_scale_=rhs.max_scale_;
        syms_=rhs.syms_;
        filter_=rhs.filter_;
        else_filter_=rhs.else_filter_;
        also_filter_=rhs.also_filter_;
    }

    template <class T>
    void copy_path_ptr(T* sym)
    {
        std::string path = path_processor_type::to_string(*sym->get_filename());
        sym->set_filename( parse_path(path) );
    }

    template <class T>
    void copy_text_ptr(T* sym)
    {
        std::string name = to_expression_string(*sym->get_name());
        sym->set_name( parse_expression(name) );

        // FIXME - orientation doesn't appear to be initialized in constructor?
        //std::string orientation = to_expression_string(*sym->get_orientation());
        //sym->set_orientation( parse_expression(orientation) );

        unsigned text_size = sym->get_text_size();
        position displace = sym->get_displacement();
        vertical_alignment_e valign = sym->get_vertical_alignment();
        horizontal_alignment_e halign = sym->get_horizontal_alignment();
        justify_alignment_e jalign = sym->get_justify_alignment();

        text_placements_ptr placements = text_placements_ptr(boost::make_shared<text_placements_dummy>());
        sym->set_placement_options( placements );

        sym->set_text_size(text_size);
        sym->set_displacement(displace);
        sym->set_vertical_alignment(valign);
        sym->set_horizontal_alignment(halign);
        sym->set_justify_alignment(jalign);
    }
};

}

#endif // MAPNIK_RULE_HPP
<commit_msg>keep proper type in text sym deep copy - refs #946<commit_after>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2011 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_RULE_HPP
#define MAPNIK_RULE_HPP

// mapnik
#include <mapnik/line_symbolizer.hpp>
#include <mapnik/line_pattern_symbolizer.hpp>
#include <mapnik/polygon_symbolizer.hpp>
#include <mapnik/polygon_pattern_symbolizer.hpp>
#include <mapnik/point_symbolizer.hpp>
#include <mapnik/raster_symbolizer.hpp>
#include <mapnik/shield_symbolizer.hpp>
#include <mapnik/text_symbolizer.hpp>
#include <mapnik/markers_symbolizer.hpp>
#include <mapnik/glyph_symbolizer.hpp>
#include <mapnik/feature.hpp>
#include <mapnik/filter_factory.hpp>
#include <mapnik/expression_string.hpp>

// boost
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/variant.hpp>

// stl
#include <string>
#include <vector>

namespace mapnik
{
inline bool operator==(point_symbolizer const& lhs,
                       point_symbolizer const& rhs)
{
    return (&lhs == &rhs);
}
inline bool operator==(line_symbolizer const& lhs,
                       line_symbolizer const& rhs)
{
    return (&lhs == &rhs);
}
inline bool operator==(line_pattern_symbolizer const& lhs,
                       line_pattern_symbolizer const& rhs)
{
    return (&lhs == &rhs);
}

inline bool operator==(polygon_symbolizer const& lhs,
                       polygon_symbolizer const& rhs)
{
    return (&lhs == &rhs);
}

inline bool operator==(polygon_pattern_symbolizer const& lhs,
                       polygon_pattern_symbolizer const& rhs)
{
    return (&lhs == &rhs);
}

inline bool operator==(raster_symbolizer const& lhs,
                       raster_symbolizer const& rhs)
{
    return (&lhs == &rhs);
}

inline bool operator==(text_symbolizer const& lhs,
                       text_symbolizer const& rhs)
{
    return (&lhs == &rhs);
}

inline bool operator==(shield_symbolizer const& lhs,
                       shield_symbolizer const& rhs)
{
    return (&lhs == &rhs);
}

inline bool operator==(building_symbolizer const& lhs,
                       building_symbolizer const& rhs)
{
    return (&lhs == &rhs);
}

inline bool operator==(markers_symbolizer const& lhs,
                       markers_symbolizer const& rhs)
{
    return (&lhs == &rhs);
}

inline bool operator==(glyph_symbolizer const& lhs,
                       glyph_symbolizer const& rhs)
{
    return (&lhs == &rhs);
}
typedef boost::variant<point_symbolizer,
                       line_symbolizer,
                       line_pattern_symbolizer,
                       polygon_symbolizer,
                       polygon_pattern_symbolizer,
                       raster_symbolizer,
                       shield_symbolizer,
                       text_symbolizer,
                       building_symbolizer,
                       markers_symbolizer,
                       glyph_symbolizer> symbolizer;

class rule
{
public:
    typedef std::vector<symbolizer> symbolizers;
private:

    std::string name_;
    std::string title_;
    std::string abstract_;
    double min_scale_;
    double max_scale_;
    symbolizers syms_;
    expression_ptr filter_;
    bool else_filter_;
    bool also_filter_;
public:
    rule()
        : name_(),
          title_(),
          abstract_(),
          min_scale_(0),
          max_scale_(std::numeric_limits<double>::infinity()),
          syms_(),
          filter_(boost::make_shared<mapnik::expr_node>(true)),
          else_filter_(false),
          also_filter_(false) {}

    rule(const std::string& name,
         const std::string& title="",
         double min_scale_denominator=0,
         double max_scale_denominator=std::numeric_limits<double>::infinity())
        : name_(name),
          title_(title),
          min_scale_(min_scale_denominator),
          max_scale_(max_scale_denominator),
          syms_(),
          filter_(boost::make_shared<mapnik::expr_node>(true)),
          else_filter_(false),
          also_filter_(false)  {}

    rule(const rule& rhs, bool deep_copy = false)
        : name_(rhs.name_),
          title_(rhs.title_),
          abstract_(rhs.abstract_),
          min_scale_(rhs.min_scale_),
          max_scale_(rhs.max_scale_),
          syms_(rhs.syms_),
          filter_(rhs.filter_),
          else_filter_(rhs.else_filter_),
          also_filter_(rhs.also_filter_)
    {
        if (deep_copy) {
            //std::string expr = to_expression_string(rhs.filter_);
            //filter_ = parse_expression(expr,"utf8");

            symbolizers::iterator it  = syms_.begin(),
                                  end = syms_.end();

            // FIXME - metawriter_ptr?

            for(; it != end; ++it) {

                /*if (polygon_symbolizer *sym = boost::get<polygon_symbolizer>(&(*it))) {
                    // no shared pointers
                } else if (line_symbolizer *sym = boost::get<line_symbolizer>(&(*it))) {
                    // no shared pointers
                } else if (building_symbolizer *sym = boost::get<building_symbolizer>(&(*it))) {
                    // no shared pointers
                }*/

                if (markers_symbolizer *sym = boost::get<markers_symbolizer>(&(*it))) {
                    copy_path_ptr(sym);
                } else if (point_symbolizer *sym = boost::get<point_symbolizer>(&(*it))) {
                    copy_path_ptr(sym);
                } else if (polygon_pattern_symbolizer *sym = boost::get<polygon_pattern_symbolizer>(&(*it))) {
                    copy_path_ptr(sym);
                } else if (line_pattern_symbolizer *sym = boost::get<line_pattern_symbolizer>(&(*it))) {
                    copy_path_ptr(sym);
                } else if (raster_symbolizer *sym = boost::get<raster_symbolizer>(&(*it))) {
                    raster_colorizer_ptr old_colorizer = sym->get_colorizer(),
                                         new_colorizer = raster_colorizer_ptr();

                    new_colorizer->set_stops( old_colorizer->get_stops());
                    new_colorizer->set_default_mode( old_colorizer->get_default_mode() );
                    new_colorizer->set_default_color( old_colorizer->get_default_color() );
                    new_colorizer->set_epsilon( old_colorizer->get_epsilon() );

                    sym->set_colorizer(new_colorizer);
                } else if (shield_symbolizer *sym = boost::get<shield_symbolizer>(&(*it))) {
                    copy_path_ptr(sym);
                    copy_text_ptr(sym);
                } else if (text_symbolizer *sym = boost::get<text_symbolizer>(&(*it))) {
                    copy_text_ptr(sym);
                }
            }
        }
    }

    rule& operator=(rule const& rhs)
    {
        rule tmp(rhs);
        swap(tmp);
        return *this;
    }
    bool operator==(rule const& other)
    {
        return  (this == &other);
    }

    void set_max_scale(double scale)
    {
        max_scale_=scale;
    }

    double get_max_scale() const
    {
        return max_scale_;
    }

    void set_min_scale(double scale)
    {
        min_scale_=scale;
    }

    double get_min_scale() const
    {
        return min_scale_;
    }

    void set_name(std::string const& name)
    {
        name_=name;
    }

    std::string const& get_name() const
    {
        return name_;
    }

    std::string const& get_title() const
    {
        return  title_;
    }

    void set_title(std::string const& title)
    {
        title_=title;
    }

    void set_abstract(std::string const& abstract)
    {
        abstract_=abstract;
    }

    std::string const& get_abstract() const
    {
        return abstract_;
    }

    void append(const symbolizer& sym)
    {
        syms_.push_back(sym);
    }

    void remove_at(size_t index)
    {
        if (index < syms_.size())
        {
            syms_.erase(syms_.begin()+index);
        }
    }

    const symbolizers& get_symbolizers() const
    {
        return syms_;
    }

    symbolizers::const_iterator begin() const
    {
        return syms_.begin();
    }

    symbolizers::const_iterator end() const
    {
        return syms_.end();
    }

    symbolizers::iterator begin()
    {
        return syms_.begin();
    }

    symbolizers::iterator end()
    {
        return syms_.end();
    }

    void set_filter(const expression_ptr& filter)
    {
        filter_=filter;
    }

    expression_ptr const& get_filter() const
    {
        return filter_;
    }

    void set_else(bool else_filter)
    {
        else_filter_=else_filter;
    }

    bool has_else_filter() const
    {
        return else_filter_;
    }

    void set_also(bool also_filter)
    {
        also_filter_=also_filter;
    }

    bool has_also_filter() const
    {
        return also_filter_;
    }

    bool active(double scale) const
    {
        return ( scale >= min_scale_ - 1e-6 && scale < max_scale_ + 1e-6);
    }

private:

    void swap(rule& rhs) throw()
    {
        name_=rhs.name_;
        title_=rhs.title_;
        abstract_=rhs.abstract_;
        min_scale_=rhs.min_scale_;
        max_scale_=rhs.max_scale_;
        syms_=rhs.syms_;
        filter_=rhs.filter_;
        else_filter_=rhs.else_filter_;
        also_filter_=rhs.also_filter_;
    }

    template <class T>
    void copy_path_ptr(T* sym)
    {
        std::string path = path_processor_type::to_string(*sym->get_filename());
        sym->set_filename( parse_path(path) );
    }

    template <class T>
    void copy_text_ptr(T* sym)
    {
        std::string name = to_expression_string(*sym->get_name());
        sym->set_name( parse_expression(name) );

        // FIXME - orientation doesn't appear to be initialized in constructor?
        //std::string orientation = to_expression_string(*sym->get_orientation());
        //sym->set_orientation( parse_expression(orientation) );

        float text_size = sym->get_text_size();
        position displace = sym->get_displacement();
        vertical_alignment_e valign = sym->get_vertical_alignment();
        horizontal_alignment_e halign = sym->get_horizontal_alignment();
        justify_alignment_e jalign = sym->get_justify_alignment();

        text_placements_ptr placements = text_placements_ptr(boost::make_shared<text_placements_dummy>());
        sym->set_placement_options( placements );

        sym->set_text_size(text_size);
        sym->set_displacement(displace);
        sym->set_vertical_alignment(valign);
        sym->set_horizontal_alignment(halign);
        sym->set_justify_alignment(jalign);
    }
};

}

#endif // MAPNIK_RULE_HPP
<|endoftext|>


<commit_before>/* -------------------------------------------------------------------------- *
 *                         OpenSim:  testContext.cpp                          *
 * -------------------------------------------------------------------------- *
 * The OpenSim API is a toolkit for musculoskeletal modeling and simulation.  *
 * See http://opensim.stanford.edu and the NOTICE file for more information.  *
 * OpenSim is developed at Stanford University and supported by the US        *
 * National Institutes of Health (U54 GM072970, R24 HD065690) and by DARPA    *
 * through the Warrior Web program.                                           *
 *                                                                            *
 * Copyright (c) 2005-2017 Stanford University and the Authors                *
 * Author(s): Ayman Habib                                                     *
 *                                                                            *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may    *
 * not use this file except in compliance with the License. You may obtain a  *
 * copy of the License at http://www.apache.org/licenses/LICENSE-2.0.         *
 *                                                                            *
 * Unless required by applicable law or agreed to in writing, software        *
 * distributed under the License is distributed on an "AS IS" BASIS,          *
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   *
 * See the License for the specific language governing permissions and        *
 * limitations under the License.                                             *
 * -------------------------------------------------------------------------- */
#include "../OpenSimContext.h"
#include <OpenSim/Common/Function.h>
#include <OpenSim/Common/MarkerData.h>
#include <OpenSim/Simulation/SimbodyEngine/Body.h>
#include <OpenSim/Simulation/SimbodyEngine/Coordinate.h>
#include <OpenSim/Simulation/Model/Marker.h>
#include <OpenSim/Simulation/Model/Model.h>
#include <OpenSim/Simulation/Model/BodySet.h>
#include <OpenSim/Simulation/Model/MovingPathPoint.h>
#include <OpenSim/Simulation/Model/Muscle.h>
#include <OpenSim/Simulation/Model/PathPoint.h>
#include <OpenSim/Simulation/Wrap/PathWrap.h>
#include <OpenSim/Simulation/Model/ConditionalPathPoint.h>
#include <OpenSim/Simulation/SimbodyEngine/SimbodyEngine.h>
#include <OpenSim/Simulation/Wrap/WrapObject.h>
#include <OpenSim/Simulation/Model/Analysis.h>
#include <OpenSim/Simulation/Model/MarkerSet.h>
#include <OpenSim/Tools/AnalyzeTool.h>
#include <OpenSim/Tools/ModelScaler.h>
#include <OpenSim/Tools/Measurement.h>
#include <OpenSim/Tools/MarkerPlacer.h>
#include <OpenSim/Simulation/Model/ForceSet.h>
#include <OpenSim/Common/LoadOpenSimLibrary.h>
#include <OpenSim/Actuators/Thelen2003Muscle.h>

using namespace OpenSim;
using namespace SimTK;
using namespace std;

#define ASSERT_EQUAL(expected, found, tolerance) { \
double tol = std::max((tolerance), std::abs((expected)*(tolerance))); \
if ((found)<(expected)-(tol) || (found)>(expected)+(tol)) throw(exception());}

int main()
{
  try {
    int  status = 0;

    // To Retrace the steps taken by the GUI this test case follows the same call sequence:
    // new Model(file)
    // new OpenSimContext(model.initSystem(), model);
    // context.updateDisplayer(muscle)  // to display muscles
    // context.getCurrentPath(muscle)
    // context.getTransform(body)
    // context.transformPosition(body, loc, global)  // to display markers
    // context.getLocked(Coordinate)
    // context.getValue(cooridnate)
    LoadOpenSimLibrary("osimActuators");
    LoadOpenSimLibrary("osimSimulation");
    LoadOpenSimLibrary("osimJavaJNI");

    Model *model = new Model("wrist.osim");
    OpenSimContext* context = new OpenSimContext(&model->initSystem(), model);
    const ForceSet& fs = model->getForceSet();
    int n1 = fs.getNumGroups();
    const ObjectGroup* grp = fs.getGroup("wrist");
    assert(grp);
    const Array<const Object*>& members = grp->getMembers();
    int sz = members.getSize();
    ASSERT_EQUAL(sz,5,0);
    assert(members.get(0)->getName()=="ECRB");
    delete model;
    delete context;
    model = new Model("arm26_20.osim");
    context = new OpenSimContext(&model->initSystem(), model);
    // Make a copy of state contained in context ad make sure content match
    SimTK::State stateCopy = context->getCurrentStateCopy();
    assert(context->getCurrentStateRef().toString()==stateCopy.toString());

    Array<std::string> stateNames = model->getStateVariableNames();
    OpenSim::Force* dForce=&(model->updForceSet().get("TRIlong"));
    Muscle* dTRIlong = dynamic_cast<Muscle*>(dForce);
    assert(dTRIlong);
    context->setPropertiesFromState();
    OpenSim::Thelen2003Muscle* thelenMsl = dynamic_cast<Thelen2003Muscle*>(dTRIlong);
    AbstractProperty& dProp = thelenMsl->updPropertyByName("ignore_tendon_compliance");

    PropertyHelper::setValueBool(true, dProp);
    cout << "Prop after is " << dProp.toString() << endl;

    bool exceptionThrown = false;
    try{// adding to the system should cause Muscle that do not handle
        // ignore_tendon_compliance to throw an exception
        context->recreateSystemKeepStage();
    }
    catch (const std::exception& e) {
        cout << e.what() << endl;
        exceptionThrown = true;
        PropertyHelper::setValueBool(false, dProp);
        cout << "Prop reset to " << dProp.toString() << endl;
        // recreate the system so test can continue
        context->recreateSystemKeepStage();
    }

    SimTK_ASSERT_ALWAYS(exceptionThrown, "Setting ignore_tendon_compliance must throw an exception.");

    AbstractProperty& dProp2 = thelenMsl->updPropertyByName("ignore_tendon_compliance");
    cout << "Prop after create system is " << dProp2.toString() << endl;
    bool after = PropertyHelper::getValueBool(dProp);
    SimTK_ASSERT_ALWAYS(!after, "Property has wrong value!!");
    dTRIlong->updGeometryPath().updateGeometry(context->getCurrentStateRef());
    const OpenSim::Array<AbstractPathPoint*>& path = context->getCurrentPath(*dTRIlong);
    cout << "Muscle Path" << endl;
    cout << path.getSize() << endl;
    for(int i=0; i< path.getSize(); i++)
        cout << path[i]->getParentFrame().getName()
             << path[i]->getLocation(stateCopy) << endl;
    // Compare to known path
    const OpenSim::Body& dBody = model->getBodySet().get("r_ulna_radius_hand");
    Transform xform = context->getTransform(dBody);
    cout << xform << endl;
    double flat[16];
    context->getTransformAsDouble16(xform, flat);
    // Compare to known xform
    double markerPosition[] = {.005000000000, -0.290400000000, 0.030000000000};
    double markerPositionInGround[3];
    context->transformPosition(dBody, markerPosition, markerPositionInGround);  // to display markers
    cout << "Global frame position = " << markerPositionInGround[0] <<
        markerPositionInGround[1] << markerPositionInGround[2]<< endl;
    // Check xformed point against known position
    const Coordinate& dr_elbow_flex = model->getCoordinateSet().get("r_elbow_flex");
    bool isLocked = context->getLocked(dr_elbow_flex);
    assert(!isLocked);
    double startValue = context->getValue(dr_elbow_flex);
    cout << "Coordinate start value = " << startValue << endl;
    double length1 = context->getMuscleLength(*dTRIlong);
    cout << length1 << endl;
    ASSERT_EQUAL(.277609, length1, 1e-5);
    // Coordinate Slider
    context->setValue(dr_elbow_flex, 100*SimTK_PI/180.);
    // Get body transform, marker position and muscle path (tests wrapping as well)
    xform = context->getTransform(dBody);
    cout << "After setting coordinate to 100 deg." << endl;
    cout << xform << endl;
    // Compare to known xform
    dTRIlong->updGeometryPath().updateGeometry(context->getCurrentStateRef());
    const OpenSim::Array<AbstractPathPoint*>& newPath = context->getCurrentPath(*dTRIlong);
    // Compare to known path
    cout << "New Muscle Path" << endl;
    cout << path.getSize() << endl;
    for(int i=0; i< path.getSize(); i++)
        cout << path[i]->getParentFrame().getName()
             << path[i]->getLocation(stateCopy) << endl;
    double length2 = context->getMuscleLength(*dTRIlong);
    cout << length2 << endl;
    ASSERT_EQUAL(.315748, length2, 1e-5);
    // Test that we can lock coordinates to specific value and make this persistant.
    Coordinate& dr_elbow_flex_mod = model->updCoordinateSet().get("r_elbow_flex");
    //dr_elbow_flex_mod.setDefaultValue(0.5);
    dr_elbow_flex_mod.setDefaultLocked(true);
    context->setValue(dr_elbow_flex_mod, 0.5);
    //model->print("wrist_locked_elbow.osim");
    context->recreateSystemKeepStage();
    const Coordinate& dr_elbow_flexNew = model->getCoordinateSet().get("r_elbow_flex");
    assert(context->getLocked(dr_elbow_flexNew));
    ASSERT_EQUAL(0.5, context->getValue(dr_elbow_flexNew), 0.000001);

    // Exercise Editing workflow
    // These are the same calls done from GUI code base through Property edits
    OpenSim::Body& bdy = model->updBodySet().get("r_humerus");
    AbstractProperty& massProp = bdy.updPropertyByName("mass");
    double oldValue = PropertyHelper::getValueDouble(massProp);
    double v = oldValue + 1.0;
    context->cacheModelAndState();
    PropertyHelper::setValueDouble(v, massProp);
    context->restoreStateFromCachedModel();

    // Exercise PathPoint operations used to edit Path in GUI
    PathPointSet& pathPoints = dTRIlong->updGeometryPath().updPathPointSet();
    std::string pathBeforeInXML = dTRIlong->updGeometryPath().dump();
    std::cout << pathBeforeInXML << endl;
    int origSize = pathPoints.getSize();
    AbstractPathPoint& savePoint = pathPoints.get("TRIlong-P2");
    const std::string& saveFrameName = savePoint.getParentFrame().getName();
    AbstractPathPoint* clonedPoint = savePoint.clone();

    // Test delete second PathPoint from TRIlong muscle
    context->deletePathPoint(dTRIlong->updGeometryPath(), 2);
    assert(pathPoints.getSize() == origSize - 1);
    std::string pathAfterDeletionInXML = dTRIlong->updGeometryPath().dump();
    std::cout << pathAfterDeletionInXML << endl;

    // Test adding PathPoint to TRIlong muscle (Stationary)
    Component& frame = model->updComponent(saveFrameName);
    PhysicalFrame* physFrame = PhysicalFrame::safeDownCast(&frame);
    context->addPathPoint(dTRIlong->updGeometryPath(), 3, *physFrame);
    assert(pathPoints.getSize() == origSize);
    std::string pathAfterReinsertionInXML = dTRIlong->updGeometryPath().dump();
    std::cout << pathAfterReinsertionInXML << endl;

    // Test changing type to ConditionalPathPoint
    ConditionalPathPoint* newPoint = new ConditionalPathPoint();
    AbstractPathPoint& oldPoint = pathPoints.get(2);
    newPoint->setCoordinate(model->getCoordinateSet().get(0));
    newPoint->setParentFrame(oldPoint.getParentFrame());
    context->replacePathPoint(dTRIlong->updGeometryPath(), oldPoint, *newPoint);
    assert(pathPoints.getSize() == origSize);

    std::string pathAfterTypeChangeToViaInXML = dTRIlong->updGeometryPath().dump();
    std::cout << pathAfterTypeChangeToViaInXML << endl;

    // Make a change to a socket that is invalid and verify that we can recover
    // from that invalid change by restoring from a cached model and state.
    context->cacheModelAndState();
    Joint& shoulder = model->updComponent<Joint>("r_shoulder");
    AbstractSocket& socket = shoulder.updSocket("child_frame");
    const std::string originalConnecteeName = socket.getConnecteeName();
    try {
        // create an invalid model where joint connects two frames on ground
        context->setConnecteeName(shoulder, socket, "ground");
    }
    catch (const std::exception& e) {
        // Expect meaningful error message rather than a crash
        cout << "Exception: " << e.what() << endl;
    }
    AbstractSocket& psocket = shoulder.updSocket("parent_frame");
    const std::string poriginalConnecteeName = psocket.getConnecteeName();
    try {
        // create an invalid model
        psocket.setConnecteeName("r_ulna_radius_hand");
        context->restoreStateFromCachedModel();
    }
    catch (...) {
        // undo the change
        socket.setConnecteeName(originalConnecteeName);
        context->restoreStateFromCachedModel();
    }

    return status;
  } catch (const std::exception& e) {
      cout << "Exception: " << e.what() << endl;
      return 1;
  }
}

<commit_msg>Update test case<commit_after>/* -------------------------------------------------------------------------- *
 *                         OpenSim:  testContext.cpp                          *
 * -------------------------------------------------------------------------- *
 * The OpenSim API is a toolkit for musculoskeletal modeling and simulation.  *
 * See http://opensim.stanford.edu and the NOTICE file for more information.  *
 * OpenSim is developed at Stanford University and supported by the US        *
 * National Institutes of Health (U54 GM072970, R24 HD065690) and by DARPA    *
 * through the Warrior Web program.                                           *
 *                                                                            *
 * Copyright (c) 2005-2017 Stanford University and the Authors                *
 * Author(s): Ayman Habib                                                     *
 *                                                                            *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may    *
 * not use this file except in compliance with the License. You may obtain a  *
 * copy of the License at http://www.apache.org/licenses/LICENSE-2.0.         *
 *                                                                            *
 * Unless required by applicable law or agreed to in writing, software        *
 * distributed under the License is distributed on an "AS IS" BASIS,          *
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   *
 * See the License for the specific language governing permissions and        *
 * limitations under the License.                                             *
 * -------------------------------------------------------------------------- */
#include "../OpenSimContext.h"
#include <OpenSim/Common/Function.h>
#include <OpenSim/Common/MarkerData.h>
#include <OpenSim/Simulation/SimbodyEngine/Body.h>
#include <OpenSim/Simulation/SimbodyEngine/Coordinate.h>
#include <OpenSim/Simulation/Model/Marker.h>
#include <OpenSim/Simulation/Model/Model.h>
#include <OpenSim/Simulation/Model/BodySet.h>
#include <OpenSim/Simulation/Model/MovingPathPoint.h>
#include <OpenSim/Simulation/Model/Muscle.h>
#include <OpenSim/Simulation/Model/PathPoint.h>
#include <OpenSim/Simulation/Wrap/PathWrap.h>
#include <OpenSim/Simulation/Model/ConditionalPathPoint.h>
#include <OpenSim/Simulation/SimbodyEngine/SimbodyEngine.h>
#include <OpenSim/Simulation/Wrap/WrapObject.h>
#include <OpenSim/Simulation/Model/Analysis.h>
#include <OpenSim/Simulation/Model/MarkerSet.h>
#include <OpenSim/Tools/AnalyzeTool.h>
#include <OpenSim/Tools/ModelScaler.h>
#include <OpenSim/Tools/Measurement.h>
#include <OpenSim/Tools/MarkerPlacer.h>
#include <OpenSim/Simulation/Model/ForceSet.h>
#include <OpenSim/Common/LoadOpenSimLibrary.h>
#include <OpenSim/Actuators/Thelen2003Muscle.h>

using namespace OpenSim;
using namespace SimTK;
using namespace std;

#define ASSERT_EQUAL(expected, found, tolerance) { \
double tol = std::max((tolerance), std::abs((expected)*(tolerance))); \
if ((found)<(expected)-(tol) || (found)>(expected)+(tol)) throw(exception());}

int main()
{
  try {
    int  status = 0;

    // To Retrace the steps taken by the GUI this test case follows the same call sequence:
    // new Model(file)
    // new OpenSimContext(model.initSystem(), model);
    // context.updateDisplayer(muscle)  // to display muscles
    // context.getCurrentPath(muscle)
    // context.getTransform(body)
    // context.transformPosition(body, loc, global)  // to display markers
    // context.getLocked(Coordinate)
    // context.getValue(cooridnate)
    LoadOpenSimLibrary("osimActuators");
    LoadOpenSimLibrary("osimSimulation");
    LoadOpenSimLibrary("osimJavaJNI");

    Model *model = new Model("wrist.osim");
    OpenSimContext* context = new OpenSimContext(&model->initSystem(), model);
    const ForceSet& fs = model->getForceSet();
    int n1 = fs.getNumGroups();
    const ObjectGroup* grp = fs.getGroup("wrist");
    assert(grp);
    const Array<const Object*>& members = grp->getMembers();
    int sz = members.getSize();
    ASSERT_EQUAL(sz,5,0);
    assert(members.get(0)->getName()=="ECRB");
    delete model;
    delete context;
    model = new Model("arm26_20.osim");
    context = new OpenSimContext(&model->initSystem(), model);
    // Make a copy of state contained in context ad make sure content match
    SimTK::State stateCopy = context->getCurrentStateCopy();
    assert(context->getCurrentStateRef().toString()==stateCopy.toString());

    Array<std::string> stateNames = model->getStateVariableNames();
    OpenSim::Force* dForce=&(model->updForceSet().get("TRIlong"));
    Muscle* dTRIlong = dynamic_cast<Muscle*>(dForce);
    assert(dTRIlong);
    context->setPropertiesFromState();
    OpenSim::Thelen2003Muscle* thelenMsl = dynamic_cast<Thelen2003Muscle*>(dTRIlong);
    AbstractProperty& dProp = thelenMsl->updPropertyByName("ignore_tendon_compliance");

    PropertyHelper::setValueBool(true, dProp);
    cout << "Prop after is " << dProp.toString() << endl;

    bool exceptionThrown = false;
    try{// adding to the system should cause Muscle that do not handle
        // ignore_tendon_compliance to throw an exception
        context->recreateSystemKeepStage();
    }
    catch (const std::exception& e) {
        cout << e.what() << endl;
        exceptionThrown = true;
        PropertyHelper::setValueBool(false, dProp);
        cout << "Prop reset to " << dProp.toString() << endl;
        // recreate the system so test can continue
        context->recreateSystemKeepStage();
    }

    SimTK_ASSERT_ALWAYS(exceptionThrown, "Setting ignore_tendon_compliance must throw an exception.");

    AbstractProperty& dProp2 = thelenMsl->updPropertyByName("ignore_tendon_compliance");
    cout << "Prop after create system is " << dProp2.toString() << endl;
    bool after = PropertyHelper::getValueBool(dProp);
    SimTK_ASSERT_ALWAYS(!after, "Property has wrong value!!");
    dTRIlong->updGeometryPath().updateGeometry(context->getCurrentStateRef());
    const OpenSim::Array<AbstractPathPoint*>& path = context->getCurrentPath(*dTRIlong);
    cout << "Muscle Path" << endl;
    cout << path.getSize() << endl;
    for(int i=0; i< path.getSize(); i++)
        cout << path[i]->getParentFrame().getName()
             << path[i]->getLocation(stateCopy) << endl;
    // Compare to known path
    const OpenSim::Body& dBody = model->getBodySet().get("r_ulna_radius_hand");
    Transform xform = context->getTransform(dBody);
    cout << xform << endl;
    double flat[16];
    context->getTransformAsDouble16(xform, flat);
    // Compare to known xform
    double markerPosition[] = {.005000000000, -0.290400000000, 0.030000000000};
    double markerPositionInGround[3];
    context->transformPosition(dBody, markerPosition, markerPositionInGround);  // to display markers
    cout << "Global frame position = " << markerPositionInGround[0] <<
        markerPositionInGround[1] << markerPositionInGround[2]<< endl;
    // Check xformed point against known position
    const Coordinate& dr_elbow_flex = model->getCoordinateSet().get("r_elbow_flex");
    bool isLocked = context->getLocked(dr_elbow_flex);
    assert(!isLocked);
    double startValue = context->getValue(dr_elbow_flex);
    cout << "Coordinate start value = " << startValue << endl;
    double length1 = context->getMuscleLength(*dTRIlong);
    cout << length1 << endl;
    ASSERT_EQUAL(.277609, length1, 1e-5);
    // Coordinate Slider
    context->setValue(dr_elbow_flex, 100*SimTK_PI/180.);
    // Get body transform, marker position and muscle path (tests wrapping as well)
    xform = context->getTransform(dBody);
    cout << "After setting coordinate to 100 deg." << endl;
    cout << xform << endl;
    // Compare to known xform
    dTRIlong->updGeometryPath().updateGeometry(context->getCurrentStateRef());
    const OpenSim::Array<AbstractPathPoint*>& newPath = context->getCurrentPath(*dTRIlong);
    // Compare to known path
    cout << "New Muscle Path" << endl;
    cout << path.getSize() << endl;
    for(int i=0; i< path.getSize(); i++)
        cout << path[i]->getParentFrame().getName()
             << path[i]->getLocation(stateCopy) << endl;
    double length2 = context->getMuscleLength(*dTRIlong);
    cout << length2 << endl;
    ASSERT_EQUAL(.315748, length2, 1e-5);
    // Test that we can lock coordinates to specific value and make this persistant.
    Coordinate& dr_elbow_flex_mod = model->updCoordinateSet().get("r_elbow_flex");
    //dr_elbow_flex_mod.setDefaultValue(0.5);
    dr_elbow_flex_mod.setDefaultLocked(true);
    context->setValue(dr_elbow_flex_mod, 0.5);
    //model->print("wrist_locked_elbow.osim");
    context->recreateSystemKeepStage();
    const Coordinate& dr_elbow_flexNew = model->getCoordinateSet().get("r_elbow_flex");
    assert(context->getLocked(dr_elbow_flexNew));
    ASSERT_EQUAL(0.5, context->getValue(dr_elbow_flexNew), 0.000001);

    // Exercise Editing workflow
    // These are the same calls done from GUI code base through Property edits
    OpenSim::Body& bdy = model->updBodySet().get("r_humerus");
    AbstractProperty& massProp = bdy.updPropertyByName("mass");
    double oldValue = PropertyHelper::getValueDouble(massProp);
    double v = oldValue + 1.0;
    context->cacheModelAndState();
    PropertyHelper::setValueDouble(v, massProp);
    context->restoreStateFromCachedModel();

    // Exercise PathPoint operations used to edit Path in GUI
    PathPointSet& pathPoints = dTRIlong->updGeometryPath().updPathPointSet();
    std::string pathBeforeInXML = dTRIlong->updGeometryPath().dump();
    std::cout << pathBeforeInXML << endl;
    int origSize = pathPoints.getSize();
    AbstractPathPoint& savePoint = pathPoints.get("TRIlong-P2");
    const std::string& saveFrameName = savePoint.getParentFrame().getName();
    AbstractPathPoint* clonedPoint = savePoint.clone();

    // Test delete second PathPoint from TRIlong muscle
    context->deletePathPoint(dTRIlong->updGeometryPath(), 2);
    assert(pathPoints.getSize() == origSize - 1);
    std::string pathAfterDeletionInXML = dTRIlong->updGeometryPath().dump();
    std::cout << pathAfterDeletionInXML << endl;

    // Test adding PathPoint to TRIlong muscle (Stationary)
    Component& frame = model->updComponent(saveFrameName);
    PhysicalFrame* physFrame = PhysicalFrame::safeDownCast(&frame);
    context->addPathPoint(dTRIlong->updGeometryPath(), 3, *physFrame);
    assert(pathPoints.getSize() == origSize);
    std::string pathAfterReinsertionInXML = dTRIlong->updGeometryPath().dump();
    std::cout << pathAfterReinsertionInXML << endl;

    // Test changing type to ConditionalPathPoint
    ConditionalPathPoint* newPoint = new ConditionalPathPoint();
    AbstractPathPoint& oldPoint = pathPoints.get(2);
    newPoint->setCoordinate(model->getCoordinateSet().get(0));
    newPoint->setParentFrame(oldPoint.getParentFrame());
    context->replacePathPoint(dTRIlong->updGeometryPath(), oldPoint, *newPoint);
    assert(pathPoints.getSize() == origSize);

    std::string pathAfterTypeChangeToViaInXML = dTRIlong->updGeometryPath().dump();
    std::cout << pathAfterTypeChangeToViaInXML << endl;

    // Make a change to a socket that is invalid and verify that we can recover
    // from that invalid change by restoring from a cached model and state.
    context->cacheModelAndState();
    Joint& shoulder = model->updComponent<Joint>("r_shoulder");
    AbstractSocket& socket = shoulder.updSocket("child_frame");
    const std::string originalConnecteeName = socket.getConnecteeName();
    try {
        // create an invalid model where joint connects two frames on ground
        context->setConnecteeName(shoulder, socket, "ground");
    }
    catch (const std::exception& e) {
        // Expect meaningful error message rather than a crash
        cout << "Exception: " << e.what() << endl;
    }
    AbstractSocket& psocket = shoulder.updSocket("parent_frame");
    const std::string poriginalConnecteeName = psocket.getConnecteeName();
    try {
        // create an invalid model
        context->setConnecteeName(shoulder, psocket, "r_ulna_radius_hand");
    }
    catch (const std::exception& e) {
        // undo the change
        cout << "Exception: " << e.what() << endl;
    }

    return status;
  } catch (const std::exception& e) {
      cout << "Exception: " << e.what() << endl;
      return 1;
  }
}

<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

// Unit tests for DelayManager class.

#include "webrtc/modules/audio_coding/neteq/delay_manager.h"

#include <math.h>

#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "webrtc/modules/audio_coding/neteq/mock/mock_delay_peak_detector.h"

namespace webrtc {

using ::testing::Return;
using ::testing::_;

class DelayManagerTest : public ::testing::Test {
 protected:
  static const int kMaxNumberOfPackets = 240;
  static const int kTimeStepMs = 10;
  static const int kFs = 8000;
  static const int kFrameSizeMs = 20;
  static const int kTsIncrement = kFrameSizeMs * kFs / 1000;

  DelayManagerTest();
  virtual void SetUp();
  virtual void TearDown();
  void SetPacketAudioLength(int lengt_ms);
  void InsertNextPacket();
  void IncreaseTime(int inc_ms);

  DelayManager* dm_;
  MockDelayPeakDetector detector_;
  uint16_t seq_no_;
  uint32_t ts_;
};

DelayManagerTest::DelayManagerTest()
    : dm_(NULL),
      seq_no_(0x1234),
      ts_(0x12345678) {
}

void DelayManagerTest::SetUp() {
  EXPECT_CALL(detector_, Reset())
            .Times(1);
  dm_ = new DelayManager(kMaxNumberOfPackets, &detector_);
}

void DelayManagerTest::SetPacketAudioLength(int lengt_ms) {
  EXPECT_CALL(detector_, SetPacketAudioLength(lengt_ms));
  dm_->SetPacketAudioLength(lengt_ms);
}

void DelayManagerTest::InsertNextPacket() {
  EXPECT_EQ(0, dm_->Update(seq_no_, ts_, kFs));
  seq_no_ += 1;
  ts_ += kTsIncrement;
}

void DelayManagerTest::IncreaseTime(int inc_ms) {
  for (int t = 0; t < inc_ms; t += kTimeStepMs) {
    EXPECT_CALL(detector_, IncrementCounter(kTimeStepMs))
        .Times(1);
    dm_->UpdateCounters(kTimeStepMs);
  }
}
void DelayManagerTest::TearDown() {
  EXPECT_CALL(detector_, Die());
  delete dm_;
}

TEST_F(DelayManagerTest, CreateAndDestroy) {
  // Nothing to do here. The test fixture creates and destroys the DelayManager
  // object.
}

TEST_F(DelayManagerTest, VectorInitialization) {
  const DelayManager::IATVector& vec = dm_->iat_vector();
  double sum = 0.0;
  for (size_t i = 0; i < vec.size(); i++) {
    EXPECT_NEAR(ldexp(pow(0.5, static_cast<int>(i + 1)), 30), vec[i], 65536);
    // Tolerance 65536 in Q30 corresponds to a delta of approximately 0.00006.
    sum += vec[i];
  }
  EXPECT_EQ(1 << 30, static_cast<int>(sum));  // Should be 1 in Q30.
}

TEST_F(DelayManagerTest, SetPacketAudioLength) {
  const int kLengthMs = 30;
  // Expect DelayManager to pass on the new length to the detector object.
  EXPECT_CALL(detector_, SetPacketAudioLength(kLengthMs))
      .Times(1);
  EXPECT_EQ(0, dm_->SetPacketAudioLength(kLengthMs));
  EXPECT_EQ(-1, dm_->SetPacketAudioLength(-1));  // Illegal parameter value.
}

TEST_F(DelayManagerTest, PeakFound) {
  // Expect DelayManager to pass on the question to the detector.
  // Call twice, and let the detector return true the first time and false the
  // second time.
  EXPECT_CALL(detector_, peak_found())
      .WillOnce(Return(true))
      .WillOnce(Return(false));
  EXPECT_TRUE(dm_->PeakFound());
  EXPECT_FALSE(dm_->PeakFound());
}

TEST_F(DelayManagerTest, UpdateCounters) {
  // Expect DelayManager to pass on the counter update to the detector.
  EXPECT_CALL(detector_, IncrementCounter(kTimeStepMs))
      .Times(1);
  dm_->UpdateCounters(kTimeStepMs);
}

TEST_F(DelayManagerTest, UpdateNormal) {
  SetPacketAudioLength(kFrameSizeMs);
  // First packet arrival.
  InsertNextPacket();
  // Advance time by one frame size.
  IncreaseTime(kFrameSizeMs);
  // Second packet arrival.
  // Expect detector update method to be called once with inter-arrival time
  // equal to 1 packet, and (base) target level equal to 1 as well.
  // Return false to indicate no peaks found.
  EXPECT_CALL(detector_, Update(1, 1))
      .WillOnce(Return(false));
  InsertNextPacket();
  EXPECT_EQ(1 << 8, dm_->TargetLevel());  // In Q8.
  EXPECT_EQ(1, dm_->base_target_level());
  int lower, higher;
  dm_->BufferLimits(&lower, &higher);
  // Expect |lower| to be 75% of target level, and |higher| to be target level,
  // but also at least 20 ms higher than |lower|, which is the limiting case
  // here.
  EXPECT_EQ((1 << 8) * 3 / 4, lower);
  EXPECT_EQ(lower + (20 << 8) / kFrameSizeMs, higher);
}

TEST_F(DelayManagerTest, UpdateLongInterArrivalTime) {
  SetPacketAudioLength(kFrameSizeMs);
  // First packet arrival.
  InsertNextPacket();
  // Advance time by two frame size.
  IncreaseTime(2 * kFrameSizeMs);
  // Second packet arrival.
  // Expect detector update method to be called once with inter-arrival time
  // equal to 1 packet, and (base) target level equal to 1 as well.
  // Return false to indicate no peaks found.
  EXPECT_CALL(detector_, Update(2, 2))
      .WillOnce(Return(false));
  InsertNextPacket();
  EXPECT_EQ(2 << 8, dm_->TargetLevel());  // In Q8.
  EXPECT_EQ(2, dm_->base_target_level());
  int lower, higher;
  dm_->BufferLimits(&lower, &higher);
  // Expect |lower| to be 75% of target level, and |higher| to be target level,
  // but also at least 20 ms higher than |lower|, which is the limiting case
  // here.
  EXPECT_EQ((2 << 8) * 3 / 4, lower);
  EXPECT_EQ(lower + (20 << 8) / kFrameSizeMs, higher);
}

TEST_F(DelayManagerTest, UpdatePeakFound) {
  SetPacketAudioLength(kFrameSizeMs);
  // First packet arrival.
  InsertNextPacket();
  // Advance time by one frame size.
  IncreaseTime(kFrameSizeMs);
  // Second packet arrival.
  // Expect detector update method to be called once with inter-arrival time
  // equal to 1 packet, and (base) target level equal to 1 as well.
  // Return true to indicate that peaks are found. Let the peak height be 5.
  EXPECT_CALL(detector_, Update(1, 1))
      .WillOnce(Return(true));
  EXPECT_CALL(detector_, MaxPeakHeight())
      .WillOnce(Return(5));
  InsertNextPacket();
  EXPECT_EQ(5 << 8, dm_->TargetLevel());
  EXPECT_EQ(1, dm_->base_target_level());  // Base target level is w/o peaks.
  int lower, higher;
  dm_->BufferLimits(&lower, &higher);
  // Expect |lower| to be 75% of target level, and |higher| to be target level.
  EXPECT_EQ((5 << 8) * 3 / 4, lower);
  EXPECT_EQ(5 << 8, higher);
}

TEST_F(DelayManagerTest, TargetDelay) {
  SetPacketAudioLength(kFrameSizeMs);
  // First packet arrival.
  InsertNextPacket();
  // Advance time by one frame size.
  IncreaseTime(kFrameSizeMs);
  // Second packet arrival.
  // Expect detector update method to be called once with inter-arrival time
  // equal to 1 packet, and (base) target level equal to 1 as well.
  // Return false to indicate no peaks found.
  EXPECT_CALL(detector_, Update(1, 1))
      .WillOnce(Return(false));
  InsertNextPacket();
  const int kExpectedTarget = 1;
  EXPECT_EQ(kExpectedTarget << 8, dm_->TargetLevel());  // In Q8.
  EXPECT_EQ(1, dm_->base_target_level());
  int lower, higher;
  dm_->BufferLimits(&lower, &higher);
  // Expect |lower| to be 75% of base target level, and |higher| to be
  // lower + 20 ms headroom.
  EXPECT_EQ((1 << 8) * 3 / 4, lower);
  EXPECT_EQ(lower + (20 << 8) / kFrameSizeMs, higher);
}

TEST_F(DelayManagerTest, MaxAndRequiredDelay) {
  const int kExpectedTarget = 5;
  const int kTimeIncrement = kExpectedTarget * kFrameSizeMs;
  SetPacketAudioLength(kFrameSizeMs);
  // First packet arrival.
  InsertNextPacket();
  // Second packet arrival.
  // Expect detector update method to be called once with inter-arrival time
  // equal to |kExpectedTarget| packet. Return true to indicate peaks found.
  EXPECT_CALL(detector_, Update(kExpectedTarget, _))
      .WillRepeatedly(Return(true));
  EXPECT_CALL(detector_, MaxPeakHeight())
      .WillRepeatedly(Return(kExpectedTarget));
  IncreaseTime(kTimeIncrement);
  InsertNextPacket();

  // No limit is set.
  EXPECT_EQ(kExpectedTarget << 8, dm_->TargetLevel());

  int kMaxDelayPackets = kExpectedTarget - 2;
  int kMaxDelayMs = kMaxDelayPackets * kFrameSizeMs;
  EXPECT_TRUE(dm_->SetMaximumDelay(kMaxDelayMs));
  IncreaseTime(kTimeIncrement);
  InsertNextPacket();
  EXPECT_EQ(kExpectedTarget * kFrameSizeMs, dm_->least_required_delay_ms());
  EXPECT_EQ(kMaxDelayPackets << 8, dm_->TargetLevel());

  // Target level at least should be one packet.
  EXPECT_FALSE(dm_->SetMaximumDelay(kFrameSizeMs - 1));
}

TEST_F(DelayManagerTest, MinAndRequiredDelay) {
  const int kExpectedTarget = 5;
  const int kTimeIncrement = kExpectedTarget * kFrameSizeMs;
  SetPacketAudioLength(kFrameSizeMs);
  // First packet arrival.
  InsertNextPacket();
  // Second packet arrival.
  // Expect detector update method to be called once with inter-arrival time
  // equal to |kExpectedTarget| packet. Return true to indicate peaks found.
  EXPECT_CALL(detector_, Update(kExpectedTarget, _))
      .WillRepeatedly(Return(true));
  EXPECT_CALL(detector_, MaxPeakHeight())
      .WillRepeatedly(Return(kExpectedTarget));
  IncreaseTime(kTimeIncrement);
  InsertNextPacket();

  // No limit is applied.
  EXPECT_EQ(kExpectedTarget << 8, dm_->TargetLevel());

  int kMinDelayPackets = kExpectedTarget + 2;
  int kMinDelayMs = kMinDelayPackets * kFrameSizeMs;
  dm_->SetMinimumDelay(kMinDelayMs);
  IncreaseTime(kTimeIncrement);
  InsertNextPacket();
  EXPECT_EQ(kExpectedTarget * kFrameSizeMs, dm_->least_required_delay_ms());
  EXPECT_EQ(kMinDelayPackets << 8, dm_->TargetLevel());
}

TEST_F(DelayManagerTest, Failures) {
  // Wrong sample rate.
  EXPECT_EQ(-1, dm_->Update(0, 0, -1));
  // Wrong packet size.
  EXPECT_EQ(-1, dm_->SetPacketAudioLength(0));
  EXPECT_EQ(-1, dm_->SetPacketAudioLength(-1));

  // Minimum delay higher than a maximum delay is not accepted.
  EXPECT_TRUE(dm_->SetMaximumDelay(10));
  EXPECT_FALSE(dm_->SetMinimumDelay(20));

  // Maximum delay less than minimum delay is not accepted.
  EXPECT_TRUE(dm_->SetMaximumDelay(100));
  EXPECT_TRUE(dm_->SetMinimumDelay(80));
  EXPECT_FALSE(dm_->SetMaximumDelay(60));
}

}  // namespace webrtc
<commit_msg>Increase the tolerance in NetEq's DelayManagerTest a notch<commit_after>/*
 *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

// Unit tests for DelayManager class.

#include "webrtc/modules/audio_coding/neteq/delay_manager.h"

#include <math.h>

#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "webrtc/modules/audio_coding/neteq/mock/mock_delay_peak_detector.h"

namespace webrtc {

using ::testing::Return;
using ::testing::_;

class DelayManagerTest : public ::testing::Test {
 protected:
  static const int kMaxNumberOfPackets = 240;
  static const int kTimeStepMs = 10;
  static const int kFs = 8000;
  static const int kFrameSizeMs = 20;
  static const int kTsIncrement = kFrameSizeMs * kFs / 1000;

  DelayManagerTest();
  virtual void SetUp();
  virtual void TearDown();
  void SetPacketAudioLength(int lengt_ms);
  void InsertNextPacket();
  void IncreaseTime(int inc_ms);

  DelayManager* dm_;
  MockDelayPeakDetector detector_;
  uint16_t seq_no_;
  uint32_t ts_;
};

DelayManagerTest::DelayManagerTest()
    : dm_(NULL),
      seq_no_(0x1234),
      ts_(0x12345678) {
}

void DelayManagerTest::SetUp() {
  EXPECT_CALL(detector_, Reset())
            .Times(1);
  dm_ = new DelayManager(kMaxNumberOfPackets, &detector_);
}

void DelayManagerTest::SetPacketAudioLength(int lengt_ms) {
  EXPECT_CALL(detector_, SetPacketAudioLength(lengt_ms));
  dm_->SetPacketAudioLength(lengt_ms);
}

void DelayManagerTest::InsertNextPacket() {
  EXPECT_EQ(0, dm_->Update(seq_no_, ts_, kFs));
  seq_no_ += 1;
  ts_ += kTsIncrement;
}

void DelayManagerTest::IncreaseTime(int inc_ms) {
  for (int t = 0; t < inc_ms; t += kTimeStepMs) {
    EXPECT_CALL(detector_, IncrementCounter(kTimeStepMs))
        .Times(1);
    dm_->UpdateCounters(kTimeStepMs);
  }
}
void DelayManagerTest::TearDown() {
  EXPECT_CALL(detector_, Die());
  delete dm_;
}

TEST_F(DelayManagerTest, CreateAndDestroy) {
  // Nothing to do here. The test fixture creates and destroys the DelayManager
  // object.
}

TEST_F(DelayManagerTest, VectorInitialization) {
  const DelayManager::IATVector& vec = dm_->iat_vector();
  double sum = 0.0;
  for (size_t i = 0; i < vec.size(); i++) {
    EXPECT_NEAR(ldexp(pow(0.5, static_cast<int>(i + 1)), 30), vec[i], 65537);
    // Tolerance 65537 in Q30 corresponds to a delta of approximately 0.00006.
    sum += vec[i];
  }
  EXPECT_EQ(1 << 30, static_cast<int>(sum));  // Should be 1 in Q30.
}

TEST_F(DelayManagerTest, SetPacketAudioLength) {
  const int kLengthMs = 30;
  // Expect DelayManager to pass on the new length to the detector object.
  EXPECT_CALL(detector_, SetPacketAudioLength(kLengthMs))
      .Times(1);
  EXPECT_EQ(0, dm_->SetPacketAudioLength(kLengthMs));
  EXPECT_EQ(-1, dm_->SetPacketAudioLength(-1));  // Illegal parameter value.
}

TEST_F(DelayManagerTest, PeakFound) {
  // Expect DelayManager to pass on the question to the detector.
  // Call twice, and let the detector return true the first time and false the
  // second time.
  EXPECT_CALL(detector_, peak_found())
      .WillOnce(Return(true))
      .WillOnce(Return(false));
  EXPECT_TRUE(dm_->PeakFound());
  EXPECT_FALSE(dm_->PeakFound());
}

TEST_F(DelayManagerTest, UpdateCounters) {
  // Expect DelayManager to pass on the counter update to the detector.
  EXPECT_CALL(detector_, IncrementCounter(kTimeStepMs))
      .Times(1);
  dm_->UpdateCounters(kTimeStepMs);
}

TEST_F(DelayManagerTest, UpdateNormal) {
  SetPacketAudioLength(kFrameSizeMs);
  // First packet arrival.
  InsertNextPacket();
  // Advance time by one frame size.
  IncreaseTime(kFrameSizeMs);
  // Second packet arrival.
  // Expect detector update method to be called once with inter-arrival time
  // equal to 1 packet, and (base) target level equal to 1 as well.
  // Return false to indicate no peaks found.
  EXPECT_CALL(detector_, Update(1, 1))
      .WillOnce(Return(false));
  InsertNextPacket();
  EXPECT_EQ(1 << 8, dm_->TargetLevel());  // In Q8.
  EXPECT_EQ(1, dm_->base_target_level());
  int lower, higher;
  dm_->BufferLimits(&lower, &higher);
  // Expect |lower| to be 75% of target level, and |higher| to be target level,
  // but also at least 20 ms higher than |lower|, which is the limiting case
  // here.
  EXPECT_EQ((1 << 8) * 3 / 4, lower);
  EXPECT_EQ(lower + (20 << 8) / kFrameSizeMs, higher);
}

TEST_F(DelayManagerTest, UpdateLongInterArrivalTime) {
  SetPacketAudioLength(kFrameSizeMs);
  // First packet arrival.
  InsertNextPacket();
  // Advance time by two frame size.
  IncreaseTime(2 * kFrameSizeMs);
  // Second packet arrival.
  // Expect detector update method to be called once with inter-arrival time
  // equal to 1 packet, and (base) target level equal to 1 as well.
  // Return false to indicate no peaks found.
  EXPECT_CALL(detector_, Update(2, 2))
      .WillOnce(Return(false));
  InsertNextPacket();
  EXPECT_EQ(2 << 8, dm_->TargetLevel());  // In Q8.
  EXPECT_EQ(2, dm_->base_target_level());
  int lower, higher;
  dm_->BufferLimits(&lower, &higher);
  // Expect |lower| to be 75% of target level, and |higher| to be target level,
  // but also at least 20 ms higher than |lower|, which is the limiting case
  // here.
  EXPECT_EQ((2 << 8) * 3 / 4, lower);
  EXPECT_EQ(lower + (20 << 8) / kFrameSizeMs, higher);
}

TEST_F(DelayManagerTest, UpdatePeakFound) {
  SetPacketAudioLength(kFrameSizeMs);
  // First packet arrival.
  InsertNextPacket();
  // Advance time by one frame size.
  IncreaseTime(kFrameSizeMs);
  // Second packet arrival.
  // Expect detector update method to be called once with inter-arrival time
  // equal to 1 packet, and (base) target level equal to 1 as well.
  // Return true to indicate that peaks are found. Let the peak height be 5.
  EXPECT_CALL(detector_, Update(1, 1))
      .WillOnce(Return(true));
  EXPECT_CALL(detector_, MaxPeakHeight())
      .WillOnce(Return(5));
  InsertNextPacket();
  EXPECT_EQ(5 << 8, dm_->TargetLevel());
  EXPECT_EQ(1, dm_->base_target_level());  // Base target level is w/o peaks.
  int lower, higher;
  dm_->BufferLimits(&lower, &higher);
  // Expect |lower| to be 75% of target level, and |higher| to be target level.
  EXPECT_EQ((5 << 8) * 3 / 4, lower);
  EXPECT_EQ(5 << 8, higher);
}

TEST_F(DelayManagerTest, TargetDelay) {
  SetPacketAudioLength(kFrameSizeMs);
  // First packet arrival.
  InsertNextPacket();
  // Advance time by one frame size.
  IncreaseTime(kFrameSizeMs);
  // Second packet arrival.
  // Expect detector update method to be called once with inter-arrival time
  // equal to 1 packet, and (base) target level equal to 1 as well.
  // Return false to indicate no peaks found.
  EXPECT_CALL(detector_, Update(1, 1))
      .WillOnce(Return(false));
  InsertNextPacket();
  const int kExpectedTarget = 1;
  EXPECT_EQ(kExpectedTarget << 8, dm_->TargetLevel());  // In Q8.
  EXPECT_EQ(1, dm_->base_target_level());
  int lower, higher;
  dm_->BufferLimits(&lower, &higher);
  // Expect |lower| to be 75% of base target level, and |higher| to be
  // lower + 20 ms headroom.
  EXPECT_EQ((1 << 8) * 3 / 4, lower);
  EXPECT_EQ(lower + (20 << 8) / kFrameSizeMs, higher);
}

TEST_F(DelayManagerTest, MaxAndRequiredDelay) {
  const int kExpectedTarget = 5;
  const int kTimeIncrement = kExpectedTarget * kFrameSizeMs;
  SetPacketAudioLength(kFrameSizeMs);
  // First packet arrival.
  InsertNextPacket();
  // Second packet arrival.
  // Expect detector update method to be called once with inter-arrival time
  // equal to |kExpectedTarget| packet. Return true to indicate peaks found.
  EXPECT_CALL(detector_, Update(kExpectedTarget, _))
      .WillRepeatedly(Return(true));
  EXPECT_CALL(detector_, MaxPeakHeight())
      .WillRepeatedly(Return(kExpectedTarget));
  IncreaseTime(kTimeIncrement);
  InsertNextPacket();

  // No limit is set.
  EXPECT_EQ(kExpectedTarget << 8, dm_->TargetLevel());

  int kMaxDelayPackets = kExpectedTarget - 2;
  int kMaxDelayMs = kMaxDelayPackets * kFrameSizeMs;
  EXPECT_TRUE(dm_->SetMaximumDelay(kMaxDelayMs));
  IncreaseTime(kTimeIncrement);
  InsertNextPacket();
  EXPECT_EQ(kExpectedTarget * kFrameSizeMs, dm_->least_required_delay_ms());
  EXPECT_EQ(kMaxDelayPackets << 8, dm_->TargetLevel());

  // Target level at least should be one packet.
  EXPECT_FALSE(dm_->SetMaximumDelay(kFrameSizeMs - 1));
}

TEST_F(DelayManagerTest, MinAndRequiredDelay) {
  const int kExpectedTarget = 5;
  const int kTimeIncrement = kExpectedTarget * kFrameSizeMs;
  SetPacketAudioLength(kFrameSizeMs);
  // First packet arrival.
  InsertNextPacket();
  // Second packet arrival.
  // Expect detector update method to be called once with inter-arrival time
  // equal to |kExpectedTarget| packet. Return true to indicate peaks found.
  EXPECT_CALL(detector_, Update(kExpectedTarget, _))
      .WillRepeatedly(Return(true));
  EXPECT_CALL(detector_, MaxPeakHeight())
      .WillRepeatedly(Return(kExpectedTarget));
  IncreaseTime(kTimeIncrement);
  InsertNextPacket();

  // No limit is applied.
  EXPECT_EQ(kExpectedTarget << 8, dm_->TargetLevel());

  int kMinDelayPackets = kExpectedTarget + 2;
  int kMinDelayMs = kMinDelayPackets * kFrameSizeMs;
  dm_->SetMinimumDelay(kMinDelayMs);
  IncreaseTime(kTimeIncrement);
  InsertNextPacket();
  EXPECT_EQ(kExpectedTarget * kFrameSizeMs, dm_->least_required_delay_ms());
  EXPECT_EQ(kMinDelayPackets << 8, dm_->TargetLevel());
}

TEST_F(DelayManagerTest, Failures) {
  // Wrong sample rate.
  EXPECT_EQ(-1, dm_->Update(0, 0, -1));
  // Wrong packet size.
  EXPECT_EQ(-1, dm_->SetPacketAudioLength(0));
  EXPECT_EQ(-1, dm_->SetPacketAudioLength(-1));

  // Minimum delay higher than a maximum delay is not accepted.
  EXPECT_TRUE(dm_->SetMaximumDelay(10));
  EXPECT_FALSE(dm_->SetMinimumDelay(20));

  // Maximum delay less than minimum delay is not accepted.
  EXPECT_TRUE(dm_->SetMaximumDelay(100));
  EXPECT_TRUE(dm_->SetMinimumDelay(80));
  EXPECT_FALSE(dm_->SetMaximumDelay(60));
}

}  // namespace webrtc
<|endoftext|>


<commit_before>#include <iostream>
using namespace std;
<commit_msg>amend maint<commit_after>#include <iostream>
using namespace std;
/**
 * This is maint branch
 **/

int main() {
}

<|endoftext|>


<commit_before>/*********************************************************************************
 *
 * Inviwo - Interactive Visualization Workshop
 *
 * Copyright (c) 2019-2021 Inviwo Foundation
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *********************************************************************************/

#include <ivwdataframe/pydataframe.h>

#include <warn/push>
#include <warn/ignore/shadow>
#include <pybind11/functional.h>
#include <pybind11/stl.h>
#include <pybind11/stl_bind.h>
#include <warn/pop>

#include <inviwo/dataframe/datastructures/column.h>
#include <inviwo/dataframe/datastructures/dataframe.h>
#include <inviwo/dataframe/datastructures/datapoint.h>
#include <inviwo/dataframe/util/dataframeutil.h>

#include <inviwo/core/util/defaultvalues.h>
#include <inviwo/core/util/safecstr.h>
#include <inviwo/core/datastructures/buffer/buffer.h>
#include <modules/python3/pyportutils.h>

#include <fmt/format.h>

namespace py = pybind11;

namespace inviwo {

namespace {

struct DataFrameAddColumnReg {
    template <typename T>
    auto operator()(py::class_<DataFrame, std::shared_ptr<DataFrame>>& d) {
        auto classname = Defaultvalues<T>::getName();

        d.def(
            fmt::format("add{}Column", classname).c_str(),
            [](DataFrame& d, const std::string& header, const size_t size = 0) {
                return d.addColumn<T>(header, size);
            },
            py::arg("header"), py::arg("size") = 0);

        d.def(
            fmt::format("add{}Column", classname).c_str(),
            [](DataFrame& d, std::string header, std::vector<T> data) {
                return d.addColumn(std::move(header), std::move(data));
            },
            py::arg("header"), py::arg("data"));
    }
};

struct DataPointReg {
    template <typename T>
    auto operator()(pybind11::module& m) {
        using D = DataPoint<T>;
        auto classname = Defaultvalues<T>::getName() + "DataPoint";

        py::class_<D, DataPointBase, std::shared_ptr<D>> data(m, classname.c_str());
        data.def_property_readonly("data", &D::getData)
            .def_property_readonly("str", &D::toString)
            .def("__repr__",
                 [classname](D& p) { return fmt::format("<{}: '{}'>", classname, p.toString()); });
    }
};

struct TemplateColumnReg {
    template <typename T>
    auto operator()(pybind11::module& m) {
        using C = TemplateColumn<T>;
        auto classname = Defaultvalues<T>::getName() + "Column";

        py::class_<C, Column, std::shared_ptr<C>> col(m, classname.c_str());
        col.def_property_readonly("buffer", [](C& c) { return c.getTypedBuffer(); })
            .def(py::init<const std::string&>())
            .def("add", py::overload_cast<const T&>(&C::add))
            .def("add", py::overload_cast<std::string_view>(&C::add))
            .def("append", [](C& c, C& src) { c.append(src); })
            .def("set", &C::set)
            .def(
                "get",
                [](const C& c, size_t i) {
                    if (i >= c.getSize()) throw py::index_error();
                    return c.get(i);
                },
                py::arg("i"))
            .def(
                "get",
                [](const C& c, size_t i, bool asString) {
                    if (i >= c.getSize()) throw py::index_error();
                    return c.get(i, asString);
                },
                py::arg("i"), py::arg("asString"))
            .def("__repr__", [classname](C& c) {
                return fmt::format("<{}: '{}', {}, {}>", classname, c.getHeader(), c.getSize(),
                                   c.getBuffer()->getDataFormat()->getString());
            });
    }
};

}  // namespace

void exposeDataFrame(pybind11::module& m) {
    py::class_<DataPointBase, std::shared_ptr<DataPointBase>>(m, "DataPointBase")
        .def("__repr__",
             [](DataPointBase& p) { return fmt::format("<DataPoint: '{}'>", p.toString()); });

    py::class_<Column, std::shared_ptr<Column>>(m, "Column")
        .def_property("header", &Column::getHeader, &Column::setHeader)
        .def_property_readonly("buffer", [](Column& self) { return self.getBuffer(); })
        .def_property_readonly("size", &Column::getSize)
        .def("__repr__", [](Column& c) {
            return fmt::format("<Column: '{}', {}, {}>", c.getHeader(), c.getSize(),
                               c.getBuffer()->getDataFormat()->getString());
        });

    using Scalars = std::tuple<float, double, int, glm::i64, size_t, std::uint32_t>;
    util::for_each_type<Scalars>{}(DataPointReg{}, m);
    util::for_each_type<Scalars>{}(TemplateColumnReg{}, m);

    py::class_<CategoricalColumn, TemplateColumn<std::uint32_t>,
               std::shared_ptr<CategoricalColumn>>(m, "CategoricalColumn")
        .def(py::init<const std::string&>())
        .def_property_readonly("categories", &CategoricalColumn::getCategories,
                               py::return_value_policy::copy)
        .def("add", [](CategoricalColumn& c, const std::string& str) { c.add(str); })
        .def("append", [](CategoricalColumn& c, CategoricalColumn& src) { c.append(src); })
        .def("set", [](CategoricalColumn& c, size_t idx, const std::uint32_t& v) { c.set(idx, v); })
        .def("set", py::overload_cast<size_t, const std::string&>(&CategoricalColumn::set))
        .def(
            "get",
            [](const CategoricalColumn& c, size_t i, bool asString) {
                if (i >= c.getSize()) throw py::index_error();
                return c.get(i, asString);
            },
            py::arg("i"), py::arg("asString") = true)
        .def("__repr__", [](CategoricalColumn& c) {
            return fmt::format("<CategoricalColumn: '{}', {}, {} categories>", c.getHeader(),
                               c.getSize(), c.getCategories().size());
        });

    py::class_<DataFrame, std::shared_ptr<DataFrame>> dataframe(m, "DataFrame");
    dataframe.def(py::init<std::uint32_t>(), py::arg("size") = 0)
        .def_property_readonly("cols", &DataFrame::getNumberOfColumns)
        .def_property_readonly("rows", &DataFrame::getNumberOfRows)
        .def("indexcol", [](DataFrame& d) { return d.getIndexColumn(); })
        .def("column", [](DataFrame& self, size_t index) { return self.getColumn(index); })
        .def("addColumnFromBuffer", &DataFrame::addColumnFromBuffer)
        .def("addCategoricalColumn",
             py::overload_cast<std::string_view, size_t>(&DataFrame::addCategoricalColumn),
             py::arg("header"), py::arg("size") = 0)
        .def("addCategoricalColumn",
             py::overload_cast<std::string_view, const std::vector<std::string>&>(
                 &DataFrame::addCategoricalColumn),
             py::arg("header"), py::arg("values"))
        .def("getRow", &DataFrame::getDataItem, py::arg("index"), py::arg("asString") = false)

        .def("updateIndex", [](DataFrame& d) { d.updateIndexBuffer(); })

        // interface for operator[]
        .def(
            "__getitem__",
            [](const DataFrame& d, size_t i) {
                if (i >= d.getNumberOfColumns()) throw py::index_error();
                return *(d.begin() + i);
            },
            py::return_value_policy::reference_internal)
        // sequence protocol operations
        .def(
            "__iter__", [](const DataFrame& d) { return py::make_iterator(d.begin(), d.end()); },
            py::keep_alive<0, 1>() /* Essential: keep object alive while iterator exists */)

        .def("__repr__", [](const DataFrame& d) {
            std::string str = fmt::format("<DataFrame: {} column(s), {} rows",
                                          d.getNumberOfColumns(), d.getNumberOfRows());
            size_t i = 0;
            for (auto c : d) {
                ++i;
                str += fmt::format("\n   {:>3}: '{}', {}, {}", i, c->getHeader(), c->getSize(),
                                   c->getBuffer()->getDataFormat()->getString());
            }
            return str + ">";
        });

    util::for_each_type<Scalars>{}(DataFrameAddColumnReg{}, dataframe);

    m.def("createDataFrame", createDataFrame, py::arg("exampleRows"),
          py::arg("colheaders") = std::vector<std::string>{}, py::arg("doubleprecision") = false,
          R"delim(
Create a new DataFrame by guessing the column types from a number of rows.

Parameters
----------
exampleRows     Rows for guessing data type of each column.
colHeaders      Name of each column. If none are given, "Column 1", "Column 2", ... is used
doubleprecision If true, columns with floating point values will use double for storage
)delim")
        .def("appendColumns", dataframe::appendColumns, py::arg("left"), py::arg("right"),
             py::arg("ignoreduplicates") = false, py::arg("fillmissingrows") = false,
             R"delim(
Create a new DataFrame by appending the columns of DataFrame right to DataFrame left

Parameters
----------
ignoreduplicates   duplicate columns, i.e. same column header, are ignored if true
fillmissingrows    if true, missing rows in either DataFrame are filled with 0 or
                   "undefined" (for categorical columns)
)delim")
        .def("appendRows", dataframe::appendRows, py::arg("top"), py::arg("bottom"),
             py::arg("matchbyname") = false,
             R"delim(
Create a new DataFrame by appending the rows of DataFrame bottom to DataFrame top

Parameters
----------
matchByName    if true, column headers are used for matching columns. Otherwise columns
               are matched by order (default)
)delim")
        .def("innerJoin",
             py::overload_cast<const DataFrame&, const DataFrame&, const std::string&>(
                 dataframe::innerJoin),
             py::arg("left"), py::arg("right"), py::arg("keycolumn") = "index",
             R"delim(
Create a new DataFrame by using an inner join of DataFrame left and DataFrame right.
That is only rows with matching keys are kept.

It is assumed that the entries in the key columns are unique. Otherwise results are undefined.

Parameters
----------
keycolumn    header of the column used as key for the join operation (default: index column)
)delim")
        .def("innerJoin",
             py::overload_cast<const DataFrame&, const DataFrame&, const std::vector<std::string>&>(
                 dataframe::innerJoin),
             py::arg("left"), py::arg("right"), py::arg("keycolumns"),
             R"delim(
Create a new DataFrame by using an inner join of DataFrame left and DataFrame right.
That is only rows with matching all keys are kept.

It is assumed that the entries in the key columns are unique. Otherwise results are undefined.

Parameters
----------
keycolumns    list of headers of the columns used as key for the join operation
)delim")
        .def("innerJoin",
             py::overload_cast<const DataFrame&, const DataFrame&, const std::string&>(
                 dataframe::leftJoin),
             py::arg("left"), py::arg("right"), py::arg("keycolumn") = "index",
             R"delim(
Create a new DataFrame by using an outer left join of DataFrame left and DataFrame right.
That is all rows of left are augmented with matching rows from right.

It is assumed that the entries in the key columns of right are unique. Otherwise results
are undefined.

Parameters
----------
keycolumn    header of the column used as key for the join operation (default: index column)
)delim")
        .def("leftJoin",
             py::overload_cast<const DataFrame&, const DataFrame&, const std::vector<std::string>&>(
                 dataframe::leftJoin),
             py::arg("left"), py::arg("right"), py::arg("keycolumns"),
             R"delim(
Create a new DataFrame by using an outer left join of DataFrame left and DataFrame right.
That is all rows of left are augmented with matching rows from right.

It is assumed that the entries in the key columns of right are unique. Otherwise results
are undefined.

Parameters
----------
keycolumns    list of headers of the columns used as key for the join operation
)delim");

    exposeStandardDataPorts<DataFrame>(m, "DataFrame");
}

}  // namespace inviwo
<commit_msg>DataFramePython: updated python interface<commit_after>/*********************************************************************************
 *
 * Inviwo - Interactive Visualization Workshop
 *
 * Copyright (c) 2019-2021 Inviwo Foundation
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *********************************************************************************/

#include <ivwdataframe/pydataframe.h>

#include <warn/push>
#include <warn/ignore/shadow>
#include <pybind11/functional.h>
#include <pybind11/stl.h>
#include <pybind11/stl_bind.h>
#include <warn/pop>

#include <inviwo/dataframe/datastructures/column.h>
#include <inviwo/dataframe/datastructures/dataframe.h>
#include <inviwo/dataframe/datastructures/datapoint.h>
#include <inviwo/dataframe/util/dataframeutil.h>

#include <inviwo/core/util/defaultvalues.h>
#include <inviwo/core/util/safecstr.h>
#include <inviwo/core/datastructures/buffer/buffer.h>
#include <modules/python3/pyportutils.h>

#include <fmt/format.h>

namespace py = pybind11;

namespace inviwo {

namespace {

struct DataFrameAddColumnReg {
    template <typename T>
    auto operator()(py::class_<DataFrame, std::shared_ptr<DataFrame>>& d) {
        auto classname = Defaultvalues<T>::getName();

        d.def(
            fmt::format("add{}Column", classname).c_str(),
            [](DataFrame& d, const std::string& header, const size_t size = 0) {
                return d.addColumn<T>(header, size);
            },
            py::arg("header"), py::arg("size") = 0);

        d.def(
            fmt::format("add{}Column", classname).c_str(),
            [](DataFrame& d, std::string header, std::vector<T> data) {
                return d.addColumn(std::move(header), std::move(data));
            },
            py::arg("header"), py::arg("data"));
    }
};

struct DataPointReg {
    template <typename T>
    auto operator()(pybind11::module& m) {
        using D = DataPoint<T>;
        auto classname = Defaultvalues<T>::getName() + "DataPoint";

        py::class_<D, DataPointBase, std::shared_ptr<D>> data(m, classname.c_str());
        data.def_property_readonly("data", &D::getData)
            .def_property_readonly("str", &D::toString)
            .def("__repr__",
                 [classname](D& p) { return fmt::format("<{}: '{}'>", classname, p.toString()); });
    }
};

struct TemplateColumnReg {
    template <typename T>
    auto operator()(pybind11::module& m) {
        using C = TemplateColumn<T>;
        auto classname = Defaultvalues<T>::getName() + "Column";

        py::class_<C, Column, std::shared_ptr<C>> col(m, classname.c_str());
        col.def_property_readonly("buffer", [](C& c) { return c.getTypedBuffer(); })
            .def(py::init<std::string_view>())
            .def("add", py::overload_cast<const T&>(&C::add))
            .def("add", py::overload_cast<std::string_view>(&C::add))
            .def("append", [](C& c, C& src) { c.append(src); })
            .def("set", &C::set)
            .def(
                "get",
                [](const C& c, size_t i) {
                    if (i >= c.getSize()) throw py::index_error();
                    return c.get(i);
                },
                py::arg("i"))
            .def(
                "get",
                [](const C& c, size_t i, bool asString) {
                    if (i >= c.getSize()) throw py::index_error();
                    return c.get(i, asString);
                },
                py::arg("i"), py::arg("asString"))
            .def("__repr__", [classname](C& c) {
                return fmt::format("<{}: '{}', {}, {}>", classname, c.getHeader(), c.getSize(),
                                   c.getBuffer()->getDataFormat()->getString());
            });
    }
};

}  // namespace

void exposeDataFrame(pybind11::module& m) {
    py::class_<DataPointBase, std::shared_ptr<DataPointBase>>(m, "DataPointBase")
        .def("__repr__",
             [](DataPointBase& p) { return fmt::format("<DataPoint: '{}'>", p.toString()); });

    py::enum_<ColumnType>(m, "ColumnType")
        .value("Index", ColumnType::Index)
        .value("Ordinal", ColumnType::Ordinal)
        .value("Categorical", ColumnType::Categorical);

    py::class_<Column, std::shared_ptr<Column>>(m, "Column")
        .def_property("header", &Column::getHeader, &Column::setHeader)
        .def_property_readonly("buffer", [](Column& self) { return self.getBuffer(); })
        .def_property_readonly("size", &Column::getSize)
        .def_property_readonly("type", &Column::getColumnType)
        .def(
            "setRange",
            [](Column& c, std::optional<dvec2> range) {
                if (range) {
                    c.setRange(*range);
                } else {
                    c.unsetRange();
                }
            },
            py::arg("range"))
        .def_property_readonly("range", &Column::getRange)
        .def("__repr__", [](Column& c) {
            return fmt::format("<Column: '{}', {}, {}>", c.getHeader(), c.getSize(),
                               c.getBuffer()->getDataFormat()->getString());
        });

    using Scalars = std::tuple<float, double, int, glm::i64, size_t, std::uint32_t>;
    util::for_each_type<Scalars>{}(DataPointReg{}, m);
    util::for_each_type<Scalars>{}(TemplateColumnReg{}, m);

    py::class_<CategoricalColumn, TemplateColumn<std::uint32_t>,
               std::shared_ptr<CategoricalColumn>>(m, "CategoricalColumn")
        .def(py::init<std::string_view>())
        .def_property_readonly("categories", &CategoricalColumn::getCategories,
                               py::return_value_policy::copy)
        .def("add", [](CategoricalColumn& c, const std::string& str) { c.add(str); })
        .def("append", [](CategoricalColumn& c, CategoricalColumn& src) { c.append(src); })
        .def("set", [](CategoricalColumn& c, size_t idx, const std::uint32_t& v) { c.set(idx, v); })
        .def("set", py::overload_cast<size_t, const std::string&>(&CategoricalColumn::set))
        .def(
            "get",
            [](const CategoricalColumn& c, size_t i, bool asString) {
                if (i >= c.getSize()) throw py::index_error();
                return c.get(i, asString);
            },
            py::arg("i"), py::arg("asString") = true)
        .def("__repr__", [](CategoricalColumn& c) {
            return fmt::format("<CategoricalColumn: '{}', {}, {} categories>", c.getHeader(),
                               c.getSize(), c.getCategories().size());
        });

    py::class_<IndexColumn, TemplateColumn<std::uint32_t>, std::shared_ptr<IndexColumn>>(
        m, "IndexColumn")
        .def(py::init<std::string_view>());

    py::class_<DataFrame, std::shared_ptr<DataFrame>> dataframe(m, "DataFrame");
    dataframe.def(py::init<std::uint32_t>(), py::arg("size") = 0)
        .def_property_readonly("cols", &DataFrame::getNumberOfColumns)
        .def_property_readonly("rows", &DataFrame::getNumberOfRows)
        .def("indexcol", [](DataFrame& d) { return d.getIndexColumn(); })
        .def("column", [](DataFrame& self, size_t index) { return self.getColumn(index); })
        .def("addColumnFromBuffer", &DataFrame::addColumnFromBuffer)
        .def("addCategoricalColumn",
             py::overload_cast<std::string_view, size_t>(&DataFrame::addCategoricalColumn),
             py::arg("header"), py::arg("size") = 0)
        .def("addCategoricalColumn",
             py::overload_cast<std::string_view, const std::vector<std::string>&>(
                 &DataFrame::addCategoricalColumn),
             py::arg("header"), py::arg("values"))
        .def("getRow", &DataFrame::getDataItem, py::arg("index"), py::arg("asString") = false)

        .def("updateIndex", [](DataFrame& d) { d.updateIndexBuffer(); })

        // interface for operator[]
        .def(
            "__getitem__",
            [](const DataFrame& d, size_t i) {
                if (i >= d.getNumberOfColumns()) throw py::index_error();
                return *(d.begin() + i);
            },
            py::return_value_policy::reference_internal)
        // sequence protocol operations
        .def(
            "__iter__", [](const DataFrame& d) { return py::make_iterator(d.begin(), d.end()); },
            py::keep_alive<0, 1>() /* Essential: keep object alive while iterator exists */)

        .def("__repr__", [](const DataFrame& d) {
            std::string str = fmt::format("<DataFrame: {} column(s), {} rows",
                                          d.getNumberOfColumns(), d.getNumberOfRows());
            size_t i = 0;
            for (auto c : d) {
                ++i;
                str += fmt::format("\n   {:>3}: '{}', {}, {}", i, c->getHeader(), c->getSize(),
                                   c->getBuffer()->getDataFormat()->getString());
            }
            return str + ">";
        });

    util::for_each_type<Scalars>{}(DataFrameAddColumnReg{}, dataframe);

    m.def("createDataFrame", createDataFrame, py::arg("exampleRows"),
          py::arg("colheaders") = std::vector<std::string>{}, py::arg("doubleprecision") = false,
          R"delim(
Create a new DataFrame by guessing the column types from a number of rows.

Parameters
----------
exampleRows     Rows for guessing data type of each column.
colHeaders      Name of each column. If none are given, "Column 1", "Column 2", ... is used
doubleprecision If true, columns with floating point values will use double for storage
)delim")
        .def("appendColumns", dataframe::appendColumns, py::arg("left"), py::arg("right"),
             py::arg("ignoreduplicates") = false, py::arg("fillmissingrows") = false,
             R"delim(
Create a new DataFrame by appending the columns of DataFrame right to DataFrame left

Parameters
----------
ignoreduplicates   duplicate columns, i.e. same column header, are ignored if true
fillmissingrows    if true, missing rows in either DataFrame are filled with 0 or
                   "undefined" (for categorical columns)
)delim")
        .def("appendRows", dataframe::appendRows, py::arg("top"), py::arg("bottom"),
             py::arg("matchbyname") = false,
             R"delim(
Create a new DataFrame by appending the rows of DataFrame bottom to DataFrame top

Parameters
----------
matchByName    if true, column headers are used for matching columns. Otherwise columns
               are matched by order (default)
)delim")
        .def("innerJoin",
             py::overload_cast<const DataFrame&, const DataFrame&, const std::string&>(
                 dataframe::innerJoin),
             py::arg("left"), py::arg("right"), py::arg("keycolumn") = "index",
             R"delim(
Create a new DataFrame by using an inner join of DataFrame left and DataFrame right.
That is only rows with matching keys are kept.

It is assumed that the entries in the key columns are unique. Otherwise results are undefined.

Parameters
----------
keycolumn    header of the column used as key for the join operation (default: index column)
)delim")
        .def("innerJoin",
             py::overload_cast<const DataFrame&, const DataFrame&, const std::vector<std::string>&>(
                 dataframe::innerJoin),
             py::arg("left"), py::arg("right"), py::arg("keycolumns"),
             R"delim(
Create a new DataFrame by using an inner join of DataFrame left and DataFrame right.
That is only rows with matching all keys are kept.

It is assumed that the entries in the key columns are unique. Otherwise results are undefined.

Parameters
----------
keycolumns    list of headers of the columns used as key for the join operation
)delim")
        .def("innerJoin",
             py::overload_cast<const DataFrame&, const DataFrame&, const std::string&>(
                 dataframe::leftJoin),
             py::arg("left"), py::arg("right"), py::arg("keycolumn") = "index",
             R"delim(
Create a new DataFrame by using an outer left join of DataFrame left and DataFrame right.
That is all rows of left are augmented with matching rows from right.

It is assumed that the entries in the key columns of right are unique. Otherwise results
are undefined.

Parameters
----------
keycolumn    header of the column used as key for the join operation (default: index column)
)delim")
        .def("leftJoin",
             py::overload_cast<const DataFrame&, const DataFrame&, const std::vector<std::string>&>(
                 dataframe::leftJoin),
             py::arg("left"), py::arg("right"), py::arg("keycolumns"),
             R"delim(
Create a new DataFrame by using an outer left join of DataFrame left and DataFrame right.
That is all rows of left are augmented with matching rows from right.

It is assumed that the entries in the key columns of right are unique. Otherwise results
are undefined.

Parameters
----------
keycolumns    list of headers of the columns used as key for the join operation
)delim");

    exposeStandardDataPorts<DataFrame>(m, "DataFrame");
}

}  // namespace inviwo
<|endoftext|>