<commit_before>#include <osg/Notify>
#include <osg/MatrixTransform>
#include <osg/PositionAttitudeTransform>
#include <osg/Geometry>
#include <osg/Geode>

#include <osgUtil/Optimizer>

#include <osgDB/Registry>
#include <osgDB/ReadFile>

#include <osgGA/TrackballManipulator>
#include <osgGA/FlightManipulator>
#include <osgGA/DriveManipulator>

#include <osgSim/OverlayNode>

#include <osgViewer/Viewer>
#include <iostream>

osg::AnimationPath* createAnimationPath(const osg::Vec3& center,float radius,double looptime)
{
    // set up the animation path
    osg::AnimationPath* animationPath = new osg::AnimationPath;
    animationPath->setLoopMode(osg::AnimationPath::LOOP);

    int numSamples = 40;
    float yaw = 0.0f;
    float yaw_delta = 2.0f*osg::PI/((float)numSamples-1.0f);
    float roll = osg::inDegrees(30.0f);

    double time=0.0f;
    double time_delta = looptime/(double)numSamples;
    for(int i=0;i<numSamples;++i)
    {
        osg::Vec3 position(center+osg::Vec3(sinf(yaw)*radius,cosf(yaw)*radius,0.0f));
        osg::Quat rotation(osg::Quat(roll,osg::Vec3(0.0,1.0,0.0))*osg::Quat(-(yaw+osg::inDegrees(90.0f)),osg::Vec3(0.0,0.0,1.0)));

        animationPath->insert(time,osg::AnimationPath::ControlPoint(position,rotation));

        yaw += yaw_delta;
        time += time_delta;

    }
    return animationPath;
}

osg::Node* createBase(const osg::Vec3& center,float radius)
{

    int numTilesX = 10;
    int numTilesY = 10;

    float width = 2*radius;
    float height = 2*radius;

    osg::Vec3 v000(center - osg::Vec3(width*0.5f,height*0.5f,0.0f));
    osg::Vec3 dx(osg::Vec3(width/((float)numTilesX),0.0,0.0f));
    osg::Vec3 dy(osg::Vec3(0.0f,height/((float)numTilesY),0.0f));

    // fill in vertices for grid, note numTilesX+1 * numTilesY+1...
    osg::Vec3Array* coords = new osg::Vec3Array;
    int iy;
    for(iy=0;iy<=numTilesY;++iy)
    {
        for(int ix=0;ix<=numTilesX;++ix)
        {
            coords->push_back(v000+dx*(float)ix+dy*(float)iy);
        }
    }

    //Just two colours - black and white.
    osg::Vec4Array* colors = new osg::Vec4Array;
    colors->push_back(osg::Vec4(1.0f,1.0f,1.0f,1.0f)); // white
    colors->push_back(osg::Vec4(0.0f,0.0f,0.0f,1.0f)); // black
    int numColors=colors->size();

    int numIndicesPerRow=numTilesX+1;
    osg::UByteArray* coordIndices = new osg::UByteArray; // assumes we are using less than 256 points...
    osg::UByteArray* colorIndices = new osg::UByteArray;
    for(iy=0;iy<numTilesY;++iy)
    {
        for(int ix=0;ix<numTilesX;++ix)
        {
            // four vertices per quad.
            coordIndices->push_back(ix    +(iy+1)*numIndicesPerRow);
            coordIndices->push_back(ix    +iy*numIndicesPerRow);
            coordIndices->push_back((ix+1)+iy*numIndicesPerRow);
            coordIndices->push_back((ix+1)+(iy+1)*numIndicesPerRow);

            // one color per quad
            colorIndices->push_back((ix+iy)%numColors);
        }
    }

    // set up a single normal
    osg::Vec3Array* normals = new osg::Vec3Array;
    normals->push_back(osg::Vec3(0.0f,0.0f,1.0f));

    osg::Geometry* geom = new osg::Geometry;
    geom->setVertexArray(coords);
    geom->setVertexIndices(coordIndices);

    geom->setColorArray(colors);
    geom->setColorIndices(colorIndices);
    geom->setColorBinding(osg::Geometry::BIND_PER_PRIMITIVE);

    geom->setNormalArray(normals);
    geom->setNormalBinding(osg::Geometry::BIND_OVERALL);

    geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,coordIndices->size()));

    osg::Geode* geode = new osg::Geode;
    geode->addDrawable(geom);

    return geode;
}

osg::Node* createMovingModel(const osg::Vec3& center, float radius)
{
    float animationLength = 10.0f;

    osg::AnimationPath* animationPath = createAnimationPath(center,radius,animationLength);

    osg::Group* model = new osg::Group;

    osg::Node* glider = osgDB::readNodeFile("glider.osg");
    if (glider)
    {
        const osg::BoundingSphere& bs = glider->getBound();

        float size = radius/bs.radius()*0.3f;
        osg::MatrixTransform* positioned = new osg::MatrixTransform;
        positioned->setDataVariance(osg::Object::STATIC);
        positioned->setMatrix(osg::Matrix::translate(-bs.center())*
                                     osg::Matrix::scale(size,size,size)*
                                     osg::Matrix::rotate(osg::inDegrees(-90.0f),0.0f,0.0f,1.0f));

        positioned->addChild(glider);

        osg::PositionAttitudeTransform* xform = new osg::PositionAttitudeTransform;
        xform->setUpdateCallback(new osg::AnimationPathCallback(animationPath,0.0,1.0));
        xform->addChild(positioned);

        model->addChild(xform);
    }

    osg::Node* cessna = osgDB::readNodeFile("cessna.osg");
    if (cessna)
    {
        const osg::BoundingSphere& bs = cessna->getBound();

        float size = radius/bs.radius()*0.3f;
        osg::MatrixTransform* positioned = new osg::MatrixTransform;
        positioned->setDataVariance(osg::Object::STATIC);
        positioned->setMatrix(osg::Matrix::translate(-bs.center())*
                                     osg::Matrix::scale(size,size,size)*
                                     osg::Matrix::rotate(osg::inDegrees(180.0f),0.0f,0.0f,1.0f));

        positioned->addChild(cessna);

        osg::MatrixTransform* xform = new osg::MatrixTransform;
        xform->setUpdateCallback(new osg::AnimationPathCallback(animationPath,0.0f,2.0));
        xform->addChild(positioned);

        model->addChild(xform);
    }

    return model;
}

osg::Node* createModel(bool overlay, osgSim::OverlayNode::OverlayTechnique technique)
{
    osg::Vec3 center(0.0f,0.0f,0.0f);
    float radius = 100.0f;

    osg::Group* root = new osg::Group;

    float baseHeight = center.z()-radius*0.6;
    osg::Node* baseModel = createBase(center-osg::Vec3(0.0f,0.0f,radius*0.5),radius);
    osg::Node* movingModel = createMovingModel(center,radius*0.8f);

    if (overlay)
    {
        osgSim::OverlayNode* overlayNode = new osgSim::OverlayNode(technique);
        overlayNode->setContinuousUpdate(true);
        overlayNode->setOverlaySubgraph(movingModel);
        overlayNode->setOverlayBaseHeight(baseHeight);
        overlayNode->addChild(baseModel);
        root->addChild(overlayNode);
    }
    else
    {

        root->addChild(baseModel);
    }

    root->addChild(movingModel);

    return root;
}

int main( int argc, char **argv )
{

    bool overlay = false;
    osg::ArgumentParser arguments(&argc,argv);
    while (arguments.read("--overlay")) overlay = true;

    osgSim::OverlayNode::OverlayTechnique technique = osgSim::OverlayNode::OBJECT_DEPENDENT_WITH_ORTHOGRAPHIC_OVERLAY;
    while (arguments.read("--object")) { technique = osgSim::OverlayNode::OBJECT_DEPENDENT_WITH_ORTHOGRAPHIC_OVERLAY; overlay=true; }
    while (arguments.read("--ortho") || arguments.read("--orthographic")) { technique = osgSim::OverlayNode::VIEW_DEPENDENT_WITH_ORTHOGRAPHIC_OVERLAY; overlay=true; }
    while (arguments.read("--persp") || arguments.read("--perspective")) { technique = osgSim::OverlayNode::VIEW_DEPENDENT_WITH_PERSPECTIVE_OVERLAY; overlay=true; }

    // initialize the viewer.
    osgViewer::Viewer viewer;

    // load the nodes from the commandline arguments.
    osg::Node* model = createModel(overlay, technique);
    if (!model)
    {
        return 1;
    }

    // tilt the scene so the default eye position is looking down on the model.
    osg::MatrixTransform* rootnode = new osg::MatrixTransform;
    rootnode->setMatrix(osg::Matrix::rotate(osg::inDegrees(30.0f),1.0f,0.0f,0.0f));
    rootnode->addChild(model);

    // run optimization over the scene graph
    osgUtil::Optimizer optimzer;
    optimzer.optimize(rootnode);

    // set the scene to render
    viewer.setSceneData(rootnode);

    viewer.setCameraManipulator(new osgGA::TrackballManipulator());

#if 0

    // use of custom simulation time.

    viewer.realize();

    double simulationTime = 100.0;

    while (!viewer.done())
    {
        viewer.frame(simulationTime);
        simulationTime -= 0.01;
    }

    return 0;
#else

    // normal viewer usage.

    viewer.setUpViewOnSingleScreen(1);

    return viewer.run();

#endif
}
<commit_msg>Improved the accuracy of the overlaynode settings<commit_after>#include <osg/Notify>
#include <osg/MatrixTransform>
#include <osg/PositionAttitudeTransform>
#include <osg/Geometry>
#include <osg/Geode>

#include <osgUtil/Optimizer>

#include <osgDB/Registry>
#include <osgDB/ReadFile>

#include <osgGA/TrackballManipulator>
#include <osgGA/FlightManipulator>
#include <osgGA/DriveManipulator>

#include <osgSim/OverlayNode>

#include <osgViewer/Viewer>
#include <iostream>

osg::AnimationPath* createAnimationPath(const osg::Vec3& center,float radius,double looptime)
{
    // set up the animation path
    osg::AnimationPath* animationPath = new osg::AnimationPath;
    animationPath->setLoopMode(osg::AnimationPath::LOOP);

    int numSamples = 40;
    float yaw = 0.0f;
    float yaw_delta = 2.0f*osg::PI/((float)numSamples-1.0f);
    float roll = osg::inDegrees(30.0f);

    double time=0.0f;
    double time_delta = looptime/(double)numSamples;
    for(int i=0;i<numSamples;++i)
    {
        osg::Vec3 position(center+osg::Vec3(sinf(yaw)*radius,cosf(yaw)*radius,0.0f));
        osg::Quat rotation(osg::Quat(roll,osg::Vec3(0.0,1.0,0.0))*osg::Quat(-(yaw+osg::inDegrees(90.0f)),osg::Vec3(0.0,0.0,1.0)));

        animationPath->insert(time,osg::AnimationPath::ControlPoint(position,rotation));

        yaw += yaw_delta;
        time += time_delta;

    }
    return animationPath;
}

osg::Node* createBase(const osg::Vec3& center,float radius)
{

    int numTilesX = 10;
    int numTilesY = 10;

    float width = 2*radius;
    float height = 2*radius;

    osg::Vec3 v000(center - osg::Vec3(width*0.5f,height*0.5f,0.0f));
    osg::Vec3 dx(osg::Vec3(width/((float)numTilesX),0.0,0.0f));
    osg::Vec3 dy(osg::Vec3(0.0f,height/((float)numTilesY),0.0f));

    // fill in vertices for grid, note numTilesX+1 * numTilesY+1...
    osg::Vec3Array* coords = new osg::Vec3Array;
    int iy;
    for(iy=0;iy<=numTilesY;++iy)
    {
        for(int ix=0;ix<=numTilesX;++ix)
        {
            coords->push_back(v000+dx*(float)ix+dy*(float)iy);
        }
    }

    //Just two colours - black and white.
    osg::Vec4Array* colors = new osg::Vec4Array;
    colors->push_back(osg::Vec4(1.0f,1.0f,1.0f,1.0f)); // white
    colors->push_back(osg::Vec4(0.0f,0.0f,0.0f,1.0f)); // black
    int numColors=colors->size();

    int numIndicesPerRow=numTilesX+1;
    osg::UByteArray* coordIndices = new osg::UByteArray; // assumes we are using less than 256 points...
    osg::UByteArray* colorIndices = new osg::UByteArray;
    for(iy=0;iy<numTilesY;++iy)
    {
        for(int ix=0;ix<numTilesX;++ix)
        {
            // four vertices per quad.
            coordIndices->push_back(ix    +(iy+1)*numIndicesPerRow);
            coordIndices->push_back(ix    +iy*numIndicesPerRow);
            coordIndices->push_back((ix+1)+iy*numIndicesPerRow);
            coordIndices->push_back((ix+1)+(iy+1)*numIndicesPerRow);

            // one color per quad
            colorIndices->push_back((ix+iy)%numColors);
        }
    }

    // set up a single normal
    osg::Vec3Array* normals = new osg::Vec3Array;
    normals->push_back(osg::Vec3(0.0f,0.0f,1.0f));

    osg::Geometry* geom = new osg::Geometry;
    geom->setVertexArray(coords);
    geom->setVertexIndices(coordIndices);

    geom->setColorArray(colors);
    geom->setColorIndices(colorIndices);
    geom->setColorBinding(osg::Geometry::BIND_PER_PRIMITIVE);

    geom->setNormalArray(normals);
    geom->setNormalBinding(osg::Geometry::BIND_OVERALL);

    geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,coordIndices->size()));

    osg::Geode* geode = new osg::Geode;
    geode->addDrawable(geom);

    return geode;
}

