/***************************************************************************
 *   Copyright (c) 2007                                                    *
 *   Joachim Zettler <Joachim.Zettler@gmx.de>                              *
 *                                                                         *
 *   This file is part of the FreeCAD CAx development system.              *
 *                                                                         *
 *   This library is free software; you can redistribute it and/or         *
 *   modify it under the terms of the GNU Library 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 Library General Public License for more details.                  *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this library; see the file COPYING.LIB. If not,    *
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
 *   Suite 330, Boston, MA  02111-1307, USA                                *
 *                                                                         *
 ***************************************************************************/


#include "PreCompiled.h"

//Mesh Stuff
#include <Mod/Mesh/App/Core/MeshKernel.h>
#include <Mod/Mesh/App/Core/TopoAlgorithm.h>
#include <Mod/Mesh/App/Core/Iterator.h>
#include <Mod/Mesh/App/MeshPy.h>
#include <Mod/Mesh/App/Mesh.h>
#include <Mod/Mesh/App/Core/Elements.h>
#include <Mod/Mesh/App/Core/Grid.h>


//FreeCAD Stuff
#include <Base/Builder3D.h>

//OCC Stuff
#include <BRepBuilderAPI_MakeEdge.hxx>
#include <BRepBuilderAPI_MakeFace.hxx>
#include <BRepBuilderAPI_MakeWire.hxx>
#include <IntCurvesFace_ShapeIntersector.hxx>
#include <GCPnts_QuasiUniformDeflection.hxx>
#include <GCPnts_QuasiUniformAbscissa.hxx>
#include <Adaptor2d_Curve2d.hxx>
#include <Adaptor3d_HSurface.hxx>
#include <BRep_Tool.hxx>
#include <TColgp_HArray1OfPnt.hxx>
#include <TColStd_HArray1OfBoolean.hxx>
#include <GeomAPI_Interpolate.hxx>
#include <GeomAPI_PointsToBSpline.hxx>
#include <BRepAlgo_Section.hxx>
#include <BRep_Tool.hxx>
#include <GProp_GProps.hxx>
#include <BRepGProp.hxx>
#include <BRepAdaptor_Curve.hxx>
#include <BRepAdaptor_Surface.hxx>
#include <Geom2d_Curve.hxx>
#include <Geom2dAdaptor_Curve.hxx>
#include <Geom_Surface.hxx>
#include <Geom_Plane.hxx>
#include <Handle_Geom_Plane.hxx>
#include <TopoDS_Face.hxx>
#include <TopoDS_Wire.hxx>
#include <TopoDS.hxx>
#include <TopExp.hxx>
#include <TopExp_Explorer.hxx>
#include <Handle_TColStd_HArray1OfBoolean.hxx>
#include <BSplCLib.hxx>
#include <BRepBuilderAPI_NurbsConvert.hxx>

//Own Stuff
#include "cutting_tools.h"
#include "best_fit.h"
#include "edgesort.h"
#include "WireExplorer.h"
#include "BRepAdaptor_CompCurve2.h"
//#include "MeshInterface.h"







cutting_tools::cutting_tools(TopoDS_Shape aShape, float pitch)
        : m_Shape(aShape),
        m_aMeshAlgo(NULL),
        m_CAD_Mesh_Grid(NULL),
        m_pitch(pitch)
{
    m_ordered_cuts.clear();
    m_all_offset_cuts_high.clear();
    m_all_offset_cuts_low.clear();
    m_face_bboxes.clear();
    m_cad = false;
    m_CAD_Mesh.Clear();
    m_FaceWireMap.clear();
    m_MachiningOrder.clear();
    getShapeBB();
    fillFaceBBoxes();
    classifyShape();
    //checkFlatLevel();
    initializeMeshStuff();
    //BRepBuilderAPI_Sewing aSewer;
    //aSewer.Add(m_Shape);
    //aSewer.
    //Everything should be initialised now

}



cutting_tools::cutting_tools(TopoDS_Shape aShape)
        :m_Shape(aShape),m_aMeshAlgo(NULL),m_CAD_Mesh_Grid(NULL),m_cad(false),m_pitch(0.0)
{
    m_ordered_cuts.clear();
    m_all_offset_cuts_high.clear();
    m_all_offset_cuts_low.clear();
    m_face_bboxes.clear();
    m_CAD_Mesh.Clear();
    m_FaceWireMap.clear();
    m_MachiningOrder.clear();
    getShapeBB();
    fillFaceBBoxes();
    classifyShape();
    //checkFlatLevel();
    initializeMeshStuff();



}


cutting_tools::~cutting_tools()
{
    delete m_aMeshAlgo;
    delete m_CAD_Mesh_Grid;
}


bool cutting_tools::SetMachiningOrder(const TopoDS_Face &aFace, float x,float y,float z)
{
    std::pair<Base::Vector3f,TopoDS_Face> aTempPair;
    Base::Vector3f aPoint(x,y,z);
    aTempPair.first = aPoint;
    aTempPair.second = aFace;
    m_MachiningOrder.push_back(aTempPair);
    return true;
}


bool cutting_tools::fillFaceWireMap()
{
    std::vector<std::pair<Base::Vector3f,TopoDS_Face> >::iterator MOrderIt;
    for (MOrderIt = m_MachiningOrder.begin();MOrderIt != m_MachiningOrder.end(); ++MOrderIt)
    {
        //Here we take the flat areas and put them in a map where we can easily search for the biggest, smallest Wire
        std::pair<TopoDS_Face,std::map<Base::BoundBox3f,TopoDS_Wire,BoundBox3f_Less> >aTempPair;
        aTempPair.second.clear();
        std::pair<Base::BoundBox3f,TopoDS_Wire> aTempBBoxPair;
        aTempPair.first = MOrderIt->second;
        //Jetzt durch das Face gehen und die Wires rausfiltern
        TopExp_Explorer Explore_Face;
        Explore_Face.Init(MOrderIt->second,TopAbs_WIRE);
        //If there is no Wire -> return
        if (!Explore_Face.More()) return false;

        //Jetzt alle Wires in die map schieben
        for (Explore_Face.ReInit();Explore_Face.More();Explore_Face.Next())
        {
            aTempBBoxPair.first = getWireBBox(TopoDS::Wire(Explore_Face.Current()));
            aTempBBoxPair.second = TopoDS::Wire(Explore_Face.Current());
            aTempPair.second.insert(aTempBBoxPair);
        }
        m_FaceWireMap.insert(aTempPair);
    }
    return true;
}




bool cutting_tools::getShapeBB()
{
    //Die Cascade-Bounding Box funktioniert nicht richtig
    //Es wird dort wohl ne BoundingBox um dasKontrollnetz gelegt
    //Deshalb wird jetzt kurz das Shape tesseliert und dann die Bounding Box direkt ausgelesen
    best_fit::Tesselate_Shape(m_Shape,m_CAD_Mesh,float(0.1));
    Base::BoundBox3f aBoundBox = m_CAD_Mesh.GetBoundBox();
    m_maxlevel = aBoundBox.MaxZ;
    m_minlevel = aBoundBox.MinZ;

 //   //Hier testen wir noch ein paar OpenMesh Funktionen
 //   typedef OpenMesh::DefaultTraits MyTraits;
 //   typedef OpenMesh::TriMesh_ArrayKernelT<MyTraits>  MyMesh;

	//////// Mesh::Interface erbt von MyMesh , so dass das Objekt 'mesh' direkt in den OpenMesh-Funktionen genutzt werden kann
 //  Mesh::Interface<MyMesh> mesh(m_CAD_Mesh);
	////// proceed with mesh, e.g. refining
 //  MyMesh::EdgeIter e_it,e_end(mesh.edges_end());
 //  MyMesh::EdgeHandle eh;
 //  MyMesh::Scalar dist;
 //  MyMesh::Point startPoint,midPoint,endPoint;

 //  for ( e_it = mesh.edges_begin() ; e_it != e_end ; ++e_it )
 //  {
 //       eh = e_it.handle();
 //       dist = mesh.calc_edge_length(eh);  //get the length of the current edge
 //       if(dist>2.6) //Split the Edge now
 //       {
 //        
 //           //Get the Start and EndPoints of the Edge
 //          
 //       }

 //   }
 ////// throw away all tagged edges
 ////mesh.garbage_collection(); //Function from OpenMesh
 //mesh.release(m_CAD_Mesh);    //Function from MeshInterface to convert the Input Kernel back


   
    /* Hier ist die alte OCC BoundingBox Funktion
    Bnd_Box currentBBox;
    Standard_Real XMin, YMin, ZMin, XMax, YMax, ZMax;
    BRepBndLib::Add(m_shape, currentBBox );
       currentBBox.SetGap(1000.0);
       currentBBox.Get(XMin, YMin, ZMin, XMax, YMax, ZMax);
    */
    return true;
}


//bool cutting_tools::fillFaceMeshMap()
//{
// if(m_CAD_Mesh.size()>





bool cutting_tools::fillFaceBBoxes()
{
    TopoDS_Face atopo_surface;
    TopExp_Explorer Explorer;
    MeshCore::MeshKernel aFaceMesh;
    Base::BoundBox3f aBoundBox;
    Explorer.Init(m_Shape,TopAbs_FACE);
    for (;Explorer.More();Explorer.Next())
    {
        aFaceMesh.Clear();
        aBoundBox.Flush();
        atopo_surface = TopoDS::Face (Explorer.Current());
        best_fit::Tesselate_Face(atopo_surface,aFaceMesh,float(0.1));
        aBoundBox = aFaceMesh.GetBoundBox();
        aBoundBox.Enlarge(2.0);
        std::pair<TopoDS_Face,Base::BoundBox3f> tempPair;
        tempPair.first = atopo_surface;
        tempPair.second = aBoundBox;
        m_face_bboxes.push_back(tempPair);
    }
    return true;
}
//Hier ist die alte Version um die Bounding Box zu bestimmen
//      aAdaptor_Surface.Initialize(atopo_surface);
//      Standard_Real FirstUParameter, LastUParameter,FirstVParameter,LastVParameter;
//      gp_Pnt aSurfacePoint;
//   FirstUParameter = aAdaptor_Surface.FirstUParameter();
//      LastUParameter  = aAdaptor_Surface.LastUParameter();
//      FirstVParameter = aAdaptor_Surface.FirstVParameter();
//      LastVParameter = aAdaptor_Surface.LastVParameter();
//float urange = LastUParameter - FirstUParameter;
//float vrange = LastVParameter - FirstVParameter;
////Jetzt ein 10x10 Grid pro Face machen und so die BBox bestimmen
////Zunächst mal die BoundingBox auf Null setzen
//Bnd_Box currentBBox;
//
//for(int i=0;i<10;++i)
//{
// for(int j=0;j<10;++j)
// {
//  aAdaptor_Surface.D0(FirstUParameter+((urange/10)*i),FirstVParameter+((vrange/10)*j),aSurfacePoint);
//  currentBBox.Update(aSurfacePoint.X(),aSurfacePoint.Y(),aSurfacePoint.Z());
// }
//}
////Jetzt noch nen Rand drumherum bauen
//currentBBox.SetGap(0.5);
////Jetzt die aktuelle BoundingBox mit dem aktuellen Face in einen vector pushen
//std::pair<TopoDS_Face,Bnd_Box> tempPair;
//tempPair.first = atopo_surface;
//tempPair.second = currentBBox;
//m_face_bboxes.push_back(tempPair);
////aVertex = BRepBuilderAPI_MakeVertex(aSurfacePoint);
////BRepBndLib::Add(aVertex, currentBBox );
//currentBBox.SetVoid();


bool cutting_tools::checkPointinFaceBB(const gp_Pnt &aPnt,const Base::BoundBox3f &aBndBox)
{
    if ((aPnt.X()>aBndBox.MinX) && (aPnt.X()<aBndBox.MaxX) && (aPnt.Y()>aBndBox.MinY) && (aPnt.Y()<aBndBox.MaxY) && (aPnt.Z()>aBndBox.MinZ) && (aPnt.Z()<aBndBox.MaxZ))
        return true;

    return false;
}


bool cutting_tools::initializeMeshStuff()
{
    m_CAD_Mesh_Grid = new MeshCore::MeshFacetGrid(m_CAD_Mesh);
    m_aMeshAlgo = new MeshCore::MeshAlgorithm(m_CAD_Mesh);
    return true;
}


bool cutting_tools::arrangecuts_ZLEVEL()
{
    //We have to fill the required maps first
    fillFaceWireMap();
    //Zunächst wieder checken ob CAD oder nicht
    if (m_cad==false)
    {
        //Cast um die Nachkommastellen wegzuschneiden
        int cutnumber = (int)fabs((m_maxlevel-m_minlevel)/m_pitch);
        //m_pitch leicht korrigieren um wirklich auf die letzte Ebene zu kommen
        m_pitch = fabs(m_maxlevel-m_minlevel)/cutnumber;
        //Jetzt die Schnitte machen. Die höchste Ebene fällt weg, da hier noch kein Blech gedrückt wird
        float z_level,z_level_corrected;
        TopoDS_Shape aCutShape;
        for (int i=1;i<=cutnumber;++i)
        {
            //Jetzt schneiden (die oberste Ebene auslassen)
            z_level = m_maxlevel-(i*m_pitch);
            z_level_corrected = z_level;
            cut(z_level,m_minlevel,aCutShape,z_level_corrected);
            //cut_Mesh(z_level,m_minlevel,result,z_level_corrected);
            //Jetzt die resultierende Wire in einen Vector pushen
            std::pair<float,TopoDS_Shape> tempPair;
            tempPair.first = z_level_corrected;
            tempPair.second = aCutShape;
            m_ordered_cuts.push_back(tempPair);
        }
        return true;
    }
    //Wenn wir mehrere Faces haben oder eine CAD-Geometrie vorhanden ist
    else
    {
        //Über die MachiningOrder wird jetzt die Cutting-Folge festgelegt
        std::vector<std::pair<Base::Vector3f,TopoDS_Face> >::iterator MOrderIt;
        if (m_MachiningOrder.size()<2) return false; //Did not select at least two Levels
        //Now take two levels and perform the Cutting Stuff
        for (MOrderIt = m_MachiningOrder.begin();MOrderIt != m_MachiningOrder.end(); ++MOrderIt)
        {
            float temp_max = MOrderIt->first.z;
            //the check if MOrderIt+1 != end is performend at the bottom of the function
            float temp_min = (MOrderIt+1)->first.z;
            //set the direction flags
            if (temp_max> temp_min)
                m_direction = true;
            else m_direction = false;
            //Now we cut from temp_max to temp_min, without the flat areas at the top and bottom

            int cutnumber = (int)fabs((temp_max-temp_min)/m_UserSettings.level_distance);
            //m_pitch correction to really reach temp_min
            m_UserSettings.level_distance = fabs(temp_max-temp_min)/cutnumber;

            float z_level,z_level_corrected;
            TopoDS_Shape aCutShape;
            //Now lets cut and push the highest and lowest level also into the results vector
            std::pair<float,TopoDS_Shape> tempPair;
            //Highest Level push_back (only the proper Wire)

            tempPair.first = MOrderIt->first.z;
            //get the Wire with the smallest Bounding Box if we go from top to bottom for the highest area
            tempPair.second = m_FaceWireMap.find(MOrderIt->second)->second.begin()->second;

            m_ordered_cuts.push_back(tempPair);
            for (int i=1;i<cutnumber;++i)
            {
                if (m_direction)
                    z_level = temp_max-(i*m_UserSettings.level_distance);
                else
                    z_level = temp_max+(i*m_UserSettings.level_distance);
                z_level_corrected = z_level;
                cut(z_level,temp_min, aCutShape,z_level_corrected);
                if (z_level_corrected != z_level)
                    std::cout << "Somehow we couldnt cut" << std::endl;
                //Jetzt nur das gewünschte Resultat in den vector schieben (von oben nach unten große usw.)
                Edgesort aCuttingShapeSorter(aCutShape);
                tempPair.first = z_level_corrected;
                if (m_direction)
                    tempPair.second = aCuttingShapeSorter.GetDesiredCutShape(2);//With an Index !=1 we get the biggest one
                else
                    tempPair.second = aCuttingShapeSorter.GetDesiredCutShape(1);
                m_ordered_cuts.push_back(tempPair);
            }
            //Now push the lowest level into the ordered_cuts_vector.
            //if there is no more area to cut take the biggest wire of the bottom face
            //otherwise take the biggest wire but continue
            if (MOrderIt+2 == m_MachiningOrder.end())
            {
                tempPair.first = (MOrderIt+1)->first.z;
                tempPair.second = m_FaceWireMap.find((MOrderIt+1)->second)->second.rbegin()->second;
                m_ordered_cuts.push_back(tempPair);
                break; //No more combination to find
            }
            else
            {
                tempPair.first = (MOrderIt+1)->first.z;
                tempPair.second = m_FaceWireMap.find((MOrderIt+1)->second)->second.rbegin()->second;
                m_ordered_cuts.push_back(tempPair);
            }
        }
    }
    return true;
}








//}
//      std::map<float,std::map<Base::BoundBox3f,TopoDS_Wire,BoundBox3f_Less> >::iterator zl_wire_it;
//      //Wir holen uns jetzt den nächsten Z-Level raus. Wir müssen was kleineres
//      //wie den höchsten Wert nehmen sonst gibt er immer den höchsten Wert aus
//      zl_wire_it = m_zl_wire_combination.upper_bound(temp_max-0.1);
//      if (zl_wire_it->first == temp_max)
//      {
//          cout << "Tja, es gibt wohl nur eine flache Area";
//          temp_min = m_minlevel;
//      }
//      //Wenn es mehrere flache Bereiche gibt muss ich nochmal weitermachen
//      else
//      {
//          temp_min = zl_wire_it->first;
//          cout << "Mehrere Areas erkannt";
//      }
//      //Jetzt schnippeln von temp_max bis temp_min
//      int cutnumber = (int)fabs((temp_max-temp_min)/m_pitch);
//      //m_pitch leicht korrigieren um wirklich auf die letzte Ebene zu kommen
//      m_pitch = fabs(temp_max-temp_min)/cutnumber;
//      //Jetzt die Schnitte machen. Die höchste Ebene fällt weg, da hier noch kein Blech gedrückt wird
//      float z_level,z_level_corrected;
//      TopoDS_Shape aCutShape;
//      //Jetzt schneiden (die oberste Ebene auslassen)
//      for (int i=1;i<=20;++i)
//      {
//          z_level = temp_max-(i*m_pitch);
//          z_level_corrected = z_level;
//          //cut_Mesh(z_level,m_minlevel,result,z_level_corrected);
//          //Jetzt die resultierenden Points in den vector schieben
//          //std::pair<float,std::list<std::vector<Base::Vector3f> > > tempPair;
//          //tempPair.first = z_level_corrected;
//          //tempPair.second = result;
//          //m_ordered_cuts.push_back(tempPair);
//          cut(z_level,temp_min, aCutShape,z_level_corrected);
//          //Jetzt die gefüllte Wire in den vector schieben
//          std::pair<float,TopoDS_Shape> tempPair;
//          tempPair.first = z_level_corrected;
//          tempPair.second = aCutShape;
//          m_ordered_cuts.push_back(tempPair);
//      }
//      return true;
//  }
//    return false;
//}



//bool cutting_tools::checkFlatLevel()
//{
//    //Falls keine CAD-Geometrie da ist, gleich wieder rausspringen
//
//    if (m_cad==false) return false;
//
//    TopoDS_Face atopo_surface;
//    BRepAdaptor_Surface aAdaptor_Surface;
//    TopExp_Explorer Explorer;
//
//
//    for (Explorer.Init(m_Shape,TopAbs_FACE);Explorer.More();Explorer.Next())
//    {
//        atopo_surface = TopoDS::Face (Explorer.Current());
//        aAdaptor_Surface.Initialize(atopo_surface);
//        Standard_Real FirstUParameter, LastUParameter,FirstVParameter,LastVParameter;
//        gp_Pnt first,second,third;
//        gp_Vec first_u,first_v,second_u,second_v,third_u,third_v, Norm_first,Norm_second,Norm_third,Norm_average;
//        double u_middle,v_middle;
//        /*
//        Generate three random point on the surface to get the surface normal and decide wether its a
//        planar face or not
//        */
//        FirstUParameter = aAdaptor_Surface.FirstUParameter();
//        LastUParameter  = aAdaptor_Surface.LastUParameter();
//        FirstVParameter = aAdaptor_Surface.FirstVParameter();
//        LastVParameter = aAdaptor_Surface.LastVParameter();
//        u_middle = sqrt((FirstUParameter - LastUParameter)*(FirstUParameter - LastUParameter))/2;
//        v_middle = sqrt((FirstVParameter - LastVParameter)*(FirstVParameter - LastVParameter))/2;
//        aAdaptor_Surface.D1(sqrt((FirstUParameter-u_middle)*(FirstUParameter-u_middle))/2,sqrt((FirstVParameter-v_middle)*(FirstVParameter-v_middle))/2,first,first_u,first_v);
//        aAdaptor_Surface.D1(sqrt((u_middle)*(u_middle))/2,sqrt((v_middle)*(v_middle))/2,second,second_u,second_v);
//        aAdaptor_Surface.D1(sqrt((FirstUParameter+u_middle)*(FirstUParameter+u_middle))/2,sqrt((FirstVParameter+v_middle)*(FirstVParameter+v_middle))/2,third,third_u,third_v);
//        //Get Surface normal as Cross-Product between two Vectors
//        Norm_first = first_u.Crossed(first_v);
//        Norm_first.Normalize();
//        Norm_second = second_u.Crossed(second_v);
//        Norm_second.Normalize();
//        Norm_third = third_u.Crossed(third_v);
//        Norm_third.Normalize();
//        //Evaluate average normal vector
//        Norm_average.SetX((Norm_first.X()+Norm_second.X()+Norm_third.X())/3);
//        Norm_average.SetY((Norm_first.Y()+Norm_second.Y()+Norm_third.Y())/3);
//        Norm_average.SetZ((Norm_first.Z()+Norm_second.Z()+Norm_third.Z())/3);
//        Norm_average.Normalize();
//        gp_Vec z_normal(0,0,1);
//        gp_Vec z_normal_opposite(0,0,-1);
//        if (Norm_average.IsEqual(z_normal,0.01,0.01) || Norm_average.IsEqual(z_normal_opposite,0.01,0.01))
//        {
//            cout << "Einen flachen Bereich gefunden";
//            std::pair<float,std::map<Base::BoundBox3f,TopoDS_Wire,BoundBox3f_Less> >aTempPair;
//            aTempPair.second.clear();
//            std::pair<Base::BoundBox3f,TopoDS_Wire> aTempBBoxPair;
//
//            //Z-Wert vom flachen Bereich in ein temporäres pair pushen
//            aTempPair.first = ((first.Z()+second.Z()+third.Z())/3);
//            //Jetzt durch das Face gehen und die Wires rausfiltern
//            TopExp_Explorer Explore_Face;
//            Explore_Face.Init(atopo_surface,TopAbs_WIRE);
//            //If there is no Wire -> return
//            if (!Explore_Face.More()) return false;
//
//            //Jetzt alle Wires in die map schieben
//            for (Explore_Face.ReInit();Explore_Face.More();Explore_Face.Next())
//            {
//                aTempBBoxPair.first = getWireBBox(TopoDS::Wire(Explore_Face.Current()));
//                aTempBBoxPair.second = TopoDS::Wire(Explore_Face.Current());
//                aTempPair.second.insert(aTempBBoxPair);
//                //aTempPair.first ist ja noch auf dem gleichen Z-Wert wie vorher, deswegen muss da nichts abgepasst werden
//            }
//            m_zl_wire_combination.insert(aTempPair);
//        }
//    }
//
//    return true;
//}

