<commit_before>
#include "PreCompiled.h"

#include <Base/VectorPy.h>
#include <Base/GeometryPyCXX.h>

#include <App/DocumentObjectPy.h>

#include "Mod/Fem/Gui/ViewProviderFemMesh.h"
#include "Mod/Fem/App/FemResultVector.h"
#include "Mod/Fem/App/FemResultValue.h"

// inclusion of the generated files (generated out of ViewProviderFemMeshPy.xml)
#include "ViewProviderFemMeshPy.h"
#include "ViewProviderFemMeshPy.cpp"

using namespace FemGui;

// returns a string which represents the object e.g. when printed in python
std::string ViewProviderFemMeshPy::representation(void) const
{
    return std::string("<ViewProviderFemMesh object>");
}

PyObject* ViewProviderFemMeshPy::animate(PyObject * args)
{
    double factor;
    if (!PyArg_ParseTuple(args, "d", &factor))
        return 0;

    this->getViewProviderFemMeshPtr()->animateNodes(factor);

    Py_Return;
}

App::Color calcColor(double value,double min, double max)
{
    if (max < 0) max = 0;
    if (min > 0) min = 0;

    if (value < min)
        return App::Color (0.0,0.0,1.0);
    if (value > max)
        return App::Color (1.0,0.0,0.0);
    if ( value > max/2.0 )
        return App::Color (1.0,1-((value-(max/2.0)) / (max/2.0)),0.0);
    if ( value > 0.0 )
        return App::Color (value/(max/2.0),1.0,0.0) ;
    if ( value < min/2.0 )
        return App::Color (0.0,1-((value-(min/2.0)) / (min/2.0)),1.0);
    if ( value < 0.0 )
        return App::Color (0.0,1.0,value/(min/2.0)) ;
}

PyObject* ViewProviderFemMeshPy::setNodeColorByResult(PyObject *args)
{
    PyObject *object=0;
    int type = 0;
    if (PyArg_ParseTuple(args,"O!|i",&(App::DocumentObjectPy::Type), &object, &type)) {
        App::DocumentObject* obj = static_cast<App::DocumentObjectPy*>(object)->getDocumentObjectPtr();
        if (obj && obj->getTypeId().isDerivedFrom(Fem::FemResultValue::getClassTypeId())){
            Fem::FemResultValue *result = static_cast<Fem::FemResultValue*>(obj);
            const std::vector<long> & Ids = result->ElementNumbers.getValues() ;
            const std::vector<double> & Vals = result->Values.getValues() ;
            std::vector<App::Color> NodeColors(Vals.size());
            float max = 0.0;
            for(std::vector<double>::const_iterator it= Vals.begin();it!=Vals.end();++it)
                if(*it > max)
                    max = *it;

            // fill up color vector
            long i=0;
            for(std::vector<double>::const_iterator it= Vals.begin();it!=Vals.end();++it,i++)
                NodeColors[i] = calcColor(*it,0.0,max);

            // set the color to the view-provider
            this->getViewProviderFemMeshPtr()->setColorByNodeId(Ids,NodeColors);

        }else if (obj && obj->getTypeId().isDerivedFrom(Fem::FemResultVector::getClassTypeId())){
            Fem::FemResultVector *result = static_cast<Fem::FemResultVector*>(obj);
            const std::vector<long> & Ids = result->ElementNumbers.getValues() ;
            const std::vector<Base::Vector3d> & Vecs = result->Values.getValues() ;
            std::vector<App::Color> NodeColors(Vecs.size());
            double max = -1e12;
            double min = +1e12;
            for(std::vector<Base::Vector3d>::const_iterator it= Vecs.begin();it!=Vecs.end();++it){
                double val;
                if(type == 0)
                    val = it->Length();
                else if (type == 1)
                    val = it->x;
                else if (type == 2)
                    val = it->y;
                else if (type == 3)
                    val = it->z;
                else
                    val = it->Length();

                if(val > max)
                    max = val;
                if(val < min)
                    min = val;
            }
            // fill up color vector
            long i=0;
            for(std::vector<Base::Vector3d>::const_iterator it= Vecs.begin();it!=Vecs.end();++it,i++)
                if(type == 0)
                    NodeColors[i] = calcColor(it->Length(),0.0,max);
                else if (type == 1)
                    NodeColors[i] = calcColor(it->x,0.0,max);
                else if (type == 2)
                    NodeColors[i] = calcColor(it->y,0.0,max);
                else if (type == 3)
                    NodeColors[i] = calcColor(it->z,0.0,max);
                else
                    NodeColors[i] = calcColor(it->Length(),0.0,max);

            // set the color to the view-provider
            this->getViewProviderFemMeshPtr()->setColorByNodeId(Ids,NodeColors);

        }else{
            PyErr_SetString(PyExc_Exception, "Argument has to be a ResultValue or ResultVector!");
            return 0;
        }
    }

    Py_Return;

}

PyObject* ViewProviderFemMeshPy::setNodeDisplacementByResult(PyObject *args)
{
    PyObject *object=0;
    if (PyArg_ParseTuple(args,"O!",&(App::DocumentObjectPy::Type), &object)) {
        App::DocumentObject* obj = static_cast<App::DocumentObjectPy*>(object)->getDocumentObjectPtr();
        if (obj && obj->getTypeId().isDerivedFrom(Fem::FemResultVector::getClassTypeId())){
            Fem::FemResultVector *result = static_cast<Fem::FemResultVector*>(obj);
            const std::vector<long> & Ids = result->ElementNumbers.getValues() ;
            const std::vector<Base::Vector3d> & Vecs = result->Values.getValues() ;
            // set the displacement to the view-provider
            this->getViewProviderFemMeshPtr()->setDisplacementByNodeId(Ids,Vecs);

        }else{
            PyErr_SetString(PyExc_Exception, "Argument has to be a ResultVector!");
            return 0;
        }
    }

    Py_Return;

}
Py::Dict ViewProviderFemMeshPy::getNodeColor(void) const
{
    //return Py::List();
    throw Py::AttributeError("Not yet implemented");
}

void ViewProviderFemMeshPy::setNodeColor(Py::Dict arg)
{
    long size = arg.size();
    if(size == 0)
        this->getViewProviderFemMeshPtr()->resetColorByNodeId();
    else {
        Base::TimeInfo Start;
        Base::Console().Log("Start: ViewProviderFemMeshPy::setNodeColor() =================================\n");
        //std::map<long,App::Color> NodeColorMap;

        //for( Py::Dict::iterator it = arg.begin(); it!= arg.end();++it){
        //    Py::Int id((*it).first);
        //    Py::Tuple color((*it).second);
        //    NodeColorMap[id] = App::Color(Py::Float(color[0]),Py::Float(color[1]),Py::Float(color[2]),0);
        //}
        std::vector<long> NodeIds(size);
        std::vector<App::Color> NodeColors(size);

        long i = 0;
        for( Py::Dict::iterator it = arg.begin(); it!= arg.end();++it,i++){
            Py::Int id((*it).first);
            Py::Tuple color((*it).second);
            NodeIds[i]    = id;
            NodeColors[i] = App::Color(Py::Float(color[0]),Py::Float(color[1]),Py::Float(color[2]),0);
        }
        Base::Console().Log("    %f: Start ViewProviderFemMeshPy::setNodeColor() call \n",Base::TimeInfo::diffTimeF(Start,Base::TimeInfo()));

        //this->getViewProviderFemMeshPtr()->setColorByNodeId(NodeColorMap);
        this->getViewProviderFemMeshPtr()->setColorByNodeId(NodeIds,NodeColors);
        Base::Console().Log("    %f: Finish ViewProviderFemMeshPy::setNodeColor() call \n",Base::TimeInfo::diffTimeF(Start,Base::TimeInfo()));
	}
}

Py::Dict ViewProviderFemMeshPy::getElementColor(void) const
{
    //return Py::List();
    throw Py::AttributeError("Not yet implemented");
}

void ViewProviderFemMeshPy::setElementColor(Py::Dict arg)
{
    if(arg.size() == 0)
        this->getViewProviderFemMeshPtr()->resetColorByNodeId();
    else {
        std::map<long,App::Color> NodeColorMap;

        for( Py::Dict::iterator it = arg.begin(); it!= arg.end();++it){
            Py::Int id((*it).first);
            Py::Tuple color((*it).second);
            NodeColorMap[id] = App::Color(Py::Float(color[0]),Py::Float(color[1]),Py::Float(color[2]),0);
        }
        this->getViewProviderFemMeshPtr()->setColorByElementId(NodeColorMap);
	}
}

Py::Dict ViewProviderFemMeshPy::getNodeDisplacement(void) const
{
    //return Py::Dict();
    throw Py::AttributeError("Not yet implemented");
}

void  ViewProviderFemMeshPy::setNodeDisplacement(Py::Dict arg)
{
    if(arg.size() == 0)
        this->getViewProviderFemMeshPtr()->resetColorByNodeId();
    else {
        std::map<long,Base::Vector3d> NodeDispMap;
        union PyType_Object pyType = {&(Base::VectorPy::Type)};
        Py::Type vType(pyType.o);

        for( Py::Dict::iterator it = arg.begin(); it!= arg.end();++it){
            Py::Int id((*it).first);
            if ((*it).second.isType(vType)) {
                Py::Vector p((*it).second);
                NodeDispMap[id] = p.toVector();
            }
        }
        this->getViewProviderFemMeshPtr()->setDisplacementByNodeId(NodeDispMap);
	}
}

Py::List ViewProviderFemMeshPy::getHighlightedNodes(void) const
{
    //return Py::List();
    throw Py::AttributeError("Not yet implemented");
}

void  ViewProviderFemMeshPy::setHighlightedNodes(Py::List arg)
{
    std::set<long> res;

    for( Py::List::iterator it = arg.begin(); it!= arg.end();++it){
        Py::Int id(*it);
        if(id)
            res.insert(id);
    }
    this->getViewProviderFemMeshPtr()->setHighlightNodes(res);
}

Py::List ViewProviderFemMeshPy::getVisibleElementFaces(void) const
{
    const std::vector<unsigned long> & visElmFc = this->getViewProviderFemMeshPtr()->getVisibleElementFaces();
    std::vector<unsigned long> trans;

    // sorting out double faces through higer order elements and null entries
    long elementOld =0, faceOld=0;
    for (std::vector<unsigned long>::const_iterator it = visElmFc.begin();it!=visElmFc.end();++it){
        if(*it == 0)
            continue;

        long element = *it>>3;
        long face    = (*it&7)+1;
        if(element == elementOld && face==faceOld)
            continue;

        trans.push_back(*it);
        elementOld = element;
        faceOld    = face;
    }

    Py::List result( trans.size() );
    int i = 0;
    for (std::vector<unsigned long>::const_iterator it = trans.begin();it!=trans.end();++it,i++){
        Py::Tuple tup(2);
        long element = *it>>3;
        long face    = (*it&7)+1;
        tup.setItem( 0,Py::Int( element ) );
        tup.setItem( 1,Py::Int( face ) );
        result.setItem(i,tup);
    }

    return result;
}