osg::Node* createMovingModel(const osg::Vec3& center, float radius)
{
    float animationLength = 10.0f;

    osg::AnimationPath* animationPath = createAnimationPath(center,radius,animationLength);

    osg::Group* model = new osg::Group;

    osg::Node* glider = osgDB::readNodeFile("glider.osg");
    if (glider)
    {
        const osg::BoundingSphere& bs = glider->getBound();

        float size = radius/bs.radius()*0.3f;
        osg::MatrixTransform* positioned = new osg::MatrixTransform;
        positioned->setDataVariance(osg::Object::STATIC);
        positioned->setMatrix(osg::Matrix::translate(-bs.center())*
                                     osg::Matrix::scale(size,size,size)*
                                     osg::Matrix::rotate(osg::inDegrees(-90.0f),0.0f,0.0f,1.0f));

        positioned->addChild(glider);

        osg::PositionAttitudeTransform* xform = new osg::PositionAttitudeTransform;
        xform->setUpdateCallback(new osg::AnimationPathCallback(animationPath,0.0,1.0));
        xform->addChild(positioned);

        model->addChild(xform);
    }

    osg::Node* cessna = osgDB::readNodeFile("cessna.osg");
    if (cessna)
    {
        const osg::BoundingSphere& bs = cessna->getBound();

        float size = radius/bs.radius()*0.3f;
        osg::MatrixTransform* positioned = new osg::MatrixTransform;
        positioned->setDataVariance(osg::Object::STATIC);
        positioned->setMatrix(osg::Matrix::translate(-bs.center())*
                                     osg::Matrix::scale(size,size,size)*
                                     osg::Matrix::rotate(osg::inDegrees(180.0f),0.0f,0.0f,1.0f));

        positioned->addChild(cessna);

        osg::MatrixTransform* xform = new osg::MatrixTransform;
        xform->setUpdateCallback(new osg::AnimationPathCallback(animationPath,0.0f,2.0));
        xform->addChild(positioned);

        model->addChild(xform);
    }

    return model;
}

osg::Node* createModel(bool overlay, osgSim::OverlayNode::OverlayTechnique technique)
{
    osg::Vec3 center(0.0f,0.0f,0.0f);
    float radius = 100.0f;

    osg::Group* root = new osg::Group;

    float baseHeight = center.z()-radius*0.5;
    osg::Node* baseModel = createBase(osg::Vec3(center.x(), center.y(), baseHeight),radius);
    osg::Node* movingModel = createMovingModel(center,radius*0.8f);

    if (overlay)
    {
        osgSim::OverlayNode* overlayNode = new osgSim::OverlayNode(technique);
        overlayNode->setContinuousUpdate(true);
        overlayNode->setOverlaySubgraph(movingModel);
        overlayNode->setOverlayBaseHeight(baseHeight-0.01);
        overlayNode->addChild(baseModel);
        root->addChild(overlayNode);
    }
    else
    {

        root->addChild(baseModel);
    }

    root->addChild(movingModel);

    return root;
}

int main( int argc, char **argv )
{

    bool overlay = false;
    osg::ArgumentParser arguments(&argc,argv);
    while (arguments.read("--overlay")) overlay = true;

    osgSim::OverlayNode::OverlayTechnique technique = osgSim::OverlayNode::OBJECT_DEPENDENT_WITH_ORTHOGRAPHIC_OVERLAY;
    while (arguments.read("--object")) { technique = osgSim::OverlayNode::OBJECT_DEPENDENT_WITH_ORTHOGRAPHIC_OVERLAY; overlay=true; }
    while (arguments.read("--ortho") || arguments.read("--orthographic")) { technique = osgSim::OverlayNode::VIEW_DEPENDENT_WITH_ORTHOGRAPHIC_OVERLAY; overlay=true; }
    while (arguments.read("--persp") || arguments.read("--perspective")) { technique = osgSim::OverlayNode::VIEW_DEPENDENT_WITH_PERSPECTIVE_OVERLAY; overlay=true; }

    // initialize the viewer.
    osgViewer::Viewer viewer;

    // load the nodes from the commandline arguments.
    osg::Node* model = createModel(overlay, technique);
    if (!model)
    {
        return 1;
    }

    // tilt the scene so the default eye position is looking down on the model.
    osg::MatrixTransform* rootnode = new osg::MatrixTransform;
    rootnode->setMatrix(osg::Matrix::rotate(osg::inDegrees(30.0f),1.0f,0.0f,0.0f));
    rootnode->addChild(model);

    // run optimization over the scene graph
    osgUtil::Optimizer optimzer;
    optimzer.optimize(rootnode);

    // set the scene to render
    viewer.setSceneData(rootnode);

    viewer.setCameraManipulator(new osgGA::TrackballManipulator());

#if 1

    // use of custom simulation time.

    viewer.realize();

    double simulationTime = 0.0;

    while (!viewer.done())
    {
        viewer.frame(simulationTime);
        simulationTime += 0.001;
    }

    return 0;
#else

    // normal viewer usage.

    viewer.setUpViewOnSingleScreen(1);

    return viewer.run();

#endif
}
<|endoftext|>


<commit_before>
#include "helper.h"
#include "solid.h"
#include "vertex.h"
#include "edge.h"
#include "face.h"
#include "buffermesh.h"

#include <TopoDS.hxx>
#include <TopExp_Explorer.hxx>

#include <BRepMesh_IncrementalMesh.hxx>
#include <Poly_Array1OfTriangle.hxx>
#include <TColgp_Array1OfPnt.hxx>

Nan::Persistent<v8::Function> moxcad::Solid::constructor;

moxcad::Solid::Solid()
{
}

moxcad::Solid::~Solid()
{
}

void moxcad::Solid::Init(v8::Local<v8::Object> namespc)
{
  DEFINE_FUNCTION_TEMPLATE("Solid", tpl);

  Nan::SetPrototypeMethod(tpl, "numFaces", numFaces);
  Nan::SetPrototypeMethod(tpl, "numEdges", numEdges);
  Nan::SetPrototypeMethod(tpl, "numVertices", numVertices);
  Nan::SetPrototypeMethod(tpl, "numShells", numShells);

  Nan::SetPrototypeMethod(tpl, "eachVertex", eachVertex);
  Nan::SetPrototypeMethod(tpl, "eachEdge", eachEdge);
  Nan::SetPrototypeMethod(tpl, "eachFace", eachFace);

  Nan::SetPrototypeMethod(tpl, "tessellate", tessellate);

  constructor.Reset(tpl->GetFunction());
  namespc->Set(Nan::New("Solid").ToLocalChecked(), tpl->GetFunction());
}

NAN_METHOD(moxcad::Solid::New)
{
  ALLOW_ONLY_CONSTRUCTOR(info);
  Solid *obj = new Solid();
  obj->Wrap(info.This());
  info.GetReturnValue().Set(info.This());
}

NAN_METHOD(moxcad::Solid::numFaces)
{
  GET_SELF(moxcad::Solid, self);
  TopExp_Explorer exp(self->m_solid, TopAbs_FACE);
  unsigned int i=0;
  while(exp.More()) {
    i++;
    exp.Next();
  }
  info.GetReturnValue().Set(Nan::New<v8::Uint32>(i));
}

NAN_METHOD(moxcad::Solid::numEdges)
{
  GET_SELF(moxcad::Solid, self);
  TopExp_Explorer exp(self->m_solid, TopAbs_EDGE);
  unsigned int i=0;
  while(exp.More()) {
    TopoDS_Edge topoEdge = TopoDS::Edge(exp.Current());
    if(topoEdge.Orientation() == TopAbs_FORWARD) {
      i++;
    }
    exp.Next();
  }
  info.GetReturnValue().Set(Nan::New<v8::Uint32>(i));
}

NAN_METHOD(moxcad::Solid::numVertices)
{
  GET_SELF(moxcad::Solid, self);
  TopExp_Explorer exp(self->m_solid, TopAbs_VERTEX);
  unsigned int i=0;
  while(exp.More()) {
    i++;
    exp.Next();
  }
  info.GetReturnValue().Set(Nan::New<v8::Uint32>(i));
}

NAN_METHOD(moxcad::Solid::numShells)
{
  GET_SELF(moxcad::Solid, self);
  TopExp_Explorer exp(self->m_solid, TopAbs_SHELL);
  unsigned int i=0;
  while(exp.More()) {
    i++;
    exp.Next();
  }
  info.GetReturnValue().Set(Nan::New<v8::Uint32>(i));

}

NAN_METHOD(moxcad::Solid::eachVertex)
{
  // Extract callback function
  v8::Local<v8::Function> cb = info[0].As<v8::Function>();

  // Iterate over vertices
  GET_SELF(moxcad::Solid, self);
  TopExp_Explorer exp(self->m_solid, TopAbs_VERTEX);
  while(exp.More()) {
    TopoDS_Vertex topoVtx = TopoDS::Vertex(exp.Current());

    // Package the vertex into Javascript object and invoke callback with it
    v8::Local<v8::Object> vtxInstance = moxcad::Vertex::NewInstance();
    moxcad::Vertex *vtx = ObjectWrap::Unwrap<moxcad::Vertex>(vtxInstance);
    vtx->setOCC(topoVtx);

    // Invoke callback
    const unsigned int argc = 1;
    v8::Local<v8::Value> argv[] = { vtxInstance };
    Nan::MakeCallback(Nan::GetCurrentContext()->Global(), cb, argc, argv);

    exp.Next();
  }
  info.GetReturnValue().Set(info.This());
}

NAN_METHOD(moxcad::Solid::eachEdge)
{
  // Extract callback function
  v8::Local<v8::Function> cb = info[0].As<v8::Function>();

  // Iterate over edges
  GET_SELF(moxcad::Solid, self);
  TopExp_Explorer exp(self->m_solid, TopAbs_EDGE);
  while(exp.More()) {
    TopoDS_Edge topoEdge = TopoDS::Edge(exp.Current());

    if(topoEdge.Orientation() == TopAbs_FORWARD) {
      // Package the edge into Javascript object and invoke callback with it
      v8::Local<v8::Object> edgeInstance = moxcad::Edge::NewInstance();
      moxcad::Edge *edge = ObjectWrap::Unwrap<moxcad::Edge>(edgeInstance);
      edge->setOCC(topoEdge);

      // Invoke callback
      const unsigned int argc = 1;
      v8::Local<v8::Value> argv[] = { edgeInstance };
      Nan::MakeCallback(Nan::GetCurrentContext()->Global(), cb, argc, argv);
    }

    exp.Next();
  }
  info.GetReturnValue().Set(info.This());
}

NAN_METHOD(moxcad::Solid::eachFace)
{
  // Extract callback function
  v8::Local<v8::Function> cb = info[0].As<v8::Function>();

  // Iterate over faces
  GET_SELF(moxcad::Solid, self);
  TopExp_Explorer exp(self->m_solid, TopAbs_FACE);
  while(exp.More()) {
    TopoDS_Face topoFace = TopoDS::Face(exp.Current());

    // Package the face into Javascript object and invoke callback with it
    v8::Local<v8::Object> faceInstance = moxcad::Face::NewInstance();
    moxcad::Face *face = ObjectWrap::Unwrap<moxcad::Face>(faceInstance);
    face->setOCC(topoFace);

    // Invoke callback
    const unsigned int argc = 1;
    v8::Local<v8::Value> argv[] = { faceInstance };
    Nan::MakeCallback(Nan::GetCurrentContext()->Global(), cb, argc, argv);

    exp.Next();
  }
  info.GetReturnValue().Set(info.This());
}