//bool cutting_tools::projectWireToSurface(const TopoDS_Wire &aWire,const TopoDS_Shape &aShape,std::vector<projectPointContainer> &aContainer);
//{
// //make your wire looks like a curve to other algorithm and generate Points to offset the curve
// aContainer.clear();
// BRepAdaptor_CompCurve2 wireAdaptor(aWire);
// GCPnts_QuasiUniformDeflection aProp(wireAdaptor,0.01);
// int numberofpoints = aProp.NbPoints();
// Standard_Real Umin,Vmin,lowestdistance;
// TopoDS_Face atopo_surface,atopo_surface_shortest;
// Handle_Geom_Surface geom_surface;
//
// //Now project the points to the surface and get surface normal.
// for (int i=1;i<=numberofpoints;++i)
// {
//  lowestdistance=200;
//  //Aktuellen Punkt holen
//  gp_Pnt currentPoint = aProp.Value(i);
//  projectPointContainer aTempContainer;
//  //checken auf welches Face wir projezieren könnnen
//  for(m_face_bb_it = m_face_bboxes.begin();m_face_bb_it!=m_face_bboxes.end();++m_face_bb_it)
//  {
//   //Wenn der aktuelle Punkt in der BBox enthalten ist, dann machen wir mit der Projection weiter
//   if(checkPointinFaceBB(aProp.Value(i),m_face_bb_it->second))
//   {
//    atopo_surface = m_face_bb_it->first;
//    geom_surface = BRep_Tool::Surface(atopo_surface);
//    GeomAPI_ProjectPointOnSurf aPPS(currentPoint,geom_surface,0.001);
//    //Wenn nichts projeziert werden kann, gehts gleich weiter zum nächsten Face bzw. der nächsten BBox
//    if (aPPS.NbPoints() == 0) continue;
//    //Jetzt muss das aktuelle Face gespeichert werden, da es eventuell das face ist, welches am nächsten ist
//    double length = aPPS.LowerDistance();
//    if(lowestdistance>length)
//    {
//     lowestdistance=length;
//     atopo_surface_shortest = atopo_surface;
//     aPPS.LowerDistanceParameters (Umin,Vmin);
//    }
//   }
//  }
//  gp_Vec Uvec,Vvec,normalVec;
//  geom_surface = BRep_Tool::Surface(atopo_surface_shortest);
//  //Das Face welches am nächsten ist in der temp-struct speichern
//  aTempContainer.face = atopo_surface_shortest;
//  geom_surface->D1(Umin,Vmin,aTempContainer.point,Uvec,Vvec);
//  //Jetzt den Normalenvector auf die Fläche ausrechnen
//  normalVec = Uvec;
//  normalVec.Cross(Vvec);
//  normalVec.Normalize();
//  //Jetzt ist die Normale berechnet und auch normalisiert
//  //Jetzt noch checken ob die Normale auch wirklich wie alle anderen auf die gleiche Seite zeigt.
//  //dazu nur checken ob der Z-Wert der Normale größer Null ist (dann im 1.und 2. Quadranten)
//  if(normalVec.Z()<0) normalVec.Multiply(-1.0);
//  //Mal kurz den Winkel zur Grund-Ebene ausrechnen
//  aTempContainer.normalvector = normalVec;
//  aContainer.push_back(aTempContainer);
//
// }
// return true;
//}


TopoDS_Wire cutting_tools::ordercutShape(const TopoDS_Shape &aShape)
{
    //Bisher funktioniert das Ganze nur für Schnitte welche nur einmal rundherum laufen und noch nicht für Inseln
    TopExp_Explorer exploreShape;
    exploreShape.Init(aShape,TopAbs_EDGE);
    int k=0;
    for (; exploreShape.More(); exploreShape.Next()) //erstmal schauen wieviele Edges wir haben
    {
        k++;
    }
    //Jetzt die Edges alle in eine Wire packen (mit Add) und schauen ob ein Fehler kommt.
    //Wenn ja, dann die nächste Edge. Solange bis alle Edges drin sind.

    if (k<1)
    {
        exploreShape.ReInit();
        BRepBuilderAPI_MakeWire mkWire(TopoDS::Edge(exploreShape.Current()));
        return mkWire.Wire();
    }
    else //Nur wenn mehr als eine Edge vorhanden ist
    {
        BRepBuilderAPI_MakeWire mkWire; //WireContainer aufbauen
        std::vector<edge_container> listofedge_tmp,listofedge;
        std::vector<edge_container>::iterator it_edge;
        edge_container a_edge_container; //Definiert in der stuff.h
        listofedge.clear();
        listofedge_tmp.clear();
        exploreShape.ReInit();
        //Edge-Liste füllen
        for (; exploreShape.More(); exploreShape.Next())
        {
            a_edge_container.edge = TopoDS::Edge(exploreShape.Current());
            TopoDS_Vertex V1,V2;
            TopExp::Vertices(a_edge_container.edge,V1,V2);
            a_edge_container.firstPoint = BRep_Tool::Pnt(V1);;
            a_edge_container.lastPoint = BRep_Tool::Pnt(V2);
            listofedge.push_back(a_edge_container);
        }

        gp_Pnt lastpointoflastedge,firstpointoflastedge;
        while (listofedge.empty() == false )
        {
            listofedge_tmp.clear();
            for (it_edge = listofedge.begin();it_edge!=listofedge.end();++it_edge)
            {
                mkWire.Add((*it_edge).edge);
                if (mkWire.IsDone())
                {
                    lastpointoflastedge = (*it_edge).lastPoint;
                    firstpointoflastedge = (*it_edge).firstPoint;
                }
                else
                {
                    //Abstände ausrechnen
                    double abstand1=sqrt((*it_edge).firstPoint.SquareDistance(lastpointoflastedge));
                    double abstand2=sqrt((*it_edge).lastPoint.SquareDistance(lastpointoflastedge));
                    double abstand3=sqrt((*it_edge).firstPoint.SquareDistance(firstpointoflastedge));
                    double abstand4=sqrt((*it_edge).lastPoint.SquareDistance(firstpointoflastedge));
                    if (abstand1<0.5)
                    {
                        //Neue Edge erzeugen welche vom letzten Endpunkt zum aktuellen Startpunkt geht
                        BRepBuilderAPI_MakeEdge newedge(lastpointoflastedge,(*it_edge).firstPoint);
                        mkWire.Add(newedge);
                        mkWire.Add((*it_edge).edge);
                        lastpointoflastedge = (*it_edge).lastPoint;
                        firstpointoflastedge = (*it_edge).firstPoint;
                    }
                    else if (abstand2<0.5)
                    {
                        //Neue Edge erzeugen welche vom letzten Endpunkt zum aktuellen Startpunkt geht
                        BRepBuilderAPI_MakeEdge newedge(lastpointoflastedge,(*it_edge).lastPoint);
                        mkWire.Add(newedge);
                        mkWire.Add((*it_edge).edge);
                        lastpointoflastedge = (*it_edge).lastPoint;
                        firstpointoflastedge = (*it_edge).firstPoint;

                    }

                    else if (abstand3<0.5)
                    {
                        //Neue Edge erzeugen welche vom letzten Endpunkt zum aktuellen Startpunkt geht
                        BRepBuilderAPI_MakeEdge newedge(firstpointoflastedge,(*it_edge).firstPoint);
                        mkWire.Add(newedge);
                        mkWire.Add((*it_edge).edge);
                        lastpointoflastedge = (*it_edge).lastPoint;
                        firstpointoflastedge = (*it_edge).firstPoint;
                    }
                    else if (abstand4<0.5)
                    {
                        //Neue Edge erzeugen welche vom letzten Endpunkt zum aktuellen Startpunkt geht
                        BRepBuilderAPI_MakeEdge newedge(firstpointoflastedge,(*it_edge).lastPoint);
                        mkWire.Add(newedge);
                        mkWire.Add((*it_edge).edge);
                        lastpointoflastedge = (*it_edge).lastPoint;
                        firstpointoflastedge = (*it_edge).firstPoint;
                    }
                    else
                    {
                        listofedge_tmp.push_back(*it_edge);
                    }
                }
            }
            listofedge = listofedge_tmp;
        }
        return mkWire.Wire();
    }

}


//bool cutting_tools::OffsetWires_Standard(float radius) //Version wo nur in X,Y-Ebene verschoben wird
//{
// Base::Builder3D build;
// std::ofstream outfile;
// outfile.open("c:/atest.out");
//
//
// //Die ordered_cuts sind ein Vector wo für jede Ebene ein Pair existiert
// for(m_ordered_cuts_it = m_ordered_cuts.begin();m_ordered_cuts_it!=m_ordered_cuts.end();++m_ordered_cuts_it)
// {
//  float current_z_level = m_ordered_cuts_it->first;
//  std::vector<gp_Pnt> finalPoints;
//  finalPoints.clear();
//  Standard_Real Umin,Vmin,lowestdistance;
//  TopoDS_Face atopo_surface,atopo_surface_shortest;
//  Handle_Geom_Surface geom_surface;
//  int i=0;
//  for (;avector_it!=aPolyline_it->end();++avector_it)
//  {
//   i++;
//   lowestdistance=200;
//   //Aktuellen Punkt holen
//   gp_Pnt currentPoint(avector_it->x,avector_it->y,avector_it->z);
//   gp_Pnt nearest_Point;
//   //checken auf welches Face wir projezieren könnnen
//   for(m_face_bb_it = m_face_bboxes.begin();m_face_bb_it!=m_face_bboxes.end();++m_face_bb_it)
//   {
//    //Wenn der aktuelle Punkt in der BBox enthalten ist, dann machen wir mit der Projection weiter
//    if(checkPointinFaceBB(currentPoint,m_face_bb_it->second))
//    {
//     atopo_surface = m_face_bb_it->first;
//     geom_surface = BRep_Tool::Surface(atopo_surface);
//     GeomAPI_ProjectPointOnSurf aPPS(currentPoint,geom_surface,0.001);
//     //Wenn nichts projeziert werden kann, gehts gleich weiter zum nächsten Face bzw. der nächsten BBox
//     if (aPPS.NbPoints() == 0) continue;
//     //Jetzt muss das aktuelle Face gespeichert werden, da es eventuell das face ist, welches am nächsten ist
//     double length = aPPS.LowerDistance();
//     if(lowestdistance>length)
//     {
//      lowestdistance=length;
//      atopo_surface_shortest = atopo_surface;
//      //aPPS.LowerDistanceParameters (Umin,Vmin);
//      nearest_Point = aPPS.NearestPoint();
//     }
//    }
//   }
//   //Für eine saubere Projection auf der aktuellen Ebene wird jetzt der Richtungsvector
//   //aus dem projezierten Punkt und dem Ursprungspunkt gebildet
//   //und dieser dann hergenommen um damit nochmal in dessen Richtung zu projezieren
//   gp_Vec aVec(currentPoint,nearest_Point);
//   aVec.Normalize();
//   aVec.SetZ(0.0);
//   gp_Dir aDir(aVec);
//   gp_Lin aLine(currentPoint,aDir);
//   IntCurvesFace_ShapeIntersector aFaceIntSect;
//   aFaceIntSect.Load(m_Shape, 0.001);
//   aFaceIntSect.PerformNearest(aLine,-RealLast(), +RealLast());
//   //Jetzt holen wir uns auf der Fläche den U und V Wert um dann D1 und D2 bestimmen zu können
//   gp_Pnt projectedPoint,OffsetPoint;
//   gp_Pnt testpoint,testpoint2;
//   float abstand,abstand_old;
//   int number = aFaceIntSect.NbPnt();
//   abstand_old = 100;
//   for(int k=0;k<number;++k)
//   {
//    testpoint = aFaceIntSect.Pnt(k+1);
//    abstand = testpoint.Distance(currentPoint);
//    if(abstand<abstand_old)
//    {
//     Umin = aFaceIntSect.UParameter(k+1);
//     Vmin = aFaceIntSect.VParameter(k+1);
//     atopo_surface_shortest = aFaceIntSect.Face(k+1);
//     abstand_old = abstand;
//    }
//   }
//   gp_Vec Uvec,Vvec,normalVec,projPointVec,z_normale;
//   geom_surface = BRep_Tool::Surface(atopo_surface_shortest);
//   geom_surface->D1(Umin,Vmin,projectedPoint,Uvec,Vvec);
//   normalVec = Uvec;
//   normalVec.Cross(Vvec);
//   normalVec.Normalize();
//   //Jetzt ist die Surface-Normale berechnet und auch normalisiert
//   //Jetzt noch checken ob die Normale auch wirklich wie alle anderen auf die gleiche Seite zeigt.
//   //dazu nur checken ob der Z-Wert der Normale größer Null ist (dann im 1.und 2. Quadranten)
//   if(normalVec.Z()<0) normalVec.Multiply(-1.0);
//   //Jetzt die Normale auf die Radiuslänge verlängern
//   normalVec.Multiply(radius);
//   //Jetzt die Z-Komponente auf 0 setzen
//   normalVec.SetZ(0.0);
//   //float abstand = currentPoint.Distance(projectedPoint);
//   //if(abstand>0.2)
//   //{cout<<"error"<<endl;}
//   projPointVec.SetXYZ(projectedPoint.XYZ());
//   OffsetPoint.SetXYZ((projPointVec + normalVec).XYZ());
//   OffsetPoint.SetZ(projectedPoint.Z()+radius);//Den Radius noch dazu addieren
//   //Aktuellen OffsetPoint setzen
//   finalPoints.push_back(OffsetPoint);
//
//   //Base::Vector3f offsetPoint,projectPoint;
//   //offsetPoint.x=OffsetPoint.X();offsetPoint.y=OffsetPoint.Y();offsetPoint.z=OffsetPoint.Z();
//   //projectPoint.x=projectedPoint.X();projectPoint.y=projectedPoint.Y();projectPoint.z=projectedPoint.Z();
//   //build.addSingleArrow(projectPoint,offsetPoint);
//   build.addSinglePoint(OffsetPoint.X(),OffsetPoint.Y(),OffsetPoint.Z());
//   outfile << currentPoint.X() <<","<<currentPoint.Y()<<","<<currentPoint.Z()<<","<< projectedPoint.X() <<","<<projectedPoint.Y()<<","<<projectedPoint.Z()<<","<< OffsetPoint.X() <<","<<OffsetPoint.Y()<<","<<OffsetPoint.Z()<<","<<normalVec.X() <<","<<normalVec.Y()<<","<<normalVec.Z()<< std::endl;
//  }
//
//
// //  outfile << projectedPoint.X() <<","<<projectedPoint.Y()<<","<<projectedPoint.Z()<<std::endl;
//   //Jetzt die aktuelle Kurve als BSpline interpolieren
//  //check for intersections due to wrong offseted points
//  checkPointIntersection(finalPoints);
//  std::vector<gp_Pnt> finalPointscorrected;
//  finalPointscorrected.clear();
//  checkPointDistance(finalPoints,finalPointscorrected);
//  Handle(TColgp_HArray1OfPnt) finalOffsetPoints = new TColgp_HArray1OfPnt(1, finalPointscorrected.size());
//  for(int t=0;t<finalPointscorrected.size();++t)
//  {
//   finalOffsetPoints->SetValue(t+1,finalPointscorrected[t]);
//  }
//  GeomAPI_Interpolate aNoPeriodInterpolate(finalOffsetPoints, Standard_False, Precision::Confusion());
//  aNoPeriodInterpolate.Perform();
//  Handle_Geom_BSplineCurve aCurve(aNoPeriodInterpolate.Curve());
//   //check results
//  if (!aNoPeriodInterpolate.IsDone()) return false;
//  m_all_offset_cuts_high.push_back(aCurve);
// }
//
//
//
//
//
// build.saveToFile("c:/output.iv");
// outfile.close();
//
//
//
//
//
//return true;
//
//}

//bool cutting_tools::checkPointDistance(std::vector<gp_Pnt> &finalPoints,std::vector<gp_Pnt> &output)
//{
// std::vector<gp_Pnt>::iterator aPntIt,atempit;
// output.clear();
// double square_precision = Precision::Confusion()*Precision::Confusion();
// int i;
// for(aPntIt=finalPoints.begin();aPntIt!=finalPoints.end();++aPntIt)
// {
//  atempit=aPntIt;
//  output.push_back(*aPntIt);
//  i=1;
//  while(((*(aPntIt+i)).SquareDistance(*aPntIt))< square_precision && (aPntIt+i)!=finalPoints.end())
//  {
//   i++;
//   atempit++;
//  }
//  aPntIt = atempit;
//
//
//
// }
//
// return true;
//}

//bool cutting_tools::checkPointIntersection(std::vector<projectPointContainer> &finalPoints)
//{
// //Hier wird gecheckt ob die Punkte wirklich alle in der richtigen Reihenfolge vorliegen
// std::vector<projectPointContainer>::iterator aPntIt;
// double distance,distance_old;
// projectPointContainer nearestPointStruct;
// int k;
// for(unsigned int j=0;j<finalPoints.size();++j)
// {
//  distance_old = 100;
//  for(int i=1;i<30;++i)
//  {
//   //Wenn wir schon fast am Ende sind oder schon bald, dann rausspringen
//   if((j+i)>=finalPoints.size()) break;
//
//   distance = (finalPoints[j+i].point).SquareDistance(finalPoints[j].point);
//   if(distance<distance_old)
//   {
//    //Speichern wo wir den nächsten Punkt gefunden haben
//    k=i;
//    distance_old = distance;
//   }
//  }
//  //Jetzt checken ob der nächste Punkt bereits der gesuchte ist
//  if(k==1)
//  {
//   continue;
//  }
//  else
//  {
//   //Jetzt den Punkteaustausch vornehmen
//   nearestPointStruct = finalPoints[j+k];
//   finalPoints[j+k] = finalPoints[j+1];
//   finalPoints[j+1] = nearestPointStruct;
//  }
// }
//
//
//
// return true;
//}
//
//



Handle_Geom_BSplineCurve cutting_tools::InterpolateOrderedPoints(Handle(TColgp_HArray1OfPnt) InterpolationPoints, const bool direction)
{
    //Handle_TColStd_HArray1OfBoolean TangentFlags = new TColStd_HArray1OfBoolean(1,InterpolationPoints->Length());
    //for (int j=1;j<=TangentFlags->Length();++j)
    //    TangentFlags->SetValue(j,true);

    GeomAPI_Interpolate aBSplineInterpolation(InterpolationPoints, Standard_False, Precision::Confusion());
    //aBSplineInterpolation.Load(Tangents,TangentFlags);
    aBSplineInterpolation.Perform();
    Handle_Geom_BSplineCurve aBSplineCurve(aBSplineInterpolation.Curve());
    //check results
    //if (!aBSplineInterpolation.IsDone()) cout << "error" << endl;

    return aBSplineCurve;
}