PyObject *ViewProviderFemMeshPy::getCustomAttributes(const char* /*attr*/) const
{
    return 0;
}

int ViewProviderFemMeshPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/)
{
    return 0;
}

<commit_msg>Fix in color calculation<commit_after>
#include "PreCompiled.h"

#include <Base/VectorPy.h>
#include <Base/GeometryPyCXX.h>

#include <App/DocumentObjectPy.h>

#include "Mod/Fem/Gui/ViewProviderFemMesh.h"
#include "Mod/Fem/App/FemResultVector.h"
#include "Mod/Fem/App/FemResultValue.h"

// inclusion of the generated files (generated out of ViewProviderFemMeshPy.xml)
#include "ViewProviderFemMeshPy.h"
#include "ViewProviderFemMeshPy.cpp"

using namespace FemGui;

// returns a string which represents the object e.g. when printed in python
std::string ViewProviderFemMeshPy::representation(void) const
{
    return std::string("<ViewProviderFemMesh object>");
}

PyObject* ViewProviderFemMeshPy::animate(PyObject * args)
{
    double factor;
    if (!PyArg_ParseTuple(args, "d", &factor))
        return 0;

    this->getViewProviderFemMeshPtr()->animateNodes(factor);

    Py_Return;
}

App::Color calcColor(double value,double min, double max)
{
    if (max < 0) max = 0;
    if (min > 0) min = 0;

    if (value < min)
        return App::Color (0.0,0.0,1.0);
    if (value > max)
        return App::Color (1.0,0.0,0.0);
    if (value == 0.0)
        return App::Color (0.0,1.0,0.0);
    if ( value > max/2.0 )
        return App::Color (1.0,1-((value-(max/2.0)) / (max/2.0)),0.0);
    if ( value > 0.0 )
        return App::Color (value/(max/2.0),1.0,0.0) ;
    if ( value < min/2.0 )
        return App::Color (0.0,1-((value-(min/2.0)) / (min/2.0)),1.0);
    if ( value < 0.0 )
        return App::Color (0.0,1.0,value/(min/2.0)) ;
}

PyObject* ViewProviderFemMeshPy::setNodeColorByResult(PyObject *args)
{
    PyObject *object=0;
    int type = 0;
    if (PyArg_ParseTuple(args,"O!|i",&(App::DocumentObjectPy::Type), &object, &type)) {
        App::DocumentObject* obj = static_cast<App::DocumentObjectPy*>(object)->getDocumentObjectPtr();
        if (obj && obj->getTypeId().isDerivedFrom(Fem::FemResultValue::getClassTypeId())){
            Fem::FemResultValue *result = static_cast<Fem::FemResultValue*>(obj);
            const std::vector<long> & Ids = result->ElementNumbers.getValues() ;
            const std::vector<double> & Vals = result->Values.getValues() ;
            std::vector<App::Color> NodeColors(Vals.size());
            float max = 0.0;
            for(std::vector<double>::const_iterator it= Vals.begin();it!=Vals.end();++it)
                if(*it > max)
                    max = *it;

            // fill up color vector
            long i=0;
            for(std::vector<double>::const_iterator it= Vals.begin();it!=Vals.end();++it,i++)
                NodeColors[i] = calcColor(*it,0.0,max);

            // set the color to the view-provider
            this->getViewProviderFemMeshPtr()->setColorByNodeId(Ids,NodeColors);

        }else if (obj && obj->getTypeId().isDerivedFrom(Fem::FemResultVector::getClassTypeId())){
            Fem::FemResultVector *result = static_cast<Fem::FemResultVector*>(obj);
            const std::vector<long> & Ids = result->ElementNumbers.getValues() ;
            const std::vector<Base::Vector3d> & Vecs = result->Values.getValues() ;
            std::vector<App::Color> NodeColors(Vecs.size());
            double max = -1e12;
            double min = +1e12;
            for(std::vector<Base::Vector3d>::const_iterator it= Vecs.begin();it!=Vecs.end();++it){
                double val;
                if(type == 0)
                    val = it->Length();
                else if (type == 1)
                    val = it->x;
                else if (type == 2)
                    val = it->y;
                else if (type == 3)
                    val = it->z;
                else
                    val = it->Length();

                if(val > max)
                    max = val;
                if(val < min)
                    min = val;
            }
            // fill up color vector
            long i=0;
            for(std::vector<Base::Vector3d>::const_iterator it= Vecs.begin();it!=Vecs.end();++it,i++)
                if(type == 0)
                    NodeColors[i] = calcColor(it->Length(),0.0,max);
                else if (type == 1)
                    NodeColors[i] = calcColor(it->x,0.0,max);
                else if (type == 2)
                    NodeColors[i] = calcColor(it->y,0.0,max);
                else if (type == 3)
                    NodeColors[i] = calcColor(it->z,0.0,max);
                else
                    NodeColors[i] = calcColor(it->Length(),0.0,max);

            // set the color to the view-provider
            this->getViewProviderFemMeshPtr()->setColorByNodeId(Ids,NodeColors);

        }else{
            PyErr_SetString(PyExc_Exception, "Argument has to be a ResultValue or ResultVector!");
            return 0;
        }
    }

    Py_Return;

}

PyObject* ViewProviderFemMeshPy::setNodeDisplacementByResult(PyObject *args)
{
    PyObject *object=0;
    if (PyArg_ParseTuple(args,"O!",&(App::DocumentObjectPy::Type), &object)) {
        App::DocumentObject* obj = static_cast<App::DocumentObjectPy*>(object)->getDocumentObjectPtr();
        if (obj && obj->getTypeId().isDerivedFrom(Fem::FemResultVector::getClassTypeId())){
            Fem::FemResultVector *result = static_cast<Fem::FemResultVector*>(obj);
            const std::vector<long> & Ids = result->ElementNumbers.getValues() ;
            const std::vector<Base::Vector3d> & Vecs = result->Values.getValues() ;
            // set the displacement to the view-provider
            this->getViewProviderFemMeshPtr()->setDisplacementByNodeId(Ids,Vecs);

        }else{
            PyErr_SetString(PyExc_Exception, "Argument has to be a ResultVector!");
            return 0;
        }
    }

    Py_Return;

}
Py::Dict ViewProviderFemMeshPy::getNodeColor(void) const
{
    //return Py::List();
    throw Py::AttributeError("Not yet implemented");
}

void ViewProviderFemMeshPy::setNodeColor(Py::Dict arg)
{
    long size = arg.size();
    if(size == 0)
        this->getViewProviderFemMeshPtr()->resetColorByNodeId();
    else {
        Base::TimeInfo Start;
        Base::Console().Log("Start: ViewProviderFemMeshPy::setNodeColor() =================================\n");
        //std::map<long,App::Color> NodeColorMap;

        //for( Py::Dict::iterator it = arg.begin(); it!= arg.end();++it){
        //    Py::Int id((*it).first);
        //    Py::Tuple color((*it).second);
        //    NodeColorMap[id] = App::Color(Py::Float(color[0]),Py::Float(color[1]),Py::Float(color[2]),0);
        //}
        std::vector<long> NodeIds(size);
        std::vector<App::Color> NodeColors(size);

        long i = 0;
        for( Py::Dict::iterator it = arg.begin(); it!= arg.end();++it,i++){
            Py::Int id((*it).first);
            Py::Tuple color((*it).second);
            NodeIds[i]    = id;
            NodeColors[i] = App::Color(Py::Float(color[0]),Py::Float(color[1]),Py::Float(color[2]),0);
        }
        Base::Console().Log("    %f: Start ViewProviderFemMeshPy::setNodeColor() call \n",Base::TimeInfo::diffTimeF(Start,Base::TimeInfo()));

        //this->getViewProviderFemMeshPtr()->setColorByNodeId(NodeColorMap);
        this->getViewProviderFemMeshPtr()->setColorByNodeId(NodeIds,NodeColors);
        Base::Console().Log("    %f: Finish ViewProviderFemMeshPy::setNodeColor() call \n",Base::TimeInfo::diffTimeF(Start,Base::TimeInfo()));
	}
}

Py::Dict ViewProviderFemMeshPy::getElementColor(void) const
{
    //return Py::List();
    throw Py::AttributeError("Not yet implemented");
}

void ViewProviderFemMeshPy::setElementColor(Py::Dict arg)
{
    if(arg.size() == 0)
        this->getViewProviderFemMeshPtr()->resetColorByNodeId();
    else {
        std::map<long,App::Color> NodeColorMap;

        for( Py::Dict::iterator it = arg.begin(); it!= arg.end();++it){
            Py::Int id((*it).first);
            Py::Tuple color((*it).second);
            NodeColorMap[id] = App::Color(Py::Float(color[0]),Py::Float(color[1]),Py::Float(color[2]),0);
        }
        this->getViewProviderFemMeshPtr()->setColorByElementId(NodeColorMap);
	}
}

Py::Dict ViewProviderFemMeshPy::getNodeDisplacement(void) const
{
    //return Py::Dict();
    throw Py::AttributeError("Not yet implemented");
}

void  ViewProviderFemMeshPy::setNodeDisplacement(Py::Dict arg)
{
    if(arg.size() == 0)
        this->getViewProviderFemMeshPtr()->resetColorByNodeId();
    else {
        std::map<long,Base::Vector3d> NodeDispMap;
        union PyType_Object pyType = {&(Base::VectorPy::Type)};
        Py::Type vType(pyType.o);

        for( Py::Dict::iterator it = arg.begin(); it!= arg.end();++it){
            Py::Int id((*it).first);
            if ((*it).second.isType(vType)) {
                Py::Vector p((*it).second);
                NodeDispMap[id] = p.toVector();
            }
        }
        this->getViewProviderFemMeshPtr()->setDisplacementByNodeId(NodeDispMap);
	}
}

Py::List ViewProviderFemMeshPy::getHighlightedNodes(void) const
{
    //return Py::List();
    throw Py::AttributeError("Not yet implemented");
}

void  ViewProviderFemMeshPy::setHighlightedNodes(Py::List arg)
{
    std::set<long> res;

    for( Py::List::iterator it = arg.begin(); it!= arg.end();++it){
        Py::Int id(*it);
        if(id)
            res.insert(id);
    }
    this->getViewProviderFemMeshPtr()->setHighlightNodes(res);
}

Py::List ViewProviderFemMeshPy::getVisibleElementFaces(void) const
{
    const std::vector<unsigned long> & visElmFc = this->getViewProviderFemMeshPtr()->getVisibleElementFaces();
    std::vector<unsigned long> trans;

    // sorting out double faces through higer order elements and null entries
    long elementOld =0, faceOld=0;
    for (std::vector<unsigned long>::const_iterator it = visElmFc.begin();it!=visElmFc.end();++it){
        if(*it == 0)
            continue;

        long element = *it>>3;
        long face    = (*it&7)+1;
        if(element == elementOld && face==faceOld)
            continue;

        trans.push_back(*it);
        elementOld = element;
        faceOld    = face;
    }

    Py::List result( trans.size() );
    int i = 0;
    for (std::vector<unsigned long>::const_iterator it = trans.begin();it!=trans.end();++it,i++){
        Py::Tuple tup(2);
        long element = *it>>3;
        long face    = (*it&7)+1;
        tup.setItem( 0,Py::Int( element ) );
        tup.setItem( 1,Py::Int( face ) );
        result.setItem(i,tup);
    }

    return result;
}