NAN_METHOD(moxcad::Solid::tessellate)
{
  v8::Local<v8::Object> buffers = Nan::New<v8::Object>();

  v8::Local<v8::Array> faceBuffers = Nan::New<v8::Array>();

  //v8::Local<v8::Object> bufferMeshHdl = moxcad::BufferMesh::NewInstance();
  //moxcad::BufferMesh *bufferMesh =
  //  ObjectWrap::Unwrap<moxcad::BufferMesh>(bufferMeshHdl);

  v8::Isolate* isolate = info.GetIsolate();

  GET_SELF(moxcad::Solid, self);

  const Standard_Real aLinearDeflection   = 0.01;
  const Standard_Real anAngularDeflection = 0.5;

  TopExp_Explorer exp(self->m_solid, TopAbs_FACE);
  int i = 0;
  while(exp.More()) {
    TopoDS_Face topoFace = TopoDS::Face(exp.Current());
    BRepMesh_IncrementalMesh aMesh(topoFace, aLinearDeflection, Standard_False, anAngularDeflection);
    aMesh.Perform();
    TopLoc_Location l;
    const Handle(Poly_Triangulation)& pt = BRep_Tool::Triangulation(topoFace, l);
    const Poly_Array1OfTriangle& polyarr = pt->Triangles();

    // Indices
    v8::Local<v8::Uint32Array> idxArr =
      v8::Uint32Array::New(v8::ArrayBuffer::New(isolate, 4*polyarr.Size()),0,polyarr.Size());
    for(Standard_Integer i=polyarr.Lower(), idx=0; i<=polyarr.Upper(); i++, idx++) {
      const Poly_Triangle& ptri = polyarr.Value(i);
      idxArr->Set(Nan::GetCurrentContext(), 3*idx, Nan::New<v8::Uint32>(ptri.Value(1)));
      idxArr->Set(Nan::GetCurrentContext(), 3*idx+1, Nan::New<v8::Uint32>(ptri.Value(2)));
      idxArr->Set(Nan::GetCurrentContext(), 3*idx+2, Nan::New<v8::Uint32>(ptri.Value(3)));
    }

    // Vertices
    const TColgp_Array1OfPnt& nodes = pt->Nodes();
    int nReals = nodes.Size() * 3;
    v8::Local<v8::Float32Array> vtxArr =
      v8::Float32Array::New(v8::ArrayBuffer::New(isolate, 4*nReals),0,nReals);
    for(Standard_Integer i=nodes.Lower(), idx=0; i<=nodes.Upper(); i++, idx++) {
      const gp_Pnt& pnt = nodes.Value(i);
      vtxArr->Set(Nan::GetCurrentContext(), 3*idx, Nan::New<v8::Number>(pnt.X()));
      vtxArr->Set(Nan::GetCurrentContext(), 3*idx+1, Nan::New<v8::Number>(pnt.Y()));
      vtxArr->Set(Nan::GetCurrentContext(), 3*idx+2, Nan::New<v8::Number>(pnt.Z()));
    }

    v8::Local<v8::Object> buffers = Nan::New<v8::Object>();
    buffers->Set(Nan::New("vtx").ToLocalChecked(), vtxArr);
    buffers->Set(Nan::New("idx").ToLocalChecked(), idxArr);

    faceBuffers->Set(i, buffers);

    //bufferMesh->addFace(topoFace);
    exp.Next();

    i++;
  }

  buffers->Set(Nan::New("faceBuffers").ToLocalChecked(), faceBuffers);

  //info.GetReturnValue().Set(bufferMeshHdl);
  info.GetReturnValue().Set(buffers);
}

v8::Local<v8::Object> moxcad::Solid::NewInstance()
{
  Nan::EscapableHandleScope scope;

  const unsigned argc = 1;
  v8::Local<v8::Value> argv[1] = {Nan::New("xxx").ToLocalChecked()};
  v8::Local<v8::Function> cons = Nan::New<v8::Function>(constructor);
  v8::Local<v8::Object> instance = cons->NewInstance(argc, argv);

  return scope.Escape(instance);
}
<commit_msg>Fixes to get the buffer data loading in Three.js<commit_after>
#include "helper.h"
#include "solid.h"
#include "vertex.h"
#include "edge.h"
#include "face.h"
#include "buffermesh.h"

#include <TopoDS.hxx>
#include <TopExp_Explorer.hxx>

#include <BRepMesh_IncrementalMesh.hxx>
#include <Poly_Array1OfTriangle.hxx>
#include <TColgp_Array1OfPnt.hxx>

Nan::Persistent<v8::Function> moxcad::Solid::constructor;

moxcad::Solid::Solid()
{
}

moxcad::Solid::~Solid()
{
}

void moxcad::Solid::Init(v8::Local<v8::Object> namespc)
{
  DEFINE_FUNCTION_TEMPLATE("Solid", tpl);

  Nan::SetPrototypeMethod(tpl, "numFaces", numFaces);
  Nan::SetPrototypeMethod(tpl, "numEdges", numEdges);
  Nan::SetPrototypeMethod(tpl, "numVertices", numVertices);
  Nan::SetPrototypeMethod(tpl, "numShells", numShells);

  Nan::SetPrototypeMethod(tpl, "eachVertex", eachVertex);
  Nan::SetPrototypeMethod(tpl, "eachEdge", eachEdge);
  Nan::SetPrototypeMethod(tpl, "eachFace", eachFace);

  Nan::SetPrototypeMethod(tpl, "tessellate", tessellate);

  constructor.Reset(tpl->GetFunction());
  namespc->Set(Nan::New("Solid").ToLocalChecked(), tpl->GetFunction());
}

NAN_METHOD(moxcad::Solid::New)
{
  ALLOW_ONLY_CONSTRUCTOR(info);
  Solid *obj = new Solid();
  obj->Wrap(info.This());
  info.GetReturnValue().Set(info.This());
}

NAN_METHOD(moxcad::Solid::numFaces)
{
  GET_SELF(moxcad::Solid, self);
  TopExp_Explorer exp(self->m_solid, TopAbs_FACE);
  unsigned int i=0;
  while(exp.More()) {
    i++;
    exp.Next();
  }
  info.GetReturnValue().Set(Nan::New<v8::Uint32>(i));
}

NAN_METHOD(moxcad::Solid::numEdges)
{
  GET_SELF(moxcad::Solid, self);
  TopExp_Explorer exp(self->m_solid, TopAbs_EDGE);
  unsigned int i=0;
  while(exp.More()) {
    TopoDS_Edge topoEdge = TopoDS::Edge(exp.Current());
    if(topoEdge.Orientation() == TopAbs_FORWARD) {
      i++;
    }
    exp.Next();
  }
  info.GetReturnValue().Set(Nan::New<v8::Uint32>(i));
}

NAN_METHOD(moxcad::Solid::numVertices)
{
  GET_SELF(moxcad::Solid, self);
  TopExp_Explorer exp(self->m_solid, TopAbs_VERTEX);
  unsigned int i=0;
  while(exp.More()) {
    i++;
    exp.Next();
  }
  info.GetReturnValue().Set(Nan::New<v8::Uint32>(i));
}

NAN_METHOD(moxcad::Solid::numShells)
{
  GET_SELF(moxcad::Solid, self);
  TopExp_Explorer exp(self->m_solid, TopAbs_SHELL);
  unsigned int i=0;
  while(exp.More()) {
    i++;
    exp.Next();
  }
  info.GetReturnValue().Set(Nan::New<v8::Uint32>(i));

}

NAN_METHOD(moxcad::Solid::eachVertex)
{
  // Extract callback function
  v8::Local<v8::Function> cb = info[0].As<v8::Function>();

  // Iterate over vertices
  GET_SELF(moxcad::Solid, self);
  TopExp_Explorer exp(self->m_solid, TopAbs_VERTEX);
  while(exp.More()) {
    TopoDS_Vertex topoVtx = TopoDS::Vertex(exp.Current());

    // Package the vertex into Javascript object and invoke callback with it
    v8::Local<v8::Object> vtxInstance = moxcad::Vertex::NewInstance();
    moxcad::Vertex *vtx = ObjectWrap::Unwrap<moxcad::Vertex>(vtxInstance);
    vtx->setOCC(topoVtx);

    // Invoke callback
    const unsigned int argc = 1;
    v8::Local<v8::Value> argv[] = { vtxInstance };
    Nan::MakeCallback(Nan::GetCurrentContext()->Global(), cb, argc, argv);

    exp.Next();
  }
  info.GetReturnValue().Set(info.This());
}

NAN_METHOD(moxcad::Solid::eachEdge)
{
  // Extract callback function
  v8::Local<v8::Function> cb = info[0].As<v8::Function>();

  // Iterate over edges
  GET_SELF(moxcad::Solid, self);
  TopExp_Explorer exp(self->m_solid, TopAbs_EDGE);
  while(exp.More()) {
    TopoDS_Edge topoEdge = TopoDS::Edge(exp.Current());

    if(topoEdge.Orientation() == TopAbs_FORWARD) {
      // Package the edge into Javascript object and invoke callback with it
      v8::Local<v8::Object> edgeInstance = moxcad::Edge::NewInstance();
      moxcad::Edge *edge = ObjectWrap::Unwrap<moxcad::Edge>(edgeInstance);
      edge->setOCC(topoEdge);

      // Invoke callback
      const unsigned int argc = 1;
      v8::Local<v8::Value> argv[] = { edgeInstance };
      Nan::MakeCallback(Nan::GetCurrentContext()->Global(), cb, argc, argv);
    }

    exp.Next();
  }
  info.GetReturnValue().Set(info.This());
}

NAN_METHOD(moxcad::Solid::eachFace)
{
  // Extract callback function
  v8::Local<v8::Function> cb = info[0].As<v8::Function>();

  // Iterate over faces
  GET_SELF(moxcad::Solid, self);
  TopExp_Explorer exp(self->m_solid, TopAbs_FACE);
  while(exp.More()) {
    TopoDS_Face topoFace = TopoDS::Face(exp.Current());

    // Package the face into Javascript object and invoke callback with it
    v8::Local<v8::Object> faceInstance = moxcad::Face::NewInstance();
    moxcad::Face *face = ObjectWrap::Unwrap<moxcad::Face>(faceInstance);
    face->setOCC(topoFace);

    // Invoke callback
    const unsigned int argc = 1;
    v8::Local<v8::Value> argv[] = { faceInstance };
    Nan::MakeCallback(Nan::GetCurrentContext()->Global(), cb, argc, argv);

    exp.Next();
  }
  info.GetReturnValue().Set(info.This());
}