bool cutting_tools::CheckEdgeTangency(const TopoDS_Edge& edge1, const TopoDS_Edge& edge2)
{
    //Get the vertex which is equal to both edges
    TopoDS_Vertex CommonVertex;
    TopExp::CommonVertex(edge1,edge2,CommonVertex);
    if (CommonVertex.IsNull())
    {
        cout << "Not possible to calculate Tangency" << endl;
        return false;
    }
    gp_Pnt CommonPoint = BRep_Tool::Pnt(CommonVertex);
    BRepAdaptor_Curve aCurve1(edge1);
    BRepAdaptor_Curve aCurve2(edge2);
    gp_Vec Tangent1,Tangent2;
    gp_Pnt P;
    if (aCurve1.Value(aCurve1.FirstParameter()).IsEqual(CommonPoint,0.1))
        aCurve1.D1(aCurve1.FirstParameter(),P,Tangent1);
    else if (aCurve1.Value(aCurve1.LastParameter()).IsEqual(CommonPoint,0.1))
        aCurve1.D1(aCurve1.LastParameter(),P,Tangent1);
    if (aCurve2.Value(aCurve2.FirstParameter()).IsEqual(CommonPoint,0.1))
        aCurve2.D1(aCurve1.FirstParameter(),P,Tangent2);
    else if (aCurve2.Value(aCurve2.LastParameter()).IsEqual(CommonPoint,0.1))
        aCurve2.D1(aCurve1.LastParameter(),P,Tangent2);

    //Now we calculate the angle between the two Tangents and if the angle is below 10° then we say its continuous
    double angle = Tangent1.Angle(Tangent2);
    //Angle must be between +/-5°
    if (angle<(5.0/180.0*D_PI) || angle>(175.0/180.0*D_PI))
        return true;
    else
        return false;
}

bool cutting_tools::CheckforLastPoint(const gp_Pnt& lastPoint, int &start_index,int &start_array,const std::vector<std::vector<std::pair<gp_Pnt,double> > >& MasterPointsStorage)
{
    float dist,distold = FLT_MAX;
    for (unsigned int t=0;t<MasterPointsStorage.size();++t)
    {
        for (unsigned int k=0;k<MasterPointsStorage[t].size();k++)
        {
            dist = float(MasterPointsStorage[t][k].first.SquareDistance(lastPoint));
            if (dist<distold)
            {
                start_index = k;
                start_array = t;
                distold = dist;
            }
        }
    }
    return true;
}

bool cutting_tools::CheckforLastPoint(const gp_Pnt& lastPoint, int &start_index,int &start_array,const std::vector<std::vector<gp_Pnt> >& SlavePointsStorage)
{
    float dist,distold = FLT_MAX;
    for (unsigned int t=0;t<SlavePointsStorage.size();++t)
    {
        for (unsigned int k=0;k<SlavePointsStorage[t].size();k++)
        {
            dist = float(SlavePointsStorage[t][k].SquareDistance(lastPoint));
            if (dist<distold)
            {
                start_index = k;
                start_array = t;
                distold = dist;
            }
        }
    }
    return true;
}