PyObject *ViewProviderFemMeshPy::getCustomAttributes(const char* /*attr*/) const
{
    return 0;
}

int ViewProviderFemMeshPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/)
{
    return 0;
}

<|endoftext|>


<commit_before>/*===========================================================================*\
 *                                                                           *
 *                               OpenMesh                                    *
 *      Copyright (C) 2001-2011 by Computer Graphics Group, RWTH Aachen      *
 *                           www.openmesh.org                                *
 *                                                                           *
 *---------------------------------------------------------------------------*
 *  This file is part of OpenMesh.                                           *
 *                                                                           *
 *  OpenMesh 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 3 of           *
 *  the License, or (at your option) any later version with the              *
 *  following exceptions:                                                    *
 *                                                                           *
 *  If other files instantiate templates or use macros                       *
 *  or inline functions from this file, or you compile this file and         *
 *  link it with other files to produce an executable, this file does        *
 *  not by itself cause the resulting executable to be covered by the        *
 *  GNU Lesser General Public License. This exception does not however       *
 *  invalidate any other reasons why the executable file might be            *
 *  covered by the GNU Lesser General Public License.                        *
 *                                                                           *
 *  OpenMesh 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 LesserGeneral Public          *
 *  License along with OpenMesh.  If not,                                    *
 *  see <http://www.gnu.org/licenses/>.                                      *
 *                                                                           *
 \*===========================================================================*/

/*===========================================================================*\
 *                                                                           *
 *   $Revision: 460 $                                                         *
 *   $Date: 2011-11-16 10:45:08 +0100 (Mi, 16 Nov 2011) $                   *
 *                                                                           *
 \*===========================================================================*/

/** \file McDecimaterT.cc
 */

//=============================================================================
//
//  CLASS McDecimaterT - IMPLEMENTATION
//
//=============================================================================
#define OPENMESH_MULTIPLE_CHOICE_DECIMATER_DECIMATERT_CC

//== INCLUDES =================================================================

#include <OpenMesh/Tools/Decimater/McDecimaterT.hh>

#include <vector>
#if defined(OM_CC_MIPS)
#  include <float.h>
#else
#  include <cfloat>
#endif

//== NAMESPACE ===============================================================

namespace OpenMesh {
namespace Decimater {

//== IMPLEMENTATION ==========================================================

template<class Mesh>
McDecimaterT<Mesh>::McDecimaterT(Mesh& _mesh) :
  BaseDecimaterT<Mesh>(_mesh),
    mesh_(_mesh), randomSamples_(10) {

  // default properties
  mesh_.request_vertex_status();
  mesh_.request_halfedge_status();
  mesh_.request_edge_status();
  mesh_.request_face_status();
  mesh_.request_face_normals();

}

//-----------------------------------------------------------------------------

template<class Mesh>
McDecimaterT<Mesh>::~McDecimaterT() {
  // default properties
  mesh_.release_vertex_status();
  mesh_.release_edge_status();
  mesh_.release_halfedge_status();
  mesh_.release_face_status();
  mesh_.release_face_normals();

}

//-----------------------------------------------------------------------------
template<class Mesh>
size_t McDecimaterT<Mesh>::decimate(size_t _n_collapses) {

  if (!this->is_initialized())
    return 0;

  unsigned int n_collapses(0);

  while ( n_collapses <  _n_collapses) {

    // Optimal id and value will be collected during the random sampling
    typename Mesh::HalfedgeHandle bestHandle(-1);
    double bestEnergy = FLT_MAX;

    // Generate random samples for collapses
    for ( unsigned int i = 0; i < randomSamples_; ++i) {

      // Random halfedge handle
      typename Mesh::HalfedgeHandle tmpHandle = typename Mesh::HalfedgeHandle((static_cast<double>(rand()) / RAND_MAX) * (mesh_.n_halfedges()-1) );

      // if it is not deleted, we analyse it
      if ( ! mesh_.status(tmpHandle).deleted()  ) {

        CollapseInfo ci(mesh_, tmpHandle);

        // Check if legal we analyze the priority of this collapse operation
        if (this->is_collapse_legal(ci)) {
          double energy = this->collapse_priority(ci);

          // Check if the current samples energy is better than any energy before
          if ( energy < bestEnergy ) {
            bestEnergy = energy;
            bestHandle = tmpHandle;
          }
        } else {
          continue;
        }
      }

    }

    // Found the best energy?
    if ( bestEnergy != FLT_MAX ) {

      // setup collapse info
      CollapseInfo ci(mesh_, bestHandle);

      // check topological correctness AGAIN !
      if (!this->is_collapse_legal(ci))
        continue;

      // pre-processing
      this->preprocess_collapse(ci);

      // perform collapse
      mesh_.collapse(bestHandle);
      ++n_collapses;

      // update triangle normals
      typename Mesh::VertexFaceIter vf_it = mesh_.vf_iter(ci.v1);
      for (; vf_it; ++vf_it)
        if (!mesh_.status(vf_it).deleted())
          mesh_.set_normal(vf_it, mesh_.calc_face_normal(vf_it.handle()));

      // post-process collapse
      this->postprocess_collapse(ci);

    }

  }

  // DON'T do garbage collection here! It's up to the application.
  return n_collapses;
}

//-----------------------------------------------------------------------------

template<class Mesh>
size_t McDecimaterT<Mesh>::decimate_to_faces(size_t _nv, size_t _nf) {
  if (!this->is_initialized())
    return 0;

  unsigned int nv = mesh_.n_vertices();
  unsigned int nf = mesh_.n_faces();
  unsigned int n_collapses(0);

  while ( (_nv < nv) && (_nf < nf) ) {

    // Optimal id and value will be collected during the random sampling
    typename Mesh::HalfedgeHandle bestHandle(-1);
    double bestEnergy = FLT_MAX;

    // Generate random samples for collapses
    for ( unsigned int i = 0; i < randomSamples_; ++i) {

      // Random halfedge handle
      typename Mesh::HalfedgeHandle tmpHandle = typename Mesh::HalfedgeHandle((static_cast<double>(rand()) / RAND_MAX) * (mesh_.n_halfedges()-1) );

      // if it is not deleted, we analyse it
      if ( ! mesh_.status(tmpHandle).deleted()  ) {

        CollapseInfo ci(mesh_, tmpHandle);

        // Check if legal we analyze the priority of this collapse operation
        if (this->is_collapse_legal(ci)) {
          double energy = this->collapse_priority(ci);

          // Check if the current samples energy is better than any energy before
          if ( energy < bestEnergy ) {
            bestEnergy = energy;
            bestHandle = tmpHandle;
          }
        } else {
          continue;
        }
      }

    }

    // Found the best energy?
    if ( bestEnergy != FLT_MAX ) {

      // setup collapse info
      CollapseInfo ci(mesh_, bestHandle);

      // check topological correctness AGAIN !
      if (!this->is_collapse_legal(ci))
        continue;

      // adjust complexity in advance (need boundary status)

      // One vertex is killed by the collapse
      --nv;

      // If we are at a boundary, one face is lost,
      // otherwise two
      if (mesh_.is_boundary(ci.v0v1) || mesh_.is_boundary(ci.v1v0))
        --nf;
      else
        nf -= 2;

      // pre-processing
      this->preprocess_collapse(ci);

      // perform collapse
      mesh_.collapse(bestHandle);
      ++n_collapses;

      // update triangle normals
      typename Mesh::VertexFaceIter vf_it = mesh_.vf_iter(ci.v1);
      for (; vf_it; ++vf_it)
        if (!mesh_.status(vf_it).deleted())
          mesh_.set_normal(vf_it, mesh_.calc_face_normal(vf_it.handle()));

      // post-process collapse
      this->postprocess_collapse(ci);

    }

  }

  // DON'T do garbage collection here! It's up to the application.
  return n_collapses;
}

//=============================================================================
}// END_NS_MC_DECIMATER
} // END_NS_OPENMESH
//=============================================================================

<commit_msg>added a stop criterion for the MC-Decimater, so that an endless loop is prevented from occuring if no vertex or face constraints are given<commit_after>/*===========================================================================*\
 *                                                                           *
 *                               OpenMesh                                    *
 *      Copyright (C) 2001-2011 by Computer Graphics Group, RWTH Aachen      *
 *                           www.openmesh.org                                *
 *                                                                           *
 *---------------------------------------------------------------------------*
 *  This file is part of OpenMesh.                                           *
 *                                                                           *
 *  OpenMesh 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 3 of           *
 *  the License, or (at your option) any later version with the              *
 *  following exceptions:                                                    *
 *                                                                           *
 *  If other files instantiate templates or use macros                       *
 *  or inline functions from this file, or you compile this file and         *
 *  link it with other files to produce an executable, this file does        *
 *  not by itself cause the resulting executable to be covered by the        *
 *  GNU Lesser General Public License. This exception does not however       *
 *  invalidate any other reasons why the executable file might be            *
 *  covered by the GNU Lesser General Public License.                        *
 *                                                                           *
 *  OpenMesh 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 LesserGeneral Public          *
 *  License along with OpenMesh.  If not,                                    *
 *  see <http://www.gnu.org/licenses/>.                                      *
 *                                                                           *
 \*===========================================================================*/

/*===========================================================================*\
 *                                                                           *
 *   $Revision: 460 $                                                         *
 *   $Date: 2011-11-16 10:45:08 +0100 (Mi, 16 Nov 2011) $                   *
 *                                                                           *
 \*===========================================================================*/

/** \file McDecimaterT.cc
 */

//=============================================================================
//
//  CLASS McDecimaterT - IMPLEMENTATION
//
//=============================================================================
#define OPENMESH_MULTIPLE_CHOICE_DECIMATER_DECIMATERT_CC

//== INCLUDES =================================================================

#include <OpenMesh/Tools/Decimater/McDecimaterT.hh>

#include <vector>
#if defined(OM_CC_MIPS)
#  include <float.h>
#else
#  include <cfloat>
#endif

//== NAMESPACE ===============================================================