NAN_METHOD(moxcad::Solid::tessellate)
{
  v8::Local<v8::Object> buffers = Nan::New<v8::Object>();

  v8::Local<v8::Array> faceBuffers = Nan::New<v8::Array>();

  //v8::Local<v8::Object> bufferMeshHdl = moxcad::BufferMesh::NewInstance();
  //moxcad::BufferMesh *bufferMesh =
  //  ObjectWrap::Unwrap<moxcad::BufferMesh>(bufferMeshHdl);

  v8::Isolate* isolate = info.GetIsolate();

  GET_SELF(moxcad::Solid, self);

  const Standard_Real aLinearDeflection   = 0.01;
  const Standard_Real anAngularDeflection = 0.5;

  TopExp_Explorer exp(self->m_solid, TopAbs_FACE);
  int i = 0;
  while(exp.More()) {
    TopoDS_Face topoFace = TopoDS::Face(exp.Current());
    BRepMesh_IncrementalMesh aMesh(topoFace, aLinearDeflection, Standard_False, anAngularDeflection);
    aMesh.Perform();
    TopLoc_Location l;
    const Handle(Poly_Triangulation)& pt = BRep_Tool::Triangulation(topoFace, l);
    const Poly_Array1OfTriangle& polyarr = pt->Triangles();

    // Indices
    v8::Local<v8::Array> idxArr = Nan::New<v8::Array>();
    for(Standard_Integer i=polyarr.Lower(), idx=0; i<=polyarr.Upper(); i++, idx++) {
      const Poly_Triangle& ptri = polyarr.Value(i);
      idxArr->Set(Nan::GetCurrentContext(), 3*idx, Nan::New<v8::Uint32>(ptri.Value(1)-1));
      idxArr->Set(Nan::GetCurrentContext(), 3*idx+1, Nan::New<v8::Uint32>(ptri.Value(2)-1));
      idxArr->Set(Nan::GetCurrentContext(), 3*idx+2, Nan::New<v8::Uint32>(ptri.Value(3)-1));
    }

    // Vertices
    const TColgp_Array1OfPnt& nodes = pt->Nodes();
    int nReals = nodes.Size() * 3;
    v8::Local<v8::Array> vtxArr = Nan::New<v8::Array>();
    for(Standard_Integer i=nodes.Lower(), idx=0; i<=nodes.Upper(); i++, idx++) {
      const gp_Pnt& pnt = nodes.Value(i);
      vtxArr->Set(Nan::GetCurrentContext(), 3*idx, Nan::New<v8::Number>(pnt.X()));
      vtxArr->Set(Nan::GetCurrentContext(), 3*idx+1, Nan::New<v8::Number>(pnt.Y()));
      vtxArr->Set(Nan::GetCurrentContext(), 3*idx+2, Nan::New<v8::Number>(pnt.Z()));
    }

    v8::Local<v8::Object> buffers = Nan::New<v8::Object>();
    buffers->Set(Nan::New("vtx").ToLocalChecked(), vtxArr);
    buffers->Set(Nan::New("idx").ToLocalChecked(), idxArr);

    faceBuffers->Set(i, buffers);

    //bufferMesh->addFace(topoFace);
    exp.Next();

    i++;
  }

  buffers->Set(Nan::New("faceBuffers").ToLocalChecked(), faceBuffers);

  //info.GetReturnValue().Set(bufferMeshHdl);
  info.GetReturnValue().Set(buffers);
}

v8::Local<v8::Object> moxcad::Solid::NewInstance()
{
  Nan::EscapableHandleScope scope;

  const unsigned argc = 1;
  v8::Local<v8::Value> argv[1] = {Nan::New("xxx").ToLocalChecked()};
  v8::Local<v8::Function> cons = Nan::New<v8::Function>(constructor);
  v8::Local<v8::Object> instance = cons->NewInstance(argc, argv);

  return scope.Escape(instance);
}
<|endoftext|>


<commit_before>#pragma once

#include <stdint.h>
#include <memory>
#include "bytes.hh"
#include "utils/allocation_strategy.hh"

struct blob_storage {
    using size_type = uint32_t;
    using char_type = bytes_view::value_type;

    blob_storage** backref;
    size_type size;
    char_type data[];

    blob_storage(blob_storage** backref, size_type size) noexcept
        : backref(backref)
        , size(size)
    {
        *backref = this;
    }

    blob_storage(blob_storage&& o) noexcept
        : backref(o.backref)
        , size(o.size)
    {
        *backref = this;
        memcpy(data, o.data, size);
    }
} __attribute__((packed));

// A managed version of "bytes" (can be used with LSA).
class managed_bytes {
    static constexpr size_t max_inline_size = 15;
    struct small_blob {
        bytes_view::value_type data[max_inline_size];
        int8_t size; // -1 -> use blob_storage
    };
    union {
        blob_storage* ptr;
        small_blob small;
    } _u;
    static_assert(sizeof(small_blob) > sizeof(blob_storage*), "inline size too small");
private:
    bool external() const {
        return _u.small.size < 0;
    }
public:
    using size_type = blob_storage::size_type;
    struct initialized_later {};

    managed_bytes() {
        _u.small.size = 0;
    }

    managed_bytes(const blob_storage::char_type* ptr, size_type size)
        : managed_bytes(bytes_view(ptr, size)) {}

    managed_bytes(const bytes& b) : managed_bytes(static_cast<bytes_view>(b)) {}

    managed_bytes(initialized_later, size_type size) {
        if (size <= max_inline_size) {
            _u.small.size = size;
        } else {
            _u.small.size = -1;
            void* p = current_allocator().alloc(standard_migrator<blob_storage>,
                sizeof(blob_storage) + size, alignof(blob_storage));
            new (p) blob_storage(&_u.ptr, size);
        }
    }

    managed_bytes(bytes_view v) : managed_bytes(initialized_later(), v.size()) {
        memcpy(data(), v.data(), v.size());
    }

    ~managed_bytes() {
        if (external()) {
            current_allocator().destroy(_u.ptr);
        }
    }

    managed_bytes(const managed_bytes& o) : managed_bytes(static_cast<bytes_view>(o)) {}

    managed_bytes(managed_bytes&& o) noexcept
        : _u(o._u)
    {
        if (external()) {
            if (_u.ptr) {
                _u.ptr->backref = &_u.ptr;
            }
        }
        o._u.small.size = 0;
    }

    managed_bytes& operator=(managed_bytes&& o) {
        if (this != &o) {
            this->~managed_bytes();
            new (this) managed_bytes(std::move(o));
        }
        return *this;
    }

    managed_bytes& operator=(const managed_bytes& o) {
        if (this != &o) {
            // FIXME: not exception safe
            this->~managed_bytes();
            new (this) managed_bytes(o);
        }
        return *this;
    }

    bool operator==(const managed_bytes& o) const {
        return static_cast<bytes_view>(*this) == static_cast<bytes_view>(o);
    }

    bool operator!=(const managed_bytes& o) const {
        return !(*this == o);
    }

    operator bytes_view() const {
        return { data(), size() };
    }

    bytes_view::value_type& operator[](size_type index) {
        return data()[index];
    }

    const bytes_view::value_type& operator[](size_type index) const {
        return data()[index];
    }

    size_type size() const {
        if (external()) {
            return _u.ptr->size;
        } else {
            return _u.small.size;
        }
    }

    const blob_storage::char_type* begin() const {
        return data();
    }

    const blob_storage::char_type* end() const {
        return data() + size();
    }

    blob_storage::char_type* begin() {
        return data();
    }

    blob_storage::char_type* end() {
        return data() + size();
    }

    bool empty() const {
        return size() == 0;
    }

    blob_storage::char_type* data() {
        if (external()) {
            return _u.ptr->data;
        } else {
            return _u.small.data;
        }
    }

    const blob_storage::char_type* data() const {
        return const_cast<managed_bytes*>(this)->data();
    }
};

namespace std {

template <>
struct hash<managed_bytes> {
    size_t operator()(managed_bytes v) const {
        return hash<bytes_view>()(v);
    }
};

}
<commit_msg>managed_bytes: simplify empty()<commit_after>#pragma once

#include <stdint.h>
#include <memory>
#include "bytes.hh"
#include "utils/allocation_strategy.hh"

struct blob_storage {
    using size_type = uint32_t;
    using char_type = bytes_view::value_type;

    blob_storage** backref;
    size_type size;
    char_type data[];

    blob_storage(blob_storage** backref, size_type size) noexcept
        : backref(backref)
        , size(size)
    {
        *backref = this;
    }

    blob_storage(blob_storage&& o) noexcept
        : backref(o.backref)
        , size(o.size)
    {
        *backref = this;
        memcpy(data, o.data, size);
    }
} __attribute__((packed));

// A managed version of "bytes" (can be used with LSA).
class managed_bytes {
    static constexpr size_t max_inline_size = 15;
    struct small_blob {
        bytes_view::value_type data[max_inline_size];
        int8_t size; // -1 -> use blob_storage
    };
    union {
        blob_storage* ptr;
        small_blob small;
    } _u;
    static_assert(sizeof(small_blob) > sizeof(blob_storage*), "inline size too small");
private:
    bool external() const {
        return _u.small.size < 0;
    }
public:
    using size_type = blob_storage::size_type;
    struct initialized_later {};

    managed_bytes() {
        _u.small.size = 0;
    }

    managed_bytes(const blob_storage::char_type* ptr, size_type size)
        : managed_bytes(bytes_view(ptr, size)) {}

    managed_bytes(const bytes& b) : managed_bytes(static_cast<bytes_view>(b)) {}

    managed_bytes(initialized_later, size_type size) {
        if (size <= max_inline_size) {
            _u.small.size = size;
        } else {
            _u.small.size = -1;
            void* p = current_allocator().alloc(standard_migrator<blob_storage>,
                sizeof(blob_storage) + size, alignof(blob_storage));
            new (p) blob_storage(&_u.ptr, size);
        }
    }

    managed_bytes(bytes_view v) : managed_bytes(initialized_later(), v.size()) {
        memcpy(data(), v.data(), v.size());
    }

    ~managed_bytes() {
        if (external()) {
            current_allocator().destroy(_u.ptr);
        }
    }

    managed_bytes(const managed_bytes& o) : managed_bytes(static_cast<bytes_view>(o)) {}

    managed_bytes(managed_bytes&& o) noexcept
        : _u(o._u)
    {
        if (external()) {
            if (_u.ptr) {
                _u.ptr->backref = &_u.ptr;
            }
        }
        o._u.small.size = 0;
    }

    managed_bytes& operator=(managed_bytes&& o) {
        if (this != &o) {
            this->~managed_bytes();
            new (this) managed_bytes(std::move(o));
        }
        return *this;
    }

    managed_bytes& operator=(const managed_bytes& o) {
        if (this != &o) {
            // FIXME: not exception safe
            this->~managed_bytes();
            new (this) managed_bytes(o);
        }
        return *this;
    }

    bool operator==(const managed_bytes& o) const {
        return static_cast<bytes_view>(*this) == static_cast<bytes_view>(o);
    }

    bool operator!=(const managed_bytes& o) const {
        return !(*this == o);
    }

    operator bytes_view() const {
        return { data(), size() };
    }

    bytes_view::value_type& operator[](size_type index) {
        return data()[index];
    }

    const bytes_view::value_type& operator[](size_type index) const {
        return data()[index];
    }

    size_type size() const {
        if (external()) {
            return _u.ptr->size;
        } else {
            return _u.small.size;
        }
    }

    const blob_storage::char_type* begin() const {
        return data();
    }

    const blob_storage::char_type* end() const {
        return data() + size();
    }

    blob_storage::char_type* begin() {
        return data();
    }

    blob_storage::char_type* end() {
        return data() + size();
    }

    bool empty() const {
        return _u.small.size == 0;
    }

    blob_storage::char_type* data() {
        if (external()) {
            return _u.ptr->data;
        } else {
            return _u.small.data;
        }
    }

    const blob_storage::char_type* data() const {
        return const_cast<managed_bytes*>(this)->data();
    }
};

namespace std {

template <>
struct hash<managed_bytes> {
    size_t operator()(managed_bytes v) const {
        return hash<bytes_view>()(v);
    }
};

}
<|endoftext|>


<commit_before>#include "state.h"

#include <iostream>
#include <fstream> //for ifstream, ofstream
#include <thread>
#include <string>

#include "catch.hpp"
#include "compileflags.h"
#include "filesystem.h"
#include "gparse/com.h"

//MACHINE_PATH is calculated in the Makefile and then passed as a define through the make system (ie gcc -DMACHINEPATH='"path"')
#include MACHINE_PATH