bool cutting_tools::CheckforLastPoint(  const gp_Pnt& lastPoint,
                                        int &start_index_master,
                                        int &start_array_master,
                                        int &start_index_slave,
                                        int &start_array_slave,
                                        const std::vector<std::vector<std::pair<gp_Pnt,double> > >& MasterPointsStorage,
                                        const std::vector<std::vector<gp_Pnt> >& SlavePointsStorage
                                     )
{
    float dist,distold = FLT_MAX;
    for (unsigned int t=0;t<MasterPointsStorage.size();++t)
    {
        for (unsigned int k=0;k<MasterPointsStorage[t].size();k++)
        {
            dist = float(MasterPointsStorage[t][k].first.SquareDistance(lastPoint));
            if (dist<distold)
            {
                start_index_master = k;
                start_array_master = t;
                distold = dist;
            }
        }
    }
    distold = FLT_MAX;
    for (unsigned int t=0;t<SlavePointsStorage.size();++t)
    {
        for (unsigned int k=0;k<SlavePointsStorage[t].size();k++)
        {
            dist = float(SlavePointsStorage[t][k].SquareDistance(lastPoint));
            if (dist<distold)
            {
                start_index_slave = k;
                start_array_slave = t;
                distold = dist;
            }
        }
    }
    return true;
}
bool cutting_tools::OffsetWires_Standard() //Version wo nur in X,Y-Ebene verschoben wird
{
    //for debugging issues
    std::ofstream anoutput1,anoutput2;
    anoutput1.open("c:/master_output.txt");
    anoutput2.open("c:/slave_output.txt");
    std::vector<std::pair<float,TopoDS_Shape> >::iterator current_flat_level;
    gp_Pnt lastPoint(0.0,0.0,0.0); //Initialize the first Point to the Origin
    current_flat_level = m_ordered_cuts.begin();
    bool slave_is_wire= false; //Necessary if the slave is a wire while the master is not...
    //Nicht beim höchsten Anfangen, da wir den nicht mit dem Master fahren wollen
    for (m_ordered_cuts_it = m_ordered_cuts.begin()+1;m_ordered_cuts_it!=m_ordered_cuts.end();++m_ordered_cuts_it)
    {
        std::vector<std::pair<gp_Pnt,double> > MasterPointContainer;
        std::vector<std::vector<std::pair<gp_Pnt,double> > > MasterPointsStorage;
        std::vector<gp_Pnt> SlavePointContainer;
        std::vector<std::vector<gp_Pnt> > SlavePointsStorage;
        std::vector<std::vector<gp_Pnt> >::iterator anIterator1;
        std::vector<std::vector<std::pair<gp_Pnt,double> > >::iterator anIterator2;
        MasterPointsStorage.clear();
        SlavePointsStorage.clear();
        MasterPointContainer.clear();
        SlavePointContainer.clear();
        //Now we have to select which strategy to choose
        //if the current levels is bigger,the same, or less then the previous one
        if (m_ordered_cuts_it->first<(m_ordered_cuts_it-1)->first)
        {
            //Master is calculated as Usual, Slave follows the Master
            //Check if current Level has got a Wire
            if (m_ordered_cuts_it->second.ShapeType() == TopAbs_WIRE)
            {
                WireExplorer aWireExplorer(TopoDS::Wire(m_ordered_cuts_it->second));
                for (aWireExplorer.Init();aWireExplorer.More();aWireExplorer.Next())
                {
                    BRepAdaptor_Curve curveAdaptor(aWireExplorer.Current());
                    GCPnts_QuasiUniformAbscissa aProp(curveAdaptor,100);
                    for (int i=1;i<=aProp.NbPoints();++i)
                    {
                        std::pair<gp_Pnt,double> aTempPair;
                        //Check the direction
                        if (aWireExplorer.Current().Orientation() != TopAbs_REVERSED)
                            curveAdaptor.D0(aProp.Parameter(i),aTempPair.first);
                        else curveAdaptor.D0(aProp.Parameter(aProp.NbPoints()-i+1),aTempPair.first);
                        //aTempPair.first.SetZ(aTempPair.first.Z() + m_UserSettings.master_radius);
						aTempPair.first.SetZ(aTempPair.first.Z() );
                        aTempPair.second = 0.0; //Initialize of Angle
                        //checken ob der neue Punkt zu nahe am alten ist. Wenn ja, dann kein push_back
                        if (MasterPointContainer.size()>0 && (MasterPointContainer.rbegin()->first.SquareDistance(aTempPair.first)>(Precision::Confusion()*Precision::Confusion())))
                        {
                            MasterPointContainer.push_back(aTempPair);
                            anoutput1 << aTempPair.first.X() <<","<< aTempPair.first.Y() <<","<< aTempPair.first.Z()<<std::endl;
                            aTempPair.first.SetZ(aTempPair.first.Z() - m_UserSettings.master_radius - m_UserSettings.slave_radius - m_UserSettings.sheet_thickness);
                            SlavePointContainer.push_back(aTempPair.first);
                            anoutput2 << aTempPair.first.X() <<","<< aTempPair.first.Y() <<","<< aTempPair.first.Z()<<std::endl;
                        }
                        else if (MasterPointContainer.empty())
                        {
                            MasterPointContainer.push_back(aTempPair);
                            anoutput1 << aTempPair.first.X() <<","<< aTempPair.first.Y() <<","<< aTempPair.first.Z()<<std::endl;
                            aTempPair.first.SetZ(aTempPair.first.Z() - m_UserSettings.master_radius - m_UserSettings.slave_radius - m_UserSettings.sheet_thickness);
                            SlavePointContainer.push_back(aTempPair.first);
                            anoutput2 << aTempPair.first.X() <<","<< aTempPair.first.Y() <<","<< aTempPair.first.Z()<<std::endl;
                        }
                    }
                    //Now Interpolate the Points only if we have a non-continuous edge or if we are finished
                    //with all edges
                    bool tangency = true;
                    //If there are more Edges in the wire
                    if (aWireExplorer.MoreEdge())
                    {
                        tangency = CheckEdgeTangency(aWireExplorer.Current(),aWireExplorer.NextEdge());
                        if (!tangency)
                        {
                            //Store all the PointClouds in a StorageVector to arrange the Robot-Movement afterwards
                            MasterPointsStorage.push_back(MasterPointContainer);
                            SlavePointsStorage.push_back(SlavePointContainer);
                            MasterPointContainer.clear();
                            SlavePointContainer.clear();
                        }
                        else continue;
                    }
                    else
                    {
                        MasterPointsStorage.push_back(MasterPointContainer);
                        SlavePointsStorage.push_back(SlavePointContainer);
                        MasterPointContainer.clear();
                        SlavePointContainer.clear();
                    }
                }
                //Now check the point-cloud with the shortest distance to "lastPoint"
                int start_index = 0,start_array=0;
                CheckforLastPoint(lastPoint,start_index,start_array,MasterPointsStorage);
                //Now Interpolate the PointClouds...Cloud by Cloud
                //First we have to divide the first PointCloud as it is for sure that we start
                //Somewhere in the middle of it. We will then insert the points at the current start
                if (MasterPointsStorage.size() == 1) //If we have only one PointCloud
                {
                    //To make sure that the start and end point are the same we have to use a flag
                    bool first = true;
                    for (unsigned int i=start_index;i<MasterPointsStorage.begin()->size();i++)
                    {
                        //We skip the Endpoint as it may be the same point as the start point
                        if (i+1 == MasterPointsStorage.begin()->size())
                        {
                            i=-1;
                            first = false;
                            continue;
                        }
                        MasterPointContainer.push_back(MasterPointsStorage[0][i]);
                        SlavePointContainer.push_back(SlavePointsStorage[0][i]);
                        if (!first && i == start_index)
                            break;
                    }
                    //Now lets interpolate the Point Cloud
                    Handle(TColgp_HArray1OfPnt) InterpolationPointsMaster = new TColgp_HArray1OfPnt(1, MasterPointContainer.size());
                    Handle(TColgp_HArray1OfPnt) InterpolationPointsSlave = new TColgp_HArray1OfPnt(1, SlavePointContainer.size());
                    for (unsigned int t=0;t<MasterPointContainer.size();++t)
                    {
                        InterpolationPointsMaster->SetValue(t+1,MasterPointContainer[t].first);
                        InterpolationPointsSlave->SetValue(t+1,SlavePointContainer[t]);
                    }
                    m_all_offset_cuts_high.push_back(InterpolateOrderedPoints(InterpolationPointsMaster,true));
                    m_all_offset_cuts_low.push_back(InterpolateOrderedPoints(InterpolationPointsSlave,true));
                    //Store the last point
                    lastPoint = MasterPointContainer.rbegin()->first;
                }
                else
                {
                    anIterator1 = SlavePointsStorage.begin();
                    anIterator2 = MasterPointsStorage.begin();
                    MasterPointContainer.clear();
                    SlavePointContainer.clear();
                    for (int i=0;i<=start_index;i++)
                    {
                        MasterPointContainer.push_back(MasterPointsStorage[start_array][i]);
                        SlavePointContainer.push_back(SlavePointsStorage[start_array][i]);
                    }
                    MasterPointsStorage.insert((anIterator2+start_array+1),MasterPointContainer);
                    SlavePointsStorage.insert((anIterator1+start_array+1),SlavePointContainer);
                    MasterPointContainer.clear();
                    SlavePointContainer.clear();
                    //Reinitialize the Iterators
                    anIterator1 = SlavePointsStorage.begin();
                    anIterator2 = MasterPointsStorage.begin();
                    for (unsigned int i=start_index;i<MasterPointsStorage[start_array].size();i++)
                    {
                        MasterPointContainer.push_back(MasterPointsStorage[start_array][i]);
                        SlavePointContainer.push_back(SlavePointsStorage[start_array][i]);
                    }
                    MasterPointsStorage.insert((anIterator2+start_array+2),MasterPointContainer);
                    SlavePointsStorage.insert((anIterator1+start_array+2),SlavePointContainer);
                    //Reinitialize the Iterators
                    anIterator1 = SlavePointsStorage.begin();
                    anIterator2 = MasterPointsStorage.begin();
                    //Delete the Original PointCloud
                    MasterPointsStorage.erase(anIterator2+start_array);
                    SlavePointsStorage.erase(anIterator1+start_array);
                    MasterPointContainer.clear();
                    SlavePointContainer.clear();
                    //Now lets interpolate the Point Clouds
                    //Start at start_array+1 as the insert operations used us to do it like that
                    for (unsigned int j=start_array+1;j<MasterPointsStorage.size();++j)
                    {
                        Handle(TColgp_HArray1OfPnt) InterpolationPointsMaster = new TColgp_HArray1OfPnt(1, MasterPointsStorage[j].size());
                        Handle(TColgp_HArray1OfPnt) InterpolationPointsSlave = new TColgp_HArray1OfPnt(1, SlavePointsStorage[j].size());
                        for (unsigned int t=0;t<MasterPointsStorage[j].size();++t)
                        {
                            InterpolationPointsMaster->SetValue(t+1,MasterPointsStorage[j][t].first);
                            InterpolationPointsSlave->SetValue(t+1,SlavePointsStorage[j][t]);
                        }
                        m_all_offset_cuts_high.push_back(InterpolateOrderedPoints(InterpolationPointsMaster,true));
                        m_all_offset_cuts_low.push_back(InterpolateOrderedPoints(InterpolationPointsSlave,true));
                        if (j+1==MasterPointsStorage.size())
                        {
                            j=-1;
                            continue;
                        }
                        if (j+1==start_array+1)
                            break;

                    }
                    lastPoint = MasterPointsStorage[start_array].rbegin()->first;
                }
            }
            else //If the current Curve is no Wire (Mode <)
            {
                Edgesort aCutShapeSorter(m_ordered_cuts_it->second);
                for (aCutShapeSorter.Init();aCutShapeSorter.More();aCutShapeSorter.Next())
                {
                    //Get the PCurve and the GeomSurface
                    Handle_Geom2d_Curve a2DCurve;
                    Handle_Geom_Surface aSurface;
                    TopLoc_Location aLoc;
                    TopoDS_Edge anEdge;
                    double first2,last2;
                    bool reversed = false;
                    BRep_Tool::CurveOnSurface(aCutShapeSorter.Current(),a2DCurve,aSurface,aLoc,first2,last2);
                    //Jetzt noch die resultierende Surface und die Curve sauber drehen
                    //(vielleicht wurde ja das TopoDS_Face irgendwie gedreht oder die TopoDS_Edge)
                    if (aCutShapeSorter.Current().Orientation() == TopAbs_REVERSED)
                        reversed = true;

                    BRepAdaptor_Curve aCurveAdaptor(aCutShapeSorter.Current());
                    GCPnts_QuasiUniformAbscissa aPointGenerator(aCurveAdaptor,200);
                    int PointSize = aPointGenerator.NbPoints();
                    //Now get the surface normal to the generated points
                    for (int i=1;i<=PointSize;++i)
                    {
                        std::pair<gp_Pnt,double> PointContactPair;
                        gp_Pnt2d a2dParaPoint;
                        gp_Pnt aSurfacePoint;
                        TopoDS_Face aFace;
                        gp_Vec Uvec,Vvec,normalVec;
                        //If the curve is reversed we also have to reverse the point direction
                        if (reversed) a2DCurve->D0(aPointGenerator.Parameter(PointSize-i+1),a2dParaPoint);
                        else a2DCurve->D0(aPointGenerator.Parameter(i),a2dParaPoint);
                        GeomAdaptor_Surface aGeom_Adaptor(aSurface);
                        int t = aGeom_Adaptor.GetType();
                        aGeom_Adaptor.D1(a2dParaPoint.X(),a2dParaPoint.Y(),aSurfacePoint,Uvec,Vvec);
                        //Jetzt den Normalenvector auf die Fläche ausrechnen
                        normalVec = Uvec;
                        normalVec.Cross(Vvec);
                        normalVec.Normalize();
                        //Jetzt ist die Normale berechnet und auch normalisiert
                        //Jetzt noch checken ob die Normale auch wirklich auf die saubere Seite zeigt
                        //dazu nur checken ob der Z-Wert der Normale größer Null ist (dann im 1.und 2. Quadranten)
                        if (normalVec.Z()<0) normalVec.Multiply(-1.0);

                        //Mal kurz den Winkel zur Grund-Ebene ausrechnen
                        gp_Vec planeVec(normalVec.X(),normalVec.Y(),0.0);
                        //Den Winkel
                        PointContactPair.second = normalVec.Angle(planeVec);
                        gp_Vec NormalVecSlave = normalVec;
                        gp_Pnt SlavePoint;
                        //Jetzt die Z-Komponente auf 0 setzen
                        //normalVec.SetZ(0.0);
                        normalVec.Normalize();
                        //Jetzt die Normale mit folgender Formel multiplizieren für den Master
                        //double multiply = m_UserSettings.master_radius*(1-sin(PointContactPair.second))/cos(PointContactPair.second);
                        double multiply = m_UserSettings.master_radius;
                        normalVec.Multiply(multiply);
                        //und hier für den Slave
                        NormalVecSlave.Normalize();
                        multiply = m_UserSettings.sheet_thickness+m_UserSettings.slave_radius;
                        NormalVecSlave.Multiply(multiply);
                        //Jetzt die Richtung umdrehen
                        NormalVecSlave.Multiply(-1.0);
                        //Jetzt den OffsetPunkt berechnen
                        PointContactPair.first.SetXYZ(aSurfacePoint.XYZ());
						//PointContactPair.first.SetXYZ(aSurfacePoint.XYZ() + normalVec.XYZ());
                        SlavePoint.SetXYZ(aSurfacePoint.XYZ() + NormalVecSlave.XYZ());
                        //PointContactPair.first.SetZ(PointContactPair.first.Z() + m_UserSettings.master_radius);
                        //Damit wir keine Punkte bekommen die zu nahe beieinander liegen
                        //Den letzten hinzugefügten Punkt suchen
                        if (MasterPointContainer.size()>0 && (MasterPointContainer.rbegin()->first.SquareDistance(PointContactPair.first)>0.001))
                        {
                            MasterPointContainer.push_back(PointContactPair);
                            SlavePointContainer.push_back(SlavePoint);
                            anoutput1 << PointContactPair.first.X() <<","<< PointContactPair.first.Y() <<","<< PointContactPair.first.Z()<<std::endl;
                            anoutput2 << SlavePoint.X() <<","<< SlavePoint.Y() <<","<< SlavePoint.Z()<<std::endl;
                        }
                        else if (MasterPointContainer.empty())
                        {
                            MasterPointContainer.push_back(PointContactPair);
                            SlavePointContainer.push_back(SlavePoint);
                            anoutput1 << PointContactPair.first.X() <<","<< PointContactPair.first.Y() <<","<< PointContactPair.first.Z()<<std::endl;
                            anoutput2 << SlavePoint.X() <<","<< SlavePoint.Y() <<","<< SlavePoint.Z()<<std::endl;
                        }
                    }
                }
                //Before we Output anything we check if the lowest Coordinate can still touch the "currently" highest flat area and if yes, we have to adapt the Slave toolpath
                for (unsigned int k=0;k<SlavePointContainer.size();++k)
                {
                    if ((SlavePointContainer[k].Z()+m_UserSettings.slave_radius)>(current_flat_level->first-m_UserSettings.sheet_thickness))
                    {
                        slave_is_wire = true;
                        TopoDS_Wire aWire = TopoDS::Wire(current_flat_level->second);
                        BRepAdaptor_CompCurve2 wireAdaptor(aWire);
                        GCPnts_QuasiUniformAbscissa aProp(wireAdaptor,1000);
                        SlavePointContainer.clear();
                        for (int i=1;i<=aProp.NbPoints();++i)
                        {
                            gp_Pnt SlaveOffsetPoint;
                            wireAdaptor.D0(aProp.Parameter(i),SlaveOffsetPoint);
                            SlaveOffsetPoint.SetZ(SlaveOffsetPoint.Z() - m_UserSettings.slave_radius - m_UserSettings.sheet_thickness);
                            //checken ob der neue Punkt zu nahe am alten ist. Wenn ja, dann kein push_back
                            if (SlavePointContainer.size()>0 && (SlavePointContainer.rbegin()->SquareDistance(SlaveOffsetPoint)>(0.001)))
                            {
                                SlavePointContainer.push_back(SlaveOffsetPoint);
                                anoutput2 << SlaveOffsetPoint.X() <<","<< SlaveOffsetPoint.Y() <<","<< SlaveOffsetPoint.Z()<<std::endl;
                            }
                            else if (SlavePointContainer.empty())
                            {
                                SlavePointContainer.push_back(SlaveOffsetPoint);
                                anoutput2 << SlaveOffsetPoint.X() <<","<< SlaveOffsetPoint.Y() <<","<< SlaveOffsetPoint.Z()<<std::endl;
                            }
                        }
                        break;
                    }
                }
                MasterPointsStorage.clear();
                SlavePointsStorage.clear();
                MasterPointsStorage.push_back(MasterPointContainer);
                SlavePointsStorage.push_back(SlavePointContainer);
                int start_index_master = 0,start_array_master=0,start_index_slave=0,start_array_slave=0;
                CheckforLastPoint(lastPoint,start_index_master,start_array_master,start_index_slave,start_array_slave,MasterPointsStorage,SlavePointsStorage);
                //If the Slave is a Wire 'slave_is_wire ==true' then we have to take care of two independent point clouds
                if (!slave_is_wire)
                {
                    //First we have to divide the first PointCloud as it is for sure that we start
                    //Somewhere in the middle of it. We will then insert the points at the current start
                    MasterPointContainer.clear();
                    SlavePointContainer.clear();
                    bool first = true;
                    for (unsigned int i=start_index_master;i<MasterPointsStorage.begin()->size();i++)
                    {
                        //We skip the Endpoint as it may be the same point as the start point
                        if (i+1 == MasterPointsStorage.begin()->size())
                        {
                            first = false;
                            i=-1;
                            continue;
                        }
                        MasterPointContainer.push_back(MasterPointsStorage[0][i]);
                        SlavePointContainer.push_back(SlavePointsStorage[0][i]);
                        if (!first && i==start_index_master)
                            break;
                    }
                }
                else //if slave is a wire
                {
                    //Reset the flag
                    slave_is_wire = false;
                    //Now check the point-cloud with the shortest distance to "lastPoint"
                    //As the Master and the slave have different number of points we have to do it two times now
                    //First we have to divide the first PointCloud as it is for sure that we start
                    //Somewhere in the middle of it. We will then insert the points at the current start
                    MasterPointContainer.clear();
                    SlavePointContainer.clear();
                    bool first = true;
                    for (unsigned int i=start_index_master;i<MasterPointsStorage.begin()->size();i++)
                    {
                        //We skip the Endpoint as it may be the same point as the start point
                        if (i+1 == MasterPointsStorage.begin()->size())
                        {
                            first = false;
                            i=-1;
                            continue;
                        }
                        MasterPointContainer.push_back(MasterPointsStorage[0][i]);
                        if (!first && i== start_index_master)
                            break;
                    }
                    first = true;
                    for (unsigned int i=start_index_slave;i<SlavePointsStorage.begin()->size();i++)
                    {
                        //We skip the Endpoint as it may be the same point as the start point
                        if (i+1 == SlavePointsStorage.begin()->size())
                        {
                            first = false;
                            i=-1;
                            continue;
                        }
                        SlavePointContainer.push_back(SlavePointsStorage[0][i]);
                        if (!first && i== start_index_slave)
                            break;
                    }
                }
                //Now lets interpolate the Point Cloud
                Handle(TColgp_HArray1OfPnt) InterpolationPointsMaster = new TColgp_HArray1OfPnt(1, MasterPointContainer.size());
                Handle(TColgp_HArray1OfPnt) InterpolationPointsSlave = new TColgp_HArray1OfPnt(1, SlavePointContainer.size());
                for (unsigned int t=0;t<MasterPointContainer.size();++t)
                {
                    InterpolationPointsMaster->SetValue(t+1,MasterPointContainer[t].first);
                }
                for (unsigned int t=0;t<SlavePointContainer.size();++t)
                {
                    InterpolationPointsSlave->SetValue(t+1,SlavePointContainer[t]);
                }

                /*CheckPoints(InterpolationPointsMaster);*/
                /*CheckPoints(InterpolationPointsSlave);*/

                m_all_offset_cuts_high.push_back(InterpolateOrderedPoints(InterpolationPointsMaster,true));
                m_all_offset_cuts_low.push_back(InterpolateOrderedPoints(InterpolationPointsSlave,true));
                //Store the last point
                lastPoint = MasterPointContainer.rbegin()->first;
                //SlaveTool Path finished
            }//Current Curve < last curve is finished here
        }
        if (fabs(m_ordered_cuts_it->first-(m_ordered_cuts_it-1)->first)<=0.01)
        {
            //we only set the new flat level wire here
            //no Toolpath is calculated
            current_flat_level = m_ordered_cuts_it;
        }//end of current == last
        if (m_ordered_cuts_it->first>(m_ordered_cuts_it-1)->first)
        {
            //The Slave Tool is now the Master Tool and therefore we only have to exchange < case
            //Check if current Level has got a Wire
            if (m_ordered_cuts_it->second.ShapeType() == TopAbs_WIRE)
            {
                WireExplorer aWireExplorer(TopoDS::Wire(m_ordered_cuts_it->second));
                for (aWireExplorer.Init();aWireExplorer.More();aWireExplorer.Next())
                {
                    BRepAdaptor_Curve curveAdaptor(aWireExplorer.Current());
                    GCPnts_QuasiUniformAbscissa aProp(curveAdaptor,100);
                    for (int i=1;i<=aProp.NbPoints();++i)
                    {
                        std::pair<gp_Pnt,double> aTempPair;
                        //Check the direction
                        if (aWireExplorer.Current().Orientation() != TopAbs_REVERSED)
                            curveAdaptor.D0(aProp.Parameter(i),aTempPair.first);
                        else curveAdaptor.D0(aProp.Parameter(aProp.NbPoints()-i+1),aTempPair.first);
                        aTempPair.first.SetZ(aTempPair.first.Z() -m_UserSettings.slave_radius -m_UserSettings.sheet_thickness);
                        aTempPair.second = 0.0; //Initialize of Angle
                        //checken ob der neue Punkt zu nahe am alten ist. Wenn ja, dann kein push_back
                        if (MasterPointContainer.size()>0 && (MasterPointContainer.rbegin()->first.SquareDistance(aTempPair.first)>(Precision::Confusion()*Precision::Confusion())))
                        {
                            MasterPointContainer.push_back(aTempPair);
                            anoutput1 << aTempPair.first.X() <<","<< aTempPair.first.Y() <<","<< aTempPair.first.Z()<<std::endl;
                            aTempPair.first.SetZ(aTempPair.first.Z() + m_UserSettings.master_radius + m_UserSettings.slave_radius + m_UserSettings.sheet_thickness);
                            SlavePointContainer.push_back(aTempPair.first);
                            anoutput2 << aTempPair.first.X() <<","<< aTempPair.first.Y() <<","<< aTempPair.first.Z()<<std::endl;
                        }
                        else if (MasterPointContainer.empty())
                        {
                            MasterPointContainer.push_back(aTempPair);
                            anoutput1 << aTempPair.first.X() <<","<< aTempPair.first.Y() <<","<< aTempPair.first.Z()<<std::endl;
                            aTempPair.first.SetZ(aTempPair.first.Z() + m_UserSettings.master_radius + m_UserSettings.slave_radius + m_UserSettings.sheet_thickness);
                            SlavePointContainer.push_back(aTempPair.first);
                            anoutput2 << aTempPair.first.X() <<","<< aTempPair.first.Y() <<","<< aTempPair.first.Z()<<std::endl;
                        }
                    }
                    //Now Interpolate the Points only if we have a non-continuous edge or if we are finished
                    //with all edges
                    bool tangency = true;
                    //If there are more Edges in the wire
                    if (aWireExplorer.MoreEdge())
                    {
                        tangency = CheckEdgeTangency(aWireExplorer.Current(),aWireExplorer.NextEdge());
                        if (!tangency)
                        {
                            //Store all the PointClouds in a StorageVector to arrange the Robot-Movement afterwards
                            MasterPointsStorage.push_back(MasterPointContainer);
                            SlavePointsStorage.push_back(SlavePointContainer);
                            MasterPointContainer.clear();
                            SlavePointContainer.clear();
                        }
                        else continue;
                    }
                    else
                    {
                        MasterPointsStorage.push_back(MasterPointContainer);
                        SlavePointsStorage.push_back(SlavePointContainer);
                        MasterPointContainer.clear();
                        SlavePointContainer.clear();
                    }
                }
                //Now check the point-cloud with the shortest distance to "lastPoint"
                int start_index = 0,start_array=0;
                CheckforLastPoint(lastPoint,start_index,start_array,MasterPointsStorage);
                //Now Interpolate the PointClouds...Cloud by Cloud
                //First we have to divide the first PointCloud as it is for sure that we start
                //Somewhere in the middle of it. We will then insert the points at the current start
                if (MasterPointsStorage.size() == 1) //If we have only one PointCloud
                {
                    bool first = true;
                    for (unsigned int i=start_index;i<MasterPointsStorage.begin()->size();i++)
                    {
                        //We skip the Endpoint as it may be the same point as the start point
                        if (i+1 == MasterPointsStorage.begin()->size())
                        {
                            i=-1;
                            first = false;
                            continue;
                        }
                        MasterPointContainer.push_back(MasterPointsStorage[0][i]);
                        SlavePointContainer.push_back(SlavePointsStorage[0][i]);
                        if (!first && i == start_index)
                            break;
                    }
                    //Now lets interpolate the Point Cloud
                    Handle(TColgp_HArray1OfPnt) InterpolationPointsMaster = new TColgp_HArray1OfPnt(1, MasterPointContainer.size());
                    Handle(TColgp_HArray1OfPnt) InterpolationPointsSlave = new TColgp_HArray1OfPnt(1, SlavePointContainer.size());
                    for (unsigned int t=0;t<MasterPointContainer.size();++t)
                    {
                        InterpolationPointsMaster->SetValue(t+1,MasterPointContainer[t].first);
                        InterpolationPointsSlave->SetValue(t+1,SlavePointContainer[t]);
                    }
                    m_all_offset_cuts_low.push_back(InterpolateOrderedPoints(InterpolationPointsMaster,true));
                    m_all_offset_cuts_high.push_back(InterpolateOrderedPoints(InterpolationPointsSlave,true));
                    //Store the last point
                    lastPoint = MasterPointContainer.rbegin()->first;
                }
                else
                {
                    anIterator1 = SlavePointsStorage.begin();
                    anIterator2 = MasterPointsStorage.begin();
                    MasterPointContainer.clear();
                    SlavePointContainer.clear();
                    for (int i=0;i<=start_index;i++)
                    {
                        MasterPointContainer.push_back(MasterPointsStorage[start_array][i]);
                        SlavePointContainer.push_back(SlavePointsStorage[start_array][i]);
                    }
                    MasterPointsStorage.insert((anIterator2+start_array+1),MasterPointContainer);
                    SlavePointsStorage.insert((anIterator1+start_array+1),SlavePointContainer);
                    MasterPointContainer.clear();
                    SlavePointContainer.clear();
                    //Reinitialize the Iterators
                    anIterator1 = SlavePointsStorage.begin();
                    anIterator2 = MasterPointsStorage.begin();
                    for (unsigned int i=start_index;i<MasterPointsStorage[start_array].size();i++)
                    {
                        MasterPointContainer.push_back(MasterPointsStorage[start_array][i]);
                        SlavePointContainer.push_back(SlavePointsStorage[start_array][i]);
                    }
                    MasterPointsStorage.insert((anIterator2+start_array+2),MasterPointContainer);
                    SlavePointsStorage.insert((anIterator1+start_array+2),SlavePointContainer);
                    //Reinitialize the Iterators
                    anIterator1 = SlavePointsStorage.begin();
                    anIterator2 = MasterPointsStorage.begin();
                    //Delete the Original PointCloud
                    MasterPointsStorage.erase(anIterator2+start_array);
                    SlavePointsStorage.erase(anIterator1+start_array);
                    MasterPointContainer.clear();
                    SlavePointContainer.clear();
                    //Now lets interpolate the Point Clouds
                    //Start at start_array+1 as the insert operations used us to do it like that
                    for (unsigned int j=start_array+1;j<MasterPointsStorage.size();++j)
                    {
                        Handle(TColgp_HArray1OfPnt) InterpolationPointsMaster = new TColgp_HArray1OfPnt(1, MasterPointsStorage[j].size());
                        Handle(TColgp_HArray1OfPnt) InterpolationPointsSlave = new TColgp_HArray1OfPnt(1, SlavePointsStorage[j].size());
                        for (unsigned int t=0;t<MasterPointsStorage[j].size();++t)
                        {
                            InterpolationPointsMaster->SetValue(t+1,MasterPointsStorage[j][t].first);
                            InterpolationPointsSlave->SetValue(t+1,SlavePointsStorage[j][t]);
                        }
                        m_all_offset_cuts_low.push_back(InterpolateOrderedPoints(InterpolationPointsMaster,true));
                        m_all_offset_cuts_high.push_back(InterpolateOrderedPoints(InterpolationPointsSlave,true));
                        if (j+1==MasterPointsStorage.size())
                        {
                            j=-1;
                            continue;
                        }
                        if (j+1==start_array+1)
                            break;
                    }
                    lastPoint = MasterPointsStorage[start_array].rbegin()->first;
                }
            }
            else //If the current Curve is no Wire (Mode >)
            {
                Edgesort aCutShapeSorter(m_ordered_cuts_it->second);
                for (aCutShapeSorter.Init();aCutShapeSorter.More();aCutShapeSorter.Next())
                {
                    //Get the PCurve and the GeomSurface
                    Handle_Geom2d_Curve a2DCurve;
                    Handle_Geom_Surface aSurface;
                    TopLoc_Location aLoc;
                    TopoDS_Edge anEdge;
                    double first2,last2;
                    bool reversed = false;
                    BRep_Tool::CurveOnSurface(aCutShapeSorter.Current(),a2DCurve,aSurface,aLoc,first2,last2);
                    //Jetzt noch die resultierende Surface und die Curve sauber drehen
                    //(vielleicht wurde ja das TopoDS_Face irgendwie gedreht oder die TopoDS_Edge)
                    if (aCutShapeSorter.Current().Orientation() == TopAbs_REVERSED)
                        reversed = true;

                    BRepAdaptor_Curve aCurveAdaptor(aCutShapeSorter.Current());
                    GCPnts_QuasiUniformAbscissa aPointGenerator(aCurveAdaptor,200);
                    int PointSize = aPointGenerator.NbPoints();
                    //Now get the surface normal to the generated points
                    for (int i=1;i<=PointSize;++i)
                    {
                        std::pair<gp_Pnt,double> PointContactPair;
                        gp_Pnt2d a2dParaPoint;
                        gp_Pnt aSurfacePoint;
                        TopoDS_Face aFace;
                        gp_Vec Uvec,Vvec,normalVec;
                        //If the curve is reversed we also have to reverse the point direction
                        if (reversed) a2DCurve->D0(aPointGenerator.Parameter(PointSize-i+1),a2dParaPoint);
                        else a2DCurve->D0(aPointGenerator.Parameter(i),a2dParaPoint);
                        GeomAdaptor_Surface aGeom_Adaptor(aSurface);
                        int t = aGeom_Adaptor.GetType();
                        aGeom_Adaptor.D1(a2dParaPoint.X(),a2dParaPoint.Y(),aSurfacePoint,Uvec,Vvec);
                        //Jetzt den Normalenvector auf die Fläche ausrechnen
                        normalVec = Uvec;
                        normalVec.Cross(Vvec);
                        normalVec.Normalize();
                        //Jetzt ist die Normale berechnet und auch normalisiert
                        //Jetzt noch checken ob die Normale auch wirklich auf die saubere Seite zeigt
                        //dazu nur checken ob der Z-Wert der Normale größer Null ist (dann im 1.und 2. Quadranten)
                        if (normalVec.Z()<0) normalVec.Multiply(-1.0);

                        //Mal kurz den Winkel zur Grund-Ebene ausrechnen
                        gp_Vec planeVec(normalVec.X(),normalVec.Y(),0.0);
                        //Den Winkel
                        PointContactPair.second = normalVec.Angle(planeVec);
                        gp_Vec NormalVecSlave = normalVec;
                        gp_Pnt SlavePoint;
                        //Jetzt die Z-Komponente auf 0 setzen
                        //normalVec.SetZ(0.0);
                        normalVec.Normalize();
                        //Jetzt die Normale mit folgender Formel multiplizieren für den Master
                        //double multiply = (m_UserSettings.slave_radius*(1-sin(PointContactPair.second))/cos(PointContactPair.second))+m_UserSettings.sheet_thickness;
                        double multiply = m_UserSettings.slave_radius + m_UserSettings.sheet_thickness;
                        //As the Master is now the Slave we have to multiply it also by -1.0
                        normalVec.Multiply(-multiply);
                        //und hier für den Slave
                        NormalVecSlave.Normalize();
                        multiply = m_UserSettings.master_radius;
                        NormalVecSlave.Multiply(multiply);
                        //Jetzt den OffsetPunkt berechnen
                        PointContactPair.first.SetXYZ(aSurfacePoint.XYZ() + normalVec.XYZ());
                        SlavePoint.SetXYZ(aSurfacePoint.XYZ() + NormalVecSlave.XYZ());
                        //PointContactPair.first.SetZ(PointContactPair.first.Z() - m_UserSettings.slave_radius);
                        //Damit wir keine Punkte bekommen die zu nahe beieinander liegen
                        //Den letzten hinzugefügten Punkt suchen
                        if (MasterPointContainer.size()>0 && (MasterPointContainer.rbegin()->first.SquareDistance(PointContactPair.first)>(Precision::Confusion()*Precision::Confusion())))
                        {
                            MasterPointContainer.push_back(PointContactPair);
                            SlavePointContainer.push_back(SlavePoint);
                            anoutput1 << PointContactPair.first.X() <<","<< PointContactPair.first.Y() <<","<< PointContactPair.first.Z()<<std::endl;
                            anoutput2 << SlavePoint.X() <<","<< SlavePoint.Y() <<","<< SlavePoint.Z()<<std::endl;
                        }
                        else if (MasterPointContainer.empty())
                        {
                            MasterPointContainer.push_back(PointContactPair);
                            SlavePointContainer.push_back(SlavePoint);
                            anoutput1 << PointContactPair.first.X() <<","<< PointContactPair.first.Y() <<","<< PointContactPair.first.Z()<<std::endl;
                            anoutput2 << SlavePoint.X() <<","<< SlavePoint.Y() <<","<< SlavePoint.Z()<<std::endl;
                        }
                    }
                }
                //Before we Output anything we check if the lowest Coordinate can still touch the "currently" highest flat area and if yes, we have to adapt the Slave toolpath
                for (unsigned int k=0;k<SlavePointContainer.size();++k)
                {
                    if ((SlavePointContainer[k].Z()-m_UserSettings.master_radius)<(current_flat_level->first))
                    {
                        slave_is_wire = true;
                        TopoDS_Wire aWire = TopoDS::Wire(current_flat_level->second);
                        BRepAdaptor_CompCurve2 wireAdaptor(aWire);
                        GCPnts_QuasiUniformAbscissa aProp(wireAdaptor,1000);
                        SlavePointContainer.clear();
                        for (int i=1;i<=aProp.NbPoints();++i)
                        {
                            gp_Pnt SlaveOffsetPoint;
                            wireAdaptor.D0(aProp.Parameter(i),SlaveOffsetPoint);
                            SlaveOffsetPoint.SetZ(SlaveOffsetPoint.Z() + m_UserSettings.master_radius);
                            //checken ob der neue Punkt zu nahe am alten ist. Wenn ja, dann kein push_back
                            if (SlavePointContainer.size()>0 && (SlavePointContainer.rbegin()->SquareDistance(SlaveOffsetPoint)>(0.001)))
                            {
                                SlavePointContainer.push_back(SlaveOffsetPoint);
                                anoutput2 << SlaveOffsetPoint.X() <<","<< SlaveOffsetPoint.Y() <<","<< SlaveOffsetPoint.Z()<<std::endl;
                            }
                            else if (SlavePointContainer.empty())
                            {
                                SlavePointContainer.push_back(SlaveOffsetPoint);
                                anoutput2 << SlaveOffsetPoint.X() <<","<< SlaveOffsetPoint.Y() <<","<< SlaveOffsetPoint.Z()<<std::endl;
                            }
                        }
                        break;
                    }
                }
                MasterPointsStorage.push_back(MasterPointContainer);
                SlavePointsStorage.push_back(SlavePointContainer);
                int start_index_master = 0,start_array_master=0,start_index_slave=0,start_array_slave=0;
                CheckforLastPoint(lastPoint,start_index_master,start_array_master,start_index_slave,start_array_slave,MasterPointsStorage,SlavePointsStorage);
                //If the Slave is a Wire 'slave_is_wire ==true' then we have to take care of two independent point clouds
                if (!slave_is_wire)
                {
                    //First we have to divide the first PointCloud as it is for sure that we start
                    //Somewhere in the middle of it. We will then insert the points at the current start
                    MasterPointContainer.clear();
                    SlavePointContainer.clear();
                    bool first =  true;
                    for (unsigned int i=start_index_master;i<MasterPointsStorage.begin()->size();i++)
                    {
                        if (i+1 == MasterPointsStorage.begin()->size())
                        {
                            i=-1;
                            first = false;
                            continue;
                        }
                        MasterPointContainer.push_back(MasterPointsStorage[0][i]);
                        SlavePointContainer.push_back(SlavePointsStorage[0][i]);
                        if (!first && i == start_index_master)
                            break;
                    }

                }
                else //if slave is a wire
                {
                    //Now check the point-cloud with the shortest distance to "lastPoint"
                    //As the Master and the slave have different number of points we have to do it two times now
                    //First we have to divide the first PointCloud as it is for sure that we start
                    //Somewhere in the middle of it. We will then insert the points at the current start
                    MasterPointContainer.clear();
                    SlavePointContainer.clear();
                    bool first = true;
                    for (unsigned int i=start_index_master;i<MasterPointsStorage.begin()->size();i++)
                    {
                        //We skip the Endpoint as it may be the same point as the start point
                        if (i+1 == MasterPointsStorage.begin()->size())
                        {
                            i=-1;
                            first = false;
                            continue;
                        }
                        MasterPointContainer.push_back(MasterPointsStorage[0][i]);
                        if (!first && i== start_index_master)
                            break;
                    }
                    for (unsigned int i=start_index_slave;i<SlavePointsStorage.begin()->size();i++)
                    {
                        //We skip the Endpoint as it may be the same point as the start point
                        if (i+1 == SlavePointsStorage.begin()->size())
                        {
                            i=-1;
                            first = false;
                            continue;
                        }
                        SlavePointContainer.push_back(SlavePointsStorage[0][i]);
                        if (!first && i == start_index_slave)
                            break;
                    }
                }
                //Now lets interpolate the Point Cloud
                Handle(TColgp_HArray1OfPnt) InterpolationPointsMaster = new TColgp_HArray1OfPnt(1, MasterPointContainer.size());
                Handle(TColgp_HArray1OfPnt) InterpolationPointsSlave = new TColgp_HArray1OfPnt(1, SlavePointContainer.size());
                for (unsigned int t=0;t<MasterPointContainer.size();++t)
                {
                    InterpolationPointsMaster->SetValue(t+1,MasterPointContainer[t].first);
                }
                for (unsigned int t=0;t<SlavePointContainer.size();++t)
                {
                    InterpolationPointsSlave->SetValue(t+1,SlavePointContainer[t]);
                }

                /*CheckPoints(InterpolationPointsMaster);*/
                /*CheckPoints(InterpolationPointsSlave);*/

                m_all_offset_cuts_low.push_back(InterpolateOrderedPoints(InterpolationPointsMaster,true));
                m_all_offset_cuts_high.push_back(InterpolateOrderedPoints(InterpolationPointsSlave,true));
                //Store the last point
                lastPoint = MasterPointContainer.rbegin()->first;
            }//Current Curve > last curve is finished here
        }
    }
    return true;
}