namespace OpenMesh {
namespace Decimater {

//== IMPLEMENTATION ==========================================================

template<class Mesh>
McDecimaterT<Mesh>::McDecimaterT(Mesh& _mesh) :
  BaseDecimaterT<Mesh>(_mesh),
    mesh_(_mesh), randomSamples_(10) {

  // default properties
  mesh_.request_vertex_status();
  mesh_.request_halfedge_status();
  mesh_.request_edge_status();
  mesh_.request_face_status();
  mesh_.request_face_normals();

}

//-----------------------------------------------------------------------------

template<class Mesh>
McDecimaterT<Mesh>::~McDecimaterT() {
  // default properties
  mesh_.release_vertex_status();
  mesh_.release_edge_status();
  mesh_.release_halfedge_status();
  mesh_.release_face_status();
  mesh_.release_face_normals();

}

//-----------------------------------------------------------------------------
template<class Mesh>
size_t McDecimaterT<Mesh>::decimate(size_t _n_collapses) {

  if (!this->is_initialized())
    return 0;

  unsigned int n_collapses(0);

  while ( n_collapses <  _n_collapses) {

    // Optimal id and value will be collected during the random sampling
    typename Mesh::HalfedgeHandle bestHandle(-1);
    double bestEnergy = FLT_MAX;

    // Generate random samples for collapses
    for ( unsigned int i = 0; i < randomSamples_; ++i) {

      // Random halfedge handle
      typename Mesh::HalfedgeHandle tmpHandle = typename Mesh::HalfedgeHandle((static_cast<double>(rand()) / RAND_MAX) * (mesh_.n_halfedges()-1) );

      // if it is not deleted, we analyse it
      if ( ! mesh_.status(tmpHandle).deleted()  ) {

        CollapseInfo ci(mesh_, tmpHandle);

        // Check if legal we analyze the priority of this collapse operation
        if (this->is_collapse_legal(ci)) {
          double energy = this->collapse_priority(ci);

          // Check if the current samples energy is better than any energy before
          if ( energy < bestEnergy ) {
            bestEnergy = energy;
            bestHandle = tmpHandle;
          }
        } else {
          continue;
        }
      }

    }

    // Found the best energy?
    if ( bestEnergy != FLT_MAX ) {

      // setup collapse info
      CollapseInfo ci(mesh_, bestHandle);

      // check topological correctness AGAIN !
      if (!this->is_collapse_legal(ci))
        continue;

      // pre-processing
      this->preprocess_collapse(ci);

      // perform collapse
      mesh_.collapse(bestHandle);
      ++n_collapses;

      // update triangle normals
      typename Mesh::VertexFaceIter vf_it = mesh_.vf_iter(ci.v1);
      for (; vf_it; ++vf_it)
        if (!mesh_.status(vf_it).deleted())
          mesh_.set_normal(vf_it, mesh_.calc_face_normal(vf_it.handle()));

      // post-process collapse
      this->postprocess_collapse(ci);

    }

  }

  // DON'T do garbage collection here! It's up to the application.
  return n_collapses;
}

//-----------------------------------------------------------------------------

template<class Mesh>
size_t McDecimaterT<Mesh>::decimate_to_faces(size_t _nv, size_t _nf) {
  if (!this->is_initialized())
    return 0;

  unsigned int nv = mesh_.n_vertices();
  unsigned int nf = mesh_.n_faces();
  unsigned int n_collapses(0);

  // check if no vertex or face contraints were set
  bool contraintsOnly = (_nv == 0) && (_nf == 1);

  // check if no legal collapses were found three times in a row
  // for the sampled halfedges
  bool foundNoLegalCollapsesThrice = false;

  // store the last two amount of legal collapses found
  int lastLegalCollapses = -1;
  int beforeLastLegalCollapses = -1;

  while ( (contraintsOnly && !foundNoLegalCollapsesThrice) && (_nv < nv) && (_nf < nf) ) {

    // Optimal id and value will be collected during the random sampling
    typename Mesh::HalfedgeHandle bestHandle(-1);
    double bestEnergy = FLT_MAX;

    // Generate random samples for collapses
    unsigned int legalCollapses = 0;
    for ( unsigned int i = 0; i < randomSamples_; ++i) {

      // Random halfedge handle
      typename Mesh::HalfedgeHandle tmpHandle = typename Mesh::HalfedgeHandle((static_cast<double>(rand()) / RAND_MAX) * (mesh_.n_halfedges()-1) );

      // if it is not deleted, we analyse it
      if ( ! mesh_.status(tmpHandle).deleted()  ) {

        CollapseInfo ci(mesh_, tmpHandle);

        // Check if legal we analyze the priority of this collapse operation
        if (this->is_collapse_legal(ci)) {
          ++legalCollapses;
          double energy = this->collapse_priority(ci);

          // Check if the current samples energy is better than any energy before
          if ( energy < bestEnergy ) {
            bestEnergy = energy;
            bestHandle = tmpHandle;
          }
        } else {
          continue;
        }
      }

    }

    if (contraintsOnly) {
      // check if no legal collapses were found three times in a row
      foundNoLegalCollapsesThrice = (beforeLastLegalCollapses == 0) && (lastLegalCollapses == 0) && (legalCollapses == 0);

      // store amount of last legal collapses found
      beforeLastLegalCollapses = lastLegalCollapses;
      lastLegalCollapses = legalCollapses;
    }

    // Found the best energy?
    if ( bestEnergy != FLT_MAX ) {

      // setup collapse info
      CollapseInfo ci(mesh_, bestHandle);

      // check topological correctness AGAIN !
      if (!this->is_collapse_legal(ci))
        continue;

      // adjust complexity in advance (need boundary status)

      // One vertex is killed by the collapse
      --nv;

      // If we are at a boundary, one face is lost,
      // otherwise two
      if (mesh_.is_boundary(ci.v0v1) || mesh_.is_boundary(ci.v1v0))
        --nf;
      else
        nf -= 2;

      // pre-processing
      this->preprocess_collapse(ci);

      // perform collapse
      mesh_.collapse(bestHandle);
      ++n_collapses;

      // update triangle normals
      typename Mesh::VertexFaceIter vf_it = mesh_.vf_iter(ci.v1);
      for (; vf_it; ++vf_it)
        if (!mesh_.status(vf_it).deleted())
          mesh_.set_normal(vf_it, mesh_.calc_face_normal(vf_it.handle()));

      // post-process collapse
      this->postprocess_collapse(ci);

    }

  }

  // DON'T do garbage collection here! It's up to the application.
  return n_collapses;
}

//=============================================================================
}// END_NS_MC_DECIMATER
} // END_NS_OPENMESH
//=============================================================================

<|endoftext|>


<commit_before>/*
 * The Apache Software License, Version 1.1
 *
 *
 * Copyright (c) 1999 The Apache Software 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.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Xalan" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 APACHE SOFTWARE FOUNDATION OR
 * ITS 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.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation and was
 * originally based on software copyright (c) 1999, International
 * Business Machines, Inc., http://www.ibm.com.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */
// Class header file.
#include "DOMStringPrintWriter.hpp"

#include <vector>

#include <PlatformSupport/DOMStringHelper.hpp>

DOMStringPrintWriter::DOMStringPrintWriter(const XalanDOMString&	theBaseString) :
	PrintWriter(true),
	m_outputString(theBaseString)
{
}

DOMStringPrintWriter::~DOMStringPrintWriter()
{
}

bool
DOMStringPrintWriter::checkError() const
{
	return false;
}

void
DOMStringPrintWriter::close()
{
}

void
DOMStringPrintWriter::flush()
{
}

void
DOMStringPrintWriter::write(
			const char*		s,
			unsigned int	theOffset,
			unsigned int	theLength)
{
	write(XalanDOMString(s), theOffset, theLength);
}

void
DOMStringPrintWriter::write(
			const XalanDOMChar*		s,
			unsigned int			theOffset,
			unsigned int			theLength)
{
#if !defined(XALAN_NO_NAMESPACES)
	using std::vector;
	using std::copy;
#endif

	assert(s != 0);
	assert(theLength == UINT_MAX || length(s) >= theOffset + theLength);

	if (theLength == UINT_MAX)
	{
			m_outputString += (s + theOffset);
	}
	else
	{
		vector<XalanDOMChar>	theBuffer(theLength + 1);

		// We'll copy the characters into the vector first.
		copy(s + theOffset,
			 s + theOffset + theLength,
			 theBuffer.begin());

		// Now append a terminated 0.
		theBuffer.back() = 0;

		// Now append the data.
		m_outputString += theBuffer.begin();
	}
}

void
DOMStringPrintWriter::write(XalanDOMChar	c)
{
	// Write the data as a null-terminated array,
	// so we can guarantee null-termination of our
	// string...
	XalanDOMChar	theBuffer[2] = { 0, 0 };

	theBuffer[0] = c;

	m_outputString += theBuffer;
}

void
DOMStringPrintWriter::write(
			const XalanDOMString&	s,
			unsigned int			theOffset,
			unsigned int			theLength)
{
	assert(s != 0);
	assert(theLength == UINT_MAX || length(s) >= theOffset + theLength);

	if (theOffset == 0 && theLength == UINT_MAX)
	{
		m_outputString += s;
	}
	else
	{
		m_outputString += substring(s, theOffset, theOffset + theLength);
	}
}

#if !defined(XALAN_BOOL_AS_INT)
void
DOMStringPrintWriter::print(bool	b)
{
	if (b == true)
	{
		print(s_trueString);
	}
	else
	{
		print(s_falseString);
	}
}
#endif

void
DOMStringPrintWriter::print(char	c)
{
	write(c);
}

void
DOMStringPrintWriter::print(
			const char*		s,
			unsigned int	theLength)
{
	write(s,
		  0,
		  theLength);
}

void
DOMStringPrintWriter::print(
			const XalanDOMChar*		s,
			unsigned int			theLength)
{
	write(s,
		  0,
		  theLength);
}

void
DOMStringPrintWriter::print(double	d)
{
	m_outputString += DoubleToDOMString(d);
}

void
DOMStringPrintWriter::print(int	i)
{
	m_outputString += LongToDOMString(i);
}

void
DOMStringPrintWriter::print(long	l)
{
	m_outputString += LongToDOMString(l);
}

void
DOMStringPrintWriter::print(const XalanDOMString&	s)
{
	m_outputString += s;
}

void
DOMStringPrintWriter::println()
{
	m_outputString += s_newlineString;
}

#if !defined(XALAN_BOOL_AS_INT)
void
DOMStringPrintWriter::println(bool	b)
{
	print(b);

	println();
}
#endif

void
DOMStringPrintWriter::println(char	c)
{
	print(c);

	println();
}

void
DOMStringPrintWriter::println(
			const char*		s,
			unsigned int	theLength)
{
	print(s, theLength);

	println();
}

void
DOMStringPrintWriter::println(
			const XalanDOMChar*		s,
			unsigned int			theLength)
{
	print(s, theLength);

	println();
}

void
DOMStringPrintWriter::println(double	d)
{
	print(d);

	println();
}

void
DOMStringPrintWriter::println(int		i)
{
	print(i);

	println();
}

void
DOMStringPrintWriter::println(long	l)
{
	print(l);

	println();
}

void
DOMStringPrintWriter::println(const XalanDOMString&	s)
{
	print(s);

	println();
}
<commit_msg>Added explicit initialization of vectors.<commit_after>/*
 * The Apache Software License, Version 1.1
 *
 *
 * Copyright (c) 1999 The Apache Software 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.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Xalan" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 APACHE SOFTWARE FOUNDATION OR
 * ITS 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.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation and was
 * originally based on software copyright (c) 1999, International
 * Business Machines, Inc., http://www.ibm.com.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */
// Class header file.
#include "DOMStringPrintWriter.hpp"

#include <vector>

#include <PlatformSupport/DOMStringHelper.hpp>

DOMStringPrintWriter::DOMStringPrintWriter(const XalanDOMString&	theBaseString) :
	PrintWriter(true),
	m_outputString(theBaseString)
{
}