//This test must be contained in a class so as to have access to special functions in the State (if it ever needs that)
struct TestClass {
    TestClass() {
        GIVEN("A State with Driver, Filesystem & Com interfaces") {
            //setup code:
            std::ofstream inputFile;
            //disable buffering before opening
            inputFile.rdbuf()->pubsetbuf(0, 0);
            inputFile.open("PRINTIPI_TEST_INPUT", std::fstream::out | std::fstream::trunc);
            std::ifstream outputFile;
            outputFile.rdbuf()->pubsetbuf(0, 0);
            //must open with the ::out flag to automatically create the file if it doesn't exist
            outputFile.open("PRINTIPI_TEST_OUTPUT", std::fstream::in | std::fstream::out | std::fstream::trunc);

            machines::MACHINE driver;
            FileSystem fs("/tmp/");
            gparse::Com com = gparse::Com("PRINTIPI_TEST_INPUT", "PRINTIPI_TEST_OUTPUT");
            State<machines::MACHINE> state(driver, fs, com, true);

            std::thread eventThread([&](){
                state.eventLoop();
            });

            //convenience function to read and wait for the next line from Printipi's output
            auto readLine = [&]() {
                std::string tempRead;
                do {
                    if (outputFile.eof()) {
                        outputFile.clear();
                    }
                } while (!std::getline(outputFile, tempRead));
                return tempRead;
            };

            //@cmd g-code command to send to printer (a newline character will be appended)
            //@expect expected response
            auto sendCommand = [&](const std::string &cmd, const std::string &expect) {
                INFO("Sending command: '" + cmd + "'");
                inputFile << cmd << '\n';
                INFO("It should be acknowledged with '" + expect + "'");
                REQUIRE(readLine() == expect);
            };

            //Verify that the position as reported by the motion planner is near (@x, @y, @z)
            auto verifyPosition = [&](float x, float y, float z) {
            	Vector4f actualPos = state.motionPlanner().actualCartesianPosition();
                INFO("Actual position: " + std::string(actualPos));
                REQUIRE(actualPos.xyz().distance(x, y, z) <= 4);
            };

            bool hasExited = false;
            auto exitOnce = [&]() {
                if (!hasExited) {
                    sendCommand("M0", "ok");
                    eventThread.join();
                    hasExited = true;
                }
            };

            //each WHEN/THEN case corresponds to a single test;
            //the above setup code and the teardown code further below are re-run for EVERY 'when' case.
            //This is also repeated recursively.

            //test homing
            WHEN("The machine is homed") {
                sendCommand("G28", "ok");
            }
            //test G1 movement
            WHEN("The machine is homed & moved to (40, -10, 50)") {
                sendCommand("G28", "ok");
                sendCommand("G1 X40 Y-10 Z50", "ok");
                //test G1 movement
                THEN("The actual position should be near (40, -10, 50)") {
	                exitOnce(); //force the G1 code to complete
	                verifyPosition(40, -10, 50);
            	}
            	//test successive G1 movements
            	WHEN("The machine is moved to another absolute position afterward, (-30, 20, 80) at F=3000") {
            		sendCommand("G1 X-30 Y20 Z80 F3000", "ok");
            		THEN("The actual position should be near (-30, 20, 80)") {
	            		exitOnce(); //force the G1 code to complete
	            		verifyPosition(-30, 20, 80);
            		}
            	}
            	//test G91 (relative) movement
            	WHEN("The machine is moved a RELATIVE amount (-70, 30, 30) at F=3000") {
            		//put into relative movement mode
            		sendCommand("G91", "ok");
            		sendCommand("G1 X-70 Y30 Z30 F3000", "ok");
            		THEN("The actual position should be near (-30, 20, 80)") {
	            		exitOnce(); //force the G1 code to complete
	            		verifyPosition(-30, 20, 80);
            		}
            	}
            	//test comment parsing
            	WHEN("A movement command to (30, 10, 30) is coupled with a comment") {
            		sendCommand("G1 X30 Y10 Z30; HELLO, I am a comment!", "ok");
            		THEN("The actual position should be near (30, 10, 30)") {
	            		exitOnce(); //force the G1 code to complete
	            		verifyPosition(30, 10, 30);
            		}
            	}
            }
            //test automatic homing
            WHEN("The machine is moved to (40, -10, 50) before being homed") {
            	sendCommand("G1 X40 Y-10 Z50", "ok");
                THEN("The actual position should be near (40, -10, 50)") {
	                exitOnce(); //force the G1 code to complete
	                verifyPosition(40, -10, 50);
            	}
            }
            //test automatic homing using G0
            WHEN("The machine is moved to (40, -10, 50) before being homed, using G0 command") {
            	sendCommand("G0 X40 Y-10 Z50", "ok");
                THEN("The actual position should be near (40, -10, 50)") {
	                exitOnce(); //force the G0 code to complete
	                verifyPosition(40, -10, 50);
            	}
            }
            //test using inch coordinates
            WHEN("The machine is moved to (-1, 2, 1) in inches") {
            	//home machine
            	sendCommand("G28", "ok");
            	//put into inches mode
            	sendCommand("G20", "ok");
            	//move absolute
            	sendCommand("G1 X-1 Y2 Z1", "ok");
            	THEN("The actual position (in mm) should be near (-1, 2, 1)*25.4") {
            		exitOnce(); //force the G1 code to complete
            		verifyPosition(-1*25.4, 2*25.4, 1*25.4);
            	}
            }
            //test M18; let steppers move freely
            WHEN("The M18 command is sent to let the steppers move freely") {
            	sendCommand("M18", "ok");
            	//"then the machine shouldn't crash"
            }
            //test gcode printing from file
            WHEN("Commands are read from a file with M32") {
            	//home
            	sendCommand("G28", "ok");
            	//"initialize" the SD card
            	sendCommand("M21", "ok");
            	std::ofstream gfile("/tmp/test-printipi-m32.gcode", std::fstream::out | std::fstream::trunc);
            	//test newlines / whitespace
            	gfile << "\n";
            	gfile << " \t \n";
            	//test comment & G90
            	gfile << "G90 \t ; comment \n";
            	gfile << "G1 X40 Y-10 Z50";
            	AND_WHEN("The file is terminated with a newline") {
	            	//test ending the file WITHOUT a newline
	            	gfile << "\n" << std::flush;
	            	//load & run the file
	            	sendCommand("M32 /test-printipi-m32.gcode", "ok");
	            	THEN("The actual position should be near (40, -10, 50)") {
	            		//note: Printipi is able to monitor multiple file inputs simultaneously,
	            		// if we send it M0 immediately, it may not have read the G1 from the file, and so it will exit
	            		// there is no way to query the status of this file read, so we must just sleep & hopr
	            		std::this_thread::sleep_for(std::chrono::seconds(1));
		                exitOnce(); //force the G0 code to complete
		                verifyPosition(40, -10, 50);
	            	}
	            }
	            AND_WHEN("The file does NOT end on an empty line") {
	            	//test ending the file WITHOUT a newline
	            	gfile << std::flush;
	            	//load & run the file
	            	sendCommand("M32 /test-printipi-m32.gcode", "ok");
	            	THEN("The actual position should be near (40, -10, 50)") {
	            		//note: Printipi is able to monitor multiple file inputs simultaneously,
	            		// if we send it M0 immediately, it may not have read the G1 from the file, and so it will exit
	            		// there is no way to query the status of this file read, so we must just sleep & hopr
	            		std::this_thread::sleep_for(std::chrono::seconds(1));
		                exitOnce(); //force the G0 code to complete
		                verifyPosition(40, -10, 50);
	            	}
	            }
            }
            //test M84; stop idle hold (same as M18)
            WHEN("The M84 command is sent to stop the idle hold") {
            	sendCommand("M84", "ok");
            	//"then the machine shouldn't crash"
            }
            WHEN("The M106 command is sent to activate fans") {
            	sendCommand("M106", "ok");
            	WHEN("The M107 command is sent to disactivate fans") {
            		sendCommand("M107", "ok");
            		//"then the machine shouldn't crash"
            	}
            }
            WHEN("The M106 command is sent to activate fans at a specific PWM between 0.0-1.0") {
            	sendCommand("M106 S0.7", "ok");
            	//"then the machine shouldn't crash"
            }
            WHEN("The M106 command is sent to activate fans at a specific PWM between 0-255") {
            	sendCommand("M106 S64", "ok");
            	//"then the machine shouldn't crash", and S64 should be interpreted as 64/255 duty cycle.
            }
            WHEN("The M117 command is sent") {
            	sendCommand("M117 Hello, World!", "ok");
            	//"then the machine shouldn't crash"
            }
            //Teardown code:
            exitOnce();
        }
    }
};

SCENARIO("State will respond correctly to gcode commands", "[state]") {
    TestClass();
}<commit_msg>Don't write to /tmp folder; that requires root permissions<commit_after>#include "state.h"

#include <iostream>
#include <fstream> //for ifstream, ofstream
#include <thread>
#include <string>

#include "catch.hpp"
#include "compileflags.h"
#include "filesystem.h"
#include "gparse/com.h"

//MACHINE_PATH is calculated in the Makefile and then passed as a define through the make system (ie gcc -DMACHINEPATH='"path"')
#include MACHINE_PATH

//This test must be contained in a class so as to have access to special functions in the State (if it ever needs that)
struct TestClass {
    TestClass() {
        GIVEN("A State with Driver, Filesystem & Com interfaces") {
            //setup code:
            std::ofstream inputFile;
            //disable buffering before opening
            inputFile.rdbuf()->pubsetbuf(0, 0);
            inputFile.open("PRINTIPI_TEST_INPUT", std::fstream::out | std::fstream::trunc);
            std::ifstream outputFile;
            outputFile.rdbuf()->pubsetbuf(0, 0);
            //must open with the ::out flag to automatically create the file if it doesn't exist
            outputFile.open("PRINTIPI_TEST_OUTPUT", std::fstream::in | std::fstream::out | std::fstream::trunc);

            machines::MACHINE driver;
            FileSystem fs("./");
            gparse::Com com = gparse::Com("PRINTIPI_TEST_INPUT", "PRINTIPI_TEST_OUTPUT");
            State<machines::MACHINE> state(driver, fs, com, true);

            std::thread eventThread([&](){
                state.eventLoop();
            });

            //convenience function to read and wait for the next line from Printipi's output
            auto readLine = [&]() {
                std::string tempRead;
                do {
                    if (outputFile.eof()) {
                        outputFile.clear();
                    }
                } while (!std::getline(outputFile, tempRead));
                return tempRead;
            };

            //@cmd g-code command to send to printer (a newline character will be appended)
            //@expect expected response
            auto sendCommand = [&](const std::string &cmd, const std::string &expect) {
                INFO("Sending command: '" + cmd + "'");
                inputFile << cmd << '\n';
                INFO("It should be acknowledged with '" + expect + "'");
                REQUIRE(readLine() == expect);
            };

            //Verify that the position as reported by the motion planner is near (@x, @y, @z)
            auto verifyPosition = [&](float x, float y, float z) {
            	Vector4f actualPos = state.motionPlanner().actualCartesianPosition();
                INFO("Actual position: " + std::string(actualPos));
                REQUIRE(actualPos.xyz().distance(x, y, z) <= 4);
            };

            bool hasExited = false;
            auto exitOnce = [&]() {
                if (!hasExited) {
                    sendCommand("M0", "ok");
                    eventThread.join();
                    hasExited = true;
                }
            };

            //each WHEN/THEN case corresponds to a single test;
            //the above setup code and the teardown code further below are re-run for EVERY 'when' case.
            //This is also repeated recursively.

            //test homing
            WHEN("The machine is homed") {
                sendCommand("G28", "ok");
            }
            //test G1 movement
            WHEN("The machine is homed & moved to (40, -10, 50)") {
                sendCommand("G28", "ok");
                sendCommand("G1 X40 Y-10 Z50", "ok");
                //test G1 movement
                THEN("The actual position should be near (40, -10, 50)") {
	                exitOnce(); //force the G1 code to complete
	                verifyPosition(40, -10, 50);
            	}
            	//test successive G1 movements
            	WHEN("The machine is moved to another absolute position afterward, (-30, 20, 80) at F=3000") {
            		sendCommand("G1 X-30 Y20 Z80 F3000", "ok");
            		THEN("The actual position should be near (-30, 20, 80)") {
	            		exitOnce(); //force the G1 code to complete
	            		verifyPosition(-30, 20, 80);
            		}
            	}
            	//test G91 (relative) movement
            	WHEN("The machine is moved a RELATIVE amount (-70, 30, 30) at F=3000") {
            		//put into relative movement mode
            		sendCommand("G91", "ok");
            		sendCommand("G1 X-70 Y30 Z30 F3000", "ok");
            		THEN("The actual position should be near (-30, 20, 80)") {
	            		exitOnce(); //force the G1 code to complete
	            		verifyPosition(-30, 20, 80);
            		}
            	}
            	//test comment parsing
            	WHEN("A movement command to (30, 10, 30) is coupled with a comment") {
            		sendCommand("G1 X30 Y10 Z30; HELLO, I am a comment!", "ok");
            		THEN("The actual position should be near (30, 10, 30)") {
	            		exitOnce(); //force the G1 code to complete
	            		verifyPosition(30, 10, 30);
            		}
            	}
            }
            //test automatic homing
            WHEN("The machine is moved to (40, -10, 50) before being homed") {
            	sendCommand("G1 X40 Y-10 Z50", "ok");
                THEN("The actual position should be near (40, -10, 50)") {
	                exitOnce(); //force the G1 code to complete
	                verifyPosition(40, -10, 50);
            	}
            }
            //test automatic homing using G0
            WHEN("The machine is moved to (40, -10, 50) before being homed, using G0 command") {
            	sendCommand("G0 X40 Y-10 Z50", "ok");
                THEN("The actual position should be near (40, -10, 50)") {
	                exitOnce(); //force the G0 code to complete
	                verifyPosition(40, -10, 50);
            	}
            }
            //test using inch coordinates
            WHEN("The machine is moved to (-1, 2, 1) in inches") {
            	//home machine
            	sendCommand("G28", "ok");
            	//put into inches mode
            	sendCommand("G20", "ok");
            	//move absolute
            	sendCommand("G1 X-1 Y2 Z1", "ok");
            	THEN("The actual position (in mm) should be near (-1, 2, 1)*25.4") {
            		exitOnce(); //force the G1 code to complete
            		verifyPosition(-1*25.4, 2*25.4, 1*25.4);
            	}
            }
            //test M18; let steppers move freely
            WHEN("The M18 command is sent to let the steppers move freely") {
            	sendCommand("M18", "ok");
            	//"then the machine shouldn't crash"
            }
            //test gcode printing from file
            WHEN("Commands are read from a file with M32") {
            	//home
            	sendCommand("G28", "ok");
            	//"initialize" the SD card
            	sendCommand("M21", "ok");
            	std::ofstream gfile("test-printipi-m32.gcode", std::fstream::out | std::fstream::trunc);
            	//test newlines / whitespace
            	gfile << "\n";
            	gfile << " \t \n";
            	//test comment & G90
            	gfile << "G90 \t ; comment \n";
            	gfile << "G1 X40 Y-10 Z50";
            	AND_WHEN("The file is terminated with a newline") {
	            	//test ending the file WITHOUT a newline
	            	gfile << "\n" << std::flush;
	            	//load & run the file
	            	sendCommand("M32 /test-printipi-m32.gcode", "ok");
	            	THEN("The actual position should be near (40, -10, 50)") {
	            		//note: Printipi is able to monitor multiple file inputs simultaneously,
	            		// if we send it M0 immediately, it may not have read the G1 from the file, and so it will exit
	            		// there is no way to query the status of this file read, so we must just sleep & hopr
	            		std::this_thread::sleep_for(std::chrono::seconds(1));
		                exitOnce(); //force the G0 code to complete
		                verifyPosition(40, -10, 50);
	            	}
	            }
	            AND_WHEN("The file does NOT end on an empty line") {
	            	//test ending the file WITHOUT a newline
	            	gfile << std::flush;
	            	//load & run the file
	            	sendCommand("M32 /test-printipi-m32.gcode", "ok");
	            	THEN("The actual position should be near (40, -10, 50)") {
	            		//note: Printipi is able to monitor multiple file inputs simultaneously,
	            		// if we send it M0 immediately, it may not have read the G1 from the file, and so it will exit
	            		// there is no way to query the status of this file read, so we must just sleep & hopr
	            		std::this_thread::sleep_for(std::chrono::seconds(1));
		                exitOnce(); //force the G0 code to complete
		                verifyPosition(40, -10, 50);
	            	}
	            }
            }
            //test M84; stop idle hold (same as M18)
            WHEN("The M84 command is sent to stop the idle hold") {
            	sendCommand("M84", "ok");
            	//"then the machine shouldn't crash"
            }
            WHEN("The M106 command is sent to activate fans") {
            	sendCommand("M106", "ok");
            	WHEN("The M107 command is sent to disactivate fans") {
            		sendCommand("M107", "ok");
            		//"then the machine shouldn't crash"
            	}
            }
            WHEN("The M106 command is sent to activate fans at a specific PWM between 0.0-1.0") {
            	sendCommand("M106 S0.7", "ok");
            	//"then the machine shouldn't crash"
            }
            WHEN("The M106 command is sent to activate fans at a specific PWM between 0-255") {
            	sendCommand("M106 S64", "ok");
            	//"then the machine shouldn't crash", and S64 should be interpreted as 64/255 duty cycle.
            }
            WHEN("The M117 command is sent") {
            	sendCommand("M117 Hello, World!", "ok");
            	//"then the machine shouldn't crash"
            }
            //Teardown code:
            exitOnce();
        }
    }
};