bool cutting_tools::CheckPoints(Handle(TColgp_HArray1OfPnt) PointArray)
{
    Standard_Integer ii ;
    Standard_Real tolerance_squared = Precision::Confusion() * Precision::Confusion(),
                                      distance_squared ;
    Standard_Boolean result = Standard_True ;
    for (ii = PointArray->Lower() ; result && ii < PointArray->Upper() ; ii++)
    {
        distance_squared = PointArray->Value(ii).SquareDistance(PointArray->Value(ii+1)) ;
        if (distance_squared < tolerance_squared)
        {
            return false;
        }

    }
    return true;

}
Base::BoundBox3f cutting_tools::getWireBBox(TopoDS_Wire aWire)
{
    //Fill Bounding Boxes with Wires
    //Therefore we have to evaluate some points on our wire and feed the BBox Algorithm
    Base::BoundBox3f currentBox;
    BRepAdaptor_CompCurve2 wireAdaptor(aWire);
    GCPnts_QuasiUniformDeflection aProp(wireAdaptor,0.1);
    Base::Vector3f aPoint;
    currentBox.Flush();
    for (int j=1;j<=aProp.NbPoints();++j)
    {
        aPoint.x = float(aProp.Value(j).X());
        aPoint.y = float(aProp.Value(j).Y());
        aPoint.z = float(aProp.Value(j).Z());
        currentBox.Add(aPoint);
    }
    return currentBox;
}

TopoDS_Shape cutting_tools::getProperCut(TopoDS_Shape& aShape)
{
    //A cutting Shape is coming as aShape
    //check direction to decide which Topology to hold and which to delete
    if (m_direction)//From top to bottom
    {

    }
    TopoDS_Shape aReturnShape;
    return aReturnShape;
}

bool cutting_tools::calculateAccurateSlaveZLevel(std::vector<std::pair<gp_Pnt,double> >& OffsetPoints, double current_z_level, double &slave_z_level, double &average_sheet_thickness, double &average_angle, bool &cutpos)
{
    //Mittelwert von allen Normalenwinkeln und damit dann den Mittelwert der Blechdicke bilden

    bool direction,area;
    //Zunächst checken in welchem Bereich vom Teil wir uns aufhalten
    if (current_z_level < m_MachiningOrder[0].first.z && current_z_level > m_MachiningOrder[1].first.z)
    {
        //Wir sind von oben nach unten im ersten Teil
        direction = true;
        area = true;
    }
    else if (m_MachiningOrder.size() > 2 && current_z_level < m_MachiningOrder[1].first.z)
    {
        //Wir sind von oben nach unten im zweiten Teil
        direction = true;
        area = false;
    }
    else if (m_MachiningOrder.size() > 2 && current_z_level < m_MachiningOrder[2].first.z && current_z_level > m_MachiningOrder[1].first.z)
    {
        //Wir sind von unten nach oben im 2. Teil
        direction = false;
        area = false;

    }
    else
    {
        cout << "Konnte keine Zuordnung finden" << endl;
    }

    average_angle = 0.0;
    double slave_z_leveltop,slave_z_levelbottom;

    for (unsigned int i=0;i<OffsetPoints.size();++i)
    {
        average_angle = average_angle + OffsetPoints[i].second;
    }

    average_angle = average_angle/OffsetPoints.size();
    //Average Sheet thickness based on sin-law
    average_sheet_thickness = m_UserSettings.sheet_thickness * sin ((PI/2)-average_angle);


    slave_z_leveltop = current_z_level + \
                       (m_UserSettings.master_radius*(1-sin(average_angle))) - \
                       (sin(average_angle)*(average_sheet_thickness+m_UserSettings.slave_radius)) + \
                       ((average_sheet_thickness+m_UserSettings.slave_radius)/cos(average_angle));

    slave_z_levelbottom = current_z_level - ( m_UserSettings.master_radius + \
                          m_UserSettings.slave_radius + \
                          average_sheet_thickness) * \
                          sin(average_angle) - \
                          m_UserSettings.master_radius;

    if (direction && area && slave_z_leveltop < (m_MachiningOrder[0].first.z - m_UserSettings.sheet_thickness))
    {
        slave_z_level = slave_z_leveltop;
        cutpos = true;
        return true;
    }
    else if (direction && area && slave_z_leveltop > (m_MachiningOrder[0].first.z-m_UserSettings.sheet_thickness))
    {
        if ((slave_z_levelbottom + m_UserSettings.slave_radius) < (m_MachiningOrder[0].first.z - m_UserSettings.sheet_thickness))
        {
            slave_z_level = slave_z_levelbottom;
            cutpos = false;
            return true;
        }
        else
        {
            slave_z_level = m_MachiningOrder[0].first.z;
            return true;
        }
    }
    //Die oberste Ebene ist hier abgehakt
    if (direction && !area && slave_z_leveltop < (m_MachiningOrder[1].first.z - m_UserSettings.sheet_thickness))
    {
        slave_z_level = slave_z_leveltop;
        cutpos = true;
        return true;
    }
    else if (direction && !area && slave_z_leveltop > (m_MachiningOrder[1].first.z-m_UserSettings.sheet_thickness))
    {
        if ((slave_z_levelbottom + m_UserSettings.slave_radius) < (m_MachiningOrder[1].first.z - m_UserSettings.sheet_thickness))
        {
            slave_z_level = slave_z_levelbottom;
            cutpos = false;
            return true;
        }
        else
        {
            slave_z_level = m_MachiningOrder[1].first.z;
            return true;
        }
    }
    //Jetzt noch die Geschichte wo wir wieder nach oben fahren
    if (!direction && !area && slave_z_leveltop < (m_MachiningOrder[1].first.z - m_UserSettings.sheet_thickness))
    {
        slave_z_level = slave_z_leveltop;
        cutpos = true;
        return true;
    }
    else if (direction && !area && slave_z_leveltop > (m_MachiningOrder[2].first.z-m_UserSettings.sheet_thickness))
    {
        if ((slave_z_levelbottom + m_UserSettings.slave_radius) < (m_MachiningOrder[2].first.z - m_UserSettings.sheet_thickness))
        {
            slave_z_level = slave_z_levelbottom;
            cutpos = false;
            return true;
        }
        else
        {
            slave_z_level = m_MachiningOrder[2].first.z;
            return true;
        }
    }

    return  true;
}

//bool cutting_tools::OffsetWires() //Hier ist die alte Version
//{
// for(m_it = m_all_cuts.begin();m_it<m_all_cuts.end();++m_it)
// {
//  // make your wire looks like a curve to other algorithm and generate Points to offset the curve
//  BRepAdaptor_CompCurve2 wireAdaptor((*m_it).second);
//  GCPnts_QuasiUniformDeflection aProp(wireAdaptor,0.001);
//  int numberofpoints = aProp.NbPoints();
//  Handle(TColgp_HArray1OfPnt) finalOffsetPoints = new TColgp_HArray1OfPnt(1, numberofpoints);
//  //Now project the points to the surface. As we might have many faces just project to every face and take the min distance
//  for (int i=1;i<=numberofpoints;++i)
//  {
//   TopExp_Explorer ExShape;
//   ExShape.Init(m_Shape,TopAbs_FACE);
//   Standard_Real Umin,Vmin;
//   double distance_old,distance;
//   Handle_Geom_Surface nearest_surface;
//   distance_old = 200.0;
//   for (; ExShape.More(); ExShape.Next())
//   {
//    const TopoDS_Face &atopo_surface =  TopoDS::Face (ExShape.Current());
//    Handle_Geom_Surface geom_surface = BRep_Tool::Surface(atopo_surface);
//    gp_Pnt currentPoint = aProp.Value(i);
//    GeomAPI_ProjectPointOnSurf aPPS(currentPoint,geom_surface,0.001);
//    if (! aPPS.IsDone())
//    {
//     return false;
//    }
//    distance = aPPS.LowerDistance();
//    if(distance<distance_old)
//    {
//     distance_old = distance;
//     aPPS.LowerDistanceParameters (Umin,Vmin);
//     nearest_surface = geom_surface;
//    }
//   }
//   gp_Pnt projectedPoint,OffsetPoint;
//   gp_Vec Uvec,Vvec,normalVec,projPointVec;
//   nearest_surface->D1(Umin,Vmin,projectedPoint,Uvec,Vvec);
//   normalVec = Uvec;
//   normalVec.Cross(Vvec);
//   normalVec.Normalize(); //Jetzt ist die Normale berechnet und auch normalisiert
//   //zeroPoint.SetCoord(0.0,0.0,0.0); //
//   projPointVec.SetXYZ(projectedPoint.XYZ());
//   OffsetPoint.SetXYZ((projPointVec + (normalVec*(-21))).XYZ());
//   finalOffsetPoints->SetValue(i,OffsetPoint); //Aktuellen OffsetPoint setzen
//  }
//  Standard_Boolean isPeriodic = Standard_False;
//  GeomAPI_Interpolate aNoPeriodInterpolate(finalOffsetPoints, isPeriodic, Precision::Confusion());
//  aNoPeriodInterpolate.Perform();
//  Handle_Geom_BSplineCurve aCurve(aNoPeriodInterpolate.Curve());
//
//  // check results
//  if (!aNoPeriodInterpolate.IsDone()) return false;
//  m_all_offset_cuts_high.push_back(aCurve);
// }
//}

gp_Dir cutting_tools::getPerpendicularVec(gp_Vec& anInput)
{
    double x,y;
    gp_Dir Output;
    x = anInput.X();
    y = anInput.Y();
    if (x != 0.0 && y != 0.0)
    {
        Output.SetCoord(1.0,(-x/y),0.0);
    }
    else if (x == 0 && y == 0)
    {
        Output.SetCoord(1.0,1.0,0.0);
    }
    else if (x == 0 && y != 0)
    {
        Output.SetCoord(1.0,0.0,0.0);
    }
    else if (x != 0 && y == 0)
    {
        Output.SetCoord(0.0,1.0,0.0);
    }
    return Output;
}