DOMStringPrintWriter::~DOMStringPrintWriter()
{
}

bool
DOMStringPrintWriter::checkError() const
{
	return false;
}

void
DOMStringPrintWriter::close()
{
}

void
DOMStringPrintWriter::flush()
{
}

void
DOMStringPrintWriter::write(
			const char*		s,
			unsigned int	theOffset,
			unsigned int	theLength)
{
	write(XalanDOMString(s), theOffset, theLength);
}

void
DOMStringPrintWriter::write(
			const XalanDOMChar*		s,
			unsigned int			theOffset,
			unsigned int			theLength)
{
#if !defined(XALAN_NO_NAMESPACES)
	using std::vector;
	using std::copy;
#endif

	assert(s != 0);
	assert(theLength == UINT_MAX || length(s) >= theOffset + theLength);

	if (theLength == UINT_MAX)
	{
			m_outputString += (s + theOffset);
	}
	else
	{
		vector<XalanDOMChar>	theBuffer(theLength + 1, 0);

		// We'll copy the characters into the vector first.
		copy(s + theOffset,
			 s + theOffset + theLength,
			 theBuffer.begin());

		// Now append a terminated 0.
		theBuffer.back() = 0;

		// Now append the data.
		m_outputString += theBuffer.begin();
	}
}

void
DOMStringPrintWriter::write(XalanDOMChar	c)
{
	// Write the data as a null-terminated array,
	// so we can guarantee null-termination of our
	// string...
	XalanDOMChar	theBuffer[2] = { 0, 0 };

	theBuffer[0] = c;

	m_outputString += theBuffer;
}

void
DOMStringPrintWriter::write(
			const XalanDOMString&	s,
			unsigned int			theOffset,
			unsigned int			theLength)
{
	assert(s != 0);
	assert(theLength == UINT_MAX || length(s) >= theOffset + theLength);

	if (theOffset == 0 && theLength == UINT_MAX)
	{
		m_outputString += s;
	}
	else
	{
		m_outputString += substring(s, theOffset, theOffset + theLength);
	}
}

#if !defined(XALAN_BOOL_AS_INT)
void
DOMStringPrintWriter::print(bool	b)
{
	if (b == true)
	{
		print(s_trueString);
	}
	else
	{
		print(s_falseString);
	}
}
#endif

void
DOMStringPrintWriter::print(char	c)
{
	write(c);
}

void
DOMStringPrintWriter::print(
			const char*		s,
			unsigned int	theLength)
{
	write(s,
		  0,
		  theLength);
}

void
DOMStringPrintWriter::print(
			const XalanDOMChar*		s,
			unsigned int			theLength)
{
	write(s,
		  0,
		  theLength);
}

void
DOMStringPrintWriter::print(double	d)
{
	m_outputString += DoubleToDOMString(d);
}

void
DOMStringPrintWriter::print(int	i)
{
	m_outputString += LongToDOMString(i);
}

void
DOMStringPrintWriter::print(long	l)
{
	m_outputString += LongToDOMString(l);
}

void
DOMStringPrintWriter::print(const XalanDOMString&	s)
{
	m_outputString += s;
}

void
DOMStringPrintWriter::println()
{
	m_outputString += s_newlineString;
}

#if !defined(XALAN_BOOL_AS_INT)
void
DOMStringPrintWriter::println(bool	b)
{
	print(b);

	println();
}
#endif

void
DOMStringPrintWriter::println(char	c)
{
	print(c);

	println();
}

void
DOMStringPrintWriter::println(
			const char*		s,
			unsigned int	theLength)
{
	print(s, theLength);

	println();
}

void
DOMStringPrintWriter::println(
			const XalanDOMChar*		s,
			unsigned int			theLength)
{
	print(s, theLength);

	println();
}

void
DOMStringPrintWriter::println(double	d)
{
	print(d);

	println();
}

void
DOMStringPrintWriter::println(int		i)
{
	print(i);

	println();
}

void
DOMStringPrintWriter::println(long	l)
{
	print(l);

	println();
}

void
DOMStringPrintWriter::println(const XalanDOMString&	s)
{
	print(s);

	println();
}
<|endoftext|>


<commit_before>#include "APSEthernet.h"

#ifdef _WIN32
#include "iphlpapi.h"
#else
#include <ifaddrs.h>
#endif

APSEthernet::APSEthernet() : socket_(ios_, udp::endpoint(udp::v4(), APS_PROTO)) {
    FILE_LOG(logDEBUG) << "Creating ethernet interface";
    //enable broadcasting for enumerating
    socket_.set_option(asio::socket_base::broadcast(true));

    //io_service will return immediately so post receive task before .run()
    setup_receive();

    //Setup the asio service to run on a background thread
    receiveThread_ = std::thread([this](){ ios_.run(); });
};

APSEthernet::~APSEthernet() {
    FILE_LOG(logDEBUG) << "Cleaning up ethernet interface";
    ios_.stop();
    receiveThread_.join();
}

void APSEthernet::setup_receive(){
    socket_.async_receive_from(
        asio::buffer(receivedData_, 2048), senderEndpoint_,
        [this](std::error_code ec, std::size_t bytesReceived)
        {
            //If there is anything to look at hand it off to the sorter
            if (!ec && bytesReceived > 0)
            {
                vector<uint8_t> packetData(receivedData_, receivedData_ + bytesReceived);
                sort_packet(packetData, senderEndpoint_);
            }

            //Start the receiver again
            setup_receive();
    });
}

void APSEthernet::sort_packet(const vector<uint8_t> & packetData, const udp::endpoint & sender){
    //If we have the endpoint address then add it to the queue
    string senderIP = sender.address().to_string();
    if (msgQueues_.find(senderIP) == msgQueues_.end()) {
        //If it isn't in our list of APSs then perhaps we are seeing an enumerate status response
        //If so add the device info to the set
        if (packetData.size() == 84) {
            devInfo_[senderIP].endpoint = sender;
            //Turn the byte array into a packet to extract the MAC address and firmware version
            //MAC not strictly necessary as we could just use the broadcast MAC address
            APSEthernetPacket packet = APSEthernetPacket(packetData);
            devInfo_[senderIP].macAddr = packet.header.src;
            APSStatusBank_t statusRegs;
            std::copy(packet.payload.begin(), packet.payload.end(), statusRegs.array);
            FILE_LOG(logDEBUG1) << "Added device with IP " << senderIP <<
                " ; MAC addresss " << devInfo_[senderIP].macAddr.to_string() <<
                " ; firmware version " << hexn<4> << statusRegs.userFirmwareVersion;
        }
    }
    else {
        //Turn the byte array into an APSEthernetPacket
        APSEthernetPacket packet = APSEthernetPacket(packetData);
        //Grab a lock and push the packet into the message queue
        mLock_.lock();
        msgQueues_[senderIP].emplace(packet);
        mLock_.unlock();
    }
}

/* PUBLIC methods */

void APSEthernet::init() {
    reset_maps();
}

vector<string> APSEthernet::get_local_IPs(){
    vector<string> IPs;

    #ifdef _WIN32

    DWORD dwRetVal = 0;
    ULONG family = AF_INET; // we only care about IPv4 for now
    LONG flags = GAA_FLAG_INCLUDE_PREFIX; // not sure what the address prefix is
    PIP_ADAPTER_ADDRESSES pAddresses = nullptr;
    PIP_ADAPTER_ADDRESSES pCurAddresses = nullptr;
    PIP_ADAPTER_UNICAST_ADDRESS pUnicast = nullptr;

    ULONG outBufLen = 15000; //MSDN recommends preallocating 15KB
    pAddresses = (IP_ADAPTER_ADDRESSES *) malloc(outBufLen);
    dwRetVal = GetAdaptersAddresses(family, flags, nullptr, pAddresses, &outBufLen);

    if (dwRetVal == NO_ERROR) {
      for(pCurAddresses = pAddresses; pCurAddresses != nullptr; pCurAddresses = pCurAddresses->Next){
        FILE_LOG(logDEBUG1) << "Found IPv4 interface.";
        FILE_LOG(logDEBUG3) << "IfIndex (IPv4 interface): " << pCurAddresses->IfIndex;
        FILE_LOG(logDEBUG2) << "Adapter name: " << pCurAddresses->AdapterName;
        FILE_LOG(logDEBUG2) << "Friendly adapter name: " << pCurAddresses->FriendlyName; //TODO figure out unicode printing

        //Not yet supported in mingw64
        FILE_LOG(logDEBUG2) << "Transmit link speed: " << pCurAddresses->TransmitLinkSpeed;
        FILE_LOG(logDEBUG2) << "Receive link speed: " << pCurAddresses->ReceiveLinkSpeed;

        for(pUnicast = pCurAddresses->FirstUnicastAddress; pUnicast != nullptr; pUnicast = pUnicast->Next){
            char IPV4Addr[16]; //should be LPTSTR; 16 is maximum length of null terminated xxx.xxx.xxx.xxx
            DWORD addrSize = 16;
            int val;
            val = WSAAddressToString(pUnicast->Address.lpSockaddr, pUnicast->Address.iSockaddrLength, nullptr, IPV4Addr, &addrSize);
            if ( val != 0) {
              val = WSAGetLastError();
              FILE_LOG(logERROR) << "WSAAddressToString error code: " << val;
              continue;
            }
            IPs.push_back(string(IPV4Addr));
            FILE_LOG(logDEBUG1) << "IPv4 address: " << IPs.back();
            FILE_LOG(logDEBUG1) << "Prefix length: " << pUnicast->OnLinkPrefixLength; //doesn't seem to work with mingw64
        }
      }
      free(pAddresses);
      pAddresses = nullptr;
    }
    else{
      FILE_LOG(logERROR) << "Call to GetAdaptersAddresses failed.";
      free(pAddresses);
      pAddresses = nullptr;
    }

    #else

    struct ifaddrs *ifap, *ifa;

    getifaddrs(&ifap);
    for (ifa = ifap; ifa != nullptr; ifa = ifa->ifa_next) {
        //We only care about IPv4 addresses
        if (ifa->ifa_addr->sa_family == AF_INET) {
            struct sockaddr_in* sa = (struct sockaddr_in *) ifa->ifa_addr;
            char* addr = inet_ntoa(sa->sin_addr);
            IPs.push_back(string(addr));
            FILE_LOG(logDEBUG1) << "Interface: " << ifa->ifa_name << "Address: " << IPs.back();
        }
    }
    freeifaddrs(ifap);
    #endif //_WIN32

    return IPs;
}