SCENARIO("State will respond correctly to gcode commands", "[state]") {
    TestClass();
}<|endoftext|>


<commit_before>//
//  libavg - Media Playback Engine.
//  Copyright (C) 2003-2011 Ulrich von Zadow
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
//  Current versions can be found at www.libavg.de
//

#include "WrapHelper.h"

#include "../base/Logger.h"
#include "../base/Exception.h"
#include "../base/MathHelper.h"
#include "../base/ObjectCounter.h"

#include "../player/PythonLogSink.h"
#include "../player/PublisherDefinitionRegistry.h"

#include <boost/version.hpp>

using namespace avg;
using namespace std;
using namespace boost::python;

namespace Vec2Helper
{
    int len(const glm::vec2&)
    {
        return 2;
    }

    float getX(const glm::vec2& pt)
    {
        return pt.x;
    }

    float getY(const glm::vec2& pt)
    {
        return pt.y;
    }

    void setX(glm::vec2& pt, float val)
    {
        pt.x = val;
    }

    void setY(glm::vec2& pt, float val)
    {
        pt.y = val;
    }

    void checkItemRange(int i) {
        if (i != 0 && i != 1) {
            throw std::out_of_range("Index out of range for Point2D. Must be 0 or 1.");
        }
    }

    float getItem(const glm::vec2& pt, int i)
    {
        checkItemRange(i);
        if (i==0) {
            return pt.x;
        } else {
            return pt.y;
        }
    }

    void setItem(glm::vec2& pt, int i, float val)
    {
        checkItemRange(i);
        if (i==0) {
            pt.x = val;
        } else {
            pt.y = val;
        }
    }

    string str(const glm::vec2& pt)
    {
        stringstream st;
        st << "(" << pt.x << "," << pt.y << ")";
        return st.str();
    }

    string repr(const glm::vec2& pt)
    {
        stringstream st;
        st << "avg.Point2D(" << pt.x << "," << pt.y << ")";
        return st.str();
    }

    long getHash(const glm::vec2& pt)
    {
        // Wild guess at what could constitute a good hash function.
        // Will generate very bad hashes if most values are in a range < 0.1,
        // but this is meant for pixel values anyway, right? ;-).
        return long(pt.x*42+pt.y*23);
    }

    glm::vec2 safeGetNormalized(const glm::vec2& pt)
    {
        if (pt.x==0 && pt.y==0) {
            throw Exception(AVG_ERR_OUT_OF_RANGE, "Can't normalize (0,0).");
        } else {
            float invNorm = 1/sqrt(pt.x*pt.x+pt.y*pt.y);
            return glm::vec2(pt.x*invNorm, pt.y*invNorm);
        }
    }

    float getNorm(const glm::vec2& pt)
    {
        return glm::length(pt);
    }

    float vecAngle(const glm::vec2& pt1, const glm::vec2& pt2)
    {
        float angle = fmod((atan2(pt1.y, pt1.x) - atan2(pt2.y, pt2.x)), float(2*M_PI));
        if (angle < 0) {
            angle += 2*M_PI;
        }
        return angle;
    }
}

// The ConstVec2 stuff is there so that vec2 attributes behave sensibly. That is,
// node.pos.x = 30 causes an error instead of failing silently.
ConstVec2::ConstVec2()
{
}

ConstVec2::ConstVec2(const glm::vec2& other)
{
    x = other.x;
    y = other.y;
}

glm::vec2 ConstVec2::toVec2() const
{
    return glm::vec2(x,y);
}

void checkEmptyArgs(const boost::python::tuple &args, int numArgs)
{
    if (boost::python::len(args) != numArgs) {
        throw avg::Exception(AVG_ERR_INVALID_ARGS,
                "Nodes must be constructed using named parameters. Positional parameters are not supported.");
    }
}

template<class VEC2>
struct Vec2_to_python_tuple
{
    static PyObject* convert (VEC2 v)
    {
        return boost::python::incref(boost::python::make_tuple(v.x, v.y).ptr());
    }
};

template<class VEC3>
struct Vec3_to_python_tuple
{
    static PyObject* convert (VEC3 v)
    {
        return boost::python::incref(boost::python::make_tuple(v.x, v.y, v.z).ptr());
    }
};

template<class VEC2, class ATTR>
struct vec2_from_python
{
    vec2_from_python()
    {
        boost::python::converter::registry::push_back(
                &convertible, &construct, boost::python::type_id<VEC2>());
    }

    static void* convertible(PyObject* obj_ptr)
    {
        // Using PySequence_Check here causes infinite recursion.
        if (!PyTuple_Check(obj_ptr) && !PyList_Check(obj_ptr)) {
            return 0;
        }
        if (PySequence_Size(obj_ptr) != 2) {
            return 0;
        }
        return obj_ptr;
    }

    static void construct(PyObject* obj_ptr,
            boost::python::converter::rvalue_from_python_stage1_data* data)
    {
        VEC2 pt;
        PyObject * pEntry = PySequence_GetItem(obj_ptr, 0);
        pt.x = (ATTR)PyFloat_AsDouble(pEntry);
        Py_DECREF(pEntry);
        pEntry = PySequence_GetItem(obj_ptr, 1);
        pt.y = (ATTR)PyFloat_AsDouble(pEntry);
        Py_DECREF(pEntry);
        void* storage = (
                (boost::python::converter::rvalue_from_python_storage<VEC2>*)data)
                    ->storage.bytes;
        new (storage) VEC2(pt);
        data->convertible = storage;
    }
};

template<class VEC3, class ATTR>
struct vec3_from_python
{
    vec3_from_python()
    {
        boost::python::converter::registry::push_back(
                &convertible, &construct, boost::python::type_id<VEC3>());
    }

    static void* convertible(PyObject* obj_ptr)
    {
        if (!PySequence_Check(obj_ptr)) {
            return 0;
        }
        if (PySequence_Size(obj_ptr) != 3) {
            return 0;
        }
        return obj_ptr;
    }

    static void construct(PyObject* obj_ptr,
            boost::python::converter::rvalue_from_python_stage1_data* data)
    {
        VEC3 t;
        PyObject * pEntry = PySequence_GetItem(obj_ptr, 0);
        t.x = (ATTR)PyFloat_AsDouble(pEntry);
        Py_DECREF(pEntry);
        pEntry = PySequence_GetItem(obj_ptr, 1);
        t.y = (ATTR)PyFloat_AsDouble(pEntry);
        Py_DECREF(pEntry);
        pEntry = PySequence_GetItem(obj_ptr, 2);
        t.z = (ATTR)PyFloat_AsDouble(pEntry);
        Py_DECREF(pEntry);
        void* storage = (
                (boost::python::converter::rvalue_from_python_storage<VEC3>*)
                        data)->storage.bytes;
        new (storage) VEC3(t);
        data->convertible = storage;
    }
};

struct UTF8String_to_unicode
{
    static PyObject *convert(const UTF8String & s)
    {
        const char * pStr = s.c_str();
        return PyUnicode_DecodeUTF8(pStr, strlen(pStr), "ignore");
    }
};

struct UTF8String_from_unicode
{
    UTF8String_from_unicode()
    {
        boost::python::converter::registry::push_back(
                &convertible,
                &construct,
                boost::python::type_id<UTF8String>());
    }

    static void* convertible(PyObject* obj_ptr)
    {
        if (!PyUnicode_Check(obj_ptr)) return 0;
        return obj_ptr;
    }

    static void construct(PyObject* obj_ptr,
            boost::python::converter::rvalue_from_python_stage1_data* data)
    {
        PyObject * pPyUTF8 = PyUnicode_AsUTF8String(obj_ptr);
        char * psz = PyString_AsString(pPyUTF8);
        void* storage = (
                (boost::python::converter::rvalue_from_python_storage<UTF8String>*)data)
                        ->storage.bytes;
        new (storage) UTF8String(psz);
        data->convertible = storage;
        Py_DECREF(pPyUTF8);
    }
};

struct UTF8String_from_string
{
    UTF8String_from_string()
    {
        boost::python::converter::registry::push_back(
                &convertible,
                &construct,
                boost::python::type_id<UTF8String>());
    }

    static void* convertible(PyObject* obj_ptr)
    {
        if (!PyString_Check(obj_ptr)) return 0;
        return obj_ptr;
    }