bool cutting_tools::OffsetWires_Spiral()
{
    std::ofstream anoutput1;
    anoutput1.open("c:/spiral.txt");
    Base::Builder3D log;
    std::vector<std::pair<float,TopoDS_Shape> >::iterator current_flat_level;
    current_flat_level = m_ordered_cuts.begin();
    SpiralHelper lastPoint;
    lastPoint.SurfacePoint.SetCoord(0.0,0.0,0.0);
    bool slave_done= false; //Necessary if the slave is already output
    bool just_started = true; //
    gp_Vec direction_vector(0.0,0.0,1.0),last_direction_vector(0.0,0.0,1.0);//Just to initialize them
    bool direction = true; //for the Robot, this tells the algo that we have to switch the direction
    //Nicht beim höchsten Anfangen, da wir den nicht mit dem Master fahren wollen
    for (m_ordered_cuts_it = m_ordered_cuts.begin()+1;m_ordered_cuts_it!=m_ordered_cuts.end();++m_ordered_cuts_it)
    {
        std::vector<SpiralHelper> OffsetSpiralPoints,TempSpiralPoints;
        std::vector<gp_Pnt> SlavePointContainer;
        OffsetSpiralPoints.clear();
        TempSpiralPoints.clear();

        //Now we have to select which strategy to choose
        //if the current levels is bigger,the same, or less then the previous one
        if (m_ordered_cuts_it->first<(m_ordered_cuts_it-1)->first)
        {
            //Master is calculated as Usual, Slave stays at the currently highest level
            //Check if Last Level has got a Wire as we go from the last to the current with our Spiral
            SpiralHelper aSpiralStruct;
            double CurveLength = 0.0;
            if ((m_ordered_cuts_it-1)->second.ShapeType() == TopAbs_WIRE)
            {
                WireExplorer aWireExplorer(TopoDS::Wire((m_ordered_cuts_it-1)->second));
                for (aWireExplorer.Init();aWireExplorer.More();aWireExplorer.Next())
                {
                    CurveLength = CurveLength + GetEdgeLength(aWireExplorer.Current());
                    BRepAdaptor_Curve curveAdaptor(aWireExplorer.Current());
                    //Adjust the point amount based on the curve length make every 0.3mm a point
                    //GCPnts_QuasiUniformAbscissa aProp(curveAdaptor,int(GetEdgeLength(aWireExplorer.Current())/0.4));
                    GCPnts_QuasiUniformAbscissa aProp(curveAdaptor,1000);
                    for (int i=1;i<=aProp.NbPoints();++i)
                    {
                        //Check the direction
                        if (aWireExplorer.Current().Orientation() != TopAbs_REVERSED)
                            curveAdaptor.D1(aProp.Parameter(i),aSpiralStruct.SurfacePoint,aSpiralStruct.LineD1);
                        else curveAdaptor.D1(aProp.Parameter(aProp.NbPoints()-i+1),aSpiralStruct.SurfacePoint,aSpiralStruct.LineD1);

                        aSpiralStruct.SurfaceNormal.SetCoord(0.0,0.0,1.0);
                        TempSpiralPoints.push_back(aSpiralStruct);
                    }
                }
            }
            else
            {
                CurveLength = 0.0;
                Edgesort aCutShapeSorter((m_ordered_cuts_it-1)->second);
                for (aCutShapeSorter.Init();aCutShapeSorter.More();aCutShapeSorter.Next())
                {
                    CurveLength = CurveLength + GetEdgeLength(aCutShapeSorter.Current());
                    //Get the PCurve and the GeomSurface
                    Handle_Geom2d_Curve a2DCurve;
                    Handle_Geom_Surface aSurface;
                    TopLoc_Location aLoc;
                    double first2,last2;
                    bool reversed = false;
                    BRep_Tool::CurveOnSurface(aCutShapeSorter.Current(),a2DCurve,aSurface,aLoc,first2,last2);
                    //Jetzt noch die resultierende Surface und die Curve sauber drehen
                    //(vielleicht wurde ja das TopoDS_Face irgendwie gedreht oder die TopoDS_Edge)
                    if (aCutShapeSorter.Current().Orientation() == TopAbs_REVERSED)
                        reversed = true;

                    BRepAdaptor_Curve aCurveAdaptor(aCutShapeSorter.Current());
                    GCPnts_QuasiUniformAbscissa aPointGenerator(aCurveAdaptor,1000);
                    int PointSize = aPointGenerator.NbPoints();
                    //Now get the surface normal to the generated points
                    for (int i=1;i<=PointSize;++i)
                    {
                        gp_Pnt2d a2dParaPoint;
                        gp_Pnt aSurfacePoint;
                        TopoDS_Face aFace;
                        gp_Vec Uvec,Vvec,normalVec;
                        //If the curve is reversed we also have to reverse the point direction
                        if (reversed)
                        {
                            a2DCurve->D0(aPointGenerator.Parameter(PointSize-i+1),a2dParaPoint);
                            aCurveAdaptor.D1(aPointGenerator.Parameter(PointSize-i+1),aSurfacePoint,aSpiralStruct.LineD1);
                        }
                        else
                        {
                            a2DCurve->D0(aPointGenerator.Parameter(i),a2dParaPoint);
                            aCurveAdaptor.D1(aPointGenerator.Parameter(i),aSurfacePoint,aSpiralStruct.LineD1);
                        }
                        GeomAdaptor_Surface aGeom_Adaptor(aSurface);
                        aGeom_Adaptor.D1(a2dParaPoint.X(),a2dParaPoint.Y(),aSurfacePoint,Uvec,Vvec);
                        //Jetzt den Normalenvector auf die Fläche ausrechnen
                        normalVec = Uvec;
                        normalVec.Cross(Vvec);
                        normalVec.Normalize();
                        //Jetzt ist die Normale berechnet und auch normalisiert
                        //Jetzt noch checken ob die Normale auch wirklich auf die saubere Seite zeigt
                        //dazu nur checken ob der Z-Wert der Normale größer Null ist (dann im 1.und 2. Quadranten)
                        if (normalVec.Z()<0)
                            normalVec.Multiply(-1.0);

                        //Mal kurz den Winkel zur Grund-Ebene ausrechnen
                        gp_Vec planeVec(normalVec.X(),normalVec.Y(),0.0);
                        //Den Winkel speichern
                        float angle = float(normalVec.Angle(planeVec));
                        aSpiralStruct.SurfaceNormal = normalVec;
                        aSpiralStruct.SurfacePoint = aSurfacePoint;
                        TempSpiralPoints.push_back(aSpiralStruct);
                    }
                }
            }

            //Now we have to find the shortest distance to the lastPoint of the previous spiral.
            //At the beginning the lastPoint is the Origin
            //This represents our startPoint. If we just started, then we skip this point

            unsigned int start_index = 0,adapted_start_index=0;
            float dist,distold = FLT_MAX;
            for (unsigned int t=0;t<TempSpiralPoints.size();t++)
            {
                dist = float(TempSpiralPoints[t].SurfacePoint.SquareDistance(lastPoint.SurfacePoint));
                if (dist<distold)
                {
                    start_index = t;
                    distold = dist;
                }
            }

            //now we know where to  start at our PointCloud, its the index t

            //Calculate the Slave Toolpath for the current flat area
            if (!slave_done)//if we did not calculate the slave toolpath for the current flat area
            {
                const TopoDS_Wire &aWire = TopoDS::Wire(current_flat_level->second);
                BRepAdaptor_CompCurve2 wireAdaptor(aWire);
                GCPnts_QuasiUniformAbscissa aProp(wireAdaptor,1000);
                SlavePointContainer.clear();
                gp_Pnt SlaveOffsetPoint;
                for (int i=1;i<=aProp.NbPoints();++i)
                {
                    wireAdaptor.D0(aProp.Parameter(i),SlaveOffsetPoint);
                    SlaveOffsetPoint.SetZ(SlaveOffsetPoint.Z() - m_UserSettings.sheet_thickness - m_UserSettings.slave_radius);
                    //checken ob der neue Punkt zu nahe am alten ist. Wenn ja, dann kein push_back
                    if (SlavePointContainer.size()>0 && (SlavePointContainer.rbegin()->SquareDistance(SlaveOffsetPoint)>(Precision::Confusion()*Precision::Confusion())))
                        SlavePointContainer.push_back(SlaveOffsetPoint);
                    else if (SlavePointContainer.empty())
                        SlavePointContainer.push_back(SlaveOffsetPoint);
                }
                //Now Interpolate the Slave with reordered points based on the last point
                int index = 0;
                distold = FLT_MAX;
                for (unsigned int t=0;t<SlavePointContainer.size();t++)
                {
                    dist = float(SlavePointContainer[t].SquareDistance(lastPoint.SurfacePoint));
                    if (dist<distold)
                    {
                        index = t;
                        distold = dist;
                    }
                }
                Handle(TColgp_HArray1OfPnt) InterpolationPoints = new TColgp_HArray1OfPnt(1, SlavePointContainer.size());
                adapted_start_index = start_index+1;
                for (unsigned int t=index+1;t<SlavePointContainer.size();++t)
                {
                    InterpolationPoints->SetValue(t+1,SlavePointContainer[t]);
                    if (t==index)
                        break;
                    if (t+1==SlavePointContainer.size())
                        t=-1;
                }

                m_all_offset_cuts_low.push_back(InterpolateOrderedPoints(InterpolationPoints,true));
                slave_done = true;
            } //Slave done

            gp_Pnt PreviousPoint = TempSpiralPoints[start_index].SurfacePoint;
            //check the current direction we would go
            gp_Vec help_Vec(TempSpiralPoints[start_index].SurfacePoint.Coord());
            if (!start_index+1>=TempSpiralPoints.size())
                direction_vector = TempSpiralPoints[start_index+1].SurfacePoint.Coord();
            else
                direction_vector = TempSpiralPoints[1].SurfacePoint.Coord();//As Start and End-Point are the same
            direction_vector.Subtract(help_Vec);
            //direction_vector.SetZ(0.0);
            direction_vector.Normalize();
            direction_vector.Multiply(1.0); //Zum testen bauen wir keinen Offset ein
            //switch the Spiral-direction if the clockwise flag is checked and  the
            //angle between the last and current round is more then 90°
            double angle = direction_vector.Angle(last_direction_vector);
            if (m_UserSettings.clockwise && angle<(D_PI*0.5))
                direction = true; //We are already in the wright direction
            else if (m_UserSettings.clockwise && angle >(D_PI*0.5))
                direction = false; //we have to switch the direction
            else if (!m_UserSettings.clockwise && angle<(D_PI*0.5))
                direction = false;
            else if (!m_UserSettings.clockwise && angle>(D_PI*0.5))
                direction = true;


            IntCurvesFace_ShapeIntersector anIntersector;
            anIntersector.Load(m_Shape,0.01);

            //Insert the first point into the TempSpiralPoints if we have just started
            std::vector<SpiralHelper> TempSpiralPointsFinal;
            TempSpiralPointsFinal.clear();
            if (!just_started)
                TempSpiralPointsFinal.push_back(lastPoint);
            else
                just_started = false;//The first point is now done

            if (direction && ((start_index+1)<TempSpiralPoints.size()))
                adapted_start_index = start_index+1;
            else if (direction && ((start_index+1)>=TempSpiralPoints.size()))
                adapted_start_index = 1;//Not 0 because the last one is already added
            else
            {
                if (start_index==0)
                    adapted_start_index = TempSpiralPoints.size()-2; //Skip the last Point as its equal to the first one
                else
                    adapted_start_index = start_index-1;
            }
            std::cout<<"Angle ="<< angle<< " Direction = "<< direction << "Start_Index: "<< adapted_start_index <<","<<start_index<<std::endl;
            double distance=0.0;
            for (unsigned int j=adapted_start_index;j<TempSpiralPoints.size();++j)
            {
                distance = distance + PreviousPoint.Distance(TempSpiralPoints[j].SurfacePoint);
                double delta_z = distance * m_UserSettings.level_distance / CurveLength;
                //we have to store the currentPoint for the distance calculation
                //before we change something at its coordinates
                PreviousPoint = TempSpiralPoints[j].SurfacePoint;

                TempSpiralPoints[j].SurfacePoint.SetZ(TempSpiralPoints[j].SurfacePoint.Z()-delta_z);
                //before we shoot we check if the z-level is not exactly the same as the next round
                //if (fabs(current_z_value-m_UserSettings.level_distance-TempSpiralPoints[j].SurfacePoint.Z())<=0.04)
                //    break; //We have nearly reached the next z-level, therefore we stop here.
                //Now we have to shoot to get the real Point we want
                //Therefore we have to generate the Direction Vector where to Shoot
                gp_Dir Shooting_Direction = getPerpendicularVec(TempSpiralPoints[j].LineD1);
                gp_Lin aLine(TempSpiralPoints[j].SurfacePoint,Shooting_Direction);
                anIntersector.Perform(aLine,-RealLast(),RealLast());
                if (anIntersector.NbPnt()<=1) //Just to debug
                    anIntersector.Perform(aLine,-RealLast(),RealLast());
                //Now we set the real Surface Point
                //How many Points did we get??
                int current_index;
                int points = anIntersector.NbPnt();
                if (points>0)
                {
                    float shortestDistance, shortestDistanceOld = FLT_MAX;
                    for (int g=1;g<=points;g++)
                    {
                        const gp_Pnt& TestPoint = anIntersector.Pnt(g);
                        shortestDistance = float(TestPoint.SquareDistance(TempSpiralPoints[j].SurfacePoint));
                        if (shortestDistance<shortestDistanceOld)
                        {
                            current_index = g;
                            shortestDistanceOld = shortestDistance;
                        }
                    }
                    //Now we check how far the shortest Distance is. If its more then 14mm then we jump to the
                    //next Point

                    if (shortestDistanceOld>156)//The point is more then 14mm away (square distance)
                        continue;
                    TempSpiralPoints[j].SurfacePoint = anIntersector.Pnt(current_index);
                }
                else //We have to try a mesh intersection as the Nurb Intersection does not seem to work
                {
                    cout << "Big Probleme";
                    continue;
                }
                //Now get the Proper Normal at this point
                BRepAdaptor_Surface aFaceAdaptor(anIntersector.Face(current_index));
                gp_Pnt P;
                gp_Vec U_Vec,V_Vec;
                aFaceAdaptor.D1(anIntersector.UParameter(current_index),anIntersector.VParameter(current_index),P,U_Vec,V_Vec);
                U_Vec.Cross(V_Vec);
                U_Vec.Normalize();
                if (U_Vec.Z() < 0) U_Vec.Multiply(-1.0);

                TempSpiralPoints[j].SurfaceNormal = U_Vec;
                TempSpiralPointsFinal.push_back(TempSpiralPoints[j]);
                //If we reached the end before we processed all points, then we start at the beginning.
                if (direction)
                {
                    if (j==start_index)
                        break; //Now we have completed all Points
                    else if (j+1==TempSpiralPoints.size())
                    {
                        j=-1;    //-1 because at the for we ++ the variable directly
                        continue;
                    }

                }
                else
                {
                    if (j==start_index)
                        break; //Now we have completed all Points
                    else if (j-1<0)
                    {
                        j=TempSpiralPoints.size()-3;
                        continue;
                    }
                    //We switch to the end and skip the last point
                    //as its the same as the point at j=0;
                    j=j-2;//As the for puts +1 for each step
                }
            }
            //Offset for Master and Slave
            OffsetSpiralPoints = OffsetSpiral(TempSpiralPointsFinal);
            //Now store the lastPoint of the currentSpiral as we need it for the next one
            lastPoint = *(TempSpiralPointsFinal.rbegin());
            gp_Vec temp_vector((OffsetSpiralPoints.rbegin()+5)->SurfacePoint.Coord());
            last_direction_vector = OffsetSpiralPoints.rbegin()->SurfacePoint.Coord();
            last_direction_vector.Subtract(temp_vector);
            //last_direction_vector.SetZ(0.0);
            last_direction_vector.Normalize();
            last_direction_vector.Multiply(10.0);
            Handle(TColgp_HArray1OfPnt) InterpolationPoints = new TColgp_HArray1OfPnt(1,OffsetSpiralPoints.size());
            TColgp_Array1OfVec Tangents(1,OffsetSpiralPoints.size());
            for (unsigned int t=0;t<OffsetSpiralPoints.size();++t)
            {
                InterpolationPoints->SetValue(t+1,OffsetSpiralPoints[t].SurfacePoint);
            }
            bool check = CheckPoints(InterpolationPoints);
            //Here we interpolate. If direction == true this means that the rotation is like the initial rotation
            m_all_offset_cuts_high.push_back(InterpolateOrderedPoints(InterpolationPoints,true));
        }
        if (m_ordered_cuts_it->first==(m_ordered_cuts_it-1)->first)
        {
            //we only set the new flat level wire here
            //no Toolpath is calculated
            current_flat_level = m_ordered_cuts_it;
            slave_done = false; //This is to generate the next flat level for the slave
            just_started = true; //We start a new section
        }//end of current == last
        //Now lets take the case when we move up again
        if (m_ordered_cuts_it->first>(m_ordered_cuts_it-1)->first)
        {
            //Slave is calculated as Usual, Master stays at the currently highest level
            //Check if Last Level has got a Wire as we go from the last to the current with our Spiral
            SpiralHelper aSpiralStruct;
            double CurveLength = 0.0;
            if ((m_ordered_cuts_it-1)->second.ShapeType() == TopAbs_WIRE)
            {
                WireExplorer aWireExplorer(TopoDS::Wire((m_ordered_cuts_it-1)->second));
                for (aWireExplorer.Init();aWireExplorer.More();aWireExplorer.Next())
                {
                    CurveLength = CurveLength + GetEdgeLength(aWireExplorer.Current());
                    BRepAdaptor_Curve curveAdaptor(aWireExplorer.Current());
                    //Adjust the point amount based on the curve length make every 0.3mm a point
                    //GCPnts_QuasiUniformAbscissa aProp(curveAdaptor,int(GetEdgeLength(aWireExplorer.Current())/0.4));
                    GCPnts_QuasiUniformAbscissa aProp(curveAdaptor,1000);
                    for (int i=1;i<=aProp.NbPoints();++i)
                    {
                        //Check the direction
                        if (aWireExplorer.Current().Orientation() != TopAbs_REVERSED)
                            curveAdaptor.D1(aProp.Parameter(i),aSpiralStruct.SurfacePoint,aSpiralStruct.LineD1);
                        else curveAdaptor.D1(aProp.Parameter(aProp.NbPoints()-i+1),aSpiralStruct.SurfacePoint,aSpiralStruct.LineD1);

                        aSpiralStruct.SurfaceNormal.SetCoord(0.0,0.0,1.0);
                        TempSpiralPoints.push_back(aSpiralStruct);
                    }
                }
            }
            else
            {
                CurveLength = 0.0;
                Edgesort aCutShapeSorter((m_ordered_cuts_it-1)->second);
                for (aCutShapeSorter.Init();aCutShapeSorter.More();aCutShapeSorter.Next())
                {
                    CurveLength = CurveLength + GetEdgeLength(aCutShapeSorter.Current());
                    //Get the PCurve and the GeomSurface
                    Handle_Geom2d_Curve a2DCurve;
                    Handle_Geom_Surface aSurface;
                    TopLoc_Location aLoc;
                    double first2,last2;
                    bool reversed = false;
                    BRep_Tool::CurveOnSurface(aCutShapeSorter.Current(),a2DCurve,aSurface,aLoc,first2,last2);
                    //Jetzt noch die resultierende Surface und die Curve sauber drehen
                    //(vielleicht wurde ja das TopoDS_Face irgendwie gedreht oder die TopoDS_Edge)
                    if (aCutShapeSorter.Current().Orientation() == TopAbs_REVERSED)
                        reversed = true;

                    BRepAdaptor_Curve aCurveAdaptor(aCutShapeSorter.Current());
                    GCPnts_QuasiUniformAbscissa aPointGenerator(aCurveAdaptor,1000);
                    int PointSize = aPointGenerator.NbPoints();
                    //Now get the surface normal to the generated points
                    for (int i=1;i<=PointSize;++i)
                    {
                        gp_Pnt2d a2dParaPoint;
                        gp_Pnt aSurfacePoint;
                        TopoDS_Face aFace;
                        gp_Vec Uvec,Vvec,normalVec;
                        //If the curve is reversed we also have to reverse the point direction
                        if (reversed)
                        {
                            a2DCurve->D0(aPointGenerator.Parameter(PointSize-i+1),a2dParaPoint);
                            aCurveAdaptor.D1(aPointGenerator.Parameter(PointSize-i+1),aSurfacePoint,aSpiralStruct.LineD1);
                        }
                        else
                        {
                            a2DCurve->D0(aPointGenerator.Parameter(i),a2dParaPoint);
                            aCurveAdaptor.D1(aPointGenerator.Parameter(i),aSurfacePoint,aSpiralStruct.LineD1);
                        }
                        GeomAdaptor_Surface aGeom_Adaptor(aSurface);
                        aGeom_Adaptor.D1(a2dParaPoint.X(),a2dParaPoint.Y(),aSurfacePoint,Uvec,Vvec);
                        //Jetzt den Normalenvector auf die Fläche ausrechnen
                        normalVec = Uvec;
                        normalVec.Cross(Vvec);
                        normalVec.Normalize();
                        //Jetzt ist die Normale berechnet und auch normalisiert
                        //Jetzt noch checken ob die Normale auch wirklich auf die saubere Seite zeigt
                        //dazu nur checken ob der Z-Wert der Normale größer Null ist (dann im 1.und 2. Quadranten)
                        if (normalVec.Z()<0)
                            normalVec.Multiply(-1.0);

                        //Mal kurz den Winkel zur Grund-Ebene ausrechnen
                        gp_Vec planeVec(normalVec.X(),normalVec.Y(),0.0);
                        //Den Winkel speichern
                        float angle = float(normalVec.Angle(planeVec));
                        aSpiralStruct.SurfaceNormal = normalVec;
                        aSpiralStruct.SurfacePoint = aSurfacePoint;
                        TempSpiralPoints.push_back(aSpiralStruct);
                    }
                }
            }

            //Now we have to find the shortest distance to the lastPoint of the previous spiral.
            //At the beginning the lastPoint is the Origin
            //This represents our startPoint. If we just started, then we skip this point

            int start_index = 0,adapted_start_index=0;
            float dist,distold = FLT_MAX;
            for (unsigned int t=0;t<TempSpiralPoints.size();t++)
            {
                dist = float(TempSpiralPoints[t].SurfacePoint.SquareDistance(lastPoint.SurfacePoint));
                if (dist<distold)
                {
                    start_index = t;
                    distold = dist;
                }
            }

            //now we know where to  start at our PointCloud, its the index t

            //Calculate the Slave Toolpath for the current flat area
            if (!slave_done)//if we did not calculate the slave toolpath for the current flat area
            {
                const TopoDS_Wire &aWire = TopoDS::Wire(current_flat_level->second);
                BRepAdaptor_CompCurve2 wireAdaptor(aWire);
                GCPnts_QuasiUniformAbscissa aProp(wireAdaptor,1000);
                SlavePointContainer.clear();
                gp_Pnt SlaveOffsetPoint;
                for (int i=1;i<=aProp.NbPoints();++i)
                {
                    wireAdaptor.D0(aProp.Parameter(i),SlaveOffsetPoint);
                    SlaveOffsetPoint.SetZ(SlaveOffsetPoint.Z() + m_UserSettings.master_radius);
                    //checken ob der neue Punkt zu nahe am alten ist. Wenn ja, dann kein push_back
                    if (SlavePointContainer.size()>0 && (SlavePointContainer.rbegin()->SquareDistance(SlaveOffsetPoint)>(Precision::Confusion()*Precision::Confusion())))
                        SlavePointContainer.push_back(SlaveOffsetPoint);
                    else if (SlavePointContainer.empty())
                        SlavePointContainer.push_back(SlaveOffsetPoint);
                }

                //Now Interpolate the Slave with reordered points based on the last point
                Handle(TColgp_HArray1OfPnt) InterpolationPoints = new TColgp_HArray1OfPnt(1, SlavePointContainer.size());
                adapted_start_index = start_index+1;
                for (int t=adapted_start_index;t<SlavePointContainer.size();++t)
                {
                    InterpolationPoints->SetValue(t+1,SlavePointContainer[t]);
                    if (t==start_index)
                        break;
                    if (t+1==SlavePointContainer.size())
                        t=-1;
                }

                m_all_offset_cuts_high.push_back(InterpolateOrderedPoints(InterpolationPoints,true));
                slave_done = true;
            } //Slave done

            gp_Pnt PreviousPoint = TempSpiralPoints[start_index].SurfacePoint;
            //check the current direction we would go
            gp_Vec help_Vec(TempSpiralPoints[start_index].SurfacePoint.Coord());
            direction_vector = TempSpiralPoints[start_index+1].SurfacePoint.Coord();
            direction_vector.Subtract(help_Vec);
            //direction_vector.SetZ(0.0);
            direction_vector.Normalize();
            direction_vector.Multiply(10.0);
            //switch the Spiral-direction if the clockwise flag is checked and  the
            //angle between the last and current round is more then 90°
            double angle = direction_vector.Angle(last_direction_vector);

            if (m_UserSettings.clockwise && angle<(D_PI*0.5))
                direction = true; //We are already in the wright direction
            else if (m_UserSettings.clockwise && angle >(D_PI*0.5))
                direction = false; //we have to switch the direction
            else if (!m_UserSettings.clockwise && angle<(D_PI*0.5))
                direction = false;
            else if (!m_UserSettings.clockwise && angle>(D_PI*0.5))
                direction = true;


            IntCurvesFace_ShapeIntersector anIntersector;
            anIntersector.Load(m_Shape,0.01);

            //Insert the first point into the TempSpiralPoints if we have just started
            std::vector<SpiralHelper> TempSpiralPointsFinal;
            TempSpiralPointsFinal.clear();
            gp_Pnt origin(0.0,0.0,0.0);
            if (!just_started)
                TempSpiralPointsFinal.push_back(lastPoint);
            else
                just_started=false;

            if (direction)
                adapted_start_index = start_index+1;
            else
            {
                if (start_index==0)
                    adapted_start_index = TempSpiralPoints.size()-2; //Skip the last Point as its equal to the first one
                else
                    adapted_start_index = start_index-1;
            }
            std::cout<<"Angle ="<< angle<< " Direction = "<< direction << "Start_Index: "<< adapted_start_index <<","<<start_index<<std::endl;
            double distance=0.0;
            for (int j=adapted_start_index;j<TempSpiralPoints.size();++j)
            {
                distance = distance + PreviousPoint.Distance(TempSpiralPoints[j].SurfacePoint);
                double delta_z = distance * m_UserSettings.level_distance / CurveLength;
                //we have to store the currentPoint for the distance calculation
                //before we change something at its coordinates
                PreviousPoint = TempSpiralPoints[j].SurfacePoint;

                TempSpiralPoints[j].SurfacePoint.SetZ(TempSpiralPoints[j].SurfacePoint.Z()+delta_z);
                //before we shoot we check if the z-level is not exactly the same as the next round
                //if (fabs(current_z_value-m_UserSettings.level_distance-TempSpiralPoints[j].SurfacePoint.Z())<=0.04)
                //    break; //We have nearly reached the next z-level, therefore we stop here.
                //Now we have to shoot to get the real Point we want
                //Therefore we have to generate the Direction Vector where to Shoot
                gp_Dir Shooting_Direction = getPerpendicularVec(TempSpiralPoints[j].LineD1);
                gp_Lin aLine(TempSpiralPoints[j].SurfacePoint,Shooting_Direction);
                anIntersector.Perform(aLine,-RealLast(),RealLast());
                if (anIntersector.NbPnt()<=1) //Just to debug
                    anIntersector.Perform(aLine,-RealLast(),RealLast());
                //Now we set the real Surface Point
                //How many Points did we get??
                int current_index;
                int points = anIntersector.NbPnt();
                if (points>0)
                {
                    float shortestDistance, shortestDistanceOld = FLT_MAX;
                    for (int g=1;g<=points;g++)
                    {
                        const gp_Pnt& TestPoint = anIntersector.Pnt(g);
                        shortestDistance = float(TestPoint.SquareDistance(TempSpiralPoints[j].SurfacePoint));
                        if (shortestDistance<shortestDistanceOld)
                        {
                            current_index = g;
                            shortestDistanceOld = shortestDistance;
                        }
                    }
                    //Now we check how far the shortest Distance is. If its more than 5mm then we jump to the
                    //next Point

                    if (shortestDistanceOld>50 )
                        continue;
                    TempSpiralPoints[j].SurfacePoint = anIntersector.Pnt(current_index);
                }
                else //We have to try a mesh intersection as the Nurb Intersection does not seem to work
                {
                    cout << "Big Probleme";
                    continue;
                }
                //Now get the Proper Normal at this point
                BRepAdaptor_Surface aFaceAdaptor(anIntersector.Face(current_index));
                gp_Pnt P;
                gp_Vec U_Vec,V_Vec;
                aFaceAdaptor.D1(anIntersector.UParameter(current_index),anIntersector.VParameter(current_index),P,U_Vec,V_Vec);
                U_Vec.Cross(V_Vec);
                U_Vec.Normalize();
                if (U_Vec.Z() < 0) U_Vec.Multiply(-1.0);

                TempSpiralPoints[j].SurfaceNormal = U_Vec;
                TempSpiralPointsFinal.push_back(TempSpiralPoints[j]);
                //If we reached the end before we processed all points, then we start at the beginning.
                if (direction)
                {
                    if (j==start_index)
                        break; //Now we have completed all Points
                    else if (j+1==TempSpiralPoints.size())
                    {
                        j=-1;    //-1 because at the for we ++ the variable directly
                        continue;
                    }

                }
                else
                {
                    if (j==start_index)
                        break; //Now we have completed all Points
                    else if (j-1<0)
                    {
                        j=TempSpiralPoints.size()-3;
                        continue;
                    }
                    //We switch to the end and skip the last point
                    //as its the same as the point at j=0;
                    j=j-2;//As the for puts +1 for each step
                }
            }
            //Offset for the slave
            OffsetSpiralPoints = OffsetSpiral(TempSpiralPointsFinal,false);
            //Now store the lastPoint of the currentSpiral as we need it for the next one
            lastPoint = *(TempSpiralPointsFinal.rbegin());
            gp_Vec temp_vector((OffsetSpiralPoints.rbegin()+5)->SurfacePoint.Coord());
            last_direction_vector = OffsetSpiralPoints.rbegin()->SurfacePoint.Coord();
            last_direction_vector.Subtract(temp_vector);
            //last_direction_vector.SetZ(0.0);
            last_direction_vector.Normalize();
            last_direction_vector.Multiply(10.0);
            Handle(TColgp_HArray1OfPnt) InterpolationPoints = new TColgp_HArray1OfPnt(1,OffsetSpiralPoints.size());
            TColgp_Array1OfVec Tangents(1,OffsetSpiralPoints.size());
            for (unsigned int t=0;t<OffsetSpiralPoints.size();++t)
            {
                InterpolationPoints->SetValue(t+1,OffsetSpiralPoints[t].SurfacePoint);
            }
            bool check = CheckPoints(InterpolationPoints);
            //Here we interpolate. If direction == true this means that the rotation is like the initial rotation
            m_all_offset_cuts_low.push_back(InterpolateOrderedPoints(InterpolationPoints,true));
        }
    }
    anoutput1.close();
    log.saveToFile("C:/normals.iv");
    return true;
}