set<string> APSEthernet::enumerate() {
	/*
	 * Look for all APS units that respond to the broadcast packet
	 */

	FILE_LOG(logDEBUG1) << "APSEthernet::enumerate";

    reset_maps();

    vector<string> localIPs = get_local_IPs();

    for(auto IP : localIPs){
        //Put together the broadcast status request
        APSEthernetPacket broadcastPacket = APSEthernetPacket::create_broadcast_packet();
        typedef asio::ip::address_v4 addrv4;
        addrv4 broadcastAddr = addrv4::broadcast(addrv4::from_string(IP), addrv4::from_string("255.255.255.0"));
        FILE_LOG(logDEBUG2) << "Sending enumerate broadcast out on: " << broadcastAddr.to_string();
        udp::endpoint broadCastEndPoint(broadcastAddr, APS_PROTO);
        socket_.send_to(asio::buffer(broadcastPacket.serialize()), broadCastEndPoint);
    }

    std::this_thread::sleep_for(std::chrono::milliseconds(100));

    set<string> deviceSerials;
    for (auto kv : devInfo_) {
        FILE_LOG(logINFO) << "Found device: " << kv.first;
        deviceSerials.insert(kv.first);
    }
    return deviceSerials;
}

void APSEthernet::reset_maps() {
    devInfo_.clear();
    msgQueues_.clear();
}

void APSEthernet::connect(string serial) {

    mLock_.lock();
	msgQueues_[serial] = queue<APSEthernetPacket>();
    mLock_.unlock();
    if (devInfo_.find(serial) == devInfo_.end()) {
        devInfo_[serial].endpoint = udp::endpoint(asio::ip::address_v4::from_string(serial), APS_PROTO);
        devInfo_[serial].macAddr = MACAddr("FF:FF:FF:FF:FF:FF");
    }
}

void APSEthernet::disconnect(string serial) {
    mLock_.lock();
	msgQueues_.erase(serial);
    mLock_.unlock();
}

int APSEthernet::send(string serial, APSEthernetPacket msg, bool checkResponse) {
    msg.header.dest = devInfo_[serial].macAddr;
    return send_chunk(serial, vector<APSEthernetPacket>(1, msg), !checkResponse);
}

int APSEthernet::send(string serial, vector<APSEthernetPacket> msg, unsigned ackEvery /* see header for default */) {
    //Fill out the destination  MAC address
    FILE_LOG(logDEBUG3) << "Sending " << msg.size() << " packets to " << serial;
    auto iter = msg.begin();
    bool noACK = false;
    if (ackEvery == 0) {
        noACK = true;
        ackEvery = 1;
    }
    // it's nice to have extra status on slow EPROM writes
    bool verbose = (msg[0].header.command.cmd == static_cast<uint32_t>(APS_COMMANDS::EPROMIO));

    vector<APSEthernetPacket> buffer(ackEvery);

    while (iter != msg.end()){

        //Copy the next chunk into a buffer
        auto endPoint = iter + ackEvery;
        if (endPoint > msg.end()) {
            endPoint = msg.end();
        }
        auto chunkSize = std::distance(iter, endPoint);
        buffer.resize(chunkSize);
        std::copy(iter, endPoint, buffer.begin());

        for (auto & packet : buffer ){
            // insert the target MAC address - not really necessary anymore because UDP does filtering
            packet.header.dest = devInfo_[serial].macAddr;
            //NOACK sets the top bit of the command nibble of the command word
            packet.header.command.cmd |= (1 << 3);
        }

        //Apply acknowledge flag to last element of chunk
        if (!noACK){
            buffer.back().header.command.cmd &= ~(1 << 3);
        }

        auto result = send_chunk(serial, buffer, noACK);
        if (result != 0){
            return result;
        }
        std::advance(iter, chunkSize);

        if (verbose && (std::distance(msg.begin(), iter) % 1000 == 0)) {
            FILE_LOG(logDEBUG) << "Write " << 100*std::distance(msg.begin(), iter)/msg.size() << "% complete";
        }
    }
    return 0;
}

int APSEthernet::send_chunk(string serial, vector<APSEthernetPacket> chunk, bool noACK){

    unsigned seqNum, retryct = 0;

    while (retryct++ < 3) {
        seqNum = 0;
        for (auto packet : chunk){
            packet.header.seqNum = seqNum;
            seqNum++;
            FILE_LOG(logDEBUG4) << "Packet command: " << print_APSCommand(packet.header.command);
            socket_.send_to(asio::buffer(packet.serialize()), devInfo_[serial].endpoint);
            // std::this_thread::sleep_for(std::chrono::microseconds(100));
        }

        if(noACK) break;
        //Wait for acknowledge
        //TODO: how to check response mode/stat for success?
        try{
            auto response = receive(serial)[0];
            break;
        }
        catch (std::exception& e) {
            FILE_LOG(logDEBUG) << "No acknowledge received, retrying ...";
        }
    }

    if (retryct == 3) {
        return -1;
    }
    return 0;
}