    static void construct(PyObject* obj_ptr,
            boost::python::converter::rvalue_from_python_stage1_data* data)
    {
        const char * psz = PyString_AsString(obj_ptr);
        void* storage = (
                (boost::python::converter::rvalue_from_python_storage<UTF8String>*)data)
                        ->storage.bytes;
        new (storage) UTF8String(psz);
        data->convertible = storage;
    }
};

void exportMessages(object& nodeClass, const string& sClassName)
{
    PublisherDefinitionPtr pPubDef = PublisherDefinitionRegistry::get()
            ->getDefinition(sClassName);
    const vector<MessageID>& messageIDs = pPubDef->getMessageIDs();
    for (unsigned i=0; i<messageIDs.size(); ++i) {
        string sName = messageIDs[i].m_sName;
        nodeClass.attr(sName.c_str()) = messageIDs[i];
    }
};

struct type_info_to_string{
    static PyObject* convert(const std::type_info& info)
    {
        boost::python::object result(ObjectCounter::get()->demangle(info.name()));
        return boost::python::incref(result.ptr());
    }
};

void export_base()
{
    // Exceptions

    translateException<exception>(PyExc_RuntimeError);
    translateException<Exception>(PyExc_RuntimeError);
    to_python_converter< exception, Exception_to_python_exception<exception> >();
    to_python_converter< Exception, Exception_to_python_exception<Exception> >();

    // vec2
    to_python_converter<IntPoint, Vec2_to_python_tuple<IntPoint> >();
    vec2_from_python<IntPoint, int>();
    vec2_from_python<glm::vec2, float>();
    vec2_from_python<ConstVec2, float>();

    // vector<vec2>
    to_python_converter<vector<glm::vec2>, to_list<vector<glm::vec2> > >();
    from_python_sequence<vector<IntPoint>, variable_capacity_policy>();
    from_python_sequence<vector<glm::vec2>, variable_capacity_policy>();

    // vec3
    to_python_converter<glm::ivec3, Vec3_to_python_tuple<glm::ivec3> >();
    to_python_converter<glm::vec3, Vec3_to_python_tuple<glm::vec3> >();
    vec3_from_python<glm::ivec3, int>();
    vec3_from_python<glm::vec3, float>();

    // vector<vec3>
    to_python_converter<vector<glm::ivec3>, to_list<vector<glm::ivec3> > >();
    to_python_converter<vector<glm::vec3>, to_list<vector<glm::vec3> > >();
    from_python_sequence<vector<glm::ivec3>, variable_capacity_policy>();
    from_python_sequence<vector<glm::vec3>, variable_capacity_policy>();

    // string
    to_python_converter<UTF8String, UTF8String_to_unicode>();
    UTF8String_from_unicode();
    UTF8String_from_string();

    to_python_converter<vector<string>, to_list<vector<string> > >();
    from_python_sequence<vector<string>, variable_capacity_policy>();

    from_python_sequence<vector<float>, variable_capacity_policy>();
    from_python_sequence<vector<int>, variable_capacity_policy>();

    to_python_converter<std::type_info, type_info_to_string>();
    //Maps
    to_python_converter<TypeMap, to_dict<TypeMap> >();
}

namespace {
    std::map<PyObject *, LogSinkPtr> m_pyObjectMap;
}

void addPythonLogger(PyObject * self, PyObject * pyLogger)
{
    Logger * logger = Logger::get();
    LogSinkPtr logSink(new PythonLogSink(pyLogger));
    logger->addLogSink(logSink);
    m_pyObjectMap[pyLogger] = logSink;
}

void removePythonLogger(PyObject * self, PyObject * pyLogger)
{
    Logger* logger = Logger::get();
    std::map<PyObject *, LogSinkPtr>::iterator it;
    it = m_pyObjectMap.find(pyLogger);
    if( it !=m_pyObjectMap.end() ){
        logger->removeLogSink(it->second);
        m_pyObjectMap.erase(it);
    }
}

void pytrace(PyObject * self, size_t category, const UTF8String& sMsg, unsigned severity)
{
    avgDeprecationWarning(string("1.8"), "logger.trace",
            "any of the logging convenience functions");
    Logger::get()->trace(sMsg, category, severity);
}

<commit_msg>Fixed #397<commit_after>//
//  libavg - Media Playback Engine.
//  Copyright (C) 2003-2011 Ulrich von Zadow
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
//  Current versions can be found at www.libavg.de
//

#include "WrapHelper.h"

#include "../base/Logger.h"
#include "../base/Exception.h"
#include "../base/MathHelper.h"
#include "../base/ObjectCounter.h"

#include "../player/PythonLogSink.h"
#include "../player/PublisherDefinitionRegistry.h"

#include <boost/version.hpp>

using namespace avg;
using namespace std;
using namespace boost::python;

namespace Vec2Helper
{
    int len(const glm::vec2&)
    {
        return 2;
    }

    float getX(const glm::vec2& pt)
    {
        return pt.x;
    }

    float getY(const glm::vec2& pt)
    {
        return pt.y;
    }

    void setX(glm::vec2& pt, float val)
    {
        pt.x = val;
    }

    void setY(glm::vec2& pt, float val)
    {
        pt.y = val;
    }

    void checkItemRange(int i) {
        if (i != 0 && i != 1) {
            throw std::out_of_range("Index out of range for Point2D. Must be 0 or 1.");
        }
    }

    float getItem(const glm::vec2& pt, int i)
    {
        checkItemRange(i);
        if (i==0) {
            return pt.x;
        } else {
            return pt.y;
        }
    }

    void setItem(glm::vec2& pt, int i, float val)
    {
        checkItemRange(i);
        if (i==0) {
            pt.x = val;
        } else {
            pt.y = val;
        }
    }

    string str(const glm::vec2& pt)
    {
        stringstream st;
        st << "(" << pt.x << "," << pt.y << ")";
        return st.str();
    }

    string repr(const glm::vec2& pt)
    {
        stringstream st;
        st << "avg.Point2D(" << pt.x << "," << pt.y << ")";
        return st.str();
    }

    long getHash(const glm::vec2& pt)
    {
        // Wild guess at what could constitute a good hash function.
        // Will generate very bad hashes if most values are in a range < 0.1,
        // but this is meant for pixel values anyway, right? ;-).
        return long(pt.x*42+pt.y*23);
    }

    glm::vec2 safeGetNormalized(const glm::vec2& pt)
    {
        if (pt.x==0 && pt.y==0) {
            throw Exception(AVG_ERR_OUT_OF_RANGE, "Can't normalize (0,0).");
        } else {
            float invNorm = 1/sqrt(pt.x*pt.x+pt.y*pt.y);
            return glm::vec2(pt.x*invNorm, pt.y*invNorm);
        }
    }

    float getNorm(const glm::vec2& pt)
    {
        return glm::length(pt);
    }

    float vecAngle(const glm::vec2& pt1, const glm::vec2& pt2)
    {
        float angle = fmod((atan2(pt1.y, pt1.x) - atan2(pt2.y, pt2.x)), float(2*M_PI));
        if (angle < 0) {
            angle += 2*M_PI;
        }
        return angle;
    }
}

// The ConstVec2 stuff is there so that vec2 attributes behave sensibly. That is,
// node.pos.x = 30 causes an error instead of failing silently.
ConstVec2::ConstVec2()
{
}

ConstVec2::ConstVec2(const glm::vec2& other)
{
    x = other.x;
    y = other.y;
}

glm::vec2 ConstVec2::toVec2() const
{
    return glm::vec2(x,y);
}

void checkEmptyArgs(const boost::python::tuple &args, int numArgs)
{
    if (boost::python::len(args) != numArgs) {
        throw avg::Exception(AVG_ERR_INVALID_ARGS,
                "Nodes must be constructed using named parameters. Positional parameters are not supported.");
    }
}

template<class VEC2>
struct Vec2_to_python_tuple
{
    static PyObject* convert (VEC2 v)
    {
        return boost::python::incref(boost::python::make_tuple(v.x, v.y).ptr());
    }
};

template<class VEC3>
struct Vec3_to_python_tuple
{
    static PyObject* convert (VEC3 v)
    {
        return boost::python::incref(boost::python::make_tuple(v.x, v.y, v.z).ptr());
    }
};

template<class VEC2, class ATTR>
struct vec2_from_python
{
    vec2_from_python()
    {
        boost::python::converter::registry::push_back(
                &convertible, &construct, boost::python::type_id<VEC2>());
    }

    static void* convertible(PyObject* obj_ptr)
    {
        // Using PySequence_Check here causes infinite recursion.
        if (!PyTuple_Check(obj_ptr) && !PyList_Check(obj_ptr)) {
            return 0;
        }
        if (PySequence_Size(obj_ptr) != 2) {
            return 0;
        }
        return obj_ptr;
    }

    static void construct(PyObject* obj_ptr,
            boost::python::converter::rvalue_from_python_stage1_data* data)
    {
        VEC2 pt;
        PyObject * pEntry = PySequence_GetItem(obj_ptr, 0);
        pt.x = (ATTR)PyFloat_AsDouble(pEntry);
        Py_DECREF(pEntry);
        pEntry = PySequence_GetItem(obj_ptr, 1);
        pt.y = (ATTR)PyFloat_AsDouble(pEntry);
        Py_DECREF(pEntry);
        void* storage = (
                (boost::python::converter::rvalue_from_python_storage<VEC2>*)data)
                    ->storage.bytes;
        new (storage) VEC2(pt);
        data->convertible = storage;
    }
};

template<class VEC3, class ATTR>
struct vec3_from_python
{
    vec3_from_python()
    {
        boost::python::converter::registry::push_back(
                &convertible, &construct, boost::python::type_id<VEC3>());
    }

    static void* convertible(PyObject* obj_ptr)
    {
        if (!PySequence_Check(obj_ptr)) {
            return 0;
        }
        if (PySequence_Size(obj_ptr) != 3) {
            return 0;
        }
        return obj_ptr;
    }

    static void construct(PyObject* obj_ptr,
            boost::python::converter::rvalue_from_python_stage1_data* data)
    {
        VEC3 t;
        PyObject * pEntry = PySequence_GetItem(obj_ptr, 0);
        t.x = (ATTR)PyFloat_AsDouble(pEntry);
        Py_DECREF(pEntry);
        pEntry = PySequence_GetItem(obj_ptr, 1);
        t.y = (ATTR)PyFloat_AsDouble(pEntry);
        Py_DECREF(pEntry);
        pEntry = PySequence_GetItem(obj_ptr, 2);
        t.z = (ATTR)PyFloat_AsDouble(pEntry);
        Py_DECREF(pEntry);
        void* storage = (
                (boost::python::converter::rvalue_from_python_storage<VEC3>*)
                        data)->storage.bytes;
        new (storage) VEC3(t);
        data->convertible = storage;
    }
};

struct UTF8String_to_unicode
{
    static PyObject *convert(const UTF8String & s)
    {
        const char * pStr = s.c_str();
        return PyUnicode_DecodeUTF8(pStr, strlen(pStr), "ignore");
    }
};

struct UTF8String_from_unicode
{
    UTF8String_from_unicode()
    {
        boost::python::converter::registry::push_back(
                &convertible,
                &construct,
                boost::python::type_id<UTF8String>());
    }

    static void* convertible(PyObject* obj_ptr)
    {
        if (!PyUnicode_Check(obj_ptr)) return 0;
        return obj_ptr;
    }

    static void construct(PyObject* obj_ptr,
            boost::python::converter::rvalue_from_python_stage1_data* data)
    {
        PyObject * pPyUTF8 = PyUnicode_AsUTF8String(obj_ptr);
        char * psz = PyString_AsString(pPyUTF8);
        void* storage = (
                (boost::python::converter::rvalue_from_python_storage<UTF8String>*)data)
                        ->storage.bytes;
        new (storage) UTF8String(psz);
        data->convertible = storage;
        Py_DECREF(pPyUTF8);
    }
};

struct UTF8String_from_string
{
    UTF8String_from_string()
    {
        boost::python::converter::registry::push_back(
                &convertible,
                &construct,
                boost::python::type_id<UTF8String>());
    }

    static void* convertible(PyObject* obj_ptr)
    {
        if (!PyString_Check(obj_ptr)) return 0;
        return obj_ptr;
    }

    static void construct(PyObject* obj_ptr,
            boost::python::converter::rvalue_from_python_stage1_data* data)
    {
        const char * psz = PyString_AsString(obj_ptr);
        void* storage = (
                (boost::python::converter::rvalue_from_python_storage<UTF8String>*)data)
                        ->storage.bytes;
        new (storage) UTF8String(psz);
        data->convertible = storage;
    }
};