std::vector<SpiralHelper> cutting_tools::OffsetSpiral(const std::vector<SpiralHelper>& SpiralPoints,bool master_or_slave)
{
    std::vector<SpiralHelper> OffsetPoints;
    SpiralHelper OffsetPoint;
    OffsetPoints.clear();
    //Offset the Points now

    for (unsigned int i=0;i<SpiralPoints.size();++i)
    {
        OffsetPoint = SpiralPoints[i];
        if (master_or_slave)
        {
            OffsetPoint.SurfaceNormal.Multiply(m_UserSettings.master_radius);
            OffsetPoint.SurfacePoint.SetXYZ(OffsetPoint.SurfacePoint.XYZ() + OffsetPoint.SurfaceNormal.XYZ());
        }
        else
        {
            OffsetPoint.SurfaceNormal.Multiply(m_UserSettings.slave_radius+m_UserSettings.sheet_thickness);
            OffsetPoint.SurfacePoint.SetXYZ(OffsetPoint.SurfacePoint.XYZ() - OffsetPoint.SurfaceNormal.XYZ());
        }
        if (OffsetPoints.size()>0 && (OffsetPoints.rbegin()->SurfacePoint.SquareDistance(OffsetPoint.SurfacePoint)>(Precision::Confusion()*Precision::Confusion())) && (fabs(OffsetPoints.rbegin()->SurfacePoint.Z()-OffsetPoint.SurfacePoint.Z())< (m_UserSettings.level_distance/10.0)))
        {
            OffsetPoints.push_back(OffsetPoint);
        }
        else if (OffsetPoints.empty())
        {
            OffsetPoints.push_back(OffsetPoint);
        }
        else if ((i+1==SpiralPoints.size()) && (OffsetPoints.rbegin()->SurfacePoint.SquareDistance(OffsetPoint.SurfacePoint)<(Precision::Confusion()*Precision::Confusion())))
        {
            //This part is necessary as the last point of the current spiral would otherwise not be offsetted
            OffsetPoints.pop_back();//Delete the two currently last points
            OffsetPoints.pop_back();
            OffsetPoints.push_back(OffsetPoint);//Add the last point
        }
    }
    return OffsetPoints;
}
std::vector<float> cutting_tools::getFlatAreas()
{
    std::vector<float> FlatAreas;
    FlatAreas.clear();
    for (unsigned int i=0;i<m_MachiningOrder.size();++i)
        FlatAreas.push_back(m_MachiningOrder[i].first.z);
    return FlatAreas;
}


bool cutting_tools::OffsetWires_FeatureBased()
{
    std::vector<std::pair<float,TopoDS_Shape> >::iterator current_flat_level;
    gp_Pnt lastPoint(0.0,0.0,0.0); //Initialize the first Point to the Origin
    current_flat_level = m_ordered_cuts.begin();
    bool slave_done= false; //Necessary if the slave is already put out
    //Nicht beim höchsten Anfangen, da wir den nicht mit dem Master fahren wollen
    for (m_ordered_cuts_it = m_ordered_cuts.begin()+1;m_ordered_cuts_it!=m_ordered_cuts.end();++m_ordered_cuts_it)
    {
        std::vector<std::pair<gp_Pnt,double> > MasterPointContainer;
        std::vector<std::vector<std::pair<gp_Pnt,double> > > MasterPointsStorage;
        std::vector<gp_Pnt> SlavePointContainer;
        std::vector<std::vector<gp_Pnt> > SlavePointsStorage;
        std::vector<std::vector<gp_Pnt> >::iterator anIterator1;
        std::vector<std::vector<std::pair<gp_Pnt,double> > >::iterator anIterator2;
        MasterPointsStorage.clear();
        SlavePointsStorage.clear();
        MasterPointContainer.clear();
        SlavePointContainer.clear();
        //Now we have to select which strategy to choose
        //if the current levels is bigger,the same, or less then the previous one
        if (m_ordered_cuts_it->first<(m_ordered_cuts_it-1)->first)
        {
            //Master is calculated as Usual, Slave stays at the currently highest level
            //Check if current Level has got a Wire
            if (m_ordered_cuts_it->second.ShapeType() == TopAbs_WIRE)
            {
                WireExplorer aWireExplorer(TopoDS::Wire(m_ordered_cuts_it->second));
                for (aWireExplorer.Init();aWireExplorer.More();aWireExplorer.Next())
                {
                    BRepAdaptor_Curve curveAdaptor(aWireExplorer.Current());
                    GCPnts_QuasiUniformAbscissa aProp(curveAdaptor,100);
                    for (int i=1;i<=aProp.NbPoints();++i)
                    {
                        std::pair<gp_Pnt,double> aTempPair;
                        //Check the direction
                        if (aWireExplorer.Current().Orientation() != TopAbs_REVERSED)
                            curveAdaptor.D0(aProp.Parameter(i),aTempPair.first);
                        else curveAdaptor.D0(aProp.Parameter(aProp.NbPoints()-i+1),aTempPair.first);
                        aTempPair.first.SetZ(aTempPair.first.Z() + m_UserSettings.master_radius);
                        aTempPair.second = 0.0; //Initialize of Angle
                        //checken ob der neue Punkt zu nahe am alten ist. Wenn ja, dann kein push_back
                        if (MasterPointContainer.size()>0 && (MasterPointContainer.rbegin()->first.SquareDistance(aTempPair.first)>(Precision::Confusion()*Precision::Confusion())))
                        {
                            MasterPointContainer.push_back(aTempPair);
                        }
                        else if (MasterPointContainer.empty())
                        {
                            MasterPointContainer.push_back(aTempPair);
                        }
                    }
                    //Now Interpolate the Points only if we have a non-continuous edge or if we are finished
                    //with all edges
                    bool tangency = true;
                    //If there are more Edges in the wire
                    if (aWireExplorer.MoreEdge())
                    {
                        tangency = CheckEdgeTangency(aWireExplorer.Current(),aWireExplorer.NextEdge());
                        if (!tangency)
                        {
                            //Store all the PointClouds in a StorageVector to arrange the Robot-Movement afterwards
                            MasterPointsStorage.push_back(MasterPointContainer);
                            MasterPointContainer.clear();
                        }
                        else continue;
                    }
                    else
                    {
                        MasterPointsStorage.push_back(MasterPointContainer);
                        MasterPointContainer.clear();
                    }
                }
                //Now check the point-cloud with the shortest distance to "lastPoint"
                int start_index = 0,start_array=0;
                CheckforLastPoint(lastPoint,start_index,start_array,MasterPointsStorage);
                //Now Interpolate the PointClouds...Cloud by Cloud
                //First we have to divide the first PointCloud as it is for sure that we start
                //Somewhere in the middle of it. We will then insert the points at the current start
                if (MasterPointsStorage.size() == 1) //If we have only one PointCloud
                {
                    MasterPointContainer.clear();
                    bool first = true;
                    for (unsigned int i=start_index;i<MasterPointsStorage.begin()->size();i++)
                    {
                        if (i+1 == MasterPointsStorage.begin()->size())
                        {
                            first = false;
                            i=-1;
                            continue;
                        }
                        MasterPointContainer.push_back(MasterPointsStorage[0][i]);
                        if (!first && i == start_index)
                            break;
                    }
                    //Now lets interpolate the Point Cloud
                    Handle(TColgp_HArray1OfPnt) InterpolationPointsMaster = new TColgp_HArray1OfPnt(1, MasterPointContainer.size());
                    for (unsigned int t=0;t<MasterPointContainer.size();++t)
                    {
                        InterpolationPointsMaster->SetValue(t+1,MasterPointContainer[t].first);
                    }
                    m_all_offset_cuts_high.push_back(InterpolateOrderedPoints(InterpolationPointsMaster,true));
                    //Store the last point
                    lastPoint = MasterPointContainer.rbegin()->first;
                }
                else //If we have more than one PointCloud
                {
                    anIterator2 = MasterPointsStorage.begin();
                    MasterPointContainer.clear();
                    for (int i=0;i<=start_index;i++)
                    {
                        MasterPointContainer.push_back(MasterPointsStorage[start_array][i]);
                    }
                    MasterPointsStorage.insert((anIterator2+start_array+1),MasterPointContainer);
                    MasterPointContainer.clear();
                    //Reinitialize the Iterators
                    anIterator2 = MasterPointsStorage.begin();
                    for (unsigned int i=start_index;i<MasterPointsStorage[start_array].size();i++)
                    {
                        MasterPointContainer.push_back(MasterPointsStorage[start_array][i]);
                    }
                    MasterPointsStorage.insert((anIterator2+start_array+2),MasterPointContainer);
                    //Reinitialize the Iterators
                    anIterator2 = MasterPointsStorage.begin();
                    //Delete the Original PointCloud
                    MasterPointsStorage.erase(anIterator2+start_array);
                    MasterPointContainer.clear();
                    //Now lets interpolate the Point Clouds
                    //Start at start_array+1 as the insert operations used us to do it like that
                    for (unsigned int j=start_array+1;j<MasterPointsStorage.size();++j)
                    {
                        Handle(TColgp_HArray1OfPnt) InterpolationPointsMaster = new TColgp_HArray1OfPnt(1, MasterPointsStorage[j].size());
                        for (unsigned int t=0;t<MasterPointsStorage[j].size();++t)
                        {
                            InterpolationPointsMaster->SetValue(t+1,MasterPointsStorage[j][t].first);
                        }
                        m_all_offset_cuts_high.push_back(InterpolateOrderedPoints(InterpolationPointsMaster,true));
                        if (j+1==MasterPointsStorage.size())
                        {
                            j=-1;
                            continue;
                        }
                        if (j+1==start_array+1)
                            break;
                    }
                    lastPoint = MasterPointsStorage[start_array].rbegin()->first;
                }
            }
            else //If the current Curve is no Wire
            {
                Edgesort aCutShapeSorter(m_ordered_cuts_it->second);
                for (aCutShapeSorter.Init();aCutShapeSorter.More();aCutShapeSorter.Next())
                {
                    //Get the PCurve and the GeomSurface
                    Handle_Geom2d_Curve a2DCurve;
                    Handle_Geom_Surface aSurface;
                    TopLoc_Location aLoc;
                    TopoDS_Edge anEdge;
                    double first2,last2;
                    bool reversed = false;
                    BRep_Tool::CurveOnSurface(aCutShapeSorter.Current(),a2DCurve,aSurface,aLoc,first2,last2);
                    //Jetzt noch die resultierende Surface und die Curve sauber drehen
                    //(vielleicht wurde ja das TopoDS_Face irgendwie gedreht oder die TopoDS_Edge)
                    if (aCutShapeSorter.Current().Orientation() == TopAbs_REVERSED)
                        reversed = true;

                    BRepAdaptor_Curve aCurveAdaptor(aCutShapeSorter.Current());
                    GCPnts_QuasiUniformAbscissa aPointGenerator(aCurveAdaptor,200);
                    int PointSize = aPointGenerator.NbPoints();
                    //Now get the surface normal to the generated points
                    for (int i=1;i<=PointSize;++i)
                    {
                        std::pair<gp_Pnt,double> PointContactPair;
                        gp_Pnt2d a2dParaPoint;
                        gp_Pnt aSurfacePoint;
                        TopoDS_Face aFace;
                        gp_Vec Uvec,Vvec,normalVec;
                        //If the curve is reversed we also have to reverse the point direction
                        if (reversed) a2DCurve->D0(aPointGenerator.Parameter(PointSize-i+1),a2dParaPoint);
                        else a2DCurve->D0(aPointGenerator.Parameter(i),a2dParaPoint);
                        GeomAdaptor_Surface aGeom_Adaptor(aSurface);
                        int t = aGeom_Adaptor.GetType();
                        aGeom_Adaptor.D1(a2dParaPoint.X(),a2dParaPoint.Y(),aSurfacePoint,Uvec,Vvec);
                        //Jetzt den Normalenvector auf die Fläche ausrechnen
                        normalVec = Uvec;
                        normalVec.Cross(Vvec);
                        normalVec.Normalize();
                        //Jetzt ist die Normale berechnet und auch normalisiert
                        //Jetzt noch checken ob die Normale auch wirklich auf die saubere Seite zeigt
                        //dazu nur checken ob der Z-Wert der Normale größer Null ist (dann im 1.und 2. Quadranten)
                        if (normalVec.Z()<0) normalVec.Multiply(-1.0);

                        //Mal kurz den Winkel zur Grund-Ebene ausrechnen
                        gp_Vec planeVec(normalVec.X(),normalVec.Y(),0.0);
                        //Den Winkel
                        PointContactPair.second = normalVec.Angle(planeVec);
                        gp_Vec NormalVecSlave = normalVec;
                        //Jetzt die Z-Komponente auf 0 setzen
                        //normalVec.SetZ(0.0);
                        normalVec.Normalize();
                        //Jetzt die Normale mit folgender Formel multiplizieren für den Master
                        //double multiply = m_UserSettings.master_radius*(1-sin(PointContactPair.second))/cos(PointContactPair.second);
                        double multiply = m_UserSettings.master_radius;
                        normalVec.Multiply(multiply);
                        //Jetzt den OffsetPunkt berechnen
                        PointContactPair.first.SetXYZ(aSurfacePoint.XYZ() + normalVec.XYZ());
                        //Damit wir keine Punkte bekommen die zu nahe beieinander liegen
                        //Den letzten hinzugefügten Punkt suchen
                        if (MasterPointContainer.size()>0 && (MasterPointContainer.rbegin()->first.SquareDistance(PointContactPair.first)>0.001))
                        {
                            MasterPointContainer.push_back(PointContactPair);
                        }
                        else if (MasterPointContainer.empty())
                        {
                            MasterPointContainer.push_back(PointContactPair);
                        }
                    }
                }
                MasterPointsStorage.push_back(MasterPointContainer);
                int start_index_master = 0,start_array_master=0;
                CheckforLastPoint(lastPoint,start_index_master,start_array_master,MasterPointsStorage);
                //First we have to divide the first PointCloud as it is for sure that we start
                //Somewhere in the middle of it. We will then insert the points at the current start
                MasterPointContainer.clear();
                bool first = true;
                for (unsigned int i=start_index_master;i<MasterPointsStorage.begin()->size();i++)
                {
                    if (i+1 == MasterPointsStorage.begin()->size())
                    {
                        first = false;
                        i=-1;
                        continue;
                    }
                    MasterPointContainer.push_back(MasterPointsStorage[0][i]);
                    if (!first && i == start_index_master)
                        break;
                }
                //Now lets interpolate the Point Cloud
                Handle(TColgp_HArray1OfPnt) InterpolationPointsMaster = new TColgp_HArray1OfPnt(1, MasterPointContainer.size());
                for (unsigned int t=0;t<MasterPointContainer.size();++t)
                    InterpolationPointsMaster->SetValue(t+1,MasterPointContainer[t].first);

                m_all_offset_cuts_high.push_back(InterpolateOrderedPoints(InterpolationPointsMaster,true));
                //Store the last point
                lastPoint = MasterPointContainer.rbegin()->first;
                //SlaveTool Path finished
            }//end calculation of the master if its not a wire
            //Calculate the Slave Toolpath
            if (!slave_done)//if we did not calculate the slave toolpath for the current flat area
            {
                TopoDS_Wire aWire = TopoDS::Wire(current_flat_level->second);
                BRepAdaptor_CompCurve2 wireAdaptor(aWire);
                GCPnts_QuasiUniformAbscissa aProp(wireAdaptor,1000);
                SlavePointContainer.clear();
                SlavePointsStorage.clear();
                for (int i=1;i<=aProp.NbPoints();++i)
                {
                    gp_Pnt SlaveOffsetPoint;
                    wireAdaptor.D0(aProp.Parameter(i),SlaveOffsetPoint);
                    SlaveOffsetPoint.SetZ(SlaveOffsetPoint.Z() - m_UserSettings.sheet_thickness - m_UserSettings.slave_radius);
                    //checken ob der neue Punkt zu nahe am alten ist. Wenn ja, dann kein push_back
                    if (SlavePointContainer.size()>0 && (SlavePointContainer.rbegin()->SquareDistance(SlaveOffsetPoint)>(Precision::Confusion()*Precision::Confusion())))
                        SlavePointContainer.push_back(SlaveOffsetPoint);
                    else if (SlavePointContainer.empty())
                        SlavePointContainer.push_back(SlaveOffsetPoint);
                }
                SlavePointsStorage.push_back(SlavePointContainer);
                int start_index_slave = 0,start_array_slave=0;
                CheckforLastPoint(lastPoint,start_index_slave,start_array_slave,SlavePointsStorage);
                //First we have to divide the first PointCloud as it is for sure that we start
                //Somewhere in the middle of it. We will then insert the points at the current start
                SlavePointContainer.clear();
                bool first = true;
                for (unsigned int i=start_index_slave;i<SlavePointsStorage.begin()->size();i++)
                {
                    if (i+1 == SlavePointsStorage.begin()->size())
                    {
                        first = false;
                        i=-1;
                        continue;
                    }
                    SlavePointContainer.push_back(SlavePointsStorage[0][i]);
                    if (!first && i == start_index_slave)
                        break;
                }
                //Now Interpolate the Slave, therefore we also have to take the curves normal directions
                Handle(TColgp_HArray1OfPnt) InterpolationPoints = new TColgp_HArray1OfPnt(1, SlavePointContainer.size());
                for (unsigned int t=0;t<SlavePointContainer.size();++t)
                    InterpolationPoints->SetValue(t+1,SlavePointContainer[t]);

                m_all_offset_cuts_low.push_back(InterpolateOrderedPoints(InterpolationPoints,true));
                slave_done = true;
            } //Slave done
        }//Current Curve < last curve is finished here
        if (m_ordered_cuts_it->first==(m_ordered_cuts_it-1)->first)
        {
            //we only set the new flat level wire here
            //no Toolpath is calculated
            current_flat_level = m_ordered_cuts_it;
            slave_done = false; //This is to generate the next flat level for the slave
        }//end of current == last
        if (m_ordered_cuts_it->first>(m_ordered_cuts_it-1)->first)
        {
            //The Slave Tool is now the Master Tool and therefore we only have to exchange < case
            if (m_ordered_cuts_it->second.ShapeType() == TopAbs_WIRE)
            {
                WireExplorer aWireExplorer(TopoDS::Wire(m_ordered_cuts_it->second));
                for (aWireExplorer.Init();aWireExplorer.More();aWireExplorer.Next())
                {
                    BRepAdaptor_Curve curveAdaptor(aWireExplorer.Current());
                    GCPnts_QuasiUniformAbscissa aProp(curveAdaptor,100);
                    for (int i=1;i<=aProp.NbPoints();++i)
                    {
                        std::pair<gp_Pnt,double> aTempPair;
                        //Check the direction
                        if (aWireExplorer.Current().Orientation() != TopAbs_REVERSED)
                            curveAdaptor.D0(aProp.Parameter(i),aTempPair.first);
                        else curveAdaptor.D0(aProp.Parameter(aProp.NbPoints()-i+1),aTempPair.first);
                        aTempPair.first.SetZ(aTempPair.first.Z() - m_UserSettings.slave_radius - m_UserSettings.sheet_thickness);
                        aTempPair.second = 0.0; //Initialize of Angle
                        //checken ob der neue Punkt zu nahe am alten ist. Wenn ja, dann kein push_back
                        if (MasterPointContainer.size()>0 && (MasterPointContainer.rbegin()->first.SquareDistance(aTempPair.first)>(Precision::Confusion()*Precision::Confusion())))
                        {
                            MasterPointContainer.push_back(aTempPair);
                        }
                        else if (MasterPointContainer.empty())
                        {
                            MasterPointContainer.push_back(aTempPair);
                        }
                    }
                    //Now Interpolate the Points only if we have a non-continuous edge or if we are finished
                    //with all edges
                    bool tangency = true;
                    //If there are more Edges in the wire
                    if (aWireExplorer.MoreEdge())
                    {
                        tangency = CheckEdgeTangency(aWireExplorer.Current(),aWireExplorer.NextEdge());
                        if (!tangency)
                        {
                            //Store all the PointClouds in a StorageVector to arrange the Robot-Movement afterwards
                            MasterPointsStorage.push_back(MasterPointContainer);
                            MasterPointContainer.clear();
                        }
                        else continue;
                    }
                    else
                    {
                        MasterPointsStorage.push_back(MasterPointContainer);
                        MasterPointContainer.clear();
                    }
                }
                //Now check the point-cloud with the shortest distance to "lastPoint"
                int start_index = 0,start_array=0;
                CheckforLastPoint(lastPoint,start_index,start_array,MasterPointsStorage);
                //Now Interpolate the PointClouds...Cloud by Cloud
                //First we have to divide the first PointCloud as it is for sure that we start
                //Somewhere in the middle of it. We will then insert the points at the current start
                if (MasterPointsStorage.size() == 1) //If we have only one PointCloud
                {
                    MasterPointContainer.clear();
                    bool first = true;
                    for (unsigned int i=start_index;i<MasterPointsStorage.begin()->size();i++)
                    {
                        if (i+1 == MasterPointsStorage.begin()->size())
                        {
                            first = false;
                            i=-1;
                            continue;
                        }
                        MasterPointContainer.push_back(MasterPointsStorage[0][i]);
                        if (!first && i == start_index)
                            break;
                    }
                    //Now lets interpolate the Point Cloud
                    Handle(TColgp_HArray1OfPnt) InterpolationPointsMaster = new TColgp_HArray1OfPnt(1, MasterPointContainer.size());
                    for (unsigned int t=0;t<MasterPointContainer.size();++t)
                    {
                        InterpolationPointsMaster->SetValue(t+1,MasterPointContainer[t].first);
                    }
                    m_all_offset_cuts_low.push_back(InterpolateOrderedPoints(InterpolationPointsMaster,true));
                    //Store the last point
                    lastPoint = MasterPointContainer.rbegin()->first;
                }
                else //If we have more than one PointCloud
                {
                    anIterator2 = MasterPointsStorage.begin();
                    MasterPointContainer.clear();
                    for (int i=0;i<=start_index;i++)
                    {
                        MasterPointContainer.push_back(MasterPointsStorage[start_array][i]);
                    }
                    MasterPointsStorage.insert((anIterator2+start_array+1),MasterPointContainer);
                    MasterPointContainer.clear();
                    //Reinitialize the Iterators
                    anIterator2 = MasterPointsStorage.begin();
                    for (unsigned int i=start_index;i<MasterPointsStorage[start_array].size();i++)
                    {
                        MasterPointContainer.push_back(MasterPointsStorage[start_array][i]);
                    }
                    MasterPointsStorage.insert((anIterator2+start_array+2),MasterPointContainer);
                    //Reinitialize the Iterators
                    anIterator2 = MasterPointsStorage.begin();
                    //Delete the Original PointCloud
                    MasterPointsStorage.erase(anIterator2+start_array);
                    MasterPointContainer.clear();
                    //Now lets interpolate the Point Clouds
                    //Start at start_array+1 as the insert operations used us to do it like that
                    for (unsigned int j=start_array+1;j<MasterPointsStorage.size();++j)
                    {
                        Handle(TColgp_HArray1OfPnt) InterpolationPointsMaster = new TColgp_HArray1OfPnt(1, MasterPointsStorage[j].size());
                        for (unsigned int t=0;t<MasterPointsStorage[j].size();++t)
                        {
                            InterpolationPointsMaster->SetValue(t+1,MasterPointsStorage[j][t].first);
                        }
                        m_all_offset_cuts_low.push_back(InterpolateOrderedPoints(InterpolationPointsMaster,true));
                        if (j+1==MasterPointsStorage.size())
                        {
                            j=-1;
                            continue;
                        }
                        if (j+1==start_array+1)
                            break;
                    }
                    lastPoint = MasterPointsStorage[start_array].rbegin()->first;
                }
            }
            else //If the current Curve is no Wire
            {
                Edgesort aCutShapeSorter(m_ordered_cuts_it->second);
                for (aCutShapeSorter.Init();aCutShapeSorter.More();aCutShapeSorter.Next())
                {
                    //Get the PCurve and the GeomSurface
                    Handle_Geom2d_Curve a2DCurve;
                    Handle_Geom_Surface aSurface;
                    TopLoc_Location aLoc;
                    TopoDS_Edge anEdge;
                    double first2,last2;
                    bool reversed = false;
                    BRep_Tool::CurveOnSurface(aCutShapeSorter.Current(),a2DCurve,aSurface,aLoc,first2,last2);
                    //Jetzt noch die resultierende Surface und die Curve sauber drehen
                    //(vielleicht wurde ja das TopoDS_Face irgendwie gedreht oder die TopoDS_Edge)
                    if (aCutShapeSorter.Current().Orientation() == TopAbs_REVERSED)
                        reversed = true;

                    BRepAdaptor_Curve aCurveAdaptor(aCutShapeSorter.Current());
                    GCPnts_QuasiUniformAbscissa aPointGenerator(aCurveAdaptor,200);
                    int PointSize = aPointGenerator.NbPoints();
                    //Now get the surface normal to the generated points
                    for (int i=1;i<=PointSize;++i)
                    {
                        std::pair<gp_Pnt,double> PointContactPair;
                        gp_Pnt2d a2dParaPoint;
                        gp_Pnt aSurfacePoint;
                        TopoDS_Face aFace;
                        gp_Vec Uvec,Vvec,normalVec;
                        //If the curve is reversed we also have to reverse the point direction
                        if (reversed) a2DCurve->D0(aPointGenerator.Parameter(PointSize-i+1),a2dParaPoint);
                        else a2DCurve->D0(aPointGenerator.Parameter(i),a2dParaPoint);
                        GeomAdaptor_Surface aGeom_Adaptor(aSurface);
                        int t = aGeom_Adaptor.GetType();
                        aGeom_Adaptor.D1(a2dParaPoint.X(),a2dParaPoint.Y(),aSurfacePoint,Uvec,Vvec);
                        //Jetzt den Normalenvector auf die Fläche ausrechnen
                        normalVec = Uvec;
                        normalVec.Cross(Vvec);
                        normalVec.Normalize();
                        //Jetzt ist die Normale berechnet und auch normalisiert
                        //Jetzt noch checken ob die Normale auch wirklich auf die saubere Seite zeigt
                        //dazu nur checken ob der Z-Wert der Normale größer Null ist (dann im 1.und 2. Quadranten)
                        if (normalVec.Z()<0) normalVec.Multiply(-1.0);

                        //Mal kurz den Winkel zur Grund-Ebene ausrechnen
                        gp_Vec planeVec(normalVec.X(),normalVec.Y(),0.0);
                        //Den Winkel
                        PointContactPair.second = normalVec.Angle(planeVec);
                        gp_Vec NormalVecSlave = normalVec;
                        //Jetzt die Z-Komponente auf 0 setzen
                        //normalVec.SetZ(0.0);
                        normalVec.Normalize();
                        //Jetzt die Normale mit folgender Formel multiplizieren für den Master
                        //double multiply = m_UserSettings.master_radius*(1-sin(PointContactPair.second))/cos(PointContactPair.second);
                        double multiply = m_UserSettings.slave_radius + m_UserSettings.sheet_thickness;
                        normalVec.Multiply(multiply);
                        normalVec.Multiply(-1.0); //As the master is the slave
                        //Jetzt den OffsetPunkt berechnen
                        PointContactPair.first.SetXYZ(aSurfacePoint.XYZ() + normalVec.XYZ());
                        //Damit wir keine Punkte bekommen die zu nahe beieinander liegen
                        //Den letzten hinzugefügten Punkt suchen
                        if (MasterPointContainer.size()>0 && (MasterPointContainer.rbegin()->first.SquareDistance(PointContactPair.first)>0.001))
                        {
                            MasterPointContainer.push_back(PointContactPair);
                        }
                        else if (MasterPointContainer.empty())
                        {
                            MasterPointContainer.push_back(PointContactPair);
                        }
                    }
                }
                MasterPointsStorage.push_back(MasterPointContainer);
                int start_index_master = 0,start_array_master=0;
                CheckforLastPoint(lastPoint,start_index_master,start_array_master,MasterPointsStorage);
                //First we have to divide the first PointCloud as it is for sure that we start
                //Somewhere in the middle of it. We will then insert the points at the current start
                MasterPointContainer.clear();
                bool first = true;
                for (unsigned int i=start_index_master;i<MasterPointsStorage.begin()->size();i++)
                {
                    if (i+1 == MasterPointsStorage.begin()->size())
                    {
                        first = false;
                        i=-1;
                        continue;
                    }
                    MasterPointContainer.push_back(MasterPointsStorage[0][i]);
                    if (!first && i == start_index_master)
                        break;
                }
                //Now lets interpolate the Point Cloud
                Handle(TColgp_HArray1OfPnt) InterpolationPointsMaster = new TColgp_HArray1OfPnt(1, MasterPointContainer.size());
                for (unsigned int t=0;t<MasterPointContainer.size();++t)
                    InterpolationPointsMaster->SetValue(t+1,MasterPointContainer[t].first);

                m_all_offset_cuts_low.push_back(InterpolateOrderedPoints(InterpolationPointsMaster,true));
                //Store the last point
                lastPoint = MasterPointContainer.rbegin()->first;
                //SlaveTool Path finished
            }//end calculation of the master if its not a wire
            //Calculate the Slave Toolpath
            if (!slave_done)//if we did not calculate the slave toolpath for the current flat area
            {
                TopoDS_Wire aWire = TopoDS::Wire(current_flat_level->second);
                BRepAdaptor_CompCurve2 wireAdaptor(aWire);
                GCPnts_QuasiUniformAbscissa aProp(wireAdaptor,1000);
                SlavePointContainer.clear();
                SlavePointsStorage.clear();
                for (int i=1;i<=aProp.NbPoints();++i)
                {
                    gp_Pnt SlaveOffsetPoint;
                    wireAdaptor.D0(aProp.Parameter(i),SlaveOffsetPoint);
                    SlaveOffsetPoint.SetZ(SlaveOffsetPoint.Z() + m_UserSettings.master_radius);
                    //checken ob der neue Punkt zu nahe am alten ist. Wenn ja, dann kein push_back
                    if (SlavePointContainer.size()>0 && (SlavePointContainer.rbegin()->SquareDistance(SlaveOffsetPoint)>(Precision::Confusion()*Precision::Confusion())))
                    {
                        SlavePointContainer.push_back(SlaveOffsetPoint);
                    }
                    else if (SlavePointContainer.empty())
                    {
                        SlavePointContainer.push_back(SlaveOffsetPoint);
                    }
                }
                SlavePointsStorage.push_back(SlavePointContainer);
                int start_index_slave = 0,start_array_slave=0;
                CheckforLastPoint(lastPoint,start_index_slave,start_array_slave,SlavePointsStorage);
                //First we have to divide the first PointCloud as it is for sure that we start
                //Somewhere in the middle of it. We will then insert the points at the current start
                SlavePointContainer.clear();
                bool first = true;
                for (unsigned int i=start_index_slave;i<SlavePointsStorage.begin()->size();i++)
                {
                    if (i+1 == SlavePointsStorage.begin()->size())
                    {
                        first = false;
                        i=-1;
                        continue;
                    }
                    SlavePointContainer.push_back(SlavePointsStorage[0][i]);
                    if (!first && i == start_index_slave)
                        break;
                }
                //Now Interpolate the Slave, therefore we also have to take the curves normal directions
                Handle(TColgp_HArray1OfPnt) InterpolationPoints = new TColgp_HArray1OfPnt(1, SlavePointContainer.size());
                for (unsigned int t=0;t<SlavePointContainer.size();++t)
                    InterpolationPoints->SetValue(t+1,SlavePointContainer[t]);

                m_all_offset_cuts_high.push_back(InterpolateOrderedPoints(InterpolationPoints,true));
                slave_done = true;
            } //Slave done
        }//Current Curve > Last Curve
    }//Main for Loop which goes through all the curves

    return true;
}