vector<APSEthernetPacket> APSEthernet::receive(string serial, size_t numPackets, size_t timeoutMS) {
    //Read the packets coming back in up to the timeout
    //Defaults: receive(string serial, size_t numPackets = 1, size_t timeoutMS = 1000);
    std::chrono::time_point<std::chrono::steady_clock> start, end;

    start = std::chrono::steady_clock::now();
    size_t elapsedTime = 0;

    vector<APSEthernetPacket> outVec;

    while (elapsedTime < timeoutMS){
        if (!msgQueues_[serial].empty()){
            mLock_.lock();
            outVec.push_back(msgQueues_[serial].front());
            msgQueues_[serial].pop();
            mLock_.unlock();
            FILE_LOG(logDEBUG4) << "Received packet command: " << print_APSCommand(outVec.back().header.command);
            if (outVec.size() == numPackets){
                FILE_LOG(logDEBUG3) << "Received " << numPackets << " packets from " << serial;
                return outVec;
            }
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
        end = std::chrono::steady_clock::now();
        elapsedTime =  std::chrono::duration_cast<std::chrono::milliseconds>(end-start).count();
    }

    throw runtime_error("Timed out on receive");
}
<commit_msg>:bug: Check for null pointer in ifa_addr.<commit_after>#include "APSEthernet.h"

#ifdef _WIN32
#include "iphlpapi.h"
#else
#include <ifaddrs.h>
#endif

APSEthernet::APSEthernet() : socket_(ios_, udp::endpoint(udp::v4(), APS_PROTO)) {
    FILE_LOG(logDEBUG) << "Creating ethernet interface";
    //enable broadcasting for enumerating
    socket_.set_option(asio::socket_base::broadcast(true));

    //io_service will return immediately so post receive task before .run()
    setup_receive();

    //Setup the asio service to run on a background thread
    receiveThread_ = std::thread([this](){ ios_.run(); });
};

APSEthernet::~APSEthernet() {
    FILE_LOG(logDEBUG) << "Cleaning up ethernet interface";
    ios_.stop();
    receiveThread_.join();
}

void APSEthernet::setup_receive(){
    socket_.async_receive_from(
        asio::buffer(receivedData_, 2048), senderEndpoint_,
        [this](std::error_code ec, std::size_t bytesReceived)
        {
            //If there is anything to look at hand it off to the sorter
            if (!ec && bytesReceived > 0)
            {
                vector<uint8_t> packetData(receivedData_, receivedData_ + bytesReceived);
                sort_packet(packetData, senderEndpoint_);
            }

            //Start the receiver again
            setup_receive();
    });
}

void APSEthernet::sort_packet(const vector<uint8_t> & packetData, const udp::endpoint & sender){
    //If we have the endpoint address then add it to the queue
    string senderIP = sender.address().to_string();
    if (msgQueues_.find(senderIP) == msgQueues_.end()) {
        //If it isn't in our list of APSs then perhaps we are seeing an enumerate status response
        //If so add the device info to the set
        if (packetData.size() == 84) {
            devInfo_[senderIP].endpoint = sender;
            //Turn the byte array into a packet to extract the MAC address and firmware version
            //MAC not strictly necessary as we could just use the broadcast MAC address
            APSEthernetPacket packet = APSEthernetPacket(packetData);
            devInfo_[senderIP].macAddr = packet.header.src;
            APSStatusBank_t statusRegs;
            std::copy(packet.payload.begin(), packet.payload.end(), statusRegs.array);
            FILE_LOG(logDEBUG1) << "Added device with IP " << senderIP <<
                " ; MAC addresss " << devInfo_[senderIP].macAddr.to_string() <<
                " ; firmware version " << hexn<4> << statusRegs.userFirmwareVersion;
        }
    }
    else {
        //Turn the byte array into an APSEthernetPacket
        APSEthernetPacket packet = APSEthernetPacket(packetData);
        //Grab a lock and push the packet into the message queue
        mLock_.lock();
        msgQueues_[senderIP].emplace(packet);
        mLock_.unlock();
    }
}

/* PUBLIC methods */

void APSEthernet::init() {
    reset_maps();
}

vector<string> APSEthernet::get_local_IPs(){
    vector<string> IPs;

    #ifdef _WIN32

    DWORD dwRetVal = 0;
    ULONG family = AF_INET; // we only care about IPv4 for now
    LONG flags = GAA_FLAG_INCLUDE_PREFIX; // not sure what the address prefix is
    PIP_ADAPTER_ADDRESSES pAddresses = nullptr;
    PIP_ADAPTER_ADDRESSES pCurAddresses = nullptr;
    PIP_ADAPTER_UNICAST_ADDRESS pUnicast = nullptr;

    ULONG outBufLen = 15000; //MSDN recommends preallocating 15KB
    pAddresses = (IP_ADAPTER_ADDRESSES *) malloc(outBufLen);
    dwRetVal = GetAdaptersAddresses(family, flags, nullptr, pAddresses, &outBufLen);

    if (dwRetVal == NO_ERROR) {
      for(pCurAddresses = pAddresses; pCurAddresses != nullptr; pCurAddresses = pCurAddresses->Next){
        FILE_LOG(logDEBUG1) << "Found IPv4 interface.";
        FILE_LOG(logDEBUG3) << "IfIndex (IPv4 interface): " << pCurAddresses->IfIndex;
        FILE_LOG(logDEBUG2) << "Adapter name: " << pCurAddresses->AdapterName;
        FILE_LOG(logDEBUG2) << "Friendly adapter name: " << pCurAddresses->FriendlyName; //TODO figure out unicode printing

        //Not yet supported in mingw64
        FILE_LOG(logDEBUG2) << "Transmit link speed: " << pCurAddresses->TransmitLinkSpeed;
        FILE_LOG(logDEBUG2) << "Receive link speed: " << pCurAddresses->ReceiveLinkSpeed;

        for(pUnicast = pCurAddresses->FirstUnicastAddress; pUnicast != nullptr; pUnicast = pUnicast->Next){
            char IPV4Addr[16]; //should be LPTSTR; 16 is maximum length of null terminated xxx.xxx.xxx.xxx
            DWORD addrSize = 16;
            int val;
            val = WSAAddressToString(pUnicast->Address.lpSockaddr, pUnicast->Address.iSockaddrLength, nullptr, IPV4Addr, &addrSize);
            if ( val != 0) {
              val = WSAGetLastError();
              FILE_LOG(logERROR) << "WSAAddressToString error code: " << val;
              continue;
            }
            IPs.push_back(string(IPV4Addr));
            FILE_LOG(logDEBUG1) << "IPv4 address: " << IPs.back();
            FILE_LOG(logDEBUG1) << "Prefix length: " << pUnicast->OnLinkPrefixLength; //doesn't seem to work with mingw64
        }
      }
      free(pAddresses);
      pAddresses = nullptr;
    }
    else{
      FILE_LOG(logERROR) << "Call to GetAdaptersAddresses failed.";
      free(pAddresses);
      pAddresses = nullptr;
    }

    #else

    struct ifaddrs *ifap, *ifa;

    getifaddrs(&ifap);
    for (ifa = ifap; ifa != nullptr; ifa = ifa->ifa_next) {
        //We only care about IPv4 addresses
        if ( ifa->ifa_addr == nullptr) continue; //According to Linux Programmers Manual "This field may contain a null pointer."
        if (ifa->ifa_addr->sa_family == AF_INET) {
            struct sockaddr_in* sa = (struct sockaddr_in *) ifa->ifa_addr;
            char* addr = inet_ntoa(sa->sin_addr);
            IPs.push_back(string(addr));
            FILE_LOG(logDEBUG1) << "Interface: " << ifa->ifa_name << "; Address: " << IPs.back();
        }
    }
    freeifaddrs(ifap);
    #endif //_WIN32

    return IPs;
}

set<string> APSEthernet::enumerate() {
	/*
	 * Look for all APS units that respond to the broadcast packet
	 */

	FILE_LOG(logDEBUG1) << "APSEthernet::enumerate";

    reset_maps();

    vector<string> localIPs = get_local_IPs();

    for(auto IP : localIPs){
        //Put together the broadcast status request
        APSEthernetPacket broadcastPacket = APSEthernetPacket::create_broadcast_packet();
        typedef asio::ip::address_v4 addrv4;
        addrv4 broadcastAddr = addrv4::broadcast(addrv4::from_string(IP), addrv4::from_string("255.255.255.0"));
        FILE_LOG(logDEBUG2) << "Sending enumerate broadcast out on: " << broadcastAddr.to_string();
        udp::endpoint broadCastEndPoint(broadcastAddr, APS_PROTO);
        socket_.send_to(asio::buffer(broadcastPacket.serialize()), broadCastEndPoint);
    }

    std::this_thread::sleep_for(std::chrono::milliseconds(100));

    set<string> deviceSerials;
    for (auto kv : devInfo_) {
        FILE_LOG(logINFO) << "Found device: " << kv.first;
        deviceSerials.insert(kv.first);
    }
    return deviceSerials;
}

void APSEthernet::reset_maps() {
    devInfo_.clear();
    msgQueues_.clear();
}

void APSEthernet::connect(string serial) {

    mLock_.lock();
	msgQueues_[serial] = queue<APSEthernetPacket>();
    mLock_.unlock();
    if (devInfo_.find(serial) == devInfo_.end()) {
        devInfo_[serial].endpoint = udp::endpoint(asio::ip::address_v4::from_string(serial), APS_PROTO);
        devInfo_[serial].macAddr = MACAddr("FF:FF:FF:FF:FF:FF");
    }
}

void APSEthernet::disconnect(string serial) {
    mLock_.lock();
	msgQueues_.erase(serial);
    mLock_.unlock();
}

int APSEthernet::send(string serial, APSEthernetPacket msg, bool checkResponse) {
    msg.header.dest = devInfo_[serial].macAddr;
    return send_chunk(serial, vector<APSEthernetPacket>(1, msg), !checkResponse);
}

int APSEthernet::send(string serial, vector<APSEthernetPacket> msg, unsigned ackEvery /* see header for default */) {
    //Fill out the destination  MAC address
    FILE_LOG(logDEBUG3) << "Sending " << msg.size() << " packets to " << serial;
    auto iter = msg.begin();
    bool noACK = false;
    if (ackEvery == 0) {
        noACK = true;
        ackEvery = 1;
    }
    // it's nice to have extra status on slow EPROM writes
    bool verbose = (msg[0].header.command.cmd == static_cast<uint32_t>(APS_COMMANDS::EPROMIO));

    vector<APSEthernetPacket> buffer(ackEvery);

    while (iter != msg.end()){

        //Copy the next chunk into a buffer
        auto endPoint = iter + ackEvery;
        if (endPoint > msg.end()) {
            endPoint = msg.end();
        }
        auto chunkSize = std::distance(iter, endPoint);
        buffer.resize(chunkSize);
        std::copy(iter, endPoint, buffer.begin());

        for (auto & packet : buffer ){
            // insert the target MAC address - not really necessary anymore because UDP does filtering
            packet.header.dest = devInfo_[serial].macAddr;
            //NOACK sets the top bit of the command nibble of the command word
            packet.header.command.cmd |= (1 << 3);
        }

        //Apply acknowledge flag to last element of chunk
        if (!noACK){
            buffer.back().header.command.cmd &= ~(1 << 3);
        }

        auto result = send_chunk(serial, buffer, noACK);
        if (result != 0){
            return result;
        }
        std::advance(iter, chunkSize);

        if (verbose && (std::distance(msg.begin(), iter) % 1000 == 0)) {
            FILE_LOG(logDEBUG) << "Write " << 100*std::distance(msg.begin(), iter)/msg.size() << "% complete";
        }
    }
    return 0;
}

int APSEthernet::send_chunk(string serial, vector<APSEthernetPacket> chunk, bool noACK){

    unsigned seqNum, retryct = 0;

    while (retryct++ < 3) {
        seqNum = 0;
        for (auto packet : chunk){
            packet.header.seqNum = seqNum;
            seqNum++;
            FILE_LOG(logDEBUG4) << "Packet command: " << print_APSCommand(packet.header.command);
            socket_.send_to(asio::buffer(packet.serialize()), devInfo_[serial].endpoint);
            // std::this_thread::sleep_for(std::chrono::microseconds(100));
        }

        if(noACK) break;
        //Wait for acknowledge
        //TODO: how to check response mode/stat for success?
        try{
            auto response = receive(serial)[0];
            break;
        }
        catch (std::exception& e) {
            FILE_LOG(logDEBUG) << "No acknowledge received, retrying ...";
        }
    }

    if (retryct == 3) {
        return -1;
    }
    return 0;
}

vector<APSEthernetPacket> APSEthernet::receive(string serial, size_t numPackets, size_t timeoutMS) {
    //Read the packets coming back in up to the timeout
    //Defaults: receive(string serial, size_t numPackets = 1, size_t timeoutMS = 1000);
    std::chrono::time_point<std::chrono::steady_clock> start, end;

    start = std::chrono::steady_clock::now();
    size_t elapsedTime = 0;

    vector<APSEthernetPacket> outVec;

    while (elapsedTime < timeoutMS){
        if (!msgQueues_[serial].empty()){
            mLock_.lock();
            outVec.push_back(msgQueues_[serial].front());
            msgQueues_[serial].pop();
            mLock_.unlock();
            FILE_LOG(logDEBUG4) << "Received packet command: " << print_APSCommand(outVec.back().header.command);
            if (outVec.size() == numPackets){
                FILE_LOG(logDEBUG3) << "Received " << numPackets << " packets from " << serial;
                return outVec;
            }
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
        end = std::chrono::steady_clock::now();
        elapsedTime =  std::chrono::duration_cast<std::chrono::milliseconds>(end-start).count();
    }

    throw runtime_error("Timed out on receive");
}
<|endoftext|>


<commit_before>// Copyright eeGeo Ltd (2012-2015), All Rights Reserved

#include <map>
#include "SearchQueryPerformer.h"
#include "ISearchService.h"
#include "ISearchResultRepository.h"
#include "IInterestPointProvider.h"
#include "LatLongAltitude.h"
#include "RenderCamera.h"
#include "SearchResultModel.h"
#include "BidirectionalBus.h"
#include "SearchQueryResultsRemovedMessage.h"

namespace
{
    float GetSearchRadius(const Eegeo::Camera::RenderCamera& renderCamera)
    {
        const float SearchRadiusMax = 50000.f;
        float radius = (renderCamera.GetAltitude() * Eegeo::Math::Tan(renderCamera.GetFOV()));
        return Eegeo::Min(SearchRadiusMax, radius);
    }
}

namespace ExampleApp
{
    namespace Search
    {
        namespace SdkModel
        {
            SearchQueryPerformer::SearchQueryPerformer(ISearchService& searchService,
                                                       ISearchResultRepository& searchResultRepository,
                                                       Eegeo::Camera::GlobeCamera::GpsGlobeCameraController& cameraController,
                                                       ExampleAppMessaging::TMessageBus& messageBus)
                : m_searchService(searchService)
                , m_searchResultsRepository(searchResultRepository)
                , m_pSearchResultResponseReceivedCallback(Eegeo_NEW((Eegeo::Helpers::TCallback2<SearchQueryPerformer, const SearchQuery&, const std::vector<SearchResultModel>&>))(this, &SearchQueryPerformer::HandleSearchResultsResponseReceived))
                , m_previousQuery("", false, false, Eegeo::Space::LatLongAltitude(0.0, 0.0, 0.0), 0.f)
                , m_hasQuery(false)
                , m_cameraController(cameraController)
                , m_messageBus(messageBus)
            {
                m_searchService.InsertOnReceivedQueryResultsCallback(*m_pSearchResultResponseReceivedCallback);
            }

            SearchQueryPerformer::~SearchQueryPerformer()
            {
                m_searchService.RemoveOnReceivedQueryResultsCallback(*m_pSearchResultResponseReceivedCallback);

                Eegeo_DELETE m_pSearchResultResponseReceivedCallback;
            }

            bool SearchQueryPerformer::HasQuery() const
            {
                return m_hasQuery;
            }

            void SearchQueryPerformer::PerformSearchQuery(const std::string& query, bool isTag, bool tryInteriorSearch)
            {
                Eegeo::Space::LatLongAltitude location = Eegeo::Space::LatLongAltitude::FromECEF(m_cameraController.GetEcefInterestPoint());
                PerformSearchQuery(query, isTag, tryInteriorSearch, location);
            }

            void SearchQueryPerformer::PerformSearchQuery(const std::string& query, bool isTag, bool tryInteriorSearch, float radius)
            {
                const Eegeo::Space::LatLongAltitude& location = Eegeo::Space::LatLongAltitude::FromECEF(m_cameraController.GetEcefInterestPoint());
                PerformSearchQuery(query, isTag, tryInteriorSearch, location, radius);
            }

            void SearchQueryPerformer::PerformSearchQuery(const std::string& query,
                    bool isTag,
                    bool tryInteriorSearch,
                    const Eegeo::Space::LatLongAltitude& location)
            {
                const float radius = GetSearchRadius(m_cameraController.GetRenderCamera());
                PerformSearchQuery(query, isTag, tryInteriorSearch, location, radius);
            }

            void SearchQueryPerformer::PerformSearchQuery(const std::string& query,
                                                          bool isTag,
                                                          bool tryInteriorSearch,
                                                          const Eegeo::Space::LatLongAltitude& location,
                                                          float radius)
            {
                m_hasQuery = true;
                SearchQuery searchQuery(query, isTag, tryInteriorSearch, location, radius);
                m_previousQuery = searchQuery;
                m_searchService.PerformLocationQuerySearch(searchQuery);
            }

            void SearchQueryPerformer::RemoveSearchQueryResults()
            {
                m_hasQuery = false;

                m_searchService.CancelInFlightQueries();

                RemoveExistingSearchResults();

                m_queryResultsClearedCallbacks.ExecuteCallbacks();
                m_messageBus.Publish(SearchQueryResultsRemovedMessage());
            }

            void SearchQueryPerformer::InsertOnSearchResultsClearedCallback(Eegeo::Helpers::ICallback0& callback)
            {
                m_queryResultsClearedCallbacks.AddCallback(callback);
            }

            void SearchQueryPerformer::RemoveOnSearchResultsClearedCallback(Eegeo::Helpers::ICallback0& callback)
            {
                m_queryResultsClearedCallbacks.RemoveCallback(callback);
            }

            void SearchQueryPerformer::HandleSearchResultsResponseReceived(const SearchQuery& query,
                    const std::vector<SearchResultModel>& results)
            {
                // Ideally we should keep merge the new results into the old result set in a stable way, so that the
                // result list doesn't jumble up when we move about and perform new searches.

                std::map<SearchResultModel, size_t> currentRepoHash;
                std::set<size_t> retainedResultIndices;
                std::vector<SearchResultModel*> newResultTemporaryStorage;
                const int topIndexBeforeAddingNewResult = static_cast<int>(m_searchResultsRepository.GetItemCount() - 1);

                // First of all lets map the contents of the results repo so we can easily look results up later.
                // Map the result to its index.
                for(size_t i = 0; i < m_searchResultsRepository.GetItemCount(); ++ i)
                {
                    SearchResultModel* pItem = m_searchResultsRepository.GetItemAtIndex(i);
                    currentRepoHash.insert(std::make_pair(*pItem, i));
                }

                // For the new results, check if we already have the result using the map. If so, we flag the results
                // index as 'retained'. This will be used later when removing old results. If we have not seen the result
                // before, add it to a new temporary vector.
                for(std::vector<SearchResultModel>::const_iterator it = results.begin();
                        it != results.end();
                        ++ it)
                {
                    std::map<SearchResultModel, size_t>::iterator mapIt = currentRepoHash.find(*it);

                    if(mapIt == currentRepoHash.end())
                    {
                        SearchResultModel* pItem = Eegeo_NEW(SearchResultModel)(*it);

                        newResultTemporaryStorage.push_back(pItem);
                    }
                    else
                    {
                        retainedResultIndices.insert(mapIt->second);
                    }
                }

                // Walk backwards over the repository removing old results which are not retained, by inspection of
                // the retained indices.
                for(int i = topIndexBeforeAddingNewResult; i >= 0; --i)
                {
                    if(retainedResultIndices.find(i) == retainedResultIndices.end())
                    {
                        SearchResultModel* pItem = m_searchResultsRepository.GetItemAtIndex(i);
                        m_searchResultsRepository.RemoveItem(pItem);
                        Eegeo_DELETE pItem;
                    }
                }

                // Add the new results on the end after removing the old results.
                for(std::vector<SearchResultModel*>::const_iterator it = newResultTemporaryStorage.begin();
                        it != newResultTemporaryStorage.end();
                        ++ it)
                {
                    m_searchResultsRepository.AddItem(*it);
                }
            }

            void SearchQueryPerformer::RemoveExistingSearchResults()
            {
                while(m_searchResultsRepository.GetItemCount() > 0)
                {
                    SearchResultModel* pItem = m_searchResultsRepository.GetItemAtIndex(0);
                    m_searchResultsRepository.RemoveItem(pItem);
                    Eegeo_DELETE pItem;
                }
            }
        }
    }
}
<commit_msg>Add (arbitrary) minimum search radius to search queries.<commit_after>// Copyright eeGeo Ltd (2012-2015), All Rights Reserved

#include <map>
#include "SearchQueryPerformer.h"
#include "ISearchService.h"
#include "ISearchResultRepository.h"
#include "IInterestPointProvider.h"
#include "LatLongAltitude.h"
#include "RenderCamera.h"
#include "SearchResultModel.h"
#include "BidirectionalBus.h"
#include "SearchQueryResultsRemovedMessage.h"

namespace
{
    float GetSearchRadius(const Eegeo::Camera::RenderCamera& renderCamera)
    {
        const float SearchRadiusMin = 1000.f;
        const float SearchRadiusMax = 50000.f;
        float radius = (renderCamera.GetAltitude() * Eegeo::Math::Tan(renderCamera.GetFOV()));
        return Eegeo::Clamp(radius, SearchRadiusMin, SearchRadiusMax);
    }
}

namespace ExampleApp
{
    namespace Search
    {
        namespace SdkModel
        {
            SearchQueryPerformer::SearchQueryPerformer(ISearchService& searchService,
                                                       ISearchResultRepository& searchResultRepository,
                                                       Eegeo::Camera::GlobeCamera::GpsGlobeCameraController& cameraController,
                                                       ExampleAppMessaging::TMessageBus& messageBus)
                : m_searchService(searchService)
                , m_searchResultsRepository(searchResultRepository)
                , m_pSearchResultResponseReceivedCallback(Eegeo_NEW((Eegeo::Helpers::TCallback2<SearchQueryPerformer, const SearchQuery&, const std::vector<SearchResultModel>&>))(this, &SearchQueryPerformer::HandleSearchResultsResponseReceived))
                , m_previousQuery("", false, false, Eegeo::Space::LatLongAltitude(0.0, 0.0, 0.0), 0.f)
                , m_hasQuery(false)
                , m_cameraController(cameraController)
                , m_messageBus(messageBus)
            {
                m_searchService.InsertOnReceivedQueryResultsCallback(*m_pSearchResultResponseReceivedCallback);
            }

            SearchQueryPerformer::~SearchQueryPerformer()
            {
                m_searchService.RemoveOnReceivedQueryResultsCallback(*m_pSearchResultResponseReceivedCallback);

                Eegeo_DELETE m_pSearchResultResponseReceivedCallback;
            }

            bool SearchQueryPerformer::HasQuery() const
            {
                return m_hasQuery;
            }

            void SearchQueryPerformer::PerformSearchQuery(const std::string& query, bool isTag, bool tryInteriorSearch)
            {
                Eegeo::Space::LatLongAltitude location = Eegeo::Space::LatLongAltitude::FromECEF(m_cameraController.GetEcefInterestPoint());
                PerformSearchQuery(query, isTag, tryInteriorSearch, location);
            }

            void SearchQueryPerformer::PerformSearchQuery(const std::string& query, bool isTag, bool tryInteriorSearch, float radius)
            {
                const Eegeo::Space::LatLongAltitude& location = Eegeo::Space::LatLongAltitude::FromECEF(m_cameraController.GetEcefInterestPoint());
                PerformSearchQuery(query, isTag, tryInteriorSearch, location, radius);
            }

            void SearchQueryPerformer::PerformSearchQuery(const std::string& query,
                    bool isTag,
                    bool tryInteriorSearch,
                    const Eegeo::Space::LatLongAltitude& location)
            {
                const float radius = GetSearchRadius(m_cameraController.GetRenderCamera());
                PerformSearchQuery(query, isTag, tryInteriorSearch, location, radius);
            }

            void SearchQueryPerformer::PerformSearchQuery(const std::string& query,
                                                          bool isTag,
                                                          bool tryInteriorSearch,
                                                          const Eegeo::Space::LatLongAltitude& location,
                                                          float radius)
            {
                m_hasQuery = true;
                SearchQuery searchQuery(query, isTag, tryInteriorSearch, location, radius);
                m_previousQuery = searchQuery;
                m_searchService.PerformLocationQuerySearch(searchQuery);
            }

            void SearchQueryPerformer::RemoveSearchQueryResults()
            {
                m_hasQuery = false;

                m_searchService.CancelInFlightQueries();

                RemoveExistingSearchResults();

                m_queryResultsClearedCallbacks.ExecuteCallbacks();
                m_messageBus.Publish(SearchQueryResultsRemovedMessage());
            }

            void SearchQueryPerformer::InsertOnSearchResultsClearedCallback(Eegeo::Helpers::ICallback0& callback)
            {
                m_queryResultsClearedCallbacks.AddCallback(callback);
            }

            void SearchQueryPerformer::RemoveOnSearchResultsClearedCallback(Eegeo::Helpers::ICallback0& callback)
            {
                m_queryResultsClearedCallbacks.RemoveCallback(callback);
            }

            void SearchQueryPerformer::HandleSearchResultsResponseReceived(const SearchQuery& query,
                    const std::vector<SearchResultModel>& results)
            {
                // Ideally we should keep merge the new results into the old result set in a stable way, so that the
                // result list doesn't jumble up when we move about and perform new searches.

                std::map<SearchResultModel, size_t> currentRepoHash;
                std::set<size_t> retainedResultIndices;
                std::vector<SearchResultModel*> newResultTemporaryStorage;
                const int topIndexBeforeAddingNewResult = static_cast<int>(m_searchResultsRepository.GetItemCount() - 1);

                // First of all lets map the contents of the results repo so we can easily look results up later.
                // Map the result to its index.
                for(size_t i = 0; i < m_searchResultsRepository.GetItemCount(); ++ i)
                {
                    SearchResultModel* pItem = m_searchResultsRepository.GetItemAtIndex(i);
                    currentRepoHash.insert(std::make_pair(*pItem, i));
                }

                // For the new results, check if we already have the result using the map. If so, we flag the results
                // index as 'retained'. This will be used later when removing old results. If we have not seen the result
                // before, add it to a new temporary vector.
                for(std::vector<SearchResultModel>::const_iterator it = results.begin();
                        it != results.end();
                        ++ it)
                {
                    std::map<SearchResultModel, size_t>::iterator mapIt = currentRepoHash.find(*it);

                    if(mapIt == currentRepoHash.end())
                    {
                        SearchResultModel* pItem = Eegeo_NEW(SearchResultModel)(*it);

                        newResultTemporaryStorage.push_back(pItem);
                    }
                    else
                    {
                        retainedResultIndices.insert(mapIt->second);
                    }
                }

                // Walk backwards over the repository removing old results which are not retained, by inspection of
                // the retained indices.
                for(int i = topIndexBeforeAddingNewResult; i >= 0; --i)
                {
                    if(retainedResultIndices.find(i) == retainedResultIndices.end())
                    {
                        SearchResultModel* pItem = m_searchResultsRepository.GetItemAtIndex(i);
                        m_searchResultsRepository.RemoveItem(pItem);
                        Eegeo_DELETE pItem;
                    }
                }

                // Add the new results on the end after removing the old results.
                for(std::vector<SearchResultModel*>::const_iterator it = newResultTemporaryStorage.begin();
                        it != newResultTemporaryStorage.end();
                        ++ it)
                {
                    m_searchResultsRepository.AddItem(*it);
                }
            }

            void SearchQueryPerformer::RemoveExistingSearchResults()
            {
                while(m_searchResultsRepository.GetItemCount() > 0)
                {
                    SearchResultModel* pItem = m_searchResultsRepository.GetItemAtIndex(0);
                    m_searchResultsRepository.RemoveItem(pItem);
                    Eegeo_DELETE pItem;
                }
            }
        }
    }
}
<|endoftext|>