void exportMessages(object& nodeClass, const string& sClassName)
{
    PublisherDefinitionPtr pPubDef = PublisherDefinitionRegistry::get()
            ->getDefinition(sClassName);
    const vector<MessageID>& messageIDs = pPubDef->getMessageIDs();
    for (unsigned i=0; i<messageIDs.size(); ++i) {
        string sName = messageIDs[i].m_sName;
        nodeClass.attr(sName.c_str()) = messageIDs[i];
    }
};

struct type_info_to_string{
    static PyObject* convert(const std::type_info& info)
    {
        boost::python::object result(ObjectCounter::get()->demangle(info.name()));
        return boost::python::incref(result.ptr());
    }
};

void export_base()
{
    // Exceptions

    translateException<exception>(PyExc_RuntimeError);
    translateException<out_of_range>(PyExc_IndexError);
    translateException<Exception>(PyExc_RuntimeError);
    to_python_converter< exception, Exception_to_python_exception<exception> >();
    to_python_converter< Exception, Exception_to_python_exception<Exception> >();

    // vec2
    to_python_converter<IntPoint, Vec2_to_python_tuple<IntPoint> >();
    vec2_from_python<IntPoint, int>();
    vec2_from_python<glm::vec2, float>();
    vec2_from_python<ConstVec2, float>();

    // vector<vec2>
    to_python_converter<vector<glm::vec2>, to_list<vector<glm::vec2> > >();
    from_python_sequence<vector<IntPoint>, variable_capacity_policy>();
    from_python_sequence<vector<glm::vec2>, variable_capacity_policy>();

    // vec3
    to_python_converter<glm::ivec3, Vec3_to_python_tuple<glm::ivec3> >();
    to_python_converter<glm::vec3, Vec3_to_python_tuple<glm::vec3> >();
    vec3_from_python<glm::ivec3, int>();
    vec3_from_python<glm::vec3, float>();

    // vector<vec3>
    to_python_converter<vector<glm::ivec3>, to_list<vector<glm::ivec3> > >();
    to_python_converter<vector<glm::vec3>, to_list<vector<glm::vec3> > >();
    from_python_sequence<vector<glm::ivec3>, variable_capacity_policy>();
    from_python_sequence<vector<glm::vec3>, variable_capacity_policy>();

    // string
    to_python_converter<UTF8String, UTF8String_to_unicode>();
    UTF8String_from_unicode();
    UTF8String_from_string();

    to_python_converter<vector<string>, to_list<vector<string> > >();
    from_python_sequence<vector<string>, variable_capacity_policy>();

    from_python_sequence<vector<float>, variable_capacity_policy>();
    from_python_sequence<vector<int>, variable_capacity_policy>();

    to_python_converter<std::type_info, type_info_to_string>();
    //Maps
    to_python_converter<TypeMap, to_dict<TypeMap> >();
}

namespace {
    std::map<PyObject *, LogSinkPtr> m_pyObjectMap;
}

void addPythonLogger(PyObject * self, PyObject * pyLogger)
{
    Logger * logger = Logger::get();
    LogSinkPtr logSink(new PythonLogSink(pyLogger));
    logger->addLogSink(logSink);
    m_pyObjectMap[pyLogger] = logSink;
}

void removePythonLogger(PyObject * self, PyObject * pyLogger)
{
    Logger* logger = Logger::get();
    std::map<PyObject *, LogSinkPtr>::iterator it;
    it = m_pyObjectMap.find(pyLogger);
    if( it !=m_pyObjectMap.end() ){
        logger->removeLogSink(it->second);
        m_pyObjectMap.erase(it);
    }
}

void pytrace(PyObject * self, size_t category, const UTF8String& sMsg, unsigned severity)
{
    avgDeprecationWarning(string("1.8"), "logger.trace",
            "any of the logging convenience functions");
    Logger::get()->trace(sMsg, category, severity);
}

<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company.  For licensing terms and
** conditions see http://www.qt.io/terms-conditions.  For further information
** use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, The Qt Company gives you certain additional
** rights.  These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

#include "commandsfile.h"
#include "command_p.h"
#include <coreplugin/dialogs/shortcutsettings.h>

#include <app/app_version.h>

#include <utils/qtcassert.h>

#include <utils/fileutils.h>

#include <QKeySequence>
#include <QFile>
#include <QXmlStreamAttributes>
#include <QXmlStreamWriter>
#include <QXmlStreamReader>
#include <QDebug>
#include <QDateTime>

namespace Core {
namespace Internal {

struct Context // XML parsing context with strings.
{
    Context();

    const QString mappingElement;
    const QString shortCutElement;
    const QString idAttribute;
    const QString keyElement;
    const QString valueAttribute;
};

Context::Context() :
    mappingElement(QLatin1String("mapping")),
    shortCutElement(QLatin1String("shortcut")),
    idAttribute(QLatin1String("id")),
    keyElement(QLatin1String("key")),
    valueAttribute(QLatin1String("value"))
{
}

/*!
    \class CommandsFile
    \brief The CommandsFile class provides a collection of import and export commands.
    \inheaderfile commandsfile.h
*/

/*!
    ...
*/
CommandsFile::CommandsFile(const QString &filename)
    : m_filename(filename)
{

}

/*!
    ...
*/
QMap<QString, QKeySequence> CommandsFile::importCommands() const
{
    QMap<QString, QKeySequence> result;

    QFile file(m_filename);
    if (!file.open(QIODevice::ReadOnly|QIODevice::Text))
        return result;

    Context ctx;
    QXmlStreamReader r(&file);

    QString currentId;

    while (!r.atEnd()) {
        switch (r.readNext()) {
        case QXmlStreamReader::StartElement: {
            const QStringRef name = r.name();
            if (name == ctx.shortCutElement) {
                currentId = r.attributes().value(ctx.idAttribute).toString();
            } else if (name == ctx.keyElement) {
                QTC_ASSERT(!currentId.isEmpty(), return result);
                const QXmlStreamAttributes attributes = r.attributes();
                if (attributes.hasAttribute(ctx.valueAttribute)) {
                    const QString keyString = attributes.value(ctx.valueAttribute).toString();
                    result.insert(currentId, QKeySequence(keyString));
                } else {
                    result.insert(currentId, QKeySequence());
                }
                currentId.clear();
            } // if key element
        } // case QXmlStreamReader::StartElement
        default:
            break;
        } // switch
    } // while !atEnd
    file.close();
    return result;
}

/*!
    ...
*/

bool CommandsFile::exportCommands(const QList<ShortcutItem *> &items)
{
    Utils::FileSaver saver(m_filename, QIODevice::Text);
    if (!saver.hasError()) {
        const Context ctx;
        QXmlStreamWriter w(saver.file());
        w.setAutoFormatting(true);
        w.setAutoFormattingIndent(1); // Historical, used to be QDom.
        w.writeStartDocument();
        w.writeDTD(QLatin1String("<!DOCTYPE KeyboardMappingScheme>"));
        w.writeComment(QString::fromLatin1(" Written by Qt Creator %1, %2. ").
                       arg(QLatin1String(Constants::IDE_VERSION_LONG),
                           QDateTime::currentDateTime().toString(Qt::ISODate)));
        w.writeStartElement(ctx.mappingElement);
        foreach (const ShortcutItem *item, items) {
            const Id id = item->m_cmd->id();
            if (item->m_key.isEmpty()) {
                w.writeEmptyElement(ctx.shortCutElement);
                w.writeAttribute(ctx.idAttribute, id.toString());
            } else {
                w.writeStartElement(ctx.shortCutElement);
                w.writeAttribute(ctx.idAttribute, id.toString());
                w.writeEmptyElement(ctx.keyElement);
                w.writeAttribute(ctx.valueAttribute, item->m_key.toString());
                w.writeEndElement(); // Shortcut
            }
        }
        w.writeEndElement();
        w.writeEndDocument();

        saver.setResult(&w);
    }
    return saver.finalize();
}

} // namespace Internal
} // namespace Core
<commit_msg>Fix importing empty shortcuts from keyboard shortcut settings<commit_after>/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company.  For licensing terms and
** conditions see http://www.qt.io/terms-conditions.  For further information
** use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, The Qt Company gives you certain additional
** rights.  These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

#include "commandsfile.h"
#include "command_p.h"
#include <coreplugin/dialogs/shortcutsettings.h>

#include <app/app_version.h>

#include <utils/qtcassert.h>

#include <utils/fileutils.h>

#include <QKeySequence>
#include <QFile>
#include <QXmlStreamAttributes>
#include <QXmlStreamWriter>
#include <QXmlStreamReader>
#include <QDebug>
#include <QDateTime>

namespace Core {
namespace Internal {

struct Context // XML parsing context with strings.
{
    Context();

    const QString mappingElement;
    const QString shortCutElement;
    const QString idAttribute;
    const QString keyElement;
    const QString valueAttribute;
};

Context::Context() :
    mappingElement(QLatin1String("mapping")),
    shortCutElement(QLatin1String("shortcut")),
    idAttribute(QLatin1String("id")),
    keyElement(QLatin1String("key")),
    valueAttribute(QLatin1String("value"))
{
}

/*!
    \class CommandsFile
    \brief The CommandsFile class provides a collection of import and export commands.
    \inheaderfile commandsfile.h
*/

/*!
    ...
*/
CommandsFile::CommandsFile(const QString &filename)
    : m_filename(filename)
{

}

/*!
    ...
*/
QMap<QString, QKeySequence> CommandsFile::importCommands() const
{
    QMap<QString, QKeySequence> result;

    QFile file(m_filename);
    if (!file.open(QIODevice::ReadOnly|QIODevice::Text))
        return result;

    Context ctx;
    QXmlStreamReader r(&file);

    QString currentId;

    while (!r.atEnd()) {
        switch (r.readNext()) {
        case QXmlStreamReader::StartElement: {
            const QStringRef name = r.name();
            if (name == ctx.shortCutElement) {
                if (!currentId.isEmpty()) // shortcut element without key element == empty shortcut
                    result.insert(currentId, QKeySequence());
                currentId = r.attributes().value(ctx.idAttribute).toString();
            } else if (name == ctx.keyElement) {
                QTC_ASSERT(!currentId.isEmpty(), return result);
                const QXmlStreamAttributes attributes = r.attributes();
                if (attributes.hasAttribute(ctx.valueAttribute)) {
                    const QString keyString = attributes.value(ctx.valueAttribute).toString();
                    result.insert(currentId, QKeySequence(keyString));
                } else {
                    result.insert(currentId, QKeySequence());
                }
                currentId.clear();
            } // if key element
        } // case QXmlStreamReader::StartElement
        default:
            break;
        } // switch
    } // while !atEnd
    file.close();
    return result;
}

/*!
    ...
*/

bool CommandsFile::exportCommands(const QList<ShortcutItem *> &items)
{
    Utils::FileSaver saver(m_filename, QIODevice::Text);
    if (!saver.hasError()) {
        const Context ctx;
        QXmlStreamWriter w(saver.file());
        w.setAutoFormatting(true);
        w.setAutoFormattingIndent(1); // Historical, used to be QDom.
        w.writeStartDocument();
        w.writeDTD(QLatin1String("<!DOCTYPE KeyboardMappingScheme>"));
        w.writeComment(QString::fromLatin1(" Written by Qt Creator %1, %2. ").
                       arg(QLatin1String(Constants::IDE_VERSION_LONG),
                           QDateTime::currentDateTime().toString(Qt::ISODate)));
        w.writeStartElement(ctx.mappingElement);
        foreach (const ShortcutItem *item, items) {
            const Id id = item->m_cmd->id();
            if (item->m_key.isEmpty()) {
                w.writeEmptyElement(ctx.shortCutElement);
                w.writeAttribute(ctx.idAttribute, id.toString());
            } else {
                w.writeStartElement(ctx.shortCutElement);
                w.writeAttribute(ctx.idAttribute, id.toString());
                w.writeEmptyElement(ctx.keyElement);
                w.writeAttribute(ctx.valueAttribute, item->m_key.toString());
                w.writeEndElement(); // Shortcut
            }
        }
        w.writeEndElement();
        w.writeEndDocument();

        saver.setResult(&w);
    }
    return saver.finalize();
}

} // namespace Internal
} // namespace Core
<|endoftext|>