bool cutting_tools::cut_Mesh(float z_level, float min_level, std::list<std::vector<Base::Vector3f> >&result, float &z_level_corrected)
{
    //std::ofstream outfile;
    //outfile.open("c:/mesh_cut.out");
    Base::Vector3f z_level_plane,normal;
    z_level_plane.z=z_level;
    normal.x=0;
    normal.y=0;
    normal.z=1.0;
    bool cutok;
    //Die Richtung für die Korrektur wird hier festgelegt
    bool direction=true;
    float factor = 0.0;
    do
    {
        cutok = true;
        m_aMeshAlgo->CutWithPlane(z_level_plane,normal,*m_CAD_Mesh_Grid,result);
        //std::list<std::vector<Base::Vector3f> >::iterator it;
        //std::vector<Base::Vector3f>::iterator vector_it;
        //checken ob wirklich ein Schnitt zustande gekommen ist
        if (result.size()==0)
        {
            cutok = false;
            //Jedes Mal ein wenig mehr Abstand für die Korrektur einfügen
            factor = factor+float(0.05);
            if (factor>=1) factor = float(0.95);
            //Wenn wir das erste Mal eine Korrektur machen müssen gehts zunächst mal mit Minus rein
            if (direction)
            {
                z_level_plane.z = (z_level-(m_pitch*factor));
                z_level_corrected = z_level_plane.z;
                direction=false;
                continue;
            }
            else
            {
                z_level_plane.z = (z_level+(m_pitch*factor));
                z_level_corrected = z_level_plane.z;
                direction=true;
                continue;
            }
        }
    }
    while (cutok==false);
    //for(vector_it=(*(result.begin())).begin();vector_it<(*(result.begin())).end();++vector_it)
    //outfile << (*vector_it).x <<","<<(*vector_it).y <<","<<(*vector_it).z<< std::endl;
    //outfile.close();
    return true;
}






bool cutting_tools::cut(float z_level, float min_level, TopoDS_Shape &aCutShape, float &z_level_corrected)
{
    gp_Pnt aPlanePnt(0,0,z_level);
    gp_Dir aPlaneDir(0,0,1);
    bool cutok;
    //Die Richtung für die Korrektur wird hier festgelegt
    bool correction=true;
    float factor = 0.0;
    do
    {
        cutok = true;
        Handle_Geom_Plane aPlane = new Geom_Plane(aPlanePnt, aPlaneDir);
        BRepBuilderAPI_MakeFace Face(aPlane);
        BRepAlgo_Section mkCut(m_Shape, Face.Face(),Standard_False);
        mkCut.Approximation (Standard_True);
        mkCut.ComputePCurveOn1(Standard_True);
        mkCut.Build();
        //Den neuen Algorithmus checken
        //Edgesort aSorter(mkCut.Shape());
        //aSorter.Init();

        //Jetzt checken ob auch wirlich edges vorhanden sind
        TopExp_Explorer exploreShape;
        exploreShape.Init(mkCut.Shape(),TopAbs_EDGE);
        //Wenn keine Edge vorhanden ist
        if (!exploreShape.More())
        {
            cutok = false;
            //Jedes Mal ein wenig mehr Abstand für die Korrektur einfügen
            factor = factor+float(0.05);
            if (factor>=1) factor = float(0.95);
            //Wenn wir das erste Mal eine Korrektur machen müssen gehts zunächst mal mit Minus rein
            if (correction)
            {
                aPlanePnt.SetZ(z_level-(m_pitch*factor));
                z_level_corrected = float(aPlanePnt.Z());
                correction=false;
                continue;
            }
            else
            {
                aPlanePnt.SetZ(z_level+(m_pitch*factor));
                z_level_corrected = float(aPlanePnt.Z());
                correction=true;
                continue;
            }
        }
        //Das Shape, welches per Referenz übergeben wird jetzt mit dem geordneten Schnitt füllen
        aCutShape = mkCut.Shape();

    }
    while (cutok==false);

    return true;
}





bool cutting_tools::classifyShape()
{
    TopExp_Explorer Explorer;
    Explorer.Init(m_Shape,TopAbs_FACE);
    if (!Explorer.More()) return false;
    //checken wieviele verschiedene Faces wir haben
    int k=0;
    for (; Explorer.More(); Explorer.Next())
    {
        k++;
    }
    std::cout <<"We have " << k << "Faces" << std::endl;
    //Wenn mehr als ein Face vorhanden, dann eine Membervariable setzen
    if (k>1) m_cad = true;
    return true;
}



/* Hier ging das alte cut los




 bool cutok=true;
    //Falls wir nur ein Face haben und keine flachen Bereiche
    if (m_all_cuts.empty())
    {
        //Schnitte über die Bounding Box bestimmen
        Bnd_Box currentBBox;

        Standard_Real XMin, YMin, ZMin, XMax, YMax, ZMax;
        BRepBndLib::Add(m_Shape, currentBBox );
        currentBBox.SetGap(0.0);
        currentBBox.Get(XMin, YMin, ZMin, XMax, YMax, ZMax);
        double maxlevel=Max(ZMax,ZMin);
        double minlevel=Min(ZMax,ZMin);
        int cutnumber = fabs((maxlevel-minlevel)/m_pitch);//Cast um die Nachkommastellen wegzuschneiden
        m_pitch = fabs(maxlevel-minlevel)/cutnumber;//m_pitch leicht korrigieren um wirklich auf die letzte Ebene zu kommen

  //Aktuell wird die letzte Ebene bei selbst approxmierten Flächen nicht als Bahnkurve betrachtet
  //Auch die erste Ebene fällt komplett weg, da unwichtig. Lediglich für die untere Maschine ist die Bahn sinnvoll falls überhaupt noch flache Bereiche vorhanden sind
        for (int i=0;i<cutnumber;++i)
  {
            //Jetzt schneiden (die oberste Ebene auslassen)
            double z_level = maxlevel-(i*m_pitch);
            gp_Pnt aPlanePnt(0,0,z_level);
            gp_Dir aPlaneDir(0,0,1);

   do
   {
    cutok = true;
    Handle_Geom_Plane aPlane = new Geom_Plane(aPlanePnt, aPlaneDir);
    BRepBuilderAPI_MakeFace Face(aPlane);
    BRepAlgo_Section mkCut(m_Shape, Face.Face(),Standard_False);
    mkCut.Approximation (Standard_True);
    mkCut.Build();
    //Jetzt checken ob auch wirlich edges vorhanden sind
    TopExp_Explorer exploreShape;
    exploreShape.Init(mkCut.Shape(),TopAbs_EDGE);
    //Wenn keine Edge vorhanden ist
    if(!exploreShape.More())
    {
     cutok = false;
     aPlanePnt.SetZ(z_level-(m_pitch/5));
     if(aPlanePnt.Z()<minlevel) aPlanePnt.SetZ(minlevel+m_pitch/5);
     continue;
    }
    //Weil der Punkt sich ja geändert haben könnte
    m_zl_wire_combination.first = aPlanePnt.Z();
    m_zl_wire_combination.second = ordercutShape(mkCut.Shape());
    //Geordnete Edges in den All_Cuts-Vector stecken
    m_all_cuts.push_back(m_zl_wire_combination);
   }while (cutok==false);
  }
        return true;
    }
    //Bei mehreren flachen Bereichen
    else
    {
        std::vector<float> InitialPlaneLevels;
  std::vector<std::pair<float,TopoDS_Wire> > atemp_storage;
  atemp_storage.clear();
        std::vector<float>::iterator temp_it;
        for (m_it= m_all_cuts.begin();m_it<m_all_cuts.end();++m_it)
        {
            //Die Wires filtern und z.B. bei zwei Wires auf der obersten Ebene nur die innere nehmen
   Bnd_Box currentBBox,BBox2;
   Standard_Real X1Min, Y1Min, Z1Min, X1Max, Y1Max, Z1Max,X2Min, Y2Min, Z2Min, X2Max, Y2Max, Z2Max;
   BRepBndLib::Add((*m_it).second, currentBBox );
   currentBBox.SetGap(0.0);
   currentBBox.Get(X1Min, Y1Min, Z1Min, X1Max, Y1Max, Z1Max);
   if((*(m_it+1)).first == (*m_it).first) //Wenn die beiden Wires auf der gleichen Ebene liegen.....
   {
    BRepBndLib::Add((*(m_it+1)).second,BBox2);
    BBox2.SetGap(0.0);
    BBox2.Get(X2Min,Y2Min,Z2Min,X2Max,Y2Max,Z2Max);
   }
   //Jetzt checken welche kleiner ist
   if(X1Min<X2Min && X1Max>X2Max && Y1Min < Y2Min && Y1Max>Y2Max) //1 ist größer
   {
    if(m_it == m_all_cuts.begin())//Wenn wir auf der obersten Ebene sind....
    {
     atemp_storage.push_back(*(m_it+1));
     m_it++;//Wir überspringen damit das nächste //Damit haben wir nur noch ein Problem falls wir mehr als zwei Wires auf einer ebene haben.
    }
    else
    {
     atemp_storage.push_back(*(m_it));
    }
   }
   //Mal schauen ob der Wert schon in der Liste vorhanden ist
            temp_it = std::find(InitialPlaneLevels.begin(),InitialPlaneLevels.end(),(*m_it).first);
            if (temp_it == InitialPlaneLevels.end())
            {
                InitialPlaneLevels.push_back((*m_it).first);
            }
        }
  //Jetzt die flachen Bereiche der Höhe nach sortieren
  std::sort(InitialPlaneLevels.begin(),InitialPlaneLevels.end(),FloatHuge);

  //Die Schnitte müssen jetzt zwischen die flachen Stücke einsortiert werden
  for (temp_it=InitialPlaneLevels.begin();temp_it<InitialPlaneLevels.end();++temp_it)
        {
            //Debug cout << "Bereich" <<endl;
   double maxlevel=*temp_it;//Maximaler aktueller Wert
   if(temp_it+1==InitialPlaneLevels.end()) continue;
   double minlevel=*(temp_it+1);
   int cutnumber = fabs((maxlevel-minlevel)/m_pitch);//Cast um die Nachkommastellen wegzuschneiden
   m_pitch = fabs(maxlevel-minlevel)/cutnumber;

   for (int i=1;i<cutnumber;++i)
   {
    //Jetzt schneiden (die oberste Ebene auslassen und die unterste, da dort wieder wires kommen)
    double z_level = maxlevel-(i*m_pitch);
    gp_Pnt aPlanePnt(0,0,z_level);
    gp_Dir aPlaneDir(0,0,1);
    cutok=true;
    do
    {
     Handle_Geom_Plane aPlane = new Geom_Plane(aPlanePnt, aPlaneDir);
     BRepBuilderAPI_MakeFace Face(aPlane);
     BRepAlgo_Section mkCut(m_Shape, Face.Face(),Standard_False);
     try
     {
      mkCut.Approximation (Standard_True);
      mkCut.Build();
     }
     catch(...)
     {
      cutok = false;
      aPlanePnt.SetZ(z_level-(m_pitch/5));
      continue;
     }

     //Weil der Punkt sich ja geändert haben könnte
     m_zl_wire_combination.first = aPlanePnt.Z();
     m_zl_wire_combination.second = ordercutShape(mkCut.Shape());
     //Geordnete Edges in den All_Cuts-Vector stecken
     m_all_cuts.push_back(m_zl_wire_combination);
    }while (cutok=false);

   }
  }
 }
}
*/

//TopoDS_Compound  cutting_tools::getCutShape()
//{
//  BRep_Builder builder;
//  TopoDS_Compound Comp;
//  builder.MakeCompound(Comp);
////  for(m_it = m_ordered_wires.begin();m_it < m_ordered_wires.end();++m_it)
////  {
////   builder.Add(Comp,(*m_it).second);
////  }
//  return Comp;
//
//}




double cutting_tools::GetEdgeLength(const TopoDS_Edge& anEdge)
{
    GProp_GProps lProps;
    BRepGProp::LinearProperties(anEdge,lProps);
    double length = lProps.Mass();
    return length;
}

